@copilotkit/runtime 1.66.2 → 1.66.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agent/converters/aisdk.cjs +17 -9
- package/dist/agent/converters/aisdk.cjs.map +1 -1
- package/dist/agent/converters/aisdk.d.cts +1 -1
- package/dist/agent/converters/aisdk.d.cts.map +1 -1
- package/dist/agent/converters/aisdk.d.mts +1 -1
- package/dist/agent/converters/aisdk.d.mts.map +1 -1
- package/dist/agent/converters/aisdk.mjs +17 -9
- package/dist/agent/converters/aisdk.mjs.map +1 -1
- package/dist/agent/converters/tanstack.cjs +17 -9
- package/dist/agent/converters/tanstack.cjs.map +1 -1
- package/dist/agent/converters/tanstack.d.cts +1 -1
- package/dist/agent/converters/tanstack.d.cts.map +1 -1
- package/dist/agent/converters/tanstack.d.mts +1 -1
- package/dist/agent/converters/tanstack.d.mts.map +1 -1
- package/dist/agent/converters/tanstack.mjs +17 -9
- package/dist/agent/converters/tanstack.mjs.map +1 -1
- package/dist/agent/index.cjs +6 -4
- package/dist/agent/index.cjs.map +1 -1
- package/dist/agent/index.d.cts.map +1 -1
- package/dist/agent/index.d.mts.map +1 -1
- package/dist/agent/index.mjs +6 -4
- package/dist/agent/index.mjs.map +1 -1
- package/dist/agent/state-delta.cjs +322 -0
- package/dist/agent/state-delta.cjs.map +1 -0
- package/dist/agent/state-delta.mjs +321 -0
- package/dist/agent/state-delta.mjs.map +1 -0
- package/dist/package.cjs +1 -1
- package/dist/runtime/package.mjs +1 -1
- package/dist/v2/index.cjs +6 -1
- package/dist/v2/index.d.cts +2 -2
- package/dist/v2/index.d.mts +2 -2
- package/dist/v2/index.mjs +2 -2
- package/dist/v2/runtime/core/channel-manager.cjs +110 -9
- package/dist/v2/runtime/core/channel-manager.cjs.map +1 -1
- package/dist/v2/runtime/core/channel-manager.d.cts +79 -8
- package/dist/v2/runtime/core/channel-manager.d.cts.map +1 -1
- package/dist/v2/runtime/core/channel-manager.d.mts +79 -8
- package/dist/v2/runtime/core/channel-manager.d.mts.map +1 -1
- package/dist/v2/runtime/core/channel-manager.mjs +110 -9
- package/dist/v2/runtime/core/channel-manager.mjs.map +1 -1
- package/dist/v2/runtime/core/runtime.cjs.map +1 -1
- package/dist/v2/runtime/core/runtime.d.cts +1 -0
- package/dist/v2/runtime/core/runtime.d.cts.map +1 -1
- package/dist/v2/runtime/core/runtime.d.mts +1 -0
- package/dist/v2/runtime/core/runtime.d.mts.map +1 -1
- package/dist/v2/runtime/core/runtime.mjs.map +1 -1
- package/dist/v2/runtime/handlers/shared/agent-utils.cjs +2 -5
- package/dist/v2/runtime/handlers/shared/agent-utils.cjs.map +1 -1
- package/dist/v2/runtime/handlers/shared/agent-utils.mjs +2 -5
- package/dist/v2/runtime/handlers/shared/agent-utils.mjs.map +1 -1
- package/dist/v2/runtime/handlers/shared/mcp-apps-servers.cjs +23 -0
- package/dist/v2/runtime/handlers/shared/mcp-apps-servers.cjs.map +1 -0
- package/dist/v2/runtime/handlers/shared/mcp-apps-servers.mjs +22 -0
- package/dist/v2/runtime/handlers/shared/mcp-apps-servers.mjs.map +1 -0
- package/dist/v2/runtime/index.d.cts +1 -1
- package/dist/v2/runtime/index.d.mts +1 -1
- package/dist/v2/runtime/open-generative-ui-middleware.cjs +1 -0
- package/dist/v2/runtime/open-generative-ui-middleware.cjs.map +1 -1
- package/dist/v2/runtime/open-generative-ui-middleware.mjs +1 -0
- package/dist/v2/runtime/open-generative-ui-middleware.mjs.map +1 -1
- package/dist/v2/runtime/runner/in-memory.cjs +335 -101
- package/dist/v2/runtime/runner/in-memory.cjs.map +1 -1
- package/dist/v2/runtime/runner/in-memory.d.cts +246 -6
- package/dist/v2/runtime/runner/in-memory.d.cts.map +1 -1
- package/dist/v2/runtime/runner/in-memory.d.mts +246 -6
- package/dist/v2/runtime/runner/in-memory.d.mts.map +1 -1
- package/dist/v2/runtime/runner/in-memory.mjs +331 -102
- package/dist/v2/runtime/runner/in-memory.mjs.map +1 -1
- package/dist/v2/runtime/runner/index.d.cts +1 -1
- package/dist/v2/runtime/runner/index.d.mts +1 -1
- package/dist/v2/runtime/runner/index.mjs +1 -1
- package/package.json +5 -5
- package/skills/runtime/references/agent-runners-in-memory.md +49 -34
- package/skills/runtime/references/agent-runners.md +47 -21
- package/skills/runtime/references/wiring-mcp-apps-middleware.md +5 -0
|
@@ -5,6 +5,71 @@ import { ReplaySubject } from "rxjs";
|
|
|
5
5
|
import { EventType, compactEvents } from "@ag-ui/client";
|
|
6
6
|
|
|
7
7
|
//#region src/v2/runtime/runner/in-memory.ts
|
|
8
|
+
const ɵINMEMORY_DEFAULTS = {
|
|
9
|
+
maxThreads: 1e3,
|
|
10
|
+
maxRunsPerThread: 100,
|
|
11
|
+
maxBytes: 512 * 1024 ** 2
|
|
12
|
+
};
|
|
13
|
+
/**
|
|
14
|
+
* A limit value is well-formed iff it is a non-negative integer OR `+Infinity`.
|
|
15
|
+
* `+Infinity` is the documented "disabled/unbounded" sentinel and `0` is the
|
|
16
|
+
* documented run-cap disable sentinel; both are non-negative and pass. Every
|
|
17
|
+
* enforcement site (`evictThreadsIfNeeded`, `enforceRunCap`,
|
|
18
|
+
* `evictByBytesIfNeeded`) compares its counter against the limit with `>` in a
|
|
19
|
+
* `while`/`if` guard, so only these shapes keep those loops finite and correct.
|
|
20
|
+
* Rejected: negatives (drive `count > -1` true on an empty collection, so
|
|
21
|
+
* `enforceRunCap` `shift()!`s `undefined` and throws), `-Infinity` (loops never
|
|
22
|
+
* terminate their intent — always "over"), `NaN` (every `>` is false, silently
|
|
23
|
+
* disabling the bound), and non-integer finites (fractional caps are nonsense).
|
|
24
|
+
*/
|
|
25
|
+
function ɵisValidLimit(value) {
|
|
26
|
+
return value === Infinity || Number.isInteger(value) && value >= 0;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Normalize a fully-resolved limits bag so every field is well-formed before it
|
|
30
|
+
* can reach an enforcement loop. Each field is validated independently against
|
|
31
|
+
* {@link ɵisValidLimit}; an invalid value is CLAMPED to its
|
|
32
|
+
* {@link ɵINMEMORY_DEFAULTS} floor and a single `console.warn` naming the field
|
|
33
|
+
* and the received value is emitted.
|
|
34
|
+
*
|
|
35
|
+
* Clamp-and-warn (rather than throw) is deliberate and matches this file's
|
|
36
|
+
* established posture toward bad input: `ɵestimateBytes` swallows serialization
|
|
37
|
+
* failures and returns 0, the limits-clobber path warns rather than throwing,
|
|
38
|
+
* and both the eviction and clobber logs are wrapped so "logging must never
|
|
39
|
+
* break construction/a run". Constructing a bounded in-memory runner is a
|
|
40
|
+
* best-effort, non-durable convenience; a typo'd bound must degrade to a safe
|
|
41
|
+
* default, never abort construction or (worse) surface later as an unhandled
|
|
42
|
+
* rejection from the fire-and-forget finalize path.
|
|
43
|
+
*/
|
|
44
|
+
function ɵnormalizeLimits(limits) {
|
|
45
|
+
const normalized = { ...limits };
|
|
46
|
+
for (const field of Object.keys(ɵINMEMORY_DEFAULTS)) {
|
|
47
|
+
const value = limits[field];
|
|
48
|
+
if (!ɵisValidLimit(value)) {
|
|
49
|
+
const fallback = ɵINMEMORY_DEFAULTS[field];
|
|
50
|
+
normalized[field] = fallback;
|
|
51
|
+
try {
|
|
52
|
+
console.warn(`[CopilotKit] InMemoryAgentRunner: invalid ${field} value ${String(value)} (expected a non-negative integer or Infinity); falling back to ${String(fallback)}.`);
|
|
53
|
+
} catch {}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return normalized;
|
|
57
|
+
}
|
|
58
|
+
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.";
|
|
59
|
+
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.";
|
|
60
|
+
/**
|
|
61
|
+
* Best-effort approximate byte size of a value, via serialized length.
|
|
62
|
+
* Never throws — returns 0 when the value cannot be serialized. This is an
|
|
63
|
+
* approximation (UTF-16 length, not exact heap bytes), used only for relative
|
|
64
|
+
* accounting against `maxBytes`.
|
|
65
|
+
*/
|
|
66
|
+
function ɵestimateBytes(value) {
|
|
67
|
+
try {
|
|
68
|
+
return JSON.stringify(value)?.length ?? 0;
|
|
69
|
+
} catch {
|
|
70
|
+
return 0;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
8
73
|
var InMemoryEventStore = class {
|
|
9
74
|
constructor(threadId) {
|
|
10
75
|
this.threadId = threadId;
|
|
@@ -15,39 +80,240 @@ var InMemoryEventStore = class {
|
|
|
15
80
|
this.agent = null;
|
|
16
81
|
this.runSubject = null;
|
|
17
82
|
this.stopRequested = false;
|
|
83
|
+
this.activeFinalize = null;
|
|
18
84
|
this.currentEvents = null;
|
|
85
|
+
this.messagesSnapshot = [];
|
|
86
|
+
this.approxMessagesSnapshotBytes = 0;
|
|
87
|
+
this.createdAt = null;
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
var ɵBoundedThreadStore = class {
|
|
91
|
+
constructor(limits) {
|
|
92
|
+
this.map = /* @__PURE__ */ new Map();
|
|
93
|
+
this.totalBytes = 0;
|
|
94
|
+
this.warned = false;
|
|
95
|
+
this.limitsExplicitlySet = false;
|
|
96
|
+
this.clobberWarned = false;
|
|
97
|
+
this.limits = ɵnormalizeLimits(limits);
|
|
98
|
+
}
|
|
99
|
+
get byteTotal() {
|
|
100
|
+
return this.totalBytes;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* The store's CURRENT effective bounds. Exposed (with the `ɵ` internal-API
|
|
104
|
+
* prefix) so a partial `setLimits` can coalesce unspecified fields against the
|
|
105
|
+
* live config rather than the hardcoded {@link ɵINMEMORY_DEFAULTS} — a partial
|
|
106
|
+
* update must be a partial update, never a silent reset of the fields the
|
|
107
|
+
* caller did not mention. Returns a copy so callers cannot mutate the store's
|
|
108
|
+
* bounds through it.
|
|
109
|
+
*/
|
|
110
|
+
get ɵlimits() {
|
|
111
|
+
return { ...this.limits };
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Reconfigure the process-global store's bounds. Called by the
|
|
115
|
+
* {@link InMemoryAgentRunner} constructor when limits are passed. Because the
|
|
116
|
+
* store is a per-process singleton, this replaces the bounds for ALL in-memory
|
|
117
|
+
* threads. Emits {@link LIMITS_CLOBBER_GUIDANCE} at most ONCE per store when a
|
|
118
|
+
* SECOND (or later) explicit set arrives whose resolved values differ from the
|
|
119
|
+
* prior explicit set — i.e. a genuine clobber of an already-customized config.
|
|
120
|
+
* The first explicit customization (defaults → custom) is the intended
|
|
121
|
+
* override and never warns; identical re-sets never warn.
|
|
122
|
+
*/
|
|
123
|
+
setLimits(limits) {
|
|
124
|
+
const normalized = ɵnormalizeLimits(limits);
|
|
125
|
+
if (this.limitsExplicitlySet && !this.clobberWarned && (normalized.maxThreads !== this.limits.maxThreads || normalized.maxRunsPerThread !== this.limits.maxRunsPerThread || normalized.maxBytes !== this.limits.maxBytes)) {
|
|
126
|
+
this.clobberWarned = true;
|
|
127
|
+
try {
|
|
128
|
+
console.warn(LIMITS_CLOBBER_GUIDANCE);
|
|
129
|
+
} catch {}
|
|
130
|
+
}
|
|
131
|
+
this.limitsExplicitlySet = true;
|
|
132
|
+
this.limits = normalized;
|
|
133
|
+
}
|
|
134
|
+
get size() {
|
|
135
|
+
return this.map.size;
|
|
136
|
+
}
|
|
137
|
+
/** Re-insert at the tail so Map iteration order stays LRU-first. */
|
|
138
|
+
touchOrder(threadId, store) {
|
|
139
|
+
this.map.delete(threadId);
|
|
140
|
+
this.map.set(threadId, store);
|
|
141
|
+
}
|
|
142
|
+
getOrCreate(threadId) {
|
|
143
|
+
const existing = this.map.get(threadId);
|
|
144
|
+
if (existing) {
|
|
145
|
+
this.touchOrder(threadId, existing);
|
|
146
|
+
return existing;
|
|
147
|
+
}
|
|
148
|
+
const store = new InMemoryEventStore(threadId);
|
|
149
|
+
this.map.set(threadId, store);
|
|
150
|
+
this.evictThreadsIfNeeded(threadId);
|
|
151
|
+
return store;
|
|
152
|
+
}
|
|
153
|
+
get(threadId, opts) {
|
|
154
|
+
const store = this.map.get(threadId);
|
|
155
|
+
if (store && opts.touch) this.touchOrder(threadId, store);
|
|
156
|
+
return store;
|
|
157
|
+
}
|
|
158
|
+
peek(threadId) {
|
|
159
|
+
return this.map.get(threadId);
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Evict the least-recently-used thread that is neither running NOR
|
|
163
|
+
* mid-finalization. Returns false if none evictable. The `protect` thread
|
|
164
|
+
* (typically the one just created) is never evicted, so a fresh thread is not
|
|
165
|
+
* immediately dropped when it is the only non-running candidate.
|
|
166
|
+
*
|
|
167
|
+
* A thread is skipped while `isRunning` OR `stopRequested` is set.
|
|
168
|
+
* `stop()` flips `isRunning` to false the moment it aborts the agent, but the
|
|
169
|
+
* run keeps finalizing asynchronously (the abort trips the `catch` in
|
|
170
|
+
* `runAgent`, which later calls `appendRun`). During that window
|
|
171
|
+
* `stopRequested` stays true; evicting the thread then would make the pending
|
|
172
|
+
* `appendRun` hit `if (!store) return` and silently drop the aborted run's
|
|
173
|
+
* history. Guarding on `stopRequested` keeps the thread alive until
|
|
174
|
+
* finalization completes.
|
|
175
|
+
*/
|
|
176
|
+
evictOneLru(protect) {
|
|
177
|
+
for (const [threadId, store] of this.map) {
|
|
178
|
+
if (threadId === protect) continue;
|
|
179
|
+
if (store.isRunning || store.stopRequested) continue;
|
|
180
|
+
this.removeThread(threadId, store);
|
|
181
|
+
this.noteEviction();
|
|
182
|
+
return true;
|
|
183
|
+
}
|
|
184
|
+
return false;
|
|
185
|
+
}
|
|
186
|
+
appendRun(threadId, run) {
|
|
187
|
+
const store = this.map.get(threadId);
|
|
188
|
+
if (!store) return;
|
|
189
|
+
if (store.createdAt === null) store.createdAt = run.createdAt;
|
|
190
|
+
if (run.messages.length > 0) {
|
|
191
|
+
this.totalBytes -= store.approxMessagesSnapshotBytes;
|
|
192
|
+
store.messagesSnapshot = run.messages;
|
|
193
|
+
store.approxMessagesSnapshotBytes = ɵestimateBytes(run.messages);
|
|
194
|
+
this.totalBytes += store.approxMessagesSnapshotBytes;
|
|
195
|
+
}
|
|
196
|
+
run.messages = [];
|
|
197
|
+
run.approxMessageBytes = 0;
|
|
198
|
+
run.approxEventBytes = ɵestimateBytes(run.events);
|
|
199
|
+
store.historicRuns.push(run);
|
|
200
|
+
this.totalBytes += run.approxEventBytes;
|
|
201
|
+
this.touchOrder(threadId, store);
|
|
202
|
+
this.enforceRunCap(store);
|
|
203
|
+
this.evictByBytesIfNeeded(threadId);
|
|
204
|
+
}
|
|
205
|
+
enforceRunCap(store) {
|
|
206
|
+
const cap = this.limits.maxRunsPerThread;
|
|
207
|
+
if (!cap || cap === Infinity) return;
|
|
208
|
+
while (store.historicRuns.length > cap) {
|
|
209
|
+
const dropped = store.historicRuns.shift();
|
|
210
|
+
this.totalBytes -= dropped.approxEventBytes ?? 0;
|
|
211
|
+
this.noteEviction();
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* Trim the store back under the byte ceiling by evicting LRU non-running
|
|
216
|
+
* threads. `protect` (the just-appended thread) is never self-evicted, so a
|
|
217
|
+
* fresh run pushes OTHER threads out rather than dropping itself.
|
|
218
|
+
*/
|
|
219
|
+
evictByBytesIfNeeded(protect) {
|
|
220
|
+
while (this.totalBytes > this.limits.maxBytes) if (!this.evictOneLru(protect)) break;
|
|
221
|
+
}
|
|
222
|
+
removeThread(threadId, store) {
|
|
223
|
+
for (const run of store.historicRuns) this.totalBytes -= run.approxEventBytes ?? 0;
|
|
224
|
+
this.totalBytes -= store.approxMessagesSnapshotBytes;
|
|
225
|
+
this.map.delete(threadId);
|
|
226
|
+
}
|
|
227
|
+
evictThreadsIfNeeded(protect) {
|
|
228
|
+
while (this.map.size > this.limits.maxThreads) if (!this.evictOneLru(protect)) break;
|
|
229
|
+
}
|
|
230
|
+
noteEviction() {
|
|
231
|
+
if (this.warned) return;
|
|
232
|
+
this.warned = true;
|
|
233
|
+
try {
|
|
234
|
+
console.warn(EVICTION_GUIDANCE);
|
|
235
|
+
} catch {}
|
|
236
|
+
}
|
|
237
|
+
listThreads() {
|
|
238
|
+
const threads = [];
|
|
239
|
+
for (const [threadId, store] of this.map) {
|
|
240
|
+
if (store.historicRuns.length === 0) continue;
|
|
241
|
+
const lastRun = store.historicRuns[store.historicRuns.length - 1];
|
|
242
|
+
threads.push({
|
|
243
|
+
id: threadId,
|
|
244
|
+
name: null,
|
|
245
|
+
agentId: lastRun.agentId,
|
|
246
|
+
organizationId: "",
|
|
247
|
+
createdById: "",
|
|
248
|
+
archived: false,
|
|
249
|
+
createdAt: new Date(store.createdAt ?? lastRun.createdAt).toISOString(),
|
|
250
|
+
updatedAt: new Date(lastRun.createdAt).toISOString()
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
return threads.sort((a, b) => new Date(b.updatedAt).getTime() - new Date(a.updatedAt).getTime());
|
|
254
|
+
}
|
|
255
|
+
clear() {
|
|
256
|
+
this.map.clear();
|
|
257
|
+
this.totalBytes = 0;
|
|
258
|
+
this.warned = false;
|
|
19
259
|
}
|
|
20
260
|
};
|
|
21
|
-
|
|
261
|
+
/**
|
|
262
|
+
* Process-wide singleton backing every {@link InMemoryAgentRunner}. Exported
|
|
263
|
+
* (with the `ɵ` internal-API prefix) so tests can inspect the exact store the
|
|
264
|
+
* runner writes to; not part of the public API.
|
|
265
|
+
*/
|
|
266
|
+
const ɵGLOBAL_STORE = new ɵBoundedThreadStore(ɵINMEMORY_DEFAULTS);
|
|
267
|
+
const sharedStore = ɵGLOBAL_STORE;
|
|
22
268
|
var InMemoryAgentRunner = class extends AgentRunner {
|
|
269
|
+
/**
|
|
270
|
+
* @param options Per-runner behavior (`onConcurrentRun`) plus optional bounds
|
|
271
|
+
* for the in-memory store ({@link InMemoryLimits}).
|
|
272
|
+
*
|
|
273
|
+
* Note the differing scopes: `onConcurrentRun` is per-runner instance, while
|
|
274
|
+
* the limits reconfigure the PROCESS-GLOBAL store shared by every
|
|
275
|
+
* `InMemoryAgentRunner`. Omit the limits for safe defaults
|
|
276
|
+
* ({@link ɵINMEMORY_DEFAULTS}); passing none leaves the store untouched. When
|
|
277
|
+
* multiple runners are constructed with differing limits, the last-constructed
|
|
278
|
+
* wins — in practice the OSS/SSE default construction passes nothing. If a
|
|
279
|
+
* second (or later) runner is constructed with limits that DIFFER from an
|
|
280
|
+
* already-customized store, a one-time `console.warn` is emitted to signal that
|
|
281
|
+
* the shared store's bounds are being clobbered for ALL in-memory threads.
|
|
282
|
+
*/
|
|
23
283
|
constructor(options) {
|
|
24
284
|
super();
|
|
25
285
|
this.ɵsupportsLocalThreadEndpoints = true;
|
|
26
|
-
|
|
286
|
+
const { onConcurrentRun, ...limits } = options ?? {};
|
|
287
|
+
this.onConcurrentRun = onConcurrentRun ?? "throw";
|
|
288
|
+
if (limits.maxThreads !== void 0 || limits.maxRunsPerThread !== void 0 || limits.maxBytes !== void 0) {
|
|
289
|
+
const current = sharedStore.ɵlimits;
|
|
290
|
+
sharedStore.setLimits({
|
|
291
|
+
maxThreads: limits.maxThreads ?? current.maxThreads,
|
|
292
|
+
maxRunsPerThread: limits.maxRunsPerThread ?? current.maxRunsPerThread,
|
|
293
|
+
maxBytes: limits.maxBytes ?? current.maxBytes
|
|
294
|
+
});
|
|
295
|
+
}
|
|
27
296
|
}
|
|
28
297
|
run(request) {
|
|
29
|
-
|
|
30
|
-
if (
|
|
31
|
-
existingStore = new InMemoryEventStore(request.threadId);
|
|
32
|
-
GLOBAL_STORE.set(request.threadId, existingStore);
|
|
33
|
-
}
|
|
34
|
-
const store = existingStore;
|
|
35
|
-
if (store.isRunning) {
|
|
298
|
+
const store = sharedStore.getOrCreate(request.threadId);
|
|
299
|
+
if (store.isRunning || store.stopRequested) {
|
|
36
300
|
if (this.onConcurrentRun !== "supersede") throw new Error("Thread already running");
|
|
37
301
|
const priorAgent = store.agent;
|
|
38
|
-
|
|
302
|
+
const priorFinalize = store.activeFinalize;
|
|
303
|
+
if (priorFinalize) priorFinalize.stopRequested = true;
|
|
39
304
|
store.isRunning = false;
|
|
40
305
|
if (priorAgent) try {
|
|
41
306
|
priorAgent.abortRun();
|
|
42
307
|
} catch (error) {
|
|
43
308
|
console.error("Failed to abort superseded run", error);
|
|
44
309
|
}
|
|
45
|
-
store.stopRequested = false;
|
|
46
310
|
}
|
|
47
311
|
store.isRunning = true;
|
|
48
312
|
store.currentRunId = request.input.runId;
|
|
49
313
|
store.agent = request.agent;
|
|
50
314
|
store.stopRequested = false;
|
|
315
|
+
const finalizeControl = { stopRequested: false };
|
|
316
|
+
store.activeFinalize = finalizeControl;
|
|
51
317
|
const seenMessageIds = /* @__PURE__ */ new Set();
|
|
52
318
|
const currentRunEvents = [];
|
|
53
319
|
store.currentEvents = currentRunEvents;
|
|
@@ -60,12 +326,48 @@ var InMemoryAgentRunner = class extends AgentRunner {
|
|
|
60
326
|
}
|
|
61
327
|
}
|
|
62
328
|
const nextSubject = new ReplaySubject(Infinity);
|
|
63
|
-
const prevSubject = store.subject;
|
|
64
329
|
store.subject = nextSubject;
|
|
65
330
|
const runSubject = new ReplaySubject(Infinity);
|
|
66
331
|
store.runSubject = runSubject;
|
|
67
332
|
const runAgent = async () => {
|
|
68
333
|
const parentRunId = store.historicRuns[store.historicRuns.length - 1]?.runId ?? null;
|
|
334
|
+
const finalizeRun = (opts) => {
|
|
335
|
+
const isError = opts.interruptionMessage !== void 0;
|
|
336
|
+
const preFinalizeEventCount = currentRunEvents.length;
|
|
337
|
+
const appendedEvents = finalizeRunEvents(currentRunEvents, {
|
|
338
|
+
stopRequested: finalizeControl.stopRequested,
|
|
339
|
+
...isError ? { interruptionMessage: opts.interruptionMessage } : {}
|
|
340
|
+
});
|
|
341
|
+
for (const event of appendedEvents) {
|
|
342
|
+
runSubject.next(event);
|
|
343
|
+
nextSubject.next(event);
|
|
344
|
+
}
|
|
345
|
+
const ownsThread = store.currentRunId === request.input.runId;
|
|
346
|
+
if (ownsThread && (!isError || preFinalizeEventCount > 0)) {
|
|
347
|
+
const compactedEvents = compactEvents(currentRunEvents);
|
|
348
|
+
sharedStore.appendRun(request.threadId, {
|
|
349
|
+
threadId: request.threadId,
|
|
350
|
+
runId: request.input.runId,
|
|
351
|
+
agentId: request.agent.agentId ?? "default",
|
|
352
|
+
parentRunId,
|
|
353
|
+
events: compactedEvents,
|
|
354
|
+
messages: Array.isArray(request.agent.messages) ? [...request.agent.messages] : [],
|
|
355
|
+
createdAt: Date.now()
|
|
356
|
+
});
|
|
357
|
+
}
|
|
358
|
+
if (ownsThread) {
|
|
359
|
+
store.currentEvents = null;
|
|
360
|
+
store.currentRunId = null;
|
|
361
|
+
store.agent = null;
|
|
362
|
+
store.runSubject = null;
|
|
363
|
+
store.stopRequested = false;
|
|
364
|
+
store.isRunning = false;
|
|
365
|
+
store.activeFinalize = null;
|
|
366
|
+
}
|
|
367
|
+
runSubject.complete();
|
|
368
|
+
nextSubject.complete();
|
|
369
|
+
if (store.subject === nextSubject) store.subject = null;
|
|
370
|
+
};
|
|
69
371
|
try {
|
|
70
372
|
await request.agent.runAgent(request.input, {
|
|
71
373
|
onEvent: ({ event }) => {
|
|
@@ -94,77 +396,16 @@ var InMemoryAgentRunner = class extends AgentRunner {
|
|
|
94
396
|
}
|
|
95
397
|
}
|
|
96
398
|
});
|
|
97
|
-
|
|
98
|
-
for (const event of appendedEvents) {
|
|
99
|
-
runSubject.next(event);
|
|
100
|
-
nextSubject.next(event);
|
|
101
|
-
}
|
|
102
|
-
if (store.currentRunId === request.input.runId) {
|
|
103
|
-
const compactedEvents = compactEvents(currentRunEvents);
|
|
104
|
-
store.historicRuns.push({
|
|
105
|
-
threadId: request.threadId,
|
|
106
|
-
runId: request.input.runId,
|
|
107
|
-
agentId: request.agent.agentId ?? "default",
|
|
108
|
-
parentRunId,
|
|
109
|
-
events: compactedEvents,
|
|
110
|
-
messages: Array.isArray(request.agent.messages) ? [...request.agent.messages] : [],
|
|
111
|
-
createdAt: Date.now()
|
|
112
|
-
});
|
|
113
|
-
}
|
|
114
|
-
if (store.currentRunId === request.input.runId) {
|
|
115
|
-
store.currentEvents = null;
|
|
116
|
-
store.currentRunId = null;
|
|
117
|
-
store.agent = null;
|
|
118
|
-
store.runSubject = null;
|
|
119
|
-
store.stopRequested = false;
|
|
120
|
-
store.isRunning = false;
|
|
121
|
-
}
|
|
122
|
-
runSubject.complete();
|
|
123
|
-
nextSubject.complete();
|
|
399
|
+
finalizeRun({});
|
|
124
400
|
} catch (error) {
|
|
125
|
-
|
|
126
|
-
const appendedEvents = finalizeRunEvents(currentRunEvents, {
|
|
127
|
-
stopRequested: store.stopRequested,
|
|
128
|
-
interruptionMessage
|
|
129
|
-
});
|
|
130
|
-
for (const event of appendedEvents) {
|
|
131
|
-
runSubject.next(event);
|
|
132
|
-
nextSubject.next(event);
|
|
133
|
-
}
|
|
134
|
-
if (store.currentRunId === request.input.runId && currentRunEvents.length > 0) {
|
|
135
|
-
const compactedEvents = compactEvents(currentRunEvents);
|
|
136
|
-
store.historicRuns.push({
|
|
137
|
-
threadId: request.threadId,
|
|
138
|
-
runId: request.input.runId,
|
|
139
|
-
agentId: request.agent.agentId ?? "default",
|
|
140
|
-
parentRunId,
|
|
141
|
-
events: compactedEvents,
|
|
142
|
-
messages: Array.isArray(request.agent.messages) ? [...request.agent.messages] : [],
|
|
143
|
-
createdAt: Date.now()
|
|
144
|
-
});
|
|
145
|
-
}
|
|
146
|
-
if (store.currentRunId === request.input.runId) {
|
|
147
|
-
store.currentEvents = null;
|
|
148
|
-
store.currentRunId = null;
|
|
149
|
-
store.agent = null;
|
|
150
|
-
store.runSubject = null;
|
|
151
|
-
store.stopRequested = false;
|
|
152
|
-
store.isRunning = false;
|
|
153
|
-
}
|
|
154
|
-
runSubject.complete();
|
|
155
|
-
nextSubject.complete();
|
|
401
|
+
finalizeRun({ interruptionMessage: error instanceof Error ? error.message : String(error) });
|
|
156
402
|
}
|
|
157
403
|
};
|
|
158
|
-
if (prevSubject) prevSubject.subscribe({
|
|
159
|
-
next: (e) => nextSubject.next(e),
|
|
160
|
-
error: (err) => nextSubject.error(err),
|
|
161
|
-
complete: () => {}
|
|
162
|
-
});
|
|
163
404
|
runAgent();
|
|
164
405
|
return runSubject.asObservable();
|
|
165
406
|
}
|
|
166
407
|
connect(request) {
|
|
167
|
-
const store =
|
|
408
|
+
const store = sharedStore.get(request.threadId, { touch: true });
|
|
168
409
|
const connectionSubject = new ReplaySubject(Infinity);
|
|
169
410
|
if (!store) {
|
|
170
411
|
connectionSubject.complete();
|
|
@@ -190,20 +431,23 @@ var InMemoryAgentRunner = class extends AgentRunner {
|
|
|
190
431
|
return connectionSubject.asObservable();
|
|
191
432
|
}
|
|
192
433
|
isRunning(request) {
|
|
193
|
-
const store =
|
|
434
|
+
const store = sharedStore.peek(request.threadId);
|
|
194
435
|
return Promise.resolve(store?.isRunning ?? false);
|
|
195
436
|
}
|
|
196
437
|
stop(request) {
|
|
197
|
-
const store =
|
|
438
|
+
const store = sharedStore.peek(request.threadId);
|
|
198
439
|
if (!store || !store.isRunning) return Promise.resolve(false);
|
|
199
440
|
if (request.runId !== void 0 && store.currentRunId !== request.runId) return Promise.resolve(false);
|
|
200
441
|
if (store.stopRequested) return Promise.resolve(false);
|
|
201
442
|
store.stopRequested = true;
|
|
202
443
|
store.isRunning = false;
|
|
444
|
+
const finalizeControl = store.activeFinalize;
|
|
445
|
+
if (finalizeControl) finalizeControl.stopRequested = true;
|
|
203
446
|
const agent = store.agent;
|
|
204
447
|
if (!agent) {
|
|
205
448
|
store.stopRequested = false;
|
|
206
449
|
store.isRunning = false;
|
|
450
|
+
if (finalizeControl) finalizeControl.stopRequested = false;
|
|
207
451
|
return Promise.resolve(false);
|
|
208
452
|
}
|
|
209
453
|
try {
|
|
@@ -213,6 +457,7 @@ var InMemoryAgentRunner = class extends AgentRunner {
|
|
|
213
457
|
console.error("Failed to abort agent run", error);
|
|
214
458
|
store.stopRequested = false;
|
|
215
459
|
store.isRunning = true;
|
|
460
|
+
if (finalizeControl) finalizeControl.stopRequested = false;
|
|
216
461
|
return Promise.resolve(false);
|
|
217
462
|
}
|
|
218
463
|
}
|
|
@@ -224,23 +469,7 @@ var InMemoryAgentRunner = class extends AgentRunner {
|
|
|
224
469
|
* `ThreadRecord` so the HTTP handler can use the same response envelope.
|
|
225
470
|
*/
|
|
226
471
|
listThreads() {
|
|
227
|
-
|
|
228
|
-
for (const [threadId, store] of GLOBAL_STORE) {
|
|
229
|
-
if (store.historicRuns.length === 0) continue;
|
|
230
|
-
const firstRun = store.historicRuns[0];
|
|
231
|
-
const lastRun = store.historicRuns[store.historicRuns.length - 1];
|
|
232
|
-
threads.push({
|
|
233
|
-
id: threadId,
|
|
234
|
-
name: null,
|
|
235
|
-
agentId: lastRun.agentId,
|
|
236
|
-
organizationId: "",
|
|
237
|
-
createdById: "",
|
|
238
|
-
archived: false,
|
|
239
|
-
createdAt: new Date(firstRun.createdAt).toISOString(),
|
|
240
|
-
updatedAt: new Date(lastRun.createdAt).toISOString()
|
|
241
|
-
});
|
|
242
|
-
}
|
|
243
|
-
return threads.sort((a, b) => new Date(b.updatedAt).getTime() - new Date(a.updatedAt).getTime());
|
|
472
|
+
return sharedStore.listThreads();
|
|
244
473
|
}
|
|
245
474
|
/**
|
|
246
475
|
* Returns all messages for a thread, using the snapshot captured at the end
|
|
@@ -252,9 +481,9 @@ var InMemoryAgentRunner = class extends AgentRunner {
|
|
|
252
481
|
* with the Intelligence platform's `ThreadMessage` type.
|
|
253
482
|
*/
|
|
254
483
|
getThreadMessages(threadId) {
|
|
255
|
-
const store =
|
|
256
|
-
if (!store
|
|
257
|
-
return
|
|
484
|
+
const store = sharedStore.peek(threadId);
|
|
485
|
+
if (!store) return [];
|
|
486
|
+
return [...store.messagesSnapshot];
|
|
258
487
|
}
|
|
259
488
|
/**
|
|
260
489
|
* Returns all AG-UI events for a thread, compacted across historic runs.
|
|
@@ -265,7 +494,7 @@ var InMemoryAgentRunner = class extends AgentRunner {
|
|
|
265
494
|
* late-joining inspector sees matches what this method returns.
|
|
266
495
|
*/
|
|
267
496
|
getThreadEvents(threadId) {
|
|
268
|
-
const store =
|
|
497
|
+
const store = sharedStore.peek(threadId);
|
|
269
498
|
if (!store || store.historicRuns.length === 0) return [];
|
|
270
499
|
const all = [];
|
|
271
500
|
for (const run of store.historicRuns) all.push(...run.events);
|
|
@@ -287,7 +516,7 @@ var InMemoryAgentRunner = class extends AgentRunner {
|
|
|
287
516
|
const event = events[i];
|
|
288
517
|
if (event.type === EventType.STATE_SNAPSHOT) {
|
|
289
518
|
const snapshot = event.snapshot;
|
|
290
|
-
if (snapshot && typeof snapshot === "object") return snapshot;
|
|
519
|
+
if (snapshot && typeof snapshot === "object" && !Array.isArray(snapshot)) return { ...snapshot };
|
|
291
520
|
return null;
|
|
292
521
|
}
|
|
293
522
|
}
|
|
@@ -303,10 +532,10 @@ var InMemoryAgentRunner = class extends AgentRunner {
|
|
|
303
532
|
* not be wiped this way.
|
|
304
533
|
*/
|
|
305
534
|
clearThreads() {
|
|
306
|
-
|
|
535
|
+
sharedStore.clear();
|
|
307
536
|
}
|
|
308
537
|
};
|
|
309
538
|
|
|
310
539
|
//#endregion
|
|
311
|
-
export { InMemoryAgentRunner };
|
|
540
|
+
export { InMemoryAgentRunner, ɵBoundedThreadStore, ɵGLOBAL_STORE, ɵINMEMORY_DEFAULTS, ɵestimateBytes, ɵnormalizeLimits };
|
|
312
541
|
//# sourceMappingURL=in-memory.mjs.map
|