dsh-plugin-dev-kb 1.8.0 → 1.8.1

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 (229) hide show
  1. package/CHANGELOG.md +186 -178
  2. package/README.en.md +172 -172
  3. package/README.md +183 -183
  4. package/kb/INDEX.md +236 -234
  5. package/kb/README.md +88 -88
  6. package/kb/extra/AGENTS.md +77 -77
  7. package/kb/extra/deepseek-llm-api-wire-extensions.md +162 -162
  8. package/kb/extra/deepseek-llm-api-wire-extensions.zh.md +162 -162
  9. package/kb/extra/defensive-patterns/package-surface.md +233 -232
  10. package/kb/extra/defensive-patterns/package-surface.zh.md +233 -232
  11. package/kb/extra/defensive-patterns/remote-and-api.md +117 -117
  12. package/kb/extra/defensive-patterns/remote-and-api.zh.md +117 -117
  13. package/kb/extra/development.md +221 -221
  14. package/kb/extra/development.zh.md +228 -228
  15. package/kb/extra/event-producer-consumer.md +133 -123
  16. package/kb/extra/event-producer-consumer.zh.md +135 -125
  17. package/kb/extra/module-graph.md +1961 -1888
  18. package/kb/extra/module-graph.zh.md +1963 -1890
  19. package/kb/extra/persistence-changes/2026-11-05-timeline-priority-notice.md +60 -0
  20. package/kb/extra/persistence-changes/2026-11-05-timeline-priority-notice.zh.md +60 -0
  21. package/kb/extra/persistence-changes/README.md +92 -92
  22. package/kb/extra/persistence-changes/README.zh.md +92 -92
  23. package/kb/extra/persistence-changes/historical-formats/README.md +75 -75
  24. package/kb/extra/persistence-changes/historical-formats/README.zh.md +75 -75
  25. package/kb/extra/persistence-changes/releases/README.md +103 -103
  26. package/kb/extra/persistence-changes/releases/README.zh.md +103 -103
  27. package/kb/extra/rescope.md +55 -55
  28. package/kb/extra/rescope.zh.md +55 -55
  29. package/kb/extra/subsystems/boot.md +631 -621
  30. package/kb/extra/subsystems/boot.zh.md +631 -621
  31. package/kb/extra/subsystems/computer-use.md +179 -137
  32. package/kb/extra/subsystems/computer-use.zh.md +179 -137
  33. package/kb/extra/subsystems/extensions.md +387 -384
  34. package/kb/extra/subsystems/extensions.zh.md +387 -384
  35. package/kb/extra/subsystems/feedback.md +418 -400
  36. package/kb/extra/subsystems/feedback.zh.md +418 -400
  37. package/kb/extra/subsystems/forge.md +2169 -1226
  38. package/kb/extra/subsystems/forge.zh.md +2171 -1226
  39. package/kb/extra/subsystems/liveshare.md +167 -158
  40. package/kb/extra/subsystems/liveshare.zh.md +167 -158
  41. package/kb/extra/subsystems/mcp.md +237 -234
  42. package/kb/extra/subsystems/mcp.zh.md +237 -234
  43. package/kb/extra/subsystems/office-kit.md +122 -0
  44. package/kb/extra/subsystems/office-kit.zh.md +122 -0
  45. package/kb/extra/subsystems/working-directory.md +95 -64
  46. package/kb/extra/subsystems/working-directory.zh.md +95 -64
  47. package/kb/extra/subsystems/worktrees.md +187 -127
  48. package/kb/extra/subsystems/worktrees.zh.md +187 -127
  49. package/kb/extra/upgrade-guide/v0.2.1-alpha.1/python-ptc-sandbox/guide.md +21 -21
  50. package/kb/extra/upgrade-guide/v0.2.1-alpha.1/python-ptc-sandbox/guide.zh.md +21 -21
  51. package/kb/extra/upgrade-guide/v0.2.1-rc.4/sandbox-unsupported-policy-error/guide.md +18 -0
  52. package/kb/extra/upgrade-guide/v0.2.1-rc.4/sandbox-unsupported-policy-error/guide.zh.md +18 -0
  53. package/kb/extra/upgrade-guide/v0.2.2-alpha.2/llm-error-builtin-codes/guide.md +18 -0
  54. package/kb/extra/upgrade-guide/v0.2.2-alpha.2/llm-error-builtin-codes/guide.zh.md +18 -0
  55. package/kb/extra/upgrade-guide/v0.2.2-alpha.3/office-kit-package/guide.md +34 -0
  56. package/kb/extra/upgrade-guide/v0.2.2-alpha.3/office-kit-package/guide.zh.md +34 -0
  57. package/kb/extra/upgrade-guide/v0.2.2-alpha.3/office-operation-tools/guide.md +49 -0
  58. package/kb/extra/upgrade-guide/v0.2.2-alpha.3/office-operation-tools/guide.zh.md +49 -0
  59. package/kb/extra/upgrade-guide/v0.2.2-alpha.3/office-skill-resources/guide.md +36 -0
  60. package/kb/extra/upgrade-guide/v0.2.2-alpha.3/office-skill-resources/guide.zh.md +36 -0
  61. package/kb/extra/upgrade-guide/v0.2.2-rc.1/account-notification-fields/guide.md +26 -0
  62. package/kb/extra/upgrade-guide/v0.2.2-rc.1/account-notification-fields/guide.zh.md +26 -0
  63. package/kb/extra/upgrade-guide/v0.2.2-rc.1/account-remote-provider/guide.md +22 -0
  64. package/kb/extra/upgrade-guide/v0.2.2-rc.1/account-remote-provider/guide.zh.md +22 -0
  65. package/kb/extra/upgrade-guide/v0.2.2-rc.1/archive-worktrees/guide.md +21 -0
  66. package/kb/extra/upgrade-guide/v0.2.2-rc.1/archive-worktrees/guide.zh.md +21 -0
  67. package/kb/extra/upgrade-guide/v0.2.2-rc.1/attachment-error-codes/guide.md +23 -0
  68. package/kb/extra/upgrade-guide/v0.2.2-rc.1/attachment-error-codes/guide.zh.md +23 -0
  69. package/kb/extra/upgrade-guide/v0.2.2-rc.1/cua-ax-read-limits/guide.md +20 -0
  70. package/kb/extra/upgrade-guide/v0.2.2-rc.1/cua-ax-read-limits/guide.zh.md +20 -0
  71. package/kb/extra/upgrade-guide/v0.2.2-rc.1/cua-hover-ax-only/guide.md +18 -0
  72. package/kb/extra/upgrade-guide/v0.2.2-rc.1/cua-hover-ax-only/guide.zh.md +18 -0
  73. package/kb/extra/upgrade-guide/v0.2.2-rc.1/cua-native-platform/guide.md +23 -0
  74. package/kb/extra/upgrade-guide/v0.2.2-rc.1/cua-native-platform/guide.zh.md +23 -0
  75. package/kb/extra/upgrade-guide/v0.2.2-rc.1/dsh-doc-skill/guide.md +35 -0
  76. package/kb/extra/upgrade-guide/v0.2.2-rc.1/dsh-doc-skill/guide.zh.md +35 -0
  77. package/kb/extra/upgrade-guide/v0.2.2-rc.1/forge-change-request-overview/guide.md +26 -0
  78. package/kb/extra/upgrade-guide/v0.2.2-rc.1/forge-change-request-overview/guide.zh.md +26 -0
  79. package/kb/extra/upgrade-guide/v0.2.2-rc.1/forge-pinned-diff/guide.md +26 -0
  80. package/kb/extra/upgrade-guide/v0.2.2-rc.1/forge-pinned-diff/guide.zh.md +26 -0
  81. package/kb/extra/upgrade-guide/v0.2.2-rc.1/forge-review-reads/guide.md +19 -0
  82. package/kb/extra/upgrade-guide/v0.2.2-rc.1/forge-review-reads/guide.zh.md +19 -0
  83. package/kb/extra/upgrade-guide/v0.2.2-rc.1/package-export-cleanup/guide.md +26 -0
  84. package/kb/extra/upgrade-guide/v0.2.2-rc.1/package-export-cleanup/guide.zh.md +26 -0
  85. package/kb/extra/upgrade-guide/v0.2.2-rc.1/plugin-update-method/guide.md +22 -0
  86. package/kb/extra/upgrade-guide/v0.2.2-rc.1/plugin-update-method/guide.zh.md +22 -0
  87. package/kb/extra/upgrade-guide/v0.2.2-rc.1/ptc-bootstrap-protocol/guide.md +25 -0
  88. package/kb/extra/upgrade-guide/v0.2.2-rc.1/ptc-bootstrap-protocol/guide.zh.md +25 -0
  89. package/kb/extra/upgrade-guide/v0.2.2-rc.1/ptc-inspect-output/guide.md +48 -0
  90. package/kb/extra/upgrade-guide/v0.2.2-rc.1/ptc-inspect-output/guide.zh.md +48 -0
  91. package/kb/extra/upgrade-guide/v0.2.2-rc.1/ptc-scoped-repl-default/guide.md +45 -0
  92. package/kb/extra/upgrade-guide/v0.2.2-rc.1/ptc-scoped-repl-default/guide.zh.md +45 -0
  93. package/kb/extra/upgrade-guide/v0.2.2-rc.1/runtime-context-reminders/guide.md +19 -0
  94. package/kb/extra/upgrade-guide/v0.2.2-rc.1/runtime-context-reminders/guide.zh.md +19 -0
  95. package/kb/extra/upgrade-guide/v0.2.2-rc.1/session-append-overloads/guide.md +17 -0
  96. package/kb/extra/upgrade-guide/v0.2.2-rc.1/session-append-overloads/guide.zh.md +17 -0
  97. package/kb/extra/upgrade-guide/v0.2.2-rc.1/subagent-interrupt-default/guide.md +27 -0
  98. package/kb/extra/upgrade-guide/v0.2.2-rc.1/subagent-interrupt-default/guide.zh.md +27 -0
  99. package/kb/extra/upgrade-guide/v0.2.2-rc.1/terminal-backend-cleanup-error/guide.md +20 -0
  100. package/kb/extra/upgrade-guide/v0.2.2-rc.1/terminal-backend-cleanup-error/guide.zh.md +20 -0
  101. package/kb/extra/upgrade-guide/v0.2.2-rc.1/terminal-bundle-sidebar-backend/guide.md +19 -0
  102. package/kb/extra/upgrade-guide/v0.2.2-rc.1/terminal-bundle-sidebar-backend/guide.zh.md +19 -0
  103. package/kb/extra/upgrade-guide/v0.2.2-rc.1/web-fetch-transport-error-codes/guide.md +18 -0
  104. package/kb/extra/upgrade-guide/v0.2.2-rc.1/web-fetch-transport-error-codes/guide.zh.md +18 -0
  105. package/kb/extra/upgrade-guide/v0.2.2-rc.1/web-search-billing/guide.md +41 -0
  106. package/kb/extra/upgrade-guide/v0.2.2-rc.1/web-search-billing/guide.zh.md +41 -0
  107. package/kb/extra/upgrade-guide/v0.2.2-rc.1/window-drag-regions/guide.md +21 -0
  108. package/kb/extra/upgrade-guide/v0.2.2-rc.1/window-drag-regions/guide.zh.md +21 -0
  109. package/kb/extra/upgrade-guide/v0.2.2-rc.2/client-module-metadata/guide.md +29 -0
  110. package/kb/extra/upgrade-guide/v0.2.2-rc.2/client-module-metadata/guide.zh.md +29 -0
  111. package/kb/extra/upgrade-guide/v0.2.2-rc.2/connection-page-peer/guide.md +25 -0
  112. package/kb/extra/upgrade-guide/v0.2.2-rc.2/connection-page-peer/guide.zh.md +25 -0
  113. package/kb/extra/upgrade-guide/v0.2.2-rc.2/cordis-root-and-intercept-metadata/guide.md +31 -0
  114. package/kb/extra/upgrade-guide/v0.2.2-rc.2/cordis-root-and-intercept-metadata/guide.zh.md +31 -0
  115. package/kb/extra/upgrade-guide/v0.2.2-rc.2/cua-agents-without-preset/guide.md +24 -0
  116. package/kb/extra/upgrade-guide/v0.2.2-rc.2/cua-agents-without-preset/guide.zh.md +24 -0
  117. package/kb/extra/upgrade-guide/v0.2.2-rc.2/cua-macos-provider/guide.md +24 -0
  118. package/kb/extra/upgrade-guide/v0.2.2-rc.2/cua-macos-provider/guide.zh.md +24 -0
  119. package/kb/extra/upgrade-guide/v0.2.2-rc.2/cua-partial-observations/guide.md +19 -0
  120. package/kb/extra/upgrade-guide/v0.2.2-rc.2/cua-partial-observations/guide.zh.md +19 -0
  121. package/kb/extra/upgrade-guide/v0.2.2-rc.2/deepseek-files-timeout/guide.md +15 -0
  122. package/kb/extra/upgrade-guide/v0.2.2-rc.2/deepseek-files-timeout/guide.zh.md +15 -0
  123. package/kb/extra/upgrade-guide/v0.2.2-rc.2/experimental-tool-error-codes/guide.md +28 -0
  124. package/kb/extra/upgrade-guide/v0.2.2-rc.2/experimental-tool-error-codes/guide.zh.md +28 -0
  125. package/kb/extra/upgrade-guide/v0.2.2-rc.2/package-export-cleanup/guide.md +36 -0
  126. package/kb/extra/upgrade-guide/v0.2.2-rc.2/package-export-cleanup/guide.zh.md +36 -0
  127. package/kb/extra/upgrade-guide/v0.2.2-rc.2/python-ptc-removal/guide.md +22 -0
  128. package/kb/extra/upgrade-guide/v0.2.2-rc.2/python-ptc-removal/guide.zh.md +22 -0
  129. package/kb/extra/upgrade-guide/v0.2.2-rc.2/session-status-package/guide.md +31 -0
  130. package/kb/extra/upgrade-guide/v0.2.2-rc.2/session-status-package/guide.zh.md +31 -0
  131. package/kb/extra/upgrade-guide/v0.2.2-rc.2/shared-search-filter/guide.md +18 -0
  132. package/kb/extra/upgrade-guide/v0.2.2-rc.2/shared-search-filter/guide.zh.md +18 -0
  133. package/kb/extra/upgrade-guide/v0.2.2-rc.2/shell-fs-tool-error-codes/guide.md +30 -0
  134. package/kb/extra/upgrade-guide/v0.2.2-rc.2/shell-fs-tool-error-codes/guide.zh.md +30 -0
  135. package/kb/extra/web-styling.md +57 -57
  136. package/kb/extra/web-styling.zh.md +57 -57
  137. package/kb/meta/search-index.json +4508 -3980
  138. package/kb/meta/source.json +18 -18
  139. package/kb/site/develop/practice/llm-adapter.md +192 -192
  140. package/kb/site/en/develop/practice/llm-adapter.md +192 -192
  141. package/kb/site/en/guide/desktop-uninstall-cleanup.md +63 -0
  142. package/kb/site/en/guide/plugin-market.md +153 -151
  143. package/kb/site/en/guide/providers.md +234 -234
  144. package/kb/site/en/guide/quickstart.md +35 -34
  145. package/kb/site/en/reference/capability-seams.md +789 -767
  146. package/kb/site/en/reference/config-catalog.md +5432 -5128
  147. package/kb/site/en/reference/cookbook/adding-a-package.md +175 -175
  148. package/kb/site/en/reference/cookbook/adding-a-tool.md +105 -104
  149. package/kb/site/en/reference/cookbook/extension-cookbook.md +178 -135
  150. package/kb/site/en/reference/cordis-api/context.md +373 -369
  151. package/kb/site/en/reference/cordis-api/fiber.md +457 -433
  152. package/kb/site/en/reference/cordis-api/inherited.md +44 -44
  153. package/kb/site/en/reference/cordis-api/service.md +118 -107
  154. package/kb/site/en/reference/experimental-persistence-catalog.md +36 -23
  155. package/kb/site/en/reference/index.md +174 -174
  156. package/kb/site/en/reference/persistence-catalog.md +9453 -9432
  157. package/kb/site/en/reference/subsystems/compaction.md +288 -288
  158. package/kb/site/en/reference/subsystems/core.md +1172 -1171
  159. package/kb/site/en/reference/subsystems/credentials.md +526 -497
  160. package/kb/site/en/reference/subsystems/filesystem.md +708 -708
  161. package/kb/site/en/reference/subsystems/index.md +82 -82
  162. package/kb/site/en/reference/subsystems/llm-streaming.md +1290 -1288
  163. package/kb/site/en/reference/subsystems/lsp.md +230 -206
  164. package/kb/site/en/reference/subsystems/persistence.md +451 -432
  165. package/kb/site/en/reference/subsystems/plan.md +91 -91
  166. package/kb/site/en/reference/subsystems/ptc-runtime.md +295 -261
  167. package/kb/site/en/reference/subsystems/sandbox.md +250 -232
  168. package/kb/site/en/reference/subsystems/session-query.md +525 -515
  169. package/kb/site/en/reference/subsystems/session-reference.md +231 -229
  170. package/kb/site/en/reference/subsystems/session.md +1478 -1431
  171. package/kb/site/en/reference/subsystems/sidebar-right.md +165 -165
  172. package/kb/site/en/reference/subsystems/skills.md +356 -356
  173. package/kb/site/en/reference/subsystems/slots.md +235 -231
  174. package/kb/site/en/reference/subsystems/terminal.md +188 -188
  175. package/kb/site/en/reference/subsystems/tools.md +902 -891
  176. package/kb/site/en/reference/subsystems/user-questions.md +330 -327
  177. package/kb/site/en/reference/subsystems/web-client.md +161 -104
  178. package/kb/site/en/reference/subsystems/web-server.md +434 -351
  179. package/kb/site/en/reference/subsystems/web.md +268 -268
  180. package/kb/site/en/reference/subsystems/workflow.md +287 -282
  181. package/kb/site/en/reference/subsystems/workspace.md +743 -651
  182. package/kb/site/en/reference/tool-catalog.md +2867 -2704
  183. package/kb/site/guide/desktop-uninstall-cleanup.md +63 -0
  184. package/kb/site/guide/plugin-market.md +153 -151
  185. package/kb/site/guide/providers.md +234 -234
  186. package/kb/site/guide/quickstart.md +35 -34
  187. package/kb/site/reference/capability-seams.md +789 -767
  188. package/kb/site/reference/config-catalog.md +5432 -5128
  189. package/kb/site/reference/cookbook/adding-a-package.md +175 -175
  190. package/kb/site/reference/cookbook/adding-a-tool.md +107 -106
  191. package/kb/site/reference/cookbook/extension-cookbook.md +182 -139
  192. package/kb/site/reference/cordis-api/context.md +373 -369
  193. package/kb/site/reference/cordis-api/fiber.md +459 -435
  194. package/kb/site/reference/cordis-api/inherited.md +44 -44
  195. package/kb/site/reference/cordis-api/service.md +118 -107
  196. package/kb/site/reference/experimental-persistence-catalog.md +36 -23
  197. package/kb/site/reference/index.md +178 -178
  198. package/kb/site/reference/persistence-catalog.md +9453 -9432
  199. package/kb/site/reference/subsystems/compaction.md +288 -288
  200. package/kb/site/reference/subsystems/core.md +1184 -1183
  201. package/kb/site/reference/subsystems/credentials.md +526 -497
  202. package/kb/site/reference/subsystems/filesystem.md +708 -708
  203. package/kb/site/reference/subsystems/index.md +82 -82
  204. package/kb/site/reference/subsystems/llm-streaming.md +1296 -1294
  205. package/kb/site/reference/subsystems/lsp.md +230 -206
  206. package/kb/site/reference/subsystems/persistence.md +451 -432
  207. package/kb/site/reference/subsystems/plan.md +91 -91
  208. package/kb/site/reference/subsystems/ptc-runtime.md +295 -261
  209. package/kb/site/reference/subsystems/sandbox.md +250 -232
  210. package/kb/site/reference/subsystems/session-query.md +525 -515
  211. package/kb/site/reference/subsystems/session-reference.md +231 -229
  212. package/kb/site/reference/subsystems/session.md +1482 -1435
  213. package/kb/site/reference/subsystems/sidebar-right.md +165 -165
  214. package/kb/site/reference/subsystems/skills.md +356 -356
  215. package/kb/site/reference/subsystems/slots.md +239 -235
  216. package/kb/site/reference/subsystems/terminal.md +188 -188
  217. package/kb/site/reference/subsystems/tools.md +902 -891
  218. package/kb/site/reference/subsystems/user-questions.md +330 -327
  219. package/kb/site/reference/subsystems/web-client.md +161 -104
  220. package/kb/site/reference/subsystems/web-server.md +434 -351
  221. package/kb/site/reference/subsystems/web.md +268 -268
  222. package/kb/site/reference/subsystems/workflow.md +287 -282
  223. package/kb/site/reference/subsystems/workspace.md +743 -651
  224. package/kb/site/reference/tool-catalog.md +2872 -2709
  225. package/lib/index.js +359 -359
  226. package/package.json +68 -68
  227. package/skills/dsh-plugin-dev-kb.md +71 -70
  228. package/kb/extra/subsystems/office-to-pdf.md +0 -87
  229. package/kb/extra/subsystems/office-to-pdf.zh.md +0 -87
