@morlay/session-rdb 0.1.10-alpha.0 → 0.1.10-alpha.3

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