Todos & the wake loop
Every session has a built-in todo list. It's not just a notepad — open todos drive the agent forward on their own, so a single request can turn into a multi-step job the agent works through without you prodding it each turn.
What a todo is
A todo belongs to one session and has a status:
| Status | Meaning |
|---|---|
Open | Actionable — still drives the wake loop below. |
Done | Completed and closed. |
Blocked | Held, with a reason explaining why work can't proceed. |
The skills
The agent manages its own list with three skills:
todo_write— add one or more items (items: string[]). Returns each new item's id.todo_list— list this session's todos with ids and statuses (blocked items show their reason).todo_update— close an item:todo_update(id, "done"), ortodo_update(id, "blocked", reason).
How todos reach the agent
Todos are deliberately not in the system prompt. They change every turn, and putting per-turn data in the system prompt would poison the model's prompt cache (a real cost and latency hit). Instead, when a session has open todos the runtime injects a short nudge as a user message — and it includes the live list inline, so the agent can act immediately without spending a todo_list round-trip first:
You still have open todos — keep working and close each with todo_update(id, "done") as you finish: [1842] Roll back the last payments deploy [1843] Confirm the queue drained [1844] Post-mortem note in the wiki (blocked: waiting on the on-call to confirm root cause) (todo_list shows the full list at any time.)
Each item carries its id (pass it to todo_update) and blocked items show their reason. todo_list is still there to re-read the list on demand, but the common path spends no extra call.
The wake loop
When a turn finishes and the session still has open todos and isn't parked waiting on something, the session re-enters automatically — no external input needed. It gets the nudge and runs another turn. That's what lets an agent grind through a checklist end-to-end.
This is bounded so a session can't spin forever:
- Wake budget. Each activation gets up to
maxWakeCyclesre-entries (default 25). - Shared budget. The same budget also covers the reply-nudge and subagent-completion nudges, so they can't fight each other into a loop.
- Graceful stop. If the budget runs out with todos still open, the session parks
Idlerather than spinning — it resumes when fresh input arrives (which resets the budget).
Todos and session state
Todos shape how a session yields. With open todos, a yielding session goes Idle (there's still work to pick back up). With none open, it goes Dormant — fully asleep until a wake source (a human message, a peer reply, a cron kick) arrives. See session states.
todo_write a plan up front, then let the wake loop carry it through the items — closing each with todo_update — and only message you when it's done or blocked.