test(subagent): execute control tools in ACP replay

This commit is contained in:
Tianyi Cui
2026-08-08 18:30:03 +08:00
parent ce16cf1b3b
commit 07fbc2cda8
6 changed files with 34 additions and 23 deletions

View File

@@ -2,5 +2,5 @@
# side as of the last confirmed-consistent state. Both languages carry equal authority;
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write .agents/notes/implemented/feature/2026-08-06-continuable-subagent-interrupt.md
2026-08-06-continuable-subagent-interrupt.md: 79397ac58d9bc1a117053f05f1a5b7971c48cfbb
2026-08-06-continuable-subagent-interrupt.zh.md: 2b29895c21bf2290b750bdce6979dbe2d76635a7
2026-08-06-continuable-subagent-interrupt.md: cb97a77434b43ee71fa30deee9811f7a45c53f1a
2026-08-06-continuable-subagent-interrupt.zh.md: ee05afd963584331def8c7d6acfa26830b7c1d0f

View File

@@ -45,4 +45,4 @@ The model-facing `interrupt_agent(agent_id)` tool in `dsh-tool-subagent-control`
## Testing
Core coverage in `packages/subagent/subagent/tests/continuation.spec.ts` proves the durable `turn/end` abort, parked-then-FIFO-resumed queue, untouched descendant, both authority kinds with their cancel causes, self/sibling/stale/non-ancestor rejection, absent/one-shot/disposal-race no-ops, and the unchanged `keepInbox` loop behavior. Host coverage in `packages/host/apiproxy/tests` proves the RPC calls only the core primitive (no agents/catalog/history reads), the `subagent-unauthorized`/`internal` mappings, the wire schema's continuable-mode fence, and carrier round-trips. Client coverage pins the address-routed `Session.cancel()`, the InputBar's independent Send and Stop actions with the parent-offline locked-input/Send state, and the read-only-composer selector's running exception; the keyless assembled Web scenarios (`apps/web/tests/subagent-interrupt.e2e.ts`, `subagent-interrupt-ui.e2e.ts`) hold real child turns open with replay hang entries and prove the parent-offline UI-to-RPC abort path, queued Send, the parked follow-up, and the FIFO resume end to end. Tool coverage in `packages/subagent/tool-subagent-control/tests` proves direct and deep ancestor interrupts with the `parent` cause and parked queue, self/sibling/stranger rejection without touching the target, absent-target no-ops without cold resume, and the descendants listing's pre-order positions; the keyless ACP snapshots pin the new tool schemas in every recorded request header.
Core coverage in `packages/subagent/subagent/tests/continuation.spec.ts` proves the durable `turn/end` abort, parked-then-FIFO-resumed queue, untouched descendant, both authority kinds with their cancel causes, self/sibling/stale/non-ancestor rejection, absent/one-shot/disposal-race no-ops, and the unchanged `keepInbox` loop behavior. Host coverage in `packages/host/apiproxy/tests` proves the RPC calls only the core primitive (no agents/catalog/history reads), the `subagent-unauthorized`/`internal` mappings, the wire schema's continuable-mode fence, and carrier round-trips. Client coverage pins the address-routed `Session.cancel()`, the InputBar's independent Send and Stop actions with the parent-offline locked-input/Send state, and the read-only-composer selector's running exception; the keyless assembled Web scenarios (`apps/web/tests/subagent-interrupt.e2e.ts`, `subagent-interrupt-ui.e2e.ts`) hold real child turns open with replay hang entries and prove the parent-offline UI-to-RPC abort path, queued Send, the parked follow-up, and the FIFO resume end to end. Tool coverage in `packages/subagent/tool-subagent-control/tests` proves direct and deep ancestor interrupts with the `parent` cause and parked queue, self/sibling/stranger rejection without touching the target, absent-target no-ops without cold resume, and the descendants listing's pre-order positions; the keyless ACP snapshot executes `list_agents({ scope: 'descendants' })` and `interrupt_agent` through the assembled application against one settled child, while recorded request headers continue to pin both schemas.

View File

@@ -45,4 +45,4 @@ Host RPC `subagent.interrupt` 接收 continuable 的 `SubagentAddress` 并返回
## Testing
`packages/subagent/subagent/tests/continuation.spec.ts` 中的核心覆盖证明了持久化 `turn/end` 中止、队列先暂停后按 FIFO 恢复、后代不受影响、两种授权及其取消 cause、self/sibling/stale/非 ancestor 拒绝、absent/一次性/disposal 竞态 no-op以及 `keepInbox` 循环行为不变。`packages/host/apiproxy/tests` 中的 Host 覆盖证明 RPC 只调用核心原语(不读 agents/目录/历史)、`subagent-unauthorized``internal` 映射、wire schema 的 continuable 模式围栏以及 carrier 往返。客户端覆盖固定按地址路由的 `Session.cancel()`、InputBar 的独立 Send 与 Stop 操作及 parent 离线时锁定输入区和 Send 的状态,以及只读 composer selector 的运行例外keyless 组装 Web 场景(`apps/web/tests/subagent-interrupt.e2e.ts``subagent-interrupt-ui.e2e.ts`)通过多条 replay hang 条目保持多个真实 child 轮次打开,端到端证明 parent 离线时从 UI 到 RPC 的中止路径、Send 入队、follow-up 暂停以及 FIFO 恢复。`packages/subagent/tool-subagent-control/tests` 中的工具覆盖证明直接与更深 ancestor 以 `parent` cause 中断并暂停队列、self/sibling/陌生调用方被拒绝且不触碰目标、目标不存在时 no-op 且不冷恢复,以及 descendants 列表的 pre-order 位置keyless ACP 快照把新工具 schema 固定在每个已录制请求 header
`packages/subagent/subagent/tests/continuation.spec.ts` 中的核心覆盖证明了持久化 `turn/end` 中止、队列先暂停后按 FIFO 恢复、后代不受影响、两种授权及其取消 cause、self/sibling/stale/非 ancestor 拒绝、absent/一次性/disposal 竞态 no-op以及 `keepInbox` 循环行为不变。`packages/host/apiproxy/tests` 中的 Host 覆盖证明 RPC 只调用核心原语(不读 agents/目录/历史)、`subagent-unauthorized``internal` 映射、wire schema 的 continuable 模式围栏以及 carrier 往返。客户端覆盖固定按地址路由的 `Session.cancel()`、InputBar 的独立 Send 与 Stop 操作及 parent 离线时锁定输入区和 Send 的状态,以及只读 composer selector 的运行例外keyless 组装 Web 场景(`apps/web/tests/subagent-interrupt.e2e.ts``subagent-interrupt-ui.e2e.ts`)通过多条 replay hang 条目保持多个真实 child 轮次打开,端到端证明 parent 离线时从 UI 到 RPC 的中止路径、Send 入队、follow-up 暂停以及 FIFO 恢复。`packages/subagent/tool-subagent-control/tests` 中的工具覆盖证明直接与更深 ancestor 以 `parent` cause 中断并暂停队列、self/sibling/陌生调用方被拒绝且不触碰目标、目标不存在时 no-op 且不冷恢复,以及 descendants 列表的 pre-order 位置keyless ACP 快照通过组装应用,针对一个已结算的 child 执行 `list_agents({ scope: 'descendants' })``interrupt_agent`,同时已录制请求 header 仍固定这两个 schema