Reminder and Proactive Messaging System¶
Last Updated: December 9, 2025
Overview¶
This document describes the reminder and proactive messaging system in Archety. The system supports:
- User Reminders - One-time and recurring reminders set by users
- Proactive Messages - Automated insights from calendar, email, and travel monitoring
Architecture¶
┌─────────────────────────────────────────────────────────────────────┐
│ Background Scheduler │
│ (APScheduler - AsyncIO) │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │
│ │ Reminder Check │ │ Calendar Checks │ │ Email Urgency │ │
│ │ (every 1 min) │ │ (7:30am daily) │ │ (3x daily) │ │
│ └────────┬────────┘ └────────┬────────┘ └────────┬────────┘ │
│ │ │ │ │
│ └────────────────────┼────────────────────┘ │
│ ▼ │
│ ┌─────────────────────────┐ │
│ │ SmartScheduler │ │
│ │ (jitter, rate limiting) │ │
│ └────────────┬────────────┘ │
│ ▼ │
│ ┌─────────────────────────┐ │
│ │ ProactiveSender │ │
│ └────────────┬────────────┘ │
│ ▼ │
│ ┌─────────────────────────┐ │
│ │ Edge Agent Manager │ │
│ │ (WebSocket/HTTP) │ │
│ └────────────┬────────────┘ │
└─────────────────────────────────┼───────────────────────────────────┘
▼
┌─────────────────────────┐
│ Mac Mini Edge Agent │
│ (iMessage delivery) │
└─────────────────────────┘
Components¶
1. Reminder Service (app/scheduler/reminder_service.py)¶
Manages user reminders with support for:
- One-time reminders: Send at a specific datetime
- Daily reminders: Send at a specific time every day
- Weekly reminders: Send at a specific time on specific day(s)
Key Features: - Timezone-aware scheduling using user's stored timezone - Automatic calculation of next send time for recurring reminders - Support for "minutes before" to remind before an event
Example Usage:
from app.scheduler.reminder_service import get_reminder_service
from datetime import time
service = get_reminder_service()
# Create a weekly reminder for Monday at 1:00 PM (1 hour before 2:00 PM event)
service.create_reminder(
user_phone="+14155551234",
title="Shibuya Crossing",
reminder_type="weekly",
day_of_week=0, # Monday
time_of_day=time(14, 0), # 2:00 PM local time
minutes_before=60, # Remind 1 hour before
)
2. Background Scheduler Service (app/scheduler/background_service.py)¶
APScheduler-based service that runs scheduled jobs:
| Job | Schedule | Description |
|---|---|---|
reminder_check |
Every minute | Check and send due reminders |
morning_calendar_check |
7:30 AM daily | Analyze calendar for stress |
email_urgency_scan |
8am, 1pm, 6pm | Scan for urgent emails |
calendar_events_scan |
Every 30 min (8am-10pm) | Monitor calendar changes |
evening_prep |
5:30 PM daily | Evening preparation reminder |
travel_brain |
Every 20 min (8am-11pm) | Flight tracking and travel reminders |
cancellation_protector |
8am, noon, 4pm, 8pm | Warn about cancellation deadlines |
Enabling the Scheduler: Set environment variable:
3. User Timezone Storage¶
Users have a timezone column in the users table:
Supported Values: Any IANA timezone (e.g., America/New_York, Asia/Tokyo, Europe/London)
4. Database Schema (user_reminders table)¶
CREATE TABLE user_reminders (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_phone VARCHAR(20) NOT NULL,
persona_id VARCHAR(50) DEFAULT 'sage',
-- Content
title VARCHAR(255) NOT NULL,
description TEXT,
-- Timing
reminder_type VARCHAR(20) NOT NULL, -- one_time, daily, weekly
remind_at TIMESTAMPTZ, -- For one-time
day_of_week INTEGER, -- 0=Monday to 6=Sunday
time_of_day TIME, -- In user's local timezone
minutes_before INTEGER DEFAULT 60,
-- Linked event (optional)
linked_event_id VARCHAR(255),
linked_event_title VARCHAR(255),
-- Status
status VARCHAR(20) DEFAULT 'active', -- active, paused, completed, deleted
last_sent_at TIMESTAMPTZ,
next_send_at TIMESTAMPTZ,
send_count INTEGER DEFAULT 0,
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW()
);
Message Delivery Flow¶
- Scheduler checks for due reminders (every minute)
- ReminderService.get_due_reminders() returns reminders where
next_send_at <= NOW() - Generate message in Sage's voice
- SmartScheduler applies jitter and rate limiting
- ProactiveSender routes to edge agent
- EdgeAgentManager queues command (WebSocket or HTTP polling)
- Mac Mini Edge Agent delivers via iMessage
- ReminderService.mark_reminder_sent() updates status and calculates next occurrence
Anti-Hallucination System¶
Sage's passport now includes explicit capabilities to prevent hallucination:
{
"capabilities": {
"can_do": [
"Have conversations and provide emotional support",
"Remember things about the user",
"Search the web for information",
"Check calendar events IF user has connected Google Calendar via OAuth",
"Check emails IF user has connected Gmail via OAuth"
],
"cannot_do": [
"Set reminders or alarms (feature not yet implemented)",
"Schedule messages for later delivery",
"Send notifications at specific times"
],
"anti_hallucination": {
"rule": "NEVER claim to have done something you cannot do",
"examples_of_violations": [
"BAD: 'okie setting ur weekly schedule~'",
"BAD: 'mmk connected! setting reminders'"
]
}
}
}
Note: When the reminder system is fully integrated with Sage's conversation flow, update the can_do list to include reminder capabilities.
Configuration¶
Environment Variables¶
| Variable | Default | Description |
|---|---|---|
ENABLE_PROACTIVE_SCHEDULER |
false |
Enable background scheduler |
Railway Deployment¶
To enable proactive messages in Railway:
- Go to Railway dashboard
- Select the service
- Click "Variables"
- Add:
ENABLE_PROACTIVE_SCHEDULER=true - Redeploy the service
Testing¶
Test Reminder Creation¶
from app.scheduler.reminder_service import get_reminder_service
from datetime import time
service = get_reminder_service()
# Create a test reminder for next minute
from datetime import datetime, timedelta
import pytz
reminder = service.create_reminder(
user_phone="+14155551234",
title="Test reminder",
reminder_type="one_time",
remind_at=datetime.now(pytz.UTC) + timedelta(minutes=2),
minutes_before=0,
)
print(f"Created reminder: {reminder}")
Check Due Reminders¶
from app.scheduler.reminder_service import get_reminder_service
service = get_reminder_service()
due = service.get_due_reminders()
print(f"Due reminders: {due}")
Future Enhancements¶
- Natural Language Reminder Parsing - Allow users to say "remind me tomorrow at 3pm"
- Calendar Integration - Automatically create reminders for calendar events
- Snooze Support - Allow users to snooze reminders
- Reminder Preferences - Let users customize reminder timing and frequency
- Group Reminders - Reminders that notify multiple users in a group