Skip to content

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:

  1. Gathers context: Calendar events, recent conversations, memories, and your guidance
  2. Makes a decision: Should she reach out, or give you space?
  3. Crafts a message: If reaching out, she composes a personalized message
  4. 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:

TitleDescription
Morning check-insI like when you check in around 7-8am while I'm having coffee, especially if I have a busy day ahead
Therapy follow-upsPlease check in about an hour after my therapy appointments to see how I'm doing
Don't over-messageIf we just talked within the last 2 hours, I probably don't need another check-in
Calendar remindersGive 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:

PriorityUse CaseExample
HighTime-sensitive situations"Your meeting with Sarah starts in 15 minutes"
NormalRoutine check-ins"How are you feeling after your therapy session?"
LowNon-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:

  1. Acknowledge it now
  2. 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:

SettingDefaultDescription
heartbeat.modelclaude-sonnet-4-5Model for heartbeat decisions
heartbeat.max_steps30Tool iterations when crafting messages
heartbeat.history_limitnullMessages 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 --force

Scheduling

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:

  1. Decision phase: Structured output to decide whether to reach out
  2. 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.