From 4240c7dd7bf1872238f451358238a6b3967e6bc2 Mon Sep 17 00:00:00 2001 From: Dudu-0223 Date: Sun, 26 Jul 2026 02:32:34 +0800 Subject: [PATCH] feat(subagent): durable child catalog and list_agents Implements the durable-subagent-catalog RFC: SubagentControlService.listChildren() enumerates a parent's direct continuable children from one sessionQuery trace, validates each child's sole subagent/descriptor event (now carrying the durable creation label), and returns one ordered SubagentListEntry[] with per-child corrupt/unsupported/unavailable diagnostics. The list_agents tool ships as a separately loadable plugin of dsh-tool-subagent-control requiring sessionQuery at load; send_message stays usable without it. --- ...continuable-background-subagents.i18n.yaml | 4 +- ...-07-21-continuable-background-subagents.md | 2 +- ...-21-continuable-background-subagents.zh.md | 2 +- ...subagent-catalog-and-list-agents.i18n.yaml | 6 + ...urable-subagent-catalog-and-list-agents.md | 33 +- ...ble-subagent-catalog-and-list-agents.zh.md | 33 +- ...subagent-catalog-and-list-agents.i18n.yaml | 6 - docs/cordis-catalog/events.md | 8 +- docs/cordis-catalog/services.md | 15 +- docs/core-data-structures/subagent.i18n.yaml | 4 +- docs/core-data-structures/subagent.md | 6 +- docs/core-data-structures/subagent.zh.md | 6 +- docs/event-producer-consumer.md | 8 +- docs/tool-catalog.md | 17 +- examples/acp-agent/composition.md | 3 + examples/acp-agent/cordis.yml | 7 +- examples/acp-agent/tests/acp.snapshot.ts | 4 + .../system-prompt.expected.md | 12 + .../tool-schemas.expected.json | 8 + .../both-mode-turn/tool-schemas.expected.json | 8 + .../code-mode-turn/system-prompt.expected.md | 12 + .../lsp-definition/tool-schemas.expected.json | 8 + .../pty-tools/tool-schemas.expected.json | 8 + .../tool-schemas.expected.json | 8 + .../subagent-continuable/session.1.jsonl | 2 +- .../snapshots/subagent-list-agents/input.json | 14 + .../subagent-list-agents/session.1.jsonl | 18 + .../subagent-list-agents/session.jsonl | 46 ++ .../stdout.expected.jsonl | 4 + .../text-turn/tool-schemas.expected.json | 8 + .../web-fetch/tool-schemas.expected.json | 8 + .../cordis/tool-cordis/src/api-catalog.ts | 10 +- .../core/tools/tests/gen-tool-catalog.spec.ts | 2 +- packages/subagent/subagent/package.json | 5 + .../subagent/subagent/src/continuation.ts | 3 + packages/subagent/subagent/src/descriptor.ts | 15 + packages/subagent/subagent/src/index.ts | 118 +++++ .../subagent/tests/continuation.spec.ts | 1 + .../subagent/tests/list-children.spec.ts | 402 ++++++++++++++++++ .../subagent/subagent/tests/service.spec.ts | 29 +- packages/subagent/subagent/tsconfig.json | 3 + .../tool-subagent-control/README.i18n.yaml | 4 +- .../subagent/tool-subagent-control/README.md | 20 +- .../tool-subagent-control/README.zh.md | 20 +- .../tool-subagent-control/package.json | 11 +- .../tool-subagent-control/src/list-agents.ts | 76 ++++ .../tests/list-agents.spec.ts | 143 +++++++ .../tests/tool-subagent-control.spec.ts | 3 + .../tool-subagent-control/tsconfig.json | 3 + packages/subagent/tool-subagent/src/index.ts | 1 + packages/workspace/workspace/tsconfig.json | 3 + pnpm-lock.yaml | 12 +- scripts/gen-cordis-catalog.ts | 1 + scripts/gen-tool-catalog.ts | 8 +- tsconfig.base.json | 1 + 55 files changed, 1128 insertions(+), 94 deletions(-) create mode 100644 .agents/notes/implemented/feature/2026-07-22-durable-subagent-catalog-and-list-agents.i18n.yaml rename .agents/notes/{proposed => implemented}/feature/2026-07-22-durable-subagent-catalog-and-list-agents.md (78%) rename .agents/notes/{proposed => implemented}/feature/2026-07-22-durable-subagent-catalog-and-list-agents.zh.md (78%) delete mode 100644 .agents/notes/proposed/feature/2026-07-22-durable-subagent-catalog-and-list-agents.i18n.yaml create mode 100644 examples/acp-agent/tests/snapshots/subagent-list-agents/input.json create mode 100644 examples/acp-agent/tests/snapshots/subagent-list-agents/session.1.jsonl create mode 100644 examples/acp-agent/tests/snapshots/subagent-list-agents/session.jsonl create mode 100644 examples/acp-agent/tests/snapshots/subagent-list-agents/stdout.expected.jsonl create mode 100644 packages/subagent/subagent/tests/list-children.spec.ts create mode 100644 packages/subagent/tool-subagent-control/src/list-agents.ts create mode 100644 packages/subagent/tool-subagent-control/tests/list-agents.spec.ts diff --git a/.agents/notes/implemented/feature/2026-07-21-continuable-background-subagents.i18n.yaml b/.agents/notes/implemented/feature/2026-07-21-continuable-background-subagents.i18n.yaml index 0694137ab0..247279d605 100644 --- a/.agents/notes/implemented/feature/2026-07-21-continuable-background-subagents.i18n.yaml +++ b/.agents/notes/implemented/feature/2026-07-21-continuable-background-subagents.i18n.yaml @@ -2,5 +2,5 @@ # 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 .agents/notes/implemented/feature/2026-07-21-continuable-background-subagents.md -2026-07-21-continuable-background-subagents.md: 513ee668a9e04c05bb50f946016c460e09d1ddcd -2026-07-21-continuable-background-subagents.zh.md: 88cdd00582b18a2092c6993507fe3d4b92f237ae +2026-07-21-continuable-background-subagents.md: b914533e759e46a3f8641ac2c9b49badc2377c5c +2026-07-21-continuable-background-subagents.zh.md: 9f3dace04fe6f13e2136a8f3b96cc2ef4363e82d diff --git a/.agents/notes/implemented/feature/2026-07-21-continuable-background-subagents.md b/.agents/notes/implemented/feature/2026-07-21-continuable-background-subagents.md index 513ee668a9..b914533e75 100644 --- a/.agents/notes/implemented/feature/2026-07-21-continuable-background-subagents.md +++ b/.agents/notes/implemented/feature/2026-07-21-continuable-background-subagents.md @@ -27,7 +27,7 @@ durable child Session Foreground delegation keeps its one-shot behavior. Continuation covers background in-process spawn and fork children. Each `tool-subagent` instance selects `backgroundMode: 'one-shot' | 'continuable'`; configured continuable mode requires the mounted provider's `resume` capability, while a resumable provider may still use one-shot background policy. ACP children remain one-shot until the deferred ACP continuation work below is complete. -`ctx.subagents` is the only public service. Ordinary `start` stays collection-, Task-, and persistence-agnostic: it validates provider capabilities, dispatches one activation, observes run lifecycle, and returns a holder-owned run. An injected internal continuation manager owns stable child ids, descriptor persistence and lookup, Task-backed activation, and routing through `startContinuable` and `followup`; provider start and resume dispatch use private closures after the manager resolves continuation state. The provider-bound `@deepseek-ai/dsh-tool-subagent` plugin and human-facing adapters call the intent operations for continuable background work; foreground and one-shot background delegation use ordinary `start`. The globally named model tool is a thin optional adapter in `@deepseek-ai/dsh-tool-subagent-control`, and its presence does not decide whether continuable work starts. Parent-to-child enumeration and `list_agents` belong to a separate durable-catalog proposal. +`ctx.subagents` is the only public service. Ordinary `start` stays collection-, Task-, and persistence-agnostic: it validates provider capabilities, dispatches one activation, observes run lifecycle, and returns a holder-owned run. An injected internal continuation manager owns stable child ids, descriptor persistence and lookup, Task-backed activation, and routing through `startContinuable` and `followup`; provider start and resume dispatch use private closures after the manager resolves continuation state. The provider-bound `@deepseek-ai/dsh-tool-subagent` plugin and human-facing adapters call the intent operations for continuable background work; foreground and one-shot background delegation use ordinary `start`. The globally named model tool is a thin optional adapter in `@deepseek-ai/dsh-tool-subagent-control`, and its presence does not decide whether continuable work starts. Parent-to-child enumeration and `list_agents` belong to the [durable subagent catalog](2026-07-22-durable-subagent-catalog-and-list-agents.md). ### Task and cancellation ownership diff --git a/.agents/notes/implemented/feature/2026-07-21-continuable-background-subagents.zh.md b/.agents/notes/implemented/feature/2026-07-21-continuable-background-subagents.zh.md index 88cdd00582..9f3dace04f 100644 --- a/.agents/notes/implemented/feature/2026-07-21-continuable-background-subagents.zh.md +++ b/.agents/notes/implemented/feature/2026-07-21-continuable-background-subagents.zh.md @@ -27,7 +27,7 @@ durable child Session 前台委派保持一次性行为。继续执行覆盖后台的进程内 spawn 和 fork child。每个 `tool-subagent` 实例都会选择 `backgroundMode: 'one-shot' | 'continuable'`;配置为可继续模式时,所挂载提供方必须具备 `resume` 功能,而可恢复的提供方仍可采用一次性后台策略。在下述 ACP(Agent Client Protocol)后续工作完成前,ACP child 仍保持一次性行为。 -`ctx.subagents` 是唯一的公开服务。普通 `start` 不感知 child 集合、Task 与持久化:它校验提供方功能、分发一次激活、观察 run 生命周期,并返回由持有方负责的 run。注入的内部继续执行管理器负责管理稳定的 child id、描述符持久化与查找、由 Task 支撑的激活,以及通过 `startContinuable` 和 `followup` 进行的路由;管理器解析继续执行状态后,提供方的 start 与 resume 分发通过私有闭包进行。按提供方绑定的 `@deepseek-ai/dsh-tool-subagent` 插件及面向用户的适配器调用这些意图操作来处理可继续后台工作;前台和一次性后台委派使用普通 `start`。全局命名的模型工具是 `@deepseek-ai/dsh-tool-subagent-control` 中的可选轻量适配器,它是否存在不会决定是否启动可继续工作。parent 到 child 的枚举与 `list_agents` 属于单独的持久化目录提案。 +`ctx.subagents` 是唯一的公开服务。普通 `start` 不感知 child 集合、Task 与持久化:它校验提供方功能、分发一次激活、观察 run 生命周期,并返回由持有方负责的 run。注入的内部继续执行管理器负责管理稳定的 child id、描述符持久化与查找、由 Task 支撑的激活,以及通过 `startContinuable` 和 `followup` 进行的路由;管理器解析继续执行状态后,提供方的 start 与 resume 分发通过私有闭包进行。按提供方绑定的 `@deepseek-ai/dsh-tool-subagent` 插件及面向用户的适配器调用这些意图操作来处理可继续后台工作;前台和一次性后台委派使用普通 `start`。全局命名的模型工具是 `@deepseek-ai/dsh-tool-subagent-control` 中的可选轻量适配器,它是否存在不会决定是否启动可继续工作。parent 到 child 的枚举与 `list_agents` 属于[持久化 subagent 目录](2026-07-22-durable-subagent-catalog-and-list-agents.md)。 ### Task 与取消的所有权 diff --git a/.agents/notes/implemented/feature/2026-07-22-durable-subagent-catalog-and-list-agents.i18n.yaml b/.agents/notes/implemented/feature/2026-07-22-durable-subagent-catalog-and-list-agents.i18n.yaml new file mode 100644 index 0000000000..bcec32d7c7 --- /dev/null +++ b/.agents/notes/implemented/feature/2026-07-22-durable-subagent-catalog-and-list-agents.i18n.yaml @@ -0,0 +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 .agents/notes/implemented/feature/2026-07-22-durable-subagent-catalog-and-list-agents.md +2026-07-22-durable-subagent-catalog-and-list-agents.md: 634cb8708f951dfd62e1345f96420cba0d7baed9 +2026-07-22-durable-subagent-catalog-and-list-agents.zh.md: a3d282d3de63ca3bc081c071e66c2713422ebcc2 diff --git a/.agents/notes/proposed/feature/2026-07-22-durable-subagent-catalog-and-list-agents.md b/.agents/notes/implemented/feature/2026-07-22-durable-subagent-catalog-and-list-agents.md similarity index 78% rename from .agents/notes/proposed/feature/2026-07-22-durable-subagent-catalog-and-list-agents.md rename to .agents/notes/implemented/feature/2026-07-22-durable-subagent-catalog-and-list-agents.md index 23d5b3924a..634cb8708f 100644 --- a/.agents/notes/proposed/feature/2026-07-22-durable-subagent-catalog-and-list-agents.md +++ b/.agents/notes/implemented/feature/2026-07-22-durable-subagent-catalog-and-list-agents.md @@ -1,6 +1,6 @@ # Agent Note: Durable subagent catalog and list_agents -Status: proposed +Status: implemented English | [中文](2026-07-22-durable-subagent-catalog-and-list-agents.zh.md) @@ -10,9 +10,9 @@ Continuable background subagents expose a stable child id and persist the recons Enumeration must cross-check immutable session lineage, descriptor validity, and the live-preferred session corpus without loading or resuming an Agent merely to display it. It must also define how missing, corrupt, deleted, or unsupported children affect the list and whether repeatedly loading many child logs needs an index. -## Proposal +## Decision -Treat parent-to-child enumeration and `list_agents` as one separately reviewed feature built on the durable child-session contract. `SubagentService.listChildren(parentSessionId: SessionId)` must: +Parent-to-child enumeration and `list_agents` are one separately reviewed feature built on the durable child-session contract. `SubagentService.listChildren(parentSessionId: SessionId)` does the following: - use `ctx.sessionQuery.traceSession(parentSessionId)` to obtain the parent's direct live-preferred child sessions; - read and validate each candidate's `subagent/descriptor` event without activating the child; @@ -21,7 +21,7 @@ Treat parent-to-child enumeration and `list_agents` as one separately reviewed f - report a live child as `running` and a persisted-only child as `complete`; - return every resulting child in stable `createdAt` ascending, child-id ascending order. -Descriptor persistence, by-id lookup, direct-parent authorization, and provider-independent cold resume remain owned by the implemented Activation contract. This proposal extends the descriptor with a durable `label` and requires listing to diagnose duplicate descriptor events; it cannot weaken the existing facts or invent a second descriptor representation. +Descriptor persistence, by-id lookup, direct-parent authorization, and provider-independent cold resume remain owned by the implemented Activation contract. The catalog extends the descriptor with a durable `label` and requires listing to diagnose duplicate descriptor events; it does not weaken the existing facts or invent a second descriptor representation. ### Enumeration decision @@ -46,7 +46,7 @@ If measured scale later requires an index, that index is derived state: session - `kind: 'child'` carries readonly `id: SessionId`, durable `label: string`, and `status: 'running' | 'complete'`; - `kind: 'diagnostic'` carries readonly `id: SessionId` and `reason: 'corrupt' | 'unsupported' | 'unavailable'`. -A valid descriptor produces one child entry, a per-child inspection failure produces one diagnostic entry, and a candidate without a descriptor produces no entry. Child status `running` means that the logical record is live in `ctx.sessions`; `complete` means that it exists only in persistence. These values are neither `AgentStatus` nor the manager's internal Activation state, and the result does not expose the internal `createdAt` sorting key. Exact Activation states and durable outcomes such as successful completion, failure, cancellation, and stop reason require a separate durable activation record and are outside this proposal. +A valid descriptor produces one child entry, a per-child inspection failure produces one diagnostic entry, and a candidate without a descriptor produces no entry. Child status `running` means that the logical record is live in `ctx.sessions`; `complete` means that it exists only in persistence. These values are neither `AgentStatus` nor the manager's internal Activation state, and the result does not expose the internal `createdAt` sorting key. Exact Activation states and durable outcomes such as successful completion, failure, cancellation, and stop reason require a separate durable activation record and are outside this feature. The model-facing `list_agents` tool takes no arguments, derives `parentSessionId` from the current execution Agent, and is a thin adapter in `@deepseek-ai/dsh-tool-subagent-control`. It renders entries in array order as ` [] —