Add @deepseek-ai/dsh-tool-todo (a new packages/todo/ group): a model-facing
todo_write(todos: [{content, status}]) tool with whole-list-replace semantics.
Each call appends the full list as a todo/write event to the calling agent's
session log; the current list is the most recent such event (last-write-wins).
Single-owner — a non-agent caller is rejected. Beyond the schema's
type/required/enum checks, execute rejects empty/duplicate content and more than
one in_progress task, narrowing the loosely-typed args into a real TodoItem[].
Both UIs render off the existing session/event: the stdio UI prints a glyphed
checklist; the ACP bridge maps the list to a `plan` sessionUpdate (todosToPlan
synthesizes the priority ACP requires; status maps 1:1). Wired into the
coding-agent, acp-agent, and snapshot example configs with a system-prompt nudge.
Tests: unit (schema, validation, append/replace, no-agent rejection, presentCall,
HMR-safety, Loader export-shape guard), full-loop integration through the agent
loop, the ACP todosToPlan mapping + stream-update arm, the stdio render arm, and
a session/load replay that re-emits the plan. New-group TS wiring added to
tsconfig.base/json/build. RFC + a doc-inventory sweep (architecture, packages
README, AGENTS layout, cookbook group list, example READMEs) ship with it.
The todo-plan ACP snapshot scenario is recorded separately (needs an API key).
1.8 KiB
@deepseek-ai/dsh-tool-todo
The model-facing todo_write tool: the agent's whole task list, replaced wholesale on each call.
What it does
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, completed — exactly the ACP PlanEntryStatus triple.
Single owner
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 RFC.
Validation
Beyond the schema's type/required/enum checks, execute rejects an empty or duplicate content and more than one in_progress task (a coherent plan has at most one task active). Ordering and the discipline of keeping the list current are left to the model via the tool description.
Rendering
The tool writes only the session event; it does not render. UIs subscribe to session/event and render the todo/write data themselves: the stdio UI prints a glyphed checklist, and the ACP bridge maps the list to a plan sessionUpdate (synthesizing the priority ACP requires).
Export shape
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).