Forráskód Böngészése

docs(subagent): record the public interrupt in the continuation contract docs

The continuable-subagent core-data page and the continuation lifecycle
Agent Note claimed no public subagent cancellation existed; interrupt()
is now that one public stop. Both pages and their Chinese counterparts
state the shipped contract and link the owning interrupt note.

Refs #1535
Hypatia May 1 hónapja
szülő
commit
57e9e6977c

+ 2 - 2
.agents/notes/implemented/feature/2026-07-28-continuable-subagent-conversations.i18n.yaml

@@ -2,5 +2,5 @@
 # side as of the last confirmed-consistent state. Both languages carry equal authority;
 # after editing either side, bring the other along and re-record with:
 #   pnpm run verify-translation-pairing --write .agents/notes/implemented/feature/2026-07-28-continuable-subagent-conversations.md
-2026-07-28-continuable-subagent-conversations.md: a5a900c3bb30a8d965aabc0bf498f8399ee70e7a
-2026-07-28-continuable-subagent-conversations.zh.md: bde8e874ba27950dc0fab8449c6ba2ee49737d41
+2026-07-28-continuable-subagent-conversations.md: 56d9abb2b09577b2fba14b9a655a941417e0c493
+2026-07-28-continuable-subagent-conversations.zh.md: 77f497ceeef35dcc519242dca30c81759cc3b907

+ 3 - 3
.agents/notes/implemented/feature/2026-07-28-continuable-subagent-conversations.md

@@ -131,7 +131,7 @@ Parent-originated delivery requires the parent to be live when admitted and keep
 
 ### Durability, disposal, and recovery
 
-Without Tasks there is no `task_output`, `task_kill`, Task status, per-message result promise, or public subagent cancellation operation. The caller signal can abort start or follow-up only before inbox acceptance. After acceptance, the parent cannot cancel the message, turn, or Activation through `ctx.subagents`; `Agent.cancel()` remains a lower-level Agent capability that this version does not expose through the subagent service.
+Without Tasks there is no `task_output`, `task_kill`, Task status, or per-message result promise. The caller signal can abort start or follow-up only before inbox acceptance. After acceptance, the parent cannot cancel the accepted message or dispose the Activation through `ctx.subagents`; the only public stop is the later [current-turn interrupt](2026-08-06-continuable-subagent-interrupt.md), which cancels the live target's current turn with `keepInbox` and leaves residency, pending work, and descendants intact.
 
 Host and manager teardown remains the lifecycle stop path. Manager unload applies it globally; a host applies it only below the exact top-level Agents it owns. Each form closes the applicable admission scope, stops the selected visible Activations, awaits admitted materializations in that scope, releases child-first, and preserves the durable Sessions.
 
@@ -145,7 +145,7 @@ Session and descriptor persistence survive restart. Activation state, Agent inbo
 
 This version covers continuable in-process children and leaves one-shot delegation unchanged. Remote providers require a separate Activation handle with equivalent authenticated control and child-first quiescence contracts before they can support the same behavior.
 
-It adds no host-user continuation, subagent steering operation, durable mailbox, cross-process lease, automatic replay of interrupted inbox work, team authority, workflow authority, public subagent cancellation operation, public residency query, new live-Activation or descendant limit, or runtime cache. Existing delegation-depth policy remains unchanged. Optional child-to-parent reporting is a later consumer of this lifecycle rather than part of the base continuable capability.
+It adds no host-user continuation, subagent steering operation, durable mailbox, cross-process lease, automatic replay of interrupted inbox work, team authority, workflow authority, public residency query, new live-Activation or descendant limit, or runtime cache; the later [current-turn interrupt](2026-08-06-continuable-subagent-interrupt.md) added the one public stop operation on top of this lifecycle. Existing delegation-depth policy remains unchanged. Optional child-to-parent reporting is a later consumer of this lifecycle rather than part of the base continuable capability.
 
 ## Alternatives considered
 
