@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
@@ -1445,6 +1445,300 @@ const LicenseContext = (0, react.createContext)({
1445
1445
  });
1446
1446
  const useLicenseContext = () => (0, react.useContext)(LicenseContext);
1447
1447
 
1448
+ //#endregion
1449
+ //#region src/v2/types/defineToolCallRenderer.ts
1450
+ function defineToolCallRenderer(def) {
1451
+ const argsSchema = def.name === "*" && !def.args ? zod.z.any() : def.args;
1452
+ return {
1453
+ name: def.name,
1454
+ args: argsSchema,
1455
+ render: def.render,
1456
+ ...def.agentId ? { agentId: def.agentId } : {}
1457
+ };
1458
+ }
1459
+
1460
+ //#endregion
1461
+ //#region src/v2/hooks/use-render-tool.tsx
1462
+ const EMPTY_DEPS$1 = [];
1463
+ /**
1464
+ * Registers a renderer entry in CopilotKit's `renderToolCalls` registry.
1465
+ *
1466
+ * Key behavior:
1467
+ * - deduplicates by `agentId:name` (latest registration wins),
1468
+ * - keeps renderer entries on cleanup so historical chat tool calls can still render,
1469
+ * - refreshes registration when `deps` change.
1470
+ *
1471
+ * @typeParam S - Schema type describing tool call parameters.
1472
+ * @param config - Renderer config for wildcard or named tools.
1473
+ * @param deps - Optional dependencies to refresh registration.
1474
+ *
1475
+ * @example
1476
+ * ```tsx
1477
+ * useRenderTool(
1478
+ * {
1479
+ * name: "searchDocs",
1480
+ * parameters: z.object({ query: z.string() }),
1481
+ * render: ({ status, parameters, result }) => {
1482
+ * if (status === "executing") return <div>Searching {parameters.query}</div>;
1483
+ * if (status === "complete") return <div>{result}</div>;
1484
+ * return <div>Preparing...</div>;
1485
+ * },
1486
+ * },
1487
+ * [],
1488
+ * );
1489
+ * ```
1490
+ *
1491
+ * @example
1492
+ * ```tsx
1493
+ * useRenderTool(
1494
+ * {
1495
+ * name: "summarize",
1496
+ * parameters: z.object({ text: z.string() }),
1497
+ * agentId: "research-agent",
1498
+ * render: ({ name, status }) => <div>{name}: {status}</div>,
1499
+ * },
1500
+ * [selectedAgentId],
1501
+ * );
1502
+ * ```
1503
+ */
1504
+ function useRenderTool(config, deps) {
1505
+ const { copilotkit } = useCopilotKit();
1506
+ const extraDeps = deps ?? EMPTY_DEPS$1;
1507
+ (0, react.useEffect)(() => {
1508
+ const renderer = config.name === "*" && !config.parameters ? defineToolCallRenderer({
1509
+ name: "*",
1510
+ render: (props) => config.render({
1511
+ ...props,
1512
+ parameters: props.args
1513
+ }),
1514
+ ...config.agentId ? { agentId: config.agentId } : {}
1515
+ }) : defineToolCallRenderer({
1516
+ name: config.name,
1517
+ args: config.parameters,
1518
+ render: (props) => config.render({
1519
+ ...props,
1520
+ parameters: props.args
1521
+ }),
1522
+ ...config.agentId ? { agentId: config.agentId } : {}
1523
+ });
1524
+ copilotkit.addHookRenderToolCall(renderer);
1525
+ }, [
1526
+ config.name,
1527
+ copilotkit,
1528
+ JSON.stringify(extraDeps)
1529
+ ]);
1530
+ }
1531
+
1532
+ //#endregion
1533
+ //#region src/v2/hooks/use-default-render-tool.tsx
1534
+ /**
1535
+ * Registers a wildcard (`"*"`) tool-call renderer via `useRenderTool`.
1536
+ *
1537
+ * - Call with no config to use CopilotKit's built-in default tool-call card.
1538
+ * - Pass `config.render` to replace the default UI with your own fallback renderer.
1539
+ *
1540
+ * This is useful when you want a generic renderer for tools that do not have a
1541
+ * dedicated `useRenderTool({ name: "..." })` registration.
1542
+ *
1543
+ * @param config - Optional custom wildcard render function.
1544
+ * @param deps - Optional dependencies to refresh registration.
1545
+ *
1546
+ * @example
1547
+ * ```tsx
1548
+ * useDefaultRenderTool();
1549
+ * ```
1550
+ *
1551
+ * @example
1552
+ * ```tsx
1553
+ * useDefaultRenderTool({
1554
+ * render: ({ name, status }) => <div>{name}: {status}</div>,
1555
+ * });
1556
+ * ```
1557
+ *
1558
+ * @example
1559
+ * ```tsx
1560
+ * useDefaultRenderTool(
1561
+ * {
1562
+ * render: ({ name, result }) => (
1563
+ * <ToolEventRow title={name} payload={result} compact={compactMode} />
1564
+ * ),
1565
+ * },
1566
+ * [compactMode],
1567
+ * );
1568
+ * ```
1569
+ */
1570
+ function useDefaultRenderTool(config, deps) {
1571
+ useRenderTool({
1572
+ name: "*",
1573
+ render: config?.render ?? DefaultToolCallRenderer
1574
+ }, deps);
1575
+ }
1576
+ function DefaultToolCallRenderer({ name, parameters, status, result }) {
1577
+ const [isExpanded, setIsExpanded] = (0, react.useState)(false);
1578
+ const statusString = String(status);
1579
+ const isActive = statusString === "inProgress" || statusString === "executing";
1580
+ const isComplete = statusString === "complete";
1581
+ const statusLabel = isActive ? "Running" : isComplete ? "Done" : status;
1582
+ const dotColor = isActive ? "#f59e0b" : isComplete ? "#10b981" : "#a1a1aa";
1583
+ const badgeBg = isActive ? "#fef3c7" : isComplete ? "#d1fae5" : "#f4f4f5";
1584
+ const badgeColor = isActive ? "#92400e" : isComplete ? "#065f46" : "#3f3f46";
1585
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1586
+ "data-testid": "copilot-tool-render",
1587
+ "data-tool-name": name,
1588
+ "data-status": statusString,
1589
+ "data-args": safeStringifyForAttr(parameters),
1590
+ "data-result": safeStringifyForAttr(result),
1591
+ style: {
1592
+ marginTop: "8px",
1593
+ paddingBottom: "8px"
1594
+ },
1595
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1596
+ style: {
1597
+ borderRadius: "12px",
1598
+ border: "1px solid #e4e4e7",
1599
+ backgroundColor: "#fafafa",
1600
+ padding: "14px 16px"
1601
+ },
1602
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1603
+ onClick: () => setIsExpanded(!isExpanded),
1604
+ style: {
1605
+ display: "flex",
1606
+ alignItems: "center",
1607
+ justifyContent: "space-between",
1608
+ gap: "10px",
1609
+ cursor: "pointer",
1610
+ userSelect: "none"
1611
+ },
1612
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1613
+ style: {
1614
+ display: "flex",
1615
+ alignItems: "center",
1616
+ gap: "8px",
1617
+ minWidth: 0
1618
+ },
1619
+ children: [
1620
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
1621
+ style: {
1622
+ height: "14px",
1623
+ width: "14px",
1624
+ color: "#71717a",
1625
+ transition: "transform 0.15s",
1626
+ transform: isExpanded ? "rotate(90deg)" : "rotate(0deg)",
1627
+ flexShrink: 0
1628
+ },
1629
+ fill: "none",
1630
+ viewBox: "0 0 24 24",
1631
+ strokeWidth: 2,
1632
+ stroke: "currentColor",
1633
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
1634
+ strokeLinecap: "round",
1635
+ strokeLinejoin: "round",
1636
+ d: "M8.25 4.5l7.5 7.5-7.5 7.5"
1637
+ })
1638
+ }),
1639
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { style: {
1640
+ display: "inline-block",
1641
+ height: "8px",
1642
+ width: "8px",
1643
+ borderRadius: "50%",
1644
+ backgroundColor: dotColor,
1645
+ flexShrink: 0
1646
+ } }),
1647
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1648
+ "data-testid": "copilot-tool-render-name",
1649
+ style: {
1650
+ fontSize: "13px",
1651
+ fontWeight: 600,
1652
+ color: "#18181b",
1653
+ overflow: "hidden",
1654
+ textOverflow: "ellipsis",
1655
+ whiteSpace: "nowrap"
1656
+ },
1657
+ children: name
1658
+ })
1659
+ ]
1660
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1661
+ "data-testid": "copilot-tool-render-status",
1662
+ style: {
1663
+ display: "inline-flex",
1664
+ alignItems: "center",
1665
+ borderRadius: "9999px",
1666
+ padding: "2px 8px",
1667
+ fontSize: "11px",
1668
+ fontWeight: 500,
1669
+ backgroundColor: badgeBg,
1670
+ color: badgeColor,
1671
+ flexShrink: 0
1672
+ },
1673
+ children: statusLabel
1674
+ })]
1675
+ }), isExpanded && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1676
+ style: {
1677
+ marginTop: "12px",
1678
+ display: "grid",
1679
+ gap: "12px"
1680
+ },
1681
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1682
+ style: {
1683
+ fontSize: "10px",
1684
+ textTransform: "uppercase",
1685
+ letterSpacing: "0.05em",
1686
+ color: "#71717a"
1687
+ },
1688
+ children: "Arguments"
1689
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("pre", {
1690
+ style: {
1691
+ marginTop: "6px",
1692
+ maxHeight: "200px",
1693
+ overflow: "auto",
1694
+ borderRadius: "6px",
1695
+ backgroundColor: "#f4f4f5",
1696
+ padding: "10px",
1697
+ fontSize: "11px",
1698
+ lineHeight: 1.6,
1699
+ color: "#27272a",
1700
+ whiteSpace: "pre-wrap",
1701
+ wordBreak: "break-word"
1702
+ },
1703
+ children: JSON.stringify(parameters ?? {}, null, 2)
1704
+ })] }), result !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1705
+ style: {
1706
+ fontSize: "10px",
1707
+ textTransform: "uppercase",
1708
+ letterSpacing: "0.05em",
1709
+ color: "#71717a"
1710
+ },
1711
+ children: "Result"
1712
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("pre", {
1713
+ style: {
1714
+ marginTop: "6px",
1715
+ maxHeight: "200px",
1716
+ overflow: "auto",
1717
+ borderRadius: "6px",
1718
+ backgroundColor: "#f4f4f5",
1719
+ padding: "10px",
1720
+ fontSize: "11px",
1721
+ lineHeight: 1.6,
1722
+ color: "#27272a",
1723
+ whiteSpace: "pre-wrap",
1724
+ wordBreak: "break-word"
1725
+ },
1726
+ children: typeof result === "string" ? result : JSON.stringify(result, null, 2)
1727
+ })] })]
1728
+ })]
1729
+ })
1730
+ });
1731
+ }
1732
+ function safeStringifyForAttr(value) {
1733
+ if (value === void 0 || value === null) return "";
1734
+ if (typeof value === "string") return value;
1735
+ try {
1736
+ return JSON.stringify(value);
1737
+ } catch {
1738
+ return String(value);
1739
+ }
1740
+ }
1741
+
1448
1742
  //#endregion
