dsh-plugin-dev-kb 1.0.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 (234) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +56 -0
  3. package/cordis.patch.yml +12 -0
  4. package/kb/INDEX.md +210 -0
  5. package/kb/README.md +69 -0
  6. package/kb/extra/AGENTS.md +75 -0
  7. package/kb/extra/api-gateway.md +164 -0
  8. package/kb/extra/api-gateway.zh.md +164 -0
  9. package/kb/extra/cookbook/adding-a-vendored-package.md +59 -0
  10. package/kb/extra/cookbook/adding-a-vendored-package.zh.md +59 -0
  11. package/kb/extra/cookbook/maintaining-dsh-code-review.md +64 -0
  12. package/kb/extra/cookbook/maintaining-dsh-code-review.zh.md +64 -0
  13. package/kb/extra/cookbook/responding-to-pr-review-on-a-stack.md +32 -0
  14. package/kb/extra/cookbook/responding-to-pr-review-on-a-stack.zh.md +32 -0
  15. package/kb/extra/defensive-patterns.md +33 -0
  16. package/kb/extra/defensive-patterns.zh.md +33 -0
  17. package/kb/extra/development.md +171 -0
  18. package/kb/extra/development.zh.md +171 -0
  19. package/kb/extra/event-producer-consumer.md +76 -0
  20. package/kb/extra/event-producer-consumer.zh.md +78 -0
  21. package/kb/extra/glossary.md +45 -0
  22. package/kb/extra/glossary.zh.md +45 -0
  23. package/kb/extra/graph-atlas.md +24 -0
  24. package/kb/extra/graph-atlas.zh.md +26 -0
  25. package/kb/extra/i18n/README.md +60 -0
  26. package/kb/extra/i18n/README.zh.md +60 -0
  27. package/kb/extra/i18n/style-samples.md +87 -0
  28. package/kb/extra/i18n/terminology.md +214 -0
  29. package/kb/extra/i18n/translation-prompt.md +263 -0
  30. package/kb/extra/i18n/translation-rules.md +69 -0
  31. package/kb/extra/i18n/translation-rules.zh.md +69 -0
  32. package/kb/extra/module-graph.md +1641 -0
  33. package/kb/extra/module-graph.zh.md +1643 -0
  34. package/kb/extra/postmortem/0001-acp-default-export-drops-inject.md +113 -0
  35. package/kb/extra/postmortem/0001-acp-default-export-drops-inject.zh.md +113 -0
  36. package/kb/extra/postmortem/0002-js-expression-disabled-filesystem-tools.md +47 -0
  37. package/kb/extra/postmortem/0002-js-expression-disabled-filesystem-tools.zh.md +47 -0
  38. package/kb/extra/postmortem/0003-web-agent-gui-feedback-loop.md +53 -0
  39. package/kb/extra/postmortem/0003-web-agent-gui-feedback-loop.zh.md +53 -0
  40. package/kb/extra/postmortem/0004-landlock-partial-notice-misclassified-child-failures.md +55 -0
  41. package/kb/extra/postmortem/0004-landlock-partial-notice-misclassified-child-failures.zh.md +55 -0
  42. package/kb/extra/postmortem/README.md +18 -0
  43. package/kb/extra/postmortem/README.zh.md +18 -0
  44. package/kb/extra/rescope.md +53 -0
  45. package/kb/extra/rescope.zh.md +53 -0
  46. package/kb/extra/subsystems/attachment.md +125 -0
  47. package/kb/extra/subsystems/attachment.zh.md +125 -0
  48. package/kb/extra/subsystems/extensions.md +364 -0
  49. package/kb/extra/subsystems/extensions.zh.md +364 -0
  50. package/kb/extra/subsystems/feedback.md +266 -0
  51. package/kb/extra/subsystems/feedback.zh.md +266 -0
  52. package/kb/extra/testing.md +49 -0
  53. package/kb/extra/testing.zh.md +49 -0
  54. package/kb/extra/web-styling.md +25 -0
  55. package/kb/extra/web-styling.zh.md +25 -0
  56. package/kb/meta/search-index.json +1328 -0
  57. package/kb/meta/site-pages.txt +168 -0
  58. package/kb/meta/source.json +13 -0
  59. package/kb/meta/topics.md +75 -0
  60. package/kb/site/develop/basic/config.md +108 -0
  61. package/kb/site/develop/basic/index.md +146 -0
  62. package/kb/site/develop/basic/publish.md +185 -0
  63. package/kb/site/develop/basic/tool.md +54 -0
  64. package/kb/site/develop/cordis-tutorial/01-first-plugin.md +95 -0
  65. package/kb/site/develop/cordis-tutorial/02-lifecycle-and-effects.md +98 -0
  66. package/kb/site/develop/cordis-tutorial/03-services.md +98 -0
  67. package/kb/site/develop/cordis-tutorial/04-events.md +144 -0
  68. package/kb/site/develop/cordis-tutorial/05-config.md +84 -0
  69. package/kb/site/develop/cordis-tutorial/06-composition-and-hmr.md +113 -0
  70. package/kb/site/develop/cordis-tutorial/07-into-the-harness.md +107 -0
  71. package/kb/site/develop/cordis-tutorial/index.md +62 -0
  72. package/kb/site/develop/framework/events.md +145 -0
  73. package/kb/site/develop/framework/index.md +139 -0
  74. package/kb/site/develop/framework/service.md +152 -0
  75. package/kb/site/develop/practice/index.md +157 -0
  76. package/kb/site/develop/practice/llm-adapter.md +190 -0
  77. package/kb/site/en/develop/basic/config.md +108 -0
  78. package/kb/site/en/develop/basic/index.md +146 -0
  79. package/kb/site/en/develop/basic/publish.md +185 -0
  80. package/kb/site/en/develop/basic/tool.md +54 -0
  81. package/kb/site/en/develop/cordis-tutorial/01-first-plugin.md +95 -0
  82. package/kb/site/en/develop/cordis-tutorial/02-lifecycle-and-effects.md +98 -0
  83. package/kb/site/en/develop/cordis-tutorial/03-services.md +98 -0
  84. package/kb/site/en/develop/cordis-tutorial/04-events.md +144 -0
  85. package/kb/site/en/develop/cordis-tutorial/05-config.md +84 -0
  86. package/kb/site/en/develop/cordis-tutorial/06-composition-and-hmr.md +113 -0
  87. package/kb/site/en/develop/cordis-tutorial/07-into-the-harness.md +107 -0
  88. package/kb/site/en/develop/cordis-tutorial/index.md +60 -0
  89. package/kb/site/en/develop/framework/events.md +145 -0
  90. package/kb/site/en/develop/framework/index.md +139 -0
  91. package/kb/site/en/develop/framework/service.md +150 -0
  92. package/kb/site/en/develop/practice/index.md +157 -0
  93. package/kb/site/en/develop/practice/llm-adapter.md +190 -0
  94. package/kb/site/en/guide/providers-custom-form.png +0 -0
  95. package/kb/site/en/guide/providers-models-page.png +0 -0
  96. package/kb/site/en/guide/providers.md +100 -0
  97. package/kb/site/en/guide/python-sdk.md +106 -0
  98. package/kb/site/en/guide/quickstart.md +32 -0
  99. package/kb/site/en/index.md +8 -0
  100. package/kb/site/en/reference/agent-lifecycle.md +86 -0
  101. package/kb/site/en/reference/capability-seams.md +475 -0
  102. package/kb/site/en/reference/config-catalog.md +3155 -0
  103. package/kb/site/en/reference/cookbook/adding-a-conversation-node.md +235 -0
  104. package/kb/site/en/reference/cookbook/adding-a-package.md +120 -0
  105. package/kb/site/en/reference/cookbook/adding-a-settings-card.md +102 -0
  106. package/kb/site/en/reference/cookbook/adding-a-tool.md +96 -0
  107. package/kb/site/en/reference/cookbook/adding-an-llm-adapter.md +45 -0
  108. package/kb/site/en/reference/cookbook/extension-cookbook.md +131 -0
  109. package/kb/site/en/reference/cordis-api/context.md +368 -0
  110. package/kb/site/en/reference/cordis-api/events.md +211 -0
  111. package/kb/site/en/reference/cordis-api/fiber.md +379 -0
  112. package/kb/site/en/reference/cordis-api/inherited.md +43 -0
  113. package/kb/site/en/reference/cordis-api/registry.md +156 -0
  114. package/kb/site/en/reference/cordis-api/service.md +106 -0
  115. package/kb/site/en/reference/cordis-primer.md +46 -0
  116. package/kb/site/en/reference/index.md +131 -0
  117. package/kb/site/en/reference/persistence-catalog.md +949 -0
  118. package/kb/site/en/reference/subsystems/approval.md +173 -0
  119. package/kb/site/en/reference/subsystems/client-modules.md +121 -0
  120. package/kb/site/en/reference/subsystems/code-runtime.md +194 -0
  121. package/kb/site/en/reference/subsystems/commands.md +190 -0
  122. package/kb/site/en/reference/subsystems/compaction.md +241 -0
  123. package/kb/site/en/reference/subsystems/core.md +1073 -0
  124. package/kb/site/en/reference/subsystems/credentials.md +136 -0
  125. package/kb/site/en/reference/subsystems/filesystem.md +498 -0
  126. package/kb/site/en/reference/subsystems/goal.md +280 -0
  127. package/kb/site/en/reference/subsystems/index.md +58 -0
  128. package/kb/site/en/reference/subsystems/invariants.md +91 -0
  129. package/kb/site/en/reference/subsystems/jobs.md +293 -0
  130. package/kb/site/en/reference/subsystems/llm-streaming.md +920 -0
  131. package/kb/site/en/reference/subsystems/lsp.md +205 -0
  132. package/kb/site/en/reference/subsystems/permission-presets.md +134 -0
  133. package/kb/site/en/reference/subsystems/persistence.md +388 -0
  134. package/kb/site/en/reference/subsystems/plan.md +90 -0
  135. package/kb/site/en/reference/subsystems/sandbox.md +221 -0
  136. package/kb/site/en/reference/subsystems/schedule.md +189 -0
  137. package/kb/site/en/reference/subsystems/scope.md +62 -0
  138. package/kb/site/en/reference/subsystems/session-projection.md +265 -0
  139. package/kb/site/en/reference/subsystems/session-query.md +498 -0
  140. package/kb/site/en/reference/subsystems/session-reference.md +111 -0
  141. package/kb/site/en/reference/subsystems/session-telemetry.md +197 -0
  142. package/kb/site/en/reference/subsystems/session-title.md +207 -0
  143. package/kb/site/en/reference/subsystems/session.md +852 -0
  144. package/kb/site/en/reference/subsystems/settings.md +313 -0
  145. package/kb/site/en/reference/subsystems/shell.md +306 -0
  146. package/kb/site/en/reference/subsystems/skills.md +334 -0
  147. package/kb/site/en/reference/subsystems/spill.md +120 -0
  148. package/kb/site/en/reference/subsystems/storage.md +232 -0
  149. package/kb/site/en/reference/subsystems/subagent.md +737 -0
  150. package/kb/site/en/reference/subsystems/subprocess.md +327 -0
  151. package/kb/site/en/reference/subsystems/system-prompt.md +210 -0
  152. package/kb/site/en/reference/subsystems/terminal.md +187 -0
  153. package/kb/site/en/reference/subsystems/token-meter.md +93 -0
  154. package/kb/site/en/reference/subsystems/tools.md +723 -0
  155. package/kb/site/en/reference/subsystems/typert.md +339 -0
  156. package/kb/site/en/reference/subsystems/user-questions.md +181 -0
  157. package/kb/site/en/reference/subsystems/web-server.md +111 -0
  158. package/kb/site/en/reference/subsystems/web.md +202 -0
  159. package/kb/site/en/reference/subsystems/workflow.md +281 -0
  160. package/kb/site/en/reference/subsystems/workspace.md +231 -0
  161. package/kb/site/en/reference/tool-catalog.md +1877 -0
  162. package/kb/site/en/reference/tool-execution-pipeline.md +66 -0
  163. package/kb/site/guide/providers-custom-form.zh.png +0 -0
  164. package/kb/site/guide/providers-models-page.zh.png +0 -0
  165. package/kb/site/guide/providers.md +100 -0
  166. package/kb/site/guide/python-sdk.md +106 -0
  167. package/kb/site/guide/quickstart.md +32 -0
  168. package/kb/site/index.md +8 -0
  169. package/kb/site/reference/agent-lifecycle.md +86 -0
  170. package/kb/site/reference/capability-seams.md +475 -0
  171. package/kb/site/reference/config-catalog.md +3154 -0
  172. package/kb/site/reference/cookbook/adding-a-conversation-node.md +235 -0
  173. package/kb/site/reference/cookbook/adding-a-package.md +120 -0
  174. package/kb/site/reference/cookbook/adding-a-settings-card.md +102 -0
  175. package/kb/site/reference/cookbook/adding-a-tool.md +98 -0
  176. package/kb/site/reference/cookbook/adding-an-llm-adapter.md +45 -0
  177. package/kb/site/reference/cookbook/extension-cookbook.md +133 -0
  178. package/kb/site/reference/cordis-api/context.md +368 -0
  179. package/kb/site/reference/cordis-api/events.md +211 -0
  180. package/kb/site/reference/cordis-api/fiber.md +379 -0
  181. package/kb/site/reference/cordis-api/inherited.md +43 -0
  182. package/kb/site/reference/cordis-api/registry.md +156 -0
  183. package/kb/site/reference/cordis-api/service.md +106 -0
  184. package/kb/site/reference/cordis-primer.md +52 -0
  185. package/kb/site/reference/index.md +135 -0
  186. package/kb/site/reference/persistence-catalog.md +949 -0
  187. package/kb/site/reference/subsystems/approval.md +173 -0
  188. package/kb/site/reference/subsystems/client-modules.md +121 -0
  189. package/kb/site/reference/subsystems/code-runtime.md +194 -0
  190. package/kb/site/reference/subsystems/commands.md +190 -0
  191. package/kb/site/reference/subsystems/compaction.md +241 -0
  192. package/kb/site/reference/subsystems/core.md +1081 -0
  193. package/kb/site/reference/subsystems/credentials.md +136 -0
  194. package/kb/site/reference/subsystems/filesystem.md +498 -0
  195. package/kb/site/reference/subsystems/goal.md +280 -0
  196. package/kb/site/reference/subsystems/index.md +58 -0
  197. package/kb/site/reference/subsystems/invariants.md +91 -0
  198. package/kb/site/reference/subsystems/jobs.md +293 -0
  199. package/kb/site/reference/subsystems/llm-streaming.md +926 -0
  200. package/kb/site/reference/subsystems/lsp.md +205 -0
  201. package/kb/site/reference/subsystems/permission-presets.md +134 -0
  202. package/kb/site/reference/subsystems/persistence.md +388 -0
  203. package/kb/site/reference/subsystems/plan.md +90 -0
  204. package/kb/site/reference/subsystems/sandbox.md +221 -0
  205. package/kb/site/reference/subsystems/schedule.md +189 -0
  206. package/kb/site/reference/subsystems/scope.md +62 -0
  207. package/kb/site/reference/subsystems/session-projection.md +265 -0
  208. package/kb/site/reference/subsystems/session-query.md +498 -0
  209. package/kb/site/reference/subsystems/session-reference.md +111 -0
  210. package/kb/site/reference/subsystems/session-telemetry.md +197 -0
  211. package/kb/site/reference/subsystems/session-title.md +207 -0
  212. package/kb/site/reference/subsystems/session.md +854 -0
  213. package/kb/site/reference/subsystems/settings.md +313 -0
  214. package/kb/site/reference/subsystems/shell.md +306 -0
  215. package/kb/site/reference/subsystems/skills.md +334 -0
  216. package/kb/site/reference/subsystems/spill.md +120 -0
  217. package/kb/site/reference/subsystems/storage.md +232 -0
  218. package/kb/site/reference/subsystems/subagent.md +739 -0
  219. package/kb/site/reference/subsystems/subprocess.md +327 -0
  220. package/kb/site/reference/subsystems/system-prompt.md +210 -0
  221. package/kb/site/reference/subsystems/terminal.md +187 -0
  222. package/kb/site/reference/subsystems/token-meter.md +93 -0
  223. package/kb/site/reference/subsystems/tools.md +723 -0
  224. package/kb/site/reference/subsystems/typert.md +339 -0
  225. package/kb/site/reference/subsystems/user-questions.md +181 -0
  226. package/kb/site/reference/subsystems/web-server.md +111 -0
  227. package/kb/site/reference/subsystems/web.md +202 -0
  228. package/kb/site/reference/subsystems/workflow.md +281 -0
  229. package/kb/site/reference/subsystems/workspace.md +231 -0
  230. package/kb/site/reference/tool-catalog.md +1880 -0
  231. package/kb/site/reference/tool-execution-pipeline.md +66 -0
  232. package/package.json +40 -0
  233. package/scripts/rebuild-index.mjs +88 -0
  234. package/skills/dsh-plugin-dev-kb.md +66 -0
