Cron & Scheduled Tasks¶
Autobot includes a built-in scheduler for recurring checks, one-time reminders, and deferred tasks. Jobs fire as full agent turns with access to all tools.
Overview¶
The cron system solves three common needs:
- Reminders — "Remind me to drink water in 30 minutes" (one-time deferred task)
- Recurring reports — "Send me a weather summary every morning at 9am" (cron expression)
- Periodic tasks — "Check my email every 5 minutes" (fixed interval)
When a job fires, it triggers a full agent turn — the agent can use MCP tools, web search, memory, and any other registered tools to complete the task. The response is automatically delivered to the user.
Schedule Types¶
Fixed Interval (every_seconds)¶
Runs repeatedly at a fixed interval.
Cron Expression (cron_expr)¶
Standard 5-field cron syntax: MIN(0-59) HOUR(0-23) DOM(1-31) MON(1-12) DOW(0-6)
Supports: * (any), ranges (9-17), steps (*/5), lists (1,15,30), combos (1-30/10), named months (jan-dec), named days (mon-sun), and shortcuts (@hourly, @daily, @weekly, @monthly, @yearly).
All values must be integers. Minimum granularity is 1 minute — for sub-minute intervals, use every_seconds.
"Send me a morning briefing at 9am"
→ cron_expr: "0 9 * * *"
"Every 5 minutes during work hours on weekdays"
→ cron_expr: "*/5 9-17 * * 1-5"
One-Time (at)¶
Runs once at a specific time, then auto-deletes.
How It Works¶
Job Execution Flow¶
graph LR
TIMER[Timer fires] --> CHECK{Job due?}
CHECK -->|yes| CALLBACK[on_job callback]
CALLBACK --> BUS[Event Bus]
BUS --> LOOP[Agent Loop]
LOOP --> LLM[LLM + Tools]
LLM --> USER[Response auto-delivered]
style TIMER fill:#ab47bc,stroke:#8e24aa,color:#fff
style BUS fill:#7c4dff,stroke:#651fff,color:#fff
style LOOP fill:#5c6bc0,stroke:#3949ab,color:#fff
style LLM fill:#26a69a,stroke:#00897b,color:#fff
style USER fill:#ffa726,stroke:#fb8c00,color:#fff
- Timer fires — The cron service detects a job is due
- Publish to bus — An
InboundMessageis published to the event bus withchannel: "system"andsender_id: "cron:{job_id}" - Agent turn — The agent loop picks up the message and executes the job's prompt
- Tool execution — The agent uses any tools needed (MCP, web search, etc.) to fulfill the task
- Explicit delivery — The agent uses the
messagetool to send results to the user (no auto-delivery)
Background Turn Restrictions¶
Cron turns use a minimal system prompt and exclude certain tools to prevent unintended behavior:
spawn— excluded to prevent background task proliferation
Cron turns never auto-deliver the final response. The agent must use the message tool explicitly to notify the user. This enables conditional delivery — the agent can stay silent when there is nothing to report (e.g., "monitor X, only notify if Y changes").
The cron tool is available so jobs can self-remove when their task defines a stop condition (e.g., "monitor X until condition Y, then stop").
Exec Payloads¶
Many monitoring and automation tasks are deterministic — check a URL, compare a value, send an alert. These don't need an LLM reasoning about what to do every time. But setting them up still requires figuring out the right shell command, cron expression, and wiring.
Exec payloads let you describe what you want in natural language — the agent writes the shell command once, and from that point on it runs directly on a schedule with zero LLM involvement. No tokens, no latency, no cost per execution.
User: "Check air quality in NYC every hour, notify me if AQI goes above 100"
The agent creates an exec job with the right
curl | jqpipeline. Every hour the command runs on its own — the LLM is never called again.
How exec differs from agent turns¶
| Agent turn | Exec payload | |
|---|---|---|
| Setup | LLM writes the prompt | LLM writes the shell command |
| Each run | Full LLM turn with tools | Shell command only |
| Token cost | Per-invocation | Zero (after setup) |
| Output | LLM-generated response | Raw stdout |
| Best for | Tasks needing reasoning | Deterministic checks and scripts |
Parameters¶
exec_command— The shell command to run (mutually exclusive withmessage)PREV_OUTPUT— Environment variable containing the previous run's stdout, enabling change detection
Execution flow¶
graph LR
TIMER[Timer fires] --> EXEC[Run command in sandbox]
EXEC --> CHECK{Output empty?}
CHECK -->|no| DELIVER[Deliver to channel]
CHECK -->|yes| SKIP[Skip silently]
style TIMER fill:#ab47bc,stroke:#8e24aa,color:#fff
style EXEC fill:#5c6bc0,stroke:#3949ab,color:#fff
style CHECK fill:#7c4dff,stroke:#651fff,color:#fff
style DELIVER fill:#ffa726,stroke:#fb8c00,color:#fff
style SKIP fill:#78909c,stroke:#546e7a,color:#fff
Exec commands run inside the sandbox (bubblewrap or Docker) by default. Set sandbox: none to run directly on the host.
Examples¶
Air quality check (only notify on change):
autobot cron add \
--name "air-quality" \
--exec 'curl -s "https://api.example.com/aqi?city=NYC" | jq -r .aqi' \
--every 3600 \
--channel telegram --to "12345"
The PREV_OUTPUT variable lets you detect changes:
autobot cron add \
--name "uptime-monitor" \
--exec 'STATUS=$(curl -so /dev/null -w "%{http_code}" https://example.com); [ "$STATUS" != "$PREV_OUTPUT" ] && echo "$STATUS" || true' \
--every 300 \
--channel slack --to "C12345"
Disk space alert:
autobot cron add \
--name "disk-check" \
--exec 'USAGE=$(df -h / | awk "NR==2{print \$5}" | tr -d "%"); [ "$USAGE" -gt 90 ] && echo "Disk usage: ${USAGE}%" || true' \
--cron "0 */6 * * *" \
--channel telegram --to "12345"
Configuration¶
Enable Cron¶
Agent Creates Jobs¶
The agent creates cron jobs via the cron tool when users make scheduling requests. The tool supports three actions:
| Action | Description | Required Parameters |
|---|---|---|
add |
Create a new job | message + one of: every_seconds, cron_expr, at |
list |
List jobs for current owner | — |
show |
Show full job details | job_id |
update |
Update schedule or message in-place | job_id + at least one of: message, every_seconds, cron_expr, at |
enable |
Resume a paused job | job_id |
disable |
Pause a job without deleting it | job_id |
remove |
Delete a job | job_id |
Telegram /cron Command¶
Send /cron in Telegram to instantly see all your scheduled jobs — no LLM round-trip needed.
Example output:
Scheduled jobs (2)
1. abc123 — Check GitHub stars
⏱ Every 10 min | ✅ 2 min ago
2. def456 — Morning briefing
🕐 0 9 * * 1-5 (UTC) | ⏳ pending
Empty state shows: "No scheduled jobs. Ask me in chat to schedule something."
Built-in Cron Skill¶
A built-in skill (src/skills/cron/SKILL.md) is automatically loaded when the cron tool is registered. It teaches the agent:
- Message quality — write self-contained prompts with specific tool names and URLs
- Timezone handling — ask the user, convert to UTC, confirm both times
- Update-first rule — list existing jobs before creating, update instead of duplicate
- Schedule type selection — when to use
every_secondsvscron_exprvsat
The skill declares tool: cron in its frontmatter, so it's included automatically whenever the cron tool is available — both in interactive and background turns.
CLI Management¶
List Jobs¶
Show Job Details¶
Add Jobs Manually¶
# Recurring interval
autobot cron add --name "standup" --message "Time for standup!" --every 3600
# Cron expression
autobot cron add --name "morning" --message "Good morning!" --cron "0 9 * * *"
# One-time
autobot cron add --name "reminder" --message "Call dentist" --at "2026-02-20T15:00:00Z"
Update a Job¶
# Change schedule
autobot cron update <job_id> --cron "0 8 * * *"
# Change message
autobot cron update <job_id> --message "New task instructions"
# Change both
autobot cron update <job_id> --every 600 --message "Updated check"
Remove a Job¶
Clear All Jobs¶
Enable/Disable¶
Force Run¶
Per-Owner Isolation¶
Jobs created via the cron tool are automatically scoped to the originating channel and chat. A Telegram user's jobs are isolated from a Slack user's jobs.
- Owner format:
channel:chat_id(e.g.,telegram:634643933) - List only shows the current owner's jobs
- Update only works on the current owner's jobs
- Remove only works on the current owner's jobs
- Jobs created via CLI have no owner restriction
Examples¶
One-Time Reminder¶
User says: "Remind me in 30 minutes to take a break"
The agent creates:
Job fires once, delivers the reminder, then auto-deletes.
Daily Report¶
User says: "Send me a weather summary every morning at 9am"
The agent creates:
cron add:
message: "Use web_search to find current weather for user's location.
Send a brief summary."
cron_expr: "0 9 * * *"
Periodic Check¶
User says: "Check my email every 5 minutes"
The agent creates:
Update Existing Job¶
User says: "Change my morning report to 8am instead"
The agent updates:
Job identity (id, created_at_ms, state) is preserved — only the schedule changes.
File Structure¶
Permissions:
cron.json:0600(user read/write only)- Parent directory:
0700(user-only access)
Troubleshooting¶
Job doesn't fire¶
Check:
- Is cron enabled in config? (
cron.enabled: true) - Is the job enabled? (
autobot cron show <job_id>) - Is the gateway running? (Jobs only fire while the gateway process is active)
- Check logs for
Cron: executing jobentries
See Also¶
- Architecture — System design and message flow
- Configuration — Full config reference
- Security — File permissions and job isolation