@morlay/session-rdb 0.0.15 → 0.0.16-alpha.1
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 +11 -13
- package/dist/artifact.d.mts +30 -16
- package/dist/artifact.mjs +3 -3
- package/dist/{import-DFed8DBt.mjs → import-mZZgDBLd.mjs} +73 -83
- package/dist/import.d.mts +2 -3
- package/dist/import.mjs +2 -2
- package/dist/index-GdKkSSb-.d.mts +239 -0
- package/dist/index.d.mts +3 -3
- package/dist/index.mjs +763 -168
- package/dist/log-DBFUBPhv.mjs +394 -0
- package/dist/{schema-CFXZURX7.d.mts → schema-COK7-wRV.d.mts} +3 -15
- package/dist/sqlite-CnWwGToZ.mjs +698 -0
- package/dist/storage.d.mts +2 -7
- package/dist/storage.mjs +2 -3
- package/dist/testing.d.mts +30 -1
- package/dist/testing.mjs +676 -1991
- package/drizzle/postgres/20260908072805_v2_initial/migration.sql +58 -0
- package/drizzle/postgres/20260908072805_v2_initial/snapshot.json +686 -0
- package/drizzle/postgres/20260908072806_v3_drop_original_seq/migration.sql +1 -0
- package/drizzle/postgres/20260908072806_v3_drop_original_seq/snapshot.json +673 -0
- package/drizzle/sqlite/20260908072751_v2_initial/migration.sql +53 -0
- package/drizzle/sqlite/20260908072751_v2_initial/snapshot.json +492 -0
- package/drizzle/sqlite/20260908072752_v3_drop_original_seq/migration.sql +6 -0
- package/drizzle/sqlite/20260908072752_v3_drop_original_seq/snapshot.json +513 -0
- package/package.json +17 -12
- package/src/adapters/index.ts +10 -2
- package/src/adapters/to-postgres.ts +19 -15
- package/src/adapters/to-sqlite.ts +20 -14
- package/src/{entities → adapters}/types.ts +7 -8
- package/src/backend.ts +0 -7
- package/src/branch.ts +29 -110
- package/src/drizzle/postgres-v2.ts +11 -0
- package/src/drizzle/postgres-v3.ts +11 -0
- package/src/drizzle/sqlite-v2.ts +10 -0
- package/src/drizzle/sqlite-v3.ts +11 -0
- package/src/entities/index.ts +2 -30
- package/src/entities/v2/index.ts +20 -0
- package/src/entities/v2/session-events.ts +11 -0
- package/src/entities/v3/events.ts +27 -0
- package/src/entities/v3/index.ts +27 -0
- package/src/entities/v3/persistence-state.ts +10 -0
- package/src/entities/v3/schema-meta.ts +9 -0
- package/src/entities/v3/session-events.ts +26 -0
- package/src/entities/v3/sessions.ts +20 -0
- package/src/import.ts +65 -57
- package/src/index.ts +929 -227
- package/src/invariant.ts +0 -2
- package/src/legacy.ts +110 -0
- package/src/log.ts +143 -91
- package/src/postgres.ts +75 -93
- package/src/schema.ts +3 -14
- package/src/sqlite.ts +59 -46
- package/src/testing/contract.ts +460 -375
- package/src/testing/coordinator-contract.ts +108 -1374
- package/src/testing.ts +1 -6
- package/dist/index-uUvn6gYp.d.mts +0 -111
- package/dist/schema-vwzwEXNE.mjs +0 -878
- package/dist/sqlite-CG_Qcx5C.mjs +0 -356
- package/src/adapters/ddl.ts +0 -77
- package/src/entities/events.ts +0 -27
- package/src/entities/persistence-state.ts +0 -10
- package/src/entities/schema-meta.ts +0 -9
- package/src/entities/session-events.ts +0 -29
- package/src/entities/sessions.ts +0 -20
- package/src/migrate.ts +0 -137
|
@@ -0,0 +1,698 @@
|
|
|
1
|
+
import { d as sessionConflictRow, f as sessionInsertRow } from "./log-DBFUBPhv.mjs";
|
|
2
|
+
import { randomUUID } from "node:crypto";
|
|
3
|
+
import { and, eq, gte, sql } from "drizzle-orm";
|
|
4
|
+
import { check, index, integer, sqliteTable, text, unique } from "drizzle-orm/sqlite-core";
|
|
5
|
+
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";
|
|
6
|
+
import { readdirSync, statSync } from "node:fs";
|
|
7
|
+
import { mkdir, open } from "node:fs/promises";
|
|
8
|
+
import { dirname, resolve } from "node:path";
|
|
9
|
+
import { DatabaseSync } from "node:sqlite";
|
|
10
|
+
import { drizzle } from "drizzle-orm/node-sqlite";
|
|
11
|
+
import { migrate } from "drizzle-orm/node-sqlite/migrator";
|
|
12
|
+
//#region src/write-guard.ts
|
|
13
|
+
var WriteGuard = class {
|
|
14
|
+
headSeqs = /* @__PURE__ */ new Map();
|
|
15
|
+
confirmHead(id, head) {
|
|
16
|
+
this.headSeqs.set(id, head);
|
|
17
|
+
}
|
|
18
|
+
assertNoConcurrentWriter(id, storedHead) {
|
|
19
|
+
const known = this.headSeqs.get(id);
|
|
20
|
+
if (known === void 0) {
|
|
21
|
+
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`);
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
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`);
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
//#endregion
|
|
28
|
+
//#region src/adapters/types.ts
|
|
29
|
+
function toProperty(name) {
|
|
30
|
+
return name.replace(/_([a-z])/g, (_match, char) => char.toUpperCase());
|
|
31
|
+
}
|
|
32
|
+
//#endregion
|
|
33
|
+
//#region src/adapters/to-sqlite.ts
|
|
34
|
+
function buildColumn$1(name, c, tables) {
|
|
35
|
+
let col;
|
|
36
|
+
switch (c.type) {
|
|
37
|
+
case "text": {
|
|
38
|
+
const built = text(name);
|
|
39
|
+
col = c.primaryKey ? built.primaryKey() : built;
|
|
40
|
+
break;
|
|
41
|
+
}
|
|
42
|
+
case "serial":
|
|
43
|
+
col = integer(name).primaryKey({ autoIncrement: true });
|
|
44
|
+
break;
|
|
45
|
+
case "integer":
|
|
46
|
+
case "bigint": {
|
|
47
|
+
const built = integer(name);
|
|
48
|
+
col = c.primaryKey ? built.primaryKey() : built;
|
|
49
|
+
break;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
if (c.notNull) col = col.notNull();
|
|
53
|
+
if (c.default !== void 0) col = col.default(c.default);
|
|
54
|
+
if (c.unique) col = col.unique();
|
|
55
|
+
if (c.references) {
|
|
56
|
+
const { table, column, onDelete } = c.references;
|
|
57
|
+
col = col.references(() => tables[table][toProperty(column)], { onDelete });
|
|
58
|
+
}
|
|
59
|
+
return col;
|
|
60
|
+
}
|
|
61
|
+
function toSqliteSchema(defs) {
|
|
62
|
+
const tables = {};
|
|
63
|
+
for (const def of defs) {
|
|
64
|
+
const columns = {};
|
|
65
|
+
for (const [name, c] of Object.entries(def.columns)) columns[toProperty(name)] = buildColumn$1(name, c, tables);
|
|
66
|
+
const extra = (self) => [
|
|
67
|
+
...Object.entries(def.checks ?? {}).map(([name, c]) => check(name, sql.raw(c.expression))),
|
|
68
|
+
...Object.entries(def.uniques ?? {}).map(([name, u]) => unique(name).on(...u.columns.map((n) => self[toProperty(n)]))),
|
|
69
|
+
...Object.entries(def.indexes ?? {}).map(([name, i]) => index(name).on(...i.columns.map((n) => self[toProperty(n)])))
|
|
70
|
+
];
|
|
71
|
+
tables[def.name] = sqliteTable(def.name, columns, extra);
|
|
72
|
+
}
|
|
73
|
+
return tables;
|
|
74
|
+
}
|
|
75
|
+
//#endregion
|
|
76
|
+
//#region src/adapters/to-postgres.ts
|
|
77
|
+
function buildColumn(name, c, tables) {
|
|
78
|
+
let col;
|
|
79
|
+
switch (c.type) {
|
|
80
|
+
case "text": {
|
|
81
|
+
const built = text$1(name);
|
|
82
|
+
col = c.primaryKey ? built.primaryKey() : built;
|
|
83
|
+
break;
|
|
84
|
+
}
|
|
85
|
+
case "serial":
|
|
86
|
+
col = serial(name).primaryKey();
|
|
87
|
+
break;
|
|
88
|
+
case "integer": {
|
|
89
|
+
const built = integer$1(name);
|
|
90
|
+
col = c.primaryKey ? built.primaryKey() : built;
|
|
91
|
+
break;
|
|
92
|
+
}
|
|
93
|
+
case "bigint": {
|
|
94
|
+
const built = bigint(name, { mode: "number" });
|
|
95
|
+
col = c.primaryKey ? built.primaryKey() : built;
|
|
96
|
+
break;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
if (c.notNull) col = col.notNull();
|
|
100
|
+
if (c.default !== void 0) col = col.default(c.default);
|
|
101
|
+
if (c.unique) col = col.unique();
|
|
102
|
+
if (c.references) {
|
|
103
|
+
const { table, column, onDelete } = c.references;
|
|
104
|
+
col = col.references(() => tables[table][toProperty(column)], { onDelete });
|
|
105
|
+
}
|
|
106
|
+
return col;
|
|
107
|
+
}
|
|
108
|
+
function toPostgresSchema(defs, schemaName = "public") {
|
|
109
|
+
const tables = {};
|
|
110
|
+
const table = schemaName === "public" ? pgTable : pgSchema(schemaName).table;
|
|
111
|
+
for (const def of defs) {
|
|
112
|
+
const columns = {};
|
|
113
|
+
for (const [name, c] of Object.entries(def.columns)) columns[toProperty(name)] = buildColumn(name, c, tables);
|
|
114
|
+
const extra = (self) => [
|
|
115
|
+
...Object.entries(def.checks ?? {}).map(([name, c]) => check$1(name, sql.raw(c.expression))),
|
|
116
|
+
...Object.entries(def.uniques ?? {}).map(([name, u]) => unique$1(name).on(...u.columns.map((n) => self[toProperty(n)]))),
|
|
117
|
+
...Object.entries(def.indexes ?? {}).map(([name, i]) => index$1(name).on(...i.columns.map((n) => self[toProperty(n)])))
|
|
118
|
+
];
|
|
119
|
+
tables[def.name] = table(def.name, columns, extra);
|
|
120
|
+
}
|
|
121
|
+
return tables;
|
|
122
|
+
}
|
|
123
|
+
//#endregion
|
|
124
|
+
//#region src/entities/v3/persistence-state.ts
|
|
125
|
+
const persistenceState = {
|
|
126
|
+
name: "t_persistence_state",
|
|
127
|
+
columns: {
|
|
128
|
+
f_singleton: {
|
|
129
|
+
type: "integer",
|
|
130
|
+
primaryKey: true
|
|
131
|
+
},
|
|
132
|
+
f_store_id: {
|
|
133
|
+
type: "text",
|
|
134
|
+
notNull: true
|
|
135
|
+
}
|
|
136
|
+
},
|
|
137
|
+
checks: { ck_persistence_state_singleton: { expression: "f_singleton = 1" } }
|
|
138
|
+
};
|
|
139
|
+
//#endregion
|
|
140
|
+
//#region 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
|
+
//#endregion
|
|
155
|
+
//#region src/entities/v3/sessions.ts
|
|
156
|
+
const sessions = {
|
|
157
|
+
name: "t_sessions",
|
|
158
|
+
columns: {
|
|
159
|
+
f_id: {
|
|
160
|
+
type: "serial",
|
|
161
|
+
primaryKey: true
|
|
162
|
+
},
|
|
163
|
+
f_session_id: {
|
|
164
|
+
type: "text",
|
|
165
|
+
notNull: true,
|
|
166
|
+
unique: true
|
|
167
|
+
},
|
|
168
|
+
f_head_event_id: {
|
|
169
|
+
type: "text",
|
|
170
|
+
notNull: true,
|
|
171
|
+
default: ""
|
|
172
|
+
},
|
|
173
|
+
f_head_sequence: {
|
|
174
|
+
type: "integer",
|
|
175
|
+
notNull: true,
|
|
176
|
+
default: -1
|
|
177
|
+
},
|
|
178
|
+
f_version: {
|
|
179
|
+
type: "integer",
|
|
180
|
+
notNull: true
|
|
181
|
+
},
|
|
182
|
+
f_created_at: {
|
|
183
|
+
type: "bigint",
|
|
184
|
+
notNull: true
|
|
185
|
+
},
|
|
186
|
+
f_cwd: { type: "text" },
|
|
187
|
+
f_parent_session: { type: "text" },
|
|
188
|
+
f_seed_length: { type: "integer" },
|
|
189
|
+
f_origin: { type: "text" },
|
|
190
|
+
f_delegation_depth: { type: "integer" },
|
|
191
|
+
f_incarnation: {
|
|
192
|
+
type: "text",
|
|
193
|
+
notNull: true
|
|
194
|
+
},
|
|
195
|
+
f_revision: {
|
|
196
|
+
type: "integer",
|
|
197
|
+
notNull: true
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
};
|
|
201
|
+
//#endregion
|
|
202
|
+
//#region src/entities/v3/events.ts
|
|
203
|
+
const events = {
|
|
204
|
+
name: "t_events",
|
|
205
|
+
columns: {
|
|
206
|
+
f_id: {
|
|
207
|
+
type: "serial",
|
|
208
|
+
primaryKey: true
|
|
209
|
+
},
|
|
210
|
+
f_event_id: {
|
|
211
|
+
type: "text",
|
|
212
|
+
notNull: true,
|
|
213
|
+
unique: true
|
|
214
|
+
},
|
|
215
|
+
f_parent_id: {
|
|
216
|
+
type: "text",
|
|
217
|
+
notNull: true,
|
|
218
|
+
default: ""
|
|
219
|
+
},
|
|
220
|
+
f_type: {
|
|
221
|
+
type: "text",
|
|
222
|
+
notNull: true,
|
|
223
|
+
default: ""
|
|
224
|
+
},
|
|
225
|
+
f_kind: {
|
|
226
|
+
type: "text",
|
|
227
|
+
notNull: true,
|
|
228
|
+
default: ""
|
|
229
|
+
},
|
|
230
|
+
f_role: {
|
|
231
|
+
type: "text",
|
|
232
|
+
notNull: true,
|
|
233
|
+
default: ""
|
|
234
|
+
},
|
|
235
|
+
f_name: {
|
|
236
|
+
type: "text",
|
|
237
|
+
notNull: true,
|
|
238
|
+
default: ""
|
|
239
|
+
},
|
|
240
|
+
f_action_id: {
|
|
241
|
+
type: "text",
|
|
242
|
+
notNull: true,
|
|
243
|
+
default: ""
|
|
244
|
+
},
|
|
245
|
+
f_encoding: {
|
|
246
|
+
type: "text",
|
|
247
|
+
notNull: true,
|
|
248
|
+
default: ""
|
|
249
|
+
},
|
|
250
|
+
f_data: {
|
|
251
|
+
type: "text",
|
|
252
|
+
notNull: true
|
|
253
|
+
},
|
|
254
|
+
f_created_at: {
|
|
255
|
+
type: "bigint",
|
|
256
|
+
notNull: true,
|
|
257
|
+
default: 0
|
|
258
|
+
}
|
|
259
|
+
},
|
|
260
|
+
indexes: {
|
|
261
|
+
idx_events_kind: { columns: ["f_kind"] },
|
|
262
|
+
idx_events_role: { columns: ["f_role"] },
|
|
263
|
+
idx_events_name: { columns: ["f_name"] },
|
|
264
|
+
idx_events_action_id: { columns: ["f_action_id"] }
|
|
265
|
+
}
|
|
266
|
+
};
|
|
267
|
+
//#endregion
|
|
268
|
+
//#region src/entities/v3/session-events.ts
|
|
269
|
+
const sessionEvents$1 = {
|
|
270
|
+
name: "t_session_events",
|
|
271
|
+
columns: {
|
|
272
|
+
f_id: {
|
|
273
|
+
type: "serial",
|
|
274
|
+
primaryKey: true
|
|
275
|
+
},
|
|
276
|
+
f_session_id: {
|
|
277
|
+
type: "text",
|
|
278
|
+
notNull: true,
|
|
279
|
+
references: {
|
|
280
|
+
table: "t_sessions",
|
|
281
|
+
column: "f_session_id",
|
|
282
|
+
onDelete: "cascade"
|
|
283
|
+
}
|
|
284
|
+
},
|
|
285
|
+
f_event_id: {
|
|
286
|
+
type: "text",
|
|
287
|
+
notNull: true,
|
|
288
|
+
references: {
|
|
289
|
+
table: "t_events",
|
|
290
|
+
column: "f_event_id",
|
|
291
|
+
onDelete: "cascade"
|
|
292
|
+
}
|
|
293
|
+
},
|
|
294
|
+
f_sequence: {
|
|
295
|
+
type: "integer",
|
|
296
|
+
notNull: true
|
|
297
|
+
},
|
|
298
|
+
f_surface_op: { type: "text" }
|
|
299
|
+
},
|
|
300
|
+
uniques: { uq_session_events_session_sequence: { columns: ["f_session_id", "f_sequence"] } },
|
|
301
|
+
indexes: { idx_session_events_event_id: { columns: ["f_event_id"] } }
|
|
302
|
+
};
|
|
303
|
+
//#endregion
|
|
304
|
+
//#region src/entities/v3/index.ts
|
|
305
|
+
const sqliteTableDefs = [
|
|
306
|
+
persistenceState,
|
|
307
|
+
schemaMeta,
|
|
308
|
+
sessions,
|
|
309
|
+
events,
|
|
310
|
+
sessionEvents$1
|
|
311
|
+
];
|
|
312
|
+
const postgresTableDefs = [
|
|
313
|
+
persistenceState,
|
|
314
|
+
schemaMeta,
|
|
315
|
+
sessions,
|
|
316
|
+
events,
|
|
317
|
+
sessionEvents$1
|
|
318
|
+
];
|
|
319
|
+
({ ...sessionEvents$1 }), { ...sessionEvents$1.columns };
|
|
320
|
+
//#endregion
|
|
321
|
+
//#region src/schema.ts
|
|
322
|
+
const SCHEMA_VERSION = 3;
|
|
323
|
+
const SESSION_PERSISTENCE_SQLITE_APPLICATION_ID = 1146308688;
|
|
324
|
+
const EVENT_ENCODING = "json";
|
|
325
|
+
const sqliteTables = toSqliteSchema(sqliteTableDefs);
|
|
326
|
+
const tPersistenceState = sqliteTables["t_persistence_state"];
|
|
327
|
+
const tSchemaMeta = sqliteTables["t_schema_meta"];
|
|
328
|
+
const tSessions = sqliteTables["t_sessions"];
|
|
329
|
+
const tEvents = sqliteTables["t_events"];
|
|
330
|
+
const tSessionEvents = sqliteTables["t_session_events"];
|
|
331
|
+
const DEFAULT_BUSY_TIMEOUT_MS = 5e3;
|
|
332
|
+
function eventKind(event) {
|
|
333
|
+
switch (event.type) {
|
|
334
|
+
case "user/message": return "message";
|
|
335
|
+
case "assistant/message":
|
|
336
|
+
if ((event.data.message?.content)?.some((block) => block?.type === "reasoning")) return "thinking";
|
|
337
|
+
return "message";
|
|
338
|
+
case "turn/start":
|
|
339
|
+
case "turn/end":
|
|
340
|
+
case "step/start":
|
|
341
|
+
case "step/end":
|
|
342
|
+
case "session/end-seed": return "turn";
|
|
343
|
+
case "tool/call":
|
|
344
|
+
case "tool/result":
|
|
345
|
+
case "tool/code-dispatch-start":
|
|
346
|
+
case "tool/code-dispatch": return "tool";
|
|
347
|
+
case "request/header":
|
|
348
|
+
case "request/context": return "request";
|
|
349
|
+
case "model/selection":
|
|
350
|
+
case "permission/preset":
|
|
351
|
+
case "approval/policy":
|
|
352
|
+
case "sandbox/mode":
|
|
353
|
+
case "plan/mode":
|
|
354
|
+
case "agent-preset/selected": return "config";
|
|
355
|
+
case "approval/asked":
|
|
356
|
+
case "approval/decided":
|
|
357
|
+
case "command/run":
|
|
358
|
+
case "command/done":
|
|
359
|
+
case "hook/invoked":
|
|
360
|
+
case "hook/result":
|
|
361
|
+
case "feedback/record": return "audit";
|
|
362
|
+
case "session/title":
|
|
363
|
+
case "session/title-llm-request":
|
|
364
|
+
case "session-log-deepseek/delivery-accepted": return "lifecycle";
|
|
365
|
+
case "agent/inbox/spliced": return "inbox";
|
|
366
|
+
case "compaction/start":
|
|
367
|
+
case "compaction/end":
|
|
368
|
+
case "compaction/summary":
|
|
369
|
+
case "compaction/prune": return "compaction";
|
|
370
|
+
case "llm/retry":
|
|
371
|
+
case "llm/retry-started": return "llm";
|
|
372
|
+
case "subagent/descriptor":
|
|
373
|
+
case "subagent/model-selection-policy": return "subagent";
|
|
374
|
+
case "team/member":
|
|
375
|
+
case "team/task":
|
|
376
|
+
case "team/message/queued":
|
|
377
|
+
case "team/message/delivered": return "team";
|
|
378
|
+
case "tool-workflow/run-start":
|
|
379
|
+
case "tool-workflow/run-end":
|
|
380
|
+
case "tool-workflow/agent-start":
|
|
381
|
+
case "tool-workflow/agent-end": return "workflow";
|
|
382
|
+
case "goal/change": return "goal";
|
|
383
|
+
case "schedule/change": return "schedule";
|
|
384
|
+
case "todo/write": return "todo";
|
|
385
|
+
case "web/deepseek-search-llm-request": return "web";
|
|
386
|
+
default: return "";
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
function eventDimensions(event) {
|
|
390
|
+
const kind = eventKind(event);
|
|
391
|
+
const type = event.type;
|
|
392
|
+
const data = event.data;
|
|
393
|
+
switch (type) {
|
|
394
|
+
case "user/message": return {
|
|
395
|
+
kind,
|
|
396
|
+
role: "user",
|
|
397
|
+
name: "",
|
|
398
|
+
actionId: ""
|
|
399
|
+
};
|
|
400
|
+
case "assistant/message": return {
|
|
401
|
+
kind,
|
|
402
|
+
role: "assistant",
|
|
403
|
+
name: "",
|
|
404
|
+
actionId: ""
|
|
405
|
+
};
|
|
406
|
+
case "tool/result": return {
|
|
407
|
+
kind,
|
|
408
|
+
role: "tool",
|
|
409
|
+
name: "",
|
|
410
|
+
actionId: data["message"]?.content?.[0]?.toolCallId ?? ""
|
|
411
|
+
};
|
|
412
|
+
case "tool/call": return {
|
|
413
|
+
kind,
|
|
414
|
+
role: "",
|
|
415
|
+
name: typeof data["name"] === "string" ? data["name"] : "",
|
|
416
|
+
actionId: typeof data["callId"] === "string" ? data["callId"] : ""
|
|
417
|
+
};
|
|
418
|
+
case "tool/code-dispatch-start":
|
|
419
|
+
case "tool/code-dispatch": return {
|
|
420
|
+
kind,
|
|
421
|
+
role: "",
|
|
422
|
+
name: "",
|
|
423
|
+
actionId: typeof data["subCallId"] === "string" ? data["subCallId"] : ""
|
|
424
|
+
};
|
|
425
|
+
case "command/run":
|
|
426
|
+
case "command/done": return {
|
|
427
|
+
kind,
|
|
428
|
+
role: "",
|
|
429
|
+
name: type === "command/run" && typeof data["name"] === "string" ? data["name"] : "",
|
|
430
|
+
actionId: typeof data["commandId"] === "string" ? data["commandId"] : ""
|
|
431
|
+
};
|
|
432
|
+
case "approval/asked":
|
|
433
|
+
case "approval/decided": return {
|
|
434
|
+
kind,
|
|
435
|
+
role: "",
|
|
436
|
+
name: "",
|
|
437
|
+
actionId: typeof data["id"] === "string" ? data["id"] : ""
|
|
438
|
+
};
|
|
439
|
+
case "hook/invoked":
|
|
440
|
+
case "hook/result": return {
|
|
441
|
+
kind,
|
|
442
|
+
role: "",
|
|
443
|
+
name: "",
|
|
444
|
+
actionId: typeof data["handlerId"] === "string" ? data["handlerId"] : ""
|
|
445
|
+
};
|
|
446
|
+
case "llm/retry":
|
|
447
|
+
case "llm/retry-started": return {
|
|
448
|
+
kind,
|
|
449
|
+
role: "",
|
|
450
|
+
name: "",
|
|
451
|
+
actionId: typeof data["retryId"] === "string" ? data["retryId"] : ""
|
|
452
|
+
};
|
|
453
|
+
case "tool-workflow/run-start":
|
|
454
|
+
case "tool-workflow/run-end":
|
|
455
|
+
case "tool-workflow/agent-start":
|
|
456
|
+
case "tool-workflow/agent-end": return {
|
|
457
|
+
kind,
|
|
458
|
+
role: "",
|
|
459
|
+
name: "",
|
|
460
|
+
actionId: typeof data["runId"] === "string" ? data["runId"] : ""
|
|
461
|
+
};
|
|
462
|
+
case "todo/write": return {
|
|
463
|
+
kind,
|
|
464
|
+
role: "",
|
|
465
|
+
name: "todos",
|
|
466
|
+
actionId: ""
|
|
467
|
+
};
|
|
468
|
+
case "subagent/descriptor": return {
|
|
469
|
+
kind,
|
|
470
|
+
role: "",
|
|
471
|
+
name: typeof data["label"] === "string" ? data["label"] : "",
|
|
472
|
+
actionId: ""
|
|
473
|
+
};
|
|
474
|
+
default: return {
|
|
475
|
+
kind,
|
|
476
|
+
role: "",
|
|
477
|
+
name: "",
|
|
478
|
+
actionId: ""
|
|
479
|
+
};
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
//#endregion
|
|
483
|
+
//#region src/sqlite.ts
|
|
484
|
+
/** drizzle-kit 生成的迁移目录(随包根 drizzle/ 发布;src/dist 形态经相对 URL 统一解析)。 */
|
|
485
|
+
const sqliteMigrationsDir = new URL("../drizzle/sqlite/", import.meta.url).pathname;
|
|
486
|
+
const sqliteTxQueues = /* @__PURE__ */ new Map();
|
|
487
|
+
function enqueueSqliteTx(path, fn) {
|
|
488
|
+
const run = (sqliteTxQueues.get(path) ?? Promise.resolve()).then(fn);
|
|
489
|
+
sqliteTxQueues.set(path, run.then(() => void 0, () => void 0));
|
|
490
|
+
return run;
|
|
491
|
+
}
|
|
492
|
+
async function createDatabaseFile(path) {
|
|
493
|
+
try {
|
|
494
|
+
await (await open(path, "wx", 384)).close();
|
|
495
|
+
} catch (error) {
|
|
496
|
+
if (error.code !== "EEXIST") throw error;
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
function openDatabase(path, journalMode, busyTimeout = DEFAULT_BUSY_TIMEOUT_MS) {
|
|
500
|
+
const db = new DatabaseSync(path);
|
|
501
|
+
try {
|
|
502
|
+
configureDatabase(db, path, journalMode, busyTimeout);
|
|
503
|
+
const dbx = drizzle({ client: db });
|
|
504
|
+
const { user_version: onDisk } = db.prepare("PRAGMA user_version").get();
|
|
505
|
+
if (onDisk === 2 && !hasMigrationsTable(db)) baselineV2(db);
|
|
506
|
+
migrate(dbx, { migrationsFolder: sqliteMigrationsDir });
|
|
507
|
+
dbx.insert(tPersistenceState).values({
|
|
508
|
+
fSingleton: 1,
|
|
509
|
+
fStoreId: randomUUID()
|
|
510
|
+
}).onConflictDoNothing().run();
|
|
511
|
+
if (onDisk === 0 || onDisk === 2) db.exec(`PRAGMA user_version = 3`);
|
|
512
|
+
db.exec(`PRAGMA journal_mode = ${journalMode.toUpperCase()}`);
|
|
513
|
+
return db;
|
|
514
|
+
} catch (error) {
|
|
515
|
+
db.close();
|
|
516
|
+
throw error;
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
function hasMigrationsTable(db) {
|
|
520
|
+
return db.prepare("SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = '__drizzle_migrations'").get() !== void 0;
|
|
521
|
+
}
|
|
522
|
+
/** 标记 v2 baseline 已应用(迁移表 v1 结构:id/hash/created_at/name/applied_at)。 */
|
|
523
|
+
function baselineV2(db) {
|
|
524
|
+
const dir = readdirSync(sqliteMigrationsDir).find((name) => name.endsWith("_v2_initial"));
|
|
525
|
+
if (dir === void 0) throw new Error("missing v2 baseline migration in drizzle/sqlite");
|
|
526
|
+
db.exec(`
|
|
527
|
+
CREATE TABLE __drizzle_migrations (
|
|
528
|
+
id INTEGER PRIMARY KEY,
|
|
529
|
+
hash text NOT NULL,
|
|
530
|
+
created_at numeric,
|
|
531
|
+
name text,
|
|
532
|
+
applied_at TEXT
|
|
533
|
+
)
|
|
534
|
+
`);
|
|
535
|
+
db.prepare("INSERT INTO __drizzle_migrations (hash, created_at, name, applied_at) VALUES (?, ?, ?, ?)").run("baseline", 0, dir, (/* @__PURE__ */ new Date()).toISOString());
|
|
536
|
+
}
|
|
537
|
+
function configureDatabase(db, path, journalMode, busyTimeout) {
|
|
538
|
+
db.exec("PRAGMA foreign_keys = ON");
|
|
539
|
+
db.exec(`PRAGMA busy_timeout = ${busyTimeout}`);
|
|
540
|
+
drizzle({ client: db }).transaction((tx) => {
|
|
541
|
+
const { user_version: onDisk } = tx.get(sql`PRAGMA user_version`);
|
|
542
|
+
const { application_id: applicationId } = tx.get(sql`PRAGMA application_id`);
|
|
543
|
+
const { count: userObjectCount } = tx.get(sql`SELECT COUNT(*) AS count FROM sqlite_schema WHERE name NOT GLOB 'sqlite_*'`);
|
|
544
|
+
if (onDisk === 0 && (applicationId !== 0 || userObjectCount > 0)) throw new Error(`session database at "${path}" has an unversioned schema or application identity`);
|
|
545
|
+
if (onDisk !== 0 && onDisk !== 2 && onDisk !== 3) throw new Error(`session database at "${path}" has schema version ${onDisk}, incompatible with this build (3)`);
|
|
546
|
+
if (onDisk >= 1 && applicationId !== 1146308688) throw new Error(`session database at "${path}" has application id ${applicationId}, expected ${SESSION_PERSISTENCE_SQLITE_APPLICATION_ID}`);
|
|
547
|
+
if (onDisk === 0) {
|
|
548
|
+
tx.run(sql.raw(`PRAGMA application_id = ${SESSION_PERSISTENCE_SQLITE_APPLICATION_ID}`));
|
|
549
|
+
tx.run(sql.raw(`PRAGMA user_version = 3`));
|
|
550
|
+
}
|
|
551
|
+
}, { behavior: "immediate" });
|
|
552
|
+
}
|
|
553
|
+
var SqliteBackend = class SqliteBackend {
|
|
554
|
+
options;
|
|
555
|
+
kind = "sqlite";
|
|
556
|
+
storeIdentity;
|
|
557
|
+
dbPath = "";
|
|
558
|
+
db;
|
|
559
|
+
constructor(options) {
|
|
560
|
+
this.options = options;
|
|
561
|
+
}
|
|
562
|
+
async open() {
|
|
563
|
+
const actual = this.options.path === ":memory:" ? this.options.path : resolve(this.options.path);
|
|
564
|
+
this.dbPath = actual;
|
|
565
|
+
if (actual !== ":memory:") {
|
|
566
|
+
await mkdir(dirname(actual), {
|
|
567
|
+
recursive: true,
|
|
568
|
+
mode: 448
|
|
569
|
+
});
|
|
570
|
+
await createDatabaseFile(actual);
|
|
571
|
+
}
|
|
572
|
+
await enqueueSqliteTx(actual, async () => {
|
|
573
|
+
this.db = drizzle({ client: openDatabase(actual, this.options.journalMode, this.options.busyTimeout) });
|
|
574
|
+
migrate(this.db, { migrationsFolder: sqliteMigrationsDir });
|
|
575
|
+
});
|
|
576
|
+
try {
|
|
577
|
+
const row = this.db.select({ fStoreId: tPersistenceState.fStoreId }).from(tPersistenceState).where(eq(tPersistenceState.fSingleton, 1)).get();
|
|
578
|
+
/* v8 ignore next -- openDatabase inserts the singleton before returning. */
|
|
579
|
+
if (row === void 0) throw new Error(`session database at "${actual}" has no store identity`);
|
|
580
|
+
if (row.fStoreId.length === 0) throw new Error(`session database at "${actual}" has no valid store identity`);
|
|
581
|
+
if (actual !== ":memory:") {
|
|
582
|
+
const identity = statSync(actual, { bigint: true });
|
|
583
|
+
this.storeIdentity = `file:${identity.dev}:${identity.ino}:${identity.birthtimeNs}:store:${row.fStoreId}`;
|
|
584
|
+
} else this.storeIdentity = `memory:store:${row.fStoreId}`;
|
|
585
|
+
} catch (error) {
|
|
586
|
+
this.db.$client.close();
|
|
587
|
+
throw error;
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
async close() {
|
|
591
|
+
if (this.db === void 0) return;
|
|
592
|
+
this.db.$client.close();
|
|
593
|
+
}
|
|
594
|
+
async getSession(id) {
|
|
595
|
+
return this.db.select().from(tSessions).where(eq(tSessions.fSessionId, id)).get();
|
|
596
|
+
}
|
|
597
|
+
async getEventRows(id, fromSequence) {
|
|
598
|
+
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();
|
|
599
|
+
}
|
|
600
|
+
async listSessions() {
|
|
601
|
+
return this.db.select().from(tSessions).all();
|
|
602
|
+
}
|
|
603
|
+
async transaction(fn) {
|
|
604
|
+
return enqueueSqliteTx(this.dbPath, async () => {
|
|
605
|
+
this.db.$client.exec("BEGIN IMMEDIATE");
|
|
606
|
+
try {
|
|
607
|
+
const result = await fn(this.tx);
|
|
608
|
+
this.db.$client.exec("COMMIT");
|
|
609
|
+
return result;
|
|
610
|
+
} catch (error) {
|
|
611
|
+
/* v8 ignore start */
|
|
612
|
+
try {
|
|
613
|
+
this.db.$client.exec("ROLLBACK");
|
|
614
|
+
} catch {}
|
|
615
|
+
throw error;
|
|
616
|
+
}
|
|
617
|
+
});
|
|
618
|
+
}
|
|
619
|
+
tx = {
|
|
620
|
+
upsertSession: (storage, incarnation) => this.upsertSession(storage, incarnation),
|
|
621
|
+
getHead: (id) => this.getHead(id),
|
|
622
|
+
getSeedLength: (id) => this.getSeedLength(id),
|
|
623
|
+
updateSeedLength: (id, seedLength) => this.updateSeedLength(id, seedLength),
|
|
624
|
+
insertEvents: (events) => this.insertEvents(events),
|
|
625
|
+
insertBridges: (rows) => this.insertBridges(rows),
|
|
626
|
+
updateHead: (id, headEventId, headSequence) => this.updateHead(id, headEventId, headSequence),
|
|
627
|
+
bumpRevision: (id) => this.bumpRevision(id),
|
|
628
|
+
deleteBridgeTail: (id, fromSequence) => this.deleteBridgeTail(id, fromSequence),
|
|
629
|
+
getPrevBridge: (id, sequence) => this.getPrevBridge(id, sequence)
|
|
630
|
+
};
|
|
631
|
+
async upsertSession(storage, incarnation) {
|
|
632
|
+
this.db.insert(tSessions).values(sessionInsertRow(storage, incarnation)).onConflictDoUpdate({
|
|
633
|
+
target: tSessions.fSessionId,
|
|
634
|
+
set: sessionConflictRow(storage)
|
|
635
|
+
}).run();
|
|
636
|
+
}
|
|
637
|
+
async getHead(id) {
|
|
638
|
+
const head = this.db.select({
|
|
639
|
+
fHeadEventId: tSessions.fHeadEventId,
|
|
640
|
+
fHeadSequence: tSessions.fHeadSequence
|
|
641
|
+
}).from(tSessions).where(eq(tSessions.fSessionId, id)).get();
|
|
642
|
+
/* v8 ignore next -- appendBatch/commitRepair always materialize the row before reading the head */
|
|
643
|
+
if (head === void 0) throw new Error(`session "${id}" has no materialized row`);
|
|
644
|
+
return head;
|
|
645
|
+
}
|
|
646
|
+
async getSeedLength(id) {
|
|
647
|
+
const row = this.db.select({ fSeedLength: tSessions.fSeedLength }).from(tSessions).where(eq(tSessions.fSessionId, id)).get();
|
|
648
|
+
/* v8 ignore next -- rewind always materializes the row before reading the seed length */
|
|
649
|
+
if (row === void 0) throw new Error(`session "${id}" has no materialized row`);
|
|
650
|
+
return row.fSeedLength;
|
|
651
|
+
}
|
|
652
|
+
async updateSeedLength(id, seedLength) {
|
|
653
|
+
this.db.update(tSessions).set({ fSeedLength: seedLength }).where(eq(tSessions.fSessionId, id)).run();
|
|
654
|
+
}
|
|
655
|
+
static INSERT_BATCH_ROWS = 1e3;
|
|
656
|
+
async insertEvents(events) {
|
|
657
|
+
if (events.length === 0) return;
|
|
658
|
+
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();
|
|
659
|
+
}
|
|
660
|
+
async insertBridges(rows) {
|
|
661
|
+
if (rows.length === 0) return;
|
|
662
|
+
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();
|
|
663
|
+
}
|
|
664
|
+
async updateHead(id, headEventId, headSequence) {
|
|
665
|
+
this.db.update(tSessions).set({
|
|
666
|
+
fHeadEventId: headEventId,
|
|
667
|
+
fHeadSequence: headSequence
|
|
668
|
+
}).where(eq(tSessions.fSessionId, id)).run();
|
|
669
|
+
}
|
|
670
|
+
async bumpRevision(id) {
|
|
671
|
+
this.db.update(tSessions).set({ fRevision: sql`${tSessions.fRevision} + 1` }).where(eq(tSessions.fSessionId, id)).run();
|
|
672
|
+
}
|
|
673
|
+
async deleteBridgeTail(id, fromSequence) {
|
|
674
|
+
this.db.delete(tSessionEvents).where(and(eq(tSessionEvents.fSessionId, id), gte(tSessionEvents.fSequence, fromSequence))).run();
|
|
675
|
+
}
|
|
676
|
+
async getPrevBridge(id, sequence) {
|
|
677
|
+
return this.db.select({
|
|
678
|
+
fEventId: tSessionEvents.fEventId,
|
|
679
|
+
fSequence: tSessionEvents.fSequence
|
|
680
|
+
}).from(tSessionEvents).where(and(eq(tSessionEvents.fSessionId, id), eq(tSessionEvents.fSequence, sequence))).get();
|
|
681
|
+
}
|
|
682
|
+
eventRows() {
|
|
683
|
+
return this.db.select({
|
|
684
|
+
fEventId: tSessionEvents.fEventId,
|
|
685
|
+
fSequence: tSessionEvents.fSequence,
|
|
686
|
+
fType: tEvents.fType,
|
|
687
|
+
fKind: tEvents.fKind,
|
|
688
|
+
fRole: tEvents.fRole,
|
|
689
|
+
fName: tEvents.fName,
|
|
690
|
+
fActionId: tEvents.fActionId,
|
|
691
|
+
fCreatedAt: tEvents.fCreatedAt,
|
|
692
|
+
fData: tEvents.fData,
|
|
693
|
+
fSurfaceOp: tSessionEvents.fSurfaceOp
|
|
694
|
+
}).from(tSessionEvents).innerJoin(tEvents, eq(tSessionEvents.fEventId, tEvents.fEventId));
|
|
695
|
+
}
|
|
696
|
+
};
|
|
697
|
+
//#endregion
|
|
698
|
+
export { SCHEMA_VERSION as a, eventKind as c, tSchemaMeta as d, tSessionEvents as f, WriteGuard as g, toPostgresSchema as h, EVENT_ENCODING as i, tEvents as l, postgresTableDefs as m, openDatabase as n, SESSION_PERSISTENCE_SQLITE_APPLICATION_ID as o, tSessions as p, DEFAULT_BUSY_TIMEOUT_MS as r, eventDimensions as s, SqliteBackend as t, tPersistenceState as u };
|