@morlay/session-rdb 0.0.20 → 0.0.21-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.
Files changed (69) hide show
  1. package/README.md +59 -31
  2. package/dist/artifact.d.mts +25 -65
  3. package/dist/artifact.mjs +2 -2
  4. package/dist/{schema-BkQN5GyT.d.mts → backend-DpdtxYpz.d.mts} +346 -93
  5. package/dist/{branch-Co6xlbi0.mjs → branch-5JzX9rUq.mjs} +163 -52
  6. package/dist/deletion.d.mts +7 -0
  7. package/dist/deletion.mjs +2 -0
  8. package/dist/dist-vIVO6bA-.mjs +1524 -0
  9. package/dist/import.d.mts +4 -4
  10. package/dist/import.mjs +28 -11
  11. package/dist/index.d.mts +2 -3
  12. package/dist/index.mjs +4 -1631
  13. package/dist/{log-DO69NQnn.mjs → log-CnYct2Dv.mjs} +37 -82
  14. package/dist/{sqlite-DYExtbLo.mjs → sqlite-fpvm5Dzs.mjs} +205 -75
  15. package/dist/src-CWTWV7vx.mjs +1904 -0
  16. package/dist/storage.d.mts +14 -5
  17. package/dist/storage.mjs +2 -2
  18. package/dist/testing.d.mts +1 -26
  19. package/dist/testing.mjs +10503 -9614
  20. package/drizzle/postgres/20260918120000_v3_event_usage/migration.sql +14 -0
  21. package/drizzle/postgres/20260918120000_v3_event_usage/snapshot.json +1309 -0
  22. package/drizzle/sqlite/20260918120000_v3_event_usage/migration.sql +14 -0
  23. package/drizzle/sqlite/20260918120000_v3_event_usage/snapshot.json +1031 -0
  24. package/package.json +28 -29
  25. package/src/adapters/to-postgres.ts +1 -3
  26. package/src/adapters/to-sqlite.ts +0 -2
  27. package/src/adapters/types.ts +0 -3
  28. package/src/artifact.ts +0 -2
  29. package/src/backend.ts +31 -2
  30. package/src/branch.ts +223 -135
  31. package/src/deletion.ts +87 -0
  32. package/src/drizzle/postgres-v2.ts +0 -1
  33. package/src/drizzle/postgres-v3.ts +0 -1
  34. package/src/drizzle/sqlite-v2.ts +0 -1
  35. package/src/drizzle/sqlite-v3.ts +0 -1
  36. package/src/entities/v2/session-events.ts +0 -1
  37. package/src/entities/v3/event-usage.ts +25 -0
  38. package/src/entities/v3/events.ts +1 -3
  39. package/src/entities/v3/index.ts +4 -0
  40. package/src/entities/v3/session-events.ts +1 -2
  41. package/src/entities/v3/session-projcache-rows.ts +0 -8
  42. package/src/entities/v3/sessions.ts +3 -3
  43. package/src/entities/v3/storage-units.ts +0 -1
  44. package/src/entities/v3/workspace-sessions.ts +0 -5
  45. package/src/entities/v3/workspace-state.ts +0 -6
  46. package/src/entities/v3/workspaces.ts +0 -5
  47. package/src/export.ts +103 -0
  48. package/src/gc.ts +76 -0
  49. package/src/import-storages.ts +4 -37
  50. package/src/import.ts +54 -31
  51. package/src/index.ts +150 -114
  52. package/src/legacy.ts +4 -42
  53. package/src/log.ts +63 -106
  54. package/src/postgres.ts +249 -22
  55. package/src/schema.ts +6 -6
  56. package/src/session-query.ts +0 -4
  57. package/src/sqlite.ts +231 -55
  58. package/src/storage-takeover/index.ts +2 -28
  59. package/src/storage-takeover/projection-cache.ts +30 -134
  60. package/src/storage-takeover/repository.ts +20 -59
  61. package/src/storage-takeover/storage-backend.ts +3 -33
  62. package/src/storage-takeover/types.ts +14 -56
  63. package/src/storage.ts +0 -2
  64. package/src/testing/contract.ts +19 -50
  65. package/src/testing/coordinator-contract.ts +10 -25
  66. package/src/testing.ts +1 -4
  67. package/src/usage.ts +191 -0
  68. package/dist/index-D55G9n4k.d.mts +0 -271
  69. package/dist/magic-string.es-BgJoa-3K.mjs +0 -1017
