diff --git a/.agents/notes/implemented/simplification/2026-09-17-unified-subagent-activations.i18n.yaml b/.agents/notes/implemented/simplification/2026-09-17-unified-subagent-activations.i18n.yaml
index 07734a8954..b94acc03f3 100644
--- a/.agents/notes/implemented/simplification/2026-09-17-unified-subagent-activations.i18n.yaml
+++ b/.agents/notes/implemented/simplification/2026-09-17-unified-subagent-activations.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/simplification/2026-09-17-unified-subagent-activations.md
-2026-09-17-unified-subagent-activations.md: 07701562fb8804717e4c9ab43825ffa773266e04
-2026-09-17-unified-subagent-activations.zh.md: 1d98d11e063f5ef92c08e558c2646a15f0cbc3ac
+2026-09-17-unified-subagent-activations.md: ae663e7eab0673f6f6d7a43cd5ca4656e3f757e1
+2026-09-17-unified-subagent-activations.zh.md: 2a2a243f6c13e13bd4f9b0b59bcf5d4218077c07
diff --git a/.agents/notes/implemented/simplification/2026-09-17-unified-subagent-activations.md b/.agents/notes/implemented/simplification/2026-09-17-unified-subagent-activations.md
index 07701562fb..ae663e7eab 100644
--- a/.agents/notes/implemented/simplification/2026-09-17-unified-subagent-activations.md
+++ b/.agents/notes/implemented/simplification/2026-09-17-unified-subagent-activations.md
@@ -14,13 +14,13 @@ Local one-shot execution and continuable residency separately owned child creati
Local spawn and fork providers contribute only `prepareContinuable()` data; the subagent service owns Agent creation and residency. The standalone in-process driver package is removed. Structured tools, validation, instructions, and terminal guards attach to one activation and close with it. After capture, that activation rejects further input; cold resume reconstructs an ordinary conversation without the previous schema.
-ACP, DSH SDK, Codex, and Claude Code retain their existing single-execution provider adapters. They join the same activation capacity and ownership graph without accepting follow-up messages or fabricating a local child Session. The parent-owned catalog registers both local children and external leaves. External entries carry an optional last-recorded outcome; pending means no terminal outcome is recorded and makes no claim about process liveness. Settlement updates the same entry without storing or broadcasting full output. Caller delivery returns the complete result; parent delivery queues the complete completion notice.
+ACP, DSH SDK, Codex, and Claude Code retain their existing single-execution provider adapters. They join the same activation capacity and ownership graph without accepting follow-up messages or fabricating a local child Session. The parent-owned catalog registers both local children and external leaves. External entries carry `external: true` because they have no local Session to open. Creation records membership once; execution and cleanup do not update the catalog. Execution status has no required consumer and remains outside the membership record. Caller delivery returns the complete result; parent delivery queues the complete completion notice.
The model-facing tool always returns a background child id and promises the manager's completion notice. It has no foreground switch or Job integration. Workflows select caller delivery, await the activation result, and dispose the handle before finishing; they add neither a completion notice nor initial return guidance to the parent/child exchange. Headless completion waits for its own child tree and subsequent parent turns.
### Retained decisions
-The native Session reader permits one pending-to-terminal update for an external catalog entry only when its mode, creation time, and label remain unchanged. Local duplicate membership and repeated terminal updates still reject restoration.
+The native Session reader rejects duplicate child membership for both local and external catalog entries.
The [named-provider seam](../feature/2026-06-21-subagent-capability-seam.md), [continuable residency](../feature/2026-07-28-continuable-subagent-conversations.md), [fork request prefix](../architecture/2026-08-10-fork-children-stay-one-shot.md), [settlement delivery ordering](../feature/2026-08-06-manager-owned-subagent-settlement-delivery.md), [parent-owned catalog](../architecture/2026-09-01-parent-owned-subagent-catalog.md), and [activation capacity](../feature/2026-09-15-continuable-activation-capacity.md) retain their independent rationale. This decision owns the shared entry point, external participation, and caller-versus-parent result delivery. Those records are partially superseded, not candidates for consolidation or archival.
@@ -28,7 +28,7 @@ Released Session generations remain immutable. Historical one-shot descriptors s
## Alternatives considered
-**Keep a separate external execution projection.** It duplicates membership, restoration, and client update handling already supplied by the parent catalog. Repeated directory snapshots would also retransmit every retained result. The catalog retains only identity and outcome; results remain with their recipient.
+**Keep a separate external execution projection.** It duplicates membership, restoration, and client update handling already supplied by the parent catalog. Repeated directory snapshots would also retransmit every retained result. The catalog retains only membership; results remain with their recipient.
**Keep one-shot local execution for synchronous workflows.** Awaiting a result is a consumer requirement. A second Agent lifecycle duplicates cancellation and cleanup solely to supply a promise, which the activation can supply directly.
@@ -44,4 +44,4 @@ One manager controls admission, cancellation, ownership, and resource release ac
## Verification
-Focused tests cover local spawn/fork inheritance, structured capture and schema-free cold resume, explicit cancellation, external catalog updates, result-versus-disposal ordering, workflow collection without parent notices, and headless child-tree completion. Existing replay generations are preserved; updated recorded-session cases cover the current model-visible tool and notice behavior.
+Focused tests cover local spawn/fork inheritance, structured capture and schema-free cold resume, explicit cancellation, external catalog membership, result-versus-disposal ordering, workflow collection without parent notices, and headless child-tree completion. Existing replay generations are preserved; updated recorded-session cases cover the current model-visible tool and notice behavior.
diff --git a/.agents/notes/implemented/simplification/2026-09-17-unified-subagent-activations.zh.md b/.agents/notes/implemented/simplification/2026-09-17-unified-subagent-activations.zh.md
index 1d98d11e06..2a2a243f6c 100644
--- a/.agents/notes/implemented/simplification/2026-09-17-unified-subagent-activations.zh.md
+++ b/.agents/notes/implemented/simplification/2026-09-17-unified-subagent-activations.zh.md
@@ -14,13 +14,13 @@ Status: implemented
本地 spawn 和 fork 提供方只贡献 `prepareContinuable()` 数据,由 subagent 服务拥有 Agent 创建与驻留。独立进程内驱动器包被移除。结构化工具、校验、指令与终止保护附着于一次 activation,并随其关闭。捕获后该 activation 拒绝继续输入;冷恢复重建普通对话,不携带此前的 schema。
-ACP、DSH SDK、Codex 和 Claude Code 保留单次执行适配器,共享 activation 容量与所有权,不接受后续消息,也不伪造本地子 Session。父级拥有的 catalog 同时登记本地子代理与外部叶子项。外部条目携带可选的最近记录结果状态;pending 表示尚无终态记录,不声明进程是否存活。结束时更新同一条目,不存储或广播完整输出。caller 交付返回完整结果,parent 交付将完整完成通知加入父级队列。
+ACP、DSH SDK、Codex 和 Claude Code 保留单次执行适配器,共享 activation 容量与所有权,不接受后续消息,也不伪造本地子 Session。父级拥有的 catalog 同时登记本地子代理与外部叶子项。外部条目携带 `external: true`,因为没有可打开的本地 Session。创建时记录一次成员关系;执行与清理不更新目录。执行状态没有必需的消费者,不属于成员记录。caller 交付返回完整结果,parent 交付将完整完成通知加入父级队列。
面向模型的工具始终返回后台子级 ID,并承诺管理器的完成通知。它没有前台开关或 Job 集成。工作流选择调用方投递,等待 activation 结果并在完成前释放句柄;它们不向父子交互添加完成通知或初始返回指导。Headless 完成流程等待其自身子树与后续父级轮次。
### 保留的决策
-原生 Session 读取器仅在模式、创建时间与标签不变时,允许外部目录条目从 pending 更新到终态一次。本地重复成员记录与重复终态更新仍会拒绝恢复。
+原生 Session 读取器对本地与外部目录条目都拒绝重复的子级成员记录。
[具名提供方 seam](../feature/2026-06-21-subagent-capability-seam.zh.md)、[可继续驻留](../feature/2026-07-28-continuable-subagent-conversations.zh.md)、[fork 请求前缀](../architecture/2026-08-10-fork-children-stay-one-shot.zh.md)、[结算投递顺序](../feature/2026-08-06-manager-owned-subagent-settlement-delivery.zh.md)、[父级拥有的目录](../architecture/2026-09-01-parent-owned-subagent-catalog.zh.md) 和 [activation 容量](../feature/2026-09-15-continuable-activation-capacity.zh.md) 保留各自独立理由。本决策拥有共享入口、外部执行参与以及调用方与父级之间的结果投递选择。这些记录仅被部分取代,不满足合并或归档条件。
@@ -28,7 +28,7 @@ ACP、DSH SDK、Codex 和 Claude Code 保留单次执行适配器,共享 activ
## 考虑过的替代方案
-**保留独立的外部执行投影。** 它重复父级 catalog 已提供的成员登记、恢复与客户端更新,每次目录快照还会重传全部结果。catalog 仅保留身份和结果状态,完整结果由接收方负责。
+**保留独立的外部执行投影。** 它重复父级 catalog 已提供的成员登记、恢复与客户端更新,每次目录快照还会重传全部结果。catalog 仅保留成员关系,完整结果由接收方负责。
**为同步工作流保留本地一次性执行。** 等待结果是消费者需求。仅为提供 promise 而维护第二套 Agent 生命周期会重复取消与清理;activation 可以直接提供该 promise。
@@ -44,4 +44,4 @@ ACP、DSH SDK、Codex 和 Claude Code 保留单次执行适配器,共享 activ
## 验证
-聚焦测试覆盖本地 spawn/fork 继承、结构化捕获与无 schema 冷恢复、显式取消、外部目录更新、结果与释放顺序、不发父级通知的工作流收集,以及 headless 子树完成。既有回放世代得到保留;更新的录制会话用例覆盖当前面向模型的工具与通知行为。
+聚焦测试覆盖本地 spawn/fork 继承、结构化捕获与无 schema 冷恢复、显式取消、外部目录成员关系、结果与释放顺序、不发父级通知的工作流收集,以及 headless 子树完成。既有回放世代得到保留;更新的录制会话用例覆盖当前面向模型的工具与通知行为。
diff --git a/docs/persistence-catalog.i18n.yaml b/docs/persistence-catalog.i18n.yaml
index 3890f7edf9..8dc9332e84 100644
--- a/docs/persistence-catalog.i18n.yaml
+++ b/docs/persistence-catalog.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 docs/persistence-catalog.md
-persistence-catalog.md: da664a192c53db44f3a08a461c6f21411a319e7a
-persistence-catalog.zh.md: 03d8d52b0d6dc2c0b00872b1d2fd9cbac5add652
+persistence-catalog.md: c7212b543a367e27d7816cfbc5e3aa51a058b0be
+persistence-catalog.zh.md: 77b36cf26cf73c29bcaa702575a7101b25d7202f
diff --git a/docs/persistence-catalog.md b/docs/persistence-catalog.md
index da664a192c..c7212b543a 100644
--- a/docs/persistence-catalog.md
+++ b/docs/persistence-catalog.md
@@ -57,7 +57,7 @@ The [format references](persistence-changes/historical-formats/README.md) cover
| `event:session/title-llm-request` | event | `fa8f7d3ebf08a76c7f7a8b0781873c4d819b964da5dbb52cd3cdfa5da34f452d` | [`{ type: "session/title-llm-request" }`](#persistence-type-sha256-fa8f7d3ebf08a76c7f7a8b0781873c4d819b964da5dbb52cd3cdfa5da34f452d) |
| `event:step/end` | event | `e0a787e6ec76c7c94fecbc501b489164ab0293db05bc947914077ad01e674f05` | [`{ type: "step/end" }`](#persistence-type-sha256-e0a787e6ec76c7c94fecbc501b489164ab0293db05bc947914077ad01e674f05) |
| `event:step/start` | event | `4513e088d43e6c68425be30451b9f961cc264fe7318ca62681c41d4d78615986` | [`{ type: "step/start" }`](#persistence-type-sha256-4513e088d43e6c68425be30451b9f961cc264fe7318ca62681c41d4d78615986) |
-| `event:subagent/catalog` | event | `289dc7167ce5942019b86cab1ccfacb72eaf7460ef3c45e3c23780bf8e030376` | [`{ type: "subagent/catalog" }`](#persistence-type-sha256-289dc7167ce5942019b86cab1ccfacb72eaf7460ef3c45e3c23780bf8e030376) |
+| `event:subagent/catalog` | event | `568d482eeba04a9422773e7563cf935bd06f16896758cf880cf19b6bab8b1ada` | [`{ type: "subagent/catalog" }`](#persistence-type-sha256-568d482eeba04a9422773e7563cf935bd06f16896758cf880cf19b6bab8b1ada) |
| `event:subagent/descriptor` | event | `b79ada42962cad0190a9d465805260567621fa3a4abd757eb31e6016b52d5ab5` | [`{ type: "subagent/descriptor" }`](#persistence-type-sha256-b79ada42962cad0190a9d465805260567621fa3a4abd757eb31e6016b52d5ab5) |
| `event:subagent/model-selection-policy` | event | `a6567ccb2e530606b775371eb4fa31468d72084339968e8b0440a516a23b39dc` | [`{ type: "subagent/model-selection-policy" }`](#persistence-type-sha256-a6567ccb2e530606b775371eb4fa31468d72084339968e8b0440a516a23b39dc) |
| `event:system/message` | event | `69081694be231d56fd9580ba14645fd5e35373202605d5c5c841a9435b5fa3b1` | [`{ type: "system/message" }`](#persistence-type-sha256-69081694be231d56fd9580ba14645fd5e35373202605d5c5c841a9435b5fa3b1) |
@@ -2219,14 +2219,6 @@ SHA-256: `383cf73ae4748e5beb281ae74857ba1ffb92ae01075b9fbf51af78b666007a8f`
`"recall"`
-
-
-### `"refusal"`
-
-SHA-256: `4a561c111a78d4520e850c93795369ea169ff86abd082aee86c8fd38121a65aa`
-
-`"refusal"`
-
### `"rejected"`
@@ -4563,7 +4555,7 @@ One of:
- [`{ type: "finish" }`](#persistence-type-sha256-a2b49331716ba0a7078b026d145306a70eae64b7313c3e6204bd931e75ec3bb1)
- [`{ type: "usage" }`](#persistence-type-sha256-f1a96afe7503ac1817de769cd0ba13d9b80111c7034c50106b3344ecc14cc515)
-
+
@@ -4571,17 +4563,17 @@ One of:
### `SubagentCatalogEvent`
-SHA-256: `c974738c089d11324eac001a8e4c032d278cebc0221efb9896ff72b922d82b7e`
+SHA-256: `4e6c80858802af53690bae54e01a4e416c7aec5b34a8817b21d6cf33a9603b52`
Sources: [`packages/subagent/subagent/src/catalog.ts:28`](../packages/subagent/subagent/src/catalog.ts)
One of:
-- [`{ childCreatedAt, childId, external?, label?, … }`](#persistence-type-sha256-bbf8af8c6b0edb0b150ea997c51bdc63e8def8428adb228bbc88af0577344391)
-- [`{ childCreatedAt, childId, external?, label?, … }`](#persistence-type-sha256-0300cd374b1d3d9843f1a5f523c3c8fc920d2bc04be32380331359ac1edb1dbe)
-- [`{ childCreatedAt, childId, label, mode, … }`](#persistence-type-sha256-e2ad8f5bcd813f268ffef1b10a12d7ddd2b8ced8b9b6d0275b3f6b7ed094a187)
- [`{ childCreatedAt, childId, label, mode, … }`](#persistence-type-sha256-0e10d7c096b47e61defe372a5fc8bd37d835199410eac44367f95c45082498bd)
+- [`{ childCreatedAt, childId, label, mode, … }`](#persistence-type-sha256-e2ad8f5bcd813f268ffef1b10a12d7ddd2b8ced8b9b6d0275b3f6b7ed094a187)
- [`{ childCreatedAt, childId, label?, mode, … }`](#persistence-type-sha256-a0f25b5aeeebf2bf1542ce67726b095e3b139a39c71861d57e827f12dea1f499)
+- [`{ childCreatedAt, childId, external?, label?, … }`](#persistence-type-sha256-7e25366aed8235508ef657343f0f263f3354b8418406dcd857acecd7e1c74cd0)
+- [`{ childCreatedAt, childId, external?, label?, … }`](#persistence-type-sha256-718c94fb5c5ac43cdd163d384bd75b3ca8bc355a02727423cd42d97948263128)
@@ -5459,21 +5451,6 @@ One of:
- `"pause"`
- `"resume"`
-
-
-### `union (6 variants)`
-
-SHA-256: `ae2a48f2f3e53adc041eb3d7ee98a4880af2f81484c20c1d07a0f80dbfb7c0c1`
-
-One of:
-
-- `"aborted"`
-- `"completed"`
-- `"error"`
-- `"max-tokens"`
-- `"pending"`
-- `"refusal"`
-
### `union (7 variants)`
@@ -5632,36 +5609,36 @@ SHA-256: `df843efd87c78cf8de98f8e0d0216956cd568d0d0e56a22c853663e274aa1a18`
Array of [`{ capturedFormatVersion?, capturedThroughSeq, compacted, inputIndex, … }`](#persistence-type-sha256-4f91f187baeabef5c7950c4780d31bbe39f3345dff591a6cf26ac7e135ac1cf4).
-
+
### `{ childCreatedAt, childId, external?, label?, … }`
-SHA-256: `0300cd374b1d3d9843f1a5f523c3c8fc920d2bc04be32380331359ac1edb1dbe`
+SHA-256: `718c94fb5c5ac43cdd163d384bd75b3ca8bc355a02727423cd42d97948263128`
| Property | Presence | Type |
|---|---|---|
| `childCreatedAt` | required | `number` |
| `childId` | required | `string` |
-| `external` | optional | [`union (6 variants)`](#persistence-type-sha256-ae2a48f2f3e53adc041eb3d7ee98a4880af2f81484c20c1d07a0f80dbfb7c0c1) |
-| `label` | optional | `string` |
-| `mode` | required | `"one-shot"` |
-| `version` | required | `0` |
-
-
-
-### `{ childCreatedAt, childId, external?, label?, … }`
-
-SHA-256: `bbf8af8c6b0edb0b150ea997c51bdc63e8def8428adb228bbc88af0577344391`
-
-| Property | Presence | Type |
-|---|---|---|
-| `childCreatedAt` | required | `number` |
-| `childId` | required | `string` |
-| `external` | optional | [`union (6 variants)`](#persistence-type-sha256-ae2a48f2f3e53adc041eb3d7ee98a4880af2f81484c20c1d07a0f80dbfb7c0c1) |
+| `external` | optional | `true` |
| `label` | optional | `string` |
| `mode` | required | `"one-shot"` |
| `version` | required | `1` |
+
+
+### `{ childCreatedAt, childId, external?, label?, … }`
+
+SHA-256: `7e25366aed8235508ef657343f0f263f3354b8418406dcd857acecd7e1c74cd0`
+
+| Property | Presence | Type |
+|---|---|---|
+| `childCreatedAt` | required | `number` |
+| `childId` | required | `string` |
+| `external` | optional | `true` |
+| `label` | optional | `string` |
+| `mode` | required | `"one-shot"` |
+| `version` | required | `0` |
+
### `{ childCreatedAt, childId, label, mode, … }`
@@ -8299,17 +8276,17 @@ SHA-256: `4513e088d43e6c68425be30451b9f961cc264fe7318ca62681c41d4d78615986`
| `time` | required | `number` |
| `type` | required | `"step/start"` |
-
+
### `{ type: "subagent/catalog" }`
-SHA-256: `289dc7167ce5942019b86cab1ccfacb72eaf7460ef3c45e3c23780bf8e030376`
+SHA-256: `568d482eeba04a9422773e7563cf935bd06f16896758cf880cf19b6bab8b1ada`
| Property | Presence | Type |
|---|---|---|
-| `data` | required | [`SubagentCatalogEvent`](#persistence-type-sha256-c974738c089d11324eac001a8e4c032d278cebc0221efb9896ff72b922d82b7e) |
+| `data` | required | [`SubagentCatalogEvent`](#persistence-type-sha256-4e6c80858802af53690bae54e01a4e416c7aec5b34a8817b21d6cf33a9603b52) |
| `ignorable` | optional | `true` |
| `seq` | required | `number` |
| `time` | required | `number` |
diff --git a/docs/persistence-catalog.zh.md b/docs/persistence-catalog.zh.md
index 03d8d52b0d..77b36cf26c 100644
--- a/docs/persistence-catalog.zh.md
+++ b/docs/persistence-catalog.zh.md
@@ -59,7 +59,7 @@
| `event:session/title-llm-request` | event | `fa8f7d3ebf08a76c7f7a8b0781873c4d819b964da5dbb52cd3cdfa5da34f452d` | [`{ type: "session/title-llm-request" }`](#persistence-type-sha256-fa8f7d3ebf08a76c7f7a8b0781873c4d819b964da5dbb52cd3cdfa5da34f452d) |
| `event:step/end` | event | `e0a787e6ec76c7c94fecbc501b489164ab0293db05bc947914077ad01e674f05` | [`{ type: "step/end" }`](#persistence-type-sha256-e0a787e6ec76c7c94fecbc501b489164ab0293db05bc947914077ad01e674f05) |
| `event:step/start` | event | `4513e088d43e6c68425be30451b9f961cc264fe7318ca62681c41d4d78615986` | [`{ type: "step/start" }`](#persistence-type-sha256-4513e088d43e6c68425be30451b9f961cc264fe7318ca62681c41d4d78615986) |
-| `event:subagent/catalog` | event | `289dc7167ce5942019b86cab1ccfacb72eaf7460ef3c45e3c23780bf8e030376` | [`{ type: "subagent/catalog" }`](#persistence-type-sha256-289dc7167ce5942019b86cab1ccfacb72eaf7460ef3c45e3c23780bf8e030376) |
+| `event:subagent/catalog` | event | `568d482eeba04a9422773e7563cf935bd06f16896758cf880cf19b6bab8b1ada` | [`{ type: "subagent/catalog" }`](#persistence-type-sha256-568d482eeba04a9422773e7563cf935bd06f16896758cf880cf19b6bab8b1ada) |
| `event:subagent/descriptor` | event | `b79ada42962cad0190a9d465805260567621fa3a4abd757eb31e6016b52d5ab5` | [`{ type: "subagent/descriptor" }`](#persistence-type-sha256-b79ada42962cad0190a9d465805260567621fa3a4abd757eb31e6016b52d5ab5) |
| `event:subagent/model-selection-policy` | event | `a6567ccb2e530606b775371eb4fa31468d72084339968e8b0440a516a23b39dc` | [`{ type: "subagent/model-selection-policy" }`](#persistence-type-sha256-a6567ccb2e530606b775371eb4fa31468d72084339968e8b0440a516a23b39dc) |
| `event:system/message` | event | `69081694be231d56fd9580ba14645fd5e35373202605d5c5c841a9435b5fa3b1` | [`{ type: "system/message" }`](#persistence-type-sha256-69081694be231d56fd9580ba14645fd5e35373202605d5c5c841a9435b5fa3b1) |
@@ -2221,14 +2221,6 @@ SHA-256: `383cf73ae4748e5beb281ae74857ba1ffb92ae01075b9fbf51af78b666007a8f`
`"recall"`
-
-
-### `"refusal"`
-
-SHA-256: `4a561c111a78d4520e850c93795369ea169ff86abd082aee86c8fd38121a65aa`
-
-`"refusal"`
-
### `"rejected"`
@@ -4565,7 +4557,7 @@ SHA-256: `47e9095591ae36e7c39ee62553097772f2bfa16304536a72dbbc3f691f605180`
- [`{ type: "finish" }`](#persistence-type-sha256-a2b49331716ba0a7078b026d145306a70eae64b7313c3e6204bd931e75ec3bb1)
- [`{ type: "usage" }`](#persistence-type-sha256-f1a96afe7503ac1817de769cd0ba13d9b80111c7034c50106b3344ecc14cc515)
-
+
@@ -4573,17 +4565,17 @@ SHA-256: `47e9095591ae36e7c39ee62553097772f2bfa16304536a72dbbc3f691f605180`
### `SubagentCatalogEvent`
-SHA-256: `c974738c089d11324eac001a8e4c032d278cebc0221efb9896ff72b922d82b7e`
+SHA-256: `4e6c80858802af53690bae54e01a4e416c7aec5b34a8817b21d6cf33a9603b52`
来源:[`packages/subagent/subagent/src/catalog.ts:28`](../packages/subagent/subagent/src/catalog.ts)
以下类型之一:
-- [`{ childCreatedAt, childId, external?, label?, … }`](#persistence-type-sha256-bbf8af8c6b0edb0b150ea997c51bdc63e8def8428adb228bbc88af0577344391)
-- [`{ childCreatedAt, childId, external?, label?, … }`](#persistence-type-sha256-0300cd374b1d3d9843f1a5f523c3c8fc920d2bc04be32380331359ac1edb1dbe)
-- [`{ childCreatedAt, childId, label, mode, … }`](#persistence-type-sha256-e2ad8f5bcd813f268ffef1b10a12d7ddd2b8ced8b9b6d0275b3f6b7ed094a187)
- [`{ childCreatedAt, childId, label, mode, … }`](#persistence-type-sha256-0e10d7c096b47e61defe372a5fc8bd37d835199410eac44367f95c45082498bd)
+- [`{ childCreatedAt, childId, label, mode, … }`](#persistence-type-sha256-e2ad8f5bcd813f268ffef1b10a12d7ddd2b8ced8b9b6d0275b3f6b7ed094a187)
- [`{ childCreatedAt, childId, label?, mode, … }`](#persistence-type-sha256-a0f25b5aeeebf2bf1542ce67726b095e3b139a39c71861d57e827f12dea1f499)
+- [`{ childCreatedAt, childId, external?, label?, … }`](#persistence-type-sha256-7e25366aed8235508ef657343f0f263f3354b8418406dcd857acecd7e1c74cd0)
+- [`{ childCreatedAt, childId, external?, label?, … }`](#persistence-type-sha256-718c94fb5c5ac43cdd163d384bd75b3ca8bc355a02727423cd42d97948263128)
@@ -5461,21 +5453,6 @@ SHA-256: `62e6a429ff3f390a46ac39fb468692032f42936ca0db19b53454184189401379`
- `"pause"`
- `"resume"`
-
-
-### `union (6 variants)`
-
-SHA-256: `ae2a48f2f3e53adc041eb3d7ee98a4880af2f81484c20c1d07a0f80dbfb7c0c1`
-
-以下类型之一:
-
-- `"aborted"`
-- `"completed"`
-- `"error"`
-- `"max-tokens"`
-- `"pending"`
-- `"refusal"`
-
### `union (7 variants)`
@@ -5634,36 +5611,36 @@ SHA-256: `df843efd87c78cf8de98f8e0d0216956cd568d0d0e56a22c853663e274aa1a18`
[`{ capturedFormatVersion?, capturedThroughSeq, compacted, inputIndex, … }`](#persistence-type-sha256-4f91f187baeabef5c7950c4780d31bbe39f3345dff591a6cf26ac7e135ac1cf4) 的数组。
-
+
### `{ childCreatedAt, childId, external?, label?, … }`
-SHA-256: `0300cd374b1d3d9843f1a5f523c3c8fc920d2bc04be32380331359ac1edb1dbe`
+SHA-256: `718c94fb5c5ac43cdd163d384bd75b3ca8bc355a02727423cd42d97948263128`
| 属性 | 存在性 | 类型 |
|---|---|---|
| `childCreatedAt` | 必需 | `number` |
| `childId` | 必需 | `string` |
-| `external` | 可选 | [`union (6 variants)`](#persistence-type-sha256-ae2a48f2f3e53adc041eb3d7ee98a4880af2f81484c20c1d07a0f80dbfb7c0c1) |
-| `label` | 可选 | `string` |
-| `mode` | 必需 | `"one-shot"` |
-| `version` | 必需 | `0` |
-
-
-
-### `{ childCreatedAt, childId, external?, label?, … }`
-
-SHA-256: `bbf8af8c6b0edb0b150ea997c51bdc63e8def8428adb228bbc88af0577344391`
-
-| 属性 | 存在性 | 类型 |
-|---|---|---|
-| `childCreatedAt` | 必需 | `number` |
-| `childId` | 必需 | `string` |
-| `external` | 可选 | [`union (6 variants)`](#persistence-type-sha256-ae2a48f2f3e53adc041eb3d7ee98a4880af2f81484c20c1d07a0f80dbfb7c0c1) |
+| `external` | 可选 | `true` |
| `label` | 可选 | `string` |
| `mode` | 必需 | `"one-shot"` |
| `version` | 必需 | `1` |
+
+
+### `{ childCreatedAt, childId, external?, label?, … }`
+
+SHA-256: `7e25366aed8235508ef657343f0f263f3354b8418406dcd857acecd7e1c74cd0`
+
+| 属性 | 存在性 | 类型 |
+|---|---|---|
+| `childCreatedAt` | 必需 | `number` |
+| `childId` | 必需 | `string` |
+| `external` | 可选 | `true` |
+| `label` | 可选 | `string` |
+| `mode` | 必需 | `"one-shot"` |
+| `version` | 必需 | `0` |
+
### `{ childCreatedAt, childId, label, mode, … }`
@@ -8301,17 +8278,17 @@ SHA-256: `4513e088d43e6c68425be30451b9f961cc264fe7318ca62681c41d4d78615986`
| `time` | 必需 | `number` |
| `type` | 必需 | `"step/start"` |
-
+
### `{ type: "subagent/catalog" }`
-SHA-256: `289dc7167ce5942019b86cab1ccfacb72eaf7460ef3c45e3c23780bf8e030376`
+SHA-256: `568d482eeba04a9422773e7563cf935bd06f16896758cf880cf19b6bab8b1ada`
| 属性 | 存在性 | 类型 |
|---|---|---|
-| `data` | 必需 | [`SubagentCatalogEvent`](#persistence-type-sha256-c974738c089d11324eac001a8e4c032d278cebc0221efb9896ff72b922d82b7e) |
+| `data` | 必需 | [`SubagentCatalogEvent`](#persistence-type-sha256-4e6c80858802af53690bae54e01a4e416c7aec5b34a8817b21d6cf33a9603b52) |
| `ignorable` | 可选 | `true` |
| `seq` | 必需 | `number` |
| `time` | 必需 | `number` |
diff --git a/docs/persistence-changes/2026-09-19-external-subagent-catalog.i18n.yaml b/docs/persistence-changes/2026-09-19-external-subagent-catalog.i18n.yaml
index e9884f53db..2a9ff18bf9 100644
--- a/docs/persistence-changes/2026-09-19-external-subagent-catalog.i18n.yaml
+++ b/docs/persistence-changes/2026-09-19-external-subagent-catalog.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 docs/persistence-changes/2026-09-19-external-subagent-catalog.md
-2026-09-19-external-subagent-catalog.md: 6c5321567c8479ff7432f411833d5549dd7259fc
-2026-09-19-external-subagent-catalog.zh.md: 6d51ab84c386ff07129569433991980851e2dd7e
+2026-09-19-external-subagent-catalog.md: 3ca24aec51d7aaaf1cb7421ab90032df4f5d8b20
+2026-09-19-external-subagent-catalog.zh.md: 34f0d4a08722376c1d5223adbbac71ed06f211c1
diff --git a/docs/persistence-changes/2026-09-19-external-subagent-catalog.md b/docs/persistence-changes/2026-09-19-external-subagent-catalog.md
index 6c5321567c..3ca24aec51 100644
--- a/docs/persistence-changes/2026-09-19-external-subagent-catalog.md
+++ b/docs/persistence-changes/2026-09-19-external-subagent-catalog.md
@@ -9,7 +9,7 @@ English | [中文](2026-09-19-external-subagent-catalog.zh.md)
## Summary
-Add an optional external execution outcome to one-shot parent catalog entries.
+Add an optional external membership marker to one-shot parent catalog entries.
## Table of Contents
@@ -28,19 +28,19 @@ baseline: false
changes:
- root: "event:subagent/catalog"
previous: "2026-09-20-unknown-child-catalog"
- after: "289dc7167ce5942019b86cab1ccfacb72eaf7460ef3c45e3c23780bf8e030376"
+ after: "568d482eeba04a9422773e7563cf935bd06f16896758cf880cf19b6bab8b1ada"
decision: same-version
```
## Compatibility
-Existing version-0 and version-1 one-shot and continuable catalog facts remain valid without the optional external field. Version-1 unknown-mode facts remain unchanged. The field identifies a child without a local Session and records pending or a terminal outcome. Pending means no terminal outcome is recorded, not that a process is live. Settlement appends a complete catalog fact with the same identity and creation time; the projection keeps one row per child. No new Session event type, full result payload, or Session format version is introduced.
+Existing version-0 and version-1 catalog facts remain valid without the optional external field. The literal true identifies a child without a local Session. Creation writes one membership fact; execution and cleanup do not update it. Duplicate child ids remain invalid. Unknown-mode and continuable entries do not accept the marker. This updates the unaccepted optional-field addition in this PR and does not change released generations or the Session format version.
## Verification
-Catalog projection, V3-to-V4 catalog completion and validation, and JSONL catalog migration tests passed: 124 tests passed and one skipped. Coverage includes mixed unknown-mode and external entries at both supported payload versions, external pending-to-terminal updates, and rejection of external fields on unknown or continuable entries.
+Focused catalog, external activation, native V4 restoration, and client tests passed. They cover creation-time membership, no settlement update, cleanup failure independent of catalog membership, and duplicate-child rejection.
## Dev Note
diff --git a/docs/persistence-changes/2026-09-19-external-subagent-catalog.schema.json b/docs/persistence-changes/2026-09-19-external-subagent-catalog.schema.json
index 27682957da..acdecbf08c 100644
--- a/docs/persistence-changes/2026-09-19-external-subagent-catalog.schema.json
+++ b/docs/persistence-changes/2026-09-19-external-subagent-catalog.schema.json
@@ -6,7 +6,7 @@
"kind": "event",
"event": "subagent/catalog",
"surface": false,
- "digest": "289dc7167ce5942019b86cab1ccfacb72eaf7460ef3c45e3c23780bf8e030376",
+ "digest": "568d482eeba04a9422773e7563cf935bd06f16896758cf880cf19b6bab8b1ada",
"schema": {
"root": 0,
"nodes": [
@@ -20,7 +20,7 @@
},
{
"name": "ignorable",
- "type": 21,
+ "type": 5,
"optional": true
},
{
@@ -35,7 +35,7 @@
},
{
"name": "type",
- "type": 22,
+ "type": 15,
"optional": false
}
],
@@ -45,10 +45,10 @@
"kind": "union",
"types": [
2,
- 14,
- 16,
- 18,
- 19
+ 8,
+ 10,
+ 12,
+ 13
]
},
{
@@ -76,12 +76,12 @@
},
{
"name": "mode",
- "type": 12,
+ "type": 6,
"optional": false
},
{
"name": "version",
- "type": 13,
+ "type": 7,
"optional": false
}
],
@@ -95,40 +95,9 @@
"kind": "primitive",
"type": "string"
},
- {
- "kind": "union",
- "types": [
- 6,
- 7,
- 8,
- 9,
- 10,
- 11
- ]
- },
{
"kind": "literal",
- "value": "aborted"
- },
- {
- "kind": "literal",
- "value": "completed"
- },
- {
- "kind": "literal",
- "value": "error"
- },
- {
- "kind": "literal",
- "value": "max-tokens"
- },
- {
- "kind": "literal",
- "value": "pending"
- },
- {
- "kind": "literal",
- "value": "refusal"
+ "value": true
},
{
"kind": "literal",
@@ -163,12 +132,12 @@
},
{
"name": "mode",
- "type": 12,
+ "type": 6,
"optional": false
},
{
"name": "version",
- "type": 15,
+ "type": 9,
"optional": false
}
],
@@ -198,12 +167,12 @@
},
{
"name": "mode",
- "type": 17,
+ "type": 11,
"optional": false
},
{
"name": "version",
- "type": 13,
+ "type": 7,
"optional": false
}
],
@@ -233,12 +202,12 @@
},
{
"name": "mode",
- "type": 17,
+ "type": 11,
"optional": false
},
{
"name": "version",
- "type": 15,
+ "type": 9,
"optional": false
}
],
@@ -264,12 +233,12 @@
},
{
"name": "mode",
- "type": 20,
+ "type": 14,
"optional": false
},
{
"name": "version",
- "type": 15,
+ "type": 9,
"optional": false
}
],
@@ -279,10 +248,6 @@
"kind": "literal",
"value": "unknown"
},
- {
- "kind": "literal",
- "value": true
- },
{
"kind": "literal",
"value": "subagent/catalog"
diff --git a/docs/persistence-changes/2026-09-19-external-subagent-catalog.zh.md b/docs/persistence-changes/2026-09-19-external-subagent-catalog.zh.md
index 6d51ab84c3..34f0d4a087 100644
--- a/docs/persistence-changes/2026-09-19-external-subagent-catalog.zh.md
+++ b/docs/persistence-changes/2026-09-19-external-subagent-catalog.zh.md
@@ -9,7 +9,7 @@ kind: persistence-change
## 概述
-为父级目录中的一次性条目添加可选的外部执行结果。
+为一次性父级目录条目增加可选的外部成员标记。
## 目录
@@ -28,19 +28,19 @@ baseline: false
changes:
- root: "event:subagent/catalog"
previous: "2026-09-20-unknown-child-catalog"
- after: "289dc7167ce5942019b86cab1ccfacb72eaf7460ef3c45e3c23780bf8e030376"
+ after: "568d482eeba04a9422773e7563cf935bd06f16896758cf880cf19b6bab8b1ada"
decision: same-version
```
## 兼容性
-已有 version-0 和 version-1 的一次性及可继续目录事实在没有可选 external 字段时仍然有效。Version-1 的未知模式事实保持不变。该字段标识没有本地 Session 的子任务,并记录 pending 或终态。Pending 表示尚未记录终态,不代表进程存活。结束时追加具有相同身份与创建时间的完整目录事实;投影为每个子任务保留一行。不引入新的 Session 事件类型、完整结果载荷或 Session 格式版本。
+已有 version-0 与 version-1 目录事实无需 external 字段仍然有效。字面量 true 标记没有本地 Session 的子级。创建时写入一次成员事实,执行与清理均不更新该记录。重复子 id 仍然无效。未知模式与可继续条目不接受此标记。本次更新本 PR 尚未接受的可选字段新增记录,不改变已发布世代或 Session 格式版本。
## 验证
-目录投影、V3 到 V4 目录补全与校验、JSONL 目录迁移测试通过:124 项通过,1 项跳过。覆盖两种受支持载荷版本中混合的未知模式和外部条目、外部 pending 到终态的更新,以及拒绝未知模式或可继续条目上的 external 字段。
+聚焦的目录、外部 activation、原生 V4 恢复与客户端测试通过,覆盖创建时成员登记、不发布结算更新、清理失败独立于目录成员关系以及拒绝重复子级。
## 开发备注
diff --git a/docs/persistence-changes/historical-formats/README.i18n.yaml b/docs/persistence-changes/historical-formats/README.i18n.yaml
index 62def62200..1f6bfd547a 100644
--- a/docs/persistence-changes/historical-formats/README.i18n.yaml
+++ b/docs/persistence-changes/historical-formats/README.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 docs/persistence-changes/historical-formats/README.md
-README.md: 453137b6f196b5572973d793f6648220b2366571
-README.zh.md: a64ce4f1e030f35b831afc794625c0ec79ab394b
+README.md: a1064e815879d709fe466441bb4e8441c10b3aae
+README.zh.md: 87c9d32fa6ca5248886dad663a926afc6983406f
diff --git a/docs/persistence-changes/historical-formats/README.md b/docs/persistence-changes/historical-formats/README.md
index 453137b6f1..a1064e8158 100644
--- a/docs/persistence-changes/historical-formats/README.md
+++ b/docs/persistence-changes/historical-formats/README.md
@@ -32,7 +32,7 @@ The index is generated from validated snapshots and the current writer constant.
| 1 | PR #3349 | [V1](v1.md) | [JSON](v1.schema.json) | 54 / 415 |
| 2 | `dsh-v0.1.3-alpha.2` | [V2](v2.md) | [JSON](v2.schema.json) | 56 / 435 |
| 3 | PR #4320 | [V3](v3.md) | [JSON](v3.schema.json) | 60 / 467 |
-| 4 | Current checkout | [Current catalog](../../persistence-catalog.md) | [JSON](../../persistence-schema.json) | 62 / 589 |
+| 4 | Current checkout | [Current catalog](../../persistence-catalog.md) | [JSON](../../persistence-schema.json) | 62 / 587 |
diff --git a/docs/persistence-changes/historical-formats/README.zh.md b/docs/persistence-changes/historical-formats/README.zh.md
index a64ce4f1e0..87c9d32fa6 100644
--- a/docs/persistence-changes/historical-formats/README.zh.md
+++ b/docs/persistence-changes/historical-formats/README.zh.md
@@ -32,7 +32,7 @@ description: "查找从 V0 到工作区写入器版本的每个 Session 格式
| 1 | PR #3349 | [V1](v1.zh.md) | [JSON](v1.schema.json) | 54 / 415 |
| 2 | `dsh-v0.1.3-alpha.2` | [V2](v2.zh.md) | [JSON](v2.schema.json) | 56 / 435 |
| 3 | PR #4320 | [V3](v3.zh.md) | [JSON](v3.schema.json) | 60 / 467 |
-| 4 | 当前工作树 | [当前目录](../../persistence-catalog.zh.md) | [JSON](../../persistence-schema.json) | 62 / 589 |
+| 4 | 当前工作树 | [当前目录](../../persistence-catalog.zh.md) | [JSON](../../persistence-schema.json) | 62 / 587 |
diff --git a/docs/persistence-schema.json b/docs/persistence-schema.json
index 30205cf72a..fc82d0c3f4 100644
--- a/docs/persistence-schema.json
+++ b/docs/persistence-schema.json
@@ -14850,7 +14850,7 @@
"kind": "event",
"event": "subagent/catalog",
"surface": false,
- "digest": "289dc7167ce5942019b86cab1ccfacb72eaf7460ef3c45e3c23780bf8e030376",
+ "digest": "568d482eeba04a9422773e7563cf935bd06f16896758cf880cf19b6bab8b1ada",
"schema": {
"root": 0,
"nodes": [
@@ -14864,7 +14864,7 @@
},
{
"name": "ignorable",
- "type": 21,
+ "type": 5,
"optional": true
},
{
@@ -14879,7 +14879,7 @@
},
{
"name": "type",
- "type": 22,
+ "type": 15,
"optional": false
}
],
@@ -14889,10 +14889,10 @@
"kind": "union",
"types": [
2,
- 14,
- 16,
- 18,
- 19
+ 8,
+ 10,
+ 12,
+ 13
]
},
{
@@ -14920,12 +14920,12 @@
},
{
"name": "mode",
- "type": 12,
+ "type": 6,
"optional": false
},
{
"name": "version",
- "type": 13,
+ "type": 7,
"optional": false
}
],
@@ -14939,40 +14939,9 @@
"kind": "primitive",
"type": "string"
},
- {
- "kind": "union",
- "types": [
- 6,
- 7,
- 8,
- 9,
- 10,
- 11
- ]
- },
{
"kind": "literal",
- "value": "aborted"
- },
- {
- "kind": "literal",
- "value": "completed"
- },
- {
- "kind": "literal",
- "value": "error"
- },
- {
- "kind": "literal",
- "value": "max-tokens"
- },
- {
- "kind": "literal",
- "value": "pending"
- },
- {
- "kind": "literal",
- "value": "refusal"
+ "value": true
},
{
"kind": "literal",
@@ -15007,12 +14976,12 @@
},
{
"name": "mode",
- "type": 12,
+ "type": 6,
"optional": false
},
{
"name": "version",
- "type": 15,
+ "type": 9,
"optional": false
}
],
@@ -15042,12 +15011,12 @@
},
{
"name": "mode",
- "type": 17,
+ "type": 11,
"optional": false
},
{
"name": "version",
- "type": 13,
+ "type": 7,
"optional": false
}
],
@@ -15077,12 +15046,12 @@
},
{
"name": "mode",
- "type": 17,
+ "type": 11,
"optional": false
},
{
"name": "version",
- "type": 15,
+ "type": 9,
"optional": false
}
],
@@ -15108,12 +15077,12 @@
},
{
"name": "mode",
- "type": 20,
+ "type": 14,
"optional": false
},
{
"name": "version",
- "type": 15,
+ "type": 9,
"optional": false
}
],
@@ -15123,10 +15092,6 @@
"kind": "literal",
"value": "unknown"
},
- {
- "kind": "literal",
- "value": true
- },
{
"kind": "literal",
"value": "subagent/catalog"
@@ -21826,103 +21791,6 @@
"packages/extensions/cordis-host-runner/src/index.ts:14"
]
},
- {
- "digest": "0300cd374b1d3d9843f1a5f523c3c8fc920d2bc04be32380331359ac1edb1dbe",
- "schema": {
- "root": 0,
- "nodes": [
- {
- "kind": "object",
- "properties": [
- {
- "name": "childCreatedAt",
- "type": 1,
- "optional": false
- },
- {
- "name": "childId",
- "type": 2,
- "optional": false
- },
- {
- "name": "external",
- "type": 3,
- "optional": true
- },
- {
- "name": "label",
- "type": 2,
- "optional": true
- },
- {
- "name": "mode",
- "type": 10,
- "optional": false
- },
- {
- "name": "version",
- "type": 11,
- "optional": false
- }
- ],
- "indices": []
- },
- {
- "kind": "primitive",
- "type": "number"
- },
- {
- "kind": "primitive",
- "type": "string"
- },
- {
- "kind": "union",
- "types": [
- 4,
- 5,
- 6,
- 7,
- 8,
- 9
- ]
- },
- {
- "kind": "literal",
- "value": "aborted"
- },
- {
- "kind": "literal",
- "value": "completed"
- },
- {
- "kind": "literal",
- "value": "error"
- },
- {
- "kind": "literal",
- "value": "max-tokens"
- },
- {
- "kind": "literal",
- "value": "pending"
- },
- {
- "kind": "literal",
- "value": "refusal"
- },
- {
- "kind": "literal",
- "value": "one-shot"
- },
- {
- "kind": "literal",
- "value": 0
- }
- ]
- },
- "names": [],
- "sources": []
- },
{
"digest": "0341d4c3dfcd53253cdc1e789d9130d3cd848b6f8b0706bf92f313a68b39ff23",
"schema": {
@@ -32712,293 +32580,6 @@
"names": [],
"sources": []
},
- {
- "digest": "289dc7167ce5942019b86cab1ccfacb72eaf7460ef3c45e3c23780bf8e030376",
- "schema": {
- "root": 0,
- "nodes": [
- {
- "kind": "object",
- "properties": [
- {
- "name": "data",
- "type": 1,
- "optional": false
- },
- {
- "name": "ignorable",
- "type": 21,
- "optional": true
- },
- {
- "name": "seq",
- "type": 3,
- "optional": false
- },
- {
- "name": "time",
- "type": 3,
- "optional": false
- },
- {
- "name": "type",
- "type": 22,
- "optional": false
- }
- ],
- "indices": []
- },
- {
- "kind": "union",
- "types": [
- 2,
- 14,
- 16,
- 18,
- 19
- ]
- },
- {
- "kind": "object",
- "properties": [
- {
- "name": "childCreatedAt",
- "type": 3,
- "optional": false
- },
- {
- "name": "childId",
- "type": 4,
- "optional": false
- },
- {
- "name": "external",
- "type": 5,
- "optional": true
- },
- {
- "name": "label",
- "type": 4,
- "optional": true
- },
- {
- "name": "mode",
- "type": 12,
- "optional": false
- },
- {
- "name": "version",
- "type": 13,
- "optional": false
- }
- ],
- "indices": []
- },
- {
- "kind": "primitive",
- "type": "number"
- },
- {
- "kind": "primitive",
- "type": "string"
- },
- {
- "kind": "union",
- "types": [
- 6,
- 7,
- 8,
- 9,
- 10,
- 11
- ]
- },
- {
- "kind": "literal",
- "value": "aborted"
- },
- {
- "kind": "literal",
- "value": "completed"
- },
- {
- "kind": "literal",
- "value": "error"
- },
- {
- "kind": "literal",
- "value": "max-tokens"
- },
- {
- "kind": "literal",
- "value": "pending"
- },
- {
- "kind": "literal",
- "value": "refusal"
- },
- {
- "kind": "literal",
- "value": "one-shot"
- },
- {
- "kind": "literal",
- "value": 0
- },
- {
- "kind": "object",
- "properties": [
- {
- "name": "childCreatedAt",
- "type": 3,
- "optional": false
- },
- {
- "name": "childId",
- "type": 4,
- "optional": false
- },
- {
- "name": "external",
- "type": 5,
- "optional": true
- },
- {
- "name": "label",
- "type": 4,
- "optional": true
- },
- {
- "name": "mode",
- "type": 12,
- "optional": false
- },
- {
- "name": "version",
- "type": 15,
- "optional": false
- }
- ],
- "indices": []
- },
- {
- "kind": "literal",
- "value": 1
- },
- {
- "kind": "object",
- "properties": [
- {
- "name": "childCreatedAt",
- "type": 3,
- "optional": false
- },
- {
- "name": "childId",
- "type": 4,
- "optional": false
- },
- {
- "name": "label",
- "type": 4,
- "optional": false
- },
- {
- "name": "mode",
- "type": 17,
- "optional": false
- },
- {
- "name": "version",
- "type": 13,
- "optional": false
- }
- ],
- "indices": []
- },
- {
- "kind": "literal",
- "value": "continuable"
- },
- {
- "kind": "object",
- "properties": [
- {
- "name": "childCreatedAt",
- "type": 3,
- "optional": false
- },
- {
- "name": "childId",
- "type": 4,
- "optional": false
- },
- {
- "name": "label",
- "type": 4,
- "optional": false
- },
- {
- "name": "mode",
- "type": 17,
- "optional": false
- },
- {
- "name": "version",
- "type": 15,
- "optional": false
- }
- ],
- "indices": []
- },
- {
- "kind": "object",
- "properties": [
- {
- "name": "childCreatedAt",
- "type": 3,
- "optional": false
- },
- {
- "name": "childId",
- "type": 4,
- "optional": false
- },
- {
- "name": "label",
- "type": 4,
- "optional": true
- },
- {
- "name": "mode",
- "type": 20,
- "optional": false
- },
- {
- "name": "version",
- "type": 15,
- "optional": false
- }
- ],
- "indices": []
- },
- {
- "kind": "literal",
- "value": "unknown"
- },
- {
- "kind": "literal",
- "value": true
- },
- {
- "kind": "literal",
- "value": "subagent/catalog"
- }
- ]
- },
- "names": [],
- "sources": []
- },
{
"digest": "290f32bfd81ff85a8095886a8066fae06e885942b63a8bcd6a8d66d328445808",
"schema": {
@@ -43987,20 +43568,6 @@
"names": [],
"sources": []
},
- {
- "digest": "4a561c111a78d4520e850c93795369ea169ff86abd082aee86c8fd38121a65aa",
- "schema": {
- "root": 0,
- "nodes": [
- {
- "kind": "literal",
- "value": "refusal"
- }
- ]
- },
- "names": [],
- "sources": []
- },
{
"digest": "4a9bef9f27a942520cc2279ca7dd3082556e4bff543f66b5c0333e1bbed1cd86",
"schema": {
@@ -44655,6 +44222,227 @@
"names": [],
"sources": []
},
+ {
+ "digest": "4e6c80858802af53690bae54e01a4e416c7aec5b34a8817b21d6cf33a9603b52",
+ "schema": {
+ "root": 0,
+ "nodes": [
+ {
+ "kind": "union",
+ "types": [
+ 1,
+ 6,
+ 8,
+ 10,
+ 13
+ ]
+ },
+ {
+ "kind": "object",
+ "properties": [
+ {
+ "name": "childCreatedAt",
+ "type": 2,
+ "optional": false
+ },
+ {
+ "name": "childId",
+ "type": 3,
+ "optional": false
+ },
+ {
+ "name": "label",
+ "type": 3,
+ "optional": false
+ },
+ {
+ "name": "mode",
+ "type": 4,
+ "optional": false
+ },
+ {
+ "name": "version",
+ "type": 5,
+ "optional": false
+ }
+ ],
+ "indices": []
+ },
+ {
+ "kind": "primitive",
+ "type": "number"
+ },
+ {
+ "kind": "primitive",
+ "type": "string"
+ },
+ {
+ "kind": "literal",
+ "value": "continuable"
+ },
+ {
+ "kind": "literal",
+ "value": 0
+ },
+ {
+ "kind": "object",
+ "properties": [
+ {
+ "name": "childCreatedAt",
+ "type": 2,
+ "optional": false
+ },
+ {
+ "name": "childId",
+ "type": 3,
+ "optional": false
+ },
+ {
+ "name": "label",
+ "type": 3,
+ "optional": false
+ },
+ {
+ "name": "mode",
+ "type": 4,
+ "optional": false
+ },
+ {
+ "name": "version",
+ "type": 7,
+ "optional": false
+ }
+ ],
+ "indices": []
+ },
+ {
+ "kind": "literal",
+ "value": 1
+ },
+ {
+ "kind": "object",
+ "properties": [
+ {
+ "name": "childCreatedAt",
+ "type": 2,
+ "optional": false
+ },
+ {
+ "name": "childId",
+ "type": 3,
+ "optional": false
+ },
+ {
+ "name": "label",
+ "type": 3,
+ "optional": true
+ },
+ {
+ "name": "mode",
+ "type": 9,
+ "optional": false
+ },
+ {
+ "name": "version",
+ "type": 7,
+ "optional": false
+ }
+ ],
+ "indices": []
+ },
+ {
+ "kind": "literal",
+ "value": "unknown"
+ },
+ {
+ "kind": "object",
+ "properties": [
+ {
+ "name": "childCreatedAt",
+ "type": 2,
+ "optional": false
+ },
+ {
+ "name": "childId",
+ "type": 3,
+ "optional": false
+ },
+ {
+ "name": "external",
+ "type": 11,
+ "optional": true
+ },
+ {
+ "name": "label",
+ "type": 3,
+ "optional": true
+ },
+ {
+ "name": "mode",
+ "type": 12,
+ "optional": false
+ },
+ {
+ "name": "version",
+ "type": 5,
+ "optional": false
+ }
+ ],
+ "indices": []
+ },
+ {
+ "kind": "literal",
+ "value": true
+ },
+ {
+ "kind": "literal",
+ "value": "one-shot"
+ },
+ {
+ "kind": "object",
+ "properties": [
+ {
+ "name": "childCreatedAt",
+ "type": 2,
+ "optional": false
+ },
+ {
+ "name": "childId",
+ "type": 3,
+ "optional": false
+ },
+ {
+ "name": "external",
+ "type": 11,
+ "optional": true
+ },
+ {
+ "name": "label",
+ "type": 3,
+ "optional": true
+ },
+ {
+ "name": "mode",
+ "type": 12,
+ "optional": false
+ },
+ {
+ "name": "version",
+ "type": 7,
+ "optional": false
+ }
+ ],
+ "indices": []
+ }
+ ]
+ },
+ "names": [
+ "packages/subagent/subagent/src/catalog.ts#SubagentCatalogEvent"
+ ],
+ "sources": [
+ "packages/subagent/subagent/src/catalog.ts:28"
+ ]
+ },
{
"digest": "4e887768586528565381dadbddee6cef555874148089f4579e0b4b1ad096fc9c",
"schema": {
@@ -49784,6 +49572,258 @@
"names": [],
"sources": []
},
+ {
+ "digest": "568d482eeba04a9422773e7563cf935bd06f16896758cf880cf19b6bab8b1ada",
+ "schema": {
+ "root": 0,
+ "nodes": [
+ {
+ "kind": "object",
+ "properties": [
+ {
+ "name": "data",
+ "type": 1,
+ "optional": false
+ },
+ {
+ "name": "ignorable",
+ "type": 5,
+ "optional": true
+ },
+ {
+ "name": "seq",
+ "type": 3,
+ "optional": false
+ },
+ {
+ "name": "time",
+ "type": 3,
+ "optional": false
+ },
+ {
+ "name": "type",
+ "type": 15,
+ "optional": false
+ }
+ ],
+ "indices": []
+ },
+ {
+ "kind": "union",
+ "types": [
+ 2,
+ 8,
+ 10,
+ 12,
+ 13
+ ]
+ },
+ {
+ "kind": "object",
+ "properties": [
+ {
+ "name": "childCreatedAt",
+ "type": 3,
+ "optional": false
+ },
+ {
+ "name": "childId",
+ "type": 4,
+ "optional": false
+ },
+ {
+ "name": "external",
+ "type": 5,
+ "optional": true
+ },
+ {
+ "name": "label",
+ "type": 4,
+ "optional": true
+ },
+ {
+ "name": "mode",
+ "type": 6,
+ "optional": false
+ },
+ {
+ "name": "version",
+ "type": 7,
+ "optional": false
+ }
+ ],
+ "indices": []
+ },
+ {
+ "kind": "primitive",
+ "type": "number"
+ },
+ {
+ "kind": "primitive",
+ "type": "string"
+ },
+ {
+ "kind": "literal",
+ "value": true
+ },
+ {
+ "kind": "literal",
+ "value": "one-shot"
+ },
+ {
+ "kind": "literal",
+ "value": 0
+ },
+ {
+ "kind": "object",
+ "properties": [
+ {
+ "name": "childCreatedAt",
+ "type": 3,
+ "optional": false
+ },
+ {
+ "name": "childId",
+ "type": 4,
+ "optional": false
+ },
+ {
+ "name": "external",
+ "type": 5,
+ "optional": true
+ },
+ {
+ "name": "label",
+ "type": 4,
+ "optional": true
+ },
+ {
+ "name": "mode",
+ "type": 6,
+ "optional": false
+ },
+ {
+ "name": "version",
+ "type": 9,
+ "optional": false
+ }
+ ],
+ "indices": []
+ },
+ {
+ "kind": "literal",
+ "value": 1
+ },
+ {
+ "kind": "object",
+ "properties": [
+ {
+ "name": "childCreatedAt",
+ "type": 3,
+ "optional": false
+ },
+ {
+ "name": "childId",
+ "type": 4,
+ "optional": false
+ },
+ {
+ "name": "label",
+ "type": 4,
+ "optional": false
+ },
+ {
+ "name": "mode",
+ "type": 11,
+ "optional": false
+ },
+ {
+ "name": "version",
+ "type": 7,
+ "optional": false
+ }
+ ],
+ "indices": []
+ },
+ {
+ "kind": "literal",
+ "value": "continuable"
+ },
+ {
+ "kind": "object",
+ "properties": [
+ {
+ "name": "childCreatedAt",
+ "type": 3,
+ "optional": false
+ },
+ {
+ "name": "childId",
+ "type": 4,
+ "optional": false
+ },
+ {
+ "name": "label",
+ "type": 4,
+ "optional": false
+ },
+ {
+ "name": "mode",
+ "type": 11,
+ "optional": false
+ },
+ {
+ "name": "version",
+ "type": 9,
+ "optional": false
+ }
+ ],
+ "indices": []
+ },
+ {
+ "kind": "object",
+ "properties": [
+ {
+ "name": "childCreatedAt",
+ "type": 3,
+ "optional": false
+ },
+ {
+ "name": "childId",
+ "type": 4,
+ "optional": false
+ },
+ {
+ "name": "label",
+ "type": 4,
+ "optional": true
+ },
+ {
+ "name": "mode",
+ "type": 14,
+ "optional": false
+ },
+ {
+ "name": "version",
+ "type": 9,
+ "optional": false
+ }
+ ],
+ "indices": []
+ },
+ {
+ "kind": "literal",
+ "value": "unknown"
+ },
+ {
+ "kind": "literal",
+ "value": "subagent/catalog"
+ }
+ ]
+ },
+ "names": [],
+ "sources": []
+ },
{
"digest": "56b7c047694875a843ba020dfea3c8380d54ae4ff88ab349e72004dc21e07c23",
"schema": {
@@ -59103,6 +59143,72 @@
"names": [],
"sources": []
},
+ {
+ "digest": "718c94fb5c5ac43cdd163d384bd75b3ca8bc355a02727423cd42d97948263128",
+ "schema": {
+ "root": 0,
+ "nodes": [
+ {
+ "kind": "object",
+ "properties": [
+ {
+ "name": "childCreatedAt",
+ "type": 1,
+ "optional": false
+ },
+ {
+ "name": "childId",
+ "type": 2,
+ "optional": false
+ },
+ {
+ "name": "external",
+ "type": 3,
+ "optional": true
+ },
+ {
+ "name": "label",
+ "type": 2,
+ "optional": true
+ },
+ {
+ "name": "mode",
+ "type": 4,
+ "optional": false
+ },
+ {
+ "name": "version",
+ "type": 5,
+ "optional": false
+ }
+ ],
+ "indices": []
+ },
+ {
+ "kind": "primitive",
+ "type": "number"
+ },
+ {
+ "kind": "primitive",
+ "type": "string"
+ },
+ {
+ "kind": "literal",
+ "value": true
+ },
+ {
+ "kind": "literal",
+ "value": "one-shot"
+ },
+ {
+ "kind": "literal",
+ "value": 1
+ }
+ ]
+ },
+ "names": [],
+ "sources": []
+ },
{
"digest": "719c51f67074f97034a6c94297fe1f4b5d3c8d392c57586dab732aa5398c7a91",
"schema": {
@@ -60871,6 +60977,72 @@
"names": [],
"sources": []
},
+ {
+ "digest": "7e25366aed8235508ef657343f0f263f3354b8418406dcd857acecd7e1c74cd0",
+ "schema": {
+ "root": 0,
+ "nodes": [
+ {
+ "kind": "object",
+ "properties": [
+ {
+ "name": "childCreatedAt",
+ "type": 1,
+ "optional": false
+ },
+ {
+ "name": "childId",
+ "type": 2,
+ "optional": false
+ },
+ {
+ "name": "external",
+ "type": 3,
+ "optional": true
+ },
+ {
+ "name": "label",
+ "type": 2,
+ "optional": true
+ },
+ {
+ "name": "mode",
+ "type": 4,
+ "optional": false
+ },
+ {
+ "name": "version",
+ "type": 5,
+ "optional": false
+ }
+ ],
+ "indices": []
+ },
+ {
+ "kind": "primitive",
+ "type": "number"
+ },
+ {
+ "kind": "primitive",
+ "type": "string"
+ },
+ {
+ "kind": "literal",
+ "value": true
+ },
+ {
+ "kind": "literal",
+ "value": "one-shot"
+ },
+ {
+ "kind": "literal",
+ "value": 0
+ }
+ ]
+ },
+ "names": [],
+ "sources": []
+ },
{
"digest": "7eaef7a763ba5b949b8464bc8add8980a3a5ab566014b69787aeba71e5dfe07b",
"schema": {
@@ -70623,51 +70795,6 @@
"names": [],
"sources": []
},
- {
- "digest": "ae2a48f2f3e53adc041eb3d7ee98a4880af2f81484c20c1d07a0f80dbfb7c0c1",
- "schema": {
- "root": 0,
- "nodes": [
- {
- "kind": "union",
- "types": [
- 1,
- 2,
- 3,
- 4,
- 5,
- 6
- ]
- },
- {
- "kind": "literal",
- "value": "aborted"
- },
- {
- "kind": "literal",
- "value": "completed"
- },
- {
- "kind": "literal",
- "value": "error"
- },
- {
- "kind": "literal",
- "value": "max-tokens"
- },
- {
- "kind": "literal",
- "value": "pending"
- },
- {
- "kind": "literal",
- "value": "refusal"
- }
- ]
- },
- "names": [],
- "sources": []
- },
{
"digest": "ae63d9d1080ed8945031594d93f8d96651d7101b848719cc3e1475875cd3d649",
"schema": {
@@ -73167,103 +73294,6 @@
"names": [],
"sources": []
},
- {
- "digest": "bbf8af8c6b0edb0b150ea997c51bdc63e8def8428adb228bbc88af0577344391",
- "schema": {
- "root": 0,
- "nodes": [
- {
- "kind": "object",
- "properties": [
- {
- "name": "childCreatedAt",
- "type": 1,
- "optional": false
- },
- {
- "name": "childId",
- "type": 2,
- "optional": false
- },
- {
- "name": "external",
- "type": 3,
- "optional": true
- },
- {
- "name": "label",
- "type": 2,
- "optional": true
- },
- {
- "name": "mode",
- "type": 10,
- "optional": false
- },
- {
- "name": "version",
- "type": 11,
- "optional": false
- }
- ],
- "indices": []
- },
- {
- "kind": "primitive",
- "type": "number"
- },
- {
- "kind": "primitive",
- "type": "string"
- },
- {
- "kind": "union",
- "types": [
- 4,
- 5,
- 6,
- 7,
- 8,
- 9
- ]
- },
- {
- "kind": "literal",
- "value": "aborted"
- },
- {
- "kind": "literal",
- "value": "completed"
- },
- {
- "kind": "literal",
- "value": "error"
- },
- {
- "kind": "literal",
- "value": "max-tokens"
- },
- {
- "kind": "literal",
- "value": "pending"
- },
- {
- "kind": "literal",
- "value": "refusal"
- },
- {
- "kind": "literal",
- "value": "one-shot"
- },
- {
- "kind": "literal",
- "value": 1
- }
- ]
- },
- "names": [],
- "sources": []
- },
{
"digest": "bcf0caf62d964b2fcf5404bd5c909cfa9a21c3f3c96e6b7e36d9e33565223825",
"schema": {
@@ -73953,258 +73983,6 @@
"names": [],
"sources": []
},
- {
- "digest": "c974738c089d11324eac001a8e4c032d278cebc0221efb9896ff72b922d82b7e",
- "schema": {
- "root": 0,
- "nodes": [
- {
- "kind": "union",
- "types": [
- 1,
- 13,
- 15,
- 17,
- 18
- ]
- },
- {
- "kind": "object",
- "properties": [
- {
- "name": "childCreatedAt",
- "type": 2,
- "optional": false
- },
- {
- "name": "childId",
- "type": 3,
- "optional": false
- },
- {
- "name": "external",
- "type": 4,
- "optional": true
- },
- {
- "name": "label",
- "type": 3,
- "optional": true
- },
- {
- "name": "mode",
- "type": 11,
- "optional": false
- },
- {
- "name": "version",
- "type": 12,
- "optional": false
- }
- ],
- "indices": []
- },
- {
- "kind": "primitive",
- "type": "number"
- },
- {
- "kind": "primitive",
- "type": "string"
- },
- {
- "kind": "union",
- "types": [
- 5,
- 6,
- 7,
- 8,
- 9,
- 10
- ]
- },
- {
- "kind": "literal",
- "value": "aborted"
- },
- {
- "kind": "literal",
- "value": "completed"
- },
- {
- "kind": "literal",
- "value": "error"
- },
- {
- "kind": "literal",
- "value": "max-tokens"
- },
- {
- "kind": "literal",
- "value": "pending"
- },
- {
- "kind": "literal",
- "value": "refusal"
- },
- {
- "kind": "literal",
- "value": "one-shot"
- },
- {
- "kind": "literal",
- "value": 1
- },
- {
- "kind": "object",
- "properties": [
- {
- "name": "childCreatedAt",
- "type": 2,
- "optional": false
- },
- {
- "name": "childId",
- "type": 3,
- "optional": false
- },
- {
- "name": "external",
- "type": 4,
- "optional": true
- },
- {
- "name": "label",
- "type": 3,
- "optional": true
- },
- {
- "name": "mode",
- "type": 11,
- "optional": false
- },
- {
- "name": "version",
- "type": 14,
- "optional": false
- }
- ],
- "indices": []
- },
- {
- "kind": "literal",
- "value": 0
- },
- {
- "kind": "object",
- "properties": [
- {
- "name": "childCreatedAt",
- "type": 2,
- "optional": false
- },
- {
- "name": "childId",
- "type": 3,
- "optional": false
- },
- {
- "name": "label",
- "type": 3,
- "optional": false
- },
- {
- "name": "mode",
- "type": 16,
- "optional": false
- },
- {
- "name": "version",
- "type": 12,
- "optional": false
- }
- ],
- "indices": []
- },
- {
- "kind": "literal",
- "value": "continuable"
- },
- {
- "kind": "object",
- "properties": [
- {
- "name": "childCreatedAt",
- "type": 2,
- "optional": false
- },
- {
- "name": "childId",
- "type": 3,
- "optional": false
- },
- {
- "name": "label",
- "type": 3,
- "optional": false
- },
- {
- "name": "mode",
- "type": 16,
- "optional": false
- },
- {
- "name": "version",
- "type": 14,
- "optional": false
- }
- ],
- "indices": []
- },
- {
- "kind": "object",
- "properties": [
- {
- "name": "childCreatedAt",
- "type": 2,
- "optional": false
- },
- {
- "name": "childId",
- "type": 3,
- "optional": false
- },
- {
- "name": "label",
- "type": 3,
- "optional": true
- },
- {
- "name": "mode",
- "type": 19,
- "optional": false
- },
- {
- "name": "version",
- "type": 12,
- "optional": false
- }
- ],
- "indices": []
- },
- {
- "kind": "literal",
- "value": "unknown"
- }
- ]
- },
- "names": [
- "packages/subagent/subagent/src/catalog.ts#SubagentCatalogEvent"
- ],
- "sources": [
- "packages/subagent/subagent/src/catalog.ts:28"
- ]
- },
{
"digest": "c99e20441bf15341e9db54385e558e5c03d21dff2eb1ac08ccbf47c2d8747954",
"schema": {
diff --git a/docs/subsystems/subagent.i18n.yaml b/docs/subsystems/subagent.i18n.yaml
index 08ae8cfcf1..952708e10a 100644
--- a/docs/subsystems/subagent.i18n.yaml
+++ b/docs/subsystems/subagent.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 docs/subsystems/subagent.md
-subagent.md: b6113686c677213bd052907b72eb54aa6e690489
-subagent.zh.md: 7a5bd28e0ab8a3a4384e14959145e4565d9d0d4b
+subagent.md: 03172744ad388322fb4b292317aeec01acab6de9
+subagent.zh.md: 2c1399eeb697480ebfceef86f76b0cda5fc912d6
diff --git a/docs/subsystems/subagent.md b/docs/subsystems/subagent.md
index b6113686c6..03172744ad 100644
--- a/docs/subsystems/subagent.md
+++ b/docs/subsystems/subagent.md
@@ -219,7 +219,7 @@ type SubagentInterruptAuthority =
## Durable enumeration
-`listChildren` reads the parent-owned catalog. `listDescendants` combines Session corpus identity with parent catalogs, including external leaves beneath ordinary Sessions. External one-shot entries carry an optional `external` outcome and cannot open a child Session or accept follow-ups. `pending` means no terminal outcome is recorded, not that a process is live. Complete results go to the caller or the parent completion notice. `list_agents` exposes only continuable children with `running` or `inactive` activity; see the [control tool](../../packages/subagent/tool-subagent-control/README.md#list_agents) for its scopes and diagnostics.
+`listChildren` reads the parent-owned catalog. `listDescendants` combines Session corpus identity with parent catalogs, including external leaves beneath ordinary Sessions. External one-shot entries carry `external: true` and cannot open a child Session or accept follow-ups. Catalog entries record membership at creation without execution status. Complete results go to the caller or the parent completion notice. `list_agents` exposes only continuable children with `running` or `inactive` activity; see the [control tool](../../packages/subagent/tool-subagent-control/README.md#list_agents) for its scopes and diagnostics.
```ts type-equiv
/**
diff --git a/docs/subsystems/subagent.zh.md b/docs/subsystems/subagent.zh.md
index 7a5bd28e0a..2c1399eeb6 100644
--- a/docs/subsystems/subagent.zh.md
+++ b/docs/subsystems/subagent.zh.md
@@ -219,7 +219,7 @@ type SubagentInterruptAuthority =
## 持久枚举
-`listChildren` 读取父级拥有的目录。`listDescendants` 结合 Session 语料中的身份与父级目录,包括普通 Session 下的外部叶子项。外部一次性条目携带可选的 `external` 结果状态,不能打开子 Session 或接受后续消息。`pending` 表示尚无终态记录,不表示进程存活。完整结果交给调用方或父级完成通知。`list_agents` 只展示可继续子级,其活跃状态为 `running` 或 `inactive`;作用域与诊断信息见[控制工具](../../packages/subagent/tool-subagent-control/README.zh.md#list_agents)。
+`listChildren` 读取父级拥有的目录。`listDescendants` 结合 Session 语料中的身份与父级目录,包括普通 Session 下的外部叶子项。外部一次性条目携带 `external: true`,不能打开子 Session 或接受后续消息。目录在创建时记录成员关系,不携带执行状态。完整结果交给调用方或父级完成通知。`list_agents` 只展示可继续子级,其活跃状态为 `running` 或 `inactive`;作用域与诊断信息见[控制工具](../../packages/subagent/tool-subagent-control/README.zh.md#list_agents)。
```ts type-equiv
/**
diff --git a/packages/client/ui-subagent/README.i18n.yaml b/packages/client/ui-subagent/README.i18n.yaml
index 9dc14fc396..89b02134f4 100644
--- a/packages/client/ui-subagent/README.i18n.yaml
+++ b/packages/client/ui-subagent/README.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 packages/client/ui-subagent/README.md
-README.md: 75f1e46d55a540abdb6d1bb36526088af871d6cb
-README.zh.md: 55173646ea63884b7803b951a2189acb68695f00
+README.md: eecb0e0d0a85de5ce67dbc674df8f966b020882e
+README.zh.md: edfef9ee1dcb565dda247ca4e7f834d304566ed8
diff --git a/packages/client/ui-subagent/README.md b/packages/client/ui-subagent/README.md
index 75f1e46d55..eecb0e0d0a 100644
--- a/packages/client/ui-subagent/README.md
+++ b/packages/client/ui-subagent/README.md
@@ -9,7 +9,7 @@ English | [中文](README.zh.md)
## Summary
-Browse local subagent conversations and external tasks from the parent header. Local conversations show activity, token usage, and duration; continuable children accept follow-up prompts and support Stop. External tasks appear as non-navigable leaves with their last recorded outcome; pending does not assert process liveness. Historical one-shot conversations open as read-only records. The ordinary sidebar omits subagent conversations, and the separate `@` source inserts a running child's label without granting continuation authority.
+Browse local subagent conversations and external tasks from the parent header. Local conversations show activity, token usage, and duration; continuable children accept follow-up prompts and support Stop. External tasks appear as non-navigable leaves without an execution status. Historical one-shot conversations open as read-only records. The ordinary sidebar omits subagent conversations, and the separate `@` source inserts a running child's label without granting continuation authority.
## Table of Contents
diff --git a/packages/client/ui-subagent/README.zh.md b/packages/client/ui-subagent/README.zh.md
index 55173646ea..edfef9ee1d 100644
--- a/packages/client/ui-subagent/README.zh.md
+++ b/packages/client/ui-subagent/README.zh.md
@@ -9,7 +9,7 @@ kind: "package-reference"
## 概述
-从父会话页头浏览本地子代理对话与外部任务。本地对话展示活跃状态、token 用量和耗时;可继续子代理接受后续提示词并支持 Stop。外部任务显示为不可打开的叶子项,展示最近记录的结果状态;pending 不声明进程存活。历史一次性对话以只读记录打开。普通侧边栏省略子代理对话;独立的 `@` 来源插入运行中子代理的标签,不授予继续执行权限。
+从父会话页头浏览本地子代理对话与外部任务。本地对话展示活跃状态、token 用量和耗时;可继续子代理接受后续提示词并支持 Stop。外部任务显示为不可打开的叶子项,不展示执行状态。历史一次性对话以只读记录打开。普通侧边栏省略子代理对话;独立的 `@` 来源插入运行中子代理的标签,不授予继续执行权限。
## 目录
diff --git a/packages/client/ui-subagent/src/client/SubagentHeaderLineage.tsx b/packages/client/ui-subagent/src/client/SubagentHeaderLineage.tsx
index 2aaf0b9820..5d95b29440 100644
--- a/packages/client/ui-subagent/src/client/SubagentHeaderLineage.tsx
+++ b/packages/client/ui-subagent/src/client/SubagentHeaderLineage.tsx
@@ -241,9 +241,9 @@ function CatalogRows({
const label = entry.label ?? entry.id
const mode = external !== undefined ? t('mode.external') : entry.mode === 'unknown' ? t('mode.unknown')
: entry.mode === 'one-shot' ? t('mode.oneShot') : t('mode.continuable')
- const completed = external !== undefined ? external === 'completed' : entry.activity === 'inactive'
+ const completed = external === undefined && entry.activity === 'inactive'
&& summary?.projectionValues?.subagentTiming?.lastTurnCompleted === true
- const activity = external !== undefined ? t(`external.${external}`) : entry.activity === 'running'
+ const activity = external !== undefined ? undefined : entry.activity === 'running'
? t('activity.running')
: completed
? t('activity.completed')
diff --git a/packages/client/ui-subagent/src/client/locales.ts b/packages/client/ui-subagent/src/client/locales.ts
index 52eb13e8d5..575b249c97 100644
--- a/packages/client/ui-subagent/src/client/locales.ts
+++ b/packages/client/ui-subagent/src/client/locales.ts
@@ -23,12 +23,6 @@ export const zh = {
'load.error': '无法加载子代理',
'retry': '重试',
'mode.external': '外部任务',
- 'external.pending': '结果待定',
- 'external.completed': '已完成',
- 'external.aborted': '已中止',
- 'external.error': '失败',
- 'external.refusal': '已拒绝',
- 'external.max-tokens': '已达输出上限',
'mode.oneShot': '一次性',
'mode.continuable': '可继续',
'mode.unknown': '模式未知',
@@ -72,12 +66,6 @@ export const en: Record = {
'load.error': 'Unable to load subagents',
'retry': 'Retry',
'mode.external': 'External task',
- 'external.pending': 'Outcome pending',
- 'external.completed': 'Completed',
- 'external.aborted': 'Aborted',
- 'external.error': 'Failed',
- 'external.refusal': 'Refused',
- 'external.max-tokens': 'Output limit reached',
'mode.oneShot': 'one-shot',
'mode.continuable': 'continuable',
'mode.unknown': 'unknown mode',
diff --git a/packages/client/ui-subagent/tests/conversation-ui.client.spec.tsx b/packages/client/ui-subagent/tests/conversation-ui.client.spec.tsx
index 523afc6f40..50a0e52424 100644
--- a/packages/client/ui-subagent/tests/conversation-ui.client.spec.tsx
+++ b/packages/client/ui-subagent/tests/conversation-ui.client.spec.tsx
@@ -948,19 +948,17 @@ describe('SubagentReadOnlyComposer', () => {
})
-it('updates an external leaf from shared projection values without opening a child Session', () => {
- const external = (status: 'pending' | 'completed') => props(catalog({ entries: [
- { id: CHILD, mode: 'one-shot', label: 'External review', activity: 'inactive', external: status },
+it('shows external membership without an execution status or child Session navigation', () => {
+ const input = props(catalog({ entries: [
+ { id: CHILD, mode: 'one-shot', label: 'External review', activity: 'inactive', external: true },
] }))
- const input = external('pending')
- const view = render()
+ render()
hoverCatalog(screen.getByRole('button', { name: /子代理/ }))
- const row = screen.getByRole('treeitem', { name: /External review.*结果待定/ })
+ const row = screen.getByRole('treeitem', { name: /External review.*外部任务/ })
expect(row.getAttribute('aria-disabled')).toBe('true')
expect(row.hasAttribute('aria-expanded')).toBe(false)
+ expect(row.getAttribute('aria-label')).not.toMatch(/已完成|未运行|结果待定/)
fireEvent.click(row)
expect(input.openChild).not.toHaveBeenCalled()
expect(input.openChildAside).not.toHaveBeenCalled()
- view.rerender()
- expect(screen.getByRole('treeitem', { name: /External review.*已完成/ })).toBeTruthy()
})
diff --git a/packages/client/ui-workflow-run/tests/workflow-run.client.spec.tsx b/packages/client/ui-workflow-run/tests/workflow-run.client.spec.tsx
index 49430c2d20..995d5b7d6e 100644
--- a/packages/client/ui-workflow-run/tests/workflow-run.client.spec.tsx
+++ b/packages/client/ui-workflow-run/tests/workflow-run.client.spec.tsx
@@ -878,7 +878,7 @@ describe('WorkflowRunPanel', () => {
})
it.each([
- ['external execution', listState({ projectionsBySession: { [PARENT_ID]: { state: 'ready', error: null, values: { subagentCatalog: [{ createdAt: 1, id: CHILD_ID, mode: 'one-shot', external: 'pending' }] } } } }), 'running'],
+ ['external execution', listState({ projectionsBySession: { [PARENT_ID]: { state: 'ready', error: null, values: { subagentCatalog: [{ createdAt: 1, id: CHILD_ID, mode: 'one-shot', external: true }] } } } }), 'running'],
['catalog absent', listState({ projectionsBySession: {} }), 'running'],
['catalog empty', listState({ projectionsBySession: { [PARENT_ID]: { state: 'ready', error: null, values: { subagentCatalog: [] } } } }), 'running'],
['wrong parent', listState({ projectionsBySession: { ['other' as SessionId]: { state: 'ready', error: null, values: { subagentCatalog: [{ createdAt: 1, id: CHILD_ID, mode: 'one-shot' }] } } } }), 'running'],
diff --git a/packages/extensions/tool-cordis/src/api-catalog.ts b/packages/extensions/tool-cordis/src/api-catalog.ts
index 39d048d95b..3af4220d34 100644
--- a/packages/extensions/tool-cordis/src/api-catalog.ts
+++ b/packages/extensions/tool-cordis/src/api-catalog.ts
@@ -4696,10 +4696,6 @@ export const TYPE_API: readonly TypeApiEntry[] = [
name: 'EpochHeader',
declaration: 'export interface EpochHeader {\n config: LlmCallConfig;\n adapterDefaults?: LlmCallConfigAdapterDefaults;\n tools?: ToolSchema[];\n system?: never;\n}',
},
- {
- name: 'ExternalSubagentStatus',
- declaration: 'export type ExternalSubagentStatus = \'pending\' | \'completed\' | \'max-tokens\' | \'aborted\' | \'refusal\' | \'error\';',
- },
{
name: 'FeedbackCategory',
declaration: 'export type FeedbackCategory = \'task-result\' | \'instruction-following\' | \'product-interaction\' | \'service-stability\' | \'resource-cost\' | \'security-privacy-permission\' | \'other\';',
@@ -6426,11 +6422,11 @@ export const TYPE_API: readonly TypeApiEntry[] = [
},
{
name: 'SubagentCatalogEntry',
- declaration: 'export type SubagentCatalogEntry = {\n readonly id: SessionId;\n readonly createdAt: number;\n} & ({\n readonly mode: \'one-shot\';\n readonly label?: string;\n readonly external?: ExternalSubagentStatus;\n} | {\n readonly mode: \'continuable\';\n readonly label: string;\n} | {\n readonly mode: \'unknown\';\n readonly label?: string;\n});',
+ declaration: 'export type SubagentCatalogEntry = {\n readonly id: SessionId;\n readonly createdAt: number;\n} & ({\n readonly mode: \'one-shot\';\n readonly label?: string;\n readonly external?: true;\n} | {\n readonly mode: \'continuable\';\n readonly label: string;\n} | {\n readonly mode: \'unknown\';\n readonly label?: string;\n});',
},
{
name: 'SubagentCatalogRow',
- declaration: 'export type SubagentCatalogRow = {\n readonly id: SessionId;\n readonly activity: \'running\' | \'inactive\';\n} & ({\n readonly mode: \'one-shot\';\n readonly external?: ExternalSubagentStatus;\n readonly label?: string;\n} | {\n readonly mode: \'continuable\';\n readonly label: string;\n});',
+ declaration: 'export type SubagentCatalogRow = {\n readonly id: SessionId;\n readonly activity: \'running\' | \'inactive\';\n} & ({\n readonly mode: \'one-shot\';\n readonly external?: true;\n readonly label?: string;\n} | {\n readonly mode: \'continuable\';\n readonly label: string;\n});',
},
{
name: 'SubagentDescendantListEntry',
diff --git a/packages/session/session-format-v3-to-v4/README.i18n.yaml b/packages/session/session-format-v3-to-v4/README.i18n.yaml
index 467e3ca59e..7512038cd6 100644
--- a/packages/session/session-format-v3-to-v4/README.i18n.yaml
+++ b/packages/session/session-format-v3-to-v4/README.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 packages/session/session-format-v3-to-v4/README.md
-README.md: 593b065c50e0e3b2eecf0c88c6aaa547a51aaf6b
-README.zh.md: 26809e369330b9f06520a9a97d5a815ec46d0a65
+README.md: 60f3c7e41b133a6dbd410fe2e99a134c337efe07
+README.zh.md: 568ef405478681f115ac4a3a469db89675a88aa7
diff --git a/packages/session/session-format-v3-to-v4/README.md b/packages/session/session-format-v3-to-v4/README.md
index 593b065c50..60f3c7e41b 100644
--- a/packages/session/session-format-v3-to-v4/README.md
+++ b/packages/session/session-format-v3-to-v4/README.md
@@ -243,7 +243,7 @@ Current common admission does not validate each user/tool/developer content bloc
| `compaction/start`, `compaction/summary`, `compaction/end` | Match compaction id, source command, and the active turn context. Summary spans name exact current-surface nodes and exclude the protected head; successful completion has one summary. Inherited unfinished compactions expire at the end-seed marker. |
| `compaction/prune` | Its span names exact current-surface nodes and excludes the protected head; it does not require a compaction transaction or its owner fields. |
| Compact checkpoint replacement | Its `compact-checkpoint` source identifies the active compaction. |
-| Native `subagent/catalog` | Validates own version-0/version-1 payloads after the inherited cut. A child id is unique except for one external pending-to-terminal update with unchanged mode, creation time, and label. Native reads neither collect child logs nor compare their physical facts; inherited entries do not establish own membership. |
+| Native `subagent/catalog` | Validates own version-0/version-1 payloads after the inherited cut. Each child id must be unique. Native reads neither collect child logs nor compare their physical facts; inherited entries do not establish own membership. |
| Inherited cut and delivery | Apply the marker, coordinate, and generation-ownership rules stated above. |
These checks are generation-owned in [relationships.ts](src/relationships.ts). Full common message/envelope acceptance and plugin-owned message projections additionally use the installed Session; the exported V4 restorer alone is not a replacement for complete catalog restoration.
diff --git a/packages/session/session-format-v3-to-v4/README.zh.md b/packages/session/session-format-v3-to-v4/README.zh.md
index 26809e3693..568ef40547 100644
--- a/packages/session/session-format-v3-to-v4/README.zh.md
+++ b/packages/session/session-format-v3-to-v4/README.zh.md
@@ -243,7 +243,7 @@ System image 接纳要求非空 attachment id、PNG/JPEG/WebP/GIF MIME 类
| `compaction/start`、`compaction/summary`、`compaction/end` | 匹配 compaction id、源 command 和活动 turn 上下文。Summary 区间引用精确的当前表面节点且排除 protected head;成功完成需要一个 summary。继承的未完成 compaction 在 end-seed marker 处过期。 |
| `compaction/prune` | 其区间引用精确的当前表面节点且排除 protected head;它不要求存在 compaction 事务或其所有者字段。 |
| Compact checkpoint 替换 | 其 `compact-checkpoint` 来源标识活动 compaction。 |
-| 原生 `subagent/catalog` | 校验继承截点之后的自身 version-0/version-1 载荷。子 id 保持唯一,例外是一次外部 pending 到终态的更新,其模式、创建时间与标签必须不变。原生读取不收集子日志,也不比较其物理事实;继承条目不建立自身成员关系。 |
+| 原生 `subagent/catalog` | 校验继承截点之后的自身 version-0/version-1 载荷。每个子 id 必须唯一。原生读取不收集子日志,也不比较其物理事实;继承条目不建立自身成员关系。 |
| 继承截点与 delivery | 应用上文的 marker、坐标及代际归属规则。 |
这些检查由 [relationships.ts](src/relationships.ts) 按代际拥有。完整的通用消息/信封接纳与插件拥有的消息投影还使用已安装 Session;单独的导出 V4 恢复器不能替代完整 catalog 恢复。
diff --git a/packages/session/session-format-v3-to-v4/src/validation.ts b/packages/session/session-format-v3-to-v4/src/validation.ts
index 8b1c6e4cde..8c0e74d61d 100644
--- a/packages/session/session-format-v3-to-v4/src/validation.ts
+++ b/packages/session/session-format-v3-to-v4/src/validation.ts
@@ -2,7 +2,7 @@
import { isAbsolute } from 'node:path'
import { SessionFormatError, SessionFormatUnsupportedMigrationError, isSessionFormatJsonObject, sessionFormatCount } from '@deepseek-ai/dsh-session-format'
-import type { SessionFormatArtifact, SessionFormatEvent, SessionFormatJsonObject } from '@deepseek-ai/dsh-session-format'
+import type { SessionFormatArtifact, SessionFormatEvent } from '@deepseek-ai/dsh-session-format'
import { assertV4DeveloperData } from './developer.ts'
import { assertV4LifecycleRelationships } from './relationships.ts'
import { assertV4MessageSources } from './message-sources.ts'
@@ -108,7 +108,7 @@ export function validateDeliveryAccepted(event: SessionFormatEvent, currentVersi
* @param knownEventTypes - installed event types whose payloads this reader interprets.
*/
export function assertReleasedV4Relationships(artifact: SessionFormatArtifact, knownEventTypes: ReadonlySet): void {
- const catalogs = new Map()
+ const ids = new Set()
for (const event of artifact.events) {
if (!knownEventTypes.has(event.type)) continue
assertV4DeveloperData(event)
@@ -122,13 +122,8 @@ export function assertReleasedV4Relationships(artifact: SessionFormatArtifact, k
if (event.type === 'subagent/catalog' && event.seq >= artifact.inheritedEventCount) {
const fact = catalogFact(event.data)
const id = fact['childId'] as string
- const previous = catalogs.get(id)
- if (previous !== undefined && !(previous['mode'] === 'one-shot' && fact['mode'] === 'one-shot'
- && previous['external'] === 'pending' && typeof fact['external'] === 'string' && fact['external'] !== 'pending'
- && previous['childCreatedAt'] === fact['childCreatedAt'] && previous['label'] === fact['label'])) {
- throw new SessionFormatError(`duplicate catalog child ${id}`)
- }
- catalogs.set(id, fact)
+ if (ids.has(id)) throw new SessionFormatError(`duplicate catalog child ${id}`)
+ ids.add(id)
}
}
assertV4LifecycleRelationships(artifact, knownEventTypes)
diff --git a/packages/session/session-format-v3-to-v4/tests/codec.spec.ts b/packages/session/session-format-v3-to-v4/tests/codec.spec.ts
index 559bc5b6ce..dbbf4a06f7 100644
--- a/packages/session/session-format-v3-to-v4/tests/codec.spec.ts
+++ b/packages/session/session-format-v3-to-v4/tests/codec.spec.ts
@@ -162,20 +162,9 @@ describe('V4 framing and restoration', () => {
})
-it('restores one external pending-to-terminal catalog update while retaining local uniqueness', () => {
- const data = { ...(catalog.data as SessionFormatJsonObject), mode: 'one-shot', label: 'External', external: 'pending' }
- const start = { ...catalog, data }
- const end = { ...catalog, seq: 1, data: { ...data, external: 'completed' } }
- expect(restore([start, end]).events).toEqual([start, end])
- for (const changed of [
- { ...data },
- { ...end.data, mode: 'continuable' },
- { ...end.data, childCreatedAt: 999 },
- { ...end.data, label: 'different child' },
- { ...end.data, external: undefined },
- ]) {
- expect(() => restore([start, { ...end, data: changed as SessionFormatJsonObject }])).toThrow('duplicate catalog child')
- }
- expect(() => restore([start, end, { ...end, seq: 2 }])).toThrow('duplicate catalog child')
- expect(() => restore([catalog, end])).toThrow('duplicate catalog child')
+it('restores external membership and rejects duplicate catalog children', () => {
+ const data = { ...(catalog.data as SessionFormatJsonObject), mode: 'one-shot', label: 'External', external: true }
+ const entry = { ...catalog, data }
+ expect(restore([entry]).events).toEqual([entry])
+ expect(() => restore([entry, { ...entry, seq: 1 }])).toThrow('duplicate catalog child')
})
diff --git a/packages/subagent/subagent/README.i18n.yaml b/packages/subagent/subagent/README.i18n.yaml
index 08bbe3cdd1..fa70da8bb8 100644
--- a/packages/subagent/subagent/README.i18n.yaml
+++ b/packages/subagent/subagent/README.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 packages/subagent/subagent/README.md
-README.md: 9424d0e71212aae455e72f1552b80af6504df811
-README.zh.md: e9760d94361a4d7efe1c0467d01c20eb340aa77b
+README.md: 2316002d7607a5eccad1b5acfe59822722b393cc
+README.zh.md: 3568bbb3dd69def11b29b08290ff2c9d88c278f7
diff --git a/packages/subagent/subagent/README.md b/packages/subagent/subagent/README.md
index 9424d0e712..2316002d76 100644
--- a/packages/subagent/subagent/README.md
+++ b/packages/subagent/subagent/README.md
@@ -110,7 +110,7 @@ The service validates requested capabilities before creation. Local providers su
The manager reserves child identity and capacity, materializes a local Agent or external execution, and accepts the initial task. Local `result` becomes ready after Agent activity, pending input, and owned descendants finish, with activity revalidated after the final Session flush. The manager publishes this result while closing admission, before releasing the local handle. External results can become ready before process cleanup finishes. Closing admission and disposing the handle prevent late work from entering a released Agent. Parent delivery emits its notice after settlement; caller delivery leaves collection to the awaiting workflow. Headless hosts repeat `agent.whenIdle()` and `waitForChildren(agent)` until no child work remains, so completion notices can produce the final parent answer.
-Successful local and external creation appends a `subagent/catalog` fact to the parent Session. External entries have no child Session and carry only their last recorded outcome. Terminal updates replace the same catalog row; pending does not imply a live process. Complete results go to the caller or the parent completion notice. Projections exclude inherited facts. Direct lists read one parent projection; descendant lists enumerate Session-backed children through corpus identity. Catalog payload v0 records known modes; v1 also accepts unknown mode. Readers support both versions. Historical migration appends a v1 `subagent/catalog` from a readable child header when its descriptor is unavailable; normal creation retains v0. Its `mode: 'unknown'` projection keeps the child visible without claiming continuation support; an existing complete entry remains authoritative.
+Successful local and external creation appends a `subagent/catalog` fact to the parent Session. External entries use `external: true` to identify children without a local Session. Catalog entries record membership once at creation and carry no execution status. Complete results go to the caller or the parent completion notice. Projections exclude inherited facts. Direct lists read one parent projection; descendant lists enumerate Session-backed children through corpus identity. Catalog payload v0 records known modes; v1 also accepts unknown mode. Readers support both versions. Historical migration appends a v1 `subagent/catalog` from a readable child header when its descriptor is unavailable; normal creation retains v0. Its `mode: 'unknown'` projection keeps the child visible without claiming continuation support; an existing complete entry remains authoritative.
### Ownership and invariants
diff --git a/packages/subagent/subagent/README.zh.md b/packages/subagent/subagent/README.zh.md
index e9760d9436..3568bbb3dd 100644
--- a/packages/subagent/subagent/README.zh.md
+++ b/packages/subagent/subagent/README.zh.md
@@ -110,7 +110,7 @@ kind: "package-reference"
管理器预留子级身份与容量,创建本地 Agent 或外部执行,并接受初始任务。本地 Agent 活动、待处理输入及所拥有的后代均结束后,管理器在最终 Session flush 后重新验证活动状态,使 `result` 就绪。管理器在关闭准入时发布结果,随后释放本地句柄。外部结果可以早于进程清理完成而就绪。关闭准入并释放句柄可防止迟到工作进入已释放的 Agent。父级投递在结算后发送通知;调用方投递由等待中的工作流收集。Headless 宿主交替等待 `agent.whenIdle()` 与 `waitForChildren(agent)`,直至没有子级工作,让完成通知可驱动父级生成最终答案。
-本地与外部创建成功时,都向父 Session 追加 `subagent/catalog` 事实。外部条目没有子 Session,只携带最近记录的结果状态。终态更新替换同一目录条目;pending 不表示进程正在运行。完整结果交给调用方或父级完成通知。投影排除继承事实。直接列表读取一个父级投影;后代列表通过语料中的身份枚举具有 Session 的子级。Catalog 载荷 v0 记录已知模式,v1 还接受未知模式,读取器支持两版。历史迁移在 descriptor 不可用时根据可读子 header 追加 v1 `subagent/catalog`;正常创建保留 v0。其 `mode: 'unknown'` 投影让子会话保持可见,但不表示支持继续执行;已有完整条目仍具有权威性。
+本地与外部创建成功时,都向父 Session 追加 `subagent/catalog` 事实。外部条目以 `external: true` 标记没有本地 Session 的子级。目录仅在创建时记录一次成员关系,不携带执行状态。完整结果交给调用方或父级完成通知。投影排除继承事实。直接列表读取一个父级投影;后代列表通过语料中的身份枚举具有 Session 的子级。Catalog 载荷 v0 记录已知模式,v1 还接受未知模式,读取器支持两版。历史迁移在 descriptor 不可用时根据可读子 header 追加 v1 `subagent/catalog`;正常创建保留 v0。其 `mode: 'unknown'` 投影让子会话保持可见,但不表示支持继续执行;已有完整条目仍具有权威性。
### 所有权与不变式
diff --git a/packages/subagent/subagent/src/catalog.ts b/packages/subagent/subagent/src/catalog.ts
index 1c0d4f9031..9925073e08 100644
--- a/packages/subagent/subagent/src/catalog.ts
+++ b/packages/subagent/subagent/src/catalog.ts
@@ -15,13 +15,13 @@ import type {
SessionLogOffset,
} from '@deepseek-ai/dsh-session'
import type { ProjectionDefinition } from '@deepseek-ai/dsh-session-projection'
-import type { ExternalSubagentStatus, SubagentCatalogEntry } from './projection-types.ts'
+import type { SubagentCatalogEntry } from './projection-types.ts'
/** Catalog payload version emitted by live child creation. */
export const SUBAGENT_CATALOG_VERSION = 0
type KnownCatalogMode =
- | { readonly mode: 'one-shot'; readonly label?: string; readonly external?: ExternalSubagentStatus }
+ | { readonly mode: 'one-shot'; readonly label?: string; readonly external?: true }
| { readonly mode: 'continuable'; readonly label: string }
/** Parent catalog v0 records known modes; v1 also retains children with unknown mode. */
@@ -57,7 +57,7 @@ const oneShotCatalogSchema = z.object({
childCreatedAt: z.number().int().nonnegative(),
mode: z.literal('one-shot'),
label: z.string().optional(),
- external: z.enum(['pending', 'completed', 'max-tokens', 'aborted', 'refusal', 'error']).optional(),
+ external: z.literal(true).optional(),
}).strict()
const continuableCatalogSchema = z.object({
version: z.union([z.literal(0), z.literal(1)]),
@@ -103,12 +103,12 @@ declare module '@deepseek-ai/dsh-session-projection/types' {
* @returns current direct-child rows in parent catalog event order.
*/
function subagentCatalogEntries(state: SubagentCatalogState): SubagentCatalogEntry[] {
- const entries = new Map()
+ const entries: SubagentCatalogEntry[] = []
for (const data of iterateChunkedList(state.head)) {
const { version: _version, childId, childCreatedAt, ...descriptor } = data
- entries.set(childId, { id: childId, createdAt: childCreatedAt, ...descriptor })
+ entries.push({ id: childId, createdAt: childCreatedAt, ...descriptor })
}
- return [...entries.values()]
+ return entries
}
/** Parent-owned direct-child catalog projection; invalid own facts reject restoration. */
@@ -120,7 +120,7 @@ export const subagentCatalogProjectionDefinition = {
if (event.type !== 'subagent/catalog' || event.seq < state.inheritedEventCount) return state
return { ...state, head: appendChunkedList(state.head, eventDataSchema.parse(event.data)) }
},
- stateVersion: 4,
+ stateVersion: 5,
wire: { viewSchema, view: subagentCatalogEntries },
} satisfies ProjectionDefinition<'subagentCatalog', SubagentCatalogState>
diff --git a/packages/subagent/subagent/src/continuation-activation.ts b/packages/subagent/subagent/src/continuation-activation.ts
index d550734e9b..e345e61ff8 100644
--- a/packages/subagent/subagent/src/continuation-activation.ts
+++ b/packages/subagent/subagent/src/continuation-activation.ts
@@ -92,8 +92,6 @@ export interface Activation {
* another runtime-incarnation reference. Non-empty blocks settlement.
*/
readonly ownedChildren: Set
- /** Complete the accepted external catalog fact after resource release. */
- settleCatalog?: (status: SubagentResult['stopReason']) => void
/** The lifecycle observer that emits this epoch's start and terminal edges. */
readonly observer: ActivationObserver
/**
@@ -980,14 +978,6 @@ export class ContinuableActivationRegistry {
activation.releaseSlot()
if (failure !== undefined) result = { output: [], stopReason: 'error' }
activation.result.resolve(result)
- if (activation.settleCatalog !== undefined) {
- try {
- activation.settleCatalog(result.stopReason)
- } catch (error: unknown) {
- failure ??= new SubagentError(`subagent "${childId}" catalog settlement could not be recorded`, 'ACTIVATION_TEARDOWN_FAILED', { cause: error })
- }
- }
- if (failure !== undefined) result = { output: [], stopReason: 'error' }
this.notifySettlement(activation, result, includeOutput)
this.releaseOwnership(childId)
activation.observer.settle(result)
diff --git a/packages/subagent/subagent/src/continuation.ts b/packages/subagent/subagent/src/continuation.ts
index 789a2b0c74..90084d0a20 100644
--- a/packages/subagent/subagent/src/continuation.ts
+++ b/packages/subagent/subagent/src/continuation.ts
@@ -43,7 +43,6 @@ import { SubagentError } from './error.ts'
import { isAdjacentAgentSendMessageTool } from './internal.ts'
import type { ActivationObserver } from './lifecycle.ts'
import type {
- SubagentStopReason,
ContinuableCreateRequest,
ContinuableCreateSpec,
SubagentInterruptAuthority,
@@ -225,14 +224,9 @@ export class SubagentContinuationManager {
this.activations.authorizeLineage(parent, activation.childId, activation.parent.id)
spec.signal.throwIfAborted()
const childId = activation.childId
- const childCreatedAt = Date.now()
- const recordCatalog = (external: 'pending' | SubagentStopReason): void => {
- parent.session.append('subagent/catalog', {
- version: 0, childId, childCreatedAt, mode: 'one-shot', label: spec.label, external,
- })
- }
- recordCatalog('pending')
- activation.settleCatalog = recordCatalog
+ parent.session.append('subagent/catalog', {
+ version: 0, childId, childCreatedAt: Date.now(), mode: 'one-shot', label: spec.label, external: true,
+ })
this.activations.announce(activation)
return this.receipt(activation)
} catch (error: unknown) {
diff --git a/packages/subagent/subagent/src/control-types.ts b/packages/subagent/subagent/src/control-types.ts
index eb772bf6c2..27ed730b7b 100644
--- a/packages/subagent/subagent/src/control-types.ts
+++ b/packages/subagent/subagent/src/control-types.ts
@@ -8,7 +8,6 @@
import type { PromptContentPart } from '@deepseek-ai/dsh-attachment/types'
import type { Branded } from '@deepseek-ai/dsh-brand'
import type { MessageId } from '@deepseek-ai/dsh-llm/brand'
-import type { ExternalSubagentStatus } from './projection-types.ts'
import type { SessionId } from '@deepseek-ai/dsh-session/types'
/**
@@ -34,7 +33,7 @@ export type SubagentCatalogRow =
| {
/** A terminal one-shot child. */
readonly mode: 'one-shot'
- readonly external?: ExternalSubagentStatus
+ readonly external?: true
/** Optional durable creation label from the owning catalog or child descriptor. */
readonly label?: string
}
diff --git a/packages/subagent/subagent/src/index.ts b/packages/subagent/subagent/src/index.ts
index 3ee490a80b..bfb2fb661e 100644
--- a/packages/subagent/subagent/src/index.ts
+++ b/packages/subagent/subagent/src/index.ts
@@ -115,7 +115,7 @@ export type { AgentMessageSource, SubagentSettledMessageSource } from './continu
export type * from './control-types.ts'
export type { SubagentDescendantListEntry } from './list-children.ts'
export type { SubagentRunEndInfo, SubagentRunInfo } from './types.ts'
-export type { ExternalSubagentStatus, SubagentCatalogEntry, SubagentIdentityProjection, SubagentTimingProjection } from './projection-types.ts'
+export type { SubagentCatalogEntry, SubagentIdentityProjection, SubagentTimingProjection } from './projection-types.ts'
declare module '@deepseek-ai/cordis' {
interface Context {
diff --git a/packages/subagent/subagent/src/projection-types.ts b/packages/subagent/subagent/src/projection-types.ts
index 05b93e113a..94d1683c20 100644
--- a/packages/subagent/subagent/src/projection-types.ts
+++ b/packages/subagent/subagent/src/projection-types.ts
@@ -6,9 +6,6 @@
import type { SessionId, SessionSeq } from '@deepseek-ai/dsh-session/types'
-/** Last recorded external execution outcome; pending does not assert process liveness. */
-export type ExternalSubagentStatus = 'pending' | 'completed' | 'max-tokens' | 'aborted' | 'refusal' | 'error'
-
/** One current direct-child discovery row materialized from parent facts. */
export type SubagentCatalogEntry =
& {
@@ -16,7 +13,7 @@ export type SubagentCatalogEntry =
readonly createdAt: number
}
& (
- | { readonly mode: 'one-shot'; readonly label?: string; readonly external?: ExternalSubagentStatus }
+ | { readonly mode: 'one-shot'; readonly label?: string; readonly external?: true }
| { readonly mode: 'continuable'; readonly label: string }
| { readonly mode: 'unknown'; readonly label?: string }
)
diff --git a/packages/subagent/subagent/tests/catalog.spec.ts b/packages/subagent/subagent/tests/catalog.spec.ts
index cc4d4e3908..6eb9a41a59 100644
--- a/packages/subagent/subagent/tests/catalog.spec.ts
+++ b/packages/subagent/subagent/tests/catalog.spec.ts
@@ -157,8 +157,8 @@ describe('subagent catalog projection', () => {
it.each([
{ version: 0, childId: 'child', childCreatedAt: 0, mode: 'unknown' },
- { version: 1, childId: 'child', childCreatedAt: 0, mode: 'unknown', external: 'pending' },
- { version: 1, childId: 'child', childCreatedAt: 0, mode: 'continuable', label: 'child', external: 'pending' },
+ { version: 1, childId: 'child', childCreatedAt: 0, mode: 'unknown', external: true },
+ { version: 1, childId: 'child', childCreatedAt: 0, mode: 'continuable', label: 'child', external: true },
{ version: 9, childId: 'child', childCreatedAt: 0, mode: 'one-shot' },
{ version: 0, childId: 'child', childCreatedAt: 0, mode: 'continuable' },
{ version: 0, childId: 'child', childCreatedAt: -1, mode: 'one-shot' },
@@ -181,22 +181,21 @@ describe('subagent catalog projection', () => {
describe('external catalog membership', () => {
- it.each([0, 1] as const)('updates external leaves alongside unknown children at payload version %s', (version) => {
+ it.each([0, 1] as const)('restores external leaves alongside unknown children at payload version %s', (version) => {
const start = fact(0, 'external', 1, { mode: 'one-shot', label: 'Review' })
- const pending = { ...start, data: { ...start.data, version, mode: 'one-shot' as const, external: 'pending' as const } }
- const end = { ...pending, seq: SessionSeq(2), data: { ...pending.data, external: 'completed' as const } }
+ const entry = { ...start, data: { ...start.data, version, mode: 'one-shot' as const, external: true as const } }
const unknown: SessionEvent<'subagent/catalog'> = {
type: 'subagent/catalog', seq: SessionSeq(3), time: 0,
data: { version: 1, childId: SessionId('unreadable'), childCreatedAt: 3, mode: 'unknown' },
}
- const state = fold([pending, fact(1, 'local', 2, { mode: 'continuable', label: 'Implement' }), end, unknown])
+ const state = fold([entry, fact(1, 'local', 2, { mode: 'continuable', label: 'Implement' }), unknown])
const restored = subagentCatalogProjectionDefinition.stateSchema.parse(JSON.parse(JSON.stringify(state)))
expect(subagentCatalogProjectionDefinition.wire.view(restored)).toEqual([
- { id: SessionId('external'), createdAt: 1, mode: 'one-shot', label: 'Review', external: 'completed' },
+ { id: SessionId('external'), createdAt: 1, mode: 'one-shot', label: 'Review', external: true },
{ id: SessionId('local'), createdAt: 2, mode: 'continuable', label: 'Implement' },
{ id: SessionId('unreadable'), createdAt: 3, mode: 'unknown' },
])
- expect(subagentCatalogProjectionDefinition.wire.view(fold([pending]))[0]).toMatchObject({ external: 'pending' })
+ expect(subagentCatalogProjectionDefinition.wire.view(fold([entry]))[0]).toMatchObject({ external: true })
expect(subagentCatalogProjectionDefinition.wire.viewSchema.safeParse([{ id: 'external', createdAt: 1, mode: 'one-shot', external: 'invalid' }]).success).toBe(false)
})
})
diff --git a/packages/subagent/subagent/tests/external-activation.spec.ts b/packages/subagent/subagent/tests/external-activation.spec.ts
index 09341fedae..c31cfba74d 100644
--- a/packages/subagent/subagent/tests/external-activation.spec.ts
+++ b/packages/subagent/subagent/tests/external-activation.spec.ts
@@ -138,7 +138,7 @@ describe('external subagent activations', () => {
await disposal
await activation.dispose()
expect(backend.dispose).toHaveBeenCalledTimes(1)
- expect(fixture.records()).toMatchObject([{ id: activation.childId, external: 'completed' }])
+ expect(fixture.records()).toMatchObject([{ id: activation.childId, external: true }])
expect(fixture.parent.inbox.nextTurn).toEqual([])
})
@@ -183,7 +183,7 @@ describe('external subagent activations', () => {
backend.result.resolve(failed)
await expect(activation.result).resolves.toEqual(failed)
await activation.dispose()
- expect(fixture.records()).toMatchObject([{ id: activation.childId, external: 'error' }])
+ expect(fixture.records()).toMatchObject([{ id: activation.childId, external: true }])
if (delivery === 'caller') {
expect(fixture.parent.inbox.nextTurn).toEqual([])
} else {
@@ -198,31 +198,6 @@ describe('external subagent activations', () => {
}
})
- it('reports a catalog settlement write failure and releases capacity', async () => {
- const backend = execution()
- backend.cleanup.resolve(undefined)
- const fixture = await setup(async () => backend.run)
- const activation = await fixture.start('parent')
- const append = fixture.parent.session.append.bind(fixture.parent.session)
- const spy = vi.spyOn(fixture.parent.session, 'append').mockImplementation((...args) => {
- if (args[0] === 'subagent/catalog') throw new Error('storage unavailable')
- return Reflect.apply(append, fixture.parent.session, args) as ReturnType
- })
- try {
- backend.result.resolve(completed)
- await expect(activation.result).resolves.toEqual(completed)
- await expect(activation.dispose()).rejects.toThrow('catalog settlement could not be recorded')
- expect(await fixture.ctx.subagents.waitForChildren(fixture.parent)).toBe(false)
- expect(fixture.records()).toMatchObject([{ id: activation.childId }])
- expect(fixture.parent.inbox.nextTurn[0]?.content).toEqual([
- { type: 'text', text: `Background subagent ${activation.childId} failed before it finished.` },
- { type: 'text', text: 'It left no closing message.' },
- ])
- } finally {
- spy.mockRestore()
- }
- })
-
it('reports cleanup failure separately from a completed result without exposing the private cause', async () => {
const backend = execution()
const fixture = await setup(async () => backend.run)
@@ -240,13 +215,13 @@ describe('external subagent activations', () => {
await expect(activation.result).resolves.toEqual(completed)
expect(activation.dispose()).toBe(disposal)
expect(fixture.records()).toMatchObject([{
- id: activation.childId, external: 'error',
+ id: activation.childId, external: true,
}])
})
})
-it('discovers an external leaf through the parent catalog and publishes a small terminal update', async () => {
+it('records external membership at creation without publishing settlement updates', async () => {
const backend = execution()
const fixture = await setup(async () => backend.run)
const snapshots: unknown[] = []
@@ -254,7 +229,7 @@ it('discovers an external leaf through the parent catalog and publishes a small
const unsubscribe = fixture.ctx.sessionProjections.onChanged(() => { snapshots.push(fixture.records()) })
try {
const activation = await fixture.start()
- const expected = { id: activation.childId, mode: 'one-shot', label: 'External work', external: 'pending' }
+ const expected = { id: activation.childId, mode: 'one-shot', label: 'External work', external: true }
expect(await fixture.ctx.subagents.listChildren(fixture.parent.id)).toMatchObject([expected])
expect(await fixture.ctx.subagents.listDescendants(fixture.parent.id)).toEqual([])
expect(fixture.ctx.sessions.get(activation.childId)).toBeUndefined()
@@ -263,8 +238,8 @@ it('discovers an external leaf through the parent catalog and publishes a small
backend.cleanup.resolve(undefined)
await expect(activation.result).resolves.toEqual(result)
await activation.dispose()
- expect(await fixture.ctx.subagents.listChildren(fixture.parent.id)).toMatchObject([{ ...expected, external: 'completed' }])
- expect(snapshots).toHaveLength(2)
+ expect(await fixture.ctx.subagents.listChildren(fixture.parent.id)).toMatchObject([expected])
+ expect(snapshots).toHaveLength(1)
expect(JSON.stringify(snapshots).length).toBeLessThan(1_000)
} finally {
unsubscribe()
diff --git a/packages/subagent/tool-subagent-control/tests/list-agents.spec.ts b/packages/subagent/tool-subagent-control/tests/list-agents.spec.ts
index 7b3a36eea5..fae30701fe 100644
--- a/packages/subagent/tool-subagent-control/tests/list-agents.spec.ts
+++ b/packages/subagent/tool-subagent-control/tests/list-agents.spec.ts
@@ -302,7 +302,7 @@ describe('dsh-tool-subagent-control/list-agents', () => {
await waitNoActivation(ctx, started.childId)
})
- it.each([undefined, 'pending', 'completed'] as const)('omits one-shot intermediates with outcome %s while surfacing their descendants', async (external) => {
+ it.each([undefined, true] as const)('omits one-shot intermediates with external marker %s while surfacing their descendants', async (external) => {
const { ctx, parent } = await setup([])
// Deterministic service rows: a one-shot intermediate owning a continuable
// leaf, plus a positioned diagnostic. The tool filters only the one-shot.
@@ -355,17 +355,14 @@ describe('dsh-tool-subagent-control/list-agents', () => {
expect(listDescendants).toHaveBeenCalledWith(parent.id, signal)
})
- it.each(['pending', 'completed', 'max-tokens', 'aborted', 'refusal', 'error'] as const)(
- 'omits external children with recorded outcome %s',
- async (external) => {
- const { ctx, parent } = await setup([])
- vi.spyOn(ctx.subagents, 'listChildren').mockResolvedValue([
- { id: SessionId('external-task'), createdAt: 1, mode: 'one-shot', external },
- { id: SessionId('resumable-child'), createdAt: 2, mode: 'continuable', label: 'local task' },
- ])
- const result = await callTool(ctx, 'list_agents', {}, parent)
- expect(result.isError).toBe(false)
- expect(text(result)).toBe('resumable-child [inactive] — local task')
- },
- )
+ it('omits external children', async () => {
+ const { ctx, parent } = await setup([])
+ vi.spyOn(ctx.subagents, 'listChildren').mockResolvedValue([
+ { id: SessionId('external-task'), createdAt: 1, mode: 'one-shot', external: true },
+ { id: SessionId('resumable-child'), createdAt: 2, mode: 'continuable', label: 'local task' },
+ ])
+ const result = await callTool(ctx, 'list_agents', {}, parent)
+ expect(result.isError).toBe(false)
+ expect(text(result)).toBe('resumable-child [inactive] — local task')
+ })
})