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
|
### 请求头事件:`request/header`
|
|
142
170
|
|
|
143
|
-
请求信封(即 `EpochHeader`:调用配置 + 适配器所提供默认值的标记 +
|
|
171
|
+
请求信封(即 `EpochHeader`:调用配置 + 适配器所提供默认值的标记 + 已组装的工具 schema)会作为会话状态写入日志,因此每个对话请求都是日志的纯函数(见可重建性 Agent Note)。渲染后的系统提示词不属于请求头:它是派生历史,即 surface 第 0 号节点上的 `system/message` 事件以及任何后续的历史内系统节点([决策](https://github.com/deepseek-ai/deepseek-harness/blob/master/.agents/notes/implemented/architecture/2026-09-02-system-prompt-as-surface-node.zh.md)),因此提示词变更替换或追加一个系统节点,而请求头保持不变。带有 reason `'initial'` 或 `'resume'` 的完整 `request/header` 快照记录每个 agent loop 实例的边界;请求变化时会追加 reason 为 `'change'` 的快照;未变的信封显式开启消息序列或跟随 surface 替换时,会追加 reason 为 `'series'` 的快照。如果发生变化的快照所属请求同时开启序列,它会携带 `startsSeries: true`。普通的仅追加后续 Turn,以及同一模型消息序列内的后续 Step 与重试沿用最新快照。`foldRequestHeader(events)` 通过选择最新快照重建请求头。该事件不是 `SurfaceEventType`,不产生 LLM 消息。
|
|
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
|
+
当前事件接纳要求 `request/header.header` 为规范形式:禁止任何 `system` 字段,必须省略 `tools: []` 与 `adapterDefaults: {}`。仅含空白的系统消息内容、`config.stop: []` 与嵌套扩展保持不变。seed、append 与当前持久化读取拒绝非规范 header,而不会静默规范化;[V3 信封决策](https://github.com/deepseek-ai/deepseek-harness/blob/master/.agents/notes/implemented/architecture/2026-09-06-v3-canonical-session-envelopes.zh.md)负责历史转换。包含旧版 `request/header-delta` 事件或完整快照原因为 `fallback` 的旧版 v0 日志,会被拒绝,而不会以不完整方式回放。
|
|
164
191
|
|
|
165
192
|
### 路由容量事件:`request/context`
|
|
166
193
|
|
|
167
|
-
请求所解析到的路由的上下文元数据是独立的已记录状态,在同一步骤内紧随 `request/header`
|
|
194
|
+
请求所解析到的路由的上下文元数据是独立的已记录状态,在同一步骤内紧随 `request/header` 追加,且仅在提供方、模型、容量或 `systemPromptUpdate` 模式与上一条记录不同时追加。它保持在 `EpochHeader` 之外,因为该类型是 `headerEquals` 逐字段比较的重建约定。容量与更新模式描述的是路由,不是请求输入,把它们折叠进去会让一次路由变化被登记为请求信封的 `change`,也会把适配器元数据拉进 loop 的重建不变式。与 `request/header` 一样,它不是 `SurfaceEventType`,也不产生 LLM 消息。`session.requestContext()` 以增量方式归并最新一条记录;agent loop 在决定变化后的系统提示词是替换最新的系统节点还是追加到已缓存历史之后时,读取该记录的 `systemPromptUpdate`([决策规则](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/core/agent-loop/README.zh.md#understand-the-implementation))。适配器不公布容量的路由会以缺失 `contextWindow` 的形式记录,因此新记录可以清除较早路由的容量;未声明更新模式的路由同样会清除较早路由的 `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
|
基于 `type` 的真正可辨识联合(而非独立的 `type`/`data` 联合),因此 `switch (event.type)` 能直接收窄 `event.data`,无需类型断言。`seq` 是日志中的单调递增位置(`seq = log.length`);`time` 为 epoch 毫秒。
|
|
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)` 与 `SessionLogOffset(value)` 只接纳非负安全整数,并拒绝负零。它们仅添加编译期品牌,不改变序列化后的数值;算术会返回普通 `number`,调用方必须按结果的预期角色通过对应构造器重新接纳。
|
|
235
|
+
|
|
185
236
|
```ts type-equiv
|
|
186
237
|
/**
|
|
187
238
|
* One immutable entry in the session log.
|
|
@@ -190,9 +241,9 @@ interface RequestContext {
|
|
|
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,35 +251,37 @@ 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`。由于 `SessionEventMap` 可通过合并扩展,对 `SessionEvent` 的 switch 语句禁止使用 `assertNever`:插件添加的变体是合法的未知值;处理已知 case 后在 `default` 中放行。
|
|
224
277
|
|
|
225
|
-
|
|
278
|
+
每个 surface 事件都要求 `surfaceOp`;已知仅日志事件禁止两个 surface 元数据字段。原生未知或已退役的可忽略信封保持不透明。`assistant/message` 嵌入其提供方 stream,并禁止 `sourceEventSeqs`。System、user 与 tool surface 事件可以在来源或替换操作需要时引用完整、非空且唯一的较早事件集合。`tool/result` 仅在工具结果块带有 `isError: true` 时可以携带 `data.error`;失败结果的失败身份仍可省略。
|
|
226
279
|
|
|
227
280
|
<a id="surface-types"></a>
|
|
228
281
|
|
|
229
282
|
## Surface 类型
|
|
230
283
|
|
|
231
|
-
|
|
284
|
+
四种产生消息的类型(`SurfaceEventType`:`system/message`、`user/message`、`assistant/message`、`tool/result`)携带 surface 元数据,用来声明它们如何加入有序的派生 surface。`system/message` 承载渲染后的系统提示词:循环把第一条追加为 surface 第 0 号节点,并在提示词变化时恰好替换最新的系统节点,或在历史内路由上追加一条新的;surface 折叠拒绝任何其他覆盖第 0 号节点 `system/message` 的替换,而后续系统节点是普通历史,压缩替换可以遮蔽它。见 [session surface Agent Note](https://github.com/deepseek-ai/deepseek-harness/blob/master/.agents/notes/implemented/architecture/2026-06-18-session-surface.zh.md)。
|
|
232
285
|
|
|
233
286
|
### `SurfaceEventType`:事件类型中产生消息的子集
|
|
234
287
|
|
|
@@ -236,9 +289,11 @@ type SessionEvent<T extends SessionEventType = SessionEventType> = {
|
|
|
236
289
|
/**
|
|
237
290
|
* The subset of {@link SessionEventType} values whose events produce LLM
|
|
238
291
|
* messages and are eligible to appear on the ordered surface. Only these
|
|
239
|
-
* event types may carry {@link SurfaceOp} and
|
|
292
|
+
* event types may carry {@link SurfaceOp}; system, user, and tool events may also cite
|
|
293
|
+
* earlier sources through {@link SessionEvent.sourceEventSeqs}.
|
|
240
294
|
*/
|
|
241
295
|
type SurfaceEventType =
|
|
296
|
+
| 'system/message'
|
|
242
297
|
| 'user/message'
|
|
243
298
|
| 'assistant/message'
|
|
244
299
|
| 'tool/result'
|
|
@@ -253,19 +308,19 @@ type SurfaceEventType =
|
|
|
253
308
|
*
|
|
254
309
|
* - `'append'`: added to the tail — normal path for user/assistant/tool
|
|
255
310
|
* messages.
|
|
256
|
-
* - `{ op: 'replace',
|
|
257
|
-
* (inclusive) through `
|
|
258
|
-
* surface nodes in the current surface. `
|
|
311
|
+
* - `{ op: 'replace', startSeq, endSeq }`: replaces surface nodes from `startSeq`
|
|
312
|
+
* (inclusive) through `endSeq` (inclusive) with this node. Both must exist as
|
|
313
|
+
* surface nodes in the current surface. `startSeq === endSeq` replaces a single
|
|
259
314
|
* node. The node's {@link SessionEvent.sourceEventSeqs} must include every
|
|
260
315
|
* shadowed surface node. Used by compaction; any surface-replacing producer
|
|
261
316
|
* may use it.
|
|
262
317
|
*/
|
|
263
318
|
type SurfaceOp =
|
|
264
319
|
| 'append'
|
|
265
|
-
| { op: 'replace';
|
|
320
|
+
| { op: 'replace'; startSeq: SessionSeq; endSeq: SessionSeq }
|
|
266
321
|
```
|
|
267
322
|
|
|
268
|
-
`'append'` 是常规的尾部追加路径。`replace`
|
|
323
|
+
`'append'` 是常规的尾部追加路径。`replace` 恰好包含 `op`、`startSeq` 和 `endSeq`,不接受别名或额外键。它遮蔽这两个当前 surface 事件序号之间的闭区间,并在原位置插入新事件;相同端点仅替换一个条目。端点必须早于替换事件,但它们的相对顺序按 surface 顺序而非数值序号顺序确定。
|
|
269
324
|
|
|
270
325
|
### `SurfaceIntent`:`session.append()` 的参数
|
|
271
326
|
|
|
@@ -274,21 +329,20 @@ type SurfaceOp =
|
|
|
274
329
|
* Surface placement and cited source-event seqs for {@link Session.append}. Required on
|
|
275
330
|
* message-producing events and forbidden on log-only events.
|
|
276
331
|
*/
|
|
277
|
-
|
|
332
|
+
type SurfaceIntent<T extends SurfaceEventType = SurfaceEventType> = {
|
|
278
333
|
surfaceOp: SurfaceOp
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
}
|
|
334
|
+
} & (T extends 'assistant/message' ? {
|
|
335
|
+
/** Assistant messages embed their provider stream instead of citing source events. */
|
|
336
|
+
sourceEventSeqs?: never
|
|
337
|
+
} : {
|
|
338
|
+
/** Complete non-empty set of known earlier source-event seqs. */
|
|
339
|
+
sourceEventSeqs?: SessionSeq[]
|
|
340
|
+
})
|
|
287
341
|
```
|
|
288
342
|
|
|
289
343
|
对 `SurfaceEventType` 事件必填:每个产生消息的事件都必须声明它如何加入 surface(派生模型历史的唯一来源)。面向人类的 transcript(文本记录)是另一个投影,读取的是日志中追加来源的事件,因为 surface 会有意遮蔽替换所概括的范围(见 [dsh-session](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/core/session/README.zh.md) 的 `isAppendSurfaceEvent`)。非 surface 类型在编译期拒绝此参数。
|
|
290
344
|
|
|
291
|
-
|
|
345
|
+
`assistant/message` 不能携带 `sourceEventSeqs`;它的 `stream` 拥有精确 provider 证据。其他 surface event 不引用较早 event 时省略该字段,需要引用时使用完整非空 list。
|
|
292
346
|
|
|
293
347
|
### `SessionSurface`:实时只读 surface 投影
|
|
294
348
|
|
|
@@ -300,7 +354,7 @@ interface SurfaceIntent {
|
|
|
300
354
|
/** Readonly live projection of the message-producing session events. */
|
|
301
355
|
interface SessionSurface {
|
|
302
356
|
/** Current surface event sequences in model-visible order. */
|
|
303
|
-
readonly nodes: readonly
|
|
357
|
+
readonly nodes: readonly SessionSeq[]
|
|
304
358
|
/** Monotonic count of committed positional replacements. */
|
|
305
359
|
readonly replaceGeneration: number
|
|
306
360
|
}
|
|
@@ -314,13 +368,13 @@ interface SessionSurface {
|
|
|
314
368
|
/** One replacement operation observed while folding a session surface. */
|
|
315
369
|
interface SurfaceFoldReplacement {
|
|
316
370
|
/** Seq of the event that replaced the prior surface range. */
|
|
317
|
-
seq:
|
|
371
|
+
seq: SessionSeq
|
|
318
372
|
/** Declared inclusive start seq of the replaced surface range. */
|
|
319
|
-
start:
|
|
373
|
+
start: SessionSeq
|
|
320
374
|
/** Declared inclusive end seq of the replaced surface range. */
|
|
321
|
-
end:
|
|
375
|
+
end: SessionSeq
|
|
322
376
|
/** Actual surface entries removed by the operation, in surface order. */
|
|
323
|
-
shadowedSeqs:
|
|
377
|
+
shadowedSeqs: SessionSeq[]
|
|
324
378
|
}
|
|
325
379
|
```
|
|
326
380
|
|
|
@@ -328,7 +382,7 @@ interface SurfaceFoldReplacement {
|
|
|
328
382
|
/** Complete result of replaying the surface operations in a session log. */
|
|
329
383
|
interface SurfaceFoldResult {
|
|
330
384
|
/** Current surface event sequences in model-visible order. */
|
|
331
|
-
nodes:
|
|
385
|
+
nodes: SessionSeq[]
|
|
332
386
|
/** Replacement operations in event order. */
|
|
333
387
|
replacements: SurfaceFoldReplacement[]
|
|
334
388
|
}
|
|
@@ -352,24 +406,27 @@ declare class Session {
|
|
|
352
406
|
get surface(): SessionSurface;
|
|
353
407
|
/**
|
|
354
408
|
* Detached, deep-frozen creation metadata (format version, cwd, lineage,
|
|
355
|
-
*
|
|
409
|
+
* and whether fork history exists). Supplied by the store via `ctx.sessions.create()`. When a
|
|
356
410
|
* `Session` is created without a store-owned header, a minimal header is
|
|
357
411
|
* synthesized (stamped with the current {@link SESSION_FORMAT_VERSION}) so
|
|
358
412
|
* `session.header` is always present. Kept out of the event log — it is a
|
|
359
413
|
* storage concern, not replayable conversation state.
|
|
360
414
|
*/
|
|
361
415
|
readonly header: SessionHeader;
|
|
416
|
+
/** Number of leading events inherited from this Session's fork parent. */
|
|
417
|
+
readonly inheritedEventCount: SessionLogOffset;
|
|
362
418
|
/** The session identity, derived from its durable header's single copy. */
|
|
363
419
|
get id(): SessionId;
|
|
364
420
|
/**
|
|
365
421
|
* The first seq appended IN THIS PROCESS: the length of the constructor
|
|
366
422
|
* seed (0 without one). Events with smaller seq values entered through
|
|
367
423
|
* construction — replay, fork, or resume — and were never published on the
|
|
368
|
-
* `session/event` firehose (constructor seeds do not emit)
|
|
369
|
-
*
|
|
370
|
-
*
|
|
371
|
-
*
|
|
372
|
-
*
|
|
424
|
+
* `session/event` firehose (constructor seeds do not emit). This offset marks
|
|
425
|
+
* the constructor-input boundary for lifecycle ownership and persistence
|
|
426
|
+
* adoption; consumers that need complete canonical history still start at
|
|
427
|
+
* seq 0. Distinct from {@link inheritedEventCount}, the DURABLE
|
|
428
|
+
* fork-lineage cut: a resumed session's constructor seed is its full stored
|
|
429
|
+
* log, while the inherited count keeps the original fork value — this field is the
|
|
373
430
|
* in-process construction fact.
|
|
374
431
|
*
|
|
375
432
|
* Not persisted itself: a seeded session projects it into the log as the
|
|
@@ -382,36 +439,74 @@ declare class Session {
|
|
|
382
439
|
* When this lifecycle appends the marker, it occupies this seq before the
|
|
383
440
|
* store attaches and therefore does not publish either. Otherwise this seq
|
|
384
441
|
* holds an ordinary published write.
|
|
385
|
-
|
|
386
|
-
readonly firstLiveSeq:
|
|
442
|
+
*/
|
|
443
|
+
readonly firstLiveSeq: SessionLogOffset;
|
|
387
444
|
/**
|
|
388
445
|
* Create a detached session by validating and snapshotting borrowed seed
|
|
389
446
|
* events and storage metadata.
|
|
390
447
|
* @param id - session identity.
|
|
391
448
|
* @param seed - optional borrowed replay or fork events.
|
|
392
449
|
* @param header - optional borrowed storage metadata.
|
|
450
|
+
* @param inheritedEventCount - exact fork-inherited prefix length for a seeded header.
|
|
393
451
|
* @returns a detached session.
|
|
394
452
|
*/
|
|
395
|
-
static create(
|
|
453
|
+
static create(
|
|
454
|
+
id: SessionId,
|
|
455
|
+
seed?: readonly SessionEvent[],
|
|
456
|
+
header?: SessionHeader,
|
|
457
|
+
inheritedEventCount?: SessionLogOffset,
|
|
458
|
+
): Session;
|
|
396
459
|
/**
|
|
397
|
-
* Restore a detached session by
|
|
398
|
-
*
|
|
399
|
-
* and header fields are validated
|
|
460
|
+
* Restore a detached session by adopting an independently owned or deeply frozen seed.
|
|
461
|
+
* Runtime-required event fields, event envelopes, sequence continuity, surface
|
|
462
|
+
* transitions, and header fields are validated without copying or freezing events.
|
|
463
|
+
* Embedded Assistant streams remain opaque until a stream consumer or storage
|
|
464
|
+
* verifier reads them.
|
|
400
465
|
* @param id - restored session identity.
|
|
401
|
-
* @param seed -
|
|
402
|
-
* @param header -
|
|
466
|
+
* @param seed - independently owned or deeply frozen events.
|
|
467
|
+
* @param header - independently owned storage metadata.
|
|
468
|
+
* @param inheritedEventCount - exact fork-inherited prefix length decoded from storage.
|
|
469
|
+
* @param eventState - aliasing state carried from the operation that produced the seed.
|
|
403
470
|
* @returns a restored detached session.
|
|
404
471
|
*/
|
|
405
|
-
static fromRestore(
|
|
472
|
+
static fromRestore(
|
|
473
|
+
id: SessionId,
|
|
474
|
+
seed: readonly SessionEvent[],
|
|
475
|
+
header: SessionHeader,
|
|
476
|
+
inheritedEventCount: SessionLogOffset,
|
|
477
|
+
eventState: SessionSeedEventState,
|
|
478
|
+
): Session;
|
|
479
|
+
/**
|
|
480
|
+
* Return the immutable event stored at one exact sequence number.
|
|
481
|
+
* @param seq - event sequence number.
|
|
482
|
+
* @returns the accepted event, or undefined when the log does not contain it.
|
|
483
|
+
*/
|
|
484
|
+
eventAt(seq: SessionSeq): SessionEvent | undefined;
|
|
485
|
+
/**
|
|
486
|
+
* Materialize an immutable snapshot of a half-open event sequence range.
|
|
487
|
+
* A full current snapshot is reused until the next append; every previously
|
|
488
|
+
* returned snapshot remains stable after later appends.
|
|
489
|
+
* @param fromSeq - non-negative inclusive sequence number; defaults to the log start.
|
|
490
|
+
* @param toSeqExclusive - non-negative exclusive sequence number; defaults to the current end.
|
|
491
|
+
* @returns a frozen array of the selected deeply frozen events.
|
|
492
|
+
*/
|
|
493
|
+
snapshotEvents(
|
|
494
|
+
fromSeq: SessionLogOffset = SessionLogOffset(0),
|
|
495
|
+
toSeqExclusive: SessionLogOffset = this.seq,
|
|
496
|
+
): readonly SessionEvent[];
|
|
497
|
+
/**
|
|
498
|
+
* Return this Session's events after its fork-inherited prefix.
|
|
499
|
+
* @returns a fresh array containing child-owned events in log order.
|
|
500
|
+
*/
|
|
501
|
+
ownEvents(): readonly SessionEvent[];
|
|
406
502
|
/**
|
|
407
|
-
*
|
|
408
|
-
*
|
|
409
|
-
*
|
|
410
|
-
* cast nor ordinary JavaScript can rewrite durable history.
|
|
503
|
+
* Whether one existing event position is outside the fork-inherited prefix.
|
|
504
|
+
* @param seq - event position in this Session.
|
|
505
|
+
* @returns true when the event belongs to this Session rather than its parent.
|
|
411
506
|
*/
|
|
412
|
-
|
|
507
|
+
isOwnSeq(seq: SessionSeq): boolean;
|
|
413
508
|
/** The next event's sequence number — always the log length (the `seq = log.length` contiguity contract). */
|
|
414
|
-
get seq():
|
|
509
|
+
get seq(): SessionLogOffset;
|
|
415
510
|
/**
|
|
416
511
|
* Append one typed event to the log and synchronously notify observers via
|
|
417
512
|
* the store-owned, module-private publication hooks. The hot path never blocks
|
|
@@ -429,7 +524,8 @@ declare class Session {
|
|
|
429
524
|
* declare how it joins the surface, the sole source of derived model
|
|
430
525
|
* history) and
|
|
431
526
|
* rejected by the compiler for non-surface types like `turn/start` or
|
|
432
|
-
* `assistant/
|
|
527
|
+
* `assistant/attempt`. Assistant messages embed their exact provider
|
|
528
|
+
* stream and cannot cite top-level source events.
|
|
433
529
|
* @returns the logged event — its assigned `seq`/`time` plus the SNAPSHOT of
|
|
434
530
|
* `data` that entered the log, so reading `event.data` back sees the logged
|
|
435
531
|
* value, never the caller's still-mutable input.
|
|
@@ -437,9 +533,10 @@ declare class Session {
|
|
|
437
533
|
* (BigInt, function, symbol, undefined, negative zero, non-finite number,
|
|
438
534
|
* circular reference, sparse array, or an exotic object such as
|
|
439
535
|
* Map/Set/Date/class instance), or when the candidate violates the
|
|
536
|
+
* request-header empty-field or tool-error consistency rules, or the
|
|
440
537
|
* canonical surface contract (marker shape and eligibility, unique
|
|
441
538
|
* earlier source-event references, positional replacement validity, and complete
|
|
442
|
-
* shadowed-node coverage). One
|
|
539
|
+
* shadowed-node coverage). One iterative pass reads, validates, and
|
|
443
540
|
* copies each nested value once, so a stateful getter cannot supply one value
|
|
444
541
|
* to validation and another to storage. The event log is the durable source
|
|
445
542
|
* of truth, so a bad event fails at the append site rather than later during
|
|
@@ -450,13 +547,13 @@ declare class Session {
|
|
|
450
547
|
append<T extends SessionEventType>(
|
|
451
548
|
type: T,
|
|
452
549
|
data: SessionEventMap[T],
|
|
453
|
-
...opts: T extends SurfaceEventType ? [opts: SurfaceIntent] : []
|
|
550
|
+
...opts: T extends SurfaceEventType ? [opts: SurfaceIntent<T>] : []
|
|
454
551
|
): SessionEvent<T>;
|
|
455
552
|
/**
|
|
456
553
|
* The {@link EpochHeader} in force after the log's last header event — the
|
|
457
554
|
* header the NEXT request will be compared against — or undefined before
|
|
458
555
|
* the first `request/header` snapshot. The live, incrementally-maintained
|
|
459
|
-
* form of `foldRequestHeader(session.
|
|
556
|
+
* form of `foldRequestHeader(session.snapshotEvents())`: each header event is folded
|
|
460
557
|
* once, when first seen, so a per-step read costs O(new events).
|
|
461
558
|
* @returns the folded header, or undefined when no header event exists yet.
|
|
462
559
|
*/
|
|
@@ -501,17 +598,17 @@ declare class Session {
|
|
|
501
598
|
`Session.deriveMessages()` 将事件日志投影为模型看到的 `Message[]`。它是缓存的(每个 surface 节点在首次出现时投影一次;surface 重写触发重建)且冻结的(每次调用返回一个新数组,引用共享的深冻结消息,因此通过投影修改已记录的历史在类型上不可表达)。`deriveEventMessage(event)` 是折叠所应用的逐节点纯函数,公开暴露以便外部重建器和开发不变式检查能以完全相同的规则投影日志前缀,不会与缓存产生分歧。投影规则:
|
|
502
599
|
|
|
503
600
|
- `user/message` → 一条携带确切 `content` 的 user 消息;可选 envelope 仅作为日志中的展示元数据保留。
|
|
504
|
-
- `assistant/message` → 一条 assistant
|
|
601
|
+
- `assistant/message` → 一条 assistant 消息,包含生成它的提供方和模型,以及可选的适配器私有回放状态。其嵌入式紧凑 stream 是回放、usage 与 UI 证据,而不是第二条 message。**内容为空的** `assistant/message` 也会跳过:因 max-tokens 而截断且无内容的步骤仍会记录一条 `assistant/message` 来保存 stream、usage、提供方和模型,但无内容的 assistant 轮次不得进入提供方 transcript(文本记录)。
|
|
505
602
|
- `tool/result` → 一条携带 `tool-result` 块的 user 消息。
|
|
506
603
|
- `user/message`(注入上下文,即非 `user` 来源)→ 按时间顺序在相应位置生成一条 user-role 消息,并原样承载其 `content`;其类型化 source 标明生产方,并携带所有生产方专用数据。
|
|
507
604
|
|
|
508
|
-
其余所有事件(`turn/*`、`step
|
|
605
|
+
其余所有事件(`turn/*`、`step/*`、`assistant/attempt`、插件所属的 `llm/retry`)均为结构信息,不会投影为消息。token 记账会展开每个 `assistant/message` 或 `assistant/attempt` 的嵌入式 stream,message 顶层 `usage` 存在时仍是已提交 message 的权威。失败的模型请求 attempt 因此可以保留提供方 usage,而无需虚构 assistant message。当前逻辑校验会拒绝没有提供方/模型的 request header 和 assistant 消息,而不会猜测路由;受支持的历史表示会在当前 Session 存在前,由其相邻格式迁移边归一化并校验。
|
|
509
606
|
|
|
510
607
|
## 活跃会话 fork API
|
|
511
608
|
|
|
512
609
|
`ctx.sessions.create(id, { seed, meta })` 是底层的回放/fork 原语。对于普通的活跃会话 fork,`SessionStore` 暴露一个策略 API:
|
|
513
610
|
|
|
514
|
-
- `fork(source, boundary?, childSessionId?)` 接受一个活跃的 `Session` 对象或活跃的 `SessionId`,选取到 `
|
|
611
|
+
- `fork(source, boundary?, childSessionId?)` 接受一个活跃的 `Session` 对象或活跃的 `SessionId`,选取到 `SessionSeq` boundary(含)为止的源事件(默认为当前最后一个事件),要求所选前缀结束时没有开放轮次,然后创建一个活跃的子会话,包含深克隆的 seed event、`parentSession`、`isSeeded: true`、精确 `inheritedEventCount` 及继承的 `cwd`。
|
|
515
612
|
|
|
516
613
|
显式 `boundary` 允许调用者从任意稳定的轮次间位置 fork,包括之前的 `turn/end` 或更晚的独立纯日志事件,即使源会话有更新的事件或正在进行的轮次。API 拒绝结束于开放轮次内的前缀,而不是静默截断。更广泛的执行关系健全性检查留在既有的 `dsh-invariants` 插件和持久化修复路径中,不在 `fork()` 中重复。`dsh-subagent-fork-in-process` 保留其已完成前缀截断逻辑,因为工具调用时的委托通常在父轮次仍然打开时启动;普通的会话分支应显式指定请求的 boundary。
|
|
517
614
|
|
|
@@ -545,8 +642,10 @@ interface TurnEndReasonMap {
|
|
|
545
642
|
/** At least one step reached its output-token ceiling, even if a plugin continued the turn. */
|
|
546
643
|
'max-tokens': { kind: 'max-tokens' }
|
|
547
644
|
/**
|
|
548
|
-
* A
|
|
549
|
-
*
|
|
645
|
+
* A crash-orphaned turn was closed after the fact: agent-loop resume appends
|
|
646
|
+
* this closer for a stored log whose last turn never ended, and session-query
|
|
647
|
+
* synthesizes it on cold reads. The loop never emits this marker live, and
|
|
648
|
+
* the events recorded before the crash remain intact.
|
|
550
649
|
*/
|
|
551
650
|
interrupted: { kind: 'interrupted' }
|
|
552
651
|
}
|
|
@@ -562,9 +661,9 @@ interface TurnEndReasonMap {
|
|
|
562
661
|
|
|
563
662
|
## 种子结束边界:`session/end-seed`
|
|
564
663
|
|
|
565
|
-
|
|
664
|
+
新 fork constructor 要求 seed 等于 inherited prefix,并在精确持久 cut 追加 `session/end-seed { inherited: true }`。restore 会保留该 tagged marker,并且只在完整 stored seed 尚未以 marker 结尾时追加普通 `session/end-seed {}`。两种形式都只进入 log 且不产生 message;`Session` constructor 是唯一合法 writer。
|
|
566
665
|
|
|
567
|
-
|
|
666
|
+
对于 fork lineage,定位 payload 携带 `inherited: true` 的最后一个 marker;当前格式 decoding 只在 `SessionHeader.isSeeded` 为 true 时要求该 marker,并从其 seq 推导 `inheritedEventCount`。对于 lifecycle ownership,定位任一形式的最后一个 `session/end-seed`。重新打开已经以任一 marker 结尾的 seed 时,不会再追加普通 marker。
|
|
568
667
|
|
|
569
668
|
它之所以必要,是因为种子历史与实时工作在字节层面完全相同,这会让任何拥有独立开/闭括号的插件失效:一个未配对的 `compaction/start`,无论写入方是在压缩中途崩溃、还是此刻正在压缩,读起来都一样。在 `session/end-seed` 之前的开启标记来自构造种子,并且属于一个已结束的生命周期,无论结束原因为何(崩溃、进程接替,或从仍在运行的父会话 fork 出来),因此其所有方可以视之为已死。这只覆盖*本*会话继承的括号:另一个并发存活的会话可能在同一段历史上持有开放括号,而它自己的边界在别处,因此容忍并发写入方还需要日志之外的存活信号。核心写入该边界但不从中读取任何内容——括号的词汇表仍归其所属插件,这也正是崩溃修复只关闭轮次/步骤/工具边界而从不处理 `compaction/*` 的原因。
|
|
570
669
|
|
|
@@ -576,11 +675,11 @@ interface TurnEndReasonMap {
|
|
|
576
675
|
|
|
577
676
|
如果同一个插件事件族中的多条事件要组装成一个 Web Client Conversation Node,该事件族中的每条 start、update、result、resource 或 interruption 事件都必须携带或独立推导出同一个稳定业务 id。此要求只约束需要关联的 Node 事件族,并不要求每条 Session 事件都有业务 id;Client 因此无须根据相邻关系猜测归属,也无须扫描历史。参见 [Conversation 子系统](./conversation.md)。
|
|
578
677
|
|
|
579
|
-
钩子桥接层的 `hook/invoked` / `hook/result` 对(来自 `@deepseek-ai/dsh-hook-protocol`)通过 `handlerId` 关联。`UserPromptSubmit`、`PreToolUse`、`PostToolUse` 与 `Stop` 在 loop 已打开的轮次内触发,因此其 `hook/*` 记录天然位于轮次之内。`SessionStart` 不生成 `hook/*` 记录,因为它在轮次 1 之前运行;其上下文会在 inbox
|
|
678
|
+
钩子桥接层的 `hook/invoked` / `hook/result` 对(来自 `@deepseek-ai/dsh-hook-protocol`)通过 `handlerId` 关联。`UserPromptSubmit`、`PreToolUse`、`PostToolUse` 与 `Stop` 在 loop 已打开的轮次内触发,因此其 `hook/*` 记录天然位于轮次之内。`SessionStart` 不生成 `hook/*` 记录,因为它在轮次 1 之前运行;其上下文会在 inbox 中保持待处理,直到唤醒交付打开一个轮次。
|
|
580
679
|
|
|
581
680
|
## 持久性约定
|
|
582
681
|
|
|
583
|
-
|
|
682
|
+
持久化后端依赖的约定如下:持久日志无损保存每个事件,每个 Assistant attempt 都是一个 `assistant/message` 或 `assistant/attempt`,其嵌入式紧凑 stream 会保留原始带时间 chunk。`seq` 在这些 settlement 与所有交错事件之间保持连续。后端可以为事件批次选择自己的存储 framing,只要句柄的 `read()` 返回与追加时完全一致的事件即可;当前 JSONL 每个事件写一行(见 [persistence.md](./persistence.md))。所有 `event.data` 都必须可序列化为 JSON;`Session.append` 会从源头强制这一要求(遇到不可序列化数据时抛出),因此错误事件绝不会进入日志,`session.snapshotEvents()` 始终与后端可持久化的内容一致。新增会携带不可序列化数据、破坏核心执行嵌套或违反事件所有方声明关系的事件类型,都会构成磁盘格式的破坏性变更。
|
|
584
683
|
|
|
585
684
|
消费此约定的后端见 [persistence.md](./persistence.md)。
|
|
586
685
|
|
|
@@ -588,7 +687,7 @@ interface TurnEndReasonMap {
|
|
|
588
687
|
|
|
589
688
|
`ModelCatalog` 是 `session/modelCatalog` 返回的 Host generation 模型目录:它携带部署默认值、可路由 provider id、成功的 provider 分组与相互隔离的 provider 失败。它不由某个 Session 派生,因此与 Session projection 分开保存。
|
|
590
689
|
|
|
591
|
-
`SessionOpenWorkspacePathRequest` 携带绝对路径或已按 workspace 解析的 `path`。`SessionOpenWorkspacePathValue` 确认 Host 已接受原生交接。Session-aware Client 会在已知当前 Session cwd 时据此解析相对路径;controller 将路径原样交给打开器,并通过 Session Remote 错误词汇表报告无效请求、取消与打开器失败。
|
|
690
|
+
`SessionOpenWorkspacePathRequest` 携带绝对路径或已按 workspace 解析的 `path`。`SessionOpenWorkspacePathValue` 确认 Host 已接受原生交接。Session-aware Client 会在已知当前 Session cwd 时据此解析相对路径;controller 将路径原样交给打开器,并通过 Session Remote 错误词汇表报告无效请求、取消与打开器失败。 可选的 `action: "reveal"` 选择文件管理器导航;省略时使用默认应用打开。
|
|
592
691
|
|
|
593
692
|
<!-- BEGIN GENERATED cordis-surface (gen-cordis-catalog.ts) — do not edit between markers -->
|
|
594
693
|
|
|
@@ -618,7 +717,7 @@ resolveAgent(sessionId: SessionId): Promise<ApiSessionAgentResult>
|
|
|
618
717
|
* @param signal - optional caller cancellation for persistence reads.
|
|
619
718
|
* @returns the current attached state or persisted header and event prefix.
|
|
620
719
|
*/
|
|
621
|
-
inspect( sessionId: SessionId, signal?: AbortSignal, ): Promise<
|
|
720
|
+
inspect( sessionId: SessionId, signal?: AbortSignal, ): Promise<SessionInspection>
|
|
622
721
|
|
|
623
722
|
/**
|
|
624
723
|
* Read all visible Session rows without resuming an Agent.
|
|
@@ -662,12 +761,18 @@ inspect( sessionId: SessionId, signal?: AbortSignal, ): Promise<{ meta: SessionH
|
|
|
662
761
|
*/
|
|
663
762
|
@Remote canOpenWorkspacePath(): boolean
|
|
664
763
|
|
|
764
|
+
/**
|
|
765
|
+
* Describe the serving desktop for authenticated file-action routes.
|
|
766
|
+
* @returns Host name, configured availability, and platform-specific file-manager behavior.
|
|
767
|
+
*/
|
|
768
|
+
workspaceDesktop(): { name: string; available: boolean; fileManager: 'finder' | 'explorer' | 'directory' | null }
|
|
769
|
+
|
|
665
770
|
/**
|
|
666
771
|
* Open one path prepared by a Session-aware caller on the Host desktop.
|
|
667
772
|
* @param request - path after best-effort Session workspace resolution.
|
|
668
773
|
* @param signal - caller lifetime; abort terminates the native command.
|
|
669
774
|
* @returns confirmation after the native opener accepts the path.
|
|
670
|
-
* @throws
|
|
775
|
+
* @throws RemoteError when the request is invalid, cancelled, or the opener fails.
|
|
671
776
|
*/
|
|
672
777
|
@Remote('openWorkspacePath') async openWorkspacePath( request: SessionOpenWorkspacePathRequest, signal: AbortSignal, ): Promise<SessionOpenWorkspacePathValue>
|
|
673
778
|
|
|
@@ -726,7 +831,8 @@ inspect( sessionId: SessionId, signal?: AbortSignal, ): Promise<{ meta: SessionH
|
|
|
726
831
|
* Follow one Session log from its opening or resume cursor.
|
|
727
832
|
* @param request - durable address and last committed sequence already held by the caller.
|
|
728
833
|
* @param signal - cancellation owned by the Remote stream carrier.
|
|
729
|
-
* @returns a complete opening snapshot followed by gap-free event
|
|
834
|
+
* @returns a complete opening snapshot followed by gap-free durable event
|
|
835
|
+
* frames and optional cursorless assistant-stream frames.
|
|
730
836
|
*/
|
|
731
837
|
@Remote({ mode: 'stream' }) follow(request: SessionFollowRequest, signal: AbortSignal): AsyncIterable<SessionFollowFrame>
|
|
732
838
|
|
|
@@ -738,7 +844,7 @@ inspect( sessionId: SessionId, signal?: AbortSignal, ): Promise<{ meta: SessionH
|
|
|
738
844
|
@Remote({ mode: 'stream' }) control(signal: AbortSignal): AsyncIterable<SessionControlFrame>
|
|
739
845
|
```
|
|
740
846
|
|
|
741
|
-
Types: [
|
|
847
|
+
Types: [SessionId](./core.md) · [SessionInspection](./persistence.md) · [SessionSearchRequest](./session-query.md)
|
|
742
848
|
|
|
743
849
|
Source: [`packages/api/session-controller/src/index.ts`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/api/session-controller/src/index.ts)
|
|
744
850
|
|
|
@@ -748,7 +854,7 @@ Source: [`packages/api/session-controller/src/index.ts`](https://github.com/deep
|
|
|
748
854
|
|
|
749
855
|
In-memory session store (`ctx.sessions`).
|
|
750
856
|
|
|
751
|
-
Persistence is intentionally not implemented here —
|
|
857
|
+
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.
|
|
752
858
|
|
|
753
859
|
```ts cordis-catalog
|
|
754
860
|
/**
|
|
@@ -785,10 +891,9 @@ create(id?: SessionId, options?: CreateSessionOptions): Session
|
|
|
785
891
|
*
|
|
786
892
|
* @param id - the session id; omitted, the store mints `session-<n>`.
|
|
787
893
|
* @param options - seed events and/or creation metadata for the header. With
|
|
788
|
-
* `
|
|
789
|
-
*
|
|
790
|
-
*
|
|
791
|
-
* retain no mutable aliases.
|
|
894
|
+
* `eventState`, every seed event is either independently owned or any
|
|
895
|
+
* shared value is deeply frozen; {@link Session.fromRestore} validates and
|
|
896
|
+
* adopts those values without copying or freezing them.
|
|
792
897
|
* @returns the constructed session, NOT yet in the store.
|
|
793
898
|
* @throws if a session with `id` already exists, metadata is not a plain
|
|
794
899
|
* lossless-JSON record with valid scalar fields, or `meta.cwd` is a
|
|
@@ -871,7 +976,7 @@ list(): Session[]
|
|
|
871
976
|
* `SessionStore`'s id policy.
|
|
872
977
|
* @returns The created live child session.
|
|
873
978
|
*/
|
|
874
|
-
fork(source: SessionForkSource, boundary?:
|
|
979
|
+
fork(source: SessionForkSource, boundary?: SessionSeq, childSessionId?: SessionId): Session
|
|
875
980
|
```
|
|
876
981
|
|
|
877
982
|
Types: [CreateSessionOptions](./persistence.md) · [PrepareSessionOptions](./persistence.md) · [SessionId](./core.md)
|