dsh-plugin-dev-kb 1.0.9 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (169) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/README.en.md +14 -8
  3. package/README.md +14 -8
  4. package/docs/banner.svg +25 -0
  5. package/kb/INDEX.md +8 -2
  6. package/kb/README.md +4 -4
  7. package/kb/extra/AGENTS.md +3 -3
  8. package/kb/extra/cookbook/adding-a-remote-api.md +197 -0
  9. package/kb/extra/cookbook/adding-a-remote-api.zh.md +197 -0
  10. package/kb/extra/cookbook/adding-a-session-format-version.md +109 -0
  11. package/kb/extra/cookbook/adding-a-session-format-version.zh.md +109 -0
  12. package/kb/extra/cookbook/adding-a-vendored-package.md +1 -1
  13. package/kb/extra/cookbook/adding-a-vendored-package.zh.md +1 -1
  14. package/kb/extra/cookbook/responding-to-pr-review-on-a-stack.md +1 -1
  15. package/kb/extra/cookbook/responding-to-pr-review-on-a-stack.zh.md +1 -1
  16. package/kb/extra/deepseek-llm-api-wire-extensions.md +19 -15
  17. package/kb/extra/deepseek-llm-api-wire-extensions.zh.md +19 -15
  18. package/kb/extra/development.md +13 -3
  19. package/kb/extra/development.zh.md +13 -3
  20. package/kb/extra/event-producer-consumer.md +49 -46
  21. package/kb/extra/event-producer-consumer.zh.md +52 -49
  22. package/kb/extra/i18n/README.md +2 -1
  23. package/kb/extra/i18n/README.zh.md +3 -2
  24. package/kb/extra/i18n/style-samples.md +2 -2
  25. package/kb/extra/i18n/translation-prompt.md +1 -1
  26. package/kb/extra/module-graph.md +493 -1002
  27. package/kb/extra/module-graph.zh.md +495 -1004
  28. package/kb/extra/rescope.md +1 -1
  29. package/kb/extra/rescope.zh.md +1 -1
  30. package/kb/extra/session-format-status.md +47 -0
  31. package/kb/extra/session-format-status.zh.md +47 -0
  32. package/kb/extra/subsystems/agent-team.md +3 -2
  33. package/kb/extra/subsystems/agent-team.zh.md +3 -2
  34. package/kb/extra/subsystems/attachment.md +157 -4
  35. package/kb/extra/subsystems/attachment.zh.md +157 -4
  36. package/kb/extra/subsystems/feedback.md +164 -30
  37. package/kb/extra/subsystems/feedback.zh.md +164 -30
  38. package/kb/extra/testing.md +9 -4
  39. package/kb/extra/testing.zh.md +9 -4
  40. package/kb/extra/web-styling.md +5 -0
  41. package/kb/extra/web-styling.zh.md +5 -0
  42. package/kb/meta/search-index.json +223 -151
  43. package/kb/meta/site-pages.txt +187 -181
  44. package/kb/meta/source.json +5 -5
  45. package/kb/meta/topics.md +5 -5
  46. package/kb/site/develop/basic/publish.md +1 -1
  47. package/kb/site/develop/cordis-tutorial/07-into-the-harness.md +3 -2
  48. package/kb/site/develop/practice/llm-adapter.md +4 -3
  49. package/kb/site/en/develop/basic/publish.md +1 -1
  50. package/kb/site/en/develop/cordis-tutorial/07-into-the-harness.md +3 -2
  51. package/kb/site/en/develop/practice/llm-adapter.md +4 -3
  52. package/kb/site/en/guide/network-proxy.md +87 -0
  53. package/kb/site/en/guide/providers.md +70 -17
  54. package/kb/site/en/guide/python-sdk.md +19 -3
  55. package/kb/site/en/guide/schedule.md +2 -0
  56. package/kb/site/en/reference/agent-lifecycle.md +17 -8
  57. package/kb/site/en/reference/api-gateway.md +2 -2
  58. package/kb/site/en/reference/capability-seams.md +24 -10
  59. package/kb/site/en/reference/config-catalog.md +322 -264
  60. package/kb/site/en/reference/cookbook/adding-a-package.md +1 -2
  61. package/kb/site/en/reference/cookbook/adding-a-settings-card.md +10 -8
  62. package/kb/site/en/reference/cookbook/adding-a-tool.md +1 -1
  63. package/kb/site/en/reference/cookbook/extension-cookbook.md +16 -13
  64. package/kb/site/en/reference/index.md +26 -9
  65. package/kb/site/en/reference/persistence-catalog.md +189 -95
  66. package/kb/site/en/reference/subsystems/approval.md +1 -1
  67. package/kb/site/en/reference/subsystems/client-modules.md +9 -0
  68. package/kb/site/en/reference/subsystems/client-resources.md +94 -0
  69. package/kb/site/en/reference/subsystems/code-runtime.md +7 -3
  70. package/kb/site/en/reference/subsystems/commands.md +25 -16
  71. package/kb/site/en/reference/subsystems/compaction.md +10 -10
  72. package/kb/site/en/reference/subsystems/conversation.md +11 -4
  73. package/kb/site/en/reference/subsystems/core.md +173 -29
  74. package/kb/site/en/reference/subsystems/credentials.md +5 -4
  75. package/kb/site/en/reference/subsystems/filesystem.md +15 -0
  76. package/kb/site/en/reference/subsystems/goal.md +38 -2
  77. package/kb/site/en/reference/subsystems/index.md +3 -1
  78. package/kb/site/en/reference/subsystems/invariants.md +1 -1
  79. package/kb/site/en/reference/subsystems/llm-streaming.md +43 -12
  80. package/kb/site/en/reference/subsystems/lsp.md +1 -1
  81. package/kb/site/en/reference/subsystems/permission-presets.md +5 -5
  82. package/kb/site/en/reference/subsystems/persistence.md +211 -199
  83. package/kb/site/en/reference/subsystems/plan.md +3 -3
  84. package/kb/site/en/reference/subsystems/sandbox.md +2 -0
  85. package/kb/site/en/reference/subsystems/schedule.md +9 -3
  86. package/kb/site/en/reference/subsystems/scope.md +1 -1
  87. package/kb/site/en/reference/subsystems/session-projection.md +50 -24
  88. package/kb/site/en/reference/subsystems/session-query.md +19 -13
  89. package/kb/site/en/reference/subsystems/session-reference.md +29 -1
  90. package/kb/site/en/reference/subsystems/session-telemetry.md +27 -10
  91. package/kb/site/en/reference/subsystems/session-title.md +6 -6
  92. package/kb/site/en/reference/subsystems/session.md +208 -103
  93. package/kb/site/en/reference/subsystems/settings.md +30 -12
  94. package/kb/site/en/reference/subsystems/shell.md +9 -6
  95. package/kb/site/en/reference/subsystems/sidebar-right.md +151 -0
  96. package/kb/site/en/reference/subsystems/skills.md +1 -1
  97. package/kb/site/en/reference/subsystems/slots.md +41 -32
  98. package/kb/site/en/reference/subsystems/spill.md +13 -6
  99. package/kb/site/en/reference/subsystems/storage.md +28 -5
  100. package/kb/site/en/reference/subsystems/subagent.md +58 -105
  101. package/kb/site/en/reference/subsystems/subprocess.md +25 -26
  102. package/kb/site/en/reference/subsystems/system-prompt.md +18 -3
  103. package/kb/site/en/reference/subsystems/token-meter.md +4 -3
  104. package/kb/site/en/reference/subsystems/tools.md +5 -5
  105. package/kb/site/en/reference/subsystems/typert.md +18 -18
  106. package/kb/site/en/reference/subsystems/web-client.md +1 -1
  107. package/kb/site/en/reference/subsystems/workspace.md +88 -9
  108. package/kb/site/en/reference/tool-catalog.md +56 -66
  109. package/kb/site/en/reference/tool-execution-pipeline.md +2 -2
  110. package/kb/site/guide/network-proxy.md +87 -0
  111. package/kb/site/guide/providers.md +70 -17
  112. package/kb/site/guide/python-sdk.md +19 -3
  113. package/kb/site/guide/schedule.md +2 -0
  114. package/kb/site/reference/agent-lifecycle.md +17 -8
  115. package/kb/site/reference/api-gateway.md +2 -2
  116. package/kb/site/reference/capability-seams.md +24 -10
  117. package/kb/site/reference/config-catalog.md +321 -263
  118. package/kb/site/reference/cookbook/adding-a-package.md +1 -2
  119. package/kb/site/reference/cookbook/adding-a-settings-card.md +10 -8
  120. package/kb/site/reference/cookbook/adding-a-tool.md +1 -1
  121. package/kb/site/reference/cookbook/extension-cookbook.md +16 -13
  122. package/kb/site/reference/index.md +26 -9
  123. package/kb/site/reference/persistence-catalog.md +190 -96
  124. package/kb/site/reference/subsystems/approval.md +1 -1
  125. package/kb/site/reference/subsystems/client-modules.md +9 -0
  126. package/kb/site/reference/subsystems/client-resources.md +94 -0
  127. package/kb/site/reference/subsystems/code-runtime.md +7 -3
  128. package/kb/site/reference/subsystems/commands.md +25 -16
  129. package/kb/site/reference/subsystems/compaction.md +10 -10
  130. package/kb/site/reference/subsystems/conversation.md +11 -4
  131. package/kb/site/reference/subsystems/core.md +173 -29
  132. package/kb/site/reference/subsystems/credentials.md +5 -4
  133. package/kb/site/reference/subsystems/filesystem.md +15 -0
  134. package/kb/site/reference/subsystems/goal.md +38 -2
  135. package/kb/site/reference/subsystems/index.md +14 -12
  136. package/kb/site/reference/subsystems/invariants.md +1 -1
  137. package/kb/site/reference/subsystems/llm-streaming.md +43 -12
  138. package/kb/site/reference/subsystems/lsp.md +1 -1
  139. package/kb/site/reference/subsystems/permission-presets.md +5 -5
  140. package/kb/site/reference/subsystems/persistence.md +210 -198
  141. package/kb/site/reference/subsystems/plan.md +3 -3
  142. package/kb/site/reference/subsystems/schedule.md +9 -3
  143. package/kb/site/reference/subsystems/scope.md +1 -1
  144. package/kb/site/reference/subsystems/session-projection.md +50 -24
  145. package/kb/site/reference/subsystems/session-query.md +19 -13
  146. package/kb/site/reference/subsystems/session-reference.md +29 -1
  147. package/kb/site/reference/subsystems/session-telemetry.md +27 -10
  148. package/kb/site/reference/subsystems/session-title.md +6 -6
  149. package/kb/site/reference/subsystems/session.md +208 -103
  150. package/kb/site/reference/subsystems/settings.md +30 -12
  151. package/kb/site/reference/subsystems/shell.md +9 -6
  152. package/kb/site/reference/subsystems/sidebar-right.md +151 -0
  153. package/kb/site/reference/subsystems/skills.md +1 -1
  154. package/kb/site/reference/subsystems/slots.md +41 -32
  155. package/kb/site/reference/subsystems/spill.md +13 -6
  156. package/kb/site/reference/subsystems/storage.md +28 -5
  157. package/kb/site/reference/subsystems/subagent.md +58 -105
  158. package/kb/site/reference/subsystems/subprocess.md +25 -26
  159. package/kb/site/reference/subsystems/system-prompt.md +18 -3
  160. package/kb/site/reference/subsystems/token-meter.md +4 -3
  161. package/kb/site/reference/subsystems/tools.md +5 -5
  162. package/kb/site/reference/subsystems/typert.md +18 -18
  163. package/kb/site/reference/subsystems/web-client.md +1 -1
  164. package/kb/site/reference/subsystems/workspace.md +88 -9
  165. package/kb/site/reference/tool-catalog.md +55 -65
  166. package/kb/site/reference/tool-execution-pipeline.md +2 -2
  167. package/lib/index.js +25 -44
  168. package/package.json +3 -2
  169. package/skills/dsh-plugin-dev-kb.md +4 -4
