@copilotkit/core 0.0.0-mme-a2ui-v0-9-20260402165117

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs ADDED
@@ -0,0 +1,3415 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
+ let _ag_ui_client = require("@ag-ui/client");
3
+ let _copilotkit_shared = require("@copilotkit/shared");
4
+ let rxjs = require("rxjs");
5
+ let rxjs_operators = require("rxjs/operators");
6
+ let phoenix = require("phoenix");
7
+ let zod_to_json_schema = require("zod-to-json-schema");
8
+ let rxjs_fetch = require("rxjs/fetch");
9
+
10
+ //#region src/utils/phoenix-observable.ts
11
+ /**
12
+ * Adapt Phoenix socket open/error callbacks into an observable signal stream.
13
+ *
14
+ * The returned observable is shared and replayable by the caller when needed,
15
+ * but this helper itself does not own socket connection teardown.
16
+ */
17
+ function ɵcreatePhoenixSocketSignals$(socket) {
18
+ return new rxjs.Observable((observer) => {
19
+ socket.onOpen(() => observer.next({ type: "open" }));
20
+ socket.onError((error) => observer.next({
21
+ type: "error",
22
+ error
23
+ }));
24
+ });
25
+ }
26
+ /**
27
+ * Adapt a Phoenix channel join attempt into a single-outcome observable.
28
+ */
29
+ function ɵcreatePhoenixJoinOutcome$(channel) {
30
+ return new rxjs.Observable((observer) => {
31
+ channel.join().receive("ok", () => {
32
+ observer.next({ type: "joined" });
33
+ observer.complete();
34
+ }).receive("error", (response) => {
35
+ observer.next({
36
+ type: "error",
37
+ response
38
+ });
39
+ observer.complete();
40
+ }).receive("timeout", () => {
41
+ observer.next({ type: "timeout" });
42
+ observer.complete();
43
+ });
44
+ });
45
+ }
46
+ /**
47
+ * Create a cold Phoenix socket session.
48
+ *
49
+ * The socket is constructed and connected on subscription, and disconnected on
50
+ * teardown. Each subscription creates an isolated socket instance.
51
+ */
52
+ function ɵphoenixSocket$(options) {
53
+ return (0, rxjs.defer)(() => {
54
+ const socket = new phoenix.Socket(options.url, options.options);
55
+ const signals$ = ɵcreatePhoenixSocketSignals$(socket).pipe((0, rxjs_operators.shareReplay)({
56
+ bufferSize: 1,
57
+ refCount: true
58
+ }));
59
+ socket.connect();
60
+ return (0, rxjs.concat)((0, rxjs.of)({
61
+ socket,
62
+ signals$
63
+ }), rxjs.NEVER).pipe((0, rxjs_operators.finalize)(() => socket.disconnect()));
64
+ });
65
+ }
66
+ /**
67
+ * Create a cold Phoenix channel session from a socket session stream.
68
+ *
69
+ * A channel is created and joined for each active socket session. If the
70
+ * upstream socket session changes, the previous channel is left before the
71
+ * next one becomes active.
72
+ */
73
+ function ɵphoenixChannel$(options) {
74
+ return options.socket$.pipe((0, rxjs_operators.switchMap)(({ socket }) => (0, rxjs.defer)(() => {
75
+ const channel = socket.channel(options.topic, options.params);
76
+ return (0, rxjs.concat)((0, rxjs.of)({
77
+ channel,
78
+ joinOutcome$: ɵcreatePhoenixJoinOutcome$(channel).pipe((0, rxjs_operators.shareReplay)({
79
+ bufferSize: 1,
80
+ refCount: true
81
+ }))
82
+ }), rxjs.NEVER).pipe((0, rxjs_operators.finalize)(() => {
83
+ if (options.leaveOnUnsubscribe !== false) channel.leave();
84
+ }));
85
+ })));
86
+ }
87
+ /**
88
+ * Observe a named Phoenix channel event as an observable payload stream.
89
+ */
90
+ function ɵobservePhoenixEvent$(channel, eventName) {
91
+ return new rxjs.Observable((observer) => {
92
+ const ref = channel.on(eventName, (payload) => observer.next(payload));
93
+ return () => {
94
+ channel.off(eventName, ref);
95
+ };
96
+ });
97
+ }
98
+ /**
99
+ * Flatten channel sessions into their join-outcome stream.
100
+ */
101
+ function ɵobservePhoenixJoinOutcome$(channel$) {
102
+ return channel$.pipe((0, rxjs_operators.switchMap)((session) => session.joinOutcome$));
103
+ }
104
+ /**
105
+ * Complete when a channel joins successfully, or error if the join fails.
106
+ */
107
+ function ɵjoinPhoenixChannel$(channel$) {
108
+ return ɵobservePhoenixJoinOutcome$(channel$).pipe((0, rxjs_operators.take)(1), (0, rxjs_operators.mergeMap)((outcome) => {
109
+ if (outcome.type === "joined") return rxjs.EMPTY;
110
+ throw outcome.type === "timeout" ? /* @__PURE__ */ new Error("Timed out joining channel") : /* @__PURE__ */ new Error(`Failed to join channel: ${JSON.stringify(outcome.response)}`);
111
+ }));
112
+ }
113
+ /**
114
+ * Flatten socket sessions into their lifecycle signal stream.
115
+ */
116
+ function ɵobservePhoenixSocketSignals$(socket$) {
117
+ return socket$.pipe((0, rxjs_operators.switchMap)((session) => session.signals$));
118
+ }
119
+ /**
120
+ * Error after a socket emits the configured number of consecutive error
121
+ * signals, resetting the counter after each successful open signal.
122
+ */
123
+ function ɵobservePhoenixSocketHealth$(socketSignals$, maxConsecutiveErrors) {
124
+ return socketSignals$.pipe((0, rxjs_operators.scan)((consecutiveErrors, signal) => signal.type === "open" ? 0 : consecutiveErrors + 1, 0), (0, rxjs_operators.filter)((consecutiveErrors) => consecutiveErrors >= maxConsecutiveErrors), (0, rxjs_operators.take)(1), (0, rxjs_operators.mergeMap)((consecutiveErrors) => (0, rxjs.throwError)(() => /* @__PURE__ */ new Error(`WebSocket connection failed after ${consecutiveErrors} consecutive errors`))));
125
+ }
126
+
127
+ //#endregion
128
+ //#region src/intelligence-agent.ts
129
+ const CLIENT_AG_UI_EVENT = "ag_ui_event";
130
+ const STOP_RUN_EVENT = "stop_run";
131
+ var AgentThreadLockedError = class extends Error {
132
+ constructor(threadId) {
133
+ super(threadId ? `Thread ${threadId} is locked` : "Thread is locked");
134
+ this.name = "AgentThreadLockedError";
135
+ }
136
+ };
137
+ var IntelligenceAgent = class IntelligenceAgent extends _ag_ui_client.AbstractAgent {
138
+ config;
139
+ socket = null;
140
+ activeChannel = null;
141
+ runId = null;
142
+ sharedState;
143
+ constructor(config, sharedState = { lastSeenEventIds: /* @__PURE__ */ new Map() }) {
144
+ super();
145
+ this.config = config;
146
+ this.sharedState = sharedState;
147
+ }
148
+ clone() {
149
+ return new IntelligenceAgent(this.config, this.sharedState);
150
+ }
151
+ /**
152
+ * Override of AbstractAgent.connectAgent that removes the `verifyEvents` step.
153
+ *
154
+ * Background: AbstractAgent's connectAgent pipeline runs events through
155
+ * `verifyEvents`, which validates that the stream follows the AG-UI protocol
156
+ * lifecycle — specifically, it expects a RUN_STARTED event before any content
157
+ * events and a RUN_FINISHED/RUN_ERROR event to complete the stream.
158
+ *
159
+ * IntelligenceAgent uses long-lived WebSocket connections rather than
160
+ * request-scoped SSE streams. When connecting to replay historical messages
161
+ * for an existing thread, the connection semantics don't map to a single
162
+ * agent run start/stop cycle. The replayed events may not include
163
+ * RUN_STARTED/RUN_FINISHED bookends (or may contain events from multiple
164
+ * past runs), which causes verifyEvents to either never complete or to
165
+ * error out.
166
+ *
167
+ * This override replicates the base connectAgent implementation exactly,
168
+ * substituting only `transformChunks` (which is still needed for message
169
+ * reassembly) and omitting `verifyEvents`.
170
+ *
171
+ * TODO: Remove this override once AG-UI's AbstractAgent supports opting out
172
+ * of verifyEvents for transports with different connection life-cycles.
173
+ */
174
+ async connectAgent(parameters, subscriber) {
175
+ const self = this;
176
+ try {
177
+ this.isRunning = true;
178
+ this.agentId = this.agentId ?? (0, _ag_ui_client.randomUUID)();
179
+ const input = this.prepareRunAgentInput(parameters);
180
+ let result;
181
+ const previousMessageIds = new Set(this.messages.map((m) => m.id));
182
+ const subscribers = [
183
+ { onRunFinishedEvent: (event) => {
184
+ result = event.result;
185
+ } },
186
+ ...this.subscribers,
187
+ subscriber ?? {}
188
+ ];
189
+ await this.onInitialize(input, subscribers);
190
+ self.activeRunDetach$ = new rxjs.Subject();
191
+ let resolveCompletion;
192
+ self.activeRunCompletionPromise = new Promise((resolve) => {
193
+ resolveCompletion = resolve;
194
+ });
195
+ const source$ = (0, rxjs.defer)(() => this.connect(input)).pipe((0, _ag_ui_client.transformChunks)(this.debug), (0, rxjs_operators.takeUntil)(self.activeRunDetach$));
196
+ const applied$ = this.apply(input, source$, subscribers);
197
+ await (0, rxjs.lastValueFrom)(this.processApplyEvents(input, applied$, subscribers).pipe((0, rxjs_operators.catchError)((error) => {
198
+ this.isRunning = false;
199
+ return this.onError(input, error, subscribers);
200
+ }), (0, rxjs_operators.finalize)(() => {
201
+ this.isRunning = false;
202
+ this.onFinalize(input, subscribers);
203
+ resolveCompletion?.();
204
+ resolveCompletion = void 0;
205
+ self.activeRunCompletionPromise = void 0;
206
+ self.activeRunDetach$ = void 0;
207
+ })), { defaultValue: void 0 });
208
+ const newMessages = (0, _ag_ui_client.structuredClone_)(this.messages).filter((m) => !previousMessageIds.has(m.id));
209
+ return {
210
+ result,
211
+ newMessages
212
+ };
213
+ } finally {
214
+ this.isRunning = false;
215
+ }
216
+ }
217
+ abortRun() {
218
+ if (this.activeChannel && this.runId) {
219
+ const fallback = setTimeout(() => clear(), 5e3);
220
+ const clear = () => {
221
+ clearTimeout(fallback);
222
+ this.detachActiveRun();
223
+ this.cleanup();
224
+ };
225
+ this.activeChannel.push(STOP_RUN_EVENT, { run_id: this.runId }).receive("ok", clear).receive("error", clear).receive("timeout", clear);
226
+ } else {
227
+ this.detachActiveRun();
228
+ this.cleanup();
229
+ }
230
+ }
231
+ /**
232
+ * Trigger the run via REST, then join the realtime thread channel and relay
233
+ * server-pushed AG-UI events to the Observable subscriber.
234
+ */
235
+ run(input) {
236
+ this.threadId = input.threadId;
237
+ this.runId = input.runId;
238
+ return (0, rxjs.defer)(() => this.requestJoinCredentials$("run", input)).pipe((0, rxjs.switchMap)((credentials) => this.observeThread$(input, credentials, {
239
+ completeOnRunError: false,
240
+ streamMode: "run"
241
+ })));
242
+ }
243
+ /**
244
+ * Reconnect to an existing thread by fetching websocket credentials and
245
+ * joining the realtime thread channel.
246
+ */
247
+ connect(input) {
248
+ this.threadId = input.threadId;
249
+ this.runId = input.runId;
250
+ return (0, rxjs.defer)(() => this.requestConnectPlan$(input)).pipe((0, rxjs.switchMap)((plan) => {
251
+ if (plan === null) return rxjs.EMPTY;
252
+ if (plan.mode === "bootstrap") {
253
+ this.setLastSeenEventId(input.threadId, plan.latestEventId);
254
+ for (const event of plan.events) this.updateRunIdFromEvent(event);
255
+ return (0, rxjs.from)(plan.events);
256
+ }
257
+ this.setLastSeenEventId(input.threadId, plan.joinFromEventId);
258
+ for (const event of plan.events) this.updateRunIdFromEvent(event);
259
+ return (0, rxjs.concat)((0, rxjs.from)(plan.events), this.observeThread$(input, { joinToken: plan.joinToken }, {
260
+ completeOnRunError: true,
261
+ streamMode: "connect",
262
+ replayCursor: plan.joinFromEventId
263
+ }));
264
+ }));
265
+ }
266
+ /**
267
+ * Tear down a specific channel + socket pair that belongs to one pipeline.
268
+ * Only nulls instance references when they still point to the owned resource,
269
+ * so a concurrent pipeline's resources are never clobbered.
270
+ */
271
+ cleanupOwned(ownChannel, ownSocket) {
272
+ if (ownChannel) {
273
+ ownChannel.leave();
274
+ if (this.activeChannel === ownChannel) this.activeChannel = null;
275
+ }
276
+ if (ownSocket) {
277
+ ownSocket.disconnect();
278
+ if (this.socket === ownSocket) this.socket = null;
279
+ }
280
+ if (this.threadId) this.sharedState.lastSeenEventIds.delete(this.threadId);
281
+ this.runId = null;
282
+ }
283
+ cleanup() {
284
+ this.cleanupOwned(this.activeChannel, this.socket);
285
+ }
286
+ requestJoinCredentials$(mode, input) {
287
+ return (0, rxjs.defer)(async () => {
288
+ try {
289
+ const response = await fetch(this.buildRuntimeUrl(mode), {
290
+ method: "POST",
291
+ headers: {
292
+ "Content-Type": "application/json",
293
+ ...this.config.headers
294
+ },
295
+ body: JSON.stringify({
296
+ threadId: input.threadId,
297
+ runId: input.runId,
298
+ messages: input.messages,
299
+ tools: input.tools,
300
+ context: input.context,
301
+ state: input.state,
302
+ forwardedProps: input.forwardedProps
303
+ }),
304
+ ...this.config.credentials ? { credentials: this.config.credentials } : {}
305
+ });
306
+ if (response.status === 409) throw new AgentThreadLockedError(input.threadId);
307
+ if (!response.ok) {
308
+ const text = await response.text().catch(() => "");
309
+ throw new Error(text || response.statusText || String(response.status));
310
+ }
311
+ const payload = await response.json();
312
+ if (!payload.joinToken) throw new Error("missing joinToken");
313
+ return { joinToken: payload.joinToken };
314
+ } catch (error) {
315
+ if (error instanceof AgentThreadLockedError) throw error;
316
+ throw new Error(`REST ${mode} request failed: ${error instanceof Error ? error.message : String(error)}`);
317
+ }
318
+ });
319
+ }
320
+ requestConnectPlan$(input) {
321
+ return (0, rxjs.defer)(async () => {
322
+ try {
323
+ const response = await fetch(this.buildRuntimeUrl("connect"), {
324
+ method: "POST",
325
+ headers: {
326
+ "Content-Type": "application/json",
327
+ ...this.config.headers
328
+ },
329
+ body: JSON.stringify({
330
+ threadId: input.threadId,
331
+ runId: input.runId,
332
+ messages: input.messages,
333
+ tools: input.tools,
334
+ context: input.context,
335
+ state: input.state,
336
+ forwardedProps: input.forwardedProps,
337
+ lastSeenEventId: this.getReconnectCursor(input)
338
+ }),
339
+ ...this.config.credentials ? { credentials: this.config.credentials } : {}
340
+ });
341
+ if (response.status === 204) return null;
342
+ if (!response.ok) {
343
+ const text = await response.text().catch(() => "");
344
+ throw new Error(text || response.statusText || String(response.status));
345
+ }
346
+ return this.normalizeConnectPlan(await response.json());
347
+ } catch (error) {
348
+ throw new Error(`REST connect request failed: ${error instanceof Error ? error.message : String(error)}`);
349
+ }
350
+ });
351
+ }
352
+ normalizeConnectPlan(payload) {
353
+ const envelope = payload && typeof payload === "object" ? payload : null;
354
+ if (envelope?.mode === "bootstrap") return {
355
+ mode: "bootstrap",
356
+ latestEventId: typeof envelope.latestEventId === "string" ? envelope.latestEventId : null,
357
+ events: Array.isArray(envelope.events) ? envelope.events : []
358
+ };
359
+ if (envelope?.mode === "live") {
360
+ if (typeof envelope.joinToken !== "string" || envelope.joinToken.length === 0) throw new Error("missing joinToken");
361
+ return {
362
+ mode: "live",
363
+ joinToken: envelope.joinToken,
364
+ joinFromEventId: typeof envelope.joinFromEventId === "string" ? envelope.joinFromEventId : null,
365
+ events: Array.isArray(envelope.events) ? envelope.events : []
366
+ };
367
+ }
368
+ throw new Error("invalid connect plan");
369
+ }
370
+ observeThread$(input, credentials, options) {
371
+ return (0, rxjs.defer)(() => {
372
+ let ownSocket = null;
373
+ let ownChannel = null;
374
+ const socket$ = ɵphoenixSocket$({
375
+ url: this.config.url,
376
+ options: {
377
+ params: {
378
+ ...this.config.socketParams ?? {},
379
+ join_token: credentials.joinToken
380
+ },
381
+ reconnectAfterMs: (0, _copilotkit_shared.phoenixExponentialBackoff)(100, 1e4),
382
+ rejoinAfterMs: (0, _copilotkit_shared.phoenixExponentialBackoff)(1e3, 3e4)
383
+ }
384
+ }).pipe((0, rxjs_operators.tap)(({ socket }) => {
385
+ ownSocket = socket;
386
+ this.socket = ownSocket;
387
+ }), (0, rxjs_operators.shareReplay)({
388
+ bufferSize: 1,
389
+ refCount: true
390
+ }));
391
+ const { topic, params } = this.createThreadChannelDescriptor(input, options.streamMode, options.replayCursor);
392
+ const channel$ = ɵphoenixChannel$({
393
+ socket$,
394
+ topic,
395
+ params
396
+ }).pipe((0, rxjs_operators.tap)(({ channel }) => {
397
+ ownChannel = channel;
398
+ this.activeChannel = ownChannel;
399
+ }), (0, rxjs_operators.shareReplay)({
400
+ bufferSize: 1,
401
+ refCount: true
402
+ }));
403
+ const threadEvents$ = this.observeThreadEvents$(input.threadId, channel$, options).pipe((0, rxjs_operators.share)());
404
+ const threadCompleted$ = threadEvents$.pipe((0, rxjs_operators.ignoreElements)(), (0, rxjs_operators.endWith)(null), (0, rxjs_operators.take)(1));
405
+ return (0, rxjs.merge)(this.joinThreadChannel$(channel$), this.observeSocketHealth$(socket$).pipe((0, rxjs_operators.takeUntil)(threadCompleted$)), threadEvents$).pipe((0, rxjs_operators.finalize)(() => this.cleanupOwned(ownChannel, ownSocket)));
406
+ });
407
+ }
408
+ joinThreadChannel$(channel$) {
409
+ return ɵjoinPhoenixChannel$(channel$);
410
+ }
411
+ observeSocketHealth$(socket$) {
412
+ return ɵobservePhoenixSocketHealth$(ɵobservePhoenixSocketSignals$(socket$), 5);
413
+ }
414
+ observeThreadEvents$(threadId, channel$, options) {
415
+ return channel$.pipe((0, rxjs_operators.switchMap)(({ channel }) => this.observeChannelEvent$(channel, CLIENT_AG_UI_EVENT)), (0, rxjs_operators.tap)((payload) => {
416
+ this.updateLastSeenEventId(threadId, payload);
417
+ this.updateRunIdFromEvent(payload);
418
+ }), (0, rxjs_operators.mergeMap)((payload) => (0, rxjs.from)(this.createThreadNotifications(payload, options.completeOnRunError))), (0, rxjs.dematerialize)());
419
+ }
420
+ observeChannelEvent$(channel, eventName) {
421
+ return ɵobservePhoenixEvent$(channel, eventName);
422
+ }
423
+ createThreadNotifications(payload, completeOnRunError) {
424
+ if (payload.type === _ag_ui_client.EventType.RUN_FINISHED) return [rxjs.Notification.createNext(payload), rxjs.Notification.createComplete()];
425
+ if (payload.type === _ag_ui_client.EventType.RUN_ERROR) {
426
+ const errorMessage = payload.message ?? "Run error";
427
+ return completeOnRunError ? [rxjs.Notification.createNext(payload), rxjs.Notification.createComplete()] : [rxjs.Notification.createNext(payload), rxjs.Notification.createError(new Error(errorMessage))];
428
+ }
429
+ return [rxjs.Notification.createNext(payload)];
430
+ }
431
+ buildRuntimeUrl(mode) {
432
+ const path = `${this.config.runtimeUrl}/agent/${encodeURIComponent(this.config.agentId)}/${mode}`;
433
+ const origin = typeof window !== "undefined" && window.location ? window.location.origin : "http://localhost";
434
+ return new URL(path, new URL(this.config.runtimeUrl, origin)).toString();
435
+ }
436
+ createThreadChannelDescriptor(input, streamMode, replayCursor) {
437
+ const params = streamMode === "run" ? {
438
+ stream_mode: "run",
439
+ run_id: input.runId
440
+ } : {
441
+ stream_mode: "connect",
442
+ last_seen_event_id: replayCursor === void 0 ? this.getReconnectCursor(input) : replayCursor
443
+ };
444
+ return {
445
+ topic: `thread:${input.threadId}`,
446
+ params
447
+ };
448
+ }
449
+ getLastSeenEventId(threadId) {
450
+ return this.sharedState.lastSeenEventIds.get(threadId) ?? null;
451
+ }
452
+ getReconnectCursor(input) {
453
+ return this.hasLocalThreadMessages(input) ? this.getLastSeenEventId(input.threadId) : null;
454
+ }
455
+ hasLocalThreadMessages(input) {
456
+ return Array.isArray(input.messages) && input.messages.length > 0;
457
+ }
458
+ updateLastSeenEventId(threadId, payload) {
459
+ const eventId = this.readEventId(payload);
460
+ if (!eventId) return;
461
+ this.sharedState.lastSeenEventIds.set(threadId, eventId);
462
+ }
463
+ setLastSeenEventId(threadId, eventId) {
464
+ if (!eventId) return;
465
+ this.sharedState.lastSeenEventIds.set(threadId, eventId);
466
+ }
467
+ /**
468
+ * Keep `this.runId` in sync with the backend's actual run ID.
469
+ *
470
+ * During a `connect` (resume) flow the client generates a fresh `runId`
471
+ * via `prepareRunAgentInput`, but the backend is running under its own
472
+ * run ID. If the client later sends `STOP_RUN_EVENT` with the wrong
473
+ * `runId`, the gateway's runner channel will not match it and the agent
474
+ * keeps running. Extracting the run ID from live events fixes this.
475
+ *
476
+ * The runner normalises events to `run_id` (snake_case) before pushing
477
+ * to the gateway, so we check both `runId` and `run_id`.
478
+ */
479
+ updateRunIdFromEvent(payload) {
480
+ const record = payload;
481
+ const eventRunId = record.runId ?? record.run_id;
482
+ if (typeof eventRunId === "string" && eventRunId.length > 0) this.runId = eventRunId;
483
+ }
484
+ readEventId(payload) {
485
+ const metadata = payload.metadata;
486
+ if (!metadata || typeof metadata !== "object") return null;
487
+ const runnerEventId = metadata.cpki_event_id;
488
+ return typeof runnerEventId === "string" ? runnerEventId : null;
489
+ }
490
+ };
491
+
492
+ //#endregion
493
+ //#region src/agent.ts
494
+ function hasHeaders(agent) {
495
+ return "headers" in agent;
496
+ }
497
+ function hasCredentials(agent) {
498
+ return "credentials" in agent;
499
+ }
500
+ function isZodError(error) {
501
+ return error !== null && typeof error === "object" && "name" in error && error.name === "ZodError";
502
+ }
503
+ function isAbortError(error) {
504
+ return (error instanceof DOMException || error instanceof Error) && error.name === "AbortError";
505
+ }
506
+ function withAbortErrorHandling(observable) {
507
+ return observable.pipe((0, rxjs_operators.catchError)((error) => {
508
+ if (isZodError(error) || isAbortError(error)) return rxjs.EMPTY;
509
+ throw error;
510
+ }));
511
+ }
512
+ var ProxiedCopilotRuntimeAgent = class ProxiedCopilotRuntimeAgent extends _ag_ui_client.HttpAgent {
513
+ runtimeUrl;
514
+ credentials;
515
+ transport;
516
+ singleEndpointUrl;
517
+ runtimeMode;
518
+ intelligence;
519
+ delegate;
520
+ runtimeInfoPromise;
521
+ constructor(config) {
522
+ const normalizedRuntimeUrl = config.runtimeUrl ? config.runtimeUrl.replace(/\/$/, "") : void 0;
523
+ const transport = config.transport ?? "rest";
524
+ const runUrl = transport === "single" ? normalizedRuntimeUrl ?? config.runtimeUrl ?? "" : `${normalizedRuntimeUrl ?? config.runtimeUrl}/agent/${encodeURIComponent(config.agentId ?? "")}/run`;
525
+ if (!runUrl) throw new Error("ProxiedCopilotRuntimeAgent requires a runtimeUrl when transport is set to 'single'.");
526
+ super({
527
+ ...config,
528
+ url: runUrl
529
+ });
530
+ this.runtimeUrl = normalizedRuntimeUrl ?? config.runtimeUrl;
531
+ this.credentials = config.credentials;
532
+ this.transport = transport;
533
+ this.runtimeMode = config.runtimeMode ?? _copilotkit_shared.RUNTIME_MODE_SSE;
534
+ this.intelligence = config.intelligence;
535
+ if (this.transport === "single") this.singleEndpointUrl = this.runtimeUrl;
536
+ }
537
+ async detachActiveRun() {
538
+ if (this.delegate) await this.delegate.detachActiveRun();
539
+ await super.detachActiveRun();
540
+ }
541
+ abortRun() {
542
+ if (this.delegate) {
543
+ this.syncDelegate(this.delegate);
544
+ this.delegate.abortRun();
545
+ this.detachActiveRun();
546
+ return;
547
+ }
548
+ if (!this.agentId || !this.threadId) return;
549
+ if (typeof fetch === "undefined") return;
550
+ if (this.transport === "single") {
551
+ if (!this.singleEndpointUrl) return;
552
+ const headers = new Headers({
553
+ ...this.headers,
554
+ "Content-Type": "application/json"
555
+ });
556
+ fetch(this.singleEndpointUrl, {
557
+ method: "POST",
558
+ headers,
559
+ body: JSON.stringify({
560
+ method: "agent/stop",
561
+ params: {
562
+ agentId: this.agentId,
563
+ threadId: this.threadId
564
+ }
565
+ }),
566
+ ...this.credentials ? { credentials: this.credentials } : {}
567
+ }).catch((error) => {
568
+ console.error("ProxiedCopilotRuntimeAgent: stop request failed", error);
569
+ });
570
+ return;
571
+ }
572
+ if (!this.runtimeUrl) return;
573
+ const stopPath = `${this.runtimeUrl}/agent/${encodeURIComponent(this.agentId)}/stop/${encodeURIComponent(this.threadId)}`;
574
+ const origin = typeof window !== "undefined" && window.location ? window.location.origin : "http://localhost";
575
+ const base = new URL(this.runtimeUrl, origin);
576
+ const stopUrl = new URL(stopPath, base);
577
+ fetch(stopUrl.toString(), {
578
+ method: "POST",
579
+ headers: {
580
+ "Content-Type": "application/json",
581
+ ...this.headers
582
+ },
583
+ ...this.credentials ? { credentials: this.credentials } : {}
584
+ }).catch((error) => {
585
+ console.error("ProxiedCopilotRuntimeAgent: stop request failed", error);
586
+ });
587
+ }
588
+ async connectAgent(parameters, subscriber) {
589
+ if (this.runtimeMode !== _copilotkit_shared.RUNTIME_MODE_INTELLIGENCE) return super.connectAgent(parameters, subscriber);
590
+ if (this.delegate) await this.delegate.detachActiveRun();
591
+ await this.resolveDelegate();
592
+ const delegate = this.delegate;
593
+ const bridgeSub = delegate.subscribe({
594
+ onMessagesChanged: () => {
595
+ this.setMessages([...delegate.messages]);
596
+ },
597
+ onStateChanged: () => {
598
+ this.setState({ ...delegate.state });
599
+ },
600
+ onRunInitialized: () => {
601
+ this.isRunning = true;
602
+ },
603
+ onRunFinalized: () => {
604
+ this.isRunning = false;
605
+ },
606
+ onRunFailed: () => {
607
+ this.isRunning = false;
608
+ }
609
+ });
610
+ const forwardedSubs = this.subscribers.map((s) => delegate.subscribe(s));
611
+ try {
612
+ const result = await delegate.connectAgent(parameters, subscriber);
613
+ this.setMessages([...delegate.messages]);
614
+ this.setState({ ...delegate.state });
615
+ return result;
616
+ } finally {
617
+ this.isRunning = false;
618
+ bridgeSub.unsubscribe();
619
+ for (const sub of forwardedSubs) sub.unsubscribe();
620
+ }
621
+ }
622
+ connect(input) {
623
+ if (this.runtimeMode === _copilotkit_shared.RUNTIME_MODE_INTELLIGENCE) return this.#connectViaDelegate(input);
624
+ return this.#connectViaHttp(input);
625
+ }
626
+ run(input) {
627
+ if (this.runtimeMode === _copilotkit_shared.RUNTIME_MODE_INTELLIGENCE) return this.#runViaDelegate(input);
628
+ return this.#runViaHttp(input);
629
+ }
630
+ #connectViaDelegate(input) {
631
+ return (0, rxjs.defer)(() => (0, rxjs.from)(this.resolveDelegate())).pipe((0, rxjs_operators.switchMap)((delegate) => withAbortErrorHandling(delegate.connect(input))));
632
+ }
633
+ #connectViaHttp(input) {
634
+ if (this.transport === "single") {
635
+ if (!this.singleEndpointUrl) throw new Error("Single endpoint transport requires a runtimeUrl");
636
+ const requestInit = this.createSingleRouteRequestInit(input, "agent/connect", { agentId: this.agentId });
637
+ return withAbortErrorHandling((0, _ag_ui_client.transformHttpEventStream)((0, _ag_ui_client.runHttpRequest)(this.singleEndpointUrl, requestInit)));
638
+ }
639
+ return withAbortErrorHandling((0, _ag_ui_client.transformHttpEventStream)((0, _ag_ui_client.runHttpRequest)(`${this.runtimeUrl}/agent/${this.agentId}/connect`, this.requestInit(input))));
640
+ }
641
+ #runViaDelegate(input) {
642
+ return (0, rxjs.defer)(() => (0, rxjs.from)(this.resolveDelegate())).pipe((0, rxjs_operators.switchMap)((delegate) => withAbortErrorHandling(delegate.run(input))));
643
+ }
644
+ #runViaHttp(input) {
645
+ if (this.transport === "single") {
646
+ if (!this.singleEndpointUrl) throw new Error("Single endpoint transport requires a runtimeUrl");
647
+ const requestInit = this.createSingleRouteRequestInit(input, "agent/run", { agentId: this.agentId });
648
+ return withAbortErrorHandling((0, _ag_ui_client.transformHttpEventStream)((0, _ag_ui_client.runHttpRequest)(this.singleEndpointUrl, requestInit)));
649
+ }
650
+ return withAbortErrorHandling(super.run(input));
651
+ }
652
+ clone() {
653
+ const cloned = new ProxiedCopilotRuntimeAgent({
654
+ runtimeUrl: this.runtimeUrl,
655
+ agentId: this.agentId,
656
+ description: this.description,
657
+ headers: { ...this.headers },
658
+ credentials: this.credentials,
659
+ transport: this.transport,
660
+ runtimeMode: this.runtimeMode,
661
+ intelligence: this.intelligence
662
+ });
663
+ cloned.threadId = this.threadId;
664
+ cloned.setState(this.state);
665
+ cloned.setMessages(this.messages);
666
+ if (this.delegate) {
667
+ cloned.delegate = this.delegate.clone();
668
+ cloned.syncDelegate(cloned.delegate);
669
+ }
670
+ return cloned;
671
+ }
672
+ async resolveDelegate() {
673
+ await this.ensureRuntimeMode();
674
+ if (!this.delegate) {
675
+ if (this.runtimeMode !== _copilotkit_shared.RUNTIME_MODE_INTELLIGENCE) throw new Error("A delegate is only created for Intelligence mode");
676
+ this.delegate = this.createIntelligenceDelegate();
677
+ }
678
+ this.syncDelegate(this.delegate);
679
+ return this.delegate;
680
+ }
681
+ async ensureRuntimeMode() {
682
+ if (this.runtimeMode !== "pending") return;
683
+ if (!this.runtimeUrl) throw new Error("Runtime URL is not set");
684
+ this.runtimeInfoPromise ??= this.fetchRuntimeInfo().then((runtimeInfo) => {
685
+ this.runtimeMode = runtimeInfo.mode ?? _copilotkit_shared.RUNTIME_MODE_SSE;
686
+ this.intelligence = runtimeInfo.intelligence;
687
+ });
688
+ await this.runtimeInfoPromise;
689
+ }
690
+ async fetchRuntimeInfo() {
691
+ const headers = { ...this.headers };
692
+ let init;
693
+ let url;
694
+ if (this.transport === "single") {
695
+ if (!this.singleEndpointUrl) throw new Error("Single endpoint transport requires a runtimeUrl");
696
+ if (!headers["Content-Type"]) headers["Content-Type"] = "application/json";
697
+ url = this.runtimeUrl;
698
+ init = {
699
+ method: "POST",
700
+ body: JSON.stringify({ method: "info" })
701
+ };
702
+ } else {
703
+ url = `${this.runtimeUrl}/info`;
704
+ init = {};
705
+ }
706
+ const response = await fetch(url, {
707
+ ...init,
708
+ headers,
709
+ ...this.credentials ? { credentials: this.credentials } : {}
710
+ });
711
+ if (!response.ok) throw new Error(`Runtime info request failed with status ${response.status}`);
712
+ return await response.json();
713
+ }
714
+ createSingleRouteRequestInit(input, method, params) {
715
+ if (!this.agentId) throw new Error("ProxiedCopilotRuntimeAgent requires agentId to make runtime requests");
716
+ const baseInit = super.requestInit(input);
717
+ const headers = new Headers(baseInit.headers ?? {});
718
+ headers.set("Content-Type", "application/json");
719
+ headers.set("Accept", headers.get("Accept") ?? "text/event-stream");
720
+ let originalBody = void 0;
721
+ if (typeof baseInit.body === "string") try {
722
+ originalBody = JSON.parse(baseInit.body);
723
+ } catch (error) {
724
+ console.warn("ProxiedCopilotRuntimeAgent: failed to parse request body for single route transport", error);
725
+ }
726
+ const envelope = { method };
727
+ if (params && Object.keys(params).length > 0) envelope.params = params;
728
+ if (originalBody !== void 0) envelope.body = originalBody;
729
+ return {
730
+ ...baseInit,
731
+ headers,
732
+ body: JSON.stringify(envelope),
733
+ ...this.credentials ? { credentials: this.credentials } : {}
734
+ };
735
+ }
736
+ createIntelligenceDelegate() {
737
+ if (!this.runtimeUrl || !this.agentId || !this.intelligence?.wsUrl) throw new Error("Intelligence mode requires runtimeUrl, agentId, and intelligence websocket metadata");
738
+ return new IntelligenceAgent({
739
+ url: this.intelligence.wsUrl,
740
+ runtimeUrl: this.runtimeUrl,
741
+ agentId: this.agentId,
742
+ headers: { ...this.headers },
743
+ credentials: this.credentials
744
+ });
745
+ }
746
+ syncDelegate(delegate) {
747
+ delegate.agentId = this.agentId;
748
+ delegate.description = this.description;
749
+ delegate.threadId = this.threadId;
750
+ delegate.setMessages(this.messages);
751
+ delegate.setState(this.state);
752
+ if (hasHeaders(delegate)) delegate.headers = { ...this.headers };
753
+ if (hasCredentials(delegate)) delegate.credentials = this.credentials;
754
+ }
755
+ };
756
+
757
+ //#endregion
758
+ //#region src/core/agent-registry.ts
759
+ /**
760
+ * Manages agent registration, lifecycle, and runtime connectivity for CopilotKitCore.
761
+ * Handles both local development agents and remote runtime agents.
762
+ */
763
+ var AgentRegistry = class {
764
+ _agents = {};
765
+ localAgents = {};
766
+ remoteAgents = {};
767
+ _runtimeUrl;
768
+ _runtimeVersion;
769
+ _runtimeConnectionStatus = CopilotKitCoreRuntimeConnectionStatus.Disconnected;
770
+ _runtimeTransport = "rest";
771
+ _audioFileTranscriptionEnabled = false;
772
+ _runtimeMode = _copilotkit_shared.RUNTIME_MODE_SSE;
773
+ _intelligence;
774
+ _a2uiEnabled = false;
775
+ _licenseStatus;
776
+ constructor(core) {
777
+ this.core = core;
778
+ }
779
+ /**
780
+ * Get all agents as a readonly record
781
+ */
782
+ get agents() {
783
+ return this._agents;
784
+ }
785
+ get runtimeUrl() {
786
+ return this._runtimeUrl;
787
+ }
788
+ get runtimeVersion() {
789
+ return this._runtimeVersion;
790
+ }
791
+ get runtimeConnectionStatus() {
792
+ return this._runtimeConnectionStatus;
793
+ }
794
+ get runtimeTransport() {
795
+ return this._runtimeTransport;
796
+ }
797
+ get audioFileTranscriptionEnabled() {
798
+ return this._audioFileTranscriptionEnabled;
799
+ }
800
+ get runtimeMode() {
801
+ return this._runtimeMode;
802
+ }
803
+ get intelligence() {
804
+ return this._intelligence;
805
+ }
806
+ get a2uiEnabled() {
807
+ return this._a2uiEnabled;
808
+ }
809
+ get licenseStatus() {
810
+ return this._licenseStatus;
811
+ }
812
+ /**
813
+ * Initialize agents from configuration
814
+ */
815
+ initialize(agents) {
816
+ this.localAgents = this.assignAgentIds(agents);
817
+ this.applyHeadersToAgents(this.localAgents);
818
+ this._agents = this.localAgents;
819
+ }
820
+ /**
821
+ * Set the runtime URL and update connection
822
+ */
823
+ setRuntimeUrl(runtimeUrl) {
824
+ const normalizedRuntimeUrl = runtimeUrl ? runtimeUrl.replace(/\/$/, "") : void 0;
825
+ if (this._runtimeUrl === normalizedRuntimeUrl) return;
826
+ this._runtimeUrl = normalizedRuntimeUrl;
827
+ this.updateRuntimeConnection();
828
+ }
829
+ setRuntimeTransport(runtimeTransport) {
830
+ if (this._runtimeTransport === runtimeTransport) return;
831
+ this._runtimeTransport = runtimeTransport;
832
+ this.updateRuntimeConnection();
833
+ }
834
+ /**
835
+ * Set all agents at once (for development use)
836
+ */
837
+ setAgents__unsafe_dev_only(agents) {
838
+ Object.entries(agents).forEach(([id, agent]) => {
839
+ if (agent) this.validateAndAssignAgentId(id, agent);
840
+ });
841
+ this.localAgents = agents;
842
+ this._agents = {
843
+ ...this.localAgents,
844
+ ...this.remoteAgents
845
+ };
846
+ this.applyHeadersToAgents(this._agents);
847
+ this.notifyAgentsChanged();
848
+ }
849
+ /**
850
+ * Add a single agent (for development use)
851
+ */
852
+ addAgent__unsafe_dev_only({ id, agent }) {
853
+ this.validateAndAssignAgentId(id, agent);
854
+ this.localAgents[id] = agent;
855
+ this.applyHeadersToAgent(agent);
856
+ this._agents = {
857
+ ...this.localAgents,
858
+ ...this.remoteAgents
859
+ };
860
+ this.notifyAgentsChanged();
861
+ }
862
+ /**
863
+ * Remove an agent by ID (for development use)
864
+ */
865
+ removeAgent__unsafe_dev_only(id) {
866
+ delete this.localAgents[id];
867
+ this._agents = {
868
+ ...this.localAgents,
869
+ ...this.remoteAgents
870
+ };
871
+ this.notifyAgentsChanged();
872
+ }
873
+ /**
874
+ * Get an agent by ID
875
+ */
876
+ getAgent(id) {
877
+ if (id in this._agents) return this._agents[id];
878
+ if (this.runtimeUrl !== void 0 && (this.runtimeConnectionStatus === CopilotKitCoreRuntimeConnectionStatus.Disconnected || this.runtimeConnectionStatus === CopilotKitCoreRuntimeConnectionStatus.Connecting)) return;
879
+ console.warn(`Agent ${id} not found`);
880
+ }
881
+ /**
882
+ * Apply current headers to an agent
883
+ */
884
+ applyHeadersToAgent(agent) {
885
+ if (agent instanceof _ag_ui_client.HttpAgent) agent.headers = { ...this.core.headers };
886
+ }
887
+ /**
888
+ * Apply current headers to all agents
889
+ */
890
+ applyHeadersToAgents(agents) {
891
+ Object.values(agents).forEach((agent) => {
892
+ this.applyHeadersToAgent(agent);
893
+ });
894
+ }
895
+ /**
896
+ * Apply current credentials to an agent
897
+ */
898
+ applyCredentialsToAgent(agent) {
899
+ if (agent instanceof ProxiedCopilotRuntimeAgent) agent.credentials = this.core.credentials;
900
+ }
901
+ /**
902
+ * Apply current credentials to all agents
903
+ */
904
+ applyCredentialsToAgents(agents) {
905
+ Object.values(agents).forEach((agent) => {
906
+ this.applyCredentialsToAgent(agent);
907
+ });
908
+ }
909
+ /**
910
+ * Update runtime connection and fetch remote agents
911
+ */
912
+ async updateRuntimeConnection() {
913
+ if (typeof window === "undefined") return;
914
+ if (!this.runtimeUrl) {
915
+ this._runtimeConnectionStatus = CopilotKitCoreRuntimeConnectionStatus.Disconnected;
916
+ this._runtimeVersion = void 0;
917
+ this._audioFileTranscriptionEnabled = false;
918
+ this._runtimeMode = _copilotkit_shared.RUNTIME_MODE_SSE;
919
+ this._intelligence = void 0;
920
+ this._a2uiEnabled = false;
921
+ this.remoteAgents = {};
922
+ this._agents = this.localAgents;
923
+ await this.notifyRuntimeStatusChanged(CopilotKitCoreRuntimeConnectionStatus.Disconnected);
924
+ await this.notifyAgentsChanged();
925
+ return;
926
+ }
927
+ this._runtimeConnectionStatus = CopilotKitCoreRuntimeConnectionStatus.Connecting;
928
+ await this.notifyRuntimeStatusChanged(CopilotKitCoreRuntimeConnectionStatus.Connecting);
929
+ try {
930
+ const runtimeInfoResponse = await this.fetchRuntimeInfo();
931
+ const { version, ...runtimeInfo } = runtimeInfoResponse;
932
+ const credentials = this.core.credentials;
933
+ this.remoteAgents = Object.fromEntries(Object.entries(runtimeInfo.agents).map(([id, { description }]) => {
934
+ const agent = new ProxiedCopilotRuntimeAgent({
935
+ runtimeUrl: this.runtimeUrl,
936
+ agentId: id,
937
+ description,
938
+ transport: this._runtimeTransport,
939
+ credentials,
940
+ runtimeMode: runtimeInfoResponse.mode ?? _copilotkit_shared.RUNTIME_MODE_SSE,
941
+ intelligence: runtimeInfoResponse.intelligence
942
+ });
943
+ this.applyHeadersToAgent(agent);
944
+ return [id, agent];
945
+ }));
946
+ this._agents = {
947
+ ...this.localAgents,
948
+ ...this.remoteAgents
949
+ };
950
+ this._runtimeConnectionStatus = CopilotKitCoreRuntimeConnectionStatus.Connected;
951
+ this._runtimeVersion = version;
952
+ this._audioFileTranscriptionEnabled = runtimeInfoResponse.audioFileTranscriptionEnabled ?? false;
953
+ this._runtimeMode = runtimeInfoResponse.mode ?? _copilotkit_shared.RUNTIME_MODE_SSE;
954
+ this._intelligence = runtimeInfoResponse.intelligence;
955
+ this._a2uiEnabled = runtimeInfoResponse.a2uiEnabled ?? false;
956
+ this._licenseStatus = runtimeInfoResponse.licenseStatus;
957
+ await this.notifyRuntimeStatusChanged(CopilotKitCoreRuntimeConnectionStatus.Connected);
958
+ await this.notifyAgentsChanged();
959
+ } catch (error) {
960
+ this._runtimeConnectionStatus = CopilotKitCoreRuntimeConnectionStatus.Error;
961
+ this._runtimeVersion = void 0;
962
+ this._audioFileTranscriptionEnabled = false;
963
+ this._runtimeMode = _copilotkit_shared.RUNTIME_MODE_SSE;
964
+ this._intelligence = void 0;
965
+ this._a2uiEnabled = false;
966
+ this.remoteAgents = {};
967
+ this._agents = this.localAgents;
968
+ await this.notifyRuntimeStatusChanged(CopilotKitCoreRuntimeConnectionStatus.Error);
969
+ await this.notifyAgentsChanged();
970
+ const message = error instanceof Error ? error.message : JSON.stringify(error);
971
+ _copilotkit_shared.logger.warn(`Failed to load runtime info (${this.runtimeUrl}/info): ${message}`);
972
+ const runtimeError = error instanceof Error ? error : new Error(String(error));
973
+ await this.core.emitError({
974
+ error: runtimeError,
975
+ code: CopilotKitCoreErrorCode.RUNTIME_INFO_FETCH_FAILED,
976
+ context: { runtimeUrl: this.runtimeUrl }
977
+ });
978
+ }
979
+ }
980
+ async fetchRuntimeInfo() {
981
+ if (!this.runtimeUrl) throw new Error("Runtime URL is not set");
982
+ const baseHeaders = this.core.headers;
983
+ const credentials = this.core.credentials;
984
+ const headers = { ...baseHeaders };
985
+ if (this._runtimeTransport === "single") {
986
+ if (!headers["Content-Type"]) headers["Content-Type"] = "application/json";
987
+ const response = await fetch(this.runtimeUrl, {
988
+ method: "POST",
989
+ headers,
990
+ body: JSON.stringify({ method: "info" }),
991
+ ...credentials ? { credentials } : {}
992
+ });
993
+ if ("ok" in response && !response.ok) throw new Error(`Runtime info request failed with status ${response.status}`);
994
+ return await response.json();
995
+ }
996
+ const response = await fetch(`${this.runtimeUrl}/info`, {
997
+ headers,
998
+ ...credentials ? { credentials } : {}
999
+ });
1000
+ if ("ok" in response && !response.ok) throw new Error(`Runtime info request failed with status ${response.status}`);
1001
+ return await response.json();
1002
+ }
1003
+ /**
1004
+ * Assign agent IDs to a record of agents
1005
+ */
1006
+ assignAgentIds(agents) {
1007
+ Object.entries(agents).forEach(([id, agent]) => {
1008
+ if (agent) this.validateAndAssignAgentId(id, agent);
1009
+ });
1010
+ return agents;
1011
+ }
1012
+ /**
1013
+ * Validate and assign an agent ID
1014
+ */
1015
+ validateAndAssignAgentId(registrationId, agent) {
1016
+ if (agent.agentId && agent.agentId !== registrationId) throw new Error(`Agent registration mismatch: Agent with ID "${agent.agentId}" cannot be registered under key "${registrationId}". The agent ID must match the registration key or be undefined.`);
1017
+ if (!agent.agentId) agent.agentId = registrationId;
1018
+ }
1019
+ /**
1020
+ * Notify subscribers of runtime status changes
1021
+ */
1022
+ async notifyRuntimeStatusChanged(status) {
1023
+ await this.core.notifySubscribers((subscriber) => subscriber.onRuntimeConnectionStatusChanged?.({
1024
+ copilotkit: this.core,
1025
+ status
1026
+ }), "Error in CopilotKitCore subscriber (onRuntimeConnectionStatusChanged):");
1027
+ }
1028
+ /**
1029
+ * Notify subscribers of agent changes
1030
+ */
1031
+ async notifyAgentsChanged() {
1032
+ await this.core.notifySubscribers((subscriber) => subscriber.onAgentsChanged?.({
1033
+ copilotkit: this.core,
1034
+ agents: this._agents
1035
+ }), "Subscriber onAgentsChanged error:");
1036
+ }
1037
+ };
1038
+
1039
+ //#endregion
1040
+ //#region src/core/context-store.ts
1041
+ /**
1042
+ * Manages context storage and lifecycle for CopilotKitCore.
1043
+ * Context represents additional information available to agents during execution.
1044
+ */
1045
+ var ContextStore = class {
1046
+ _context = {};
1047
+ constructor(core) {
1048
+ this.core = core;
1049
+ }
1050
+ /**
1051
+ * Get all context entries as a readonly record
1052
+ */
1053
+ get context() {
1054
+ return this._context;
1055
+ }
1056
+ /**
1057
+ * Add a new context entry
1058
+ * @returns The ID of the created context entry
1059
+ */
1060
+ addContext({ description, value }) {
1061
+ const id = (0, _copilotkit_shared.randomUUID)();
1062
+ this._context[id] = {
1063
+ description,
1064
+ value
1065
+ };
1066
+ this.notifySubscribers();
1067
+ return id;
1068
+ }
1069
+ /**
1070
+ * Remove a context entry by ID
1071
+ */
1072
+ removeContext(id) {
1073
+ delete this._context[id];
1074
+ this.notifySubscribers();
1075
+ }
1076
+ /**
1077
+ * Notify all subscribers of context changes
1078
+ */
1079
+ async notifySubscribers() {
1080
+ await this.core.notifySubscribers((subscriber) => subscriber.onContextChanged?.({
1081
+ copilotkit: this.core,
1082
+ context: this._context
1083
+ }), "Subscriber onContextChanged error:");
1084
+ }
1085
+ };
1086
+
1087
+ //#endregion
1088
+ //#region src/core/suggestion-engine.ts
1089
+ /**
1090
+ * Manages suggestion generation, streaming, and lifecycle for CopilotKitCore.
1091
+ * Handles both dynamic (AI-generated) and static suggestions.
1092
+ */
1093
+ var SuggestionEngine = class {
1094
+ _suggestionsConfig = {};
1095
+ _suggestions = {};
1096
+ _runningSuggestions = {};
1097
+ constructor(core) {
1098
+ this.core = core;
1099
+ }
1100
+ /**
1101
+ * Initialize with suggestion configs
1102
+ */
1103
+ initialize(suggestionsConfig) {
1104
+ for (const config of suggestionsConfig) this._suggestionsConfig[(0, _copilotkit_shared.randomUUID)()] = config;
1105
+ }
1106
+ /**
1107
+ * Add a suggestion configuration
1108
+ * @returns The ID of the created config
1109
+ */
1110
+ addSuggestionsConfig(config) {
1111
+ const id = (0, _copilotkit_shared.randomUUID)();
1112
+ this._suggestionsConfig[id] = config;
1113
+ this.notifySuggestionsConfigChanged();
1114
+ return id;
1115
+ }
1116
+ /**
1117
+ * Remove a suggestion configuration by ID
1118
+ */
1119
+ removeSuggestionsConfig(id) {
1120
+ delete this._suggestionsConfig[id];
1121
+ this.notifySuggestionsConfigChanged();
1122
+ }
1123
+ /**
1124
+ * Reload suggestions for a specific agent
1125
+ * This triggers generation of new suggestions based on current configs
1126
+ *
1127
+ * @param agentId - The consumer agent ID
1128
+ * @param consumerAgent - Optional: the specific agent instance whose messages should be used
1129
+ * for availability filtering and context. When running with per-thread clones, the thread
1130
+ * clone holds the conversation messages; passing it here ensures dynamic suggestions fire
1131
+ * after the first message even though the registry agent has an empty message list.
1132
+ */
1133
+ reloadSuggestions(agentId, consumerAgent) {
1134
+ this.clearSuggestions(agentId);
1135
+ const agent = consumerAgent ?? this.core.getAgent(agentId);
1136
+ if (!agent) return;
1137
+ const messageCount = agent.messages?.length ?? 0;
1138
+ let hasAnySuggestions = false;
1139
+ for (const config of Object.values(this._suggestionsConfig)) {
1140
+ if (config.consumerAgentId !== void 0 && config.consumerAgentId !== "*" && config.consumerAgentId !== agentId) continue;
1141
+ if (!this.shouldShowSuggestions(config, messageCount)) continue;
1142
+ const suggestionId = (0, _copilotkit_shared.randomUUID)();
1143
+ if (isDynamicSuggestionsConfig(config)) {
1144
+ if (!hasAnySuggestions) {
1145
+ hasAnySuggestions = true;
1146
+ this.notifySuggestionsStartedLoading(agentId);
1147
+ }
1148
+ this.generateSuggestions(suggestionId, config, agentId, agent);
1149
+ } else if (isStaticSuggestionsConfig(config)) this.addStaticSuggestions(suggestionId, config, agentId);
1150
+ }
1151
+ }
1152
+ /**
1153
+ * Clear all suggestions for a specific agent
1154
+ */
1155
+ clearSuggestions(agentId) {
1156
+ const runningAgents = this._runningSuggestions[agentId];
1157
+ if (runningAgents) {
1158
+ for (const agent of runningAgents) agent.abortRun();
1159
+ delete this._runningSuggestions[agentId];
1160
+ }
1161
+ this._suggestions[agentId] = {};
1162
+ this.notifySuggestionsChanged(agentId, []);
1163
+ }
1164
+ /**
1165
+ * Get current suggestions for an agent
1166
+ */
1167
+ getSuggestions(agentId) {
1168
+ return {
1169
+ suggestions: Object.values(this._suggestions[agentId] ?? {}).flat(),
1170
+ isLoading: (this._runningSuggestions[agentId]?.length ?? 0) > 0
1171
+ };
1172
+ }
1173
+ /**
1174
+ * Generate suggestions using a provider agent
1175
+ */
1176
+ async generateSuggestions(suggestionId, config, consumerAgentId, consumerAgent) {
1177
+ let agent = void 0;
1178
+ try {
1179
+ const suggestionsProviderAgent = this.core.getAgent(config.providerAgentId ?? "default");
1180
+ if (!suggestionsProviderAgent) throw new Error(`Suggestions provider agent not found: ${config.providerAgentId}`);
1181
+ const suggestionsConsumerAgent = consumerAgent ?? this.core.getAgent(consumerAgentId);
1182
+ if (!suggestionsConsumerAgent) throw new Error(`Suggestions consumer agent not found: ${consumerAgentId}`);
1183
+ agent = suggestionsProviderAgent.clone();
1184
+ agent.threadId = suggestionId;
1185
+ agent.messages = JSON.parse(JSON.stringify(suggestionsConsumerAgent.messages));
1186
+ agent.state = JSON.parse(JSON.stringify(suggestionsConsumerAgent.state));
1187
+ this._suggestions[consumerAgentId] = {
1188
+ ...this._suggestions[consumerAgentId] ?? {},
1189
+ [suggestionId]: []
1190
+ };
1191
+ this._runningSuggestions[consumerAgentId] = [...this._runningSuggestions[consumerAgentId] ?? [], agent];
1192
+ agent.addMessage({
1193
+ id: suggestionId,
1194
+ role: "user",
1195
+ content: [
1196
+ `Suggest what the user could say next. Provide clear, highly relevant suggestions by calling the \`copilotkitSuggest\` tool.`,
1197
+ `Provide at least ${config.minSuggestions ?? 1} and at most ${config.maxSuggestions ?? 3} suggestions.`,
1198
+ `The user has the following tools available: ${JSON.stringify(this.core.buildFrontendTools(consumerAgentId))}.`,
1199
+ ` ${config.instructions}`
1200
+ ].join("\n")
1201
+ });
1202
+ await agent.runAgent({
1203
+ context: Object.values(this.core.context),
1204
+ forwardedProps: {
1205
+ ...this.core.properties,
1206
+ toolChoice: {
1207
+ type: "function",
1208
+ function: { name: "copilotkitSuggest" }
1209
+ }
1210
+ },
1211
+ tools: [SUGGEST_TOOL]
1212
+ }, { onMessagesChanged: ({ messages }) => {
1213
+ this.extractSuggestions(messages, suggestionId, consumerAgentId, true);
1214
+ } });
1215
+ } catch (error) {
1216
+ console.warn("Error generating suggestions:", error);
1217
+ } finally {
1218
+ this.finalizeSuggestions(suggestionId, consumerAgentId);
1219
+ const runningAgents = this._runningSuggestions[consumerAgentId];
1220
+ if (agent && runningAgents) {
1221
+ const filteredAgents = runningAgents.filter((a) => a !== agent);
1222
+ this._runningSuggestions[consumerAgentId] = filteredAgents;
1223
+ if (filteredAgents.length === 0) {
1224
+ delete this._runningSuggestions[consumerAgentId];
1225
+ await this.notifySuggestionsFinishedLoading(consumerAgentId);
1226
+ }
1227
+ }
1228
+ }
1229
+ }
1230
+ /**
1231
+ * Finalize suggestions by marking them as no longer loading
1232
+ */
1233
+ finalizeSuggestions(suggestionId, consumerAgentId) {
1234
+ const agentSuggestions = this._suggestions[consumerAgentId];
1235
+ const currentSuggestions = agentSuggestions?.[suggestionId];
1236
+ if (agentSuggestions && currentSuggestions && currentSuggestions.length > 0) {
1237
+ const finalizedSuggestions = currentSuggestions.filter((suggestion) => suggestion.title !== "" || suggestion.message !== "").map((suggestion) => ({
1238
+ ...suggestion,
1239
+ isLoading: false
1240
+ }));
1241
+ if (finalizedSuggestions.length > 0) agentSuggestions[suggestionId] = finalizedSuggestions;
1242
+ else delete agentSuggestions[suggestionId];
1243
+ const allSuggestions = Object.values(this._suggestions[consumerAgentId] ?? {}).flat();
1244
+ this.notifySuggestionsChanged(consumerAgentId, allSuggestions, "finalized");
1245
+ }
1246
+ }
1247
+ /**
1248
+ * Extract suggestions from messages (called during streaming)
1249
+ */
1250
+ extractSuggestions(messages, suggestionId, consumerAgentId, isRunning) {
1251
+ const idx = messages.findIndex((message) => message.id === suggestionId);
1252
+ if (idx == -1) return;
1253
+ const suggestions = [];
1254
+ const newMessages = messages.slice(idx + 1);
1255
+ for (const message of newMessages) if (message.role === "assistant" && message.toolCalls) {
1256
+ for (const toolCall of message.toolCalls) if (toolCall.function.name === "copilotkitSuggest") {
1257
+ const parsed = (0, _copilotkit_shared.partialJSONParse)(Array.isArray(toolCall.function.arguments) ? toolCall.function.arguments.join("") : toolCall.function.arguments);
1258
+ if (parsed && typeof parsed === "object" && "suggestions" in parsed) {
1259
+ const parsedSuggestions = parsed.suggestions;
1260
+ if (Array.isArray(parsedSuggestions)) {
1261
+ for (const item of parsedSuggestions) if (item && typeof item === "object" && "title" in item) suggestions.push({
1262
+ title: item.title ?? "",
1263
+ message: item.message ?? "",
1264
+ isLoading: false
1265
+ });
1266
+ }
1267
+ }
1268
+ }
1269
+ }
1270
+ if (isRunning && suggestions.length > 0) suggestions[suggestions.length - 1].isLoading = true;
1271
+ const agentSuggestions = this._suggestions[consumerAgentId];
1272
+ if (agentSuggestions) {
1273
+ agentSuggestions[suggestionId] = suggestions;
1274
+ const allSuggestions = Object.values(this._suggestions[consumerAgentId] ?? {}).flat();
1275
+ this.notifySuggestionsChanged(consumerAgentId, allSuggestions, "suggestions changed");
1276
+ }
1277
+ }
1278
+ /**
1279
+ * Notify subscribers of suggestions config changes
1280
+ */
1281
+ async notifySuggestionsConfigChanged() {
1282
+ await this.core.notifySubscribers((subscriber) => subscriber.onSuggestionsConfigChanged?.({
1283
+ copilotkit: this.core,
1284
+ suggestionsConfig: this._suggestionsConfig
1285
+ }), "Subscriber onSuggestionsConfigChanged error:");
1286
+ }
1287
+ /**
1288
+ * Notify subscribers of suggestions changes
1289
+ */
1290
+ async notifySuggestionsChanged(agentId, suggestions, context = "") {
1291
+ await this.core.notifySubscribers((subscriber) => subscriber.onSuggestionsChanged?.({
1292
+ copilotkit: this.core,
1293
+ agentId,
1294
+ suggestions
1295
+ }), `Subscriber onSuggestionsChanged error: ${context}`);
1296
+ }
1297
+ /**
1298
+ * Notify subscribers that suggestions started loading
1299
+ */
1300
+ async notifySuggestionsStartedLoading(agentId) {
1301
+ await this.core.notifySubscribers((subscriber) => subscriber.onSuggestionsStartedLoading?.({
1302
+ copilotkit: this.core,
1303
+ agentId
1304
+ }), "Subscriber onSuggestionsStartedLoading error:");
1305
+ }
1306
+ /**
1307
+ * Notify subscribers that suggestions finished loading
1308
+ */
1309
+ async notifySuggestionsFinishedLoading(agentId) {
1310
+ await this.core.notifySubscribers((subscriber) => subscriber.onSuggestionsFinishedLoading?.({
1311
+ copilotkit: this.core,
1312
+ agentId
1313
+ }), "Subscriber onSuggestionsFinishedLoading error:");
1314
+ }
1315
+ /**
1316
+ * Check if suggestions should be shown based on availability and message count
1317
+ */
1318
+ shouldShowSuggestions(config, messageCount) {
1319
+ const availability = config.available;
1320
+ if (!availability) if (isDynamicSuggestionsConfig(config)) return messageCount > 0;
1321
+ else return messageCount === 0;
1322
+ switch (availability) {
1323
+ case "disabled": return false;
1324
+ case "before-first-message": return messageCount === 0;
1325
+ case "after-first-message": return messageCount > 0;
1326
+ case "always": return true;
1327
+ default: return false;
1328
+ }
1329
+ }
1330
+ /**
1331
+ * Add static suggestions directly without AI generation
1332
+ */
1333
+ addStaticSuggestions(suggestionId, config, consumerAgentId) {
1334
+ const suggestions = config.suggestions.map((s) => ({
1335
+ ...s,
1336
+ isLoading: false
1337
+ }));
1338
+ this._suggestions[consumerAgentId] = {
1339
+ ...this._suggestions[consumerAgentId] ?? {},
1340
+ [suggestionId]: suggestions
1341
+ };
1342
+ const allSuggestions = Object.values(this._suggestions[consumerAgentId] ?? {}).flat();
1343
+ this.notifySuggestionsChanged(consumerAgentId, allSuggestions, "static suggestions added");
1344
+ }
1345
+ };
1346
+ /**
1347
+ * Type guard for dynamic suggestions config
1348
+ */
1349
+ function isDynamicSuggestionsConfig(config) {
1350
+ return "instructions" in config;
1351
+ }
1352
+ /**
1353
+ * Type guard for static suggestions config
1354
+ */
1355
+ function isStaticSuggestionsConfig(config) {
1356
+ return "suggestions" in config;
1357
+ }
1358
+ /**
1359
+ * The tool definition for AI-generated suggestions
1360
+ */
1361
+ const SUGGEST_TOOL = {
1362
+ name: "copilotkitSuggest",
1363
+ description: "Suggest what the user could say next",
1364
+ parameters: {
1365
+ type: "object",
1366
+ properties: { suggestions: {
1367
+ type: "array",
1368
+ description: "List of suggestions shown to the user as buttons.",
1369
+ items: {
1370
+ type: "object",
1371
+ properties: {
1372
+ title: {
1373
+ type: "string",
1374
+ description: "The title of the suggestion. This is shown as a button and should be short."
1375
+ },
1376
+ message: {
1377
+ type: "string",
1378
+ description: "The message to send when the suggestion is clicked. This should be a clear, complete sentence and will be sent as an instruction to the AI."
1379
+ }
1380
+ },
1381
+ required: ["title", "message"]
1382
+ }
1383
+ } },
1384
+ required: ["suggestions"]
1385
+ }
1386
+ };
1387
+
1388
+ //#endregion
1389
+ //#region src/core/run-handler.ts
1390
+ /**
1391
+ * Handles agent execution, tool calling, and agent connectivity for CopilotKitCore.
1392
+ * Manages the complete lifecycle of agent runs including tool execution and follow-ups.
1393
+ */
1394
+ var RunHandler = class {
1395
+ _tools = [];
1396
+ /**
1397
+ * Tracks whether the current run (including in-flight tool execution)
1398
+ * has been aborted via `stopAgent()` or `agent.abortRun()`. Created
1399
+ * fresh in `runAgent()`, aborted by `abortCurrentRun()`.
1400
+ */
1401
+ _runAbortController = null;
1402
+ /**
1403
+ * Tracks recursive `runAgent` depth so that the abort controller and
1404
+ * `agent.abortRun()` intercept are only set up / torn down at the
1405
+ * top-level call, not on follow-up recursive calls from
1406
+ * `processAgentResult`.
1407
+ */
1408
+ _runDepth = 0;
1409
+ constructor(core) {
1410
+ this.core = core;
1411
+ }
1412
+ /**
1413
+ * Abort the current run. Called by `CopilotKitCore.stopAgent()` to signal
1414
+ * that in-flight tool handlers should stop and `processAgentResult` should
1415
+ * not start a follow-up run.
1416
+ */
1417
+ abortCurrentRun() {
1418
+ this._runAbortController?.abort();
1419
+ }
1420
+ /**
1421
+ * Typed access to CopilotKitCore's internal ("friend") methods.
1422
+ * Centralises the single unavoidable cast so call-sites stay clean.
1423
+ */
1424
+ get _internal() {
1425
+ return this.core;
1426
+ }
1427
+ /**
1428
+ * Get all tools as a readonly array
1429
+ */
1430
+ get tools() {
1431
+ return this._tools;
1432
+ }
1433
+ /**
1434
+ * Initialize with tools
1435
+ */
1436
+ initialize(tools) {
1437
+ this._tools = tools;
1438
+ }
1439
+ /**
1440
+ * Add a tool to the registry
1441
+ */
1442
+ addTool(tool) {
1443
+ if (this._tools.findIndex((t) => t.name === tool.name && t.agentId === tool.agentId) !== -1) {
1444
+ _copilotkit_shared.logger.warn(`Tool already exists: '${tool.name}' for agent '${tool.agentId || "global"}', skipping.`);
1445
+ return;
1446
+ }
1447
+ this._tools.push(tool);
1448
+ }
1449
+ /**
1450
+ * Remove a tool by name and optionally by agentId
1451
+ */
1452
+ removeTool(id, agentId) {
1453
+ this._tools = this._tools.filter((tool) => {
1454
+ if (agentId !== void 0) return !(tool.name === id && tool.agentId === agentId);
1455
+ return !(tool.name === id && !tool.agentId);
1456
+ });
1457
+ }
1458
+ /**
1459
+ * Get a tool by name and optionally by agentId.
1460
+ * If agentId is provided, it will first look for an agent-specific tool,
1461
+ * then fall back to a global tool with the same name.
1462
+ */
1463
+ getTool(params) {
1464
+ const { toolName, agentId } = params;
1465
+ if (agentId) {
1466
+ const agentTool = this._tools.find((tool) => tool.name === toolName && tool.agentId === agentId);
1467
+ if (agentTool) return agentTool;
1468
+ }
1469
+ return this._tools.find((tool) => tool.name === toolName && !tool.agentId);
1470
+ }
1471
+ /**
1472
+ * Set all tools at once. Replaces existing tools.
1473
+ */
1474
+ setTools(tools) {
1475
+ this._tools = [...tools];
1476
+ }
1477
+ /**
1478
+ * Connect an agent (establish initial connection)
1479
+ */
1480
+ async connectAgent({ agent }) {
1481
+ try {
1482
+ await agent.detachActiveRun();
1483
+ agent.setMessages([]);
1484
+ agent.setState({});
1485
+ if (agent instanceof _ag_ui_client.HttpAgent) agent.headers = { ...this._internal.headers };
1486
+ const runAgentResult = await agent.connectAgent({
1487
+ forwardedProps: this._internal.properties,
1488
+ tools: this.buildFrontendTools(agent.agentId),
1489
+ context: Object.values(this._internal.context)
1490
+ }, this.createAgentErrorSubscriber(agent));
1491
+ return this.processAgentResult({
1492
+ runAgentResult,
1493
+ agent
1494
+ });
1495
+ } catch (error) {
1496
+ const connectError = error instanceof Error ? error : new Error(String(error));
1497
+ if (!(connectError.name === "AbortError" || connectError.message === "Fetch is aborted" || connectError.message === "signal is aborted without reason" || connectError.message === "component unmounted")) {
1498
+ const context = {};
1499
+ if (agent.agentId) context.agentId = agent.agentId;
1500
+ await this._internal.emitError({
1501
+ error: connectError,
1502
+ code: CopilotKitCoreErrorCode.AGENT_CONNECT_FAILED,
1503
+ context
1504
+ });
1505
+ }
1506
+ return { newMessages: [] };
1507
+ }
1508
+ }
1509
+ /**
1510
+ * Run an agent
1511
+ */
1512
+ async runAgent({ agent, forwardedProps }) {
1513
+ if (agent.agentId) this._internal.suggestionEngine.clearSuggestions(agent.agentId);
1514
+ if (agent instanceof _ag_ui_client.HttpAgent) agent.headers = { ...this._internal.headers };
1515
+ if (agent.detachActiveRun) await agent.detachActiveRun();
1516
+ this._internal.subscribeAgentToStateManager(agent);
1517
+ const isTopLevel = this._runDepth === 0;
1518
+ let originalAbortRun;
1519
+ if (isTopLevel) {
1520
+ this._runAbortController = new AbortController();
1521
+ const controller = this._runAbortController;
1522
+ originalAbortRun = agent.abortRun.bind(agent);
1523
+ agent.abortRun = () => {
1524
+ controller.abort();
1525
+ originalAbortRun();
1526
+ };
1527
+ }
1528
+ this._runDepth++;
1529
+ try {
1530
+ const runAgentResult = await agent.runAgent({
1531
+ forwardedProps: {
1532
+ ...this._internal.properties,
1533
+ ...forwardedProps
1534
+ },
1535
+ tools: this.buildFrontendTools(agent.agentId),
1536
+ context: Object.values(this._internal.context)
1537
+ }, this.createAgentErrorSubscriber(agent));
1538
+ return await this.processAgentResult({
1539
+ runAgentResult,
1540
+ agent
1541
+ });
1542
+ } catch (error) {
1543
+ const runError = error instanceof Error ? error : new Error(String(error));
1544
+ const context = {};
1545
+ if (agent.agentId) context.agentId = agent.agentId;
1546
+ await this._internal.emitError({
1547
+ error: runError,
1548
+ code: CopilotKitCoreErrorCode.AGENT_RUN_FAILED,
1549
+ context
1550
+ });
1551
+ return { newMessages: [] };
1552
+ } finally {
1553
+ this._runDepth--;
1554
+ if (isTopLevel && originalAbortRun) agent.abortRun = originalAbortRun;
1555
+ }
1556
+ }
1557
+ /**
1558
+ * Process agent result and execute tools
1559
+ */
1560
+ async processAgentResult({ runAgentResult, agent }) {
1561
+ const { newMessages } = runAgentResult;
1562
+ const agentId = agent.agentId;
1563
+ let needsFollowUp = false;
1564
+ for (const message of newMessages) if (message.role === "assistant") {
1565
+ for (const toolCall of message.toolCalls || []) if (newMessages.findIndex((m) => m.role === "tool" && m.toolCallId === toolCall.id) === -1) {
1566
+ const tool = this.getTool({
1567
+ toolName: toolCall.function.name,
1568
+ agentId: agent.agentId
1569
+ });
1570
+ if (tool) {
1571
+ if (await this.executeSpecificTool(tool, toolCall, message, agent, agentId)) needsFollowUp = true;
1572
+ } else {
1573
+ const wildcardTool = this.getTool({
1574
+ toolName: "*",
1575
+ agentId: agent.agentId
1576
+ });
1577
+ if (wildcardTool) {
1578
+ if (await this.executeWildcardTool(wildcardTool, toolCall, message, agent, agentId)) needsFollowUp = true;
1579
+ }
1580
+ }
1581
+ }
1582
+ }
1583
+ if (needsFollowUp && !this._runAbortController?.signal.aborted) {
1584
+ await this._internal.waitForPendingFrameworkUpdates();
1585
+ return await this.runAgent({ agent });
1586
+ }
1587
+ this._internal.suggestionEngine.reloadSuggestions(agentId, agent);
1588
+ return runAgentResult;
1589
+ }
1590
+ /**
1591
+ * Shared handler execution logic used by executeSpecificTool, executeWildcardTool, and runTool.
1592
+ * Handles arg parsing, subscriber notifications, handler invocation, result stringification,
1593
+ * and error handling.
1594
+ */
1595
+ async executeToolHandler({ tool, toolCall, agent, agentId, handlerArgs, toolType, messageId }) {
1596
+ let toolCallResult = "";
1597
+ let errorMessage;
1598
+ let isArgumentError = false;
1599
+ let parsedArgs;
1600
+ try {
1601
+ parsedArgs = parseToolArguments(handlerArgs, toolCall.function.name);
1602
+ } catch (error) {
1603
+ const parseError = error instanceof Error ? error : new Error(String(error));
1604
+ errorMessage = parseError.message;
1605
+ isArgumentError = true;
1606
+ await this._internal.emitError({
1607
+ error: parseError,
1608
+ code: CopilotKitCoreErrorCode.TOOL_ARGUMENT_PARSE_FAILED,
1609
+ context: {
1610
+ agentId,
1611
+ toolCallId: toolCall.id,
1612
+ toolName: toolCall.function.name,
1613
+ rawArguments: handlerArgs,
1614
+ toolType,
1615
+ ...messageId ? { messageId } : {}
1616
+ }
1617
+ });
1618
+ }
1619
+ await this._internal.notifySubscribers((subscriber) => subscriber.onToolExecutionStart?.({
1620
+ copilotkit: this.core,
1621
+ toolCallId: toolCall.id,
1622
+ agentId,
1623
+ toolName: toolCall.function.name,
1624
+ args: parsedArgs
1625
+ }), "Subscriber onToolExecutionStart error:");
1626
+ if (!errorMessage) try {
1627
+ const result = await tool.handler(parsedArgs, {
1628
+ toolCall,
1629
+ agent,
1630
+ signal: this._runAbortController?.signal
1631
+ });
1632
+ if (result === void 0 || result === null) toolCallResult = "";
1633
+ else if (typeof result === "string") toolCallResult = result;
1634
+ else toolCallResult = JSON.stringify(result);
1635
+ } catch (error) {
1636
+ const handlerError = error instanceof Error ? error : new Error(String(error));
1637
+ errorMessage = handlerError.message;
1638
+ await this._internal.emitError({
1639
+ error: handlerError,
1640
+ code: CopilotKitCoreErrorCode.TOOL_HANDLER_FAILED,
1641
+ context: {
1642
+ agentId,
1643
+ toolCallId: toolCall.id,
1644
+ toolName: toolCall.function.name,
1645
+ parsedArgs,
1646
+ toolType,
1647
+ ...messageId ? { messageId } : {}
1648
+ }
1649
+ });
1650
+ }
1651
+ if (errorMessage) toolCallResult = `Error: ${errorMessage}`;
1652
+ await this._internal.notifySubscribers((subscriber) => subscriber.onToolExecutionEnd?.({
1653
+ copilotkit: this.core,
1654
+ toolCallId: toolCall.id,
1655
+ agentId,
1656
+ toolName: toolCall.function.name,
1657
+ result: errorMessage ? "" : toolCallResult,
1658
+ error: errorMessage
1659
+ }), "Subscriber onToolExecutionEnd error:");
1660
+ return {
1661
+ result: toolCallResult,
1662
+ error: errorMessage,
1663
+ isArgumentError
1664
+ };
1665
+ }
1666
+ /**
1667
+ * Execute a specific tool
1668
+ */
1669
+ async executeSpecificTool(tool, toolCall, message, agent, agentId) {
1670
+ if (tool?.agentId && tool.agentId !== agent.agentId) return false;
1671
+ let handlerResult = {
1672
+ result: "",
1673
+ error: void 0,
1674
+ isArgumentError: false
1675
+ };
1676
+ if (tool?.handler) handlerResult = await this.executeToolHandler({
1677
+ tool,
1678
+ toolCall,
1679
+ agent,
1680
+ agentId,
1681
+ handlerArgs: toolCall.function.arguments,
1682
+ toolType: "specific",
1683
+ messageId: message.id
1684
+ });
1685
+ {
1686
+ const messageIndex = agent.messages.findIndex((m) => m.id === message.id);
1687
+ if (messageIndex === -1) return false;
1688
+ const toolMessage = {
1689
+ id: (0, _copilotkit_shared.randomUUID)(),
1690
+ role: "tool",
1691
+ toolCallId: toolCall.id,
1692
+ content: handlerResult.result
1693
+ };
1694
+ agent.messages.splice(messageIndex + 1, 0, toolMessage);
1695
+ if (!handlerResult.error && tool?.followUp !== false) return true;
1696
+ }
1697
+ return false;
1698
+ }
1699
+ /**
1700
+ * Execute a wildcard tool.
1701
+ * Wildcard tools receive args wrapped as `{toolName, args}`, which differs from
1702
+ * specific tools, so this method keeps its own arg-wrapping logic rather than
1703
+ * delegating to `executeToolHandler`.
1704
+ */
1705
+ async executeWildcardTool(wildcardTool, toolCall, message, agent, agentId) {
1706
+ if (wildcardTool?.agentId && wildcardTool.agentId !== agent.agentId) return false;
1707
+ let toolCallResult = "";
1708
+ let errorMessage;
1709
+ if (wildcardTool?.handler) {
1710
+ let parsedArgs;
1711
+ try {
1712
+ parsedArgs = parseToolArguments(toolCall.function.arguments, toolCall.function.name);
1713
+ } catch (error) {
1714
+ const parseError = error instanceof Error ? error : new Error(String(error));
1715
+ errorMessage = parseError.message;
1716
+ await this._internal.emitError({
1717
+ error: parseError,
1718
+ code: CopilotKitCoreErrorCode.TOOL_ARGUMENT_PARSE_FAILED,
1719
+ context: {
1720
+ agentId,
1721
+ toolCallId: toolCall.id,
1722
+ toolName: toolCall.function.name,
1723
+ rawArguments: toolCall.function.arguments,
1724
+ toolType: "wildcard",
1725
+ messageId: message.id
1726
+ }
1727
+ });
1728
+ }
1729
+ const wildcardArgs = {
1730
+ toolName: toolCall.function.name,
1731
+ args: parsedArgs
1732
+ };
1733
+ await this._internal.notifySubscribers((subscriber) => subscriber.onToolExecutionStart?.({
1734
+ copilotkit: this.core,
1735
+ toolCallId: toolCall.id,
1736
+ agentId,
1737
+ toolName: toolCall.function.name,
1738
+ args: wildcardArgs
1739
+ }), "Subscriber onToolExecutionStart error:");
1740
+ if (!errorMessage) try {
1741
+ const result = await wildcardTool.handler(wildcardArgs, {
1742
+ toolCall,
1743
+ agent
1744
+ });
1745
+ if (result === void 0 || result === null) toolCallResult = "";
1746
+ else if (typeof result === "string") toolCallResult = result;
1747
+ else toolCallResult = JSON.stringify(result);
1748
+ } catch (error) {
1749
+ const handlerError = error instanceof Error ? error : new Error(String(error));
1750
+ errorMessage = handlerError.message;
1751
+ await this._internal.emitError({
1752
+ error: handlerError,
1753
+ code: CopilotKitCoreErrorCode.TOOL_HANDLER_FAILED,
1754
+ context: {
1755
+ agentId,
1756
+ toolCallId: toolCall.id,
1757
+ toolName: toolCall.function.name,
1758
+ parsedArgs: wildcardArgs,
1759
+ toolType: "wildcard",
1760
+ messageId: message.id
1761
+ }
1762
+ });
1763
+ }
1764
+ if (errorMessage) toolCallResult = `Error: ${errorMessage}`;
1765
+ await this._internal.notifySubscribers((subscriber) => subscriber.onToolExecutionEnd?.({
1766
+ copilotkit: this.core,
1767
+ toolCallId: toolCall.id,
1768
+ agentId,
1769
+ toolName: toolCall.function.name,
1770
+ result: errorMessage ? "" : toolCallResult,
1771
+ error: errorMessage
1772
+ }), "Subscriber onToolExecutionEnd error:");
1773
+ }
1774
+ {
1775
+ const messageIndex = agent.messages.findIndex((m) => m.id === message.id);
1776
+ if (messageIndex === -1) return false;
1777
+ const toolMessage = {
1778
+ id: (0, _copilotkit_shared.randomUUID)(),
1779
+ role: "tool",
1780
+ toolCallId: toolCall.id,
1781
+ content: toolCallResult
1782
+ };
1783
+ agent.messages.splice(messageIndex + 1, 0, toolMessage);
1784
+ if (!errorMessage && wildcardTool?.followUp !== false) return true;
1785
+ }
1786
+ return false;
1787
+ }
1788
+ /**
1789
+ * Programmatically execute a registered frontend tool without going through an LLM turn.
1790
+ * The handler runs, render components show up in the UI, and both the tool call and
1791
+ * result messages are added to `agent.messages`.
1792
+ */
1793
+ async runTool(params) {
1794
+ const { name, agentId, parameters = {}, followUp = false } = params;
1795
+ const tool = this.getTool({
1796
+ toolName: name,
1797
+ agentId
1798
+ });
1799
+ if (!tool) {
1800
+ const error = /* @__PURE__ */ new Error(`Tool not found: ${name}`);
1801
+ await this._internal.emitError({
1802
+ error,
1803
+ code: CopilotKitCoreErrorCode.TOOL_NOT_FOUND,
1804
+ context: {
1805
+ toolName: name,
1806
+ agentId
1807
+ }
1808
+ });
1809
+ throw error;
1810
+ }
1811
+ const resolvedAgentId = agentId ?? "default";
1812
+ const agent = this._internal.getAgent(resolvedAgentId);
1813
+ if (!agent) {
1814
+ const error = /* @__PURE__ */ new Error(`Agent not found: ${resolvedAgentId}`);
1815
+ await this._internal.emitError({
1816
+ error,
1817
+ code: CopilotKitCoreErrorCode.AGENT_NOT_FOUND,
1818
+ context: { agentId: resolvedAgentId }
1819
+ });
1820
+ throw error;
1821
+ }
1822
+ const toolCallId = (0, _copilotkit_shared.randomUUID)();
1823
+ const assistantMessage = {
1824
+ id: (0, _copilotkit_shared.randomUUID)(),
1825
+ role: "assistant",
1826
+ content: "",
1827
+ toolCalls: [{
1828
+ id: toolCallId,
1829
+ type: "function",
1830
+ function: {
1831
+ name,
1832
+ arguments: JSON.stringify(parameters)
1833
+ }
1834
+ }]
1835
+ };
1836
+ agent.messages.push(assistantMessage);
1837
+ let handlerResult = {
1838
+ result: "",
1839
+ error: void 0,
1840
+ isArgumentError: false
1841
+ };
1842
+ if (tool.handler) handlerResult = await this.executeToolHandler({
1843
+ tool,
1844
+ toolCall: assistantMessage.toolCalls[0],
1845
+ agent,
1846
+ agentId: resolvedAgentId,
1847
+ handlerArgs: parameters,
1848
+ toolType: "runTool"
1849
+ });
1850
+ const toolResultMessage = {
1851
+ id: (0, _copilotkit_shared.randomUUID)(),
1852
+ role: "tool",
1853
+ toolCallId,
1854
+ content: handlerResult.result
1855
+ };
1856
+ const assistantIndex = agent.messages.findIndex((m) => m.id === assistantMessage.id);
1857
+ if (assistantIndex !== -1) agent.messages.splice(assistantIndex + 1, 0, toolResultMessage);
1858
+ else agent.messages.push(toolResultMessage);
1859
+ if (!handlerResult.error && followUp !== false) {
1860
+ if (typeof followUp === "string" && followUp !== "generate") {
1861
+ const userMessage = {
1862
+ id: (0, _copilotkit_shared.randomUUID)(),
1863
+ role: "user",
1864
+ content: followUp
1865
+ };
1866
+ agent.messages.push(userMessage);
1867
+ }
1868
+ await this._internal.waitForPendingFrameworkUpdates();
1869
+ await this.runAgent({ agent });
1870
+ }
1871
+ return {
1872
+ toolCallId,
1873
+ result: handlerResult.result,
1874
+ error: handlerResult.error
1875
+ };
1876
+ }
1877
+ /**
1878
+ * Build frontend tools for an agent
1879
+ */
1880
+ buildFrontendTools(agentId) {
1881
+ return this._tools.filter((tool) => tool.available !== false && (!tool.agentId || tool.agentId === agentId)).map((tool) => ({
1882
+ name: tool.name,
1883
+ description: tool.description ?? "",
1884
+ parameters: createToolSchema(tool)
1885
+ }));
1886
+ }
1887
+ /**
1888
+ * Create an agent error subscriber
1889
+ */
1890
+ createAgentErrorSubscriber(agent) {
1891
+ const emitAgentError = async (error, code, extraContext = {}) => {
1892
+ const context = { ...extraContext };
1893
+ if (agent.agentId) context.agentId = agent.agentId;
1894
+ await this._internal.emitError({
1895
+ error,
1896
+ code,
1897
+ context
1898
+ });
1899
+ };
1900
+ return {
1901
+ onRunFailed: async ({ error }) => {
1902
+ await emitAgentError(error, error instanceof AgentThreadLockedError ? CopilotKitCoreErrorCode.AGENT_THREAD_LOCKED : CopilotKitCoreErrorCode.AGENT_RUN_FAILED_EVENT, { source: "onRunFailed" });
1903
+ },
1904
+ onRunErrorEvent: async ({ event }) => {
1905
+ const eventError = event?.rawEvent instanceof Error ? event.rawEvent : event?.rawEvent?.error instanceof Error ? event.rawEvent.error : void 0;
1906
+ const errorMessage = typeof event?.rawEvent?.error === "string" ? event.rawEvent.error : event?.message ?? "Agent run error";
1907
+ const rawError = eventError ?? new Error(errorMessage);
1908
+ if (event?.code && !rawError.code) rawError.code = event.code;
1909
+ await emitAgentError(rawError, CopilotKitCoreErrorCode.AGENT_RUN_ERROR_EVENT, {
1910
+ source: "onRunErrorEvent",
1911
+ event,
1912
+ runtimeErrorCode: event?.code
1913
+ });
1914
+ }
1915
+ };
1916
+ }
1917
+ };
1918
+ /**
1919
+ * Empty tool schema constant
1920
+ */
1921
+ const EMPTY_TOOL_SCHEMA = {
1922
+ type: "object",
1923
+ properties: {}
1924
+ };
1925
+ /**
1926
+ * Create a JSON schema from a tool's parameters
1927
+ */
1928
+ function createToolSchema(tool) {
1929
+ if (!tool.parameters) return { ...EMPTY_TOOL_SCHEMA };
1930
+ const rawSchema = (0, _copilotkit_shared.schemaToJsonSchema)(tool.parameters, { zodToJsonSchema: zod_to_json_schema.zodToJsonSchema });
1931
+ if (!rawSchema || typeof rawSchema !== "object") return { ...EMPTY_TOOL_SCHEMA };
1932
+ const { $schema, ...schema } = rawSchema;
1933
+ if (typeof schema.type !== "string") schema.type = "object";
1934
+ if (typeof schema.properties !== "object" || schema.properties === null) schema.properties = {};
1935
+ stripAdditionalProperties(schema);
1936
+ return schema;
1937
+ }
1938
+ function stripAdditionalProperties(schema) {
1939
+ if (!schema || typeof schema !== "object") return;
1940
+ if (Array.isArray(schema)) {
1941
+ schema.forEach(stripAdditionalProperties);
1942
+ return;
1943
+ }
1944
+ const record = schema;
1945
+ if (record.additionalProperties !== void 0) delete record.additionalProperties;
1946
+ for (const value of Object.values(record)) stripAdditionalProperties(value);
1947
+ }
1948
+ /**
1949
+ * Ensures parsed tool arguments are a plain object.
1950
+ * Throws for non-object values so the caller's catch block can emit
1951
+ * a structured TOOL_ARGUMENT_PARSE_FAILED error.
1952
+ *
1953
+ * @internal Exported for testing only.
1954
+ */
1955
+ function ensureObjectArgs(parsed, toolName) {
1956
+ if (typeof parsed === "object" && parsed !== null && !Array.isArray(parsed)) return parsed;
1957
+ throw new Error(`Tool arguments for ${toolName} parsed to non-object (${typeof parsed})`);
1958
+ }
1959
+ /**
1960
+ * Parses raw tool call arguments into a validated object.
1961
+ *
1962
+ * Some LLM providers (e.g. @ai-sdk/openai-compatible) may send empty string "",
1963
+ * null, or undefined instead of "{}". This function normalises those cases to an
1964
+ * empty object so callers don't crash on JSON.parse("").
1965
+ *
1966
+ * A debug-level warning is emitted when the fallback triggers so silent coercion
1967
+ * is observable in logs.
1968
+ *
1969
+ * @internal Exported for testing only.
1970
+ */
1971
+ function parseToolArguments(rawArgs, toolName) {
1972
+ if (rawArgs === "" || rawArgs === null || rawArgs === void 0) {
1973
+ _copilotkit_shared.logger.debug(`[parseToolArguments] Tool "${toolName}" received empty/null/undefined arguments — defaulting to {}`);
1974
+ return {};
1975
+ }
1976
+ return ensureObjectArgs(typeof rawArgs === "string" ? JSON.parse(rawArgs) : rawArgs, toolName);
1977
+ }
1978
+
1979
+ //#endregion
1980
+ //#region src/core/state-manager.ts
1981
+ /**
1982
+ * Manages state and message tracking by run for CopilotKitCore.
1983
+ * Tracks agent state snapshots and message-to-run associations.
1984
+ */
1985
+ var StateManager = class {
1986
+ stateByRun = /* @__PURE__ */ new Map();
1987
+ messageToRun = /* @__PURE__ */ new Map();
1988
+ activeRun = /* @__PURE__ */ new Map();
1989
+ agentSubscriptions = /* @__PURE__ */ new Map();
1990
+ constructor(core) {
1991
+ this.core = core;
1992
+ }
1993
+ /**
1994
+ * Initialize state tracking for an agent
1995
+ */
1996
+ initialize() {}
1997
+ /**
1998
+ * Subscribe to an agent's events to track state and messages.
1999
+ *
2000
+ * Registry agents (subscribed via `onAgentsChanged`) use the bare `agentId`
2001
+ * key so that `unsubscribeFromAgent(agentId)` can remove them when they
2002
+ * are replaced. Per-thread clones (subscribed via `subscribeAgentToStateManager`)
2003
+ * pass `{ isClone: true }` to use a composite `agentId:threadId` key, keeping
2004
+ * their subscription independent of the registry agent's.
2005
+ */
2006
+ subscribeToAgent(agent, { isClone = false } = {}) {
2007
+ if (!agent.agentId) return;
2008
+ const agentId = agent.agentId;
2009
+ const subscriptionKey = isClone && agent.threadId ? `${agentId}:${agent.threadId}` : agentId;
2010
+ const existingUnsubscribe = this.agentSubscriptions.get(subscriptionKey);
2011
+ if (existingUnsubscribe) {
2012
+ existingUnsubscribe();
2013
+ this.agentSubscriptions.delete(subscriptionKey);
2014
+ }
2015
+ let revoked = false;
2016
+ let subRunId;
2017
+ let runFinished = false;
2018
+ const effectiveInput = (input) => ({
2019
+ ...input,
2020
+ runId: subRunId ?? input.runId
2021
+ });
2022
+ const { unsubscribe } = agent.subscribe({
2023
+ onRunStartedEvent: ({ input, state }) => {
2024
+ if (revoked) return;
2025
+ if (runFinished && input.runId === subRunId) subRunId = (0, _ag_ui_client.randomUUID)();
2026
+ else subRunId = input.runId;
2027
+ runFinished = false;
2028
+ this.handleRunStarted(agent, effectiveInput(input), state);
2029
+ },
2030
+ onRunFinishedEvent: ({ input, state }) => {
2031
+ if (revoked) return;
2032
+ runFinished = true;
2033
+ this.handleRunFinished(agent, effectiveInput(input), state);
2034
+ },
2035
+ onStateSnapshotEvent: ({ event, input, state }) => {
2036
+ if (revoked) return;
2037
+ this.handleStateSnapshot(agent, event, effectiveInput(input), state);
2038
+ },
2039
+ onStateDeltaEvent: ({ event, input, state }) => {
2040
+ if (revoked) return;
2041
+ this.handleStateDelta(agent, event, effectiveInput(input), state);
2042
+ },
2043
+ onMessagesSnapshotEvent: ({ event, input, messages }) => {
2044
+ if (revoked) return;
2045
+ this.handleMessagesSnapshot(agent, event, effectiveInput(input), messages);
2046
+ },
2047
+ onNewMessage: ({ message, input }) => {
2048
+ if (revoked) return;
2049
+ this.handleNewMessage(agent, message, input ? effectiveInput(input) : void 0);
2050
+ }
2051
+ });
2052
+ this.agentSubscriptions.set(subscriptionKey, () => {
2053
+ revoked = true;
2054
+ unsubscribe();
2055
+ });
2056
+ }
2057
+ /**
2058
+ * Unsubscribe a registry agent's subscription (bare `agentId` key).
2059
+ * Per-thread clone subscriptions use composite `agentId:threadId` keys and
2060
+ * are replaced (not removed) by subsequent subscribeToAgent(agent, { isClone: true })
2061
+ * calls for the same (agentId, threadId) pair.
2062
+ */
2063
+ unsubscribeFromAgent(agentId) {
2064
+ const unsubscribe = this.agentSubscriptions.get(agentId);
2065
+ if (unsubscribe) {
2066
+ unsubscribe();
2067
+ this.agentSubscriptions.delete(agentId);
2068
+ }
2069
+ }
2070
+ /**
2071
+ * Get state for a specific run
2072
+ * Returns a deep copy to prevent external mutations
2073
+ */
2074
+ getStateByRun(agentId, threadId, runId) {
2075
+ const state = this.stateByRun.get(agentId)?.get(threadId)?.get(runId);
2076
+ if (!state) return void 0;
2077
+ return JSON.parse(JSON.stringify(state));
2078
+ }
2079
+ /**
2080
+ * Get runId associated with a message
2081
+ */
2082
+ getRunIdForMessage(agentId, threadId, messageId) {
2083
+ return this.messageToRun.get(agentId)?.get(threadId)?.get(messageId);
2084
+ }
2085
+ /**
2086
+ * Get all states for an agent's thread
2087
+ */
2088
+ getStatesForThread(agentId, threadId) {
2089
+ return this.stateByRun.get(agentId)?.get(threadId) ?? /* @__PURE__ */ new Map();
2090
+ }
2091
+ /**
2092
+ * Get all run IDs for an agent's thread
2093
+ */
2094
+ getRunIdsForThread(agentId, threadId) {
2095
+ const threadStates = this.stateByRun.get(agentId)?.get(threadId);
2096
+ return threadStates ? Array.from(threadStates.keys()) : [];
2097
+ }
2098
+ /**
2099
+ * Handle run started event
2100
+ */
2101
+ handleRunStarted(agent, input, state) {
2102
+ if (!agent.agentId) return;
2103
+ const { threadId, runId } = input;
2104
+ this.activeRun.set(`${agent.agentId}:${threadId}`, runId);
2105
+ if (state && Object.keys(state).length > 0) this.saveState(agent.agentId, threadId, runId, state);
2106
+ }
2107
+ /**
2108
+ * Handle run finished event
2109
+ */
2110
+ handleRunFinished(agent, input, state) {
2111
+ if (!agent.agentId) return;
2112
+ const { threadId, runId } = input;
2113
+ this.activeRun.delete(`${agent.agentId}:${threadId}`);
2114
+ if (state && Object.keys(state).length > 0) this.saveState(agent.agentId, threadId, runId, state);
2115
+ }
2116
+ /**
2117
+ * Handle state snapshot event
2118
+ */
2119
+ handleStateSnapshot(agent, event, input, state) {
2120
+ if (!agent.agentId) return;
2121
+ const { threadId, runId } = input;
2122
+ const mergedState = {
2123
+ ...state,
2124
+ ...event.snapshot
2125
+ };
2126
+ this.saveState(agent.agentId, threadId, runId, mergedState);
2127
+ }
2128
+ /**
2129
+ * Handle state delta event
2130
+ */
2131
+ handleStateDelta(agent, event, input, state) {
2132
+ if (!agent.agentId) return;
2133
+ const { threadId, runId } = input;
2134
+ this.saveState(agent.agentId, threadId, runId, state);
2135
+ }
2136
+ /**
2137
+ * Handle messages snapshot event
2138
+ */
2139
+ handleMessagesSnapshot(agent, event, input, messages) {
2140
+ if (!agent.agentId) return;
2141
+ const { threadId, runId } = input;
2142
+ for (const message of event.messages) this.associateMessageWithRun(agent.agentId, threadId, message.id, runId);
2143
+ }
2144
+ /**
2145
+ * Handle new message event
2146
+ */
2147
+ handleNewMessage(agent, message, input) {
2148
+ if (!agent.agentId) return;
2149
+ if (!input) {
2150
+ const threadId = agent.threadId ?? "";
2151
+ const runId = this.activeRun.get(`${agent.agentId}:${threadId}`);
2152
+ if (runId) this.associateMessageWithRun(agent.agentId, threadId, message.id, runId);
2153
+ return;
2154
+ }
2155
+ const { threadId, runId } = input;
2156
+ this.associateMessageWithRun(agent.agentId, threadId, message.id, runId);
2157
+ }
2158
+ /**
2159
+ * Save state for a specific run
2160
+ */
2161
+ saveState(agentId, threadId, runId, state) {
2162
+ if (!this.stateByRun.has(agentId)) this.stateByRun.set(agentId, /* @__PURE__ */ new Map());
2163
+ const agentStates = this.stateByRun.get(agentId);
2164
+ if (!agentStates.has(threadId)) agentStates.set(threadId, /* @__PURE__ */ new Map());
2165
+ agentStates.get(threadId).set(runId, JSON.parse(JSON.stringify(state)));
2166
+ }
2167
+ /**
2168
+ * Associate a message with a run
2169
+ */
2170
+ associateMessageWithRun(agentId, threadId, messageId, runId) {
2171
+ if (!this.messageToRun.has(agentId)) this.messageToRun.set(agentId, /* @__PURE__ */ new Map());
2172
+ const agentMessages = this.messageToRun.get(agentId);
2173
+ if (!agentMessages.has(threadId)) agentMessages.set(threadId, /* @__PURE__ */ new Map());
2174
+ agentMessages.get(threadId).set(messageId, runId);
2175
+ }
2176
+ /**
2177
+ * Clear all state for an agent
2178
+ */
2179
+ clearAgentState(agentId) {
2180
+ this.stateByRun.delete(agentId);
2181
+ this.messageToRun.delete(agentId);
2182
+ }
2183
+ /**
2184
+ * Clear all state for a thread
2185
+ */
2186
+ clearThreadState(agentId, threadId) {
2187
+ this.stateByRun.get(agentId)?.delete(threadId);
2188
+ this.messageToRun.get(agentId)?.delete(threadId);
2189
+ }
2190
+ };
2191
+
2192
+ //#endregion
2193
+ //#region src/core/core.ts
2194
+ let CopilotKitCoreErrorCode = /* @__PURE__ */ function(CopilotKitCoreErrorCode) {
2195
+ CopilotKitCoreErrorCode["RUNTIME_INFO_FETCH_FAILED"] = "runtime_info_fetch_failed";
2196
+ CopilotKitCoreErrorCode["AGENT_CONNECT_FAILED"] = "agent_connect_failed";
2197
+ CopilotKitCoreErrorCode["AGENT_RUN_FAILED"] = "agent_run_failed";
2198
+ CopilotKitCoreErrorCode["AGENT_RUN_FAILED_EVENT"] = "agent_run_failed_event";
2199
+ CopilotKitCoreErrorCode["AGENT_RUN_ERROR_EVENT"] = "agent_run_error_event";
2200
+ CopilotKitCoreErrorCode["TOOL_ARGUMENT_PARSE_FAILED"] = "tool_argument_parse_failed";
2201
+ CopilotKitCoreErrorCode["TOOL_HANDLER_FAILED"] = "tool_handler_failed";
2202
+ CopilotKitCoreErrorCode["TOOL_NOT_FOUND"] = "tool_not_found";
2203
+ CopilotKitCoreErrorCode["AGENT_NOT_FOUND"] = "agent_not_found";
2204
+ /**
2205
+ * Emitted when an agent run fails because the thread is already locked
2206
+ * by another active run.
2207
+ *
2208
+ * @example
2209
+ * ```tsx
2210
+ * <CopilotKitProvider
2211
+ * onError={({ code, error, context }) => {
2212
+ * if (code === "agent_thread_locked") {
2213
+ * // Show "Agent is busy, retry?" UI
2214
+ * }
2215
+ * }}
2216
+ * />
2217
+ * ```
2218
+ */
2219
+ CopilotKitCoreErrorCode["AGENT_THREAD_LOCKED"] = "agent_thread_locked";
2220
+ CopilotKitCoreErrorCode["TRANSCRIPTION_FAILED"] = "transcription_failed";
2221
+ CopilotKitCoreErrorCode["TRANSCRIPTION_SERVICE_NOT_CONFIGURED"] = "transcription_service_not_configured";
2222
+ CopilotKitCoreErrorCode["TRANSCRIPTION_INVALID_AUDIO"] = "transcription_invalid_audio";
2223
+ CopilotKitCoreErrorCode["TRANSCRIPTION_RATE_LIMITED"] = "transcription_rate_limited";
2224
+ CopilotKitCoreErrorCode["TRANSCRIPTION_AUTH_FAILED"] = "transcription_auth_failed";
2225
+ CopilotKitCoreErrorCode["TRANSCRIPTION_NETWORK_ERROR"] = "transcription_network_error";
2226
+ return CopilotKitCoreErrorCode;
2227
+ }({});
2228
+ let CopilotKitCoreRuntimeConnectionStatus = /* @__PURE__ */ function(CopilotKitCoreRuntimeConnectionStatus) {
2229
+ CopilotKitCoreRuntimeConnectionStatus["Disconnected"] = "disconnected";
2230
+ CopilotKitCoreRuntimeConnectionStatus["Connected"] = "connected";
2231
+ CopilotKitCoreRuntimeConnectionStatus["Connecting"] = "connecting";
2232
+ CopilotKitCoreRuntimeConnectionStatus["Error"] = "error";
2233
+ return CopilotKitCoreRuntimeConnectionStatus;
2234
+ }({});
2235
+ var CopilotKitCore = class {
2236
+ _headers;
2237
+ _credentials;
2238
+ _properties;
2239
+ subscribers = /* @__PURE__ */ new Set();
2240
+ agentRegistry;
2241
+ contextStore;
2242
+ suggestionEngine;
2243
+ runHandler;
2244
+ stateManager;
2245
+ constructor({ runtimeUrl, runtimeTransport = "rest", headers = {}, credentials, properties = {}, agents__unsafe_dev_only = {}, tools = [], suggestionsConfig = [] }) {
2246
+ this._headers = headers;
2247
+ this._credentials = credentials;
2248
+ this._properties = properties;
2249
+ this.agentRegistry = new AgentRegistry(this);
2250
+ this.contextStore = new ContextStore(this);
2251
+ this.suggestionEngine = new SuggestionEngine(this);
2252
+ this.runHandler = new RunHandler(this);
2253
+ this.stateManager = new StateManager(this);
2254
+ this.agentRegistry.initialize(agents__unsafe_dev_only);
2255
+ this.runHandler.initialize(tools);
2256
+ this.suggestionEngine.initialize(suggestionsConfig);
2257
+ this.stateManager.initialize();
2258
+ this.agentRegistry.setRuntimeTransport(runtimeTransport);
2259
+ this.agentRegistry.setRuntimeUrl(runtimeUrl);
2260
+ this.subscribe({ onAgentsChanged: ({ agents }) => {
2261
+ Object.values(agents).forEach((agent) => {
2262
+ if (agent.agentId) this.stateManager.subscribeToAgent(agent);
2263
+ });
2264
+ } });
2265
+ }
2266
+ /**
2267
+ * Internal method used by delegate classes and subclasses to notify subscribers
2268
+ */
2269
+ async notifySubscribers(handler, errorMessage) {
2270
+ await Promise.all(Array.from(this.subscribers).map(async (subscriber) => {
2271
+ try {
2272
+ await handler(subscriber);
2273
+ } catch (error) {
2274
+ console.error(errorMessage, error);
2275
+ }
2276
+ }));
2277
+ }
2278
+ /**
2279
+ * Internal method used by delegate classes to emit errors
2280
+ */
2281
+ async emitError({ error, code, context = {} }) {
2282
+ await this.notifySubscribers((subscriber) => subscriber.onError?.({
2283
+ copilotkit: this,
2284
+ error,
2285
+ code,
2286
+ context
2287
+ }), "Subscriber onError error:");
2288
+ }
2289
+ /**
2290
+ * Snapshot accessors
2291
+ */
2292
+ get context() {
2293
+ return this.contextStore.context;
2294
+ }
2295
+ get agents() {
2296
+ return this.agentRegistry.agents;
2297
+ }
2298
+ get tools() {
2299
+ return this.runHandler.tools;
2300
+ }
2301
+ get runtimeUrl() {
2302
+ return this.agentRegistry.runtimeUrl;
2303
+ }
2304
+ setRuntimeUrl(runtimeUrl) {
2305
+ this.agentRegistry.setRuntimeUrl(runtimeUrl);
2306
+ }
2307
+ get runtimeTransport() {
2308
+ return this.agentRegistry.runtimeTransport;
2309
+ }
2310
+ setRuntimeTransport(runtimeTransport) {
2311
+ this.agentRegistry.setRuntimeTransport(runtimeTransport);
2312
+ }
2313
+ get runtimeVersion() {
2314
+ return this.agentRegistry.runtimeVersion;
2315
+ }
2316
+ get headers() {
2317
+ return this._headers;
2318
+ }
2319
+ get credentials() {
2320
+ return this._credentials;
2321
+ }
2322
+ get properties() {
2323
+ return this._properties;
2324
+ }
2325
+ get runtimeConnectionStatus() {
2326
+ return this.agentRegistry.runtimeConnectionStatus;
2327
+ }
2328
+ get audioFileTranscriptionEnabled() {
2329
+ return this.agentRegistry.audioFileTranscriptionEnabled;
2330
+ }
2331
+ get runtimeMode() {
2332
+ return this.agentRegistry.runtimeMode;
2333
+ }
2334
+ get intelligence() {
2335
+ return this.agentRegistry.intelligence;
2336
+ }
2337
+ get a2uiEnabled() {
2338
+ return this.agentRegistry.a2uiEnabled;
2339
+ }
2340
+ get licenseStatus() {
2341
+ return this.agentRegistry.licenseStatus;
2342
+ }
2343
+ /**
2344
+ * Configuration updates
2345
+ */
2346
+ setHeaders(headers) {
2347
+ this._headers = headers;
2348
+ this.agentRegistry.applyHeadersToAgents(this.agentRegistry.agents);
2349
+ this.notifySubscribers((subscriber) => subscriber.onHeadersChanged?.({
2350
+ copilotkit: this,
2351
+ headers: this.headers
2352
+ }), "Subscriber onHeadersChanged error:");
2353
+ }
2354
+ setCredentials(credentials) {
2355
+ this._credentials = credentials;
2356
+ this.agentRegistry.applyCredentialsToAgents(this.agentRegistry.agents);
2357
+ }
2358
+ setProperties(properties) {
2359
+ this._properties = properties;
2360
+ this.notifySubscribers((subscriber) => subscriber.onPropertiesChanged?.({
2361
+ copilotkit: this,
2362
+ properties: this.properties
2363
+ }), "Subscriber onPropertiesChanged error:");
2364
+ }
2365
+ /**
2366
+ * Agent management (delegated to AgentRegistry)
2367
+ */
2368
+ setAgents__unsafe_dev_only(agents) {
2369
+ this.agentRegistry.setAgents__unsafe_dev_only(agents);
2370
+ }
2371
+ addAgent__unsafe_dev_only(params) {
2372
+ this.agentRegistry.addAgent__unsafe_dev_only(params);
2373
+ }
2374
+ removeAgent__unsafe_dev_only(id) {
2375
+ this.agentRegistry.removeAgent__unsafe_dev_only(id);
2376
+ }
2377
+ getAgent(id) {
2378
+ return this.agentRegistry.getAgent(id);
2379
+ }
2380
+ /**
2381
+ * Context management (delegated to ContextStore)
2382
+ */
2383
+ addContext(context) {
2384
+ return this.contextStore.addContext(context);
2385
+ }
2386
+ removeContext(id) {
2387
+ this.contextStore.removeContext(id);
2388
+ }
2389
+ /**
2390
+ * Suggestions management (delegated to SuggestionEngine)
2391
+ */
2392
+ addSuggestionsConfig(config) {
2393
+ return this.suggestionEngine.addSuggestionsConfig(config);
2394
+ }
2395
+ removeSuggestionsConfig(id) {
2396
+ this.suggestionEngine.removeSuggestionsConfig(id);
2397
+ }
2398
+ reloadSuggestions(agentId) {
2399
+ this.suggestionEngine.reloadSuggestions(agentId);
2400
+ }
2401
+ clearSuggestions(agentId) {
2402
+ this.suggestionEngine.clearSuggestions(agentId);
2403
+ }
2404
+ getSuggestions(agentId) {
2405
+ return this.suggestionEngine.getSuggestions(agentId);
2406
+ }
2407
+ /**
2408
+ * Tool management (delegated to RunHandler)
2409
+ */
2410
+ addTool(tool) {
2411
+ this.runHandler.addTool(tool);
2412
+ }
2413
+ removeTool(id, agentId) {
2414
+ this.runHandler.removeTool(id, agentId);
2415
+ }
2416
+ getTool(params) {
2417
+ return this.runHandler.getTool(params);
2418
+ }
2419
+ setTools(tools) {
2420
+ this.runHandler.setTools(tools);
2421
+ }
2422
+ /**
2423
+ * Subscription lifecycle
2424
+ */
2425
+ subscribe(subscriber) {
2426
+ this.subscribers.add(subscriber);
2427
+ return { unsubscribe: () => {
2428
+ this.subscribers.delete(subscriber);
2429
+ } };
2430
+ }
2431
+ /**
2432
+ * Agent connectivity (delegated to RunHandler)
2433
+ */
2434
+ async connectAgent(params) {
2435
+ return this.runHandler.connectAgent(params);
2436
+ }
2437
+ stopAgent(params) {
2438
+ this.runHandler.abortCurrentRun();
2439
+ params.agent.abortRun();
2440
+ }
2441
+ async runAgent(params) {
2442
+ return this.runHandler.runAgent(params);
2443
+ }
2444
+ /**
2445
+ * Programmatically execute a registered frontend tool without going through an LLM turn.
2446
+ * The handler runs, render components show up in the UI, and both the tool call and
2447
+ * result messages are added to `agent.messages`.
2448
+ */
2449
+ async runTool(params) {
2450
+ return this.runHandler.runTool(params);
2451
+ }
2452
+ /**
2453
+ * State management (delegated to StateManager)
2454
+ */
2455
+ getStateByRun(agentId, threadId, runId) {
2456
+ return this.stateManager.getStateByRun(agentId, threadId, runId);
2457
+ }
2458
+ getRunIdForMessage(agentId, threadId, messageId) {
2459
+ return this.stateManager.getRunIdForMessage(agentId, threadId, messageId);
2460
+ }
2461
+ getRunIdsForThread(agentId, threadId) {
2462
+ return this.stateManager.getRunIdsForThread(agentId, threadId);
2463
+ }
2464
+ subscribeAgentToStateManager(agent) {
2465
+ this.stateManager.subscribeToAgent(agent, { isClone: true });
2466
+ }
2467
+ /**
2468
+ * Internal method used by RunHandler to build frontend tools
2469
+ */
2470
+ buildFrontendTools(agentId) {
2471
+ return this.runHandler.buildFrontendTools(agentId);
2472
+ }
2473
+ /**
2474
+ * Called before each follow-up agent run (after tool execution).
2475
+ *
2476
+ * When a frontend tool handler calls framework state setters (e.g. React's
2477
+ * setState), those updates are batched and deferred — they do not take effect
2478
+ * until the framework's scheduler runs (React uses MessageChannel).
2479
+ * useAgentContext registers context via useLayoutEffect, which runs
2480
+ * synchronously after React commits that deferred batch.
2481
+ *
2482
+ * Without yielding here, the follow-up runAgent reads the context store
2483
+ * synchronously while the deferred updates are still pending, producing stale
2484
+ * context for the next agent turn.
2485
+ *
2486
+ * Override in framework-specific subclasses to yield to the framework
2487
+ * scheduler before the follow-up run. The base implementation is a no-op
2488
+ * because non-React environments have no deferred state to flush.
2489
+ */
2490
+ async waitForPendingFrameworkUpdates() {}
2491
+ };
2492
+
2493
+ //#endregion
2494
+ //#region src/types.ts
2495
+ /**
2496
+ * Status of a tool call execution
2497
+ */
2498
+ let ToolCallStatus = /* @__PURE__ */ function(ToolCallStatus) {
2499
+ ToolCallStatus["InProgress"] = "inProgress";
2500
+ ToolCallStatus["Executing"] = "executing";
2501
+ ToolCallStatus["Complete"] = "complete";
2502
+ return ToolCallStatus;
2503
+ }({});
2504
+
2505
+ //#endregion
2506
+ //#region src/utils/markdown.ts
2507
+ function completePartialMarkdown(input) {
2508
+ let s = input;
2509
+ const fenceMatches = Array.from(s.matchAll(/^(\s*)(`{3,}|~{3,})/gm));
2510
+ if (fenceMatches.length % 2 === 1) {
2511
+ const [, indent, fence] = fenceMatches[0];
2512
+ s += `\n${indent}${fence}`;
2513
+ }
2514
+ if (s.match(/\[([^\]]*)\]\(([^)]*)$/)) s += ")";
2515
+ const openElements = [];
2516
+ const chars = Array.from(s);
2517
+ const codeBlockRanges = [];
2518
+ const inlineCodeRanges = [];
2519
+ let tempCodeFenceCount = 0;
2520
+ let currentCodeBlockStart = -1;
2521
+ for (let i = 0; i < chars.length; i++) if (i === 0 || chars[i - 1] === "\n") {
2522
+ const lineMatch = s.substring(i).match(/^(\s*)(`{3,}|~{3,})/);
2523
+ if (lineMatch) {
2524
+ tempCodeFenceCount++;
2525
+ if (tempCodeFenceCount % 2 === 1) currentCodeBlockStart = i;
2526
+ else if (currentCodeBlockStart !== -1) {
2527
+ codeBlockRanges.push({
2528
+ start: currentCodeBlockStart,
2529
+ end: i + lineMatch[0].length
2530
+ });
2531
+ currentCodeBlockStart = -1;
2532
+ }
2533
+ i += lineMatch[0].length - 1;
2534
+ }
2535
+ }
2536
+ for (let i = 0; i < chars.length; i++) if (chars[i] === "`") {
2537
+ let backslashCount = 0;
2538
+ for (let j = i - 1; j >= 0 && chars[j] === "\\"; j--) backslashCount++;
2539
+ if (backslashCount % 2 === 0) {
2540
+ for (let j = i + 1; j < chars.length; j++) if (chars[j] === "`") {
2541
+ let closingBackslashCount = 0;
2542
+ for (let k = j - 1; k >= 0 && chars[k] === "\\"; k--) closingBackslashCount++;
2543
+ if (closingBackslashCount % 2 === 0) {
2544
+ inlineCodeRanges.push({
2545
+ start: i,
2546
+ end: j + 1
2547
+ });
2548
+ i = j;
2549
+ break;
2550
+ }
2551
+ }
2552
+ }
2553
+ }
2554
+ const isInCode = (pos) => {
2555
+ return codeBlockRanges.some((range) => pos >= range.start && pos < range.end) || inlineCodeRanges.some((range) => pos >= range.start && pos < range.end);
2556
+ };
2557
+ for (let i = 0; i < chars.length; i++) {
2558
+ const char = chars[i];
2559
+ const nextChar = chars[i + 1];
2560
+ const prevChar = chars[i - 1];
2561
+ if (isInCode(i)) continue;
2562
+ if (char === "[") {
2563
+ let isCompleteLink = false;
2564
+ let bracketDepth = 1;
2565
+ let j = i + 1;
2566
+ while (j < chars.length && bracketDepth > 0) {
2567
+ if (chars[j] === "[" && !isInCode(j)) bracketDepth++;
2568
+ if (chars[j] === "]" && !isInCode(j)) bracketDepth--;
2569
+ j++;
2570
+ }
2571
+ if (bracketDepth === 0 && chars[j] === "(") {
2572
+ let parenDepth = 1;
2573
+ j++;
2574
+ while (j < chars.length && parenDepth > 0) {
2575
+ if (chars[j] === "(" && !isInCode(j)) parenDepth++;
2576
+ if (chars[j] === ")" && !isInCode(j)) parenDepth--;
2577
+ j++;
2578
+ }
2579
+ if (parenDepth === 0) {
2580
+ isCompleteLink = true;
2581
+ i = j - 1;
2582
+ continue;
2583
+ }
2584
+ }
2585
+ if (!isCompleteLink) {
2586
+ const existingIndex = openElements.findIndex((el) => el.type === "bracket");
2587
+ if (existingIndex !== -1) openElements.splice(existingIndex, 1);
2588
+ else openElements.push({
2589
+ type: "bracket",
2590
+ marker: "[",
2591
+ position: i
2592
+ });
2593
+ }
2594
+ } else if (char === "*" && nextChar === "*") {
2595
+ const existingIndex = openElements.findIndex((el) => el.type === "bold_star");
2596
+ if (existingIndex !== -1) openElements.splice(existingIndex, 1);
2597
+ else openElements.push({
2598
+ type: "bold_star",
2599
+ marker: "**",
2600
+ position: i
2601
+ });
2602
+ i++;
2603
+ } else if (char === "_" && nextChar === "_") {
2604
+ const existingIndex = openElements.findIndex((el) => el.type === "bold_underscore");
2605
+ if (existingIndex !== -1) openElements.splice(existingIndex, 1);
2606
+ else openElements.push({
2607
+ type: "bold_underscore",
2608
+ marker: "__",
2609
+ position: i
2610
+ });
2611
+ i++;
2612
+ } else if (char === "~" && nextChar === "~") {
2613
+ const existingIndex = openElements.findIndex((el) => el.type === "strike");
2614
+ if (existingIndex !== -1) openElements.splice(existingIndex, 1);
2615
+ else openElements.push({
2616
+ type: "strike",
2617
+ marker: "~~",
2618
+ position: i
2619
+ });
2620
+ i++;
2621
+ } else if (char === "*" && prevChar !== "*" && nextChar !== "*") {
2622
+ const existingIndex = openElements.findIndex((el) => el.type === "italic_star");
2623
+ if (existingIndex !== -1) openElements.splice(existingIndex, 1);
2624
+ else openElements.push({
2625
+ type: "italic_star",
2626
+ marker: "*",
2627
+ position: i
2628
+ });
2629
+ } else if (char === "_" && prevChar !== "_" && nextChar !== "_") {
2630
+ const existingIndex = openElements.findIndex((el) => el.type === "italic_underscore");
2631
+ if (existingIndex !== -1) openElements.splice(existingIndex, 1);
2632
+ else openElements.push({
2633
+ type: "italic_underscore",
2634
+ marker: "_",
2635
+ position: i
2636
+ });
2637
+ }
2638
+ }
2639
+ let backtickCount = 0;
2640
+ for (let i = 0; i < chars.length; i++) if (chars[i] === "`" && !isInCode(i)) backtickCount++;
2641
+ if (backtickCount % 2 === 1) s += "`";
2642
+ openElements.sort((a, b) => b.position - a.position);
2643
+ const closers = openElements.map((el) => {
2644
+ switch (el.type) {
2645
+ case "bracket": return "]";
2646
+ case "bold_star": return "**";
2647
+ case "bold_underscore": return "__";
2648
+ case "strike": return "~~";
2649
+ case "italic_star": return "*";
2650
+ case "italic_underscore": return "_";
2651
+ default: return "";
2652
+ }
2653
+ });
2654
+ let result = s + closers.join("");
2655
+ const finalFenceMatches = Array.from(result.matchAll(/^(\s*)(`{3,}|~{3,})/gm));
2656
+ const hasUnclosedBacktick = (result.match(/`/g) || []).length % 2 === 1;
2657
+ const hasUnclosedCodeFence = finalFenceMatches.length % 2 === 1;
2658
+ let shouldCloseParens = !hasUnclosedBacktick && !hasUnclosedCodeFence;
2659
+ if (shouldCloseParens) {
2660
+ const lastOpenParen = result.lastIndexOf("(");
2661
+ if (lastOpenParen !== -1) {
2662
+ if ((result.substring(0, lastOpenParen).match(/`/g) || []).length % 2 === 1) shouldCloseParens = false;
2663
+ }
2664
+ }
2665
+ if (shouldCloseParens) {
2666
+ const openParens = (result.match(/\(/g) || []).length;
2667
+ const closeParens = (result.match(/\)/g) || []).length;
2668
+ if (openParens > closeParens) result += ")".repeat(openParens - closeParens);
2669
+ }
2670
+ return result;
2671
+ }
2672
+
2673
+ //#endregion
2674
+ //#region src/utils/micro-redux.ts
2675
+ const INTERNAL_ACTION_TYPES = {
2676
+ boot: "@@micro-redux/boot",
2677
+ init: "@@micro-redux/init",
2678
+ stop: "@@micro-redux/stop"
2679
+ };
2680
+ const INTERNAL_BOOT_ACTION = { type: INTERNAL_ACTION_TYPES.boot };
2681
+ /**
2682
+ * Builds a typed action creator from a type string and payload factory.
2683
+ */
2684
+ function createTypedActionCreator(type, factory) {
2685
+ const creator = ((...args) => ({
2686
+ ...factory(...args),
2687
+ type
2688
+ }));
2689
+ creator.type = type;
2690
+ creator.match = (action) => action.type === type;
2691
+ return creator;
2692
+ }
2693
+ /**
2694
+ * Declares a payload-based action config for `createActionGroup`.
2695
+ *
2696
+ * @example
2697
+ * ```ts
2698
+ * const actions = createActionGroup("User", {
2699
+ * loaded: props<{ id: string }>(),
2700
+ * });
2701
+ * ```
2702
+ */
2703
+ function props() {
2704
+ return { kind: "props" };
2705
+ }
2706
+ /**
2707
+ * Declares a no-payload action config for `createActionGroup`.
2708
+ *
2709
+ * @example
2710
+ * ```ts
2711
+ * const actions = createActionGroup("User", {
2712
+ * reset: empty(),
2713
+ * });
2714
+ * ```
2715
+ */
2716
+ function empty() {
2717
+ return { kind: "empty" };
2718
+ }
2719
+ /**
2720
+ * Creates a namespaced group of typed action creators.
2721
+ *
2722
+ * Action types are formatted as: `[Source] actionName`.
2723
+ */
2724
+ function createActionGroup(source, config) {
2725
+ const group = {};
2726
+ for (const eventName of Object.keys(config)) {
2727
+ const eventConfig = config[eventName];
2728
+ if (!eventConfig) continue;
2729
+ const actionType = `[${source}] ${eventName}`;
2730
+ if (eventConfig.kind === "props") {
2731
+ group[eventName] = createTypedActionCreator(actionType, (payload) => ({ ...payload }));
2732
+ continue;
2733
+ }
2734
+ group[eventName] = createTypedActionCreator(actionType, () => ({}));
2735
+ }
2736
+ return group;
2737
+ }
2738
+ /**
2739
+ * Registers one reducer handler for one or more action creators.
2740
+ *
2741
+ * @throws Error when called without at least one action creator and reducer.
2742
+ */
2743
+ function on(...args) {
2744
+ if (args.length < 2) throw new Error("on requires at least one action creator and one reducer");
2745
+ const reducer = args[args.length - 1];
2746
+ return {
2747
+ creators: args.slice(0, -1),
2748
+ reducer
2749
+ };
2750
+ }
2751
+ /**
2752
+ * Creates a reducer from an initial state and `on(...)` handler entries.
2753
+ *
2754
+ * Unknown action types return the current state unchanged.
2755
+ */
2756
+ function createReducer(initialState, ...entries) {
2757
+ const reducerMap = /* @__PURE__ */ new Map();
2758
+ for (const entry of entries) for (const creator of entry.creators) {
2759
+ const handlers = reducerMap.get(creator.type) ?? [];
2760
+ handlers.push(entry.reducer);
2761
+ reducerMap.set(creator.type, handlers);
2762
+ }
2763
+ return (state, action) => {
2764
+ const currentState = state ?? initialState;
2765
+ const handlers = reducerMap.get(action.type);
2766
+ if (!handlers || handlers.length === 0) return currentState;
2767
+ let nextState = currentState;
2768
+ for (const handler of handlers) nextState = handler(nextState, action);
2769
+ return nextState;
2770
+ };
2771
+ }
2772
+ /**
2773
+ * Creates a selector that caches and reuses the last computed result
2774
+ * when all input references are unchanged.
2775
+ */
2776
+ function createSelector(...args) {
2777
+ if (args.length === 1) {
2778
+ const projector = args[0];
2779
+ let hasCached = false;
2780
+ let lastState;
2781
+ let lastResult;
2782
+ return (state) => {
2783
+ if (hasCached && state === lastState) return lastResult;
2784
+ lastState = state;
2785
+ lastResult = projector(state);
2786
+ hasCached = true;
2787
+ return lastResult;
2788
+ };
2789
+ }
2790
+ const projector = args[args.length - 1];
2791
+ const selectors = args.slice(0, -1);
2792
+ let hasCached = false;
2793
+ let lastInputs = [];
2794
+ let lastResult;
2795
+ return (state) => {
2796
+ const inputs = selectors.map((selector) => selector(state));
2797
+ if (hasCached && inputs.length === lastInputs.length && inputs.every((value, index) => value === lastInputs[index])) return lastResult;
2798
+ lastInputs = inputs;
2799
+ lastResult = projector(...inputs);
2800
+ hasCached = true;
2801
+ return lastResult;
2802
+ };
2803
+ }
2804
+ /**
2805
+ * RxJS operator that maps state emissions through a selector and suppresses
2806
+ * unchanged projected values via reference equality.
2807
+ */
2808
+ function select(selector) {
2809
+ return (source$) => source$.pipe((0, rxjs_operators.map)(selector), (0, rxjs_operators.distinctUntilChanged)());
2810
+ }
2811
+ /**
2812
+ * RxJS operator that filters an action stream by action creators and narrows
2813
+ * the output action type to the matched creator union.
2814
+ *
2815
+ * @throws Error when called without at least one action creator.
2816
+ */
2817
+ function ofType(...creators) {
2818
+ if (creators.length === 0) throw new Error("ofType requires at least one action creator");
2819
+ const actionTypes = new Set(creators.map((creator) => creator.type));
2820
+ return (source$) => {
2821
+ return source$.pipe((0, rxjs_operators.filter)((action) => {
2822
+ return actionTypes.has(action.type);
2823
+ }));
2824
+ };
2825
+ }
2826
+ /**
2827
+ * Creates an effect descriptor consumed by `createStore`.
2828
+ */
2829
+ function createEffect(factory, options = {}) {
2830
+ if (options.dispatch === false) return {
2831
+ run: factory,
2832
+ dispatch: false
2833
+ };
2834
+ return {
2835
+ run: factory,
2836
+ dispatch: true
2837
+ };
2838
+ }
2839
+ /**
2840
+ * Creates a small observable store with reducer + effects.
2841
+ *
2842
+ * Behavior:
2843
+ * - `init()` starts effects and dispatches `@@micro-redux/init`.
2844
+ * - `stop()` dispatches `@@micro-redux/stop` and unsubscribes all effects.
2845
+ * - Effect action observation is scheduled on `asapScheduler` to avoid
2846
+ * synchronous re-entrancy in the effect loop.
2847
+ * - Any effect error triggers fail-fast teardown and errors both `actions$`
2848
+ * and `state$`.
2849
+ */
2850
+ function createStore(options) {
2851
+ const reducer = options.reducer;
2852
+ const effects = options.effects ?? [];
2853
+ let hasFatalError = false;
2854
+ let isRunning = false;
2855
+ let effectSubscriptions = new rxjs.Subscription();
2856
+ let currentState = reducer(void 0, INTERNAL_BOOT_ACTION);
2857
+ const stateSubject = new rxjs.BehaviorSubject(currentState);
2858
+ const actionsSubject = new rxjs.Subject();
2859
+ const dispatchInternal = (action) => {
2860
+ if (hasFatalError) throw new Error("Store is in a failed state due to an effect error");
2861
+ currentState = reducer(currentState, action);
2862
+ stateSubject.next(currentState);
2863
+ actionsSubject.next(action);
2864
+ };
2865
+ const failFast = (error) => {
2866
+ if (hasFatalError) return;
2867
+ hasFatalError = true;
2868
+ isRunning = false;
2869
+ effectSubscriptions.unsubscribe();
2870
+ effectSubscriptions = new rxjs.Subscription();
2871
+ actionsSubject.error(error);
2872
+ stateSubject.error(error);
2873
+ };
2874
+ const startEffects = () => {
2875
+ for (const effect of effects) {
2876
+ const scheduledActions$ = actionsSubject.asObservable().pipe((0, rxjs_operators.observeOn)(rxjs.asapScheduler));
2877
+ const state$ = stateSubject.asObservable();
2878
+ if (effect.dispatch) {
2879
+ const subscription = effect.run(scheduledActions$, state$).subscribe({
2880
+ next: (effectAction) => {
2881
+ if (hasFatalError) return;
2882
+ dispatchInternal(effectAction);
2883
+ },
2884
+ error: (error) => {
2885
+ failFast(error);
2886
+ }
2887
+ });
2888
+ effectSubscriptions.add(subscription);
2889
+ continue;
2890
+ }
2891
+ const subscription = effect.run(scheduledActions$, state$).subscribe({ error: (error) => {
2892
+ failFast(error);
2893
+ } });
2894
+ effectSubscriptions.add(subscription);
2895
+ }
2896
+ };
2897
+ return {
2898
+ dispatch(action) {
2899
+ dispatchInternal(action);
2900
+ },
2901
+ getState() {
2902
+ return currentState;
2903
+ },
2904
+ get state$() {
2905
+ return stateSubject.asObservable();
2906
+ },
2907
+ get actions$() {
2908
+ return actionsSubject.asObservable();
2909
+ },
2910
+ select(selector) {
2911
+ return stateSubject.asObservable().pipe(select(selector));
2912
+ },
2913
+ init() {
2914
+ if (hasFatalError || isRunning) return;
2915
+ isRunning = true;
2916
+ startEffects();
2917
+ if (hasFatalError) return;
2918
+ dispatchInternal({ type: INTERNAL_ACTION_TYPES.init });
2919
+ },
2920
+ stop() {
2921
+ if (hasFatalError || !isRunning) return;
2922
+ dispatchInternal({ type: INTERNAL_ACTION_TYPES.stop });
2923
+ effectSubscriptions.unsubscribe();
2924
+ effectSubscriptions = new rxjs.Subscription();
2925
+ isRunning = false;
2926
+ }
2927
+ };
2928
+ }
2929
+
2930
+ //#endregion
2931
+ //#region src/threads.ts
2932
+ const THREADS_CHANNEL_EVENT = "thread_metadata";
2933
+ const THREAD_SUBSCRIBE_PATH = "/threads/subscribe";
2934
+ const MAX_SOCKET_RETRIES = 5;
2935
+ const REQUEST_TIMEOUT_MS = 15e3;
2936
+ const initialThreadState = {
2937
+ threads: [],
2938
+ isLoading: false,
2939
+ isFetchingNextPage: false,
2940
+ error: null,
2941
+ context: null,
2942
+ sessionId: 0,
2943
+ metadataCredentialsRequested: false,
2944
+ metadataJoinCode: null,
2945
+ nextCursor: null
2946
+ };
2947
+ const threadAdapterEvents = createActionGroup("Thread Adapter", {
2948
+ started: empty(),
2949
+ stopped: empty(),
2950
+ contextChanged: props(),
2951
+ fetchNextPageRequested: empty(),
2952
+ renameRequested: props(),
2953
+ archiveRequested: props(),
2954
+ deleteRequested: props()
2955
+ });
2956
+ const threadRestEvents = createActionGroup("Thread REST", {
2957
+ listRequested: props(),
2958
+ listSucceeded: props(),
2959
+ listFailed: props(),
2960
+ nextPageSucceeded: props(),
2961
+ nextPageFailed: props(),
2962
+ metadataCredentialsRequested: props(),
2963
+ metadataCredentialsSucceeded: props(),
2964
+ metadataCredentialsFailed: props(),
2965
+ mutationFinished: props()
2966
+ });
2967
+ const threadSocketEvents = createActionGroup("Thread Socket", {
2968
+ opened: props(),
2969
+ errored: props(),
2970
+ joinFailed: props(),
2971
+ joinTimedOut: props(),
2972
+ metadataReceived: props()
2973
+ });
2974
+ const threadDomainEvents = createActionGroup("Thread Domain", {
2975
+ threadUpserted: props(),
2976
+ threadDeleted: props()
2977
+ });
2978
+ function sortThreadsByUpdatedAt(threads) {
2979
+ return [...threads].sort((left, right) => right.updatedAt.localeCompare(left.updatedAt));
2980
+ }
2981
+ function upsertThread(threads, thread) {
2982
+ const existingIndex = threads.findIndex((item) => item.id === thread.id);
2983
+ if (existingIndex === -1) return sortThreadsByUpdatedAt([...threads, thread]);
2984
+ const next = [...threads];
2985
+ next[existingIndex] = thread;
2986
+ return sortThreadsByUpdatedAt(next);
2987
+ }
2988
+ const threadReducer = createReducer(initialThreadState, on(threadAdapterEvents.contextChanged, (state, { context }) => ({
2989
+ ...state,
2990
+ context,
2991
+ sessionId: state.sessionId + 1,
2992
+ threads: [],
2993
+ isLoading: Boolean(context),
2994
+ isFetchingNextPage: false,
2995
+ error: null,
2996
+ metadataCredentialsRequested: false,
2997
+ metadataJoinCode: null,
2998
+ nextCursor: null
2999
+ })), on(threadAdapterEvents.stopped, (state) => ({
3000
+ ...state,
3001
+ threads: [],
3002
+ isLoading: false,
3003
+ isFetchingNextPage: false,
3004
+ error: null,
3005
+ metadataCredentialsRequested: false,
3006
+ metadataJoinCode: null,
3007
+ nextCursor: null
3008
+ })), on(threadRestEvents.listRequested, (state, { sessionId }) => {
3009
+ if (sessionId !== state.sessionId || !state.context) return state;
3010
+ return {
3011
+ ...state,
3012
+ isLoading: true,
3013
+ error: null
3014
+ };
3015
+ }), on(threadRestEvents.listSucceeded, (state, { sessionId, threads, joinCode, nextCursor }) => {
3016
+ if (sessionId !== state.sessionId) return state;
3017
+ return {
3018
+ ...state,
3019
+ threads: sortThreadsByUpdatedAt(threads),
3020
+ isLoading: false,
3021
+ error: null,
3022
+ metadataJoinCode: joinCode,
3023
+ nextCursor
3024
+ };
3025
+ }), on(threadRestEvents.listFailed, (state, { sessionId, error }) => {
3026
+ if (sessionId !== state.sessionId) return state;
3027
+ return {
3028
+ ...state,
3029
+ isLoading: false,
3030
+ error
3031
+ };
3032
+ }), on(threadRestEvents.nextPageSucceeded, (state, { sessionId, threads, nextCursor }) => {
3033
+ if (sessionId !== state.sessionId) return state;
3034
+ let merged = state.threads;
3035
+ for (const thread of threads) merged = upsertThread(merged, thread);
3036
+ return {
3037
+ ...state,
3038
+ threads: merged,
3039
+ isFetchingNextPage: false,
3040
+ nextCursor
3041
+ };
3042
+ }), on(threadRestEvents.nextPageFailed, (state, { sessionId, error }) => {
3043
+ if (sessionId !== state.sessionId) return state;
3044
+ return {
3045
+ ...state,
3046
+ isFetchingNextPage: false,
3047
+ error
3048
+ };
3049
+ }), on(threadRestEvents.metadataCredentialsFailed, (state, { sessionId, error }) => {
3050
+ if (sessionId !== state.sessionId) return state;
3051
+ return {
3052
+ ...state,
3053
+ error
3054
+ };
3055
+ }), on(threadRestEvents.metadataCredentialsRequested, (state, { sessionId }) => {
3056
+ if (sessionId !== state.sessionId) return state;
3057
+ return {
3058
+ ...state,
3059
+ metadataCredentialsRequested: true
3060
+ };
3061
+ }), on(threadAdapterEvents.fetchNextPageRequested, (state) => {
3062
+ if (!state.nextCursor || state.isFetchingNextPage) return state;
3063
+ return {
3064
+ ...state,
3065
+ isFetchingNextPage: true
3066
+ };
3067
+ }), on(threadRestEvents.mutationFinished, (state, { outcome }) => ({
3068
+ ...state,
3069
+ error: outcome.ok ? state.error : outcome.error
3070
+ })), on(threadDomainEvents.threadUpserted, (state, { sessionId, thread }) => {
3071
+ if (sessionId !== state.sessionId) return state;
3072
+ return {
3073
+ ...state,
3074
+ threads: upsertThread(state.threads, thread)
3075
+ };
3076
+ }), on(threadDomainEvents.threadDeleted, (state, { sessionId, threadId }) => {
3077
+ if (sessionId !== state.sessionId) return state;
3078
+ return {
3079
+ ...state,
3080
+ threads: state.threads.filter((thread) => thread.id !== threadId)
3081
+ };
3082
+ }));
3083
+ const selectThreads = createSelector((state) => state.threads);
3084
+ const selectThreadsIsLoading = createSelector((state) => state.isLoading);
3085
+ const selectThreadsError = createSelector((state) => state.error);
3086
+ const selectHasNextPage = createSelector((state) => state.nextCursor != null);
3087
+ const selectIsFetchingNextPage = createSelector((state) => state.isFetchingNextPage);
3088
+ let threadRequestId = 0;
3089
+ function createThreadRequestId() {
3090
+ threadRequestId += 1;
3091
+ return `thread-request-${threadRequestId}`;
3092
+ }
3093
+ function createThreadFetchObservable(environment, context, sessionId) {
3094
+ return (0, rxjs.defer)(() => {
3095
+ const params = { agentId: context.agentId };
3096
+ if (context.includeArchived) params.includeArchived = "true";
3097
+ if (context.limit != null) params.limit = String(context.limit);
3098
+ const qs = new URLSearchParams(params);
3099
+ return (0, rxjs_fetch.fromFetch)(`${context.runtimeUrl}/threads?${qs.toString()}`, {
3100
+ selector: (response) => {
3101
+ if (!response.ok) throw new Error(`Failed to fetch threads: ${response.status}`);
3102
+ return response.json();
3103
+ },
3104
+ fetch: environment.fetch,
3105
+ method: "GET",
3106
+ headers: { ...context.headers }
3107
+ }).pipe((0, rxjs_operators.timeout)({
3108
+ first: REQUEST_TIMEOUT_MS,
3109
+ with: () => {
3110
+ throw new Error("Request timed out");
3111
+ }
3112
+ }), (0, rxjs_operators.map)((data) => threadRestEvents.listSucceeded({
3113
+ sessionId,
3114
+ threads: data.threads,
3115
+ joinCode: typeof data.joinCode === "string" && data.joinCode.length > 0 ? data.joinCode : null,
3116
+ nextCursor: data.nextCursor ?? null
3117
+ })), (0, rxjs_operators.catchError)((error) => {
3118
+ return (0, rxjs.of)(threadRestEvents.listFailed({
3119
+ sessionId,
3120
+ error: error instanceof Error ? error : new Error(String(error))
3121
+ }));
3122
+ }));
3123
+ });
3124
+ }
3125
+ function createThreadMetadataCredentialsObservable(environment, context, sessionId) {
3126
+ return (0, rxjs.defer)(() => {
3127
+ return (0, rxjs_fetch.fromFetch)(`${context.runtimeUrl}${THREAD_SUBSCRIBE_PATH}`, {
3128
+ selector: async (response) => {
3129
+ if (!response.ok) throw new Error(`Failed to fetch thread metadata credentials: ${response.status}`);
3130
+ return response.json();
3131
+ },
3132
+ fetch: environment.fetch,
3133
+ method: "POST",
3134
+ headers: {
3135
+ ...context.headers,
3136
+ "Content-Type": "application/json"
3137
+ },
3138
+ body: JSON.stringify({})
3139
+ }).pipe((0, rxjs_operators.timeout)({
3140
+ first: REQUEST_TIMEOUT_MS,
3141
+ with: () => {
3142
+ throw new Error("Request timed out");
3143
+ }
3144
+ }), (0, rxjs_operators.map)((data) => {
3145
+ if (typeof data.joinToken !== "string" || data.joinToken.length === 0) throw new Error("missing joinToken");
3146
+ return threadRestEvents.metadataCredentialsSucceeded({
3147
+ sessionId,
3148
+ joinToken: data.joinToken
3149
+ });
3150
+ }), (0, rxjs_operators.catchError)((error) => {
3151
+ return (0, rxjs.of)(threadRestEvents.metadataCredentialsFailed({
3152
+ sessionId,
3153
+ error: error instanceof Error ? error : new Error(String(error))
3154
+ }));
3155
+ }));
3156
+ });
3157
+ }
3158
+ function createThreadMutationObservable(environment, context, request) {
3159
+ return (0, rxjs.defer)(() => {
3160
+ return (0, rxjs_fetch.fromFetch)(`${context.runtimeUrl}${request.path}`, {
3161
+ selector: async (response) => {
3162
+ if (!response.ok) throw new Error(`Request failed: ${response.status}`);
3163
+ return null;
3164
+ },
3165
+ fetch: environment.fetch,
3166
+ method: request.method,
3167
+ headers: {
3168
+ ...context.headers,
3169
+ "Content-Type": "application/json"
3170
+ },
3171
+ body: JSON.stringify(request.body)
3172
+ }).pipe((0, rxjs_operators.map)(() => threadRestEvents.mutationFinished({ outcome: {
3173
+ requestId: request.requestId,
3174
+ ok: true
3175
+ } })), (0, rxjs_operators.catchError)((error) => {
3176
+ return (0, rxjs.of)(threadRestEvents.mutationFinished({ outcome: {
3177
+ requestId: request.requestId,
3178
+ ok: false,
3179
+ error: error instanceof Error ? error : new Error(String(error))
3180
+ } }));
3181
+ }));
3182
+ });
3183
+ }
3184
+ function createThreadStore(environment) {
3185
+ const store = createStore({
3186
+ reducer: threadReducer,
3187
+ effects: [
3188
+ createEffect((actions$, state$) => actions$.pipe(ofType(threadAdapterEvents.contextChanged), (0, rxjs_operators.withLatestFrom)(state$), (0, rxjs_operators.filter)(([, state]) => Boolean(state.context)), (0, rxjs_operators.map)(([, state]) => threadRestEvents.listRequested({ sessionId: state.sessionId })))),
3189
+ createEffect((actions$, state$) => actions$.pipe(ofType(threadRestEvents.listRequested), (0, rxjs_operators.switchMap)((action) => state$.pipe((0, rxjs_operators.map)((state) => state.context), (0, rxjs_operators.filter)((context) => Boolean(context)), (0, rxjs_operators.take)(1), (0, rxjs_operators.map)((context) => ({
3190
+ action,
3191
+ context
3192
+ })), (0, rxjs_operators.takeUntil)(actions$.pipe(ofType(threadAdapterEvents.contextChanged, threadAdapterEvents.stopped))), (0, rxjs_operators.switchMap)(({ action: currentAction, context }) => createThreadFetchObservable(environment, context, currentAction.sessionId)))))),
3193
+ createEffect((actions$, state$) => actions$.pipe(ofType(threadRestEvents.listSucceeded), (0, rxjs_operators.withLatestFrom)(state$), (0, rxjs_operators.filter)(([action, state]) => {
3194
+ return action.sessionId === state.sessionId && !state.metadataCredentialsRequested && Boolean(state.context?.wsUrl) && Boolean(state.metadataJoinCode);
3195
+ }), (0, rxjs_operators.map)(([action]) => threadRestEvents.metadataCredentialsRequested({ sessionId: action.sessionId })))),
3196
+ createEffect((actions$, state$) => actions$.pipe(ofType(threadRestEvents.metadataCredentialsRequested), (0, rxjs_operators.switchMap)((action) => state$.pipe((0, rxjs_operators.map)((state) => state.context), (0, rxjs_operators.filter)((context) => Boolean(context)), (0, rxjs_operators.take)(1), (0, rxjs_operators.map)((context) => ({
3197
+ action,
3198
+ context
3199
+ })), (0, rxjs_operators.takeUntil)(actions$.pipe(ofType(threadAdapterEvents.contextChanged, threadAdapterEvents.stopped))), (0, rxjs_operators.switchMap)(({ action: currentAction, context }) => createThreadMetadataCredentialsObservable(environment, context, currentAction.sessionId)))))),
3200
+ createEffect((actions$, state$) => actions$.pipe(ofType(threadRestEvents.metadataCredentialsSucceeded), (0, rxjs_operators.withLatestFrom)(state$), (0, rxjs_operators.filter)(([action, state]) => {
3201
+ return action.sessionId === state.sessionId && Boolean(state.context?.wsUrl);
3202
+ }), (0, rxjs_operators.switchMap)(([action, state]) => {
3203
+ const context = state.context;
3204
+ const joinToken = action.joinToken;
3205
+ const joinCode = state.metadataJoinCode;
3206
+ const shutdown$ = actions$.pipe(ofType(threadAdapterEvents.contextChanged, threadAdapterEvents.stopped));
3207
+ return (0, rxjs.defer)(() => {
3208
+ const socket$ = ɵphoenixSocket$({
3209
+ url: context.wsUrl,
3210
+ options: {
3211
+ params: { join_token: joinToken },
3212
+ reconnectAfterMs: (0, _copilotkit_shared.phoenixExponentialBackoff)(100, 1e4),
3213
+ rejoinAfterMs: (0, _copilotkit_shared.phoenixExponentialBackoff)(1e3, 3e4)
3214
+ }
3215
+ }).pipe((0, rxjs_operators.shareReplay)({
3216
+ bufferSize: 1,
3217
+ refCount: true
3218
+ }));
3219
+ const channel$ = ɵphoenixChannel$({
3220
+ socket$,
3221
+ topic: `user_meta:${joinCode}`
3222
+ }).pipe((0, rxjs_operators.shareReplay)({
3223
+ bufferSize: 1,
3224
+ refCount: true
3225
+ }));
3226
+ const socketSignals$ = ɵobservePhoenixSocketSignals$(socket$).pipe((0, rxjs_operators.share)());
3227
+ const fatalSocketShutdown$ = ɵobservePhoenixSocketHealth$(socketSignals$, MAX_SOCKET_RETRIES).pipe((0, rxjs_operators.catchError)(() => {
3228
+ console.warn(`[threads] WebSocket failed after ${MAX_SOCKET_RETRIES} attempts, giving up`);
3229
+ return (0, rxjs.of)(void 0);
3230
+ }), (0, rxjs_operators.share)());
3231
+ return (0, rxjs.merge)(socketSignals$.pipe((0, rxjs_operators.map)((signal) => signal.type === "open" ? threadSocketEvents.opened({ sessionId: action.sessionId }) : threadSocketEvents.errored({ sessionId: action.sessionId }))), channel$.pipe((0, rxjs_operators.switchMap)(({ channel }) => ɵobservePhoenixEvent$(channel, THREADS_CHANNEL_EVENT)), (0, rxjs_operators.map)((payload) => threadSocketEvents.metadataReceived({
3232
+ sessionId: action.sessionId,
3233
+ payload
3234
+ }))), ɵobservePhoenixJoinOutcome$(channel$).pipe((0, rxjs_operators.filter)((outcome) => outcome.type !== "joined"), (0, rxjs_operators.map)((outcome) => outcome.type === "timeout" ? threadSocketEvents.joinTimedOut({ sessionId: action.sessionId }) : threadSocketEvents.joinFailed({ sessionId: action.sessionId })))).pipe((0, rxjs_operators.takeUntil)((0, rxjs.merge)(shutdown$, fatalSocketShutdown$)));
3235
+ });
3236
+ }))),
3237
+ createEffect((actions$, state$) => actions$.pipe(ofType(threadSocketEvents.metadataReceived), (0, rxjs_operators.withLatestFrom)(state$), (0, rxjs_operators.filter)(([action, state]) => action.sessionId === state.sessionId), (0, rxjs_operators.map)(([action, state]) => {
3238
+ if (action.payload.operation === "deleted") return threadDomainEvents.threadDeleted({
3239
+ sessionId: action.sessionId,
3240
+ threadId: action.payload.deleted.id
3241
+ });
3242
+ if (action.payload.operation === "archived" && !state.context?.includeArchived) return threadDomainEvents.threadDeleted({
3243
+ sessionId: action.sessionId,
3244
+ threadId: action.payload.threadId
3245
+ });
3246
+ return threadDomainEvents.threadUpserted({
3247
+ sessionId: action.sessionId,
3248
+ thread: action.payload.thread
3249
+ });
3250
+ }))),
3251
+ createEffect((actions$, state$) => actions$.pipe(ofType(threadAdapterEvents.fetchNextPageRequested), (0, rxjs_operators.withLatestFrom)(state$), (0, rxjs_operators.filter)(([, state]) => Boolean(state.context) && Boolean(state.nextCursor)), (0, rxjs_operators.switchMap)(([, state]) => {
3252
+ const context = state.context;
3253
+ const params = {
3254
+ agentId: context.agentId,
3255
+ cursor: state.nextCursor
3256
+ };
3257
+ if (context.includeArchived) params.includeArchived = "true";
3258
+ if (context.limit != null) params.limit = String(context.limit);
3259
+ return (0, rxjs_fetch.fromFetch)(`${context.runtimeUrl}/threads?${new URLSearchParams(params).toString()}`, {
3260
+ selector: (response) => {
3261
+ if (!response.ok) throw new Error(`Failed to fetch next page: ${response.status}`);
3262
+ return response.json();
3263
+ },
3264
+ fetch: environment.fetch,
3265
+ method: "GET",
3266
+ headers: { ...context.headers }
3267
+ }).pipe((0, rxjs_operators.timeout)({
3268
+ first: REQUEST_TIMEOUT_MS,
3269
+ with: () => {
3270
+ throw new Error("Request timed out");
3271
+ }
3272
+ }), (0, rxjs_operators.map)((data) => threadRestEvents.nextPageSucceeded({
3273
+ sessionId: state.sessionId,
3274
+ threads: data.threads,
3275
+ nextCursor: data.nextCursor ?? null
3276
+ })), (0, rxjs_operators.catchError)((error) => (0, rxjs.of)(threadRestEvents.nextPageFailed({
3277
+ sessionId: state.sessionId,
3278
+ error: error instanceof Error ? error : new Error(String(error))
3279
+ }))), (0, rxjs_operators.takeUntil)(actions$.pipe(ofType(threadAdapterEvents.contextChanged, threadAdapterEvents.stopped))));
3280
+ }))),
3281
+ createEffect((actions$, state$) => actions$.pipe(ofType(threadAdapterEvents.renameRequested, threadAdapterEvents.archiveRequested, threadAdapterEvents.deleteRequested), (0, rxjs_operators.withLatestFrom)(state$), (0, rxjs_operators.mergeMap)(([action, state]) => {
3282
+ const context = state.context;
3283
+ if (!context?.runtimeUrl) {
3284
+ const requestId = action.requestId;
3285
+ return (0, rxjs.of)(threadRestEvents.mutationFinished({ outcome: {
3286
+ requestId,
3287
+ ok: false,
3288
+ error: /* @__PURE__ */ new Error("Runtime URL is not configured")
3289
+ } }));
3290
+ }
3291
+ const commonBody = { agentId: context.agentId };
3292
+ if (threadAdapterEvents.renameRequested.match(action)) return createThreadMutationObservable(environment, context, {
3293
+ requestId: action.requestId,
3294
+ method: "PATCH",
3295
+ path: `/threads/${encodeURIComponent(action.threadId)}`,
3296
+ body: {
3297
+ ...commonBody,
3298
+ name: action.name
3299
+ }
3300
+ });
3301
+ if (threadAdapterEvents.archiveRequested.match(action)) return createThreadMutationObservable(environment, context, {
3302
+ requestId: action.requestId,
3303
+ method: "POST",
3304
+ path: `/threads/${encodeURIComponent(action.threadId)}/archive`,
3305
+ body: commonBody
3306
+ });
3307
+ return createThreadMutationObservable(environment, context, {
3308
+ requestId: action.requestId,
3309
+ method: "DELETE",
3310
+ path: `/threads/${encodeURIComponent(action.threadId)}`,
3311
+ body: commonBody
3312
+ });
3313
+ })))
3314
+ ]
3315
+ });
3316
+ function trackMutation(dispatchAction) {
3317
+ const resultPromise = (0, rxjs.firstValueFrom)((0, rxjs.merge)(store.actions$.pipe(ofType(threadRestEvents.mutationFinished), (0, rxjs_operators.filter)((action) => action.outcome.requestId === dispatchAction.requestId), (0, rxjs_operators.map)((action) => action.outcome)), store.actions$.pipe(ofType(threadAdapterEvents.stopped), (0, rxjs_operators.map)(() => ({
3318
+ requestId: dispatchAction.requestId,
3319
+ ok: false,
3320
+ error: /* @__PURE__ */ new Error("Thread store stopped before mutation completed")
3321
+ })))).pipe((0, rxjs_operators.take)(1))).then((outcome) => {
3322
+ if (outcome.ok) return;
3323
+ throw outcome.error;
3324
+ });
3325
+ store.dispatch(dispatchAction);
3326
+ return resultPromise;
3327
+ }
3328
+ return {
3329
+ start() {
3330
+ store.init();
3331
+ store.dispatch(threadAdapterEvents.started());
3332
+ },
3333
+ stop() {
3334
+ store.dispatch(threadAdapterEvents.stopped());
3335
+ store.stop();
3336
+ },
3337
+ setContext(context) {
3338
+ store.dispatch(threadAdapterEvents.contextChanged({ context }));
3339
+ },
3340
+ fetchNextPage() {
3341
+ store.dispatch(threadAdapterEvents.fetchNextPageRequested());
3342
+ },
3343
+ renameThread(threadId, name) {
3344
+ return trackMutation(threadAdapterEvents.renameRequested({
3345
+ requestId: createThreadRequestId(),
3346
+ threadId,
3347
+ name
3348
+ }));
3349
+ },
3350
+ archiveThread(threadId) {
3351
+ return trackMutation(threadAdapterEvents.archiveRequested({
3352
+ requestId: createThreadRequestId(),
3353
+ threadId
3354
+ }));
3355
+ },
3356
+ deleteThread(threadId) {
3357
+ return trackMutation(threadAdapterEvents.deleteRequested({
3358
+ requestId: createThreadRequestId(),
3359
+ threadId
3360
+ }));
3361
+ },
3362
+ getState() {
3363
+ return store.getState();
3364
+ },
3365
+ select: store.select.bind(store)
3366
+ };
3367
+ }
3368
+ const ɵthreadAdapterEvents = threadAdapterEvents;
3369
+ const ɵselectThreads = selectThreads;
3370
+ const ɵselectThreadsIsLoading = selectThreadsIsLoading;
3371
+ const ɵselectThreadsError = selectThreadsError;
3372
+ const ɵselectHasNextPage = selectHasNextPage;
3373
+ const ɵselectIsFetchingNextPage = selectIsFetchingNextPage;
3374
+
3375
+ //#endregion
3376
+ exports.AgentRegistry = AgentRegistry;
3377
+ exports.AgentThreadLockedError = AgentThreadLockedError;
3378
+ exports.ContextStore = ContextStore;
3379
+ exports.CopilotKitCore = CopilotKitCore;
3380
+ exports.CopilotKitCoreErrorCode = CopilotKitCoreErrorCode;
3381
+ exports.CopilotKitCoreRuntimeConnectionStatus = CopilotKitCoreRuntimeConnectionStatus;
3382
+ exports.IntelligenceAgent = IntelligenceAgent;
3383
+ exports.ProxiedCopilotRuntimeAgent = ProxiedCopilotRuntimeAgent;
3384
+ exports.RunHandler = RunHandler;
3385
+ exports.StateManager = StateManager;
3386
+ exports.SuggestionEngine = SuggestionEngine;
3387
+ exports.ToolCallStatus = ToolCallStatus;
3388
+ exports.completePartialMarkdown = completePartialMarkdown;
3389
+ exports.createActionGroup = createActionGroup;
3390
+ exports.createEffect = createEffect;
3391
+ exports.createReducer = createReducer;
3392
+ exports.createSelector = createSelector;
3393
+ exports.createStore = createStore;
3394
+ exports.empty = empty;
3395
+ exports.ensureObjectArgs = ensureObjectArgs;
3396
+ exports.ofType = ofType;
3397
+ exports.on = on;
3398
+ exports.parseToolArguments = parseToolArguments;
3399
+ exports.props = props;
3400
+ exports.select = select;
3401
+ exports.ɵcreateThreadStore = createThreadStore;
3402
+ exports.ɵjoinPhoenixChannel$ = ɵjoinPhoenixChannel$;
3403
+ exports.ɵobservePhoenixEvent$ = ɵobservePhoenixEvent$;
3404
+ exports.ɵobservePhoenixJoinOutcome$ = ɵobservePhoenixJoinOutcome$;
3405
+ exports.ɵobservePhoenixSocketHealth$ = ɵobservePhoenixSocketHealth$;
3406
+ exports.ɵobservePhoenixSocketSignals$ = ɵobservePhoenixSocketSignals$;
3407
+ exports.ɵphoenixChannel$ = ɵphoenixChannel$;
3408
+ exports.ɵphoenixSocket$ = ɵphoenixSocket$;
3409
+ exports.ɵselectHasNextPage = ɵselectHasNextPage;
3410
+ exports.ɵselectIsFetchingNextPage = ɵselectIsFetchingNextPage;
3411
+ exports.ɵselectThreads = ɵselectThreads;
3412
+ exports.ɵselectThreadsError = ɵselectThreadsError;
3413
+ exports.ɵselectThreadsIsLoading = ɵselectThreadsIsLoading;
3414
+ exports.ɵthreadAdapterEvents = ɵthreadAdapterEvents;
3415
+ //# sourceMappingURL=index.cjs.map