openfox 0.2.4 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agent-defaults/builder.agent.md +3 -7
- package/dist/agent-defaults/code-reviewer.agent.md +1 -2
- package/dist/agent-defaults/explorer.agent.md +22 -0
- package/dist/agent-defaults/planner.agent.md +2 -9
- package/dist/agent-defaults/verifier.agent.md +4 -5
- package/dist/ask-3RK5YJZE.js +19 -0
- package/dist/auto-compaction-WRS52ERL.js +23 -0
- package/dist/{branch.api-WDHKZJS6.js → branch.api-XADFZZAA.js} +2 -2
- package/dist/chat-handler-226J7XYE.js +201 -0
- package/dist/chunk-22CTURMH.js +128 -0
- package/dist/chunk-2KSCZSXQ.js +680 -0
- package/dist/{chunk-4Z2OZ6NX.js → chunk-3EHGGBWE.js} +29 -2
- package/dist/{chunk-GZ7ZVJIW.js → chunk-3UQACISJ.js} +159 -49
- package/dist/{chunk-AF7XS4OI.js → chunk-5YQAXUKO.js} +4 -1
- package/dist/{chunk-MTX5Y3V3.js → chunk-7IOZFJBW.js} +2 -2
- package/dist/{chunk-FUUYYDVG.js → chunk-ARKA56ZM.js} +2842 -4508
- package/dist/chunk-BU2ZYOEA.js +1067 -0
- package/dist/{chunk-O4W4555H.js → chunk-HGMG5F72.js} +1353 -1134
- package/dist/chunk-M2C5GEQR.js +137 -0
- package/dist/chunk-OWDPT3M6.js +314 -0
- package/dist/{chunk-U62ZECSQ.js → chunk-PNBH3RAX.js} +2 -2
- package/dist/chunk-QDEKU5RL.js +75 -0
- package/dist/chunk-QYP5HQV4.js +438 -0
- package/dist/chunk-S4QKTRGJ.js +343 -0
- package/dist/{chunk-ARFK3RZW.js → chunk-TPT6HP4H.js} +1 -1
- package/dist/chunk-TVQOONDR.js +75 -0
- package/dist/{chunk-5ZUU23XF.js → chunk-U6BUT37A.js} +286 -33
- package/dist/{chunk-S5NRIWEU.js → chunk-VUQPJFMQ.js} +12 -12
- package/dist/{chunk-26NRL3HX.js → chunk-XFXOSPYH.js} +3 -2
- package/dist/{chunk-N2XYX4ZA.js → chunk-XKFPU2FA.js} +13 -2
- package/dist/cli/dev.js +4 -3
- package/dist/cli/index.js +4 -3
- package/dist/{config-GIXXE4VK.js → config-GIFCEJDU.js} +11 -6
- package/dist/events-G6CKHDNZ.js +114 -0
- package/dist/folding-O52L65VN.js +37 -0
- package/dist/{init-2EJ5AC7Z.js → init-MKTUIGZU.js} +46 -13
- package/dist/orchestrator-4CBESUGB.js +39 -0
- package/dist/package.json +9 -2
- package/dist/{paths-4BPXWB2W.js → paths-MM76AWYB.js} +2 -2
- package/dist/processor-LAKHPIG3.js +236 -0
- package/dist/{project-creator-CWPKRIKI.js → project-creator-KAQXMZCT.js} +9 -8
- package/dist/projects-GSMMEVSO.js +19 -0
- package/dist/{protocol-BFFz3WM0.d.ts → protocol-BZ6OIASP.d.ts} +71 -50
- package/dist/protocol-WQENDK72.js +88 -0
- package/dist/{provider-QEVVNYOX.js → provider-4KTWU6IV.js} +41 -9
- package/dist/runtime-config-2XJJIMSC.js +9 -0
- package/dist/{serve-RYEBLJVN.js → serve-5NHW3X5B.js} +50 -18
- package/dist/server/index.d.ts +81 -27
- package/dist/server/index.js +19 -11
- package/dist/{settings-D5O3TMLD.js → settings-NCLN4JDZ.js} +4 -4
- package/dist/shared/index.d.ts +2 -2
- package/dist/shared/index.js +1 -1
- package/dist/tools-WOZVXNEQ.js +48 -0
- package/dist/vision-fallback-63VPBSEN.js +22 -0
- package/dist/web/assets/{index-DHmZmhnR.css → index-BsaaC0X0.css} +32 -1
- package/dist/web/assets/index-CvO7QkGh.js +141 -0
- package/dist/web/index.html +2 -2
- package/dist/web/sounds/typing.mp3 +0 -0
- package/dist/workflow-defaults/default.workflow.json +1 -1
- package/package.json +9 -2
- package/dist/agent-defaults/debugger.agent.md +0 -21
- package/dist/agent-defaults/test-generator.agent.md +0 -22
- package/dist/chunk-L4XDA3V7.js +0 -218
- package/dist/tools-QMIIDJTV.js +0 -48
- 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
|