@crazx/dsh-session-persistence 0.1.2-alpha.3.zw.2 → 0.1.2-alpha.5.zw.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.i18n.yaml +2 -2
- package/README.md +6 -6
- package/README.zh.md +6 -6
- package/lib/index.js +88 -31
- package/lib/types/coordinator.d.ts +15 -16
- package/lib/types/index.d.ts +25 -14
- package/package.json +9 -16
- package/lib/invariant.js +0 -23
- package/lib/types/invariant.d.ts +0 -16
package/README.i18n.yaml
CHANGED
|
@@ -2,5 +2,5 @@
|
|
|
2
2
|
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
|
3
3
|
# after editing either side, bring the other along and re-record with:
|
|
4
4
|
# pnpm run verify-translation-pairing --write packages/session/session-persistence/README.md
|
|
5
|
-
README.md:
|
|
6
|
-
README.zh.md:
|
|
5
|
+
README.md: 44457cad08e386de686aabe1a31be27673a59c72
|
|
6
|
+
README.zh.md: 33193b2982e5fd45496abd998a0b38ffdfb71170
|
package/README.md
CHANGED
|
@@ -9,7 +9,7 @@ English | [中文](README.zh.md)
|
|
|
9
9
|
|
|
10
10
|
## Summary
|
|
11
11
|
|
|
12
|
-
`dsh-session-persistence` stores a session's event log durably, reloads it on resume, and lists stored sessions through the backend-neutral `ctx.sessionPersistence` service. The persisted unit is the existing `SessionEvent` log — there is no parallel stored message type
|
|
12
|
+
`dsh-session-persistence` stores a session's event log durably, reloads it on resume, and lists stored sessions through the backend-neutral `ctx.sessionPersistence` service. The persisted unit is the existing `SessionEvent` log — there is no parallel stored message type. `SessionHeader.isSeeded` makes lineage visible to lightweight listing, while the exact `inheritedEventCount` accompanies every body-bearing storage read and prepared Session. A backend owns its storage, while the service owns append-only logs, contiguous sequence numbers, crash recovery that preserves an interrupted turn instead of truncating it, and durable writes that resolve only after the batch is safe. The shipped JSONL provider implements this service with one artifact per Session; third-party providers may implement the same contract without changing the loop or model.
|
|
13
13
|
|
|
14
14
|
## Table of Contents
|
|
15
15
|
|
|
@@ -36,18 +36,18 @@ The seam ships the [JSONL](../session-persistence-jsonl/README.md) backend. It s
|
|
|
36
36
|
With a backend mounted, you can store a session's events durably, reload the stored log, and list what is stored:
|
|
37
37
|
|
|
38
38
|
```text
|
|
39
|
-
await ctx.sessionPersistence.create(meta)
|
|
39
|
+
await ctx.sessionPersistence.create(meta, inheritedEventCount) // cut required when meta.isSeeded
|
|
40
40
|
await ctx.sessionPersistence.ensureMaterialized(session) // persist an empty resumable session
|
|
41
41
|
await ctx.sessionPersistence.append(id, events) // durably persist a batch
|
|
42
|
-
const { meta, events } = await ctx.sessionPersistence.load(id)
|
|
42
|
+
const { meta, inheritedEventCount, events } = await ctx.sessionPersistence.load(id)
|
|
43
43
|
const headers = await ctx.sessionPersistence.list() // every stored session
|
|
44
44
|
```
|
|
45
45
|
|
|
46
|
-
`append` resolves only after the batch is durable, so a resolved write survives an OS crash or power loss. Ordinary `create` remains lazy; a lifecycle frontend calls `ensureMaterialized` only when an empty session must itself appear in durable listing without inventing an event. `load` returns an immutable balanced log and commits any needed crash recovery; `inspect` reads the same view without committing recovery.
|
|
46
|
+
`append` resolves only after the batch is durable, so a resolved write survives an OS crash or power loss. Ordinary `create(meta, inheritedEventCount)` remains lazy; `meta.isSeeded: true` requires the sibling exact cut, while unseeded metadata may omit it and rejects a nonzero value. The first materializing batch for a seeded session must reach the complete inherited prefix, so storage never exposes metadata whose cut exceeds its log. A lifecycle frontend calls `ensureMaterialized` only when an empty session must itself appear in durable listing without inventing an event. `load` returns an immutable balanced log and commits any needed crash recovery; `inspect` reads the same complete view without committing recovery. `readFrom` accepts a `SessionLogOffset` and returns a detached `SessionEventSuffix` carrying that `fromSeq`, the unchanged inherited cut, and only stored events at or after the cut. A session's artifact location (`locate`) resolves without filesystem I/O.
|
|
47
47
|
|
|
48
48
|
### Resuming and crash recovery
|
|
49
49
|
|
|
50
|
-
Resume is `load` plus session preparation: the stored log comes back with its header lineage intact, so
|
|
50
|
+
Resume is `load` plus session preparation: the stored log comes back with its header lineage and exact inherited cut intact, so ownership checks do not infer the cut from a marker or the full restore length. A session that crashed mid-turn reloads with its interrupted final turn preserved and balanced: `load` appends synthetic `tool/result` and `turn/end {interrupted}` closers for unanswered calls instead of dropping the events — a single turn can be large, and those events were durably written before the crash. Only a never-fully-written torn tail fragment is discarded.
|
|
51
51
|
|
|
52
52
|
### Failures and recovery
|
|
53
53
|
|
|
@@ -83,7 +83,7 @@ The package is the Service Definition of a capability seam with two halves. The
|
|
|
83
83
|
| [`src/write-behind.ts`](src/write-behind.ts) | The per-session bounded write controller and flush barrier |
|
|
84
84
|
| [`src/preparations.ts`](src/preparations.ts) | Bounded retention of unpublished Session preparations for resume reuse |
|
|
85
85
|
| [`src/revision.ts`](src/revision.ts) | The branded opaque revision token |
|
|
86
|
-
|
|
|
86
|
+
| — | No runtime invariant companion is published; persistence correctness requires backend round-trip and crash-tail tests; this package exposes no continuously observable in-process relation. |
|
|
87
87
|
|
|
88
88
|
### The write path at a glance
|
|
89
89
|
|
package/README.zh.md
CHANGED
|
@@ -9,7 +9,7 @@ kind: "package-reference"
|
|
|
9
9
|
|
|
10
10
|
## 概述
|
|
11
11
|
|
|
12
|
-
`dsh-session-persistence` 通过后端无关的 `ctx.sessionPersistence` 服务持久存储会话的事件日志、在恢复时重新加载并列出已存储会话。持久化单元就是现有 `SessionEvent`
|
|
12
|
+
`dsh-session-persistence` 通过后端无关的 `ctx.sessionPersistence` 服务持久存储会话的事件日志、在恢复时重新加载并列出已存储会话。持久化单元就是现有 `SessionEvent` 日志——不存在另一套并行的存储消息类型。`SessionHeader.isSeeded` 让轻量列表可见血缘,而精确的 `inheritedEventCount` 随每次带正文的存储读取与 prepared Session 一同传输。后端拥有自己的存储,而服务拥有仅追加日志、连续序列号、保留中断轮次而非截断的崩溃恢复,以及只在批次安全后才返回的持久写入。随产品交付的 JSONL provider 用每个 Session 一份产物实现该服务;第三方 provider 可以实现同一约定,而不改变 loop 或模型。
|
|
13
13
|
|
|
14
14
|
## 目录
|
|
15
15
|
|
|
@@ -36,18 +36,18 @@ seam 随产品交付 [JSONL](../session-persistence-jsonl/README.zh.md) 后端
|
|
|
36
36
|
挂载后端后,你可以持久存储会话事件、重新加载已存储日志并列出已存储内容:
|
|
37
37
|
|
|
38
38
|
```text
|
|
39
|
-
await ctx.sessionPersistence.create(meta)
|
|
39
|
+
await ctx.sessionPersistence.create(meta, inheritedEventCount) // cut required when meta.isSeeded
|
|
40
40
|
await ctx.sessionPersistence.ensureMaterialized(session) // persist an empty resumable session
|
|
41
41
|
await ctx.sessionPersistence.append(id, events) // durably persist a batch
|
|
42
|
-
const { meta, events } = await ctx.sessionPersistence.load(id)
|
|
42
|
+
const { meta, inheritedEventCount, events } = await ctx.sessionPersistence.load(id)
|
|
43
43
|
const headers = await ctx.sessionPersistence.list() // every stored session
|
|
44
44
|
```
|
|
45
45
|
|
|
46
|
-
`append` 只在批次持久后返回,因此成功返回的写入在操作系统崩溃或断电后依然存在。普通 `create`
|
|
46
|
+
`append` 只在批次持久后返回,因此成功返回的写入在操作系统崩溃或断电后依然存在。普通 `create(meta, inheritedEventCount)` 保持惰性;`meta.isSeeded: true` 要求单独的精确 cut,unseeded metadata 可以省略它并拒绝非零值。seeded 会话的首个物化批次必须到达完整继承前缀,因此存储绝不公开 cut 超过日志的 metadata。只有当空会话本身必须出现在持久列表中时,生命周期前端才调用 `ensureMaterialized`,且不会虚构事件。`load` 返回不可变的平衡日志并提交任何需要的崩溃恢复;`inspect` 读取同一份完整视图但不提交恢复。`readFrom` 接受 `SessionLogOffset`,并返回分离的 `SessionEventSuffix`,其中携带该 `fromSeq`、不变的继承 cut,以及 cut 位置或之后的存储事件。会话的产物位置(`locate`)不经文件系统 I/O 即可解析。
|
|
47
47
|
|
|
48
48
|
### 恢复与崩溃恢复
|
|
49
49
|
|
|
50
|
-
恢复就是 `load`
|
|
50
|
+
恢复就是 `load` 加会话准备:存储日志连同其 header 血缘与精确继承切点一起返回,因此所有权检查不从标记或完整恢复长度推断切点。中途崩溃的会话重新加载时,其被中断的最终轮次会保留并保持平衡:`load` 为未获回答的调用追加合成 `tool/result` 与 `turn/end {interrupted}` closer,而不是丢弃事件——单个轮次可能很大,而这些事件在崩溃前已持久写入。只有从未完整写入的撕裂尾部碎片会被丢弃。
|
|
51
51
|
|
|
52
52
|
### 失败与恢复
|
|
53
53
|
|
|
@@ -83,7 +83,7 @@ const headers = await ctx.sessionPersistence.list() // every stored sessi
|
|
|
83
83
|
| [`src/write-behind.ts`](src/write-behind.ts) | 每会话有界写入控制器与 flush 屏障 |
|
|
84
84
|
| [`src/preparations.ts`](src/preparations.ts) | 为恢复复用而有界保留的未发布 Session 准备结果 |
|
|
85
85
|
| [`src/revision.ts`](src/revision.ts) | 带品牌类型的不透明修订值 token |
|
|
86
|
-
|
|
|
86
|
+
| — | 不发布运行时不变式伴生入口;协调器断言存储/活动身份与 cwd。 |
|
|
87
87
|
|
|
88
88
|
### 写入路径概览
|
|
89
89
|
|
package/lib/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Service } from "@deepseek-ai/cordis";
|
|
2
|
-
import { KNOWN_SESSION_EVENT_TYPES, SESSION_FORMAT_VERSION, SessionPreparation, adoptSessionEvent, interruptedTurnClosers, snapshotSessionEvent } from "@deepseek-ai/dsh-session";
|
|
2
|
+
import { KNOWN_SESSION_EVENT_TYPES, SESSION_FORMAT_VERSION, SessionLogOffset, SessionPreparation, SessionSeq, adoptSessionEvent, interruptedTurnClosers, snapshotSessionEvent } from "@deepseek-ai/dsh-session";
|
|
3
3
|
import { MAX_TIMER_DELAY_MS } from "@deepseek-ai/dsh-timeout";
|
|
4
4
|
import { snapshotJsonValue } from "@deepseek-ai/dsh-util-values";
|
|
5
5
|
//#region lib/types/revision.js
|
|
@@ -549,6 +549,20 @@ function seedCoversPrefix(seed, prefix) {
|
|
|
549
549
|
return seedEvent !== void 0 && JSON.stringify(seedEvent) === JSON.stringify(event);
|
|
550
550
|
});
|
|
551
551
|
}
|
|
552
|
+
/** Normalize the exact fork cut paired with one logical Session header. */
|
|
553
|
+
function storageMetadata(meta, inheritedEventCount) {
|
|
554
|
+
if (meta.isSeeded && inheritedEventCount === void 0) throw new TypeError("seeded session metadata requires an inherited event count");
|
|
555
|
+
const cut = SessionLogOffset(inheritedEventCount ?? 0);
|
|
556
|
+
if (!meta.isSeeded && cut !== 0) throw new TypeError("unseeded session metadata inherited event count must be 0");
|
|
557
|
+
return {
|
|
558
|
+
meta,
|
|
559
|
+
inheritedEventCount: cut
|
|
560
|
+
};
|
|
561
|
+
}
|
|
562
|
+
/** Exact storage metadata owned by one live Session. */
|
|
563
|
+
function sessionStorageMetadata(session) {
|
|
564
|
+
return storageMetadata(session.header, session.inheritedEventCount);
|
|
565
|
+
}
|
|
552
566
|
/** Reject events from an obsolete v0 vocabulary that this build cannot replay. */
|
|
553
567
|
function assertSupportedEvents(events, id) {
|
|
554
568
|
const legacyType = "request/header-delta";
|
|
@@ -576,7 +590,12 @@ function legacyMessageId(id, seq) {
|
|
|
576
590
|
/** Read a replacement target while leaving malformed surface metadata to the session validator. */
|
|
577
591
|
function replacementStart(event) {
|
|
578
592
|
const op = asRecord(event.surfaceOp);
|
|
579
|
-
|
|
593
|
+
if (op?.["op"] !== "replace" || typeof op["start"] !== "number") return void 0;
|
|
594
|
+
try {
|
|
595
|
+
return SessionSeq(op["start"]);
|
|
596
|
+
} catch {
|
|
597
|
+
return;
|
|
598
|
+
}
|
|
580
599
|
}
|
|
581
600
|
/** Whether one suffix event needs facts available only from the preceding stored prefix. */
|
|
582
601
|
function needsLegacyPrefix(event) {
|
|
@@ -852,12 +871,21 @@ var PersistenceCoordinator = class {
|
|
|
852
871
|
/**
|
|
853
872
|
* Register detached session metadata for lazy creation on the first append.
|
|
854
873
|
* @param meta - header to snapshot; duplicate tracked or persisted ids reject.
|
|
874
|
+
* @param inheritedEventCount - exact inherited prefix length; required for
|
|
875
|
+
* a seeded header and omitted only for an unseeded header.
|
|
855
876
|
*/
|
|
856
|
-
create(meta) {
|
|
877
|
+
create(meta, inheritedEventCount) {
|
|
857
878
|
const snapshot = snapshotJsonValue(meta);
|
|
858
879
|
if (snapshot === void 0) return Promise.reject(/* @__PURE__ */ new TypeError("session metadata must be losslessly JSON-serializable"));
|
|
859
880
|
if (!Number.isSafeInteger(snapshot.createdAt) || snapshot.createdAt < 0) return Promise.reject(/* @__PURE__ */ new TypeError("session metadata createdAt must be a non-negative safe integer"));
|
|
860
|
-
|
|
881
|
+
let storage;
|
|
882
|
+
try {
|
|
883
|
+
storage = storageMetadata(snapshot, inheritedEventCount);
|
|
884
|
+
} catch (error) {
|
|
885
|
+
/* v8 ignore next -- Session storage validation only throws Error instances. */
|
|
886
|
+
return Promise.reject(error instanceof Error ? error : new TypeError("invalid session storage metadata", { cause: error }));
|
|
887
|
+
}
|
|
888
|
+
return this.serialize(snapshot.id, () => this.createCore(storage));
|
|
861
889
|
}
|
|
862
890
|
/**
|
|
863
891
|
* Materialize one exact live session without inventing a session event.
|
|
@@ -871,17 +899,18 @@ var PersistenceCoordinator = class {
|
|
|
871
899
|
if (state === void 0) throw new Error(`session "${session.id}" is not registered for persistence`);
|
|
872
900
|
if (state.materialized) return;
|
|
873
901
|
if (this.backend.materializeHeader === void 0) throw new Error("session persistence backend cannot materialize an empty session");
|
|
874
|
-
await this.backend.materializeHeader(state.
|
|
902
|
+
await this.backend.materializeHeader(state.storage);
|
|
875
903
|
state.materialized = true;
|
|
876
904
|
this.preparations.invalidate(session.id);
|
|
877
905
|
});
|
|
878
906
|
}
|
|
879
|
-
async createCore(
|
|
907
|
+
async createCore(storage) {
|
|
908
|
+
const { meta } = storage;
|
|
880
909
|
if (this.states.has(meta.id) || this.preparations.has(meta.id)) throw new Error(`session "${meta.id}" already exists in this backend`);
|
|
881
910
|
if (await this.backend.loadStored(meta.id) !== void 0) throw new Error(`session "${meta.id}" already has a persisted log on disk; load/resume it instead of creating`);
|
|
882
911
|
this.states.set(meta.id, {
|
|
883
|
-
|
|
884
|
-
cursor: 0,
|
|
912
|
+
storage,
|
|
913
|
+
cursor: SessionLogOffset(0),
|
|
885
914
|
materialized: false
|
|
886
915
|
});
|
|
887
916
|
}
|
|
@@ -908,9 +937,11 @@ var PersistenceCoordinator = class {
|
|
|
908
937
|
const current = await this.backend.readStoredRevision(id);
|
|
909
938
|
if (current !== state.revision) throw new Error(`session "${id}" log was advanced by another writer (expected revision ${String(state.revision)}, found ${String(current)}): refusing to append — another harness process is sharing this sessions root; continue the session in the process that owns it, or restart it here`);
|
|
910
939
|
}
|
|
940
|
+
const nextCursor = SessionLogOffset(state.cursor + events.length);
|
|
941
|
+
if (!state.materialized && nextCursor < state.storage.inheritedEventCount) throw new Error(`session "${id}" cannot materialize before its inherited prefix is complete`);
|
|
911
942
|
let appended = false;
|
|
912
943
|
try {
|
|
913
|
-
await this.backend.appendBatch(state.
|
|
944
|
+
await this.backend.appendBatch(state.storage, events, state.materialized);
|
|
914
945
|
appended = true;
|
|
915
946
|
} finally {
|
|
916
947
|
if (!appended && state.revision !== void 0) try {
|
|
@@ -918,7 +949,7 @@ var PersistenceCoordinator = class {
|
|
|
918
949
|
} catch {}
|
|
919
950
|
}
|
|
920
951
|
state.materialized = true;
|
|
921
|
-
state.cursor
|
|
952
|
+
state.cursor = nextCursor;
|
|
922
953
|
try {
|
|
923
954
|
await this.refreshRevision(id, state);
|
|
924
955
|
} catch {
|
|
@@ -955,7 +986,7 @@ var PersistenceCoordinator = class {
|
|
|
955
986
|
throw new Error(`cannot prepare session "${id}" while it is live`);
|
|
956
987
|
}
|
|
957
988
|
return SessionPreparation.create(reservation.source.session, { release: () => {
|
|
958
|
-
this.preparations.release(reservation, reservation.state.owner === void 0 && reservation.source.session.
|
|
989
|
+
this.preparations.release(reservation, reservation.state.owner === void 0 && reservation.source.session.seq === reservation.source.sessionLength);
|
|
959
990
|
} });
|
|
960
991
|
}
|
|
961
992
|
}
|
|
@@ -1085,10 +1116,15 @@ var PersistenceCoordinator = class {
|
|
|
1085
1116
|
* @param id - persisted session to read.
|
|
1086
1117
|
* @param fromSeq - first event seq to include; a non-negative safe integer.
|
|
1087
1118
|
* @param signal - optional cancellation for queued and backend read work.
|
|
1088
|
-
* @returns stored
|
|
1119
|
+
* @returns stored metadata, the requested offset, and valid events with `seq >= fromSeq`.
|
|
1089
1120
|
*/
|
|
1090
1121
|
readFrom(id, fromSeq, signal) {
|
|
1091
|
-
|
|
1122
|
+
try {
|
|
1123
|
+
SessionLogOffset(fromSeq);
|
|
1124
|
+
} catch (error) {
|
|
1125
|
+
/* v8 ignore next -- Session log-offset validation only throws Error instances. */
|
|
1126
|
+
return Promise.reject(error instanceof Error ? error : new TypeError("invalid session read offset", { cause: error }));
|
|
1127
|
+
}
|
|
1092
1128
|
const retired = Promise.resolve(this.retirements.get(id));
|
|
1093
1129
|
return (signal === void 0 ? retired : observeQueuedAbort(retired, signal, () => false)).then(() => this.serialize(id, () => this.readFromCore(id, fromSeq, signal), signal));
|
|
1094
1130
|
}
|
|
@@ -1110,6 +1146,8 @@ var PersistenceCoordinator = class {
|
|
|
1110
1146
|
const whole = await this.readStoredPrefix(id, signal);
|
|
1111
1147
|
return {
|
|
1112
1148
|
meta: whole.meta,
|
|
1149
|
+
inheritedEventCount: whole.inheritedEventCount,
|
|
1150
|
+
fromSeq,
|
|
1113
1151
|
events: whole.events.filter((event) => event.seq >= fromSeq)
|
|
1114
1152
|
};
|
|
1115
1153
|
}
|
|
@@ -1117,12 +1155,16 @@ var PersistenceCoordinator = class {
|
|
|
1117
1155
|
this.assertEventsSupported(suffix.meta, events);
|
|
1118
1156
|
return {
|
|
1119
1157
|
meta: structuredClone(suffix.meta),
|
|
1158
|
+
inheritedEventCount: SessionLogOffset(suffix.inheritedEventCount),
|
|
1159
|
+
fromSeq,
|
|
1120
1160
|
events
|
|
1121
1161
|
};
|
|
1122
1162
|
}
|
|
1123
1163
|
const whole = await this.readStoredPrefix(id, signal);
|
|
1124
1164
|
return {
|
|
1125
1165
|
meta: whole.meta,
|
|
1166
|
+
inheritedEventCount: whole.inheritedEventCount,
|
|
1167
|
+
fromSeq,
|
|
1126
1168
|
events: whole.events.slice(fromSeq)
|
|
1127
1169
|
};
|
|
1128
1170
|
}
|
|
@@ -1138,6 +1180,7 @@ var PersistenceCoordinator = class {
|
|
|
1138
1180
|
this.assertEventsSupported(stored.meta, events);
|
|
1139
1181
|
return {
|
|
1140
1182
|
meta: structuredClone(stored.meta),
|
|
1183
|
+
inheritedEventCount: SessionLogOffset(stored.inheritedEventCount),
|
|
1141
1184
|
events
|
|
1142
1185
|
};
|
|
1143
1186
|
}
|
|
@@ -1146,26 +1189,29 @@ var PersistenceCoordinator = class {
|
|
|
1146
1189
|
const stored = await this.backend.loadStored(id);
|
|
1147
1190
|
if (stored === void 0) throw new SessionPersistenceNotFoundError(id);
|
|
1148
1191
|
try {
|
|
1149
|
-
const { meta, events, revision, tornMarker } = stored;
|
|
1192
|
+
const { meta, inheritedEventCount, events, revision, tornMarker } = stored;
|
|
1150
1193
|
this.assertStoredId(id, meta);
|
|
1151
1194
|
this.assertVersion(meta);
|
|
1152
1195
|
const storedEvents = adoptStoredEvents(events, id);
|
|
1153
1196
|
this.assertEventsSupported(meta, storedEvents);
|
|
1197
|
+
if (inheritedEventCount > storedEvents.length) throw new Error(`session "${id}" inherited event count exceeds its stored event count`);
|
|
1154
1198
|
const closers = interruptedTurnClosers(storedEvents).map(adoptSessionEvent);
|
|
1155
1199
|
const balanced = [...storedEvents, ...closers];
|
|
1156
1200
|
const session = this.ctx.sessions.prepare(id, {
|
|
1157
1201
|
seed: balanced,
|
|
1158
1202
|
meta,
|
|
1203
|
+
inheritedEventCount,
|
|
1159
1204
|
seedSource: "persistence"
|
|
1160
1205
|
});
|
|
1161
1206
|
return {
|
|
1162
1207
|
inspection: Object.freeze({
|
|
1163
1208
|
meta: session.header,
|
|
1209
|
+
inheritedEventCount: session.inheritedEventCount,
|
|
1164
1210
|
events: Object.freeze(balanced)
|
|
1165
1211
|
}),
|
|
1166
1212
|
session,
|
|
1167
1213
|
revision,
|
|
1168
|
-
sessionLength: session.
|
|
1214
|
+
sessionLength: session.seq,
|
|
1169
1215
|
tornMarker,
|
|
1170
1216
|
closers
|
|
1171
1217
|
};
|
|
@@ -1177,20 +1223,20 @@ var PersistenceCoordinator = class {
|
|
|
1177
1223
|
/** Commit one prepared repair and establish its ownerless durable cursor. */
|
|
1178
1224
|
async commitPrepared(source) {
|
|
1179
1225
|
const id = source.inspection.meta.id;
|
|
1180
|
-
const cursor = source.inspection.events.length;
|
|
1226
|
+
const cursor = SessionLogOffset(source.inspection.events.length);
|
|
1181
1227
|
const existing = this.states.get(id);
|
|
1182
1228
|
if (existing?.owner !== void 0) throw new Error(`session "${id}" already has a live persistence owner`);
|
|
1183
1229
|
if (!await this.isPreparedSourceCurrent(source)) return void 0;
|
|
1184
1230
|
if (source.tornMarker !== void 0 || source.closers.length > 0) {
|
|
1185
|
-
await this.backend.commitRepair(source.inspection
|
|
1231
|
+
await this.backend.commitRepair(source.inspection, source.tornMarker, source.closers);
|
|
1186
1232
|
return;
|
|
1187
1233
|
}
|
|
1188
1234
|
const state = existing ?? {
|
|
1189
|
-
|
|
1235
|
+
storage: source.inspection,
|
|
1190
1236
|
cursor,
|
|
1191
1237
|
materialized: true
|
|
1192
1238
|
};
|
|
1193
|
-
state.
|
|
1239
|
+
state.storage = source.inspection;
|
|
1194
1240
|
state.cursor = cursor;
|
|
1195
1241
|
state.materialized = true;
|
|
1196
1242
|
state.revision = source.revision;
|
|
@@ -1206,7 +1252,7 @@ var PersistenceCoordinator = class {
|
|
|
1206
1252
|
}
|
|
1207
1253
|
/** Return one durable immutable view of an already-live Session. */
|
|
1208
1254
|
async loadLiveSnapshot(session) {
|
|
1209
|
-
const events = session.
|
|
1255
|
+
const events = session.snapshotEvents();
|
|
1210
1256
|
await this.flush(session);
|
|
1211
1257
|
const state = this.states.get(session.id);
|
|
1212
1258
|
/* v8 ignore next -- successful flush always publishes this live session's durable state */
|
|
@@ -1214,7 +1260,8 @@ var PersistenceCoordinator = class {
|
|
|
1214
1260
|
if (events.length === 0 && !state.materialized) throw new Error(`session "${session.id}" not found`);
|
|
1215
1261
|
if (interruptedTurnClosers(events).length > 0) throw new Error(`cannot load session "${session.id}" while its live turn is open; use the live Session or wait for the turn to close`);
|
|
1216
1262
|
return Object.freeze({
|
|
1217
|
-
meta: state.meta,
|
|
1263
|
+
meta: state.storage.meta,
|
|
1264
|
+
inheritedEventCount: state.storage.inheritedEventCount,
|
|
1218
1265
|
events
|
|
1219
1266
|
});
|
|
1220
1267
|
}
|
|
@@ -1222,7 +1269,8 @@ var PersistenceCoordinator = class {
|
|
|
1222
1269
|
inspectLive(session) {
|
|
1223
1270
|
return Object.freeze({
|
|
1224
1271
|
meta: session.header,
|
|
1225
|
-
|
|
1272
|
+
inheritedEventCount: session.inheritedEventCount,
|
|
1273
|
+
events: session.snapshotEvents()
|
|
1226
1274
|
});
|
|
1227
1275
|
}
|
|
1228
1276
|
/** Await one retiring lifecycle with caller cancellation. */
|
|
@@ -1353,7 +1401,7 @@ var PersistenceCoordinator = class {
|
|
|
1353
1401
|
this.live.set(session, restored);
|
|
1354
1402
|
return restored;
|
|
1355
1403
|
}
|
|
1356
|
-
const seed = session.
|
|
1404
|
+
const seed = session.snapshotEvents();
|
|
1357
1405
|
const live = {
|
|
1358
1406
|
init: Promise.resolve(),
|
|
1359
1407
|
writes: this.createWriteBehind(session, () => live.init)
|
|
@@ -1367,7 +1415,7 @@ var PersistenceCoordinator = class {
|
|
|
1367
1415
|
attachPrepared(session, reservation) {
|
|
1368
1416
|
const { source, state } = reservation;
|
|
1369
1417
|
if (source.session !== session || state.owner !== void 0 || state.cursor !== source.inspection.events.length || session.firstLiveSeq !== state.cursor) throw new Error(`session "${session.id}" preparation no longer matches its persistence state`);
|
|
1370
|
-
const suffix = session.
|
|
1418
|
+
const suffix = session.snapshotEvents(state.cursor).map((event) => structuredClone(event));
|
|
1371
1419
|
this.preparations.attach(reservation);
|
|
1372
1420
|
state.owner = session;
|
|
1373
1421
|
const live = {
|
|
@@ -1413,7 +1461,8 @@ var PersistenceCoordinator = class {
|
|
|
1413
1461
|
/* v8 ignore next -- initFor dedupes per session object; same-object re-entry can't occur */
|
|
1414
1462
|
if (tracked.owner === session) return;
|
|
1415
1463
|
if (tracked.owner === void 0) {
|
|
1416
|
-
if (tracked.meta.cwd !== session.header.cwd) throw new Error(`session "${id}" is already persisted at a different cwd (persisted: ${String(tracked.meta.cwd)}, live: ${String(session.header.cwd)}) (id collision)`);
|
|
1464
|
+
if (tracked.storage.meta.cwd !== session.header.cwd) throw new Error(`session "${id}" is already persisted at a different cwd (persisted: ${String(tracked.storage.meta.cwd)}, live: ${String(session.header.cwd)}) (id collision)`);
|
|
1465
|
+
if (tracked.storage.inheritedEventCount !== session.inheritedEventCount) throw new Error(`session "${id}" is already persisted with a different inherited event count (id collision)`);
|
|
1417
1466
|
if (!await this.seedMatchesPersisted(id, seed, tracked.cursor)) throw new Error(`session "${id}" is already persisted with ${tracked.cursor} event(s) that do not match this live session (id collision)`);
|
|
1418
1467
|
tracked.owner = session;
|
|
1419
1468
|
const suffix = seed.slice(tracked.cursor);
|
|
@@ -1429,8 +1478,11 @@ var PersistenceCoordinator = class {
|
|
|
1429
1478
|
await this.adoptLivePrefix(session, seed, live);
|
|
1430
1479
|
return;
|
|
1431
1480
|
}
|
|
1432
|
-
const
|
|
1433
|
-
await this.createCore(
|
|
1481
|
+
const storage = sessionStorageMetadata(session);
|
|
1482
|
+
await this.createCore({
|
|
1483
|
+
meta: { ...storage.meta },
|
|
1484
|
+
inheritedEventCount: storage.inheritedEventCount
|
|
1485
|
+
});
|
|
1434
1486
|
const created = this.states.get(id);
|
|
1435
1487
|
/* v8 ignore next -- create() always sets the state for the id */
|
|
1436
1488
|
if (created !== void 0) created.owner = session;
|
|
@@ -1443,17 +1495,21 @@ var PersistenceCoordinator = class {
|
|
|
1443
1495
|
* live suffix that was ahead of the stored prefix.
|
|
1444
1496
|
*/
|
|
1445
1497
|
async adoptLivePrefix(session, seed, stored) {
|
|
1446
|
-
const { meta, events, tornMarker } = stored;
|
|
1498
|
+
const { meta, inheritedEventCount, events, tornMarker } = stored;
|
|
1447
1499
|
this.assertStoredId(session.header.id, meta);
|
|
1448
1500
|
if (meta.cwd !== session.header.cwd) throw new Error(`session "${session.header.id}" is already persisted at a different cwd (persisted: ${String(meta.cwd)}, live: ${String(session.header.cwd)}) (id collision)`);
|
|
1501
|
+
if (inheritedEventCount !== session.inheritedEventCount) throw new Error(`session "${session.header.id}" is already persisted with a different inherited event count (id collision)`);
|
|
1449
1502
|
this.assertVersion(meta);
|
|
1450
1503
|
const storedEvents = snapshotStoredEvents(events, session.header.id);
|
|
1451
1504
|
this.assertEventsSupported(meta, storedEvents);
|
|
1452
1505
|
if (!seedCoversPrefix(seed, storedEvents)) throw new Error(`session "${session.header.id}" already has a persisted log on disk that does not match this live session (id collision)`);
|
|
1453
|
-
if (tornMarker !== void 0) await this.backend.commitRepair(
|
|
1506
|
+
if (tornMarker !== void 0) await this.backend.commitRepair(stored, tornMarker, []);
|
|
1454
1507
|
const state = {
|
|
1455
|
-
|
|
1456
|
-
|
|
1508
|
+
storage: {
|
|
1509
|
+
meta: { ...meta },
|
|
1510
|
+
inheritedEventCount
|
|
1511
|
+
},
|
|
1512
|
+
cursor: SessionLogOffset(storedEvents.length),
|
|
1457
1513
|
materialized: true,
|
|
1458
1514
|
owner: session,
|
|
1459
1515
|
...tornMarker === void 0 ? { revision: stored.revision } : {}
|
|
@@ -1560,6 +1616,7 @@ var SessionPersistence = class extends Service {
|
|
|
1560
1616
|
return SessionPreparation.create(sessions.prepare(id, {
|
|
1561
1617
|
seed: loaded.events.map((event) => structuredClone(event)),
|
|
1562
1618
|
meta: structuredClone(loaded.meta),
|
|
1619
|
+
inheritedEventCount: SessionLogOffset(loaded.inheritedEventCount),
|
|
1563
1620
|
seedSource: "persistence"
|
|
1564
1621
|
}));
|
|
1565
1622
|
}
|
|
@@ -6,8 +6,8 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import { Context } from '@deepseek-ai/cordis';
|
|
8
8
|
import { SessionPreparation } from '@deepseek-ai/dsh-session';
|
|
9
|
-
import type { Session, SessionEvent, SessionId, SessionHeader } from '@deepseek-ai/dsh-session';
|
|
10
|
-
import type { BorrowedSessionSource, SessionInspection, SessionLocation } from './index.ts';
|
|
9
|
+
import type { Session, SessionEvent, SessionId, SessionHeader, SessionLogOffset as SessionLogOffsetType } from '@deepseek-ai/dsh-session';
|
|
10
|
+
import type { BorrowedSessionSource, SessionEventSuffix, SessionInspection, SessionLocation, SessionStorageMetadata } from './index.ts';
|
|
11
11
|
import type { SessionPersistenceRevision } from './revision.ts';
|
|
12
12
|
/** Default number of detached session preparations retained by a coordinator. */
|
|
13
13
|
export declare const DEFAULT_PREPARED_SESSION_CACHE_SIZE = 5;
|
|
@@ -65,8 +65,7 @@ export interface PersistenceCoordinatorOptions {
|
|
|
65
65
|
* returns its value to {@link PersistenceBackend.commitRepair}; each backend
|
|
66
66
|
* owns the marker type.
|
|
67
67
|
*/
|
|
68
|
-
export interface StoredPrefix<TornMarker = unknown> {
|
|
69
|
-
meta: SessionHeader;
|
|
68
|
+
export interface StoredPrefix<TornMarker = unknown> extends SessionStorageMetadata {
|
|
70
69
|
events: SessionEvent[];
|
|
71
70
|
/** Revision observed for exactly this detached prefix. */
|
|
72
71
|
revision: SessionPersistenceRevision;
|
|
@@ -78,8 +77,7 @@ export interface StoredPrefix<TornMarker = unknown> {
|
|
|
78
77
|
* {@link PersistenceBackend.loadStoredFrom} hook. Non-mutating reads carry no
|
|
79
78
|
* torn marker: there is nothing to repair.
|
|
80
79
|
*/
|
|
81
|
-
export interface StoredSuffix {
|
|
82
|
-
meta: SessionHeader;
|
|
80
|
+
export interface StoredSuffix extends SessionStorageMetadata {
|
|
83
81
|
events: SessionEvent[];
|
|
84
82
|
}
|
|
85
83
|
/**
|
|
@@ -141,9 +139,9 @@ export interface PersistenceBackend<TornMarker = unknown> {
|
|
|
141
139
|
* validated by the coordinator before this hook runs).
|
|
142
140
|
* @param signal - optional cancellation for backend read work.
|
|
143
141
|
*/
|
|
144
|
-
loadStoredFrom?(id: SessionId, fromSeq:
|
|
142
|
+
loadStoredFrom?(id: SessionId, fromSeq: SessionLogOffsetType, signal?: AbortSignal): Promise<StoredSuffix | undefined>;
|
|
145
143
|
/** Durably create an empty header-only session artifact. */
|
|
146
|
-
materializeHeader?(
|
|
144
|
+
materializeHeader?(storage: SessionStorageMetadata): Promise<void>;
|
|
147
145
|
/**
|
|
148
146
|
* Durably append a CONTIGUOUS batch, lazily materializing the session first
|
|
149
147
|
* when `!isMaterialized`. The materialize-write and the first event batch MUST
|
|
@@ -151,8 +149,10 @@ export interface PersistenceBackend<TornMarker = unknown> {
|
|
|
151
149
|
* empty session). Returns once the batch is durable. A rejection must leave
|
|
152
150
|
* the stored log unchanged (roll partial work back): the coordinator keeps
|
|
153
151
|
* its cursor across the failure and retries the same batch.
|
|
152
|
+
* The coordinator calls this only after a first batch reaches the declared
|
|
153
|
+
* inherited prefix length.
|
|
154
154
|
*/
|
|
155
|
-
appendBatch(
|
|
155
|
+
appendBatch(storage: SessionStorageMetadata, events: readonly SessionEvent[], isMaterialized: boolean): Promise<void>;
|
|
156
156
|
/**
|
|
157
157
|
* Make a crash repair durable: truncate the torn tail (iff
|
|
158
158
|
* `tornMarker !== undefined`) and append `closers` (iff any). NOT required to
|
|
@@ -160,7 +160,7 @@ export interface PersistenceBackend<TornMarker = unknown> {
|
|
|
160
160
|
* Used by load (truncate + synthetic closers) and by live-adoption (truncate
|
|
161
161
|
* only, `closers = []`).
|
|
162
162
|
*/
|
|
163
|
-
commitRepair(
|
|
163
|
+
commitRepair(storage: SessionStorageMetadata, tornMarker: TornMarker | undefined, closers: readonly SessionEvent[]): Promise<void>;
|
|
164
164
|
/**
|
|
165
165
|
* List all stored (materialized) sessions' metadata.
|
|
166
166
|
* @param signal - optional cancellation for backend listing work.
|
|
@@ -215,8 +215,10 @@ export declare class PersistenceCoordinator<TornMarker = unknown> {
|
|
|
215
215
|
/**
|
|
216
216
|
* Register detached session metadata for lazy creation on the first append.
|
|
217
217
|
* @param meta - header to snapshot; duplicate tracked or persisted ids reject.
|
|
218
|
+
* @param inheritedEventCount - exact inherited prefix length; required for
|
|
219
|
+
* a seeded header and omitted only for an unseeded header.
|
|
218
220
|
*/
|
|
219
|
-
create(meta: SessionHeader): Promise<void>;
|
|
221
|
+
create(meta: SessionHeader, inheritedEventCount?: SessionLogOffsetType): Promise<void>;
|
|
220
222
|
/**
|
|
221
223
|
* Materialize one exact live session without inventing a session event.
|
|
222
224
|
* @param session - live session already registered through the write path.
|
|
@@ -282,12 +284,9 @@ export declare class PersistenceCoordinator<TornMarker = unknown> {
|
|
|
282
284
|
* @param id - persisted session to read.
|
|
283
285
|
* @param fromSeq - first event seq to include; a non-negative safe integer.
|
|
284
286
|
* @param signal - optional cancellation for queued and backend read work.
|
|
285
|
-
* @returns stored
|
|
287
|
+
* @returns stored metadata, the requested offset, and valid events with `seq >= fromSeq`.
|
|
286
288
|
*/
|
|
287
|
-
readFrom(id: SessionId, fromSeq:
|
|
288
|
-
meta: SessionHeader;
|
|
289
|
-
events: SessionEvent[];
|
|
290
|
-
}>;
|
|
289
|
+
readFrom(id: SessionId, fromSeq: SessionLogOffsetType, signal?: AbortSignal): Promise<SessionEventSuffix>;
|
|
291
290
|
private readFromCore;
|
|
292
291
|
/** Read one detached physical prefix without logical recovery or caching. */
|
|
293
292
|
private readStoredPrefix;
|
package/lib/types/index.d.ts
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* @module @deepseek-ai/dsh-session-persistence
|
|
6
6
|
*/
|
|
7
7
|
import { Context, Service } from '@deepseek-ai/cordis';
|
|
8
|
-
import { SessionPreparation } from '@deepseek-ai/dsh-session';
|
|
8
|
+
import { SessionPreparation, SessionLogOffset } from '@deepseek-ai/dsh-session';
|
|
9
9
|
import type { Session, SessionEvent, SessionId, SessionHeader } from '@deepseek-ai/dsh-session';
|
|
10
10
|
import type { SessionPersistenceRevision } from './revision.ts';
|
|
11
11
|
export type { SessionHeader } from '@deepseek-ai/dsh-session';
|
|
@@ -18,13 +18,25 @@ export interface SessionPersistenceSnapshot {
|
|
|
18
18
|
/** Opaque source-qualified token that changes whenever this stored log changes. */
|
|
19
19
|
revision: SessionPersistenceRevision;
|
|
20
20
|
}
|
|
21
|
-
/**
|
|
22
|
-
export interface
|
|
23
|
-
/** Validated immutable
|
|
21
|
+
/** Logical Session header paired with its exact inherited cut for body-bearing storage operations. */
|
|
22
|
+
export interface SessionStorageMetadata {
|
|
23
|
+
/** Validated immutable Session header. */
|
|
24
24
|
readonly meta: SessionHeader;
|
|
25
|
+
/** Number of leading events inherited from the Session's fork parent. */
|
|
26
|
+
readonly inheritedEventCount: SessionLogOffset;
|
|
27
|
+
}
|
|
28
|
+
/** Immutable logical session prepared from persistence or a live owner. */
|
|
29
|
+
export interface SessionInspection extends SessionStorageMetadata {
|
|
25
30
|
/** Validated contiguous logical event log. */
|
|
26
31
|
readonly events: readonly SessionEvent[];
|
|
27
32
|
}
|
|
33
|
+
/** Detached logical suffix returned by one explicit stored-log offset read. */
|
|
34
|
+
export interface SessionEventSuffix extends SessionStorageMetadata {
|
|
35
|
+
/** First requested log offset; {@link events} contains only seqs at or after it. */
|
|
36
|
+
readonly fromSeq: SessionLogOffset;
|
|
37
|
+
/** Valid contiguous stored events at or after {@link fromSeq}; not a complete Session log when the offset is nonzero. */
|
|
38
|
+
readonly events: readonly SessionEvent[];
|
|
39
|
+
}
|
|
28
40
|
/** A borrowed exact Session source returned from a cold materialization or concurrent live owner. */
|
|
29
41
|
export type BorrowedSessionSource = Disposable & ({
|
|
30
42
|
/** A reusable unpublished Session is pinned until this observation is disposed. */
|
|
@@ -42,9 +54,7 @@ export type BorrowedSessionSource = Disposable & ({
|
|
|
42
54
|
readonly inspection: SessionInspection;
|
|
43
55
|
});
|
|
44
56
|
/** A backend's own raw artifact text for one session, verbatim. */
|
|
45
|
-
export interface SessionRawArtifact {
|
|
46
|
-
/** The session header parsed from the artifact's own first line. */
|
|
47
|
-
readonly meta: SessionHeader;
|
|
57
|
+
export interface SessionRawArtifact extends SessionStorageMetadata {
|
|
48
58
|
/** The artifact's base filename on disk, without any physical encoding suffix. */
|
|
49
59
|
readonly filename: string;
|
|
50
60
|
/** The artifact's full text content, decoded from the backend's physical encoding. */
|
|
@@ -111,8 +121,10 @@ export declare abstract class SessionPersistence extends Service {
|
|
|
111
121
|
* created-but-never-appended session is absent from {@link list}
|
|
112
122
|
* — abandoned sessions leave nothing behind.
|
|
113
123
|
* @param meta - the immutable header (id, version, cwd, lineage) to record.
|
|
124
|
+
* @param inheritedEventCount - exact fork-inherited prefix length. Required
|
|
125
|
+
* for a seeded header and omitted only for an unseeded header.
|
|
114
126
|
*/
|
|
115
|
-
abstract create(meta: SessionHeader): Promise<void>;
|
|
127
|
+
abstract create(meta: SessionHeader, inheritedEventCount?: SessionLogOffset): Promise<void>;
|
|
116
128
|
/**
|
|
117
129
|
* Ensure a live session has a durable header even when it has no events.
|
|
118
130
|
* Ordinary sessions remain lazily materialized; lifecycle frontends call
|
|
@@ -128,6 +140,8 @@ export declare abstract class SessionPersistence extends Service {
|
|
|
128
140
|
* Coordinator-backed implementations also reject when the durable log
|
|
129
141
|
* advanced since this process last observed it — another harness process
|
|
130
142
|
* sharing the sessions root — rather than interleave duplicate seqs.
|
|
143
|
+
* A seeded session's first materializing batch must reach its complete
|
|
144
|
+
* inherited prefix.
|
|
131
145
|
* @param id - the session the batch belongs to.
|
|
132
146
|
* @param events - the contiguous batch to persist, in seq order.
|
|
133
147
|
*/
|
|
@@ -197,14 +211,11 @@ export declare abstract class SessionPersistence extends Service {
|
|
|
197
211
|
* forward. The primitive bounds what is returned and refolded, not every
|
|
198
212
|
* backend's physical read.
|
|
199
213
|
* @param id - the persisted session to read.
|
|
200
|
-
* @param fromSeq - first event
|
|
214
|
+
* @param fromSeq - first event offset to include.
|
|
201
215
|
* @param signal - optional cancellation for queued and backend read work.
|
|
202
|
-
* @returns the
|
|
216
|
+
* @returns storage metadata, the requested offset, and stored events with `seq >= fromSeq`.
|
|
203
217
|
*/
|
|
204
|
-
abstract readFrom(id: SessionId, fromSeq:
|
|
205
|
-
meta: SessionHeader;
|
|
206
|
-
events: SessionEvent[];
|
|
207
|
-
}>;
|
|
218
|
+
abstract readFrom(id: SessionId, fromSeq: SessionLogOffset, signal?: AbortSignal): Promise<SessionEventSuffix>;
|
|
208
219
|
/**
|
|
209
220
|
* Lightweight listing from metadata, without a full-log parse.
|
|
210
221
|
* @param signal - optional cancellation for backend listing work.
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@crazx/dsh-session-persistence",
|
|
3
3
|
"description": "Abstract durable session persistence seam (ctx.sessionPersistence) for the DeepSeek Harness",
|
|
4
|
-
"version": "0.1.2-alpha.
|
|
4
|
+
"version": "0.1.2-alpha.5.zw.1",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
7
7
|
},
|
|
@@ -18,35 +18,28 @@
|
|
|
18
18
|
"types": "./lib/types/index.d.ts",
|
|
19
19
|
"default": "./lib/index.js"
|
|
20
20
|
},
|
|
21
|
-
"./invariant": {
|
|
22
|
-
"types": "./lib/types/invariant.d.ts",
|
|
23
|
-
"default": "./lib/invariant.js"
|
|
24
|
-
},
|
|
25
21
|
"./src/*": "./src/*",
|
|
26
22
|
"./package.json": "./package.json"
|
|
27
23
|
},
|
|
28
24
|
"files": [
|
|
29
25
|
"lib/index.js",
|
|
30
|
-
"lib/invariant.js",
|
|
31
26
|
"lib/types/**/*.d.ts"
|
|
32
27
|
],
|
|
33
28
|
"license": "MIT",
|
|
34
29
|
"peerDependencies": {
|
|
35
30
|
"@deepseek-ai/cordis": "^4.0.2",
|
|
36
|
-
"@deepseek-ai/dsh-brand": "^0.1.2-alpha.
|
|
37
|
-
"@deepseek-ai/dsh-
|
|
38
|
-
"@deepseek-ai/dsh-
|
|
39
|
-
"@deepseek-ai/dsh-timeout": "^0.1.2-alpha.3"
|
|
31
|
+
"@deepseek-ai/dsh-brand": "^0.1.2-alpha.5",
|
|
32
|
+
"@deepseek-ai/dsh-session": "^0.1.2-alpha.5",
|
|
33
|
+
"@deepseek-ai/dsh-timeout": "^0.1.2-alpha.5"
|
|
40
34
|
},
|
|
41
35
|
"devDependencies": {
|
|
42
36
|
"@deepseek-ai/cordis": "^4.0.2",
|
|
43
|
-
"@deepseek-ai/dsh-brand": "^0.1.2-alpha.
|
|
44
|
-
"@deepseek-ai/dsh-
|
|
45
|
-
"@deepseek-ai/dsh-
|
|
46
|
-
"@deepseek-ai/dsh-
|
|
47
|
-
"@deepseek-ai/dsh-timeout": "^0.1.2-alpha.3"
|
|
37
|
+
"@deepseek-ai/dsh-brand": "^0.1.2-alpha.5",
|
|
38
|
+
"@deepseek-ai/dsh-scope": "^0.1.2-alpha.5",
|
|
39
|
+
"@deepseek-ai/dsh-session": "^0.1.2-alpha.5",
|
|
40
|
+
"@deepseek-ai/dsh-timeout": "^0.1.2-alpha.5"
|
|
48
41
|
},
|
|
49
42
|
"dependencies": {
|
|
50
|
-
"@deepseek-ai/dsh-util-values": "^0.1.2-alpha.
|
|
43
|
+
"@deepseek-ai/dsh-util-values": "^0.1.2-alpha.5"
|
|
51
44
|
}
|
|
52
45
|
}
|
package/lib/invariant.js
DELETED
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
//#region lib/types/invariant.js
|
|
2
|
-
/**
|
|
3
|
-
* Package-owned invariant companion for `@deepseek-ai/dsh-session-persistence`.
|
|
4
|
-
* @module @deepseek-ai/dsh-session-persistence/invariant
|
|
5
|
-
*/
|
|
6
|
-
const PACKAGE_NAME = "@deepseek-ai/dsh-session-persistence";
|
|
7
|
-
/** Cordis companion plugin name. */
|
|
8
|
-
const name = "session-persistence-invariant";
|
|
9
|
-
/** Service required before the companion can reserve package ownership. */
|
|
10
|
-
const inject = ["invariants"];
|
|
11
|
-
/**
|
|
12
|
-
* No runtime invariant: persistence correctness requires backend round-trip and crash-tail tests;
|
|
13
|
-
* this package exposes no continuously observable in-process relation.
|
|
14
|
-
*/
|
|
15
|
-
const install = () => {};
|
|
16
|
-
/**
|
|
17
|
-
* Register this package's invariant companion.
|
|
18
|
-
* @param ctx - Cordis context carrying the invariant service.
|
|
19
|
-
* @returns the installed registration's disposer after setup succeeds.
|
|
20
|
-
*/
|
|
21
|
-
const apply = (ctx) => Promise.resolve(ctx.invariants.register(PACKAGE_NAME, install));
|
|
22
|
-
//#endregion
|
|
23
|
-
export { apply, inject, name };
|
package/lib/types/invariant.d.ts
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Package-owned invariant companion for `@deepseek-ai/dsh-session-persistence`.
|
|
3
|
-
* @module @deepseek-ai/dsh-session-persistence/invariant
|
|
4
|
-
*/
|
|
5
|
-
import type { Context } from '@deepseek-ai/cordis';
|
|
6
|
-
/** Cordis companion plugin name. */
|
|
7
|
-
export declare const name = "session-persistence-invariant";
|
|
8
|
-
/** Service required before the companion can reserve package ownership. */
|
|
9
|
-
export declare const inject: string[];
|
|
10
|
-
/**
|
|
11
|
-
* Register this package's invariant companion.
|
|
12
|
-
* @param ctx - Cordis context carrying the invariant service.
|
|
13
|
-
* @returns the installed registration's disposer after setup succeeds.
|
|
14
|
-
*/
|
|
15
|
-
export declare const apply: (ctx: Context) => Promise<() => void>;
|
|
16
|
-
//# sourceMappingURL=invariant.d.ts.map
|