DSH / Atlas
2026-07-21implementedsimplificationarchived 2026-07-26

Ship the TUI without `todo_write`; keep it a one-line opt-in

Ship the TUI without `todo_write`; keep it a one-line opt-in

The shipped tui-agent `cordis.yml` loaded `@deepseek-ai/dsh-tool-todo`, exposing `todo_write` by default. The tool is a task-tracking convenience, not a core coding affordance like `bash` or the `read`/`write`/`edit` fs tools; most TUI sessions never call it, yet shipping it enlarges the wire tool list and system prompt for every turn. Meanwhile the TUI's plan rendering is event-driven: `packages/ui/tui/src/index.ts`

English

Problem

The shipped tui-agent cordis.yml loaded @deepseek-ai/dsh-tool-todo, exposing todo_write by default. The tool is a task-tracking convenience, not a core coding affordance like bash or the read/write/edit fs tools; most TUI sessions never call it, yet shipping it enlarges the wire tool list and system prompt for every turn. Meanwhile the TUI's plan rendering is event-driven: packages/ui/tui/src/index.ts listens for the todo/write session event and TodoComponent.render returns nothing when the list is empty, so the front door already tolerates the tool being absent or present with no runtime coupling to the plugin.

Decision

The tui-agent cordis.yml no longer loads tool-todo; todo_write is opt-in. The code-mode.cordis.yml overlay inherits the base composition, so its generated SDK drops todo_write too. Enabling it is one entry — add @deepseek-ai/dsh-tool-todo to cordis.yml (or a ~/.dsh personal overlay) — after which the model logs the whole-list todo/write snapshot and the TUI renders the plan, unchanged. The TodoItem type and the todo/write event stay in @deepseek-ai/dsh-session and the TUI's plan rendering stays wired, so both the default (disabled) and opt-in (enabled) paths are first-class. The sibling acp-agent, headless-agent, and jsonrpc-agent examples still ship the tool.

Alternatives considered

Keep todo_write in the shipped TUI default — rejected: it is an opt-in convenience, not a core tool, and shipping it spends every turn's tool-list and prompt budget on a feature most sessions ignore. The examples that still ship it retain the plugin's real-composition coverage.

Drop the TUI's plan rendering and todo tests along with the default entry — rejected: the requirement is to support both the enabled and disabled cases, and the event-driven TodoComponent already renders plans with zero plugin coupling, so deleting it would discard a working capability for no gain. The enabled path keeps dedicated coverage instead.

Testing

examples/tui-agent/tests/tui.snapshot.ts mounts ToolTodo only when a scenario sets enableTodo: only the todo-plan scenario does (the enabled-path proof, whose session.jsonl/terminal.expected.txt pin the rendered plan), while every other scenario runs the default todo-free composition. tests/harness.ts makes ToolTodo a todo opt-in that only tests/todo-write.e2e.ts sets, so the with-key todo e2e still drives the real tool while the other suites match the shipped stack. The keyless tests/tui-keyless-smoke.e2e.ts boots the real cordis.yml and asserts nothing about todo, so the default boot is unaffected.

Consequences

The default TUI wire tool list and system prompt shrink by one tool; a session that wants task tracking adds one plugin entry. examples/tui-agent/composition.md (regenerated) and its leaf-entry table no longer list tool-todo, and the curated summary in scripts/gen-doc-graphs.ts drops it. The @deepseek-ai/dsh-tool-todo package is unchanged and still shipped by the acp/headless/jsonrpc examples, so its coverage requirement is met there. Restoring the default would re-add the one cordis.yml entry and flip the snapshot/harness opt-in flags back on.

中文

Problem

出厂的 tui-agent cordis.yml 加载了 @deepseek-ai/dsh-tool-todo,默认向模型暴露 todo_write。这个工具是一项任务追踪的便利功能,而非像 bashread/write/edit 文件系统工具那样的核心编码能力;多数 TUI 会话从不调用它,但出厂加载它会让每一轮的协议工具列表和系统提示词都随之变大。而 TUI 的计划渲染是事件驱动的:packages/ui/tui/src/index.ts 监听 todo/write 会话事件,TodoComponent.render 在列表为空时不返回任何内容,因此这个入口本就能容忍该工具的缺席或存在,与该插件没有任何运行时耦合。

Decision

tui-agent cordis.yml 不再加载 tool-todotodo_write 改为可选启用。code-mode.cordis.yml 覆盖配置继承基础组合,因此它生成的 SDK 同样不再包含 todo_write。启用它只需一条配置项——把 @deepseek-ai/dsh-tool-todo 加入 cordis.yml(或 ~/.dsh 的个人覆盖配置)——此后模型照旧记录整份清单的 todo/write 快照,TUI 照旧渲染该计划。TodoItem 类型与 todo/write 事件仍留在 @deepseek-ai/dsh-session,TUI 的计划渲染也保持接线,因此默认(禁用)与可选启用(启用)两条路径都是一等公民。同类的 acp-agent、headless-agent、jsonrpc-agent 示例仍然出厂携带该工具。

Alternatives considered

在出厂的 TUI 默认配置中保留 todo_write 否决:它是一项可选启用的便利功能,而非核心工具,出厂加载它会为多数会话都忽略的功能花掉每一轮的工具列表与提示词预算。仍然携带它的示例保留了该插件的真实组合覆盖。

连同默认配置项一起删掉 TUI 的计划渲染与 todo 测试。 否决:需求是同时支持启用与禁用两种情形,而事件驱动的 TodoComponent 本就在零插件耦合下渲染计划,删掉它等于白白丢弃一项可用能力。取而代之,启用路径保留专门的覆盖。

Testing

examples/tui-agent/tests/tui.snapshot.ts 根据逐场景的 enableTodo 开关决定是否挂载 ToolTodo:只有 todo-plan 场景挂载它(启用路径的证明,其 session.jsonl/terminal.expected.txt 固定了渲染出的计划),其余每个场景都运行默认的无 todo 组合。tests/harness.tsToolTodo 做成一个 todo 可选项,只有 tests/todo-write.e2e.ts 会开启它,因此带密钥的 todo e2e 仍然驱动真实工具,而其余套件与出厂技术栈保持一致。无密钥的 tests/tui-keyless-smoke.e2e.ts 启动真实的 cordis.yml,且不对 todo 作任何断言,因此默认启动不受影响。

Consequences

默认 TUI 的协议工具列表和系统提示词少了一个工具;想要任务追踪的会话加一条插件配置项即可。examples/tui-agent/composition.md(已重新生成)及其叶子条目表不再列出 tool-todoscripts/gen-doc-graphs.ts 中人工维护的摘要也去掉了它。@deepseek-ai/dsh-tool-todo 包本身没有变动,仍由 acp/headless/jsonrpc 示例出厂携带,因此它的覆盖需求在那里得到满足。若要恢复默认,只需重新加入那一条 cordis.yml 配置项,并把快照/harness 的可选开关重新打开。