@copilotkit/runtime 1.62.1 → 1.62.2-canary.1783457132

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) hide show
  1. package/dist/package.cjs +1 -1
  2. package/dist/package.mjs +1 -1
  3. package/dist/v2/index.cjs +5 -1
  4. package/dist/v2/index.d.cts +2 -2
  5. package/dist/v2/index.d.mts +2 -2
  6. package/dist/v2/index.mjs +2 -2
  7. package/dist/v2/runtime/core/fetch-handler.cjs +27 -0
  8. package/dist/v2/runtime/core/fetch-handler.cjs.map +1 -1
  9. package/dist/v2/runtime/core/fetch-handler.d.cts.map +1 -1
  10. package/dist/v2/runtime/core/fetch-handler.d.mts.map +1 -1
  11. package/dist/v2/runtime/core/fetch-handler.mjs +27 -0
  12. package/dist/v2/runtime/core/fetch-handler.mjs.map +1 -1
  13. package/dist/v2/runtime/core/fetch-router.cjs +10 -0
  14. package/dist/v2/runtime/core/fetch-router.cjs.map +1 -1
  15. package/dist/v2/runtime/core/fetch-router.mjs +10 -0
  16. package/dist/v2/runtime/core/fetch-router.mjs.map +1 -1
  17. package/dist/v2/runtime/core/hooks.cjs.map +1 -1
  18. package/dist/v2/runtime/core/hooks.d.cts +7 -0
  19. package/dist/v2/runtime/core/hooks.d.cts.map +1 -1
  20. package/dist/v2/runtime/core/hooks.d.mts +7 -0
  21. package/dist/v2/runtime/core/hooks.d.mts.map +1 -1
  22. package/dist/v2/runtime/core/hooks.mjs.map +1 -1
  23. package/dist/v2/runtime/core/runtime.cjs +5 -0
  24. package/dist/v2/runtime/core/runtime.cjs.map +1 -1
  25. package/dist/v2/runtime/core/runtime.d.cts +21 -0
  26. package/dist/v2/runtime/core/runtime.d.cts.map +1 -1
  27. package/dist/v2/runtime/core/runtime.d.mts +21 -0
  28. package/dist/v2/runtime/core/runtime.d.mts.map +1 -1
  29. package/dist/v2/runtime/core/runtime.mjs +5 -0
  30. package/dist/v2/runtime/core/runtime.mjs.map +1 -1
  31. package/dist/v2/runtime/handlers/handle-connect.cjs +2 -1
  32. package/dist/v2/runtime/handlers/handle-connect.cjs.map +1 -1
  33. package/dist/v2/runtime/handlers/handle-connect.mjs +2 -1
  34. package/dist/v2/runtime/handlers/handle-connect.mjs.map +1 -1
  35. package/dist/v2/runtime/handlers/header-utils.cjs +169 -9
  36. package/dist/v2/runtime/handlers/header-utils.cjs.map +1 -1
  37. package/dist/v2/runtime/handlers/header-utils.d.cts +54 -0
  38. package/dist/v2/runtime/handlers/header-utils.d.cts.map +1 -0
  39. package/dist/v2/runtime/handlers/header-utils.d.mts +54 -0
  40. package/dist/v2/runtime/handlers/header-utils.d.mts.map +1 -0
  41. package/dist/v2/runtime/handlers/header-utils.mjs +168 -9
  42. package/dist/v2/runtime/handlers/header-utils.mjs.map +1 -1
  43. package/dist/v2/runtime/handlers/intelligence/memories.cjs +209 -0
  44. package/dist/v2/runtime/handlers/intelligence/memories.cjs.map +1 -0
  45. package/dist/v2/runtime/handlers/intelligence/memories.mjs +204 -0
  46. package/dist/v2/runtime/handlers/intelligence/memories.mjs.map +1 -0
  47. package/dist/v2/runtime/handlers/shared/agent-utils.cjs +18 -5
  48. package/dist/v2/runtime/handlers/shared/agent-utils.cjs.map +1 -1
  49. package/dist/v2/runtime/handlers/shared/agent-utils.mjs +18 -5
  50. package/dist/v2/runtime/handlers/shared/agent-utils.mjs.map +1 -1
  51. package/dist/v2/runtime/handlers/sse/connect.cjs +2 -2
  52. package/dist/v2/runtime/handlers/sse/connect.cjs.map +1 -1
  53. package/dist/v2/runtime/handlers/sse/connect.mjs +3 -3
  54. package/dist/v2/runtime/handlers/sse/connect.mjs.map +1 -1
  55. package/dist/v2/runtime/index.d.cts +1 -1
  56. package/dist/v2/runtime/index.d.mts +1 -1
  57. package/dist/v2/runtime/intelligence-platform/client.cjs +71 -2
  58. package/dist/v2/runtime/intelligence-platform/client.cjs.map +1 -1
  59. package/dist/v2/runtime/intelligence-platform/client.d.cts +112 -0
  60. package/dist/v2/runtime/intelligence-platform/client.d.cts.map +1 -1
  61. package/dist/v2/runtime/intelligence-platform/client.d.mts +112 -0
  62. package/dist/v2/runtime/intelligence-platform/client.d.mts.map +1 -1
  63. package/dist/v2/runtime/intelligence-platform/client.mjs +71 -2
  64. package/dist/v2/runtime/intelligence-platform/client.mjs.map +1 -1
  65. package/dist/v2/runtime/runner/in-memory.cjs +221 -37
  66. package/dist/v2/runtime/runner/in-memory.cjs.map +1 -1
  67. package/dist/v2/runtime/runner/in-memory.d.cts +164 -3
  68. package/dist/v2/runtime/runner/in-memory.d.cts.map +1 -1
  69. package/dist/v2/runtime/runner/in-memory.d.mts +164 -3
  70. package/dist/v2/runtime/runner/in-memory.d.mts.map +1 -1
  71. package/dist/v2/runtime/runner/in-memory.mjs +218 -38
  72. package/dist/v2/runtime/runner/in-memory.mjs.map +1 -1
  73. package/dist/v2/runtime/runner/index.d.cts +1 -1
  74. package/dist/v2/runtime/runner/index.d.mts +1 -1
  75. package/dist/v2/runtime/runner/index.mjs +1 -1
  76. package/package.json +2 -2
@@ -6,6 +6,26 @@ let rxjs = require("rxjs");
6
6
  let _ag_ui_client = require("@ag-ui/client");
7
7
 
8
8
  //#region src/v2/runtime/runner/in-memory.ts
