@copilotkit/react-core 1.63.0 → 1.63.2-canary.1784918757

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.
@@ -30,13 +30,12 @@ let _copilotkit_react_core_v2_context = require("@copilotkit/react-core/v2/conte
30
30
  let react = require("react");
31
31
  react = __toESM(react);
32
32
  let _copilotkit_shared = require("@copilotkit/shared");
33
- let tailwind_merge = require("tailwind-merge");
34
33
  let react_jsx_runtime = require("react/jsx-runtime");
35
34
  let _ag_ui_client = require("@ag-ui/client");
36
35
  let _copilotkit_core = require("@copilotkit/core");
37
36
  let zod = require("zod");
38
37
 
39
- //#region src/v2/lib/slots.tsx
38
+ //#region src/v2/lib/shallow-stable-ref.ts
40
39
  /**
41
40
  * Shallow equality comparison for objects.
42
41
  */
@@ -76,49 +75,6 @@ function useShallowStableRef(value) {
76
75
  ref.current = value;
77
76
  return ref.current;
78
77
  }
79
- /**
80
- * Check if a value is a React component type (function, class, forwardRef, memo, etc.)
81
- */
82
- function isReactComponentType(value) {
83
- if (typeof value === "function") return true;
84
- if (value && typeof value === "object" && "$$typeof" in value && !react.default.isValidElement(value)) return true;
85
- return false;
86
- }
87
- /**
88
- * Internal function to render a slot value as a React element (non-memoized).
89
- */
90
- function renderSlotElement(slot, DefaultComponent, props) {
91
- if (typeof slot === "string") {
92
- const existingClassName = props.className;
93
- return react.default.createElement(DefaultComponent, {
94
- ...props,
95
- className: (0, tailwind_merge.twMerge)(existingClassName, slot)
96
- });
97
- }
98
- if (isReactComponentType(slot)) return react.default.createElement(slot, props);
99
- if (slot && typeof slot === "object" && !react.default.isValidElement(slot)) return react.default.createElement(DefaultComponent, {
100
- ...props,
101
- ...slot
102
- });
103
- return react.default.createElement(DefaultComponent, props);
104
- }
105
- /**
106
- * Internal memoized wrapper component for renderSlot.
107
- * Uses forwardRef to support ref forwarding.
108
- */
109
- const MemoizedSlotWrapper = react.default.memo(react.default.forwardRef(function MemoizedSlotWrapper(props, ref) {
110
- const { $slot, $component, ...rest } = props;
111
- return renderSlotElement($slot, $component, ref !== null ? {
112
- ...rest,
113
- ref
114
- } : rest);
115
- }), (prev, next) => {
116
- if (prev.$slot !== next.$slot) return false;
117
- if (prev.$component !== next.$component) return false;
118
- const { $slot: _ps, $component: _pc, ...prevRest } = prev;
119
- const { $slot: _ns, $component: _nc, ...nextRest } = next;
120
- return shallowEqual(prevRest, nextRest);
121
- });
122
78
 
123
79
  //#endregion
124
80
  //#region src/v2/providers/CopilotChatConfigurationProvider.tsx
@@ -345,11 +301,14 @@ function useAgent({ agentId, updates, throttleMs } = {}) {
345
301
  const [, forceUpdate] = (0, react.useReducer)((x) => x + 1, 0);
346
302
  const updateFlags = (0, react.useMemo)(() => updates ?? ALL_UPDATES, [JSON.stringify(updates)]);
347
303
  const provisionalAgentCache = (0, react.useRef)(/* @__PURE__ */ new Map());
348
- const agent = (0, react.useMemo)(() => {
304
+ const { agent, isReady } = (0, react.useMemo)(() => {
349
305
  const existing = copilotkit.getAgent(resolvedAgentId);
350
306
  if (existing) {
351
307
  provisionalAgentCache.current.delete(resolvedAgentId);
352
- return existing;
308
+ return {
309
+ agent: existing,
310
+ isReady: true
311
+ };
353
312
  }
354
313
  const isRuntimeConfigured = copilotkit.runtimeUrl !== void 0;
355
314
  const status = copilotkit.runtimeConnectionStatus;
@@ -357,7 +316,10 @@ function useAgent({ agentId, updates, throttleMs } = {}) {
357
316
  const cached = provisionalAgentCache.current.get(resolvedAgentId);
358
317
  if (cached) {
359
318
  copilotkit.applyHeadersToAgent(cached);
360
- return cached;
319
+ return {
320
+ agent: cached,
321
+ isReady: false
322
+ };
361
323
  }
362
324
  const provisional = new _copilotkit_core.ProxiedCopilotRuntimeAgent({
363
325
  runtimeUrl: copilotkit.runtimeUrl,
@@ -367,13 +329,19 @@ function useAgent({ agentId, updates, throttleMs } = {}) {
367
329
  });
368
330
  copilotkit.applyHeadersToAgent(provisional);
369
331
  provisionalAgentCache.current.set(resolvedAgentId, provisional);
370
- return provisional;
332
+ return {
333
+ agent: provisional,
334
+ isReady: false
335
+ };
371
336
  }
372
337
  if (isRuntimeConfigured && status === _copilotkit_core.CopilotKitCoreRuntimeConnectionStatus.Error) {
373
338
  const cached = provisionalAgentCache.current.get(resolvedAgentId);
374
339
  if (cached) {
375
340
  copilotkit.applyHeadersToAgent(cached);
376
- return cached;
341
+ return {
342
+ agent: cached,
343
+ isReady: false
344
+ };
377
345
  }
378
346
  const provisional = new _copilotkit_core.ProxiedCopilotRuntimeAgent({
379
347
  runtimeUrl: copilotkit.runtimeUrl,
@@ -383,7 +351,10 @@ function useAgent({ agentId, updates, throttleMs } = {}) {
383
351
  });
384
352
  copilotkit.applyHeadersToAgent(provisional);
385
353
  provisionalAgentCache.current.set(resolvedAgentId, provisional);
386
- return provisional;
354
+ return {
355
+ agent: provisional,
356
+ isReady: false
357
+ };
387
358
  }
388
359
  const knownAgents = Object.keys(copilotkit.agents ?? {});
389
360
  const runtimePart = isRuntimeConfigured ? `runtimeUrl=${copilotkit.runtimeUrl}` : "no runtimeUrl";
@@ -444,7 +415,10 @@ function useAgent({ agentId, updates, throttleMs } = {}) {
444
415
  configThreadId,
445
416
  configHasExplicitThreadId
446
417
  ]);
447
- return { agent };
418
+ return {
419
+ agent,
420
+ isReady
421
+ };
448
422
  }
449
423
 
450
424
  //#endregion
@@ -685,8 +659,9 @@ function useInterrupt(config) {
685
659
  const pendingRef = (0, react.useRef)(pending);
686
660
  pendingRef.current = pending;
687
661
  const [handlerResult, setHandlerResult] = (0, react.useState)(null);
688
- const responsesRef = (0, react.useRef)({});
662
+ const interruptStateRef = (0, react.useRef)(new _copilotkit_core.ɵInterruptState());
689
663
  (0, react.useEffect)(() => {
664
+ const interruptState = interruptStateRef.current;
690
665
  let localLegacy = null;
691
666
  let localStandard = null;
692
667
  const subscription = agent.subscribe({
@@ -702,106 +677,111 @@ function useInterrupt(config) {
702
677
  onRunStartedEvent: () => {
703
678
  localLegacy = null;
704
679
  localStandard = null;
705
- responsesRef.current = {};
680
+ interruptState.clear();
706
681
  setPending(null);
707
682
  },
708
683
  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
- });
684
+ if (localStandard && localStandard.length > 0) {
685
+ interruptState.setStandard(localStandard);
686
+ setPending(interruptState.pending);
687
+ } else if (localLegacy) {
688
+ interruptState.setLegacy(localLegacy);
689
+ setPending(interruptState.pending);
690
+ }
717
691
  localLegacy = null;
718
692
  localStandard = null;
719
693
  },
720
694
  onRunFailed: () => {
721
695
  localLegacy = null;
722
696
  localStandard = null;
723
- responsesRef.current = {};
697
+ interruptState.clear();
724
698
  setPending(null);
725
699
  }
726
700
  });
727
- return () => subscription.unsubscribe();
701
+ return () => {
702
+ subscription.unsubscribe();
703
+ interruptState.clear();
704
+ };
728
705
  }, [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 {
706
+ const resolve = (0, react.useCallback)(async (payload, interruptId) => {
707
+ const current = pendingRef.current;
708
+ if (!current) return;
709
+ 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.`);
710
+ const decision = interruptStateRef.current.resolve(payload, interruptId);
711
+ if (decision.kind === "legacy-resume") try {
752
712
  return await copilotkit.runAgent({
753
713
  agent,
754
- resume
714
+ forwardedProps: { command: {
715
+ resume: decision.payload,
716
+ interruptEvent: decision.interruptValue
717
+ } }
755
718
  });
756
719
  } catch (err) {
757
720
  console.error("[CopilotKit] useInterrupt resolve: runAgent rejected; clearing pending + rethrowing", err);
758
721
  setPending(null);
759
722
  throw err;
760
723
  }
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 {
724
+ if (decision.kind === "expired") {
725
+ console.error(`[CopilotKit] useInterrupt: interrupt ${decision.interrupt.id} expired at ${decision.interrupt.expiresAt}; not resuming.`);
726
+ interruptStateRef.current.clear();
727
+ setPending(null);
728
+ return;
729
+ }
730
+ if (decision.kind !== "resume") return;
731
+ for (const toolResult of decision.toolResults) agent.addMessage({
732
+ id: (0, _ag_ui_client.randomUUID)(),
733
+ role: "tool",
734
+ toolCallId: toolResult.toolCallId,
735
+ content: toolResult.content
736
+ });
737
+ try {
766
738
  return await copilotkit.runAgent({
767
739
  agent,
768
- forwardedProps: { command: {
769
- resume: payload,
770
- interruptEvent: current.event.value
771
- } }
740
+ resume: decision.resume
772
741
  });
773
742
  } catch (err) {
774
743
  console.error("[CopilotKit] useInterrupt resolve: runAgent rejected; clearing pending + rethrowing", err);
744
+ interruptStateRef.current.clear();
775
745
  setPending(null);
776
746
  throw err;
777
747
  }
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
- ]);
748
+ }, [agent, copilotkit]);
791
749
  const cancel = (0, react.useCallback)(async (interruptId) => {
792
750
  const current = pendingRef.current;
793
751
  if (!current) return;
794
- if (current.kind === "legacy") {
752
+ 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.`);
753
+ const decision = interruptStateRef.current.cancel(interruptId);
754
+ if (decision.kind === "dismiss") {
795
755
  console.warn("[CopilotKit] useInterrupt: cancel() is not supported for legacy on_interrupt interrupts; dismissing.");
756
+ interruptStateRef.current.clear();
796
757
  setPending(null);
797
758
  return;
798
759
  }
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]);
760
+ if (decision.kind === "expired") {
761
+ console.error(`[CopilotKit] useInterrupt: interrupt ${decision.interrupt.id} expired at ${decision.interrupt.expiresAt}; not resuming.`);
762
+ interruptStateRef.current.clear();
763
+ setPending(null);
764
+ return;
765
+ }
766
+ if (decision.kind !== "resume") return;
767
+ for (const toolResult of decision.toolResults) agent.addMessage({
768
+ id: (0, _ag_ui_client.randomUUID)(),
769
+ role: "tool",
770
+ toolCallId: toolResult.toolCallId,
771
+ content: toolResult.content
772
+ });
773
+ try {
774
+ return await copilotkit.runAgent({
775
+ agent,
776
+ resume: decision.resume
777
+ });
778
+ } catch (err) {
779
+ console.error("[CopilotKit] useInterrupt resolve: runAgent rejected; clearing pending + rethrowing", err);
780
+ interruptStateRef.current.clear();
781
+ setPending(null);
782
+ throw err;
783
+ }
784
+ }, [agent, copilotkit]);
805
785
  const renderRef = (0, react.useRef)(config.render);
806
786
  renderRef.current = config.render;
807
787
  const enabledRef = (0, react.useRef)(config.enabled);
@@ -843,7 +823,7 @@ function useInterrupt(config) {
843
823
  maybePromise = handler({
844
824
  event: legacyEvent,
845
825
  interrupt: pending.kind === "standard" ? pending.interrupts[0] : null,
846
- interrupts: pending.kind === "standard" ? pending.interrupts : [],
826
+ interrupts: pending.kind === "standard" ? [...pending.interrupts] : [],
847
827
  resolve: resolveRef.current,
848
828
  cancel: cancelRef.current
849
829
  });
@@ -872,7 +852,7 @@ function useInterrupt(config) {
872
852
  return renderRef.current({
873
853
  event: legacyEvent,
874
854
  interrupt: pending.kind === "standard" ? pending.interrupts[0] : null,
875
- interrupts: pending.kind === "standard" ? pending.interrupts : [],
855
+ interrupts: pending.kind === "standard" ? [...pending.interrupts] : [],
876
856
  result: handlerResult,
877
857
  resolve,
878
858
  cancel
@@ -1418,6 +1398,76 @@ function useRenderTool(config, deps) {
1418
1398
  ]);
1419
1399
  }
1420
1400
 
1401
+ //#endregion
1402
+ //#region src/v2/hooks/use-render-tool-call.tsx
1403
+ /**
1404
+ * Memoized component that renders a single tool call.
1405
+ * This prevents unnecessary re-renders when parent components update
1406
+ * but the tool call data hasn't changed.
1407
+ */
1408
+ const ToolCallRenderer = react.default.memo(function ToolCallRenderer({ toolCall, toolMessage, RenderComponent, isExecuting }) {
1409
+ const args = (0, react.useMemo)(() => (0, _copilotkit_shared.partialJSONParse)(toolCall.function.arguments), [toolCall.function.arguments]);
1410
+ const toolName = toolCall.function.name;
1411
+ if (toolMessage) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RenderComponent, {
1412
+ name: toolName,
1413
+ toolCallId: toolCall.id,
1414
+ args,
1415
+ status: _copilotkit_core.ToolCallStatus.Complete,
1416
+ result: toolMessage.content
1417
+ });
1418
+ else if (isExecuting) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RenderComponent, {
1419
+ name: toolName,
1420
+ toolCallId: toolCall.id,
1421
+ args,
1422
+ status: _copilotkit_core.ToolCallStatus.Executing,
1423
+ result: void 0
1424
+ });
1425
+ else return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RenderComponent, {
1426
+ name: toolName,
1427
+ toolCallId: toolCall.id,
1428
+ args,
1429
+ status: _copilotkit_core.ToolCallStatus.InProgress,
1430
+ result: void 0
1431
+ });
1432
+ }, (prevProps, nextProps) => {
1433
+ if (prevProps.toolCall.id !== nextProps.toolCall.id) return false;
1434
+ if (prevProps.toolCall.function.name !== nextProps.toolCall.function.name) return false;
1435
+ if (prevProps.toolCall.function.arguments !== nextProps.toolCall.function.arguments) return false;
1436
+ if (prevProps.toolMessage?.content !== nextProps.toolMessage?.content) return false;
1437
+ if (prevProps.isExecuting !== nextProps.isExecuting) return false;
1438
+ if (prevProps.RenderComponent !== nextProps.RenderComponent) return false;
1439
+ return true;
1440
+ });
1441
+ /**
1442
+ * Hook that returns a function to render tool calls based on the render functions
1443
+ * defined in CopilotKitProvider.
1444
+ *
1445
+ * @returns A function that takes a tool call and optional tool message and returns the rendered component
1446
+ */
1447
+ function useRenderToolCall() {
1448
+ const { copilotkit, executingToolCallIds } = (0, _copilotkit_react_core_v2_context.useCopilotKit)();
1449
+ const agentId = useCopilotChatConfiguration()?.agentId ?? _copilotkit_shared.DEFAULT_AGENT_ID;
1450
+ const renderToolCalls = (0, react.useSyncExternalStore)((callback) => {
1451
+ return copilotkit.subscribe({ onRenderToolCallsChanged: callback }).unsubscribe;
1452
+ }, () => copilotkit.renderToolCalls, () => copilotkit.renderToolCalls);
1453
+ return (0, react.useCallback)(({ toolCall, toolMessage }) => {
1454
+ const exactMatches = renderToolCalls.filter((rc) => rc.name === toolCall.function.name);
1455
+ const renderConfig = exactMatches.find((rc) => rc.agentId === agentId) || exactMatches.find((rc) => !rc.agentId) || exactMatches[0] || renderToolCalls.find((rc) => rc.name === "*");
1456
+ if (!renderConfig) return null;
1457
+ const RenderComponent = renderConfig.render;
1458
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ToolCallRenderer, {
1459
+ toolCall,
1460
+ toolMessage,
1461
+ RenderComponent,
1462
+ isExecuting: executingToolCallIds.has(toolCall.id)
1463
+ }, toolCall.id);
1464
+ }, [
1465
+ renderToolCalls,
1466
+ executingToolCallIds,
1467
+ agentId
1468
+ ]);
1469
+ }
1470
+
1421
1471
  //#endregion
1422
1472
  //#region src/v2/hooks/use-capabilities.tsx
1423
1473
  /**
@@ -1447,6 +1497,7 @@ Object.defineProperty(exports, 'CopilotKitCoreReact', {
1447
1497
  return _copilotkit_react_core_v2_context.CopilotKitCoreReact;
1448
1498
  }
1449
1499
  });
1500
+ exports.UseAgentUpdate = UseAgentUpdate;
1450
1501
  exports.defineToolCallRenderer = defineToolCallRenderer;
1451
1502
  exports.useAgent = useAgent;
1452
1503
  exports.useAgentContext = useAgentContext;
@@ -1454,10 +1505,17 @@ exports.useCapabilities = useCapabilities;
1454
1505
  exports.useComponent = useComponent;
1455
1506
  exports.useConfigureSuggestions = useConfigureSuggestions;
1456
1507
  exports.useCopilotChatConfiguration = useCopilotChatConfiguration;
1508
+ Object.defineProperty(exports, 'useCopilotKit', {
1509
+ enumerable: true,
1510
+ get: function () {
1511
+ return _copilotkit_react_core_v2_context.useCopilotKit;
1512
+ }
1513
+ });
1457
1514
  exports.useFrontendTool = useFrontendTool;
1458
1515
  exports.useHumanInTheLoop = useHumanInTheLoop;
1459
1516
  exports.useInterrupt = useInterrupt;
1460
1517
  exports.useRenderTool = useRenderTool;
1518
+ exports.useRenderToolCall = useRenderToolCall;
1461
1519
  exports.useSuggestions = useSuggestions;
1462
1520
  exports.useThreads = useThreads;
1463
1521
  //# sourceMappingURL=headless.cjs.map