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.
- package/CHANGELOG.md +16 -0
- package/README.en.md +144 -0
- package/README.md +21 -7
- package/kb/INDEX.md +19 -5
- package/kb/README.md +11 -10
- package/kb/extra/AGENTS.md +4 -4
- package/kb/extra/cookbook/adding-a-vendored-package.md +2 -2
- package/kb/extra/cookbook/adding-a-vendored-package.zh.md +2 -2
- package/kb/extra/deepseek-llm-api-wire-extensions.md +159 -0
- package/kb/extra/deepseek-llm-api-wire-extensions.zh.md +159 -0
- package/kb/extra/development.md +8 -14
- package/kb/extra/development.zh.md +8 -14
- package/kb/extra/event-producer-consumer.md +47 -41
- package/kb/extra/event-producer-consumer.zh.md +47 -41
- package/kb/extra/glossary.md +1 -1
- package/kb/extra/glossary.zh.md +1 -1
- package/kb/extra/graph-atlas.md +0 -2
- package/kb/extra/graph-atlas.zh.md +0 -2
- package/kb/extra/i18n/README.md +4 -4
- package/kb/extra/i18n/README.zh.md +4 -4
- package/kb/extra/module-graph.md +680 -413
- package/kb/extra/module-graph.zh.md +681 -414
- package/kb/extra/postmortem/0001-acp-default-export-drops-inject.md +2 -2
- package/kb/extra/postmortem/0001-acp-default-export-drops-inject.zh.md +2 -2
- package/kb/extra/postmortem/0002-js-expression-disabled-filesystem-tools.md +2 -2
- package/kb/extra/postmortem/0002-js-expression-disabled-filesystem-tools.zh.md +2 -2
- package/kb/extra/postmortem/0003-web-agent-gui-feedback-loop.md +2 -2
- package/kb/extra/postmortem/0003-web-agent-gui-feedback-loop.zh.md +2 -2
- package/kb/extra/postmortem/0004-landlock-partial-notice-misclassified-child-failures.md +1 -1
- package/kb/extra/postmortem/0004-landlock-partial-notice-misclassified-child-failures.zh.md +1 -1
- package/kb/extra/rescope.md +2 -2
- package/kb/extra/rescope.zh.md +2 -2
- package/kb/extra/subsystems/agent-team.md +24 -1
- package/kb/extra/subsystems/agent-team.zh.md +24 -1
- package/kb/extra/subsystems/attachment.md +12 -4
- package/kb/extra/subsystems/attachment.zh.md +12 -4
- package/kb/extra/subsystems/extensions.md +18 -0
- package/kb/extra/subsystems/extensions.zh.md +18 -0
- package/kb/extra/subsystems/feedback.md +2 -2
- package/kb/extra/subsystems/feedback.zh.md +2 -2
- package/kb/extra/subsystems/todo.md +32 -0
- package/kb/extra/subsystems/todo.zh.md +32 -0
- package/kb/extra/subsystems/webhook.md +70 -0
- package/kb/extra/subsystems/webhook.zh.md +70 -0
- package/kb/extra/testing.md +11 -10
- package/kb/extra/testing.zh.md +8 -7
- package/kb/meta/search-index.json +269 -161
- package/kb/meta/site-pages.txt +182 -168
- package/kb/meta/source.json +5 -5
- package/kb/meta/topics.md +14 -6
- package/kb/site/develop/basic/publish.md +2 -2
- package/kb/site/develop/basic/tool.md +1 -1
- package/kb/site/develop/cordis-tutorial/07-into-the-harness.md +4 -4
- package/kb/site/develop/framework/events.md +1 -1
- package/kb/site/develop/practice/dynamic-cordis.md +17 -0
- package/kb/site/develop/practice/llm-adapter.md +3 -3
- package/kb/site/en/develop/basic/publish.md +2 -2
- package/kb/site/en/develop/basic/tool.md +1 -1
- package/kb/site/en/develop/cordis-tutorial/07-into-the-harness.md +4 -4
- package/kb/site/en/develop/framework/events.md +1 -1
- package/kb/site/en/develop/practice/dynamic-cordis.md +17 -0
- package/kb/site/en/develop/practice/llm-adapter.md +3 -3
- package/kb/site/en/guide/github-review.md +104 -0
- package/kb/site/en/guide/mcp-memory.md +103 -0
- package/kb/site/en/guide/python-sdk.md +80 -34
- package/kb/site/en/guide/schedule.md +21 -0
- package/kb/site/en/reference/agent-lifecycle.md +1 -1
- package/kb/{extra → site/en/reference}/api-gateway.md +11 -9
- package/kb/site/en/reference/capability-seams.md +115 -67
- package/kb/site/en/reference/config-catalog.md +358 -164
- package/kb/site/en/reference/cookbook/adding-a-package.md +2 -2
- package/kb/site/en/reference/cookbook/adding-a-settings-card.md +2 -2
- package/kb/site/en/reference/cookbook/adding-a-tool.md +11 -4
- package/kb/site/en/reference/cookbook/adding-an-llm-adapter.md +1 -1
- package/kb/site/en/reference/cookbook/extension-cookbook.md +6 -6
- package/kb/site/en/reference/cordis-api/inherited.md +1 -1
- package/kb/site/en/reference/cordis-primer.md +2 -1
- package/kb/site/en/reference/index.md +19 -7
- package/kb/site/en/reference/persistence-catalog.md +91 -44
- package/kb/site/en/reference/subsystems/approval.md +10 -10
- package/kb/site/en/reference/subsystems/client-modules.md +58 -16
- package/kb/site/en/reference/subsystems/code-runtime.md +3 -3
- package/kb/site/en/reference/subsystems/compaction.md +2 -2
- package/kb/site/en/reference/{cookbook/adding-a-conversation-node.md → subsystems/conversation.md} +43 -24
- package/kb/site/en/reference/subsystems/core.md +70 -12
- package/kb/site/en/reference/subsystems/credentials.md +43 -3
- package/kb/site/en/reference/subsystems/filesystem.md +12 -2
- package/kb/site/en/reference/subsystems/index.md +6 -1
- package/kb/site/en/reference/subsystems/jobs.md +1 -1
- package/kb/site/en/reference/subsystems/llm-streaming.md +132 -11
- package/kb/site/en/reference/subsystems/permission-presets.md +1 -1
- package/kb/site/en/reference/subsystems/persistence.md +22 -3
- package/kb/site/en/reference/subsystems/plan.md +1 -1
- package/kb/site/en/reference/subsystems/session-projection.md +74 -33
- package/kb/site/en/reference/subsystems/session-query.md +9 -1
- package/kb/site/en/reference/subsystems/session-reference.md +28 -7
- package/kb/site/en/reference/subsystems/session-telemetry.md +2 -3
- package/kb/site/en/reference/subsystems/session.md +260 -41
- package/kb/site/en/reference/subsystems/settings.md +78 -1
- package/kb/site/en/reference/subsystems/skills.md +23 -0
- package/kb/site/en/reference/subsystems/slots.md +177 -0
- package/kb/site/en/reference/subsystems/spill.md +2 -2
- package/kb/site/en/reference/subsystems/storage.md +9 -1
- package/kb/site/en/reference/subsystems/subagent.md +90 -23
- package/kb/site/en/reference/subsystems/system-prompt.md +4 -4
- package/kb/site/en/reference/subsystems/token-meter.md +25 -10
- package/kb/site/en/reference/subsystems/tools.md +39 -39
- package/kb/site/en/reference/subsystems/typert.md +44 -37
- package/kb/site/en/reference/subsystems/user-questions.md +33 -33
- package/kb/site/en/reference/subsystems/web-client.md +98 -0
- package/kb/site/en/reference/subsystems/web-server.md +11 -5
- package/kb/site/en/reference/subsystems/web.md +7 -1
- package/kb/site/en/reference/subsystems/workspace.md +95 -2
- package/kb/site/en/reference/tool-catalog.md +76 -18
- package/kb/site/en/reference/tool-execution-pipeline.md +1 -1
- package/kb/site/guide/github-review.md +104 -0
- package/kb/site/guide/mcp-memory.md +103 -0
- package/kb/site/guide/python-sdk.md +87 -41
- package/kb/site/guide/schedule.md +21 -0
- package/kb/site/reference/agent-lifecycle.md +1 -1
- package/kb/{extra/api-gateway.zh.md → site/reference/api-gateway.md} +11 -9
- package/kb/site/reference/capability-seams.md +115 -67
- package/kb/site/reference/config-catalog.md +357 -163
- package/kb/site/reference/cookbook/adding-a-package.md +2 -2
- package/kb/site/reference/cookbook/adding-a-settings-card.md +2 -2
- package/kb/site/reference/cookbook/adding-a-tool.md +11 -4
- package/kb/site/reference/cookbook/adding-an-llm-adapter.md +1 -1
- package/kb/site/reference/cookbook/extension-cookbook.md +6 -6
- package/kb/site/reference/cordis-api/inherited.md +1 -1
- package/kb/site/reference/cordis-primer.md +2 -1
- package/kb/site/reference/index.md +19 -7
- package/kb/site/reference/persistence-catalog.md +87 -40
- package/kb/site/reference/subsystems/approval.md +10 -10
- package/kb/site/reference/subsystems/client-modules.md +58 -16
- package/kb/site/reference/subsystems/code-runtime.md +3 -3
- package/kb/site/reference/subsystems/compaction.md +2 -2
- package/kb/site/reference/{cookbook/adding-a-conversation-node.md → subsystems/conversation.md} +43 -24
- package/kb/site/reference/subsystems/core.md +70 -12
- package/kb/site/reference/subsystems/credentials.md +43 -3
- package/kb/site/reference/subsystems/filesystem.md +12 -2
- package/kb/site/reference/subsystems/index.md +6 -1
- package/kb/site/reference/subsystems/jobs.md +1 -1
- package/kb/site/reference/subsystems/llm-streaming.md +132 -11
- package/kb/site/reference/subsystems/persistence.md +22 -3
- package/kb/site/reference/subsystems/plan.md +1 -1
- package/kb/site/reference/subsystems/session-projection.md +74 -33
- package/kb/site/reference/subsystems/session-query.md +9 -1
- package/kb/site/reference/subsystems/session-reference.md +28 -7
- package/kb/site/reference/subsystems/session-telemetry.md +2 -3
- package/kb/site/reference/subsystems/session.md +260 -41
- package/kb/site/reference/subsystems/settings.md +78 -1
- package/kb/site/reference/subsystems/skills.md +23 -0
- package/kb/site/reference/subsystems/slots.md +177 -0
- package/kb/site/reference/subsystems/spill.md +2 -2
- package/kb/site/reference/subsystems/storage.md +9 -1
- package/kb/site/reference/subsystems/subagent.md +90 -23
- package/kb/site/reference/subsystems/system-prompt.md +4 -4
- package/kb/site/reference/subsystems/token-meter.md +25 -10
- package/kb/site/reference/subsystems/tools.md +39 -39
- package/kb/site/reference/subsystems/typert.md +44 -37
- package/kb/site/reference/subsystems/user-questions.md +33 -33
- package/kb/site/reference/subsystems/web-client.md +98 -0
- package/kb/site/reference/subsystems/web-server.md +11 -5
- package/kb/site/reference/subsystems/web.md +7 -1
- package/kb/site/reference/subsystems/workspace.md +95 -2
- package/kb/site/reference/tool-catalog.md +76 -18
- package/kb/site/reference/tool-execution-pipeline.md +1 -1
- package/package.json +11 -3
- 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
|
|
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:
|
|
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?:
|
|
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.
|
|
202
|
-
*
|
|
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: '
|
|
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
|
-
|
|
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/
|
|
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
|
|
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:
|
|
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:
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
209
|
-
* order
|
|
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
|
|
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:
|
|
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
|
|
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.
|
|
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`:
|
|
133
|
+
`ctx.userQuestions`: validation plus the scoped answerer waterfall.
|
|
152
134
|
|
|
153
135
|
```ts cordis-catalog
|
|
154
136
|
/**
|
|
155
|
-
*
|
|
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 `
|
|
174
|
-
*
|
|
175
|
-
*
|
|
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-
|
|
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,
|
|
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
|
-
/**
|
|
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)
|
|
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,
|
|
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
|
|