Skip to content

Scheduling

Talon’s scheduler lets agents create, manage, cancel, and delete durable scheduled tasks. Schedules are stored in SQLite, survive daemon restarts, and run through the normal message queue.

Context matters here: use Talon’s schedule_manage host tool for anything you want to keep. Native model-side cron helpers such as CronCreate and CronDelete are session-bound and disappear when the conversation ends.

Grant the schedule.manage:own capability to the persona:

personas:
- name: assistant
capabilities:
allow:
- schedule.manage:own
- channel.send:my-telegram

The agent manages schedules through the schedule_manage host tool.

ParameterTypeDescription
actioncreate, update, cancel, delete, listOperation to perform
scheduleIdstringRequired for update, cancel, and delete
cronExprstringRequired for create; optional for update
labelstringHuman-readable label
promptstringInline prompt text
promptFilestringPrompt alias from the persona’s prompts/ directory (resolved at fire time)

Rules that matter:

  • Schedules are cron-based
  • cancel disables a schedule without deleting it
  • delete removes it permanently
  • prompt and promptFile are mutually exclusive
  • promptFile is a prompt alias such as daily-summary, not a filesystem path
  • When an agent creates a schedule via schedule_manage, it runs on a dedicated thread scoped to that persona and channel (see Dedicated schedule threads below)

Example agent-side call:

{
"action": "create",
"cronExpr": "0 8 * * 1-5",
"label": "Morning briefing",
"prompt": "Check my open tasks and send a short morning briefing."
}
Terminal window
npx talonctl add-schedule \
--persona assistant \
--channel my-telegram \
--cron "0 9 * * 1-5" \
--label "Morning briefing" \
--prompt "Good morning. Please summarize what I should focus on today."
# Or use a prompt file (resolved at fire time)
npx talonctl add-schedule \
--persona assistant \
--channel my-telegram \
--cron "0 9 * * 1-5" \
--label "Morning briefing" \
--prompt-file daily-summary
npx talonctl list-schedules
# Permanently delete one schedule
npx talonctl remove-schedule <schedule-id>

The scheduler tick interval controls how often due schedules are checked. Default is 5 seconds.

scheduler:
tickIntervalMs: 5000

Reduce this for near-real-time scheduling. For low-traffic deployments, 30 seconds or more is fine.

For recurring prompts, store the prompt in the persona’s prompts/ directory and reference it by alias:

personas/assistant/prompts/daily-summary.md
Check my open tasks and recent messages. Summarize:
- What's pending from yesterday
- What's due today
- Any blockers or things that need my attention
Keep it short.

The matching host-tool call uses promptFile: "daily-summary". Talon resolves that alias when the schedule fires, so you can update the prompt file without changing the schedule row.

talonctl add-schedule supports --prompt-file as an alternative to --prompt. The value is a basename (no .md extension) in the persona’s prompts/ directory. The scheduler resolves it to file content at fire time.

Terminal window
npx talonctl add-schedule \
--persona assistant \
--channel my-telegram \
--cron "0 9 * * 1-5" \
--label "Morning briefing" \
--prompt-file daily-summary

--prompt and --prompt-file are mutually exclusive. The scheduler stores the alias in the schedule payload and reads the file each time the schedule fires, so edits to the prompt file take effect on the next run without touching the schedule row.

If a schedule was created with --prompt "file:daily-summary" (or file:///daily-summary), the scheduler detects the file: prefix at fire time, promotes it to a proper promptFile alias, and logs a warning. The schedule still works — no manual migration needed — but you should update the row to use the explicit promptFile field for clarity.

When a schedule is created — whether via schedule_manage or talonctl add-schedule — Talon assigns it a dedicated thread scoped to the persona and channel combination. Each scheduled run executes in isolation from live conversations.

This matters for two reasons:

  1. No double responses. Previously, scheduled runs reused the live conversation thread, which caused the agent to reply both in its dedicated thread and in the original channel. Dedicated threads fix this by isolating execution.
  2. Clean observational memory. Scheduled runs no longer pollute the live thread’s memory log, session resumption ID, or recent-message replay. The agent processes the scheduled prompt with a fresh context.

The dedicated thread records the origin chat’s external_id in its metadata so that channel.send still delivers notifications to the right place. List, update, cancel, and delete operations remain persona-scoped, so schedules stay visible and editable from the live chat where the agent was invoked.

  1. The scheduler checks for schedules whose nextRunAt is due.
  2. Due schedules are enqueued like normal messages for the target persona and channel.
  3. The agent processes the prompt through the normal queue and tool pipeline.
  4. After a successful run, Talon computes the next cron fire time and updates the row.

Scheduled tasks compete with user messages in the same FIFO queue. They don’t preempt active conversations.