1449
1743
  //#region src/v2/hooks/use-render-tool-call.tsx
1450
1744
  /**
@@ -1499,13 +1793,10 @@ function useRenderToolCall() {
1499
1793
  }, () => copilotkit.renderToolCalls, () => copilotkit.renderToolCalls);
1500
1794
  return (0, react.useCallback)(({ toolCall, toolMessage }) => {
1501
1795
  const exactMatches = renderToolCalls.filter((rc) => rc.name === toolCall.function.name);
1502
- const renderConfig = exactMatches.find((rc) => rc.agentId === agentId) || exactMatches.find((rc) => !rc.agentId) || exactMatches[0] || renderToolCalls.find((rc) => rc.name === "*");
1503
- if (!renderConfig) return null;
1504
- const RenderComponent = renderConfig.render;
1505
1796
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ToolCallRenderer, {
1506
1797
  toolCall,
1507
1798
  toolMessage,
1508
- RenderComponent,
1799
+ RenderComponent: (exactMatches.find((rc) => rc.agentId === agentId) || exactMatches.find((rc) => !rc.agentId) || exactMatches[0] || renderToolCalls.find((rc) => rc.name === "*"))?.render ?? defaultToolCallRenderAdapter,
1509
1800
  isExecuting: executingToolCallIds.has(toolCall.id)
1510
1801
  }, toolCall.id);
1511
1802
  }, [
@@ -1514,6 +1805,15 @@ function useRenderToolCall() {
1514
1805
  agentId
1515
1806
  ]);
1516
1807
  }
1808
+ function defaultToolCallRenderAdapter(props) {
1809
+ const status = props.status === _copilotkit_core.ToolCallStatus.Complete ? "complete" : props.status === _copilotkit_core.ToolCallStatus.Executing ? "executing" : "inProgress";
1810
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DefaultToolCallRenderer, {
1811
+ name: props.name,
1812
+ parameters: props.args,
1813
+ status,
1814
+ result: props.result
1815
+ });
1816
+ }
1517
1817
 
1518
1818
  //#endregion
1519
1819
  //#region src/v2/components/CopilotKitInspector.tsx
@@ -2753,18 +3053,6 @@ function getOperationSurfaceId(operation) {
2753
3053
  return operation?.createSurface?.surfaceId ?? operation?.updateComponents?.surfaceId ?? operation?.updateDataModel?.surfaceId ?? operation?.deleteSurface?.surfaceId ?? null;
2754
3054
  }
2755
3055
 
2756
- //#endregion
2757
- //#region src/v2/types/defineToolCallRenderer.ts
2758
- function defineToolCallRenderer(def) {
2759
- const argsSchema = def.name === "*" && !def.args ? zod.z.any() : def.args;
2760
- return {
2761
- name: def.name,
2762
- args: argsSchema,
2763
- render: def.render,
2764
- ...def.agentId ? { agentId: def.agentId } : {}
2765
- };
2766
- }
2767
-
2768
3056
  //#endregion
2769
3057
  //#region src/v2/a2ui/A2UIToolCallRenderer.tsx
2770
3058
  /**
@@ -3587,374 +3875,113 @@ function useRenderActivityMessage() {
3587
3875
  }, [
3588
3876
  agentId,
3589
3877
  copilotkit,
3590
- findRenderer
3591
- ]);
3592
- return (0, react.useMemo)(() => ({
3593
- renderActivityMessage,
3594
- findRenderer
3595
- }), [renderActivityMessage, findRenderer]);
3596
- }
3597
-
3598
- //#endregion
3599
- //#region src/v2/hooks/use-frontend-tool.tsx
3600
- const EMPTY_DEPS$1 = [];
3601
- function useFrontendTool(tool, deps) {
3602
- const { copilotkit } = useCopilotKit();
3603
- const extraDeps = deps ?? EMPTY_DEPS$1;
3604
- (0, react.useEffect)(() => {
3605
- const name = tool.name;
3606
- if (copilotkit.getTool({
3607
- toolName: name,
3608
- agentId: tool.agentId
3609
- })) {
3610
- console.warn(`Tool '${name}' already exists for agent '${tool.agentId || "global"}'. Overriding with latest registration.`);
3611
- copilotkit.removeTool(name, tool.agentId);
3612
- }
3613
- copilotkit.addTool(tool);
3614
- if (tool.render) copilotkit.addHookRenderToolCall({
3615
- name,
3616
- args: tool.parameters,
3617
- agentId: tool.agentId,
3618
- render: tool.render
3619
- });
3620
- return () => {
3621
- copilotkit.removeTool(name, tool.agentId);
3622
- };
3623
- }, [
3624
- tool.name,
3625
- tool.available,
3626
- copilotkit,
3627
- JSON.stringify(extraDeps)
3628
- ]);
3629
- }
3630
-
3631
- //#endregion
3632
- //#region src/v2/hooks/use-component.tsx
3633
- /**
3634
- * Registers a React component as a frontend tool renderer in chat.
3635
- *
3636
- * This hook is a convenience wrapper around `useFrontendTool` that:
3637
- * - builds a model-facing tool description,
3638
- * - forwards optional schema parameters (any Standard Schema V1 compatible library),
3639
- * - renders your component with tool call parameters.
3640
- *
3641
- * Use this when you want to display a typed visual component for a tool call
3642
- * without manually wiring a full frontend tool object.
3643
- *
3644
- * When `parameters` is provided, render props are inferred from the schema.
3645
- * When omitted, the render component may accept any props.
3646
- *
3647
- * @typeParam TSchema - Schema describing tool parameters, or `undefined` when no schema is given.
3648
- * @param config - Tool registration config.
3649
- * @param deps - Optional dependencies to refresh registration (same semantics as `useEffect`).
3650
- *
3651
- * @example
3652
- * ```tsx
3653
- * // Without parameters — render accepts any props
3654
- * useComponent({
3655
- * name: "showGreeting",
3656
- * render: ({ message }: { message: string }) => <div>{message}</div>,
3657
- * });
3658
- * ```
3659
- *
3660
- * @example
3661
- * ```tsx
3662
- * // With parameters — render props inferred from schema
3663
- * useComponent({
3664
- * name: "showWeatherCard",
3665
- * parameters: z.object({ city: z.string() }),
3666
- * render: ({ city }) => <div>{city}</div>,
3667
- * });
3668
- * ```
3669
- *
3670
- * @example
3671
- * ```tsx
3672
- * useComponent(
3673
- * {
3674
- * name: "renderProfile",
3675
- * parameters: z.object({ userId: z.string() }),
3676
- * render: ProfileCard,
3677
- * agentId: "support-agent",
3678
- * },
3679
- * [selectedAgentId],
3680
- * );
3681
- * ```
3682
- */
3683
- function useComponent(config, deps) {
3684
- const prefix = `Use this tool to display the "${config.name}" component in the chat. This tool renders a visual UI component for the user.`;
3685
- const fullDescription = config.description ? `${prefix}\n\n${config.description}` : prefix;
3686
- useFrontendTool({
3687
- name: config.name,
3688
- description: fullDescription,
3689
- parameters: config.parameters,
3690
- render: ({ args }) => {
3691
- const Component = config.render;
3692
- return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Component, { ...args });
3693
- },
3694
- agentId: config.agentId
3695
- }, deps);
3696
- }
3697
-
3698
- //#endregion
3699
- //#region src/v2/hooks/use-render-tool.tsx
3700
- const EMPTY_DEPS = [];
3701
- /**
3702
- * Registers a renderer entry in CopilotKit's `renderToolCalls` registry.
3703
- *
3704
- * Key behavior:
3705
- * - deduplicates by `agentId:name` (latest registration wins),
3706
- * - keeps renderer entries on cleanup so historical chat tool calls can still render,
3707
- * - refreshes registration when `deps` change.
3708
- *
3709
- * @typeParam S - Schema type describing tool call parameters.
3710
- * @param config - Renderer config for wildcard or named tools.
3711
- * @param deps - Optional dependencies to refresh registration.
3712
- *
3713
- * @example
3714
- * ```tsx
3715
- * useRenderTool(
3716
- * {
3717
- * name: "searchDocs",
3718
- * parameters: z.object({ query: z.string() }),
3719
- * render: ({ status, parameters, result }) => {
3720
- * if (status === "executing") return <div>Searching {parameters.query}</div>;
3721
- * if (status === "complete") return <div>{result}</div>;
3722
- * return <div>Preparing...</div>;
3723
- * },
3724
- * },
3725
- * [],
3726
- * );
3727
- * ```
3728
- *
3729
- * @example
3730
- * ```tsx
3731
- * useRenderTool(
3732
- * {
3733
- * name: "summarize",
3734
- * parameters: z.object({ text: z.string() }),
3735
- * agentId: "research-agent",
3736
- * render: ({ name, status }) => <div>{name}: {status}</div>,
3737
- * },
3738
- * [selectedAgentId],
3739
- * );
3740
- * ```
3741
- */
3742
- function useRenderTool(config, deps) {
3878
+ findRenderer
3879
+ ]);
3880
+ return (0, react.useMemo)(() => ({
3881
+ renderActivityMessage,
3882
+ findRenderer
3883
+ }), [renderActivityMessage, findRenderer]);
3884
+ }
3885
+
3886
+ //#endregion
3887
+ //#region src/v2/hooks/use-frontend-tool.tsx
3888
+ const EMPTY_DEPS = [];
3889
+ function useFrontendTool(tool, deps) {
3743
3890
  const { copilotkit } = useCopilotKit();
3744
3891
  const extraDeps = deps ?? EMPTY_DEPS;
3745
3892
  (0, react.useEffect)(() => {
3746
- const renderer = config.name === "*" && !config.parameters ? defineToolCallRenderer({
3747
- name: "*",
3748
- render: (props) => config.render({
3749
- ...props,
3750
- parameters: props.args
3751
- }),
3752
- ...config.agentId ? { agentId: config.agentId } : {}
3753
- }) : defineToolCallRenderer({
3754
- name: config.name,
3755
- args: config.parameters,
3756
- render: (props) => config.render({
3757
- ...props,
3758
- parameters: props.args
3759
- }),
3760
- ...config.agentId ? { agentId: config.agentId } : {}
3893
+ const name = tool.name;
3894
+ if (copilotkit.getTool({
3895
+ toolName: name,
3896
+ agentId: tool.agentId
3897
+ })) {
3898
+ console.warn(`Tool '${name}' already exists for agent '${tool.agentId || "global"}'. Overriding with latest registration.`);
3899
+ copilotkit.removeTool(name, tool.agentId);
3900
+ }
3901
+ copilotkit.addTool(tool);
3902
+ if (tool.render) copilotkit.addHookRenderToolCall({
3903
+ name,
3904
+ args: tool.parameters,
3905
+ agentId: tool.agentId,
3906
+ render: tool.render
3761
3907
  });
3762
- copilotkit.addHookRenderToolCall(renderer);
3908
+ return () => {
3909
+ copilotkit.removeTool(name, tool.agentId);
3910
+ };
3763
3911
  }, [
3764
- config.name,
3912
+ tool.name,
3913
+ tool.available,
3765
3914
  copilotkit,
3766
3915
  JSON.stringify(extraDeps)
3767
3916
  ]);
3768
3917
  }
3769
3918
 
3770
3919
  //#endregion
3771
- //#region src/v2/hooks/use-default-render-tool.tsx
3920
+ //#region src/v2/hooks/use-component.tsx
3772
3921
  /**
3773
- * Registers a wildcard (`"*"`) tool-call renderer via `useRenderTool`.
3922
+ * Registers a React component as a frontend tool renderer in chat.
3774
3923
  *
3775
- * - Call with no config to use CopilotKit's built-in default tool-call card.
3776
- * - Pass `config.render` to replace the default UI with your own fallback renderer.
3924
+ * This hook is a convenience wrapper around `useFrontendTool` that:
3925
+ * - builds a model-facing tool description,
3926
+ * - forwards optional schema parameters (any Standard Schema V1 compatible library),
3927
+ * - renders your component with tool call parameters.
3777
3928
  *
3778
- * This is useful when you want a generic renderer for tools that do not have a
3779
- * dedicated `useRenderTool({ name: "..." })` registration.
3929
+ * Use this when you want to display a typed visual component for a tool call
3930
+ * without manually wiring a full frontend tool object.
3780
3931
  *
3781
- * @param config - Optional custom wildcard render function.
3782
- * @param deps - Optional dependencies to refresh registration.
3932
+ * When `parameters` is provided, render props are inferred from the schema.
3933
+ * When omitted, the render component may accept any props.
3934
+ *
3935
+ * @typeParam TSchema - Schema describing tool parameters, or `undefined` when no schema is given.
3936
+ * @param config - Tool registration config.
3937
+ * @param deps - Optional dependencies to refresh registration (same semantics as `useEffect`).
3783
3938
  *
3784
3939
  * @example
3785
3940
  * ```tsx
3786
- * useDefaultRenderTool();
3941
+ * // Without parameters — render accepts any props
3942
+ * useComponent({
3943
+ * name: "showGreeting",
3944
+ * render: ({ message }: { message: string }) => <div>{message}</div>,
3945
+ * });
3787
3946
  * ```
3788
3947
  *
3789
3948
  * @example
3790
3949
  * ```tsx
3791
- * useDefaultRenderTool({
3792
- * render: ({ name, status }) => <div>{name}: {status}</div>,
3950
+ * // With parameters — render props inferred from schema
3951
+ * useComponent({
3952
+ * name: "showWeatherCard",
3953
+ * parameters: z.object({ city: z.string() }),
3954
+ * render: ({ city }) => <div>{city}</div>,
3793
3955
  * });
3794
3956
  * ```
3795
3957
  *
3796
3958
  * @example
3797
3959
  * ```tsx
3798
- * useDefaultRenderTool(
3960
+ * useComponent(
3799
3961
  * {
3800
- * render: ({ name, result }) => (
3801
- * <ToolEventRow title={name} payload={result} compact={compactMode} />
3802
- * ),
3962
+ * name: "renderProfile",
3963
+ * parameters: z.object({ userId: z.string() }),
3964
+ * render: ProfileCard,
3965
+ * agentId: "support-agent",
3803
3966
  * },
3804
- * [compactMode],
3967
+ * [selectedAgentId],
3805
3968
  * );
3806
3969
  * ```
3807
3970
  */
3808
- function useDefaultRenderTool(config, deps) {
3809
- useRenderTool({
3810
- name: "*",
3811
- render: config?.render ?? DefaultToolCallRenderer
3812
- }, deps);
3813
- }
3814
- function DefaultToolCallRenderer({ name, parameters, status, result }) {
3815
- const [isExpanded, setIsExpanded] = (0, react.useState)(false);
3816
- const statusString = String(status);
3817
- const isActive = statusString === "inProgress" || statusString === "executing";
3818
- const isComplete = statusString === "complete";
3819
- return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3820
- style: {
3821
- marginTop: "8px",
3822
- paddingBottom: "8px"
3971
+ function useComponent(config, deps) {
3972
+ const prefix = `Use this tool to display the "${config.name}" component in the chat. This tool renders a visual UI component for the user.`;
3973
+ const fullDescription = config.description ? `${prefix}\n\n${config.description}` : prefix;
3974
+ useFrontendTool({
3975
+ name: config.name,
3976
+ description: fullDescription,
3977
+ parameters: config.parameters,
3978
+ render: ({ args }) => {
3979
+ const Component = config.render;
3980
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Component, { ...args });
3823
3981
  },
3824
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3825
- style: {
3826
- borderRadius: "12px",
3827
- border: "1px solid #e4e4e7",
3828
- backgroundColor: "#fafafa",
3829
- padding: "14px 16px"
3830
- },
3831
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3832
- onClick: () => setIsExpanded(!isExpanded),
3833
- style: {
3834
- display: "flex",
3835
- alignItems: "center",
3836
- justifyContent: "space-between",
3837
- gap: "10px",
3838
- cursor: "pointer",
3839
- userSelect: "none"
3840
- },
3841
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3842
- style: {
3843
- display: "flex",
3844
- alignItems: "center",
3845
- gap: "8px",
3846
- minWidth: 0
3847
- },
3848
- children: [
3849
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
3850
- style: {
3851
- height: "14px",
3852
- width: "14px",
3853
- color: "#71717a",
3854
- transition: "transform 0.15s",
3855
- transform: isExpanded ? "rotate(90deg)" : "rotate(0deg)",
3856
- flexShrink: 0
3857
- },
3858
- fill: "none",
3859
- viewBox: "0 0 24 24",
3860
- strokeWidth: 2,
3861
- stroke: "currentColor",
3862
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
3863
- strokeLinecap: "round",
3864
- strokeLinejoin: "round",
3865
- d: "M8.25 4.5l7.5 7.5-7.5 7.5"
3866
- })
3867
- }),
3868
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { style: {
3869
- display: "inline-block",
3870
- height: "8px",
3871
- width: "8px",
3872
- borderRadius: "50%",
3873
- backgroundColor: isActive ? "#f59e0b" : isComplete ? "#10b981" : "#a1a1aa",
3874
- flexShrink: 0
3875
- } }),
3876
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3877
- style: {
3878
- fontSize: "13px",
3879
- fontWeight: 600,
3880
- color: "#18181b",
3881
- overflow: "hidden",
3882
- textOverflow: "ellipsis",
3883
- whiteSpace: "nowrap"
3884
- },
3885
- children: name
3886
- })
3887
- ]
3888
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3889
- style: {
3890
- display: "inline-flex",
3891
- alignItems: "center",
3892
- borderRadius: "9999px",
3893
- padding: "2px 8px",
3894
- fontSize: "11px",
3895
- fontWeight: 500,
3896
- backgroundColor: isActive ? "#fef3c7" : isComplete ? "#d1fae5" : "#f4f4f5",
3897
- color: isActive ? "#92400e" : isComplete ? "#065f46" : "#3f3f46",
3898
- flexShrink: 0
3899
- },
3900
- children: isActive ? "Running" : isComplete ? "Done" : status
3901
- })]
3902
- }), isExpanded && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3903
- style: {
3904
- marginTop: "12px",
3905
- display: "grid",
3906
- gap: "12px"
3907
- },
3908
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3909
- style: {
3910
- fontSize: "10px",
3911
- textTransform: "uppercase",
3912
- letterSpacing: "0.05em",
3913
- color: "#71717a"
3914
- },
3915
- children: "Arguments"
3916
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("pre", {
3917
- style: {
3918
- marginTop: "6px",
3919
- maxHeight: "200px",
3920
- overflow: "auto",
3921
- borderRadius: "6px",
3922
- backgroundColor: "#f4f4f5",
3923
- padding: "10px",
3924
- fontSize: "11px",
3925
- lineHeight: 1.6,
3926
- color: "#27272a",
3927
- whiteSpace: "pre-wrap",
3928
- wordBreak: "break-word"
3929
- },
3930
- children: JSON.stringify(parameters ?? {}, null, 2)
3931
- })] }), result !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3932
- style: {
3933
- fontSize: "10px",
3934
- textTransform: "uppercase",
3935
- letterSpacing: "0.05em",
3936
- color: "#71717a"
3937
- },
3938
- children: "Result"
3939
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("pre", {
3940
- style: {
3941
- marginTop: "6px",
3942
- maxHeight: "200px",
3943
- overflow: "auto",
3944
- borderRadius: "6px",
3945
- backgroundColor: "#f4f4f5",
3946
- padding: "10px",
3947
- fontSize: "11px",
3948
- lineHeight: 1.6,
3949
- color: "#27272a",
3950
- whiteSpace: "pre-wrap",
3951
- wordBreak: "break-word"
3952
- },
3953
- children: typeof result === "string" ? result : JSON.stringify(result, null, 2)
3954
- })] })]
3955
- })]
3956
- })
3957
- });
3982
+ agentId: config.agentId,
3983
+ followUp: config.followUp
3984
+ }, deps);
3958
3985
  }
