@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.
Files changed (143) hide show
  1. package/README.md +24 -3
  2. package/dist/artifact.d.mts +118 -3
  3. package/dist/artifact.mjs +5 -3
  4. package/dist/{log-C0q5svm7.d.mts → backend-BKtJ5sO5.d.mts} +189 -291
  5. package/dist/backend-rdb-BDCntVDy.mjs +1341 -0
  6. package/dist/branch-CG1PUlO6.mjs +833 -0
  7. package/dist/deletion-SSX7IqU8.mjs +35 -0
  8. package/dist/deletion.d.mts +1 -3
  9. package/dist/deletion.mjs +2 -2
  10. package/dist/export-CfJQjQfr.mjs +123 -0
  11. package/dist/export.d.mts +1 -3
  12. package/dist/export.mjs +32 -57
  13. package/dist/import.d.mts +1 -3
  14. package/dist/import.mjs +32 -78
  15. package/dist/index-CUD2ZpTA.d.mts +71 -0
  16. package/dist/index.d.mts +20 -2
  17. package/dist/index.mjs +1562 -5
  18. package/dist/open-YoyD_kah.mjs +561 -0
  19. package/dist/organize.d.mts +9 -0
  20. package/dist/organize.mjs +30 -0
  21. package/dist/route-DpfY0zq-.mjs +114 -0
  22. package/dist/storage.d.mts +41 -50
  23. package/dist/storage.mjs +3 -1
  24. package/dist/testing.d.mts +9 -5
  25. package/dist/testing.mjs +27 -8
  26. package/dist/usage-row-BQQM-_t6.mjs +36 -0
  27. package/dist/usage.d.mts +1 -1
  28. package/dist/usage.mjs +8 -42
  29. package/dist/write-guard-CYrY58GM.mjs +174 -0
  30. package/package.json +32 -36
  31. package/src/artifact.ts +4 -1
  32. package/src/backend-rdb.ts +331 -0
  33. package/src/backend.ts +32 -15
  34. package/src/{branch.ts → branch/index.ts} +125 -112
  35. package/src/branch/port.ts +62 -0
  36. package/src/branch/semantics.ts +69 -0
  37. package/src/db/open.ts +180 -0
  38. package/src/db/stats.ts +449 -0
  39. package/src/deletion-error.ts +17 -0
  40. package/src/deletion.ts +19 -72
  41. package/src/entities/index.ts +16 -1
  42. package/src/entities/v3/event-usage.ts +3 -3
  43. package/src/entities/v3/events.ts +3 -3
  44. package/src/entities/v3/index.ts +4 -16
  45. package/src/entities/v3/persistence-state.ts +3 -3
  46. package/src/entities/v3/schema-meta.ts +3 -3
  47. package/src/entities/v3/session-counts.ts +3 -3
  48. package/src/entities/v3/session-events.ts +3 -3
  49. package/src/entities/v3/session-projcache-rows.ts +3 -3
  50. package/src/entities/v3/session-usage.ts +3 -3
  51. package/src/entities/v3/sessions.ts +3 -3
  52. package/src/entities/v3/storage-records.ts +17 -0
  53. package/src/entities/v3/storage-units.ts +3 -3
  54. package/src/{schema.ts → event-shape.ts} +0 -46
  55. package/src/export.ts +32 -78
  56. package/src/handles.ts +349 -0
  57. package/src/http/route.ts +178 -0
  58. package/src/import-storages.ts +5 -2
  59. package/src/import.ts +63 -125
  60. package/src/index.ts +15 -1630
  61. package/src/legacy.ts +3 -1
  62. package/src/live-routing.ts +205 -0
  63. package/src/log/export.ts +84 -0
  64. package/src/{log.ts → log/repair.ts} +1 -272
  65. package/src/log/rows.ts +107 -0
  66. package/src/log/usage-row.ts +64 -0
  67. package/src/migrations.ts +196 -0
  68. package/src/organize.ts +38 -0
  69. package/src/read-path.ts +190 -0
  70. package/src/routes.ts +2 -1
  71. package/src/rows.ts +25 -77
  72. package/src/session-ops.ts +444 -0
  73. package/src/session-query.ts +17 -4
  74. package/src/session-store.ts +675 -0
  75. package/src/stats-fold.ts +44 -0
  76. package/src/storage-takeover/index.ts +2 -5
  77. package/src/storage-takeover/legacy-domain-migration.ts +118 -0
  78. package/src/storage-takeover/projection-cache.ts +139 -77
  79. package/src/storage-takeover/read-model.ts +189 -0
  80. package/src/storage-takeover/repository.ts +238 -425
  81. package/src/storage-takeover/storage-backend.ts +47 -149
  82. package/src/storage-takeover/types.ts +45 -14
  83. package/src/storage-takeover/workspace-documents.ts +65 -0
  84. package/src/storage.ts +4 -2
  85. package/src/testing.ts +21 -1
  86. package/src/usage.ts +11 -65
  87. package/src/version.ts +13 -0
  88. package/src/write-path.ts +286 -0
  89. package/dist/branch-DbFW3rJX.mjs +0 -391
  90. package/dist/deletion-ODZFcbZm.mjs +0 -2218
  91. package/dist/gc.d.mts +0 -6
  92. package/dist/gc.mjs +0 -49
  93. package/dist/log-DYxdjJ-l.mjs +0 -562
  94. package/dist/routes-DGBlBE_I.mjs +0 -10
  95. package/dist/sqlite-DjOnMRh2.mjs +0 -1777
  96. package/drizzle/postgres/20260908072805_v2_initial/migration.sql +0 -58
  97. package/drizzle/postgres/20260908072805_v2_initial/snapshot.json +0 -686
  98. package/drizzle/postgres/20260908072806_v3_drop_original_seq/migration.sql +0 -1
  99. package/drizzle/postgres/20260908072806_v3_drop_original_seq/snapshot.json +0 -673
  100. package/drizzle/postgres/20260910120001_v3_storage_tables/migration.sql +0 -65
  101. package/drizzle/postgres/20260910120001_v3_storage_tables/snapshot.json +0 -1183
  102. package/drizzle/postgres/20260910130001_v3_session_title_backfill/migration.sql +0 -10
  103. package/drizzle/postgres/20260910130001_v3_session_title_backfill/snapshot.json +0 -1183
  104. package/drizzle/postgres/20260918120000_v3_event_usage/migration.sql +0 -14
  105. package/drizzle/postgres/20260918120000_v3_event_usage/snapshot.json +0 -1309
  106. package/drizzle/postgres/20260921075900_v3_agent_preset/migration.sql +0 -1
  107. package/drizzle/postgres/20260922090001_v3_pinned_seq/migration.sql +0 -2
  108. package/drizzle/postgres/20260922093001_v3_last_event_at/migration.sql +0 -10
  109. package/drizzle/postgres/20260923120000_v3_usage_materialized/migration.sql +0 -48
  110. package/drizzle/postgres/20261008103001_v3_schedule_tasks/migration.sql +0 -11
  111. package/drizzle/sqlite/20260908072751_v2_initial/migration.sql +0 -53
  112. package/drizzle/sqlite/20260908072751_v2_initial/snapshot.json +0 -492
  113. package/drizzle/sqlite/20260908072752_v3_drop_original_seq/migration.sql +0 -6
  114. package/drizzle/sqlite/20260908072752_v3_drop_original_seq/snapshot.json +0 -513
  115. package/drizzle/sqlite/20260910120000_v3_storage_tables/migration.sql +0 -66
  116. package/drizzle/sqlite/20260910120000_v3_storage_tables/snapshot.json +0 -905
  117. package/drizzle/sqlite/20260910130000_v3_session_title_backfill/migration.sql +0 -13
  118. package/drizzle/sqlite/20260910130000_v3_session_title_backfill/snapshot.json +0 -905
  119. package/drizzle/sqlite/20260918120000_v3_event_usage/migration.sql +0 -14
  120. package/drizzle/sqlite/20260918120000_v3_event_usage/snapshot.json +0 -1031
  121. package/drizzle/sqlite/20260921075855_v3_agent_preset/migration.sql +0 -7
  122. package/drizzle/sqlite/20260921075855_v3_agent_preset/snapshot.json +0 -1041
  123. package/drizzle/sqlite/20260922090000_v3_pinned_seq/migration.sql +0 -4
  124. package/drizzle/sqlite/20260922093000_v3_last_event_at/migration.sql +0 -13
  125. package/drizzle/sqlite/20260923120000_v3_usage_materialized/migration.sql +0 -48
  126. package/drizzle/sqlite/20261008103000_v3_schedule_tasks/migration.sql +0 -14
  127. package/src/adapters/index.ts +0 -12
  128. package/src/adapters/to-postgres.ts +0 -93
  129. package/src/adapters/to-sqlite.ts +0 -83
  130. package/src/adapters/types.ts +0 -44
  131. package/src/drizzle/postgres-v2.ts +0 -10
  132. package/src/drizzle/postgres-v3.ts +0 -18
  133. package/src/drizzle/sqlite-v2.ts +0 -9
  134. package/src/drizzle/sqlite-v3.ts +0 -18
  135. package/src/entities/v2/index.ts +0 -20
  136. package/src/entities/v2/session-events.ts +0 -10
  137. package/src/entities/v3/schedule-tasks.ts +0 -16
  138. package/src/entities/v3/workspace-sessions.ts +0 -19
  139. package/src/entities/v3/workspace-state.ts +0 -12
  140. package/src/entities/v3/workspaces.ts +0 -14
  141. package/src/gc.ts +0 -77
  142. package/src/postgres.ts +0 -1015
  143. package/src/sqlite.ts +0 -1086
