@copilotkit/react-core 1.59.0 → 1.59.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.
@@ -1502,6 +1502,47 @@ function useRenderTool(config, deps) {
1502
1502
  //#endregion
1503
1503
  //#region src/v2/hooks/use-default-render-tool.tsx
1504
1504
  /**
1505
+ * Module-level dedup set so an unknown status value only emits a console
1506
+ * warning the FIRST time we encounter it. Otherwise a stuck/unmapped status
1507
+ * would log on every re-render (potentially many per second).
1508
+ */
1509
+ const warnedUnknownStatuses = /* @__PURE__ */ new Set();
1510
+ /**
1511
+ * Map a {@link ToolCallStatus} enum value to the documented string-union
1512
+ * status the {@link DefaultRenderProps} contract exposes. Unknown / future
1513
+ * enum members log a warning (once per distinct value) and fall back to
1514
+ * `"inProgress"`.
1515
+ */
1516
+ function mapToolCallStatus(status) {
1517
+ switch (status) {
1518
+ case ToolCallStatus.Complete: return "complete";
1519
+ case ToolCallStatus.Executing: return "executing";
1520
+ case ToolCallStatus.InProgress: return "inProgress";
1521
+ default: {
1522
+ const key = String(status);
1523
+ if (!warnedUnknownStatuses.has(key)) {
1524
+ warnedUnknownStatuses.add(key);
1525
+ console.warn(`[CopilotKit] Unknown ToolCallStatus "${key}" in default tool-call renderer; falling back to "inProgress".`);
1526
+ }
1527
+ return "inProgress";
1528
+ }
1529
+ }
1530
+ }
1531
+ /**
1532
+ * Convert the framework-internal renderer props (`args`, enum status) into
1533
+ * the documented {@link DefaultRenderProps} shape (`parameters`, string-union
1534
+ * status) so a user `config.render` always sees the documented contract.
1535
+ */
1536
+ function adaptRendererProps(props) {
1537
+ return {
1538
+ name: props.name,
1539
+ toolCallId: props.toolCallId,
1540
+ parameters: props.args,
1541
+ status: mapToolCallStatus(props.status),
1542
+ result: props.result
1543
+ };
1544
+ }
1545
+ /**
1505
1546
  * Registers a wildcard (`"*"`) tool-call renderer via `useRenderTool`.
1506
1547
  *
1507
1548
  * - Call with no config to use CopilotKit's built-in default tool-call card.
@@ -1538,16 +1579,33 @@ function useRenderTool(config, deps) {
1538
1579
  * ```
1539
1580
  */
1540
1581
  function useDefaultRenderTool(config, deps) {
1582
+ const userRender = config?.render;
1541
1583
  useRenderTool({
1542
1584
  name: "*",
1543
- render: config?.render ?? DefaultToolCallRenderer
1585
+ render: userRender ? (raw) => userRender(adaptRendererProps(raw)) : (raw) => /* @__PURE__ */ jsx(DefaultToolCallRenderer, { ...adaptRendererProps(raw) })
1544
1586
  }, deps);
1545
1587
  }
1546
- function DefaultToolCallRenderer({ name, parameters, status, result }) {
1588
+ /**
1589
+ * Guarded JSON.stringify used inside the expanded `<pre>` blocks. A circular
1590
+ * reference would otherwise crash the entire React tree on render.
1591
+ */
1592
+ function safeStringifyForPre(value) {
1593
+ try {
1594
+ return JSON.stringify(value, null, 2);
1595
+ } catch (err) {
1596
+ console.warn("[CopilotKit] Failed to JSON.stringify tool-call payload for default renderer; falling back to String():", err);
1597
+ try {
1598
+ return String(value);
1599
+ } catch (innerErr) {
1600
+ console.warn("[CopilotKit] safeStringifyForPre: value could not be stringified:", innerErr);
1601
+ return "[unserializable]";
1602
+ }
1603
+ }
1604
+ }
1605
+ function DefaultToolCallRenderer({ name, toolCallId, parameters, status, result }) {
1547
1606
  const [isExpanded, setIsExpanded] = useState(false);
1548
- const statusString = String(status);
1549
- const isActive = statusString === "inProgress" || statusString === "executing";
1550
- const isComplete = statusString === "complete";
1607
+ const isActive = status === "inProgress" || status === "executing";
1608
+ const isComplete = status === "complete";
1551
1609
  const statusLabel = isActive ? "Running" : isComplete ? "Done" : status;
1552
1610
  const dotColor = isActive ? "#f59e0b" : isComplete ? "#10b981" : "#a1a1aa";
1553
1611
  const badgeBg = isActive ? "#fef3c7" : isComplete ? "#d1fae5" : "#f4f4f5";
@@ -1555,7 +1613,8 @@ function DefaultToolCallRenderer({ name, parameters, status, result }) {
1555
1613
  return /* @__PURE__ */ jsx("div", {
1556
1614
  "data-testid": "copilot-tool-render",
1557
1615
  "data-tool-name": name,
1558
- "data-status": statusString,
1616
+ "data-tool-call-id": toolCallId,
1617
+ "data-status": status,
1559
1618
  "data-args": safeStringifyForAttr(parameters),
1560
1619
  "data-result": safeStringifyForAttr(result),
1561
1620
  style: {
@@ -1569,7 +1628,9 @@ function DefaultToolCallRenderer({ name, parameters, status, result }) {
1569
1628
  backgroundColor: "#fafafa",
1570
1629
  padding: "14px 16px"
1571
1630
  },
1572
- children: [/* @__PURE__ */ jsxs("div", {
1631
+ children: [/* @__PURE__ */ jsxs("button", {
1632
+ type: "button",
1633
+ "aria-expanded": isExpanded,
1573
1634
  onClick: () => setIsExpanded(!isExpanded),
1574
1635
  style: {
1575
1636
  display: "flex",
@@ -1577,7 +1638,15 @@ function DefaultToolCallRenderer({ name, parameters, status, result }) {
1577
1638
  justifyContent: "space-between",
1578
1639
  gap: "10px",
1579
1640
  cursor: "pointer",
1580
- userSelect: "none"
1641
+ userSelect: "none",
1642
+ width: "100%",
1643
+ border: "none",
1644
+ padding: 0,
1645
+ margin: 0,
1646
+ background: "transparent",
1647
+ textAlign: "left",
1648
+ font: "inherit",
1649
+ color: "inherit"
1581
1650
  },
1582
1651
  children: [/* @__PURE__ */ jsxs("div", {
1583
1652
  style: {
@@ -1670,7 +1739,7 @@ function DefaultToolCallRenderer({ name, parameters, status, result }) {
1670
1739
  whiteSpace: "pre-wrap",
1671
1740
  wordBreak: "break-word"
1672
1741
  },
1673
- children: JSON.stringify(parameters ?? {}, null, 2)
1742
+ children: safeStringifyForPre(parameters ?? {})
1674
1743
  })] }), result !== void 0 && /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("div", {
1675
1744
  style: {
1676
1745
  fontSize: "10px",
@@ -1693,7 +1762,7 @@ function DefaultToolCallRenderer({ name, parameters, status, result }) {
1693
1762
  whiteSpace: "pre-wrap",
1694
1763
  wordBreak: "break-word"
1695
1764
  },
1696
- children: typeof result === "string" ? result : JSON.stringify(result, null, 2)
1765
+ children: typeof result === "string" ? result : safeStringifyForPre(result)
1697
1766
  })] })]
1698
1767
  })]
1699
1768
  })
@@ -1704,8 +1773,14 @@ function safeStringifyForAttr(value) {
1704
1773
  if (typeof value === "string") return value;
1705
1774
  try {
1706
1775
  return JSON.stringify(value);
1707
- } catch {
1708
- return String(value);
1776
+ } catch (err) {
1777
+ console.warn("[CopilotKit] Failed to JSON.stringify tool-call payload for data-* attribute; falling back to String():", err);
1778
+ try {
1779
+ return String(value);
1780
+ } catch (innerErr) {
1781
+ console.warn("[CopilotKit] safeStringifyForAttr: value could not be stringified:", innerErr);
1782
+ return "";
1783
+ }
1709
1784
  }
1710
1785
  }
1711
1786
 
@@ -1776,11 +1851,11 @@ function useRenderToolCall() {
1776
1851
  ]);
1777
1852
  }
1778
1853
  function defaultToolCallRenderAdapter(props) {
1779
- const status = props.status === ToolCallStatus.Complete ? "complete" : props.status === ToolCallStatus.Executing ? "executing" : "inProgress";
1780
1854
  return /* @__PURE__ */ jsx(DefaultToolCallRenderer, {
1781
1855
  name: props.name,
1856
+ toolCallId: props.toolCallId,
1782
1857
  parameters: props.args,
1783
- status,
1858
+ status: mapToolCallStatus(props.status),
1784
1859
  result: props.result
1785
1860
  });
1786
1861
  }
@@ -3420,6 +3495,9 @@ function A2UICatalogContext({ catalog, includeSchema }) {
3420
3495
  //#region src/v2/providers/CopilotKitProvider.tsx
3421
3496
  const HEADER_NAME = "X-CopilotCloud-Public-Api-Key";
3422
3497
  const COPILOT_CLOUD_CHAT_URL$1 = "https://api.cloud.copilotkit.ai/copilotkit/v1";
3498
+ const EMPTY_HEADERS = Object.freeze({});
3499
+ const EMPTY_PROPERTIES = Object.freeze({});
3500
+ const EMPTY_AGENTS = Object.freeze({});
3423
3501
  const DEFAULT_DESIGN_SKILL = `When generating UI with generateSandboxedUi, follow these design principles inspired by shadcn/ui:
3424
3502
 
3425
3503
  - Use a minimal, flat aesthetic. Avoid drop shadows and gradients — rely on subtle borders (1px solid, light gray like #e5e7eb) to define surfaces.
@@ -3440,7 +3518,7 @@ function useStableArrayProp(prop, warningMessage, isMeaningfulChange) {
3440
3518
  }, [value, warningMessage]);
3441
3519
  return value;
3442
3520
  }
3443
- const CopilotKitProvider = ({ children, runtimeUrl, headers: headersProp = {}, credentials, publicApiKey, publicLicenseKey, licenseToken, properties = {}, agents__unsafe_dev_only: agents = {}, selfManagedAgents = {}, renderToolCalls, renderActivityMessages, renderCustomMessages, frontendTools, humanInTheLoop, openGenerativeUI, showDevConsole = false, useSingleEndpoint, onError, a2ui, defaultThrottleMs, inspectorDefaultAnchor, debug }) => {
3521
+ const CopilotKitProvider = ({ children, runtimeUrl, headers: headersProp = EMPTY_HEADERS, credentials, publicApiKey, publicLicenseKey, licenseToken, properties = EMPTY_PROPERTIES, agents__unsafe_dev_only: agents = EMPTY_AGENTS, selfManagedAgents = EMPTY_AGENTS, renderToolCalls, renderActivityMessages, renderCustomMessages, frontendTools, humanInTheLoop, openGenerativeUI, showDevConsole = false, useSingleEndpoint, onError, a2ui, defaultThrottleMs, inspectorDefaultAnchor, debug }) => {
3444
3522
  const [shouldRenderInspector, setShouldRenderInspector] = useState(false);
3445
3523
  const [runtimeA2UIEnabled, setRuntimeA2UIEnabled] = useState(false);
3446
3524
  const [runtimeOpenGenUIEnabled, setRuntimeOpenGenUIEnabled] = useState(false);
@@ -4472,7 +4550,6 @@ function useInterrupt(config) {
4472
4550
  return () => subscription.unsubscribe();
4473
4551
  }, [agent]);
4474
4552
  const resolve = useCallback((response) => {
4475
- setPendingEvent(null);
4476
4553
  copilotkit.runAgent({
4477
4554
  agent,
4478
4555
  forwardedProps: { command: {
@@ -4481,44 +4558,67 @@ function useInterrupt(config) {
4481
4558
  } }
4482
4559
  });
4483
4560
  }, [agent, copilotkit]);
4561
+ const renderRef = useRef(config.render);
4562
+ renderRef.current = config.render;
4563
+ const enabledRef = useRef(config.enabled);
4564
+ enabledRef.current = config.enabled;
4565
+ const handlerRef = useRef(config.handler);
4566
+ handlerRef.current = config.handler;
4567
+ const resolveRef = useRef(resolve);
4568
+ resolveRef.current = resolve;
4569
+ const isEnabled = (event) => {
4570
+ const predicate = enabledRef.current;
4571
+ if (!predicate) return true;
4572
+ try {
4573
+ return predicate(event);
4574
+ } catch (err) {
4575
+ console.error("[CopilotKit] useInterrupt enabled predicate threw; treating interrupt as disabled:", err);
4576
+ return false;
4577
+ }
4578
+ };
4484
4579
  useEffect(() => {
4485
4580
  if (!pendingEvent) {
4486
4581
  setHandlerResult(null);
4487
4582
  return;
4488
4583
  }
4489
- if (config.enabled && !config.enabled(pendingEvent)) {
4584
+ if (!isEnabled(pendingEvent)) {
4490
4585
  setHandlerResult(null);
4491
4586
  return;
4492
4587
  }
4493
- const handler = config.handler;
4588
+ const handler = handlerRef.current;
4494
4589
  if (!handler) {
4495
4590
  setHandlerResult(null);
4496
4591
  return;
4497
4592
  }
4498
4593
  let cancelled = false;
4499
- const maybePromise = handler({
4500
- event: pendingEvent,
4501
- resolve
4502
- });
4594
+ let maybePromise;
4595
+ try {
4596
+ maybePromise = handler({
4597
+ event: pendingEvent,
4598
+ resolve: resolveRef.current
4599
+ });
4600
+ } catch (err) {
4601
+ console.error("[CopilotKit] useInterrupt handler threw; result will be null:", err);
4602
+ if (!cancelled) setHandlerResult(null);
4603
+ return () => {
4604
+ cancelled = true;
4605
+ };
4606
+ }
4503
4607
  if (isPromiseLike(maybePromise)) Promise.resolve(maybePromise).then((resolved) => {
4504
4608
  if (!cancelled) setHandlerResult(resolved);
4505
- }).catch(() => {
4609
+ }).catch((err) => {
4610
+ console.error("[CopilotKit] useInterrupt handler rejected; result will be null:", err);
4506
4611
  if (!cancelled) setHandlerResult(null);
4507
4612
  });
4508
4613
  else setHandlerResult(maybePromise);
4509
4614
  return () => {
4510
4615
  cancelled = true;
4511
4616
  };
4512
- }, [
4513
- pendingEvent,
4514
- config.enabled,
4515
- config.handler,
4516
- resolve
4517
- ]);
4617
+ }, [pendingEvent]);
4518
4618
  const element = useMemo(() => {
4519
4619
  if (!pendingEvent) return null;
4520
- if (config.enabled && !config.enabled(pendingEvent)) return null;
4521
- return config.render({
4620
+ if (!isEnabled(pendingEvent)) return null;
4621
+ return renderRef.current({
4522
4622
  event: pendingEvent,
4523
4623
  result: handlerResult,
4524
4624
  resolve
@@ -4526,19 +4626,22 @@ function useInterrupt(config) {
4526
4626
  }, [
4527
4627
  pendingEvent,
4528
4628
  handlerResult,
4529
- config.enabled,
4530
- config.render,
4531
4629
  resolve
4532
4630
  ]);
4533
4631
  useEffect(() => {
4534
4632
  if (config.renderInChat === false) return;
4535
4633
  copilotkit.setInterruptElement(element);
4536
- return () => copilotkit.setInterruptElement(null);
4537
4634
  }, [
4538
4635
  element,
4539
4636
  config.renderInChat,
4540
4637
  copilotkit
4541
4638
  ]);
4639
+ useEffect(() => {
4640
+ if (config.renderInChat === false) return;
4641
+ return () => {
4642
+ copilotkit.setInterruptElement(null);
4643
+ };
4644
+ }, []);
4542
4645
  if (config.renderInChat === false) return element;
4543
4646
  }
4544
4647
 
@@ -7026,14 +7129,14 @@ function CopilotChat({ agentId, threadId, labels, chatView, isModalDefaultOpen,
7026
7129
  const [lastConnectedThreadId, setLastConnectedThreadId] = useState(null);
7027
7130
  const isConnecting = hasExplicitThreadId && lastConnectedThreadId !== resolvedThreadId;
7028
7131
  useEffect(() => {
7132
+ agent.threadId = resolvedThreadId;
7029
7133
  if (!hasExplicitThreadId) return;
7030
7134
  let detached = false;
7031
7135
  const connectAbortController = new AbortController();
7032
7136
  if (agent instanceof HttpAgent) agent.abortController = connectAbortController;
7033
- agent.threadId = resolvedThreadId;
7034
- const connect = async (agent) => {
7137
+ const connect = async (agentToConnect) => {
7035
7138
  try {
7036
- await copilotkit.connectAgent({ agent });
7139
+ await copilotkit.connectAgent({ agent: agentToConnect });
7037
7140
  } catch (error) {
7038
7141
  if (detached) return;
7039
7142
  console.error("CopilotChat: connectAgent failed", error);
@@ -8187,6 +8290,7 @@ function BannerErrorDisplay({ bannerError, onDismiss }) {
8187
8290
  const details = bannerError.details;
8188
8291
  const link = extractUrl(bannerError.message);
8189
8292
  return /* @__PURE__ */ jsxs("div", {
8293
+ "data-testid": "copilot-error-banner",
8190
8294
  style: {
8191
8295
  position: "fixed",
8192
8296
  bottom: "20px",
@@ -8387,7 +8491,7 @@ function ToastProvider({ enabled, children }) {
8387
8491
  }, []);
8388
8492
  const addToast = useCallback((toast) => {
8389
8493
  if (!enabled) return;
8390
- const id = toast.id ?? Math.random().toString(36).substring(2, 9);
8494
+ const id = toast.id ?? Math.random().toString(36).slice(2, 9);
8391
8495
  setToasts((currentToasts) => {
8392
8496
  if (currentToasts.find((toast) => toast.id === id)) return currentToasts;
8393
8497
  return [...currentToasts, {
@@ -8744,6 +8848,7 @@ function UsageBanner({ severity = Severity.CRITICAL, message = "", onClose, acti
8744
8848
  }
8745
8849
  ` }), /* @__PURE__ */ jsx("div", {
8746
8850
  className: "usage-banner",
8851
+ "data-testid": "copilot-error-banner",
8747
8852
  children: /* @__PURE__ */ jsxs("div", {
8748
8853
  className: "banner-content",
8749
8854
  children: [
@@ -9982,4 +10087,4 @@ function validateProps(props) {
9982
10087
 
9983
10088
  //#endregion
9984
10089
  export { createA2UIMessageRenderer as $, IntelligenceIndicator as A, useInterrupt as B, CopilotChatToggleButton as C, CopilotChatView_default as D, CopilotChat as E, CopilotChatAttachmentRenderer as F, useAgent as G, useSuggestions as H, CopilotChatAssistantMessage_default as I, useFrontendTool as J, useHumanInTheLoop as K, CopilotChatToolCallsView as L, CopilotChatSuggestionPill as M, CopilotChatReasoningMessage_default as N, CopilotChatAttachmentQueue as O, CopilotChatUserMessage_default as P, useAgentContext as Q, useAttachments as R, CopilotModalHeader as S, DefaultOpenIcon as T, useCapabilities as U, useConfigureSuggestions as V, UseAgentUpdate as W, useRenderCustomMessages as X, useRenderActivityMessage as Y, CopilotKitProvider as Z, WildcardToolCallRender as _, ThreadsProvider as a, CopilotKitInspector as at, CopilotPopupView as b, CoAgentStateRendersProvider as c, useRenderTool as ct, shouldShowDevConsole as d, CopilotKitCoreReact as dt, SandboxFunctionsContext as et, useToast as f, CopilotChatInput_default as ft, useCopilotContext as g, useCopilotChatConfiguration as gt, CopilotContext as h, CopilotChatConfigurationProvider as ht, ThreadsContext as i, MCPAppsActivityType as it, CopilotChatSuggestionView as j, CopilotChatMessageView as k, useCoAgentStateRenders as l, defineToolCallRenderer as lt, useCopilotMessagesContext as m, CopilotChatAudioRecorder as mt, defaultCopilotContextCategories as n, MCPAppsActivityContentSchema as nt, useThreads as o, useRenderToolCall as ot, CopilotMessagesContext as p, AudioRecorderError as pt, useComponent as q, CoAgentStateRenderBridge as r, MCPAppsActivityRenderer as rt, CoAgentStateRendersContext as s, useDefaultRenderTool as st, CopilotKit as t, useSandboxFunctions as tt, useAsyncCallback as u, useCopilotKit as ut, CopilotPopup as v, DefaultCloseIcon as w, CopilotSidebarView as x, CopilotSidebar as y, useThreads$1 as z };
9985
- //# sourceMappingURL=copilotkit-L4mM_JqG.mjs.map
10090
+ //# sourceMappingURL=copilotkit-B5RsC6la.mjs.map