@@ -0,0 +1,1081 @@
1
+ ---
2
+ editSource: "docs/subsystems/core.zh.md"
3
+ outline: [2,3]
4
+ ---
5
+
6
+ # 核心
7
+
8
+ **核心**子系统即 [`packages/core`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/core/README.md),包含每个组合都会启动的包:事件溯源的会话日志、系统提示词组装、工具注册表、agent(智能体)类型,以及驱动它们的具体循环。本页说明 `agent`/`agent-loop` 这对包所声明的内容:agent 如何被创建与拥有,以及 `Agent` 句柄的投递、取消与拦截约定;本页还说明每个子系统都遵循的两个类型模式。该组的专属页面与目录其余部分见[子系统 README](./index.md)。
9
+
10
+ ## 主干逐包速览
11
+
12
+ 一个轮次按同一条循环流经六个包:[`agent-loop`](https://github.com/deepseek-ai/deepseek-harness/tree/master/packages/core/agent-loop) 中的 driver 认领一条排队的提示词,在[会话日志](./session.md)(`ctx.sessions`)上开启轮次,通过 [system-prompt](./system-prompt.md)(`ctx.systemPrompt`)组装请求前缀并从日志派生历史,经 [LLM(大语言模型) seam](./llm-streaming.md) 流式获取模型响应,经[工具注册表](./tools.md)(`ctx.tools`)分发工具调用,并把每个模型可见的事实追加回日志,供下一步派生。循环搬运的对话词汇——`Message`、`ContentBlock`、`StreamChunk`、模型请求——由 [`packages/llm`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/llm/README.md) 声明,记录在 [llm-streaming.md](./llm-streaming.md)。
13
+
14
+ | 包 | 负责内容 | 页面 |
15
+ |---|---|---|
16
+ | `session/` | 仅追加的 `SessionEvent` 日志与内存 store——唯一真源(`ctx.sessions`) | [session.md](./session.md) |
17
+ | `system-prompt/` | 提示词段落与工具 schema 组装(`ctx.systemPrompt`) | [system-prompt.md](./system-prompt.md) |
18
+ | `tools/` | 带作用域的工具注册表与受保护的执行流水线(`ctx.tools`) | [tools.md](./tools.md) |
19
+ | `agent/` | `Agent` 接口、实时注册表、发起者作用域与 `agent/*` 事件词汇(`ctx.agents`) | 本页 |
20
+ | `agent-loop/` | 实现公开 `Agent` 约定的具体 driver(`ctx.agentLoop`) | 本页 |
21
+ | `scope/` | 注册表与循环用于构建按 agent 作用域的注册原语 | [scope.md](./scope.md) |
22
+
23
+ `scope/` 是这里唯一的非服务包:一个零依赖库(`createScope`/`scopeOf`/`scopeTarget`),在模块图中位于 `session/` 与 `system-prompt/` 之下,正是为了让它们消费它而不形成环。`agent-loop` 是公开 `Agent` 约定的唯一具体实现,放在这里因为它是 harness 的默认产品循环;它在 `ctx.agents.withInitiator()` 内运行每个 driver。扩展插件依赖 `agent`——包括需要发起 Agent 时——而绝不直接依赖 `agent-loop`,因此循环保持可替换。把这条主干接成可运行 agent 的默认组合是 [`examples/agent-spine-demo`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/examples/agent-spine-demo/README.md)。
24
+
25
+ <a id="creation-and-ownership"></a>
26
+
27
+ ## 创建与所有权
28
+
29
+ 消费方通过 `ctx.agents` 创建 agent——`create()` 在一个调用方提供的 `SessionId` 下构建全新会话与 agent,`resume()` 先加载持久会话——或者通过循环的声明式配置条目创建。编程式创建返回归属所有者的句柄:
30
+
31
+ 源码:[`packages/core/agent/src/index.ts`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/core/agent/src/index.ts)
32
+
33
+ ```ts type-equiv
34
+ /**
35
+ * An owned agent plus its disposer, returned by {@link AgentRegistry.create} /
36
+ * {@link AgentRegistry.resume}. The disposer is a CAPABILITY: among consumers,
37
+ * only the holder can tear this agent down. The registered factory provider is
38
+ * also a structural owner because the scoped agent depends on that provider's
39
+ * service API; provider unload stops and drains every live handle it made.
40
+ * `dispose()` stops the loop, awaits its exit, unregisters the agent, removes
41
+ * its session from the store, and finally unwinds its scoped world.
42
+ *
43
+ * `ctx.agents.get(id)` still returns a bare {@link Agent} — the handle is
44
+ * exposed only to the consumer owner that created it; the structural provider
45
+ * reaches the same teardown internally. Config-created agents (the loop's own
46
+ * startup) are owned by the loop fiber and never need a handle.
47
+ */
48
+ interface AgentHandle {
49
+ agent: Agent
50
+ dispose(): Promise<void>
51
+ }
52
+ ```
53
+
54
+ `CreateAgentOptions` 携带共享标识以及新 agent 发布前所需的一切:会话元数据(`meta`——已校验的 `cwd`、fork 谱系、seed 边界、来源分类、委派深度)、fork 用的可选 `seed` 回放前缀、按 agent 的 `AgentOptions`、仅创建期有效的取消 `signal`,以及 `setup`。`ResumeAgentOptions` 是持久标识的对应项:`resumeSessionId`、`agentOptions`、`signal` 与 `setup`。`setup` 回调(`AgentSetup`)在两个 id 都尚未发布时组装 agent 的作用域世界——凡经 `agentCtx` 注册的内容都先于 `agent/created` 与第一次提示词组装存在——并可返回一个在发布前一刻调用的同步 commit;setup 拒绝、commit 抛出或所有者 dispose(资源释放)都会回滚事务,两个 id 均不发布。
55
+
56
+ `AgentFactory` 是注册表背后的创建接口:循环经 `ctx.agents.setFactory()` 注册其工厂,因此消费方使用 `ctx.agents` 时无需依赖具体循环包。确切的 `create`/`resume` 签名及回滚约定见下方[生成区块](#ctxagents--agentregistry)。
57
+
58
+ <a id="the-agent-handle"></a>
59
+
60
+ ## Agent 句柄
61
+
62
+ `Agent` 是每个插件(UI、钩子、orchestrator)面向编程的 surface;`ctx.agents.get(id)` 返回它,[发起者作用域](#initiating-agent)携带它。具体实现为 dsh-agent-loop 包内部细节;循环外没有任何组件依赖它。统一的 `send` 方法直接暴露 target 与 wakeup 路由;`followup`、`steer` 与 `inject` 是固定预设的别名方法。
63
+
64
+ 源码:[`packages/core/agent/src/types.ts`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/core/agent/src/types.ts)
65
+
66
+ ```ts type-equiv
67
+ /** Public live-agent handle. */
68
+ interface Agent {
69
+ /** The single identity shared with {@link session}. */
70
+ readonly id: SessionId
71
+ /** The provider route and model this agent's requests use. */
72
+ readonly options: AgentOptions
73
+ /** The live session this agent drives; its log is the durable source of truth. */
74
+ readonly session: Session
75
+ /** The agent-owned projection of durable pending work. */
76
+ readonly inbox: Inbox
77
+ /** The current lifecycle state, mirrored on every `agent/status` transition. */
78
+ readonly status: AgentStatus
79
+ /** Agent-scoped context; its contributions are agent-local, unwind on disposal, and reject registration afterward. */
80
+ readonly ctx: Context
81
+
82
+ /**
83
+ * Clear queued and steering work — unless `keepInbox` — and abort the active
84
+ * turn or between-turn task. The first cause wins for that activity. With no
85
+ * active activity, cancellation is a no-op and does not arm later work.
86
+ * @param cause - the stable caller intent carried by the active operation signal.
87
+ * @param options - cancellation options; `keepInbox` preserves pending work.
88
+ */
89
+ cancel(cause: AgentCancelCause, options?: CancelOptions): void
90
+
91
+ /**
92
+ * Resolve after the current whole-agent activity reaches quiescence. This
93
+ * follows replacement work started before the observed driver retires,
94
+ * but does not identify the settlement of any particular message.
95
+ * @returns fulfillment after no active driver or maintenance task remains.
96
+ */
97
+ whenIdle(): Promise<void>
98
+
99
+ /**
100
+ * Run one non-turn maintenance task from the true idle phase. The task starts
101
+ * synchronously after claiming that phase; later waking input remains in the
102
+ * inbox until the task settles, while public status stays `idle`.
103
+ * `whenIdle()` follows both the task and any waking work released behind it.
104
+ * @param task - operation whose fulfillment or rejection is preserved, with a signal aborted by {@link cancel}.
105
+ * @throws synchronously when turn-driving or another maintenance task already owns the agent.
106
+ * @returns the task promise.
107
+ */
108
+ runMaintenance<T>(task: (signal: AbortSignal) => Promise<T>): Promise<T>
109
+
110
+ /**
111
+ * Route identified input to an inbox boundary and optionally wake the driver.
112
+ * Waking input submitted after active cancellation is queued for the next
113
+ * turn and runs when the aborted activity converges to idle; a `disposed`
114
+ * cancel leaves it parked. A wake submitted while already idle always opens
115
+ * its turn boundary, even when its message is cleared before the driver
116
+ * claims ([cancel-convergence wake latch](../../../../.agents/notes/implemented/bug-fix/2026-08-07-cancel-convergence-wake-latch.md)).
117
+ * @param message - identified content and the source that supplied it.
118
+ * @param target - the preferred next-turn or next-step inbox boundary.
119
+ * @param wakeup - whether delivery may wake the driver.
120
+ */
121
+ send(message: UserMessage, target: InboxTarget, wakeup: boolean): void
122
+
123
+ /**
124
+ * Queue an ordinary follow-up turn and wake the driver. The item becomes the
125
+ * sole ordinary message of its own turn.
126
+ * @param message - identified prompt content and the source that supplied it.
127
+ */
128
+ followup(message: UserMessage): void
129
+
130
+ /**
131
+ * Submit steering for the nearest step. An idle driver starts a turn;
132
+ * a running driver consumes it at its next step boundary.
133
+ * A rejected step leaves steering parked in the inbox until the next
134
+ * wake; cancellation or disposal may discard pending steering.
135
+ * @param message - identified steering content and the source that supplied it.
136
+ */
137
+ steer(message: UserMessage): void
138
+
139
+ /**
140
+ * Queue model-facing context for the next pre-step without waking the
141
+ * driver. A running driver claims it at the nearest later step boundary;
142
+ * idle drivers leave it pending until follow-up or steering
143
+ * wakes them. It may miss a request whose pre-step already claimed its
144
+ * batch. Cancellation or disposal may discard pending context.
145
+ * @param message - identified injected context and the source that supplied it.
146
+ */
147
+ inject(message: UserMessage): void
148
+ }
149
+ ```
150
+
151
+ ```ts type-equiv
152
+ /**
153
+ * An agent's lifecycle state, emitted on every transition as `agent/status`:
154
+ * `idle` means no driver is active; `running` begins when waking input starts
155
+ * cancellable pre-step processing and lasts while the driver drains,
156
+ * closes, or checkpoints turns. Disposal removes the agent from its registry;
157
+ * it is not a third observable status.
158
+ */
159
+ type AgentStatus = 'idle' | 'running'
160
+ ```
161
+
162
+ `running` 描述整个驱动器的排空区间,可能跨越连续的排队轮次;它不能证明某个轮次仍然打开。dispose 会把 agent 从注册表移除并发出 `agent/disposed`;它不是一个终态 status 值。`followup()` 不返回句柄:其 `MessageId` 标识的是持久的 inbox 插入、认领与丢弃事实,而非之后的助手输出或轮次结束。`whenIdle()` 观察的是整个 agent,因此只有当调用方明确拥有从回执到空闲的这段区间时,才能把它称为一次 run([决策](https://github.com/deepseek-ai/deepseek-harness/blob/master/.agents/notes/implemented/architecture/2026-07-30-followup-enqueue-and-owned-runs.md))。
163
+
164
+ ```ts type-equiv
165
+ /** Merge-extensible agent creation options. Persona belongs to system-prompt sections. */
166
+ interface AgentOptions {
167
+ /** Provider route (must have a registered adapter at call time). */
168
+ provider?: string
169
+ /** Model id interpreted by the selected provider adapter. */
170
+ model?: string
171
+ /** Maximum output tokens for each conversation-model request. */
172
+ maxTokens?: number
173
+ }
174
+ ```
175
+
176
+ 在 `agent/request` 之后,分发要求 `provider` 与 `model` 都存在。提供 `maxTokens` 时,它必须是正安全整数,并限制每次对话模型请求的输出;省略时,系统会在写入请求 header 前填入确切模型的适配器默认值,否则提供方行为保持不变。agent 作用域的 `deployment:persona` 提示词段落可以遮蔽全局默认 persona。
177
+
178
+ inbox 即投递词汇——agent 以持久投影形式拥有的两条有序待处理消息列表:
179
+
180
+ ```ts type-equiv
181
+ /** One of the two ordered pending-message lists owned by an agent. */
182
+ type InboxTarget = 'next-turn' | 'next-step'
183
+ ```
184
+
185
+ 每个待处理入队项就是其 `UserMessage`;`MessageId` 是唯一标识。`Inbox.append`、`prepend`、`replace`、`remove`、`clear`、`splice` 与 `claim` 会记录规范化的持久 `agent/inbox/spliced` 变更,并拒绝重复的待处理 id。`replace(messageId, newMessage)` 与 `remove(messageId)` 通过 `MessageId` 跨两份列表定位待处理消息;替换可以改变标识,并先将旧消息作为 discarded 发布,再将新消息作为 inserted 发布。普通删除和 `clear()` 都表示取消。`claim(target)` 通过纯删除 splice 移除拟进入步骤的批次——全部 `next-step` 输入,外加轮次边界上的一条 `next-turn` 消息——且不发出 discarded 通知;循环另行逐条发出 claimed 通知。UI 投影等整体队列消费方通过持久 splice 重建 `nextTurn` 与 `nextStep`,而跟踪单条消息的消费方使用精确的 `agent/inbox/inserted`、`claimed` 与 `discarded` 通知。
186
+
187
+ 取消:
188
+
189
+ ```ts type-equiv
190
+ /** Options for {@link Agent.cancel}. */
191
+ interface CancelOptions {
192
+ /**
193
+ * Preserve queued and steering inbox items instead of discarding them. The
194
+ * active turn is still aborted, but un-started and pending work survives for a
195
+ * later turn and no canceled inbox splice is logged.
196
+ */
197
+ keepInbox?: boolean | undefined
198
+ }
199
+ ```
200
+
201
+ ```ts type-equiv
202
+ /** Why an active agent driver was cancelled. */
203
+ type AgentCancelCause =
204
+ | { readonly kind: 'user' }
205
+ | { readonly kind: 'parent' }
206
+ | { readonly kind: 'hook'; readonly reason: string }
207
+ | { readonly kind: 'disposed' }
208
+ ```
209
+
210
+ cause 是由 TypeScript 强制约束的同进程输入。活跃的取消持有者会将它复制到仅运行时的 `AbortSignal.reason`;signal 不授予协作监听器任何分类权限。持久 `turn/end` 保留粗粒度 `{ kind: 'aborted' }` 结果;若需记录谁请求了取消,应使用单独的持久事件,而不是让终态结果承担额外含义。
211
+
212
+ [事件分类](../index.md#events)负责 `agent/*` 生命周期、检查点与 waterfall(瀑布式事件)约定。轮次和步骤边界是持久会话事件,而不是 agent emit。
213
+
214
+ <a id="initiating-agent"></a>
215
+
216
+ ## 发起 Agent
217
+
218
+ `ctx.agents` 携带的进程本地 initiator 就是上面的确切 `Agent`,不是单独的 frame 或复制的标识。环境中存在该值既不能证明存活,也不代表授权;[initiator 作用域决策](https://github.com/deepseek-ai/deepseek-harness/blob/master/.agents/notes/implemented/architecture/2026-07-15-agent-initiator-scope.md)定义其生命周期和作用域规则。
219
+
220
+ ## 拦截决策
221
+
222
+ pre-step 决策使用与持久 user-role 输入相同、带标识的 `UserMessage` 类型。进入步骤的批次具有权威性,并保留每条消息的 `id` 和 `source`。钩子桥接层把其原生决策字段映射到这一类型化结果上。
223
+
224
+ 源码:[`packages/core/agent/src/types.ts`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/core/agent/src/types.ts)
225
+
226
+ `agent/pre-step` 接收一个 payload,携带独占的已领取批次(`messages`)、拟进入步骤的坐标(`turn`、`step`)与当前轮次的取消 `signal`。首次提案在已打开的轮次内、任何步骤开始前运行;工具 continuation 可以在步骤之间提交空的已领取批次:
227
+
228
+ 它返回 `PreStepDecision`。reject 不会打开步骤。enter 提供在 `step/start` 后追加的完整消息批次;最终决策省略的已领取消息保持已删除,而领取后插入的输入仍留待后续处理:
229
+
230
+ ```ts type-equiv
231
+ /** Whether and with which messages the loop enters a proposed step. */
232
+ type PreStepDecision =
233
+ | { kind: 'reject' }
234
+ | { kind: 'enter'; messages: UserMessage[] }
235
+ ```
236
+
237
+ `agent/request-error` 在失败的模型步骤关闭之后、其轮次关闭之前运行。listener 可以在失败轮次的 signal 仍然存活时修复持久状态或 await 策略工作。处理该错误的 listener 返回 `{ kind: 'retry' }` 且不调用 `next()`;默认的 `undefined` 会让失败保持终态。
238
+
239
+ ```ts type-equiv
240
+ /** Action returned by a listener that owns model-request recovery. */
241
+ type RequestErrorAction = { kind: 'retry' } | undefined
242
+ ```
243
+
244
+ `agent/pre-step` 是请求推导前唯一的串行监听器链。`agent/turn-stopping` 在轮次没有工具或 steering(中途引导)后续时运行,先于最后一次 steering 排空。
245
+
246
+ `agent/session-start` 携带 `SessionStartSource`(会话生命周期为何开始;桥接层据此匹配其 SessionStart):
247
+
248
+ ```ts type-equiv
249
+ /** Why a session lifecycle began; seeded creates are `startup`, while persisted loads are `resume`. */
250
+ type SessionStartSource = 'startup' | 'resume' | 'clear' | 'compact'
251
+ ```
252
+
253
+ ## 会话
254
+
255
+ `Session` 是一份类型化 `SessionEvent` 的**仅追加日志**——唯一的真源。LLM 消息历史从日志*派生*(`deriveMessages()`),而非单独存储。每个条目携带单调的 `seq`、`time` 与按 `type` 判别的 `data` payload;surface 变体还可以在 `sourceEventSeqs` 中列出被引用的较早事件,并携带 `surfaceOp`。
256
+
257
+ `SessionEvent` 信封的确切条件字段、十二种事件变体(`turn/start`、`turn/end`、`step/start`、`step/end`、`user/message`、`assistant/chunk`、`assistant/message`、`tool/call`、`tool/result`、`steering/message`、`todo/write`、`request/header`)、`deriveMessages()` 投影规则、`TurnTrigger`/`TurnEndReason` 原因以及执行封闭和独立事件规则都在 **[session.md](./session.md)** 中。日志如何持久化——`SessionPersistence` 接口、JSONL/SQLite 后端、`session/flush` 检查点、崩溃恢复与 `SessionHeader`——则在 **[persistence.md](./persistence.md)** 中。
258
+
259
+ ## `ToolDefinition`
260
+
261
+ 唯一属于核心的流水线编写类型:每个已注册工具*是什么*——一个面向模型的 `ToolSchema` 加上一个 `execute` 函数,以及可选的最终内容回调与 UI 回调。工具作者很少手动构造它(`defineTool` DSL 会使用类型化参数构建),但它是注册表存储并由循环用于分发的约定。
262
+
263
+ 其完整字段、`defineTool`/`ValueSchemaSpec`/`ParameterSchemaSpec` 类型化 schema DSL、`ToolExecution`/`ToolExecutionResult` waterfall 类型,以及工具展示 UI 类型都在 **[tools.md](./tools.md)** 中。
264
+
265
+ ## 全仓通用类型模式
266
+
267
+ 两个模式在每个子系统中反复出现,只在此处记录一次。
268
+
269
+ <a id="the-map--derived-union-pattern"></a>
270
+
271
+ ### `…Map → derived-union` 模式
272
+
273
+ harness 中几乎所有可扩展的和类型都遵循同一模式:一个以判别标签为键的接口(`…Map`),联合类型由 `keyof` 派生。插件通过**声明合并**添加变体——无需修改拥有该类型的包。
274
+
275
+ ```ts ignore-check
276
+ // The pattern, schematically:
277
+ interface ThingMap {
278
+ 'a': { kind: 'a'; /* … */ }
279
+ 'b': { kind: 'b'; /* … */ }
280
+ }
281
+ type ThingKind = keyof ThingMap // 'a' | 'b'
282
+ type Thing = ThingMap[keyof ThingMap] // the discriminated union
283
+
284
+ // A plugin extends it without touching the source package:
285
+ declare module '@deepseek-ai/dsh-llm' {
286
+ interface ThingMap {
287
+ 'c': { kind: 'c'; /* … */ }
288
+ }
289
+ }
290
+ ```
291
+
292
+ 六个规范 map 使用此模式;插件作者扩展它们:
293
+
294
+ | Map | 包 | 派生 | 目录 |
295
+ |---|---|---|---|
296
+ | `ContentBlockMap` | dsh-llm | `ContentBlock` | [llm-streaming.md](./llm-streaming.md#content-blocks-and-messages) |
297
+ | `MessageSourceMap` | dsh-llm | `MessageSource` | [llm-streaming.md](./llm-streaming.md#content-blocks-and-messages) |
298
+ | `FinishReasonMap` | dsh-llm | `FinishReason` | [llm-streaming.md](./llm-streaming.md#the-model-request-and-result) |
299
+ | `TurnTriggerMap` | dsh-session | `TurnTrigger` | [session.md](./session.md) |
300
+ | `TurnEndReasonMap` | dsh-session | `TurnEndReason` | [session.md](./session.md) |
301
+ | `SessionEventMap` | dsh-session | `SessionEvent` | [session.md](./session.md) |
302
+
303
+ 消费方最常 `switch` 的两个大型判别联合类型是:**`StreamChunk`**(流式协议)和 **`SessionEvent`**(日志条目)。按仓库约定,对标签做 `switch`——不要链式 `if`——这样每个分支都能窄化类型,拼错的标签会编译失败。
304
+
305
+ <a id="branded-ids"></a>
306
+
307
+ ### 品牌化 ID
308
+
309
+ 在包之间传递的 ID 都经过**品牌化**——结构上是字符串,但在类型层面不可互换(不能把 `SessionId` 传给需要 `CallId` 的位置)。每种类型通过各自的工厂构造;比较、日志记录和 JSON 行为与普通字符串相同。
310
+
311
+ `Branded<B>` 原语位于独立的纯类型包 [dsh-brand](https://github.com/deepseek-ai/deepseek-harness/tree/master/packages/util/brand) 中(没有运行时代码,也不依赖 harness 包),因此任何包都能品牌化其拥有的 id,而无需依赖无关的能力包。
312
+
313
+ 源码:[`packages/util/brand/src/index.ts`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/util/brand/src/index.ts)
314
+
315
+ ```ts type-equiv
316
+ /** A string carrying a compile-time-only brand `B`. */
317
+ type Branded<B extends string> = string & { readonly [BRAND]: B }
318
+ ```
319
+
320
+ 两个核心 ID 是 `CallId`(关联工具调用及其结果;dsh-llm)和 `SessionId`(活跃 agent 与持久会话共享的标识;dsh-session)。能力包也会品牌化各自的 id,例如 [jobs.md](./jobs.md) 中的 `JobId`。
321
+
322
+ <!-- BEGIN GENERATED cordis-surface (gen-cordis-catalog.ts) — do not edit between markers -->
323
+
324
+ <a id="cordis-surface"></a>
325
+
326
+ ## Cordis API
327
+
328
+ Generated from source by `scripts/gen-cordis-catalog.ts` (verified fresh by `pnpm run verify-cordis-catalog` in doc-sync; regenerate with `pnpm run gen-cordis-catalog`) — this section is byte-identical in both language sides of the page. Signature blocks use a `ts cordis-catalog` fence and keep the original source JSDoc; dispatch modes are defined in the [primer](../cordis-primer.md#dispatch-modes), and the framework-inherited `ctx` API lives in [cordis-api/inherited.md](../cordis-api/inherited.md).
329
+
330
+ <a id="ctxagentdefaultmodel--agentdefaultmodelconfig"></a>
331
+
332
+ ### `ctx.agentDefaultModel` — `AgentDefaultModelConfig`
333
+
334
+ Owns the default model selection independently of any Host or transport. The composition entry remains usable without a settings provider; when one is mounted, its user layer is read live.
335
+
336
+ ```ts cordis-catalog
337
+ /**
338
+ * Read the current default model selection.
339
+ * @returns a detached provider, model, and optional reasoning selection.
340
+ */
341
+ currentSelection(): ModelSelection
342
+
343
+ /**
344
+ * Save the complete default model selection. A deployment without a settings
345
+ * provider keeps its composition entry.
346
+ * @param next - resolved selection accepted by an entry point.
347
+ * @returns fulfillment after the optional settings write settles.
348
+ */
349
+ async saveSelection(next: ModelSelection): Promise<void>
350
+ ```
351
+
352
+ Source: [`packages/core/agent-default-model/src/index.ts:64`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/core/agent-default-model/src/index.ts)
353
+
354
+ <a id="ctxagentloop--agentloop"></a>
355
+
356
+ ### `ctx.agentLoop` — `AgentLoop`
357
+
358
+ Concrete agent factory and driver service.
359
+
360
+ ```ts cordis-catalog
361
+ /**
362
+ * Create an agent and session under one caller-supplied identity, owned by
363
+ * the accessing fiber. Constructor-driven config calls mint a fresh combined
364
+ * id before entering this boundary.
365
+ * @param id - shared agent/session identity.
366
+ * @param options - concrete loop options.
367
+ * @param meta - optional fresh-session workspace metadata.
368
+ * @returns the published running agent.
369
+ */
370
+ create(id: SessionId, options: AgentOptions = {}, meta: Pick<SessionHeader, 'cwd'> = {}): Agent
371
+
372
+ /**
373
+ * Create an owned agent on a caller-supplied session id.
374
+ * @param ownerCtx - caller context that structurally owns the lifecycle.
375
+ * @param options - identities, session seed/metadata, loop options, setup, and cancellation.
376
+ * @returns the published handle.
377
+ */
378
+ async createAgent(ownerCtx: Context, options: CreateAgentOptions): Promise<AgentHandle>
379
+
380
+ /**
381
+ * Resume an owned agent from the configured persistence service.
382
+ * @param ownerCtx - caller context that owns load, setup, and the live lifecycle.
383
+ * @param options - persisted identity, loop options, setup, and cancellation.
384
+ * @returns the published handle.
385
+ */
386
+ async resume(ownerCtx: Context, options: ResumeAgentOptions): Promise<AgentHandle>
387
+ ```
388
+
389
+ Types: [SessionHeader](./persistence.md)
390
+
391
+ Source: [`packages/core/agent-loop/src/index.ts:296`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/core/agent-loop/src/index.ts)
392
+
393
+ <a id="ctxagentpresets--agentpresets"></a>
394
+
395
+ ### `ctx.agentPresets` — `AgentPresets`
396
+
397
+ Registry over the deployment's agent presets.
398
+
399
+ Discovery is unmemoized: `list()` and `resolve()` re-read the roots on every call so a preset authored while the process runs is visible immediately, and a preset deleted underneath a picker disappears from the next read.
400
+
401
+ ```ts cordis-catalog
402
+ /**
403
+ * Every preset the configured roots currently supply.
404
+ * @returns the presets, first-root-wins per id.
405
+ */
406
+ async list(): Promise<AgentPreset[]>
407
+
408
+ /**
409
+ * Resolve one preset by id.
410
+ *
411
+ * A broken preset resolves — deleting one, reading one, and reporting one
412
+ * all need the row — and the mounting paths refuse it AFTER resolution
413
+ * through {@link resolveMountable}.
414
+ * @param id - the preset id, or `undefined` for {@link defaultId}.
415
+ * @returns the resolved preset.
416
+ * @throws when no configured root supplies that id.
417
+ */
418
+ async resolve(id?: string): Promise<AgentPreset>
419
+
420
+ /**
421
+ * Compose one agent from a preset: ensure the preset's standing mount, then
422
+ * parent the agent's scope key to it so the mount's registrations and
423
+ * listeners cover this agent.
424
+ *
425
+ * Call from the agent factory's `setup(agentCtx)`; a rejection there rolls
426
+ * the agent creation back, so a broken preset never yields a half-composed
427
+ * session.
428
+ * @param agentCtx - the agent's scope context.
429
+ * @param id - the preset id, or `undefined` for {@link defaultId}.
430
+ * @returns the preset that was composed, for the caller to record.
431
+ * @throws when the preset is unknown or its composition is unusable.
432
+ */
433
+ async mount(agentCtx: Context, id?: string): Promise<AgentPreset>
434
+
435
+ /**
436
+ * Join one agent to the SAME standing composition another already runs on.
437
+ *
438
+ * This is how a child agent inherits its parent's capabilities. It is a bind,
439
+ * not a mount: the parent's generation is already composed, so the child gets
440
+ * that exact instance — the same plugin objects, the same tool registrations,
441
+ * the same prompt sections. Re-resolving the parent's preset by id instead
442
+ * would re-read the roster, and a composition file edited since the parent
443
+ * started would hand the child a DIFFERENT generation than the one its
444
+ * parent's history was produced under (and a preset deleted since would fail
445
+ * the child outright while its parent keeps running).
446
+ *
447
+ * Synchronous, and with no composition failure mode of its own — it reads no
448
+ * roster, mounts nothing, and touches no file — which is what lets a child
449
+ * creation window use it: the two in-process subagent drivers compose their
450
+ * children inside a synchronous `setup`. It still rejects a caller error, as
451
+ * the `@throws` below record.
452
+ *
453
+ * A parent that joined no preset — a rosterless deployment — yields no join
454
+ * and no error: there, the model-facing rows sit in the host composition and
455
+ * the child already sees them through the global layer.
456
+ * @param agentCtx - the joining agent's scope context.
457
+ * @param parentCtx - the scope context of the agent whose composition to join.
458
+ * @returns the preset id joined, or undefined when the parent joined none.
459
+ * @throws when `agentCtx` carries no scope, or has already joined a preset.
460
+ */
461
+ composeFrom(agentCtx: Context, parentCtx: Context): string | undefined
462
+
463
+ /**
464
+ * The preset one live agent runs on.
465
+ *
466
+ * Read from the live scope chain rather than from the session, so it answers
467
+ * for an agent whose session has not recorded a preset yet — a child agent
468
+ * whose durable header is being built from its parent's composition.
469
+ * @param agentCtx - the agent's scope context.
470
+ * @returns the preset id, or undefined when the agent joined none.
471
+ */
472
+ composedPreset(agentCtx: Context): string | undefined
473
+
474
+ /**
475
+ * Read one preset's composition text.
476
+ * @param id - the preset id.
477
+ * @returns the composition exactly as stored.
478
+ * @throws when no configured root supplies that id.
479
+ */
480
+ async read(id: string): Promise<string>
481
+
482
+ /**
483
+ * Create a locally authored preset by copying an existing one whole.
484
+ *
485
+ * Copy is the only authoring write. Composition text never crosses this
486
+ * seam: the source is named by id and its directory is copied as it stands,
487
+ * so the copy is exactly as loadable as its source and authoring grants no
488
+ * capability the roster did not already carry. The copy is NOT mounted to
489
+ * validate — a source that mounts today yields a copy that mounts today.
490
+ * @param from - the preset the copy starts from; shipped presets are the
491
+ * primary source, so any trust is accepted.
492
+ * @param id - the new preset's id, which becomes its directory name.
493
+ * @param name - display name for the copy; absent falls back to the id.
494
+ * @throws when the source is unknown, the id is unusable or already taken,
495
+ * or the deployment configures no writable root.
496
+ */
497
+ async copy(from: string, id: string, name?: string): Promise<void>
498
+
499
+ /**
500
+ * Delete a locally authored preset.
501
+ * @param id - the preset id.
502
+ * @throws when the preset is unknown or ships with the deployment.
503
+ */
504
+ async remove(id: string): Promise<void>
505
+
506
+ /**
507
+ * One agent's instance of a service its preset mounted.
508
+ *
509
+ * A preset publishes services behind `isolate` realms, which are invisible
510
+ * outside the group that declares them — including to the host. This is how a
511
+ * caller holding the agent reads one anyway: a request that is ABOUT a
512
+ * session but arrives from outside it, which is every browser RPC.
513
+ *
514
+ * Read addressing only. A host row that `inject`s a service cannot use this,
515
+ * because injection resolves before any session exists and has no agent to
516
+ * key by; such a service belongs on the host plane instead.
517
+ * @param agent - the agent whose composition to look inside.
518
+ * @param name - the service name as the preset's rows resolve it.
519
+ * @returns the agent's instance, or undefined when its preset mounts none.
520
+ */
521
+ serviceFor<K extends string & keyof Context>(agent: { ctx: Context }, name: K): Context[K] | undefined
522
+
523
+ /**
524
+ * Re-link one agent to a different preset's standing composition.
525
+ *
526
+ * Only valid while the agent has produced nothing: swapping tools mid
527
+ * conversation would leave logged tool calls the new composition cannot
528
+ * make. The CALLER owns that check — this method does not read session
529
+ * history.
530
+ *
531
+ * The swap is a parent re-link, not an unmount: standing mounts are shared
532
+ * and permanent, so the old composition stays for its other agents and the
533
+ * new one is ensured BEFORE the link moves. An unknown or unusable preset
534
+ * therefore throws with the agent exactly as it was — there is no torn-down
535
+ * state to restore. The re-link runs through the binding this roster kept
536
+ * from the agent's mount — dsh-scope's only re-link authority. An agent
537
+ * that never composed one has nothing to re-link: the switch is then the
538
+ * agent's first bind, exactly a mount.
539
+ * @param agentCtx - the agent's scope context.
540
+ * @param id - the preset to compose the agent from instead.
541
+ * @returns the preset now installed.
542
+ * @throws when the preset is unknown or its composition is unusable.
543
+ */
544
+ async recompose(agentCtx: Context, id: string): Promise<AgentPreset>
545
+
546
+ /**
547
+ * The standing scope key of one preset, for a host reader with no agent.
548
+ *
549
+ * A cold transcript read resolves tool presenters against the composition
550
+ * the session recorded, and the standing mount makes that possible without
551
+ * resuming anything: ensuring the mount composes plugins but starts no
552
+ * agent, no session, and no turn.
553
+ * @param id - the preset id, or `undefined` for {@link defaultId}.
554
+ * @returns the standing scope key readers pass as a registry view scope.
555
+ * @throws when the preset is unknown or its composition is unusable.
556
+ */
557
+ async standingKeyFor(id?: string): Promise<ScopeKey>
558
+ ```
559
+
560
+ Types: [ScopeKey](./scope.md)
561
+
562
+ Source: [`packages/preset/agent-presets/src/index.ts:82`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/preset/agent-presets/src/index.ts)
563
+
564
+ <a id="ctxagents--agentregistry"></a>
565
+
566
+ ### `ctx.agents` — `AgentRegistry`
567
+
568
+ Agent service (`ctx.agents`): tracks live agents and carries the initiating Agent through one process-local asynchronous driver chain. Agent *creation* is provided by whichever plugin implements the AgentFactory (`@deepseek-ai/dsh-agent-loop`), registered via setFactory.
569
+
570
+ Initiator methods provide same-process causal attribution only. Ambient presence is neither liveness proof nor authorization; subjects and owners remain explicit, as does identity at worker, process, persistence, and wire boundaries. Returned Promise boundaries drain during teardown, except a nested lineage that starts an owning-fiber unload is excluded from its own drain.
571
+
572
+ ```ts cordis-catalog
573
+ /**
574
+ * Read the Agent that initiated the inherited asynchronous driver chain.
575
+ * Use this optional form for logging, tracing, metrics, or host attribution
576
+ * that also supports agentless calls. When a parent creates a child, setup
577
+ * reports the causal parent while `agentCtx.agent` identifies the child.
578
+ * @returns the inherited Agent, or `undefined` outside an initiator boundary
579
+ * and inside an explicit clearing boundary.
580
+ * @throws when this service instance has been disposed.
581
+ */
582
+ currentInitiator(): Agent | undefined
583
+
584
+ /**
585
+ * Read the initiating Agent and fail when no initiator boundary is active.
586
+ * Use this for private helpers contractually below a driver, or for a
587
+ * deployment-owned outbound request whose contract forbids agentless calls.
588
+ * Generic or direct-call paths use optional lookup or explicit request fields.
589
+ * @returns the inherited Agent.
590
+ * @throws when no initiator is active or this service instance has been disposed.
591
+ */
592
+ requireInitiator(): Agent
593
+
594
+ /**
595
+ * Run an operation with one exact Agent as its process-local initiator. The
596
+ * exact synchronous value or Promise returned by the operation is preserved.
597
+ * Custom drivers and test harnesses wrap their complete returned foreground
598
+ * lifetime.
599
+ * A queue or wire receiver may establish this boundary only after validating
600
+ * explicit identity and resolving the exact live Agent; this method does neither.
601
+ * Detached work remains owned by the subsystem that starts it.
602
+ * @param agent - initiating Agent to inherit; presence is neither liveness proof nor authorization.
603
+ * @param operation - synchronous or asynchronous operation to invoke.
604
+ * @returns the exact value returned by `operation`.
605
+ * @throws when the initiator scope is closing/disposed, or when `operation` throws.
606
+ */
607
+ withInitiator<T>(agent: Agent, operation: () => T): T
608
+
609
+ /**
610
+ * Run an operation inside a boundary that hides any inherited initiating
611
+ * Agent. The exact synchronous value or Promise is preserved.
612
+ * Use this while creating lazy shared timers, queue pumps, pool maintenance,
613
+ * watchers, or exporters so they do not inherit the first Agent that happens
614
+ * to initialize them. It clears only initiator attribution, not explicit
615
+ * fields, and does not own or drain detached resources.
616
+ * @param operation - synchronous or asynchronous operation to invoke without an initiator.
617
+ * @returns the exact value returned by `operation`.
618
+ * @throws when the initiator scope is closing/disposed, or when `operation` throws.
619
+ */
620
+ withoutInitiator<T>(operation: () => T): T
621
+
622
+ /**
623
+ * Register the agent-creation factory (the loop calls this on construction,
624
+ * effect-scoped). A traced Cordis service is canonicalized to its concrete
625
+ * target; each create/resume call is then traced through that caller's
626
+ * context so ownership follows the caller without stacking proxy layers.
627
+ * Throws if a factory is already registered. Returns the disposer; on
628
+ * dispose the factory slot is cleared.
629
+ * @param factory - the loop-owned factory {@link create}/{@link resume} delegate to.
630
+ * @returns the disposer that clears the factory slot. The exact
631
+ * Cordis effect disposer (single-shot): composite (generator) effects may
632
+ * yield it directly — exact identity nests the teardown in order.
633
+ */
634
+ setFactory(factory: AgentFactory): () => void
635
+
636
+ /**
637
+ * Create and publish a new agent through the registered factory.
638
+ * Distinct from {@link register} (which records an already-constructed
639
+ * agent): this constructs the agent and its session. Rejects if no factory is
640
+ * registered or creation/setup fails. The resolved {@link AgentHandle} lets
641
+ * the owner tear down exactly this agent.
642
+ * @param options - shared identity, session seed/metadata, and agent options.
643
+ * @returns the handle after setup, rollback-covered publication, and loop start complete.
644
+ */
645
+ async create(options: CreateAgentOptions): Promise<AgentHandle>
646
+
647
+ /**
648
+ * Load a persisted session and resume an agent on it through the registered
649
+ * factory. Rejects if no factory is registered; the factory rejects if
650
+ * session persistence is not configured or persistence/setup fails.
651
+ * @param options - persisted identity, configuration, and optional setup.
652
+ * @returns the handle after setup, rollback-covered publication, and loop start complete.
653
+ */
654
+ async resume(options: ResumeAgentOptions): Promise<AgentHandle>
655
+
656
+ /**
657
+ * Register a live agent. Throws if an agent with the same id is already
658
+ * registered. Emits `agent/created` on registration and `agent/disposed`
659
+ * when the calling fiber is disposed — both with the agent's scope carrier
660
+ * (`scopeTarget(agent, agent)`): the subject is the agent in hand, so the
661
+ * emits are scope-filtered regardless of which context invoked `register`
662
+ * (calling through `agent.ctx` scopes EFFECTS; dispatch scoping always
663
+ * requires passing the carrier). Returns the disposer.
664
+ * @param agent - the already-constructed agent to record in the store.
665
+ * @returns the EXACT Cordis effect disposer (single-shot; a repeat call
666
+ * returns undefined without awaiting an in-flight teardown). Exact
667
+ * identity is load-bearing: a composite (generator) effect that owns a
668
+ * teardown ORDER — the agent factory's lifecycle chain — must yield THIS
669
+ * function so Cordis nests the unregistration at that yield position;
670
+ * yielding a wrapper would leave it disposing as a concurrent sibling on
671
+ * owner unload, unregistering the agent (and emitting `agent/disposed`)
672
+ * while its final turn is still draining.
673
+ */
674
+ register(agent: Agent): () => void
675
+
676
+ /**
677
+ * Insert an already-constructed agent without announcing it. This is the
678
+ * advanced ordered-lifecycle primitive used by the async agent factory: it
679
+ * first completes setup while the agent is unpublished, then assigns the
680
+ * returned detach closure into its pre-installed composite teardown before
681
+ * calling {@link announce}. Ordinary callers use {@link register}.
682
+ * @param agent - the prepared, unpublished agent.
683
+ * @param owner - live agent whose scoped context created this agent, or
684
+ * undefined for a top-level runtime root. This is runtime ownership, not
685
+ * the resumed session's durable parent lineage.
686
+ * @returns an idempotent closure that removes this exact entry and emits
687
+ * `agent/disposed` with listener failures contained. When called from a
688
+ * synchronous `agent/created` listener, removal and disposal wait until
689
+ * that creation dispatch unwinds.
690
+ */
691
+ enter(agent: Agent, owner: Agent | undefined): () => void
692
+
693
+ /**
694
+ * Announce an agent previously inserted with {@link enter}.
695
+ * @param agent - the live inserted agent to announce.
696
+ * @throws if `agent` is not the exact live registry entry for its id, or its
697
+ * creation announcement already began (including a reentrant call from a
698
+ * creation listener).
699
+ */
700
+ announce(agent: Agent): void
701
+
702
+ /**
703
+ * Look up a live agent.
704
+ * @param id - the shared agent/session id to look up.
705
+ * @returns the agent, or undefined when no live agent has that id.
706
+ */
707
+ get(id: SessionId): Agent | undefined
708
+
709
+ /**
710
+ * Test whether a live agent was created through one exact parent agent's
711
+ * scoped context. Runtime ownership is independent of durable session
712
+ * lineage and remains unambiguous when unrelated providers reuse an id.
713
+ * @param id - the candidate child agent's shared agent/session id.
714
+ * @param owner - the expected runtime creator agent.
715
+ * @returns true only while the exact child entry is live under that owner.
716
+ */
717
+ isOwnedBy(id: SessionId, owner: Agent): boolean
718
+
719
+ /**
720
+ * All live agents, in registration order.
721
+ * @returns a fresh array; mutating it does not affect the registry.
722
+ */
723
+ list(): Agent[]
724
+
725
+ /**
726
+ * All live top-level agents in registration order. A top-level agent was
727
+ * created without an owning agent context; durable session lineage does not
728
+ * affect this runtime relation, so a resumed fork may still be a root.
729
+ * @returns a fresh array; mutating it does not affect the registry.
730
+ */
731
+ roots(): Agent[]
732
+ ```
733
+
734
+ Source: [`packages/core/agent/src/index.ts:256`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/core/agent/src/index.ts)
735
+
736
+ <a id="agent-events"></a>
737
+
738
+ ### `agent/*` events
739
+
740
+ <a id="agentcreated--emit"></a>
741
+
742
+ #### `agent/created` — emit
743
+
744
+ A fully configured agent and live session were published. Setup is composition-only; `agent/session-start` is the first startup-driving extension point. Synchronous listener failure vetoes publication, while returned-promise rejection is reported. Detach requested during dispatch waits until every creation listener has observed the stable entry.
745
+
746
+ ```ts cordis-catalog
747
+ /**
748
+ * A fully configured agent and live session were published. Setup is
749
+ * composition-only; `agent/session-start` is the first startup-driving extension point.
750
+ * Synchronous listener failure vetoes publication, while returned-promise
751
+ * rejection is reported. Detach requested during dispatch waits until every
752
+ * creation listener has observed the stable entry.
753
+ * @param payload.agent - the newly registered agent with its live session and completed setup.
754
+ * Scope-filtered dispatch (`@deepseek-ai/dsh-scope`): agent-scoped listeners receive only that agent.
755
+ * @mode emit
756
+ */
757
+ 'agent/created'(this: Scoped<Agent>, payload: { agent: Agent }): void
758
+ ```
759
+
760
+ Types: [Scoped](./scope.md)
761
+
762
+ Source: [`packages/core/agent/src/runtime-types.ts:159`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/core/agent/src/runtime-types.ts)
763
+
764
+ <a id="agentdisposed--emit"></a>
765
+
766
+ #### `agent/disposed` — emit
767
+
768
+ An agent left the registry; AgentLoop emits this after driver quiescence and scoped-registration unwind, but before session detachment. Custom registry users own their driver-ordering contract.
769
+
770
+ ```ts cordis-catalog
771
+ /**
772
+ * An agent left the registry; AgentLoop emits this after driver quiescence
773
+ * and scoped-registration unwind, but before session detachment. Custom
774
+ * registry users own their driver-ordering contract.
775
+ * @param payload.agent - the exact agent removed from the registry.
776
+ * Scope-filtered dispatch (`@deepseek-ai/dsh-scope`): agent-scoped listeners receive only that agent.
777
+ * @mode emit
778
+ */
779
+ 'agent/disposed'(this: Scoped<Agent>, payload: { agent: Agent }): void
780
+ ```
781
+
782
+ Types: [Scoped](./scope.md)
783
+
784
+ Source: [`packages/core/agent/src/runtime-types.ts:168`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/core/agent/src/runtime-types.ts)
785
+
786
+ <a id="agenterror--emit"></a>
787
+
788
+ #### `agent/error` — emit
789
+
790
+ A step or turn errored. The machine reports a failure here even when the error has no in-turn position for a durable record.
791
+
792
+ ```ts cordis-catalog
793
+ /**
794
+ * A step or turn errored. The machine reports a failure here even when
795
+ * the error has no in-turn position for a durable record.
796
+ * @param payload.agent - the agent whose turn errored.
797
+ * @param payload.turn - the turn in which the failure surfaced.
798
+ * @param payload.step - the step at which the failure surfaced.
799
+ * @param payload.error - the failure, verbatim.
800
+ * Scope-filtered dispatch (`@deepseek-ai/dsh-scope`): agent-scoped listeners receive only that agent.
801
+ * @mode emit
802
+ */
803
+ 'agent/error'(this: Scoped<Agent>, payload: { agent: Agent; turn: number; step: number; error: unknown }): void
804
+ ```
805
+
806
+ Types: [Scoped](./scope.md)
807
+
808
+ Source: [`packages/core/agent/src/runtime-types.ts:290`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/core/agent/src/runtime-types.ts)
809
+
810
+ <a id="agentinboxclaimed--emit"></a>
811
+
812
+ #### `agent/inbox/claimed` — emit
813
+
814
+ One message left the inbox inside its open turn. If the proposed step is rejected, the claimed message ends here: it is neither discarded nor re-emitted as a user/message, and the turn closes without a step.
815
+
816
+ ```ts cordis-catalog
817
+ /**
818
+ * One message left the inbox inside its open turn. If the proposed step
819
+ * is rejected, the claimed message ends here: it is neither discarded nor
820
+ * re-emitted as a user/message, and the turn closes without a step.
821
+ * @param payload.agent - the agent whose inbox changed.
822
+ * @param payload.message - the claimed message.
823
+ * @param payload.turn - the owning turn.
824
+ * Scope-filtered dispatch (`@deepseek-ai/dsh-scope`): agent-scoped listeners receive only that agent.
825
+ * @mode emit
826
+ */
827
+ 'agent/inbox/claimed'(this: Scoped<Agent>, payload: { agent: Agent; message: UserMessage; turn: number }): void
828
+ ```
829
+
830
+ Types: [Scoped](./scope.md) · [UserMessage](./session.md)
831
+
832
+ Source: [`packages/core/agent/src/runtime-types.ts:197`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/core/agent/src/runtime-types.ts)
833
+
834
+ <a id="agentinboxdiscarded--emit"></a>
835
+
836
+ #### `agent/inbox/discarded` — emit
837
+
838
+ One message was discarded from the live inbox.
839
+
840
+ ```ts cordis-catalog
841
+ /**
842
+ * One message was discarded from the live inbox.
843
+ * @param payload.agent - the agent whose inbox changed.
844
+ * @param payload.message - the discarded message.
845
+ * Scope-filtered dispatch (`@deepseek-ai/dsh-scope`): agent-scoped listeners receive only that agent.
846
+ * @mode emit
847
+ */
848
+ 'agent/inbox/discarded'(this: Scoped<Agent>, payload: { agent: Agent; message: UserMessage }): void
849
+ ```
850
+
851
+ Types: [Scoped](./scope.md) · [UserMessage](./session.md)
852
+
853
+ Source: [`packages/core/agent/src/runtime-types.ts:205`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/core/agent/src/runtime-types.ts)
854
+
855
+ <a id="agentinboxinserted--emit"></a>
856
+
857
+ #### `agent/inbox/inserted` — emit
858
+
859
+ One message entered the live inbox.
860
+
861
+ ```ts cordis-catalog
862
+ /**
863
+ * One message entered the live inbox.
864
+ * @param payload.agent - the agent whose inbox changed.
865
+ * @param payload.message - the inserted message.
866
+ * Scope-filtered dispatch (`@deepseek-ai/dsh-scope`): agent-scoped listeners receive only that agent.
867
+ * @mode emit
868
+ */
869
+ 'agent/inbox/inserted'(this: Scoped<Agent>, payload: { agent: Agent; message: UserMessage }): void
870
+ ```
871
+
872
+ Types: [Scoped](./scope.md) · [UserMessage](./session.md)
873
+
874
+ Source: [`packages/core/agent/src/runtime-types.ts:186`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/core/agent/src/runtime-types.ts)
875
+
876
+ <a id="agentpre-step--waterfall"></a>
877
+
878
+ #### `agent/pre-step` — waterfall
879
+
880
+ Reject a proposed step or replace the messages that enter it. Calling `next()` preserves the current messages.
881
+
882
+ ```ts cordis-catalog
883
+ /**
884
+ * Reject a proposed step or replace the messages that enter it. Calling
885
+ * `next()` preserves the current messages.
886
+ * @param payload.agent - the agent proposing the step.
887
+ * @param payload.messages - messages removed from the inbox for this step.
888
+ * @param payload.turn - the turn that will own the step.
889
+ * @param payload.step - the step proposed by the loop.
890
+ * @param payload.signal - the current turn's cancellation signal.
891
+ * Scope-filtered dispatch (`@deepseek-ai/dsh-scope`): agent-scoped listeners receive only that agent.
892
+ * @mode waterfall
893
+ */
894
+ 'agent/pre-step'(this: Scoped<Agent>, payload: { agent: Agent; messages: UserMessage[]; turn: number; step: number; signal: AbortSignal }, next: () => Promise<PreStepDecision>): Promise<PreStepDecision>
895
+ ```
896
+
897
+ Types: [Scoped](./scope.md) · [UserMessage](./session.md)
898
+
899
+ Source: [`packages/core/agent/src/runtime-types.ts:231`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/core/agent/src/runtime-types.ts)
900
+
901
+ <a id="agentrequest--waterfall"></a>
902
+
903
+ #### `agent/request` — waterfall
904
+
905
+ Replace the frozen call configuration. `await next()` yields the config the machine would use (agent options on the first request, the logged header afterwards); return a replacement to switch. Model-visible content must use logged channels; this waterfall cannot mutate messages.
906
+
907
+ ```ts cordis-catalog
908
+ /**
909
+ * Replace the frozen call configuration. `await next()` yields the config
910
+ * the machine would use (agent options on the first request, the logged
911
+ * header afterwards); return a replacement to switch. Model-visible
912
+ * content must use logged channels; this waterfall cannot mutate messages.
913
+ * @param payload.agent - the agent making the model call.
914
+ * @param payload.turn - the open turn number.
915
+ * @param payload.step - the step whose request this is.
916
+ * @param payload.signal - the current turn's explicit abort signal.
917
+ * Scope-filtered dispatch (`@deepseek-ai/dsh-scope`): agent-scoped listeners receive only that agent.
918
+ * @mode waterfall
919
+ */
920
+ 'agent/request'(this: Scoped<Agent>, payload: { agent: Agent; turn: number; step: number; signal: AbortSignal }, next: () => Promise<LlmCallConfig>): Promise<LlmCallConfig>
921
+ ```
922
+
923
+ Types: [LlmCallConfig](./llm-streaming.md) · [Scoped](./scope.md)
924
+
925
+ Source: [`packages/core/agent/src/runtime-types.ts:244`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/core/agent/src/runtime-types.ts)
926
+
927
+ <a id="agentrequest-error--waterfall"></a>
928
+
929
+ #### `agent/request-error` — waterfall
930
+
931
+ Handle one failed model-request attempt before the loop retries or closes its step. A listener returns `{ kind: 'retry' }` without calling `next()` when it owns recovery, or calls `next()` to delegate. The default `undefined` leaves the failure terminal.
932
+
933
+ ```ts cordis-catalog
934
+ /**
935
+ * Handle one failed model-request attempt before the loop retries or closes
936
+ * its step. A listener returns `{ kind: 'retry' }` without calling `next()`
937
+ * when it owns recovery, or calls `next()` to delegate. The default
938
+ * `undefined` leaves the failure terminal.
939
+ * @param payload.agent - the agent whose request failed.
940
+ * @param payload.turn - the turn containing the failed request.
941
+ * @param payload.step - the step containing the failed request attempt.
942
+ * @param payload.provider - the provider selected for the failed request.
943
+ * @param payload.failure - serializable facts normalized at the final adapter boundary.
944
+ * @param payload.retryPolicy - the policy of the adapter registration that served the failed request.
945
+ * @param payload.signal - the turn abort signal.
946
+ * Scope-filtered dispatch (`@deepseek-ai/dsh-scope`): agent-scoped listeners receive only that agent.
947
+ * @mode waterfall
948
+ */
949
+ 'agent/request-error'(this: Scoped<Agent>, payload: { agent: Agent; turn: number; step: number; provider: string; failure: LlmFailure; retryPolicy: ResolvedRetryPolicy | undefined; signal: AbortSignal }, next: () => Promise<RequestErrorAction>): Promise<RequestErrorAction>
950
+ ```
951
+
952
+ Types: [LlmFailure](./llm-streaming.md) · [ResolvedRetryPolicy](./llm-streaming.md) · [Scoped](./scope.md)
953
+
954
+ Source: [`packages/core/agent/src/runtime-types.ts:260`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/core/agent/src/runtime-types.ts)
955
+
956
+ <a id="agentsession-start--emit"></a>
957
+
958
+ #### `agent/session-start` — emit
959
+
960
+ The session lifecycle began, once before the first turn. Use `agent.inject()` to seed model-facing context. This is a notification, not a veto; disposal requested by a lifecycle owner is rechecked before the driver starts.
961
+
962
+ ```ts cordis-catalog
963
+ /**
964
+ * The session lifecycle began, once before the first turn. Use
965
+ * `agent.inject()` to seed model-facing context. This is a notification, not
966
+ * a veto; disposal requested by a lifecycle owner is rechecked before the
967
+ * driver starts.
968
+ * @param payload.agent - the agent whose session lifecycle began.
969
+ * @param payload.source - why the session started (fresh startup, resume, …).
970
+ * Scope-filtered dispatch (`@deepseek-ai/dsh-scope`): agent-scoped listeners receive only that agent.
971
+ * @mode emit
972
+ */
973
+ 'agent/session-start'(this: Scoped<Agent>, payload: { agent: Agent; source: SessionStartSource }): void
974
+ ```
975
+
976
+ Types: [Scoped](./scope.md)
977
+
978
+ Source: [`packages/core/agent/src/runtime-types.ts:217`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/core/agent/src/runtime-types.ts)
979
+
980
+ <a id="agentstatus--emit"></a>
981
+
982
+ #### `agent/status` — emit
983
+
984
+ Agent status changed (`idle` ⇄ `running`). A waking delivery enters `running` synchronously after reserving cancellation; `idle` means no driver remains scheduled or active.
985
+
986
+ ```ts cordis-catalog
987
+ /**
988
+ * Agent status changed (`idle` ⇄ `running`). A waking delivery enters
989
+ * `running` synchronously after reserving cancellation; `idle` means no
990
+ * driver remains scheduled or active.
991
+ * @param payload.agent - the agent whose status flipped.
992
+ * @param payload.status - the status just entered (the transition's destination).
993
+ * Scope-filtered dispatch (`@deepseek-ai/dsh-scope`): agent-scoped listeners receive only that agent.
994
+ * @mode emit
995
+ */
996
+ 'agent/status'(this: Scoped<Agent>, payload: { agent: Agent; status: AgentStatus }): void
997
+ ```
998
+
999
+ Types: [Scoped](./scope.md)
1000
+
1001
+ Source: [`packages/core/agent/src/runtime-types.ts:178`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/core/agent/src/runtime-types.ts)
1002
+
1003
+ <a id="agentturn-stopping--serial"></a>
1004
+
1005
+ #### `agent/turn-stopping` — serial
1006
+
1007
+ The turn is about to close: the model owes no response (no live tool calls, no fresh steering). Awaited before the boundary commits — a listener that objects steers (`agent.steer(...)`) and the machine re-reads its inbox: fresh steering runs another step, none closes the turn. Data decides, so listener order cannot change the outcome. The inverse control (stop a tool loop early) is data too: a tool result carrying `concludesTurn` ends the turn at its step. The conclusion never short-circuits already-submitted next-step work: same-step `additionalContexts` or racing steering still runs, and the turn closes only when that inbox drains.
1008
+
1009
+ ```ts cordis-catalog
1010
+ /**
1011
+ * The turn is about to close: the model owes no response (no live tool
1012
+ * calls, no fresh steering). Awaited before the boundary commits — a
1013
+ * listener that objects steers (`agent.steer(...)`) and the machine
1014
+ * re-reads its inbox: fresh steering runs another step, none closes the
1015
+ * turn. Data decides, so listener order cannot change the outcome. The
1016
+ * inverse control (stop a tool loop early) is data too: a tool result
1017
+ * carrying `concludesTurn` ends the turn at its step. The conclusion
1018
+ * never short-circuits already-submitted next-step work: same-step
1019
+ * `additionalContexts` or racing steering still runs, and the turn
1020
+ * closes only when that inbox drains.
1021
+ * @param payload.agent - the agent whose turn is at its stop boundary.
1022
+ * @param payload.turn - the turn about to close.
1023
+ * @param payload.signal - the current turn's explicit abort signal.
1024
+ * Scope-filtered dispatch (`@deepseek-ai/dsh-scope`): agent-scoped listeners receive only that agent.
1025
+ * @mode serial
1026
+ */
1027
+ 'agent/turn-stopping'(this: Scoped<Agent>, payload: { agent: Agent; turn: number; signal: AbortSignal }): Promise<void> | void
1028
+ ```
1029
+
1030
+ Types: [Scoped](./scope.md)
1031
+
1032
+ Source: [`packages/core/agent/src/runtime-types.ts:278`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/core/agent/src/runtime-types.ts)
1033
+
1034
+ <a id="agent-loop-events"></a>
1035
+
1036
+ ### `agent-loop/*` events
1037
+
1038
+ <a id="agent-loopconfig-start-failed--emit"></a>
1039
+
1040
+ #### `agent-loop/config-start-failed` — emit
1041
+
1042
+ A declarative agent entry failed before it could publish a live agent. Consumers that buffer work for the configured identity use this transient signal to reject that work instead of waiting forever. Normal factory teardown suppresses failures from the cancelled startup attempt.
1043
+
1044
+ ```ts cordis-catalog
1045
+ /**
1046
+ * A declarative agent entry failed before it could publish a live agent.
1047
+ * Consumers that buffer work for the configured identity use this
1048
+ * transient signal to reject that work instead of waiting forever. Normal
1049
+ * factory teardown suppresses failures from the cancelled startup attempt.
1050
+ * @param payload.sessionId - exact shared agent/session identity that failed startup.
1051
+ * @param payload.error - persistence, setup, or publication failure.
1052
+ * @mode emit
1053
+ */
1054
+ 'agent-loop/config-start-failed'(payload: { sessionId: SessionId; error: unknown }): void
1055
+ ```
1056
+
1057
+ Source: [`packages/core/agent-loop/src/index.ts:183`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/core/agent-loop/src/index.ts)
1058
+
1059
+ <a id="agent-preset-events"></a>
1060
+
1061
+ ### `agent-preset/*` events
1062
+
1063
+ <a id="agent-presetselected--emit"></a>
1064
+
1065
+ #### `agent-preset/selected` — emit
1066
+
1067
+ One session committed a different agent preset to its durable log. Consumers invalidate only state derived from that session's composition.
1068
+
1069
+ ```ts cordis-catalog
1070
+ /**
1071
+ * One session committed a different agent preset to its durable log.
1072
+ * Consumers invalidate only state derived from that session's composition.
1073
+ * @mode emit
1074
+ * @param sessionId - the session whose composition changed.
1075
+ * @param agentPreset - the preset recorded by the committed selection.
1076
+ */
1077
+ 'agent-preset/selected'(sessionId: SessionId, agentPreset: string): void
1078
+ ```
1079
+
1080
+ Source: [`packages/preset/agent-presets/src/types.ts:13`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/preset/agent-presets/src/types.ts)
1081
+ <!-- END GENERATED cordis-surface -->