Service Scheduling
Common delayed and scheduled execution for service actions
Service Action Scheduling
Downcity now supports one-shot scheduling options for any service action:
--delay <ms>: delayed execution--time <time>: scheduled execution
This applies to:
city chat sendcity memory searchcity service command <service> <action>- and the dedicated action API
/service/<service>/<action>
The goal is simple: if you only need to run one existing action later, you do not need to create a task.
Good Fits
Use this when you want to:
- send a chat message 10 minutes later
- run a memory flush at 21
- execute one service action tomorrow morning
Do not use it for:
- recurring schedules
- cron
- longer workflows
- long-lived jobs that need richer task artifacts
For those, use city task.
Two Entry Points
There are now two user-facing entry points, and both go through the same persistent scheduler:
- CLI action commands
city chat send --chat-key ctx_xxx --text "remind me tonight" --time "2026-03-25T21:00:00+08:00"
city memory flush --session-id telegram-chat-123 --delay 600000- HTTP action API
POST /service/chat/send
Content-Type: application/json
{
"chatKey": "ctx_xxx",
"text": "remind me tonight",
"time": "2026-03-25T21:00:00+08:00"
}No matter which entry you use, Downcity writes the scheduled job first and executes it later.
Parameter Formats
--delay <ms>
Use a non-negative integer in milliseconds.
Example:
city chat send --chat-key ctx_xxx --text "send in 10 seconds" --delay 10000--time <time>
Supported formats:
- Unix timestamp in seconds or milliseconds
- timezone-aware ISO datetime
Examples:
city chat send --chat-key ctx_xxx --text "send at 9pm" --time "2026-03-25T21:00:00+08:00"
city memory flush --session-id telegram-chat-123 --time 1774443600Notes:
- ISO datetime must include a timezone offset such as
+08:00orZ --delayand--timecannot be used together
Persistence Location
Scheduled jobs are stored in the current agent project at:
.downcity/schedule.sqliteThis means:
- the runtime can return immediately after accepting the request
- unfinished jobs survive runtime restart
- the next runtime start restores and continues them
Runtime Behavior
A scheduled service action moves through these states:
pending: accepted and waitingrunning: picked up by the scheduler and currently executingsucceeded: execution finished successfullyfailed: execution finished with an errorcancelled: cancelled and no longer executed
Restart recovery rules:
- if a job was still
pending, it will be resumed later or executed immediately if overdue - if a job was left in
running, startup resets it back topendingand retries it
Return Shape
When a command is accepted as a scheduled job, the response looks like this:
{
"success": true,
"data": {
"scheduled": true,
"jobId": "sched_xxxxxxxxxxxxxxxx",
"runAtMs": 1774443600000,
"status": "pending"
}
}This means:
- the action was not executed immediately
- a persistent scheduled job was created
jobIdis the unique identifier for that scheduled record
Examples
1. Delay a chat send
city chat send \
--chat-key ctx_xxx \
--text "remind me in 10 minutes" \
--delay 6000002. Schedule a memory flush
city memory flush \
--session-id telegram-chat-123 \
--time "2026-03-25T23:00:00+08:00"3. Schedule through the generic command bridge
city service command chat send \
--payload '{"chatKey":"ctx_xxx","text":"send later"}' \
--time "2026-03-25T21:00:00+08:00"4. Schedule through the HTTP action API
curl -X POST http://127.0.0.1:5314/service/chat/send \
-H 'content-type: application/json' \
-d '{
"chatKey": "ctx_xxx",
"text": "send later",
"time": "2026-03-25T21:00:00+08:00"
}'Difference From task
Think of the boundary like this:
- service action scheduling: add one-shot
delay/timeto an existing action task: define a reusable long-lived automation task
If your need is “run this existing action once, later”, use this scheduling feature.
If your need is “define an ongoing automation job”, use city task.
Inspection And Management Commands
Besides creating scheduled jobs, you can now inspect and manage the local schedule store directly.
Use:
city service schedule <subcommand>Currently supported:
listinfo <jobId>cancel <jobId>
These commands read the current agent project's .downcity/schedule.sqlite directly, so they do not require the runtime to be currently online.
list
List scheduled jobs.
Key options:
--status <pending|running|succeeded|failed|cancelled>: filter by status--limit <n>: limit result size--path <path>: target agent project path--agent <name>: resolve agent via console registry
Examples:
city service schedule list --path /abs/path/to/agent
city service schedule list --status pending --limit 20 --path /abs/path/to/agentinfo <jobId>
Show one scheduled job in detail.
Returned fields include:
- service/action
- payload
- runAtMs
- status
- error (when present)
Example:
city service schedule info sched_xxxxxxxxxxxxxxxx --path /abs/path/to/agentcancel <jobId>
Cancel a job that has not started yet.
Rules:
- only
pendingjobs can be cancelled running/succeeded/failed/cancelledjobs are not cancelled again
Example:
city service schedule cancel sched_xxxxxxxxxxxxxxxx --path /abs/path/to/agentTroubleshooting Flow
If one delayed or scheduled action did not behave as expected, a simple flow is:
- Check the lists
city service schedule list --status pending --path /abs/path/to/agent
city service schedule list --status failed --path /abs/path/to/agent- Inspect one job
city service schedule info <jobId> --path /abs/path/to/agent- Cancel a pending job if you want to stop it before execution
city service schedule cancel <jobId> --path /abs/path/to/agent