package/src/usage.ts ADDED
@@ -0,0 +1,191 @@
1
+ import type { Context } from "@deepseek-ai/cordis";
2
+ import type { SessionPersistenceRdb } from "./index.ts";
3
+
4
+ export const SESSION_USAGE_PATH = "/api/session.usage";
5
+
6
+ const DAY_MS = 24 * 60 * 60 * 1000;
7
+
8
+ /**
9
+ * 时间范围的语义键:`all` 不限;`day` / `week` 是**本地自然日 / 自然周**(周一起算);
10
+ * `7d` / `30d` / `90d` 是最近 N 天(滚动窗口)。
11
+ */
12
+ export type UsageRangeKey = "all" | "day" | "week" | "7d" | "30d" | "90d";
13
+
14
+ const ROLLING_DAYS: Record<"7d" | "30d" | "90d", number> = { "7d": 7, "30d": 30, "90d": 90 };
15
+
16
+ function localDayStart(now: number): Date {
17
+ const start = new Date(now);
18
+ start.setHours(0, 0, 0, 0);
19
+ return start;
20
+ }
21
+
22
+ /**
23
+ * 范围起点(含),不限时为 undefined。自然日 / 自然周按 host 的本地时区算,周边界是周一 00:00。
24
+ * @param range - 语义键。
25
+ * @param now - 当前时刻。
26
+ * @returns 起点毫秒时间戳,或 undefined。
27
+ */
28
+ export function resolveUsageSince(range: UsageRangeKey, now: number): number | undefined {
29
+ switch (range) {
30
+ case "all":
31
+ return undefined;
32
+ case "day":
33
+ return localDayStart(now).getTime();
34
+ case "week": {
35
+ const start = localDayStart(now);
36
+ // getDay(): 0 = 周日;换算成「距离本周一几天」。
37
+ start.setDate(start.getDate() - ((start.getDay() + 6) % 7));
38
+ return start.getTime();
39
+ }
40
+ default:
41
+ return now - ROLLING_DAYS[range] * DAY_MS;
42
+ }
43
+ }
44
+
45
+ function parseUsageRange(value: unknown): UsageRangeKey {
46
+ return value === "day" || value === "week" || value === "7d" || value === "30d" || value === "90d"
47
+ ? value
48
+ : "all";
49
+ }
50
+
51
+ /** 一段用量合计(token 字段直接取事件里模型报的 usage)。 */
52
+ export interface UsageTotals {
53
+ events: number;
54
+ inputTokens: number;
55
+ outputTokens: number;
56
+ cacheReadTokens: number;
57
+ reasoningTokens: number;
58
+ totalTokens: number;
59
+ }
60
+
61
+ /** 一天 × 一个模型 × 是否子代理 的用量桶:总览、按天、按模型都由它折叠。 */
62
+ export interface UsageBucket extends UsageTotals {
63
+ day: string;
64
+ provider: string | null;
65
+ model: string | null;
66
+ subagent: boolean;
67
+ }
68
+
69
+ /** 一条会话的用量行。 */
70
+ export interface UsageSessionRow extends UsageTotals {
71
+ sessionId: string;
72
+ title: string | null;
73
+ subagent: boolean;
74
+ archived: boolean;
75
+ }
76
+
77
+ /** 后端的原始聚合结果。 */
78
+ export interface UsageAggregate {
79
+ buckets: UsageBucket[];
80
+ sessions: UsageSessionRow[];
81
+ }
82
+
83
+ /** 一次统计请求的完整回报。 */
84
+ export interface SessionUsageReport {
85
+ totals: UsageTotals;
86
+ subagent: UsageTotals;
87
+ /** 只被人类会话引用的部分(`totals − subagent`)。 */
88
+ human: UsageTotals;
89
+ buckets: UsageBucket[];
90
+ sessions: UsageSessionRow[];
91
+ }
92
+
93
+ function emptyTotals(): UsageTotals {
94
+ return {
95
+ events: 0,
96
+ inputTokens: 0,
97
+ outputTokens: 0,
98
+ cacheReadTokens: 0,
99
+ reasoningTokens: 0,
100
+ totalTokens: 0,
101
+ };
102
+ }
103
+
104
+ /** 折叠一组行(桶或会话行)的用量。 */
105
+ export function sumUsage(rows: readonly UsageTotals[]): UsageTotals {
106
+ const totals = emptyTotals();
107
+ for (const row of rows) {
108
+ totals.events += row.events;
109
+ totals.inputTokens += row.inputTokens;
110
+ totals.outputTokens += row.outputTokens;
111
+ totals.cacheReadTokens += row.cacheReadTokens;
112
+ totals.reasoningTokens += row.reasoningTokens;
113
+ totals.totalTokens += row.totalTokens;
114
+ }
115
+ return totals;
116
+ }
117
+
118
+ /**
119
+ * 用量统计通道:一次请求回报总览(含 subagent 拆分)、按天 × 模型的桶与按会话的行。
120
+ * 聚合只算被会话引用的事件行——fork 共享行因此只计一次,已删会话留下的孤儿行不计。
121
+ */
122
+ export function registerSessionUsage(ctx: Context, persistence: SessionPersistenceRdb): void {
123
+ ctx.inject(["webServer", "connection"] as const, (webCtx) => {
124
+ const webServer = webCtx.webServer as unknown as {
125
+ register(route: {
126
+ kind: "exact";
127
+ path: string;
128
+ handler: (
129
+ req: import("node:http").IncomingMessage,
130
+ res: import("node:http").ServerResponse,
131
+ ) => void | Promise<void>;
132
+ }): () => void;
133
+ };
134
+ const connection = webCtx.get("connection") as unknown as {
135
+ requestRejection(request: {
136
+ headers: import("node:http").IncomingHttpHeaders;
137
+ }): number | undefined;
138
+ };
139
+ return webCtx.effect(
140
+ () =>
141
+ webServer.register({
142
+ kind: "exact",
143
+ path: SESSION_USAGE_PATH,
144
+ handler: async (req, res) => {
145
+ const rejection = connection.requestRejection(req);
146
+ if (rejection !== undefined) {
147
+ res.writeHead(rejection);
148
+ res.end(rejection === 401 ? "unauthorized" : "forbidden");
149
+ return;
150
+ }
151
+ if (req.method !== "POST") {
152
+ res.writeHead(405, { "content-type": "application/json" });
153
+ res.end(JSON.stringify({ error: "method not allowed" }));
154
+ return;
155
+ }
156
+ const chunks: Buffer[] = [];
157
+ for await (const chunk of req) chunks.push(chunk as Buffer);
158
+ const raw = Buffer.concat(chunks).toString("utf8");
159
+ let envelope: { range?: unknown } = {};
160
+ if (raw !== "") {
161
+ try {
162
+ envelope = JSON.parse(raw) as { range?: unknown };
163
+ } catch {
164
+ res.writeHead(400, { "content-type": "application/json" });
165
+ res.end(JSON.stringify({ error: "request body is not JSON" }));
166
+ return;
167
+ }
168
+ }
169
+ try {
170
+ const aggregate = await persistence.usageReport(
171
+ resolveUsageSince(parseUsageRange(envelope.range), Date.now()),
172
+ );
173
+ const totals = sumUsage(aggregate.buckets);
174
+ const subagent = sumUsage(aggregate.buckets.filter((bucket) => bucket.subagent));
175
+ const human = sumUsage(aggregate.buckets.filter((bucket) => !bucket.subagent));
176
+ res.writeHead(200, { "content-type": "application/json" });
177
+ res.end(JSON.stringify({ totals, subagent, human, ...aggregate }));
178
+ } catch (error: unknown) {
179
+ res.writeHead(500, { "content-type": "application/json" });
180
+ res.end(
181
+ JSON.stringify({
182
+ error: error instanceof Error ? error.message : "usage report failed",
183
+ }),
184
+ );
185
+ }
186
+ },
187
+ }),
188
+ `session-rdb: ${SESSION_USAGE_PATH} route`,
189
+ );
190
+ });
191
+ }
@@ -1,271 +0,0 @@
1
- import { a as JournalMode, b as WriteGuard, x as Backend } from "./schema-BkQN5GyT.mjs";
2
- import z from "@deepseek-ai/schemastery";
3
- import { SessionAccess, SessionHandle, SessionHandleAppendOptions, SessionHandleFlushOptions, SessionHandleReadOptions, SessionHandleReadResult, SessionPersistence, SessionPersistenceCreateOptions, SessionPersistenceListOptions, SessionPersistenceOpenOptions, SessionPersistenceRevision, SessionPersistenceSnapshot, SessionPersistenceStatOptions } from "@deepseek-ai/dsh-session-persistence";
4
- import { Session, SessionEvent, SessionHeader, SessionId, SessionLogOffset } from "@deepseek-ai/dsh-session";
5
- import { BranchAnchorMode, BranchBoundary, ForkFromOptions, SessionBranch, SessionBranchProvider } from "@morlay/session-branch";
6
- import { Context } from "@deepseek-ai/cordis";
7
- //#region src/branch.d.ts
8
- declare function locateTurnEnd(events: readonly SessionEvent[], atSeq?: number, mode?: BranchAnchorMode): number;
9
- interface LiveSessionHooks {
10
- getSession(id: SessionId): Session | undefined;
11
- getAgent(id: SessionId): LiveAgentLike | undefined;
12
- flush(session: Session): Promise<boolean>;
13
- resetProjections?(session: Session): void;
14
- refreshProjectionCache?(session: ProjectionCacheSession): Promise<void>;
15
- }
16
- /**
17
- * 投影检查点刷新所需的最小会话面:cold rewind 没有 live Session 可复用,
18
- * 而截断后的前缀未必是合法的独立会话(surface 引用可能悬空),不能走
19
- * `Session.create` 的校验;缓存服务只读 id / header / inheritedEventCount
20
- * 与事件前缀,用普通对象承载即可。
21
- */
22
- interface ProjectionCacheSession {
23
- readonly id: SessionId;
24
- readonly header: SessionHeader;
25
- readonly inheritedEventCount: SessionLogOffset;
26
- snapshotEvents(): readonly SessionEvent[];
27
- }
28
- interface LiveAgentLike {
29
- session: Session;
30
- requestHeaderLogged?: boolean;
31
- inbox?: {
32
- clear(): void;
33
- };
34
- }
35
- declare function truncateLiveSession(session: Session, newLength: number): void;
36
- declare class SessionBranchRdbProvider implements SessionBranchProvider {
37
- private readonly persistence;
38
- private readonly live;
39
- readonly name = "session-rdb";
40
- constructor(persistence: SessionPersistenceRdb, live?: LiveSessionHooks);
41
- readBranchPrefix(id: SessionId, atSeq?: number, mode?: BranchAnchorMode, signal?: AbortSignal): Promise<BranchBoundary>;
42
- readRawEvents(id: SessionId, signal?: AbortSignal): Promise<{
43
- meta: SessionHeader;
44
- events: readonly SessionEvent[];
45
- }>;
46
- forkFrom(sourceId: SessionId, options?: ForkFromOptions, signal?: AbortSignal): Promise<SessionId>;
47
- rewind(id: SessionId, toBoundary: number, signal?: AbortSignal): Promise<SessionPersistenceSnapshot>;
48
- /**
49
- * 让持久化投影检查点跟随截断后的 log(见 {@link LiveSessionHooks.refreshProjectionCache})。
50
- * 缓存是派生数据:刷新失败只让缓存多滞后一轮,不能让 rewind 失败。
51
- */
52
- private refreshProjectionCache;
53
- }
54
- declare class SessionBranchRdb extends SessionBranch {
55
- static inject: string[];
56
- constructor(ctx: import("@deepseek-ai/cordis").Context);
57
- private readonly provider;
58
- readBranchPrefix(id: SessionId, atSeq?: number, mode?: BranchAnchorMode, signal?: AbortSignal): Promise<BranchBoundary>;
59
- readRawEvents(id: SessionId, signal?: AbortSignal): Promise<{
60
- meta: SessionHeader;
61
- events: readonly SessionEvent[];
62
- }>;
63
- forkFrom(sourceId: SessionId, options?: ForkFromOptions, signal?: AbortSignal): Promise<SessionId>;
64
- rewind(id: SessionId, toBoundary: number, signal?: AbortSignal): Promise<SessionPersistenceSnapshot>;
65
- timeline(sessionId: SessionId, signal?: AbortSignal): Promise<import("@morlay/session-branch").BranchTimeline>;
66
- }
67
- //#endregion
68
- //#region src/index.d.ts
69
- interface SessionPersistenceRdbInternals {
70
- readonly backend: Backend;
71
- readonly writeGuard: WriteGuard;
72
- create(meta: SessionHeader, inheritedEventCount?: number): Promise<SessionHandle>;
73
- append(id: SessionId, events: readonly SessionEvent[]): Promise<void>;
74
- load(id: SessionId): Promise<import("@deepseek-ai/dsh-session-persistence").SessionInspection>;
75
- inspect(id: SessionId, signal?: AbortSignal): Promise<import("@deepseek-ai/dsh-session-persistence").SessionInspection>;
76
- readFrom(id: SessionId, fromSeq: number, signal?: AbortSignal): Promise<{
77
- meta: SessionHeader;
78
- inheritedEventCount: number;
79
- events: readonly SessionEvent[];
80
- }>;
81
- listSnapshots(signal?: AbortSignal): Promise<SessionPersistenceSnapshot[]>;
82
- readStoredRevision(id: SessionId, signal?: AbortSignal): Promise<import("@deepseek-ai/dsh-session-persistence").SessionPersistenceRevision | undefined>;
83
- registerReuseEventIds(childId: SessionId, map: ReadonlyMap<number, string>): void;
84
- }
85
- /** 投影 checkpoint 写节流(替换上游 `session-projection-cache` 的 Config)。 */
86
- interface ProjectionCacheOptions {
87
- /** 两次强制点之间累积多少已提交事件强制落盘(默认 200)。 */
88
- writeEveryEvents?: number;
89
- /** 脏 checkpoint 在强制点之间允许滞留的最长毫秒数(默认 5000)。 */
90
- writeIntervalMs?: number;
91
- }
92
- type Config = {
93
- type: "sqlite";
94
- path: string;
95
- journalMode?: JournalMode;
96
- busyTimeout?: number;
97
- projectionCache?: ProjectionCacheOptions;
98
- } | {
99
- type: "postgres";
100
- connectionString: string;
101
- schema?: string;
102
- projectionCache?: ProjectionCacheOptions;
103
- };
104
- /** 一个已创建但未 materialize 的会话(本进程可见,其他进程不可见)。 */
105
- interface PendingSession {
106
- readonly header: SessionHeader;
107
- readonly revision: SessionPersistenceRevision;
108
- readonly inheritedEventCount: SessionLogOffset;
109
- /** 输入空间 cursor(全 delta 批次也推进)。 */
110
- readonly cursor: number;
111
- /** 是否调用过 append(close 时保留 pending)。 */
112
- readonly everAppended: boolean;
113
- }
114
- /** 会话级写所有权与 live 路由簿记。 */
115
- declare class RdbBackendTracker {
116
- private readonly name;
117
- /** 每个 id 的活跃 write handle;`null` 表示 claim 构造中。 */
118
- private readonly writers;
119
- private readonly pending;
120
- private readonly openHandles;
121
- private counter;
122
- constructor(name: string);
123
- registerCreated(header: SessionHeader, inheritedEventCount: SessionLogOffset): void;
124
- /** 更新 pending 的 cursor / everAppended(handle append 后同步)。 */
125
- updatePending(id: SessionId, cursor: number, everAppended: boolean): void;
126
- claimWrite(id: SessionId): void;
127
- releaseClaim(id: SessionId): void;
128
- pendingOf(id: SessionId): PendingSession | undefined;
129
- hasPending(id: SessionId): boolean;
130
- pendingEntries(): IterableIterator<[SessionId, PendingSession]>;
131
- materialized(id: SessionId): void;
132
- adopt(handle: RdbSessionHandle): RdbSessionHandle;
133
- release(handle: RdbSessionHandle, materialized: boolean): void;
134
- writerOf(id: SessionId): RdbSessionHandle | undefined;
135
- flushAll(): Promise<void>;
136
- closeAll(): Promise<void>;
137
- }
138
- /** 一个打开会话的存储句柄:read / append / flush / close。 */
139
- declare class RdbSessionHandle implements SessionHandle {
140
- private readonly persistence;
141
- readonly id: SessionId;
142
- readonly header: SessionHeader;
143
- readonly access: SessionAccess;
144
- private readonly state;
145
- private chain;
146
- private closing;
147
- /** 稠密 next-seq(输入空间计数,与旧版 coordinator cursor 同语义)。 */
148
- private cursor;
149
- private materialized;
150
- /** live 路由缓冲(上游 seq 事件,drain 时过滤 delta 后重编号稠密)。 */
151
- private buffered;
152
- private batchTimer;
153
- private drainPaused;
154
- private draining;
155
- /** write open 时发现的 torn tail 起点(append 前先截断)。 */
156
- private tornTruncateTo;
157
- /** 是否调用过 append(即使全 delta 未落库)——close 时保留 pending。 */
158
- private everAppended;
159
- constructor(persistence: SessionPersistenceRdb, id: SessionId, header: SessionHeader, access: SessionAccess, state: {
160
- cursor: number;
161
- materialized: boolean;
162
- inheritedEventCount: SessionLogOffset;
163
- tornTruncateTo?: number;
164
- });
165
- get inheritedEventCount(): SessionLogOffset;
166
- /** 输入空间 cursor(下一个待落库的上游 seq)。 */
167
- get cursorValue(): number;
168
- read(offset?: number, length?: number, options?: SessionHandleReadOptions): Promise<SessionHandleReadResult>;
169
- append(events: readonly SessionEvent[], options?: SessionHandleAppendOptions): Promise<void>;
170
- flush(options?: SessionHandleFlushOptions): Promise<void>;
171
- close(): Promise<void>;
172
- [Symbol.asyncDispose](): Promise<void>;
173
- /** live 路由:缓冲一个已发布事件(持久化自有副本),并启动批量窗口。 */
174
- enqueueLive(event: SessionEvent, reportBackgroundFailure: (error: unknown) => void): void;
175
- /** rewind 截断后对齐 handle 的稠密 cursor 与继承前缀(DB 已截断)。 */
176
- resetAfterRewind(cursor: number, inheritedEventCount?: number): void;
177
- /** 排空 live 缓冲(过滤 delta + 重编号稠密 + append)。 */
178
- drainLive(): Promise<void>;
179
- private drainBuffered;
180
- private enqueueChain;
181
- private run;
182
- private assertOpen;
183
- static readonly LIVE_WRITE_BATCH_MAX_DELAY_MS = 200;
184
- }
185
- declare class SessionPersistenceRdb extends SessionPersistence {
186
- config: Config;
187
- static inject: string[];
188
- static Config: z<Config>;
189
- static readonly settingsNs = "session-rdb";
190
- readonly name = "session-rdb";
191
- readonly tracker: RdbBackendTracker;
192
- private readonly backend;
193
- private storeIdentity;
194
- private readonly ready;
195
- private readonly writeGuard;
196
- private readonly reuseEventIds;
197
- /** live 路由:session/created 后 handle 就绪前的缓冲。 */
198
- private readonly liveBuffers;
199
- private readonly liveReady;
200
- constructor(ctx: Context, config: Config, injectedBackend?: Backend);
201
- private init;
202
- create(header: SessionHeader, options?: SessionPersistenceCreateOptions): Promise<SessionHandle>;
203
- open(id: SessionId, access: SessionAccess, options?: SessionPersistenceOpenOptions): Promise<SessionHandle>;
204
- flush(): Promise<void>;
205
- stat(id: SessionId, options?: SessionPersistenceStatOptions): Promise<SessionPersistenceSnapshot | undefined>;
206
- list(options?: SessionPersistenceListOptions): Promise<readonly SessionPersistenceSnapshot[]>;
207
- /** 导出当前世代的 jsonl artifact,视图只读不落库。 */
208
- readRaw(id: SessionId, signal?: AbortSignal): Promise<{
209
- meta: SessionHeader;
210
- inheritedEventCount: number;
211
- filename: string;
212
- content: string;
213
- } | undefined>;
214
- /** 空会话 materialize(flush 的持久化屏障)。 */
215
- materializeEmpty(meta: SessionHeader, inheritedEventCount: SessionLogOffset): Promise<void>;
216
- /** 原样 append 落库(handle 已校验 contiguity;torn tail 先截断)。
217
- * 与上游 JSONL 一致:ignorable 事件原样存储,不做过滤。写路径校验当前
218
- * 格式形状(fail-closed):未知类型(非 ignorable)与非法消息形状拒绝入库;
219
- * 非当前格式(v0/v1/v2)数据只在读取时经 legacy 转换链动态转换,不落新库。 */
220
- appendBatch(meta: SessionHeader, inheritedEventCount: SessionLogOffset, events: readonly SessionEvent[], tornTruncateTo?: number): Promise<boolean>;
221
- /** 读取一个会话的稠密 log(含 torn tail 检测,不含修复改写)。 */
222
- readLog(id: SessionId, options?: {
223
- fromSeq?: number;
224
- }, signal?: AbortSignal): Promise<{
225
- meta: SessionHeader;
226
- inheritedEventCount: number;
227
- events: SessionEvent[];
228
- tornFrom?: number;
229
- incarnation: string;
230
- revision: number;
231
- /** 存储桥接行数(迁移链可能生成/合并事件,与 `events.length` 不同)。 */
232
- storedCount: number;
233
- /** 是否经上游迁移链转换(非当前格式 → 当前格式)。 */
234
- migrated: boolean;
235
- } | undefined>;
236
- /**
237
- * 把迁移链读出的当前格式视图整体落库(非当前格式会话写打开时的一次性迁移)。
238
- *
239
- * 迁移链会生成/合并事件(end-seed / attempt / chunk 合并),事件 seq 空间
240
- * 与存储桥接行数不再相等;写路径以存储 head 为锚点重编号,二者不一致会让
241
- * append 撞上已有行。这里在同一事务内删光本会话桥接行、按迁移视图重建
242
- * (新事件行,完整信封),并更新 head 与 revision;旧事件行保留(可能被
243
- * fork 子会话引用,孤儿由惰性 GC 处理)。
244
- */
245
- private rewriteMigratedLog;
246
- listSnapshots(signal?: AbortSignal): Promise<Array<SessionPersistenceSnapshot & {
247
- inheritedEventCount: number;
248
- }>>;
249
- readStoredRevision(id: SessionId, signal?: AbortSignal): Promise<SessionPersistenceRevision | undefined>;
250
- /** 便捷:create + append + close(测试与导入路径共用)。 */
251
- createAndAppend(header: SessionHeader, events: readonly SessionEvent[], inheritedEventCount?: number): Promise<void>;
252
- /** 便捷:open(read) + read 全量 + close。 */
253
- load(id: SessionId, signal?: AbortSignal): Promise<import("@deepseek-ai/dsh-session-persistence").SessionInspection>;
254
- /** 便捷:open(write) + append + close。 */
255
- append(id: SessionId, events: readonly SessionEvent[]): Promise<void>;
256
- /** 便捷:readFrom(稠密后缀)。 */
257
- readFrom(id: SessionId, fromSeq: number, signal?: AbortSignal): Promise<{
258
- meta: SessionHeader;
259
- inheritedEventCount: number;
260
- events: readonly SessionEvent[];
261
- }>;
262
- close(): Promise<void>;
263
- registerReuseEventIds(childId: SessionId, map: ReadonlyMap<number, string>): void;
264
- internals(): SessionPersistenceRdbInternals;
265
- private rowRevision;
266
- private installLiveRouting;
267
- /** session/created 后为 live 会话建立 write handle(create 或 adopt)。 */
268
- private ensureLiveHandle;
269
- }
270
- //#endregion
271
- export { SessionBranchRdb as a, truncateLiveSession as c, SessionPersistenceRdbInternals as i, ProjectionCacheOptions as n, SessionBranchRdbProvider as o, SessionPersistenceRdb as r, locateTurnEnd as s, Config as t };