@copilotkit/react-core 1.57.1 → 1.57.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.
Files changed (41) hide show
  1. package/dist/{copilotkit-sQWiKtxA.d.cts → copilotkit-BK9CVq9A.d.cts} +6 -1
  2. package/dist/{copilotkit-sQWiKtxA.d.cts.map → copilotkit-BK9CVq9A.d.cts.map} +1 -1
  3. package/dist/{copilotkit-DjxXMYHG.mjs → copilotkit-CC8DjOiC.mjs} +404 -367
  4. package/dist/copilotkit-CC8DjOiC.mjs.map +1 -0
  5. package/dist/{copilotkit-C3k13WZn.cjs → copilotkit-CtXcs1ea.cjs} +403 -366
  6. package/dist/copilotkit-CtXcs1ea.cjs.map +1 -0
  7. package/dist/{copilotkit-BN4I_y1n.d.mts → copilotkit-WlmeVijs.d.mts} +6 -1
  8. package/dist/{copilotkit-BN4I_y1n.d.mts.map → copilotkit-WlmeVijs.d.mts.map} +1 -1
  9. package/dist/index.cjs +1 -1
  10. package/dist/index.d.cts +1 -1
  11. package/dist/index.d.mts +1 -1
  12. package/dist/index.mjs +1 -1
  13. package/dist/index.umd.js +191 -15
  14. package/dist/index.umd.js.map +1 -1
  15. package/dist/v2/headless.cjs +110 -3
  16. package/dist/v2/headless.cjs.map +1 -1
  17. package/dist/v2/headless.d.cts +142 -2
  18. package/dist/v2/headless.d.cts.map +1 -1
  19. package/dist/v2/headless.d.mts +142 -1
  20. package/dist/v2/headless.d.mts.map +1 -1
  21. package/dist/v2/headless.mjs +108 -4
  22. package/dist/v2/headless.mjs.map +1 -1
  23. package/dist/v2/index.cjs +1 -1
  24. package/dist/v2/index.css +1 -1
  25. package/dist/v2/index.d.cts +1 -1
  26. package/dist/v2/index.d.mts +1 -1
  27. package/dist/v2/index.mjs +1 -1
  28. package/dist/v2/index.umd.js +403 -364
  29. package/dist/v2/index.umd.js.map +1 -1
  30. package/package.json +6 -6
  31. package/src/v2/components/chat/CopilotSidebar.tsx +5 -1
  32. package/src/v2/components/chat/CopilotSidebarView.tsx +24 -10
  33. package/src/v2/components/chat/__tests__/CopilotChatPerf.e2e.test.tsx +28 -0
  34. package/src/v2/components/chat/__tests__/CopilotSidebarView.position.test.tsx +159 -0
  35. package/src/v2/headless.ts +23 -1
  36. package/src/v2/hooks/__tests__/use-component.test.tsx +4 -1
  37. package/src/v2/hooks/use-component.tsx +2 -0
  38. package/src/v2/hooks/use-default-render-tool.tsx +18 -1
  39. package/src/v2/hooks/use-render-tool-call.tsx +35 -5
  40. package/dist/copilotkit-C3k13WZn.cjs.map +0 -1
  41. package/dist/copilotkit-DjxXMYHG.mjs.map +0 -1
@@ -1463,6 +1463,301 @@ _radix_ui_react_dropdown_menu = __toESM(_radix_ui_react_dropdown_menu);
1463
1463
  });
1464
1464
  const useLicenseContext = () => (0, react.useContext)(LicenseContext);
1465
1465
 
