@copilotkit/sqlite-runner 1.82.0 → 1.84.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.
@@ -1,19 +1,51 @@
1
- import { AgentRunner, ThreadAlreadyRunningError, finalizeRunEvents } from "@copilotkit/runtime/v2";
1
+ import { AgentRunner, ThreadAccessDeniedError, ThreadAlreadyRunningError, finalizeRunEvents, ɵresolveThreadOwner } from "@copilotkit/runtime/v2";
2
2
  import { ReplaySubject } from "rxjs";
3
3
  import { EventType, compactEvents } from "@ag-ui/client";
4
4
  import Database from "better-sqlite3";
5
5
 
6
6
  //#region src/sqlite-runner.ts
7
- const SCHEMA_VERSION = 1;
8
- const ACTIVE_CONNECTIONS = /* @__PURE__ */ new Map();
7
+ /**
8
+ * 2: adds `thread_owners`. Also written as the `version` of each stored run,
9
+ * so runs stored from now on are tagged 2.
10
+ */
11
+ const SCHEMA_VERSION = 2;
12
+ /**
13
+ * Owner of a legacy thread: one whose runs were stored before owners were
14
+ * recorded (schema 1), so it has runs but no `thread_owners` row. Before the
15
+ * upgrade, every caller that held a thread id could reach that thread. A
16
+ * legacy thread keeps exactly that: it stays a capability, reachable by id for
17
+ * every caller and every identity, and continuing it never claims it. Only a
18
+ * thread that has no runs yet is claimed, by its first run, so no new thread
19
+ * can become legacy. The value holds a NUL character, which no request
20
+ * identity can hold.
21
+ */
22
+ const LEGACY_THREAD_OWNER = "\0legacy";
9
23
  var SqliteAgentRunner = class extends AgentRunner {
10
24
  db;
25
+ /**
26
+ * Live runs of this runner, by thread id, for streaming and stop. Kept per
27
+ * instance: owners are checked against this instance's database, so a live
28
+ * run of another runner, which may hold the same thread id for a different
29
+ * owner, must never be bridged, stopped or torn down from here.
30
+ */
31
+ activeConnections = /* @__PURE__ */ new Map();
11
32
  constructor(options = {}) {
12
33
  super();
13
34
  const dbPath = options.dbPath ?? ":memory:";
14
35
  if (!Database) 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.");
15
36
  this.db = new Database(dbPath);
16
37
  this.initializeSchema();
38
+ this.warnIfLegacyThreads();
39
+ }
40
+ /**
41
+ * Logs once, when the database opens, how many legacy threads it holds
42
+ * (see {@link LEGACY_THREAD_OWNER}), so operators know those threads are
43
+ * still reachable by id for every caller.
44
+ */
45
+ warnIfLegacyThreads() {
46
+ 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;
47
+ if (count === 0) return;
48
+ 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.`);
17
49
  }
18
50
  initializeSchema() {
19
51
  this.db.exec(`
@@ -35,6 +67,13 @@ var SqliteAgentRunner = class extends AgentRunner {
35
67
  current_run_id TEXT,
36
68
  updated_at INTEGER NOT NULL
37
69
  )
70
+ `);
71
+ this.db.exec(`
72
+ CREATE TABLE IF NOT EXISTS thread_owners (
73
+ thread_id TEXT PRIMARY KEY,
74
+ owner_id TEXT NOT NULL,
75
+ created_at INTEGER NOT NULL
76
+ )
38
77
  `);
39
78
  this.db.exec(`
40
79
  CREATE INDEX IF NOT EXISTS idx_thread_id ON agent_runs(thread_id);
@@ -106,8 +145,59 @@ var SqliteAgentRunner = class extends AgentRunner {
106
145
  currentRunId: result?.current_run_id ?? null
107
146
  };
108
147
  }
148
+ /**
149
+ * Owner of a thread: the recorded owner, {@link LEGACY_THREAD_OWNER} for a
150
+ * thread whose runs predate owner records, or `undefined` for an unknown
151
+ * thread.
152
+ *
153
+ * The owner record and the run check are read by one statement, so they
154
+ * come from one snapshot of the database. With two reads, another runner on
155
+ * the same file could claim the thread and store its first run in between,
156
+ * and this call would see runs but no owner and take that thread for a
157
+ * legacy one, open to every caller.
158
+ */
159
+ getThreadOwner(threadId) {
160
+ 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);
161
+ if (row?.owner_id != null) return row.owner_id;
162
+ return row?.has_runs ? LEGACY_THREAD_OWNER : void 0;
163
+ }
164
+ /**
165
+ * True when a call acting as `ownerId` may reach an existing thread: its
166
+ * owner, or anyone for a legacy thread.
167
+ */
168
+ isOwnedBy(threadId, ownerId) {
169
+ const owner = this.getThreadOwner(threadId);
170
+ return owner === LEGACY_THREAD_OWNER || owner === ɵresolveThreadOwner(ownerId);
171
+ }
172
+ /**
173
+ * False when the thread is held by an owner other than `scope`'s. A legacy
174
+ * thread is held by nobody, so it is accessible to every caller.
175
+ */
176
+ isThreadAccessible(threadId, scope) {
177
+ const owner = this.getThreadOwner(threadId);
178
+ return owner === void 0 || owner === LEGACY_THREAD_OWNER || owner === ɵresolveThreadOwner(scope.ownerId);
179
+ }
180
+ /**
181
+ * Records `owner` as the owner of a new thread, and returns true when the
182
+ * run may go on as `owner`: `owner` holds the thread afterward, or the
183
+ * thread is legacy, which stays unowned. The read, the insert and the
184
+ * re-read run in one IMMEDIATE transaction, so when two runners or
185
+ * processes on one database file claim a new thread at once, exactly one
186
+ * wins and the other reads the winner's row.
187
+ */
188
+ claimThread(threadId, owner) {
189
+ return this.db.transaction(() => {
190
+ const current = this.getThreadOwner(threadId);
191
+ if (current === LEGACY_THREAD_OWNER) return true;
192
+ 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());
193
+ return this.getThreadOwner(threadId) === owner;
194
+ }).immediate();
195
+ }
109
196
  run(request) {
197
+ const owner = ɵresolveThreadOwner(request.ownerId);
198
+ if (!this.isThreadAccessible(request.threadId, { ownerId: owner })) throw new ThreadAccessDeniedError();
110
199
  if (this.getRunState(request.threadId).isRunning) throw new ThreadAlreadyRunningError();
200
+ if (!this.claimThread(request.threadId, owner)) throw new ThreadAccessDeniedError();
111
201
  this.setRunState(request.threadId, true, request.input.runId);
112
202
  const seenMessageIds = /* @__PURE__ */ new Set();
113
203
  const currentRunEvents = [];
@@ -121,15 +211,16 @@ var SqliteAgentRunner = class extends AgentRunner {
121
211
  }
122
212
  }
