Scheduled work
The scheduler wakes an agent when nobody is talking to it. Four trigger shapes, one job table.
| Cron | a crontab expression — 0 9 * * * |
| Every | an interval — 15m, 2h |
| At | once, at a time — 2h from now, or a timestamp |
| Webhook | when a URL is called, rather than on a clock |
The dashboard’s Schedule page covers the first three and Webhooks the fourth. They are the same jobs seen from the two sides that matter: what fires on a clock, and what fires when something calls it.
Schedule stays daemon-wide rather than per agent, even though every job has an owner, because you usually want to see everything scheduled at once.
From the CLI
nouride cron # what is scheduled and enablednouride cron --all # including disabled onesnouride cron run <id> # fire it nownouride cron runs <id> # its historynouride cron enable|disable <id>nouride cron rm <id>Creating one
nouride cron add --name "morning-brief" \ --cron "0 9 * * *" --tz Asia/Jakarta \ --agent nouva \ --gateway telegram:nouva --chat 123456789 \ --message "Summarise anything that changed overnight and anything waiting on me."| Flag | |
|---|---|
--name | what it is called |
--cron "0 9 * * *" / --every 15m / --at 2h | pick exactly one |
--tz <zone> | which clock the schedule is read against |
--agent <id> | which agent owns it |
--gateway <id> --chat <id> | required — where the result goes |
--message <prompt> | wake the agent with this |
--text <message> | deliver plain text instead. No model call, no cost |
--command <shell> | run a command. No model call |
--then agent | …and have the agent read its output |
--session <fresh|isolated|chat> | what history the turn sees |
--quiet | do not announce the run |
--keep | keep a one-shot after it fires |
nouride cron update <id> takes --name, the schedule flags, --tz, --text and --message.
Three kinds of job, and the cost of each
--text is a plain reminder. Nothing calls a model, so it costs nothing. Reach for it whenever
the message is the whole point.
--command runs a shell line and delivers the output. Also no model call. --then agent adds
one: the agent reads the output and reports on it, which is the difference between “here is the disk
usage” and “the disk is filling up, here is what is eating it”.
--message wakes the agent with a prompt. One turn, with tools, at the usual cost.
What history the turn sees
--session is the setting people most often want and least often find.
fresh (default) | no history. The agent starts clean every time |
isolated | a private, persistent thread for this job — it remembers its own past runs, and nothing else |
chat | the target chat’s own conversation, as if you had typed the prompt there |
Webhooks
An endpoint that runs a job when something calls it.
nouride webhook # what exists, and its URLnouride webhook add --name "deploy-finished" \ --agent nouva \ --gateway telegram:nouva --chat 123456789 \ --message "A deploy just finished. Check the health endpoint and report."nouride webhook rotate <id> # issue a new URL; the old one stops workingUse nouride cron for rm, enable, disable, run and runs — they are the same jobs.
There is deliberately no --command for a webhook. A webhook is reachable by anything that
learns its URL; an endpoint that runs argv on the host is a remote shell with a long password.
Guardrails
These exist because a model that can create jobs can create them in a loop.
min_interval_ms (60s) | the floor on a recurring schedule. The difference between an agent scheduling a check and an agent scheduling a loop of provider calls |
max_jobs (100) | across every agent |
fail_streak_limit (10) | consecutive failures before a job disables itself and says why. nouride cron enable <id> clears it |
catchup_window_ms (1h) | how late an overdue job may be and still fire when the daemon comes back. Outside it, a recurring job moves to its next occurrence and a one-shot is recorded as missed — a daemon down for three days should not open with three days of reminders |
run_retention_days (14) | how long run history is kept |
command_timeout_ms (10m) | wall-clock ceiling on a --command payload |
job_wake_max_chain (100) | how many times a finished background job may wake an agent with nobody having said anything in between |
That last one is worth a sentence. A job finishing wakes its agent, that turn often starts the next step, and its completion wakes it again — a chain with no human in it, each hop a provider call. It is set high on purpose: a nine-step install is nine jobs, and that chain is legitimate. At the limit the wake-up is still delivered and the agent is told to report and start nothing further; suppressing it would hide exactly the failures this guards against. Any message from a person resets the count.
Approvals at 3am
A scheduled turn that hits an approval is auto-approved by default and the decision is recorded in
the audit log against the job, because a job that stops at a prompt nobody will see has not run at
all. [cron] unattended_approval = "deny" makes it fail honestly instead.
Either way the hardline classes still ask, and still fail closed when nobody answers. Approvals →
Background jobs are a different thing
nouride jobs lists work an agent started during a turn — a long build, a slow script — not
anything scheduled.
nouride jobsnouride jobs log <id>nouride jobs kill <id>When one finishes it wakes the agent that started it, which is the chain job_wake_max_chain bounds.