@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,1777 +0,0 @@
1
- import { COUNTED_EVENT_TYPES, addActivityTotals, addTokenTotals, addTotals, emptyTotals, localDayKey } from "./usage.mjs";
2
- import { _ as sessionConflictRow, b as titleOfEventData, v as sessionInsertRow } from "./log-DYxdjJ-l.mjs";
3
- import { SESSION_FORMAT_VERSION, SessionId, SessionLogOffset, SessionSeq } from "@deepseek-ai/dsh-session";
4
- import { randomUUID } from "node:crypto";
5
- import { and, desc, eq, gte, inArray, isNotNull, lt, notInArray, sql } from "drizzle-orm";
6
- import { check, index, integer, sqliteTable, text, unique } from "drizzle-orm/sqlite-core";
7
- import { bigint, check as check$1, index as index$1, integer as integer$1, pgSchema, pgTable, serial, text as text$1, unique as unique$1 } from "drizzle-orm/pg-core";
8
- import { mkdir, open, readdir, stat } from "node:fs/promises";
9
- import { dirname, resolve } from "node:path";
10
- import { fileURLToPath } from "node:url";
11
- import { DatabaseSync } from "node:sqlite";
12
- import { drizzle } from "drizzle-orm/node-sqlite";
13
- import { migrate } from "drizzle-orm/node-sqlite/migrator";
14
- //#region src/write-guard.ts
15
- var WriteGuard = class {
16
- headSeqs = /* @__PURE__ */ new Map();
17
- confirmHead(id, head) {
18
- this.headSeqs.set(id, head);
19
- }
20
- has(id) {
21
- return this.headSeqs.has(id);
22
- }
23
- assertNoConcurrentWriter(id, storedHead) {
24
- const known = this.headSeqs.get(id);
25
- if (known === void 0) {
26
- if (storedHead !== -1) throw new Error(`session "${id}" has a persisted log this instance has not read; another writer may own it — load the session first`);
27
- return;
28
- }
29
- if (known !== storedHead) throw new Error(`session "${id}" was modified by another writer (stored head ${storedHead}, this instance last confirmed head ${known}); concurrent writers on one session are not supported`);
30
- }
31
- };
32
- //#endregion
33
- //#region src/adapters/types.ts
34
- function toProperty(name) {
35
- return name.replace(/_([a-z])/g, (_match, char) => char.toUpperCase());
36
- }
37
- //#endregion
38
- //#region src/adapters/to-sqlite.ts
39
- function buildColumn$1(name, c, tables) {
40
- let col;
41
- switch (c.type) {
42
- case "text": {
43
- const built = text(name);
44
- col = c.primaryKey ? built.primaryKey() : built;
45
- break;
46
- }
47
- case "serial":
48
- col = integer(name).primaryKey({ autoIncrement: true });
49
- break;
50
- case "integer":
51
- case "bigint": {
52
- const built = integer(name);
53
- col = c.primaryKey ? built.primaryKey() : built;
54
- break;
55
- }
56
- }
57
- if (c.notNull) col = col.notNull();
58
- if (c.default !== void 0) col = col.default(c.default);
59
- if (c.unique) col = col.unique();
60
- if (c.references) {
61
- const { table, column, onDelete } = c.references;
62
- col = col.references(() => tables[table][toProperty(column)], { onDelete });
63
- }
64
- return col;
65
- }
66
- function toSqliteSchema(defs) {
67
- const tables = {};
68
- for (const def of defs) {
69
- const columns = {};
70
- for (const [name, c] of Object.entries(def.columns)) columns[toProperty(name)] = buildColumn$1(name, c, tables);
71
- const extra = (self) => [
72
- ...Object.entries(def.checks ?? {}).map(([name, c]) => check(name, sql.raw(c.expression))),
73
- ...Object.entries(def.uniques ?? {}).map(([name, u]) => unique(name).on(...u.columns.map((n) => self[toProperty(n)]))),
74
- ...Object.entries(def.indexes ?? {}).map(([name, i]) => index(name).on(...i.columns.map((n) => self[toProperty(n)])))
75
- ];
76
- tables[def.name] = sqliteTable(def.name, columns, extra);
77
- }
78
- return tables;
79
- }
80
- //#endregion
81
- //#region src/adapters/to-postgres.ts
82
- function buildColumn(name, c, tables) {
83
- let col;
84
- switch (c.type) {
85
- case "text": {
86
- const built = text$1(name);
87
- col = c.primaryKey ? built.primaryKey() : built;
88
- break;
89
- }
90
- case "serial":
91
- col = serial(name).primaryKey();
92
- break;
93
- case "integer": {
94
- const built = integer$1(name);
95
- col = c.primaryKey ? built.primaryKey() : built;
96
- break;
97
- }
98
- case "bigint": {
99
- const built = bigint(name, { mode: "number" });
100
- col = c.primaryKey ? built.primaryKey() : built;
101
- break;
102
- }
103
- }
104
- if (c.notNull) col = col.notNull();
105
- if (c.default !== void 0) col = col.default(c.default);
106
- if (c.unique) col = col.unique();
107
- if (c.references) {
108
- const { table, column, onDelete } = c.references;
109
- col = col.references(() => tables[table][toProperty(column)], { onDelete });
110
- }
111
- return col;
112
- }
113
- function toPostgresSchema(defs, schemaName = "public") {
114
- const tables = {};
115
- const table = schemaName === "public" ? pgTable : pgSchema(schemaName).table;
116
- for (const def of defs) {
117
- const columns = {};
118
- for (const [name, c] of Object.entries(def.columns)) columns[toProperty(name)] = buildColumn(name, c, tables);
119
- const extra = (self) => [
120
- ...Object.entries(def.checks ?? {}).map(([name, c]) => check$1(name, sql.raw(c.expression))),
121
- ...Object.entries(def.uniques ?? {}).map(([name, u]) => unique$1(name).on(...u.columns.map((n) => self[toProperty(n)]))),
122
- ...Object.entries(def.indexes ?? {}).map(([name, i]) => index$1(name).on(...i.columns.map((n) => self[toProperty(n)])))
123
- ];
124
- tables[def.name] = table(def.name, columns, extra);
125
- }
126
- return tables;
127
- }
128
- //#endregion
129
- //#region src/entities/v3/persistence-state.ts
130
- const persistenceState = {
131
- name: "t_persistence_state",
132
- columns: {
133
- f_singleton: {
134
- type: "integer",
135
- primaryKey: true
136
- },
137
- f_store_id: {
138
- type: "text",
139
- notNull: true
140
- }
141
- },
142
- checks: { ck_persistence_state_singleton: { expression: "f_singleton = 1" } }
143
- };
144
- //#endregion
145
- //#region src/entities/v3/schema-meta.ts
146
- const schemaMeta = {
147
- name: "t_schema_meta",
148
- columns: {
149
- f_key: {
150
- type: "text",
151
- primaryKey: true
152
- },
153
- f_value: {
154
- type: "text",
155
- notNull: true
156
- }
157
- }
158
- };
159
- //#endregion
160
- //#region src/entities/v3/sessions.ts
161
- const sessions = {
162
- name: "t_sessions",
163
- columns: {
164
- f_id: {
165
- type: "serial",
166
- primaryKey: true
167
- },
168
- f_session_id: {
169
- type: "text",
170
- notNull: true,
171
- unique: true
172
- },
173
- f_head_event_id: {
174
- type: "text",
175
- notNull: true,
176
- default: ""
177
- },
178
- f_head_sequence: {
179
- type: "integer",
180
- notNull: true,
181
- default: -1
182
- },
183
- f_version: {
184
- type: "integer",
185
- notNull: true
186
- },
187
- f_created_at: {
188
- type: "bigint",
189
- notNull: true
190
- },
191
- f_cwd: { type: "text" },
192
- f_parent_session: { type: "text" },
193
- f_seed_length: { type: "integer" },
194
- f_origin: { type: "text" },
195
- f_delegation_depth: { type: "integer" },
196
- f_agent_preset: { type: "text" },
197
- f_incarnation: {
198
- type: "text",
199
- notNull: true
200
- },
201
- f_revision: {
202
- type: "integer",
203
- notNull: true
204
- },
205
- f_archived_at: { type: "bigint" },
206
- f_pinned_seq: { type: "integer" },
207
- f_last_event_at: { type: "bigint" },
208
- f_title: { type: "text" },
209
- f_title_seq: { type: "integer" }
210
- }
211
- };
212
- //#endregion
213
- //#region src/entities/v3/events.ts
214
- const events = {
215
- name: "t_events",
216
- columns: {
217
- f_id: {
218
- type: "serial",
219
- primaryKey: true
220
- },
221
- f_event_id: {
222
- type: "text",
223
- notNull: true,
224
- unique: true
225
- },
226
- f_parent_id: {
227
- type: "text",
228
- notNull: true,
229
- default: ""
230
- },
231
- f_type: {
232
- type: "text",
233
- notNull: true,
234
- default: ""
235
- },
236
- f_kind: {
237
- type: "text",
238
- notNull: true,
239
- default: ""
240
- },
241
- f_role: {
242
- type: "text",
243
- notNull: true,
244
- default: ""
245
- },
246
- f_name: {
247
- type: "text",
248
- notNull: true,
249
- default: ""
250
- },
251
- f_action_id: {
252
- type: "text",
253
- notNull: true,
254
- default: ""
255
- },
256
- f_encoding: {
257
- type: "text",
258
- notNull: true,
259
- default: ""
260
- },
261
- f_data: {
262
- type: "text",
263
- notNull: true
264
- },
265
- f_created_at: {
266
- type: "bigint",
267
- notNull: true,
268
- default: 0
269
- }
270
- },
271
- indexes: {
272
- idx_events_kind: { columns: ["f_kind"] },
273
- idx_events_role: { columns: ["f_role"] },
274
- idx_events_name: { columns: ["f_name"] },
275
- idx_events_action_id: { columns: ["f_action_id"] }
276
- }
277
- };
278
- //#endregion
279
- //#region src/entities/v3/session-events.ts
280
- const sessionEvents$1 = {
281
- name: "t_session_events",
282
- columns: {
283
- f_id: {
284
- type: "serial",
285
- primaryKey: true
286
- },
287
- f_session_id: {
288
- type: "text",
289
- notNull: true,
290
- references: {
291
- table: "t_sessions",
292
- column: "f_session_id",
293
- onDelete: "cascade"
294
- }
295
- },
296
- f_event_id: {
297
- type: "text",
298
- notNull: true,
299
- references: {
300
- table: "t_events",
301
- column: "f_event_id",
302
- onDelete: "cascade"
303
- }
304
- },
305
- f_sequence: {
306
- type: "integer",
307
- notNull: true
308
- },
309
- f_surface_op: { type: "text" }
310
- },
311
- uniques: { uq_session_events_session_sequence: { columns: ["f_session_id", "f_sequence"] } },
312
- indexes: { idx_session_events_event_id: { columns: ["f_event_id"] } }
313
- };
314
- //#endregion
315
- //#region src/entities/v3/storage-units.ts
316
- const storageUnits = {
317
- name: "t_storage_units",
318
- columns: {
319
- f_name: {
320
- type: "text",
321
- primaryKey: true
322
- },
323
- f_version: {
324
- type: "integer",
325
- notNull: true
326
- }
327
- }
328
- };
329
- //#endregion
330
- //#region src/entities/v3/workspaces.ts
331
- const workspaces = {
332
- name: "t_workspaces",
333
- columns: {
334
- f_id: {
335
- type: "serial",
336
- primaryKey: true
337
- },
338
- f_workspace_id: {
339
- type: "text",
340
- notNull: true,
341
- unique: true
342
- },
343
- f_path: {
344
- type: "text",
345
- notNull: true
346
- },
347
- f_title: {
348
- type: "text",
349
- notNull: true
350
- },
351
- f_created_at: {
352
- type: "text",
353
- notNull: true
354
- },
355
- f_updated_at: {
356
- type: "text",
357
- notNull: true
358
- },
359
- f_position: {
360
- type: "integer",
361
- notNull: true,
362
- default: -1
363
- }
364
- }
365
- };
366
- //#endregion
367
- //#region src/entities/v3/workspace-sessions.ts
368
- const workspaceSessions = {
369
- name: "t_workspace_sessions",
370
- columns: {
371
- f_id: {
372
- type: "serial",
373
- primaryKey: true
374
- },
375
- f_workspace_id: {
376
- type: "text",
377
- notNull: true,
378
- references: {
379
- table: "t_workspaces",
380
- column: "f_workspace_id",
381
- onDelete: "cascade"
382
- }
383
- },
384
- f_session_id: {
385
- type: "text",
386
- notNull: true
387
- },
388
- f_position: {
389
- type: "integer",
390
- notNull: true
391
- }
392
- },
393
- uniques: { uq_workspace_sessions_workspace_session: { columns: ["f_workspace_id", "f_session_id"] } },
394
- indexes: { idx_workspace_sessions_session_id: { columns: ["f_session_id"] } }
395
- };
396
- //#endregion
397
- //#region src/entities/v3/workspace-state.ts
398
- const workspaceState = {
399
- name: "t_workspace_state",
400
- columns: {
401
- f_singleton: {
402
- type: "integer",
403
- primaryKey: true
404
- },
405
- f_initialized: {
406
- type: "integer",
407
- notNull: true
408
- },
409
- f_pending_operation: { type: "text" },
410
- f_pending_workspace_id: { type: "text" }
411
- },
412
- checks: { ck_workspace_state_singleton: { expression: "f_singleton = 1" } }
413
- };
414
- //#endregion
415
- //#region src/entities/v3/schedule-tasks.ts
416
- const scheduleTasks = {
417
- name: "t_schedule_tasks",
418
- columns: {
419
- f_id: {
420
- type: "text",
421
- primaryKey: true
422
- },
423
- f_session_id: {
424
- type: "text",
425
- notNull: true
426
- },
427
- f_status: {
428
- type: "text",
429
- notNull: true
430
- },
431
- f_record: {
432
- type: "text",
433
- notNull: true
434
- },
435
- f_last_delivery: { type: "text" },
436
- f_delivery_history: { type: "text" }
437
- },
438
- indexes: { idx_schedule_tasks_session_id: { columns: ["f_session_id"] } }
439
- };
440
- //#endregion
441
- //#region src/entities/v3/session-projcache-rows.ts
442
- const sessionProjcacheRows = {
443
- name: "t_session_projcache_row",
444
- columns: {
445
- f_id: {
446
- type: "serial",
447
- primaryKey: true
448
- },
449
- f_session_id: {
450
- type: "text",
451
- notNull: true,
452
- references: {
453
- table: "t_sessions",
454
- column: "f_session_id",
455
- onDelete: "cascade"
456
- }
457
- },
458
- f_key: {
459
- type: "text",
460
- notNull: true
461
- },
462
- f_ver: {
463
- type: "integer",
464
- notNull: true
465
- },
466
- f_seq: {
467
- type: "integer",
468
- notNull: true
469
- },
470
- f_val: {
471
- type: "text",
472
- notNull: true
473
- }
474
- },
475
- uniques: { uq_session_projcache_row_session_key: { columns: ["f_session_id", "f_key"] } },
476
- indexes: { idx_session_projcache_row_key: { columns: ["f_key"] } }
477
- };
478
- //#endregion
479
- //#region src/entities/v3/event-usage.ts
480
- const eventUsage = {
481
- name: "t_event_usage",
482
- columns: {
483
- f_event_id: {
484
- type: "text",
485
- primaryKey: true,
486
- references: {
487
- table: "t_events",
488
- column: "f_event_id",
489
- onDelete: "cascade"
490
- }
491
- },
492
- f_created_at: {
493
- type: "bigint",
494
- notNull: true
495
- },
496
- f_day: {
497
- type: "text",
498
- notNull: true
499
- },
500
- f_provider: { type: "text" },
501
- f_model: { type: "text" },
502
- f_referenced: {
503
- type: "integer",
504
- notNull: true,
505
- default: 0
506
- },
507
- f_subagent: {
508
- type: "integer",
509
- notNull: true,
510
- default: 0
511
- },
512
- f_input_tokens: {
513
- type: "integer",
514
- notNull: true,
515
- default: 0
516
- },
517
- f_output_tokens: {
518
- type: "integer",
519
- notNull: true,
520
- default: 0
521
- },
522
- f_cache_read_tokens: {
523
- type: "integer",
524
- notNull: true,
525
- default: 0
526
- },
527
- f_reasoning_tokens: {
528
- type: "integer",
529
- notNull: true,
530
- default: 0
531
- },
532
- f_total_tokens: {
533
- type: "integer",
534
- notNull: true,
535
- default: 0
536
- }
537
- },
538
- indexes: { idx_event_usage_created_at: { columns: ["f_created_at"] } }
539
- };
540
- //#endregion
541
- //#region src/entities/v3/session-usage.ts
542
- const sessionUsage = {
543
- name: "t_session_usage",
544
- columns: {
545
- f_session_id: {
546
- type: "text",
547
- notNull: true,
548
- references: {
549
- table: "t_sessions",
550
- column: "f_session_id",
551
- onDelete: "cascade"
552
- }
553
- },
554
- f_day: {
555
- type: "text",
556
- notNull: true
557
- },
558
- f_provider: {
559
- type: "text",
560
- notNull: true,
561
- default: ""
562
- },
563
- f_model: {
564
- type: "text",
565
- notNull: true,
566
- default: ""
567
- },
568
- f_input_tokens: {
569
- type: "integer",
570
- notNull: true,
571
- default: 0
572
- },
573
- f_output_tokens: {
574
- type: "integer",
575
- notNull: true,
576
- default: 0
577
- },
578
- f_cache_read_tokens: {
579
- type: "integer",
580
- notNull: true,
581
- default: 0
582
- },
583
- f_reasoning_tokens: {
584
- type: "integer",
585
- notNull: true,
586
- default: 0
587
- },
588
- f_total_tokens: {
589
- type: "integer",
590
- notNull: true,
591
- default: 0
592
- }
593
- },
594
- uniques: { uq_session_usage_key: { columns: [
595
- "f_session_id",
596
- "f_day",
597
- "f_provider",
598
- "f_model"
599
- ] } }
600
- };
601
- //#endregion
602
- //#region src/entities/v3/session-counts.ts
603
- const sessionCounts = {
604
- name: "t_session_counts",
605
- columns: {
606
- f_session_id: {
607
- type: "text",
608
- notNull: true,
609
- references: {
610
- table: "t_sessions",
611
- column: "f_session_id",
612
- onDelete: "cascade"
613
- }
614
- },
615
- f_day: {
616
- type: "text",
617
- notNull: true
618
- },
619
- f_turns: {
620
- type: "integer",
621
- notNull: true,
622
- default: 0
623
- },
624
- f_steps: {
625
- type: "integer",
626
- notNull: true,
627
- default: 0
628
- },
629
- f_user_inputs: {
630
- type: "integer",
631
- notNull: true,
632
- default: 0
633
- },
634
- f_tool_calls: {
635
- type: "integer",
636
- notNull: true,
637
- default: 0
638
- }
639
- },
640
- uniques: { uq_session_counts_key: { columns: ["f_session_id", "f_day"] } }
641
- };
642
- //#endregion
643
- //#region src/entities/v3/index.ts
644
- const sqliteTableDefs = [
645
- persistenceState,
646
- schemaMeta,
647
- sessions,
648
- events,
649
- sessionEvents$1,
650
- storageUnits,
651
- workspaces,
652
- workspaceSessions,
653
- workspaceState,
654
- scheduleTasks,
655
- sessionProjcacheRows,
656
- eventUsage,
657
- sessionUsage,
658
- sessionCounts
659
- ];
660
- const postgresTableDefs = [
661
- persistenceState,
662
- schemaMeta,
663
- sessions,
664
- events,
665
- sessionEvents$1,
666
- storageUnits,
667
- workspaces,
668
- workspaceSessions,
669
- workspaceState,
670
- scheduleTasks,
671
- sessionProjcacheRows,
672
- eventUsage,
673
- sessionUsage,
674
- sessionCounts
675
- ];
676
- ({ ...sessionEvents$1 }), { ...sessionEvents$1.columns };
677
- //#endregion
678
- //#region src/schema.ts
679
- const SCHEMA_VERSION = 3;
680
- const SESSION_PERSISTENCE_SQLITE_APPLICATION_ID = 1146308688;
681
- const EVENT_ENCODING = "json";
682
- const sqliteTables = toSqliteSchema(sqliteTableDefs);
683
- const tPersistenceState = sqliteTables["t_persistence_state"];
684
- const tSchemaMeta = sqliteTables["t_schema_meta"];
685
- const tSessions = sqliteTables["t_sessions"];
686
- const tEvents = sqliteTables["t_events"];
687
- const tSessionEvents = sqliteTables["t_session_events"];
688
- const tStorageUnits = sqliteTables["t_storage_units"];
689
- const tWorkspaces = sqliteTables["t_workspaces"];
690
- const tWorkspaceSessions = sqliteTables["t_workspace_sessions"];
691
- const tWorkspaceState = sqliteTables["t_workspace_state"];
692
- const tScheduleTasks = sqliteTables["t_schedule_tasks"];
693
- const tSessionProjcacheRows = sqliteTables["t_session_projcache_row"];
694
- const tEventUsage = sqliteTables["t_event_usage"];
695
- const tSessionUsage = sqliteTables["t_session_usage"];
696
- const tSessionCounts = sqliteTables["t_session_counts"];
697
- const DEFAULT_BUSY_TIMEOUT_MS = 5e3;
698
- function eventKind(event) {
699
- switch (event.type) {
700
- case "user/message": return "message";
701
- case "assistant/message":
702
- if ((event.data.message?.content)?.some((block) => block?.type === "reasoning")) return "thinking";
703
- return "message";
704
- case "turn/start":
705
- case "turn/end":
706
- case "step/start":
707
- case "step/end":
708
- case "session/end-seed": return "turn";
709
- case "tool/call":
710
- case "tool/result":
711
- case "tool/ptc-dispatch-start":
712
- case "tool/ptc-dispatch":
713
- case "tool/code-dispatch-start":
714
- case "tool/code-dispatch": return "tool";
715
- case "request/header":
716
- case "request/context": return "request";
717
- case "model/selection":
718
- case "permission/preset":
719
- case "approval/policy":
720
- case "sandbox/mode":
721
- case "plan/mode":
722
- case "agent-preset/selected": return "config";
723
- case "approval/asked":
724
- case "approval/decided":
725
- case "command/run":
726
- case "command/done":
727
- case "hook/invoked":
728
- case "hook/result":
729
- case "feedback/record": return "audit";
730
- case "session/title":
731
- case "session/title-llm-request":
732
- case "session-log-deepseek/delivery-accepted": return "lifecycle";
733
- case "agent/inbox/spliced": return "inbox";
734
- case "compaction/start":
735
- case "compaction/end":
736
- case "compaction/summary":
737
- case "compaction/prune": return "compaction";
738
- case "llm/retry":
739
- case "llm/retry-started": return "llm";
740
- case "subagent/descriptor":
741
- case "subagent/model-selection-policy": return "subagent";
742
- case "team/member":
743
- case "team/task":
744
- case "team/message/queued":
745
- case "team/message/delivered": return "team";
746
- case "tool-workflow/run-start":
747
- case "tool-workflow/run-end":
748
- case "tool-workflow/agent-start":
749
- case "tool-workflow/agent-end": return "workflow";
750
- case "goal/change": return "goal";
751
- case "schedule/change": return "schedule";
752
- case "todo/write": return "todo";
753
- case "web/deepseek-search-llm-request": return "web";
754
- default: return "";
755
- }
756
- }
757
- function eventDimensions(event) {
758
- const kind = eventKind(event);
759
- const type = event.type;
760
- const data = event.data;
761
- switch (type) {
762
- case "user/message": return {
763
- kind,
764
- role: "user",
765
- name: "",
766
- actionId: ""
767
- };
768
- case "assistant/message": return {
769
- kind,
770
- role: "assistant",
771
- name: "",
772
- actionId: ""
773
- };
774
- case "tool/result": return {
775
- kind,
776
- role: "tool",
777
- name: "",
778
- actionId: data["message"]?.toolCallId ?? ""
779
- };
780
- case "tool/call": return {
781
- kind,
782
- role: "",
783
- name: typeof data["name"] === "string" ? data["name"] : "",
784
- actionId: typeof data["callId"] === "string" ? data["callId"] : ""
785
- };
786
- case "tool/ptc-dispatch-start":
787
- case "tool/ptc-dispatch":
788
- case "tool/code-dispatch-start":
789
- case "tool/code-dispatch": return {
790
- kind,
791
- role: "",
792
- name: "",
793
- actionId: typeof data["subCallId"] === "string" ? data["subCallId"] : ""
794
- };
795
- case "command/run":
796
- case "command/done": return {
797
- kind,
798
- role: "",
799
- name: type === "command/run" && typeof data["name"] === "string" ? data["name"] : "",
800
- actionId: typeof data["commandId"] === "string" ? data["commandId"] : ""
801
- };
802
- case "approval/asked":
803
- case "approval/decided": return {
804
- kind,
805
- role: "",
806
- name: "",
807
- actionId: typeof data["id"] === "string" ? data["id"] : ""
808
- };
809
- case "hook/invoked":
810
- case "hook/result": return {
811
- kind,
812
- role: "",
813
- name: "",
814
- actionId: typeof data["handlerId"] === "string" ? data["handlerId"] : ""
815
- };
816
- case "llm/retry":
817
- case "llm/retry-started": return {
818
- kind,
819
- role: "",
820
- name: "",
821
- actionId: typeof data["retryId"] === "string" ? data["retryId"] : ""
822
- };
823
- case "tool-workflow/run-start":
824
- case "tool-workflow/run-end":
825
- case "tool-workflow/agent-start":
826
- case "tool-workflow/agent-end": return {
827
- kind,
828
- role: "",
829
- name: "",
830
- actionId: typeof data["runId"] === "string" ? data["runId"] : ""
831
- };
832
- case "todo/write": return {
833
- kind,
834
- role: "",
835
- name: "todos",
836
- actionId: ""
837
- };
838
- case "subagent/descriptor": return {
839
- kind,
840
- role: "",
841
- name: typeof data["label"] === "string" ? data["label"] : "",
842
- actionId: ""
843
- };
844
- default: return {
845
- kind,
846
- role: "",
847
- name: "",
848
- actionId: ""
849
- };
850
- }
851
- }
852
- //#endregion
853
- //#region src/storage-takeover/repository.ts
854
- async function allRows(query) {
855
- const q = query;
856
- if (typeof q.all === "function") return q.all();
857
- return await query;
858
- }
859
- async function oneRow(query) {
860
- const q = query;
861
- if (typeof q.get === "function") return q.get();
862
- return (await query)[0];
863
- }
864
- async function runQuery(query) {
865
- const q = query;
866
- if (typeof q.run === "function") {
867
- q.run();
868
- return;
869
- }
870
- await query;
871
- }
872
- function identityOfSession(row) {
873
- return {
874
- formatVersion: row.fVersion,
875
- createdAt: row.fCreatedAt,
876
- ...row.fCwd === null ? {} : { cwd: row.fCwd },
877
- isSeeded: row.fSeedLength !== null,
878
- ...row.fSeedLength === null ? {} : { inheritedEventCount: SessionLogOffset(row.fSeedLength) }
879
- };
880
- }
881
- function seqCursor(seq) {
882
- return seq === -1 ? -1 : SessionSeq(seq);
883
- }
884
- function pendingMutationOf(row) {
885
- const operation = row.fPendingOperation;
886
- if (operation !== "create" && operation !== "delete") return void 0;
887
- return {
888
- operation,
889
- workspaceId: row.fPendingWorkspaceId ?? ""
890
- };
891
- }
892
- function taskFromRow(row) {
893
- return {
894
- sessionId: row.fSessionId,
895
- record: JSON.parse(row.fRecord),
896
- status: row.fStatus === "inactive" ? "inactive" : "active",
897
- ...row.fLastDelivery === null ? {} : { lastDelivery: JSON.parse(row.fLastDelivery) },
898
- ...row.fDeliveryHistory === null ? {} : { deliveryHistory: JSON.parse(row.fDeliveryHistory) }
899
- };
900
- }
901
- function rowFromTask(id, task) {
902
- return {
903
- fId: id,
904
- fSessionId: task.sessionId,
905
- fStatus: task.status === "inactive" ? "inactive" : "active",
906
- fRecord: JSON.stringify(task.record),
907
- fLastDelivery: task.lastDelivery === void 0 ? null : JSON.stringify(task.lastDelivery),
908
- fDeliveryHistory: task.deliveryHistory === void 0 ? null : JSON.stringify(task.deliveryHistory)
909
- };
910
- }
911
- function createStorageRepository(host) {
912
- const tables = host.tables;
913
- const tUnits = tables["t_storage_units"];
914
- const tSessions = tables["t_sessions"];
915
- const tWorkspaces = tables["t_workspaces"];
916
- const tWorkspaceSessions = tables["t_workspace_sessions"];
917
- const tWorkspaceState = tables["t_workspace_state"];
918
- const tScheduleTasks = tables["t_schedule_tasks"];
919
- const tProjcacheRows = tables["t_session_projcache_row"];
920
- const sessionIdentity = {
921
- fSessionId: tSessions.fSessionId,
922
- fVersion: tSessions.fVersion,
923
- fCreatedAt: tSessions.fCreatedAt,
924
- fCwd: tSessions.fCwd,
925
- fSeedLength: tSessions.fSeedLength
926
- };
927
- const dbx = async () => await host.db();
928
- return {
929
- async readUnitVersion(name) {
930
- return (await oneRow((await dbx()).select().from(tUnits).where(eq(tUnits.fName, name))))?.fVersion;
931
- },
932
- async insertUnitVersion(name, version) {
933
- await runQuery((await dbx()).insert(tUnits).values({
934
- fName: name,
935
- fVersion: version
936
- }).onConflictDoNothing());
937
- },
938
- async listWorkspaces() {
939
- const db = await dbx();
940
- const rows = await allRows(db.select().from(tWorkspaces));
941
- const links = await allRows(db.select().from(tWorkspaceSessions));
942
- const sessions = /* @__PURE__ */ new Map();
943
- for (const link of links) {
944
- const owned = sessions.get(link.fWorkspaceId) ?? [];
945
- owned.push({
946
- id: link.fSessionId,
947
- position: link.fPosition
948
- });
949
- sessions.set(link.fWorkspaceId, owned);
950
- }
951
- return rows.map((row) => ({
952
- id: row.fWorkspaceId,
953
- record: {
954
- path: row.fPath,
955
- title: row.fTitle,
956
- sessionIds: (sessions.get(row.fWorkspaceId) ?? []).sort((left, right) => left.position - right.position).map((entry) => SessionId(entry.id)),
957
- createdAt: row.fCreatedAt,
958
- updatedAt: row.fUpdatedAt
959
- }
960
- }));
961
- },
962
- async putWorkspace(id, record) {
963
- await host.writeAtomically(async () => {
964
- const db = await dbx();
965
- const values = {
966
- fWorkspaceId: id,
967
- fPath: record.path,
968
- fTitle: record.title,
969
- fCreatedAt: record.createdAt,
970
- fUpdatedAt: record.updatedAt
971
- };
972
- await runQuery(db.insert(tWorkspaces).values(values).onConflictDoUpdate({
973
- target: tWorkspaces.fWorkspaceId,
974
- set: values
975
- }));
976
- await runQuery(db.delete(tWorkspaceSessions).where(eq(tWorkspaceSessions.fWorkspaceId, id)));
977
- const links = record.sessionIds.map((sessionId, position) => ({
978
- fWorkspaceId: id,
979
- fSessionId: sessionId,
980
- fPosition: position
981
- }));
982
- if (links.length > 0) await runQuery(db.insert(tWorkspaceSessions).values(links));
983
- });
984
- },
985
- async deleteWorkspace(id) {
986
- await host.writeAtomically(async () => {
987
- const db = await dbx();
988
- await runQuery(db.delete(tWorkspaceSessions).where(eq(tWorkspaceSessions.fWorkspaceId, id)));
989
- await runQuery(db.delete(tWorkspaces).where(eq(tWorkspaces.fWorkspaceId, id)));
990
- });
991
- },
992
- async readWorkspaceState() {
993
- const db = await dbx();
994
- const row = await oneRow(db.select().from(tWorkspaceState).where(eq(tWorkspaceState.fSingleton, 1)));
995
- if (row === void 0) return null;
996
- const ordered = await allRows(db.select().from(tWorkspaces).where(gte(tWorkspaces.fPosition, 0)).orderBy(tWorkspaces.fPosition));
997
- const archives = await allRows(db.select({ fSessionId: tSessions.fSessionId }).from(tSessions).where(isNotNull(tSessions.fArchivedAt)).orderBy(tSessions.fArchivedAt));
998
- const pinned = await allRows(db.select({
999
- fSessionId: tSessions.fSessionId,
1000
- fPinnedSeq: tSessions.fPinnedSeq
1001
- }).from(tSessions).where(isNotNull(tSessions.fPinnedSeq)).orderBy(tSessions.fPinnedSeq));
1002
- const pendingMutation = pendingMutationOf(row);
1003
- return {
1004
- initialized: row.fInitialized !== 0,
1005
- workspaceIds: ordered.map((entry) => entry.fWorkspaceId),
1006
- archivedSessionIds: archives.map((entry) => entry.fSessionId),
1007
- pinnedSessionIds: pinned.map((entry) => entry.fSessionId),
1008
- ...pendingMutation === void 0 ? {} : { pendingMutation }
1009
- };
1010
- },
1011
- async writeWorkspaceState(state) {
1012
- await host.writeAtomically(async () => {
1013
- const db = await dbx();
1014
- const pending = state.pendingMutation;
1015
- const values = {
1016
- fSingleton: 1,
1017
- fInitialized: state.initialized ? 1 : 0,
1018
- fPendingOperation: pending === void 0 || typeof pending.operation !== "string" ? null : pending.operation,
1019
- fPendingWorkspaceId: pending === void 0 || typeof pending.workspaceId !== "string" ? null : pending.workspaceId
1020
- };
1021
- await runQuery(db.insert(tWorkspaceState).values(values).onConflictDoUpdate({
1022
- target: tWorkspaceState.fSingleton,
1023
- set: values
1024
- }));
1025
- await runQuery(db.update(tWorkspaces).set({ fPosition: -1 }));
1026
- for (const [position, workspaceId] of state.workspaceIds.entries()) await runQuery(db.update(tWorkspaces).set({ fPosition: position }).where(eq(tWorkspaces.fWorkspaceId, workspaceId)));
1027
- const pinned = state.pinnedSessionIds ?? [];
1028
- 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))));
1029
- for (const [position, sessionId] of pinned.entries()) await runQuery(db.update(tSessions).set({ fPinnedSeq: position }).where(eq(tSessions.fSessionId, sessionId)));
1030
- const archived = state.archivedSessionIds;
1031
- 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))));
1032
- const stamp = Date.now();
1033
- for (const sessionId of archived) await runQuery(db.insert(tSessions).values({
1034
- fSessionId: sessionId,
1035
- fHeadEventId: "",
1036
- fHeadSequence: -1,
1037
- fVersion: SESSION_FORMAT_VERSION,
1038
- fCreatedAt: stamp,
1039
- fCwd: null,
1040
- fParentSession: null,
1041
- fSeedLength: null,
1042
- fOrigin: null,
1043
- fDelegationDepth: null,
1044
- fIncarnation: randomUUID(),
1045
- fRevision: 0,
1046
- fArchivedAt: stamp
1047
- }).onConflictDoUpdate({
1048
- target: tSessions.fSessionId,
1049
- set: { fArchivedAt: sql`COALESCE(${tSessions.fArchivedAt}, ${stamp})` }
1050
- }));
1051
- });
1052
- },
1053
- async listScheduleTasks() {
1054
- return (await allRows((await dbx()).select().from(tScheduleTasks))).map((row) => ({
1055
- id: row.fId,
1056
- task: taskFromRow(row)
1057
- }));
1058
- },
1059
- async putScheduleTask(id, task) {
1060
- await host.writeAtomically(async () => {
1061
- const db = await dbx();
1062
- const values = rowFromTask(id, task);
1063
- await runQuery(db.insert(tScheduleTasks).values(values).onConflictDoUpdate({
1064
- target: tScheduleTasks.fId,
1065
- set: values
1066
- }));
1067
- });
1068
- },
1069
- async deleteScheduleTask(id) {
1070
- await host.writeAtomically(async () => {
1071
- await runQuery((await dbx()).delete(tScheduleTasks).where(eq(tScheduleTasks.fId, id)));
1072
- });
1073
- },
1074
- async loadProjcache() {
1075
- const db = await dbx();
1076
- const rows = await allRows(db.select().from(tProjcacheRows));
1077
- if (rows.length === 0) return [];
1078
- const sessions = await allRows(db.select(sessionIdentity).from(tSessions));
1079
- const entries = /* @__PURE__ */ new Map();
1080
- for (const session of sessions) entries.set(session.fSessionId, {
1081
- sessionId: SessionId(session.fSessionId),
1082
- identity: identityOfSession(session),
1083
- rows: {}
1084
- });
1085
- for (const row of rows) {
1086
- const entry = entries.get(row.fSessionId);
1087
- if (entry === void 0) continue;
1088
- entry.rows[row.fKey] = {
1089
- ver: row.fVer,
1090
- seq: seqCursor(row.fSeq),
1091
- val: JSON.parse(row.fVal)
1092
- };
1093
- }
1094
- return [...entries.values()].filter((entry) => Object.keys(entry.rows).length > 0);
1095
- },
1096
- async putProjcache(sessionId, rows) {
1097
- await host.writeAtomically(async () => {
1098
- const db = await dbx();
1099
- await runQuery(db.delete(tProjcacheRows).where(eq(tProjcacheRows.fSessionId, sessionId)));
1100
- const values = Object.entries(rows).map(([key, row]) => ({
1101
- fSessionId: sessionId,
1102
- fKey: key,
1103
- fVer: row.ver,
1104
- fSeq: row.seq,
1105
- fVal: JSON.stringify(row.val)
1106
- }));
1107
- if (values.length > 0) await runQuery(db.insert(tProjcacheRows).values(values));
1108
- });
1109
- },
1110
- async pruneStaleProjcache() {
1111
- const db = await dbx();
1112
- const stale = await allRows(db.select({ fSessionId: tProjcacheRows.fSessionId }).from(tProjcacheRows).where(and(lt(tProjcacheRows.fSeq, 0), inArray(tProjcacheRows.fSessionId, db.select({ fSessionId: tSessions.fSessionId }).from(tSessions).where(gte(tSessions.fHeadSequence, 0))))));
1113
- if (stale.length === 0) return 0;
1114
- await runQuery(db.delete(tProjcacheRows).where(inArray(tProjcacheRows.fSessionId, stale.map((row) => row.fSessionId))));
1115
- return stale.length;
1116
- },
1117
- ...host.dbDirect === void 0 ? {} : {
1118
- readProjcacheDirect: (sessionId) => {
1119
- const db = host.dbDirect();
1120
- const stored = db.select().from(tProjcacheRows).where(eq(tProjcacheRows.fSessionId, sessionId)).all();
1121
- if (stored.length === 0) return void 0;
1122
- const session = db.select(sessionIdentity).from(tSessions).where(eq(tSessions.fSessionId, sessionId)).get();
1123
- if (session === void 0) return void 0;
1124
- const rows = {};
1125
- for (const row of stored) rows[row.fKey] = {
1126
- ver: row.fVer,
1127
- seq: seqCursor(row.fSeq),
1128
- val: JSON.parse(row.fVal)
1129
- };
1130
- return {
1131
- sessionId: SessionId(sessionId),
1132
- identity: identityOfSession(session),
1133
- rows
1134
- };
1135
- },
1136
- readSessionTitleDirect: (sessionId) => {
1137
- const row = host.dbDirect().select({
1138
- fTitle: tSessions.fTitle,
1139
- fTitleSeq: tSessions.fTitleSeq
1140
- }).from(tSessions).where(eq(tSessions.fSessionId, sessionId)).get();
1141
- if (row === void 0 || row.fTitle === null || row.fTitleSeq === null) return void 0;
1142
- return {
1143
- title: row.fTitle,
1144
- seq: row.fTitleSeq
1145
- };
1146
- }
1147
- }
1148
- };
1149
- }
1150
- //#endregion
1151
- //#region src/sqlite.ts
1152
- const EVENT_REFERENCED_SQL = `EXISTS (SELECT 1 FROM t_session_events rb
1153
- WHERE rb.f_event_id = %EVENT%)`;
1154
- const EVENT_SUBAGENT_SQL = `EXISTS (SELECT 1 FROM t_session_events sb
1155
- JOIN t_sessions ss ON ss.f_session_id = sb.f_session_id
1156
- WHERE sb.f_event_id = %EVENT% AND ss.f_origin = 'subagent')`;
1157
- const COUNTED_EVENT_TYPE_SQL = COUNTED_EVENT_TYPES.map((type) => `'${type}'`).join(", ");
1158
- const REFRESH_FLAG_CHUNK = 400;
1159
- function withEvent(expression, column) {
1160
- return expression.replace("%EVENT%", column);
1161
- }
1162
- function backfillUsageTables(db) {
1163
- backfillEventUsage(db);
1164
- backfillSessionUsage(db);
1165
- backfillSessionCounts(db);
1166
- }
1167
- function tableIsEmpty(db, table) {
1168
- return db.prepare(`SELECT count(*) AS n FROM ${table}`).get().n === 0;
1169
- }
1170
- function backfillEventUsage(db) {
1171
- if (!tableIsEmpty(db, "t_event_usage")) return;
1172
- db.exec(`INSERT OR IGNORE INTO t_event_usage (
1173
- f_event_id, f_created_at, f_day, f_provider, f_model, f_referenced, f_subagent,
1174
- f_input_tokens, f_output_tokens, f_cache_read_tokens, f_reasoning_tokens, f_total_tokens)
1175
- SELECT e.f_event_id, e.f_created_at,
1176
- date(e.f_created_at / 1000, 'unixepoch', 'localtime'),
1177
- coalesce(json_extract(e.f_data, '$.data.message.source.provider'),
1178
- json_extract(e.f_data, '$.message.source.provider')),
1179
- coalesce(json_extract(e.f_data, '$.data.message.source.model'),
1180
- json_extract(e.f_data, '$.message.source.model')),
1181
- ${withEvent(EVENT_REFERENCED_SQL, "e.f_event_id")},
1182
- ${withEvent(EVENT_SUBAGENT_SQL, "e.f_event_id")},
1183
- coalesce(json_extract(e.f_data, '$.data.usage.inputTokens'),
1184
- json_extract(e.f_data, '$.usage.inputTokens'), 0),
1185
- coalesce(json_extract(e.f_data, '$.data.usage.outputTokens'),
1186
- json_extract(e.f_data, '$.usage.outputTokens'), 0),
1187
- coalesce(json_extract(e.f_data, '$.data.usage.cacheReadTokens'),
1188
- json_extract(e.f_data, '$.usage.cacheReadTokens'), 0),
1189
- coalesce(json_extract(e.f_data, '$.data.usage.reasoningTokens'),
1190
- json_extract(e.f_data, '$.usage.reasoningTokens'), 0),
1191
- coalesce(json_extract(e.f_data, '$.data.usage.totalTokens'),
1192
- json_extract(e.f_data, '$.usage.totalTokens'), 0)
1193
- FROM t_events e
1194
- WHERE e.f_type = 'assistant/message'
1195
- AND (json_extract(e.f_data, '$.data.usage') IS NOT NULL
1196
- OR json_extract(e.f_data, '$.usage') IS NOT NULL)`);
1197
- }
1198
- function backfillSessionUsage(db) {
1199
- if (!tableIsEmpty(db, "t_session_usage")) return;
1200
- db.exec(`INSERT OR IGNORE INTO t_session_usage (
1201
- f_session_id, f_day, f_provider, f_model,
1202
- f_input_tokens, f_output_tokens, f_cache_read_tokens, f_reasoning_tokens, f_total_tokens)
1203
- SELECT b.f_session_id, u.f_day, coalesce(u.f_provider, ''), coalesce(u.f_model, ''),
1204
- sum(u.f_input_tokens), sum(u.f_output_tokens), sum(u.f_cache_read_tokens),
1205
- sum(u.f_reasoning_tokens), sum(u.f_total_tokens)
1206
- FROM t_session_events b
1207
- JOIN t_event_usage u ON u.f_event_id = b.f_event_id
1208
- GROUP BY b.f_session_id, u.f_day, coalesce(u.f_provider, ''), coalesce(u.f_model, '')`);
1209
- }
1210
- function backfillSessionCounts(db) {
1211
- if (!tableIsEmpty(db, "t_session_counts")) return;
1212
- db.exec(`INSERT OR IGNORE INTO t_session_counts (
1213
- f_session_id, f_day, f_turns, f_steps, f_user_inputs, f_tool_calls)
1214
- SELECT b.f_session_id,
1215
- date(e.f_created_at / 1000, 'unixepoch', 'localtime'),
1216
- sum(CASE WHEN e.f_type = 'turn/start' THEN 1 ELSE 0 END),
1217
- sum(CASE WHEN e.f_type = 'step/start' THEN 1 ELSE 0 END),
1218
- sum(CASE WHEN e.f_type = 'user/message' THEN 1 ELSE 0 END),
1219
- sum(CASE WHEN e.f_type = 'tool/call' THEN 1 ELSE 0 END)
1220
- FROM t_session_events b
1221
- JOIN t_events e ON e.f_event_id = b.f_event_id
1222
- WHERE e.f_type IN (${COUNTED_EVENT_TYPE_SQL})
1223
- GROUP BY b.f_session_id, 2`);
1224
- }
1225
- function sessionListRowOf(row) {
1226
- return {
1227
- sessionId: typeof row["session_id"] === "string" ? row["session_id"] : String(row["session_id"]),
1228
- title: typeof row["title"] === "string" ? row["title"] : null,
1229
- origin: typeof row["origin"] === "string" ? row["origin"] : null,
1230
- cwd: typeof row["cwd"] === "string" ? row["cwd"] : null,
1231
- createdAt: Number(row["created_at"]),
1232
- updatedAt: Number(row["updated_at"]),
1233
- archived: row["archived"] === 1 || row["archived"] === true,
1234
- subagent: row["origin"] === "subagent",
1235
- workspace: typeof row["workspace"] === "string" ? row["workspace"] : null
1236
- };
1237
- }
1238
- function tokenTotalsOf(row) {
1239
- return {
1240
- inputTokens: row.input_tokens ?? 0,
1241
- outputTokens: row.output_tokens ?? 0,
1242
- cacheReadTokens: row.cache_read_tokens ?? 0,
1243
- reasoningTokens: row.reasoning_tokens ?? 0,
1244
- totalTokens: row.total_tokens ?? 0
1245
- };
1246
- }
1247
- function activityTotalsOf(row) {
1248
- return {
1249
- turns: row.turns ?? 0,
1250
- steps: row.steps ?? 0,
1251
- userInputs: row.user_inputs ?? 0,
1252
- toolCalls: row.tool_calls ?? 0
1253
- };
1254
- }
1255
- function emptyActivity() {
1256
- return {
1257
- turns: 0,
1258
- steps: 0,
1259
- userInputs: 0,
1260
- toolCalls: 0
1261
- };
1262
- }
1263
- function hasUsageSession(column) {
1264
- return `EXISTS (SELECT 1 FROM t_session_usage ub WHERE ub.f_session_id = ${column})`;
1265
- }
1266
- const sqliteMigrationsDir = fileURLToPath(new URL("../drizzle/sqlite/", import.meta.url));
1267
- const sqliteTxQueues = /* @__PURE__ */ new Map();
1268
- function enqueueSqliteTx(path, fn) {
1269
- const run = (sqliteTxQueues.get(path) ?? Promise.resolve()).then(fn);
1270
- sqliteTxQueues.set(path, run.then(() => void 0, () => void 0));
1271
- return run;
1272
- }
1273
- async function createDatabaseFile(path) {
1274
- try {
1275
- await (await open(path, "wx", 384)).close();
1276
- } catch (error) {
1277
- if (error.code !== "EEXIST") throw error;
1278
- }
1279
- }
1280
- async function openDatabase(path, journalMode, busyTimeout = DEFAULT_BUSY_TIMEOUT_MS) {
1281
- const db = new DatabaseSync(path);
1282
- try {
1283
- configureDatabase(db, path, journalMode, busyTimeout);
1284
- const dbx = drizzle({ client: db });
1285
- const { user_version: onDisk } = db.prepare("PRAGMA user_version").get();
1286
- if (onDisk === 2 && !hasMigrationsTable(db)) await baselineV2(db);
1287
- migrate(dbx, { migrationsFolder: sqliteMigrationsDir });
1288
- backfillUsageTables(db);
1289
- dbx.insert(tPersistenceState).values({
1290
- fSingleton: 1,
1291
- fStoreId: randomUUID()
1292
- }).onConflictDoNothing().run();
1293
- if (onDisk === 0 || onDisk === 2) db.exec(`PRAGMA user_version = 3`);
1294
- db.exec(`PRAGMA journal_mode = ${journalMode.toUpperCase()}`);
1295
- return db;
1296
- } catch (error) {
1297
- db.close();
1298
- throw error;
1299
- }
1300
- }
1301
- function hasMigrationsTable(db) {
1302
- return db.prepare("SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = '__drizzle_migrations'").get() !== void 0;
1303
- }
1304
- async function baselineV2(db) {
1305
- const dir = (await readdir(sqliteMigrationsDir)).find((name) => name.endsWith("_v2_initial"));
1306
- if (dir === void 0) throw new Error("missing v2 baseline migration in drizzle/sqlite");
1307
- db.exec(`
1308
- CREATE TABLE __drizzle_migrations (
1309
- id INTEGER PRIMARY KEY,
1310
- hash text NOT NULL,
1311
- created_at numeric,
1312
- name text,
1313
- applied_at TEXT
1314
- )
1315
- `);
1316
- db.prepare("INSERT INTO __drizzle_migrations (hash, created_at, name, applied_at) VALUES (?, ?, ?, ?)").run("baseline", 0, dir, (/* @__PURE__ */ new Date()).toISOString());
1317
- }
1318
- function configureDatabase(db, path, journalMode, busyTimeout) {
1319
- db.exec("PRAGMA foreign_keys = ON");
1320
- db.exec(`PRAGMA busy_timeout = ${busyTimeout}`);
1321
- drizzle({ client: db }).transaction((tx) => {
1322
- const { user_version: onDisk } = tx.get(sql`PRAGMA user_version`);
1323
- const { application_id: applicationId } = tx.get(sql`PRAGMA application_id`);
1324
- const { count: userObjectCount } = tx.get(sql`SELECT COUNT(*) AS count FROM sqlite_schema WHERE name NOT GLOB 'sqlite_*'`);
1325
- if (onDisk === 0 && (applicationId !== 0 || userObjectCount > 0)) throw new Error(`session database at "${path}" has an unversioned schema or application identity`);
1326
- if (onDisk !== 0 && onDisk !== 2 && onDisk !== 3) throw new Error(`session database at "${path}" has schema version ${onDisk}, incompatible with this build (3)`);
1327
- if (onDisk >= 1 && applicationId !== 1146308688) throw new Error(`session database at "${path}" has application id ${applicationId}, expected ${SESSION_PERSISTENCE_SQLITE_APPLICATION_ID}`);
1328
- if (onDisk === 0) {
1329
- tx.run(sql.raw(`PRAGMA application_id = ${SESSION_PERSISTENCE_SQLITE_APPLICATION_ID}`));
1330
- tx.run(sql.raw(`PRAGMA user_version = 3`));
1331
- }
1332
- }, { behavior: "immediate" });
1333
- }
1334
- var SqliteBackend = class SqliteBackend {
1335
- options;
1336
- kind = "sqlite";
1337
- storeIdentity;
1338
- storage;
1339
- dbPath = "";
1340
- db;
1341
- dbReady;
1342
- resolveDb;
1343
- rejectDb;
1344
- constructor(options) {
1345
- this.options = options;
1346
- this.dbReady = new Promise((resolve, reject) => {
1347
- this.resolveDb = resolve;
1348
- this.rejectDb = reject;
1349
- });
1350
- this.dbReady.catch(() => {});
1351
- this.storage = createStorageRepository({
1352
- db: () => this.dbReady,
1353
- dbDirect: () => {
1354
- if (this.db === void 0) throw new Error("sqlite session database is not open");
1355
- return this.db;
1356
- },
1357
- writeAtomically: (fn) => enqueueSqliteTx(this.dbPath, async () => {
1358
- const db = await this.dbReady;
1359
- db.$client.exec("BEGIN IMMEDIATE");
1360
- try {
1361
- const result = await fn();
1362
- db.$client.exec("COMMIT");
1363
- return result;
1364
- } catch (error) {
1365
- try {
1366
- db.$client.exec("ROLLBACK");
1367
- } catch {}
1368
- throw error;
1369
- }
1370
- }),
1371
- tables: {
1372
- t_sessions: tSessions,
1373
- t_storage_units: tStorageUnits,
1374
- t_workspaces: tWorkspaces,
1375
- t_workspace_sessions: tWorkspaceSessions,
1376
- t_workspace_state: tWorkspaceState,
1377
- t_schedule_tasks: tScheduleTasks,
1378
- t_session_projcache_row: tSessionProjcacheRows
1379
- }
1380
- });
1381
- }
1382
- async open() {
1383
- try {
1384
- await this.doOpen();
1385
- this.resolveDb(this.db);
1386
- } catch (error) {
1387
- this.rejectDb(error);
1388
- throw error;
1389
- }
1390
- }
1391
- async doOpen() {
1392
- const actual = this.options.path === ":memory:" ? this.options.path : resolve(this.options.path);
1393
- this.dbPath = actual;
1394
- if (actual !== ":memory:") {
1395
- await mkdir(dirname(actual), {
1396
- recursive: true,
1397
- mode: 448
1398
- });
1399
- await createDatabaseFile(actual);
1400
- }
1401
- await enqueueSqliteTx(actual, async () => {
1402
- this.db = drizzle({ client: await openDatabase(actual, this.options.journalMode, this.options.busyTimeout) });
1403
- migrate(this.db, { migrationsFolder: sqliteMigrationsDir });
1404
- });
1405
- try {
1406
- const row = this.db.select({ fStoreId: tPersistenceState.fStoreId }).from(tPersistenceState).where(eq(tPersistenceState.fSingleton, 1)).get();
1407
- if (row === void 0) throw new Error(`session database at "${actual}" has no store identity`);
1408
- if (row.fStoreId.length === 0) throw new Error(`session database at "${actual}" has no valid store identity`);
1409
- if (actual !== ":memory:") {
1410
- const identity = await stat(actual, { bigint: true });
1411
- this.storeIdentity = `file:${identity.dev}:${identity.ino}:${identity.birthtimeNs}:store:${row.fStoreId}`;
1412
- } else this.storeIdentity = `memory:store:${row.fStoreId}`;
1413
- } catch (error) {
1414
- this.db.$client.close();
1415
- throw error;
1416
- }
1417
- }
1418
- async close() {
1419
- if (this.db === void 0) return;
1420
- this.db.$client.close();
1421
- }
1422
- async getSession(id) {
1423
- return this.db.select().from(tSessions).where(eq(tSessions.fSessionId, id)).get();
1424
- }
1425
- async getEventRows(id, fromSequence) {
1426
- return (fromSequence === void 0 ? this.eventRows().where(eq(tSessionEvents.fSessionId, id)) : this.eventRows().where(and(eq(tSessionEvents.fSessionId, id), gte(tSessionEvents.fSequence, fromSequence)))).orderBy(tSessionEvents.fSequence).all();
1427
- }
1428
- async getEventTypeAt(id, sequence) {
1429
- return this.db.select({ fType: tEvents.fType }).from(tSessionEvents).innerJoin(tEvents, eq(tSessionEvents.fEventId, tEvents.fEventId)).where(and(eq(tSessionEvents.fSessionId, id), eq(tSessionEvents.fSequence, sequence))).get()?.fType;
1430
- }
1431
- async getEventTypesBefore(id, beforeSequence, limit) {
1432
- return this.db.select({
1433
- fSequence: tSessionEvents.fSequence,
1434
- fType: tEvents.fType
1435
- }).from(tSessionEvents).innerJoin(tEvents, eq(tSessionEvents.fEventId, tEvents.fEventId)).where(and(eq(tSessionEvents.fSessionId, id), lt(tSessionEvents.fSequence, beforeSequence))).orderBy(desc(tSessionEvents.fSequence)).limit(limit).all();
1436
- }
1437
- async listSessions() {
1438
- return this.db.select().from(tSessions).all();
1439
- }
1440
- async transaction(fn) {
1441
- return enqueueSqliteTx(this.dbPath, async () => {
1442
- this.db.$client.exec("BEGIN IMMEDIATE");
1443
- try {
1444
- const result = await fn(this.tx);
1445
- this.db.$client.exec("COMMIT");
1446
- return result;
1447
- } catch (error) {
1448
- try {
1449
- this.db.$client.exec("ROLLBACK");
1450
- } catch {}
1451
- throw error;
1452
- }
1453
- });
1454
- }
1455
- tx = {
1456
- upsertSession: (storage, incarnation) => this.upsertSession(storage, incarnation),
1457
- getHead: (id) => this.getHead(id),
1458
- getSeedLength: (id) => this.getSeedLength(id),
1459
- updateSeedLength: (id, seedLength) => this.updateSeedLength(id, seedLength),
1460
- insertEvents: (events, usageRows) => this.insertEvents(events, usageRows),
1461
- insertBridges: (rows) => this.insertBridges(rows),
1462
- updateHead: (id, headEventId, headSequence) => this.updateHead(id, headEventId, headSequence),
1463
- bumpRevision: (id, lastEventAt) => this.bumpRevision(id, lastEventAt),
1464
- refreshTitle: (id) => this.refreshTitle(id),
1465
- deleteBridgeTail: (id, fromSequence) => this.deleteBridgeTail(id, fromSequence),
1466
- getPrevBridge: (id, sequence) => this.getPrevBridge(id, sequence),
1467
- deleteSession: (id) => this.deleteSession(id),
1468
- deleteSessions: (ids) => this.deleteSessions(ids)
1469
- };
1470
- async upsertSession(storage, incarnation) {
1471
- this.db.insert(tSessions).values(sessionInsertRow(storage, incarnation)).onConflictDoUpdate({
1472
- target: tSessions.fSessionId,
1473
- set: sessionConflictRow(storage)
1474
- }).run();
1475
- }
1476
- async getHead(id) {
1477
- const head = this.db.select({
1478
- fHeadEventId: tSessions.fHeadEventId,
1479
- fHeadSequence: tSessions.fHeadSequence
1480
- }).from(tSessions).where(eq(tSessions.fSessionId, id)).get();
1481
- if (head === void 0) throw new Error(`session "${id}" has no materialized row`);
1482
- return head;
1483
- }
1484
- async getSeedLength(id) {
1485
- const row = this.db.select({ fSeedLength: tSessions.fSeedLength }).from(tSessions).where(eq(tSessions.fSessionId, id)).get();
1486
- if (row === void 0) throw new Error(`session "${id}" has no materialized row`);
1487
- return row.fSeedLength;
1488
- }
1489
- async updateSeedLength(id, seedLength) {
1490
- this.db.update(tSessions).set({ fSeedLength: seedLength }).where(eq(tSessions.fSessionId, id)).run();
1491
- }
1492
- static INSERT_BATCH_ROWS = 1e3;
1493
- async insertEvents(events, usageRows) {
1494
- if (events.length === 0) return;
1495
- for (let i = 0; i < events.length; i += SqliteBackend.INSERT_BATCH_ROWS) this.db.insert(tEvents).values(events.slice(i, i + SqliteBackend.INSERT_BATCH_ROWS).map((event) => ({ ...event }))).run();
1496
- if (usageRows.length > 0) this.db.insert(tEventUsage).values(usageRows.map((row) => ({ ...row }))).onConflictDoNothing().run();
1497
- }
1498
- async insertBridges(rows) {
1499
- if (rows.length === 0) return;
1500
- for (let i = 0; i < rows.length; i += SqliteBackend.INSERT_BATCH_ROWS) this.db.insert(tSessionEvents).values(rows.slice(i, i + SqliteBackend.INSERT_BATCH_ROWS).map((row) => ({ ...row }))).run();
1501
- }
1502
- async updateHead(id, headEventId, headSequence) {
1503
- this.db.update(tSessions).set({
1504
- fHeadEventId: headEventId,
1505
- fHeadSequence: headSequence
1506
- }).where(eq(tSessions.fSessionId, id)).run();
1507
- }
1508
- async bumpRevision(id, lastEventAt) {
1509
- this.db.update(tSessions).set({
1510
- fRevision: sql`${tSessions.fRevision} + 1`,
1511
- ...lastEventAt === void 0 ? {} : { fLastEventAt: sql`MAX(COALESCE(${tSessions.fLastEventAt}, 0), ${lastEventAt})` }
1512
- }).where(eq(tSessions.fSessionId, id)).run();
1513
- }
1514
- async refreshTitle(id) {
1515
- const row = this.db.select({
1516
- fSequence: tSessionEvents.fSequence,
1517
- fData: tEvents.fData
1518
- }).from(tSessionEvents).innerJoin(tEvents, eq(tEvents.fEventId, tSessionEvents.fEventId)).where(and(eq(tSessionEvents.fSessionId, id), eq(tEvents.fType, "session/title"))).orderBy(desc(tSessionEvents.fSequence)).limit(1).get();
1519
- const title = row === void 0 ? void 0 : titleOfEventData(row.fData);
1520
- this.db.update(tSessions).set({
1521
- fTitle: title ?? null,
1522
- fTitleSeq: title === void 0 ? null : row.fSequence
1523
- }).where(eq(tSessions.fSessionId, id)).run();
1524
- }
1525
- async deleteBridgeTail(id, fromSequence) {
1526
- return this.db.$client.prepare("DELETE FROM t_session_events WHERE f_session_id = ? AND f_sequence >= ? RETURNING f_event_id").all(id, fromSequence).map((row) => row.f_event_id);
1527
- }
1528
- async getPrevBridge(id, sequence) {
1529
- return this.db.select({
1530
- fEventId: tSessionEvents.fEventId,
1531
- fSequence: tSessionEvents.fSequence
1532
- }).from(tSessionEvents).where(and(eq(tSessionEvents.fSessionId, id), eq(tSessionEvents.fSequence, sequence))).get();
1533
- }
1534
- async deleteSession(id) {
1535
- const rows = this.db.$client.prepare("DELETE FROM t_session_events WHERE f_session_id = ? RETURNING f_event_id").all(id);
1536
- this.db.delete(tWorkspaceSessions).where(eq(tWorkspaceSessions.fSessionId, id)).run();
1537
- this.db.delete(tSessionProjcacheRows).where(eq(tSessionProjcacheRows.fSessionId, id)).run();
1538
- this.db.delete(tSessions).where(eq(tSessions.fSessionId, id)).run();
1539
- return rows.map((row) => row.f_event_id);
1540
- }
1541
- async collectOrphans() {
1542
- const referenced = this.db.select({ fEventId: tSessionEvents.fEventId }).from(tSessionEvents);
1543
- const info = this.db.delete(tEvents).where(notInArray(tEvents.fEventId, referenced)).run();
1544
- this.db.delete(tEventUsage).where(notInArray(tEventUsage.fEventId, this.db.select({ fEventId: tEvents.fEventId }).from(tEvents))).run();
1545
- return Number(info.changes);
1546
- }
1547
- async listOrphanSubagentSessions() {
1548
- return this.db.$client.prepare(`SELECT f_session_id AS id FROM t_sessions
1549
- WHERE f_origin = 'subagent'
1550
- AND (f_parent_session IS NULL
1551
- OR f_parent_session NOT IN (SELECT f_session_id FROM t_sessions))`).all().map((row) => row.id);
1552
- }
1553
- async deleteSessions(ids) {
1554
- if (ids.length === 0) return {
1555
- deleted: 0,
1556
- eventIds: []
1557
- };
1558
- const placeholders = ids.map(() => "?").join(", ");
1559
- const rows = this.db.$client.prepare(`DELETE FROM t_session_events WHERE f_session_id IN (${placeholders}) RETURNING f_event_id`).all(...ids);
1560
- this.db.delete(tWorkspaceSessions).where(inArray(tWorkspaceSessions.fSessionId, ids)).run();
1561
- this.db.delete(tSessionProjcacheRows).where(inArray(tSessionProjcacheRows.fSessionId, ids)).run();
1562
- const info = this.db.delete(tSessions).where(inArray(tSessions.fSessionId, ids)).run();
1563
- return {
1564
- deleted: Number(info.changes),
1565
- eventIds: rows.map((row) => row.f_event_id)
1566
- };
1567
- }
1568
- async listSessionRows(query = {}) {
1569
- const needle = query.query?.trim().toLowerCase() ?? "";
1570
- const like = `%${needle}%`;
1571
- const where = `WHERE (? = '' OR lower(COALESCE(s.f_title, '')) LIKE ? OR lower(COALESCE(w.f_title, '')) LIKE ?)
1572
- AND (? = 1 OR s.f_origin IS NOT 'subagent')`;
1573
- const filterParams = [
1574
- needle,
1575
- like,
1576
- like,
1577
- query.includeSubagents === true ? 1 : 0
1578
- ];
1579
- const limit = query.limit ?? 20;
1580
- const offset = query.offset ?? 0;
1581
- const rows = this.db.$client.prepare(`SELECT s.f_session_id AS session_id,
1582
- s.f_title AS title,
1583
- s.f_origin AS origin,
1584
- s.f_cwd AS cwd,
1585
- s.f_created_at AS created_at,
1586
- (s.f_archived_at IS NOT NULL) AS archived,
1587
- COALESCE(s.f_last_event_at, s.f_created_at) AS updated_at,
1588
- w.f_title AS workspace
1589
- FROM t_sessions s
1590
- LEFT JOIN t_workspaces w ON w.f_workspace_id = (
1591
- SELECT b.f_workspace_id FROM t_workspace_sessions b
1592
- WHERE b.f_session_id = s.f_session_id ORDER BY b.f_position LIMIT 1)
1593
- ${where}
1594
- ORDER BY updated_at DESC, s.f_session_id
1595
- LIMIT ? OFFSET ?`).all(...filterParams, limit, offset);
1596
- const counted = this.db.$client.prepare(`SELECT count(*) AS n
1597
- FROM t_sessions s
1598
- LEFT JOIN t_workspaces w ON w.f_workspace_id = (
1599
- SELECT b.f_workspace_id FROM t_workspace_sessions b
1600
- WHERE b.f_session_id = s.f_session_id ORDER BY b.f_position LIMIT 1)
1601
- ${where}`).get(...filterParams);
1602
- return {
1603
- items: rows.map((row) => sessionListRowOf(row)),
1604
- total: Number(counted.n)
1605
- };
1606
- }
1607
- async usageReport(sinceMs) {
1608
- const usageSince = sinceMs === void 0 ? "" : " AND f_created_at >= ?";
1609
- const params = sinceMs === void 0 ? [] : [sinceMs];
1610
- const daySince = sinceMs === void 0 ? "" : " AND %COL% >= ?";
1611
- const dayParams = sinceMs === void 0 ? [] : [localDayKey(sinceMs)];
1612
- const dayFilter = (column) => daySince.replace("%COL%", column);
1613
- const buckets = this.db.$client.prepare(`SELECT f_day AS day,
1614
- f_provider AS provider,
1615
- f_model AS model,
1616
- f_subagent AS subagent,
1617
- sum(f_input_tokens) AS input_tokens,
1618
- sum(f_output_tokens) AS output_tokens,
1619
- sum(f_cache_read_tokens) AS cache_read_tokens,
1620
- sum(f_reasoning_tokens) AS reasoning_tokens,
1621
- sum(f_total_tokens) AS total_tokens
1622
- FROM t_event_usage
1623
- WHERE f_referenced = 1${usageSince}
1624
- GROUP BY f_day, f_provider, f_model, f_subagent`).all(...params);
1625
- const tokenScope = this.db.$client.prepare(`SELECT f_subagent AS subagent,
1626
- sum(f_input_tokens) AS input_tokens,
1627
- sum(f_output_tokens) AS output_tokens,
1628
- sum(f_cache_read_tokens) AS cache_read_tokens,
1629
- sum(f_reasoning_tokens) AS reasoning_tokens,
1630
- sum(f_total_tokens) AS total_tokens
1631
- FROM t_event_usage
1632
- WHERE f_referenced = 1${usageSince}
1633
- GROUP BY f_subagent`).all(...params);
1634
- const activityScope = this.db.$client.prepare(`SELECT (s.f_origin = 'subagent') AS subagent,
1635
- sum(c.f_turns) AS turns,
1636
- sum(c.f_steps) AS steps,
1637
- sum(c.f_user_inputs) AS user_inputs,
1638
- sum(c.f_tool_calls) AS tool_calls
1639
- FROM t_session_counts c
1640
- JOIN t_sessions s ON s.f_session_id = c.f_session_id
1641
- WHERE ${hasUsageSession("c.f_session_id")}${dayFilter("c.f_day")}
1642
- GROUP BY subagent`).all(...dayParams);
1643
- const tokenSessions = this.db.$client.prepare(`SELECT u.f_session_id AS session_id,
1644
- s.f_title AS title,
1645
- (s.f_origin = 'subagent') AS subagent,
1646
- (s.f_archived_at IS NOT NULL) AS archived,
1647
- sum(u.f_input_tokens) AS input_tokens,
1648
- sum(u.f_output_tokens) AS output_tokens,
1649
- sum(u.f_cache_read_tokens) AS cache_read_tokens,
1650
- sum(u.f_reasoning_tokens) AS reasoning_tokens,
1651
- sum(u.f_total_tokens) AS total_tokens
1652
- FROM t_session_usage u
1653
- JOIN t_sessions s ON s.f_session_id = u.f_session_id
1654
- WHERE 1 = 1${dayFilter("u.f_day")}
1655
- GROUP BY u.f_session_id, s.f_title, s.f_origin, s.f_archived_at`).all(...dayParams);
1656
- const activitySessions = this.db.$client.prepare(`SELECT f_session_id AS session_id,
1657
- sum(f_turns) AS turns,
1658
- sum(f_steps) AS steps,
1659
- sum(f_user_inputs) AS user_inputs,
1660
- sum(f_tool_calls) AS tool_calls
1661
- FROM t_session_counts
1662
- WHERE 1 = 1${dayFilter("f_day")}
1663
- GROUP BY f_session_id`).all(...dayParams);
1664
- const human = emptyTotals();
1665
- const subagent = emptyTotals();
1666
- for (const row of tokenScope) addTokenTotals(row.subagent === 1 ? subagent : human, tokenTotalsOf(row));
1667
- for (const row of activityScope) addActivityTotals(row.subagent === 1 ? subagent : human, activityTotalsOf(row));
1668
- const totals = addTotals(addTotals(emptyTotals(), human), subagent);
1669
- const activityBySession = /* @__PURE__ */ new Map();
1670
- for (const row of activitySessions) activityBySession.set(row.session_id, activityTotalsOf(row));
1671
- return {
1672
- totals,
1673
- subagent,
1674
- human,
1675
- buckets: buckets.map((row) => ({
1676
- day: row.day,
1677
- provider: row.provider,
1678
- model: row.model,
1679
- subagent: row.subagent === 1,
1680
- ...tokenTotalsOf(row)
1681
- })),
1682
- sessions: tokenSessions.map((row) => ({
1683
- sessionId: row.session_id,
1684
- title: row.title,
1685
- subagent: row.subagent === 1,
1686
- archived: row.archived === 1,
1687
- ...tokenTotalsOf(row),
1688
- ...activityBySession.get(row.session_id) ?? emptyActivity()
1689
- }))
1690
- };
1691
- }
1692
- async incrementSessionUsage(id, buckets) {
1693
- if (buckets.length === 0) return;
1694
- const statement = this.db.$client.prepare(`INSERT INTO t_session_usage (f_session_id, f_day, f_provider, f_model,
1695
- f_input_tokens, f_output_tokens, f_cache_read_tokens, f_reasoning_tokens, f_total_tokens)
1696
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
1697
- ON CONFLICT(f_session_id, f_day, f_provider, f_model) DO UPDATE SET
1698
- f_input_tokens = f_input_tokens + excluded.f_input_tokens,
1699
- f_output_tokens = f_output_tokens + excluded.f_output_tokens,
1700
- f_cache_read_tokens = f_cache_read_tokens + excluded.f_cache_read_tokens,
1701
- f_reasoning_tokens = f_reasoning_tokens + excluded.f_reasoning_tokens,
1702
- f_total_tokens = f_total_tokens + excluded.f_total_tokens`);
1703
- for (const bucket of buckets) statement.run(id, bucket.day, bucket.provider ?? "", bucket.model ?? "", bucket.inputTokens, bucket.outputTokens, bucket.cacheReadTokens, bucket.reasoningTokens, bucket.totalTokens);
1704
- }
1705
- async incrementSessionCounts(id, buckets) {
1706
- if (buckets.length === 0) return;
1707
- const statement = this.db.$client.prepare(`INSERT INTO t_session_counts (f_session_id, f_day, f_turns, f_steps, f_user_inputs, f_tool_calls)
1708
- VALUES (?, ?, ?, ?, ?, ?)
1709
- ON CONFLICT(f_session_id, f_day) DO UPDATE SET
1710
- f_turns = f_turns + excluded.f_turns,
1711
- f_steps = f_steps + excluded.f_steps,
1712
- f_user_inputs = f_user_inputs + excluded.f_user_inputs,
1713
- f_tool_calls = f_tool_calls + excluded.f_tool_calls`);
1714
- for (const bucket of buckets) statement.run(id, bucket.day, bucket.turns, bucket.steps, bucket.userInputs, bucket.toolCalls);
1715
- }
1716
- async rebuildSessionStats(id) {
1717
- this.db.$client.prepare("DELETE FROM t_session_usage WHERE f_session_id = ?").run(id);
1718
- this.db.$client.prepare(`INSERT INTO t_session_usage (
1719
- f_session_id, f_day, f_provider, f_model,
1720
- f_input_tokens, f_output_tokens, f_cache_read_tokens, f_reasoning_tokens, f_total_tokens)
1721
- SELECT b.f_session_id, u.f_day, coalesce(u.f_provider, ''), coalesce(u.f_model, ''),
1722
- sum(u.f_input_tokens), sum(u.f_output_tokens), sum(u.f_cache_read_tokens),
1723
- sum(u.f_reasoning_tokens), sum(u.f_total_tokens)
1724
- FROM t_session_events b
1725
- JOIN t_event_usage u ON u.f_event_id = b.f_event_id
1726
- WHERE b.f_session_id = ?
1727
- GROUP BY b.f_session_id, u.f_day, coalesce(u.f_provider, ''), coalesce(u.f_model, '')`).run(id);
1728
- this.db.$client.prepare("DELETE FROM t_session_counts WHERE f_session_id = ?").run(id);
1729
- this.db.$client.prepare(`INSERT INTO t_session_counts (
1730
- f_session_id, f_day, f_turns, f_steps, f_user_inputs, f_tool_calls)
1731
- SELECT b.f_session_id,
1732
- date(e.f_created_at / 1000, 'unixepoch', 'localtime'),
1733
- sum(CASE WHEN e.f_type = 'turn/start' THEN 1 ELSE 0 END),
1734
- sum(CASE WHEN e.f_type = 'step/start' THEN 1 ELSE 0 END),
1735
- sum(CASE WHEN e.f_type = 'user/message' THEN 1 ELSE 0 END),
1736
- sum(CASE WHEN e.f_type = 'tool/call' THEN 1 ELSE 0 END)
1737
- FROM t_session_events b
1738
- JOIN t_events e ON e.f_event_id = b.f_event_id
1739
- WHERE b.f_session_id = ? AND e.f_type IN (${COUNTED_EVENT_TYPE_SQL})
1740
- GROUP BY b.f_session_id, 2`).run(id);
1741
- }
1742
- async refreshEventUsageFlags(eventIds) {
1743
- for (let at = 0; at < eventIds.length; at += REFRESH_FLAG_CHUNK) {
1744
- const chunk = eventIds.slice(at, at + REFRESH_FLAG_CHUNK);
1745
- const placeholders = chunk.map(() => "?").join(", ");
1746
- this.db.$client.prepare(`UPDATE t_event_usage SET
1747
- f_referenced = ${withEvent(EVENT_REFERENCED_SQL, "f_event_id")},
1748
- f_subagent = ${withEvent(EVENT_SUBAGENT_SQL, "f_event_id")}
1749
- WHERE f_event_id IN (${placeholders})`).run(...chunk);
1750
- }
1751
- }
1752
- async deleteSessionStats(id) {
1753
- this.db.$client.prepare("DELETE FROM t_session_usage WHERE f_session_id = ?").run(id);
1754
- this.db.$client.prepare("DELETE FROM t_session_counts WHERE f_session_id = ?").run(id);
1755
- }
1756
- async vacuum() {
1757
- await enqueueSqliteTx(this.dbPath, async () => {
1758
- this.db.$client.exec("VACUUM");
1759
- });
1760
- }
1761
- eventRows() {
1762
- return this.db.select({
1763
- fEventId: tSessionEvents.fEventId,
1764
- fSequence: tSessionEvents.fSequence,
1765
- fType: tEvents.fType,
1766
- fKind: tEvents.fKind,
1767
- fRole: tEvents.fRole,
1768
- fName: tEvents.fName,
1769
- fActionId: tEvents.fActionId,
1770
- fCreatedAt: tEvents.fCreatedAt,
1771
- fData: tEvents.fData,
1772
- fSurfaceOp: tSessionEvents.fSurfaceOp
1773
- }).from(tSessionEvents).innerJoin(tEvents, eq(tSessionEvents.fEventId, tEvents.fEventId));
1774
- }
1775
- };
1776
- //#endregion
1777
- export { tWorkspaces as C, WriteGuard as E, tWorkspaceState as S, toPostgresSchema as T, tSessionProjcacheRows as _, EVENT_ENCODING as a, tStorageUnits as b, eventDimensions as c, tEvents as d, tPersistenceState as f, tSessionEvents as g, tSessionCounts as h, DEFAULT_BUSY_TIMEOUT_MS as i, eventKind as l, tSchemaMeta as m, openDatabase as n, SCHEMA_VERSION as o, tScheduleTasks as p, createStorageRepository as r, SESSION_PERSISTENCE_SQLITE_APPLICATION_ID as s, SqliteBackend as t, tEventUsage as u, tSessionUsage as v, postgresTableDefs as w, tWorkspaceSessions as x, tSessions as y };