dsh-plugin-dev-kb 1.0.9 → 1.3.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/CHANGELOG.md +26 -0
- package/README.en.md +14 -8
- package/README.md +14 -8
- package/docs/banner.svg +25 -0
- package/kb/INDEX.md +8 -2
- package/kb/README.md +4 -4
- package/kb/extra/AGENTS.md +3 -3
- package/kb/extra/cookbook/adding-a-remote-api.md +197 -0
- package/kb/extra/cookbook/adding-a-remote-api.zh.md +197 -0
- package/kb/extra/cookbook/adding-a-session-format-version.md +109 -0
- package/kb/extra/cookbook/adding-a-session-format-version.zh.md +109 -0
- package/kb/extra/cookbook/adding-a-vendored-package.md +1 -1
- package/kb/extra/cookbook/adding-a-vendored-package.zh.md +1 -1
- package/kb/extra/cookbook/responding-to-pr-review-on-a-stack.md +1 -1
- package/kb/extra/cookbook/responding-to-pr-review-on-a-stack.zh.md +1 -1
- package/kb/extra/deepseek-llm-api-wire-extensions.md +19 -15
- package/kb/extra/deepseek-llm-api-wire-extensions.zh.md +19 -15
- package/kb/extra/development.md +13 -3
- package/kb/extra/development.zh.md +13 -3
- package/kb/extra/event-producer-consumer.md +49 -46
- package/kb/extra/event-producer-consumer.zh.md +52 -49
- package/kb/extra/i18n/README.md +2 -1
- package/kb/extra/i18n/README.zh.md +3 -2
- package/kb/extra/i18n/style-samples.md +2 -2
- package/kb/extra/i18n/translation-prompt.md +1 -1
- package/kb/extra/module-graph.md +493 -1002
- package/kb/extra/module-graph.zh.md +495 -1004
- package/kb/extra/rescope.md +1 -1
- package/kb/extra/rescope.zh.md +1 -1
- package/kb/extra/session-format-status.md +47 -0
- package/kb/extra/session-format-status.zh.md +47 -0
- package/kb/extra/subsystems/agent-team.md +3 -2
- package/kb/extra/subsystems/agent-team.zh.md +3 -2
- package/kb/extra/subsystems/attachment.md +157 -4
- package/kb/extra/subsystems/attachment.zh.md +157 -4
- package/kb/extra/subsystems/feedback.md +164 -30
- package/kb/extra/subsystems/feedback.zh.md +164 -30
- package/kb/extra/testing.md +9 -4
- package/kb/extra/testing.zh.md +9 -4
- package/kb/extra/web-styling.md +5 -0
- package/kb/extra/web-styling.zh.md +5 -0
- package/kb/meta/search-index.json +223 -151
- package/kb/meta/site-pages.txt +187 -181
- package/kb/meta/source.json +5 -5
- package/kb/meta/topics.md +5 -5
- package/kb/site/develop/basic/publish.md +1 -1
- package/kb/site/develop/cordis-tutorial/07-into-the-harness.md +3 -2
- package/kb/site/develop/practice/llm-adapter.md +4 -3
- package/kb/site/en/develop/basic/publish.md +1 -1
- package/kb/site/en/develop/cordis-tutorial/07-into-the-harness.md +3 -2
- package/kb/site/en/develop/practice/llm-adapter.md +4 -3
- package/kb/site/en/guide/network-proxy.md +87 -0
- package/kb/site/en/guide/providers.md +70 -17
- package/kb/site/en/guide/python-sdk.md +19 -3
- package/kb/site/en/guide/schedule.md +2 -0
- package/kb/site/en/reference/agent-lifecycle.md +17 -8
- package/kb/site/en/reference/api-gateway.md +2 -2
- package/kb/site/en/reference/capability-seams.md +24 -10
- package/kb/site/en/reference/config-catalog.md +322 -264
- package/kb/site/en/reference/cookbook/adding-a-package.md +1 -2
- package/kb/site/en/reference/cookbook/adding-a-settings-card.md +10 -8
- package/kb/site/en/reference/cookbook/adding-a-tool.md +1 -1
- package/kb/site/en/reference/cookbook/extension-cookbook.md +16 -13
- package/kb/site/en/reference/index.md +26 -9
- package/kb/site/en/reference/persistence-catalog.md +189 -95
- package/kb/site/en/reference/subsystems/approval.md +1 -1
- package/kb/site/en/reference/subsystems/client-modules.md +9 -0
- package/kb/site/en/reference/subsystems/client-resources.md +94 -0
- package/kb/site/en/reference/subsystems/code-runtime.md +7 -3
- package/kb/site/en/reference/subsystems/commands.md +25 -16
- package/kb/site/en/reference/subsystems/compaction.md +10 -10
- package/kb/site/en/reference/subsystems/conversation.md +11 -4
- package/kb/site/en/reference/subsystems/core.md +173 -29
- package/kb/site/en/reference/subsystems/credentials.md +5 -4
- package/kb/site/en/reference/subsystems/filesystem.md +15 -0
- package/kb/site/en/reference/subsystems/goal.md +38 -2
- package/kb/site/en/reference/subsystems/index.md +3 -1
- package/kb/site/en/reference/subsystems/invariants.md +1 -1
- package/kb/site/en/reference/subsystems/llm-streaming.md +43 -12
- package/kb/site/en/reference/subsystems/lsp.md +1 -1
- package/kb/site/en/reference/subsystems/permission-presets.md +5 -5
- package/kb/site/en/reference/subsystems/persistence.md +211 -199
- package/kb/site/en/reference/subsystems/plan.md +3 -3
- package/kb/site/en/reference/subsystems/sandbox.md +2 -0
- package/kb/site/en/reference/subsystems/schedule.md +9 -3
- package/kb/site/en/reference/subsystems/scope.md +1 -1
- package/kb/site/en/reference/subsystems/session-projection.md +50 -24
- package/kb/site/en/reference/subsystems/session-query.md +19 -13
- package/kb/site/en/reference/subsystems/session-reference.md +29 -1
- package/kb/site/en/reference/subsystems/session-telemetry.md +27 -10
- package/kb/site/en/reference/subsystems/session-title.md +6 -6
- package/kb/site/en/reference/subsystems/session.md +208 -103
- package/kb/site/en/reference/subsystems/settings.md +30 -12
- package/kb/site/en/reference/subsystems/shell.md +9 -6
- package/kb/site/en/reference/subsystems/sidebar-right.md +151 -0
- package/kb/site/en/reference/subsystems/skills.md +1 -1
- package/kb/site/en/reference/subsystems/slots.md +41 -32
- package/kb/site/en/reference/subsystems/spill.md +13 -6
- package/kb/site/en/reference/subsystems/storage.md +28 -5
- package/kb/site/en/reference/subsystems/subagent.md +58 -105
- package/kb/site/en/reference/subsystems/subprocess.md +25 -26
- package/kb/site/en/reference/subsystems/system-prompt.md +18 -3
- package/kb/site/en/reference/subsystems/token-meter.md +4 -3
- package/kb/site/en/reference/subsystems/tools.md +5 -5
- package/kb/site/en/reference/subsystems/typert.md +18 -18
- package/kb/site/en/reference/subsystems/web-client.md +1 -1
- package/kb/site/en/reference/subsystems/workspace.md +88 -9
- package/kb/site/en/reference/tool-catalog.md +56 -66
- package/kb/site/en/reference/tool-execution-pipeline.md +2 -2
- package/kb/site/guide/network-proxy.md +87 -0
- package/kb/site/guide/providers.md +70 -17
- package/kb/site/guide/python-sdk.md +19 -3
- package/kb/site/guide/schedule.md +2 -0
- package/kb/site/reference/agent-lifecycle.md +17 -8
- package/kb/site/reference/api-gateway.md +2 -2
- package/kb/site/reference/capability-seams.md +24 -10
- package/kb/site/reference/config-catalog.md +321 -263
- package/kb/site/reference/cookbook/adding-a-package.md +1 -2
- package/kb/site/reference/cookbook/adding-a-settings-card.md +10 -8
- package/kb/site/reference/cookbook/adding-a-tool.md +1 -1
- package/kb/site/reference/cookbook/extension-cookbook.md +16 -13
- package/kb/site/reference/index.md +26 -9
- package/kb/site/reference/persistence-catalog.md +190 -96
- package/kb/site/reference/subsystems/approval.md +1 -1
- package/kb/site/reference/subsystems/client-modules.md +9 -0
- package/kb/site/reference/subsystems/client-resources.md +94 -0
- package/kb/site/reference/subsystems/code-runtime.md +7 -3
- package/kb/site/reference/subsystems/commands.md +25 -16
- package/kb/site/reference/subsystems/compaction.md +10 -10
- package/kb/site/reference/subsystems/conversation.md +11 -4
- package/kb/site/reference/subsystems/core.md +173 -29
- package/kb/site/reference/subsystems/credentials.md +5 -4
- package/kb/site/reference/subsystems/filesystem.md +15 -0
- package/kb/site/reference/subsystems/goal.md +38 -2
- package/kb/site/reference/subsystems/index.md +14 -12
- package/kb/site/reference/subsystems/invariants.md +1 -1
- package/kb/site/reference/subsystems/llm-streaming.md +43 -12
- package/kb/site/reference/subsystems/lsp.md +1 -1
- package/kb/site/reference/subsystems/permission-presets.md +5 -5
- package/kb/site/reference/subsystems/persistence.md +210 -198
- package/kb/site/reference/subsystems/plan.md +3 -3
- package/kb/site/reference/subsystems/schedule.md +9 -3
- package/kb/site/reference/subsystems/scope.md +1 -1
- package/kb/site/reference/subsystems/session-projection.md +50 -24
- package/kb/site/reference/subsystems/session-query.md +19 -13
- package/kb/site/reference/subsystems/session-reference.md +29 -1
- package/kb/site/reference/subsystems/session-telemetry.md +27 -10
- package/kb/site/reference/subsystems/session-title.md +6 -6
- package/kb/site/reference/subsystems/session.md +208 -103
- package/kb/site/reference/subsystems/settings.md +30 -12
- package/kb/site/reference/subsystems/shell.md +9 -6
- package/kb/site/reference/subsystems/sidebar-right.md +151 -0
- package/kb/site/reference/subsystems/skills.md +1 -1
- package/kb/site/reference/subsystems/slots.md +41 -32
- package/kb/site/reference/subsystems/spill.md +13 -6
- package/kb/site/reference/subsystems/storage.md +28 -5
- package/kb/site/reference/subsystems/subagent.md +58 -105
- package/kb/site/reference/subsystems/subprocess.md +25 -26
- package/kb/site/reference/subsystems/system-prompt.md +18 -3
- package/kb/site/reference/subsystems/token-meter.md +4 -3
- package/kb/site/reference/subsystems/tools.md +5 -5
- package/kb/site/reference/subsystems/typert.md +18 -18
- package/kb/site/reference/subsystems/web-client.md +1 -1
- package/kb/site/reference/subsystems/workspace.md +88 -9
- package/kb/site/reference/tool-catalog.md +55 -65
- package/kb/site/reference/tool-execution-pipeline.md +2 -2
- package/lib/index.js +25 -44
- package/package.json +3 -2
- package/skills/dsh-plugin-dev-kb.md +4 -4
|
@@ -200,8 +200,22 @@ prepareDocument(): Promise<string | undefined>
|
|
|
200
200
|
* @param schema - schemastery schema resolving this namespace's value.
|
|
201
201
|
* @param options - composition `base` layer and effect timing.
|
|
202
202
|
* @returns the owner scope for reads, observation, and updates.
|
|
203
|
+
* @throws {TypeError} when `ns` is not a lowercase hyphenated identifier.
|
|
203
204
|
*/
|
|
204
|
-
register<T>(ns:
|
|
205
|
+
register<const Namespace extends string, T>( ns: Namespace & SettingsNamespaceInput<Namespace>, schema: z<T>, options?: SettingsRegisterOptions<T>, ): SettingsScope<T>
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Attach one optional-settings consumer to this provider. The consumer
|
|
209
|
+
* registers its composition entry as the base layer while this provider is
|
|
210
|
+
* present, then falls back to that entry if the provider detaches.
|
|
211
|
+
* @param owner - consumer context whose unload suppresses fallback work.
|
|
212
|
+
* @param ns - consumer-owned settings namespace.
|
|
213
|
+
* @param schema - schema resolving the namespace.
|
|
214
|
+
* @param entry - composition entry used as the base and fallback value.
|
|
215
|
+
* @param hooks - source sink, change notification, and optional validation.
|
|
216
|
+
* @throws {TypeError} when `ns` is not a lowercase hyphenated identifier.
|
|
217
|
+
*/
|
|
218
|
+
installSection<const Namespace extends string, T>( owner: Context, ns: Namespace & SettingsNamespaceInput<Namespace>, schema: z<T>, entry: T, hooks: SettingsSectionHooks<T>, ): void
|
|
205
219
|
|
|
206
220
|
/**
|
|
207
221
|
* Describe every registered namespace for configuration surfaces, including
|
|
@@ -216,8 +230,9 @@ describe(options?: SettingsDescribeOptions): SettingsDescriptor[]
|
|
|
216
230
|
* Read one registered namespace's resolved value.
|
|
217
231
|
* @param ns - the namespace to read.
|
|
218
232
|
* @returns the resolved value, or `undefined` while unregistered.
|
|
233
|
+
* @throws {TypeError} when `ns` is not a lowercase hyphenated identifier.
|
|
219
234
|
*/
|
|
220
|
-
get(ns:
|
|
235
|
+
get<const Namespace extends string>(ns: Namespace & SettingsNamespaceInput<Namespace>): unknown
|
|
221
236
|
|
|
222
237
|
/**
|
|
223
238
|
* Merge a patch into one registered namespace's user layer, validate the
|
|
@@ -229,8 +244,9 @@ get(ns: SettingsNamespace): unknown
|
|
|
229
244
|
* @param patch - plain-object patch over the user section.
|
|
230
245
|
* @param expectedRevision - the descriptor `revision` the caller read; a
|
|
231
246
|
* namespace that moved past it rejects with {@link SettingsConflictError}.
|
|
247
|
+
* @throws {TypeError} when `ns` is not a lowercase hyphenated identifier.
|
|
232
248
|
*/
|
|
233
|
-
async update(ns:
|
|
249
|
+
async update<const Namespace extends string>( ns: Namespace & SettingsNamespaceInput<Namespace>, patch: object, expectedRevision?: number, ): Promise<void>
|
|
234
250
|
|
|
235
251
|
/**
|
|
236
252
|
* Replace one registered namespace's user section wholesale, validate,
|
|
@@ -241,8 +257,9 @@ async update(ns: SettingsNamespace, patch: object, expectedRevision?: number): P
|
|
|
241
257
|
* @param section - the complete next user section.
|
|
242
258
|
* @param expectedRevision - the descriptor `revision` the caller read; a
|
|
243
259
|
* namespace that moved past it rejects with {@link SettingsConflictError}.
|
|
260
|
+
* @throws {TypeError} when `ns` is not a lowercase hyphenated identifier.
|
|
244
261
|
*/
|
|
245
|
-
async replace(ns:
|
|
262
|
+
async replace<const Namespace extends string>( ns: Namespace & SettingsNamespaceInput<Namespace>, section: object, expectedRevision?: number, ): Promise<void>
|
|
246
263
|
|
|
247
264
|
/**
|
|
248
265
|
* Apply path-addressed edits to one registered namespace's user section,
|
|
@@ -255,8 +272,9 @@ async replace(ns: SettingsNamespace, section: object, expectedRevision?: number)
|
|
|
255
272
|
* @param ops - ordered path edits; later ops observe earlier ones.
|
|
256
273
|
* @param expectedRevision - the descriptor `revision` the caller read; a
|
|
257
274
|
* namespace that moved past it rejects with {@link SettingsConflictError}.
|
|
275
|
+
* @throws {TypeError} when `ns` is not a lowercase hyphenated identifier.
|
|
258
276
|
*/
|
|
259
|
-
async mutate(ns:
|
|
277
|
+
async mutate<const Namespace extends string>( ns: Namespace & SettingsNamespaceInput<Namespace>, ops: readonly SettingsPathOp[], expectedRevision?: number, ): Promise<void>
|
|
260
278
|
```
|
|
261
279
|
|
|
262
280
|
Source: [`packages/settings/settings/src/index.ts`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/settings/settings/src/index.ts)
|
|
@@ -265,14 +283,14 @@ Source: [`packages/settings/settings/src/index.ts`](https://github.com/deepseek-
|
|
|
265
283
|
|
|
266
284
|
### `ctx.settingsController` — `SettingsController`
|
|
267
285
|
|
|
268
|
-
Host service backing the generated `ctx.remote.settings` namespace. Every remote read uses `redactSecrets: true`, so a `role('secret')` field cannot ride a response. Writes expose the settings service's merge, replacement, and path-addressed operations, and classify every provider refusal as `settings
|
|
286
|
+
Host service backing the generated `ctx.remote.settings` namespace. Every remote read uses `redactSecrets: true`, so a `role('secret')` field cannot ride a response. Writes expose the settings service's merge, replacement, and path-addressed operations, and classify every provider refusal as `settings/conflict` or `settings/rejected` with the service's message.
|
|
269
287
|
|
|
270
288
|
```ts cordis-catalog
|
|
271
289
|
/**
|
|
272
290
|
* Describe every registered namespace for a configuration page: redacted
|
|
273
291
|
* layered values plus the serialized schema the page renders its form from.
|
|
274
292
|
* @returns provider writability, local-document presence, and one view per namespace.
|
|
275
|
-
* @throws
|
|
293
|
+
* @throws RemoteError when no settings provider is mounted.
|
|
276
294
|
*/
|
|
277
295
|
@Remote describe(): SettingsDescribeValue
|
|
278
296
|
|
|
@@ -288,7 +306,7 @@ Host service backing the generated `ctx.remote.settings` namespace. Every remote
|
|
|
288
306
|
* @param patch - fields to merge into the user section.
|
|
289
307
|
* @param expectedRevision - revision the caller read; `undefined` writes unconditionally.
|
|
290
308
|
* @returns the namespace's redacted view after the write.
|
|
291
|
-
* @throws
|
|
309
|
+
* @throws RemoteError when the request is invalid, no provider is mounted, or the provider refuses the write.
|
|
292
310
|
*/
|
|
293
311
|
@Remote update( ns: string, patch: Record<string, JsonValue>, expectedRevision: number | undefined, ): Promise<SettingsNamespaceView>
|
|
294
312
|
|
|
@@ -298,7 +316,7 @@ Host service backing the generated `ctx.remote.settings` namespace. Every remote
|
|
|
298
316
|
* @param section - complete replacement user section.
|
|
299
317
|
* @param expectedRevision - revision the caller read; `undefined` writes unconditionally.
|
|
300
318
|
* @returns the namespace's redacted view after the write.
|
|
301
|
-
* @throws
|
|
319
|
+
* @throws RemoteError when the request is invalid, no provider is mounted, or the provider refuses the write.
|
|
302
320
|
*/
|
|
303
321
|
@Remote replace( ns: string, section: Record<string, JsonValue>, expectedRevision: number | undefined, ): Promise<SettingsNamespaceView>
|
|
304
322
|
|
|
@@ -310,7 +328,7 @@ Host service backing the generated `ctx.remote.settings` namespace. Every remote
|
|
|
310
328
|
* @param ops - the edits to apply, in order.
|
|
311
329
|
* @param expectedRevision - revision the caller read; `undefined` writes unconditionally.
|
|
312
330
|
* @returns the namespace's redacted view after the write.
|
|
313
|
-
* @throws
|
|
331
|
+
* @throws RemoteError when the request is invalid, no provider is mounted, or the provider refuses the write.
|
|
314
332
|
*/
|
|
315
333
|
@Remote async mutate( ns: string, ops: SettingsPathOpView[], expectedRevision: number | undefined, ): Promise<SettingsNamespaceView>
|
|
316
334
|
|
|
@@ -318,7 +336,7 @@ Host service backing the generated `ctx.remote.settings` namespace. Every remote
|
|
|
318
336
|
* Materialize the provider-owned settings document and open it in a native text editor.
|
|
319
337
|
* @param signal - caller lifetime; abort terminates preparation or the native command.
|
|
320
338
|
* @returns confirmation after the native opener accepts the document.
|
|
321
|
-
* @throws
|
|
339
|
+
* @throws RemoteError when no document exists, preparation fails, or opening fails.
|
|
322
340
|
*/
|
|
323
341
|
@Remote async openSettingsDocument(signal: AbortSignal): Promise<SettingsDocumentOpenValue>
|
|
324
342
|
|
|
@@ -327,7 +345,7 @@ Host service backing the generated `ctx.remote.settings` namespace. Every remote
|
|
|
327
345
|
* @param agentPreset - preset id resolved against Host-owned roots.
|
|
328
346
|
* @param signal - caller lifetime; abort terminates the native command.
|
|
329
347
|
* @returns an opened confirmation or the resolved directory for text display.
|
|
330
|
-
* @throws
|
|
348
|
+
* @throws RemoteError when the preset is missing, read-only, invalid, or cannot be opened.
|
|
331
349
|
*/
|
|
332
350
|
@Remote async openAgentPresetDirectory( agentPreset: string, signal: AbortSignal, ): Promise<AgentPresetDirectoryOpenValue>
|
|
333
351
|
```
|
|
@@ -5,7 +5,7 @@ outline: [2,3]
|
|
|
5
5
|
|
|
6
6
|
# Bash Executor
|
|
7
7
|
|
|
8
|
-
The bash execution seam is split across a Service Definition ([dsh-shell](https://github.com/deepseek-ai/deepseek-harness/tree/master/packages/shell/shell), `ctx.shell`), Service Providers ([dsh-bash-local](https://github.com/deepseek-ai/deepseek-harness/tree/master/packages/shell/bash-local) and [dsh-bash-sandbox](https://github.com/deepseek-ai/deepseek-harness/tree/master/packages/shell/bash-sandbox)), and Consumer ([dsh-tool-bash](https://github.com/deepseek-ai/deepseek-harness/tree/master/packages/shell/tool-bash), the `bash` schema). Generic background-job ids, ownership, and controls live in [jobs.md](./jobs.md); this seam returns a task-free process handle.
|
|
8
|
+
The bash execution seam is split across a Service Definition ([dsh-shell](https://github.com/deepseek-ai/deepseek-harness/tree/master/packages/shell/shell), `ctx.shell`), Service Providers ([dsh-bash-local](https://github.com/deepseek-ai/deepseek-harness/tree/master/packages/shell/bash-local) and [dsh-bash-sandbox](https://github.com/deepseek-ai/deepseek-harness/tree/master/packages/shell/bash-sandbox)), and Consumer ([dsh-tool-bash](https://github.com/deepseek-ai/deepseek-harness/tree/master/packages/shell/tool-bash), the `bash` schema). Generic background-job ids, ownership, and controls live in [jobs.md](./jobs.md); this seam returns a task-free process handle. Managed-range mechanics live behind the [subprocess seam](./subprocess.md).
|
|
9
9
|
|
|
10
10
|
Source: [`packages/shell/shell/src/types.ts`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/shell/shell/src/types.ts)
|
|
11
11
|
|
|
@@ -101,7 +101,7 @@ interface ShellExecSpec {
|
|
|
101
101
|
}
|
|
102
102
|
```
|
|
103
103
|
|
|
104
|
-
`stdin` and `env` are trusted in-process plugin inputs and are not exposed by `dsh-tool-bash`. The local executor scrubs ambient credentials before merging explicit caller-supplied env.
|
|
104
|
+
`stdin` and `env` are trusted in-process plugin inputs and are not exposed by `dsh-tool-bash`. The local executor scrubs ambient credentials before merging explicit caller-supplied env.
|
|
105
105
|
|
|
106
106
|
`stdoutMaxBytes` is also trusted-plugin-only. It lets a foreground consumer request complete stdout up to a bounded parser budget without changing stderr, background jobs, or the model-facing bash tool's ordinary output cap.
|
|
107
107
|
|
|
@@ -169,7 +169,7 @@ The `SANDBOX_UNAVAILABLE` error code (owned by the [sandbox seam](./sandbox.md))
|
|
|
169
169
|
|
|
170
170
|
## Background processes: `ShellProcess`
|
|
171
171
|
|
|
172
|
-
`start()` returns a handle with no id or owner. `dsh-tool-bash` adapts it into `ctx.jobs.start()` hooks; the generic runtime then owns job identity and lifecycle. `done` resolves when the process
|
|
172
|
+
`start()` returns a handle with no id or owner. `dsh-tool-bash` adapts it into `ctx.jobs.start()` hooks; the generic runtime then owns job identity and lifecycle. `done` resolves when the underlying process settles and never rejects; a subprocess provider rejection becomes a `killed` process with a stage-neutral error on stderr. Reads remain valid after settlement, and sandbox facts are stamped before `done` resolves.
|
|
173
173
|
|
|
174
174
|
```ts type-equiv
|
|
175
175
|
/**
|
|
@@ -185,7 +185,10 @@ interface ShellProcess {
|
|
|
185
185
|
exitCode: number | null
|
|
186
186
|
/** Terminating signal name, when signal-killed. */
|
|
187
187
|
signal: NodeJS.Signals | null
|
|
188
|
-
/**
|
|
188
|
+
/**
|
|
189
|
+
* Resolves when the underlying process settles (never rejects — provider
|
|
190
|
+
* rejection settles as `killed` with a stage-neutral error on stderr).
|
|
191
|
+
*/
|
|
189
192
|
readonly done: Promise<void>
|
|
190
193
|
/** Sandbox facts, stamped once a confined process settles. */
|
|
191
194
|
sandbox?: ShellSandboxInfo
|
|
@@ -196,7 +199,7 @@ interface ShellProcess {
|
|
|
196
199
|
*/
|
|
197
200
|
readOutput(): ShellProcessRead
|
|
198
201
|
/**
|
|
199
|
-
*
|
|
202
|
+
* Terminate the provider-managed range. Returns false when it had already finished
|
|
200
203
|
* (no-op); idempotent.
|
|
201
204
|
*/
|
|
202
205
|
kill(): boolean
|
|
@@ -221,7 +224,7 @@ interface ShellProcessRead {
|
|
|
221
224
|
|
|
222
225
|
## The service
|
|
223
226
|
|
|
224
|
-
`ShellExecutor` owns `resolve`, foreground `run`, background-process `start`, and the `sandboxMode` capability fact. `dsh-bash-local` owns command defaulting, timeout/abort classification, the terminal environment, and the background read merge;
|
|
227
|
+
`ShellExecutor` owns `resolve`, foreground `run`, background-process `start`, and the `sandboxMode` capability fact. `dsh-bash-local` owns command defaulting, timeout/abort classification, the terminal environment, and the background read merge; managed-range termination, bounded collectors, spill files, credential scrubbing, and disposal quiescence are the [subprocess service](./subprocess.md)'s. `dsh-tool-bash` owns model-facing rendering and adapts background handles into the [generic job runtime](./jobs.md). `dsh-shell` owns the shell tools' shared exit-status contract: the exported `parseExitStatus`/`ParsedExitStatus` inverts the `[exit code: N]` / `[killed by signal: X]` markers `dsh-tool-bash`'s `renderResult` and `dsh-tool-pwsh`'s `renderPwshResult` append, and both tools' `presentResult` use it to split the rendered text into the terminal card's output body and its exit-status pill.
|
|
225
228
|
|
|
226
229
|
<!-- BEGIN GENERATED cordis-surface (gen-cordis-catalog.ts) — do not edit between markers -->
|
|
227
230
|
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
---
|
|
2
|
+
editSource: "docs/subsystems/sidebar-right.md"
|
|
3
|
+
outline: [2,3]
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Right Sidebar
|
|
7
|
+
|
|
8
|
+
The right Sidebar is the Web Client's per-Session docking surface: a column of panes and tabs beside the conversation in which addressed content — a workspace file, a directory tree, the product's own pages — opens, splits, floats, and closes. [`dsh-client-ui-sidebar-right`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/client/ui-sidebar-right/README.md) owns the surface, the tab-type registry, and the navigation service; [`dsh-client-ui-dockkit`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/client/ui-dockkit/README.md) is its internal layout engine; [`dsh-client-resources`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/client/resources/README.md) turns addresses into live values for any component; [`dsh-api-workspace-files`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/api/workspace-files/README.md) provides both the Host workspace service and the Client `file` resource provider.
|
|
9
|
+
|
|
10
|
+
This page is the reference for the subsystem's contracts: addresses, tab-type registration, the navigation service, the extension slots and their owner props, the resource model, the Workspace Files service, the shipped types, and what is deliberately not built. How the layout engine, the frame, and the surface fit together is in the [Agent Note](https://github.com/deepseek-ai/deepseek-harness/blob/master/.agents/notes/implemented/feature/2026-09-04-right-sidebar-docking-infrastructure.md); slot mechanics are in the [Slots reference](./slots.md).
|
|
11
|
+
|
|
12
|
+
## Position and ownership
|
|
13
|
+
|
|
14
|
+
One docking surface exists per Session, held in a session-scoped slot store and drawn by `rightbar.session`. The root-scoped `rightbar` controller mounts that seat only while Conversation is selected; a reload returns every session to the collapsed default, and switching sessions keeps each surface where it was ([state](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/client/ui-sidebar-right/README.md#state)). The surface's every change is one recorded history entry computed by the kit's pure planners; a docked pane never stays empty, and an empty root pane receives the default page selected from registered guide entries.
|
|
15
|
+
|
|
16
|
+
A tab type is two registrations that share the definition's `id`: a static definition in `ctx.sidebarRightTabs` saying which addresses its `kind` opens, and a keyed slot registration supplying its body. The framework injects `useTabInfo()` for live Sidebar, pane and tab information; each type keeps its own state in its slot store. Packages import each other's declarations only as types.
|
|
17
|
+
|
|
18
|
+
| Package | Role |
|
|
19
|
+
|---|---|
|
|
20
|
+
| [`client/ui-sidebar-right`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/client/ui-sidebar-right/README.md) | The panel and rail seats, the layout store, `ctx.sidebarRightTabs`, `ctx.sidebarRight`, the Tab domain, the guide type |
|
|
21
|
+
| [`client/ui-dockkit`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/client/ui-dockkit/README.md) | Pure layout engine and React surface; an internal dependency of `ui-sidebar-right`, not a stable interface |
|
|
22
|
+
| [`client/resources`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/client/resources/README.md) | `ctx.resources`, `useResource`, the protocol → value roster `ResourceProtocolMap` |
|
|
23
|
+
| [`api/workspace-files`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/api/workspace-files/README.md) | Host `ctx.workspaceFiles`, the `workspaceFiles` Remote namespace, and the Client `file` resource provider |
|
|
24
|
+
| [`util/workspace-path`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/util/workspace-path/README.md) | The file address grammar: `fileAddressFor`, `parseFileAddress` |
|
|
25
|
+
| [`client/ui-sidebar-documentpreview`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/client/ui-sidebar-documentpreview/README.md), [`client/ui-sidebar-files`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/client/ui-sidebar-files/README.md) | The shipped `text` and `files` types |
|
|
26
|
+
|
|
27
|
+
## Addresses
|
|
28
|
+
|
|
29
|
+
Every tab is opened by an address string, and the address is the tab's content identity. Two families exist.
|
|
30
|
+
|
|
31
|
+
A **resource address** is a `dsh-resource://<type>/…` URL. The host names the resource protocol — the key of `ResourceProtocolMap` — and everything after it is the protocol's own path; one scheme serves every protocol, so adding a protocol adds a host, never a scheme. The `file` protocol's path opens with its scope: `session/<sessionId>` followed by the path relative to that session's workspace root (`dsh-resource://file/session/abc/src/notes.txt`), or `absolute` followed by the absolute path with its leading `/` dropped (`dsh-resource://file/absolute/home/ys/notes.txt`, `dsh-resource://file/absolute/C:/x/y.txt` on Windows). Every id and path segment is component-encoded, with `:` kept literal for drive letters. `fileAddressFor(sessionId, cwd, path)` builds one — a relative path or an absolute path inside the workspace becomes `session`-relative, any other absolute path becomes `absolute` — and `parseFileAddress(address)` reads it back or returns `undefined` ([grammar](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/util/workspace-path/README.md)).
|
|
32
|
+
|
|
33
|
+
A **page address** is what the Sidebar records for a tab opened by kind rather than by resource: `sidebar://<kind>`, written by the Sidebar itself when `openTab(kind)` runs. Callers never build one — the guide and the file tree are opened as `openTab('guide')` and `openTab('files')` — and no other navigation address exists ([not built](#not-built)).
|
|
34
|
+
|
|
35
|
+
Tab identity is the pair `(kind, address)`: the registry's claim uses the address verbatim as the record's `contentId`, so opening the same address through the same type finds the existing tab, and the same address through two types is two tabs.
|
|
36
|
+
|
|
37
|
+
## Tab-type registration
|
|
38
|
+
|
|
39
|
+
`ctx.sidebarRightTabs.register(definition)` registers one implementation of a type for the caller's lifetime and returns the disposer; the caller holds it inside its own `ctx.effect`, so an implementation lives exactly as long as the plugin that contributed it, and a second registration of the same `id` throws ([extension seats](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/client/ui-sidebar-right/README.md#extension-seats)). The definition is static: no runtime hook, nothing per tab or per session.
|
|
40
|
+
|
|
41
|
+
| Field | Meaning |
|
|
42
|
+
|---|---|
|
|
43
|
+
| `id` | The implementation's identity, unique across every registration; a package name is the natural value (`@deepseek-ai/dsh-client-ui-sidebar-files`). It is the key the body and title register under. |
|
|
44
|
+
| `kind` | The type's discriminator: what its tabs are, and what `openTab` names. Not unique — an extension may take over a builtin's kind. The shipped kinds are `guide`, `text`, `files`. |
|
|
45
|
+
| `patterns` | Optional resource-address globs the type recognizes; a page type opened by kind omits them. A pattern containing `:` matches the whole address (`dsh-resource://file/**`); one without matches the URL's path at any depth (`*.md`), and an address that is not a URL matches no such pattern. Matching is case-insensitive and does not hide dotfiles; the syntax is picomatch's POSIX dialect. |
|
|
46
|
+
| `priority` | One of three literal bands: `extension` (the default and the highest: a type from outside the product outranks every shipped viewer), `builtin` (types shipped with the product), `fallback` (plain-content viewers anything more specific should beat). |
|
|
47
|
+
| `canOpen(address)` | Optional synchronous veto of a glob match; it runs on every routing decision. |
|
|
48
|
+
| `title(address)` | The chip's text, captured into the layout record when the tab opens and never rewritten. |
|
|
49
|
+
| `guide` | Optional entry boxes for the guide page: `{ order, title(), description?(), icon? }`. Picking a box opens the contributing type as a page; omit to stay off the page. |
|
|
50
|
+
|
|
51
|
+
Routing is a ranked claim. `candidates(address)` ranks the types whose patterns match and whose `canOpen` does not veto: by band, then by the length of the longest matched pattern, then by registration order. `claim(address, kind?)` picks the first candidate, or the named `kind` outright — its globs are skipped, its `canOpen` still applies — and returns `{ kind, contentId: address, title }`. An address no type claims throws: it is a wiring mistake, not a user error.
|
|
52
|
+
|
|
53
|
+
One `kind` may carry one `builtin` and one `extension` registration at the same time. The extension is the one in force for claims, `get(kind)`, `openTab(kind)`, and the guide page, and the seat finds a tab's body and title under the definition in force's `id`, so no slot priority is involved; when the extension unregisters, the builtin resumes. Every other collision on a kind, and every duplicate `id`, throws.
|
|
54
|
+
|
|
55
|
+
```ts ignore-check
|
|
56
|
+
import type { Context } from '@deepseek-ai/cordis'
|
|
57
|
+
import type {} from '@deepseek-ai/dsh-client-ui-sidebar-right/client'
|
|
58
|
+
|
|
59
|
+
export const inject = ['sidebarRightTabs', 'slots']
|
|
60
|
+
|
|
61
|
+
export function apply(ctx: Context): void {
|
|
62
|
+
ctx.effect(() => ctx.sidebarRightTabs.register({
|
|
63
|
+
id: '@acme/dsh-client-ui-image',
|
|
64
|
+
kind: 'image',
|
|
65
|
+
patterns: ['*.png', '*.jpg', '*.gif', '*.svg'],
|
|
66
|
+
canOpen: address => address.startsWith('dsh-resource://file/'),
|
|
67
|
+
title: address => address.slice(address.lastIndexOf('/') + 1),
|
|
68
|
+
}), 'image type')
|
|
69
|
+
ctx.effect(() => ctx.slots.inject('sidebar.right.pane.tab', () => ctx.slots.register(
|
|
70
|
+
{ name: 'sidebar.right.pane.tab', key: '@acme/dsh-client-ui-image' },
|
|
71
|
+
ImageBody,
|
|
72
|
+
)), 'image body')
|
|
73
|
+
}
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Navigation: `ctx.sidebarRight`
|
|
77
|
+
|
|
78
|
+
Two opens are the navigation controller, and every way into the column calls one of them: `openResource(address, options?)` for a `dsh-resource://` address — the conversation's file links, a tool row's line reference, a file tree's rows — and `openTab(kind, options?)` for a page — the strip's add control, a guide entry box. Both run four steps as one history entry — claim (the registry ranks the resource's types, or the named `kind`'s implementation in force answers); focus a tab already showing the same `(kind, address)`; otherwise seat a new tab; expand the column — and then record the navigation in the Tab domain ([service](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/client/ui-sidebar-right/README.md#ctxsidebarright)). Content the user cannot see is not opened, so a collapsed column expands in the same step. `openResource` throws for an address outside `dsh-resource://` or one no type claims; `openTab` throws for a kind nothing registered: both are wiring mistakes, not user errors.
|
|
79
|
+
|
|
80
|
+
| Option | Meaning |
|
|
81
|
+
|---|---|
|
|
82
|
+
| `paneId` | Land a new tab in this pane; default is the active docked pane (the first docked pane while a floating pane is active). |
|
|
83
|
+
| `replaceTab` | Take this tab's pane and strip slot, closing it in the same step; a floating tab lends no place, so the new tab lands as if unplaced. |
|
|
84
|
+
| `revealIfOpened` | Default `true`: a tab already showing the same `(kind, address)` is focused and handed `params`. `false` opens another tab regardless. |
|
|
85
|
+
| `kind` (`openResource` only) | Name the opening type instead of ranking claims; its implementation in force opens the address, and its `canOpen` still applies. |
|
|
86
|
+
| `params` | Navigation parameters for the body, delivered as `navigation.params`. `openResource` types them by resource type through the merge-extensible `SidebarRightResourceParamsMap` (the text preview declares `{ line?: number }`); `openTab<K>` types them by kind through `SidebarRightTabParamsMap`, `undefined` for a kind that declares none; a body reads `SidebarRightNavigationParams`, the union of both. Values are JSON-shaped by convention and not validated at run time. |
|
|
87
|
+
|
|
88
|
+
Placement is the caller's option, never a type's property. The conversation calls `openResource(fileAddressFor(sessionId, cwd, path))` and, from a `read` tool row, adds `{ params: { line } }` from the call's 1-based `offset`; a guide entry box calls `tab.actions.openTab(entry.kind, { replaceTab: true })`; a file-tree row calls `tab.actions.openResource(address)`; the strip's add control calls `openTab('guide', { paneId, revealIfOpened: false })`.
|
|
89
|
+
|
|
90
|
+
`close(tabId)` closes a tab; `active()` returns the active pane's active tab; `isExpanded()` and `toggleExpanded()` read and flip the column, the flip recorded in the sequence. Reads answer for the no-Session case with `undefined` or `false`; writes need a mounted Session surface and throw without one rather than write into a surface nobody draws.
|
|
91
|
+
|
|
92
|
+
`focus(tabId)` makes a tab its pane's active tab; `split(paneId?)` splits the active docked pane, or the named one, and returns the new pane's id — or `undefined`, recording nothing, when the pane budget or the column's width forbids a split; `float(tabId, rect?)` lifts a tab into a floating pane; `dock(paneId)` returns a floating pane to the docked area. All four run the store's existing actions and record one history entry each; a target that does not exist or is already in the requested state is a no-op, and like `open` they throw without a mounted Session surface. `TabId`, `PaneId`, `TabRecord`, and `FloatRect` are re-exported from the package's `/client` entry so a caller needs no dockkit import.
|
|
93
|
+
|
|
94
|
+
## Slots and owner props
|
|
95
|
+
|
|
96
|
+
The Sidebar declares four extension slots; its document tab declares the additional keyed document body below ([hierarchy](./slots.md)).
|
|
97
|
+
|
|
98
|
+
| Slot | Cardinality | Purpose |
|
|
99
|
+
|---|---|---|
|
|
100
|
+
| `sidebar.right.pane.tab` | keyed by the definition's `id`, Session scope | One tab's body. The seat dispatches a tab to the `id` of its kind's implementation in force, so the registrant receives every tab of its kind, docked or floating. A kind whose implementation registered no body renders the owner's "nothing can view this" notice. |
|
|
101
|
+
| `sidebar.right.pane.tab.title` | keyed by the definition's `id`, Session scope | The chip's title, with the same owner share as the body. Optional: without an entry the chip shows the `title(address)` text captured at open time; a type with a live title reads its own store here. |
|
|
102
|
+
| `sidebar.right.tab.guide` | chain, Session scope | Replaces the guide tab's contents without replacing the tab; the first non-declining entry takes the body, otherwise the shipped guide renders. |
|
|
103
|
+
| `sidebar.right.tab.menu.item` | list, Session scope | Content-level actions appended after the kit's own layout actions. An item that acts must call the owner's `dismiss()`. |
|
|
104
|
+
| `sidebar.right.tab.document` | keyed by the document implementation's `id`, Session scope | The selected file renderer inside the document tab; the parent owns shared loading and toolbar controls. |
|
|
105
|
+
|
|
106
|
+
A body, title and guide replacement receive the framework-injected `useTabInfo()`. It returns `{ sidebar, panel, tab }`: `sidebar` holds `expanded` and `fullscreen`, `panel.id` names the containing pane, and `tab` contains its record fields plus `visible`, `navigation`, `signal`, and `actions`. Docked bodies are visible only while expanded and active; docked titles need only expansion; floats stay visible. `signal` aborts when the record disappears or the plugin unloads, not on hiding or Session switching. `tab.actions` provides `openResource`, `openTab`, and `close`, bound to the tab's own Session. Open placement defaults to its current pane; `revealIfOpened` defaults to `true`, and `replaceTab: true` replaces this record in the same history entry. Menu entries retain plain `tab` and `dismiss` owner parameters.
|
|
107
|
+
|
|
108
|
+
`navigation.revision` increments on every navigation to the tab whether or not `params` changed, so a body can act on "navigated again" alone; it is `1` for a tab opened by address and `0` for a record nobody opened by address — a seeded guide, or a tab restored by undo. The Tab domain holds one occurrence per open record: a record that appears is pinned in the resource model, so switching tabs unmounts a body without dropping its content; a record that vanishes is aborted and dropped; a record restored by undo is a new occurrence ([Tab domain](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/client/ui-sidebar-right/README.md#the-tab-domain)).
|
|
109
|
+
|
|
110
|
+
## Document renderers
|
|
111
|
+
|
|
112
|
+
The `text` tab is the shared Document Preview owner. Its [root registration](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/client/ui-sidebar-documentpreview/src/client/index.ts) declares `sidebar.right.tab.document` and provides `ctx.documentPreviews`. A renderer registers `DocumentPreviewDefinition` metadata in its own effect, then waits through `ctx.slots.inject('sidebar.right.tab.document', ...)` and registers its component with `key: definition.id` and its locale namespace. Changing the renderer does not change the tab or resource address; the [extension decision](https://github.com/deepseek-ai/deepseek-harness/blob/master/.agents/notes/implemented/architecture/2026-09-08-document-preview-operations.md) separates preview policy from resource ownership.
|
|
113
|
+
|
|
114
|
+
The [registry](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/client/ui-sidebar-documentpreview/src/client/document/registry.ts) records unique `id`, `extensions`, localized `title()`, `loading`, optional `priority`, and optional `wrap`. Case-insensitive suffix matching ranks `extension` (the default) before `builtin`, then longer suffixes before shorter ones, then registration order. Unlike tab-kind replacement, the registry keeps all implementations available; the toolbar lists matching alternatives and remembers the selection per tab. Unknown extensions use plain text. `loading` is `text-pages` or `bytes-complete`; `wrap` advertises support for the shared source-wrap control.
|
|
115
|
+
|
|
116
|
+
[`DocumentPreviewProps`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/client/ui-sidebar-documentpreview/src/client/document/contract.ts) derives from `PropsRuntime<'sidebar.right.tab.document'>`. The owner supplies the original `resourceAddress`, `content`, and current `wrap`: text content is `{ kind: 'text', text, pages: [{ offset, text, lines }], eof }`, with cumulative `text`; complete bytes are `{ kind: 'bytes', data }`, with `Uint8Array<ArrayBuffer>` data. These transient buffers are borrowed read-only and must not enter durable layout or Session JSON. PDF copies the bytes before Worker transfer, preserving the owner's buffer. The child receives the same framework-bound `useTabInfo` and the global metadata-only `useResource`. The parent reads through ordinary inject callbacks to `remote.workspaceFiles.read`/`readAll` and owns page appends, per-tab refresh, and loading status. HTML's own inject callback uses `readRelated`; Host code resolves paths. Markdown and code retain one incremental renderer across appends and settle at EOF; HTML and PDF receive complete bytes.
|
|
117
|
+
|
|
118
|
+
Preview records its loaded version and the version observed when a read starts. Refresh rereads only that tab, without changing shared metadata or another tab's content. Reads are non-transactional; versions are opaque equality tokens, not ordered timestamps ([resource observation and Preview RPC](https://github.com/deepseek-ai/deepseek-harness/blob/master/.agents/notes/implemented/architecture/2026-09-08-document-preview-operations.md)).
|
|
119
|
+
|
|
120
|
+
## Resource model
|
|
121
|
+
|
|
122
|
+
The model is documented in [Client Resources](./client-resources.md); this section states what the Sidebar relies on. A resource is one address, and a resource address is a `dsh-resource://<type>/…` URL whose lower-cased host is the protocol key. The protocol's owning client package registers one provider with `ctx.resources.register(provider)` for its own lifetime; a second provider for the same protocol throws ([provide a protocol](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/client/resources/README.md#provide-a-protocol)). A provider is `{ protocol, open(address, { signal }) }`: `open` yields `RemoteResult` frames — the current state first, one frame per later change — and stops when `signal` aborts; a failure is an `{ ok: false, error }` frame, never a throw, and a throw inside the stream is a programming error the model does not catch.
|
|
123
|
+
|
|
124
|
+
`useResource<P>(address)` is a global standard prop on every slot component, whatever its scope. It returns `{ status, value, failure }`: `none` when the address's protocol has no provider or the address is not a resource address (`sidebar://guide` names no resource), `loading` until the first frame, `live` with the latest `ok` value, `failed` with the latest frame's failure beside the last value. ([read a resource](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/client/resources/README.md#read-a-resource)).
|
|
125
|
+
|
|
126
|
+
A resource stays open while it has a holder — a subscribed `useResource` or a `ctx.resources.pin(address, signal)`; the first holder opens the provider's stream, later holders share it and read the latest value at once, and the last release aborts the stream and discards the value. Streams carry metadata, not content: the `file` value is `{ absolutePath, version, bytes? }`, and a consumer reads file text itself, by page, through the Workspace Files service ([lifecycle](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/client/resources/README.md#lifecycle)).
|
|
127
|
+
|
|
128
|
+
## Workspace Files
|
|
129
|
+
|
|
130
|
+
The Host `ctx.workspaceFiles` service and generated `workspaceFiles` Remote namespace read files allowed by the Session filesystem backend: `stat(path)` returns `{ absolutePath, version, bytes? }`; `read(path, { offset?, limit? })` returns one page of lines (`offset` 1-based, `limit` capped by the configured page size) as `{ …stat, offset, text, eof }`; `readBytes(path, { offset?, length? })` returns one raw byte window (`offset` 0-based, `length` capped by the configured byte limit) as base64 `{ …stat, offset, data, eof }` with no text decoding. `list(path)` remains inside the workspace root and returns a directory's direct children (`name`, `type: 'file' | 'directory' | 'other'`, `size?`) cut to the configured cap with `truncated` set. `changes()` likewise remains workspace-scoped and yields `{ kind: 'ready' }` once subscribed, then `{ kind: 'change', change }` frames whose payload is `{ absolutePath, version }` or `{ absolutePath, absent: true }` ([README](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/api/workspace-files/README.md#use-this-package)). File operations reject final symlinks and enforce transfer caps; `read` additionally requires UTF-8 text. Failures use `workspace-file/*` codes ([failures](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/api/workspace-files/README.md)).
|
|
131
|
+
|
|
132
|
+
[`dsh-api-workspace-files`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/api/workspace-files/README.md) registers the `file` provider, with `ResourceProtocolMap.file` directly naming `WorkspaceFileStat`. A Session address carries the authorizing Session and a relative or absolute path, passed unchanged to the Host for resolution. The provider waits for Host `ready` before stat and filters changes by `stat.absolutePath`. Bare `absolute` addresses have no authorizing Session and fail with `workspace-file/unknown-workspace`, without borrowing current or Tab Session. Any UI, including Global components, shares the observation for the same complete address. Preview's ordinary Remote callbacks use the Session in that address; Host `readAll` and `readRelated` remain, and Preview's `rpc.ts` decodes byte results.
|
|
133
|
+
|
|
134
|
+
## Shipped types
|
|
135
|
+
|
|
136
|
+
- **`guide`** — `builtin`, opened as `openTab('guide')`. A muted compass sits above one capsule per contributed `guide` entry, in `order`; short lists show registered descriptions, and every missing icon uses the shipped placeholder. Picking a capsule opens the contributing type as a page in the guide tab's place. A pane holds at most one guide tab, and the strip's add control appears only while its pane has none. A new pane receives the registered default page: the sole guide entry directly, or the guide when the entry count is not one ([guide](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/client/ui-sidebar-right/README.md#the-guide)).
|
|
137
|
+
- **`text`** — `fallback`, `dsh-resource://file/**`, claiming Session addresses only. Document Preview observes metadata through `useResource<'file'>`, loads content through Remote callbacks, and owns renderer selection, the toolbar, per-tab refresh, scroll, and source navigation; unknown extensions render as plain text ([README](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/client/ui-sidebar-documentpreview/README.md)).
|
|
138
|
+
- **`files`** — `builtin`, opened as `openTab('files')`. The workspace directory tree, listed lazily through `list`, opening a file with `tab.actions.openResource(fileAddressFor(sessionId, root, path))` into its own pane ([README](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/client/ui-sidebar-files/README.md)).
|
|
139
|
+
|
|
140
|
+
<a id="not-built"></a>
|
|
141
|
+
## Not built
|
|
142
|
+
|
|
143
|
+
- Persistence: layout state is memory-only; a reload starts every session collapsed, and no session's tabs are visible from another.
|
|
144
|
+
- A read-only layout snapshot or subscription on `ctx.sidebarRight`: the service exposes operations only, and dockkit's `LayoutState`/`LayoutOp` are internal.
|
|
145
|
+
- A capability-discovery array (`features`) on the service.
|
|
146
|
+
- An `option` priority band for tab types: nothing lists a tab type without letting it claim.
|
|
147
|
+
- Retitling a record: `title(address)` is captured once; a live chip comes from the title slot, not from the record.
|
|
148
|
+
- Naming a tab implementation when opening: `openResource` names a kind at most; document-renderer selection belongs to the file tab's toolbar.
|
|
149
|
+
- An address lookup on the service (`find`): a caller opens with `revealIfOpened` and lets the surface de-duplicate.
|
|
150
|
+
- Navigation addresses beyond the Sidebar's own `sidebar://<kind>` bookkeeping; their grammar waits for the navigation controller as a whole.
|
|
151
|
+
- A user-facing undo, a content navigation stack, and tab icons ([deferred](https://github.com/deepseek-ai/deepseek-harness/blob/master/.agents/notes/implemented/feature/2026-09-04-right-sidebar-docking-infrastructure.md#deferred)).
|
|
@@ -261,7 +261,7 @@ Host service backing `ctx.remote.skills` without activating a cold Agent.
|
|
|
261
261
|
* @param request - Session identity whose cwd and preset select the catalog view.
|
|
262
262
|
* @param signal - caller lifetime carried by the Remote transport; admitted catalog reads retain their existing completion semantics.
|
|
263
263
|
* @returns user-invocable skill metadata without loading skill bodies.
|
|
264
|
-
* @throws
|
|
264
|
+
* @throws RemoteError when the Session cannot be inspected or no registry can serve it.
|
|
265
265
|
*/
|
|
266
266
|
@Remote async list(request: SkillListRequest, signal: AbortSignal): Promise<SkillListValue>
|
|
267
267
|
```
|
|
@@ -85,6 +85,7 @@ The shipped adapters add these standard props. They are available according to t
|
|
|
85
85
|
|---|---|---|
|
|
86
86
|
| every scope | `useSessions`, `useSessionPendingInteraction` | `ui-session` |
|
|
87
87
|
| every scope | `useWorkspaces` | `ui-workspace` |
|
|
88
|
+
| every scope | `usePanelInfo` | `ui-layout` |
|
|
88
89
|
| `session` | `sessionId`, `useSession`, `useProjection` | `ui-session` |
|
|
89
90
|
| `session-maybe` | optional `sessionId`, `useSession`, `useProjection` results | `ui-session` |
|
|
90
91
|
| `session` | `useConversation`, `useInput`, `inputActions` | `ui-conversation` |
|
|
@@ -115,6 +116,7 @@ root
|
|
|
115
116
|
├─ sidebar
|
|
116
117
|
│ ├─ sidebar.brand.mark
|
|
117
118
|
│ ├─ sidebar.brand.name
|
|
119
|
+
│ ├─ sidebar.panellist
|
|
118
120
|
│ ├─ sidebar.footer.action
|
|
119
121
|
│ ├─ sidebar.workspaces
|
|
120
122
|
│ │ └─ sidebar.workspaces.directoryFlow
|
|
@@ -130,38 +132,45 @@ root
|
|
|
130
132
|
│ ├─ settings.models.footer
|
|
131
133
|
│ └─ settings.plugins.tab
|
|
132
134
|
│ └─ settings.plugin.item
|
|
133
|
-
├─
|
|
134
|
-
│
|
|
135
|
-
│
|
|
136
|
-
│
|
|
137
|
-
│
|
|
138
|
-
│
|
|
139
|
-
│
|
|
140
|
-
│
|
|
141
|
-
│
|
|
142
|
-
│
|
|
143
|
-
│
|
|
144
|
-
│
|
|
145
|
-
│
|
|
146
|
-
│
|
|
147
|
-
│
|
|
148
|
-
│ ├─ conversation.
|
|
149
|
-
│
|
|
150
|
-
│
|
|
151
|
-
│
|
|
152
|
-
│
|
|
153
|
-
│
|
|
154
|
-
│ ├─ conversation.input.
|
|
155
|
-
│ ├─ conversation.input.
|
|
156
|
-
│
|
|
157
|
-
│
|
|
158
|
-
│
|
|
159
|
-
│
|
|
160
|
-
│
|
|
161
|
-
│
|
|
162
|
-
│
|
|
163
|
-
├─
|
|
164
|
-
│ └─ conversation.
|
|
135
|
+
├─ main
|
|
136
|
+
│ └─ main.conversation
|
|
137
|
+
│ ├─ conversation.session
|
|
138
|
+
│ │ └─ conversation.view
|
|
139
|
+
│ │ ├─ conversation.chat.node
|
|
140
|
+
│ │ │ ├─ conversation.chat.assistant-actions
|
|
141
|
+
│ │ │ ├─ conversation.chat.commandview
|
|
142
|
+
│ │ │ ├─ conversation.chat.turnTail
|
|
143
|
+
│ │ │ └─ tool.call.toolview
|
|
144
|
+
│ │ │ ├─ tool.call.images
|
|
145
|
+
│ │ │ └─ tool.view.cordis
|
|
146
|
+
│ │ ├─ conversation.message.images
|
|
147
|
+
│ │ └─ conversation.trajectory.images
|
|
148
|
+
│ ├─ conversation.session.header
|
|
149
|
+
│ │ ├─ conversation.session.header.lineage
|
|
150
|
+
│ │ ├─ conversation.session.header.actions
|
|
151
|
+
│ │ ├─ conversation.session.header.utilities
|
|
152
|
+
│ │ └─ conversation.session.header.corner
|
|
153
|
+
│ ├─ conversation.composer
|
|
154
|
+
│ │ └─ conversation.approval.detail
|
|
155
|
+
│ ├─ conversation.composer.bar
|
|
156
|
+
│ │ ├─ conversation.input.attachments
|
|
157
|
+
│ │ ├─ conversation.input.plan
|
|
158
|
+
│ │ └─ conversation.input.model
|
|
159
|
+
│ ├─ conversation.input.overlay
|
|
160
|
+
│ ├─ conversation.input.dock
|
|
161
|
+
│ ├─ conversation.composer.dock
|
|
162
|
+
│ ├─ conversation.input.left
|
|
163
|
+
│ ├─ conversation.input.right
|
|
164
|
+
│ ├─ conversation.hero.brand.mark
|
|
165
|
+
│ ├─ conversation.hero.workspace
|
|
166
|
+
│ │ └─ conversation.hero.workspace.directoryFlow
|
|
167
|
+
│ └─ conversation.hero.agentPreset
|
|
168
|
+
├─ rightbar
|
|
169
|
+
│ └─ rightbar.session
|
|
170
|
+
│ ├─ sidebar.right.pane.tab
|
|
171
|
+
│ │ └─ sidebar.right.tab.guide
|
|
172
|
+
│ ├─ sidebar.right.pane.tab.title
|
|
173
|
+
│ └─ sidebar.right.tab.menu.item
|
|
165
174
|
└─ shell.overlay
|
|
166
175
|
```
|
|
167
176
|
|
|
@@ -5,13 +5,13 @@ outline: [2,3]
|
|
|
5
5
|
|
|
6
6
|
# Spill Storage
|
|
7
7
|
|
|
8
|
-
The spill storage
|
|
8
|
+
The spill storage [capability seam](https://github.com/deepseek-ai/deepseek-harness/blob/master/.agents/notes/implemented/architecture/2026-07-08-tool-output-spill-files.md) persists caller-provided text and returns a model-facing locator with retrieval guidance. Its Service Definition is [dsh-spill](https://github.com/deepseek-ai/deepseek-harness/tree/master/packages/spill/spill) (`ctx.spillStore`), and its local Service Provider is [dsh-spill-local](https://github.com/deepseek-ai/deepseek-harness/tree/master/packages/spill/spill-local). Consumers include the [tool-result policy](https://github.com/deepseek-ai/deepseek-harness/tree/master/packages/spill/spill-policy) and [session references](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/context/session-reference/README.md). Spill is optional, not part of the [agent-loop spine](./core.md); consumers own preview and spill decisions, while storage saves the supplied text verbatim.
|
|
9
9
|
|
|
10
10
|
Source: [`packages/spill/spill/src/types.ts`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/spill/spill/src/types.ts)
|
|
11
11
|
|
|
12
12
|
## The save request
|
|
13
13
|
|
|
14
|
-
`saveText` is the sole service operation: persist `content` verbatim, return an opaque locator, a backend-supplied retrieval hint, and the exact byte count. The request carries the save-time storage namespace (`owner`),
|
|
14
|
+
`saveText` is the sole service operation: persist `content` verbatim, return an opaque locator, a backend-supplied retrieval hint, and the exact byte count. The request carries the save-time storage namespace (`owner`), descriptive producer provenance (`source`, never access control), and a `suggestedName` the backend may use as a naming hint, not a path. Tool provenance identifies the actual tool call; session-reference provenance identifies the captured source session, while its owner is the target session receiving the context.
|
|
15
15
|
|
|
16
16
|
```ts type-equiv
|
|
17
17
|
/** One request to persist text to a spill artifact. */
|
|
@@ -45,17 +45,24 @@ A retention-period cleanup may expire old locators with other old session artifa
|
|
|
45
45
|
|
|
46
46
|
```ts type-equiv
|
|
47
47
|
/**
|
|
48
|
-
*
|
|
49
|
-
*
|
|
50
|
-
*
|
|
48
|
+
* Producer of a spilled artifact. Tool results carry their model-issued call id;
|
|
49
|
+
* session references identify the captured source session instead. Descriptive
|
|
50
|
+
* provenance only, never access control.
|
|
51
51
|
*/
|
|
52
|
-
|
|
52
|
+
type SpillSource = {
|
|
53
|
+
kind: 'tool'
|
|
53
54
|
/** The tool whose result was spilled (e.g. `web_fetch`). */
|
|
54
55
|
toolName: string
|
|
55
56
|
/** The model-issued call id the result belongs to. */
|
|
56
57
|
callId: ToolCallId
|
|
57
58
|
/** A short human label for the artifact (e.g. `result`). */
|
|
58
59
|
label: string
|
|
60
|
+
} | {
|
|
61
|
+
kind: 'session-reference'
|
|
62
|
+
/** Session whose projected conversation was captured. */
|
|
63
|
+
sessionId: SessionId
|
|
64
|
+
/** Host-provided label for the referenced session. */
|
|
65
|
+
label: string
|
|
59
66
|
}
|
|
60
67
|
```
|
|
61
68
|
|
|
@@ -47,7 +47,7 @@ interface StorageBackend {
|
|
|
47
47
|
}
|
|
48
48
|
```
|
|
49
49
|
|
|
50
|
-
A backend owns one medium (a file-tree root, a database file) and exposes optional operation groups; `kv` is the only shipped group. `KvFacet.open(descriptor)` opens one named unit — `KvUnitDescriptor` carries the name, format version, table names, and whether a global singleton slot exists — and returns a `KvUnit` with `loadAll`, `putRecord`, `deleteRecord`, `setGlobal`, and `close`. Unit and table names must match `UNIT_NAME_RE` (safe as a file name and as a SQL identifier segment); record keys are arbitrary strings that never reach file paths. A unit does not serialize concurrent writes — ordering belongs to the caller — but each single call is atomic on the medium and durable once resolved. A medium stamped with a different version rejects `version-mismatch`;
|
|
50
|
+
A backend owns one medium (a file-tree root, a database file) and exposes optional operation groups; `kv` is the only shipped group. `KvFacet.open(descriptor)` opens one named unit — `KvUnitDescriptor` carries the name, current format version, optional compatible record versions, table names, and whether a global singleton slot exists — and returns a `KvUnit` with `loadAll`, `putRecord`, `deleteRecord`, `setGlobal`, and `close`. Unit and table names must match `UNIT_NAME_RE` (safe as a file name and as a SQL identifier segment); record keys are arbitrary strings that never reach file paths. A unit does not serialize concurrent writes — ordering belongs to the caller — but each single call is atomic on the medium and durable once resolved. A `single` medium stamped with a different version rejects `version-mismatch`; a `per-record` document stamped outside the accepted set reads as absent. A medium that cannot be parsed as the unit rejects `malformed-medium`. [`backend.ts`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/storage/storage/src/backend.ts) is the normative clause-by-clause contract, and the shared conformance suite in [`tests/contract.ts`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/storage/storage/tests/contract.ts) checks every clause against each backend. The [json backend](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/storage/storage-json/README.md) republishes one whole human-readable file per unit atomically; the [sqlite backend](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/storage/storage-sqlite/README.md) stores one document per row in one database for frequently updated data.
|
|
51
51
|
|
|
52
52
|
## Declaring a domain
|
|
53
53
|
|
|
@@ -58,16 +58,36 @@ A domain is declared once by its owning package as a spec object — the single
|
|
|
58
58
|
interface DomainSpec {
|
|
59
59
|
/** Domain name; must match `UNIT_NAME_RE` (doubles as the backend unit name). */
|
|
60
60
|
readonly name: string
|
|
61
|
-
/**
|
|
61
|
+
/** Current domain format version; reads enforce it according to the selected layout. */
|
|
62
62
|
readonly version: number
|
|
63
63
|
/**
|
|
64
64
|
* Medium layout for the backend unit: `single` (the default) stores the
|
|
65
65
|
* whole unit as one document; `per-record` stores each record as its own
|
|
66
66
|
* document, for units whose records are large, sparse, or individually
|
|
67
|
-
* disposable — the projection cache — and scopes version
|
|
68
|
-
* (
|
|
67
|
+
* disposable — the projection cache — and scopes version checks per record
|
|
68
|
+
* (an unaccepted record document is discarded, never migrated).
|
|
69
69
|
*/
|
|
70
70
|
readonly layout?: 'single' | 'per-record'
|
|
71
|
+
/**
|
|
72
|
+
* Older domain versions whose stored records the current record schemas
|
|
73
|
+
* also accept (the declaring owner vouches for that, typically by
|
|
74
|
+
* declaring the fields older records lack as optional). `per-record` backends
|
|
75
|
+
* read documents stamped with a listed version instead of discarding them,
|
|
76
|
+
* and accept a legacy whole-unit file so stamped for the one-time
|
|
77
|
+
* bootstrap; writes always stamp {@link version}.
|
|
78
|
+
*/
|
|
79
|
+
readonly compatibleVersions?: readonly number[]
|
|
80
|
+
/**
|
|
81
|
+
* What `open` does with a stored table record that fails its zod schema.
|
|
82
|
+
* Absent (the default), the whole open rejects with `invalid-record` —
|
|
83
|
+
* right for authoritative data. `'backup-and-skip'` is for domains whose
|
|
84
|
+
* records are disposable derived data: the backend moves the record's
|
|
85
|
+
* document aside (`KvUnit.backupRecord`), the failure is logged with
|
|
86
|
+
* its cause, and the open continues with the record absent. A backend
|
|
87
|
+
* without `backupRecord` (no per-record document to move) falls back
|
|
88
|
+
* to the rejecting default. The global slot always rejects.
|
|
89
|
+
*/
|
|
90
|
+
readonly invalidRecords?: 'backup-and-skip'
|
|
71
91
|
/** Optional global singleton slot. */
|
|
72
92
|
readonly global?: DomainGlobalSpec<unknown>
|
|
73
93
|
/** Table declarations keyed by table name; each name must match `UNIT_NAME_RE`. */
|
|
@@ -183,7 +203,10 @@ The mounted domain facility. Opens declared domains over routed backends; one fa
|
|
|
183
203
|
* (`facet-unsupported`); open the unit projected from the spec (backend
|
|
184
204
|
* `version-mismatch`/`malformed-medium` pass through); load and validate
|
|
185
205
|
* every stored record against the spec's zod schemas (`invalid-record`
|
|
186
|
-
* with the offending table and key
|
|
206
|
+
* with the offending table and key — unless the spec declares
|
|
207
|
+
* `invalidRecords: 'backup-and-skip'` and the unit can move documents aside, in
|
|
208
|
+
* which case the failing record is backed up, logged, and skipped);
|
|
209
|
+
* construct the domain.
|
|
187
210
|
*
|
|
188
211
|
* Lifecycle: the CALLER owns the returned handle and closes it via
|
|
189
212
|
* `Domain.close()` (typically as its own `ctx.effect` disposer) — the
|