@@ -1,212 +1,86 @@
1
- import { Session, SessionEvent, SessionHeader, SessionId, SessionLogOffset } from "@deepseek-ai/dsh-session";
1
+ import { Database } from "@morlay/rdbkit";
2
+ import { SessionEvent, SessionHeader, SessionId, SessionLogOffset } from "@deepseek-ai/dsh-session";
2
3
  import { SessionAccess, SessionHandle, SessionHandleAppendOptions, SessionHandleFlushOptions, SessionHandleReadOptions, SessionHandleReadResult, SessionPersistence, SessionPersistenceCreateOptions, SessionPersistenceListOptions, SessionPersistenceOpenOptions, SessionPersistenceRevision, SessionPersistenceSnapshot, SessionPersistenceStatOptions, SessionStorageMetadata } from "@deepseek-ai/dsh-session-persistence";
3
- import { ScheduleTask } from "@deepseek-ai/dsh-schedule";
4
+ import "@deepseek-ai/dsh-schedule";
4
5
  import { CheckpointIdentity } from "@deepseek-ai/dsh-session-projection-cache";
5
6
  import { ProjectionCheckpoint } from "@deepseek-ai/dsh-session-projection";
6
- import { WorkspaceDomainState, WorkspaceRecord } from "@deepseek-ai/dsh-workspace";
7
+ import "@deepseek-ai/dsh-workspace";
7
8
  import { Context } from "@deepseek-ai/cordis";
8
9
  import { Volatile } from "@deepseek-ai/cosmokit";
9
10
  import z from "@deepseek-ai/schemastery";
