@copilotkit/react-core 1.63.1 → 1.63.2

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.
@@ -345,11 +345,14 @@ function useAgent({ agentId, updates, throttleMs } = {}) {
345
345
  const [, forceUpdate] = (0, react.useReducer)((x) => x + 1, 0);
346
346
  const updateFlags = (0, react.useMemo)(() => updates ?? ALL_UPDATES, [JSON.stringify(updates)]);
347
347
  const provisionalAgentCache = (0, react.useRef)(/* @__PURE__ */ new Map());
348
- const agent = (0, react.useMemo)(() => {
348
+ const { agent, isReady } = (0, react.useMemo)(() => {
349
349
  const existing = copilotkit.getAgent(resolvedAgentId);
350
350
  if (existing) {
351
351
  provisionalAgentCache.current.delete(resolvedAgentId);
352
- return existing;
352
+ return {
353
+ agent: existing,
354
+ isReady: true
355
+ };
353
356
  }
354
357
  const isRuntimeConfigured = copilotkit.runtimeUrl !== void 0;
355
358
  const status = copilotkit.runtimeConnectionStatus;
@@ -357,7 +360,10 @@ function useAgent({ agentId, updates, throttleMs } = {}) {
357
360
  const cached = provisionalAgentCache.current.get(resolvedAgentId);
358
361
  if (cached) {
359
362
  copilotkit.applyHeadersToAgent(cached);
360
- return cached;
363
+ return {
364
+ agent: cached,
365
+ isReady: false
366
+ };
361
367
  }
362
368
  const provisional = new _copilotkit_core.ProxiedCopilotRuntimeAgent({
363
369
  runtimeUrl: copilotkit.runtimeUrl,
@@ -367,13 +373,19 @@ function useAgent({ agentId, updates, throttleMs } = {}) {
367
373
  });
368
374
  copilotkit.applyHeadersToAgent(provisional);
369
375
  provisionalAgentCache.current.set(resolvedAgentId, provisional);
370
- return provisional;
376
+ return {
377
+ agent: provisional,
378
+ isReady: false
379
+ };
371
380
  }
372
381
  if (isRuntimeConfigured && status === _copilotkit_core.CopilotKitCoreRuntimeConnectionStatus.Error) {
373
382
  const cached = provisionalAgentCache.current.get(resolvedAgentId);
374
383
  if (cached) {
375
384
  copilotkit.applyHeadersToAgent(cached);
376
- return cached;
385
+ return {
386
+ agent: cached,
387
+ isReady: false
388
+ };
377
389
  }
378
390
  const provisional = new _copilotkit_core.ProxiedCopilotRuntimeAgent({
379
391
  runtimeUrl: copilotkit.runtimeUrl,
@@ -383,7 +395,10 @@ function useAgent({ agentId, updates, throttleMs } = {}) {
383
395
  });
384
396
  copilotkit.applyHeadersToAgent(provisional);
385
397
  provisionalAgentCache.current.set(resolvedAgentId, provisional);
386
- return provisional;
398
+ return {
399
+ agent: provisional,
400
+ isReady: false
401
+ };
387
402
  }
388
403
  const knownAgents = Object.keys(copilotkit.agents ?? {});
389
404
  const runtimePart = isRuntimeConfigured ? `runtimeUrl=${copilotkit.runtimeUrl}` : "no runtimeUrl";
@@ -444,7 +459,10 @@ function useAgent({ agentId, updates, throttleMs } = {}) {
444
459
  configThreadId,
445
460
  configHasExplicitThreadId
446
461
  ]);
447
- return { agent };
462
+ return {
463
+ agent,
464
+ isReady
465
+ };
448
466
  }
449
467
 
450
468
  //#endregion
@@ -685,8 +703,9 @@ function useInterrupt(config) {
685
703
  const pendingRef = (0, react.useRef)(pending);
686
704
  pendingRef.current = pending;
687
705
  const [handlerResult, setHandlerResult] = (0, react.useState)(null);
688
- const responsesRef = (0, react.useRef)({});
706
+ const interruptStateRef = (0, react.useRef)(new _copilotkit_core.ɵInterruptState());
689
707
  (0, react.useEffect)(() => {
708
+ const interruptState = interruptStateRef.current;
690
709
  let localLegacy = null;
691
710
  let localStandard = null;
692
711
  const subscription = agent.subscribe({
@@ -702,106 +721,111 @@ function useInterrupt(config) {
702
721
  onRunStartedEvent: () => {
703
722
  localLegacy = null;
704
723
  localStandard = null;
705
- responsesRef.current = {};
724
+ interruptState.clear();
706
725
  setPending(null);
707
726
  },
708
727
  onRunFinalized: () => {
709
- if (localStandard && localStandard.length > 0) setPending({
710
- kind: "standard",
711
- interrupts: localStandard
712
- });
713
- else if (localLegacy) setPending({
714
- kind: "legacy",
715
- event: localLegacy
716
- });
728
+ if (localStandard && localStandard.length > 0) {
729
+ interruptState.setStandard(localStandard);
730
+ setPending(interruptState.pending);
731
+ } else if (localLegacy) {
732
+ interruptState.setLegacy(localLegacy);
733
+ setPending(interruptState.pending);
734
+ }
717
735
  localLegacy = null;
718
736
  localStandard = null;
719
737
  },
720
738
  onRunFailed: () => {
721
739
  localLegacy = null;
722
740
  localStandard = null;
723
- responsesRef.current = {};
741
+ interruptState.clear();
724
742
  setPending(null);
725
743
  }
726
744
  });
727
- return () => subscription.unsubscribe();
745
+ return () => {
746
+ subscription.unsubscribe();
747
+ interruptState.clear();
748
+ };
728
749
  }, [agent]);
729
- const submitStandardIfComplete = (0, react.useCallback)(async (interrupts) => {
730
- if (!interrupts.every((i) => responsesRef.current[i.id])) return;
731
- const expired = interrupts.find((i) => (0, _ag_ui_client.isInterruptExpired)(i));
732
- if (expired) {
733
- console.error(`[CopilotKit] useInterrupt: interrupt ${expired.id} expired at ${expired.expiresAt}; not resuming.`);
734
- responsesRef.current = {};
735
- setPending(null);
736
- return;
737
- }
738
- const resume = (0, _ag_ui_client.buildResumeArray)(interrupts, responsesRef.current);
739
- for (const i of interrupts) {
740
- if (!i.toolCallId) continue;
741
- const response = responsesRef.current[i.id];
742
- const content = response.status === "cancelled" ? { status: "cancelled" } : response.payload ?? { status: "resolved" };
743
- agent.addMessage({
744
- id: (0, _ag_ui_client.randomUUID)(),
745
- role: "tool",
746
- toolCallId: i.toolCallId,
747
- content: JSON.stringify(content)
748
- });
749
- }
750
- responsesRef.current = {};
751
- try {
750
+ const resolve = (0, react.useCallback)(async (payload, interruptId) => {
751
+ const current = pendingRef.current;
752
+ if (!current) return;
753
+ if (current.kind === "standard" && current.interrupts.length > 1 && interruptId === void 0) console.warn(`[CopilotKit] useInterrupt: resolve()/cancel() called without an interruptId while ${current.interrupts.length} interrupts are open; defaulting to the first. Pass an interruptId to address a specific interrupt.`);
754
+ const decision = interruptStateRef.current.resolve(payload, interruptId);
755
+ if (decision.kind === "legacy-resume") try {
752
756
  return await copilotkit.runAgent({
753
757
  agent,
754
- resume
758
+ forwardedProps: { command: {
759
+ resume: decision.payload,
760
+ interruptEvent: decision.interruptValue
761
+ } }
755
762
  });
756
763
  } catch (err) {
757
764
  console.error("[CopilotKit] useInterrupt resolve: runAgent rejected; clearing pending + rethrowing", err);
758
765
  setPending(null);
759
766
  throw err;
760
767
  }
761
- }, [agent, copilotkit]);
762
- const resolve = (0, react.useCallback)(async (payload, interruptId) => {
763
- const current = pendingRef.current;
764
- if (!current) return;
765
- if (current.kind === "legacy") try {
768
+ if (decision.kind === "expired") {
769
+ console.error(`[CopilotKit] useInterrupt: interrupt ${decision.interrupt.id} expired at ${decision.interrupt.expiresAt}; not resuming.`);
770
+ interruptStateRef.current.clear();
771
+ setPending(null);
772
+ return;
773
+ }
774
+ if (decision.kind !== "resume") return;
775
+ for (const toolResult of decision.toolResults) agent.addMessage({
776
+ id: (0, _ag_ui_client.randomUUID)(),
777
+ role: "tool",
778
+ toolCallId: toolResult.toolCallId,
779
+ content: toolResult.content
780
+ });
781
+ try {
766
782
  return await copilotkit.runAgent({
767
783
  agent,
768
- forwardedProps: { command: {
769
- resume: payload,
770
- interruptEvent: current.event.value
771
- } }
784
+ resume: decision.resume
772
785
  });
773
786
  } catch (err) {
774
787
  console.error("[CopilotKit] useInterrupt resolve: runAgent rejected; clearing pending + rethrowing", err);
788
+ interruptStateRef.current.clear();
775
789
  setPending(null);
776
790
  throw err;
777
791
  }
778
- if (current.interrupts.length > 1 && interruptId === void 0) console.warn(`[CopilotKit] useInterrupt: resolve()/cancel() called without an interruptId while ${current.interrupts.length} interrupts are open; defaulting to the first. Pass an interruptId to address a specific interrupt.`);
779
- const id = interruptId ?? current.interrupts[0]?.id;
780
- if (!id) return;
781
- responsesRef.current[id] = {
782
- status: "resolved",
783
- payload
784
- };
785
- return submitStandardIfComplete(current.interrupts);
786
- }, [
787
- agent,
788
- copilotkit,
789
- submitStandardIfComplete
790
- ]);
792
+ }, [agent, copilotkit]);
791
793
  const cancel = (0, react.useCallback)(async (interruptId) => {
792
794
  const current = pendingRef.current;
793
795
  if (!current) return;
794
- if (current.kind === "legacy") {
796
+ if (current.kind === "standard" && current.interrupts.length > 1 && interruptId === void 0) console.warn(`[CopilotKit] useInterrupt: resolve()/cancel() called without an interruptId while ${current.interrupts.length} interrupts are open; defaulting to the first. Pass an interruptId to address a specific interrupt.`);
797
+ const decision = interruptStateRef.current.cancel(interruptId);
798
+ if (decision.kind === "dismiss") {
795
799
  console.warn("[CopilotKit] useInterrupt: cancel() is not supported for legacy on_interrupt interrupts; dismissing.");
800
+ interruptStateRef.current.clear();
796
801
  setPending(null);
797
802
  return;
798
803
  }
799
- if (current.interrupts.length > 1 && interruptId === void 0) console.warn(`[CopilotKit] useInterrupt: resolve()/cancel() called without an interruptId while ${current.interrupts.length} interrupts are open; defaulting to the first. Pass an interruptId to address a specific interrupt.`);
800
- const id = interruptId ?? current.interrupts[0]?.id;
801
- if (!id) return;
802
- responsesRef.current[id] = { status: "cancelled" };
803
- return submitStandardIfComplete(current.interrupts);
804
- }, [submitStandardIfComplete]);
804
+ if (decision.kind === "expired") {
805
+ console.error(`[CopilotKit] useInterrupt: interrupt ${decision.interrupt.id} expired at ${decision.interrupt.expiresAt}; not resuming.`);
806
+ interruptStateRef.current.clear();
807
+ setPending(null);
808
+ return;
809
+ }
810
+ if (decision.kind !== "resume") return;
811
+ for (const toolResult of decision.toolResults) agent.addMessage({
812
+ id: (0, _ag_ui_client.randomUUID)(),
813
+ role: "tool",
814
+ toolCallId: toolResult.toolCallId,
815
+ content: toolResult.content
816
+ });
817
+ try {
818
+ return await copilotkit.runAgent({
819
+ agent,
820
+ resume: decision.resume
821
+ });
822
+ } catch (err) {
823
+ console.error("[CopilotKit] useInterrupt resolve: runAgent rejected; clearing pending + rethrowing", err);
824
+ interruptStateRef.current.clear();
825
+ setPending(null);
826
+ throw err;
827
+ }
828
+ }, [agent, copilotkit]);
805
829
  const renderRef = (0, react.useRef)(config.render);
806
830
  renderRef.current = config.render;
807
831
  const enabledRef = (0, react.useRef)(config.enabled);
@@ -843,7 +867,7 @@ function useInterrupt(config) {
843
867
  maybePromise = handler({
844
868
  event: legacyEvent,
845
869
  interrupt: pending.kind === "standard" ? pending.interrupts[0] : null,
846
- interrupts: pending.kind === "standard" ? pending.interrupts : [],
870
+ interrupts: pending.kind === "standard" ? [...pending.interrupts] : [],
847
871
  resolve: resolveRef.current,
848
872
  cancel: cancelRef.current
849
873
  });
@@ -872,7 +896,7 @@ function useInterrupt(config) {
872
896
  return renderRef.current({
873
897
  event: legacyEvent,
874
898
  interrupt: pending.kind === "standard" ? pending.interrupts[0] : null,
875
- interrupts: pending.kind === "standard" ? pending.interrupts : [],
899
+ interrupts: pending.kind === "standard" ? [...pending.interrupts] : [],
876
900
  result: handlerResult,
877
901
  resolve,
878
902
  cancel