Scheduled Tasks
Scheduled tasks are per-instance jobs that fire on a schedule (or on demand) and run a prompt through the supervisor as if a user had sent a message. Use them for recurring digests, polling integrations that don’t push webhooks, periodic clean-up prompts, or one-shot reminders.
No top-level sidebar entry. Scheduled tasks are not exposed at
/scheduled-tasksor anywhere in the global sidebar. They live only inside the instance detail page, under Triggers → Scheduled. To see all scheduled tasks across the system you have to open each instance in turn.
The UI lives inside the instance detail page, under Triggers → Scheduled. (Webhooks live next door under Triggers → Webhooks, runs history under Triggers → Runs.) Each row is one task with inline controls for enable/disable, edit, Run now, and delete.
What a task looks like
A task carries enough state to run autonomously and report back. The fields below mirror scheduled_tasks in packages/engine/src/scheduled-tasks/schema.ts.
| Field | Type | Notes |
|---|---|---|
name | string | Operator-facing label. |
description | string (optional) | Free-text reminder of what the task does. |
prompt | string | The user-message text sent to the supervisor when the task fires. |
schedule | discriminated union (cron / interval / one-shot) | See below. |
enabled | boolean | Master switch. Disabled tasks never tick. |
outboundChannel | enum (optional) | slack, telegram, whatsapp, or unset. Where the answer is sent. |
outboundTarget | string (optional) | Channel id or user id within outboundChannel. |
keepHistory | boolean | If false, each run uses a fresh conversation id (no memory carry-over). |
deleteAfterRun | boolean | One-shot tasks delete themselves after success. |
maxRetries | int (default 3) | Auto-disable after this many consecutive errors. |
Schedule shape
type ScheduleConfig =
| { type: "cron"; expression: string; timezone?: string }
| { type: "interval"; everyMs: number; anchorAt?: string }
| { type: "one-shot"; runAt: string };- Cron uses standard 5-field POSIX cron, optionally timezone-anchored.
- Interval ticks every
everyMsfromanchorAt(or task creation time). - One-shot fires once at the given ISO timestamp. Combine with
deleteAfterRun: truefor fire-and-forget reminders.
Lifecycle
- The scheduler service ticks every 30 s (
TICK_INTERVAL_MS = 30_000,packages/engine/src/scheduled-tasks/scheduler.service.ts:26). - On each tick it claims up to
MAX_CONCURRENT = 3tasks whosenextRunAtis in the past, in priority order, and runs them in parallel. - Each fire emits a
cronevent on the activity bus before the supervisor runs — useful as a heartbeat in the Activity feed. - The supervisor processes the prompt exactly as if it were a user message. The conversation id is either reused (
keepHistory: true) or freshly minted (keepHistory: false). - The reply, if any, is delivered to
outboundChannel/outboundTargetwhen configured. Otherwise it stays in the conversation for browsing. - The run is recorded in
scheduled_task_runs(run_id,started_at,completed_at,duration_ms,status,output,error,tool_calls,token_usage). - On error,
consecutive_errorsincrements. AftermaxRetriesfailures the task is auto-disabled and shows the error badge in the UI.
API
All endpoints are scoped to an instance — replace :slug with the instance slug.
| Method | Path | Description |
|---|---|---|
| GET | /api/instances/:slug/scheduled-tasks | List tasks for the instance. |
| POST | /api/instances/:slug/scheduled-tasks | Create a task. |
| GET | /api/instances/:slug/scheduled-tasks/:id | Get one task (full state). |
| PATCH | /api/instances/:slug/scheduled-tasks/:id | Update fields (name, prompt, schedule, enabled, …). |
| DELETE | /api/instances/:slug/scheduled-tasks/:id | Remove the task and cascade-delete its runs. |
| POST | /api/instances/:slug/scheduled-tasks/:id/run | Trigger an immediate execution (triggerType: manual). |
| GET | /api/instances/:slug/scheduled-tasks/:id/runs | Paginated run log for a single task (most recent first). |
| GET | /api/instances/:slug/scheduled-tasks/runs | Cross-task global run log for the instance (no :id). |
Worked example: daily digest at 09:00 Europe/Rome
- Open the instance detail page → Triggers → Scheduled → New task.
- Name: Morning digest.
- Schedule:
cron, expression0 9 * * *, timezoneEurope/Rome. - Prompt: “Search the last 24 hours of memories and summarise the three most important events. Be terse.”
- Outbound channel:
slack, target:C012345678. - Enable, save.
The next morning at 09:00 Rome time you’ll see one row in the Activity feed (cron), one tool call (searchMemory), one outbound (slack), and one entry in the Runs subtab.
Caveats
- Tick granularity is 30 s. Don’t expect sub-minute accuracy.
- Max concurrency is 3. A burst of due tasks queues; the rest wait for the next tick.
- Engine restart resets
nextRunAtfor interval schedules. The scheduler recomputes the next fire fromlastRunAt. Cron schedules are unaffected. - Auto-disable is per-task, not global. A flaky task won’t take down its neighbours.
See also: Activity for live visibility into firings, Activity Stream (concept) for the underlying bus.