dsh-plugin-dev-kb 1.1.0 → 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 +16 -0
- package/README.en.md +14 -8
- package/README.md +14 -8
- package/docs/banner.svg +25 -0
- package/kb/INDEX.md +6 -2
- package/kb/README.md +4 -4
- package/kb/extra/AGENTS.md +2 -2
- 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 +2 -2
- package/kb/extra/deepseek-llm-api-wire-extensions.zh.md +2 -2
- package/kb/extra/development.md +13 -3
- package/kb/extra/development.zh.md +13 -3
- package/kb/extra/event-producer-consumer.md +33 -31
- package/kb/extra/event-producer-consumer.zh.md +34 -32
- package/kb/extra/i18n/README.md +2 -1
- package/kb/extra/i18n/README.zh.md +3 -2
- package/kb/extra/i18n/translation-prompt.md +1 -1
- package/kb/extra/module-graph.md +62 -24
- package/kb/extra/module-graph.zh.md +62 -24
- 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 +1 -1
- package/kb/extra/subsystems/agent-team.zh.md +1 -1
- package/kb/extra/subsystems/feedback.md +164 -30
- package/kb/extra/subsystems/feedback.zh.md +164 -30
- package/kb/extra/testing.md +4 -3
- package/kb/extra/testing.zh.md +4 -3
- package/kb/extra/web-styling.md +1 -0
- package/kb/extra/web-styling.zh.md +1 -0
- package/kb/meta/search-index.json +151 -103
- package/kb/meta/site-pages.txt +188 -184
- package/kb/meta/source.json +5 -5
- package/kb/meta/topics.md +2 -2
- package/kb/site/develop/basic/publish.md +1 -1
- package/kb/site/en/develop/basic/publish.md +1 -1
- package/kb/site/en/guide/providers.md +1 -1
- package/kb/site/en/guide/python-sdk.md +19 -3
- package/kb/site/en/reference/agent-lifecycle.md +12 -5
- package/kb/site/en/reference/capability-seams.md +10 -3
- package/kb/site/en/reference/config-catalog.md +173 -42
- package/kb/site/en/reference/cookbook/adding-a-tool.md +1 -1
- package/kb/site/en/reference/cookbook/extension-cookbook.md +1 -1
- package/kb/site/en/reference/index.md +18 -8
- package/kb/site/en/reference/persistence-catalog.md +121 -48
- 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/compaction.md +1 -1
- package/kb/site/en/reference/subsystems/core.md +79 -16
- package/kb/site/en/reference/subsystems/filesystem.md +15 -0
- package/kb/site/en/reference/subsystems/goal.md +37 -1
- package/kb/site/en/reference/subsystems/index.md +2 -0
- package/kb/site/en/reference/subsystems/invariants.md +1 -1
- package/kb/site/en/reference/subsystems/llm-streaming.md +20 -10
- package/kb/site/en/reference/subsystems/lsp.md +1 -1
- package/kb/site/en/reference/subsystems/persistence.md +36 -15
- package/kb/site/en/reference/subsystems/schedule.md +1 -1
- package/kb/site/en/reference/subsystems/scope.md +1 -1
- package/kb/site/en/reference/subsystems/session-reference.md +4 -0
- package/kb/site/en/reference/subsystems/session-telemetry.md +22 -6
- package/kb/site/en/reference/subsystems/session.md +64 -42
- 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/slots.md +41 -33
- package/kb/site/en/reference/subsystems/spill.md +13 -6
- package/kb/site/en/reference/subsystems/subagent.md +20 -13
- package/kb/site/en/reference/subsystems/subprocess.md +25 -26
- package/kb/site/en/reference/subsystems/system-prompt.md +3 -1
- package/kb/site/en/reference/subsystems/token-meter.md +2 -1
- package/kb/site/en/reference/subsystems/tools.md +5 -5
- package/kb/site/en/reference/subsystems/workspace.md +80 -1
- package/kb/site/en/reference/tool-catalog.md +46 -2
- package/kb/site/en/reference/tool-execution-pipeline.md +2 -2
- package/kb/site/guide/providers.md +1 -1
- package/kb/site/guide/python-sdk.md +19 -3
- package/kb/site/reference/agent-lifecycle.md +12 -5
- package/kb/site/reference/capability-seams.md +10 -3
- package/kb/site/reference/config-catalog.md +173 -42
- package/kb/site/reference/cookbook/adding-a-tool.md +1 -1
- package/kb/site/reference/cookbook/extension-cookbook.md +1 -1
- package/kb/site/reference/index.md +18 -8
- package/kb/site/reference/persistence-catalog.md +127 -54
- 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/compaction.md +1 -1
- package/kb/site/reference/subsystems/core.md +79 -16
- package/kb/site/reference/subsystems/filesystem.md +15 -0
- package/kb/site/reference/subsystems/goal.md +37 -1
- 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 +20 -10
- package/kb/site/reference/subsystems/lsp.md +1 -1
- package/kb/site/reference/subsystems/persistence.md +36 -15
- package/kb/site/reference/subsystems/schedule.md +1 -1
- package/kb/site/reference/subsystems/scope.md +1 -1
- package/kb/site/reference/subsystems/session-reference.md +4 -0
- package/kb/site/reference/subsystems/session-telemetry.md +22 -6
- package/kb/site/reference/subsystems/session.md +64 -42
- package/kb/site/reference/subsystems/shell.md +9 -6
- package/kb/site/reference/subsystems/sidebar-right.md +151 -0
- package/kb/site/reference/subsystems/slots.md +41 -33
- package/kb/site/reference/subsystems/spill.md +13 -6
- package/kb/site/reference/subsystems/subagent.md +20 -13
- package/kb/site/reference/subsystems/subprocess.md +25 -26
- package/kb/site/reference/subsystems/system-prompt.md +3 -1
- package/kb/site/reference/subsystems/token-meter.md +2 -1
- package/kb/site/reference/subsystems/tools.md +5 -5
- package/kb/site/reference/subsystems/workspace.md +80 -1
- package/kb/site/reference/tool-catalog.md +46 -2
- 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 +3 -3
|
@@ -48,6 +48,12 @@ Vendored CLIs, build-only and test-only executables, direct in-process plugin mo
|
|
|
48
48
|
|
|
49
49
|
The Python SDK follows the same application architecture. Its runtime wheel packages the normal `dsh` CLI as `deepseek-harness-sdk-runtime-<platform>-<arch>`, and the client launches `dsh --profile sdk` with an explicit Harness home by default. The minimal example selects the shipped `sdk-minimal` profile. Python exposes profile selection and ordered patch files rather than a complete Cordis tree; persistent external plugins are installed through `dsh plugin`. The removed private direct-config carrier has no compatibility bin or fallback parser.
|
|
50
50
|
|
|
51
|
+
## Desktop application
|
|
52
|
+
|
|
53
|
+
The [Electron desktop application](https://github.com/deepseek-ai/deepseek-harness/blob/master/apps/desktop/README.md) owns the reserved `$DSH_HOME/profiles/desktop` npm project. Each signed Electron release binds one exact dsh version and carries a first-party offline seed; startup installs that version into the writable profile with the bundled pnpm, while retaining exact desktop-plugin versions from the previous profile. CLI profiles share supported product data under `$DSH_HOME`, but never executable packages, plugin activation, lockfiles, or `node_modules` with Desktop.
|
|
54
|
+
|
|
55
|
+
Electron starts the private Desktop Host package under its bundled upstream Node.js process; that package loads the installed dsh backend and matching client graph from the reserved profile. Unary RPC, Remote streams, and version-matched client assets cross versioned framed byte pipes with Node IPC reserved for lifecycle control, then reach the renderer through the secure `dsh-app://` protocol; the desktop composition opens no Web server or loopback port. Only shell-owned UI can run plugin transactions through the bundled pnpm and its private `$DSH_HOME/desktop/pnpm/store`.
|
|
56
|
+
|
|
51
57
|
## Core packages
|
|
52
58
|
|
|
53
59
|
Here are some core packages that contribute to the Cordis tree.
|
|
@@ -80,13 +86,15 @@ A **step** is one model request plus the tools it calls. A **turn** is zero or m
|
|
|
80
86
|
```text
|
|
81
87
|
turn/start
|
|
82
88
|
claim next-step input plus one queued message
|
|
83
|
-
assemble prompt sections + tool schemas
|
|
89
|
+
assemble prompt sections + tool schemas; project runtime context
|
|
84
90
|
-> agent/pre-step reject | enter(messages, startsRequestSeries?)
|
|
85
91
|
reject, or a first enter rewritten empty -> close the turn with no step
|
|
86
92
|
step/start
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
93
|
+
agent/request -> prepareCall (cancellation commits neither system nor users)
|
|
94
|
+
reconcile system/message using the prepared call capability
|
|
95
|
+
append entered messages as user/message; log request/header and request/context as needed
|
|
96
|
+
derive and freeze model history from the log
|
|
97
|
+
stream the bound prepared call -> llm/stream -> agent/assistant-stream start
|
|
90
98
|
agent/assistant-stream chunk*
|
|
91
99
|
assistant/message | assistant/attempt -> agent/assistant-stream end
|
|
92
100
|
tool/call* -> tools/pre-execute -> tools/execute -> tools/post-execute -> tool/result*
|
|
@@ -96,11 +104,13 @@ turn/start
|
|
|
96
104
|
turn/end
|
|
97
105
|
```
|
|
98
106
|
|
|
99
|
-
`turn/*`, `step/*`, `user/message`, `assistant/message`, `assistant/attempt`, and `tool/*` are durable session events; the rest are live extension points across three domains. `agent/assistant-stream` publishes process-local start, transient chunk, and end frames. The loop commits the complete compact stream as one message or log-only attempt before a committed end frame, and the Web Session-follow adapter is the live event's only remote consumer. `agent/pre-step`, `agent/request`, `llm/stream`, and the three `tools/*` events are waterfalls, whose listeners must call `next()` to delegate; `agent/turn-stopping` is serial and has no `next()`.
|
|
107
|
+
`turn/*`, `step/*`, `system/message`, `user/message`, `assistant/message`, `assistant/attempt`, and `tool/*` are durable session events; the rest are live extension points across three domains. `agent/assistant-stream` publishes process-local start, transient chunk, and end frames. The loop commits the complete compact stream as one message or log-only attempt before a committed end frame, and the Web Session-follow adapter is the live event's only remote consumer. `agent/pre-step`, `agent/request`, `llm/stream`, and the three `tools/*` events are waterfalls, whose listeners must call `next()` to delegate; `agent/turn-stopping` is serial and has no `next()`.
|
|
100
108
|
|
|
101
109
|
Input reaches the driver through one inbox. Some messages wake it immediately; injected context waits in the inbox until another message does.
|
|
102
110
|
|
|
103
|
-
`agent/pre-step` decides
|
|
111
|
+
`agent/pre-step` decides the accepted input. Listeners may rewrite or reject claimed messages; a rejected or empty first claim closes a durable turn without a step. An enter decision may set `startsRequestSeries`: the loop logs a fresh `request/header` (reason `series`, or `change` with `startsSeries: true` when the envelope also changed). Wrapping listeners preserve that declaration with `{ ...decision, messages }`. After assembly and `step/start`, `agent/request` and `prepareCall()` resolve the actual route before the system prompt and accepted users are committed; cancellation during either async phase commits neither. The prepared call capability governs prompt admission, not the preceding `request/context`. Every attempt synchronously reconciles the same rendered assembly, appends users only on the first attempt, logs header/context as needed, and derives and freezes the request before streaming the bound call. Retries do not repeat assembly or `agent/pre-step`. Surface replacements after attachment start a new request series, including during the first resumed pre-step; unchanged resume continues the series. The first admitted step reserves the system head before user messages even for an empty prompt (no wire message). The prompt travels only as `system/message` history: an empty rendering clears all active system nodes, leaving no old prompt model-visible; capable routes can append non-empty updates after the cached prefix; incapable routes and new request series consolidate non-empty prompt text at the first system node, with logged empty replacements for non-empty later system nodes ([decision](https://github.com/deepseek-ai/deepseek-harness/blob/master/.agents/notes/implemented/architecture/2026-09-02-system-prompt-as-surface-node.md); [decision rule](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/core/agent-loop/README.md#understand-the-implementation)).
|
|
112
|
+
|
|
113
|
+
The loop sends immutable requests while keeping cancellation live. It reuses message-freeze provenance only for identities it has fully frozen; [agent-loop](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/core/agent-loop/README.md) owns the request construction rules.
|
|
104
114
|
|
|
105
115
|
Details: the [sequence diagram](./agent-lifecycle.md), the [tool pipeline](./tool-execution-pipeline.md), and [cancellation and error recovery](./subsystems/core.md#the-agent-handle).
|
|
106
116
|
|
|
@@ -108,7 +118,7 @@ Details: the [sequence diagram](./agent-lifecycle.md), the [tool pipeline](./too
|
|
|
108
118
|
|
|
109
119
|
The session log is the source of the context the model sees. `deriveMessages()` projects model history from it. Each `assistant/message` embeds the exact compact timed stream that produced its assembled content; `assistant/attempt` retains settled failed, retried, cancelled, and stream-error attempts without adding model history. Fork, resume, transcripts, telemetry, and persistence all derive from these durable settlements, while live UI incrementality comes from `agent/assistant-stream`; a hard process loss before settlement leaves no durable attempt stream ([decision](https://github.com/deepseek-ai/deepseek-harness/blob/master/.agents/notes/implemented/architecture/2026-09-01-v2-embedded-assistant-streams.md)).
|
|
110
120
|
|
|
111
|
-
Session consumers know only the current logical format. Header-only `stat` and `list` rescan each Session directory, select its numerically highest canonical generation, and translate a supported historical header without loading events or publishing a successor. A stored-session `open` selects that same generation, refuses a future version, or composes the static adjacent migration chain in
|
|
121
|
+
Session consumers know only the current logical format. Header-only `stat` and `list` rescan each Session directory, select its numerically highest canonical generation, and translate a supported historical header without loading events or publishing a successor. A stored-session `open` selects that same generation, refuses a future version, or decodes and composes the static adjacent migration chain once before returning validated current logical events. A read open uses that in-memory result without publishing a successor; a write open first encodes, verifies, and exclusively publishes the final version-named successor beside the unchanged source. Ordinary repair of an unsealed interrupted tail remains a handle consumer responsibility; migration inserts a missing interrupted `turn/end` only for the bounded released restart already sealed by a later `turn/start`. JSONL v0 uses `session.jsonl[.zstd]`, v1 and later use lowercase `session.vN.jsonl[.zstd]`, and committed generation paths are never renamed, replaced, or deleted. The JSONL provider owns physical framing, compression, generation selection, and exclusive publication, while each adjacent migration package owns exactly one `vN -> vN+1` step ([decision](https://github.com/deepseek-ai/deepseek-harness/blob/master/.agents/notes/implemented/architecture/2026-08-31-released-session-format-migrations.md)).
|
|
112
122
|
|
|
113
123
|
**Model-visible means logged.** Anything that reaches a model request must be reconstructable from the log, and a runtime invariant asserts it. This is why a new model-visible input requires a new session event: extend `SessionEventMap` and render from the log.
|
|
114
124
|
|
|
@@ -120,7 +130,7 @@ A **seam** is a swappable capability with three roles: a **Service Definition**
|
|
|
120
130
|
|
|
121
131
|
Seams are why one provider swap changes the whole product. Filesystem and subprocess providers share one execution world, so pointing them at a remote sandbox moves Bash, PTY, and LSP with them, with no provider forks. [Subagent providers](./subsystems/subagent.md) vary just as widely behind one interface, from a fresh child agent to a delegated turn in another product.
|
|
122
132
|
|
|
123
|
-
[Experimental Agent Teams](https://github.com/deepseek-ai/deepseek-harness/blob/master/docs/subsystems/agent-team.md) is a
|
|
133
|
+
[Experimental Agent Teams](https://github.com/deepseek-ai/deepseek-harness/blob/master/docs/subsystems/agent-team.md) is a published opt-in coordination seam on `ctx.agentTeams`, with a durable roster, task board, and mailbox layered over continuable subagents.
|
|
124
134
|
|
|
125
135
|
## Where new behavior goes
|
|
126
136
|
|
|
@@ -10,7 +10,7 @@ outline: "deep"
|
|
|
10
10
|
|
|
11
11
|
Every event type that can appear in a session's durable event log: the complete persisted `SessionEvent` envelope and each member of the merge-extensible `SessionEventMap` — the owning vocabulary in `@deepseek-ai/dsh-session` plus every plugin declaration merge into `@deepseek-ai/dsh-session/types` in this repo — with source JSDoc, full payload declaration, surface badge, and declaration site. It complements [session.md](./subsystems/session.md) (surface ordering and the `deriveMessages()` projection), [persistence.md](./subsystems/persistence.md) (how the log is made durable), and the generated region of [session.md](./subsystems/session.md#cordis-surface) (the live bus wiring — a log event is NOT a cordis event; it reaches listeners via the single `session/event` emit).
|
|
12
12
|
|
|
13
|
-
This file is GENERATED from source (`scripts/gen-persistence-catalog.ts`) and verified fresh by `pnpm run verify-persistence-catalog` (part of `doc-sync`) — do not edit it by hand. Declaration blocks retain the source declaration and nested property JSDoc, removing only the indentation imposed by a containing interface/module, and use a `ts persistence-catalog` fence (skipped by doc-typecheck because declarations reference types from their owning modules). Type names in a payload link to the page that documents them.
|
|
13
|
+
This file is GENERATED from source (`scripts/gen-persistence-catalog.ts`) and verified fresh by `pnpm run verify-persistence-catalog` (part of `doc-sync`) — do not edit it by hand. Declaration blocks retain the source declaration and nested property JSDoc, removing only the indentation imposed by a containing interface/module, and use a `ts persistence-catalog` fence (skipped by doc-typecheck because declarations reference types from their owning modules). Type names in a payload link to the page that documents them. The archived [persistence-log-catalog record](https://github.com/deepseek-ai/deepseek-harness/blob/master/.agents/notes/archived/process/2026-07-04-persistence-log-catalog.md) documents the original catalog decision.
|
|
14
14
|
|
|
15
15
|
The envelope declarations below compose each event's `type`, monotonic `seq`, epoch-ms `time`, `data`, the optional `ignorable` unknown-type skip marker, and the conditional `surfaceOp`/`sourceEventSeqs` fields. **surface** marks a `SurfaceEventType` member: it produces an LLM message and declares how it joins the surface list. **log-only** marks everything else: a durable, replayable record with no derived-history contribution. Every payload is JSON-serializable (enforced at `Session.append`). Current writers stamp `SESSION_FORMAT_VERSION`; supported historical artifacts reach this current vocabulary through the build-static adjacent migration catalog ([the version lifecycle](./subsystems/persistence.md)). Scope: the packages in this repo; a downstream plugin can merge further current-version event types, which are outside this catalog by construction and require an explicit disposition at a later format edge.
|
|
16
16
|
|
|
@@ -23,10 +23,11 @@ export type SessionEventType = keyof SessionEventMap
|
|
|
23
23
|
/**
|
|
24
24
|
* The subset of {@link SessionEventType} values whose events produce LLM
|
|
25
25
|
* messages and are eligible to appear on the ordered surface. Only these
|
|
26
|
-
* event types may carry {@link SurfaceOp}; user and tool events may also cite
|
|
26
|
+
* event types may carry {@link SurfaceOp}; system, user, and tool events may also cite
|
|
27
27
|
* earlier sources through {@link SessionEvent.sourceEventSeqs}.
|
|
28
28
|
*/
|
|
29
29
|
export type SurfaceEventType =
|
|
30
|
+
| 'system/message'
|
|
30
31
|
| 'user/message'
|
|
31
32
|
| 'assistant/message'
|
|
32
33
|
| 'tool/result'
|
|
@@ -37,16 +38,16 @@ export type SurfaceEventType =
|
|
|
37
38
|
*
|
|
38
39
|
* - `'append'`: added to the tail — normal path for user/assistant/tool
|
|
39
40
|
* messages.
|
|
40
|
-
* - `{ op: 'replace',
|
|
41
|
-
* (inclusive) through `
|
|
42
|
-
* surface nodes in the current surface. `
|
|
41
|
+
* - `{ op: 'replace', startSeq, endSeq }`: replaces surface nodes from `startSeq`
|
|
42
|
+
* (inclusive) through `endSeq` (inclusive) with this node. Both must exist as
|
|
43
|
+
* surface nodes in the current surface. `startSeq === endSeq` replaces a single
|
|
43
44
|
* node. The node's {@link SessionEvent.sourceEventSeqs} must include every
|
|
44
45
|
* shadowed surface node. Used by compaction; any surface-replacing producer
|
|
45
46
|
* may use it.
|
|
46
47
|
*/
|
|
47
48
|
export type SurfaceOp =
|
|
48
49
|
| 'append'
|
|
49
|
-
| { op: 'replace';
|
|
50
|
+
| { op: 'replace'; startSeq: SessionSeq; endSeq: SessionSeq }
|
|
50
51
|
|
|
51
52
|
/**
|
|
52
53
|
* One immutable entry in the session log.
|
|
@@ -55,7 +56,7 @@ export type SurfaceOp =
|
|
|
55
56
|
* unions), so `switch (event.type)` narrows `event.data` without casts.
|
|
56
57
|
*
|
|
57
58
|
* The {@link sourceEventSeqs} and {@link surfaceOp} fields are conditional:
|
|
58
|
-
* they only exist on {@link SurfaceEventType} variants (`user/message`,
|
|
59
|
+
* they only exist on {@link SurfaceEventType} variants (`system/message`, `user/message`,
|
|
59
60
|
* `assistant/message`, `tool/result`).
|
|
60
61
|
* Non-surface events (boundary markers, attempts, errors) never carry
|
|
61
62
|
* surface metadata — the compiler enforces this at `Session.append()`
|
|
@@ -80,20 +81,14 @@ export type SessionEvent<T extends SessionEventType = SessionEventType> = {
|
|
|
80
81
|
* inconvenience) rather than silently resuming a gutted session.
|
|
81
82
|
*/
|
|
82
83
|
ignorable?: true
|
|
83
|
-
} & (K extends SurfaceEventType ? {
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
* `assistant/message` embeds its provider stream and cannot carry this field.
|
|
88
|
-
*/
|
|
89
|
-
sourceEventSeqs?: SessionSeq[]
|
|
90
|
-
/** How this event entered the surface; absent for non-surface events. */
|
|
91
|
-
surfaceOp?: SurfaceOp
|
|
92
|
-
} : object)
|
|
84
|
+
} & (K extends SurfaceEventType ? SurfaceIntent<K> : {
|
|
85
|
+
surfaceOp?: never
|
|
86
|
+
sourceEventSeqs?: never
|
|
87
|
+
})
|
|
93
88
|
}[T]
|
|
94
89
|
```
|
|
95
90
|
|
|
96
|
-
Sources: [`packages/core/session/src/types.ts:
|
|
91
|
+
Sources: [`packages/core/session/src/types.ts:404`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/core/session/src/types.ts) · [`packages/core/session/src/types.ts:412`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/core/session/src/types.ts) · [`packages/core/session/src/types.ts:434`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/core/session/src/types.ts) · [`packages/core/session/src/types.ts:465`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/core/session/src/types.ts)
|
|
97
92
|
|
|
98
93
|
## Events
|
|
99
94
|
|
|
@@ -106,8 +101,8 @@ Sources: [`packages/core/session/src/types.ts:379`](https://github.com/deepseek-
|
|
|
106
101
|
```ts persistence-catalog
|
|
107
102
|
/**
|
|
108
103
|
* One normalized mutation of an agent's durable pending-message lists.
|
|
109
|
-
*
|
|
110
|
-
*
|
|
104
|
+
* The session-projection registry applies the committed event before
|
|
105
|
+
* `Session.append()` returns; Inbox live notifications follow that commit.
|
|
111
106
|
*/
|
|
112
107
|
'agent/inbox/spliced': {
|
|
113
108
|
target: InboxTarget
|
|
@@ -118,7 +113,7 @@ Sources: [`packages/core/session/src/types.ts:379`](https://github.com/deepseek-
|
|
|
118
113
|
}
|
|
119
114
|
```
|
|
120
115
|
|
|
121
|
-
Source: [`packages/core/agent/src/types.ts:
|
|
116
|
+
Source: [`packages/core/agent/src/types.ts:87`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/core/agent/src/types.ts)
|
|
122
117
|
|
|
123
118
|
### `agent-preset/*`
|
|
124
119
|
|
|
@@ -220,7 +215,7 @@ Source: [`packages/interaction/user-approval/src/index.ts:33`](https://github.co
|
|
|
220
215
|
'assistant/attempt': { turn: number; step: number; stream: AssistantStreamRecord[] }
|
|
221
216
|
```
|
|
222
217
|
|
|
223
|
-
Source: [`packages/core/session/src/types.ts:
|
|
218
|
+
Source: [`packages/core/session/src/types.ts:335`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/core/session/src/types.ts)
|
|
224
219
|
|
|
225
220
|
<a id="assistantmessage--surface"></a>
|
|
226
221
|
|
|
@@ -250,7 +245,7 @@ Source: [`packages/core/session/src/types.ts:313`](https://github.com/deepseek-a
|
|
|
250
245
|
|
|
251
246
|
Types: [TokenUsage](./subsystems/llm-streaming.md)
|
|
252
247
|
|
|
253
|
-
Source: [`packages/core/session/src/types.ts:
|
|
248
|
+
Source: [`packages/core/session/src/types.ts:321`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/core/session/src/types.ts)
|
|
254
249
|
|
|
255
250
|
### `command/*`
|
|
256
251
|
|
|
@@ -406,8 +401,45 @@ Types: [ContentBlock](./subsystems/core.md) · [TokenUsage](./subsystems/llm-str
|
|
|
406
401
|
|
|
407
402
|
Source: [`packages/compaction/compaction/src/types.ts:34`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/compaction/compaction/src/types.ts)
|
|
408
403
|
|
|
404
|
+
### `deliverables/*`
|
|
405
|
+
|
|
406
|
+
<a id="deliverablespresented--log-only"></a>
|
|
407
|
+
|
|
408
|
+
#### `deliverables/presented` — log-only
|
|
409
|
+
|
|
410
|
+
```ts persistence-catalog
|
|
411
|
+
/** Declared filesystem files from a successful final present result, including nested calls. */
|
|
412
|
+
'deliverables/presented': { turn: number; callId: ToolCallId; files: PresentedFile[] }
|
|
413
|
+
```
|
|
414
|
+
|
|
415
|
+
Types: [ToolCallId](./subsystems/core.md)
|
|
416
|
+
|
|
417
|
+
Source: [`packages/fs/tool-present/src/types.ts:15`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/fs/tool-present/src/types.ts)
|
|
418
|
+
|
|
409
419
|
### `feedback/*`
|
|
410
420
|
|
|
421
|
+
<a id="feedbackmessage-delete--log-only"></a>
|
|
422
|
+
|
|
423
|
+
#### `feedback/message-delete` — log-only
|
|
424
|
+
|
|
425
|
+
```ts persistence-catalog
|
|
426
|
+
/** Log-only deletion; earlier ratings and notes remain in the log. */
|
|
427
|
+
'feedback/message-delete': MessageFeedbackDelete
|
|
428
|
+
```
|
|
429
|
+
|
|
430
|
+
Source: [`packages/feedback/message-feedback/src/types.ts:58`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/feedback/message-feedback/src/types.ts)
|
|
431
|
+
|
|
432
|
+
<a id="feedbackmessage-put--log-only"></a>
|
|
433
|
+
|
|
434
|
+
#### `feedback/message-put` — log-only
|
|
435
|
+
|
|
436
|
+
```ts persistence-catalog
|
|
437
|
+
/** Log-only human feedback; never enters model history. */
|
|
438
|
+
'feedback/message-put': MessageFeedbackPut
|
|
439
|
+
```
|
|
440
|
+
|
|
441
|
+
Source: [`packages/feedback/message-feedback/src/types.ts:56`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/feedback/message-feedback/src/types.ts)
|
|
442
|
+
|
|
411
443
|
<a id="feedbackrecord--log-only"></a>
|
|
412
444
|
|
|
413
445
|
#### `feedback/record` — log-only
|
|
@@ -417,10 +449,10 @@ Source: [`packages/compaction/compaction/src/types.ts:34`](https://github.com/de
|
|
|
417
449
|
* One recorded human remark about this session. Log-only and independent
|
|
418
450
|
* of its trigger; it never enters model context or derived history.
|
|
419
451
|
*/
|
|
420
|
-
'feedback/record':
|
|
452
|
+
'feedback/record': FeedbackRecord
|
|
421
453
|
```
|
|
422
454
|
|
|
423
|
-
Source: [`packages/feedback/command-feedback/src/
|
|
455
|
+
Source: [`packages/feedback/command-feedback/src/types.ts:40`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/feedback/command-feedback/src/types.ts)
|
|
424
456
|
|
|
425
457
|
### `goal/*`
|
|
426
458
|
|
|
@@ -570,13 +602,15 @@ Source: [`packages/plan/plan-mode/src/index.ts:46`](https://github.com/deepseek-
|
|
|
570
602
|
|
|
571
603
|
```ts persistence-catalog
|
|
572
604
|
/**
|
|
573
|
-
* Route metadata for the next request, logged only when the route
|
|
574
|
-
* changes. It does not participate in request
|
|
605
|
+
* Route metadata for the next request, logged only when the route, capacity,
|
|
606
|
+
* or system prompt update mode changes. It does not participate in request
|
|
607
|
+
* reconstruction or header equality. Prompt admission uses the bound prepared
|
|
608
|
+
* call's capability, not this snapshot from an earlier request.
|
|
575
609
|
*/
|
|
576
610
|
'request/context': RequestContext
|
|
577
611
|
```
|
|
578
612
|
|
|
579
|
-
Source: [`packages/core/session/src/types.ts:
|
|
613
|
+
Source: [`packages/core/session/src/types.ts:377`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/core/session/src/types.ts)
|
|
580
614
|
|
|
581
615
|
<a id="requestheader--log-only"></a>
|
|
582
616
|
|
|
@@ -595,7 +629,7 @@ Source: [`packages/core/session/src/types.ts:352`](https://github.com/deepseek-a
|
|
|
595
629
|
}
|
|
596
630
|
```
|
|
597
631
|
|
|
598
|
-
Source: [`packages/core/session/src/types.ts:
|
|
632
|
+
Source: [`packages/core/session/src/types.ts:365`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/core/session/src/types.ts)
|
|
599
633
|
|
|
600
634
|
### `sandbox/*`
|
|
601
635
|
|
|
@@ -670,7 +704,7 @@ Source: [`packages/schedule/schedule/src/types.ts:219`](https://github.com/deeps
|
|
|
670
704
|
'session/end-seed': { inherited?: true }
|
|
671
705
|
```
|
|
672
706
|
|
|
673
|
-
Source: [`packages/core/session/src/types.ts:
|
|
707
|
+
Source: [`packages/core/session/src/types.ts:400`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/core/session/src/types.ts)
|
|
674
708
|
|
|
675
709
|
<a id="sessiontitle--log-only"></a>
|
|
676
710
|
|
|
@@ -719,7 +753,7 @@ Source: [`packages/session/session-title-llm/src/index.ts:45`](https://github.co
|
|
|
719
753
|
}
|
|
720
754
|
```
|
|
721
755
|
|
|
722
|
-
Source: [`packages/session/session-log-deepseek/src/types.ts:
|
|
756
|
+
Source: [`packages/session/session-log-deepseek/src/types.ts:81`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/session/session-log-deepseek/src/types.ts)
|
|
723
757
|
|
|
724
758
|
### `step/*`
|
|
725
759
|
|
|
@@ -732,7 +766,7 @@ Source: [`packages/session/session-log-deepseek/src/types.ts:59`](https://github
|
|
|
732
766
|
'step/end': { turn: number; step: number }
|
|
733
767
|
```
|
|
734
768
|
|
|
735
|
-
Source: [`packages/core/session/src/types.ts:
|
|
769
|
+
Source: [`packages/core/session/src/types.ts:289`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/core/session/src/types.ts)
|
|
736
770
|
|
|
737
771
|
<a id="stepstart--log-only"></a>
|
|
738
772
|
|
|
@@ -743,10 +777,24 @@ Source: [`packages/core/session/src/types.ts:280`](https://github.com/deepseek-a
|
|
|
743
777
|
'step/start': { turn: number; step: number }
|
|
744
778
|
```
|
|
745
779
|
|
|
746
|
-
Source: [`packages/core/session/src/types.ts:
|
|
780
|
+
Source: [`packages/core/session/src/types.ts:287`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/core/session/src/types.ts)
|
|
747
781
|
|
|
748
782
|
### `subagent/*`
|
|
749
783
|
|
|
784
|
+
<a id="subagentcatalog--log-only"></a>
|
|
785
|
+
|
|
786
|
+
#### `subagent/catalog` — log-only
|
|
787
|
+
|
|
788
|
+
```ts persistence-catalog
|
|
789
|
+
/**
|
|
790
|
+
* A direct child's complete discovery fact.
|
|
791
|
+
* @param data - versioned parent-owned catalog entry.
|
|
792
|
+
*/
|
|
793
|
+
'subagent/catalog': SubagentCatalogEvent
|
|
794
|
+
```
|
|
795
|
+
|
|
796
|
+
Source: [`packages/subagent/subagent/src/catalog.ts:40`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/subagent/subagent/src/catalog.ts)
|
|
797
|
+
|
|
750
798
|
<a id="subagentdescriptor--log-only"></a>
|
|
751
799
|
|
|
752
800
|
#### `subagent/descriptor` — log-only
|
|
@@ -783,6 +831,30 @@ Source: [`packages/subagent/subagent/src/descriptor.ts:38`](https://github.com/d
|
|
|
783
831
|
|
|
784
832
|
Source: [`packages/subagent/tool-subagent/src/model-selection-state.ts:17`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/subagent/tool-subagent/src/model-selection-state.ts)
|
|
785
833
|
|
|
834
|
+
### `system/*`
|
|
835
|
+
|
|
836
|
+
<a id="systemmessage--surface"></a>
|
|
837
|
+
|
|
838
|
+
#### `system/message` — surface
|
|
839
|
+
|
|
840
|
+
```ts persistence-catalog
|
|
841
|
+
/**
|
|
842
|
+
* The rendered system prompt on the model-visible surface. The loop appends
|
|
843
|
+
* the first one as surface node 0 before the step's first `user/message`.
|
|
844
|
+
* A prepared in-history route can append nonempty changes in a continuing
|
|
845
|
+
* series. An incapable route or new series normalizes text to the first system
|
|
846
|
+
* node. Normalization empties nonempty later nodes, then rewrites the head if
|
|
847
|
+
* needed, through logged per-node replacements. An empty rendering always
|
|
848
|
+
* clears all active system nodes, leaving no older instructions model-visible.
|
|
849
|
+
* Empty later nodes are dormant and project to no message; an empty head with
|
|
850
|
+
* no active later node records "no system prompt". Restored nonempty text follows
|
|
851
|
+
* the same route and series rule; empty nodes never restore older text.
|
|
852
|
+
*/
|
|
853
|
+
'system/message': { turn: number; step: number; message: SystemMessage }
|
|
854
|
+
```
|
|
855
|
+
|
|
856
|
+
Source: [`packages/core/session/src/types.ts:310`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/core/session/src/types.ts)
|
|
857
|
+
|
|
786
858
|
### `team/*`
|
|
787
859
|
|
|
788
860
|
<a id="teammember--log-only"></a>
|
|
@@ -874,16 +946,16 @@ Source: [`packages/todo/tool-todo/src/types.ts:31`](https://github.com/deepseek-
|
|
|
874
946
|
|
|
875
947
|
Types: [ToolCallId](./subsystems/core.md)
|
|
876
948
|
|
|
877
|
-
Source: [`packages/core/session/src/types.ts:
|
|
949
|
+
Source: [`packages/core/session/src/types.ts:341`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/core/session/src/types.ts)
|
|
878
950
|
|
|
879
|
-
<a id="
|
|
951
|
+
<a id="toolptc-dispatch--log-only"></a>
|
|
880
952
|
|
|
881
|
-
#### `tool/
|
|
953
|
+
#### `tool/ptc-dispatch` — log-only
|
|
882
954
|
|
|
883
955
|
```ts persistence-catalog
|
|
884
956
|
/**
|
|
885
957
|
* One bridged sub-dispatch SETTLING: the pairing ids (matching the
|
|
886
|
-
* `tool/
|
|
958
|
+
* `tool/ptc-dispatch-start` with the same `subCallId`), the tool `name`
|
|
887
959
|
* with the same JSON-normalized `arguments`, and the sub-call's complete
|
|
888
960
|
* model-facing outcome in `tool/result`'s own vocabulary
|
|
889
961
|
* (`content` + `isError`), so UIs render a sub-call through the exact
|
|
@@ -896,30 +968,30 @@ Source: [`packages/core/session/src/types.ts:319`](https://github.com/deepseek-a
|
|
|
896
968
|
* before returning), so its execution-enclosure relation holds by
|
|
897
969
|
* construction.
|
|
898
970
|
*/
|
|
899
|
-
'tool/
|
|
971
|
+
'tool/ptc-dispatch': PtcDispatchEventData
|
|
900
972
|
```
|
|
901
973
|
|
|
902
974
|
Source: [`packages/core/tools/src/types.ts:56`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/core/tools/src/types.ts)
|
|
903
975
|
|
|
904
|
-
<a id="
|
|
976
|
+
<a id="toolptc-dispatch-start--log-only"></a>
|
|
905
977
|
|
|
906
|
-
#### `tool/
|
|
978
|
+
#### `tool/ptc-dispatch-start` — log-only
|
|
907
979
|
|
|
908
980
|
```ts persistence-catalog
|
|
909
981
|
/**
|
|
910
982
|
* One sub-dispatch STARTING inside a `run_code` program: the parent
|
|
911
|
-
* `run_code` call id, the
|
|
912
|
-
* numbered in submission order), and the tool `name` with its
|
|
983
|
+
* `run_code` call id, the opaque sub-call id (new calls use
|
|
984
|
+
* `<parent>:ptc:<n>`, numbered in submission order), and the tool `name` with its
|
|
913
985
|
* JSON-normalized `arguments` — the exact value dispatched, normalized
|
|
914
986
|
* BEFORE dispatch, so this append can never fail on payload shape.
|
|
915
987
|
* Appended when the scheduler actually starts the call (not at
|
|
916
988
|
* submission), so a start means the tool body pipeline was entered; a
|
|
917
989
|
* call abandoned in the queue logs nothing. Log-only: `deriveMessages()`
|
|
918
990
|
* ignores it; UIs use it for live per-sub-call running state and pair it
|
|
919
|
-
* with `tool/
|
|
991
|
+
* with `tool/ptc-dispatch` by `subCallId` (timing = the two events'
|
|
920
992
|
* `time` fields).
|
|
921
993
|
*/
|
|
922
|
-
'tool/
|
|
994
|
+
'tool/ptc-dispatch-start': PtcDispatchStartEventData
|
|
923
995
|
```
|
|
924
996
|
|
|
925
997
|
Source: [`packages/core/tools/src/types.ts:40`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/core/tools/src/types.ts)
|
|
@@ -944,12 +1016,13 @@ Source: [`packages/core/tools/src/types.ts:40`](https://github.com/deepseek-ai/d
|
|
|
944
1016
|
turn: number
|
|
945
1017
|
step: number
|
|
946
1018
|
message: ToolResultMessage
|
|
1019
|
+
/** Optional failure identity; allowed only when the tool-result block has `isError: true`. */
|
|
947
1020
|
error?: { name: string; code: string }
|
|
948
1021
|
meta?: JsonValue
|
|
949
1022
|
}
|
|
950
1023
|
```
|
|
951
1024
|
|
|
952
|
-
Source: [`packages/core/session/src/types.ts:
|
|
1025
|
+
Source: [`packages/core/session/src/types.ts:353`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/core/session/src/types.ts)
|
|
953
1026
|
|
|
954
1027
|
### `tool-workflow/*`
|
|
955
1028
|
|
|
@@ -1029,7 +1102,7 @@ Source: [`packages/workflow/tool-workflow/src/types.ts:47`](https://github.com/d
|
|
|
1029
1102
|
|
|
1030
1103
|
Types: [TurnEndReason](./subsystems/session.md)
|
|
1031
1104
|
|
|
1032
|
-
Source: [`packages/core/session/src/types.ts:
|
|
1105
|
+
Source: [`packages/core/session/src/types.ts:285`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/core/session/src/types.ts)
|
|
1033
1106
|
|
|
1034
1107
|
<a id="turnstart--log-only"></a>
|
|
1035
1108
|
|
|
@@ -1045,7 +1118,7 @@ Source: [`packages/core/session/src/types.ts:276`](https://github.com/deepseek-a
|
|
|
1045
1118
|
'turn/start': { turn: number }
|
|
1046
1119
|
```
|
|
1047
1120
|
|
|
1048
|
-
Source: [`packages/core/session/src/types.ts:
|
|
1121
|
+
Source: [`packages/core/session/src/types.ts:276`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/core/session/src/types.ts)
|
|
1049
1122
|
|
|
1050
1123
|
### `user/*`
|
|
1051
1124
|
|
|
@@ -1064,7 +1137,7 @@ Source: [`packages/core/session/src/types.ts:267`](https://github.com/deepseek-a
|
|
|
1064
1137
|
'user/message': UserMessage
|
|
1065
1138
|
```
|
|
1066
1139
|
|
|
1067
|
-
Source: [`packages/core/session/src/types.ts:
|
|
1140
|
+
Source: [`packages/core/session/src/types.ts:297`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/core/session/src/types.ts)
|
|
1068
1141
|
|
|
1069
1142
|
### `web/*`
|
|
1070
1143
|
|
|
@@ -49,7 +49,7 @@ type ApprovalOutcome = 'allowed-once' | 'rejected' | 'cancelled' | 'unavailable'
|
|
|
49
49
|
type ApprovalPolicy = 'ask' | 'never'
|
|
50
50
|
```
|
|
51
51
|
|
|
52
|
-
Both policies contribute their complete current meaning to the cache-safe runtime-context snapshot. The sourced `user/message` is the durable model-visible input; changing approval state appends a new full snapshot after retained history without
|
|
52
|
+
Both policies contribute their complete current meaning to the cache-safe runtime-context snapshot. The sourced `user/message` is the durable model-visible input; changing approval state appends a new full snapshot after retained history without touching the `system/message` nodes that hold the rendered system prompt.
|
|
53
53
|
|
|
54
54
|
## Approval request
|
|
55
55
|
|
|
@@ -133,6 +133,15 @@ graph(): WebBootGraph
|
|
|
133
133
|
*/
|
|
134
134
|
clientPath(id: string): string | undefined
|
|
135
135
|
|
|
136
|
+
/**
|
|
137
|
+
* Serve an advertised revisioned bundle or source map without a Web server.
|
|
138
|
+
* Unknown URLs return 404, unsupported methods return 405, and `HEAD`
|
|
139
|
+
* returns the same immutable headers without a body.
|
|
140
|
+
* @param request - shell-carrier request for a `/plugins` resource.
|
|
141
|
+
* @returns the exact response also exposed by the optional Web route.
|
|
142
|
+
*/
|
|
143
|
+
fetchBundle(request: Request): Response
|
|
144
|
+
|
|
136
145
|
/**
|
|
137
146
|
* Filesystem baseline captured before an entry's current bytes were read.
|
|
138
147
|
* HMR compares it with the live files when installing a watch, so a write
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
---
|
|
2
|
+
editSource: "docs/subsystems/client-resources.md"
|
|
3
|
+
outline: [2,3]
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Client Resources
|
|
7
|
+
|
|
8
|
+
The client resource model turns an address into live data for any Web Client component. [`dsh-client-resources`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/client/resources/README.md) provides the `ctx.resources` service and the `useResource` global standard hook; a package that owns a kind of content registers one **provider** for its **protocol**, and a component reads the content's current state by **address** without importing the owner's runtime. The right Sidebar's tabs are the model's first consumer ([Right Sidebar](./sidebar-right.md)); the decision record is the [client resource model Agent Note](https://github.com/deepseek-ai/deepseek-harness/blob/master/.agents/notes/implemented/architecture/2026-09-05-client-resource-model.md).
|
|
9
|
+
|
|
10
|
+
This page is the developer reference: how to write an address, how to register a provider, how to read a resource, what the states and failures mean, and how the model holds and releases a resource.
|
|
11
|
+
|
|
12
|
+
## Addresses
|
|
13
|
+
|
|
14
|
+
A resource address is a `dsh-resource://<type>/…` URL. The host names the protocol and must be a key of `ResourceProtocolMap`; the path is the protocol's own, and its owner percent-encodes each segment. A protocol that needs a scope puts it in the path: the `file` protocol's addresses read `dsh-resource://file/session/<sessionId>/<path>`, where path is workspace-relative or absolute with its leading slashes preserved, built with `fileAddressFor(sessionId, cwd, path)` and read back with `parseFileAddress(address)` from [`dsh-util-workspace-path`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/util/workspace-path/README.md). The model itself reads only the scheme and the host: `protocolOf(address)` returns the lower-cased host of a `dsh-resource://` URL and `undefined` for anything else. Addresses under any other scheme — the Sidebar's `sidebar://guide` — name no resource and read as `none`.
|
|
15
|
+
|
|
16
|
+
| Address | Protocol key | Reads as |
|
|
17
|
+
|---|---|---|
|
|
18
|
+
| `dsh-resource://file/session/s1/notes/a.md` | `file` | the metadata of `notes/a.md` under session `s1`'s workspace root, when the `file` provider is registered |
|
|
19
|
+
| `dsh-resource://file/absolute/home/me/notes.md` | `file` | parseable but fails with `workspace-file/unknown-workspace`: no authorizing Session, and neither current nor Tab Session is borrowed |
|
|
20
|
+
| `DSH-RESOURCE://File/session/s1/a` | `file` | a distinct record: addresses compare as strings, and `openResource` accepts only the canonical lower-case spelling that `fileAddressFor` emits |
|
|
21
|
+
| `sidebar://guide` | — | `none`: a navigation address |
|
|
22
|
+
| `/home/me/notes.md` | — | `none`: not a URL |
|
|
23
|
+
|
|
24
|
+
## Registering a provider
|
|
25
|
+
|
|
26
|
+
The owner of a protocol declares its value type on `ResourceProtocolMap` and registers one provider inside its own `ctx.effect`, so the protocol lives exactly as long as the plugin ([provide a protocol](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/client/resources/README.md#provide-a-protocol)). `open(address, { signal })` returns a stream of `RemoteResult` frames — the current state first, then one frame per change — and must stop when `signal` aborts. A failure is an `ok: false` frame carrying a `RemoteFailure`; a throw inside the stream is a programming error and is not caught.
|
|
27
|
+
|
|
28
|
+
```ts ignore-check
|
|
29
|
+
import type { Context } from '@deepseek-ai/cordis'
|
|
30
|
+
import type { RemoteResult } from '@deepseek-ai/dsh-typert-protocol'
|
|
31
|
+
import type {} from '@deepseek-ai/dsh-client-resources/client'
|
|
32
|
+
|
|
33
|
+
interface NoteView { readonly title: string; readonly updatedAt: string }
|
|
34
|
+
|
|
35
|
+
declare module '@deepseek-ai/dsh-client-ui-slots' {
|
|
36
|
+
interface ResourceProtocolMap { note: NoteView }
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export const inject = ['resources', 'remote']
|
|
40
|
+
|
|
41
|
+
export function apply(ctx: Context): void {
|
|
42
|
+
ctx.effect(() => ctx.resources.register<'note'>({
|
|
43
|
+
protocol: 'note',
|
|
44
|
+
async *open(address, { signal }): AsyncIterable<RemoteResult<NoteView>> {
|
|
45
|
+
const id = new URL(address).pathname.slice(1)
|
|
46
|
+
yield await ctx.remote.notes.read(id, signal)
|
|
47
|
+
for await (const change of ctx.remote.notes.follow(id, signal)) yield change
|
|
48
|
+
},
|
|
49
|
+
}), 'my-notes: note resource provider')
|
|
50
|
+
}
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
A protocol has exactly one provider; a second registration throws. Registering while addresses of the protocol are already held opens their streams at once; disposing the provider ends those streams and the addresses read `none` until a provider returns.
|
|
54
|
+
|
|
55
|
+
## Reading a resource
|
|
56
|
+
|
|
57
|
+
Every slot component receives `useResource` in its props, whatever its scope ([Slots](./slots.md)). `useResource<P>(address)` names the protocol as the type argument and returns the address's current snapshot; subscribing is what holds the resource open, and a component that mounts while another holder keeps the resource alive reads the latest value at once without reopening the stream ([read a resource](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/client/resources/README.md#read-a-resource)).
|
|
58
|
+
|
|
59
|
+
| `status` | Meaning | `value` | `failure` |
|
|
60
|
+
|---|---|---|---|
|
|
61
|
+
| `none` | No provider is registered for the address's protocol, or the address is not a resource address | `undefined` | `undefined` |
|
|
62
|
+
| `loading` | The provider's stream is open and has not yielded yet | `undefined` | `undefined` |
|
|
63
|
+
| `live` | The latest frame succeeded | the latest `ok` value | `undefined` |
|
|
64
|
+
| `failed` | The latest frame reported a failure | the last `ok` value, kept | the frame's `RemoteFailure` |
|
|
65
|
+
|
|
66
|
+
```tsx ignore-check
|
|
67
|
+
import type { PropsRuntime } from '@deepseek-ai/dsh-client-ui-slots'
|
|
68
|
+
import type {} from '@deepseek-ai/dsh-api-workspace-files/client'
|
|
69
|
+
|
|
70
|
+
type Props = PropsRuntime<'sidebar.right.pane.tab'>
|
|
71
|
+
|
|
72
|
+
export function FileHeader({ useTabInfo, useResource, t }: Props) {
|
|
73
|
+
const { tab } = useTabInfo()
|
|
74
|
+
const meta = useResource<'file'>(tab.contentId)
|
|
75
|
+
if (meta.status === 'failed') return <p role="alert">{t('failed', { code: meta.failure.code })}</p>
|
|
76
|
+
return (
|
|
77
|
+
<header>
|
|
78
|
+
{tab.title}
|
|
79
|
+
</header>
|
|
80
|
+
)
|
|
81
|
+
}
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
A consumer presents `failed` itself: the model keeps the last value beside the failure so a body can show stale content with a notice rather than a blank, and the next `ok` frame clears the failure. Nothing in the model produces user-visible text.
|
|
85
|
+
|
|
86
|
+
## Holding and releasing
|
|
87
|
+
|
|
88
|
+
A resource is alive while it has a holder: a subscribed `useResource`, or a pin. `ctx.resources.pin(address, signal)` keeps a resource open without subscribing until `signal` aborts, and an already-aborted signal pins nothing; the right Sidebar pins every open tab record's address for the record's life, so switching tabs unmounts a body without closing its stream. The first holder opens the provider's stream; the last release aborts it, discards the value, and returns the snapshot to `loading` (provider present) or `none` (absent). A frame the provider yields after that release is dropped, and the iterator is returned. `ctx.resources.source(address)` is the bare observable behind the hook, reference-stable per address, for callers outside React; reading its snapshot does not hold the resource ([lifecycle](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/client/resources/README.md#lifecycle)).
|
|
89
|
+
|
|
90
|
+
Streams carry metadata, not content. The `file` provider's value is `WorkspaceFileStat { absolutePath, version, bytes? }`: the first frame comes from Host `stat`, and later observations update the version. A consumer reads content through the Workspace Files Remote namespace; Preview owns refresh independently per tab ([`dsh-api-workspace-files`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/api/workspace-files/README.md)).
|
|
91
|
+
|
|
92
|
+
## Limits
|
|
93
|
+
|
|
94
|
+
Records live for the page lifetime: an address's record stays after its last holder leaves, holding no stream and no value, so memory grows with the number of distinct addresses ever read. A provider that ignores `signal` keeps running until its next frame. The failure type is the Remote face's `RemoteFailure`, so a provider whose source is not a Remote call mints one. A misspelled protocol or a malformed address reads as `none` with no other diagnostic.
|
|
@@ -11,7 +11,7 @@ Source: [`packages/compaction/compaction/src/types.ts`](https://github.com/deeps
|
|
|
11
11
|
|
|
12
12
|
## The `compaction/*` session events
|
|
13
13
|
|
|
14
|
-
Compaction extends [`SessionEventMap`](./session.md) with three event types via declaration merging. All three are **log-only** — they record the lock, summary, selected range, shadowed event seqs, token count, and model call without joining the surface. `SurfaceEventType` is deliberately NOT extended (only message-producing events reach the model), so the summary itself rides on a separate `user/message` with `surfaceOp: { op: 'replace',
|
|
14
|
+
Compaction extends [`SessionEventMap`](./session.md) with three event types via declaration merging. All three are **log-only** — they record the lock, summary, selected range, shadowed event seqs, token count, and model call without joining the surface. `SurfaceEventType` is deliberately NOT extended (only message-producing events reach the model), so the summary itself rides on a separate `user/message` with `surfaceOp: { op: 'replace', startSeq, endSeq }` — the only surface mutation performed by summary compaction. The [Agent Note](https://github.com/deepseek-ai/deepseek-harness/blob/master/.agents/notes/implemented/feature/2026-06-18-compaction-capability-seam.md) owns the rationale for reusing `user/message`.
|
|
15
15
|
|
|
16
16
|
| Event | Payload | Role |
|
|
17
17
|
|---|---|---|
|