@@ -1,891 +1,902 @@
1
- ---
2
- editSource: "docs/subsystems/tools.zh.md"
3
- rawMarkdownPath: "reference/subsystems/tools.md"
4
- outline: [2,3]
5
- ---
6
-
7
- # 工具
8
-
9
- [dsh-tools](https://github.com/deepseek-ai/deepseek-harness/tree/master/packages/core/tools) 的工具流水线。[core.md](./core.md) 介绍了核心包共用、用于编写流水线的类型 `ToolDefinition`;面向模型的 [`ToolSchema`](./llm-streaming.md#the-model-request-and-result) 协议类型与模型请求一起声明。本页记录 `ToolDefinition` 的每个字段、用于构建它的类型化 schema DSL、带守卫的执行类型和 UI 展示类型。
10
-
11
- 源码:[`packages/core/tools/src/index.ts`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/core/tools/src/index.ts) · [`packages/core/tools/src/schema.ts`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/core/tools/src/schema.ts) · [`packages/core/tools/src/presentation.ts`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/core/tools/src/presentation.ts)
12
-
13
- ## `ToolDefinition` — 一个已注册的工具
14
-
15
- 由一个 `ToolSchema`(面向模型的字段)、必需的规范输出声明、`execute` 函数、仅供宿主使用的调度器元数据、可选的最终内容回调和可选 UI 展示函数组成。注册表持有这些定义,循环通过它们分派调用。注册表的 `schemas()` 通过显式允许列表构建面向模型的 `ToolSchema[]`;`output`/`execute`/`projectContent`/`finalizeContent`/`timeoutMs`/`isConcurrencySafe`/`presentCall`/`presentResult` 绝不能泄漏到模型请求中。
16
-
17
- ```ts type-equiv
18
- /** Tool-owned canonical output contract used after the body returns a JSON value. */
19
- interface ToolOutputDefinition {
20
- /** Raw supported JSON Schema enforced against every successful canonical value. */
21
- readonly schema: JsonSchemaNode
22
- /** Pure projection from validated arguments and value to Native/model content. */
23
- render(args: unknown, value: JsonValue): ContentBlock[]
24
- /** Pure replayable presentation projection for native and nested calls. */
25
- presentationMeta?(args: unknown, value: JsonValue): JsonValue
26
- }
27
- ```
28
-
29
- ```ts type-equiv
30
- /** A registered tool: its schema plus the execution function. */
31
- interface ToolDefinition extends ToolSchema {
32
- /** Mandatory canonical output declaration. */
33
- readonly output: ToolOutputDefinition
34
- /**
35
- * Run one accepted call and return only its canonical lossless-JSON value.
36
- * Async work must observe or forward `exec.signal` and settle only after its
37
- * owned work reaches quiescence. The executor preserves caller cancellation
38
- * through around-dispatch signal replacement and does not abandon this
39
- * promise, but it cannot hard-kill same-process code.
40
- * @param args - losslessly snapshotted, frozen model arguments.
41
- * @param exec - execution identity, cancellation signal, and context deferral.
42
- * @returns the canonical value declared by `output.schema`.
43
- */
44
- execute(args: unknown, exec: ToolRunContext): Promise<unknown>
45
- /**
46
- * Install execution-prepared content before `tools/post-execute` policies.
47
- * The callback is captured when the call starts and runs once for a
48
- * normalized outcome entering post-execute. Policy replacements remain
49
- * authoritative; pipeline failures that bypass post-execute skip projection.
50
- * @param exec - immutable execution identity and arguments.
51
- * @param result - normalized result before post-execute policy.
52
- * @returns replacement content, or undefined to preserve the renderer output.
53
- */
54
- projectContent?(exec: Readonly<ToolExecution>, result: Readonly<ToolExecutionResult>): ContentBlock[] | undefined
55
- /**
56
- * Synchronous last-mile transform for model-facing content. The executor
57
- * snapshots this callback when execution starts and invokes it exactly once
58
- * for every normalized outcome, including pipeline failures that bypass
59
- * `tools/post-execute`, immediately before lossless materialization.
60
- * Returning `undefined` preserves the content; every other result field
61
- * remains executor-owned. The callback must be total and must not throw.
62
- * @param exec - immutable execution identity and arguments.
63
- * @param result - complete normalized outcome before materialization.
64
- * @returns replacement content, or `undefined` to preserve it.
65
- */
66
- finalizeContent?(exec: Readonly<ToolExecution>, result: Readonly<ToolExecutionResult>): ContentBlock[] | undefined
67
- /**
68
- * Cooperative tool-call timeout budget in milliseconds. Omit for no deadline.
69
- * Enforced by `@deepseek-ai/dsh-tool-call-timeout-policy` (a `tools/execute` wrapper); it
70
- * is NEVER sent to the model — `schemas()` whitelists only name/description/
71
- * parameters. Declaring it asserts this tool forwards `exec.signal` to a
72
- * cooperative implementation that can reach quiescence when the signal aborts.
73
- */
74
- timeoutMs?: number
75
- /**
76
- * Pure synchronous classifier for overlap with sibling tool calls. Only
77
- * `true` opts in; omission, exceptions, non-`true` returns, and invalid
78
- * `defineTool` arguments are exclusive. This metadata is never model-visible.
79
- *
80
- * Opted-in executions must not mutate parent-owned state. Shared state must
81
- * tolerate concurrent dispatch; recorder races are permitted only when they
82
- * commute or fail closed. See the
83
- * [parallel-tool-call Agent Note](../../../../.agents/notes/implemented/feature/2026-07-10-parallel-tool-call-execution.md)
84
- * for the full contract.
85
- * @param args - parsed arguments; `defineTool` validates before calling.
86
- * @returns Whether this call may join a parallel group.
87
- */
88
- isConcurrencySafe?(args: unknown): boolean
89
- /**
90
- * Optional: how to present the PENDING state of one call in a UI, derived from
91
- * the call's `args` (parsed arguments, `unknown` — the tool validates/narrows
92
- * its own input). Returns a {@link ToolCallView} (a `card`-tagged render intent),
93
- * or `undefined` (or omit the method) to fall back to a generic presentation
94
- * (title = tool name, raw args as input). Pure and side-effect-free: a UI may
95
- * call it during live streaming AND a session-log replay, so it must depend
96
- * only on `args`.
97
- */
98
- presentCall?(args: unknown): ToolCallView | undefined
99
- /**
100
- * Optional: how to present the COMPLETED state, given the same `args` and the
101
- * durable result projection (`content`, failure state, and optional `meta`). Returns a
102
- * {@link ToolResultView}, or `undefined` (or omit the method) to keep the
103
- * pending title and render the raw result content. Pure and side-effect-free
104
- * for the same replay reason.
105
- */
106
- presentResult?(args: unknown, result: ToolResult): ToolResultView | undefined
107
- }
108
- ```
109
-
110
- `execute` 接收 `args: unknown`——原始的 `ToolDefinition` 自行校验输入。第一方工具不需要手写校验;它们使用 `defineTool`,由后者代为校验并收窄参数类型、根据 `output.schema` 推导函数体返回类型,并为两个输出投影器提供类型约束。`finalizeContent` 特意接收不可变的执行对象而非类型化参数,因为无效输入和外层流水线失败也会到达该回调;它可以施加工具自有的内容限制,同时保留 `isError`、规范值、结构化错误身份、延迟上下文与展示元数据。
111
-
112
- ## 统一的 JSON 值 schema DSL
113
-
114
- 插件作者使用同一套词汇描述类型化参数和类型化输出值。`ValueSchemaSpec` 支持 `string`、`number`、`integer`、`boolean`、`null`、`array`、`object`、仅作者侧可用的 `json`,以及要求恰好命中一个分支的 `oneOf`;标量 `enum` 和 `const` 值必须与节点类型匹配。显式对象节点始终声明 `additionalProperties: true | false`。参数定义仍是隐式的开放对象属性映射,每个必填属性都附带 `required: true`。
115
-
116
- 源码:[`packages/core/tools/src/schema.ts`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/core/tools/src/schema.ts)
117
-
118
- ```ts type-equiv
119
- /** One author-facing schema for any lossless JSON value root. */
120
- type ValueSchemaSpec =
121
- | StringValueSchemaSpec
122
- | NumberValueSchemaSpec
123
- | IntegerValueSchemaSpec
124
- | BooleanValueSchemaSpec
125
- | NullValueSchemaSpec
126
- | ArrayValueSchemaSpec
127
- | ObjectValueSchemaSpec
128
- | JsonValueSchemaSpec
129
- | OneOfValueSchemaSpec
130
- ```
131
-
132
- ```ts type-equiv
133
- /** One implicit parameter-root property, optionally required. */
134
- type ParameterPropertySpec = ValueSchemaSpec & { required?: true }
135
- ```
136
-
137
- ```ts type-equiv
138
- /**
139
- * Tool parameter schema. The map itself is an implicit open object root;
140
- * requiredness remains a per-property `required: true` annotation.
141
- */
142
- type ParameterSchemaSpec = {
143
- [key: string]: ParameterPropertySpec
144
- [key: symbol]: never
145
- }
146
- ```
147
-
148
- `{ type: 'json' }` 推导为 `JsonValue`,并编译成仅含注解、不施加约束的原始 schema。输出根可以是对象、数组、标量或 null。`InferValue<S>` 在 16 层容器内保留字面量约束与对象开放性,之后回退为 `JsonValue`,避免耗尽 TypeScript 的类型实例化栈。`InferArgs<P>` 依据逐属性的必填标记生成必填和可选的字符串键:
149
-
150
- ```ts type-equiv
151
- /**
152
- * Infer the TypeScript value accepted by an author-facing value schema. Exact
153
- * inference is bounded to 16 container levels, then falls back to `JsonValue`.
154
- */
155
- type InferValue<S> = InferValueAt<S, []>
156
- ```
157
-
158
- ```ts type-equiv
159
- /** Infer the TypeScript argument object for an implicit parameter schema. */
160
- type InferArgs<S> = InferProperties<S, []>
161
- ```
162
-
163
- `defineTool({ name, description, parameters, output, execute, … })` 将参数推导与 `parameterSchemaSpecToJsonSchema()` 和 `validateArgs()` 绑定,并将 `execute`/`render`/`presentationMeta` 与 `InferValue<OutputSchema>` 绑定。schema 记录只包含自有且可枚举的字符串键,schema 数组是稠密的内建数组,因此推导、编译与校验观察到的是同一份声明。精确推导保持到 16 层容器,之后放宽为 `JsonValue`;运行时校验仍会继续遍历完整 schema。`valueSchemaSpecToJsonSchema()` 通过同一套已强制执行的原始子集编译输出声明。参数不匹配时抛出 `ToolArgsError`(`INVALID_ARGS`);函数体或后置策略产生的值无效时抛出 `ToolOutputError`(`INVALID_TOOL_OUTPUT`)。两者都经由常规工具错误路径处理。原始 JSON Schema 默认保持开放;不支持的关键字会被拒绝,而不会在未强制执行的情况下获准进入。
164
-
165
- 注册是一项受信任的同进程约定。注册表以 readonly 输入借用已类型化定义,要求它声明 `output`,校验其原始 schema,并检查 `timeoutMs` 必须为正有限值等语义要求;`schemas()` 在构建请求时生成面向模型的投影,使执行和展示共享同一份已解析定义,而不会将回调泄漏到协议上。
166
-
167
- ## `ToolRestriction` — 单个作用域对其继承内容的实时过滤器
168
-
169
- `ToolRestriction` 作用于该作用域继承来的工具:部署全局层,加上其链上的每个祖先作用域。注册表将 readonly 名称编译为私有集合,对多个限制取交集,再叠加该作用域**自身**的注册——后者不受约束,因此被委派的子 agent 会保留其回报所依赖的工具。仅 deny 的过滤器允许后续未列出的继承工具通过,而 allow 列表则排除它们。
170
-
171
- ```ts type-equiv
172
- /**
173
- * Per-scope filter over global tools. Restrictions intersect and do not affect
174
- * scoped registrations or the reserved PTC mode transport.
175
- */
176
- interface ToolRestriction {
177
- /** Global tool names that stay visible; everything else is removed. */
178
- readonly allow?: readonly string[]
179
- /** Global tool names removed from visibility. */
180
- readonly deny?: readonly string[]
181
- }
182
- ```
183
-
184
- ## 条件准入 — 步骤拥有的准入集合
185
-
186
- 注册可以携带 `when` 条件。AgentLoop 为每个拟议步骤以捕获的模型选择调用一次 `ctx.tools.resolve()`;返回的集合决定该步骤的工具声明、执行器分区以及批次中的每次调用,包括 PTC 程序的子调用。`ctx.tools.executable()` 在执行时应用集合,没有集合时排除条件注册。同名替换或新的限制会阻止尚未开始的调用使用已准入的注册,步骤的批次结束后循环释放集合。
187
-
188
- ```ts type-equiv
189
- /** Inputs for a tool's step-time activation condition. */
190
- interface ToolActivationContext {
191
- /** Agent preparing the step. */
192
- readonly agent: Agent
193
- /** Provider, model, and optional reasoning effort captured for the step. */
194
- readonly modelSelection: Readonly<ModelSelection>
195
- /** Cancellation of step preparation; asynchronous conditions must observe it. */
196
- readonly signal: AbortSignal
197
- }
198
- ```
199
-
200
- ```ts type-equiv
201
- /** Lifecycle-owned conditions for a registered tool. */
202
- interface ToolRegistrationOptions {
203
- /**
204
- * Decide whether the step's requests may call the tool. Omission enables
205
- * it. False excludes it from both declarations and dispatch; rejection fails
206
- * step preparation. Conditions must not mutate registrations or start tool work.
207
- * @param context - preparing agent, captured model selection, and cancellation signal.
208
- * @returns whether the tool participates in this step.
209
- */
210
- readonly when?: (context: ToolActivationContext) => boolean | Promise<boolean>
211
- }
212
- ```
213
-
214
- ```ts type-equiv
215
- /** One step's admitted registrations, also used by nested PTC calls. */
216
- interface ResolvedToolSet {
217
- /** Close admission after the step's tool batch settles. */
218
- dispose(): void
219
- /**
220
- * Read admitted registrations that remain visible in their original scope.
221
- * @param scope - exact requesting agent; another scope is never admitted.
222
- * @returns original definitions excluding removed or restricted registrations.
223
- */
224
- view(scope: ScopeKey | undefined): ReadonlyMap<string, ToolDefinition>
225
- }
226
- ```
227
-
228
- ## 执行器与原始调用批次
229
-
230
- `ctx.tools.execute()` 把完整的原始模型调用批次委托给最近作用域的执行器。服务在每个批次开始时选择当前提供者;提示词组装仅返回模型输入。`agent` 在独立服务调用中不存在;`log` 在模型步骤之外的程序调用(例如初始化钩子)中不存在。有 Agent 而没有 `log` 时,调用仍受该 Agent 的工具作用域约束,但不记录根调用/结果事件。工具自身的事件仍可使用 Agent 的 Session。
231
-
232
- ```ts type-equiv
233
- /** Model-call schemas produced by an executor. */
234
- type ToolModelInput = Pick<ToolProviderResult, 'schemas' | 'knownNames'>
235
- ```
236
-
237
- ```ts type-equiv
238
- /** Raw model calls, with optional Agent ownership and model-step logging. */
239
- interface ToolBatchInput {
240
- readonly calls: readonly ToolCallBlock[]
241
- readonly signal: AbortSignal
242
- /**
243
- * Positive integer overriding the executor's concurrency limit for this batch.
244
- * Applies to concurrency-safe root calls; program subcall limits remain separate.
245
- * Undefined uses the executor's configured limit.
246
- */
247
- readonly maxParallelToolCalls?: number
248
- /**
249
- * Owning Agent supplies scoped tools and execution context, including outside a turn.
250
- * Undefined for standalone calls outside an Agent, such as a service invocation.
251
- */
252
- readonly agent?: Agent
253
- /**
254
- * Step admission for every call in the batch, including program subcalls.
255
- * Undefined admits only unconditional registrations.
256
- */
257
- readonly toolSet?: ResolvedToolSet
258
- /**
259
- * Committed model step receiving root call/result events; requires `agent`.
260
- * Undefined when `agent` is absent or for calls outside a model response, such
261
- * as session initialization hooks. Without `log`, root call/result events are
262
- * omitted while `agent`, when provided, still supplies scope and execution context.
263
- */
264
- readonly log?: {
265
- readonly turn: number
266
- readonly step: number
267
- }
268
- }
269
- ```
270
-
271
- ```ts type-equiv
272
- /** One settled result correlated with its original model call. */
273
- interface ToolCallResult {
274
- readonly callId: ToolCallId
275
- readonly result: ToolExecutionResult
276
- }
277
- ```
278
-
279
- ```ts type-equiv
280
- /** Model-ordered outcomes and context accepted for the next step. */
281
- interface ToolBatchResult {
282
- readonly results: readonly ToolCallResult[]
283
- readonly additionalContexts: readonly UserMessage[]
284
- readonly concluded: boolean
285
- }
286
- ```
287
-
288
- ```ts type-equiv
289
- /** A plugin owns argument parsing, dispatch, scheduling, cancellation, and logging. */
290
- interface ToolExecutor {
291
- /** Guidance registered and disposed with this executor; absent means no executor-owned sections. */
292
- readonly sections?: readonly ToolPromptSection[]
293
- /**
294
- * Describe the model's callable tools.
295
- * @param tools - currently visible capability definitions, after scoped restrictions.
296
- * @returns schemas and names used to validate tool ordering.
297
- */
298
- describe(tools: readonly ToolDefinition[]): ToolModelInput
299
- /**
300
- * Execute a complete batch. With log, commit root call/result events to the
301
- * Agent's Session; without log, omit those events. Tool-owned events may still
302
- * use the owning Agent's Session. Return
303
- * one outcome per input call in input order, including skipped calls on abort.
304
- * Never settle while started work is still running. Internal failures reject
305
- * with ToolBatchExecutionError after draining, preserving committed outcomes
306
- * and context; callers must stop dependent work and repair missing log results.
307
- * An explicit maxParallelToolCalls takes precedence over executor configuration.
308
- * @param input - original calls, cancellation, optional concurrency override, Agent and logging step.
309
- * @returns settled results, deferred context, and whether a success concludes the turn.
310
- */
311
- execute(input: ToolBatchInput): Promise<ToolBatchResult>
312
- }
313
- ```
314
-
315
- ## 执行:可扩展的 waterfall(瀑布式事件)加单调策略
316
-
317
- 内置执行器把解析后的 `ToolExecutionInput` 交给共享 `ToolExecutionPipeline`。流水线负责参数快照、执行身份、策略瀑布、工具分发和最终结果通知,每次返回一个 `ToolExecutionResult`。自定义执行器可以独立实现这些行为;公开的 `ToolExecutor` 只要求工具描述和批次执行方法。
318
-
319
- ```ts type-equiv
320
- /** Opaque call identity that permits correlation without exposing mutable execution state. */
321
- type ToolExecutionToken = symbol & { readonly [toolExecutionTokenBrand]: true }
322
- ```
323
-
324
- ```ts type-equiv
325
- /**
326
- * Parsed input to the shared invocation pipeline. The pipeline
327
- * adds its own token to form a {@link ToolExecution};
328
- * callers do not choose that token.
329
- */
330
- interface ToolExecutionInput {
331
- readonly callId: ToolCallId
332
- /**
333
- * Root model-requested call owning this execution tree. Callers omit it for
334
- * a root execution; nested dispatchers propagate the enclosing value.
335
- */
336
- readonly rootCallId?: ToolCallId
337
- readonly name: string
338
- /** Binding-time tool schema for a PTC inner call; frozen by its producer and never logged. */
339
- readonly schema?: ToolSchema
340
- /** Losslessly JSON-serializable parsed arguments (tools validate their own schema). */
341
- readonly arguments: unknown
342
- /** The agent on whose behalf the call runs (set by the agent loop). */
343
- readonly agent?: Agent
344
- /** Step admission shared by Native and PTC dispatch. Conditional tools require it. */
345
- readonly toolSet?: ResolvedToolSet
346
- /**
347
- * Opaque token of the enclosing transport execution, when one exists. PTC
348
- * mode sets this on SDK sub-dispatches so commit-style observers can wait for
349
- * the outer `run_code` outcome without receiving its live mutable execution.
350
- * The token also marks the call as a transport sub-dispatch rather than a
351
- * model-direct call: with the PTC executor selected, only calls with a parent may
352
- * execute a native tool name — a model-direct call (no parent) is denied as
353
- * `UNKNOWN_TOOL` before the policy pipeline.
354
- */
355
- readonly parent?: ToolExecutionToken
356
- /** Required caller-owned cancellation for this invocation. */
357
- readonly signal: AbortSignal
358
- }
359
- ```
360
-
361
- 工具函数体接收运行时扩展。`deferContext()` 把上下文附着到本次执行自己的结果上——既是组合工具转运嵌套分派上下文的通道,也可供叶子工具铸造插件来源指令——而不会在外层调用尚未结束时注入这些上下文。
362
-
363
- ```ts type-equiv
364
- /**
365
- * Runtime context handed to a tool implementation after the executor has
366
- * accepted a {@link ToolExecution}. {@link deferContext} attaches context to
367
- * this execution's own result — a composite tool ferries nested-dispatch
368
- * context back to the outer result, and a leaf tool may mint a fresh
369
- * plugin-sourced instruction; the loop appends it only after the
370
- * `tool/result`.
371
- */
372
- interface ToolRunContext extends ToolExecution {
373
- /**
374
- * Defer one context — typically a nested-dispatch context ferried by a
375
- * composite tool, or a fresh plugin-sourced instruction — until this tool's
376
- * final result reaches the agent loop. Contexts retain their individual
377
- * source and metadata and are emitted in call order.
378
- */
379
- deferContext(context: UserMessage): void
380
- /**
381
- * Mark a successful final result as terminal for the current agent turn.
382
- * The marker rides this execution's own result (`concludesTurn` exists only
383
- * on {@link ToolExecutionSuccess}); a composite that dispatches nested
384
- * calls forwards it from the nested result, exactly like
385
- * `additionalContexts`, so only an authoritative nested success can
386
- * conclude the enclosing run.
387
- */
388
- concludeTurn(): void
389
- }
390
- ```
391
-
392
- agent loop(智能体循环)向注册表查询每个待处理调用的执行模式,并据此形成独占屏障和滚动池并行执行:
393
-
394
- ```ts type-equiv
395
- /**
396
- * Scheduling mode for one pending call. `parallel` may overlap with siblings;
397
- * `exclusive` runs alone and forms an ordering barrier.
398
- */
399
- type ToolExecutionMode =
400
- | { kind: 'parallel' }
401
- | { kind: 'exclusive' }
402
- ```
403
-
404
- PTC 绑定在构造时冻结 ToolSchema,经由调度器传入 `ToolExecution.schema`;它只是临时执行元数据。每个实际开始的子分派在策略之前只记录配对 id、名称与规范化参数。开始和结算事件都不序列化描述、参数 schema 或 schema 字段。桥接层随后把已结算调用交给 `tools/ptc-dispatch-log` waterfall;它可以改变持久内容副本,但会保留程序取得的值、模型可见的外层结果和结构化失败身份:
405
-
406
- ```ts type-equiv
407
- /**
408
- * One settled `run_code` sub-dispatch about to be logged, as seen by the
409
- * `tools/ptc-dispatch-log` waterfall: the parent execution (session owner,
410
- * outer call identity), the sub-call identity, and the outcome whose durable
411
- * copy a listener may reshape. `content` is the RENDERED result projection
412
- * (what a native `tool/result` would carry) — the program itself received
413
- * the structured `value` (or just the error message on failure); only the
414
- * `tool/ptc-dispatch` event's copy changes.
415
- */
416
- interface PtcDispatchLog {
417
- /** The outer `run_code` execution. */
418
- readonly exec: ToolExecution
419
- /** The calling agent (the scope routing key and the spill owner), when the outer call has one. */
420
- readonly agent?: Agent
421
- /** Opaque sub-call id; new calls use `<parent>:ptc:<n>`. */
422
- readonly subCallId: ToolCallId
423
- /** The dispatched sub-tool name. */
424
- readonly name: string
425
- /** Whether the sub-call settled as an error. */
426
- readonly isError: boolean
427
- /** The sub-call's complete model-facing content (the settle event's default payload). */
428
- readonly content: ContentBlock[]
429
- }
430
- ```
431
-
432
- ```ts type-equiv
433
- /**
434
- * One pending tool call inside the executor pipeline. Parsed arguments cross
435
- * one lossless-JSON materialization boundary before policy and are deep-frozen;
436
- * call identity, the caller signal, and the executor-assigned {@link token} are
437
- * readonly. The executor freezes the complete object before `tools/result`
438
- * observers run.
439
- */
440
- interface ToolExecution extends ToolExecutionInput {
441
- /** Root model-requested call, resolved for every root and nested execution. */
442
- readonly rootCallId: ToolCallId
443
- /** Executor-assigned identity shared with nested calls only as their opaque `parent` token. */
444
- readonly token: ToolExecutionToken
445
- }
446
- ```
447
-
448
- ```ts type-equiv
449
- /**
450
- * Around-dispatch view of a {@link ToolExecution}. A `tools/execute` wrapper
451
- * may replace the signal for its delegated lifetime, but it cannot remove it.
452
- * The executor fuses every replacement with the captured caller signal.
453
- */
454
- interface ToolDispatchExecution extends Omit<ToolExecution, 'signal'> {
455
- /** Cancellation signal visible to the next wrapper or tool body. */
456
- signal: AbortSignal
457
- }
458
- ```
459
-
460
- `ToolExecutionToken` 是不透明的运行时 `Symbol`,仅用于身份比较。策略执行前,`execute()` 会物化并冻结参数、拒绝非 JSON 输入并分配 token。身份字段、调用方必需的 signal 和可选的 parent token 均保持 readonly。`ToolDispatchExecution` 包装层可以替换 signal 但不能移除;注册表会在调用工具函数体前重新融合调用方的 signal。最终观察者接收冻结的执行身份。
461
-
462
- `ToolGuard` 是感知作用域的最终预分派策略。其返回类型有意不包含 allow 结果:`undefined` 保留 waterfall 的决策,而返回的 reason 只能缩减权限,因此后续监听器无法撤销它。
463
-
464
- ```ts type-equiv
465
- /**
466
- * A monotonic execution guard evaluated after every `tools/pre-execute`
467
- * listener and before the tool body. Returning a reason denies the call;
468
- * returning `undefined` leaves it unchanged. Because guards have no allow
469
- * result, listener ordering cannot turn a denial back into permission.
470
- * @param execution - the identity-protected call after extensible pre-execute policy completed.
471
- * @returns a final denial reason, or `undefined` to leave the call allowed.
472
- */
473
- type ToolGuard = (execution: Readonly<ToolExecution>) => string | undefined
474
- ```
475
-
476
- ```ts type-equiv
477
- /** Canonical failure detail; internal routing information remains optional. */
478
- interface ToolFailure {
479
- /** Human-readable failure message without the Native `Error: ` envelope. */
480
- message: string
481
- /** Internal error class/code used by policy and durable diagnostics. */
482
- info?: ToolErrorInfo
483
- }
484
- ```
485
-
486
- ```ts type-equiv
487
- /** Successful canonical tool execution, including its Native/model projection. */
488
- interface ToolExecutionSuccess {
489
- readonly isError: false
490
- /** Execution-local canonical value; deliberately omitted from durable events. */
491
- readonly value: JsonValue
492
- readonly content: ContentBlock[]
493
- readonly error?: never
494
- readonly meta?: JsonValue
495
- readonly additionalContexts?: UserMessage[]
496
- /** The agent loop stops after committing this successful result batch. */
497
- readonly concludesTurn?: true
498
- }
499
- ```
500
-
501
- ```ts type-equiv
502
- /** Failed canonical tool execution; failures never carry a successful value. */
503
- interface ToolExecutionFailure {
504
- readonly isError: true
505
- readonly error: ToolFailure
506
- readonly value?: never
507
- readonly content: ContentBlock[]
508
- readonly meta?: JsonValue
509
- readonly additionalContexts?: UserMessage[]
510
- readonly concludesTurn?: never
511
- }
512
- ```
513
-
514
- ```ts type-equiv
515
- /** The discriminated, execution-local outcome of one tool call. */
516
- type ToolExecutionResult = ToolExecutionSuccess | ToolExecutionFailure
517
- ```
518
-
519
- ```ts type-equiv
520
- /** Structured error metadata for a failed tool call (alongside the model-facing text). */
521
- interface ToolErrorInfo {
522
- name: string
523
- code: string
524
- /** Optional raw user-facing detail; durable projections preserve it but model-facing content does not include it. */
525
- reason?: string
526
- }
527
- ```
528
-
529
- 结果仅承载产出。调用身份保留在不可变的 `ToolExecution` 上,后者伴随结果经过每个钩子,并出现在持久化的 `tool/call` / `tool/result` 会话事件上,因此包装层无法创建第二个相互矛盾的身份。规范的 `value` 仅存在于执行期间:循环只持久化 `content`、`error` 和 `meta`,`tool/ptc-dispatch` 则存储子调用渲染后的 `content`、`isError`、可选结构化 `error` 和展示 `meta`。回放可以重现展示,却无法重建规范的中间值。可选的 `ToolErrorInfo.reason` 保留面向用户的原始详情,不将其加入模型可见内容。
530
-
531
- 成功时,注册表会快照并校验函数体返回值,将其冻结,然后调用纯渲染器,并为原生与嵌套调用执行可选的展示元数据投影器。注册表会在 `tools/result` 之前另行物化持久展示字段;无效值、渲染器/投影器失败或非 JSON 展示都会转为 JSON 安全的 `isError`。因此,最终实时观察者能看到精确的执行期值,以及可安全用于后续持久追加的字段。
532
-
533
- 在得到最终内容之前,注册表会物化候选结果;若内容、结构化错误、附加上下文或展示元数据无法物化,则会转为仍可到达 `finalizeContent` 的 JSON 安全 `isError` 结果。注册表恰好调用该回调一次,随后在 `tools/result` 之前立即物化并冻结已接受的结果,因此实时观察到的产出可安全用于后续持久化的 `tool/result` 追加。
534
-
535
- 每个拦截 waterfall 返回一个类型化的 **Decision**(与 `agent/*` waterfall 共享的惯用模式)。`tools/pre-execute` 监听器接收 `(exec, next)` 并返回 `PreToolDecision`;`tools/execute` 包装层返回 `ToolExecutionResult`;`tools/post-execute` 监听器接收 `(exec, result, next)` 并返回 `PostToolDecision`:
536
-
537
- ```ts type-equiv
538
- /**
539
- * Pre-dispatch decision. `allow` runs the call; `deny` materializes its
540
- * model-facing reason and optional structured error identity; `cancel` selects
541
- * the canonical cancellation result without presenting a policy denial; `ask`
542
- * runs only after an approval service returns `allowed-once` and otherwise
543
- * denies; its `reason` is the audited approval reason and its optional
544
- * `displayReason` is the localized prompt text. Input rewriting is excluded because arguments are already logged and
545
- * presented.
546
- */
547
- type PreToolDecision =
548
- | { kind: 'allow' }
549
- | { kind: 'deny'; reason: string; info?: ToolErrorInfo }
550
- | { kind: 'cancel' }
551
- | { kind: 'ask'; reason?: string; displayReason?: { readonly en: string; readonly [locale: string]: string } }
552
- ```
553
-
554
- ```ts type-equiv
555
- /**
556
- * Post-dispatch decision: accept, replace one projection, attach context for the
557
- * next request, or block by turning corrective feedback into an error result.
558
- */
559
- type PostToolDecision =
560
- | { kind: 'accept'; content?: ContentBlock[]; value?: never; additionalContexts?: UserMessage[] }
561
- | { kind: 'accept'; value: JsonValue; content?: never; additionalContexts?: UserMessage[] }
562
- | { kind: 'block'; feedback: ContentBlock[]; additionalContexts?: UserMessage[] }
563
- ```
564
-
565
- 调用 `next()` 获取默认决策,或直接返回一个决策以短路。前置策略可以 deny 或 ask;只有 `allowed-once` 才继续执行,而未授权、缺少审批通道或服务、或无 agent 的请求都会变为拒绝。拒绝可以附带结构化身份与用户可见详情,而不改变其模型可见原因。Guard 仍可施加最终拒绝。参数不可被改写,因为历史记录、审计、UI 和执行必须保持一致。
566
-
567
- 后置策略可以替换内容或值,但不能同时替换两者。替换内容会保留规范值和现有元数据;替换值会重新校验并重新计算内容/元数据;阻止会移除值,并转为包含纠正反馈的 `isError`。内容替换是展示策略,而非保密策略;需要隐藏程序化值的监听器必须阻止或替换该值。`tools/result` 在归一化后接收冻结的执行和结果;观察者无法对其进行变换,观察者的失败也会被隔离。未知工具和抛出异常的工具都会变为结构化错误(`ToolNotFoundError` 映射为 `UNKNOWN_TOOL`),调用失败但不终止当前轮次。
568
-
569
- ## 已强制执行的原始 JSON Schema 子集
570
-
571
- subagent、工作流、MCP 和动态注册提供的原始 schema 使用作者侧 DSL 在协议层的对应表示。`assertSupportedJsonSchema()` 接受任意 JSON 根,`validateJsonSchemaValue()` 强制执行该 schema,`JsonSchemaError` 则报告每条不受支持或格式错误的 schema 路径。仅含注解的空节点表示不受约束的无损 JSON。`oneOf` 至少要求两个分支,且一个值必须恰好匹配其中一个。仍要求对象根的消费方调用 `assertObjectJsonSchema()` 并携带 `ObjectJsonSchema`;这样,subagent/工作流中由调用方定义的结构化输出可以继续以对象为根,而不会限制共享词汇。
572
-
573
- ```ts type-equiv
574
- /** Scalar JSON values supported by `enum` and `const`. */
575
- type JsonSchemaScalar = string | number | boolean | null
576
- ```
577
-
578
- ```ts type-equiv
579
- /** Single-type keywords accepted by the enforced subset. */
580
- type JsonSchemaType = 'object' | 'array' | 'string' | 'number' | 'integer' | 'boolean' | 'null'
581
- ```
582
-
583
- ```ts type-equiv
584
- /**
585
- * One raw JSON Schema node in the enforced subset. The optional fields express
586
- * the external wire schema; {@link assertSupportedJsonSchema} rejects invalid
587
- * combinations before a caller treats the node as trusted.
588
- */
589
- interface JsonSchemaNode {
590
- /** Omit with no constraints for any JSON value, or use `oneOf`. */
591
- type?: JsonSchemaType
592
- /** Exactly one branch must validate; at least two branches are required. */
593
- oneOf?: JsonSchemaNode[]
594
- /** Nested property schemas (`type: 'object'` only). */
595
- properties?: Record<string, JsonSchemaNode>
596
- /** Required property names; each must appear in `properties`. */
597
- required?: string[]
598
- /** `false` rejects undeclared keys; absent/`true` follows JSON Schema's open default. */
599
- additionalProperties?: boolean
600
- /** Item schema (`type: 'array'` only); absent accepts any JSON item. */
601
- items?: JsonSchemaNode
602
- /** Allowed values for a scalar node. */
603
- enum?: JsonSchemaScalar[]
604
- /** The single allowed value for a scalar node. */
605
- const?: JsonSchemaScalar
606
- /** Annotation, ignored for validation. */
607
- description?: string
608
- /** Annotation, ignored for validation. */
609
- title?: string
610
- /** Annotation, ignored for validation but required to be lossless JSON. */
611
- default?: JsonValue
612
- /** Annotation, ignored for validation but required to be lossless JSON. */
613
- examples?: JsonValue
614
- }
615
- ```
616
-
617
- ```ts type-equiv
618
- /** A consumer-constrained object-rooted schema. */
619
- type ObjectJsonSchema = JsonSchemaNode & { type: 'object' }
620
- ```
621
-
622
- ## 工具展示 UI 词汇
623
-
624
- 工具希望其调用在 UI 中如何呈现(编辑器工具调用卡片、CLI(命令行界面)日志行),提供方无关,使工具在不依赖任何客户端协议的情况下描述自身。`presentCall`/`presentResult` 返回一个 **`card` 标签的渲染意图**——一个可辨识联合类型,UI 桥接层据此分发:
625
-
626
- - `ToolCallView`(待执行):`{ card: 'generic', title, kind?, rawInput?, content?, locations? }`(默认卡片;`locations` 是 `{ path, line? }[]`,表示调用读取/修改的文件,供编辑器跟随)、`{ card: 'terminal', title, description?, cwd? }`(shell 命令→终端卡片)、或 `{ card: 'diff', title, diffs, locations? }`(文件创建/修改→行内 diff 卡片;`diffs` 是 `{ path, oldText, newText }[]`,新文件时 `oldText: null`)。
627
- - `ToolResultView`(已完成):`{ card: 'generic', title?, content? }`、`{ card: 'terminal', title?, output?, exitCode?, signal? }`(捕获的运行输出 + 退出状态;有能力的 UI 显示退出状态标签,其他 UI 可以派生围栏 ` ```console ` 回退)、`{ card: 'diff', title?, diffs }`(已完成的文件变更→要展示的变更,通常是从变更前后内容计算出带上下文行的已应用 hunk,或在没有前像时的整文件 diff)、`{ card: 'search', shape, title?, truncated, total, … }`(已完成的发现型搜索→`shape: 'matches'`(grep)为按文件分组的匹配,`shape: 'paths'`(glob)为扁平路径列表;`truncated`/`total` 报告内联结果是否被截断,使 UI 永不把部分结果当作完整结果呈现;该视图不携带结果文本——无 search 卡片的 UI 回退到原始结果内容)、`{ card: 'read', title?, path, offset, lines, totalLines, lang?, content? }`(已完成的文件读取→带行号、可选语法高亮的代码视图;`offset` 是窗口请求的 1-based 起始行,即使 `lines` 为空也保留;`lang` 是从扩展名推得的语言提示,`content` 是无读取能力的 UI 回退时使用的去信封文本)、或 `{ card: 'web', kind: 'search' | 'fetch', title?, … }`(已完成的 web 检索;`kind: 'search'` 携带结构化的 `sources`/`answer?`/`truncated`,`kind: 'fetch'` 携带 `url`/`statusCode`/`truncated`,不具备 `web` 能力的 UI 回退到原始结果内容——正文不会重复进视图)。已完成视图会替换待执行视图,因此变更工具即使与调用时的片段重复也要返回 diff 结果;搜索和 web 检索都没有 `card` 的调用时对应视图(其 pending 状态保持为 generic 卡片,因为结构化结果只在 `execute` 之后才存在)。
628
-
629
- `ToolCallKind`(`'read' | 'edit' | 'delete' | 'move' | 'search' | 'execute' | 'fetch' | 'other'`)用于为通用卡片选择图标。`FileLocation`(`{ path, line? }`)、`FileDiff`(`{ path, oldText, newText }`)与 `ReadFileLine`(`{ number, text }`,读取窗口中一行带 1-based 行号的内容)是共享的文件卡片词汇。该设计由[渲染意图联合类型 Agent Note](https://github.com/deepseek-ai/deepseek-harness/blob/master/.agents/notes/implemented/architecture/2026-07-02-tool-render-intent-union.zh.md)固定;host/client 运行时将这套中性词汇投影为各自的视图。
630
-
631
- 完整的展示字段文档见 [`packages/core/tools/src/presentation.ts`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/core/tools/src/presentation.ts)。`bash` schema 与执行器见 [shell.md](./shell.md);通用后台控制见 [jobs.md](./jobs.md)。
632
-
633
- <!-- BEGIN GENERATED cordis-surface (gen-cordis-catalog.ts) — do not edit between markers -->
634
-
635
- <a id="cordis-surface"></a>
636
-
637
- ## Cordis API
638
-
639
- 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`) — the language sides differ only in locale-specific paired document paths. 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).
640
-
641
- <a id="ctxtools--toolsservice"></a>
642
-
643
- ### `ctx.tools` — `ToolsService`
644
-
645
- Concrete tool registry and scoped executor dispatcher.
646
-
647
- ```ts cordis-catalog
648
- /**
649
- * Register globally or in the calling agent scope. Scoped tools shadow
650
- * globals; duplicates within one layer and the reserved `run_code` name fail.
651
- * @param definition - tool schema, execution, and optional finalization/presentation callbacks.
652
- * @param options - optional step-time activation condition.
653
- * @returns the exact disposer that unregisters the tool.
654
- */
655
- register(definition: ToolDefinition, options: ToolRegistrationOptions = {}): () => void
656
-
657
- /**
658
- * Restrict global tools for the calling agent scope. Empty filters, unknown
659
- * names, scope-local names, and reserved transport names fail. Restrictions
660
- * intersect; scoped registrations remain visible.
661
- * @param filter - global-tool mask: `allow` (keep only) and/or `deny` (remove).
662
- * @returns the exact disposer that lifts this restriction.
663
- */
664
- restrict(filter: ToolRestriction): () => void
665
-
666
- /**
667
- * Look up a tool as one scope sees it (scoped
668
- * shadows global; a restricted-away global reads as absent). Presenters pass
669
- * the calling agent so the rendered card matches the definition that
670
- * actually executed.
671
- * @param name - the tool name as registered.
672
- * @param scope - the viewing scope (the agent); omitted = the global view.
673
- * @param toolSet - step admission; omit only to inspect registered definitions.
674
- * @returns the definition the scope resolves, or undefined when none is visible.
675
- */
676
- get(name: string, scope?: ScopeKey, toolSet?: ResolvedToolSet): ToolDefinition | undefined
677
-
678
- /**
679
- * Resolve the definition a call may execute. With a step's admitted set, the
680
- * set decides; without one, conditional registrations are unavailable.
681
- * @param name - the tool name as registered.
682
- * @param scope - the calling agent; omitted = the global view.
683
- * @param toolSet - step admission, or omitted for a call outside a model step.
684
- * @returns the executable definition, or undefined when the call must fail as `UNKNOWN_TOOL`.
685
- */
686
- executable(name: string, scope?: ScopeKey, toolSet?: ResolvedToolSet): ToolDefinition | undefined
687
-
688
- /**
689
- * Resolve conditions once for this step without changing shared registrations.
690
- * Cancellation or a rejected condition publishes no partial selection. Removed
691
- * registrations cannot be restored by a late condition result.
692
- * @param context - preparing agent, captured model selection, and cancellation.
693
- * @returns the complete selection; the caller disposes it after its tool batch settles.
694
- */
695
- async resolve(context: ToolActivationContext): Promise<ResolvedToolSet>
696
-
697
- /**
698
- * Read the visible capability definitions for descriptions and program bindings.
699
- * @param scope - viewing Agent or preset; omitted selects the global view.
700
- * @param toolSet - step admission; omit only to inspect registered definitions.
701
- * @returns definitions after scoped shadowing and restrictions, limited to the step's admission when given.
702
- */
703
- definitions(scope?: ScopeKey, toolSet?: ResolvedToolSet): readonly ToolDefinition[]
704
-
705
- /**
706
- * Project visible definitions onto the allowlisted model-facing schema fields,
707
- * excluding execution and presentation callbacks.
708
- * @param scope - the viewing scope (the agent); omitted = the global view.
709
- * @param toolSet - step admission, or omitted for registered-definition inspection.
710
- * @returns one deep-cloned schema per visible tool.
711
- */
712
- schemas(scope?: ScopeKey, toolSet?: ResolvedToolSet): ToolSchema[]
713
-
714
- /**
715
- * Provide the executor for the calling scope; an unscoped contribution is the deployment default.
716
- * Rendering any executor-owned section requires the viewing scope's selection to remain available.
717
- * @param executor - initialized provider owned by the contributing plugin.
718
- * @returns the effect disposer. Unloading leaves the selection unavailable until it is replaced.
719
- */
720
- setExecutor(executor: ToolExecutor): () => void
721
-
722
- /**
723
- * Delegate a raw call batch to its Agent's provider or the deployment default.
724
- * Select at dispatch; accepted batches settle through that provider even if it unloads.
725
- * @param input - original calls, optional owning Agent, and optional logging step.
726
- * @returns the selected executor's settled batch; unavailable selections reject.
727
- */
728
- async execute(input: ToolBatchInput): Promise<ToolBatchResult>
729
-
730
- /**
731
- * Register a monotonic denial in the calling scope.
732
- * @param guard - evaluated after extensible pre-execute policy by the shared pipeline.
733
- * @returns the effect disposer that removes this guard.
734
- */
735
- guard(guard: ToolGuard): () => void
736
-
737
- /**
738
- * Check global and inherited guards after pre-execute policy and approval resolve.
739
- * @param exec - invocation whose guards are checked before dispatch.
740
- * @returns the first denial reason, or undefined when no guard denies.
741
- */
742
- checkGuards(exec: ToolExecution): string | undefined
743
- ```
744
-
745
- Types: [ScopeKey](./scope.md)
746
-
747
- Source: [`packages/core/tools/src/index.ts`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/core/tools/src/index.ts)
748
-
749
- <a id="tools-events"></a>
750
-
751
- ### `tools/*` events
752
-
753
- <a id="toolschange--emit"></a>
754
-
755
- #### `tools/change` — emit
756
-
757
- A tool was registered or unregistered, or a scoped restriction changed. Unfiltered registry notification: scoped listeners receive changes from every scope because a global contribution can affect every agent's next assembly.
758
-
759
- ```ts cordis-catalog
760
- /**
761
- * A tool was registered or unregistered, or a scoped restriction changed.
762
- * Unfiltered registry notification: scoped listeners receive changes from every
763
- * scope because a global contribution can affect every agent's next assembly.
764
- * @mode emit
765
- */
766
- 'tools/change'(): void
767
- ```
768
-
769
- Source: [`packages/core/tools/src/events.ts`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/core/tools/src/events.ts)
770
-
771
- <a id="toolsexecute--waterfall"></a>
772
-
773
- #### `tools/execute` — waterfall
774
-
775
- Around-dispatch waterfall for timeout, retry, or metrics. `next()` returns a normalized result; wrappers may change only `exec.signal`, while call identity remains immutable. The registry re-fuses the original caller signal before the body, so replacement cannot detach caller cancellation; wrappers must still restore their signal and reach quiescence. Scope-filtered dispatch (`@deepseek-ai/dsh-scope`): agent-scoped listeners receive only that agent's calls.
776
-
777
- ```ts cordis-catalog
778
- /**
779
- * Around-dispatch waterfall for timeout, retry, or metrics. `next()` returns
780
- * a normalized result; wrappers may change only `exec.signal`, while call
781
- * identity remains immutable. The registry re-fuses the original caller
782
- * signal before the body, so replacement cannot detach caller cancellation;
783
- * wrappers must still restore their signal and reach quiescence.
784
- * Scope-filtered dispatch (`@deepseek-ai/dsh-scope`): agent-scoped listeners receive only that agent's calls.
785
- * @param exec - the allowed call about to dispatch (name, parsed arguments, caller agent, signal).
786
- * @mode waterfall
787
- */
788
- 'tools/execute'(this: Scoped<ToolsService>, exec: ToolDispatchExecution, next: () => Promise<ToolExecutionResult>): Promise<ToolExecutionResult>
789
- ```
790
-
791
- Types: [Scoped](./scope.md)
792
-
793
- Source: [`packages/core/tools/src/events.ts`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/core/tools/src/events.ts)
794
-
795
- <a id="toolspost-execute--waterfall"></a>
796
-
797
- #### `tools/post-execute` — waterfall
798
-
799
- Accept, replace, enrich, or block a normalized dispatch result. `next()` accepts it unchanged; thrown tools still reach this waterfall as errors. Async listeners must observe `exec.signal`; after they settle, caller cancellation replaces only a successful accepted outcome with the code selected by whether the tool body was invoked. Scope-filtered dispatch (`@deepseek-ai/dsh-scope`): agent-scoped listeners receive only that agent's calls.
800
-
801
- ```ts cordis-catalog
802
- /**
803
- * Accept, replace, enrich, or block a normalized dispatch result. `next()`
804
- * accepts it unchanged; thrown tools still reach this waterfall as errors. Async
805
- * listeners must observe `exec.signal`; after they settle, caller
806
- * cancellation replaces only a successful accepted outcome with the code
807
- * selected by whether the tool body was invoked.
808
- * Scope-filtered dispatch (`@deepseek-ai/dsh-scope`): agent-scoped listeners receive only that agent's calls.
809
- * @param exec - the call that just ran (name, parsed arguments, caller agent).
810
- * @param result - the dispatch outcome a listener may accept, replace, or block.
811
- * @mode waterfall
812
- */
813
- 'tools/post-execute'(this: Scoped<ToolsService>, exec: ToolExecution, result: Readonly<ToolExecutionResult>, next: () => Promise<PostToolDecision>): Promise<PostToolDecision>
814
- ```
815
-
816
- Types: [Scoped](./scope.md)
817
-
818
- Source: [`packages/core/tools/src/events.ts`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/core/tools/src/events.ts)
819
-
820
- <a id="toolspre-execute--waterfall"></a>
821
-
822
- #### `tools/pre-execute` — waterfall
823
-
824
- Allow, deny, cancel, or ask before dispatch. `next()` delegates to allow; `cancel` selects the canonical pre-dispatch cancellation result, and missing approval support turns `ask` into denial. Async gates must observe `exec.signal`; the registry rechecks cancellation after they settle but never abandons their promise. Scope-filtered dispatch (`@deepseek-ai/dsh-scope`): agent-scoped listeners receive only that agent's calls.
825
-
826
- ```ts cordis-catalog
827
- /**
828
- * Allow, deny, cancel, or ask before dispatch. `next()` delegates to allow;
829
- * `cancel` selects the canonical pre-dispatch cancellation result, and missing
830
- * approval support turns `ask` into denial. Async gates must observe
831
- * `exec.signal`; the registry rechecks cancellation after they settle but
832
- * never abandons their promise.
833
- * Scope-filtered dispatch (`@deepseek-ai/dsh-scope`): agent-scoped listeners receive only that agent's calls.
834
- * @param exec - the pending call (name, parsed arguments, caller agent).
835
- * @mode waterfall
836
- */
837
- 'tools/pre-execute'(this: Scoped<ToolsService>, exec: ToolExecution, next: () => Promise<PreToolDecision>): Promise<PreToolDecision>
838
- ```
839
-
840
- Types: [Scoped](./scope.md)
841
-
842
- Source: [`packages/core/tools/src/events.ts`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/core/tools/src/events.ts)
843
-
844
- <a id="toolsptc-dispatch-log--waterfall"></a>
845
-
846
- #### `tools/ptc-dispatch-log` — waterfall
847
-
848
- 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.
849
-
850
- ```ts cordis-catalog
851
- /**
852
- * Allow a listener to replace content in the DURABLE LOG COPY of one
853
- * `run_code` sub-dispatch outcome before the bridge appends its
854
- * `tool/ptc-dispatch` event. `next()` keeps the
855
- * content unchanged; a listener may return replacement blocks (e.g. the
856
- * spill policy's preview + locator for an oversized text result). Only the
857
- * logged copy is affected — the program already received the complete
858
- * value, and the model sees neither. A throwing listener is contained:
859
- * the bridge falls back to logging the original settled content.
860
- * Scope-filtered dispatch (`@deepseek-ai/dsh-scope`): agent-scoped listeners receive only that agent's dispatches.
861
- * @param dispatch - the parent execution, sub-call identity, and the settled content to log.
862
- * @mode waterfall
863
- */
864
- 'tools/ptc-dispatch-log'(this: Scoped<ToolsService>, dispatch: PtcDispatchLog, next: () => Promise<ContentBlock[]>): Promise<ContentBlock[]>
865
- ```
866
-
867
- Types: [ContentBlock](./llm-streaming.md) · [Scoped](./scope.md)
868
-
869
- Source: [`packages/core/tool-executor-ptc/src/runtime-types.ts`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/core/tool-executor-ptc/src/runtime-types.ts)
870
-
871
- <a id="toolsresult--emit"></a>
872
-
873
- #### `tools/result` — emit
874
-
875
- Observe the frozen, lossless-JSON final outcome. Listener failures are contained. Scope-filtered dispatch (`@deepseek-ai/dsh-scope`): keyed by `exec.agent`.
876
-
877
- ```ts cordis-catalog
878
- /**
879
- * Observe the frozen, lossless-JSON final outcome. Listener failures are contained.
880
- * Scope-filtered dispatch (`@deepseek-ai/dsh-scope`): keyed by `exec.agent`.
881
- * @param exec - the execution object that traversed the pipeline.
882
- * @param result - a deep-frozen snapshot of the final returned result.
883
- * @mode emit
884
- */
885
- 'tools/result'(this: Scoped<ToolsService>, exec: Readonly<ToolExecution>, result: Readonly<ToolExecutionResult>): undefined
886
- ```
887
-
888
- Types: [Scoped](./scope.md)
889
-
890
- Source: [`packages/core/tools/src/events.ts`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/core/tools/src/events.ts)
891
- <!-- END GENERATED cordis-surface -->
1
+ ---
2
+ editSource: "docs/subsystems/tools.zh.md"
3
+ rawMarkdownPath: "reference/subsystems/tools.md"
4
+ outline: [2,3]
5
+ ---
6
+
7
+ # 工具
8
+
9
+ [dsh-tools](https://github.com/deepseek-ai/deepseek-harness/tree/master/packages/core/tools) 的工具流水线。[core.md](./core.md) 介绍了核心包共用、用于编写流水线的类型 `ToolDefinition`;面向模型的 [`ToolSchema`](./llm-streaming.md#the-model-request-and-result) 协议类型与模型请求一起声明。本页记录 `ToolDefinition` 的每个字段、用于构建它的类型化 schema DSL、带守卫的执行类型和 UI 展示类型。
10
+
11
+ 源码:[`packages/core/tools/src/index.ts`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/core/tools/src/index.ts) · [`packages/core/tools/src/schema.ts`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/core/tools/src/schema.ts) · [`packages/core/tools/src/presentation.ts`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/core/tools/src/presentation.ts)
12
+
13
+ ## `ToolDefinition` — 一个已注册的工具
14
+
15
+ 由一个 `ToolSchema`(面向模型的字段)、必需的规范输出声明、`execute` 函数、仅供宿主使用的调度器元数据、可选的最终内容回调和可选 UI 展示函数组成。注册表持有这些定义,循环通过它们分派调用。注册表的 `schemas()` 通过显式允许列表构建面向模型的 `ToolSchema[]`;`output`/`execute`/`projectContent`/`finalizeContent`/`timeoutMs`/`isConcurrencySafe`/`presentCall`/`presentResult` 绝不能泄漏到模型请求中。
16
+
17
+ ```ts type-equiv
18
+ /** Tool-owned canonical output contract used after the body returns a JSON value. */
19
+ interface ToolOutputDefinition {
20
+ /** Raw supported JSON Schema enforced against every successful canonical value. */
21
+ readonly schema: JsonSchemaNode
22
+ /** Pure projection from validated arguments and value to Native/model content. */
23
+ render(args: unknown, value: JsonValue): ContentBlock[]
24
+ /** Pure replayable presentation projection for native and nested calls. */
25
+ presentationMeta?(args: unknown, value: JsonValue): JsonValue
26
+ }
27
+ ```
28
+
29
+ ```ts type-equiv
30
+ /** A registered tool: its schema plus the execution function. */
31
+ interface ToolDefinition extends ToolSchema {
32
+ /** Mandatory canonical output declaration. */
33
+ readonly output: ToolOutputDefinition
34
+ /**
35
+ * Run one accepted call and return only its canonical lossless-JSON value.
36
+ * Async work must observe or forward `exec.signal` and settle only after its
37
+ * owned work reaches quiescence. The executor preserves caller cancellation
38
+ * through around-dispatch signal replacement and does not abandon this
39
+ * promise, but it cannot hard-kill same-process code.
40
+ * @param args - losslessly snapshotted, frozen model arguments.
41
+ * @param exec - execution identity, cancellation signal, and context deferral.
42
+ * @returns the canonical value declared by `output.schema`.
43
+ */
44
+ execute(args: unknown, exec: ToolRunContext): Promise<unknown>
45
+ /**
46
+ * Install execution-prepared content before `tools/post-execute` policies.
47
+ * The callback is captured when the call starts and runs once for a
48
+ * normalized outcome entering post-execute. Policy replacements remain
49
+ * authoritative; pipeline failures that bypass post-execute skip projection.
50
+ * @param exec - immutable execution identity and arguments.
51
+ * @param result - normalized result before post-execute policy.
52
+ * @returns replacement content, or undefined to preserve the renderer output.
53
+ */
54
+ projectContent?(exec: Readonly<ToolExecution>, result: Readonly<ToolExecutionResult>): ContentBlock[] | undefined
55
+ /**
56
+ * Synchronous last-mile transform for model-facing content. The executor
57
+ * snapshots this callback when execution starts and invokes it exactly once
58
+ * for every normalized outcome, including pipeline failures that bypass
59
+ * `tools/post-execute`, immediately before lossless materialization.
60
+ * Returning `undefined` preserves the content; every other result field
61
+ * remains executor-owned. The callback must be total and must not throw.
62
+ * @param exec - immutable execution identity and arguments.
63
+ * @param result - complete normalized outcome before materialization.
64
+ * @returns replacement content, or `undefined` to preserve it.
65
+ */
66
+ finalizeContent?(exec: Readonly<ToolExecution>, result: Readonly<ToolExecutionResult>): ContentBlock[] | undefined
67
+ /**
68
+ * Cooperative tool-call timeout budget in milliseconds. Omit for no deadline.
69
+ * Enforced by `@deepseek-ai/dsh-tool-call-timeout-policy` (a `tools/execute` wrapper); it
70
+ * is NEVER sent to the model — `schemas()` whitelists only name/description/
71
+ * parameters. Declaring it asserts this tool forwards `exec.signal` to a
72
+ * cooperative implementation that can reach quiescence when the signal aborts.
73
+ */
74
+ timeoutMs?: number
75
+ /**
76
+ * Pure synchronous classifier for overlap with sibling tool calls. Only
77
+ * `true` opts in; omission, exceptions, non-`true` returns, and invalid
78
+ * `defineTool` arguments are exclusive. This metadata is never model-visible.
79
+ *
80
+ * Opted-in executions must not mutate parent-owned state. Shared state must
81
+ * tolerate concurrent dispatch; recorder races are permitted only when they
82
+ * commute or fail closed. See the
83
+ * [parallel-tool-call Agent Note](../../../../.agents/notes/implemented/feature/2026-07-10-parallel-tool-call-execution.md)
84
+ * for the full contract.
85
+ * @param args - parsed arguments; `defineTool` validates before calling.
86
+ * @returns Whether this call may join a parallel group.
87
+ */
88
+ isConcurrencySafe?(args: unknown): boolean
89
+ /**
90
+ * Optional: how to present the PENDING state of one call in a UI, derived from
91
+ * the call's `args` (parsed arguments, `unknown` — the tool validates/narrows
92
+ * its own input). Returns a {@link ToolCallView} (a `card`-tagged render intent),
93
+ * or `undefined` (or omit the method) to fall back to a generic presentation
94
+ * (title = tool name, raw args as input). Pure and side-effect-free: a UI may
95
+ * call it during live streaming AND a session-log replay, so it must depend
96
+ * only on `args`.
97
+ */
98
+ presentCall?(args: unknown): ToolCallView | undefined
99
+ /**
100
+ * Optional: how to present the COMPLETED state, given the same `args` and the
101
+ * durable result projection (`content`, failure state, and optional `meta`). Returns a
102
+ * {@link ToolResultView}, or `undefined` (or omit the method) to keep the
103
+ * pending title and render the raw result content. Pure and side-effect-free
104
+ * for the same replay reason.
105
+ */
106
+ presentResult?(args: unknown, result: ToolResult): ToolResultView | undefined
107
+ }
108
+ ```
109
+
110
+ `execute` 接收 `args: unknown`——原始的 `ToolDefinition` 自行校验输入。第一方工具不需要手写校验;它们使用 `defineTool`,由后者代为校验并收窄参数类型、根据 `output.schema` 推导函数体返回类型,并为两个输出投影器提供类型约束。`finalizeContent` 特意接收不可变的执行对象而非类型化参数,因为无效输入和外层流水线失败也会到达该回调;它可以施加工具自有的内容限制,同时保留 `isError`、规范值、结构化错误身份、延迟上下文与展示元数据。
111
+
112
+ ## 统一的 JSON 值 schema DSL
113
+
114
+ 插件作者使用同一套词汇描述类型化参数和类型化输出值。`ValueSchemaSpec` 支持 `string`、`number`、`integer`、`boolean`、`null`、`array`、`object`、仅作者侧可用的 `json`,以及要求恰好命中一个分支的 `oneOf`;标量 `enum` 和 `const` 值必须与节点类型匹配。显式对象节点始终声明 `additionalProperties: true | false`。参数定义仍是隐式的开放对象属性映射,每个必填属性都附带 `required: true`。
115
+
116
+ 源码:[`packages/core/tools/src/schema.ts`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/core/tools/src/schema.ts)
117
+
118
+ ```ts type-equiv
119
+ /** One author-facing schema for any lossless JSON value root. */
120
+ type ValueSchemaSpec =
121
+ | StringValueSchemaSpec
122
+ | NumberValueSchemaSpec
123
+ | IntegerValueSchemaSpec
124
+ | BooleanValueSchemaSpec
125
+ | NullValueSchemaSpec
126
+ | ArrayValueSchemaSpec
127
+ | ObjectValueSchemaSpec
128
+ | JsonValueSchemaSpec
129
+ | OneOfValueSchemaSpec
130
+ ```
131
+
132
+ ```ts type-equiv
133
+ /** One implicit parameter-root property, optionally required. */
134
+ type ParameterPropertySpec = ValueSchemaSpec & { required?: true }
135
+ ```
136
+
137
+ ```ts type-equiv
138
+ /**
139
+ * Tool parameter schema. The map itself is an implicit open object root;
140
+ * requiredness remains a per-property `required: true` annotation.
141
+ */
142
+ type ParameterSchemaSpec = {
143
+ [key: string]: ParameterPropertySpec
144
+ [key: symbol]: never
145
+ }
146
+ ```
147
+
148
+ `{ type: 'json' }` 推导为 `JsonValue`,并编译成仅含注解、不施加约束的原始 schema。输出根可以是对象、数组、标量或 null。`InferValue<S>` 在 16 层容器内保留字面量约束与对象开放性,之后回退为 `JsonValue`,避免耗尽 TypeScript 的类型实例化栈。`InferArgs<P>` 依据逐属性的必填标记生成必填和可选的字符串键:
149
+
150
+ ```ts type-equiv
151
+ /**
152
+ * Infer the TypeScript value accepted by an author-facing value schema. Exact
153
+ * inference is bounded to 16 container levels, then falls back to `JsonValue`.
154
+ */
155
+ type InferValue<S> = InferValueAt<S, []>
156
+ ```
157
+
158
+ ```ts type-equiv
159
+ /** Infer the TypeScript argument object for an implicit parameter schema. */
160
+ type InferArgs<S> = InferProperties<S, []>
161
+ ```
162
+
163
+ `defineTool({ name, description, parameters, output, execute, … })` 将参数推导与 `parameterSchemaSpecToJsonSchema()` 和 `validateArgs()` 绑定,并将 `execute`/`render`/`presentationMeta` 与 `InferValue<OutputSchema>` 绑定。schema 记录只包含自有且可枚举的字符串键,schema 数组是稠密的内建数组,因此推导、编译与校验观察到的是同一份声明。精确推导保持到 16 层容器,之后放宽为 `JsonValue`;运行时校验仍会继续遍历完整 schema。`valueSchemaSpecToJsonSchema()` 通过同一套已强制执行的原始子集编译输出声明。参数不匹配时抛出 `ToolArgsError`(`INVALID_ARGS`);函数体或后置策略产生的值无效时抛出 `ToolOutputError`(`INVALID_TOOL_OUTPUT`)。两者都经由常规工具错误路径处理。原始 JSON Schema 默认保持开放;不支持的关键字会被拒绝,而不会在未强制执行的情况下获准进入。
164
+
165
+ 注册是一项受信任的同进程约定。注册表以 readonly 输入借用已类型化定义,要求它声明 `output`,校验其原始 schema,并检查 `timeoutMs` 必须为正有限值等语义要求;`schemas()` 在构建请求时生成面向模型的投影,使执行和展示共享同一份已解析定义,而不会将回调泄漏到协议上。
166
+
167
+ ## `ToolRestriction` — 单个作用域对其继承内容的实时过滤器
168
+
169
+ `ToolRestriction` 作用于该作用域继承来的工具:部署全局层,加上其链上的每个祖先作用域。注册表将 readonly 名称编译为私有集合,对多个限制取交集,再叠加该作用域**自身**的注册——后者不受约束,因此被委派的子 agent 会保留其回报所依赖的工具。仅 deny 的过滤器允许后续未列出的继承工具通过,而 allow 列表则排除它们。
170
+
171
+ ```ts type-equiv
172
+ /**
173
+ * Per-scope filter over global tools. Restrictions intersect and do not affect
174
+ * scoped registrations or the reserved PTC mode transport.
175
+ */
176
+ interface ToolRestriction {
177
+ /** Global tool names that stay visible; everything else is removed. */
178
+ readonly allow?: readonly string[]
179
+ /** Global tool names removed from visibility. */
180
+ readonly deny?: readonly string[]
181
+ }
182
+ ```
183
+
184
+ ## 条件准入 — 步骤拥有的准入集合
185
+
186
+ 注册可以携带 `when` 条件。AgentLoop 为每个拟议步骤以捕获的模型选择调用一次 `ctx.tools.resolve()`;返回的集合决定该步骤的工具声明、执行器分区以及批次中的每次调用,包括 PTC 程序的子调用。`ctx.tools.executable()` 在执行时应用集合,没有集合时排除条件注册。同名替换或新的限制会阻止尚未开始的调用使用已准入的注册,步骤的批次结束后循环释放集合。
187
+
188
+ ```ts type-equiv
189
+ /** Inputs for a tool's step-time activation condition. */
190
+ interface ToolActivationContext {
191
+ /** Agent preparing the step. */
192
+ readonly agent: Agent
193
+ /** Provider, model, and optional reasoning effort captured for the step. */
194
+ readonly modelSelection: Readonly<ModelSelection>
195
+ /** Cancellation of step preparation; asynchronous conditions must observe it. */
196
+ readonly signal: AbortSignal
197
+ }
198
+ ```
199
+
200
+ ```ts type-equiv
201
+ /** Lifecycle-owned conditions for a registered tool. */
202
+ interface ToolRegistrationOptions {
203
+ /**
204
+ * Decide whether the step's requests may call the tool. Omission enables
205
+ * it. False excludes it from both declarations and dispatch; rejection fails
206
+ * step preparation. Conditions must not mutate registrations or start tool work.
207
+ * @param context - preparing agent, captured model selection, and cancellation signal.
208
+ * @returns whether the tool participates in this step.
209
+ */
210
+ readonly when?: (context: ToolActivationContext) => boolean | Promise<boolean>
211
+ }
212
+ ```
213
+
214
+ ```ts type-equiv
215
+ /** One step's admitted registrations, also used by nested PTC calls. */
216
+ interface ResolvedToolSet {
217
+ /** Close admission after the step's tool batch settles. */
218
+ dispose(): void
219
+ /**
220
+ * Read admitted registrations that remain visible in their original scope.
221
+ * @param scope - exact requesting agent; another scope is never admitted.
222
+ * @returns original definitions excluding removed or restricted registrations.
223
+ */
224
+ view(scope: ScopeKey | undefined): ReadonlyMap<string, ToolDefinition>
225
+ }
226
+ ```
227
+
228
+ ## 执行器与原始调用批次
229
+
230
+ `ctx.tools.execute()` 把完整的原始模型调用批次委托给最近作用域的执行器。服务在每个批次开始时选择当前提供者;提示词组装仅返回模型输入。`agent` 在独立服务调用中不存在;`log` 在模型步骤之外的程序调用(例如初始化钩子)中不存在。有 Agent 而没有 `log` 时,调用仍受该 Agent 的工具作用域约束,但不记录根调用/结果事件。工具自身的事件仍可使用 Agent 的 Session。
231
+
232
+ ```ts type-equiv
233
+ /** Model-call schemas produced by an executor. */
234
+ type ToolModelInput = Pick<ToolProviderResult, 'schemas' | 'knownNames'>
235
+ ```
236
+
237
+ ```ts type-equiv
238
+ /** Raw model calls, with optional Agent ownership and model-step logging. */
239
+ interface ToolBatchInput {
240
+ readonly calls: readonly ToolCallBlock[]
241
+ readonly signal: AbortSignal
242
+ /**
243
+ * Positive integer overriding the executor's concurrency limit for this batch.
244
+ * Applies to concurrency-safe root calls; program subcall limits remain separate.
245
+ * Undefined uses the executor's configured limit.
246
+ */
247
+ readonly maxParallelToolCalls?: number
248
+ /**
249
+ * Owning Agent supplies scoped tools and execution context, including outside a turn.
250
+ * Undefined for standalone calls outside an Agent, such as a service invocation.
251
+ */
252
+ readonly agent?: Agent
253
+ /**
254
+ * Step admission for every call in the batch, including program subcalls.
255
+ * Undefined admits only unconditional registrations.
256
+ */
257
+ readonly toolSet?: ResolvedToolSet
258
+ /**
259
+ * Committed model step receiving root call/result events; requires `agent`.
260
+ * Undefined when `agent` is absent or for calls outside a model response, such
261
+ * as session initialization hooks. Without `log`, root call/result events are
262
+ * omitted while `agent`, when provided, still supplies scope and execution context.
263
+ */
264
+ readonly log?: {
265
+ readonly turn: number
266
+ readonly step: number
267
+ }
268
+ }
269
+ ```
270
+
271
+ ```ts type-equiv
272
+ /** One settled result correlated with its original model call. */
273
+ interface ToolCallResult {
274
+ readonly callId: ToolCallId
275
+ readonly result: ToolExecutionResult
276
+ }
277
+ ```
278
+
279
+ ```ts type-equiv
280
+ /** Model-ordered outcomes and context accepted for the next step. */
281
+ interface ToolBatchResult {
282
+ readonly results: readonly ToolCallResult[]
283
+ readonly additionalContexts: readonly UserMessage[]
284
+ readonly concluded: boolean
285
+ }
286
+ ```
287
+
288
+ ```ts type-equiv
289
+ /** A plugin owns argument parsing, dispatch, scheduling, cancellation, and logging. */
290
+ interface ToolExecutor {
291
+ /** Guidance registered and disposed with this executor; absent means no executor-owned sections. */
292
+ readonly sections?: readonly ToolPromptSection[]
293
+ /**
294
+ * Describe the model's callable tools.
295
+ * @param tools - currently visible capability definitions, after scoped restrictions.
296
+ * @returns schemas and names used to validate tool ordering.
297
+ */
298
+ describe(tools: readonly ToolDefinition[]): ToolModelInput
299
+ /**
300
+ * Execute a complete batch. With log, commit root call/result events to the
301
+ * Agent's Session; without log, omit those events. Tool-owned events may still
302
+ * use the owning Agent's Session. Return
303
+ * one outcome per input call in input order, including skipped calls on abort.
304
+ * Never settle while started work is still running. Internal failures reject
305
+ * with ToolBatchExecutionError after draining, preserving committed outcomes
306
+ * and context; callers must stop dependent work and repair missing log results.
307
+ * An explicit maxParallelToolCalls takes precedence over executor configuration.
308
+ * @param input - original calls, cancellation, optional concurrency override, Agent and logging step.
309
+ * @returns settled results, deferred context, and whether a success concludes the turn.
310
+ */
311
+ execute(input: ToolBatchInput): Promise<ToolBatchResult>
312
+ }
313
+ ```
314
+
315
+ ## 执行:可扩展的 waterfall(瀑布式事件)加单调策略
316
+
317
+ 内置执行器把解析后的 `ToolExecutionInput` 交给共享 `ToolExecutionPipeline`。流水线负责参数快照、执行身份、策略瀑布、工具分发和最终结果通知,每次返回一个 `ToolExecutionResult`。自定义执行器可以独立实现这些行为;公开的 `ToolExecutor` 只要求工具描述和批次执行方法。
318
+
319
+ ```ts type-equiv
320
+ /** Opaque call identity that permits correlation without exposing mutable execution state. */
321
+ type ToolExecutionToken = symbol & { readonly [toolExecutionTokenBrand]: true }
322
+ ```
323
+
324
+ ```ts type-equiv
325
+ /**
326
+ * Parsed input to the shared invocation pipeline. The pipeline
327
+ * adds its own token to form a {@link ToolExecution};
328
+ * callers do not choose that token.
329
+ */
330
+ interface ToolExecutionInput {
331
+ readonly callId: ToolCallId
332
+ /**
333
+ * Root model-requested call owning this execution tree. Callers omit it for
334
+ * a root execution; nested dispatchers propagate the enclosing value.
335
+ */
336
+ readonly rootCallId?: ToolCallId
337
+ readonly name: string
338
+ /** Binding-time tool schema for a PTC inner call; frozen by its producer and never logged. */
339
+ readonly schema?: ToolSchema
340
+ /** Losslessly JSON-serializable parsed arguments (tools validate their own schema). */
341
+ readonly arguments: unknown
342
+ /** The agent on whose behalf the call runs (set by the agent loop). */
343
+ readonly agent?: Agent
344
+ /** Step admission shared by Native and PTC dispatch. Conditional tools require it. */
345
+ readonly toolSet?: ResolvedToolSet
346
+ /**
347
+ * Opaque token of the enclosing transport execution, when one exists. PTC
348
+ * mode sets this on SDK sub-dispatches so commit-style observers can wait for
349
+ * the outer `run_code` outcome without receiving its live mutable execution.
350
+ * The token also marks the call as a transport sub-dispatch rather than a
351
+ * model-direct call: with the PTC executor selected, only calls with a parent may
352
+ * execute a native tool name — a model-direct call (no parent) is denied as
353
+ * `UNKNOWN_TOOL` before the policy pipeline.
354
+ */
355
+ readonly parent?: ToolExecutionToken
356
+ /** Required caller-owned cancellation for this invocation. */
357
+ readonly signal: AbortSignal
358
+ }
359
+ ```
360
+
361
+ 工具函数体接收运行时扩展。`deferContext()` 把上下文附着到本次执行自己的结果上——既是组合工具转运嵌套分派上下文的通道,也可供叶子工具铸造插件来源指令——而不会在外层调用尚未结束时注入这些上下文。
362
+
363
+ ```ts type-equiv
364
+ /**
365
+ * Runtime context handed to a tool implementation after the executor has
366
+ * accepted a {@link ToolExecution}. {@link deferContext} attaches context to
367
+ * this execution's own result — a composite tool ferries nested-dispatch
368
+ * context back to the outer result, and a leaf tool may mint a fresh
369
+ * plugin-sourced instruction; the loop appends it only after the
370
+ * `tool/result`.
371
+ */
372
+ interface ToolRunContext extends ToolExecution {
373
+ /**
374
+ * Defer one context — typically a nested-dispatch context ferried by a
375
+ * composite tool, or a fresh plugin-sourced instruction — until this tool's
376
+ * final result reaches the agent loop. Contexts retain their individual
377
+ * source and metadata and are emitted in call order.
378
+ */
379
+ deferContext(context: UserMessage): void
380
+ /**
381
+ * Mark a successful final result as terminal for the current agent turn.
382
+ * The marker rides this execution's own result (`concludesTurn` exists only
383
+ * on {@link ToolExecutionSuccess}); a composite that dispatches nested
384
+ * calls forwards it from the nested result, exactly like
385
+ * `additionalContexts`, so only an authoritative nested success can
386
+ * conclude the enclosing run.
387
+ */
388
+ concludeTurn(): void
389
+ }
390
+ ```
391
+
392
+ agent loop(智能体循环)向注册表查询每个待处理调用的执行模式,并据此形成独占屏障和滚动池并行执行:
393
+
394
+ ```ts type-equiv
395
+ /**
396
+ * Scheduling mode for one pending call. `parallel` may overlap with siblings;
397
+ * `exclusive` runs alone and forms an ordering barrier.
398
+ */
399
+ type ToolExecutionMode =
400
+ | { kind: 'parallel' }
401
+ | { kind: 'exclusive' }
402
+ ```
403
+
404
+ PTC 绑定在构造时冻结 ToolSchema,经由调度器传入 `ToolExecution.schema`;它只是临时执行元数据。每个实际开始的子分派在策略之前只记录配对 id、名称与规范化参数。开始和结算事件都不序列化描述、参数 schema 或 schema 字段。桥接层随后把已结算调用交给 `tools/ptc-dispatch-log` waterfall;它可以改变持久内容副本,但会保留程序取得的值、模型可见的外层结果和结构化失败身份:
405
+
406
+ ```ts type-equiv
407
+ /**
408
+ * One settled `run_code` sub-dispatch about to be logged, as seen by the
409
+ * `tools/ptc-dispatch-log` waterfall: the parent execution (session owner,
410
+ * outer call identity), the sub-call identity, and the outcome whose durable
411
+ * copy a listener may reshape. `content` is the RENDERED result projection
412
+ * (what a native `tool/result` would carry) — the program itself received
413
+ * the structured `value` (or just the error message on failure); only the
414
+ * `tool/ptc-dispatch` event's copy changes.
415
+ */
416
+ interface PtcDispatchLog {
417
+ /** The outer `run_code` execution. */
418
+ readonly exec: ToolExecution
419
+ /** The calling agent (the scope routing key and the spill owner), when the outer call has one. */
420
+ readonly agent?: Agent
421
+ /** Opaque sub-call id; new calls use `<parent>:ptc:<n>`. */
422
+ readonly subCallId: ToolCallId
423
+ /** The dispatched sub-tool name. */
424
+ readonly name: string
425
+ /** Whether the sub-call settled as an error. */
426
+ readonly isError: boolean
427
+ /** The sub-call's complete model-facing content (the settle event's default payload). */
428
+ readonly content: ContentBlock[]
429
+ }
430
+ ```
431
+
432
+ ```ts type-equiv
433
+ /**
434
+ * One pending tool call inside the executor pipeline. Parsed arguments cross
435
+ * one lossless-JSON materialization boundary before policy and are deep-frozen;
436
+ * call identity, the caller signal, and the executor-assigned {@link token} are
437
+ * readonly. The executor freezes the complete object before `tools/result`
438
+ * observers run.
439
+ */
440
+ interface ToolExecution extends ToolExecutionInput {
441
+ /** Root model-requested call, resolved for every root and nested execution. */
442
+ readonly rootCallId: ToolCallId
443
+ /** Executor-assigned identity shared with nested calls only as their opaque `parent` token. */
444
+ readonly token: ToolExecutionToken
445
+ }
446
+ ```
447
+
448
+ ```ts type-equiv
449
+ /**
450
+ * Around-dispatch view of a {@link ToolExecution}. A `tools/execute` wrapper
451
+ * may replace the signal for its delegated lifetime, but it cannot remove it.
452
+ * The executor fuses every replacement with the captured caller signal.
453
+ */
454
+ interface ToolDispatchExecution extends Omit<ToolExecution, 'signal'> {
455
+ /** Cancellation signal visible to the next wrapper or tool body. */
456
+ signal: AbortSignal
457
+ }
458
+ ```
459
+
460
+ `ToolExecutionToken` 是不透明的运行时 `Symbol`,仅用于身份比较。策略执行前,`execute()` 会物化并冻结参数、拒绝非 JSON 输入并分配 token。身份字段、调用方必需的 signal 和可选的 parent token 均保持 readonly。`ToolDispatchExecution` 包装层可以替换 signal 但不能移除;注册表会在调用工具函数体前重新融合调用方的 signal。最终观察者接收冻结的执行身份。
461
+
462
+ `ToolGuard` 是感知作用域的最终预分派策略。其返回类型有意不包含 allow 结果:`undefined` 保留 waterfall 的决策,而返回的 reason 或 `ToolGuardDenial` 只能缩减权限,因此后续监听器无法撤销它。拒绝中可选的 `info` 会成为失败结果的结构化错误身份。
463
+
464
+ ```ts type-equiv
465
+ /**
466
+ * A monotonic execution guard evaluated after every `tools/pre-execute`
467
+ * listener and before the tool body. Returning a reason denies the call;
468
+ * returning `undefined` leaves it unchanged. Because guards have no allow
469
+ * result, listener ordering cannot turn a denial back into permission.
470
+ * @param execution - the identity-protected call after extensible pre-execute policy completed.
471
+ * @returns a final denial reason, a denial with structured error identity, or `undefined` to leave the call allowed.
472
+ */
473
+ type ToolGuard = (execution: Readonly<ToolExecution>) => string | ToolGuardDenial | undefined
474
+ ```
475
+
476
+ ```ts type-equiv
477
+ /** A guard denial: the model-facing reason and optional structured error identity recorded on the result. */
478
+ interface ToolGuardDenial {
479
+ /** Model-facing reason rendered as the failed call's content. */
480
+ readonly reason: string
481
+ /** Structured error identity persisted beside the reason. */
482
+ readonly info?: ToolErrorInfo
483
+ }
484
+ ```
485
+
486
+ ```ts type-equiv
487
+ /** Canonical failure detail; internal routing information remains optional. */
488
+ interface ToolFailure {
489
+ /** Human-readable failure message without the Native `Error: ` envelope. */
490
+ message: string
491
+ /** Internal error class/code used by policy and durable diagnostics. */
492
+ info?: ToolErrorInfo
493
+ }
494
+ ```
495
+
496
+ ```ts type-equiv
497
+ /** Successful canonical tool execution, including its Native/model projection. */
498
+ interface ToolExecutionSuccess {
499
+ readonly isError: false
500
+ /** Execution-local canonical value; deliberately omitted from durable events. */
501
+ readonly value: JsonValue
502
+ readonly content: ContentBlock[]
503
+ readonly error?: never
504
+ readonly meta?: JsonValue
505
+ readonly additionalContexts?: UserMessage[]
506
+ /** The agent loop stops after committing this successful result batch. */
507
+ readonly concludesTurn?: true
508
+ }
509
+ ```
510
+
511
+ ```ts type-equiv
512
+ /** Failed canonical tool execution; failures never carry a successful value. */
513
+ interface ToolExecutionFailure {
514
+ readonly isError: true
515
+ readonly error: ToolFailure
516
+ readonly value?: never
517
+ readonly content: ContentBlock[]
518
+ readonly meta?: JsonValue
519
+ readonly additionalContexts?: UserMessage[]
520
+ readonly concludesTurn?: never
521
+ }
522
+ ```
523
+
524
+ ```ts type-equiv
525
+ /** The discriminated, execution-local outcome of one tool call. */
526
+ type ToolExecutionResult = ToolExecutionSuccess | ToolExecutionFailure
527
+ ```
528
+
529
+ ```ts type-equiv
530
+ /** Structured error metadata for a failed tool call (alongside the model-facing text). */
531
+ interface ToolErrorInfo {
532
+ name: string
533
+ code: string
534
+ /** Optional raw user-facing detail; durable projections preserve it but model-facing content does not include it. */
535
+ reason?: string
536
+ }
537
+ ```
538
+
539
+ 结果仅承载产出。调用身份保留在不可变的 `ToolExecution` 上,后者伴随结果经过每个钩子,并出现在持久化的 `tool/call` / `tool/result` 会话事件上,因此包装层无法创建第二个相互矛盾的身份。规范的 `value` 仅存在于执行期间:循环只持久化 `content`、`error` 和 `meta`,`tool/ptc-dispatch` 则存储子调用渲染后的 `content`、`isError`、可选结构化 `error` 和展示 `meta`。回放可以重现展示,却无法重建规范的中间值。可选的 `ToolErrorInfo.reason` 保留面向用户的原始详情,不将其加入模型可见内容。注册表会在以下情况设置 `info`:抛出的是 `HarnessError`(取其 `name` 和 `code`)、pre-execute 的 `deny` 自带 `info`,以及注册表自己生成的取消结果(`AbortError`,code 为 `ABORTED` 或 `ABORTED_BEFORE_DISPATCH`);已开始的工具主体在取消后抛出 signal 的 reason 或 name 为 `AbortError` 的 `Error` 时,结果保留其 message,并带上 `AbortError` 与 `ABORTED`,`tools/execute` 包装器在调用方取消后抛出这类值时也得到同样的标识,工具主体尚未开始时为 `ABORTED_BEFORE_DISPATCH`;`tools/execute` 包装器也可能返回带 `info` 的结果,例如超时策略的 `TOOL_TIMEOUT`。审批未授予的 `ask` 会变成 name 为 `ApprovalError` 的拒绝(该 name 标识注册表的拒绝,并不对应可抛出的类),code 为 `APPROVAL_REJECTED`、`APPROVAL_CANCELLED` 或 `APPROVAL_UNAVAILABLE`。guard 拒绝或 post-execute block 只有自带 `info` 时才会携带它;其他抛出值产生的 `error` 不含 `info`。
540
+
541
+ 成功时,注册表会快照并校验函数体返回值,将其冻结,然后调用纯渲染器,并为原生与嵌套调用执行可选的展示元数据投影器。注册表会在 `tools/result` 之前另行物化持久展示字段;无效值、渲染器/投影器失败或非 JSON 展示都会转为 JSON 安全的 `isError`。因此,最终实时观察者能看到精确的执行期值,以及可安全用于后续持久追加的字段。
542
+
543
+ 在得到最终内容之前,注册表会物化候选结果;若内容、结构化错误、附加上下文或展示元数据无法物化,则会转为仍可到达 `finalizeContent` 的 JSON 安全 `isError` 结果。注册表恰好调用该回调一次,随后在 `tools/result` 之前立即物化并冻结已接受的结果,因此实时观察到的产出可安全用于后续持久化的 `tool/result` 追加。
544
+
545
+ 每个拦截 waterfall 返回一个类型化的 **Decision**(与 `agent/*` waterfall 共享的惯用模式)。`tools/pre-execute` 监听器接收 `(exec, next)` 并返回 `PreToolDecision`;`tools/execute` 包装层返回 `ToolExecutionResult`;`tools/post-execute` 监听器接收 `(exec, result, next)` 并返回 `PostToolDecision`:
546
+
547
+ ```ts type-equiv
548
+ /**
549
+ * Pre-dispatch decision. `allow` runs the call; `deny` materializes its
550
+ * model-facing reason and optional structured error identity; `cancel` selects
551
+ * the canonical cancellation result without presenting a policy denial; `ask`
552
+ * runs only after an approval service returns `allowed-once` and otherwise
553
+ * denies; its `reason` is the audited approval reason and its optional
554
+ * `displayReason` is the localized prompt text. Input rewriting is excluded because arguments are already logged and
555
+ * presented.
556
+ */
557
+ type PreToolDecision =
558
+ | { kind: 'allow' }
559
+ | { kind: 'deny'; reason: string; info?: ToolErrorInfo }
560
+ | { kind: 'cancel' }
561
+ | { kind: 'ask'; reason?: string; displayReason?: { readonly en: string; readonly [locale: string]: string } }
562
+ ```
563
+
564
+ ```ts type-equiv
565
+ /**
566
+ * Post-dispatch decision: accept, replace one projection, attach context for the
567
+ * next request, or block by turning corrective feedback into an error result
568
+ * whose optional `info` is its structured error identity.
569
+ */
570
+ type PostToolDecision =
571
+ | { kind: 'accept'; content?: ContentBlock[]; value?: never; additionalContexts?: UserMessage[] }
572
+ | { kind: 'accept'; value: JsonValue; content?: never; additionalContexts?: UserMessage[] }
573
+ | { kind: 'block'; feedback: ContentBlock[]; info?: ToolErrorInfo; additionalContexts?: UserMessage[] }
574
+ ```
575
+
576
+ 调用 `next()` 获取默认决策,或直接返回一个决策以短路。前置策略可以 deny 或 ask;只有 `allowed-once` 才继续执行,而未授权、缺少审批通道或服务、或无 agent 的请求都会变为拒绝。拒绝可以附带结构化身份与用户可见详情,而不改变其模型可见原因。Guard 仍可施加最终拒绝。参数不可被改写,因为历史记录、审计、UI 和执行必须保持一致。
577
+
578
+ 后置策略可以替换内容或值,但不能同时替换两者。替换内容会保留规范值和现有元数据;替换值会重新校验并重新计算内容/元数据;阻止会移除值,并转为包含纠正反馈的 `isError`。内容替换是展示策略,而非保密策略;需要隐藏程序化值的监听器必须阻止或替换该值。`tools/result` 在归一化后接收冻结的执行和结果;观察者无法对其进行变换,观察者的失败也会被隔离。未知工具和抛出异常的工具都会变为结构化错误(`ToolNotFoundError` 映射为 `UNKNOWN_TOOL`),调用失败但不终止当前轮次。工具主体所需的 Agent、服务或进行中的 turn 不存在时,抛出 `ToolContextUnavailableError`(`TOOL_CONTEXT_UNAVAILABLE`)。
579
+
580
+ ## 已强制执行的原始 JSON Schema 子集
581
+
582
+ subagent、工作流、MCP 和动态注册提供的原始 schema 使用作者侧 DSL 在协议层的对应表示。`assertSupportedJsonSchema()` 接受任意 JSON 根,`validateJsonSchemaValue()` 强制执行该 schema,`JsonSchemaError` 则报告每条不受支持或格式错误的 schema 路径。仅含注解的空节点表示不受约束的无损 JSON。`oneOf` 至少要求两个分支,且一个值必须恰好匹配其中一个。仍要求对象根的消费方调用 `assertObjectJsonSchema()` 并携带 `ObjectJsonSchema`;这样,subagent/工作流中由调用方定义的结构化输出可以继续以对象为根,而不会限制共享词汇。
583
+
584
+ ```ts type-equiv
585
+ /** Scalar JSON values supported by `enum` and `const`. */
586
+ type JsonSchemaScalar = string | number | boolean | null
587
+ ```
588
+
589
+ ```ts type-equiv
590
+ /** Single-type keywords accepted by the enforced subset. */
591
+ type JsonSchemaType = 'object' | 'array' | 'string' | 'number' | 'integer' | 'boolean' | 'null'
592
+ ```
593
+
594
+ ```ts type-equiv
595
+ /**
596
+ * One raw JSON Schema node in the enforced subset. The optional fields express
597
+ * the external wire schema; {@link assertSupportedJsonSchema} rejects invalid
598
+ * combinations before a caller treats the node as trusted.
599
+ */
600
+ interface JsonSchemaNode {
601
+ /** Omit with no constraints for any JSON value, or use `oneOf`. */
602
+ type?: JsonSchemaType
603
+ /** Exactly one branch must validate; at least two branches are required. */
604
+ oneOf?: JsonSchemaNode[]
605
+ /** Nested property schemas (`type: 'object'` only). */
606
+ properties?: Record<string, JsonSchemaNode>
607
+ /** Required property names; each must appear in `properties`. */
608
+ required?: string[]
609
+ /** `false` rejects undeclared keys; absent/`true` follows JSON Schema's open default. */
610
+ additionalProperties?: boolean
611
+ /** Item schema (`type: 'array'` only); absent accepts any JSON item. */
612
+ items?: JsonSchemaNode
613
+ /** Allowed values for a scalar node. */
614
+ enum?: JsonSchemaScalar[]
615
+ /** The single allowed value for a scalar node. */
616
+ const?: JsonSchemaScalar
617
+ /** Annotation, ignored for validation. */
618
+ description?: string
619
+ /** Annotation, ignored for validation. */
620
+ title?: string
621
+ /** Annotation, ignored for validation but required to be lossless JSON. */
622
+ default?: JsonValue
623
+ /** Annotation, ignored for validation but required to be lossless JSON. */
624
+ examples?: JsonValue
625
+ }
626
+ ```
627
+
628
+ ```ts type-equiv
629
+ /** A consumer-constrained object-rooted schema. */
630
+ type ObjectJsonSchema = JsonSchemaNode & { type: 'object' }
631
+ ```
632
+
633
+ ## 工具展示 UI 词汇
634
+
635
+ 工具希望其调用在 UI 中如何呈现(编辑器工具调用卡片、CLI(命令行界面)日志行),提供方无关,使工具在不依赖任何客户端协议的情况下描述自身。`presentCall`/`presentResult` 返回一个 **`card` 标签的渲染意图**——一个可辨识联合类型,UI 桥接层据此分发:
636
+
637
+ - `ToolCallView`(待执行):`{ card: 'generic', title, kind?, rawInput?, content?, locations? }`(默认卡片;`locations` 是 `{ path, line? }[]`,表示调用读取/修改的文件,供编辑器跟随)、`{ card: 'terminal', title, description?, cwd? }`(shell 命令→终端卡片)、或 `{ card: 'diff', title, diffs, locations? }`(文件创建/修改→行内 diff 卡片;`diffs` 是 `{ path, oldText, newText }[]`,新文件时 `oldText: null`)。
638
+ - `ToolResultView`(已完成):`{ card: 'generic', title?, content? }`、`{ card: 'terminal', title?, output?, exitCode?, signal? }`(捕获的运行输出 + 退出状态;有能力的 UI 显示退出状态标签,其他 UI 可以派生围栏 ` ```console ` 回退)、`{ card: 'diff', title?, diffs }`(已完成的文件变更→要展示的变更,通常是从变更前后内容计算出带上下文行的已应用 hunk,或在没有前像时的整文件 diff)、`{ card: 'search', shape, title?, truncated, total, … }`(已完成的发现型搜索→`shape: 'matches'`(grep)为按文件分组的匹配,`shape: 'paths'`(glob)为扁平路径列表;`truncated`/`total` 报告内联结果是否被截断,使 UI 永不把部分结果当作完整结果呈现;该视图不携带结果文本——无 search 卡片的 UI 回退到原始结果内容)、`{ card: 'read', title?, path, offset, lines, totalLines, lang?, content? }`(已完成的文件读取→带行号、可选语法高亮的代码视图;`offset` 是窗口请求的 1-based 起始行,即使 `lines` 为空也保留;`lang` 是从扩展名推得的语言提示,`content` 是无读取能力的 UI 回退时使用的去信封文本)、或 `{ card: 'web', kind: 'search' | 'fetch', title?, … }`(已完成的 web 检索;`kind: 'search'` 携带结构化的 `sources`/`answer?`/`truncated`,`kind: 'fetch'` 携带 `url`/`statusCode`/`truncated`,不具备 `web` 能力的 UI 回退到原始结果内容——正文不会重复进视图)。已完成视图会替换待执行视图,因此变更工具即使与调用时的片段重复也要返回 diff 结果;搜索和 web 检索都没有 `card` 的调用时对应视图(其 pending 状态保持为 generic 卡片,因为结构化结果只在 `execute` 之后才存在)。
639
+
640
+ `ToolCallKind`(`'read' | 'edit' | 'delete' | 'move' | 'search' | 'execute' | 'fetch' | 'other'`)用于为通用卡片选择图标。`FileLocation`(`{ path, line? }`)、`FileDiff`(`{ path, oldText, newText }`)与 `ReadFileLine`(`{ number, text }`,读取窗口中一行带 1-based 行号的内容)是共享的文件卡片词汇。该设计由[渲染意图联合类型 Agent Note](https://github.com/deepseek-ai/deepseek-harness/blob/master/.agents/notes/implemented/architecture/2026-07-02-tool-render-intent-union.zh.md)固定;host/client 运行时将这套中性词汇投影为各自的视图。
641
+
642
+ 完整的展示字段文档见 [`packages/core/tools/src/presentation.ts`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/core/tools/src/presentation.ts)。`bash` schema 与执行器见 [shell.md](./shell.md);通用后台控制见 [jobs.md](./jobs.md)。
643
+
644
+ <!-- BEGIN GENERATED cordis-surface (gen-cordis-catalog.ts) — do not edit between markers -->
645
+
646
+ <a id="cordis-surface"></a>
647
+
648
+ ## Cordis API
649
+
650
+ 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`) — the language sides differ only in locale-specific paired document paths. 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).
651
+
652
+ <a id="ctxtools--toolsservice"></a>
653
+
654
+ ### `ctx.tools` — `ToolsService`
655
+
656
+ Concrete tool registry and scoped executor dispatcher.
657
+
658
+ ```ts cordis-catalog
659
+ /**
660
+ * Register globally or in the calling agent scope. Scoped tools shadow
661
+ * globals; duplicates within one layer and the reserved `run_code` name fail.
662
+ * @param definition - tool schema, execution, and optional finalization/presentation callbacks.
663
+ * @param options - optional step-time activation condition.
664
+ * @returns the exact disposer that unregisters the tool.
665
+ */
666
+ register(definition: ToolDefinition, options: ToolRegistrationOptions = {}): () => void
667
+
668
+ /**
669
+ * Restrict global tools for the calling agent scope. Empty filters, unknown
670
+ * names, scope-local names, and reserved transport names fail. Restrictions
671
+ * intersect; scoped registrations remain visible.
672
+ * @param filter - global-tool mask: `allow` (keep only) and/or `deny` (remove).
673
+ * @returns the exact disposer that lifts this restriction.
674
+ */
675
+ restrict(filter: ToolRestriction): () => void
676
+
677
+ /**
678
+ * Look up a tool as one scope sees it (scoped
679
+ * shadows global; a restricted-away global reads as absent). Presenters pass
680
+ * the calling agent so the rendered card matches the definition that
681
+ * actually executed.
682
+ * @param name - the tool name as registered.
683
+ * @param scope - the viewing scope (the agent); omitted = the global view.
684
+ * @param toolSet - step admission; omit only to inspect registered definitions.
685
+ * @returns the definition the scope resolves, or undefined when none is visible.
686
+ */
687
+ get(name: string, scope?: ScopeKey, toolSet?: ResolvedToolSet): ToolDefinition | undefined
688
+
689
+ /**
690
+ * Resolve the definition a call may execute. With a step's admitted set, the
691
+ * set decides; without one, conditional registrations are unavailable.
692
+ * @param name - the tool name as registered.
693
+ * @param scope - the calling agent; omitted = the global view.
694
+ * @param toolSet - step admission, or omitted for a call outside a model step.
695
+ * @returns the executable definition, or undefined when the call must fail as `UNKNOWN_TOOL`.
696
+ */
697
+ executable(name: string, scope?: ScopeKey, toolSet?: ResolvedToolSet): ToolDefinition | undefined
698
+
699
+ /**
700
+ * Resolve conditions once for this step without changing shared registrations.
701
+ * Cancellation or a rejected condition publishes no partial selection. Removed
702
+ * registrations cannot be restored by a late condition result.
703
+ * @param context - preparing agent, captured model selection, and cancellation.
704
+ * @returns the complete selection; the caller disposes it after its tool batch settles.
705
+ */
706
+ async resolve(context: ToolActivationContext): Promise<ResolvedToolSet>
707
+
708
+ /**
709
+ * Read the visible capability definitions for descriptions and program bindings.
710
+ * @param scope - viewing Agent or preset; omitted selects the global view.
711
+ * @param toolSet - step admission; omit only to inspect registered definitions.
712
+ * @returns definitions after scoped shadowing and restrictions, limited to the step's admission when given.
713
+ */
714
+ definitions(scope?: ScopeKey, toolSet?: ResolvedToolSet): readonly ToolDefinition[]
715
+
716
+ /**
717
+ * Project visible definitions onto the allowlisted model-facing schema fields,
718
+ * excluding execution and presentation callbacks.
719
+ * @param scope - the viewing scope (the agent); omitted = the global view.
720
+ * @param toolSet - step admission, or omitted for registered-definition inspection.
721
+ * @returns one deep-cloned schema per visible tool.
722
+ */
723
+ schemas(scope?: ScopeKey, toolSet?: ResolvedToolSet): ToolSchema[]
724
+
725
+ /**
726
+ * Provide the executor for the calling scope; an unscoped contribution is the deployment default.
727
+ * Rendering any executor-owned section requires the viewing scope's selection to remain available.
728
+ * @param executor - initialized provider owned by the contributing plugin.
729
+ * @returns the effect disposer. Unloading leaves the selection unavailable until it is replaced.
730
+ */
731
+ setExecutor(executor: ToolExecutor): () => void
732
+
733
+ /**
734
+ * Delegate a raw call batch to its Agent's provider or the deployment default.
735
+ * Select at dispatch; accepted batches settle through that provider even if it unloads.
736
+ * @param input - original calls, optional owning Agent, and optional logging step.
737
+ * @returns the selected executor's settled batch; unavailable selections reject.
738
+ */
739
+ async execute(input: ToolBatchInput): Promise<ToolBatchResult>
740
+
741
+ /**
742
+ * Register a monotonic denial in the calling scope.
743
+ * @param guard - evaluated after extensible pre-execute policy by the shared pipeline.
744
+ * @returns the effect disposer that removes this guard.
745
+ */
746
+ guard(guard: ToolGuard): () => void
747
+
748
+ /**
749
+ * Check global and inherited guards after pre-execute policy and approval resolve.
750
+ * @param exec - invocation whose guards are checked before dispatch.
751
+ * @returns the first denial with its optional structured identity, or undefined when no guard denies.
752
+ */
753
+ checkGuards(exec: ToolExecution): ToolGuardDenial | undefined
754
+ ```
755
+
756
+ Types: [ScopeKey](./scope.md)
757
+
758
+ Source: [`packages/core/tools/src/index.ts`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/core/tools/src/index.ts)
759
+
760
+ <a id="tools-events"></a>
761
+
762
+ ### `tools/*` events
763
+
764
+ <a id="toolschange--emit"></a>
765
+
766
+ #### `tools/change` — emit
767
+
768
+ A tool was registered or unregistered, or a scoped restriction changed. Unfiltered registry notification: scoped listeners receive changes from every scope because a global contribution can affect every agent's next assembly.
769
+
770
+ ```ts cordis-catalog
771
+ /**
772
+ * A tool was registered or unregistered, or a scoped restriction changed.
773
+ * Unfiltered registry notification: scoped listeners receive changes from every
774
+ * scope because a global contribution can affect every agent's next assembly.
775
+ * @mode emit
776
+ */
777
+ 'tools/change'(): void
778
+ ```
779
+
780
+ Source: [`packages/core/tools/src/events.ts`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/core/tools/src/events.ts)
781
+
782
+ <a id="toolsexecute--waterfall"></a>
783
+
784
+ #### `tools/execute` — waterfall
785
+
786
+ Around-dispatch waterfall for timeout, retry, or metrics. `next()` returns a normalized result; wrappers may change only `exec.signal`, while call identity remains immutable. The registry re-fuses the original caller signal before the body, so replacement cannot detach caller cancellation; wrappers must still restore their signal and reach quiescence. Scope-filtered dispatch (`@deepseek-ai/dsh-scope`): agent-scoped listeners receive only that agent's calls.
787
+
788
+ ```ts cordis-catalog
789
+ /**
790
+ * Around-dispatch waterfall for timeout, retry, or metrics. `next()` returns
791
+ * a normalized result; wrappers may change only `exec.signal`, while call
792
+ * identity remains immutable. The registry re-fuses the original caller
793
+ * signal before the body, so replacement cannot detach caller cancellation;
794
+ * wrappers must still restore their signal and reach quiescence.
795
+ * Scope-filtered dispatch (`@deepseek-ai/dsh-scope`): agent-scoped listeners receive only that agent's calls.
796
+ * @param exec - the allowed call about to dispatch (name, parsed arguments, caller agent, signal).
797
+ * @mode waterfall
798
+ */
799
+ 'tools/execute'(this: Scoped<ToolsService>, exec: ToolDispatchExecution, next: () => Promise<ToolExecutionResult>): Promise<ToolExecutionResult>
800
+ ```
801
+
802
+ Types: [Scoped](./scope.md)
803
+
804
+ Source: [`packages/core/tools/src/events.ts`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/core/tools/src/events.ts)
805
+
806
+ <a id="toolspost-execute--waterfall"></a>
807
+
808
+ #### `tools/post-execute` — waterfall
809
+
810
+ Accept, replace, enrich, or block a normalized dispatch result. `next()` accepts it unchanged; thrown tools still reach this waterfall as errors. Async listeners must observe `exec.signal`; after they settle, caller cancellation replaces only a successful accepted outcome with the code selected by whether the tool body was invoked. Scope-filtered dispatch (`@deepseek-ai/dsh-scope`): agent-scoped listeners receive only that agent's calls.
811
+
812
+ ```ts cordis-catalog
813
+ /**
814
+ * Accept, replace, enrich, or block a normalized dispatch result. `next()`
815
+ * accepts it unchanged; thrown tools still reach this waterfall as errors. Async
816
+ * listeners must observe `exec.signal`; after they settle, caller
817
+ * cancellation replaces only a successful accepted outcome with the code
818
+ * selected by whether the tool body was invoked.
819
+ * Scope-filtered dispatch (`@deepseek-ai/dsh-scope`): agent-scoped listeners receive only that agent's calls.
820
+ * @param exec - the call that just ran (name, parsed arguments, caller agent).
821
+ * @param result - the dispatch outcome a listener may accept, replace, or block.
822
+ * @mode waterfall
823
+ */
824
+ 'tools/post-execute'(this: Scoped<ToolsService>, exec: ToolExecution, result: Readonly<ToolExecutionResult>, next: () => Promise<PostToolDecision>): Promise<PostToolDecision>
825
+ ```
826
+
827
+ Types: [Scoped](./scope.md)
828
+
829
+ Source: [`packages/core/tools/src/events.ts`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/core/tools/src/events.ts)
830
+
831
+ <a id="toolspre-execute--waterfall"></a>
832
+
833
+ #### `tools/pre-execute` — waterfall
834
+
835
+ Allow, deny, cancel, or ask before dispatch. `next()` delegates to allow; `cancel` selects the canonical pre-dispatch cancellation result, and missing approval support turns `ask` into denial. Async gates must observe `exec.signal`; the registry rechecks cancellation after they settle but never abandons their promise. Scope-filtered dispatch (`@deepseek-ai/dsh-scope`): agent-scoped listeners receive only that agent's calls.
836
+
837
+ ```ts cordis-catalog
838
+ /**
839
+ * Allow, deny, cancel, or ask before dispatch. `next()` delegates to allow;
840
+ * `cancel` selects the canonical pre-dispatch cancellation result, and missing
841
+ * approval support turns `ask` into denial. Async gates must observe
842
+ * `exec.signal`; the registry rechecks cancellation after they settle but
843
+ * never abandons their promise.
844
+ * Scope-filtered dispatch (`@deepseek-ai/dsh-scope`): agent-scoped listeners receive only that agent's calls.
845
+ * @param exec - the pending call (name, parsed arguments, caller agent).
846
+ * @mode waterfall
847
+ */
848
+ 'tools/pre-execute'(this: Scoped<ToolsService>, exec: ToolExecution, next: () => Promise<PreToolDecision>): Promise<PreToolDecision>
849
+ ```
850
+
851
+ Types: [Scoped](./scope.md)
852
+
853
+ Source: [`packages/core/tools/src/events.ts`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/core/tools/src/events.ts)
854
+
855
+ <a id="toolsptc-dispatch-log--waterfall"></a>
856
+
857
+ #### `tools/ptc-dispatch-log` — waterfall
858
+
859
+ 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.
860
+
861
+ ```ts cordis-catalog
862
+ /**
863
+ * Allow a listener to replace content in the DURABLE LOG COPY of one
864
+ * `run_code` sub-dispatch outcome before the bridge appends its
865
+ * `tool/ptc-dispatch` event. `next()` keeps the
866
+ * content unchanged; a listener may return replacement blocks (e.g. the
867
+ * spill policy's preview + locator for an oversized text result). Only the
868
+ * logged copy is affected — the program already received the complete
869
+ * value, and the model sees neither. A throwing listener is contained:
870
+ * the bridge falls back to logging the original settled content.
871
+ * Scope-filtered dispatch (`@deepseek-ai/dsh-scope`): agent-scoped listeners receive only that agent's dispatches.
872
+ * @param dispatch - the parent execution, sub-call identity, and the settled content to log.
873
+ * @mode waterfall
874
+ */
875
+ 'tools/ptc-dispatch-log'(this: Scoped<ToolsService>, dispatch: PtcDispatchLog, next: () => Promise<ContentBlock[]>): Promise<ContentBlock[]>
876
+ ```
877
+
878
+ Types: [ContentBlock](./llm-streaming.md) · [Scoped](./scope.md)
879
+
880
+ Source: [`packages/core/tool-executor-ptc/src/runtime-types.ts`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/core/tool-executor-ptc/src/runtime-types.ts)
881
+
882
+ <a id="toolsresult--emit"></a>
883
+
884
+ #### `tools/result` — emit
885
+
886
+ Observe the frozen, lossless-JSON final outcome. Listener failures are contained. Scope-filtered dispatch (`@deepseek-ai/dsh-scope`): keyed by `exec.agent`.
887
+
888
+ ```ts cordis-catalog
889
+ /**
890
+ * Observe the frozen, lossless-JSON final outcome. Listener failures are contained.
891
+ * Scope-filtered dispatch (`@deepseek-ai/dsh-scope`): keyed by `exec.agent`.
892
+ * @param exec - the execution object that traversed the pipeline.
893
+ * @param result - a deep-frozen snapshot of the final returned result.
894
+ * @mode emit
895
+ */
896
+ 'tools/result'(this: Scoped<ToolsService>, exec: Readonly<ToolExecution>, result: Readonly<ToolExecutionResult>): undefined
897
+ ```
898
+
899
+ Types: [Scoped](./scope.md)
900
+
901
+ Source: [`packages/core/tools/src/events.ts`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/core/tools/src/events.ts)
902
+ <!-- END GENERATED cordis-surface -->