feat(subagent): catalog one-shot child sessions

This commit is contained in:
Dudu-0223
2026-07-27 16:58:37 +08:00
committed by Tianyi Cui
parent de6e572e51
commit 774ee34b9a
85 changed files with 1034 additions and 539 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-07-21-continuable-background-subagents.md
2026-07-21-continuable-background-subagents.md: b914533e759e46a3f8641ac2c9b49badc2377c5c
2026-07-21-continuable-background-subagents.zh.md: 9f3dace04fe6f13e2136a8f3b96cc2ef4363e82d
2026-07-21-continuable-background-subagents.md: 6bbbfb25fb24eba95de888c9d23c79e24fc8535b
2026-07-21-continuable-background-subagents.zh.md: e23fbe67cc2d16a2c01a1b7ed4884008f8932dd4

View File

@@ -27,7 +27,7 @@ durable child Session
Foreground delegation keeps its one-shot behavior. Continuation covers background in-process spawn and fork children. Each `tool-subagent` instance selects `backgroundMode: 'one-shot' | 'continuable'`; configured continuable mode requires the mounted provider's `resume` capability, while a resumable provider may still use one-shot background policy. ACP children remain one-shot until the deferred ACP continuation work below is complete.
`ctx.subagents` is the only public service. Ordinary `start` stays collection-, Task-, and persistence-agnostic: it validates provider capabilities, dispatches one activation, observes run lifecycle, and returns a holder-owned run. An injected internal continuation manager owns stable child ids, descriptor persistence and lookup, Task-backed activation, and routing through `startContinuable` and `followup`; provider start and resume dispatch use private closures after the manager resolves continuation state. The provider-bound `@deepseek-ai/dsh-tool-subagent` plugin and human-facing adapters call the intent operations for continuable background work; foreground and one-shot background delegation use ordinary `start`. The globally named model tool is a thin optional adapter in `@deepseek-ai/dsh-tool-subagent-control`, and its presence does not decide whether continuable work starts. Parent-to-child enumeration and `list_agents` belong to the [durable subagent catalog](2026-07-22-durable-subagent-catalog-and-list-agents.md).
`ctx.subagents` is the only public service. Ordinary `start` stays collection-, Activation-, and persistence-agnostic: it validates provider capabilities, resolves the one-shot descriptor, dispatches one run, observes run lifecycle, and returns a holder-owned run. An injected internal continuation manager owns stable child ids, continuable descriptor persistence and lookup, Activation lifecycle, and routing through `startContinuable` and `followup`; provider preparation uses a private closure before the manager composes the child itself. The provider-bound `@deepseek-ai/dsh-tool-subagent` plugin and human-facing adapters call the intent operations for continuable background work; foreground and one-shot background delegation use ordinary `start`. The globally named model tool is a thin optional adapter in `@deepseek-ai/dsh-tool-subagent-control`, and its presence does not decide whether continuable work starts. Parent-to-child enumeration, the shared one-shot/continuable descriptor identity, and `list_agents` belong to the [durable subagent catalog](2026-07-22-durable-subagent-catalog-and-list-agents.md).
### Task and cancellation ownership
@@ -73,7 +73,7 @@ Human input uses the same `followup` operation. The UI may display the child tra
The continuation manager snapshots every descriptor input with the seam's `snapshotSubagentDescriptor()` (built on [`snapshotJsonValue`](../../../../packages/core/session/src/json.ts)) before Task creation, matching the detached lossless-JSON boundary already used by Agent messages. A child-scoped setup contribution — a prepended one-shot `agent/prompt-submit` listener installed by the in-process driver — appends one model-hidden `subagent/descriptor` event before downstream prompt admission can block or throw. Allowed admission opens the initial child turn afterward; rejected admission leaves the descriptor as a pre-turn log-only fact, and the activation's final required checkpoint persists it. The event carries no `surfaceOp`, remains outside model history, and survives when compaction replaces surface history. A known child id is resumable only when loading that child session yields a supported descriptor in the child's own suffix (after `seedLength`, so a fork seed cannot leak an ancestor's descriptor) and its header identifies the caller as the direct parent.
The versioned descriptor (`SUBAGENT_DESCRIPTOR_VERSION` in [descriptor.ts](../../../../packages/subagent/subagent/src/descriptor.ts)) contains the subagent provider name, resolved child `agentOptions.provider` and `agentOptions.model`, and optional `persona` and `toolFilter`. It does not snapshot the merge-extensible `AgentOptions` object: unrelated extension values cannot make continuation fail merely because they are not JSON. It deliberately omits `subagentDepth`; cold resume relies on the persisted header's `delegationDepth` rather than reconstructing depth from the descriptor. `outputSchema` belongs to one activation's result contract rather than durable child composition. The child header remains authoritative for the child id, `cwd`, `parentSession`, `seedLength`, and `delegationDepth`, while the persisted child transcript owns the fork seed and subsequent history. [`delegationDepthOf()`](../../../../packages/subagent/subagent/src/index.ts) takes the maximum of header and runtime values, so reconstructed runtime options may deepen the persisted value but never lower it and a resumed child cannot regain a top-level delegation budget.
The continuable arm of the versioned descriptor (`SUBAGENT_DESCRIPTOR_VERSION` in [descriptor.ts](../../../../packages/subagent/subagent/src/descriptor.ts)) carries `mode: 'continuable'`, the subagent provider name, resolved child `agentOptions.provider` and `agentOptions.model`, and optional `persona` and `toolFilter`. It does not snapshot the merge-extensible `AgentOptions` object: unrelated extension values cannot make continuation fail merely because they are not JSON. It deliberately omits `subagentDepth`; cold resume relies on the persisted header's `delegationDepth` rather than reconstructing depth from the descriptor. `outputSchema` belongs to one activation's result contract rather than durable child composition. The child header remains authoritative for the child id, `cwd`, `parentSession`, `seedLength`, and `delegationDepth`, while the persisted child transcript owns the fork seed and subsequent history. [`delegationDepthOf()`](../../../../packages/subagent/subagent/src/index.ts) takes the maximum of header and runtime values, so reconstructed runtime options may deepen the persisted value but never lower it and a resumed child cannot regain a top-level delegation budget.
Cold resume cannot depend on an optional method of `SubagentRun`, because that run has been disposed and is not retained across process restart. A run represents one disposable activation and exposes only activation-scoped operations. `SubagentRun.steer?()` names the confirmed live-only capability so it cannot be confused with service orchestration or the model-facing tool.

View File

@@ -27,7 +27,7 @@ durable child Session
前台委派保持一次性行为。继续执行覆盖后台的进程内 spawn 和 fork child。每个 `tool-subagent` 实例都会选择 `backgroundMode: 'one-shot' | 'continuable'`;配置为可继续模式时,所挂载提供方必须具备 `resume` 功能,而可恢复的提供方仍可采用一次性后台策略。在下述 ACPAgent Client Protocol后续工作完成前ACP child 仍保持一次性行为。
`ctx.subagents` 是唯一的公开服务。普通 `start` 不感知 child 集合、Task 与持久化:它校验提供方功能、分发一次激活、观察 run 生命周期,并返回由持有方负责的 run。注入的内部继续执行管理器负责管理稳定的 child id、描述符持久化与查找、由 Task 支撑的激活,以及通过 `startContinuable``followup` 进行的路由;管理器解析继续执行状态后,提供方的 start 与 resume 分发通过私有闭包进行。按提供方绑定的 `@deepseek-ai/dsh-tool-subagent` 插件及面向用户的适配器调用这些意图操作来处理可继续后台工作;前台和一次性后台委派使用普通 `start`。全局命名的模型工具是 `@deepseek-ai/dsh-tool-subagent-control` 中的可选轻量适配器它是否存在不会决定是否启动可继续工作。parent 到 child 的枚举与 `list_agents` 属于[持久化 subagent 目录](2026-07-22-durable-subagent-catalog-and-list-agents.md)。
`ctx.subagents` 是唯一的公开服务。普通 `start` 不感知 child 集合、Activation 与持久化:它校验提供方功能、解析一次性描述符、分发一个 run、观察 run 生命周期,并返回由持有方负责的 run。注入的内部继续执行管理器负责管理稳定的 child id、可继续描述符持久化与查找、Activation 生命周期,以及通过 `startContinuable``followup` 进行的路由;管理器自行组合 child 之前,提供方通过私有闭包提供准备数据。按提供方绑定的 `@deepseek-ai/dsh-tool-subagent` 插件及面向用户的适配器调用这些意图操作来处理可继续后台工作;前台和一次性后台委派使用普通 `start`。全局命名的模型工具是 `@deepseek-ai/dsh-tool-subagent-control` 中的可选轻量适配器它是否存在不会决定是否启动可继续工作。parent 到 child 的枚举、一次性/可继续模式共享的描述符身份`list_agents` 属于[持久化 subagent 目录](2026-07-22-durable-subagent-catalog-and-list-agents.md)。
### Task 与取消的所有权
@@ -73,7 +73,7 @@ durable child Session
继续执行管理器在创建 Task 前,通过 seam 的 `snapshotSubagentDescriptor()`(基于 [`snapshotJsonValue`](../../../../packages/core/session/src/json.ts) 构建)对每项描述符输入建立快照;这一边界与 Agent 消息现有的分离式无损 JSON 边界一致。作用于 child 作用域的 setup contribution——由进程内驱动前置安装的一次性 `agent/prompt-submit` 监听器——会在下游 prompt admission 能够阻止请求或抛出异常之前追加一个对模型隐藏的 `subagent/descriptor` 事件。admission 获准后才会开启 child 的初始轮次admission 被拒绝时,描述符会作为轮次前的仅日志事实保留,并由该 activation 最终的必需检查点持久化。该事件不携带 `surfaceOp`,不进入模型历史,并在压缩替换 surface 历史时继续保留。只有在加载已知 child id 对应的 child 会话后,能在该 child 自身的后缀中(`seedLength` 之后,因此 fork seed 不会泄露祖先的描述符)得到受支持的描述符,且会话 header 将调用方标识为直接 parent 时,该 id 才可恢复。
版本化描述符([descriptor.ts](../../../../packages/subagent/subagent/src/descriptor.ts) 中的 `SUBAGENT_DESCRIPTOR_VERSION`包含 subagent 提供方名称、已解析的 child `agentOptions.provider``agentOptions.model`,以及可选的 `persona``toolFilter`。它不会对可通过声明合并扩展的 `AgentOptions` 对象建立快照:与此无关的扩展值不会仅因无法表示为 JSON 而导致继续执行失败。描述符会特意省略 `subagentDepth`;从持久化存储恢复时,系统依赖持久化 header 中的 `delegationDepth`,而不根据描述符重建深度。`outputSchema` 属于单次激活的结果契约,不属于持久化 child 组合配置。child header 仍是 child id、`cwd``parentSession``seedLength``delegationDepth` 的权威信息,持久化 child transcript 则负责保存 fork seed 和后续历史。[`delegationDepthOf()`](../../../../packages/subagent/subagent/src/index.ts) 会在 header 值和运行时值中取最大值,因此重建后的运行时选项可以加深持久化值,但绝不能降低它,恢复后的 child 无法重新获得顶层委派预算。
版本化描述符的可继续分支[descriptor.ts](../../../../packages/subagent/subagent/src/descriptor.ts) 中的 `SUBAGENT_DESCRIPTOR_VERSION`携带 `mode: 'continuable'`subagent 提供方名称、已解析的 child `agentOptions.provider``agentOptions.model`,以及可选的 `persona``toolFilter`。它不会对可通过声明合并扩展的 `AgentOptions` 对象建立快照:与此无关的扩展值不会仅因无法表示为 JSON 而导致继续执行失败。描述符会特意省略 `subagentDepth`;从持久化存储恢复时,系统依赖持久化 header 中的 `delegationDepth`,而不根据描述符重建深度。`outputSchema` 属于单次激活的结果契约,不属于持久化 child 组合配置。child header 仍是 child id、`cwd``parentSession``seedLength``delegationDepth` 的权威信息,持久化 child transcript 则负责保存 fork seed 和后续历史。[`delegationDepthOf()`](../../../../packages/subagent/subagent/src/index.ts) 会在 header 值和运行时值中取最大值,因此重建后的运行时选项可以加深持久化值,但绝不能降低它,恢复后的 child 无法重新获得顶层委派预算。
从持久化存储恢复不能依赖 `SubagentRun` 的可选方法,因为该 run 已被 dispose并且进程重启后不会保留。run 表示一次可 dispose 的激活,只暴露作用于当前激活的操作。`SubagentRun.steer?()` 这一名称明确指代提供确认语义且仅适用于在线消息的功能,以免该功能与服务编排或面向模型的工具混淆。

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-07-22-durable-subagent-catalog-and-list-agents.md
2026-07-22-durable-subagent-catalog-and-list-agents.md: b13e2144bc872a54935e9a6c0d6eef5eeb0e23ef
2026-07-22-durable-subagent-catalog-and-list-agents.zh.md: e5d89adb8d5a1312447a16904f5631ffc717d458
2026-07-22-durable-subagent-catalog-and-list-agents.md: 16d81e4fa5cdbf63fde5e8c42fbe50ec18b18718
2026-07-22-durable-subagent-catalog-and-list-agents.zh.md: 5cdce5059b85c17a4ca3b6b22e57a0bc070f3e26

View File

@@ -6,22 +6,22 @@ English | [中文](2026-07-22-durable-subagent-catalog-and-list-agents.zh.md)
## Problem
Continuable background subagents expose a stable child id and persist the reconstruction descriptor in that child's session, so `send_message` can resume a known child without any listing operation. `list_agents` has a different requirement: after parent restart, enumerate only that parent's direct continuable children even when the caller no longer knows their ids. The durable Session and Activation design is owned by [continuable subagents](../../implemented/feature/2026-07-28-continuable-subagent-conversations.md); this note owns enumeration and its model-facing query.
Continuable background subagents expose a stable child id and persist reconstruction data in that child's session, so `send_message` can resume a known child without any listing operation. Discovery has two consumers with different needs: a UI may show both one-shot work and continuable conversations, while the model should receive only children on which `send_message` is meaningful. The durable Session and Activation design is owned by [continuable subagents](../../implemented/feature/2026-07-28-continuable-subagent-conversations.md); this note owns the shared durable inventory and the model-facing projection.
Enumeration must cross-check immutable session lineage, descriptor validity, and the live-preferred session corpus without loading or resuming an Agent merely to display it. It must also define how missing, corrupt, deleted, or unsupported children affect the list and whether repeatedly loading many child logs needs an index.
Enumeration must cross-check immutable session lineage, descriptor validity, and the live-preferred session corpus without loading or resuming an Agent merely to display it. Trace lineage supplies candidates but cannot distinguish an ordinary session fork from a subagent, so the child log needs durable classification. The contract must also define how lifecycle mode, missing or corrupt records, deletion, unsupported versions, and repeated child-log loads affect service and tool consumers.
## Decision
Parent-to-child enumeration and `list_agents` are one separately reviewed feature built on the durable child-session contract. `SubagentService.listChildren(parentSessionId: SessionId)` does the following:
Parent-to-child enumeration is a service capability with consumer-specific projections. `SubagentService.listChildren(parentSessionId: SessionId)` ([subagent/src/index.ts](../../../../packages/subagent/subagent/src/index.ts)) does the following:
- use `ctx.sessionQuery.traceSession(parentSessionId)` to obtain the parent's direct live-preferred child sessions;
- read and validate each candidate's `subagent/descriptor` event without activating the child;
- silently omit candidates without a descriptor, and omit a candidate that becomes unavailable or has a corrupt or unsupported descriptor with a per-child diagnostic;
- expose every child whose supported valid descriptor carries a durable creation `label`, regardless of whether its provider is currently registered;
- report a live child as `running` and a persisted-only child as `complete`;
- expose every local session-backed subagent whose supported valid descriptor carries a durable creation `label` and `mode`, regardless of whether its provider is currently registered;
- report corpus activity separately as `running` or `inactive`, without implying completion or resumability;
- return every resulting child in stable `createdAt` ascending, child-id ascending order.
Descriptor persistence, by-id lookup, direct-parent authorization, and provider-independent cold resume remain owned by the implemented Activation contract. The catalog extends the descriptor with a durable `label` and requires listing to diagnose duplicate descriptor events; it does not weaken the existing facts or invent a second descriptor representation.
Every ordinary local start receives a `one-shot` descriptor, while the continuation manager persists a `continuable` descriptor containing its additional reconstruction fields. The model-facing `list_agents` adapter filters the service result to continuable children and maps `inactive` to its existing `complete` presentation; a UI can consume both modes and decide how to render inactive one-shot history. Descriptor persistence, by-id lookup, direct-parent authorization, and provider-independent cold resume remain owned by the implemented Activation contract. Listing consumes those facts but cannot weaken them or invent a second descriptor representation.
### Enumeration decision
@@ -29,9 +29,9 @@ The first implementation consumes `ctx.sessionQuery.traceSession(parentSessionId
Corpus construction precedes per-child descriptor inspection. A failure while building the initial trace, including persistence listing failure, a live/persisted header conflict anywhere in the observed corpus, or invalid target lineage, fails the whole `list_agents` call because no trustworthy candidate set exists. Only failures after a successful trace are isolated to one candidate; "corrupt child" in that per-child contract therefore means corrupt loaded event surface or descriptor data, not a corpus-level header conflict.
Session lineage is broader than subagent identity: an ordinary `ctx.sessions.fork()` and a one-shot subagent also create direct children. The session header gains no `kind` discriminator; each candidate must instead contain exactly one valid `subagent/descriptor` event. The Activation contract writes it only during initial creation and cold resume appends no further descriptor; a second event is corruption rather than evidence of another Activation. The event is the sole evidence that a traced child is a continuable background subagent; its short creation `label` comes from the delegation's `description`, while its continuation fields remain the reconstruction input for provider-independent cold resume. A candidate without the event is an ordinary fork, one-shot child, or another non-continuable session and is omitted without a diagnostic.
Session lineage is broader than subagent identity: an ordinary `ctx.sessions.fork()` also creates a direct child. The session header gains no `kind` discriminator; each candidate must instead contain exactly one valid `subagent/descriptor` event in its own suffix. `SubagentService.start()` resolves `{ mode: 'one-shot', provider, label }` before ordinary provider dispatch, while the continuation manager snapshots and seeds `{ mode: 'continuable', ...composition }` during initial child creation. The local in-process one-shot driver appends its resolved descriptor only during initial creation, and cold resume appends no further descriptor; a second event is corruption rather than evidence of another Activation. The event is the sole evidence that a traced child is a session-backed subagent. A candidate without it is an ordinary fork, a remote child without a local session record, or another non-subagent session and is omitted without a diagnostic.
The published logical record is also the status source: `SessionRecord.live` means `running`, while `live: false, persisted: true` means `complete`. This status comes directly from the trace and causes no additional child-log load. `complete` means that no Activation is live; it encodes neither successful completion nor a permanently closed child, and `send_message` may materialize another Activation. Conversely, `running` says only that the session is live: a live Agent outside the continuation manager's matching Activation still appears as `running`, but `send_message` rejects rather than adopting it. A child is not visible before its session is published, and no process-local Activation entry is added as a second candidate or status source. Listing is a snapshot that may race publication, disposal, or a later message; `send_message` remains the authoritative delivery-time operation.
The published logical record is also the activity source: `SessionRecord.live` means `running`, while `live: false, persisted: true` means `inactive`. Activity comes directly from the trace and causes no additional child-log load. `inactive` encodes neither successful completion nor resumability: it may describe settled one-shot history or a continuable child for which `send_message` can materialize another Activation. Conversely, `running` says only that the session is live: a live continuable Agent outside the continuation manager's matching Activation still appears as `running`, but `send_message` rejects it as an ownership conflict. A child is not visible before its session is published, and no process-local Activation entry is added as a second candidate or activity source. Listing is a snapshot that may race publication, disposal, or a later message; `send_message` remains the authoritative delivery-time operation.
The subagent service keeps `sessionQuery` optional so start and follow-up remain available without it. Its public `listChildren(parentSessionId: SessionId)` method resolves the optional service and dynamically loads the optional session-query runtime only when called; ordinary subagent imports, start, and follow-up therefore do not evaluate that package. Listing belongs directly to `SubagentService`: it interprets the query's lineage, events, and live state without resolving the Activation-based continuation manager or consulting Agent registrations, Activations, or providers, so a deployment with sessions, `subagents`, and `sessionQuery` can list even when `agents` is absent. The method throws `SubagentError` with stable code `SUBAGENT_CONTROL_SESSION_QUERY_UNAVAILABLE` before loading the runtime or doing query work when the query service is absent. `@deepseek-ai/dsh-tool-subagent-control` exports separately loadable tool plugins: the `send_message` adapter requires only `subagents`, while the `list_agents` adapter requires both `subagents` and `sessionQuery` at load. A deployment may therefore use `send_message` without installing or loading session query; the list tool catches missing service wiring at plugin load, while another direct service consumer receives the same explicit call-time contract.
@@ -45,14 +45,14 @@ If measured scale later requires an index, that index is derived state: session
`SubagentService.listChildren(parentSessionId: SessionId)` returns `Promise<SubagentListEntry[]>`, one array preserving the trace's candidate order rather than separate child and diagnostic arrays. `SubagentListEntry` is a closed union discriminated by its readonly `kind`:
- `kind: 'child'` carries readonly `id: SessionId`, durable `label: string`, and `status: 'running' | 'complete'`;
- `kind: 'child'` carries readonly `id: SessionId`, durable `label: string`, `mode: 'one-shot' | 'continuable'`, and `activity: 'running' | 'inactive'`;
- `kind: 'diagnostic'` carries readonly `id: SessionId` and `reason: 'corrupt' | 'unsupported' | 'unavailable'`.
A valid descriptor produces one child entry, a per-child inspection failure produces one diagnostic entry, and a candidate without a descriptor produces no entry. Child status `running` means that the logical record is live in `ctx.sessions`; `complete` means that it exists only in persistence. These values are neither `AgentStatus` nor the manager's internal Activation state, and the result does not expose the internal `createdAt` sorting key. Exact Activation states and durable outcomes such as successful completion, failure, cancellation, and stop reason require a separate durable activation record and are outside this feature.
A valid descriptor produces one child entry, a per-child inspection failure produces one diagnostic entry, and a candidate without a descriptor produces no entry. `mode` is durable creation policy; `activity` is a process-local corpus snapshot. Activity is neither `AgentStatus`, the manager's internal Activation state, nor a durable outcome, and the result does not expose the internal `createdAt` sorting key. Exact Activation states and durable outcomes such as successful completion, failure, cancellation, and stop reason require a separate durable activation record and are outside this feature.
The model-facing `list_agents` tool takes no arguments, derives `parentSessionId` from the current execution Agent, and is a thin adapter in `@deepseek-ai/dsh-tool-subagent-control`. It renders entries in array order as `<id> [<status>] — <label>` for a child and `<id> [diagnostic: <reason>]` for a diagnostic; an empty array renders `(no subagents)`.
The model-facing `list_agents` tool takes no arguments, derives `parentSessionId` from the current execution Agent, and is a thin adapter in `@deepseek-ai/dsh-tool-subagent-control`. It keeps diagnostics, drops `one-shot` child entries, maps a continuable child's `running` activity to `running` and `inactive` activity to `complete`, then renders `<id> [<status>] — <label>` or `<id> [diagnostic: <reason>]` in the surviving trace order. An empty projection renders `(no subagents)`.
Diagnostics use three fixed reasons. Malformed event surfaces, conflicting headers discovered during an exact child load, a read result whose immutable header differs from the traced candidate or no longer names the requested direct parent, a target that is no longer the located descriptor event, malformed descriptor content, and multiple descriptor events map to `corrupt`. An unknown descriptor version maps to `unsupported`. `SESSION_QUERY_SESSION_NOT_FOUND`, `SESSION_QUERY_EVENT_NOT_FOUND`, and `SESSION_QUERY_PERSISTENCE_FAILED` from a per-child read map to `unavailable`. This phase boundary is intentional: a persistence outage during the initial trace fails the operation, while the same outage beginning during candidate reads may produce one identical `unavailable` diagnostic per affected child; v1 neither coalesces those diagnostics nor promotes them to a global failure. A missing descriptor is instead a non-continuable-child exclusion without a diagnostic. Configuration/window errors and unrecognized failures are not child diagnostics and propagate as operation failures. Each diagnostic identifies the child id and reason without exposing model-hidden descriptor content; the candidate is omitted while healthy siblings remain visible. Sessions outside the trace's direct descendants are never read and produce no diagnostic.
Diagnostics use three fixed reasons. Malformed event surfaces, conflicting headers discovered during an exact child load, a read result whose immutable header differs from the traced candidate or no longer names the requested direct parent, a target that is no longer the located descriptor event, malformed descriptor content, and multiple descriptor events map to `corrupt`. An unknown descriptor version maps to `unsupported`. `SESSION_QUERY_SESSION_NOT_FOUND`, `SESSION_QUERY_EVENT_NOT_FOUND`, and `SESSION_QUERY_PERSISTENCE_FAILED` from a per-child read map to `unavailable`. This phase boundary is intentional: a persistence outage during the initial trace fails the operation, while the same outage beginning during candidate reads may produce one identical `unavailable` diagnostic per affected child; the first version neither coalesces those diagnostics nor promotes them to a global failure. A missing descriptor is instead a non-subagent exclusion without a diagnostic. Configuration/window errors and unrecognized failures are not child diagnostics and propagate as operation failures. Each diagnostic identifies the child id and reason without exposing model-hidden descriptor content; the candidate is omitted while healthy siblings remain visible. Sessions outside the trace's direct descendants are never read and produce no diagnostic.
Diagnostics are transient query results, not session events or catalog state. Deriving a diagnostic performs no additional load beyond the `listEvents()` or conditional `readEvent()` operation whose result produced it.
@@ -64,7 +64,7 @@ The first version has no child deletion operation. If later product behavior del
**Rebuild lineage directly from `SessionPersistence.list()`.** This duplicates session query's live-preferred corpus merge, immutable-header consistency checks, direct-child tracing, and deterministic ordering. Listing uses the existing trusted query service and adds only subagent-specific descriptor validation and rendering.
**List every traced child session.** `parentSession` proves lineage but does not prove that the child is a continuable subagent: ordinary session forks and one-shot subagents share that header field. Listing must also read and validate the descriptor.
**List every traced child session.** `parentSession` proves lineage but does not prove that the child is a subagent: ordinary session forks share that header field. Listing must also read and validate the descriptor.
**Add a `kind` discriminator to `SessionHeader`.** The header would still not carry the reconstruction data required to validate or resume a continuable subagent, so listing would have to read the descriptor anyway. Treating the descriptor as the sole subagent discriminator avoids a second classification source.
@@ -88,8 +88,9 @@ The first version has no child deletion operation. If later product behavior del
## Testing
- `packages/subagent/subagent/tests/list-children.spec.ts` pins a query-only composition with sessions, `subagents`, and `sessionQuery` but no `agents`, then drives the full real stack (agent loop, JSONL persistence, spawn/fork providers, the subagent service, and a concrete session-query service) keylessly: fresh discovery through a real `startContinuable()` child; a persisted (restart-shaped) parent target; `createdAt`-then-id ordering with authored ties; ordinary-fork and fork-seed ancestor-descriptor exclusion without diagnostics; live `running` vs persisted `complete`; duplicate-descriptor, malformed-payload, invalid-surface, mismatched-header, and changed-read-target corruption diagnostics that leave healthy siblings visible; unsupported-version and per-child unavailable diagnostics; provider absence without child omission; compacted/uncompacted twins listing identically; grandchild exclusion; trace-phase failure failing the whole call while candidate-phase failures isolate to one child; configuration/window and unrecognized failures propagating as operation failures; forwarded trace/exact-read cancellation with stable `CANCELLED` normalization; and the `SUBAGENT_CONTROL_SESSION_QUERY_UNAVAILABLE` no-service contract. `packages/subagent/subagent/tests/optional-session-query.spec.ts` rejects eager evaluation of the optional runtime while importing the ordinary subagent surface.
- `packages/subagent/tool-subagent-control/tests/list-agents.spec.ts` pins the `list_agents` schema (no parameters), the fixed child/diagnostic/empty text forms, an end-to-end settled-child listing with its durable label, the no-agent rejection, load-time `sessionQuery` injection, and HMR disposal.
- `packages/subagent/subagent/tests/service.spec.ts` pins descriptor v2 parsing for both modes and proves raw start resolves a one-shot descriptor before provider dispatch. `packages/subagent/subagent-inprocess/tests/subagent-inprocess.spec.ts` proves the local driver appends that descriptor inside the initial turn. Workflow and delegation-tool tests pin the display label across their respective start boundaries.
- `packages/subagent/subagent/tests/list-children.spec.ts` pins a query-only composition with sessions, `subagents`, and `sessionQuery` but no `agents`, then drives the full real stack (agent loop, JSONL persistence, spawn/fork providers, the subagent service, and a concrete session-query service) keylessly: one-shot and continuable children from one real trace; a persisted (restart-shaped) parent target; `createdAt`-then-id ordering with authored ties; ordinary-fork and fork-seed ancestor-descriptor exclusion without diagnostics; live `running` vs persisted `inactive`; duplicate-descriptor, malformed-payload, invalid-surface, mismatched-header, and changed-read-target corruption diagnostics that leave healthy siblings visible; unsupported-version and per-child unavailable diagnostics; provider absence without child omission; compacted/uncompacted twins listing identically; grandchild exclusion; trace-phase failure failing the whole call while candidate-phase failures isolate to one child; configuration/window and unrecognized failures propagating as operation failures; forwarded trace/exact-read cancellation with stable `CANCELLED` normalization; and the `SUBAGENT_CONTROL_SESSION_QUERY_UNAVAILABLE` no-service contract. `packages/subagent/subagent/tests/optional-session-query.spec.ts` rejects eager evaluation of the optional runtime while importing the ordinary subagent surface.
- `packages/subagent/tool-subagent-control/tests/list-agents.spec.ts` pins the `list_agents` schema (no parameters), the continuable-only projection that omits a healthy one-shot sibling while preserving diagnostics, the fixed child/diagnostic/empty text forms, an end-to-end settled-child listing with its durable label, the no-agent rejection, load-time `sessionQuery` injection, and HMR disposal.
- The keyless ACP snapshot scenario `subagent-list-agents` (examples/acp-agent) pins the model-visible transcript: a background delegation settles, and `list_agents` executes for real against the subagent service, session query, and JSONL persistence, rendering `<id> [complete] — <label>`.
## Consequences
@@ -97,7 +98,9 @@ The first version has no child deletion operation. If later product behavior del
- Session tracing observes the complete logical corpus, then descriptor validation reads every direct-child log once and candidates with exactly one descriptor twice. In the persisted-only worst case, work is O(D × C + Σ L_i), not merely O(D), because each exact read rescans persistence and loads and clones the full candidate log. A later derived index must preserve the same authorization, per-child diagnostic, and fallback behavior.
- Corpus construction is an all-or-nothing trust boundary: one live/persisted header conflict fails the initial trace and hides otherwise healthy siblings. Per-child isolation begins only after that trace succeeds.
- A torn child tail is surfaced, not repaired: the non-mutating `inspect()` read returns the valid stored prefix, so a child interrupted mid-write may list from a shorter log until the resume path's repairing load closes it.
- The first version has no deletion operation, so persisted children remain listed for as long as their sessions remain in persistence even though live Agent resources remain bounded by resident Activations.
- The query returns every direct continuable child and diagnostic without a service cursor or candidate cap. Stable ordering makes the result deterministic but does not bound model-context growth; service pagination or deletion remains a later product decision.
- `running` and `complete` are process-local corpus snapshots, not delivery promises. Another process may activate a persisted child while this process reports it as `complete`; cross-process accuracy requires a shared lease.
- Making the durable `label` a required descriptor field is a pre-release format change: the descriptor version stays 1, and logs written by pre-label builds fail descriptor validation as `corrupt` (consistent with the repo's no-compatibility stance before the first tagged release).
- There is no deletion operation, so persisted children remain listed for as long as their sessions remain in persistence even though live Agent resources remain bounded by resident Activations.
- The service returns every direct session-backed subagent and diagnostic without a cursor or candidate cap. Stable ordering makes the result deterministic; the model projection avoids one-shot context growth but remains unbounded in the number of continuable children.
- `running` and `inactive` are process-local corpus snapshots, not outcomes or delivery promises. Another process may activate a persisted child while this process reports it as `inactive`; cross-process accuracy requires a shared lease.
- Persisting lifecycle mode is a pre-release descriptor-format change: version 2 rejects old version-1 descriptors as unsupported. Every local session-backed start now pays one small log event so UI and other service consumers can classify one-shot history without replaying model-visible tool results.
- One-shot session persistence remains best-effort. A one-shot child is visible while live and remains discoverable after disposal only when its session checkpoint reached persistence; unlike continuable activation, catalog participation does not add a required final flush or turn a persistence failure into a run failure.
- Remote ACP one-shot runs remain outside the catalog because they publish no local child session for `traceSession()` to discover. Enumerating them would require a separate durable local record rather than pretending remote lifecycle ids are session ids.

View File

@@ -6,22 +6,22 @@ Status: implemented
## 问题
可继续的后台 subagent 会公开稳定的 child id并将重建描述符持久化在该 child 的会话中,因此 `send_message` 无需任何列表查询操作即可恢复已知 child。`list_agents` 的要求不同parent 重启后,即使调用方不再知道各 child id也要只枚举该 parent 的直接可继续 child。[可继续 subagent](../../implemented/feature/2026-07-28-continuable-subagent-conversations.md)负责持久化 Session 与 Activation 设计;本记录负责枚举及其面向模型的查询
可继续的后台 subagent 会公开稳定的 child id并将重建数据持久化在该 child 的会话中,因此 `send_message` 无需任何列表查询操作即可恢复已知 child。发现功能有两类需求不同的消费方UI 可以同时展示一次性工作和可继续对话,而模型只应收到适合使用 `send_message` child。[可继续 subagent](../../implemented/feature/2026-07-28-continuable-subagent-conversations.md)负责持久化 Session 与 Activation 设计;本记录负责共享的持久化清单及面向模型的投影
枚举必须交叉核对不可变的会话谱系、描述符有效性与实时优先的会话语料,而不能仅为展示就加载或恢复 Agent。它还必须定义缺失损坏、删除不受支持的 child 如何影响列表,以及反复加载大量 child 日志是否需要索引
枚举必须交叉核对不可变的会话谱系、描述符有效性与实时优先的会话语料,而不能仅为展示就加载或恢复 Agent。追踪谱系可以提供候选项,却无法区分普通会话 fork 与 subagent因此 child 日志需要持久化分类。契约还必须定义生命周期模式、缺失损坏的记录、删除不受支持的版本,以及反复加载大量 child 日志会如何影响服务与工具消费方
## 决策
parent 到 child 的枚举`list_agents` 是一个基于持久化 child Session 契约、单独评审的功能。`SubagentService.listChildren(parentSessionId: SessionId)` 执行以下操作:
parent 到 child 的枚举是一项带消费方专用投影的服务功能。`SubagentService.listChildren(parentSessionId: SessionId)`[subagent/src/index.ts](../../../../packages/subagent/subagent/src/index.ts)执行以下操作:
- 使用 `ctx.sessionQuery.traceSession(parentSessionId)` 获取 parent 的直接且实时优先的 child 会话;
- 读取并校验每个候选会话的 `subagent/descriptor` 事件,但不激活 child
- 默默排除不含描述符的候选;如果候选变得不可用,或其描述符损坏或版本不受支持,则排除该候选并产生对应 child 的 diagnostic
- 公开每个拥有受支持且有效描述符的 child;该描述符必须带有持久化的创建 `label`,而其提供方当前是否已注册不影响公开;
-存活 child 报告为 `running`,只存在于持久化存储中的 child 报告为 `complete`
- 公开每个由本地会话支撑、拥有受支持且有效描述符的 subagent;该描述符必须带有持久化的创建 `label``mode`,而其提供方当前是否已注册不影响公开;
-语料活动状态单独报告为 `running``inactive`,但不暗示已完成或可恢复
-`createdAt` 升序、再按 child id 升序稳定返回所有结果 child。
描述符持久化、按 id 查找、直接 parent 鉴权和不依赖提供方的冷恢复仍归已实现的 Activation 契约负责。该目录会为描述符增加持久化 `label`,并要求列表查询诊断重复的描述符事件;它不会削弱现有事实,也不会发明第二种描述符表示。
每次普通的本地启动都会收到 `one-shot` 描述符,而继续执行管理器会持久化包含附加重建字段的 `continuable` 描述符。面向模型的 `list_agents` 适配器会将服务结果过滤为可继续 child并将 `inactive` 映射为其现有的 `complete` 表示UI 可以消费两种模式,并自行决定如何渲染非活跃的一次性历史。描述符持久化、按 id 查找、直接 parent 鉴权和不依赖提供方的冷恢复仍归已实现的 Activation 契约负责。列表查询消费这些事实,但不能削弱它们,也不能另行发明第二种描述符表示。
### 枚举决策
@@ -29,9 +29,9 @@ parent 到 child 的枚举及 `list_agents` 是一个基于持久化 child Sessi
语料构建先于逐 child 描述符检查。构建初始追踪时如果发生持久化列表查询失败、所观测语料中任意位置的存活/持久化 header 冲突或目标谱系无效,整个 `list_agents` 调用都会失败,因为此时不存在可信的候选集。只有初始追踪成功后的失败才会被隔离到单个候选;因此,这项逐 child 契约中的“损坏 child”是指已加载的事件 surface 或描述符数据损坏,而不是语料级 header 冲突。
会话谱系涵盖的范围比 subagent 身份更广:普通 `ctx.sessions.fork()` 和一次性 subagent 也会创建直接 child。会话 header 不新增 `kind` 判别字段;每个候选必须改为恰好包含一个有效的 `subagent/descriptor` 事件。Activation 契约只在初始创建期间写入该事件,从持久化存储恢复时不会追加其他描述符;第二个事件属于损坏,而不是另一次 Activation 的证据。该事件是已追踪 child 属于可继续后台 subagent 的唯一证据;其简短创建 `label` 来自委派的 `description`,其余继续执行字段仍是不依赖提供方的冷恢复所使用的重建输入。缺少该事件的候选属于普通 fork、一次性 child 或其他不可继续的会话,系统会将其排除且不产生 diagnostic。
会话谱系涵盖的范围比 subagent 身份更广:普通 `ctx.sessions.fork()` 也会创建直接 child。会话 header 不新增 `kind` 判别字段;每个候选必须改为在自身后缀中恰好包含一个有效的 `subagent/descriptor` 事件。`SubagentService.start()` 会在普通提供方分发前解析 `{ mode: 'one-shot', provider, label }`,而继续执行管理器会在初始创建 child 时为 `{ mode: 'continuable', ...composition }` 建立快照并将其作为 seed。本地进程内的一次性驱动只在初始创建期间追加已解析的描述符,从持久化存储恢复时不会追加其他描述符;第二个事件属于损坏,而不是另一次 Activation 的证据。该事件是已追踪 child 属于由会话支撑的 subagent 的唯一证据。缺少该事件的候选属于普通 fork、没有本地会话记录的远端 child 或其他非 subagent 会话,系统会将其排除且不产生 diagnostic。
已发布的逻辑记录同时也是状态来源:`SessionRecord.live` 表示 `running`,而 `live: false, persisted: true` 表示 `complete`状态直接来自追踪结果,不会导致额外加载 child 日志。`complete` 表示当前没有存活的 Activation既不表示执行成功也不表示 child 已永久关闭;`send_message` 可物化另一次 Activation。反过来`running` 只表示会话存活:位于继续执行管理器对应 Activation 之外的存活 Agent 仍会显示为 `running`,但 `send_message` 会拒绝,而不会接管它。child 会话发布前不可见,也不会添加进程内 Activation 条目作为第二个候选来源或状态来源。列表查询是一份快照可能与发布、dispose 或后续消息发生竞态;`send_message` 仍是消息送达时的权威操作。
已发布的逻辑记录同时也是活动状态来源:`SessionRecord.live` 表示 `running`,而 `live: false, persisted: true` 表示 `inactive`活动状态直接来自追踪结果,不会导致额外加载 child 日志。`inactive` 既不表示执行成功,也不表示可恢复:它可能表示已结算的一次性历史,也可能表示 `send_message`以为其物化另一次 Activation 的可继续 child。反过来,`running` 只表示会话存活:位于继续执行管理器对应 Activation 之外的存活可继续 Agent 仍会显示为 `running`,但 `send_message`将其作为所有权冲突拒绝。child 会话发布前不可见,也不会添加进程内 Activation 条目作为第二个候选来源或活动状态来源。列表查询是一份快照可能与发布、dispose 或后续消息发生竞态;`send_message` 仍是消息送达时的权威操作。
subagent 服务将 `sessionQuery` 保持为可选依赖,因此没有该服务时仍可执行 start 和 follow-up。其公开的 `listChildren(parentSessionId: SessionId)` 方法只在被调用时才会解析这个可选服务,并动态加载可选的会话查询运行时;因此,普通 subagent 导入、start 和 follow-up 都不会触发该包求值。列表查询直接由 `SubagentService` 负责:它解释查询返回的谱系、事件和存活状态,无需解析基于 Activation 的继续执行管理器,也不会查询 Agent 注册信息、Activation 或提供方;因此,仅包含会话、`subagents``sessionQuery` 的部署即使缺少 `agents` 也能执行列表查询。如果查询服务缺失,该方法会在加载运行时或执行查询工作前抛出 `SubagentError`,并携带稳定错误码 `SUBAGENT_CONTROL_SESSION_QUERY_UNAVAILABLE``@deepseek-ai/dsh-tool-subagent-control` 导出可分别加载的工具插件:`send_message` 适配器只要求 `subagents`,而 `list_agents` 适配器在加载时同时要求 `subagents``sessionQuery`。因此,部署可以在既不安装也不加载会话查询的情况下使用 `send_message`;列表工具会在插件加载时捕获服务未接入的问题,而其他直接服务消费方会收到同一项明确的调用时契约。
@@ -45,14 +45,14 @@ subagent 服务将 `sessionQuery` 保持为可选依赖,因此没有该服务
`SubagentService.listChildren(parentSessionId: SessionId)` 返回 `Promise<SubagentListEntry[]>`,其中的单个数组不会将 child 与 diagnostic 分开,而是保留追踪结果中的候选顺序。`SubagentListEntry` 是一个由只读 `kind` 判别的封闭联合类型:
- `kind: 'child'` 携带只读的 `id: SessionId`、持久化 `label: string``status: 'running' | 'complete'`
- `kind: 'child'` 携带只读的 `id: SessionId`、持久化 `label: string``mode: 'one-shot' | 'continuable'``activity: 'running' | 'inactive'`
- `kind: 'diagnostic'` 携带只读的 `id: SessionId``reason: 'corrupt' | 'unsupported' | 'unavailable'`
有效描述符产生一个 child 条目,逐 child 检查失败产生一个 diagnostic 条目,缺少描述符的候选不产生条目。child 状态 `running` 表示逻辑记录在 `ctx.sessions` 中存活;`complete` 表示该记录只存在于持久化存储中。这些值既不是 `AgentStatus`,也不是管理器内部的 Activation 状态,结果不公开内部 `createdAt` 排序键。成功完成、失败、取消和停止原因等精确 Activation 状态与持久化结果需要单独的持久化激活记录,不在本功能范围内。
有效描述符产生一个 child 条目,逐 child 检查失败产生一个 diagnostic 条目,缺少描述符的候选不产生条目。`mode` 是持久化创建策略;`activity` 是进程本地语料快照。活动状态既不是 `AgentStatus`管理器内部的 Activation 状态,也不是持久化结果,结果不公开内部 `createdAt` 排序键。成功完成、失败、取消和停止原因等精确 Activation 状态与持久化结果需要单独的持久化激活记录,不在本功能范围内。
面向模型的 `list_agents` 工具不接受参数,从当前正在执行的 Agent 推导 `parentSessionId`,并作为 `@deepseek-ai/dsh-tool-subagent-control` 中的轻量适配器。它按数组顺序将 child 渲染 `<id> [<status>] — <label>`,将 diagnostic 渲染为 `<id> [diagnostic: <reason>]`;空数组渲染为 `(no subagents)`
面向模型的 `list_agents` 工具不接受参数,从当前正在执行的 Agent 推导 `parentSessionId`,并作为 `@deepseek-ai/dsh-tool-subagent-control` 中的轻量适配器。它保留 diagnostic丢弃 `one-shot` child 条目,将可继续 child 的 `running` 活动状态映射为 `running``inactive` 活动状态映射为 `complete`,然后按剩余的追踪顺序渲染 `<id> [<status>] — <label>` `<id> [diagnostic: <reason>]`。空投影渲染为 `(no subagents)`
diagnostic 使用三种固定原因。格式错误的事件 surface、精确加载 child 时发现的 header 冲突、读取结果中的不可变 header 与追踪到的候选不一致或不再指向请求的直接 parent、读取目标不再是先前定位的描述符事件、格式错误的描述符内容和多个描述符事件映射为 `corrupt`。未知描述符版本映射为 `unsupported`。逐 child 读取产生的 `SESSION_QUERY_SESSION_NOT_FOUND``SESSION_QUERY_EVENT_NOT_FOUND``SESSION_QUERY_PERSISTENCE_FAILED` 映射为 `unavailable`。这项阶段边界是有意为之:初始追踪期间发生持久化故障会让操作失败,而同一故障如果始于候选读取期间,可能会让每个受影响的 child 分别产生一条相同的 `unavailable` diagnostic第一版既不合并这些 diagnostic也不会把它们提升为全局失败。缺少描述符则作为不可继续 child 排除,且不产生 diagnostic。配置错误、窗口错误和未识别的失败不属于 child diagnostic会作为操作失败继续向上传播。每条 diagnostic 都标识 child id 及原因,不暴露对模型隐藏的描述符内容;系统会排除该候选,而其他健康的 sibling 仍然可见。系统绝不会读取不属于追踪结果直接后代的会话,也不会为它们产生 diagnostic。
diagnostic 使用三种固定原因。格式错误的事件 surface、精确加载 child 时发现的 header 冲突、读取结果中的不可变 header 与追踪到的候选不一致或不再指向请求的直接 parent、读取目标不再是先前定位的描述符事件、格式错误的描述符内容和多个描述符事件映射为 `corrupt`。未知描述符版本映射为 `unsupported`。逐 child 读取产生的 `SESSION_QUERY_SESSION_NOT_FOUND``SESSION_QUERY_EVENT_NOT_FOUND``SESSION_QUERY_PERSISTENCE_FAILED` 映射为 `unavailable`。这项阶段边界是有意为之:初始追踪期间发生持久化故障会让操作失败,而同一故障如果始于候选读取期间,可能会让每个受影响的 child 分别产生一条相同的 `unavailable` diagnostic第一版既不合并这些 diagnostic也不会把它们提升为全局失败。缺少描述符则作为非 subagent 排除,且不产生 diagnostic。配置错误、窗口错误和未识别的失败不属于 child diagnostic会作为操作失败继续向上传播。每条 diagnostic 都标识 child id 及原因,不暴露对模型隐藏的描述符内容;系统会排除该候选,而其他健康的 sibling 仍然可见。系统绝不会读取不属于追踪结果直接后代的会话,也不会为它们产生 diagnostic。
diagnostic 是瞬时查询结果,不属于会话事件或目录状态。推导 diagnostic 时,除了产生该结果的 `listEvents()` 或条件性 `readEvent()` 操作外,不会执行额外加载。
@@ -64,7 +64,7 @@ diagnostic 是瞬时查询结果,不属于会话事件或目录状态。推导
**直接通过 `SessionPersistence.list()` 重建谱系。** 这种做法会重复实现会话查询中的实时优先语料合并、不可变 header 一致性检查、直接 child 追踪和确定性排序。列表查询应使用现有可信查询服务,只增加 subagent 特有的描述符校验与渲染。
**列出每个已追踪的 child 会话。** `parentSession` 能证明谱系,却不能证明 child 是可继续的 subagent普通会话 fork 和一次性 subagent 也使用这个 header 字段。列表查询还必须读取并校验描述符。
**列出每个已追踪的 child 会话。** `parentSession` 能证明谱系,却不能证明 child 是 subagent普通会话 fork 也使用这个 header 字段。列表查询还必须读取并校验描述符。
**为 `SessionHeader` 添加 `kind` 判别字段。** header 仍不会携带校验或恢复可继续 subagent 所需的重建数据,因此列表查询无论如何都必须读取描述符。将描述符作为唯一的 subagent 判别信息,可避免引入第二个分类来源。
@@ -88,8 +88,9 @@ diagnostic 是瞬时查询结果,不属于会话事件或目录状态。推导
## 测试
- `packages/subagent/subagent/tests/list-children.spec.ts` 固定一个只有会话、`subagents``sessionQuery` 而没有 `agents` 的纯查询组合再以无密钥方式驱动完整真实栈agent loop、JSONL 持久化、spawn/fork 提供方、subagent 服务,以及一个具体的会话查询服务):通过真实 `startContinuable()` child 的全新发现;只存在于持久化存储中(重启形态)的 parent 目标;带有人工构造并列项的按 `createdAt` 再按 id 排序;排除普通 fork 和 fork seed 中祖先描述符且不产生 diagnostic存活 `running` 与持久化 `complete` 的对比;重复描述符、载荷格式错误、无效 surface、header 不匹配和读取目标已变化的损坏 diagnostic 均不隐藏健康的 sibling不受支持版本与逐 child unavailable diagnostic提供方缺失时不排除 child压缩与未压缩的孪生 child 列表结果一致;排除孙代会话;追踪阶段失败导致整次调用失败而候选阶段失败只隔离到单个 child配置窗口错误和无法识别的失败作为操作失败向上传播转发 trace精确读取取消并稳定归一化为 `CANCELLED`;以及 `SUBAGENT_CONTROL_SESSION_QUERY_UNAVAILABLE` 缺服务契约。`packages/subagent/subagent/tests/optional-session-query.spec.ts` 会在导入普通 subagent surface 时拒绝对可选运行时的 eager 求值
- `packages/subagent/tool-subagent-control/tests/list-agents.spec.ts` 固定 `list_agents` 的 schema无参数、childdiagnostic空结果的固定文本形式、带持久化 label 的已结束 child 端到端列表、无调用 agent 时的拒绝、加载时的 `sessionQuery` 注入,以及 HMR dispose
- `packages/subagent/subagent/tests/service.spec.ts` 固定两种模式下的描述符 v2 解析,并证明底层启动会在分发给提供方之前解析出一次性描述符。`packages/subagent/subagent-inprocess/tests/subagent-inprocess.spec.ts` 证明本地驱动会在初始轮次内追加该描述符。workflow 与委派工具测试固定显示标签在各自启动边界上的传递
- `packages/subagent/subagent/tests/list-children.spec.ts` 固定一个只有会话、`subagents``sessionQuery` 而没有 `agents` 的纯查询组合再以无密钥方式驱动完整真实栈agent loop、JSONL 持久化、spawn/fork 提供方、subagent 服务,以及一个具体的会话查询服务):来自同一真实追踪的一次性与可继续 child只存在于持久化存储中重启形态的 parent 目标;带有人工构造并列项的按 `createdAt` 再按 id 排序;排除普通 fork 和 fork seed 中祖先描述符且不产生 diagnostic存活 `running` 与持久化 `inactive` 的对比;重复描述符、载荷格式错误、无效 surface、header 不匹配和读取目标已变化的损坏 diagnostic 均不隐藏健康的 sibling不受支持版本与逐 child unavailable diagnostic提供方缺失时不排除 child压缩与未压缩的孪生 child 列表结果一致;排除孙代会话;追踪阶段失败导致整次调用失败而候选阶段失败只隔离到单个 child配置窗口错误和无法识别的失败作为操作失败向上传播转发 trace精确读取取消并稳定归一化为 `CANCELLED`;以及 `SUBAGENT_CONTROL_SESSION_QUERY_UNAVAILABLE` 缺服务契约。`packages/subagent/subagent/tests/optional-session-query.spec.ts` 会在导入普通 subagent surface 时拒绝对可选运行时的 eager 求值
- `packages/subagent/tool-subagent-control/tests/list-agents.spec.ts` 固定 `list_agents` 的 schema无参数、只保留可继续 child 且排除健康的一次性 sibling、同时保留 diagnostic 的投影、childdiagnostic空结果的固定文本形式、带持久化 label 的已结束 child 端到端列表、无调用 agent 时的拒绝、加载时的 `sessionQuery` 注入,以及 HMR dispose。
- 无密钥 ACP 快照场景 `subagent-list-agents`examples/acp-agent固定模型可见的转写一次后台委派结束后`list_agents` 针对 subagent 服务、会话查询和 JSONL 持久化真实执行,渲染 `<id> [complete] — <label>`
## 影响
@@ -97,7 +98,9 @@ diagnostic 是瞬时查询结果,不属于会话事件或目录状态。推导
- 会话追踪会观察完整的逻辑语料,随后描述符校验会读取每个直接 child 的日志一次,并对恰好含有一个描述符的候选读取两次。对于只存在于持久化存储中的最坏情况,工作量为 O(D × C + Σ L_i),而不只是 O(D),因为每次精确读取都会重新扫描持久化存储,并加载和克隆候选的完整日志。后续的派生索引必须保持相同的鉴权、逐 child diagnostic 和回退行为。
- 语料构建是一个全有或全无的信任边界:一处存活/持久化 header 冲突就会导致初始追踪失败,并隐藏原本健康的 sibling。只有初始追踪成功后逐 child 隔离才会生效。
- 撕裂的 child 尾部会被呈现而非修复:非变更的 `inspect()` 读取返回有效的已存储前缀,因此写入中途被打断的 child 在恢复路径的修复加载将其关闭之前,可能以较短的日志形式出现在列表中。
- 第一版没有删除操作,因此只要 child 会话仍保留在持久化存储中,它们就会继续出现在列表里,但存活 Agent 资源仍由驻留 Activation 数量限制。
- 查询会返回每个直接可继续 child 和 diagnostic不设服务 cursor 或候选数量上限。稳定排序可使结果确定,但不会限制模型上下文增长;服务分页或删除仍是后续的产品决策
- `running``complete` 是进程语料快照,而非消息送达承诺。另一个进程可能在当前进程将某个持久化 child 报告为 `complete` 时激活它;跨进程准确性需要共享租约。
- 持久化 `label` 设为描述符必填字段是一次发布前的格式变更:描述符版本保持为 1加入 label 之前的构建所写入的日志会在描述符校验中判为 `corrupt`(与仓库首个正式版本之前不做兼容承诺的立场一致)。
- 没有删除操作,因此只要 child 会话仍保留在持久化存储中,它们就会继续出现在列表里,但存活 Agent 资源仍由驻留 Activation 数量限制。
- 服务会返回每个直接且由会话支撑的 subagent 和 diagnostic不设 cursor 或候选数量上限。稳定排序可使结果确定;模型投影避免了一次性 child 带来的上下文增长,但可继续 child 的数量仍无上限
- `running``inactive` 是进程本地语料快照,而非结果或消息送达承诺。另一个进程可能在当前进程将某个持久化 child 报告为 `inactive` 时激活它;跨进程准确性需要共享租约。
- 持久化生命周期模式是一次发布前的描述符格式变更:版本 2 会将旧版版本 1 描述符拒绝为不受支持。现在,每次由本地会话支撑的启动都会产生一个小型日志事件,使 UI 和其他服务消费方无需重放模型可见的工具结果即可对一次性历史进行分类。
- 一次性会话持久化仍为尽力执行。一次性 child 在存活期间可见dispose 后只有在其会话检查点到达持久化存储时才可继续被发现;目录参与不会像可继续激活那样增加必需的最终 flush也不会把持久化失败变为 run 失败。
- 远端 ACP 一次性运行仍不在目录中,因为它们不会发布可供 `traceSession()` 发现的本地 child 会话。若要枚举这些运行,需要单独的持久化本地记录,不能假装远端生命周期 id 就是会话 id。

View File

@@ -1517,7 +1517,7 @@ export interface Config {
export type PermissionPolicy = 'allow' | 'reject'
```
Source: [`packages/subagent/subagent-acp/src/index.ts:21`](../packages/subagent/subagent-acp/src/index.ts)
Source: [`packages/subagent/subagent-acp/src/index.ts:26`](../packages/subagent/subagent-acp/src/index.ts)
## `@deepseek-ai/dsh-subagent-dsh-sdk`

View File

@@ -794,7 +794,7 @@ A ready child settled. Scope-filtered dispatch uses the same delegating parent c
Types: [Scoped](../core-data-structures/scope.md) · [SubagentService](../core-data-structures/subagent.md)
Source: [`packages/subagent/subagent/src/index.ts:142`](../../packages/subagent/subagent/src/index.ts)
Source: [`packages/subagent/subagent/src/index.ts:151`](../../packages/subagent/subagent/src/index.ts)
### `subagent/provider-added` — emit
@@ -811,7 +811,7 @@ A provider became resolvable in the registry.
Types: [SubagentProvider](../core-data-structures/subagent.md)
Source: [`packages/subagent/subagent/src/index.ts:116`](../../packages/subagent/subagent/src/index.ts)
Source: [`packages/subagent/subagent/src/index.ts:125`](../../packages/subagent/subagent/src/index.ts)
### `subagent/provider-removed` — emit
@@ -826,7 +826,7 @@ A provider left the registry. Accepted runs remain holder-owned.
'subagent/provider-removed'(name: string): void
```
Source: [`packages/subagent/subagent/src/index.ts:122`](../../packages/subagent/subagent/src/index.ts)
Source: [`packages/subagent/subagent/src/index.ts:131`](../../packages/subagent/subagent/src/index.ts)
### `subagent/start` — emit
@@ -848,7 +848,7 @@ A provider established a ready child. For in-process providers, `ctx.agents.get(
Types: [Scoped](../core-data-structures/scope.md) · [SubagentService](../core-data-structures/subagent.md)
Source: [`packages/subagent/subagent/src/index.ts:133`](../../packages/subagent/subagent/src/index.ts)
Source: [`packages/subagent/subagent/src/index.ts:142`](../../packages/subagent/subagent/src/index.ts)
## `system-prompt/*`

View File

@@ -1993,11 +1993,11 @@ async followup( parent: Agent, childId: SessionId, content: ContentBlock[], opti
async drainContinuableDescendants(parents: readonly Agent[]): Promise<void>
/**
* Enumerate the parent's direct continuable children from the live-preferred
* session corpus without loading or resuming an Agent. Session query supplies
* lineage, candidate order, event reads, and live state; this service
* interprets descriptors, status, and per-child diagnostics without consulting
* Agent registrations, Activations, or providers.
* Enumerate the parent's direct session-backed subagents from the
* live-preferred session corpus without loading or resuming an Agent. Session
* query supplies lineage, candidate order, event reads, and live state; this
* service interprets descriptor mode, activity, and per-child diagnostics
* without consulting Agent registrations, Activations, or providers.
*
* The trace and exact descriptor read receive `signal`; the full event-list
* read has no signal parameter, so the scan rechecks cancellation around
@@ -2040,7 +2040,7 @@ list(): string[]
* fulfills; a rejection therefore has no run for the caller to dispose and
* emits no run lifecycle events.
* @param name - the provider to use.
* @param request - child prompt, parent, signal, and optional capabilities.
* @param request - child label, prompt, parent, signal, and optional capabilities.
* @returns the ready holder-owned run.
*/
async start(name: string, request: SubagentStartRequest): Promise<SubagentRun>
@@ -2048,7 +2048,7 @@ async start(name: string, request: SubagentStartRequest): Promise<SubagentRun>
Types: [Agent](../core-data-structures/core.md) · [ContentBlock](../core-data-structures/core.md) · [ContinuableStart](../core-data-structures/subagent.md) · [ContinuableStartSpec](../core-data-structures/subagent.md) · [MessageId](../core-data-structures/core.md) · [SessionId](../core-data-structures/core.md) · [SubagentFollowupOptions](../core-data-structures/subagent.md) · [SubagentListEntry](../core-data-structures/subagent.md) · [SubagentProvider](../core-data-structures/subagent.md) · [SubagentRun](../core-data-structures/subagent.md) · [SubagentStartRequest](../core-data-structures/subagent.md)
Source: [`packages/subagent/subagent/src/index.ts:147`](../../packages/subagent/subagent/src/index.ts)
Source: [`packages/subagent/subagent/src/index.ts:156`](../../packages/subagent/subagent/src/index.ts)
## `ctx.subprocess` — `SubprocessService` (abstract seam)

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 docs/core-data-structures/subagent.md
subagent.md: ae0d84af09e65bf284780722bab02282fd6c3f37
subagent.zh.md: 9d2ba038885110a52cc3c7153cc908b05ea8b15f
subagent.md: 7af4e119fa07d360f03242b2db85c60a5301cd6b
subagent.zh.md: 046135ea378e07d5e5d96b8e7fc9663c4a9329c6

View File

@@ -41,9 +41,12 @@ The tool layer builds this request from the model input and its own config; the
* What a caller asks for when starting a ONE-SHOT subagent. The tool layer
* builds this from the model's `{ description, prompt }` plus its own config;
* the service validates {@link SubagentCapabilities} against the named provider
* before dispatching to {@link SubagentProvider.start}.
* and resolves the durable descriptor before dispatching to
* {@link SubagentProvider.start}.
*/
interface SubagentStartRequest {
/** Short display label persisted with a session-backed child. */
readonly label: string
/** Content delivered as the child's user message. */
readonly prompt: ContentBlock[]
/**
@@ -95,7 +98,18 @@ interface SubagentStartRequest {
`signal` is the single cancellation channel before and after readiness. The [subagent composition-controls Agent Note](../../.agents/notes/implemented/feature/2026-07-12-subagent-persona-tool-filter-and-depth.md) owns the persona, live global-tool filter, absolute-depth, and visibility-not-authority rationale.
Providers receive exactly this request: one-shot delegation has no service-resolved continuation state, because a continuable child never reaches `SubagentProvider.start()`.
The caller-facing request does not carry catalog format details or continuation state. `SubagentService.start()` resolves the detached one-shot descriptor after capability checks, then passes this provider-facing request to the selected transport; a continuable child never reaches `SubagentProvider.start()`:
```ts type-equiv
/**
* Provider-facing one-shot request after {@link SubagentService.start} resolves
* the durable child descriptor.
*/
interface ResolvedSubagentStartRequest extends SubagentStartRequest {
/** Detached descriptor a session-backed provider persists in the child log. */
readonly descriptor: SubagentDescriptorData
}
```
## Continuable children and activations
@@ -200,11 +214,13 @@ interface ContinuableCreateSpec {
}
```
The descriptor (`SubagentDescriptorData` in [descriptor.ts](../../packages/subagent/subagent/src/descriptor.ts)) snapshots explicit fields — provider name, the delegation `description` as the durable creation `label`, resolved child `agentOptions.provider`/`model`, optional `persona`/`toolFilter` — never the merge-extensible `AgentOptions` object, so an unrelated extension value cannot break continuation and a later composition input is a deliberate version change. It omits `subagentDepth` (cold resume trusts the persisted header's `delegationDepth` as the monotone floor) and `outputSchema` (a one-shot result contract, not durable composition). The continuation manager appends the model-hidden `subagent/descriptor` event after any provider-supplied lineage and before the initial prompt is admitted; `header.seedLength` remains the fork-lineage boundary, so descriptor lookup reads the child's own suffix. The event is log-only: no `surfaceOp`, never in model history, and retained across compaction by the append-only log.
The descriptor (`SubagentDescriptorData` in [descriptor.ts](../../packages/subagent/subagent/src/descriptor.ts)) is a mode-discriminated durable identity for every session-backed subagent. Both modes carry the provider name and delegation `description` as the durable creation `label`. `one-shot` stops there; `continuable` additionally snapshots resolved child `agentOptions.provider`/`model` and optional `persona`/`toolFilter` for cold resume. It never snapshots the merge-extensible `AgentOptions` object, so an unrelated extension value cannot break continuation and a later composition input is a deliberate version change. It omits `subagentDepth` (cold resume trusts the persisted header's `delegationDepth` as the monotone floor) and `outputSchema` (one run or Activation's result contract, not durable identity).
A local one-shot provider appends the descriptor inside the child's initial turn before its first request. The continuation manager appends the descriptor after any provider-supplied lineage and before the initial prompt is admitted; `header.seedLength` remains the fork-lineage boundary, so descriptor lookup reads the child's own suffix. The event is log-only: no `surfaceOp`, never in model history, and retained across compaction by the append-only log. Malformed current-version descriptors are corrupt; unsupported versions cannot be classified by this runtime.
## Durable enumeration: `listChildren()` and `SubagentListEntry`
`SubagentService.listChildren(parentSessionId)` enumerates the parent's direct continuable children from one `ctx.sessionQuery.traceSession()` observation, without loading or resuming any Agent. Session lineage is broader than subagent identity — ordinary forks and one-shot children share `parentSession` — so exactly one supported `subagent/descriptor` event in the child's own suffix (after `seedLength`, so a fork seed cannot leak an ancestor's descriptor) is the sole subagent discriminator. The result is one `SubagentListEntry[]` in the trace's `createdAt`-then-id candidate order: a valid descriptor yields a `child` entry whose `status` snapshots the logical record (`running` = live in `ctx.sessions`, `complete` = persisted only, resumable by `send_message`); a per-child inspection failure yields a `diagnostic` entry (`corrupt`, `unsupported`, or `unavailable`) so one damaged sibling cannot hide healthy children; a missing descriptor yields no entry. A failure while building the initial trace fails the whole call — per-child isolation begins only after a trustworthy candidate set exists. The service keeps `sessionQuery` optional for by-id continuation: `listChildren()` throws `SUBAGENT_CONTROL_SESSION_QUERY_UNAVAILABLE` when it is absent, while the model-facing `list_agents` adapter (the separately loadable `/list-agents` plugin of [dsh-tool-subagent-control](../../packages/subagent/tool-subagent-control)) requires the service at plugin load. Listing does not consult the continuation manager's Activation map or provider availability; `send_message` remains the authoritative delivery-time operation, and a listed `running` child may still reject delivery as an ownership conflict.
`SubagentService.listChildren(parentSessionId)` enumerates the parent's direct session-backed subagents from one `ctx.sessionQuery.traceSession()` observation, without loading or resuming any Agent. Session lineage is broader than subagent identity — ordinary forks share `parentSession` — so exactly one supported `subagent/descriptor` event in the child's own suffix (after `seedLength`, so a fork seed cannot leak an ancestor's descriptor) is the sole subagent discriminator. The result is one `SubagentListEntry[]` in the trace's `createdAt`-then-id candidate order: a valid descriptor yields a `child` entry with `mode: 'one-shot' | 'continuable'` and `activity: 'running' | 'inactive'`; a per-child inspection failure yields a `diagnostic` entry (`corrupt`, `unsupported`, or `unavailable`) so one damaged sibling cannot hide healthy children; a missing descriptor yields no entry. Activity snapshots only whether the logical record is live in `ctx.sessions`, not outcome or resumability. A service consumer such as a UI can display both modes, while the model-facing `list_agents` adapter (the separately loadable `/list-agents` plugin of [dsh-tool-subagent-control](../../packages/subagent/tool-subagent-control)) keeps only continuable entries and maps activity to its existing `running`/`complete` vocabulary. A failure while building the initial trace fails the whole call — per-child isolation begins only after a trustworthy candidate set exists. The service keeps `sessionQuery` optional for by-id continuation: `listChildren()` throws `SubagentError` with code `SUBAGENT_CONTROL_SESSION_QUERY_UNAVAILABLE` when it is absent, while the list tool requires `ctx.subagents` and `ctx.sessionQuery` at plugin load. Listing does not consult the continuation manager's Activation map, Agent registry, or provider availability; `send_message` remains the authoritative delivery-time operation, and a listed running continuable child may still reject delivery as an ownership conflict.
## The terminal result: `SubagentResult`
@@ -295,7 +311,7 @@ interface SubagentRun {
}
```
A local one-shot run MUST publish an ordinary child agent/session before `start()` fulfills, return that child session id as `SubagentRun.id`, expose the exact child as `localAgent`, and record `request.parent.session.id` in the child's `parentSession` header. Runtime ownership may place the child under the parent, provider, or root scope. A remote provider instead returns a parent-scoped lifecycle id and `localAgent: undefined`.
A local one-shot run MUST publish an ordinary child agent/session before `start()` fulfills, return that child session id as `SubagentRun.id`, expose the exact child as `localAgent`, record `request.parent.session.id` in the child's `parentSession` header, and append the resolved descriptor inside the child's initial turn before its first request. Runtime ownership may place the child under the parent, provider, or root scope. A remote provider instead returns a parent-scoped lifecycle id and `localAgent: undefined`; without a local child Session, it is absent from trace-backed enumeration.
## The provider seam: `SubagentProvider`
@@ -321,13 +337,13 @@ interface SubagentProvider {
/**
* Establish a ONE-SHOT child and return its handle only after publication.
* The service has already validated that every requested start-time
* capability is supported, so an implementation may assume e.g.
* `request.maxDepth` is honorable when present. If setup fails or
* `request.signal` aborts before fulfillment, the provider owns and cleans
* all partial resources before this promise rejects. Ownership transfers to
* the caller only on fulfillment.
* capability is supported and resolved `request.descriptor`, so a
* session-backed implementation appends that descriptor inside the child's
* initial turn. If setup fails or `request.signal` aborts before fulfillment,
* the provider owns and cleans all partial resources before this promise
* rejects. Ownership transfers to the caller only on fulfillment.
*/
start(request: SubagentStartRequest): Promise<SubagentRun>
start(request: ResolvedSubagentStartRequest): Promise<SubagentRun>
/**
* OPTIONAL (continuable-creation capability): contribute the detached
* creation inputs that distinguish this provider's continuable children —

View File

@@ -41,9 +41,12 @@ interface SubagentCapabilities {
* What a caller asks for when starting a ONE-SHOT subagent. The tool layer
* builds this from the model's `{ description, prompt }` plus its own config;
* the service validates {@link SubagentCapabilities} against the named provider
* before dispatching to {@link SubagentProvider.start}.
* and resolves the durable descriptor before dispatching to
* {@link SubagentProvider.start}.
*/
interface SubagentStartRequest {
/** Short display label persisted with a session-backed child. */
readonly label: string
/** Content delivered as the child's user message. */
readonly prompt: ContentBlock[]
/**
@@ -95,7 +98,18 @@ interface SubagentStartRequest {
`signal` 是就绪前后唯一的取消通道。[subagent 组合控制 Agent Note](../../.agents/notes/implemented/feature/2026-07-12-subagent-persona-tool-filter-and-depth.md)规定 persona、live 全局工具过滤、绝对深度以及「可见性而非权限」的设计理由。
提供方接收的正是此请求:单次委派不含由服务解析的继续执行状态,因为可继续子 agent 绝不会到达 `SubagentProvider.start()`
面向调用方的请求不携带目录格式细节或继续执行状态。`SubagentService.start()` 会在功能检查后解析分离的一次性描述符,再将以下面向提供方的请求传给所选传输;可继续子 agent 绝不会到达 `SubagentProvider.start()`
```ts type-equiv
/**
* Provider-facing one-shot request after {@link SubagentService.start} resolves
* the durable child descriptor.
*/
interface ResolvedSubagentStartRequest extends SubagentStartRequest {
/** Detached descriptor a session-backed provider persists in the child log. */
readonly descriptor: SubagentDescriptorData
}
```
## 可继续子 agent 与激活
@@ -200,11 +214,13 @@ interface ContinuableCreateSpec {
}
```
描述符([descriptor.ts](../../packages/subagent/subagent/src/descriptor.ts) 中的 `SubagentDescriptorData`会对显式字段建立快照——提供方名称作为持久化创建 `label` 的委派 `description`已解析的子 agent `agentOptions.provider`/`model`可选的 `persona`/`toolFilter`——绝不会对可合并扩展的 `AgentOptions` 对象建立快照,因此无关的扩展值不会破坏继续执行,后续新增组合配置输入则是一次有意的版本更改。省略 `subagentDepth`(冷恢复以持久化 header 中的 `delegationDepth` 作为单调下界)和 `outputSchema`(单次结果契约,而非持久化组合配置)。继续执行管理器会在任何提供方提供的谱系之后、初始 prompt 获准之前,追加对模型隐藏的 `subagent/descriptor` 事件;`header.seedLength` 仍是 fork 谱系边界,因此描述符查找会读取子 agent 自身的后缀。该事件只进入日志:不含 `surfaceOp`,绝不进入模型历史,并由仅追加日志跨压缩保留
描述符([descriptor.ts](../../packages/subagent/subagent/src/descriptor.ts) 中的 `SubagentDescriptorData`是每个由会话支撑的 subagent 所使用、按模式判别的持久化身份。两种模式都携带提供方名称作为持久化创建 `label` 的委派 `description`。`one-shot` 到此为止;`continuable` 还会对已解析的子 agent `agentOptions.provider``model`可选的 `persona``toolFilter` 建立快照,用于冷恢复。它绝不会对可合并扩展的 `AgentOptions` 对象建立快照,因此无关的扩展值不会破坏继续执行,后续新增组合配置输入则是一次有意的版本更改。描述符省略 `subagentDepth`(冷恢复以持久化 header 中的 `delegationDepth` 作为单调下界)和 `outputSchema`(单次运行或 Activation 的结果契约,而非持久化身份)
本地一次性提供方会在子 agent 的初始轮次内、首次请求前追加描述符。继续执行管理器会在任何提供方提供的谱系之后、初始 prompt 获准之前追加描述符;`header.seedLength` 仍是 fork 谱系边界,因此描述符查找会读取子 agent 自身的后缀。该事件只进入日志:不含 `surfaceOp`,绝不进入模型历史,并由仅追加日志跨压缩保留。格式错误的当前版本描述符属于损坏;本运行时无法对不受支持的版本进行分类。
## 持久化枚举:`listChildren()` 与 `SubagentListEntry`
`SubagentService.listChildren(parentSessionId)` 从一次 `ctx.sessionQuery.traceSession()` 观测中枚举 parent 直接可继续 child,而不会加载或恢复任何 Agent。会话谱系的范围比 subagent 身份更广——普通 fork 和一次性 child 都会共享 `parentSession`——因此child 自身后缀中恰好一个受支持的 `subagent/descriptor` 事件(位于 `seedLength` 之后,避免 fork seed 泄漏祖先描述符)是唯一的 subagent 判别信息。结果是一个按追踪结果中 `createdAt`、再按 id 排列候选顺序的 `SubagentListEntry[]`:有效描述符生成 `child` 条目,其 `status` 是逻辑记录的快照(`running` 表示在 `ctx.sessions` 中存活,`complete` 表示只存在于持久化存储中并可由 `send_message` 恢复);逐 child 检查失败生成 `diagnostic` 条目(`corrupt`、`unsupported` 或 `unavailable`),因此一个损坏的 sibling 不会隐藏健康 child缺少描述符则不生成条目。构建初始追踪时的失败会让整个调用失败——只有得到可信候选集后才开始逐 child 隔离。服务将 `sessionQuery` 保持为按 id 继续执行时的可选依赖:缺少该服务时,`listChildren()` 抛出 `SUBAGENT_CONTROL_SESSION_QUERY_UNAVAILABLE`,而面向模型的 `list_agents` 适配器([dsh-tool-subagent-control](../../packages/subagent/tool-subagent-control) 中可单独加载的 `/list-agents` 插件)会在插件加载时要求该服务。枚举不会查询继续执行管理器的 Activation map 或提供方可用性;`send_message` 仍是消息送达时的权威操作,列表中的 `running` child 仍可能因所有权冲突而拒绝投递。
`SubagentService.listChildren(parentSessionId)` 从一次 `ctx.sessionQuery.traceSession()` 观测中枚举 parent 直接且由会话支撑的 subagent,而不会加载或恢复任何 Agent。会话谱系的范围比 subagent 身份更广——普通 fork 会共享 `parentSession`——因此child 自身后缀中恰好一个受支持的 `subagent/descriptor` 事件(位于 `seedLength` 之后,避免 fork seed 泄漏祖先描述符)是唯一的 subagent 判别信息。结果是一个按追踪结果中 `createdAt`、再按 id 排列候选顺序的 `SubagentListEntry[]`:有效描述符生成带有 `mode: 'one-shot' | 'continuable'` 和 `activity: 'running' | 'inactive'` 的 `child` 条目;逐 child 检查失败生成 `diagnostic` 条目(`corrupt`、`unsupported` 或 `unavailable`),因此一个损坏的 sibling 不会隐藏健康 child缺少描述符则不生成条目。活动状态只表示逻辑记录是否在 `ctx.sessions` 中存活而不表示结果或可恢复性。UI 等服务消费方可以展示两种模式;面向模型的 `list_agents` 适配器([dsh-tool-subagent-control](../../packages/subagent/tool-subagent-control) 中可单独加载的 `/list-agents` 插件)则只保留可继续条目,并将活动状态映射到现有的 `running``complete` 词汇。构建初始追踪时的失败会让整个调用失败——只有得到可信候选集后才开始逐 child 隔离。服务将 `sessionQuery` 保持为按 id 继续执行时的可选依赖:缺少该服务时,`listChildren()` 抛出 `SubagentError`,并携带错误码 `SUBAGENT_CONTROL_SESSION_QUERY_UNAVAILABLE`;列表工具则在插件加载时要求 `ctx.subagents` 与 `ctx.sessionQuery`。枚举不会查询继续执行管理器的 Activation map、Agent 注册表或提供方可用性;`send_message` 仍是消息送达时的权威操作,列表中的运行中可继续 child 仍可能因所有权冲突而拒绝投递。
## 终态结果:`SubagentResult`
@@ -295,7 +311,7 @@ interface SubagentRun {
}
```
本地单次 run 必须在 `start()` fulfill 之前发布一个普通子 agent/会话,将该子会话 id 作为 `SubagentRun.id` 返回,以 `localAgent` 暴露确切的子 agent在子 agent 的 `parentSession` header 中记录 `request.parent.session.id`。运行时所有权可以把子 agent 放在 parent、提供方或 root 作用域下。远程提供方则返回 parent 作用域的生命周期 id 与 `localAgent: undefined`。
本地单次 run 必须在 `start()` fulfill 之前发布一个普通子 agent会话,将该子会话 id 作为 `SubagentRun.id` 返回,以 `localAgent` 暴露确切的子 agent在子 agent 的 `parentSession` header 中记录 `request.parent.session.id`,并在子 agent 的初始轮次内、首次请求前追加已解析的描述符。运行时所有权可以把子 agent 放在 parent、提供方或 root 作用域下。远程提供方则返回 parent 作用域的生命周期 id 与 `localAgent: undefined`;由于没有本地 child Session它不会出现在基于追踪的枚举结果中
<a id="the-provider-seam-subagentprovider"></a>
@@ -323,13 +339,13 @@ interface SubagentProvider {
/**
* Establish a ONE-SHOT child and return its handle only after publication.
* The service has already validated that every requested start-time
* capability is supported, so an implementation may assume e.g.
* `request.maxDepth` is honorable when present. If setup fails or
* `request.signal` aborts before fulfillment, the provider owns and cleans
* all partial resources before this promise rejects. Ownership transfers to
* the caller only on fulfillment.
* capability is supported and resolved `request.descriptor`, so a
* session-backed implementation appends that descriptor inside the child's
* initial turn. If setup fails or `request.signal` aborts before fulfillment,
* the provider owns and cleans all partial resources before this promise
* rejects. Ownership transfers to the caller only on fulfillment.
*/
start(request: SubagentStartRequest): Promise<SubagentRun>
start(request: ResolvedSubagentStartRequest): Promise<SubagentRun>
/**
* OPTIONAL (continuable-creation capability): contribute the detached
* creation inputs that distinguish this provider's continuable children —

View File

@@ -22,7 +22,7 @@ This matrix shows which packages dispatch each harness-owned event and which pac
| `agent/session-start` | `emit` | [`packages/core/agent/src/types.ts:362`](../packages/core/agent/src/types.ts) | [`agent-loop`](../packages/core/agent-loop) (`emitAgentEvent`) | [`goal`](../packages/goal/goal), [`goal-session`](../packages/goal/goal-session), [`hooks-claude`](../packages/hooks/hooks-claude), [`hooks-codex`](../packages/hooks/hooks-codex), [`workspace-context`](../packages/context/workspace-context) |
| `agent/settled` | `emit` | [`packages/core/agent/src/types.ts:450`](../packages/core/agent/src/types.ts) | [`agent-loop`](../packages/core/agent-loop) (`emitAgentEvent`) | [`compact-basic`](../packages/compact/compact-basic) |
| `agent/status` | `emit` | [`packages/core/agent/src/types.ts:298`](../packages/core/agent/src/types.ts) | [`agent-loop`](../packages/core/agent-loop) (`emitAgentEvent`) | [`agent`](../packages/core/agent), `apiproxy`, [`goal-session`](../packages/goal/goal-session), [`tui`](../packages/ui/tui) |
| `agent/step` | `serial` | [`packages/core/agent/src/types.ts:389`](../packages/core/agent/src/types.ts) | [`agent-loop`](../packages/core/agent-loop) (`serial`) | [`compact-basic`](../packages/compact/compact-basic), [`plan-mode`](../packages/plan/plan-mode), [`session-checkpoint-policy`](../packages/session-persistence/session-checkpoint-policy), [`time-context`](../packages/context/time-context), [`tmux-context`](../packages/context/tmux-context), [`tool-skill`](../packages/skill/tool-skill), [`workspace-context`](../packages/context/workspace-context) |
| `agent/step` | `serial` | [`packages/core/agent/src/types.ts:389`](../packages/core/agent/src/types.ts) | [`agent-loop`](../packages/core/agent-loop) (`serial`) | [`compact-basic`](../packages/compact/compact-basic), [`plan-mode`](../packages/plan/plan-mode), [`session-checkpoint-policy`](../packages/session-persistence/session-checkpoint-policy), [`subagent-inprocess`](../packages/subagent/subagent-inprocess), [`time-context`](../packages/context/time-context), [`tmux-context`](../packages/context/tmux-context), [`tool-skill`](../packages/skill/tool-skill), [`workspace-context`](../packages/context/workspace-context) |
| `agent/turn-stopping` | `serial` | [`packages/core/agent/src/types.ts:436`](../packages/core/agent/src/types.ts) | [`agent-loop`](../packages/core/agent-loop) (`serial`) | [`hooks-claude`](../packages/hooks/hooks-claude), [`hooks-codex`](../packages/hooks/hooks-codex) |
| `approval/request` | `waterfall` | [`packages/ui/user-approval/src/index.ts:30`](../packages/ui/user-approval/src/index.ts) | [`user-approval`](../packages/ui/user-approval) (`waterfall`) | [`acp`](../packages/acp/acp), `apiproxy` |
| `commands/change` | `emit` | [`packages/ui/commands/src/index.ts:154`](../packages/ui/commands/src/index.ts) | [`commands`](../packages/ui/commands) (`events.dispatch`) | `apiproxy`, [`tui`](../packages/ui/tui) |
@@ -41,10 +41,10 @@ This matrix shows which packages dispatch each harness-owned event and which pac
| `settings/document-updated` | `emit` | [`packages/settings/settings/src/index.ts:150`](../packages/settings/settings/src/index.ts) | [`settings`](../packages/settings/settings) (`events.dispatch`) | `apiproxy` |
| `settings/updated` | `emit` | [`packages/settings/settings/src/index.ts:137`](../packages/settings/settings/src/index.ts) | [`settings`](../packages/settings/settings) (`events.dispatch`) | [`settings`](../packages/settings/settings) |
| `skills/change` | `emit` | [`packages/skill/skill/src/index.ts:188`](../packages/skill/skill/src/index.ts) | [`skill`](../packages/skill/skill) (`events.dispatch`) | [`tui`](../packages/ui/tui) |
| `subagent/end` | `emit` | [`packages/subagent/subagent/src/index.ts:142`](../packages/subagent/subagent/src/index.ts) | [`subagent`](../packages/subagent/subagent) (`events.dispatch`) | [`hooks-claude`](../packages/hooks/hooks-claude), [`jsonrpc`](../packages/ui/jsonrpc), [`subagent`](../packages/subagent/subagent) |
| `subagent/provider-added` | `emit` | [`packages/subagent/subagent/src/index.ts:116`](../packages/subagent/subagent/src/index.ts) | [`subagent`](../packages/subagent/subagent) (`emit`) | [`subagent`](../packages/subagent/subagent), [`tool-subagent`](../packages/subagent/tool-subagent) |
| `subagent/provider-removed` | `emit` | [`packages/subagent/subagent/src/index.ts:122`](../packages/subagent/subagent/src/index.ts) | [`subagent`](../packages/subagent/subagent) (`events.dispatch`) | [`subagent`](../packages/subagent/subagent), [`tool-subagent`](../packages/subagent/tool-subagent) |
| `subagent/start` | `emit` | [`packages/subagent/subagent/src/index.ts:133`](../packages/subagent/subagent/src/index.ts) | [`subagent`](../packages/subagent/subagent) (`events.dispatch`) | [`hooks-claude`](../packages/hooks/hooks-claude), [`subagent`](../packages/subagent/subagent) |
| `subagent/end` | `emit` | [`packages/subagent/subagent/src/index.ts:151`](../packages/subagent/subagent/src/index.ts) | [`subagent`](../packages/subagent/subagent) (`events.dispatch`) | [`hooks-claude`](../packages/hooks/hooks-claude), [`jsonrpc`](../packages/ui/jsonrpc), [`subagent`](../packages/subagent/subagent) |
| `subagent/provider-added` | `emit` | [`packages/subagent/subagent/src/index.ts:125`](../packages/subagent/subagent/src/index.ts) | [`subagent`](../packages/subagent/subagent) (`emit`) | [`subagent`](../packages/subagent/subagent), [`tool-subagent`](../packages/subagent/tool-subagent) |
| `subagent/provider-removed` | `emit` | [`packages/subagent/subagent/src/index.ts:131`](../packages/subagent/subagent/src/index.ts) | [`subagent`](../packages/subagent/subagent) (`events.dispatch`) | [`subagent`](../packages/subagent/subagent), [`tool-subagent`](../packages/subagent/tool-subagent) |
| `subagent/start` | `emit` | [`packages/subagent/subagent/src/index.ts:142`](../packages/subagent/subagent/src/index.ts) | [`subagent`](../packages/subagent/subagent) (`events.dispatch`) | [`hooks-claude`](../packages/hooks/hooks-claude), [`subagent`](../packages/subagent/subagent) |
| `system-prompt/assemble` | `waterfall` | [`packages/core/system-prompt/src/index.ts:29`](../packages/core/system-prompt/src/index.ts) | [`system-prompt`](../packages/core/system-prompt) (`waterfall`) | [`agent`](../packages/core/agent), [`system-prompt`](../packages/core/system-prompt) |
| `system-prompt/change` | `emit` | [`packages/core/system-prompt/src/index.ts:35`](../packages/core/system-prompt/src/index.ts) | [`system-prompt`](../packages/core/system-prompt) (`emit`) | - |
| `telemetry/record` | `waterfall` | [`packages/telemetry/session-telemetry/src/index.ts:41`](../packages/telemetry/session-telemetry/src/index.ts) | [`session-telemetry`](../packages/telemetry/session-telemetry) (`waterfall`) | - |

View File

@@ -522,11 +522,11 @@ Source: [`packages/core/session/src/types.ts:216`](../packages/core/session/src/
```ts persistence-catalog
/**
* Durable declared composition of a continuable subagent child, appended
* once by the establishing provider inside the child's initial turn,
* before its first request. Log-only: it carries no `surfaceOp`, never
* enters model history, and the append-only log retains it when
* compaction replaces surface history.
* Durable identity and lifecycle mode of a session-backed subagent child,
* appended once by the establishing provider inside the child's initial
* turn, before its first request. Continuable records also carry their
* resumable composition. Log-only: it carries no `surfaceOp`, never enters
* model history, and survives compaction.
*/
'subagent/descriptor': SubagentDescriptorData
```

View File

@@ -1151,7 +1151,7 @@ The registered tool name is the load-time `toolName` config (default `subagent`)
### `list_agents`
List your background subagents by durable id and label. Status is a snapshot of the stored record: running means the subagent session is currently live in this process, complete means it exists only in storage and a `send_message` starts a new turn on the same conversation. The snapshot is not a delivery promise — `send_message` performs the authoritative check and may still fail. Children that could not be read are reported as diagnostics instead of being silently dropped.
List your continuable background subagents by durable id and label. Status is a snapshot of the stored record: running means the subagent session is currently live in this process, complete means it exists only in storage and a `send_message` starts a new turn on the same conversation. The snapshot is not a delivery promise — `send_message` performs the authoritative check and may still fail. Children that could not be read are reported as diagnostics instead of being silently dropped.
```json
{

View File

@@ -1,16 +1,17 @@
{"type":"session","version":0,"id":"22222222-2222-4222-8222-222222222222","createdAt":1783950001000,"cwd":"{{cwd}}","parentSession":"11111111-1111-4111-8111-111111111111","delegationDepth":1}
{"type":"turn/start","seq":0,"time":1783957884563,"data":{"turn":1,"trigger":{"kind":"message","source":{"kind":"user"}}}}
{"type":"user/message","seq":1,"time":1783957884563,"data":{"content":[{"type":"text","text":"Reply with exactly DIRECT_CHILD_OK and nothing else."}],"source":{"kind":"user"},"role":"user","id":"08833a2b-cc54-47c8-9702-a156211e37b1"},"surfaceOp":"append"}
{"type":"user/message","seq":1,"time":1783957884563,"data":{"content":[{"type":"text","text":"Reply with exactly DIRECT_CHILD_OK and nothing else."}],"source":{"kind":"user"},"role":"user","id":"9769d9fc-4aef-45ae-bea7-dfa1697971ef"},"surfaceOp":"append"}
{"type":"session/title","seq":2,"time":1783957884563,"data":{"title":"Reply with exactly DIRECT_CHILD_OK and","messageSeqs":[1],"source":{"kind":"fallback"}}}
{"type":"user/message","seq":3,"time":1785464659102,"data":{"content":[{"type":"text","text":"Current runtime context. This snapshot supersedes earlier runtime-context snapshots.\n\nCurrent DSH file policy: danger-full-access. The DSH file sandbox does not restrict file modifications by available operations.\n\nApproval prompts are disabled in this session: actions that require approval are rejected automatically — do not request sandbox escalation (do not set `sandbox_permissions`)."}],"source":{"kind":"plugin","plugin":"@deepseek-ai/dsh-system-prompt"},"role":"user","id":"c12fb438-014d-4998-a5b1-afd6650206fc"},"surfaceOp":"append"}
{"type":"step/start","seq":4,"time":1785464659102,"data":{"turn":1,"step":1}}
{"type":"request/header","seq":5,"time":1785464659102,"data":{"header":{"config":{"provider":"deepseek-official","model":"deepseek-v4-flash"},"system":"{{system}}","tools":"{{tools}}"},"reason":"initial"}}
{"type":"request/context","seq":6,"time":1785487621173,"data":{"provider":"deepseek-official","model":"deepseek-v4-flash"}}
{"type":"assistant/chunk","seq":7,"time":1783957884564,"data":{"turn":1,"step":1,"chunk":{"type":"block-start","index":0,"blockType":"text"}}}
{"type":"assistant/chunk","seq":8,"time":1783957884564,"data":{"turn":1,"step":1,"chunk":{"type":"text-delta","index":0,"text":"DIRECT_CHILD_OK"}}}
{"type":"assistant/chunk","seq":9,"time":1783957884564,"data":{"turn":1,"step":1,"chunk":{"type":"block-end","index":0,"block":{"type":"text","text":"DIRECT_CHILD_OK"}}}}
{"type":"assistant/chunk","seq":10,"time":1785464659110,"data":{"turn":1,"step":1,"chunk":{"type":"usage","usage":{"inputTokens":3,"outputTokens":3}}}}
{"type":"assistant/chunk","seq":11,"time":1785487621180,"data":{"turn":1,"step":1,"chunk":{"type":"finish","reason":{"kind":"stop"}}}}
{"type":"assistant/message","seq":12,"time":1785487621180,"data":{"turn":1,"step":1,"message":{"role":"assistant","content":[{"type":"text","text":"DIRECT_CHILD_OK"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"28029a3a-d646-40f7-ba4c-f4b158705fc7"},"usage":{"inputTokens":3,"outputTokens":3}},"sourceEventSeqs":[7,8,9,10,11],"surfaceOp":"append"}
{"type":"step/end","seq":13,"time":1785487621180,"data":{"turn":1,"step":1}}
{"type":"turn/end","seq":14,"time":1785487621181,"data":{"turn":1,"reason":{"kind":"completed"}}}
{"type":"subagent/descriptor","seq":3,"time":1785534720685,"data":{"version":2,"mode":"one-shot","provider":"spawn","label":"Check direct child"}}
{"type":"user/message","seq":4,"time":1785534720686,"data":{"content":[{"type":"text","text":"Current runtime context. This snapshot supersedes earlier runtime-context snapshots.\n\nCurrent DSH file policy: danger-full-access. The DSH file sandbox does not restrict file modifications by available operations.\n\nApproval prompts are disabled in this session: actions that require approval are rejected automatically — do not request sandbox escalation (do not set `sandbox_permissions`)."}],"source":{"kind":"plugin","plugin":"@deepseek-ai/dsh-system-prompt"},"role":"user","id":"7fd53a8f-7165-47ea-adcf-84a8d303ec47"},"surfaceOp":"append"}
{"type":"step/start","seq":5,"time":1785534720686,"data":{"turn":1,"step":1}}
{"type":"request/header","seq":6,"time":1785534720686,"data":{"header":{"config":{"provider":"deepseek-official","model":"deepseek-v4-flash"},"system":"{{system}}","tools":"{{tools}}"},"reason":"initial"}}
{"type":"request/context","seq":7,"time":1785534720687,"data":{"provider":"deepseek-official","model":"deepseek-v4-flash"}}
{"type":"assistant/chunk","seq":8,"time":1783957884564,"data":{"turn":1,"step":1,"chunk":{"type":"block-start","index":0,"blockType":"text"}}}
{"type":"assistant/chunk","seq":9,"time":1783957884564,"data":{"turn":1,"step":1,"chunk":{"type":"text-delta","index":0,"text":"DIRECT_CHILD_OK"}}}
{"type":"assistant/chunk","seq":10,"time":1785464659110,"data":{"turn":1,"step":1,"chunk":{"type":"block-end","index":0,"block":{"type":"text","text":"DIRECT_CHILD_OK"}}}}
{"type":"assistant/chunk","seq":11,"time":1785487621180,"data":{"turn":1,"step":1,"chunk":{"type":"usage","usage":{"inputTokens":3,"outputTokens":3}}}}
{"type":"assistant/chunk","seq":12,"time":1785534720695,"data":{"turn":1,"step":1,"chunk":{"type":"finish","reason":{"kind":"stop"}}}}
{"type":"assistant/message","seq":13,"time":1785534720695,"data":{"turn":1,"step":1,"message":{"role":"assistant","content":[{"type":"text","text":"DIRECT_CHILD_OK"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"7032baa0-b8a1-4c26-8064-8a95f6fc7309"},"usage":{"inputTokens":3,"outputTokens":3}},"sourceEventSeqs":[8,9,10,11,12],"surfaceOp":"append"}
{"type":"step/end","seq":14,"time":1785534720695,"data":{"turn":1,"step":1}}
{"type":"turn/end","seq":15,"time":1785534720695,"data":{"turn":1,"reason":{"kind":"completed"}}}

View File

@@ -1,16 +1,17 @@
{"type":"session","version":0,"id":"33333333-3333-4333-8333-333333333333","createdAt":1783950002000,"cwd":"{{cwd}}","parentSession":"11111111-1111-4111-8111-111111111111","delegationDepth":1}
{"type":"turn/start","seq":0,"time":1783957884700,"data":{"turn":1,"trigger":{"kind":"message","source":{"kind":"user"}}}}
{"type":"user/message","seq":1,"time":1783957884700,"data":{"content":[{"type":"text","text":"Reply with exactly WORKFLOW_CHILD_OK and nothing else."}],"source":{"kind":"user"},"role":"user","id":"6760a904-ddd4-40e0-b149-2c3ac8b2b2f7"},"surfaceOp":"append"}
{"type":"user/message","seq":1,"time":1783957884700,"data":{"content":[{"type":"text","text":"Reply with exactly WORKFLOW_CHILD_OK and nothing else."}],"source":{"kind":"user"},"role":"user","id":"a9ccad1e-ded5-4f5f-9419-a6bc440e96c3"},"surfaceOp":"append"}
{"type":"session/title","seq":2,"time":1783957884700,"data":{"title":"Reply with exactly WORKFLOW_CHILD_OK and","messageSeqs":[1],"source":{"kind":"fallback"}}}
{"type":"user/message","seq":3,"time":1785464659272,"data":{"content":[{"type":"text","text":"Current runtime context. This snapshot supersedes earlier runtime-context snapshots.\n\nCurrent DSH file policy: danger-full-access. The DSH file sandbox does not restrict file modifications by available operations.\n\nApproval prompts are disabled in this session: actions that require approval are rejected automatically — do not request sandbox escalation (do not set `sandbox_permissions`)."}],"source":{"kind":"plugin","plugin":"@deepseek-ai/dsh-system-prompt"},"role":"user","id":"3c1c478b-8b96-4c9c-9bea-92c4871294fe"},"surfaceOp":"append"}
{"type":"step/start","seq":4,"time":1785464659272,"data":{"turn":1,"step":1}}
{"type":"request/header","seq":5,"time":1785464659272,"data":{"header":{"config":{"provider":"deepseek-official","model":"deepseek-v4-flash"},"system":"{{system}}","tools":"{{tools}}"},"reason":"initial"}}
{"type":"request/context","seq":6,"time":1785487621338,"data":{"provider":"deepseek-official","model":"deepseek-v4-flash"}}
{"type":"assistant/chunk","seq":7,"time":1783957884701,"data":{"turn":1,"step":1,"chunk":{"type":"block-start","index":0,"blockType":"text"}}}
{"type":"assistant/chunk","seq":8,"time":1783957884701,"data":{"turn":1,"step":1,"chunk":{"type":"text-delta","index":0,"text":"WORKFLOW_CHILD_OK"}}}
{"type":"assistant/chunk","seq":9,"time":1783957884701,"data":{"turn":1,"step":1,"chunk":{"type":"block-end","index":0,"block":{"type":"text","text":"WORKFLOW_CHILD_OK"}}}}
{"type":"assistant/chunk","seq":10,"time":1785464659280,"data":{"turn":1,"step":1,"chunk":{"type":"usage","usage":{"inputTokens":3,"outputTokens":3}}}}
{"type":"assistant/chunk","seq":11,"time":1785487621348,"data":{"turn":1,"step":1,"chunk":{"type":"finish","reason":{"kind":"stop"}}}}
{"type":"assistant/message","seq":12,"time":1785487621348,"data":{"turn":1,"step":1,"message":{"role":"assistant","content":[{"type":"text","text":"WORKFLOW_CHILD_OK"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"45af891b-11f1-4e6d-9c5b-375c46f574dd"},"usage":{"inputTokens":3,"outputTokens":3}},"sourceEventSeqs":[7,8,9,10,11],"surfaceOp":"append"}
{"type":"step/end","seq":13,"time":1785487621348,"data":{"turn":1,"step":1}}
{"type":"turn/end","seq":14,"time":1785487621348,"data":{"turn":1,"reason":{"kind":"completed"}}}
{"type":"subagent/descriptor","seq":3,"time":1785534720833,"data":{"version":2,"mode":"one-shot","provider":"spawn","label":"workflow-child"}}
{"type":"user/message","seq":4,"time":1785534720834,"data":{"content":[{"type":"text","text":"Current runtime context. This snapshot supersedes earlier runtime-context snapshots.\n\nCurrent DSH file policy: danger-full-access. The DSH file sandbox does not restrict file modifications by available operations.\n\nApproval prompts are disabled in this session: actions that require approval are rejected automatically — do not request sandbox escalation (do not set `sandbox_permissions`)."}],"source":{"kind":"plugin","plugin":"@deepseek-ai/dsh-system-prompt"},"role":"user","id":"253f3d13-7f37-430b-8dc8-ce9ee53f1ddc"},"surfaceOp":"append"}
{"type":"step/start","seq":5,"time":1785534720834,"data":{"turn":1,"step":1}}
{"type":"request/header","seq":6,"time":1785534720835,"data":{"header":{"config":{"provider":"deepseek-official","model":"deepseek-v4-flash"},"system":"{{system}}","tools":"{{tools}}"},"reason":"initial"}}
{"type":"request/context","seq":7,"time":1785534720835,"data":{"provider":"deepseek-official","model":"deepseek-v4-flash"}}
{"type":"assistant/chunk","seq":8,"time":1783957884701,"data":{"turn":1,"step":1,"chunk":{"type":"block-start","index":0,"blockType":"text"}}}
{"type":"assistant/chunk","seq":9,"time":1783957884701,"data":{"turn":1,"step":1,"chunk":{"type":"text-delta","index":0,"text":"WORKFLOW_CHILD_OK"}}}
{"type":"assistant/chunk","seq":10,"time":1785464659280,"data":{"turn":1,"step":1,"chunk":{"type":"block-end","index":0,"block":{"type":"text","text":"WORKFLOW_CHILD_OK"}}}}
{"type":"assistant/chunk","seq":11,"time":1785487621348,"data":{"turn":1,"step":1,"chunk":{"type":"usage","usage":{"inputTokens":3,"outputTokens":3}}}}
{"type":"assistant/chunk","seq":12,"time":1785534720842,"data":{"turn":1,"step":1,"chunk":{"type":"finish","reason":{"kind":"stop"}}}}
{"type":"assistant/message","seq":13,"time":1785534720842,"data":{"turn":1,"step":1,"message":{"role":"assistant","content":[{"type":"text","text":"WORKFLOW_CHILD_OK"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"892d0e12-63cd-4700-aebb-ba610e36bec6"},"usage":{"inputTokens":3,"outputTokens":3}},"sourceEventSeqs":[8,9,10,11,12],"surfaceOp":"append"}
{"type":"step/end","seq":14,"time":1785534720842,"data":{"turn":1,"step":1}}
{"type":"turn/end","seq":15,"time":1785534720842,"data":{"turn":1,"reason":{"kind":"completed"}}}

View File

@@ -94,7 +94,7 @@ interface ToolArgsMap {
} & Record<string, JsonValue>;
/** Read the current same-session goal, including its exact id/revision, objective, phase, completed continuation rounds, round limit, blocker reason when present, and whether another continuation is armed. Call this before updating a goal. */
get_goal: Record<string, JsonValue>;
/** List your background subagents by durable id and label. Status is a snapshot of the stored record: running means the subagent session is currently live in this process, complete means it exists only in storage and a `send_message` starts a new turn on the same conversation. The snapshot is not a delivery promise — `send_message` performs the authoritative check and may still fail. Children that could not be read are reported as diagnostics instead of being silently dropped. */
/** List your continuable background subagents by durable id and label. Status is a snapshot of the stored record: running means the subagent session is currently live in this process, complete means it exists only in storage and a `send_message` starts a new turn on the same conversation. The snapshot is not a delivery promise — `send_message` performs the authoritative check and may still fail. Children that could not be read are reported as diagnostics instead of being silently dropped. */
list_agents: Record<string, JsonValue>;
/** Run a foreground fresh-agent Ralph loop toward one immutable objective. Use only when the direct human explicitly asks for Ralph or fresh-agent iteration. Each round opens a new child with no parent conversation or prior child session; the shared workspace is long-term memory, and only a bounded structured report crosses rounds. The call returns when a worker reports completion or a concrete blocker, or at the round limit. Ordinary long-running same-session work belongs to goal tools. */
ralph: {

View File

@@ -174,7 +174,7 @@
},
{
"name": "list_agents",
"description": "List your background subagents by durable id and label. Status is a snapshot of the stored record: running means the subagent session is currently live in this process, complete means it exists only in storage and a `send_message` starts a new turn on the same conversation. The snapshot is not a delivery promise — `send_message` performs the authoritative check and may still fail. Children that could not be read are reported as diagnostics instead of being silently dropped.",
"description": "List your continuable background subagents by durable id and label. Status is a snapshot of the stored record: running means the subagent session is currently live in this process, complete means it exists only in storage and a `send_message` starts a new turn on the same conversation. The snapshot is not a delivery promise — `send_message` performs the authoritative check and may still fail. Children that could not be read are reported as diagnostics instead of being silently dropped.",
"parameters": {
"type": "object",
"properties": {}

View File

@@ -117,7 +117,7 @@
},
{
"name": "list_agents",
"description": "List your background subagents by durable id and label. Status is a snapshot of the stored record: running means the subagent session is currently live in this process, complete means it exists only in storage and a `send_message` starts a new turn on the same conversation. The snapshot is not a delivery promise — `send_message` performs the authoritative check and may still fail. Children that could not be read are reported as diagnostics instead of being silently dropped.",
"description": "List your continuable background subagents by durable id and label. Status is a snapshot of the stored record: running means the subagent session is currently live in this process, complete means it exists only in storage and a `send_message` starts a new turn on the same conversation. The snapshot is not a delivery promise — `send_message` performs the authoritative check and may still fail. Children that could not be read are reported as diagnostics instead of being silently dropped.",
"parameters": {
"type": "object",
"properties": {}

View File

@@ -77,7 +77,7 @@ interface ToolArgsMap {
} & Record<string, JsonValue>;
/** Read the current same-session goal, including its exact id/revision, objective, phase, completed continuation rounds, round limit, blocker reason when present, and whether another continuation is armed. Call this before updating a goal. */
get_goal: Record<string, JsonValue>;
/** List your background subagents by durable id and label. Status is a snapshot of the stored record: running means the subagent session is currently live in this process, complete means it exists only in storage and a `send_message` starts a new turn on the same conversation. The snapshot is not a delivery promise — `send_message` performs the authoritative check and may still fail. Children that could not be read are reported as diagnostics instead of being silently dropped. */
/** List your continuable background subagents by durable id and label. Status is a snapshot of the stored record: running means the subagent session is currently live in this process, complete means it exists only in storage and a `send_message` starts a new turn on the same conversation. The snapshot is not a delivery promise — `send_message` performs the authoritative check and may still fail. Children that could not be read are reported as diagnostics instead of being silently dropped. */
list_agents: Record<string, JsonValue>;
/** Run a foreground fresh-agent Ralph loop toward one immutable objective. Use only when the direct human explicitly asks for Ralph or fresh-agent iteration. Each round opens a new child with no parent conversation or prior child session; the shared workspace is long-term memory, and only a bounded structured report crosses rounds. The call returns when a worker reports completion or a concrete blocker, or at the round limit. Ordinary long-running same-session work belongs to goal tools. */
ralph: {

View File

@@ -117,7 +117,7 @@
},
{
"name": "list_agents",
"description": "List your background subagents by durable id and label. Status is a snapshot of the stored record: running means the subagent session is currently live in this process, complete means it exists only in storage and a `send_message` starts a new turn on the same conversation. The snapshot is not a delivery promise — `send_message` performs the authoritative check and may still fail. Children that could not be read are reported as diagnostics instead of being silently dropped.",
"description": "List your continuable background subagents by durable id and label. Status is a snapshot of the stored record: running means the subagent session is currently live in this process, complete means it exists only in storage and a `send_message` starts a new turn on the same conversation. The snapshot is not a delivery promise — `send_message` performs the authoritative check and may still fail. Children that could not be read are reported as diagnostics instead of being silently dropped.",
"parameters": {
"type": "object",
"properties": {}

View File

@@ -117,7 +117,7 @@
},
{
"name": "list_agents",
"description": "List your background subagents by durable id and label. Status is a snapshot of the stored record: running means the subagent session is currently live in this process, complete means it exists only in storage and a `send_message` starts a new turn on the same conversation. The snapshot is not a delivery promise — `send_message` performs the authoritative check and may still fail. Children that could not be read are reported as diagnostics instead of being silently dropped.",
"description": "List your continuable background subagents by durable id and label. Status is a snapshot of the stored record: running means the subagent session is currently live in this process, complete means it exists only in storage and a `send_message` starts a new turn on the same conversation. The snapshot is not a delivery promise — `send_message` performs the authoritative check and may still fail. Children that could not be read are reported as diagnostics instead of being silently dropped.",
"parameters": {
"type": "object",
"properties": {}

View File

@@ -117,7 +117,7 @@
},
{
"name": "list_agents",
"description": "List your background subagents by durable id and label. Status is a snapshot of the stored record: running means the subagent session is currently live in this process, complete means it exists only in storage and a `send_message` starts a new turn on the same conversation. The snapshot is not a delivery promise — `send_message` performs the authoritative check and may still fail. Children that could not be read are reported as diagnostics instead of being silently dropped.",
"description": "List your continuable background subagents by durable id and label. Status is a snapshot of the stored record: running means the subagent session is currently live in this process, complete means it exists only in storage and a `send_message` starts a new turn on the same conversation. The snapshot is not a delivery promise — `send_message` performs the authoritative check and may still fail. Children that could not be read are reported as diagnostics instead of being silently dropped.",
"parameters": {
"type": "object",
"properties": {}

View File

@@ -1,5 +1,5 @@
{"type":"session","version":0,"id":"33333333-3333-4333-8333-333333333333","createdAt":1789000001000,"cwd":"{{cwd}}","parentSession":"11111111-1111-4111-8111-111111111111","delegationDepth":1}
{"type":"subagent/descriptor","seq":0,"time":1785544945198,"data":{"version":1,"provider":"spawn","label":"Reply with CHILD_OK","agentProvider":"deepseek-official","agentModel":"deepseek-v4-flash"}}
{"type":"subagent/descriptor","seq":0,"time":1785544945198,"data":{"version":2,"mode":"continuable","provider":"spawn","label":"Reply with CHILD_OK","agentProvider":"deepseek-official","agentModel":"deepseek-v4-flash"}}
{"type":"session/end-seed","seq":1,"time":1785544945198,"data":{}}
{"type":"turn/start","seq":2,"time":1785544945199,"data":{"turn":1,"trigger":{"kind":"message","source":{"kind":"user"}}}}
{"type":"user/message","seq":3,"time":1785544945199,"data":{"content":[{"type":"text","text":"Reply with exactly the word CHILD_OK and nothing else."}],"source":{"kind":"user"},"role":"user","id":"cd28c816-821e-412c-bc7f-404bbb599641"},"surfaceOp":"append"}

View File

@@ -1,26 +1,27 @@
{"type":"session","version":0,"id":"22222222-2222-4222-8222-222222222222","createdAt":1001,"cwd":"{{cwd}}","parentSession":"11111111-1111-4111-8111-111111111111","delegationDepth":1}
{"type":"turn/start","seq":0,"time":1784540790312,"data":{"turn":1,"trigger":{"kind":"message","source":{"kind":"user"}}}}
{"type":"user/message","seq":1,"time":1784540790312,"data":{"content":[{"type":"text","text":"Call subagent once. Ask that child to attempt one further subagent call, then report the result."}],"source":{"kind":"user"},"role":"user","id":"52f9dcaa-5b80-455c-b419-76d2153bb3ba"},"surfaceOp":"append"}
{"type":"user/message","seq":1,"time":1784540790312,"data":{"content":[{"type":"text","text":"Call subagent once. Ask that child to attempt one further subagent call, then report the result."}],"source":{"kind":"user"},"role":"user","id":"9cf268f7-2c64-4344-8512-dc9cb4dc66d4"},"surfaceOp":"append"}
{"type":"session/title","seq":2,"time":1784540790312,"data":{"title":"Call subagent once. Ask that","messageSeqs":[1],"source":{"kind":"fallback"}}}
{"type":"user/message","seq":3,"time":1785464656951,"data":{"content":[{"type":"text","text":"Current runtime context. This snapshot supersedes earlier runtime-context snapshots.\n\nCurrent DSH file policy: danger-full-access. The DSH file sandbox does not restrict file modifications by available operations.\n\nApproval prompts are disabled in this session: actions that require approval are rejected automatically — do not request sandbox escalation (do not set `sandbox_permissions`)."}],"source":{"kind":"plugin","plugin":"@deepseek-ai/dsh-system-prompt"},"role":"user","id":"189ac761-4e7f-442d-98c1-6be0dbf48403"},"surfaceOp":"append"}
{"type":"step/start","seq":4,"time":1785464656951,"data":{"turn":1,"step":1}}
{"type":"request/header","seq":5,"time":1785464656952,"data":{"header":{"config":{"provider":"deepseek-official","model":"deepseek-v4-flash"},"system":"{{system}}","tools":"{{tools}}"},"reason":"initial"}}
{"type":"request/context","seq":6,"time":1785487618141,"data":{"provider":"deepseek-official","model":"deepseek-v4-flash"}}
{"type":"assistant/chunk","seq":7,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}}}
{"type":"assistant/chunk","seq":8,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":0,"id":"call_depth_one_child","name":"subagent","argumentsDelta":"{\"description\":\"Start depth two\",\"prompt\":\"Attempt one subagent call beyond the configured cap, then report the rejection.\"}"}}}
{"type":"assistant/chunk","seq":9,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"call_depth_one_child","name":"subagent","arguments":"{\"description\":\"Start depth two\",\"prompt\":\"Attempt one subagent call beyond the configured cap, then report the rejection.\"}"}}}}
{"type":"assistant/chunk","seq":10,"time":1785464656958,"data":{"turn":1,"step":1,"chunk":{"type":"usage","usage":{"inputTokens":10,"outputTokens":5}}}}
{"type":"assistant/chunk","seq":11,"time":1785487618150,"data":{"turn":1,"step":1,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}}
{"type":"assistant/message","seq":12,"time":1785487618150,"data":{"turn":1,"step":1,"message":{"role":"assistant","content":[{"type":"tool-call","id":"call_depth_one_child","name":"subagent","arguments":"{\"description\":\"Start depth two\",\"prompt\":\"Attempt one subagent call beyond the configured cap, then report the rejection.\"}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"eda28e57-5b5a-4671-827d-d2205c02c0e7"},"usage":{"inputTokens":10,"outputTokens":5}},"sourceEventSeqs":[7,8,9,10,11],"surfaceOp":"append"}
{"type":"tool/call","seq":13,"time":1785487618150,"data":{"turn":1,"step":1,"callId":"call_depth_one_child","name":"subagent","arguments":"{\"description\":\"Start depth two\",\"prompt\":\"Attempt one subagent call beyond the configured cap, then report the rejection.\"}"}}
{"type":"tool/result","seq":14,"time":1785487618208,"data":{"turn":1,"step":1,"message":{"source":{"kind":"tool","callId":"call_depth_one_child"},"content":[{"type":"tool-result","toolCallId":"call_depth_one_child","content":[{"type":"text","text":"DEPTH_REJECTED"}],"isError":false}],"role":"user","id":"a917693d-5c66-4f0a-ba32-ba6ac9316687"}},"sourceEventSeqs":[13],"surfaceOp":"append"}
{"type":"step/end","seq":15,"time":1785487618208,"data":{"turn":1,"step":1}}
{"type":"step/start","seq":16,"time":1785487618216,"data":{"turn":1,"step":2}}
{"type":"assistant/chunk","seq":17,"time":1784540790365,"data":{"turn":1,"step":2,"chunk":{"type":"block-start","index":0,"blockType":"text"}}}
{"type":"assistant/chunk","seq":18,"time":1784540790365,"data":{"turn":1,"step":2,"chunk":{"type":"text-delta","index":0,"text":"DEPTH_ONE_DONE"}}}
{"type":"assistant/chunk","seq":19,"time":1784540790365,"data":{"turn":1,"step":2,"chunk":{"type":"block-end","index":0,"block":{"type":"text","text":"DEPTH_ONE_DONE"}}}}
{"type":"assistant/chunk","seq":20,"time":1785464657024,"data":{"turn":1,"step":2,"chunk":{"type":"usage","usage":{"inputTokens":10,"outputTokens":2}}}}
{"type":"assistant/chunk","seq":21,"time":1785487618220,"data":{"turn":1,"step":2,"chunk":{"type":"finish","reason":{"kind":"stop"}}}}
{"type":"assistant/message","seq":22,"time":1785487618220,"data":{"turn":1,"step":2,"message":{"role":"assistant","content":[{"type":"text","text":"DEPTH_ONE_DONE"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"1197004a-074f-4ec6-ae8e-9bb102578f12"},"usage":{"inputTokens":10,"outputTokens":2}},"sourceEventSeqs":[17,18,19,20,21],"surfaceOp":"append"}
{"type":"step/end","seq":23,"time":1785487618220,"data":{"turn":1,"step":2}}
{"type":"turn/end","seq":24,"time":1785487618220,"data":{"turn":1,"reason":{"kind":"completed"}}}
{"type":"subagent/descriptor","seq":3,"time":1785534718281,"data":{"version":2,"mode":"one-shot","provider":"spawn","label":"Start depth one"}}
{"type":"user/message","seq":4,"time":1785534718281,"data":{"content":[{"type":"text","text":"Current runtime context. This snapshot supersedes earlier runtime-context snapshots.\n\nCurrent DSH file policy: danger-full-access. The DSH file sandbox does not restrict file modifications by available operations.\n\nApproval prompts are disabled in this session: actions that require approval are rejected automatically — do not request sandbox escalation (do not set `sandbox_permissions`)."}],"source":{"kind":"plugin","plugin":"@deepseek-ai/dsh-system-prompt"},"role":"user","id":"5cb1da9d-4cb0-4ac9-9f3e-f72a09d893da"},"surfaceOp":"append"}
{"type":"step/start","seq":5,"time":1785534718281,"data":{"turn":1,"step":1}}
{"type":"request/header","seq":6,"time":1785534718281,"data":{"header":{"config":{"provider":"deepseek-official","model":"deepseek-v4-flash"},"system":"{{system}}","tools":"{{tools}}"},"reason":"initial"}}
{"type":"request/context","seq":7,"time":1785534718281,"data":{"provider":"deepseek-official","model":"deepseek-v4-flash"}}
{"type":"assistant/chunk","seq":8,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}}}
{"type":"assistant/chunk","seq":9,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":0,"id":"call_depth_one_child","name":"subagent","argumentsDelta":"{\"description\":\"Start depth two\",\"prompt\":\"Attempt one subagent call beyond the configured cap, then report the rejection.\"}"}}}
{"type":"assistant/chunk","seq":10,"time":1785464656958,"data":{"turn":1,"step":1,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"call_depth_one_child","name":"subagent","arguments":"{\"description\":\"Start depth two\",\"prompt\":\"Attempt one subagent call beyond the configured cap, then report the rejection.\"}"}}}}
{"type":"assistant/chunk","seq":11,"time":1785487618150,"data":{"turn":1,"step":1,"chunk":{"type":"usage","usage":{"inputTokens":10,"outputTokens":5}}}}
{"type":"assistant/chunk","seq":12,"time":1785534718290,"data":{"turn":1,"step":1,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}}
{"type":"assistant/message","seq":13,"time":1785534718290,"data":{"turn":1,"step":1,"message":{"role":"assistant","content":[{"type":"tool-call","id":"call_depth_one_child","name":"subagent","arguments":"{\"description\":\"Start depth two\",\"prompt\":\"Attempt one subagent call beyond the configured cap, then report the rejection.\"}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"c39f921b-e161-4e43-ba17-7cf5d4c41074"},"usage":{"inputTokens":10,"outputTokens":5}},"sourceEventSeqs":[8,9,10,11,12],"surfaceOp":"append"}
{"type":"tool/call","seq":14,"time":1785534718290,"data":{"turn":1,"step":1,"callId":"call_depth_one_child","name":"subagent","arguments":"{\"description\":\"Start depth two\",\"prompt\":\"Attempt one subagent call beyond the configured cap, then report the rejection.\"}"}}
{"type":"tool/result","seq":15,"time":1785534718349,"data":{"turn":1,"step":1,"message":{"source":{"kind":"tool","callId":"call_depth_one_child"},"content":[{"type":"tool-result","toolCallId":"call_depth_one_child","content":[{"type":"text","text":"DEPTH_REJECTED"}],"isError":false}],"role":"user","id":"e101e96d-c1cc-4321-a4d9-f9f400a8c744"}},"sourceEventSeqs":[14],"surfaceOp":"append"}
{"type":"step/end","seq":16,"time":1785534718349,"data":{"turn":1,"step":1}}
{"type":"step/start","seq":17,"time":1785534718357,"data":{"turn":1,"step":2}}
{"type":"assistant/chunk","seq":18,"time":1784540790365,"data":{"turn":1,"step":2,"chunk":{"type":"block-start","index":0,"blockType":"text"}}}
{"type":"assistant/chunk","seq":19,"time":1784540790365,"data":{"turn":1,"step":2,"chunk":{"type":"text-delta","index":0,"text":"DEPTH_ONE_DONE"}}}
{"type":"assistant/chunk","seq":20,"time":1785464657024,"data":{"turn":1,"step":2,"chunk":{"type":"block-end","index":0,"block":{"type":"text","text":"DEPTH_ONE_DONE"}}}}
{"type":"assistant/chunk","seq":21,"time":1785487618220,"data":{"turn":1,"step":2,"chunk":{"type":"usage","usage":{"inputTokens":10,"outputTokens":2}}}}
{"type":"assistant/chunk","seq":22,"time":1785534718362,"data":{"turn":1,"step":2,"chunk":{"type":"finish","reason":{"kind":"stop"}}}}
{"type":"assistant/message","seq":23,"time":1785534718362,"data":{"turn":1,"step":2,"message":{"role":"assistant","content":[{"type":"text","text":"DEPTH_ONE_DONE"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"404f7c93-114c-4802-8659-4580946fb70c"},"usage":{"inputTokens":10,"outputTokens":2}},"sourceEventSeqs":[18,19,20,21,22],"surfaceOp":"append"}
{"type":"step/end","seq":24,"time":1785534718362,"data":{"turn":1,"step":2}}
{"type":"turn/end","seq":25,"time":1785534718362,"data":{"turn":1,"reason":{"kind":"completed"}}}

View File

@@ -1,26 +1,27 @@
{"type":"session","version":0,"id":"33333333-3333-4333-8333-333333333333","createdAt":1002,"cwd":"{{cwd}}","parentSession":"22222222-2222-4222-8222-222222222222","delegationDepth":2}
{"type":"turn/start","seq":0,"time":1784540790319,"data":{"turn":1,"trigger":{"kind":"message","source":{"kind":"user"}}}}
{"type":"user/message","seq":1,"time":1784540790319,"data":{"content":[{"type":"text","text":"Attempt one subagent call beyond the configured cap, then report the rejection."}],"source":{"kind":"user"},"role":"user","id":"916b15da-2125-4866-a864-18466808560d"},"surfaceOp":"append"}
{"type":"user/message","seq":1,"time":1784540790319,"data":{"content":[{"type":"text","text":"Attempt one subagent call beyond the configured cap, then report the rejection."}],"source":{"kind":"user"},"role":"user","id":"17f4fd95-842c-4c48-90d9-cdd04456687f"},"surfaceOp":"append"}
{"type":"session/title","seq":2,"time":1784540790319,"data":{"title":"Attempt one subagent call beyond","messageSeqs":[1],"source":{"kind":"fallback"}}}
{"type":"user/message","seq":3,"time":1785464656977,"data":{"content":[{"type":"text","text":"Current runtime context. This snapshot supersedes earlier runtime-context snapshots.\n\nCurrent DSH file policy: danger-full-access. The DSH file sandbox does not restrict file modifications by available operations.\n\nApproval prompts are disabled in this session: actions that require approval are rejected automatically — do not request sandbox escalation (do not set `sandbox_permissions`)."}],"source":{"kind":"plugin","plugin":"@deepseek-ai/dsh-system-prompt"},"role":"user","id":"de0aab9d-e7f9-46ff-9ca6-7980288d9b05"},"surfaceOp":"append"}
{"type":"step/start","seq":4,"time":1785464656977,"data":{"turn":1,"step":1}}
{"type":"request/header","seq":5,"time":1785464656978,"data":{"header":{"config":{"provider":"deepseek-official","model":"deepseek-v4-flash"},"system":"{{system}}","tools":"{{tools}}"},"reason":"initial"}}
{"type":"request/context","seq":6,"time":1785487618171,"data":{"provider":"deepseek-official","model":"deepseek-v4-flash"}}
{"type":"assistant/chunk","seq":7,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}}}
{"type":"assistant/chunk","seq":8,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":0,"id":"call_depth_three_rejected","name":"subagent","argumentsDelta":"{\"description\":\"Exceed depth cap\",\"prompt\":\"This child must never start.\"}"}}}
{"type":"assistant/chunk","seq":9,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"call_depth_three_rejected","name":"subagent","arguments":"{\"description\":\"Exceed depth cap\",\"prompt\":\"This child must never start.\"}"}}}}
{"type":"assistant/chunk","seq":10,"time":1785464656985,"data":{"turn":1,"step":1,"chunk":{"type":"usage","usage":{"inputTokens":10,"outputTokens":5}}}}
{"type":"assistant/chunk","seq":11,"time":1785487618180,"data":{"turn":1,"step":1,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}}
{"type":"assistant/message","seq":12,"time":1785487618180,"data":{"turn":1,"step":1,"message":{"role":"assistant","content":[{"type":"tool-call","id":"call_depth_three_rejected","name":"subagent","arguments":"{\"description\":\"Exceed depth cap\",\"prompt\":\"This child must never start.\"}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"139d69c1-0ac9-44d9-b099-2c878a759721"},"usage":{"inputTokens":10,"outputTokens":5}},"sourceEventSeqs":[7,8,9,10,11],"surfaceOp":"append"}
{"type":"tool/call","seq":13,"time":1785487618180,"data":{"turn":1,"step":1,"callId":"call_depth_three_rejected","name":"subagent","arguments":"{\"description\":\"Exceed depth cap\",\"prompt\":\"This child must never start.\"}"}}
{"type":"tool/result","seq":14,"time":1785487618187,"data":{"turn":1,"step":1,"message":{"source":{"kind":"tool","callId":"call_depth_three_rejected"},"content":[{"type":"tool-result","toolCallId":"call_depth_three_rejected","content":[{"type":"text","text":"Error: subagent depth 3 exceeds maxDepth 2"}],"isError":true}],"role":"user","id":"8a664b9a-b66a-43ab-b52d-9655c8286b35"}},"sourceEventSeqs":[13],"surfaceOp":"append"}
{"type":"step/end","seq":15,"time":1785487618187,"data":{"turn":1,"step":1}}
{"type":"step/start","seq":16,"time":1785487618195,"data":{"turn":1,"step":2}}
{"type":"assistant/chunk","seq":17,"time":1784540790339,"data":{"turn":1,"step":2,"chunk":{"type":"block-start","index":0,"blockType":"text"}}}
{"type":"assistant/chunk","seq":18,"time":1784540790339,"data":{"turn":1,"step":2,"chunk":{"type":"text-delta","index":0,"text":"DEPTH_REJECTED"}}}
{"type":"assistant/chunk","seq":19,"time":1784540790339,"data":{"turn":1,"step":2,"chunk":{"type":"block-end","index":0,"block":{"type":"text","text":"DEPTH_REJECTED"}}}}
{"type":"assistant/chunk","seq":20,"time":1785464657004,"data":{"turn":1,"step":2,"chunk":{"type":"usage","usage":{"inputTokens":10,"outputTokens":2}}}}
{"type":"assistant/chunk","seq":21,"time":1785487618200,"data":{"turn":1,"step":2,"chunk":{"type":"finish","reason":{"kind":"stop"}}}}
{"type":"assistant/message","seq":22,"time":1785487618200,"data":{"turn":1,"step":2,"message":{"role":"assistant","content":[{"type":"text","text":"DEPTH_REJECTED"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"78f76171-f8f5-4e01-87b8-26a9f68129db"},"usage":{"inputTokens":10,"outputTokens":2}},"sourceEventSeqs":[17,18,19,20,21],"surfaceOp":"append"}
{"type":"step/end","seq":23,"time":1785487618200,"data":{"turn":1,"step":2}}
{"type":"turn/end","seq":24,"time":1785487618200,"data":{"turn":1,"reason":{"kind":"completed"}}}
{"type":"subagent/descriptor","seq":3,"time":1785534718311,"data":{"version":2,"mode":"one-shot","provider":"spawn","label":"Start depth two"}}
{"type":"user/message","seq":4,"time":1785534718311,"data":{"content":[{"type":"text","text":"Current runtime context. This snapshot supersedes earlier runtime-context snapshots.\n\nCurrent DSH file policy: danger-full-access. The DSH file sandbox does not restrict file modifications by available operations.\n\nApproval prompts are disabled in this session: actions that require approval are rejected automatically — do not request sandbox escalation (do not set `sandbox_permissions`)."}],"source":{"kind":"plugin","plugin":"@deepseek-ai/dsh-system-prompt"},"role":"user","id":"6b793864-979d-4962-b641-5bb6439afcd8"},"surfaceOp":"append"}
{"type":"step/start","seq":5,"time":1785534718311,"data":{"turn":1,"step":1}}
{"type":"request/header","seq":6,"time":1785534718311,"data":{"header":{"config":{"provider":"deepseek-official","model":"deepseek-v4-flash"},"system":"{{system}}","tools":"{{tools}}"},"reason":"initial"}}
{"type":"request/context","seq":7,"time":1785534718311,"data":{"provider":"deepseek-official","model":"deepseek-v4-flash"}}
{"type":"assistant/chunk","seq":8,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}}}
{"type":"assistant/chunk","seq":9,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":0,"id":"call_depth_three_rejected","name":"subagent","argumentsDelta":"{\"description\":\"Exceed depth cap\",\"prompt\":\"This child must never start.\"}"}}}
{"type":"assistant/chunk","seq":10,"time":1785464656985,"data":{"turn":1,"step":1,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"call_depth_three_rejected","name":"subagent","arguments":"{\"description\":\"Exceed depth cap\",\"prompt\":\"This child must never start.\"}"}}}}
{"type":"assistant/chunk","seq":11,"time":1785487618180,"data":{"turn":1,"step":1,"chunk":{"type":"usage","usage":{"inputTokens":10,"outputTokens":5}}}}
{"type":"assistant/chunk","seq":12,"time":1785534718320,"data":{"turn":1,"step":1,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}}
{"type":"assistant/message","seq":13,"time":1785534718320,"data":{"turn":1,"step":1,"message":{"role":"assistant","content":[{"type":"tool-call","id":"call_depth_three_rejected","name":"subagent","arguments":"{\"description\":\"Exceed depth cap\",\"prompt\":\"This child must never start.\"}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"cb36f38d-54b2-4730-acd1-063ae39da283"},"usage":{"inputTokens":10,"outputTokens":5}},"sourceEventSeqs":[8,9,10,11,12],"surfaceOp":"append"}
{"type":"tool/call","seq":14,"time":1785534718320,"data":{"turn":1,"step":1,"callId":"call_depth_three_rejected","name":"subagent","arguments":"{\"description\":\"Exceed depth cap\",\"prompt\":\"This child must never start.\"}"}}
{"type":"tool/result","seq":15,"time":1785534718328,"data":{"turn":1,"step":1,"message":{"source":{"kind":"tool","callId":"call_depth_three_rejected"},"content":[{"type":"tool-result","toolCallId":"call_depth_three_rejected","content":[{"type":"text","text":"Error: subagent depth 3 exceeds maxDepth 2"}],"isError":true}],"role":"user","id":"39af6fe9-28b8-4565-bce4-190e67750f8d"}},"sourceEventSeqs":[14],"surfaceOp":"append"}
{"type":"step/end","seq":16,"time":1785534718328,"data":{"turn":1,"step":1}}
{"type":"step/start","seq":17,"time":1785534718336,"data":{"turn":1,"step":2}}
{"type":"assistant/chunk","seq":18,"time":1784540790339,"data":{"turn":1,"step":2,"chunk":{"type":"block-start","index":0,"blockType":"text"}}}
{"type":"assistant/chunk","seq":19,"time":1784540790339,"data":{"turn":1,"step":2,"chunk":{"type":"text-delta","index":0,"text":"DEPTH_REJECTED"}}}
{"type":"assistant/chunk","seq":20,"time":1785464657004,"data":{"turn":1,"step":2,"chunk":{"type":"block-end","index":0,"block":{"type":"text","text":"DEPTH_REJECTED"}}}}
{"type":"assistant/chunk","seq":21,"time":1785487618200,"data":{"turn":1,"step":2,"chunk":{"type":"usage","usage":{"inputTokens":10,"outputTokens":2}}}}
{"type":"assistant/chunk","seq":22,"time":1785534718341,"data":{"turn":1,"step":2,"chunk":{"type":"finish","reason":{"kind":"stop"}}}}
{"type":"assistant/message","seq":23,"time":1785534718341,"data":{"turn":1,"step":2,"message":{"role":"assistant","content":[{"type":"text","text":"DEPTH_REJECTED"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"ac69b81e-1f3a-4218-b51a-5b4059bddab6"},"usage":{"inputTokens":10,"outputTokens":2}},"sourceEventSeqs":[18,19,20,21,22],"surfaceOp":"append"}
{"type":"step/end","seq":24,"time":1785534718341,"data":{"turn":1,"step":2}}
{"type":"turn/end","seq":25,"time":1785534718341,"data":{"turn":1,"reason":{"kind":"completed"}}}

View File

@@ -1,8 +1,8 @@
{"type":"session","version":0,"id":"ada8966c-9fa3-441b-8721-37ff1e795e6a","createdAt":1783352137161,"cwd":"{{cwd}}","parentSession":"96cf59c9-b347-48b9-b234-a5200913ad05","seedLength":40,"delegationDepth":1}
{"type":"turn/start","seq":0,"time":1783352134837,"data":{"turn":1,"trigger":{"kind":"message","source":{"kind":"user"}}}}
{"type":"user/message","seq":1,"time":1783352134838,"data":{"content":[{"type":"text","text":"Remember this fact for later: the project codeword is MARMALADE. Reply with the single word OK and stop. Do not use any tools."}],"source":{"kind":"user"},"role":"user","id":"a560cd00-2828-4856-bc51-b0e717ca0a5f"},"surfaceOp":"append"}
{"type":"user/message","seq":1,"time":1783352134838,"data":{"content":[{"type":"text","text":"Remember this fact for later: the project codeword is MARMALADE. Reply with the single word OK and stop. Do not use any tools."}],"source":{"kind":"user"},"role":"user","id":"181ef004-8be2-416c-a223-3ff7f99cc79c"},"surfaceOp":"append"}
{"type":"session/title","seq":2,"time":1783352134838,"data":{"title":"Remember this fact for later:","messageSeqs":[1],"source":{"kind":"fallback"}}}
{"type":"user/message","seq":3,"time":1785464655093,"data":{"content":[{"type":"text","text":"Current runtime context. This snapshot supersedes earlier runtime-context snapshots.\n\nCurrent DSH file policy: danger-full-access. The DSH file sandbox does not restrict file modifications by available operations.\n\nApproval prompts are disabled in this session: actions that require approval are rejected automatically — do not request sandbox escalation (do not set `sandbox_permissions`)."}],"source":{"kind":"plugin","plugin":"@deepseek-ai/dsh-system-prompt"},"role":"user","id":"99b33ab5-8578-4ced-bc7e-ed0f0f9d125c"},"surfaceOp":"append"}
{"type":"user/message","seq":3,"time":1785464655093,"data":{"content":[{"type":"text","text":"Current runtime context. This snapshot supersedes earlier runtime-context snapshots.\n\nCurrent DSH file policy: danger-full-access. The DSH file sandbox does not restrict file modifications by available operations.\n\nApproval prompts are disabled in this session: actions that require approval are rejected automatically — do not request sandbox escalation (do not set `sandbox_permissions`)."}],"source":{"kind":"plugin","plugin":"@deepseek-ai/dsh-system-prompt"},"role":"user","id":"33079cbe-843c-4c20-9dbe-4d7052238458"},"surfaceOp":"append"}
{"type":"step/start","seq":4,"time":1785464655093,"data":{"turn":1,"step":1}}
{"type":"request/header","seq":5,"time":1785464655093,"data":{"header":{"config":{"provider":"deepseek-official","model":"deepseek-v4-flash"},"system":"{{system}}","tools":"{{tools}}"},"reason":"initial"}}
{"type":"request/context","seq":6,"time":1785487615348,"data":{"provider":"deepseek-official","model":"deepseek-v4-flash"}}
@@ -14,22 +14,23 @@
{"type":"assistant/chunk","seq":34,"time":1783352135771,"data":{"turn":1,"step":1,"chunk":{"type":"block-end","index":1,"block":{"type":"text","text":"OK"}}}}
{"type":"assistant/chunk","seq":35,"time":1785464655104,"data":{"turn":1,"step":1,"chunk":{"type":"usage","usage":{"inputTokens":2885,"outputTokens":25,"cacheReadTokens":0,"reasoningTokens":23}}}}
{"type":"assistant/chunk","seq":36,"time":1785487615358,"data":{"turn":1,"step":1,"chunk":{"type":"finish","reason":{"kind":"stop"}}}}
{"type":"assistant/message","seq":37,"time":1785487615358,"data":{"turn":1,"step":1,"message":{"role":"assistant","content":[{"type":"reasoning","text":"The user wants me to remember the codeword \"MARMALADE\" and reply with just \"OK\"."},{"type":"text","text":"OK"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"03ede8a3-a816-4e2f-9e44-b232665058e7"},"usage":{"inputTokens":2885,"outputTokens":25,"cacheReadTokens":0,"reasoningTokens":23}},"sourceEventSeqs":[7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36],"surfaceOp":"append"}
{"type":"assistant/message","seq":37,"time":1785487615358,"data":{"turn":1,"step":1,"message":{"role":"assistant","content":[{"type":"reasoning","text":"The user wants me to remember the codeword \"MARMALADE\" and reply with just \"OK\"."},{"type":"text","text":"OK"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"964d6e93-7b80-419e-bb25-b2c11dfef18e"},"usage":{"inputTokens":2885,"outputTokens":25,"cacheReadTokens":0,"reasoningTokens":23}},"sourceEventSeqs":[7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36],"surfaceOp":"append"}
{"type":"step/end","seq":38,"time":1785487615358,"data":{"turn":1,"step":1}}
{"type":"turn/end","seq":39,"time":1785487615359,"data":{"turn":1,"reason":{"kind":"completed"}}}
{"type":"session/end-seed","seq":40,"time":1785487615386,"data":{}}
{"type":"turn/start","seq":41,"time":1785487615387,"data":{"turn":2,"trigger":{"kind":"message","source":{"kind":"user"}}}}
{"type":"user/message","seq":42,"time":1785487615387,"data":{"content":[{"type":"text","text":"What is the project codeword mentioned earlier in this conversation? Reply with exactly that one word and nothing else."}],"source":{"kind":"user"},"role":"user","id":"3c4bcb1e-1217-406b-b1d9-ae6963e0330e"},"surfaceOp":"append"}
{"type":"step/start","seq":43,"time":1785487615404,"data":{"turn":2,"step":1}}
{"type":"request/header","seq":44,"time":1785487615404,"data":{"header":{"config":{"provider":"deepseek-official","model":"deepseek-v4-flash"},"system":"{{system}}","tools":"{{tools}}"},"reason":"resume"}}
{"type":"assistant/chunk","seq":45,"time":1783352137989,"data":{"turn":2,"step":1,"chunk":{"type":"block-start","index":0,"blockType":"reasoning"}}}
{"type":"reasoning-chunks","seq0":46,"time0":1783352138020,"data":{"turn":2,"step":1,"index":0,"dt":[26,0,0,0,28,1,0,0,0,0,28,0,0,0,0,28,28,1,0,0,28,0,0,29,0,0,28,1,28,1,0,0,0],"texts":["The"," user"," asked"," me"," to"," remember"," the"," project"," cod","ew","ord"," \"","M","ARM","AL","ADE","\""," and"," now"," they","'re"," asking"," what"," it"," is","."," I"," should"," just"," reply"," with"," that"," word","."]}}
{"type":"assistant/chunk","seq":80,"time":1783352138275,"data":{"turn":2,"step":1,"chunk":{"type":"block-start","index":1,"blockType":"text"}}}
{"type":"text-chunks","seq0":81,"time0":1783352138305,"data":{"turn":2,"step":1,"index":1,"dt":[2,0,0],"texts":["M","ARM","AL","ADE"]}}
{"type":"assistant/chunk","seq":85,"time":1785142305270,"data":{"turn":2,"step":1,"chunk":{"type":"block-end","index":0,"block":{"type":"reasoning","text":"The user asked me to remember the project codeword \"MARMALADE\" and now they're asking what it is. I should just reply with that word."}}}}
{"type":"assistant/chunk","seq":86,"time":1785381572250,"data":{"turn":2,"step":1,"chunk":{"type":"block-end","index":1,"block":{"type":"text","text":"MARMALADE"}}}}
{"type":"assistant/chunk","seq":87,"time":1785464655160,"data":{"turn":2,"step":1,"chunk":{"type":"usage","usage":{"inputTokens":97,"outputTokens":39,"cacheReadTokens":2816,"reasoningTokens":34}}}}
{"type":"assistant/chunk","seq":88,"time":1785487615414,"data":{"turn":2,"step":1,"chunk":{"type":"finish","reason":{"kind":"stop"}}}}
{"type":"assistant/message","seq":89,"time":1785487615414,"data":{"turn":2,"step":1,"message":{"role":"assistant","content":[{"type":"reasoning","text":"The user asked me to remember the project codeword \"MARMALADE\" and now they're asking what it is. I should just reply with that word."},{"type":"text","text":"MARMALADE"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"e661d5ba-5d1a-444a-990a-e0cfdf97c61f"},"usage":{"inputTokens":97,"outputTokens":39,"cacheReadTokens":2816,"reasoningTokens":34}},"sourceEventSeqs":[45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88],"surfaceOp":"append"}
{"type":"step/end","seq":90,"time":1785487615415,"data":{"turn":2,"step":1}}
{"type":"turn/end","seq":91,"time":1785487615415,"data":{"turn":2,"reason":{"kind":"completed"}}}
{"type":"user/message","seq":42,"time":1785487615387,"data":{"content":[{"type":"text","text":"What is the project codeword mentioned earlier in this conversation? Reply with exactly that one word and nothing else."}],"source":{"kind":"user"},"role":"user","id":"162a8354-c69d-410c-8d04-8408724a5ac6"},"surfaceOp":"append"}
{"type":"subagent/descriptor","seq":43,"time":1785534713921,"data":{"version":2,"mode":"one-shot","provider":"fork","label":"Recall project codeword"}}
{"type":"step/start","seq":44,"time":1785534713921,"data":{"turn":2,"step":1}}
{"type":"request/header","seq":45,"time":1785534713922,"data":{"header":{"config":{"provider":"deepseek-official","model":"deepseek-v4-flash"},"system":"{{system}}","tools":"{{tools}}"},"reason":"resume"}}
{"type":"assistant/chunk","seq":46,"time":1783352138020,"data":{"turn":2,"step":1,"chunk":{"type":"block-start","index":0,"blockType":"reasoning"}}}
{"type":"reasoning-chunks","seq0":47,"time0":1783352138046,"data":{"turn":2,"step":1,"index":0,"dt":[0,0,0,28,1,0,0,0,0,28,0,0,0,0,28,28,1,0,0,28,0,0,29,0,0,28,1,28,1,0,0,0,0],"texts":["The"," user"," asked"," me"," to"," remember"," the"," project"," cod","ew","ord"," \"","M","ARM","AL","ADE","\""," and"," now"," they","'re"," asking"," what"," it"," is","."," I"," should"," just"," reply"," with"," that"," word","."]}}
{"type":"assistant/chunk","seq":81,"time":1783352138305,"data":{"turn":2,"step":1,"chunk":{"type":"block-start","index":1,"blockType":"text"}}}
{"type":"text-chunks","seq0":82,"time0":1783352138307,"data":{"turn":2,"step":1,"index":1,"dt":[0,0,1790166963],"texts":["M","ARM","AL","ADE"]}}
{"type":"assistant/chunk","seq":86,"time":1785381572250,"data":{"turn":2,"step":1,"chunk":{"type":"block-end","index":0,"block":{"type":"reasoning","text":"The user asked me to remember the project codeword \"MARMALADE\" and now they're asking what it is. I should just reply with that word."}}}}
{"type":"assistant/chunk","seq":87,"time":1785464655160,"data":{"turn":2,"step":1,"chunk":{"type":"block-end","index":1,"block":{"type":"text","text":"MARMALADE"}}}}
{"type":"assistant/chunk","seq":88,"time":1785487615414,"data":{"turn":2,"step":1,"chunk":{"type":"usage","usage":{"inputTokens":97,"outputTokens":39,"cacheReadTokens":2816,"reasoningTokens":34}}}}
{"type":"assistant/chunk","seq":89,"time":1785534713931,"data":{"turn":2,"step":1,"chunk":{"type":"finish","reason":{"kind":"stop"}}}}
{"type":"assistant/message","seq":90,"time":1785534713931,"data":{"turn":2,"step":1,"message":{"role":"assistant","content":[{"type":"reasoning","text":"The user asked me to remember the project codeword \"MARMALADE\" and now they're asking what it is. I should just reply with that word."},{"type":"text","text":"MARMALADE"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"2bbeb2ce-5f83-44e4-8497-efc539c5f5ff"},"usage":{"inputTokens":97,"outputTokens":39,"cacheReadTokens":2816,"reasoningTokens":34}},"sourceEventSeqs":[46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89],"surfaceOp":"append"}
{"type":"step/end","seq":91,"time":1785534713931,"data":{"turn":2,"step":1}}
{"type":"turn/end","seq":92,"time":1785534713932,"data":{"turn":2,"reason":{"kind":"completed"}}}

View File

@@ -1,5 +1,5 @@
{"type":"session","version":0,"id":"33333333-3333-4333-8333-333333333333","createdAt":1789000001000,"cwd":"{{cwd}}","parentSession":"11111111-1111-4111-8111-111111111111","delegationDepth":1}
{"type":"subagent/descriptor","seq":0,"time":1785531795641,"data":{"version":1,"provider":"spawn","label":"Reply with CHILD_OK","agentProvider":"deepseek-official","agentModel":"deepseek-v4-flash"}}
{"type":"subagent/descriptor","seq":0,"time":1785531795641,"data":{"version":2,"mode":"continuable","provider":"spawn","label":"Reply with CHILD_OK","agentProvider":"deepseek-official","agentModel":"deepseek-v4-flash"}}
{"type":"session/end-seed","seq":1,"time":1785531795641,"data":{}}
{"type":"turn/start","seq":2,"time":1785531795641,"data":{"turn":1,"trigger":{"kind":"message","source":{"kind":"user"}}}}
{"type":"user/message","seq":3,"time":1785531795642,"data":{"content":[{"type":"text","text":"Reply with exactly the word CHILD_OK and nothing else."}],"source":{"kind":"user"},"role":"user","id":"33a98a9e-5402-4a7e-b919-05d179da1b84"},"surfaceOp":"append"}

View File

@@ -1,19 +1,20 @@
{"type":"session","version":0,"id":"e4aafa18-b9e3-48d0-8aae-6c9b25dcae80","createdAt":1783352145223,"cwd":"{{cwd}}","parentSession":"959ffdf5-03e2-465e-9482-009b704632dc","delegationDepth":1}
{"type":"turn/start","seq":0,"time":1783352145224,"data":{"turn":1,"trigger":{"kind":"message","source":{"kind":"user"}}}}
{"type":"user/message","seq":1,"time":1783352145224,"data":{"content":[{"type":"text","text":"Reply with exactly the word ALPHA and nothing else."}],"source":{"kind":"user"},"role":"user","id":"fb27f490-7305-4ba6-bcf6-84bc53b6f989"},"surfaceOp":"append"}
{"type":"user/message","seq":1,"time":1783352145224,"data":{"content":[{"type":"text","text":"Reply with exactly the word ALPHA and nothing else."}],"source":{"kind":"user"},"role":"user","id":"ad85672f-96fe-4462-a2c7-0411ab9c92c2"},"surfaceOp":"append"}
{"type":"session/title","seq":2,"time":1783352145224,"data":{"title":"Reply with exactly the word","messageSeqs":[1],"source":{"kind":"fallback"}}}
{"type":"user/message","seq":3,"time":1785464655997,"data":{"content":[{"type":"text","text":"Current runtime context. This snapshot supersedes earlier runtime-context snapshots.\n\nCurrent DSH file policy: danger-full-access. The DSH file sandbox does not restrict file modifications by available operations.\n\nApproval prompts are disabled in this session: actions that require approval are rejected automatically — do not request sandbox escalation (do not set `sandbox_permissions`)."}],"source":{"kind":"plugin","plugin":"@deepseek-ai/dsh-system-prompt"},"role":"user","id":"91c18155-2ffe-40ff-9d7c-c1fd5ac65edd"},"surfaceOp":"append"}
{"type":"step/start","seq":4,"time":1785464655997,"data":{"turn":1,"step":1}}
{"type":"request/header","seq":5,"time":1785464655998,"data":{"header":{"config":{"provider":"deepseek-official","model":"deepseek-v4-flash"},"system":"{{system}}","tools":"{{tools}}"},"reason":"initial"}}
{"type":"request/context","seq":6,"time":1785487616770,"data":{"provider":"deepseek-official","model":"deepseek-v4-flash"}}
{"type":"assistant/chunk","seq":7,"time":1783352145985,"data":{"turn":1,"step":1,"chunk":{"type":"block-start","index":0,"blockType":"reasoning"}}}
{"type":"reasoning-chunks","seq0":8,"time0":1783352146014,"data":{"turn":1,"step":1,"index":0,"dt":[28,1,0,0,0,28,0,0,0,0,0,29,0,0,0,0,29,0],"texts":["The"," user"," asked"," me"," to"," reply"," with"," exactly"," the"," word"," \"","AL","P","HA","\""," and"," nothing"," else","."]}}
{"type":"assistant/chunk","seq":27,"time":1783352146129,"data":{"turn":1,"step":1,"chunk":{"type":"block-start","index":1,"blockType":"text"}}}
{"type":"text-chunks","seq0":28,"time0":1783352146129,"data":{"turn":1,"step":1,"index":1,"dt":[0,0],"texts":["AL","P","HA"]}}
{"type":"assistant/chunk","seq":31,"time":1783352146129,"data":{"turn":1,"step":1,"chunk":{"type":"block-end","index":0,"block":{"type":"reasoning","text":"The user asked me to reply with exactly the word \"ALPHA\" and nothing else."}}}}
{"type":"assistant/chunk","seq":32,"time":1783352146129,"data":{"turn":1,"step":1,"chunk":{"type":"block-end","index":1,"block":{"type":"text","text":"ALPHA"}}}}
{"type":"assistant/chunk","seq":33,"time":1785464656007,"data":{"turn":1,"step":1,"chunk":{"type":"usage","usage":{"inputTokens":48,"outputTokens":23,"cacheReadTokens":2816,"reasoningTokens":19}}}}
{"type":"assistant/chunk","seq":34,"time":1785487616779,"data":{"turn":1,"step":1,"chunk":{"type":"finish","reason":{"kind":"stop"}}}}
{"type":"assistant/message","seq":35,"time":1785487616779,"data":{"turn":1,"step":1,"message":{"role":"assistant","content":[{"type":"reasoning","text":"The user asked me to reply with exactly the word \"ALPHA\" and nothing else."},{"type":"text","text":"ALPHA"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"fde3885f-df8c-4f43-be69-bd3e2567d9c1"},"usage":{"inputTokens":48,"outputTokens":23,"cacheReadTokens":2816,"reasoningTokens":19}},"sourceEventSeqs":[7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34],"surfaceOp":"append"}
{"type":"step/end","seq":36,"time":1785487616779,"data":{"turn":1,"step":1}}
{"type":"turn/end","seq":37,"time":1785487616779,"data":{"turn":1,"reason":{"kind":"completed"}}}
{"type":"subagent/descriptor","seq":3,"time":1785534714983,"data":{"version":2,"mode":"one-shot","provider":"spawn","label":"Reply ALPHA only"}}
{"type":"user/message","seq":4,"time":1785534714983,"data":{"content":[{"type":"text","text":"Current runtime context. This snapshot supersedes earlier runtime-context snapshots.\n\nCurrent DSH file policy: danger-full-access. The DSH file sandbox does not restrict file modifications by available operations.\n\nApproval prompts are disabled in this session: actions that require approval are rejected automatically — do not request sandbox escalation (do not set `sandbox_permissions`)."}],"source":{"kind":"plugin","plugin":"@deepseek-ai/dsh-system-prompt"},"role":"user","id":"fbb2d7af-c195-4726-9f67-2dcb19d35e36"},"surfaceOp":"append"}
{"type":"step/start","seq":5,"time":1785534714984,"data":{"turn":1,"step":1}}
{"type":"request/header","seq":6,"time":1785534714984,"data":{"header":{"config":{"provider":"deepseek-official","model":"deepseek-v4-flash"},"system":"{{system}}","tools":"{{tools}}"},"reason":"initial"}}
{"type":"request/context","seq":7,"time":1785534714984,"data":{"provider":"deepseek-official","model":"deepseek-v4-flash"}}
{"type":"assistant/chunk","seq":8,"time":1783352146014,"data":{"turn":1,"step":1,"chunk":{"type":"block-start","index":0,"blockType":"reasoning"}}}
{"type":"reasoning-chunks","seq0":9,"time0":1783352146042,"data":{"turn":1,"step":1,"index":0,"dt":[1,0,0,0,28,0,0,0,0,0,29,0,0,0,0,29,0,0],"texts":["The"," user"," asked"," me"," to"," reply"," with"," exactly"," the"," word"," \"","AL","P","HA","\""," and"," nothing"," else","."]}}
{"type":"assistant/chunk","seq":28,"time":1783352146129,"data":{"turn":1,"step":1,"chunk":{"type":"block-start","index":1,"blockType":"text"}}}
{"type":"text-chunks","seq0":29,"time0":1783352146129,"data":{"turn":1,"step":1,"index":1,"dt":[0,0],"texts":["AL","P","HA"]}}
{"type":"assistant/chunk","seq":32,"time":1783352146129,"data":{"turn":1,"step":1,"chunk":{"type":"block-end","index":0,"block":{"type":"reasoning","text":"The user asked me to reply with exactly the word \"ALPHA\" and nothing else."}}}}
{"type":"assistant/chunk","seq":33,"time":1785464656007,"data":{"turn":1,"step":1,"chunk":{"type":"block-end","index":1,"block":{"type":"text","text":"ALPHA"}}}}
{"type":"assistant/chunk","seq":34,"time":1785487616779,"data":{"turn":1,"step":1,"chunk":{"type":"usage","usage":{"inputTokens":48,"outputTokens":23,"cacheReadTokens":2816,"reasoningTokens":19}}}}
{"type":"assistant/chunk","seq":35,"time":1785534714992,"data":{"turn":1,"step":1,"chunk":{"type":"finish","reason":{"kind":"stop"}}}}
{"type":"assistant/message","seq":36,"time":1785534714992,"data":{"turn":1,"step":1,"message":{"role":"assistant","content":[{"type":"reasoning","text":"The user asked me to reply with exactly the word \"ALPHA\" and nothing else."},{"type":"text","text":"ALPHA"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"f8fb22e3-f730-4aad-ab39-ca3d57fedc8d"},"usage":{"inputTokens":48,"outputTokens":23,"cacheReadTokens":2816,"reasoningTokens":19}},"sourceEventSeqs":[8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35],"surfaceOp":"append"}
{"type":"step/end","seq":37,"time":1785534714992,"data":{"turn":1,"step":1}}
{"type":"turn/end","seq":38,"time":1785534714992,"data":{"turn":1,"reason":{"kind":"completed"}}}

View File

@@ -1,8 +1,8 @@
{"type":"session","version":0,"id":"02b3a8dd-1d5e-4866-825f-5fbf5000a632","createdAt":1783352147504,"cwd":"{{cwd}}","parentSession":"959ffdf5-03e2-465e-9482-009b704632dc","seedLength":34,"delegationDepth":1}
{"type":"turn/start","seq":0,"time":1783352142834,"data":{"turn":1,"trigger":{"kind":"message","source":{"kind":"user"}}}}
{"type":"user/message","seq":1,"time":1783352142834,"data":{"content":[{"type":"text","text":"Remember this fact for later: the project codeword is SAFFRON. Reply with the single word OK and stop. Do not use any tools."}],"source":{"kind":"user"},"role":"user","id":"f2c31c03-e865-411b-a2be-d9592f5c2583"},"surfaceOp":"append"}
{"type":"user/message","seq":1,"time":1783352142834,"data":{"content":[{"type":"text","text":"Remember this fact for later: the project codeword is SAFFRON. Reply with the single word OK and stop. Do not use any tools."}],"source":{"kind":"user"},"role":"user","id":"7d598287-d208-4f2d-a97b-66f397094c6c"},"surfaceOp":"append"}
{"type":"session/title","seq":2,"time":1783352142834,"data":{"title":"Remember this fact for later:","messageSeqs":[1],"source":{"kind":"fallback"}}}
{"type":"user/message","seq":3,"time":1785464655947,"data":{"content":[{"type":"text","text":"Current runtime context. This snapshot supersedes earlier runtime-context snapshots.\n\nCurrent DSH file policy: danger-full-access. The DSH file sandbox does not restrict file modifications by available operations.\n\nApproval prompts are disabled in this session: actions that require approval are rejected automatically — do not request sandbox escalation (do not set `sandbox_permissions`)."}],"source":{"kind":"plugin","plugin":"@deepseek-ai/dsh-system-prompt"},"role":"user","id":"a8d3db5a-1cca-4055-bda7-bb91fe671181"},"surfaceOp":"append"}
{"type":"user/message","seq":3,"time":1785464655947,"data":{"content":[{"type":"text","text":"Current runtime context. This snapshot supersedes earlier runtime-context snapshots.\n\nCurrent DSH file policy: danger-full-access. The DSH file sandbox does not restrict file modifications by available operations.\n\nApproval prompts are disabled in this session: actions that require approval are rejected automatically — do not request sandbox escalation (do not set `sandbox_permissions`)."}],"source":{"kind":"plugin","plugin":"@deepseek-ai/dsh-system-prompt"},"role":"user","id":"cea0bf45-1a24-4b9f-a55c-6c60b66df83c"},"surfaceOp":"append"}
{"type":"step/start","seq":4,"time":1785464655947,"data":{"turn":1,"step":1}}
{"type":"request/header","seq":5,"time":1785464655948,"data":{"header":{"config":{"provider":"deepseek-official","model":"deepseek-v4-flash"},"system":"{{system}}","tools":"{{tools}}"},"reason":"initial"}}
{"type":"request/context","seq":6,"time":1785487616723,"data":{"provider":"deepseek-official","model":"deepseek-v4-flash"}}
@@ -14,22 +14,23 @@
{"type":"assistant/chunk","seq":28,"time":1783352143768,"data":{"turn":1,"step":1,"chunk":{"type":"block-end","index":1,"block":{"type":"text","text":"OK"}}}}
{"type":"assistant/chunk","seq":29,"time":1785464655958,"data":{"turn":1,"step":1,"chunk":{"type":"usage","usage":{"inputTokens":2883,"outputTokens":19,"cacheReadTokens":0,"reasoningTokens":17}}}}
{"type":"assistant/chunk","seq":30,"time":1785487616733,"data":{"turn":1,"step":1,"chunk":{"type":"finish","reason":{"kind":"stop"}}}}
{"type":"assistant/message","seq":31,"time":1785487616733,"data":{"turn":1,"step":1,"message":{"role":"assistant","content":[{"type":"reasoning","text":"The user wants me to remember a codeword and just reply with \"OK\"."},{"type":"text","text":"OK"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"f2c52abb-08c0-4cef-86a5-da9e4baad065"},"usage":{"inputTokens":2883,"outputTokens":19,"cacheReadTokens":0,"reasoningTokens":17}},"sourceEventSeqs":[7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30],"surfaceOp":"append"}
{"type":"assistant/message","seq":31,"time":1785487616733,"data":{"turn":1,"step":1,"message":{"role":"assistant","content":[{"type":"reasoning","text":"The user wants me to remember a codeword and just reply with \"OK\"."},{"type":"text","text":"OK"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"b135fc45-d8bc-4dee-b76a-fca657c964f4"},"usage":{"inputTokens":2883,"outputTokens":19,"cacheReadTokens":0,"reasoningTokens":17}},"sourceEventSeqs":[7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30],"surfaceOp":"append"}
{"type":"step/end","seq":32,"time":1785487616733,"data":{"turn":1,"step":1}}
{"type":"turn/end","seq":33,"time":1785487616733,"data":{"turn":1,"reason":{"kind":"completed"}}}
{"type":"session/end-seed","seq":34,"time":1785487616816,"data":{}}
{"type":"turn/start","seq":35,"time":1785487616817,"data":{"turn":2,"trigger":{"kind":"message","source":{"kind":"user"}}}}
{"type":"user/message","seq":36,"time":1785487616817,"data":{"content":[{"type":"text","text":"What is the project codeword mentioned earlier in this conversation? Reply with exactly that one word and nothing else."}],"source":{"kind":"user"},"role":"user","id":"57d1af20-46cc-474a-9a10-0510b7103e8d"},"surfaceOp":"append"}
{"type":"step/start","seq":37,"time":1785487616833,"data":{"turn":2,"step":1}}
{"type":"request/header","seq":38,"time":1785487616833,"data":{"header":{"config":{"provider":"deepseek-official","model":"deepseek-v4-flash"},"system":"{{system}}","tools":"{{tools}}"},"reason":"resume"}}
{"type":"assistant/chunk","seq":39,"time":1783352148048,"data":{"turn":2,"step":1,"chunk":{"type":"block-start","index":0,"blockType":"reasoning"}}}
{"type":"reasoning-chunks","seq0":40,"time0":1783352148049,"data":{"turn":2,"step":1,"index":0,"dt":[0,27,0,1,0,0,0,29,0,0,0,35,0,0,0,0,26,29,31,0,30,0,0,27,1,27,0,1,0,0],"texts":["The"," user"," is"," asking"," me"," to"," recall"," the"," project"," cod","ew","ord"," that"," was"," mentioned"," earlier"," in"," the"," conversation","."," I"," was"," told"," to"," remember"," it",":"," SA","FF","RON","."]}}
{"type":"assistant/chunk","seq":71,"time":1783352148344,"data":{"turn":2,"step":1,"chunk":{"type":"block-start","index":1,"blockType":"text"}}}
{"type":"text-chunks","seq0":72,"time0":1783352148345,"data":{"turn":2,"step":1,"index":1,"dt":[0,0],"texts":["SA","FF","RON"]}}
{"type":"assistant/chunk","seq":75,"time":1785142306309,"data":{"turn":2,"step":1,"chunk":{"type":"block-end","index":0,"block":{"type":"reasoning","text":"The user is asking me to recall the project codeword that was mentioned earlier in the conversation. I was told to remember it: SAFFRON."}}}}
{"type":"assistant/chunk","seq":76,"time":1785381573552,"data":{"turn":2,"step":1,"chunk":{"type":"block-end","index":1,"block":{"type":"text","text":"SAFFRON"}}}}
{"type":"assistant/chunk","seq":77,"time":1785464656072,"data":{"turn":2,"step":1,"chunk":{"type":"usage","usage":{"inputTokens":95,"outputTokens":35,"cacheReadTokens":2816,"reasoningTokens":31}}}}
{"type":"assistant/chunk","seq":78,"time":1785487616843,"data":{"turn":2,"step":1,"chunk":{"type":"finish","reason":{"kind":"stop"}}}}
{"type":"assistant/message","seq":79,"time":1785487616843,"data":{"turn":2,"step":1,"message":{"role":"assistant","content":[{"type":"reasoning","text":"The user is asking me to recall the project codeword that was mentioned earlier in the conversation. I was told to remember it: SAFFRON."},{"type":"text","text":"SAFFRON"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"665d18cd-24e5-4bc3-9563-39c8b92b1bea"},"usage":{"inputTokens":95,"outputTokens":35,"cacheReadTokens":2816,"reasoningTokens":31}},"sourceEventSeqs":[39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78],"surfaceOp":"append"}
{"type":"step/end","seq":80,"time":1785487616844,"data":{"turn":2,"step":1}}
{"type":"turn/end","seq":81,"time":1785487616844,"data":{"turn":2,"reason":{"kind":"completed"}}}
{"type":"user/message","seq":36,"time":1785487616817,"data":{"content":[{"type":"text","text":"What is the project codeword mentioned earlier in this conversation? Reply with exactly that one word and nothing else."}],"source":{"kind":"user"},"role":"user","id":"b4019060-df12-4a06-82d8-175c65b22b47"},"surfaceOp":"append"}
{"type":"subagent/descriptor","seq":37,"time":1785534715042,"data":{"version":2,"mode":"one-shot","provider":"fork","label":"Recall project codeword"}}
{"type":"step/start","seq":38,"time":1785534715042,"data":{"turn":2,"step":1}}
{"type":"request/header","seq":39,"time":1785534715043,"data":{"header":{"config":{"provider":"deepseek-official","model":"deepseek-v4-flash"},"system":"{{system}}","tools":"{{tools}}"},"reason":"resume"}}
{"type":"assistant/chunk","seq":40,"time":1783352148049,"data":{"turn":2,"step":1,"chunk":{"type":"block-start","index":0,"blockType":"reasoning"}}}
{"type":"reasoning-chunks","seq0":41,"time0":1783352148049,"data":{"turn":2,"step":1,"index":0,"dt":[27,0,1,0,0,0,29,0,0,0,35,0,0,0,0,26,29,31,0,30,0,0,27,1,27,0,1,0,0,31],"texts":["The"," user"," is"," asking"," me"," to"," recall"," the"," project"," cod","ew","ord"," that"," was"," mentioned"," earlier"," in"," the"," conversation","."," I"," was"," told"," to"," remember"," it",":"," SA","FF","RON","."]}}
{"type":"assistant/chunk","seq":72,"time":1783352148345,"data":{"turn":2,"step":1,"chunk":{"type":"block-start","index":1,"blockType":"text"}}}
{"type":"text-chunks","seq0":73,"time0":1783352148345,"data":{"turn":2,"step":1,"index":1,"dt":[0,1790157964],"texts":["SA","FF","RON"]}}
{"type":"assistant/chunk","seq":76,"time":1785381573552,"data":{"turn":2,"step":1,"chunk":{"type":"block-end","index":0,"block":{"type":"reasoning","text":"The user is asking me to recall the project codeword that was mentioned earlier in the conversation. I was told to remember it: SAFFRON."}}}}
{"type":"assistant/chunk","seq":77,"time":1785464656072,"data":{"turn":2,"step":1,"chunk":{"type":"block-end","index":1,"block":{"type":"text","text":"SAFFRON"}}}}
{"type":"assistant/chunk","seq":78,"time":1785487616843,"data":{"turn":2,"step":1,"chunk":{"type":"usage","usage":{"inputTokens":95,"outputTokens":35,"cacheReadTokens":2816,"reasoningTokens":31}}}}
{"type":"assistant/chunk","seq":79,"time":1785534715053,"data":{"turn":2,"step":1,"chunk":{"type":"finish","reason":{"kind":"stop"}}}}
{"type":"assistant/message","seq":80,"time":1785534715053,"data":{"turn":2,"step":1,"message":{"role":"assistant","content":[{"type":"reasoning","text":"The user is asking me to recall the project codeword that was mentioned earlier in the conversation. I was told to remember it: SAFFRON."},{"type":"text","text":"SAFFRON"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"67daf66d-964c-4133-aa33-e709b6806faf"},"usage":{"inputTokens":95,"outputTokens":35,"cacheReadTokens":2816,"reasoningTokens":31}},"sourceEventSeqs":[40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79],"surfaceOp":"append"}
{"type":"step/end","seq":81,"time":1785534715053,"data":{"turn":2,"step":1}}
{"type":"turn/end","seq":82,"time":1785534715053,"data":{"turn":2,"reason":{"kind":"completed"}}}

View File

@@ -1,19 +1,20 @@
{"type":"session","version":0,"id":"553f8e92-aac1-4df3-8657-eacbb58f9581","createdAt":1783352127669,"cwd":"{{cwd}}","parentSession":"14dda109-5728-45ba-a002-7db9543fe50e","delegationDepth":1}
{"type":"turn/start","seq":0,"time":1783352127670,"data":{"turn":1,"trigger":{"kind":"message","source":{"kind":"user"}}}}
{"type":"user/message","seq":1,"time":1783352127670,"data":{"content":[{"type":"text","text":"Reply with exactly the word ALPHA and nothing else."}],"source":{"kind":"user"},"role":"user","id":"471b4074-8d28-4b7f-9534-d976ce308bfb"},"surfaceOp":"append"}
{"type":"user/message","seq":1,"time":1783352127670,"data":{"content":[{"type":"text","text":"Reply with exactly the word ALPHA and nothing else."}],"source":{"kind":"user"},"role":"user","id":"78174c9e-c9f2-4c05-8dc4-12108419e6ad"},"surfaceOp":"append"}
{"type":"session/title","seq":2,"time":1783352127670,"data":{"title":"Reply with exactly the word","messageSeqs":[1],"source":{"kind":"fallback"}}}
{"type":"user/message","seq":3,"time":1785464654232,"data":{"content":[{"type":"text","text":"Current runtime context. This snapshot supersedes earlier runtime-context snapshots.\n\nCurrent DSH file policy: danger-full-access. The DSH file sandbox does not restrict file modifications by available operations.\n\nApproval prompts are disabled in this session: actions that require approval are rejected automatically — do not request sandbox escalation (do not set `sandbox_permissions`)."}],"source":{"kind":"plugin","plugin":"@deepseek-ai/dsh-system-prompt"},"role":"user","id":"f8f198c9-60cf-45dc-8491-18cf4a635960"},"surfaceOp":"append"}
{"type":"step/start","seq":4,"time":1785464654232,"data":{"turn":1,"step":1}}
{"type":"request/header","seq":5,"time":1785464654232,"data":{"header":{"config":{"provider":"deepseek-official","model":"deepseek-v4-flash"},"system":"{{system}}","tools":"{{tools}}"},"reason":"initial"}}
{"type":"request/context","seq":6,"time":1785487613979,"data":{"provider":"deepseek-official","model":"deepseek-v4-flash"}}
{"type":"assistant/chunk","seq":7,"time":1783352128240,"data":{"turn":1,"step":1,"chunk":{"type":"block-start","index":0,"blockType":"reasoning"}}}
{"type":"reasoning-chunks","seq0":8,"time0":1783352128280,"data":{"turn":1,"step":1,"index":0,"dt":[0,0,0,0,1,19,0,0,0,0,1,31,0,0,0,0,32,1],"texts":["The"," user"," wants"," me"," to"," reply"," with"," exactly"," the"," word"," \"","AL","P","HA","\""," and"," nothing"," else","."]}}
{"type":"assistant/chunk","seq":27,"time":1783352128365,"data":{"turn":1,"step":1,"chunk":{"type":"block-start","index":1,"blockType":"text"}}}
{"type":"text-chunks","seq0":28,"time0":1783352128365,"data":{"turn":1,"step":1,"index":1,"dt":[0,0],"texts":["AL","P","HA"]}}
{"type":"assistant/chunk","seq":31,"time":1783352128365,"data":{"turn":1,"step":1,"chunk":{"type":"block-end","index":0,"block":{"type":"reasoning","text":"The user wants me to reply with exactly the word \"ALPHA\" and nothing else."}}}}
{"type":"assistant/chunk","seq":32,"time":1783352128365,"data":{"turn":1,"step":1,"chunk":{"type":"block-end","index":1,"block":{"type":"text","text":"ALPHA"}}}}
{"type":"assistant/chunk","seq":33,"time":1785464654241,"data":{"turn":1,"step":1,"chunk":{"type":"usage","usage":{"inputTokens":49,"outputTokens":23,"cacheReadTokens":2816,"reasoningTokens":19}}}}
{"type":"assistant/chunk","seq":34,"time":1785487613988,"data":{"turn":1,"step":1,"chunk":{"type":"finish","reason":{"kind":"stop"}}}}
{"type":"assistant/message","seq":35,"time":1785487613988,"data":{"turn":1,"step":1,"message":{"role":"assistant","content":[{"type":"reasoning","text":"The user wants me to reply with exactly the word \"ALPHA\" and nothing else."},{"type":"text","text":"ALPHA"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"0301dbae-d9c2-4cdd-8888-7378c8facb25"},"usage":{"inputTokens":49,"outputTokens":23,"cacheReadTokens":2816,"reasoningTokens":19}},"sourceEventSeqs":[7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34],"surfaceOp":"append"}
{"type":"step/end","seq":36,"time":1785487613988,"data":{"turn":1,"step":1}}
{"type":"turn/end","seq":37,"time":1785487613988,"data":{"turn":1,"reason":{"kind":"completed"}}}
{"type":"subagent/descriptor","seq":3,"time":1785534712796,"data":{"version":2,"mode":"one-shot","provider":"spawn","label":"Return ALPHA only"}}
{"type":"user/message","seq":4,"time":1785534712796,"data":{"content":[{"type":"text","text":"Current runtime context. This snapshot supersedes earlier runtime-context snapshots.\n\nCurrent DSH file policy: danger-full-access. The DSH file sandbox does not restrict file modifications by available operations.\n\nApproval prompts are disabled in this session: actions that require approval are rejected automatically — do not request sandbox escalation (do not set `sandbox_permissions`)."}],"source":{"kind":"plugin","plugin":"@deepseek-ai/dsh-system-prompt"},"role":"user","id":"e0295965-aacd-4173-a0ad-551a1fd36cb0"},"surfaceOp":"append"}
{"type":"step/start","seq":5,"time":1785534712796,"data":{"turn":1,"step":1}}
{"type":"request/header","seq":6,"time":1785534712796,"data":{"header":{"config":{"provider":"deepseek-official","model":"deepseek-v4-flash"},"system":"{{system}}","tools":"{{tools}}"},"reason":"initial"}}
{"type":"request/context","seq":7,"time":1785534712797,"data":{"provider":"deepseek-official","model":"deepseek-v4-flash"}}
{"type":"assistant/chunk","seq":8,"time":1783352128280,"data":{"turn":1,"step":1,"chunk":{"type":"block-start","index":0,"blockType":"reasoning"}}}
{"type":"reasoning-chunks","seq0":9,"time0":1783352128280,"data":{"turn":1,"step":1,"index":0,"dt":[0,0,0,1,19,0,0,0,0,1,31,0,0,0,0,32,1,0],"texts":["The"," user"," wants"," me"," to"," reply"," with"," exactly"," the"," word"," \"","AL","P","HA","\""," and"," nothing"," else","."]}}
{"type":"assistant/chunk","seq":28,"time":1783352128365,"data":{"turn":1,"step":1,"chunk":{"type":"block-start","index":1,"blockType":"text"}}}
{"type":"text-chunks","seq0":29,"time0":1783352128365,"data":{"turn":1,"step":1,"index":1,"dt":[0,0],"texts":["AL","P","HA"]}}
{"type":"assistant/chunk","seq":32,"time":1783352128365,"data":{"turn":1,"step":1,"chunk":{"type":"block-end","index":0,"block":{"type":"reasoning","text":"The user wants me to reply with exactly the word \"ALPHA\" and nothing else."}}}}
{"type":"assistant/chunk","seq":33,"time":1785464654241,"data":{"turn":1,"step":1,"chunk":{"type":"block-end","index":1,"block":{"type":"text","text":"ALPHA"}}}}
{"type":"assistant/chunk","seq":34,"time":1785487613988,"data":{"turn":1,"step":1,"chunk":{"type":"usage","usage":{"inputTokens":49,"outputTokens":23,"cacheReadTokens":2816,"reasoningTokens":19}}}}
{"type":"assistant/chunk","seq":35,"time":1785534712805,"data":{"turn":1,"step":1,"chunk":{"type":"finish","reason":{"kind":"stop"}}}}
{"type":"assistant/message","seq":36,"time":1785534712805,"data":{"turn":1,"step":1,"message":{"role":"assistant","content":[{"type":"reasoning","text":"The user wants me to reply with exactly the word \"ALPHA\" and nothing else."},{"type":"text","text":"ALPHA"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"6bfa1fd4-4505-425e-ac8c-ab09c76995ea"},"usage":{"inputTokens":49,"outputTokens":23,"cacheReadTokens":2816,"reasoningTokens":19}},"sourceEventSeqs":[8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35],"surfaceOp":"append"}
{"type":"step/end","seq":37,"time":1785534712805,"data":{"turn":1,"step":1}}
{"type":"turn/end","seq":38,"time":1785534712805,"data":{"turn":1,"reason":{"kind":"completed"}}}

View File

@@ -1,20 +1,21 @@
{"type":"session","version":0,"id":"5f49e80c-16fc-42c7-a617-0b6bd0680aa3","createdAt":1783352129662,"cwd":"{{cwd}}","parentSession":"14dda109-5728-45ba-a002-7db9543fe50e","delegationDepth":1}
{"type":"turn/start","seq":0,"time":1783352129662,"data":{"turn":1,"trigger":{"kind":"message","source":{"kind":"user"}}}}
{"type":"user/message","seq":1,"time":1783352129662,"data":{"content":[{"type":"text","text":"Reply with exactly the word BETA and nothing else."}],"source":{"kind":"user"},"role":"user","id":"834c2ca6-672a-4ad6-b8f7-5830004fc1bd"},"surfaceOp":"append"}
{"type":"user/message","seq":1,"time":1783352129662,"data":{"content":[{"type":"text","text":"Reply with exactly the word BETA and nothing else."}],"source":{"kind":"user"},"role":"user","id":"9542fa06-afea-4f6d-9db1-b6848def4bc7"},"surfaceOp":"append"}
{"type":"session/title","seq":2,"time":1783352129662,"data":{"title":"Reply with exactly the word","messageSeqs":[1],"source":{"kind":"fallback"}}}
{"type":"user/message","seq":3,"time":1785464654289,"data":{"content":[{"type":"text","text":"Current runtime context. This snapshot supersedes earlier runtime-context snapshots.\n\nCurrent DSH file policy: danger-full-access. The DSH file sandbox does not restrict file modifications by available operations.\n\nApproval prompts are disabled in this session: actions that require approval are rejected automatically — do not request sandbox escalation (do not set `sandbox_permissions`)."}],"source":{"kind":"plugin","plugin":"@deepseek-ai/dsh-system-prompt"},"role":"user","id":"baf16355-f0cf-4c91-8967-5200fce9ed6d"},"surfaceOp":"append"}
{"type":"step/start","seq":4,"time":1785464654289,"data":{"turn":1,"step":1}}
{"type":"request/header","seq":5,"time":1785464654289,"data":{"header":{"config":{"provider":"deepseek-official","model":"deepseek-v4-flash"},"system":"{{system}}","tools":"{{tools}}"},"reason":"initial"}}
{"type":"request/context","seq":6,"time":1785487614032,"data":{"provider":"deepseek-official","model":"deepseek-v4-flash"}}
{"type":"assistant/chunk","seq":7,"time":1783352130375,"data":{"turn":1,"step":1,"chunk":{"type":"block-start","index":0,"blockType":"reasoning"}}}
{"type":"reasoning-chunks","seq0":8,"time0":1783352130413,"data":{"turn":1,"step":1,"index":0,"dt":[0,0,0,0,0,35,0,0,0,0,0,36,0,0,0,0,0],"texts":["The"," user"," wants"," me"," to"," reply"," with"," exactly"," the"," word"," \"","B","ETA","\""," and"," nothing"," else","."]}}
{"type":"assistant/chunk","seq":26,"time":1783352130527,"data":{"turn":1,"step":1,"chunk":{"type":"block-start","index":1,"blockType":"text"}}}
{"type":"assistant/chunk","seq":27,"time":1783352130527,"data":{"turn":1,"step":1,"chunk":{"type":"text-delta","index":1,"text":"B"}}}
{"type":"assistant/chunk","seq":28,"time":1783352130527,"data":{"turn":1,"step":1,"chunk":{"type":"text-delta","index":1,"text":"ETA"}}}
{"type":"assistant/chunk","seq":29,"time":1783352130527,"data":{"turn":1,"step":1,"chunk":{"type":"block-end","index":0,"block":{"type":"reasoning","text":"The user wants me to reply with exactly the word \"BETA\" and nothing else."}}}}
{"type":"assistant/chunk","seq":30,"time":1783352130527,"data":{"turn":1,"step":1,"chunk":{"type":"block-end","index":1,"block":{"type":"text","text":"BETA"}}}}
{"type":"assistant/chunk","seq":31,"time":1785464654297,"data":{"turn":1,"step":1,"chunk":{"type":"usage","usage":{"inputTokens":48,"outputTokens":21,"cacheReadTokens":2816,"reasoningTokens":18}}}}
{"type":"assistant/chunk","seq":32,"time":1785487614041,"data":{"turn":1,"step":1,"chunk":{"type":"finish","reason":{"kind":"stop"}}}}
{"type":"assistant/message","seq":33,"time":1785487614042,"data":{"turn":1,"step":1,"message":{"role":"assistant","content":[{"type":"reasoning","text":"The user wants me to reply with exactly the word \"BETA\" and nothing else."},{"type":"text","text":"BETA"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"da606b2a-17a7-48ee-b548-ca23457861b7"},"usage":{"inputTokens":48,"outputTokens":21,"cacheReadTokens":2816,"reasoningTokens":18}},"sourceEventSeqs":[7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32],"surfaceOp":"append"}
{"type":"step/end","seq":34,"time":1785487614042,"data":{"turn":1,"step":1}}
{"type":"turn/end","seq":35,"time":1785487614042,"data":{"turn":1,"reason":{"kind":"completed"}}}
{"type":"subagent/descriptor","seq":3,"time":1785534712851,"data":{"version":2,"mode":"one-shot","provider":"spawn","label":"Return BETA only"}}
{"type":"user/message","seq":4,"time":1785534712851,"data":{"content":[{"type":"text","text":"Current runtime context. This snapshot supersedes earlier runtime-context snapshots.\n\nCurrent DSH file policy: danger-full-access. The DSH file sandbox does not restrict file modifications by available operations.\n\nApproval prompts are disabled in this session: actions that require approval are rejected automatically — do not request sandbox escalation (do not set `sandbox_permissions`)."}],"source":{"kind":"plugin","plugin":"@deepseek-ai/dsh-system-prompt"},"role":"user","id":"bc269a2f-dda1-4536-9eec-a4982f443872"},"surfaceOp":"append"}
{"type":"step/start","seq":5,"time":1785534712851,"data":{"turn":1,"step":1}}
{"type":"request/header","seq":6,"time":1785534712851,"data":{"header":{"config":{"provider":"deepseek-official","model":"deepseek-v4-flash"},"system":"{{system}}","tools":"{{tools}}"},"reason":"initial"}}
{"type":"request/context","seq":7,"time":1785534712852,"data":{"provider":"deepseek-official","model":"deepseek-v4-flash"}}
{"type":"assistant/chunk","seq":8,"time":1783352130413,"data":{"turn":1,"step":1,"chunk":{"type":"block-start","index":0,"blockType":"reasoning"}}}
{"type":"reasoning-chunks","seq0":9,"time0":1783352130413,"data":{"turn":1,"step":1,"index":0,"dt":[0,0,0,0,35,0,0,0,0,0,36,0,0,0,0,0,43],"texts":["The"," user"," wants"," me"," to"," reply"," with"," exactly"," the"," word"," \"","B","ETA","\""," and"," nothing"," else","."]}}
{"type":"assistant/chunk","seq":27,"time":1783352130527,"data":{"turn":1,"step":1,"chunk":{"type":"block-start","index":1,"blockType":"text"}}}
{"type":"assistant/chunk","seq":28,"time":1783352130527,"data":{"turn":1,"step":1,"chunk":{"type":"text-delta","index":1,"text":"B"}}}
{"type":"assistant/chunk","seq":29,"time":1783352130527,"data":{"turn":1,"step":1,"chunk":{"type":"text-delta","index":1,"text":"ETA"}}}
{"type":"assistant/chunk","seq":30,"time":1783352130527,"data":{"turn":1,"step":1,"chunk":{"type":"block-end","index":0,"block":{"type":"reasoning","text":"The user wants me to reply with exactly the word \"BETA\" and nothing else."}}}}
{"type":"assistant/chunk","seq":31,"time":1785464654297,"data":{"turn":1,"step":1,"chunk":{"type":"block-end","index":1,"block":{"type":"text","text":"BETA"}}}}
{"type":"assistant/chunk","seq":32,"time":1785487614041,"data":{"turn":1,"step":1,"chunk":{"type":"usage","usage":{"inputTokens":48,"outputTokens":21,"cacheReadTokens":2816,"reasoningTokens":18}}}}
{"type":"assistant/chunk","seq":33,"time":1785534712861,"data":{"turn":1,"step":1,"chunk":{"type":"finish","reason":{"kind":"stop"}}}}
{"type":"assistant/message","seq":34,"time":1785534712861,"data":{"turn":1,"step":1,"message":{"role":"assistant","content":[{"type":"reasoning","text":"The user wants me to reply with exactly the word \"BETA\" and nothing else."},{"type":"text","text":"BETA"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"168875e0-29b7-4a9b-878e-f6090352b0a7"},"usage":{"inputTokens":48,"outputTokens":21,"cacheReadTokens":2816,"reasoningTokens":18}},"sourceEventSeqs":[8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33],"surfaceOp":"append"}
{"type":"step/end","seq":35,"time":1785534712861,"data":{"turn":1,"step":1}}
{"type":"turn/end","seq":36,"time":1785534712861,"data":{"turn":1,"reason":{"kind":"completed"}}}

View File

@@ -1,19 +1,20 @@
{"type":"session","version":0,"id":"ea339828-7885-42e1-9083-4355e6f1708d","createdAt":1783352120855,"cwd":"{{cwd}}","parentSession":"5138ed0d-e86e-4a7d-b75b-803307e92b17","delegationDepth":1}
{"type":"turn/start","seq":0,"time":1783352120856,"data":{"turn":1,"trigger":{"kind":"message","source":{"kind":"user"}}}}
{"type":"user/message","seq":1,"time":1783352120856,"data":{"content":[{"type":"text","text":"Reply with exactly the word CHILD_OK and nothing else."}],"source":{"kind":"user"},"role":"user","id":"affbb7de-4cce-4341-b21b-8f777b15992f"},"surfaceOp":"append"}
{"type":"user/message","seq":1,"time":1783352120856,"data":{"content":[{"type":"text","text":"Reply with exactly the word CHILD_OK and nothing else."}],"source":{"kind":"user"},"role":"user","id":"c7b8251e-05a6-48da-b7e1-00612d1679a4"},"surfaceOp":"append"}
{"type":"session/title","seq":2,"time":1783352120856,"data":{"title":"Reply with exactly the word","messageSeqs":[1],"source":{"kind":"fallback"}}}
{"type":"user/message","seq":3,"time":1785464653304,"data":{"content":[{"type":"text","text":"Current runtime context. This snapshot supersedes earlier runtime-context snapshots.\n\nCurrent DSH file policy: danger-full-access. The DSH file sandbox does not restrict file modifications by available operations.\n\nApproval prompts are disabled in this session: actions that require approval are rejected automatically — do not request sandbox escalation (do not set `sandbox_permissions`)."}],"source":{"kind":"plugin","plugin":"@deepseek-ai/dsh-system-prompt"},"role":"user","id":"2f0cc797-8b90-410d-909c-09026b61a2b2"},"surfaceOp":"append"}
{"type":"step/start","seq":4,"time":1785464653304,"data":{"turn":1,"step":1}}
{"type":"request/header","seq":5,"time":1785464653304,"data":{"header":{"config":{"provider":"deepseek-official","model":"deepseek-v4-flash"},"system":"{{system}}","tools":"{{tools}}"},"reason":"initial"}}
{"type":"request/context","seq":6,"time":1785487612670,"data":{"provider":"deepseek-official","model":"deepseek-v4-flash"}}
{"type":"assistant/chunk","seq":7,"time":1783352121635,"data":{"turn":1,"step":1,"chunk":{"type":"block-start","index":0,"blockType":"reasoning"}}}
{"type":"reasoning-chunks","seq0":8,"time0":1783352121663,"data":{"turn":1,"step":1,"index":0,"dt":[1,0,0,0,0,27,0,0,29,0,0,27,0,0,0,0],"texts":["The"," user"," wants"," me"," to"," reply"," with"," exactly"," the"," word"," CH","ILD","_OK"," and"," nothing"," else","."]}}
{"type":"assistant/chunk","seq":25,"time":1783352121748,"data":{"turn":1,"step":1,"chunk":{"type":"block-start","index":1,"blockType":"text"}}}
{"type":"text-chunks","seq0":26,"time0":1783352121777,"data":{"turn":1,"step":1,"index":1,"dt":[0,0],"texts":["CH","ILD","_OK"]}}
{"type":"assistant/chunk","seq":29,"time":1783352121777,"data":{"turn":1,"step":1,"chunk":{"type":"block-end","index":0,"block":{"type":"reasoning","text":"The user wants me to reply with exactly the word CHILD_OK and nothing else."}}}}
{"type":"assistant/chunk","seq":30,"time":1783352121777,"data":{"turn":1,"step":1,"chunk":{"type":"block-end","index":1,"block":{"type":"text","text":"CHILD_OK"}}}}
{"type":"assistant/chunk","seq":31,"time":1785464653313,"data":{"turn":1,"step":1,"chunk":{"type":"usage","usage":{"inputTokens":48,"outputTokens":21,"cacheReadTokens":2816,"reasoningTokens":17}}}}
{"type":"assistant/chunk","seq":32,"time":1785487612679,"data":{"turn":1,"step":1,"chunk":{"type":"finish","reason":{"kind":"stop"}}}}
{"type":"assistant/message","seq":33,"time":1785487612679,"data":{"turn":1,"step":1,"message":{"role":"assistant","content":[{"type":"reasoning","text":"The user wants me to reply with exactly the word CHILD_OK and nothing else."},{"type":"text","text":"CHILD_OK"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"33709424-d097-40af-8fa2-8548571b1fb1"},"usage":{"inputTokens":48,"outputTokens":21,"cacheReadTokens":2816,"reasoningTokens":17}},"sourceEventSeqs":[7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32],"surfaceOp":"append"}
{"type":"step/end","seq":34,"time":1785487612680,"data":{"turn":1,"step":1}}
{"type":"turn/end","seq":35,"time":1785487612680,"data":{"turn":1,"reason":{"kind":"completed"}}}
{"type":"subagent/descriptor","seq":3,"time":1785534711744,"data":{"version":2,"mode":"one-shot","provider":"spawn","label":"Reply with CHILD_OK"}}
{"type":"user/message","seq":4,"time":1785534711745,"data":{"content":[{"type":"text","text":"Current runtime context. This snapshot supersedes earlier runtime-context snapshots.\n\nCurrent DSH file policy: danger-full-access. The DSH file sandbox does not restrict file modifications by available operations.\n\nApproval prompts are disabled in this session: actions that require approval are rejected automatically — do not request sandbox escalation (do not set `sandbox_permissions`)."}],"source":{"kind":"plugin","plugin":"@deepseek-ai/dsh-system-prompt"},"role":"user","id":"c5e8f2e3-b145-4d6e-97f9-4d85374410cb"},"surfaceOp":"append"}
{"type":"step/start","seq":5,"time":1785534711745,"data":{"turn":1,"step":1}}
{"type":"request/header","seq":6,"time":1785534711745,"data":{"header":{"config":{"provider":"deepseek-official","model":"deepseek-v4-flash"},"system":"{{system}}","tools":"{{tools}}"},"reason":"initial"}}
{"type":"request/context","seq":7,"time":1785534711745,"data":{"provider":"deepseek-official","model":"deepseek-v4-flash"}}
{"type":"assistant/chunk","seq":8,"time":1783352121663,"data":{"turn":1,"step":1,"chunk":{"type":"block-start","index":0,"blockType":"reasoning"}}}
{"type":"reasoning-chunks","seq0":9,"time0":1783352121664,"data":{"turn":1,"step":1,"index":0,"dt":[0,0,0,0,27,0,0,29,0,0,27,0,0,0,0,1],"texts":["The"," user"," wants"," me"," to"," reply"," with"," exactly"," the"," word"," CH","ILD","_OK"," and"," nothing"," else","."]}}
{"type":"assistant/chunk","seq":26,"time":1783352121777,"data":{"turn":1,"step":1,"chunk":{"type":"block-start","index":1,"blockType":"text"}}}
{"type":"text-chunks","seq0":27,"time0":1783352121777,"data":{"turn":1,"step":1,"index":1,"dt":[0,0],"texts":["CH","ILD","_OK"]}}
{"type":"assistant/chunk","seq":30,"time":1783352121777,"data":{"turn":1,"step":1,"chunk":{"type":"block-end","index":0,"block":{"type":"reasoning","text":"The user wants me to reply with exactly the word CHILD_OK and nothing else."}}}}
{"type":"assistant/chunk","seq":31,"time":1785464653313,"data":{"turn":1,"step":1,"chunk":{"type":"block-end","index":1,"block":{"type":"text","text":"CHILD_OK"}}}}
{"type":"assistant/chunk","seq":32,"time":1785487612679,"data":{"turn":1,"step":1,"chunk":{"type":"usage","usage":{"inputTokens":48,"outputTokens":21,"cacheReadTokens":2816,"reasoningTokens":17}}}}
{"type":"assistant/chunk","seq":33,"time":1785534711753,"data":{"turn":1,"step":1,"chunk":{"type":"finish","reason":{"kind":"stop"}}}}
{"type":"assistant/message","seq":34,"time":1785534711753,"data":{"turn":1,"step":1,"message":{"role":"assistant","content":[{"type":"reasoning","text":"The user wants me to reply with exactly the word CHILD_OK and nothing else."},{"type":"text","text":"CHILD_OK"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"cbea1608-409b-4c7d-bf3b-830c9699d4cc"},"usage":{"inputTokens":48,"outputTokens":21,"cacheReadTokens":2816,"reasoningTokens":17}},"sourceEventSeqs":[8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33],"surfaceOp":"append"}
{"type":"step/end","seq":35,"time":1785534711753,"data":{"turn":1,"step":1}}
{"type":"turn/end","seq":36,"time":1785534711753,"data":{"turn":1,"reason":{"kind":"completed"}}}

View File

@@ -117,7 +117,7 @@
},
{
"name": "list_agents",
"description": "List your background subagents by durable id and label. Status is a snapshot of the stored record: running means the subagent session is currently live in this process, complete means it exists only in storage and a `send_message` starts a new turn on the same conversation. The snapshot is not a delivery promise — `send_message` performs the authoritative check and may still fail. Children that could not be read are reported as diagnostics instead of being silently dropped.",
"description": "List your continuable background subagents by durable id and label. Status is a snapshot of the stored record: running means the subagent session is currently live in this process, complete means it exists only in storage and a `send_message` starts a new turn on the same conversation. The snapshot is not a delivery promise — `send_message` performs the authoritative check and may still fail. Children that could not be read are reported as diagnostics instead of being silently dropped.",
"parameters": {
"type": "object",
"properties": {}

View File

@@ -117,7 +117,7 @@
},
{
"name": "list_agents",
"description": "List your background subagents by durable id and label. Status is a snapshot of the stored record: running means the subagent session is currently live in this process, complete means it exists only in storage and a `send_message` starts a new turn on the same conversation. The snapshot is not a delivery promise — `send_message` performs the authoritative check and may still fail. Children that could not be read are reported as diagnostics instead of being silently dropped.",
"description": "List your continuable background subagents by durable id and label. Status is a snapshot of the stored record: running means the subagent session is currently live in this process, complete means it exists only in storage and a `send_message` starts a new turn on the same conversation. The snapshot is not a delivery promise — `send_message` performs the authoritative check and may still fail. Children that could not be read are reported as diagnostics instead of being silently dropped.",
"parameters": {
"type": "object",
"properties": {}

View File

@@ -1,19 +1,20 @@
{"type":"session","version":0,"id":"583a4db2-3350-436c-b4a5-5615fd159052","createdAt":1783600636316,"cwd":"{{cwd}}","parentSession":"3fd7d599-56b1-493a-930d-f1fc5e1556e8","delegationDepth":1}
{"type":"turn/start","seq":0,"time":1783600636316,"data":{"turn":1,"trigger":{"kind":"message","source":{"kind":"user"}}}}
{"type":"user/message","seq":1,"time":1783600636316,"data":{"content":[{"type":"text","text":"Reply with exactly the word WF_CHILD_OK and nothing else."}],"source":{"kind":"user"},"role":"user","id":"7591c8f5-44bb-453f-bfcf-d8d3450c8352"},"surfaceOp":"append"}
{"type":"user/message","seq":1,"time":1783600636316,"data":{"content":[{"type":"text","text":"Reply with exactly the word WF_CHILD_OK and nothing else."}],"source":{"kind":"user"},"role":"user","id":"a7990b4d-9b1d-47de-883b-47f6755c2b9b"},"surfaceOp":"append"}
{"type":"session/title","seq":2,"time":1783600636316,"data":{"title":"Reply with exactly the word","messageSeqs":[1],"source":{"kind":"fallback"}}}
{"type":"user/message","seq":3,"time":1785464658064,"data":{"content":[{"type":"text","text":"Current runtime context. This snapshot supersedes earlier runtime-context snapshots.\n\nCurrent DSH file policy: danger-full-access. The DSH file sandbox does not restrict file modifications by available operations.\n\nApproval prompts are disabled in this session: actions that require approval are rejected automatically — do not request sandbox escalation (do not set `sandbox_permissions`)."}],"source":{"kind":"plugin","plugin":"@deepseek-ai/dsh-system-prompt"},"role":"user","id":"c7e53774-1584-4493-9ed3-7933c1f217c9"},"surfaceOp":"append"}
{"type":"step/start","seq":4,"time":1785464658064,"data":{"turn":1,"step":1}}
{"type":"request/header","seq":5,"time":1785464658065,"data":{"header":{"config":{"provider":"deepseek-official","model":"deepseek-v4-flash"},"system":"{{system}}","tools":"{{tools}}"},"reason":"initial"}}
{"type":"request/context","seq":6,"time":1785487619699,"data":{"provider":"deepseek-official","model":"deepseek-v4-flash"}}
{"type":"assistant/chunk","seq":7,"time":1783600638173,"data":{"turn":1,"step":1,"chunk":{"type":"block-start","index":0,"blockType":"reasoning"}}}
{"type":"reasoning-chunks","seq0":8,"time0":1783600638189,"data":{"turn":1,"step":1,"index":0,"dt":[0,0,0,0,24,0,0,0,0,29,0,0,0,0,0,34,0],"texts":["The"," user"," wants"," me"," to"," reply"," with"," exactly"," \"","WF","_CH","ILD","_OK","\""," and"," nothing"," else","."]}}
{"type":"assistant/chunk","seq":26,"time":1783600638276,"data":{"turn":1,"step":1,"chunk":{"type":"block-start","index":1,"blockType":"text"}}}
{"type":"text-chunks","seq0":27,"time0":1783600638276,"data":{"turn":1,"step":1,"index":1,"dt":[0,4,0],"texts":["WF","_CH","ILD","_OK"]}}
{"type":"assistant/chunk","seq":31,"time":1783600638280,"data":{"turn":1,"step":1,"chunk":{"type":"block-end","index":0,"block":{"type":"reasoning","text":"The user wants me to reply with exactly \"WF_CHILD_OK\" and nothing else."}}}}
{"type":"assistant/chunk","seq":32,"time":1783600638280,"data":{"turn":1,"step":1,"chunk":{"type":"block-end","index":1,"block":{"type":"text","text":"WF_CHILD_OK"}}}}
{"type":"assistant/chunk","seq":33,"time":1785464658074,"data":{"turn":1,"step":1,"chunk":{"type":"usage","usage":{"inputTokens":17,"outputTokens":23,"cacheReadTokens":3072,"reasoningTokens":18}}}}
{"type":"assistant/chunk","seq":34,"time":1785487619708,"data":{"turn":1,"step":1,"chunk":{"type":"finish","reason":{"kind":"stop"}}}}
{"type":"assistant/message","seq":35,"time":1785487619708,"data":{"turn":1,"step":1,"message":{"role":"assistant","content":[{"type":"reasoning","text":"The user wants me to reply with exactly \"WF_CHILD_OK\" and nothing else."},{"type":"text","text":"WF_CHILD_OK"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"766b6c2b-e33f-499f-a39c-7f3441d65717"},"usage":{"inputTokens":17,"outputTokens":23,"cacheReadTokens":3072,"reasoningTokens":18}},"sourceEventSeqs":[7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34],"surfaceOp":"append"}
{"type":"step/end","seq":36,"time":1785487619708,"data":{"turn":1,"step":1}}
{"type":"turn/end","seq":37,"time":1785487619708,"data":{"turn":1,"reason":{"kind":"completed"}}}
{"type":"subagent/descriptor","seq":3,"time":1785534719523,"data":{"version":2,"mode":"one-shot","provider":"spawn","label":"Reply with exactly the word WF_CHILD_OK and not…"}}
{"type":"user/message","seq":4,"time":1785534719523,"data":{"content":[{"type":"text","text":"Current runtime context. This snapshot supersedes earlier runtime-context snapshots.\n\nCurrent DSH file policy: danger-full-access. The DSH file sandbox does not restrict file modifications by available operations.\n\nApproval prompts are disabled in this session: actions that require approval are rejected automatically — do not request sandbox escalation (do not set `sandbox_permissions`)."}],"source":{"kind":"plugin","plugin":"@deepseek-ai/dsh-system-prompt"},"role":"user","id":"44d0e7d4-c05c-446d-afd2-28b2a9aec7e8"},"surfaceOp":"append"}
{"type":"step/start","seq":5,"time":1785534719523,"data":{"turn":1,"step":1}}
{"type":"request/header","seq":6,"time":1785534719523,"data":{"header":{"config":{"provider":"deepseek-official","model":"deepseek-v4-flash"},"system":"{{system}}","tools":"{{tools}}"},"reason":"initial"}}
{"type":"request/context","seq":7,"time":1785534719524,"data":{"provider":"deepseek-official","model":"deepseek-v4-flash"}}
{"type":"assistant/chunk","seq":8,"time":1783600638189,"data":{"turn":1,"step":1,"chunk":{"type":"block-start","index":0,"blockType":"reasoning"}}}
{"type":"reasoning-chunks","seq0":9,"time0":1783600638189,"data":{"turn":1,"step":1,"index":0,"dt":[0,0,0,24,0,0,0,0,29,0,0,0,0,0,34,0,0],"texts":["The"," user"," wants"," me"," to"," reply"," with"," exactly"," \"","WF","_CH","ILD","_OK","\""," and"," nothing"," else","."]}}
{"type":"assistant/chunk","seq":27,"time":1783600638276,"data":{"turn":1,"step":1,"chunk":{"type":"block-start","index":1,"blockType":"text"}}}
{"type":"text-chunks","seq0":28,"time0":1783600638276,"data":{"turn":1,"step":1,"index":1,"dt":[4,0,0],"texts":["WF","_CH","ILD","_OK"]}}
{"type":"assistant/chunk","seq":32,"time":1783600638280,"data":{"turn":1,"step":1,"chunk":{"type":"block-end","index":0,"block":{"type":"reasoning","text":"The user wants me to reply with exactly \"WF_CHILD_OK\" and nothing else."}}}}
{"type":"assistant/chunk","seq":33,"time":1785464658074,"data":{"turn":1,"step":1,"chunk":{"type":"block-end","index":1,"block":{"type":"text","text":"WF_CHILD_OK"}}}}
{"type":"assistant/chunk","seq":34,"time":1785487619708,"data":{"turn":1,"step":1,"chunk":{"type":"usage","usage":{"inputTokens":17,"outputTokens":23,"cacheReadTokens":3072,"reasoningTokens":18}}}}
{"type":"assistant/chunk","seq":35,"time":1785534719532,"data":{"turn":1,"step":1,"chunk":{"type":"finish","reason":{"kind":"stop"}}}}
{"type":"assistant/message","seq":36,"time":1785534719533,"data":{"turn":1,"step":1,"message":{"role":"assistant","content":[{"type":"reasoning","text":"The user wants me to reply with exactly \"WF_CHILD_OK\" and nothing else."},{"type":"text","text":"WF_CHILD_OK"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"f2b9fe23-12f6-4b47-aaf3-d5ec14a57581"},"usage":{"inputTokens":17,"outputTokens":23,"cacheReadTokens":3072,"reasoningTokens":18}},"sourceEventSeqs":[8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35],"surfaceOp":"append"}
{"type":"step/end","seq":37,"time":1785534719533,"data":{"turn":1,"step":1}}
{"type":"turn/end","seq":38,"time":1785534719533,"data":{"turn":1,"reason":{"kind":"completed"}}}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -2,26 +2,27 @@
{"type":"sandbox/mode","seq":0,"time":0,"data":{"mode":"read-only","source":"delegation"}}
{"type":"turn/start","seq":1,"time":0,"data":{"turn":1,"trigger":{"kind":"message","source":{"kind":"user"}}}}
{"type":"user/message","seq":2,"time":0,"data":{"content":[{"type":"text","text":"Use the write tool exactly once with file_path set to exactly the relative path inherited.txt and content escaped. If the write is denied, reply with the single word CHILD_DENIED and the denial marker line; do not retry and do not request escalation. If it succeeds, reply CHILD_WROTE."}],"source":{"kind":"user"},"role":"user","id":"{{sessionId}}"},"surfaceOp":"append"}
{"type":"session/title","seq":3,"time":0,"data":{"title":"Use the write tool exactly","messageSeqs":[2],"source":{"kind":"fallback"}}}
{"type":"user/message","seq":4,"time":0,"data":{"content":[{"type":"text","text":"Current runtime context. This snapshot supersedes earlier runtime-context snapshots.\n\nCurrent DSH file policy: read-only. Any available operation enforced by the DSH file sandbox cannot modify files in the standing mode. Do not refuse a required modification from this policy alone: try an available tool normally and follow any denial and escalation guidance it returns."}],"source":{"kind":"plugin","plugin":"@deepseek-ai/dsh-system-prompt"},"role":"user","id":"{{sessionId}}"},"surfaceOp":"append"}
{"type":"step/start","seq":5,"time":0,"data":{"turn":1,"step":1}}
{"type":"request/header","seq":6,"time":0,"data":{"header":{"config":{"provider":"deepseek-official","model":"deepseek-v4-flash"},"system":"{{system}}","tools":"{{tools}}"},"reason":"initial"}}
{"type":"request/context","seq":7,"time":0,"data":{"provider":"deepseek-official","model":"deepseek-v4-flash"}}
{"type":"assistant/chunk","seq":8,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}}}
{"type":"assistant/chunk","seq":9,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":0,"id":"child-write","name":"write","argumentsDelta":"{\"file_path\": \"inherited.txt\", \"content\": \"escaped\"}"}}}
{"type":"assistant/chunk","seq":10,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"child-write","name":"write","arguments":"{\"file_path\": \"inherited.txt\", \"content\": \"escaped\"}"}}}}
{"type":"assistant/chunk","seq":11,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"usage","usage":{"inputTokens":10,"outputTokens":5}}}}
{"type":"assistant/chunk","seq":12,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}}
{"type":"assistant/message","seq":13,"time":0,"data":{"turn":1,"step":1,"message":{"role":"assistant","content":[{"type":"tool-call","id":"child-write","name":"write","arguments":"{\"file_path\": \"inherited.txt\", \"content\": \"escaped\"}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{sessionId}}"},"usage":{"inputTokens":10,"outputTokens":5}},"sourceEventSeqs":[8,9,10,11,12],"surfaceOp":"append"}
{"type":"tool/call","seq":14,"time":0,"data":{"turn":1,"step":1,"callId":"child-write","name":"write","arguments":"{\"file_path\": \"inherited.txt\", \"content\": \"escaped\"}"}}
{"type":"tool/result","seq":15,"time":0,"data":{"turn":1,"step":1,"message":{"source":{"kind":"tool","callId":"child-write"},"content":[{"type":"tool-result","toolCallId":"child-write","content":[{"type":"text","text":"Error: [sandbox: file access denied under read-only mode]\n[sandbox: escalation available — retry this exact operation once with sandbox_permissions (the narrowest wider mode that suffices) + justification; the approval prompt asks the user]"}],"isError":true}],"role":"user","id":"{{sessionId}}"},"error":{"name":"FsError","code":"FS_SANDBOX_DENIED"}},"sourceEventSeqs":[14],"surfaceOp":"append"}
{"type":"step/end","seq":16,"time":0,"data":{"turn":1,"step":1}}
{"type":"step/start","seq":17,"time":0,"data":{"turn":1,"step":2}}
{"type":"assistant/chunk","seq":18,"time":0,"data":{"turn":1,"step":2,"chunk":{"type":"block-start","index":0,"blockType":"text"}}}
{"type":"assistant/chunk","seq":19,"time":0,"data":{"turn":1,"step":2,"chunk":{"type":"text-delta","index":0,"text":"CHILD_DENIED [sandbox: file access denied under read-only mode]"}}}
{"type":"assistant/chunk","seq":20,"time":0,"data":{"turn":1,"step":2,"chunk":{"type":"block-end","index":0,"block":{"type":"text","text":"CHILD_DENIED [sandbox: file access denied under read-only mode]"}}}}
{"type":"assistant/chunk","seq":21,"time":0,"data":{"turn":1,"step":2,"chunk":{"type":"usage","usage":{"inputTokens":10,"outputTokens":5}}}}
{"type":"assistant/chunk","seq":22,"time":0,"data":{"turn":1,"step":2,"chunk":{"type":"finish","reason":{"kind":"stop"}}}}
{"type":"assistant/message","seq":23,"time":0,"data":{"turn":1,"step":2,"message":{"role":"assistant","content":[{"type":"text","text":"CHILD_DENIED [sandbox: file access denied under read-only mode]"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{sessionId}}"},"usage":{"inputTokens":10,"outputTokens":5}},"sourceEventSeqs":[18,19,20,21,22],"surfaceOp":"append"}
{"type":"step/end","seq":24,"time":0,"data":{"turn":1,"step":2}}
{"type":"turn/end","seq":25,"time":0,"data":{"turn":1,"reason":{"kind":"completed"}}}
{"type":"subagent/descriptor","seq":3,"time":0,"data":{"version":2,"mode":"one-shot","provider":"spawn","label":"Delegated write probe"}}
{"type":"session/title","seq":4,"time":0,"data":{"title":"Use the write tool exactly","messageSeqs":[2],"source":{"kind":"fallback"}}}
{"type":"user/message","seq":5,"time":0,"data":{"content":[{"type":"text","text":"Current runtime context. This snapshot supersedes earlier runtime-context snapshots.\n\nCurrent DSH file policy: read-only. Any available operation enforced by the DSH file sandbox cannot modify files in the standing mode. Do not refuse a required modification from this policy alone: try an available tool normally and follow any denial and escalation guidance it returns."}],"source":{"kind":"plugin","plugin":"@deepseek-ai/dsh-system-prompt"},"role":"user","id":"{{sessionId}}"},"surfaceOp":"append"}
{"type":"step/start","seq":6,"time":0,"data":{"turn":1,"step":1}}
{"type":"request/header","seq":7,"time":0,"data":{"header":{"config":{"provider":"deepseek-official","model":"deepseek-v4-flash"},"system":"{{system}}","tools":"{{tools}}"},"reason":"initial"}}
{"type":"request/context","seq":8,"time":0,"data":{"provider":"deepseek-official","model":"deepseek-v4-flash"}}
{"type":"assistant/chunk","seq":9,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}}}
{"type":"assistant/chunk","seq":10,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":0,"id":"child-write","name":"write","argumentsDelta":"{\"file_path\": \"inherited.txt\", \"content\": \"escaped\"}"}}}
{"type":"assistant/chunk","seq":11,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"child-write","name":"write","arguments":"{\"file_path\": \"inherited.txt\", \"content\": \"escaped\"}"}}}}
{"type":"assistant/chunk","seq":12,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"usage","usage":{"inputTokens":10,"outputTokens":5}}}}
{"type":"assistant/chunk","seq":13,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}}
{"type":"assistant/message","seq":14,"time":0,"data":{"turn":1,"step":1,"message":{"role":"assistant","content":[{"type":"tool-call","id":"child-write","name":"write","arguments":"{\"file_path\": \"inherited.txt\", \"content\": \"escaped\"}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{sessionId}}"},"usage":{"inputTokens":10,"outputTokens":5}},"sourceEventSeqs":[9,10,11,12,13],"surfaceOp":"append"}
{"type":"tool/call","seq":15,"time":0,"data":{"turn":1,"step":1,"callId":"child-write","name":"write","arguments":"{\"file_path\": \"inherited.txt\", \"content\": \"escaped\"}"}}
{"type":"tool/result","seq":16,"time":0,"data":{"turn":1,"step":1,"message":{"source":{"kind":"tool","callId":"child-write"},"content":[{"type":"tool-result","toolCallId":"child-write","content":[{"type":"text","text":"Error: [sandbox: file access denied under read-only mode]\n[sandbox: escalation available — retry this exact operation once with sandbox_permissions (the narrowest wider mode that suffices) + justification; the approval prompt asks the user]"}],"isError":true}],"role":"user","id":"{{sessionId}}"},"error":{"name":"FsError","code":"FS_SANDBOX_DENIED"}},"sourceEventSeqs":[15],"surfaceOp":"append"}
{"type":"step/end","seq":17,"time":0,"data":{"turn":1,"step":1}}
{"type":"step/start","seq":18,"time":0,"data":{"turn":1,"step":2}}
{"type":"assistant/chunk","seq":19,"time":0,"data":{"turn":1,"step":2,"chunk":{"type":"block-start","index":0,"blockType":"text"}}}
{"type":"assistant/chunk","seq":20,"time":0,"data":{"turn":1,"step":2,"chunk":{"type":"text-delta","index":0,"text":"CHILD_DENIED [sandbox: file access denied under read-only mode]"}}}
{"type":"assistant/chunk","seq":21,"time":0,"data":{"turn":1,"step":2,"chunk":{"type":"block-end","index":0,"block":{"type":"text","text":"CHILD_DENIED [sandbox: file access denied under read-only mode]"}}}}
{"type":"assistant/chunk","seq":22,"time":0,"data":{"turn":1,"step":2,"chunk":{"type":"usage","usage":{"inputTokens":10,"outputTokens":5}}}}
{"type":"assistant/chunk","seq":23,"time":0,"data":{"turn":1,"step":2,"chunk":{"type":"finish","reason":{"kind":"stop"}}}}
{"type":"assistant/message","seq":24,"time":0,"data":{"turn":1,"step":2,"message":{"role":"assistant","content":[{"type":"text","text":"CHILD_DENIED [sandbox: file access denied under read-only mode]"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{sessionId}}"},"usage":{"inputTokens":10,"outputTokens":5}},"sourceEventSeqs":[19,20,21,22,23],"surfaceOp":"append"}
{"type":"step/end","seq":25,"time":0,"data":{"turn":1,"step":2}}
{"type":"turn/end","seq":26,"time":0,"data":{"turn":1,"reason":{"kind":"completed"}}}

View File

@@ -99,37 +99,38 @@
{"method":"session.event","params":{"sessionId":"{{sessionId}}","event":{"type":"turn/start","seq":0,"time":0,"data":{"turn":1,"trigger":{"kind":"message","source":{"kind":"user"}}}}}}
{"method":"session.event","params":{"sessionId":"{{sessionId}}","event":{"type":"user/message","seq":1,"time":0,"data":{"content":[{"type":"text","text":"Reply with exactly: child answer 42."}],"source":{"kind":"user"},"role":"user","id":"{{sessionId}}"},"surfaceOp":"append"}}}
{"method":"session.event","params":{"sessionId":"{{sessionId}}","event":{"type":"session/title","seq":2,"time":0,"data":{"title":"Reply with exactly: child answer","messageSeqs":[1],"source":{"kind":"fallback"}}}}}
{"method":"session.event","params":{"sessionId":"{{sessionId}}","event":{"type":"step/start","seq":3,"time":0,"data":{"turn":1,"step":1}}}}
{"method":"session.event","params":{"sessionId":"{{sessionId}}","event":{"type":"request/header","seq":4,"time":0,"data":{"header":{"config":{"provider":"deepseek-official","model":"deepseek-v4-flash"},"system":"{{system}}","tools":"{{tools}}"},"reason":"initial"}}}}
{"method":"session.event","params":{"sessionId":"{{sessionId}}","event":{"type":"request/context","seq":5,"time":0,"data":{"provider":"deepseek-official","model":"deepseek-v4-flash"}}}}
{"method":"session.event","params":{"sessionId":"{{sessionId}}","event":{"type":"assistant/chunk","seq":6,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"block-start","index":0,"blockType":"reasoning"}}}}}
{"method":"session.event","params":{"sessionId":"{{sessionId}}","event":{"type":"assistant/chunk","seq":7,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"The"}}}}}
{"method":"session.event","params":{"sessionId":"{{sessionId}}","event":{"type":"assistant/chunk","seq":8,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" user"}}}}}
{"method":"session.event","params":{"sessionId":"{{sessionId}}","event":{"type":"assistant/chunk","seq":9,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" wants"}}}}}
{"method":"session.event","params":{"sessionId":"{{sessionId}}","event":{"type":"assistant/chunk","seq":10,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" me"}}}}}
{"method":"session.event","params":{"sessionId":"{{sessionId}}","event":{"type":"assistant/chunk","seq":11,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" to"}}}}}
{"method":"session.event","params":{"sessionId":"{{sessionId}}","event":{"type":"assistant/chunk","seq":12,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" reply"}}}}}
{"method":"session.event","params":{"sessionId":"{{sessionId}}","event":{"type":"assistant/chunk","seq":13,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" with"}}}}}
{"method":"session.event","params":{"sessionId":"{{sessionId}}","event":{"type":"assistant/chunk","seq":14,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" exactly"}}}}}
{"method":"session.event","params":{"sessionId":"{{sessionId}}","event":{"type":"assistant/chunk","seq":15,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" \""}}}}}
{"method":"session.event","params":{"sessionId":"{{sessionId}}","event":{"type":"assistant/chunk","seq":16,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"child"}}}}}
{"method":"session.event","params":{"sessionId":"{{sessionId}}","event":{"type":"assistant/chunk","seq":17,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" answer"}}}}}
{"method":"session.event","params":{"sessionId":"{{sessionId}}","event":{"type":"assistant/chunk","seq":18,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" "}}}}}
{"method":"session.event","params":{"sessionId":"{{sessionId}}","event":{"type":"assistant/chunk","seq":19,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"42"}}}}}
{"method":"session.event","params":{"sessionId":"{{sessionId}}","event":{"type":"assistant/chunk","seq":20,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":".\""}}}}}
{"method":"session.event","params":{"sessionId":"{{sessionId}}","event":{"type":"assistant/chunk","seq":21,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"block-start","index":1,"blockType":"text"}}}}}
{"method":"session.event","params":{"sessionId":"{{sessionId}}","event":{"type":"assistant/chunk","seq":22,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"text-delta","index":1,"text":"child"}}}}}
{"method":"session.event","params":{"sessionId":"{{sessionId}}","event":{"type":"assistant/chunk","seq":23,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"text-delta","index":1,"text":" answer"}}}}}
{"method":"session.event","params":{"sessionId":"{{sessionId}}","event":{"type":"assistant/chunk","seq":24,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"text-delta","index":1,"text":" "}}}}}
{"method":"session.event","params":{"sessionId":"{{sessionId}}","event":{"type":"assistant/chunk","seq":25,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"text-delta","index":1,"text":"42"}}}}}
{"method":"session.event","params":{"sessionId":"{{sessionId}}","event":{"type":"assistant/chunk","seq":26,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"text-delta","index":1,"text":"."}}}}}
{"method":"session.event","params":{"sessionId":"{{sessionId}}","event":{"type":"assistant/chunk","seq":27,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"block-end","index":0,"block":{"type":"reasoning","text":"The user wants me to reply with exactly \"child answer 42.\""}}}}}}
{"method":"session.event","params":{"sessionId":"{{sessionId}}","event":{"type":"assistant/chunk","seq":28,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"block-end","index":1,"block":{"type":"text","text":"child answer 42."}}}}}}
{"method":"session.event","params":{"sessionId":"{{sessionId}}","event":{"type":"assistant/chunk","seq":29,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"usage","usage":{"inputTokens":107,"outputTokens":20,"cacheReadTokens":1664,"reasoningTokens":14}}}}}}
{"method":"session.event","params":{"sessionId":"{{sessionId}}","event":{"type":"assistant/chunk","seq":30,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"finish","reason":{"kind":"stop"}}}}}}
{"method":"session.event","params":{"sessionId":"{{sessionId}}","event":{"type":"assistant/message","seq":31,"time":0,"data":{"turn":1,"step":1,"message":{"role":"assistant","content":[{"type":"reasoning","text":"The user wants me to reply with exactly \"child answer 42.\""},{"type":"text","text":"child answer 42."}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{sessionId}}"},"usage":{"inputTokens":107,"outputTokens":20,"cacheReadTokens":1664,"reasoningTokens":14}},"sourceEventSeqs":[6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30],"surfaceOp":"append"}}}
{"method":"session.event","params":{"sessionId":"{{sessionId}}","event":{"type":"step/end","seq":32,"time":0,"data":{"turn":1,"step":1}}}}
{"method":"session.event","params":{"sessionId":"{{sessionId}}","event":{"type":"turn/end","seq":33,"time":0,"data":{"turn":1,"reason":{"kind":"completed"}}}}}
{"method":"session.event","params":{"sessionId":"{{sessionId}}","event":{"type":"subagent/descriptor","seq":3,"time":0,"data":{"version":2,"mode":"one-shot","provider":"spawn","label":"echo probe"}}}}
{"method":"session.event","params":{"sessionId":"{{sessionId}}","event":{"type":"step/start","seq":4,"time":0,"data":{"turn":1,"step":1}}}}
{"method":"session.event","params":{"sessionId":"{{sessionId}}","event":{"type":"request/header","seq":5,"time":0,"data":{"header":{"config":{"provider":"deepseek-official","model":"deepseek-v4-flash"},"system":"{{system}}","tools":"{{tools}}"},"reason":"initial"}}}}
{"method":"session.event","params":{"sessionId":"{{sessionId}}","event":{"type":"request/context","seq":6,"time":0,"data":{"provider":"deepseek-official","model":"deepseek-v4-flash"}}}}
{"method":"session.event","params":{"sessionId":"{{sessionId}}","event":{"type":"assistant/chunk","seq":7,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"block-start","index":0,"blockType":"reasoning"}}}}}
{"method":"session.event","params":{"sessionId":"{{sessionId}}","event":{"type":"assistant/chunk","seq":8,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"The"}}}}}
{"method":"session.event","params":{"sessionId":"{{sessionId}}","event":{"type":"assistant/chunk","seq":9,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" user"}}}}}
{"method":"session.event","params":{"sessionId":"{{sessionId}}","event":{"type":"assistant/chunk","seq":10,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" wants"}}}}}
{"method":"session.event","params":{"sessionId":"{{sessionId}}","event":{"type":"assistant/chunk","seq":11,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" me"}}}}}
{"method":"session.event","params":{"sessionId":"{{sessionId}}","event":{"type":"assistant/chunk","seq":12,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" to"}}}}}
{"method":"session.event","params":{"sessionId":"{{sessionId}}","event":{"type":"assistant/chunk","seq":13,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" reply"}}}}}
{"method":"session.event","params":{"sessionId":"{{sessionId}}","event":{"type":"assistant/chunk","seq":14,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" with"}}}}}
{"method":"session.event","params":{"sessionId":"{{sessionId}}","event":{"type":"assistant/chunk","seq":15,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" exactly"}}}}}
{"method":"session.event","params":{"sessionId":"{{sessionId}}","event":{"type":"assistant/chunk","seq":16,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" \""}}}}}
{"method":"session.event","params":{"sessionId":"{{sessionId}}","event":{"type":"assistant/chunk","seq":17,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"child"}}}}}
{"method":"session.event","params":{"sessionId":"{{sessionId}}","event":{"type":"assistant/chunk","seq":18,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" answer"}}}}}
{"method":"session.event","params":{"sessionId":"{{sessionId}}","event":{"type":"assistant/chunk","seq":19,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" "}}}}}
{"method":"session.event","params":{"sessionId":"{{sessionId}}","event":{"type":"assistant/chunk","seq":20,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"42"}}}}}
{"method":"session.event","params":{"sessionId":"{{sessionId}}","event":{"type":"assistant/chunk","seq":21,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":".\""}}}}}
{"method":"session.event","params":{"sessionId":"{{sessionId}}","event":{"type":"assistant/chunk","seq":22,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"block-start","index":1,"blockType":"text"}}}}}
{"method":"session.event","params":{"sessionId":"{{sessionId}}","event":{"type":"assistant/chunk","seq":23,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"text-delta","index":1,"text":"child"}}}}}
{"method":"session.event","params":{"sessionId":"{{sessionId}}","event":{"type":"assistant/chunk","seq":24,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"text-delta","index":1,"text":" answer"}}}}}
{"method":"session.event","params":{"sessionId":"{{sessionId}}","event":{"type":"assistant/chunk","seq":25,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"text-delta","index":1,"text":" "}}}}}
{"method":"session.event","params":{"sessionId":"{{sessionId}}","event":{"type":"assistant/chunk","seq":26,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"text-delta","index":1,"text":"42"}}}}}
{"method":"session.event","params":{"sessionId":"{{sessionId}}","event":{"type":"assistant/chunk","seq":27,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"text-delta","index":1,"text":"."}}}}}
{"method":"session.event","params":{"sessionId":"{{sessionId}}","event":{"type":"assistant/chunk","seq":28,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"block-end","index":0,"block":{"type":"reasoning","text":"The user wants me to reply with exactly \"child answer 42.\""}}}}}}
{"method":"session.event","params":{"sessionId":"{{sessionId}}","event":{"type":"assistant/chunk","seq":29,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"block-end","index":1,"block":{"type":"text","text":"child answer 42."}}}}}}
{"method":"session.event","params":{"sessionId":"{{sessionId}}","event":{"type":"assistant/chunk","seq":30,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"usage","usage":{"inputTokens":107,"outputTokens":20,"cacheReadTokens":1664,"reasoningTokens":14}}}}}}
{"method":"session.event","params":{"sessionId":"{{sessionId}}","event":{"type":"assistant/chunk","seq":31,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"finish","reason":{"kind":"stop"}}}}}}
{"method":"session.event","params":{"sessionId":"{{sessionId}}","event":{"type":"assistant/message","seq":32,"time":0,"data":{"turn":1,"step":1,"message":{"role":"assistant","content":[{"type":"reasoning","text":"The user wants me to reply with exactly \"child answer 42.\""},{"type":"text","text":"child answer 42."}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{sessionId}}"},"usage":{"inputTokens":107,"outputTokens":20,"cacheReadTokens":1664,"reasoningTokens":14}},"sourceEventSeqs":[7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31],"surfaceOp":"append"}}}
{"method":"session.event","params":{"sessionId":"{{sessionId}}","event":{"type":"step/end","seq":33,"time":0,"data":{"turn":1,"step":1}}}}
{"method":"session.event","params":{"sessionId":"{{sessionId}}","event":{"type":"turn/end","seq":34,"time":0,"data":{"turn":1,"reason":{"kind":"completed"}}}}}
{"method":"subagent.finished","params":{"provider":"spawn","agentId":"{{sessionId}}","parentSessionId":"{{sessionId}}","childSessionId":"{{sessionId}}","status":"ok","stopReason":"completed","lastAssistantMessage":[{"type":"reasoning","text":"The user wants me to reply with exactly \"child answer 42.\""},{"type":"text","text":"child answer 42."}]}}
{"method":"session.event","params":{"sessionId":"{{sessionId}}","event":{"type":"tool/result","seq":97,"time":0,"data":{"turn":1,"step":1,"message":{"source":{"kind":"tool","callId":"call_00_oHPNQ1nLoakoaAGXIxCM7404"},"content":[{"type":"tool-result","toolCallId":"call_00_oHPNQ1nLoakoaAGXIxCM7404","content":[{"type":"text","text":"child answer 42."}],"isError":false}],"role":"user","id":"{{sessionId}}"}},"sourceEventSeqs":[96],"surfaceOp":"append"}}}
{"method":"session.event","params":{"sessionId":"{{sessionId}}","event":{"type":"step/end","seq":98,"time":0,"data":{"turn":1,"step":1}}}}

View File

@@ -1,18 +1,19 @@
{"type":"session","version":0,"id":"0b7fd85c-9f6f-4d46-b954-363984ce66fb","createdAt":1785097410282,"cwd":"{{cwd}}","parentSession":"sdk-snapshot-subagent","delegationDepth":1}
{"type":"turn/start","seq":0,"time":1785097410283,"data":{"turn":1,"trigger":{"kind":"message","source":{"kind":"user"}}}}
{"type":"user/message","seq":1,"time":1785097410283,"data":{"content":[{"type":"text","text":"Reply with exactly: child answer 42."}],"source":{"kind":"user"},"role":"user","id":"de0c784b-6140-4686-bc55-9680cc57a705"},"surfaceOp":"append"}
{"type":"user/message","seq":1,"time":1785097410283,"data":{"content":[{"type":"text","text":"Reply with exactly: child answer 42."}],"source":{"kind":"user"},"role":"user","id":"b33e0611-0483-44a9-8b57-9828b87cb846"},"surfaceOp":"append"}
{"type":"session/title","seq":2,"time":1785097410283,"data":{"title":"Reply with exactly: child answer","messageSeqs":[1],"source":{"kind":"fallback"}}}
{"type":"step/start","seq":3,"time":1785097410284,"data":{"turn":1,"step":1}}
{"type":"request/header","seq":4,"time":1785097410284,"data":{"header":{"config":{"provider":"deepseek-official","model":"deepseek-v4-flash"},"system":"{{system}}","tools":"{{tools}}"},"reason":"initial"}}
{"type":"request/context","seq":5,"time":1785460688790,"data":{"provider":"deepseek-official","model":"deepseek-v4-flash"}}
{"type":"assistant/chunk","seq":6,"time":1785097410836,"data":{"turn":1,"step":1,"chunk":{"type":"block-start","index":0,"blockType":"reasoning"}}}
{"type":"reasoning-chunks","seq0":7,"time0":1785097410985,"data":{"turn":1,"step":1,"index":0,"dt":[26,0,0,24,1,0,0,0,25,0,1,0,51],"texts":["The"," user"," wants"," me"," to"," reply"," with"," exactly"," \"","child"," answer"," ","42",".\""]}}
{"type":"assistant/chunk","seq":21,"time":1785097411113,"data":{"turn":1,"step":1,"chunk":{"type":"block-start","index":1,"blockType":"text"}}}
{"type":"text-chunks","seq0":22,"time0":1785097411114,"data":{"turn":1,"step":1,"index":1,"dt":[0,0,0,24],"texts":["child"," answer"," ","42","."]}}
{"type":"assistant/chunk","seq":27,"time":1785097411138,"data":{"turn":1,"step":1,"chunk":{"type":"block-end","index":0,"block":{"type":"reasoning","text":"The user wants me to reply with exactly \"child answer 42.\""}}}}
{"type":"assistant/chunk","seq":28,"time":1785097411138,"data":{"turn":1,"step":1,"chunk":{"type":"block-end","index":1,"block":{"type":"text","text":"child answer 42."}}}}
{"type":"assistant/chunk","seq":29,"time":1785097411138,"data":{"turn":1,"step":1,"chunk":{"type":"usage","usage":{"inputTokens":107,"outputTokens":20,"cacheReadTokens":1664,"reasoningTokens":14}}}}
{"type":"assistant/chunk","seq":30,"time":1785460688799,"data":{"turn":1,"step":1,"chunk":{"type":"finish","reason":{"kind":"stop"}}}}
{"type":"assistant/message","seq":31,"time":1785460688799,"data":{"turn":1,"step":1,"message":{"role":"assistant","content":[{"type":"reasoning","text":"The user wants me to reply with exactly \"child answer 42.\""},{"type":"text","text":"child answer 42."}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"f8a51b63-2677-471a-92c7-6b02e29372b5"},"usage":{"inputTokens":107,"outputTokens":20,"cacheReadTokens":1664,"reasoningTokens":14}},"sourceEventSeqs":[6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30],"surfaceOp":"append"}
{"type":"step/end","seq":32,"time":1785460688799,"data":{"turn":1,"step":1}}
{"type":"turn/end","seq":33,"time":1785460688799,"data":{"turn":1,"reason":{"kind":"completed"}}}
{"type":"subagent/descriptor","seq":3,"time":1785534755048,"data":{"version":2,"mode":"one-shot","provider":"spawn","label":"echo probe"}}
{"type":"step/start","seq":4,"time":1785534755048,"data":{"turn":1,"step":1}}
{"type":"request/header","seq":5,"time":1785534755048,"data":{"header":{"config":{"provider":"deepseek-official","model":"deepseek-v4-flash"},"system":"{{system}}","tools":"{{tools}}"},"reason":"initial"}}
{"type":"request/context","seq":6,"time":1785534755048,"data":{"provider":"deepseek-official","model":"deepseek-v4-flash"}}
{"type":"assistant/chunk","seq":7,"time":1785097410985,"data":{"turn":1,"step":1,"chunk":{"type":"block-start","index":0,"blockType":"reasoning"}}}
{"type":"reasoning-chunks","seq0":8,"time0":1785097411011,"data":{"turn":1,"step":1,"index":0,"dt":[0,0,24,1,0,0,0,25,0,1,0,51,0],"texts":["The"," user"," wants"," me"," to"," reply"," with"," exactly"," \"","child"," answer"," ","42",".\""]}}
{"type":"assistant/chunk","seq":22,"time":1785097411114,"data":{"turn":1,"step":1,"chunk":{"type":"block-start","index":1,"blockType":"text"}}}
{"type":"text-chunks","seq0":23,"time0":1785097411114,"data":{"turn":1,"step":1,"index":1,"dt":[0,0,24,0],"texts":["child"," answer"," ","42","."]}}
{"type":"assistant/chunk","seq":28,"time":1785097411138,"data":{"turn":1,"step":1,"chunk":{"type":"block-end","index":0,"block":{"type":"reasoning","text":"The user wants me to reply with exactly \"child answer 42.\""}}}}
{"type":"assistant/chunk","seq":29,"time":1785097411138,"data":{"turn":1,"step":1,"chunk":{"type":"block-end","index":1,"block":{"type":"text","text":"child answer 42."}}}}
{"type":"assistant/chunk","seq":30,"time":1785460688799,"data":{"turn":1,"step":1,"chunk":{"type":"usage","usage":{"inputTokens":107,"outputTokens":20,"cacheReadTokens":1664,"reasoningTokens":14}}}}
{"type":"assistant/chunk","seq":31,"time":1785534755056,"data":{"turn":1,"step":1,"chunk":{"type":"finish","reason":{"kind":"stop"}}}}
{"type":"assistant/message","seq":32,"time":1785534755056,"data":{"turn":1,"step":1,"message":{"role":"assistant","content":[{"type":"reasoning","text":"The user wants me to reply with exactly \"child answer 42.\""},{"type":"text","text":"child answer 42."}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"2349c266-bebd-43c6-b434-c4c60809adfc"},"usage":{"inputTokens":107,"outputTokens":20,"cacheReadTokens":1664,"reasoningTokens":14}},"sourceEventSeqs":[7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31],"surfaceOp":"append"}
{"type":"step/end","seq":33,"time":1785534755056,"data":{"turn":1,"step":1}}
{"type":"turn/end","seq":34,"time":1785534755056,"data":{"turn":1,"reason":{"kind":"completed"}}}

View File

@@ -898,7 +898,7 @@ export const SERVICE_API: readonly ServiceApiEntry[] = [
},
{
signature: 'listChildren(parentSessionId: SessionId, signal?: AbortSignal): Promise<SubagentListEntry[]>',
jsDoc: '/**\n * Enumerate the parent\'s direct continuable children from the live-preferred\n * session corpus without loading or resuming an Agent. Session query supplies\n * lineage, candidate order, event reads, and live state; this service\n * interprets descriptors, status, and per-child diagnostics without consulting\n * Agent registrations, Activations, or providers.\n *\n * The trace and exact descriptor read receive `signal`; the full event-list\n * read has no signal parameter, so the scan rechecks cancellation around\n * every await and between candidates. Query rejections that settle after an\n * abort become a stable `SubagentError` with code `CANCELLED`.\n * @param parentSessionId - parent session whose direct children are listed.\n * @param signal - caller-owned cancellation forwarded where supported and\n * observed around every query await.\n * @returns children and per-child diagnostics in stable trace order.\n * @throws {@link SubagentError} when session query is unavailable or the\n * caller cancels the scan.\n */',
jsDoc: '/**\n * Enumerate the parent\'s direct session-backed subagents from the\n * live-preferred session corpus without loading or resuming an Agent. Session\n * query supplies lineage, candidate order, event reads, and live state; this\n * service interprets descriptor mode, activity, and per-child diagnostics\n * without consulting Agent registrations, Activations, or providers.\n *\n * The trace and exact descriptor read receive `signal`; the full event-list\n * read has no signal parameter, so the scan rechecks cancellation around\n * every await and between candidates. Query rejections that settle after an\n * abort become a stable `SubagentError` with code `CANCELLED`.\n * @param parentSessionId - parent session whose direct children are listed.\n * @param signal - caller-owned cancellation forwarded where supported and\n * observed around every query await.\n * @returns children and per-child diagnostics in stable trace order.\n * @throws {@link SubagentError} when session query is unavailable or the\n * caller cancels the scan.\n */',
},
{
signature: 'registerProvider(provider: SubagentProvider): () => void',
@@ -914,7 +914,7 @@ export const SERVICE_API: readonly ServiceApiEntry[] = [
},
{
signature: 'async start(name: string, request: SubagentStartRequest): Promise<SubagentRun>',
jsDoc: '/**\n * Establish a ready child on the named provider. Capability and semantic\n * checks run before delegation. Provider ownership lasts until its promise\n * fulfills; a rejection therefore has no run for the caller to dispose and\n * emits no run lifecycle events.\n * @param name - the provider to use.\n * @param request - child prompt, parent, signal, and optional capabilities.\n * @returns the ready holder-owned run.\n */',
jsDoc: '/**\n * Establish a ready child on the named provider. Capability and semantic\n * checks run before delegation. Provider ownership lasts until its promise\n * fulfills; a rejection therefore has no run for the caller to dispose and\n * emits no run lifecycle events.\n * @param name - the provider to use.\n * @param request - child label, prompt, parent, signal, and optional capabilities.\n * @returns the ready holder-owned run.\n */',
},
],
},
@@ -1813,7 +1813,11 @@ export const TYPE_API: readonly TypeApiEntry[] = [
},
{
name: 'ContinuableStartSpec',
declaration: 'export interface ContinuableStartSpec {\n readonly provider: string;\n readonly label: string;\n readonly request: Omit<SubagentStartRequest, \'signal\' | \'outputSchema\'>;\n readonly signal: AbortSignal;\n}',
declaration: 'export interface ContinuableStartSpec {\n readonly provider: string;\n readonly label: string;\n readonly request: Omit<SubagentStartRequest, \'label\' | \'signal\' | \'outputSchema\'>;\n readonly signal: AbortSignal;\n}',
},
{
name: 'ContinuableSubagentDescriptorData',
declaration: 'export interface ContinuableSubagentDescriptorData extends SubagentDescriptorBase {\n readonly mode: \'continuable\';\n readonly agentProvider?: string;\n readonly agentModel?: string;\n readonly persona?: string;\n readonly toolFilter?: ToolRestriction;\n}',
},
{
name: 'CreateAgentOptions',
@@ -2127,6 +2131,10 @@ export const TYPE_API: readonly TypeApiEntry[] = [
name: 'ObjectJsonSchema',
declaration: 'export type ObjectJsonSchema = JsonSchemaNode & {\n type: \'object\';\n};',
},
{
name: 'OneShotSubagentDescriptorData',
declaration: 'export interface OneShotSubagentDescriptorData extends SubagentDescriptorBase {\n readonly mode: \'one-shot\';\n}',
},
{
name: 'PermissionSelect',
declaration: 'export interface PermissionSelect {\n options: PresetOption[];\n currentValue: string;\n}',
@@ -2311,6 +2319,10 @@ export const TYPE_API: readonly TypeApiEntry[] = [
name: 'ResolvedRetryPolicy',
declaration: 'export type ResolvedRetryPolicy = ResolvedNormalRetryPolicy | ResolvedAlwaysRetryPolicy;',
},
{
name: 'ResolvedSubagentStartRequest',
declaration: 'export interface ResolvedSubagentStartRequest extends SubagentStartRequest {\n readonly descriptor: SubagentDescriptorData;\n}',
},
{
name: 'ResumeAgentOptions',
declaration: 'export interface ResumeAgentOptions {\n readonly resumeSessionId: SessionId;\n readonly agentOptions?: AgentOptions;\n readonly signal?: AbortSignal;\n readonly setup?: (agentCtx: Context) => Promise<void> | void;\n}',
@@ -2691,17 +2703,21 @@ export const TYPE_API: readonly TypeApiEntry[] = [
name: 'SubagentCapabilities',
declaration: 'export interface SubagentCapabilities {\n readonly outputSchema: boolean;\n readonly depthLimit: boolean;\n readonly toolFilter: boolean;\n readonly persona: boolean;\n}',
},
{
name: 'SubagentDescriptorData',
declaration: 'export type SubagentDescriptorData = OneShotSubagentDescriptorData | ContinuableSubagentDescriptorData;',
},
{
name: 'SubagentFollowupOptions',
declaration: 'export interface SubagentFollowupOptions {\n readonly source: MessageSource;\n readonly signal: AbortSignal;\n}',
},
{
name: 'SubagentListEntry',
declaration: 'export type SubagentListEntry = {\n readonly kind: \'child\';\n readonly id: SessionId;\n readonly label: string;\n readonly status: \'running\' | \'complete\';\n} | {\n readonly kind: \'diagnostic\';\n readonly id: SessionId;\n readonly reason: \'corrupt\' | \'unsupported\' | \'unavailable\';\n};',
declaration: 'export type SubagentListEntry = {\n readonly kind: \'child\';\n readonly id: SessionId;\n readonly label: string;\n readonly mode: \'one-shot\' | \'continuable\';\n readonly activity: \'running\' | \'inactive\';\n} | {\n readonly kind: \'diagnostic\';\n readonly id: SessionId;\n readonly reason: \'corrupt\' | \'unsupported\' | \'unavailable\';\n};',
},
{
name: 'SubagentProvider',
declaration: 'export interface SubagentProvider {\n readonly name: string;\n readonly capabilities: SubagentCapabilities;\n readonly inheritsParentContext: boolean;\n start(request: SubagentStartRequest): Promise<SubagentRun>;\n prepareContinuable?(request: ContinuableCreateRequest): Promise<ContinuableCreateSpec>;\n}',
declaration: 'export interface SubagentProvider {\n readonly name: string;\n readonly capabilities: SubagentCapabilities;\n readonly inheritsParentContext: boolean;\n start(request: ResolvedSubagentStartRequest): Promise<SubagentRun>;\n prepareContinuable?(request: ContinuableCreateRequest): Promise<ContinuableCreateSpec>;\n}',
},
{
name: 'SubagentResult',
@@ -2713,7 +2729,7 @@ export const TYPE_API: readonly TypeApiEntry[] = [
},
{
name: 'SubagentStartRequest',
declaration: 'export interface SubagentStartRequest {\n readonly prompt: ContentBlock[];\n readonly parent: Agent;\n readonly signal: AbortSignal;\n readonly agentOptions?: AgentOptions;\n readonly outputSchema?: ObjectJsonSchema;\n readonly maxDepth?: number;\n readonly toolFilter?: ToolRestriction;\n readonly persona?: string;\n}',
declaration: 'export interface SubagentStartRequest {\n readonly label: string;\n readonly prompt: ContentBlock[];\n readonly parent: Agent;\n readonly signal: AbortSignal;\n readonly agentOptions?: AgentOptions;\n readonly outputSchema?: ObjectJsonSchema;\n readonly maxDepth?: number;\n readonly toolFilter?: ToolRestriction;\n readonly persona?: string;\n}',
},
{
name: 'SubagentStopReason',

View File

@@ -11,7 +11,12 @@ import { accessSync, constants, statSync } from 'node:fs'
import { isAbsolute, resolve } from 'node:path'
import type { Context } from 'cordis'
import z from 'schemastery'
import type { SubagentCapabilities, SubagentProvider, SubagentStartRequest } from '@deepseek-ai/dsh-subagent'
import type {
ResolvedSubagentStartRequest,
SubagentCapabilities,
SubagentProvider,
SubagentStartRequest,
} from '@deepseek-ai/dsh-subagent'
import { type AcpRunSpec, DEFAULT_DISPOSE_EOF_GRACE_MS, DEFAULT_DISPOSE_GRACE_MS, type PermissionPolicy, startAcpRun } from './run.ts'
export const name = 'subagent-acp'
@@ -143,7 +148,7 @@ class AcpProvider implements SubagentProvider {
constructor(readonly name: string, private readonly ctx: Context, private readonly config: ResolvedConfig) {}
start(request: SubagentStartRequest) {
start(request: ResolvedSubagentStartRequest) {
const spec: AcpRunSpec = {
command: this.config.command,
args: this.config.args,

View File

@@ -64,6 +64,7 @@ describe.skipIf(!process.env.DEEPSEEK_API_KEY)('ACP backend with-key e2e (drive
})
const run = await ctx.subagents.start('acp', {
label: 'reply pong',
prompt: [{ type: 'text', text: 'Reply with exactly the word PONG and nothing else. Do not use any tools.' }],
parent: fakeParent,
signal: new AbortController().signal,
@@ -95,6 +96,7 @@ describe.skipIf(!process.env.DEEPSEEK_API_KEY)('ACP backend with-key e2e (drive
})
const run = await ctx.subagents.start('acp', {
label: 'write proof file',
prompt: [{ type: 'text', text:
'Use the bash tool to write the text ACP_CHILD_WAS_HERE into a file named proof.txt '
+ 'in the current directory. Then reply DONE.' }],

View File

@@ -27,7 +27,7 @@ const mockServer = fileURLToPath(new URL('./mock-acp-server.ts', import.meta.url
const fakeParent = { id: 'parent', session: { header: { cwd: process.cwd() } } } as unknown as Agent
function request(text = 'p', signal = new AbortController().signal) {
return { prompt: [{ type: 'text' as const, text }], parent: fakeParent, signal }
return { label: text, prompt: [{ type: 'text' as const, text }], parent: fakeParent, signal }
}
interface SetupEnv {
@@ -126,7 +126,9 @@ describe('child env layering (through the subprocess seam)', () => {
// explicit entry merges after it and the child must see the value.
const ctx = await setup({ MOCK_ECHO_ENV: 'DSH_ACP_TEST_FACT', DSH_ACP_TEST_FACT: 'managed' })
const parent = { id: 'parent', session: { header: { cwd: process.cwd() } } } as unknown as Agent
const run = await ctx.subagents.start('acp', { prompt: [{ type: 'text' as const, text: 'p' }], parent, signal: new AbortController().signal })
const run = await ctx.subagents.start('acp', {
label: 'p', prompt: [{ type: 'text' as const, text: 'p' }], parent, signal: new AbortController().signal,
})
const result = await run.result
await run.dispose()
const text = result.output.filter(b => b.type === 'text').map(b => (b as { text: string }).text).join('')
@@ -208,7 +210,9 @@ describe('cwd resolution', () => {
try {
const ctx = await setup({ MOCK_ECHO_CWD: '1' })
const parent = { id: 'parent', session: { header: { cwd: workdir } } } as unknown as Agent
const run = await ctx.subagents.start('acp', { prompt: [{ type: 'text' as const, text: 'p' }], parent, signal: new AbortController().signal })
const run = await ctx.subagents.start('acp', {
label: 'p', prompt: [{ type: 'text' as const, text: 'p' }], parent, signal: new AbortController().signal,
})
const result = await run.result
await run.dispose()
// Line 1: where the child process actually ran; line 2: the workspace the
@@ -229,7 +233,9 @@ describe('cwd resolution', () => {
// A command that would create the sentinel if the child were ever spawned.
await ctx.plugin(acp, { providerName: 'acp', command: 'touch', args: [sentinel], permission: 'reject', env: {} })
const parent = { id: 'parent', session: { header: {} } } as unknown as Agent
await expect(ctx.subagents.start('acp', { prompt: [{ type: 'text' as const, text: 'p' }], parent, signal: new AbortController().signal }))
await expect(ctx.subagents.start('acp', {
label: 'p', prompt: [{ type: 'text' as const, text: 'p' }], parent, signal: new AbortController().signal,
}))
.rejects.toThrow('no working directory')
// Resolution failed BEFORE the process boundary — nothing was launched.
expect(existsSync(sentinel)).toBe(false)
@@ -254,7 +260,9 @@ describe('cwd resolution', () => {
env: { MOCK_ECHO_CWD: '1' },
})
const parent = { id: 'parent', session: { header: { cwd: parentDir } } } as unknown as Agent
const run = await ctx.subagents.start('acp', { prompt: [{ type: 'text' as const, text: 'p' }], parent, signal: new AbortController().signal })
const run = await ctx.subagents.start('acp', {
label: 'p', prompt: [{ type: 'text' as const, text: 'p' }], parent, signal: new AbortController().signal,
})
const result = await run.result
await run.dispose()
expect(text(result.output)).toBe(`${configured}\n${configured}`)
@@ -350,7 +358,9 @@ describe('cwd resolution', () => {
// re-introduce the launch-directory dependency this resolution removes.
const ctx = await setup({})
const parent = { id: 'parent', session: { header: { cwd: 'relative/workspace' } } } as unknown as Agent
await expect(ctx.subagents.start('acp', { prompt: [{ type: 'text' as const, text: 'p' }], parent, signal: new AbortController().signal }))
await expect(ctx.subagents.start('acp', {
label: 'p', prompt: [{ type: 'text' as const, text: 'p' }], parent, signal: new AbortController().signal,
}))
.rejects.toThrow('must be an absolute path')
})
@@ -361,7 +371,9 @@ describe('cwd resolution', () => {
try {
const ctx = await setup({})
const parent = { id: 'parent', session: { header: { cwd: file } } } as unknown as Agent
await expect(ctx.subagents.start('acp', { prompt: [{ type: 'text' as const, text: 'p' }], parent, signal: new AbortController().signal }))
await expect(ctx.subagents.start('acp', {
label: 'p', prompt: [{ type: 'text' as const, text: 'p' }], parent, signal: new AbortController().signal,
}))
.rejects.toThrow('not an accessible directory')
} finally {
rmSync(tmp, { recursive: true, force: true })
@@ -377,7 +389,9 @@ describe('cwd resolution', () => {
await ctx.plugin(LocalSubprocessService)
await ctx.plugin(acp, { providerName: 'acp', command: 'touch', args: [sentinel], permission: 'reject', env: {} })
const parent = { id: 'parent', session: { header: { cwd: join(tmp, 'vanished') } } } as unknown as Agent
await expect(ctx.subagents.start('acp', { prompt: [{ type: 'text' as const, text: 'p' }], parent, signal: new AbortController().signal }))
await expect(ctx.subagents.start('acp', {
label: 'p', prompt: [{ type: 'text' as const, text: 'p' }], parent, signal: new AbortController().signal,
}))
.rejects.toThrow('not an accessible directory')
expect(existsSync(sentinel)).toBe(false)
} finally {

View File

@@ -30,7 +30,7 @@ const fakeRuntime = fileURLToPath(new URL('../../../sdk/sdk-client/tests/fake-ru
const fakeParent = { id: 'parent', session: { header: { cwd: process.cwd() } } } as unknown as Agent
function request(text = 'p', signal = new AbortController().signal) {
return { prompt: [{ type: 'text' as const, text }], parent: fakeParent, signal }
return { label: text, prompt: [{ type: 'text' as const, text }], parent: fakeParent, signal }
}
/** Mount the SDK backend pointed at the fake runtime, scripted by `fakeEnv`. */
@@ -441,7 +441,9 @@ describe('dsh-subagent-dsh-sdk provider', () => {
it('fails loud when neither config cwd nor parent session cwd exists', async () => {
const ctx = await setup()
const parent = { id: 'parent', session: { header: {} } } as unknown as Agent
await expect(ctx.subagents.start('dsh-sdk', { prompt: [{ type: 'text' as const, text: 'p' }], parent, signal: new AbortController().signal }))
await expect(ctx.subagents.start('dsh-sdk', {
label: 'p', prompt: [{ type: 'text' as const, text: 'p' }], parent, signal: new AbortController().signal,
}))
.rejects.toThrow('no working directory for the child')
await ctx.fiber.dispose()
})

View File

@@ -14,9 +14,9 @@ import type { Agent } from '@deepseek-ai/dsh-agent'
import type {
ContinuableCreateRequest,
ContinuableCreateSpec,
ResolvedSubagentStartRequest,
SubagentCapabilities,
SubagentProvider,
SubagentStartRequest,
} from '@deepseek-ai/dsh-subagent'
import { startInProcessRun } from '@deepseek-ai/dsh-subagent-inprocess'
@@ -65,7 +65,7 @@ class ForkProvider implements SubagentProvider {
constructor(readonly name: string) {}
start(request: SubagentStartRequest) {
start(request: ResolvedSubagentStartRequest) {
const seed = completedTurnPrefix(request.parent)
return startInProcessRun(request, {
// Only pass a seed when there's a completed turn to inherit; an empty seed

View File

@@ -22,8 +22,16 @@ async function mountInvariants(ctx: Context): Promise<void> {
await ctx.plugin(AgentLoopInvariant)
}
function start(ctx: Context, provider: string, request: Omit<SubagentStartRequest, 'signal'> & { signal?: AbortSignal }) {
return ctx.subagents.start(provider, { signal: request.signal ?? new AbortController().signal, ...request })
function start(
ctx: Context,
provider: string,
request: Omit<SubagentStartRequest, 'label' | 'signal'> & { label?: string; signal?: AbortSignal },
) {
return ctx.subagents.start(provider, {
label: request.label ?? 'child task',
signal: request.signal ?? new AbortController().signal,
...request,
})
}
/**

View File

@@ -25,8 +25,16 @@ async function mountInvariants(ctx: Context): Promise<void> {
await ctx.plugin(AgentLoopInvariant)
}
function start(ctx: Context, provider: string, request: Omit<SubagentStartRequest, 'signal'> & { signal?: AbortSignal }) {
return ctx.subagents.start(provider, { signal: request.signal ?? new AbortController().signal, ...request })
function start(
ctx: Context,
provider: string,
request: Omit<SubagentStartRequest, 'label' | 'signal'> & { label?: string; signal?: AbortSignal },
) {
return ctx.subagents.start(provider, {
label: request.label ?? 'child task',
signal: request.signal ?? new AbortController().signal,
...request,
})
}
/** A bare `stop` finish that streams no content → the turn ends `completed`

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 packages/subagent/subagent-inprocess/README.md
README.md: 0495b7cae003a8c280689c4bfdd991e0f6950569
README.zh.md: 2e512ffd281c6334db925c97b110934bbcc19eef
README.md: 800ba24b65cedbbff31008a94b45957d5a65657e
README.zh.md: f6e0bd6fcb2340d195469ad57e2e96941c684fdd

View File

@@ -11,7 +11,7 @@ This package is the shared run driver for the two in-process providers' one-shot
The driver follows this sequence:
1. Validate the parent depth and optional absolute `maxDepth`, then derive child depth as parent depth plus one and persist it in the child session header.
2. Mint a fresh child session id and call `parent.ctx.agents.create` directly, passing the optional fork seed and required request signal into the factory's creation transaction. During the unpublished setup window, install the requested persona, tool restriction, and structured-output runtime.
2. Mint a fresh child session id and call `parent.ctx.agents.create` directly, passing the optional fork seed and required request signal into the factory's creation transaction. During the unpublished setup window, install the requested persona, tool restriction, structured-output runtime, and a one-shot `agent/step` contribution that appends the resolved `subagent/descriptor` event after the initial `turn/start` and before the first request.
3. Publish the child, retain the returned `AgentHandle`, and drive one task with `child.followup(prompt)` followed by `child.whenIdle()`.
4. Read the child's own last assistant message and latest message-triggered turn reason, excluding the fork seed prefix so a seeded parent message is never mistaken for child output.

View File

@@ -11,7 +11,7 @@
驱动器按以下顺序运行:
1. 校验父 agent 深度和可选的绝对 `maxDepth`,然后把子 agent 深度推导为父 agent 深度加一,并将其持久化到子 agent 会话 header。
2. 生成全新的子 agent 会话 id并直接调用 `parent.ctx.agents.create`,把可选的 fork 初始内容和必需的请求信号传入工厂的创建事务。在未发布的设置窗口中,安装请求的 persona、工具限制结构化输出运行时。
2. 生成全新的子 agent 会话 id并直接调用 `parent.ctx.agents.create`,把可选的 fork 初始内容和必需的请求信号传入工厂的创建事务。在未发布的设置窗口中,安装请求的 persona、工具限制结构化输出运行时,以及一次性的 `agent/step` contribution该 contribution 会在初始 `turn/start` 之后、首次请求之前追加已解析的 `subagent/descriptor` 事件
3. 发布子 agent保留返回的 `AgentHandle`,并通过先调用 `child.followup(prompt)`、再调用 `child.whenIdle()` 来驱动一项任务。
4. 读取子 agent 自身最后一条 assistant 消息,以及由消息触发的最新轮次原因;排除 fork 初始内容前缀,确保作为初始内容的父 agent 消息绝不会被误认为子 agent 输出。

View File

@@ -24,9 +24,10 @@ import {
resolveChildDepth,
} from '@deepseek-ai/dsh-subagent'
import type {
ResolvedSubagentStartRequest,
SubagentDescriptorData,
SubagentResult,
SubagentRun,
SubagentStartRequest,
SubagentStopReason,
} from '@deepseek-ai/dsh-subagent'
// Type-only: make `ctx.get('sandboxPolicy')` / `ctx.get('approval')` resolve
@@ -72,16 +73,27 @@ function prePublicationAbort(): Error {
return new Error('subagent request was aborted before child publication')
}
/** Append one one-shot descriptor inside the child's initial turn before its first request. */
function attachDescriptorAppend(childCtx: Context, descriptor: SubagentDescriptorData): void {
let appended = false
childCtx.on('agent/step', (agent) => {
if (appended) return
appended = true
agent.session.append('subagent/descriptor', descriptor)
})
}
/**
* Establish and drive one in-process one-shot child. Fulfillment means the agent
* is already published in the registry; rejection means the agent factory's
* creation transaction and any partially-created child have reached quiescence.
* Every start appends its resolved descriptor inside the child's initial turn.
* @param request - the trusted typed start request, including its required signal.
* @param options - the optional fork seed.
* @returns a ready holder-owned run.
*/
export async function startInProcessRun(
request: SubagentStartRequest,
request: ResolvedSubagentStartRequest,
options: InProcessRunOptions,
): Promise<SubagentRun> {
assertSubagentMaxDepth(request.maxDepth)
@@ -116,6 +128,7 @@ export async function startInProcessRun(
if (request.outputSchema !== undefined) {
structured = attachStructuredRuntime(childCtx, request.outputSchema)
}
attachDescriptorAppend(childCtx, request.descriptor)
}
const handle = await parent.ctx.agents.create({

View File

@@ -14,6 +14,7 @@ import SandboxPolicyService, { setSandboxMode } from '@deepseek-ai/dsh-sandbox-p
import { SessionId, type SessionEvent } from '@deepseek-ai/dsh-session'
import * as ToolFs from '@deepseek-ai/dsh-tool-fs'
import ApprovalService, { setApprovalPolicy } from '@deepseek-ai/dsh-user-approval'
import { snapshotSubagentDescriptor } from '@deepseek-ai/dsh-subagent'
import { MockAdapter, textResponse, toolCallResponse } from '../../../core/agent-loop/tests/mock-adapter.ts'
import { startInProcessRun } from '../src/index.ts'
@@ -52,9 +53,15 @@ async function setupWalled(script: Script): Promise<{ ctx: Context; parent: Agen
function spawnRequest(parent: Agent) {
return {
label: 'child task',
prompt: [{ type: 'text' as const, text: 'child task' }],
parent,
signal: new AbortController().signal,
descriptor: snapshotSubagentDescriptor({
mode: 'one-shot',
provider: 'spawn',
label: 'child task',
}),
}
}

View File

@@ -8,7 +8,10 @@ import InvariantService from '@deepseek-ai/dsh-invariants'
import * as SessionInvariant from '@deepseek-ai/dsh-session/invariant'
import * as AgentInvariant from '@deepseek-ai/dsh-agent/invariant'
import * as AgentLoopInvariant from '@deepseek-ai/dsh-agent-loop/invariant'
import SubagentService, { type SubagentStartRequest } from '@deepseek-ai/dsh-subagent'
import SubagentService, {
type ResolvedSubagentStartRequest,
type SubagentStartRequest,
} from '@deepseek-ai/dsh-subagent'
import type { Config as ToolConfig, ObjectJsonSchema } from '@deepseek-ai/dsh-tools'
import { defineContentToolFixture, RUN_CODE_NAME } from '@deepseek-ai/dsh-tools'
import { MockAdapter, textResponse, toolCallResponse } from '../../../core/agent-loop/tests/mock-adapter.ts'
@@ -69,7 +72,7 @@ async function setup(script: Script, options: SetupOptions = {}) {
name: 'spawn',
capabilities: { outputSchema: true, depthLimit: true, toolFilter: false, persona: false },
inheritsParentContext: false,
start: (request: SubagentStartRequest) => startInProcessRun(request, {}),
start: (request: ResolvedSubagentStartRequest) => startInProcessRun(request, {}),
})
ctx.llm.registerAdapter(['mock'], adapter)
const parent = ctx.agentLoop.create(SessionId('parent'), { provider: 'mock', model: 'mock' })
@@ -78,6 +81,7 @@ async function setup(script: Script, options: SetupOptions = {}) {
function structuredRequest(parent: SubagentStartRequest['parent'], extra?: Partial<SubagentStartRequest>): SubagentStartRequest {
return {
label: 'produce the answer',
prompt: [{ type: 'text', text: 'produce the answer' }],
parent,
signal: new AbortController().signal,

View File

@@ -9,7 +9,7 @@ import InvariantService from '@deepseek-ai/dsh-invariants'
import * as SessionInvariant from '@deepseek-ai/dsh-session/invariant'
import * as AgentInvariant from '@deepseek-ai/dsh-agent/invariant'
import * as AgentLoopInvariant from '@deepseek-ai/dsh-agent-loop/invariant'
import SubagentService from '@deepseek-ai/dsh-subagent'
import SubagentService, { snapshotSubagentDescriptor } from '@deepseek-ai/dsh-subagent'
import { maxTokensResponse, MockAdapter, textResponse } from '../../../core/agent-loop/tests/mock-adapter.ts'
import { startInProcessRun } from '../src/index.ts'
@@ -35,7 +35,17 @@ async function setup(script: Script, parentOptions: Partial<AgentOptions> = {})
}
function request(parent: Agent, signal = new AbortController().signal) {
return { prompt: [{ type: 'text' as const, text: 'child task' }], parent, signal }
return {
label: 'child task',
prompt: [{ type: 'text' as const, text: 'child task' }],
parent,
signal,
descriptor: snapshotSubagentDescriptor({
mode: 'one-shot',
provider: 'test',
label: 'child task',
}),
}
}
function text(blocks: readonly { type: string; text?: string }[]): string {

View File

@@ -10,9 +10,9 @@ import type { Context } from 'cordis'
import z from 'schemastery'
import type {
ContinuableCreateSpec,
ResolvedSubagentStartRequest,
SubagentCapabilities,
SubagentProvider,
SubagentStartRequest,
} from '@deepseek-ai/dsh-subagent'
import { startInProcessRun } from '@deepseek-ai/dsh-subagent-inprocess'
@@ -45,7 +45,7 @@ class SpawnProvider implements SubagentProvider {
constructor(readonly name: string) {}
start(request: SubagentStartRequest) {
start(request: ResolvedSubagentStartRequest) {
// Fresh child: no seed. The shared driver mints ids, stamps cwd/lineage/
// depth, drives the one-shot (including the structured capture when the
// request carries an outputSchema), and maps the result.

View File

@@ -49,8 +49,16 @@ function text(blocks: { type: string; text?: string }[]): string {
return blocks.filter(b => b.type === 'text').map(b => b.text).join('')
}
function start(ctx: Context, provider: string, request: Omit<SubagentStartRequest, 'signal'> & { signal?: AbortSignal }) {
return ctx.subagents.start(provider, { signal: request.signal ?? new AbortController().signal, ...request })
function start(
ctx: Context,
provider: string,
request: Omit<SubagentStartRequest, 'label' | 'signal'> & { label?: string; signal?: AbortSignal },
) {
return ctx.subagents.start(provider, {
label: request.label ?? 'child task',
signal: request.signal ?? new AbortController().signal,
...request,
})
}
/** Invoke the child lifecycle effect while its parent-owned setup is still unpublished. */

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 packages/subagent/subagent/README.md
README.md: 0c93856bf973781254195b2f3869a54833bf83ac
README.zh.md: aa4c58d01582863e702404d1c8f3d32d3db7f534
README.md: df2582518e97bb38d124f8c33259c30b9d11d759
README.zh.md: d788f30f31d92c3fd919b62891e9b03624f455f7

View File

@@ -28,13 +28,13 @@ Multiple providers may coexist under different names. This lets a deployment exp
| `registerProvider(provider)` | Register one trusted same-process implementation by name. Registration is effect-scoped; removing it prevents new starts but does not revoke runs already returned to callers. Duplicate names fail loud. |
| `getProvider(name)` | Return the provider, or `undefined` when absent. |
| `list()` | Return provider names in insertion order. |
| `start(name, request)` | Validate an ordinary caller request, then await the provider until a real one-shot child is ready. Fulfillment returns a holder-owned `SubagentRun`; rejection means the provider has already cleaned every partial startup resource. Continuable children never enter through this operation. |
| `start(name, request)` | Validate an ordinary caller request, resolve its detached `one-shot` descriptor, then await the provider until a real one-shot child is ready. Fulfillment returns a holder-owned `SubagentRun`; rejection means the provider has already cleaned every partial startup resource. Continuable children never enter through this operation. |
| `startContinuable(spec)` | Establish one durable continuable child and deliver its initial prompt. Resolves with `{ childId, messageId }` when the child's inbox accepts that prompt, without waiting for the turn to start or for the message to reach the Session log; any earlier failure rejects with no ids and rolls the child back entirely. Requires `ctx.agents`, session persistence, and a provider with the `prepareContinuable` capability. |
| `followup(parent, childId, content, { source, signal })` | Deliver one later message from the exact live direct parent as the child's next FIFO turn, matching `Agent.followup()` terminology, and return the accepted `MessageId`. A resident child's inbox accepts it directly (waking a waiting Activation); an absent one cold-resumes from its persisted Session. Requires `ctx.agents`; cold resume also requires session persistence. |
| `drainContinuableDescendants(parents)` | Close admission below exact live host-owned parent Agents, stop only their visible continuable descendants, await materializations admitted below those roots through publication or rollback, then release the selected forests child-first. The cutoff lasts until each exact parent leaves the registry; unrelated parent forests and manager-wide admission remain live. |
| `listChildren(parentSessionId, signal?)` | List direct continuable children and per-child diagnostics in stable trace order without loading or resuming them. Requires session query; it does not require `ctx.agents` or the continuation manager. |
| `listChildren(parentSessionId, signal?)` | List direct session-backed subagents with their `one-shot`/`continuable` mode and `running`/`inactive` activity, plus per-child diagnostics, in stable trace order without loading or resuming them. Requires session query; it does not require `ctx.agents` or the continuation manager. |
`SubagentStartRequest.signal` is required and is the canonical cancellation channel for a one-shot `start`. An abort before publication makes `start()` reject after rollback; an abort after publication cancels the live child. The request may also select a model, require structured output, cap delegation depth, restrict child tools, or set a child persona. For a continuable start or follow-up, the caller signal owns lookup, materialization, and admission only until inbox acceptance; afterward the manager owns the Activation independently, so later caller cancellation neither cancels the accepted turn nor disposes the child.
`SubagentStartRequest.label` is the short durable display label for a session-backed child. `signal` is required and is the canonical cancellation channel for a one-shot `start`. An abort before publication makes `start()` reject after rollback; an abort after publication cancels the live child. The request may also select a model, require structured output, cap delegation depth, restrict child tools, or set a child persona. For a continuable start or follow-up, the caller signal owns lookup, materialization, and admission only until inbox acceptance; afterward the manager owns the Activation independently, so later caller cancellation neither cancels the accepted turn nor disposes the child.
Follow-up authority comes from the exact live direct parent recorded in the child's durable header. Cold resume checks that authority before reconstruction and again in the final no-await inbox-admission span, so a parent unregistered or replaced during materialization cannot authorize delivery. The `source` on a follow-up is durable provenance retained on the delivered message and grants no authority.
@@ -53,7 +53,7 @@ Continuable creation is the optional `SubagentProvider.prepareContinuable?()` me
## The durable descriptor
The seam owns the versioned `subagent/descriptor` session event vocabulary (`src/descriptor.ts`): `snapshotSubagentDescriptor()` validates and detaches the declared composition before the child session exists, and `foldSubagentDescriptor()` validates the complete current-version payload before recovering it from a loaded child log. Malformed current-version payloads fail before materialization; unsupported versions make the child non-resumable. The payload records the provider name, resolved child `agentOptions.provider`/`model`, and optional `persona`/`toolFilter` explicit fields, never the merge-extensible `AgentOptions` object, so an unrelated extension value cannot break continuation. It omits `subagentDepth` (the persisted header's `delegationDepth` is the monotone floor) and `outputSchema` (never captured for a continuable child). The event is log-only: no `surfaceOp`, absent from model history, and retained by the append-only log across compaction.
The seam owns the versioned `subagent/descriptor` session event vocabulary (`src/descriptor.ts`): `snapshotSubagentDescriptor()` validates and detaches the record before provider work, and `foldSubagentDescriptor()` validates the complete current-version payload before recovering it from a loaded child log. Every local session-backed start appends one descriptor with the durable creation label, provider name, and lifecycle `mode`. A `one-shot` descriptor stops there; a `continuable` descriptor additionally records resolved child `agentOptions.provider`/`model` and optional `persona`/`toolFilter` for cold resume. These are explicit fields, never the merge-extensible `AgentOptions` object, so an unrelated extension value cannot break continuation. The descriptor omits `subagentDepth` (the persisted header's `delegationDepth` is the monotone floor) and `outputSchema` (an activation's result contract). The event is log-only: no `surfaceOp`, absent from model history, and retained by the append-only log across compaction. Malformed current-version payloads are corrupt; unsupported versions cannot be classified by this runtime.
## Delegation depth
@@ -67,7 +67,7 @@ The seam owns the depth vocabulary shared by implementations and consumers: the
`SubagentRun.result` resolves to `{ output, structured?, stopReason }`. Child-level failures resolve with a non-`completed` reason; only an infrastructure fault that the seam cannot represent may reject. `dispose()` is idempotent, cancels remaining work, and waits for the child resources to quiesce.
A local run publishes an ordinary child agent/session before `start()` fulfills, returns that shared session id as `SubagentRun.id`, exposes the exact child as `SubagentRun.localAgent`, and records `request.parent.session.id` in the child's `parentSession` header. Remote providers instead mint a parent-scoped lifecycle id and return `localAgent: undefined`.
A local run publishes an ordinary child agent/session before `start()` fulfills, returns that shared session id as `SubagentRun.id`, exposes the exact child as `SubagentRun.localAgent`, records `request.parent.session.id` in the child's `parentSession` header, and appends the resolved descriptor inside its initial turn. Remote providers instead mint a parent-scoped lifecycle id and return `localAgent: undefined`; without a local child session, their one-shot runs are not part of trace-backed enumeration.
## Continuable children and Activations
@@ -89,7 +89,9 @@ Provider additions and removals also emit `subagent/provider-added` and `subagen
## Collection model
The model-facing tool collects synchronously by default: it awaits the child result and disposes the run before returning. One-shot background delegation registers a plain Task in the tool, whose generic status, collection, and cancellation tools own later interaction. Continuable background delegation calls `ctx.subagents.startContinuable()` and returns only the durable child id; the child owns its own turns from inbox acceptance, so there is no Task, no result promise, and no public subagent cancellation — a caller sends later work with the `send_message` follow-up tool, and the durable child Session remains the source of the child's detailed output. The continuation manager exists only while `ctx.agents` is available, and session persistence is resolved per continuation operation. Independently, `listChildren()` resolves session query and dynamically imports its optional runtime only when called, then interprets a read-only live-preferred scan without consulting the continuation manager, Agent registrations, Activations, or providers. It forwards the caller's signal to cancellable trace and exact-read operations, checks cancellation around the remaining event-list read, and reports every observed abort as `SubagentError` code `CANCELLED`. See the [background subagent tasks Agent Note](../../../.agents/notes/implemented/feature/2026-07-08-background-subagent-tasks.md), the [continuable background subagents Agent Note](../../../.agents/notes/implemented/feature/2026-07-21-continuable-background-subagents.md), the [durable catalog Agent Note](../../../.agents/notes/implemented/feature/2026-07-22-durable-subagent-catalog-and-list-agents.md), the [merged-service Agent Note](../../../.agents/notes/implemented/simplification/2026-07-26-merge-subagent-control-service.md), the [capability-seam Agent Note](../../../.agents/notes/implemented/feature/2026-06-21-subagent-capability-seam.md), and `src/types.ts` for the complete contracts.
The model-facing tool collects synchronously by default: it awaits the child result and disposes the run before returning. One-shot background delegation registers a plain Task in the tool, whose generic status, collection, and cancellation tools own later interaction. Continuable background delegation calls `ctx.subagents.startContinuable()` and returns only the durable child id; the child owns its own turns from inbox acceptance, so there is no Task, no result promise, and no public subagent cancellation — a caller sends later work with the `send_message` follow-up tool, and the durable child Session remains the source of the child's detailed output. The continuation manager exists only while `ctx.agents` is available, and session persistence is resolved per continuation operation. Independently, `listChildren()` resolves session query and dynamically imports its optional runtime only when called, then interprets a read-only live-preferred scan of all descriptor-bearing direct children without consulting the continuation manager, Agent registrations, Activations, or providers. Service consumers such as a UI can retain both modes; the model-facing `list_agents` tool projects only `continuable` entries and maps service activity to its existing `running`/`complete` vocabulary. The scan forwards the caller's signal to cancellable trace and exact-read operations, checks cancellation around the remaining event-list read, and reports every observed abort as `SubagentError` code `CANCELLED`. See the [background subagent tasks Agent Note](../../../.agents/notes/implemented/feature/2026-07-08-background-subagent-tasks.md), the [continuable background subagents Agent Note](../../../.agents/notes/implemented/feature/2026-07-21-continuable-background-subagents.md), the [durable catalog Agent Note](../../../.agents/notes/implemented/feature/2026-07-22-durable-subagent-catalog-and-list-agents.md), the [merged-service Agent Note](../../../.agents/notes/implemented/simplification/2026-07-26-merge-subagent-control-service.md), the [capability-seam Agent Note](../../../.agents/notes/implemented/feature/2026-06-21-subagent-capability-seam.md), and `src/types.ts` for the complete contracts.
Continuable Activations require final durability confirmation. One-shot runs retain best-effort session checkpointing, so a completed one-shot child is discoverable after disposal only when its session actually reached persistence; the service does not invent a catalog entry from Task history when that checkpoint is absent.
## Model Experience
@@ -101,9 +103,10 @@ No direct invalidation; the named consumers own any request-prefix changes.
## Known Limitations and Deferred Work
- **ACP children remain one-shot** — an ACP `prepareContinuable` requires persisting the remote session id in provider-specific descriptor data and a per-child continuation advertisement, since ACP `loadSession` support is negotiated per child rather than established by the method's presence. Remote providers also require a separate Activation ownership contract with equivalent authenticated control and child-first quiescence before they support continuable children.
- **ACP children remain one-shot and are not trace-enumerable** — an ACP run has no local child session in the parent's session corpus. An ACP `prepareContinuable` requires persisting the remote session id in provider-specific descriptor data and a per-child continuation advertisement, since ACP `loadSession` support is negotiated per child rather than established by the method's presence. Remote providers also require a separate Activation ownership contract with equivalent authenticated control and child-first quiescence before they support continuable children.
- **No report delivery** — the MVP exposes no `report` tool, child-to-parent content delivery, or automatic parent wakeup; a completed child turn leaves its output in the durable child Session until a caller inspects that transcript or submits another authorized turn.
- **No host-user continuation** — `followup()` requires the exact live direct parent. A future host adapter needs a concrete authenticated interaction before the seam gains a separate user capability.
- **No subagent steering** — every continuation message opens a later FIFO turn, so a parent cannot redirect a turn already underway; the manager stores no current-turn controller state.
- **Process-local residency** — the Activation inbox and ownership graph do not coordinate two harness processes; concurrent access to one persistence store still requires a durable mailbox and cross-process lease protocol.
- **No replay of accepted-but-unlogged messages** — only messages written to the child Session log are reconstructable with their admitted provenance. A crash may lose an accepted initial prompt or follow-up that never reached the log; a later authorized message can cold-resume the child, but the lost message is not replayed automatically.
- **Lifecycle events are observe-only** — a run-affecting `subagent/end` continuation or decision surface waits for a concrete consumer.

View File

@@ -28,13 +28,13 @@ subagent seam 允许一个 agent智能体通过具名提供方把工作委
| `registerProvider(provider)` | 按名称注册一个可信的同进程实现。注册受 effect 作用域约束;移除注册会阻止新的启动,但不会撤销已返回给调用方的运行。重复名称会立即失败。 |
| `getProvider(name)` | 返回提供方;不存在时返回 `undefined`。 |
| `list()` | 按插入顺序返回提供方名称。 |
| `start(name, request)` | 校验普通调用方请求,然后等待提供方,直到真实的一次性子 agent 就绪。兑现时返回由持有方拥有的 `SubagentRun`;拒绝表示提供方已清理所有局部启动资源。可继续子 agent 绝不通过此操作进入。 |
| `start(name, request)` | 校验普通调用方请求,解析其分离的 `one-shot` 描述符,然后等待提供方,直到真实的一次性子 agent 就绪。兑现时返回由持有方拥有的 `SubagentRun`;拒绝表示提供方已清理所有局部启动资源。可继续子 agent 绝不通过此操作进入。 |
| `startContinuable(spec)` | 建立一个持久化可继续子 agent并投递其初始提示词。子 agent 的 inbox 接受该提示词时,兑现为 `{ childId, messageId }`,无需等待轮次开始或消息写入 Session 日志;此前任何失败都会以无 id 拒绝,并完全回滚该子 agent。要求 `ctx.agents`、会话持久化以及具备 `prepareContinuable` 能力的提供方。 |
| `followup(parent, childId, content, { source, signal })` | 将来自确切在线直接父级的一条后续消息作为子 agent 的下一个 FIFO 轮次投递,术语与 `Agent.followup()` 一致,并返回被接受的 `MessageId`。驻留中的子 agent 由其 inbox 直接接受(唤醒处于 waiting 的 Activation不驻留的则从其持久化 Session 冷恢复。要求 `ctx.agents`;冷恢复还要求会话持久化。 |
| `drainContinuableDescendants(parents)` | 在由 host 确切拥有的在线 parent Agent 之下关闭准入,只停止其可见的可继续后代,等待在这些根之下已获准的物化过程完成发布或回滚,再按 child-first 顺序释放所选森林。该截止状态会持续到每个确切 parent 离开注册表;无关的 parent 森林和管理器全局准入保持在线。 |
| `listChildren(parentSessionId, signal?)` | 按稳定的追踪顺序列出直接可继续 child 及逐 child diagnostic且不会加载或恢复它们。要求会话查询不要求 `ctx.agents` 或继续执行管理器。 |
| `listChildren(parentSessionId, signal?)` | 按稳定的追踪顺序列出由会话支撑的直接 subagent包括其 `one-shot``continuable` 模式和 `running``inactive` 活动状态,以及逐 child diagnostic且不会加载或恢复它们。要求会话查询不要求 `ctx.agents` 或继续执行管理器。 |
`SubagentStartRequest.signal` 是必填项,也是一次性 `start` 的规范取消通道。发布前中止会使 `start()` 在回滚后拒绝;发布后中止会取消实时子 agent。请求还可以选择模型、要求结构化输出、限制委派深度、约束子 agent 工具或设置子 agent persona。对于可继续启动或后续操作调用方信号只在 inbox 接受之前掌管查找、物化和准入;此后由管理器独立拥有 Activation因此调用方后续取消既不会取消已接受的轮次也不会 dispose 子 agent。
`SubagentStartRequest.label` 是由会话支撑的 child 所使用的简短持久化显示标签。`signal` 是必填项,也是一次性 `start` 的规范取消通道。发布前中止会使 `start()` 在回滚后拒绝;发布后中止会取消实时子 agent。请求还可以选择模型、要求结构化输出、限制委派深度、约束子 agent 工具或设置子 agent persona。对于可继续启动或后续操作调用方信号只在 inbox 接受之前掌管查找、物化和准入;此后由管理器独立拥有 Activation因此调用方后续取消既不会取消已接受的轮次也不会 dispose 子 agent。
后续操作的权限来自子 agent 持久化 header 中记录的确切在线直接父级。冷恢复会在重建前检查该权限,并在最终无 await 的 inbox 准入区间再次检查,因此在物化期间被注销或替换的 parent 无法授权投递。后续操作上的 `source` 是保留在所投递消息上的持久化来源,不授予任何权限。
@@ -53,7 +53,7 @@ subagent seam 允许一个 agent智能体通过具名提供方把工作委
## 持久化描述符
该 seam 拥有版本化的 `subagent/descriptor` 会话事件词汇(`src/descriptor.ts``snapshotSubagentDescriptor()` 会在子 agent 会话存在之前校验并分离声明的组合配置`foldSubagentDescriptor()` 则会在从已加载子 agent 日志中恢复描述符之前,校验当前版本的完整 payload。格式错误的当前版本 payload 会在物化前失败;不受支持的版本会使子 agent 无法恢复。payload 记录提供方名称、已解析的子 agent `agentOptions.provider`/`model`,以及可选 `persona`/`toolFilter`这些是显式字段,绝不是可通过合并扩展的 `AgentOptions` 对象,因此无关的扩展值不会破坏继续执行。省略 `subagentDepth`(持久化 header 的 `delegationDepth` 是单调下界)和 `outputSchema`可继续子 agent 从不捕获它)。该事件只进入日志:不含 `surfaceOp`,不进入模型历史,并由仅追加日志跨压缩保留。
该 seam 拥有版本化的 `subagent/descriptor` 会话事件词汇(`src/descriptor.ts``snapshotSubagentDescriptor()` 会在提供方工作之前校验并分离记录`foldSubagentDescriptor()` 则会在从已加载子 agent 日志中恢复描述符之前,校验当前版本的完整 payload。每次由本地会话支撑的启动都会追加一个描述符,其中包含持久化创建标签、提供方名称与生命周期 `mode``one-shot` 描述符到此为止;`continuable` 描述符还会记录已解析的子 agent `agentOptions.provider``model`,以及用于从持久化存储恢复的可选 `persona``toolFilter`这些是显式字段,绝不是可通过合并扩展的 `AgentOptions` 对象,因此无关的扩展值不会破坏继续执行。描述符省略 `subagentDepth`(持久化 header 的 `delegationDepth` 是单调下界)和 `outputSchema`单次激活的结果契约)。该事件只进入日志:不含 `surfaceOp`,不进入模型历史,并由仅追加日志跨压缩保留。格式错误的当前版本 payload 属于损坏;本运行时无法对不受支持的版本进行分类。
## 委派深度
@@ -67,7 +67,7 @@ subagent seam 允许一个 agent智能体通过具名提供方把工作委
`SubagentRun.result` 兑现为 `{ output, structured?, stopReason }`。子 agent 级失败会以非 `completed` 原因兑现;只有 seam 无法表示的基础设施故障才可以拒绝。`dispose()` 是幂等的,会取消剩余工作,并等待子 agent 资源完全停稳。
本地运行会在 `start()` 兑现前发布普通的子 agent/会话,把该共享会话 id 作为 `SubagentRun.id` 返回,以 `SubagentRun.localAgent` 公开准确的子 agent`request.parent.session.id` 记录到子 agent 的 `parentSession` header。远程提供方则生成父级作用域的生命周期 id并返回 `localAgent: undefined`
本地运行会在 `start()` 兑现前发布普通的子 agent会话,把该共享会话 id 作为 `SubagentRun.id` 返回,以 `SubagentRun.localAgent` 公开准确的子 agent`request.parent.session.id` 记录到子 agent 的 `parentSession` header,并在其初始轮次内追加已解析的描述符。远程提供方则生成 parent 作用域的生命周期 id并返回 `localAgent: undefined`;由于没有本地 child 会话,其一次性运行不会进入基于追踪的枚举结果
## 可继续子 agent 与 Activation
@@ -89,7 +89,9 @@ subagent seam 允许一个 agent智能体通过具名提供方把工作委
## 收集模型
面向模型的工具默认同步收集:先等待子 agent 结果,再 dispose 运行,然后才返回。一次性后台委派会在工具中注册普通 Task其通用状态、收集和取消工具负责后续交互。可继续后台委派会调用 `ctx.subagents.startContinuable()`,只返回持久化子 agent id子 agent 自 inbox 接受起就拥有自己的轮次,因此没有 Task、没有结果 promise也没有公开的子 agent 取消操作——调用方通过 `send_message` 后续操作工具发送后续工作,而持久化子 agent Session 仍是子 agent 详细输出的来源。只有 `ctx.agents` 可用时,继续执行管理器才会存在,而会话持久化按每项继续执行操作解析。与此独立,`listChildren()` 只在被调用时解析会话查询并动态导入其可选运行时,然后解释只读、实时优先扫描结果且不查询继续执行管理器、Agent 注册信息、Activation 或提供方。会把调用方的取消信号转发可取消的追踪与精确读取操作,在其余事件列表读取的前后检查取消,并将每次检测到的中止报告为 `SubagentError` 错误码 `CANCELLED`。完整契约见[后台 subagent 任务 Agent Note](../../../.agents/notes/implemented/feature/2026-07-08-background-subagent-tasks.md)、[可继续后台 subagent Agent Note](../../../.agents/notes/implemented/feature/2026-07-21-continuable-background-subagents.md)、[持久化目录 Agent Note](../../../.agents/notes/implemented/feature/2026-07-22-durable-subagent-catalog-and-list-agents.md)、[服务合并 Agent Note](../../../.agents/notes/implemented/simplification/2026-07-26-merge-subagent-control-service.md)、[能力 seam Agent Note](../../../.agents/notes/implemented/feature/2026-06-21-subagent-capability-seam.md)和 `src/types.ts`
面向模型的工具默认同步收集:先等待子 agent 结果,再 dispose 运行,然后才返回。一次性后台委派会在工具中注册普通 Task其通用状态、收集和取消工具负责后续交互。可继续后台委派会调用 `ctx.subagents.startContinuable()`,只返回持久化子 agent id子 agent 自 inbox 接受起就拥有自己的轮次,因此没有 Task、没有结果 promise也没有公开的子 agent 取消操作——调用方通过 `send_message` 后续操作工具发送后续工作,而持久化子 agent Session 仍是子 agent 详细输出的来源。只有 `ctx.agents` 可用时,继续执行管理器才会存在,而会话持久化按每项继续执行操作解析。与此独立,`listChildren()` 只在被调用时解析会话查询并动态导入其可选运行时,然后解释对所有带描述符的直接 child 所作的只读、实时优先扫描且不查询继续执行管理器、Agent 注册信息、Activation 或提供方。UI 等服务消费方可以保留两种模式;面向模型的 `list_agents` 工具只投影 `continuable` 条目,并将服务活动状态映射到现有的 `running``complete` 词汇。扫描会把调用方的取消信号转发可取消的追踪与精确读取操作,在其余事件列表读取的前后检查取消,并将每次检测到的中止报告为 `SubagentError` 错误码 `CANCELLED`。完整契约见[后台 subagent 任务 Agent Note](../../../.agents/notes/implemented/feature/2026-07-08-background-subagent-tasks.md)、[可继续后台 subagent Agent Note](../../../.agents/notes/implemented/feature/2026-07-21-continuable-background-subagents.md)、[持久化目录 Agent Note](../../../.agents/notes/implemented/feature/2026-07-22-durable-subagent-catalog-and-list-agents.md)、[服务合并 Agent Note](../../../.agents/notes/implemented/simplification/2026-07-26-merge-subagent-control-service.md)、[能力 seam Agent Note](../../../.agents/notes/implemented/feature/2026-06-21-subagent-capability-seam.md)和 `src/types.ts`
可继续 Activation 要求最终持久性确认。一次性运行保留尽力执行的会话检查点,因此已完成的一次性 child 只有在其会话确实进入持久化存储时,才可在 dispose 后继续被发现;如果该检查点缺失,服务不会根据 Task 历史虚构目录条目。
## 模型体验
@@ -101,9 +103,10 @@ subagent seam 允许一个 agent智能体通过具名提供方把工作委
## 已知限制与延期工作
- **ACP 子 agent 仍为一次性**ACP 的 `prepareContinuable` 需要在提供方专用描述符数据中持久化远端会话 id并按子 agent 声明继续执行功能,因为 ACP 的 `loadSession` 支持按子 agent 协商,而不是通过方法是否存在来确定。远程提供方还需要一份独立的 Activation 所有权契约,具备等效的经认证控制和子先于父的停稳保证,才能支持可继续子 agent。
- **ACP 子 agent 仍为一次性,且无法通过追踪枚举**ACP 运行在 parent 会话语料中没有本地 child 会话。ACP 的 `prepareContinuable` 需要在提供方专用描述符数据中持久化远端会话 id并按子 agent 声明继续执行功能,因为 ACP 的 `loadSession` 支持按子 agent 协商,而不是通过方法是否存在来确定。远程提供方还需要一份独立的 Activation 所有权契约,具备等效的经认证控制和子先于父的停稳保证,才能支持可继续子 agent。
- **无 report 投递**MVP 不提供 `report` 工具、子到父的内容投递或自动唤醒父级;已完成的子 agent 轮次会把其输出留在持久化子 agent Session 中,直到调用方查看该 transcript 或提交另一个经授权的轮次。
- **无 host-user 继续执行**`followup()` 要求确切在线直接父级。未来 host 适配器需要具体的经认证交互,才能让该 seam 获得单独的用户能力。
- **无 subagent steering**:每条后续消息都会开启后续 FIFO 轮次,因此父级无法重定向已经在进行的轮次;管理器不保存任何当前轮次控制器状态。
- **驻留仅限进程内**Activation inbox 与所有权图不会在两个 harness 进程之间协调;对单个持久化存储的并发访问仍然需要持久化邮箱和跨进程租约协议。
- **不重放已接受但未记录的消息**:只有写入子 agent Session 日志的消息才能连同其被接受时的来源一起重建。崩溃可能丢失从未写入日志、已被接受的初始提示词或后续消息;此后一条经授权的消息可以冷恢复该子 agent但丢失的消息不会自动重放。
- **生命周期事件只供观察**:影响运行的 `subagent/end` 延续或决策接口仍需等待具体消费方。

View File

@@ -65,7 +65,7 @@ export interface ContinuableStartSpec {
* The delegation request. The manager reserves the stable child id, resolves
* the durable descriptor, and composes the child itself.
*/
readonly request: Omit<SubagentStartRequest, 'signal' | 'outputSchema'>
readonly request: Omit<SubagentStartRequest, 'label' | 'signal' | 'outputSchema'>
/** Caller cancellation, owning the operation only until inbox acceptance. */
readonly signal: AbortSignal
}
@@ -298,6 +298,7 @@ export class SubagentContinuationManager {
const agentProvider = request.agentOptions?.provider ?? parent.options.provider
const agentModel = request.agentOptions?.model ?? parent.options.model
const descriptor = snapshotSubagentDescriptor({
mode: 'continuable',
provider: spec.provider,
label: spec.label,
...agentProvider !== undefined ? { agentProvider } : {},
@@ -587,7 +588,7 @@ export class SubagentContinuationManager {
// which may carry an ANCESTOR's descriptor when the parent is itself a
// continuable child.
const descriptor = foldSubagentDescriptor(loaded.events.slice(loaded.meta.seedLength ?? 0))
if (descriptor === undefined) {
if (descriptor === undefined || descriptor.mode !== 'continuable') {
throw new SubagentError(
`subagent "${childId}" has no supported continuation state and cannot be resumed; `
+ 'do not retry send_message with this id',

View File

@@ -1,9 +1,9 @@
/**
* The durable continuable-child descriptor: the versioned, model-hidden
* `subagent/descriptor` session event that records a child's declared
* composition so a known child id can be cold-resumed after its run — and its
* process — are gone. Providers append it turn-enclosed in the child's initial
* turn; the continuation manager folds it back on resume.
* The durable subagent-child descriptor: the versioned, model-hidden
* `subagent/descriptor` session event that identifies every session-backed
* subagent and records whether it is one-shot or continuable. Continuable
* descriptors additionally preserve the declared composition required for
* cold resume. Providers append it turn-enclosed in the child's initial turn.
*
* The descriptor deliberately snapshots explicit fields rather than the
* merge-extensible `AgentOptions` object: an unrelated extension value cannot
@@ -23,11 +23,11 @@ import type { ToolRestriction } from '@deepseek-ai/dsh-tools'
declare module '@deepseek-ai/dsh-session' {
interface SessionEventMap {
/**
* Durable declared composition of a continuable subagent child, appended
* once by the establishing provider inside the child's initial turn,
* before its first request. Log-only: it carries no `surfaceOp`, never
* enters model history, and the append-only log retains it when
* compaction replaces surface history.
* Durable identity and lifecycle mode of a session-backed subagent child,
* appended once by the establishing provider inside the child's initial
* turn, before its first request. Continuable records also carry their
* resumable composition. Log-only: it carries no `surfaceOp`, never enters
* model history, and survives compaction.
*/
'subagent/descriptor': SubagentDescriptorData
}
@@ -39,12 +39,14 @@ declare module '@deepseek-ai/dsh-session' {
* Supporting another composition input is a deliberate version change, never
* an implicit extra field.
*/
export const SUBAGENT_DESCRIPTOR_VERSION = 1
export const SUBAGENT_DESCRIPTOR_VERSION = 2
/** The `subagent/descriptor` event payload — a continuable child's declared composition. */
export interface SubagentDescriptorData {
/** Fields shared by every supported `subagent/descriptor` payload. */
interface SubagentDescriptorBase {
/** Descriptor format version ({@link SUBAGENT_DESCRIPTOR_VERSION}). */
readonly version: number
/** Whether the child is a terminal one-shot run or a resumable conversation. */
readonly mode: 'one-shot' | 'continuable'
/** The `ctx.subagents` provider name that established the child. */
readonly provider: string
/**
@@ -53,6 +55,16 @@ export interface SubagentDescriptorData {
* replaying parent tool results or exposing the child prompt.
*/
readonly label: string
}
/** A session-backed subagent that cannot be cold-resumed after its run. */
export interface OneShotSubagentDescriptorData extends SubagentDescriptorBase {
readonly mode: 'one-shot'
}
/** A session-backed subagent whose declared composition supports cold resume. */
export interface ContinuableSubagentDescriptorData extends SubagentDescriptorBase {
readonly mode: 'continuable'
/** Resolved child `agentOptions.provider`, when one was declared. */
readonly agentProvider?: string
/** Resolved child `agentOptions.model`, when one was declared. */
@@ -63,12 +75,29 @@ export interface SubagentDescriptorData {
readonly toolFilter?: ToolRestriction
}
/** Inputs {@link snapshotSubagentDescriptor} validates and detaches. */
export interface SubagentDescriptorInput {
/** The supported durable subagent identity and optional continuation composition. */
export type SubagentDescriptorData =
| OneShotSubagentDescriptorData
| ContinuableSubagentDescriptorData
/** Fields shared by descriptor snapshot inputs. */
interface SubagentDescriptorInputBase {
/** Whether the child is a terminal one-shot run or a resumable conversation. */
readonly mode: 'one-shot' | 'continuable'
/** The `ctx.subagents` provider name that will establish the child. */
readonly provider: string
/** The initial delegation's short `description`, the durable creation label. */
readonly label: string
}
/** Input for a one-shot child's durable identity. */
export interface OneShotSubagentDescriptorInput extends SubagentDescriptorInputBase {
readonly mode: 'one-shot'
}
/** Input for a continuable child's durable identity and resumable composition. */
export interface ContinuableSubagentDescriptorInput extends SubagentDescriptorInputBase {
readonly mode: 'continuable'
/** Requested child `agentOptions.provider`. */
readonly agentProvider?: string
/** Requested child `agentOptions.model`. */
@@ -79,10 +108,20 @@ export interface SubagentDescriptorInput {
readonly toolFilter?: ToolRestriction
}
const DESCRIPTOR_KEYS = new Set([
/** Inputs {@link snapshotSubagentDescriptor} validates and detaches. */
export type SubagentDescriptorInput =
| OneShotSubagentDescriptorInput
| ContinuableSubagentDescriptorInput
const DESCRIPTOR_BASE_KEYS = [
'version',
'mode',
'provider',
'label',
] as const
const ONE_SHOT_DESCRIPTOR_KEYS = new Set(DESCRIPTOR_BASE_KEYS)
const CONTINUABLE_DESCRIPTOR_KEYS = new Set([
...DESCRIPTOR_BASE_KEYS,
'agentProvider',
'agentModel',
'persona',
@@ -155,7 +194,15 @@ function parseSubagentDescriptor(value: unknown): SubagentDescriptorData | undef
}
if (version !== SUBAGENT_DESCRIPTOR_VERSION) return undefined
assertKnownKeys(value, DESCRIPTOR_KEYS, 'payload')
const mode = value['mode']
if (mode !== 'one-shot' && mode !== 'continuable') {
throw new Error('persisted subagent descriptor mode must be "one-shot" or "continuable"')
}
assertKnownKeys(
value,
mode === 'one-shot' ? ONE_SHOT_DESCRIPTOR_KEYS : CONTINUABLE_DESCRIPTOR_KEYS,
'payload',
)
const provider = value['provider']
if (typeof provider !== 'string') {
throw new Error('persisted subagent descriptor provider must be a string')
@@ -164,6 +211,14 @@ function parseSubagentDescriptor(value: unknown): SubagentDescriptorData | undef
if (typeof label !== 'string') {
throw new Error('persisted subagent descriptor label must be a string')
}
if (mode === 'one-shot') {
return {
version: SUBAGENT_DESCRIPTOR_VERSION,
mode,
provider,
label,
}
}
const agentProvider = optionalString(value, 'agentProvider')
const agentModel = optionalString(value, 'agentModel')
const persona = optionalString(value, 'persona')
@@ -172,6 +227,7 @@ function parseSubagentDescriptor(value: unknown): SubagentDescriptorData | undef
: undefined
return {
version: SUBAGENT_DESCRIPTOR_VERSION,
mode,
provider,
label,
...agentProvider !== undefined ? { agentProvider } : {},
@@ -190,15 +246,32 @@ function parseSubagentDescriptor(value: unknown): SubagentDescriptorData | undef
* @returns the versioned, detached descriptor payload.
* @throws when a field is not losslessly JSON-serializable.
*/
export function snapshotSubagentDescriptor(
input: OneShotSubagentDescriptorInput,
): OneShotSubagentDescriptorData
/**
* Validate and detach a continuable descriptor input.
* @param input - the caller-collected continuable composition fields.
* @returns the versioned, detached continuable descriptor payload.
* @throws when a field is not losslessly JSON-serializable.
*/
export function snapshotSubagentDescriptor(
input: ContinuableSubagentDescriptorInput,
): ContinuableSubagentDescriptorData
export function snapshotSubagentDescriptor(input: SubagentDescriptorInput): SubagentDescriptorData {
const candidate: SubagentDescriptorData = {
version: SUBAGENT_DESCRIPTOR_VERSION,
mode: input.mode,
provider: input.provider,
label: input.label,
...input.agentProvider !== undefined ? { agentProvider: input.agentProvider } : {},
...input.agentModel !== undefined ? { agentModel: input.agentModel } : {},
...input.persona !== undefined ? { persona: input.persona } : {},
...input.toolFilter !== undefined ? { toolFilter: input.toolFilter } : {},
...input.mode === 'continuable'
? {
...input.agentProvider !== undefined ? { agentProvider: input.agentProvider } : {},
...input.agentModel !== undefined ? { agentModel: input.agentModel } : {},
...input.persona !== undefined ? { persona: input.persona } : {},
...input.toolFilter !== undefined ? { toolFilter: input.toolFilter } : {},
}
: {},
}
const snapshot = snapshotJsonValue(candidate)
if (snapshot === undefined) {
@@ -214,8 +287,8 @@ export function snapshotSubagentDescriptor(input: SubagentDescriptorInput): Suba
* composition.
* @param events - the loaded child session events.
* @returns the descriptor, or `undefined` when the log has none or its
* version is not {@link SUBAGENT_DESCRIPTOR_VERSION} (the child is not
* resumable by this runtime).
* version is not {@link SUBAGENT_DESCRIPTOR_VERSION} (the child cannot be
* classified by this runtime).
* @throws when a current-version persisted payload does not match its complete
* declared schema.
*/

View File

@@ -41,6 +41,7 @@ import type { SessionId } from '@deepseek-ai/dsh-session'
import type {
ContinuableCreateRequest,
ContinuableCreateSpec,
ResolvedSubagentStartRequest,
SubagentCapabilities,
SubagentProvider,
SubagentRun,
@@ -60,12 +61,14 @@ import type {
} from './continuation.ts'
import { listChildren as listSubagentChildren } from './list-children.ts'
import type { SubagentListEntry } from './list-children.ts'
import { snapshotSubagentDescriptor } from './descriptor.ts'
export * from './out-of-process.ts'
export { SubagentRunId } from './types.ts'
export type {
ContinuableCreateRequest,
ContinuableCreateSpec,
ResolvedSubagentStartRequest,
SubagentCapabilities,
SubagentProvider,
SubagentResult,
@@ -79,7 +82,14 @@ export {
snapshotSubagentDescriptor,
SUBAGENT_DESCRIPTOR_VERSION,
} from './descriptor.ts'
export type { SubagentDescriptorData, SubagentDescriptorInput } from './descriptor.ts'
export type {
ContinuableSubagentDescriptorData,
ContinuableSubagentDescriptorInput,
OneShotSubagentDescriptorData,
OneShotSubagentDescriptorInput,
SubagentDescriptorData,
SubagentDescriptorInput,
} from './descriptor.ts'
export { seedDescriptorTurn } from './descriptor-seed.ts'
export { SubagentError } from './error.ts'
export { settleRun } from './run-settlement.ts'
@@ -224,11 +234,11 @@ export class SubagentService extends Service {
}
/**
* Enumerate the parent's direct continuable children from the live-preferred
* session corpus without loading or resuming an Agent. Session query supplies
* lineage, candidate order, event reads, and live state; this service
* interprets descriptors, status, and per-child diagnostics without consulting
* Agent registrations, Activations, or providers.
* Enumerate the parent's direct session-backed subagents from the
* live-preferred session corpus without loading or resuming an Agent. Session
* query supplies lineage, candidate order, event reads, and live state; this
* service interprets descriptor mode, activity, and per-child diagnostics
* without consulting Agent registrations, Activations, or providers.
*
* The trace and exact descriptor read receive `signal`; the full event-list
* read has no signal parameter, so the scan rechecks cancellation around
@@ -293,7 +303,7 @@ export class SubagentService extends Service {
* fulfills; a rejection therefore has no run for the caller to dispose and
* emits no run lifecycle events.
* @param name - the provider to use.
* @param request - child prompt, parent, signal, and optional capabilities.
* @param request - child label, prompt, parent, signal, and optional capabilities.
* @returns the ready holder-owned run.
*/
async start(name: string, request: SubagentStartRequest): Promise<SubagentRun> {
@@ -301,7 +311,13 @@ export class SubagentService extends Service {
this.assertCapabilities(provider, request)
assertSubagentMaxDepth(request.maxDepth)
if (request.outputSchema !== undefined) assertObjectJsonSchema(request.outputSchema)
return observeRun(this.emitLifecycle, name, request.parent, await provider.start(request))
const descriptor = snapshotSubagentDescriptor({
mode: 'one-shot',
provider: name,
label: request.label,
})
const resolved: ResolvedSubagentStartRequest = { ...request, descriptor }
return observeRun(this.emitLifecycle, name, request.parent, await provider.start(resolved))
}
/**

View File

@@ -1,7 +1,8 @@
/**
* Read-only interpretation of session-query lineage as durable subagent
* children. The module owns no catalog state and does not consult Activation,
* Agent-registry, continuation-manager, or provider state.
* Agent-registry, continuation-manager, or provider state. A child's
* descriptor distinguishes one-shot work from a continuable conversation.
*
* @module @deepseek-ai/dsh-subagent
*/
@@ -31,14 +32,15 @@ export type SubagentListEntry =
readonly id: SessionId
/** The durable creation label from the child's descriptor. */
readonly label: string
/** Lifecycle policy declared when the child was created. */
readonly mode: 'one-shot' | 'continuable'
/**
* Corpus snapshot status: `running` means the logical record is live in
* `ctx.sessions`; `complete` means it exists only in persistence and
* `send_message` may materialize another Activation. Neither encodes a durable
* outcome, and a listed `running` child may still reject delivery as an
* ownership conflict.
* Corpus snapshot activity: `running` means the logical record is live in
* `ctx.sessions`; `inactive` means it exists only in persistence. Neither
* encodes a durable outcome, and a continuable child may still reject
* delivery as an ownership conflict.
*/
readonly status: 'running' | 'complete'
readonly activity: 'running' | 'inactive'
}
| {
readonly kind: 'diagnostic'
@@ -54,7 +56,7 @@ export type SubagentListEntry =
}
/**
* Interpret one parent's direct session descendants as continuable subagents
* Interpret one parent's direct session descendants as session-backed subagents
* without loading or resuming an Agent.
* @param ctx - context carrying the optional session-query service.
* @param parentSessionId - parent session whose direct children are listed.
@@ -108,7 +110,7 @@ async function inspectChild(
try {
const records = await runListingQuery(() => query.listEvents(childId), signal)
// Only the child's own suffix: a fork seed may replay an ancestor's
// descriptor without making the fork itself a continuable subagent.
// descriptor without making the fork itself a subagent.
const seedLength = candidate.header.seedLength ?? 0
const descriptorSeqs = records
.filter(record => record.seq >= seedLength && record.type === 'subagent/descriptor')
@@ -141,7 +143,8 @@ async function inspectChild(
kind: 'child',
id: childId,
label: descriptor.label,
status: candidate.live ? 'running' : 'complete',
mode: descriptor.mode,
activity: candidate.live ? 'running' : 'inactive',
}
} catch (error: unknown) {
const reason = perChildDiagnosticReason(error, queryRuntime.SessionQueryError)

View File

@@ -14,6 +14,7 @@ import type { Branded } from '@deepseek-ai/dsh-brand'
import type { ContentBlock } from '@deepseek-ai/dsh-llm'
import type { SessionEvent, SessionId } from '@deepseek-ai/dsh-session'
import type { ObjectJsonSchema, ToolRestriction } from '@deepseek-ai/dsh-tools'
import type { SubagentDescriptorData } from './descriptor.ts'
/** Identifies one accepted subagent run across its lifecycle event pair. */
export type SubagentRunId = Branded<'SubagentRunId'>
@@ -88,9 +89,12 @@ export interface SubagentCapabilities {
* What a caller asks for when starting a ONE-SHOT subagent. The tool layer
* builds this from the model's `{ description, prompt }` plus its own config;
* the service validates {@link SubagentCapabilities} against the named provider
* before dispatching to {@link SubagentProvider.start}.
* and resolves the durable descriptor before dispatching to
* {@link SubagentProvider.start}.
*/
export interface SubagentStartRequest {
/** Short display label persisted with a session-backed child. */
readonly label: string
/** Content delivered as the child's user message. */
readonly prompt: ContentBlock[]
/**
@@ -139,6 +143,15 @@ export interface SubagentStartRequest {
readonly persona?: string
}
/**
* Provider-facing one-shot request after {@link SubagentService.start} resolves
* the durable child descriptor.
*/
export interface ResolvedSubagentStartRequest extends SubagentStartRequest {
/** Detached descriptor a session-backed provider persists in the child log. */
readonly descriptor: SubagentDescriptorData
}
/**
* What the continuation manager asks a provider for while materializing one
* continuable child's FIRST activation. The manager has already reserved the
@@ -268,13 +281,13 @@ export interface SubagentProvider {
/**
* Establish a ONE-SHOT child and return its handle only after publication.
* The service has already validated that every requested start-time
* capability is supported, so an implementation may assume e.g.
* `request.maxDepth` is honorable when present. If setup fails or
* `request.signal` aborts before fulfillment, the provider owns and cleans
* all partial resources before this promise rejects. Ownership transfers to
* the caller only on fulfillment.
* capability is supported and resolved `request.descriptor`, so a
* session-backed implementation appends that descriptor inside the child's
* initial turn. If setup fails or `request.signal` aborts before fulfillment,
* the provider owns and cleans all partial resources before this promise
* rejects. Ownership transfers to the caller only on fulfillment.
*/
start(request: SubagentStartRequest): Promise<SubagentRun>
start(request: ResolvedSubagentStartRequest): Promise<SubagentRun>
/**
* OPTIONAL (continuable-creation capability): contribute the detached
* creation inputs that distinguish this provider's continuable children —

View File

@@ -201,7 +201,9 @@ describe('SubagentService.startContinuable', () => {
const descriptor = loaded.events[descriptorIndex] as SessionEvent<'subagent/descriptor'>
expect(descriptor.data).toEqual({
version: SUBAGENT_DESCRIPTOR_VERSION,
mode: 'continuable',
provider: 'spawn',
label: 'child task',
agentProvider: 'mock',
agentModel: 'mock',
})
@@ -294,7 +296,9 @@ describe('SubagentService.startContinuable', () => {
expect(descriptor?.data).toEqual({
version: SUBAGENT_DESCRIPTOR_VERSION,
mode: 'continuable',
provider: 'spawn',
label: 'child task',
})
await drainManager(ctx)
})
@@ -326,7 +330,9 @@ describe('SubagentService.startContinuable', () => {
expect(child.session.events.find(event => event.type === 'subagent/descriptor')?.data)
.toEqual({
version: SUBAGENT_DESCRIPTOR_VERSION,
mode: 'continuable',
provider: 'spawn',
label: 'child task',
toolFilter: { deny: ['noop'] },
})
await drainManager(ctx)
@@ -526,8 +532,9 @@ describe('SubagentService.followup residency routing', () => {
it('reports an unresumable child whose persisted log has no supported descriptor', async () => {
const { ctx, parent } = await setup([textResponse('one shot')])
// A ONE-SHOT child persists a log but never seeds a descriptor.
// A one-shot child has durable identity but no supported continuation state.
const run = await ctx.subagents.start('spawn', {
label: 'one-shot work',
prompt: message('one-shot work'),
parent,
signal: testSignal,
@@ -774,6 +781,7 @@ describe('continuable durability and teardown', () => {
])
const { ctx, parent } = await setupWith(adapter)
const run = await ctx.subagents.start('spawn', {
label: 'one-shot task',
prompt: message('one-shot task'),
parent,
signal: testSignal,
@@ -1374,6 +1382,7 @@ describe('continuable public surface', () => {
it('keeps one-shot runs free of a steering capability', async () => {
const { ctx, parent } = await setup([textResponse('one shot')])
const run = await ctx.subagents.start('spawn', {
label: 'one-shot work',
prompt: message('one-shot work'),
parent,
signal: testSignal,

View File

@@ -98,7 +98,7 @@ function childEvents(descriptor: unknown): SessionEvent[] {
}
function descriptorPayload(label: string, version = SUBAGENT_DESCRIPTOR_VERSION) {
return { version, provider: 'spawn', label }
return { version, mode: 'continuable' as const, provider: 'spawn', label }
}
describe('SubagentService.listChildren', () => {
@@ -121,7 +121,7 @@ describe('SubagentService.listChildren', () => {
child.append('subagent/descriptor', descriptorPayload('query-only child'))
await expect(ctx.subagents.listChildren(parentId)).resolves.toEqual([
{ kind: 'child', id: childId, label: 'query-only child', status: 'running' },
{ kind: 'child', id: childId, label: 'query-only child', mode: 'continuable', activity: 'running' },
])
})
@@ -132,15 +132,46 @@ describe('SubagentService.listChildren', () => {
)
})
it('lists a persisted continuable child as complete with its durable label', async () => {
it('lists a persisted continuable child as inactive with its durable label', async () => {
const { ctx, parent } = await setup([textResponse('done')])
const childId = await startChild(ctx, parent, 'summarize the doc')
const entries = await ctx.subagents.listChildren(parent.id)
expect(entries).toEqual([
{ kind: 'child', id: childId, label: 'summarize the doc', status: 'complete' },
{ kind: 'child', id: childId, label: 'summarize the doc', mode: 'continuable', activity: 'inactive' },
])
})
it('lists one-shot and continuable children from the same trace', async () => {
const { ctx, parent } = await setup([textResponse('once'), textResponse('again')])
const oneShot = await ctx.subagents.start('spawn', {
label: 'one-shot child',
prompt: [{ type: 'text', text: 'finish once' }],
parent,
signal: new AbortController().signal,
})
const oneShotId = oneShot.id
await oneShot.result
await oneShot.dispose()
const continuableId = await startChild(ctx, parent, 'continuable child')
const entries = await ctx.subagents.listChildren(parent.id)
expect(entries).toHaveLength(2)
expect(entries).toContainEqual({
kind: 'child',
id: oneShotId,
label: 'one-shot child',
mode: 'one-shot',
activity: 'inactive',
})
expect(entries).toContainEqual({
kind: 'child',
id: continuableId,
label: 'continuable child',
mode: 'continuable',
activity: 'inactive',
})
})
it('accepts a persisted (non-live) parent target after restart', async () => {
const { ctx } = await setup([])
// A parent that exists only in persistence — the restart shape.
@@ -159,7 +190,7 @@ describe('SubagentService.listChildren', () => {
}, childEvents(descriptorPayload('persisted parent case')))
const entries = await ctx.subagents.listChildren(coldParent)
expect(entries).toEqual([
{ kind: 'child', id: childId, label: 'persisted parent case', status: 'complete' },
{ kind: 'child', id: childId, label: 'persisted parent case', mode: 'continuable', activity: 'inactive' },
])
})
@@ -197,8 +228,12 @@ describe('SubagentService.listChildren', () => {
live.append('turn/start', { turn: 1, trigger: { kind: 'message', source: { kind: 'user' } } })
live.append('subagent/descriptor', descriptorPayload('live child'))
const entries = await ctx.subagents.listChildren(parent.id)
expect(entries).toContainEqual({ kind: 'child', id: settled, label: 'settled child', status: 'complete' })
expect(entries).toContainEqual({ kind: 'child', id: liveId, label: 'live child', status: 'running' })
expect(entries).toContainEqual({
kind: 'child', id: settled, label: 'settled child', mode: 'continuable', activity: 'inactive',
})
expect(entries).toContainEqual({
kind: 'child', id: liveId, label: 'live child', mode: 'continuable', activity: 'running',
})
})
it('diagnoses duplicate descriptors as corrupt without hiding healthy siblings', async () => {
@@ -217,7 +252,9 @@ describe('SubagentService.listChildren', () => {
}, events)
const entries = await ctx.subagents.listChildren(parent.id)
expect(entries).toContainEqual({ kind: 'diagnostic', id: corrupt, reason: 'corrupt' })
expect(entries).toContainEqual({ kind: 'child', id: healthy, label: 'healthy sibling', status: 'complete' })
expect(entries).toContainEqual({
kind: 'child', id: healthy, label: 'healthy sibling', mode: 'continuable', activity: 'inactive',
})
})
it('diagnoses an invalid child event surface as corrupt', async () => {
@@ -244,7 +281,7 @@ describe('SubagentService.listChildren', () => {
const { ctx, parent } = await setup([])
const malformed = await authorChild(ctx, '00000000-0000-4000-8000-0000000000ff', {
parentSession: parent.id,
}, childEvents({ version: SUBAGENT_DESCRIPTOR_VERSION, provider: 7 }))
}, childEvents({ version: SUBAGENT_DESCRIPTOR_VERSION, mode: 'continuable', provider: 7 }))
const entries = await ctx.subagents.listChildren(parent.id)
expect(entries).toEqual([{ kind: 'diagnostic', id: malformed, reason: 'corrupt' }])
})
@@ -275,10 +312,15 @@ describe('SubagentService.listChildren', () => {
const { ctx, parent } = await setup([])
const foreign = await authorChild(ctx, '00000000-0000-4000-8000-0000000000bb', {
parentSession: parent.id,
}, childEvents({ version: SUBAGENT_DESCRIPTOR_VERSION, provider: 'not-mounted', label: 'orphan provider' }))
}, childEvents({
version: SUBAGENT_DESCRIPTOR_VERSION,
mode: 'continuable',
provider: 'not-mounted',
label: 'orphan provider',
}))
const entries = await ctx.subagents.listChildren(parent.id)
expect(entries).toEqual([
{ kind: 'child', id: foreign, label: 'orphan provider', status: 'complete' },
{ kind: 'child', id: foreign, label: 'orphan provider', mode: 'continuable', activity: 'inactive' },
])
})
@@ -393,8 +435,8 @@ describe('SubagentService.listChildren', () => {
}, compactedEvents)
const entries = await ctx.subagents.listChildren(parent.id)
expect(entries).toEqual([
{ kind: 'child', id: plain, label: 'twin child', status: 'complete' },
{ kind: 'child', id: compacted, label: 'twin child', status: 'complete' },
{ kind: 'child', id: plain, label: 'twin child', mode: 'continuable', activity: 'inactive' },
{ kind: 'child', id: compacted, label: 'twin child', mode: 'continuable', activity: 'inactive' },
])
})
@@ -406,7 +448,7 @@ describe('SubagentService.listChildren', () => {
}, childEvents(descriptorPayload('grandchild')))
const entries = await ctx.subagents.listChildren(parent.id)
expect(entries).toEqual([
{ kind: 'child', id: childId, label: 'direct child', status: 'complete' },
{ kind: 'child', id: childId, label: 'direct child', mode: 'continuable', activity: 'inactive' },
])
})

View File

@@ -10,6 +10,7 @@ import SubagentService, {
SUBAGENT_DESCRIPTOR_VERSION,
SubagentError,
assertSubagentMaxDepth,
type ResolvedSubagentStartRequest,
type SubagentCapabilities,
type SubagentProvider,
type SubagentResult,
@@ -27,6 +28,7 @@ const NO_CAPS: SubagentCapabilities = { outputSchema: false, depthLimit: false,
function baseRequest(overrides: Partial<SubagentStartRequest> = {}): SubagentStartRequest {
return {
label: 'do a thing',
prompt: [{ type: 'text', text: 'do a thing' }],
parent: fakeParent(),
signal: new AbortController().signal,
@@ -37,7 +39,7 @@ function baseRequest(overrides: Partial<SubagentStartRequest> = {}): SubagentSta
class StubProvider implements SubagentProvider {
readonly inheritsParentContext = false
startCount = 0
lastRequest: SubagentStartRequest | undefined
lastRequest: ResolvedSubagentStartRequest | undefined
constructor(
readonly name: string,
@@ -48,7 +50,7 @@ class StubProvider implements SubagentProvider {
},
) {}
async start(request: SubagentStartRequest): Promise<SubagentRun> {
async start(request: ResolvedSubagentStartRequest): Promise<SubagentRun> {
this.startCount += 1
this.lastRequest = request
return {
@@ -104,16 +106,23 @@ describe('SubagentService', () => {
.rejects.toMatchObject({ code: 'NO_PROVIDER' })
})
it('borrows ordinary start requests and exposes no provider continuation operations', async () => {
it('resolves the one-shot descriptor and exposes no provider continuation operations', async () => {
const { subagents } = await service()
const provider = new StubProvider('one-shot')
subagents.registerProvider(provider)
const request = baseRequest()
await subagents.start('one-shot', request)
// One-shot start borrows the caller's exact request; the seam has no
// provider-facing resume or steer surface to dispatch through.
expect(provider.lastRequest).toBe(request)
expect(provider.lastRequest).toEqual({
...request,
descriptor: {
version: SUBAGENT_DESCRIPTOR_VERSION,
mode: 'one-shot',
provider: 'one-shot',
label: 'do a thing',
},
})
expect(provider.lastRequest).not.toBe(request)
expectTypeOf<Parameters<SubagentService['start']>[1]>().toExtend<SubagentStartRequest>()
expect('resume' in subagents).toBe(false)
expect('resume' in provider).toBe(false)
@@ -299,15 +308,17 @@ describe('subagent descriptors', () => {
it('omits absent fields, recovers a complete payload, and rejects unsupported versions', () => {
expect(foldSubagentDescriptor([])).toBeUndefined()
const minimal = snapshotSubagentDescriptor({ provider: 'spawn', label: 'child work' })
const minimal = snapshotSubagentDescriptor({ mode: 'one-shot', provider: 'spawn', label: 'child work' })
expect(minimal).toEqual({
version: SUBAGENT_DESCRIPTOR_VERSION,
mode: 'one-shot',
provider: 'spawn',
label: 'child work',
})
expect(foldSubagentDescriptor([event(minimal)])).toEqual(minimal)
const complete = {
version: SUBAGENT_DESCRIPTOR_VERSION,
mode: 'continuable' as const,
provider: 'spawn',
label: 'complete child',
agentProvider: 'deepseek',
@@ -316,6 +327,7 @@ describe('subagent descriptors', () => {
toolFilter: { allow: ['read'], deny: ['bash'] },
}
expect(snapshotSubagentDescriptor({
mode: 'continuable',
provider: complete.provider,
label: complete.label,
agentProvider: complete.agentProvider,
@@ -325,15 +337,28 @@ describe('subagent descriptors', () => {
})).toEqual(complete)
expect(foldSubagentDescriptor([event(complete)])).toEqual(complete)
expect(foldSubagentDescriptor([
event({ version: SUBAGENT_DESCRIPTOR_VERSION, provider: 'spawn', label: 'l', toolFilter: { allow: ['read'] } }),
event({
version: SUBAGENT_DESCRIPTOR_VERSION,
mode: 'continuable',
provider: 'spawn',
label: 'l',
toolFilter: { allow: ['read'] },
}),
])).toMatchObject({ toolFilter: { allow: ['read'] } })
expect(foldSubagentDescriptor([
event({ version: SUBAGENT_DESCRIPTOR_VERSION, provider: 'spawn', label: 'l', toolFilter: { deny: ['bash'] } }),
event({
version: SUBAGENT_DESCRIPTOR_VERSION,
mode: 'continuable',
provider: 'spawn',
label: 'l',
toolFilter: { deny: ['bash'] },
}),
])).toMatchObject({ toolFilter: { deny: ['bash'] } })
expect(foldSubagentDescriptor([
event({ version: SUBAGENT_DESCRIPTOR_VERSION + 1, provider: 'spawn' }),
])).toBeUndefined()
expect(() => snapshotSubagentDescriptor({
mode: 'continuable',
provider: 'spawn',
label: 'bad',
toolFilter: { deny: [Symbol('not-json')] as unknown as string[] },
@@ -346,19 +371,94 @@ describe('subagent descriptors', () => {
['array payload', [], 'payload must be an object'],
['missing version', { provider: 'spawn' }, 'version must be a number'],
['string version', { version: '1', provider: 'spawn' }, 'version must be a number'],
['unknown payload field', { version: 1, provider: 'spawn', extra: true }, 'payload has unknown field "extra"'],
['missing provider', { version: 1 }, 'provider must be a string'],
['missing label', { version: 1, provider: 'spawn' }, 'label must be a string'],
['invalid label', { version: 1, provider: 'spawn', label: 7 }, 'label must be a string'],
['invalid provider', { version: 1, provider: 7 }, 'provider must be a string'],
['invalid agent provider', { version: 1, provider: 'spawn', label: 'l', agentProvider: 7 }, 'agentProvider must be a string'],
['invalid agent model', { version: 1, provider: 'spawn', label: 'l', agentModel: [] }, 'agentModel must be a string'],
['invalid persona', { version: 1, provider: 'spawn', label: 'l', persona: {} }, 'persona must be a string'],
['non-object tool filter', { version: 1, provider: 'spawn', label: 'l', toolFilter: [] }, 'toolFilter must be an object'],
['unknown tool-filter field', { version: 1, provider: 'spawn', label: 'l', toolFilter: { except: ['bash'] } }, 'toolFilter has unknown field "except"'],
['empty tool filter', { version: 1, provider: 'spawn', label: 'l', toolFilter: {} }, 'toolFilter must declare allow and/or deny'],
['non-array allow list', { version: 1, provider: 'spawn', label: 'l', toolFilter: { allow: 'read' } }, 'toolFilter.allow must be an array of strings'],
['non-string deny item', { version: 1, provider: 'spawn', label: 'l', toolFilter: { deny: [7] } }, 'toolFilter.deny must be an array of strings'],
['missing mode', { version: SUBAGENT_DESCRIPTOR_VERSION }, 'mode must be "one-shot" or "continuable"'],
['invalid mode', { version: SUBAGENT_DESCRIPTOR_VERSION, mode: 'later' }, 'mode must be "one-shot" or "continuable"'],
['unknown one-shot field', {
version: SUBAGENT_DESCRIPTOR_VERSION,
mode: 'one-shot',
provider: 'spawn',
label: 'l',
persona: 'reviewer',
}, 'payload has unknown field "persona"'],
['unknown payload field', {
version: SUBAGENT_DESCRIPTOR_VERSION,
mode: 'continuable',
provider: 'spawn',
extra: true,
}, 'payload has unknown field "extra"'],
['missing provider', { version: SUBAGENT_DESCRIPTOR_VERSION, mode: 'continuable' }, 'provider must be a string'],
['missing label', {
version: SUBAGENT_DESCRIPTOR_VERSION,
mode: 'continuable',
provider: 'spawn',
}, 'label must be a string'],
['invalid label', {
version: SUBAGENT_DESCRIPTOR_VERSION,
mode: 'continuable',
provider: 'spawn',
label: 7,
}, 'label must be a string'],
['invalid provider', {
version: SUBAGENT_DESCRIPTOR_VERSION,
mode: 'continuable',
provider: 7,
}, 'provider must be a string'],
['invalid agent provider', {
version: SUBAGENT_DESCRIPTOR_VERSION,
mode: 'continuable',
provider: 'spawn',
label: 'l',
agentProvider: 7,
}, 'agentProvider must be a string'],
['invalid agent model', {
version: SUBAGENT_DESCRIPTOR_VERSION,
mode: 'continuable',
provider: 'spawn',
label: 'l',
agentModel: [],
}, 'agentModel must be a string'],
['invalid persona', {
version: SUBAGENT_DESCRIPTOR_VERSION,
mode: 'continuable',
provider: 'spawn',
label: 'l',
persona: {},
}, 'persona must be a string'],
['non-object tool filter', {
version: SUBAGENT_DESCRIPTOR_VERSION,
mode: 'continuable',
provider: 'spawn',
label: 'l',
toolFilter: [],
}, 'toolFilter must be an object'],
['unknown tool-filter field', {
version: SUBAGENT_DESCRIPTOR_VERSION,
mode: 'continuable',
provider: 'spawn',
label: 'l',
toolFilter: { except: ['bash'] },
}, 'toolFilter has unknown field "except"'],
['empty tool filter', {
version: SUBAGENT_DESCRIPTOR_VERSION,
mode: 'continuable',
provider: 'spawn',
label: 'l',
toolFilter: {},
}, 'toolFilter must declare allow and/or deny'],
['non-array allow list', {
version: SUBAGENT_DESCRIPTOR_VERSION,
mode: 'continuable',
provider: 'spawn',
label: 'l',
toolFilter: { allow: 'read' },
}, 'toolFilter.allow must be an array of strings'],
['non-string deny item', {
version: SUBAGENT_DESCRIPTOR_VERSION,
mode: 'continuable',
provider: 'spawn',
label: 'l',
toolFilter: { deny: [7] },
}, 'toolFilter.deny must be an array of strings'],
])('rejects a malformed persisted descriptor: %s', (_case, data, detail) => {
expect(() => foldSubagentDescriptor([event(data)])).toThrow(detail)
})

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 packages/subagent/tool-subagent-control/README.md
README.md: 1fe0e49006d12b95e6c6e38cab7895238b3d3c98
README.zh.md: 9d2d46888b0790548df35cb54613ee2334143db7
README.md: 78621701cd4fe8ac9ad8b3f0985271ee6e2a7e8c
README.zh.md: f6ed8144dcfc8df8f2e6edad89c4d42b641d666e

View File

@@ -6,7 +6,7 @@ The optional, globally named `send_message` and `list_agents` tools are thin ada
The tool performs no lifecycle routing — residency and cold resume belong to the subagent service. It passes `exec.agent` as the exact live parent that authorizes delivery and attributes every message as durable provenance `{ kind: 'coordinator', senderSessionId: parent.id }`, which the service retains but never treats as authority. Every message becomes the subagent's next FIFO turn through `Agent.followup()`: if the child is still working, the message waits until its current turn finishes, so it cannot redirect work already underway. The tool forwards its execution signal, which owns admission only until inbox acceptance; once the child accepts the message the accepted turn cannot be cancelled through this tool. The child does not reply to the sender — its transcript by that id is the source of what it did. A delivery failure becomes an errored tool result stating the message was not delivered.
`list_agents` takes no arguments, derives the parent id from the calling agent, and renders `ctx.subagents.listChildren()`'s complete entry array without a cursor. It is discovery only: durable identity comes from each child's descriptor, while delivery-time authority and Activation ownership checks remain `send_message`'s.
`list_agents` takes no arguments, derives the parent id from the calling agent, and projects `ctx.subagents.listChildren()` to continuable children without a cursor. The service result also contains one-shot session-backed subagents for consumers such as a UI, but those entries are omitted from this model tool because they cannot accept `send_message`. Diagnostics remain visible. Durable identity and mode come from each child's descriptor, while delivery-time authority and Activation ownership checks remain `send_message`'s.
## Model Experience
@@ -42,7 +42,7 @@ Append-only; newly visible content follows the reusable request prefix and does
#### What the model sees
One line per entry in the trace's stable order: `<id> [<status>] — <label>` for a child (`running` = the logical session is live, `complete` = persisted only and resumable by `send_message`), `<id> [diagnostic: <reason>]` for a candidate that could not be read (`corrupt`, `unsupported`, or `unavailable`), and `(no subagents)` for an empty result. Diagnostics never expose descriptor contents.
One line per continuable child in the trace's stable order: `<id> [<status>] — <label>` (`running` = the logical session is live, `complete` = persisted only and resumable by `send_message`), plus `<id> [diagnostic: <reason>]` for a candidate that could not be read (`corrupt`, `unsupported`, or `unavailable`). One-shot children are intentionally absent; `(no subagents)` means no continuable child or diagnostic survived the projection. Diagnostics never expose descriptor contents.
#### Token effect

View File

@@ -6,7 +6,7 @@
本工具不执行生命周期路由——驻留与冷恢复归 subagent 服务所有。它将 `exec.agent` 作为授权投递的准确实时父级传入,并把每条消息的来源标记为持久化来源 `{ kind: 'coordinator', senderSessionId: parent.id }`;服务会保留该来源,但绝不将其视为权限。每条消息都会通过 `Agent.followup()` 成为子 agent智能体的下一个 FIFO 轮次:如果子 agent 仍在工作,该消息会等待其当前轮次结束,因此无法重定向已经在进行的工作。本工具会转发其执行信号,该信号只在 inbox 接受之前掌管准入;一旦子 agent 接受消息,已接受的轮次便无法再通过本工具取消。子 agent 不会回复发送方——通过该 id 查看其 transcript 即是其所做工作的来源。投递失败会变为出错的工具结果,并明确说明消息未送达。
`list_agents` 不接受参数,从调用 Agent 推导 parent id在没有 cursor 的情况下渲染 `ctx.subagents.listChildren()`完整条目数组。它只负责发现:持久化身份来自每个 child 的描述符,消息送达时的鉴权和 Activation 所有权检查仍归 `send_message` 负责。
`list_agents` 不接受参数,从调用它的 agent 推导 parent id且不使用 cursor,将 `ctx.subagents.listChildren()`结果投影为可继续 child。服务结果还包含由会话支撑的一次性 subagent以供 UI 等消费方使用;但这些条目无法接受 `send_message`因此会从这个模型工具中排除。diagnostic 仍然可见。持久化身份和模式来自每个子 agent 的描述符,消息送达时的鉴权和 Activation 所有权检查仍归 `send_message` 负责。
## 模型体验
@@ -42,7 +42,7 @@
#### 模型看到的内容
每个条目按追踪结果的稳定顺序占一行child 使用 `<id> [<status>] — <label>``running` 表示逻辑会话存活,`complete` 表示存在于持久化存储中且可由 `send_message` 恢复),无法读取的候选使用 `<id> [diagnostic: <reason>]``corrupt``unsupported``unavailable`,空结果使用 `(no subagents)`。Diagnostic 绝不暴露描述符内容。
按追踪结果的稳定顺序,每个可继续 child 占一行:渲染为 `<id> [<status>] — <label>``running` 表示逻辑会话存活,`complete` 表示存在于持久化存储中,可通过 `send_message` 恢复),另为无法读取的候选项渲染 `<id> [diagnostic: <reason>]``corrupt``unsupported``unavailable`。一次性 child 会被有意排除;`(no subagents)` 表示投影后没有留下可继续 child 或 diagnostic。诊断信息绝不暴露描述符内容。
#### Token 影响

View File

@@ -1,6 +1,7 @@
/**
* The globally named `list_agents` tool: a thin model-facing adapter over
* `ctx.subagents.listChildren()`. It is separately loadable from the
* the continuable projection of `ctx.subagents.listChildren()`. It is
* separately loadable from the
* root `send_message` plugin because it additionally requires the session
* query service — a deployment may use `send_message` without loading session
* query, and this plugin catches that misconfiguration at load.
@@ -15,6 +16,19 @@ import type {} from '@deepseek-ai/dsh-subagent'
export const name = 'tool-subagent-list-agents'
export const inject = ['tools', 'subagents', 'sessionQuery']
type ListAgentsEntry =
| {
readonly kind: 'child'
readonly id: string
readonly label: string
readonly status: 'running' | 'complete'
}
| {
readonly kind: 'diagnostic'
readonly id: string
readonly reason: 'corrupt' | 'unsupported' | 'unavailable'
}
/**
* Register the `list_agents` tool.
* @param ctx - context carrying the tool registry, subagent service, and session query.
@@ -23,7 +37,7 @@ export function apply(ctx: Context): void {
ctx.tools.register(defineTool({
name: 'list_agents',
description:
'List your background subagents by durable id and label. Status is a snapshot of the stored '
'List your continuable background subagents by durable id and label. Status is a snapshot of the stored '
+ 'record: running means the subagent session is currently live in this process, complete means '
+ 'it exists only in storage and a `send_message` starts a new turn on the same conversation. '
+ 'The snapshot is not a delivery promise — `send_message` performs the authoritative check and '
@@ -74,7 +88,21 @@ export function apply(ctx: Context): void {
}
// The registry drains started tool bodies, so the scan must observe the
// call's signal rather than finish a slow catalog after cancellation.
return await ctx.subagents.listChildren(parent.id, exec.signal)
const entries = await ctx.subagents.listChildren(parent.id, exec.signal)
const visible: ListAgentsEntry[] = []
for (const entry of entries) {
if (entry.kind === 'diagnostic') {
visible.push(entry)
} else if (entry.mode === 'continuable') {
visible.push({
kind: 'child',
id: entry.id,
label: entry.label,
status: entry.activity === 'running' ? 'running' : 'complete',
})
}
}
return visible
},
}))
}

View File

@@ -90,19 +90,49 @@ describe('dsh-tool-subagent-control/list-agents', () => {
// Pin the render deterministically past the service: the tool is a thin
// adapter, so its fixed text forms are what this test pins.
const entries: SubagentListEntry[] = [
{ kind: 'child', id: started.childId, label: 'real child', status: 'complete' },
{
kind: 'child',
id: SessionId('one-shot-child'),
label: 'finished once',
mode: 'one-shot',
activity: 'inactive',
},
{
kind: 'child',
id: started.childId,
label: 'real child',
mode: 'continuable',
activity: 'inactive',
},
{
kind: 'child',
id: SessionId('running-child'),
label: 'still working',
mode: 'continuable',
activity: 'running',
},
{ kind: 'diagnostic', id: SessionId('broken-child'), reason: 'corrupt' },
]
ctx.subagents.listChildren = () => Promise.resolve(entries)
const result = await callTool(ctx, 'list_agents', {}, parent)
expect(result.isError).toBe(false)
expect(text(result)).toBe(
`${started.childId} [complete] — real child\nbroken-child [diagnostic: corrupt]`,
`${started.childId} [complete] — real child\n`
+ 'running-child [running] — still working\n'
+ 'broken-child [diagnostic: corrupt]',
)
})
it('lists a real settled child end-to-end with its durable label', async () => {
const { ctx, parent } = await setup([textResponse('done')])
it('lists a real settled continuable child and omits a real one-shot sibling', async () => {
const { ctx, parent } = await setup([textResponse('once'), textResponse('done')])
const oneShot = await ctx.subagents.start('spawn', {
label: 'finished once',
prompt: [{ type: 'text', text: 'one-shot task' }],
parent,
signal: new AbortController().signal,
})
await oneShot.result
await oneShot.dispose()
const started = await ctx.subagents.startContinuable({
provider: 'spawn',
label: 'summarize the doc',

View File

@@ -280,6 +280,7 @@ export function apply(ctx: Context, config: Config): void {
const maxDepth = typeof config.maxDepth === 'number' ? config.maxDepth : undefined
const request = {
label: args.description,
prompt: [{ type: 'text', text: args.prompt }] as ContentBlock[],
parent,
...config.agentOptions !== undefined ? { agentOptions: config.agentOptions } : {},

View File

@@ -12,6 +12,7 @@ function fakeParent(id = 'parent-1'): Agent {
function baseRequest(over: Partial<SubagentStartRequest> = {}): SubagentStartRequest {
return {
label: 'task',
prompt: [{ type: 'text', text: 'task' }],
parent: fakeParent(),
signal: new AbortController().signal,

View File

@@ -1007,6 +1007,7 @@ describe('depth budget configuration', () => {
it('defaults maxDepth to 3 and forwards it in the start request', async () => {
const { ctx, requests } = await captureSetup()
await callSubagent(ctx, { description: 'd', prompt: 'p' })
expect(requests[0]?.label).toBe('d')
expect(requests[0]?.maxDepth).toBe(3)
expect(requests[0]?.toolFilter).toBeUndefined()
})

View File

@@ -91,6 +91,7 @@ async function settleSubagent(
})
try {
const run = await ctx.subagents.start(info.provider, {
label: 'synthetic child',
parent,
prompt: [],
signal: new AbortController().signal,
@@ -556,11 +557,13 @@ describe('HarnessSdkServer', () => {
})
const firstRun = await ctx.subagents.start('reused', {
label: 'first reused child',
parent: oldParent.agent,
prompt: [],
signal: new AbortController().signal,
})
const sameLifetimeRun = await ctx.subagents.start('reused', {
label: 'same lifetime child',
parent: oldParent.agent,
prompt: [],
signal: new AbortController().signal,
@@ -580,6 +583,7 @@ describe('HarnessSdkServer', () => {
})
currentLocalAgent = newChild.agent
const secondRun = await ctx.subagents.start('reused', {
label: 'second reused child',
parent: newParent.agent,
prompt: [],
signal: new AbortController().signal,
@@ -650,6 +654,7 @@ describe('HarnessSdkServer', () => {
}),
})
const localRun = await ctx.subagents.start('reused-provider', {
label: 'local reused child',
parent: parent.agent,
prompt: [],
signal: new AbortController().signal,
@@ -668,6 +673,7 @@ describe('HarnessSdkServer', () => {
}),
})
const remoteRun = await ctx.subagents.start('reused-provider', {
label: 'remote reused child',
parent: parent.agent,
prompt: [],
signal: new AbortController().signal,
@@ -750,6 +756,7 @@ describe('HarnessSdkServer', () => {
// Start before the server subscribes. The terminal payload still carries
// this run's exact local child without reconstructing it from ids.
const missedStartRun = await ctx.subagents.start('fork', {
label: 'missed start child',
parent: parentHandle.agent,
prompt: [],
signal: new AbortController().signal,

View File

@@ -322,6 +322,7 @@ export class WorkerRun implements WorkflowRun {
let run: SubagentRun
try {
run = await this.subagents.start(this.provider, {
label: request.label,
prompt: [{ type: 'text', text: request.prompt }],
parent: this.parent,
signal: this.controller.signal,

View File

@@ -275,6 +275,7 @@ export class WorkflowExecution {
let run: ChildHandle
try {
run = await this.children.startAgent({
label,
prompt: rawPrompt,
...opts.schema !== undefined ? { schema: opts.schema } : {},
...opts.provider !== undefined ? { provider: opts.provider } : {},

View File

@@ -38,6 +38,8 @@ export interface WorkerInit {
/** What the worker asks the host to start for one `agent()` call (options already validated worker-side). */
export interface ChildStartRequest {
/** Short display label resolved by the worker runtime. */
label: string
/** The child's prompt text. */
prompt: string
/** The structured-output schema, if the call passed one (already subset-checked). */

View File

@@ -420,10 +420,13 @@ describe('runWorkerSession over an in-process MessageChannel', () => {
`))
await host.result()
const starts = host.ofType(WorkerToHostType.AgentStart).map(m => m.info)
const requests = host.ofType(WorkerToHostType.ChildStart).map(m => m.request)
expect(starts[0]).toMatchObject({ seq: 1, phase: 'Find' })
expect(starts[0]!.label.length).toBeLessThanOrEqual(48)
expect(starts[0]!.label).not.toContain('second line')
expect(starts[1]).toMatchObject({ seq: 2, label: 'named', phase: 'Custom' })
expect(requests[0]!.label).toBe(starts[0]!.label)
expect(requests[1]!.label).toBe('named')
host.close()
})

View File

@@ -211,7 +211,7 @@ describe('dsh-workflow-workerthread', () => {
reply: () => ({ output: [], structured: { files: ['x.ts', 'y.ts'] }, stopReason: 'completed' }),
})
const result = await run(ctx, parent, scripted(`
const found = await agent('list files', { model: 'deepseek-v4-pro', schema: { type: 'object', properties: { files: { type: 'array', items: { type: 'string' } } }, required: ['files'] } })
const found = await agent('list files', { label: 'inventory', model: 'deepseek-v4-pro', schema: { type: 'object', properties: { files: { type: 'array', items: { type: 'string' } } }, required: ['files'] } })
return { first: found.files[0], count: found.files.length }
`))
expect(result.value).toEqual({ first: 'x.ts', count: 2 })
@@ -221,6 +221,7 @@ describe('dsh-workflow-workerthread', () => {
required: ['files'],
})
expect(provider.runs[0]!.request.agentOptions).toEqual({ model: 'deepseek-v4-pro' })
expect(provider.runs[0]!.request.label).toBe('inventory')
expect(provider.runs[0]!.request.parent).toBeDefined()
})

View File

@@ -1094,6 +1094,11 @@
"symbol": "SubagentStartRequest",
"source": "packages/subagent/subagent/src/types.ts"
},
{
"doc": "docs/core-data-structures/subagent.md",
"symbol": "ResolvedSubagentStartRequest",
"source": "packages/subagent/subagent/src/types.ts"
},
{
"doc": "docs/core-data-structures/subagent.md",
"symbol": "CoordinatorMessageSource",