dsh-plugin-dev-kb 1.0.7 → 1.0.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (169) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/README.en.md +144 -0
  3. package/README.md +21 -7
  4. package/kb/INDEX.md +19 -5
  5. package/kb/README.md +11 -10
  6. package/kb/extra/AGENTS.md +4 -4
  7. package/kb/extra/cookbook/adding-a-vendored-package.md +2 -2
  8. package/kb/extra/cookbook/adding-a-vendored-package.zh.md +2 -2
  9. package/kb/extra/deepseek-llm-api-wire-extensions.md +159 -0
  10. package/kb/extra/deepseek-llm-api-wire-extensions.zh.md +159 -0
  11. package/kb/extra/development.md +8 -14
  12. package/kb/extra/development.zh.md +8 -14
  13. package/kb/extra/event-producer-consumer.md +47 -41
  14. package/kb/extra/event-producer-consumer.zh.md +47 -41
  15. package/kb/extra/glossary.md +1 -1
  16. package/kb/extra/glossary.zh.md +1 -1
  17. package/kb/extra/graph-atlas.md +0 -2
  18. package/kb/extra/graph-atlas.zh.md +0 -2
  19. package/kb/extra/i18n/README.md +4 -4
  20. package/kb/extra/i18n/README.zh.md +4 -4
  21. package/kb/extra/module-graph.md +680 -413
  22. package/kb/extra/module-graph.zh.md +681 -414
  23. package/kb/extra/postmortem/0001-acp-default-export-drops-inject.md +2 -2
  24. package/kb/extra/postmortem/0001-acp-default-export-drops-inject.zh.md +2 -2
  25. package/kb/extra/postmortem/0002-js-expression-disabled-filesystem-tools.md +2 -2
  26. package/kb/extra/postmortem/0002-js-expression-disabled-filesystem-tools.zh.md +2 -2
  27. package/kb/extra/postmortem/0003-web-agent-gui-feedback-loop.md +2 -2
  28. package/kb/extra/postmortem/0003-web-agent-gui-feedback-loop.zh.md +2 -2
  29. package/kb/extra/postmortem/0004-landlock-partial-notice-misclassified-child-failures.md +1 -1
  30. package/kb/extra/postmortem/0004-landlock-partial-notice-misclassified-child-failures.zh.md +1 -1
  31. package/kb/extra/rescope.md +2 -2
  32. package/kb/extra/rescope.zh.md +2 -2
  33. package/kb/extra/subsystems/agent-team.md +24 -1
  34. package/kb/extra/subsystems/agent-team.zh.md +24 -1
  35. package/kb/extra/subsystems/attachment.md +12 -4
  36. package/kb/extra/subsystems/attachment.zh.md +12 -4
  37. package/kb/extra/subsystems/extensions.md +18 -0
  38. package/kb/extra/subsystems/extensions.zh.md +18 -0
  39. package/kb/extra/subsystems/feedback.md +2 -2
  40. package/kb/extra/subsystems/feedback.zh.md +2 -2
  41. package/kb/extra/subsystems/todo.md +32 -0
  42. package/kb/extra/subsystems/todo.zh.md +32 -0
  43. package/kb/extra/subsystems/webhook.md +70 -0
  44. package/kb/extra/subsystems/webhook.zh.md +70 -0
  45. package/kb/extra/testing.md +11 -10
  46. package/kb/extra/testing.zh.md +8 -7
  47. package/kb/meta/search-index.json +269 -161
  48. package/kb/meta/site-pages.txt +182 -168
  49. package/kb/meta/source.json +5 -5
  50. package/kb/meta/topics.md +14 -6
  51. package/kb/site/develop/basic/publish.md +2 -2
  52. package/kb/site/develop/basic/tool.md +1 -1
  53. package/kb/site/develop/cordis-tutorial/07-into-the-harness.md +4 -4
  54. package/kb/site/develop/framework/events.md +1 -1
  55. package/kb/site/develop/practice/dynamic-cordis.md +17 -0
  56. package/kb/site/develop/practice/llm-adapter.md +3 -3
  57. package/kb/site/en/develop/basic/publish.md +2 -2
  58. package/kb/site/en/develop/basic/tool.md +1 -1
  59. package/kb/site/en/develop/cordis-tutorial/07-into-the-harness.md +4 -4
  60. package/kb/site/en/develop/framework/events.md +1 -1
  61. package/kb/site/en/develop/practice/dynamic-cordis.md +17 -0
  62. package/kb/site/en/develop/practice/llm-adapter.md +3 -3
  63. package/kb/site/en/guide/github-review.md +104 -0
  64. package/kb/site/en/guide/mcp-memory.md +103 -0
  65. package/kb/site/en/guide/python-sdk.md +80 -34
  66. package/kb/site/en/guide/schedule.md +21 -0
  67. package/kb/site/en/reference/agent-lifecycle.md +1 -1
  68. package/kb/{extra → site/en/reference}/api-gateway.md +11 -9
  69. package/kb/site/en/reference/capability-seams.md +115 -67
  70. package/kb/site/en/reference/config-catalog.md +358 -164
  71. package/kb/site/en/reference/cookbook/adding-a-package.md +2 -2
  72. package/kb/site/en/reference/cookbook/adding-a-settings-card.md +2 -2
  73. package/kb/site/en/reference/cookbook/adding-a-tool.md +11 -4
  74. package/kb/site/en/reference/cookbook/adding-an-llm-adapter.md +1 -1
  75. package/kb/site/en/reference/cookbook/extension-cookbook.md +6 -6
  76. package/kb/site/en/reference/cordis-api/inherited.md +1 -1
  77. package/kb/site/en/reference/cordis-primer.md +2 -1
  78. package/kb/site/en/reference/index.md +19 -7
  79. package/kb/site/en/reference/persistence-catalog.md +91 -44
  80. package/kb/site/en/reference/subsystems/approval.md +10 -10
  81. package/kb/site/en/reference/subsystems/client-modules.md +58 -16
  82. package/kb/site/en/reference/subsystems/code-runtime.md +3 -3
  83. package/kb/site/en/reference/subsystems/compaction.md +2 -2
  84. package/kb/site/en/reference/{cookbook/adding-a-conversation-node.md → subsystems/conversation.md} +43 -24
  85. package/kb/site/en/reference/subsystems/core.md +70 -12
  86. package/kb/site/en/reference/subsystems/credentials.md +43 -3
  87. package/kb/site/en/reference/subsystems/filesystem.md +12 -2
  88. package/kb/site/en/reference/subsystems/index.md +6 -1
  89. package/kb/site/en/reference/subsystems/jobs.md +1 -1
  90. package/kb/site/en/reference/subsystems/llm-streaming.md +132 -11
  91. package/kb/site/en/reference/subsystems/permission-presets.md +1 -1
  92. package/kb/site/en/reference/subsystems/persistence.md +22 -3
  93. package/kb/site/en/reference/subsystems/plan.md +1 -1
  94. package/kb/site/en/reference/subsystems/session-projection.md +74 -33
  95. package/kb/site/en/reference/subsystems/session-query.md +9 -1
  96. package/kb/site/en/reference/subsystems/session-reference.md +28 -7
  97. package/kb/site/en/reference/subsystems/session-telemetry.md +2 -3
  98. package/kb/site/en/reference/subsystems/session.md +260 -41
  99. package/kb/site/en/reference/subsystems/settings.md +78 -1
  100. package/kb/site/en/reference/subsystems/skills.md +23 -0
  101. package/kb/site/en/reference/subsystems/slots.md +177 -0
  102. package/kb/site/en/reference/subsystems/spill.md +2 -2
  103. package/kb/site/en/reference/subsystems/storage.md +9 -1
  104. package/kb/site/en/reference/subsystems/subagent.md +90 -23
  105. package/kb/site/en/reference/subsystems/system-prompt.md +4 -4
  106. package/kb/site/en/reference/subsystems/token-meter.md +25 -10
  107. package/kb/site/en/reference/subsystems/tools.md +39 -39
  108. package/kb/site/en/reference/subsystems/typert.md +44 -37
  109. package/kb/site/en/reference/subsystems/user-questions.md +33 -33
  110. package/kb/site/en/reference/subsystems/web-client.md +98 -0
  111. package/kb/site/en/reference/subsystems/web-server.md +11 -5
  112. package/kb/site/en/reference/subsystems/web.md +7 -1
  113. package/kb/site/en/reference/subsystems/workspace.md +95 -2
  114. package/kb/site/en/reference/tool-catalog.md +76 -18
  115. package/kb/site/en/reference/tool-execution-pipeline.md +1 -1
  116. package/kb/site/guide/github-review.md +104 -0
  117. package/kb/site/guide/mcp-memory.md +103 -0
  118. package/kb/site/guide/python-sdk.md +87 -41
  119. package/kb/site/guide/schedule.md +21 -0
  120. package/kb/site/reference/agent-lifecycle.md +1 -1
  121. package/kb/{extra/api-gateway.zh.md → site/reference/api-gateway.md} +11 -9
  122. package/kb/site/reference/capability-seams.md +115 -67
  123. package/kb/site/reference/config-catalog.md +357 -163
  124. package/kb/site/reference/cookbook/adding-a-package.md +2 -2
  125. package/kb/site/reference/cookbook/adding-a-settings-card.md +2 -2
  126. package/kb/site/reference/cookbook/adding-a-tool.md +11 -4
  127. package/kb/site/reference/cookbook/adding-an-llm-adapter.md +1 -1
  128. package/kb/site/reference/cookbook/extension-cookbook.md +6 -6
  129. package/kb/site/reference/cordis-api/inherited.md +1 -1
  130. package/kb/site/reference/cordis-primer.md +2 -1
  131. package/kb/site/reference/index.md +19 -7
  132. package/kb/site/reference/persistence-catalog.md +87 -40
  133. package/kb/site/reference/subsystems/approval.md +10 -10
  134. package/kb/site/reference/subsystems/client-modules.md +58 -16
  135. package/kb/site/reference/subsystems/code-runtime.md +3 -3
  136. package/kb/site/reference/subsystems/compaction.md +2 -2
  137. package/kb/site/reference/{cookbook/adding-a-conversation-node.md → subsystems/conversation.md} +43 -24
  138. package/kb/site/reference/subsystems/core.md +70 -12
  139. package/kb/site/reference/subsystems/credentials.md +43 -3
  140. package/kb/site/reference/subsystems/filesystem.md +12 -2
  141. package/kb/site/reference/subsystems/index.md +6 -1
  142. package/kb/site/reference/subsystems/jobs.md +1 -1
  143. package/kb/site/reference/subsystems/llm-streaming.md +132 -11
  144. package/kb/site/reference/subsystems/persistence.md +22 -3
  145. package/kb/site/reference/subsystems/plan.md +1 -1
  146. package/kb/site/reference/subsystems/session-projection.md +74 -33
  147. package/kb/site/reference/subsystems/session-query.md +9 -1
  148. package/kb/site/reference/subsystems/session-reference.md +28 -7
  149. package/kb/site/reference/subsystems/session-telemetry.md +2 -3
  150. package/kb/site/reference/subsystems/session.md +260 -41
  151. package/kb/site/reference/subsystems/settings.md +78 -1
  152. package/kb/site/reference/subsystems/skills.md +23 -0
  153. package/kb/site/reference/subsystems/slots.md +177 -0
  154. package/kb/site/reference/subsystems/spill.md +2 -2
  155. package/kb/site/reference/subsystems/storage.md +9 -1
  156. package/kb/site/reference/subsystems/subagent.md +90 -23
  157. package/kb/site/reference/subsystems/system-prompt.md +4 -4
  158. package/kb/site/reference/subsystems/token-meter.md +25 -10
  159. package/kb/site/reference/subsystems/tools.md +39 -39
  160. package/kb/site/reference/subsystems/typert.md +44 -37
  161. package/kb/site/reference/subsystems/user-questions.md +33 -33
  162. package/kb/site/reference/subsystems/web-client.md +98 -0
  163. package/kb/site/reference/subsystems/web-server.md +11 -5
  164. package/kb/site/reference/subsystems/web.md +7 -1
  165. package/kb/site/reference/subsystems/workspace.md +95 -2
  166. package/kb/site/reference/tool-catalog.md +76 -18
  167. package/kb/site/reference/tool-execution-pipeline.md +1 -1
  168. package/package.json +11 -3
  169. package/skills/dsh-plugin-dev-kb.md +8 -6