1466
+ //#endregion
1467
+ //#region src/v2/types/defineToolCallRenderer.ts
1468
+ function defineToolCallRenderer(def) {
1469
+ const argsSchema = def.name === "*" && !def.args ? zod.z.any() : def.args;
1470
+ return {
1471
+ name: def.name,
1472
+ args: argsSchema,
1473
+ render: def.render,
1474
+ ...def.agentId ? { agentId: def.agentId } : {}
1475
+ };
1476
+ }
1477
+
1478
+ //#endregion
1479
+ //#region src/v2/hooks/use-render-tool.tsx
1480
+ const EMPTY_DEPS$1 = [];
1481
+ /**
1482
+ * Registers a renderer entry in CopilotKit's `renderToolCalls` registry.
1483
+ *
1484
+ * Key behavior:
1485
+ * - deduplicates by `agentId:name` (latest registration wins),
1486
+ * - keeps renderer entries on cleanup so historical chat tool calls can still render,
1487
+ * - refreshes registration when `deps` change.
1488
+ *
1489
+ * @typeParam S - Schema type describing tool call parameters.
1490
+ * @param config - Renderer config for wildcard or named tools.
1491
+ * @param deps - Optional dependencies to refresh registration.
1492
+ *
1493
+ * @example
1494
+ * ```tsx
1495
+ * useRenderTool(
1496
+ * {
1497
+ * name: "searchDocs",
1498
+ * parameters: z.object({ query: z.string() }),
1499
+ * render: ({ status, parameters, result }) => {
1500
+ * if (status === "executing") return <div>Searching {parameters.query}</div>;
1501
+ * if (status === "complete") return <div>{result}</div>;
1502
+ * return <div>Preparing...</div>;
1503
+ * },
1504
+ * },
1505
+ * [],
1506
+ * );
1507
+ * ```
1508
+ *
1509
+ * @example
1510
+ * ```tsx
1511
+ * useRenderTool(
1512
+ * {
1513
+ * name: "summarize",
1514
+ * parameters: z.object({ text: z.string() }),
1515
+ * agentId: "research-agent",
1516
+ * render: ({ name, status }) => <div>{name}: {status}</div>,
1517
+ * },
1518
+ * [selectedAgentId],
1519
+ * );
1520
+ * ```
1521
+ */
1522
+ function useRenderTool(config, deps) {
1523
+ const { copilotkit } = useCopilotKit();
1524
+ const extraDeps = deps !== null && deps !== void 0 ? deps : EMPTY_DEPS$1;
1525
+ (0, react.useEffect)(() => {
1526
+ const renderer = config.name === "*" && !config.parameters ? defineToolCallRenderer({
1527
+ name: "*",
1528
+ render: (props) => config.render({
1529
+ ...props,
1530
+ parameters: props.args
1531
+ }),
1532
+ ...config.agentId ? { agentId: config.agentId } : {}
1533
+ }) : defineToolCallRenderer({
1534
+ name: config.name,
1535
+ args: config.parameters,
1536
+ render: (props) => config.render({
1537
+ ...props,
1538
+ parameters: props.args
1539
+ }),
1540
+ ...config.agentId ? { agentId: config.agentId } : {}
1541
+ });
1542
+ copilotkit.addHookRenderToolCall(renderer);
1543
+ }, [
1544
+ config.name,
1545
+ copilotkit,
1546
+ JSON.stringify(extraDeps)
1547
+ ]);
1548
+ }
1549
+
1550
+ //#endregion
1551
+ //#region src/v2/hooks/use-default-render-tool.tsx
1552
+ /**
1553
+ * Registers a wildcard (`"*"`) tool-call renderer via `useRenderTool`.
1554
+ *
1555
+ * - Call with no config to use CopilotKit's built-in default tool-call card.
1556
+ * - Pass `config.render` to replace the default UI with your own fallback renderer.
1557
+ *
1558
+ * This is useful when you want a generic renderer for tools that do not have a
1559
+ * dedicated `useRenderTool({ name: "..." })` registration.
1560
+ *
1561
+ * @param config - Optional custom wildcard render function.
1562
+ * @param deps - Optional dependencies to refresh registration.
1563
+ *
1564
+ * @example
1565
+ * ```tsx
1566
+ * useDefaultRenderTool();
1567
+ * ```
1568
+ *
1569
+ * @example
1570
+ * ```tsx
1571
+ * useDefaultRenderTool({
1572
+ * render: ({ name, status }) => <div>{name}: {status}</div>,
1573
+ * });
1574
+ * ```
1575
+ *
1576
+ * @example
1577
+ * ```tsx
1578
+ * useDefaultRenderTool(
1579
+ * {
1580
+ * render: ({ name, result }) => (
1581
+ * <ToolEventRow title={name} payload={result} compact={compactMode} />
1582
+ * ),
1583
+ * },
1584
+ * [compactMode],
1585
+ * );
1586
+ * ```
1587
+ */
1588
+ function useDefaultRenderTool(config, deps) {
1589
+ var _config$render;
1590
+ useRenderTool({
1591
+ name: "*",
1592
+ render: (_config$render = config === null || config === void 0 ? void 0 : config.render) !== null && _config$render !== void 0 ? _config$render : DefaultToolCallRenderer
1593
+ }, deps);
1594
+ }
1595
+ function DefaultToolCallRenderer({ name, parameters, status, result }) {
1596
+ const [isExpanded, setIsExpanded] = (0, react.useState)(false);
1597
+ const statusString = String(status);
1598
+ const isActive = statusString === "inProgress" || statusString === "executing";
1599
+ const isComplete = statusString === "complete";
1600
+ const statusLabel = isActive ? "Running" : isComplete ? "Done" : status;
1601
+ const dotColor = isActive ? "#f59e0b" : isComplete ? "#10b981" : "#a1a1aa";
1602
+ const badgeBg = isActive ? "#fef3c7" : isComplete ? "#d1fae5" : "#f4f4f5";
1603
+ const badgeColor = isActive ? "#92400e" : isComplete ? "#065f46" : "#3f3f46";
1604
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1605
+ "data-testid": "copilot-tool-render",
1606
+ "data-tool-name": name,
1607
+ "data-status": statusString,
1608
+ "data-args": safeStringifyForAttr(parameters),
1609
+ "data-result": safeStringifyForAttr(result),
1610
+ style: {
1611
+ marginTop: "8px",
1612
+ paddingBottom: "8px"
1613
+ },
1614
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1615
+ style: {
1616
+ borderRadius: "12px",
1617
+ border: "1px solid #e4e4e7",
1618
+ backgroundColor: "#fafafa",
1619
+ padding: "14px 16px"
1620
+ },
1621
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1622
+ onClick: () => setIsExpanded(!isExpanded),
1623
+ style: {
1624
+ display: "flex",
1625
+ alignItems: "center",
1626
+ justifyContent: "space-between",
1627
+ gap: "10px",
1628
+ cursor: "pointer",
1629
+ userSelect: "none"
1630
+ },
1631
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1632
+ style: {
1633
+ display: "flex",
1634
+ alignItems: "center",
1635
+ gap: "8px",
1636
+ minWidth: 0
1637
+ },
1638
+ children: [
1639
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
1640
+ style: {
1641
+ height: "14px",
1642
+ width: "14px",
1643
+ color: "#71717a",
1644
+ transition: "transform 0.15s",
1645
+ transform: isExpanded ? "rotate(90deg)" : "rotate(0deg)",
1646
+ flexShrink: 0
1647
+ },
1648
+ fill: "none",
1649
+ viewBox: "0 0 24 24",
1650
+ strokeWidth: 2,
1651
+ stroke: "currentColor",
1652
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
1653
+ strokeLinecap: "round",
1654
+ strokeLinejoin: "round",
1655
+ d: "M8.25 4.5l7.5 7.5-7.5 7.5"
1656
+ })
1657
+ }),
1658
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { style: {
1659
+ display: "inline-block",
1660
+ height: "8px",
1661
+ width: "8px",
1662
+ borderRadius: "50%",
1663
+ backgroundColor: dotColor,
1664
+ flexShrink: 0
1665
+ } }),
1666
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1667
+ "data-testid": "copilot-tool-render-name",
1668
+ style: {
1669
+ fontSize: "13px",
1670
+ fontWeight: 600,
1671
+ color: "#18181b",
1672
+ overflow: "hidden",
1673
+ textOverflow: "ellipsis",
1674
+ whiteSpace: "nowrap"
1675
+ },
1676
+ children: name
1677
+ })
1678
+ ]
1679
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1680
+ "data-testid": "copilot-tool-render-status",
1681
+ style: {
1682
+ display: "inline-flex",
1683
+ alignItems: "center",
1684
+ borderRadius: "9999px",
1685
+ padding: "2px 8px",
1686
+ fontSize: "11px",
1687
+ fontWeight: 500,
1688
+ backgroundColor: badgeBg,
1689
+ color: badgeColor,
1690
+ flexShrink: 0
1691
+ },
1692
+ children: statusLabel
1693
+ })]
1694
+ }), isExpanded && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1695
+ style: {
1696
+ marginTop: "12px",
1697
+ display: "grid",
1698
+ gap: "12px"
1699
+ },
1700
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1701
+ style: {
1702
+ fontSize: "10px",
1703
+ textTransform: "uppercase",
1704
+ letterSpacing: "0.05em",
1705
+ color: "#71717a"
1706
+ },
1707
+ children: "Arguments"
1708
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("pre", {
1709
+ style: {
1710
+ marginTop: "6px",
1711
+ maxHeight: "200px",
1712
+ overflow: "auto",
1713
+ borderRadius: "6px",
1714
+ backgroundColor: "#f4f4f5",
1715
+ padding: "10px",
1716
+ fontSize: "11px",
1717
+ lineHeight: 1.6,
1718
+ color: "#27272a",
1719
+ whiteSpace: "pre-wrap",
1720
+ wordBreak: "break-word"
1721
+ },
1722
+ children: JSON.stringify(parameters !== null && parameters !== void 0 ? parameters : {}, null, 2)
1723
+ })] }), result !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1724
+ style: {
1725
+ fontSize: "10px",
1726
+ textTransform: "uppercase",
1727
+ letterSpacing: "0.05em",
1728
+ color: "#71717a"
1729
+ },
1730
+ children: "Result"
1731
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("pre", {
1732
+ style: {
1733
+ marginTop: "6px",
1734
+ maxHeight: "200px",
1735
+ overflow: "auto",
1736
+ borderRadius: "6px",
1737
+ backgroundColor: "#f4f4f5",
1738
+ padding: "10px",
1739
+ fontSize: "11px",
1740
+ lineHeight: 1.6,
1741
+ color: "#27272a",
1742
+ whiteSpace: "pre-wrap",
1743
+ wordBreak: "break-word"
1744
+ },
1745
+ children: typeof result === "string" ? result : JSON.stringify(result, null, 2)
1746
+ })] })]
1747
+ })]
1748
+ })
1749
+ });
1750
+ }
1751
+ function safeStringifyForAttr(value) {
1752
+ if (value === void 0 || value === null) return "";
1753
+ if (typeof value === "string") return value;
1754
+ try {
1755
+ return JSON.stringify(value);
1756
+ } catch (_unused) {
1757
+ return String(value);
1758
+ }
1759
+ }
1760
+
1466
1761
  //#endregion
