openfox 0.2.4 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. package/dist/agent-defaults/builder.agent.md +3 -7
  2. package/dist/agent-defaults/code-reviewer.agent.md +1 -2
  3. package/dist/agent-defaults/debugger.agent.md +1 -2
  4. package/dist/agent-defaults/planner.agent.md +2 -9
  5. package/dist/agent-defaults/test-generator.agent.md +1 -1
  6. package/dist/agent-defaults/verifier.agent.md +4 -5
  7. package/dist/ask-3RK5YJZE.js +19 -0
  8. package/dist/auto-compaction-7LNZ5J3W.js +23 -0
  9. package/dist/{branch.api-WDHKZJS6.js → branch.api-XADFZZAA.js} +2 -2
  10. package/dist/chat-handler-T7SLWZ4E.js +201 -0
  11. package/dist/chunk-22CTURMH.js +128 -0
  12. package/dist/chunk-2KSCZSXQ.js +680 -0
  13. package/dist/{chunk-S5NRIWEU.js → chunk-2TBNG7CS.js} +12 -12
  14. package/dist/{chunk-4Z2OZ6NX.js → chunk-3EHGGBWE.js} +29 -2
  15. package/dist/{chunk-GZ7ZVJIW.js → chunk-3UQACISJ.js} +159 -49
  16. package/dist/{chunk-AF7XS4OI.js → chunk-5YQAXUKO.js} +4 -1
  17. package/dist/{chunk-MTX5Y3V3.js → chunk-7IOZFJBW.js} +2 -2
  18. package/dist/chunk-BU2ZYOEA.js +1067 -0
  19. package/dist/chunk-M2C5GEQR.js +137 -0
  20. package/dist/{chunk-U62ZECSQ.js → chunk-PNBH3RAX.js} +2 -2
  21. package/dist/chunk-QDEKU5RL.js +75 -0
  22. package/dist/chunk-QYP5HQV4.js +438 -0
  23. package/dist/{chunk-O4W4555H.js → chunk-RAZQTQK2.js} +1353 -1134
  24. package/dist/chunk-S4QKTRGJ.js +343 -0
  25. package/dist/{chunk-ARFK3RZW.js → chunk-TPT6HP4H.js} +1 -1
  26. package/dist/chunk-TVQOONDR.js +75 -0
  27. package/dist/{chunk-5ZUU23XF.js → chunk-U6BUT37A.js} +286 -33
  28. package/dist/chunk-VZXXJKSQ.js +314 -0
  29. package/dist/{chunk-26NRL3HX.js → chunk-XFXOSPYH.js} +3 -2
  30. package/dist/{chunk-N2XYX4ZA.js → chunk-XKFPU2FA.js} +13 -2
  31. package/dist/{chunk-FUUYYDVG.js → chunk-YB2574C6.js} +2841 -4507
  32. package/dist/cli/dev.js +4 -3
  33. package/dist/cli/index.js +4 -3
  34. package/dist/{config-GIXXE4VK.js → config-GIFCEJDU.js} +11 -6
  35. package/dist/events-G6CKHDNZ.js +114 -0
  36. package/dist/folding-O52L65VN.js +37 -0
  37. package/dist/{init-2EJ5AC7Z.js → init-MKTUIGZU.js} +46 -13
  38. package/dist/orchestrator-UGXX7JRQ.js +39 -0
  39. package/dist/package.json +9 -2
  40. package/dist/{paths-4BPXWB2W.js → paths-MM76AWYB.js} +2 -2
  41. package/dist/processor-KLHKDILM.js +236 -0
  42. package/dist/{project-creator-CWPKRIKI.js → project-creator-KAQXMZCT.js} +9 -8
  43. package/dist/projects-GSMMEVSO.js +19 -0
  44. package/dist/{protocol-BFFz3WM0.d.ts → protocol-BZ6OIASP.d.ts} +71 -50
  45. package/dist/protocol-WQENDK72.js +88 -0
  46. package/dist/{provider-QEVVNYOX.js → provider-4KTWU6IV.js} +41 -9
  47. package/dist/runtime-config-2XJJIMSC.js +9 -0
  48. package/dist/{serve-RYEBLJVN.js → serve-UK6B6G2O.js} +38 -18
  49. package/dist/server/index.d.ts +81 -27
  50. package/dist/server/index.js +19 -11
  51. package/dist/{settings-D5O3TMLD.js → settings-NCLN4JDZ.js} +4 -4
  52. package/dist/shared/index.d.ts +2 -2
  53. package/dist/shared/index.js +1 -1
  54. package/dist/tools-SGBQE736.js +48 -0
  55. package/dist/vision-fallback-63VPBSEN.js +22 -0
  56. package/dist/web/assets/{index-DHmZmhnR.css → index-BsaaC0X0.css} +32 -1
  57. package/dist/web/assets/index-CvO7QkGh.js +141 -0
  58. package/dist/web/index.html +2 -2
  59. package/dist/web/sounds/typing.mp3 +0 -0
  60. package/dist/workflow-defaults/default.workflow.json +1 -1
  61. package/package.json +9 -2
  62. package/dist/chunk-L4XDA3V7.js +0 -218
  63. package/dist/tools-QMIIDJTV.js +0 -48
  64. package/dist/web/assets/index-CAD7Iv_d.js +0 -122
