@morlay/session-rdb 0.1.9 → 0.1.10-alpha.0
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 +6 -0
- package/dist/artifact.d.mts +1 -2
- package/dist/artifact.mjs +2 -2
- package/dist/{branch-DbFW3rJX.mjs → branch-CoPCRKer.mjs} +18 -8
- package/dist/{deletion-ODZFcbZm.mjs → deletion-CWl89TIR.mjs} +60 -29
- package/dist/deletion.d.mts +1 -3
- package/dist/deletion.mjs +2 -2
- package/dist/export.d.mts +1 -3
- package/dist/export.mjs +2 -3
- package/dist/gc.d.mts +1 -3
- package/dist/gc.mjs +2 -3
- package/dist/import.d.mts +1 -3
- package/dist/import.mjs +3 -4
- package/dist/index.d.mts +2 -2
- package/dist/index.mjs +5 -5
- package/dist/{log-DYxdjJ-l.mjs → log-CcWy46VE.mjs} +22 -26
- package/dist/{log-C0q5svm7.d.mts → log-TYgd8Xkr.d.mts} +21 -22
- package/dist/{routes-DGBlBE_I.mjs → routes-hZjTcX_R.mjs} +1 -2
- package/dist/{sqlite-DjOnMRh2.mjs → sqlite-BNRd1aqR.mjs} +47 -46
- package/dist/storage.d.mts +1 -3
- package/dist/storage.mjs +1 -1
- package/dist/testing.d.mts +1 -5
- package/dist/testing.mjs +3 -5
- package/dist/usage.d.mts +1 -1
- package/dist/usage.mjs +2 -3
- package/package.json +25 -25
- package/src/branch.ts +18 -4
- package/src/index.ts +41 -5
- package/src/log.ts +21 -25
- package/src/session-query.ts +17 -4
- package/src/sqlite.ts +18 -8
- package/src/storage-takeover/projection-cache.ts +17 -6
- package/src/storage-takeover/repository.ts +19 -0
- package/src/storage-takeover/storage-backend.ts +13 -0
- package/src/storage-takeover/types.ts +5 -0
package/README.md
CHANGED
|
@@ -18,11 +18,17 @@ provider 面),并接管 `ctx.sessionQuery`(全文检索 disabled)与 `$D
|
|
|
18
18
|
path: /absolute/path/to/sessions.sqlite # 自定义路径请用绝对路径;省略则由 bundle patch 的默认值兜底
|
|
19
19
|
journalMode: wal
|
|
20
20
|
busyTimeout: 5000
|
|
21
|
+
preparedSessionCacheSize: 1 # 冷会话观察缓存容量(条数,最小 1)
|
|
21
22
|
```
|
|
22
23
|
|
|
23
24
|
PostgreSQL 同理:`type: postgres` + `connectionString`(可加 `schema`,默认 `public`)。字段、默认值、装配默认值的
|
|
24
25
|
来源与「改配置由 Loader 重挂这一行生效」见[设计总览](./.agents/designs/20260917-设计总览.md)的「配置门面」。
|
|
25
26
|
|
|
27
|
+
`preparedSessionCacheSize` 透传给上游 `session-query` 的冷会话观察缓存:缓存**按条目数**持有完整 `Session`
|
|
28
|
+
(含整份 append-only log),被活跃观察租约钉住的条目不淘汰;一次读取中途转成 live 时会重试 live 路径,但不会
|
|
29
|
+
顺手清掉已缓存的 cold 条目——同一个 id 因此可能同时有两份 log 驻留。上游默认 5,**本行默认 1**(本地单用户、
|
|
30
|
+
rdb 后端,冷读复用的收益不值这份内存放大);上游校验下限是 1,不能设 0。
|
|
31
|
+
|
|
26
32
|
管理面的 HTTP 通道统一挂在 `/api/morlay/v1/session` 前缀下(均为 `POST`,body `{ sessionId }` 或列表请求
|
|
27
33
|
`{ query?, includeSubagents?, page?, pageSize? }`):`/rows`(完整语料,含归档)、`/export`、`/import`、
|
|
28
34
|
`/delete`、`/gc`、`/usage`(前缀与上游分家的理由见
|
package/dist/artifact.d.mts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { C as usageRowOf,
|
|
1
|
+
import { C as usageRowOf, It as SESSION_IMPORT_PATH, S as toJsonlArtifact, _ as sealOwnEvents, a as normalizeToCurrentShape, b as syncMeteringRanges, c as renameLegacyPtcEvents, d as repairReadView, f as repairRequestHeaders, g as scanRows, h as rowToMeta, i as needsShapeAdoption, l as repairAssistantSettlement, m as rowToEvent, n as OWN_EVENT_TYPES, o as orphanInboxSpliceSeqs, p as repairSurfaceOps, r as findSurfaceRepairs, s as recomputeReplaceProvenance, t as EventUsageRow, u as repairOrphanInboxSplices, v as sessionConflictRow, x as titleOfEventData, y as sessionInsertRow } from "./log-TYgd8Xkr.mjs";
|
|
2
2
|
import { SESSION_LOG_ARTIFACT_FILENAME, parseImportZip, parseJsonlArtifact, persistImport, registerSessionImport } from "./import.mjs";
|
|
3
|
-
//#endregion
|
|
4
3
|
export { EventUsageRow, OWN_EVENT_TYPES, SESSION_IMPORT_PATH, SESSION_LOG_ARTIFACT_FILENAME, findSurfaceRepairs, needsShapeAdoption, normalizeToCurrentShape, orphanInboxSpliceSeqs, parseImportZip, parseJsonlArtifact, persistImport, recomputeReplaceProvenance, registerSessionImport, renameLegacyPtcEvents, repairAssistantSettlement, repairOrphanInboxSplices, repairReadView, repairRequestHeaders, repairSurfaceOps, rowToEvent, rowToMeta, scanRows, sealOwnEvents, sessionConflictRow, sessionInsertRow, syncMeteringRanges, titleOfEventData, toJsonlArtifact, usageRowOf };
|
package/dist/artifact.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { i as SESSION_IMPORT_PATH } from "./routes-
|
|
2
|
-
import { S as usageRowOf, _ as sessionConflictRow, a as orphanInboxSpliceSeqs, b as titleOfEventData, c as repairAssistantSettlement, d as repairRequestHeaders, f as repairSurfaceOps, g as sealOwnEvents, h as scanRows, i as normalizeToCurrentShape, l as repairOrphanInboxSplices, m as rowToMeta, n as findSurfaceRepairs, o as recomputeReplaceProvenance, p as rowToEvent, r as needsShapeAdoption, s as renameLegacyPtcEvents, t as OWN_EVENT_TYPES, u as repairReadView, v as sessionInsertRow, x as toJsonlArtifact, y as syncMeteringRanges } from "./log-
|
|
1
|
+
import { i as SESSION_IMPORT_PATH } from "./routes-hZjTcX_R.mjs";
|
|
2
|
+
import { S as usageRowOf, _ as sessionConflictRow, a as orphanInboxSpliceSeqs, b as titleOfEventData, c as repairAssistantSettlement, d as repairRequestHeaders, f as repairSurfaceOps, g as sealOwnEvents, h as scanRows, i as normalizeToCurrentShape, l as repairOrphanInboxSplices, m as rowToMeta, n as findSurfaceRepairs, o as recomputeReplaceProvenance, p as rowToEvent, r as needsShapeAdoption, s as renameLegacyPtcEvents, t as OWN_EVENT_TYPES, u as repairReadView, v as sessionInsertRow, x as toJsonlArtifact, y as syncMeteringRanges } from "./log-CcWy46VE.mjs";
|
|
3
3
|
import { SESSION_LOG_ARTIFACT_FILENAME, parseImportZip, parseJsonlArtifact, persistImport, registerSessionImport } from "./import.mjs";
|
|
4
4
|
export { OWN_EVENT_TYPES, SESSION_IMPORT_PATH, SESSION_LOG_ARTIFACT_FILENAME, findSurfaceRepairs, needsShapeAdoption, normalizeToCurrentShape, orphanInboxSpliceSeqs, parseImportZip, parseJsonlArtifact, persistImport, recomputeReplaceProvenance, registerSessionImport, renameLegacyPtcEvents, repairAssistantSettlement, repairOrphanInboxSplices, repairReadView, repairRequestHeaders, repairSurfaceOps, rowToEvent, rowToMeta, scanRows, sealOwnEvents, sessionConflictRow, sessionInsertRow, syncMeteringRanges, titleOfEventData, toJsonlArtifact, usageRowOf };
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { d as repairRequestHeaders, h as scanRows, i as normalizeToCurrentShape, m as rowToMeta } from "./log-
|
|
1
|
+
import { d as repairRequestHeaders, h as scanRows, i as normalizeToCurrentShape, m as rowToMeta } from "./log-CcWy46VE.mjs";
|
|
2
2
|
import { SESSION_FORMAT_VERSION, SessionLogOffset } from "@deepseek-ai/dsh-session";
|
|
3
3
|
import { createSessionFormatCatalogWithChildren } from "@deepseek-ai/dsh-session-format-catalog";
|
|
4
4
|
import { randomUUID } from "node:crypto";
|
|
5
5
|
import { SessionBranch, SessionBranchError, balanceRewindPrefix, rewindKeepLength } from "@morlay/session-branch";
|
|
6
|
-
|
|
6
|
+
// src/legacy.ts
|
|
7
7
|
function physicalHeader(row) {
|
|
8
8
|
const common = {
|
|
9
9
|
type: "session",
|
|
@@ -66,8 +66,7 @@ function adoptLegacyRows(row, eventRows) {
|
|
|
66
66
|
...tornFrom !== void 0 ? { tornFrom } : {}
|
|
67
67
|
};
|
|
68
68
|
}
|
|
69
|
-
|
|
70
|
-
//#region src/branch.ts
|
|
69
|
+
// src/branch.ts
|
|
71
70
|
function assertRewindBoundary(id, toBoundary, boundaryType, head) {
|
|
72
71
|
if (toBoundary === -1) return;
|
|
73
72
|
if (toBoundary > head) throw new SessionBranchError(`rewind boundary ${toBoundary} is beyond the stored head ${head}`, "INVALID_BOUNDARY");
|
|
@@ -113,8 +112,20 @@ function resetSurfaceManager(surfaceManager) {
|
|
|
113
112
|
surfaceManager._lastProcessedSeq = surfaceManager.baseSeq - 1;
|
|
114
113
|
surfaceManager._pendingPlan = void 0;
|
|
115
114
|
}
|
|
115
|
+
/**
|
|
116
|
+
* 会话**当前内存 log** 的快照(复制一份)。用于"需要完整序列"的操作(首次落库的 seed、rewind / fork 的历史),
|
|
117
|
+
* 不走已弃用的同步历史读 `snapshotEvents()`——那三个读口上游已声明「新调用禁止」,而这里要的是当前内存里
|
|
118
|
+
* 那份完整日志,不是按需分页的历史。
|
|
119
|
+
* @param session - 会话。
|
|
120
|
+
* @returns 事件副本(调用方可以自由遍历)。
|
|
121
|
+
*/
|
|
122
|
+
function liveEventsOf(session) {
|
|
123
|
+
return [...session.log];
|
|
124
|
+
}
|
|
125
|
+
/** @returns 截断后日志里最后一个 `turn/start` 的 turn 号(没有则 0)。 */
|
|
116
126
|
function truncateLiveSession(session, newLength) {
|
|
117
127
|
const s = session;
|
|
128
|
+
const lastTurn = s.log.slice(0, newLength).findLast((event) => event.type === "turn/start")?.data.turn ?? 0;
|
|
118
129
|
s.log.length = newLength;
|
|
119
130
|
s.eventsSnapshot = void 0;
|
|
120
131
|
s.headerFold = void 0;
|
|
@@ -125,6 +136,7 @@ function truncateLiveSession(session, newLength) {
|
|
|
125
136
|
s.derivedNodes = 0;
|
|
126
137
|
s.derivedGeneration = 0;
|
|
127
138
|
resetSurfaceManager(s.surfaceManager);
|
|
139
|
+
return lastTurn;
|
|
128
140
|
}
|
|
129
141
|
function replaceLiveSessionLog(session, events) {
|
|
130
142
|
const s = session;
|
|
@@ -262,13 +274,12 @@ var SessionBranchRdbProvider = class {
|
|
|
262
274
|
});
|
|
263
275
|
internals.writeGuard.confirmHead(id, denseBoundary);
|
|
264
276
|
if (live !== void 0) {
|
|
265
|
-
truncateLiveSession(live, keepLength);
|
|
277
|
+
const lastTurn = truncateLiveSession(live, keepLength);
|
|
266
278
|
this.live.resetProjections?.(live);
|
|
267
279
|
this.live.resetTokenMeter?.(live);
|
|
268
280
|
const agent = this.live.getAgent(id);
|
|
269
281
|
if (agent !== void 0) {
|
|
270
282
|
agent.requestHeaderLogged = false;
|
|
271
|
-
const lastTurn = live.snapshotEvents().findLast((e) => e.type === "turn/start")?.data.turn ?? 0;
|
|
272
283
|
const phase = agent.phase;
|
|
273
284
|
if (phase !== void 0) phase.lastTurn = lastTurn;
|
|
274
285
|
}
|
|
@@ -387,5 +398,4 @@ var SessionBranchRdb = class extends SessionBranch {
|
|
|
387
398
|
return this.provider.rewind(id, toBoundary, signal);
|
|
388
399
|
}
|
|
389
400
|
};
|
|
390
|
-
|
|
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 };
|
|
401
|
+
export { replaceLiveSessionLog as a, convertLegacyRows as c, locateTurnEnd as i, isLegacyVersion as l, SessionBranchRdbProvider as n, truncateLiveSession as o, liveEventsOf as r, adoptLegacyRows as s, SessionBranchRdb as t, restoreCatalog as u };
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { a as SESSION_ROWS_PATH, t as SESSION_DELETE_PATH } from "./routes-
|
|
1
|
+
import { a as SESSION_ROWS_PATH, t as SESSION_DELETE_PATH } from "./routes-hZjTcX_R.mjs";
|
|
2
2
|
import { COUNTED_EVENT_TYPES, addActivityCount, addActivityTotals, addTokenTotals, addTotals, emptyTotals, localDayKey, registerSessionUsage } from "./usage.mjs";
|
|
3
|
-
import { S as usageRowOf, _ as sessionConflictRow, b as titleOfEventData, g as sealOwnEvents, h as scanRows, m as rowToMeta, r as needsShapeAdoption, u as repairReadView, v as sessionInsertRow, x as toJsonlArtifact } from "./log-
|
|
4
|
-
import { c as isLegacyVersion,
|
|
3
|
+
import { S as usageRowOf, _ as sessionConflictRow, b as titleOfEventData, g as sealOwnEvents, h as scanRows, m as rowToMeta, r as needsShapeAdoption, u as repairReadView, v as sessionInsertRow, x as toJsonlArtifact } from "./log-CcWy46VE.mjs";
|
|
4
|
+
import { c as convertLegacyRows, l as isLegacyVersion, r as liveEventsOf, s as adoptLegacyRows, t as SessionBranchRdb } from "./branch-CoPCRKer.mjs";
|
|
5
5
|
import { registerSessionImport } from "./import.mjs";
|
|
6
|
-
import { E as WriteGuard, T as toPostgresSchema, a as EVENT_ENCODING, c as eventDimensions, i as DEFAULT_BUSY_TIMEOUT_MS, r as createStorageRepository, t as SqliteBackend, w as postgresTableDefs } from "./sqlite-
|
|
6
|
+
import { E as WriteGuard, T as toPostgresSchema, a as EVENT_ENCODING, c as eventDimensions, i as DEFAULT_BUSY_TIMEOUT_MS, r as createStorageRepository, t as SqliteBackend, w as postgresTableDefs } from "./sqlite-BNRd1aqR.mjs";
|
|
7
7
|
import { registerSessionExport } from "./export.mjs";
|
|
8
8
|
import { registerSessionGc } from "./gc.mjs";
|
|
9
9
|
import { SESSION_FORMAT_VERSION, SessionLogOffset } from "@deepseek-ai/dsh-session";
|
|
@@ -21,7 +21,7 @@ import { migrate } from "drizzle-orm/node-postgres/migrator";
|
|
|
21
21
|
import { SessionQueryEngine, SessionQueryError } from "@deepseek-ai/dsh-session-query";
|
|
22
22
|
import { StorageError, storageBackendServiceKey } from "@deepseek-ai/dsh-storage";
|
|
23
23
|
import { Service } from "@deepseek-ai/cordis";
|
|
24
|
-
|
|
24
|
+
// src/postgres.ts
|
|
25
25
|
const postgresMigrationsDir = fileURLToPath(new URL("../drizzle/postgres/", import.meta.url));
|
|
26
26
|
const pgWriteQueues = /* @__PURE__ */ new Map();
|
|
27
27
|
const REFRESH_FLAG_CHUNK = 400;
|
|
@@ -640,8 +640,7 @@ var PostgresBackend = class PostgresBackend {
|
|
|
640
640
|
}).from(this.tables["t_session_events"]).innerJoin(this.tables["t_events"], eq(this.tables["t_session_events"].fEventId, this.tables["t_events"].fEventId));
|
|
641
641
|
}
|
|
642
642
|
};
|
|
643
|
-
|
|
644
|
-
//#region src/rows.ts
|
|
643
|
+
// src/rows.ts
|
|
645
644
|
const MAX_PAGE_SIZE = 200;
|
|
646
645
|
const DEFAULT_PAGE_SIZE = 20;
|
|
647
646
|
function registerSessionRows(ctx, backend) {
|
|
@@ -699,21 +698,27 @@ function registerSessionRows(ctx, backend) {
|
|
|
699
698
|
}), "session-rdb: session rows route");
|
|
700
699
|
});
|
|
701
700
|
}
|
|
702
|
-
|
|
703
|
-
//#region src/session-query.ts
|
|
701
|
+
// src/session-query.ts
|
|
704
702
|
var SessionQueryRdb = class extends SessionQueryEngine {
|
|
703
|
+
/**
|
|
704
|
+
* 本次装配用的冷会话观察缓存容量(由 `session-rdb` 行的 `preparedSessionCacheSize` 透传;undefined 表示沿用
|
|
705
|
+
* 上游默认)。它是**按条目数**持有完整 Session 的缓存:条目多一条就多一份完整 append-only log 驻留。
|
|
706
|
+
*/
|
|
707
|
+
preparedSessionCacheSize;
|
|
705
708
|
constructor(ctx, config = {}) {
|
|
706
709
|
super(ctx, config);
|
|
710
|
+
this.preparedSessionCacheSize = config.preparedSessionCacheSize;
|
|
707
711
|
}
|
|
708
712
|
async listSessions(signal) {
|
|
709
713
|
const records = await super.listSessions(signal);
|
|
710
|
-
const archived = this.archivedSessionIds();
|
|
714
|
+
const archived = await this.archivedSessionIds();
|
|
711
715
|
if (archived.size === 0) return records;
|
|
712
716
|
return records.filter((record) => !archived.has(String(record.header.id)));
|
|
713
717
|
}
|
|
714
|
-
archivedSessionIds() {
|
|
718
|
+
async archivedSessionIds() {
|
|
715
719
|
const registry = this.ctx.get("workspaceRegistry");
|
|
716
|
-
return new Set(registry
|
|
720
|
+
if (registry !== void 0) return new Set(registry.archivedSessionIds ?? []);
|
|
721
|
+
return await this.ctx.get("sessionPersistence")?.archivedSessionIds?.() ?? /* @__PURE__ */ new Set();
|
|
717
722
|
}
|
|
718
723
|
async searchSessions() {
|
|
719
724
|
throw searchDisabled();
|
|
@@ -725,8 +730,12 @@ var SessionQueryRdb = class extends SessionQueryEngine {
|
|
|
725
730
|
function searchDisabled() {
|
|
726
731
|
return new SessionQueryError("session search is disabled: this deployment serves session queries from the rdb backend without a full-text index", "SESSION_QUERY_SEARCH_DISABLED");
|
|
727
732
|
}
|
|
728
|
-
|
|
729
|
-
|
|
733
|
+
// src/storage-takeover/projection-cache.ts
|
|
734
|
+
function selects(keys, key) {
|
|
735
|
+
if (keys === void 0) return true;
|
|
736
|
+
if ("has" in keys) return keys.has(key);
|
|
737
|
+
return keys.includes(key);
|
|
738
|
+
}
|
|
730
739
|
const SESSION_PROJECTION_CACHE_SERVICE = "sessionProjectionCache";
|
|
731
740
|
const PREDECESSOR_TITLE_KEY = "title";
|
|
732
741
|
var SessionProjectionCacheRdb = class extends Service {
|
|
@@ -770,7 +779,7 @@ var SessionProjectionCacheRdb = class extends Service {
|
|
|
770
779
|
return record === void 0 || !identityMatches(record.identity, lifecycleIdentityOf(meta)) ? void 0 : record;
|
|
771
780
|
}
|
|
772
781
|
withDirectTitle(id, keys, snapshot) {
|
|
773
|
-
if (keys
|
|
782
|
+
if (!selects(keys, PREDECESSOR_TITLE_KEY)) return snapshot;
|
|
774
783
|
if (snapshot?.values[PREDECESSOR_TITLE_KEY] !== void 0) return snapshot;
|
|
775
784
|
const direct = this.repository.readSessionTitleDirect?.(id);
|
|
776
785
|
if (direct === void 0) return snapshot;
|
|
@@ -930,8 +939,7 @@ function predecessorIdentityMatches(stored, expected) {
|
|
|
930
939
|
function lifecycleIdentityMatches(stored, expected) {
|
|
931
940
|
return stored.createdAt === expected.createdAt && stored.cwd === expected.cwd && (stored.isSeeded ?? false) === expected.isSeeded;
|
|
932
941
|
}
|
|
933
|
-
|
|
934
|
-
//#region src/storage-takeover/storage-backend.ts
|
|
942
|
+
// src/storage-takeover/storage-backend.ts
|
|
935
943
|
const WORKSPACE_UNIT = "workspace";
|
|
936
944
|
const WORKSPACE_TABLE = "workspaces";
|
|
937
945
|
const SCHEDULE_UNIT = "schedule";
|
|
@@ -1038,6 +1046,11 @@ var WorkspaceKvUnit = class extends TrackedKvUnit {
|
|
|
1038
1046
|
this.assertTable(table);
|
|
1039
1047
|
await this.track(this.repository.deleteWorkspace(key));
|
|
1040
1048
|
}
|
|
1049
|
+
async deleteRecords(table, keys) {
|
|
1050
|
+
this.assertOpen();
|
|
1051
|
+
this.assertTable(table);
|
|
1052
|
+
await this.track(this.repository.deleteWorkspaces([...new Set(keys)]));
|
|
1053
|
+
}
|
|
1041
1054
|
async setGlobal(value) {
|
|
1042
1055
|
this.assertOpen();
|
|
1043
1056
|
await this.track(this.repository.writeWorkspaceState(workspaceStateOf(value)));
|
|
@@ -1064,6 +1077,11 @@ var ScheduleKvUnit = class extends TrackedKvUnit {
|
|
|
1064
1077
|
this.assertTable(table);
|
|
1065
1078
|
await this.track(this.repository.deleteScheduleTask(key));
|
|
1066
1079
|
}
|
|
1080
|
+
async deleteRecords(table, keys) {
|
|
1081
|
+
this.assertOpen();
|
|
1082
|
+
this.assertTable(table);
|
|
1083
|
+
await this.track(this.repository.deleteScheduleTasks([...new Set(keys)]));
|
|
1084
|
+
}
|
|
1067
1085
|
setGlobal(_value) {
|
|
1068
1086
|
throw new Error(`unit '${this.descriptor.name}' declares no global slot`);
|
|
1069
1087
|
}
|
|
@@ -1095,8 +1113,7 @@ function scheduleTaskOf(value) {
|
|
|
1095
1113
|
if (typeof task !== "object" || task === null || typeof task.sessionId !== "string" || typeof task.record !== "object" || task.record === null) throw new TypeError("schedule task does not match the stored shape");
|
|
1096
1114
|
return task;
|
|
1097
1115
|
}
|
|
1098
|
-
|
|
1099
|
-
//#region src/storage-takeover/index.ts
|
|
1116
|
+
// src/storage-takeover/index.ts
|
|
1100
1117
|
function installStorageTakeover(ctx, options) {
|
|
1101
1118
|
ctx.inject(["storage"], (storageCtx) => {
|
|
1102
1119
|
const backend = new RdbStorageBackend(options.repository);
|
|
@@ -1115,8 +1132,7 @@ function installStorageTakeover(ctx, options) {
|
|
|
1115
1132
|
ready: options.ready
|
|
1116
1133
|
});
|
|
1117
1134
|
}
|
|
1118
|
-
|
|
1119
|
-
//#region src/index.ts
|
|
1135
|
+
// src/index.ts
|
|
1120
1136
|
function maxEventTime(events) {
|
|
1121
1137
|
let max;
|
|
1122
1138
|
for (const event of events) {
|
|
@@ -1385,6 +1401,10 @@ var RdbSessionHandle = class RdbSessionHandle {
|
|
|
1385
1401
|
static LIVE_WRITE_BATCH_MAX_DELAY_MS = 200;
|
|
1386
1402
|
};
|
|
1387
1403
|
const localized = (text) => text;
|
|
1404
|
+
const preparedSessionCacheSchema = z.number().step(1).min(1).default(1).description(localized({
|
|
1405
|
+
zh: "冷会话观察缓存容量(条数,最小 1):每个条目持一份完整会话日志;本部署默认 1,把同 id 两份日志的驻留压到最小。",
|
|
1406
|
+
en: "Cold prepared-Session observation cache size (entries, minimum 1): each entry holds one complete session log. This deployment defaults to 1, minimizing duplicate per-id residency."
|
|
1407
|
+
}));
|
|
1388
1408
|
const projectionCacheSchema = z.object({
|
|
1389
1409
|
writeEveryEvents: z.natural().min(1).default(DEFAULT_PROJECTION_WRITE_EVERY_EVENTS).description(localized({
|
|
1390
1410
|
zh: "每累计这么多事件写回一次投影 checkpoint。",
|
|
@@ -1419,7 +1439,8 @@ var SessionPersistenceRdb = class extends SessionPersistence {
|
|
|
1419
1439
|
zh: "写锁冲突时的忙等超时(毫秒)。",
|
|
1420
1440
|
en: "Busy timeout in ms while a write lock is contended."
|
|
1421
1441
|
})),
|
|
1422
|
-
projectionCache: projectionCacheSchema
|
|
1442
|
+
projectionCache: projectionCacheSchema,
|
|
1443
|
+
preparedSessionCacheSize: preparedSessionCacheSchema
|
|
1423
1444
|
}), z.object({
|
|
1424
1445
|
type: z.const("postgres"),
|
|
1425
1446
|
connectionString: z.string().required().description(localized({
|
|
@@ -1430,7 +1451,8 @@ var SessionPersistenceRdb = class extends SessionPersistence {
|
|
|
1430
1451
|
zh: "库内的 schema 名;默认 public。",
|
|
1431
1452
|
en: "Schema name inside the database; `public` by default."
|
|
1432
1453
|
})),
|
|
1433
|
-
projectionCache: projectionCacheSchema
|
|
1454
|
+
projectionCache: projectionCacheSchema,
|
|
1455
|
+
preparedSessionCacheSize: preparedSessionCacheSchema
|
|
1434
1456
|
})]).description(localized({
|
|
1435
1457
|
zh: "会话持久化后端:改这里的连接参数会在这一行重挂时生效(Loader 会重装被改的行)。",
|
|
1436
1458
|
en: "Session persistence backend: edits apply when this row is remounted (the Loader re-installs changed rows)."
|
|
@@ -1452,7 +1474,8 @@ var SessionPersistenceRdb = class extends SessionPersistence {
|
|
|
1452
1474
|
this.ready = this.init();
|
|
1453
1475
|
this.installLiveRouting(ctx);
|
|
1454
1476
|
new SessionBranchRdb(this.ctx);
|
|
1455
|
-
this.
|
|
1477
|
+
const queryConfig = this.config.preparedSessionCacheSize === void 0 ? {} : { preparedSessionCacheSize: this.config.preparedSessionCacheSize };
|
|
1478
|
+
this.ctx.plugin(SessionQueryRdb, queryConfig);
|
|
1456
1479
|
registerSessionImport(this.ctx, this);
|
|
1457
1480
|
registerSessionDeletion(this.ctx, this);
|
|
1458
1481
|
registerSessionExport(this.ctx, this);
|
|
@@ -1623,6 +1646,16 @@ var SessionPersistenceRdb = class extends SessionPersistence {
|
|
|
1623
1646
|
await this.ready;
|
|
1624
1647
|
await this.backend.vacuum();
|
|
1625
1648
|
}
|
|
1649
|
+
/**
|
|
1650
|
+
* 已归档的会话 id(`t_sessions.f_archived_at` 非空)。`session/list` 默认过滤掉它们。
|
|
1651
|
+
* 读的是持久化里的归档标记,不依赖 `workspaceRegistry` 服务的就绪时机。
|
|
1652
|
+
* @returns 归档过的 Session id 集。
|
|
1653
|
+
*/
|
|
1654
|
+
async archivedSessionIds() {
|
|
1655
|
+
await this.ready;
|
|
1656
|
+
const state = await this.backend.storage.readWorkspaceState();
|
|
1657
|
+
return new Set((state?.archivedSessionIds ?? []).map(String));
|
|
1658
|
+
}
|
|
1626
1659
|
async unarchiveBeforeDeletion(id) {
|
|
1627
1660
|
const registry = this.ctx.get("workspaceRegistry");
|
|
1628
1661
|
if (registry === void 0) return;
|
|
@@ -2011,14 +2044,14 @@ var SessionPersistenceRdb = class extends SessionPersistence {
|
|
|
2011
2044
|
let handle;
|
|
2012
2045
|
if (stored === void 0) {
|
|
2013
2046
|
handle = await this.create(session.header, { inheritedEventCount: session.inheritedEventCount });
|
|
2014
|
-
const seed = session
|
|
2047
|
+
const seed = liveEventsOf(session);
|
|
2015
2048
|
if (seed.length > 0) await handle.append(seed);
|
|
2016
2049
|
} else {
|
|
2017
2050
|
if (stored.meta.cwd !== session.header.cwd) throw new Error(`session "${id}" is already persisted at a different cwd (persisted: ${String(stored.meta.cwd)}, live: ${String(session.header.cwd)}) (id collision)`);
|
|
2018
2051
|
if (stored.inheritedEventCount !== session.inheritedEventCount) throw new Error(`session "${id}" is already persisted with a different inherited event count (id collision)`);
|
|
2019
2052
|
assertVersion(stored.meta);
|
|
2020
2053
|
repairReadView(stored.events);
|
|
2021
|
-
const seed = session
|
|
2054
|
+
const seed = liveEventsOf(session);
|
|
2022
2055
|
if (!seedCoversPrefix(seed, stored.events)) throw new Error(`session "${id}" already has a persisted log on disk that does not match this live session (id collision)`);
|
|
2023
2056
|
handle = await this.open(id, "write");
|
|
2024
2057
|
const suffix = seed.slice(stored.events.length);
|
|
@@ -2152,8 +2185,7 @@ async function appendEventTail(tx, meta, events, anchor, reuse) {
|
|
|
2152
2185
|
usageRows
|
|
2153
2186
|
};
|
|
2154
2187
|
}
|
|
2155
|
-
|
|
2156
|
-
//#region src/deletion.ts
|
|
2188
|
+
// src/deletion.ts
|
|
2157
2189
|
const STATUS_OF_DELETION_ERROR = {
|
|
2158
2190
|
SESSION_NOT_FOUND: 404,
|
|
2159
2191
|
SESSION_NOT_ARCHIVED: 409,
|
|
@@ -2214,5 +2246,4 @@ function registerSessionDeletion(ctx, persistence) {
|
|
|
2214
2246
|
}));
|
|
2215
2247
|
});
|
|
2216
2248
|
}
|
|
2217
|
-
//#endregion
|
|
2218
2249
|
export { SessionDeletionError as n, SessionPersistenceRdb as r, registerSessionDeletion as t };
|
package/dist/deletion.d.mts
CHANGED
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Nt as SESSION_DELETE_PATH, et as SessionPersistenceRdb } from "./log-TYgd8Xkr.mjs";
|
|
2
2
|
import { Context } from "@deepseek-ai/cordis";
|
|
3
|
-
//#region src/deletion.d.ts
|
|
4
3
|
export declare function registerSessionDeletion(ctx: Context, persistence: SessionPersistenceRdb): void;
|
|
5
|
-
//#endregion
|
|
6
4
|
export { SESSION_DELETE_PATH };
|
package/dist/deletion.mjs
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { t as SESSION_DELETE_PATH } from "./routes-
|
|
2
|
-
import { t as registerSessionDeletion } from "./deletion-
|
|
1
|
+
import { t as SESSION_DELETE_PATH } from "./routes-hZjTcX_R.mjs";
|
|
2
|
+
import { t as registerSessionDeletion } from "./deletion-CWl89TIR.mjs";
|
|
3
3
|
export { SESSION_DELETE_PATH, registerSessionDeletion };
|
package/dist/export.d.mts
CHANGED
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Pt as SESSION_EXPORT_PATH, et as SessionPersistenceRdb } from "./log-TYgd8Xkr.mjs";
|
|
2
2
|
import { Context } from "@deepseek-ai/cordis";
|
|
3
|
-
//#region src/export.d.ts
|
|
4
3
|
export declare function registerSessionExport(ctx: Context, persistence: SessionPersistenceRdb): void;
|
|
5
|
-
//#endregion
|
|
6
4
|
export { SESSION_EXPORT_PATH };
|
package/dist/export.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { n as SESSION_EXPORT_PATH } from "./routes-
|
|
1
|
+
import { n as SESSION_EXPORT_PATH } from "./routes-hZjTcX_R.mjs";
|
|
2
2
|
import { strToU8, zip } from "fflate";
|
|
3
|
-
|
|
3
|
+
// src/export.ts
|
|
4
4
|
function safeFilename(sessionId) {
|
|
5
5
|
return sessionId.replace(/[^A-Za-z0-9._-]/gu, "_");
|
|
6
6
|
}
|
|
@@ -69,5 +69,4 @@ function registerSessionExport(ctx, persistence) {
|
|
|
69
69
|
}), `session-rdb: ${SESSION_EXPORT_PATH} route`);
|
|
70
70
|
});
|
|
71
71
|
}
|
|
72
|
-
//#endregion
|
|
73
72
|
export { SESSION_EXPORT_PATH, registerSessionExport };
|
package/dist/gc.d.mts
CHANGED
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Ft as SESSION_GC_PATH, et as SessionPersistenceRdb } from "./log-TYgd8Xkr.mjs";
|
|
2
2
|
import { Context } from "@deepseek-ai/cordis";
|
|
3
|
-
//#region src/gc.d.ts
|
|
4
3
|
export declare function registerSessionGc(ctx: Context, persistence: SessionPersistenceRdb): void;
|
|
5
|
-
//#endregion
|
|
6
4
|
export { SESSION_GC_PATH };
|
package/dist/gc.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { r as SESSION_GC_PATH } from "./routes-
|
|
2
|
-
|
|
1
|
+
import { r as SESSION_GC_PATH } from "./routes-hZjTcX_R.mjs";
|
|
2
|
+
// src/gc.ts
|
|
3
3
|
async function stopRunningAgents(ctx) {
|
|
4
4
|
const running = ctx.get("agents")?.list() ?? [];
|
|
5
5
|
for (const agent of running) agent.cancel?.({ kind: "user" }, { keepInbox: true });
|
|
@@ -45,5 +45,4 @@ function registerSessionGc(ctx, persistence) {
|
|
|
45
45
|
}), `session-rdb: ${SESSION_GC_PATH} route`);
|
|
46
46
|
});
|
|
47
47
|
}
|
|
48
|
-
//#endregion
|
|
49
48
|
export { SESSION_GC_PATH, registerSessionGc };
|
package/dist/import.d.mts
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { It as SESSION_IMPORT_PATH, et as SessionPersistenceRdb } from "./log-TYgd8Xkr.mjs";
|
|
2
2
|
import { Session, SessionEvent, SessionId } from "@deepseek-ai/dsh-session";
|
|
3
3
|
import { SessionStorageMetadata } from "@deepseek-ai/dsh-session-persistence";
|
|
4
4
|
import { Context } from "@deepseek-ai/cordis";
|
|
5
|
-
//#region src/import.d.ts
|
|
6
5
|
export declare const SESSION_LOG_ARTIFACT_FILENAME = "session.jsonl";
|
|
7
6
|
export declare function parseJsonlArtifact(content: string): SessionStorageMetadata & {
|
|
8
7
|
events: SessionEvent[];
|
|
@@ -19,5 +18,4 @@ export declare function persistImport(persistence: SessionPersistenceRdb, branch
|
|
|
19
18
|
get(id: SessionId): Session | undefined;
|
|
20
19
|
}, stopLoop?: (id: SessionId) => Promise<void>): Promise<SessionId>;
|
|
21
20
|
export declare function registerSessionImport(ctx: Context, persistence: SessionPersistenceRdb): void;
|
|
22
|
-
//#endregion
|
|
23
21
|
export { SESSION_IMPORT_PATH };
|
package/dist/import.mjs
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { i as SESSION_IMPORT_PATH } from "./routes-
|
|
2
|
-
import {
|
|
1
|
+
import { i as SESSION_IMPORT_PATH } from "./routes-hZjTcX_R.mjs";
|
|
2
|
+
import { a as replaceLiveSessionLog, u as restoreCatalog } from "./branch-CoPCRKer.mjs";
|
|
3
3
|
import { SESSION_FORMAT_VERSION, SessionLogOffset } from "@deepseek-ai/dsh-session";
|
|
4
4
|
import { randomUUID } from "node:crypto";
|
|
5
5
|
import { parseSessionFormatLogFilename } from "@deepseek-ai/dsh-session-format";
|
|
6
6
|
import { balanceRewindPrefix } from "@morlay/session-branch";
|
|
7
7
|
import { unzip } from "fflate";
|
|
8
|
-
|
|
8
|
+
// src/import.ts
|
|
9
9
|
const SESSION_LOG_ARTIFACT_FILENAME = "session.jsonl";
|
|
10
10
|
const MAX_IMPORT_ZIP_BYTES = 67108864;
|
|
11
11
|
function parseJsonlArtifact(content) {
|
|
@@ -205,5 +205,4 @@ function registerSessionImport(ctx, persistence) {
|
|
|
205
205
|
}), `session-rdb: ${SESSION_IMPORT_PATH} route`);
|
|
206
206
|
});
|
|
207
207
|
}
|
|
208
|
-
//#endregion
|
|
209
208
|
export { SESSION_IMPORT_PATH, SESSION_LOG_ARTIFACT_FILENAME, parseImportZip, parseJsonlArtifact, persistImport, registerSessionImport };
|
package/dist/index.d.mts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { $ as SessionDeletionErrorCode,
|
|
2
|
-
export { Config, ProjectionCacheOptions, ResolvedConfig, SCHEMA_VERSION, SESSION_ROWS_PATH, SessionBranchRdb, SessionBranchRdbProvider, SessionDeletionError, SessionDeletionErrorCode, SessionPersistenceRdb, SessionPersistenceRdb as default, SessionPersistenceRdbInternals, type SessionRowsItem, type SessionRowsValue, locateTurnEnd };
|
|
1
|
+
import { $ as SessionDeletionErrorCode, Lt as SESSION_ROWS_PATH, Q as SessionDeletionError, X as ProjectionCacheOptions, Y as Config, Z as ResolvedConfig, at as locateTurnEnd, ct as SessionRowsValue, et as SessionPersistenceRdb, it as liveEventsOf, mt as SCHEMA_VERSION, nt as SessionBranchRdb, rt as SessionBranchRdbProvider, st as SessionRowsItem, tt as SessionPersistenceRdbInternals } from "./log-TYgd8Xkr.mjs";
|
|
2
|
+
export { Config, ProjectionCacheOptions, ResolvedConfig, SCHEMA_VERSION, SESSION_ROWS_PATH, SessionBranchRdb, SessionBranchRdbProvider, SessionDeletionError, SessionDeletionErrorCode, SessionPersistenceRdb, SessionPersistenceRdb as default, SessionPersistenceRdbInternals, type SessionRowsItem, type SessionRowsValue, liveEventsOf, locateTurnEnd };
|
package/dist/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { a as SESSION_ROWS_PATH } from "./routes-
|
|
2
|
-
import { n as SessionBranchRdbProvider, r as
|
|
3
|
-
import { o as SCHEMA_VERSION } from "./sqlite-
|
|
4
|
-
import { n as SessionDeletionError, r as SessionPersistenceRdb } from "./deletion-
|
|
5
|
-
export { SCHEMA_VERSION, SESSION_ROWS_PATH, SessionBranchRdb, SessionBranchRdbProvider, SessionDeletionError, SessionPersistenceRdb, SessionPersistenceRdb as default, locateTurnEnd };
|
|
1
|
+
import { a as SESSION_ROWS_PATH } from "./routes-hZjTcX_R.mjs";
|
|
2
|
+
import { i as locateTurnEnd, n as SessionBranchRdbProvider, r as liveEventsOf, t as SessionBranchRdb } from "./branch-CoPCRKer.mjs";
|
|
3
|
+
import { o as SCHEMA_VERSION } from "./sqlite-BNRd1aqR.mjs";
|
|
4
|
+
import { n as SessionDeletionError, r as SessionPersistenceRdb } from "./deletion-CWl89TIR.mjs";
|
|
5
|
+
export { SCHEMA_VERSION, SESSION_ROWS_PATH, SessionBranchRdb, SessionBranchRdbProvider, SessionDeletionError, SessionPersistenceRdb, SessionPersistenceRdb as default, liveEventsOf, locateTurnEnd };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { localDayKey } from "./usage.mjs";
|
|
2
2
|
import { SessionSeq } from "@deepseek-ai/dsh-session";
|
|
3
3
|
import { sessionFormatCatalog } from "@deepseek-ai/dsh-session-format-catalog";
|
|
4
|
-
|
|
4
|
+
// src/log.ts
|
|
5
5
|
function normalizeSurfaceOp(value) {
|
|
6
6
|
if (typeof value !== "object" || value === null || Array.isArray(value)) return value;
|
|
7
7
|
const record = value;
|
|
@@ -63,12 +63,13 @@ function sessionConflictRow(storage) {
|
|
|
63
63
|
function rowToEvent(row) {
|
|
64
64
|
const surfaceOp = row.fSurfaceOp !== null ? normalizeSurfaceOp(JSON.parse(row.fSurfaceOp)) : void 0;
|
|
65
65
|
const record = JSON.parse(row.fData);
|
|
66
|
-
if (typeof record === "object" && record !== null && !Array.isArray(record) && typeof record["type"] === "string" && typeof record["seq"] === "number" && typeof record["time"] === "number" && "data" in record)
|
|
67
|
-
|
|
68
|
-
seq
|
|
69
|
-
time
|
|
70
|
-
|
|
71
|
-
|
|
66
|
+
if (typeof record === "object" && record !== null && !Array.isArray(record) && typeof record["type"] === "string" && typeof record["seq"] === "number" && typeof record["time"] === "number" && "data" in record) {
|
|
67
|
+
const event = record;
|
|
68
|
+
event.seq = row.fSequence;
|
|
69
|
+
event.time = row.fCreatedAt;
|
|
70
|
+
if (surfaceOp !== void 0) event.surfaceOp = surfaceOp;
|
|
71
|
+
return event;
|
|
72
|
+
}
|
|
72
73
|
return {
|
|
73
74
|
type: row.fType,
|
|
74
75
|
seq: row.fSequence,
|
|
@@ -474,33 +475,29 @@ function repairOrphanInboxSplices(events) {
|
|
|
474
475
|
}
|
|
475
476
|
}
|
|
476
477
|
function scanRows(rows, base = 0) {
|
|
477
|
-
const parsed = rows.map((row) => {
|
|
478
|
-
try {
|
|
479
|
-
return {
|
|
480
|
-
ok: true,
|
|
481
|
-
event: rowToEvent(row)
|
|
482
|
-
};
|
|
483
|
-
} catch {
|
|
484
|
-
return { ok: false };
|
|
485
|
-
}
|
|
486
|
-
});
|
|
487
478
|
let lastTurnEnd = -1;
|
|
488
|
-
for (let i =
|
|
489
|
-
|
|
490
|
-
|
|
479
|
+
for (let i = rows.length - 1; i >= 0; i--) {
|
|
480
|
+
if (rows[i]?.fType !== "turn/end") continue;
|
|
481
|
+
try {
|
|
482
|
+
rowToEvent(rows[i]);
|
|
483
|
+
lastTurnEnd = i;
|
|
484
|
+
break;
|
|
485
|
+
} catch {}
|
|
491
486
|
}
|
|
492
487
|
const preserved = [];
|
|
493
488
|
for (let i = 0; i < rows.length; i++) {
|
|
494
|
-
|
|
495
|
-
|
|
489
|
+
let event;
|
|
490
|
+
try {
|
|
491
|
+
event = rowToEvent(rows[i]);
|
|
492
|
+
} catch {
|
|
496
493
|
if (i <= lastTurnEnd) throw new Error(`corrupt session log: unparsable committed event at seq ${rows[i]?.fSequence}`);
|
|
497
494
|
break;
|
|
498
495
|
}
|
|
499
|
-
if (
|
|
500
|
-
if (i <= lastTurnEnd) throw new Error(`corrupt session log: seq gap in committed region (expected ${base + i}, got ${
|
|
496
|
+
if (event.seq !== base + i) {
|
|
497
|
+
if (i <= lastTurnEnd) throw new Error(`corrupt session log: seq gap in committed region (expected ${base + i}, got ${event.seq})`);
|
|
501
498
|
break;
|
|
502
499
|
}
|
|
503
|
-
preserved.push(
|
|
500
|
+
preserved.push(event);
|
|
504
501
|
}
|
|
505
502
|
return preserved.length < rows.length ? {
|
|
506
503
|
preserved,
|
|
@@ -558,5 +555,4 @@ function usageRowOf(event, subagent) {
|
|
|
558
555
|
fTotalTokens: numericField(usage["totalTokens"])
|
|
559
556
|
};
|
|
560
557
|
}
|
|
561
|
-
//#endregion
|
|
562
558
|
export { usageRowOf as S, sessionConflictRow as _, orphanInboxSpliceSeqs as a, titleOfEventData as b, repairAssistantSettlement as c, repairRequestHeaders as d, repairSurfaceOps as f, sealOwnEvents as g, scanRows as h, normalizeToCurrentShape as i, repairOrphanInboxSplices as l, rowToMeta as m, findSurfaceRepairs as n, recomputeReplaceProvenance as o, rowToEvent as p, needsShapeAdoption as r, renameLegacyPtcEvents as s, OWN_EVENT_TYPES as t, repairReadView as u, sessionInsertRow as v, toJsonlArtifact as x, syncMeteringRanges as y };
|