@deepseek-ai/dsh-session-persistence-jsonl 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 +2 -2
- package/README.md +4 -0
- package/README.zh.md +4 -0
- package/lib/index.js +23 -19
- package/lib/types/migration-verifier.d.ts +1 -1
- package/lib/worker.cjs +92 -12
- package/package.json +12 -12
package/README.i18n.yaml
CHANGED
|
@@ -30,8 +30,8 @@
|
|
|
30
30
|
en: 1eaa87736cac029b
|
|
31
31
|
zh: 26224e8889786d69
|
|
32
32
|
/deepseek-ai-dsh-session-persistence-jsonl/use-this-package/reading-the-logs:
|
|
33
|
-
en:
|
|
34
|
-
zh:
|
|
33
|
+
en: 3e53fc90a486d373
|
|
34
|
+
zh: 00e6c6e65c56629e
|
|
35
35
|
/deepseek-ai-dsh-session-persistence-jsonl/understand-the-implementation:
|
|
36
36
|
en: a35407bb25671e7e
|
|
37
37
|
zh: 6a00f9510c85adcb
|
package/README.md
CHANGED
|
@@ -79,8 +79,12 @@ The current-generation scanner applies the current codec owner’s structural ad
|
|
|
79
79
|
|
|
80
80
|
### Reading the logs
|
|
81
81
|
|
|
82
|
+
Materialized `stat` and `list` snapshots include the format catalog's header classification as `formatStatus`, alongside the translated logical `header`. A read-only preparation leaves the selected historical version unchanged; the status becomes `current` after publication of the successor. Unmaterialized pending Sessions omit `formatStatus`.
|
|
83
|
+
|
|
82
84
|
`open(id, 'read'|'write')` selects the highest canonical generation. Current input follows the ordinary fast path. For historical input, a read open decodes and migrates the source once, validates the current logical result, and returns it without publishing a successor. A write open reuses that revision-keyed preparation when available, or performs the same preparation, then encodes a same-directory temporary file in bounded chunks, verifies it in a Worker Thread, rechecks the source revision, and publishes the current successor without overwrite before returning. The source remains byte-identical. Source drift after preparation rejects that write open without replacing the logical history already returned to readers; a later write open prepares the new revision. The backend marks decoded event graphs `shared-frozen` when it freezes them before memoization; every nested object and array is frozen, and handle reads and slices preserve that state, including empty slices. Only an unmaterialized pending log reports `detached`. `stat(id)` and `list()` select and translate only the highest generation header without reading event rows or starting migration; snapshots carry the selected file’s `sizeBytes` and a best-effort revision. Current revisions identify that file; historical revisions also fingerprint the selected files across the persistence root, so child changes invalidate cached logical events. Fingerprinting reads filesystem metadata only; unrelated changes conservatively invalidate historical revisions. One `list()` call shares a corpus fingerprint across its historical entries. With `compression: 'none'`, the log is newline-delimited text an external reader can consume directly; the compressed default must be read through the backend.
|
|
83
85
|
|
|
86
|
+
Verification accepts a successful Worker response only after the Worker exits naturally with code 0. Its concurrency slot remains held through that exit; cancellation and Worker errors terminate and join the Worker before rejecting.
|
|
87
|
+
|
|
84
88
|
Historical body preparation completes the parent catalog through [V3→V4](../session-format-v3-to-v4/README.md). It finds candidate direct children from headers, reads each child's own descriptor through historical codecs, and retains compact evidence plus source revisions. It neither prepares child catalogs nor publishes child successors. Unreadable or unsupported headers, including corrupt Zstandard header frames, are omitted from discovery and `list()`. Direct access to a corrupt compressed header still rejects; header I/O and cancellation errors propagate. Child decoding and descriptor-field failures produce warnings naming the child path and retain header identity through `subagent/catalog` when the parent has no complete entry; healthy children and existing parent catalog entries remain available. Opening a damaged child still reports that child's error. Missing, unsupported, or multiple descriptors likewise produce unknown-mode membership without inventing a label. Published unknown entries remain browsable; child history reads retry the actual log and resolve its mode from a valid descriptor. Preparation rechecks membership and inspected source revisions before returning, reuse, and publication, including failed child reads so a repaired child invalidates stale preparation. Source drift retries a read open once and refuses a write open. Cancellation still aborts the operation. Current V4 opens bypass discovery and validate catalog fields, uniqueness, and current delivery ownership before exposing events.
|
|
85
89
|
|
|
86
90
|
Historical `stat` and `list` revisions require metadata work proportional to the root’s Session count. Fresh body preparation scans all selected headers and decodes direct-child bodies; memo reuse still scans membership and checks revisions. Read-only access never publishes an upgrade, so cold processes and evicted preparations repeat that work. Current V4 body reads and revisions avoid the historical corpus scan. See the [measured costs and diagnostic command](../../../.agents/notes/implemented/architecture/2026-08-31-released-session-format-migrations.md#catalog-scan-measurements).
|
package/README.zh.md
CHANGED
|
@@ -79,8 +79,12 @@ kind: "package-reference"
|
|
|
79
79
|
|
|
80
80
|
### 读取日志
|
|
81
81
|
|
|
82
|
+
已实体化的 `stat` 与 `list` 快照在转换后的逻辑 `header` 之外,通过 `formatStatus` 保留格式 catalog 的头部分类。只读准备不会改变所选历史版本;发布后继之后,状态才变为 `current`。尚未实体化的待定 Session 省略 `formatStatus`。
|
|
83
|
+
|
|
82
84
|
`open(id, 'read'|'write')` 选择最高规范 generation。当前格式输入走普通快速路径。对于历史输入,只读 open 会单遍解码并迁移源、校验当前逻辑结果,然后在不发布后继的情况下返回。写 open 会在可用时复用按 revision 为键的 preparation,否则执行同一套 preparation,再按有界分片编码同目录临时文件、在 Worker Thread 中校验、复查源修订,并在返回前以不覆盖方式发布当前后继。源保持逐字节不变。如果源在 preparation 后发生变化,该次写 open 会失败,已经返回给读方的逻辑历史不会被替换;后续写 open 会针对新的 revision 重新执行 preparation。后端在 memo 化前冻结已解码的 event graph,并在此时将其标记为 `shared-frozen`;每个嵌套对象和数组都会冻结,句柄读取和 slice 即使为空也保留该状态。只有尚未实体化的 pending 空日志报告 `detached`。`stat(id)` 与 `list()` 只选择并转换最高 generation 的 header,不读取事件行,也不启动迁移;快照携带所选文件的 `sizeBytes` 与尽力而为的修订号。当前格式修订号标识该文件;历史格式修订号还包含持久化根目录中所选文件的指纹,因此子日志变化会使缓存的逻辑事件失效。指纹只读取文件系统元数据;无关变化也会保守地使历史修订号失效。一次 `list()` 为其历史条目共用一个全库指纹。选择 `compression: 'none'` 后,日志是外部读取方可直接消费的换行分隔文本;压缩默认值必须经后端读取。
|
|
83
85
|
|
|
86
|
+
校验只有在 Worker 自然以退出码 0 结束后,才接受其成功响应。并发配额一直保留到该次退出;取消或 Worker 错误会先终止并等待 Worker 退出,再使校验失败。
|
|
87
|
+
|
|
84
88
|
历史正文准备通过 [V3→V4](../session-format-v3-to-v4/README.zh.md) 补齐父目录:从 header 找到候选直属子 Session,通过历史编解码器逐个读取其自身 descriptor,保留紧凑证据与来源修订。此过程不准备子目录,也不发布子后继。不可读或不支持的 header(包括损坏的 Zstandard header 帧)不参与发现,也不出现在 `list()` 中。直接访问损坏的压缩 header 仍会失败;header 的 I/O 错误与取消错误继续传播。子日志解码或 descriptor 字段失败会产生带子路径的警告;父目录没有完整条目时,通过 `subagent/catalog` 保留 header 身份信息。健康子项和已有父目录项仍可使用。打开损坏子 Session 时仍报告该子会话的错误。缺失、不支持或多个 descriptor 同样生成模式未知的目录项,不编造标签。已发布的未知条目仍可浏览;读取子历史时会重试实际日志,并从有效 descriptor 确定模式。准备返回、复用与发布前会重新检查成员集合及已检查来源的修订,也包括读取失败的子日志,使修复后的子日志能够使旧准备缓存失效。来源变化时只读打开重试一次,写打开拒绝发布。取消仍会中止操作。当前 V4 打开跳过发现,并在暴露事件前校验目录字段、唯一性及当前投递归属。
|
|
85
89
|
|
|
86
90
|
历史格式的 `stat` 与 `list` 修订号需要与根目录 Session 数量成正比的元数据工作。首次正文准备扫描所有所选 header 并解码直属子正文;复用准备缓存仍扫描成员集合并检查修订。只读访问从不发布升级,因此冷进程与被淘汰的准备缓存会重复这些工作。当前 V4 正文读取和修订号避开历史全库扫描。见[实测成本与诊断命令](../../../.agents/notes/implemented/architecture/2026-08-31-released-session-format-migrations.zh.md#catalog-scan-measurements)。
|
package/lib/index.js
CHANGED
|
@@ -1476,7 +1476,7 @@ function workerSpawn(request) {
|
|
|
1476
1476
|
* @param expectedEventCount - exact logical event count expected after decoding.
|
|
1477
1477
|
* @param expectedPrefix - verified physical prefix; an append tail may be present and is not validated.
|
|
1478
1478
|
* @param signal - optional cancellation for scheduler wait and Worker execution.
|
|
1479
|
-
* @returns
|
|
1479
|
+
* @returns the worker's physical identity and digest after its natural zero exit.
|
|
1480
1480
|
*/
|
|
1481
1481
|
function verifyCurrentGenerationInWorker(path, compression, expectedId, expectedEventCount, expectedPrefix, signal) {
|
|
1482
1482
|
return verificationScheduler.run(() => runVerificationWorker(path, compression, expectedId, expectedEventCount, expectedPrefix, signal), signal);
|
|
@@ -1493,6 +1493,7 @@ function runVerificationWorker(path, compression, expectedId, expectedEventCount
|
|
|
1493
1493
|
const worker = new Worker(entry, options);
|
|
1494
1494
|
return new Promise((resolve, reject) => {
|
|
1495
1495
|
let settled = false;
|
|
1496
|
+
let success;
|
|
1496
1497
|
const cleanup = () => {
|
|
1497
1498
|
signal?.removeEventListener("abort", abort);
|
|
1498
1499
|
};
|
|
@@ -1521,17 +1522,16 @@ function runVerificationWorker(path, compression, expectedId, expectedEventCount
|
|
|
1521
1522
|
fail(error);
|
|
1522
1523
|
return;
|
|
1523
1524
|
}
|
|
1524
|
-
|
|
1525
|
-
cleanup();
|
|
1526
|
-
worker.terminate().then(() => {
|
|
1527
|
-
resolve(response.result);
|
|
1528
|
-
}, (error) => {
|
|
1529
|
-
reject(error instanceof Error ? error : new Error(String(error)));
|
|
1530
|
-
});
|
|
1525
|
+
success = response;
|
|
1531
1526
|
});
|
|
1532
1527
|
worker.once("error", fail);
|
|
1533
1528
|
worker.once("exit", (code) => {
|
|
1534
|
-
if (
|
|
1529
|
+
if (settled) return;
|
|
1530
|
+
settled = true;
|
|
1531
|
+
cleanup();
|
|
1532
|
+
if (success === void 0) reject(/* @__PURE__ */ new Error(`migration verifier exited before reporting a result (code ${code})`));
|
|
1533
|
+
else if (code !== 0) reject(/* @__PURE__ */ new Error(`migration verifier exited after reporting a result (code ${code})`));
|
|
1534
|
+
else resolve(success.result);
|
|
1535
1535
|
});
|
|
1536
1536
|
const abort = () => {
|
|
1537
1537
|
fail(verifierAbortError(signal));
|
|
@@ -2544,13 +2544,13 @@ var JsonlSessionPersistence = class extends SessionPersistence {
|
|
|
2544
2544
|
};
|
|
2545
2545
|
const selected = await this.findLog(id, options?.signal);
|
|
2546
2546
|
if (selected === void 0) return void 0;
|
|
2547
|
-
const
|
|
2548
|
-
if (
|
|
2547
|
+
const metadata = await this.readGenerationHeader(selected, id, options?.signal);
|
|
2548
|
+
if (metadata === void 0) return void 0;
|
|
2549
2549
|
try {
|
|
2550
2550
|
const identity = await stat(selected.sourcePath, { bigint: true });
|
|
2551
2551
|
options?.signal?.throwIfAborted();
|
|
2552
2552
|
return {
|
|
2553
|
-
|
|
2553
|
+
...metadata,
|
|
2554
2554
|
revision: selected.sourceVersion < SESSION_FORMAT_VERSION ? SessionPersistenceRevision(`${fileRevision(identity)}:${await this.historicalCorpusRevision(options?.signal)}`) : fileRevision(identity),
|
|
2555
2555
|
sizeBytes: Number(identity.size)
|
|
2556
2556
|
};
|
|
@@ -2581,6 +2581,7 @@ var JsonlSessionPersistence = class extends SessionPersistence {
|
|
|
2581
2581
|
listed.add(artifact.header.id);
|
|
2582
2582
|
snapshots.push({
|
|
2583
2583
|
header: artifact.header,
|
|
2584
|
+
formatStatus: artifact.formatStatus,
|
|
2584
2585
|
revision: artifact.sourceVersion < SESSION_FORMAT_VERSION ? SessionPersistenceRevision(`${fileRevision(identity)}:${corpusRevision}`) : fileRevision(identity),
|
|
2585
2586
|
sizeBytes: Number(identity.size)
|
|
2586
2587
|
});
|
|
@@ -3036,18 +3037,18 @@ var JsonlSessionPersistence = class extends SessionPersistence {
|
|
|
3036
3037
|
const ids = /* @__PURE__ */ new Set();
|
|
3037
3038
|
for (const selected of await this.listGenerations(signal)) {
|
|
3038
3039
|
signal?.throwIfAborted();
|
|
3039
|
-
let
|
|
3040
|
+
let metadata;
|
|
3040
3041
|
try {
|
|
3041
|
-
|
|
3042
|
+
metadata = await this.readGenerationHeader(selected, void 0, signal);
|
|
3042
3043
|
} catch (error) {
|
|
3043
3044
|
if (error instanceof SessionFormatUnsupportedError || error instanceof SessionPersistenceCorruptionError) continue;
|
|
3044
3045
|
throw error;
|
|
3045
3046
|
}
|
|
3046
|
-
if (
|
|
3047
|
-
if (ids.has(header.id)) throw new Error(`duplicate JSONL session id "${header.id}" appears in multiple project directories`);
|
|
3048
|
-
ids.add(header.id);
|
|
3047
|
+
if (metadata === void 0) continue;
|
|
3048
|
+
if (ids.has(metadata.header.id)) throw new Error(`duplicate JSONL session id "${metadata.header.id}" appears in multiple project directories`);
|
|
3049
|
+
ids.add(metadata.header.id);
|
|
3049
3050
|
artifacts.push({
|
|
3050
|
-
|
|
3051
|
+
...metadata,
|
|
3051
3052
|
path: selected.sourcePath,
|
|
3052
3053
|
sourceVersion: selected.sourceVersion
|
|
3053
3054
|
});
|
|
@@ -3089,7 +3090,10 @@ var JsonlSessionPersistence = class extends SessionPersistence {
|
|
|
3089
3090
|
if (result.status === "malformed") return void 0;
|
|
3090
3091
|
const header = this.currentHeader(result.header);
|
|
3091
3092
|
await this.assertStoredIdentity(selected.sourcePath, selected.sourceVersion, header, expectedId, signal);
|
|
3092
|
-
return
|
|
3093
|
+
return {
|
|
3094
|
+
header,
|
|
3095
|
+
formatStatus: result.status
|
|
3096
|
+
};
|
|
3093
3097
|
}
|
|
3094
3098
|
/** Convert format-catalog string identities to current branded Session metadata. */
|
|
3095
3099
|
currentHeader(header) {
|
|
@@ -9,7 +9,7 @@ import type { JsonlExpectedPrefix, JsonlVerifiedGeneration } from './generation.
|
|
|
9
9
|
* @param expectedEventCount - exact logical event count expected after decoding.
|
|
10
10
|
* @param expectedPrefix - verified physical prefix; an append tail may be present and is not validated.
|
|
11
11
|
* @param signal - optional cancellation for scheduler wait and Worker execution.
|
|
12
|
-
* @returns
|
|
12
|
+
* @returns the worker's physical identity and digest after its natural zero exit.
|
|
13
13
|
*/
|
|
14
14
|
export declare function verifyCurrentGenerationInWorker(path: string, compression: JsonlCompression, expectedId: string, expectedEventCount: number, expectedPrefix?: JsonlExpectedPrefix, signal?: AbortSignal): Promise<JsonlVerifiedGeneration>;
|
|
15
15
|
//# sourceMappingURL=migration-verifier.d.ts.map
|
package/lib/worker.cjs
CHANGED
|
@@ -3184,6 +3184,7 @@ const KNOWN_SESSION_EVENT_TYPES = new Set([
|
|
|
3184
3184
|
"turn/start",
|
|
3185
3185
|
"user/message",
|
|
3186
3186
|
"web/deepseek-search-llm-request",
|
|
3187
|
+
"working-directory/change",
|
|
3187
3188
|
"workspace/changes"
|
|
3188
3189
|
]);
|
|
3189
3190
|
/** Event types whose model-visible effects require an explicit pure interpreter. */
|
|
@@ -5827,20 +5828,33 @@ var LlmError = class extends HarnessError {
|
|
|
5827
5828
|
* Resolve one call under its current adapter registration. The returned
|
|
5828
5829
|
* one-shot handle keeps that registration across header logging and dispatch,
|
|
5829
5830
|
* so HMR cannot combine one adapter's capability result with another adapter.
|
|
5830
|
-
*
|
|
5831
|
+
* An optional synchronous callback selects concrete controls using captured
|
|
5832
|
+
* model metadata. Defaults and validation apply to its result. Callback
|
|
5833
|
+
* failures and cancellation reject preparation before dispatch.
|
|
5834
|
+
* @param config - provider/model route and optional concrete request controls.
|
|
5831
5835
|
* @param signal - optional cancellation for adapter-owned capability lookup.
|
|
5836
|
+
* @param configure - pure control selection, called once before defaults and validation.
|
|
5832
5837
|
* @returns a prepared config and its registration-bound stream entry point.
|
|
5833
5838
|
*/
|
|
5834
|
-
async prepareCall(config, signal) {
|
|
5835
|
-
|
|
5836
|
-
const
|
|
5837
|
-
const
|
|
5838
|
-
const
|
|
5839
|
+
async prepareCall(config, signal, configure) {
|
|
5840
|
+
signal?.throwIfAborted();
|
|
5841
|
+
const { provider, model, ...controls } = config;
|
|
5842
|
+
const registration = this.registration(provider);
|
|
5843
|
+
const adapterCall = await registration.adapter.prepareCall(provider, model, signal);
|
|
5844
|
+
signal?.throwIfAborted();
|
|
5845
|
+
const modelInfo = this.normalizeModelInfo(registration, model, adapterCall.model);
|
|
5846
|
+
const proposed = configure === void 0 ? config : {
|
|
5847
|
+
...configure(deepFreeze(structuredClone(controls)), deepFreeze(modelInfo)),
|
|
5848
|
+
provider,
|
|
5849
|
+
model
|
|
5850
|
+
};
|
|
5851
|
+
signal?.throwIfAborted();
|
|
5852
|
+
const resolved = this.resolveCallWithInfo(proposed, modelInfo);
|
|
5839
5853
|
const resolvedConfig = deepFreeze(structuredClone(resolved.config));
|
|
5840
5854
|
const context = resolved.context === void 0 ? void 0 : deepFreeze(structuredClone(resolved.context));
|
|
5841
5855
|
const adapterDefaults = deepFreeze({
|
|
5842
|
-
...
|
|
5843
|
-
...
|
|
5856
|
+
...proposed.reasoningEffort === void 0 && resolvedConfig.reasoningEffort !== void 0 ? { reasoningEffort: true } : {},
|
|
5857
|
+
...proposed.maxTokens === void 0 && resolvedConfig.maxTokens !== void 0 ? { maxTokens: true } : {}
|
|
5844
5858
|
});
|
|
5845
5859
|
let dispatched = false;
|
|
5846
5860
|
return Object.freeze({
|
|
@@ -6321,6 +6335,32 @@ function invokeContainedSessionObservers(ctx, name, id, args, callbacks) {
|
|
|
6321
6335
|
/** Store attachment for the append path; module-private to keep Session store-agnostic publicly. */
|
|
6322
6336
|
const attachments = /* @__PURE__ */ new WeakMap();
|
|
6323
6337
|
/**
|
|
6338
|
+
* Grammar of a {@link PluginRecordType}: `plugin:` followed by
|
|
6339
|
+
* slash-separated segments of lowercase letters, digits, `.`, `_`, and `-`,
|
|
6340
|
+
* each segment starting with a letter or digit.
|
|
6341
|
+
*/
|
|
6342
|
+
const PLUGIN_RECORD_TYPE = /^plugin:[a-z0-9][a-z0-9._-]*(?:\/[a-z0-9][a-z0-9._-]*)*$/u;
|
|
6343
|
+
/**
|
|
6344
|
+
* Whether one event type name follows the plugin record grammar.
|
|
6345
|
+
* @param type - an event type name.
|
|
6346
|
+
* @returns whether the name is a {@link PluginRecordType}.
|
|
6347
|
+
*/
|
|
6348
|
+
function isPluginRecordType(type) {
|
|
6349
|
+
return PLUGIN_RECORD_TYPE.test(type);
|
|
6350
|
+
}
|
|
6351
|
+
/**
|
|
6352
|
+
* Type one constructed plugin record as a log event after checking its type
|
|
6353
|
+
* against the plugin record grammar. A restored Session already holds events
|
|
6354
|
+
* whose types are outside `SessionEventMap` when they are marked ignorable;
|
|
6355
|
+
* a plugin record is such an event.
|
|
6356
|
+
* @param value - the frozen record envelope built by {@link Session}.
|
|
6357
|
+
* @throws when the record type is outside the plugin record grammar.
|
|
6358
|
+
*/
|
|
6359
|
+
function assertPluginRecordEvent(value) {
|
|
6360
|
+
const { type } = value;
|
|
6361
|
+
if (!isPluginRecordType(type)) throw new Error(`plugin record type "${type}" must be "plugin:" followed by lowercase slash-separated segments`);
|
|
6362
|
+
}
|
|
6363
|
+
/**
|
|
6324
6364
|
* An event-sourced session: an append-only log of {@link SessionEvent}s.
|
|
6325
6365
|
*
|
|
6326
6366
|
* Plain class (not a Service) — create live instances via
|
|
@@ -6531,8 +6571,7 @@ var Session = class Session {
|
|
|
6531
6571
|
if (dataSnapshot === void 0) throw new Error(`session event "${type}" carries non-JSON-serializable data`);
|
|
6532
6572
|
const surfaceMetadataSnapshot = snapshotJsonValue(surfaceMetadata);
|
|
6533
6573
|
if (surfaceMetadataSnapshot === void 0) throw new Error(`session event "${type}" carries non-JSON-serializable surface metadata`);
|
|
6534
|
-
const entry =
|
|
6535
|
-
if (entry?.appending) throw new Error("session append cannot reenter while another append is being published");
|
|
6574
|
+
const entry = this.publicationEntry();
|
|
6536
6575
|
const event = deepFreeze({
|
|
6537
6576
|
type,
|
|
6538
6577
|
seq: SessionSeq(this.log.length),
|
|
@@ -6541,6 +6580,48 @@ var Session = class Session {
|
|
|
6541
6580
|
...surfaceMetadataSnapshot
|
|
6542
6581
|
});
|
|
6543
6582
|
validateSessionEventData(event, `session event "${type}" at seq ${event.seq}`);
|
|
6583
|
+
this.commit(event, entry);
|
|
6584
|
+
return event;
|
|
6585
|
+
}
|
|
6586
|
+
static {}
|
|
6587
|
+
/**
|
|
6588
|
+
* Commit one plugin record; {@link appendPluginRecord} is the only caller.
|
|
6589
|
+
* @param type - record type, checked against the plugin record grammar.
|
|
6590
|
+
* @param data - JSON payload, snapshotted before it enters the log.
|
|
6591
|
+
* @returns the record's sequence number.
|
|
6592
|
+
*/
|
|
6593
|
+
#appendRecord(type, data) {
|
|
6594
|
+
const dataSnapshot = snapshotJsonValue(data);
|
|
6595
|
+
if (dataSnapshot === void 0) throw new Error(`plugin record "${type}" carries non-JSON-serializable data`);
|
|
6596
|
+
const entry = this.publicationEntry();
|
|
6597
|
+
const record = deepFreeze({
|
|
6598
|
+
type,
|
|
6599
|
+
seq: SessionSeq(this.log.length),
|
|
6600
|
+
time: Date.now(),
|
|
6601
|
+
data: dataSnapshot,
|
|
6602
|
+
ignorable: true
|
|
6603
|
+
});
|
|
6604
|
+
assertPluginRecordEvent(record);
|
|
6605
|
+
this.commit(record, entry);
|
|
6606
|
+
return record.seq;
|
|
6607
|
+
}
|
|
6608
|
+
/**
|
|
6609
|
+
* Read the store attachment for one append, refusing an append that
|
|
6610
|
+
* reenters while another append is being published.
|
|
6611
|
+
* @returns the attachment, or undefined for a detached Session.
|
|
6612
|
+
*/
|
|
6613
|
+
publicationEntry() {
|
|
6614
|
+
const entry = attachments.get(this);
|
|
6615
|
+
if (entry?.appending) throw new Error("session append cannot reenter while another append is being published");
|
|
6616
|
+
return entry;
|
|
6617
|
+
}
|
|
6618
|
+
/**
|
|
6619
|
+
* Accept one frozen candidate at the next sequence number, enter it into the
|
|
6620
|
+
* log, and notify `session/event` observers with per-listener containment.
|
|
6621
|
+
* @param event - the candidate built at the current log length.
|
|
6622
|
+
* @param entry - the attachment read before the candidate was built.
|
|
6623
|
+
*/
|
|
6624
|
+
commit(event, entry) {
|
|
6544
6625
|
this.surfaceManager.validateNext(event);
|
|
6545
6626
|
if (entry !== void 0) entry.appending = true;
|
|
6546
6627
|
try {
|
|
@@ -6554,7 +6635,6 @@ var Session = class Session {
|
|
|
6554
6635
|
this.log.push(event);
|
|
6555
6636
|
this.eventsSnapshot = void 0;
|
|
6556
6637
|
if (callbacks !== void 0 && entry !== void 0) invokeContainedSessionObservers(entry.emitCtx, "session/event", entry.id, callbackArgs, callbacks);
|
|
6557
|
-
return event;
|
|
6558
6638
|
} finally {
|
|
6559
6639
|
if (entry !== void 0) {
|
|
6560
6640
|
entry.appending = false;
|
|
@@ -12201,7 +12281,7 @@ function assertV4LifecycleRelationships(artifact, knownEventTypes) {
|
|
|
12201
12281
|
* @returns validated catalog payload.
|
|
12202
12282
|
*/
|
|
12203
12283
|
function catalogFact(value, subject = "subagent/catalog") {
|
|
12204
|
-
if (!isSessionFormatJsonObject(value) || value["version"] !== 0 && value["version"] !== 1 || typeof value["childId"] !== "string" || value["mode"] !== "continuable" && value["mode"] !== "one-shot" && value["mode"] !== "unknown" || value["version"] === 0 && value["mode"] === "unknown" || value["mode"] === "continuable" && typeof value["label"] !== "string" || value["label"] !== void 0 && typeof value["label"] !== "string") throw new SessionFormatError(`${subject} requires a supported versioned catalog fact`);
|
|
12284
|
+
if (!isSessionFormatJsonObject(value) || value["version"] !== 0 && value["version"] !== 1 && value["version"] !== 2 || typeof value["childId"] !== "string" || value["mode"] !== "continuable" && value["mode"] !== "one-shot" && value["mode"] !== "external" && value["mode"] !== "unknown" || value["version"] === 2 !== (value["mode"] === "external") || value["version"] === 0 && value["mode"] === "unknown" || value["mode"] === "continuable" && typeof value["label"] !== "string" || value["label"] !== void 0 && typeof value["label"] !== "string") throw new SessionFormatError(`${subject} requires a supported versioned catalog fact`);
|
|
12205
12285
|
sessionFormatCount(value["childCreatedAt"], "catalog child creation time");
|
|
12206
12286
|
return value;
|
|
12207
12287
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@deepseek-ai/dsh-session-persistence-jsonl",
|
|
3
3
|
"description": "JSONL durable session persistence backend 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
|
},
|
|
@@ -28,23 +28,23 @@
|
|
|
28
28
|
],
|
|
29
29
|
"license": "MIT",
|
|
30
30
|
"peerDependencies": {
|
|
31
|
-
"@deepseek-ai/dsh-session": "0.2.1-alpha.
|
|
32
|
-
"@deepseek-ai/dsh-session-persistence": "0.2.1-alpha.
|
|
31
|
+
"@deepseek-ai/dsh-session": "0.2.1-alpha.2",
|
|
32
|
+
"@deepseek-ai/dsh-session-persistence": "0.2.1-alpha.2",
|
|
33
33
|
"@deepseek-ai/cordis": "~4.0.5-alpha.1"
|
|
34
34
|
},
|
|
35
35
|
"dependencies": {
|
|
36
|
-
"
|
|
37
|
-
"@deepseek-ai/dsh-session-format": "0.2.1-alpha.
|
|
38
|
-
"@deepseek-ai/dsh-
|
|
39
|
-
"@deepseek-ai/dsh-session-format-v3-to-v4": "0.2.1-alpha.
|
|
40
|
-
"@deepseek-ai/dsh-session-format-catalog": "0.2.1-alpha.1",
|
|
36
|
+
"@deepseek-ai/dsh-llm": "0.2.1-alpha.2",
|
|
37
|
+
"@deepseek-ai/dsh-session-format": "0.2.1-alpha.2",
|
|
38
|
+
"@deepseek-ai/dsh-session-format-catalog": "0.2.1-alpha.2",
|
|
39
|
+
"@deepseek-ai/dsh-session-format-v3-to-v4": "0.2.1-alpha.2",
|
|
41
40
|
"@deepseek-ai/node-addon-system": "~0.1.2",
|
|
42
|
-
"@deepseek-ai/schemastery": "~3.18.5-alpha.1"
|
|
41
|
+
"@deepseek-ai/schemastery": "~3.18.5-alpha.1",
|
|
42
|
+
"koffi": "3.1.1"
|
|
43
43
|
},
|
|
44
44
|
"devDependencies": {
|
|
45
|
-
"@deepseek-ai/dsh-session": "0.2.1-alpha.
|
|
46
|
-
"@deepseek-ai/dsh-session-format-v0-to-v1": "0.2.1-alpha.
|
|
47
|
-
"@deepseek-ai/dsh-session-persistence": "0.2.1-alpha.
|
|
45
|
+
"@deepseek-ai/dsh-session": "0.2.1-alpha.2",
|
|
46
|
+
"@deepseek-ai/dsh-session-format-v0-to-v1": "0.2.1-alpha.2",
|
|
47
|
+
"@deepseek-ai/dsh-session-persistence": "0.2.1-alpha.2",
|
|
48
48
|
"@deepseek-ai/cordis": "~4.0.5-alpha.1"
|
|
49
49
|
}
|
|
50
50
|
}
|