@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.
- package/README.md +24 -3
- package/dist/artifact.d.mts +118 -3
- package/dist/artifact.mjs +5 -3
- package/dist/{log-C0q5svm7.d.mts → backend-BKtJ5sO5.d.mts} +189 -291
- package/dist/backend-rdb-BDCntVDy.mjs +1341 -0
- package/dist/branch-CG1PUlO6.mjs +833 -0
- package/dist/deletion-SSX7IqU8.mjs +35 -0
- package/dist/deletion.d.mts +1 -3
- package/dist/deletion.mjs +2 -2
- package/dist/export-CfJQjQfr.mjs +123 -0
- package/dist/export.d.mts +1 -3
- package/dist/export.mjs +32 -57
- package/dist/import.d.mts +1 -3
- package/dist/import.mjs +32 -78
- package/dist/index-CUD2ZpTA.d.mts +71 -0
- package/dist/index.d.mts +20 -2
- package/dist/index.mjs +1562 -5
- package/dist/open-YoyD_kah.mjs +561 -0
- package/dist/organize.d.mts +9 -0
- package/dist/organize.mjs +30 -0
- package/dist/route-DpfY0zq-.mjs +114 -0
- package/dist/storage.d.mts +41 -50
- package/dist/storage.mjs +3 -1
- package/dist/testing.d.mts +9 -5
- package/dist/testing.mjs +27 -8
- package/dist/usage-row-BQQM-_t6.mjs +36 -0
- package/dist/usage.d.mts +1 -1
- package/dist/usage.mjs +8 -42
- package/dist/write-guard-CYrY58GM.mjs +174 -0
- package/package.json +32 -36
- package/src/artifact.ts +4 -1
- package/src/backend-rdb.ts +331 -0
- package/src/backend.ts +32 -15
- package/src/{branch.ts → branch/index.ts} +125 -112
- package/src/branch/port.ts +62 -0
- package/src/branch/semantics.ts +69 -0
- package/src/db/open.ts +180 -0
- package/src/db/stats.ts +449 -0
- package/src/deletion-error.ts +17 -0
- package/src/deletion.ts +19 -72
- package/src/entities/index.ts +16 -1
- package/src/entities/v3/event-usage.ts +3 -3
- package/src/entities/v3/events.ts +3 -3
- package/src/entities/v3/index.ts +4 -16
- package/src/entities/v3/persistence-state.ts +3 -3
- package/src/entities/v3/schema-meta.ts +3 -3
- package/src/entities/v3/session-counts.ts +3 -3
- package/src/entities/v3/session-events.ts +3 -3
- package/src/entities/v3/session-projcache-rows.ts +3 -3
- package/src/entities/v3/session-usage.ts +3 -3
- package/src/entities/v3/sessions.ts +3 -3
- package/src/entities/v3/storage-records.ts +17 -0
- package/src/entities/v3/storage-units.ts +3 -3
- package/src/{schema.ts → event-shape.ts} +0 -46
- package/src/export.ts +32 -78
- package/src/handles.ts +349 -0
- package/src/http/route.ts +178 -0
- package/src/import-storages.ts +5 -2
- package/src/import.ts +63 -125
- package/src/index.ts +15 -1630
- package/src/legacy.ts +3 -1
- package/src/live-routing.ts +205 -0
- package/src/log/export.ts +84 -0
- package/src/{log.ts → log/repair.ts} +1 -272
- package/src/log/rows.ts +107 -0
- package/src/log/usage-row.ts +64 -0
- package/src/migrations.ts +196 -0
- package/src/organize.ts +38 -0
- package/src/read-path.ts +190 -0
- package/src/routes.ts +2 -1
- package/src/rows.ts +25 -77
- package/src/session-ops.ts +444 -0
- package/src/session-query.ts +17 -4
- package/src/session-store.ts +675 -0
- package/src/stats-fold.ts +44 -0
- package/src/storage-takeover/index.ts +2 -5
- package/src/storage-takeover/legacy-domain-migration.ts +118 -0
- package/src/storage-takeover/projection-cache.ts +139 -77
- package/src/storage-takeover/read-model.ts +189 -0
- package/src/storage-takeover/repository.ts +238 -425
- package/src/storage-takeover/storage-backend.ts +47 -149
- package/src/storage-takeover/types.ts +45 -14
- package/src/storage-takeover/workspace-documents.ts +65 -0
- package/src/storage.ts +4 -2
- package/src/testing.ts +21 -1
- package/src/usage.ts +11 -65
- package/src/version.ts +13 -0
- package/src/write-path.ts +286 -0
- package/dist/branch-DbFW3rJX.mjs +0 -391
- package/dist/deletion-ODZFcbZm.mjs +0 -2218
- package/dist/gc.d.mts +0 -6
- package/dist/gc.mjs +0 -49
- package/dist/log-DYxdjJ-l.mjs +0 -562
- package/dist/routes-DGBlBE_I.mjs +0 -10
- package/dist/sqlite-DjOnMRh2.mjs +0 -1777
- package/drizzle/postgres/20260908072805_v2_initial/migration.sql +0 -58
- package/drizzle/postgres/20260908072805_v2_initial/snapshot.json +0 -686
- package/drizzle/postgres/20260908072806_v3_drop_original_seq/migration.sql +0 -1
- package/drizzle/postgres/20260908072806_v3_drop_original_seq/snapshot.json +0 -673
- package/drizzle/postgres/20260910120001_v3_storage_tables/migration.sql +0 -65
- package/drizzle/postgres/20260910120001_v3_storage_tables/snapshot.json +0 -1183
- package/drizzle/postgres/20260910130001_v3_session_title_backfill/migration.sql +0 -10
- package/drizzle/postgres/20260910130001_v3_session_title_backfill/snapshot.json +0 -1183
- package/drizzle/postgres/20260918120000_v3_event_usage/migration.sql +0 -14
- package/drizzle/postgres/20260918120000_v3_event_usage/snapshot.json +0 -1309
- package/drizzle/postgres/20260921075900_v3_agent_preset/migration.sql +0 -1
- package/drizzle/postgres/20260922090001_v3_pinned_seq/migration.sql +0 -2
- package/drizzle/postgres/20260922093001_v3_last_event_at/migration.sql +0 -10
- package/drizzle/postgres/20260923120000_v3_usage_materialized/migration.sql +0 -48
- package/drizzle/postgres/20261008103001_v3_schedule_tasks/migration.sql +0 -11
- package/drizzle/sqlite/20260908072751_v2_initial/migration.sql +0 -53
- package/drizzle/sqlite/20260908072751_v2_initial/snapshot.json +0 -492
- package/drizzle/sqlite/20260908072752_v3_drop_original_seq/migration.sql +0 -6
- package/drizzle/sqlite/20260908072752_v3_drop_original_seq/snapshot.json +0 -513
- package/drizzle/sqlite/20260910120000_v3_storage_tables/migration.sql +0 -66
- package/drizzle/sqlite/20260910120000_v3_storage_tables/snapshot.json +0 -905
- package/drizzle/sqlite/20260910130000_v3_session_title_backfill/migration.sql +0 -13
- package/drizzle/sqlite/20260910130000_v3_session_title_backfill/snapshot.json +0 -905
- package/drizzle/sqlite/20260918120000_v3_event_usage/migration.sql +0 -14
- package/drizzle/sqlite/20260918120000_v3_event_usage/snapshot.json +0 -1031
- package/drizzle/sqlite/20260921075855_v3_agent_preset/migration.sql +0 -7
- package/drizzle/sqlite/20260921075855_v3_agent_preset/snapshot.json +0 -1041
- package/drizzle/sqlite/20260922090000_v3_pinned_seq/migration.sql +0 -4
- package/drizzle/sqlite/20260922093000_v3_last_event_at/migration.sql +0 -13
- package/drizzle/sqlite/20260923120000_v3_usage_materialized/migration.sql +0 -48
- package/drizzle/sqlite/20261008103000_v3_schedule_tasks/migration.sql +0 -14
- package/src/adapters/index.ts +0 -12
- package/src/adapters/to-postgres.ts +0 -93
- package/src/adapters/to-sqlite.ts +0 -83
- package/src/adapters/types.ts +0 -44
- package/src/drizzle/postgres-v2.ts +0 -10
- package/src/drizzle/postgres-v3.ts +0 -18
- package/src/drizzle/sqlite-v2.ts +0 -9
- package/src/drizzle/sqlite-v3.ts +0 -18
- package/src/entities/v2/index.ts +0 -20
- package/src/entities/v2/session-events.ts +0 -10
- package/src/entities/v3/schedule-tasks.ts +0 -16
- package/src/entities/v3/workspace-sessions.ts +0 -19
- package/src/entities/v3/workspace-state.ts +0 -12
- package/src/entities/v3/workspaces.ts +0 -14
- package/src/gc.ts +0 -77
- package/src/postgres.ts +0 -1015
- package/src/sqlite.ts +0 -1086
package/dist/sqlite-DjOnMRh2.mjs
DELETED
|
@@ -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 };
|