1467
1762
  //#region src/v2/hooks/use-render-tool-call.tsx
1468
1763
  /**
@@ -1519,14 +1814,13 @@ _radix_ui_react_dropdown_menu = __toESM(_radix_ui_react_dropdown_menu);
1519
1814
  return copilotkit.subscribe({ onRenderToolCallsChanged: callback }).unsubscribe;
1520
1815
  }, () => copilotkit.renderToolCalls, () => copilotkit.renderToolCalls);
1521
1816
  return (0, react.useCallback)(({ toolCall, toolMessage }) => {
1817
+ var _renderConfig$render;
1522
1818
  const exactMatches = renderToolCalls.filter((rc) => rc.name === toolCall.function.name);
1523
1819
  const renderConfig = exactMatches.find((rc) => rc.agentId === agentId) || exactMatches.find((rc) => !rc.agentId) || exactMatches[0] || renderToolCalls.find((rc) => rc.name === "*");
1524
- if (!renderConfig) return null;
1525
- const RenderComponent = renderConfig.render;
1526
1820
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ToolCallRenderer, {
1527
1821
  toolCall,
1528
1822
  toolMessage,
1529
- RenderComponent,
1823
+ RenderComponent: (_renderConfig$render = renderConfig === null || renderConfig === void 0 ? void 0 : renderConfig.render) !== null && _renderConfig$render !== void 0 ? _renderConfig$render : defaultToolCallRenderAdapter,
1530
1824
  isExecuting: executingToolCallIds.has(toolCall.id)
1531
1825
  }, toolCall.id);
1532
1826
  }, [
@@ -1535,6 +1829,15 @@ _radix_ui_react_dropdown_menu = __toESM(_radix_ui_react_dropdown_menu);
1535
1829
  agentId
1536
1830
  ]);
1537
1831
  }
1832
+ function defaultToolCallRenderAdapter(props) {
1833
+ const status = props.status === _copilotkit_core.ToolCallStatus.Complete ? "complete" : props.status === _copilotkit_core.ToolCallStatus.Executing ? "executing" : "inProgress";
1834
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DefaultToolCallRenderer, {
1835
+ name: props.name,
1836
+ parameters: props.args,
1837
+ status,
1838
+ result: props.result
1839
+ });
1840
+ }
1538
1841
 
1539
1842
  //#endregion
1540
1843
  //#region src/v2/components/CopilotKitInspector.tsx
@@ -2791,18 +3094,6 @@ window.parent.postMessage({jsonrpc:"2.0",method:"ui/notifications/sandbox-proxy-
2791
3094
  return (_ref = (_ref2 = (_ref3 = (_operation$createSurf = operation === null || operation === void 0 || (_operation$createSurf2 = operation.createSurface) === null || _operation$createSurf2 === void 0 ? void 0 : _operation$createSurf2.surfaceId) !== null && _operation$createSurf !== void 0 ? _operation$createSurf : operation === null || operation === void 0 || (_operation$updateComp = operation.updateComponents) === null || _operation$updateComp === void 0 ? void 0 : _operation$updateComp.surfaceId) !== null && _ref3 !== void 0 ? _ref3 : operation === null || operation === void 0 || (_operation$updateData = operation.updateDataModel) === null || _operation$updateData === void 0 ? void 0 : _operation$updateData.surfaceId) !== null && _ref2 !== void 0 ? _ref2 : operation === null || operation === void 0 || (_operation$deleteSurf = operation.deleteSurface) === null || _operation$deleteSurf === void 0 ? void 0 : _operation$deleteSurf.surfaceId) !== null && _ref !== void 0 ? _ref : null;
2792
3095
  }
2793
3096
 
2794
- //#endregion
2795
- //#region src/v2/types/defineToolCallRenderer.ts
2796
- function defineToolCallRenderer(def) {
2797
- const argsSchema = def.name === "*" && !def.args ? zod.z.any() : def.args;
2798
- return {
2799
- name: def.name,
2800
- args: argsSchema,
2801
- render: def.render,
2802
- ...def.agentId ? { agentId: def.agentId } : {}
2803
- };
2804
- }
2805
-
2806
3097
  //#endregion
2807
3098
  //#region src/v2/a2ui/A2UIToolCallRenderer.tsx
2808
3099
  /**
@@ -3640,374 +3931,112 @@ window.parent.postMessage({jsonrpc:"2.0",method:"ui/notifications/sandbox-proxy-
3640
3931
  agentId,
3641
3932
  copilotkit,
3642
3933
  findRenderer
3643
- ]);
3644
- return (0, react.useMemo)(() => ({
3645
- renderActivityMessage,
3646
- findRenderer
3647
- }), [renderActivityMessage, findRenderer]);
3648
- }
3649
-
3650
- //#endregion
3651
- //#region src/v2/hooks/use-frontend-tool.tsx
3652
- const EMPTY_DEPS$1 = [];
3653
- function useFrontendTool(tool, deps) {
3654
- const { copilotkit } = useCopilotKit();
3655
- const extraDeps = deps !== null && deps !== void 0 ? deps : EMPTY_DEPS$1;
3656
- (0, react.useEffect)(() => {
3657
- const name = tool.name;
3658
- if (copilotkit.getTool({
3659
- toolName: name,
3660
- agentId: tool.agentId
3661
- })) {
3662
- console.warn(`Tool '${name}' already exists for agent '${tool.agentId || "global"}'. Overriding with latest registration.`);
3663
- copilotkit.removeTool(name, tool.agentId);
3664
- }
3665
- copilotkit.addTool(tool);
3666
- if (tool.render) copilotkit.addHookRenderToolCall({
3667
- name,
3668
- args: tool.parameters,
3669
- agentId: tool.agentId,
3670
- render: tool.render
3671
- });
3672
- return () => {
3673
- copilotkit.removeTool(name, tool.agentId);
3674
- };
3675
- }, [
3676
- tool.name,
3677
- tool.available,
3678
- copilotkit,
3679
- JSON.stringify(extraDeps)
3680
- ]);
3681
- }
3682
-
3683
- //#endregion
3684
- //#region src/v2/hooks/use-component.tsx
3685
- /**
3686
- * Registers a React component as a frontend tool renderer in chat.
3687
- *
3688
- * This hook is a convenience wrapper around `useFrontendTool` that:
3689
- * - builds a model-facing tool description,
3690
- * - forwards optional schema parameters (any Standard Schema V1 compatible library),
3691
- * - renders your component with tool call parameters.
3692
- *
3693
- * Use this when you want to display a typed visual component for a tool call
3694
- * without manually wiring a full frontend tool object.
3695
- *
3696
- * When `parameters` is provided, render props are inferred from the schema.
3697
- * When omitted, the render component may accept any props.
3698
- *
3699
- * @typeParam TSchema - Schema describing tool parameters, or `undefined` when no schema is given.
3700
- * @param config - Tool registration config.
3701
- * @param deps - Optional dependencies to refresh registration (same semantics as `useEffect`).
3702
- *
3703
- * @example
3704
- * ```tsx
3705
- * // Without parameters — render accepts any props
3706
- * useComponent({
3707
- * name: "showGreeting",
3708
- * render: ({ message }: { message: string }) => <div>{message}</div>,
3709
- * });
3710
- * ```
3711
- *
3712
- * @example
3713
- * ```tsx
3714
- * // With parameters — render props inferred from schema
3715
- * useComponent({
3716
- * name: "showWeatherCard",
3717
- * parameters: z.object({ city: z.string() }),
3718
- * render: ({ city }) => <div>{city}</div>,
3719
- * });
3720
- * ```
3721
- *
3722
- * @example
3723
- * ```tsx
3724
- * useComponent(
3725
- * {
3726
- * name: "renderProfile",
3727
- * parameters: z.object({ userId: z.string() }),
3728
- * render: ProfileCard,
3729
- * agentId: "support-agent",
3730
- * },
3731
- * [selectedAgentId],
3732
- * );
3733
- * ```
3734
- */
3735
- function useComponent(config, deps) {
3736
- const prefix = `Use this tool to display the "${config.name}" component in the chat. This tool renders a visual UI component for the user.`;
3737
- const fullDescription = config.description ? `${prefix}\n\n${config.description}` : prefix;
3738
- useFrontendTool({
3739
- name: config.name,
3740
- description: fullDescription,
3741
- parameters: config.parameters,
3742
- render: ({ args }) => {
3743
- const Component = config.render;
3744
- return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Component, { ...args });
3745
- },
3746
- agentId: config.agentId
3747
- }, deps);
3748
- }
3749
-
3750
- //#endregion
3751
- //#region src/v2/hooks/use-render-tool.tsx
3752
- const EMPTY_DEPS = [];
3753
- /**
3754
- * Registers a renderer entry in CopilotKit's `renderToolCalls` registry.
3755
- *
3756
- * Key behavior:
3757
- * - deduplicates by `agentId:name` (latest registration wins),
3758
- * - keeps renderer entries on cleanup so historical chat tool calls can still render,
3759
- * - refreshes registration when `deps` change.
3760
- *
3761
- * @typeParam S - Schema type describing tool call parameters.
3762
- * @param config - Renderer config for wildcard or named tools.
3763
- * @param deps - Optional dependencies to refresh registration.
3764
- *
3765
- * @example
3766
- * ```tsx
3767
- * useRenderTool(
3768
- * {
3769
- * name: "searchDocs",
3770
- * parameters: z.object({ query: z.string() }),
3771
- * render: ({ status, parameters, result }) => {
3772
- * if (status === "executing") return <div>Searching {parameters.query}</div>;
3773
- * if (status === "complete") return <div>{result}</div>;
3774
- * return <div>Preparing...</div>;
3775
- * },
3776
- * },
3777
- * [],
3778
- * );
3779
- * ```
3780
- *
3781
- * @example
3782
- * ```tsx
3783
- * useRenderTool(
3784
- * {
3785
- * name: "summarize",
3786
- * parameters: z.object({ text: z.string() }),
3787
- * agentId: "research-agent",
3788
- * render: ({ name, status }) => <div>{name}: {status}</div>,
3789
- * },
3790
- * [selectedAgentId],
3791
- * );
3792
- * ```
3793
- */
3794
- function useRenderTool(config, deps) {
3934
+ ]);
3935
+ return (0, react.useMemo)(() => ({
3936
+ renderActivityMessage,
3937
+ findRenderer
3938
+ }), [renderActivityMessage, findRenderer]);
3939
+ }
3940
+
3941
+ //#endregion
3942
+ //#region src/v2/hooks/use-frontend-tool.tsx
3943
+ const EMPTY_DEPS = [];
3944
+ function useFrontendTool(tool, deps) {
3795
3945
  const { copilotkit } = useCopilotKit();
3796
3946
  const extraDeps = deps !== null && deps !== void 0 ? deps : EMPTY_DEPS;
3797
3947
  (0, react.useEffect)(() => {
3798
- const renderer = config.name === "*" && !config.parameters ? defineToolCallRenderer({
3799
- name: "*",
3800
- render: (props) => config.render({
3801
- ...props,
3802
- parameters: props.args
3803
- }),
3804
- ...config.agentId ? { agentId: config.agentId } : {}
3805
- }) : defineToolCallRenderer({
3806
- name: config.name,
3807
- args: config.parameters,
3808
- render: (props) => config.render({
3809
- ...props,
3810
- parameters: props.args
3811
- }),
3812
- ...config.agentId ? { agentId: config.agentId } : {}
3948
+ const name = tool.name;
3949
+ if (copilotkit.getTool({
3950
+ toolName: name,
3951
+ agentId: tool.agentId
3952
+ })) {
3953
+ console.warn(`Tool '${name}' already exists for agent '${tool.agentId || "global"}'. Overriding with latest registration.`);
3954
+ copilotkit.removeTool(name, tool.agentId);
3955
+ }
3956
+ copilotkit.addTool(tool);
3957
+ if (tool.render) copilotkit.addHookRenderToolCall({
3958
+ name,
3959
+ args: tool.parameters,
3960
+ agentId: tool.agentId,
3961
+ render: tool.render
3813
3962
  });
3814
- copilotkit.addHookRenderToolCall(renderer);
3963
+ return () => {
3964
+ copilotkit.removeTool(name, tool.agentId);
3965
+ };
3815
3966
  }, [
3816
- config.name,
3967
+ tool.name,
3968
+ tool.available,
3817
3969
  copilotkit,
3818
3970
  JSON.stringify(extraDeps)
3819
3971
  ]);
3820
3972
  }
3821
3973
 
3822
3974
  //#endregion
3823
- //#region src/v2/hooks/use-default-render-tool.tsx
3975
+ //#region src/v2/hooks/use-component.tsx
3824
3976
  /**
3825
- * Registers a wildcard (`"*"`) tool-call renderer via `useRenderTool`.
3977
+ * Registers a React component as a frontend tool renderer in chat.
3826
3978
  *
3827
- * - Call with no config to use CopilotKit's built-in default tool-call card.
3828
- * - Pass `config.render` to replace the default UI with your own fallback renderer.
3979
+ * This hook is a convenience wrapper around `useFrontendTool` that:
3980
+ * - builds a model-facing tool description,
3981
+ * - forwards optional schema parameters (any Standard Schema V1 compatible library),
3982
+ * - renders your component with tool call parameters.
3829
3983
  *
3830
- * This is useful when you want a generic renderer for tools that do not have a
3831
- * dedicated `useRenderTool({ name: "..." })` registration.
3984
+ * Use this when you want to display a typed visual component for a tool call
3985
+ * without manually wiring a full frontend tool object.
3832
3986
  *
3833
- * @param config - Optional custom wildcard render function.
3834
- * @param deps - Optional dependencies to refresh registration.
3987
+ * When `parameters` is provided, render props are inferred from the schema.
3988
+ * When omitted, the render component may accept any props.
3989
+ *
3990
+ * @typeParam TSchema - Schema describing tool parameters, or `undefined` when no schema is given.
3991
+ * @param config - Tool registration config.
3992
+ * @param deps - Optional dependencies to refresh registration (same semantics as `useEffect`).
3835
3993
  *
3836
3994
  * @example
3837
3995
  * ```tsx
3838
- * useDefaultRenderTool();
3996
+ * // Without parameters — render accepts any props
3997
+ * useComponent({
3998
+ * name: "showGreeting",
3999
+ * render: ({ message }: { message: string }) => <div>{message}</div>,
4000
+ * });
3839
4001
  * ```
3840
4002
  *
3841
4003
  * @example
3842
4004
  * ```tsx
3843
- * useDefaultRenderTool({
3844
- * render: ({ name, status }) => <div>{name}: {status}</div>,
4005
+ * // With parameters — render props inferred from schema
4006
+ * useComponent({
4007
+ * name: "showWeatherCard",
4008
+ * parameters: z.object({ city: z.string() }),
4009
+ * render: ({ city }) => <div>{city}</div>,
3845
4010
  * });
3846
4011
  * ```
3847
4012
  *
3848
4013
  * @example
3849
4014
  * ```tsx
3850
- * useDefaultRenderTool(
4015
+ * useComponent(
3851
4016
  * {
3852
- * render: ({ name, result }) => (
3853
- * <ToolEventRow title={name} payload={result} compact={compactMode} />
3854
- * ),
4017
+ * name: "renderProfile",
4018
+ * parameters: z.object({ userId: z.string() }),
4019
+ * render: ProfileCard,
4020
+ * agentId: "support-agent",
3855
4021
  * },
3856
- * [compactMode],
4022
+ * [selectedAgentId],
3857
4023
  * );
3858
4024
  * ```
3859
4025
  */
3860
- function useDefaultRenderTool(config, deps) {
3861
- var _config$render;
3862
- useRenderTool({
3863
- name: "*",
3864
- render: (_config$render = config === null || config === void 0 ? void 0 : config.render) !== null && _config$render !== void 0 ? _config$render : DefaultToolCallRenderer
3865
- }, deps);
3866
- }
3867
- function DefaultToolCallRenderer({ name, parameters, status, result }) {
3868
- const [isExpanded, setIsExpanded] = (0, react.useState)(false);
3869
- const statusString = String(status);
3870
- const isActive = statusString === "inProgress" || statusString === "executing";
3871
- const isComplete = statusString === "complete";
3872
- return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3873
- style: {
3874
- marginTop: "8px",
3875
- paddingBottom: "8px"
4026
+ function useComponent(config, deps) {
4027
+ const prefix = `Use this tool to display the "${config.name}" component in the chat. This tool renders a visual UI component for the user.`;
4028
+ const fullDescription = config.description ? `${prefix}\n\n${config.description}` : prefix;
4029
+ useFrontendTool({
4030
+ name: config.name,
4031
+ description: fullDescription,
4032
+ parameters: config.parameters,
4033
+ render: ({ args }) => {
4034
+ const Component = config.render;
4035
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Component, { ...args });
3876
4036
  },
3877
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3878
- style: {
3879
- borderRadius: "12px",
3880
- border: "1px solid #e4e4e7",
3881
- backgroundColor: "#fafafa",
3882
- padding: "14px 16px"
3883
- },
3884
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3885
- onClick: () => setIsExpanded(!isExpanded),
3886
- style: {
3887
- display: "flex",
3888
- alignItems: "center",
3889
- justifyContent: "space-between",
3890
- gap: "10px",
3891
- cursor: "pointer",
3892
- userSelect: "none"
3893
- },
3894
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3895
- style: {
3896
- display: "flex",
3897
- alignItems: "center",
3898
- gap: "8px",
3899
- minWidth: 0
3900
- },
3901
- children: [
3902
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
3903
- style: {
3904
- height: "14px",
3905
- width: "14px",
3906
- color: "#71717a",
3907
- transition: "transform 0.15s",
3908
- transform: isExpanded ? "rotate(90deg)" : "rotate(0deg)",
3909
- flexShrink: 0
3910
- },
3911
- fill: "none",
3912
- viewBox: "0 0 24 24",
3913
- strokeWidth: 2,
3914
- stroke: "currentColor",
3915
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
3916
- strokeLinecap: "round",
3917
- strokeLinejoin: "round",
3918
- d: "M8.25 4.5l7.5 7.5-7.5 7.5"
3919
- })
3920
- }),
3921
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { style: {
3922
- display: "inline-block",
3923
- height: "8px",
3924
- width: "8px",
3925
- borderRadius: "50%",
3926
- backgroundColor: isActive ? "#f59e0b" : isComplete ? "#10b981" : "#a1a1aa",
3927
- flexShrink: 0
3928
- } }),
3929
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3930
- style: {
3931
- fontSize: "13px",
3932
- fontWeight: 600,
3933
- color: "#18181b",
3934
- overflow: "hidden",
3935
- textOverflow: "ellipsis",
3936
- whiteSpace: "nowrap"
3937
- },
3938
- children: name
3939
- })
3940
- ]
3941
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3942
- style: {
3943
- display: "inline-flex",
3944
- alignItems: "center",
3945
- borderRadius: "9999px",
3946
- padding: "2px 8px",
3947
- fontSize: "11px",
3948
- fontWeight: 500,
3949
- backgroundColor: isActive ? "#fef3c7" : isComplete ? "#d1fae5" : "#f4f4f5",
3950
- color: isActive ? "#92400e" : isComplete ? "#065f46" : "#3f3f46",
3951
- flexShrink: 0
3952
- },
3953
- children: isActive ? "Running" : isComplete ? "Done" : status
3954
- })]
3955
- }), isExpanded && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3956
- style: {
3957
- marginTop: "12px",
3958
- display: "grid",
3959
- gap: "12px"
3960
- },
3961
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3962
- style: {
3963
- fontSize: "10px",
3964
- textTransform: "uppercase",
3965
- letterSpacing: "0.05em",
3966
- color: "#71717a"
3967
- },
3968
- children: "Arguments"
3969
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("pre", {
3970
- style: {
3971
- marginTop: "6px",
3972
- maxHeight: "200px",
3973
- overflow: "auto",
3974
- borderRadius: "6px",
3975
- backgroundColor: "#f4f4f5",
3976
- padding: "10px",
3977
- fontSize: "11px",
3978
- lineHeight: 1.6,
3979
- color: "#27272a",
3980
- whiteSpace: "pre-wrap",
3981
- wordBreak: "break-word"
3982
- },
3983
- children: JSON.stringify(parameters !== null && parameters !== void 0 ? parameters : {}, null, 2)
3984
- })] }), result !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3985
- style: {
3986
- fontSize: "10px",
3987
- textTransform: "uppercase",
3988
- letterSpacing: "0.05em",
3989
- color: "#71717a"
3990
- },
3991
- children: "Result"
3992
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("pre", {
3993
- style: {
3994
- marginTop: "6px",
3995
- maxHeight: "200px",
3996
- overflow: "auto",
3997
- borderRadius: "6px",
3998
- backgroundColor: "#f4f4f5",
3999
- padding: "10px",
4000
- fontSize: "11px",
4001
- lineHeight: 1.6,
4002
- color: "#27272a",
4003
- whiteSpace: "pre-wrap",
4004
- wordBreak: "break-word"
4005
- },
4006
- children: typeof result === "string" ? result : JSON.stringify(result, null, 2)
4007
- })] })]
4008
- })]
4009
- })
4010
- });
4037
+ agentId: config.agentId,
4038
+ followUp: config.followUp
4039
+ }, deps);
4011
4040
  }
