@deepseek-ai/dsh-session 0.2.1-alpha.1 → 0.2.1-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/README.i18n.yaml +7 -4
- package/README.md +13 -1
- package/README.zh.md +13 -1
- package/lib/index.js +134 -18
- package/lib/types/index.d.ts +49 -1
- package/lib/types/index.js +120 -5
- package/lib/types/known-event-types.js +1 -0
- package/lib/types/types.d.ts +45 -14
- package/lib/types/types.js +14 -14
- package/package.json +8 -8
package/README.i18n.yaml
CHANGED
|
@@ -24,8 +24,11 @@
|
|
|
24
24
|
en: aa5f1951ecf3ac0c
|
|
25
25
|
zh: 62a599ace8f2bf15
|
|
26
26
|
/deepseek-ai-dsh-session/use-this-package/read-the-log:
|
|
27
|
-
en:
|
|
28
|
-
zh:
|
|
27
|
+
en: c4bf3103b9762b6c
|
|
28
|
+
zh: 369c734bb20de2ea
|
|
29
|
+
/deepseek-ai-dsh-session/use-this-package/write-experimental-plugin-records:
|
|
30
|
+
en: 6c758f98535b734c
|
|
31
|
+
zh: ac54342937477f37
|
|
29
32
|
/deepseek-ai-dsh-session/use-this-package/fork-a-session:
|
|
30
33
|
en: 1c40d17a786059d3
|
|
31
34
|
zh: 81320ce118977c65
|
|
@@ -99,8 +102,8 @@
|
|
|
99
102
|
en: 5eb30cfb410d52c0
|
|
100
103
|
zh: da56365b8ab896da
|
|
101
104
|
/deepseek-ai-dsh-session/known-limitations-and-deferred-work:
|
|
102
|
-
en:
|
|
103
|
-
zh:
|
|
105
|
+
en: 45afe96a4e252db9
|
|
106
|
+
zh: 920bcc011823817b
|
|
104
107
|
/deepseek-ai-dsh-session/known-limitations-and-deferred-work/dev-note:
|
|
105
108
|
en: e67f633dc6fe7773
|
|
106
109
|
zh: 3bb0c20c93f16f98
|
package/README.md
CHANGED
|
@@ -63,6 +63,18 @@ Append, seed/restore, and event adoption/snapshot reject any `header.system` and
|
|
|
63
63
|
|
|
64
64
|
Session log positions use two numeric types. `SessionSeq` identifies an existing event or inclusive event watermark; `SessionLogOffset` identifies a gap, prefix length, or read boundary and may equal the event count. `SessionSeqCursor` adds the `-1` “no event yet” value, while `OptionalSessionSeq` uses `null` when absence is data. The constructors validate non-negative safe integers, and the brands disappear at runtime, so durable JSON and wire values remain ordinary numbers.
|
|
65
65
|
|
|
66
|
+
<a id="write-experimental-plugin-records"></a>
|
|
67
|
+
|
|
68
|
+
### Write experimental plugin records
|
|
69
|
+
|
|
70
|
+
`appendPluginRecord(session, type, data)` appends one plugin record for a package under `packages/experimental/`; the `verify-plugin-record-callers` check rejects any other production caller in this repository. Plugins outside this repository must not call it either, although no repository check can inspect them.
|
|
71
|
+
|
|
72
|
+
Declare each record in `PluginRecordMap` through `@deepseek-ai/dsh-session/types`, with a description and an explicit payload type annotation in the owning experimental package's `src/`. Names use `plugin:<owner>/<record>`, where `owner` is the suffix of the package name `@deepseek-ai/dsh-experimental-<owner>`; for example, `plugin:pi-extensions/entry`. TypeScript checks each name and payload against its declaration; the writer's runtime JSON snapshot rejects values that cannot be preserved losslessly. Put extension-defined or other dynamic names inside the payload of a declared record.
|
|
73
|
+
|
|
74
|
+
The [experimental persistence catalog](../../../docs/experimental-persistence-catalog.md) lists current plugin-record declarations with their owners, descriptions, payload type annotations, and source files. These declarations remain separate from `SessionEventMap`, expanded persistence schemas, released type history, and `KNOWN_SESSION_EVENT_TYPES`. Records carry `ignorable: true`: a build that does not recognize one retains and skips it on read. Records never enter the model-visible surface. Resume and fork carry them with the rest of the log; Session format migration keeps them on a best-effort basis.
|
|
75
|
+
|
|
76
|
+
`pluginRecordOf(event)` returns an event as a record, or `undefined` for any other event; a `ctx.sessionProjections` unit that passes each event to it rebuilds plugin state on resume. Its `data` remains `unknown` even when the current map declares the name. The owner validates it before use because earlier builds can write different payloads, and the V3-to-V4 format edge can rename an unknown ignorable V3 event into the same `plugin:` namespace. Removing or renaming a declaration does not discard stored records. The [ignorable events decision](../../../.agents/notes/implemented/architecture/2026-08-30-retain-ignorable-external-session-events.md) owns the compatibility policy.
|
|
77
|
+
|
|
66
78
|
### Fork a session
|
|
67
79
|
|
|
68
80
|
`ctx.sessions.fork(source, boundary?, childSessionId?)` copies an exact inclusive event prefix (default: the last event) from a live source. `buildForkSeed` in `dsh-session/fork` places an inherited marker after the copied events, adds missing error tool results only for the open step, and closes that step and turn with a `forked` reason. Closed steps and turns remain unchanged, including historical missing results. The marker and closers belong to the child; `inheritedEventCount` counts only the copied prefix.
|
|
@@ -188,7 +200,7 @@ Logging causes no invalidation, and exact reconstruction preserves request-prefi
|
|
|
188
200
|
These limits define when the session store needs special care. They are current package constraints, not a task backlog.
|
|
189
201
|
|
|
190
202
|
- **Live-source Store API** — persisted-but-unloaded sources use the Host observation path. Fork closes only the open tail and does not repair missing results in previously closed steps.
|
|
191
|
-
- **`SESSION_FORMAT_VERSION` names the [current logical representation](../../../docs/session-format-status.md)** — the current reader rejects retired `header.system` and validates `system/message` payloads and protected-head rewrites. Historical headers and events belong to adjacent format packages; the [adjacent migration chain](../../session/session-format-catalog/README.md) converts supported history before constructing `Session`, and the write-open path publishes only the current-format 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)).
|
|
203
|
+
- **`SESSION_FORMAT_VERSION` names the [current logical representation](../../../docs/session-format-status.md)** — the current reader rejects retired `header.system` and validates `system/message` payloads and protected-head rewrites. Historical headers and events belong to adjacent format packages; the [adjacent migration chain](../../session/session-format-catalog/README.md) converts supported history before constructing `Session`, and the write-open path publishes only the current-format 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)). [Reader compatibility](../../../.agents/notes/implemented/process/2026-10-08-session-reader-compatibility-review.md) determines whether a change needs a version increase.
|
|
192
204
|
- **`TurnEndReasonMap` omits the ACP-named `refusal` / `max_turn_requests` variants** — producer-gated: they land when an adapter or the loop first emits them.
|
|
193
205
|
- **No session tree beyond fork** — a pi-style entry tree over branched sessions is deferred unless a consumer needs more than boundary-based forking.
|
|
194
206
|
|
package/README.zh.md
CHANGED
|
@@ -63,6 +63,18 @@ session.deriveMessages() // the derived model history
|
|
|
63
63
|
|
|
64
64
|
会话日志位置使用两种数字类型。`SessionSeq` 标识已有事件或包含端点的事件水位;`SessionLogOffset` 标识间隙、前缀长度或读取边界,并且可以等于事件数量。`SessionSeqCursor` 添加 `-1` 这个“尚无事件”值,`OptionalSessionSeq` 则在缺失本身属于数据时使用 `null`。构造函数会校验非负安全整数,brand 在运行时会被擦除,因此持久 JSON 与 wire 值仍是普通数字。
|
|
65
65
|
|
|
66
|
+
<a id="write-experimental-plugin-records"></a>
|
|
67
|
+
|
|
68
|
+
### 写入实验性插件记录
|
|
69
|
+
|
|
70
|
+
`appendPluginRecord(session, type, data)` 为 `packages/experimental/` 下的包追加一条插件记录;`verify-plugin-record-callers` 检查拒绝本仓库中任何其他生产调用方。本仓库之外的插件同样不得调用它,尽管仓库检查无法检查它们。
|
|
71
|
+
|
|
72
|
+
在所属实验性包的 `src/` 中,通过 `@deepseek-ai/dsh-session/types` 的 `PluginRecordMap` 为每条记录声明描述与显式 payload 类型注解。名称使用 `plugin:<owner>/<record>`,其中 `owner` 是包名 `@deepseek-ai/dsh-experimental-<owner>` 的后缀;例如 `plugin:pi-extensions/entry`。TypeScript 按声明检查每个名称与 payload;写入方运行时的 JSON 快照拒绝无法无损保留的值。扩展自定义名称或其他动态名称放在已声明记录的 payload 中。
|
|
73
|
+
|
|
74
|
+
[实验性持久化目录](../../../docs/experimental-persistence-catalog.zh.md)列出当前插件记录声明及其所属包、描述、payload 类型注解与源文件。这些声明与 `SessionEventMap`、展开的持久化 schema、已发布类型历史及 `KNOWN_SESSION_EVENT_TYPES` 分开。记录带有 `ignorable: true`:不认识某条记录的构建在读取时保留并跳过它。记录从不进入模型可见的 surface。resume 与 fork 随日志其余部分一起携带它们;Session 格式迁移以尽力而为的方式保留它们。
|
|
75
|
+
|
|
76
|
+
`pluginRecordOf(event)` 把事件作为记录返回,对其他任何事件返回 `undefined`;把每个事件都交给它的 `ctx.sessionProjections` 单元在 resume 时重建插件状态。即使当前映射声明了该名称,其 `data` 仍为 `unknown`。所属包在使用前校验数据,因为早先构建可能写入不同的 payload,V3-to-V4 格式迁移边也可能把未知的 ignorable V3 事件改名放入同一个 `plugin:` 命名空间。删除或重命名声明不会丢弃已存记录。[ignorable 事件决策](../../../.agents/notes/implemented/architecture/2026-08-30-retain-ignorable-external-session-events.zh.md)定义兼容性策略。
|
|
77
|
+
|
|
66
78
|
### 派生会话的 fork
|
|
67
79
|
|
|
68
80
|
`ctx.sessions.fork(source, boundary?, childSessionId?)` 从实时源会话复制包含切点的精确事件前缀(默认:最后一个事件)。`dsh-session/fork` 的 `buildForkSeed` 在复制事件之后放置继承标记,仅为开放步骤补缺失的错误工具结果,并以 `forked` 原因关闭步骤和轮次。已关闭的步骤与轮次保持原样,包括历史缺失结果。标记和结束事件属于子会话;`inheritedEventCount` 只统计复制的前缀。
|
|
@@ -188,7 +200,7 @@ session.deriveMessages() // the derived model history
|
|
|
188
200
|
这些限制说明会话存储何时需要特别留意。它们是当前包约束,不是任务积压。
|
|
189
201
|
|
|
190
202
|
- **Store API 仅接受活跃源会话** — 已持久化但未加载的源会话通过 Host 观察路径处理。Fork 仅闭合开放尾部,不修复先前已闭合步骤中缺失的结果。
|
|
191
|
-
- **`SESSION_FORMAT_VERSION` 命名[当前逻辑表示](../../../docs/session-format-status.zh.md)**——当前读取器拒绝已退役的 `header.system`,并校验 `system/message` 载荷与受保护头节点的重写。历史 header 与事件归相邻格式包所有;[相邻迁移链](../../session/session-format-catalog/README.zh.md)在构造 `Session` 前转换受支持的历史,写打开只发布当前格式的后继代际。同版本未知事件要求信封显式带有 `ignorable` 标记,但这不保证结构迁移的安全性([机制](../../../.agents/notes/implemented/architecture/2026-08-31-released-session-format-migrations.zh.md))。
|
|
203
|
+
- **`SESSION_FORMAT_VERSION` 命名[当前逻辑表示](../../../docs/session-format-status.zh.md)**——当前读取器拒绝已退役的 `header.system`,并校验 `system/message` 载荷与受保护头节点的重写。历史 header 与事件归相邻格式包所有;[相邻迁移链](../../session/session-format-catalog/README.zh.md)在构造 `Session` 前转换受支持的历史,写打开只发布当前格式的后继代际。同版本未知事件要求信封显式带有 `ignorable` 标记,但这不保证结构迁移的安全性([机制](../../../.agents/notes/implemented/architecture/2026-08-31-released-session-format-migrations.zh.md))。[读取器兼容性](../../../.agents/notes/implemented/process/2026-10-08-session-reader-compatibility-review.zh.md)决定变更是否需要升版本。
|
|
192
204
|
- **`TurnEndReasonMap` 不含 ACP(Agent Client Protocol)命名的 `refusal`/`max_turn_requests` 变体**:受生产方约束;只有当适配器或循环首次产生这些变体时才加入。
|
|
193
205
|
- **fork 之外没有会话树**:基于分支会话的 pi 风格条目树被推迟,除非消费方需要超越基于边界的 forking 的能力。
|
|
194
206
|
|
package/lib/index.js
CHANGED
|
@@ -38,20 +38,20 @@ function SessionLogOffset(value) {
|
|
|
38
38
|
* event-body read composes the build-static adjacent chain and publishes only
|
|
39
39
|
* this final generation before constructing a Session.
|
|
40
40
|
*
|
|
41
|
-
* The version is a single monotonic integer with no major/minor split.
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
*
|
|
53
|
-
*
|
|
54
|
-
* `.agents/notes/implemented/
|
|
41
|
+
* The version is a single monotonic integer with no major/minor split. Retain
|
|
42
|
+
* it when older readers resume correctly, preserve safely ignorable records,
|
|
43
|
+
* or refuse required features before execution, mutation, or tail repair.
|
|
44
|
+
* New readers must preserve historical meanings, including logs mixing old
|
|
45
|
+
* and new record representations. Bump when an existing discriminator cannot
|
|
46
|
+
* prevent unsafe interpretation; a structural difference alone is insufficient.
|
|
47
|
+
* Unknown fields and values require evidence of the released reader's actual
|
|
48
|
+
* admission behavior. The per-event {@link SessionEvent.ignorable} marker
|
|
49
|
+
* governs unknown event types, not arbitrary nested payload changes.
|
|
50
|
+
* Same-version readers retain earlier representations: the adjacent migration
|
|
51
|
+
* chain does not run when stored and current versions match. A subsequent
|
|
52
|
+
* migration must account for all supported representations of its source.
|
|
53
|
+
* Compatibility review and the retained migration obligations are recorded in
|
|
54
|
+
* `.agents/notes/implemented/process/2026-10-08-session-reader-compatibility-review.md`.
|
|
55
55
|
*/
|
|
56
56
|
const SESSION_FORMAT_VERSION = 4;
|
|
57
57
|
//#endregion
|
|
@@ -135,6 +135,7 @@ const KNOWN_SESSION_EVENT_TYPES = new Set([
|
|
|
135
135
|
"turn/start",
|
|
136
136
|
"user/message",
|
|
137
137
|
"web/deepseek-search-llm-request",
|
|
138
|
+
"working-directory/change",
|
|
138
139
|
"workspace/changes"
|
|
139
140
|
]);
|
|
140
141
|
/** Event types whose model-visible effects require an explicit pure interpreter. */
|
|
@@ -1238,6 +1239,79 @@ function invokeContainedSessionObservers(ctx, name, id, args, callbacks) {
|
|
|
1238
1239
|
/** Store attachment for the append path; module-private to keep Session store-agnostic publicly. */
|
|
1239
1240
|
const attachments = /* @__PURE__ */ new WeakMap();
|
|
1240
1241
|
/**
|
|
1242
|
+
* Grammar of a {@link PluginRecordType}: `plugin:` followed by
|
|
1243
|
+
* slash-separated segments of lowercase letters, digits, `.`, `_`, and `-`,
|
|
1244
|
+
* each segment starting with a letter or digit.
|
|
1245
|
+
*/
|
|
1246
|
+
const PLUGIN_RECORD_TYPE = /^plugin:[a-z0-9][a-z0-9._-]*(?:\/[a-z0-9][a-z0-9._-]*)*$/u;
|
|
1247
|
+
/**
|
|
1248
|
+
* Whether one event type name follows the plugin record grammar.
|
|
1249
|
+
* @param type - an event type name.
|
|
1250
|
+
* @returns whether the name is a {@link PluginRecordType}.
|
|
1251
|
+
*/
|
|
1252
|
+
function isPluginRecordType(type) {
|
|
1253
|
+
return PLUGIN_RECORD_TYPE.test(type);
|
|
1254
|
+
}
|
|
1255
|
+
/**
|
|
1256
|
+
* Type one constructed plugin record as a log event after checking its type
|
|
1257
|
+
* against the plugin record grammar. A restored Session already holds events
|
|
1258
|
+
* whose types are outside `SessionEventMap` when they are marked ignorable;
|
|
1259
|
+
* a plugin record is such an event.
|
|
1260
|
+
* @param value - the frozen record envelope built by {@link Session}.
|
|
1261
|
+
* @throws when the record type is outside the plugin record grammar.
|
|
1262
|
+
*/
|
|
1263
|
+
function assertPluginRecordEvent(value) {
|
|
1264
|
+
const { type } = value;
|
|
1265
|
+
if (!isPluginRecordType(type)) throw new Error(`plugin record type "${type}" must be "plugin:" followed by lowercase slash-separated segments`);
|
|
1266
|
+
}
|
|
1267
|
+
/** Module-private access to {@link Session}'s record commit, assigned once by its static block. */
|
|
1268
|
+
let commitPluginRecord;
|
|
1269
|
+
/**
|
|
1270
|
+
* Append one experimental plugin record: an event marked `ignorable` whose
|
|
1271
|
+
* type is declared in {@link PluginRecordMap}, outside {@link SessionEventMap}.
|
|
1272
|
+
* The plugin record catalog lists it separately from released event schemas.
|
|
1273
|
+
* A reader that does not recognize the type retains and skips the record, which never
|
|
1274
|
+
* enters the model-visible surface, and fork and resume carry it with the rest
|
|
1275
|
+
* of the log. A Session format migration retains records on a best-effort
|
|
1276
|
+
* basis, so a record holds plugin-owned state that its owner can lose without
|
|
1277
|
+
* changing how the rest of the log is interpreted.
|
|
1278
|
+
*
|
|
1279
|
+
* Only production source under `packages/experimental/` may call this
|
|
1280
|
+
* function; the `verify-plugin-record-callers` gate rejects any other
|
|
1281
|
+
* production caller in this repository. Read records back with
|
|
1282
|
+
* {@link pluginRecordOf}.
|
|
1283
|
+
* @param session - the Session whose log receives the record.
|
|
1284
|
+
* @param type - declared record name: `plugin:` followed by slash-separated
|
|
1285
|
+
* segments of lowercase letters, digits, `.`, `_`, and `-`, each starting
|
|
1286
|
+
* with a letter or digit.
|
|
1287
|
+
* @param data - JSON payload, snapshotted before it enters the log.
|
|
1288
|
+
* @returns the sequence number of the committed record.
|
|
1289
|
+
* @throws when `type` is outside that grammar, when `data` is not losslessly
|
|
1290
|
+
* JSON-serializable, or when the call reenters another append's publication;
|
|
1291
|
+
* a rejected record does not change the log.
|
|
1292
|
+
*/
|
|
1293
|
+
function appendPluginRecord(session, type, data) {
|
|
1294
|
+
return commitPluginRecord(session, type, data);
|
|
1295
|
+
}
|
|
1296
|
+
/**
|
|
1297
|
+
* Read one committed event as a plugin record. The V3-to-V4 format edge also
|
|
1298
|
+
* renames each unknown ignorable V3 event into the `plugin:` namespace, so an
|
|
1299
|
+
* owner names its records under its own package name and validates `data`.
|
|
1300
|
+
* @param event - any committed Session event, live or restored.
|
|
1301
|
+
* @returns the record, or undefined when the event is not an ignorable event
|
|
1302
|
+
* whose type follows the plugin record grammar.
|
|
1303
|
+
*/
|
|
1304
|
+
function pluginRecordOf(event) {
|
|
1305
|
+
const type = event.type;
|
|
1306
|
+
if (event.ignorable !== true || !isPluginRecordType(type)) return void 0;
|
|
1307
|
+
return {
|
|
1308
|
+
type,
|
|
1309
|
+
seq: event.seq,
|
|
1310
|
+
time: event.time,
|
|
1311
|
+
data: event.data
|
|
1312
|
+
};
|
|
1313
|
+
}
|
|
1314
|
+
/**
|
|
1241
1315
|
* An event-sourced session: an append-only log of {@link SessionEvent}s.
|
|
1242
1316
|
*
|
|
1243
1317
|
* Plain class (not a Service) — create live instances via
|
|
@@ -1448,8 +1522,7 @@ var Session = class Session {
|
|
|
1448
1522
|
if (dataSnapshot === void 0) throw new Error(`session event "${type}" carries non-JSON-serializable data`);
|
|
1449
1523
|
const surfaceMetadataSnapshot = snapshotJsonValue(surfaceMetadata);
|
|
1450
1524
|
if (surfaceMetadataSnapshot === void 0) throw new Error(`session event "${type}" carries non-JSON-serializable surface metadata`);
|
|
1451
|
-
const entry =
|
|
1452
|
-
if (entry?.appending) throw new Error("session append cannot reenter while another append is being published");
|
|
1525
|
+
const entry = this.publicationEntry();
|
|
1453
1526
|
const event = deepFreeze({
|
|
1454
1527
|
type,
|
|
1455
1528
|
seq: SessionSeq(this.log.length),
|
|
@@ -1458,6 +1531,50 @@ var Session = class Session {
|
|
|
1458
1531
|
...surfaceMetadataSnapshot
|
|
1459
1532
|
});
|
|
1460
1533
|
validateSessionEventData(event, `session event "${type}" at seq ${event.seq}`);
|
|
1534
|
+
this.commit(event, entry);
|
|
1535
|
+
return event;
|
|
1536
|
+
}
|
|
1537
|
+
static {
|
|
1538
|
+
commitPluginRecord = (session, type, data) => session.#appendRecord(type, data);
|
|
1539
|
+
}
|
|
1540
|
+
/**
|
|
1541
|
+
* Commit one plugin record; {@link appendPluginRecord} is the only caller.
|
|
1542
|
+
* @param type - record type, checked against the plugin record grammar.
|
|
1543
|
+
* @param data - JSON payload, snapshotted before it enters the log.
|
|
1544
|
+
* @returns the record's sequence number.
|
|
1545
|
+
*/
|
|
1546
|
+
#appendRecord(type, data) {
|
|
1547
|
+
const dataSnapshot = snapshotJsonValue(data);
|
|
1548
|
+
if (dataSnapshot === void 0) throw new Error(`plugin record "${type}" carries non-JSON-serializable data`);
|
|
1549
|
+
const entry = this.publicationEntry();
|
|
1550
|
+
const record = deepFreeze({
|
|
1551
|
+
type,
|
|
1552
|
+
seq: SessionSeq(this.log.length),
|
|
1553
|
+
time: Date.now(),
|
|
1554
|
+
data: dataSnapshot,
|
|
1555
|
+
ignorable: true
|
|
1556
|
+
});
|
|
1557
|
+
assertPluginRecordEvent(record);
|
|
1558
|
+
this.commit(record, entry);
|
|
1559
|
+
return record.seq;
|
|
1560
|
+
}
|
|
1561
|
+
/**
|
|
1562
|
+
* Read the store attachment for one append, refusing an append that
|
|
1563
|
+
* reenters while another append is being published.
|
|
1564
|
+
* @returns the attachment, or undefined for a detached Session.
|
|
1565
|
+
*/
|
|
1566
|
+
publicationEntry() {
|
|
1567
|
+
const entry = attachments.get(this);
|
|
1568
|
+
if (entry?.appending) throw new Error("session append cannot reenter while another append is being published");
|
|
1569
|
+
return entry;
|
|
1570
|
+
}
|
|
1571
|
+
/**
|
|
1572
|
+
* Accept one frozen candidate at the next sequence number, enter it into the
|
|
1573
|
+
* log, and notify `session/event` observers with per-listener containment.
|
|
1574
|
+
* @param event - the candidate built at the current log length.
|
|
1575
|
+
* @param entry - the attachment read before the candidate was built.
|
|
1576
|
+
*/
|
|
1577
|
+
commit(event, entry) {
|
|
1461
1578
|
this.surfaceManager.validateNext(event);
|
|
1462
1579
|
if (entry !== void 0) entry.appending = true;
|
|
1463
1580
|
try {
|
|
@@ -1471,7 +1588,6 @@ var Session = class Session {
|
|
|
1471
1588
|
this.log.push(event);
|
|
1472
1589
|
this.eventsSnapshot = void 0;
|
|
1473
1590
|
if (callbacks !== void 0 && entry !== void 0) invokeContainedSessionObservers(entry.emitCtx, "session/event", entry.id, callbackArgs, callbacks);
|
|
1474
|
-
return event;
|
|
1475
1591
|
} finally {
|
|
1476
1592
|
if (entry !== void 0) {
|
|
1477
1593
|
entry.appending = false;
|
|
@@ -1930,4 +2046,4 @@ var SessionStore = class extends Service {
|
|
|
1930
2046
|
}
|
|
1931
2047
|
};
|
|
1932
2048
|
//#endregion
|
|
1933
|
-
export { KNOWN_SESSION_EVENT_TYPES, SESSION_FORMAT_VERSION, Session, SessionForkError, SessionId, SessionLogOffset, SessionPreparation, SessionSeq, SessionStore, SessionStore as default, TOOL_NOT_STARTED, TOOL_OUTCOME_UNKNOWN, ToolCallRecovery, adoptSessionEvent, buildForkSeed, canonicalHeader, decodeSeqRanges, deriveEventMessage, encodeSeqRanges, foldRequestHeader, foldSurface, headerEquals, interruptedTurnClosers, isAppendSurfaceEvent, isReplacementSurfaceEvent, isSurfaceEligibleType, isSurfaceEvent, snapshotSessionEvent };
|
|
2049
|
+
export { KNOWN_SESSION_EVENT_TYPES, SESSION_FORMAT_VERSION, Session, SessionForkError, SessionId, SessionLogOffset, SessionPreparation, SessionSeq, SessionStore, SessionStore as default, TOOL_NOT_STARTED, TOOL_OUTCOME_UNKNOWN, ToolCallRecovery, adoptSessionEvent, appendPluginRecord, buildForkSeed, canonicalHeader, decodeSeqRanges, deriveEventMessage, encodeSeqRanges, foldRequestHeader, foldSurface, headerEquals, interruptedTurnClosers, isAppendSurfaceEvent, isReplacementSurfaceEvent, isSurfaceEligibleType, isSurfaceEvent, pluginRecordOf, snapshotSessionEvent };
|
package/lib/types/index.d.ts
CHANGED
|
@@ -10,7 +10,7 @@ import type { Scoped } from '@deepseek-ai/dsh-scope';
|
|
|
10
10
|
import type { Message } from '@deepseek-ai/dsh-llm';
|
|
11
11
|
import { SessionLogOffset, SessionSeq } from './types.ts';
|
|
12
12
|
import type { TypertLookup } from '@deepseek-ai/dsh-typert-protocol';
|
|
13
|
-
import type { CreateSessionOptions, EpochHeader, PrepareSessionOptions, RequestContext, SessionEvent, SessionEventMap, SessionEventType, SessionHeader, SessionId, SessionSeedEventState, SurfaceIntent, SurfaceEventType } from './types.ts';
|
|
13
|
+
import type { CreateSessionOptions, EpochHeader, PluginRecord, PluginRecordMap, PluginRecordType, PrepareSessionOptions, RequestContext, SessionEvent, SessionEventMap, SessionEventType, SessionHeader, SessionId, SessionSeedEventState, SurfaceIntent, SurfaceEventType } from './types.ts';
|
|
14
14
|
import type { SessionSurface, SessionMessageProjection } from './surface.ts';
|
|
15
15
|
import type { ToolHistory } from '@deepseek-ai/dsh-llm';
|
|
16
16
|
export { buildForkSeed } from './fork.ts';
|
|
@@ -90,6 +90,40 @@ export declare function adoptSessionEvent<T extends SessionEvent>(event: T): T;
|
|
|
90
90
|
* @returns a detached event snapshot with a validated, deeply frozen message.
|
|
91
91
|
*/
|
|
92
92
|
export declare function snapshotSessionEvent<T extends SessionEvent>(event: T): T;
|
|
93
|
+
/**
|
|
94
|
+
* Append one experimental plugin record: an event marked `ignorable` whose
|
|
95
|
+
* type is declared in {@link PluginRecordMap}, outside {@link SessionEventMap}.
|
|
96
|
+
* The plugin record catalog lists it separately from released event schemas.
|
|
97
|
+
* A reader that does not recognize the type retains and skips the record, which never
|
|
98
|
+
* enters the model-visible surface, and fork and resume carry it with the rest
|
|
99
|
+
* of the log. A Session format migration retains records on a best-effort
|
|
100
|
+
* basis, so a record holds plugin-owned state that its owner can lose without
|
|
101
|
+
* changing how the rest of the log is interpreted.
|
|
102
|
+
*
|
|
103
|
+
* Only production source under `packages/experimental/` may call this
|
|
104
|
+
* function; the `verify-plugin-record-callers` gate rejects any other
|
|
105
|
+
* production caller in this repository. Read records back with
|
|
106
|
+
* {@link pluginRecordOf}.
|
|
107
|
+
* @param session - the Session whose log receives the record.
|
|
108
|
+
* @param type - declared record name: `plugin:` followed by slash-separated
|
|
109
|
+
* segments of lowercase letters, digits, `.`, `_`, and `-`, each starting
|
|
110
|
+
* with a letter or digit.
|
|
111
|
+
* @param data - JSON payload, snapshotted before it enters the log.
|
|
112
|
+
* @returns the sequence number of the committed record.
|
|
113
|
+
* @throws when `type` is outside that grammar, when `data` is not losslessly
|
|
114
|
+
* JSON-serializable, or when the call reenters another append's publication;
|
|
115
|
+
* a rejected record does not change the log.
|
|
116
|
+
*/
|
|
117
|
+
export declare function appendPluginRecord<K extends Extract<keyof PluginRecordMap, PluginRecordType>>(session: Session, type: K, data: NoInfer<PluginRecordMap[K]>): SessionSeq;
|
|
118
|
+
/**
|
|
119
|
+
* Read one committed event as a plugin record. The V3-to-V4 format edge also
|
|
120
|
+
* renames each unknown ignorable V3 event into the `plugin:` namespace, so an
|
|
121
|
+
* owner names its records under its own package name and validates `data`.
|
|
122
|
+
* @param event - any committed Session event, live or restored.
|
|
123
|
+
* @returns the record, or undefined when the event is not an ignorable event
|
|
124
|
+
* whose type follows the plugin record grammar.
|
|
125
|
+
*/
|
|
126
|
+
export declare function pluginRecordOf(event: SessionEvent): PluginRecord | undefined;
|
|
93
127
|
/**
|
|
94
128
|
* An event-sourced session: an append-only log of {@link SessionEvent}s.
|
|
95
129
|
*
|
|
@@ -99,6 +133,7 @@ export declare function snapshotSessionEvent<T extends SessionEvent>(event: T):
|
|
|
99
133
|
* @typert object
|
|
100
134
|
*/
|
|
101
135
|
export declare class Session {
|
|
136
|
+
#private;
|
|
102
137
|
private log;
|
|
103
138
|
/** Single incremental owner of surface acceptance and projection state. */
|
|
104
139
|
private readonly surfaceManager;
|
|
@@ -240,6 +275,19 @@ export declare class Session {
|
|
|
240
275
|
* rejects before the log changes.
|
|
241
276
|
*/
|
|
242
277
|
append<T extends SessionEventType>(type: T, data: SessionEventMap[T], ...opts: T extends SurfaceEventType ? [opts: SurfaceIntent<T>] : []): SessionEvent<T>;
|
|
278
|
+
/**
|
|
279
|
+
* Read the store attachment for one append, refusing an append that
|
|
280
|
+
* reenters while another append is being published.
|
|
281
|
+
* @returns the attachment, or undefined for a detached Session.
|
|
282
|
+
*/
|
|
283
|
+
private publicationEntry;
|
|
284
|
+
/**
|
|
285
|
+
* Accept one frozen candidate at the next sequence number, enter it into the
|
|
286
|
+
* log, and notify `session/event` observers with per-listener containment.
|
|
287
|
+
* @param event - the candidate built at the current log length.
|
|
288
|
+
* @param entry - the attachment read before the candidate was built.
|
|
289
|
+
*/
|
|
290
|
+
private commit;
|
|
243
291
|
/** Cached fold of the request-header events — see {@link requestHeader}. */
|
|
244
292
|
private headerFold;
|
|
245
293
|
/** Log position (events consumed) the header fold has reached. */
|
package/lib/types/index.js
CHANGED
|
@@ -324,6 +324,77 @@ function invokeContainedSessionObservers(ctx, name, id, args, callbacks) {
|
|
|
324
324
|
}
|
|
325
325
|
/** Store attachment for the append path; module-private to keep Session store-agnostic publicly. */
|
|
326
326
|
const attachments = new WeakMap();
|
|
327
|
+
/**
|
|
328
|
+
* Grammar of a {@link PluginRecordType}: `plugin:` followed by
|
|
329
|
+
* slash-separated segments of lowercase letters, digits, `.`, `_`, and `-`,
|
|
330
|
+
* each segment starting with a letter or digit.
|
|
331
|
+
*/
|
|
332
|
+
const PLUGIN_RECORD_TYPE = /^plugin:[a-z0-9][a-z0-9._-]*(?:\/[a-z0-9][a-z0-9._-]*)*$/u;
|
|
333
|
+
/**
|
|
334
|
+
* Whether one event type name follows the plugin record grammar.
|
|
335
|
+
* @param type - an event type name.
|
|
336
|
+
* @returns whether the name is a {@link PluginRecordType}.
|
|
337
|
+
*/
|
|
338
|
+
function isPluginRecordType(type) {
|
|
339
|
+
return PLUGIN_RECORD_TYPE.test(type);
|
|
340
|
+
}
|
|
341
|
+
/**
|
|
342
|
+
* Type one constructed plugin record as a log event after checking its type
|
|
343
|
+
* against the plugin record grammar. A restored Session already holds events
|
|
344
|
+
* whose types are outside `SessionEventMap` when they are marked ignorable;
|
|
345
|
+
* a plugin record is such an event.
|
|
346
|
+
* @param value - the frozen record envelope built by {@link Session}.
|
|
347
|
+
* @throws when the record type is outside the plugin record grammar.
|
|
348
|
+
*/
|
|
349
|
+
function assertPluginRecordEvent(value) {
|
|
350
|
+
const { type } = value;
|
|
351
|
+
if (!isPluginRecordType(type)) {
|
|
352
|
+
throw new Error(`plugin record type "${type}" must be "plugin:" followed by lowercase slash-separated segments`);
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
/** Module-private access to {@link Session}'s record commit, assigned once by its static block. */
|
|
356
|
+
let commitPluginRecord;
|
|
357
|
+
/**
|
|
358
|
+
* Append one experimental plugin record: an event marked `ignorable` whose
|
|
359
|
+
* type is declared in {@link PluginRecordMap}, outside {@link SessionEventMap}.
|
|
360
|
+
* The plugin record catalog lists it separately from released event schemas.
|
|
361
|
+
* A reader that does not recognize the type retains and skips the record, which never
|
|
362
|
+
* enters the model-visible surface, and fork and resume carry it with the rest
|
|
363
|
+
* of the log. A Session format migration retains records on a best-effort
|
|
364
|
+
* basis, so a record holds plugin-owned state that its owner can lose without
|
|
365
|
+
* changing how the rest of the log is interpreted.
|
|
366
|
+
*
|
|
367
|
+
* Only production source under `packages/experimental/` may call this
|
|
368
|
+
* function; the `verify-plugin-record-callers` gate rejects any other
|
|
369
|
+
* production caller in this repository. Read records back with
|
|
370
|
+
* {@link pluginRecordOf}.
|
|
371
|
+
* @param session - the Session whose log receives the record.
|
|
372
|
+
* @param type - declared record name: `plugin:` followed by slash-separated
|
|
373
|
+
* segments of lowercase letters, digits, `.`, `_`, and `-`, each starting
|
|
374
|
+
* with a letter or digit.
|
|
375
|
+
* @param data - JSON payload, snapshotted before it enters the log.
|
|
376
|
+
* @returns the sequence number of the committed record.
|
|
377
|
+
* @throws when `type` is outside that grammar, when `data` is not losslessly
|
|
378
|
+
* JSON-serializable, or when the call reenters another append's publication;
|
|
379
|
+
* a rejected record does not change the log.
|
|
380
|
+
*/
|
|
381
|
+
export function appendPluginRecord(session, type, data) {
|
|
382
|
+
return commitPluginRecord(session, type, data);
|
|
383
|
+
}
|
|
384
|
+
/**
|
|
385
|
+
* Read one committed event as a plugin record. The V3-to-V4 format edge also
|
|
386
|
+
* renames each unknown ignorable V3 event into the `plugin:` namespace, so an
|
|
387
|
+
* owner names its records under its own package name and validates `data`.
|
|
388
|
+
* @param event - any committed Session event, live or restored.
|
|
389
|
+
* @returns the record, or undefined when the event is not an ignorable event
|
|
390
|
+
* whose type follows the plugin record grammar.
|
|
391
|
+
*/
|
|
392
|
+
export function pluginRecordOf(event) {
|
|
393
|
+
const type = event.type;
|
|
394
|
+
if (event.ignorable !== true || !isPluginRecordType(type))
|
|
395
|
+
return undefined;
|
|
396
|
+
return { type, seq: event.seq, time: event.time, data: event.data };
|
|
397
|
+
}
|
|
327
398
|
/**
|
|
328
399
|
* An event-sourced session: an append-only log of {@link SessionEvent}s.
|
|
329
400
|
*
|
|
@@ -578,10 +649,7 @@ export class Session {
|
|
|
578
649
|
if (surfaceMetadataSnapshot === undefined) {
|
|
579
650
|
throw new Error(`session event "${type}" carries non-JSON-serializable surface metadata`);
|
|
580
651
|
}
|
|
581
|
-
const entry =
|
|
582
|
-
if (entry?.appending) {
|
|
583
|
-
throw new Error('session append cannot reenter while another append is being published');
|
|
584
|
-
}
|
|
652
|
+
const entry = this.publicationEntry();
|
|
585
653
|
const event = deepFreeze({
|
|
586
654
|
type,
|
|
587
655
|
seq: SessionSeq(this.log.length),
|
|
@@ -590,6 +658,54 @@ export class Session {
|
|
|
590
658
|
...surfaceMetadataSnapshot,
|
|
591
659
|
});
|
|
592
660
|
validateSessionEventData(event, `session event "${type}" at seq ${event.seq}`);
|
|
661
|
+
this.commit(event, entry);
|
|
662
|
+
return event;
|
|
663
|
+
}
|
|
664
|
+
static {
|
|
665
|
+
commitPluginRecord = (session, type, data) => session.#appendRecord(type, data);
|
|
666
|
+
}
|
|
667
|
+
/**
|
|
668
|
+
* Commit one plugin record; {@link appendPluginRecord} is the only caller.
|
|
669
|
+
* @param type - record type, checked against the plugin record grammar.
|
|
670
|
+
* @param data - JSON payload, snapshotted before it enters the log.
|
|
671
|
+
* @returns the record's sequence number.
|
|
672
|
+
*/
|
|
673
|
+
#appendRecord(type, data) {
|
|
674
|
+
const dataSnapshot = snapshotJsonValue(data);
|
|
675
|
+
if (dataSnapshot === undefined) {
|
|
676
|
+
throw new Error(`plugin record "${type}" carries non-JSON-serializable data`);
|
|
677
|
+
}
|
|
678
|
+
const entry = this.publicationEntry();
|
|
679
|
+
const record = deepFreeze({
|
|
680
|
+
type,
|
|
681
|
+
seq: SessionSeq(this.log.length),
|
|
682
|
+
time: Date.now(),
|
|
683
|
+
data: dataSnapshot,
|
|
684
|
+
ignorable: true,
|
|
685
|
+
});
|
|
686
|
+
assertPluginRecordEvent(record);
|
|
687
|
+
this.commit(record, entry);
|
|
688
|
+
return record.seq;
|
|
689
|
+
}
|
|
690
|
+
/**
|
|
691
|
+
* Read the store attachment for one append, refusing an append that
|
|
692
|
+
* reenters while another append is being published.
|
|
693
|
+
* @returns the attachment, or undefined for a detached Session.
|
|
694
|
+
*/
|
|
695
|
+
publicationEntry() {
|
|
696
|
+
const entry = attachments.get(this);
|
|
697
|
+
if (entry?.appending) {
|
|
698
|
+
throw new Error('session append cannot reenter while another append is being published');
|
|
699
|
+
}
|
|
700
|
+
return entry;
|
|
701
|
+
}
|
|
702
|
+
/**
|
|
703
|
+
* Accept one frozen candidate at the next sequence number, enter it into the
|
|
704
|
+
* log, and notify `session/event` observers with per-listener containment.
|
|
705
|
+
* @param event - the candidate built at the current log length.
|
|
706
|
+
* @param entry - the attachment read before the candidate was built.
|
|
707
|
+
*/
|
|
708
|
+
commit(event, entry) {
|
|
593
709
|
this.surfaceManager.validateNext(event);
|
|
594
710
|
if (entry !== undefined)
|
|
595
711
|
entry.appending = true;
|
|
@@ -604,7 +720,6 @@ export class Session {
|
|
|
604
720
|
if (callbacks !== undefined && entry !== undefined) {
|
|
605
721
|
invokeContainedSessionObservers(entry.emitCtx, 'session/event', entry.id, callbackArgs, callbacks);
|
|
606
722
|
}
|
|
607
|
-
return event;
|
|
608
723
|
}
|
|
609
724
|
finally {
|
|
610
725
|
if (entry !== undefined) {
|
|
@@ -77,6 +77,7 @@ export const KNOWN_SESSION_EVENT_TYPES = new Set([
|
|
|
77
77
|
'turn/start',
|
|
78
78
|
'user/message',
|
|
79
79
|
'web/deepseek-search-llm-request',
|
|
80
|
+
'working-directory/change',
|
|
80
81
|
'workspace/changes',
|
|
81
82
|
]);
|
|
82
83
|
/** Event types whose model-visible effects require an explicit pure interpreter. */
|
package/lib/types/types.d.ts
CHANGED
|
@@ -36,20 +36,20 @@ export type OptionalSessionSeq = SessionSeq | null;
|
|
|
36
36
|
* event-body read composes the build-static adjacent chain and publishes only
|
|
37
37
|
* this final generation before constructing a Session.
|
|
38
38
|
*
|
|
39
|
-
* The version is a single monotonic integer with no major/minor split.
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
* `.agents/notes/implemented/
|
|
39
|
+
* The version is a single monotonic integer with no major/minor split. Retain
|
|
40
|
+
* it when older readers resume correctly, preserve safely ignorable records,
|
|
41
|
+
* or refuse required features before execution, mutation, or tail repair.
|
|
42
|
+
* New readers must preserve historical meanings, including logs mixing old
|
|
43
|
+
* and new record representations. Bump when an existing discriminator cannot
|
|
44
|
+
* prevent unsafe interpretation; a structural difference alone is insufficient.
|
|
45
|
+
* Unknown fields and values require evidence of the released reader's actual
|
|
46
|
+
* admission behavior. The per-event {@link SessionEvent.ignorable} marker
|
|
47
|
+
* governs unknown event types, not arbitrary nested payload changes.
|
|
48
|
+
* Same-version readers retain earlier representations: the adjacent migration
|
|
49
|
+
* chain does not run when stored and current versions match. A subsequent
|
|
50
|
+
* migration must account for all supported representations of its source.
|
|
51
|
+
* Compatibility review and the retained migration obligations are recorded in
|
|
52
|
+
* `.agents/notes/implemented/process/2026-10-08-session-reader-compatibility-review.md`.
|
|
53
53
|
*/
|
|
54
54
|
export declare const SESSION_FORMAT_VERSION = 4;
|
|
55
55
|
/**
|
|
@@ -510,6 +510,37 @@ export type SessionEvent<T extends SessionEventType = SessionEventType> = {
|
|
|
510
510
|
sourceEventSeqs?: never;
|
|
511
511
|
});
|
|
512
512
|
}[T];
|
|
513
|
+
/**
|
|
514
|
+
* Event type of an experimental plugin record: `plugin:` followed by
|
|
515
|
+
* slash-separated segments of lowercase letters, digits, `.`, `_`, and `-`,
|
|
516
|
+
* each starting with a letter or digit. A record type is never a
|
|
517
|
+
* {@link SessionEventMap} member.
|
|
518
|
+
*/
|
|
519
|
+
export type PluginRecordType = `plugin:${string}`;
|
|
520
|
+
/**
|
|
521
|
+
* Payloads written by experimental packages, keyed by their `plugin:` record
|
|
522
|
+
* names. Packages augment this map to type writes and enter the current plugin
|
|
523
|
+
* record catalog; these declarations do not enter {@link SessionEventMap} or
|
|
524
|
+
* released persistence schemas. Stored records still require owner validation.
|
|
525
|
+
*/
|
|
526
|
+
export interface PluginRecordMap {
|
|
527
|
+
}
|
|
528
|
+
/**
|
|
529
|
+
* One committed experimental plugin record, read from the log by
|
|
530
|
+
* `pluginRecordOf`. The record's owner validates `data` before use, because a
|
|
531
|
+
* restored record carries whatever JSON an earlier build of its owner wrote,
|
|
532
|
+
* or a V3 event that a format migration renamed into the `plugin:` namespace held.
|
|
533
|
+
*/
|
|
534
|
+
export interface PluginRecord {
|
|
535
|
+
/** The record type, chosen by the owning plugin. */
|
|
536
|
+
readonly type: PluginRecordType;
|
|
537
|
+
/** The record's position in the Session log. */
|
|
538
|
+
readonly seq: SessionSeq;
|
|
539
|
+
/** Unix epoch milliseconds at append. */
|
|
540
|
+
readonly time: number;
|
|
541
|
+
/** The JSON payload as committed. */
|
|
542
|
+
readonly data: unknown;
|
|
543
|
+
}
|
|
513
544
|
declare module '@deepseek-ai/dsh-typert-protocol' {
|
|
514
545
|
interface RemoteErrorDetailsMap {
|
|
515
546
|
/** The named Session does not exist; produced by every layer that resolves a SessionId. */
|
package/lib/types/types.js
CHANGED
|
@@ -36,20 +36,20 @@ export function SessionLogOffset(value) {
|
|
|
36
36
|
* event-body read composes the build-static adjacent chain and publishes only
|
|
37
37
|
* this final generation before constructing a Session.
|
|
38
38
|
*
|
|
39
|
-
* The version is a single monotonic integer with no major/minor split.
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
* `.agents/notes/implemented/
|
|
39
|
+
* The version is a single monotonic integer with no major/minor split. Retain
|
|
40
|
+
* it when older readers resume correctly, preserve safely ignorable records,
|
|
41
|
+
* or refuse required features before execution, mutation, or tail repair.
|
|
42
|
+
* New readers must preserve historical meanings, including logs mixing old
|
|
43
|
+
* and new record representations. Bump when an existing discriminator cannot
|
|
44
|
+
* prevent unsafe interpretation; a structural difference alone is insufficient.
|
|
45
|
+
* Unknown fields and values require evidence of the released reader's actual
|
|
46
|
+
* admission behavior. The per-event {@link SessionEvent.ignorable} marker
|
|
47
|
+
* governs unknown event types, not arbitrary nested payload changes.
|
|
48
|
+
* Same-version readers retain earlier representations: the adjacent migration
|
|
49
|
+
* chain does not run when stored and current versions match. A subsequent
|
|
50
|
+
* migration must account for all supported representations of its source.
|
|
51
|
+
* Compatibility review and the retained migration obligations are recorded in
|
|
52
|
+
* `.agents/notes/implemented/process/2026-10-08-session-reader-compatibility-review.md`.
|
|
53
53
|
*/
|
|
54
54
|
export const SESSION_FORMAT_VERSION = 4;
|
|
55
55
|
//# sourceMappingURL=types.js.map
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@deepseek-ai/dsh-session",
|
|
3
3
|
"description": "Event-sourced session store for the DeepSeek Harness",
|
|
4
|
-
"version": "0.2.1-alpha.
|
|
4
|
+
"version": "0.2.1-alpha.2",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
7
7
|
},
|
|
@@ -40,18 +40,18 @@
|
|
|
40
40
|
],
|
|
41
41
|
"license": "MIT",
|
|
42
42
|
"peerDependencies": {
|
|
43
|
-
"@deepseek-ai/dsh-scope": "0.2.1-alpha.
|
|
43
|
+
"@deepseek-ai/dsh-scope": "0.2.1-alpha.2",
|
|
44
44
|
"@deepseek-ai/cordis": "~4.0.5-alpha.1"
|
|
45
45
|
},
|
|
46
46
|
"devDependencies": {
|
|
47
|
-
"@deepseek-ai/dsh-scope": "0.2.1-alpha.
|
|
48
|
-
"@deepseek-ai/dsh-typert-protocol": "0.2.1-alpha.
|
|
49
|
-
"@deepseek-ai/dsh-typert-registry": "0.2.1-alpha.
|
|
47
|
+
"@deepseek-ai/dsh-scope": "0.2.1-alpha.2",
|
|
48
|
+
"@deepseek-ai/dsh-typert-protocol": "0.2.1-alpha.2",
|
|
49
|
+
"@deepseek-ai/dsh-typert-registry": "0.2.1-alpha.2",
|
|
50
50
|
"@deepseek-ai/cordis": "~4.0.5-alpha.1"
|
|
51
51
|
},
|
|
52
52
|
"dependencies": {
|
|
53
|
-
"@deepseek-ai/dsh-llm": "0.2.1-alpha.
|
|
54
|
-
"@deepseek-ai/dsh-brand": "0.2.1-alpha.
|
|
55
|
-
"@deepseek-ai/dsh-util-values": "0.2.1-alpha.
|
|
53
|
+
"@deepseek-ai/dsh-llm": "0.2.1-alpha.2",
|
|
54
|
+
"@deepseek-ai/dsh-brand": "0.2.1-alpha.2",
|
|
55
|
+
"@deepseek-ai/dsh-util-values": "0.2.1-alpha.2"
|
|
56
56
|
}
|
|
57
57
|
}
|