@copilotkit/core 1.74.0 → 1.75.1

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
@@ -25,20 +25,23 @@ let zod_to_json_schema = require("zod-to-json-schema");
25
25
  *
26
26
  * `transformChunks` is still applied — message reassembly is needed either way.
27
27
  *
28
- * This mirrors the base `AbstractAgent.connectAgent` implementation exactly
29
- * apart from that omission, so callers keep the same subscriber notifications,
30
- * detach semantics, and `{ result, newMessages }` return shape.
28
+ * Connection-local replay hooks track the phase before applying events. An
29
+ * explicitly live RUN_ERROR clears busy without closing the connection;
30
+ * historical errors remain data. Connections without hooks retain the existing
31
+ * completion behavior. Subscriber, detach, and result contracts are preserved.
31
32
  *
32
33
  * TODO: Remove this in favour of the base implementation once AG-UI's
33
34
  * AbstractAgent supports opting out of `verifyEvents` for transports whose
34
- * connection life-cycle isn't a single run. As of `@ag-ui/client@0.0.57`
35
+ * connection life-cycle isn't a single run AND preserves the connection-local
36
+ * replay lifecycle and running-state behavior below. Skipping verification alone
37
+ * is insufficient. As of `@ag-ui/client@0.0.57`
35
38
  * `connectAgent(parameters?, subscriber?)` takes no such option.
36
39
  *
37
40
  * @param agent - The agent whose `connect()` stream should be consumed.
38
41
  * @param parameters - Run parameters, forwarded to `prepareRunAgentInput`.
39
42
  * @param subscriber - Optional one-shot subscriber for this connect call.
40
43
  */
