dsh-plugin-dev-kb 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (234) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +56 -0
  3. package/cordis.patch.yml +12 -0
  4. package/kb/INDEX.md +210 -0
  5. package/kb/README.md +69 -0
  6. package/kb/extra/AGENTS.md +75 -0
  7. package/kb/extra/api-gateway.md +164 -0
  8. package/kb/extra/api-gateway.zh.md +164 -0
  9. package/kb/extra/cookbook/adding-a-vendored-package.md +59 -0
  10. package/kb/extra/cookbook/adding-a-vendored-package.zh.md +59 -0
  11. package/kb/extra/cookbook/maintaining-dsh-code-review.md +64 -0
  12. package/kb/extra/cookbook/maintaining-dsh-code-review.zh.md +64 -0
  13. package/kb/extra/cookbook/responding-to-pr-review-on-a-stack.md +32 -0
  14. package/kb/extra/cookbook/responding-to-pr-review-on-a-stack.zh.md +32 -0
  15. package/kb/extra/defensive-patterns.md +33 -0
  16. package/kb/extra/defensive-patterns.zh.md +33 -0
  17. package/kb/extra/development.md +171 -0
  18. package/kb/extra/development.zh.md +171 -0
  19. package/kb/extra/event-producer-consumer.md +76 -0
  20. package/kb/extra/event-producer-consumer.zh.md +78 -0
  21. package/kb/extra/glossary.md +45 -0
  22. package/kb/extra/glossary.zh.md +45 -0
  23. package/kb/extra/graph-atlas.md +24 -0
  24. package/kb/extra/graph-atlas.zh.md +26 -0
  25. package/kb/extra/i18n/README.md +60 -0
  26. package/kb/extra/i18n/README.zh.md +60 -0
  27. package/kb/extra/i18n/style-samples.md +87 -0
  28. package/kb/extra/i18n/terminology.md +214 -0
  29. package/kb/extra/i18n/translation-prompt.md +263 -0
  30. package/kb/extra/i18n/translation-rules.md +69 -0
  31. package/kb/extra/i18n/translation-rules.zh.md +69 -0
  32. package/kb/extra/module-graph.md +1641 -0
  33. package/kb/extra/module-graph.zh.md +1643 -0
  34. package/kb/extra/postmortem/0001-acp-default-export-drops-inject.md +113 -0
  35. package/kb/extra/postmortem/0001-acp-default-export-drops-inject.zh.md +113 -0
  36. package/kb/extra/postmortem/0002-js-expression-disabled-filesystem-tools.md +47 -0
  37. package/kb/extra/postmortem/0002-js-expression-disabled-filesystem-tools.zh.md +47 -0
  38. package/kb/extra/postmortem/0003-web-agent-gui-feedback-loop.md +53 -0
  39. package/kb/extra/postmortem/0003-web-agent-gui-feedback-loop.zh.md +53 -0
  40. package/kb/extra/postmortem/0004-landlock-partial-notice-misclassified-child-failures.md +55 -0
  41. package/kb/extra/postmortem/0004-landlock-partial-notice-misclassified-child-failures.zh.md +55 -0
  42. package/kb/extra/postmortem/README.md +18 -0
  43. package/kb/extra/postmortem/README.zh.md +18 -0
  44. package/kb/extra/rescope.md +53 -0
  45. package/kb/extra/rescope.zh.md +53 -0
  46. package/kb/extra/subsystems/attachment.md +125 -0
  47. package/kb/extra/subsystems/attachment.zh.md +125 -0
  48. package/kb/extra/subsystems/extensions.md +364 -0
  49. package/kb/extra/subsystems/extensions.zh.md +364 -0
  50. package/kb/extra/subsystems/feedback.md +266 -0
  51. package/kb/extra/subsystems/feedback.zh.md +266 -0
  52. package/kb/extra/testing.md +49 -0
  53. package/kb/extra/testing.zh.md +49 -0
  54. package/kb/extra/web-styling.md +25 -0
  55. package/kb/extra/web-styling.zh.md +25 -0
  56. package/kb/meta/search-index.json +1328 -0
  57. package/kb/meta/site-pages.txt +168 -0
  58. package/kb/meta/source.json +13 -0
  59. package/kb/meta/topics.md +75 -0
  60. package/kb/site/develop/basic/config.md +108 -0
  61. package/kb/site/develop/basic/index.md +146 -0
  62. package/kb/site/develop/basic/publish.md +185 -0
  63. package/kb/site/develop/basic/tool.md +54 -0
  64. package/kb/site/develop/cordis-tutorial/01-first-plugin.md +95 -0
  65. package/kb/site/develop/cordis-tutorial/02-lifecycle-and-effects.md +98 -0
  66. package/kb/site/develop/cordis-tutorial/03-services.md +98 -0
  67. package/kb/site/develop/cordis-tutorial/04-events.md +144 -0
  68. package/kb/site/develop/cordis-tutorial/05-config.md +84 -0
  69. package/kb/site/develop/cordis-tutorial/06-composition-and-hmr.md +113 -0
  70. package/kb/site/develop/cordis-tutorial/07-into-the-harness.md +107 -0
  71. package/kb/site/develop/cordis-tutorial/index.md +62 -0
  72. package/kb/site/develop/framework/events.md +145 -0
  73. package/kb/site/develop/framework/index.md +139 -0
  74. package/kb/site/develop/framework/service.md +152 -0
  75. package/kb/site/develop/practice/index.md +157 -0
  76. package/kb/site/develop/practice/llm-adapter.md +190 -0
  77. package/kb/site/en/develop/basic/config.md +108 -0
  78. package/kb/site/en/develop/basic/index.md +146 -0
  79. package/kb/site/en/develop/basic/publish.md +185 -0
  80. package/kb/site/en/develop/basic/tool.md +54 -0
  81. package/kb/site/en/develop/cordis-tutorial/01-first-plugin.md +95 -0
  82. package/kb/site/en/develop/cordis-tutorial/02-lifecycle-and-effects.md +98 -0
  83. package/kb/site/en/develop/cordis-tutorial/03-services.md +98 -0
  84. package/kb/site/en/develop/cordis-tutorial/04-events.md +144 -0
  85. package/kb/site/en/develop/cordis-tutorial/05-config.md +84 -0
  86. package/kb/site/en/develop/cordis-tutorial/06-composition-and-hmr.md +113 -0
  87. package/kb/site/en/develop/cordis-tutorial/07-into-the-harness.md +107 -0
  88. package/kb/site/en/develop/cordis-tutorial/index.md +60 -0
  89. package/kb/site/en/develop/framework/events.md +145 -0
  90. package/kb/site/en/develop/framework/index.md +139 -0
  91. package/kb/site/en/develop/framework/service.md +150 -0
  92. package/kb/site/en/develop/practice/index.md +157 -0
  93. package/kb/site/en/develop/practice/llm-adapter.md +190 -0
  94. package/kb/site/en/guide/providers-custom-form.png +0 -0
  95. package/kb/site/en/guide/providers-models-page.png +0 -0
  96. package/kb/site/en/guide/providers.md +100 -0
  97. package/kb/site/en/guide/python-sdk.md +106 -0
  98. package/kb/site/en/guide/quickstart.md +32 -0
  99. package/kb/site/en/index.md +8 -0
  100. package/kb/site/en/reference/agent-lifecycle.md +86 -0
  101. package/kb/site/en/reference/capability-seams.md +475 -0
  102. package/kb/site/en/reference/config-catalog.md +3155 -0
  103. package/kb/site/en/reference/cookbook/adding-a-conversation-node.md +235 -0
  104. package/kb/site/en/reference/cookbook/adding-a-package.md +120 -0
  105. package/kb/site/en/reference/cookbook/adding-a-settings-card.md +102 -0
  106. package/kb/site/en/reference/cookbook/adding-a-tool.md +96 -0
  107. package/kb/site/en/reference/cookbook/adding-an-llm-adapter.md +45 -0
  108. package/kb/site/en/reference/cookbook/extension-cookbook.md +131 -0
  109. package/kb/site/en/reference/cordis-api/context.md +368 -0
  110. package/kb/site/en/reference/cordis-api/events.md +211 -0
  111. package/kb/site/en/reference/cordis-api/fiber.md +379 -0
  112. package/kb/site/en/reference/cordis-api/inherited.md +43 -0
  113. package/kb/site/en/reference/cordis-api/registry.md +156 -0
  114. package/kb/site/en/reference/cordis-api/service.md +106 -0
  115. package/kb/site/en/reference/cordis-primer.md +46 -0
  116. package/kb/site/en/reference/index.md +131 -0
  117. package/kb/site/en/reference/persistence-catalog.md +949 -0
  118. package/kb/site/en/reference/subsystems/approval.md +173 -0
  119. package/kb/site/en/reference/subsystems/client-modules.md +121 -0
  120. package/kb/site/en/reference/subsystems/code-runtime.md +194 -0
  121. package/kb/site/en/reference/subsystems/commands.md +190 -0
  122. package/kb/site/en/reference/subsystems/compaction.md +241 -0
  123. package/kb/site/en/reference/subsystems/core.md +1073 -0
  124. package/kb/site/en/reference/subsystems/credentials.md +136 -0
  125. package/kb/site/en/reference/subsystems/filesystem.md +498 -0
  126. package/kb/site/en/reference/subsystems/goal.md +280 -0
  127. package/kb/site/en/reference/subsystems/index.md +58 -0
  128. package/kb/site/en/reference/subsystems/invariants.md +91 -0
  129. package/kb/site/en/reference/subsystems/jobs.md +293 -0
  130. package/kb/site/en/reference/subsystems/llm-streaming.md +920 -0
  131. package/kb/site/en/reference/subsystems/lsp.md +205 -0
  132. package/kb/site/en/reference/subsystems/permission-presets.md +134 -0
  133. package/kb/site/en/reference/subsystems/persistence.md +388 -0
  134. package/kb/site/en/reference/subsystems/plan.md +90 -0
  135. package/kb/site/en/reference/subsystems/sandbox.md +221 -0
  136. package/kb/site/en/reference/subsystems/schedule.md +189 -0
  137. package/kb/site/en/reference/subsystems/scope.md +62 -0
  138. package/kb/site/en/reference/subsystems/session-projection.md +265 -0
  139. package/kb/site/en/reference/subsystems/session-query.md +498 -0
  140. package/kb/site/en/reference/subsystems/session-reference.md +111 -0
  141. package/kb/site/en/reference/subsystems/session-telemetry.md +197 -0
  142. package/kb/site/en/reference/subsystems/session-title.md +207 -0
  143. package/kb/site/en/reference/subsystems/session.md +852 -0
  144. package/kb/site/en/reference/subsystems/settings.md +313 -0
  145. package/kb/site/en/reference/subsystems/shell.md +306 -0
  146. package/kb/site/en/reference/subsystems/skills.md +334 -0
  147. package/kb/site/en/reference/subsystems/spill.md +120 -0
  148. package/kb/site/en/reference/subsystems/storage.md +232 -0
  149. package/kb/site/en/reference/subsystems/subagent.md +737 -0
  150. package/kb/site/en/reference/subsystems/subprocess.md +327 -0
  151. package/kb/site/en/reference/subsystems/system-prompt.md +210 -0
  152. package/kb/site/en/reference/subsystems/terminal.md +187 -0
  153. package/kb/site/en/reference/subsystems/token-meter.md +93 -0
  154. package/kb/site/en/reference/subsystems/tools.md +723 -0
  155. package/kb/site/en/reference/subsystems/typert.md +339 -0
  156. package/kb/site/en/reference/subsystems/user-questions.md +181 -0
  157. package/kb/site/en/reference/subsystems/web-server.md +111 -0
  158. package/kb/site/en/reference/subsystems/web.md +202 -0
  159. package/kb/site/en/reference/subsystems/workflow.md +281 -0
  160. package/kb/site/en/reference/subsystems/workspace.md +231 -0
  161. package/kb/site/en/reference/tool-catalog.md +1877 -0
  162. package/kb/site/en/reference/tool-execution-pipeline.md +66 -0
  163. package/kb/site/guide/providers-custom-form.zh.png +0 -0
  164. package/kb/site/guide/providers-models-page.zh.png +0 -0
  165. package/kb/site/guide/providers.md +100 -0
  166. package/kb/site/guide/python-sdk.md +106 -0
  167. package/kb/site/guide/quickstart.md +32 -0
  168. package/kb/site/index.md +8 -0
  169. package/kb/site/reference/agent-lifecycle.md +86 -0
  170. package/kb/site/reference/capability-seams.md +475 -0
  171. package/kb/site/reference/config-catalog.md +3154 -0
  172. package/kb/site/reference/cookbook/adding-a-conversation-node.md +235 -0
  173. package/kb/site/reference/cookbook/adding-a-package.md +120 -0
  174. package/kb/site/reference/cookbook/adding-a-settings-card.md +102 -0
  175. package/kb/site/reference/cookbook/adding-a-tool.md +98 -0
  176. package/kb/site/reference/cookbook/adding-an-llm-adapter.md +45 -0
  177. package/kb/site/reference/cookbook/extension-cookbook.md +133 -0
  178. package/kb/site/reference/cordis-api/context.md +368 -0
  179. package/kb/site/reference/cordis-api/events.md +211 -0
  180. package/kb/site/reference/cordis-api/fiber.md +379 -0
  181. package/kb/site/reference/cordis-api/inherited.md +43 -0
  182. package/kb/site/reference/cordis-api/registry.md +156 -0
  183. package/kb/site/reference/cordis-api/service.md +106 -0
  184. package/kb/site/reference/cordis-primer.md +52 -0
  185. package/kb/site/reference/index.md +135 -0
  186. package/kb/site/reference/persistence-catalog.md +949 -0
  187. package/kb/site/reference/subsystems/approval.md +173 -0
  188. package/kb/site/reference/subsystems/client-modules.md +121 -0
  189. package/kb/site/reference/subsystems/code-runtime.md +194 -0
  190. package/kb/site/reference/subsystems/commands.md +190 -0
  191. package/kb/site/reference/subsystems/compaction.md +241 -0
  192. package/kb/site/reference/subsystems/core.md +1081 -0
  193. package/kb/site/reference/subsystems/credentials.md +136 -0
  194. package/kb/site/reference/subsystems/filesystem.md +498 -0
  195. package/kb/site/reference/subsystems/goal.md +280 -0
  196. package/kb/site/reference/subsystems/index.md +58 -0
  197. package/kb/site/reference/subsystems/invariants.md +91 -0
  198. package/kb/site/reference/subsystems/jobs.md +293 -0
  199. package/kb/site/reference/subsystems/llm-streaming.md +926 -0
  200. package/kb/site/reference/subsystems/lsp.md +205 -0
  201. package/kb/site/reference/subsystems/permission-presets.md +134 -0
  202. package/kb/site/reference/subsystems/persistence.md +388 -0
  203. package/kb/site/reference/subsystems/plan.md +90 -0
  204. package/kb/site/reference/subsystems/sandbox.md +221 -0
  205. package/kb/site/reference/subsystems/schedule.md +189 -0
  206. package/kb/site/reference/subsystems/scope.md +62 -0
  207. package/kb/site/reference/subsystems/session-projection.md +265 -0
  208. package/kb/site/reference/subsystems/session-query.md +498 -0
  209. package/kb/site/reference/subsystems/session-reference.md +111 -0
  210. package/kb/site/reference/subsystems/session-telemetry.md +197 -0
  211. package/kb/site/reference/subsystems/session-title.md +207 -0
  212. package/kb/site/reference/subsystems/session.md +854 -0
  213. package/kb/site/reference/subsystems/settings.md +313 -0
  214. package/kb/site/reference/subsystems/shell.md +306 -0
  215. package/kb/site/reference/subsystems/skills.md +334 -0
  216. package/kb/site/reference/subsystems/spill.md +120 -0
  217. package/kb/site/reference/subsystems/storage.md +232 -0
  218. package/kb/site/reference/subsystems/subagent.md +739 -0
  219. package/kb/site/reference/subsystems/subprocess.md +327 -0
  220. package/kb/site/reference/subsystems/system-prompt.md +210 -0
  221. package/kb/site/reference/subsystems/terminal.md +187 -0
  222. package/kb/site/reference/subsystems/token-meter.md +93 -0
  223. package/kb/site/reference/subsystems/tools.md +723 -0
  224. package/kb/site/reference/subsystems/typert.md +339 -0
  225. package/kb/site/reference/subsystems/user-questions.md +181 -0
  226. package/kb/site/reference/subsystems/web-server.md +111 -0
  227. package/kb/site/reference/subsystems/web.md +202 -0
  228. package/kb/site/reference/subsystems/workflow.md +281 -0
  229. package/kb/site/reference/subsystems/workspace.md +231 -0
  230. package/kb/site/reference/tool-catalog.md +1880 -0
  231. package/kb/site/reference/tool-execution-pipeline.md +66 -0
  232. package/package.json +40 -0
  233. package/scripts/rebuild-index.mjs +88 -0
  234. package/skills/dsh-plugin-dev-kb.md +66 -0
