@morlay/session-rdb 0.0.12 → 0.0.13
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/dist/artifact.d.mts +64 -0
- package/dist/artifact.mjs +3 -0
- package/dist/import-D2vUnUZe.mjs +438 -0
- package/dist/import.d.mts +24 -0
- package/dist/import.mjs +2 -0
- package/dist/index-uUvn6gYp.d.mts +111 -0
- package/dist/index.d.mts +3 -203
- package/dist/index.mjs +7 -1571
- package/dist/invariant.d.mts +1 -2
- package/dist/invariant.mjs +0 -2
- package/dist/magic-string.es-BWtG0AyA.mjs +1017 -0
- package/dist/schema-B-8G4lWc.mjs +852 -0
- package/dist/schema-CFXZURX7.d.mts +116 -0
- package/dist/sqlite-DIXY-dFZ.mjs +356 -0
- package/dist/storage.d.mts +44 -0
- package/dist/storage.mjs +3 -0
- package/dist/testing.d.mts +31 -0
- package/dist/testing.mjs +16051 -0
- package/package.json +19 -2
- package/src/artifact.ts +4 -0
- package/src/index.ts +4 -0
- package/src/log.ts +86 -0
- package/src/storage.ts +5 -0
- package/src/testing/contract.ts +562 -0
- package/src/testing/coordinator-contract.ts +1723 -0
- package/src/testing/helpers.ts +20 -0
- package/src/testing.ts +12 -0
- package/dist/index.d.mts.map +0 -1
- package/dist/index.mjs.map +0 -1
- package/dist/invariant.d.mts.map +0 -1
- package/dist/invariant.mjs.map +0 -1
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { b as EventRow, x as SessionRow } from "./schema-CFXZURX7.mjs";
|
|
2
|
+
import { SESSION_IMPORT_PATH, SESSION_LOG_ARTIFACT_FILENAME, expandProvenanceFromStorage, parseImportZip, parseJsonlArtifact, persistImport, registerSessionImport } from "./import.mjs";
|
|
3
|
+
import { SessionStorageMetadata } from "@deepseek-ai/dsh-session-persistence";
|
|
4
|
+
import { SessionEvent, SessionHeader, SurfaceOp } from "@deepseek-ai/dsh-session";
|
|
5
|
+
//#region src/log.d.ts
|
|
6
|
+
declare function rowToMeta(row: SessionRow): SessionHeader;
|
|
7
|
+
declare function sessionInsertRow(storage: SessionStorageMetadata, incarnation: string): {
|
|
8
|
+
fSessionId: string;
|
|
9
|
+
fHeadEventId: string;
|
|
10
|
+
fHeadSequence: number;
|
|
11
|
+
fVersion: number;
|
|
12
|
+
fCreatedAt: number;
|
|
13
|
+
fCwd: string | null;
|
|
14
|
+
fParentSession: string | null;
|
|
15
|
+
fSeedLength: number | null;
|
|
16
|
+
fOrigin: string | null;
|
|
17
|
+
fDelegationDepth: number | null;
|
|
18
|
+
fIncarnation: string;
|
|
19
|
+
fRevision: number;
|
|
20
|
+
};
|
|
21
|
+
declare function sessionConflictRow(storage: SessionStorageMetadata): {
|
|
22
|
+
fVersion: number;
|
|
23
|
+
fCreatedAt: number;
|
|
24
|
+
fCwd: string | null;
|
|
25
|
+
fParentSession: string | null;
|
|
26
|
+
fSeedLength: number | null;
|
|
27
|
+
fOrigin: string | null;
|
|
28
|
+
fDelegationDepth: number | null;
|
|
29
|
+
};
|
|
30
|
+
declare function remapSurfaceOp(op: SurfaceOp, remap: (seq: number) => number): SurfaceOp;
|
|
31
|
+
declare function remapShadowedRange(range: {
|
|
32
|
+
start: number;
|
|
33
|
+
end: number;
|
|
34
|
+
}, remap: (seq: number) => number): {
|
|
35
|
+
start: number;
|
|
36
|
+
end: number;
|
|
37
|
+
};
|
|
38
|
+
declare function rowToEvent(row: EventRow, seqMap: ReadonlyMap<number, number>): SessionEvent;
|
|
39
|
+
declare function buildSeqMap(rows: readonly Pick<EventRow, "fSequence" | "fOriginalSeq">[]): Map<number, number>;
|
|
40
|
+
declare function recomputeReplaceProvenance(events: SessionEvent[]): void;
|
|
41
|
+
declare function findSurfaceRepairs(events: readonly SessionEvent[]): {
|
|
42
|
+
degradeToAppend: Set<number>;
|
|
43
|
+
addAppendMarker: Set<number>;
|
|
44
|
+
clearSurfaceOp: Set<number>;
|
|
45
|
+
};
|
|
46
|
+
declare function repairSurfaceOps(events: SessionEvent[]): void;
|
|
47
|
+
/**
|
|
48
|
+
* 定位无法从空状态增量重放的 agent/inbox/spliced 事件(孤儿操作)。
|
|
49
|
+
*
|
|
50
|
+
* 上游 Inbox 每次构造都从会话起点重放全部 inbox splice,失败即拒绝整个
|
|
51
|
+
* 会话(resume / 编辑重放不可用)。rewind 截断历史轮次后若残留 splice 引用
|
|
52
|
+
* 已被截断的排队消息(插入被删、消费保留,或反之),重放时越界或重复——
|
|
53
|
+
* 无法独立重放。
|
|
54
|
+
*/
|
|
55
|
+
declare function orphanInboxSpliceSeqs(events: readonly SessionEvent[]): Set<number>;
|
|
56
|
+
/** 内存修复:把孤儿 inbox splice 改写为 no-op(调用方负责持久化)。 */
|
|
57
|
+
declare function repairOrphanInboxSplices(events: SessionEvent[]): void;
|
|
58
|
+
declare function scanRows(rows: readonly EventRow[], base?: number, seqMap?: ReadonlyMap<number, number>): {
|
|
59
|
+
preserved: SessionEvent[];
|
|
60
|
+
tornFrom?: number;
|
|
61
|
+
};
|
|
62
|
+
declare function toJsonlArtifact(meta: SessionHeader, inheritedEventCount: number, events: readonly SessionEvent[]): string;
|
|
63
|
+
//#endregion
|
|
64
|
+
export { SESSION_IMPORT_PATH, SESSION_LOG_ARTIFACT_FILENAME, buildSeqMap, expandProvenanceFromStorage, findSurfaceRepairs, orphanInboxSpliceSeqs, parseImportZip, parseJsonlArtifact, persistImport, recomputeReplaceProvenance, registerSessionImport, remapShadowedRange, remapSurfaceOp, repairOrphanInboxSplices, repairSurfaceOps, rowToEvent, rowToMeta, scanRows, sessionConflictRow, sessionInsertRow, toJsonlArtifact };
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import { A as toJsonlArtifact, C as repairOrphanInboxSplices, D as scanRows, E as rowToMeta, O as sessionConflictRow, S as remapSurfaceOp, T as rowToEvent, _ as buildSeqMap, b as recomputeReplaceProvenance, k as sessionInsertRow, v as findSurfaceRepairs, w as repairSurfaceOps, x as remapShadowedRange, y as orphanInboxSpliceSeqs } from "./schema-B-8G4lWc.mjs";
|
|
2
|
+
import { a as parseJsonlArtifact, i as parseImportZip, n as SESSION_LOG_ARTIFACT_FILENAME, o as persistImport, r as expandProvenanceFromStorage, s as registerSessionImport, t as SESSION_IMPORT_PATH } from "./import-D2vUnUZe.mjs";
|
|
3
|
+
export { SESSION_IMPORT_PATH, SESSION_LOG_ARTIFACT_FILENAME, buildSeqMap, expandProvenanceFromStorage, findSurfaceRepairs, orphanInboxSpliceSeqs, parseImportZip, parseJsonlArtifact, persistImport, recomputeReplaceProvenance, registerSessionImport, remapShadowedRange, remapSurfaceOp, repairOrphanInboxSplices, repairSurfaceOps, rowToEvent, rowToMeta, scanRows, sessionConflictRow, sessionInsertRow, toJsonlArtifact };
|
|
@@ -0,0 +1,438 @@
|
|
|
1
|
+
import { E as rowToMeta, l as isPersistedEvent } from "./schema-B-8G4lWc.mjs";
|
|
2
|
+
import { randomUUID } from "node:crypto";
|
|
3
|
+
import { SESSION_FORMAT_VERSION, SessionLogOffset, decodeSeqRanges, decodeStorageRecord } from "@deepseek-ai/dsh-session";
|
|
4
|
+
import { SessionBranch, SessionBranchError, balanceRewindPrefix, buildTimeline } from "@morlay/session-branch";
|
|
5
|
+
import { unzipSync } from "fflate";
|
|
6
|
+
//#region src/branch.ts
|
|
7
|
+
function locateTurnEnd(events, atSeq, mode = "after") {
|
|
8
|
+
const ends = events.filter((event) => event.type === "turn/end").map((event) => event.seq);
|
|
9
|
+
if (atSeq === void 0) {
|
|
10
|
+
const last = ends.at(-1);
|
|
11
|
+
if (last === void 0) throw new SessionBranchError("session has no closed turn", "OPEN_TURN");
|
|
12
|
+
return last;
|
|
13
|
+
}
|
|
14
|
+
if (mode === "before") {
|
|
15
|
+
let boundary = -1;
|
|
16
|
+
for (const seq of ends) if (seq < atSeq) boundary = seq;
|
|
17
|
+
else break;
|
|
18
|
+
return boundary;
|
|
19
|
+
}
|
|
20
|
+
const firstAfter = ends.find((seq) => seq >= atSeq);
|
|
21
|
+
if (firstAfter !== void 0) return firstAfter;
|
|
22
|
+
const lastStart = [...events].reverse().find((event) => event.type === "turn/start");
|
|
23
|
+
if (lastStart !== void 0 && lastStart.seq <= atSeq) throw new SessionBranchError(`anchor ${atSeq} lies inside an open turn`, "OPEN_TURN");
|
|
24
|
+
const last = ends.at(-1);
|
|
25
|
+
if (last === void 0) throw new SessionBranchError("session has no closed turn", "OPEN_TURN");
|
|
26
|
+
return last;
|
|
27
|
+
}
|
|
28
|
+
function renumber(events, offset) {
|
|
29
|
+
return events.map((event, index) => ({
|
|
30
|
+
...event,
|
|
31
|
+
seq: offset + index
|
|
32
|
+
}));
|
|
33
|
+
}
|
|
34
|
+
function mintSessionId() {
|
|
35
|
+
return `session-${randomUUID()}`;
|
|
36
|
+
}
|
|
37
|
+
function truncateLiveSession(session, newLength) {
|
|
38
|
+
const s = session;
|
|
39
|
+
s.log.length = newLength;
|
|
40
|
+
s.eventsSnapshot = void 0;
|
|
41
|
+
s.headerFold = void 0;
|
|
42
|
+
s.headerFoldSeq = 0;
|
|
43
|
+
s.contextFold = void 0;
|
|
44
|
+
s.contextFoldSeq = 0;
|
|
45
|
+
s.derived = [];
|
|
46
|
+
s.derivedNodes = 0;
|
|
47
|
+
s.derivedGeneration = 0;
|
|
48
|
+
s.surfaceManager._state = {
|
|
49
|
+
nodes: [],
|
|
50
|
+
replaceGeneration: 0
|
|
51
|
+
};
|
|
52
|
+
s.surfaceManager._lastProcessedSeq = s.surfaceManager.baseSeq - 1;
|
|
53
|
+
s.surfaceManager._pendingPlan = void 0;
|
|
54
|
+
}
|
|
55
|
+
function replaceLiveSessionLog(session, events) {
|
|
56
|
+
const s = session;
|
|
57
|
+
s.log.length = 0;
|
|
58
|
+
s.log.push(...events);
|
|
59
|
+
s.eventsSnapshot = void 0;
|
|
60
|
+
s.headerFold = void 0;
|
|
61
|
+
s.headerFoldSeq = 0;
|
|
62
|
+
s.contextFold = void 0;
|
|
63
|
+
s.contextFoldSeq = 0;
|
|
64
|
+
s.derived = [];
|
|
65
|
+
s.derivedNodes = 0;
|
|
66
|
+
s.derivedGeneration = 0;
|
|
67
|
+
s.surfaceManager._state = {
|
|
68
|
+
nodes: [],
|
|
69
|
+
replaceGeneration: 0
|
|
70
|
+
};
|
|
71
|
+
s.surfaceManager._lastProcessedSeq = s.surfaceManager.baseSeq - 1;
|
|
72
|
+
s.surfaceManager._pendingPlan = void 0;
|
|
73
|
+
}
|
|
74
|
+
var SessionBranchRdbProvider = class {
|
|
75
|
+
persistence;
|
|
76
|
+
live;
|
|
77
|
+
name = "session-rdb";
|
|
78
|
+
constructor(persistence, live = {
|
|
79
|
+
getSession: () => void 0,
|
|
80
|
+
getAgent: () => void 0,
|
|
81
|
+
flush: async () => true,
|
|
82
|
+
setCoordinatorCursor: () => {},
|
|
83
|
+
setCoordinatorState: () => {},
|
|
84
|
+
setCoordinatorSeedLength: () => {}
|
|
85
|
+
}) {
|
|
86
|
+
this.persistence = persistence;
|
|
87
|
+
this.live = live;
|
|
88
|
+
}
|
|
89
|
+
async readBranchPrefix(id, atSeq, mode = "after", signal) {
|
|
90
|
+
const { events } = await this.readRawEvents(id, signal);
|
|
91
|
+
const boundary = locateTurnEnd(events, atSeq, mode);
|
|
92
|
+
return {
|
|
93
|
+
seq: boundary,
|
|
94
|
+
events: events.slice(0, boundary + 1)
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
async readRawEvents(id, signal) {
|
|
98
|
+
const stored = await this.persistence.loadStored(id, signal);
|
|
99
|
+
if (stored === void 0) throw new SessionBranchError(`session "${id}" not found`, "SESSION_NOT_FOUND");
|
|
100
|
+
return {
|
|
101
|
+
meta: stored.meta,
|
|
102
|
+
events: stored.events
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
async forkFrom(sourceId, options = {}, signal) {
|
|
106
|
+
signal?.throwIfAborted();
|
|
107
|
+
const { atSeq, anchorMode = "after", seedSuffix = [], childSessionId, meta = {} } = options;
|
|
108
|
+
const source = await this.persistence.inspect(sourceId, signal);
|
|
109
|
+
const boundary = locateTurnEnd(source.events, atSeq, anchorMode);
|
|
110
|
+
const prefix = source.events.slice(0, boundary + 1);
|
|
111
|
+
const childId = childSessionId ?? mintSessionId();
|
|
112
|
+
const childMeta = {
|
|
113
|
+
version: SESSION_FORMAT_VERSION,
|
|
114
|
+
id: childId,
|
|
115
|
+
createdAt: meta.createdAt ?? Date.now(),
|
|
116
|
+
...meta.cwd !== void 0 ? { cwd: meta.cwd } : source.meta.cwd !== void 0 ? { cwd: source.meta.cwd } : {},
|
|
117
|
+
parentSession: sourceId,
|
|
118
|
+
isSeeded: true,
|
|
119
|
+
...meta.agentPreset !== void 0 ? { agentPreset: meta.agentPreset } : source.meta.agentPreset !== void 0 ? { agentPreset: source.meta.agentPreset } : {},
|
|
120
|
+
...meta.origin !== void 0 ? { origin: meta.origin } : {},
|
|
121
|
+
...meta.delegationDepth !== void 0 ? { delegationDepth: meta.delegationDepth } : {}
|
|
122
|
+
};
|
|
123
|
+
const seed = [...renumber(prefix, 0), ...renumber(seedSuffix, prefix.length)];
|
|
124
|
+
const internals = this.persistence.internals();
|
|
125
|
+
const sourceRows = await internals.backend.getEventRows(sourceId);
|
|
126
|
+
const sourceEventIds = new Map(sourceRows.map((row) => [row.fOriginalSeq, row.fEventId]));
|
|
127
|
+
const reuse = /* @__PURE__ */ new Map();
|
|
128
|
+
for (const event of prefix) {
|
|
129
|
+
const eventId = sourceEventIds.get(event.seq);
|
|
130
|
+
if (eventId !== void 0) reuse.set(event.seq, eventId);
|
|
131
|
+
}
|
|
132
|
+
internals.registerReuseEventIds(childId, reuse);
|
|
133
|
+
await this.persistence.create(childMeta, prefix.length);
|
|
134
|
+
if (seed.length > 0) await this.persistence.append(childId, seed);
|
|
135
|
+
return childId;
|
|
136
|
+
}
|
|
137
|
+
async rewind(id, toBoundary, signal) {
|
|
138
|
+
signal?.throwIfAborted();
|
|
139
|
+
if (!Number.isSafeInteger(toBoundary) || toBoundary < -1) throw new SessionBranchError(`rewind boundary must be a non-negative safe integer, got ${toBoundary}`, "INVALID_BOUNDARY");
|
|
140
|
+
const live = this.live.getSession(id);
|
|
141
|
+
if (live !== void 0) await this.live.flush(live);
|
|
142
|
+
const raw = live === void 0 ? await this.readRawEvents(id, signal) : void 0;
|
|
143
|
+
const inspection = live === void 0 ? void 0 : await this.persistence.inspect(id, signal);
|
|
144
|
+
const events = live === void 0 ? raw.events : inspection.events;
|
|
145
|
+
const meta = live === void 0 ? raw.meta : inspection.meta;
|
|
146
|
+
const boundaryEvent = events[toBoundary];
|
|
147
|
+
if (toBoundary === -1) {} else if (boundaryEvent === void 0) throw new SessionBranchError(`rewind boundary ${toBoundary} does not exist in session "${id}"`, "INVALID_BOUNDARY");
|
|
148
|
+
else if (boundaryEvent.type !== "turn/end" && boundaryEvent.type !== "user/message") throw new SessionBranchError(`rewind boundary ${toBoundary} is not a turn/end or user/message (${boundaryEvent.type})`, "INVALID_BOUNDARY");
|
|
149
|
+
const rawKeepLength = toBoundary === -1 ? 0 : boundaryEvent.type === "turn/end" ? toBoundary + 1 : toBoundary;
|
|
150
|
+
const keepLength = balanceRewindPrefix(events.slice(0, rawKeepLength)).length;
|
|
151
|
+
const internals = this.persistence.internals();
|
|
152
|
+
const denseBoundary = live === void 0 ? keepLength - 1 : events.slice(0, keepLength).filter(isPersistedEvent).length - 1;
|
|
153
|
+
const newSeedLength = await internals.backend.transaction(async (tx) => {
|
|
154
|
+
signal?.throwIfAborted();
|
|
155
|
+
const head = await tx.getHead(id);
|
|
156
|
+
if (denseBoundary > head.fHeadSequence) throw new SessionBranchError(`rewind boundary ${toBoundary} is beyond the stored head ${head.fHeadSequence}`, "INVALID_BOUNDARY");
|
|
157
|
+
if (denseBoundary < head.fHeadSequence) {
|
|
158
|
+
await tx.deleteBridgeTail(id, denseBoundary + 1);
|
|
159
|
+
const prev = denseBoundary === -1 ? void 0 : await tx.getPrevBridge(id, denseBoundary);
|
|
160
|
+
if (prev === void 0) await tx.updateHead(id, "", -1);
|
|
161
|
+
else await tx.updateHead(id, prev.fEventId, prev.fSequence);
|
|
162
|
+
}
|
|
163
|
+
const storedSeedLength = await tx.getSeedLength(id);
|
|
164
|
+
let shrunk = storedSeedLength;
|
|
165
|
+
if (storedSeedLength !== null && storedSeedLength > denseBoundary + 1) {
|
|
166
|
+
await tx.updateSeedLength(id, denseBoundary + 1);
|
|
167
|
+
shrunk = denseBoundary + 1;
|
|
168
|
+
}
|
|
169
|
+
await tx.bumpRevision(id);
|
|
170
|
+
return shrunk;
|
|
171
|
+
});
|
|
172
|
+
if (newSeedLength !== null) this.live.setCoordinatorSeedLength(id, newSeedLength);
|
|
173
|
+
internals.writeGuard.confirmHead(id, denseBoundary);
|
|
174
|
+
if (live !== void 0) {
|
|
175
|
+
truncateLiveSession(live, keepLength);
|
|
176
|
+
const agent = this.live.getAgent(id);
|
|
177
|
+
if (agent !== void 0) {
|
|
178
|
+
agent.requestHeaderLogged = false;
|
|
179
|
+
const lastTurn = live.snapshotEvents().findLast((e) => e.type === "turn/start")?.data.turn ?? 0;
|
|
180
|
+
const phase = agent.phase;
|
|
181
|
+
if (phase !== void 0) phase.lastTurn = lastTurn;
|
|
182
|
+
}
|
|
183
|
+
this.live.setCoordinatorCursor(id, keepLength);
|
|
184
|
+
} else if (boundaryEvent?.type === "user/message") this.live.setCoordinatorState(id, keepLength, meta);
|
|
185
|
+
else await this.persistence.load(id);
|
|
186
|
+
const row = await internals.backend.getSession(id);
|
|
187
|
+
if (row === void 0) return {
|
|
188
|
+
header: {
|
|
189
|
+
version: SESSION_FORMAT_VERSION,
|
|
190
|
+
id,
|
|
191
|
+
createdAt: meta.createdAt,
|
|
192
|
+
...meta.cwd !== void 0 ? { cwd: meta.cwd } : {},
|
|
193
|
+
...meta.parentSession !== void 0 ? { parentSession: meta.parentSession } : {},
|
|
194
|
+
isSeeded: meta.isSeeded,
|
|
195
|
+
...meta.origin !== void 0 ? { origin: meta.origin } : {},
|
|
196
|
+
...meta.delegationDepth !== void 0 ? { delegationDepth: meta.delegationDepth } : {},
|
|
197
|
+
...meta.agentPreset !== void 0 ? { agentPreset: meta.agentPreset } : {}
|
|
198
|
+
},
|
|
199
|
+
revision: await internals.readStoredRevision(id) ?? await this.persistence.readStoredRevision(id)
|
|
200
|
+
};
|
|
201
|
+
return {
|
|
202
|
+
header: rowToMeta(row),
|
|
203
|
+
revision: await internals.readStoredRevision(id)
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
};
|
|
207
|
+
var SessionBranchRdb = class extends SessionBranch {
|
|
208
|
+
static inject = ["sessionPersistence", "sessions"];
|
|
209
|
+
constructor(ctx) {
|
|
210
|
+
super(ctx);
|
|
211
|
+
}
|
|
212
|
+
provider = new SessionBranchRdbProvider(this.ctx.sessionPersistence, {
|
|
213
|
+
getSession: (id) => this.ctx.sessions.get(id),
|
|
214
|
+
getAgent: (id) => {
|
|
215
|
+
return this.ctx.get("agents")?.get(id);
|
|
216
|
+
},
|
|
217
|
+
flush: (session) => this.ctx.sessions.flush(session),
|
|
218
|
+
setCoordinatorCursor: (id, cursor) => {
|
|
219
|
+
const state = this.ctx.sessionPersistence.coordinator?.states?.get(id);
|
|
220
|
+
if (state !== void 0) state.cursor = cursor;
|
|
221
|
+
},
|
|
222
|
+
setCoordinatorState: (id, cursor, meta) => {
|
|
223
|
+
const states = this.ctx.sessionPersistence.coordinator?.states;
|
|
224
|
+
if (states === void 0) return;
|
|
225
|
+
const state = states.get(id);
|
|
226
|
+
if (state !== void 0) state.cursor = cursor;
|
|
227
|
+
else states.set(id, {
|
|
228
|
+
meta,
|
|
229
|
+
cursor,
|
|
230
|
+
materialized: true
|
|
231
|
+
});
|
|
232
|
+
},
|
|
233
|
+
setCoordinatorSeedLength: (id, seedLength) => {
|
|
234
|
+
const state = this.ctx.sessionPersistence.coordinator?.states?.get(id);
|
|
235
|
+
if (state?.storage !== void 0 && state.storage.inheritedEventCount > seedLength) state.storage = {
|
|
236
|
+
...state.storage,
|
|
237
|
+
inheritedEventCount: seedLength
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
});
|
|
241
|
+
readBranchPrefix(id, atSeq, mode, signal) {
|
|
242
|
+
return this.provider.readBranchPrefix(id, atSeq, mode, signal);
|
|
243
|
+
}
|
|
244
|
+
readRawEvents(id, signal) {
|
|
245
|
+
return this.provider.readRawEvents(id, signal);
|
|
246
|
+
}
|
|
247
|
+
forkFrom(sourceId, options, signal) {
|
|
248
|
+
return this.provider.forkFrom(sourceId, options, signal);
|
|
249
|
+
}
|
|
250
|
+
rewind(id, toBoundary, signal) {
|
|
251
|
+
return this.provider.rewind(id, toBoundary, signal);
|
|
252
|
+
}
|
|
253
|
+
syncLiveCursor(sessionId) {
|
|
254
|
+
const live = this.ctx.sessions.get(sessionId);
|
|
255
|
+
if (live === void 0) return;
|
|
256
|
+
const state = this.ctx.sessionPersistence.coordinator?.states?.get(sessionId);
|
|
257
|
+
if (state === void 0) return;
|
|
258
|
+
let cursor = state.cursor;
|
|
259
|
+
while (live.snapshotEvents()[cursor]?.ignorable === true) cursor += 1;
|
|
260
|
+
state.cursor = cursor;
|
|
261
|
+
}
|
|
262
|
+
async timeline(sessionId, signal) {
|
|
263
|
+
const persistence = this.ctx.sessionPersistence;
|
|
264
|
+
const snapshots = await persistence.listSnapshots(signal);
|
|
265
|
+
const readOwnEvents = async (id, fromSeq, s) => {
|
|
266
|
+
const live = this.ctx.sessions.get(id);
|
|
267
|
+
if (live !== void 0) return live.snapshotEvents().slice(fromSeq);
|
|
268
|
+
return (await persistence.readFrom(id, fromSeq, s)).events;
|
|
269
|
+
};
|
|
270
|
+
return buildTimeline(snapshots, readOwnEvents, sessionId, signal);
|
|
271
|
+
}
|
|
272
|
+
};
|
|
273
|
+
//#endregion
|
|
274
|
+
//#region src/import.ts
|
|
275
|
+
const SESSION_LOG_ARTIFACT_FILENAME = "session.jsonl";
|
|
276
|
+
const SESSION_IMPORT_PATH = "/api/session.import";
|
|
277
|
+
const MAX_IMPORT_ZIP_BYTES = 67108864;
|
|
278
|
+
function expandProvenanceFromStorage(parsed) {
|
|
279
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) throw new TypeError("imported session records must be objects");
|
|
280
|
+
const record = parsed;
|
|
281
|
+
if (record.sourceEventSeqs === void 0) return parsed;
|
|
282
|
+
if (!Number.isSafeInteger(record.seq) || record.seq < 0) throw new TypeError("imported session event seq must be a non-negative safe integer");
|
|
283
|
+
return {
|
|
284
|
+
...record,
|
|
285
|
+
sourceEventSeqs: decodeSeqRanges(record.sourceEventSeqs, record.seq + 1)
|
|
286
|
+
};
|
|
287
|
+
}
|
|
288
|
+
function parseJsonlArtifact(content) {
|
|
289
|
+
const lines = content.split("\n");
|
|
290
|
+
if (lines.length === 0 || lines[0] === "") throw new Error("imported session log is empty");
|
|
291
|
+
let header;
|
|
292
|
+
try {
|
|
293
|
+
header = JSON.parse(lines[0]);
|
|
294
|
+
} catch {
|
|
295
|
+
throw new Error("imported session log has an unparsable header line");
|
|
296
|
+
}
|
|
297
|
+
if (typeof header !== "object" || header === null || header["type"] !== "session" || typeof header["id"] !== "string" || typeof header["version"] !== "number" || !Number.isSafeInteger(header["createdAt"]) || header["createdAt"] < 0) throw new Error("imported session log has an invalid header line");
|
|
298
|
+
const seedLength = header["seedLength"];
|
|
299
|
+
if (seedLength !== void 0 && (!Number.isSafeInteger(seedLength) || seedLength < 0)) throw new Error("imported session log has an invalid seedLength");
|
|
300
|
+
const events = [];
|
|
301
|
+
for (let i = 1; i < lines.length; i++) {
|
|
302
|
+
const line = lines[i];
|
|
303
|
+
if (line === void 0 || line === "") continue;
|
|
304
|
+
let parsed;
|
|
305
|
+
try {
|
|
306
|
+
parsed = JSON.parse(line);
|
|
307
|
+
} catch {
|
|
308
|
+
throw new Error(`imported session log has an unparsable event line at ${i}`);
|
|
309
|
+
}
|
|
310
|
+
for (const event of decodeStorageRecord(expandProvenanceFromStorage(parsed))) events.push(event);
|
|
311
|
+
}
|
|
312
|
+
for (let i = 0; i < events.length; i++) if (events[i].seq !== i) throw new Error(`imported session log seq gap at ${i} (got ${events[i].seq}); import requires a dense log`);
|
|
313
|
+
const origin = header["origin"];
|
|
314
|
+
const delegationDepth = header["delegationDepth"];
|
|
315
|
+
const inheritedEventCount = Math.min(seedLength ?? 0, events.length);
|
|
316
|
+
return {
|
|
317
|
+
meta: {
|
|
318
|
+
version: header["version"],
|
|
319
|
+
id: header["id"],
|
|
320
|
+
createdAt: header["createdAt"],
|
|
321
|
+
...typeof header["cwd"] === "string" ? { cwd: header["cwd"] } : {},
|
|
322
|
+
...typeof header["parentSession"] === "string" ? { parentSession: header["parentSession"] } : {},
|
|
323
|
+
isSeeded: seedLength !== void 0,
|
|
324
|
+
...origin === "subagent" ? { origin } : {},
|
|
325
|
+
...Number.isSafeInteger(delegationDepth) && delegationDepth > 0 ? { delegationDepth } : {},
|
|
326
|
+
...typeof header["agentPreset"] === "string" ? { agentPreset: header["agentPreset"] } : {}
|
|
327
|
+
},
|
|
328
|
+
inheritedEventCount: SessionLogOffset(inheritedEventCount),
|
|
329
|
+
events
|
|
330
|
+
};
|
|
331
|
+
}
|
|
332
|
+
function parseImportZip(zip) {
|
|
333
|
+
let entries;
|
|
334
|
+
try {
|
|
335
|
+
entries = unzipSync(zip);
|
|
336
|
+
} catch {
|
|
337
|
+
throw new Error("imported zip is not a valid ZIP archive");
|
|
338
|
+
}
|
|
339
|
+
const artifact = entries[SESSION_LOG_ARTIFACT_FILENAME];
|
|
340
|
+
if (artifact === void 0) throw new Error(`imported zip is missing ${SESSION_LOG_ARTIFACT_FILENAME}`);
|
|
341
|
+
return parseJsonlArtifact(new TextDecoder().decode(artifact));
|
|
342
|
+
}
|
|
343
|
+
async function persistImport(persistence, branch, imported, targetId, sessions) {
|
|
344
|
+
const id = targetId ?? `session-${randomUUID()}`;
|
|
345
|
+
if (targetId !== void 0) {
|
|
346
|
+
if (branch === void 0) throw new Error("sessionBranch service is unavailable");
|
|
347
|
+
await branch.rewind(targetId, -1);
|
|
348
|
+
} else await persistence.create({
|
|
349
|
+
...imported.meta,
|
|
350
|
+
id
|
|
351
|
+
}, imported.inheritedEventCount);
|
|
352
|
+
if (imported.events.length > 0) await persistence.append(id, imported.events);
|
|
353
|
+
if (targetId !== void 0) {
|
|
354
|
+
const live = sessions?.get(targetId);
|
|
355
|
+
if (live !== void 0) replaceLiveSessionLog(live, imported.events);
|
|
356
|
+
}
|
|
357
|
+
return id;
|
|
358
|
+
}
|
|
359
|
+
function registerSessionImport(ctx, persistence) {
|
|
360
|
+
ctx.inject(["webServer", "connection"], (webCtx) => {
|
|
361
|
+
const webServer = webCtx.webServer;
|
|
362
|
+
const connection = webCtx.get("connection");
|
|
363
|
+
return webCtx.effect(() => webServer.register({
|
|
364
|
+
kind: "exact",
|
|
365
|
+
path: SESSION_IMPORT_PATH,
|
|
366
|
+
handler: async (req, res) => {
|
|
367
|
+
const rejection = connection.requestRejection(req);
|
|
368
|
+
if (rejection !== void 0) {
|
|
369
|
+
res.writeHead(rejection);
|
|
370
|
+
res.end(rejection === 401 ? "unauthorized" : "forbidden");
|
|
371
|
+
return;
|
|
372
|
+
}
|
|
373
|
+
const chunks = [];
|
|
374
|
+
for await (const chunk of req) chunks.push(chunk);
|
|
375
|
+
const body = Buffer.concat(chunks);
|
|
376
|
+
let envelope;
|
|
377
|
+
try {
|
|
378
|
+
envelope = JSON.parse(body.toString("utf8"));
|
|
379
|
+
} catch {
|
|
380
|
+
res.writeHead(400, { "content-type": "application/json" });
|
|
381
|
+
res.end(JSON.stringify({ error: "request body is not JSON" }));
|
|
382
|
+
return;
|
|
383
|
+
}
|
|
384
|
+
if (typeof envelope.zip !== "string" || envelope.zip === "") {
|
|
385
|
+
res.writeHead(400, { "content-type": "application/json" });
|
|
386
|
+
res.end(JSON.stringify({ error: "missing zip field" }));
|
|
387
|
+
return;
|
|
388
|
+
}
|
|
389
|
+
if (envelope.sessionId !== void 0 && (typeof envelope.sessionId !== "string" || envelope.sessionId === "")) {
|
|
390
|
+
res.writeHead(400, { "content-type": "application/json" });
|
|
391
|
+
res.end(JSON.stringify({ error: "sessionId must be a non-empty string" }));
|
|
392
|
+
return;
|
|
393
|
+
}
|
|
394
|
+
let zip;
|
|
395
|
+
try {
|
|
396
|
+
zip = Buffer.from(envelope.zip, "base64");
|
|
397
|
+
} catch {
|
|
398
|
+
res.writeHead(400, { "content-type": "application/json" });
|
|
399
|
+
res.end(JSON.stringify({ error: "zip field is not valid base64" }));
|
|
400
|
+
return;
|
|
401
|
+
}
|
|
402
|
+
if (zip.byteLength > MAX_IMPORT_ZIP_BYTES) {
|
|
403
|
+
res.writeHead(413, { "content-type": "application/json" });
|
|
404
|
+
res.end(JSON.stringify({ error: "imported zip exceeds the size limit" }));
|
|
405
|
+
return;
|
|
406
|
+
}
|
|
407
|
+
let imported;
|
|
408
|
+
try {
|
|
409
|
+
imported = parseImportZip(zip);
|
|
410
|
+
} catch (error) {
|
|
411
|
+
res.writeHead(400, { "content-type": "application/json" });
|
|
412
|
+
res.end(JSON.stringify({ error: error instanceof Error ? error.message : "imported zip is invalid" }));
|
|
413
|
+
return;
|
|
414
|
+
}
|
|
415
|
+
const targetId = typeof envelope.sessionId === "string" ? envelope.sessionId : void 0;
|
|
416
|
+
const branch = webCtx.get("sessionBranch");
|
|
417
|
+
try {
|
|
418
|
+
const sessions = webCtx.get("sessions");
|
|
419
|
+
const id = await persistImport(persistence, branch, imported, targetId, sessions);
|
|
420
|
+
res.writeHead(200, { "content-type": "application/json" });
|
|
421
|
+
res.end(JSON.stringify({ sessionId: id }));
|
|
422
|
+
} catch (error) {
|
|
423
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
424
|
+
if (targetId !== void 0 && /not found/i.test(message)) {
|
|
425
|
+
res.writeHead(404, { "content-type": "application/json" });
|
|
426
|
+
res.end(JSON.stringify({ error: `session "${targetId}" not found` }));
|
|
427
|
+
return;
|
|
428
|
+
}
|
|
429
|
+
res.writeHead(500, { "content-type": "application/json" });
|
|
430
|
+
res.end(JSON.stringify({ error: error instanceof Error ? error.message : "import failed" }));
|
|
431
|
+
return;
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
}), `session-rdb: ${SESSION_IMPORT_PATH} route`);
|
|
435
|
+
});
|
|
436
|
+
}
|
|
437
|
+
//#endregion
|
|
438
|
+
export { parseJsonlArtifact as a, SessionBranchRdb as c, parseImportZip as i, SessionBranchRdbProvider as l, SESSION_LOG_ARTIFACT_FILENAME as n, persistImport as o, expandProvenanceFromStorage as r, registerSessionImport as s, SESSION_IMPORT_PATH as t, locateTurnEnd as u };
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { n as SessionPersistenceRdb } from "./index-uUvn6gYp.mjs";
|
|
2
|
+
import { SessionStorageMetadata } from "@deepseek-ai/dsh-session-persistence";
|
|
3
|
+
import { Session, SessionEvent, SessionId } from "@deepseek-ai/dsh-session";
|
|
4
|
+
import { Context } from "@deepseek-ai/cordis";
|
|
5
|
+
//#region src/import.d.ts
|
|
6
|
+
declare const SESSION_LOG_ARTIFACT_FILENAME = "session.jsonl";
|
|
7
|
+
declare const SESSION_IMPORT_PATH = "/api/session.import";
|
|
8
|
+
declare function expandProvenanceFromStorage(parsed: unknown): unknown;
|
|
9
|
+
declare function parseJsonlArtifact(content: string): SessionStorageMetadata & {
|
|
10
|
+
events: SessionEvent[];
|
|
11
|
+
};
|
|
12
|
+
declare function parseImportZip(zip: Uint8Array): SessionStorageMetadata & {
|
|
13
|
+
events: SessionEvent[];
|
|
14
|
+
};
|
|
15
|
+
declare function persistImport(persistence: SessionPersistenceRdb, branch: {
|
|
16
|
+
rewind(id: SessionId, toBoundary: number): Promise<unknown>;
|
|
17
|
+
} | undefined, imported: SessionStorageMetadata & {
|
|
18
|
+
events: SessionEvent[];
|
|
19
|
+
}, targetId?: SessionId, sessions?: {
|
|
20
|
+
get(id: SessionId): Session | undefined;
|
|
21
|
+
}): Promise<SessionId>;
|
|
22
|
+
declare function registerSessionImport(ctx: Context, persistence: SessionPersistenceRdb): void;
|
|
23
|
+
//#endregion
|
|
24
|
+
export { SESSION_IMPORT_PATH, SESSION_LOG_ARTIFACT_FILENAME, expandProvenanceFromStorage, parseImportZip, parseJsonlArtifact, persistImport, registerSessionImport };
|
package/dist/import.mjs
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import { a as parseJsonlArtifact, i as parseImportZip, n as SESSION_LOG_ARTIFACT_FILENAME, o as persistImport, r as expandProvenanceFromStorage, s as registerSessionImport, t as SESSION_IMPORT_PATH } from "./import-D2vUnUZe.mjs";
|
|
2
|
+
export { SESSION_IMPORT_PATH, SESSION_LOG_ARTIFACT_FILENAME, expandProvenanceFromStorage, parseImportZip, parseJsonlArtifact, persistImport, registerSessionImport };
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { _ as WriteGuard, o as JournalMode, v as Backend } from "./schema-CFXZURX7.mjs";
|
|
2
|
+
import z from "@deepseek-ai/schemastery";
|
|
3
|
+
import { PersistenceBackend, SessionLocation, SessionPersistence, SessionPersistenceRevision, SessionPersistenceSnapshot, SessionStorageMetadata, StoredPrefix, StoredSuffix } from "@deepseek-ai/dsh-session-persistence";
|
|
4
|
+
import { Session, SessionEvent, SessionHeader, SessionId } from "@deepseek-ai/dsh-session";
|
|
5
|
+
import { BranchAnchorMode, BranchBoundary, ForkFromOptions, SessionBranch, SessionBranchProvider } from "@morlay/session-branch";
|
|
6
|
+
import { Context } from "@deepseek-ai/cordis";
|
|
7
|
+
//#region src/branch.d.ts
|
|
8
|
+
declare function locateTurnEnd(events: readonly SessionEvent[], atSeq?: number, mode?: BranchAnchorMode): number;
|
|
9
|
+
interface LiveSessionHooks {
|
|
10
|
+
getSession(id: SessionId): Session | undefined;
|
|
11
|
+
getAgent(id: SessionId): LiveAgentLike | undefined;
|
|
12
|
+
flush(session: Session): Promise<boolean>;
|
|
13
|
+
setCoordinatorCursor(id: SessionId, cursor: number): void;
|
|
14
|
+
setCoordinatorState(id: SessionId, cursor: number, meta: SessionHeader): void;
|
|
15
|
+
setCoordinatorSeedLength(id: SessionId, seedLength: number): void;
|
|
16
|
+
}
|
|
17
|
+
interface LiveAgentLike {
|
|
18
|
+
session: Session;
|
|
19
|
+
requestHeaderLogged?: boolean;
|
|
20
|
+
}
|
|
21
|
+
declare class SessionBranchRdbProvider implements SessionBranchProvider {
|
|
22
|
+
private readonly persistence;
|
|
23
|
+
private readonly live;
|
|
24
|
+
readonly name = "session-rdb";
|
|
25
|
+
constructor(persistence: SessionPersistenceRdb, live?: LiveSessionHooks);
|
|
26
|
+
readBranchPrefix(id: SessionId, atSeq?: number, mode?: BranchAnchorMode, signal?: AbortSignal): Promise<BranchBoundary>;
|
|
27
|
+
readRawEvents(id: SessionId, signal?: AbortSignal): Promise<{
|
|
28
|
+
meta: SessionHeader;
|
|
29
|
+
events: readonly SessionEvent[];
|
|
30
|
+
}>;
|
|
31
|
+
forkFrom(sourceId: SessionId, options?: ForkFromOptions, signal?: AbortSignal): Promise<SessionId>;
|
|
32
|
+
rewind(id: SessionId, toBoundary: number, signal?: AbortSignal): Promise<SessionPersistenceSnapshot>;
|
|
33
|
+
}
|
|
34
|
+
declare class SessionBranchRdb extends SessionBranch {
|
|
35
|
+
static inject: string[];
|
|
36
|
+
constructor(ctx: import("@deepseek-ai/cordis").Context);
|
|
37
|
+
private readonly provider;
|
|
38
|
+
readBranchPrefix(id: SessionId, atSeq?: number, mode?: BranchAnchorMode, signal?: AbortSignal): Promise<BranchBoundary>;
|
|
39
|
+
readRawEvents(id: SessionId, signal?: AbortSignal): Promise<{
|
|
40
|
+
meta: SessionHeader;
|
|
41
|
+
events: readonly SessionEvent[];
|
|
42
|
+
}>;
|
|
43
|
+
forkFrom(sourceId: SessionId, options?: ForkFromOptions, signal?: AbortSignal): Promise<SessionId>;
|
|
44
|
+
rewind(id: SessionId, toBoundary: number, signal?: AbortSignal): Promise<SessionPersistenceSnapshot>;
|
|
45
|
+
syncLiveCursor(sessionId: SessionId): void;
|
|
46
|
+
timeline(sessionId: SessionId, signal?: AbortSignal): Promise<import("@morlay/session-branch").BranchTimeline>;
|
|
47
|
+
}
|
|
48
|
+
//#endregion
|
|
49
|
+
//#region src/index.d.ts
|
|
50
|
+
interface SessionPersistenceRdbInternals {
|
|
51
|
+
readonly backend: Backend;
|
|
52
|
+
readonly writeGuard: WriteGuard;
|
|
53
|
+
create(meta: SessionHeader, inheritedEventCount?: number): Promise<void>;
|
|
54
|
+
append(id: SessionId, events: readonly SessionEvent[]): Promise<void>;
|
|
55
|
+
load(id: SessionId): Promise<import("@deepseek-ai/dsh-session-persistence").SessionInspection>;
|
|
56
|
+
inspect(id: SessionId, signal?: AbortSignal): Promise<import("@deepseek-ai/dsh-session-persistence").SessionInspection>;
|
|
57
|
+
readFrom(id: SessionId, fromSeq: number, signal?: AbortSignal): Promise<import("@deepseek-ai/dsh-session-persistence").SessionEventSuffix>;
|
|
58
|
+
listSnapshots(signal?: AbortSignal): Promise<SessionPersistenceSnapshot[]>;
|
|
59
|
+
readStoredRevision(id: SessionId, signal?: AbortSignal): Promise<import("@deepseek-ai/dsh-session-persistence").SessionPersistenceRevision | undefined>;
|
|
60
|
+
registerReuseEventIds(childId: SessionId, map: ReadonlyMap<number, string>): void;
|
|
61
|
+
}
|
|
62
|
+
type Config = {
|
|
63
|
+
type: "sqlite";
|
|
64
|
+
path: string;
|
|
65
|
+
journalMode?: JournalMode;
|
|
66
|
+
busyTimeout?: number;
|
|
67
|
+
} | {
|
|
68
|
+
type: "postgres";
|
|
69
|
+
connectionString: string;
|
|
70
|
+
schema?: string;
|
|
71
|
+
};
|
|
72
|
+
declare class SessionPersistenceRdb extends SessionPersistence implements PersistenceBackend<number> {
|
|
73
|
+
config: Config;
|
|
74
|
+
static inject: string[];
|
|
75
|
+
static Config: z<Config>;
|
|
76
|
+
static readonly settingsNs = "session-rdb";
|
|
77
|
+
readonly name = "session-rdb";
|
|
78
|
+
readonly supportsRawArtifacts = true;
|
|
79
|
+
readRaw(id: SessionId, signal?: AbortSignal): Promise<import("@deepseek-ai/dsh-session-persistence").SessionRawArtifact | undefined>;
|
|
80
|
+
private readonly backend;
|
|
81
|
+
private storeIdentity;
|
|
82
|
+
private readonly ready;
|
|
83
|
+
private readonly coordinator;
|
|
84
|
+
private readonly writeGuard;
|
|
85
|
+
private readonly reuseEventIds;
|
|
86
|
+
constructor(ctx: Context, config: Config, injectedBackend?: Backend);
|
|
87
|
+
private init;
|
|
88
|
+
locate(_meta: SessionHeader): SessionLocation | undefined;
|
|
89
|
+
create(meta: SessionHeader, inheritedEventCount?: number): Promise<void>;
|
|
90
|
+
append(id: SessionId, events: readonly SessionEvent[]): Promise<void>;
|
|
91
|
+
load(id: SessionId): Promise<import("@deepseek-ai/dsh-session-persistence").SessionInspection>;
|
|
92
|
+
inspect(id: SessionId, signal?: AbortSignal): Promise<import("@deepseek-ai/dsh-session-persistence").SessionInspection>;
|
|
93
|
+
readFrom(id: SessionId, fromSeq: number, signal?: AbortSignal): Promise<import("@deepseek-ai/dsh-session-persistence").SessionEventSuffix>;
|
|
94
|
+
borrowSession(id: SessionId, signal?: AbortSignal): Promise<import("@deepseek-ai/dsh-session-persistence").BorrowedSessionSource>;
|
|
95
|
+
loadStored(id: SessionId, signal?: AbortSignal): Promise<StoredPrefix<number> | undefined>;
|
|
96
|
+
loadStoredFrom(id: SessionId, fromSeq: number, signal?: AbortSignal): Promise<StoredSuffix | undefined>;
|
|
97
|
+
private readPrefix;
|
|
98
|
+
readStoredRevision(id: SessionId, signal?: AbortSignal): Promise<SessionPersistenceRevision | undefined>;
|
|
99
|
+
private readLog;
|
|
100
|
+
appendBatch(storage: SessionStorageMetadata, events: readonly SessionEvent[], _isMaterialized: boolean): Promise<void>;
|
|
101
|
+
commitRepair(storage: SessionStorageMetadata, tornMarker: number | undefined, closers: readonly SessionEvent[]): Promise<void>;
|
|
102
|
+
list(signal?: AbortSignal): Promise<SessionHeader[]>;
|
|
103
|
+
listSnapshots(signal?: AbortSignal): Promise<Array<SessionPersistenceSnapshot & {
|
|
104
|
+
inheritedEventCount: number;
|
|
105
|
+
}>>;
|
|
106
|
+
close(): Promise<void>;
|
|
107
|
+
registerReuseEventIds(childId: SessionId, map: ReadonlyMap<number, string>): void;
|
|
108
|
+
internals(): SessionPersistenceRdbInternals;
|
|
109
|
+
}
|
|
110
|
+
//#endregion
|
|
111
|
+
export { SessionBranchRdbProvider as a, SessionBranchRdb as i, SessionPersistenceRdb as n, locateTurnEnd as o, SessionPersistenceRdbInternals as r, Config as t };
|