@copilotkitnext/runtime 0.0.15 → 0.0.17-alpha.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/index.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  // package.json
2
2
  var package_default = {
3
3
  name: "@copilotkitnext/runtime",
4
- version: "0.0.15",
4
+ version: "0.0.17-alpha.0",
5
5
  description: "Server-side runtime package for CopilotKit2",
6
6
  main: "dist/index.js",
7
7
  types: "dist/index.d.ts",
@@ -29,35 +29,25 @@ var package_default = {
29
29
  devDependencies: {
30
30
  "@copilotkitnext/eslint-config": "workspace:*",
31
31
  "@copilotkitnext/typescript-config": "workspace:*",
32
- "@types/better-sqlite3": "^7.6.13",
33
32
  "@types/node": "^22.15.3",
34
- "better-sqlite3": "^12.2.0",
35
33
  eslint: "^9.30.0",
36
- "ioredis-mock": "^8.9.0",
37
34
  openai: "^5.9.0",
38
35
  tsup: "^8.5.0",
39
36
  typescript: "5.8.2",
40
37
  vitest: "^3.0.5"
41
38
  },
42
39
  dependencies: {
43
- "@ag-ui/client": "0.0.40-alpha.3",
44
- "@ag-ui/core": "0.0.40-alpha.3",
45
- "@ag-ui/encoder": "0.0.40-alpha.3",
40
+ "@ag-ui/client": "0.0.40-alpha.6",
41
+ "@ag-ui/core": "0.0.40-alpha.6",
42
+ "@ag-ui/encoder": "0.0.40-alpha.6",
46
43
  "@copilotkitnext/shared": "workspace:*",
47
44
  hono: "^4.6.13",
48
- ioredis: "^5.7.0",
49
- kysely: "^0.28.5",
50
45
  rxjs: "7.8.1"
51
46
  },
52
47
  peerDependencies: {
53
- "better-sqlite3": "^12.2.0",
54
48
  openai: "^5.9.0"
55
49
  },
56
- peerDependenciesMeta: {
57
- "better-sqlite3": {
58
- optional: true
59
- }
60
- },
50
+ peerDependenciesMeta: {},
61
51
  engines: {
62
52
  node: ">=18"
63
53
  }
@@ -70,161 +60,9 @@ var AgentRunner = class {
70
60
  // src/runner/in-memory.ts
71
61
  import { ReplaySubject } from "rxjs";
72
62
  import {
73
- EventType as EventType2
74
- } from "@ag-ui/client";
75
-
76
- // src/runner/event-compaction.ts
77
- import {
78
- EventType
63
+ EventType,
64
+ compactEvents
79
65
  } from "@ag-ui/client";
80
- function compactEvents(events) {
81
- const compacted = [];
82
- const pendingTextMessages = /* @__PURE__ */ new Map();
83
- const pendingToolCalls = /* @__PURE__ */ new Map();
84
- for (const event of events) {
85
- if (event.type === EventType.TEXT_MESSAGE_START) {
86
- const startEvent = event;
87
- const messageId = startEvent.messageId;
88
- if (!pendingTextMessages.has(messageId)) {
89
- pendingTextMessages.set(messageId, {
90
- contents: [],
91
- otherEvents: []
92
- });
93
- }
94
- const pending = pendingTextMessages.get(messageId);
95
- pending.start = startEvent;
96
- } else if (event.type === EventType.TEXT_MESSAGE_CONTENT) {
97
- const contentEvent = event;
98
- const messageId = contentEvent.messageId;
99
- if (!pendingTextMessages.has(messageId)) {
100
- pendingTextMessages.set(messageId, {
101
- contents: [],
102
- otherEvents: []
103
- });
104
- }
105
- const pending = pendingTextMessages.get(messageId);
106
- pending.contents.push(contentEvent);
107
- } else if (event.type === EventType.TEXT_MESSAGE_END) {
108
- const endEvent = event;
109
- const messageId = endEvent.messageId;
110
- if (!pendingTextMessages.has(messageId)) {
111
- pendingTextMessages.set(messageId, {
112
- contents: [],
113
- otherEvents: []
114
- });
115
- }
116
- const pending = pendingTextMessages.get(messageId);
117
- pending.end = endEvent;
118
- flushTextMessage(messageId, pending, compacted);
119
- pendingTextMessages.delete(messageId);
120
- } else if (event.type === EventType.TOOL_CALL_START) {
121
- const startEvent = event;
122
- const toolCallId = startEvent.toolCallId;
123
- if (!pendingToolCalls.has(toolCallId)) {
124
- pendingToolCalls.set(toolCallId, {
125
- args: [],
126
- otherEvents: []
127
- });
128
- }
129
- const pending = pendingToolCalls.get(toolCallId);
130
- pending.start = startEvent;
131
- } else if (event.type === EventType.TOOL_CALL_ARGS) {
132
- const argsEvent = event;
133
- const toolCallId = argsEvent.toolCallId;
134
- if (!pendingToolCalls.has(toolCallId)) {
135
- pendingToolCalls.set(toolCallId, {
136
- args: [],
137
- otherEvents: []
138
- });
139
- }
140
- const pending = pendingToolCalls.get(toolCallId);
141
- pending.args.push(argsEvent);
142
- } else if (event.type === EventType.TOOL_CALL_END) {
143
- const endEvent = event;
144
- const toolCallId = endEvent.toolCallId;
145
- if (!pendingToolCalls.has(toolCallId)) {
146
- pendingToolCalls.set(toolCallId, {
147
- args: [],
148
- otherEvents: []
149
- });
150
- }
151
- const pending = pendingToolCalls.get(toolCallId);
152
- pending.end = endEvent;
153
- flushToolCall(toolCallId, pending, compacted);
154
- pendingToolCalls.delete(toolCallId);
155
- } else {
156
- let addedToBuffer = false;
157
- for (const [messageId, pending] of pendingTextMessages) {
158
- if (pending.start && !pending.end) {
159
- pending.otherEvents.push(event);
160
- addedToBuffer = true;
161
- break;
162
- }
163
- }
164
- if (!addedToBuffer) {
165
- for (const [toolCallId, pending] of pendingToolCalls) {
166
- if (pending.start && !pending.end) {
167
- pending.otherEvents.push(event);
168
- addedToBuffer = true;
169
- break;
170
- }
171
- }
172
- }
173
- if (!addedToBuffer) {
174
- compacted.push(event);
175
- }
176
- }
177
- }
178
- for (const [messageId, pending] of pendingTextMessages) {
179
- flushTextMessage(messageId, pending, compacted);
180
- }
181
- for (const [toolCallId, pending] of pendingToolCalls) {
182
- flushToolCall(toolCallId, pending, compacted);
183
- }
184
- return compacted;
185
- }
186
- function flushTextMessage(messageId, pending, compacted) {
187
- if (pending.start) {
188
- compacted.push(pending.start);
189
- }
190
- if (pending.contents.length > 0) {
191
- const concatenatedDelta = pending.contents.map((c) => c.delta).join("");
192
- const compactedContent = {
193
- type: EventType.TEXT_MESSAGE_CONTENT,
194
- messageId,
195
- delta: concatenatedDelta
196
- };
197
- compacted.push(compactedContent);
198
- }
199
- if (pending.end) {
200
- compacted.push(pending.end);
201
- }
202
- for (const otherEvent of pending.otherEvents) {
203
- compacted.push(otherEvent);
204
- }
205
- }
206
- function flushToolCall(toolCallId, pending, compacted) {
207
- if (pending.start) {
208
- compacted.push(pending.start);
209
- }
210
- if (pending.args.length > 0) {
211
- const concatenatedArgs = pending.args.map((a) => a.delta).join("");
212
- const compactedArgs = {
213
- type: EventType.TOOL_CALL_ARGS,
214
- toolCallId,
215
- delta: concatenatedArgs
216
- };
217
- compacted.push(compactedArgs);
218
- }
219
- if (pending.end) {
220
- compacted.push(pending.end);
221
- }
222
- for (const otherEvent of pending.otherEvents) {
223
- compacted.push(otherEvent);
224
- }
225
- }
226
-
227
- // src/runner/in-memory.ts
228
66
  var InMemoryEventStore = class {
229
67
  constructor(threadId) {
230
68
  this.threadId = threadId;
@@ -242,61 +80,6 @@ var InMemoryEventStore = class {
242
80
  };
243
81
  var GLOBAL_STORE = /* @__PURE__ */ new Map();
244
82
  var InMemoryAgentRunner = class extends AgentRunner {
245
- convertMessageToEvents(message) {
246
- const events = [];
247
- if ((message.role === "assistant" || message.role === "user" || message.role === "developer" || message.role === "system") && message.content) {
248
- const textStartEvent = {
249
- type: EventType2.TEXT_MESSAGE_START,
250
- messageId: message.id,
251
- role: message.role
252
- };
253
- events.push(textStartEvent);
254
- const textContentEvent = {
255
- type: EventType2.TEXT_MESSAGE_CONTENT,
256
- messageId: message.id,
257
- delta: message.content
258
- };
259
- events.push(textContentEvent);
260
- const textEndEvent = {
261
- type: EventType2.TEXT_MESSAGE_END,
262
- messageId: message.id
263
- };
264
- events.push(textEndEvent);
265
- }
266
- if (message.role === "assistant" && message.toolCalls) {
267
- for (const toolCall of message.toolCalls) {
268
- const toolStartEvent = {
269
- type: EventType2.TOOL_CALL_START,
270
- toolCallId: toolCall.id,
271
- toolCallName: toolCall.function.name,
272
- parentMessageId: message.id
273
- };
274
- events.push(toolStartEvent);
275
- const toolArgsEvent = {
276
- type: EventType2.TOOL_CALL_ARGS,
277
- toolCallId: toolCall.id,
278
- delta: toolCall.function.arguments
279
- };
280
- events.push(toolArgsEvent);
281
- const toolEndEvent = {
282
- type: EventType2.TOOL_CALL_END,
283
- toolCallId: toolCall.id
284
- };
285
- events.push(toolEndEvent);
286
- }
287
- }
288
- if (message.role === "tool" && message.toolCallId) {
289
- const toolResultEvent = {
290
- type: EventType2.TOOL_CALL_RESULT,
291
- messageId: message.id,
292
- toolCallId: message.toolCallId,
293
- content: message.content,
294
- role: "tool"
295
- };
296
- events.push(toolResultEvent);
297
- }
298
- return events;
299
- }
300
83
  run(request) {
301
84
  let existingStore = GLOBAL_STORE.get(request.threadId);
302
85
  if (!existingStore) {
@@ -317,6 +100,13 @@ var InMemoryAgentRunner = class extends AgentRunner {
317
100
  if ("messageId" in event && typeof event.messageId === "string") {
318
101
  historicMessageIds.add(event.messageId);
319
102
  }
103
+ if (event.type === EventType.RUN_STARTED) {
104
+ const runStarted = event;
105
+ const messages = runStarted.input?.messages ?? [];
106
+ for (const message of messages) {
107
+ historicMessageIds.add(message.id);
108
+ }
109
+ }
320
110
  }
321
111
  }
322
112
  const nextSubject = new ReplaySubject(Infinity);
@@ -330,9 +120,26 @@ var InMemoryAgentRunner = class extends AgentRunner {
330
120
  try {
331
121
  await request.agent.runAgent(request.input, {
332
122
  onEvent: ({ event }) => {
333
- runSubject.next(event);
334
- nextSubject.next(event);
335
- currentRunEvents.push(event);
123
+ let processedEvent = event;
124
+ if (event.type === EventType.RUN_STARTED) {
125
+ const runStartedEvent = event;
126
+ if (!runStartedEvent.input) {
127
+ const sanitizedMessages = request.input.messages ? request.input.messages.filter(
128
+ (message) => !historicMessageIds.has(message.id)
129
+ ) : void 0;
130
+ const updatedInput = {
131
+ ...request.input,
132
+ ...sanitizedMessages !== void 0 ? { messages: sanitizedMessages } : {}
133
+ };
134
+ processedEvent = {
135
+ ...runStartedEvent,
136
+ input: updatedInput
137
+ };
138
+ }
139
+ }
140
+ runSubject.next(processedEvent);
141
+ nextSubject.next(processedEvent);
142
+ currentRunEvents.push(processedEvent);
336
143
  },
337
144
  onNewMessage: ({ message }) => {
338
145
  if (!seenMessageIds.has(message.id)) {
@@ -344,14 +151,6 @@ var InMemoryAgentRunner = class extends AgentRunner {
344
151
  for (const message of request.input.messages) {
345
152
  if (!seenMessageIds.has(message.id)) {
346
153
  seenMessageIds.add(message.id);
347
- const events = this.convertMessageToEvents(message);
348
- const isNewMessage = !historicMessageIds.has(message.id);
349
- for (const event of events) {
350
- nextSubject.next(event);
351
- if (isNewMessage) {
352
- currentRunEvents.push(event);
353
- }
354
- }
355
154
  }
356
155
  }
357
156
  }
@@ -1140,726 +939,10 @@ function createCopilotEndpoint({
1140
939
  return c.json({ error: "Not found" }, 404);
1141
940
  });
1142
941
  }
1143
-
1144
- // src/runner/sqlite.ts
1145
- import { ReplaySubject as ReplaySubject2 } from "rxjs";
1146
- import {
1147
- EventType as EventType3
1148
- } from "@ag-ui/client";
1149
- import Database from "better-sqlite3";
1150
- var SCHEMA_VERSION = 1;
1151
- var ACTIVE_CONNECTIONS = /* @__PURE__ */ new Map();
1152
- var SqliteAgentRunner = class extends AgentRunner {
1153
- db;
1154
- constructor(options = {}) {
1155
- super();
1156
- const dbPath = options.dbPath ?? ":memory:";
1157
- if (!Database) {
1158
- throw new Error(
1159
- "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."
1160
- );
1161
- }
1162
- this.db = new Database(dbPath);
1163
- this.initializeSchema();
1164
- }
1165
- convertMessageToEvents(message) {
1166
- const events = [];
1167
- if ((message.role === "assistant" || message.role === "user" || message.role === "developer" || message.role === "system") && message.content) {
1168
- const textStartEvent = {
1169
- type: EventType3.TEXT_MESSAGE_START,
1170
- messageId: message.id,
1171
- role: message.role
1172
- };
1173
- events.push(textStartEvent);
1174
- const textContentEvent = {
1175
- type: EventType3.TEXT_MESSAGE_CONTENT,
1176
- messageId: message.id,
1177
- delta: message.content
1178
- };
1179
- events.push(textContentEvent);
1180
- const textEndEvent = {
1181
- type: EventType3.TEXT_MESSAGE_END,
1182
- messageId: message.id
1183
- };
1184
- events.push(textEndEvent);
1185
- }
1186
- if (message.role === "assistant" && message.toolCalls) {
1187
- for (const toolCall of message.toolCalls) {
1188
- const toolStartEvent = {
1189
- type: EventType3.TOOL_CALL_START,
1190
- toolCallId: toolCall.id,
1191
- toolCallName: toolCall.function.name,
1192
- parentMessageId: message.id
1193
- };
1194
- events.push(toolStartEvent);
1195
- const toolArgsEvent = {
1196
- type: EventType3.TOOL_CALL_ARGS,
1197
- toolCallId: toolCall.id,
1198
- delta: toolCall.function.arguments
1199
- };
1200
- events.push(toolArgsEvent);
1201
- const toolEndEvent = {
1202
- type: EventType3.TOOL_CALL_END,
1203
- toolCallId: toolCall.id
1204
- };
1205
- events.push(toolEndEvent);
1206
- }
1207
- }
1208
- if (message.role === "tool" && message.toolCallId) {
1209
- const toolResultEvent = {
1210
- type: EventType3.TOOL_CALL_RESULT,
1211
- messageId: message.id,
1212
- toolCallId: message.toolCallId,
1213
- content: message.content,
1214
- role: "tool"
1215
- };
1216
- events.push(toolResultEvent);
1217
- }
1218
- return events;
1219
- }
1220
- initializeSchema() {
1221
- this.db.exec(`
1222
- CREATE TABLE IF NOT EXISTS agent_runs (
1223
- id INTEGER PRIMARY KEY AUTOINCREMENT,
1224
- thread_id TEXT NOT NULL,
1225
- run_id TEXT NOT NULL UNIQUE,
1226
- parent_run_id TEXT,
1227
- events TEXT NOT NULL,
1228
- input TEXT NOT NULL,
1229
- created_at INTEGER NOT NULL,
1230
- version INTEGER NOT NULL
1231
- )
1232
- `);
1233
- this.db.exec(`
1234
- CREATE TABLE IF NOT EXISTS run_state (
1235
- thread_id TEXT PRIMARY KEY,
1236
- is_running INTEGER DEFAULT 0,
1237
- current_run_id TEXT,
1238
- updated_at INTEGER NOT NULL
1239
- )
1240
- `);
1241
- this.db.exec(`
1242
- CREATE INDEX IF NOT EXISTS idx_thread_id ON agent_runs(thread_id);
1243
- CREATE INDEX IF NOT EXISTS idx_parent_run_id ON agent_runs(parent_run_id);
1244
- `);
1245
- this.db.exec(`
1246
- CREATE TABLE IF NOT EXISTS schema_version (
1247
- version INTEGER PRIMARY KEY,
1248
- applied_at INTEGER NOT NULL
1249
- )
1250
- `);
1251
- const currentVersion = this.db.prepare("SELECT version FROM schema_version ORDER BY version DESC LIMIT 1").get();
1252
- if (!currentVersion || currentVersion.version < SCHEMA_VERSION) {
1253
- this.db.prepare("INSERT OR REPLACE INTO schema_version (version, applied_at) VALUES (?, ?)").run(SCHEMA_VERSION, Date.now());
1254
- }
1255
- }
1256
- storeRun(threadId, runId, events, input, parentRunId) {
1257
- const compactedEvents = compactEvents(events);
1258
- const stmt = this.db.prepare(`
1259
- INSERT INTO agent_runs (thread_id, run_id, parent_run_id, events, input, created_at, version)
1260
- VALUES (?, ?, ?, ?, ?, ?, ?)
1261
- `);
1262
- stmt.run(
1263
- threadId,
1264
- runId,
1265
- parentRunId ?? null,
1266
- JSON.stringify(compactedEvents),
1267
- // Store only this run's compacted events
1268
- JSON.stringify(input),
1269
- Date.now(),
1270
- SCHEMA_VERSION
1271
- );
1272
- }
1273
- getHistoricRuns(threadId) {
1274
- const stmt = this.db.prepare(`
1275
- WITH RECURSIVE run_chain AS (
1276
- -- Base case: find the root runs (those without parent)
1277
- SELECT * FROM agent_runs
1278
- WHERE thread_id = ? AND parent_run_id IS NULL
1279
-
1280
- UNION ALL
1281
-
1282
- -- Recursive case: find children of current level
1283
- SELECT ar.* FROM agent_runs ar
1284
- INNER JOIN run_chain rc ON ar.parent_run_id = rc.run_id
1285
- WHERE ar.thread_id = ?
1286
- )
1287
- SELECT * FROM run_chain
1288
- ORDER BY created_at ASC
1289
- `);
1290
- const rows = stmt.all(threadId, threadId);
1291
- return rows.map((row) => ({
1292
- id: row.id,
1293
- thread_id: row.thread_id,
1294
- run_id: row.run_id,
1295
- parent_run_id: row.parent_run_id,
1296
- events: JSON.parse(row.events),
1297
- input: JSON.parse(row.input),
1298
- created_at: row.created_at,
1299
- version: row.version
1300
- }));
1301
- }
1302
- getLatestRunId(threadId) {
1303
- const stmt = this.db.prepare(`
1304
- SELECT run_id FROM agent_runs
1305
- WHERE thread_id = ?
1306
- ORDER BY created_at DESC
1307
- LIMIT 1
1308
- `);
1309
- const result = stmt.get(threadId);
1310
- return result?.run_id ?? null;
1311
- }
1312
- setRunState(threadId, isRunning, runId) {
1313
- const stmt = this.db.prepare(`
1314
- INSERT OR REPLACE INTO run_state (thread_id, is_running, current_run_id, updated_at)
1315
- VALUES (?, ?, ?, ?)
1316
- `);
1317
- stmt.run(threadId, isRunning ? 1 : 0, runId ?? null, Date.now());
1318
- }
1319
- getRunState(threadId) {
1320
- const stmt = this.db.prepare(`
1321
- SELECT is_running, current_run_id FROM run_state WHERE thread_id = ?
1322
- `);
1323
- const result = stmt.get(threadId);
1324
- return {
1325
- isRunning: result?.is_running === 1,
1326
- currentRunId: result?.current_run_id ?? null
1327
- };
1328
- }
1329
- run(request) {
1330
- const runState = this.getRunState(request.threadId);
1331
- if (runState.isRunning) {
1332
- throw new Error("Thread already running");
1333
- }
1334
- this.setRunState(request.threadId, true, request.input.runId);
1335
- const seenMessageIds = /* @__PURE__ */ new Set();
1336
- const currentRunEvents = [];
1337
- const historicRuns = this.getHistoricRuns(request.threadId);
1338
- const historicMessageIds = /* @__PURE__ */ new Set();
1339
- for (const run of historicRuns) {
1340
- for (const event of run.events) {
1341
- if ("messageId" in event && typeof event.messageId === "string") {
1342
- historicMessageIds.add(event.messageId);
1343
- }
1344
- }
1345
- }
1346
- const nextSubject = new ReplaySubject2(Infinity);
1347
- const prevSubject = ACTIVE_CONNECTIONS.get(request.threadId);
1348
- ACTIVE_CONNECTIONS.set(request.threadId, nextSubject);
1349
- const runSubject = new ReplaySubject2(Infinity);
1350
- const runAgent = async () => {
1351
- const parentRunId = this.getLatestRunId(request.threadId);
1352
- try {
1353
- await request.agent.runAgent(request.input, {
1354
- onEvent: ({ event }) => {
1355
- runSubject.next(event);
1356
- nextSubject.next(event);
1357
- currentRunEvents.push(event);
1358
- },
1359
- onNewMessage: ({ message }) => {
1360
- if (!seenMessageIds.has(message.id)) {
1361
- seenMessageIds.add(message.id);
1362
- }
1363
- },
1364
- onRunStartedEvent: () => {
1365
- if (request.input.messages) {
1366
- for (const message of request.input.messages) {
1367
- if (!seenMessageIds.has(message.id)) {
1368
- seenMessageIds.add(message.id);
1369
- const events = this.convertMessageToEvents(message);
1370
- const isNewMessage = !historicMessageIds.has(message.id);
1371
- for (const event of events) {
1372
- nextSubject.next(event);
1373
- if (isNewMessage) {
1374
- currentRunEvents.push(event);
1375
- }
1376
- }
1377
- }
1378
- }
1379
- }
1380
- }
1381
- });
1382
- this.storeRun(
1383
- request.threadId,
1384
- request.input.runId,
1385
- currentRunEvents,
1386
- request.input,
1387
- parentRunId
1388
- );
1389
- this.setRunState(request.threadId, false);
1390
- runSubject.complete();
1391
- nextSubject.complete();
1392
- } catch {
1393
- if (currentRunEvents.length > 0) {
1394
- this.storeRun(
1395
- request.threadId,
1396
- request.input.runId,
1397
- currentRunEvents,
1398
- request.input,
1399
- parentRunId
1400
- );
1401
- }
1402
- this.setRunState(request.threadId, false);
1403
- runSubject.complete();
1404
- nextSubject.complete();
1405
- }
1406
- };
1407
- if (prevSubject) {
1408
- prevSubject.subscribe({
1409
- next: (e) => nextSubject.next(e),
1410
- error: (err) => nextSubject.error(err),
1411
- complete: () => {
1412
- }
1413
- });
1414
- }
1415
- runAgent();
1416
- return runSubject.asObservable();
1417
- }
1418
- connect(request) {
1419
- const connectionSubject = new ReplaySubject2(Infinity);
1420
- const historicRuns = this.getHistoricRuns(request.threadId);
1421
- const allHistoricEvents = [];
1422
- for (const run of historicRuns) {
1423
- allHistoricEvents.push(...run.events);
1424
- }
1425
- const compactedEvents = compactEvents(allHistoricEvents);
1426
- const emittedMessageIds = /* @__PURE__ */ new Set();
1427
- for (const event of compactedEvents) {
1428
- connectionSubject.next(event);
1429
- if ("messageId" in event && typeof event.messageId === "string") {
1430
- emittedMessageIds.add(event.messageId);
1431
- }
1432
- }
1433
- const activeSubject = ACTIVE_CONNECTIONS.get(request.threadId);
1434
- const runState = this.getRunState(request.threadId);
1435
- if (activeSubject && runState.isRunning) {
1436
- activeSubject.subscribe({
1437
- next: (event) => {
1438
- if ("messageId" in event && typeof event.messageId === "string" && emittedMessageIds.has(event.messageId)) {
1439
- return;
1440
- }
1441
- connectionSubject.next(event);
1442
- },
1443
- complete: () => connectionSubject.complete(),
1444
- error: (err) => connectionSubject.error(err)
1445
- });
1446
- } else {
1447
- connectionSubject.complete();
1448
- }
1449
- return connectionSubject.asObservable();
1450
- }
1451
- isRunning(request) {
1452
- const runState = this.getRunState(request.threadId);
1453
- return Promise.resolve(runState.isRunning);
1454
- }
1455
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
1456
- stop(_request) {
1457
- throw new Error("Method not implemented.");
1458
- }
1459
- /**
1460
- * Close the database connection (for cleanup)
1461
- */
1462
- close() {
1463
- if (this.db) {
1464
- this.db.close();
1465
- }
1466
- }
1467
- };
1468
-
1469
- // src/runner/enterprise.ts
1470
- import { ReplaySubject as ReplaySubject3 } from "rxjs";
1471
- import {
1472
- EventType as EventType4
1473
- } from "@ag-ui/client";
1474
- var SCHEMA_VERSION2 = 1;
1475
- var redisKeys = {
1476
- stream: (threadId, runId) => `stream:${threadId}:${runId}`,
1477
- active: (threadId) => `active:${threadId}`,
1478
- lock: (threadId) => `lock:${threadId}`
1479
- };
1480
- var EnterpriseAgentRunner = class extends AgentRunner {
1481
- db;
1482
- redis;
1483
- redisSub;
1484
- serverId;
1485
- streamRetentionMs;
1486
- streamActiveTTLMs;
1487
- lockTTLMs;
1488
- constructor(options) {
1489
- super();
1490
- this.db = options.kysely;
1491
- this.redis = options.redis;
1492
- this.redisSub = options.redisSub || options.redis.duplicate();
1493
- this.serverId = options.serverId || this.generateServerId();
1494
- this.streamRetentionMs = options.streamRetentionMs ?? 36e5;
1495
- this.streamActiveTTLMs = options.streamActiveTTLMs ?? 3e5;
1496
- this.lockTTLMs = options.lockTTLMs ?? 3e5;
1497
- this.initializeSchema();
1498
- }
1499
- run(request) {
1500
- const runSubject = new ReplaySubject3(Infinity);
1501
- const executeRun = async () => {
1502
- const { threadId, input, agent } = request;
1503
- const runId = input.runId;
1504
- const streamKey = redisKeys.stream(threadId, runId);
1505
- const activeRunId = await this.redis.get(redisKeys.active(threadId));
1506
- if (activeRunId) {
1507
- throw new Error("Thread already running");
1508
- }
1509
- const lockAcquired = await this.redis.set(
1510
- redisKeys.lock(threadId),
1511
- this.serverId,
1512
- "PX",
1513
- this.lockTTLMs,
1514
- "NX"
1515
- );
1516
- if (!lockAcquired) {
1517
- throw new Error("Thread already running");
1518
- }
1519
- await this.redis.setex(
1520
- redisKeys.active(threadId),
1521
- Math.floor(this.lockTTLMs / 1e3),
1522
- runId
1523
- );
1524
- await this.setRunState(threadId, true, runId);
1525
- const currentRunEvents = [];
1526
- const seenMessageIds = /* @__PURE__ */ new Set();
1527
- const historicRuns = await this.getHistoricRuns(threadId);
1528
- const historicMessageIds = /* @__PURE__ */ new Set();
1529
- for (const run of historicRuns) {
1530
- const events = JSON.parse(run.events);
1531
- for (const event of events) {
1532
- if ("messageId" in event && typeof event.messageId === "string") {
1533
- historicMessageIds.add(event.messageId);
1534
- }
1535
- }
1536
- }
1537
- const parentRunId = historicRuns[historicRuns.length - 1]?.run_id ?? null;
1538
- try {
1539
- await agent.runAgent(input, {
1540
- onEvent: async ({ event }) => {
1541
- runSubject.next(event);
1542
- currentRunEvents.push(event);
1543
- await this.redis.xadd(
1544
- streamKey,
1545
- "MAXLEN",
1546
- "~",
1547
- "10000",
1548
- "*",
1549
- "type",
1550
- event.type,
1551
- "data",
1552
- JSON.stringify(event)
1553
- );
1554
- await this.redis.pexpire(streamKey, this.streamActiveTTLMs);
1555
- if (event.type === EventType4.RUN_FINISHED || event.type === EventType4.RUN_ERROR) {
1556
- await this.redis.pexpire(streamKey, this.streamRetentionMs);
1557
- }
1558
- },
1559
- onNewMessage: ({ message }) => {
1560
- if (!seenMessageIds.has(message.id)) {
1561
- seenMessageIds.add(message.id);
1562
- }
1563
- },
1564
- onRunStartedEvent: async () => {
1565
- if (input.messages) {
1566
- for (const message of input.messages) {
1567
- if (!seenMessageIds.has(message.id)) {
1568
- seenMessageIds.add(message.id);
1569
- const events = this.convertMessageToEvents(message);
1570
- const isNewMessage = !historicMessageIds.has(message.id);
1571
- for (const event of events) {
1572
- await this.redis.xadd(
1573
- streamKey,
1574
- "MAXLEN",
1575
- "~",
1576
- "10000",
1577
- "*",
1578
- "type",
1579
- event.type,
1580
- "data",
1581
- JSON.stringify(event)
1582
- );
1583
- if (isNewMessage) {
1584
- currentRunEvents.push(event);
1585
- }
1586
- }
1587
- }
1588
- }
1589
- }
1590
- await this.redis.pexpire(streamKey, this.streamActiveTTLMs);
1591
- }
1592
- });
1593
- const compactedEvents = compactEvents(currentRunEvents);
1594
- await this.storeRun(threadId, runId, compactedEvents, input, parentRunId);
1595
- } finally {
1596
- await this.setRunState(threadId, false);
1597
- await this.redis.del(redisKeys.active(threadId));
1598
- await this.redis.del(redisKeys.lock(threadId));
1599
- const exists = await this.redis.exists(streamKey);
1600
- if (exists) {
1601
- await this.redis.pexpire(streamKey, this.streamRetentionMs);
1602
- }
1603
- runSubject.complete();
1604
- }
1605
- };
1606
- executeRun().catch((error) => {
1607
- runSubject.error(error);
1608
- });
1609
- return runSubject.asObservable();
1610
- }
1611
- connect(request) {
1612
- const connectionSubject = new ReplaySubject3(Infinity);
1613
- const streamConnection = async () => {
1614
- const { threadId } = request;
1615
- const historicRuns = await this.getHistoricRuns(threadId);
1616
- const allHistoricEvents = [];
1617
- for (const run of historicRuns) {
1618
- const events = JSON.parse(run.events);
1619
- allHistoricEvents.push(...events);
1620
- }
1621
- const compactedEvents = compactEvents(allHistoricEvents);
1622
- const emittedMessageIds = /* @__PURE__ */ new Set();
1623
- for (const event of compactedEvents) {
1624
- connectionSubject.next(event);
1625
- if ("messageId" in event && typeof event.messageId === "string") {
1626
- emittedMessageIds.add(event.messageId);
1627
- }
1628
- }
1629
- const activeRunId = await this.redis.get(redisKeys.active(threadId));
1630
- if (activeRunId) {
1631
- const streamKey = redisKeys.stream(threadId, activeRunId);
1632
- let lastId = "0-0";
1633
- let consecutiveEmptyReads = 0;
1634
- while (true) {
1635
- try {
1636
- const result = await this.redis.call(
1637
- "XREAD",
1638
- "BLOCK",
1639
- "5000",
1640
- "COUNT",
1641
- "100",
1642
- "STREAMS",
1643
- streamKey,
1644
- lastId
1645
- );
1646
- if (!result || result.length === 0) {
1647
- consecutiveEmptyReads++;
1648
- const exists = await this.redis.exists(streamKey);
1649
- if (!exists) {
1650
- break;
1651
- }
1652
- const stillActive = await this.redis.get(redisKeys.active(threadId));
1653
- if (stillActive !== activeRunId) {
1654
- break;
1655
- }
1656
- if (consecutiveEmptyReads > 3) {
1657
- break;
1658
- }
1659
- continue;
1660
- }
1661
- consecutiveEmptyReads = 0;
1662
- const [, messages] = result[0] || [null, []];
1663
- for (const [id, fields] of messages || []) {
1664
- lastId = id;
1665
- let eventData = null;
1666
- let eventType = null;
1667
- for (let i = 0; i < fields.length; i += 2) {
1668
- if (fields[i] === "data") {
1669
- eventData = fields[i + 1] ?? null;
1670
- } else if (fields[i] === "type") {
1671
- eventType = fields[i + 1] ?? null;
1672
- }
1673
- }
1674
- if (eventData) {
1675
- const event = JSON.parse(eventData);
1676
- if ("messageId" in event && typeof event.messageId === "string" && emittedMessageIds.has(event.messageId)) {
1677
- continue;
1678
- }
1679
- connectionSubject.next(event);
1680
- if (eventType === EventType4.RUN_FINISHED || eventType === EventType4.RUN_ERROR) {
1681
- connectionSubject.complete();
1682
- return;
1683
- }
1684
- }
1685
- }
1686
- } catch {
1687
- break;
1688
- }
1689
- }
1690
- }
1691
- connectionSubject.complete();
1692
- };
1693
- streamConnection().catch(() => connectionSubject.complete());
1694
- return connectionSubject.asObservable();
1695
- }
1696
- async isRunning(request) {
1697
- const { threadId } = request;
1698
- const activeRunId = await this.redis.get(redisKeys.active(threadId));
1699
- if (activeRunId) return true;
1700
- const lockExists = await this.redis.exists(redisKeys.lock(threadId));
1701
- if (lockExists) return true;
1702
- const state = await this.db.selectFrom("run_state").where("thread_id", "=", threadId).selectAll().executeTakeFirst();
1703
- return state?.is_running === 1;
1704
- }
1705
- async stop(request) {
1706
- const { threadId } = request;
1707
- const activeRunId = await this.redis.get(redisKeys.active(threadId));
1708
- if (!activeRunId) {
1709
- return false;
1710
- }
1711
- const streamKey = redisKeys.stream(threadId, activeRunId);
1712
- await this.redis.xadd(
1713
- streamKey,
1714
- "*",
1715
- "type",
1716
- EventType4.RUN_ERROR,
1717
- "data",
1718
- JSON.stringify({
1719
- type: EventType4.RUN_ERROR,
1720
- error: "Run stopped by user"
1721
- })
1722
- );
1723
- await this.redis.pexpire(streamKey, this.streamRetentionMs);
1724
- await this.setRunState(threadId, false);
1725
- await this.redis.del(redisKeys.active(threadId));
1726
- await this.redis.del(redisKeys.lock(threadId));
1727
- return true;
1728
- }
1729
- // Helper methods
1730
- convertMessageToEvents(message) {
1731
- const events = [];
1732
- if ((message.role === "assistant" || message.role === "user" || message.role === "developer" || message.role === "system") && message.content) {
1733
- const textStartEvent = {
1734
- type: EventType4.TEXT_MESSAGE_START,
1735
- messageId: message.id,
1736
- role: message.role
1737
- };
1738
- events.push(textStartEvent);
1739
- const textContentEvent = {
1740
- type: EventType4.TEXT_MESSAGE_CONTENT,
1741
- messageId: message.id,
1742
- delta: message.content
1743
- };
1744
- events.push(textContentEvent);
1745
- const textEndEvent = {
1746
- type: EventType4.TEXT_MESSAGE_END,
1747
- messageId: message.id
1748
- };
1749
- events.push(textEndEvent);
1750
- }
1751
- if (message.role === "assistant" && message.toolCalls) {
1752
- for (const toolCall of message.toolCalls) {
1753
- const toolStartEvent = {
1754
- type: EventType4.TOOL_CALL_START,
1755
- toolCallId: toolCall.id,
1756
- toolCallName: toolCall.function.name,
1757
- parentMessageId: message.id
1758
- };
1759
- events.push(toolStartEvent);
1760
- const toolArgsEvent = {
1761
- type: EventType4.TOOL_CALL_ARGS,
1762
- toolCallId: toolCall.id,
1763
- delta: toolCall.function.arguments
1764
- };
1765
- events.push(toolArgsEvent);
1766
- const toolEndEvent = {
1767
- type: EventType4.TOOL_CALL_END,
1768
- toolCallId: toolCall.id
1769
- };
1770
- events.push(toolEndEvent);
1771
- }
1772
- }
1773
- if (message.role === "tool" && message.toolCallId) {
1774
- const toolResultEvent = {
1775
- type: EventType4.TOOL_CALL_RESULT,
1776
- messageId: message.id,
1777
- toolCallId: message.toolCallId,
1778
- content: message.content,
1779
- role: "tool"
1780
- };
1781
- events.push(toolResultEvent);
1782
- }
1783
- return events;
1784
- }
1785
- async initializeSchema() {
1786
- try {
1787
- await this.db.schema.createTable("agent_runs").ifNotExists().addColumn("id", "integer", (col) => col.primaryKey().autoIncrement()).addColumn("thread_id", "text", (col) => col.notNull()).addColumn("run_id", "text", (col) => col.notNull().unique()).addColumn("parent_run_id", "text").addColumn("events", "text", (col) => col.notNull()).addColumn("input", "text", (col) => col.notNull()).addColumn("created_at", "integer", (col) => col.notNull()).addColumn("version", "integer", (col) => col.notNull()).execute().catch(() => {
1788
- });
1789
- await this.db.schema.createTable("run_state").ifNotExists().addColumn("thread_id", "text", (col) => col.primaryKey()).addColumn("is_running", "integer", (col) => col.defaultTo(0)).addColumn("current_run_id", "text").addColumn("server_id", "text").addColumn("updated_at", "integer", (col) => col.notNull()).execute().catch(() => {
1790
- });
1791
- await this.db.schema.createTable("schema_version").ifNotExists().addColumn("version", "integer", (col) => col.primaryKey()).addColumn("applied_at", "integer", (col) => col.notNull()).execute().catch(() => {
1792
- });
1793
- await this.db.schema.createIndex("idx_thread_id").ifNotExists().on("agent_runs").column("thread_id").execute().catch(() => {
1794
- });
1795
- await this.db.schema.createIndex("idx_parent_run_id").ifNotExists().on("agent_runs").column("parent_run_id").execute().catch(() => {
1796
- });
1797
- const currentVersion = await this.db.selectFrom("schema_version").orderBy("version", "desc").limit(1).selectAll().executeTakeFirst();
1798
- if (!currentVersion || currentVersion.version < SCHEMA_VERSION2) {
1799
- await this.db.insertInto("schema_version").values({
1800
- version: SCHEMA_VERSION2,
1801
- applied_at: Date.now()
1802
- }).onConflict(
1803
- (oc) => oc.column("version").doUpdateSet({ applied_at: Date.now() })
1804
- ).execute();
1805
- }
1806
- } catch {
1807
- }
1808
- }
1809
- async storeRun(threadId, runId, events, input, parentRunId) {
1810
- await this.db.insertInto("agent_runs").values({
1811
- thread_id: threadId,
1812
- run_id: runId,
1813
- parent_run_id: parentRunId,
1814
- events: JSON.stringify(events),
1815
- input: JSON.stringify(input),
1816
- created_at: Date.now(),
1817
- version: SCHEMA_VERSION2
1818
- }).execute();
1819
- }
1820
- async getHistoricRuns(threadId) {
1821
- const rows = await this.db.selectFrom("agent_runs").where("thread_id", "=", threadId).orderBy("created_at", "asc").selectAll().execute();
1822
- return rows.map((row) => ({
1823
- id: Number(row.id),
1824
- thread_id: row.thread_id,
1825
- run_id: row.run_id,
1826
- parent_run_id: row.parent_run_id,
1827
- events: row.events,
1828
- input: row.input,
1829
- created_at: row.created_at,
1830
- version: row.version
1831
- }));
1832
- }
1833
- async setRunState(threadId, isRunning, runId) {
1834
- await this.db.insertInto("run_state").values({
1835
- thread_id: threadId,
1836
- is_running: isRunning ? 1 : 0,
1837
- current_run_id: runId ?? null,
1838
- server_id: this.serverId,
1839
- updated_at: Date.now()
1840
- }).onConflict(
1841
- (oc) => oc.column("thread_id").doUpdateSet({
1842
- is_running: isRunning ? 1 : 0,
1843
- current_run_id: runId ?? null,
1844
- server_id: this.serverId,
1845
- updated_at: Date.now()
1846
- })
1847
- ).execute();
1848
- }
1849
- async close() {
1850
- await this.db.destroy();
1851
- this.redis.disconnect();
1852
- this.redisSub.disconnect();
1853
- }
1854
- generateServerId() {
1855
- return `server-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
1856
- }
1857
- };
1858
942
  export {
943
+ AgentRunner,
1859
944
  CopilotRuntime,
1860
- EnterpriseAgentRunner,
1861
945
  InMemoryAgentRunner,
1862
- SqliteAgentRunner,
1863
946
  VERSION,
1864
947
  createCopilotEndpoint
1865
948
  };