@@ -160,7 +160,7 @@ Registration is a trusted same-process contract. The registry borrows the typed
160
160
  ```ts type-equiv
161
161
  /**
162
162
  * Per-scope filter over global tools. Restrictions intersect and do not affect
163
- * scoped registrations or the reserved Code Mode transport.
163
+ * scoped registrations or the reserved PTC mode transport.
164
164
  */
165
165
  interface ToolRestriction {
166
166
  /** Global tool names that stay visible; everything else is removed. */
@@ -186,23 +186,23 @@ type ToolExecutionToken = symbol & { readonly [toolExecutionTokenBrand]: true }
186
186
  * callers do not choose that token.
187
187
  */
188
188
  interface ToolExecutionInput {
189
- readonly callId: CallId
189
+ readonly callId: ToolCallId
190
190
  /**
191
191
  * Root model-requested call owning this execution tree. Callers omit it for
192
192
  * a root execution; nested dispatchers propagate the enclosing value.
193
193
  */
194
- readonly rootCallId?: CallId
194
+ readonly rootCallId?: ToolCallId
195
195
  readonly name: string
196
196
  /** Losslessly JSON-serializable parsed arguments (tools validate their own schema). */
197
197
  readonly arguments: unknown
198
198
  /** The agent on whose behalf the call runs (set by the agent loop). */
199
199
  readonly agent?: Agent
200
200
  /**
201
- * Opaque token of the enclosing transport execution, when one exists. Code
202
- * Mode sets this on SDK sub-dispatches so commit-style observers can wait for
201
+ * Opaque token of the enclosing transport execution, when one exists. PTC
202
+ * mode sets this on SDK sub-dispatches so commit-style observers can wait for
203
203
  * the outer `run_code` outcome without receiving its live mutable execution.
204
204
  * The token also marks the call as a transport sub-dispatch rather than a
205
- * model-direct call: under `mode: 'code'`, only calls WITH a parent may
205
+ * model-direct call: under `mode: 'ptc'`, only calls WITH a parent may
206
206
  * execute a native tool name — a model-direct call (no parent) is denied as
207
207
  * `UNKNOWN_TOOL` before the policy pipeline. See {@link ToolRuntime.execute}.
208
208
  */
@@ -255,25 +255,25 @@ type ToolExecutionMode =
255
255
  | { kind: 'exclusive' }
256
256
  ```