@@ -7,10 +7,12 @@ outline: [2,3]
7
7
 
8
8
  The subagent seam lets an agent delegate work to a child agent. Like [bash](./shell.md), it is **one optional capability**, not part of the agent loop, so its types live here rather than in [core.md](./core.md). It differs from the other capability seams because **multiple provider implementations coexist** in one context, registered by name (`ctx.subagents`), while bash allows only one executor. Its registry follows the [LLM adapter registry](./llm-streaming.md), not the single-service bash executor.
9
9
 
10
- Service Definition: [dsh-subagent](https://github.com/deepseek-ai/deepseek-harness/tree/master/packages/subagent/subagent) (`ctx.subagents` + the vocabulary below). Service Providers are sibling packages (`dsh-subagent-spawn-in-process`, `dsh-subagent-fork-in-process`, `dsh-subagent-acp`, `dsh-subagent-codex`, `dsh-subagent-claude-code`, `dsh-subagent-dsh-sdk`); the model-facing Consumers are [dsh-tool-subagent](https://github.com/deepseek-ai/deepseek-harness/tree/master/packages/subagent/tool-subagent) (per-provider delegation), [dsh-tool-subagent-control](https://github.com/deepseek-ai/deepseek-harness/tree/master/packages/subagent/tool-subagent-control) (the optional global `send_message`, `interrupt_agent`, and `list_agents` controls), and [dsh-tool-subagent-report](https://github.com/deepseek-ai/deepseek-harness/tree/master/packages/subagent/tool-subagent-report) (the optional child-scoped `report` return channel). The same `ctx.subagents` service owns continuable-child orchestration through an internal activation manager and read-only child and descendant discovery straight from the session store and optional session persistence. Product-provider rationale lives in [the Codex and Claude Code Agent Note](https://github.com/deepseek-ai/deepseek-harness/blob/master/.agents/notes/implemented/feature/2026-08-04-claude-code-and-codex-subagent-backends.md); common-seam rationale lives in [the subagent Agent Note](https://github.com/deepseek-ai/deepseek-harness/blob/master/.agents/notes/implemented/feature/2026-06-21-subagent-capability-seam.md), [the continuable subagents Agent Note](https://github.com/deepseek-ai/deepseek-harness/blob/master/.agents/notes/implemented/feature/2026-07-28-continuable-subagent-conversations.md), [the report-tool Agent Note](https://github.com/deepseek-ai/deepseek-harness/blob/master/.agents/notes/implemented/feature/2026-07-30-continuable-subagent-report-tool.md), [the durable catalog Agent Note](https://github.com/deepseek-ai/deepseek-harness/blob/master/.agents/notes/implemented/feature/2026-07-22-durable-subagent-catalog-and-list-agents.md), [the list-identity-projection Agent Note](https://github.com/deepseek-ai/deepseek-harness/blob/master/.agents/notes/implemented/architecture/2026-08-06-subagent-list-identity-projection.md), and [the merged-service Agent Note](https://github.com/deepseek-ai/deepseek-harness/blob/master/.agents/notes/implemented/simplification/2026-07-26-merge-subagent-control-service.md).
10
+ Service Definition: [dsh-subagent](https://github.com/deepseek-ai/deepseek-harness/tree/master/packages/subagent/subagent) (`ctx.subagents` + the vocabulary below). Service Providers are sibling packages (`dsh-subagent-spawn-in-process`, `dsh-subagent-fork-in-process`, `dsh-subagent-acp`, `dsh-subagent-codex`, `dsh-subagent-claude-code`, `dsh-subagent-dsh-sdk`); the model-facing Consumers are [dsh-tool-subagent](https://github.com/deepseek-ai/deepseek-harness/tree/master/packages/subagent/tool-subagent) (per-provider delegation) and [dsh-tool-subagent-control](https://github.com/deepseek-ai/deepseek-harness/tree/master/packages/subagent/tool-subagent-control) (the optional global `send_message`, `interrupt_agent`, and `list_agents` controls). The same `ctx.subagents` service owns continuable-child orchestration through an internal activation manager and read-only child and descendant discovery straight from the session store and optional session persistence. Product-provider rationale lives in [the Codex and Claude Code Agent Note](https://github.com/deepseek-ai/deepseek-harness/blob/master/.agents/notes/implemented/feature/2026-08-04-claude-code-and-codex-subagent-backends.md); common-seam rationale lives in [the subagent Agent Note](https://github.com/deepseek-ai/deepseek-harness/blob/master/.agents/notes/implemented/feature/2026-06-21-subagent-capability-seam.md), [the continuable subagents Agent Note](https://github.com/deepseek-ai/deepseek-harness/blob/master/.agents/notes/implemented/feature/2026-07-28-continuable-subagent-conversations.md), and [the adjacent-Agent messaging Agent Note](https://github.com/deepseek-ai/deepseek-harness/blob/master/.agents/notes/implemented/architecture/2026-08-27-adjacent-agent-steer-messaging.md); [the archived list-identity-projection record](https://github.com/deepseek-ai/deepseek-harness/blob/master/.agents/notes/archived/architecture/2026-08-06-subagent-list-identity-projection.md) documents the original list-identity decision.
11
11
 
12
12
  Sources: [`packages/subagent/subagent/src/types.ts`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/subagent/subagent/src/types.ts), [`packages/subagent/subagent/src/index.ts`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/subagent/subagent/src/index.ts), and [`packages/subagent/subagent/src/continuation.ts`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/subagent/subagent/src/continuation.ts)
13
13
 
14
+ The `subagentCatalog` projection exposes `SubagentCatalogEntry[]` in parent event order through Session observations and client snapshots. Each entry contains the child id, creation time, mode, and mode-dependent label; fork-inherited catalog facts are excluded. [The subagent package](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/subagent/subagent/README.md) owns catalog creation and persistence semantics.
15
+
14
16
  ## Two kinds of capability, discovered two ways
15
17
 
16
18
  A provider advertises its **start-time** features on a static descriptor the service checks BEFORE a one-shot run exists; a request that needs one the provider lacks is rejected loud (`SubagentError('UNSUPPORTED_CAPABILITY')`), never accepted-then-ignored. Those flags describe only the one-shot [`start()`](#the-provider-contract-subagentprovider) path, where the provider composes the child. **Continuable** children are composed by the continuation manager itself, so they are gated by one optional method whose presence IS the capability, with TS narrowing as the discovery mechanism: [`SubagentProvider.prepareContinuable`](#the-provider-contract-subagentprovider).
@@ -99,7 +101,7 @@ interface SubagentStartRequest {
99
101
  /**
100
102
  * Optional per-child persona. Requires {@link SubagentCapabilities.persona};
101
103
  * rejected at start otherwise. In-process backends register it as a scoped
102
- * `deployment:persona` section on the child, SHADOWING the deployment's
104
+ * `deployment:persona-prefix` section on the child, SHADOWING the deployment's
103
105
  * persona for this child alone — same template semantics as the deployment
104
106
  * persona (strict `{{…}}` interpolation against the registered variables).
105
107
  */
@@ -136,21 +138,23 @@ persisted Session
136
138
 
137
139
  `SubagentRuntime.startContinuable()` reserves the stable child id, snapshots the versioned `subagent/descriptor` payload, asks the named provider for its detached `ContinuableCreateSpec`, creates the child Agent through a private activation-owner scope, establishes any continuable-parent ownership, and submits the initial prompt. It resolves with `{ childId, messageId }` when inbox acceptance yields the message id — without waiting for the turn to start or for the message to enter the Session log. Every failure before that acceptance rejects with neither id, disposing any created handle and rolling back the Activation and parent ownership.
138
140
 
139
- `SubagentRuntime.followup()` is the sole continuation-message operation, and routing depends only on Activation residency:
141
+ `SubagentRuntime.sendMessage()` is the sole model-authored message operation. It accepts the exact live sender plus a target id, permits only a direct parent or direct continuable child, derives sender attribution itself, and routes a direct-child target by Activation residency:
140
142
 
141
- | Activation state | `followup` |
143
+ | Target Activation state | `sendMessage` |
142
144
  |---|---|
143
- | `running` | enqueue in the same Activation |
144
- | `waiting` | wake the same Activation |
145
- | no Activation | cold-resume a new Activation |
145
+ | `running` | steer the nearest step in the same Activation |
146
+ | `waiting` | wake and steer the same Activation |
147
+ | no Activation | cold-resume a new Activation, then steer it |
148
+
149
+ `running` means the Agent has an active driver or maintenance task; `waiting` means no Agent activity is active but its Inbox is nonempty or it owns at least one child Activation that has not completed disposal; `settled` means no Agent activity is active, the Inbox is empty, and every owned child is disposed, at which point the manager disposes the [`AgentHandle`](./core.md#creation-and-ownership) and removes the Activation. The manager derives these internal conditions from `Agent.whenIdle()`, `Agent.inbox.hasPending`, the owned-child set, and an Activation generation that invalidates stale observations, rather than maintaining a second execution state machine. After the final Session flush, the child-lock decision uses the synchronous task entry of `Agent.runMaintenance()` to claim the idle phase and close admission in the same JavaScript turn. This conservative rule does not distinguish delivery modes: context parked by `Agent.inject()` can keep an idle Activation and its live ancestors resident until a waking delivery claims it, a queue mutation removes it, or manager teardown discards it.
146
150
 
147
- `running` means the Agent has an active admission or turn, or waking inbox work; `waiting` means it is quiescent but still owns at least one child Activation that has not completed disposal; `settled` means quiescent with every owned child disposed, at which point the manager disposes the [`AgentHandle`](./core.md#creation-and-ownership) and removes the Activation. The manager derives these internal conditions from Agent quiescence and the owned-child set rather than maintaining a second execution state machine.
151
+ The Agent inbox is the only queue. Every Agent message uses `Agent.steer()`: an idle target starts a turn, while a running target claims it at the nearest step boundary. The browser `subagent.prompt` Remote separately carries `delivery: 'queue' | 'steer'` through the same internal admission path; Queue opens a later FIFO turn, while Steer retains the Agent loop's best-effort nearest-step behavior and the message's human source. Successful delivery returns the accepted `MessageId`; the existing `agent/inbox/inserted`, `agent/inbox/claimed`, and `agent/inbox/discarded` events remain the message-lifecycle observations, and the continuation layer defines no second queue.
148
152
 
149
- The Agent inbox is the only queue. Every continuation message becomes one `Agent.followup()` FIFO turn, so accepted messages have one observable order and a follow-up cannot redirect a turn already underway. Successful delivery returns the accepted `MessageId`; the existing `agent/inbox/inserted`, `agent/inbox/claimed`, and `agent/inbox/discarded` events remain the message-lifecycle observations, and the continuation layer defines no subagent-specific delivery route.
153
+ Authority comes from the exact live sender. Parent-to-child delivery requires the target's `SessionHeader.parentSession` to name the sender; child-to-parent delivery requires the sender's resident Activation to name the target. Siblings, ancestors beyond one edge, self-targets, stale Agent objects, and one-shot children are rejected. Each accepted message is framed as `Agent <sender-id> sent a message:` and records `AgentMessageSource`; provenance records the sender but grants no authority.
150
154
 
151
- Follow-up authority comes from an exact live Agent tool context. The authenticated Agent must be the durable child's direct parent recorded in `SessionHeader.parentSession`. `MessageSource` and `senderSessionId` record who supplied an admitted message but grant no authority; the optional model-facing tool uses `CoordinatorMessageSource`.
155
+ For `startContinuable()`, `sendMessage()`, and browser prompt delivery, the caller signal owns lookup, materialization, and admission only until inbox acceptance. Afterwards the manager owns the Activation independently: later caller cancellation neither cancels the accepted turn nor disposes the child. The public subagent service exposes no caller-selected Agent-message scheduling; browser human Queue and Steer remain internal adapter choices.
152
156
 
153
- For both operations the caller signal owns lookup, materialization, and admission only until inbox acceptance. Afterwards the manager owns the Activation independently: later caller cancellation neither cancels the accepted turn nor disposes the child, and the seam exposes no steering operation.
157
+ Live queue occurrence mutation remains in the Session domain. `session.updateQueue` admits ordinary Edit, Remove, and QueueDock Steer for a live subagent-owned Agent only when its current projected identity is continuable and its descriptor sequence is in that child's own non-seed suffix. The identity projection folds descriptors last-wins so a child descriptor supersedes descriptors retained from fork lineage; the own-suffix sequence check prevents a seed-only ancestor identity from authorizing mutation. One-shot, missing, unknown, corrupt, or cold children remain rejected, and queue mutation never cold-resumes a child. The target Session id is the human authority for these mutations, including pending `nextStep` steering or injected context. Steer requires a queued `MessageId` and an Agent that reports running when the command begins; cancellation after admission uses the Agent's accepted waking `nextTurn` fallback. Edit rewrites content under the same `MessageId`, and both Edit and Steer complete their Inbox work synchronously, so settlement observes only the final state. `agent/inbox/claimed` and `agent/inbox/discarded` wake the watcher to re-read whether any pending occurrence remains; this lets direct Agent delivery resume parked work and lets removing the last parked occurrence settle an idle child. The [human inbox-control Agent Note](https://github.com/deepseek-ai/deepseek-harness/blob/master/.agents/notes/implemented/feature/2026-08-27-continuable-subagent-human-inbox-control.md) owns these semantics.
154
158
 
155
159
  `SubagentRuntime.interrupt(targetSessionId, authority)` is the one public stop: it authorizes synchronously, issues `Agent.cancel(cause, { keepInbox: true })` on the live target, and returns without awaiting quiescence. The Activation, its unclaimed pending inbox work, and published descendants are untouched; work already claimed into the interrupted turn is not requeued. Once the interrupted driver is idle, a waking send resumes the parked FIFO queue. An absent target — unknown, one-shot, or already settled — and a manager-less composition are accepted no-ops. For a live target, a mismatched parent address or caller outside its live ancestry rejects with `UNAUTHORIZED`; stale ancestor objects and self-targeting ancestor requests reject before target lookup.
156
160
 
@@ -165,26 +169,24 @@ type SubagentInterruptAuthority =
165
169
  | { readonly kind: 'ancestor'; readonly agent: Agent }
166
170
  ```
167
171
 
168
- Every Activation owns its `AgentHandle` and an `ownedChildren: Set<SessionId>`; because one Session has at most one live Activation, the child Session id identifies the live child without another runtime-incarnation reference. Starting a child or submitting parent-originated work registers the child in a continuation-managed parent's set before the child can run, and that parent cannot settle while the set is non-empty. A top-level or other non-continuation Agent has no Activation and stays outside the waiting graph. Child release happens only after the child Agent is quiescent, every child of that child is disposed, the best-effort final session flush settles, and the child's `AgentHandle` completes disposal.
172
+ Every Activation owns its `AgentHandle` and an `ownedChildren: Set<SessionId>`; because one Session has at most one live Activation, the child Session id identifies the live child without another runtime-incarnation reference. Starting a child or submitting parent-originated work registers the child in a continuation-managed parent's set before the child can run, and that parent cannot settle while the set is non-empty. A top-level or other non-continuation Agent has no Activation and stays outside the waiting graph. Child release happens only after the child has no active Agent work, its Inbox is empty, every child of that child is disposed, the best-effort final session flush settles, and the child's `AgentHandle` completes disposal.
169
173
 
170
174
  Final settlement awaits `ctx.sessions.flush(session)` but ignores its participation boolean because an arbitrary listener cannot prove that a persistence backend stored the state. Rejection is logged without failing the Activation, and the manager still disposes the handle and releases ownership; the persisted child state may then be missing or stale on a later resume. Manager unload invokes an internal manager-wide drain that closes admission and disposes every live forest; `drainContinuableDescendants(parents)` closes admission only below exact live host-owned Agents and disposes their continuable descendants while unrelated forests remain live. Both await already-admitted materializations in their scope, propagate cancellation top-down, release handles child-first, and await every selected branch despite individual failures. Durable child Sessions survive that process-local teardown.
171
175
 
172
176
  ```ts type-equiv
173
- /** Attribution for a model coordinator's follow-up to one of its children. */
174
- interface CoordinatorMessageSource {
175
- readonly kind: 'coordinator'
177
+ /** Durable attribution for one model-authored message between adjacent Agents. */
178
+ interface AgentMessageSource {
179
+ readonly kind: 'agent-message'
176
180
  /** A message another agent addressed to this one (`relay` context form). */
177
181
  readonly form: 'relay'
178
- /** Session id of the agent whose tool call produced the follow-up. */
182
+ /** Session id of the Agent whose tool call produced the message. */
179
183
  readonly senderSessionId: SessionId
180
184
  }
181
185
  ```
182
186
 
183
187
  ```ts type-equiv
184
- /** Options for following up with one continuable child. */
185
- interface SubagentFollowupOptions {
186
- /** Durable attribution retained on the delivered message; it grants no authority. */
187
- readonly source: MessageSource
188
+ /** Options for one model-authored message between adjacent Agents. */
189
+ interface SubagentSendMessageOptions {
188
190
  /** Caller cancellation, owning the operation only until inbox acceptance. */
189
191
  readonly signal: AbortSignal
190
192
  }
@@ -200,33 +202,13 @@ interface ContinuableStart {
200
202
  }
201
203
  ```
202
204
 
203
- An optional continuable-child setup contribution can install scope-local capabilities after base child composition and before Activation publication. The registry is ordered and transactional: a failed or revoked setup rolls back the unpublished Activation, child-scope disposal releases every installation, new registrations affect the next Activation, and registration removal revokes every resident installation immediately.
204
-
205
- `SubagentRuntime.reportFrom()` uses that extension point without adding a second queue or a result-bearing child wrapper. The exact live child Agent authorizes the call; callers cannot name a recipient. The manager derives the only recipient from the child's durable `parentSession`, requires that parent Agent to be live, frames the selected content as one `subagent-report` user message, and returns the message's stable `MessageId`. Quiet delivery uses `Agent.inject()` and does not wake the parent; next-step delivery uses `Agent.steer()`, waking an idle parent or joining a running parent's nearest step boundary. Neither mode concludes the child's turn, and no final answer reports implicitly.
206
-
207
- ```ts type-equiv
208
- /** Durable attribution for a continuable child's explicit parent report. */
209
- interface SubagentReportMessageSource {
210
- readonly kind: 'subagent-report'
211
- /** A message another agent addressed to this one (`relay` context form). */
212
- readonly form: 'relay'
213
- /** Session id of the reporting child. */
214
- readonly senderSessionId: SessionId
215
- }
216
- ```
217
-
218
- ```ts type-equiv
219
- /** Deployment scheduling policy for accepted child reports. */
220
- type SubagentReportDelivery = 'quiet' | 'next-step'
221
- ```
222
-
223
- Reporting is the child's own choice, so the manager keeps a separate account of its own: when a resident Activation settles, it delivers one notice to the child's durable direct parent describing how that epoch ended and carrying its final assistant content. That delivery is unconditional for every child whose id a caller received, happens before the ownership release that would let the parent be judged settled, and reaches a resident parent through the same waking-admission accounting as a report. A parent whose own lineage is already tearing down receives it without a wake, because waking a quiescent Agent starts a turn rather than queueing work. Its provenance is a distinct kind so a transcript never presents a runtime account as something the child wrote.
205
+ When a resident Activation settles, the manager delivers one notice to the child's durable direct parent describing how that epoch ended and carrying its final assistant content. That delivery is unconditional for every child whose id a caller received, happens before the ownership release that would let the parent be judged settled, and reaches a resident parent through the same waking Agent delivery as an Agent message. A parent whose own lineage is already tearing down receives it without a wake, because waking an idle Agent starts a turn rather than queueing work. Its provenance is a distinct kind so a transcript never presents a runtime account as something the child wrote.
224
206
 
225
207
  ```ts type-equiv
226
208
  /**
227
209
  * Durable attribution for the runtime's own account of a continuable child
228
210
  * settling. Deliberately a different kind from
229
- * {@link SubagentReportMessageSource}: a report is content the child chose,
211
+ * {@link AgentMessageSource}: an Agent message is content the sender chose,
230
212
  * while this message is the manager stating what became of the child, and a
231
213
  * transcript that merged them would credit the child with words it never wrote.
232
214
  */
@@ -241,16 +223,6 @@ interface SubagentSettledMessageSource {
241
223
  }
242
224
  ```
243
225
 
244
- ```ts type-equiv
245
- /** Options for one continuable child's report to its direct parent. */
246
- interface SubagentReportOptions {
247
- /** Already-resolved parent scheduling policy. */
248
- readonly delivery: SubagentReportDelivery
249
- /** Caller cancellation, owning authorization and admission until acceptance. */
250
- readonly signal: AbortSignal
251
- }
252
- ```
253
-
254
226
  The provider participates only in preparing the initial creation spec, where `spawn` and `fork` differ. Its returned spec carries only detached provider-specific creation inputs — the optional parent-history seed — and no Agent, `AgentHandle`, prompt delivery, result, disposal, or resume operation. Cold resume does not dispatch through a provider at all: the manager folds the generic descriptor, calls `ctx.agents.resume()` through the same activation-owner scope, and submits the waiting turn.
255
227
 
256
228
  ```ts type-equiv
@@ -293,11 +265,11 @@ interface ContinuableCreateSpec {
293
265
 
294
266
  The descriptor (`SubagentDescriptorData` in [descriptor.ts](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/subagent/subagent/src/descriptor.ts)) is a mode-discriminated durable identity for every session-backed subagent. Both modes carry the provider name. A `one-shot` descriptor optionally carries a caller-owned display `label`; a `continuable` descriptor requires the delegation `description` as its durable creation label and additionally snapshots resolved child `agentOptions.provider`/`model`/`reasoningEffort` 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).
295
267
 
296
- 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: resume-time descriptor authority reads the child's own suffix, while the list-serving identity projection folds `subagent/descriptor` last-wins so the child's own descriptor overrides a fork-seeded ancestor's. 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.
268
+ 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; `Session.inheritedEventCount` remains the fork-lineage boundary: resume-time descriptor authority reads the child's own suffix, while the list-serving identity projection folds `subagent/descriptor` last-wins so the child's own descriptor overrides a fork-seeded ancestor's. A seeded cold list skips a cache hint until an authoritative observation supplies that exact cut. 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.
297
269
 
298
270
  ## Durable enumeration: `listChildren()`, `listDescendants()`, and their entries
299
271
 
300
- `SubagentRuntime.listChildren(parentSessionId)` enumerates the parent's direct session-backed subagents from the live-preferred merge of `ctx.sessions.list()` and optional `ctx.sessionPersistence.list()` — no query service, and no Agent is loaded or resumed. Candidates are the direct children whose durable header carries `origin: 'subagent'`; the marker classifies enumeration and coarse generic-route denial but cannot establish a valid descriptor, resumability, or authorization — the projection fold owns identity, and the Activation contract owns resume. Each row's `mode`/`label` is the registered `subagent` projection unit's value, served through a three-rung ladder: the registry's watermark cache for a live child (zero log reads); the optional projection checkpoint cache for a cold one (`cachedSnapshot` — an identity passing the own-suffix seq gate is final, because an own descriptor is immutable once appended); otherwise one `persistence.inspect()` reading folded through the registry (bounded concurrency, recomputed per listing). The cache is a pure optional accelerator: absent, serving the `null` sentinel or missing the key, failing the seq gate, or faulting, it falls silently through to the authoritative refold. The fold is `subagent/descriptor` last-wins with no failure channel: the child's own descriptor overrides a fork-seeded ancestor's, and a malformed or unknown-version payload folds to a serializable `null` sentinel, treated as no value. The result is one `SubagentListEntry[]` in `createdAt`-then-id order: a served identity yields a `child` entry with `mode: 'one-shot' | 'continuable'` and `activity: 'running' | 'inactive'`; continuable entries always carry `label`, while one-shot entries carry it only when the start caller supplied presentation metadata. A settled candidate whose fold served no identity yields a `corrupt` diagnostic — missing, malformed, and unknown-version descriptors deliberately undistinguished (`unsupported` remains in the type but is never produced); a running candidate without an identity is omitted (the creation window before its descriptor lands); a failed cold inspection yields one `unavailable` diagnostic retried on the next listing, so one damaged sibling cannot hide healthy children. `hasChildren` marks a direct descendant with durable subagent origin, read from the same merged material. Activity snapshots only whether the logical record is live in `ctx.sessions`, not outcome or resumability. Absent persistence, enumeration is live-only rather than an error — a cold child cannot be resumed then either. `listChildren()` throws `SubagentError` with code `SUBAGENT_CONTROL_PROJECTIONS_UNAVAILABLE` when the `ctx.sessionProjections` registry is absent and `SUBAGENT_CONTROL_SESSION_STORE_UNAVAILABLE` when the session store is, both checked before any read so a deployment with zero children still fails deterministically; the list tool requires `ctx.subagents` and `ctx.agents` at plugin load. A service consumer such as a UI can display both modes and choose an unlabeled one-shot fallback, while the model-facing `list_agents` adapter (the separately loadable `/list-agents` plugin of [dsh-tool-subagent-control](https://github.com/deepseek-ai/deepseek-harness/tree/master/packages/subagent/tool-subagent-control)) keeps only continuable entries and refines status through the live Agent registry into its own `running`/`idle`/`ready` vocabulary, whose `ready` names a storage-only child as resumable rather than terminal. 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 read-path rationale lives in [the list-identity-projection Agent Note](https://github.com/deepseek-ai/deepseek-harness/blob/master/.agents/notes/implemented/architecture/2026-08-06-subagent-list-identity-projection.md).
272
+ `SubagentRuntime.listChildren(parentSessionId)` enumerates the parent's direct session-backed subagents from the live-preferred merge of `ctx.sessions` and the session-query engine's `listSessions()` — no Agent is loaded or resumed. Candidates are the direct children whose durable header carries `origin: 'subagent'`; the marker classifies enumeration and coarse generic-route denial but cannot establish a valid descriptor, resumability, or authorization — the projection fold owns identity, and the Activation contract owns resume. Each row's `mode`/`label` is the registered `subagent` projection unit's value, served through a three-rung ladder: the registry's watermark cache for a live child (zero log reads); the optional projection checkpoint cache for a cold one (`cachedSnapshot` — an identity passing the own-suffix seq gate is final, because an own descriptor is immutable once appended); otherwise one `query.observeSession()` cold observation folded through the registry (bounded concurrency, recomputed per listing). The cache is a pure optional accelerator: absent, serving the `null` sentinel or missing the key, failing the seq gate, or faulting, it falls silently through to the authoritative refold. The fold is `subagent/descriptor` last-wins with no failure channel: the child's own descriptor overrides a fork-seeded ancestor's, and a malformed or unknown-version payload folds to a serializable `null` sentinel, treated as no value. The result is one `SubagentListEntry[]` in `createdAt`-then-id order: a served identity yields a `child` entry with `mode: 'one-shot' | 'continuable'` and `activity: 'running' | 'inactive'`; continuable entries always carry `label`, while one-shot entries carry it only when the start caller supplied presentation metadata. A settled candidate whose fold served no identity yields a `corrupt` diagnostic — missing, malformed, and unknown-version descriptors deliberately undistinguished (`unsupported` remains in the type but is never produced); a running candidate without an identity is omitted (the creation window before its descriptor lands); a failed cold inspection yields one `unavailable` diagnostic retried on the next listing, so one damaged sibling cannot hide healthy children. `hasChildren` marks a direct descendant with durable subagent origin, read from the same merged material. Activity snapshots only whether the logical record is live in `ctx.sessions`, not outcome or resumability. Absent persistence, enumeration is live-only rather than an error — a cold child cannot be resumed then either. `listChildren()` throws `SubagentError` with code `SUBAGENT_CONTROL_PROJECTIONS_UNAVAILABLE` when the `ctx.sessionProjections` registry is absent and `SUBAGENT_CONTROL_SESSION_STORE_UNAVAILABLE` when the session store is, both checked before any read so a deployment with zero children still fails deterministically; the list tool requires `ctx.subagents` and `ctx.agents` at plugin load. A service consumer such as a UI can display both modes and choose an unlabeled one-shot fallback, while the model-facing `list_agents` adapter (the separately loadable `/list-agents` plugin of [dsh-tool-subagent-control](https://github.com/deepseek-ai/deepseek-harness/tree/master/packages/subagent/tool-subagent-control)) keeps only continuable entries and refines status through the live Agent registry into its own `running`/`idle`/`ready` vocabulary, whose `ready` names a storage-only child as resumable rather than terminal. 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 archived list-identity-projection record](https://github.com/deepseek-ai/deepseek-harness/blob/master/.agents/notes/archived/architecture/2026-08-06-subagent-list-identity-projection.md) documents the original read-path decision.
301
273
 
302
274
  `SubagentRuntime.listDescendants(rootSessionId)` applies the same live-preferred corpus and projection-backed interpretation to the root's complete descendant tree in stable pre-order. Ordinary sessions and one-shot children remain traversal nodes, so continuable descendants below them are discovered; only `origin: 'subagent'` candidates produce rows. Each returned child or diagnostic adds its position from the enumerated durable header, while a cold inspection revalidates that complete lifecycle before serving identity:
303
275
 
@@ -504,11 +476,11 @@ Generated from source by `scripts/gen-cordis-catalog.ts` (verified fresh by `pnp
504
476
 
505
477
  ### `ctx.subagentModelSelection` — `SubagentModelSelectionConfig`
506
478
 
507
- Singleton settings owner read by delegation tools when an Agent is published.
479
+ Singleton settings owner read when delegation tools are composed for a Session.
508
480
 
509
481
  ```ts cordis-catalog
510
482
  /**
511
- * Read a detached selection preference for the next eligible Agent publication.
483
+ * Read a detached selection preference for the next eligible Session composition.
512
484
  * @returns the enabled state and exact allowed routes.
513
485
  */
514
486
  current(): SubagentModelSelectionSettings
@@ -535,21 +507,20 @@ Named provider registry with one-shot runs, durable discovery, and continuable-c
535
507
  async startContinuable(spec: ContinuableStartSpec): Promise<ContinuableStart>
536
508
 
537
509
  /**
538
- * Deliver one later message to a continuable child as its next FIFO turn. A
539
- * resident child's Agent inbox accepts it directly (waking a `waiting`
540
- * Activation), while an absent one is cold-resumed from its persisted
541
- * Session. The Agent inbox is the only queue, so every accepted message has
542
- * one observable order.
543
- * @param parent - the exact live direct parent authorizing this delivery.
544
- * @param childId - durable child session id.
545
- * @param content - user-role content to deliver.
546
- * @param options - the message source fields and caller cancellation, which stops the
547
- * operation only before inbox acceptance.
510
+ * Steer one model-authored message to the sender's direct parent or direct
511
+ * continuable child. A running target admits it at the nearest step boundary;
512
+ * an idle target starts a turn, and an absent direct child cold-resumes from
513
+ * persistence. The service derives durable sender attribution from the exact
514
+ * live sender. Caller cancellation stops only pre-acceptance work.
515
+ * @param sender - exact live Agent authorizing and originating the message.
516
+ * @param targetId - durable direct-parent or direct-child session id.
517
+ * @param content - model-authored content to deliver.
518
+ * @param options - caller cancellation before inbox acceptance.
548
519
  * @returns the accepted message's inbox id.
549
- * @throws when continuation services are unavailable, parent authority is
550
- * rejected, or the message was not admitted.
520
+ * @throws when continuation services are unavailable, adjacency is rejected,
521
+ * or the message was not admitted.
551
522
  */
552
- async followup( parent: Agent, childId: SessionId, content: ContentBlock[], options: SubagentFollowupOptions, ): Promise<MessageId>
523
+ async sendMessage( sender: Agent, targetId: SessionId, content: ContentBlock[], options: SubagentSendMessageOptions, ): Promise<MessageId>
553
524
 
554
525
  /**
555
526
  * Interrupt one live continuable child's current turn under a human parent
@@ -568,29 +539,6 @@ async followup( parent: Agent, childId: SessionId, content: ContentBlock[], opti
568
539
  */
569
540
  interrupt(targetSessionId: SessionId, authority: SubagentInterruptAuthority): void
570
541
 
571
- /**
572
- * Deliver selected content from one live continuable child to its durable
573
- * direct parent. The child is the authority credential; callers cannot name a
574
- * recipient. Reporting does not conclude the child's turn or Activation.
575
- * @param child - exact live reporting child.
576
- * @param content - selected model-facing content.
577
- * @param options - parent scheduling and pre-acceptance cancellation.
578
- * @returns the stable identity of the parent-accepted message.
579
- * @throws when continuation services are unavailable, sender authorization
580
- * fails, or the direct parent is not live.
581
- */
582
- async reportFrom( child: Agent, content: ContentBlock[], options: SubagentReportOptions, ): Promise<MessageId>
583
-
584
- /**
585
- * Compose one deployment capability into every continuable child's
586
- * unpublished creation context on fresh creation and cold resume. Grants wait
587
- * for the next Activation; removing the contribution revokes every resident
588
- * installation immediately.
589
- * @param contribution - synchronous child-scope installer.
590
- * @returns the exact Cordis effect disposer.
591
- */
592
- registerContinuableSetup(contribution: ContinuableSetupContribution): () => void
593
-
594
542
  /**
595
543
  * Close continuable admission below exact live parent Agents, stop only their
596
544
  * visible descendant Activations synchronously, then await admitted scoped
@@ -660,9 +608,9 @@ listDescendants(rootSessionId: SessionId, signal?: AbortSignal): Promise<Subagen
660
608
  * @param parentSessionId - parent session whose direct children are listed.
661
609
  * @param signal - carrier cancellation forwarded to Session queries.
662
610
  * @returns the catalog view for that parent.
663
- * @throws {TypertRemoteFailure} `bad-request` for an empty parent id,
664
- * `cancelled` for an aborted read, `subagent-projections-unavailable` when
665
- * the deployment has no projection registry, otherwise `internal`.
611
+ * @throws {RemoteError} `gateway/bad-request` for an empty parent id,
612
+ * `gateway/cancelled` for an aborted read, `subagent/projections-unavailable` when
613
+ * the deployment has no projection registry, otherwise `gateway/internal`.
666
614
  */
667
615
  @Remote('list') async remoteExportList(parentSessionId: SessionId, signal: AbortSignal): Promise<SubagentCatalog>
668
616
 
@@ -670,15 +618,18 @@ listDescendants(rootSessionId: SessionId, signal?: AbortSignal): Promise<Subagen
670
618
  * Deliver one browser-authored message to a continuable child through the
671
619
  * exact live direct parent, retaining the caller-minted request identity and
672
620
  * validated browser zone on the accepted message. Success identifies the
673
- * message the child's FIFO inbox accepted; later execution is independent of
674
- * this call.
675
- * @param request - durable address, minted identity, content, and optional browser zone.
621
+ * message the child's inbox accepted; later execution is independent of this
622
+ * call. Queue delivery targets a later turn; steer delivery targets the
623
+ * nearest step and retains the Agent loop's best-effort fallback semantics.
624
+ * Image parts are admitted and persisted through the attachment store
625
+ * before delivery, and the child's model must accept image input.
626
+ * @param request - durable address, delivery, minted identity, content, and optional browser zone.
676
627
  * @param signal - carrier cancellation, owning the call until inbox acceptance.
677
628
  * @returns the accepted message's inbox identity.
678
- * @throws {TypertRemoteFailure} `bad-request`, `invalid-time-zone`,
679
- * `subagent-parent-unavailable`, `subagent-not-resumable`,
680
- * `subagent-unauthorized`, `subagent-delivery-unavailable`, `cancelled`, or
681
- * `internal`.
629
+ * @throws {RemoteError} `gateway/bad-request`, `subagent/attachment-invalid`,
630
+ * `subagent/invalid-time-zone`, `subagent/parent-unavailable`,
631
+ * `subagent/not-resumable`, `subagent/unauthorized`,
632
+ * `subagent/delivery-unavailable`, `gateway/cancelled`, or `gateway/internal`.
682
633
  */
683
634
  @Remote('prompt') async prompt(request: SubagentPromptRequest, signal: AbortSignal): Promise<SubagentPromptReceipt>
684
635
 
@@ -692,9 +643,9 @@ listDescendants(rootSessionId: SessionId, signal?: AbortSignal): Promise<Subagen
692
643
  * @param parentSessionId - durable direct parent whose authority is claimed.
693
644
  * @param mode - required continuable-address discriminator.
694
645
  * @returns acknowledgement that the cancel signal was admitted, not that the target is quiescent.
695
- * @throws {TypertRemoteFailure} `bad-request` for an empty id,
696
- * `subagent-unauthorized` when the address does not own the live target,
697
- * otherwise `internal`.
646
+ * @throws {RemoteError} `gateway/bad-request` for an empty id,
647
+ * `subagent/unauthorized` when the address does not own the live target,
648
+ * otherwise `gateway/internal`.
698
649
  */
699
650
  @Remote('interruptByParent') interruptByParent( childSessionId: SessionId, parentSessionId: SessionId, mode: 'continuable', ): SubagentInterruptReceipt
700
651
 
@@ -726,6 +677,8 @@ list(): string[]
726
677
  * fulfills; a rejection therefore has no run for the caller to dispose and
727
678
  * emits no run lifecycle events. Post-publication turn and infrastructure
728
679
  * failures settle through the returned run.
680
+ * A catalog append failure disposes the run and handles its result rejection;
681
+ * the caller receives the catalog error even if disposal also fails.
729
682
  * @param name - the provider to use.
730
683
  * @param request - child label, prompt, parent, signal, and optional capabilities.
731
684
  * @returns the published holder-owned run.
@@ -109,14 +109,15 @@ interface SubprocessSpawnSpec {
109
109
  stdio: SubprocessStdio
110
110
  /**
111
111
  * Positive finite grace period in milliseconds, no greater than
112
- * `MAX_TIMER_DELAY_MS`, for the {@link SubprocessHandle.terminate} escalation
113
- * and for draining still-open collected pipes after the process exits (an
114
- * inherited descriptor held by a surviving descendant cannot hold the
115
- * outcome open indefinitely).
112
+ * `MAX_TIMER_DELAY_MS`, available to the provider's termination procedure
113
+ * and used for draining still-open collected pipes after the process exits
114
+ * (an inherited descriptor held by a survivor cannot hold the outcome open
115
+ * indefinitely). Providers document whether range termination is staged or
116
+ * immediate.
116
117
  */
117
118
  graceMs: number
118
119
  /**
119
- * Abort signal — starts the terminate escalation on the process tree when
120
+ * Abort signal — starts the terminate escalation on the managed range when
120
121
  * it fires. The caller owns deadlines and cause classification; this seam
121
122
  * only reacts to the abort.
122
123
  */
@@ -132,23 +133,20 @@ interface SubprocessSpawnSpec {
132
133
  }
133
134
  ```
134
135
 
135
- ## Handles: streams, readers, and tree-scoped termination
136
+ ## Handles: streams, readers, and managed-range termination
136
137
 
137
- A spawn returns a live handle immediately. Collect-mode readers take whole-stream byte offsets and never consume, so independent readers cannot steal one another's deltas; piped streams belong to the caller. Termination is tree-scoped on every platform: `terminate()` the only termination verb — escalates SIGTERM→grace→SIGKILL, and `waitForExit()` observes the whole tree enough for a consumer to build its own teardown ladder (the ACP backend's stdin-EOF-first `disposeAcpChild` is the template).
138
+ A spawn returns a live handle synchronously while target and managed-range identities remain provider-private. Collect-mode readers take whole-stream byte offsets and never consume, so independent readers cannot steal one another's deltas; piped streams belong to the caller. `terminate()` starts the provider's documented procedure, and `waitForExit()` observes the same provider-managed range; staged providers may use `graceMs`, while immediate providers do not delay. Consumers can build their own teardown ladders over those two operations (the ACP backend's stdin-EOF-first `disposeAcpChild` is the template).
138
139
 
139
140
  ```ts type-equiv
140
141
  /**
141
- * A live child process rooted in its own process tree. Collected output
142
+ * A live subprocess and its provider-managed process range. Collected output
142
143
  * remains readable after exit; piped streams belong to the caller.
143
144
  *
144
- * Termination is tree-scoped everywhere: POSIX signals the detached process
145
- * group (falling back to the direct child when the group is gone), Windows
146
- * terminates the tree via `taskkill /T`, so helper processes cannot outlive
147
- * the handle unnoticed.
145
+ * Termination and {@link SubprocessHandle.waitForExit} use the same managed
146
+ * range. Each provider documents the range it can observe and its signalling
147
+ * and observation limits.
148
148
  */
149
149
  interface SubprocessHandle {
150
- /** Process id (tree root); -1 when the spawn itself failed. */
151
- readonly pid: number
152
150
  /** The child's stdin, present iff spawned with `stdin: 'pipe'`. */
153
151
  readonly stdin: Writable | undefined
154
152
  /** The child's raw stdout, present iff spawned with `stdout: 'pipe'`. */
@@ -157,20 +155,20 @@ interface SubprocessHandle {
157
155
  readonly stderr: Readable | undefined
158
156
  /** Offset-based readers for collect-mode streams (also readable after exit). */
159
157
  readonly collected: SubprocessCollectedOutputs
160
- /** Resolves at process close with exit facts; rejects only for spawn-level failures. */
158
+ /** Resolves with spawned-command exit facts; rejects for spawn or provider failures. */
161
159
  readonly done: Promise<SubprocessOutcome>
162
160
  /**
163
- * Begin the SIGTERM `graceMs` SIGKILL escalation on the process tree
164
- * (Windows force-terminates immediately) — the seam's only termination
165
- * verb. Idempotent, a no-op once the tree is gone (the pid may be reused),
166
- * and also triggered by the spec's abort signal.
161
+ * Begin the provider's documented termination procedure on the managed range
162
+ * — the seam's only termination verb. Idempotent, a no-op once that range is
163
+ * gone, and also triggered by the spec's abort signal.
167
164
  */
168
165
  terminate(): void
169
166
  /**
170
- * Wait until the process tree has exitedthe tree, not just the direct
171
- * child, so a still-running helper is observable before teardown returns.
167
+ * Wait until the same managed range is empty — not just until the spawned
168
+ * command reports its outcome, so surviving work remains observable.
172
169
  * @param signal - optional bound for the wait.
173
- * @returns `true` when the tree exited, `false` when the signal aborted first.
170
+ * @returns `true` when the managed range is empty, `false` when the signal aborted first.
171
+ * @throws when the selected provider can no longer observe its managed range.
174
172
  */
175
173
  waitForExit(signal?: AbortSignal): Promise<boolean>
176
174
  }
@@ -249,7 +247,7 @@ The terminal spec fully specifies argv, cwd, environment overrides, dimensions,
249
247
 
250
248
  ## Service behavior
251
249
 
252
- The abstract [`SubprocessRuntime`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/subprocess/subprocess/src/index.ts) Service Definition specifies execution-world coordinates, executable lookup, ordinary `spawn`, and `spawnTerminal`. [`LocalSubprocessRuntime`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/subprocess/subprocess-local/src/index.ts) provides them with detached process trees, per-disposition wiring, credential scrubbing, `node-pty`, platform process inspection, and terminate-and-join disposal. See [`dsh-subprocess`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/subprocess/subprocess/README.md) for the Service Definition contract and [`dsh-subprocess-local`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/subprocess/subprocess-local/README.md) for local mechanics.
250
+ The abstract [`SubprocessRuntime`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/subprocess/subprocess/src/index.ts) Service Definition specifies execution-world coordinates, executable lookup, ordinary `spawn`, and `spawnTerminal`. [`LocalSubprocessRuntime`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/subprocess/subprocess-local/src/index.ts) provides them with platform-selected managed ranges, per-disposition wiring, credential scrubbing, `node-pty`, platform process inspection, and terminate-and-join disposal. See [`dsh-subprocess`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/subprocess/subprocess/README.md) for the Service Definition contract and [`dsh-subprocess-local`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/subprocess/subprocess-local/README.md) for local mechanics.
253
251
 
254
252
  <!-- BEGIN GENERATED cordis-surface (gen-cordis-catalog.ts) — do not edit between markers -->
255
253
 
@@ -285,9 +283,9 @@ Abstract subprocess service. Subclass, implement spawn, and load the subclass as
285
283
  Implementations must honor these semantics:
286
284
 
287
285
  - Executable paths belong to one execution world shared with the mounted filesystem provider.
288
- - spawn returns immediately with a live handle; `done` resolves at process close with exit facts and rejects only for spawn-level failures.
286
+ - spawn returns a live handle synchronously. Target identity remains provider-private; `done` resolves with the spawned command's exit facts and may reject for spawn or provider failures.
289
287
  - Collect-mode readers are offset-based and non-consuming, so independent readers never consume one another's output; lossy reads report truncation and the spill file holding the complete stream when one exists. Piped streams are handed to the caller raw and never buffered here.
290
- - SubprocessHandle.terminate (and the spec's abort signal) escalates SIGTERM→grace→SIGKILL — the only termination verb tree-scoped on every platform. SubprocessHandle.waitForExit observes whole-tree liveness, so a consumer-owned teardown ladder can hold each tier on real quiescence.
288
+ - SubprocessHandle.terminate (and the spec's abort signal) starts the provider's documented procedure against its managed range. SubprocessHandle.waitForExit observes that same range so a consumer-owned teardown ladder can hold each tier on real quiescence; each provider documents its signalling and observability limits.
291
289
  - Disposal of the service terminates all still-running managed processes and awaits their exit.
292
290
  - spawnTerminal owns terminal allocation, text transport, foreground groups, signalling, and whole-session quiescence behind one awaited termination method; readiness and persistent-shell policy stay in the PTY consumer. Its output stream ends after queued terminal output when the top-level process exits.
293
291
 
@@ -310,13 +308,14 @@ abstract resolveExecutable( command: string, env?: Readonly<Record<string, strin
310
308
  * applies no defaults.
311
309
  * @param spec - argv, directory, stdio dispositions, grace, cancellation, and environment.
312
310
  * @returns the live process handle (streams/readers, signalling, outcome promise).
311
+ * @throws synchronously when pre-aborted or when argv, cwd, environment, or grace is invalid before handle creation.
313
312
  */
314
313
  abstract spawn(spec: SubprocessSpawnSpec): SubprocessHandle
315
314
 
316
315
  /**
317
316
  * Allocate a real terminal and start one owned process session. This is the
318
317
  * only non-pipe process primitive: implementations own terminal byte I/O,
319
- * foreground groups, signals, and complete session-tree cleanup.
318
+ * foreground groups, signals, and whole-session quiescence.
320
319
  * @param spec - fully specified argv, cwd, environment, dimensions, grace, and allocation cancellation.
321
320
  * @returns the live terminal handle after allocation succeeds.
322
321
  */
@@ -42,7 +42,9 @@ interface ToolProviderResult {
42
42
 
43
43
  ## Prompt sections
44
44
 
45
- `PromptSection` is a readonly same-process registration contract. Its text may be static or resolved from the current assembly context. Sections sort by ascending order and then code-unit name; `FIRST_PARTY_SECTION_ORDER` publishes the sparse named allocation for repository-owned contributions. One effective `complete` section becomes the sole prompt section after cooperative assembly.
45
+ The exported `PERSONA_PREFIX_SECTION` (`deployment:persona-prefix`) and `PERSONA_SUFFIX_SECTION` (`deployment:persona-suffix`) name the slots shared by global configuration and scoped contributions. Their `PromptSectionOrderName` entries are `DEPLOYMENT_PERSONA_PREFIX` and `DEPLOYMENT_PERSONA_SUFFIX`; the [package README](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/core/system-prompt/README.md#configure-the-prompt) owns their placement and template configuration.
46
+
47
+ `PromptSection` is a readonly same-process registration contract. Its text may be static or resolved from the current assembly context. Sections sort by ascending order and then code-unit name; repository contributors resolve the service-owned named allocation through `getSectionOrder()`. Runtime-context contributors resolve their independent allocation through `getContextOrder()`. One effective `complete` section becomes the sole prompt section after cooperative assembly. agent-loop renders the assembled sections with `renderPrompt` and commits the text as a `system/message` surface node — appended as surface node 0 on the first step, then replaced in place when the rendered text changes or, when the prepared call declares `systemPromptUpdate: 'in-history'`, appended after the cached history for non-empty updates in a continuing series — so the prompt reaches the model as a message of derived history rather than as a request field ([decision](https://github.com/deepseek-ai/deepseek-harness/blob/master/.agents/notes/implemented/architecture/2026-09-02-system-prompt-as-surface-node.md); [decision rule](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/core/agent-loop/README.md#understand-the-implementation)).
46
48
 
47
49
  ```ts type-equiv
48
50
  /** One contributed section of the system prompt (registry input). */
@@ -51,8 +53,7 @@ interface PromptSection {
51
53
  readonly name: string
52
54
  /**
53
55
  * Sections are concatenated in ascending order. Equal orders use code-unit
54
- * name order. Repository-owned placements use
55
- * {@link FIRST_PARTY_SECTION_ORDER}.
56
+ * name order.
56
57
  */
57
58
  readonly order: number
58
59
  /**
@@ -112,6 +113,20 @@ Registry service for the prompt inputs assembled before each model step.
112
113
  */
113
114
  section(section: PromptSection): () => void
114
115
 
116
+ /**
117
+ * Resolve the centrally owned placement of a repository prompt section.
118
+ * @param name - stable section placement name.
119
+ * @returns the section's numeric sort order.
120
+ */
121
+ getSectionOrder(name: PromptSectionOrderName): number
122
+
123
+ /**
124
+ * Resolve the centrally owned placement of a repository runtime context.
125
+ * @param name - stable context placement name.
126
+ * @returns the context's numeric sort order.
127
+ */
128
+ getContextOrder(name: PromptContextOrderName): number
129
+
115
130
  /**
116
131
  * Register ordered dynamic context in the calling context's scope. Scoped
117
132
  * entries shadow global entries with the same name.
@@ -15,7 +15,7 @@ Source: [`packages/llm/token-meter/src/types.ts`](https://github.com/deepseek-ai
15
15
  /** Detached immutable request-pressure and surface snapshot at one consumed log revision. */
16
16
  interface TokenMeasurement {
17
17
  /** Number of durable events consumed; equal to the next unread event seq. */
18
- readonly logRevision: number
18
+ readonly logRevision: SessionLogOffset
19
19
  /** Provider or heuristic anchor used for this measurement. */
20
20
  readonly baseline: TokenMeasurementBaseline
21
21
  /** Signed repricing of current surface content relative to the baseline anchor. */
@@ -37,7 +37,7 @@ Every measurement resolves the effective envelope's routed provider/model to tha
37
37
  /** One token-priced node in the current ordered session surface. */
38
38
  interface TokenSurfaceNode {
39
39
  /** Durable sequence number of the surface event. */
40
- readonly seq: number
40
+ readonly seq: SessionSeq
41
41
  /**
42
42
  * Request-pressure tokens for the exact message projected by this node under
43
43
  * the measured route: image occurrences carry the route's declared visual
@@ -81,7 +81,8 @@ Replay owner for one service-wide estimator and isolated per-session folds.
81
81
  * usage is reused only when the latest successful call's canonical request
82
82
  * envelope matches `requestHeader` and its total is no lower than that
83
83
  * call's full route-priced anchor; otherwise the complete envelope and
84
- * surface are repriced.
84
+ * surface are repriced. The anchor includes all surface nodes immediately
85
+ * before the assistant message, including inputs admitted after step/start.
85
86
  *
86
87
  * `requestHeader` replaces the latest logged envelope for pressure and node
87
88
  * pricing; the node set always describes the current session surface. Every
@@ -265,14 +265,14 @@ PTC mode's bridge additionally exposes each settled sub-dispatch to the `tools/p
265
265
  * copy a listener may reshape. `content` is the RENDERED result projection
266
266
  * (what a native `tool/result` would carry) — the program itself received
267
267
  * the structured `value` (or just the error message on failure); only the
268
- * `tool/code-dispatch` event's copy changes.
268
+ * `tool/ptc-dispatch` event's copy changes.
269
269
  */
270
270
  interface PtcDispatchLog {
271
271
  /** The outer `run_code` execution. */
272
272
  readonly exec: ToolExecution
273
273
  /** The calling agent (the scope routing key and the spill owner), when the outer call has one. */
274
274
  readonly agent?: Agent
275
- /** Deterministic sub-call id (`<parent>:code:<n>`). */
275
+ /** Opaque sub-call id; new calls use `<parent>:ptc:<n>`. */
276
276
  readonly subCallId: ToolCallId
277
277
  /** The dispatched sub-tool name. */
278
278
  readonly name: string
@@ -370,7 +370,7 @@ interface ToolExecutionFailure {
370
370
  type ToolExecutionResult = ToolExecutionSuccess | ToolExecutionFailure
371
371
  ```
372
372
 
373
- The result carries only the outcome. Call identity remains on the immutable `ToolExecution` that accompanies it through every hook and on the durable `tool/call` / `tool/result` session events, so wrappers cannot create a second, disagreeing identity. The canonical `value` is execution-local: the loop persists only `content`, `error`, and `meta`, while `tool/code-dispatch` stores the sub-call's rendered `content` and `isError` verbatim. Replay reproduces presentation but cannot reconstruct canonical intermediate values.
373
+ The result carries only the outcome. Call identity remains on the immutable `ToolExecution` that accompanies it through every hook and on the durable `tool/call` / `tool/result` session events, so wrappers cannot create a second, disagreeing identity. The canonical `value` is execution-local: the loop persists only `content`, `error`, and `meta`, while `tool/ptc-dispatch` stores the sub-call's rendered `content` and `isError` verbatim. Replay reproduces presentation but cannot reconstruct canonical intermediate values.
374
374
 
375
375
  On success the registry snapshots and validates the body value, freezes it, and invokes the pure renderer plus the optional top-level-call metadata projector. It separately materializes the durable presentation fields immediately before `tools/result`; an invalid value, renderer/projector failure, or non-JSON presentation becomes a JSON-safe `isError`. The final live observer therefore sees the exact execution-local value beside fields safe for the later durable append.
376
376
 
@@ -677,13 +677,13 @@ Source: [`packages/core/tools/src/index.ts`](https://github.com/deepseek-ai/deep
677
677
 
678
678
  #### `tools/ptc-dispatch-log` — waterfall
679
679
 
680
- Allow a listener to replace content in the DURABLE LOG COPY of one `run_code` sub-dispatch outcome before the bridge appends its `tool/code-dispatch` event. `next()` keeps the content unchanged; a listener may return replacement blocks (e.g. the spill policy's preview + locator for an oversized text result). Only the logged copy is affected — the program already received the complete value, and the model sees neither. A throwing listener is contained: the bridge falls back to logging the original settled content. Scope-filtered dispatch (`@deepseek-ai/dsh-scope`): agent-scoped listeners receive only that agent's dispatches.
680
+ Allow a listener to replace content in the DURABLE LOG COPY of one `run_code` sub-dispatch outcome before the bridge appends its `tool/ptc-dispatch` event. `next()` keeps the content unchanged; a listener may return replacement blocks (e.g. the spill policy's preview + locator for an oversized text result). Only the logged copy is affected — the program already received the complete value, and the model sees neither. A throwing listener is contained: the bridge falls back to logging the original settled content. Scope-filtered dispatch (`@deepseek-ai/dsh-scope`): agent-scoped listeners receive only that agent's dispatches.
681
681
 
682
682
  ```ts cordis-catalog
683
683
  /**
684
684
  * Allow a listener to replace content in the DURABLE LOG COPY of one
685
685
  * `run_code` sub-dispatch outcome before the bridge appends its
686
- * `tool/code-dispatch` event. `next()` keeps the
686
+ * `tool/ptc-dispatch` event. `next()` keeps the
687
687
  * content unchanged; a listener may return replacement blocks (e.g. the
688
688
  * spill policy's preview + locator for an oversized text result). Only the
689
689
  * logged copy is affected — the program already received the complete