@deepseek-ai/dsh-session 0.1.2-rc.1 → 0.1.5-alpha.1
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/README.i18n.yaml +2 -2
- package/README.md +15 -12
- package/README.zh.md +15 -12
- package/lib/index.js +254 -480
- package/lib/invariant.js +5 -2
- package/lib/types/index.d.ts +26 -21
- package/lib/types/index.js +109 -75
- package/lib/types/invariant.js +6 -2
- package/lib/types/known-event-types.js +6 -3
- package/lib/types/request-header.d.ts +4 -4
- package/lib/types/request-header.js +5 -7
- package/lib/types/surface.d.ts +18 -2
- package/lib/types/surface.js +110 -23
- package/lib/types/types.d.ts +101 -85
- package/lib/types/types.js +9 -10
- package/package.json +9 -13
- package/lib/types/chunk-rows.d.ts +0 -106
- package/lib/types/chunk-rows.js +0 -328
package/README.i18n.yaml
CHANGED
|
@@ -2,5 +2,5 @@
|
|
|
2
2
|
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
|
3
3
|
# after editing either side, bring the other along and re-record with:
|
|
4
4
|
# pnpm run verify-translation-pairing --write packages/core/session/README.md
|
|
5
|
-
README.md:
|
|
6
|
-
README.zh.md:
|
|
5
|
+
README.md: a06b22b5e8f48743047c68e883edd496fbc6eb77
|
|
6
|
+
README.zh.md: 755894b4c30f0a3807f472cece32685d102efbeb
|
package/README.md
CHANGED
|
@@ -9,7 +9,7 @@ English | [中文](README.zh.md)
|
|
|
9
9
|
|
|
10
10
|
## Summary
|
|
11
11
|
|
|
12
|
-
`dsh-session`
|
|
12
|
+
`dsh-session` records every model-visible fact in an append-only session log and derives model history from that record. Consumers can inspect, replay, fork, and flush sessions while preserving historical events; compaction hides superseded entries from the active conversation without deleting them. Sessions remain in memory unless a persistence backend is added, and durability checkpoints wait for configured backends. Choose this package wherever an agent needs a reconstructable session record; it does not call models.
|
|
13
13
|
|
|
14
14
|
## Table of Contents
|
|
15
15
|
|
|
@@ -47,7 +47,11 @@ session.append('user/message', { role: 'user', content: [{ type: 'text', text: '
|
|
|
47
47
|
session.deriveMessages() // the derived model history
|
|
48
48
|
```
|
|
49
49
|
|
|
50
|
-
Surface events (`user/message`, `assistant/message`, `tool/result`)
|
|
50
|
+
Surface events (`system/message`, `user/message`, `assistant/message`, `tool/result`) require `surfaceOp` in both typed events and append input. A replacement uses exactly `{ op: 'replace', startSeq, endSeq }`, with inclusive `SessionSeq` endpoints in current surface order. An Assistant message embeds its exact compact provider stream and forbids `sourceEventSeqs`. Known log-only events forbid both metadata fields and never produce a message.
|
|
51
|
+
|
|
52
|
+
Append, seed/restore, and event adoption/snapshot reject any `header.system` and exactly empty optional request-header fields (`tools: []`, `adapterDefaults: {}`) instead of normalizing input. Tool-result `data.error` is allowed only when `message.content[0].isError === true`; failure identity remains optional. Rejected appends do not change the log, derived state, or event feed. Adoption validates event-local metadata but not referenced history or replacement membership.
|
|
53
|
+
|
|
54
|
+
`system/message` holds the rendered system prompt: the first one is surface node 0, the prepared call capability governs admission, with a non-empty rendering consolidated at the first system node on an incapable route or appended after cached history inside a continuing `in-history` series; empty system nodes project to no message, so clearing the prompt requires logged empty replacements of all active system nodes, not just the latest; the surface fold rejects a replacement covering node 0 while it is a `system/message` unless the replacing event is itself a `system/message` over exactly that node, while later system nodes carry no protection and a compaction range may shadow them ([decision](../../../.agents/notes/implemented/architecture/2026-09-02-system-prompt-as-surface-node.md)).
|
|
51
55
|
|
|
52
56
|
### Read the log
|
|
53
57
|
|
|
@@ -77,7 +81,7 @@ This section explains how the package realizes the behavior above; the observabl
|
|
|
77
81
|
|
|
78
82
|
### Design concept
|
|
79
83
|
|
|
80
|
-
The package is built on event sourcing: a `Session` is an append-only log of typed `SessionEvent`s, and everything else — model history, transcripts, telemetry, titles, persistence — derives from that stream. The surface is a derived projection: an incremental manager validates append candidates, advances the ordered view from committed events, and tracks a `replaceGeneration` that bumps on every committed rewrite. Model-visible means logged: anything that reaches a model request must be reconstructable from the log.
|
|
84
|
+
The package is built on event sourcing: a `Session` is an append-only log of typed `SessionEvent`s, and everything else — model history, transcripts, telemetry, titles, persistence — derives from that stream. The surface is a derived projection: an incremental manager validates append candidates, advances the ordered view from committed events, and tracks a `replaceGeneration` that bumps on every committed rewrite. Model-visible means logged: anything that reaches a model request must be reconstructable from the log. Each model attempt that reaches settlement commits one event: `assistant/message` carries the assembled model-visible message plus its compact timed stream, while `assistant/attempt` retains a failed, retried, cancelled, or stream-error attempt without adding model history. A hard process loss before settlement leaves no durable attempt stream.
|
|
81
85
|
|
|
82
86
|
### Request headers
|
|
83
87
|
|
|
@@ -92,7 +96,6 @@ The package is built on event sourcing: a `Session` is an append-only log of typ
|
|
|
92
96
|
| [`src/surface.ts`](src/surface.ts) | Ordered surface projection, replacement validation, `deriveEventMessage` |
|
|
93
97
|
| [`src/request-header.ts`](src/request-header.ts) | `request/header` folding and reconstruction |
|
|
94
98
|
| [`dsh-util-values`](../../util/values/README.md) | Shared lossless JSON validation and detached snapshots |
|
|
95
|
-
| [`src/chunk-rows.ts`](src/chunk-rows.ts) | Shared compact-row storage codec for persistence backends |
|
|
96
99
|
| [`src/repair.ts`](src/repair.ts) | Cold repair of crash-orphaned logs |
|
|
97
100
|
| [`src/invariant.ts`](src/invariant.ts) | Invariant companion: seq, turn/step enclosure, tool call/result pairing |
|
|
98
101
|
|
|
@@ -102,11 +105,11 @@ Every append uses the shared iterative `snapshotJsonValue()` pass, which reads,
|
|
|
102
105
|
|
|
103
106
|
### Derived history
|
|
104
107
|
|
|
105
|
-
`deriveMessages()` caches each surface node's projection once and returns a fresh array per call over shared, deep-frozen messages; each of the
|
|
108
|
+
`deriveMessages()` caches each surface node's projection once and returns a fresh array per call over shared, deep-frozen messages; each of the four surface event types (`system/message`, `user/message`, `assistant/message`, `tool/result`) projects its own message kind — the system-role prompt (an empty-content system node projects to no message), user content verbatim, the assembled assistant message with its provider and model, or a user-role tool result. Embedded Assistant streams and `assistant/attempt` events remain replay and diagnostic data only. A surface rewrite rebuilds the projection — there is no raw-log fallback, so the surface is the single source of derived history.
|
|
106
109
|
|
|
107
110
|
### The request header
|
|
108
111
|
|
|
109
|
-
The loop logs a full canonical `request/header` snapshot (call config, adapter defaults, rendered system prompt,
|
|
112
|
+
The loop logs a full canonical `request/header` snapshot (call config, adapter defaults, assembled tool schemas — the rendered system prompt is a `system/message` surface node, not header state) at each loop-instance boundary and on change; `foldRequestHeader(events)` reconstructs it by selecting the latest snapshot, making every conversation request a pure function of the log. Route metadata (`request/context`) is separate logged state appended only when the provider, model, capacity, or `systemPromptUpdate` mode differs; it records the actual prepared call's mode after prompt and user admission, rather than supplying that admission decision.
|
|
110
113
|
|
|
111
114
|
</details>
|
|
112
115
|
|
|
@@ -132,7 +135,7 @@ The package-level contract is enough for most consumers; read these when you nee
|
|
|
132
135
|
|
|
133
136
|
#### What the model sees
|
|
134
137
|
|
|
135
|
-
The model receives the complete messages from `user/message`, `assistant/message`, and `tool/result` surface entries verbatim — identities, roles, sources, and content blocks are the same values established at creation, and projections never mint identities. Direct prompts and injected context remain separate `user/message` events whose sources preserve their provenance.
|
|
138
|
+
The model receives the complete messages from `system/message`, `user/message`, `assistant/message`, and `tool/result` surface entries verbatim, the system prompt first — identities, roles, sources, and content blocks are the same values established at creation, and projections never mint identities. Direct prompts and injected context remain separate `user/message` events whose sources preserve their provenance. Embedded streams, `assistant/attempt`, boundaries, and other log-only facts add no message.
|
|
136
139
|
|
|
137
140
|
#### Token effect
|
|
138
141
|
|
|
@@ -160,15 +163,15 @@ Append-only; newly visible content follows the reusable request prefix and does
|
|
|
160
163
|
|
|
161
164
|
#### What the model sees
|
|
162
165
|
|
|
163
|
-
The session reconstructs the
|
|
166
|
+
The session reconstructs the tool schemas and call config that the loop actually sent; the system prompt is part of `deriveMessages()` as surface node 0 and, after an in-history update, as the latest system node. Header events add no message to history and hold no copy of the prompt.
|
|
164
167
|
|
|
165
168
|
#### Token effect
|
|
166
169
|
|
|
167
|
-
Zero duplicate tokens from logging. The
|
|
170
|
+
Zero duplicate tokens from logging. The system nodes and schemas still incur their normal per-request cost.
|
|
168
171
|
|
|
169
172
|
#### KV Cache effect
|
|
170
173
|
|
|
171
|
-
Logging causes no invalidation, and exact reconstruction preserves request-prefix identity. A later header with changed
|
|
174
|
+
Logging causes no invalidation, and exact reconstruction preserves request-prefix identity. A later header with changed config or schemas may invalidate reuse from its first difference; a prompt change that replaces surface node 0 invalidates reuse from the first token, while an in-history append keeps the prefix through the cached history reusable.
|
|
172
175
|
|
|
173
176
|
## Known Limitations and Deferred Work
|
|
174
177
|
|
|
@@ -177,8 +180,8 @@ Logging causes no invalidation, and exact reconstruction preserves request-prefi
|
|
|
177
180
|
|
|
178
181
|
These limits define when the session store needs special care. They are current package constraints, not a task backlog.
|
|
179
182
|
|
|
180
|
-
- **`fork()` cuts only at stable boundaries of live sessions** — the selected prefix must end outside an open turn and the source must be in the store; forking a persisted-but-unloaded session is excluded from the
|
|
181
|
-
- **`SESSION_FORMAT_VERSION`
|
|
183
|
+
- **`fork()` cuts only at stable boundaries of live sessions** — the selected prefix must end outside an open turn and the source must be in the store; forking a persisted-but-unloaded session is excluded from the fork API.
|
|
184
|
+
- **`SESSION_FORMAT_VERSION` names the current V3 logical representation** — the V3 reader rejects retired `header.system` and validates `system/message` payloads and protected-head rewrites. Historical headers and events belong to adjacent format packages; the V2→V3 edge converts supported history before constructing `Session`, and write open publishes only the V3 successor. Equal-version unknown events require the envelope's explicit `ignorable` marker, which does not promise safe structural migration ([mechanism](../../../.agents/notes/implemented/architecture/2026-08-31-released-session-format-migrations.md)).
|
|
182
185
|
- **`TurnEndReasonMap` omits the ACP-named `refusal` / `max_turn_requests` variants** — producer-gated: they land when an adapter or the loop first emits them.
|
|
183
186
|
- **No session tree beyond fork** — a pi-style entry tree over branched sessions is deferred unless a consumer needs more than boundary-based forking.
|
|
184
187
|
|
package/README.zh.md
CHANGED
|
@@ -9,7 +9,7 @@ kind: "package-reference"
|
|
|
9
9
|
|
|
10
10
|
## 概述
|
|
11
11
|
|
|
12
|
-
`dsh-session`
|
|
12
|
+
`dsh-session` 在仅追加的会话日志中记录每个模型可见事实,并从该记录派生模型历史。消费方可以检查、回放、派生和刷新会话,同时保留历史事件;压缩会在活跃对话中隐藏被取代的条目,但不会删除它们。除非添加持久化后端,否则会话仅保留在内存中;持久性检查点会等待配置的后端。agent 需要可重建的会话记录时请选择本包;它本身不调用模型。
|
|
13
13
|
|
|
14
14
|
## 目录
|
|
15
15
|
|
|
@@ -47,7 +47,11 @@ session.append('user/message', { role: 'user', content: [{ type: 'text', text: '
|
|
|
47
47
|
session.deriveMessages() // the derived model history
|
|
48
48
|
```
|
|
49
49
|
|
|
50
|
-
表层事件(`user/message`、`assistant/message`、`tool/result
|
|
50
|
+
表层事件(`system/message`、`user/message`、`assistant/message`、`tool/result`)在类型化事件与追加输入中都必须带有 `surfaceOp`。替换操作仅接受 `{ op: 'replace', startSeq, endSeq }`,端点为包含边界的 `SessionSeq`,按当前 surface 顺序解释。Assistant message 嵌入其精确紧凑 provider stream,并禁止 `sourceEventSeqs`。已知仅日志事件禁止这两个元数据字段,且从不产生消息。
|
|
51
|
+
|
|
52
|
+
追加、seed/restore 与事件 adoption/snapshot 会拒绝任何 `header.system` 及恰好为空的可选请求头字段(`tools: []`、`adapterDefaults: {}`),而不规范化输入。工具结果的 `data.error` 仅在 `message.content[0].isError === true` 时允许存在;失败标识仍是可选的。被拒绝的追加不会改变日志、派生状态或事件流。Adoption 校验事件局部元数据,但不校验所引用的历史或替换端点是否属于 surface。
|
|
53
|
+
|
|
54
|
+
`system/message` 承载渲染后的系统提示词:第一条是 surface 第 0 号节点,准入依据已准备调用的能力,不具备能力的路由将非空渲染文本归并到首个系统节点,延续中的 `in-history` 序列则在缓存历史之后追加;空系统节点不投影为消息,因此清除提示词必须为所有生效的系统节点记录空内容替换,而非仅替换最新节点;当第 0 号节点是 `system/message` 时,surface 折叠拒绝覆盖它的替换,除非替换事件本身是恰好覆盖该节点的 `system/message`,而后续系统节点不受保护,压缩范围可以遮蔽它们([决策](../../../.agents/notes/implemented/architecture/2026-09-02-system-prompt-as-surface-node.zh.md))。
|
|
51
55
|
|
|
52
56
|
### 读取日志
|
|
53
57
|
|
|
@@ -77,7 +81,7 @@ session.deriveMessages() // the derived model history
|
|
|
77
81
|
|
|
78
82
|
### 设计理念
|
|
79
83
|
|
|
80
|
-
该包建立在事件溯源之上:`Session` 是类型化 `SessionEvent` 的仅追加日志,其他一切——模型历史、transcript、遥测、标题、持久化——都从这条流派生。surface 是派生投影:一个增量管理器校验追加候选、根据已提交事件推进有序视图,并跟踪每次已提交重写都会递增的 `replaceGeneration
|
|
84
|
+
该包建立在事件溯源之上:`Session` 是类型化 `SessionEvent` 的仅追加日志,其他一切——模型历史、transcript、遥测、标题、持久化——都从这条流派生。surface 是派生投影:一个增量管理器校验追加候选、根据已提交事件推进有序视图,并跟踪每次已提交重写都会递增的 `replaceGeneration`。模型可见即已记录:任何到达模型请求的内容都必须能从日志重建。每个到达 settlement 的模型 attempt 都会提交一个事件:`assistant/message` 携带组装后的模型可见 message 及其紧凑带时间 stream,`assistant/attempt` 则保留失败、重试、取消或 stream error attempt,且不添加模型历史。如果进程在 settlement 前硬中断,则不会留下持久 attempt stream。
|
|
81
85
|
|
|
82
86
|
### 请求 header
|
|
83
87
|
|
|
@@ -92,7 +96,6 @@ session.deriveMessages() // the derived model history
|
|
|
92
96
|
| [`src/surface.ts`](src/surface.ts) | 有序 surface 投影、替换校验、`deriveEventMessage` |
|
|
93
97
|
| [`src/request-header.ts`](src/request-header.ts) | `request/header` 折叠与重建 |
|
|
94
98
|
| [`dsh-util-values`](../../util/values/README.zh.md) | 共享无损 JSON 校验与分离式快照 |
|
|
95
|
-
| [`src/chunk-rows.ts`](src/chunk-rows.ts) | 供持久化后端使用的共享紧凑行存储编解码器 |
|
|
96
99
|
| [`src/repair.ts`](src/repair.ts) | 崩溃遗留日志的冷修复 |
|
|
97
100
|
| [`src/invariant.ts`](src/invariant.ts) | 不变式配套:序号、轮次/步骤闭合、工具调用/结果配对 |
|
|
98
101
|
|
|
@@ -102,11 +105,11 @@ session.deriveMessages() // the derived model history
|
|
|
102
105
|
|
|
103
106
|
### 派生历史
|
|
104
107
|
|
|
105
|
-
`deriveMessages()` 把每个 surface
|
|
108
|
+
`deriveMessages()` 把每个 surface 节点的投影缓存一次,每次调用都返回共享、深度冻结消息之上的新数组;四种 surface 事件类型(`system/message`、`user/message`、`assistant/message`、`tool/result`)各自投影自己的消息种类——system 角色的提示词(空内容的系统节点投影为无消息)、user 内容原样、带提供方与模型的组装 assistant 消息,或 user 角色的工具结果。嵌入式 Assistant stream 与 `assistant/attempt` 事件只保留重放和诊断数据。surface 重写会重建投影——不存在原始日志回退,因此 surface 是派生历史的唯一来源。
|
|
106
109
|
|
|
107
110
|
### 请求头
|
|
108
111
|
|
|
109
|
-
循环在每个循环实例边界及变更时记录完整规范 `request/header`
|
|
112
|
+
循环在每个循环实例边界及变更时记录完整规范 `request/header` 快照(调用配置、适配器默认值、组装后的工具 schema——渲染后的系统提示词是 `system/message` surface 节点,不是 header 状态);`foldRequestHeader(events)` 通过选择最新快照来重建它,使每个对话请求都成为日志的纯函数。路由元数据(`request/context`)是独立的已记录状态,仅在提供方、模型、容量或 `systemPromptUpdate` 模式变化时追加;它在提示词与用户消息准入之后记录实际已准备调用的模式,而非提供准入决策。
|
|
110
113
|
|
|
111
114
|
</details>
|
|
112
115
|
|
|
@@ -132,7 +135,7 @@ session.deriveMessages() // the derived model history
|
|
|
132
135
|
|
|
133
136
|
#### 模型看到什么
|
|
134
137
|
|
|
135
|
-
模型会原样接收 `user/message`、`assistant/message` 与 `tool/result` surface
|
|
138
|
+
模型会原样接收 `system/message`、`user/message`、`assistant/message` 与 `tool/result` surface 条目中的完整消息,系统提示词在先——标识、角色、来源与内容块都与创建时确定的值相同,投影从不生成标识。直接提示词与注入上下文仍是彼此独立的 `user/message` 事件,各事件的来源会保留其出处。嵌入式 stream、`assistant/attempt`、边界与其他仅日志事实不会添加消息。
|
|
136
139
|
|
|
137
140
|
#### Token 影响
|
|
138
141
|
|
|
@@ -160,15 +163,15 @@ session.deriveMessages() // the derived model history
|
|
|
160
163
|
|
|
161
164
|
#### 模型看到什么
|
|
162
165
|
|
|
163
|
-
|
|
166
|
+
会话会重建循环实际发送的工具 schema 与调用配置;系统提示词作为 surface 第 0 号节点、并在历史内更新之后作为最新的系统节点,属于 `deriveMessages()` 的一部分。请求头事件不向历史加入任何消息,也不持有提示词的副本。
|
|
164
167
|
|
|
165
168
|
#### Token 影响
|
|
166
169
|
|
|
167
|
-
日志记录不产生重复 token
|
|
170
|
+
日志记录不产生重复 token。各系统节点与 schema 仍会产生正常的逐请求开销。
|
|
168
171
|
|
|
169
172
|
#### KV Cache 影响
|
|
170
173
|
|
|
171
|
-
|
|
174
|
+
记录日志不会导致失效,精确重建会保持请求前缀一致。后续请求头若更改配置或 schema,可能从第一处差异开始使复用失效;替换 surface 第 0 号节点的提示词变更会从第一个 token 起使复用失效,而历史内追加则保持直到已缓存历史末尾的前缀可复用。
|
|
172
175
|
|
|
173
176
|
## 已知限制与延期工作
|
|
174
177
|
|
|
@@ -177,8 +180,8 @@ session.deriveMessages() // the derived model history
|
|
|
177
180
|
|
|
178
181
|
这些限制说明会话存储何时需要特别留意。它们是当前包约束,不是任务积压。
|
|
179
182
|
|
|
180
|
-
- **`fork()` 仅在实时会话的稳定边界处切分**:所选前缀结束时不得有开放轮次,且源会话必须位于存储中;
|
|
181
|
-
- **`SESSION_FORMAT_VERSION`
|
|
183
|
+
- **`fork()` 仅在实时会话的稳定边界处切分**:所选前缀结束时不得有开放轮次,且源会话必须位于存储中;fork API 不支持对已持久化但未加载的会话进行 fork。
|
|
184
|
+
- **`SESSION_FORMAT_VERSION` 命名当前 V3 逻辑表示**——V3 读取器拒绝已退役的 `header.system`,并校验 `system/message` 载荷与受保护头节点的重写。历史 header 与事件归相邻格式包所有;V2→V3 迁移边在构造 `Session` 前转换受支持的历史,写打开只发布 V3 后继代际。同版本未知事件要求信封显式带有 `ignorable` 标记,但这不保证结构迁移的安全性([机制](../../../.agents/notes/implemented/architecture/2026-08-31-released-session-format-migrations.zh.md))。
|
|
182
185
|
- **`TurnEndReasonMap` 不含 ACP(Agent Client Protocol)命名的 `refusal`/`max_turn_requests` 变体**:受生产方约束;只有当适配器或循环首次产生这些变体时才加入。
|
|
183
186
|
- **fork 之外没有会话树**:基于分支会话的 pi 风格条目树被推迟,除非消费方需要超越基于边界的 forking 的能力。
|
|
184
187
|
|