123
213
  const nextSubject = new ReplaySubject(Infinity);
124
- const prevSubject = ACTIVE_CONNECTIONS.get(request.threadId)?.subject;
214
+ const prevSubject = this.activeConnections.get(request.threadId)?.subject;
125
215
  const runSubject = new ReplaySubject(Infinity);
126
- ACTIVE_CONNECTIONS.set(request.threadId, {
216
+ const runConnection = {
127
217
  subject: nextSubject,
128
218
  agent: request.agent,
129
219
  runSubject,
130
220
  currentEvents: currentRunEvents,
131
221
  stopRequested: false
132
- });
222
+ };
223
+ this.activeConnections.set(request.threadId, runConnection);
133
224
  const runAgent = async () => {
134
225
  const parentRunId = this.getLatestRunId(request.threadId);
135
226
  const ensureStoppedRunStarted = (stopRequested) => {
@@ -178,7 +269,7 @@ var SqliteAgentRunner = class extends AgentRunner {
178
269
  }
179
270
  }
180
271
  });
181
- const connection = ACTIVE_CONNECTIONS.get(request.threadId);
272
+ const connection = this.activeConnections.get(request.threadId);
182
273
  ensureStoppedRunStarted(connection?.stopRequested ?? false);
183
274
  const appendedEvents = finalizeRunEvents(currentRunEvents, {
184
275
  stopRequested: connection?.stopRequested ?? false,
@@ -198,9 +289,13 @@ var SqliteAgentRunner = class extends AgentRunner {
198
289
  }
199
290
  runSubject.complete();
200
291
  nextSubject.complete();
201
- ACTIVE_CONNECTIONS.delete(request.threadId);
202
- } catch {
203
- const connection = ACTIVE_CONNECTIONS.get(request.threadId);
292
+ this.activeConnections.delete(request.threadId);
293
+ } catch (error) {
294
+ const connection = this.activeConnections.get(request.threadId);
295
+ if (!connection?.stopRequested) console.error("Sqlite agent run failed", {
296
+ threadId: request.threadId,
297
+ runId: request.input.runId
298
+ }, error);
204
299
  ensureStoppedRunStarted(connection?.stopRequested ?? false);
205
300
  const appendedEvents = finalizeRunEvents(currentRunEvents, {
206
301
  stopRequested: connection?.stopRequested ?? false,
@@ -220,7 +315,7 @@ var SqliteAgentRunner = class extends AgentRunner {
220
315
  }
221
316
  runSubject.complete();
222
317
  nextSubject.complete();
223
- ACTIVE_CONNECTIONS.delete(request.threadId);
318
+ this.activeConnections.delete(request.threadId);
224
319
  }
225
320
  };
226
321
  if (prevSubject) prevSubject.subscribe({
@@ -228,11 +323,29 @@ var SqliteAgentRunner = class extends AgentRunner {
228
323
  error: (err) => nextSubject.error(err),
229
324
  complete: () => {}
230
325
  });
231
- runAgent();
326
+ runAgent().catch((error) => {
327
+ console.error("Failed to finish sqlite agent run", {
328
+ threadId: request.threadId,
329
+ runId: request.input.runId
330
+ }, error);
331
+ try {
332
+ const state = this.getRunState(request.threadId);
333
+ if (state.currentRunId === null || state.currentRunId === request.input.runId) this.setRunState(request.threadId, false);
334
+ } catch (stateError) {
335
+ console.error("Failed to reset sqlite run state", stateError);
336
+ }
337
+ if (this.activeConnections.get(request.threadId) === runConnection) this.activeConnections.delete(request.threadId);
338
+ runSubject.complete();
339
+ nextSubject.complete();
340
+ });
232
341
  return runSubject.asObservable();
233
342
  }
234
343
  connect(request) {
235
344
  const connectionSubject = new ReplaySubject(Infinity);
345
+ if (!this.isOwnedBy(request.threadId, request.ownerId)) {
346
+ connectionSubject.complete();
347
+ return connectionSubject.asObservable();
348
+ }
236
349
  const historicRuns = this.getHistoricRuns(request.threadId);
237
350
  const allHistoricEvents = [];
238
351
  for (const run of historicRuns) allHistoricEvents.push(...run.events);
@@ -242,7 +355,7 @@ var SqliteAgentRunner = class extends AgentRunner {
242
355
  connectionSubject.next(event);
243
356
  if ("messageId" in event && typeof event.messageId === "string") emittedMessageIds.add(event.messageId);
244
357
  }
245
- const activeConnection = ACTIVE_CONNECTIONS.get(request.threadId);
358
+ const activeConnection = this.activeConnections.get(request.threadId);
246
359
  const runState = this.getRunState(request.threadId);
247
360
  if (activeConnection && (runState.isRunning || activeConnection.stopRequested)) activeConnection.subject.subscribe({
248
361
  next: (event) => {
@@ -256,12 +369,16 @@ var SqliteAgentRunner = class extends AgentRunner {
256
369
  return connectionSubject.asObservable();
257
370
  }
258
371
  isRunning(request) {
372
+ if (!this.isOwnedBy(request.threadId, request.ownerId)) return Promise.resolve(false);
259
373
  const runState = this.getRunState(request.threadId);
260
374
  return Promise.resolve(runState.isRunning);
261
375
  }
262
376
  stop(request) {
263
- if (!this.getRunState(request.threadId).isRunning) return Promise.resolve(false);
264
- const connection = ACTIVE_CONNECTIONS.get(request.threadId);
377
+ const runState = this.getRunState(request.threadId);
378
+ if (!runState.isRunning) return Promise.resolve(false);
379
+ if (!this.isOwnedBy(request.threadId, request.ownerId)) return Promise.resolve(false);
380
+ if (request.runId !== void 0 && runState.currentRunId !== request.runId) return Promise.resolve(false);
381
+ const connection = this.activeConnections.get(request.threadId);
265
382
  const agent = connection?.agent;
266
383
  if (!connection || !agent) return Promise.resolve(false);
267
384
  if (connection.stopRequested) return Promise.resolve(false);
@@ -273,7 +390,7 @@ var SqliteAgentRunner = class extends AgentRunner {
273
390
  } catch (error) {
274
391
  console.error("Failed to abort sqlite agent run", error);
275
392
  connection.stopRequested = false;
276
- this.setRunState(request.threadId, true);
393
+ this.setRunState(request.threadId, true, runState.currentRunId ?? void 0);
277
394
  return Promise.resolve(false);
278
395
  }
279
396
  }
@@ -1 +1 @@
1
- {"version":3,"file":"sqlite-runner.mjs","names":[],"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.mjs","names":[],"sources":["../src/sqlite-runner.ts"],"sourcesContent":["import {\n AgentRunner,\n ThreadAccessDeniedError,\n ThreadAlreadyRunningError,\n finalizeRunEvents,\n ɵresolveThreadOwner,\n} from \"@copilotkit/runtime/v2\";\nimport type {\n AgentRunnerConnectRequest,\n AgentRunnerIsRunningRequest,\n AgentRunnerRunRequest,\n AgentRunnerStopRequest,\n LocalThreadEndpointScope,\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\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":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@copilotkit/sqlite-runner",
3
- "version": "1.82.0",
3
+ "version": "1.84.0",
4
4
  "description": "SQLite-backed agent runner for CopilotKit2",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -24,7 +24,7 @@
24
24
  "dependencies": {
25
25
  "@ag-ui/client": "1.0.2",
26
26
  "rxjs": "7.8.1",
27
- "@copilotkit/runtime": "1.82.0"
27
+ "@copilotkit/runtime": "1.84.0"
28
28
  },
29
29
  "devDependencies": {
30
30
  "@types/better-sqlite3": "^7.6.13",