@@ -186,7 +186,7 @@ The implementation pins these behaviors:
 - `followup()` accepts only the exact live direct parent and rechecks that identity at the final no-await inbox-admission boundary after any materialization; durable message provenance cannot authorize delivery.
 - Continuation messages always use `Agent.followup()` and share its inbox FIFO, including when the child already has an open turn.
 - `ctx.subagents.followup()` and its `send_message` adapter return only the accepted `MessageId`; the continuation layer accepts no delivery target and defines no subagent-specific route result.
-- This version exposes no public subagent cancellation operation; caller signals stop start and follow-up only before inbox acceptance, while host-scoped and manager-global teardown retain child-first cleanup.
+- Caller signals stop start and follow-up only before inbox acceptance, while host-scoped and manager-global teardown retain child-first cleanup; the [current-turn interrupt](2026-08-06-continuable-subagent-interrupt.md) is the one public stop and does not enter teardown.
 - This version exposes no subagent steering operation or current-turn controller state.
 - An idle Agent with live owned children yields a `waiting` Activation whose `AgentHandle` remains retained.
 - A `next-turn` delivered to `waiting` wakes the same Activation; delivery after completed disposal cold-resumes a new Activation.

+ 3 - 3
.agents/notes/implemented/feature/2026-07-28-continuable-subagent-conversations.zh.md

@@ -131,7 +131,7 @@ activation-owner 作用域之所以存在,是因为普通 Cordis owner effect
 
 ### 持久性、dispose 与恢复
 
-没有 Task 后,系统不再提供 `task_output`、`task_kill`、Task 状态、逐消息结果 promise 或公开 subagent 取消操作。调用方 signal 只能在 inbox 接受消息前中止 start 或 follow-up。消息被接受后,parent 不能通过 `ctx.subagents` 取消该消息、轮次或激活;`Agent.cancel()` 仍是底层 Agent 能力,但本版本不通过 subagent 服务暴露它。
+没有 Task 后,系统不再提供 `task_output`、`task_kill`、Task 状态或逐消息结果 promise。调用方 signal 只能在 inbox 接受消息前中止 start 或 follow-up。消息被接受后,parent 不能通过 `ctx.subagents` 取消已接受的消息或 dispose 激活;唯一的公开停止操作是后来的[当前轮次中断](2026-08-06-continuable-subagent-interrupt.md),它以 `keepInbox` 取消在线目标的当前轮次,驻留、待处理工作与后代均保持不变。
 
 宿主和管理器拆卸仍是生命周期停止路径。管理器卸载会全局应用它;宿主只会在自己确切拥有的顶层 Agent 之下应用它。两种形式都会关闭适用的准入作用域,停止选中的可见 Activation,等待该作用域中已获准的物化过程,按 child-first 顺序释放,并保留持久化 Session。
 
@@ -145,7 +145,7 @@ activation-owner 作用域之所以存在,是因为普通 Cordis owner effect
 
 本版本覆盖可继续的进程内 child,一次性委派保持不变。远程提供方必须具备单独的激活 handle,以及等价的认证控制与 child-first 完全停稳契约,才能支持同样的行为。
 
-它不新增 host-user 继续执行、subagent steering 操作、持久化邮箱、跨进程 lease、中断 inbox 工作的自动回放、团队权限、工作流权限、公开 subagent 取消操作、公开驻留查询、新的在线激活数量或后代总数限制,以及运行时缓存。现有委派深度策略保持不变。可选的 child 到 parent 报告是后续消费该生命周期的功能,不属于基础可继续能力。
+它不新增 host-user 继续执行、subagent steering 操作、持久化邮箱、跨进程 lease、中断 inbox 工作的自动回放、团队权限、工作流权限、公开驻留查询、新的在线激活数量或后代总数限制,以及运行时缓存;后来的[当前轮次中断](2026-08-06-continuable-subagent-interrupt.md)在此生命周期之上补充了唯一的公开停止操作。现有委派深度策略保持不变。可选的 child 到 parent 报告是后续消费该生命周期的功能,不属于基础可继续能力。
 
 ## 曾考虑的替代方案
 
