Appearance
Proactive Messages
IMPORTANT
This feature is in beta and represents an initial implementation. The API and behavior may change or be removed in future releases.
Iris can proactively reach out to you when she has something helpful to share. Instead of waiting for you to start a conversation, Iris monitors context like your calendar, recent conversations, and preferences to decide when a check-in would be valuable.
TIP
Proactive messages are enabled by default. You can adjust settings and boundaries in Settings > Proactive Messages.
How It Works
Iris runs a "heartbeat" every 30 minutes. During each heartbeat, she:
- Gathers context: Calendar events, recent conversations, memories, and your guidance
- Makes a decision: Should she reach out, or give you space?
- Crafts a message: If reaching out, she composes a personalized message
- Delivers it: The message appears in your chat, with a notification if your tab is backgrounded
The key insight: Iris decides whether to reach out, but you set the guardrails. It's not a rule engine - it's a relationship where you provide guidance and Iris interprets it with judgment.
Setting Up Proactive Messages
1. Review Settings
Proactive messages are enabled by default. Visit Settings > Proactive Messages to review or adjust your preferences.
2. Add Guidance
Navigate to Guidance (in the sidebar) and add guidance entries that tell Iris when and how you'd like her to reach out. These aren't rigid rules - they're preferences Iris interprets with judgment.
Example guidance:
| Title | Description |
|---|---|
| Morning check-ins | I like when you check in around 7-8am while I'm having coffee, especially if I have a busy day ahead |
| Therapy follow-ups | Please check in about an hour after my therapy appointments to see how I'm doing |
| Don't over-message | If we just talked within the last 2 hours, I probably don't need another check-in |
| Calendar reminders | Give me a heads up 15-30 minutes before important appointments |
3. Set Boundaries (Optional)
Boundaries are hard constraints Iris must respect - unlike guidance, these aren't suggestions.
Quiet Hours: Block messages during specific time windows (e.g., 10pm - 7am)
Do Not Disturb: Block all proactive messages until you manually disable it
Temporary Silence: Quick buttons to silence for 1 hour, 4 hours, or until tomorrow
Message Priority
When Iris decides to reach out, she assigns a priority level:
| Priority | Use Case | Example |
|---|---|---|
| High | Time-sensitive situations | "Your meeting with Sarah starts in 15 minutes" |
| Normal | Routine check-ins | "How are you feeling after your therapy session?" |
| Low | Non-urgent thoughts | "When you have a moment, I noticed something about your schedule" |
The priority helps you understand urgency at a glance.
When Iris Gives Space
Iris doesn't message just because she can. She'll skip a heartbeat when:
- You just had a satisfying conversation recently
- It's work hours with no urgent needs
- There's no clear reason to interrupt
- You're in an active conversation (message sent in the last 5 minutes)
- A boundary is active (quiet hours, DND, or temporary silence)
Every decision is logged with Iris's reasoning, so you can see why she reached out or chose not to.
Viewing Activity Logs
Navigate to Logs (in the sidebar) to see Iris's heartbeat history. Each entry shows:
- Action: Whether Iris reached out or gave space
- Reasoning: Why she made that decision
- Context snapshot: What information she considered
- Conversation link: If she sent a message, a link to that conversation
Use this to understand Iris's decision-making and tune your guidance accordingly.
Scheduled Follow-ups
Iris can schedule her own future check-ins. For example, if you mention "I have a big presentation tomorrow," Iris might:
- Acknowledge it now
- Schedule a follow-up for tomorrow afternoon to ask how it went
You'll see scheduled follow-ups in the Logs page.
Configuration
Proactive message settings are in config/iris.php:
| Setting | Default | Description |
|---|---|---|
heartbeat.model | claude-sonnet-4-5 | Model for heartbeat decisions |
heartbeat.max_steps | 30 | Tool iterations when crafting messages |
heartbeat.history_limit | null | Messages included (null = use default) |
Customizing Behavior
php
// config/iris-custom.php
return [
'heartbeat' => [
'max_steps' => 10, // Simpler message crafting
'history_limit' => 25, // Less conversation context
],
];How Decisions Are Made
Each heartbeat follows this flow:
┌─────────────────────────────────────────┐
│ Heartbeat Scheduled │
│ (every 30 minutes) │
└─────────────────┬───────────────────────┘
│
▼
┌─────────────────────────────────────────┐
│ Check Eligibility │
│ - User has heartbeat enabled? │
│ - Any boundaries active? │
│ - Active conversation in progress? │
└─────────────────┬───────────────────────┘
│
eligible? │
▼
┌─────────────────────────────────────────┐
│ Gather Context │
│ - Calendar events (next 2 hours) │
│ - Recent conversation history │
│ - User's guidance entries │
│ - Scheduled follow-ups │
│ - Memories and Truths │
└─────────────────┬───────────────────────┘
│
▼
┌─────────────────────────────────────────┐
│ Make Decision │
│ - Send message or give space? │
│ - What priority level? │
│ - Schedule a follow-up? │
└─────────────────┬───────────────────────┘
│
send message?
│
▼
┌─────────────────────────────────────────┐
│ Craft Message │
│ - Full agent with tools │
│ - Can search memory, check calendar │
│ - Personalized, context-aware │
└─────────────────┬───────────────────────┘
│
▼
┌─────────────────────────────────────────┐
│ Deliver & Log │
│ - Save to conversation history │
│ - Broadcast via WebSocket │
│ - Record decision in logs │
└─────────────────────────────────────────┘Running Manually
Test heartbeat behavior with the Artisan command:
bash
# Check all eligible users (dry run)
php artisan iris:heartbeat --dry-run
# Process a specific user
php artisan iris:heartbeat --user=1
# Force process (bypass boundary checks)
php artisan iris:heartbeat --user=1 --forceScheduling
The heartbeat runs automatically via Laravel's scheduler. It's configured in routes/console.php:
php
Schedule::command('iris:heartbeat')
->everyThirtyMinutes()
->withoutOverlapping();Costs
Each heartbeat consumes API tokens for:
- Decision phase: Structured output to decide whether to reach out
- Message crafting: Full agent response with tool access (only if sending a message)
Token usage is tracked in the heartbeat source type. Start with conservative guidance that results in fewer messages while you learn Iris's decision-making patterns.
Troubleshooting
Messages not appearing: Verify proactive messages haven't been disabled in Settings > Proactive Messages and that the scheduler is running. Check the Logs page to see if heartbeats are being processed.
Too many messages: Add guidance like "Don't over-message - give me space between check-ins" and review the Logs to see what's triggering outreach.
Messages at bad times: Set up Quiet Hours in Settings > Proactive Messages to block specific time windows.
"Blocked" status in logs: Check your boundaries - you might have DND enabled or be within quiet hours. Use --force with the Artisan command to test bypassing boundaries.
Disabling Proactive Messages
Toggle off "Enable Proactive Messages" in Settings > Proactive Messages. Iris will stop running heartbeat checks and sending proactive messages.