@agi-cli/database 0.1.55

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.
@@ -0,0 +1,444 @@
1
+ {
2
+ "version": "6",
3
+ "dialect": "sqlite",
4
+ "id": "dcfa8932-b15c-4c9b-9846-07331968bb2c",
5
+ "prevId": "c0260f78-cf4f-4305-a086-c1def673eebb",
6
+ "tables": {
7
+ "artifacts": {
8
+ "name": "artifacts",
9
+ "columns": {
10
+ "id": {
11
+ "name": "id",
12
+ "type": "text",
13
+ "primaryKey": true,
14
+ "notNull": true,
15
+ "autoincrement": false
16
+ },
17
+ "message_part_id": {
18
+ "name": "message_part_id",
19
+ "type": "text",
20
+ "primaryKey": false,
21
+ "notNull": false,
22
+ "autoincrement": false
23
+ },
24
+ "kind": {
25
+ "name": "kind",
26
+ "type": "text",
27
+ "primaryKey": false,
28
+ "notNull": true,
29
+ "autoincrement": false
30
+ },
31
+ "path": {
32
+ "name": "path",
33
+ "type": "text",
34
+ "primaryKey": false,
35
+ "notNull": false,
36
+ "autoincrement": false
37
+ },
38
+ "mime": {
39
+ "name": "mime",
40
+ "type": "text",
41
+ "primaryKey": false,
42
+ "notNull": false,
43
+ "autoincrement": false
44
+ },
45
+ "size": {
46
+ "name": "size",
47
+ "type": "integer",
48
+ "primaryKey": false,
49
+ "notNull": false,
50
+ "autoincrement": false
51
+ },
52
+ "sha256": {
53
+ "name": "sha256",
54
+ "type": "text",
55
+ "primaryKey": false,
56
+ "notNull": false,
57
+ "autoincrement": false
58
+ }
59
+ },
60
+ "indexes": {
61
+ "artifacts_message_part_id_unique": {
62
+ "name": "artifacts_message_part_id_unique",
63
+ "columns": ["message_part_id"],
64
+ "isUnique": true
65
+ }
66
+ },
67
+ "foreignKeys": {
68
+ "artifacts_message_part_id_message_parts_id_fk": {
69
+ "name": "artifacts_message_part_id_message_parts_id_fk",
70
+ "tableFrom": "artifacts",
71
+ "tableTo": "message_parts",
72
+ "columnsFrom": ["message_part_id"],
73
+ "columnsTo": ["id"],
74
+ "onDelete": "cascade",
75
+ "onUpdate": "no action"
76
+ }
77
+ },
78
+ "compositePrimaryKeys": {},
79
+ "uniqueConstraints": {},
80
+ "checkConstraints": {}
81
+ },
82
+ "message_parts": {
83
+ "name": "message_parts",
84
+ "columns": {
85
+ "id": {
86
+ "name": "id",
87
+ "type": "text",
88
+ "primaryKey": true,
89
+ "notNull": true,
90
+ "autoincrement": false
91
+ },
92
+ "message_id": {
93
+ "name": "message_id",
94
+ "type": "text",
95
+ "primaryKey": false,
96
+ "notNull": true,
97
+ "autoincrement": false
98
+ },
99
+ "index": {
100
+ "name": "index",
101
+ "type": "integer",
102
+ "primaryKey": false,
103
+ "notNull": true,
104
+ "autoincrement": false
105
+ },
106
+ "step_index": {
107
+ "name": "step_index",
108
+ "type": "integer",
109
+ "primaryKey": false,
110
+ "notNull": false,
111
+ "autoincrement": false
112
+ },
113
+ "type": {
114
+ "name": "type",
115
+ "type": "text",
116
+ "primaryKey": false,
117
+ "notNull": true,
118
+ "autoincrement": false
119
+ },
120
+ "content": {
121
+ "name": "content",
122
+ "type": "text",
123
+ "primaryKey": false,
124
+ "notNull": true,
125
+ "autoincrement": false
126
+ },
127
+ "agent": {
128
+ "name": "agent",
129
+ "type": "text",
130
+ "primaryKey": false,
131
+ "notNull": true,
132
+ "autoincrement": false
133
+ },
134
+ "provider": {
135
+ "name": "provider",
136
+ "type": "text",
137
+ "primaryKey": false,
138
+ "notNull": true,
139
+ "autoincrement": false
140
+ },
141
+ "model": {
142
+ "name": "model",
143
+ "type": "text",
144
+ "primaryKey": false,
145
+ "notNull": true,
146
+ "autoincrement": false
147
+ },
148
+ "started_at": {
149
+ "name": "started_at",
150
+ "type": "integer",
151
+ "primaryKey": false,
152
+ "notNull": false,
153
+ "autoincrement": false
154
+ },
155
+ "completed_at": {
156
+ "name": "completed_at",
157
+ "type": "integer",
158
+ "primaryKey": false,
159
+ "notNull": false,
160
+ "autoincrement": false
161
+ },
162
+ "tool_name": {
163
+ "name": "tool_name",
164
+ "type": "text",
165
+ "primaryKey": false,
166
+ "notNull": false,
167
+ "autoincrement": false
168
+ },
169
+ "tool_call_id": {
170
+ "name": "tool_call_id",
171
+ "type": "text",
172
+ "primaryKey": false,
173
+ "notNull": false,
174
+ "autoincrement": false
175
+ },
176
+ "tool_duration_ms": {
177
+ "name": "tool_duration_ms",
178
+ "type": "integer",
179
+ "primaryKey": false,
180
+ "notNull": false,
181
+ "autoincrement": false
182
+ }
183
+ },
184
+ "indexes": {},
185
+ "foreignKeys": {
186
+ "message_parts_message_id_messages_id_fk": {
187
+ "name": "message_parts_message_id_messages_id_fk",
188
+ "tableFrom": "message_parts",
189
+ "tableTo": "messages",
190
+ "columnsFrom": ["message_id"],
191
+ "columnsTo": ["id"],
192
+ "onDelete": "cascade",
193
+ "onUpdate": "no action"
194
+ }
195
+ },
196
+ "compositePrimaryKeys": {},
197
+ "uniqueConstraints": {},
198
+ "checkConstraints": {}
199
+ },
200
+ "messages": {
201
+ "name": "messages",
202
+ "columns": {
203
+ "id": {
204
+ "name": "id",
205
+ "type": "text",
206
+ "primaryKey": true,
207
+ "notNull": true,
208
+ "autoincrement": false
209
+ },
210
+ "session_id": {
211
+ "name": "session_id",
212
+ "type": "text",
213
+ "primaryKey": false,
214
+ "notNull": true,
215
+ "autoincrement": false
216
+ },
217
+ "role": {
218
+ "name": "role",
219
+ "type": "text",
220
+ "primaryKey": false,
221
+ "notNull": true,
222
+ "autoincrement": false
223
+ },
224
+ "status": {
225
+ "name": "status",
226
+ "type": "text",
227
+ "primaryKey": false,
228
+ "notNull": true,
229
+ "autoincrement": false
230
+ },
231
+ "agent": {
232
+ "name": "agent",
233
+ "type": "text",
234
+ "primaryKey": false,
235
+ "notNull": true,
236
+ "autoincrement": false
237
+ },
238
+ "provider": {
239
+ "name": "provider",
240
+ "type": "text",
241
+ "primaryKey": false,
242
+ "notNull": true,
243
+ "autoincrement": false
244
+ },
245
+ "model": {
246
+ "name": "model",
247
+ "type": "text",
248
+ "primaryKey": false,
249
+ "notNull": true,
250
+ "autoincrement": false
251
+ },
252
+ "created_at": {
253
+ "name": "created_at",
254
+ "type": "integer",
255
+ "primaryKey": false,
256
+ "notNull": true,
257
+ "autoincrement": false
258
+ },
259
+ "completed_at": {
260
+ "name": "completed_at",
261
+ "type": "integer",
262
+ "primaryKey": false,
263
+ "notNull": false,
264
+ "autoincrement": false
265
+ },
266
+ "latency_ms": {
267
+ "name": "latency_ms",
268
+ "type": "integer",
269
+ "primaryKey": false,
270
+ "notNull": false,
271
+ "autoincrement": false
272
+ },
273
+ "prompt_tokens": {
274
+ "name": "prompt_tokens",
275
+ "type": "integer",
276
+ "primaryKey": false,
277
+ "notNull": false,
278
+ "autoincrement": false
279
+ },
280
+ "completion_tokens": {
281
+ "name": "completion_tokens",
282
+ "type": "integer",
283
+ "primaryKey": false,
284
+ "notNull": false,
285
+ "autoincrement": false
286
+ },
287
+ "total_tokens": {
288
+ "name": "total_tokens",
289
+ "type": "integer",
290
+ "primaryKey": false,
291
+ "notNull": false,
292
+ "autoincrement": false
293
+ },
294
+ "error": {
295
+ "name": "error",
296
+ "type": "text",
297
+ "primaryKey": false,
298
+ "notNull": false,
299
+ "autoincrement": false
300
+ },
301
+ "error_type": {
302
+ "name": "error_type",
303
+ "type": "text",
304
+ "primaryKey": false,
305
+ "notNull": false,
306
+ "autoincrement": false
307
+ },
308
+ "error_details": {
309
+ "name": "error_details",
310
+ "type": "text",
311
+ "primaryKey": false,
312
+ "notNull": false,
313
+ "autoincrement": false
314
+ },
315
+ "is_aborted": {
316
+ "name": "is_aborted",
317
+ "type": "integer",
318
+ "primaryKey": false,
319
+ "notNull": false,
320
+ "autoincrement": false
321
+ }
322
+ },
323
+ "indexes": {},
324
+ "foreignKeys": {
325
+ "messages_session_id_sessions_id_fk": {
326
+ "name": "messages_session_id_sessions_id_fk",
327
+ "tableFrom": "messages",
328
+ "tableTo": "sessions",
329
+ "columnsFrom": ["session_id"],
330
+ "columnsTo": ["id"],
331
+ "onDelete": "cascade",
332
+ "onUpdate": "no action"
333
+ }
334
+ },
335
+ "compositePrimaryKeys": {},
336
+ "uniqueConstraints": {},
337
+ "checkConstraints": {}
338
+ },
339
+ "sessions": {
340
+ "name": "sessions",
341
+ "columns": {
342
+ "id": {
343
+ "name": "id",
344
+ "type": "text",
345
+ "primaryKey": true,
346
+ "notNull": true,
347
+ "autoincrement": false
348
+ },
349
+ "title": {
350
+ "name": "title",
351
+ "type": "text",
352
+ "primaryKey": false,
353
+ "notNull": false,
354
+ "autoincrement": false
355
+ },
356
+ "agent": {
357
+ "name": "agent",
358
+ "type": "text",
359
+ "primaryKey": false,
360
+ "notNull": true,
361
+ "autoincrement": false
362
+ },
363
+ "provider": {
364
+ "name": "provider",
365
+ "type": "text",
366
+ "primaryKey": false,
367
+ "notNull": true,
368
+ "autoincrement": false
369
+ },
370
+ "model": {
371
+ "name": "model",
372
+ "type": "text",
373
+ "primaryKey": false,
374
+ "notNull": true,
375
+ "autoincrement": false
376
+ },
377
+ "project_path": {
378
+ "name": "project_path",
379
+ "type": "text",
380
+ "primaryKey": false,
381
+ "notNull": true,
382
+ "autoincrement": false
383
+ },
384
+ "created_at": {
385
+ "name": "created_at",
386
+ "type": "integer",
387
+ "primaryKey": false,
388
+ "notNull": true,
389
+ "autoincrement": false
390
+ },
391
+ "last_active_at": {
392
+ "name": "last_active_at",
393
+ "type": "integer",
394
+ "primaryKey": false,
395
+ "notNull": false,
396
+ "autoincrement": false
397
+ },
398
+ "total_input_tokens": {
399
+ "name": "total_input_tokens",
400
+ "type": "integer",
401
+ "primaryKey": false,
402
+ "notNull": false,
403
+ "autoincrement": false
404
+ },
405
+ "total_output_tokens": {
406
+ "name": "total_output_tokens",
407
+ "type": "integer",
408
+ "primaryKey": false,
409
+ "notNull": false,
410
+ "autoincrement": false
411
+ },
412
+ "total_tool_time_ms": {
413
+ "name": "total_tool_time_ms",
414
+ "type": "integer",
415
+ "primaryKey": false,
416
+ "notNull": false,
417
+ "autoincrement": false
418
+ },
419
+ "tool_counts_json": {
420
+ "name": "tool_counts_json",
421
+ "type": "text",
422
+ "primaryKey": false,
423
+ "notNull": false,
424
+ "autoincrement": false
425
+ }
426
+ },
427
+ "indexes": {},
428
+ "foreignKeys": {},
429
+ "compositePrimaryKeys": {},
430
+ "uniqueConstraints": {},
431
+ "checkConstraints": {}
432
+ }
433
+ },
434
+ "views": {},
435
+ "enums": {},
436
+ "_meta": {
437
+ "schemas": {},
438
+ "tables": {},
439
+ "columns": {}
440
+ },
441
+ "internal": {
442
+ "indexes": {}
443
+ }
444
+ }
@@ -0,0 +1,34 @@
1
+ {
2
+ "version": "7",
3
+ "dialect": "sqlite",
4
+ "entries": [
5
+ {
6
+ "idx": 0,
7
+ "version": "6",
8
+ "when": 1757903506317,
9
+ "tag": "0000_tense_shadow_king",
10
+ "breakpoints": true
11
+ },
12
+ {
13
+ "idx": 1,
14
+ "version": "6",
15
+ "when": 1757926724962,
16
+ "tag": "0001_past_kabuki",
17
+ "breakpoints": true
18
+ },
19
+ {
20
+ "idx": 2,
21
+ "version": "6",
22
+ "when": 1758380042084,
23
+ "tag": "0002_vengeful_warlock",
24
+ "breakpoints": true
25
+ },
26
+ {
27
+ "idx": 3,
28
+ "version": "6",
29
+ "when": 1759590176611,
30
+ "tag": "0003_pale_violations",
31
+ "breakpoints": true
32
+ }
33
+ ]
34
+ }
@@ -0,0 +1,14 @@
1
+ import { defineConfig } from 'drizzle-kit';
2
+ import { getLocalDataDir } from '@agi-cli/sdk';
3
+
4
+ const dataDir = getLocalDataDir(process.cwd());
5
+ const dbPath = `${dataDir}/agi.db`;
6
+
7
+ export default defineConfig({
8
+ dialect: 'sqlite',
9
+ schema: './src/schema.ts',
10
+ out: './drizzle',
11
+ dbCredentials: {
12
+ url: dbPath,
13
+ },
14
+ });
package/package.json ADDED
@@ -0,0 +1,33 @@
1
+ {
2
+ "name": "@agi-cli/database",
3
+ "version": "0.1.55",
4
+ "description": "Database and persistence layer for AGI CLI",
5
+ "type": "module",
6
+ "main": "./src/index.ts",
7
+ "types": "./src/index.ts",
8
+ "exports": {
9
+ ".": {
10
+ "import": "./src/index.ts",
11
+ "types": "./src/index.ts"
12
+ },
13
+ "./schema": {
14
+ "import": "./src/schema.ts",
15
+ "types": "./src/schema.ts"
16
+ }
17
+ },
18
+ "scripts": {
19
+ "dev": "bun run src/index.ts",
20
+ "test": "bun test",
21
+ "db:generate": "drizzle-kit generate --config=drizzle.config.ts",
22
+ "typecheck": "tsc --noEmit"
23
+ },
24
+ "dependencies": {
25
+ "@agi-cli/sdk": "0.1.55",
26
+ "drizzle-orm": "^0.44.5"
27
+ },
28
+ "devDependencies": {
29
+ "@types/bun": "latest",
30
+ "drizzle-kit": "^0.31.4",
31
+ "typescript": "^5"
32
+ }
33
+ }
package/src/index.ts ADDED
@@ -0,0 +1,75 @@
1
+ import { Database } from 'bun:sqlite';
2
+ import { drizzle, type BunSQLiteDatabase } from 'drizzle-orm/bun-sqlite';
3
+ import { loadConfig } from '@agi-cli/sdk';
4
+ import * as schema from './schema/index.ts';
5
+ import { bundledMigrations } from './runtime/migrations-bundled.ts';
6
+
7
+ const dbCache: Map<string, BunSQLiteDatabase<typeof schema>> = new Map();
8
+ const migratedPaths = new Set<string>();
9
+
10
+ export async function getDb(projectRootInput?: string) {
11
+ const cfg = await loadConfig(projectRootInput);
12
+ const dbPath = cfg.paths.dbPath;
13
+ // Data dir is ensured by loadConfig() already.
14
+
15
+ const key = dbPath;
16
+ const cached = dbCache.get(key);
17
+ if (cached) return cached;
18
+
19
+ const sqlite = new Database(dbPath, { create: true });
20
+ const db = drizzle(sqlite, { schema });
21
+
22
+ // Run migrations once per db path (apply any not yet applied)
23
+ if (!migratedPaths.has(dbPath)) {
24
+ try {
25
+ // Ensure migrations tracking table exists
26
+ sqlite.exec(
27
+ 'CREATE TABLE IF NOT EXISTS agi_migrations (name TEXT PRIMARY KEY, applied_at INTEGER NOT NULL)',
28
+ );
29
+
30
+ // Read applied migrations
31
+ const appliedRows = sqlite
32
+ .query('SELECT name FROM agi_migrations')
33
+ .all() as Array<{ name: string }>;
34
+ const applied = new Set(appliedRows.map((r) => r.name));
35
+
36
+ for (const m of bundledMigrations) {
37
+ if (applied.has(m.name)) continue;
38
+ try {
39
+ sqlite.exec('BEGIN TRANSACTION');
40
+ sqlite.exec(m.content);
41
+ sqlite.exec('COMMIT');
42
+ sqlite
43
+ .query(
44
+ 'INSERT INTO agi_migrations (name, applied_at) VALUES (?, ?)',
45
+ )
46
+ .run(m.name, Date.now());
47
+ } catch (err) {
48
+ // If migration fails due to already-applied schema (e.g., table exists / duplicate column), mark as applied and continue.
49
+ sqlite.exec('ROLLBACK');
50
+ const msg = String((err as Error)?.message ?? err);
51
+ const benign =
52
+ msg.includes('already exists') || msg.includes('duplicate column');
53
+ if (benign) {
54
+ sqlite
55
+ .query(
56
+ 'INSERT OR IGNORE INTO agi_migrations (name, applied_at) VALUES (?, ?)',
57
+ )
58
+ .run(m.name, Date.now());
59
+ continue;
60
+ }
61
+ throw err;
62
+ }
63
+ }
64
+ migratedPaths.add(dbPath);
65
+ } catch (error) {
66
+ console.error('❌ Local database migration failed:', error);
67
+ throw error;
68
+ }
69
+ }
70
+ dbCache.set(key, db);
71
+ return db;
72
+ }
73
+
74
+ export type DB = Awaited<ReturnType<typeof getDb>>;
75
+ export * as dbSchema from './schema/index.ts';
@@ -0,0 +1,20 @@
1
+ // Bundled Drizzle migrations for single-binary builds
2
+ // These imports ensure Bun embeds the SQL files into the executable.
3
+ // Order matters: keep in incremental order.
4
+ import mig0000 from '../../drizzle/0000_tense_shadow_king.sql' with {
5
+ type: 'text',
6
+ };
7
+ import mig0001 from '../../drizzle/0001_past_kabuki.sql' with { type: 'text' };
8
+ import mig0002 from '../../drizzle/0002_vengeful_warlock.sql' with {
9
+ type: 'text',
10
+ };
11
+ import mig0003 from '../../drizzle/0003_pale_violations.sql' with {
12
+ type: 'text',
13
+ };
14
+
15
+ export const bundledMigrations: Array<{ name: string; content: string }> = [
16
+ { name: '0000_tense_shadow_king.sql', content: mig0000 },
17
+ { name: '0001_past_kabuki.sql', content: mig0001 },
18
+ { name: '0002_vengeful_warlock.sql', content: mig0002 },
19
+ { name: '0003_pale_violations.sql', content: mig0003 },
20
+ ];
@@ -0,0 +1,14 @@
1
+ import { sqliteTable, text, integer } from 'drizzle-orm/sqlite-core';
2
+ import { messageParts } from './message-parts.ts';
3
+
4
+ export const artifacts = sqliteTable('artifacts', {
5
+ id: text('id').primaryKey(),
6
+ messagePartId: text('message_part_id')
7
+ .unique()
8
+ .references(() => messageParts.id, { onDelete: 'cascade' }),
9
+ kind: text('kind').notNull(), // 'file' | 'file_diff' | ...
10
+ path: text('path'),
11
+ mime: text('mime'),
12
+ size: integer('size'),
13
+ sha256: text('sha256'),
14
+ });
@@ -0,0 +1,36 @@
1
+ import { relations } from 'drizzle-orm';
2
+ export { sessions } from './sessions.ts';
3
+ export { messages } from './messages.ts';
4
+ export { messageParts } from './message-parts.ts';
5
+ export { artifacts } from './artifacts.ts';
6
+
7
+ import { sessions } from './sessions.ts';
8
+ import { messages } from './messages.ts';
9
+ import { messageParts } from './message-parts.ts';
10
+ import { artifacts } from './artifacts.ts';
11
+
12
+ export const sessionsRelations = relations(sessions, ({ many }) => ({
13
+ messages: many(messages),
14
+ }));
15
+
16
+ export const messagesRelations = relations(messages, ({ one, many }) => ({
17
+ session: one(sessions, {
18
+ fields: [messages.sessionId],
19
+ references: [sessions.id],
20
+ }),
21
+ parts: many(messageParts),
22
+ }));
23
+
24
+ export const messagePartsRelations = relations(messageParts, ({ one }) => ({
25
+ message: one(messages, {
26
+ fields: [messageParts.messageId],
27
+ references: [messages.id],
28
+ }),
29
+ }));
30
+
31
+ export const artifactsRelations = relations(artifacts, ({ one }) => ({
32
+ part: one(messageParts, {
33
+ fields: [artifacts.messagePartId],
34
+ references: [messageParts.id],
35
+ }),
36
+ }));
@@ -0,0 +1,23 @@
1
+ import { sqliteTable, text, integer } from 'drizzle-orm/sqlite-core';
2
+ import { messages } from './messages.ts';
3
+
4
+ export const messageParts = sqliteTable('message_parts', {
5
+ id: text('id').primaryKey(),
6
+ messageId: text('message_id')
7
+ .notNull()
8
+ .references(() => messages.id, { onDelete: 'cascade' }),
9
+ index: integer('index').notNull(),
10
+ stepIndex: integer('step_index'),
11
+ type: text('type').notNull(), // 'text' | 'tool_call' | 'tool_result' | 'image' | 'error'
12
+ content: text('content').notNull(), // JSON string
13
+ agent: text('agent').notNull(),
14
+ provider: text('provider').notNull(),
15
+ model: text('model').notNull(),
16
+ // Timestamps
17
+ startedAt: integer('started_at', { mode: 'number' }),
18
+ completedAt: integer('completed_at', { mode: 'number' }),
19
+ // Tool metadata (for tool_call/tool_result)
20
+ toolName: text('tool_name'),
21
+ toolCallId: text('tool_call_id'),
22
+ toolDurationMs: integer('tool_duration_ms'),
23
+ });