English | 中文
The model-facing todo_write tool: the agent's whole task list, replaced wholesale on each call.
Registers one tool, todo_write(todos: [{ content, status }]), on ctx.tools. The model sends the ENTIRE list every call — there are no partial updates or per-item edits. Each call appends a todo/write event (the full list snapshot) to the calling agent's session log via agent.session.append('todo/write', { todos }); the current list is the most recent such event (last-write-wins on replay).
status is one of pending, in_progress, or completed.
The list belongs to the ONE agent session that called the tool. There is no subagent/shared/swarm scope: a non-agent caller (no exec.agent) has nowhere to write the list and is rejected. This is a deliberate scope limit — see the Agent Note.
Beyond the schema's type/required/enum checks, execute rejects an empty or duplicate content, more than one in_progress task (a coherent plan has at most one task active), and any item key beyond content/status — an extended item shape (ids, nesting) fails loud instead of silently flattening, keeping the logged snapshot equal to what the model believes it wrote. Ordering and the discipline of keeping the list current are left to the model via the tool description.
The canonical result is { todos, counts: { pending, inProgress, completed } }; its Native renderer returns the compact update acknowledgement. The tool also writes the full todo/write session event. UIs subscribe to the event stream and render that durable list themselves: the web client shows a plan strip plus a dedicated tool row off the standing plan — latest todo/write with no later turn/start (display, lifetime).
When the composition mounts ctx.sessionProjections (@deepseek-ai/dsh-session-projection), this package registers the todos projection unit under an injected child: init = null (no write yet), apply = take the whole list from each todo/write and clear to null on each turn/start (standing plan; turn/end keeps the finished checklist; every other event returns the same state reference), view = identity, stateVersion = 2. The key merges into SessionProjectionMap here (via the interface package's /types outlet); the framework drives the unit and carriers serve the value on the history tail page and the session/projection push frame. Compositions without the registry are unaffected. Lifetime rationale: todo plan clears on next turn.
A function/namespace plugin: it exports name / inject / apply and NO default. A stray export default would collapse the module via the Loader's unwrapExports and drop inject (see docs/postmortem/0001).
The model sees the generated todo_write schema.
Fixed schema cost on every request where the tool is visible.
Prefix-stable while the definition and visibility are unchanged. Plugin lifecycle or scoped restrictions may invalidate reuse from this schema.
Each assistant tool call retains the entire replacement list in its arguments. Success returns exactly Updated todo list: <pending> pending, <inProgress> in progress, <completed> completed. Stable failures are Error: invalid todo: `content` must be a non-empty string, Error: invalid todos: duplicate content "<content>", Error: invalid todos: at most one task may be in_progress, got <count>, and Error: todo_write requires an owning agent session. The full todo/write session event is UI and replay state, not a second model message.
Token growth scales with every full list the model submits, and those call arguments remain until compaction. The result itself is small and fixed-shape.
Append-only; newly visible content follows the reusable request prefix and does not invalidate existing KV-cache entries.
content plus three-state status; whole-list replacement needs no stable id, priority, or active-form fields.