9
+ const ɵINMEMORY_DEFAULTS = {
10
+ maxThreads: 1e3,
11
+ maxRunsPerThread: 100,
12
+ maxBytes: 512 * 1024 ** 2
13
+ };
14
+ const EVICTION_GUIDANCE = "[CopilotKit] InMemoryAgentRunner evicted in-memory thread history to stay under memory limits. This runner is bounded and non-durable by design. For durable or production threads, configure an Intelligence backend.";
15
+ const LIMITS_CLOBBER_GUIDANCE = "[CopilotKit] InMemoryAgentRunner was constructed with in-memory limits that differ from the already-configured process-global store; the last-constructed runner's limits apply to ALL in-memory threads (the store is shared per-process). Configure a single consistent set of limits, or use an Intelligence backend for isolated bounds.";
16
+ /**
17
+ * Best-effort approximate byte size of a value, via serialized length.
18
+ * Never throws — returns 0 when the value cannot be serialized. This is an
19
+ * approximation (UTF-16 length, not exact heap bytes), used only for relative
20
+ * accounting against `maxBytes`.
21
+ */
22
+ function ɵestimateBytes(value) {
23
+ try {
24
+ return JSON.stringify(value)?.length ?? 0;
25
+ } catch {
26
+ return 0;
27
+ }
28
+ }
9
29
  var InMemoryEventStore = class {
10
30
  constructor(threadId) {
11
31
  this.threadId = threadId;
@@ -17,21 +37,195 @@ var InMemoryEventStore = class {
17
37
  this.runSubject = null;
18
38
  this.stopRequested = false;
19
39
  this.currentEvents = null;
40
+ this.messagesSnapshot = [];
41
+ this.approxMessagesSnapshotBytes = 0;
42
+ }
43
+ };
44
+ var ɵBoundedThreadStore = class {
45
+ constructor(limits) {
46
+ this.limits = limits;
47
+ this.map = /* @__PURE__ */ new Map();
48
+ this.totalBytes = 0;
49
+ this.warned = false;
50
+ this.limitsExplicitlySet = false;
51
+ this.clobberWarned = false;
52
+ }
53
+ get byteTotal() {
54
+ return this.totalBytes;
55
+ }
56
+ /**
57
+ * Reconfigure the process-global store's bounds. Called by the
58
+ * {@link InMemoryAgentRunner} constructor when limits are passed. Because the
59
+ * store is a per-process singleton, this replaces the bounds for ALL in-memory
60
+ * threads. Emits {@link LIMITS_CLOBBER_GUIDANCE} at most ONCE per store when a
61
+ * SECOND (or later) explicit set arrives whose resolved values differ from the
62
+ * prior explicit set — i.e. a genuine clobber of an already-customized config.
63
+ * The first explicit customization (defaults → custom) is the intended
64
+ * override and never warns; identical re-sets never warn.
65
+ */
66
+ setLimits(limits) {
67
+ if (this.limitsExplicitlySet && !this.clobberWarned && (limits.maxThreads !== this.limits.maxThreads || limits.maxRunsPerThread !== this.limits.maxRunsPerThread || limits.maxBytes !== this.limits.maxBytes)) {
68
+ this.clobberWarned = true;
69
+ try {
70
+ console.warn(LIMITS_CLOBBER_GUIDANCE);
71
+ } catch {}
72
+ }
73
+ this.limitsExplicitlySet = true;
74
+ this.limits = limits;
75
+ }
76
+ get size() {
77
+ return this.map.size;
78
+ }
79
+ /** Re-insert at the tail so Map iteration order stays LRU-first. */
80
+ touchOrder(threadId, store) {
81
+ this.map.delete(threadId);
82
+ this.map.set(threadId, store);
83
+ }
84
+ getOrCreate(threadId) {
85
+ const existing = this.map.get(threadId);
86
+ if (existing) {
87
+ this.touchOrder(threadId, existing);
88
+ return existing;
89
+ }
90
+ const store = new InMemoryEventStore(threadId);
91
+ this.map.set(threadId, store);
92
+ this.evictThreadsIfNeeded(threadId);
93
+ return store;
94
+ }
95
+ get(threadId, opts) {
96
+ const store = this.map.get(threadId);
97
+ if (store && opts.touch) this.touchOrder(threadId, store);
98
+ return store;
99
+ }
100
+ peek(threadId) {
101
+ return this.map.get(threadId);
102
+ }
103
+ /**
104
+ * Evict the least-recently-used thread that is neither running NOR
105
+ * mid-finalization. Returns false if none evictable. The `protect` thread
106
+ * (typically the one just created) is never evicted, so a fresh thread is not
107
+ * immediately dropped when it is the only non-running candidate.
108
+ *
109
+ * A thread is skipped while `isRunning` OR `stopRequested` is set.
110
+ * `stop()` flips `isRunning` to false the moment it aborts the agent, but the
111
+ * run keeps finalizing asynchronously (the abort trips the `catch` in
112
+ * `runAgent`, which later calls `appendRun`). During that window
113
+ * `stopRequested` stays true; evicting the thread then would make the pending
114
+ * `appendRun` hit `if (!store) return` and silently drop the aborted run's
115
+ * history. Guarding on `stopRequested` keeps the thread alive until
116
+ * finalization completes.
117
+ */
118
+ evictOneLru(protect) {
119
+ for (const [threadId, store] of this.map) {
120
+ if (threadId === protect) continue;
121
+ if (store.isRunning || store.stopRequested) continue;
122
+ this.removeThread(threadId, store);
123
+ this.noteEviction();
124
+ return true;
125
+ }
126
+ return false;
127
+ }
128
+ appendRun(threadId, run) {
129
+ const store = this.map.get(threadId);
130
+ if (!store) return;
131
+ if (run.messages.length > 0) {
132
+ this.totalBytes -= store.approxMessagesSnapshotBytes;
133
+ store.messagesSnapshot = run.messages;
134
+ store.approxMessagesSnapshotBytes = ɵestimateBytes(run.messages);
135
+ this.totalBytes += store.approxMessagesSnapshotBytes;
136
+ }
137
+ run.messages = [];
138
+ run.approxMessageBytes = 0;
139
+ run.approxEventBytes = ɵestimateBytes(run.events);
140
+ store.historicRuns.push(run);
141
+ this.totalBytes += run.approxEventBytes;
142
+ this.touchOrder(threadId, store);
143
+ this.enforceRunCap(store);
144
+ this.evictByBytesIfNeeded(threadId);
145
+ }
146
+ enforceRunCap(store) {
147
+ const cap = this.limits.maxRunsPerThread;
148
+ if (!cap || cap === Infinity) return;
149
+ while (store.historicRuns.length > cap) {
150
+ const dropped = store.historicRuns.shift();
151
+ this.totalBytes -= dropped.approxEventBytes ?? 0;
152
+ }
153
+ }
154
+ /**
155
+ * Trim the store back under the byte ceiling by evicting LRU non-running
156
+ * threads. `protect` (the just-appended thread) is never self-evicted, so a
157
+ * fresh run pushes OTHER threads out rather than dropping itself.
158
+ */
159
+ evictByBytesIfNeeded(protect) {
160
+ while (this.totalBytes > this.limits.maxBytes) if (!this.evictOneLru(protect)) break;
161
+ }
162
+ removeThread(threadId, store) {
163
+ for (const run of store.historicRuns) this.totalBytes -= run.approxEventBytes ?? 0;
164
+ this.totalBytes -= store.approxMessagesSnapshotBytes;
165
+ this.map.delete(threadId);
166
+ }
167
+ evictThreadsIfNeeded(protect) {
168
+ while (this.map.size > this.limits.maxThreads) if (!this.evictOneLru(protect)) break;
169
+ }
170
+ noteEviction() {
171
+ if (this.warned) return;
172
+ this.warned = true;
173
+ try {
174
+ console.warn(EVICTION_GUIDANCE);
175
+ } catch {}
176
+ }
177
+ listThreads() {
178
+ const threads = [];
179
+ for (const [threadId, store] of this.map) {
180
+ if (store.historicRuns.length === 0) continue;
181
+ const firstRun = store.historicRuns[0];
182
+ const lastRun = store.historicRuns[store.historicRuns.length - 1];
183
+ threads.push({
184
+ id: threadId,
185
+ name: null,
186
+ agentId: lastRun.agentId,
187
+ organizationId: "",
188
+ createdById: "",
189
+ archived: false,
190
+ createdAt: new Date(firstRun.createdAt).toISOString(),
191
+ updatedAt: new Date(lastRun.createdAt).toISOString()
192
+ });
193
+ }
194
+ return threads.sort((a, b) => new Date(b.updatedAt).getTime() - new Date(a.updatedAt).getTime());
195
+ }
196
+ clear() {
197
+ this.map.clear();
198
+ this.totalBytes = 0;
199
+ this.warned = false;
20
200
  }
21
201
  };
22
- const GLOBAL_STORE = /* @__PURE__ */ new Map();
202
+ /**
203
+ * Process-wide singleton backing every {@link InMemoryAgentRunner}. Exported
204
+ * (with the `ɵ` internal-API prefix) so tests can inspect the exact store the
205
+ * runner writes to; not part of the public API.
206
+ */
207
+ const ɵGLOBAL_STORE = new ɵBoundedThreadStore(ɵINMEMORY_DEFAULTS);
208
+ const sharedStore = ɵGLOBAL_STORE;
23
209
  var InMemoryAgentRunner = class extends require_agent_runner.AgentRunner {
24
- constructor(..._args) {
25
- super(..._args);
210
+ /**
211
+ * @param limits Optional bounds for the process-global in-memory store. Omit
212
+ * for safe defaults ({@link ɵINMEMORY_DEFAULTS}). When multiple runners are
213
+ * constructed with differing limits, the last-constructed wins — in practice
214
+ * the OSS/SSE default construction passes nothing. If a second (or later)
215
+ * runner is constructed with limits that DIFFER from an already-customized
216
+ * store, a one-time `console.warn` is emitted to signal that the shared store's
217
+ * bounds are being clobbered for ALL in-memory threads.
218
+ */
219
+ constructor(limits) {
220
+ super();
26
221
  this.ɵsupportsLocalThreadEndpoints = true;
222
+ if (limits) sharedStore.setLimits({
223
+ ...ɵINMEMORY_DEFAULTS,
224
+ ...limits
225
+ });
27
226
  }
28
227
  run(request) {
29
- let existingStore = GLOBAL_STORE.get(request.threadId);
30
- if (!existingStore) {
31
- existingStore = new InMemoryEventStore(request.threadId);
32
- GLOBAL_STORE.set(request.threadId, existingStore);
33
- }
34
- const store = existingStore;
228
+ const store = sharedStore.getOrCreate(request.threadId);
35
229
  if (store.isRunning) throw new Error("Thread already running");
36
230
  store.isRunning = true;
37
231
  store.currentRunId = request.input.runId;
@@ -93,7 +287,7 @@ var InMemoryAgentRunner = class extends require_agent_runner.AgentRunner {
93
287
  }
94
288
  if (store.currentRunId) {
95
289
  const compactedEvents = (0, _ag_ui_client.compactEvents)(currentRunEvents);
96
- store.historicRuns.push({
290
+ sharedStore.appendRun(request.threadId, {
97
291
  threadId: request.threadId,
98
292
  runId: store.currentRunId,
99
293
  agentId: request.agent.agentId ?? "default",
@@ -111,6 +305,7 @@ var InMemoryAgentRunner = class extends require_agent_runner.AgentRunner {
111
305
  store.isRunning = false;
112
306
  runSubject.complete();
113
307
  nextSubject.complete();
308
+ store.subject = null;
114
309
  } catch (error) {
115
310
  const interruptionMessage = error instanceof Error ? error.message : String(error);
116
311
  const appendedEvents = (0, _copilotkit_shared.finalizeRunEvents)(currentRunEvents, {
@@ -123,7 +318,7 @@ var InMemoryAgentRunner = class extends require_agent_runner.AgentRunner {
123
318
  }
124
319
  if (store.currentRunId && currentRunEvents.length > 0) {
125
320
  const compactedEvents = (0, _ag_ui_client.compactEvents)(currentRunEvents);
126
- store.historicRuns.push({
321
+ sharedStore.appendRun(request.threadId, {
127
322
  threadId: request.threadId,
128
323
  runId: store.currentRunId,
129
324
  agentId: request.agent.agentId ?? "default",
@@ -141,6 +336,7 @@ var InMemoryAgentRunner = class extends require_agent_runner.AgentRunner {
141
336
  store.isRunning = false;
142
337
  runSubject.complete();
143
338
  nextSubject.complete();
339
+ store.subject = null;
144
340
  }
145
341
  };
146
342
  if (prevSubject) prevSubject.subscribe({
@@ -152,7 +348,7 @@ var InMemoryAgentRunner = class extends require_agent_runner.AgentRunner {
152
348
  return runSubject.asObservable();
153
349
  }
154
350
  connect(request) {
155
- const store = GLOBAL_STORE.get(request.threadId);
351
+ const store = sharedStore.get(request.threadId, { touch: true });
156
352
  const connectionSubject = new rxjs.ReplaySubject(Infinity);
157
353
  if (!store) {
158
354
  connectionSubject.complete();
@@ -178,11 +374,11 @@ var InMemoryAgentRunner = class extends require_agent_runner.AgentRunner {
178
374
  return connectionSubject.asObservable();
179
375
  }
180
376
  isRunning(request) {
181
- const store = GLOBAL_STORE.get(request.threadId);
377
+ const store = sharedStore.peek(request.threadId);
182
378
  return Promise.resolve(store?.isRunning ?? false);
183
379
  }
184
380
  stop(request) {
185
- const store = GLOBAL_STORE.get(request.threadId);
381
+ const store = sharedStore.peek(request.threadId);
186
382
  if (!store || !store.isRunning) return Promise.resolve(false);
187
383
  if (store.stopRequested) return Promise.resolve(false);
188
384
  store.stopRequested = true;
@@ -211,23 +407,7 @@ var InMemoryAgentRunner = class extends require_agent_runner.AgentRunner {
211
407
  * `ThreadRecord` so the HTTP handler can use the same response envelope.
212
408
  */
213
409
  listThreads() {
214
- const threads = [];
215
- for (const [threadId, store] of GLOBAL_STORE) {
216
- if (store.historicRuns.length === 0) continue;
217
- const firstRun = store.historicRuns[0];
218
- const lastRun = store.historicRuns[store.historicRuns.length - 1];
219
- threads.push({
220
- id: threadId,
221
- name: null,
222
- agentId: lastRun.agentId,
223
- organizationId: "",
224
- createdById: "",
225
- archived: false,
226
- createdAt: new Date(firstRun.createdAt).toISOString(),
227
- updatedAt: new Date(lastRun.createdAt).toISOString()
228
- });
229
- }
230
- return threads.sort((a, b) => new Date(b.updatedAt).getTime() - new Date(a.updatedAt).getTime());
410
+ return sharedStore.listThreads();
231
411
  }
232
412
  /**
233
413
  * Returns all messages for a thread, using the snapshot captured at the end
@@ -239,9 +419,9 @@ var InMemoryAgentRunner = class extends require_agent_runner.AgentRunner {
239
419
  * with the Intelligence platform's `ThreadMessage` type.
240
420
  */
241
421
  getThreadMessages(threadId) {
242
- const store = GLOBAL_STORE.get(threadId);
243
- if (!store || store.historicRuns.length === 0) return [];
244
- return store.historicRuns[store.historicRuns.length - 1].messages;
422
+ const store = sharedStore.peek(threadId);
423
+ if (!store) return [];
424
+ return [...store.messagesSnapshot];
245
425
  }
246
426
  /**
247
427
  * Returns all AG-UI events for a thread, compacted across historic runs.
@@ -252,7 +432,7 @@ var InMemoryAgentRunner = class extends require_agent_runner.AgentRunner {
252
432
  * late-joining inspector sees matches what this method returns.
253
433
  */
254
434
  getThreadEvents(threadId) {
255
- const store = GLOBAL_STORE.get(threadId);
435
+ const store = sharedStore.peek(threadId);
256
436
  if (!store || store.historicRuns.length === 0) return [];
257
437
  const all = [];
258
438
  for (const run of store.historicRuns) all.push(...run.events);
@@ -274,7 +454,7 @@ var InMemoryAgentRunner = class extends require_agent_runner.AgentRunner {
274
454
  const event = events[i];
275
455
  if (event.type === _ag_ui_client.EventType.STATE_SNAPSHOT) {
276
456
  const snapshot = event.snapshot;
277
- if (snapshot && typeof snapshot === "object") return snapshot;
457
+ if (snapshot && typeof snapshot === "object" && !Array.isArray(snapshot)) return { ...snapshot };
278
458
  return null;
279
459
  }
280
460
  }
@@ -290,10 +470,14 @@ var InMemoryAgentRunner = class extends require_agent_runner.AgentRunner {
290
470
  * not be wiped this way.
291
471
  */
292
472
  clearThreads() {
293
- GLOBAL_STORE.clear();
473
+ sharedStore.clear();
294
474
  }
295
475
  };
296
476
 
297
477
  //#endregion
298
478
  exports.InMemoryAgentRunner = InMemoryAgentRunner;
479
+ exports.ɵBoundedThreadStore = ɵBoundedThreadStore;
480
+ exports.ɵGLOBAL_STORE = ɵGLOBAL_STORE;
481
+ exports.ɵINMEMORY_DEFAULTS = ɵINMEMORY_DEFAULTS;
482
+ exports.ɵestimateBytes = ɵestimateBytes;
299
483
  //# sourceMappingURL=in-memory.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"in-memory.cjs","names":["AgentRunner","EventType","ReplaySubject"],"sources":["../../../../src/v2/runtime/runner/in-memory.ts"],"sourcesContent":["import type {\n AgentRunnerConnectRequest,\n AgentRunnerIsRunningRequest,\n AgentRunnerRunRequest,\n} from \"./agent-runner\";\nimport { AgentRunner } from \"./agent-runner\";\nimport type { AgentRunnerStopRequest } from \"./agent-runner\";\nimport type { Observable } from \"rxjs\";\nimport { ReplaySubject } from \"rxjs\";\nimport type {\n AbstractAgent,\n BaseEvent,\n Message,\n RunStartedEvent,\n StateSnapshotEvent,\n} from \"@ag-ui/client\";\nimport { EventType, compactEvents } from \"@ag-ui/client\";\nimport { finalizeRunEvents } from \"@copilotkit/shared\";\n\ninterface HistoricRun {\n threadId: string;\n runId: string;\n /** ID of the agent that executed this run. */\n agentId: string;\n parentRunId: string | null;\n events: BaseEvent[];\n /**\n * Snapshot of all messages (input + generated) at the end of this run.\n * Used by the local thread-messages fallback endpoint.\n */\n messages: Message[];\n createdAt: number;\n}\n\n/**\n * Lightweight thread summary returned by {@link InMemoryAgentRunner.listThreads}.\n * Shape matches the Intelligence platform's ThreadRecord so the same HTTP\n * response envelope can be used for both backends.\n */\nexport interface InMemoryThread {\n id: string;\n name: string | null;\n agentId: string;\n organizationId: \"\"; // always empty in in-memory mode\n createdById: \"\"; // always empty in in-memory mode\n archived: false; // always false in in-memory mode\n createdAt: string;\n updatedAt: string;\n}\n\nclass InMemoryEventStore {\n constructor(public threadId: string) {}\n\n /** The subject that current consumers subscribe to. */\n subject: ReplaySubject<BaseEvent> | null = null;\n\n /** True while a run is actively producing events. */\n isRunning = false;\n\n /** Current run ID */\n currentRunId: string | null = null;\n\n /** Historic completed runs */\n historicRuns: HistoricRun[] = [];\n\n /** Currently running agent instance (if any). */\n agent: AbstractAgent | null = null;\n\n /** Subject returned from run() while the run is active. */\n runSubject: ReplaySubject<BaseEvent> | null = null;\n\n /** True once stop() has been requested but the run has not yet finalized. */\n stopRequested = false;\n\n /** Reference to the events emitted in the current run. */\n currentEvents: BaseEvent[] | null = null;\n}\n\nconst GLOBAL_STORE = new Map<string, InMemoryEventStore>();\n\nexport class InMemoryAgentRunner extends AgentRunner {\n readonly ɵsupportsLocalThreadEndpoints = true;\n\n run(request: AgentRunnerRunRequest): Observable<BaseEvent> {\n let existingStore = GLOBAL_STORE.get(request.threadId);\n if (!existingStore) {\n existingStore = new InMemoryEventStore(request.threadId);\n GLOBAL_STORE.set(request.threadId, existingStore);\n }\n const store = existingStore; // Now store is const and non-null\n\n if (store.isRunning) {\n throw new Error(\"Thread already running\");\n }\n store.isRunning = true;\n store.currentRunId = request.input.runId;\n store.agent = request.agent;\n store.stopRequested = false;\n\n // Track seen message IDs and current run events for this run\n const seenMessageIds = new Set<string>();\n const currentRunEvents: BaseEvent[] = [];\n store.currentEvents = currentRunEvents;\n\n // Get all previously seen message IDs from historic runs\n const historicMessageIds = new Set<string>();\n for (const run of store.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 const nextSubject = new ReplaySubject<BaseEvent>(Infinity);\n const prevSubject = store.subject;\n\n // Update the store's subject immediately\n store.subject = nextSubject;\n\n // Create a subject for run() return value\n const runSubject = new ReplaySubject<BaseEvent>(Infinity);\n store.runSubject = runSubject;\n\n // Helper function to run the agent and handle errors\n const runAgent = async () => {\n // Get parent run ID for chaining\n const lastRun = store.historicRuns[store.historicRuns.length - 1];\n const parentRunId = lastRun?.runId ?? null;\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 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 any messages from the input as seen so they aren't emitted twice\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 appendedEvents = finalizeRunEvents(currentRunEvents, {\n stopRequested: store.stopRequested,\n });\n for (const event of appendedEvents) {\n runSubject.next(event);\n nextSubject.next(event);\n }\n\n // Store the completed run in memory with ONLY its events\n if (store.currentRunId) {\n // Compact the events before storing (like SQLite does)\n const compactedEvents = compactEvents(currentRunEvents);\n\n store.historicRuns.push({\n threadId: request.threadId,\n runId: store.currentRunId,\n agentId: request.agent.agentId ?? \"default\",\n parentRunId,\n events: compactedEvents,\n // Snapshot all messages (input + generated) for the thread-messages endpoint\n messages: Array.isArray(request.agent.messages)\n ? [...request.agent.messages]\n : [],\n createdAt: Date.now(),\n });\n }\n\n // Complete the run\n store.currentEvents = null;\n store.currentRunId = null;\n store.agent = null;\n store.runSubject = null;\n store.stopRequested = false;\n store.isRunning = false;\n runSubject.complete();\n nextSubject.complete();\n } catch (error) {\n const interruptionMessage =\n error instanceof Error ? error.message : String(error);\n const appendedEvents = finalizeRunEvents(currentRunEvents, {\n stopRequested: store.stopRequested,\n interruptionMessage,\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 (store.currentRunId && currentRunEvents.length > 0) {\n // Compact the events before storing (like SQLite does)\n const compactedEvents = compactEvents(currentRunEvents);\n store.historicRuns.push({\n threadId: request.threadId,\n runId: store.currentRunId,\n agentId: request.agent.agentId ?? \"default\",\n parentRunId,\n events: compactedEvents,\n messages: Array.isArray(request.agent.messages)\n ? [...request.agent.messages]\n : [],\n createdAt: Date.now(),\n });\n }\n\n // Complete the run\n store.currentEvents = null;\n store.currentRunId = null;\n store.agent = null;\n store.runSubject = null;\n store.stopRequested = false;\n store.isRunning = false;\n runSubject.complete();\n nextSubject.complete();\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 store = GLOBAL_STORE.get(request.threadId);\n const connectionSubject = new ReplaySubject<BaseEvent>(Infinity);\n\n if (!store) {\n // No store means no events\n connectionSubject.complete();\n return connectionSubject.asObservable();\n }\n\n // Collect all historic events from memory\n const allHistoricEvents: BaseEvent[] = [];\n for (const run of store.historicRuns) {\n allHistoricEvents.push(...run.events);\n }\n\n // Apply compaction to all historic events together (like SQLite)\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 if (store.subject && (store.isRunning || store.stopRequested)) {\n store.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 store = GLOBAL_STORE.get(request.threadId);\n return Promise.resolve(store?.isRunning ?? false);\n }\n\n stop(request: AgentRunnerStopRequest): Promise<boolean | undefined> {\n const store = GLOBAL_STORE.get(request.threadId);\n if (!store || !store.isRunning) {\n return Promise.resolve(false);\n }\n if (store.stopRequested) {\n return Promise.resolve(false);\n }\n\n store.stopRequested = true;\n store.isRunning = false;\n\n const agent = store.agent;\n if (!agent) {\n store.stopRequested = false;\n store.isRunning = false;\n return Promise.resolve(false);\n }\n\n try {\n agent.abortRun();\n return Promise.resolve(true);\n } catch (error) {\n console.error(\"Failed to abort agent run\", error);\n store.stopRequested = false;\n store.isRunning = true;\n return Promise.resolve(false);\n }\n }\n\n /**\n * Returns a summary of every thread that has been run through this runner.\n *\n * This powers the local-dev fallback for `GET /threads` when the Intelligence\n * platform is not configured. Each entry mirrors the shape of a platform\n * `ThreadRecord` so the HTTP handler can use the same response envelope.\n */\n listThreads(): InMemoryThread[] {\n const threads: InMemoryThread[] = [];\n for (const [threadId, store] of GLOBAL_STORE) {\n if (store.historicRuns.length === 0) continue;\n const firstRun = store.historicRuns[0]!;\n const lastRun = store.historicRuns[store.historicRuns.length - 1]!;\n threads.push({\n id: threadId,\n name: null,\n agentId: lastRun.agentId,\n organizationId: \"\",\n createdById: \"\",\n archived: false,\n createdAt: new Date(firstRun.createdAt).toISOString(),\n updatedAt: new Date(lastRun.createdAt).toISOString(),\n });\n }\n // Most recently updated first\n return threads.sort(\n (a, b) =>\n new Date(b.updatedAt).getTime() - new Date(a.updatedAt).getTime(),\n );\n }\n\n /**\n * Returns all messages for a thread, using the snapshot captured at the end\n * of the most recent run.\n *\n * This powers the local-dev fallback for `GET /threads/:threadId/messages`\n * when the Intelligence platform is not configured. The returned `Message[]`\n * objects come directly from the ag-ui agent, so their shape is compatible\n * with the Intelligence platform's `ThreadMessage` type.\n */\n getThreadMessages(threadId: string): Message[] {\n const store = GLOBAL_STORE.get(threadId);\n if (!store || store.historicRuns.length === 0) return [];\n // The last run's snapshot has the complete conversation history\n return store.historicRuns[store.historicRuns.length - 1]!.messages;\n }\n\n /**\n * Returns all AG-UI events for a thread, compacted across historic runs.\n *\n * Powers the local-dev fallback for `GET /threads/:threadId/events` when the\n * Intelligence platform is not configured. The compaction logic matches\n * the connection-replay path in {@link connect}, so the stream a\n * late-joining inspector sees matches what this method returns.\n */\n getThreadEvents(threadId: string): BaseEvent[] {\n const store = GLOBAL_STORE.get(threadId);\n if (!store || store.historicRuns.length === 0) return [];\n const all: BaseEvent[] = [];\n for (const run of store.historicRuns) all.push(...run.events);\n return compactEvents(all);\n }\n\n /**\n * Returns the agent state snapshot for a thread.\n *\n * Derived from the last `STATE_SNAPSHOT` in the compacted event stream. The\n * AG-UI `compactEvents` helper consolidates STATE_DELTA events and produces\n * a single trailing STATE_SNAPSHOT when state changes exist, so this is a\n * faithful view of state at the end of the most recent run.\n *\n * Returns `null` when the thread has never emitted a STATE_SNAPSHOT.\n */\n getThreadState(threadId: string): Record<string, unknown> | null {\n const events = this.getThreadEvents(threadId);\n // Walk backwards — the last snapshot wins.\n for (let i = events.length - 1; i >= 0; i--) {\n const event = events[i]!;\n if (event.type === EventType.STATE_SNAPSHOT) {\n const snapshot = (event as StateSnapshotEvent).snapshot;\n if (snapshot && typeof snapshot === \"object\") {\n return snapshot as Record<string, unknown>;\n }\n return null;\n }\n }\n return null;\n }\n\n /**\n * Clears all in-memory thread history.\n *\n * Powers the local-dev fallback for `POST /threads/clear`, letting consumers\n * (e.g. the demo's Clear button) reset to an empty thread list without\n * restarting the runtime. Intentionally not exposed on the Intelligence\n * platform path: there, thread history lives in a real database and must\n * not be wiped this way.\n */\n clearThreads(): void {\n GLOBAL_STORE.clear();\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"in-memory.cjs","names":["AgentRunner","EventType","ReplaySubject"],"sources":["../../../../src/v2/runtime/runner/in-memory.ts"],"sourcesContent":["import type {\n AgentRunnerConnectRequest,\n AgentRunnerIsRunningRequest,\n AgentRunnerRunRequest,\n} from \"./agent-runner\";\nimport { AgentRunner } from \"./agent-runner\";\nimport type { AgentRunnerStopRequest } from \"./agent-runner\";\nimport type { Observable } from \"rxjs\";\nimport { ReplaySubject } from \"rxjs\";\nimport type {\n AbstractAgent,\n BaseEvent,\n Message,\n RunStartedEvent,\n StateSnapshotEvent,\n} from \"@ag-ui/client\";\nimport { EventType, compactEvents } from \"@ag-ui/client\";\nimport { finalizeRunEvents } from \"@copilotkit/shared\";\n\nexport interface InMemoryLimits {\n /** LRU cap on distinct threads. */\n maxThreads?: number;\n /** FIFO cap on runs kept per thread. `Infinity` or `0` disables the cap. */\n maxRunsPerThread?: number;\n /**\n * Approximate byte ceiling on RETAINED thread/run history. Enforced at run\n * completion (in `appendRun`), where LRU non-running threads are evicted to\n * keep the total under this limit.\n *\n * Limitation: this bounds only history that has already been committed. A\n * single in-flight run's buffered events (`currentRunEvents` and the two\n * `ReplaySubject<BaseEvent>(Infinity)` buffers in `run()`) are NOT counted\n * until that run completes, so `maxBytes` does not bound a single runaway\n * run mid-stream.\n *\n * Limitation: byte eviction drops only other LRU non-running threads and\n * never self-evicts the active/just-appended thread, so a single dominant\n * thread's own retained history is not byte-trimmed (bounded only by\n * `maxRunsPerThread`). `maxBytes` is thus a cross-thread ceiling enforced by\n * evicting OTHER threads, not a per-thread cap.\n */\n maxBytes?: number;\n}\n\nexport const ɵINMEMORY_DEFAULTS: Required<InMemoryLimits> = {\n maxThreads: 1000,\n maxRunsPerThread: 100,\n maxBytes: 512 * 1024 ** 2,\n};\n\nconst EVICTION_GUIDANCE =\n \"[CopilotKit] InMemoryAgentRunner evicted in-memory thread history to stay \" +\n \"under memory limits. This runner is bounded and non-durable by design. For \" +\n \"durable or production threads, configure an Intelligence backend.\";\n\nconst LIMITS_CLOBBER_GUIDANCE =\n \"[CopilotKit] InMemoryAgentRunner was constructed with in-memory limits that \" +\n \"differ from the already-configured process-global store; the last-constructed \" +\n \"runner's limits apply to ALL in-memory threads (the store is shared per-process). \" +\n \"Configure a single consistent set of limits, or use an Intelligence backend for \" +\n \"isolated bounds.\";\n\n/**\n * Best-effort approximate byte size of a value, via serialized length.\n * Never throws — returns 0 when the value cannot be serialized. This is an\n * approximation (UTF-16 length, not exact heap bytes), used only for relative\n * accounting against `maxBytes`.\n */\nexport function ɵestimateBytes(value: unknown): number {\n try {\n return JSON.stringify(value)?.length ?? 0;\n } catch {\n return 0;\n }\n}\n\ninterface HistoricRun {\n threadId: string;\n runId: string;\n /** ID of the agent that executed this run. */\n agentId: string;\n parentRunId: string | null;\n events: BaseEvent[];\n /**\n * Snapshot of all messages (input + generated) at the end of this run, as\n * passed in by the caller. NOTE: `BoundedThreadStore.appendRun` moves this\n * snapshot to the THREAD level (`InMemoryEventStore.messagesSnapshot`) and\n * clears this field to `[]`, so a stored HistoricRun never carries messages.\n * The thread-messages fallback reads the thread-level snapshot, not this.\n */\n messages: Message[];\n createdAt: number;\n /** Approximate retained byte size of `events`; set by BoundedThreadStore at append. */\n approxEventBytes?: number;\n /**\n * Legacy field retained for shape compatibility. `appendRun` always zeroes it\n * because message bytes are accounted at the thread level, not per run.\n */\n approxMessageBytes?: number;\n}\n\n/**\n * Lightweight thread summary returned by {@link InMemoryAgentRunner.listThreads}.\n * Shape matches the Intelligence platform's ThreadRecord so the same HTTP\n * response envelope can be used for both backends.\n */\nexport interface InMemoryThread {\n id: string;\n name: string | null;\n agentId: string;\n organizationId: \"\"; // always empty in in-memory mode\n createdById: \"\"; // always empty in in-memory mode\n archived: false; // always false in in-memory mode\n createdAt: string;\n updatedAt: string;\n}\n\nclass InMemoryEventStore {\n constructor(public threadId: string) {}\n\n /** The subject that current consumers subscribe to. */\n subject: ReplaySubject<BaseEvent> | null = null;\n\n /** True while a run is actively producing events. */\n isRunning = false;\n\n /** Current run ID */\n currentRunId: string | null = null;\n\n /** Historic completed runs */\n historicRuns: HistoricRun[] = [];\n\n /** Currently running agent instance (if any). */\n agent: AbstractAgent | null = null;\n\n /** Subject returned from run() while the run is active. */\n runSubject: ReplaySubject<BaseEvent> | null = null;\n\n /** True once stop() has been requested but the run has not yet finalized. */\n stopRequested = false;\n\n /** Reference to the events emitted in the current run. */\n currentEvents: BaseEvent[] | null = null;\n\n /**\n * The thread's single latest NON-EMPTY message snapshot, held at the THREAD\n * level (independent of `historicRuns` lifecycle). Decoupling the snapshot\n * from per-run storage means run-cap FIFO eviction and interleaved\n * empty-snapshot runs can never drop or pin the thread's message history.\n */\n messagesSnapshot: Message[] = [];\n\n /** Approximate retained byte size of `messagesSnapshot`. */\n approxMessagesSnapshotBytes = 0;\n}\n\nexport class ɵBoundedThreadStore {\n private readonly map = new Map<string, InMemoryEventStore>();\n private totalBytes = 0;\n private warned = false;\n /** True once limits have been EXPLICITLY set (via setLimits), not just the constructor default. */\n private limitsExplicitlySet = false;\n /** Warn-once latch for the clobber warning, kept distinct from the eviction `warned` latch. */\n private clobberWarned = false;\n\n constructor(private limits: Required<InMemoryLimits>) {}\n\n get byteTotal(): number {\n return this.totalBytes;\n }\n\n /**\n * Reconfigure the process-global store's bounds. Called by the\n * {@link InMemoryAgentRunner} constructor when limits are passed. Because the\n * store is a per-process singleton, this replaces the bounds for ALL in-memory\n * threads. Emits {@link LIMITS_CLOBBER_GUIDANCE} at most ONCE per store when a\n * SECOND (or later) explicit set arrives whose resolved values differ from the\n * prior explicit set — i.e. a genuine clobber of an already-customized config.\n * The first explicit customization (defaults → custom) is the intended\n * override and never warns; identical re-sets never warn.\n */\n setLimits(limits: Required<InMemoryLimits>): void {\n if (\n this.limitsExplicitlySet &&\n !this.clobberWarned &&\n (limits.maxThreads !== this.limits.maxThreads ||\n limits.maxRunsPerThread !== this.limits.maxRunsPerThread ||\n limits.maxBytes !== this.limits.maxBytes)\n ) {\n this.clobberWarned = true;\n try {\n console.warn(LIMITS_CLOBBER_GUIDANCE);\n } catch {\n // best-effort: logging must never break construction\n }\n }\n this.limitsExplicitlySet = true;\n this.limits = limits;\n }\n\n get size(): number {\n return this.map.size;\n }\n\n /** Re-insert at the tail so Map iteration order stays LRU-first. */\n private touchOrder(threadId: string, store: InMemoryEventStore): void {\n this.map.delete(threadId);\n this.map.set(threadId, store);\n }\n\n getOrCreate(threadId: string): InMemoryEventStore {\n const existing = this.map.get(threadId);\n if (existing) {\n this.touchOrder(threadId, existing);\n return existing;\n }\n const store = new InMemoryEventStore(threadId);\n this.map.set(threadId, store);\n this.evictThreadsIfNeeded(threadId);\n return store;\n }\n\n get(\n threadId: string,\n opts: { touch: boolean },\n ): InMemoryEventStore | undefined {\n const store = this.map.get(threadId);\n if (store && opts.touch) this.touchOrder(threadId, store);\n return store;\n }\n\n peek(threadId: string): InMemoryEventStore | undefined {\n return this.map.get(threadId);\n }\n\n /**\n * Evict the least-recently-used thread that is neither running NOR\n * mid-finalization. Returns false if none evictable. The `protect` thread\n * (typically the one just created) is never evicted, so a fresh thread is not\n * immediately dropped when it is the only non-running candidate.\n *\n * A thread is skipped while `isRunning` OR `stopRequested` is set.\n * `stop()` flips `isRunning` to false the moment it aborts the agent, but the\n * run keeps finalizing asynchronously (the abort trips the `catch` in\n * `runAgent`, which later calls `appendRun`). During that window\n * `stopRequested` stays true; evicting the thread then would make the pending\n * `appendRun` hit `if (!store) return` and silently drop the aborted run's\n * history. Guarding on `stopRequested` keeps the thread alive until\n * finalization completes.\n */\n private evictOneLru(protect?: string): boolean {\n for (const [threadId, store] of this.map) {\n if (threadId === protect) continue; // never evict the just-created thread\n // never evict a running or still-finalizing (stop-requested) thread\n if (store.isRunning || store.stopRequested) continue;\n this.removeThread(threadId, store);\n this.noteEviction();\n return true;\n }\n return false;\n }\n\n appendRun(threadId: string, run: HistoricRun): void {\n const store = this.map.get(threadId);\n if (!store) return; // best-effort: nothing to append to\n\n // Thread-level message snapshot: keep the single latest NON-EMPTY snapshot\n // on the store, decoupled from `historicRuns`. When the incoming run\n // carries a non-empty snapshot, replace the thread's snapshot (adjusting\n // byte accounting). When it's empty (non-array `agent.messages` or an\n // error-path run), leave the existing thread snapshot untouched so history\n // is never lost. The snapshot never lives on a HistoricRun, so run-cap FIFO\n // eviction can never drop it and an interleaved empty run can never pin it.\n if (run.messages.length > 0) {\n this.totalBytes -= store.approxMessagesSnapshotBytes;\n // Store the incoming array directly (SHALLOW, array-level copy). `run.messages`\n // is already a fresh `[...agent.messages]` array created in run(), so we own the\n // array and it is decoupled from `agent.messages` at the array level (push/splice\n // on the agent's array cannot mutate our snapshot). We deliberately do NOT deep-copy\n // here: `structuredClone` throws DataCloneError on a non-cloneable message field,\n // which would wedge the thread and hang SSE — inconsistent with `ɵestimateBytes`,\n // which tolerates the same bad-payload class. The tradeoff is that the inner\n // `Message` objects remain shared by reference with `agent.messages`, so an agent\n // that mutates its own message objects IN PLACE after the run can still be observed\n // through this snapshot. That inner-object isolation is a known limitation tracked as\n // follow-up; callers must treat returned messages as read-only. Estimate bytes on the\n // same value so accounting matches exactly what is retained.\n store.messagesSnapshot = run.messages;\n store.approxMessagesSnapshotBytes = ɵestimateBytes(run.messages);\n this.totalBytes += store.approxMessagesSnapshotBytes;\n }\n\n // Do not carry message bytes on the HistoricRun: the snapshot is now tracked\n // at the thread level, so historicRuns must never account message bytes.\n run.messages = [];\n run.approxMessageBytes = 0;\n\n // Compute this run's approximate event size once, at append time.\n run.approxEventBytes = ɵestimateBytes(run.events);\n store.historicRuns.push(run);\n this.totalBytes += run.approxEventBytes;\n this.touchOrder(threadId, store);\n\n this.enforceRunCap(store);\n this.evictByBytesIfNeeded(threadId);\n }\n\n private enforceRunCap(store: InMemoryEventStore): void {\n const cap = this.limits.maxRunsPerThread;\n if (!cap || cap === Infinity) return; // 0 or Infinity → disabled\n while (store.historicRuns.length > cap) {\n const dropped = store.historicRuns.shift()!;\n // Only event bytes live on a HistoricRun; the message snapshot is tracked\n // at the thread level and survives run-cap eviction.\n this.totalBytes -= dropped.approxEventBytes ?? 0;\n }\n }\n\n /**\n * Trim the store back under the byte ceiling by evicting LRU non-running\n * threads. `protect` (the just-appended thread) is never self-evicted, so a\n * fresh run pushes OTHER threads out rather than dropping itself.\n */\n private evictByBytesIfNeeded(protect?: string): void {\n while (this.totalBytes > this.limits.maxBytes) {\n if (!this.evictOneLru(protect)) break; // only protected/running threads left → accept overage\n }\n }\n\n private removeThread(threadId: string, store: InMemoryEventStore): void {\n for (const run of store.historicRuns) {\n this.totalBytes -= run.approxEventBytes ?? 0;\n }\n // The thread's message snapshot is tracked at the store level, so it must\n // be reclaimed here in addition to the per-run event bytes.\n this.totalBytes -= store.approxMessagesSnapshotBytes;\n this.map.delete(threadId);\n }\n\n private evictThreadsIfNeeded(protect?: string): void {\n while (this.map.size > this.limits.maxThreads) {\n if (!this.evictOneLru(protect)) break; // everything evictable is running → accept overage\n }\n }\n\n private noteEviction(): void {\n if (this.warned) return;\n this.warned = true;\n try {\n console.warn(EVICTION_GUIDANCE);\n } catch {\n // best-effort: logging must never break a run\n }\n }\n\n listThreads(): InMemoryThread[] {\n const threads: InMemoryThread[] = [];\n for (const [threadId, store] of this.map) {\n if (store.historicRuns.length === 0) continue;\n const firstRun = store.historicRuns[0]!;\n const lastRun = store.historicRuns[store.historicRuns.length - 1]!;\n threads.push({\n id: threadId,\n name: null,\n agentId: lastRun.agentId,\n organizationId: \"\",\n createdById: \"\",\n archived: false,\n createdAt: new Date(firstRun.createdAt).toISOString(),\n updatedAt: new Date(lastRun.createdAt).toISOString(),\n });\n }\n return threads.sort(\n (a, b) =>\n new Date(b.updatedAt).getTime() - new Date(a.updatedAt).getTime(),\n );\n }\n\n clear(): void {\n this.map.clear();\n this.totalBytes = 0;\n this.warned = false;\n }\n}\n\n/**\n * Process-wide singleton backing every {@link InMemoryAgentRunner}. Exported\n * (with the `ɵ` internal-API prefix) so tests can inspect the exact store the\n * runner writes to; not part of the public API.\n */\nexport const ɵGLOBAL_STORE = new ɵBoundedThreadStore(ɵINMEMORY_DEFAULTS);\nconst sharedStore = ɵGLOBAL_STORE;\n\nexport class InMemoryAgentRunner extends AgentRunner {\n readonly ɵsupportsLocalThreadEndpoints = true;\n\n /**\n * @param limits Optional bounds for the process-global in-memory store. Omit\n * for safe defaults ({@link ɵINMEMORY_DEFAULTS}). When multiple runners are\n * constructed with differing limits, the last-constructed wins — in practice\n * the OSS/SSE default construction passes nothing. If a second (or later)\n * runner is constructed with limits that DIFFER from an already-customized\n * store, a one-time `console.warn` is emitted to signal that the shared store's\n * bounds are being clobbered for ALL in-memory threads.\n */\n constructor(limits?: InMemoryLimits) {\n super();\n if (limits) {\n sharedStore.setLimits({ ...ɵINMEMORY_DEFAULTS, ...limits });\n }\n }\n\n run(request: AgentRunnerRunRequest): Observable<BaseEvent> {\n const store = sharedStore.getOrCreate(request.threadId);\n\n if (store.isRunning) {\n throw new Error(\"Thread already running\");\n }\n store.isRunning = true;\n store.currentRunId = request.input.runId;\n store.agent = request.agent;\n store.stopRequested = false;\n\n // Track seen message IDs and current run events for this run\n const seenMessageIds = new Set<string>();\n const currentRunEvents: BaseEvent[] = [];\n store.currentEvents = currentRunEvents;\n\n // Get all previously seen message IDs from historic runs\n const historicMessageIds = new Set<string>();\n for (const run of store.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 const nextSubject = new ReplaySubject<BaseEvent>(Infinity);\n const prevSubject = store.subject;\n\n // Update the store's subject immediately\n store.subject = nextSubject;\n\n // Create a subject for run() return value\n const runSubject = new ReplaySubject<BaseEvent>(Infinity);\n store.runSubject = runSubject;\n\n // Helper function to run the agent and handle errors\n const runAgent = async () => {\n // Get parent run ID for chaining\n const lastRun = store.historicRuns[store.historicRuns.length - 1];\n const parentRunId = lastRun?.runId ?? null;\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 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 any messages from the input as seen so they aren't emitted twice\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 appendedEvents = finalizeRunEvents(currentRunEvents, {\n stopRequested: store.stopRequested,\n });\n for (const event of appendedEvents) {\n runSubject.next(event);\n nextSubject.next(event);\n }\n\n // Store the completed run in memory with ONLY its events\n if (store.currentRunId) {\n // Compact the events before storing (like SQLite does)\n const compactedEvents = compactEvents(currentRunEvents);\n\n sharedStore.appendRun(request.threadId, {\n threadId: request.threadId,\n runId: store.currentRunId,\n agentId: request.agent.agentId ?? \"default\",\n parentRunId,\n events: compactedEvents,\n // Snapshot all messages (input + generated) for the thread-messages endpoint\n messages: Array.isArray(request.agent.messages)\n ? [...request.agent.messages]\n : [],\n createdAt: Date.now(),\n });\n }\n\n // Complete the run\n store.currentEvents = null;\n store.currentRunId = null;\n store.agent = null;\n store.runSubject = null;\n store.stopRequested = false;\n store.isRunning = false;\n runSubject.complete();\n nextSubject.complete();\n // Time-scoped release: events are now in historicRuns, so the infinite\n // ReplaySubject buffers are pure duplication — drop them so they become\n // collectable. connect() only subscribes to store.subject while\n // isRunning || stopRequested (both false here), and rebuilds history\n // from historicRuns, so this is safe.\n store.subject = null;\n } catch (error) {\n const interruptionMessage =\n error instanceof Error ? error.message : String(error);\n const appendedEvents = finalizeRunEvents(currentRunEvents, {\n stopRequested: store.stopRequested,\n interruptionMessage,\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 (store.currentRunId && currentRunEvents.length > 0) {\n // Compact the events before storing (like SQLite does)\n const compactedEvents = compactEvents(currentRunEvents);\n sharedStore.appendRun(request.threadId, {\n threadId: request.threadId,\n runId: store.currentRunId,\n agentId: request.agent.agentId ?? \"default\",\n parentRunId,\n events: compactedEvents,\n messages: Array.isArray(request.agent.messages)\n ? [...request.agent.messages]\n : [],\n createdAt: Date.now(),\n });\n }\n\n // Complete the run\n store.currentEvents = null;\n store.currentRunId = null;\n store.agent = null;\n store.runSubject = null;\n store.stopRequested = false;\n store.isRunning = false;\n runSubject.complete();\n nextSubject.complete();\n // Time-scoped release: events are now in historicRuns, so the infinite\n // ReplaySubject buffers are pure duplication — drop them so they become\n // collectable. connect() only subscribes to store.subject while\n // isRunning || stopRequested (both false here), and rebuilds history\n // from historicRuns, so this is safe.\n store.subject = null;\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 store = sharedStore.get(request.threadId, { touch: true });\n const connectionSubject = new ReplaySubject<BaseEvent>(Infinity);\n\n if (!store) {\n // No store means no events\n connectionSubject.complete();\n return connectionSubject.asObservable();\n }\n\n // Collect all historic events from memory\n const allHistoricEvents: BaseEvent[] = [];\n for (const run of store.historicRuns) {\n allHistoricEvents.push(...run.events);\n }\n\n // Apply compaction to all historic events together (like SQLite)\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 if (store.subject && (store.isRunning || store.stopRequested)) {\n store.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 store = sharedStore.peek(request.threadId);\n return Promise.resolve(store?.isRunning ?? false);\n }\n\n stop(request: AgentRunnerStopRequest): Promise<boolean | undefined> {\n const store = sharedStore.peek(request.threadId);\n if (!store || !store.isRunning) {\n return Promise.resolve(false);\n }\n if (store.stopRequested) {\n return Promise.resolve(false);\n }\n\n store.stopRequested = true;\n store.isRunning = false;\n\n const agent = store.agent;\n if (!agent) {\n store.stopRequested = false;\n store.isRunning = false;\n return Promise.resolve(false);\n }\n\n try {\n agent.abortRun();\n return Promise.resolve(true);\n } catch (error) {\n console.error(\"Failed to abort agent run\", error);\n store.stopRequested = false;\n store.isRunning = true;\n return Promise.resolve(false);\n }\n }\n\n /**\n * Returns a summary of every thread that has been run through this runner.\n *\n * This powers the local-dev fallback for `GET /threads` when the Intelligence\n * platform is not configured. Each entry mirrors the shape of a platform\n * `ThreadRecord` so the HTTP handler can use the same response envelope.\n */\n listThreads(): InMemoryThread[] {\n return sharedStore.listThreads();\n }\n\n /**\n * Returns all messages for a thread, using the snapshot captured at the end\n * of the most recent run.\n *\n * This powers the local-dev fallback for `GET /threads/:threadId/messages`\n * when the Intelligence platform is not configured. The returned `Message[]`\n * objects come directly from the ag-ui agent, so their shape is compatible\n * with the Intelligence platform's `ThreadMessage` type.\n */\n getThreadMessages(threadId: string): Message[] {\n const store = sharedStore.peek(threadId);\n if (!store) return [];\n // The thread's latest non-empty snapshot is held at the store level,\n // independent of `historicRuns` lifecycle, so run-cap eviction and\n // interleaved empty-snapshot runs can never lose it. Return a SHALLOW\n // (array-level) copy: a fresh array so a caller mutating array STRUCTURE\n // (push/splice/reassign elements) cannot affect the stored snapshot. We\n // deliberately do NOT deep-copy: `structuredClone` throws DataCloneError on a\n // non-cloneable message field, which would wedge the thread and hang SSE —\n // inconsistent with `ɵestimateBytes`, which tolerates the same bad-payload class.\n // The tradeoff is that the inner `Message` objects remain shared by reference with\n // the stored snapshot, so mutating a returned message's FIELD\n // (e.g. `getThreadMessages(t)[0].content = \"x\"`) is NOT isolated and would corrupt\n // the stored snapshot. That inner-object isolation is a known limitation tracked as\n // follow-up; callers must treat returned messages as read-only.\n return [...store.messagesSnapshot];\n }\n\n /**\n * Returns all AG-UI events for a thread, compacted across historic runs.\n *\n * Powers the local-dev fallback for `GET /threads/:threadId/events` when the\n * Intelligence platform is not configured. The compaction logic matches\n * the connection-replay path in {@link connect}, so the stream a\n * late-joining inspector sees matches what this method returns.\n */\n getThreadEvents(threadId: string): BaseEvent[] {\n const store = sharedStore.peek(threadId);\n if (!store || store.historicRuns.length === 0) return [];\n const all: BaseEvent[] = [];\n for (const run of store.historicRuns) all.push(...run.events);\n return compactEvents(all);\n }\n\n /**\n * Returns the agent state snapshot for a thread.\n *\n * Derived from the last `STATE_SNAPSHOT` in the compacted event stream. The\n * AG-UI `compactEvents` helper consolidates STATE_DELTA events and produces\n * a single trailing STATE_SNAPSHOT when state changes exist, so this is a\n * faithful view of state at the end of the most recent run.\n *\n * Returns `null` when the thread has never emitted a STATE_SNAPSHOT.\n */\n getThreadState(threadId: string): Record<string, unknown> | null {\n const events = this.getThreadEvents(threadId);\n // Walk backwards — the last snapshot wins.\n for (let i = events.length - 1; i >= 0; i--) {\n const event = events[i]!;\n if (event.type === EventType.STATE_SNAPSHOT) {\n const snapshot = (event as StateSnapshotEvent).snapshot;\n // Only plain objects satisfy the Record<string, unknown> contract.\n // `typeof [] === \"object\"` is true, so arrays must be rejected\n // explicitly to avoid returning an array typed as a Record.\n if (\n snapshot &&\n typeof snapshot === \"object\" &&\n !Array.isArray(snapshot)\n ) {\n // Return a defensive shallow copy so callers can't mutate the\n // snapshot object held inside the stored event (matches the\n // getThreadMessages defensive-copy approach).\n return { ...(snapshot as Record<string, unknown>) };\n }\n return null;\n }\n }\n return null;\n }\n\n /**\n * Clears all in-memory thread history.\n *\n * Powers the local-dev fallback for `POST /threads/clear`, letting consumers\n * (e.g. the demo's Clear button) reset to an empty thread list without\n * restarting the runtime. Intentionally not exposed on the Intelligence\n * platform path: there, thread history lives in a real database and must\n * not be wiped this way.\n */\n clearThreads(): void {\n sharedStore.clear();\n }\n}\n"],"mappings":"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"}