3959
3986
 
3960
3987
  //#endregion
@@ -7431,18 +7458,19 @@ CopilotModalHeader.CloseButton.displayName = "CopilotModalHeader.CloseButton";
7431
7458
  //#region src/v2/components/chat/CopilotSidebarView.tsx
7432
7459
  const DEFAULT_SIDEBAR_WIDTH = 480;
7433
7460
  const SIDEBAR_TRANSITION_MS = 260;
7434
- function CopilotSidebarView({ header, toggleButton, width, defaultOpen = true, ...props }) {
7461
+ function CopilotSidebarView({ header, toggleButton, width, defaultOpen = true, position = "right", ...props }) {
7435
7462
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CopilotChatConfigurationProvider, {
7436
7463
  isModalDefaultOpen: defaultOpen,
7437
7464
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CopilotSidebarViewInternal, {
7438
7465
  header,
7439
7466
  toggleButton,
7440
7467
  width,
7468
+ position,
7441
7469
  ...props
7442
7470
  })
7443
7471
  });
7444
7472
  }
7445
- function CopilotSidebarViewInternal({ header, toggleButton, width, ...props }) {
7473
+ function CopilotSidebarViewInternal({ header, toggleButton, width, position = "right", ...props }) {
7446
7474
  const isSidebarOpen = useCopilotChatConfiguration()?.isModalOpen ?? false;
7447
7475
  const sidebarRef = (0, react.useRef)(null);
7448
7476
  const [sidebarWidth, setSidebarWidth] = (0, react.useState)(width ?? DEFAULT_SIDEBAR_WIDTH);
@@ -7475,26 +7503,33 @@ function CopilotSidebarViewInternal({ header, toggleButton, width, ...props }) {
7475
7503
  (0, react.useLayoutEffect)(() => {
7476
7504
  if (typeof window === "undefined" || typeof window.matchMedia !== "function") return;
7477
7505
  if (!window.matchMedia("(min-width: 768px)").matches) return;
7506
+ const marginStyleProp = position === "left" ? "marginInlineStart" : "marginInlineEnd";
7507
+ const transitionCssProp = position === "left" ? "margin-inline-start" : "margin-inline-end";
7478
7508
  if (isSidebarOpen) {
7479
- if (hasMounted.current) document.body.style.transition = `margin-inline-end ${SIDEBAR_TRANSITION_MS}ms ease`;
7480
- document.body.style.marginInlineEnd = widthToMargin(sidebarWidth);
7509
+ if (hasMounted.current) document.body.style.transition = `${transitionCssProp} ${SIDEBAR_TRANSITION_MS}ms ease`;
7510
+ document.body.style[marginStyleProp] = widthToMargin(sidebarWidth);
7481
7511
  } else if (hasMounted.current) {
7482
- document.body.style.transition = `margin-inline-end ${SIDEBAR_TRANSITION_MS}ms ease`;
7483
- document.body.style.marginInlineEnd = "";
7512
+ document.body.style.transition = `${transitionCssProp} ${SIDEBAR_TRANSITION_MS}ms ease`;
7513
+ document.body.style[marginStyleProp] = "";
7484
7514
  }
7485
7515
  hasMounted.current = true;
7486
7516
  return () => {
7487
- document.body.style.marginInlineEnd = "";
7517
+ document.body.style[marginStyleProp] = "";
7488
7518
  document.body.style.transition = "";
7489
7519
  };
7490
- }, [isSidebarOpen, sidebarWidth]);
7520
+ }, [
7521
+ isSidebarOpen,
7522
+ sidebarWidth,
7523
+ position
7524
+ ]);
7491
7525
  const headerElement = renderSlot(header, CopilotModalHeader, {});
7492
- return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [renderSlot(toggleButton, CopilotChatToggleButton, {}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("aside", {
7526
+ 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", {
7493
7527
  ref: sidebarRef,
7494
7528
  "data-copilotkit": true,
7495
7529
  "data-testid": "copilot-sidebar",
7496
7530
  "data-copilot-sidebar": true,
7497
- 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"),
7531
+ "data-position": position,
7532
+ 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"),
7498
7533
  style: {
7499
7534
  ["--sidebar-width"]: widthToCss(sidebarWidth),
7500
7535
  paddingTop: "env(safe-area-inset-top)",
@@ -7701,7 +7736,7 @@ var CopilotPopupView_default = CopilotPopupView;
7701
7736
 
7702
7737
  //#endregion
7703
7738
  //#region src/v2/components/chat/CopilotSidebar.tsx
7704
- function CopilotSidebar({ header, toggleButton, defaultOpen, width, ...chatProps }) {
7739
+ function CopilotSidebar({ header, toggleButton, defaultOpen, width, position, ...chatProps }) {
7705
7740
  const { checkFeature } = useLicenseContext();
7706
7741
  const isSidebarLicensed = checkFeature("sidebar");
7707
7742
  (0, react.useEffect)(() => {
@@ -7709,13 +7744,14 @@ function CopilotSidebar({ header, toggleButton, defaultOpen, width, ...chatProps
7709
7744
  }, [isSidebarLicensed]);
7710
7745
  const SidebarViewOverride = (0, react.useMemo)(() => {
7711
7746
  const Component = (viewProps) => {
7712
- const { header: viewHeader, toggleButton: viewToggleButton, width: viewWidth, defaultOpen: viewDefaultOpen, ...restProps } = viewProps;
7747
+ const { header: viewHeader, toggleButton: viewToggleButton, width: viewWidth, defaultOpen: viewDefaultOpen, position: viewPosition, ...restProps } = viewProps;
7713
7748
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CopilotSidebarView, {
7714
7749
  ...restProps,
7715
7750
  header: header ?? viewHeader,
7716
7751
  toggleButton: toggleButton ?? viewToggleButton,
7717
7752
  width: width ?? viewWidth,
7718
- defaultOpen: defaultOpen ?? viewDefaultOpen
7753
+ defaultOpen: defaultOpen ?? viewDefaultOpen,
7754
+ position: position ?? viewPosition
7719
7755
  });
7720
7756
  };
7721
7757
  return Object.assign(Component, CopilotChatView_default);
@@ -7723,7 +7759,8 @@ function CopilotSidebar({ header, toggleButton, defaultOpen, width, ...chatProps
7723
7759
  header,
7724
7760
  toggleButton,
7725
7761
  width,
7726
- defaultOpen
7762
+ defaultOpen,
7763
+ position
7727
7764
  ]);
7728
7765
  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, {
7729
7766
  welcomeScreen: CopilotSidebarView.WelcomeScreen,
@@ -10389,4 +10426,4 @@ Object.defineProperty(exports, 'useToast', {
10389
10426
  return useToast;
10390
10427
  }
10391
10428
  });
10392
- //# sourceMappingURL=copilotkit-C3k13WZn.cjs.map
10429
+ //# sourceMappingURL=copilotkit-CtXcs1ea.cjs.map