@@ -0,0 +1,1067 @@
1
+ import {
2
+ buildContextMessagesFromEventHistory,
3
+ buildMessagesFromStoredEvents,
4
+ foldContextState,
5
+ foldSessionState
6
+ } from "./chunk-2KSCZSXQ.js";
7
+ import {
8
+ getDatabase
9
+ } from "./chunk-3EHGGBWE.js";
10
+ import {
11
+ getRuntimeConfig
12
+ } from "./chunk-TVQOONDR.js";
13
+ import {
14
+ logger
15
+ } from "./chunk-PNBH3RAX.js";
16
+
17
+ // src/server/events/store.ts
18
+ var EventStore = class {
19
+ db;
20
+ subscribers = /* @__PURE__ */ new Map();
21
+ constructor(db) {
22
+ this.db = db;
23
+ this.initSchema();
24
+ }
25
+ // --------------------------------------------------------------------------
26
+ // Schema
27
+ // --------------------------------------------------------------------------
28
+ initSchema() {
29
+ this.db.exec(`
30
+ CREATE TABLE IF NOT EXISTS events (
31
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
32
+ session_id TEXT NOT NULL,
33
+ seq INTEGER NOT NULL,
34
+ timestamp INTEGER NOT NULL,
35
+ event_type TEXT NOT NULL,
36
+ payload TEXT NOT NULL,
37
+ UNIQUE(session_id, seq)
38
+ )
39
+ `);
40
+ this.db.exec(`
41
+ CREATE INDEX IF NOT EXISTS idx_events_session_seq
42
+ ON events(session_id, seq)
43
+ `);
44
+ this.db.exec(`
45
+ CREATE INDEX IF NOT EXISTS idx_events_session_type
46
+ ON events(session_id, event_type)
47
+ `);
48
+ }
49
+ // --------------------------------------------------------------------------
50
+ // Append
51
+ // --------------------------------------------------------------------------
52
+ /**
53
+ * Append a single event to a session
54
+ */
55
+ append(sessionId, event) {
56
+ const timestamp = Date.now();
57
+ const seq = this.getNextSeq(sessionId);
58
+ const payload = JSON.stringify(event.data);
59
+ this.db.prepare(
60
+ `INSERT INTO events (session_id, seq, timestamp, event_type, payload)
61
+ VALUES (?, ?, ?, ?, ?)`
62
+ ).run(sessionId, seq, timestamp, event.type, payload);
63
+ const stored = {
64
+ seq,
65
+ timestamp,
66
+ sessionId,
67
+ type: event.type,
68
+ data: event.data
69
+ };
70
+ this.notifySubscribers(sessionId, stored);
71
+ return stored;
72
+ }
73
+ /**
74
+ * Append multiple events atomically
75
+ */
76
+ appendBatch(sessionId, events) {
77
+ if (events.length === 0) return [];
78
+ const timestamp = Date.now();
79
+ let seq = this.getNextSeq(sessionId);
80
+ const results = [];
81
+ const insert = this.db.prepare(
82
+ `INSERT INTO events (session_id, seq, timestamp, event_type, payload)
83
+ VALUES (?, ?, ?, ?, ?)`
84
+ );
85
+ const transaction = this.db.transaction(() => {
86
+ for (const event of events) {
87
+ const payload = JSON.stringify(event.data);
88
+ insert.run(sessionId, seq, timestamp, event.type, payload);
89
+ const stored = {
90
+ seq,
91
+ timestamp,
92
+ sessionId,
93
+ type: event.type,
94
+ data: event.data
95
+ };
96
+ results.push(stored);
97
+ seq++;
98
+ }
99
+ });
100
+ transaction();
101
+ for (const stored of results) {
102
+ this.notifySubscribers(sessionId, stored);
103
+ }
104
+ return results;
105
+ }
106
+ getNextSeq(sessionId) {
107
+ const row = this.db.prepare(`SELECT MAX(seq) as max_seq FROM events WHERE session_id = ?`).get(sessionId);
108
+ return (row?.max_seq ?? 0) + 1;
109
+ }
110
+ // --------------------------------------------------------------------------
111
+ // Retrieval
112
+ // --------------------------------------------------------------------------
113
+ /**
114
+ * Get all events for a session, optionally starting from a specific seq
115
+ */
116
+ getEvents(sessionId, fromSeq) {
117
+ const query = fromSeq !== void 0 ? `SELECT * FROM events WHERE session_id = ? AND seq >= ? ORDER BY seq` : `SELECT * FROM events WHERE session_id = ? ORDER BY seq`;
118
+ const rows = fromSeq !== void 0 ? this.db.prepare(query).all(sessionId, fromSeq) : this.db.prepare(query).all(sessionId);
119
+ return rows.map((row) => this.rowToStoredEvent(row));
120
+ }
121
+ /**
122
+ * Get the latest sequence number for a session
123
+ */
124
+ getLatestSeq(sessionId) {
125
+ const row = this.db.prepare(`SELECT MAX(seq) as max_seq FROM events WHERE session_id = ?`).get(sessionId);
126
+ return row?.max_seq ?? void 0;
127
+ }
128
+ /**
129
+ * Get the latest snapshot event for a session
130
+ */
131
+ getLatestSnapshot(sessionId) {
132
+ const row = this.db.prepare(
133
+ `SELECT * FROM events
134
+ WHERE session_id = ? AND event_type = 'turn.snapshot'
135
+ ORDER BY seq DESC LIMIT 1`
136
+ ).get(sessionId);
137
+ if (!row) return void 0;
138
+ return this.rowToStoredEvent(row);
139
+ }
140
+ /**
141
+ * Get the latest snapshot and all events since it
142
+ * This is the primary method for loading a session efficiently
143
+ */
144
+ getEventsSinceSnapshot(sessionId) {
145
+ const snapshotEvent = this.getLatestSnapshot(sessionId);
146
+ if (!snapshotEvent) {
147
+ return {
148
+ snapshot: void 0,
149
+ events: this.getEvents(sessionId)
150
+ };
151
+ }
152
+ const events = this.getEvents(sessionId, snapshotEvent.seq + 1);
153
+ return {
154
+ snapshot: snapshotEvent.data,
155
+ events
156
+ };
157
+ }
158
+ rowToStoredEvent(row) {
159
+ return {
160
+ seq: row.seq,
161
+ timestamp: row.timestamp,
162
+ sessionId: row.session_id,
163
+ type: row.event_type,
164
+ data: JSON.parse(row.payload)
165
+ };
166
+ }
167
+ // --------------------------------------------------------------------------
168
+ // Subscriptions
169
+ // --------------------------------------------------------------------------
170
+ /**
171
+ * Subscribe to events for a session
172
+ * Optionally replay events from a specific seq
173
+ *
174
+ * Returns an async iterator that yields events and an unsubscribe function
175
+ */
176
+ subscribe(sessionId, fromSeq) {
177
+ const queue = [];
178
+ let resolveNext = null;
179
+ let closed = false;
180
+ const closeIterator = () => {
181
+ closed = true;
182
+ if (resolveNext) {
183
+ resolveNext({ value: void 0, done: true });
184
+ resolveNext = null;
185
+ }
186
+ };
187
+ const subscriber = {
188
+ sessionId,
189
+ callback: (event) => {
190
+ if (closed) return;
191
+ if (resolveNext) {
192
+ resolveNext({ value: event, done: false });
193
+ resolveNext = null;
194
+ } else {
195
+ queue.push(event);
196
+ }
197
+ },
198
+ close: closeIterator,
199
+ closed: false
200
+ };
201
+ let sessionSubs = this.subscribers.get(sessionId);
202
+ if (!sessionSubs) {
203
+ sessionSubs = /* @__PURE__ */ new Set();
204
+ this.subscribers.set(sessionId, sessionSubs);
205
+ }
206
+ sessionSubs.add(subscriber);
207
+ if (fromSeq !== void 0) {
208
+ const replayEvents = this.getEvents(sessionId, fromSeq);
209
+ queue.push(...replayEvents);
210
+ }
211
+ const iterator = {
212
+ [Symbol.asyncIterator]() {
213
+ return this;
214
+ },
215
+ async next() {
216
+ if (closed) {
217
+ return { value: void 0, done: true };
218
+ }
219
+ const queued = queue.shift();
220
+ if (queued) {
221
+ return { value: queued, done: false };
222
+ }
223
+ return new Promise((resolve) => {
224
+ resolveNext = resolve;
225
+ });
226
+ },
227
+ async return() {
228
+ closed = true;
229
+ subscriber.closed = true;
230
+ return { value: void 0, done: true };
231
+ }
232
+ };
233
+ const unsubscribe = () => {
234
+ subscriber.closed = true;
235
+ closeIterator();
236
+ sessionSubs?.delete(subscriber);
237
+ };
238
+ return { iterator, unsubscribe };
239
+ }
240
+ notifySubscribers(sessionId, event) {
241
+ const sessionSubs = this.subscribers.get(sessionId);
242
+ if (!sessionSubs) return;
243
+ for (const subscriber of sessionSubs) {
244
+ if (!subscriber.closed) {
245
+ subscriber.callback(event);
246
+ }
247
+ }
248
+ }
249
+ // --------------------------------------------------------------------------
250
+ // Cleanup
251
+ // --------------------------------------------------------------------------
252
+ /**
253
+ * Delete all events for a session
254
+ */
255
+ deleteSession(sessionId) {
256
+ this.db.prepare(`DELETE FROM events WHERE session_id = ?`).run(sessionId);
257
+ const sessionSubs = this.subscribers.get(sessionId);
258
+ if (sessionSubs) {
259
+ for (const subscriber of sessionSubs) {
260
+ subscriber.closed = true;
261
+ subscriber.close();
262
+ }
263
+ this.subscribers.delete(sessionId);
264
+ }
265
+ }
266
+ /**
267
+ * Delete all events up to (and including) a given sequence number.
268
+ * This is used to clean up events that are now contained in a snapshot.
269
+ *
270
+ * @param sessionId - The session ID
271
+ * @param upToSeq - The sequence number to delete up to (inclusive)
272
+ * @returns The number of events deleted
273
+ */
274
+ deleteEventsUpToSeq(sessionId, upToSeq) {
275
+ const result = this.db.prepare(`DELETE FROM events WHERE session_id = ? AND seq <= ?`).run(sessionId, upToSeq);
276
+ return result.changes;
277
+ }
278
+ /**
279
+ * Clean up old events, keeping only:
280
+ * - session.initialized event (seq 1)
281
+ * - All snapshot events
282
+ * - State-changing events (criteria.set, criterion.updated, mode.changed, phase.changed, context.state, etc.)
283
+ * - Events after the latest snapshot (current window)
284
+ *
285
+ * This is the recommended cleanup method that preserves all snapshots and state.
286
+ *
287
+ * @param sessionId - The session ID
288
+ * @returns The number of events deleted
289
+ */
290
+ cleanupOldEvents(sessionId) {
291
+ const latestSnapshotSeq = this.getLatestSnapshotSeq(sessionId);
292
+ if (latestSnapshotSeq === 0) {
293
+ return 0;
294
+ }
295
+ const result = this.db.prepare(`
296
+ DELETE FROM events
297
+ WHERE session_id = ? AND seq > 1 AND seq < ?
298
+ AND event_type NOT IN (
299
+ 'criteria.set',
300
+ 'criterion.updated',
301
+ 'mode.changed',
302
+ 'phase.changed',
303
+ 'todo.updated',
304
+ 'context.state'
305
+ )
306
+ `).run(sessionId, latestSnapshotSeq);
307
+ return result.changes;
308
+ }
309
+ /**
310
+ * Get the latest snapshot sequence number for a session
311
+ * @returns The sequence number of the latest snapshot, or 0 if none
312
+ */
313
+ getLatestSnapshotSeq(sessionId) {
314
+ const row = this.db.prepare(`
315
+ SELECT seq FROM events
316
+ WHERE session_id = ? AND event_type = 'turn.snapshot'
317
+ ORDER BY seq DESC LIMIT 1
318
+ `).get(sessionId);
319
+ return row?.seq ?? 0;
320
+ }
321
+ /**
322
+ * One-time storage optimization: delete old snapshots and strip
323
+ * promptContext.messages from remaining snapshots across all sessions.
324
+ * Safe to run multiple times (idempotent).
325
+ */
326
+ optimizeStorage() {
327
+ let deletedSnapshots = 0;
328
+ let strippedSnapshots = 0;
329
+ const deleteResult = this.db.prepare(`
330
+ DELETE FROM events
331
+ WHERE event_type = 'turn.snapshot'
332
+ AND id NOT IN (
333
+ SELECT e1.id FROM events e1
334
+ WHERE e1.event_type = 'turn.snapshot'
335
+ AND e1.seq = (
336
+ SELECT MAX(e2.seq) FROM events e2
337
+ WHERE e2.session_id = e1.session_id
338
+ AND e2.event_type = 'turn.snapshot'
339
+ )
340
+ )
341
+ `).run();
342
+ deletedSnapshots = deleteResult.changes;
343
+ const snapshots = this.db.prepare(`SELECT id, payload FROM events WHERE event_type = 'turn.snapshot'`).all();
344
+ const updateStmt = this.db.prepare(`UPDATE events SET payload = ? WHERE id = ?`);
345
+ for (const row of snapshots) {
346
+ const data = JSON.parse(row.payload);
347
+ const messages = data.messages;
348
+ if (!messages) continue;
349
+ let lastAssistantIdx = -1;
350
+ for (let i = messages.length - 1; i >= 0; i--) {
351
+ const msg = messages[i];
352
+ if (!msg) continue;
353
+ if (msg.role === "assistant" && msg.promptContext) {
354
+ lastAssistantIdx = i;
355
+ break;
356
+ }
357
+ }
358
+ let changed = false;
359
+ for (let i = 0; i < messages.length; i++) {
360
+ const msg = messages[i];
361
+ if (!msg) continue;
362
+ const pc = msg.promptContext;
363
+ if (pc?.messages && pc.messages.length > 0 && i !== lastAssistantIdx) {
364
+ pc.messages = [];
365
+ changed = true;
366
+ }
367
+ }
368
+ if (changed) {
369
+ updateStmt.run(JSON.stringify(data), row.id);
370
+ strippedSnapshots++;
371
+ }
372
+ }
373
+ return { deletedSnapshots, strippedSnapshots };
374
+ }
375
+ };
376
+ var eventStoreInstance = null;
377
+ function initEventStore(db) {
378
+ eventStoreInstance = new EventStore(db);
379
+ resetStaleRunningSessions(eventStoreInstance, db);
380
+ const result = eventStoreInstance.optimizeStorage();
381
+ if (result.deletedSnapshots > 0 || result.strippedSnapshots > 0) {
382
+ logger.info("Storage optimized", result);
383
+ }
384
+ return eventStoreInstance;
385
+ }
386
+ function resetStaleRunningSessions(eventStore, db) {
387
+ const sessions = db.prepare(`SELECT id FROM sessions`).all();
388
+ let resetCount = 0;
389
+ for (const { id: sessionId } of sessions) {
390
+ const lastRunningEvent = db.prepare(`
391
+ SELECT payload FROM events
392
+ WHERE session_id = ? AND event_type = 'running.changed'
393
+ ORDER BY seq DESC LIMIT 1
394
+ `).get(sessionId);
395
+ if (lastRunningEvent) {
396
+ const data = JSON.parse(lastRunningEvent.payload);
397
+ if (data.isRunning === true) {
398
+ eventStore.append(sessionId, {
399
+ type: "running.changed",
400
+ data: { isRunning: false }
401
+ });
402
+ resetCount++;
403
+ }
404
+ }
405
+ }
406
+ if (resetCount > 0) {
407
+ logger.info("EventStore reset stale running sessions", { count: resetCount });
408
+ }
409
+ }
410
+ function getEventStore() {
411
+ if (!eventStoreInstance) {
412
+ throw new Error("EventStore not initialized. Call initEventStore first.");
413
+ }
414
+ return eventStoreInstance;
415
+ }
416
+
417
+ // src/server/events/types.ts
418
+ function isTurnEvent(event) {
419
+ return typeof event === "object" && event !== null && "type" in event && "data" in event && typeof event.type === "string";
420
+ }
421
+ function isStoredEvent(event) {
422
+ return typeof event === "object" && event !== null && "seq" in event && "timestamp" in event && "sessionId" in event && "type" in event && "data" in event;
423
+ }
424
+ function createEvent(type, data) {
425
+ return { type, data };
426
+ }
427
+
428
+ // src/server/db/sessions.ts
429
+ function createSession(projectId, workdir, title, providerId, providerModel) {
430
+ const db = getDatabase();
431
+ const now = (/* @__PURE__ */ new Date()).toISOString();
432
+ const id = crypto.randomUUID();
433
+ db.prepare(`
434
+ INSERT INTO sessions (id, project_id, workdir, phase, mode, workflow_phase, is_running, created_at, updated_at, title, provider_id, provider_model, danger_level)
435
+ VALUES (?, ?, ?, 'idle', 'planner', 'plan', 0, ?, ?, ?, ?, ?, 'normal')
436
+ `).run(id, projectId, workdir, now, now, title ?? null, providerId ?? null, providerModel ?? null);
437
+ return {
438
+ id,
439
+ projectId,
440
+ workdir,
441
+ mode: "planner",
442
+ phase: "plan",
443
+ isRunning: false,
444
+ summary: null,
445
+ providerId: providerId ?? null,
446
+ providerModel: providerModel ?? null,
447
+ createdAt: now,
448
+ updatedAt: now,
449
+ messages: [],
450
+ criteria: [],
451
+ contextWindows: [],
452
+ executionState: null,
453
+ metadata: {
454
+ ...title ? { title } : {},
455
+ totalTokensUsed: 0,
456
+ totalToolCalls: 0,
457
+ iterationCount: 0
458
+ },
459
+ dangerLevel: "normal"
460
+ };
461
+ }
462
+ function getSession(id) {
463
+ const db = getDatabase();
464
+ const row = db.prepare(`
465
+ SELECT * FROM sessions WHERE id = ?
466
+ `).get(id);
467
+ if (!row) {
468
+ return null;
469
+ }
470
+ return {
471
+ id: row.id,
472
+ projectId: row.project_id,
473
+ workdir: row.workdir,
474
+ mode: row.mode ?? "planner",
475
+ phase: row.workflow_phase ?? "plan",
476
+ isRunning: Boolean(row.is_running),
477
+ summary: row.summary ?? null,
478
+ providerId: row.provider_id ?? null,
479
+ providerModel: row.provider_model ?? null,
480
+ createdAt: row.created_at,
481
+ updatedAt: row.updated_at,
482
+ messages: [],
483
+ criteria: [],
484
+ contextWindows: [],
485
+ executionState: null,
486
+ metadata: {
487
+ ...row.title ? { title: row.title } : {},
488
+ totalTokensUsed: row.total_tokens_used,
489
+ totalToolCalls: row.total_tool_calls,
490
+ iterationCount: row.iteration_count
491
+ },
492
+ dangerLevel: row.danger_level ?? "normal"
493
+ };
494
+ }
495
+ function updateSessionProvider(id, providerId, providerModel) {
496
+ const db = getDatabase();
497
+ const now = (/* @__PURE__ */ new Date()).toISOString();
498
+ db.prepare(`
499
+ UPDATE sessions SET provider_id = ?, provider_model = ?, updated_at = ? WHERE id = ?
500
+ `).run(providerId, providerModel, now, id);
501
+ }
502
+ function updateSessionDangerLevel(id, dangerLevel) {
503
+ const db = getDatabase();
504
+ const now = (/* @__PURE__ */ new Date()).toISOString();
505
+ db.prepare(`
506
+ UPDATE sessions SET danger_level = ?, updated_at = ? WHERE id = ?
507
+ `).run(dangerLevel, now, id);
508
+ }
509
+ function updateSessionSummary(id, summary) {
510
+ const db = getDatabase();
511
+ const now = (/* @__PURE__ */ new Date()).toISOString();
512
+ db.prepare(`
513
+ UPDATE sessions SET summary = ?, updated_at = ? WHERE id = ?
514
+ `).run(summary, now, id);
515
+ }
516
+ function updateSessionMetadata(id, metadata) {
517
+ const db = getDatabase();
518
+ const now = (/* @__PURE__ */ new Date()).toISOString();
519
+ const updates = ["updated_at = ?"];
520
+ const values = [now];
521
+ if (metadata.title !== void 0) {
522
+ updates.push("title = ?");
523
+ values.push(metadata.title ?? "");
524
+ }
525
+ if (metadata.totalTokensUsed !== void 0) {
526
+ updates.push("total_tokens_used = ?");
527
+ values.push(metadata.totalTokensUsed);
528
+ }
529
+ if (metadata.totalToolCalls !== void 0) {
530
+ updates.push("total_tool_calls = ?");
531
+ values.push(metadata.totalToolCalls);
532
+ }
533
+ if (metadata.iterationCount !== void 0) {
534
+ updates.push("iteration_count = ?");
535
+ values.push(metadata.iterationCount);
536
+ }
537
+ values.push(id);
538
+ db.prepare(`
539
+ UPDATE sessions SET ${updates.join(", ")} WHERE id = ?
540
+ `).run(...values);
541
+ }
542
+ function updateSessionMessageCount(id, delta) {
543
+ try {
544
+ const db = getDatabase();
545
+ const now = (/* @__PURE__ */ new Date()).toISOString();
546
+ db.prepare(`
547
+ UPDATE sessions SET message_count = message_count + ?, updated_at = ? WHERE id = ?
548
+ `).run(delta, now, id);
549
+ } catch {
550
+ }
551
+ }
552
+ function listSessions() {
553
+ const db = getDatabase();
554
+ const rows = db.prepare(`
555
+ SELECT
556
+ s.id,
557
+ s.project_id,
558
+ s.workdir,
559
+ s.mode,
560
+ s.workflow_phase,
561
+ s.is_running,
562
+ s.created_at,
563
+ s.updated_at,
564
+ s.title,
565
+ s.provider_id,
566
+ s.provider_model,
567
+ s.message_count
568
+ FROM sessions s
569
+ ORDER BY s.updated_at DESC
570
+ `).all();
571
+ return rows.map((row) => ({
572
+ id: row.id,
573
+ projectId: row.project_id,
574
+ ...row.title ? { title: row.title } : {},
575
+ workdir: row.workdir,
576
+ mode: row.mode ?? "planner",
577
+ phase: row.workflow_phase ?? "plan",
578
+ isRunning: Boolean(row.is_running),
579
+ providerId: row.provider_id ?? null,
580
+ providerModel: row.provider_model ?? null,
581
+ createdAt: row.created_at,
582
+ updatedAt: row.updated_at,
583
+ criteriaCount: 0,
584
+ // Derived from events
585
+ criteriaCompleted: 0,
586
+ // Derived from events
587
+ messageCount: row.message_count
588
+ }));
589
+ }
590
+ function listSessionsByProject(projectId, limit = 20, offset = 0) {
591
+ const db = getDatabase();
592
+ const rows = db.prepare(`
593
+ SELECT
594
+ s.id,
595
+ s.project_id,
596
+ s.workdir,
597
+ s.mode,
598
+ s.workflow_phase,
599
+ s.is_running,
600
+ s.created_at,
601
+ s.updated_at,
602
+ s.title,
603
+ s.provider_id,
604
+ s.provider_model,
605
+ s.message_count
606
+ FROM sessions s
607
+ WHERE s.project_id = ?
608
+ ORDER BY s.updated_at DESC
609
+ LIMIT ? OFFSET ?
610
+ `).all(projectId, limit + 1, offset);
611
+ const hasMore = rows.length > limit;
612
+ const sessions = rows.slice(0, limit).map((row) => ({
613
+ id: row.id,
614
+ projectId: row.project_id,
615
+ ...row.title ? { title: row.title } : {},
616
+ workdir: row.workdir,
617
+ mode: row.mode ?? "planner",
618
+ phase: row.workflow_phase ?? "plan",
619
+ isRunning: Boolean(row.is_running),
620
+ providerId: row.provider_id ?? null,
621
+ providerModel: row.provider_model ?? null,
622
+ createdAt: row.created_at,
623
+ updatedAt: row.updated_at,
624
+ criteriaCount: 0,
625
+ criteriaCompleted: 0,
626
+ messageCount: row.message_count
627
+ }));
628
+ return { sessions, hasMore };
629
+ }
630
+ function deleteSession(id) {
631
+ const db = getDatabase();
632
+ db.prepare("DELETE FROM sessions WHERE id = ?").run(id);
633
+ }
634
+
635
+ // src/server/events/session.ts
636
+ function toSnapshotMessage(message) {
637
+ return {
638
+ id: message.id,
639
+ role: message.role,
640
+ content: message.content,
641
+ timestamp: new Date(message.timestamp).getTime(),
642
+ ...message.thinkingContent !== void 0 && { thinkingContent: message.thinkingContent },
643
+ ...message.toolCalls !== void 0 && { toolCalls: message.toolCalls },
644
+ ...message.segments !== void 0 && { segments: message.segments },
645
+ ...message.stats !== void 0 && { stats: message.stats },
646
+ ...message.tokenCount !== void 0 && { tokenCount: message.tokenCount },
647
+ ...message.isStreaming !== void 0 && { isStreaming: message.isStreaming },
648
+ ...message.partial !== void 0 && { partial: message.partial },
649
+ ...message.subAgentId !== void 0 && { subAgentId: message.subAgentId },
650
+ ...message.subAgentType !== void 0 && { subAgentType: message.subAgentType },
651
+ ...message.isSystemGenerated !== void 0 && { isSystemGenerated: message.isSystemGenerated },
652
+ ...message.messageKind !== void 0 && { messageKind: message.messageKind },
653
+ ...message.contextWindowId !== void 0 && { contextWindowId: message.contextWindowId },
654
+ ...message.isCompactionSummary !== void 0 && { isCompactionSummary: message.isCompactionSummary },
655
+ ...message.promptContext !== void 0 && { promptContext: message.promptContext },
656
+ ...message.attachments !== void 0 && { attachments: message.attachments },
657
+ ...message.metadata !== void 0 && { metadata: message.metadata }
658
+ };
659
+ }
660
+ function getSessionState(sessionId, maxTokens) {
661
+ const eventStore = getEventStore();
662
+ const latestSnapshotEvent = eventStore.getLatestSnapshot(sessionId);
663
+ const events = eventStore.getEvents(sessionId);
664
+ if (events.length === 0) {
665
+ return void 0;
666
+ }
667
+ let initialWindowId;
668
+ for (const event of events) {
669
+ if (event.type === "session.initialized") {
670
+ const data = event.data;
671
+ initialWindowId = data.contextWindowId;
672
+ break;
673
+ }
674
+ }
675
+ if (!initialWindowId) {
676
+ return void 0;
677
+ }
678
+ const config = getRuntimeConfig();
679
+ const effectiveMaxTokens = maxTokens ?? config.context.maxTokens;
680
+ if (latestSnapshotEvent) {
681
+ const state = foldSessionState(events, initialWindowId, effectiveMaxTokens);
682
+ return {
683
+ ...state,
684
+ messages: buildMessagesFromStoredEvents(events).map(toSnapshotMessage)
685
+ };
686
+ }
687
+ return foldSessionState(events, initialWindowId, effectiveMaxTokens);
688
+ }
689
+ function getCurrentWindowMessages(sessionId) {
690
+ const currentWindowId = getCurrentContextWindowId(sessionId);
691
+ if (!currentWindowId) return [];
692
+ const state = getSessionState(sessionId);
693
+ if (!state) return [];
694
+ return state.messages.filter((m) => m.contextWindowId === currentWindowId);
695
+ }
696
+ function getContextMessages(sessionId) {
697
+ const eventStore = getEventStore();
698
+ const currentWindowId = getCurrentContextWindowId(sessionId);
699
+ if (!currentWindowId) return [];
700
+ const events = eventStore.getEvents(sessionId);
701
+ if (events.length === 0) return [];
702
+ return buildContextMessagesFromEventHistory(events, currentWindowId, { includeVerifier: false });
703
+ }
704
+ function getCurrentContextWindowId(sessionId) {
705
+ const eventStore = getEventStore();
706
+ const events = eventStore.getEvents(sessionId);
707
+ const contextResult = foldContextState(events, "");
708
+ return contextResult.currentContextWindowId || void 0;
709
+ }
710
+ function getReadFilesCache(sessionId) {
711
+ const state = getSessionState(sessionId);
712
+ return state?.readFiles ?? [];
713
+ }
714
+ function isFileInCache(sessionId, path) {
715
+ const cache = getReadFilesCache(sessionId);
716
+ return cache.some((f) => f.path === path);
717
+ }
718
+ function emitSessionInitialized(sessionId, projectId, workdir, contextWindowId, title) {
719
+ const eventStore = getEventStore();
720
+ eventStore.append(sessionId, {
721
+ type: "session.initialized",
722
+ data: {
723
+ projectId,
724
+ workdir,
725
+ contextWindowId,
726
+ ...title !== void 0 && { title }
727
+ }
728
+ });
729
+ }
730
+ function emitUserMessage(sessionId, content, options) {
731
+ const eventStore = getEventStore();
732
+ const messageId = crypto.randomUUID();
733
+ eventStore.append(sessionId, {
734
+ type: "message.start",
735
+ data: {
736
+ messageId,
737
+ role: "user",
738
+ content,
739
+ ...options?.contextWindowId !== void 0 && { contextWindowId: options.contextWindowId },
740
+ ...options?.isSystemGenerated !== void 0 && { isSystemGenerated: options.isSystemGenerated },
741
+ ...options?.messageKind !== void 0 && { messageKind: options.messageKind },
742
+ ...options?.isCompactionSummary !== void 0 && { isCompactionSummary: options.isCompactionSummary },
743
+ ...options?.tokenCount !== void 0 && { tokenCount: options.tokenCount },
744
+ ...options?.attachments !== void 0 && { attachments: options.attachments },
745
+ ...options?.subAgentId !== void 0 && { subAgentId: options.subAgentId },
746
+ ...options?.subAgentType !== void 0 && { subAgentType: options.subAgentType },
747
+ ...options?.metadata !== void 0 && { metadata: options.metadata }
748
+ }
749
+ });
750
+ eventStore.append(sessionId, {
751
+ type: "message.done",
752
+ data: { messageId }
753
+ });
754
+ updateSessionMessageCount(sessionId, 1);
755
+ return messageId;
756
+ }
757
+ function emitAssistantMessageStart(sessionId, options) {
758
+ const eventStore = getEventStore();
759
+ const messageId = crypto.randomUUID();
760
+ eventStore.append(sessionId, {
761
+ type: "message.start",
762
+ data: {
763
+ messageId,
764
+ role: "assistant",
765
+ ...options?.contextWindowId !== void 0 && { contextWindowId: options.contextWindowId },
766
+ ...options?.subAgentId !== void 0 && { subAgentId: options.subAgentId },
767
+ ...options?.subAgentType !== void 0 && { subAgentType: options.subAgentType }
768
+ }
769
+ });
770
+ updateSessionMessageCount(sessionId, 1);
771
+ return messageId;
772
+ }
773
+ function emitMessageDelta(sessionId, messageId, content) {
774
+ const eventStore = getEventStore();
775
+ eventStore.append(sessionId, {
776
+ type: "message.delta",
777
+ data: { messageId, content }
778
+ });
779
+ }
780
+ function emitMessageThinking(sessionId, messageId, content) {
781
+ const eventStore = getEventStore();
782
+ eventStore.append(sessionId, {
783
+ type: "message.thinking",
784
+ data: { messageId, content }
785
+ });
786
+ }
787
+ function emitMessageDone(sessionId, messageId, options) {
788
+ const eventStore = getEventStore();
789
+ eventStore.append(sessionId, {
790
+ type: "message.done",
791
+ data: {
792
+ messageId,
793
+ ...options?.stats !== void 0 && { stats: options.stats },
794
+ ...options?.segments !== void 0 && { segments: options.segments },
795
+ ...options?.partial !== void 0 && { partial: options.partial },
796
+ ...options?.promptContext !== void 0 && { promptContext: options.promptContext },
797
+ ...options?.tokenCount !== void 0 && { tokenCount: options.tokenCount }
798
+ }
799
+ });
800
+ }
801
+ function emitToolPreparing(sessionId, messageId, index, name) {
802
+ const eventStore = getEventStore();
803
+ eventStore.append(sessionId, {
804
+ type: "tool.preparing",
805
+ data: { messageId, index, name }
806
+ });
807
+ }
808
+ function emitToolCall(sessionId, messageId, toolCall) {
809
+ const eventStore = getEventStore();
810
+ eventStore.append(sessionId, {
811
+ type: "tool.call",
812
+ data: { messageId, toolCall }
813
+ });
814
+ }
815
+ function emitToolOutput(sessionId, toolCallId, stream, content) {
816
+ const eventStore = getEventStore();
817
+ eventStore.append(sessionId, {
818
+ type: "tool.output",
819
+ data: { toolCallId, stream, content }
820
+ });
821
+ }
822
+ function emitToolResult(sessionId, messageId, toolCallId, result) {
823
+ const eventStore = getEventStore();
824
+ eventStore.append(sessionId, {
825
+ type: "tool.result",
826
+ data: { messageId, toolCallId, result }
827
+ });
828
+ }
829
+ function emitModeChanged(sessionId, mode, auto, reason) {
830
+ const eventStore = getEventStore();
831
+ eventStore.append(sessionId, {
832
+ type: "mode.changed",
833
+ data: {
834
+ mode,
835
+ auto,
836
+ ...reason !== void 0 && { reason }
837
+ }
838
+ });
839
+ }
840
+ function emitPhaseChanged(sessionId, phase) {
841
+ const eventStore = getEventStore();
842
+ eventStore.append(sessionId, {
843
+ type: "phase.changed",
844
+ data: { phase }
845
+ });
846
+ }
847
+ function emitRunningChanged(sessionId, isRunning) {
848
+ const eventStore = getEventStore();
849
+ eventStore.append(sessionId, {
850
+ type: "running.changed",
851
+ data: { isRunning }
852
+ });
853
+ }
854
+ function emitCriteriaSet(sessionId, criteria) {
855
+ const eventStore = getEventStore();
856
+ eventStore.append(sessionId, {
857
+ type: "criteria.set",
858
+ data: { criteria }
859
+ });
860
+ }
861
+ function emitCriterionUpdated(sessionId, criterionId, status) {
862
+ const eventStore = getEventStore();
863
+ eventStore.append(sessionId, {
864
+ type: "criterion.updated",
865
+ data: { criterionId, status }
866
+ });
867
+ }
868
+ function emitTodosUpdated(sessionId, todos) {
869
+ const eventStore = getEventStore();
870
+ eventStore.append(sessionId, {
871
+ type: "todo.updated",
872
+ data: { todos }
873
+ });
874
+ }
875
+ function emitFileRead(sessionId, path, tokenCount, contextWindowId) {
876
+ const eventStore = getEventStore();
877
+ eventStore.append(sessionId, {
878
+ type: "file.read",
879
+ data: { path, tokenCount, contextWindowId }
880
+ });
881
+ }
882
+ function emitContextCompacted(sessionId, closedWindowId, newWindowId, beforeTokens, afterTokens, summary) {
883
+ const eventStore = getEventStore();
884
+ eventStore.append(sessionId, {
885
+ type: "context.compacted",
886
+ data: {
887
+ closedWindowId,
888
+ newWindowId,
889
+ beforeTokens,
890
+ afterTokens,
891
+ summary
892
+ }
893
+ });
894
+ }
895
+ function emitContextState(sessionId, currentTokens, maxTokens, compactionCount, dangerZone, canCompact) {
896
+ const eventStore = getEventStore();
897
+ eventStore.append(sessionId, {
898
+ type: "context.state",
899
+ data: {
900
+ currentTokens,
901
+ maxTokens,
902
+ compactionCount,
903
+ dangerZone,
904
+ canCompact
905
+ }
906
+ });
907
+ }
908
+ function emitChatDone(sessionId, messageId, reason, stats) {
909
+ const eventStore = getEventStore();
910
+ eventStore.append(sessionId, {
911
+ type: "chat.done",
912
+ data: {
913
+ messageId,
914
+ reason,
915
+ ...stats !== void 0 && { stats }
916
+ }
917
+ });
918
+ }
919
+ function emitChatError(sessionId, error, recoverable) {
920
+ const eventStore = getEventStore();
921
+ eventStore.append(sessionId, {
922
+ type: "chat.error",
923
+ data: { error, recoverable }
924
+ });
925
+ }
926
+ function emitFormatRetry(sessionId, attempt, maxAttempts) {
927
+ const eventStore = getEventStore();
928
+ eventStore.append(sessionId, {
929
+ type: "format.retry",
930
+ data: { attempt, maxAttempts }
931
+ });
932
+ }
933
+ function emitTurnSnapshot(sessionId, snapshot) {
934
+ const eventStore = getEventStore();
935
+ eventStore.append(sessionId, {
936
+ type: "turn.snapshot",
937
+ data: snapshot
938
+ });
939
+ }
940
+ function compactContext(sessionId, summary, beforeTokens) {
941
+ const state = getSessionState(sessionId);
942
+ if (!state) {
943
+ throw new Error("Session not found");
944
+ }
945
+ const closedWindowId = state.currentContextWindowId;
946
+ const newWindowId = crypto.randomUUID();
947
+ const summaryMessageId = crypto.randomUUID();
948
+ const eventStore = getEventStore();
949
+ eventStore.append(sessionId, {
950
+ type: "message.start",
951
+ data: {
952
+ messageId: summaryMessageId,
953
+ role: "assistant",
954
+ content: summary,
955
+ contextWindowId: closedWindowId,
956
+ isCompactionSummary: true
957
+ }
958
+ });
959
+ eventStore.append(sessionId, {
960
+ type: "message.done",
961
+ data: { messageId: summaryMessageId }
962
+ });
963
+ emitContextCompacted(sessionId, closedWindowId, newWindowId, beforeTokens, 0, summary);
964
+ return { newWindowId, summaryMessageId };
965
+ }
966
+ function getRecentUserPromptsForSession(sessionId, limit = 10) {
967
+ try {
968
+ const eventStore = getEventStore();
969
+ const db = eventStore.db;
970
+ if (!db) {
971
+ return [];
972
+ }
973
+ const isRealUserMessage = (msg) => msg.role === "user" && !msg.isSystemGenerated && !msg.messageKind && !msg.subAgentType;
974
+ const promptMap = /* @__PURE__ */ new Map();
975
+ const snapshotRow = db.prepare(`
976
+ SELECT payload, timestamp
977
+ FROM events
978
+ WHERE session_id = ? AND event_type = 'turn.snapshot'
979
+ ORDER BY timestamp DESC
980
+ LIMIT 1
981
+ `).get(sessionId);
982
+ if (snapshotRow) {
983
+ const snapshot = JSON.parse(snapshotRow.payload);
984
+ for (const msg of snapshot.messages) {
985
+ if (isRealUserMessage(msg)) {
986
+ promptMap.set(msg.id, {
987
+ id: msg.id,
988
+ content: msg.content,
989
+ timestamp: new Date(msg.timestamp).toISOString()
990
+ });
991
+ }
992
+ }
993
+ }
994
+ const rows = db.prepare(`
995
+ SELECT payload, timestamp
996
+ FROM events
997
+ WHERE session_id = ? AND event_type = 'message.start'
998
+ AND json_extract(payload, '$.role') = 'user'
999
+ AND json_extract(payload, '$.isSystemGenerated') IS NULL
1000
+ AND json_extract(payload, '$.messageKind') IS NULL
1001
+ AND json_extract(payload, '$.subAgentType') IS NULL
1002
+ ORDER BY timestamp DESC
1003
+ LIMIT ?
1004
+ `).all(sessionId, limit);
1005
+ for (const row of rows) {
1006
+ const message = JSON.parse(row.payload);
1007
+ promptMap.set(message.messageId, {
1008
+ id: message.messageId,
1009
+ content: message.content,
1010
+ timestamp: new Date(row.timestamp).toISOString()
1011
+ });
1012
+ }
1013
+ return [...promptMap.values()].sort((a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime()).slice(0, limit);
1014
+ } catch (error) {
1015
+ return [];
1016
+ }
1017
+ }
1018
+
1019
+ export {
1020
+ EventStore,
1021
+ initEventStore,
1022
+ getEventStore,
1023
+ isTurnEvent,
1024
+ isStoredEvent,
1025
+ createEvent,
1026
+ createSession,
1027
+ getSession,
1028
+ updateSessionProvider,
1029
+ updateSessionDangerLevel,
1030
+ updateSessionSummary,
1031
+ updateSessionMetadata,
1032
+ listSessions,
1033
+ listSessionsByProject,
1034
+ deleteSession,
1035
+ getSessionState,
1036
+ getCurrentWindowMessages,
1037
+ getContextMessages,
1038
+ getCurrentContextWindowId,
1039
+ getReadFilesCache,
1040
+ isFileInCache,
1041
+ emitSessionInitialized,
1042
+ emitUserMessage,
1043
+ emitAssistantMessageStart,
1044
+ emitMessageDelta,
1045
+ emitMessageThinking,
1046
+ emitMessageDone,
1047
+ emitToolPreparing,
1048
+ emitToolCall,
1049
+ emitToolOutput,
1050
+ emitToolResult,
1051
+ emitModeChanged,
1052
+ emitPhaseChanged,
1053
+ emitRunningChanged,
1054
+ emitCriteriaSet,
1055
+ emitCriterionUpdated,
1056
+ emitTodosUpdated,
1057
+ emitFileRead,
1058
+ emitContextCompacted,
1059
+ emitContextState,
1060
+ emitChatDone,
1061
+ emitChatError,
1062
+ emitFormatRetry,
1063
+ emitTurnSnapshot,
1064
+ compactContext,
1065
+ getRecentUserPromptsForSession
1066
+ };
1067
+ //# sourceMappingURL=chunk-BU2ZYOEA.js.map