| name | scheduler |
| description | Schedule one-time or recurring tasks (reminders, checks, reports). A sub-agent with full tool access executes the instruction at the scheduled time. Tools: scheduler. Always use datetime tool for trigger_at_ms. |
Scheduler Skill
Schedule complex tasks for the future. At the scheduled time, a sub-agent (mini AI) runs in the background and executes the stored instruction with all available tools (SMS, HTTP, TTS, beep, Intent, etc.).
Use the scheduler tool for:
- Tasks that need execution (e.g. "send SMS", "remind me to call", "check calendar", "play alarm and speak")
- Recurring tasks (daily, weekly, interval)
Examples: "Remind me in 10 minutes to call Peter", "Send SMS tomorrow at 9 AM", "Check calendar every day at 8 AM"
NOT suitable for: Simple countdown timers that only need an acoustic alarm. Use the timer tool instead for timers that just play a beep when time is up.
Tool: scheduler
Create a schedule
{
"action": "create",
"label": "{OPTIONAL_USER_FRIENDLY_HEADLINE}",
"instruction": "{NATURAL_LANGUAGE_INSTRUCTION}",
"trigger_at_ms": {TIMESTAMP_MS},
"recurrence_type": "once"
}
The label is an optional user-friendly headline for the schedule (e.g. "Morning reminder", "Daily calendar check"). If provided, it will be displayed prominently in the UI instead of the instruction text.
The instruction is the command the sub-agent will execute. Write it as if you were giving Sanna the command directly. The sub-agent has access to all tools (SMS, HTTP, TTS, beep, Intent, etc.).
IMPORTANT: The instruction must contain everything the sub-agent needs. It has no context of the current conversation. For example, include the phone number directly rather than "Peter's number".
Recurring schedules
Interval (every X minutes/hours)
{
"action": "create",
"instruction": "{INSTRUCTION}",
"trigger_at_ms": {FIRST_TRIGGER_MS},
"recurrence_type": "interval",
"recurrence_interval_ms": {INTERVAL_IN_MS}
}
Example "every 30 minutes": Use datetime tool with action: "interval", amount: 30, interval_unit: "minute" → returns 1800000 (use as recurrence_interval_ms)
Daily
{
"action": "create",
"instruction": "{INSTRUCTION}",
"trigger_at_ms": {NEXT_TRIGGER_MS},
"recurrence_type": "daily",
"recurrence_time": "08:00"
}
Weekly
{
"action": "create",
"instruction": "{INSTRUCTION}",
"trigger_at_ms": {NEXT_TRIGGER_MS},
"recurrence_type": "weekly",
"recurrence_time": "09:00",
"recurrence_days_of_week": [1, 3, 5]
}
Days of week: 1=Mon, 2=Tue, 3=Wed, 4=Thu, 5=Fri, 6=Sat, 7=Sun
List all schedules
{
"action": "list"
}
Get a single schedule
{
"action": "get",
"schedule_id": "{ID}"
}
Update a schedule
{
"action": "update",
"schedule_id": "{ID}",
"label": "{NEW_LABEL}",
"instruction": "{NEW_INSTRUCTION}",
"trigger_at_ms": {NEW_TIMESTAMP}
}
Only the specified fields are changed; the rest remains the same.
Enable or disable a schedule
{
"action": "update",
"schedule_id": "{ID}",
"enabled": true
}
Set enabled: false to temporarily disable a schedule without deleting it. Disabled schedules are not executed.
Delete a schedule
{
"action": "delete",
"schedule_id": "{ID}"
}
Disable a schedule (pause)
{
"action": "disable",
"schedule_id": "{ID}"
}
Enable a schedule
{
"action": "enable",
"schedule_id": "{ID}"
}
Time calculation
IMPORTANT: trigger_at_ms is a Unix timestamp in milliseconds. The trigger must be at least 1 minute in the future.
Workflow for time calculation
For relative times ("in 5 minutes", "in an hour", "in 2 weeks"):
- Use
datetime tool with action: "add", base: "now", amount: {N}, unit: "{unit}"
- "in 5 minutes":
datetime add with base: "now", amount: 5, unit: "minute"
- "in an hour":
datetime add with base: "now", amount: 1, unit: "hour"
- "in 2 weeks":
datetime add with base: "now", amount: 2, unit: "week"
For absolute times ("tomorrow at 9", "next Tuesday at 8 PM", "next January", "on 2024-03-15"):
- Use
datetime tool with action: "absolute" — this returns the exact Unix timestamp. Do NOT calculate manually.
- Parameters:
base: Enum value ("today", "tomorrow", "yesterday", "next_monday"–"next_sunday", "next_january"–"next_december"), Unix timestamp in milliseconds (number), or ISO date/datetime string (e.g. "2024-03-15", "2024-03-15T14:00:00", "2024-03-15T14:00:00Z")
time (optional): "HH:MM" or "HH:MM:SS" (e.g. "09:00", "20:00")
anchor (optional): "beginofday", "endofday", "beginofweek", "endofweek", "beginofmonth", "endofmonth"
output_unit: "milliseconds" (default) or "seconds"
- Use the returned timestamp directly as
trigger_at_ms.
For interval calculations (for recurrence_interval_ms):
- Use
datetime tool with action: "interval", amount: {N}, interval_unit: "{unit}"
- Returns milliseconds (use directly as
recurrence_interval_ms)
- Units:
"millisecond"/"ms", "second", "minute", "hour", "day", "week", "month", "year"
Examples
- "in 5 minutes" →
datetime add with base: "now", amount: 5, unit: "minute" → use returned timestamp as trigger_at_ms
- "tomorrow at 9" →
datetime absolute with base: "tomorrow", time: "09:00", output_unit: "milliseconds"
- "next Tuesday at 8 PM" →
datetime absolute with base: "next_tuesday", time: "20:00", output_unit: "milliseconds"
- "in two weeks at noon" →
datetime add with base: "now", amount: 2, unit: "week", then datetime absolute with base: {result}, time: "12:00" OR use datetime add with base: "now", amount: 2, unit: "week", time: "12:00"
- "next month at 10 AM" →
datetime absolute with base: "next_january" (or current month + 1), time: "10:00", output_unit: "milliseconds"
- "every 30 minutes" →
datetime interval with amount: 30, interval_unit: "minute" → returns 1800000 → use as recurrence_interval_ms
CRITICAL: For any date/time calculations, ALWAYS use the datetime tool. Do NOT compute timestamps manually — this causes calculation errors.
Writing instructions
The sub-agent is a standalone agent with no conversation context. The instruction must be self-explanatory:
Good:
"Speak via TTS: Attention, the roast needs to come out of the oven!"
"Use the beep tool to play an alarm tone (tone: alarm, count: 5), then speak via TTS: Timer finished!"
"Send an SMS to +4366012345678 with the text: I'll be there in 10 minutes"
"Fetch the Google Calendar events and read the next 3 appointments via TTS"
Bad:
"Send Peter an SMS" (Who is Peter? What should the SMS say?)
"Remind me" (About what? Context is missing.)
For contacts: Look up the number first with the query tool and include it directly in the instruction.
Workflows
"Remind me in 5 minutes about the roast"
datetime: add with base: "now", amount: 5, unit: "minute" → get timestamp
scheduler: create with:
instruction: "Use the beep tool (tone: alarm, count: 5). Then speak via TTS: The roast needs to come out of the oven!"
trigger_at_ms: timestamp from step 1
recurrence_type: "once"
- Confirm to the user: "I'll remind you in 5 minutes."
"Send an SMS to Peter at 2 PM: on my way"
query: Look up contact "Peter" → get number
datetime: absolute with base: "today", time: "14:00", output_unit: "milliseconds" → get exact timestamp
scheduler: create with:
instruction: "Send an SMS to +4366012345678 with the text: On my way"
trigger_at_ms: timestamp from step 2
recurrence_type: "once"
"Read my appointments every morning at 8"
datetime: absolute with base: "tomorrow", time: "08:00", output_unit: "milliseconds" → get exact timestamp
scheduler: create with:
instruction: "Fetch my Google Calendar events for today and read them via TTS"
trigger_at_ms: timestamp from step 1
recurrence_type: "daily"
recurrence_time: "08:00"
"Announce the time every minute"
datetime: add with base: "now", amount: 1, unit: "minute" → get first trigger timestamp
datetime: interval with amount: 1, interval_unit: "minute" → get interval in ms (60000)
scheduler: create with:
instruction: "Speak via TTS: The current time is [current time]"
trigger_at_ms: timestamp from step 1
recurrence_type: "interval"
recurrence_interval_ms: interval from step 2
- Confirm to the user: "I'll announce the time every minute, starting in 1 minute."
"What do I have scheduled?"
scheduler: list
- Report results to user (describe by instruction and time, NOT by ID)
"Delete the alarm / reminder"
scheduler: list → find the relevant schedule
scheduler: delete with the found ID
"Pause the daily calendar check"
scheduler: list → find the schedule
scheduler: disable with the ID
Important rules
- Minimum 1 minute: The scheduler only works for tasks at least 1 minute in the future. For immediate sounds, use the
beep tool directly.
- Never show IDs: Schedule IDs are internal. When talking to the user, describe schedules by their task and time (e.g. "your roast reminder at 3:15 PM"), never by ID.
- Reminders: Use
beep + tts in the instruction so the user hears both a tone and the message.
- Schedules survive app restarts and device reboots.
- The sub-agent has the same tools as the main app (except the scheduler itself).
- SMS instructions require the phone number (not the contact name).
- Intervals under 1 minute are not allowed.
- The app must have been launched at least once for schedules to work.