dsh-plugin-dev-kb 1.0.8 → 1.1.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 +21 -0
- package/README.en.md +6 -6
- package/README.md +6 -6
- package/kb/INDEX.md +21 -5
- package/kb/README.md +11 -10
- package/kb/extra/AGENTS.md +4 -4
- 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-vendored-package.md +2 -2
- package/kb/extra/cookbook/adding-a-vendored-package.zh.md +2 -2
- package/kb/extra/deepseek-llm-api-wire-extensions.md +163 -0
- package/kb/extra/deepseek-llm-api-wire-extensions.zh.md +163 -0
- package/kb/extra/development.md +8 -14
- package/kb/extra/development.zh.md +8 -14
- package/kb/extra/event-producer-consumer.md +55 -48
- package/kb/extra/event-producer-consumer.zh.md +58 -51
- package/kb/extra/glossary.md +1 -1
- package/kb/extra/glossary.zh.md +1 -1
- package/kb/extra/graph-atlas.md +0 -2
- package/kb/extra/graph-atlas.zh.md +0 -2
- package/kb/extra/i18n/README.md +4 -4
- package/kb/extra/i18n/README.zh.md +4 -4
- package/kb/extra/i18n/style-samples.md +2 -2
- package/kb/extra/module-graph.md +646 -926
- package/kb/extra/module-graph.zh.md +648 -928
- package/kb/extra/postmortem/0001-acp-default-export-drops-inject.md +2 -2
- package/kb/extra/postmortem/0001-acp-default-export-drops-inject.zh.md +2 -2
- package/kb/extra/postmortem/0002-js-expression-disabled-filesystem-tools.md +2 -2
- package/kb/extra/postmortem/0002-js-expression-disabled-filesystem-tools.zh.md +2 -2
- package/kb/extra/postmortem/0003-web-agent-gui-feedback-loop.md +2 -2
- package/kb/extra/postmortem/0003-web-agent-gui-feedback-loop.zh.md +2 -2
- package/kb/extra/postmortem/0004-landlock-partial-notice-misclassified-child-failures.md +1 -1
- package/kb/extra/postmortem/0004-landlock-partial-notice-misclassified-child-failures.zh.md +1 -1
- package/kb/extra/rescope.md +2 -2
- package/kb/extra/rescope.zh.md +2 -2
- package/kb/extra/subsystems/agent-team.md +28 -4
- package/kb/extra/subsystems/agent-team.zh.md +28 -4
- package/kb/extra/subsystems/attachment.md +168 -7
- package/kb/extra/subsystems/attachment.zh.md +168 -7
- package/kb/extra/subsystems/extensions.md +18 -0
- package/kb/extra/subsystems/extensions.zh.md +18 -0
- package/kb/extra/subsystems/feedback.md +4 -4
- package/kb/extra/subsystems/feedback.zh.md +4 -4
- package/kb/extra/subsystems/todo.md +32 -0
- package/kb/extra/subsystems/todo.zh.md +32 -0
- package/kb/extra/subsystems/webhook.md +70 -0
- package/kb/extra/subsystems/webhook.zh.md +70 -0
- package/kb/extra/testing.md +15 -10
- package/kb/extra/testing.zh.md +13 -8
- package/kb/extra/web-styling.md +4 -0
- package/kb/extra/web-styling.zh.md +4 -0
- package/kb/meta/search-index.json +309 -177
- package/kb/meta/site-pages.txt +183 -167
- package/kb/meta/source.json +5 -5
- package/kb/meta/topics.md +14 -6
- package/kb/site/develop/basic/publish.md +2 -2
- package/kb/site/develop/basic/tool.md +1 -1
- package/kb/site/develop/cordis-tutorial/07-into-the-harness.md +5 -4
- package/kb/site/develop/framework/events.md +1 -1
- package/kb/site/develop/practice/dynamic-cordis.md +17 -0
- package/kb/site/develop/practice/llm-adapter.md +4 -3
- package/kb/site/en/develop/basic/publish.md +2 -2
- package/kb/site/en/develop/basic/tool.md +1 -1
- package/kb/site/en/develop/cordis-tutorial/07-into-the-harness.md +5 -4
- package/kb/site/en/develop/framework/events.md +1 -1
- package/kb/site/en/develop/practice/dynamic-cordis.md +17 -0
- package/kb/site/en/develop/practice/llm-adapter.md +4 -3
- package/kb/site/en/guide/github-review.md +104 -0
- package/kb/site/en/guide/mcp-memory.md +103 -0
- 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 +80 -34
- package/kb/site/en/guide/schedule.md +23 -0
- package/kb/site/en/reference/agent-lifecycle.md +6 -4
- package/kb/{extra → site/en/reference}/api-gateway.md +12 -10
- package/kb/site/en/reference/capability-seams.md +128 -73
- package/kb/site/en/reference/config-catalog.md +481 -360
- package/kb/site/en/reference/cookbook/adding-a-package.md +3 -4
- package/kb/site/en/reference/cookbook/adding-a-settings-card.md +12 -10
- package/kb/site/en/reference/cookbook/adding-a-tool.md +11 -4
- package/kb/site/en/reference/cookbook/adding-an-llm-adapter.md +1 -1
- package/kb/site/en/reference/cookbook/extension-cookbook.md +20 -17
- package/kb/site/en/reference/cordis-api/inherited.md +1 -1
- package/kb/site/en/reference/cordis-primer.md +2 -1
- package/kb/site/en/reference/index.md +30 -11
- package/kb/site/en/reference/persistence-catalog.md +148 -80
- package/kb/site/en/reference/subsystems/approval.md +10 -10
- package/kb/site/en/reference/subsystems/client-modules.md +58 -16
- package/kb/site/en/reference/subsystems/code-runtime.md +10 -6
- package/kb/site/en/reference/subsystems/commands.md +25 -16
- package/kb/site/en/reference/subsystems/compaction.md +11 -11
- package/kb/site/en/reference/{cookbook/adding-a-conversation-node.md → subsystems/conversation.md} +50 -24
- package/kb/site/en/reference/subsystems/core.md +156 -17
- package/kb/site/en/reference/subsystems/credentials.md +44 -3
- package/kb/site/en/reference/subsystems/filesystem.md +12 -2
- package/kb/site/en/reference/subsystems/goal.md +1 -1
- package/kb/site/en/reference/subsystems/index.md +7 -2
- package/kb/site/en/reference/subsystems/jobs.md +1 -1
- package/kb/site/en/reference/subsystems/llm-streaming.md +154 -12
- package/kb/site/en/reference/subsystems/permission-presets.md +6 -6
- package/kb/site/en/reference/subsystems/persistence.md +185 -175
- package/kb/site/en/reference/subsystems/plan.md +2 -2
- 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/session-projection.md +115 -48
- package/kb/site/en/reference/subsystems/session-query.md +28 -14
- package/kb/site/en/reference/subsystems/session-reference.md +53 -8
- package/kb/site/en/reference/subsystems/session-telemetry.md +8 -8
- package/kb/site/en/reference/subsystems/session-title.md +6 -6
- package/kb/site/en/reference/subsystems/session.md +401 -99
- package/kb/site/en/reference/subsystems/settings.md +101 -6
- package/kb/site/en/reference/subsystems/skills.md +23 -0
- package/kb/site/en/reference/subsystems/slots.md +178 -0
- package/kb/site/en/reference/subsystems/spill.md +2 -2
- package/kb/site/en/reference/subsystems/storage.md +34 -3
- package/kb/site/en/reference/subsystems/subagent.md +122 -109
- package/kb/site/en/reference/subsystems/system-prompt.md +17 -4
- package/kb/site/en/reference/subsystems/token-meter.md +27 -12
- package/kb/site/en/reference/subsystems/tools.md +39 -39
- package/kb/site/en/reference/subsystems/typert.md +62 -55
- package/kb/site/en/reference/subsystems/user-questions.md +33 -33
- package/kb/site/en/reference/subsystems/web-client.md +98 -0
- package/kb/site/en/reference/subsystems/web-server.md +11 -5
- package/kb/site/en/reference/subsystems/web.md +7 -1
- package/kb/site/en/reference/subsystems/workspace.md +102 -9
- package/kb/site/en/reference/tool-catalog.md +86 -82
- package/kb/site/en/reference/tool-execution-pipeline.md +1 -1
- package/kb/site/guide/github-review.md +104 -0
- package/kb/site/guide/mcp-memory.md +103 -0
- package/kb/site/guide/network-proxy.md +87 -0
- package/kb/site/guide/providers.md +70 -17
- package/kb/site/guide/python-sdk.md +87 -41
- package/kb/site/guide/schedule.md +23 -0
- package/kb/site/reference/agent-lifecycle.md +6 -4
- package/kb/{extra/api-gateway.zh.md → site/reference/api-gateway.md} +12 -10
- package/kb/site/reference/capability-seams.md +128 -73
- package/kb/site/reference/config-catalog.md +481 -360
- package/kb/site/reference/cookbook/adding-a-package.md +3 -4
- package/kb/site/reference/cookbook/adding-a-settings-card.md +12 -10
- package/kb/site/reference/cookbook/adding-a-tool.md +11 -4
- package/kb/site/reference/cookbook/adding-an-llm-adapter.md +1 -1
- package/kb/site/reference/cookbook/extension-cookbook.md +20 -17
- package/kb/site/reference/cordis-api/inherited.md +1 -1
- package/kb/site/reference/cordis-primer.md +2 -1
- package/kb/site/reference/index.md +30 -11
- package/kb/site/reference/persistence-catalog.md +148 -80
- package/kb/site/reference/subsystems/approval.md +10 -10
- package/kb/site/reference/subsystems/client-modules.md +58 -16
- package/kb/site/reference/subsystems/code-runtime.md +10 -6
- package/kb/site/reference/subsystems/commands.md +25 -16
- package/kb/site/reference/subsystems/compaction.md +11 -11
- package/kb/site/reference/{cookbook/adding-a-conversation-node.md → subsystems/conversation.md} +50 -24
- package/kb/site/reference/subsystems/core.md +156 -17
- package/kb/site/reference/subsystems/credentials.md +44 -3
- package/kb/site/reference/subsystems/filesystem.md +12 -2
- package/kb/site/reference/subsystems/goal.md +1 -1
- package/kb/site/reference/subsystems/index.md +7 -2
- package/kb/site/reference/subsystems/jobs.md +1 -1
- package/kb/site/reference/subsystems/llm-streaming.md +154 -12
- package/kb/site/reference/subsystems/permission-presets.md +5 -5
- package/kb/site/reference/subsystems/persistence.md +184 -174
- package/kb/site/reference/subsystems/plan.md +2 -2
- package/kb/site/reference/subsystems/schedule.md +9 -3
- package/kb/site/reference/subsystems/session-projection.md +115 -48
- package/kb/site/reference/subsystems/session-query.md +28 -14
- package/kb/site/reference/subsystems/session-reference.md +53 -8
- package/kb/site/reference/subsystems/session-telemetry.md +8 -8
- package/kb/site/reference/subsystems/session-title.md +6 -6
- package/kb/site/reference/subsystems/session.md +401 -99
- package/kb/site/reference/subsystems/settings.md +101 -6
- package/kb/site/reference/subsystems/skills.md +23 -0
- package/kb/site/reference/subsystems/slots.md +178 -0
- package/kb/site/reference/subsystems/spill.md +2 -2
- package/kb/site/reference/subsystems/storage.md +34 -3
- package/kb/site/reference/subsystems/subagent.md +122 -109
- package/kb/site/reference/subsystems/system-prompt.md +17 -4
- package/kb/site/reference/subsystems/token-meter.md +27 -12
- package/kb/site/reference/subsystems/tools.md +39 -39
- package/kb/site/reference/subsystems/typert.md +62 -55
- package/kb/site/reference/subsystems/user-questions.md +33 -33
- package/kb/site/reference/subsystems/web-client.md +98 -0
- package/kb/site/reference/subsystems/web-server.md +11 -5
- package/kb/site/reference/subsystems/web.md +7 -1
- package/kb/site/reference/subsystems/workspace.md +102 -9
- package/kb/site/reference/tool-catalog.md +85 -81
- package/kb/site/reference/tool-execution-pipeline.md +1 -1
- package/package.json +2 -2
- package/skills/dsh-plugin-dev-kb.md +8 -6
|
@@ -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,6 @@ interface SessionEventMap {
|
|
|
56
56
|
* project their `content` verbatim; `source` tells them apart.
|
|
57
57
|
*/
|
|
58
58
|
'user/message': UserMessage
|
|
59
|
-
/** Raw stream chunk — token-level replay fidelity. */
|
|
60
|
-
'assistant/chunk': { turn: number; step: number; chunk: StreamChunk }
|
|
61
59
|
/**
|
|
62
60
|
* Assembled assistant message for one step (derived history uses this).
|
|
63
61
|
* Carries the step's `usage` when the adapter reported token accounting, so
|
|
@@ -68,13 +66,27 @@ interface SessionEventMap {
|
|
|
68
66
|
* marker distinguishes that prefix without re-deriving interruption from turn
|
|
69
67
|
* boundaries. An aborted turn with no such event streamed no visible content.
|
|
70
68
|
*/
|
|
71
|
-
'assistant/message': {
|
|
69
|
+
'assistant/message': {
|
|
70
|
+
turn: number
|
|
71
|
+
step: number
|
|
72
|
+
message: AssistantMessage
|
|
73
|
+
/** Exact timed model stream, compacted without joining delta boundaries. */
|
|
74
|
+
stream: AssistantStreamRecord[]
|
|
75
|
+
usage?: TokenUsage
|
|
76
|
+
interrupted?: true
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* One model attempt that committed no surface message. The embedded stream
|
|
80
|
+
* preserves a failed, retried, cancelled, or stream-error attempt that
|
|
81
|
+
* reached settlement without fabricating model-visible history.
|
|
82
|
+
*/
|
|
83
|
+
'assistant/attempt': { turn: number; step: number; stream: AssistantStreamRecord[] }
|
|
72
84
|
/**
|
|
73
85
|
* The model requested one tool invocation: `name` with the raw `arguments`
|
|
74
86
|
* JSON string exactly as the model produced it (unparsed). `callId` pairs the
|
|
75
87
|
* call with its `tool/result`.
|
|
76
88
|
*/
|
|
77
|
-
'tool/call': { turn: number; step: number; callId:
|
|
89
|
+
'tool/call': { turn: number; step: number; callId: ToolCallId; name: string; arguments: string }
|
|
78
90
|
/**
|
|
79
91
|
* A completed tool call's model-facing result, optional internal failure
|
|
80
92
|
* identity, and optional tool-private `meta` presentation payload. `meta` is
|
|
@@ -93,13 +105,16 @@ interface SessionEventMap {
|
|
|
93
105
|
error?: { name: string; code: string }
|
|
94
106
|
meta?: JsonValue
|
|
95
107
|
}
|
|
96
|
-
/** Whole-list snapshot; latest write wins on replay. Log-only UI state; never derived history. */
|
|
97
|
-
'todo/write': { todos: TodoItem[] }
|
|
98
108
|
/**
|
|
99
109
|
* Full header for the next request, appended inside its step before dispatch.
|
|
100
110
|
* It is log-only; the latest snapshot reconstructs the request header.
|
|
101
111
|
*/
|
|
102
|
-
'request/header': {
|
|
112
|
+
'request/header': {
|
|
113
|
+
header: EpochHeader
|
|
114
|
+
reason: RequestHeaderReason
|
|
115
|
+
/** A changed header also begins a distinct model-message series. */
|
|
116
|
+
startsSeries?: true
|
|
117
|
+
}
|
|
103
118
|
/**
|
|
104
119
|
* Route metadata for the next request, logged only when the route or capacity
|
|
105
120
|
* changes. It does not participate in request reconstruction or header equality.
|
|
@@ -109,12 +124,12 @@ interface SessionEventMap {
|
|
|
109
124
|
* Marks the end of a constructor seed. Events before it have smaller seq
|
|
110
125
|
* values and came from the seed (resume, fork, or replay); this lifecycle
|
|
111
126
|
* produced none of them. This log-only event is the durable projection of
|
|
112
|
-
* {@link Session.firstLiveSeq}.
|
|
113
|
-
* carry the meaning.
|
|
127
|
+
* {@link Session.firstLiveSeq}.
|
|
114
128
|
*
|
|
115
|
-
*
|
|
116
|
-
*
|
|
117
|
-
*
|
|
129
|
+
* A fresh fork child owns one `{ inherited: true }` marker at its exact
|
|
130
|
+
* inherited-prefix cut, even when that prefix ends in an ancestor marker.
|
|
131
|
+
* The last tagged marker is the current Session's cut; untagged markers keep
|
|
132
|
+
* ordinary restore and replay lifecycle boundaries.
|
|
118
133
|
*
|
|
119
134
|
* `Session`'s constructor is the only legitimate writer. The invariant
|
|
120
135
|
* companion deliberately constrains nothing here, so a plugin appending one
|
|
@@ -127,40 +142,17 @@ interface SessionEventMap {
|
|
|
127
142
|
* writers — a concurrently live session holds its own boundary elsewhere,
|
|
128
143
|
* so tolerating concurrent writers needs a signal beyond the log.
|
|
129
144
|
*/
|
|
130
|
-
'session/end-seed':
|
|
145
|
+
'session/end-seed': { inherited?: true }
|
|
131
146
|
}
|
|
132
147
|
```
|
|
133
148
|
|
|
134
149
|
`UserMessage` 是普通提示词、注入上下文、steering(中途引导)与实时收件箱事件共享的带标识且冻结的 user-role 值。事件包装层只会增加事件本地的位置或结果事实;条目待处理期间,loop 只额外附加驱动器自有的路由状态。
|
|
135
150
|
|
|
136
|
-
### `TodoItem`:一条待办项
|
|
137
|
-
|
|
138
|
-
这是 `todo/write` 事件全量列表快照中的单元。它有意保持精简:一行 `content` 加一个三态 `status`(没有 id、优先级或 `activeForm`);列表在每次写入时整体替换,因此条目无需稳定标识。见 [todo_write Agent Note](https://github.com/deepseek-ai/deepseek-harness/blob/master/.agents/notes/implemented/feature/2026-06-29-todo-write-tool.zh.md)。
|
|
139
|
-
|
|
140
|
-
```ts type-equiv
|
|
141
|
-
/**
|
|
142
|
-
* One entry in an agent's todo list — the unit of the `todo/write`
|
|
143
|
-
* {@link SessionEventMap} event's whole-list snapshot.
|
|
144
|
-
*
|
|
145
|
-
* Deliberately minimal: a human-readable `content` line and a three-state
|
|
146
|
-
* `status`. No id, priority, or `activeForm` — the list is replaced wholesale
|
|
147
|
-
* on every write (last-write-wins), so entries need no stable identity. The
|
|
148
|
-
* three statuses describe the complete portable lifecycle needed by model and
|
|
149
|
-
* UI consumers.
|
|
150
|
-
*/
|
|
151
|
-
interface TodoItem {
|
|
152
|
-
/** What this task is — a short imperative line shown in the UI. */
|
|
153
|
-
content: string
|
|
154
|
-
/** Lifecycle state. `in_progress` marks a task being worked now; parallel work may mark several. */
|
|
155
|
-
status: 'pending' | 'in_progress' | 'completed'
|
|
156
|
-
}
|
|
157
|
-
```
|
|
158
|
-
|
|
159
151
|
<a id="the-request-header-event-requestheader"></a>
|
|
160
152
|
|
|
161
153
|
### 请求头事件:`request/header`
|
|
162
154
|
|
|
163
|
-
请求信封(即 `EpochHeader`:调用配置 + 适配器所提供默认值的标记 + 渲染后的系统提示词 + 已组装的工具 schema)会作为会话状态写入日志,因此每个对话请求都是日志的纯函数(见可重建性 Agent Note)。带有 reason `'initial'` 或 `'resume'` 的完整 `request/header` 快照记录每个 agent loop
|
|
155
|
+
请求信封(即 `EpochHeader`:调用配置 + 适配器所提供默认值的标记 + 渲染后的系统提示词 + 已组装的工具 schema)会作为会话状态写入日志,因此每个对话请求都是日志的纯函数(见可重建性 Agent Note)。带有 reason `'initial'` 或 `'resume'` 的完整 `request/header` 快照记录每个 agent loop 实例的边界;请求变化时会追加 reason 为 `'change'` 的快照;未变的信封显式开启消息序列或跟随 surface 替换时,会追加 reason 为 `'series'` 的快照。如果发生变化的快照所属请求同时开启序列,它会携带 `startsSeries: true`。普通的仅追加后续 Turn,以及同一模型消息序列内的后续 Step 与重试沿用最新快照。`foldRequestHeader(events)` 通过选择最新快照重建请求头。该事件不是 `SurfaceEventType`,不产生 LLM 消息。
|
|
164
156
|
|
|
165
157
|
```ts type-equiv
|
|
166
158
|
/**
|
|
@@ -202,6 +194,28 @@ interface RequestContext {
|
|
|
202
194
|
|
|
203
195
|
基于 `type` 的真正可辨识联合(而非独立的 `type`/`data` 联合),因此 `switch (event.type)` 能直接收窄 `event.data`,无需类型断言。`seq` 是日志中的单调递增位置(`seq = log.length`);`time` 为 epoch 毫秒。
|
|
204
196
|
|
|
197
|
+
```ts type-equiv
|
|
198
|
+
/** Sequence number of one existing event in a Session log. */
|
|
199
|
+
type SessionSeq = BrandedNumber<'SessionSeq'>
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
```ts type-equiv
|
|
203
|
+
/** A Session log gap, prefix length, or read offset, which may equal the event count. */
|
|
204
|
+
type SessionLogOffset = BrandedNumber<'SessionLogOffset'>
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
```ts type-equiv
|
|
208
|
+
/** Inclusive Session event watermark, or `-1` before any event exists. */
|
|
209
|
+
type SessionSeqCursor = SessionSeq | -1
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
```ts type-equiv
|
|
213
|
+
/** One existing Session event position, or explicit absence. */
|
|
214
|
+
type OptionalSessionSeq = SessionSeq | null
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
`SessionSeq(value)` 与 `SessionLogOffset(value)` 只接纳非负安全整数,并拒绝负零。它们仅添加编译期品牌,不改变序列化后的数值;算术会返回普通 `number`,调用方必须按结果的预期角色通过对应构造器重新接纳。
|
|
218
|
+
|
|
205
219
|
```ts type-equiv
|
|
206
220
|
/**
|
|
207
221
|
* One immutable entry in the session log.
|
|
@@ -212,7 +226,7 @@ interface RequestContext {
|
|
|
212
226
|
* The {@link sourceEventSeqs} and {@link surfaceOp} fields are conditional:
|
|
213
227
|
* they only exist on {@link SurfaceEventType} variants (`user/message`,
|
|
214
228
|
* `assistant/message`, `tool/result`).
|
|
215
|
-
* Non-surface events (boundary markers,
|
|
229
|
+
* Non-surface events (boundary markers, attempts, errors) never carry
|
|
216
230
|
* surface metadata — the compiler enforces this at `Session.append()`
|
|
217
231
|
* call sites.
|
|
218
232
|
*/
|
|
@@ -220,7 +234,7 @@ type SessionEvent<T extends SessionEventType = SessionEventType> = {
|
|
|
220
234
|
[K in SessionEventType]: {
|
|
221
235
|
type: K
|
|
222
236
|
/** Monotonic sequence number within the session. */
|
|
223
|
-
seq:
|
|
237
|
+
seq: SessionSeq
|
|
224
238
|
/** Unix epoch milliseconds. */
|
|
225
239
|
time: number
|
|
226
240
|
data: SessionEventMap[K]
|
|
@@ -237,14 +251,11 @@ type SessionEvent<T extends SessionEventType = SessionEventType> = {
|
|
|
237
251
|
ignorable?: true
|
|
238
252
|
} & (K extends SurfaceEventType ? {
|
|
239
253
|
/**
|
|
240
|
-
* Seq numbers of earlier events that this event cites as sources
|
|
241
|
-
*
|
|
242
|
-
*
|
|
243
|
-
* `assistant/message` may carry a present empty array for a known empty
|
|
244
|
-
* provider stream; when the field is absent, the event does not record which
|
|
245
|
-
* earlier events produced the message.
|
|
254
|
+
* Seq numbers of earlier events that this event cites as sources, such as
|
|
255
|
+
* the surface nodes shadowed by a compaction replacement. A v2
|
|
256
|
+
* `assistant/message` embeds its provider stream and cannot carry this field.
|
|
246
257
|
*/
|
|
247
|
-
sourceEventSeqs?:
|
|
258
|
+
sourceEventSeqs?: SessionSeq[]
|
|
248
259
|
/** How this event entered the surface; absent for non-surface events. */
|
|
249
260
|
surfaceOp?: SurfaceOp
|
|
250
261
|
} : object)
|
|
@@ -253,7 +264,7 @@ type SessionEvent<T extends SessionEventType = SessionEventType> = {
|
|
|
253
264
|
|
|
254
265
|
`SessionEventType = keyof SessionEventMap`。由于 `SessionEventMap` 可通过合并扩展,对 `SessionEvent` 的 switch 语句禁止使用 `assertNever`:插件添加的变体是合法的未知值;处理已知 case 后在 `default` 中放行。
|
|
255
266
|
|
|
256
|
-
|
|
267
|
+
V2 `assistant/message` 嵌入 provider stream,不能携带 `sourceEventSeqs`。User 与 tool surface event 可以在 provenance 或 replacement operation 需要时引用完整且非空的唯一较早 event 集合。
|
|
257
268
|
|
|
258
269
|
<a id="surface-types"></a>
|
|
259
270
|
|
|
@@ -267,7 +278,8 @@ type SessionEvent<T extends SessionEventType = SessionEventType> = {
|
|
|
267
278
|
/**
|
|
268
279
|
* The subset of {@link SessionEventType} values whose events produce LLM
|
|
269
280
|
* messages and are eligible to appear on the ordered surface. Only these
|
|
270
|
-
* event types may carry {@link SurfaceOp} and
|
|
281
|
+
* event types may carry {@link SurfaceOp}; user and tool events may also cite
|
|
282
|
+
* earlier sources through {@link SessionEvent.sourceEventSeqs}.
|
|
271
283
|
*/
|
|
272
284
|
type SurfaceEventType =
|
|
273
285
|
| 'user/message'
|
|
@@ -293,7 +305,7 @@ type SurfaceEventType =
|
|
|
293
305
|
*/
|
|
294
306
|
type SurfaceOp =
|
|
295
307
|
| 'append'
|
|
296
|
-
| { op: 'replace'; start:
|
|
308
|
+
| { op: 'replace'; start: SessionSeq; end: SessionSeq }
|
|
297
309
|
```
|
|
298
310
|
|
|
299
311
|
`'append'` 是常规的尾部追加路径。`replace` 会遮蔽从 `start` 到 `end`(含两端)的 surface 条目(两者都必须是有效的 surface seq;`start === end` 时仅替换单个条目),并在原位置插入新事件。
|
|
@@ -305,21 +317,20 @@ type SurfaceOp =
|
|
|
305
317
|
* Surface placement and cited source-event seqs for {@link Session.append}. Required on
|
|
306
318
|
* message-producing events and forbidden on log-only events.
|
|
307
319
|
*/
|
|
308
|
-
|
|
320
|
+
type SurfaceIntent<T extends SurfaceEventType = SurfaceEventType> = {
|
|
309
321
|
surfaceOp: SurfaceOp
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
}
|
|
322
|
+
} & (T extends 'assistant/message' ? {
|
|
323
|
+
/** V2 Assistant messages embed their provider stream instead of citing source events. */
|
|
324
|
+
sourceEventSeqs?: never
|
|
325
|
+
} : {
|
|
326
|
+
/** Complete non-empty set of known earlier source-event seqs. */
|
|
327
|
+
sourceEventSeqs?: SessionSeq[]
|
|
328
|
+
})
|
|
318
329
|
```
|
|
319
330
|
|
|
320
331
|
对 `SurfaceEventType` 事件必填:每个产生消息的事件都必须声明它如何加入 surface(派生模型历史的唯一来源)。面向人类的 transcript(文本记录)是另一个投影,读取的是日志中追加来源的事件,因为 surface 会有意遮蔽替换所概括的范围(见 [dsh-session](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/core/session/README.zh.md) 的 `isAppendSurfaceEvent`)。非 surface 类型在编译期拒绝此参数。
|
|
321
332
|
|
|
322
|
-
|
|
333
|
+
`assistant/message` 不能携带 `sourceEventSeqs`;它的 `stream` 拥有精确 provider 证据。其他 surface event 不引用较早 event 时省略该字段,需要引用时使用完整非空 list。
|
|
323
334
|
|
|
324
335
|
### `SessionSurface`:实时只读 surface 投影
|
|
325
336
|
|
|
@@ -331,7 +342,7 @@ interface SurfaceIntent {
|
|
|
331
342
|
/** Readonly live projection of the message-producing session events. */
|
|
332
343
|
interface SessionSurface {
|
|
333
344
|
/** Current surface event sequences in model-visible order. */
|
|
334
|
-
readonly nodes: readonly
|
|
345
|
+
readonly nodes: readonly SessionSeq[]
|
|
335
346
|
/** Monotonic count of committed positional replacements. */
|
|
336
347
|
readonly replaceGeneration: number
|
|
337
348
|
}
|
|
@@ -345,13 +356,13 @@ interface SessionSurface {
|
|
|
345
356
|
/** One replacement operation observed while folding a session surface. */
|
|
346
357
|
interface SurfaceFoldReplacement {
|
|
347
358
|
/** Seq of the event that replaced the prior surface range. */
|
|
348
|
-
seq:
|
|
359
|
+
seq: SessionSeq
|
|
349
360
|
/** Declared inclusive start seq of the replaced surface range. */
|
|
350
|
-
start:
|
|
361
|
+
start: SessionSeq
|
|
351
362
|
/** Declared inclusive end seq of the replaced surface range. */
|
|
352
|
-
end:
|
|
363
|
+
end: SessionSeq
|
|
353
364
|
/** Actual surface entries removed by the operation, in surface order. */
|
|
354
|
-
shadowedSeqs:
|
|
365
|
+
shadowedSeqs: SessionSeq[]
|
|
355
366
|
}
|
|
356
367
|
```
|
|
357
368
|
|
|
@@ -359,7 +370,7 @@ interface SurfaceFoldReplacement {
|
|
|
359
370
|
/** Complete result of replaying the surface operations in a session log. */
|
|
360
371
|
interface SurfaceFoldResult {
|
|
361
372
|
/** Current surface event sequences in model-visible order. */
|
|
362
|
-
nodes:
|
|
373
|
+
nodes: SessionSeq[]
|
|
363
374
|
/** Replacement operations in event order. */
|
|
364
375
|
replacements: SurfaceFoldReplacement[]
|
|
365
376
|
}
|
|
@@ -383,24 +394,27 @@ declare class Session {
|
|
|
383
394
|
get surface(): SessionSurface;
|
|
384
395
|
/**
|
|
385
396
|
* Detached, deep-frozen creation metadata (format version, cwd, lineage,
|
|
386
|
-
*
|
|
397
|
+
* and whether fork history exists). Supplied by the store via `ctx.sessions.create()`. When a
|
|
387
398
|
* `Session` is created without a store-owned header, a minimal header is
|
|
388
399
|
* synthesized (stamped with the current {@link SESSION_FORMAT_VERSION}) so
|
|
389
400
|
* `session.header` is always present. Kept out of the event log — it is a
|
|
390
401
|
* storage concern, not replayable conversation state.
|
|
391
402
|
*/
|
|
392
403
|
readonly header: SessionHeader;
|
|
404
|
+
/** Number of leading events inherited from this Session's fork parent. */
|
|
405
|
+
readonly inheritedEventCount: SessionLogOffset;
|
|
393
406
|
/** The session identity, derived from its durable header's single copy. */
|
|
394
407
|
get id(): SessionId;
|
|
395
408
|
/**
|
|
396
409
|
* The first seq appended IN THIS PROCESS: the length of the constructor
|
|
397
410
|
* seed (0 without one). Events with smaller seq values entered through
|
|
398
411
|
* construction — replay, fork, or resume — and were never published on the
|
|
399
|
-
* `session/event` firehose (constructor seeds do not emit)
|
|
400
|
-
*
|
|
401
|
-
*
|
|
402
|
-
*
|
|
403
|
-
*
|
|
412
|
+
* `session/event` firehose (constructor seeds do not emit). This offset marks
|
|
413
|
+
* the constructor-input boundary for lifecycle ownership and persistence
|
|
414
|
+
* adoption; consumers that need complete canonical history still start at
|
|
415
|
+
* seq 0. Distinct from {@link inheritedEventCount}, the DURABLE
|
|
416
|
+
* fork-lineage cut: a resumed session's constructor seed is its full stored
|
|
417
|
+
* log, while the inherited count keeps the original fork value — this field is the
|
|
404
418
|
* in-process construction fact.
|
|
405
419
|
*
|
|
406
420
|
* Not persisted itself: a seeded session projects it into the log as the
|
|
@@ -413,17 +427,23 @@ declare class Session {
|
|
|
413
427
|
* When this lifecycle appends the marker, it occupies this seq before the
|
|
414
428
|
* store attaches and therefore does not publish either. Otherwise this seq
|
|
415
429
|
* holds an ordinary published write.
|
|
416
|
-
|
|
417
|
-
readonly firstLiveSeq:
|
|
430
|
+
*/
|
|
431
|
+
readonly firstLiveSeq: SessionLogOffset;
|
|
418
432
|
/**
|
|
419
433
|
* Create a detached session by validating and snapshotting borrowed seed
|
|
420
434
|
* events and storage metadata.
|
|
421
435
|
* @param id - session identity.
|
|
422
436
|
* @param seed - optional borrowed replay or fork events.
|
|
423
437
|
* @param header - optional borrowed storage metadata.
|
|
438
|
+
* @param inheritedEventCount - exact fork-inherited prefix length for a seeded header.
|
|
424
439
|
* @returns a detached session.
|
|
425
440
|
*/
|
|
426
|
-
static create(
|
|
441
|
+
static create(
|
|
442
|
+
id: SessionId,
|
|
443
|
+
seed?: readonly SessionEvent[],
|
|
444
|
+
header?: SessionHeader,
|
|
445
|
+
inheritedEventCount?: SessionLogOffset,
|
|
446
|
+
): Session;
|
|
427
447
|
/**
|
|
428
448
|
* Restore a detached session by taking ownership of fresh persistence values.
|
|
429
449
|
* The storage format, event envelopes, sequence continuity, surface transitions,
|
|
@@ -431,18 +451,46 @@ declare class Session {
|
|
|
431
451
|
* @param id - restored session identity.
|
|
432
452
|
* @param seed - fresh detached events whose ownership is transferred.
|
|
433
453
|
* @param header - fresh detached metadata whose ownership is transferred.
|
|
454
|
+
* @param inheritedEventCount - exact fork-inherited prefix length decoded from storage.
|
|
434
455
|
* @returns a restored detached session.
|
|
435
456
|
*/
|
|
436
|
-
static fromRestore(
|
|
457
|
+
static fromRestore(
|
|
458
|
+
id: SessionId,
|
|
459
|
+
seed: readonly SessionEvent[],
|
|
460
|
+
header: SessionHeader,
|
|
461
|
+
inheritedEventCount: SessionLogOffset,
|
|
462
|
+
): Session;
|
|
463
|
+
/**
|
|
464
|
+
* Return the immutable event stored at one exact sequence number.
|
|
465
|
+
* @param seq - event sequence number.
|
|
466
|
+
* @returns the accepted event, or undefined when the log does not contain it.
|
|
467
|
+
*/
|
|
468
|
+
eventAt(seq: SessionSeq): SessionEvent | undefined;
|
|
469
|
+
/**
|
|
470
|
+
* Materialize an immutable snapshot of a half-open event sequence range.
|
|
471
|
+
* A full current snapshot is reused until the next append; every previously
|
|
472
|
+
* returned snapshot remains stable after later appends.
|
|
473
|
+
* @param fromSeq - non-negative inclusive sequence number; defaults to the log start.
|
|
474
|
+
* @param toSeqExclusive - non-negative exclusive sequence number; defaults to the current end.
|
|
475
|
+
* @returns a frozen array of the selected deeply frozen events.
|
|
476
|
+
*/
|
|
477
|
+
snapshotEvents(
|
|
478
|
+
fromSeq: SessionLogOffset = SessionLogOffset(0),
|
|
479
|
+
toSeqExclusive: SessionLogOffset = this.seq,
|
|
480
|
+
): readonly SessionEvent[];
|
|
437
481
|
/**
|
|
438
|
-
*
|
|
439
|
-
*
|
|
440
|
-
* Events and their nested data are deep-frozen at acceptance, so neither a
|
|
441
|
-
* cast nor ordinary JavaScript can rewrite durable history.
|
|
482
|
+
* Return this Session's events after its fork-inherited prefix.
|
|
483
|
+
* @returns a fresh array containing child-owned events in log order.
|
|
442
484
|
*/
|
|
443
|
-
|
|
485
|
+
ownEvents(): readonly SessionEvent[];
|
|
486
|
+
/**
|
|
487
|
+
* Whether one existing event position is outside the fork-inherited prefix.
|
|
488
|
+
* @param seq - event position in this Session.
|
|
489
|
+
* @returns true when the event belongs to this Session rather than its parent.
|
|
490
|
+
*/
|
|
491
|
+
isOwnSeq(seq: SessionSeq): boolean;
|
|
444
492
|
/** The next event's sequence number — always the log length (the `seq = log.length` contiguity contract). */
|
|
445
|
-
get seq():
|
|
493
|
+
get seq(): SessionLogOffset;
|
|
446
494
|
/**
|
|
447
495
|
* Append one typed event to the log and synchronously notify observers via
|
|
448
496
|
* the store-owned, module-private publication hooks. The hot path never blocks
|
|
@@ -460,7 +508,8 @@ declare class Session {
|
|
|
460
508
|
* declare how it joins the surface, the sole source of derived model
|
|
461
509
|
* history) and
|
|
462
510
|
* rejected by the compiler for non-surface types like `turn/start` or
|
|
463
|
-
* `assistant/
|
|
511
|
+
* `assistant/attempt`. Assistant messages embed their exact provider
|
|
512
|
+
* stream and cannot cite top-level source events.
|
|
464
513
|
* @returns the logged event — its assigned `seq`/`time` plus the SNAPSHOT of
|
|
465
514
|
* `data` that entered the log, so reading `event.data` back sees the logged
|
|
466
515
|
* value, never the caller's still-mutable input.
|
|
@@ -470,7 +519,7 @@ declare class Session {
|
|
|
470
519
|
* Map/Set/Date/class instance), or when the candidate violates the
|
|
471
520
|
* canonical surface contract (marker shape and eligibility, unique
|
|
472
521
|
* earlier source-event references, positional replacement validity, and complete
|
|
473
|
-
* shadowed-node coverage). One
|
|
522
|
+
* shadowed-node coverage). One iterative pass reads, validates, and
|
|
474
523
|
* copies each nested value once, so a stateful getter cannot supply one value
|
|
475
524
|
* to validation and another to storage. The event log is the durable source
|
|
476
525
|
* of truth, so a bad event fails at the append site rather than later during
|
|
@@ -481,13 +530,13 @@ declare class Session {
|
|
|
481
530
|
append<T extends SessionEventType>(
|
|
482
531
|
type: T,
|
|
483
532
|
data: SessionEventMap[T],
|
|
484
|
-
...opts: T extends SurfaceEventType ? [opts: SurfaceIntent] : []
|
|
533
|
+
...opts: T extends SurfaceEventType ? [opts: SurfaceIntent<T>] : []
|
|
485
534
|
): SessionEvent<T>;
|
|
486
535
|
/**
|
|
487
536
|
* The {@link EpochHeader} in force after the log's last header event — the
|
|
488
537
|
* header the NEXT request will be compared against — or undefined before
|
|
489
538
|
* the first `request/header` snapshot. The live, incrementally-maintained
|
|
490
|
-
* form of `foldRequestHeader(session.
|
|
539
|
+
* form of `foldRequestHeader(session.snapshotEvents())`: each header event is folded
|
|
491
540
|
* once, when first seen, so a per-step read costs O(new events).
|
|
492
541
|
* @returns the folded header, or undefined when no header event exists yet.
|
|
493
542
|
*/
|
|
@@ -532,17 +581,17 @@ declare class Session {
|
|
|
532
581
|
`Session.deriveMessages()` 将事件日志投影为模型看到的 `Message[]`。它是缓存的(每个 surface 节点在首次出现时投影一次;surface 重写触发重建)且冻结的(每次调用返回一个新数组,引用共享的深冻结消息,因此通过投影修改已记录的历史在类型上不可表达)。`deriveEventMessage(event)` 是折叠所应用的逐节点纯函数,公开暴露以便外部重建器和开发不变式检查能以完全相同的规则投影日志前缀,不会与缓存产生分歧。投影规则:
|
|
533
582
|
|
|
534
583
|
- `user/message` → 一条携带确切 `content` 的 user 消息;可选 envelope 仅作为日志中的展示元数据保留。
|
|
535
|
-
- `assistant/message` → 一条 assistant
|
|
584
|
+
- `assistant/message` → 一条 assistant 消息,包含生成它的提供方和模型,以及可选的适配器私有回放状态。其嵌入式紧凑 stream 是回放、usage 与 UI 证据,而不是第二条 message。**内容为空的** `assistant/message` 也会跳过:因 max-tokens 而截断且无内容的步骤仍会记录一条 `assistant/message` 来保存 stream、usage、提供方和模型,但无内容的 assistant 轮次不得进入提供方 transcript(文本记录)。
|
|
536
585
|
- `tool/result` → 一条携带 `tool-result` 块的 user 消息。
|
|
537
586
|
- `user/message`(注入上下文,即非 `user` 来源)→ 按时间顺序在相应位置生成一条 user-role 消息,并原样承载其 `content`;其类型化 source 标明生产方,并携带所有生产方专用数据。
|
|
538
587
|
|
|
539
|
-
其余所有事件(`turn/*`、`step
|
|
588
|
+
其余所有事件(`turn/*`、`step/*`、`assistant/attempt`、插件所属的 `llm/retry`)均为结构信息,不会投影为消息。token 记账会展开每个 `assistant/message` 或 `assistant/attempt` 的嵌入式 stream,message 顶层 `usage` 存在时仍是已提交 message 的权威。失败的模型请求 attempt 因此可以保留提供方 usage,而无需虚构 assistant message。当前逻辑校验会拒绝没有提供方/模型的 request header 和 assistant 消息,而不会猜测路由;受支持的历史表示会在当前 Session 存在前,由其相邻格式迁移边归一化并校验。
|
|
540
589
|
|
|
541
590
|
## 活跃会话 fork API
|
|
542
591
|
|
|
543
592
|
`ctx.sessions.create(id, { seed, meta })` 是底层的回放/fork 原语。对于普通的活跃会话 fork,`SessionStore` 暴露一个策略 API:
|
|
544
593
|
|
|
545
|
-
- `fork(source, boundary?, childSessionId?)` 接受一个活跃的 `Session` 对象或活跃的 `SessionId`,选取到 `
|
|
594
|
+
- `fork(source, boundary?, childSessionId?)` 接受一个活跃的 `Session` 对象或活跃的 `SessionId`,选取到 `SessionSeq` boundary(含)为止的源事件(默认为当前最后一个事件),要求所选前缀结束时没有开放轮次,然后创建一个活跃的子会话,包含深克隆的 seed event、`parentSession`、`isSeeded: true`、精确 `inheritedEventCount` 及继承的 `cwd`。
|
|
546
595
|
|
|
547
596
|
显式 `boundary` 允许调用者从任意稳定的轮次间位置 fork,包括之前的 `turn/end` 或更晚的独立纯日志事件,即使源会话有更新的事件或正在进行的轮次。API 拒绝结束于开放轮次内的前缀,而不是静默截断。更广泛的执行关系健全性检查留在既有的 `dsh-invariants` 插件和持久化修复路径中,不在 `fork()` 中重复。`dsh-subagent-fork-in-process` 保留其已完成前缀截断逻辑,因为工具调用时的委托通常在父轮次仍然打开时启动;普通的会话分支应显式指定请求的 boundary。
|
|
548
597
|
|
|
@@ -576,8 +625,10 @@ interface TurnEndReasonMap {
|
|
|
576
625
|
/** At least one step reached its output-token ceiling, even if a plugin continued the turn. */
|
|
577
626
|
'max-tokens': { kind: 'max-tokens' }
|
|
578
627
|
/**
|
|
579
|
-
* A
|
|
580
|
-
*
|
|
628
|
+
* A crash-orphaned turn was closed after the fact: agent-loop resume appends
|
|
629
|
+
* this closer for a stored log whose last turn never ended, and session-query
|
|
630
|
+
* synthesizes it on cold reads. The loop never emits this marker live, and
|
|
631
|
+
* the events recorded before the crash remain intact.
|
|
581
632
|
*/
|
|
582
633
|
interrupted: { kind: 'interrupted' }
|
|
583
634
|
}
|
|
@@ -593,9 +644,9 @@ interface TurnEndReasonMap {
|
|
|
593
644
|
|
|
594
645
|
## 种子结束边界:`session/end-seed`
|
|
595
646
|
|
|
596
|
-
|
|
647
|
+
新 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。
|
|
597
648
|
|
|
598
|
-
|
|
649
|
+
对于 fork lineage,定位 payload 携带 `inherited: true` 的最后一个 marker;v2 decoding 只在 `SessionHeader.isSeeded` 为 true 时要求该 marker,并从其 seq 推导 `inheritedEventCount`。对于 lifecycle ownership,定位任一形式的最后一个 `session/end-seed`。重新打开已经以任一 marker 结尾的 seed 时,不会再追加普通 marker。
|
|
599
650
|
|
|
600
651
|
它之所以必要,是因为种子历史与实时工作在字节层面完全相同,这会让任何拥有独立开/闭括号的插件失效:一个未配对的 `compaction/start`,无论写入方是在压缩中途崩溃、还是此刻正在压缩,读起来都一样。在 `session/end-seed` 之前的开启标记来自构造种子,并且属于一个已结束的生命周期,无论结束原因为何(崩溃、进程接替,或从仍在运行的父会话 fork 出来),因此其所有方可以视之为已死。这只覆盖*本*会话继承的括号:另一个并发存活的会话可能在同一段历史上持有开放括号,而它自己的边界在别处,因此容忍并发写入方还需要日志之外的存活信号。核心写入该边界但不从中读取任何内容——括号的词汇表仍归其所属插件,这也正是崩溃修复只关闭轮次/步骤/工具边界而从不处理 `compaction/*` 的原因。
|
|
601
652
|
|
|
@@ -603,18 +654,24 @@ interface TurnEndReasonMap {
|
|
|
603
654
|
|
|
604
655
|
## 插件贡献的仅日志事件
|
|
605
656
|
|
|
606
|
-
插件可以通过 declaration merging 添加额外的 `SessionEventMap` 类型。这些是**仅日志**事件:不是 `SurfaceEventType`(不携带 `surfaceOp`,不参与派生历史)。事件所有方决定它们属于一个开放的执行轮次,还是可以独立位于轮次之间,并在自己的不变量配套插件中强制所需关系。生成的[持久化日志事件目录](../persistence-catalog.md)
|
|
657
|
+
插件可以通过 declaration merging 添加额外的 `SessionEventMap` 类型。这些是**仅日志**事件:不是 `SurfaceEventType`(不携带 `surfaceOp`,不参与派生历史)。事件所有方决定它们属于一个开放的执行轮次,还是可以独立位于轮次之间,并在自己的不变量配套插件中强制所需关系。生成的[持久化日志事件目录](../persistence-catalog.md)会列出每个核心或插件贡献的事件;压缩 seam 的 `compaction/*` 语义在 [compaction.md](./compaction.md) 中讨论。
|
|
607
658
|
|
|
608
|
-
如果同一个插件事件族中的多条事件要组装成一个 Web Client Conversation Node,该事件族中的每条 start、update、result、resource 或 interruption 事件都必须携带或独立推导出同一个稳定业务 id。此要求只约束需要关联的 Node 事件族,并不要求每条 Session 事件都有业务 id;Client 因此无须根据相邻关系猜测归属,也无须扫描历史。参见 [Conversation
|
|
659
|
+
如果同一个插件事件族中的多条事件要组装成一个 Web Client Conversation Node,该事件族中的每条 start、update、result、resource 或 interruption 事件都必须携带或独立推导出同一个稳定业务 id。此要求只约束需要关联的 Node 事件族,并不要求每条 Session 事件都有业务 id;Client 因此无须根据相邻关系猜测归属,也无须扫描历史。参见 [Conversation 子系统](./conversation.md)。
|
|
609
660
|
|
|
610
661
|
钩子桥接层的 `hook/invoked` / `hook/result` 对(来自 `@deepseek-ai/dsh-hook-protocol`)通过 `handlerId` 关联。`UserPromptSubmit`、`PreToolUse`、`PostToolUse` 与 `Stop` 在 loop 已打开的轮次内触发,因此其 `hook/*` 记录天然位于轮次之内。`SessionStart` 不生成 `hook/*` 记录,因为它在轮次 1 之前运行;其上下文会在 inbox 中保持待处理,直到唤醒交付打开一个轮次(见[钩子桥接 Agent Note](https://github.com/deepseek-ai/deepseek-harness/blob/master/.agents/notes/implemented/feature/2026-06-30-hook-bridges.zh.md))。
|
|
611
662
|
|
|
612
663
|
## 持久性约定
|
|
613
664
|
|
|
614
|
-
|
|
665
|
+
持久化后端依赖的约定如下:持久日志无损保存每个事件,每个 Assistant attempt 都是一个 `assistant/message` 或 `assistant/attempt`,其嵌入式紧凑 stream 会保留原始带时间 chunk。`seq` 在这些 settlement 与所有交错事件之间保持连续。后端可以为事件批次选择自己的存储 framing,只要句柄的 `read()` 返回与追加时完全一致的事件即可;当前 JSONL v2 每个事件写一行(见 [persistence.md](./persistence.md))。所有 `event.data` 都必须可序列化为 JSON;`Session.append` 会从源头强制这一要求(遇到不可序列化数据时抛出),因此错误事件绝不会进入日志,`session.snapshotEvents()` 始终与后端可持久化的内容一致。新增会携带不可序列化数据、破坏核心执行嵌套或违反事件所有方声明关系的事件类型,都会构成磁盘格式的破坏性变更。
|
|
615
666
|
|
|
616
667
|
消费此约定的后端见 [persistence.md](./persistence.md)。
|
|
617
668
|
|
|
669
|
+
## Remote 目录与 workspace 打开
|
|
670
|
+
|
|
671
|
+
`ModelCatalog` 是 `session/modelCatalog` 返回的 Host generation 模型目录:它携带部署默认值、可路由 provider id、成功的 provider 分组与相互隔离的 provider 失败。它不由某个 Session 派生,因此与 Session projection 分开保存。
|
|
672
|
+
|
|
673
|
+
`SessionOpenWorkspacePathRequest` 携带绝对路径或已按 workspace 解析的 `path`。`SessionOpenWorkspacePathValue` 确认 Host 已接受原生交接。Session-aware Client 会在已知当前 Session cwd 时据此解析相对路径;controller 将路径原样交给打开器,并通过 Session Remote 错误词汇表报告无效请求、取消与打开器失败。
|
|
674
|
+
|
|
618
675
|
<!-- BEGIN GENERATED cordis-surface (gen-cordis-catalog.ts) — do not edit between markers -->
|
|
619
676
|
|
|
620
677
|
<a id="cordis-surface"></a>
|
|
@@ -623,13 +680,158 @@ interface TurnEndReasonMap {
|
|
|
623
680
|
|
|
624
681
|
Generated from source by `scripts/gen-cordis-catalog.ts` (verified fresh by `pnpm run verify-cordis-catalog` in doc-sync; regenerate with `pnpm run gen-cordis-catalog`) — the language sides differ only in locale-specific paired document paths. Signature blocks use a `ts cordis-catalog` fence and keep the original source JSDoc; dispatch modes are defined in the [primer](../cordis-primer.md#dispatch-modes), and the framework-inherited `ctx` API lives in [cordis-api/inherited.md](../cordis-api/inherited.md).
|
|
625
682
|
|
|
683
|
+
<a id="ctxsessioncontroller--sessioncontroller"></a>
|
|
684
|
+
|
|
685
|
+
### `ctx.sessionController` — `SessionController`
|
|
686
|
+
|
|
687
|
+
Host service backing the generated `ctx.remote.session` namespace.
|
|
688
|
+
|
|
689
|
+
```ts cordis-catalog
|
|
690
|
+
/**
|
|
691
|
+
* Resolve or resume one ordinary Session for another Host API domain.
|
|
692
|
+
* @param sessionId - Session identity whose Agent owns the operation.
|
|
693
|
+
* @returns the live Agent or the stable Session-domain failure.
|
|
694
|
+
*/
|
|
695
|
+
resolveAgent(sessionId: SessionId): Promise<ApiSessionAgentResult>
|
|
696
|
+
|
|
697
|
+
/**
|
|
698
|
+
* Inspect one attached or persisted Session without activating its Agent.
|
|
699
|
+
* @param sessionId - durable Session identity.
|
|
700
|
+
* @param signal - optional caller cancellation for persistence reads.
|
|
701
|
+
* @returns the current attached state or persisted header and event prefix.
|
|
702
|
+
*/
|
|
703
|
+
inspect( sessionId: SessionId, signal?: AbortSignal, ): Promise<SessionInspection>
|
|
704
|
+
|
|
705
|
+
/**
|
|
706
|
+
* Read all visible Session rows without resuming an Agent.
|
|
707
|
+
* @param _request - reserved empty list request.
|
|
708
|
+
* @param signal - cancellation for persistence reads.
|
|
709
|
+
* @returns visible Session summaries ordered by activity.
|
|
710
|
+
*/
|
|
711
|
+
@Remote('list') async list(_request: SessionListRequest, signal: AbortSignal): Promise<SessionListValue>
|
|
712
|
+
|
|
713
|
+
/**
|
|
714
|
+
* Search visible Session content without resuming an Agent.
|
|
715
|
+
* @param request - literal message-content query.
|
|
716
|
+
* @param signal - cancellation for list and search reads.
|
|
717
|
+
* @returns authorized bounded Session search results.
|
|
718
|
+
*/
|
|
719
|
+
@Remote('search') search(request: SessionSearchRequest, signal: AbortSignal): Promise<SessionSearchValue>
|
|
720
|
+
|
|
721
|
+
/**
|
|
722
|
+
* Create or idempotently adopt one ordinary Session.
|
|
723
|
+
* @param request - requested identity, location, and Agent preset.
|
|
724
|
+
* @returns the Session identity and resolved preset when configured.
|
|
725
|
+
*/
|
|
726
|
+
@Remote('create') create(request: SessionCreateRequest): Promise<SessionCreateValue>
|
|
727
|
+
|
|
728
|
+
/**
|
|
729
|
+
* Select one Session-local model after explicitly resuming the Session.
|
|
730
|
+
* @param request - Session identity and requested model selection.
|
|
731
|
+
* @returns the normalized selection installed for the Session.
|
|
732
|
+
*/
|
|
733
|
+
@Remote('selectModel') selectModel(request: SessionSelectModelRequest): Promise<SessionSelectModelValue>
|
|
734
|
+
|
|
735
|
+
/**
|
|
736
|
+
* Describe every currently routable model for Host-generation selectors.
|
|
737
|
+
* @returns provider-grouped models, the deployment default, and isolated provider failures.
|
|
738
|
+
*/
|
|
739
|
+
@Remote('modelCatalog') modelCatalog(): Promise<ModelCatalog>
|
|
740
|
+
|
|
741
|
+
/**
|
|
742
|
+
* Report whether this deployment can hand a Session workspace path to a native desktop.
|
|
743
|
+
* @returns true when the matching open operation is available.
|
|
744
|
+
*/
|
|
745
|
+
@Remote canOpenWorkspacePath(): boolean
|
|
746
|
+
|
|
747
|
+
/**
|
|
748
|
+
* Open one path prepared by a Session-aware caller on the Host desktop.
|
|
749
|
+
* @param request - path after best-effort Session workspace resolution.
|
|
750
|
+
* @param signal - caller lifetime; abort terminates the native command.
|
|
751
|
+
* @returns confirmation after the native opener accepts the path.
|
|
752
|
+
* @throws RemoteError when the request is invalid, cancelled, or the opener fails.
|
|
753
|
+
*/
|
|
754
|
+
@Remote('openWorkspacePath') async openWorkspacePath( request: SessionOpenWorkspacePathRequest, signal: AbortSignal, ): Promise<SessionOpenWorkspacePathValue>
|
|
755
|
+
|
|
756
|
+
/**
|
|
757
|
+
* Rename one Session after explicitly resuming it.
|
|
758
|
+
* @param request - Session identity and proposed title.
|
|
759
|
+
* @returns the accepted title and durable event sequence.
|
|
760
|
+
*/
|
|
761
|
+
@Remote('rename') rename(request: SessionRenameRequest): Promise<SessionRenameValue>
|
|
762
|
+
|
|
763
|
+
/**
|
|
764
|
+
* Fork one cold-readable completed-turn prefix into a new Session.
|
|
765
|
+
* @param request - source Session and optional event anchor.
|
|
766
|
+
* @returns the new Session identity.
|
|
767
|
+
*/
|
|
768
|
+
@Remote('fork') fork(request: SessionForkRequest): Promise<SessionForkValue>
|
|
769
|
+
|
|
770
|
+
/**
|
|
771
|
+
* Admit one prompt after explicitly resuming its Session.
|
|
772
|
+
* @param request - Session identity, prompt content, source metadata, and delivery mode.
|
|
773
|
+
* @param signal - caller cancellation before prompt admission begins.
|
|
774
|
+
* @returns acknowledgement that the Agent accepted the prompt.
|
|
775
|
+
*/
|
|
776
|
+
@Remote('prompt') prompt(request: SessionPromptRequest, signal: AbortSignal): Promise<SessionPromptValue>
|
|
777
|
+
|
|
778
|
+
/**
|
|
779
|
+
* Read one image proven reachable from the addressed Session log.
|
|
780
|
+
* @param request - Session and attachment identities used for authorization.
|
|
781
|
+
* @returns the durable attachment reference and base64-encoded bytes.
|
|
782
|
+
*/
|
|
783
|
+
@Remote('attachment') attachment(request: SessionAttachmentRequest): Promise<SessionAttachmentValue>
|
|
784
|
+
|
|
785
|
+
/**
|
|
786
|
+
* Mutate one still-pending queue occurrence on a live Agent.
|
|
787
|
+
* @param request - Session, queue item, and requested mutation.
|
|
788
|
+
* @returns acknowledgement that the queue mutation was applied.
|
|
789
|
+
*/
|
|
790
|
+
@Remote('updateQueue') updateQueue(request: SessionUpdateQueueRequest): SessionUpdateQueueValue
|
|
791
|
+
|
|
792
|
+
/**
|
|
793
|
+
* Cancel one active Agent turn without dropping its pending inbox.
|
|
794
|
+
* @param request - Session whose active Agent turn is cancelled.
|
|
795
|
+
* @returns acknowledgement that cancellation was requested.
|
|
796
|
+
*/
|
|
797
|
+
@Remote('cancel') cancel(request: SessionCancelRequest): SessionCancelValue
|
|
798
|
+
|
|
799
|
+
/**
|
|
800
|
+
* Read one cold-safe, message-aligned Session history page.
|
|
801
|
+
* @param request - durable address, backward cursor, and page budget.
|
|
802
|
+
* @param signal - cancellation for persistence reads.
|
|
803
|
+
* @returns one chronological page.
|
|
804
|
+
*/
|
|
805
|
+
@Remote('page') page(request: SessionPageRequest, signal: AbortSignal): Promise<SessionPage>
|
|
806
|
+
|
|
807
|
+
/**
|
|
808
|
+
* Follow one Session log from its opening or resume cursor.
|
|
809
|
+
* @param request - durable address and last committed sequence already held by the caller.
|
|
810
|
+
* @param signal - cancellation owned by the Remote stream carrier.
|
|
811
|
+
* @returns a complete opening snapshot followed by gap-free durable event
|
|
812
|
+
* frames and optional cursorless assistant-stream frames.
|
|
813
|
+
*/
|
|
814
|
+
@Remote({ mode: 'stream' }) follow(request: SessionFollowRequest, signal: AbortSignal): AsyncIterable<SessionFollowFrame>
|
|
815
|
+
|
|
816
|
+
/**
|
|
817
|
+
* Stream a complete live-control baseline followed by replacement frames.
|
|
818
|
+
* @param signal - cancellation owned by the Remote stream carrier.
|
|
819
|
+
* @returns one complete baseline followed by live replacement frames.
|
|
820
|
+
*/
|
|
821
|
+
@Remote({ mode: 'stream' }) control(signal: AbortSignal): AsyncIterable<SessionControlFrame>
|
|
822
|
+
```
|
|
823
|
+
|
|
824
|
+
Types: [SessionId](./core.md) · [SessionInspection](./persistence.md) · [SessionSearchRequest](./session-query.md)
|
|
825
|
+
|
|
826
|
+
Source: [`packages/api/session-controller/src/index.ts`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/api/session-controller/src/index.ts)
|
|
827
|
+
|
|
626
828
|
<a id="ctxsessions--sessionstore"></a>
|
|
627
829
|
|
|
628
830
|
### `ctx.sessions` — `SessionStore`
|
|
629
831
|
|
|
630
832
|
In-memory session store (`ctx.sessions`).
|
|
631
833
|
|
|
632
|
-
Persistence is intentionally not implemented here —
|
|
834
|
+
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.
|
|
633
835
|
|
|
634
836
|
```ts cordis-catalog
|
|
635
837
|
/**
|
|
@@ -752,13 +954,113 @@ list(): Session[]
|
|
|
752
954
|
* `SessionStore`'s id policy.
|
|
753
955
|
* @returns The created live child session.
|
|
754
956
|
*/
|
|
755
|
-
fork(source: SessionForkSource, boundary?:
|
|
957
|
+
fork(source: SessionForkSource, boundary?: SessionSeq, childSessionId?: SessionId): Session
|
|
756
958
|
```
|
|
757
959
|
|
|
758
960
|
Types: [CreateSessionOptions](./persistence.md) · [PrepareSessionOptions](./persistence.md) · [SessionId](./core.md)
|
|
759
961
|
|
|
760
962
|
Source: [`packages/core/session/src/index.ts`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/core/session/src/index.ts)
|
|
761
963
|
|
|
964
|
+
<a id="api-session-events"></a>
|
|
965
|
+
|
|
966
|
+
### `api-session/*` events
|
|
967
|
+
|
|
968
|
+
<a id="api-sessionactivity--emit"></a>
|
|
969
|
+
|
|
970
|
+
#### `api-session/activity` — emit
|
|
971
|
+
|
|
972
|
+
One user-authored durable message advanced Session list activity.
|
|
973
|
+
|
|
974
|
+
```ts cordis-catalog
|
|
975
|
+
/**
|
|
976
|
+
* One user-authored durable message advanced Session list activity.
|
|
977
|
+
* @mode emit
|
|
978
|
+
* @param sessionId - addressed Session identity.
|
|
979
|
+
* @param updatedAt - durable message time used for list ordering.
|
|
980
|
+
*/
|
|
981
|
+
'api-session/activity'(sessionId: SessionId, updatedAt: number): void
|
|
982
|
+
```
|
|
983
|
+
|
|
984
|
+
Types: [SessionId](./core.md)
|
|
985
|
+
|
|
986
|
+
Source: [`packages/api/session-controller/src/types.ts`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/api/session-controller/src/types.ts)
|
|
987
|
+
|
|
988
|
+
<a id="api-sessionadded--emit"></a>
|
|
989
|
+
|
|
990
|
+
#### `api-session/added` — emit
|
|
991
|
+
|
|
992
|
+
A Session became visible to Session list consumers.
|
|
993
|
+
|
|
994
|
+
```ts cordis-catalog
|
|
995
|
+
/**
|
|
996
|
+
* A Session became visible to Session list consumers.
|
|
997
|
+
* @mode emit
|
|
998
|
+
* @param summary - initial list row for the Session.
|
|
999
|
+
*/
|
|
1000
|
+
'api-session/added'(summary: SessionSummary): void
|
|
1001
|
+
```
|
|
1002
|
+
|
|
1003
|
+
Source: [`packages/api/session-controller/src/types.ts`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/api/session-controller/src/types.ts)
|
|
1004
|
+
|
|
1005
|
+
<a id="api-sessionerror--emit"></a>
|
|
1006
|
+
|
|
1007
|
+
#### `api-session/error` — emit
|
|
1008
|
+
|
|
1009
|
+
One Agent failed outside a durable turn position.
|
|
1010
|
+
|
|
1011
|
+
```ts cordis-catalog
|
|
1012
|
+
/**
|
|
1013
|
+
* One Agent failed outside a durable turn position.
|
|
1014
|
+
* @mode emit
|
|
1015
|
+
* @param sessionId - Agent and Session identity.
|
|
1016
|
+
* @param message - user-safe failure chain.
|
|
1017
|
+
*/
|
|
1018
|
+
'api-session/error'(sessionId: SessionId, message: string): void
|
|
1019
|
+
```
|
|
1020
|
+
|
|
1021
|
+
Types: [SessionId](./core.md)
|
|
1022
|
+
|
|
1023
|
+
Source: [`packages/api/session-controller/src/types.ts`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/api/session-controller/src/types.ts)
|
|
1024
|
+
|
|
1025
|
+
<a id="api-sessionremoved--emit"></a>
|
|
1026
|
+
|
|
1027
|
+
#### `api-session/removed` — emit
|
|
1028
|
+
|
|
1029
|
+
A Session left the live Host registry.
|
|
1030
|
+
|
|
1031
|
+
```ts cordis-catalog
|
|
1032
|
+
/**
|
|
1033
|
+
* A Session left the live Host registry.
|
|
1034
|
+
* @mode emit
|
|
1035
|
+
* @param sessionId - removed Session identity.
|
|
1036
|
+
*/
|
|
1037
|
+
'api-session/removed'(sessionId: SessionId): void
|
|
1038
|
+
```
|
|
1039
|
+
|
|
1040
|
+
Types: [SessionId](./core.md)
|
|
1041
|
+
|
|
1042
|
+
Source: [`packages/api/session-controller/src/types.ts`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/api/session-controller/src/types.ts)
|
|
1043
|
+
|
|
1044
|
+
<a id="api-sessionstatus--emit"></a>
|
|
1045
|
+
|
|
1046
|
+
#### `api-session/status` — emit
|
|
1047
|
+
|
|
1048
|
+
One Agent changed running state.
|
|
1049
|
+
|
|
1050
|
+
```ts cordis-catalog
|
|
1051
|
+
/**
|
|
1052
|
+
* One Agent changed running state.
|
|
1053
|
+
* @mode emit
|
|
1054
|
+
* @param sessionId - Agent and Session identity.
|
|
1055
|
+
* @param running - whether the Agent is running.
|
|
1056
|
+
*/
|
|
1057
|
+
'api-session/status'(sessionId: SessionId, running: boolean): void
|
|
1058
|
+
```
|
|
1059
|
+
|
|
1060
|
+
Types: [SessionId](./core.md)
|
|
1061
|
+
|
|
1062
|
+
Source: [`packages/api/session-controller/src/types.ts`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/api/session-controller/src/types.ts)
|
|
1063
|
+
|
|
762
1064
|
<a id="session-events"></a>
|
|
763
1065
|
|
|
764
1066
|
### `session/*` events
|