@deepseek-ai/dsh-session-format 0.1.3-alpha.2

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 DeepSeek
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,6 @@
1
+ # Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each
2
+ # side as of the last confirmed-consistent state. Both languages carry equal authority;
3
+ # after editing either side, bring the other along and re-record with:
4
+ # pnpm run verify-translation-pairing --write packages/session/session-format/README.md
5
+ README.md: d44a7091bc5840afcb5d1e73f594284c3d5c8206
6
+ README.zh.md: f4b6acd8e0ba3c28a1b5088fc577e1b4289ca9a0
package/README.md ADDED
@@ -0,0 +1,112 @@
1
+ ---
2
+ description: "Pure adjacent Session format planning, lossless JSON value checks, header-only migration, and physical codec dispatch."
3
+ kind: "package-library"
4
+ ---
5
+
6
+ # @deepseek-ai/dsh-session-format
7
+
8
+ English | [中文](README.zh.md)
9
+
10
+ ## Summary
11
+
12
+ `dsh-session-format` lets persistence code restore a current Session directly or compose a unique sequence of adjacent migrations while consuming physical rows once. A restore transfers caller-owned parsed values through stateful stages without intermediate artifact copies or freezing. Physical framing, compression, immutable generation naming, exclusive publication, and Cordis lifecycle behavior remain outside this library.
13
+
14
+ ## Table of Contents
15
+
16
+ - [Use this package](#use-this-package)
17
+ - [Understand the implementation](#understand-the-implementation)
18
+ - [Further Exploration](#further-exploration)
19
+ - [Model Experience](#model-experience)
20
+ - [Known Limitations and Deferred Work](#known-limitations-and-deferred-work)
21
+ - [Dev Note](#dev-note)
22
+
23
+ -----
24
+
25
+ <a id="use-this-package"></a>
26
+ ## Use this package
27
+
28
+ ### When to use it
29
+
30
+ Use this library from persistence or format-catalog code that must classify a physical Session header, restore current logical values, or compose released adjacent migrations. It is not a Cordis plugin and has no profile mount row. No runtime invariant companion is published because each completed operation validates its result; decoder and transformer state belongs to one unfinished streaming restore and is never shared across restores.
31
+
32
+ ### Entry point
33
+
34
+ ```text
35
+ const catalog = createSessionFormatCatalog({ currentVersion, codecs, currentEncoder, migrations, restoreCurrent, restoreTransformedCurrent, restoreCurrentHeader })
36
+ const descriptor = catalog.readHeader(physicalHeader)
37
+ const restore = catalog.createRestore(physicalHeader, { recovery: 'recoverable', validation: 'transformed' })
38
+ for (const row of physicalRows) restore.decodeRow(row)
39
+ const current = restore.finish()
40
+ const headerRecord = catalog.encodeCurrentHeader(current.header, current.inheritedEventCount)
41
+ const eventRecords = current.events.map(catalog.encodeCurrentEvent)
42
+ ```
43
+
44
+ `createSessionFormatCatalog()` accepts one frozen codec per supported version, the current record encoder, one migration per adjacent version pair, and current artifact and header restorers. `readHeader()` returns a `current`, `migration-required`, `unsupported`, or `malformed` descriptor without reading events. Body readers create one restore, push each parsed physical row through `decodeRow()`, and call `finish()` once for a current artifact. Writers encode its header and events record by record.
45
+
46
+ The `recovery` option selects strict row failure or recoverable suffix handling. `validation: 'current'` applies all installed current-format validation. `validation: 'transformed'` applies released current-format validation after historical migration, while already-current input receives only its codec's physical validation.
47
+
48
+ The recoverable decoder returns the accepted logical prefix. A codec may drop one malformed or sequence-gapped row and its uncommitted suffix, but a later decoded `turn/end` makes the original issue fatal.
49
+
50
+ -----
51
+
52
+ <a id="understand-the-implementation"></a>
53
+ ## Understand the implementation
54
+
55
+ <details>
56
+ <summary>Implementation internals — click to expand</summary>
57
+
58
+ The chain validates unique gap-free ordering at construction. The catalog composes one row decoder with stateful adjacent event transformers, retains only their bounded state and the final current events, and performs target validation at `finish()`; only the caller decides whether and how to publish that result.
59
+
60
+ | File | Role |
61
+ |---|---|
62
+ | [`src/chain.ts`](src/chain.ts) | Adjacent plan construction and current bypass |
63
+ | [`src/catalog.ts`](src/catalog.ts) | Physical version dispatch and header classification |
64
+ | [`src/json.ts`](src/json.ts) | Detached lossless JSON snapshots and common coordinate checks |
65
+ | [`src/filename.ts`](src/filename.ts) | Canonical `session[.vN].jsonl` basename shared by persistence, export, and fixtures |
66
+
67
+ </details>
68
+
69
+ -----
70
+
71
+ <a id="further-exploration"></a>
72
+ ## Further Exploration
73
+
74
+ - [Released v0 to v1 edge](../session-format-v0-to-v1/README.md) — frozen historical decoding and identity conversion.
75
+ - [Static catalog](../session-format-catalog/README.md) — first-party codec and migration assembly.
76
+ - [JSONL persistence](../session-persistence-jsonl/README.md) — durable framing and generation publication.
77
+
78
+ -----
79
+
80
+ <a id="model-experience"></a>
81
+ ## Model Experience
82
+
83
+ ### Session restoration
84
+
85
+ #### What the model sees
86
+
87
+ Nothing directly. Consumers reconstruct model history from the validated current artifact through `deriveMessages()`.
88
+
89
+ #### Token effect
90
+
91
+ Zero direct tokens.
92
+
93
+ #### KV Cache effect
94
+
95
+ No direct effect. A migration that changes current history can change the cache identity owned by request reconstruction.
96
+
97
+ ## Known Limitations and Deferred Work
98
+
99
+ <a id="known-limitations-and-deferred-work"></a>
100
+
101
+ - **Final current history remains resident** — streaming retains only bounded intermediate state, but the returned current event array and any required sequence-remap table remain O(event count).
102
+ - **Adjacent integer versions only** — the library does not expose spans, stable event identities, or a general reference-rewrite algebra.
103
+
104
+ <a id="dev-note"></a>
105
+ ### Dev Note
106
+
107
+ <details>
108
+ <summary>Working context for maintainers — click to expand</summary>
109
+
110
+ None.
111
+
112
+ </details>
package/README.zh.md ADDED
@@ -0,0 +1,112 @@
1
+ ---
2
+ description: "纯函数式相邻 Session 格式规划、无损 JSON 值检查、仅标头迁移与物理编解码分派。"
3
+ kind: "package-library"
4
+ ---
5
+
6
+ # @deepseek-ai/dsh-session-format
7
+
8
+ [English](README.md) | 中文
9
+
10
+ ## 概述
11
+
12
+ `dsh-session-format` 让持久化代码可以直接还原当前 Session,或在只消费一次物理行的同时组合唯一的相邻迁移序列。一次还原会让调用方拥有的已解析值流经有状态 Stage,不复制或冻结中间 artifact。物理分帧、压缩、不可变 generation 命名、排他发布和 Cordis 生命周期行为不属于本库。
13
+
14
+ ## 目录
15
+
16
+ - [使用本包](#use-this-package)
17
+ - [理解实现](#understand-the-implementation)
18
+ - [进一步探索](#further-exploration)
19
+ - [模型体验](#model-experience)
20
+ - [已知限制与延期工作](#known-limitations-and-deferred-work)
21
+ - [开发备注](#dev-note)
22
+
23
+ -----
24
+
25
+ <a id="use-this-package"></a>
26
+ ## 使用本包
27
+
28
+ ### 何时使用
29
+
30
+ 当持久化或格式目录代码需要分类物理 Session header、还原当前逻辑值或组合已发布相邻迁移时,使用本库。它不是 Cordis 插件,也没有 profile 挂载行。它不发布运行时不变式伴生入口,因为每个已完成操作都会校验结果;decoder 与 transformer 状态只属于一次尚未完成的流式还原,绝不在多次还原间共享。
31
+
32
+ ### 入口
33
+
34
+ ```text
35
+ const catalog = createSessionFormatCatalog({ currentVersion, codecs, currentEncoder, migrations, restoreCurrent, restoreTransformedCurrent, restoreCurrentHeader })
36
+ const descriptor = catalog.readHeader(physicalHeader)
37
+ const restore = catalog.createRestore(physicalHeader, { recovery: 'recoverable', validation: 'transformed' })
38
+ for (const row of physicalRows) restore.decodeRow(row)
39
+ const current = restore.finish()
40
+ const headerRecord = catalog.encodeCurrentHeader(current.header, current.inheritedEventCount)
41
+ const eventRecords = current.events.map(catalog.encodeCurrentEvent)
42
+ ```
43
+
44
+ `createSessionFormatCatalog()` 接收每个受支持版本的一个冻结 codec、当前格式的逐记录 encoder、每组相邻版本的一个迁移,以及当前 artifact 与 header 还原器。`readHeader()` 在不读取事件的情况下返回 `current`、`migration-required`、`unsupported` 或 `malformed` 描述符。正文读取方创建一次 restore,把每个已解析物理行传给 `decodeRow()`,再调用一次 `finish()` 获得当前 artifact。写入方逐条编码其 header 与事件。
45
+
46
+ `recovery` 选项决定严格拒绝故障行,还是执行可恢复后缀处理。`validation: 'current'` 会执行已安装 current 格式的全部校验。`validation: 'transformed'` 会在历史迁移后执行已发布 current 格式校验;已经是 current 的输入则只接受其 codec 的物理校验。
47
+
48
+ 可恢复解码器返回已接受的逻辑前缀。编解码器可以丢弃一个格式错误或序号不连续的行及其未提交后缀,但后续成功解码的 `turn/end` 会使原始问题成为致命错误。
49
+
50
+ -----
51
+
52
+ <a id="understand-the-implementation"></a>
53
+ ## 理解实现
54
+
55
+ <details>
56
+ <summary>实现细节——点击展开</summary>
57
+
58
+ 迁移链在构造时校验唯一且无缺口的顺序。Catalog 把一个行 decoder 与有状态的相邻事件 transformer 组合起来,只保留其有界状态与最终当前事件,并在 `finish()` 时执行目标校验;只有调用方决定是否发布该结果以及如何发布。
59
+
60
+ | 文件 | 职责 |
61
+ |---|---|
62
+ | [`src/chain.ts`](src/chain.ts) | 相邻计划构造与当前格式绕过 |
63
+ | [`src/catalog.ts`](src/catalog.ts) | 物理版本分派与标头分类 |
64
+ | [`src/json.ts`](src/json.ts) | 分离的无损 JSON 快照与通用坐标校验 |
65
+ | [`src/filename.ts`](src/filename.ts) | 持久化、导出与 fixture 共用的规范 `session[.vN].jsonl` 文件名 |
66
+
67
+ </details>
68
+
69
+ -----
70
+
71
+ <a id="further-exploration"></a>
72
+ ## 进一步探索
73
+
74
+ - [已发布 v0 到 v1 迁移边](../session-format-v0-to-v1/README.zh.md)——冻结的历史解码与恒等转换。
75
+ - [静态目录](../session-format-catalog/README.zh.md)——第一方编解码器与迁移装配。
76
+ - [JSONL 持久化](../session-persistence-jsonl/README.zh.md)——持久化分帧与代际发布。
77
+
78
+ -----
79
+
80
+ <a id="model-experience"></a>
81
+ ## 模型体验
82
+
83
+ ### Session 还原
84
+
85
+ #### 模型看到什么
86
+
87
+ 没有直接内容。消费方通过 `deriveMessages()` 从经过校验的当前产物重建模型历史。
88
+
89
+ #### Token 影响
90
+
91
+ 不直接产生 token。
92
+
93
+ #### KV Cache 影响
94
+
95
+ 没有直接影响。迁移若改变当前历史,可能改变由请求重建逻辑拥有的缓存身份。
96
+
97
+ ## 已知限制与延期工作
98
+
99
+ <a id="known-limitations-and-deferred-work"></a>
100
+
101
+ - **最终当前历史仍常驻内存**——流式处理只保留有界中间状态,但返回的当前事件数组和必需的序号重映射表仍为 O(事件数)。
102
+ - **仅支持相邻整数版本**——本库不暴露 span、稳定事件身份或通用引用重写代数。
103
+
104
+ <a id="dev-note"></a>
105
+ ### 开发备注
106
+
107
+ <details>
108
+ <summary>维护者的工作上下文——点击展开</summary>
109
+
110
+ 无。
111
+
112
+ </details>
package/lib/index.js ADDED
@@ -0,0 +1,499 @@
1
+ import { deepFreeze, snapshotJsonValue } from "@deepseek-ai/dsh-util-values";
2
+ //#region lib/types/error.js
3
+ /** Error raised when a durable Session artifact cannot be restored or migrated losslessly. */
4
+ var SessionFormatError = class extends Error {
5
+ name = "SessionFormatError";
6
+ };
7
+ /** A readable artifact whose released source policy has no supported migration. */
8
+ var SessionFormatUnsupportedMigrationError = class extends SessionFormatError {
9
+ name = "SessionFormatUnsupportedMigrationError";
10
+ };
11
+ //#endregion
12
+ //#region lib/types/json.js
13
+ /**
14
+ * Test whether a value is a non-null, non-array object.
15
+ * @param value - candidate value.
16
+ * @returns whether the value is an object record.
17
+ */
18
+ function isSessionFormatJsonObject(value) {
19
+ return typeof value === "object" && value !== null && !Array.isArray(value);
20
+ }
21
+ /**
22
+ * Require a non-negative safe integer without the JSON-unstable negative zero.
23
+ * @param value - candidate count.
24
+ * @param label - diagnostic subject.
25
+ * @returns validated count.
26
+ */
27
+ function sessionFormatCount(value, label) {
28
+ if (!Number.isSafeInteger(value) || value < 0 || Object.is(value, -0)) throw new SessionFormatError(`${label} must be a non-negative safe integer`);
29
+ return value;
30
+ }
31
+ /**
32
+ * Require a safe integer without the JSON-unstable negative zero.
33
+ * @param value - candidate integer.
34
+ * @param label - diagnostic subject.
35
+ * @returns validated integer.
36
+ */
37
+ function sessionFormatSafeInteger(value, label) {
38
+ if (!Number.isSafeInteger(value) || Object.is(value, -0)) throw new SessionFormatError(`${label} must be a safe integer`);
39
+ return value;
40
+ }
41
+ /**
42
+ * Require a non-negative integral format version.
43
+ * @param value - candidate version.
44
+ * @param label - diagnostic subject.
45
+ * @returns validated version.
46
+ */
47
+ function sessionFormatVersion(value, label = "Session format version") {
48
+ return sessionFormatCount(value, label);
49
+ }
50
+ /**
51
+ * Read only the version required for directional dispatch.
52
+ * @param headerValue - untrusted physical header value.
53
+ * @returns validated stored version.
54
+ */
55
+ function inspectSessionFormatVersion(headerValue) {
56
+ if (!isSessionFormatJsonObject(headerValue)) throw new SessionFormatError("Session header must be a JSON object");
57
+ return sessionFormatVersion(headerValue["version"]);
58
+ }
59
+ /**
60
+ * Detach and deeply freeze a caller-supplied lossless JSON value.
61
+ * @param value - borrowed candidate.
62
+ * @param label - diagnostic subject.
63
+ * @returns an immutable detached JSON snapshot.
64
+ */
65
+ function snapshotSessionFormatJson(value, label = "Session value") {
66
+ const snapshot = snapshotJsonValue(value);
67
+ if (snapshot === void 0) throw new SessionFormatError(`${label} is not lossless JSON`);
68
+ return deepFreeze(snapshot);
69
+ }
70
+ /**
71
+ * Snapshot one logical header without inspecting an event body.
72
+ * @param header - borrowed logical header.
73
+ * @param label - diagnostic subject.
74
+ * @returns immutable detached header.
75
+ */
76
+ function snapshotSessionFormatHeader(header, label = "Session header") {
77
+ const snapshot = snapshotSessionFormatJson(header, label);
78
+ if (!isSessionFormatJsonObject(snapshot)) throw new SessionFormatError(`${label} must be a JSON object`);
79
+ inspectSessionFormatVersion(snapshot);
80
+ if (typeof snapshot["id"] !== "string") throw new SessionFormatError(`${label} id must be a string`);
81
+ sessionFormatCount(snapshot["createdAt"], `${label} createdAt`);
82
+ if (typeof snapshot["isSeeded"] !== "boolean") throw new SessionFormatError(`${label} isSeeded must be a boolean`);
83
+ sessionFormatCount(snapshot["delegationDepth"], `${label} delegationDepth`);
84
+ return snapshot;
85
+ }
86
+ //#endregion
87
+ //#region lib/types/chain.js
88
+ /**
89
+ * Validate and freeze one adjacent migration declaration.
90
+ * @param migration - named exact adjacent conversion.
91
+ * @returns immutable validated declaration.
92
+ */
93
+ function defineSessionFormatMigration(migration) {
94
+ if (typeof migration.name !== "string" || migration.name.length === 0) throw new SessionFormatError("Session migration name must be a non-empty string");
95
+ const from = sessionFormatVersion(migration.fromVersion, `${migration.name} fromVersion`);
96
+ if (sessionFormatVersion(migration.toVersion, `${migration.name} toVersion`) !== from + 1) throw new SessionFormatError(`${migration.name} must declare adjacent v${from}->v${from + 1}`);
97
+ return Object.freeze({ ...migration });
98
+ }
99
+ /**
100
+ * Compile a unique, complete adjacent migration chain.
101
+ * @param options - current version, adjacent declarations, and current restorer.
102
+ * @returns immutable planner and streaming migration compiler.
103
+ */
104
+ function createSessionFormatChain(options) {
105
+ return new CompiledSessionFormatChain(options);
106
+ }
107
+ var CompiledSessionFormatChain = class {
108
+ currentVersion;
109
+ migrations;
110
+ restoreCurrentHeader;
111
+ constructor(options) {
112
+ this.currentVersion = sessionFormatVersion(options.currentVersion, "current Session format version");
113
+ this.restoreCurrentHeader = options.restoreCurrentHeader;
114
+ const byFrom = /* @__PURE__ */ new Map();
115
+ const names = /* @__PURE__ */ new Set();
116
+ for (const candidate of options.migrations) {
117
+ const migration = defineSessionFormatMigration(candidate);
118
+ if (byFrom.has(migration.fromVersion)) throw new SessionFormatError(`Session migration v${migration.fromVersion}->v${migration.toVersion} is duplicated`);
119
+ if (names.has(migration.name)) throw new SessionFormatError(`Session migration name ${JSON.stringify(migration.name)} is duplicated`);
120
+ byFrom.set(migration.fromVersion, migration);
121
+ names.add(migration.name);
122
+ }
123
+ const ordered = [];
124
+ for (let version = 0; version < this.currentVersion; version += 1) {
125
+ const migration = byFrom.get(version);
126
+ if (migration === void 0) throw new SessionFormatUnsupportedMigrationError(`Session migration v${version}->v${version + 1} is missing`);
127
+ ordered.push(migration);
128
+ }
129
+ if (byFrom.size !== ordered.length) throw new SessionFormatError(`Session migration from v${[...byFrom.keys()].find((version) => version >= this.currentVersion)} does not lead to current v${this.currentVersion}`);
130
+ this.migrations = Object.freeze(ordered);
131
+ }
132
+ plan(fromVersion) {
133
+ const from = sessionFormatVersion(fromVersion, "stored Session format version");
134
+ if (from > this.currentVersion) throw new SessionFormatUnsupportedMigrationError(`stored Session uses newer format v${from}; this build writes v${this.currentVersion}`);
135
+ return Object.freeze(this.migrations.slice(from));
136
+ }
137
+ createStream(sourceHeader, sourceCut, output) {
138
+ let header = sourceHeader;
139
+ const validatedSourceCut = sessionFormatCount(sourceCut, "Session inherited event count");
140
+ let inheritedEventCount = validatedSourceCut;
141
+ const stages = [];
142
+ const plan = this.plan(header.version);
143
+ for (const [index, migration] of plan.entries()) {
144
+ const targetHeader = this.advanceHeader(migration, header);
145
+ let stage;
146
+ try {
147
+ stage = migration.createStage({
148
+ sourceHeader: header,
149
+ targetHeader,
150
+ sourceInheritedEventCount: inheritedEventCount,
151
+ sourceKind: index === 0 ? "decoded" : "transformed"
152
+ });
153
+ } catch (error) {
154
+ throwUnsupportedRefusal(migration, error);
155
+ }
156
+ header = targetHeader;
157
+ stages.push({
158
+ migration,
159
+ stage
160
+ });
161
+ if (index + 1 < plan.length) {
162
+ const targetCut = stage.headerInheritedEventCount;
163
+ if (targetCut === void 0) throw new SessionFormatError(`${migration.name} must expose its inherited cut before the next migration`);
164
+ inheritedEventCount = targetCut;
165
+ }
166
+ }
167
+ return new CompiledSessionFormatMigrationStream(header, validatedSourceCut, stages, output);
168
+ }
169
+ migrateHeader(source) {
170
+ let current = snapshotSessionFormatHeader(source, "stored Session header");
171
+ for (const migration of this.plan(current.version)) current = this.advanceHeader(migration, current);
172
+ current = snapshotSessionFormatHeader(this.restoreCurrentHeader(current), "current Session header restoration");
173
+ if (current.version !== this.currentVersion) throw new SessionFormatError(`current Session header restorer returned v${current.version}; expected v${this.currentVersion}`);
174
+ return current;
175
+ }
176
+ advanceHeader(migration, source) {
177
+ let target;
178
+ try {
179
+ target = migration.migrateHeader(snapshotSessionFormatHeader(source, `${migration.name} header input`));
180
+ } catch (error) {
181
+ throwUnsupportedRefusal(migration, error, "Session header");
182
+ }
183
+ const current = snapshotSessionFormatHeader(target, `${migration.name} header output`);
184
+ if (current.version !== migration.toVersion) throw new SessionFormatError(`${migration.name} header returned v${current.version}; expected v${migration.toVersion}`);
185
+ try {
186
+ migration.validateTargetHeader(current);
187
+ } catch (error) {
188
+ throwUnsupportedRefusal(migration, error, "Session header");
189
+ }
190
+ return current;
191
+ }
192
+ };
193
+ var ChainedMigrationContext = class {
194
+ entry;
195
+ output;
196
+ constructor(entry, output) {
197
+ this.entry = entry;
198
+ this.output = output;
199
+ }
200
+ emitEvent(event) {
201
+ try {
202
+ this.entry.stage.transformEvent(event, this.output);
203
+ } catch (error) {
204
+ throwUnsupportedRefusal(this.entry.migration, error);
205
+ }
206
+ }
207
+ emitRun(run) {
208
+ try {
209
+ this.entry.stage.transformRun(run, this.output);
210
+ } catch (error) {
211
+ throwUnsupportedRefusal(this.entry.migration, error);
212
+ }
213
+ }
214
+ finish() {
215
+ let targetCut;
216
+ try {
217
+ targetCut = this.entry.stage.finish(this.output);
218
+ } catch (error) {
219
+ throwUnsupportedRefusal(this.entry.migration, error);
220
+ }
221
+ if (this.entry.stage.headerInheritedEventCount !== void 0 && this.entry.stage.headerInheritedEventCount !== targetCut) throw new SessionFormatError(`${this.entry.migration.name} changed its predeclared inherited cut`);
222
+ return targetCut;
223
+ }
224
+ };
225
+ var CompiledSessionFormatMigrationStream = class {
226
+ header;
227
+ sourceInheritedEventCount;
228
+ input;
229
+ stages;
230
+ constructor(header, sourceInheritedEventCount, entries, output) {
231
+ this.header = header;
232
+ this.sourceInheritedEventCount = sourceInheritedEventCount;
233
+ const stages = new Array(entries.length);
234
+ let downstream = output;
235
+ for (const [offset, entry] of entries.toReversed().entries()) {
236
+ const context = new ChainedMigrationContext(entry, downstream);
237
+ stages[entries.length - offset - 1] = context;
238
+ downstream = context;
239
+ }
240
+ this.input = downstream;
241
+ this.stages = stages;
242
+ }
243
+ emitEvent(event) {
244
+ this.input.emitEvent(event);
245
+ }
246
+ emitRun(run) {
247
+ this.input.emitRun(run);
248
+ }
249
+ finish() {
250
+ let inheritedEventCount = this.sourceInheritedEventCount;
251
+ for (const stage of this.stages) inheritedEventCount = stage.finish();
252
+ return inheritedEventCount;
253
+ }
254
+ };
255
+ function throwUnsupportedRefusal(migration, error, subject = "Session") {
256
+ if (error instanceof SessionFormatUnsupportedMigrationError) throw error;
257
+ const detail = error instanceof Error ? error.message : String(error);
258
+ throw new SessionFormatUnsupportedMigrationError(`${migration.name} refuses this format v${migration.fromVersion} ${subject}: ${detail}`, { cause: error });
259
+ }
260
+ //#endregion
261
+ //#region lib/types/context.js
262
+ /** Migration output context that expands compact runs into retained events. */
263
+ var SessionFormatEventCollector = class {
264
+ /** Events retained by this collector in source order. */
265
+ values = [];
266
+ /**
267
+ * Retain one settled event.
268
+ * @param event - settled event emitted by the upstream stage.
269
+ */
270
+ emitEvent(event) {
271
+ this.values.push(event);
272
+ }
273
+ /**
274
+ * Expand one compact run directly into retained events.
275
+ * @param run - compact event run emitted by the upstream stage.
276
+ */
277
+ emitRun(run) {
278
+ for (const event of run.expand()) this.values.push(event);
279
+ }
280
+ };
281
+ //#endregion
282
+ //#region lib/types/catalog.js
283
+ /**
284
+ * Compile a build-static physical codec and adjacent migration catalog.
285
+ * @param options - complete codecs, migrations, current version, and restorer.
286
+ * @returns immutable physical dispatch and migration operations.
287
+ */
288
+ function createSessionFormatCatalog(options) {
289
+ const chain = createSessionFormatChain(options);
290
+ const codecs = /* @__PURE__ */ new Map();
291
+ for (const codec of options.codecs) {
292
+ const version = sessionFormatVersion(codec.version, "Session format codec version");
293
+ if (codecs.has(version)) throw new SessionFormatError(`Session format codec v${version} is duplicated`);
294
+ codecs.set(version, Object.freeze({ ...codec }));
295
+ }
296
+ for (let version = 0; version <= chain.currentVersion; version += 1) if (!codecs.has(version)) throw new SessionFormatError(`Session format codec v${version} is missing`);
297
+ if (codecs.size !== chain.currentVersion + 1) throw new SessionFormatError(`Session format codec v${[...codecs.keys()].find((version) => version > chain.currentVersion)} is newer than current v${chain.currentVersion}`);
298
+ function readHeader(headerValue) {
299
+ let storedVersion;
300
+ try {
301
+ storedVersion = inspectSessionFormatVersion(headerValue);
302
+ } catch (error) {
303
+ return malformed(chain.currentVersion, error);
304
+ }
305
+ if (storedVersion > chain.currentVersion) return Object.freeze({
306
+ status: "unsupported",
307
+ storedVersion,
308
+ targetVersion: chain.currentVersion,
309
+ reason: `stored Session uses newer format v${storedVersion}; this build writes v${chain.currentVersion}`
310
+ });
311
+ const codec = codecs.get(storedVersion);
312
+ /* v8 ignore next -- construction proves every supported version has exactly one codec. */
313
+ if (codec === void 0) return Object.freeze({
314
+ status: "unsupported",
315
+ storedVersion,
316
+ targetVersion: chain.currentVersion,
317
+ reason: `this build has no Session format codec for v${storedVersion}`
318
+ });
319
+ try {
320
+ const decoded = snapshotSessionFormatHeader(codec.decodeHeader(headerValue), `format v${storedVersion} header`);
321
+ const header = chain.migrateHeader(decoded);
322
+ return Object.freeze({
323
+ status: storedVersion === chain.currentVersion ? "current" : "migration-required",
324
+ storedVersion,
325
+ targetVersion: chain.currentVersion,
326
+ header
327
+ });
328
+ } catch (error) {
329
+ if (error instanceof SessionFormatUnsupportedMigrationError) return Object.freeze({
330
+ status: "unsupported",
331
+ storedVersion,
332
+ targetVersion: chain.currentVersion,
333
+ reason: error.message
334
+ });
335
+ return malformed(chain.currentVersion, error, storedVersion);
336
+ }
337
+ }
338
+ function artifactCodec(headerValue) {
339
+ const storedVersion = inspectSessionFormatVersion(headerValue);
340
+ if (storedVersion > chain.currentVersion) throw new SessionFormatUnsupportedMigrationError(`stored Session uses newer format v${storedVersion}; this build writes v${chain.currentVersion}`);
341
+ const codec = codecs.get(storedVersion);
342
+ /* v8 ignore next -- construction proves every supported version has exactly one codec. */
343
+ if (codec === void 0) throw new SessionFormatUnsupportedMigrationError(`this build has no Session format codec for v${storedVersion}`);
344
+ return {
345
+ storedVersion,
346
+ codec
347
+ };
348
+ }
349
+ function encodeCurrentHeader(header, inheritedEventCount) {
350
+ if (inspectSessionFormatVersion(header) !== chain.currentVersion) throw new SessionFormatError(`encodeCurrent requires Session format v${chain.currentVersion}`);
351
+ const encoded = options.currentEncoder.encodeHeader(header, inheritedEventCount);
352
+ if (inspectSessionFormatVersion(encoded) !== chain.currentVersion) throw new SessionFormatError("current Session codec returned a non-current header");
353
+ return encoded;
354
+ }
355
+ function createRestore(headerValue, restoreOptions) {
356
+ const { storedVersion, codec } = artifactCodec(headerValue);
357
+ const decoder = codec.createDecoder(headerValue, restoreOptions.recovery);
358
+ const sourceCut = decoder.headerInheritedEventCount;
359
+ if (storedVersion === chain.currentVersion) return new CurrentSessionFormatRestore(decoder, sourceCut, restoreOptions.validation === "current" ? options.restoreCurrent : identityArtifact, chain.currentVersion);
360
+ const collector = new SessionFormatEventCollector();
361
+ return new MigratingSessionFormatRestore(decoder, sourceCut, chain.createStream(decoder.header, requiredHistoricalCut(storedVersion, sourceCut), collector), collector, restoreOptions.validation === "current" ? options.restoreCurrent : options.restoreTransformedCurrent, restoreOptions.validation, storedVersion, chain.currentVersion);
362
+ }
363
+ return Object.freeze({
364
+ currentVersion: chain.currentVersion,
365
+ readHeader,
366
+ createRestore,
367
+ encodeCurrentHeader,
368
+ encodeCurrentEvent: options.currentEncoder.encodeEvent.bind(options.currentEncoder)
369
+ });
370
+ }
371
+ var CurrentSessionFormatRestore = class {
372
+ decoder;
373
+ sourceInheritedEventCount;
374
+ restoreArtifact;
375
+ currentVersion;
376
+ header;
377
+ collector = new SessionFormatEventCollector();
378
+ constructor(decoder, sourceInheritedEventCount, restoreArtifact, currentVersion) {
379
+ this.decoder = decoder;
380
+ this.sourceInheritedEventCount = sourceInheritedEventCount;
381
+ this.restoreArtifact = restoreArtifact;
382
+ this.currentVersion = currentVersion;
383
+ this.header = decoder.header;
384
+ }
385
+ decodeRow(rowValue) {
386
+ this.decoder.decodeRow(rowValue, this.collector);
387
+ }
388
+ finish() {
389
+ const inheritedEventCount = finishDecoder(this.decoder, this.collector, this.sourceInheritedEventCount);
390
+ return restoreCurrentVersion(this.restoreArtifact({
391
+ header: this.header,
392
+ inheritedEventCount,
393
+ events: this.collector.values
394
+ }), this.currentVersion);
395
+ }
396
+ };
397
+ var MigratingSessionFormatRestore = class {
398
+ decoder;
399
+ sourceInheritedEventCount;
400
+ migration;
401
+ collector;
402
+ restoreArtifact;
403
+ validation;
404
+ sourceVersion;
405
+ currentVersion;
406
+ header;
407
+ constructor(decoder, sourceInheritedEventCount, migration, collector, restoreArtifact, validation, sourceVersion, currentVersion) {
408
+ this.decoder = decoder;
409
+ this.sourceInheritedEventCount = sourceInheritedEventCount;
410
+ this.migration = migration;
411
+ this.collector = collector;
412
+ this.restoreArtifact = restoreArtifact;
413
+ this.validation = validation;
414
+ this.sourceVersion = sourceVersion;
415
+ this.currentVersion = currentVersion;
416
+ this.header = migration.header;
417
+ }
418
+ decodeRow(rowValue) {
419
+ this.decoder.decodeRow(rowValue, this);
420
+ }
421
+ emitEvent(event) {
422
+ this.migration.emitEvent(event);
423
+ }
424
+ emitRun(run) {
425
+ this.migration.emitRun(run);
426
+ }
427
+ finish() {
428
+ finishDecoder(this.decoder, this, this.sourceInheritedEventCount);
429
+ const artifact = {
430
+ header: this.header,
431
+ inheritedEventCount: this.migration.finish(),
432
+ events: this.collector.values
433
+ };
434
+ let restored;
435
+ try {
436
+ restored = this.restoreArtifact(artifact);
437
+ } catch (error) {
438
+ if (this.validation === "current" || error instanceof SessionFormatUnsupportedMigrationError) throw error;
439
+ const detail = error instanceof Error ? error.message : String(error);
440
+ throw new SessionFormatUnsupportedMigrationError(`Session migration from v${this.sourceVersion} to v${this.currentVersion} refuses the transformed artifact: ${detail}`, { cause: error });
441
+ }
442
+ return restoreCurrentVersion(restored, this.currentVersion);
443
+ }
444
+ };
445
+ function finishDecoder(decoder, context, sourceInheritedEventCount) {
446
+ const inheritedEventCount = decoder.finish(context);
447
+ if (sourceInheritedEventCount !== void 0 && inheritedEventCount !== sourceInheritedEventCount) throw new SessionFormatError("streaming decoder changed its predeclared inherited cut");
448
+ return inheritedEventCount;
449
+ }
450
+ function restoreCurrentVersion(artifact, currentVersion) {
451
+ if (artifact.header.version !== currentVersion) throw new SessionFormatError(`current Session restorer returned v${artifact.header.version}; expected v${currentVersion}`);
452
+ return artifact;
453
+ }
454
+ function identityArtifact(artifact) {
455
+ return artifact;
456
+ }
457
+ function requiredHistoricalCut(version, cut) {
458
+ if (cut === void 0) throw new SessionFormatError(`format v${version} decoder must expose its inherited cut before migration`);
459
+ return cut;
460
+ }
461
+ function malformed(targetVersion, error, storedVersion) {
462
+ return Object.freeze({
463
+ status: "malformed",
464
+ ...storedVersion === void 0 ? {} : { storedVersion },
465
+ targetVersion,
466
+ reason: error instanceof Error ? error.message : String(error)
467
+ });
468
+ }
469
+ //#endregion
470
+ //#region lib/types/filename.js
471
+ /** Canonical raw log basename shared by every generation-addressed Session artifact. */
472
+ const CANONICAL_LOG_FILENAME = /^session(?:\.v([1-9][0-9]*))?\.jsonl$/u;
473
+ /**
474
+ * Name the raw JSONL log of one immutable Session format generation. Version
475
+ * zero keeps the original `session.jsonl`; every later generation carries a
476
+ * lowercase numeric `.vN` component before the `.jsonl` suffix.
477
+ * @param version - non-negative safe integer Session format version.
478
+ * @returns the canonical basename, without any compression suffix.
479
+ */
480
+ function sessionFormatLogFilename(version) {
481
+ const generation = sessionFormatVersion(version, "Session log generation version");
482
+ return generation === 0 ? "session.jsonl" : `session.v${generation}.jsonl`;
483
+ }
484
+ /**
485
+ * Read the generation named by one raw JSONL log basename. Temporary,
486
+ * uppercase, leading-zero, `.v0`, and compression-suffixed names are not
487
+ * canonical.
488
+ * @param filename - one basename from a Session directory or archive.
489
+ * @returns its Session format version, or `undefined` when the name is not canonical.
490
+ */
491
+ function parseSessionFormatLogFilename(filename) {
492
+ const match = CANONICAL_LOG_FILENAME.exec(filename);
493
+ if (match === null) return void 0;
494
+ if (match[1] === void 0) return 0;
495
+ const version = Number(match[1]);
496
+ return Number.isSafeInteger(version) ? version : void 0;
497
+ }
498
+ //#endregion
499
+ export { SessionFormatError, SessionFormatEventCollector, SessionFormatUnsupportedMigrationError, createSessionFormatCatalog, createSessionFormatChain, defineSessionFormatMigration, inspectSessionFormatVersion, isSessionFormatJsonObject, parseSessionFormatLogFilename, sessionFormatCount, sessionFormatLogFilename, sessionFormatSafeInteger, sessionFormatVersion, snapshotSessionFormatHeader, snapshotSessionFormatJson };
@@ -0,0 +1,8 @@
1
+ import type { SessionFormatCatalog, SessionFormatCatalogOptions } from './types.ts';
2
+ /**
3
+ * Compile a build-static physical codec and adjacent migration catalog.
4
+ * @param options - complete codecs, migrations, current version, and restorer.
5
+ * @returns immutable physical dispatch and migration operations.
6
+ */
7
+ export declare function createSessionFormatCatalog(options: SessionFormatCatalogOptions): SessionFormatCatalog;
8
+ //# sourceMappingURL=catalog.d.ts.map
@@ -0,0 +1,14 @@
1
+ import type { SessionFormatChain, SessionFormatChainOptions, SessionFormatMigration } from './types.ts';
2
+ /**
3
+ * Validate and freeze one adjacent migration declaration.
4
+ * @param migration - named exact adjacent conversion.
5
+ * @returns immutable validated declaration.
6
+ */
7
+ export declare function defineSessionFormatMigration(migration: SessionFormatMigration): SessionFormatMigration;
8
+ /**
9
+ * Compile a unique, complete adjacent migration chain.
10
+ * @param options - current version, adjacent declarations, and current restorer.
11
+ * @returns immutable planner and streaming migration compiler.
12
+ */
13
+ export declare function createSessionFormatChain(options: SessionFormatChainOptions): SessionFormatChain;
14
+ //# sourceMappingURL=chain.d.ts.map
@@ -0,0 +1,17 @@
1
+ import type { SessionFormatEvent, SessionFormatEventRun, SessionFormatMigrationContext } from './types.ts';
2
+ /** Migration output context that expands compact runs into retained events. */
3
+ export declare class SessionFormatEventCollector implements SessionFormatMigrationContext {
4
+ /** Events retained by this collector in source order. */
5
+ readonly values: SessionFormatEvent[];
6
+ /**
7
+ * Retain one settled event.
8
+ * @param event - settled event emitted by the upstream stage.
9
+ */
10
+ emitEvent(event: SessionFormatEvent): void;
11
+ /**
12
+ * Expand one compact run directly into retained events.
13
+ * @param run - compact event run emitted by the upstream stage.
14
+ */
15
+ emitRun(run: SessionFormatEventRun): void;
16
+ }
17
+ //# sourceMappingURL=context.d.ts.map
@@ -0,0 +1,9 @@
1
+ /** Error raised when a durable Session artifact cannot be restored or migrated losslessly. */
2
+ export declare class SessionFormatError extends Error {
3
+ readonly name: string;
4
+ }
5
+ /** A readable artifact whose released source policy has no supported migration. */
6
+ export declare class SessionFormatUnsupportedMigrationError extends SessionFormatError {
7
+ readonly name = "SessionFormatUnsupportedMigrationError";
8
+ }
9
+ //# sourceMappingURL=error.d.ts.map
@@ -0,0 +1,18 @@
1
+ /** Canonical raw log basename shared by every generation-addressed Session artifact. */
2
+ /**
3
+ * Name the raw JSONL log of one immutable Session format generation. Version
4
+ * zero keeps the original `session.jsonl`; every later generation carries a
5
+ * lowercase numeric `.vN` component before the `.jsonl` suffix.
6
+ * @param version - non-negative safe integer Session format version.
7
+ * @returns the canonical basename, without any compression suffix.
8
+ */
9
+ export declare function sessionFormatLogFilename(version: number): string;
10
+ /**
11
+ * Read the generation named by one raw JSONL log basename. Temporary,
12
+ * uppercase, leading-zero, `.v0`, and compression-suffixed names are not
13
+ * canonical.
14
+ * @param filename - one basename from a Session directory or archive.
15
+ * @returns its Session format version, or `undefined` when the name is not canonical.
16
+ */
17
+ export declare function parseSessionFormatLogFilename(filename: string): number | undefined;
18
+ //# sourceMappingURL=filename.d.ts.map
@@ -0,0 +1,9 @@
1
+ /** Pure adjacent streaming Session format migration machinery. */
2
+ export * from './chain.ts';
3
+ export * from './catalog.ts';
4
+ export * from './context.ts';
5
+ export * from './error.ts';
6
+ export * from './filename.ts';
7
+ export * from './json.ts';
8
+ export * from './types.ts';
9
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,49 @@
1
+ import type { SessionFormatHeader, SessionFormatJsonValue } from './types.ts';
2
+ /**
3
+ * Test whether a value is a non-null, non-array object.
4
+ * @param value - candidate value.
5
+ * @returns whether the value is an object record.
6
+ */
7
+ export declare function isSessionFormatJsonObject(value: unknown): value is Record<string, unknown>;
8
+ /**
9
+ * Require a non-negative safe integer without the JSON-unstable negative zero.
10
+ * @param value - candidate count.
11
+ * @param label - diagnostic subject.
12
+ * @returns validated count.
13
+ */
14
+ export declare function sessionFormatCount(value: unknown, label: string): number;
15
+ /**
16
+ * Require a safe integer without the JSON-unstable negative zero.
17
+ * @param value - candidate integer.
18
+ * @param label - diagnostic subject.
19
+ * @returns validated integer.
20
+ */
21
+ export declare function sessionFormatSafeInteger(value: unknown, label: string): number;
22
+ /**
23
+ * Require a non-negative integral format version.
24
+ * @param value - candidate version.
25
+ * @param label - diagnostic subject.
26
+ * @returns validated version.
27
+ */
28
+ export declare function sessionFormatVersion(value: unknown, label?: string): number;
29
+ /**
30
+ * Read only the version required for directional dispatch.
31
+ * @param headerValue - untrusted physical header value.
32
+ * @returns validated stored version.
33
+ */
34
+ export declare function inspectSessionFormatVersion(headerValue: unknown): number;
35
+ /**
36
+ * Detach and deeply freeze a caller-supplied lossless JSON value.
37
+ * @param value - borrowed candidate.
38
+ * @param label - diagnostic subject.
39
+ * @returns an immutable detached JSON snapshot.
40
+ */
41
+ export declare function snapshotSessionFormatJson(value: unknown, label?: string): SessionFormatJsonValue;
42
+ /**
43
+ * Snapshot one logical header without inspecting an event body.
44
+ * @param header - borrowed logical header.
45
+ * @param label - diagnostic subject.
46
+ * @returns immutable detached header.
47
+ */
48
+ export declare function snapshotSessionFormatHeader(header: SessionFormatHeader, label?: string): SessionFormatHeader;
49
+ //# sourceMappingURL=json.d.ts.map
@@ -0,0 +1,190 @@
1
+ /** Scalar value admitted at the durable Session JSON boundary. */
2
+ export type SessionFormatJsonPrimitive = null | boolean | number | string;
3
+ /** Lossless JSON value admitted at the durable Session boundary. */
4
+ export type SessionFormatJsonValue = SessionFormatJsonPrimitive | readonly SessionFormatJsonValue[] | SessionFormatJsonObject;
5
+ /** Lossless JSON object admitted at the durable Session boundary. */
6
+ export interface SessionFormatJsonObject {
7
+ readonly [key: string]: SessionFormatJsonValue;
8
+ }
9
+ /** Logical Session metadata shared by supported historical and current formats. */
10
+ export interface SessionFormatHeader extends SessionFormatJsonObject {
11
+ readonly version: number;
12
+ readonly id: string;
13
+ readonly createdAt: number;
14
+ readonly cwd?: string;
15
+ readonly parentSession?: string;
16
+ readonly isSeeded: boolean;
17
+ readonly origin?: 'subagent';
18
+ readonly delegationDepth: number;
19
+ readonly agentPreset?: string;
20
+ }
21
+ /** One decoded logical Session event. */
22
+ export interface SessionFormatEvent extends SessionFormatJsonObject {
23
+ readonly type: string;
24
+ readonly seq: number;
25
+ readonly time: number;
26
+ readonly data: SessionFormatJsonValue;
27
+ }
28
+ /** One detached complete logical Session artifact. */
29
+ export interface SessionFormatArtifact {
30
+ readonly header: SessionFormatHeader;
31
+ /** Exact inherited prefix length, available only after a body read. */
32
+ readonly inheritedEventCount: number;
33
+ readonly events: readonly SessionFormatEvent[];
34
+ }
35
+ /** One independently maintained adjacent streaming migration. */
36
+ export interface SessionFormatMigration {
37
+ readonly name: string;
38
+ readonly fromVersion: number;
39
+ readonly toVersion: number;
40
+ /** Convert one header without reading event bodies. */
41
+ migrateHeader(header: SessionFormatHeader): SessionFormatHeader;
42
+ /** Create the stateful body stage for one source artifact. */
43
+ createStage(input: SessionFormatMigrationStageInput): SessionFormatMigrationStage;
44
+ /** Refuse any header that the adjacent target writer cannot emit. */
45
+ validateTargetHeader(header: SessionFormatHeader): void;
46
+ }
47
+ /** Headers and inherited cut supplied when one adjacent body stage is created. */
48
+ export interface SessionFormatMigrationStageInput {
49
+ /** Validated source metadata for this adjacent edge. */
50
+ readonly sourceHeader: SessionFormatHeader;
51
+ /** Validated target metadata produced by this edge's header migration. */
52
+ readonly targetHeader: SessionFormatHeader;
53
+ /** Exact inherited prefix length in source coordinates. */
54
+ readonly sourceInheritedEventCount: number;
55
+ /** Whether this edge consumes physical decode output or a prior migration's validated output. */
56
+ readonly sourceKind: 'decoded' | 'transformed';
57
+ }
58
+ /** Inputs that compile the unique complete migration chain. */
59
+ export interface SessionFormatChainOptions {
60
+ readonly currentVersion: number;
61
+ readonly migrations: readonly SessionFormatMigration[];
62
+ /** Restore and validate a detached current header without reading event bodies. */
63
+ readonly restoreCurrentHeader: (header: SessionFormatHeader) => SessionFormatHeader;
64
+ }
65
+ /** Pure adjacent planner and streaming migration compiler. */
66
+ export interface SessionFormatChain {
67
+ readonly currentVersion: number;
68
+ /** Compile the complete migration stage chain for one decoded source artifact. */
69
+ createStream(header: SessionFormatHeader, inheritedEventCount: number, context: SessionFormatMigrationContext): SessionFormatMigrationStream;
70
+ /** Convert only a supported header to the current logical representation. */
71
+ migrateHeader(header: SessionFormatHeader): SessionFormatHeader;
72
+ }
73
+ /** Physical-row failure policy selected once for one restore. */
74
+ export type SessionFormatRecovery = 'strict' | 'recoverable';
75
+ /** Pure physical JSON codec frozen with one released Session format. */
76
+ export interface SessionFormatCodec {
77
+ readonly version: number;
78
+ /** Decode one physical header into body-independent logical metadata. */
79
+ decodeHeader(value: unknown): SessionFormatHeader;
80
+ /** Create one row-at-a-time decoder with an explicit failure policy. */
81
+ createDecoder(headerValue: unknown, recovery: SessionFormatRecovery): SessionFormatArtifactDecoder;
82
+ }
83
+ /** Stateful physical-row decoder used by streaming persistence restores. */
84
+ export interface SessionFormatArtifactDecoder {
85
+ readonly header: SessionFormatHeader;
86
+ /** Inherited cut known before body decoding; current formats may derive it at EOF. */
87
+ readonly headerInheritedEventCount?: number;
88
+ /** Decode one physical row and synchronously emit its events or compact run. */
89
+ decodeRow(rowValue: unknown, context: SessionFormatMigrationContext): void;
90
+ /** Finish row validation and return the exact inherited cut. */
91
+ finish(context: SessionFormatMigrationContext): number;
92
+ }
93
+ /** Stateless physical record encoder for the installed current format. */
94
+ export interface SessionFormatCurrentEncoder {
95
+ /** Encode the physical header record for one current artifact. */
96
+ encodeHeader(header: SessionFormatHeader, inheritedEventCount: number): SessionFormatJsonObject;
97
+ /** Encode one current logical event as one physical record. */
98
+ encodeEvent(event: SessionFormatEvent): SessionFormatJsonObject;
99
+ }
100
+ /** A codec-owned compact run that adjacent migrations may consume without expanding. */
101
+ export interface SessionFormatEventRun {
102
+ readonly runType: string;
103
+ readonly firstSeq: number;
104
+ readonly eventCount: number;
105
+ /** Expand the run for a migration that has no direct handler. */
106
+ expand(): Iterable<SessionFormatEvent>;
107
+ }
108
+ /** Synchronous output channel owned by a compiled migration stream. */
109
+ export interface SessionFormatMigrationContext {
110
+ /** Deliver one settled event to the next stage before returning. */
111
+ emitEvent(event: SessionFormatEvent): void;
112
+ /** Deliver one compact event run to the next stage before returning. */
113
+ emitRun(run: SessionFormatEventRun): void;
114
+ }
115
+ /** Stateful adjacent migration stage used by streaming persistence restores. */
116
+ export interface SessionFormatMigrationStage {
117
+ /** Target inherited cut when it is unchanged and known before EOF. */
118
+ readonly headerInheritedEventCount?: number;
119
+ /** Transform one source event and synchronously emit every settled target item. */
120
+ transformEvent(event: SessionFormatEvent, context: SessionFormatMigrationContext): void;
121
+ /** Transform one compact source run without requiring an intermediate expansion array. */
122
+ transformRun(run: SessionFormatEventRun, context: SessionFormatMigrationContext): void;
123
+ /** Emit trailing target items and return the exact target inherited cut. */
124
+ finish(context: SessionFormatMigrationContext): number;
125
+ }
126
+ /** One composed migration chain that emits settled current events to its owner. */
127
+ export interface SessionFormatMigrationStream extends SessionFormatMigrationContext {
128
+ readonly header: SessionFormatHeader;
129
+ /** Settle all migration stages and return the exact current inherited cut. */
130
+ finish(): number;
131
+ }
132
+ /** Header-only classification that never inspects event rows. */
133
+ export type SessionFormatHeaderReadResult = {
134
+ readonly status: 'current' | 'migration-required';
135
+ readonly storedVersion: number;
136
+ readonly targetVersion: number;
137
+ /** Latest logical header. The exact inherited cut requires a body read. */
138
+ readonly header: SessionFormatHeader;
139
+ } | {
140
+ readonly status: 'unsupported';
141
+ readonly storedVersion: number;
142
+ readonly targetVersion: number;
143
+ readonly reason: string;
144
+ } | {
145
+ readonly status: 'malformed';
146
+ readonly storedVersion?: number;
147
+ readonly targetVersion: number;
148
+ readonly reason: string;
149
+ };
150
+ /** Inputs for a build-static physical codec and migration catalog. */
151
+ export interface SessionFormatCatalogOptions extends SessionFormatChainOptions {
152
+ readonly codecs: readonly SessionFormatCodec[];
153
+ /** Restore and validate a complete current artifact. */
154
+ readonly restoreCurrent: (artifact: SessionFormatArtifact) => SessionFormatArtifact;
155
+ /** Encode current records without materializing an artifact-sized row array. */
156
+ readonly currentEncoder: SessionFormatCurrentEncoder;
157
+ /** Validate an exclusively owned transformed artifact without copying or freezing it. */
158
+ readonly restoreTransformedCurrent: (artifact: SessionFormatArtifact) => SessionFormatArtifact;
159
+ }
160
+ /** Policies applied by one physical-row restore. */
161
+ export interface SessionFormatRestoreOptions {
162
+ readonly recovery: SessionFormatRecovery;
163
+ /**
164
+ * `current` applies all installed current-format validation. `transformed` applies
165
+ * released current-format validation only after migration; current input receives only codec validation.
166
+ */
167
+ readonly validation: 'transformed' | 'current';
168
+ }
169
+ /** Build-static physical dispatch and adjacent migration catalog. */
170
+ export interface SessionFormatCatalog {
171
+ readonly currentVersion: number;
172
+ /** Classify and translate one header without reading event rows. */
173
+ readHeader(headerValue: unknown): SessionFormatHeaderReadResult;
174
+ /** Create one single-pass physical-row restore into current logical events. */
175
+ createRestore(headerValue: unknown, options: SessionFormatRestoreOptions): SessionFormatRestore;
176
+ /** Encode one current physical header record. */
177
+ encodeCurrentHeader(header: SessionFormatHeader, inheritedEventCount: number): SessionFormatJsonObject;
178
+ /** Encode one current physical event record. */
179
+ encodeCurrentEvent(event: SessionFormatEvent): SessionFormatJsonObject;
180
+ }
181
+ /** One caller-owned physical-row restore whose final value is a current logical artifact. */
182
+ export interface SessionFormatRestore {
183
+ /** Current logical header available before body decoding. */
184
+ readonly header: SessionFormatHeader;
185
+ /** Decode one physical row in file order. */
186
+ decodeRow(rowValue: unknown): void;
187
+ /** Finish every decoder and migration stage and return the current artifact. */
188
+ finish(): SessionFormatArtifact;
189
+ }
190
+ //# sourceMappingURL=types.d.ts.map
package/package.json ADDED
@@ -0,0 +1,38 @@
1
+ {
2
+ "name": "@deepseek-ai/dsh-session-format",
3
+ "description": "Streaming adjacent Session format migration machinery",
4
+ "version": "0.1.3-alpha.2",
5
+ "publishConfig": {
6
+ "access": "public"
7
+ },
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git+https://github.com/deepseek-ai/deepseek-harness.git",
11
+ "directory": "packages/session/session-format"
12
+ },
13
+ "type": "module",
14
+ "main": "lib/index.js",
15
+ "types": "lib/types/index.d.ts",
16
+ "exports": {
17
+ ".": {
18
+ "types": "./lib/types/index.d.ts",
19
+ "default": "./lib/index.js"
20
+ },
21
+ "./src/*": "./src/*",
22
+ "./package.json": "./package.json"
23
+ },
24
+ "files": [
25
+ "lib/index.js",
26
+ "lib/types/**/*.d.ts"
27
+ ],
28
+ "license": "MIT",
29
+ "dependencies": {
30
+ "@deepseek-ai/dsh-util-values": "^0.1.3-alpha.2"
31
+ },
32
+ "peerDependencies": {
33
+ "@deepseek-ai/cordis": "^4.0.2"
34
+ },
35
+ "devDependencies": {
36
+ "@deepseek-ai/cordis": "^4.0.2"
37
+ }
38
+ }