dsh-plugin-dev-kb 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +56 -0
- package/cordis.patch.yml +12 -0
- package/kb/INDEX.md +210 -0
- package/kb/README.md +69 -0
- package/kb/extra/AGENTS.md +75 -0
- package/kb/extra/api-gateway.md +164 -0
- package/kb/extra/api-gateway.zh.md +164 -0
- package/kb/extra/cookbook/adding-a-vendored-package.md +59 -0
- package/kb/extra/cookbook/adding-a-vendored-package.zh.md +59 -0
- package/kb/extra/cookbook/maintaining-dsh-code-review.md +64 -0
- package/kb/extra/cookbook/maintaining-dsh-code-review.zh.md +64 -0
- package/kb/extra/cookbook/responding-to-pr-review-on-a-stack.md +32 -0
- package/kb/extra/cookbook/responding-to-pr-review-on-a-stack.zh.md +32 -0
- package/kb/extra/defensive-patterns.md +33 -0
- package/kb/extra/defensive-patterns.zh.md +33 -0
- package/kb/extra/development.md +171 -0
- package/kb/extra/development.zh.md +171 -0
- package/kb/extra/event-producer-consumer.md +76 -0
- package/kb/extra/event-producer-consumer.zh.md +78 -0
- package/kb/extra/glossary.md +45 -0
- package/kb/extra/glossary.zh.md +45 -0
- package/kb/extra/graph-atlas.md +24 -0
- package/kb/extra/graph-atlas.zh.md +26 -0
- package/kb/extra/i18n/README.md +60 -0
- package/kb/extra/i18n/README.zh.md +60 -0
- package/kb/extra/i18n/style-samples.md +87 -0
- package/kb/extra/i18n/terminology.md +214 -0
- package/kb/extra/i18n/translation-prompt.md +263 -0
- package/kb/extra/i18n/translation-rules.md +69 -0
- package/kb/extra/i18n/translation-rules.zh.md +69 -0
- package/kb/extra/module-graph.md +1641 -0
- package/kb/extra/module-graph.zh.md +1643 -0
- package/kb/extra/postmortem/0001-acp-default-export-drops-inject.md +113 -0
- package/kb/extra/postmortem/0001-acp-default-export-drops-inject.zh.md +113 -0
- package/kb/extra/postmortem/0002-js-expression-disabled-filesystem-tools.md +47 -0
- package/kb/extra/postmortem/0002-js-expression-disabled-filesystem-tools.zh.md +47 -0
- package/kb/extra/postmortem/0003-web-agent-gui-feedback-loop.md +53 -0
- package/kb/extra/postmortem/0003-web-agent-gui-feedback-loop.zh.md +53 -0
- package/kb/extra/postmortem/0004-landlock-partial-notice-misclassified-child-failures.md +55 -0
- package/kb/extra/postmortem/0004-landlock-partial-notice-misclassified-child-failures.zh.md +55 -0
- package/kb/extra/postmortem/README.md +18 -0
- package/kb/extra/postmortem/README.zh.md +18 -0
- package/kb/extra/rescope.md +53 -0
- package/kb/extra/rescope.zh.md +53 -0
- package/kb/extra/subsystems/attachment.md +125 -0
- package/kb/extra/subsystems/attachment.zh.md +125 -0
- package/kb/extra/subsystems/extensions.md +364 -0
- package/kb/extra/subsystems/extensions.zh.md +364 -0
- package/kb/extra/subsystems/feedback.md +266 -0
- package/kb/extra/subsystems/feedback.zh.md +266 -0
- package/kb/extra/testing.md +49 -0
- package/kb/extra/testing.zh.md +49 -0
- package/kb/extra/web-styling.md +25 -0
- package/kb/extra/web-styling.zh.md +25 -0
- package/kb/meta/search-index.json +1328 -0
- package/kb/meta/site-pages.txt +168 -0
- package/kb/meta/source.json +13 -0
- package/kb/meta/topics.md +75 -0
- package/kb/site/develop/basic/config.md +108 -0
- package/kb/site/develop/basic/index.md +146 -0
- package/kb/site/develop/basic/publish.md +185 -0
- package/kb/site/develop/basic/tool.md +54 -0
- package/kb/site/develop/cordis-tutorial/01-first-plugin.md +95 -0
- package/kb/site/develop/cordis-tutorial/02-lifecycle-and-effects.md +98 -0
- package/kb/site/develop/cordis-tutorial/03-services.md +98 -0
- package/kb/site/develop/cordis-tutorial/04-events.md +144 -0
- package/kb/site/develop/cordis-tutorial/05-config.md +84 -0
- package/kb/site/develop/cordis-tutorial/06-composition-and-hmr.md +113 -0
- package/kb/site/develop/cordis-tutorial/07-into-the-harness.md +107 -0
- package/kb/site/develop/cordis-tutorial/index.md +62 -0
- package/kb/site/develop/framework/events.md +145 -0
- package/kb/site/develop/framework/index.md +139 -0
- package/kb/site/develop/framework/service.md +152 -0
- package/kb/site/develop/practice/index.md +157 -0
- package/kb/site/develop/practice/llm-adapter.md +190 -0
- package/kb/site/en/develop/basic/config.md +108 -0
- package/kb/site/en/develop/basic/index.md +146 -0
- package/kb/site/en/develop/basic/publish.md +185 -0
- package/kb/site/en/develop/basic/tool.md +54 -0
- package/kb/site/en/develop/cordis-tutorial/01-first-plugin.md +95 -0
- package/kb/site/en/develop/cordis-tutorial/02-lifecycle-and-effects.md +98 -0
- package/kb/site/en/develop/cordis-tutorial/03-services.md +98 -0
- package/kb/site/en/develop/cordis-tutorial/04-events.md +144 -0
- package/kb/site/en/develop/cordis-tutorial/05-config.md +84 -0
- package/kb/site/en/develop/cordis-tutorial/06-composition-and-hmr.md +113 -0
- package/kb/site/en/develop/cordis-tutorial/07-into-the-harness.md +107 -0
- package/kb/site/en/develop/cordis-tutorial/index.md +60 -0
- package/kb/site/en/develop/framework/events.md +145 -0
- package/kb/site/en/develop/framework/index.md +139 -0
- package/kb/site/en/develop/framework/service.md +150 -0
- package/kb/site/en/develop/practice/index.md +157 -0
- package/kb/site/en/develop/practice/llm-adapter.md +190 -0
- package/kb/site/en/guide/providers-custom-form.png +0 -0
- package/kb/site/en/guide/providers-models-page.png +0 -0
- package/kb/site/en/guide/providers.md +100 -0
- package/kb/site/en/guide/python-sdk.md +106 -0
- package/kb/site/en/guide/quickstart.md +32 -0
- package/kb/site/en/index.md +8 -0
- package/kb/site/en/reference/agent-lifecycle.md +86 -0
- package/kb/site/en/reference/capability-seams.md +475 -0
- package/kb/site/en/reference/config-catalog.md +3155 -0
- package/kb/site/en/reference/cookbook/adding-a-conversation-node.md +235 -0
- package/kb/site/en/reference/cookbook/adding-a-package.md +120 -0
- package/kb/site/en/reference/cookbook/adding-a-settings-card.md +102 -0
- package/kb/site/en/reference/cookbook/adding-a-tool.md +96 -0
- package/kb/site/en/reference/cookbook/adding-an-llm-adapter.md +45 -0
- package/kb/site/en/reference/cookbook/extension-cookbook.md +131 -0
- package/kb/site/en/reference/cordis-api/context.md +368 -0
- package/kb/site/en/reference/cordis-api/events.md +211 -0
- package/kb/site/en/reference/cordis-api/fiber.md +379 -0
- package/kb/site/en/reference/cordis-api/inherited.md +43 -0
- package/kb/site/en/reference/cordis-api/registry.md +156 -0
- package/kb/site/en/reference/cordis-api/service.md +106 -0
- package/kb/site/en/reference/cordis-primer.md +46 -0
- package/kb/site/en/reference/index.md +131 -0
- package/kb/site/en/reference/persistence-catalog.md +949 -0
- package/kb/site/en/reference/subsystems/approval.md +173 -0
- package/kb/site/en/reference/subsystems/client-modules.md +121 -0
- package/kb/site/en/reference/subsystems/code-runtime.md +194 -0
- package/kb/site/en/reference/subsystems/commands.md +190 -0
- package/kb/site/en/reference/subsystems/compaction.md +241 -0
- package/kb/site/en/reference/subsystems/core.md +1073 -0
- package/kb/site/en/reference/subsystems/credentials.md +136 -0
- package/kb/site/en/reference/subsystems/filesystem.md +498 -0
- package/kb/site/en/reference/subsystems/goal.md +280 -0
- package/kb/site/en/reference/subsystems/index.md +58 -0
- package/kb/site/en/reference/subsystems/invariants.md +91 -0
- package/kb/site/en/reference/subsystems/jobs.md +293 -0
- package/kb/site/en/reference/subsystems/llm-streaming.md +920 -0
- package/kb/site/en/reference/subsystems/lsp.md +205 -0
- package/kb/site/en/reference/subsystems/permission-presets.md +134 -0
- package/kb/site/en/reference/subsystems/persistence.md +388 -0
- package/kb/site/en/reference/subsystems/plan.md +90 -0
- package/kb/site/en/reference/subsystems/sandbox.md +221 -0
- package/kb/site/en/reference/subsystems/schedule.md +189 -0
- package/kb/site/en/reference/subsystems/scope.md +62 -0
- package/kb/site/en/reference/subsystems/session-projection.md +265 -0
- package/kb/site/en/reference/subsystems/session-query.md +498 -0
- package/kb/site/en/reference/subsystems/session-reference.md +111 -0
- package/kb/site/en/reference/subsystems/session-telemetry.md +197 -0
- package/kb/site/en/reference/subsystems/session-title.md +207 -0
- package/kb/site/en/reference/subsystems/session.md +852 -0
- package/kb/site/en/reference/subsystems/settings.md +313 -0
- package/kb/site/en/reference/subsystems/shell.md +306 -0
- package/kb/site/en/reference/subsystems/skills.md +334 -0
- package/kb/site/en/reference/subsystems/spill.md +120 -0
- package/kb/site/en/reference/subsystems/storage.md +232 -0
- package/kb/site/en/reference/subsystems/subagent.md +737 -0
- package/kb/site/en/reference/subsystems/subprocess.md +327 -0
- package/kb/site/en/reference/subsystems/system-prompt.md +210 -0
- package/kb/site/en/reference/subsystems/terminal.md +187 -0
- package/kb/site/en/reference/subsystems/token-meter.md +93 -0
- package/kb/site/en/reference/subsystems/tools.md +723 -0
- package/kb/site/en/reference/subsystems/typert.md +339 -0
- package/kb/site/en/reference/subsystems/user-questions.md +181 -0
- package/kb/site/en/reference/subsystems/web-server.md +111 -0
- package/kb/site/en/reference/subsystems/web.md +202 -0
- package/kb/site/en/reference/subsystems/workflow.md +281 -0
- package/kb/site/en/reference/subsystems/workspace.md +231 -0
- package/kb/site/en/reference/tool-catalog.md +1877 -0
- package/kb/site/en/reference/tool-execution-pipeline.md +66 -0
- package/kb/site/guide/providers-custom-form.zh.png +0 -0
- package/kb/site/guide/providers-models-page.zh.png +0 -0
- package/kb/site/guide/providers.md +100 -0
- package/kb/site/guide/python-sdk.md +106 -0
- package/kb/site/guide/quickstart.md +32 -0
- package/kb/site/index.md +8 -0
- package/kb/site/reference/agent-lifecycle.md +86 -0
- package/kb/site/reference/capability-seams.md +475 -0
- package/kb/site/reference/config-catalog.md +3154 -0
- package/kb/site/reference/cookbook/adding-a-conversation-node.md +235 -0
- package/kb/site/reference/cookbook/adding-a-package.md +120 -0
- package/kb/site/reference/cookbook/adding-a-settings-card.md +102 -0
- package/kb/site/reference/cookbook/adding-a-tool.md +98 -0
- package/kb/site/reference/cookbook/adding-an-llm-adapter.md +45 -0
- package/kb/site/reference/cookbook/extension-cookbook.md +133 -0
- package/kb/site/reference/cordis-api/context.md +368 -0
- package/kb/site/reference/cordis-api/events.md +211 -0
- package/kb/site/reference/cordis-api/fiber.md +379 -0
- package/kb/site/reference/cordis-api/inherited.md +43 -0
- package/kb/site/reference/cordis-api/registry.md +156 -0
- package/kb/site/reference/cordis-api/service.md +106 -0
- package/kb/site/reference/cordis-primer.md +52 -0
- package/kb/site/reference/index.md +135 -0
- package/kb/site/reference/persistence-catalog.md +949 -0
- package/kb/site/reference/subsystems/approval.md +173 -0
- package/kb/site/reference/subsystems/client-modules.md +121 -0
- package/kb/site/reference/subsystems/code-runtime.md +194 -0
- package/kb/site/reference/subsystems/commands.md +190 -0
- package/kb/site/reference/subsystems/compaction.md +241 -0
- package/kb/site/reference/subsystems/core.md +1081 -0
- package/kb/site/reference/subsystems/credentials.md +136 -0
- package/kb/site/reference/subsystems/filesystem.md +498 -0
- package/kb/site/reference/subsystems/goal.md +280 -0
- package/kb/site/reference/subsystems/index.md +58 -0
- package/kb/site/reference/subsystems/invariants.md +91 -0
- package/kb/site/reference/subsystems/jobs.md +293 -0
- package/kb/site/reference/subsystems/llm-streaming.md +926 -0
- package/kb/site/reference/subsystems/lsp.md +205 -0
- package/kb/site/reference/subsystems/permission-presets.md +134 -0
- package/kb/site/reference/subsystems/persistence.md +388 -0
- package/kb/site/reference/subsystems/plan.md +90 -0
- package/kb/site/reference/subsystems/sandbox.md +221 -0
- package/kb/site/reference/subsystems/schedule.md +189 -0
- package/kb/site/reference/subsystems/scope.md +62 -0
- package/kb/site/reference/subsystems/session-projection.md +265 -0
- package/kb/site/reference/subsystems/session-query.md +498 -0
- package/kb/site/reference/subsystems/session-reference.md +111 -0
- package/kb/site/reference/subsystems/session-telemetry.md +197 -0
- package/kb/site/reference/subsystems/session-title.md +207 -0
- package/kb/site/reference/subsystems/session.md +854 -0
- package/kb/site/reference/subsystems/settings.md +313 -0
- package/kb/site/reference/subsystems/shell.md +306 -0
- package/kb/site/reference/subsystems/skills.md +334 -0
- package/kb/site/reference/subsystems/spill.md +120 -0
- package/kb/site/reference/subsystems/storage.md +232 -0
- package/kb/site/reference/subsystems/subagent.md +739 -0
- package/kb/site/reference/subsystems/subprocess.md +327 -0
- package/kb/site/reference/subsystems/system-prompt.md +210 -0
- package/kb/site/reference/subsystems/terminal.md +187 -0
- package/kb/site/reference/subsystems/token-meter.md +93 -0
- package/kb/site/reference/subsystems/tools.md +723 -0
- package/kb/site/reference/subsystems/typert.md +339 -0
- package/kb/site/reference/subsystems/user-questions.md +181 -0
- package/kb/site/reference/subsystems/web-server.md +111 -0
- package/kb/site/reference/subsystems/web.md +202 -0
- package/kb/site/reference/subsystems/workflow.md +281 -0
- package/kb/site/reference/subsystems/workspace.md +231 -0
- package/kb/site/reference/tool-catalog.md +1880 -0
- package/kb/site/reference/tool-execution-pipeline.md +66 -0
- package/package.json +40 -0
- package/scripts/rebuild-index.mjs +88 -0
- package/skills/dsh-plugin-dev-kb.md +66 -0
|
@@ -0,0 +1,339 @@
|
|
|
1
|
+
---
|
|
2
|
+
editSource: "docs/subsystems/typert.md"
|
|
3
|
+
outline: [2,3]
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Typert remote calls
|
|
7
|
+
|
|
8
|
+
Types shared by generated Remote artifacts, the Host Gateway, and consumer API assemblies. The [Typert Gateway Agent Note](https://github.com/deepseek-ai/deepseek-harness/blob/master/.agents/notes/implemented/architecture/2026-08-02-typert-remote-method-calls.md) owns the architecture and transport decisions; this page records the literal public contracts from [`dsh-typert-protocol`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/typert/protocol/src/types.ts) and [`dsh-api-gateway`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/api/gateway/src/types.ts).
|
|
9
|
+
|
|
10
|
+
## Lookup and Context declarations
|
|
11
|
+
|
|
12
|
+
Business-object packages extend two empty maps through declaration merging. A lookup associates one Host object type with its wire identity; a Context declaration associates one scoped Context kind with its wire identity. Generated descriptors name these keys, while runtime providers supply the live resolution behavior.
|
|
13
|
+
|
|
14
|
+
```ts type-equiv
|
|
15
|
+
/** Merge-extensible Host object lookup declarations. */
|
|
16
|
+
interface TypertLookupMap {}
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
```ts type-equiv
|
|
20
|
+
/** Merge-extensible scoped Context declarations. */
|
|
21
|
+
interface TypertContextMap {}
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
The registry retains a lookup's wire declaration after its resolver unloads. SRC discovery therefore continues to classify the parameter as a lookup and fails unavailable instead of accepting the wire value as an ordinary business object.
|
|
25
|
+
|
|
26
|
+
```ts type-equiv
|
|
27
|
+
/** Stable wire declaration retained after a lookup provider unloads. */
|
|
28
|
+
interface TypertLookupDefinition {
|
|
29
|
+
/** Merge-declared lookup key. */
|
|
30
|
+
readonly key: string
|
|
31
|
+
/** Source parameter name recognized by the SRC weak parser. */
|
|
32
|
+
readonly parameter: string
|
|
33
|
+
/** Wire field replacing the Host object parameter. */
|
|
34
|
+
readonly wire: string
|
|
35
|
+
/** Canonical Host type symbol used by strict generation. */
|
|
36
|
+
readonly hostTypeSymbol: string
|
|
37
|
+
/** Canonical wire type symbol used by strict generation. */
|
|
38
|
+
readonly wireTypeSymbol: string
|
|
39
|
+
}
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Invocation descriptors
|
|
43
|
+
|
|
44
|
+
An `InvocationDescriptor` is local reflection, not a wire message. Host and consumer builds generate corresponding descriptors; the request sends only the endpoint and named `args`. Strict codecs carry generated schemas, while SRC codecs enforce JSON-safe values without structural type recovery. Cancellation is an out-of-band carrier signal injected after business parameters and never enters `args`.
|
|
45
|
+
|
|
46
|
+
```ts type-equiv
|
|
47
|
+
/** Codec attached to one invocation parameter or result. */
|
|
48
|
+
type TypertCodec =
|
|
49
|
+
| {
|
|
50
|
+
readonly mode: 'strict'
|
|
51
|
+
readonly typeSymbol: string
|
|
52
|
+
readonly schema: TypertSchema
|
|
53
|
+
}
|
|
54
|
+
| {
|
|
55
|
+
readonly mode: 'src-json'
|
|
56
|
+
}
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
```ts type-equiv
|
|
60
|
+
/** One ordered business parameter in a Remote invocation. */
|
|
61
|
+
interface InvocationParameterDescriptor {
|
|
62
|
+
/** Source-level parameter name. */
|
|
63
|
+
readonly name: string
|
|
64
|
+
/** Required key in the wire `args` object. */
|
|
65
|
+
readonly wire: string
|
|
66
|
+
/** Whether the value is JSON or requires a registered Host lookup. */
|
|
67
|
+
readonly source: 'json' | 'lookup'
|
|
68
|
+
/** Lookup key when `source` is `lookup`. */
|
|
69
|
+
readonly lookup?: string
|
|
70
|
+
/** Boundary codec for the wire representation. */
|
|
71
|
+
readonly codec: TypertCodec
|
|
72
|
+
/** Missing wire fields decode to `undefined` only for an explicitly declared `T | undefined`. */
|
|
73
|
+
readonly acceptsUndefined?: true
|
|
74
|
+
}
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
```ts type-equiv
|
|
78
|
+
/** Carrier-independent description of one exported method invocation. */
|
|
79
|
+
interface InvocationDescriptor {
|
|
80
|
+
/** Globally stable generated identity. */
|
|
81
|
+
readonly id: string
|
|
82
|
+
/** Cordis service key owning the method. */
|
|
83
|
+
readonly service: string
|
|
84
|
+
/** Wire namespace, defaulting to the service key. */
|
|
85
|
+
readonly namespace: string
|
|
86
|
+
/** Public instance method name. */
|
|
87
|
+
readonly method: string
|
|
88
|
+
/** Service member invoked when the exported method name is an alias. */
|
|
89
|
+
readonly implementation?: string
|
|
90
|
+
/** Receiver selection mode. */
|
|
91
|
+
readonly invocation:
|
|
92
|
+
| { readonly kind: 'direct' }
|
|
93
|
+
| {
|
|
94
|
+
readonly kind: 'context'
|
|
95
|
+
readonly context: string
|
|
96
|
+
readonly wire: string
|
|
97
|
+
readonly codec: TypertCodec
|
|
98
|
+
}
|
|
99
|
+
/** Optional consuming-Context projection for one direct lookup parameter. */
|
|
100
|
+
readonly scope?: {
|
|
101
|
+
/** Context kind whose Client binder supplies the identity. */
|
|
102
|
+
readonly context: string
|
|
103
|
+
/** Lookup parameter wire field replaced by the Context identity. */
|
|
104
|
+
readonly wire: string
|
|
105
|
+
}
|
|
106
|
+
/** Ordered business parameters. */
|
|
107
|
+
readonly parameters: readonly InvocationParameterDescriptor[]
|
|
108
|
+
/** Transport cancellation injected after business parameters instead of entering wire args. */
|
|
109
|
+
readonly cancellation?: {
|
|
110
|
+
/** Reserved final Host method parameter. */
|
|
111
|
+
readonly parameter: 'signal'
|
|
112
|
+
}
|
|
113
|
+
/** Codec for the resolved method result. */
|
|
114
|
+
readonly result: TypertCodec
|
|
115
|
+
/** Source declaration used only for diagnostics. */
|
|
116
|
+
readonly sourceLocation?: InvocationSourceLocation
|
|
117
|
+
}
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
## Typert registry
|
|
121
|
+
|
|
122
|
+
`ctx.typert` separates current-environment descriptors, explicitly selected Remote contributions, lookup providers, and scoped Context providers. A lookup provider owns the stable wire declaration and default resolver; Host composition can configure an effect-scoped synchronous or asynchronous resolver for the same key, and unloading that configuration restores the default policy. Registrations are Cordis-owned effects and return awaitable disposers.
|
|
123
|
+
|
|
124
|
+
```ts type-equiv
|
|
125
|
+
/** Minimal Typert runtime consumed through dependency inversion. */
|
|
126
|
+
interface TypertRegistryContract {
|
|
127
|
+
readonly local: TypertLocalRegistry
|
|
128
|
+
readonly remotes: TypertRemoteRegistry
|
|
129
|
+
readonly lookups: TypertLookupRegistry
|
|
130
|
+
readonly contexts: TypertContextRegistry
|
|
131
|
+
}
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
Generated consumer declarations merge direct namespaces into the map inherited by `TypertClientRemote`.
|
|
135
|
+
|
|
136
|
+
```ts type-equiv
|
|
137
|
+
/** Merge-extensible direct namespace surface generated for Client Remote services. */
|
|
138
|
+
interface TypertRemoteNamespaceMap {}
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
## Host Gateway
|
|
142
|
+
|
|
143
|
+
Connection decodes its carrier envelope before calling `ctx.typertGateway`. The request carries exact named wire fields and the carrier's cancellation signal separately; infrastructure and boundary failures use the Gateway's in-process error taxonomy, ordinary exceptions are folded by the RPC adapter into the transport's `internal` error code, and existing RPC errors carried by lookup policy through `TypertLookupFailure` are returned unchanged.
|
|
144
|
+
|
|
145
|
+
```ts type-equiv
|
|
146
|
+
/** One Remote method request after a carrier has decoded its envelope. */
|
|
147
|
+
interface InvokeRemoteRequest {
|
|
148
|
+
/** Remote namespace selected by the generated descriptor. */
|
|
149
|
+
readonly namespace: string
|
|
150
|
+
/** Exported Service method name. */
|
|
151
|
+
readonly method: string
|
|
152
|
+
/** Named wire values; fields must exactly match the descriptor. */
|
|
153
|
+
readonly args: Readonly<Record<string, unknown>>
|
|
154
|
+
/** Carrier or direct-caller cancellation injected only into cancellation-aware methods. */
|
|
155
|
+
readonly signal?: AbortSignal
|
|
156
|
+
}
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
```ts type-equiv
|
|
160
|
+
/** Stable infrastructure and boundary failures emitted before or after business execution. */
|
|
161
|
+
type TypertGatewayErrorCode =
|
|
162
|
+
| 'ambiguous-endpoint'
|
|
163
|
+
| 'arguments-invalid'
|
|
164
|
+
| 'binding-invalid'
|
|
165
|
+
| 'context-failed'
|
|
166
|
+
| 'context-not-found'
|
|
167
|
+
| 'context-unavailable'
|
|
168
|
+
| 'definition-unavailable'
|
|
169
|
+
| 'input-invalid'
|
|
170
|
+
| 'invocation-unavailable'
|
|
171
|
+
| 'lookup-failed'
|
|
172
|
+
| 'lookup-not-found'
|
|
173
|
+
| 'lookup-unavailable'
|
|
174
|
+
| 'method-unavailable'
|
|
175
|
+
| 'provider-mismatch'
|
|
176
|
+
| 'result-invalid'
|
|
177
|
+
| 'service-unavailable'
|
|
178
|
+
| 'signature-invalid'
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
```ts type-equiv
|
|
182
|
+
/** Host dispatcher consumed by Connection adapters. */
|
|
183
|
+
interface TypertGateway {
|
|
184
|
+
/**
|
|
185
|
+
* Invoke one live Remote method without assuming a carrier or response envelope.
|
|
186
|
+
* @param request - decoded endpoint and named wire arguments.
|
|
187
|
+
* @returns the validated business result.
|
|
188
|
+
* @throws {@link TypertGatewayError} for dispatch, provider, or boundary failures; lookup-policy and business errors retain identity.
|
|
189
|
+
*/
|
|
190
|
+
invoke(request: InvokeRemoteRequest): Promise<unknown>
|
|
191
|
+
}
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
## Consumer Remote
|
|
195
|
+
|
|
196
|
+
`ctx.remote` exposes only namespaces contributed by imported `/remote` artifacts. `$mount()` installs generated descriptors and concrete methods as one fiber-owned operation. Each namespace is a traced `remote.<namespace>` Cordis child Service whose lifetime spans its mounted methods; no JavaScript Proxy or Host business Service type enters the consumer.
|
|
197
|
+
|
|
198
|
+
```ts type-equiv
|
|
199
|
+
/** Client Remote capability implemented by the Gateway and consumed by Remote assemblies. */
|
|
200
|
+
interface TypertClientRemote extends TypertRemoteNamespaceMap {
|
|
201
|
+
/**
|
|
202
|
+
* Mount one generated Host-for-Client contribution in the caller's fiber.
|
|
203
|
+
* @param contribution - explicitly selected Remote package artifact.
|
|
204
|
+
* @returns disposer after namespace services and concrete methods are ready.
|
|
205
|
+
*/
|
|
206
|
+
$mount(contribution: TypertRemoteContribution): Promise<TypertDisposer>
|
|
207
|
+
/**
|
|
208
|
+
* Subscribe to one forwarded Host event; delivery is one-way, in registration
|
|
209
|
+
* order, and isolates a throwing listener from the rest.
|
|
210
|
+
* @template Event - forwarded event name selected by the Host assembly.
|
|
211
|
+
* @param event - forwarded Host event name, unchanged on the wire.
|
|
212
|
+
* @param listener - receives the Host's argument list as declared by Cordis `Events`.
|
|
213
|
+
* @returns disposer owned by the calling fiber.
|
|
214
|
+
*/
|
|
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
|
|
228
|
+
}
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
<!-- BEGIN GENERATED cordis-surface (gen-cordis-catalog.ts) — do not edit between markers -->
|
|
232
|
+
|
|
233
|
+
<a id="cordis-surface"></a>
|
|
234
|
+
|
|
235
|
+
## Cordis API
|
|
236
|
+
|
|
237
|
+
Generated from source by `scripts/gen-cordis-catalog.ts` (verified fresh by `pnpm run verify-cordis-catalog` in doc-sync; regenerate with `pnpm run gen-cordis-catalog`) — this section is byte-identical in both language sides of the page. Signature blocks use a `ts cordis-catalog` fence and keep the original source JSDoc; dispatch modes are defined in the [primer](../cordis-primer.md#dispatch-modes), and the framework-inherited `ctx` API lives in [cordis-api/inherited.md](../cordis-api/inherited.md).
|
|
238
|
+
|
|
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:22`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/host/apiproxy/src/api/index.ts)
|
|
255
|
+
|
|
256
|
+
<a id="ctxtypert--typertregistry"></a>
|
|
257
|
+
|
|
258
|
+
### `ctx.typert` — `TypertRegistry`
|
|
259
|
+
|
|
260
|
+
Registry of generated schemas, package reflection, invocations, and Remote dependency providers.
|
|
261
|
+
|
|
262
|
+
```ts cordis-catalog
|
|
263
|
+
/**
|
|
264
|
+
* Register one generated contribution atomically for the calling fiber.
|
|
265
|
+
* Duplicate package-face identities, schemas, invocation ids, or endpoints
|
|
266
|
+
* reject the whole batch.
|
|
267
|
+
* @param contribution - generated schemas, reflection, and Host invocations.
|
|
268
|
+
* @returns the exact effect disposer that removes this contribution.
|
|
269
|
+
*/
|
|
270
|
+
register(contribution: TypertContribution): TypertDisposer
|
|
271
|
+
|
|
272
|
+
/**
|
|
273
|
+
* Look up one schema by `<package>#<name>`.
|
|
274
|
+
* @param key - global schema key.
|
|
275
|
+
* @returns the live schema record, or `undefined` when absent.
|
|
276
|
+
*/
|
|
277
|
+
get(key: string): TypertSchemaRecord | undefined
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
* Resolve one required schema.
|
|
281
|
+
* @param key - global schema key.
|
|
282
|
+
* @returns the live schema record.
|
|
283
|
+
* @throws when the key is malformed, the package face is absent, or the schema is not contributed.
|
|
284
|
+
*/
|
|
285
|
+
resolve(key: string): TypertSchemaRecord
|
|
286
|
+
|
|
287
|
+
/**
|
|
288
|
+
* Enumerate live schemas in registration order.
|
|
289
|
+
* @param filter - optional package and face restriction.
|
|
290
|
+
* @returns matching schema records.
|
|
291
|
+
*/
|
|
292
|
+
list(filter: TypertSchemaFilter = {}): TypertSchemaRecord[]
|
|
293
|
+
|
|
294
|
+
/**
|
|
295
|
+
* Look up generated reflection for one package face.
|
|
296
|
+
* @param packageName - exact npm package name.
|
|
297
|
+
* @param face - face to query; defaults to the host runtime.
|
|
298
|
+
* @returns the live package record, or `undefined` when absent.
|
|
299
|
+
*/
|
|
300
|
+
getPackage(packageName: string, face: TypertFace = 'host'): TypertPackageRecord | undefined
|
|
301
|
+
|
|
302
|
+
/**
|
|
303
|
+
* Enumerate generated package reflection in registration order.
|
|
304
|
+
* @param filter - optional package and face restriction.
|
|
305
|
+
* @returns matching package records.
|
|
306
|
+
*/
|
|
307
|
+
listPackages(filter: TypertPackageFilter = {}): TypertPackageRecord[]
|
|
308
|
+
|
|
309
|
+
/**
|
|
310
|
+
* Project a live Zod schema to JSON Schema without caching the result.
|
|
311
|
+
* @param key - global schema key.
|
|
312
|
+
* @param params - Zod projection parameters.
|
|
313
|
+
* @returns a fresh JSON Schema document.
|
|
314
|
+
*/
|
|
315
|
+
toJSONSchema(key: string, params?: z.core.ToJSONSchemaParams): z.core.JSONSchema.BaseSchema
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
Types: [TypertContribution](./invariants.md) · [TypertFace](./invariants.md) · [TypertPackageFilter](./invariants.md) · [TypertPackageRecord](./invariants.md) · [TypertSchemaFilter](./invariants.md) · [TypertSchemaRecord](./invariants.md)
|
|
319
|
+
|
|
320
|
+
Source: [`packages/typert/registry/src/service.ts:446`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/typert/registry/src/service.ts)
|
|
321
|
+
|
|
322
|
+
<a id="ctxtypertgateway--typertgatewayservice"></a>
|
|
323
|
+
|
|
324
|
+
### `ctx.typertGateway` — `TypertGatewayService`
|
|
325
|
+
|
|
326
|
+
Resolve strict generated definitions or conservative SRC markers against current Cordis Services and Typert providers.
|
|
327
|
+
|
|
328
|
+
```ts cordis-catalog
|
|
329
|
+
/**
|
|
330
|
+
* Invoke one live Remote method through strict generated reflection or SRC markers.
|
|
331
|
+
* @param request - decoded endpoint and exact named wire arguments.
|
|
332
|
+
* @returns the validated business result.
|
|
333
|
+
* @throws {@link TypertGatewayError} for dispatch, provider, or boundary failures; lookup-policy and business errors retain identity.
|
|
334
|
+
*/
|
|
335
|
+
async invoke(request: InvokeRemoteRequest): Promise<unknown>
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
Source: [`packages/api/gateway/src/index.ts:90`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/api/gateway/src/index.ts)
|
|
339
|
+
<!-- END GENERATED cordis-surface -->
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
---
|
|
2
|
+
editSource: "docs/subsystems/user-questions.md"
|
|
3
|
+
outline: [2,3]
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# User Interaction
|
|
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.
|
|
9
|
+
|
|
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
|
+
|
|
12
|
+
## Question options
|
|
13
|
+
|
|
14
|
+
`AskUserQuestionOption` contains one selectable choice. `label` is the user-facing option text and also the model-facing selected value; `description` is optional UI help text.
|
|
15
|
+
|
|
16
|
+
```ts type-equiv
|
|
17
|
+
/** One selectable answer offered to the user. */
|
|
18
|
+
interface AskUserQuestionOption {
|
|
19
|
+
/** User-facing label. */
|
|
20
|
+
label: string
|
|
21
|
+
/** Optional extra context rendered by capable UIs. */
|
|
22
|
+
description?: string
|
|
23
|
+
}
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Presentation intent
|
|
27
|
+
|
|
28
|
+
`AskUserQuestionIntent` optionally declares a known decision kind. It is tagged on `kind` so intents can be added; a UI that does not recognise a tag renders the generic option list. An intent changes presentation only — a UI honouring it answers with the same option labels a generic UI would send, so the caller reads the same answer fields either way. `approve` names the affirmative option instead of relying on option order. `ask()` rejects the two assertions no type can carry: an `approve` naming none of its own question's options, and an intent on a question with no `detail`.
|
|
29
|
+
|
|
30
|
+
```ts type-equiv
|
|
31
|
+
/**
|
|
32
|
+
* A caller-declared presentation intent: the question IS this kind of
|
|
33
|
+
* decision, so a UI that recognises the tag may present it as such instead of as a
|
|
34
|
+
* generic option list. Tagged so further intents can be added; a UI that does
|
|
35
|
+
* not know a tag renders the generic flow, and the answer encoding is identical
|
|
36
|
+
* either way — an intent changes presentation only, never the protocol.
|
|
37
|
+
*/
|
|
38
|
+
type AskUserQuestionIntent = {
|
|
39
|
+
/** A plan submitted for review: `detail` is the plan markdown `ask()` requires, and the decision approves or declines it. */
|
|
40
|
+
kind: 'plan-review'
|
|
41
|
+
/**
|
|
42
|
+
* The option label that approves the plan; every other option declines it.
|
|
43
|
+
* Named rather than positional so no UI infers the verdict from option order.
|
|
44
|
+
* An `approve` naming no option of its own question is rejected at `ask()`.
|
|
45
|
+
*/
|
|
46
|
+
approve: string
|
|
47
|
+
}
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## Question item
|
|
51
|
+
|
|
52
|
+
`AskUserQuestionItem` is one question in a request. The caller supplies a stable `id`, which is echoed back with the answer so batched questions remain routable. Optional `detail` carries supporting text that providers render with the question but keep out of selectable option labels.
|
|
53
|
+
|
|
54
|
+
```ts type-equiv
|
|
55
|
+
/** One question in a user-questions request. */
|
|
56
|
+
interface AskUserQuestionItem {
|
|
57
|
+
/** Stable caller-provided question id, echoed in the answer. */
|
|
58
|
+
id: string
|
|
59
|
+
/** The question to display. */
|
|
60
|
+
question: string
|
|
61
|
+
/** Optional supporting detail rendered with the question but kept out of option labels. */
|
|
62
|
+
detail?: string
|
|
63
|
+
/** Optional short heading/group label. */
|
|
64
|
+
header?: string
|
|
65
|
+
/** Optional choices the UI can render as a menu. */
|
|
66
|
+
options?: AskUserQuestionOption[]
|
|
67
|
+
/** Whether more than one option may be selected. Defaults to single-select. */
|
|
68
|
+
multiSelect?: boolean
|
|
69
|
+
/** Optional presentation intent for capable UIs; absent asks for the generic option list. */
|
|
70
|
+
intent?: AskUserQuestionIntent
|
|
71
|
+
}
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## Ask request
|
|
75
|
+
|
|
76
|
+
`AskUserQuestionRequest` is the cross-package request. `questions` is an array so a UI can present related prompts in one flow while preserving a stable id per answer. When present, `agent` is the exact live caller; the interaction seam admits it only while the live registry identifies that instance as a runtime root.
|
|
77
|
+
|
|
78
|
+
```ts type-equiv
|
|
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
|
+
}
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
## Answer
|
|
91
|
+
|
|
92
|
+
Providers return one answer item per question id. `selected` contains selected option labels, and `custom` carries a free-form "Other" answer when the user typed one. For a single-select question, `custom` overrides the selected choice and `selected` is empty. For a multi-select question, `custom` may supplement the labels in `selected`. A UI may also use an item with empty `selected` and no `custom` to preserve a skipped question in an otherwise completed batch.
|
|
93
|
+
|
|
94
|
+
```ts type-equiv
|
|
95
|
+
/** Answer to one question. */
|
|
96
|
+
interface AskUserQuestionAnswerItem {
|
|
97
|
+
/** The answered question id. */
|
|
98
|
+
id: string
|
|
99
|
+
/** Selected option labels. May accompany custom text for a multi-select question. */
|
|
100
|
+
selected: string[]
|
|
101
|
+
/** Optional free-text "Other" answer. */
|
|
102
|
+
custom?: string
|
|
103
|
+
}
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
```ts type-equiv
|
|
107
|
+
/** The human's answer. */
|
|
108
|
+
interface AskUserQuestionAnswer {
|
|
109
|
+
/** Structured answers keyed by question id. */
|
|
110
|
+
answers: AskUserQuestionAnswerItem[]
|
|
111
|
+
}
|
|
112
|
+
```
|
|
113
|
+
|
|
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
|
+
## Errors
|
|
126
|
+
|
|
127
|
+
`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.
|
|
128
|
+
|
|
129
|
+
```ts type-equiv
|
|
130
|
+
/** Stable error taxonomy for user-questions failures. */
|
|
131
|
+
class UserQuestionError extends HarnessError {
|
|
132
|
+
constructor(message: string, code: string, options?: ErrorOptions) {
|
|
133
|
+
super(message, code, options)
|
|
134
|
+
this.name = 'UserQuestionError'
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
<!-- BEGIN GENERATED cordis-surface (gen-cordis-catalog.ts) — do not edit between markers -->
|
|
140
|
+
|
|
141
|
+
<a id="cordis-surface"></a>
|
|
142
|
+
|
|
143
|
+
## Cordis API
|
|
144
|
+
|
|
145
|
+
Generated from source by `scripts/gen-cordis-catalog.ts` (verified fresh by `pnpm run verify-cordis-catalog` in doc-sync; regenerate with `pnpm run gen-cordis-catalog`) — this section is byte-identical in both language sides of the page. Signature blocks use a `ts cordis-catalog` fence and keep the original source JSDoc; dispatch modes are defined in the [primer](../cordis-primer.md#dispatch-modes), and the framework-inherited `ctx` API lives in [cordis-api/inherited.md](../cordis-api/inherited.md).
|
|
146
|
+
|
|
147
|
+
<a id="ctxuserquestions--userquestionservice"></a>
|
|
148
|
+
|
|
149
|
+
### `ctx.userQuestions` — `UserQuestionService`
|
|
150
|
+
|
|
151
|
+
`ctx.userQuestions`: one active UI provider plus an `ask()` API.
|
|
152
|
+
|
|
153
|
+
```ts cordis-catalog
|
|
154
|
+
/**
|
|
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.
|
|
164
|
+
*
|
|
165
|
+
* When a caller supplies an agent, human interaction is valid only for the
|
|
166
|
+
* exact live runtime root. Runtime ownership, not durable session lineage,
|
|
167
|
+
* decides this boundary: an owned child has no human answerer and would
|
|
168
|
+
* block forever, while a lineage-bearing session resumed as a new runtime
|
|
169
|
+
* root may ask normally.
|
|
170
|
+
*
|
|
171
|
+
* @param request Questions, owner agent, and abort signal.
|
|
172
|
+
* @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.
|
|
176
|
+
*/
|
|
177
|
+
async ask(request: AskUserQuestionRequest): Promise<AskUserQuestionAnswer>
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
Source: [`packages/interaction/user-questions/src/index.ts:51`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/interaction/user-questions/src/index.ts)
|
|
181
|
+
<!-- END GENERATED cordis-surface -->
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
---
|
|
2
|
+
editSource: "docs/subsystems/web-server.md"
|
|
3
|
+
outline: [2,3]
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# HTTP Server
|
|
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.
|
|
9
|
+
|
|
10
|
+
Source: [`packages/host/webserver/src/index.ts`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/host/webserver/src/index.ts)
|
|
11
|
+
|
|
12
|
+
## Routes
|
|
13
|
+
|
|
14
|
+
```ts type-equiv
|
|
15
|
+
/** Route match kind: 'exact' matches the pathname verbatim; 'prefix' p matches p and p/<anything>. */
|
|
16
|
+
type WebRouteKind = 'exact' | 'prefix'
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
```ts type-equiv
|
|
20
|
+
/** One named route registration. */
|
|
21
|
+
interface WebRoute {
|
|
22
|
+
kind: WebRouteKind
|
|
23
|
+
/** Absolute pathname, no trailing slash. */
|
|
24
|
+
path: string
|
|
25
|
+
/** Owns the full response lifecycle (may hold the response open, e.g. SSE). */
|
|
26
|
+
handler: (req: IncomingMessage, res: ServerResponse) => void | Promise<void>
|
|
27
|
+
}
|
|
28
|
+
```
|
|
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, any miss falls back to `index.html` with HTTP 200 (SPA routing), and unknown extensions ship as octet-stream.
|
|
31
|
+
|
|
32
|
+
## Config
|
|
33
|
+
|
|
34
|
+
```ts type-equiv
|
|
35
|
+
/** Gateway config: the listen address. */
|
|
36
|
+
interface Config {
|
|
37
|
+
/** Listen host; the two supported values are loopback and all-interfaces. */
|
|
38
|
+
host: '127.0.0.1' | '0.0.0.0'
|
|
39
|
+
/** Listen port; zero requests an OS-assigned port. */
|
|
40
|
+
port: number
|
|
41
|
+
}
|
|
42
|
+
```
|
|
43
|
+
|
|
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.
|
|
45
|
+
|
|
46
|
+
## The service
|
|
47
|
+
|
|
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. `tapIndex(transform)` adds a pure html-to-html transform applied to every index response — `/` and each SPA fallback — in registration order; [dsh-client-modules](https://github.com/deepseek-ai/deepseek-harness/tree/master/packages/client/modules) uses it to inject the boot manifest. `port` reads the listening port, including the port assigned by the OS when `config.port` is 0.
|
|
49
|
+
|
|
50
|
+
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
|
+
|
|
52
|
+
<!-- BEGIN GENERATED cordis-surface (gen-cordis-catalog.ts) — do not edit between markers -->
|
|
53
|
+
|
|
54
|
+
<a id="cordis-surface"></a>
|
|
55
|
+
|
|
56
|
+
## Cordis API
|
|
57
|
+
|
|
58
|
+
Generated from source by `scripts/gen-cordis-catalog.ts` (verified fresh by `pnpm run verify-cordis-catalog` in doc-sync; regenerate with `pnpm run gen-cordis-catalog`) — this section is byte-identical in both language sides of the page. Signature blocks use a `ts cordis-catalog` fence and keep the original source JSDoc; dispatch modes are defined in the [primer](../cordis-primer.md#dispatch-modes), and the framework-inherited `ctx` API lives in [cordis-api/inherited.md](../cordis-api/inherited.md).
|
|
59
|
+
|
|
60
|
+
<a id="ctxwebserver--webserver"></a>
|
|
61
|
+
|
|
62
|
+
### `ctx.webServer` — `WebServer`
|
|
63
|
+
|
|
64
|
+
The browser HTTP carrier service. Activation listens immediately. Route registration order does not affect requests because configured named routes must be distinct, and the fallback handler answers anything not yet claimed during startup with 404 until its owner registers. A listen failure rejects initialization, and the boot process reports the failed fiber.
|
|
65
|
+
|
|
66
|
+
```ts cordis-catalog
|
|
67
|
+
/**
|
|
68
|
+
* Register a named route. Duplicate (kind, path) throws — route patterns are
|
|
69
|
+
* a composition-level contract, so a collision is a misconfiguration.
|
|
70
|
+
* @param route - kind, path, and the owning handler.
|
|
71
|
+
* @returns the disposer removing the route.
|
|
72
|
+
*/
|
|
73
|
+
register(route: WebRoute): () => void
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Register an exact-path HTTP upgrade route. Duplicate paths throw because
|
|
77
|
+
* one socket can have only one protocol owner.
|
|
78
|
+
* @param route - pathname and handler owning negotiation plus socket use.
|
|
79
|
+
* @returns the disposer removing the route.
|
|
80
|
+
*/
|
|
81
|
+
registerUpgrade(route: WebUpgradeRoute): () => void
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Claim the fallback seat: the handler answering every request no named
|
|
85
|
+
* route matches (the SPA dist server in the shipped Web composition). One
|
|
86
|
+
* owner only — a second registration throws, because two fallbacks cannot
|
|
87
|
+
* compose.
|
|
88
|
+
* @param handler - owns the full response lifecycle of unmatched requests.
|
|
89
|
+
* @returns the disposer releasing the seat.
|
|
90
|
+
*/
|
|
91
|
+
registerFallback(handler: WebRoute['handler']): () => void
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Register an index.html transform, applied by the fallback owner to every
|
|
95
|
+
* index response ({@link applyIndexTaps}) in registration order.
|
|
96
|
+
* @param transform - pure html-to-html function.
|
|
97
|
+
* @returns the disposer removing the transform.
|
|
98
|
+
*/
|
|
99
|
+
tapIndex(transform: (html: string) => string): () => void
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Run an index.html body through the registered taps in registration order
|
|
103
|
+
* — called by the fallback owner on every index response it renders.
|
|
104
|
+
* @param html - the raw index.html body.
|
|
105
|
+
* @returns the transformed body.
|
|
106
|
+
*/
|
|
107
|
+
applyIndexTaps(html: string): string
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
Source: [`packages/host/webserver/src/index.ts:59`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/host/webserver/src/index.ts)
|
|
111
|
+
<!-- END GENERATED cordis-surface -->
|