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
|
@@ -24,8 +24,8 @@ interface UserMessage extends Message {
|
|
|
24
24
|
/**
|
|
25
25
|
* The merge-extensible, append-only source of truth for an agent interaction.
|
|
26
26
|
* Message history is derived from this log. Every event is lossless JSON and
|
|
27
|
-
* sequence numbers stay contiguous
|
|
28
|
-
*
|
|
27
|
+
* sequence numbers stay contiguous. Assistant attempt events embed their exact
|
|
28
|
+
* compact raw streams so persistence stores one durable settlement per attempt.
|
|
29
29
|
*/
|
|
30
30
|
interface SessionEventMap {
|
|
31
31
|
/**
|
|
@@ -56,8 +56,19 @@ interface SessionEventMap {
|
|
|
56
56
|
* project their `content` verbatim; `source` tells them apart.
|
|
57
57
|
*/
|
|
58
58
|
'user/message': UserMessage
|
|
59
|
-
/**
|
|
60
|
-
|
|
59
|
+
/**
|
|
60
|
+
* The rendered system prompt on the model-visible surface. The loop appends
|
|
61
|
+
* the first one as surface node 0 before the step's first `user/message`.
|
|
62
|
+
* A prepared in-history route can append nonempty changes in a continuing
|
|
63
|
+
* series. An incapable route or new series normalizes text to the first system
|
|
64
|
+
* node. Normalization empties nonempty later nodes, then rewrites the head if
|
|
65
|
+
* needed, through logged per-node replacements. An empty rendering always
|
|
66
|
+
* clears all active system nodes, leaving no older instructions model-visible.
|
|
67
|
+
* Empty later nodes are dormant and project to no message; an empty head with
|
|
68
|
+
* no active later node records "no system prompt". Restored nonempty text follows
|
|
69
|
+
* the same route and series rule; empty nodes never restore older text.
|
|
70
|
+
*/
|
|
71
|
+
'system/message': { turn: number; step: number; message: SystemMessage }
|
|
61
72
|
/**
|
|
62
73
|
* Assembled assistant message for one step (derived history uses this).
|
|
63
74
|
* Carries the step's `usage` when the adapter reported token accounting, so
|
|
@@ -68,7 +79,21 @@ interface SessionEventMap {
|
|
|
68
79
|
* marker distinguishes that prefix without re-deriving interruption from turn
|
|
69
80
|
* boundaries. An aborted turn with no such event streamed no visible content.
|
|
70
81
|
*/
|
|
71
|
-
'assistant/message': {
|
|
82
|
+
'assistant/message': {
|
|
83
|
+
turn: number
|
|
84
|
+
step: number
|
|
85
|
+
message: AssistantMessage
|
|
86
|
+
/** Exact timed model stream, compacted without joining delta boundaries. */
|
|
87
|
+
stream: AssistantStreamRecord[]
|
|
88
|
+
usage?: TokenUsage
|
|
89
|
+
interrupted?: true
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* One model attempt that committed no surface message. The embedded stream
|
|
93
|
+
* preserves a failed, retried, cancelled, or stream-error attempt that
|
|
94
|
+
* reached settlement without fabricating model-visible history.
|
|
95
|
+
*/
|
|
96
|
+
'assistant/attempt': { turn: number; step: number; stream: AssistantStreamRecord[] }
|
|
72
97
|
/**
|
|
73
98
|
* The model requested one tool invocation: `name` with the raw `arguments`
|
|
74
99
|
* JSON string exactly as the model produced it (unparsed). `callId` pairs the
|
|
@@ -90,6 +115,7 @@ interface SessionEventMap {
|
|
|
90
115
|
turn: number
|
|
91
116
|
step: number
|
|
92
117
|
message: ToolResultMessage
|
|
118
|
+
/** Optional failure identity; allowed only when the tool-result block has `isError: true`. */
|
|
93
119
|
error?: { name: string; code: string }
|
|
94
120
|
meta?: JsonValue
|
|
95
121
|
}
|
|
@@ -104,20 +130,22 @@ interface SessionEventMap {
|
|
|
104
130
|
startsSeries?: true
|
|
105
131
|
}
|
|
106
132
|
/**
|
|
107
|
-
* Route metadata for the next request, logged only when the route
|
|
108
|
-
* changes. It does not participate in request
|
|
133
|
+
* Route metadata for the next request, logged only when the route, capacity,
|
|
134
|
+
* or system prompt update mode changes. It does not participate in request
|
|
135
|
+
* reconstruction or header equality. Prompt admission uses the bound prepared
|
|
136
|
+
* call's capability, not this snapshot from an earlier request.
|
|
109
137
|
*/
|
|
110
138
|
'request/context': RequestContext
|
|
111
139
|
/**
|
|
112
140
|
* Marks the end of a constructor seed. Events before it have smaller seq
|
|
113
141
|
* values and came from the seed (resume, fork, or replay); this lifecycle
|
|
114
142
|
* produced none of them. This log-only event is the durable projection of
|
|
115
|
-
* {@link Session.firstLiveSeq}.
|
|
116
|
-
* carry the meaning.
|
|
143
|
+
* {@link Session.firstLiveSeq}.
|
|
117
144
|
*
|
|
118
|
-
*
|
|
119
|
-
*
|
|
120
|
-
*
|
|
145
|
+
* A fresh fork child owns one `{ inherited: true }` marker at its exact
|
|
146
|
+
* inherited-prefix cut, even when that prefix ends in an ancestor marker.
|
|
147
|
+
* The last tagged marker is the current Session's cut; untagged markers keep
|
|
148
|
+
* ordinary restore and replay lifecycle boundaries.
|
|
121
149
|
*
|
|
122
150
|
* `Session`'s constructor is the only legitimate writer. The invariant
|
|
123
151
|
* companion deliberately constrains nothing here, so a plugin appending one
|
|
@@ -130,7 +158,7 @@ interface SessionEventMap {
|
|
|
130
158
|
* writers — a concurrently live session holds its own boundary elsewhere,
|
|
131
159
|
* so tolerating concurrent writers needs a signal beyond the log.
|
|
132
160
|
*/
|
|
133
|
-
'session/end-seed':
|
|
161
|
+
'session/end-seed': { inherited?: true }
|
|
134
162
|
}
|
|
135
163
|
```
|
|
136
164
|
|
|
@@ -140,12 +168,13 @@ interface SessionEventMap {
|
|
|
140
168
|
|
|
141
169
|
### The request header event: `request/header`
|
|
142
170
|
|
|
143
|
-
The request envelope — the `EpochHeader` (call config + markers for adapter-supplied defaults +
|
|
171
|
+
The request envelope — the `EpochHeader` (call config + markers for adapter-supplied defaults + assembled tool schemas) — is logged session state, so every conversation request is a pure function of the log (the reconstructability Agent Note). The rendered system prompt is not part of the header: it is derived history, the `system/message` event at surface node 0 and any later in-history system node ([decision](https://github.com/deepseek-ai/deepseek-harness/blob/master/.agents/notes/implemented/architecture/2026-09-02-system-prompt-as-surface-node.md)), so a prompt change replaces or appends a system node and leaves the header unchanged. A full `request/header` snapshot with reason `'initial'` or `'resume'` records each loop-instance boundary; a changed request appends a snapshot with reason `'change'`; and an unchanged envelope beginning an explicitly declared message series or following a surface replacement appends a snapshot with reason `'series'`. A changed snapshot carries `startsSeries: true` when that request also begins a series. Ordinary append-only later Turns, further Steps, and retries in the same model-message series inherit the latest snapshot. `foldRequestHeader(events)` reconstructs the header by selecting the latest snapshot. The event is not a `SurfaceEventType`: it produces no LLM message.
|
|
144
172
|
|
|
145
173
|
```ts type-equiv
|
|
146
174
|
/**
|
|
147
|
-
* Logged request state outside derived history: call config
|
|
148
|
-
*
|
|
175
|
+
* Logged request state outside derived history: call config and tools. The
|
|
176
|
+
* system prompt is derived history — surface node 0, a `system/message` event.
|
|
177
|
+
* The latest full `request/header` snapshot reconstructs the header; canonical
|
|
149
178
|
* empty optional fields are absent.
|
|
150
179
|
*/
|
|
151
180
|
interface EpochHeader {
|
|
@@ -153,18 +182,16 @@ interface EpochHeader {
|
|
|
153
182
|
config: LlmCallConfig
|
|
154
183
|
/** Effective config fields materialized from the exact adapter rather than proposed by a caller. */
|
|
155
184
|
adapterDefaults?: LlmCallConfigAdapterDefaults
|
|
156
|
-
/** Rendered system prompt text; absent for a system-less request. */
|
|
157
|
-
system?: string
|
|
158
185
|
/** Assembled tool schemas; absent for a tool-less request. */
|
|
159
186
|
tools?: ToolSchema[]
|
|
160
187
|
}
|
|
161
188
|
```
|
|
162
189
|
|
|
163
|
-
|
|
190
|
+
Current event acceptance requires canonical `request/header.header`: any `system` field is forbidden, and `tools: []` and `adapterDefaults: {}` must be omitted. Whitespace-only system-message content, `config.stop: []`, and nested extensions remain unchanged. Seed, append, and current persistence reads reject noncanonical headers rather than silently normalizing them; [the V3 envelope decision](https://github.com/deepseek-ai/deepseek-harness/blob/master/.agents/notes/implemented/architecture/2026-09-06-v3-canonical-session-envelopes.md) owns historical conversion. Legacy v0 logs containing `request/header-delta` or its full-snapshot `fallback` reason are rejected rather than replayed incompletely.
|
|
164
191
|
|
|
165
192
|
### The route capacity event: `request/context`
|
|
166
193
|
|
|
167
|
-
The context metadata of the route a request resolved to is separate logged state, appended beside `request/header` inside the same step and only when the provider, model, or
|
|
194
|
+
The context metadata of the route a request resolved to is separate logged state, appended beside `request/header` inside the same step and only when the provider, model, capacity, or `systemPromptUpdate` mode differs from the previous record. It stays outside `EpochHeader` because that type is the reconstruction contract compared field-wise by `headerEquals`: capacity and the update mode describe a route, not a request input, so folding them in would let a route change register as a request-envelope `change` and would pull adapter metadata into the loop's reconstruction invariant. Like `request/header`, it is not a `SurfaceEventType` and produces no LLM message. `session.requestContext()` folds the latest record incrementally; the agent loop reads that record's `systemPromptUpdate` when it decides whether a changed system prompt replaces the latest system node or is appended after the cached history ([decision rule](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/core/agent-loop/README.md#understand-the-implementation)). A route whose adapter advertises no capacity is recorded with `contextWindow` absent, so the new record clears an older route's capacity; a route without a declared update mode likewise clears an older route's `systemPromptUpdate`.
|
|
168
195
|
|
|
169
196
|
```ts type-equiv
|
|
170
197
|
/** Registration-bound metadata for one resolved model route. */
|
|
@@ -175,6 +202,8 @@ interface RequestContext {
|
|
|
175
202
|
model: string
|
|
176
203
|
/** Maximum combined request and response context in tokens, when advertised. */
|
|
177
204
|
contextWindow?: number
|
|
205
|
+
/** `'in-history'` when the route reads the latest `system` message at any position as the effective system prompt. */
|
|
206
|
+
systemPromptUpdate?: SystemPromptUpdate
|
|
178
207
|
}
|
|
179
208
|
```
|
|
180
209
|
|
|
@@ -182,6 +211,28 @@ interface RequestContext {
|
|
|
182
211
|
|
|
183
212
|
A proper discriminated union over `type` (not independent `type`/`data` unions), so `switch (event.type)` narrows `event.data` without casts. `seq` is the monotonic position in the log (`seq = log.length`); `time` is epoch ms.
|
|
184
213
|
|
|
214
|
+
```ts type-equiv
|
|
215
|
+
/** Sequence number of one existing event in a Session log. */
|
|
216
|
+
type SessionSeq = BrandedNumber<'SessionSeq'>
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
```ts type-equiv
|
|
220
|
+
/** A Session log gap, prefix length, or read offset, which may equal the event count. */
|
|
221
|
+
type SessionLogOffset = BrandedNumber<'SessionLogOffset'>
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
```ts type-equiv
|
|
225
|
+
/** Inclusive Session event watermark, or `-1` before any event exists. */
|
|
226
|
+
type SessionSeqCursor = SessionSeq | -1
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
```ts type-equiv
|
|
230
|
+
/** One existing Session event position, or explicit absence. */
|
|
231
|
+
type OptionalSessionSeq = SessionSeq | null
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
`SessionSeq(value)` and `SessionLogOffset(value)` admit only non-negative safe integers and reject negative zero. They add compile-time brands without changing the serialized number; arithmetic returns an ordinary `number` that callers must admit again through the constructor for its intended role.
|
|
235
|
+
|
|
185
236
|
```ts type-equiv
|
|
186
237
|
/**
|
|
187
238
|
* One immutable entry in the session log.
|
|
@@ -190,9 +241,9 @@ A proper discriminated union over `type` (not independent `type`/`data` unions),
|
|
|
190
241
|
* unions), so `switch (event.type)` narrows `event.data` without casts.
|
|
191
242
|
*
|
|
192
243
|
* The {@link sourceEventSeqs} and {@link surfaceOp} fields are conditional:
|
|
193
|
-
* they only exist on {@link SurfaceEventType} variants (`user/message`,
|
|
244
|
+
* they only exist on {@link SurfaceEventType} variants (`system/message`, `user/message`,
|
|
194
245
|
* `assistant/message`, `tool/result`).
|
|
195
|
-
* Non-surface events (boundary markers,
|
|
246
|
+
* Non-surface events (boundary markers, attempts, errors) never carry
|
|
196
247
|
* surface metadata — the compiler enforces this at `Session.append()`
|
|
197
248
|
* call sites.
|
|
198
249
|
*/
|
|
@@ -200,33 +251,35 @@ type SessionEvent<T extends SessionEventType = SessionEventType> = {
|
|
|
200
251
|
[K in SessionEventType]: {
|
|
201
252
|
type: K
|
|
202
253
|
/** Monotonic sequence number within the session. */
|
|
203
|
-
seq:
|
|
254
|
+
seq: SessionSeq
|
|
204
255
|
/** Unix epoch milliseconds. */
|
|
205
256
|
time: number
|
|
206
257
|
data: SessionEventMap[K]
|
|
207
|
-
} & (K extends SurfaceEventType ? {
|
|
208
258
|
/**
|
|
209
|
-
*
|
|
210
|
-
*
|
|
211
|
-
*
|
|
212
|
-
*
|
|
213
|
-
*
|
|
214
|
-
*
|
|
259
|
+
* Marks an event a reader may safely skip when it does not recognize
|
|
260
|
+
* `type`. Absent means required: a reader meeting an unrecognized type
|
|
261
|
+
* without this marker MUST refuse to reconstruct the session instead of
|
|
262
|
+
* silently dropping the event, because an unrecognized required event may
|
|
263
|
+
* change how the rest of the log is interpreted. A writer sets `true` only
|
|
264
|
+
* on purely informational records whose loss cannot affect reconstruction;
|
|
265
|
+
* defaulting to required means a forgotten marker over-refuses (an
|
|
266
|
+
* inconvenience) rather than silently resuming a gutted session.
|
|
215
267
|
*/
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
surfaceOp?:
|
|
219
|
-
|
|
268
|
+
ignorable?: true
|
|
269
|
+
} & (K extends SurfaceEventType ? SurfaceIntent<K> : {
|
|
270
|
+
surfaceOp?: never
|
|
271
|
+
sourceEventSeqs?: never
|
|
272
|
+
})
|
|
220
273
|
}[T]
|
|
221
274
|
```
|
|
222
275
|
|
|
223
276
|
`SessionEventType = keyof SessionEventMap`. Because `SessionEventMap` is merge-extensible, switches over `SessionEvent` must NOT use `assertNever` — a plugin-added variant is a valid unknown value; handle the known cases and fall through `default`.
|
|
224
277
|
|
|
225
|
-
|
|
278
|
+
Every surface event requires `surfaceOp`; known log-only events forbid both surface metadata fields. Native unknown or obsolete ignorable envelopes remain opaque. `assistant/message` embeds its provider stream and forbids `sourceEventSeqs`. System, user, and tool surface events may cite a complete non-empty set of unique earlier events when their provenance or replacement operation requires it. A `tool/result` may carry `data.error` only when its tool-result block has `isError: true`; failure identity remains optional for failed results.
|
|
226
279
|
|
|
227
280
|
## Surface types
|
|
228
281
|
|
|
229
|
-
The
|
|
282
|
+
The four message-producing types (`SurfaceEventType` — `system/message`, `user/message`, `assistant/message`, `tool/result`) carry surface metadata declaring how they join the ordered derived surface. `system/message` holds the rendered system prompt: the loop appends the first one as surface node 0 and, when the prompt changes, replaces exactly the latest system node or appends a new one on an in-history route; the surface fold rejects any other replacement covering a `system/message` at node 0, while a later system node is ordinary history that a compaction replacement may shadow. See the [session surface Agent Note](https://github.com/deepseek-ai/deepseek-harness/blob/master/.agents/notes/implemented/architecture/2026-06-18-session-surface.md).
|
|
230
283
|
|
|
231
284
|
### `SurfaceEventType` — the message-producing subset of event types
|
|
232
285
|
|
|
@@ -234,9 +287,11 @@ The three message-producing types (`SurfaceEventType` — `user/message`, `assis
|
|
|
234
287
|
/**
|
|
235
288
|
* The subset of {@link SessionEventType} values whose events produce LLM
|
|
236
289
|
* messages and are eligible to appear on the ordered surface. Only these
|
|
237
|
-
* event types may carry {@link SurfaceOp} and
|
|
290
|
+
* event types may carry {@link SurfaceOp}; system, user, and tool events may also cite
|
|
291
|
+
* earlier sources through {@link SessionEvent.sourceEventSeqs}.
|
|
238
292
|
*/
|
|
239
293
|
type SurfaceEventType =
|
|
294
|
+
| 'system/message'
|
|
240
295
|
| 'user/message'
|
|
241
296
|
| 'assistant/message'
|
|
242
297
|
| 'tool/result'
|
|
@@ -251,19 +306,19 @@ type SurfaceEventType =
|
|
|
251
306
|
*
|
|
252
307
|
* - `'append'`: added to the tail — normal path for user/assistant/tool
|
|
253
308
|
* messages.
|
|
254
|
-
* - `{ op: 'replace',
|
|
255
|
-
* (inclusive) through `
|
|
256
|
-
* surface nodes in the current surface. `
|
|
309
|
+
* - `{ op: 'replace', startSeq, endSeq }`: replaces surface nodes from `startSeq`
|
|
310
|
+
* (inclusive) through `endSeq` (inclusive) with this node. Both must exist as
|
|
311
|
+
* surface nodes in the current surface. `startSeq === endSeq` replaces a single
|
|
257
312
|
* node. The node's {@link SessionEvent.sourceEventSeqs} must include every
|
|
258
313
|
* shadowed surface node. Used by compaction; any surface-replacing producer
|
|
259
314
|
* may use it.
|
|
260
315
|
*/
|
|
261
316
|
type SurfaceOp =
|
|
262
317
|
| 'append'
|
|
263
|
-
| { op: 'replace';
|
|
318
|
+
| { op: 'replace'; startSeq: SessionSeq; endSeq: SessionSeq }
|
|
264
319
|
```
|
|
265
320
|
|
|
266
|
-
`'append'` is the normal tail-append path. `replace`
|
|
321
|
+
`'append'` is the normal tail-append path. `replace` contains exactly `op`, `startSeq`, and `endSeq`, with no aliases or extra keys. It shadows the inclusive span between those current surface event sequences and inserts the new event in their place; equal endpoints replace one entry. Endpoints must precede the replacing event, but their relative order is surface order, not numeric sequence order.
|
|
267
322
|
|
|
268
323
|
### `SurfaceIntent` — the parameter to `session.append()`
|
|
269
324
|
|
|
@@ -272,21 +327,20 @@ type SurfaceOp =
|
|
|
272
327
|
* Surface placement and cited source-event seqs for {@link Session.append}. Required on
|
|
273
328
|
* message-producing events and forbidden on log-only events.
|
|
274
329
|
*/
|
|
275
|
-
|
|
330
|
+
type SurfaceIntent<T extends SurfaceEventType = SurfaceEventType> = {
|
|
276
331
|
surfaceOp: SurfaceOp
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
}
|
|
332
|
+
} & (T extends 'assistant/message' ? {
|
|
333
|
+
/** Assistant messages embed their provider stream instead of citing source events. */
|
|
334
|
+
sourceEventSeqs?: never
|
|
335
|
+
} : {
|
|
336
|
+
/** Complete non-empty set of known earlier source-event seqs. */
|
|
337
|
+
sourceEventSeqs?: SessionSeq[]
|
|
338
|
+
})
|
|
285
339
|
```
|
|
286
340
|
|
|
287
341
|
Required for `SurfaceEventType` events — every message-producing event must declare how it joins the surface, the sole source of derived model history. A human-facing transcript is the other projection and reads the log's append-origin events instead, because the surface deliberately shadows the ranges a replacement summarizes (`isAppendSurfaceEvent` in [dsh-session](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/core/session/README.md)). Non-surface types reject it at compile time.
|
|
288
342
|
|
|
289
|
-
|
|
343
|
+
`assistant/message` cannot carry `sourceEventSeqs`; its `stream` owns exact provider evidence. Other surface events omit the field when they cite no earlier event and use a complete non-empty list when they do.
|
|
290
344
|
|
|
291
345
|
### `SessionSurface` — the live readonly surface projection
|
|
292
346
|
|
|
@@ -298,7 +352,7 @@ Only `assistant/message` may carry a present empty `sourceEventSeqs`; when the f
|
|
|
298
352
|
/** Readonly live projection of the message-producing session events. */
|
|
299
353
|
interface SessionSurface {
|
|
300
354
|
/** Current surface event sequences in model-visible order. */
|
|
301
|
-
readonly nodes: readonly
|
|
355
|
+
readonly nodes: readonly SessionSeq[]
|
|
302
356
|
/** Monotonic count of committed positional replacements. */
|
|
303
357
|
readonly replaceGeneration: number
|
|
304
358
|
}
|
|
@@ -312,13 +366,13 @@ interface SessionSurface {
|
|
|
312
366
|
/** One replacement operation observed while folding a session surface. */
|
|
313
367
|
interface SurfaceFoldReplacement {
|
|
314
368
|
/** Seq of the event that replaced the prior surface range. */
|
|
315
|
-
seq:
|
|
369
|
+
seq: SessionSeq
|
|
316
370
|
/** Declared inclusive start seq of the replaced surface range. */
|
|
317
|
-
start:
|
|
371
|
+
start: SessionSeq
|
|
318
372
|
/** Declared inclusive end seq of the replaced surface range. */
|
|
319
|
-
end:
|
|
373
|
+
end: SessionSeq
|
|
320
374
|
/** Actual surface entries removed by the operation, in surface order. */
|
|
321
|
-
shadowedSeqs:
|
|
375
|
+
shadowedSeqs: SessionSeq[]
|
|
322
376
|
}
|
|
323
377
|
```
|
|
324
378
|
|
|
@@ -326,7 +380,7 @@ interface SurfaceFoldReplacement {
|
|
|
326
380
|
/** Complete result of replaying the surface operations in a session log. */
|
|
327
381
|
interface SurfaceFoldResult {
|
|
328
382
|
/** Current surface event sequences in model-visible order. */
|
|
329
|
-
nodes:
|
|
383
|
+
nodes: SessionSeq[]
|
|
330
384
|
/** Replacement operations in event order. */
|
|
331
385
|
replacements: SurfaceFoldReplacement[]
|
|
332
386
|
}
|
|
@@ -350,24 +404,27 @@ declare class Session {
|
|
|
350
404
|
get surface(): SessionSurface;
|
|
351
405
|
/**
|
|
352
406
|
* Detached, deep-frozen creation metadata (format version, cwd, lineage,
|
|
353
|
-
*
|
|
407
|
+
* and whether fork history exists). Supplied by the store via `ctx.sessions.create()`. When a
|
|
354
408
|
* `Session` is created without a store-owned header, a minimal header is
|
|
355
409
|
* synthesized (stamped with the current {@link SESSION_FORMAT_VERSION}) so
|
|
356
410
|
* `session.header` is always present. Kept out of the event log — it is a
|
|
357
411
|
* storage concern, not replayable conversation state.
|
|
358
412
|
*/
|
|
359
413
|
readonly header: SessionHeader;
|
|
414
|
+
/** Number of leading events inherited from this Session's fork parent. */
|
|
415
|
+
readonly inheritedEventCount: SessionLogOffset;
|
|
360
416
|
/** The session identity, derived from its durable header's single copy. */
|
|
361
417
|
get id(): SessionId;
|
|
362
418
|
/**
|
|
363
419
|
* The first seq appended IN THIS PROCESS: the length of the constructor
|
|
364
420
|
* seed (0 without one). Events with smaller seq values entered through
|
|
365
421
|
* construction — replay, fork, or resume — and were never published on the
|
|
366
|
-
* `session/event` firehose (constructor seeds do not emit)
|
|
367
|
-
*
|
|
368
|
-
*
|
|
369
|
-
*
|
|
370
|
-
*
|
|
422
|
+
* `session/event` firehose (constructor seeds do not emit). This offset marks
|
|
423
|
+
* the constructor-input boundary for lifecycle ownership and persistence
|
|
424
|
+
* adoption; consumers that need complete canonical history still start at
|
|
425
|
+
* seq 0. Distinct from {@link inheritedEventCount}, the DURABLE
|
|
426
|
+
* fork-lineage cut: a resumed session's constructor seed is its full stored
|
|
427
|
+
* log, while the inherited count keeps the original fork value — this field is the
|
|
371
428
|
* in-process construction fact.
|
|
372
429
|
*
|
|
373
430
|
* Not persisted itself: a seeded session projects it into the log as the
|
|
@@ -380,36 +437,74 @@ declare class Session {
|
|
|
380
437
|
* When this lifecycle appends the marker, it occupies this seq before the
|
|
381
438
|
* store attaches and therefore does not publish either. Otherwise this seq
|
|
382
439
|
* holds an ordinary published write.
|
|
383
|
-
|
|
384
|
-
readonly firstLiveSeq:
|
|
440
|
+
*/
|
|
441
|
+
readonly firstLiveSeq: SessionLogOffset;
|
|
385
442
|
/**
|
|
386
443
|
* Create a detached session by validating and snapshotting borrowed seed
|
|
387
444
|
* events and storage metadata.
|
|
388
445
|
* @param id - session identity.
|
|
389
446
|
* @param seed - optional borrowed replay or fork events.
|
|
390
447
|
* @param header - optional borrowed storage metadata.
|
|
448
|
+
* @param inheritedEventCount - exact fork-inherited prefix length for a seeded header.
|
|
391
449
|
* @returns a detached session.
|
|
392
450
|
*/
|
|
393
|
-
static create(
|
|
451
|
+
static create(
|
|
452
|
+
id: SessionId,
|
|
453
|
+
seed?: readonly SessionEvent[],
|
|
454
|
+
header?: SessionHeader,
|
|
455
|
+
inheritedEventCount?: SessionLogOffset,
|
|
456
|
+
): Session;
|
|
394
457
|
/**
|
|
395
|
-
* Restore a detached session by
|
|
396
|
-
*
|
|
397
|
-
* and header fields are validated
|
|
458
|
+
* Restore a detached session by adopting an independently owned or deeply frozen seed.
|
|
459
|
+
* Runtime-required event fields, event envelopes, sequence continuity, surface
|
|
460
|
+
* transitions, and header fields are validated without copying or freezing events.
|
|
461
|
+
* Embedded Assistant streams remain opaque until a stream consumer or storage
|
|
462
|
+
* verifier reads them.
|
|
398
463
|
* @param id - restored session identity.
|
|
399
|
-
* @param seed -
|
|
400
|
-
* @param header -
|
|
464
|
+
* @param seed - independently owned or deeply frozen events.
|
|
465
|
+
* @param header - independently owned storage metadata.
|
|
466
|
+
* @param inheritedEventCount - exact fork-inherited prefix length decoded from storage.
|
|
467
|
+
* @param eventState - aliasing state carried from the operation that produced the seed.
|
|
401
468
|
* @returns a restored detached session.
|
|
402
469
|
*/
|
|
403
|
-
static fromRestore(
|
|
470
|
+
static fromRestore(
|
|
471
|
+
id: SessionId,
|
|
472
|
+
seed: readonly SessionEvent[],
|
|
473
|
+
header: SessionHeader,
|
|
474
|
+
inheritedEventCount: SessionLogOffset,
|
|
475
|
+
eventState: SessionSeedEventState,
|
|
476
|
+
): Session;
|
|
477
|
+
/**
|
|
478
|
+
* Return the immutable event stored at one exact sequence number.
|
|
479
|
+
* @param seq - event sequence number.
|
|
480
|
+
* @returns the accepted event, or undefined when the log does not contain it.
|
|
481
|
+
*/
|
|
482
|
+
eventAt(seq: SessionSeq): SessionEvent | undefined;
|
|
483
|
+
/**
|
|
484
|
+
* Materialize an immutable snapshot of a half-open event sequence range.
|
|
485
|
+
* A full current snapshot is reused until the next append; every previously
|
|
486
|
+
* returned snapshot remains stable after later appends.
|
|
487
|
+
* @param fromSeq - non-negative inclusive sequence number; defaults to the log start.
|
|
488
|
+
* @param toSeqExclusive - non-negative exclusive sequence number; defaults to the current end.
|
|
489
|
+
* @returns a frozen array of the selected deeply frozen events.
|
|
490
|
+
*/
|
|
491
|
+
snapshotEvents(
|
|
492
|
+
fromSeq: SessionLogOffset = SessionLogOffset(0),
|
|
493
|
+
toSeqExclusive: SessionLogOffset = this.seq,
|
|
494
|
+
): readonly SessionEvent[];
|
|
495
|
+
/**
|
|
496
|
+
* Return this Session's events after its fork-inherited prefix.
|
|
497
|
+
* @returns a fresh array containing child-owned events in log order.
|
|
498
|
+
*/
|
|
499
|
+
ownEvents(): readonly SessionEvent[];
|
|
404
500
|
/**
|
|
405
|
-
*
|
|
406
|
-
*
|
|
407
|
-
*
|
|
408
|
-
* cast nor ordinary JavaScript can rewrite durable history.
|
|
501
|
+
* Whether one existing event position is outside the fork-inherited prefix.
|
|
502
|
+
* @param seq - event position in this Session.
|
|
503
|
+
* @returns true when the event belongs to this Session rather than its parent.
|
|
409
504
|
*/
|
|
410
|
-
|
|
505
|
+
isOwnSeq(seq: SessionSeq): boolean;
|
|
411
506
|
/** The next event's sequence number — always the log length (the `seq = log.length` contiguity contract). */
|
|
412
|
-
get seq():
|
|
507
|
+
get seq(): SessionLogOffset;
|
|
413
508
|
/**
|
|
414
509
|
* Append one typed event to the log and synchronously notify observers via
|
|
415
510
|
* the store-owned, module-private publication hooks. The hot path never blocks
|
|
@@ -427,7 +522,8 @@ declare class Session {
|
|
|
427
522
|
* declare how it joins the surface, the sole source of derived model
|
|
428
523
|
* history) and
|
|
429
524
|
* rejected by the compiler for non-surface types like `turn/start` or
|
|
430
|
-
* `assistant/
|
|
525
|
+
* `assistant/attempt`. Assistant messages embed their exact provider
|
|
526
|
+
* stream and cannot cite top-level source events.
|
|
431
527
|
* @returns the logged event — its assigned `seq`/`time` plus the SNAPSHOT of
|
|
432
528
|
* `data` that entered the log, so reading `event.data` back sees the logged
|
|
433
529
|
* value, never the caller's still-mutable input.
|
|
@@ -435,9 +531,10 @@ declare class Session {
|
|
|
435
531
|
* (BigInt, function, symbol, undefined, negative zero, non-finite number,
|
|
436
532
|
* circular reference, sparse array, or an exotic object such as
|
|
437
533
|
* Map/Set/Date/class instance), or when the candidate violates the
|
|
534
|
+
* request-header empty-field or tool-error consistency rules, or the
|
|
438
535
|
* canonical surface contract (marker shape and eligibility, unique
|
|
439
536
|
* earlier source-event references, positional replacement validity, and complete
|
|
440
|
-
* shadowed-node coverage). One
|
|
537
|
+
* shadowed-node coverage). One iterative pass reads, validates, and
|
|
441
538
|
* copies each nested value once, so a stateful getter cannot supply one value
|
|
442
539
|
* to validation and another to storage. The event log is the durable source
|
|
443
540
|
* of truth, so a bad event fails at the append site rather than later during
|
|
@@ -448,13 +545,13 @@ declare class Session {
|
|
|
448
545
|
append<T extends SessionEventType>(
|
|
449
546
|
type: T,
|
|
450
547
|
data: SessionEventMap[T],
|
|
451
|
-
...opts: T extends SurfaceEventType ? [opts: SurfaceIntent] : []
|
|
548
|
+
...opts: T extends SurfaceEventType ? [opts: SurfaceIntent<T>] : []
|
|
452
549
|
): SessionEvent<T>;
|
|
453
550
|
/**
|
|
454
551
|
* The {@link EpochHeader} in force after the log's last header event — the
|
|
455
552
|
* header the NEXT request will be compared against — or undefined before
|
|
456
553
|
* the first `request/header` snapshot. The live, incrementally-maintained
|
|
457
|
-
* form of `foldRequestHeader(session.
|
|
554
|
+
* form of `foldRequestHeader(session.snapshotEvents())`: each header event is folded
|
|
458
555
|
* once, when first seen, so a per-step read costs O(new events).
|
|
459
556
|
* @returns the folded header, or undefined when no header event exists yet.
|
|
460
557
|
*/
|
|
@@ -499,17 +596,17 @@ declare class Session {
|
|
|
499
596
|
`Session.deriveMessages()` projects the event log into the `Message[]` the model sees — cached (each surface node projected once, when first seen; a surface rewrite rebuilds) and frozen (a fresh array per call over shared, deep-frozen messages, so mutating logged history through a projection is unrepresentable). `deriveEventMessage(event)` is the per-node pure function the fold applies — public so external reconstructors and the dev invariant project a log prefix with exactly the same rules and cannot disagree with the cache. The projection rules:
|
|
500
597
|
|
|
501
598
|
- `user/message` → a user message carrying exact `content`; an optional envelope remains log-only display metadata.
|
|
502
|
-
- `assistant/message` → an assistant message with the provider and model that produced it plus optional adapter-private replay state.
|
|
599
|
+
- `assistant/message` → an assistant message with the provider and model that produced it plus optional adapter-private replay state. Its embedded compact stream is replay, usage, and UI evidence rather than a second message. An **empty-content** `assistant/message` is also skipped — a max-tokens step cut off with no content still records an `assistant/message` to hold its stream, usage, provider, and model, but a content-less assistant turn must not enter the provider transcript.
|
|
503
600
|
- `tool/result` → a user message carrying a `tool-result` block.
|
|
504
601
|
- `user/message` (injected context, i.e. non-`user` source) → a user-role message carrying its `content` verbatim at its chronological position; its typed source names the producer and carries any producer-specific data.
|
|
505
602
|
|
|
506
|
-
Everything else (`turn/*`, `step/*`, plugin-owned `llm/retry`) is structural and does not project into a message. Token accounting
|
|
603
|
+
Everything else (`turn/*`, `step/*`, `assistant/attempt`, plugin-owned `llm/retry`) is structural and does not project into a message. Token accounting expands the embedded stream on each `assistant/message` or `assistant/attempt`, while the message's top-level `usage` remains the committed-message authority when present. A failed model-request attempt therefore retains its provider usage without fabricating an assistant message. Current logical validation rejects request headers and assistant messages that omit provider/model instead of guessing a route; supported historical representations are normalized and validated by their adjacent format edge before a current Session exists.
|
|
507
604
|
|
|
508
605
|
## Live-session fork API
|
|
509
606
|
|
|
510
607
|
`ctx.sessions.create(id, { seed, meta })` is the low-level replay/fork primitive. For ordinary live-session forks, `SessionStore` exposes one policy API:
|
|
511
608
|
|
|
512
|
-
- `fork(source, boundary?, childSessionId?)` accepts a live `Session` object or live `SessionId`, selects source events through the inclusive `
|
|
609
|
+
- `fork(source, boundary?, childSessionId?)` accepts a live `Session` object or live `SessionId`, selects source events through the inclusive `SessionSeq` boundary (default: current last event), requires the selected prefix to end outside an open turn, then creates a live child session with deep-cloned seed events, `parentSession`, `isSeeded: true`, the exact `inheritedEventCount`, and inherited `cwd`.
|
|
513
610
|
|
|
514
611
|
An explicit `boundary` lets callers fork from any stable between-turn position, including a previous `turn/end` or a later standalone log-only event, even if the source has newer events or an open current turn. The API rejects a prefix that ends inside an open turn instead of clipping silently. Broader execution-relation sanity stays in the existing `dsh-invariants` plugin and persistence repair path rather than being duplicated in `fork()`. `dsh-subagent-fork-in-process` keeps its completed-prefix clipping because tool-time delegation usually starts while the parent turn is open; ordinary session branching should make the requested boundary explicit.
|
|
515
612
|
|
|
@@ -541,8 +638,10 @@ interface TurnEndReasonMap {
|
|
|
541
638
|
/** At least one step reached its output-token ceiling, even if a plugin continued the turn. */
|
|
542
639
|
'max-tokens': { kind: 'max-tokens' }
|
|
543
640
|
/**
|
|
544
|
-
* A
|
|
545
|
-
*
|
|
641
|
+
* A crash-orphaned turn was closed after the fact: agent-loop resume appends
|
|
642
|
+
* this closer for a stored log whose last turn never ended, and session-query
|
|
643
|
+
* synthesizes it on cold reads. The loop never emits this marker live, and
|
|
644
|
+
* the events recorded before the crash remain intact.
|
|
546
645
|
*/
|
|
547
646
|
interrupted: { kind: 'interrupted' }
|
|
548
647
|
}
|
|
@@ -558,9 +657,9 @@ The optional `dsh-session/invariant` companion enforces the relations owned by c
|
|
|
558
657
|
|
|
559
658
|
## The end-seed boundary: `session/end-seed`
|
|
560
659
|
|
|
561
|
-
A
|
|
660
|
+
A fresh fork constructor requires its seed to equal the inherited prefix and appends `session/end-seed { inherited: true }` at the exact durable cut. A restore retains that tagged marker and appends an ordinary `session/end-seed {}` only when its complete stored seed does not already end in a marker. Both forms are log-only and produce no message; `Session`'s constructor is the only legitimate writer.
|
|
562
661
|
|
|
563
|
-
|
|
662
|
+
For fork lineage, locate the LAST marker whose payload carries `inherited: true`; current-format decoding requires it exactly when `SessionHeader.isSeeded` is true and derives `inheritedEventCount` from its seq. For lifecycle ownership, locate the last `session/end-seed` of either form. Reopening a seed that already ends in any marker does not append another ordinary marker.
|
|
564
663
|
|
|
565
664
|
It exists because seed history and live work are otherwise byte-identical, which defeats any plugin owning a standalone open/close bracket: an unmatched `compaction/start` reads the same whether the writer crashed mid-compaction or is compacting right now. An opening marker before `session/end-seed` came from the constructor seed and belongs to an ended lifecycle, whatever ended it (a crash, a succeeding process, or a fork out of a still-running parent), so its owner may treat it as dead. That covers only brackets *this* session inherited: a concurrently live session holding an open bracket over the same history has its own boundary elsewhere, so tolerating concurrent writers needs a liveness signal beyond the log. Core writes the boundary and reads nothing from it — a bracket's vocabulary stays with its owning plugin, which is why crash repair closes turn/step/tool boundaries and never `compaction/*`.
|
|
566
665
|
|
|
@@ -572,11 +671,11 @@ A plugin may declaration-merge extra `SessionEventMap` types. These are **log-on
|
|
|
572
671
|
|
|
573
672
|
When several events in one plugin-owned family assemble into one Web Client Conversation Node, every start, update, result, resource, or interruption event in that family carries or independently derives the same stable business id. This requirement applies to correlated Node families, not to every Session event; it lets the client group each event without guessing from adjacency or scanning history. See the [Conversation subsystem](./conversation.md).
|
|
574
673
|
|
|
575
|
-
The hook bridges' `hook/invoked` / `hook/result` pairs (from `@deepseek-ai/dsh-hook-protocol`) correlate by `handlerId`. `UserPromptSubmit`, `PreToolUse`, `PostToolUse`, and `Stop` fire inside the loop's open turn, so their `hook/*` records are turn-enclosed by construction. `SessionStart` gets no `hook/*` record because it runs before turn 1; its context remains pending in the inbox until a waking delivery opens a turn
|
|
674
|
+
The hook bridges' `hook/invoked` / `hook/result` pairs (from `@deepseek-ai/dsh-hook-protocol`) correlate by `handlerId`. `UserPromptSubmit`, `PreToolUse`, `PostToolUse`, and `Stop` fire inside the loop's open turn, so their `hook/*` records are turn-enclosed by construction. `SessionStart` gets no `hook/*` record because it runs before turn 1; its context remains pending in the inbox until a waking delivery opens a turn.
|
|
576
675
|
|
|
577
676
|
## Durability contract
|
|
578
677
|
|
|
579
|
-
What a persistence backend relies on: the durable log persists every event losslessly,
|
|
678
|
+
What a persistence backend relies on: the durable log persists every event losslessly, and every Assistant attempt is one `assistant/message` or `assistant/attempt` whose embedded compact stream preserves the original timed chunks. `seq` stays contiguous across these settlements and all interleaved events. A backend may choose its own storage framing for an event batch as long as a handle's `read()` returns the exact appended events; current JSONL writes one row per event (see [persistence.md](./persistence.md)). All `event.data` must be JSON-serializable; `Session.append` enforces this at the source (throwing on non-serializable data), so a bad event never enters the log and `session.snapshotEvents()` always equals what a backend can persist. Adding an event type that carries non-serializable data, corrupts core execution nesting, or violates its owner's declared relation is a breaking change to the on-disk format.
|
|
580
679
|
|
|
581
680
|
The backends that consume this contract are on [persistence.md](./persistence.md).
|
|
582
681
|
|
|
@@ -584,7 +683,7 @@ The backends that consume this contract are on [persistence.md](./persistence.md
|
|
|
584
683
|
|
|
585
684
|
`ModelCatalog` is the Host-generation model directory returned by `session/modelCatalog`: it carries the deployment default, routable provider ids, successful provider groups, and isolated provider failures. It is not derived from one Session and remains separate from Session projections.
|
|
586
685
|
|
|
587
|
-
`SessionOpenWorkspacePathRequest` carries an absolute or workspace-resolved `path
|
|
686
|
+
`SessionOpenWorkspacePathRequest` carries an absolute or workspace-resolved `path`; optional `action: "reveal"` selects file-manager navigation instead of default-application opening. `SessionOpenWorkspacePathValue` confirms that the Host accepted the native handoff. A Session-aware Client resolves relative paths against its current Session cwd when known; the controller hands the path to the opener unchanged and reports invalid requests, cancellation, and opener failures through the Session Remote error vocabulary.
|
|
588
687
|
|
|
589
688
|
<!-- BEGIN GENERATED cordis-surface (gen-cordis-catalog.ts) — do not edit between markers -->
|
|
590
689
|
|
|
@@ -614,7 +713,7 @@ resolveAgent(sessionId: SessionId): Promise<ApiSessionAgentResult>
|
|
|
614
713
|
* @param signal - optional caller cancellation for persistence reads.
|
|
615
714
|
* @returns the current attached state or persisted header and event prefix.
|
|
616
715
|
*/
|
|
617
|
-
inspect( sessionId: SessionId, signal?: AbortSignal, ): Promise<
|
|
716
|
+
inspect( sessionId: SessionId, signal?: AbortSignal, ): Promise<SessionInspection>
|
|
618
717
|
|
|
619
718
|
/**
|
|
620
719
|
* Read all visible Session rows without resuming an Agent.
|
|
@@ -658,12 +757,18 @@ inspect( sessionId: SessionId, signal?: AbortSignal, ): Promise<{ meta: SessionH
|
|
|
658
757
|
*/
|
|
659
758
|
@Remote canOpenWorkspacePath(): boolean
|
|
660
759
|
|
|
760
|
+
/**
|
|
761
|
+
* Describe the serving desktop for authenticated file-action routes.
|
|
762
|
+
* @returns Host name, configured availability, and platform-specific file-manager behavior.
|
|
763
|
+
*/
|
|
764
|
+
workspaceDesktop(): { name: string; available: boolean; fileManager: 'finder' | 'explorer' | 'directory' | null }
|
|
765
|
+
|
|
661
766
|
/**
|
|
662
767
|
* Open one path prepared by a Session-aware caller on the Host desktop.
|
|
663
768
|
* @param request - path after best-effort Session workspace resolution.
|
|
664
769
|
* @param signal - caller lifetime; abort terminates the native command.
|
|
665
770
|
* @returns confirmation after the native opener accepts the path.
|
|
666
|
-
* @throws
|
|
771
|
+
* @throws RemoteError when the request is invalid, cancelled, or the opener fails.
|
|
667
772
|
*/
|
|
668
773
|
@Remote('openWorkspacePath') async openWorkspacePath( request: SessionOpenWorkspacePathRequest, signal: AbortSignal, ): Promise<SessionOpenWorkspacePathValue>
|
|
669
774
|
|
|
@@ -722,7 +827,8 @@ inspect( sessionId: SessionId, signal?: AbortSignal, ): Promise<{ meta: SessionH
|
|
|
722
827
|
* Follow one Session log from its opening or resume cursor.
|
|
723
828
|
* @param request - durable address and last committed sequence already held by the caller.
|
|
724
829
|
* @param signal - cancellation owned by the Remote stream carrier.
|
|
725
|
-
* @returns a complete opening snapshot followed by gap-free event
|
|
830
|
+
* @returns a complete opening snapshot followed by gap-free durable event
|
|
831
|
+
* frames and optional cursorless assistant-stream frames.
|
|
726
832
|
*/
|
|
727
833
|
@Remote({ mode: 'stream' }) follow(request: SessionFollowRequest, signal: AbortSignal): AsyncIterable<SessionFollowFrame>
|
|
728
834
|
|
|
@@ -734,7 +840,7 @@ inspect( sessionId: SessionId, signal?: AbortSignal, ): Promise<{ meta: SessionH
|
|
|
734
840
|
@Remote({ mode: 'stream' }) control(signal: AbortSignal): AsyncIterable<SessionControlFrame>
|
|
735
841
|
```
|
|
736
842
|
|
|
737
|
-
Types: [
|
|
843
|
+
Types: [SessionId](./core.md) · [SessionInspection](./persistence.md) · [SessionSearchRequest](./session-query.md)
|
|
738
844
|
|
|
739
845
|
Source: [`packages/api/session-controller/src/index.ts`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/api/session-controller/src/index.ts)
|
|
740
846
|
|
|
@@ -744,7 +850,7 @@ Source: [`packages/api/session-controller/src/index.ts`](https://github.com/deep
|
|
|
744
850
|
|
|
745
851
|
In-memory session store (`ctx.sessions`).
|
|
746
852
|
|
|
747
|
-
Persistence is intentionally not implemented here —
|
|
853
|
+
Persistence is intentionally not implemented here — the agent lifecycle attaches a session-log writer to each published session's write handle; a session published outside that lifecycle persists nothing.
|
|
748
854
|
|
|
749
855
|
```ts cordis-catalog
|
|
750
856
|
/**
|
|
@@ -781,10 +887,9 @@ create(id?: SessionId, options?: CreateSessionOptions): Session
|
|
|
781
887
|
*
|
|
782
888
|
* @param id - the session id; omitted, the store mints `session-<n>`.
|
|
783
889
|
* @param options - seed events and/or creation metadata for the header. With
|
|
784
|
-
* `
|
|
785
|
-
*
|
|
786
|
-
*
|
|
787
|
-
* retain no mutable aliases.
|
|
890
|
+
* `eventState`, every seed event is either independently owned or any
|
|
891
|
+
* shared value is deeply frozen; {@link Session.fromRestore} validates and
|
|
892
|
+
* adopts those values without copying or freezing them.
|
|
788
893
|
* @returns the constructed session, NOT yet in the store.
|
|
789
894
|
* @throws if a session with `id` already exists, metadata is not a plain
|
|
790
895
|
* lossless-JSON record with valid scalar fields, or `meta.cwd` is a
|
|
@@ -867,7 +972,7 @@ list(): Session[]
|
|
|
867
972
|
* `SessionStore`'s id policy.
|
|
868
973
|
* @returns The created live child session.
|
|
869
974
|
*/
|
|
870
|
-
fork(source: SessionForkSource, boundary?:
|
|
975
|
+
fork(source: SessionForkSource, boundary?: SessionSeq, childSessionId?: SessionId): Session
|
|
871
976
|
```
|
|
872
977
|
|
|
873
978
|
Types: [CreateSessionOptions](./persistence.md) · [PrepareSessionOptions](./persistence.md) · [SessionId](./core.md)
|