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:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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))。
|
||||
|
||||
## 导出形状
|
||||
|
||||
|
||||
@@ -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: {
|
||||
|
||||
@@ -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 })
|
||||
|
||||
Reference in New Issue
Block a user