4012
4041
 
4013
4042
  //#endregion
@@ -7564,18 +7593,19 @@ window.parent.postMessage({jsonrpc:"2.0",method:"ui/notifications/sandbox-proxy-
7564
7593
  //#region src/v2/components/chat/CopilotSidebarView.tsx
7565
7594
  const DEFAULT_SIDEBAR_WIDTH = 480;
7566
7595
  const SIDEBAR_TRANSITION_MS = 260;
7567
- function CopilotSidebarView({ header, toggleButton, width, defaultOpen = true, ...props }) {
7596
+ function CopilotSidebarView({ header, toggleButton, width, defaultOpen = true, position = "right", ...props }) {
7568
7597
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CopilotChatConfigurationProvider, {
7569
7598
  isModalDefaultOpen: defaultOpen,
7570
7599
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CopilotSidebarViewInternal, {
7571
7600
  header,
7572
7601
  toggleButton,
7573
7602
  width,
7603
+ position,
7574
7604
  ...props
7575
7605
  })
7576
7606
  });
7577
7607
  }
7578
- function CopilotSidebarViewInternal({ header, toggleButton, width, ...props }) {
7608
+ function CopilotSidebarViewInternal({ header, toggleButton, width, position = "right", ...props }) {
7579
7609
  var _configuration$isModa;
7580
7610
  const configuration = useCopilotChatConfiguration();
7581
7611
  const isSidebarOpen = (_configuration$isModa = configuration === null || configuration === void 0 ? void 0 : configuration.isModalOpen) !== null && _configuration$isModa !== void 0 ? _configuration$isModa : false;
@@ -7610,26 +7640,33 @@ window.parent.postMessage({jsonrpc:"2.0",method:"ui/notifications/sandbox-proxy-
7610
7640
  (0, react.useLayoutEffect)(() => {
7611
7641
  if (typeof window === "undefined" || typeof window.matchMedia !== "function") return;
7612
7642
  if (!window.matchMedia("(min-width: 768px)").matches) return;
7643
+ const marginStyleProp = position === "left" ? "marginInlineStart" : "marginInlineEnd";
7644
+ const transitionCssProp = position === "left" ? "margin-inline-start" : "margin-inline-end";
7613
7645
  if (isSidebarOpen) {
7614
- if (hasMounted.current) document.body.style.transition = `margin-inline-end ${SIDEBAR_TRANSITION_MS}ms ease`;
7615
- document.body.style.marginInlineEnd = widthToMargin(sidebarWidth);
7646
+ if (hasMounted.current) document.body.style.transition = `${transitionCssProp} ${SIDEBAR_TRANSITION_MS}ms ease`;
7647
+ document.body.style[marginStyleProp] = widthToMargin(sidebarWidth);
7616
7648
  } else if (hasMounted.current) {
7617
- document.body.style.transition = `margin-inline-end ${SIDEBAR_TRANSITION_MS}ms ease`;
7618
- document.body.style.marginInlineEnd = "";
7649
+ document.body.style.transition = `${transitionCssProp} ${SIDEBAR_TRANSITION_MS}ms ease`;
7650
+ document.body.style[marginStyleProp] = "";
7619
7651
  }
7620
7652
  hasMounted.current = true;
7621
7653
  return () => {
7622
- document.body.style.marginInlineEnd = "";
7654
+ document.body.style[marginStyleProp] = "";
7623
7655
  document.body.style.transition = "";
7624
7656
  };
7625
- }, [isSidebarOpen, sidebarWidth]);
7657
+ }, [
7658
+ isSidebarOpen,
7659
+ sidebarWidth,
7660
+ position
7661
+ ]);
7626
7662
  const headerElement = renderSlot(header, CopilotModalHeader, {});
7627
- return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [renderSlot(toggleButton, CopilotChatToggleButton, {}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("aside", {
7663
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [renderSlot(toggleButton, CopilotChatToggleButton, position === "left" ? { className: "cpk:left-6 cpk:right-auto" } : {}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("aside", {
7628
7664
  ref: sidebarRef,
7629
7665
  "data-copilotkit": true,
7630
7666
  "data-testid": "copilot-sidebar",
7631
7667
  "data-copilot-sidebar": true,
7632
- className: cn("copilotKitSidebar copilotKitWindow", "cpk:fixed cpk:right-0 cpk:top-0 cpk:z-[1200] cpk:flex", "cpk:h-[100vh] cpk:h-[100dvh] cpk:max-h-screen", "cpk:w-full", "cpk:border-l cpk:border-border cpk:bg-background cpk:text-foreground cpk:shadow-xl", "cpk:transition-transform cpk:duration-300 cpk:ease-out", isSidebarOpen ? "cpk:translate-x-0" : "cpk:translate-x-full cpk:pointer-events-none"),
7668
+ "data-position": position,
7669
+ className: cn("copilotKitSidebar copilotKitWindow", "cpk:fixed cpk:top-0 cpk:z-[1200] cpk:flex", position === "left" ? "cpk:left-0" : "cpk:right-0", "cpk:h-[100vh] cpk:h-[100dvh] cpk:max-h-screen", "cpk:w-full", position === "left" ? "cpk:border-r" : "cpk:border-l", "cpk:border-border cpk:bg-background cpk:text-foreground cpk:shadow-xl", "cpk:transition-transform cpk:duration-300 cpk:ease-out", isSidebarOpen ? "cpk:translate-x-0" : position === "left" ? "cpk:-translate-x-full cpk:pointer-events-none" : "cpk:translate-x-full cpk:pointer-events-none"),
7633
7670
  style: {
7634
7671
  ["--sidebar-width"]: widthToCss(sidebarWidth),
7635
7672
  paddingTop: "env(safe-area-inset-top)",
@@ -7838,7 +7875,7 @@ window.parent.postMessage({jsonrpc:"2.0",method:"ui/notifications/sandbox-proxy-
7838
7875
 
7839
7876
  //#endregion
7840
7877
  //#region src/v2/components/chat/CopilotSidebar.tsx
7841
- function CopilotSidebar({ header, toggleButton, defaultOpen, width, ...chatProps }) {
7878
+ function CopilotSidebar({ header, toggleButton, defaultOpen, width, position, ...chatProps }) {
7842
7879
  const { checkFeature } = useLicenseContext();
7843
7880
  const isSidebarLicensed = checkFeature("sidebar");
7844
7881
  (0, react.useEffect)(() => {
@@ -7846,13 +7883,14 @@ window.parent.postMessage({jsonrpc:"2.0",method:"ui/notifications/sandbox-proxy-
7846
7883
  }, [isSidebarLicensed]);
7847
7884
  const SidebarViewOverride = (0, react.useMemo)(() => {
7848
7885
  const Component = (viewProps) => {
7849
- const { header: viewHeader, toggleButton: viewToggleButton, width: viewWidth, defaultOpen: viewDefaultOpen, ...restProps } = viewProps;
7886
+ const { header: viewHeader, toggleButton: viewToggleButton, width: viewWidth, defaultOpen: viewDefaultOpen, position: viewPosition, ...restProps } = viewProps;
7850
7887
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CopilotSidebarView, {
7851
7888
  ...restProps,
7852
7889
  header: header !== null && header !== void 0 ? header : viewHeader,
7853
7890
  toggleButton: toggleButton !== null && toggleButton !== void 0 ? toggleButton : viewToggleButton,
7854
7891
  width: width !== null && width !== void 0 ? width : viewWidth,
7855
- defaultOpen: defaultOpen !== null && defaultOpen !== void 0 ? defaultOpen : viewDefaultOpen
7892
+ defaultOpen: defaultOpen !== null && defaultOpen !== void 0 ? defaultOpen : viewDefaultOpen,
7893
+ position: position !== null && position !== void 0 ? position : viewPosition
7856
7894
  });
7857
7895
  };
7858
7896
  return Object.assign(Component, CopilotChatView_default);
@@ -7860,7 +7898,8 @@ window.parent.postMessage({jsonrpc:"2.0",method:"ui/notifications/sandbox-proxy-
7860
7898
  header,
7861
7899
  toggleButton,
7862
7900
  width,
7863
- defaultOpen
7901
+ defaultOpen,
7902
+ position
7864
7903
  ]);
7865
7904
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [!isSidebarLicensed && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(InlineFeatureWarning, { featureName: "Sidebar" }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CopilotChat, {
7866
7905
  welcomeScreen: CopilotSidebarView.WelcomeScreen,