41
- async function ɵconnectWithoutEventVerification(agent, parameters, subscriber) {
44
+ async function ɵconnectWithoutEventVerification(agent, parameters, subscriber, connect) {
42
45
  const self = agent;
43
46
  try {
44
47
  agent.isRunning = true;
@@ -46,10 +49,20 @@ async function ɵconnectWithoutEventVerification(agent, parameters, subscriber)
46
49
  const input = self.prepareRunAgentInput(parameters);
47
50
  let result;
48
51
  const previousMessageIds = new Set(agent.messages.map((m) => m.id));
52
+ let isReplaying = true;
53
+ const liveErrors = /* @__PURE__ */ new WeakSet();
49
54
  const subscribers = [
50
- { onRunFinishedEvent: (event) => {
51
- if (event.outcome === "success") result = event.result;
52
- } },
55
+ {
56
+ onRunStartedEvent: () => {
57
+ agent.isRunning = true;
58
+ },
59
+ onRunErrorEvent: ({ event }) => {
60
+ if (liveErrors.has(event)) agent.isRunning = false;
61
+ },
62
+ onRunFinishedEvent: (event) => {
63
+ if (event.outcome === "success") result = event.result;
64
+ }
65
+ },
53
66
  ...agent.subscribers,
54
67
  subscriber ?? {}
55
68
  ];
@@ -59,7 +72,18 @@ async function ɵconnectWithoutEventVerification(agent, parameters, subscriber)
59
72
  self.activeRunCompletionPromise = new Promise((resolve) => {
60
73
  resolveCompletion = resolve;
61
74
  });
62
- const source$ = (0, rxjs.defer)(() => self.connect(input)).pipe((0, _ag_ui_client.transformChunks)(self.debugLogger), (0, rxjs_operators.takeUntil)(self.activeRunDetach$));
75
+ const lifecycle = {
76
+ onReplayStarted: () => {
77
+ isReplaying = true;
78
+ agent.isRunning = true;
79
+ },
80
+ onReplayFinished: () => {
81
+ isReplaying = false;
82
+ }
83
+ };
84
+ const source$ = (0, rxjs.defer)(() => connect ? connect(input, lifecycle) : self.connect(input)).pipe((0, rxjs_operators.tap)((event) => {
85
+ if (!isReplaying && event.type === _ag_ui_client.EventType.RUN_ERROR) liveErrors.add(event);
86
+ }), (0, _ag_ui_client.transformChunks)(self.debugLogger), (0, rxjs_operators.takeUntil)(self.activeRunDetach$));
63
87
  const applied$ = self.apply(input, source$, subscribers);
64
88
  await (0, rxjs.lastValueFrom)(self.processApplyEvents(input, applied$, subscribers).pipe((0, rxjs_operators.catchError)((error) => {
65
89
  agent.isRunning = false;
@@ -206,9 +230,11 @@ function ɵobservePhoenixSocketHealth$(socketSignals$, maxConsecutiveErrors) {
206
230
  const globalFetch = (...args) => fetch(...args);
207
231
  const CLIENT_AG_UI_EVENT = "ag_ui_event";
208
232
  const REPLAY_COMPLETE_EVENT = "replay_complete";
233
+ const REPLAY_FAILED_EVENT = "replay_failed";
209
234
  const STREAM_IDLE_EVENT = "stream_idle";
210
235
  const STOP_RUN_EVENT = "stop_run";
211
236
  const CONNECT_STREAM_IDLE_REPLAY_FALLBACK_MS = 100;
237
+ const CONNECT_IDLE_WARNING_MS = 3e4;
212
238
  const MAX_UNOPENED_CREDENTIAL_REFRESHES = 2;
213
239
  var AgentThreadLockedError = class extends Error {
214
240
  constructor(threadId) {
@@ -296,7 +322,7 @@ var IntelligenceAgent = class IntelligenceAgent extends _ag_ui_client.AbstractAg
296
322
  ...parameters,
297
323
  runId: this.canonicalRunId
298
324
  };
299
- return ɵconnectWithoutEventVerification(this, effectiveParameters, subscriber);
325
+ return ɵconnectWithoutEventVerification(this, effectiveParameters, subscriber, (input, lifecycle) => this.connect(input, lifecycle));
300
326
  }
301
327
  abortRun() {
302
328
  if (this.activeChannel && this.canonicalRunId) {
@@ -345,17 +371,22 @@ var IntelligenceAgent = class IntelligenceAgent extends _ag_ui_client.AbstractAg
345
371
  * gateway only streams events past it instead of replaying the
346
372
  * entire history every time the chat re-opens a socket.
347
373
  */
348
- connect(input) {
374
+ connect(input, lifecycle) {
349
375
  this.threadId = input.threadId;
350
376
  this.canonicalRunId = null;
351
377
  const replayCursor = this.getReconnectCursor(input);
352
378
  return (0, rxjs.defer)(() => this.requestJoinCredentials$("connect", input, replayCursor)).pipe((0, rxjs.switchMap)((credentials) => {
353
- if (credentials === null) return rxjs.EMPTY;
379
+ if (credentials === null) {
380
+ lifecycle?.onReplayStarted?.();
381
+ lifecycle?.onReplayFinished?.();
382
+ return rxjs.EMPTY;
383
+ }
354
384
  const canonicalInput = this.applyCanonicalRunIdentity(input, credentials, { fallbackToInputRunId: false });
355
385
  return this.observeThread$(canonicalInput, credentials, {
356
386
  completeOnRunError: false,
357
387
  streamMode: "connect",
358
- replayCursor
388
+ replayCursor,
389
+ lifecycle
359
390
  });
360
391
  }));
361
392
  }
@@ -455,8 +486,24 @@ var IntelligenceAgent = class IntelligenceAgent extends _ag_ui_client.AbstractAg
455
486
  }
456
487
  observeThreadSession$(input, credentials, options) {
457
488
  return (0, rxjs.defer)(() => {
489
+ options.lifecycle?.onReplayStarted?.();
458
490
  let ownSocket = null;
459
491
  let ownChannel = null;
492
+ let replayFinished = false;
493
+ let warnedAboutIdle = false;
494
+ let idleWarning;
495
+ const clearIdleWarning = () => {
496
+ clearTimeout(idleWarning);
497
+ idleWarning = void 0;
498
+ };
499
+ const scheduleIdleWarning = () => {
500
+ clearIdleWarning();
501
+ if (options.streamMode !== "connect" || !replayFinished || warnedAboutIdle) return;
502
+ idleWarning = setTimeout(() => {
503
+ warnedAboutIdle = true;
504
+ this.debugLogger?.lifecycle("INTELLIGENCE", "No progress or stream_idle for 30 seconds after replay_complete; the run may still be active or its status unavailable.", { threadId: input.threadId });
505
+ }, CONNECT_IDLE_WARNING_MS);
506
+ };
460
507
  const socket$ = ɵphoenixSocket$({
461
508
  url: credentials.realtime.clientUrl,
462
509
  options: {
@@ -490,19 +537,42 @@ var IntelligenceAgent = class IntelligenceAgent extends _ag_ui_client.AbstractAg
490
537
  let latestObservedReplayCursor = null;
491
538
  const threadEvents$ = this.observeThreadEvents$(input.threadId, channel$, options).pipe((0, rxjs_operators.tap)((payload) => {
492
539
  latestObservedReplayCursor = this.readEventId(payload) ?? latestObservedReplayCursor;
540
+ scheduleIdleWarning();
493
541
  }), (0, rxjs_operators.share)());
494
- const replayComplete$ = this.observeControlEvent$(input.threadId, channel$, REPLAY_COMPLETE_EVENT).pipe((0, rxjs_operators.shareReplay)({
495
- bufferSize: 1,
496
- refCount: true
542
+ const controlCursor = {
543
+ applied: null,
544
+ replayFailed: false
545
+ };
546
+ const replayFailed$ = channel$.pipe((0, rxjs_operators.switchMap)(({ channel }) => this.observeChannelEvent$(channel, REPLAY_FAILED_EVENT)), (0, rxjs_operators.tap)(() => {
547
+ controlCursor.replayFailed = true;
497
548
  }));
498
- const streamIdle$ = this.observeControlEvent$(input.threadId, channel$, STREAM_IDLE_EVENT).pipe((0, rxjs_operators.shareReplay)({
549
+ const replayComplete$ = this.observeControlEvent$(input.threadId, channel$, REPLAY_COMPLETE_EVENT, controlCursor).pipe((0, rxjs_operators.tap)(() => {
550
+ replayFinished = true;
551
+ scheduleIdleWarning();
552
+ options.lifecycle?.onReplayFinished?.();
553
+ }), (0, rxjs_operators.share)());
554
+ const streamIdle$ = this.observeControlEvent$(input.threadId, channel$, STREAM_IDLE_EVENT, controlCursor).pipe((0, rxjs_operators.share)());
555
+ const replayRestart$ = (0, rxjs.merge)(this.observeControlEvent$(input.threadId, channel$, "phx_error", controlCursor), ɵobservePhoenixSocketSignals$(socket$).pipe((0, rxjs_operators.filter)((signal) => signal.type === "error"))).pipe((0, rxjs_operators.tap)(() => {
556
+ replayFinished = false;
557
+ warnedAboutIdle = false;
558
+ clearIdleWarning();
559
+ options.lifecycle?.onReplayStarted?.();
560
+ }), (0, rxjs_operators.share)());
561
+ const streamIdleCompletion$ = options.streamMode === "connect" ? replayRestart$.pipe((0, rxjs_operators.startWith)(null), (0, rxjs.switchMap)(() => (0, rxjs.merge)((0, rxjs.combineLatest)([replayComplete$.pipe((0, rxjs_operators.take)(1)), streamIdle$.pipe((0, rxjs_operators.take)(1))]), streamIdle$.pipe((0, rxjs_operators.take)(1), (0, rxjs_operators.filter)((payload) => this.canFallbackCompleteConnect(payload, reconnectCursor, latestObservedReplayCursor)), (0, rxjs_operators.delay)(CONNECT_STREAM_IDLE_REPLAY_FALLBACK_MS), (0, rxjs_operators.tap)(() => {
562
+ this.debugLogger?.lifecycle("INTELLIGENCE", "Completing connection on stream_idle fallback without replay_complete.", {
563
+ threadId: input.threadId,
564
+ fallbackMs: CONNECT_STREAM_IDLE_REPLAY_FALLBACK_MS
565
+ });
566
+ })))), (0, rxjs_operators.take)(1), (0, rxjs_operators.shareReplay)({
499
567
  bufferSize: 1,
500
568
  refCount: true
501
- }));
502
- const streamIdleCompletion$ = options.streamMode === "connect" ? (0, rxjs.merge)((0, rxjs.combineLatest)([replayComplete$.pipe((0, rxjs_operators.take)(1)), streamIdle$.pipe((0, rxjs_operators.take)(1))]), streamIdle$.pipe((0, rxjs_operators.take)(1), (0, rxjs_operators.filter)((payload) => this.canFallbackCompleteConnect(payload, reconnectCursor, latestObservedReplayCursor)), (0, rxjs_operators.delay)(CONNECT_STREAM_IDLE_REPLAY_FALLBACK_MS))).pipe((0, rxjs_operators.take)(1)) : rxjs.EMPTY;
569
+ })) : rxjs.EMPTY;
503
570
  const threadCompleted$ = threadEvents$.pipe((0, rxjs_operators.ignoreElements)(), (0, rxjs_operators.endWith)(null), (0, rxjs_operators.take)(1));
504
571
  const terminal$ = (0, rxjs.merge)(threadCompleted$, streamIdleCompletion$);
505
- return (0, rxjs.merge)(this.joinThreadChannel$(channel$), this.observeSocketHealth$(socket$, options.onSocketOpen).pipe((0, rxjs_operators.takeUntil)(terminal$)), threadEvents$.pipe((0, rxjs_operators.takeUntil)(streamIdleCompletion$)), replayComplete$.pipe((0, rxjs_operators.ignoreElements)(), (0, rxjs_operators.takeUntil)(terminal$)), streamIdleCompletion$.pipe((0, rxjs_operators.ignoreElements)(), (0, rxjs_operators.takeUntil)(threadCompleted$))).pipe((0, rxjs_operators.finalize)(() => this.cleanupOwned(ownChannel, ownSocket)));
572
+ return (0, rxjs.merge)(replayFailed$.pipe((0, rxjs_operators.ignoreElements)(), (0, rxjs_operators.takeUntil)(terminal$)), replayComplete$.pipe((0, rxjs_operators.ignoreElements)(), (0, rxjs_operators.takeUntil)(terminal$)), this.joinThreadChannel$(channel$), this.observeSocketHealth$(socket$, options.onSocketOpen).pipe((0, rxjs_operators.takeUntil)(terminal$)), threadEvents$.pipe((0, rxjs_operators.takeUntil)(streamIdleCompletion$)), streamIdleCompletion$.pipe((0, rxjs_operators.ignoreElements)(), (0, rxjs_operators.takeUntil)(threadCompleted$))).pipe((0, rxjs_operators.finalize)(() => {
573
+ clearIdleWarning();
574
+ this.cleanupOwned(ownChannel, ownSocket);
575
+ }));
506
576
  });
507
577
  }
508
578
  joinThreadChannel$(channel$) {
@@ -522,8 +592,8 @@ var IntelligenceAgent = class IntelligenceAgent extends _ag_ui_client.AbstractAg
522
592
  errorOnRunError: options.streamMode === "run"
523
593
  })), (0, rxjs.dematerialize)());
524
594
  }
525
- observeControlEvent$(threadId, channel$, eventName) {
526
- return channel$.pipe((0, rxjs_operators.switchMap)(({ channel }) => this.observeChannelEvent$(channel, eventName)), (0, rxjs_operators.tap)((payload) => this.updateLastSeenEventIdFromControl(threadId, payload)));
595
+ observeControlEvent$(threadId, channel$, eventName, controlCursor) {
596
+ return channel$.pipe((0, rxjs_operators.switchMap)(({ channel }) => this.observeChannelEvent$(channel, eventName)), (0, rxjs_operators.tap)((payload) => this.updateLastSeenEventIdFromControl(threadId, payload, controlCursor)));
527
597
  }
528
598
  observeChannelEvent$(channel, eventName) {
529
599
  return ɵobservePhoenixEvent$(channel, eventName);
@@ -572,9 +642,11 @@ var IntelligenceAgent = class IntelligenceAgent extends _ag_ui_client.AbstractAg
572
642
  if (!eventId) return;
573
643
  this.advanceLastSeenEventId(threadId, eventId);
574
644
  }
575
- updateLastSeenEventIdFromControl(threadId, payload) {
645
+ updateLastSeenEventIdFromControl(threadId, payload, controlCursor) {
576
646
  const eventId = this.readControlEventId(payload);
577
647
  if (!eventId) return;
648
+ if (eventId === controlCursor.applied && !controlCursor.replayFailed) return;
649
+ controlCursor.applied = eventId;
578
650
  this.advanceLastSeenEventId(threadId, eventId);
579
651
  }
580
652
  advanceLastSeenEventId(threadId, eventId) {
@@ -982,7 +1054,7 @@ var ProxiedCopilotRuntimeAgent = class ProxiedCopilotRuntimeAgent extends _ag_ui
982
1054
  });
983
1055
  }
984
1056
  async connectAgent(parameters, subscriber) {
985
- if (this.runtimeMode !== _copilotkit_shared.RUNTIME_MODE_INTELLIGENCE) return ɵconnectWithoutEventVerification(this, parameters, subscriber);
1057
+ if (this.runtimeMode !== _copilotkit_shared.RUNTIME_MODE_INTELLIGENCE) return ɵconnectWithoutEventVerification(this, parameters, subscriber, (input, lifecycle) => this.connect(input, lifecycle));
986
1058
  if (this.delegate) await this.delegate.detachActiveRun();
987
1059
  await this.resolveDelegate();
988
1060
  const delegate = this.delegate;
@@ -1003,7 +1075,10 @@ var ProxiedCopilotRuntimeAgent = class ProxiedCopilotRuntimeAgent extends _ag_ui
1003
1075
  this.isRunning = false;
1004
1076
  },
1005
1077
  onRunErrorEvent: () => {
1006
- this.isRunning = false;
1078
+ this.isRunning = delegate.isRunning;
1079
+ },
1080
+ onRunStartedEvent: () => {
1081
+ this.isRunning = delegate.isRunning;
1007
1082
  }
1008
1083
  });
1009
1084
  const forwardedSubs = this.subscribers.map((s) => delegate.subscribe(s));
@@ -1018,9 +1093,9 @@ var ProxiedCopilotRuntimeAgent = class ProxiedCopilotRuntimeAgent extends _ag_ui
1018
1093
  for (const sub of forwardedSubs) sub.unsubscribe();
1019
1094
  }
1020
1095
  }
1021
- connect(input) {
1022
- if (this.runtimeMode === "pending" || this.transport === "auto" && this.runtimeMode !== _copilotkit_shared.RUNTIME_MODE_INTELLIGENCE) return (0, rxjs.defer)(() => (0, rxjs.from)(this.ensureRuntimeConfiguration())).pipe((0, rxjs_operators.switchMap)(() => this.connect(input)));
1023
- if (this.runtimeMode === _copilotkit_shared.RUNTIME_MODE_INTELLIGENCE) return this.#connectViaDelegate(input);
1096
+ connect(input, lifecycle) {
1097
+ if (this.runtimeMode === "pending" || this.transport === "auto" && this.runtimeMode !== _copilotkit_shared.RUNTIME_MODE_INTELLIGENCE) return (0, rxjs.defer)(() => (0, rxjs.from)(this.ensureRuntimeConfiguration())).pipe((0, rxjs_operators.switchMap)(() => this.connect(input, lifecycle)));
1098
+ if (this.runtimeMode === _copilotkit_shared.RUNTIME_MODE_INTELLIGENCE) return this.#connectViaDelegate(input, lifecycle);
1024
1099
  return this.#connectViaHttp(input);
1025
1100
  }
1026
1101
  run(input) {
@@ -1028,8 +1103,8 @@ var ProxiedCopilotRuntimeAgent = class ProxiedCopilotRuntimeAgent extends _ag_ui
1028
1103
  if (this.runtimeMode === _copilotkit_shared.RUNTIME_MODE_INTELLIGENCE) return this.#runViaDelegate(input);
1029
1104
  return this.#runViaHttp(input);
1030
1105
  }
1031
- #connectViaDelegate(input) {
1032
- return (0, rxjs.defer)(() => (0, rxjs.from)(this.resolveDelegate())).pipe((0, rxjs_operators.switchMap)((delegate) => withAbortErrorHandling(delegate.connect(input))));
1106
+ #connectViaDelegate(input, lifecycle) {
1107
+ return (0, rxjs.defer)(() => (0, rxjs.from)(this.resolveDelegate())).pipe((0, rxjs_operators.switchMap)((delegate) => withAbortErrorHandling(delegate.connect(input, lifecycle))));
1033
1108
  }
1034
1109
  #connectViaHttp(unfiltered) {
1035
1110
  const input = this.#applyMessageFilter(unfiltered);
@@ -1223,6 +1298,7 @@ var ProxiedCopilotRuntimeAgent = class ProxiedCopilotRuntimeAgent extends _ag_ui
1223
1298
  delegate.agentId = this.routedAgentId();
1224
1299
  delegate.description = this.description;
1225
1300
  delegate.threadId = this.threadId;
1301
+ delegate.debugLogger = this.debugLogger;
1226
1302
  delegate.setMessages(this.messages);
1227
1303
  delegate.setState(this.state);
1228
1304
  if (hasHeaders(delegate)) delegate.headers = { ...this.headers };
@@ -5394,6 +5470,7 @@ const ALLOWED_KEYS = new Set([
5394
5470
  "onMessagesChanged",
5395
5471
  "onStateChanged",
5396
5472
  "onRunInitialized",
5473
+ "onRunStartedEvent",
5397
5474
  "onRunFinalized",
5398
5475
  "onRunFailed",
5399
5476
  "onRunErrorEvent"
@@ -5995,6 +6072,10 @@ var CopilotKitCore = class {
5995
6072
  const fn = sub.onRunInitialized;
5996
6073
  guarded.onRunInitialized = (params) => safeCall("onRunInitialized", fn, params);
5997
6074
  }
6075
+ if (sub.onRunStartedEvent) {
6076
+ const fn = sub.onRunStartedEvent;
6077
+ guarded.onRunStartedEvent = (params) => safeCall("onRunStartedEvent", fn, params);
6078
+ }
5998
6079
  if (sub.onRunFinalized) {
5999
6080
  const fn = sub.onRunFinalized;
6000
6081
  guarded.onRunFinalized = (params) => safeCall("onRunFinalized", fn, params);
@@ -6046,6 +6127,7 @@ var CopilotKitCore = class {
6046
6127
  });
6047
6128
  const lifecycleOnly = {};
6048
6129
  if (subscriber.onRunInitialized) lifecycleOnly.onRunInitialized = subscriber.onRunInitialized;
6130
+ if (subscriber.onRunStartedEvent) lifecycleOnly.onRunStartedEvent = subscriber.onRunStartedEvent;
6049
6131
  if (subscriber.onRunFinalized) lifecycleOnly.onRunFinalized = subscriber.onRunFinalized;
6050
6132
  if (subscriber.onRunFailed) lifecycleOnly.onRunFailed = subscriber.onRunFailed;
6051
6133
  if (subscriber.onRunErrorEvent) lifecycleOnly.onRunErrorEvent = subscriber.onRunErrorEvent;