@@ -186,7 +186,7 @@ activation-owner 作用域之所以存在,是因为普通 Cordis owner effect
 - `followup()` 只接受确切的在线直接 parent,并在任何物化之后的最终无 await 的 inbox 准入边界再次检查该身份;持久化消息来源信息不能授权投递。
 - 继续执行消息始终使用 `Agent.followup()` 并共享其 inbox FIFO,包括 child 已有开放轮次的情况。
 - `ctx.subagents.followup()` 及其 `send_message` 适配器只返回已接受的 `MessageId`;继续执行层不接受投递 target,也不定义 subagent 专属路由结果。
-- 本版本不暴露公开 subagent 取消操作;调用方 signal 只能在 inbox 接受消息前停止 start 和 follow-up,限定到宿主的拆卸与管理器全局拆卸则保留 child-first 清理。
+- 调用方 signal 只能在 inbox 接受消息前停止 start 和 follow-up,限定到宿主的拆卸与管理器全局拆卸则保留 child-first 清理;[当前轮次中断](2026-08-06-continuable-subagent-interrupt.md)是唯一的公开停止操作,且不进入拆卸流程。
 - 本版本不暴露 subagent steering 操作或当前轮次控制方状态。
 - 带有在线所持 child 的空闲 Agent 会产生 `waiting` 激活,其 `AgentHandle` 继续保留。
 - 向 `waiting` 投递 `next-turn` 会唤醒同一个激活;完成 dispose 后投递消息会冷恢复新激活。

+ 2 - 2
docs/core-data-structures/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/core-data-structures/subagent.md
-subagent.md: 4d7552eb5749284d90e31e62d8ac02e3d7a21b1d
-subagent.zh.md: ebcb1ce445c164352109d6613028c7a36c10d6d4
+subagent.md: fba934233fb7e890d6a68bd4cc7d37f90ab913ed
+subagent.zh.md: 70651397f82e2cf4c0fe18aa26ff8247484ca0aa

+ 14 - 1
docs/core-data-structures/subagent.md

@@ -139,7 +139,20 @@ The Agent inbox is the only queue. Every continuation message becomes one `Agent
 
 Follow-up authority comes from an exact live Agent tool context. The authenticated Agent must be the durable child's direct parent recorded in `SessionHeader.parentSession`. `MessageSource` and `senderSessionId` are durable provenance after admission and grant no authority; the optional model-facing tool uses `CoordinatorMessageSource`.
 
-For both operations the caller signal owns lookup, materialization, and admission only until inbox acceptance. Afterwards the manager owns the Activation independently: later caller cancellation neither cancels the accepted turn nor disposes the child, and the seam exposes no public subagent cancellation or steering operation.
+For both operations the caller signal owns lookup, materialization, and admission only until inbox acceptance. Afterwards the manager owns the Activation independently: later caller cancellation neither cancels the accepted turn nor disposes the child, and the seam exposes no steering operation.
+
+`SubagentService.interrupt(targetSessionId, authority)` is the one public stop: it authorizes synchronously, issues `Agent.cancel(cause, { keepInbox: true })` on the live target, and returns without awaiting quiescence. The Activation, its pending inbox work, and published descendants are untouched; only a later waking send resumes the parked FIFO queue. An absent target — unknown, one-shot, or already settled — and a manager-less composition are accepted no-ops; a wrong parent address or a stale, self-targeting, or non-ancestor caller rejects with `UNAUTHORIZED`.
+
+```ts type-equiv
+/**
+ * Authority under which one interrupt request is admitted. `user` carries the
+ * durable direct-parent address a human client presented; `ancestor` carries
+ * the exact live Agent object whose recorded lineage must contain the caller.
+ */
+type SubagentInterruptAuthority =
+  | { readonly kind: 'user'; readonly parentSessionId: SessionId }
+  | { readonly kind: 'ancestor'; readonly agent: Agent }
+```
 
 Every Activation owns its `AgentHandle` and an `ownedChildren: Set<SessionId>`; because one Session has at most one live Activation, the child Session id identifies the live child without another runtime-incarnation reference. Starting a child or submitting parent-originated work registers the child in a continuation-managed parent's set before the child can run, and that parent cannot settle while the set is non-empty. A top-level or other non-continuation Agent has no Activation and stays outside the waiting graph. Child release happens only after the child Agent is quiescent, every child of that child is disposed, the best-effort final session flush settles, and the child's `AgentHandle` completes disposal.
 

+ 14 - 1
docs/core-data-structures/subagent.zh.md

@@ -139,7 +139,20 @@ Agent 收件箱是唯一的队列。每条继续执行消息都会成为一个 `
 
 后续操作的权限来自确切的在线 Agent 工具上下文。已认证的 Agent 必须是持久化子 agent 在 `SessionHeader.parentSession` 中记录的直接父级。`MessageSource` 与 `senderSessionId` 在准入之后是持久的来源凭据,不授予任何权限;可选的面向模型工具使用 `CoordinatorMessageSource`。
 
