Merge branch 'web2-todo' into feat/todo-multi-in-progress

Stack the parallel-in_progress change on the web todo display (#497): the
GUI is now the surface where several active items are visible, so the two
land as a chain rather than colliding on tool-todo at merge time.

Conflicts combined rather than resolved to one side: tool-todo keeps this
branch's parallel-allowing validation AND web2-todo's additionalProperties
unknown-key rejection, in src/index.ts and both README sides; the spec
drops web2-todo's 'two in_progress' rejection case and keeps its unknown-key
case; the two headless advanced-toolchain session fixtures keep this
branch's parallel transcripts.
This commit is contained in:
Chinesezjc
2026-07-27 14:01:00 +08:00
66 changed files with 1084 additions and 75 deletions

View File

@@ -1,6 +1,6 @@
# Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each
# side as of the last confirmed-consistent state. Both languages carry equal authority;
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write
README.md: 2f9f35be9c1a0f0234e2c69650ade48e2e5ab199
README.zh.md: bc94547c693d7a979ede62ea83afb89cc07ccf39
# pnpm run verify-translation-pairing --write packages/todo/tool-todo/README.md
README.md: 30d4caca05ab3a0d7fa4291f55b3b03d7e7d1363
README.zh.md: b0a471957940a16e10271887e6ba8c48390fb40a

View File

@@ -16,11 +16,11 @@ The list belongs to the ONE agent session that called the tool. There is no suba
## Validation
Beyond the schema's type/required/enum checks, `execute` rejects an empty or duplicate `content`. Any number of tasks may be `in_progress` at once — parallel work (concurrent subagents, background commands) legitimately runs several tasks simultaneously. Ordering and the discipline of keeping the list current are left to the model via the tool description.
Beyond the schema's type/required/enum checks, `execute` rejects an empty or duplicate `content`, 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. Any number of tasks may be `in_progress` at once — parallel work (concurrent subagents, background commands) legitimately runs several tasks simultaneously. Ordering and the discipline of keeping the list current are left to the model via the tool description.
## Rendering
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 [TUI app](../../examples/tui-demo) shows it as a persistent plan.
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 [TUI app](../../examples/tui-demo) shows it as a persistent plan, and the [web client](../../client/ui-conversation) renders a plan strip plus a dedicated tool row off `ConversationSnapshot.todos` ([Agent Note](../../../.agents/notes/implemented/feature/2026-07-23-web-todo-display.md)).
## Export shape

View File

@@ -16,11 +16,11 @@
## 验证
除 schema 的类型/必填/枚举检查外,`execute` 还会拒绝空或重复的 `content`。任意数量的任务可以同时处于 `in_progress`——并行工作(并发 subagent、后台命令确实会同时推进多个任务。顺序与保持列表最新的纪律由模型根据工具描述负责。
除 schema 的类型/必填/枚举检查外,`execute` 还会拒绝空或重复的 `content`,以及 `content`/`status` 之外的任何条目键——扩展条目形状id、嵌套会响亮失败而不是被静默压平保证落日志的快照与模型自认为写入的内容一致。任意数量的任务可以同时处于 `in_progress`——并行工作(并发 subagent、后台命令确实会同时推进多个任务。顺序与保持列表最新的纪律由模型根据工具描述负责。
## 渲染
规范结果为 `{ todos, counts: { pending, inProgress, completed } }`;其 Native 渲染器返回精简的更新确认。工具还会写入完整 `todo/write` 会话事件。UI 订阅事件流,并自行渲染该持久列表[TUI 应用](../../examples/tui-demo)将其显示为持久计划。
规范结果为 `{ todos, counts: { pending, inProgress, completed } }`;其 Native 渲染器返回精简的更新确认。工具还会写入完整 `todo/write` 会话事件。UI 订阅事件流,并自行渲染该持久列表[TUI 应用](../../examples/tui-demo)将其显示为持久计划[web 客户端](../../client/ui-conversation)则基于 `ConversationSnapshot.todos` 渲染计划横条与专属工具行([Agent Note](../../../.agents/notes/implemented/feature/2026-07-23-web-todo-display.md)
## 导出形状

View File

@@ -32,7 +32,10 @@ const DESCRIPTION =
* Validate the value constraints the ParameterSchemaSpec can't express and build the canonical {@link
* TodoItem}[]: trimmed non-empty unique content. Any number of items may be in_progress —
* parallel work (subagents, background commands) legitimately runs several tasks at once. The
* registry has already enforced the status enum; the cast below records that guarantee.
* registry has already enforced the status enum and rejected unknown item keys
* (`additionalProperties: false` — the logged snapshot must equal what the model believes it
* wrote, so a nested/extended item shape fails loud at the schema boundary instead of silently
* flattening); the cast below records that guarantee.
*/
function toTodoList(raw: { content: string; status: string }[]): TodoItem[] {
const todos: TodoItem[] = []
@@ -63,7 +66,7 @@ export function apply(ctx: Context): void {
description: 'The COMPLETE task list, replacing any previous list.',
items: {
type: 'object',
additionalProperties: true,
additionalProperties: false,
properties: {
content: { type: 'string', required: true, description: 'What the task is — a short imperative line.' },
status: {

View File

@@ -143,6 +143,7 @@ describe('dsh-tool-todo', () => {
it.each([
{ label: 'empty content', todos: [{ content: ' ', status: 'pending' }], fragment: 'non-empty' },
{ label: 'duplicate content', todos: [{ content: 'dup', status: 'pending' }, { content: 'dup', status: 'completed' }], fragment: 'duplicate' },
{ label: 'unknown item keys', todos: [{ content: 'a', status: 'pending', children: [] }], fragment: 'not a declared property' },
])('rejects $label as an isError result', async ({ todos, fragment }) => {
const ctx = await setup()
const result = await callTodo(ctx, { todos })