@copilotkit/core 1.56.2 → 1.56.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +285 -125
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +93 -25
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +93 -25
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +285 -125
- package/dist/index.mjs.map +1 -1
- package/dist/index.umd.js +292 -130
- package/dist/index.umd.js.map +1 -1
- package/package.json +3 -2
package/dist/index.cjs
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
2
|
+
let _tanstack_pacer = require("@tanstack/pacer");
|
|
2
3
|
let _ag_ui_client = require("@ag-ui/client");
|
|
3
4
|
let _copilotkit_shared = require("@copilotkit/shared");
|
|
4
5
|
let rxjs = require("rxjs");
|
|
@@ -127,6 +128,8 @@ function ɵobservePhoenixSocketHealth$(socketSignals$, maxConsecutiveErrors) {
|
|
|
127
128
|
//#endregion
|
|
128
129
|
//#region src/intelligence-agent.ts
|
|
129
130
|
const CLIENT_AG_UI_EVENT = "ag_ui_event";
|
|
131
|
+
const REPLAY_COMPLETE_EVENT = "replay_complete";
|
|
132
|
+
const STREAM_IDLE_EVENT = "stream_idle";
|
|
130
133
|
const STOP_RUN_EVENT = "stop_run";
|
|
131
134
|
var AgentThreadLockedError = class extends Error {
|
|
132
135
|
constructor(threadId) {
|
|
@@ -138,7 +141,7 @@ var IntelligenceAgent = class IntelligenceAgent extends _ag_ui_client.AbstractAg
|
|
|
138
141
|
config;
|
|
139
142
|
socket = null;
|
|
140
143
|
activeChannel = null;
|
|
141
|
-
|
|
144
|
+
canonicalRunId = null;
|
|
142
145
|
sharedState;
|
|
143
146
|
constructor(config, sharedState = { lastSeenEventIds: /* @__PURE__ */ new Map() }) {
|
|
144
147
|
super();
|
|
@@ -176,7 +179,11 @@ var IntelligenceAgent = class IntelligenceAgent extends _ag_ui_client.AbstractAg
|
|
|
176
179
|
try {
|
|
177
180
|
this.isRunning = true;
|
|
178
181
|
this.agentId = this.agentId ?? (0, _ag_ui_client.randomUUID)();
|
|
179
|
-
const
|
|
182
|
+
const effectiveParameters = parameters?.runId || !this.canonicalRunId ? parameters : {
|
|
183
|
+
...parameters,
|
|
184
|
+
runId: this.canonicalRunId
|
|
185
|
+
};
|
|
186
|
+
const input = this.prepareRunAgentInput(effectiveParameters);
|
|
180
187
|
let result;
|
|
181
188
|
const previousMessageIds = new Set(this.messages.map((m) => m.id));
|
|
182
189
|
const subscribers = [
|
|
@@ -215,14 +222,14 @@ var IntelligenceAgent = class IntelligenceAgent extends _ag_ui_client.AbstractAg
|
|
|
215
222
|
}
|
|
216
223
|
}
|
|
217
224
|
abortRun() {
|
|
218
|
-
if (this.activeChannel && this.
|
|
225
|
+
if (this.activeChannel && this.canonicalRunId) {
|
|
219
226
|
const fallback = setTimeout(() => clear(), 5e3);
|
|
220
227
|
const clear = () => {
|
|
221
228
|
clearTimeout(fallback);
|
|
222
229
|
this.detachActiveRun();
|
|
223
230
|
this.cleanup();
|
|
224
231
|
};
|
|
225
|
-
this.activeChannel.push(STOP_RUN_EVENT, { run_id: this.
|
|
232
|
+
this.activeChannel.push(STOP_RUN_EVENT, { run_id: this.canonicalRunId }).receive("ok", clear).receive("error", clear).receive("timeout", clear);
|
|
226
233
|
} else {
|
|
227
234
|
this.detachActiveRun();
|
|
228
235
|
this.cleanup();
|
|
@@ -234,11 +241,15 @@ var IntelligenceAgent = class IntelligenceAgent extends _ag_ui_client.AbstractAg
|
|
|
234
241
|
*/
|
|
235
242
|
run(input) {
|
|
236
243
|
this.threadId = input.threadId;
|
|
237
|
-
this.
|
|
238
|
-
return (0, rxjs.defer)(() => this.requestJoinCredentials$("run", input)).pipe((0, rxjs.switchMap)((credentials) =>
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
244
|
+
this.canonicalRunId = input.runId;
|
|
245
|
+
return (0, rxjs.defer)(() => this.requestJoinCredentials$("run", input)).pipe((0, rxjs.switchMap)((credentials) => {
|
|
246
|
+
if (credentials === null) return (0, rxjs.throwError)(() => /* @__PURE__ */ new Error("REST run request returned no credentials"));
|
|
247
|
+
const canonicalInput = this.applyCanonicalRunIdentity(input, credentials, { fallbackToInputRunId: true });
|
|
248
|
+
return this.observeThread$(canonicalInput, credentials, {
|
|
249
|
+
completeOnRunError: false,
|
|
250
|
+
streamMode: "run"
|
|
251
|
+
});
|
|
252
|
+
}));
|
|
242
253
|
}
|
|
243
254
|
/**
|
|
244
255
|
* Reconnect to an existing thread by fetching websocket credentials and
|
|
@@ -246,21 +257,15 @@ var IntelligenceAgent = class IntelligenceAgent extends _ag_ui_client.AbstractAg
|
|
|
246
257
|
*/
|
|
247
258
|
connect(input) {
|
|
248
259
|
this.threadId = input.threadId;
|
|
249
|
-
this.
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
if (
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
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
|
-
}));
|
|
260
|
+
this.canonicalRunId = null;
|
|
261
|
+
this.clearReconnectCursor(input.threadId);
|
|
262
|
+
return (0, rxjs.defer)(() => this.requestJoinCredentials$("connect", input)).pipe((0, rxjs.switchMap)((credentials) => {
|
|
263
|
+
if (credentials === null) return rxjs.EMPTY;
|
|
264
|
+
const canonicalInput = this.applyCanonicalRunIdentity(input, credentials, { fallbackToInputRunId: false });
|
|
265
|
+
return this.observeThread$(canonicalInput, credentials, {
|
|
266
|
+
completeOnRunError: false,
|
|
267
|
+
streamMode: "connect"
|
|
268
|
+
});
|
|
264
269
|
}));
|
|
265
270
|
}
|
|
266
271
|
/**
|
|
@@ -277,8 +282,7 @@ var IntelligenceAgent = class IntelligenceAgent extends _ag_ui_client.AbstractAg
|
|
|
277
282
|
ownSocket.disconnect();
|
|
278
283
|
if (this.socket === ownSocket) this.socket = null;
|
|
279
284
|
}
|
|
280
|
-
|
|
281
|
-
this.runId = null;
|
|
285
|
+
this.canonicalRunId = null;
|
|
282
286
|
}
|
|
283
287
|
cleanup() {
|
|
284
288
|
this.cleanupOwned(this.activeChannel, this.socket);
|
|
@@ -299,80 +303,56 @@ var IntelligenceAgent = class IntelligenceAgent extends _ag_ui_client.AbstractAg
|
|
|
299
303
|
tools: input.tools,
|
|
300
304
|
context: input.context,
|
|
301
305
|
state: input.state,
|
|
302
|
-
forwardedProps: input.forwardedProps
|
|
306
|
+
forwardedProps: input.forwardedProps,
|
|
307
|
+
...mode === "connect" ? { lastSeenEventId: this.getReconnectCursor(input) } : {}
|
|
303
308
|
}),
|
|
304
309
|
...this.config.credentials ? { credentials: this.config.credentials } : {}
|
|
305
310
|
});
|
|
306
|
-
if (response.status ===
|
|
311
|
+
if (response.status === 204 && mode === "connect") return null;
|
|
312
|
+
if (response.status === 409 && mode === "run") throw new AgentThreadLockedError(input.threadId);
|
|
307
313
|
if (!response.ok) {
|
|
308
314
|
const text = await response.text().catch(() => "");
|
|
309
315
|
throw new Error(text || response.statusText || String(response.status));
|
|
310
316
|
}
|
|
311
|
-
|
|
312
|
-
if (!payload.joinToken) throw new Error("missing joinToken");
|
|
313
|
-
return { joinToken: payload.joinToken };
|
|
317
|
+
return this.normalizeJoinCredentials(await response.json(), input);
|
|
314
318
|
} catch (error) {
|
|
315
319
|
if (error instanceof AgentThreadLockedError) throw error;
|
|
316
320
|
throw new Error(`REST ${mode} request failed: ${error instanceof Error ? error.message : String(error)}`, { cause: error });
|
|
317
321
|
}
|
|
318
322
|
});
|
|
319
323
|
}
|
|
320
|
-
|
|
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)}`, { cause: error });
|
|
349
|
-
}
|
|
350
|
-
});
|
|
351
|
-
}
|
|
352
|
-
normalizeConnectPlan(payload) {
|
|
324
|
+
normalizeJoinCredentials(payload, input) {
|
|
353
325
|
const envelope = payload && typeof payload === "object" ? payload : null;
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
326
|
+
const realtime = envelope?.realtime && typeof envelope.realtime === "object" ? envelope.realtime : null;
|
|
327
|
+
if (typeof envelope?.joinToken !== "string" || !envelope.joinToken) throw new Error("missing joinToken");
|
|
328
|
+
if (typeof realtime?.clientUrl !== "string" || !realtime.clientUrl) throw new Error("missing realtime.clientUrl");
|
|
329
|
+
if (typeof realtime.topic !== "string" || !realtime.topic) throw new Error("missing realtime.topic");
|
|
330
|
+
return {
|
|
331
|
+
threadId: typeof envelope.threadId === "string" && envelope.threadId ? envelope.threadId : input.threadId,
|
|
332
|
+
runId: typeof envelope.runId === "string" && envelope.runId ? envelope.runId : null,
|
|
333
|
+
joinToken: envelope.joinToken,
|
|
334
|
+
realtime: {
|
|
335
|
+
clientUrl: realtime.clientUrl,
|
|
336
|
+
topic: realtime.topic
|
|
337
|
+
}
|
|
358
338
|
};
|
|
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
339
|
}
|
|
370
340
|
observeThread$(input, credentials, options) {
|
|
341
|
+
return this.observeThreadSession$(input, credentials, options).pipe((0, rxjs_operators.catchError)((error) => {
|
|
342
|
+
if (!this.isSocketReconnectExhaustedError(error)) return (0, rxjs.throwError)(() => error);
|
|
343
|
+
return this.requestJoinCredentials$("connect", input).pipe((0, rxjs.switchMap)((refreshedCredentials) => refreshedCredentials === null ? rxjs.EMPTY : this.observeThread$(this.applyCanonicalRunIdentity(input, refreshedCredentials, { fallbackToInputRunId: options.streamMode === "run" }), refreshedCredentials, {
|
|
344
|
+
...options,
|
|
345
|
+
channelMode: "connect",
|
|
346
|
+
replayCursor: this.getReconnectCursor(input)
|
|
347
|
+
})));
|
|
348
|
+
}));
|
|
349
|
+
}
|
|
350
|
+
observeThreadSession$(input, credentials, options) {
|
|
371
351
|
return (0, rxjs.defer)(() => {
|
|
372
352
|
let ownSocket = null;
|
|
373
353
|
let ownChannel = null;
|
|
374
354
|
const socket$ = ɵphoenixSocket$({
|
|
375
|
-
url:
|
|
355
|
+
url: credentials.realtime.clientUrl,
|
|
376
356
|
options: {
|
|
377
357
|
params: {
|
|
378
358
|
...this.config.socketParams,
|
|
@@ -388,10 +368,10 @@ var IntelligenceAgent = class IntelligenceAgent extends _ag_ui_client.AbstractAg
|
|
|
388
368
|
bufferSize: 1,
|
|
389
369
|
refCount: true
|
|
390
370
|
}));
|
|
391
|
-
const
|
|
371
|
+
const params = this.createThreadChannelParams(input, options.channelMode ?? options.streamMode, options.replayCursor);
|
|
392
372
|
const channel$ = ɵphoenixChannel$({
|
|
393
373
|
socket$,
|
|
394
|
-
topic,
|
|
374
|
+
topic: credentials.realtime.topic,
|
|
395
375
|
params
|
|
396
376
|
}).pipe((0, rxjs_operators.tap)(({ channel }) => {
|
|
397
377
|
ownChannel = channel;
|
|
@@ -401,8 +381,14 @@ var IntelligenceAgent = class IntelligenceAgent extends _ag_ui_client.AbstractAg
|
|
|
401
381
|
refCount: true
|
|
402
382
|
}));
|
|
403
383
|
const threadEvents$ = this.observeThreadEvents$(input.threadId, channel$, options).pipe((0, rxjs_operators.share)());
|
|
404
|
-
const
|
|
405
|
-
|
|
384
|
+
const replayComplete$ = this.observeControlEvent$(input.threadId, channel$, REPLAY_COMPLETE_EVENT).pipe((0, rxjs_operators.ignoreElements)(), (0, rxjs_operators.share)());
|
|
385
|
+
const streamIdle$ = this.observeControlEvent$(input.threadId, channel$, STREAM_IDLE_EVENT).pipe((0, rxjs_operators.shareReplay)({
|
|
386
|
+
bufferSize: 1,
|
|
387
|
+
refCount: true
|
|
388
|
+
}));
|
|
389
|
+
const streamIdleCompletion$ = options.streamMode === "connect" ? streamIdle$.pipe((0, rxjs_operators.take)(1)) : rxjs.EMPTY;
|
|
390
|
+
const terminal$ = (0, rxjs.merge)(threadEvents$.pipe((0, rxjs_operators.ignoreElements)(), (0, rxjs_operators.endWith)(null), (0, rxjs_operators.take)(1)), streamIdleCompletion$);
|
|
391
|
+
return (0, rxjs.merge)(this.joinThreadChannel$(channel$), this.observeSocketHealth$(socket$).pipe((0, rxjs_operators.takeUntil)(terminal$)), threadEvents$.pipe((0, rxjs_operators.takeUntil)(streamIdleCompletion$)), replayComplete$.pipe((0, rxjs_operators.takeUntil)(terminal$)), streamIdleCompletion$.pipe((0, rxjs_operators.ignoreElements)())).pipe((0, rxjs_operators.finalize)(() => this.cleanupOwned(ownChannel, ownSocket)));
|
|
406
392
|
});
|
|
407
393
|
}
|
|
408
394
|
joinThreadChannel$(channel$) {
|
|
@@ -414,17 +400,23 @@ var IntelligenceAgent = class IntelligenceAgent extends _ag_ui_client.AbstractAg
|
|
|
414
400
|
observeThreadEvents$(threadId, channel$, options) {
|
|
415
401
|
return channel$.pipe((0, rxjs_operators.switchMap)(({ channel }) => this.observeChannelEvent$(channel, CLIENT_AG_UI_EVENT)), (0, rxjs_operators.tap)((payload) => {
|
|
416
402
|
this.updateLastSeenEventId(threadId, payload);
|
|
417
|
-
|
|
418
|
-
|
|
403
|
+
}), (0, rxjs_operators.mergeMap)((payload) => this.createThreadNotifications(payload, {
|
|
404
|
+
completeOnRunError: options.completeOnRunError,
|
|
405
|
+
completeOnRunFinished: options.streamMode === "run",
|
|
406
|
+
errorOnRunError: options.streamMode === "run"
|
|
407
|
+
})), (0, rxjs.dematerialize)());
|
|
408
|
+
}
|
|
409
|
+
observeControlEvent$(threadId, channel$, eventName) {
|
|
410
|
+
return channel$.pipe((0, rxjs_operators.switchMap)(({ channel }) => this.observeChannelEvent$(channel, eventName)), (0, rxjs_operators.tap)((payload) => this.updateLastSeenEventIdFromControl(threadId, payload)));
|
|
419
411
|
}
|
|
420
412
|
observeChannelEvent$(channel, eventName) {
|
|
421
413
|
return ɵobservePhoenixEvent$(channel, eventName);
|
|
422
414
|
}
|
|
423
|
-
createThreadNotifications(payload,
|
|
424
|
-
if (payload.type === _ag_ui_client.EventType.RUN_FINISHED) return [rxjs.Notification.createNext(payload), rxjs.Notification.createComplete()];
|
|
415
|
+
createThreadNotifications(payload, options) {
|
|
416
|
+
if (payload.type === _ag_ui_client.EventType.RUN_FINISHED) return options.completeOnRunFinished ? [rxjs.Notification.createNext(payload), rxjs.Notification.createComplete()] : [rxjs.Notification.createNext(payload)];
|
|
425
417
|
if (payload.type === _ag_ui_client.EventType.RUN_ERROR) {
|
|
426
418
|
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))];
|
|
419
|
+
return options.completeOnRunError ? [rxjs.Notification.createNext(payload), rxjs.Notification.createComplete()] : options.errorOnRunError ? [rxjs.Notification.createNext(payload), rxjs.Notification.createError(new Error(errorMessage))] : [rxjs.Notification.createNext(payload)];
|
|
428
420
|
}
|
|
429
421
|
return [rxjs.Notification.createNext(payload)];
|
|
430
422
|
}
|
|
@@ -433,60 +425,58 @@ var IntelligenceAgent = class IntelligenceAgent extends _ag_ui_client.AbstractAg
|
|
|
433
425
|
const origin = typeof window !== "undefined" && window.location ? window.location.origin : "http://localhost";
|
|
434
426
|
return new URL(path, new URL(this.config.runtimeUrl, origin)).toString();
|
|
435
427
|
}
|
|
436
|
-
|
|
437
|
-
|
|
428
|
+
createThreadChannelParams(input, streamMode, replayCursor) {
|
|
429
|
+
return streamMode === "run" ? {
|
|
438
430
|
stream_mode: "run",
|
|
439
431
|
run_id: input.runId
|
|
440
432
|
} : {
|
|
441
433
|
stream_mode: "connect",
|
|
442
434
|
last_seen_event_id: replayCursor === void 0 ? this.getReconnectCursor(input) : replayCursor
|
|
443
435
|
};
|
|
444
|
-
return {
|
|
445
|
-
topic: `thread:${input.threadId}`,
|
|
446
|
-
params
|
|
447
|
-
};
|
|
448
436
|
}
|
|
449
437
|
getLastSeenEventId(threadId) {
|
|
450
438
|
return this.sharedState.lastSeenEventIds.get(threadId) ?? null;
|
|
451
439
|
}
|
|
452
440
|
getReconnectCursor(input) {
|
|
453
|
-
return this.
|
|
441
|
+
return this.getLastSeenEventId(input.threadId);
|
|
454
442
|
}
|
|
455
|
-
|
|
456
|
-
|
|
443
|
+
clearReconnectCursor(threadId) {
|
|
444
|
+
this.sharedState.lastSeenEventIds.delete(threadId);
|
|
457
445
|
}
|
|
458
446
|
updateLastSeenEventId(threadId, payload) {
|
|
459
447
|
const eventId = this.readEventId(payload);
|
|
460
448
|
if (!eventId) return;
|
|
461
449
|
this.sharedState.lastSeenEventIds.set(threadId, eventId);
|
|
462
450
|
}
|
|
463
|
-
|
|
451
|
+
updateLastSeenEventIdFromControl(threadId, payload) {
|
|
452
|
+
const eventId = this.readControlEventId(payload);
|
|
464
453
|
if (!eventId) return;
|
|
465
454
|
this.sharedState.lastSeenEventIds.set(threadId, eventId);
|
|
466
455
|
}
|
|
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
456
|
readEventId(payload) {
|
|
485
457
|
const metadata = payload.metadata;
|
|
486
458
|
if (!metadata || typeof metadata !== "object") return null;
|
|
487
459
|
const runnerEventId = metadata.cpki_event_id;
|
|
488
460
|
return typeof runnerEventId === "string" ? runnerEventId : null;
|
|
489
461
|
}
|
|
462
|
+
readControlEventId(payload) {
|
|
463
|
+
if (!payload || typeof payload !== "object") return null;
|
|
464
|
+
const latestEventId = payload.latestEventId;
|
|
465
|
+
return typeof latestEventId === "string" ? latestEventId : null;
|
|
466
|
+
}
|
|
467
|
+
applyCanonicalRunIdentity(input, credentials, options) {
|
|
468
|
+
this.threadId = credentials.threadId;
|
|
469
|
+
const runId = credentials.runId ?? (options.fallbackToInputRunId ? input.runId : null);
|
|
470
|
+
this.canonicalRunId = runId;
|
|
471
|
+
return {
|
|
472
|
+
...input,
|
|
473
|
+
threadId: credentials.threadId,
|
|
474
|
+
...runId === null ? {} : { runId }
|
|
475
|
+
};
|
|
476
|
+
}
|
|
477
|
+
isSocketReconnectExhaustedError(error) {
|
|
478
|
+
return error instanceof Error && error.message.includes("WebSocket connection failed after");
|
|
479
|
+
}
|
|
490
480
|
};
|
|
491
481
|
|
|
492
482
|
//#endregion
|
|
@@ -2309,8 +2299,21 @@ let CopilotKitCoreErrorCode = /* @__PURE__ */ function(CopilotKitCoreErrorCode)
|
|
|
2309
2299
|
CopilotKitCoreErrorCode["TRANSCRIPTION_RATE_LIMITED"] = "transcription_rate_limited";
|
|
2310
2300
|
CopilotKitCoreErrorCode["TRANSCRIPTION_AUTH_FAILED"] = "transcription_auth_failed";
|
|
2311
2301
|
CopilotKitCoreErrorCode["TRANSCRIPTION_NETWORK_ERROR"] = "transcription_network_error";
|
|
2302
|
+
CopilotKitCoreErrorCode["SUBSCRIBER_CALLBACK_FAILED"] = "subscriber_callback_failed";
|
|
2312
2303
|
return CopilotKitCoreErrorCode;
|
|
2313
2304
|
}({});
|
|
2305
|
+
/**
|
|
2306
|
+
* Runtime allowlist derived from {@link SUBSCRIBE_TO_AGENT_KEYS}. Hoisted
|
|
2307
|
+
* to module scope so the Set is allocated once, not per-subscription.
|
|
2308
|
+
*/
|
|
2309
|
+
const ALLOWED_KEYS = new Set([
|
|
2310
|
+
"onMessagesChanged",
|
|
2311
|
+
"onStateChanged",
|
|
2312
|
+
"onRunInitialized",
|
|
2313
|
+
"onRunFinalized",
|
|
2314
|
+
"onRunFailed",
|
|
2315
|
+
"onRunErrorEvent"
|
|
2316
|
+
]);
|
|
2314
2317
|
let CopilotKitCoreRuntimeConnectionStatus = /* @__PURE__ */ function(CopilotKitCoreRuntimeConnectionStatus) {
|
|
2315
2318
|
CopilotKitCoreRuntimeConnectionStatus["Disconnected"] = "disconnected";
|
|
2316
2319
|
CopilotKitCoreRuntimeConnectionStatus["Connected"] = "connected";
|
|
@@ -2376,6 +2379,16 @@ var CopilotKitCore = class {
|
|
|
2376
2379
|
}), "Subscriber onError error:");
|
|
2377
2380
|
}
|
|
2378
2381
|
/**
|
|
2382
|
+
* Log a message to the console and emit an error to subscribers.
|
|
2383
|
+
* Catches failures from `emitError` itself to prevent unhandled rejections.
|
|
2384
|
+
*/
|
|
2385
|
+
logAndEmitError(message, params, logLevel = "error") {
|
|
2386
|
+
console[logLevel](message, params.error);
|
|
2387
|
+
this.emitError(params).catch((emitErr) => {
|
|
2388
|
+
console.error(message + " — emitError itself failed:", emitErr);
|
|
2389
|
+
});
|
|
2390
|
+
}
|
|
2391
|
+
/**
|
|
2379
2392
|
* Snapshot accessors
|
|
2380
2393
|
*/
|
|
2381
2394
|
get context() {
|
|
@@ -2412,17 +2425,30 @@ var CopilotKitCore = class {
|
|
|
2412
2425
|
return this._properties;
|
|
2413
2426
|
}
|
|
2414
2427
|
/**
|
|
2415
|
-
* Default throttle interval (ms)
|
|
2416
|
-
*
|
|
2417
|
-
* `
|
|
2418
|
-
* or `<CopilotChat>` overrides this default and disables throttling.
|
|
2428
|
+
* Default throttle interval (ms) used by `subscribeToAgentWithOptions()`
|
|
2429
|
+
* when the caller does not specify an explicit `throttleMs`.
|
|
2430
|
+
* `undefined` means no default is configured; `0` means no throttling.
|
|
2419
2431
|
*/
|
|
2420
2432
|
get defaultThrottleMs() {
|
|
2421
2433
|
return this._defaultThrottleMs;
|
|
2422
2434
|
}
|
|
2435
|
+
/**
|
|
2436
|
+
* Set the default throttle interval (ms) for `subscribeToAgentWithOptions()`.
|
|
2437
|
+
*
|
|
2438
|
+
* Accepts a non-negative finite number or `undefined` (to clear the
|
|
2439
|
+
* default). Invalid values (NaN, Infinity, negative) are logged as
|
|
2440
|
+
* errors and ignored — the previous valid value is preserved.
|
|
2441
|
+
*/
|
|
2423
2442
|
setDefaultThrottleMs(value) {
|
|
2424
2443
|
if (value !== void 0 && (!Number.isFinite(value) || value < 0)) {
|
|
2425
|
-
this.
|
|
2444
|
+
this.logAndEmitError(`CopilotKitCore.setDefaultThrottleMs: value must be a non-negative finite number or undefined, got ${value}. Keeping current value (${this._defaultThrottleMs}).`, {
|
|
2445
|
+
error: /* @__PURE__ */ new Error(`setDefaultThrottleMs: invalid value (${value}), keeping current value (${this._defaultThrottleMs})`),
|
|
2446
|
+
code: CopilotKitCoreErrorCode.SUBSCRIBER_CALLBACK_FAILED,
|
|
2447
|
+
context: {
|
|
2448
|
+
value,
|
|
2449
|
+
currentValue: this._defaultThrottleMs
|
|
2450
|
+
}
|
|
2451
|
+
});
|
|
2426
2452
|
return;
|
|
2427
2453
|
}
|
|
2428
2454
|
this._defaultThrottleMs = value;
|
|
@@ -2543,6 +2569,137 @@ var CopilotKitCore = class {
|
|
|
2543
2569
|
} };
|
|
2544
2570
|
}
|
|
2545
2571
|
/**
|
|
2572
|
+
* Subscribe to an agent's notification and lifecycle events with
|
|
2573
|
+
* optional configuration (e.g. throttling).
|
|
2574
|
+
*
|
|
2575
|
+
* Wraps every callback with error protection (`safeCall`) and applies
|
|
2576
|
+
* the options before delegating to `agent.subscribe()`.
|
|
2577
|
+
*
|
|
2578
|
+
* See {@link SubscribeToAgentSubscriber} for the accepted callback subset
|
|
2579
|
+
* and the rationale for excluding AG-UI event handlers.
|
|
2580
|
+
*/
|
|
2581
|
+
subscribeToAgentWithOptions(agent, subscriber, options) {
|
|
2582
|
+
const resolved = options?.throttleMs ?? this._defaultThrottleMs ?? 0;
|
|
2583
|
+
let effectiveMs = 0;
|
|
2584
|
+
if (!Number.isFinite(resolved) || resolved < 0) {
|
|
2585
|
+
const source = options?.throttleMs !== void 0 ? "throttleMs" : "defaultThrottleMs";
|
|
2586
|
+
this.logAndEmitError(`CopilotKitCore.subscribeToAgentWithOptions: ${source} must be a non-negative finite number, got ${resolved}. Falling back to unthrottled.`, {
|
|
2587
|
+
error: /* @__PURE__ */ new Error(`subscribeToAgentWithOptions: invalid ${source} (${resolved}), falling back to unthrottled`),
|
|
2588
|
+
code: CopilotKitCoreErrorCode.SUBSCRIBER_CALLBACK_FAILED,
|
|
2589
|
+
context: {
|
|
2590
|
+
agentId: agent.agentId,
|
|
2591
|
+
source,
|
|
2592
|
+
value: resolved
|
|
2593
|
+
}
|
|
2594
|
+
});
|
|
2595
|
+
} else effectiveMs = resolved;
|
|
2596
|
+
const agentLabel = agent.agentId || "(unknown agent)";
|
|
2597
|
+
const safeCall = (label, fn, ...args) => {
|
|
2598
|
+
const reportError = (err, verb) => {
|
|
2599
|
+
this.logAndEmitError(`CopilotKitCore.subscribeToAgentWithOptions[${agentLabel}]: ${label} callback ${verb}:`, {
|
|
2600
|
+
error: err instanceof Error ? err : new Error(String(err)),
|
|
2601
|
+
code: CopilotKitCoreErrorCode.SUBSCRIBER_CALLBACK_FAILED,
|
|
2602
|
+
context: {
|
|
2603
|
+
agentId: agent.agentId,
|
|
2604
|
+
callback: label
|
|
2605
|
+
}
|
|
2606
|
+
});
|
|
2607
|
+
};
|
|
2608
|
+
try {
|
|
2609
|
+
const result = fn(...args);
|
|
2610
|
+
if (result instanceof Promise) return result.catch((err) => {
|
|
2611
|
+
reportError(err, "rejected");
|
|
2612
|
+
});
|
|
2613
|
+
return result;
|
|
2614
|
+
} catch (err) {
|
|
2615
|
+
reportError(err, "threw");
|
|
2616
|
+
}
|
|
2617
|
+
};
|
|
2618
|
+
const guardAll = (sub) => {
|
|
2619
|
+
const guarded = {};
|
|
2620
|
+
if (sub.onMessagesChanged) {
|
|
2621
|
+
const fn = sub.onMessagesChanged;
|
|
2622
|
+
guarded.onMessagesChanged = (params) => safeCall("onMessagesChanged", fn, params);
|
|
2623
|
+
}
|
|
2624
|
+
if (sub.onStateChanged) {
|
|
2625
|
+
const fn = sub.onStateChanged;
|
|
2626
|
+
guarded.onStateChanged = (params) => safeCall("onStateChanged", fn, params);
|
|
2627
|
+
}
|
|
2628
|
+
if (sub.onRunInitialized) {
|
|
2629
|
+
const fn = sub.onRunInitialized;
|
|
2630
|
+
guarded.onRunInitialized = (params) => safeCall("onRunInitialized", fn, params);
|
|
2631
|
+
}
|
|
2632
|
+
if (sub.onRunFinalized) {
|
|
2633
|
+
const fn = sub.onRunFinalized;
|
|
2634
|
+
guarded.onRunFinalized = (params) => safeCall("onRunFinalized", fn, params);
|
|
2635
|
+
}
|
|
2636
|
+
if (sub.onRunFailed) {
|
|
2637
|
+
const fn = sub.onRunFailed;
|
|
2638
|
+
guarded.onRunFailed = (params) => safeCall("onRunFailed", fn, params);
|
|
2639
|
+
}
|
|
2640
|
+
if (sub.onRunErrorEvent) {
|
|
2641
|
+
const fn = sub.onRunErrorEvent;
|
|
2642
|
+
guarded.onRunErrorEvent = (params) => safeCall("onRunErrorEvent", fn, params);
|
|
2643
|
+
}
|
|
2644
|
+
return guarded;
|
|
2645
|
+
};
|
|
2646
|
+
for (const key of Object.keys(subscriber)) if (typeof subscriber[key] === "function" && !ALLOWED_KEYS.has(key)) {
|
|
2647
|
+
const message = `CopilotKitCore.subscribeToAgentWithOptions[${agentLabel}]: callback "${key}" is not supported and was dropped. Supported callbacks: ${Array.from(ALLOWED_KEYS).join(", ")}. Use agent.subscribe() directly for event handlers and per-item notifications.`;
|
|
2648
|
+
this.logAndEmitError(message, {
|
|
2649
|
+
error: new Error(message),
|
|
2650
|
+
code: CopilotKitCoreErrorCode.SUBSCRIBER_CALLBACK_FAILED,
|
|
2651
|
+
context: {
|
|
2652
|
+
agentId: agent.agentId,
|
|
2653
|
+
droppedCallback: key
|
|
2654
|
+
}
|
|
2655
|
+
}, "warn");
|
|
2656
|
+
}
|
|
2657
|
+
if (effectiveMs <= 0) {
|
|
2658
|
+
const subscription = agent.subscribe(guardAll(subscriber));
|
|
2659
|
+
return { unsubscribe: () => subscription.unsubscribe() };
|
|
2660
|
+
}
|
|
2661
|
+
let active = true;
|
|
2662
|
+
let latestMessagesParams = null;
|
|
2663
|
+
let latestStateParams = null;
|
|
2664
|
+
const flushPending = () => {
|
|
2665
|
+
if (active && subscriber.onMessagesChanged && latestMessagesParams) {
|
|
2666
|
+
const params = latestMessagesParams;
|
|
2667
|
+
latestMessagesParams = null;
|
|
2668
|
+
safeCall("onMessagesChanged", subscriber.onMessagesChanged, params);
|
|
2669
|
+
}
|
|
2670
|
+
if (active && subscriber.onStateChanged && latestStateParams) {
|
|
2671
|
+
const params = latestStateParams;
|
|
2672
|
+
latestStateParams = null;
|
|
2673
|
+
safeCall("onStateChanged", subscriber.onStateChanged, params);
|
|
2674
|
+
}
|
|
2675
|
+
};
|
|
2676
|
+
const throttler = new _tanstack_pacer.Throttler(flushPending, {
|
|
2677
|
+
wait: effectiveMs,
|
|
2678
|
+
leading: true,
|
|
2679
|
+
trailing: true
|
|
2680
|
+
});
|
|
2681
|
+
const lifecycleOnly = {};
|
|
2682
|
+
if (subscriber.onRunInitialized) lifecycleOnly.onRunInitialized = subscriber.onRunInitialized;
|
|
2683
|
+
if (subscriber.onRunFinalized) lifecycleOnly.onRunFinalized = subscriber.onRunFinalized;
|
|
2684
|
+
if (subscriber.onRunFailed) lifecycleOnly.onRunFailed = subscriber.onRunFailed;
|
|
2685
|
+
if (subscriber.onRunErrorEvent) lifecycleOnly.onRunErrorEvent = subscriber.onRunErrorEvent;
|
|
2686
|
+
const wrappedSubscriber = guardAll(lifecycleOnly);
|
|
2687
|
+
if (subscriber.onMessagesChanged) wrappedSubscriber.onMessagesChanged = (params) => {
|
|
2688
|
+
latestMessagesParams = params;
|
|
2689
|
+
throttler.maybeExecute();
|
|
2690
|
+
};
|
|
2691
|
+
if (subscriber.onStateChanged) wrappedSubscriber.onStateChanged = (params) => {
|
|
2692
|
+
latestStateParams = params;
|
|
2693
|
+
throttler.maybeExecute();
|
|
2694
|
+
};
|
|
2695
|
+
const subscription = agent.subscribe(wrappedSubscriber);
|
|
2696
|
+
return { unsubscribe: () => {
|
|
2697
|
+
active = false;
|
|
2698
|
+
throttler.cancel();
|
|
2699
|
+
subscription.unsubscribe();
|
|
2700
|
+
} };
|
|
2701
|
+
}
|
|
2702
|
+
/**
|
|
2546
2703
|
* Agent connectivity (delegated to RunHandler)
|
|
2547
2704
|
*/
|
|
2548
2705
|
async connectAgent(params) {
|
|
@@ -3089,15 +3246,18 @@ const threadDomainEvents = createActionGroup("Thread Domain", {
|
|
|
3089
3246
|
threadUpserted: props(),
|
|
3090
3247
|
threadDeleted: props()
|
|
3091
3248
|
});
|
|
3092
|
-
function
|
|
3093
|
-
return [...threads].sort((left, right) =>
|
|
3249
|
+
function sortThreadsByRecency(threads) {
|
|
3250
|
+
return [...threads].sort((left, right) => {
|
|
3251
|
+
const leftKey = left.lastRunAt ?? left.updatedAt ?? left.createdAt;
|
|
3252
|
+
return (right.lastRunAt ?? right.updatedAt ?? right.createdAt).localeCompare(leftKey);
|
|
3253
|
+
});
|
|
3094
3254
|
}
|
|
3095
3255
|
function upsertThread(threads, thread) {
|
|
3096
3256
|
const existingIndex = threads.findIndex((item) => item.id === thread.id);
|
|
3097
|
-
if (existingIndex === -1) return
|
|
3257
|
+
if (existingIndex === -1) return sortThreadsByRecency([...threads, thread]);
|
|
3098
3258
|
const next = [...threads];
|
|
3099
3259
|
next[existingIndex] = thread;
|
|
3100
|
-
return
|
|
3260
|
+
return sortThreadsByRecency(next);
|
|
3101
3261
|
}
|
|
3102
3262
|
const threadReducer = createReducer(initialThreadState, on(threadAdapterEvents.contextChanged, (state, { context }) => ({
|
|
3103
3263
|
...state,
|
|
@@ -3130,7 +3290,7 @@ const threadReducer = createReducer(initialThreadState, on(threadAdapterEvents.c
|
|
|
3130
3290
|
if (sessionId !== state.sessionId) return state;
|
|
3131
3291
|
return {
|
|
3132
3292
|
...state,
|
|
3133
|
-
threads:
|
|
3293
|
+
threads: sortThreadsByRecency(threads),
|
|
3134
3294
|
isLoading: false,
|
|
3135
3295
|
error: null,
|
|
3136
3296
|
metadataJoinCode: joinCode,
|