-对于这两种操作,调用方 signal 仅在收件箱接受之前掌管查找、物化与准入。此后管理器独立掌管该 Activation:之后的调用方取消既不会取消已接受的轮次,也不会 dispose 子 agent,并且该 seam 不对外暴露任何 subagent 取消或 steering(中途引导)操作。
+对于这两种操作,调用方 signal 仅在收件箱接受之前掌管查找、物化与准入。此后管理器独立掌管该 Activation:之后的调用方取消既不会取消已接受的轮次,也不会 dispose 子 agent,并且该 seam 不对外暴露任何 steering(中途引导)操作。
+
+`SubagentService.interrupt(targetSessionId, authority)` 是唯一的公开停止操作:它同步完成鉴权,对在线目标发出 `Agent.cancel(cause, { keepInbox: true })`,然后不等待静止即返回。Activation、其待处理的 inbox 工作与已发布的后代均不受影响;只有之后的一次唤醒发送才会恢复被暂停的 FIFO 队列。不存在的目标——未知、一次性或已结算——以及未绑定管理器的组合是被接受的 no-op;错误的 parent 地址,或过期、指向自身、非 ancestor 的调用方会以 `UNAUTHORIZED` 拒绝。
+
+```ts type-equiv
+/**
+ * Authority under which one interrupt request is admitted. `user` carries the
+ * durable direct-parent address a human client presented; `ancestor` carries
+ * the exact live Agent object whose recorded lineage must contain the caller.
+ */
+type SubagentInterruptAuthority =
+  | { readonly kind: 'user'; readonly parentSessionId: SessionId }
+  | { readonly kind: 'ancestor'; readonly agent: Agent }
+```
 
 每个 Activation 都拥有自己的 `AgentHandle` 和一个 `ownedChildren: Set<SessionId>`;由于一份会话至多有一个存活 Activation,子会话 id 无需另一个运行时化身引用即可标识存活的子 agent。启动子 agent 或提交源自 parent 的工作,会在子 agent 能够运行之前将其注册到受继续执行管理的父级集合中;只要该集合非空,该父级就无法 settle。顶层或其他非继续执行的 Agent 没有 Activation,处于 waiting 图之外。只有当子 Agent 已停稳、该子 agent 的每个子级都已 dispose、best-effort 的最终会话 flush 结算完毕,且子 agent 的 `AgentHandle` 完成 dispose 之后,才会释放子 agent。
 

+ 5 - 0
scripts/type-equiv.manifest.json

@@ -1150,6 +1150,11 @@
       "symbol": "SubagentFollowupOptions",
       "source": "packages/subagent/subagent/src/continuation.ts"
     },
+    {
+      "doc": "docs/core-data-structures/subagent.md",
+      "symbol": "SubagentInterruptAuthority",
+      "source": "packages/subagent/subagent/src/continuation.ts"
+    },
     {
       "doc": "docs/core-data-structures/subagent.md",
       "symbol": "ContinuableStart",