@morlay/session-rdb 0.1.9 → 0.1.10-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.md +24 -3
- package/dist/artifact.d.mts +118 -3
- package/dist/artifact.mjs +5 -3
- package/dist/{log-C0q5svm7.d.mts → backend-BKtJ5sO5.d.mts} +189 -291
- package/dist/backend-rdb-BDCntVDy.mjs +1341 -0
- package/dist/branch-CG1PUlO6.mjs +833 -0
- package/dist/deletion-SSX7IqU8.mjs +35 -0
- package/dist/deletion.d.mts +1 -3
- package/dist/deletion.mjs +2 -2
- package/dist/export-CfJQjQfr.mjs +123 -0
- package/dist/export.d.mts +1 -3
- package/dist/export.mjs +32 -57
- package/dist/import.d.mts +1 -3
- package/dist/import.mjs +32 -78
- package/dist/index-CUD2ZpTA.d.mts +71 -0
- package/dist/index.d.mts +20 -2
- package/dist/index.mjs +1562 -5
- package/dist/open-YoyD_kah.mjs +561 -0
- package/dist/organize.d.mts +9 -0
- package/dist/organize.mjs +30 -0
- package/dist/route-DpfY0zq-.mjs +114 -0
- package/dist/storage.d.mts +41 -50
- package/dist/storage.mjs +3 -1
- package/dist/testing.d.mts +9 -5
- package/dist/testing.mjs +27 -8
- package/dist/usage-row-BQQM-_t6.mjs +36 -0
- package/dist/usage.d.mts +1 -1
- package/dist/usage.mjs +8 -42
- package/dist/write-guard-CYrY58GM.mjs +174 -0
- package/package.json +32 -36
- package/src/artifact.ts +4 -1
- package/src/backend-rdb.ts +331 -0
- package/src/backend.ts +32 -15
- package/src/{branch.ts → branch/index.ts} +125 -112
- package/src/branch/port.ts +62 -0
- package/src/branch/semantics.ts +69 -0
- package/src/db/open.ts +180 -0
- package/src/db/stats.ts +449 -0
- package/src/deletion-error.ts +17 -0
- package/src/deletion.ts +19 -72
- package/src/entities/index.ts +16 -1
- package/src/entities/v3/event-usage.ts +3 -3
- package/src/entities/v3/events.ts +3 -3
- package/src/entities/v3/index.ts +4 -16
- package/src/entities/v3/persistence-state.ts +3 -3
- package/src/entities/v3/schema-meta.ts +3 -3
- package/src/entities/v3/session-counts.ts +3 -3
- package/src/entities/v3/session-events.ts +3 -3
- package/src/entities/v3/session-projcache-rows.ts +3 -3
- package/src/entities/v3/session-usage.ts +3 -3
- package/src/entities/v3/sessions.ts +3 -3
- package/src/entities/v3/storage-records.ts +17 -0
- package/src/entities/v3/storage-units.ts +3 -3
- package/src/{schema.ts → event-shape.ts} +0 -46
- package/src/export.ts +32 -78
- package/src/handles.ts +349 -0
- package/src/http/route.ts +178 -0
- package/src/import-storages.ts +5 -2
- package/src/import.ts +63 -125
- package/src/index.ts +15 -1630
- package/src/legacy.ts +3 -1
- package/src/live-routing.ts +205 -0
- package/src/log/export.ts +84 -0
- package/src/{log.ts → log/repair.ts} +1 -272
- package/src/log/rows.ts +107 -0
- package/src/log/usage-row.ts +64 -0
- package/src/migrations.ts +196 -0
- package/src/organize.ts +38 -0
- package/src/read-path.ts +190 -0
- package/src/routes.ts +2 -1
- package/src/rows.ts +25 -77
- package/src/session-ops.ts +444 -0
- package/src/session-query.ts +17 -4
- package/src/session-store.ts +675 -0
- package/src/stats-fold.ts +44 -0
- package/src/storage-takeover/index.ts +2 -5
- package/src/storage-takeover/legacy-domain-migration.ts +118 -0
- package/src/storage-takeover/projection-cache.ts +139 -77
- package/src/storage-takeover/read-model.ts +189 -0
- package/src/storage-takeover/repository.ts +238 -425
- package/src/storage-takeover/storage-backend.ts +47 -149
- package/src/storage-takeover/types.ts +45 -14
- package/src/storage-takeover/workspace-documents.ts +65 -0
- package/src/storage.ts +4 -2
- package/src/testing.ts +21 -1
- package/src/usage.ts +11 -65
- package/src/version.ts +13 -0
- package/src/write-path.ts +286 -0
- package/dist/branch-DbFW3rJX.mjs +0 -391
- package/dist/deletion-ODZFcbZm.mjs +0 -2218
- package/dist/gc.d.mts +0 -6
- package/dist/gc.mjs +0 -49
- package/dist/log-DYxdjJ-l.mjs +0 -562
- package/dist/routes-DGBlBE_I.mjs +0 -10
- package/dist/sqlite-DjOnMRh2.mjs +0 -1777
- package/drizzle/postgres/20260908072805_v2_initial/migration.sql +0 -58
- package/drizzle/postgres/20260908072805_v2_initial/snapshot.json +0 -686
- package/drizzle/postgres/20260908072806_v3_drop_original_seq/migration.sql +0 -1
- package/drizzle/postgres/20260908072806_v3_drop_original_seq/snapshot.json +0 -673
- package/drizzle/postgres/20260910120001_v3_storage_tables/migration.sql +0 -65
- package/drizzle/postgres/20260910120001_v3_storage_tables/snapshot.json +0 -1183
- package/drizzle/postgres/20260910130001_v3_session_title_backfill/migration.sql +0 -10
- package/drizzle/postgres/20260910130001_v3_session_title_backfill/snapshot.json +0 -1183
- package/drizzle/postgres/20260918120000_v3_event_usage/migration.sql +0 -14
- package/drizzle/postgres/20260918120000_v3_event_usage/snapshot.json +0 -1309
- package/drizzle/postgres/20260921075900_v3_agent_preset/migration.sql +0 -1
- package/drizzle/postgres/20260922090001_v3_pinned_seq/migration.sql +0 -2
- package/drizzle/postgres/20260922093001_v3_last_event_at/migration.sql +0 -10
- package/drizzle/postgres/20260923120000_v3_usage_materialized/migration.sql +0 -48
- package/drizzle/postgres/20261008103001_v3_schedule_tasks/migration.sql +0 -11
- package/drizzle/sqlite/20260908072751_v2_initial/migration.sql +0 -53
- package/drizzle/sqlite/20260908072751_v2_initial/snapshot.json +0 -492
- package/drizzle/sqlite/20260908072752_v3_drop_original_seq/migration.sql +0 -6
- package/drizzle/sqlite/20260908072752_v3_drop_original_seq/snapshot.json +0 -513
- package/drizzle/sqlite/20260910120000_v3_storage_tables/migration.sql +0 -66
- package/drizzle/sqlite/20260910120000_v3_storage_tables/snapshot.json +0 -905
- package/drizzle/sqlite/20260910130000_v3_session_title_backfill/migration.sql +0 -13
- package/drizzle/sqlite/20260910130000_v3_session_title_backfill/snapshot.json +0 -905
- package/drizzle/sqlite/20260918120000_v3_event_usage/migration.sql +0 -14
- package/drizzle/sqlite/20260918120000_v3_event_usage/snapshot.json +0 -1031
- package/drizzle/sqlite/20260921075855_v3_agent_preset/migration.sql +0 -7
- package/drizzle/sqlite/20260921075855_v3_agent_preset/snapshot.json +0 -1041
- package/drizzle/sqlite/20260922090000_v3_pinned_seq/migration.sql +0 -4
- package/drizzle/sqlite/20260922093000_v3_last_event_at/migration.sql +0 -13
- package/drizzle/sqlite/20260923120000_v3_usage_materialized/migration.sql +0 -48
- package/drizzle/sqlite/20261008103000_v3_schedule_tasks/migration.sql +0 -14
- package/src/adapters/index.ts +0 -12
- package/src/adapters/to-postgres.ts +0 -93
- package/src/adapters/to-sqlite.ts +0 -83
- package/src/adapters/types.ts +0 -44
- package/src/drizzle/postgres-v2.ts +0 -10
- package/src/drizzle/postgres-v3.ts +0 -18
- package/src/drizzle/sqlite-v2.ts +0 -9
- package/src/drizzle/sqlite-v3.ts +0 -18
- package/src/entities/v2/index.ts +0 -20
- package/src/entities/v2/session-events.ts +0 -10
- package/src/entities/v3/schedule-tasks.ts +0 -16
- package/src/entities/v3/workspace-sessions.ts +0 -19
- package/src/entities/v3/workspace-state.ts +0 -12
- package/src/entities/v3/workspaces.ts +0 -14
- package/src/gc.ts +0 -77
- package/src/postgres.ts +0 -1015
- package/src/sqlite.ts +0 -1086
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import {
|
|
3
|
+
SessionLogOffset,
|
|
4
|
+
type SessionEvent,
|
|
5
|
+
type SurfaceEventType,
|
|
6
|
+
type SessionHeader,
|
|
7
|
+
type SessionId,
|
|
8
|
+
} from "@deepseek-ai/dsh-session";
|
|
9
|
+
import { validateStoredEvents } from "@deepseek-ai/dsh-session-persistence";
|
|
10
|
+
import type { Backend, BackendTx, EventInsert } from "./backend.ts";
|
|
11
|
+
import type { WriteGuard } from "./write-guard.ts";
|
|
12
|
+
import type { RdbBackendTracker } from "./handles.ts";
|
|
13
|
+
import { EVENT_ENCODING } from "./version.ts";
|
|
14
|
+
import { eventDimensions } from "./event-shape.ts";
|
|
15
|
+
import { usageRowOf, type EventUsageRow } from "./log/usage-row.ts";
|
|
16
|
+
import { sealOwnEvents } from "./log/repair.ts";
|
|
17
|
+
import { sessionCountBuckets, sessionUsageBuckets } from "./stats-fold.ts";
|
|
18
|
+
|
|
19
|
+
function maxEventTime(events: readonly { readonly time?: number }[]): number | undefined {
|
|
20
|
+
let max: number | undefined;
|
|
21
|
+
for (const event of events) {
|
|
22
|
+
const time = event.time;
|
|
23
|
+
if (typeof time !== "number") continue;
|
|
24
|
+
if (max === undefined || time > max) max = time;
|
|
25
|
+
}
|
|
26
|
+
return max;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function describeError(error: unknown): string {
|
|
30
|
+
return error instanceof Error ? error.message : "unknown error";
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export interface WritePathDeps {
|
|
34
|
+
readonly ready: Promise<void>;
|
|
35
|
+
backend(): Backend;
|
|
36
|
+
readonly tracker: RdbBackendTracker;
|
|
37
|
+
readonly writeGuard: WriteGuard;
|
|
38
|
+
readonly reuseEventIds: Map<SessionId, Map<number, string>>;
|
|
39
|
+
readonly logger: { warn(message: string): void };
|
|
40
|
+
readLog(id: SessionId): Promise<
|
|
41
|
+
| { meta: SessionHeader; inheritedEventCount: number; events: SessionEvent[] }
|
|
42
|
+
| undefined
|
|
43
|
+
>;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* 写侧:空会话物化、批量追加、统计旁路累加与重算、迁移后重写。
|
|
48
|
+
*
|
|
49
|
+
* 事务边界都在这里;`session-store.ts` 只负责装配与契约转发。
|
|
50
|
+
*/
|
|
51
|
+
export function createWritePath(deps: WritePathDeps) {
|
|
52
|
+
return {
|
|
53
|
+
async materializeEmpty(
|
|
54
|
+
meta: SessionHeader,
|
|
55
|
+
inheritedEventCount: SessionLogOffset,
|
|
56
|
+
): Promise<void> {
|
|
57
|
+
await deps.ready;
|
|
58
|
+
await deps.backend().transaction(async (tx) => {
|
|
59
|
+
await tx.upsertSession({ meta, inheritedEventCount }, randomUUID());
|
|
60
|
+
await tx.bumpRevision(meta.id);
|
|
61
|
+
});
|
|
62
|
+
deps.tracker.materialized(meta.id);
|
|
63
|
+
deps.writeGuard.confirmHead(meta.id, -1);
|
|
64
|
+
},
|
|
65
|
+
|
|
66
|
+
async appendBatch(
|
|
67
|
+
meta: SessionHeader,
|
|
68
|
+
inheritedEventCount: SessionLogOffset,
|
|
69
|
+
events: readonly SessionEvent[],
|
|
70
|
+
tornTruncateTo?: number,
|
|
71
|
+
): Promise<boolean> {
|
|
72
|
+
await deps.ready;
|
|
73
|
+
if (events.length === 0) return false;
|
|
74
|
+
|
|
75
|
+
// 本仓库自造的事件类型落库前补上 `ignorable` 信封:不补的话上游那道校验连写入都拒。
|
|
76
|
+
sealOwnEvents(events);
|
|
77
|
+
validateStoredEvents(meta, [...events]);
|
|
78
|
+
|
|
79
|
+
const reuse = deps.reuseEventIds.get(meta.id);
|
|
80
|
+
let confirmedHead = -1;
|
|
81
|
+
// 本批用量行由 `appendEventTail` 折好;事务成功后旁路据此累加 token 汇总表。
|
|
82
|
+
let usageRows: EventUsageRow[] = [];
|
|
83
|
+
await deps.backend().transaction(async (tx) => {
|
|
84
|
+
if (tornTruncateTo !== undefined) {
|
|
85
|
+
await tx.deleteBridgeTail(meta.id, tornTruncateTo);
|
|
86
|
+
const prev = await tx.getPrevBridge(meta.id, tornTruncateTo - 1);
|
|
87
|
+
if (prev === undefined) {
|
|
88
|
+
await tx.updateHead(meta.id, "", -1);
|
|
89
|
+
} else {
|
|
90
|
+
await tx.updateHead(meta.id, prev.fEventId, prev.fSequence);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
await tx.upsertSession({ meta, inheritedEventCount }, randomUUID());
|
|
94
|
+
const head = await tx.getHead(meta.id);
|
|
95
|
+
|
|
96
|
+
deps.writeGuard.assertNoConcurrentWriter(meta.id, head.fHeadSequence);
|
|
97
|
+
const appended = await appendEventTail(
|
|
98
|
+
tx,
|
|
99
|
+
meta,
|
|
100
|
+
events,
|
|
101
|
+
{ parentId: head.fHeadEventId, nextSeq: head.fHeadSequence + 1 },
|
|
102
|
+
reuse,
|
|
103
|
+
);
|
|
104
|
+
usageRows = appended.usageRows;
|
|
105
|
+
await tx.updateHeadAndRevision(
|
|
106
|
+
meta.id,
|
|
107
|
+
appended.headEventId,
|
|
108
|
+
appended.headSequence,
|
|
109
|
+
maxEventTime(events),
|
|
110
|
+
);
|
|
111
|
+
confirmedHead = appended.headSequence;
|
|
112
|
+
});
|
|
113
|
+
// 事务**成功之后**才丢掉复用映射:失败(并发写者校验、唯一键冲突等)时这份映射还没被消费,
|
|
114
|
+
// 重试同一个 append 还要靠它复用父会话的事件行——提前删掉会让前缀事件行被重新插入一遍。
|
|
115
|
+
if (reuse !== undefined) deps.reuseEventIds.delete(meta.id);
|
|
116
|
+
|
|
117
|
+
deps.writeGuard.confirmHead(meta.id, confirmedHead);
|
|
118
|
+
deps.tracker.materialized(meta.id);
|
|
119
|
+
await this.recordSessionStats(meta.id, events, usageRows);
|
|
120
|
+
return true;
|
|
121
|
+
},
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
async recordSessionStats(
|
|
125
|
+
id: SessionId,
|
|
126
|
+
events: readonly SessionEvent[],
|
|
127
|
+
usageRows: readonly EventUsageRow[],
|
|
128
|
+
): Promise<void> {
|
|
129
|
+
const usageBuckets = sessionUsageBuckets(usageRows);
|
|
130
|
+
if (usageBuckets.length > 0) {
|
|
131
|
+
try {
|
|
132
|
+
await deps.backend().incrementSessionUsage(id, usageBuckets);
|
|
133
|
+
} catch (error: unknown) {
|
|
134
|
+
deps.logger.warn(
|
|
135
|
+
`session-rdb: token usage for "${id}" not updated (${describeError(error)})`,
|
|
136
|
+
);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
const countBuckets = sessionCountBuckets(events);
|
|
140
|
+
if (countBuckets.length > 0) {
|
|
141
|
+
try {
|
|
142
|
+
await deps.backend().incrementSessionCounts(id, countBuckets);
|
|
143
|
+
} catch (error: unknown) {
|
|
144
|
+
deps.logger.warn(
|
|
145
|
+
`session-rdb: activity counts for "${id}" not updated (${describeError(error)})`,
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
},
|
|
150
|
+
|
|
151
|
+
// 重算一个会话的两张汇总表(旧日志重写后;best-effort,同 `recordSessionStats`)——
|
|
152
|
+
// fork 与 rewind 都不动统计。
|
|
153
|
+
async rebuildSessionStats(id: SessionId): Promise<void> {
|
|
154
|
+
await deps.ready;
|
|
155
|
+
try {
|
|
156
|
+
await deps.backend().rebuildSessionStats(id);
|
|
157
|
+
} catch (error: unknown) {
|
|
158
|
+
deps.logger.warn(
|
|
159
|
+
`session-rdb: session stats for "${id}" not rebuilt (${describeError(error)})`,
|
|
160
|
+
);
|
|
161
|
+
}
|
|
162
|
+
},
|
|
163
|
+
|
|
164
|
+
// 重算这些用量行的引用标记(会话删除 / 日志重写之后:消失的引用要让孤儿行退出统计)。
|
|
165
|
+
async refreshEventUsageFlags(eventIds: readonly string[]): Promise<void> {
|
|
166
|
+
const unique = [...new Set(eventIds)];
|
|
167
|
+
if (unique.length === 0) return;
|
|
168
|
+
try {
|
|
169
|
+
await deps.backend().refreshEventUsageFlags(unique);
|
|
170
|
+
} catch (error: unknown) {
|
|
171
|
+
deps.logger.warn(
|
|
172
|
+
`session-rdb: usage reference flags not refreshed (${describeError(error)})`,
|
|
173
|
+
);
|
|
174
|
+
}
|
|
175
|
+
},
|
|
176
|
+
|
|
177
|
+
// 会话删除时清掉它的汇总行(引用标记由调用方在拿到被删事件后一并重算)。
|
|
178
|
+
async dropSessionStats(id: SessionId): Promise<void> {
|
|
179
|
+
try {
|
|
180
|
+
await deps.backend().deleteSessionStats(id);
|
|
181
|
+
} catch (error: unknown) {
|
|
182
|
+
deps.logger.warn(
|
|
183
|
+
`session-rdb: session stats for "${id}" not deleted (${describeError(error)})`,
|
|
184
|
+
);
|
|
185
|
+
}
|
|
186
|
+
},
|
|
187
|
+
|
|
188
|
+
async rewriteMigratedLog(
|
|
189
|
+
id: SessionId,
|
|
190
|
+
log: { meta: SessionHeader; inheritedEventCount: number; events: SessionEvent[] },
|
|
191
|
+
): Promise<void> {
|
|
192
|
+
let removed: readonly string[] = [];
|
|
193
|
+
await deps.backend().transaction(async (tx) => {
|
|
194
|
+
// 重写会删光该会话的桥接行再重建,先做并发写者校验(与 `appendBatch` 同一个理由):否则另一实例在
|
|
195
|
+
// open 前 / 期间提交的事件会被静默丢掉;调用方刚读过这份日志,guard 没有该会话时记下当前磁盘 head。
|
|
196
|
+
const head = await tx.getHead(id);
|
|
197
|
+
if (!deps.writeGuard.has(id)) deps.writeGuard.confirmHead(id, head.fHeadSequence);
|
|
198
|
+
deps.writeGuard.assertNoConcurrentWriter(id, head.fHeadSequence);
|
|
199
|
+
removed = await tx.deleteBridgeTail(id, 0);
|
|
200
|
+
await tx.upsertSession(
|
|
201
|
+
{ meta: log.meta, inheritedEventCount: SessionLogOffset(log.inheritedEventCount) },
|
|
202
|
+
randomUUID(),
|
|
203
|
+
);
|
|
204
|
+
const appended = await appendEventTail(tx, log.meta, log.events, {
|
|
205
|
+
parentId: "",
|
|
206
|
+
nextSeq: 0,
|
|
207
|
+
});
|
|
208
|
+
await tx.updateHead(id, appended.headEventId, appended.headSequence);
|
|
209
|
+
await tx.bumpRevision(id, maxEventTime(log.events));
|
|
210
|
+
});
|
|
211
|
+
// 重写换了事件行:被删掉的那批成了孤儿(引用标记要落回 0),本会话汇总按新事件重算。
|
|
212
|
+
await this.rebuildSessionStats(id);
|
|
213
|
+
await this.refreshEventUsageFlags(removed);
|
|
214
|
+
},
|
|
215
|
+
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
async function appendEventTail(
|
|
220
|
+
tx: BackendTx,
|
|
221
|
+
meta: SessionHeader,
|
|
222
|
+
events: readonly SessionEvent[],
|
|
223
|
+
anchor: { parentId: string; nextSeq: number },
|
|
224
|
+
reuse?: ReadonlyMap<number, string>,
|
|
225
|
+
): Promise<{ headEventId: string; headSequence: number; usageRows: EventUsageRow[] }> {
|
|
226
|
+
let parentId = anchor.parentId;
|
|
227
|
+
let nextSeq = anchor.nextSeq;
|
|
228
|
+
|
|
229
|
+
const eventRows: EventInsert[] = [];
|
|
230
|
+
const usageRows: EventUsageRow[] = [];
|
|
231
|
+
const bridgeRows: Array<{
|
|
232
|
+
fSessionId: SessionId;
|
|
233
|
+
fEventId: string;
|
|
234
|
+
fSequence: number;
|
|
235
|
+
fSurfaceOp: string | null;
|
|
236
|
+
}> = [];
|
|
237
|
+
for (const event of events) {
|
|
238
|
+
const reusedId = reuse?.get(event.seq);
|
|
239
|
+
const eventId = reusedId ?? randomUUID();
|
|
240
|
+
if (reusedId === undefined) {
|
|
241
|
+
const { kind, role, name, actionId } = eventDimensions(event);
|
|
242
|
+
|
|
243
|
+
const raw = event as SessionEvent & {
|
|
244
|
+
ignorable?: unknown;
|
|
245
|
+
surfaceOp?: unknown;
|
|
246
|
+
sourceEventSeqs?: unknown;
|
|
247
|
+
};
|
|
248
|
+
const { data, surfaceOp: _surfaceOp, sourceEventSeqs: _sourceEventSeqs, ...envelope } = raw;
|
|
249
|
+
const row: EventInsert = {
|
|
250
|
+
fEventId: eventId,
|
|
251
|
+
fParentId: parentId,
|
|
252
|
+
fType: event.type,
|
|
253
|
+
fKind: kind,
|
|
254
|
+
fRole: role,
|
|
255
|
+
fName: name,
|
|
256
|
+
fActionId: actionId,
|
|
257
|
+
fEncoding: EVENT_ENCODING,
|
|
258
|
+
fData: JSON.stringify({ ...envelope, data }),
|
|
259
|
+
fCreatedAt: event.time,
|
|
260
|
+
};
|
|
261
|
+
eventRows.push(row);
|
|
262
|
+
// 用量行在这里一次折好(读侧维度一起物化),后端只负责插入,事务后旁路再据此累加汇总表。
|
|
263
|
+
const usageRow = usageRowOf(row, meta.origin === "subagent");
|
|
264
|
+
if (usageRow !== undefined) usageRows.push(usageRow);
|
|
265
|
+
}
|
|
266
|
+
const surfaceOp =
|
|
267
|
+
(event as SessionEvent<SurfaceEventType>).surfaceOp === undefined
|
|
268
|
+
? null
|
|
269
|
+
: JSON.stringify((event as SessionEvent<SurfaceEventType>).surfaceOp);
|
|
270
|
+
bridgeRows.push({
|
|
271
|
+
fSessionId: meta.id,
|
|
272
|
+
fEventId: eventId,
|
|
273
|
+
fSequence: nextSeq,
|
|
274
|
+
fSurfaceOp: surfaceOp,
|
|
275
|
+
});
|
|
276
|
+
parentId = eventId;
|
|
277
|
+
nextSeq++;
|
|
278
|
+
}
|
|
279
|
+
if (eventRows.length > 0) await tx.insertEvents(eventRows, usageRows);
|
|
280
|
+
await tx.insertBridges(bridgeRows);
|
|
281
|
+
|
|
282
|
+
if (events.some((event) => (event.type as string) === "session/title")) {
|
|
283
|
+
await tx.refreshTitle(meta.id);
|
|
284
|
+
}
|
|
285
|
+
return { headEventId: parentId, headSequence: nextSeq - 1, usageRows };
|
|
286
|
+
}
|
package/dist/branch-DbFW3rJX.mjs
DELETED
|
@@ -1,391 +0,0 @@
|
|
|
1
|
-
import { d as repairRequestHeaders, h as scanRows, i as normalizeToCurrentShape, m as rowToMeta } from "./log-DYxdjJ-l.mjs";
|
|
2
|
-
import { SESSION_FORMAT_VERSION, SessionLogOffset } from "@deepseek-ai/dsh-session";
|
|
3
|
-
import { createSessionFormatCatalogWithChildren } from "@deepseek-ai/dsh-session-format-catalog";
|
|
4
|
-
import { randomUUID } from "node:crypto";
|
|
5
|
-
import { SessionBranch, SessionBranchError, balanceRewindPrefix, rewindKeepLength } from "@morlay/session-branch";
|
|
6
|
-
//#region src/legacy.ts
|
|
7
|
-
function physicalHeader(row) {
|
|
8
|
-
const common = {
|
|
9
|
-
type: "session",
|
|
10
|
-
version: row.fVersion,
|
|
11
|
-
id: row.fSessionId,
|
|
12
|
-
createdAt: row.fCreatedAt,
|
|
13
|
-
...row.fCwd !== null ? { cwd: row.fCwd } : {},
|
|
14
|
-
...row.fParentSession !== null ? { parentSession: row.fParentSession } : {},
|
|
15
|
-
...row.fOrigin !== null ? { origin: row.fOrigin } : {},
|
|
16
|
-
delegationDepth: row.fDelegationDepth ?? 0,
|
|
17
|
-
...row.fAgentPreset !== null ? { agentPreset: row.fAgentPreset } : {}
|
|
18
|
-
};
|
|
19
|
-
return row.fVersion < 2 ? {
|
|
20
|
-
...common,
|
|
21
|
-
...row.fSeedLength !== null ? { seedLength: row.fSeedLength } : {}
|
|
22
|
-
} : {
|
|
23
|
-
...common,
|
|
24
|
-
isSeeded: row.fSeedLength !== null
|
|
25
|
-
};
|
|
26
|
-
}
|
|
27
|
-
function physicalEvent(row) {
|
|
28
|
-
const stored = JSON.parse(row.fData);
|
|
29
|
-
const full = typeof stored === "object" && stored !== null && !Array.isArray(stored) && typeof stored["type"] === "string" && "data" in stored;
|
|
30
|
-
return {
|
|
31
|
-
type: row.fType,
|
|
32
|
-
seq: row.fSequence,
|
|
33
|
-
time: row.fCreatedAt,
|
|
34
|
-
data: full ? stored["data"] : stored,
|
|
35
|
-
...row.fSurfaceOp !== null ? { surfaceOp: JSON.parse(row.fSurfaceOp) } : {}
|
|
36
|
-
};
|
|
37
|
-
}
|
|
38
|
-
const restoreCatalog = createSessionFormatCatalogWithChildren([]);
|
|
39
|
-
function convertLegacyRows(row, eventRows) {
|
|
40
|
-
const restore = restoreCatalog.createRestore(physicalHeader(row), {
|
|
41
|
-
recovery: "strict",
|
|
42
|
-
validation: "transformed"
|
|
43
|
-
});
|
|
44
|
-
for (const eventRow of eventRows) restore.decodeRow(physicalEvent(eventRow));
|
|
45
|
-
const artifact = restore.finish();
|
|
46
|
-
return {
|
|
47
|
-
meta: artifact.header,
|
|
48
|
-
inheritedEventCount: artifact.inheritedEventCount,
|
|
49
|
-
events: artifact.events
|
|
50
|
-
};
|
|
51
|
-
}
|
|
52
|
-
function isLegacyVersion(version) {
|
|
53
|
-
return version < SESSION_FORMAT_VERSION;
|
|
54
|
-
}
|
|
55
|
-
function adoptLegacyRows(row, eventRows) {
|
|
56
|
-
const { preserved, tornFrom } = scanRows(eventRows, 0);
|
|
57
|
-
repairRequestHeaders(preserved);
|
|
58
|
-
normalizeToCurrentShape(preserved);
|
|
59
|
-
return {
|
|
60
|
-
meta: {
|
|
61
|
-
...rowToMeta(row),
|
|
62
|
-
version: SESSION_FORMAT_VERSION
|
|
63
|
-
},
|
|
64
|
-
inheritedEventCount: Math.min(row.fSeedLength ?? 0, preserved.length),
|
|
65
|
-
events: preserved,
|
|
66
|
-
...tornFrom !== void 0 ? { tornFrom } : {}
|
|
67
|
-
};
|
|
68
|
-
}
|
|
69
|
-
//#endregion
|
|
70
|
-
//#region src/branch.ts
|
|
71
|
-
function assertRewindBoundary(id, toBoundary, boundaryType, head) {
|
|
72
|
-
if (toBoundary === -1) return;
|
|
73
|
-
if (toBoundary > head) throw new SessionBranchError(`rewind boundary ${toBoundary} is beyond the stored head ${head}`, "INVALID_BOUNDARY");
|
|
74
|
-
if (boundaryType === void 0) throw new SessionBranchError(`rewind boundary ${toBoundary} does not exist in session "${id}"`, "INVALID_BOUNDARY");
|
|
75
|
-
if (boundaryType !== "turn/end" && boundaryType !== "user/message") throw new SessionBranchError(`rewind boundary ${toBoundary} is not a turn/end or user/message (${boundaryType})`, "INVALID_BOUNDARY");
|
|
76
|
-
}
|
|
77
|
-
function locateTurnEnd(events, atSeq, mode = "after") {
|
|
78
|
-
const ends = events.filter((event) => event.type === "turn/end").map((event) => event.seq);
|
|
79
|
-
if (atSeq === void 0) {
|
|
80
|
-
const last = ends.at(-1);
|
|
81
|
-
if (last === void 0) throw new SessionBranchError("session has no closed turn", "OPEN_TURN");
|
|
82
|
-
return last;
|
|
83
|
-
}
|
|
84
|
-
if (mode === "before") {
|
|
85
|
-
let boundary = -1;
|
|
86
|
-
for (const seq of ends) if (seq < atSeq) boundary = seq;
|
|
87
|
-
else break;
|
|
88
|
-
return boundary;
|
|
89
|
-
}
|
|
90
|
-
const firstAfter = ends.find((seq) => seq >= atSeq);
|
|
91
|
-
if (firstAfter !== void 0) return firstAfter;
|
|
92
|
-
const lastStart = [...events].reverse().find((event) => event.type === "turn/start");
|
|
93
|
-
if (lastStart !== void 0 && lastStart.seq <= atSeq) throw new SessionBranchError(`anchor ${atSeq} lies inside an open turn`, "OPEN_TURN");
|
|
94
|
-
const last = ends.at(-1);
|
|
95
|
-
if (last === void 0) throw new SessionBranchError("session has no closed turn", "OPEN_TURN");
|
|
96
|
-
return last;
|
|
97
|
-
}
|
|
98
|
-
function renumber(events, offset) {
|
|
99
|
-
return events.map((event, index) => ({
|
|
100
|
-
...event,
|
|
101
|
-
seq: offset + index
|
|
102
|
-
}));
|
|
103
|
-
}
|
|
104
|
-
function mintSessionId() {
|
|
105
|
-
return `session-${randomUUID()}`;
|
|
106
|
-
}
|
|
107
|
-
function resetSurfaceManager(surfaceManager) {
|
|
108
|
-
const state = surfaceManager._state;
|
|
109
|
-
state.nodes = [];
|
|
110
|
-
state.replaceGeneration = 0;
|
|
111
|
-
state.contentGeneration = 0;
|
|
112
|
-
state.projectedMessages = /* @__PURE__ */ new Map();
|
|
113
|
-
surfaceManager._lastProcessedSeq = surfaceManager.baseSeq - 1;
|
|
114
|
-
surfaceManager._pendingPlan = void 0;
|
|
115
|
-
}
|
|
116
|
-
function truncateLiveSession(session, newLength) {
|
|
117
|
-
const s = session;
|
|
118
|
-
s.log.length = newLength;
|
|
119
|
-
s.eventsSnapshot = void 0;
|
|
120
|
-
s.headerFold = void 0;
|
|
121
|
-
s.headerFoldSeq = 0;
|
|
122
|
-
s.contextFold = void 0;
|
|
123
|
-
s.contextFoldSeq = 0;
|
|
124
|
-
s.derived = [];
|
|
125
|
-
s.derivedNodes = 0;
|
|
126
|
-
s.derivedGeneration = 0;
|
|
127
|
-
resetSurfaceManager(s.surfaceManager);
|
|
128
|
-
}
|
|
129
|
-
function replaceLiveSessionLog(session, events) {
|
|
130
|
-
const s = session;
|
|
131
|
-
s.log.length = 0;
|
|
132
|
-
s.log.push(...events);
|
|
133
|
-
s.eventsSnapshot = void 0;
|
|
134
|
-
s.headerFold = void 0;
|
|
135
|
-
s.headerFoldSeq = 0;
|
|
136
|
-
s.contextFold = void 0;
|
|
137
|
-
s.contextFoldSeq = 0;
|
|
138
|
-
s.derived = [];
|
|
139
|
-
s.derivedNodes = 0;
|
|
140
|
-
s.derivedGeneration = 0;
|
|
141
|
-
resetSurfaceManager(s.surfaceManager);
|
|
142
|
-
}
|
|
143
|
-
var SessionBranchRdbProvider = class {
|
|
144
|
-
persistence;
|
|
145
|
-
live;
|
|
146
|
-
name = "session-rdb";
|
|
147
|
-
constructor(persistence, live = {
|
|
148
|
-
getSession: () => void 0,
|
|
149
|
-
getAgent: () => void 0,
|
|
150
|
-
flush: async () => true
|
|
151
|
-
}) {
|
|
152
|
-
this.persistence = persistence;
|
|
153
|
-
this.live = live;
|
|
154
|
-
}
|
|
155
|
-
async readBranchPrefix(id, atSeq, mode = "after", signal) {
|
|
156
|
-
const { events } = await this.readRawEvents(id, signal);
|
|
157
|
-
const boundary = locateTurnEnd(events, atSeq, mode);
|
|
158
|
-
return {
|
|
159
|
-
seq: boundary,
|
|
160
|
-
events: events.slice(0, boundary + 1)
|
|
161
|
-
};
|
|
162
|
-
}
|
|
163
|
-
async readRawEvents(id, signal) {
|
|
164
|
-
const stored = await this.persistence.readLog(id, {}, signal);
|
|
165
|
-
if (stored === void 0) throw new SessionBranchError(`session "${id}" not found`, "SESSION_NOT_FOUND");
|
|
166
|
-
return {
|
|
167
|
-
meta: stored.meta,
|
|
168
|
-
events: stored.events
|
|
169
|
-
};
|
|
170
|
-
}
|
|
171
|
-
async forkFrom(sourceId, options = {}, signal) {
|
|
172
|
-
signal?.throwIfAborted();
|
|
173
|
-
const { atSeq, anchorMode = "after", seedSuffix = [], childSessionId, meta = {} } = options;
|
|
174
|
-
const source = await this.persistence.readLog(sourceId, {}, signal);
|
|
175
|
-
if (source === void 0) throw new SessionBranchError(`session "${sourceId}" not found`, "SESSION_NOT_FOUND");
|
|
176
|
-
const boundary = locateTurnEnd(source.events, atSeq, anchorMode);
|
|
177
|
-
const prefix = balanceRewindPrefix(source.events.slice(0, boundary + 1));
|
|
178
|
-
if (prefix.length <= boundary) this.live.warn?.(`session-rdb: fork "${sourceId}" dropped ${boundary + 1 - prefix.length} trailing event(s) from seq ${prefix.length} to keep the seed's step pairs balanced`);
|
|
179
|
-
const childId = childSessionId ?? mintSessionId();
|
|
180
|
-
const childMeta = {
|
|
181
|
-
version: SESSION_FORMAT_VERSION,
|
|
182
|
-
id: childId,
|
|
183
|
-
createdAt: meta.createdAt ?? Date.now(),
|
|
184
|
-
...meta.cwd !== void 0 ? { cwd: meta.cwd } : source.meta.cwd !== void 0 ? { cwd: source.meta.cwd } : {},
|
|
185
|
-
parentSession: sourceId,
|
|
186
|
-
isSeeded: true,
|
|
187
|
-
...meta.agentPreset !== void 0 ? { agentPreset: meta.agentPreset } : source.meta.agentPreset !== void 0 ? { agentPreset: source.meta.agentPreset } : {},
|
|
188
|
-
...meta.origin !== void 0 ? { origin: meta.origin } : {},
|
|
189
|
-
...meta.delegationDepth !== void 0 ? { delegationDepth: meta.delegationDepth } : {}
|
|
190
|
-
};
|
|
191
|
-
const seed = [...renumber(prefix, 0), ...renumber(seedSuffix, prefix.length)];
|
|
192
|
-
const internals = this.persistence.internals();
|
|
193
|
-
const reusable = !source.migrated && source.events.length === source.storedCount;
|
|
194
|
-
const reuse = /* @__PURE__ */ new Map();
|
|
195
|
-
if (reusable) {
|
|
196
|
-
const sourceRows = await internals.backend.getEventRows(sourceId);
|
|
197
|
-
const sourceEventIds = new Map(sourceRows.map((row) => [row.fSequence, row.fEventId]));
|
|
198
|
-
for (const event of prefix) {
|
|
199
|
-
const eventId = sourceEventIds.get(event.seq);
|
|
200
|
-
if (eventId !== void 0) reuse.set(event.seq, eventId);
|
|
201
|
-
}
|
|
202
|
-
} else if (prefix.length > 0) this.live.warn?.(`session-rdb: fork "${sourceId}" copies event rows instead of reusing them — its read view comes from a migration, so stored seqs need not line up`);
|
|
203
|
-
internals.registerReuseEventIds(childId, reuse);
|
|
204
|
-
try {
|
|
205
|
-
const handle = await this.persistence.create(childMeta, { inheritedEventCount: SessionLogOffset(prefix.length) });
|
|
206
|
-
try {
|
|
207
|
-
if (seed.length > 0) await handle.append(seed);
|
|
208
|
-
} finally {
|
|
209
|
-
await handle.close();
|
|
210
|
-
}
|
|
211
|
-
} catch (error) {
|
|
212
|
-
internals.dropReuseEventIds(childId);
|
|
213
|
-
throw error;
|
|
214
|
-
}
|
|
215
|
-
return childId;
|
|
216
|
-
}
|
|
217
|
-
async rewind(id, toBoundary, signal) {
|
|
218
|
-
signal?.throwIfAborted();
|
|
219
|
-
if (!Number.isSafeInteger(toBoundary) || toBoundary < -1) throw new SessionBranchError(`rewind boundary must be a non-negative safe integer, got ${toBoundary}`, "INVALID_BOUNDARY");
|
|
220
|
-
const live = this.live.getSession(id);
|
|
221
|
-
if (live !== void 0) await this.live.flush(live);
|
|
222
|
-
const internals = this.persistence.internals();
|
|
223
|
-
const row = await internals.backend.getSession(id);
|
|
224
|
-
if (row === void 0) throw new SessionBranchError(`session "${id}" not found`, "SESSION_NOT_FOUND");
|
|
225
|
-
const meta = rowToMeta(row);
|
|
226
|
-
let rawKeepLength;
|
|
227
|
-
let kept;
|
|
228
|
-
if (isLegacyVersion(row.fVersion)) {
|
|
229
|
-
const log = await this.persistence.readLog(id, {}, signal);
|
|
230
|
-
if (log === void 0) throw new SessionBranchError(`session "${id}" not found`, "SESSION_NOT_FOUND");
|
|
231
|
-
const boundaryType = log.events[toBoundary]?.type;
|
|
232
|
-
assertRewindBoundary(id, toBoundary, boundaryType, log.events.length - 1);
|
|
233
|
-
rawKeepLength = toBoundary === -1 ? 0 : boundaryType === "turn/end" ? toBoundary + 1 : toBoundary;
|
|
234
|
-
kept = balanceRewindPrefix(log.events.slice(0, rawKeepLength));
|
|
235
|
-
} else {
|
|
236
|
-
const boundaryType = toBoundary === -1 ? void 0 : await internals.backend.getEventTypeAt(id, toBoundary);
|
|
237
|
-
assertRewindBoundary(id, toBoundary, boundaryType, row.fHeadSequence);
|
|
238
|
-
rawKeepLength = toBoundary === -1 ? 0 : boundaryType === "turn/end" ? toBoundary + 1 : toBoundary;
|
|
239
|
-
}
|
|
240
|
-
const keepLength = kept === void 0 ? await this.rewindKeepLength(id, rawKeepLength, internals.backend) : kept.length;
|
|
241
|
-
if (keepLength < rawKeepLength) this.live.warn?.(`session-rdb: rewind "${id}" dropped ${rawKeepLength - keepLength} trailing event(s) from seq ${keepLength} to keep the retained prefix's step pairs balanced`);
|
|
242
|
-
const denseBoundary = keepLength - 1;
|
|
243
|
-
const newSeedLength = await internals.backend.transaction(async (tx) => {
|
|
244
|
-
signal?.throwIfAborted();
|
|
245
|
-
const head = await tx.getHead(id);
|
|
246
|
-
if (denseBoundary > head.fHeadSequence) throw new SessionBranchError(`rewind boundary ${toBoundary} is beyond the stored head ${head.fHeadSequence}`, "INVALID_BOUNDARY");
|
|
247
|
-
if (denseBoundary < head.fHeadSequence) {
|
|
248
|
-
await tx.deleteBridgeTail(id, denseBoundary + 1);
|
|
249
|
-
const prev = denseBoundary === -1 ? void 0 : await tx.getPrevBridge(id, denseBoundary);
|
|
250
|
-
if (prev === void 0) await tx.updateHead(id, "", -1);
|
|
251
|
-
else await tx.updateHead(id, prev.fEventId, prev.fSequence);
|
|
252
|
-
}
|
|
253
|
-
const storedSeedLength = await tx.getSeedLength(id);
|
|
254
|
-
let shrunk = storedSeedLength;
|
|
255
|
-
if (storedSeedLength !== null && storedSeedLength > denseBoundary + 1) {
|
|
256
|
-
await tx.updateSeedLength(id, denseBoundary + 1);
|
|
257
|
-
shrunk = denseBoundary + 1;
|
|
258
|
-
}
|
|
259
|
-
await tx.refreshTitle(id);
|
|
260
|
-
await tx.bumpRevision(id);
|
|
261
|
-
return shrunk;
|
|
262
|
-
});
|
|
263
|
-
internals.writeGuard.confirmHead(id, denseBoundary);
|
|
264
|
-
if (live !== void 0) {
|
|
265
|
-
truncateLiveSession(live, keepLength);
|
|
266
|
-
this.live.resetProjections?.(live);
|
|
267
|
-
this.live.resetTokenMeter?.(live);
|
|
268
|
-
const agent = this.live.getAgent(id);
|
|
269
|
-
if (agent !== void 0) {
|
|
270
|
-
agent.requestHeaderLogged = false;
|
|
271
|
-
const lastTurn = live.snapshotEvents().findLast((e) => e.type === "turn/start")?.data.turn ?? 0;
|
|
272
|
-
const phase = agent.phase;
|
|
273
|
-
if (phase !== void 0) phase.lastTurn = lastTurn;
|
|
274
|
-
}
|
|
275
|
-
const handle = this.persistence.tracker.writerOf(id);
|
|
276
|
-
if (handle !== void 0) handle.resetAfterRewind(keepLength, newSeedLength === null ? void 0 : newSeedLength);
|
|
277
|
-
agent?.inbox?.clear();
|
|
278
|
-
await this.live.flush(live);
|
|
279
|
-
await this.refreshProjectionCache(live);
|
|
280
|
-
} else await this.refreshProjectionCache({
|
|
281
|
-
id,
|
|
282
|
-
header: meta,
|
|
283
|
-
inheritedEventCount: SessionLogOffset(newSeedLength ?? row.fSeedLength ?? 0),
|
|
284
|
-
headSeq: keepLength - 1,
|
|
285
|
-
snapshotEvents: () => []
|
|
286
|
-
});
|
|
287
|
-
return {
|
|
288
|
-
header: rowToMeta(row),
|
|
289
|
-
revision: await internals.readStoredRevision(id)
|
|
290
|
-
};
|
|
291
|
-
}
|
|
292
|
-
async rewindKeepLength(id, rawKeepLength, backend) {
|
|
293
|
-
if (rawKeepLength === 0) return 0;
|
|
294
|
-
let limit = 64;
|
|
295
|
-
for (;;) {
|
|
296
|
-
const types = [...await backend.getEventTypesBefore(id, rawKeepLength, limit)].reverse().map((row) => row.fType);
|
|
297
|
-
const windowStart = rawKeepLength - types.length;
|
|
298
|
-
if (types.includes("turn/end") || windowStart === 0 || types.length >= rawKeepLength) return rewindKeepLength(types, rawKeepLength);
|
|
299
|
-
limit *= 4;
|
|
300
|
-
}
|
|
301
|
-
}
|
|
302
|
-
async refreshProjectionCache(session) {
|
|
303
|
-
if (this.live.refreshProjectionCache === void 0) return;
|
|
304
|
-
try {
|
|
305
|
-
await this.live.refreshProjectionCache(session);
|
|
306
|
-
} catch {}
|
|
307
|
-
}
|
|
308
|
-
};
|
|
309
|
-
var SessionBranchRdb = class extends SessionBranch {
|
|
310
|
-
static inject = ["sessionPersistence", "sessions"];
|
|
311
|
-
warned = /* @__PURE__ */ new Set();
|
|
312
|
-
constructor(ctx) {
|
|
313
|
-
super(ctx);
|
|
314
|
-
}
|
|
315
|
-
resetLiveDerivedState(session) {
|
|
316
|
-
this.resetProjectionCells(session);
|
|
317
|
-
this.resetTokenMeterFold(session);
|
|
318
|
-
}
|
|
319
|
-
warnOnce(key, message) {
|
|
320
|
-
if (this.warned.has(key)) return;
|
|
321
|
-
this.warned.add(key);
|
|
322
|
-
this.ctx.logger.warn(message);
|
|
323
|
-
}
|
|
324
|
-
resetProjectionCells(session) {
|
|
325
|
-
const registrations = this.ctx.get("sessionProjections")?.registrations;
|
|
326
|
-
if (!(registrations instanceof Map)) {
|
|
327
|
-
this.warnOnce("sessionProjections.registrations", `session-rdb: ctx.sessionProjections.registrations is not a Map (upstream field shape changed); rewind leaves the projection cell caches of "${session.id}" stale, so replayed events can be skipped`);
|
|
328
|
-
return;
|
|
329
|
-
}
|
|
330
|
-
for (const registration of registrations.values()) {
|
|
331
|
-
const cells = registration?.cells;
|
|
332
|
-
if (!(cells instanceof WeakMap)) {
|
|
333
|
-
this.warnOnce("sessionProjections.cells", `session-rdb: ctx.sessionProjections registration cells are not a WeakMap (upstream field shape changed); rewind leaves the projection cell caches of "${session.id}" stale, so replayed events can be skipped`);
|
|
334
|
-
continue;
|
|
335
|
-
}
|
|
336
|
-
cells.delete(session);
|
|
337
|
-
}
|
|
338
|
-
}
|
|
339
|
-
resetTokenMeterFold(session) {
|
|
340
|
-
const meter = this.ctx.get("tokenMeter");
|
|
341
|
-
if (meter === void 0) {
|
|
342
|
-
this.warnOnce("tokenMeter", `session-rdb: ctx.tokenMeter is not mounted; after rewind the token-meter fold watermark of "${session.id}" may stay stale, so compaction can report "step/end ... has no matching step/start event"`);
|
|
343
|
-
return;
|
|
344
|
-
}
|
|
345
|
-
const states = meter.states;
|
|
346
|
-
if (!(states instanceof WeakMap)) {
|
|
347
|
-
this.warnOnce("tokenMeter.states", `session-rdb: ctx.tokenMeter.states is not a WeakMap (upstream field shape changed); after rewind the token-meter fold watermark of "${session.id}" may stay stale, so compaction can report "step/end ... has no matching step/start event"`);
|
|
348
|
-
return;
|
|
349
|
-
}
|
|
350
|
-
states.delete(session);
|
|
351
|
-
}
|
|
352
|
-
provider = new SessionBranchRdbProvider(this.ctx.sessionPersistence, {
|
|
353
|
-
getSession: (id) => this.ctx.sessions.get(id),
|
|
354
|
-
getAgent: (id) => {
|
|
355
|
-
return this.ctx.get("agents")?.get(id);
|
|
356
|
-
},
|
|
357
|
-
flush: (session) => this.ctx.sessions.flush(session),
|
|
358
|
-
warn: (message) => {
|
|
359
|
-
this.ctx.logger.warn(message);
|
|
360
|
-
},
|
|
361
|
-
resetProjections: (session) => this.resetProjectionCells(session),
|
|
362
|
-
resetTokenMeter: (session) => this.resetTokenMeterFold(session),
|
|
363
|
-
refreshProjectionCache: async (session) => {
|
|
364
|
-
const cache = this.ctx.get("sessionProjectionCache");
|
|
365
|
-
if (cache === void 0) return;
|
|
366
|
-
try {
|
|
367
|
-
if (session.headSeq !== void 0 && cache.truncateTo !== void 0) {
|
|
368
|
-
await cache.truncateTo(session.header, session.inheritedEventCount, session.headSeq);
|
|
369
|
-
return;
|
|
370
|
-
}
|
|
371
|
-
await cache.write(session);
|
|
372
|
-
} catch (error) {
|
|
373
|
-
this.ctx.logger.warn(`session-rdb: projection cache refresh after rewind for "${session.id}" failed (cache stays stale): ${String(error)}`);
|
|
374
|
-
}
|
|
375
|
-
}
|
|
376
|
-
});
|
|
377
|
-
readBranchPrefix(id, atSeq, mode, signal) {
|
|
378
|
-
return this.provider.readBranchPrefix(id, atSeq, mode, signal);
|
|
379
|
-
}
|
|
380
|
-
readRawEvents(id, signal) {
|
|
381
|
-
return this.provider.readRawEvents(id, signal);
|
|
382
|
-
}
|
|
383
|
-
forkFrom(sourceId, options, signal) {
|
|
384
|
-
return this.provider.forkFrom(sourceId, options, signal);
|
|
385
|
-
}
|
|
386
|
-
rewind(id, toBoundary, signal) {
|
|
387
|
-
return this.provider.rewind(id, toBoundary, signal);
|
|
388
|
-
}
|
|
389
|
-
};
|
|
390
|
-
//#endregion
|
|
391
|
-
export { truncateLiveSession as a, isLegacyVersion as c, replaceLiveSessionLog as i, restoreCatalog as l, SessionBranchRdbProvider as n, adoptLegacyRows as o, locateTurnEnd as r, convertLegacyRows as s, SessionBranchRdb as t };
|