@@ -0,0 +1,202 @@
1
+ ---
2
+ editSource: "docs/subsystems/web.md"
3
+ outline: [2,3]
4
+ ---
5
+
6
+ # Web Access
7
+
8
+ The web access seam — a [capability seam](https://github.com/deepseek-ai/deepseek-harness/blob/master/.agents/notes/implemented/architecture/2026-06-24-web-capability-seam.md) that spans **two operations** (search and fetch) on one `ctx.web` service, split across packages: Service Definition ([dsh-web](https://github.com/deepseek-ai/deepseek-harness/tree/master/packages/web/web), `ctx.web` + the provider registries), Service Providers ([dsh-web-search-exa](https://github.com/deepseek-ai/deepseek-harness/tree/master/packages/web/web-search-exa), [dsh-web-search-perplexity](https://github.com/deepseek-ai/deepseek-harness/tree/master/packages/web/web-search-perplexity), [dsh-web-search-deepseek](https://github.com/deepseek-ai/deepseek-harness/tree/master/packages/web/web-search-deepseek), [dsh-web-fetch-http](https://github.com/deepseek-ai/deepseek-harness/tree/master/packages/web/web-fetch-http)), and Consumer ([dsh-tool-web](https://github.com/deepseek-ai/deepseek-harness/tree/master/packages/web/tool-web), the `web_search`/`web_fetch` tool schemas). Web is **one optional capability**, not part of the agent-loop spine — so its vocabulary lives here, not in [core.md](./core.md). A search-provider swap does not change how the model asks for a query, and a fetch-provider swap does not change how the model asks for a URL.
9
+
10
+ Source: [`packages/web/web/src/types.ts`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/web/web/src/types.ts)
11
+
12
+ ## Why one capability has two operations
13
+
14
+ Search and fetch share no request schema and no business logic, but they are deliberately one `ctx.web` middle layer: one provider-selection policy owner, one abort/error vocabulary, and one product-facing "how this harness reaches the web" configuration API. The cost is the parallel `searchX`/`fetchX` method pairs on the service; that parallelism is intentional, not a missed extraction. Providers register **capabilities** (a `WebSearchProvider` or `WebFetchProvider`), not tools; the model-facing names, schemas, prompt guidance, and presentation all live in the single `dsh-tool-web` consumer.
15
+
16
+ ## Search request and result
17
+
18
+ The model-facing tool argument is just a `query`; `maxResults` is a consumer-owned bound (`dsh-tool-web`'s `searchMaxResults` config, default `8`) passed through the seam and enforced on the way back — if a provider over-returns, the seam truncates `sources[]` and sets `truncated`.
19
+
20
+ ```ts type-equiv
21
+ /**
22
+ * What one search-capable backend can return. The model-facing argument is just
23
+ * a query; `maxResults` is a `dsh-tool-web`-layer bound passed through unchanged
24
+ * and enforced on the way back by the seam (see {@link WebSearchResult}).
25
+ */
26
+ interface WebSearchRequest {
27
+ readonly query: string
28
+ /**
29
+ * Upper bound on returned sources; the seam truncates to it. Omitted = no
30
+ * bound. `dsh-tool-web` always sets it. A provider whose API supports a
31
+ * result-count control (Exa's `numResults`) should apply it at the request
32
+ * layer as a cost/latency optimization; the seam enforces the bound
33
+ * regardless.
34
+ */
35
+ readonly maxResults?: number
36
+ }
37
+ ```
38
+
39
+ ```ts type-equiv
40
+ /**
41
+ * Normalized search outcome. `content` is optional provider-generated answer
42
+ * text or summary (Exa and DeepSeek return none; Perplexity returns a
43
+ * generated answer).
44
+ * `sources[]` is the portable citation shape. `truncated` is set by the seam
45
+ * when it cut `sources[]` down to `maxResults`.
46
+ */
47
+ interface WebSearchResult {
48
+ /** Optional provider-generated answer text, search context, or summary. */
49
+ readonly content?: string
50
+ /** Citeable sources, already truncated to the request's `maxResults`. */
51
+ readonly sources: readonly WebSearchSource[]
52
+ /** True when the seam dropped sources to honor `maxResults`. */
53
+ readonly truncated: boolean
54
+ }
55
+ ```
56
+
57
+ ```ts type-equiv
58
+ /**
59
+ * One citeable source. A source always has a URL; `title`, `snippet`, and
60
+ * `publishedAt` are optional because not every provider returns them — forcing
61
+ * adapters to invent them would make the seam lie (Perplexity citations may be
62
+ * URL-only). `dsh-tool-web` renders `title ?? hostname(url)` for display.
63
+ */
64
+ interface WebSearchSource {
65
+ readonly url: string
66
+ readonly title?: string
67
+ readonly snippet?: string
68
+ /** Publication/crawl timestamp as a provider-supplied ISO-8601 string. */
69
+ readonly publishedAt?: string
70
+ }
71
+ ```
72
+
73
+ ## Fetch request and result
74
+
75
+ ```ts type-equiv
76
+ /**
77
+ * What one fetch-capable backend is asked to retrieve. The request deliberately
78
+ * omits timeout, format, prompt, and extraction controls: cancellation is a
79
+ * direct execution argument, while presentation and higher-level LLM concerns
80
+ * belong outside safe retrieval.
81
+ */
82
+ interface WebFetchRequest {
83
+ readonly url: string
84
+ }
85
+ ```
86
+
87
+ HTTP status is part of the fetched resource state, not automatically a failure: a successful network fetch of a `404`/`500` returns a `WebFetchResult` with the status code and a bounded decoded body. `url` is the final URL after allowed redirects. `WebError` is reserved for failures to safely retrieve or represent the resource.
88
+
89
+ ```ts type-equiv
90
+ /**
91
+ * Normalized fetch outcome. A successful network fetch of a non-2xx response is
92
+ * a result, not an error: the status code is part of the fetched resource
93
+ * state. {@link WebError} is reserved for failures to safely retrieve or
94
+ * represent the resource.
95
+ */
96
+ interface WebFetchResult {
97
+ /** The final URL after allowed redirects (the request URL is in the request). */
98
+ readonly url: string
99
+ /** HTTP status code of the fetched response. */
100
+ readonly statusCode: number
101
+ /** Decoded body, classified by content kind. */
102
+ readonly body: WebFetchBody
103
+ /** True when the provider capped the decoded body. */
104
+ readonly truncated: boolean
105
+ }
106
+ ```
107
+
108
+ ```ts type-equiv
109
+ /**
110
+ * The decoded body of a fetched resource. A CLOSED discriminated union owned by
111
+ * `dsh-web`: the provider decodes the kind and `dsh-tool-web` renders it, so a
112
+ * new kind is a coordinated change across known packages, not a plugin
113
+ * extension. Consumers `switch` on `kind` ending in `default: assertNever(...)`
114
+ * so adding a kind breaks compilation at every consumer until handled. Each arm
115
+ * stays its own object literal even where fields coincide, so an arm can gain
116
+ * fields the others lack.
117
+ */
118
+ type WebFetchBody =
119
+ | { readonly kind: 'html'; readonly content: string }
120
+ | { readonly kind: 'text'; readonly content: string }
121
+ ```
122
+
123
+ ## Provider availability
124
+
125
+ A provider's `available(): boolean` is a cheap LOCAL check (credential presence, parseable config) and **must not make network calls**. It is an input to execution-time selection, not a health system: `search()`/`fetch()` read it to pick a usable provider, and a selection failure surfaces as the structured `WebError` the caller routes on — which carries the branchable detail (the missing id or ambiguous candidate set) in its code and message.
126
+
127
+ Selection never depends on registration, config, or HMR order: a capability has an explicit provider id (config `searchProvider`/`fetchProvider`, or the matching env var feeding the same field), or auto-selects when exactly one usable provider is registered; multiple usable providers with no configured id is `WEB_PROVIDER_AMBIGUOUS`, not first-wins.
128
+
129
+ ## Errors
130
+
131
+ `WebError extends HarnessError` ([core.md](./core.md) error taxonomy) with a `code: string` (open, like every other seam's error — `LlmError`, `SubagentError`), not a closed union: a provider may raise its own codes without editing `dsh-web`, and consumers must tolerate an unknown code. The codes split by owner. Seam-neutral codes are raised by the shared `WebRuntime` contract: `WEB_PROVIDER_UNAVAILABLE`, `WEB_PROVIDER_CONFIGURED_MISSING`, `WEB_PROVIDER_CONFIGURED_UNAVAILABLE`, `WEB_PROVIDER_AMBIGUOUS`, `WEB_DUPLICATE_PROVIDER` (a registration-time programming error, the analogue of `LlmRuntime`'s `DUPLICATE_ADAPTER`), `WEB_ABORTED`, and `WEB_PROVIDER_ERROR` (the catch-all for a provider's own failure surfaced through the seam, including network/transport failure — DNS, connection refused, TLS). Fetch-transport codes are owned by the `dsh-web-fetch-http` implementation and a different fetch backend need not raise them: `WEB_INVALID_URL`, `WEB_BLOCKED_URL`, `WEB_REDIRECT_BLOCKED`, `WEB_FETCH_TOO_LARGE`, `WEB_FETCH_TIMEOUT`, `WEB_UNSUPPORTED_CONTENT_TYPE`.
132
+
133
+ ## The service
134
+
135
+ `WebRuntime` registers search and fetch providers, rejects duplicate ids with `WEB_DUPLICATE_PROVIDER`, and resolves providers at execution time with structured selection errors. The local fetch backend accepts only HTTP(S), rejects credentials, caps redirects, bytes, characters, and time, revalidates every same-origin redirect hop, and decodes the body; the tool owns presentation. The local backend does not block private-network targets; do not enable `web_fetch` where it can reach sensitive internal ones.
136
+
137
+ <!-- BEGIN GENERATED cordis-surface (gen-cordis-catalog.ts) — do not edit between markers -->
138
+
139
+ <a id="cordis-surface"></a>
140
+
141
+ ## Cordis API
142
+
143
+ Generated from source by `scripts/gen-cordis-catalog.ts` (verified fresh by `pnpm run verify-cordis-catalog` in doc-sync; regenerate with `pnpm run gen-cordis-catalog`) — this section is byte-identical in both language sides of the page. Signature blocks use a `ts cordis-catalog` fence and keep the original source JSDoc; dispatch modes are defined in the [primer](../cordis-primer.md#dispatch-modes), and the framework-inherited `ctx` API lives in [cordis-api/inherited.md](../cordis-api/inherited.md).
144
+
145
+ <a id="ctxweb--webruntime"></a>
146
+
147
+ ### `ctx.web` — `WebRuntime`
148
+
149
+ The web access service. Registered as `ctx.web` (one instance per context).
150
+
151
+ Selection semantics (resolved at execution time, never order-dependent):
152
+
153
+ - A configured id that is registered and `available()` → that provider.
154
+ - A configured id not registered → `WEB_PROVIDER_CONFIGURED_MISSING`.
155
+ - A configured id registered but unavailable → `WEB_PROVIDER_CONFIGURED_UNAVAILABLE`.
156
+ - No id configured, exactly one registered usable provider → that provider.
157
+ - No id configured, multiple usable providers → `WEB_PROVIDER_AMBIGUOUS`.
158
+ - No id configured, no usable provider → `WEB_PROVIDER_UNAVAILABLE`.
159
+
160
+ ```ts cordis-catalog
161
+ /**
162
+ * Register a search provider. Throws {@link WebError} `WEB_DUPLICATE_PROVIDER`
163
+ * if its id is already registered for search. Returns a disposer; disposed
164
+ * with the calling fiber.
165
+ * @param provider - the provider; its `id` is the registry key.
166
+ * @returns the disposer that unregisters the provider.
167
+ */
168
+ registerSearchProvider(provider: WebSearchProvider): () => void
169
+
170
+ /**
171
+ * Register a fetch provider. Throws {@link WebError} `WEB_DUPLICATE_PROVIDER`
172
+ * if its id is already registered for fetch. Returns a disposer; disposed
173
+ * with the calling fiber.
174
+ * @param provider - the provider; its `id` is the registry key.
175
+ * @returns the disposer that unregisters the provider.
176
+ */
177
+ registerFetchProvider(provider: WebFetchProvider): () => void
178
+
179
+ /**
180
+ * Run one search through the selected provider. Resolves the provider at call
181
+ * time with the selection rules above; throws {@link WebError} when the
182
+ * capability cannot run. The seam enforces `request.maxResults` on the result:
183
+ * if the provider over-returns, `sources[]` is truncated and `truncated` set.
184
+ * @param request - the query and optional result limit.
185
+ * @param signal - optional cancellation signal forwarded to the provider.
186
+ * @returns the provider's results, capped to `request.maxResults`.
187
+ */
188
+ async search(request: WebSearchRequest, signal?: AbortSignal): Promise<WebSearchResult>
189
+
190
+ /**
191
+ * Retrieve one URL through the selected provider. Resolves the provider at
192
+ * call time with the selection rules above; throws {@link WebError} when the
193
+ * capability cannot run. A non-2xx response is a result, not a throw.
194
+ * @param request - the URL plus retrieval options.
195
+ * @param signal - optional cancellation signal forwarded to the provider.
196
+ * @returns the retrieval outcome; non-2xx responses resolve descriptively.
197
+ */
198
+ async fetch(request: WebFetchRequest, signal?: AbortSignal): Promise<WebFetchResult>
199
+ ```
200
+
201
+ Source: [`packages/web/web/src/index.ts:74`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/web/web/src/index.ts)
202
+ <!-- END GENERATED cordis-surface -->
@@ -0,0 +1,281 @@
1
+ ---
2
+ editSource: "docs/subsystems/workflow.md"
3
+ outline: [2,3]
4
+ ---
5
+
6
+ # Workflow
7
+
8
+ The workflow seam lets an agent run a model-written orchestration SCRIPT that starts subagents. Like [subagent](./subagent.md) it is **one optional capability**, not part of the agent loop, so its types and operations live here rather than in [core.md](./core.md). Like bash, it permits ONE engine implementation per context to provide `ctx.workflowEngine`; there is no named-provider registry (a second engine replaces the first through plugin configuration rather than running beside it).
9
+
10
+ Service Definition: [dsh-workflow](https://github.com/deepseek-ai/deepseek-harness/tree/master/packages/workflow/workflow) (`ctx.workflowEngine` + the vocabulary below). The Service Provider is [dsh-workflow-worker-thread](https://github.com/deepseek-ai/deepseek-harness/tree/master/packages/workflow/workflow-worker-thread) (a `node:worker_threads` engine — one worker per run, the script's vm context inside it); the model-facing Consumer is [dsh-tool-workflow](https://github.com/deepseek-ai/deepseek-harness/tree/master/packages/workflow/tool-workflow). The proposal and rationale: [the dynamic-workflows Agent Note](https://github.com/deepseek-ai/deepseek-harness/blob/master/.agents/notes/implemented/feature/2026-07-05-dynamic-workflows.md).
11
+
12
+ Sources: browser-safe vocabulary in [`packages/workflow/workflow/src/types.ts`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/workflow/workflow/src/types.ts), Host request and live-run handles in [`runtime-types.ts`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/workflow/workflow/src/runtime-types.ts).
13
+
14
+ ## The start request
15
+
16
+ What a caller asks for when starting a run. The ordinary workflow tool builds this from the model's `{ script, meta, args }` call plus the calling agent; specialized consumers may also select one engine-wide `subagentProvider` and lower `maxTotalAgents` for the run, but the script cannot observe or replace either policy. `meta` and `args` are plain JSON DATA (the engine validates `meta` against its schema and rejects loud BEFORE anything runs — no script text is ever evaluated to obtain it). `parent` is REQUIRED — every child the script starts is attributed to it, and cwd, lineage, and depth pass through the [subagent seam](./subagent.md).
17
+
18
+ ```ts type-equiv
19
+ /**
20
+ * What a caller asks for when starting a workflow run. `meta` and `args` are
21
+ * plain JSON data by the seam contract. `parent` is required because every
22
+ * `agent()` spawned by the script is attributed to that live Agent.
23
+ */
24
+ interface WorkflowStartRequest {
25
+ /** The plain-JS script body (top-level await allowed; ends with `return <json-value>`). */
26
+ script: string
27
+ /** The workflow's identity block, as plain JSON data (shape-validated by the engine). */
28
+ meta: WorkflowMeta
29
+ /** Optional input exposed verbatim to the script as the `args` global. */
30
+ args?: unknown
31
+ /** Optional engine-wide child-provider override for this run. */
32
+ subagentProvider?: string
33
+ /** Optional per-run total-child ceiling. */
34
+ maxTotalAgents?: number
35
+ /** The agent on whose behalf the run executes (parent of every child). */
36
+ parent: Agent
37
+ /** Cancels the run when aborted. */
38
+ signal?: AbortSignal
39
+ }
40
+ ```
41
+
42
+ ## The workflow's identity: `WorkflowMeta`
43
+
44
+ The identity block carried as data on the start request (the tool's `meta` parameter; the field vocabulary matches the Claude Code dynamic-workflows meta block). `phases` is progress vocabulary only: `phase()` calls match titles for observers; no execution structure is implied.
45
+
46
+ ```ts type-equiv
47
+ /**
48
+ * The script's identity block, provided as plain JSON data alongside the
49
+ * script body (the model-facing tool carries it as its `meta` parameter) and
50
+ * validated by the engine before the body runs. `name`/`description` are
51
+ * required; the rest is optional annotation. The field vocabulary matches the
52
+ * Claude Code dynamic-workflows meta block.
53
+ */
54
+ interface WorkflowMeta {
55
+ /** Short kebab-case workflow name (display + persistence key). */
56
+ name: string
57
+ /** One-line description of what the workflow does. */
58
+ description: string
59
+ /** Optional guidance on when this workflow applies (shown in listings). */
60
+ whenToUse?: string
61
+ /** Optional phase declarations matched by `phase()` calls. */
62
+ phases?: WorkflowPhase[]
63
+ }
64
+ ```
65
+
66
+ ## The terminal result: `WorkflowResult`
67
+
68
+ The outcome of one run, resolved by `WorkflowRun.result`. `value` is the script's materialized return value — plain host-realm JSON data (`null` when the script returned nothing) — meaningful only for `completed`. `stopReason` is a CLOSED union (engine-owned; consumers may exhaust it): `completed` | `cancelled` | `error`. A non-`completed` reason carries the failure in `error`, and the consumer maps it to an `isError` tool result rather than reporting partial output as success.
69
+
70
+ ```ts type-equiv
71
+ /**
72
+ * The outcome resolved by a live workflow run. `value` is
73
+ * the script's materialized return value (plain host-realm JSON data; `null`
74
+ * when the script returned `undefined`) — meaningful only for `completed`.
75
+ * A non-`completed` reason carries the failure in `error`; the consumer maps
76
+ * it to an `isError` tool result rather than reporting partial output.
77
+ */
78
+ interface WorkflowResult {
79
+ /** The script's return value (host JSON data; `null` for no return). */
80
+ value: unknown
81
+ /** Why the run settled. */
82
+ stopReason: WorkflowStopReason
83
+ /** The failure message (present iff `stopReason` is not `completed`). */
84
+ error?: string
85
+ /**
86
+ * How many `agent()` calls the run accepted over its whole lifetime. On a
87
+ * graceful settlement this is the script-side count (calls still queued for
88
+ * a concurrency slot included); on a termination path (grace force-settle,
89
+ * worker death) it degrades to the host-observed count — calls queued
90
+ * inside a terminated script are unknowable then.
91
+ */
92
+ agentsStarted: number
93
+ }
94
+ ```
95
+
96
+ ## A live run: `WorkflowRun`
97
+
98
+ The handle the consumer holds while a script executes. The consumer awaits `result`, may `cancel` mid-flight, and MUST `dispose` on every path. `result` does NOT reject — a script failure resolves with `stopReason: 'error'` — and once the run is cancelled it SETTLES within the engine's bounded grace even if the script itself never settles (the engine force-settles `cancelled`; the worker-thread engine then terminates the script's worker), so a consumer awaiting `result` is never wedged past a cancellation. `dispose()` = cancel + that bounded settle + child quiescence; it never hangs on a stuck script.
99
+
100
+ ```ts type-equiv
101
+ /**
102
+ * Holder-owned live workflow. `result` never rejects; consumers may cancel
103
+ * and must call idempotent `dispose()` to await script and child quiescence.
104
+ */
105
+ interface WorkflowRun {
106
+ readonly id: WorkflowRunId
107
+ /** The validated meta block available before the script body runs. */
108
+ readonly meta: WorkflowMeta
109
+ readonly result: Promise<WorkflowResult>
110
+ /** Cancel the run and its children. */
111
+ cancel(reason?: string): void
112
+ /** Cancel if needed and await bounded settlement and cleanup. */
113
+ dispose(): Promise<void>
114
+ }
115
+ ```
116
+
117
+ ## Failure discipline: `WorkflowError.fatal`
118
+
119
+ Hook misuse inside a script — bad arguments, unknown/deferred `agent()` options, a schema outside the [structured-output subset](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/core/tools/README.md), a tripped cap, a seam start failure, cancellation — throws a `WorkflowError` with `fatal: true`. The `parallel()`/`pipeline()` combinators RE-THROW fatal errors instead of mapping the item to `null`: a typo'd option must kill the script loudly, never dissolve into something that reads as an ordinary child failure. The per-item `null` is reserved for child-run failures (a non-`completed` stop reason) and ordinary in-stage script errors.
120
+
121
+ ## Events
122
+
123
+ The `workflow/*` events (`workflow/start`, `workflow/phase`, `workflow/log`, `workflow/agent-start`, `workflow/agent-end`, `workflow/end` — see the [events catalog](#cordis-surface)) are **observe-only** emits carrying DATA SNAPSHOTS: every payload starts with `WorkflowRunInfo` (id + meta), never the live `WorkflowRun`, so a subscriber cannot gain `cancel`/`dispose`, and `workflow/end` deliberately omits the result value (a listener observing outcomes must not receive a mutable alias of the caller's result). Every emit is per-listener contained — a throwing subscriber is logged, never propagated, and cannot starve the listeners registered after it — and every listener receives its own payload clone, so mutating it corrupts neither the engine nor other listeners; the containment mirrors `subagent/start`/`subagent/end`.
124
+
125
+ ## Durable Chat records
126
+
127
+ The top-level `dsh-tool-workflow` consumer projects display facts into its calling parent Session without changing execution ownership. It writes `tool-workflow/run-start` after a run is accepted, pairs member start and end by `runId + seq`, and writes `tool-workflow/run-end` only after the result is known and disposal reaches quiescence. Nested transport calls write no record. The first append failure disables later writes for that run, so the log remains empty or a legal continuous prefix and the tool result is unchanged.
128
+
129
+ `dsh-tool-workflow/invariant` validates the same protocol before live commit and when a Session is loaded: one start per run, positive unique member sequences, paired member endings, no run ending with open members, and no updates after the run ending. A missing member ending or run ending at the log tail is valid interruption evidence rather than corruption.
130
+
131
+ `dsh-client-ui-workflow-run` folds the four events through the Conversation Node engine into one `workflow-run` Chat node anchored at the run-start sequence, after the original workflow tool node. Phase groups come only from actual member starts and preserve exact strings, including the distinction between an omitted phase and `''`. Closed Locations turn missing terminal facts into interrupted presentation. The [UI package README](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/client/ui-workflow-run/README.md) owns disclosure, status, and same-parent local navigation behavior.
132
+
133
+ <!-- BEGIN GENERATED cordis-surface (gen-cordis-catalog.ts) — do not edit between markers -->
134
+
135
+ <a id="cordis-surface"></a>
136
+
137
+ ## Cordis API
138
+
139
+ Generated from source by `scripts/gen-cordis-catalog.ts` (verified fresh by `pnpm run verify-cordis-catalog` in doc-sync; regenerate with `pnpm run gen-cordis-catalog`) — this section is byte-identical in both language sides of the page. Signature blocks use a `ts cordis-catalog` fence and keep the original source JSDoc; dispatch modes are defined in the [primer](../cordis-primer.md#dispatch-modes), and the framework-inherited `ctx` API lives in [cordis-api/inherited.md](../cordis-api/inherited.md).
140
+
141
+ <a id="ctxworkflowengine--workflowengine-abstract-seam"></a>
142
+
143
+ ### `ctx.workflowEngine` — `WorkflowEngine` (abstract seam)
144
+
145
+ Workflow Service Definition contract. Invalid requests throw before publication; a live run is holder-owned, its result never rejects, cancellation and disposal are bounded, and disposal waits for child cleanup within that bound. Lifecycle listener failures are contained, and `workflow/end` fires exactly once as the result settles.
146
+
147
+ ```ts cordis-catalog
148
+ /**
149
+ * Parse and execute a workflow script.
150
+ * @param request - the script, its `args`, the parent agent, and an
151
+ * optional cancel signal.
152
+ * @returns the live run; its `result` resolves when the script settles.
153
+ */
154
+ abstract start(request: WorkflowStartRequest): WorkflowRun
155
+ ```
156
+
157
+ Source: [`packages/workflow/workflow/src/index.ts:157`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/workflow/workflow/src/index.ts)
158
+
159
+ <a id="workflow-events"></a>
160
+
161
+ ### `workflow/*` events
162
+
163
+ <a id="workflowagent-end--emit"></a>
164
+
165
+ #### `workflow/agent-end` — emit
166
+
167
+ One `agent()` call settled (clean result, child failure, or run cancellation). Paired with Events['workflow/agent-start'] by `agent.seq`, exactly once per started call on every stop path — on an engine termination path (a worker killed past its grace) the end is engine-synthesized with outcome `'cancelled'`.
168
+
169
+ ```ts cordis-catalog
170
+ /**
171
+ * One `agent()` call settled (clean result, child failure, or run
172
+ * cancellation). Paired with {@link Events['workflow/agent-start']} by
173
+ * `agent.seq`, exactly once per started call on every stop path — on an
174
+ * engine termination path (a worker killed past its grace) the end is
175
+ * engine-synthesized with outcome `'cancelled'`.
176
+ * @param info - the run's identity snapshot.
177
+ * @param agent - the call identity plus its outcome.
178
+ * @mode emit
179
+ */
180
+ 'workflow/agent-end'(info: WorkflowRunInfo, agent: WorkflowAgentEndInfo): void
181
+ ```
182
+
183
+ Source: [`packages/workflow/workflow/src/index.ts:79`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/workflow/workflow/src/index.ts)
184
+
185
+ <a id="workflowagent-start--emit"></a>
186
+
187
+ #### `workflow/agent-start` — emit
188
+
189
+ One `agent()` call established a published child run. Paired with Events['workflow/agent-end'] by `agent.seq`. A call that never receives a published run from the provider emits neither event in this pair.
190
+
191
+ ```ts cordis-catalog
192
+ /**
193
+ * One `agent()` call established a published child run. Paired with
194
+ * {@link Events['workflow/agent-end']} by `agent.seq`. A call that never
195
+ * receives a published run from the provider emits neither
196
+ * event in this pair.
197
+ * @param info - the run's identity snapshot.
198
+ * @param agent - the call's sequence number, label, phase, and child id.
199
+ * @mode emit
200
+ */
201
+ 'workflow/agent-start'(info: WorkflowRunInfo, agent: WorkflowAgentInfo): void
202
+ ```
203
+
204
+ Source: [`packages/workflow/workflow/src/index.ts:68`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/workflow/workflow/src/index.ts)
205
+
206
+ <a id="workflowend--emit"></a>
207
+
208
+ #### `workflow/end` — emit
209
+
210
+ A workflow run settled (any stop reason). Fired when WorkflowRun.result resolves. Paired with Events['workflow/start'].
211
+
212
+ ```ts cordis-catalog
213
+ /**
214
+ * A workflow run settled (any stop reason). Fired when
215
+ * {@link WorkflowRun.result} resolves. Paired with
216
+ * {@link Events['workflow/start']}.
217
+ * @param info - the run's identity snapshot.
218
+ * @param result - the outcome data (stop reason, error, agent count) —
219
+ * deliberately WITHOUT the result value (see {@link WorkflowResultInfo}).
220
+ * @mode emit
221
+ */
222
+ 'workflow/end'(info: WorkflowRunInfo, result: WorkflowResultInfo): void
223
+ ```
224
+
225
+ Source: [`packages/workflow/workflow/src/index.ts:89`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/workflow/workflow/src/index.ts)
226
+
227
+ <a id="workflowlog--emit"></a>
228
+
229
+ #### `workflow/log` — emit
230
+
231
+ The script emitted a narration line (a `log(message)` call).
232
+
233
+ ```ts cordis-catalog
234
+ /**
235
+ * The script emitted a narration line (a `log(message)` call).
236
+ * @param info - the run's identity snapshot.
237
+ * @param message - the logged message, verbatim.
238
+ * @mode emit
239
+ */
240
+ 'workflow/log'(info: WorkflowRunInfo, message: string): void
241
+ ```
242
+
243
+ Source: [`packages/workflow/workflow/src/index.ts:58`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/workflow/workflow/src/index.ts)
244
+
245
+ <a id="workflowphase--emit"></a>
246
+
247
+ #### `workflow/phase` — emit
248
+
249
+ The script entered a phase (a `phase(title)` call) — progress grouping for observers; no execution semantics.
250
+
251
+ ```ts cordis-catalog
252
+ /**
253
+ * The script entered a phase (a `phase(title)` call) — progress grouping
254
+ * for observers; no execution semantics.
255
+ * @param info - the run's identity snapshot.
256
+ * @param title - the phase title, verbatim.
257
+ * @mode emit
258
+ */
259
+ 'workflow/phase'(info: WorkflowRunInfo, title: string): void
260
+ ```
261
+
262
+ Source: [`packages/workflow/workflow/src/index.ts:51`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/workflow/workflow/src/index.ts)
263
+
264
+ <a id="workflowstart--emit"></a>
265
+
266
+ #### `workflow/start` — emit
267
+
268
+ A workflow run started — the script's meta block validated, the body about to execute. Paired with Events['workflow/end'].
269
+
270
+ ```ts cordis-catalog
271
+ /**
272
+ * A workflow run started — the script's meta block validated, the body
273
+ * about to execute. Paired with {@link Events['workflow/end']}.
274
+ * @param info - the run's identity snapshot (id + meta).
275
+ * @mode emit
276
+ */
277
+ 'workflow/start'(info: WorkflowRunInfo): void
278
+ ```
279
+
280
+ Source: [`packages/workflow/workflow/src/index.ts:43`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/workflow/workflow/src/index.ts)
281
+ <!-- END GENERATED cordis-surface -->