@copilotkit/sqlite-runner 1.82.0 → 2.0.0-rc.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.
- package/dist/sqlite-runner.cjs +137 -20
- package/dist/sqlite-runner.cjs.map +1 -1
- package/dist/sqlite-runner.d.cts +45 -1
- package/dist/sqlite-runner.d.cts.map +1 -1
- package/dist/sqlite-runner.d.mts +45 -1
- package/dist/sqlite-runner.d.mts.map +1 -1
- package/dist/sqlite-runner.mjs +133 -16
- package/dist/sqlite-runner.mjs.map +1 -1
- package/package.json +3 -3
- package/src/__tests__/sqlite-ownership.test.ts +748 -0
- package/src/sqlite-runner.ts +224 -19
package/dist/sqlite-runner.cjs
CHANGED
|
@@ -1,21 +1,53 @@
|
|
|
1
1
|
const require_runtime = require('./_virtual/_rolldown/runtime.cjs');
|
|
2
|
-
let
|
|
2
|
+
let _copilotkit_runtime = require("@copilotkit/runtime");
|
|
3
3
|
let rxjs = require("rxjs");
|
|
4
4
|
let _ag_ui_client = require("@ag-ui/client");
|
|
5
5
|
let better_sqlite3 = require("better-sqlite3");
|
|
6
6
|
better_sqlite3 = require_runtime.__toESM(better_sqlite3);
|
|
7
7
|
|
|
8
8
|
//#region src/sqlite-runner.ts
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
/**
|
|
10
|
+
* 2: adds `thread_owners`. Also written as the `version` of each stored run,
|
|
11
|
+
* so runs stored from now on are tagged 2.
|
|
12
|
+
*/
|
|
13
|
+
const SCHEMA_VERSION = 2;
|
|
14
|
+
/**
|
|
15
|
+
* Owner of a legacy thread: one whose runs were stored before owners were
|
|
16
|
+
* recorded (schema 1), so it has runs but no `thread_owners` row. Before the
|
|
17
|
+
* upgrade, every caller that held a thread id could reach that thread. A
|
|
18
|
+
* legacy thread keeps exactly that: it stays a capability, reachable by id for
|
|
19
|
+
* every caller and every identity, and continuing it never claims it. Only a
|
|
20
|
+
* thread that has no runs yet is claimed, by its first run, so no new thread
|
|
21
|
+
* can become legacy. The value holds a NUL character, which no request
|
|
22
|
+
* identity can hold.
|
|
23
|
+
*/
|
|
24
|
+
const LEGACY_THREAD_OWNER = "\0legacy";
|
|
25
|
+
var SqliteAgentRunner = class extends _copilotkit_runtime.AgentRunner {
|
|
12
26
|
db;
|
|
27
|
+
/**
|
|
28
|
+
* Live runs of this runner, by thread id, for streaming and stop. Kept per
|
|
29
|
+
* instance: owners are checked against this instance's database, so a live
|
|
30
|
+
* run of another runner, which may hold the same thread id for a different
|
|
31
|
+
* owner, must never be bridged, stopped or torn down from here.
|
|
32
|
+
*/
|
|
33
|
+
activeConnections = /* @__PURE__ */ new Map();
|
|
13
34
|
constructor(options = {}) {
|
|
14
35
|
super();
|
|
15
36
|
const dbPath = options.dbPath ?? ":memory:";
|
|
16
37
|
if (!better_sqlite3.default) throw new Error("better-sqlite3 is required for SqliteAgentRunner but was not found.\nPlease install it in your project:\n npm install better-sqlite3\n or\n pnpm add better-sqlite3\n or\n yarn add better-sqlite3\n\nIf you don't need persistence, use InMemoryAgentRunner instead.");
|
|
17
38
|
this.db = new better_sqlite3.default(dbPath);
|
|
18
39
|
this.initializeSchema();
|
|
40
|
+
this.warnIfLegacyThreads();
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Logs once, when the database opens, how many legacy threads it holds
|
|
44
|
+
* (see {@link LEGACY_THREAD_OWNER}), so operators know those threads are
|
|
45
|
+
* still reachable by id for every caller.
|
|
46
|
+
*/
|
|
47
|
+
warnIfLegacyThreads() {
|
|
48
|
+
const count = this.db.prepare("SELECT COUNT(DISTINCT thread_id) AS count FROM agent_runs WHERE thread_id NOT IN (SELECT thread_id FROM thread_owners)").get()?.count ?? 0;
|
|
49
|
+
if (count === 0) return;
|
|
50
|
+
console.warn(`[CopilotKit] SqliteAgentRunner: ${count} thread${count === 1 ? " was" : "s were"} stored before thread owners were recorded. Anyone who holds the id of one of them can still read, continue and stop it, as before the upgrade, whatever identifyUser returns. Threads created from now on belong to the identity whose run created them.`);
|
|
19
51
|
}
|
|
20
52
|
initializeSchema() {
|
|
21
53
|
this.db.exec(`
|
|
@@ -37,6 +69,13 @@ var SqliteAgentRunner = class extends _copilotkit_runtime_v2.AgentRunner {
|
|
|
37
69
|
current_run_id TEXT,
|
|
38
70
|
updated_at INTEGER NOT NULL
|
|
39
71
|
)
|
|
72
|
+
`);
|
|
73
|
+
this.db.exec(`
|
|
74
|
+
CREATE TABLE IF NOT EXISTS thread_owners (
|
|
75
|
+
thread_id TEXT PRIMARY KEY,
|
|
76
|
+
owner_id TEXT NOT NULL,
|
|
77
|
+
created_at INTEGER NOT NULL
|
|
78
|
+
)
|
|
40
79
|
`);
|
|
41
80
|
this.db.exec(`
|
|
42
81
|
CREATE INDEX IF NOT EXISTS idx_thread_id ON agent_runs(thread_id);
|
|
@@ -108,8 +147,59 @@ var SqliteAgentRunner = class extends _copilotkit_runtime_v2.AgentRunner {
|
|
|
108
147
|
currentRunId: result?.current_run_id ?? null
|
|
109
148
|
};
|
|
110
149
|
}
|
|
150
|
+
/**
|
|
151
|
+
* Owner of a thread: the recorded owner, {@link LEGACY_THREAD_OWNER} for a
|
|
152
|
+
* thread whose runs predate owner records, or `undefined` for an unknown
|
|
153
|
+
* thread.
|
|
154
|
+
*
|
|
155
|
+
* The owner record and the run check are read by one statement, so they
|
|
156
|
+
* come from one snapshot of the database. With two reads, another runner on
|
|
157
|
+
* the same file could claim the thread and store its first run in between,
|
|
158
|
+
* and this call would see runs but no owner and take that thread for a
|
|
159
|
+
* legacy one, open to every caller.
|
|
160
|
+
*/
|
|
161
|
+
getThreadOwner(threadId) {
|
|
162
|
+
const row = this.db.prepare("SELECT (SELECT owner_id FROM thread_owners WHERE thread_id = ?) AS owner_id, EXISTS (SELECT 1 FROM agent_runs WHERE thread_id = ?) AS has_runs").get(threadId, threadId);
|
|
163
|
+
if (row?.owner_id != null) return row.owner_id;
|
|
164
|
+
return row?.has_runs ? LEGACY_THREAD_OWNER : void 0;
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* True when a call acting as `ownerId` may reach an existing thread: its
|
|
168
|
+
* owner, or anyone for a legacy thread.
|
|
169
|
+
*/
|
|
170
|
+
isOwnedBy(threadId, ownerId) {
|
|
171
|
+
const owner = this.getThreadOwner(threadId);
|
|
172
|
+
return owner === LEGACY_THREAD_OWNER || owner === (0, _copilotkit_runtime.ɵresolveThreadOwner)(ownerId);
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* False when the thread is held by an owner other than `scope`'s. A legacy
|
|
176
|
+
* thread is held by nobody, so it is accessible to every caller.
|
|
177
|
+
*/
|
|
178
|
+
isThreadAccessible(threadId, scope) {
|
|
179
|
+
const owner = this.getThreadOwner(threadId);
|
|
180
|
+
return owner === void 0 || owner === LEGACY_THREAD_OWNER || owner === (0, _copilotkit_runtime.ɵresolveThreadOwner)(scope.ownerId);
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Records `owner` as the owner of a new thread, and returns true when the
|
|
184
|
+
* run may go on as `owner`: `owner` holds the thread afterward, or the
|
|
185
|
+
* thread is legacy, which stays unowned. The read, the insert and the
|
|
186
|
+
* re-read run in one IMMEDIATE transaction, so when two runners or
|
|
187
|
+
* processes on one database file claim a new thread at once, exactly one
|
|
188
|
+
* wins and the other reads the winner's row.
|
|
189
|
+
*/
|
|
190
|
+
claimThread(threadId, owner) {
|
|
191
|
+
return this.db.transaction(() => {
|
|
192
|
+
const current = this.getThreadOwner(threadId);
|
|
193
|
+
if (current === LEGACY_THREAD_OWNER) return true;
|
|
194
|
+
if (current === void 0) this.db.prepare("INSERT INTO thread_owners (thread_id, owner_id, created_at) VALUES (?, ?, ?) ON CONFLICT(thread_id) DO NOTHING").run(threadId, owner, Date.now());
|
|
195
|
+
return this.getThreadOwner(threadId) === owner;
|
|
196
|
+
}).immediate();
|
|
197
|
+
}
|
|
111
198
|
run(request) {
|
|
112
|
-
|
|
199
|
+
const owner = (0, _copilotkit_runtime.ɵresolveThreadOwner)(request.ownerId);
|
|
200
|
+
if (!this.isThreadAccessible(request.threadId, { ownerId: owner })) throw new _copilotkit_runtime.ThreadAccessDeniedError();
|
|
201
|
+
if (this.getRunState(request.threadId).isRunning) throw new _copilotkit_runtime.ThreadAlreadyRunningError();
|
|
202
|
+
if (!this.claimThread(request.threadId, owner)) throw new _copilotkit_runtime.ThreadAccessDeniedError();
|
|
113
203
|
this.setRunState(request.threadId, true, request.input.runId);
|
|
114
204
|
const seenMessageIds = /* @__PURE__ */ new Set();
|
|
115
205
|
const currentRunEvents = [];
|
|
@@ -123,15 +213,16 @@ var SqliteAgentRunner = class extends _copilotkit_runtime_v2.AgentRunner {
|
|
|
123
213
|
}
|
|
124
214
|
}
|
|
125
215
|
const nextSubject = new rxjs.ReplaySubject(Infinity);
|
|
126
|
-
const prevSubject =
|
|
216
|
+
const prevSubject = this.activeConnections.get(request.threadId)?.subject;
|
|
127
217
|
const runSubject = new rxjs.ReplaySubject(Infinity);
|
|
128
|
-
|
|
218
|
+
const runConnection = {
|
|
129
219
|
subject: nextSubject,
|
|
130
220
|
agent: request.agent,
|
|
131
221
|
runSubject,
|
|
132
222
|
currentEvents: currentRunEvents,
|
|
133
223
|
stopRequested: false
|
|
134
|
-
}
|
|
224
|
+
};
|
|
225
|
+
this.activeConnections.set(request.threadId, runConnection);
|
|
135
226
|
const runAgent = async () => {
|
|
136
227
|
const parentRunId = this.getLatestRunId(request.threadId);
|
|
137
228
|
const ensureStoppedRunStarted = (stopRequested) => {
|
|
@@ -180,9 +271,9 @@ var SqliteAgentRunner = class extends _copilotkit_runtime_v2.AgentRunner {
|
|
|
180
271
|
}
|
|
181
272
|
}
|
|
182
273
|
});
|
|
183
|
-
const connection =
|
|
274
|
+
const connection = this.activeConnections.get(request.threadId);
|
|
184
275
|
ensureStoppedRunStarted(connection?.stopRequested ?? false);
|
|
185
|
-
const appendedEvents = (0,
|
|
276
|
+
const appendedEvents = (0, _copilotkit_runtime.finalizeRunEvents)(currentRunEvents, {
|
|
186
277
|
stopRequested: connection?.stopRequested ?? false,
|
|
187
278
|
protocolVersion: request.input.protocolVersion
|
|
188
279
|
});
|
|
@@ -200,11 +291,15 @@ var SqliteAgentRunner = class extends _copilotkit_runtime_v2.AgentRunner {
|
|
|
200
291
|
}
|
|
201
292
|
runSubject.complete();
|
|
202
293
|
nextSubject.complete();
|
|
203
|
-
|
|
204
|
-
} catch {
|
|
205
|
-
const connection =
|
|
294
|
+
this.activeConnections.delete(request.threadId);
|
|
295
|
+
} catch (error) {
|
|
296
|
+
const connection = this.activeConnections.get(request.threadId);
|
|
297
|
+
if (!connection?.stopRequested) console.error("Sqlite agent run failed", {
|
|
298
|
+
threadId: request.threadId,
|
|
299
|
+
runId: request.input.runId
|
|
300
|
+
}, error);
|
|
206
301
|
ensureStoppedRunStarted(connection?.stopRequested ?? false);
|
|
207
|
-
const appendedEvents = (0,
|
|
302
|
+
const appendedEvents = (0, _copilotkit_runtime.finalizeRunEvents)(currentRunEvents, {
|
|
208
303
|
stopRequested: connection?.stopRequested ?? false,
|
|
209
304
|
protocolVersion: request.input.protocolVersion
|
|
210
305
|
});
|
|
@@ -222,7 +317,7 @@ var SqliteAgentRunner = class extends _copilotkit_runtime_v2.AgentRunner {
|
|
|
222
317
|
}
|
|
223
318
|
runSubject.complete();
|
|
224
319
|
nextSubject.complete();
|
|
225
|
-
|
|
320
|
+
this.activeConnections.delete(request.threadId);
|
|
226
321
|
}
|
|
227
322
|
};
|
|
228
323
|
if (prevSubject) prevSubject.subscribe({
|
|
@@ -230,11 +325,29 @@ var SqliteAgentRunner = class extends _copilotkit_runtime_v2.AgentRunner {
|
|
|
230
325
|
error: (err) => nextSubject.error(err),
|
|
231
326
|
complete: () => {}
|
|
232
327
|
});
|
|
233
|
-
runAgent()
|
|
328
|
+
runAgent().catch((error) => {
|
|
329
|
+
console.error("Failed to finish sqlite agent run", {
|
|
330
|
+
threadId: request.threadId,
|
|
331
|
+
runId: request.input.runId
|
|
332
|
+
}, error);
|
|
333
|
+
try {
|
|
334
|
+
const state = this.getRunState(request.threadId);
|
|
335
|
+
if (state.currentRunId === null || state.currentRunId === request.input.runId) this.setRunState(request.threadId, false);
|
|
336
|
+
} catch (stateError) {
|
|
337
|
+
console.error("Failed to reset sqlite run state", stateError);
|
|
338
|
+
}
|
|
339
|
+
if (this.activeConnections.get(request.threadId) === runConnection) this.activeConnections.delete(request.threadId);
|
|
340
|
+
runSubject.complete();
|
|
341
|
+
nextSubject.complete();
|
|
342
|
+
});
|
|
234
343
|
return runSubject.asObservable();
|
|
235
344
|
}
|
|
236
345
|
connect(request) {
|
|
237
346
|
const connectionSubject = new rxjs.ReplaySubject(Infinity);
|
|
347
|
+
if (!this.isOwnedBy(request.threadId, request.ownerId)) {
|
|
348
|
+
connectionSubject.complete();
|
|
349
|
+
return connectionSubject.asObservable();
|
|
350
|
+
}
|
|
238
351
|
const historicRuns = this.getHistoricRuns(request.threadId);
|
|
239
352
|
const allHistoricEvents = [];
|
|
240
353
|
for (const run of historicRuns) allHistoricEvents.push(...run.events);
|
|
@@ -244,7 +357,7 @@ var SqliteAgentRunner = class extends _copilotkit_runtime_v2.AgentRunner {
|
|
|
244
357
|
connectionSubject.next(event);
|
|
245
358
|
if ("messageId" in event && typeof event.messageId === "string") emittedMessageIds.add(event.messageId);
|
|
246
359
|
}
|
|
247
|
-
const activeConnection =
|
|
360
|
+
const activeConnection = this.activeConnections.get(request.threadId);
|
|
248
361
|
const runState = this.getRunState(request.threadId);
|
|
249
362
|
if (activeConnection && (runState.isRunning || activeConnection.stopRequested)) activeConnection.subject.subscribe({
|
|
250
363
|
next: (event) => {
|
|
@@ -258,12 +371,16 @@ var SqliteAgentRunner = class extends _copilotkit_runtime_v2.AgentRunner {
|
|
|
258
371
|
return connectionSubject.asObservable();
|
|
259
372
|
}
|
|
260
373
|
isRunning(request) {
|
|
374
|
+
if (!this.isOwnedBy(request.threadId, request.ownerId)) return Promise.resolve(false);
|
|
261
375
|
const runState = this.getRunState(request.threadId);
|
|
262
376
|
return Promise.resolve(runState.isRunning);
|
|
263
377
|
}
|
|
264
378
|
stop(request) {
|
|
265
|
-
|
|
266
|
-
|
|
379
|
+
const runState = this.getRunState(request.threadId);
|
|
380
|
+
if (!runState.isRunning) return Promise.resolve(false);
|
|
381
|
+
if (!this.isOwnedBy(request.threadId, request.ownerId)) return Promise.resolve(false);
|
|
382
|
+
if (request.runId !== void 0 && runState.currentRunId !== request.runId) return Promise.resolve(false);
|
|
383
|
+
const connection = this.activeConnections.get(request.threadId);
|
|
267
384
|
const agent = connection?.agent;
|
|
268
385
|
if (!connection || !agent) return Promise.resolve(false);
|
|
269
386
|
if (connection.stopRequested) return Promise.resolve(false);
|
|
@@ -275,7 +392,7 @@ var SqliteAgentRunner = class extends _copilotkit_runtime_v2.AgentRunner {
|
|
|
275
392
|
} catch (error) {
|
|
276
393
|
console.error("Failed to abort sqlite agent run", error);
|
|
277
394
|
connection.stopRequested = false;
|
|
278
|
-
this.setRunState(request.threadId, true);
|
|
395
|
+
this.setRunState(request.threadId, true, runState.currentRunId ?? void 0);
|
|
279
396
|
return Promise.resolve(false);
|
|
280
397
|
}
|
|
281
398
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sqlite-runner.cjs","names":["AgentRunner","Database","ThreadAlreadyRunningError","EventType","ReplaySubject"],"sources":["../src/sqlite-runner.ts"],"sourcesContent":["import {\n AgentRunner,\n ThreadAlreadyRunningError,\n finalizeRunEvents,\n} from \"@copilotkit/runtime/v2\";\nimport type {\n AgentRunnerConnectRequest,\n AgentRunnerIsRunningRequest,\n AgentRunnerRunRequest,\n AgentRunnerStopRequest,\n} from \"@copilotkit/runtime/v2\";\nimport type { Observable } from \"rxjs\";\nimport { ReplaySubject } from \"rxjs\";\nimport type {\n AbstractAgent,\n BaseEvent,\n RunAgentInput,\n RunStartedEvent,\n} from \"@ag-ui/client\";\nimport { EventType, compactEvents } from \"@ag-ui/client\";\nimport Database from \"better-sqlite3\";\n\nconst SCHEMA_VERSION = 1;\n\ninterface AgentRunRecord {\n id: number;\n thread_id: string;\n run_id: string;\n parent_run_id: string | null;\n events: BaseEvent[];\n input: RunAgentInput;\n created_at: number;\n version: number;\n}\n\nexport interface SqliteAgentRunnerOptions {\n dbPath?: string;\n}\n\ninterface ActiveConnectionContext {\n subject: ReplaySubject<BaseEvent>;\n agent?: AbstractAgent;\n runSubject?: ReplaySubject<BaseEvent>;\n currentEvents?: BaseEvent[];\n stopRequested?: boolean;\n}\n\n// Active connections for streaming events and stop support\nconst ACTIVE_CONNECTIONS = new Map<string, ActiveConnectionContext>();\n\nexport class SqliteAgentRunner extends AgentRunner {\n private db: any;\n\n constructor(options: SqliteAgentRunnerOptions = {}) {\n super();\n const dbPath = options.dbPath ?? \":memory:\";\n\n if (!Database) {\n throw new Error(\n \"better-sqlite3 is required for SqliteAgentRunner but was not found.\\n\" +\n \"Please install it in your project:\\n\" +\n \" npm install better-sqlite3\\n\" +\n \" or\\n\" +\n \" pnpm add better-sqlite3\\n\" +\n \" or\\n\" +\n \" yarn add better-sqlite3\\n\\n\" +\n \"If you don't need persistence, use InMemoryAgentRunner instead.\",\n );\n }\n\n this.db = new Database(dbPath);\n this.initializeSchema();\n }\n\n private initializeSchema(): void {\n // Create the agent_runs table\n this.db.exec(`\n CREATE TABLE IF NOT EXISTS agent_runs (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n thread_id TEXT NOT NULL,\n run_id TEXT NOT NULL UNIQUE,\n parent_run_id TEXT,\n events TEXT NOT NULL,\n input TEXT NOT NULL,\n created_at INTEGER NOT NULL,\n version INTEGER NOT NULL\n )\n `);\n\n // Create run_state table to track active runs\n this.db.exec(`\n CREATE TABLE IF NOT EXISTS run_state (\n thread_id TEXT PRIMARY KEY,\n is_running INTEGER DEFAULT 0,\n current_run_id TEXT,\n updated_at INTEGER NOT NULL\n )\n `);\n\n // Create indexes for efficient queries\n this.db.exec(`\n CREATE INDEX IF NOT EXISTS idx_thread_id ON agent_runs(thread_id);\n CREATE INDEX IF NOT EXISTS idx_parent_run_id ON agent_runs(parent_run_id);\n `);\n\n // Create schema version table\n this.db.exec(`\n CREATE TABLE IF NOT EXISTS schema_version (\n version INTEGER PRIMARY KEY,\n applied_at INTEGER NOT NULL\n )\n `);\n\n // Check and set schema version\n const currentVersion = this.db\n .prepare(\n \"SELECT version FROM schema_version ORDER BY version DESC LIMIT 1\",\n )\n .get() as { version: number } | undefined;\n\n if (!currentVersion || currentVersion.version < SCHEMA_VERSION) {\n this.db\n .prepare(\n \"INSERT OR REPLACE INTO schema_version (version, applied_at) VALUES (?, ?)\",\n )\n .run(SCHEMA_VERSION, Date.now());\n }\n }\n\n private storeRun(\n threadId: string,\n runId: string,\n events: BaseEvent[],\n input: RunAgentInput,\n parentRunId?: string | null,\n ): void {\n // Compact ONLY the events from this run\n const compactedEvents = compactEvents(events);\n\n const stmt = this.db.prepare(`\n INSERT INTO agent_runs (thread_id, run_id, parent_run_id, events, input, created_at, version)\n VALUES (?, ?, ?, ?, ?, ?, ?)\n `);\n\n stmt.run(\n threadId,\n runId,\n parentRunId ?? null,\n JSON.stringify(compactedEvents), // Store only this run's compacted events\n JSON.stringify(input),\n Date.now(),\n SCHEMA_VERSION,\n );\n }\n\n private getHistoricRuns(threadId: string): AgentRunRecord[] {\n const stmt = this.db.prepare(`\n WITH RECURSIVE run_chain AS (\n -- Base case: find the root runs (those without parent)\n SELECT * FROM agent_runs \n WHERE thread_id = ? AND parent_run_id IS NULL\n \n UNION ALL\n \n -- Recursive case: find children of current level\n SELECT ar.* FROM agent_runs ar\n INNER JOIN run_chain rc ON ar.parent_run_id = rc.run_id\n WHERE ar.thread_id = ?\n )\n SELECT * FROM run_chain\n ORDER BY created_at ASC\n `);\n\n const rows = stmt.all(threadId, threadId) as any[];\n\n return rows.map((row) => ({\n id: row.id,\n thread_id: row.thread_id,\n run_id: row.run_id,\n parent_run_id: row.parent_run_id,\n events: JSON.parse(row.events),\n input: JSON.parse(row.input),\n created_at: row.created_at,\n version: row.version,\n }));\n }\n\n private getLatestRunId(threadId: string): string | null {\n const stmt = this.db.prepare(`\n SELECT run_id FROM agent_runs \n WHERE thread_id = ? \n ORDER BY created_at DESC \n LIMIT 1\n `);\n\n const result = stmt.get(threadId) as { run_id: string } | undefined;\n return result?.run_id ?? null;\n }\n\n private setRunState(\n threadId: string,\n isRunning: boolean,\n runId?: string,\n ): void {\n const stmt = this.db.prepare(`\n INSERT OR REPLACE INTO run_state (thread_id, is_running, current_run_id, updated_at)\n VALUES (?, ?, ?, ?)\n `);\n stmt.run(threadId, isRunning ? 1 : 0, runId ?? null, Date.now());\n }\n\n private getRunState(threadId: string): {\n isRunning: boolean;\n currentRunId: string | null;\n } {\n const stmt = this.db.prepare(`\n SELECT is_running, current_run_id FROM run_state WHERE thread_id = ?\n `);\n const result = stmt.get(threadId) as\n | { is_running: number; current_run_id: string | null }\n | undefined;\n\n return {\n isRunning: result?.is_running === 1,\n currentRunId: result?.current_run_id ?? null,\n };\n }\n\n run(request: AgentRunnerRunRequest): Observable<BaseEvent> {\n // Check if thread is already running in database\n const runState = this.getRunState(request.threadId);\n if (runState.isRunning) {\n throw new ThreadAlreadyRunningError();\n }\n\n // Mark thread as running in database\n this.setRunState(request.threadId, true, request.input.runId);\n\n // Track seen message IDs and current run events in memory for this run\n const seenMessageIds = new Set<string>();\n const currentRunEvents: BaseEvent[] = [];\n\n // Get all previously seen message IDs from historic runs\n const historicRuns = this.getHistoricRuns(request.threadId);\n const historicMessageIds = new Set<string>();\n for (const run of historicRuns) {\n for (const event of run.events) {\n if (\"messageId\" in event && typeof event.messageId === \"string\") {\n historicMessageIds.add(event.messageId);\n }\n if (event.type === EventType.RUN_STARTED) {\n const runStarted = event as RunStartedEvent;\n const messages = runStarted.input?.messages ?? [];\n for (const message of messages) {\n historicMessageIds.add(message.id);\n }\n }\n }\n }\n\n // Get or create subject for this thread's connections\n const nextSubject = new ReplaySubject<BaseEvent>(Infinity);\n const prevConnection = ACTIVE_CONNECTIONS.get(request.threadId);\n const prevSubject = prevConnection?.subject;\n\n // Create a subject for run() return value\n const runSubject = new ReplaySubject<BaseEvent>(Infinity);\n\n // Update the active connection for this thread\n ACTIVE_CONNECTIONS.set(request.threadId, {\n subject: nextSubject,\n agent: request.agent,\n runSubject,\n currentEvents: currentRunEvents,\n stopRequested: false,\n });\n\n // Helper function to run the agent and handle errors\n const runAgent = async () => {\n // Get parent run ID for chaining\n const parentRunId = this.getLatestRunId(request.threadId);\n\n const ensureStoppedRunStarted = (stopRequested: boolean) => {\n if (\n !stopRequested ||\n currentRunEvents.some((event) => event.type === EventType.RUN_STARTED)\n ) {\n return;\n }\n // Stop can arrive before the agent sends its first event.\n const started: RunStartedEvent = {\n type: EventType.RUN_STARTED,\n threadId: request.threadId,\n runId: request.input.runId,\n input: {\n ...request.input,\n messages: request.input.messages.filter(\n (message) => !historicMessageIds.has(message.id),\n ),\n },\n };\n currentRunEvents.unshift(started);\n runSubject.next(started);\n nextSubject.next(started);\n };\n\n try {\n await request.agent.runAgent(request.input, {\n onEvent: ({ event }) => {\n let processedEvent: BaseEvent = event;\n if (event.type === EventType.RUN_STARTED) {\n const runStartedEvent = event as RunStartedEvent;\n if (!runStartedEvent.input) {\n const sanitizedMessages = request.input.messages\n ? request.input.messages.filter(\n (message) => !historicMessageIds.has(message.id),\n )\n : undefined;\n const updatedInput = {\n ...request.input,\n ...(sanitizedMessages !== undefined\n ? { messages: sanitizedMessages }\n : {}),\n };\n processedEvent = {\n ...runStartedEvent,\n input: updatedInput,\n } as RunStartedEvent;\n }\n }\n\n runSubject.next(processedEvent); // For run() return - only agent events\n nextSubject.next(processedEvent); // For connect() / store - all events\n currentRunEvents.push(processedEvent); // Accumulate for database storage\n },\n onNewMessage: ({ message }) => {\n // Called for each new message\n if (!seenMessageIds.has(message.id)) {\n seenMessageIds.add(message.id);\n }\n },\n onRunStartedEvent: () => {\n // Mark input messages as seen without emitting duplicates\n if (request.input.messages) {\n for (const message of request.input.messages) {\n if (!seenMessageIds.has(message.id)) {\n seenMessageIds.add(message.id);\n }\n }\n }\n },\n });\n\n const connection = ACTIVE_CONNECTIONS.get(request.threadId);\n ensureStoppedRunStarted(connection?.stopRequested ?? false);\n const appendedEvents = finalizeRunEvents(currentRunEvents, {\n stopRequested: connection?.stopRequested ?? false,\n protocolVersion: request.input.protocolVersion,\n });\n for (const event of appendedEvents) {\n runSubject.next(event);\n nextSubject.next(event);\n }\n\n // Store the run in database\n this.storeRun(\n request.threadId,\n request.input.runId,\n currentRunEvents,\n request.input,\n parentRunId,\n );\n\n // Mark run as complete in database\n this.setRunState(request.threadId, false);\n\n if (connection) {\n connection.agent = undefined;\n connection.runSubject = undefined;\n connection.currentEvents = undefined;\n connection.stopRequested = false;\n }\n\n // Complete the subjects\n runSubject.complete();\n nextSubject.complete();\n\n ACTIVE_CONNECTIONS.delete(request.threadId);\n } catch {\n const connection = ACTIVE_CONNECTIONS.get(request.threadId);\n ensureStoppedRunStarted(connection?.stopRequested ?? false);\n const appendedEvents = finalizeRunEvents(currentRunEvents, {\n stopRequested: connection?.stopRequested ?? false,\n protocolVersion: request.input.protocolVersion,\n });\n for (const event of appendedEvents) {\n runSubject.next(event);\n nextSubject.next(event);\n }\n\n // Store the run even if it failed (partial events)\n if (currentRunEvents.length > 0) {\n this.storeRun(\n request.threadId,\n request.input.runId,\n currentRunEvents,\n request.input,\n parentRunId,\n );\n }\n\n // Mark run as complete in database\n this.setRunState(request.threadId, false);\n\n if (connection) {\n connection.agent = undefined;\n connection.runSubject = undefined;\n connection.currentEvents = undefined;\n connection.stopRequested = false;\n }\n\n // Don't emit error to the subject, just complete it\n // This allows subscribers to get events emitted before the error\n runSubject.complete();\n nextSubject.complete();\n\n ACTIVE_CONNECTIONS.delete(request.threadId);\n }\n };\n\n // Bridge previous events if they exist\n if (prevSubject) {\n prevSubject.subscribe({\n next: (e) => nextSubject.next(e),\n error: (err) => nextSubject.error(err),\n complete: () => {\n // Don't complete nextSubject here - it needs to stay open for new events\n },\n });\n }\n\n // Start the agent execution immediately (not lazily)\n runAgent();\n\n // Return the run subject (only agent events, no injected messages)\n return runSubject.asObservable();\n }\n\n connect(request: AgentRunnerConnectRequest): Observable<BaseEvent> {\n const connectionSubject = new ReplaySubject<BaseEvent>(Infinity);\n\n // Load historic runs from database\n const historicRuns = this.getHistoricRuns(request.threadId);\n\n // Collect all historic events from database\n const allHistoricEvents: BaseEvent[] = [];\n for (const run of historicRuns) {\n allHistoricEvents.push(...run.events);\n }\n\n // Compact all events together before emitting\n const compactedEvents = compactEvents(allHistoricEvents);\n\n // Emit compacted events and track message IDs\n const emittedMessageIds = new Set<string>();\n for (const event of compactedEvents) {\n connectionSubject.next(event);\n if (\"messageId\" in event && typeof event.messageId === \"string\") {\n emittedMessageIds.add(event.messageId);\n }\n }\n\n // Bridge active run to connection if exists\n const activeConnection = ACTIVE_CONNECTIONS.get(request.threadId);\n const runState = this.getRunState(request.threadId);\n\n if (\n activeConnection &&\n (runState.isRunning || activeConnection.stopRequested)\n ) {\n activeConnection.subject.subscribe({\n next: (event) => {\n // Skip message events that we've already emitted from historic\n if (\n \"messageId\" in event &&\n typeof event.messageId === \"string\" &&\n emittedMessageIds.has(event.messageId)\n ) {\n return;\n }\n connectionSubject.next(event);\n },\n complete: () => connectionSubject.complete(),\n error: (err) => connectionSubject.error(err),\n });\n } else {\n // No active run, complete after historic events\n connectionSubject.complete();\n }\n\n return connectionSubject.asObservable();\n }\n\n isRunning(request: AgentRunnerIsRunningRequest): Promise<boolean> {\n const runState = this.getRunState(request.threadId);\n return Promise.resolve(runState.isRunning);\n }\n\n stop(request: AgentRunnerStopRequest): Promise<boolean | undefined> {\n const runState = this.getRunState(request.threadId);\n if (!runState.isRunning) {\n return Promise.resolve(false);\n }\n\n const connection = ACTIVE_CONNECTIONS.get(request.threadId);\n const agent = connection?.agent;\n\n if (!connection || !agent) {\n return Promise.resolve(false);\n }\n\n if (connection.stopRequested) {\n return Promise.resolve(false);\n }\n\n connection.stopRequested = true;\n this.setRunState(request.threadId, false);\n\n try {\n agent.abortRun();\n return Promise.resolve(true);\n } catch (error) {\n console.error(\"Failed to abort sqlite agent run\", error);\n connection.stopRequested = false;\n this.setRunState(request.threadId, true);\n return Promise.resolve(false);\n }\n }\n\n /**\n * Close the database connection (for cleanup)\n */\n close(): void {\n if (this.db) {\n this.db.close();\n }\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"sqlite-runner.cjs","names":["AgentRunner","Database","ThreadAccessDeniedError","ThreadAlreadyRunningError","EventType","ReplaySubject"],"sources":["../src/sqlite-runner.ts"],"sourcesContent":["import {\n AgentRunner,\n ThreadAccessDeniedError,\n ThreadAlreadyRunningError,\n finalizeRunEvents,\n ɵresolveThreadOwner,\n} from \"@copilotkit/runtime\";\nimport type {\n AgentRunnerConnectRequest,\n AgentRunnerIsRunningRequest,\n AgentRunnerRunRequest,\n AgentRunnerStopRequest,\n LocalThreadEndpointScope,\n} from \"@copilotkit/runtime\";\nimport type { Observable } from \"rxjs\";\nimport { ReplaySubject } from \"rxjs\";\nimport type {\n AbstractAgent,\n BaseEvent,\n RunAgentInput,\n RunStartedEvent,\n} from \"@ag-ui/client\";\nimport { EventType, compactEvents } from \"@ag-ui/client\";\nimport Database from \"better-sqlite3\";\n\n/**\n * 2: adds `thread_owners`. Also written as the `version` of each stored run,\n * so runs stored from now on are tagged 2.\n */\nconst SCHEMA_VERSION = 2;\n\n/**\n * Owner of a legacy thread: one whose runs were stored before owners were\n * recorded (schema 1), so it has runs but no `thread_owners` row. Before the\n * upgrade, every caller that held a thread id could reach that thread. A\n * legacy thread keeps exactly that: it stays a capability, reachable by id for\n * every caller and every identity, and continuing it never claims it. Only a\n * thread that has no runs yet is claimed, by its first run, so no new thread\n * can become legacy. The value holds a NUL character, which no request\n * identity can hold.\n */\nconst LEGACY_THREAD_OWNER = \"\\u0000legacy\";\n\ninterface AgentRunRecord {\n id: number;\n thread_id: string;\n run_id: string;\n parent_run_id: string | null;\n events: BaseEvent[];\n input: RunAgentInput;\n created_at: number;\n version: number;\n}\n\nexport interface SqliteAgentRunnerOptions {\n dbPath?: string;\n}\n\ninterface ActiveConnectionContext {\n subject: ReplaySubject<BaseEvent>;\n agent?: AbstractAgent;\n runSubject?: ReplaySubject<BaseEvent>;\n currentEvents?: BaseEvent[];\n stopRequested?: boolean;\n}\n\nexport class SqliteAgentRunner extends AgentRunner {\n private db: any;\n\n /**\n * Live runs of this runner, by thread id, for streaming and stop. Kept per\n * instance: owners are checked against this instance's database, so a live\n * run of another runner, which may hold the same thread id for a different\n * owner, must never be bridged, stopped or torn down from here.\n */\n private readonly activeConnections = new Map<\n string,\n ActiveConnectionContext\n >();\n\n constructor(options: SqliteAgentRunnerOptions = {}) {\n super();\n const dbPath = options.dbPath ?? \":memory:\";\n\n if (!Database) {\n throw new Error(\n \"better-sqlite3 is required for SqliteAgentRunner but was not found.\\n\" +\n \"Please install it in your project:\\n\" +\n \" npm install better-sqlite3\\n\" +\n \" or\\n\" +\n \" pnpm add better-sqlite3\\n\" +\n \" or\\n\" +\n \" yarn add better-sqlite3\\n\\n\" +\n \"If you don't need persistence, use InMemoryAgentRunner instead.\",\n );\n }\n\n this.db = new Database(dbPath);\n this.initializeSchema();\n this.warnIfLegacyThreads();\n }\n\n /**\n * Logs once, when the database opens, how many legacy threads it holds\n * (see {@link LEGACY_THREAD_OWNER}), so operators know those threads are\n * still reachable by id for every caller.\n */\n private warnIfLegacyThreads(): void {\n const row = this.db\n .prepare(\n \"SELECT COUNT(DISTINCT thread_id) AS count FROM agent_runs WHERE thread_id NOT IN (SELECT thread_id FROM thread_owners)\",\n )\n .get() as { count: number } | undefined;\n const count = row?.count ?? 0;\n if (count === 0) return;\n console.warn(\n `[CopilotKit] SqliteAgentRunner: ${count} thread${count === 1 ? \" was\" : \"s were\"} ` +\n \"stored before thread owners were recorded. Anyone who holds the id of \" +\n \"one of them can still read, continue and stop it, as before the upgrade, \" +\n \"whatever identifyUser returns. Threads created from now on belong to the \" +\n \"identity whose run created them.\",\n );\n }\n\n private initializeSchema(): void {\n // Create the agent_runs table\n this.db.exec(`\n CREATE TABLE IF NOT EXISTS agent_runs (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n thread_id TEXT NOT NULL,\n run_id TEXT NOT NULL UNIQUE,\n parent_run_id TEXT,\n events TEXT NOT NULL,\n input TEXT NOT NULL,\n created_at INTEGER NOT NULL,\n version INTEGER NOT NULL\n )\n `);\n\n // Create run_state table to track active runs\n this.db.exec(`\n CREATE TABLE IF NOT EXISTS run_state (\n thread_id TEXT PRIMARY KEY,\n is_running INTEGER DEFAULT 0,\n current_run_id TEXT,\n updated_at INTEGER NOT NULL\n )\n `);\n\n // The identity that owns each thread, recorded by the thread's first run.\n this.db.exec(`\n CREATE TABLE IF NOT EXISTS thread_owners (\n thread_id TEXT PRIMARY KEY,\n owner_id TEXT NOT NULL,\n created_at INTEGER NOT NULL\n )\n `);\n\n // Create indexes for efficient queries\n this.db.exec(`\n CREATE INDEX IF NOT EXISTS idx_thread_id ON agent_runs(thread_id);\n CREATE INDEX IF NOT EXISTS idx_parent_run_id ON agent_runs(parent_run_id);\n `);\n\n // Create schema version table\n this.db.exec(`\n CREATE TABLE IF NOT EXISTS schema_version (\n version INTEGER PRIMARY KEY,\n applied_at INTEGER NOT NULL\n )\n `);\n\n // Check and set schema version\n const currentVersion = this.db\n .prepare(\n \"SELECT version FROM schema_version ORDER BY version DESC LIMIT 1\",\n )\n .get() as { version: number } | undefined;\n\n if (!currentVersion || currentVersion.version < SCHEMA_VERSION) {\n this.db\n .prepare(\n \"INSERT OR REPLACE INTO schema_version (version, applied_at) VALUES (?, ?)\",\n )\n .run(SCHEMA_VERSION, Date.now());\n }\n }\n\n private storeRun(\n threadId: string,\n runId: string,\n events: BaseEvent[],\n input: RunAgentInput,\n parentRunId?: string | null,\n ): void {\n // Compact ONLY the events from this run\n const compactedEvents = compactEvents(events);\n\n const stmt = this.db.prepare(`\n INSERT INTO agent_runs (thread_id, run_id, parent_run_id, events, input, created_at, version)\n VALUES (?, ?, ?, ?, ?, ?, ?)\n `);\n\n stmt.run(\n threadId,\n runId,\n parentRunId ?? null,\n JSON.stringify(compactedEvents), // Store only this run's compacted events\n JSON.stringify(input),\n Date.now(),\n SCHEMA_VERSION,\n );\n }\n\n private getHistoricRuns(threadId: string): AgentRunRecord[] {\n const stmt = this.db.prepare(`\n WITH RECURSIVE run_chain AS (\n -- Base case: find the root runs (those without parent)\n SELECT * FROM agent_runs \n WHERE thread_id = ? AND parent_run_id IS NULL\n \n UNION ALL\n \n -- Recursive case: find children of current level\n SELECT ar.* FROM agent_runs ar\n INNER JOIN run_chain rc ON ar.parent_run_id = rc.run_id\n WHERE ar.thread_id = ?\n )\n SELECT * FROM run_chain\n ORDER BY created_at ASC\n `);\n\n const rows = stmt.all(threadId, threadId) as any[];\n\n return rows.map((row) => ({\n id: row.id,\n thread_id: row.thread_id,\n run_id: row.run_id,\n parent_run_id: row.parent_run_id,\n events: JSON.parse(row.events),\n input: JSON.parse(row.input),\n created_at: row.created_at,\n version: row.version,\n }));\n }\n\n private getLatestRunId(threadId: string): string | null {\n const stmt = this.db.prepare(`\n SELECT run_id FROM agent_runs \n WHERE thread_id = ? \n ORDER BY created_at DESC \n LIMIT 1\n `);\n\n const result = stmt.get(threadId) as { run_id: string } | undefined;\n return result?.run_id ?? null;\n }\n\n private setRunState(\n threadId: string,\n isRunning: boolean,\n runId?: string,\n ): void {\n const stmt = this.db.prepare(`\n INSERT OR REPLACE INTO run_state (thread_id, is_running, current_run_id, updated_at)\n VALUES (?, ?, ?, ?)\n `);\n stmt.run(threadId, isRunning ? 1 : 0, runId ?? null, Date.now());\n }\n\n private getRunState(threadId: string): {\n isRunning: boolean;\n currentRunId: string | null;\n } {\n const stmt = this.db.prepare(`\n SELECT is_running, current_run_id FROM run_state WHERE thread_id = ?\n `);\n const result = stmt.get(threadId) as\n | { is_running: number; current_run_id: string | null }\n | undefined;\n\n return {\n isRunning: result?.is_running === 1,\n currentRunId: result?.current_run_id ?? null,\n };\n }\n\n /**\n * Owner of a thread: the recorded owner, {@link LEGACY_THREAD_OWNER} for a\n * thread whose runs predate owner records, or `undefined` for an unknown\n * thread.\n *\n * The owner record and the run check are read by one statement, so they\n * come from one snapshot of the database. With two reads, another runner on\n * the same file could claim the thread and store its first run in between,\n * and this call would see runs but no owner and take that thread for a\n * legacy one, open to every caller.\n */\n private getThreadOwner(threadId: string): string | undefined {\n const row = this.db\n .prepare(\n \"SELECT (SELECT owner_id FROM thread_owners WHERE thread_id = ?) AS owner_id, EXISTS (SELECT 1 FROM agent_runs WHERE thread_id = ?) AS has_runs\",\n )\n .get(threadId, threadId) as\n | { owner_id: string | null; has_runs: number }\n | undefined;\n if (row?.owner_id != null) return row.owner_id;\n return row?.has_runs ? LEGACY_THREAD_OWNER : undefined;\n }\n\n /**\n * True when a call acting as `ownerId` may reach an existing thread: its\n * owner, or anyone for a legacy thread.\n */\n private isOwnedBy(threadId: string, ownerId: string | undefined): boolean {\n const owner = this.getThreadOwner(threadId);\n return (\n owner === LEGACY_THREAD_OWNER || owner === ɵresolveThreadOwner(ownerId)\n );\n }\n\n /**\n * False when the thread is held by an owner other than `scope`'s. A legacy\n * thread is held by nobody, so it is accessible to every caller.\n */\n isThreadAccessible(\n threadId: string,\n scope: LocalThreadEndpointScope,\n ): boolean {\n const owner = this.getThreadOwner(threadId);\n return (\n owner === undefined ||\n owner === LEGACY_THREAD_OWNER ||\n owner === ɵresolveThreadOwner(scope.ownerId)\n );\n }\n\n /**\n * Records `owner` as the owner of a new thread, and returns true when the\n * run may go on as `owner`: `owner` holds the thread afterward, or the\n * thread is legacy, which stays unowned. The read, the insert and the\n * re-read run in one IMMEDIATE transaction, so when two runners or\n * processes on one database file claim a new thread at once, exactly one\n * wins and the other reads the winner's row.\n */\n private claimThread(threadId: string, owner: string): boolean {\n return this.db\n .transaction(() => {\n const current = this.getThreadOwner(threadId);\n if (current === LEGACY_THREAD_OWNER) return true;\n if (current === undefined) {\n this.db\n .prepare(\n \"INSERT INTO thread_owners (thread_id, owner_id, created_at) VALUES (?, ?, ?) ON CONFLICT(thread_id) DO NOTHING\",\n )\n .run(threadId, owner, Date.now());\n }\n return this.getThreadOwner(threadId) === owner;\n })\n .immediate();\n }\n\n run(request: AgentRunnerRunRequest): Observable<BaseEvent> {\n // A thread belongs to the identity of its first run.\n const owner = ɵresolveThreadOwner(request.ownerId);\n if (!this.isThreadAccessible(request.threadId, { ownerId: owner })) {\n throw new ThreadAccessDeniedError();\n }\n\n // Check if thread is already running in database\n const runState = this.getRunState(request.threadId);\n if (runState.isRunning) {\n throw new ThreadAlreadyRunningError();\n }\n\n // The check above is per process. Another process on the same database\n // file can claim the thread in between, so the claim itself decides.\n if (!this.claimThread(request.threadId, owner)) {\n throw new ThreadAccessDeniedError();\n }\n\n // Mark thread as running in database\n this.setRunState(request.threadId, true, request.input.runId);\n\n // Track seen message IDs and current run events in memory for this run\n const seenMessageIds = new Set<string>();\n const currentRunEvents: BaseEvent[] = [];\n\n // Get all previously seen message IDs from historic runs\n const historicRuns = this.getHistoricRuns(request.threadId);\n const historicMessageIds = new Set<string>();\n for (const run of historicRuns) {\n for (const event of run.events) {\n if (\"messageId\" in event && typeof event.messageId === \"string\") {\n historicMessageIds.add(event.messageId);\n }\n if (event.type === EventType.RUN_STARTED) {\n const runStarted = event as RunStartedEvent;\n const messages = runStarted.input?.messages ?? [];\n for (const message of messages) {\n historicMessageIds.add(message.id);\n }\n }\n }\n }\n\n // Get or create subject for this thread's connections\n const nextSubject = new ReplaySubject<BaseEvent>(Infinity);\n const prevConnection = this.activeConnections.get(request.threadId);\n const prevSubject = prevConnection?.subject;\n\n // Create a subject for run() return value\n const runSubject = new ReplaySubject<BaseEvent>(Infinity);\n\n // Update the active connection for this thread\n const runConnection: ActiveConnectionContext = {\n subject: nextSubject,\n agent: request.agent,\n runSubject,\n currentEvents: currentRunEvents,\n stopRequested: false,\n };\n this.activeConnections.set(request.threadId, runConnection);\n\n // Helper function to run the agent and handle errors\n const runAgent = async () => {\n // Get parent run ID for chaining\n const parentRunId = this.getLatestRunId(request.threadId);\n\n const ensureStoppedRunStarted = (stopRequested: boolean) => {\n if (\n !stopRequested ||\n currentRunEvents.some((event) => event.type === EventType.RUN_STARTED)\n ) {\n return;\n }\n // Stop can arrive before the agent sends its first event.\n const started: RunStartedEvent = {\n type: EventType.RUN_STARTED,\n threadId: request.threadId,\n runId: request.input.runId,\n input: {\n ...request.input,\n messages: request.input.messages.filter(\n (message) => !historicMessageIds.has(message.id),\n ),\n },\n };\n currentRunEvents.unshift(started);\n runSubject.next(started);\n nextSubject.next(started);\n };\n\n try {\n await request.agent.runAgent(request.input, {\n onEvent: ({ event }) => {\n let processedEvent: BaseEvent = event;\n if (event.type === EventType.RUN_STARTED) {\n const runStartedEvent = event as RunStartedEvent;\n if (!runStartedEvent.input) {\n const sanitizedMessages = request.input.messages\n ? request.input.messages.filter(\n (message) => !historicMessageIds.has(message.id),\n )\n : undefined;\n const updatedInput = {\n ...request.input,\n ...(sanitizedMessages !== undefined\n ? { messages: sanitizedMessages }\n : {}),\n };\n processedEvent = {\n ...runStartedEvent,\n input: updatedInput,\n } as RunStartedEvent;\n }\n }\n\n runSubject.next(processedEvent); // For run() return - only agent events\n nextSubject.next(processedEvent); // For connect() / store - all events\n currentRunEvents.push(processedEvent); // Accumulate for database storage\n },\n onNewMessage: ({ message }) => {\n // Called for each new message\n if (!seenMessageIds.has(message.id)) {\n seenMessageIds.add(message.id);\n }\n },\n onRunStartedEvent: () => {\n // Mark input messages as seen without emitting duplicates\n if (request.input.messages) {\n for (const message of request.input.messages) {\n if (!seenMessageIds.has(message.id)) {\n seenMessageIds.add(message.id);\n }\n }\n }\n },\n });\n\n const connection = this.activeConnections.get(request.threadId);\n ensureStoppedRunStarted(connection?.stopRequested ?? false);\n const appendedEvents = finalizeRunEvents(currentRunEvents, {\n stopRequested: connection?.stopRequested ?? false,\n protocolVersion: request.input.protocolVersion,\n });\n for (const event of appendedEvents) {\n runSubject.next(event);\n nextSubject.next(event);\n }\n\n // Store the run in database\n this.storeRun(\n request.threadId,\n request.input.runId,\n currentRunEvents,\n request.input,\n parentRunId,\n );\n\n // Mark run as complete in database\n this.setRunState(request.threadId, false);\n\n if (connection) {\n connection.agent = undefined;\n connection.runSubject = undefined;\n connection.currentEvents = undefined;\n connection.stopRequested = false;\n }\n\n // Complete the subjects\n runSubject.complete();\n nextSubject.complete();\n\n this.activeConnections.delete(request.threadId);\n } catch (error) {\n const connection = this.activeConnections.get(request.threadId);\n if (!connection?.stopRequested) {\n console.error(\n \"Sqlite agent run failed\",\n { threadId: request.threadId, runId: request.input.runId },\n error,\n );\n }\n ensureStoppedRunStarted(connection?.stopRequested ?? false);\n const appendedEvents = finalizeRunEvents(currentRunEvents, {\n stopRequested: connection?.stopRequested ?? false,\n protocolVersion: request.input.protocolVersion,\n });\n for (const event of appendedEvents) {\n runSubject.next(event);\n nextSubject.next(event);\n }\n\n // Store the run even if it failed (partial events)\n if (currentRunEvents.length > 0) {\n this.storeRun(\n request.threadId,\n request.input.runId,\n currentRunEvents,\n request.input,\n parentRunId,\n );\n }\n\n // Mark run as complete in database\n this.setRunState(request.threadId, false);\n\n if (connection) {\n connection.agent = undefined;\n connection.runSubject = undefined;\n connection.currentEvents = undefined;\n connection.stopRequested = false;\n }\n\n // Don't emit error to the subject, just complete it\n // This allows subscribers to get events emitted before the error\n runSubject.complete();\n nextSubject.complete();\n\n this.activeConnections.delete(request.threadId);\n }\n };\n\n // Bridge previous events if they exist\n if (prevSubject) {\n prevSubject.subscribe({\n next: (e) => nextSubject.next(e),\n error: (err) => nextSubject.error(err),\n complete: () => {\n // Don't complete nextSubject here - it needs to stay open for new events\n },\n });\n }\n\n // Start the agent execution immediately (not lazily). A failure while\n // storing or finishing the run must not leave the thread marked running\n // in the database, where it would refuse every later run, even after a\n // restart.\n runAgent().catch((error) => {\n console.error(\n \"Failed to finish sqlite agent run\",\n { threadId: request.threadId, runId: request.input.runId },\n error,\n );\n try {\n const state = this.getRunState(request.threadId);\n if (\n state.currentRunId === null ||\n state.currentRunId === request.input.runId\n ) {\n this.setRunState(request.threadId, false);\n }\n } catch (stateError) {\n console.error(\"Failed to reset sqlite run state\", stateError);\n }\n if (this.activeConnections.get(request.threadId) === runConnection) {\n this.activeConnections.delete(request.threadId);\n }\n runSubject.complete();\n nextSubject.complete();\n });\n\n // Return the run subject (only agent events, no injected messages)\n return runSubject.asObservable();\n }\n\n connect(request: AgentRunnerConnectRequest): Observable<BaseEvent> {\n const connectionSubject = new ReplaySubject<BaseEvent>(Infinity);\n\n // A thread owned by another identity replays like an unknown one.\n if (!this.isOwnedBy(request.threadId, request.ownerId)) {\n connectionSubject.complete();\n return connectionSubject.asObservable();\n }\n\n // Load historic runs from database\n const historicRuns = this.getHistoricRuns(request.threadId);\n\n // Collect all historic events from database\n const allHistoricEvents: BaseEvent[] = [];\n for (const run of historicRuns) {\n allHistoricEvents.push(...run.events);\n }\n\n // Compact all events together before emitting\n const compactedEvents = compactEvents(allHistoricEvents);\n\n // Emit compacted events and track message IDs\n const emittedMessageIds = new Set<string>();\n for (const event of compactedEvents) {\n connectionSubject.next(event);\n if (\"messageId\" in event && typeof event.messageId === \"string\") {\n emittedMessageIds.add(event.messageId);\n }\n }\n\n // Bridge active run to connection if exists\n const activeConnection = this.activeConnections.get(request.threadId);\n const runState = this.getRunState(request.threadId);\n\n if (\n activeConnection &&\n (runState.isRunning || activeConnection.stopRequested)\n ) {\n activeConnection.subject.subscribe({\n next: (event) => {\n // Skip message events that we've already emitted from historic\n if (\n \"messageId\" in event &&\n typeof event.messageId === \"string\" &&\n emittedMessageIds.has(event.messageId)\n ) {\n return;\n }\n connectionSubject.next(event);\n },\n complete: () => connectionSubject.complete(),\n error: (err) => connectionSubject.error(err),\n });\n } else {\n // No active run, complete after historic events\n connectionSubject.complete();\n }\n\n return connectionSubject.asObservable();\n }\n\n isRunning(request: AgentRunnerIsRunningRequest): Promise<boolean> {\n // Another identity's thread reports \"not running\".\n if (!this.isOwnedBy(request.threadId, request.ownerId)) {\n return Promise.resolve(false);\n }\n const runState = this.getRunState(request.threadId);\n return Promise.resolve(runState.isRunning);\n }\n\n stop(request: AgentRunnerStopRequest): Promise<boolean | undefined> {\n const runState = this.getRunState(request.threadId);\n if (!runState.isRunning) {\n return Promise.resolve(false);\n }\n // A run on another identity's thread reports \"no active run\".\n if (!this.isOwnedBy(request.threadId, request.ownerId)) {\n return Promise.resolve(false);\n }\n // A run-scoped stop never stops a newer run on the thread.\n if (\n request.runId !== undefined &&\n runState.currentRunId !== request.runId\n ) {\n return Promise.resolve(false);\n }\n\n const connection = this.activeConnections.get(request.threadId);\n const agent = connection?.agent;\n\n if (!connection || !agent) {\n return Promise.resolve(false);\n }\n\n if (connection.stopRequested) {\n return Promise.resolve(false);\n }\n\n connection.stopRequested = true;\n this.setRunState(request.threadId, false);\n\n try {\n agent.abortRun();\n return Promise.resolve(true);\n } catch (error) {\n console.error(\"Failed to abort sqlite agent run\", error);\n connection.stopRequested = false;\n // Restore the run's id too, or a later stop by run id would refuse it.\n this.setRunState(\n request.threadId,\n true,\n runState.currentRunId ?? undefined,\n );\n return Promise.resolve(false);\n }\n }\n\n /**\n * Close the database connection (for cleanup)\n */\n close(): void {\n if (this.db) {\n this.db.close();\n }\n }\n}\n"],"mappings":";;;;;;;;;;;;AA6BA,MAAM,iBAAiB;;;;;;;;;;;AAYvB,MAAM,sBAAsB;AAyB5B,IAAa,oBAAb,cAAuCA,gCAAY;CACjD,AAAQ;;;;;;;CAQR,AAAiB,oCAAoB,IAAI,KAGtC;CAEH,YAAY,UAAoC,EAAE,EAAE;AAClD,SAAO;EACP,MAAM,SAAS,QAAQ,UAAU;AAEjC,MAAI,CAACC,uBACH,OAAM,IAAI,MACR,6QAQD;AAGH,OAAK,KAAK,IAAIA,uBAAS,OAAO;AAC9B,OAAK,kBAAkB;AACvB,OAAK,qBAAqB;;;;;;;CAQ5B,AAAQ,sBAA4B;EAMlC,MAAM,QALM,KAAK,GACd,QACC,yHACD,CACA,KAAK,EACW,SAAS;AAC5B,MAAI,UAAU,EAAG;AACjB,UAAQ,KACN,mCAAmC,MAAM,SAAS,UAAU,IAAI,SAAS,SAAS,2PAKnF;;CAGH,AAAQ,mBAAyB;AAE/B,OAAK,GAAG,KAAK;;;;;;;;;;;MAWX;AAGF,OAAK,GAAG,KAAK;;;;;;;MAOX;AAGF,OAAK,GAAG,KAAK;;;;;;MAMX;AAGF,OAAK,GAAG,KAAK;;;MAGX;AAGF,OAAK,GAAG,KAAK;;;;;MAKX;EAGF,MAAM,iBAAiB,KAAK,GACzB,QACC,mEACD,CACA,KAAK;AAER,MAAI,CAAC,kBAAkB,eAAe,UAAU,eAC9C,MAAK,GACF,QACC,4EACD,CACA,IAAI,gBAAgB,KAAK,KAAK,CAAC;;CAItC,AAAQ,SACN,UACA,OACA,QACA,OACA,aACM;EAEN,MAAM,mDAAgC,OAAO;AAO7C,EALa,KAAK,GAAG,QAAQ;;;MAG3B,CAEG,IACH,UACA,OACA,eAAe,MACf,KAAK,UAAU,gBAAgB,EAC/B,KAAK,UAAU,MAAM,EACrB,KAAK,KAAK,EACV,eACD;;CAGH,AAAQ,gBAAgB,UAAoC;AAoB1D,SAnBa,KAAK,GAAG,QAAQ;;;;;;;;;;;;;;;MAe3B,CAEgB,IAAI,UAAU,SAAS,CAE7B,KAAK,SAAS;GACxB,IAAI,IAAI;GACR,WAAW,IAAI;GACf,QAAQ,IAAI;GACZ,eAAe,IAAI;GACnB,QAAQ,KAAK,MAAM,IAAI,OAAO;GAC9B,OAAO,KAAK,MAAM,IAAI,MAAM;GAC5B,YAAY,IAAI;GAChB,SAAS,IAAI;GACd,EAAE;;CAGL,AAAQ,eAAe,UAAiC;AAStD,SARa,KAAK,GAAG,QAAQ;;;;;MAK3B,CAEkB,IAAI,SAAS,EAClB,UAAU;;CAG3B,AAAQ,YACN,UACA,WACA,OACM;AAKN,EAJa,KAAK,GAAG,QAAQ;;;MAG3B,CACG,IAAI,UAAU,YAAY,IAAI,GAAG,SAAS,MAAM,KAAK,KAAK,CAAC;;CAGlE,AAAQ,YAAY,UAGlB;EAIA,MAAM,SAHO,KAAK,GAAG,QAAQ;;MAE3B,CACkB,IAAI,SAAS;AAIjC,SAAO;GACL,WAAW,QAAQ,eAAe;GAClC,cAAc,QAAQ,kBAAkB;GACzC;;;;;;;;;;;;;CAcH,AAAQ,eAAe,UAAsC;EAC3D,MAAM,MAAM,KAAK,GACd,QACC,iJACD,CACA,IAAI,UAAU,SAAS;AAG1B,MAAI,KAAK,YAAY,KAAM,QAAO,IAAI;AACtC,SAAO,KAAK,WAAW,sBAAsB;;;;;;CAO/C,AAAQ,UAAU,UAAkB,SAAsC;EACxE,MAAM,QAAQ,KAAK,eAAe,SAAS;AAC3C,SACE,UAAU,uBAAuB,uDAA8B,QAAQ;;;;;;CAQ3E,mBACE,UACA,OACS;EACT,MAAM,QAAQ,KAAK,eAAe,SAAS;AAC3C,SACE,UAAU,UACV,UAAU,uBACV,uDAA8B,MAAM,QAAQ;;;;;;;;;;CAYhD,AAAQ,YAAY,UAAkB,OAAwB;AAC5D,SAAO,KAAK,GACT,kBAAkB;GACjB,MAAM,UAAU,KAAK,eAAe,SAAS;AAC7C,OAAI,YAAY,oBAAqB,QAAO;AAC5C,OAAI,YAAY,OACd,MAAK,GACF,QACC,iHACD,CACA,IAAI,UAAU,OAAO,KAAK,KAAK,CAAC;AAErC,UAAO,KAAK,eAAe,SAAS,KAAK;IACzC,CACD,WAAW;;CAGhB,IAAI,SAAuD;EAEzD,MAAM,qDAA4B,QAAQ,QAAQ;AAClD,MAAI,CAAC,KAAK,mBAAmB,QAAQ,UAAU,EAAE,SAAS,OAAO,CAAC,CAChE,OAAM,IAAIC,6CAAyB;AAKrC,MADiB,KAAK,YAAY,QAAQ,SAAS,CACtC,UACX,OAAM,IAAIC,+CAA2B;AAKvC,MAAI,CAAC,KAAK,YAAY,QAAQ,UAAU,MAAM,CAC5C,OAAM,IAAID,6CAAyB;AAIrC,OAAK,YAAY,QAAQ,UAAU,MAAM,QAAQ,MAAM,MAAM;EAG7D,MAAM,iCAAiB,IAAI,KAAa;EACxC,MAAM,mBAAgC,EAAE;EAGxC,MAAM,eAAe,KAAK,gBAAgB,QAAQ,SAAS;EAC3D,MAAM,qCAAqB,IAAI,KAAa;AAC5C,OAAK,MAAM,OAAO,aAChB,MAAK,MAAM,SAAS,IAAI,QAAQ;AAC9B,OAAI,eAAe,SAAS,OAAO,MAAM,cAAc,SACrD,oBAAmB,IAAI,MAAM,UAAU;AAEzC,OAAI,MAAM,SAASE,wBAAU,aAAa;IAExC,MAAM,WADa,MACS,OAAO,YAAY,EAAE;AACjD,SAAK,MAAM,WAAW,SACpB,oBAAmB,IAAI,QAAQ,GAAG;;;EAO1C,MAAM,cAAc,IAAIC,mBAAyB,SAAS;EAE1D,MAAM,cADiB,KAAK,kBAAkB,IAAI,QAAQ,SAAS,EAC/B;EAGpC,MAAM,aAAa,IAAIA,mBAAyB,SAAS;EAGzD,MAAM,gBAAyC;GAC7C,SAAS;GACT,OAAO,QAAQ;GACf;GACA,eAAe;GACf,eAAe;GAChB;AACD,OAAK,kBAAkB,IAAI,QAAQ,UAAU,cAAc;EAG3D,MAAM,WAAW,YAAY;GAE3B,MAAM,cAAc,KAAK,eAAe,QAAQ,SAAS;GAEzD,MAAM,2BAA2B,kBAA2B;AAC1D,QACE,CAAC,iBACD,iBAAiB,MAAM,UAAU,MAAM,SAASD,wBAAU,YAAY,CAEtE;IAGF,MAAM,UAA2B;KAC/B,MAAMA,wBAAU;KAChB,UAAU,QAAQ;KAClB,OAAO,QAAQ,MAAM;KACrB,OAAO;MACL,GAAG,QAAQ;MACX,UAAU,QAAQ,MAAM,SAAS,QAC9B,YAAY,CAAC,mBAAmB,IAAI,QAAQ,GAAG,CACjD;MACF;KACF;AACD,qBAAiB,QAAQ,QAAQ;AACjC,eAAW,KAAK,QAAQ;AACxB,gBAAY,KAAK,QAAQ;;AAG3B,OAAI;AACF,UAAM,QAAQ,MAAM,SAAS,QAAQ,OAAO;KAC1C,UAAU,EAAE,YAAY;MACtB,IAAI,iBAA4B;AAChC,UAAI,MAAM,SAASA,wBAAU,aAAa;OACxC,MAAM,kBAAkB;AACxB,WAAI,CAAC,gBAAgB,OAAO;QAC1B,MAAM,oBAAoB,QAAQ,MAAM,WACpC,QAAQ,MAAM,SAAS,QACpB,YAAY,CAAC,mBAAmB,IAAI,QAAQ,GAAG,CACjD,GACD;QACJ,MAAM,eAAe;SACnB,GAAG,QAAQ;SACX,GAAI,sBAAsB,SACtB,EAAE,UAAU,mBAAmB,GAC/B,EAAE;SACP;AACD,yBAAiB;SACf,GAAG;SACH,OAAO;SACR;;;AAIL,iBAAW,KAAK,eAAe;AAC/B,kBAAY,KAAK,eAAe;AAChC,uBAAiB,KAAK,eAAe;;KAEvC,eAAe,EAAE,cAAc;AAE7B,UAAI,CAAC,eAAe,IAAI,QAAQ,GAAG,CACjC,gBAAe,IAAI,QAAQ,GAAG;;KAGlC,yBAAyB;AAEvB,UAAI,QAAQ,MAAM,UAChB;YAAK,MAAM,WAAW,QAAQ,MAAM,SAClC,KAAI,CAAC,eAAe,IAAI,QAAQ,GAAG,CACjC,gBAAe,IAAI,QAAQ,GAAG;;;KAKvC,CAAC;IAEF,MAAM,aAAa,KAAK,kBAAkB,IAAI,QAAQ,SAAS;AAC/D,4BAAwB,YAAY,iBAAiB,MAAM;IAC3D,MAAM,4DAAmC,kBAAkB;KACzD,eAAe,YAAY,iBAAiB;KAC5C,iBAAiB,QAAQ,MAAM;KAChC,CAAC;AACF,SAAK,MAAM,SAAS,gBAAgB;AAClC,gBAAW,KAAK,MAAM;AACtB,iBAAY,KAAK,MAAM;;AAIzB,SAAK,SACH,QAAQ,UACR,QAAQ,MAAM,OACd,kBACA,QAAQ,OACR,YACD;AAGD,SAAK,YAAY,QAAQ,UAAU,MAAM;AAEzC,QAAI,YAAY;AACd,gBAAW,QAAQ;AACnB,gBAAW,aAAa;AACxB,gBAAW,gBAAgB;AAC3B,gBAAW,gBAAgB;;AAI7B,eAAW,UAAU;AACrB,gBAAY,UAAU;AAEtB,SAAK,kBAAkB,OAAO,QAAQ,SAAS;YACxC,OAAO;IACd,MAAM,aAAa,KAAK,kBAAkB,IAAI,QAAQ,SAAS;AAC/D,QAAI,CAAC,YAAY,cACf,SAAQ,MACN,2BACA;KAAE,UAAU,QAAQ;KAAU,OAAO,QAAQ,MAAM;KAAO,EAC1D,MACD;AAEH,4BAAwB,YAAY,iBAAiB,MAAM;IAC3D,MAAM,4DAAmC,kBAAkB;KACzD,eAAe,YAAY,iBAAiB;KAC5C,iBAAiB,QAAQ,MAAM;KAChC,CAAC;AACF,SAAK,MAAM,SAAS,gBAAgB;AAClC,gBAAW,KAAK,MAAM;AACtB,iBAAY,KAAK,MAAM;;AAIzB,QAAI,iBAAiB,SAAS,EAC5B,MAAK,SACH,QAAQ,UACR,QAAQ,MAAM,OACd,kBACA,QAAQ,OACR,YACD;AAIH,SAAK,YAAY,QAAQ,UAAU,MAAM;AAEzC,QAAI,YAAY;AACd,gBAAW,QAAQ;AACnB,gBAAW,aAAa;AACxB,gBAAW,gBAAgB;AAC3B,gBAAW,gBAAgB;;AAK7B,eAAW,UAAU;AACrB,gBAAY,UAAU;AAEtB,SAAK,kBAAkB,OAAO,QAAQ,SAAS;;;AAKnD,MAAI,YACF,aAAY,UAAU;GACpB,OAAO,MAAM,YAAY,KAAK,EAAE;GAChC,QAAQ,QAAQ,YAAY,MAAM,IAAI;GACtC,gBAAgB;GAGjB,CAAC;AAOJ,YAAU,CAAC,OAAO,UAAU;AAC1B,WAAQ,MACN,qCACA;IAAE,UAAU,QAAQ;IAAU,OAAO,QAAQ,MAAM;IAAO,EAC1D,MACD;AACD,OAAI;IACF,MAAM,QAAQ,KAAK,YAAY,QAAQ,SAAS;AAChD,QACE,MAAM,iBAAiB,QACvB,MAAM,iBAAiB,QAAQ,MAAM,MAErC,MAAK,YAAY,QAAQ,UAAU,MAAM;YAEpC,YAAY;AACnB,YAAQ,MAAM,oCAAoC,WAAW;;AAE/D,OAAI,KAAK,kBAAkB,IAAI,QAAQ,SAAS,KAAK,cACnD,MAAK,kBAAkB,OAAO,QAAQ,SAAS;AAEjD,cAAW,UAAU;AACrB,eAAY,UAAU;IACtB;AAGF,SAAO,WAAW,cAAc;;CAGlC,QAAQ,SAA2D;EACjE,MAAM,oBAAoB,IAAIC,mBAAyB,SAAS;AAGhE,MAAI,CAAC,KAAK,UAAU,QAAQ,UAAU,QAAQ,QAAQ,EAAE;AACtD,qBAAkB,UAAU;AAC5B,UAAO,kBAAkB,cAAc;;EAIzC,MAAM,eAAe,KAAK,gBAAgB,QAAQ,SAAS;EAG3D,MAAM,oBAAiC,EAAE;AACzC,OAAK,MAAM,OAAO,aAChB,mBAAkB,KAAK,GAAG,IAAI,OAAO;EAIvC,MAAM,mDAAgC,kBAAkB;EAGxD,MAAM,oCAAoB,IAAI,KAAa;AAC3C,OAAK,MAAM,SAAS,iBAAiB;AACnC,qBAAkB,KAAK,MAAM;AAC7B,OAAI,eAAe,SAAS,OAAO,MAAM,cAAc,SACrD,mBAAkB,IAAI,MAAM,UAAU;;EAK1C,MAAM,mBAAmB,KAAK,kBAAkB,IAAI,QAAQ,SAAS;EACrE,MAAM,WAAW,KAAK,YAAY,QAAQ,SAAS;AAEnD,MACE,qBACC,SAAS,aAAa,iBAAiB,eAExC,kBAAiB,QAAQ,UAAU;GACjC,OAAO,UAAU;AAEf,QACE,eAAe,SACf,OAAO,MAAM,cAAc,YAC3B,kBAAkB,IAAI,MAAM,UAAU,CAEtC;AAEF,sBAAkB,KAAK,MAAM;;GAE/B,gBAAgB,kBAAkB,UAAU;GAC5C,QAAQ,QAAQ,kBAAkB,MAAM,IAAI;GAC7C,CAAC;MAGF,mBAAkB,UAAU;AAG9B,SAAO,kBAAkB,cAAc;;CAGzC,UAAU,SAAwD;AAEhE,MAAI,CAAC,KAAK,UAAU,QAAQ,UAAU,QAAQ,QAAQ,CACpD,QAAO,QAAQ,QAAQ,MAAM;EAE/B,MAAM,WAAW,KAAK,YAAY,QAAQ,SAAS;AACnD,SAAO,QAAQ,QAAQ,SAAS,UAAU;;CAG5C,KAAK,SAA+D;EAClE,MAAM,WAAW,KAAK,YAAY,QAAQ,SAAS;AACnD,MAAI,CAAC,SAAS,UACZ,QAAO,QAAQ,QAAQ,MAAM;AAG/B,MAAI,CAAC,KAAK,UAAU,QAAQ,UAAU,QAAQ,QAAQ,CACpD,QAAO,QAAQ,QAAQ,MAAM;AAG/B,MACE,QAAQ,UAAU,UAClB,SAAS,iBAAiB,QAAQ,MAElC,QAAO,QAAQ,QAAQ,MAAM;EAG/B,MAAM,aAAa,KAAK,kBAAkB,IAAI,QAAQ,SAAS;EAC/D,MAAM,QAAQ,YAAY;AAE1B,MAAI,CAAC,cAAc,CAAC,MAClB,QAAO,QAAQ,QAAQ,MAAM;AAG/B,MAAI,WAAW,cACb,QAAO,QAAQ,QAAQ,MAAM;AAG/B,aAAW,gBAAgB;AAC3B,OAAK,YAAY,QAAQ,UAAU,MAAM;AAEzC,MAAI;AACF,SAAM,UAAU;AAChB,UAAO,QAAQ,QAAQ,KAAK;WACrB,OAAO;AACd,WAAQ,MAAM,oCAAoC,MAAM;AACxD,cAAW,gBAAgB;AAE3B,QAAK,YACH,QAAQ,UACR,MACA,SAAS,gBAAgB,OAC1B;AACD,UAAO,QAAQ,QAAQ,MAAM;;;;;;CAOjC,QAAc;AACZ,MAAI,KAAK,GACP,MAAK,GAAG,OAAO"}
|
package/dist/sqlite-runner.d.cts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AgentRunner, AgentRunnerConnectRequest, AgentRunnerIsRunningRequest, AgentRunnerRunRequest, AgentRunnerStopRequest } from "@copilotkit/runtime
|
|
1
|
+
import { AgentRunner, AgentRunnerConnectRequest, AgentRunnerIsRunningRequest, AgentRunnerRunRequest, AgentRunnerStopRequest, LocalThreadEndpointScope } from "@copilotkit/runtime";
|
|
2
2
|
import { Observable } from "rxjs";
|
|
3
3
|
import { BaseEvent } from "@ag-ui/client";
|
|
4
4
|
|
|
@@ -8,13 +8,57 @@ interface SqliteAgentRunnerOptions {
|
|
|
8
8
|
}
|
|
9
9
|
declare class SqliteAgentRunner extends AgentRunner {
|
|
10
10
|
private db;
|
|
11
|
+
/**
|
|
12
|
+
* Live runs of this runner, by thread id, for streaming and stop. Kept per
|
|
13
|
+
* instance: owners are checked against this instance's database, so a live
|
|
14
|
+
* run of another runner, which may hold the same thread id for a different
|
|
15
|
+
* owner, must never be bridged, stopped or torn down from here.
|
|
16
|
+
*/
|
|
17
|
+
private readonly activeConnections;
|
|
11
18
|
constructor(options?: SqliteAgentRunnerOptions);
|
|
19
|
+
/**
|
|
20
|
+
* Logs once, when the database opens, how many legacy threads it holds
|
|
21
|
+
* (see {@link LEGACY_THREAD_OWNER}), so operators know those threads are
|
|
22
|
+
* still reachable by id for every caller.
|
|
23
|
+
*/
|
|
24
|
+
private warnIfLegacyThreads;
|
|
12
25
|
private initializeSchema;
|
|
13
26
|
private storeRun;
|
|
14
27
|
private getHistoricRuns;
|
|
15
28
|
private getLatestRunId;
|
|
16
29
|
private setRunState;
|
|
17
30
|
private getRunState;
|
|
31
|
+
/**
|
|
32
|
+
* Owner of a thread: the recorded owner, {@link LEGACY_THREAD_OWNER} for a
|
|
33
|
+
* thread whose runs predate owner records, or `undefined` for an unknown
|
|
34
|
+
* thread.
|
|
35
|
+
*
|
|
36
|
+
* The owner record and the run check are read by one statement, so they
|
|
37
|
+
* come from one snapshot of the database. With two reads, another runner on
|
|
38
|
+
* the same file could claim the thread and store its first run in between,
|
|
39
|
+
* and this call would see runs but no owner and take that thread for a
|
|
40
|
+
* legacy one, open to every caller.
|
|
41
|
+
*/
|
|
42
|
+
private getThreadOwner;
|
|
43
|
+
/**
|
|
44
|
+
* True when a call acting as `ownerId` may reach an existing thread: its
|
|
45
|
+
* owner, or anyone for a legacy thread.
|
|
46
|
+
*/
|
|
47
|
+
private isOwnedBy;
|
|
48
|
+
/**
|
|
49
|
+
* False when the thread is held by an owner other than `scope`'s. A legacy
|
|
50
|
+
* thread is held by nobody, so it is accessible to every caller.
|
|
51
|
+
*/
|
|
52
|
+
isThreadAccessible(threadId: string, scope: LocalThreadEndpointScope): boolean;
|
|
53
|
+
/**
|
|
54
|
+
* Records `owner` as the owner of a new thread, and returns true when the
|
|
55
|
+
* run may go on as `owner`: `owner` holds the thread afterward, or the
|
|
56
|
+
* thread is legacy, which stays unowned. The read, the insert and the
|
|
57
|
+
* re-read run in one IMMEDIATE transaction, so when two runners or
|
|
58
|
+
* processes on one database file claim a new thread at once, exactly one
|
|
59
|
+
* wins and the other reads the winner's row.
|
|
60
|
+
*/
|
|
61
|
+
private claimThread;
|
|
18
62
|
run(request: AgentRunnerRunRequest): Observable<BaseEvent>;
|
|
19
63
|
connect(request: AgentRunnerConnectRequest): Observable<BaseEvent>;
|
|
20
64
|
isRunning(request: AgentRunnerIsRunningRequest): Promise<boolean>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sqlite-runner.d.cts","names":[],"sources":["../src/sqlite-runner.ts"],"mappings":";;;;;
|
|
1
|
+
{"version":3,"file":"sqlite-runner.d.cts","names":[],"sources":["../src/sqlite-runner.ts"],"mappings":";;;;;UAsDiB,wBAAA;EACf,MAAA;AAAA;AAAA,cAWW,iBAAA,SAA0B,WAAA;EAAA,QAC7B,EAAA;EAZF;AAWR;;;;;EAXQ,iBAoBW,iBAAA;cAKL,OAAA,GAAS,wBAAA;EA0RgB;;;;;EAAA,QA/P7B,mBAAA;EAAA,QAiBA,gBAAA;EAAA,QAgEA,QAAA;EAAA,QA0BA,eAAA;EAAA,QAgCA,cAAA;EAAA,QAYA,WAAA;EAAA,QAYA,WAAA;EA3MA;;;;;;;;;;;EAAA,QAuOA,cAAA;EAAA;;;;EAAA,QAgBA,SAAA;EAaN;;;;EAFF,kBAAA,CACE,QAAA,UACA,KAAA,EAAO,wBAAA;EAmC4B;;;;;;;;EAAA,QAjB7B,WAAA;EAiBR,GAAA,CAAI,OAAA,EAAS,qBAAA,GAAwB,UAAA,CAAW,SAAA;EAyQhD,OAAA,CAAQ,OAAA,EAAS,yBAAA,GAA4B,UAAA,CAAW,SAAA;EA6DxD,SAAA,CAAU,OAAA,EAAS,2BAAA,GAA8B,OAAA;EASjD,IAAA,CAAK,OAAA,EAAS,sBAAA,GAAyB,OAAA;EAAlC;;;EAkDL,KAAA,CAAA;AAAA"}
|
package/dist/sqlite-runner.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AgentRunner, AgentRunnerConnectRequest, AgentRunnerIsRunningRequest, AgentRunnerRunRequest, AgentRunnerStopRequest } from "@copilotkit/runtime
|
|
1
|
+
import { AgentRunner, AgentRunnerConnectRequest, AgentRunnerIsRunningRequest, AgentRunnerRunRequest, AgentRunnerStopRequest, LocalThreadEndpointScope } from "@copilotkit/runtime";
|
|
2
2
|
import { Observable } from "rxjs";
|
|
3
3
|
import { BaseEvent } from "@ag-ui/client";
|
|
4
4
|
|
|
@@ -8,13 +8,57 @@ interface SqliteAgentRunnerOptions {
|
|
|
8
8
|
}
|
|
9
9
|
declare class SqliteAgentRunner extends AgentRunner {
|
|
10
10
|
private db;
|
|
11
|
+
/**
|
|
12
|
+
* Live runs of this runner, by thread id, for streaming and stop. Kept per
|
|
13
|
+
* instance: owners are checked against this instance's database, so a live
|
|
14
|
+
* run of another runner, which may hold the same thread id for a different
|
|
15
|
+
* owner, must never be bridged, stopped or torn down from here.
|
|
16
|
+
*/
|
|
17
|
+
private readonly activeConnections;
|
|
11
18
|
constructor(options?: SqliteAgentRunnerOptions);
|
|
19
|
+
/**
|
|
20
|
+
* Logs once, when the database opens, how many legacy threads it holds
|
|
21
|
+
* (see {@link LEGACY_THREAD_OWNER}), so operators know those threads are
|
|
22
|
+
* still reachable by id for every caller.
|
|
23
|
+
*/
|
|
24
|
+
private warnIfLegacyThreads;
|
|
12
25
|
private initializeSchema;
|
|
13
26
|
private storeRun;
|
|
14
27
|
private getHistoricRuns;
|
|
15
28
|
private getLatestRunId;
|
|
16
29
|
private setRunState;
|
|
17
30
|
private getRunState;
|
|
31
|
+
/**
|
|
32
|
+
* Owner of a thread: the recorded owner, {@link LEGACY_THREAD_OWNER} for a
|
|
33
|
+
* thread whose runs predate owner records, or `undefined` for an unknown
|
|
34
|
+
* thread.
|
|
35
|
+
*
|
|
36
|
+
* The owner record and the run check are read by one statement, so they
|
|
37
|
+
* come from one snapshot of the database. With two reads, another runner on
|
|
38
|
+
* the same file could claim the thread and store its first run in between,
|
|
39
|
+
* and this call would see runs but no owner and take that thread for a
|
|
40
|
+
* legacy one, open to every caller.
|
|
41
|
+
*/
|
|
42
|
+
private getThreadOwner;
|
|
43
|
+
/**
|
|
44
|
+
* True when a call acting as `ownerId` may reach an existing thread: its
|
|
45
|
+
* owner, or anyone for a legacy thread.
|
|
46
|
+
*/
|
|
47
|
+
private isOwnedBy;
|
|
48
|
+
/**
|
|
49
|
+
* False when the thread is held by an owner other than `scope`'s. A legacy
|
|
50
|
+
* thread is held by nobody, so it is accessible to every caller.
|
|
51
|
+
*/
|
|
52
|
+
isThreadAccessible(threadId: string, scope: LocalThreadEndpointScope): boolean;
|
|
53
|
+
/**
|
|
54
|
+
* Records `owner` as the owner of a new thread, and returns true when the
|
|
55
|
+
* run may go on as `owner`: `owner` holds the thread afterward, or the
|
|
56
|
+
* thread is legacy, which stays unowned. The read, the insert and the
|
|
57
|
+
* re-read run in one IMMEDIATE transaction, so when two runners or
|
|
58
|
+
* processes on one database file claim a new thread at once, exactly one
|
|
59
|
+
* wins and the other reads the winner's row.
|
|
60
|
+
*/
|
|
61
|
+
private claimThread;
|
|
18
62
|
run(request: AgentRunnerRunRequest): Observable<BaseEvent>;
|
|
19
63
|
connect(request: AgentRunnerConnectRequest): Observable<BaseEvent>;
|
|
20
64
|
isRunning(request: AgentRunnerIsRunningRequest): Promise<boolean>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sqlite-runner.d.mts","names":[],"sources":["../src/sqlite-runner.ts"],"mappings":";;;;;
|
|
1
|
+
{"version":3,"file":"sqlite-runner.d.mts","names":[],"sources":["../src/sqlite-runner.ts"],"mappings":";;;;;UAsDiB,wBAAA;EACf,MAAA;AAAA;AAAA,cAWW,iBAAA,SAA0B,WAAA;EAAA,QAC7B,EAAA;EAZF;AAWR;;;;;EAXQ,iBAoBW,iBAAA;cAKL,OAAA,GAAS,wBAAA;EA0RgB;;;;;EAAA,QA/P7B,mBAAA;EAAA,QAiBA,gBAAA;EAAA,QAgEA,QAAA;EAAA,QA0BA,eAAA;EAAA,QAgCA,cAAA;EAAA,QAYA,WAAA;EAAA,QAYA,WAAA;EA3MA;;;;;;;;;;;EAAA,QAuOA,cAAA;EAAA;;;;EAAA,QAgBA,SAAA;EAaN;;;;EAFF,kBAAA,CACE,QAAA,UACA,KAAA,EAAO,wBAAA;EAmC4B;;;;;;;;EAAA,QAjB7B,WAAA;EAiBR,GAAA,CAAI,OAAA,EAAS,qBAAA,GAAwB,UAAA,CAAW,SAAA;EAyQhD,OAAA,CAAQ,OAAA,EAAS,yBAAA,GAA4B,UAAA,CAAW,SAAA;EA6DxD,SAAA,CAAU,OAAA,EAAS,2BAAA,GAA8B,OAAA;EASjD,IAAA,CAAK,OAAA,EAAS,sBAAA,GAAyB,OAAA;EAAlC;;;EAkDL,KAAA,CAAA;AAAA"}
|