@copilotkit/core 1.56.1 → 1.56.2-canary.pin-to-send

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 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");
@@ -138,7 +139,7 @@ var IntelligenceAgent = class IntelligenceAgent extends _ag_ui_client.AbstractAg
138
139
  config;
139
140
  socket = null;
140
141
  activeChannel = null;
141
- runId = null;
142
+ canonicalRunId = null;
142
143
  sharedState;
143
144
  constructor(config, sharedState = { lastSeenEventIds: /* @__PURE__ */ new Map() }) {
144
145
  super();
@@ -176,7 +177,11 @@ var IntelligenceAgent = class IntelligenceAgent extends _ag_ui_client.AbstractAg
176
177
  try {
177
178
  this.isRunning = true;
178
179
  this.agentId = this.agentId ?? (0, _ag_ui_client.randomUUID)();
179
- const input = this.prepareRunAgentInput(parameters);
180
+ const effectiveParameters = parameters?.runId || !this.canonicalRunId ? parameters : {
181
+ ...parameters,
182
+ runId: this.canonicalRunId
183
+ };
184
+ const input = this.prepareRunAgentInput(effectiveParameters);
180
185
  let result;
181
186
  const previousMessageIds = new Set(this.messages.map((m) => m.id));
182
187
  const subscribers = [
@@ -215,14 +220,14 @@ var IntelligenceAgent = class IntelligenceAgent extends _ag_ui_client.AbstractAg
215
220
  }
216
221
  }
217
222
  abortRun() {
218
- if (this.activeChannel && this.runId) {
223
+ if (this.activeChannel && this.canonicalRunId) {
219
224
  const fallback = setTimeout(() => clear(), 5e3);
220
225
  const clear = () => {
221
226
  clearTimeout(fallback);
222
227
  this.detachActiveRun();
223
228
  this.cleanup();
224
229
  };
225
- this.activeChannel.push(STOP_RUN_EVENT, { run_id: this.runId }).receive("ok", clear).receive("error", clear).receive("timeout", clear);
230
+ this.activeChannel.push(STOP_RUN_EVENT, { run_id: this.canonicalRunId }).receive("ok", clear).receive("error", clear).receive("timeout", clear);
226
231
  } else {
227
232
  this.detachActiveRun();
228
233
  this.cleanup();
@@ -234,7 +239,7 @@ var IntelligenceAgent = class IntelligenceAgent extends _ag_ui_client.AbstractAg
234
239
  */
235
240
  run(input) {
236
241
  this.threadId = input.threadId;
237
- this.runId = input.runId;
242
+ this.canonicalRunId = input.runId;
238
243
  return (0, rxjs.defer)(() => this.requestJoinCredentials$("run", input)).pipe((0, rxjs.switchMap)((credentials) => this.observeThread$(input, credentials, {
239
244
  completeOnRunError: false,
240
245
  streamMode: "run"
@@ -246,16 +251,14 @@ var IntelligenceAgent = class IntelligenceAgent extends _ag_ui_client.AbstractAg
246
251
  */
247
252
  connect(input) {
248
253
  this.threadId = input.threadId;
249
- this.runId = input.runId;
254
+ this.canonicalRunId = input.runId;
250
255
  return (0, rxjs.defer)(() => this.requestConnectPlan$(input)).pipe((0, rxjs.switchMap)((plan) => {
251
256
  if (plan === null) return rxjs.EMPTY;
252
257
  if (plan.mode === "bootstrap") {
253
258
  this.setLastSeenEventId(input.threadId, plan.latestEventId);
254
- for (const event of plan.events) this.updateRunIdFromEvent(event);
255
259
  return (0, rxjs.from)(plan.events);
256
260
  }
257
261
  this.setLastSeenEventId(input.threadId, plan.joinFromEventId);
258
- for (const event of plan.events) this.updateRunIdFromEvent(event);
259
262
  return (0, rxjs.concat)((0, rxjs.from)(plan.events), this.observeThread$(input, { joinToken: plan.joinToken }, {
260
263
  completeOnRunError: true,
261
264
  streamMode: "connect",
@@ -278,7 +281,7 @@ var IntelligenceAgent = class IntelligenceAgent extends _ag_ui_client.AbstractAg
278
281
  if (this.socket === ownSocket) this.socket = null;
279
282
  }
280
283
  if (this.threadId) this.sharedState.lastSeenEventIds.delete(this.threadId);
281
- this.runId = null;
284
+ this.canonicalRunId = null;
282
285
  }
283
286
  cleanup() {
284
287
  this.cleanupOwned(this.activeChannel, this.socket);
@@ -414,7 +417,6 @@ var IntelligenceAgent = class IntelligenceAgent extends _ag_ui_client.AbstractAg
414
417
  observeThreadEvents$(threadId, channel$, options) {
415
418
  return channel$.pipe((0, rxjs_operators.switchMap)(({ channel }) => this.observeChannelEvent$(channel, CLIENT_AG_UI_EVENT)), (0, rxjs_operators.tap)((payload) => {
416
419
  this.updateLastSeenEventId(threadId, payload);
417
- this.updateRunIdFromEvent(payload);
418
420
  }), (0, rxjs_operators.mergeMap)((payload) => (0, rxjs.from)(this.createThreadNotifications(payload, options.completeOnRunError))), (0, rxjs.dematerialize)());
419
421
  }
420
422
  observeChannelEvent$(channel, eventName) {
@@ -464,23 +466,6 @@ var IntelligenceAgent = class IntelligenceAgent extends _ag_ui_client.AbstractAg
464
466
  if (!eventId) return;
465
467
  this.sharedState.lastSeenEventIds.set(threadId, eventId);
466
468
  }
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
469
  readEventId(payload) {
485
470
  const metadata = payload.metadata;
486
471
  if (!metadata || typeof metadata !== "object") return null;
@@ -2309,8 +2294,21 @@ let CopilotKitCoreErrorCode = /* @__PURE__ */ function(CopilotKitCoreErrorCode)
2309
2294
  CopilotKitCoreErrorCode["TRANSCRIPTION_RATE_LIMITED"] = "transcription_rate_limited";
2310
2295
  CopilotKitCoreErrorCode["TRANSCRIPTION_AUTH_FAILED"] = "transcription_auth_failed";
2311
2296
  CopilotKitCoreErrorCode["TRANSCRIPTION_NETWORK_ERROR"] = "transcription_network_error";
2297
+ CopilotKitCoreErrorCode["SUBSCRIBER_CALLBACK_FAILED"] = "subscriber_callback_failed";
2312
2298
  return CopilotKitCoreErrorCode;
2313
2299
  }({});
2300
+ /**
2301
+ * Runtime allowlist derived from {@link SUBSCRIBE_TO_AGENT_KEYS}. Hoisted
2302
+ * to module scope so the Set is allocated once, not per-subscription.
2303
+ */
2304
+ const ALLOWED_KEYS = new Set([
2305
+ "onMessagesChanged",
2306
+ "onStateChanged",
2307
+ "onRunInitialized",
2308
+ "onRunFinalized",
2309
+ "onRunFailed",
2310
+ "onRunErrorEvent"
2311
+ ]);
2314
2312
  let CopilotKitCoreRuntimeConnectionStatus = /* @__PURE__ */ function(CopilotKitCoreRuntimeConnectionStatus) {
2315
2313
  CopilotKitCoreRuntimeConnectionStatus["Disconnected"] = "disconnected";
2316
2314
  CopilotKitCoreRuntimeConnectionStatus["Connected"] = "connected";
@@ -2376,6 +2374,16 @@ var CopilotKitCore = class {
2376
2374
  }), "Subscriber onError error:");
2377
2375
  }
2378
2376
  /**
2377
+ * Log a message to the console and emit an error to subscribers.
2378
+ * Catches failures from `emitError` itself to prevent unhandled rejections.
2379
+ */
2380
+ logAndEmitError(message, params, logLevel = "error") {
2381
+ console[logLevel](message, params.error);
2382
+ this.emitError(params).catch((emitErr) => {
2383
+ console.error(message + " — emitError itself failed:", emitErr);
2384
+ });
2385
+ }
2386
+ /**
2379
2387
  * Snapshot accessors
2380
2388
  */
2381
2389
  get context() {
@@ -2412,17 +2420,30 @@ var CopilotKitCore = class {
2412
2420
  return this._properties;
2413
2421
  }
2414
2422
  /**
2415
- * Default throttle interval (ms) applied by framework hooks (e.g.
2416
- * `useAgent()`) when the hook/component does not specify an explicit
2417
- * `throttleMs`. An explicit `0` passed as `throttleMs` to `useAgent()`
2418
- * or `<CopilotChat>` overrides this default and disables throttling.
2423
+ * Default throttle interval (ms) used by `subscribeToAgentWithOptions()`
2424
+ * when the caller does not specify an explicit `throttleMs`.
2425
+ * `undefined` means no default is configured; `0` means no throttling.
2419
2426
  */
2420
2427
  get defaultThrottleMs() {
2421
2428
  return this._defaultThrottleMs;
2422
2429
  }
2430
+ /**
2431
+ * Set the default throttle interval (ms) for `subscribeToAgentWithOptions()`.
2432
+ *
2433
+ * Accepts a non-negative finite number or `undefined` (to clear the
2434
+ * default). Invalid values (NaN, Infinity, negative) are logged as
2435
+ * errors and ignored — the previous valid value is preserved.
2436
+ */
2423
2437
  setDefaultThrottleMs(value) {
2424
2438
  if (value !== void 0 && (!Number.isFinite(value) || value < 0)) {
2425
- this._defaultThrottleMs = void 0;
2439
+ this.logAndEmitError(`CopilotKitCore.setDefaultThrottleMs: value must be a non-negative finite number or undefined, got ${value}. Keeping current value (${this._defaultThrottleMs}).`, {
2440
+ error: /* @__PURE__ */ new Error(`setDefaultThrottleMs: invalid value (${value}), keeping current value (${this._defaultThrottleMs})`),
2441
+ code: CopilotKitCoreErrorCode.SUBSCRIBER_CALLBACK_FAILED,
2442
+ context: {
2443
+ value,
2444
+ currentValue: this._defaultThrottleMs
2445
+ }
2446
+ });
2426
2447
  return;
2427
2448
  }
2428
2449
  this._defaultThrottleMs = value;
@@ -2543,6 +2564,137 @@ var CopilotKitCore = class {
2543
2564
  } };
2544
2565
  }
2545
2566
  /**
2567
+ * Subscribe to an agent's notification and lifecycle events with
2568
+ * optional configuration (e.g. throttling).
2569
+ *
2570
+ * Wraps every callback with error protection (`safeCall`) and applies
2571
+ * the options before delegating to `agent.subscribe()`.
2572
+ *
2573
+ * See {@link SubscribeToAgentSubscriber} for the accepted callback subset
2574
+ * and the rationale for excluding AG-UI event handlers.
2575
+ */
2576
+ subscribeToAgentWithOptions(agent, subscriber, options) {
2577
+ const resolved = options?.throttleMs ?? this._defaultThrottleMs ?? 0;
2578
+ let effectiveMs = 0;
2579
+ if (!Number.isFinite(resolved) || resolved < 0) {
2580
+ const source = options?.throttleMs !== void 0 ? "throttleMs" : "defaultThrottleMs";
2581
+ this.logAndEmitError(`CopilotKitCore.subscribeToAgentWithOptions: ${source} must be a non-negative finite number, got ${resolved}. Falling back to unthrottled.`, {
2582
+ error: /* @__PURE__ */ new Error(`subscribeToAgentWithOptions: invalid ${source} (${resolved}), falling back to unthrottled`),
2583
+ code: CopilotKitCoreErrorCode.SUBSCRIBER_CALLBACK_FAILED,
2584
+ context: {
2585
+ agentId: agent.agentId,
2586
+ source,
2587
+ value: resolved
2588
+ }
2589
+ });
2590
+ } else effectiveMs = resolved;
2591
+ const agentLabel = agent.agentId || "(unknown agent)";
2592
+ const safeCall = (label, fn, ...args) => {
2593
+ const reportError = (err, verb) => {
2594
+ this.logAndEmitError(`CopilotKitCore.subscribeToAgentWithOptions[${agentLabel}]: ${label} callback ${verb}:`, {
2595
+ error: err instanceof Error ? err : new Error(String(err)),
2596
+ code: CopilotKitCoreErrorCode.SUBSCRIBER_CALLBACK_FAILED,
2597
+ context: {
2598
+ agentId: agent.agentId,
2599
+ callback: label
2600
+ }
2601
+ });
2602
+ };
2603
+ try {
2604
+ const result = fn(...args);
2605
+ if (result instanceof Promise) return result.catch((err) => {
2606
+ reportError(err, "rejected");
2607
+ });
2608
+ return result;
2609
+ } catch (err) {
2610
+ reportError(err, "threw");
2611
+ }
2612
+ };
2613
+ const guardAll = (sub) => {
2614
+ const guarded = {};
2615
+ if (sub.onMessagesChanged) {
2616
+ const fn = sub.onMessagesChanged;
2617
+ guarded.onMessagesChanged = (params) => safeCall("onMessagesChanged", fn, params);
2618
+ }
2619
+ if (sub.onStateChanged) {
2620
+ const fn = sub.onStateChanged;
2621
+ guarded.onStateChanged = (params) => safeCall("onStateChanged", fn, params);
2622
+ }
2623
+ if (sub.onRunInitialized) {
2624
+ const fn = sub.onRunInitialized;
2625
+ guarded.onRunInitialized = (params) => safeCall("onRunInitialized", fn, params);
2626
+ }
2627
+ if (sub.onRunFinalized) {
2628
+ const fn = sub.onRunFinalized;
2629
+ guarded.onRunFinalized = (params) => safeCall("onRunFinalized", fn, params);
2630
+ }
2631
+ if (sub.onRunFailed) {
2632
+ const fn = sub.onRunFailed;
2633
+ guarded.onRunFailed = (params) => safeCall("onRunFailed", fn, params);
2634
+ }
2635
+ if (sub.onRunErrorEvent) {
2636
+ const fn = sub.onRunErrorEvent;
2637
+ guarded.onRunErrorEvent = (params) => safeCall("onRunErrorEvent", fn, params);
2638
+ }
2639
+ return guarded;
2640
+ };
2641
+ for (const key of Object.keys(subscriber)) if (typeof subscriber[key] === "function" && !ALLOWED_KEYS.has(key)) {
2642
+ 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.`;
2643
+ this.logAndEmitError(message, {
2644
+ error: new Error(message),
2645
+ code: CopilotKitCoreErrorCode.SUBSCRIBER_CALLBACK_FAILED,
2646
+ context: {
2647
+ agentId: agent.agentId,
2648
+ droppedCallback: key
2649
+ }
2650
+ }, "warn");
2651
+ }
2652
+ if (effectiveMs <= 0) {
2653
+ const subscription = agent.subscribe(guardAll(subscriber));
2654
+ return { unsubscribe: () => subscription.unsubscribe() };
2655
+ }
2656
+ let active = true;
2657
+ let latestMessagesParams = null;
2658
+ let latestStateParams = null;
2659
+ const flushPending = () => {
2660
+ if (active && subscriber.onMessagesChanged && latestMessagesParams) {
2661
+ const params = latestMessagesParams;
2662
+ latestMessagesParams = null;
2663
+ safeCall("onMessagesChanged", subscriber.onMessagesChanged, params);
2664
+ }
2665
+ if (active && subscriber.onStateChanged && latestStateParams) {
2666
+ const params = latestStateParams;
2667
+ latestStateParams = null;
2668
+ safeCall("onStateChanged", subscriber.onStateChanged, params);
2669
+ }
2670
+ };
2671
+ const throttler = new _tanstack_pacer.Throttler(flushPending, {
2672
+ wait: effectiveMs,
2673
+ leading: true,
2674
+ trailing: true
2675
+ });
2676
+ const lifecycleOnly = {};
2677
+ if (subscriber.onRunInitialized) lifecycleOnly.onRunInitialized = subscriber.onRunInitialized;
2678
+ if (subscriber.onRunFinalized) lifecycleOnly.onRunFinalized = subscriber.onRunFinalized;
2679
+ if (subscriber.onRunFailed) lifecycleOnly.onRunFailed = subscriber.onRunFailed;
2680
+ if (subscriber.onRunErrorEvent) lifecycleOnly.onRunErrorEvent = subscriber.onRunErrorEvent;
2681
+ const wrappedSubscriber = guardAll(lifecycleOnly);
2682
+ if (subscriber.onMessagesChanged) wrappedSubscriber.onMessagesChanged = (params) => {
2683
+ latestMessagesParams = params;
2684
+ throttler.maybeExecute();
2685
+ };
2686
+ if (subscriber.onStateChanged) wrappedSubscriber.onStateChanged = (params) => {
2687
+ latestStateParams = params;
2688
+ throttler.maybeExecute();
2689
+ };
2690
+ const subscription = agent.subscribe(wrappedSubscriber);
2691
+ return { unsubscribe: () => {
2692
+ active = false;
2693
+ throttler.cancel();
2694
+ subscription.unsubscribe();
2695
+ } };
2696
+ }
2697
+ /**
2546
2698
  * Agent connectivity (delegated to RunHandler)
2547
2699
  */
2548
2700
  async connectAgent(params) {
@@ -3089,15 +3241,18 @@ const threadDomainEvents = createActionGroup("Thread Domain", {
3089
3241
  threadUpserted: props(),
3090
3242
  threadDeleted: props()
3091
3243
  });
3092
- function sortThreadsByUpdatedAt(threads) {
3093
- return [...threads].sort((left, right) => right.updatedAt.localeCompare(left.updatedAt));
3244
+ function sortThreadsByRecency(threads) {
3245
+ return [...threads].sort((left, right) => {
3246
+ const leftKey = left.lastRunAt ?? left.updatedAt ?? left.createdAt;
3247
+ return (right.lastRunAt ?? right.updatedAt ?? right.createdAt).localeCompare(leftKey);
3248
+ });
3094
3249
  }
3095
3250
  function upsertThread(threads, thread) {
3096
3251
  const existingIndex = threads.findIndex((item) => item.id === thread.id);
3097
- if (existingIndex === -1) return sortThreadsByUpdatedAt([...threads, thread]);
3252
+ if (existingIndex === -1) return sortThreadsByRecency([...threads, thread]);
3098
3253
  const next = [...threads];
3099
3254
  next[existingIndex] = thread;
3100
- return sortThreadsByUpdatedAt(next);
3255
+ return sortThreadsByRecency(next);
3101
3256
  }
3102
3257
  const threadReducer = createReducer(initialThreadState, on(threadAdapterEvents.contextChanged, (state, { context }) => ({
3103
3258
  ...state,
@@ -3130,7 +3285,7 @@ const threadReducer = createReducer(initialThreadState, on(threadAdapterEvents.c
3130
3285
  if (sessionId !== state.sessionId) return state;
3131
3286
  return {
3132
3287
  ...state,
3133
- threads: sortThreadsByUpdatedAt(threads),
3288
+ threads: sortThreadsByRecency(threads),
3134
3289
  isLoading: false,
3135
3290
  error: null,
3136
3291
  metadataJoinCode: joinCode,