10
- import { BranchAnchorMode, BranchBoundary, ForkFromOptions, SessionBranch, SessionBranchProvider } from "@morlay/session-branch";
11
- //#region src/storage-takeover/types.d.ts
11
+ interface EventUsageRow {
12
+ fEventId: string;
13
+ fCreatedAt: number;
14
+ fDay: string;
15
+ fProvider: string | null;
16
+ fModel: string | null;
17
+ fReferenced: number;
18
+ fSubagent: number;
19
+ fInputTokens: number;
20
+ fOutputTokens: number;
21
+ fCacheReadTokens: number;
22
+ fReasoningTokens: number;
23
+ fTotalTokens: number;
24
+ }
25
+ declare function usageRowOf(event: {
26
+ fEventId: string;
27
+ fCreatedAt: number;
28
+ fType: string;
29
+ fData: string;
30
+ }, subagent: boolean): EventUsageRow | undefined;
31
+ interface DomainRecord {
32
+ table: string;
33
+ key: string;
34
+ value: unknown;
35
+ }
36
+ interface WorkspaceIndexEntry {
37
+ id: string;
38
+ title: string;
39
+ sessionIds: string[];
40
+ }
12
41
  interface StoredProjcacheEntry {
13
42
  sessionId: SessionId;
14
43
  identity: CheckpointIdentity;
15
44
  rows: ProjectionCheckpoint;
45
+ /** 会话标题(随快照一起加载 / 写路径刷新;`cachedSnapshot` 缺标题时用它补,不回库直读)。 */
46
+ title?: {
47
+ title: string;
48
+ seq: number;
49
+ };
16
50
  }
17
51
  interface StorageRepository {
18
52
  readUnitVersion(name: string): Promise<number | undefined>;
19
53
  insertUnitVersion(name: string, version: number): Promise<void>;
20
- listWorkspaces(): Promise<Array<{
21
- id: string;
22
- record: WorkspaceRecord;
23
- }>>;
24
- putWorkspace(id: string, record: WorkspaceRecord): Promise<void>;
25
- deleteWorkspace(id: string): Promise<void>;
26
- readWorkspaceState(): Promise<WorkspaceDomainState | null>;
27
- writeWorkspaceState(state: WorkspaceDomainState): Promise<void>;
28
- listScheduleTasks(): Promise<Array<{
29
- id: string;
30
- task: ScheduleTask;
31
- }>>;
32
- putScheduleTask(id: string, task: ScheduleTask): Promise<void>;
33
- deleteScheduleTask(id: string): Promise<void>;
54
+ listDomainRecords(domain: string): Promise<DomainRecord[]>;
55
+ readDomainGlobal(domain: string): Promise<unknown>;
56
+ putDomainRecord(domain: string, table: string, key: string, value: unknown): Promise<void>;
57
+ deleteDomainRecord(domain: string, table: string, key: string): Promise<void>;
58
+ deleteDomainRecords(domain: string, table: string, keys: readonly string[]): Promise<void>;
59
+ writeDomainGlobal(domain: string, value: unknown): Promise<void>;
60
+ listWorkspaceIndex(): Promise<WorkspaceIndexEntry[]>;
34
61
  loadProjcache(): Promise<StoredProjcacheEntry[]>;
35
- readProjcacheDirect?(sessionId: string): StoredProjcacheEntry | undefined;
36
- readSessionTitleDirect?(sessionId: string): {
62
+ /** 会话标题(投影缓存的镜像刷新与 `cachedSnapshot` 补标题用)。 */
63
+ readSessionTitle(sessionId: string): Promise<{
37
64
  title: string;
38
65
  seq: number;
39
- } | undefined;
66
+ } | undefined>;
40
67
  putProjcache(sessionId: string, rows: ProjectionCheckpoint): Promise<void>;
68
+ /** 增量写回:只 upsert `changed` 里的行、删掉 `removedKeys`,其余行原样不动。 */
69
+ patchProjcache(sessionId: string, changed: ProjectionCheckpoint, removedKeys: readonly string[]): Promise<void>;
41
70
  pruneStaleProjcache(): Promise<number>;
42
71
  }
43
- //#endregion
44
- //#region src/routes.d.ts
45
72
  declare const SESSION_ROWS_PATH = "/api/morlay/v1/session/rows";
46
73
  declare const SESSION_EXPORT_PATH = "/api/morlay/v1/session/export";
47
74
  declare const SESSION_IMPORT_PATH = "/api/morlay/v1/session/import";
48
75
  declare const SESSION_DELETE_PATH = "/api/morlay/v1/session/delete";
49
- declare const SESSION_GC_PATH = "/api/morlay/v1/session/gc";
76
+ declare const SESSION_ORGANIZE_PATH = "/api/morlay/v1/session/organize";
50
77
  declare const SESSION_USAGE_PATH = "/api/morlay/v1/session/usage";
51
- //#endregion
52
- //#region src/write-guard.d.ts
53
78
  declare class WriteGuard {
54
79
  private readonly headSeqs;
55
80
  confirmHead(id: SessionId, head: number): void;
56
81
  has(id: SessionId): boolean;
57
82
  assertNoConcurrentWriter(id: SessionId, storedHead: number): void;
58
83
  }
