@morlay/session-rdb 0.0.21-alpha.5 → 0.0.21-alpha.7
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 +53 -27
- package/dist/artifact.d.mts +27 -2
- package/dist/artifact.mjs +2 -2
- package/dist/{backend-a0xDrUFG.d.mts → backend-E0bmTXpz.d.mts} +233 -27
- package/dist/{branch-Dlmbkfjj.mjs → branch-BjUR6wfe.mjs} +31 -15
- package/dist/deletion.d.mts +1 -1
- package/dist/deletion.mjs +1 -1
- package/dist/import.d.mts +1 -1
- package/dist/import.mjs +2 -3
- package/dist/index.d.mts +2 -2
- package/dist/index.mjs +4 -4
- package/dist/{log-C-rbdFwK.mjs → log-qCNkDjh5.mjs} +93 -22
- package/dist/{sqlite-C3LHLzYW.mjs → sqlite-zcXVyMxr.mjs} +85 -10
- package/dist/{src-Bn0038Sp.mjs → src-B9X8BKyh.mjs} +210 -38
- package/dist/storage.d.mts +2 -1
- package/dist/storage.mjs +1 -1
- package/dist/testing.d.mts +2 -11
- package/dist/testing.mjs +12 -29
- package/drizzle/postgres/20260921075900_v3_agent_preset/migration.sql +1 -0
- package/drizzle/postgres/20260922090001_v3_pinned_seq/migration.sql +2 -0
- package/drizzle/postgres/20260922093001_v3_last_event_at/migration.sql +10 -0
- package/drizzle/sqlite/20260921075855_v3_agent_preset/migration.sql +7 -0
- package/drizzle/sqlite/20260921075855_v3_agent_preset/snapshot.json +1041 -0
- package/drizzle/sqlite/20260922090000_v3_pinned_seq/migration.sql +4 -0
- package/drizzle/sqlite/20260922093000_v3_last_event_at/migration.sql +13 -0
- package/locale/en.json +6 -0
- package/locale/zh.json +6 -0
- package/package.json +24 -22
- package/src/backend.ts +46 -1
- package/src/branch.ts +28 -11
- package/src/drizzle/postgres-v3.ts +1 -0
- package/src/drizzle/sqlite-v3.ts +1 -0
- package/src/entities/v3/event-usage.ts +5 -5
- package/src/entities/v3/sessions.ts +5 -0
- package/src/import.ts +3 -3
- package/src/index.ts +56 -22
- package/src/legacy.ts +14 -3
- package/src/log.ts +107 -22
- package/src/postgres.ts +65 -5
- package/src/rows.ts +125 -0
- package/src/schema.ts +3 -2
- package/src/session-query.ts +27 -1
- package/src/sqlite.ts +71 -3
- package/src/storage-takeover/projection-cache.ts +40 -15
- package/src/storage-takeover/repository.ts +34 -0
- package/src/storage-takeover/storage-backend.ts +3 -1
- package/src/testing/coordinator-contract.ts +2 -1
- package/src/testing.ts +0 -1
- package/src/write-guard.ts +5 -0
- package/src/testing/helpers.ts +0 -20
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { d as repairRequestHeaders, h as scanRows, i as normalizeToCurrentShape, m as rowToMeta } from "./log-qCNkDjh5.mjs";
|
|
2
2
|
import { randomUUID } from "node:crypto";
|
|
3
3
|
import { SESSION_FORMAT_VERSION, SessionLogOffset } from "@deepseek-ai/dsh-session";
|
|
4
|
-
import {
|
|
4
|
+
import { createSessionFormatCatalogWithChildren } from "@deepseek-ai/dsh-session-format-catalog";
|
|
5
5
|
import { SessionBranch, SessionBranchError, balanceRewindPrefix, rewindKeepLength } from "@morlay/session-branch";
|
|
6
6
|
//#region src/legacy.ts
|
|
7
7
|
function physicalHeader(row) {
|
|
@@ -13,7 +13,8 @@ function physicalHeader(row) {
|
|
|
13
13
|
...row.fCwd !== null ? { cwd: row.fCwd } : {},
|
|
14
14
|
...row.fParentSession !== null ? { parentSession: row.fParentSession } : {},
|
|
15
15
|
...row.fOrigin !== null ? { origin: row.fOrigin } : {},
|
|
16
|
-
delegationDepth: row.fDelegationDepth ?? 0
|
|
16
|
+
delegationDepth: row.fDelegationDepth ?? 0,
|
|
17
|
+
...row.fAgentPreset !== null ? { agentPreset: row.fAgentPreset } : {}
|
|
17
18
|
};
|
|
18
19
|
return row.fVersion < 2 ? {
|
|
19
20
|
...common,
|
|
@@ -34,8 +35,14 @@ function physicalEvent(row) {
|
|
|
34
35
|
...row.fSurfaceOp !== null ? { surfaceOp: JSON.parse(row.fSurfaceOp) } : {}
|
|
35
36
|
};
|
|
36
37
|
}
|
|
38
|
+
/**
|
|
39
|
+
* 读旧格式用的 catalog:v3→v4 这条边要求显式子会话证据(上游 0.1.7 起),本仓库声明
|
|
40
|
+
* **无子会话证据**——v3 的目录事实由父会话自己在成功路径上写成 `subagent/catalog` 事件,
|
|
41
|
+
* 迁移的 child evidence 只是补偿入口;我们没有更权威的子集合来源可给(见 ADR-跟随上游session-format-v4)。
|
|
42
|
+
*/
|
|
43
|
+
const restoreCatalog = createSessionFormatCatalogWithChildren([]);
|
|
37
44
|
function convertLegacyRows(row, eventRows) {
|
|
38
|
-
const restore =
|
|
45
|
+
const restore = restoreCatalog.createRestore(physicalHeader(row), {
|
|
39
46
|
recovery: "strict",
|
|
40
47
|
validation: "transformed"
|
|
41
48
|
});
|
|
@@ -53,6 +60,7 @@ function isLegacyVersion(version) {
|
|
|
53
60
|
function adoptLegacyRows(row, eventRows) {
|
|
54
61
|
const { preserved, tornFrom } = scanRows(eventRows, 0);
|
|
55
62
|
repairRequestHeaders(preserved);
|
|
63
|
+
normalizeToCurrentShape(preserved);
|
|
56
64
|
return {
|
|
57
65
|
meta: {
|
|
58
66
|
...rowToMeta(row),
|
|
@@ -187,19 +195,27 @@ var SessionBranchRdbProvider = class {
|
|
|
187
195
|
};
|
|
188
196
|
const seed = [...renumber(prefix, 0), ...renumber(seedSuffix, prefix.length)];
|
|
189
197
|
const internals = this.persistence.internals();
|
|
190
|
-
const
|
|
191
|
-
const sourceEventIds = new Map(sourceRows.map((row) => [row.fSequence, row.fEventId]));
|
|
198
|
+
const reusable = !source.migrated && source.events.length === source.storedCount;
|
|
192
199
|
const reuse = /* @__PURE__ */ new Map();
|
|
193
|
-
|
|
194
|
-
const
|
|
195
|
-
|
|
196
|
-
|
|
200
|
+
if (reusable) {
|
|
201
|
+
const sourceRows = await internals.backend.getEventRows(sourceId);
|
|
202
|
+
const sourceEventIds = new Map(sourceRows.map((row) => [row.fSequence, row.fEventId]));
|
|
203
|
+
for (const event of prefix) {
|
|
204
|
+
const eventId = sourceEventIds.get(event.seq);
|
|
205
|
+
if (eventId !== void 0) reuse.set(event.seq, eventId);
|
|
206
|
+
}
|
|
207
|
+
} 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`);
|
|
197
208
|
internals.registerReuseEventIds(childId, reuse);
|
|
198
|
-
const handle = await this.persistence.create(childMeta, { inheritedEventCount: SessionLogOffset(prefix.length) });
|
|
199
209
|
try {
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
210
|
+
const handle = await this.persistence.create(childMeta, { inheritedEventCount: SessionLogOffset(prefix.length) });
|
|
211
|
+
try {
|
|
212
|
+
if (seed.length > 0) await handle.append(seed);
|
|
213
|
+
} finally {
|
|
214
|
+
await handle.close();
|
|
215
|
+
}
|
|
216
|
+
} catch (error) {
|
|
217
|
+
internals.dropReuseEventIds(childId);
|
|
218
|
+
throw error;
|
|
203
219
|
}
|
|
204
220
|
await this.persistence.rebuildEventCounts(childId);
|
|
205
221
|
return childId;
|
|
@@ -379,4 +395,4 @@ var SessionBranchRdb = class extends SessionBranch {
|
|
|
379
395
|
}
|
|
380
396
|
};
|
|
381
397
|
//#endregion
|
|
382
|
-
export { truncateLiveSession as a, isLegacyVersion as c, replaceLiveSessionLog as i, SessionBranchRdbProvider as n, adoptLegacyRows as o, locateTurnEnd as r, convertLegacyRows as s, SessionBranchRdb as t };
|
|
398
|
+
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 };
|
package/dist/deletion.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { p as SessionPersistenceRdb } from "./backend-E0bmTXpz.mjs";
|
|
2
2
|
import { Context } from "@deepseek-ai/cordis";
|
|
3
3
|
//#region src/deletion.d.ts
|
|
4
4
|
declare const SESSION_DELETE_PATH = "/api/session.delete";
|
package/dist/deletion.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { a as registerSessionDeletion, i as SESSION_DELETE_PATH } from "./src-B9X8BKyh.mjs";
|
|
2
2
|
export { SESSION_DELETE_PATH, registerSessionDeletion };
|
package/dist/import.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { p as SessionPersistenceRdb } from "./backend-E0bmTXpz.mjs";
|
|
2
2
|
import { SessionStorageMetadata } from "@deepseek-ai/dsh-session-persistence";
|
|
3
3
|
import { Session, SessionEvent, SessionId } from "@deepseek-ai/dsh-session";
|
|
4
4
|
import { Context } from "@deepseek-ai/cordis";
|
package/dist/import.mjs
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
import { i as replaceLiveSessionLog } from "./branch-
|
|
1
|
+
import { i as replaceLiveSessionLog, l as restoreCatalog } from "./branch-BjUR6wfe.mjs";
|
|
2
2
|
import { randomUUID } from "node:crypto";
|
|
3
3
|
import { SESSION_FORMAT_VERSION, SessionLogOffset } from "@deepseek-ai/dsh-session";
|
|
4
4
|
import { parseSessionFormatLogFilename } from "@deepseek-ai/dsh-session-format";
|
|
5
|
-
import { sessionFormatCatalog } from "@deepseek-ai/dsh-session-format-catalog";
|
|
6
5
|
import { balanceRewindPrefix } from "@morlay/session-branch";
|
|
7
6
|
import { unzip } from "fflate";
|
|
8
7
|
//#region src/import.ts
|
|
@@ -20,7 +19,7 @@ function parseJsonlArtifact(content) {
|
|
|
20
19
|
}
|
|
21
20
|
let restore;
|
|
22
21
|
try {
|
|
23
|
-
restore =
|
|
22
|
+
restore = restoreCatalog.createRestore(header, {
|
|
24
23
|
recovery: "strict",
|
|
25
24
|
validation: "transformed"
|
|
26
25
|
});
|
package/dist/index.d.mts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export { Config, ProjectionCacheOptions, SCHEMA_VERSION, SessionBranchRdb, SessionBranchRdbProvider, SessionDeletionError, SessionDeletionErrorCode, SessionPersistenceRdb, SessionPersistenceRdb as default, SessionPersistenceRdbInternals, locateTurnEnd };
|
|
1
|
+
import { D as SCHEMA_VERSION, _ as locateTurnEnd, b as SessionRowsItem, d as SessionDeletionError, f as SessionDeletionErrorCode, g as SessionBranchRdbProvider, h as SessionBranchRdb, l as Config, m as SessionPersistenceRdbInternals, p as SessionPersistenceRdb, u as ProjectionCacheOptions, x as SessionRowsValue, y as SESSION_ROWS_PATH } from "./backend-E0bmTXpz.mjs";
|
|
2
|
+
export { Config, ProjectionCacheOptions, SCHEMA_VERSION, SESSION_ROWS_PATH, SessionBranchRdb, SessionBranchRdbProvider, SessionDeletionError, SessionDeletionErrorCode, SessionPersistenceRdb, SessionPersistenceRdb as default, SessionPersistenceRdbInternals, type SessionRowsItem, type SessionRowsValue, locateTurnEnd };
|
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { p as SCHEMA_VERSION } from "./sqlite-
|
|
2
|
-
import { n as SessionPersistenceRdb, t as SessionDeletionError } from "./src-
|
|
3
|
-
import { n as SessionBranchRdbProvider, r as locateTurnEnd, t as SessionBranchRdb } from "./branch-
|
|
4
|
-
export { SCHEMA_VERSION, SessionBranchRdb, SessionBranchRdbProvider, SessionDeletionError, SessionPersistenceRdb, SessionPersistenceRdb as default, locateTurnEnd };
|
|
1
|
+
import { p as SCHEMA_VERSION } from "./sqlite-zcXVyMxr.mjs";
|
|
2
|
+
import { n as SessionPersistenceRdb, r as SESSION_ROWS_PATH, t as SessionDeletionError } from "./src-B9X8BKyh.mjs";
|
|
3
|
+
import { n as SessionBranchRdbProvider, r as locateTurnEnd, t as SessionBranchRdb } from "./branch-BjUR6wfe.mjs";
|
|
4
|
+
export { SCHEMA_VERSION, SESSION_ROWS_PATH, SessionBranchRdb, SessionBranchRdbProvider, SessionDeletionError, SessionPersistenceRdb, SessionPersistenceRdb as default, locateTurnEnd };
|
|
@@ -24,7 +24,8 @@ function rowToMeta(row) {
|
|
|
24
24
|
...row.fParentSession !== null ? { parentSession: row.fParentSession } : {},
|
|
25
25
|
isSeeded: row.fSeedLength !== null,
|
|
26
26
|
...row.fOrigin !== null ? { origin: row.fOrigin } : {},
|
|
27
|
-
...row.fDelegationDepth === null ? {} : { delegationDepth: row.fDelegationDepth }
|
|
27
|
+
...row.fDelegationDepth === null ? {} : { delegationDepth: row.fDelegationDepth },
|
|
28
|
+
...row.fAgentPreset === null ? {} : { agentPreset: row.fAgentPreset }
|
|
28
29
|
};
|
|
29
30
|
}
|
|
30
31
|
function sessionInsertRow(storage, incarnation) {
|
|
@@ -40,6 +41,7 @@ function sessionInsertRow(storage, incarnation) {
|
|
|
40
41
|
fSeedLength: meta.isSeeded ? storage.inheritedEventCount : null,
|
|
41
42
|
fOrigin: meta.origin ?? null,
|
|
42
43
|
fDelegationDepth: meta.delegationDepth ?? null,
|
|
44
|
+
fAgentPreset: meta.agentPreset ?? null,
|
|
43
45
|
fIncarnation: incarnation,
|
|
44
46
|
fRevision: 0
|
|
45
47
|
};
|
|
@@ -53,7 +55,8 @@ function sessionConflictRow(storage) {
|
|
|
53
55
|
fParentSession: meta.parentSession ?? null,
|
|
54
56
|
fSeedLength: meta.isSeeded ? storage.inheritedEventCount : null,
|
|
55
57
|
fOrigin: meta.origin ?? null,
|
|
56
|
-
fDelegationDepth: meta.delegationDepth ?? null
|
|
58
|
+
fDelegationDepth: meta.delegationDepth ?? null,
|
|
59
|
+
fAgentPreset: meta.agentPreset ?? null
|
|
57
60
|
};
|
|
58
61
|
}
|
|
59
62
|
function rowToEvent(row) {
|
|
@@ -144,28 +147,15 @@ function toolResultRewriteContentOnly(original, replacement) {
|
|
|
144
147
|
const replacementData = replacement.data;
|
|
145
148
|
const originalMessage = originalData["message"];
|
|
146
149
|
const replacementMessage = replacementData["message"];
|
|
147
|
-
|
|
148
|
-
const
|
|
149
|
-
|
|
150
|
-
return isDeepEqualJson({
|
|
151
|
-
...originalData,
|
|
152
|
-
message: {
|
|
153
|
-
...originalMessage,
|
|
154
|
-
content: [{
|
|
155
|
-
...originalContent[0],
|
|
156
|
-
content: null
|
|
157
|
-
}]
|
|
158
|
-
}
|
|
159
|
-
}, {
|
|
160
|
-
...replacementData,
|
|
150
|
+
if (!Array.isArray(originalMessage?.content) || !Array.isArray(replacementMessage?.content)) return false;
|
|
151
|
+
const rest = (data, message) => ({
|
|
152
|
+
...data,
|
|
161
153
|
message: {
|
|
162
|
-
...
|
|
163
|
-
content:
|
|
164
|
-
...replacementContent[0],
|
|
165
|
-
content: null
|
|
166
|
-
}]
|
|
154
|
+
...message,
|
|
155
|
+
content: null
|
|
167
156
|
}
|
|
168
157
|
});
|
|
158
|
+
return isDeepEqualJson(rest(originalData, originalMessage), rest(replacementData, replacementMessage));
|
|
169
159
|
}
|
|
170
160
|
function findSurfaceRepairs(events) {
|
|
171
161
|
const nodes = [];
|
|
@@ -355,6 +345,87 @@ function renameLegacyPtcEvents(events) {
|
|
|
355
345
|
}
|
|
356
346
|
}
|
|
357
347
|
}
|
|
348
|
+
/**
|
|
349
|
+
* 把**旧代(v3 及更早)的消息形状**归一到当前格式(v4):迁移链(严格)拒绝那些形状时,读路径仍要
|
|
350
|
+
* 能把它们读出来——只动字段与标记,不改坐标、不改内容。
|
|
351
|
+
*
|
|
352
|
+
* 三件事:
|
|
353
|
+
* - `system/message` 的 source:`{ kind: 'plugin', plugin: '@deepseek-ai/dsh-system-prompt' }` → `{ kind: 'system-prompt' }`
|
|
354
|
+
* (v4 起 system 消息只认这个 kind);
|
|
355
|
+
* - `tool/result` 的消息:v3 是 user 角色、结果块包在 `content[0]`;v4 是 `role: 'tool'` + 顶层
|
|
356
|
+
* `toolCallId` + `content` 就是结果块;
|
|
357
|
+
* - 本仓库自己写过、上游已删除的事件类型(见 `LEGACY_OWN_EVENT_TYPES`)标 `ignorable: true`——v4 的
|
|
358
|
+
* 校验对未知类型只接受可忽略信封。
|
|
359
|
+
*
|
|
360
|
+
* 只认这张白名单:**其它未知类型保持 fail loud**(那是「数据来自更新的 harness」的信号,不能吞掉)。
|
|
361
|
+
*/
|
|
362
|
+
function normalizeToCurrentShape(events) {
|
|
363
|
+
for (const event of events) {
|
|
364
|
+
const type = event.type;
|
|
365
|
+
if (LEGACY_OWN_EVENT_TYPES.has(type)) {
|
|
366
|
+
event.ignorable = true;
|
|
367
|
+
continue;
|
|
368
|
+
}
|
|
369
|
+
if (type === "system/message") {
|
|
370
|
+
const message = event.data["message"];
|
|
371
|
+
liftSystemMessageSource(message);
|
|
372
|
+
continue;
|
|
373
|
+
}
|
|
374
|
+
if (type === "tool/result") {
|
|
375
|
+
const message = event.data["message"];
|
|
376
|
+
liftToolResultMessage(message);
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
function liftSystemMessageSource(message) {
|
|
381
|
+
if (typeof message !== "object" || message === null) return;
|
|
382
|
+
const record = message;
|
|
383
|
+
const source = record["source"];
|
|
384
|
+
if (typeof source !== "object" || source === null) return;
|
|
385
|
+
if (source["kind"] !== "plugin") return;
|
|
386
|
+
record["source"] = { kind: "system-prompt" };
|
|
387
|
+
}
|
|
388
|
+
function liftToolResultMessage(message) {
|
|
389
|
+
if (typeof message !== "object" || message === null) return;
|
|
390
|
+
const record = message;
|
|
391
|
+
if (record["role"] === "tool" && typeof record["toolCallId"] === "string") return;
|
|
392
|
+
const source = record["source"];
|
|
393
|
+
const block = Array.isArray(record["content"]) ? record["content"][0] : void 0;
|
|
394
|
+
if (typeof block !== "object" || block === null) return;
|
|
395
|
+
const lifted = block;
|
|
396
|
+
const callId = lifted["toolCallId"] ?? source?.["callId"];
|
|
397
|
+
if (typeof callId !== "string") return;
|
|
398
|
+
record["role"] = "tool";
|
|
399
|
+
record["toolCallId"] = callId;
|
|
400
|
+
record["content"] = Array.isArray(lifted["content"]) ? lifted["content"] : [];
|
|
401
|
+
if (typeof lifted["isError"] === "boolean") record["isError"] = lifted["isError"];
|
|
402
|
+
record["source"] = {
|
|
403
|
+
kind: "tool",
|
|
404
|
+
callId
|
|
405
|
+
};
|
|
406
|
+
}
|
|
407
|
+
/**
|
|
408
|
+
* 本仓库写过、后来连同机制一起删除的事件类型:读的时候标 `ignorable: true` 让校验跳过它们。
|
|
409
|
+
* 新增历史类型时登记到这里——**不要**用「所有未知类型」代替(那会掩盖更新版本的未知事件)。
|
|
410
|
+
*/
|
|
411
|
+
const LEGACY_OWN_EVENT_TYPES = /* @__PURE__ */ new Set(["session-branch/version"]);
|
|
412
|
+
/** 行里是否还带着旧代消息形状(`session-branch/version` 这类自造事件也算)——写路径曾把回退视图的结果
|
|
413
|
+
* 以当前版本号落库,所以**版本号不可信**,读之前要按内容判一次。 */
|
|
414
|
+
function hasLegacyShape(events) {
|
|
415
|
+
for (const event of events) {
|
|
416
|
+
const type = event.type;
|
|
417
|
+
if (LEGACY_OWN_EVENT_TYPES.has(type)) return true;
|
|
418
|
+
if (type === "system/message") {
|
|
419
|
+
if ((event.data["message"]?.["source"])?.["kind"] === "plugin") return true;
|
|
420
|
+
continue;
|
|
421
|
+
}
|
|
422
|
+
if (type === "tool/result") {
|
|
423
|
+
const message = event.data["message"];
|
|
424
|
+
if (message !== void 0 && message["role"] !== "tool") return true;
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
return false;
|
|
428
|
+
}
|
|
358
429
|
function repairReadView(events) {
|
|
359
430
|
repairAssistantSettlement(events);
|
|
360
431
|
repairRequestHeaders(events);
|
|
@@ -512,4 +583,4 @@ function usageRowOf(event) {
|
|
|
512
583
|
};
|
|
513
584
|
}
|
|
514
585
|
//#endregion
|
|
515
|
-
export {
|
|
586
|
+
export { sessionInsertRow as _, orphanInboxSpliceSeqs as a, toJsonlArtifact as b, repairAssistantSettlement as c, repairRequestHeaders as d, repairSurfaceOps as f, sessionConflictRow as g, scanRows as h, normalizeToCurrentShape as i, repairOrphanInboxSplices as l, rowToMeta as m, findSurfaceRepairs as n, recomputeReplaceProvenance as o, rowToEvent as p, hasLegacyShape as r, renameLegacyPtcEvents as s, LEGACY_OWN_EVENT_TYPES as t, repairReadView as u, syncMeteringRanges as v, usageRowOf as x, titleOfEventData as y };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { _ as sessionInsertRow, g as sessionConflictRow, x as usageRowOf, y as titleOfEventData } from "./log-qCNkDjh5.mjs";
|
|
2
2
|
import { randomUUID } from "node:crypto";
|
|
3
3
|
import { SESSION_FORMAT_VERSION, SessionId, SessionLogOffset, SessionSeq } from "@deepseek-ai/dsh-session";
|
|
4
4
|
import { and, desc, eq, gte, inArray, isNotNull, lt, notInArray, sql } from "drizzle-orm";
|
|
@@ -16,6 +16,10 @@ var WriteGuard = class {
|
|
|
16
16
|
confirmHead(id, head) {
|
|
17
17
|
this.headSeqs.set(id, head);
|
|
18
18
|
}
|
|
19
|
+
/** 本实例有没有这条会话的 head 记录(迁移重写用它决定是"登记基准"还是"严格比较")。 */
|
|
20
|
+
has(id) {
|
|
21
|
+
return this.headSeqs.has(id);
|
|
22
|
+
}
|
|
19
23
|
assertNoConcurrentWriter(id, storedHead) {
|
|
20
24
|
const known = this.headSeqs.get(id);
|
|
21
25
|
if (known === void 0) {
|
|
@@ -189,6 +193,7 @@ const sessions = {
|
|
|
189
193
|
f_seed_length: { type: "integer" },
|
|
190
194
|
f_origin: { type: "text" },
|
|
191
195
|
f_delegation_depth: { type: "integer" },
|
|
196
|
+
f_agent_preset: { type: "text" },
|
|
192
197
|
f_incarnation: {
|
|
193
198
|
type: "text",
|
|
194
199
|
notNull: true
|
|
@@ -198,6 +203,8 @@ const sessions = {
|
|
|
198
203
|
notNull: true
|
|
199
204
|
},
|
|
200
205
|
f_archived_at: { type: "bigint" },
|
|
206
|
+
f_pinned_seq: { type: "integer" },
|
|
207
|
+
f_last_event_at: { type: "bigint" },
|
|
201
208
|
f_title: { type: "text" },
|
|
202
209
|
f_title_seq: { type: "integer" }
|
|
203
210
|
}
|
|
@@ -468,23 +475,28 @@ const eventUsage = {
|
|
|
468
475
|
f_model: { type: "text" },
|
|
469
476
|
f_input_tokens: {
|
|
470
477
|
type: "integer",
|
|
471
|
-
notNull: true
|
|
478
|
+
notNull: true,
|
|
479
|
+
default: 0
|
|
472
480
|
},
|
|
473
481
|
f_output_tokens: {
|
|
474
482
|
type: "integer",
|
|
475
|
-
notNull: true
|
|
483
|
+
notNull: true,
|
|
484
|
+
default: 0
|
|
476
485
|
},
|
|
477
486
|
f_cache_read_tokens: {
|
|
478
487
|
type: "integer",
|
|
479
|
-
notNull: true
|
|
488
|
+
notNull: true,
|
|
489
|
+
default: 0
|
|
480
490
|
},
|
|
481
491
|
f_reasoning_tokens: {
|
|
482
492
|
type: "integer",
|
|
483
|
-
notNull: true
|
|
493
|
+
notNull: true,
|
|
494
|
+
default: 0
|
|
484
495
|
},
|
|
485
496
|
f_total_tokens: {
|
|
486
497
|
type: "integer",
|
|
487
|
-
notNull: true
|
|
498
|
+
notNull: true,
|
|
499
|
+
default: 0
|
|
488
500
|
}
|
|
489
501
|
},
|
|
490
502
|
indexes: { idx_event_usage_created_at: { columns: ["f_created_at"] } }
|
|
@@ -616,7 +628,7 @@ function eventDimensions(event) {
|
|
|
616
628
|
kind,
|
|
617
629
|
role: "tool",
|
|
618
630
|
name: "",
|
|
619
|
-
actionId: data["message"]?.
|
|
631
|
+
actionId: data["message"]?.toolCallId ?? ""
|
|
620
632
|
};
|
|
621
633
|
case "tool/call": return {
|
|
622
634
|
kind,
|
|
@@ -962,11 +974,16 @@ function createStorageRepository(host) {
|
|
|
962
974
|
if (row === void 0) return null;
|
|
963
975
|
const ordered = await allRows(db.select().from(tWorkspaces).where(gte(tWorkspaces.fPosition, 0)).orderBy(tWorkspaces.fPosition));
|
|
964
976
|
const archives = await allRows(db.select({ fSessionId: tSessions.fSessionId }).from(tSessions).where(isNotNull(tSessions.fArchivedAt)).orderBy(tSessions.fArchivedAt));
|
|
977
|
+
const pinned = await allRows(db.select({
|
|
978
|
+
fSessionId: tSessions.fSessionId,
|
|
979
|
+
fPinnedSeq: tSessions.fPinnedSeq
|
|
980
|
+
}).from(tSessions).where(isNotNull(tSessions.fPinnedSeq)).orderBy(tSessions.fPinnedSeq));
|
|
965
981
|
const pendingMutation = pendingMutationOf(row);
|
|
966
982
|
return {
|
|
967
983
|
initialized: row.fInitialized !== 0,
|
|
968
984
|
workspaceIds: ordered.map((entry) => entry.fWorkspaceId),
|
|
969
985
|
archivedSessionIds: archives.map((entry) => entry.fSessionId),
|
|
986
|
+
pinnedSessionIds: pinned.map((entry) => entry.fSessionId),
|
|
970
987
|
...pendingMutation === void 0 ? {} : { pendingMutation }
|
|
971
988
|
};
|
|
972
989
|
},
|
|
@@ -986,6 +1003,9 @@ function createStorageRepository(host) {
|
|
|
986
1003
|
}));
|
|
987
1004
|
await runQuery(db.update(tWorkspaces).set({ fPosition: -1 }));
|
|
988
1005
|
for (const [position, workspaceId] of state.workspaceIds.entries()) await runQuery(db.update(tWorkspaces).set({ fPosition: position }).where(eq(tWorkspaces.fWorkspaceId, workspaceId)));
|
|
1006
|
+
const pinned = state.pinnedSessionIds ?? [];
|
|
1007
|
+
await runQuery(pinned.length === 0 ? db.update(tSessions).set({ fPinnedSeq: null }).where(isNotNull(tSessions.fPinnedSeq)) : db.update(tSessions).set({ fPinnedSeq: null }).where(and(isNotNull(tSessions.fPinnedSeq), notInArray(tSessions.fSessionId, pinned))));
|
|
1008
|
+
for (const [position, sessionId] of pinned.entries()) await runQuery(db.update(tSessions).set({ fPinnedSeq: position }).where(eq(tSessions.fSessionId, sessionId)));
|
|
989
1009
|
const archived = state.archivedSessionIds;
|
|
990
1010
|
await runQuery(archived.length === 0 ? db.update(tSessions).set({ fArchivedAt: null }).where(isNotNull(tSessions.fArchivedAt)) : db.update(tSessions).set({ fArchivedAt: null }).where(and(isNotNull(tSessions.fArchivedAt), notInArray(tSessions.fSessionId, archived))));
|
|
991
1011
|
const stamp = Date.now();
|
|
@@ -1141,6 +1161,19 @@ function backfillEventUsage(db) {
|
|
|
1141
1161
|
AND (json_extract(e.f_data, '$.data.usage') IS NOT NULL
|
|
1142
1162
|
OR json_extract(e.f_data, '$.usage') IS NOT NULL)`);
|
|
1143
1163
|
}
|
|
1164
|
+
function sessionListRowOf(row) {
|
|
1165
|
+
return {
|
|
1166
|
+
sessionId: typeof row["session_id"] === "string" ? row["session_id"] : String(row["session_id"]),
|
|
1167
|
+
title: typeof row["title"] === "string" ? row["title"] : null,
|
|
1168
|
+
origin: typeof row["origin"] === "string" ? row["origin"] : null,
|
|
1169
|
+
cwd: typeof row["cwd"] === "string" ? row["cwd"] : null,
|
|
1170
|
+
createdAt: Number(row["created_at"]),
|
|
1171
|
+
updatedAt: Number(row["updated_at"]),
|
|
1172
|
+
archived: row["archived"] === 1 || row["archived"] === true,
|
|
1173
|
+
subagent: row["origin"] === "subagent",
|
|
1174
|
+
workspace: typeof row["workspace"] === "string" ? row["workspace"] : null
|
|
1175
|
+
};
|
|
1176
|
+
}
|
|
1144
1177
|
/** SQL 的缺失求和是 NULL:没有 usage 字段的行使该字段计 0。 */
|
|
1145
1178
|
function tokenTotalsOf(row) {
|
|
1146
1179
|
return {
|
|
@@ -1369,7 +1402,7 @@ var SqliteBackend = class SqliteBackend {
|
|
|
1369
1402
|
insertEvents: (events) => this.insertEvents(events),
|
|
1370
1403
|
insertBridges: (rows) => this.insertBridges(rows),
|
|
1371
1404
|
updateHead: (id, headEventId, headSequence) => this.updateHead(id, headEventId, headSequence),
|
|
1372
|
-
bumpRevision: (id) => this.bumpRevision(id),
|
|
1405
|
+
bumpRevision: (id, lastEventAt) => this.bumpRevision(id, lastEventAt),
|
|
1373
1406
|
refreshTitle: (id) => this.refreshTitle(id),
|
|
1374
1407
|
deleteBridgeTail: (id, fromSequence) => this.deleteBridgeTail(id, fromSequence),
|
|
1375
1408
|
getPrevBridge: (id, sequence) => this.getPrevBridge(id, sequence),
|
|
@@ -1418,8 +1451,11 @@ var SqliteBackend = class SqliteBackend {
|
|
|
1418
1451
|
fHeadSequence: headSequence
|
|
1419
1452
|
}).where(eq(tSessions.fSessionId, id)).run();
|
|
1420
1453
|
}
|
|
1421
|
-
async bumpRevision(id) {
|
|
1422
|
-
this.db.update(tSessions).set({
|
|
1454
|
+
async bumpRevision(id, lastEventAt) {
|
|
1455
|
+
this.db.update(tSessions).set({
|
|
1456
|
+
fRevision: sql`${tSessions.fRevision} + 1`,
|
|
1457
|
+
...lastEventAt === void 0 ? {} : { fLastEventAt: sql`MAX(COALESCE(${tSessions.fLastEventAt}, 0), ${lastEventAt})` }
|
|
1458
|
+
}).where(eq(tSessions.fSessionId, id)).run();
|
|
1423
1459
|
}
|
|
1424
1460
|
async refreshTitle(id) {
|
|
1425
1461
|
const row = this.db.select({
|
|
@@ -1474,6 +1510,45 @@ var SqliteBackend = class SqliteBackend {
|
|
|
1474
1510
|
* 沿 `t_events` 的事件类型数——两者限定在**有 token 用量的会话**里,时间范围各自按自己的
|
|
1475
1511
|
* `f_created_at`(同一时刻写入,口径一致)。
|
|
1476
1512
|
*/
|
|
1513
|
+
async listSessionRows(query = {}) {
|
|
1514
|
+
const needle = query.query?.trim().toLowerCase() ?? "";
|
|
1515
|
+
const like = `%${needle}%`;
|
|
1516
|
+
const where = `WHERE (? = '' OR lower(COALESCE(s.f_title, '')) LIKE ? OR lower(COALESCE(w.f_title, '')) LIKE ?)
|
|
1517
|
+
AND (? = 1 OR s.f_origin IS NOT 'subagent')`;
|
|
1518
|
+
const filterParams = [
|
|
1519
|
+
needle,
|
|
1520
|
+
like,
|
|
1521
|
+
like,
|
|
1522
|
+
query.includeSubagents === true ? 1 : 0
|
|
1523
|
+
];
|
|
1524
|
+
const limit = query.limit ?? 20;
|
|
1525
|
+
const offset = query.offset ?? 0;
|
|
1526
|
+
const rows = this.db.$client.prepare(`SELECT s.f_session_id AS session_id,
|
|
1527
|
+
s.f_title AS title,
|
|
1528
|
+
s.f_origin AS origin,
|
|
1529
|
+
s.f_cwd AS cwd,
|
|
1530
|
+
s.f_created_at AS created_at,
|
|
1531
|
+
(s.f_archived_at IS NOT NULL) AS archived,
|
|
1532
|
+
COALESCE(s.f_last_event_at, s.f_created_at) AS updated_at,
|
|
1533
|
+
w.f_title AS workspace
|
|
1534
|
+
FROM t_sessions s
|
|
1535
|
+
LEFT JOIN t_workspaces w ON w.f_workspace_id = (
|
|
1536
|
+
SELECT b.f_workspace_id FROM t_workspace_sessions b
|
|
1537
|
+
WHERE b.f_session_id = s.f_session_id ORDER BY b.f_position LIMIT 1)
|
|
1538
|
+
${where}
|
|
1539
|
+
ORDER BY updated_at DESC, s.f_session_id
|
|
1540
|
+
LIMIT ? OFFSET ?`).all(...filterParams, limit, offset);
|
|
1541
|
+
const counted = this.db.$client.prepare(`SELECT count(*) AS n
|
|
1542
|
+
FROM t_sessions s
|
|
1543
|
+
LEFT JOIN t_workspaces w ON w.f_workspace_id = (
|
|
1544
|
+
SELECT b.f_workspace_id FROM t_workspace_sessions b
|
|
1545
|
+
WHERE b.f_session_id = s.f_session_id ORDER BY b.f_position LIMIT 1)
|
|
1546
|
+
${where}`).get(...filterParams);
|
|
1547
|
+
return {
|
|
1548
|
+
items: rows.map((row) => sessionListRowOf(row)),
|
|
1549
|
+
total: Number(counted.n)
|
|
1550
|
+
};
|
|
1551
|
+
}
|
|
1477
1552
|
async usageReport(sinceMs) {
|
|
1478
1553
|
const usageSince = sinceMs === void 0 ? "" : " AND u.f_created_at >= ?";
|
|
1479
1554
|
const params = sinceMs === void 0 ? [] : [sinceMs];
|