sparkecoder 0.1.3 → 0.1.5
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 +2 -2
- package/dist/agent/index.d.ts +3 -2
- package/dist/agent/index.js +813 -566
- package/dist/agent/index.js.map +1 -1
- package/dist/bash-CGAqW7HR.d.ts +80 -0
- package/dist/cli.js +3044 -1081
- package/dist/cli.js.map +1 -1
- package/dist/db/index.d.ts +67 -3
- package/dist/db/index.js +252 -13
- package/dist/db/index.js.map +1 -1
- package/dist/{index-BxpkHy7X.d.ts → index-Btr542-G.d.ts} +18 -2
- package/dist/index.d.ts +178 -77
- package/dist/index.js +2537 -976
- package/dist/index.js.map +1 -1
- package/dist/{schema-EPbMMFza.d.ts → schema-CkrIadxa.d.ts} +371 -5
- package/dist/server/index.d.ts +9 -2
- package/dist/server/index.js +2483 -945
- package/dist/server/index.js.map +1 -1
- package/dist/tools/index.d.ts +5 -138
- package/dist/tools/index.js +787 -723
- package/dist/tools/index.js.map +1 -1
- package/package.json +4 -2
package/dist/db/index.d.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import * as drizzle_orm_better_sqlite3 from 'drizzle-orm/better-sqlite3';
|
|
2
2
|
import Database from 'better-sqlite3';
|
|
3
|
-
import { s as schema, M as ModelMessage, a as Message, N as NewSession, S as Session, L as LoadedSkill,
|
|
4
|
-
export { g as SessionConfig } from '../schema-EPbMMFza.js';
|
|
3
|
+
import { C as Checkpoint, F as FileBackup, s as schema, M as ModelMessage, a as Message, N as NewSession, S as Session, b as SessionConfig, L as LoadedSkill, c as NewTodoItem, T as TodoItem, d as NewToolExecution, e as ToolExecution, A as ActiveStream, f as NewTerminal, g as Terminal } from '../schema-CkrIadxa.js';
|
|
5
4
|
import 'drizzle-orm/sqlite-core';
|
|
6
5
|
|
|
7
6
|
declare function initDatabase(dbPath: string): drizzle_orm_better_sqlite3.BetterSQLite3Database<typeof schema> & {
|
|
@@ -16,6 +15,12 @@ declare const sessionQueries: {
|
|
|
16
15
|
getById(id: string): Session | undefined;
|
|
17
16
|
list(limit?: number, offset?: number): Session[];
|
|
18
17
|
updateStatus(id: string, status: Session["status"]): Session | undefined;
|
|
18
|
+
updateModel(id: string, model: string): Session | undefined;
|
|
19
|
+
update(id: string, updates: {
|
|
20
|
+
model?: string;
|
|
21
|
+
name?: string;
|
|
22
|
+
config?: SessionConfig;
|
|
23
|
+
}): Session | undefined;
|
|
19
24
|
delete(id: string): boolean;
|
|
20
25
|
};
|
|
21
26
|
declare const messageQueries: {
|
|
@@ -44,6 +49,11 @@ declare const messageQueries: {
|
|
|
44
49
|
getRecentBySession(sessionId: string, limit?: number): Message[];
|
|
45
50
|
countBySession(sessionId: string): number;
|
|
46
51
|
deleteBySession(sessionId: string): number;
|
|
52
|
+
/**
|
|
53
|
+
* Delete all messages with sequence >= the given sequence number
|
|
54
|
+
* (Used when reverting to a checkpoint)
|
|
55
|
+
*/
|
|
56
|
+
deleteFromSequence(sessionId: string, fromSequence: number): number;
|
|
47
57
|
};
|
|
48
58
|
declare const toolExecutionQueries: {
|
|
49
59
|
create(data: Omit<NewToolExecution, "id" | "startedAt">): ToolExecution;
|
|
@@ -54,6 +64,11 @@ declare const toolExecutionQueries: {
|
|
|
54
64
|
reject(id: string): ToolExecution | undefined;
|
|
55
65
|
complete(id: string, output: unknown, error?: string): ToolExecution | undefined;
|
|
56
66
|
getBySession(sessionId: string): ToolExecution[];
|
|
67
|
+
/**
|
|
68
|
+
* Delete all tool executions after a given timestamp
|
|
69
|
+
* (Used when reverting to a checkpoint)
|
|
70
|
+
*/
|
|
71
|
+
deleteAfterTime(sessionId: string, afterTime: Date): number;
|
|
57
72
|
};
|
|
58
73
|
declare const todoQueries: {
|
|
59
74
|
create(data: Omit<NewTodoItem, "id" | "createdAt" | "updatedAt">): TodoItem;
|
|
@@ -81,5 +96,54 @@ declare const terminalQueries: {
|
|
|
81
96
|
delete(id: string): boolean;
|
|
82
97
|
deleteBySession(sessionId: string): number;
|
|
83
98
|
};
|
|
99
|
+
declare const activeStreamQueries: {
|
|
100
|
+
create(sessionId: string, streamId: string): ActiveStream;
|
|
101
|
+
getBySessionId(sessionId: string): ActiveStream | undefined;
|
|
102
|
+
getByStreamId(streamId: string): ActiveStream | undefined;
|
|
103
|
+
finish(streamId: string): ActiveStream | undefined;
|
|
104
|
+
markError(streamId: string): ActiveStream | undefined;
|
|
105
|
+
deleteBySession(sessionId: string): number;
|
|
106
|
+
};
|
|
107
|
+
declare const checkpointQueries: {
|
|
108
|
+
create(data: {
|
|
109
|
+
sessionId: string;
|
|
110
|
+
messageSequence: number;
|
|
111
|
+
gitHead?: string;
|
|
112
|
+
}): Checkpoint;
|
|
113
|
+
getById(id: string): Checkpoint | undefined;
|
|
114
|
+
getBySession(sessionId: string): Checkpoint[];
|
|
115
|
+
getByMessageSequence(sessionId: string, messageSequence: number): Checkpoint | undefined;
|
|
116
|
+
getLatest(sessionId: string): Checkpoint | undefined;
|
|
117
|
+
/**
|
|
118
|
+
* Delete all checkpoints after a given sequence number
|
|
119
|
+
* (Used when reverting to a checkpoint)
|
|
120
|
+
*/
|
|
121
|
+
deleteAfterSequence(sessionId: string, messageSequence: number): number;
|
|
122
|
+
deleteBySession(sessionId: string): number;
|
|
123
|
+
};
|
|
124
|
+
declare const fileBackupQueries: {
|
|
125
|
+
create(data: {
|
|
126
|
+
checkpointId: string;
|
|
127
|
+
sessionId: string;
|
|
128
|
+
filePath: string;
|
|
129
|
+
originalContent: string | null;
|
|
130
|
+
existed: boolean;
|
|
131
|
+
}): FileBackup;
|
|
132
|
+
getByCheckpoint(checkpointId: string): FileBackup[];
|
|
133
|
+
getBySession(sessionId: string): FileBackup[];
|
|
134
|
+
/**
|
|
135
|
+
* Get all file backups from a given checkpoint sequence onwards (inclusive)
|
|
136
|
+
* (Used when reverting - need to restore these files)
|
|
137
|
+
*
|
|
138
|
+
* When reverting to checkpoint X, we need backups from checkpoint X and all later ones
|
|
139
|
+
* because checkpoint X's backups represent the state BEFORE processing message X.
|
|
140
|
+
*/
|
|
141
|
+
getFromSequence(sessionId: string, messageSequence: number): FileBackup[];
|
|
142
|
+
/**
|
|
143
|
+
* Check if a file already has a backup in the current checkpoint
|
|
144
|
+
*/
|
|
145
|
+
hasBackup(checkpointId: string, filePath: string): boolean;
|
|
146
|
+
deleteBySession(sessionId: string): number;
|
|
147
|
+
};
|
|
84
148
|
|
|
85
|
-
export { Message, ModelMessage, Session, Terminal, TodoItem, ToolExecution, closeDatabase, getDb, initDatabase, messageQueries, sessionQueries, skillQueries, terminalQueries, todoQueries, toolExecutionQueries };
|
|
149
|
+
export { ActiveStream, Checkpoint, FileBackup, Message, ModelMessage, Session, SessionConfig, Terminal, TodoItem, ToolExecution, activeStreamQueries, checkpointQueries, closeDatabase, fileBackupQueries, getDb, initDatabase, messageQueries, sessionQueries, skillQueries, terminalQueries, todoQueries, toolExecutionQueries };
|
package/dist/db/index.js
CHANGED
|
@@ -13,6 +13,9 @@ import { nanoid } from "nanoid";
|
|
|
13
13
|
// src/db/schema.ts
|
|
14
14
|
var schema_exports = {};
|
|
15
15
|
__export(schema_exports, {
|
|
16
|
+
activeStreams: () => activeStreams,
|
|
17
|
+
checkpoints: () => checkpoints,
|
|
18
|
+
fileBackups: () => fileBackups,
|
|
16
19
|
loadedSkills: () => loadedSkills,
|
|
17
20
|
messages: () => messages,
|
|
18
21
|
sessions: () => sessions,
|
|
@@ -88,6 +91,37 @@ var terminals = sqliteTable("terminals", {
|
|
|
88
91
|
createdAt: integer("created_at", { mode: "timestamp" }).notNull().$defaultFn(() => /* @__PURE__ */ new Date()),
|
|
89
92
|
stoppedAt: integer("stopped_at", { mode: "timestamp" })
|
|
90
93
|
});
|
|
94
|
+
var activeStreams = sqliteTable("active_streams", {
|
|
95
|
+
id: text("id").primaryKey(),
|
|
96
|
+
sessionId: text("session_id").notNull().references(() => sessions.id, { onDelete: "cascade" }),
|
|
97
|
+
streamId: text("stream_id").notNull().unique(),
|
|
98
|
+
// Unique stream identifier
|
|
99
|
+
status: text("status", { enum: ["active", "finished", "error"] }).notNull().default("active"),
|
|
100
|
+
createdAt: integer("created_at", { mode: "timestamp" }).notNull().$defaultFn(() => /* @__PURE__ */ new Date()),
|
|
101
|
+
finishedAt: integer("finished_at", { mode: "timestamp" })
|
|
102
|
+
});
|
|
103
|
+
var checkpoints = sqliteTable("checkpoints", {
|
|
104
|
+
id: text("id").primaryKey(),
|
|
105
|
+
sessionId: text("session_id").notNull().references(() => sessions.id, { onDelete: "cascade" }),
|
|
106
|
+
// The message sequence number this checkpoint was created BEFORE
|
|
107
|
+
// (i.e., the state before this user message was processed)
|
|
108
|
+
messageSequence: integer("message_sequence").notNull(),
|
|
109
|
+
// Optional git commit hash if in a git repo
|
|
110
|
+
gitHead: text("git_head"),
|
|
111
|
+
createdAt: integer("created_at", { mode: "timestamp" }).notNull().$defaultFn(() => /* @__PURE__ */ new Date())
|
|
112
|
+
});
|
|
113
|
+
var fileBackups = sqliteTable("file_backups", {
|
|
114
|
+
id: text("id").primaryKey(),
|
|
115
|
+
checkpointId: text("checkpoint_id").notNull().references(() => checkpoints.id, { onDelete: "cascade" }),
|
|
116
|
+
sessionId: text("session_id").notNull().references(() => sessions.id, { onDelete: "cascade" }),
|
|
117
|
+
// Relative path from working directory
|
|
118
|
+
filePath: text("file_path").notNull(),
|
|
119
|
+
// Original content (null means file didn't exist before)
|
|
120
|
+
originalContent: text("original_content"),
|
|
121
|
+
// Whether the file existed before this checkpoint
|
|
122
|
+
existed: integer("existed", { mode: "boolean" }).notNull().default(true),
|
|
123
|
+
createdAt: integer("created_at", { mode: "timestamp" }).notNull().$defaultFn(() => /* @__PURE__ */ new Date())
|
|
124
|
+
});
|
|
91
125
|
|
|
92
126
|
// src/db/index.ts
|
|
93
127
|
var db = null;
|
|
@@ -97,14 +131,7 @@ function initDatabase(dbPath) {
|
|
|
97
131
|
sqlite.pragma("journal_mode = WAL");
|
|
98
132
|
db = drizzle(sqlite, { schema: schema_exports });
|
|
99
133
|
sqlite.exec(`
|
|
100
|
-
|
|
101
|
-
DROP TABLE IF EXISTS loaded_skills;
|
|
102
|
-
DROP TABLE IF EXISTS todo_items;
|
|
103
|
-
DROP TABLE IF EXISTS tool_executions;
|
|
104
|
-
DROP TABLE IF EXISTS messages;
|
|
105
|
-
DROP TABLE IF EXISTS sessions;
|
|
106
|
-
|
|
107
|
-
CREATE TABLE sessions (
|
|
134
|
+
CREATE TABLE IF NOT EXISTS sessions (
|
|
108
135
|
id TEXT PRIMARY KEY,
|
|
109
136
|
name TEXT,
|
|
110
137
|
working_directory TEXT NOT NULL,
|
|
@@ -115,7 +142,7 @@ function initDatabase(dbPath) {
|
|
|
115
142
|
updated_at INTEGER NOT NULL
|
|
116
143
|
);
|
|
117
144
|
|
|
118
|
-
CREATE TABLE messages (
|
|
145
|
+
CREATE TABLE IF NOT EXISTS messages (
|
|
119
146
|
id TEXT PRIMARY KEY,
|
|
120
147
|
session_id TEXT NOT NULL REFERENCES sessions(id) ON DELETE CASCADE,
|
|
121
148
|
model_message TEXT NOT NULL,
|
|
@@ -123,7 +150,7 @@ function initDatabase(dbPath) {
|
|
|
123
150
|
created_at INTEGER NOT NULL
|
|
124
151
|
);
|
|
125
152
|
|
|
126
|
-
CREATE TABLE tool_executions (
|
|
153
|
+
CREATE TABLE IF NOT EXISTS tool_executions (
|
|
127
154
|
id TEXT PRIMARY KEY,
|
|
128
155
|
session_id TEXT NOT NULL REFERENCES sessions(id) ON DELETE CASCADE,
|
|
129
156
|
message_id TEXT REFERENCES messages(id) ON DELETE CASCADE,
|
|
@@ -138,7 +165,7 @@ function initDatabase(dbPath) {
|
|
|
138
165
|
completed_at INTEGER
|
|
139
166
|
);
|
|
140
167
|
|
|
141
|
-
CREATE TABLE todo_items (
|
|
168
|
+
CREATE TABLE IF NOT EXISTS todo_items (
|
|
142
169
|
id TEXT PRIMARY KEY,
|
|
143
170
|
session_id TEXT NOT NULL REFERENCES sessions(id) ON DELETE CASCADE,
|
|
144
171
|
content TEXT NOT NULL,
|
|
@@ -148,14 +175,14 @@ function initDatabase(dbPath) {
|
|
|
148
175
|
updated_at INTEGER NOT NULL
|
|
149
176
|
);
|
|
150
177
|
|
|
151
|
-
CREATE TABLE loaded_skills (
|
|
178
|
+
CREATE TABLE IF NOT EXISTS loaded_skills (
|
|
152
179
|
id TEXT PRIMARY KEY,
|
|
153
180
|
session_id TEXT NOT NULL REFERENCES sessions(id) ON DELETE CASCADE,
|
|
154
181
|
skill_name TEXT NOT NULL,
|
|
155
182
|
loaded_at INTEGER NOT NULL
|
|
156
183
|
);
|
|
157
184
|
|
|
158
|
-
CREATE TABLE terminals (
|
|
185
|
+
CREATE TABLE IF NOT EXISTS terminals (
|
|
159
186
|
id TEXT PRIMARY KEY,
|
|
160
187
|
session_id TEXT NOT NULL REFERENCES sessions(id) ON DELETE CASCADE,
|
|
161
188
|
name TEXT,
|
|
@@ -169,11 +196,45 @@ function initDatabase(dbPath) {
|
|
|
169
196
|
stopped_at INTEGER
|
|
170
197
|
);
|
|
171
198
|
|
|
199
|
+
-- Table for tracking active streams (for resumable streams)
|
|
200
|
+
CREATE TABLE IF NOT EXISTS active_streams (
|
|
201
|
+
id TEXT PRIMARY KEY,
|
|
202
|
+
session_id TEXT NOT NULL REFERENCES sessions(id) ON DELETE CASCADE,
|
|
203
|
+
stream_id TEXT NOT NULL UNIQUE,
|
|
204
|
+
status TEXT NOT NULL DEFAULT 'active',
|
|
205
|
+
created_at INTEGER NOT NULL,
|
|
206
|
+
finished_at INTEGER
|
|
207
|
+
);
|
|
208
|
+
|
|
209
|
+
-- Checkpoints table - created before each user message
|
|
210
|
+
CREATE TABLE IF NOT EXISTS checkpoints (
|
|
211
|
+
id TEXT PRIMARY KEY,
|
|
212
|
+
session_id TEXT NOT NULL REFERENCES sessions(id) ON DELETE CASCADE,
|
|
213
|
+
message_sequence INTEGER NOT NULL,
|
|
214
|
+
git_head TEXT,
|
|
215
|
+
created_at INTEGER NOT NULL
|
|
216
|
+
);
|
|
217
|
+
|
|
218
|
+
-- File backups table - stores original file content
|
|
219
|
+
CREATE TABLE IF NOT EXISTS file_backups (
|
|
220
|
+
id TEXT PRIMARY KEY,
|
|
221
|
+
checkpoint_id TEXT NOT NULL REFERENCES checkpoints(id) ON DELETE CASCADE,
|
|
222
|
+
session_id TEXT NOT NULL REFERENCES sessions(id) ON DELETE CASCADE,
|
|
223
|
+
file_path TEXT NOT NULL,
|
|
224
|
+
original_content TEXT,
|
|
225
|
+
existed INTEGER NOT NULL DEFAULT 1,
|
|
226
|
+
created_at INTEGER NOT NULL
|
|
227
|
+
);
|
|
228
|
+
|
|
172
229
|
CREATE INDEX IF NOT EXISTS idx_messages_session ON messages(session_id);
|
|
173
230
|
CREATE INDEX IF NOT EXISTS idx_tool_executions_session ON tool_executions(session_id);
|
|
174
231
|
CREATE INDEX IF NOT EXISTS idx_todo_items_session ON todo_items(session_id);
|
|
175
232
|
CREATE INDEX IF NOT EXISTS idx_loaded_skills_session ON loaded_skills(session_id);
|
|
176
233
|
CREATE INDEX IF NOT EXISTS idx_terminals_session ON terminals(session_id);
|
|
234
|
+
CREATE INDEX IF NOT EXISTS idx_active_streams_session ON active_streams(session_id);
|
|
235
|
+
CREATE INDEX IF NOT EXISTS idx_checkpoints_session ON checkpoints(session_id);
|
|
236
|
+
CREATE INDEX IF NOT EXISTS idx_file_backups_checkpoint ON file_backups(checkpoint_id);
|
|
237
|
+
CREATE INDEX IF NOT EXISTS idx_file_backups_session ON file_backups(session_id);
|
|
177
238
|
`);
|
|
178
239
|
return db;
|
|
179
240
|
}
|
|
@@ -211,6 +272,12 @@ var sessionQueries = {
|
|
|
211
272
|
updateStatus(id, status) {
|
|
212
273
|
return getDb().update(sessions).set({ status, updatedAt: /* @__PURE__ */ new Date() }).where(eq(sessions.id, id)).returning().get();
|
|
213
274
|
},
|
|
275
|
+
updateModel(id, model) {
|
|
276
|
+
return getDb().update(sessions).set({ model, updatedAt: /* @__PURE__ */ new Date() }).where(eq(sessions.id, id)).returning().get();
|
|
277
|
+
},
|
|
278
|
+
update(id, updates) {
|
|
279
|
+
return getDb().update(sessions).set({ ...updates, updatedAt: /* @__PURE__ */ new Date() }).where(eq(sessions.id, id)).returning().get();
|
|
280
|
+
},
|
|
214
281
|
delete(id) {
|
|
215
282
|
const result = getDb().delete(sessions).where(eq(sessions.id, id)).run();
|
|
216
283
|
return result.changes > 0;
|
|
@@ -284,6 +351,19 @@ var messageQueries = {
|
|
|
284
351
|
deleteBySession(sessionId) {
|
|
285
352
|
const result = getDb().delete(messages).where(eq(messages.sessionId, sessionId)).run();
|
|
286
353
|
return result.changes;
|
|
354
|
+
},
|
|
355
|
+
/**
|
|
356
|
+
* Delete all messages with sequence >= the given sequence number
|
|
357
|
+
* (Used when reverting to a checkpoint)
|
|
358
|
+
*/
|
|
359
|
+
deleteFromSequence(sessionId, fromSequence) {
|
|
360
|
+
const result = getDb().delete(messages).where(
|
|
361
|
+
and(
|
|
362
|
+
eq(messages.sessionId, sessionId),
|
|
363
|
+
sql`sequence >= ${fromSequence}`
|
|
364
|
+
)
|
|
365
|
+
).run();
|
|
366
|
+
return result.changes;
|
|
287
367
|
}
|
|
288
368
|
};
|
|
289
369
|
var toolExecutionQueries = {
|
|
@@ -327,6 +407,19 @@ var toolExecutionQueries = {
|
|
|
327
407
|
},
|
|
328
408
|
getBySession(sessionId) {
|
|
329
409
|
return getDb().select().from(toolExecutions).where(eq(toolExecutions.sessionId, sessionId)).orderBy(toolExecutions.startedAt).all();
|
|
410
|
+
},
|
|
411
|
+
/**
|
|
412
|
+
* Delete all tool executions after a given timestamp
|
|
413
|
+
* (Used when reverting to a checkpoint)
|
|
414
|
+
*/
|
|
415
|
+
deleteAfterTime(sessionId, afterTime) {
|
|
416
|
+
const result = getDb().delete(toolExecutions).where(
|
|
417
|
+
and(
|
|
418
|
+
eq(toolExecutions.sessionId, sessionId),
|
|
419
|
+
sql`started_at > ${afterTime.getTime()}`
|
|
420
|
+
)
|
|
421
|
+
).run();
|
|
422
|
+
return result.changes;
|
|
330
423
|
}
|
|
331
424
|
};
|
|
332
425
|
var todoQueries = {
|
|
@@ -436,8 +529,154 @@ var terminalQueries = {
|
|
|
436
529
|
return result.changes;
|
|
437
530
|
}
|
|
438
531
|
};
|
|
532
|
+
var activeStreamQueries = {
|
|
533
|
+
create(sessionId, streamId) {
|
|
534
|
+
const id = nanoid();
|
|
535
|
+
const result = getDb().insert(activeStreams).values({
|
|
536
|
+
id,
|
|
537
|
+
sessionId,
|
|
538
|
+
streamId,
|
|
539
|
+
status: "active",
|
|
540
|
+
createdAt: /* @__PURE__ */ new Date()
|
|
541
|
+
}).returning().get();
|
|
542
|
+
return result;
|
|
543
|
+
},
|
|
544
|
+
getBySessionId(sessionId) {
|
|
545
|
+
return getDb().select().from(activeStreams).where(
|
|
546
|
+
and(
|
|
547
|
+
eq(activeStreams.sessionId, sessionId),
|
|
548
|
+
eq(activeStreams.status, "active")
|
|
549
|
+
)
|
|
550
|
+
).get();
|
|
551
|
+
},
|
|
552
|
+
getByStreamId(streamId) {
|
|
553
|
+
return getDb().select().from(activeStreams).where(eq(activeStreams.streamId, streamId)).get();
|
|
554
|
+
},
|
|
555
|
+
finish(streamId) {
|
|
556
|
+
return getDb().update(activeStreams).set({ status: "finished", finishedAt: /* @__PURE__ */ new Date() }).where(eq(activeStreams.streamId, streamId)).returning().get();
|
|
557
|
+
},
|
|
558
|
+
markError(streamId) {
|
|
559
|
+
return getDb().update(activeStreams).set({ status: "error", finishedAt: /* @__PURE__ */ new Date() }).where(eq(activeStreams.streamId, streamId)).returning().get();
|
|
560
|
+
},
|
|
561
|
+
deleteBySession(sessionId) {
|
|
562
|
+
const result = getDb().delete(activeStreams).where(eq(activeStreams.sessionId, sessionId)).run();
|
|
563
|
+
return result.changes;
|
|
564
|
+
}
|
|
565
|
+
};
|
|
566
|
+
var checkpointQueries = {
|
|
567
|
+
create(data) {
|
|
568
|
+
const id = nanoid();
|
|
569
|
+
const result = getDb().insert(checkpoints).values({
|
|
570
|
+
id,
|
|
571
|
+
sessionId: data.sessionId,
|
|
572
|
+
messageSequence: data.messageSequence,
|
|
573
|
+
gitHead: data.gitHead,
|
|
574
|
+
createdAt: /* @__PURE__ */ new Date()
|
|
575
|
+
}).returning().get();
|
|
576
|
+
return result;
|
|
577
|
+
},
|
|
578
|
+
getById(id) {
|
|
579
|
+
return getDb().select().from(checkpoints).where(eq(checkpoints.id, id)).get();
|
|
580
|
+
},
|
|
581
|
+
getBySession(sessionId) {
|
|
582
|
+
return getDb().select().from(checkpoints).where(eq(checkpoints.sessionId, sessionId)).orderBy(checkpoints.messageSequence).all();
|
|
583
|
+
},
|
|
584
|
+
getByMessageSequence(sessionId, messageSequence) {
|
|
585
|
+
return getDb().select().from(checkpoints).where(
|
|
586
|
+
and(
|
|
587
|
+
eq(checkpoints.sessionId, sessionId),
|
|
588
|
+
eq(checkpoints.messageSequence, messageSequence)
|
|
589
|
+
)
|
|
590
|
+
).get();
|
|
591
|
+
},
|
|
592
|
+
getLatest(sessionId) {
|
|
593
|
+
return getDb().select().from(checkpoints).where(eq(checkpoints.sessionId, sessionId)).orderBy(desc(checkpoints.messageSequence)).limit(1).get();
|
|
594
|
+
},
|
|
595
|
+
/**
|
|
596
|
+
* Delete all checkpoints after a given sequence number
|
|
597
|
+
* (Used when reverting to a checkpoint)
|
|
598
|
+
*/
|
|
599
|
+
deleteAfterSequence(sessionId, messageSequence) {
|
|
600
|
+
const result = getDb().delete(checkpoints).where(
|
|
601
|
+
and(
|
|
602
|
+
eq(checkpoints.sessionId, sessionId),
|
|
603
|
+
sql`message_sequence > ${messageSequence}`
|
|
604
|
+
)
|
|
605
|
+
).run();
|
|
606
|
+
return result.changes;
|
|
607
|
+
},
|
|
608
|
+
deleteBySession(sessionId) {
|
|
609
|
+
const result = getDb().delete(checkpoints).where(eq(checkpoints.sessionId, sessionId)).run();
|
|
610
|
+
return result.changes;
|
|
611
|
+
}
|
|
612
|
+
};
|
|
613
|
+
var fileBackupQueries = {
|
|
614
|
+
create(data) {
|
|
615
|
+
const id = nanoid();
|
|
616
|
+
const result = getDb().insert(fileBackups).values({
|
|
617
|
+
id,
|
|
618
|
+
checkpointId: data.checkpointId,
|
|
619
|
+
sessionId: data.sessionId,
|
|
620
|
+
filePath: data.filePath,
|
|
621
|
+
originalContent: data.originalContent,
|
|
622
|
+
existed: data.existed,
|
|
623
|
+
createdAt: /* @__PURE__ */ new Date()
|
|
624
|
+
}).returning().get();
|
|
625
|
+
return result;
|
|
626
|
+
},
|
|
627
|
+
getByCheckpoint(checkpointId) {
|
|
628
|
+
return getDb().select().from(fileBackups).where(eq(fileBackups.checkpointId, checkpointId)).all();
|
|
629
|
+
},
|
|
630
|
+
getBySession(sessionId) {
|
|
631
|
+
return getDb().select().from(fileBackups).where(eq(fileBackups.sessionId, sessionId)).orderBy(fileBackups.createdAt).all();
|
|
632
|
+
},
|
|
633
|
+
/**
|
|
634
|
+
* Get all file backups from a given checkpoint sequence onwards (inclusive)
|
|
635
|
+
* (Used when reverting - need to restore these files)
|
|
636
|
+
*
|
|
637
|
+
* When reverting to checkpoint X, we need backups from checkpoint X and all later ones
|
|
638
|
+
* because checkpoint X's backups represent the state BEFORE processing message X.
|
|
639
|
+
*/
|
|
640
|
+
getFromSequence(sessionId, messageSequence) {
|
|
641
|
+
const checkpointsFrom = getDb().select().from(checkpoints).where(
|
|
642
|
+
and(
|
|
643
|
+
eq(checkpoints.sessionId, sessionId),
|
|
644
|
+
sql`message_sequence >= ${messageSequence}`
|
|
645
|
+
)
|
|
646
|
+
).all();
|
|
647
|
+
if (checkpointsFrom.length === 0) {
|
|
648
|
+
return [];
|
|
649
|
+
}
|
|
650
|
+
const checkpointIds = checkpointsFrom.map((c) => c.id);
|
|
651
|
+
const allBackups = [];
|
|
652
|
+
for (const cpId of checkpointIds) {
|
|
653
|
+
const backups = getDb().select().from(fileBackups).where(eq(fileBackups.checkpointId, cpId)).all();
|
|
654
|
+
allBackups.push(...backups);
|
|
655
|
+
}
|
|
656
|
+
return allBackups;
|
|
657
|
+
},
|
|
658
|
+
/**
|
|
659
|
+
* Check if a file already has a backup in the current checkpoint
|
|
660
|
+
*/
|
|
661
|
+
hasBackup(checkpointId, filePath) {
|
|
662
|
+
const result = getDb().select().from(fileBackups).where(
|
|
663
|
+
and(
|
|
664
|
+
eq(fileBackups.checkpointId, checkpointId),
|
|
665
|
+
eq(fileBackups.filePath, filePath)
|
|
666
|
+
)
|
|
667
|
+
).get();
|
|
668
|
+
return !!result;
|
|
669
|
+
},
|
|
670
|
+
deleteBySession(sessionId) {
|
|
671
|
+
const result = getDb().delete(fileBackups).where(eq(fileBackups.sessionId, sessionId)).run();
|
|
672
|
+
return result.changes;
|
|
673
|
+
}
|
|
674
|
+
};
|
|
439
675
|
export {
|
|
676
|
+
activeStreamQueries,
|
|
677
|
+
checkpointQueries,
|
|
440
678
|
closeDatabase,
|
|
679
|
+
fileBackupQueries,
|
|
441
680
|
getDb,
|
|
442
681
|
initDatabase,
|
|
443
682
|
messageQueries,
|
package/dist/db/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/db/index.ts","../../src/db/schema.ts"],"sourcesContent":["import Database from 'better-sqlite3';\nimport { drizzle } from 'drizzle-orm/better-sqlite3';\nimport { eq, desc, and, sql } from 'drizzle-orm';\nimport { nanoid } from 'nanoid';\nimport * as schema from './schema.js';\nimport type {\n Session,\n NewSession,\n Message,\n NewMessage,\n ToolExecution,\n NewToolExecution,\n TodoItem,\n NewTodoItem,\n SessionConfig,\n ModelMessage,\n Terminal,\n NewTerminal,\n} from './schema.js';\n\nlet db: ReturnType<typeof drizzle<typeof schema>> | null = null;\nlet sqlite: Database.Database | null = null;\n\nexport function initDatabase(dbPath: string) {\n sqlite = new Database(dbPath);\n sqlite.pragma('journal_mode = WAL');\n db = drizzle(sqlite, { schema });\n\n // Drop and recreate tables with new schema\n sqlite.exec(`\n DROP TABLE IF EXISTS terminals;\n DROP TABLE IF EXISTS loaded_skills;\n DROP TABLE IF EXISTS todo_items;\n DROP TABLE IF EXISTS tool_executions;\n DROP TABLE IF EXISTS messages;\n DROP TABLE IF EXISTS sessions;\n\n CREATE TABLE sessions (\n id TEXT PRIMARY KEY,\n name TEXT,\n working_directory TEXT NOT NULL,\n model TEXT NOT NULL,\n status TEXT NOT NULL DEFAULT 'active',\n config TEXT,\n created_at INTEGER NOT NULL,\n updated_at INTEGER NOT NULL\n );\n\n CREATE TABLE messages (\n id TEXT PRIMARY KEY,\n session_id TEXT NOT NULL REFERENCES sessions(id) ON DELETE CASCADE,\n model_message TEXT NOT NULL,\n sequence INTEGER NOT NULL DEFAULT 0,\n created_at INTEGER NOT NULL\n );\n\n CREATE TABLE tool_executions (\n id TEXT PRIMARY KEY,\n session_id TEXT NOT NULL REFERENCES sessions(id) ON DELETE CASCADE,\n message_id TEXT REFERENCES messages(id) ON DELETE CASCADE,\n tool_name TEXT NOT NULL,\n tool_call_id TEXT NOT NULL,\n input TEXT,\n output TEXT,\n status TEXT NOT NULL DEFAULT 'pending',\n requires_approval INTEGER NOT NULL DEFAULT 0,\n error TEXT,\n started_at INTEGER NOT NULL,\n completed_at INTEGER\n );\n\n CREATE TABLE todo_items (\n id TEXT PRIMARY KEY,\n session_id TEXT NOT NULL REFERENCES sessions(id) ON DELETE CASCADE,\n content TEXT NOT NULL,\n status TEXT NOT NULL DEFAULT 'pending',\n \"order\" INTEGER NOT NULL DEFAULT 0,\n created_at INTEGER NOT NULL,\n updated_at INTEGER NOT NULL\n );\n\n CREATE TABLE loaded_skills (\n id TEXT PRIMARY KEY,\n session_id TEXT NOT NULL REFERENCES sessions(id) ON DELETE CASCADE,\n skill_name TEXT NOT NULL,\n loaded_at INTEGER NOT NULL\n );\n\n CREATE TABLE terminals (\n id TEXT PRIMARY KEY,\n session_id TEXT NOT NULL REFERENCES sessions(id) ON DELETE CASCADE,\n name TEXT,\n command TEXT NOT NULL,\n cwd TEXT NOT NULL,\n pid INTEGER,\n status TEXT NOT NULL DEFAULT 'running',\n exit_code INTEGER,\n error TEXT,\n created_at INTEGER NOT NULL,\n stopped_at INTEGER\n );\n\n CREATE INDEX IF NOT EXISTS idx_messages_session ON messages(session_id);\n CREATE INDEX IF NOT EXISTS idx_tool_executions_session ON tool_executions(session_id);\n CREATE INDEX IF NOT EXISTS idx_todo_items_session ON todo_items(session_id);\n CREATE INDEX IF NOT EXISTS idx_loaded_skills_session ON loaded_skills(session_id);\n CREATE INDEX IF NOT EXISTS idx_terminals_session ON terminals(session_id);\n `);\n\n return db;\n}\n\nexport function getDb() {\n if (!db) {\n throw new Error('Database not initialized. Call initDatabase first.');\n }\n return db;\n}\n\nexport function closeDatabase() {\n if (sqlite) {\n sqlite.close();\n sqlite = null;\n db = null;\n }\n}\n\n// Session queries\nexport const sessionQueries = {\n create(data: Omit<NewSession, 'id' | 'createdAt' | 'updatedAt'>): Session {\n const id = nanoid();\n const now = new Date();\n const result = getDb()\n .insert(schema.sessions)\n .values({\n id,\n ...data,\n createdAt: now,\n updatedAt: now,\n })\n .returning()\n .get();\n return result;\n },\n\n getById(id: string): Session | undefined {\n return getDb().select().from(schema.sessions).where(eq(schema.sessions.id, id)).get();\n },\n\n list(limit = 50, offset = 0): Session[] {\n return getDb()\n .select()\n .from(schema.sessions)\n .orderBy(desc(schema.sessions.createdAt))\n .limit(limit)\n .offset(offset)\n .all();\n },\n\n updateStatus(id: string, status: Session['status']): Session | undefined {\n return getDb()\n .update(schema.sessions)\n .set({ status, updatedAt: new Date() })\n .where(eq(schema.sessions.id, id))\n .returning()\n .get();\n },\n\n delete(id: string): boolean {\n const result = getDb().delete(schema.sessions).where(eq(schema.sessions.id, id)).run();\n return result.changes > 0;\n },\n};\n\n// Message queries - stores AI SDK ModelMessage directly\nexport const messageQueries = {\n /**\n * Get the next sequence number for a session\n */\n getNextSequence(sessionId: string): number {\n const result = getDb()\n .select({ maxSeq: sql<number>`COALESCE(MAX(sequence), -1)` })\n .from(schema.messages)\n .where(eq(schema.messages.sessionId, sessionId))\n .get();\n return (result?.maxSeq ?? -1) + 1;\n },\n\n /**\n * Create a single message from a ModelMessage\n */\n create(sessionId: string, modelMessage: ModelMessage): Message {\n const id = nanoid();\n const sequence = this.getNextSequence(sessionId);\n const result = getDb()\n .insert(schema.messages)\n .values({\n id,\n sessionId,\n modelMessage,\n sequence,\n createdAt: new Date(),\n })\n .returning()\n .get();\n return result;\n },\n\n /**\n * Add multiple ModelMessages at once (from response.messages)\n * Maintains insertion order via sequence numbers\n */\n addMany(sessionId: string, modelMessages: ModelMessage[]): Message[] {\n const results: Message[] = [];\n let sequence = this.getNextSequence(sessionId);\n for (const msg of modelMessages) {\n const id = nanoid();\n const result = getDb()\n .insert(schema.messages)\n .values({\n id,\n sessionId,\n modelMessage: msg,\n sequence,\n createdAt: new Date(),\n })\n .returning()\n .get();\n results.push(result);\n sequence++;\n }\n return results;\n },\n\n /**\n * Get all messages for a session as ModelMessage[]\n * Ordered by sequence to maintain exact insertion order\n */\n getBySession(sessionId: string): Message[] {\n return getDb()\n .select()\n .from(schema.messages)\n .where(eq(schema.messages.sessionId, sessionId))\n .orderBy(schema.messages.sequence)\n .all();\n },\n\n /**\n * Get ModelMessages directly (for passing to AI SDK)\n */\n getModelMessages(sessionId: string): ModelMessage[] {\n const messages = this.getBySession(sessionId);\n return messages.map(m => m.modelMessage);\n },\n\n getRecentBySession(sessionId: string, limit = 50): Message[] {\n return getDb()\n .select()\n .from(schema.messages)\n .where(eq(schema.messages.sessionId, sessionId))\n .orderBy(desc(schema.messages.sequence))\n .limit(limit)\n .all()\n .reverse();\n },\n\n countBySession(sessionId: string): number {\n const result = getDb()\n .select({ count: sql<number>`count(*)` })\n .from(schema.messages)\n .where(eq(schema.messages.sessionId, sessionId))\n .get();\n return result?.count ?? 0;\n },\n\n deleteBySession(sessionId: string): number {\n const result = getDb()\n .delete(schema.messages)\n .where(eq(schema.messages.sessionId, sessionId))\n .run();\n return result.changes;\n },\n};\n\n// Tool execution queries\nexport const toolExecutionQueries = {\n create(data: Omit<NewToolExecution, 'id' | 'startedAt'>): ToolExecution {\n const id = nanoid();\n const result = getDb()\n .insert(schema.toolExecutions)\n .values({\n id,\n ...data,\n startedAt: new Date(),\n })\n .returning()\n .get();\n return result;\n },\n\n getById(id: string): ToolExecution | undefined {\n return getDb()\n .select()\n .from(schema.toolExecutions)\n .where(eq(schema.toolExecutions.id, id))\n .get();\n },\n\n getByToolCallId(toolCallId: string): ToolExecution | undefined {\n return getDb()\n .select()\n .from(schema.toolExecutions)\n .where(eq(schema.toolExecutions.toolCallId, toolCallId))\n .get();\n },\n\n getPendingApprovals(sessionId: string): ToolExecution[] {\n return getDb()\n .select()\n .from(schema.toolExecutions)\n .where(\n and(\n eq(schema.toolExecutions.sessionId, sessionId),\n eq(schema.toolExecutions.status, 'pending'),\n eq(schema.toolExecutions.requiresApproval, true)\n )\n )\n .all();\n },\n\n approve(id: string): ToolExecution | undefined {\n return getDb()\n .update(schema.toolExecutions)\n .set({ status: 'approved' })\n .where(eq(schema.toolExecutions.id, id))\n .returning()\n .get();\n },\n\n reject(id: string): ToolExecution | undefined {\n return getDb()\n .update(schema.toolExecutions)\n .set({ status: 'rejected' })\n .where(eq(schema.toolExecutions.id, id))\n .returning()\n .get();\n },\n\n complete(\n id: string,\n output: unknown,\n error?: string\n ): ToolExecution | undefined {\n return getDb()\n .update(schema.toolExecutions)\n .set({\n status: error ? 'error' : 'completed',\n output: output as any,\n error,\n completedAt: new Date(),\n })\n .where(eq(schema.toolExecutions.id, id))\n .returning()\n .get();\n },\n\n getBySession(sessionId: string): ToolExecution[] {\n return getDb()\n .select()\n .from(schema.toolExecutions)\n .where(eq(schema.toolExecutions.sessionId, sessionId))\n .orderBy(schema.toolExecutions.startedAt)\n .all();\n },\n};\n\n// Todo item queries\nexport const todoQueries = {\n create(data: Omit<NewTodoItem, 'id' | 'createdAt' | 'updatedAt'>): TodoItem {\n const id = nanoid();\n const now = new Date();\n const result = getDb()\n .insert(schema.todoItems)\n .values({\n id,\n ...data,\n createdAt: now,\n updatedAt: now,\n })\n .returning()\n .get();\n return result;\n },\n\n createMany(\n sessionId: string,\n items: Array<{ content: string; order?: number }>\n ): TodoItem[] {\n const now = new Date();\n const values = items.map((item, index) => ({\n id: nanoid(),\n sessionId,\n content: item.content,\n order: item.order ?? index,\n createdAt: now,\n updatedAt: now,\n }));\n\n return getDb().insert(schema.todoItems).values(values).returning().all();\n },\n\n getBySession(sessionId: string): TodoItem[] {\n return getDb()\n .select()\n .from(schema.todoItems)\n .where(eq(schema.todoItems.sessionId, sessionId))\n .orderBy(schema.todoItems.order)\n .all();\n },\n\n updateStatus(\n id: string,\n status: TodoItem['status']\n ): TodoItem | undefined {\n return getDb()\n .update(schema.todoItems)\n .set({ status, updatedAt: new Date() })\n .where(eq(schema.todoItems.id, id))\n .returning()\n .get();\n },\n\n delete(id: string): boolean {\n const result = getDb()\n .delete(schema.todoItems)\n .where(eq(schema.todoItems.id, id))\n .run();\n return result.changes > 0;\n },\n\n clearSession(sessionId: string): number {\n const result = getDb()\n .delete(schema.todoItems)\n .where(eq(schema.todoItems.sessionId, sessionId))\n .run();\n return result.changes;\n },\n};\n\n// Loaded skills queries\nexport const skillQueries = {\n load(sessionId: string, skillName: string): schema.LoadedSkill {\n const id = nanoid();\n const result = getDb()\n .insert(schema.loadedSkills)\n .values({\n id,\n sessionId,\n skillName,\n loadedAt: new Date(),\n })\n .returning()\n .get();\n return result;\n },\n\n getBySession(sessionId: string): schema.LoadedSkill[] {\n return getDb()\n .select()\n .from(schema.loadedSkills)\n .where(eq(schema.loadedSkills.sessionId, sessionId))\n .orderBy(schema.loadedSkills.loadedAt)\n .all();\n },\n\n isLoaded(sessionId: string, skillName: string): boolean {\n const result = getDb()\n .select()\n .from(schema.loadedSkills)\n .where(\n and(\n eq(schema.loadedSkills.sessionId, sessionId),\n eq(schema.loadedSkills.skillName, skillName)\n )\n )\n .get();\n return !!result;\n },\n};\n\n// Terminal queries\nexport const terminalQueries = {\n create(data: Omit<NewTerminal, 'id' | 'createdAt'>): Terminal {\n const id = nanoid();\n const result = getDb()\n .insert(schema.terminals)\n .values({\n id,\n ...data,\n createdAt: new Date(),\n })\n .returning()\n .get();\n return result;\n },\n\n getById(id: string): Terminal | undefined {\n return getDb()\n .select()\n .from(schema.terminals)\n .where(eq(schema.terminals.id, id))\n .get();\n },\n\n getBySession(sessionId: string): Terminal[] {\n return getDb()\n .select()\n .from(schema.terminals)\n .where(eq(schema.terminals.sessionId, sessionId))\n .orderBy(desc(schema.terminals.createdAt))\n .all();\n },\n\n getRunning(sessionId: string): Terminal[] {\n return getDb()\n .select()\n .from(schema.terminals)\n .where(\n and(\n eq(schema.terminals.sessionId, sessionId),\n eq(schema.terminals.status, 'running')\n )\n )\n .all();\n },\n\n updateStatus(\n id: string,\n status: Terminal['status'],\n exitCode?: number,\n error?: string\n ): Terminal | undefined {\n return getDb()\n .update(schema.terminals)\n .set({\n status,\n exitCode,\n error,\n stoppedAt: status !== 'running' ? new Date() : undefined,\n })\n .where(eq(schema.terminals.id, id))\n .returning()\n .get();\n },\n\n updatePid(id: string, pid: number): Terminal | undefined {\n return getDb()\n .update(schema.terminals)\n .set({ pid })\n .where(eq(schema.terminals.id, id))\n .returning()\n .get();\n },\n\n delete(id: string): boolean {\n const result = getDb()\n .delete(schema.terminals)\n .where(eq(schema.terminals.id, id))\n .run();\n return result.changes > 0;\n },\n\n deleteBySession(sessionId: string): number {\n const result = getDb()\n .delete(schema.terminals)\n .where(eq(schema.terminals.sessionId, sessionId))\n .run();\n return result.changes;\n },\n};\n\nexport type { \n Session, \n Message, \n ToolExecution, \n TodoItem, \n SessionConfig, \n ModelMessage,\n Terminal,\n};\n","import { sqliteTable, text, integer } from 'drizzle-orm/sqlite-core';\n\n// Sessions table - represents an agent session/thread\nexport const sessions = sqliteTable('sessions', {\n id: text('id').primaryKey(),\n name: text('name'),\n workingDirectory: text('working_directory').notNull(),\n model: text('model').notNull(),\n status: text('status', { enum: ['active', 'waiting', 'completed', 'error'] })\n .notNull()\n .default('active'),\n config: text('config', { mode: 'json' }).$type<SessionConfig>(),\n createdAt: integer('created_at', { mode: 'timestamp' })\n .notNull()\n .$defaultFn(() => new Date()),\n updatedAt: integer('updated_at', { mode: 'timestamp' })\n .notNull()\n .$defaultFn(() => new Date()),\n});\n\n// Messages table - stores AI SDK ModelMessage directly\nexport const messages = sqliteTable('messages', {\n id: text('id').primaryKey(),\n sessionId: text('session_id')\n .notNull()\n .references(() => sessions.id, { onDelete: 'cascade' }),\n // Store the entire ModelMessage as JSON (role + content)\n modelMessage: text('model_message', { mode: 'json' }).$type<ModelMessage>().notNull(),\n // Sequence number within session to maintain exact ordering\n sequence: integer('sequence').notNull().default(0),\n createdAt: integer('created_at', { mode: 'timestamp' })\n .notNull()\n .$defaultFn(() => new Date()),\n});\n\n// Tool executions - tracks all tool calls and their results\nexport const toolExecutions = sqliteTable('tool_executions', {\n id: text('id').primaryKey(),\n sessionId: text('session_id')\n .notNull()\n .references(() => sessions.id, { onDelete: 'cascade' }),\n messageId: text('message_id').references(() => messages.id, { onDelete: 'cascade' }),\n toolName: text('tool_name').notNull(),\n toolCallId: text('tool_call_id').notNull(),\n input: text('input', { mode: 'json' }),\n output: text('output', { mode: 'json' }),\n status: text('status', { enum: ['pending', 'approved', 'rejected', 'completed', 'error'] })\n .notNull()\n .default('pending'),\n requiresApproval: integer('requires_approval', { mode: 'boolean' }).notNull().default(false),\n error: text('error'),\n startedAt: integer('started_at', { mode: 'timestamp' })\n .notNull()\n .$defaultFn(() => new Date()),\n completedAt: integer('completed_at', { mode: 'timestamp' }),\n});\n\n// Todo items for the planning tool\nexport const todoItems = sqliteTable('todo_items', {\n id: text('id').primaryKey(),\n sessionId: text('session_id')\n .notNull()\n .references(() => sessions.id, { onDelete: 'cascade' }),\n content: text('content').notNull(),\n status: text('status', { enum: ['pending', 'in_progress', 'completed', 'cancelled'] })\n .notNull()\n .default('pending'),\n order: integer('order').notNull().default(0),\n createdAt: integer('created_at', { mode: 'timestamp' })\n .notNull()\n .$defaultFn(() => new Date()),\n updatedAt: integer('updated_at', { mode: 'timestamp' })\n .notNull()\n .$defaultFn(() => new Date()),\n});\n\n// Skills loaded into sessions\nexport const loadedSkills = sqliteTable('loaded_skills', {\n id: text('id').primaryKey(),\n sessionId: text('session_id')\n .notNull()\n .references(() => sessions.id, { onDelete: 'cascade' }),\n skillName: text('skill_name').notNull(),\n loadedAt: integer('loaded_at', { mode: 'timestamp' })\n .notNull()\n .$defaultFn(() => new Date()),\n});\n\n// Terminal sessions - background processes managed by agents\nexport const terminals = sqliteTable('terminals', {\n id: text('id').primaryKey(),\n sessionId: text('session_id')\n .notNull()\n .references(() => sessions.id, { onDelete: 'cascade' }),\n name: text('name'), // Optional friendly name (e.g., \"dev-server\")\n command: text('command').notNull(), // The command that was run\n cwd: text('cwd').notNull(), // Working directory\n pid: integer('pid'), // Process ID (null if not running)\n status: text('status', { enum: ['running', 'stopped', 'error'] })\n .notNull()\n .default('running'),\n exitCode: integer('exit_code'), // Exit code if stopped\n error: text('error'), // Error message if status is 'error'\n createdAt: integer('created_at', { mode: 'timestamp' })\n .notNull()\n .$defaultFn(() => new Date()),\n stoppedAt: integer('stopped_at', { mode: 'timestamp' }),\n});\n\n// Types for JSON columns\nexport interface SessionConfig {\n toolApprovals?: Record<string, boolean>;\n approvalWebhook?: string;\n skillsDirectory?: string;\n maxContextChars?: number;\n}\n\n// AI SDK ModelMessage types - stored directly for accurate context passing\n// These match the exact format from AI SDK's response.messages\nexport type ModelMessage = \n | SystemModelMessage\n | UserModelMessage\n | AssistantModelMessage\n | ToolModelMessage;\n\nexport interface SystemModelMessage {\n role: 'system';\n content: string;\n}\n\nexport interface UserModelMessage {\n role: 'user';\n content: string;\n}\n\nexport interface AssistantModelMessage {\n role: 'assistant';\n content: string | AssistantContentPart[];\n}\n\nexport interface ToolModelMessage {\n role: 'tool';\n content: ToolResultPart[];\n}\n\nexport interface AssistantContentPart {\n type: 'text' | 'tool-call';\n text?: string;\n toolCallId?: string;\n toolName?: string;\n input?: unknown;\n}\n\nexport interface ToolResultPart {\n type: 'tool-result';\n toolCallId: string;\n toolName: string;\n output: unknown;\n}\n\n// Type exports for queries\nexport type Session = typeof sessions.$inferSelect;\nexport type NewSession = typeof sessions.$inferInsert;\nexport type Message = typeof messages.$inferSelect;\nexport type NewMessage = typeof messages.$inferInsert;\nexport type ToolExecution = typeof toolExecutions.$inferSelect;\nexport type NewToolExecution = typeof toolExecutions.$inferInsert;\nexport type TodoItem = typeof todoItems.$inferSelect;\nexport type NewTodoItem = typeof todoItems.$inferInsert;\nexport type LoadedSkill = typeof loadedSkills.$inferSelect;\nexport type Terminal = typeof terminals.$inferSelect;\nexport type NewTerminal = typeof terminals.$inferInsert;"],"mappings":"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","names":["messages"]}
|
|
1
|
+
{"version":3,"sources":["../../src/db/index.ts","../../src/db/schema.ts"],"sourcesContent":["import Database from 'better-sqlite3';\nimport { drizzle } from 'drizzle-orm/better-sqlite3';\nimport { eq, desc, and, sql } from 'drizzle-orm';\nimport { nanoid } from 'nanoid';\nimport * as schema from './schema.js';\nimport type {\n Session,\n NewSession,\n Message,\n NewMessage,\n ToolExecution,\n NewToolExecution,\n TodoItem,\n NewTodoItem,\n SessionConfig,\n ModelMessage,\n Terminal,\n NewTerminal,\n ActiveStream,\n NewActiveStream,\n Checkpoint,\n NewCheckpoint,\n FileBackup,\n NewFileBackup,\n} from './schema.js';\n\nlet db: ReturnType<typeof drizzle<typeof schema>> | null = null;\nlet sqlite: Database.Database | null = null;\n\nexport function initDatabase(dbPath: string) {\n sqlite = new Database(dbPath);\n sqlite.pragma('journal_mode = WAL');\n db = drizzle(sqlite, { schema });\n\n // Create tables if they don't exist (preserves existing data)\n sqlite.exec(`\n CREATE TABLE IF NOT EXISTS sessions (\n id TEXT PRIMARY KEY,\n name TEXT,\n working_directory TEXT NOT NULL,\n model TEXT NOT NULL,\n status TEXT NOT NULL DEFAULT 'active',\n config TEXT,\n created_at INTEGER NOT NULL,\n updated_at INTEGER NOT NULL\n );\n\n CREATE TABLE IF NOT EXISTS messages (\n id TEXT PRIMARY KEY,\n session_id TEXT NOT NULL REFERENCES sessions(id) ON DELETE CASCADE,\n model_message TEXT NOT NULL,\n sequence INTEGER NOT NULL DEFAULT 0,\n created_at INTEGER NOT NULL\n );\n\n CREATE TABLE IF NOT EXISTS tool_executions (\n id TEXT PRIMARY KEY,\n session_id TEXT NOT NULL REFERENCES sessions(id) ON DELETE CASCADE,\n message_id TEXT REFERENCES messages(id) ON DELETE CASCADE,\n tool_name TEXT NOT NULL,\n tool_call_id TEXT NOT NULL,\n input TEXT,\n output TEXT,\n status TEXT NOT NULL DEFAULT 'pending',\n requires_approval INTEGER NOT NULL DEFAULT 0,\n error TEXT,\n started_at INTEGER NOT NULL,\n completed_at INTEGER\n );\n\n CREATE TABLE IF NOT EXISTS todo_items (\n id TEXT PRIMARY KEY,\n session_id TEXT NOT NULL REFERENCES sessions(id) ON DELETE CASCADE,\n content TEXT NOT NULL,\n status TEXT NOT NULL DEFAULT 'pending',\n \"order\" INTEGER NOT NULL DEFAULT 0,\n created_at INTEGER NOT NULL,\n updated_at INTEGER NOT NULL\n );\n\n CREATE TABLE IF NOT EXISTS loaded_skills (\n id TEXT PRIMARY KEY,\n session_id TEXT NOT NULL REFERENCES sessions(id) ON DELETE CASCADE,\n skill_name TEXT NOT NULL,\n loaded_at INTEGER NOT NULL\n );\n\n CREATE TABLE IF NOT EXISTS terminals (\n id TEXT PRIMARY KEY,\n session_id TEXT NOT NULL REFERENCES sessions(id) ON DELETE CASCADE,\n name TEXT,\n command TEXT NOT NULL,\n cwd TEXT NOT NULL,\n pid INTEGER,\n status TEXT NOT NULL DEFAULT 'running',\n exit_code INTEGER,\n error TEXT,\n created_at INTEGER NOT NULL,\n stopped_at INTEGER\n );\n\n -- Table for tracking active streams (for resumable streams)\n CREATE TABLE IF NOT EXISTS active_streams (\n id TEXT PRIMARY KEY,\n session_id TEXT NOT NULL REFERENCES sessions(id) ON DELETE CASCADE,\n stream_id TEXT NOT NULL UNIQUE,\n status TEXT NOT NULL DEFAULT 'active',\n created_at INTEGER NOT NULL,\n finished_at INTEGER\n );\n\n -- Checkpoints table - created before each user message\n CREATE TABLE IF NOT EXISTS checkpoints (\n id TEXT PRIMARY KEY,\n session_id TEXT NOT NULL REFERENCES sessions(id) ON DELETE CASCADE,\n message_sequence INTEGER NOT NULL,\n git_head TEXT,\n created_at INTEGER NOT NULL\n );\n\n -- File backups table - stores original file content\n CREATE TABLE IF NOT EXISTS file_backups (\n id TEXT PRIMARY KEY,\n checkpoint_id TEXT NOT NULL REFERENCES checkpoints(id) ON DELETE CASCADE,\n session_id TEXT NOT NULL REFERENCES sessions(id) ON DELETE CASCADE,\n file_path TEXT NOT NULL,\n original_content TEXT,\n existed INTEGER NOT NULL DEFAULT 1,\n created_at INTEGER NOT NULL\n );\n\n CREATE INDEX IF NOT EXISTS idx_messages_session ON messages(session_id);\n CREATE INDEX IF NOT EXISTS idx_tool_executions_session ON tool_executions(session_id);\n CREATE INDEX IF NOT EXISTS idx_todo_items_session ON todo_items(session_id);\n CREATE INDEX IF NOT EXISTS idx_loaded_skills_session ON loaded_skills(session_id);\n CREATE INDEX IF NOT EXISTS idx_terminals_session ON terminals(session_id);\n CREATE INDEX IF NOT EXISTS idx_active_streams_session ON active_streams(session_id);\n CREATE INDEX IF NOT EXISTS idx_checkpoints_session ON checkpoints(session_id);\n CREATE INDEX IF NOT EXISTS idx_file_backups_checkpoint ON file_backups(checkpoint_id);\n CREATE INDEX IF NOT EXISTS idx_file_backups_session ON file_backups(session_id);\n `);\n\n return db;\n}\n\nexport function getDb() {\n if (!db) {\n throw new Error('Database not initialized. Call initDatabase first.');\n }\n return db;\n}\n\nexport function closeDatabase() {\n if (sqlite) {\n sqlite.close();\n sqlite = null;\n db = null;\n }\n}\n\n// Session queries\nexport const sessionQueries = {\n create(data: Omit<NewSession, 'id' | 'createdAt' | 'updatedAt'>): Session {\n const id = nanoid();\n const now = new Date();\n const result = getDb()\n .insert(schema.sessions)\n .values({\n id,\n ...data,\n createdAt: now,\n updatedAt: now,\n })\n .returning()\n .get();\n return result;\n },\n\n getById(id: string): Session | undefined {\n return getDb().select().from(schema.sessions).where(eq(schema.sessions.id, id)).get();\n },\n\n list(limit = 50, offset = 0): Session[] {\n return getDb()\n .select()\n .from(schema.sessions)\n .orderBy(desc(schema.sessions.createdAt))\n .limit(limit)\n .offset(offset)\n .all();\n },\n\n updateStatus(id: string, status: Session['status']): Session | undefined {\n return getDb()\n .update(schema.sessions)\n .set({ status, updatedAt: new Date() })\n .where(eq(schema.sessions.id, id))\n .returning()\n .get();\n },\n\n updateModel(id: string, model: string): Session | undefined {\n return getDb()\n .update(schema.sessions)\n .set({ model, updatedAt: new Date() })\n .where(eq(schema.sessions.id, id))\n .returning()\n .get();\n },\n\n update(id: string, updates: { model?: string; name?: string; config?: SessionConfig }): Session | undefined {\n return getDb()\n .update(schema.sessions)\n .set({ ...updates, updatedAt: new Date() })\n .where(eq(schema.sessions.id, id))\n .returning()\n .get();\n },\n\n delete(id: string): boolean {\n const result = getDb().delete(schema.sessions).where(eq(schema.sessions.id, id)).run();\n return result.changes > 0;\n },\n};\n\n// Message queries - stores AI SDK ModelMessage directly\nexport const messageQueries = {\n /**\n * Get the next sequence number for a session\n */\n getNextSequence(sessionId: string): number {\n const result = getDb()\n .select({ maxSeq: sql<number>`COALESCE(MAX(sequence), -1)` })\n .from(schema.messages)\n .where(eq(schema.messages.sessionId, sessionId))\n .get();\n return (result?.maxSeq ?? -1) + 1;\n },\n\n /**\n * Create a single message from a ModelMessage\n */\n create(sessionId: string, modelMessage: ModelMessage): Message {\n const id = nanoid();\n const sequence = this.getNextSequence(sessionId);\n const result = getDb()\n .insert(schema.messages)\n .values({\n id,\n sessionId,\n modelMessage,\n sequence,\n createdAt: new Date(),\n })\n .returning()\n .get();\n return result;\n },\n\n /**\n * Add multiple ModelMessages at once (from response.messages)\n * Maintains insertion order via sequence numbers\n */\n addMany(sessionId: string, modelMessages: ModelMessage[]): Message[] {\n const results: Message[] = [];\n let sequence = this.getNextSequence(sessionId);\n for (const msg of modelMessages) {\n const id = nanoid();\n const result = getDb()\n .insert(schema.messages)\n .values({\n id,\n sessionId,\n modelMessage: msg,\n sequence,\n createdAt: new Date(),\n })\n .returning()\n .get();\n results.push(result);\n sequence++;\n }\n return results;\n },\n\n /**\n * Get all messages for a session as ModelMessage[]\n * Ordered by sequence to maintain exact insertion order\n */\n getBySession(sessionId: string): Message[] {\n return getDb()\n .select()\n .from(schema.messages)\n .where(eq(schema.messages.sessionId, sessionId))\n .orderBy(schema.messages.sequence)\n .all();\n },\n\n /**\n * Get ModelMessages directly (for passing to AI SDK)\n */\n getModelMessages(sessionId: string): ModelMessage[] {\n const messages = this.getBySession(sessionId);\n return messages.map(m => m.modelMessage);\n },\n\n getRecentBySession(sessionId: string, limit = 50): Message[] {\n return getDb()\n .select()\n .from(schema.messages)\n .where(eq(schema.messages.sessionId, sessionId))\n .orderBy(desc(schema.messages.sequence))\n .limit(limit)\n .all()\n .reverse();\n },\n\n countBySession(sessionId: string): number {\n const result = getDb()\n .select({ count: sql<number>`count(*)` })\n .from(schema.messages)\n .where(eq(schema.messages.sessionId, sessionId))\n .get();\n return result?.count ?? 0;\n },\n\n deleteBySession(sessionId: string): number {\n const result = getDb()\n .delete(schema.messages)\n .where(eq(schema.messages.sessionId, sessionId))\n .run();\n return result.changes;\n },\n\n /**\n * Delete all messages with sequence >= the given sequence number\n * (Used when reverting to a checkpoint)\n */\n deleteFromSequence(sessionId: string, fromSequence: number): number {\n const result = getDb()\n .delete(schema.messages)\n .where(\n and(\n eq(schema.messages.sessionId, sessionId),\n sql`sequence >= ${fromSequence}`\n )\n )\n .run();\n return result.changes;\n },\n};\n\n// Tool execution queries\nexport const toolExecutionQueries = {\n create(data: Omit<NewToolExecution, 'id' | 'startedAt'>): ToolExecution {\n const id = nanoid();\n const result = getDb()\n .insert(schema.toolExecutions)\n .values({\n id,\n ...data,\n startedAt: new Date(),\n })\n .returning()\n .get();\n return result;\n },\n\n getById(id: string): ToolExecution | undefined {\n return getDb()\n .select()\n .from(schema.toolExecutions)\n .where(eq(schema.toolExecutions.id, id))\n .get();\n },\n\n getByToolCallId(toolCallId: string): ToolExecution | undefined {\n return getDb()\n .select()\n .from(schema.toolExecutions)\n .where(eq(schema.toolExecutions.toolCallId, toolCallId))\n .get();\n },\n\n getPendingApprovals(sessionId: string): ToolExecution[] {\n return getDb()\n .select()\n .from(schema.toolExecutions)\n .where(\n and(\n eq(schema.toolExecutions.sessionId, sessionId),\n eq(schema.toolExecutions.status, 'pending'),\n eq(schema.toolExecutions.requiresApproval, true)\n )\n )\n .all();\n },\n\n approve(id: string): ToolExecution | undefined {\n return getDb()\n .update(schema.toolExecutions)\n .set({ status: 'approved' })\n .where(eq(schema.toolExecutions.id, id))\n .returning()\n .get();\n },\n\n reject(id: string): ToolExecution | undefined {\n return getDb()\n .update(schema.toolExecutions)\n .set({ status: 'rejected' })\n .where(eq(schema.toolExecutions.id, id))\n .returning()\n .get();\n },\n\n complete(\n id: string,\n output: unknown,\n error?: string\n ): ToolExecution | undefined {\n return getDb()\n .update(schema.toolExecutions)\n .set({\n status: error ? 'error' : 'completed',\n output: output as any,\n error,\n completedAt: new Date(),\n })\n .where(eq(schema.toolExecutions.id, id))\n .returning()\n .get();\n },\n\n getBySession(sessionId: string): ToolExecution[] {\n return getDb()\n .select()\n .from(schema.toolExecutions)\n .where(eq(schema.toolExecutions.sessionId, sessionId))\n .orderBy(schema.toolExecutions.startedAt)\n .all();\n },\n\n /**\n * Delete all tool executions after a given timestamp\n * (Used when reverting to a checkpoint)\n */\n deleteAfterTime(sessionId: string, afterTime: Date): number {\n const result = getDb()\n .delete(schema.toolExecutions)\n .where(\n and(\n eq(schema.toolExecutions.sessionId, sessionId),\n sql`started_at > ${afterTime.getTime()}`\n )\n )\n .run();\n return result.changes;\n },\n};\n\n// Todo item queries\nexport const todoQueries = {\n create(data: Omit<NewTodoItem, 'id' | 'createdAt' | 'updatedAt'>): TodoItem {\n const id = nanoid();\n const now = new Date();\n const result = getDb()\n .insert(schema.todoItems)\n .values({\n id,\n ...data,\n createdAt: now,\n updatedAt: now,\n })\n .returning()\n .get();\n return result;\n },\n\n createMany(\n sessionId: string,\n items: Array<{ content: string; order?: number }>\n ): TodoItem[] {\n const now = new Date();\n const values = items.map((item, index) => ({\n id: nanoid(),\n sessionId,\n content: item.content,\n order: item.order ?? index,\n createdAt: now,\n updatedAt: now,\n }));\n\n return getDb().insert(schema.todoItems).values(values).returning().all();\n },\n\n getBySession(sessionId: string): TodoItem[] {\n return getDb()\n .select()\n .from(schema.todoItems)\n .where(eq(schema.todoItems.sessionId, sessionId))\n .orderBy(schema.todoItems.order)\n .all();\n },\n\n updateStatus(\n id: string,\n status: TodoItem['status']\n ): TodoItem | undefined {\n return getDb()\n .update(schema.todoItems)\n .set({ status, updatedAt: new Date() })\n .where(eq(schema.todoItems.id, id))\n .returning()\n .get();\n },\n\n delete(id: string): boolean {\n const result = getDb()\n .delete(schema.todoItems)\n .where(eq(schema.todoItems.id, id))\n .run();\n return result.changes > 0;\n },\n\n clearSession(sessionId: string): number {\n const result = getDb()\n .delete(schema.todoItems)\n .where(eq(schema.todoItems.sessionId, sessionId))\n .run();\n return result.changes;\n },\n};\n\n// Loaded skills queries\nexport const skillQueries = {\n load(sessionId: string, skillName: string): schema.LoadedSkill {\n const id = nanoid();\n const result = getDb()\n .insert(schema.loadedSkills)\n .values({\n id,\n sessionId,\n skillName,\n loadedAt: new Date(),\n })\n .returning()\n .get();\n return result;\n },\n\n getBySession(sessionId: string): schema.LoadedSkill[] {\n return getDb()\n .select()\n .from(schema.loadedSkills)\n .where(eq(schema.loadedSkills.sessionId, sessionId))\n .orderBy(schema.loadedSkills.loadedAt)\n .all();\n },\n\n isLoaded(sessionId: string, skillName: string): boolean {\n const result = getDb()\n .select()\n .from(schema.loadedSkills)\n .where(\n and(\n eq(schema.loadedSkills.sessionId, sessionId),\n eq(schema.loadedSkills.skillName, skillName)\n )\n )\n .get();\n return !!result;\n },\n};\n\n// Terminal queries\nexport const terminalQueries = {\n create(data: Omit<NewTerminal, 'id' | 'createdAt'>): Terminal {\n const id = nanoid();\n const result = getDb()\n .insert(schema.terminals)\n .values({\n id,\n ...data,\n createdAt: new Date(),\n })\n .returning()\n .get();\n return result;\n },\n\n getById(id: string): Terminal | undefined {\n return getDb()\n .select()\n .from(schema.terminals)\n .where(eq(schema.terminals.id, id))\n .get();\n },\n\n getBySession(sessionId: string): Terminal[] {\n return getDb()\n .select()\n .from(schema.terminals)\n .where(eq(schema.terminals.sessionId, sessionId))\n .orderBy(desc(schema.terminals.createdAt))\n .all();\n },\n\n getRunning(sessionId: string): Terminal[] {\n return getDb()\n .select()\n .from(schema.terminals)\n .where(\n and(\n eq(schema.terminals.sessionId, sessionId),\n eq(schema.terminals.status, 'running')\n )\n )\n .all();\n },\n\n updateStatus(\n id: string,\n status: Terminal['status'],\n exitCode?: number,\n error?: string\n ): Terminal | undefined {\n return getDb()\n .update(schema.terminals)\n .set({\n status,\n exitCode,\n error,\n stoppedAt: status !== 'running' ? new Date() : undefined,\n })\n .where(eq(schema.terminals.id, id))\n .returning()\n .get();\n },\n\n updatePid(id: string, pid: number): Terminal | undefined {\n return getDb()\n .update(schema.terminals)\n .set({ pid })\n .where(eq(schema.terminals.id, id))\n .returning()\n .get();\n },\n\n delete(id: string): boolean {\n const result = getDb()\n .delete(schema.terminals)\n .where(eq(schema.terminals.id, id))\n .run();\n return result.changes > 0;\n },\n\n deleteBySession(sessionId: string): number {\n const result = getDb()\n .delete(schema.terminals)\n .where(eq(schema.terminals.sessionId, sessionId))\n .run();\n return result.changes;\n },\n};\n\n// Active stream queries - for resumable streams\nexport const activeStreamQueries = {\n create(sessionId: string, streamId: string): ActiveStream {\n const id = nanoid();\n const result = getDb()\n .insert(schema.activeStreams)\n .values({\n id,\n sessionId,\n streamId,\n status: 'active',\n createdAt: new Date(),\n })\n .returning()\n .get();\n return result;\n },\n\n getBySessionId(sessionId: string): ActiveStream | undefined {\n return getDb()\n .select()\n .from(schema.activeStreams)\n .where(\n and(\n eq(schema.activeStreams.sessionId, sessionId),\n eq(schema.activeStreams.status, 'active')\n )\n )\n .get();\n },\n\n getByStreamId(streamId: string): ActiveStream | undefined {\n return getDb()\n .select()\n .from(schema.activeStreams)\n .where(eq(schema.activeStreams.streamId, streamId))\n .get();\n },\n\n finish(streamId: string): ActiveStream | undefined {\n return getDb()\n .update(schema.activeStreams)\n .set({ status: 'finished', finishedAt: new Date() })\n .where(eq(schema.activeStreams.streamId, streamId))\n .returning()\n .get();\n },\n\n markError(streamId: string): ActiveStream | undefined {\n return getDb()\n .update(schema.activeStreams)\n .set({ status: 'error', finishedAt: new Date() })\n .where(eq(schema.activeStreams.streamId, streamId))\n .returning()\n .get();\n },\n\n deleteBySession(sessionId: string): number {\n const result = getDb()\n .delete(schema.activeStreams)\n .where(eq(schema.activeStreams.sessionId, sessionId))\n .run();\n return result.changes;\n },\n};\n\n// Checkpoint queries - for session revert functionality\nexport const checkpointQueries = {\n create(data: { sessionId: string; messageSequence: number; gitHead?: string }): Checkpoint {\n const id = nanoid();\n const result = getDb()\n .insert(schema.checkpoints)\n .values({\n id,\n sessionId: data.sessionId,\n messageSequence: data.messageSequence,\n gitHead: data.gitHead,\n createdAt: new Date(),\n })\n .returning()\n .get();\n return result;\n },\n\n getById(id: string): Checkpoint | undefined {\n return getDb()\n .select()\n .from(schema.checkpoints)\n .where(eq(schema.checkpoints.id, id))\n .get();\n },\n\n getBySession(sessionId: string): Checkpoint[] {\n return getDb()\n .select()\n .from(schema.checkpoints)\n .where(eq(schema.checkpoints.sessionId, sessionId))\n .orderBy(schema.checkpoints.messageSequence)\n .all();\n },\n\n getByMessageSequence(sessionId: string, messageSequence: number): Checkpoint | undefined {\n return getDb()\n .select()\n .from(schema.checkpoints)\n .where(\n and(\n eq(schema.checkpoints.sessionId, sessionId),\n eq(schema.checkpoints.messageSequence, messageSequence)\n )\n )\n .get();\n },\n\n getLatest(sessionId: string): Checkpoint | undefined {\n return getDb()\n .select()\n .from(schema.checkpoints)\n .where(eq(schema.checkpoints.sessionId, sessionId))\n .orderBy(desc(schema.checkpoints.messageSequence))\n .limit(1)\n .get();\n },\n\n /**\n * Delete all checkpoints after a given sequence number\n * (Used when reverting to a checkpoint)\n */\n deleteAfterSequence(sessionId: string, messageSequence: number): number {\n const result = getDb()\n .delete(schema.checkpoints)\n .where(\n and(\n eq(schema.checkpoints.sessionId, sessionId),\n sql`message_sequence > ${messageSequence}`\n )\n )\n .run();\n return result.changes;\n },\n\n deleteBySession(sessionId: string): number {\n const result = getDb()\n .delete(schema.checkpoints)\n .where(eq(schema.checkpoints.sessionId, sessionId))\n .run();\n return result.changes;\n },\n};\n\n// File backup queries - for storing original file content\nexport const fileBackupQueries = {\n create(data: {\n checkpointId: string;\n sessionId: string;\n filePath: string;\n originalContent: string | null;\n existed: boolean;\n }): FileBackup {\n const id = nanoid();\n const result = getDb()\n .insert(schema.fileBackups)\n .values({\n id,\n checkpointId: data.checkpointId,\n sessionId: data.sessionId,\n filePath: data.filePath,\n originalContent: data.originalContent,\n existed: data.existed,\n createdAt: new Date(),\n })\n .returning()\n .get();\n return result;\n },\n\n getByCheckpoint(checkpointId: string): FileBackup[] {\n return getDb()\n .select()\n .from(schema.fileBackups)\n .where(eq(schema.fileBackups.checkpointId, checkpointId))\n .all();\n },\n\n getBySession(sessionId: string): FileBackup[] {\n return getDb()\n .select()\n .from(schema.fileBackups)\n .where(eq(schema.fileBackups.sessionId, sessionId))\n .orderBy(schema.fileBackups.createdAt)\n .all();\n },\n\n /**\n * Get all file backups from a given checkpoint sequence onwards (inclusive)\n * (Used when reverting - need to restore these files)\n * \n * When reverting to checkpoint X, we need backups from checkpoint X and all later ones\n * because checkpoint X's backups represent the state BEFORE processing message X.\n */\n getFromSequence(sessionId: string, messageSequence: number): FileBackup[] {\n // Get all checkpoints from this sequence onwards, then get their backups\n const checkpointsFrom = getDb()\n .select()\n .from(schema.checkpoints)\n .where(\n and(\n eq(schema.checkpoints.sessionId, sessionId),\n sql`message_sequence >= ${messageSequence}`\n )\n )\n .all();\n\n if (checkpointsFrom.length === 0) {\n return [];\n }\n\n const checkpointIds = checkpointsFrom.map(c => c.id);\n \n // Get all backups for these checkpoints\n const allBackups: FileBackup[] = [];\n for (const cpId of checkpointIds) {\n const backups = getDb()\n .select()\n .from(schema.fileBackups)\n .where(eq(schema.fileBackups.checkpointId, cpId))\n .all();\n allBackups.push(...backups);\n }\n \n return allBackups;\n },\n\n /**\n * Check if a file already has a backup in the current checkpoint\n */\n hasBackup(checkpointId: string, filePath: string): boolean {\n const result = getDb()\n .select()\n .from(schema.fileBackups)\n .where(\n and(\n eq(schema.fileBackups.checkpointId, checkpointId),\n eq(schema.fileBackups.filePath, filePath)\n )\n )\n .get();\n return !!result;\n },\n\n deleteBySession(sessionId: string): number {\n const result = getDb()\n .delete(schema.fileBackups)\n .where(eq(schema.fileBackups.sessionId, sessionId))\n .run();\n return result.changes;\n },\n};\n\nexport type { \n Session, \n Message, \n ToolExecution, \n TodoItem, \n SessionConfig, \n ModelMessage,\n Terminal,\n ActiveStream,\n Checkpoint,\n FileBackup,\n};\n","import { sqliteTable, text, integer } from 'drizzle-orm/sqlite-core';\n\n// Sessions table - represents an agent session/thread\nexport const sessions = sqliteTable('sessions', {\n id: text('id').primaryKey(),\n name: text('name'),\n workingDirectory: text('working_directory').notNull(),\n model: text('model').notNull(),\n status: text('status', { enum: ['active', 'waiting', 'completed', 'error'] })\n .notNull()\n .default('active'),\n config: text('config', { mode: 'json' }).$type<SessionConfig>(),\n createdAt: integer('created_at', { mode: 'timestamp' })\n .notNull()\n .$defaultFn(() => new Date()),\n updatedAt: integer('updated_at', { mode: 'timestamp' })\n .notNull()\n .$defaultFn(() => new Date()),\n});\n\n// Messages table - stores AI SDK ModelMessage directly\nexport const messages = sqliteTable('messages', {\n id: text('id').primaryKey(),\n sessionId: text('session_id')\n .notNull()\n .references(() => sessions.id, { onDelete: 'cascade' }),\n // Store the entire ModelMessage as JSON (role + content)\n modelMessage: text('model_message', { mode: 'json' }).$type<ModelMessage>().notNull(),\n // Sequence number within session to maintain exact ordering\n sequence: integer('sequence').notNull().default(0),\n createdAt: integer('created_at', { mode: 'timestamp' })\n .notNull()\n .$defaultFn(() => new Date()),\n});\n\n// Tool executions - tracks all tool calls and their results\nexport const toolExecutions = sqliteTable('tool_executions', {\n id: text('id').primaryKey(),\n sessionId: text('session_id')\n .notNull()\n .references(() => sessions.id, { onDelete: 'cascade' }),\n messageId: text('message_id').references(() => messages.id, { onDelete: 'cascade' }),\n toolName: text('tool_name').notNull(),\n toolCallId: text('tool_call_id').notNull(),\n input: text('input', { mode: 'json' }),\n output: text('output', { mode: 'json' }),\n status: text('status', { enum: ['pending', 'approved', 'rejected', 'completed', 'error'] })\n .notNull()\n .default('pending'),\n requiresApproval: integer('requires_approval', { mode: 'boolean' }).notNull().default(false),\n error: text('error'),\n startedAt: integer('started_at', { mode: 'timestamp' })\n .notNull()\n .$defaultFn(() => new Date()),\n completedAt: integer('completed_at', { mode: 'timestamp' }),\n});\n\n// Todo items for the planning tool\nexport const todoItems = sqliteTable('todo_items', {\n id: text('id').primaryKey(),\n sessionId: text('session_id')\n .notNull()\n .references(() => sessions.id, { onDelete: 'cascade' }),\n content: text('content').notNull(),\n status: text('status', { enum: ['pending', 'in_progress', 'completed', 'cancelled'] })\n .notNull()\n .default('pending'),\n order: integer('order').notNull().default(0),\n createdAt: integer('created_at', { mode: 'timestamp' })\n .notNull()\n .$defaultFn(() => new Date()),\n updatedAt: integer('updated_at', { mode: 'timestamp' })\n .notNull()\n .$defaultFn(() => new Date()),\n});\n\n// Skills loaded into sessions\nexport const loadedSkills = sqliteTable('loaded_skills', {\n id: text('id').primaryKey(),\n sessionId: text('session_id')\n .notNull()\n .references(() => sessions.id, { onDelete: 'cascade' }),\n skillName: text('skill_name').notNull(),\n loadedAt: integer('loaded_at', { mode: 'timestamp' })\n .notNull()\n .$defaultFn(() => new Date()),\n});\n\n// Terminal sessions - background processes managed by agents\nexport const terminals = sqliteTable('terminals', {\n id: text('id').primaryKey(),\n sessionId: text('session_id')\n .notNull()\n .references(() => sessions.id, { onDelete: 'cascade' }),\n name: text('name'), // Optional friendly name (e.g., \"dev-server\")\n command: text('command').notNull(), // The command that was run\n cwd: text('cwd').notNull(), // Working directory\n pid: integer('pid'), // Process ID (null if not running)\n status: text('status', { enum: ['running', 'stopped', 'error'] })\n .notNull()\n .default('running'),\n exitCode: integer('exit_code'), // Exit code if stopped\n error: text('error'), // Error message if status is 'error'\n createdAt: integer('created_at', { mode: 'timestamp' })\n .notNull()\n .$defaultFn(() => new Date()),\n stoppedAt: integer('stopped_at', { mode: 'timestamp' }),\n});\n\n// Active streams - tracks resumable stream sessions for multi-client sync\nexport const activeStreams = sqliteTable('active_streams', {\n id: text('id').primaryKey(),\n sessionId: text('session_id')\n .notNull()\n .references(() => sessions.id, { onDelete: 'cascade' }),\n streamId: text('stream_id').notNull().unique(), // Unique stream identifier\n status: text('status', { enum: ['active', 'finished', 'error'] })\n .notNull()\n .default('active'),\n createdAt: integer('created_at', { mode: 'timestamp' })\n .notNull()\n .$defaultFn(() => new Date()),\n finishedAt: integer('finished_at', { mode: 'timestamp' }),\n});\n\n// Checkpoints - created before each user message for revert capability\nexport const checkpoints = sqliteTable('checkpoints', {\n id: text('id').primaryKey(),\n sessionId: text('session_id')\n .notNull()\n .references(() => sessions.id, { onDelete: 'cascade' }),\n // The message sequence number this checkpoint was created BEFORE\n // (i.e., the state before this user message was processed)\n messageSequence: integer('message_sequence').notNull(),\n // Optional git commit hash if in a git repo\n gitHead: text('git_head'),\n createdAt: integer('created_at', { mode: 'timestamp' })\n .notNull()\n .$defaultFn(() => new Date()),\n});\n\n// File backups - stores original file content before modifications\nexport const fileBackups = sqliteTable('file_backups', {\n id: text('id').primaryKey(),\n checkpointId: text('checkpoint_id')\n .notNull()\n .references(() => checkpoints.id, { onDelete: 'cascade' }),\n sessionId: text('session_id')\n .notNull()\n .references(() => sessions.id, { onDelete: 'cascade' }),\n // Relative path from working directory\n filePath: text('file_path').notNull(),\n // Original content (null means file didn't exist before)\n originalContent: text('original_content'),\n // Whether the file existed before this checkpoint\n existed: integer('existed', { mode: 'boolean' }).notNull().default(true),\n createdAt: integer('created_at', { mode: 'timestamp' })\n .notNull()\n .$defaultFn(() => new Date()),\n});\n\n// Types for JSON columns\nexport interface SessionConfig {\n toolApprovals?: Record<string, boolean>;\n approvalWebhook?: string;\n skillsDirectory?: string;\n maxContextChars?: number;\n}\n\n// AI SDK ModelMessage types - stored directly for accurate context passing\n// These match the exact format from AI SDK's response.messages\nexport type ModelMessage = \n | SystemModelMessage\n | UserModelMessage\n | AssistantModelMessage\n | ToolModelMessage;\n\nexport interface SystemModelMessage {\n role: 'system';\n content: string;\n}\n\nexport interface UserModelMessage {\n role: 'user';\n content: string;\n}\n\nexport interface AssistantModelMessage {\n role: 'assistant';\n content: string | AssistantContentPart[];\n}\n\nexport interface ToolModelMessage {\n role: 'tool';\n content: ToolResultPart[];\n}\n\nexport interface AssistantContentPart {\n type: 'text' | 'tool-call' | 'reasoning';\n text?: string;\n toolCallId?: string;\n toolName?: string;\n input?: unknown;\n}\n\nexport interface ToolResultPart {\n type: 'tool-result';\n toolCallId: string;\n toolName: string;\n output: unknown;\n}\n\n// Type exports for queries\nexport type Session = typeof sessions.$inferSelect;\nexport type NewSession = typeof sessions.$inferInsert;\nexport type Message = typeof messages.$inferSelect;\nexport type NewMessage = typeof messages.$inferInsert;\nexport type ToolExecution = typeof toolExecutions.$inferSelect;\nexport type NewToolExecution = typeof toolExecutions.$inferInsert;\nexport type TodoItem = typeof todoItems.$inferSelect;\nexport type NewTodoItem = typeof todoItems.$inferInsert;\nexport type LoadedSkill = typeof loadedSkills.$inferSelect;\nexport type Terminal = typeof terminals.$inferSelect;\nexport type NewTerminal = typeof terminals.$inferInsert;\nexport type ActiveStream = typeof activeStreams.$inferSelect;\nexport type NewActiveStream = typeof activeStreams.$inferInsert;\nexport type Checkpoint = typeof checkpoints.$inferSelect;\nexport type NewCheckpoint = typeof checkpoints.$inferInsert;\nexport type FileBackup = typeof fileBackups.$inferSelect;\nexport type NewFileBackup = typeof fileBackups.$inferInsert;"],"mappings":"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","names":["messages"]}
|