59
- //#endregion
60
- //#region src/schema.d.ts
61
- declare const SCHEMA_VERSION = 3;
62
- declare const SESSION_PERSISTENCE_SQLITE_APPLICATION_ID = 1146308688;
63
- declare const EVENT_ENCODING = "json";
64
- declare const tPersistenceState: any;
65
- declare const tSchemaMeta: any;
66
- declare const tSessions: any;
67
- declare const tEvents: any;
68
- declare const tSessionEvents: any;
69
- declare const tStorageUnits: any;
70
- declare const tWorkspaces: any;
71
- declare const tWorkspaceSessions: any;
72
- declare const tWorkspaceState: any;
73
- declare const tScheduleTasks: any;
74
- declare const tSessionProjcacheRows: any;
75
- declare const tEventUsage: any;
76
- declare const tSessionUsage: any;
77
- declare const tSessionCounts: any;
78
- type JournalMode = "wal" | "delete" | "truncate" | "persist";
79
- declare const DEFAULT_BUSY_TIMEOUT_MS = 5000;
80
- type EventKind = "message" | "thinking" | "turn" | "tool" | "request" | "config" | "audit" | "lifecycle" | "inbox" | "compaction" | "llm" | "subagent" | "team" | "workflow" | "goal" | "schedule" | "todo" | "web";
81
- type EventRole = "user" | "assistant" | "tool";
82
- declare function eventKind(event: {
83
- type: string;
84
- data?: unknown;
85
- }): EventKind | "";
86
- declare function eventDimensions(event: SessionEvent): {
87
- kind: string;
88
- role: string;
89
- name: string;
90
- actionId: string;
91
- };
92
- //#endregion
93
- //#region src/rows.d.ts
94
- interface SessionRowsItem {
95
- sessionId: string;
96
- title: string | null;
97
- origin: string | null;
98
- cwd: string | null;
99
- createdAt: number;
100
- updatedAt: number;
101
- archived: boolean;
102
- workspace: string | null;
103
- }
104
- interface SessionRowsValue {
105
- items: SessionRowsItem[];
106
- total: number;
107
- page: number;
108
- pageSize: number;
109
- }
110
- //#endregion
111
- //#region src/branch.d.ts
112
- declare function locateTurnEnd(events: readonly SessionEvent[], atSeq?: number, mode?: BranchAnchorMode): number;
113
- interface LiveSessionHooks {
114
- getSession(id: SessionId): Session | undefined;
115
- getAgent(id: SessionId): LiveAgentLike | undefined;
116
- flush(session: Session): Promise<boolean>;
117
- warn?(message: string): void;
118
- resetProjections?(session: Session): void;
119
- resetTokenMeter?(session: Session): void;
120
- refreshProjectionCache?(session: ProjectionCacheSession): Promise<void>;
121
- }
122
- interface ProjectionCacheSession {
123
- readonly id: SessionId;
124
- readonly header: SessionHeader;
125
- readonly inheritedEventCount: SessionLogOffset;
126
- readonly headSeq?: number;
127
- snapshotEvents(): readonly SessionEvent[];
128
- }
129
- interface LiveAgentLike {
130
- session: Session;
131
- requestHeaderLogged?: boolean;
132
- inbox?: {
133
- clear(): void;
134
- };
135
- }
136
- declare function truncateLiveSession(session: Session, newLength: number): void;
137
- declare class SessionBranchRdbProvider implements SessionBranchProvider {
138
- private readonly persistence;
139
- private readonly live;
140
- readonly name = "session-rdb";
141
- constructor(persistence: SessionPersistenceRdb, live?: LiveSessionHooks);
142
- readBranchPrefix(id: SessionId, atSeq?: number, mode?: BranchAnchorMode, signal?: AbortSignal): Promise<BranchBoundary>;
143
- readRawEvents(id: SessionId, signal?: AbortSignal): Promise<{
144
- meta: SessionHeader;
145
- events: readonly SessionEvent[];
146
- }>;
147
- forkFrom(sourceId: SessionId, options?: ForkFromOptions, signal?: AbortSignal): Promise<SessionId>;
148
- rewind(id: SessionId, toBoundary: number, signal?: AbortSignal): Promise<SessionPersistenceSnapshot>;
149
- private rewindKeepLength;
150
- private refreshProjectionCache;
151
- }
152
- declare class SessionBranchRdb extends SessionBranch {
153
- static inject: string[];
154
- private readonly warned;
155
- constructor(ctx: import("@deepseek-ai/cordis").Context);
156
- resetLiveDerivedState(session: Session): void;
157
- private warnOnce;
158
- private resetProjectionCells;
159
- private resetTokenMeterFold;
160
- private readonly provider;
161
- readBranchPrefix(id: SessionId, atSeq?: number, mode?: BranchAnchorMode, signal?: AbortSignal): Promise<BranchBoundary>;
162
- readRawEvents(id: SessionId, signal?: AbortSignal): Promise<{
163
- meta: SessionHeader;
164
- events: readonly SessionEvent[];
165
- }>;
166
- forkFrom(sourceId: SessionId, options?: ForkFromOptions, signal?: AbortSignal): Promise<SessionId>;
167
- rewind(id: SessionId, toBoundary: number, signal?: AbortSignal): Promise<SessionPersistenceSnapshot>;
168
- }
169
- //#endregion
170
- //#region src/index.d.ts
171
- interface SessionPersistenceRdbInternals {
172
- readonly backend: Backend;
173
- readonly writeGuard: WriteGuard;
174
- create(meta: SessionHeader, inheritedEventCount?: number): Promise<SessionHandle>;
175
- append(id: SessionId, events: readonly SessionEvent[]): Promise<void>;
176
- load(id: SessionId): Promise<import("@deepseek-ai/dsh-session-persistence").SessionInspection>;
177
- inspect(id: SessionId, signal?: AbortSignal): Promise<import("@deepseek-ai/dsh-session-persistence").SessionInspection>;
178
- readFrom(id: SessionId, fromSeq: number, signal?: AbortSignal): Promise<{
179
- meta: SessionHeader;
180
- inheritedEventCount: number;
181
- events: readonly SessionEvent[];
182
- }>;
183
- listSnapshots(signal?: AbortSignal): Promise<SessionPersistenceSnapshot[]>;
184
- readStoredRevision(id: SessionId, signal?: AbortSignal): Promise<import("@deepseek-ai/dsh-session-persistence").SessionPersistenceRevision | undefined>;
185
- registerReuseEventIds(childId: SessionId, map: ReadonlyMap<number, string>): void;
186
- dropReuseEventIds(childId: SessionId): void;
187
- }
188
- interface ProjectionCacheOptions {
189
- writeEveryEvents?: number;
190
- writeIntervalMs?: number;
191
- }
192
- type Config = {
193
- type: "sqlite";
194
- path: string;
195
- journalMode?: JournalMode | undefined;
196
- busyTimeout?: number | undefined;
197
- projectionCache?: ProjectionCacheOptions | undefined;
198
- } | {
199
- type: "postgres";
200
- connectionString: string;
201
- schema?: string | undefined;
202
- projectionCache?: ProjectionCacheOptions | undefined;
203
- };
204
- type ResolvedConfig = Volatile<Config>;
205
- type SessionDeletionErrorCode = "SESSION_NOT_FOUND" | "SESSION_NOT_ARCHIVED" | "SESSION_LIVE";
206
- declare class SessionDeletionError extends Error {
207
- readonly code: SessionDeletionErrorCode;
208
- constructor(message: string, code: SessionDeletionErrorCode);
209
- }
210
84
  interface PendingSession {
211
85
  readonly header: SessionHeader;
212
86
  readonly revision: SessionPersistenceRevision;
@@ -274,20 +148,99 @@ declare class RdbSessionHandle implements SessionHandle {
274
148
  private assertOpen;
275
149
  static readonly LIVE_WRITE_BATCH_MAX_DELAY_MS = 200;
276
150
  }
151
+ /** 库结构版本(写入 SQLite 的 `user_version`,不匹配即拒绝打开)。 */
152
+ declare const SCHEMA_VERSION = 3;
153
+ /** SQLite 文件头的 application id(`0x44534850` = "DSHP"),防止误开别的库。 */
154
+ declare const SESSION_PERSISTENCE_SQLITE_APPLICATION_ID = 1146308688;
155
+ /** 事件 `f_data` 的编码(目前只有 JSON)。 */
156
+ declare const EVENT_ENCODING = "json";
157
+ type JournalMode = "wal" | "delete" | "truncate" | "persist";
158
+ /** SQLite 的默认 busy timeout(毫秒)。 */
159
+ declare const DEFAULT_BUSY_TIMEOUT_MS = 5000;
160
+ /** 一份读出来的日志(分支只看这些字段)。 */
161
+ interface BranchLog {
162
+ meta: SessionHeader;
163
+ inheritedEventCount: number;
164
+ events: SessionEvent[];
165
+ tornFrom?: number;
166
+ incarnation: string;
167
+ revision: number;
168
+ storedCount: number;
169
+ migrated: boolean;
170
+ }
171
+ /**
172
+ * 分支能力需要的持久化面(端口)。
173
+ *
174
+ * 为什么不是整个 `SessionPersistenceRdb`:分支只需要「读日志前缀、写截断、认领复用映射」这几件事,
175
+ * 端口里的每个动词都有真实调用方(不是把服务的方法表转抄一遍)。rewind 走直连快路径,
176
+ * 所以这里保留了行级的 `eventRows` / `sessionRow` / `transaction`,而不是只给一个 `readLog`。
177
+ */
178
+ interface BranchPort {
179
+ readonly ready: Promise<void>;
180
+ readLog(id: SessionId, options?: {
181
+ fromSeq?: number;
182
+ }, signal?: AbortSignal): Promise<BranchLog | undefined>;
183
+ create(meta: SessionHeader, options?: {
184
+ inheritedEventCount?: SessionLogOffset;
185
+ }): Promise<SessionHandle>;
186
+ eventRows(id: SessionId, fromSequence?: number): Promise<EventRow[]>;
187
+ eventTypesBefore(id: SessionId, beforeSequence: number, limit: number): Promise<Array<Pick<EventRow, "fSequence" | "fType">>>;
188
+ sessionRow(id: SessionId): Promise<SessionRow | undefined>;
189
+ eventTypeAt(id: SessionId, sequence: number): Promise<string | undefined>;
190
+ transaction<T>(fn: (tx: BackendTx) => Promise<T>): Promise<T>;
191
+ /** head 回退与 revision bump 是同一次行写,确认位点后写路径才允许继续 append。 */
192
+ confirmHead(id: SessionId, sequence: number): void;
193
+ readStoredRevision(id: SessionId): Promise<SessionPersistenceRevision | undefined>;
194
+ /** fork 复用父会话的事件行:子会话落写时按 seq 取回父行 id。 */
195
+ registerReuseEventIds(childId: SessionId, map: ReadonlyMap<number, string>): void;
196
+ dropReuseEventIds(childId: SessionId): void;
197
+ /** rewind 截断后把活跃写句柄的游标与 seed 长度挪到新水位。 */
198
+ resetWriterAfterRewind(id: SessionId, cursor: number, inheritedEventCount?: number): void;
199
+ }
200
+ /**
201
+ * 会话删除的失败语义(叶子模块)。
202
+ *
203
+ * 放成叶子是为了断开 `session-store ↔ deletion` 的运行时环:HTTP 路由只依赖这个错误类型,不再值引入整个服务。
204
+ 现在 `deletion.ts` 对它只剩 `import type`,运行时环为 0。
205
+ */
206
+ type SessionDeletionErrorCode = "SESSION_NOT_FOUND" | "SESSION_NOT_ARCHIVED" | "SESSION_LIVE";
207
+ declare class SessionDeletionError extends Error {
208
+ readonly code: SessionDeletionErrorCode;
209
+ constructor(message: string, code: SessionDeletionErrorCode);
210
+ }
211
+ type Config = {
212
+ type: "sqlite";
213
+ path: string;
214
+ journalMode?: JournalMode | undefined;
215
+ busyTimeout?: number | undefined;
216
+ preparedSessionCacheSize?: number | undefined;
217
+ } | {
218
+ type: "postgres";
219
+ connectionString: string;
220
+ schema?: string | undefined;
221
+ preparedSessionCacheSize?: number | undefined;
222
+ };
223
+ type ResolvedConfig = Volatile<Config>;
277
224
  declare class SessionPersistenceRdb extends SessionPersistence {
278
225
  static inject: string[];
279
226
  static Config: z<Config, ResolvedConfig>;
280
227
  readonly name = "session-rdb";
281
228
  readonly tracker: RdbBackendTracker;
282
- private readonly backend;
229
+ /** 打开后的后端;未打开时访问会 fail loud(装配内部、分支端口与测试共用)。 */
230
+ backend: Backend;
283
231
  private storeIdentity;
284
- private readonly ready;
285
- private readonly writeGuard;
232
+ /** 后端打开完成(分支端口与 live 路由都先等它)。 */
233
+ readonly ready: Promise<void>;
234
+ /** 写者位点守卫(分支的 rewind 与写路径共用)。 */
235
+ readonly writeGuard: WriteGuard;
236
+ private readonly live;
237
+ private readonly readPath;
238
+ private readonly writePath;
286
239
  private readonly reuseEventIds;
287
- private readonly liveBuffers;
288
- private readonly liveReady;
289
240
  readonly config: Config;
290
- constructor(ctx: Context, config: ResolvedConfig, injectedBackend?: Backend);
241
+ constructor(ctx: Context, config: ResolvedConfig);
242
+ /** 打开完成后的 backend(所有 DB 操作的门)。 */
243
+ private database;
291
244
  private init;
292
245
  create(header: SessionHeader, options?: SessionPersistenceCreateOptions): Promise<SessionHandle>;
293
246
  open(id: SessionId, access: SessionAccess, options?: SessionPersistenceOpenOptions): Promise<SessionHandle>;
@@ -295,19 +248,29 @@ declare class SessionPersistenceRdb extends SessionPersistence {
295
248
  stat(id: SessionId, options?: SessionPersistenceStatOptions): Promise<SessionPersistenceSnapshot | undefined>;
296
249
  list(options?: SessionPersistenceListOptions): Promise<readonly SessionPersistenceSnapshot[]>;
297
250
  deleteSession(id: SessionId, signal?: AbortSignal): Promise<void>;
251
+ /**
252
+ * 整理数据:回收孤儿会话与孤儿事件行,然后**整表重算**三张派生统计表,最后 VACUUM。
253
+ *
254
+ * 顺序有依赖:先清孤儿(被删会话独占的事件行才刚成为孤儿),再按事件表重算统计,
255
+ * 否则统计会把刚删掉的引用算回去。
256
+ * @returns 三项计数(孤儿会话 / 孤儿事件行 / 重建涉及的会话数)。
257
+ */
258
+ organize(): Promise<{
259
+ orphanSessions: number;
260
+ orphanEvents: number;
261
+ rebuiltSessions: number;
262
+ }>;
298
263
  collectOrphans(): Promise<number>;
299
264
  collectOrphanSessions(): Promise<number>;
300
265
  usageReport(sinceMs?: number): Promise<UsageAggregate>;
301
266
  vacuum(): Promise<void>;
267
+ /**
268
+ * 已归档的会话 id(`t_sessions.f_archived_at` 非空)。`session/list` 默认过滤掉它们。
269
+ * 读的是持久化里的归档标记,不依赖 `workspaceRegistry` 服务的就绪时机。
270
+ * @returns 归档过的 Session id 集。
271
+ */
272
+ archivedSessionIds(): Promise<ReadonlySet<string>>;
302
273
  private unarchiveBeforeDeletion;
303
- readRaw(id: SessionId, signal?: AbortSignal): Promise<{
304
- meta: SessionHeader;
305
- inheritedEventCount: number;
306
- filename: string;
307
- content: string;
308
- } | undefined>;
309
- materializeEmpty(meta: SessionHeader, inheritedEventCount: SessionLogOffset): Promise<void>;
310
- appendBatch(meta: SessionHeader, inheritedEventCount: SessionLogOffset, events: readonly SessionEvent[], tornTruncateTo?: number): Promise<boolean>;
311
274
  readLog(id: SessionId, options?: {
312
275
  fromSeq?: number;
313
276
  }, signal?: AbortSignal): Promise<{
@@ -320,15 +283,19 @@ declare class SessionPersistenceRdb extends SessionPersistence {
320
283
  storedCount: number;
321
284
  migrated: boolean;
322
285
  } | undefined>;
323
- private recordSessionStats;
324
- rebuildSessionStats(id: SessionId): Promise<void>;
325
- private refreshEventUsageFlags;
326
- private dropSessionStats;
327
- private rewriteMigratedLog;
328
- listSnapshots(signal?: AbortSignal): Promise<Array<SessionPersistenceSnapshot & {
286
+ readRaw(id: SessionId, signal?: AbortSignal): Promise<{
287
+ meta: SessionHeader;
329
288
  inheritedEventCount: number;
330
- }>>;
289
+ filename: string;
290
+ content: string;
291
+ } | undefined>;
292
+ listSnapshots(signal?: AbortSignal): Promise<(SessionPersistenceSnapshot & {
293
+ inheritedEventCount: number;
294
+ })[]>;
331
295
  readStoredRevision(id: SessionId, signal?: AbortSignal): Promise<SessionPersistenceRevision | undefined>;
296
+ materializeEmpty(meta: SessionHeader, inheritedEventCount: SessionLogOffset): Promise<void>;
297
+ appendBatch(meta: SessionHeader, inheritedEventCount: SessionLogOffset, events: readonly SessionEvent[], tornTruncateTo?: number): Promise<boolean>;
298
+ close(): Promise<void>;
332
299
  createAndAppend(header: SessionHeader, events: readonly SessionEvent[], inheritedEventCount?: number): Promise<void>;
333
300
  load(id: SessionId, signal?: AbortSignal): Promise<import("@deepseek-ai/dsh-session-persistence").SessionInspection>;
334
301
  append(id: SessionId, events: readonly SessionEvent[]): Promise<void>;
@@ -337,19 +304,12 @@ declare class SessionPersistenceRdb extends SessionPersistence {
337
304
  inheritedEventCount: number;
338
305
  events: readonly SessionEvent[];
339
306
  }>;
340
- close(): Promise<void>;
341
307
  registerReuseEventIds(childId: SessionId, map: ReadonlyMap<number, string>): void;
342
308
  dropReuseEventIds(childId: SessionId): void;
343
- internals(): SessionPersistenceRdbInternals;
309
+ /** 分支能力的端口(见 `branch/port.ts`):每个动词都有真实调用方,不是服务方法表的转抄。 */
310
+ branchPort(): BranchPort;
344
311
  private rowRevision;
345
- private readonly liveFailures;
346
- private readonly liveDropWarned;
347
- private watchLiveReady;
348
- private installLiveRouting;
349
- private ensureLiveHandle;
350
312
  }
351
- //#endregion
352
- //#region src/usage.d.ts
353
313
  type UsageRangeKey = "all" | "day" | "week" | "7d" | "30d" | "90d";
354
314
  declare function resolveUsageSince(range: UsageRangeKey, now: number): number | undefined;
355
315
  interface UsageTokenTotals {
@@ -394,8 +354,6 @@ declare function addTokenTotals(target: UsageTokenTotals, row: UsageTokenTotals)
394
354
  declare function addActivityTotals(target: UsageActivityTotals, row: UsageActivityTotals): UsageActivityTotals;
395
355
  declare function addTotals(target: UsageTotals, row: UsageTotals): UsageTotals;
396
356
  declare function registerSessionUsage(ctx: Context, persistence: SessionPersistenceRdb): void;
397
- //#endregion
398
- //#region src/backend.d.ts
399
357
  interface SessionRow {
400
358
  fSessionId: string;
401
359
  fHeadEventId: string;
@@ -452,6 +410,8 @@ interface BackendTx {
452
410
  fSurfaceOp: string | null;
453
411
  }>): Promise<void>;
454
412
  updateHead(id: SessionId, headEventId: string, headSequence: number): Promise<void>;
413
+ /** 推进 head 并 bump revision:同一行一次 UPDATE(分两次写是纯写放大)。 */
414
+ updateHeadAndRevision(id: SessionId, headEventId: string, headSequence: number, lastEventAt?: number): Promise<void>;
455
415
  bumpRevision(id: SessionId, lastEventAt?: number): Promise<void>;
456
416
  deleteBridgeTail(id: SessionId, fromSequence: number): Promise<readonly string[]>;
457
417
  getPrevBridge(id: SessionId, sequence: number): Promise<{
@@ -493,16 +453,28 @@ interface SessionUsageBucket extends UsageTokenTotals {
493
453
  provider: string | null;
494
454
  model: string | null;
495
455
  }
456
+ /** 会话读的共享实现:写路径之外的所有查询都在这里(两方言共用一套投影查询)。 */
457
+ interface ReadModel {
458
+ getSession(id: string): Promise<SessionRow | undefined>;
459
+ getEventRows(id: string, fromSequence?: number): Promise<EventRow[]>;
460
+ getEventTypeAt(id: string, sequence: number): Promise<string | undefined>;
461
+ getEventTypesBefore(id: string, beforeSequence: number, limit: number): Promise<Array<Pick<EventRow, "fSequence" | "fType">>>;
462
+ listSessions(): Promise<SessionRow[]>;
463
+ /** 最新一条 `session/title` 事件(`refreshTitle` 用)。 */
464
+ latestTitleEvent(id: string): Promise<{
465
+ fSequence: number;
466
+ fData: string;
467
+ } | undefined>;
468
+ /** 父会话已不在表里的 subagent 会话。 */
469
+ listOrphanSubagentSessions(): Promise<string[]>;
470
+ }
496
471
  interface Backend {
497
472
  readonly kind: "sqlite" | "postgres";
498
473
  readonly storeIdentity: string;
499
474
  readonly storage: StorageRepository;
475
+ /** 读面:会话行与事件行的查询(不再是 Backend 自己的 6 个转发方法)。 */
476
+ readonly reads: ReadModel;
500
477
  open(): Promise<void>;
501
- getSession(id: SessionId): Promise<SessionRow | undefined>;
502
- getEventRows(id: SessionId, fromSequence?: number): Promise<EventRow[]>;
503
- getEventTypeAt(id: SessionId, sequence: number): Promise<string | undefined>;
504
- getEventTypesBefore(id: SessionId, beforeSequence: number, limit: number): Promise<Array<Pick<EventRow, "fSequence" | "fType">>>;
505
- listSessions(): Promise<SessionRow[]>;
506
478
  transaction<T>(fn: (tx: BackendTx) => Promise<T>): Promise<T>;
507
479
  collectOrphans(): Promise<number>;
508
480
  listOrphanSubagentSessions(): Promise<SessionId[]>;
@@ -511,84 +483,10 @@ interface Backend {
511
483
  incrementSessionUsage(id: SessionId, buckets: readonly SessionUsageBucket[]): Promise<void>;
512
484
  incrementSessionCounts(id: SessionId, buckets: readonly SessionCountBucket[]): Promise<void>;
513
485
  rebuildSessionStats(id: SessionId): Promise<void>;
486
+ rebuildAllStats(): Promise<number>;
514
487
  refreshEventUsageFlags(eventIds: readonly string[]): Promise<void>;
515
488
  deleteSessionStats(id: SessionId): Promise<void>;
516
489
  vacuum(): Promise<void>;
517
490
  close(): Promise<void>;
518
491
  }
519
- //#endregion
520
- //#region src/log.d.ts
521
- declare function rowToMeta(row: SessionRow): SessionHeader;
522
- declare function sessionInsertRow(storage: SessionStorageMetadata, incarnation: string): {
523
- fSessionId: string;
524
- fHeadEventId: string;
525
- fHeadSequence: number;
526
- fVersion: number;
527
- fCreatedAt: number;
528
- fCwd: string | null;
529
- fParentSession: string | null;
530
- fSeedLength: number | null;
531
- fOrigin: string | null;
532
- fDelegationDepth: number | null;
533
- fAgentPreset: string | null;
534
- fIncarnation: string;
535
- fRevision: number;
536
- };
537
- declare function sessionConflictRow(storage: SessionStorageMetadata): {
538
- fVersion: number;
539
- fCreatedAt: number;
540
- fCwd: string | null;
541
- fParentSession: string | null;
542
- fSeedLength: number | null;
543
- fOrigin: string | null;
544
- fDelegationDepth: number | null;
545
- fAgentPreset: string | null;
546
- };
547
- declare function rowToEvent(row: EventRow): SessionEvent;
548
- declare function recomputeReplaceProvenance(events: SessionEvent[]): void;
549
- declare function findSurfaceRepairs(events: readonly SessionEvent[]): {
550
- degradeToAppend: Set<number>;
551
- addAppendMarker: Set<number>;
552
- clearSurfaceOp: Set<number>;
553
- clampEnd: Map<number, number>;
554
- };
555
- declare function repairSurfaceOps(events: SessionEvent[]): void;
556
- declare function repairAssistantSettlement(events: SessionEvent[]): void;
557
- declare function repairRequestHeaders(events: SessionEvent[]): void;
558
- declare function syncMeteringRanges(events: SessionEvent[]): void;
559
- declare function renameLegacyPtcEvents(events: SessionEvent[]): void;
560
- declare function normalizeToCurrentShape(events: SessionEvent[]): void;
561
- declare function sealOwnEvents(events: readonly SessionEvent[]): void;
562
- declare const OWN_EVENT_TYPES: ReadonlySet<string>;
563
- declare function needsShapeAdoption(events: readonly SessionEvent[]): boolean;
564
- declare function repairReadView(events: SessionEvent[]): void;
565
- declare function orphanInboxSpliceSeqs(events: readonly SessionEvent[]): Set<number>;
566
- declare function repairOrphanInboxSplices(events: SessionEvent[]): void;
567
- declare function scanRows(rows: readonly EventRow[], base?: number): {
568
- preserved: SessionEvent[];
569
- tornFrom?: number;
570
- };
571
- declare function toJsonlArtifact(meta: SessionHeader, inheritedEventCount: number, events: readonly SessionEvent[]): string;
572
- declare function titleOfEventData(data: string): string | undefined;
573
- interface EventUsageRow {
574
- fEventId: string;
575
- fCreatedAt: number;
576
- fDay: string;
577
- fProvider: string | null;
578
- fModel: string | null;
579
- fReferenced: number;
580
- fSubagent: number;
581
- fInputTokens: number;
582
- fOutputTokens: number;
583
- fCacheReadTokens: number;
584
- fReasoningTokens: number;
585
- fTotalTokens: number;
586
- }
587
- declare function usageRowOf(event: {
588
- fEventId: string;
589
- fCreatedAt: number;
590
- fType: string;
591
- fData: string;
592
- }, subagent: boolean): EventUsageRow | undefined;
593
- //#endregion
594
- export { SessionDeletionErrorCode as $, SessionRow as A, tWorkspaces as At, UsageTotals as B, usageRowOf as C, tSessionEvents as Ct, SessionCountBucket as D, tStorageUnits as Dt, EventRow as E, tSessions as Et, UsageAggregate as F, SESSION_IMPORT_PATH as Ft, emptyTotals as G, addActivityTotals as H, UsageBucket as I, SESSION_ROWS_PATH as It, resolveUsageSince as J, localDayKey as K, UsageRangeKey as L, SESSION_USAGE_PATH as Lt, COUNTED_EVENT_TYPES as M, SESSION_DELETE_PATH as Mt, SessionUsageReport as N, SESSION_EXPORT_PATH as Nt, SessionListRowsPage as O, tWorkspaceSessions as Ot, UsageActivityTotals as P, SESSION_GC_PATH as Pt, SessionDeletionError as Q, UsageSessionRow as R, StorageRepository as Rt, toJsonlArtifact as S, tSessionCounts as St, BackendTx as T, tSessionUsage as Tt, addTokenTotals as U, addActivityCount as V, addTotals as W, ProjectionCacheOptions as X, Config as Y, ResolvedConfig as Z, sealOwnEvents as _, tEventUsage as _t, normalizeToCurrentShape as a, truncateLiveSession as at, syncMeteringRanges as b, tScheduleTasks as bt, renameLegacyPtcEvents as c, DEFAULT_BUSY_TIMEOUT_MS as ct, repairReadView as d, EventRole as dt, SessionPersistenceRdb as et, repairRequestHeaders as f, JournalMode as ft, scanRows as g, eventKind as gt, rowToMeta as h, eventDimensions as ht, needsShapeAdoption as i, locateTurnEnd as it, SessionUsageBucket as j, WriteGuard as jt, SessionListRowsQuery as k, tWorkspaceState as kt, repairAssistantSettlement as l, EVENT_ENCODING as lt, rowToEvent as m, SESSION_PERSISTENCE_SQLITE_APPLICATION_ID as mt, OWN_EVENT_TYPES as n, SessionBranchRdb as nt, orphanInboxSpliceSeqs as o, SessionRowsItem as ot, repairSurfaceOps as p, SCHEMA_VERSION as pt, registerSessionUsage as q, findSurfaceRepairs as r, SessionBranchRdbProvider as rt, recomputeReplaceProvenance as s, SessionRowsValue as st, EventUsageRow as t, SessionPersistenceRdbInternals as tt, repairOrphanInboxSplices as u, EventKind as ut, sessionConflictRow as v, tEvents as vt, Backend as w, tSessionProjcacheRows as wt, titleOfEventData as x, tSchemaMeta as xt, sessionInsertRow as y, tPersistenceState as yt, UsageTokenTotals as z };
492
+ export { SessionDeletionError as A, SESSION_EXPORT_PATH as B, emptyTotals as C, Config as D, resolveUsageSince as E, JournalMode as F, StorageRepository as G, SESSION_ORGANIZE_PATH as H, SCHEMA_VERSION as I, EventUsageRow as K, SESSION_PERSISTENCE_SQLITE_APPLICATION_ID as L, BranchPort as M, DEFAULT_BUSY_TIMEOUT_MS as N, ResolvedConfig as O, EVENT_ENCODING as P, WriteGuard as R, addTotals as S, registerSessionUsage as T, SESSION_ROWS_PATH as U, SESSION_IMPORT_PATH as V, SESSION_USAGE_PATH as W, UsageTokenTotals as _, SessionCountBucket as a, addActivityTotals as b, SessionRow as c, SessionUsageReport as d, UsageActivityTotals as f, UsageSessionRow as g, UsageRangeKey as h, ReadModel as i, SessionDeletionErrorCode as j, SessionPersistenceRdb as k, SessionUsageBucket as l, UsageBucket as m, BackendTx as n, SessionListRowsPage as o, UsageAggregate as p, usageRowOf as q, EventRow as r, SessionListRowsQuery as s, Backend as t, COUNTED_EVENT_TYPES as u, UsageTotals as v, localDayKey as w, addTokenTotals as x, addActivityCount as y, SESSION_DELETE_PATH as z };