257
257
 
258
- Code Mode's bridge additionally exposes each settled sub-dispatch to the `tools/code-dispatch-log` waterfall, which may change the durable event's copy of the content (the program's value and model-visible result remain untouched):
258
+ PTC mode's bridge additionally exposes each settled sub-dispatch to the `tools/ptc-dispatch-log` waterfall, which may change the durable event's copy of the content (the program's value and model-visible result remain untouched):
259
259
 
260
260
  ```ts type-equiv
261
261
  /**
262
262
  * One settled `run_code` sub-dispatch about to be logged, as seen by the
263
- * `tools/code-dispatch-log` waterfall: the parent execution (session owner,
263
+ * `tools/ptc-dispatch-log` waterfall: the parent execution (session owner,
264
264
  * outer call identity), the sub-call identity, and the outcome whose durable
265
265
  * copy a listener may reshape. `content` is the RENDERED result projection
266
266
  * (what a native `tool/result` would carry) — the program itself received
267
267
  * the structured `value` (or just the error message on failure); only the
268
268
  * `tool/code-dispatch` event's copy changes.
269
269
  */
270
- interface CodeDispatchLog {
270
+ interface PtcDispatchLog {
271
271
  /** The outer `run_code` execution. */
272
272
  readonly exec: ToolExecution
273
273
  /** The calling agent (the scope routing key and the spill owner), when the outer call has one. */
274
274
  readonly agent?: Agent
275
275
  /** Deterministic sub-call id (`<parent>:code:<n>`). */
276
- readonly subCallId: CallId
276
+ readonly subCallId: ToolCallId
277
277
  /** The dispatched sub-tool name. */
278
278
  readonly name: string
279
279
  /** Whether the sub-call settled as an error. */
@@ -293,7 +293,7 @@ interface CodeDispatchLog {
293
293
  */
294
294
  interface ToolExecution extends ToolExecutionInput {
295
295
  /** Root model-requested call, resolved for every root and nested execution. */
296
- readonly rootCallId: CallId
296
+ readonly rootCallId: ToolCallId
297
297
  /** Registry-assigned identity shared with nested calls only as their opaque `parent` token. */
298
298
  readonly token: ToolExecutionToken
299
299
  }
@@ -491,7 +491,7 @@ Tool registry and execution pipeline. Scoped registrations shadow globals; one v
491
491
  * declaration covers every agent joined under it.
492
492
  *
493
493
  * Scoped only, and one declaration per scope: this is how an agent preset
494
- * composes Code Mode agents beside native ones in the same process, and a
494
+ * composes PTC mode agents beside native ones in the same process, and a
495
495
  * process-global override would be the `mode` config field instead.
496
496
  * @param mode - the presentation the covered agents' models see.
497
497
  * @returns the exact disposer that restores the deployment default.
@@ -601,33 +601,6 @@ A tool was registered or unregistered, or a scoped restriction changed (the avai
601
601
 
602
602
  Source: [`packages/core/tools/src/index.ts`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/core/tools/src/index.ts)
603
603
 
604
- <a id="toolscode-dispatch-log--waterfall"></a>
605
-
606
- #### `tools/code-dispatch-log` — waterfall
607
-
608
- Allow a listener to replace content in the DURABLE LOG COPY of one `run_code` sub-dispatch outcome before the bridge appends its `tool/code-dispatch` event. `next()` keeps the content unchanged; a listener may return replacement blocks (e.g. the spill policy's preview + locator for an oversized text result). Only the logged copy is affected — the program already received the complete value, and the model sees neither. A throwing listener is contained: the bridge falls back to logging the original settled content. Scope-filtered dispatch (`@deepseek-ai/dsh-scope`): agent-scoped listeners receive only that agent's dispatches.
609
-
610
- ```ts cordis-catalog
611
- /**
612
- * Allow a listener to replace content in the DURABLE LOG COPY of one
613
- * `run_code` sub-dispatch outcome before the bridge appends its
614
- * `tool/code-dispatch` event. `next()` keeps the
615
- * content unchanged; a listener may return replacement blocks (e.g. the
616
- * spill policy's preview + locator for an oversized text result). Only the
617
- * logged copy is affected — the program already received the complete
618
- * value, and the model sees neither. A throwing listener is contained:
619
- * the bridge falls back to logging the original settled content.
620
- * Scope-filtered dispatch (`@deepseek-ai/dsh-scope`): agent-scoped listeners receive only that agent's dispatches.
621
- * @param dispatch - the parent execution, sub-call identity, and the settled content to log.
622
- * @mode waterfall
623
- */
624
- 'tools/code-dispatch-log'(this: Scoped<ToolRuntime>, dispatch: CodeDispatchLog, next: () => Promise<ContentBlock[]>): Promise<ContentBlock[]>
625
- ```
626
-
627
- Types: [ContentBlock](./llm-streaming.md) · [Scoped](./scope.md)
628
-
629
- Source: [`packages/core/tools/src/index.ts`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/core/tools/src/index.ts)
630
-
631
604
  <a id="toolsexecute--waterfall"></a>
632
605
 
633
606
  #### `tools/execute` — waterfall
@@ -700,6 +673,33 @@ Types: [Scoped](./scope.md)
700
673
 
701
674
  Source: [`packages/core/tools/src/index.ts`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/core/tools/src/index.ts)
702
675
 
676
+ <a id="toolsptc-dispatch-log--waterfall"></a>
677
+
678
+ #### `tools/ptc-dispatch-log` — waterfall
679
+
680
+ Allow a listener to replace content in the DURABLE LOG COPY of one `run_code` sub-dispatch outcome before the bridge appends its `tool/code-dispatch` event. `next()` keeps the content unchanged; a listener may return replacement blocks (e.g. the spill policy's preview + locator for an oversized text result). Only the logged copy is affected — the program already received the complete value, and the model sees neither. A throwing listener is contained: the bridge falls back to logging the original settled content. Scope-filtered dispatch (`@deepseek-ai/dsh-scope`): agent-scoped listeners receive only that agent's dispatches.
681
+
682
+ ```ts cordis-catalog
683
+ /**
684
+ * Allow a listener to replace content in the DURABLE LOG COPY of one
685
+ * `run_code` sub-dispatch outcome before the bridge appends its
686
+ * `tool/code-dispatch` event. `next()` keeps the
687
+ * content unchanged; a listener may return replacement blocks (e.g. the
688
+ * spill policy's preview + locator for an oversized text result). Only the
689
+ * logged copy is affected — the program already received the complete
690
+ * value, and the model sees neither. A throwing listener is contained:
691
+ * the bridge falls back to logging the original settled content.
692
+ * Scope-filtered dispatch (`@deepseek-ai/dsh-scope`): agent-scoped listeners receive only that agent's dispatches.
693
+ * @param dispatch - the parent execution, sub-call identity, and the settled content to log.
694
+ * @mode waterfall
695
+ */
696
+ 'tools/ptc-dispatch-log'(this: Scoped<ToolRuntime>, dispatch: PtcDispatchLog, next: () => Promise<ContentBlock[]>): Promise<ContentBlock[]>
697
+ ```
698
+
699
+ Types: [ContentBlock](./llm-streaming.md) · [Scoped](./scope.md)
700
+
701
+ Source: [`packages/core/tools/src/index.ts`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/core/tools/src/index.ts)
702
+
703
703
  <a id="toolsresult--emit"></a>
704
704
 
705
705
  #### `tools/result` — emit
@@ -87,6 +87,8 @@ interface InvocationDescriptor {
87
87
  readonly method: string
88
88
  /** Service member invoked when the exported method name is an alias. */
89
89
  readonly implementation?: string
90
+ /** Absent for unary calls; stream calls validate and deliver every yielded item. */
91
+ readonly mode?: 'stream'
90
92
  /** Receiver selection mode. */
91
93
  readonly invocation:
92
94
  | { readonly kind: 'direct' }
@@ -98,7 +100,7 @@ interface InvocationDescriptor {
98
100
  }
99
101
  /** Optional consuming-Context projection for one direct lookup parameter. */
100
102
  readonly scope?: {
101
- /** Context kind whose Client binder supplies the identity. */
103
+ /** Context kind whose Client adapter supplies the identity. */
102
104
  readonly context: string
103
105
  /** Lookup parameter wire field replaced by the Context identity. */
104
106
  readonly wire: string
@@ -110,7 +112,7 @@ interface InvocationDescriptor {
110
112
  /** Reserved final Host method parameter. */
111
113
  readonly parameter: 'signal'
112
114
  }
113
- /** Codec for the resolved method result. */
115
+ /** Codec for the unary result or each yielded stream item. */
114
116
  readonly result: TypertCodec
115
117
  /** Source declaration used only for diagnostics. */
116
118
  readonly sourceLocation?: InvocationSourceLocation
@@ -181,13 +183,31 @@ type TypertGatewayErrorCode =
181
183
  ```ts type-equiv
182
184
  /** Host dispatcher consumed by Connection adapters. */
183
185
  interface TypertGateway {
186
+ /** Carrier adapter shared by WebSocket and in-process transports. */
187
+ readonly wireStream: TypertGatewayWireStream
188
+ /**
189
+ * Register the application-selected forwarded-event source.
190
+ * @param source - stream factory installed by the Remote assembly.
191
+ * @param host - stable Host facts included in each Client generation's opening frame.
192
+ * @returns disposer removing this exact source and cancelling its active streams.
193
+ */
194
+ registerRemoteEvents(
195
+ source: TypertRemoteEventSource,
196
+ host: RemoteEventHostInfo,
197
+ ): () => Promise<void>
184
198
  /**
185
199
  * Invoke one live Remote method without assuming a carrier or response envelope.
186
200
  * @param request - decoded endpoint and named wire arguments.
187
- * @returns the validated business result.
201
+ * @returns the business result without output decoding.
188
202
  * @throws {@link TypertGatewayError} for dispatch, provider, or boundary failures; lookup-policy and business errors retain identity.
189
203
  */
190
204
  invoke(request: InvokeRemoteRequest): Promise<unknown>
205
+ /**
206
+ * Open one live stream Remote method without assuming a physical carrier.
207
+ * @param request - decoded endpoint and named wire arguments.
208
+ * @returns a cancellation-aware iterable over the business results.
209
+ */
210
+ stream(request: InvokeRemoteRequest): Promise<AsyncIterable<unknown>>
191
211
  }
192
212
  ```
193
213
 
@@ -205,26 +225,15 @@ interface TypertClientRemote extends TypertRemoteNamespaceMap {
205
225
  */
206
226
  $mount(contribution: TypertRemoteContribution): Promise<TypertDisposer>
207
227
  /**
208
- * Subscribe to one forwarded Host event; delivery is one-way, in registration
209
- * order, and isolates a throwing listener from the rest.
228
+ * Subscribe to one forwarded Host event. Notifications run in registration
229
+ * order and isolate failures; scoped waterfalls return, delegate through
230
+ * `next()`, or reject the Host dispatch.
210
231
  * @template Event - forwarded event name selected by the Host assembly.
211
232
  * @param event - forwarded Host event name, unchanged on the wire.
212
- * @param listener - receives the Host's argument list as declared by Cordis `Events`.
233
+ * @param listener - receives the Client projection of the Cordis `Events` declaration.
213
234
  * @returns disposer owned by the calling fiber.
214
235
  */
215
- $on<Event extends TypertRemoteEvent>(event: Event, listener: Events[Event]): () => void
216
- /**
217
- * Hand one decoded forwarded frame to the subscription table. The carrier
218
- * owning the Host frame sink calls this; a consumer subscribes with
219
- * {@link TypertClientRemote.$on} and never calls it.
220
- *
221
- * `event` is a plain string because this is the wire boundary: the name is
222
- * whatever the Host assembly's allowlist selected, and one nobody subscribed
223
- * to is dropped silently.
224
- * @param event - forwarded Host event name, exactly as the Host emitted it.
225
- * @param args - the Host argument list, already JSON-decoded.
226
- */
227
- $dispatch(event: string, args: readonly unknown[]): void
236
+ $on<Event extends TypertRemoteEvent>(event: Event, listener: TypertClientEventListener<Event>): () => void
228
237
  }
229
238
  ```
230
239
 
@@ -236,23 +245,6 @@ interface TypertClientRemote extends TypertRemoteNamespaceMap {
236
245
 
237
246
  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).
238
247
 
239
- <a id="ctxapiproxy--apiproxy"></a>
240
-
241
- ### `ctx.apiProxy` — `ApiProxy`
242
-
243
- Root interface of the unified API. New client-request domain = one new file pair + one field here + one map row.
244
-
245
- ```ts cordis-catalog
246
- /**
247
- * Response entry for server requests; not a domain method.
248
- * @param message - Client response carrying the server request's rpcId.
249
- * @returns Transport receipt for the response delivery.
250
- */
251
- respond(message: ClientResponse): Promise<RpcReceipt>
252
- ```
253
-
254
- Source: [`packages/host/apiproxy/src/api/index.ts`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/host/apiproxy/src/api/index.ts)
255
-
256
248
  <a id="ctxtypert--typertregistry"></a>
257
249
 
258
250
  ### `ctx.typert` — `TypertRegistry`
@@ -326,13 +318,28 @@ Source: [`packages/typert/registry/src/service.ts`](https://github.com/deepseek-
326
318
  Resolve strict generated definitions or conservative SRC markers against current Cordis Services and Typert providers.
327
319
 
328
320
  ```ts cordis-catalog
321
+ /**
322
+ * Register the sole application-selected forwarded-event source.
323
+ * @param source - stream factory installed by the Remote assembly.
324
+ * @param host - stable Host facts included in each Client generation's opening frame.
325
+ * @returns disposer removing this source and cancelling its active streams.
326
+ */
327
+ registerRemoteEvents( source: TypertRemoteEventSource, host: RemoteEventHostInfo, ): () => Promise<void>
328
+
329
329
  /**
330
330
  * Invoke one live Remote method through strict generated reflection or SRC markers.
331
331
  * @param request - decoded endpoint and exact named wire arguments.
332
- * @returns the validated business result.
332
+ * @returns the business result without output decoding.
333
333
  * @throws {@link TypertGatewayError} for dispatch, provider, or boundary failures; lookup-policy and business errors retain identity.
334
334
  */
335
335
  async invoke(request: InvokeRemoteRequest): Promise<unknown>
336
+
337
+ /**
338
+ * Open one live stream Remote method without assuming a physical carrier.
339
+ * @param request - decoded endpoint and named wire arguments.
340
+ * @returns a cancellation-aware iterable over the business results.
341
+ */
342
+ async stream(request: InvokeRemoteRequest): Promise<AsyncIterable<unknown>>
336
343
  ```
337
344
 
338
345
  Source: [`packages/api/gateway/src/index.ts`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/api/gateway/src/index.ts)
@@ -5,7 +5,7 @@ outline: [2,3]
5
5
 
6
6
  # User Interaction
7
7
 
8
- The user-questions seam of [dsh-user-questions](https://github.com/deepseek-ai/deepseek-harness/tree/master/packages/interaction/user-questions). It is the provider-neutral vocabulary a tool or permission plugin uses when it needs the human to answer before the agent can continue. UI surfaces provide the active `UserQuestionProvider`; the host runtime relays requests to its connected client.
8
+ The user-questions seam of [dsh-user-questions](https://github.com/deepseek-ai/deepseek-harness/tree/master/packages/interaction/user-questions). It is the provider-neutral vocabulary a tool or permission plugin uses when it needs the human to answer before the agent can continue. Agent-scoped waterfall listeners compose the available UI surfaces, including listeners relayed to a connected client.
9
9
 
10
10
  Source: [`packages/interaction/user-questions/src/index.ts`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/interaction/user-questions/src/index.ts)
11
11
 
@@ -77,14 +77,7 @@ interface AskUserQuestionItem {
77
77
 
78
78
  ```ts type-equiv
79
79
  /** Request for a human answer. */
80
- interface AskUserQuestionRequest {
81
- /** Questions to display. */
82
- questions: AskUserQuestionItem[]
83
- /** Exact live calling agent, when the request came from an agent tool call. */
84
- agent?: Agent
85
- /** Abort signal for the owning tool/step. */
86
- signal?: AbortSignal
87
- }
80
+ interface AskUserQuestionRequest extends AskUserQuestionRequestEvent {}
88
81
  ```
89
82
 
90
83
  ## Answer
@@ -111,17 +104,6 @@ interface AskUserQuestionAnswer {
111
104
  }
112
105
  ```
113
106
 
114
- ## Provider
115
-
116
- Only one provider may be active in a context. Provider registration is effect-bound so HMR/disposal removes the active UI.
117
-
118
- ```ts type-equiv
119
- /** UI-side provider for user questions. */
120
- interface UserQuestionProvider {
121
- ask(request: AskUserQuestionRequest): Promise<AskUserQuestionAnswer>
122
- }
123
- ```
124
-
125
107
  ## Errors
126
108
 
127
109
  `UserQuestionError` extends `HarnessError`, so `ctx.tools.execute()` preserves `{ name, code }` for model-facing tool failures such as `EMPTY_QUESTIONS`, `NO_PROVIDER`, `ASK_ABORTED`, or UI-side cancellation.
@@ -148,19 +130,11 @@ Generated from source by `scripts/gen-cordis-catalog.ts` (verified fresh by `pnp
148
130
 
149
131
  ### `ctx.userQuestions` — `UserQuestionService`
150
132
 
151
- `ctx.userQuestions`: one active UI provider plus an `ask()` API.
133
+ `ctx.userQuestions`: validation plus the scoped answerer waterfall.
152
134
 
153
135
  ```ts cordis-catalog
154
136
  /**
155
- * Register the UI provider. Only one provider may be active in a context.
156
- *
157
- * @param provider UI-side implementation that collects answers.
158
- * @returns Disposer that unregisters this provider.
159
- */
160
- registerProvider(provider: UserQuestionProvider): () => void
161
-
162
- /**
163
- * Ask the active UI provider and wait for the user's answer.
137
+ * Ask the scoped answerer waterfall and wait for the user's answer.
164
138
  *
165
139
  * When a caller supplies an agent, human interaction is valid only for the
166
140
  * exact live runtime root. Runtime ownership, not durable session lineage,
@@ -170,12 +144,38 @@ registerProvider(provider: UserQuestionProvider): () => void
170
144
  *
171
145
  * @param request Questions, owner agent, and abort signal.
172
146
  * @returns The answer chosen or typed by the human.
173
- * @throws {UserQuestionError} code `CALLER_NOT_LIVE` when a supplied
174
- * agent is not the registry's exact live instance, or `DELEGATED_CALLER`
175
- * when that live agent is owned by another agent.
147
+ * @throws {UserQuestionError} code `ASK_ABORTED` when the supplied signal
148
+ * is already or becomes aborted, `CALLER_NOT_LIVE` when a supplied agent
149
+ * is not the registry's exact live instance, or `DELEGATED_CALLER` when
150
+ * that live agent is owned by another agent.
176
151
  */
177
152
  async ask(request: AskUserQuestionRequest): Promise<AskUserQuestionAnswer>
178
153
  ```
179
154
 
180
155
  Source: [`packages/interaction/user-questions/src/index.ts`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/interaction/user-questions/src/index.ts)
156
+
157
+ <a id="user-questions-events"></a>
158
+
159
+ ### `user-questions/*` events
160
+
161
+ <a id="user-questionsrequest--waterfall"></a>
162
+
163
+ #### `user-questions/request` — waterfall
164
+
165
+ Ask composed answerers for structured user input. Return an answer to claim the request or call `next()` to delegate. Scope-filtered dispatch (`@deepseek-ai/dsh-scope`): agent-scoped listeners receive only that agent.
166
+
167
+ ```ts cordis-catalog
168
+ /**
169
+ * Ask composed answerers for structured user input. Return an answer to
170
+ * claim the request or call `next()` to delegate. Scope-filtered dispatch
171
+ * (`@deepseek-ai/dsh-scope`): agent-scoped listeners receive only that agent.
172
+ * @param request - pending user-question request.
173
+ * @mode waterfall
174
+ */
175
+ 'user-questions/request'( this: Scoped<Agent>, request: AskUserQuestionRequestEvent, next: () => Promise<AskUserQuestionAnswer>, ): Promise<AskUserQuestionAnswer>
176
+ ```
177
+
178
+ Types: [Agent](./core.md) · [Scoped](./scope.md)
179
+
180
+ Source: [`packages/interaction/user-questions/src/types.ts`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/interaction/user-questions/src/types.ts)
181
181
  <!-- END GENERATED cordis-surface -->
@@ -0,0 +1,98 @@
1
+ ---
2
+ editSource: "docs/subsystems/web-client.md"
3
+ outline: [2,3]
4
+ ---
5
+
6
+ # Web Client architecture
7
+
8
+ The Web Client is a browser-side Cordis application assembled from independently loaded plugins. Its architecture has four reusable foundations: [Client Modules](./client-modules.md) loads the plugin graph, the [API Gateway](../api-gateway.md) provides typed Host communication, [Slots](./slots.md) composes React UI, and [Conversation](./conversation.md) turns a Session history window into target-owned views. This page connects those systems and defines where Client models and feature packages belong.
9
+
10
+ ## Layers and ownership
11
+
12
+ | Layer | Main owners | Responsibility |
13
+ |---|---|---|
14
+ | Host application | business services and `packages/api/*-controller` Host entries | Own authoritative state, persistence, mutation ordering, access policy, and stream production. |
15
+ | Transport and API assembly | `client/connection`, `api/gateway`, `api/remotes` | Establish a Client generation, expose generated `ctx.remote` methods and streams, forward selected Cordis events, and carry cancellation and results. |
16
+ | Client models | `api/session-controller/client`, `api/workspace-controller/client` | Maintain React-free mirrors of Host state, resolve stream/unary races, own object identities and subscriptions, and expose narrow command services. |
17
+ | UI adapters | `client/ui-session`, `client/ui-workspace` | Convert model observables into root or Session-scoped standard Slot sources without taking ownership of business state. |
18
+ | Conversation data | `client/ui-conversation`, target packages such as `ui-chat` and `ui-trajectory` | Assemble standard events and compact historical Assistant runs into independent target snapshots and own the shared conversation shell and input flow. |
19
+ | Composition and rendering | `client/ui-slots`, `client/ui-renderer`, `client/ui-layout`, feature UI packages | Declare extension locations, derive component props, bind observables to React hooks, and mount the final tree. |
20
+
21
+ The dependency direction is Host state → Remote transport → Client model → UI adapter → Conversation or presentation → Slots → React. User actions travel back through callbacks that close over an injected Client service or generated Remote namespace. A presentation component never receives Cordis `ctx`, a transport object, or another feature plugin's implementation.
22
+
23
+ ## Browser boot
24
+
25
+ The Host writes the composed `WebBootGraph` to `window.__DSH_BOOT__` and installs the browser module-loader facade before parser-preloaded scripts execute. The module system is a lazy CommonJS table: loading a bundle registers its factory, while materializing an entry runs the factory with synchronous `require` over platform modules and declared dynamic dependencies.
26
+
27
+ The Web boot kernel creates the module system, prefetches `immediately` entries, mounts the vendored Cordis Loader, and creates every graph entry. Cordis service injection determines activation; module graph order determines only whether synchronous imports can be materialized. After the complete roster reaches a settled state, `ui-renderer` hydrates the framework-free boot DOM and calls the sole context-level `renderSlot('root')` operation. [Client Modules](./client-modules.md) owns the graph, bundle route, cache revision, and loader details.
28
+
29
+ ## Remote communication
30
+
31
+ Host business services annotate callable methods with Typert Remote decorators. Host generation emits strict descriptors, runtime codecs, declaration merges, and source maps. The Client-side `api-remotes` assembly selects those generated contributions and mounts concrete methods under `ctx.remote.<namespace>` and Session-scoped `agentCtx.remote.<namespace>`. Feature packages depend on the generated service face, not the Gateway implementation or a Host package's runtime entry.
32
+
33
+ The Connection owns request correlation, the `/api` carrier, trust checks, exact Fetch routes, and connection generations. API Gateway owns Remote dispatch, cancellation, logical streams, and selected Host event forwarding. Controller operations belong on generated Remote methods or explicit Remote streams; feature-owned downloads register exact Fetch routes. The [API Gateway reference](../api-gateway.md) defines generation and invocation, while the [Connection README](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/client/connection/README.md) defines the physical carrier and trust policy.
34
+
35
+ The internal `$events` logical stream is the Connection generation source. Its opening `ready` frame carries the Host home used for path display and establishes the generation after Host listeners are attached, before any controller begins a baseline read. `ctx.remote.$on()` delivers allowlisted ordinary events to the root Client Context and scoped waterfall events to the resolved Session Context; a waterfall listener returns a result, calls `next()`, or rejects.
36
+
37
+ ## Client models
38
+
39
+ Each API controller package owns a paired Host and Client face. The Host side owns authoritative mutation and stream production. The Client side owns an identity-stable, React-free model over the same generated wire types and exposes observable snapshots plus commands. UI packages consume these Client services and do not reproduce transport state in component stores.
40
+
41
+ ### Sessions
42
+
43
+ [`api/session-controller`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/api/session-controller/README.md) exposes Host commands for list, search, creation, selection data, prompt, queue, cancellation, pagination, and follow/control streams. Its Client side is organized as `ClientSessions → SessionManager → Session`:
44
+
45
+ - `ClientSessions` provides `ctx.sessions`, owns Session scopes and stable `SessionBinding` objects, and projects the selected list state.
46
+ - `SessionManager` owns the list baseline, live list/control updates, lazy Session instances, queues, projection stores, subagent catalogs, and conflict ordering between pulls and later updates.
47
+ - Each `Session` owns one contiguous logical-event window represented by `SessionEventLikeEntry` values, paging, follow, prompt/control state, and the observable snapshot consumed by adapters.
48
+
49
+ The durable event path opens `follow()`, whose first frame contains the current header, tail page, cursor, and complete projection baseline. History records have an explicit `event` or `chunks` discriminator and an aligned inner `event`; the journal validates each inclusive logical sequence range before the Client retains the records as `SessionEventLikeEntry` values without per-record conversion. Each physical generation atomically replaces the retained window from that snapshot; standard live events then append by sequence. `page()` is reserved for older history and gap repair. The transient control stream starts every generation with a complete baseline and then applies queue, job, and projection updates.
50
+
51
+ ### Workspaces
52
+
53
+ [`api/workspace-controller`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/api/workspace-controller/README.md) keeps Workspace mutation policy and the authoritative follow feed on the Host. `ClientWorkspaceModel` owns the browser rows, order, archived Session ids, command echoes, and stream/unary race resolution. Every stream generation starts with a complete baseline followed by `upsert`, `remove`, `order`, and `archived` increments; reconnect replaces the model from the new baseline. `WorkspaceController` exposes that model as `ctx.workspaces`, while `ui-workspace` contributes `useWorkspaces` and navigation callbacks to the UI.
54
+
55
+ This pairing is not a second source of business truth. Host controllers decide durable state and mutation outcomes; Client models maintain the latest usable local projection, preserve object identity where useful to rendering, and encode how delayed responses and replacement baselines merge.
56
+
57
+ ## Conversation and presentation
58
+
59
+ `ui-session` installs the `session` scope adapter and publishes `useSessions`, `useSession`, `sessionId`, and `useProjection`. Domain adapters add further standard sources without putting React hooks on the model objects.
60
+
61
+ `ui-conversation` binds once to each `SessionBinding.eventSource`. Its event registry correlates standard events and Client-only `chunkrow/*` history events into stable business Contexts, and its view registry materializes target snapshots. Packed runs stay single inputs and Matches through replay; Chat Assistant, Trajectory Assistant, and Turn Tail are the built-in Definitions that interpret them. `ui-chat` and `ui-trajectory` register separate Definitions and builders: they may interpret the same event family, but they do not import or share each other's final display model. The shell selects a registered view and passes its snapshot through standard hooks and Slots. [Conversation](./conversation.md) defines Context identity, replay, Location data, target builders, and keyed renderers.
62
+
63
+ `ui-slots` provides the typed registry and lifecycle ledger; `ui-renderer` is the only package that binds bare observables through `useSyncExternalStore`, owns React contexts, and renders the root tree. Feature components receive framework hooks, owner props, store actions, and explicit injection through their derived props. [Web Client Slots](./slots.md) lists those inputs, extension APIs, and the current Slot hierarchy.
64
+
65
+ ## Data paths
66
+
67
+ | Path | Sequence |
68
+ |---|---|
69
+ | durable Session display | Host Session log → packed Remote `follow`/`page` history → Client `SessionEventLikeEntry` window → Conversation Contexts → target snapshot (`chat`, `trajectory`, or another registered target) → Slot view → React |
70
+ | transient Session control | Host control baseline → Remote snapshot stream → `SessionManager` queue/job/projection stores → Session and list snapshots → standard hooks → components |
71
+ | Workspace state | Host Workspace baseline and increments → `ClientWorkspaceModel` → `ctx.workspaces.list` → `useWorkspaces` → sidebar, hero, and navigation entries |
72
+ | scoped interaction | Host Cordis waterfall → API Remotes `$events` → `ctx.remote.$on()` on the Session Context → owning UI package → result or `next()` |
73
+ | user command | component callback → registration inject face or Slot owner → `ctx.sessions`, `ctx.workspaces`, or generated scoped Remote → Host Controller → authoritative update → stream or event projection back to the Client |
74
+
75
+ ## Reconnection
76
+
77
+ Physical and logical recovery are separate. Gateway mux restores the physical WebSocket; each `RemoteStream` reopens its own logical source when the Connection publishes a usable generation. A carrier failure is retryable, while a business error, malformed opening item, or protocol violation is terminal for the owning logical stream.
78
+
79
+ Recovery follows the data's semantics:
80
+
81
+ - A durable Session journal validates logical sequence ranges and replaces its window from every generation's opening snapshot; `page()` supplies older history and repairs any later range gap.
82
+ - Session control and Workspace streams retain the last published value while disconnected, then atomically replace it from a fresh opening baseline.
83
+ - Ordinary forwarded notifications are not replayed. Stateful domains need a baseline, cursor, or explicit query; scoped waterfalls retain their own request lifetime.
84
+
85
+ There is no monolithic Client `Runtime`, `HostFrame`, `events.mux`, `events.host`, or universal `resync()` API. The Connection exposes generation state, Gateway owns logical stream supervision, and each Client model defines replacement or resume semantics appropriate to its data.
86
+
87
+ ## Package boundaries
88
+
89
+ Feature plugin packages may share declarations through `import type`; they do not runtime-import or re-export another feature plugin's values. Cross-package behavior uses injected Cordis services, and cross-package UI uses Slots. Target-specific Conversation Definitions, projection helpers, and final view data stay with their target package even when Chat and Trajectory intentionally implement parallel logic.
90
+
91
+ Shared runtime values need a narrow static owner with no feature lifecycle, such as `client/store`, `ui-primitives`, or a browser-safe utility package. Transport and generated API assembly may import runtime contributions because assembling one protocol is their explicit responsibility. A feature package does not add `dsh.client.external` merely to bypass this rule.
92
+
93
+ Use the four detailed references according to the extension being added:
94
+
95
+ - [Client Modules](./client-modules.md) for package discovery, loading, shared module identities, and boot order.
96
+ - [API Gateway](../api-gateway.md) for Host methods, generated Remote contributions, streams, and forwarded events.
97
+ - [Web Client Slots](./slots.md) for components, hooks, stores, injection, and placement.
98
+ - [Conversation](./conversation.md) for durable event correlation, target snapshots, and Chat or Trajectory view contributions.
@@ -5,7 +5,7 @@ outline: [2,3]
5
5
 
6
6
  # HTTP Server
7
7
 
8
- [dsh-host-webserver](https://github.com/deepseek-ai/deepseek-harness/tree/master/packages/host/webserver) is the browser HTTP carrier for the GUI host: a single `node:http` plugin providing `ctx.webServer`, a named-route registry, index.html transform callbacks, and one fallback handler that a plugin may claim. It is not part of the agent loop and not a capability seam; it knows no harness concepts, and another plugin registers every feature route, including the `/api` bridge, plugin bundles, and the HMR event stream ([layering note](https://github.com/deepseek-ai/deepseek-harness/blob/master/.agents/notes/implemented/architecture/2026-07-19-gui-layering-and-rpc-protocol.md)). It serves browsers only: Electron loads the built files over `file://` and sends fetch requests through an IPC bridge instead of this server.
8
+ [dsh-host-webserver](https://github.com/deepseek-ai/deepseek-harness/tree/master/packages/host/webserver) is the browser HTTP carrier for the GUI host: a single `node:http` plugin providing `ctx.webServer`, a named-route registry, optional gzip response compression, index.html transform callbacks, and one fallback handler that a plugin may claim. It is not part of the agent loop and not a capability seam; it knows no harness concepts, and another plugin registers every feature route, including the `/api` bridge, plugin bundles, and the HMR event stream ([layering note](https://github.com/deepseek-ai/deepseek-harness/blob/master/.agents/notes/implemented/architecture/2026-07-24-web-config-tree-boot-and-transport-layering.md)). It serves browsers only: Electron loads the built files over `file://` and sends fetch requests through an IPC bridge instead of this server.
9
9
 
10
10
  Source: [`packages/host/webserver/src/index.ts`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/host/webserver/src/index.ts)
11
11
 
@@ -27,25 +27,31 @@ interface WebRoute {
27
27
  }
28
28
  ```
29
29
 
30
- Match order is fixed: exact table first, then longest matching prefix, then the registered fallback. Registration order carries no request-facing semantics — named routes are composed to be disjoint, and the fallback seat answers anything no named route claims; one owner only, a second registration throws. The shipped Web composition claims the seat with [`dsh-host-frontend-static`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/host/frontend-static/src/index.ts), the SPA dist server with locked semantics: non-GET/HEAD is 405, traversal outside the dist root is 403, a readable index renders at the dist root and configured index path, existing files are served directly, absent or non-file targets are empty 404 responses, and unknown extensions ship as octet-stream.
30
+ Match order is fixed: exact table first, then longest matching prefix, then the registered fallback. Registration order carries no request-facing semantics — named routes are composed to be disjoint, and the fallback seat answers anything no named route claims; one owner only, a second registration throws. The shipped Web composition claims the seat with [`dsh-host-frontend-static`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/host/frontend-static/src/index.ts), the SPA dist server with locked semantics: Connection authenticates the dist root and configured index before their HTML is read; non-index assets remain public; non-GET/HEAD is 405, traversal outside the dist root is 403, existing files are served directly, absent or non-file targets are empty 404 responses, and unknown extensions ship as octet-stream.
31
31
 
32
32
  ## Config
33
33
 
34
34
  ```ts type-equiv
35
- /** Gateway config: the listen address. */
35
+ /** Web server listen and response-compression config. */
36
36
  interface Config {
37
37
  /** Listen host; the two supported values are loopback and all-interfaces. */
38
38
  host: '127.0.0.1' | '0.0.0.0'
39
39
  /** Listen port; zero requests an OS-assigned port. */
40
40
  port: number
41
+ /** Response compression for socket-backed HTTP requests. @default 'none' */
42
+ compression?: 'none' | 'gzip'
43
+ /** Gzip DEFLATE level from 0 through 9. @default 1 */
44
+ compressionLevel?: number
45
+ /** Minimum known response length eligible for gzip; unknown-length streams are eligible. @default 1024 */
46
+ compressionThresholdBytes?: number
41
47
  }
42
48
  ```
43
49
 
44
- `host` accepts only `127.0.0.1` (default posture) and `0.0.0.0` (deliberate network exposure); there is no TLS, auth, or origin policy, so a non-loopback bind exposes the server to that network. The dist location is an assembly fact of the frontend plugin that claims the seat.
50
+ `host` accepts only `127.0.0.1` (default posture) and `0.0.0.0` (deliberate network exposure). The carrier itself owns no TLS, authentication, or Origin policy, so a non-loopback bind exposes the server unless the composition supplies those controls. `compression` defaults to `none`; the shipped Web bundle selects gzip level 1 with a 1024-byte threshold. The shipped `dsh web` command selects loopback and rejects `--host 0.0.0.0`; its Connection plugin supplies Host/Origin checks plus browser-session authentication for every Host API route and stream. Other compositions own their bind and route-authentication policy. The dist location is an assembly fact of the frontend plugin that claims the seat.
45
51
 
46
52
  ## The service
47
53
 
48
- `WebServer` (`ctx.webServer`) listens immediately on activation; a listen failure (EADDRINUSE…) rejects initialization, and the boot process reports the failed fiber. `register(route)` adds one named route and returns its disposer; a duplicate `(kind, path)` throws because route patterns are a composition-level contract and a collision is a misconfiguration. `collectIndexInjections()` gathers structured `IndexInjection` rows over one `webserver/index-inject` emit, and `renderIndex(html)` renders them into successful root and configured index responses before applying the raw `tapIndex(transform)` escape-hatch transforms in registration order; [dsh-client-modules](https://github.com/deepseek-ai/deepseek-harness/tree/master/packages/client/modules) answers the event with the boot manifest rows. `port` reads the listening port, including the port assigned by the OS when `config.port` is 0.
54
+ `WebServer` (`ctx.webServer`) listens immediately on activation; a listen failure (EADDRINUSE…) rejects initialization, and the boot process reports the failed fiber. `register(route)` adds one named route and returns its disposer; a duplicate `(kind, path)` throws because route patterns are a composition-level contract and a collision is a misconfiguration. Gzip wraps eligible socket-backed responses inside the server, so route handlers retain direct `ServerResponse` ownership and no response-writing API is added to the service. Existing content encodings, `Cache-Control: no-transform`, ranges, SSE, ZIP, and the packaged `.gz` Worker image remain identity responses. `collectIndexInjections()` gathers structured `IndexInjection` rows over one `webserver/index-inject` emit, and `renderIndex(html)` renders them into successful root and configured index responses before applying the raw `tapIndex(transform)` escape-hatch transforms in registration order; [dsh-client-modules](https://github.com/deepseek-ai/deepseek-harness/tree/master/packages/client/modules) answers the event with the boot manifest rows. `port` reads the listening port, including the port assigned by the OS when `config.port` is 0.
49
55
 
50
56
  A request whose handling throws (a malformed %-escape hitting `decodeURIComponent`, a client dropping mid-body) is logged as a warning and answered 400 — or the socket destroyed when headers are already out — never a process exit. Disposal pairs `close()` with `closeAllConnections()` because a handler may hold its response open (SSE) and such connections never end on their own; without the force-close, teardown would hang. The package never prints: the URL line belongs to the shell. Per-package operational detail, including the dev-mode bundle watch pipeline, stays in the [README](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/host/webserver/README.md).
51
57
 
@@ -127,13 +127,19 @@ A provider's `available(): boolean` is a cheap LOCAL check (credential presence,
127
127
 
128
128
  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.
129
129
 
130
+ ## Fetch network policy
131
+
132
+ The shipped Cordis, Code, and Standard presets expose `web_fetch` in every sandbox and approval mode without per-call confirmation. File sandbox presets do not govern Web network access. A deployment that needs confirmation must add a `tools/pre-execute` policy or disable fetch.
133
+
134
+ The HTTP provider resolves each actual request, rejects non-public answers including private IPv4 reached through the active DNS64 prefix, pins the validated address set, and repeats enforcement for each same-origin redirect. A cross-origin redirect requires a new tool call and fresh public-address validation. These checks prevent SSRF access to non-public destinations but do not stop a model from sending data to a public URL.
135
+
130
136
  ## Errors
131
137
 
132
138
  `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`.
133
139
 
134
140
  ## The service
135
141
 
136
- `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.
142
+ `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, resolves each hostname once, rejects any answer set containing a non-public IPv4 or IPv6 destination or an active-prefix NAT64 translation to non-public IPv4, pins the request connection to the validated addresses, repeats those checks for every same-origin redirect hop, caps redirects, bytes, characters, and time, and decodes the body; the tool owns presentation.
137
143
 
138
144
  <!-- BEGIN GENERATED cordis-surface (gen-cordis-catalog.ts) — do not edit between markers -->
139
145