@copilotkit/react-core 1.57.0-canary.1778229181 → 1.57.1-canary.1778272612

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 (60) hide show
  1. package/dist/{copilotkit-N0YiBG5S.mjs → copilotkit-3XTEoVQO.mjs} +1917 -1754
  2. package/dist/copilotkit-3XTEoVQO.mjs.map +1 -0
  3. package/dist/{copilotkit-BYnbIBN5.d.mts → copilotkit-BCJ2yvV6.d.mts} +64 -6
  4. package/dist/copilotkit-BCJ2yvV6.d.mts.map +1 -0
  5. package/dist/{copilotkit-vx_R9p-O.d.cts → copilotkit-CBbSvze0.d.cts} +64 -6
  6. package/dist/copilotkit-CBbSvze0.d.cts.map +1 -0
  7. package/dist/{copilotkit-BLlkMAjx.cjs → copilotkit-Dnj9pi4m.cjs} +1705 -1536
  8. package/dist/copilotkit-Dnj9pi4m.cjs.map +1 -0
  9. package/dist/index.cjs +2 -5
  10. package/dist/index.cjs.map +1 -1
  11. package/dist/index.d.cts +1 -1
  12. package/dist/index.d.mts +1 -1
  13. package/dist/index.mjs +2 -5
  14. package/dist/index.mjs.map +1 -1
  15. package/dist/index.umd.js +310 -194
  16. package/dist/index.umd.js.map +1 -1
  17. package/dist/v2/headless.cjs +6 -48
  18. package/dist/v2/headless.cjs.map +1 -1
  19. package/dist/v2/headless.d.cts +0 -2
  20. package/dist/v2/headless.d.cts.map +1 -1
  21. package/dist/v2/headless.d.mts +0 -2
  22. package/dist/v2/headless.d.mts.map +1 -1
  23. package/dist/v2/headless.mjs +6 -48
  24. package/dist/v2/headless.mjs.map +1 -1
  25. package/dist/v2/index.cjs +2 -1
  26. package/dist/v2/index.css +1 -1
  27. package/dist/v2/index.d.cts +2 -2
  28. package/dist/v2/index.d.mts +2 -2
  29. package/dist/v2/index.mjs +2 -2
  30. package/dist/v2/index.umd.js +1101 -926
  31. package/dist/v2/index.umd.js.map +1 -1
  32. package/package.json +6 -6
  33. package/src/hooks/__tests__/use-copilot-chat-internal-connect.test.tsx +5 -6
  34. package/src/hooks/use-copilot-chat_internal.ts +0 -1
  35. package/src/v2/components/MCPAppsActivityRenderer.tsx +3 -9
  36. package/src/v2/components/chat/CopilotChat.tsx +2 -1
  37. package/src/v2/components/chat/CopilotChatMessageView.tsx +24 -9
  38. package/src/v2/components/chat/CopilotChatView.tsx +2 -2
  39. package/src/v2/components/chat/CopilotSidebar.tsx +5 -1
  40. package/src/v2/components/chat/CopilotSidebarView.tsx +24 -10
  41. package/src/v2/components/chat/__tests__/CopilotChat.welcomeGate.test.tsx +1 -3
  42. package/src/v2/components/chat/__tests__/CopilotChatActivityRendering.e2e.test.tsx +29 -25
  43. package/src/v2/components/chat/__tests__/CopilotSidebarView.position.test.tsx +159 -0
  44. package/src/v2/components/chat/__tests__/MCPAppsActivityRenderer.e2e.test.tsx +0 -102
  45. package/src/v2/components/chat/__tests__/MCPAppsUiMessage.e2e.test.tsx +5 -60
  46. package/src/v2/components/index.ts +1 -0
  47. package/src/v2/components/intelligence-indicator/IntelligenceIndicator.tsx +286 -0
  48. package/src/v2/components/intelligence-indicator/__tests__/IntelligenceIndicator.e2e.test.tsx +464 -0
  49. package/src/v2/components/intelligence-indicator/index.ts +2 -0
  50. package/src/v2/hooks/use-agent.tsx +7 -116
  51. package/src/v2/hooks/use-default-render-tool.tsx +18 -1
  52. package/src/v2/hooks/use-render-activity-message.tsx +3 -11
  53. package/src/v2/hooks/use-render-custom-messages.tsx +1 -6
  54. package/src/v2/hooks/use-render-tool-call.tsx +35 -5
  55. package/src/v2/styles/globals.css +118 -0
  56. package/dist/copilotkit-BLlkMAjx.cjs.map +0 -1
  57. package/dist/copilotkit-BYnbIBN5.d.mts.map +0 -1
  58. package/dist/copilotkit-N0YiBG5S.mjs.map +0 -1
  59. package/dist/copilotkit-vx_R9p-O.d.cts.map +0 -1
  60. package/src/v2/hooks/__tests__/use-agent-thread-isolation.test.tsx +0 -333
@@ -1416,1167 +1416,1462 @@ const LicenseContext = createContext({
1416
1416
  const useLicenseContext = () => useContext(LicenseContext);
1417
1417
 
1418
1418
  //#endregion
1419
- //#region src/v2/hooks/use-render-tool-call.tsx
1420
- /**
1421
- * Memoized component that renders a single tool call.
1422
- * This prevents unnecessary re-renders when parent components update
1423
- * but the tool call data hasn't changed.
1424
- */
1425
- const ToolCallRenderer = React.memo(function ToolCallRenderer({ toolCall, toolMessage, RenderComponent, isExecuting }) {
1426
- const args = useMemo(() => partialJSONParse(toolCall.function.arguments), [toolCall.function.arguments]);
1427
- const toolName = toolCall.function.name;
1428
- if (toolMessage) return /* @__PURE__ */ jsx(RenderComponent, {
1429
- name: toolName,
1430
- toolCallId: toolCall.id,
1431
- args,
1432
- status: ToolCallStatus.Complete,
1433
- result: toolMessage.content
1434
- });
1435
- else if (isExecuting) return /* @__PURE__ */ jsx(RenderComponent, {
1436
- name: toolName,
1437
- toolCallId: toolCall.id,
1438
- args,
1439
- status: ToolCallStatus.Executing,
1440
- result: void 0
1441
- });
1442
- else return /* @__PURE__ */ jsx(RenderComponent, {
1443
- name: toolName,
1444
- toolCallId: toolCall.id,
1445
- args,
1446
- status: ToolCallStatus.InProgress,
1447
- result: void 0
1448
- });
1449
- }, (prevProps, nextProps) => {
1450
- if (prevProps.toolCall.id !== nextProps.toolCall.id) return false;
1451
- if (prevProps.toolCall.function.name !== nextProps.toolCall.function.name) return false;
1452
- if (prevProps.toolCall.function.arguments !== nextProps.toolCall.function.arguments) return false;
1453
- if (prevProps.toolMessage?.content !== nextProps.toolMessage?.content) return false;
1454
- if (prevProps.isExecuting !== nextProps.isExecuting) return false;
1455
- if (prevProps.RenderComponent !== nextProps.RenderComponent) return false;
1456
- return true;
1457
- });
1419
+ //#region src/v2/types/defineToolCallRenderer.ts
1420
+ function defineToolCallRenderer(def) {
1421
+ const argsSchema = def.name === "*" && !def.args ? z.any() : def.args;
1422
+ return {
1423
+ name: def.name,
1424
+ args: argsSchema,
1425
+ render: def.render,
1426
+ ...def.agentId ? { agentId: def.agentId } : {}
1427
+ };
1428
+ }
1429
+
1430
+ //#endregion
1431
+ //#region src/v2/hooks/use-render-tool.tsx
1432
+ const EMPTY_DEPS$1 = [];
1458
1433
  /**
1459
- * Hook that returns a function to render tool calls based on the render functions
1460
- * defined in CopilotKitProvider.
1434
+ * Registers a renderer entry in CopilotKit's `renderToolCalls` registry.
1461
1435
  *
1462
- * @returns A function that takes a tool call and optional tool message and returns the rendered component
1436
+ * Key behavior:
1437
+ * - deduplicates by `agentId:name` (latest registration wins),
1438
+ * - keeps renderer entries on cleanup so historical chat tool calls can still render,
1439
+ * - refreshes registration when `deps` change.
1440
+ *
1441
+ * @typeParam S - Schema type describing tool call parameters.
1442
+ * @param config - Renderer config for wildcard or named tools.
1443
+ * @param deps - Optional dependencies to refresh registration.
1444
+ *
1445
+ * @example
1446
+ * ```tsx
1447
+ * useRenderTool(
1448
+ * {
1449
+ * name: "searchDocs",
1450
+ * parameters: z.object({ query: z.string() }),
1451
+ * render: ({ status, parameters, result }) => {
1452
+ * if (status === "executing") return <div>Searching {parameters.query}</div>;
1453
+ * if (status === "complete") return <div>{result}</div>;
1454
+ * return <div>Preparing...</div>;
1455
+ * },
1456
+ * },
1457
+ * [],
1458
+ * );
1459
+ * ```
1460
+ *
1461
+ * @example
1462
+ * ```tsx
1463
+ * useRenderTool(
1464
+ * {
1465
+ * name: "summarize",
1466
+ * parameters: z.object({ text: z.string() }),
1467
+ * agentId: "research-agent",
1468
+ * render: ({ name, status }) => <div>{name}: {status}</div>,
1469
+ * },
1470
+ * [selectedAgentId],
1471
+ * );
1472
+ * ```
1463
1473
  */
1464
- function useRenderToolCall() {
1465
- const { copilotkit, executingToolCallIds } = useCopilotKit();
1466
- const agentId = useCopilotChatConfiguration()?.agentId ?? DEFAULT_AGENT_ID;
1467
- const renderToolCalls = useSyncExternalStore((callback) => {
1468
- return copilotkit.subscribe({ onRenderToolCallsChanged: callback }).unsubscribe;
1469
- }, () => copilotkit.renderToolCalls, () => copilotkit.renderToolCalls);
1470
- return useCallback(({ toolCall, toolMessage }) => {
1471
- const exactMatches = renderToolCalls.filter((rc) => rc.name === toolCall.function.name);
1472
- const renderConfig = exactMatches.find((rc) => rc.agentId === agentId) || exactMatches.find((rc) => !rc.agentId) || exactMatches[0] || renderToolCalls.find((rc) => rc.name === "*");
1473
- if (!renderConfig) return null;
1474
- const RenderComponent = renderConfig.render;
1475
- return /* @__PURE__ */ jsx(ToolCallRenderer, {
1476
- toolCall,
1477
- toolMessage,
1478
- RenderComponent,
1479
- isExecuting: executingToolCallIds.has(toolCall.id)
1480
- }, toolCall.id);
1474
+ function useRenderTool(config, deps) {
1475
+ const { copilotkit } = useCopilotKit();
1476
+ const extraDeps = deps ?? EMPTY_DEPS$1;
1477
+ useEffect(() => {
1478
+ const renderer = config.name === "*" && !config.parameters ? defineToolCallRenderer({
1479
+ name: "*",
1480
+ render: (props) => config.render({
1481
+ ...props,
1482
+ parameters: props.args
1483
+ }),
1484
+ ...config.agentId ? { agentId: config.agentId } : {}
1485
+ }) : defineToolCallRenderer({
1486
+ name: config.name,
1487
+ args: config.parameters,
1488
+ render: (props) => config.render({
1489
+ ...props,
1490
+ parameters: props.args
1491
+ }),
1492
+ ...config.agentId ? { agentId: config.agentId } : {}
1493
+ });
1494
+ copilotkit.addHookRenderToolCall(renderer);
1481
1495
  }, [
1482
- renderToolCalls,
1483
- executingToolCallIds,
1484
- agentId
1496
+ config.name,
1497
+ copilotkit,
1498
+ JSON.stringify(extraDeps)
1485
1499
  ]);
1486
1500
  }
1487
1501
 
1488
1502
  //#endregion
1489
- //#region src/v2/components/CopilotKitInspector.tsx
1490
- const CopilotKitInspector = ({ core, ...rest }) => {
1491
- const [InspectorComponent, setInspectorComponent] = React$1.useState(null);
1492
- React$1.useEffect(() => {
1493
- let mounted = true;
1494
- import("@copilotkit/web-inspector").then((mod) => {
1495
- mod.defineWebInspector?.();
1496
- const Component = createComponent({
1497
- tagName: mod.WEB_INSPECTOR_TAG,
1498
- elementClass: mod.WebInspectorElement,
1499
- react: React$1
1500
- });
1501
- if (mounted) setInspectorComponent(() => Component);
1502
- });
1503
- return () => {
1504
- mounted = false;
1505
- };
1506
- }, []);
1507
- if (!InspectorComponent) return null;
1508
- return /* @__PURE__ */ jsx(InspectorComponent, {
1509
- ...rest,
1510
- core: core ?? null
1511
- });
1512
- };
1513
- CopilotKitInspector.displayName = "CopilotKitInspector";
1514
-
1515
- //#endregion
1516
- //#region src/v2/components/license-warning-banner.tsx
1517
- const LICENSE_BANNER_OFFSET_PX = 52;
1518
- const LICENSE_BANNER_OFFSET_VAR = "--copilotkit-license-banner-offset";
1519
- const BANNER_STYLES = {
1520
- base: {
1521
- position: "fixed",
1522
- bottom: "8px",
1523
- left: "50%",
1524
- transform: "translateX(-50%)",
1525
- zIndex: 99999,
1526
- display: "inline-flex",
1527
- alignItems: "center",
1528
- gap: "12px",
1529
- whiteSpace: "nowrap",
1530
- padding: "8px 16px",
1531
- fontSize: "13px",
1532
- fontFamily: "-apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif",
1533
- borderRadius: "6px",
1534
- boxShadow: "0 2px 8px rgba(0, 0, 0, 0.15)"
1535
- },
1536
- info: {
1537
- backgroundColor: "#eff6ff",
1538
- border: "1px solid #93c5fd",
1539
- color: "#1e40af"
1540
- },
1541
- warning: {
1542
- backgroundColor: "#fffbeb",
1543
- border: "1px solid #fbbf24",
1544
- color: "#92400e"
1545
- },
1546
- critical: {
1547
- backgroundColor: "#fef2f2",
1548
- border: "1px solid #fca5a5",
1549
- color: "#991b1b"
1550
- }
1551
- };
1552
- function getSeverityStyle(severity) {
1553
- switch (severity) {
1554
- case "warning": return BANNER_STYLES.warning;
1555
- case "critical": return BANNER_STYLES.critical;
1556
- default: return BANNER_STYLES.info;
1557
- }
1503
+ //#region src/v2/hooks/use-default-render-tool.tsx
1504
+ /**
1505
+ * Registers a wildcard (`"*"`) tool-call renderer via `useRenderTool`.
1506
+ *
1507
+ * - Call with no config to use CopilotKit's built-in default tool-call card.
1508
+ * - Pass `config.render` to replace the default UI with your own fallback renderer.
1509
+ *
1510
+ * This is useful when you want a generic renderer for tools that do not have a
1511
+ * dedicated `useRenderTool({ name: "..." })` registration.
1512
+ *
1513
+ * @param config - Optional custom wildcard render function.
1514
+ * @param deps - Optional dependencies to refresh registration.
1515
+ *
1516
+ * @example
1517
+ * ```tsx
1518
+ * useDefaultRenderTool();
1519
+ * ```
1520
+ *
1521
+ * @example
1522
+ * ```tsx
1523
+ * useDefaultRenderTool({
1524
+ * render: ({ name, status }) => <div>{name}: {status}</div>,
1525
+ * });
1526
+ * ```
1527
+ *
1528
+ * @example
1529
+ * ```tsx
1530
+ * useDefaultRenderTool(
1531
+ * {
1532
+ * render: ({ name, result }) => (
1533
+ * <ToolEventRow title={name} payload={result} compact={compactMode} />
1534
+ * ),
1535
+ * },
1536
+ * [compactMode],
1537
+ * );
1538
+ * ```
1539
+ */
1540
+ function useDefaultRenderTool(config, deps) {
1541
+ useRenderTool({
1542
+ name: "*",
1543
+ render: config?.render ?? DefaultToolCallRenderer
1544
+ }, deps);
1558
1545
  }
1559
- function BannerShell({ severity, message, actionLabel, actionUrl, onDismiss }) {
1560
- useEffect(() => {
1561
- if (typeof document === "undefined") return;
1562
- const root = document.documentElement;
1563
- root.style.setProperty(LICENSE_BANNER_OFFSET_VAR, `${LICENSE_BANNER_OFFSET_PX}px`);
1564
- return () => {
1565
- root.style.removeProperty(LICENSE_BANNER_OFFSET_VAR);
1566
- };
1567
- }, []);
1568
- return /* @__PURE__ */ jsxs("div", {
1569
- style: {
1570
- ...BANNER_STYLES.base,
1571
- ...getSeverityStyle(severity)
1546
+ function DefaultToolCallRenderer({ name, parameters, status, result }) {
1547
+ const [isExpanded, setIsExpanded] = useState(false);
1548
+ const statusString = String(status);
1549
+ const isActive = statusString === "inProgress" || statusString === "executing";
1550
+ const isComplete = statusString === "complete";
1551
+ const statusLabel = isActive ? "Running" : isComplete ? "Done" : status;
1552
+ const dotColor = isActive ? "#f59e0b" : isComplete ? "#10b981" : "#a1a1aa";
1553
+ const badgeBg = isActive ? "#fef3c7" : isComplete ? "#d1fae5" : "#f4f4f5";
1554
+ const badgeColor = isActive ? "#92400e" : isComplete ? "#065f46" : "#3f3f46";
1555
+ return /* @__PURE__ */ jsx("div", {
1556
+ "data-testid": "copilot-tool-render",
1557
+ "data-tool-name": name,
1558
+ "data-status": statusString,
1559
+ "data-args": safeStringifyForAttr(parameters),
1560
+ "data-result": safeStringifyForAttr(result),
1561
+ style: {
1562
+ marginTop: "8px",
1563
+ paddingBottom: "8px"
1572
1564
  },
1573
- children: [/* @__PURE__ */ jsx("span", { children: message }), /* @__PURE__ */ jsxs("div", {
1565
+ children: /* @__PURE__ */ jsxs("div", {
1574
1566
  style: {
1575
- display: "flex",
1576
- gap: "8px",
1577
- alignItems: "center"
1567
+ borderRadius: "12px",
1568
+ border: "1px solid #e4e4e7",
1569
+ backgroundColor: "#fafafa",
1570
+ padding: "14px 16px"
1578
1571
  },
1579
- children: [/* @__PURE__ */ jsx("a", {
1580
- href: actionUrl,
1581
- target: "_blank",
1582
- rel: "noopener noreferrer",
1572
+ children: [/* @__PURE__ */ jsxs("div", {
1573
+ onClick: () => setIsExpanded(!isExpanded),
1583
1574
  style: {
1584
- fontWeight: 600,
1585
- textDecoration: "underline",
1586
- color: "inherit"
1575
+ display: "flex",
1576
+ alignItems: "center",
1577
+ justifyContent: "space-between",
1578
+ gap: "10px",
1579
+ cursor: "pointer",
1580
+ userSelect: "none"
1587
1581
  },
1588
- children: actionLabel
1589
- }), onDismiss && /* @__PURE__ */ jsx("button", {
1590
- onClick: onDismiss,
1582
+ children: [/* @__PURE__ */ jsxs("div", {
1583
+ style: {
1584
+ display: "flex",
1585
+ alignItems: "center",
1586
+ gap: "8px",
1587
+ minWidth: 0
1588
+ },
1589
+ children: [
1590
+ /* @__PURE__ */ jsx("svg", {
1591
+ style: {
1592
+ height: "14px",
1593
+ width: "14px",
1594
+ color: "#71717a",
1595
+ transition: "transform 0.15s",
1596
+ transform: isExpanded ? "rotate(90deg)" : "rotate(0deg)",
1597
+ flexShrink: 0
1598
+ },
1599
+ fill: "none",
1600
+ viewBox: "0 0 24 24",
1601
+ strokeWidth: 2,
1602
+ stroke: "currentColor",
1603
+ children: /* @__PURE__ */ jsx("path", {
1604
+ strokeLinecap: "round",
1605
+ strokeLinejoin: "round",
1606
+ d: "M8.25 4.5l7.5 7.5-7.5 7.5"
1607
+ })
1608
+ }),
1609
+ /* @__PURE__ */ jsx("span", { style: {
1610
+ display: "inline-block",
1611
+ height: "8px",
1612
+ width: "8px",
1613
+ borderRadius: "50%",
1614
+ backgroundColor: dotColor,
1615
+ flexShrink: 0
1616
+ } }),
1617
+ /* @__PURE__ */ jsx("span", {
1618
+ "data-testid": "copilot-tool-render-name",
1619
+ style: {
1620
+ fontSize: "13px",
1621
+ fontWeight: 600,
1622
+ color: "#18181b",
1623
+ overflow: "hidden",
1624
+ textOverflow: "ellipsis",
1625
+ whiteSpace: "nowrap"
1626
+ },
1627
+ children: name
1628
+ })
1629
+ ]
1630
+ }), /* @__PURE__ */ jsx("span", {
1631
+ "data-testid": "copilot-tool-render-status",
1632
+ style: {
1633
+ display: "inline-flex",
1634
+ alignItems: "center",
1635
+ borderRadius: "9999px",
1636
+ padding: "2px 8px",
1637
+ fontSize: "11px",
1638
+ fontWeight: 500,
1639
+ backgroundColor: badgeBg,
1640
+ color: badgeColor,
1641
+ flexShrink: 0
1642
+ },
1643
+ children: statusLabel
1644
+ })]
1645
+ }), isExpanded && /* @__PURE__ */ jsxs("div", {
1591
1646
  style: {
1592
- background: "none",
1593
- border: "none",
1594
- cursor: "pointer",
1595
- color: "inherit",
1596
- fontSize: "16px"
1647
+ marginTop: "12px",
1648
+ display: "grid",
1649
+ gap: "12px"
1597
1650
  },
1598
- children: "×"
1651
+ children: [/* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("div", {
1652
+ style: {
1653
+ fontSize: "10px",
1654
+ textTransform: "uppercase",
1655
+ letterSpacing: "0.05em",
1656
+ color: "#71717a"
1657
+ },
1658
+ children: "Arguments"
1659
+ }), /* @__PURE__ */ jsx("pre", {
1660
+ style: {
1661
+ marginTop: "6px",
1662
+ maxHeight: "200px",
1663
+ overflow: "auto",
1664
+ borderRadius: "6px",
1665
+ backgroundColor: "#f4f4f5",
1666
+ padding: "10px",
1667
+ fontSize: "11px",
1668
+ lineHeight: 1.6,
1669
+ color: "#27272a",
1670
+ whiteSpace: "pre-wrap",
1671
+ wordBreak: "break-word"
1672
+ },
1673
+ children: JSON.stringify(parameters ?? {}, null, 2)
1674
+ })] }), result !== void 0 && /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("div", {
1675
+ style: {
1676
+ fontSize: "10px",
1677
+ textTransform: "uppercase",
1678
+ letterSpacing: "0.05em",
1679
+ color: "#71717a"
1680
+ },
1681
+ children: "Result"
1682
+ }), /* @__PURE__ */ jsx("pre", {
1683
+ style: {
1684
+ marginTop: "6px",
1685
+ maxHeight: "200px",
1686
+ overflow: "auto",
1687
+ borderRadius: "6px",
1688
+ backgroundColor: "#f4f4f5",
1689
+ padding: "10px",
1690
+ fontSize: "11px",
1691
+ lineHeight: 1.6,
1692
+ color: "#27272a",
1693
+ whiteSpace: "pre-wrap",
1694
+ wordBreak: "break-word"
1695
+ },
1696
+ children: typeof result === "string" ? result : JSON.stringify(result, null, 2)
1697
+ })] })]
1599
1698
  })]
1600
- })]
1699
+ })
1601
1700
  });
1602
1701
  }
1603
- function LicenseWarningBanner({ type, featureName, expiryDate, graceRemaining, onDismiss }) {
1604
- switch (type) {
1605
- case "no_license": return /* @__PURE__ */ jsx(BannerShell, {
1606
- severity: "info",
1607
- message: "Powered by CopilotKit",
1608
- actionLabel: "Get a license",
1609
- actionUrl: "https://copilotkit.ai/pricing",
1610
- onDismiss
1611
- });
1612
- case "feature_unlicensed": return /* @__PURE__ */ jsx(BannerShell, {
1613
- severity: "warning",
1614
- message: `⚠ The "${featureName}" feature requires a CopilotKit license.`,
1615
- actionLabel: "Get a license",
1616
- actionUrl: "https://copilotkit.ai/pricing",
1617
- onDismiss
1618
- });
1619
- case "expiring": return /* @__PURE__ */ jsx(BannerShell, {
1620
- severity: "warning",
1621
- message: `Your CopilotKit license expires in ${graceRemaining} day${graceRemaining !== 1 ? "s" : ""}. Please renew.`,
1622
- actionLabel: "Renew",
1623
- actionUrl: "https://cloud.copilotkit.ai",
1624
- onDismiss
1625
- });
1626
- case "expired": return /* @__PURE__ */ jsx(BannerShell, {
1627
- severity: "critical",
1628
- message: `Your CopilotKit license expired${expiryDate ? ` on ${expiryDate}` : ""}. Please renew at copilotkit.ai/pricing`,
1629
- actionLabel: "Renew now",
1630
- actionUrl: "https://copilotkit.ai/pricing",
1631
- onDismiss
1632
- });
1633
- case "invalid": return /* @__PURE__ */ jsx(BannerShell, {
1634
- severity: "critical",
1635
- message: "Invalid CopilotKit license token. Please check your configuration.",
1636
- actionLabel: "Get a license",
1637
- actionUrl: "https://copilotkit.ai/pricing",
1638
- onDismiss
1639
- });
1640
- default: return null;
1702
+ function safeStringifyForAttr(value) {
1703
+ if (value === void 0 || value === null) return "";
1704
+ if (typeof value === "string") return value;
1705
+ try {
1706
+ return JSON.stringify(value);
1707
+ } catch {
1708
+ return String(value);
1641
1709
  }
1642
1710
  }
1643
- function InlineFeatureWarning({ featureName }) {
1644
- return /* @__PURE__ */ jsxs("div", {
1645
- style: {
1646
- padding: "8px 12px",
1647
- backgroundColor: "#fffbeb",
1648
- border: "1px solid #fbbf24",
1649
- borderRadius: "6px",
1650
- fontSize: "13px",
1651
- color: "#92400e",
1652
- fontFamily: "-apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif"
1653
- },
1654
- children: [
1655
- "⚠ The \"",
1656
- featureName,
1657
- "\" feature requires a CopilotKit license.",
1658
- " ",
1659
- /* @__PURE__ */ jsx("a", {
1660
- href: "https://copilotkit.ai/pricing",
1661
- target: "_blank",
1662
- rel: "noopener noreferrer",
1663
- style: {
1664
- color: "#b45309",
1665
- textDecoration: "underline"
1666
- },
1667
- children: "Get one at copilotkit.ai/pricing"
1668
- })
1669
- ]
1670
- });
1671
- }
1672
1711
 
1673
1712
  //#endregion
1674
- //#region src/v2/components/MCPAppsActivityRenderer.tsx
1675
- const PROTOCOL_VERSION = "2025-06-18";
1676
- function buildSandboxHTML(extraCspDomains) {
1677
- const baseScriptSrc = "'self' 'wasm-unsafe-eval' 'unsafe-inline' 'unsafe-eval' blob: data: http://localhost:* https://localhost:*";
1678
- const baseFrameSrc = "* blob: data: http://localhost:* https://localhost:*";
1679
- const extra = extraCspDomains?.length ? " " + extraCspDomains.join(" ") : "";
1680
- return `<!doctype html>
1681
- <html>
1682
- <head>
1683
- <meta charset="utf-8" />
1684
- <meta http-equiv="Content-Security-Policy" content="default-src 'self'; img-src * data: blob: 'unsafe-inline'; media-src * blob: data:; font-src * blob: data:; script-src ${baseScriptSrc + extra}; style-src * blob: data: 'unsafe-inline'; connect-src *; frame-src ${baseFrameSrc + extra}; base-uri 'self';" />
1685
- <style>html,body{margin:0;padding:0;height:100%;width:100%;overflow:hidden}*{box-sizing:border-box}iframe{background-color:transparent;border:none;padding:0;overflow:hidden;width:100%;height:100%}</style>
1686
- </head>
1687
- <body>
1688
- <script>
1689
- if(window.self===window.top){throw new Error("This file must be used in an iframe.")}
1690
- const inner=document.createElement("iframe");
1691
- inner.style="width:100%;height:100%;border:none;";
1692
- inner.setAttribute("sandbox","allow-scripts allow-same-origin allow-forms");
1693
- document.body.appendChild(inner);
1694
- window.addEventListener("message",async(event)=>{
1695
- if(event.source===window.parent){
1696
- if(event.data&&event.data.method==="ui/notifications/sandbox-resource-ready"){
1697
- const{html,sandbox}=event.data.params;
1698
- if(typeof sandbox==="string")inner.setAttribute("sandbox",sandbox);
1699
- if(typeof html==="string")inner.srcdoc=html;
1700
- }else if(inner&&inner.contentWindow){
1701
- inner.contentWindow.postMessage(event.data,"*");
1702
- }
1703
- }else if(event.source===inner.contentWindow){
1704
- window.parent.postMessage(event.data,"*");
1705
- }
1706
- });
1707
- window.parent.postMessage({jsonrpc:"2.0",method:"ui/notifications/sandbox-proxy-ready",params:{}},"*");
1708
- <\/script>
1709
- </body>
1710
- </html>`;
1711
- }
1712
- /**
1713
- * Queue for serializing MCP app requests to an agent.
1714
- * Ensures requests wait for the agent to stop running and are processed one at a time.
1715
- */
1716
- var MCPAppsRequestQueue = class {
1717
- constructor() {
1718
- this.queues = /* @__PURE__ */ new Map();
1719
- this.processing = /* @__PURE__ */ new Map();
1720
- }
1721
- /**
1722
- * Add a request to the queue for a specific agent thread.
1723
- * Returns a promise that resolves when the request completes.
1724
- */
1725
- async enqueue(agent, request) {
1726
- const threadId = agent.threadId || "default";
1727
- return new Promise((resolve, reject) => {
1728
- let queue = this.queues.get(threadId);
1729
- if (!queue) {
1730
- queue = [];
1731
- this.queues.set(threadId, queue);
1732
- }
1733
- queue.push({
1734
- execute: request,
1735
- resolve,
1736
- reject
1737
- });
1738
- this.processQueue(threadId, agent);
1739
- });
1740
- }
1741
- async processQueue(threadId, agent) {
1742
- if (this.processing.get(threadId)) return;
1743
- this.processing.set(threadId, true);
1744
- try {
1745
- const queue = this.queues.get(threadId);
1746
- if (!queue) return;
1747
- while (queue.length > 0) {
1748
- const item = queue[0];
1749
- try {
1750
- await this.waitForAgentIdle(agent);
1751
- const result = await item.execute();
1752
- item.resolve(result);
1753
- } catch (error) {
1754
- item.reject(error instanceof Error ? error : new Error(String(error)));
1755
- }
1756
- queue.shift();
1757
- }
1758
- } finally {
1759
- this.processing.set(threadId, false);
1760
- }
1761
- }
1762
- waitForAgentIdle(agent) {
1763
- return new Promise((resolve) => {
1764
- if (!agent.isRunning) {
1765
- resolve();
1766
- return;
1767
- }
1768
- let done = false;
1769
- const finish = () => {
1770
- if (done) return;
1771
- done = true;
1772
- clearInterval(checkInterval);
1773
- sub.unsubscribe();
1774
- resolve();
1775
- };
1776
- const sub = agent.subscribe({
1777
- onRunFinalized: finish,
1778
- onRunFailed: finish
1779
- });
1780
- const checkInterval = setInterval(() => {
1781
- if (!agent.isRunning) finish();
1782
- }, 500);
1783
- });
1784
- }
1785
- };
1786
- const mcpAppsRequestQueue = new MCPAppsRequestQueue();
1713
+ //#region src/v2/hooks/use-render-tool-call.tsx
1787
1714
  /**
1788
- * Activity type for MCP Apps events - must match the middleware's MCPAppsActivityType
1715
+ * Memoized component that renders a single tool call.
1716
+ * This prevents unnecessary re-renders when parent components update
1717
+ * but the tool call data hasn't changed.
1789
1718
  */
1790
- const MCPAppsActivityType = "mcp-apps";
1791
- const MCPAppsActivityContentSchema = z.object({
1792
- result: z.object({
1793
- content: z.array(z.any()).optional(),
1794
- structuredContent: z.any().optional(),
1795
- isError: z.boolean().optional()
1796
- }),
1797
- resourceUri: z.string(),
1798
- serverHash: z.string(),
1799
- serverId: z.string().optional(),
1800
- toolInput: z.record(z.unknown()).optional()
1719
+ const ToolCallRenderer = React.memo(function ToolCallRenderer({ toolCall, toolMessage, RenderComponent, isExecuting }) {
1720
+ const args = useMemo(() => partialJSONParse(toolCall.function.arguments), [toolCall.function.arguments]);
1721
+ const toolName = toolCall.function.name;
1722
+ if (toolMessage) return /* @__PURE__ */ jsx(RenderComponent, {
1723
+ name: toolName,
1724
+ toolCallId: toolCall.id,
1725
+ args,
1726
+ status: ToolCallStatus.Complete,
1727
+ result: toolMessage.content
1728
+ });
1729
+ else if (isExecuting) return /* @__PURE__ */ jsx(RenderComponent, {
1730
+ name: toolName,
1731
+ toolCallId: toolCall.id,
1732
+ args,
1733
+ status: ToolCallStatus.Executing,
1734
+ result: void 0
1735
+ });
1736
+ else return /* @__PURE__ */ jsx(RenderComponent, {
1737
+ name: toolName,
1738
+ toolCallId: toolCall.id,
1739
+ args,
1740
+ status: ToolCallStatus.InProgress,
1741
+ result: void 0
1742
+ });
1743
+ }, (prevProps, nextProps) => {
1744
+ if (prevProps.toolCall.id !== nextProps.toolCall.id) return false;
1745
+ if (prevProps.toolCall.function.name !== nextProps.toolCall.function.name) return false;
1746
+ if (prevProps.toolCall.function.arguments !== nextProps.toolCall.function.arguments) return false;
1747
+ if (prevProps.toolMessage?.content !== nextProps.toolMessage?.content) return false;
1748
+ if (prevProps.isExecuting !== nextProps.isExecuting) return false;
1749
+ if (prevProps.RenderComponent !== nextProps.RenderComponent) return false;
1750
+ return true;
1801
1751
  });
1802
- function isRequest(msg) {
1803
- return "id" in msg && "method" in msg;
1804
- }
1805
- function isNotification(msg) {
1806
- return !("id" in msg) && "method" in msg;
1807
- }
1808
1752
  /**
1809
- * MCP Apps Extension Activity Renderer
1753
+ * Hook that returns a function to render tool calls based on the render functions
1754
+ * defined in CopilotKitProvider.
1810
1755
  *
1811
- * Renders MCP Apps UI in a sandboxed iframe with full protocol support.
1812
- * Fetches resource content on-demand via proxied MCP requests.
1756
+ * @returns A function that takes a tool call and optional tool message and returns the rendered component
1813
1757
  */
1814
- const MCPAppsActivityRenderer = function MCPAppsActivityRenderer({ content, agent }) {
1815
- const { copilotkit } = useCopilotKit();
1816
- const containerRef = useRef(null);
1817
- const iframeRef = useRef(null);
1818
- const [iframeReady, setIframeReady] = useState(false);
1819
- const [error, setError] = useState(null);
1820
- const [isLoading, setIsLoading] = useState(true);
1821
- const [iframeSize, setIframeSize] = useState({});
1822
- const [fetchedResource, setFetchedResource] = useState(null);
1823
- const contentRef = useRef(content);
1824
- contentRef.current = content;
1825
- const agentRef = useRef(agent);
1826
- agentRef.current = agent;
1827
- const fetchStateRef = useRef({
1828
- inProgress: false,
1829
- promise: null,
1830
- resourceUri: null
1831
- });
1832
- const { resourceUri, serverHash, serverId } = content;
1833
- const sendToIframe = useCallback((msg) => {
1834
- if (iframeRef.current?.contentWindow) {
1835
- console.log("[MCPAppsRenderer] Sending to iframe:", msg);
1836
- iframeRef.current.contentWindow.postMessage(msg, "*");
1837
- }
1838
- }, []);
1839
- const sendResponse = useCallback((id, result) => {
1840
- sendToIframe({
1841
- jsonrpc: "2.0",
1842
- id,
1843
- result
1844
- });
1845
- }, [sendToIframe]);
1846
- const sendErrorResponse = useCallback((id, code, message) => {
1847
- sendToIframe({
1848
- jsonrpc: "2.0",
1849
- id,
1850
- error: {
1851
- code,
1852
- message
1853
- }
1854
- });
1855
- }, [sendToIframe]);
1856
- const sendNotification = useCallback((method, params) => {
1857
- sendToIframe({
1858
- jsonrpc: "2.0",
1859
- method,
1860
- params: params || {}
1861
- });
1862
- }, [sendToIframe]);
1863
- useEffect(() => {
1864
- if (fetchStateRef.current.inProgress && fetchStateRef.current.resourceUri === resourceUri) {
1865
- fetchStateRef.current.promise?.then((resource) => {
1866
- if (resource) {
1867
- setFetchedResource(resource);
1868
- setIsLoading(false);
1869
- }
1870
- }).catch((err) => {
1871
- setError(err instanceof Error ? err : new Error(String(err)));
1872
- setIsLoading(false);
1873
- });
1874
- return;
1875
- }
1876
- if (!agent) {
1877
- setError(/* @__PURE__ */ new Error("No agent available to fetch resource"));
1878
- setIsLoading(false);
1879
- return;
1880
- }
1881
- fetchStateRef.current.inProgress = true;
1882
- fetchStateRef.current.resourceUri = resourceUri;
1883
- const fetchPromise = (async () => {
1884
- try {
1885
- const resource = (await mcpAppsRequestQueue.enqueue(agent, () => agent.runAgent({ forwardedProps: { __proxiedMCPRequest: {
1886
- serverHash,
1887
- serverId,
1888
- method: "resources/read",
1889
- params: { uri: resourceUri }
1890
- } } }))).result?.contents?.[0];
1891
- if (!resource) throw new Error("No resource content in response");
1892
- return resource;
1893
- } catch (err) {
1894
- console.error("[MCPAppsRenderer] Failed to fetch resource:", err);
1895
- throw err;
1896
- } finally {
1897
- fetchStateRef.current.inProgress = false;
1898
- }
1899
- })();
1900
- fetchStateRef.current.promise = fetchPromise;
1901
- fetchPromise.then((resource) => {
1902
- if (resource) {
1903
- setFetchedResource(resource);
1904
- setIsLoading(false);
1905
- }
1906
- }).catch((err) => {
1907
- setError(err instanceof Error ? err : new Error(String(err)));
1908
- setIsLoading(false);
1909
- });
1758
+ function useRenderToolCall() {
1759
+ const { copilotkit, executingToolCallIds } = useCopilotKit();
1760
+ const agentId = useCopilotChatConfiguration()?.agentId ?? DEFAULT_AGENT_ID;
1761
+ const renderToolCalls = useSyncExternalStore((callback) => {
1762
+ return copilotkit.subscribe({ onRenderToolCallsChanged: callback }).unsubscribe;
1763
+ }, () => copilotkit.renderToolCalls, () => copilotkit.renderToolCalls);
1764
+ return useCallback(({ toolCall, toolMessage }) => {
1765
+ const exactMatches = renderToolCalls.filter((rc) => rc.name === toolCall.function.name);
1766
+ return /* @__PURE__ */ jsx(ToolCallRenderer, {
1767
+ toolCall,
1768
+ toolMessage,
1769
+ RenderComponent: (exactMatches.find((rc) => rc.agentId === agentId) || exactMatches.find((rc) => !rc.agentId) || exactMatches[0] || renderToolCalls.find((rc) => rc.name === "*"))?.render ?? defaultToolCallRenderAdapter,
1770
+ isExecuting: executingToolCallIds.has(toolCall.id)
1771
+ }, toolCall.id);
1910
1772
  }, [
1911
- agent,
1912
- resourceUri,
1913
- serverHash,
1914
- serverId
1773
+ renderToolCalls,
1774
+ executingToolCallIds,
1775
+ agentId
1915
1776
  ]);
1916
- useEffect(() => {
1917
- if (isLoading || !fetchedResource) return;
1918
- const container = containerRef.current;
1919
- if (!container) return;
1777
+ }
1778
+ function defaultToolCallRenderAdapter(props) {
1779
+ const status = props.status === ToolCallStatus.Complete ? "complete" : props.status === ToolCallStatus.Executing ? "executing" : "inProgress";
1780
+ return /* @__PURE__ */ jsx(DefaultToolCallRenderer, {
1781
+ name: props.name,
1782
+ parameters: props.args,
1783
+ status,
1784
+ result: props.result
1785
+ });
1786
+ }
1787
+
1788
+ //#endregion
1789
+ //#region src/v2/components/CopilotKitInspector.tsx
1790
+ const CopilotKitInspector = ({ core, ...rest }) => {
1791
+ const [InspectorComponent, setInspectorComponent] = React$1.useState(null);
1792
+ React$1.useEffect(() => {
1920
1793
  let mounted = true;
1921
- let messageHandler = null;
1922
- let initialListener = null;
1923
- let createdIframe = null;
1924
- const setup = async () => {
1925
- try {
1926
- const iframe = document.createElement("iframe");
1927
- createdIframe = iframe;
1928
- iframe.style.width = "100%";
1929
- iframe.style.height = "100px";
1930
- iframe.style.border = "none";
1931
- iframe.style.backgroundColor = "transparent";
1932
- iframe.style.display = "block";
1933
- iframe.setAttribute("sandbox", "allow-scripts allow-same-origin allow-forms");
1934
- const sandboxReady = new Promise((resolve) => {
1935
- initialListener = (event) => {
1936
- if (event.source === iframe.contentWindow) {
1937
- if (event.data?.method === "ui/notifications/sandbox-proxy-ready") {
1938
- if (initialListener) {
1939
- window.removeEventListener("message", initialListener);
1940
- initialListener = null;
1941
- }
1942
- resolve();
1943
- }
1944
- }
1945
- };
1946
- window.addEventListener("message", initialListener);
1947
- });
1948
- if (!mounted) {
1949
- if (initialListener) {
1950
- window.removeEventListener("message", initialListener);
1951
- initialListener = null;
1952
- }
1953
- return;
1954
- }
1955
- const cspDomains = fetchedResource._meta?.ui?.csp?.resourceDomains;
1956
- iframe.srcdoc = buildSandboxHTML(cspDomains);
1957
- iframeRef.current = iframe;
1958
- container.appendChild(iframe);
1959
- await sandboxReady;
1960
- if (!mounted) return;
1961
- console.log("[MCPAppsRenderer] Sandbox proxy ready");
1962
- messageHandler = async (event) => {
1963
- if (event.source !== iframe.contentWindow) return;
1964
- const msg = event.data;
1965
- if (!msg || typeof msg !== "object" || msg.jsonrpc !== "2.0") return;
1966
- console.log("[MCPAppsRenderer] Received from iframe:", msg);
1967
- if (isRequest(msg)) switch (msg.method) {
1968
- case "ui/initialize":
1969
- sendResponse(msg.id, {
1970
- protocolVersion: PROTOCOL_VERSION,
1971
- hostInfo: {
1972
- name: "CopilotKit MCP Apps Host",
1973
- version: "1.0.0"
1974
- },
1975
- hostCapabilities: {
1976
- openLinks: {},
1977
- logging: {}
1978
- },
1979
- hostContext: {
1980
- theme: "light",
1981
- platform: "web"
1982
- }
1983
- });
1984
- break;
1985
- case "ui/message": {
1986
- const currentAgent = agentRef.current;
1987
- if (!currentAgent) {
1988
- console.warn("[MCPAppsRenderer] ui/message: No agent available");
1989
- sendResponse(msg.id, { isError: false });
1990
- break;
1991
- }
1992
- try {
1993
- const params = msg.params;
1994
- const role = params.role || "user";
1995
- const textContent = params.content?.filter((c) => c.type === "text" && c.text).map((c) => c.text).join("\n") || "";
1996
- if (textContent) currentAgent.addMessage({
1997
- id: crypto.randomUUID(),
1998
- role,
1999
- content: textContent
2000
- });
2001
- sendResponse(msg.id, { isError: false });
2002
- if ((params.followUp ?? role === "user") && textContent) mcpAppsRequestQueue.enqueue(currentAgent, () => copilotkit.runAgent({ agent: currentAgent })).catch((err) => console.error("[MCPAppsRenderer] ui/message agent run failed:", err));
2003
- } catch (err) {
2004
- console.error("[MCPAppsRenderer] ui/message error:", err);
2005
- sendResponse(msg.id, { isError: true });
2006
- }
2007
- break;
2008
- }
2009
- case "ui/open-link": {
2010
- const url = msg.params?.url;
2011
- if (url) {
2012
- window.open(url, "_blank", "noopener,noreferrer");
2013
- sendResponse(msg.id, { isError: false });
2014
- } else sendErrorResponse(msg.id, -32602, "Missing url parameter");
2015
- break;
2016
- }
2017
- case "tools/call": {
2018
- const { serverHash, serverId } = contentRef.current;
2019
- const currentAgent = agentRef.current;
2020
- if (!serverHash) {
2021
- sendErrorResponse(msg.id, -32603, "No server hash available for proxying");
2022
- break;
2023
- }
2024
- if (!currentAgent) {
2025
- sendErrorResponse(msg.id, -32603, "No agent available for proxying");
2026
- break;
2027
- }
2028
- try {
2029
- const runResult = await mcpAppsRequestQueue.enqueue(currentAgent, () => currentAgent.runAgent({ forwardedProps: { __proxiedMCPRequest: {
2030
- serverHash,
2031
- serverId,
2032
- method: "tools/call",
2033
- params: msg.params
2034
- } } }));
2035
- sendResponse(msg.id, runResult.result || {});
2036
- } catch (err) {
2037
- console.error("[MCPAppsRenderer] tools/call error:", err);
2038
- sendErrorResponse(msg.id, -32603, String(err));
2039
- }
2040
- break;
2041
- }
2042
- default: sendErrorResponse(msg.id, -32601, `Method not found: ${msg.method}`);
2043
- }
2044
- if (isNotification(msg)) switch (msg.method) {
2045
- case "ui/notifications/initialized":
2046
- console.log("[MCPAppsRenderer] Inner iframe initialized");
2047
- if (mounted) setIframeReady(true);
2048
- break;
2049
- case "ui/notifications/size-changed": {
2050
- const { width, height } = msg.params || {};
2051
- console.log("[MCPAppsRenderer] Size change:", {
2052
- width,
2053
- height
2054
- });
2055
- if (mounted) setIframeSize({
2056
- width: typeof width === "number" ? width : void 0,
2057
- height: typeof height === "number" ? height : void 0
2058
- });
2059
- break;
2060
- }
2061
- case "notifications/message":
2062
- console.log("[MCPAppsRenderer] App log:", msg.params);
2063
- break;
2064
- }
2065
- };
2066
- window.addEventListener("message", messageHandler);
2067
- let html;
2068
- if (fetchedResource.text) html = fetchedResource.text;
2069
- else if (fetchedResource.blob) html = atob(fetchedResource.blob);
2070
- else throw new Error("Resource has no text or blob content");
2071
- sendNotification("ui/notifications/sandbox-resource-ready", { html });
2072
- } catch (err) {
2073
- console.error("[MCPAppsRenderer] Setup error:", err);
2074
- if (mounted) setError(err instanceof Error ? err : new Error(String(err)));
2075
- }
2076
- };
2077
- setup();
1794
+ import("@copilotkit/web-inspector").then((mod) => {
1795
+ mod.defineWebInspector?.();
1796
+ const Component = createComponent({
1797
+ tagName: mod.WEB_INSPECTOR_TAG,
1798
+ elementClass: mod.WebInspectorElement,
1799
+ react: React$1
1800
+ });
1801
+ if (mounted) setInspectorComponent(() => Component);
1802
+ });
2078
1803
  return () => {
2079
1804
  mounted = false;
2080
- if (initialListener) {
2081
- window.removeEventListener("message", initialListener);
2082
- initialListener = null;
2083
- }
2084
- if (messageHandler) window.removeEventListener("message", messageHandler);
2085
- if (createdIframe) {
2086
- createdIframe.remove();
2087
- createdIframe = null;
2088
- }
2089
- iframeRef.current = null;
2090
1805
  };
2091
- }, [
2092
- isLoading,
2093
- fetchedResource,
2094
- sendNotification,
2095
- sendResponse,
2096
- sendErrorResponse
2097
- ]);
2098
- useEffect(() => {
2099
- if (iframeRef.current) {
2100
- if (iframeSize.width !== void 0) {
2101
- iframeRef.current.style.minWidth = `min(${iframeSize.width}px, 100%)`;
2102
- iframeRef.current.style.width = "100%";
2103
- }
2104
- if (iframeSize.height !== void 0) iframeRef.current.style.height = `${iframeSize.height}px`;
2105
- }
2106
- }, [iframeSize]);
2107
- useEffect(() => {
2108
- if (iframeReady && content.toolInput) {
2109
- console.log("[MCPAppsRenderer] Sending tool input:", content.toolInput);
2110
- sendNotification("ui/notifications/tool-input", { arguments: content.toolInput });
2111
- }
2112
- }, [
2113
- iframeReady,
2114
- content.toolInput,
2115
- sendNotification
2116
- ]);
2117
- useEffect(() => {
2118
- if (iframeReady && content.result) {
2119
- console.log("[MCPAppsRenderer] Sending tool result:", content.result);
2120
- sendNotification("ui/notifications/tool-result", content.result);
2121
- }
2122
- }, [
2123
- iframeReady,
2124
- content.result,
2125
- sendNotification
2126
- ]);
2127
- const borderStyle = fetchedResource?._meta?.ui?.prefersBorder === true ? {
2128
- borderRadius: "8px",
2129
- backgroundColor: "#f9f9f9",
2130
- border: "1px solid #e0e0e0"
2131
- } : {};
2132
- return /* @__PURE__ */ jsxs("div", {
2133
- ref: containerRef,
2134
- style: {
2135
- width: "100%",
2136
- height: iframeSize.height ? `${iframeSize.height}px` : "auto",
2137
- minHeight: "100px",
2138
- overflow: "hidden",
2139
- position: "relative",
2140
- ...borderStyle
2141
- },
2142
- children: [isLoading && /* @__PURE__ */ jsx("div", {
2143
- style: {
2144
- padding: "1rem",
2145
- color: "#666"
2146
- },
2147
- children: "Loading..."
2148
- }), error && /* @__PURE__ */ jsxs("div", {
2149
- style: {
2150
- color: "red",
2151
- padding: "1rem"
2152
- },
2153
- children: ["Error: ", error.message]
2154
- })]
1806
+ }, []);
1807
+ if (!InspectorComponent) return null;
1808
+ return /* @__PURE__ */ jsx(InspectorComponent, {
1809
+ ...rest,
1810
+ core: core ?? null
2155
1811
  });
2156
1812
  };
1813
+ CopilotKitInspector.displayName = "CopilotKitInspector";
2157
1814
 
2158
1815
  //#endregion
2159
- //#region src/v2/providers/SandboxFunctionsContext.ts
2160
- const SandboxFunctionsContext = createContext([]);
2161
- function useSandboxFunctions() {
2162
- return useContext(SandboxFunctionsContext);
1816
+ //#region src/v2/components/license-warning-banner.tsx
1817
+ const LICENSE_BANNER_OFFSET_PX = 52;
1818
+ const LICENSE_BANNER_OFFSET_VAR = "--copilotkit-license-banner-offset";
1819
+ const BANNER_STYLES = {
1820
+ base: {
1821
+ position: "fixed",
1822
+ bottom: "8px",
1823
+ left: "50%",
1824
+ transform: "translateX(-50%)",
1825
+ zIndex: 99999,
1826
+ display: "inline-flex",
1827
+ alignItems: "center",
1828
+ gap: "12px",
1829
+ whiteSpace: "nowrap",
1830
+ padding: "8px 16px",
1831
+ fontSize: "13px",
1832
+ fontFamily: "-apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif",
1833
+ borderRadius: "6px",
1834
+ boxShadow: "0 2px 8px rgba(0, 0, 0, 0.15)"
1835
+ },
1836
+ info: {
1837
+ backgroundColor: "#eff6ff",
1838
+ border: "1px solid #93c5fd",
1839
+ color: "#1e40af"
1840
+ },
1841
+ warning: {
1842
+ backgroundColor: "#fffbeb",
1843
+ border: "1px solid #fbbf24",
1844
+ color: "#92400e"
1845
+ },
1846
+ critical: {
1847
+ backgroundColor: "#fef2f2",
1848
+ border: "1px solid #fca5a5",
1849
+ color: "#991b1b"
1850
+ }
1851
+ };
1852
+ function getSeverityStyle(severity) {
1853
+ switch (severity) {
1854
+ case "warning": return BANNER_STYLES.warning;
1855
+ case "critical": return BANNER_STYLES.critical;
1856
+ default: return BANNER_STYLES.info;
1857
+ }
2163
1858
  }
2164
-
2165
- //#endregion
2166
- //#region src/v2/lib/processPartialHtml.ts
2167
- /**
2168
- * Extracts all complete `<style>` blocks from the raw HTML.
2169
- * Returns the concatenated style tags, suitable for injection into `<head>`.
2170
- */
2171
- function extractCompleteStyles(html) {
2172
- const matches = html.match(/<style\b[^>]*>[\s\S]*?<\/style>/gi);
2173
- return matches ? matches.join("") : "";
1859
+ function BannerShell({ severity, message, actionLabel, actionUrl, onDismiss }) {
1860
+ useEffect(() => {
1861
+ if (typeof document === "undefined") return;
1862
+ const root = document.documentElement;
1863
+ root.style.setProperty(LICENSE_BANNER_OFFSET_VAR, `${LICENSE_BANNER_OFFSET_PX}px`);
1864
+ return () => {
1865
+ root.style.removeProperty(LICENSE_BANNER_OFFSET_VAR);
1866
+ };
1867
+ }, []);
1868
+ return /* @__PURE__ */ jsxs("div", {
1869
+ style: {
1870
+ ...BANNER_STYLES.base,
1871
+ ...getSeverityStyle(severity)
1872
+ },
1873
+ children: [/* @__PURE__ */ jsx("span", { children: message }), /* @__PURE__ */ jsxs("div", {
1874
+ style: {
1875
+ display: "flex",
1876
+ gap: "8px",
1877
+ alignItems: "center"
1878
+ },
1879
+ children: [/* @__PURE__ */ jsx("a", {
1880
+ href: actionUrl,
1881
+ target: "_blank",
1882
+ rel: "noopener noreferrer",
1883
+ style: {
1884
+ fontWeight: 600,
1885
+ textDecoration: "underline",
1886
+ color: "inherit"
1887
+ },
1888
+ children: actionLabel
1889
+ }), onDismiss && /* @__PURE__ */ jsx("button", {
1890
+ onClick: onDismiss,
1891
+ style: {
1892
+ background: "none",
1893
+ border: "none",
1894
+ cursor: "pointer",
1895
+ color: "inherit",
1896
+ fontSize: "16px"
1897
+ },
1898
+ children: "×"
1899
+ })]
1900
+ })]
1901
+ });
2174
1902
  }
2175
- /**
2176
- * Processes raw accumulated HTML for safe preview via innerHTML injection.
2177
- * Pure function, no DOM dependencies.
2178
- *
2179
- * Pipeline (order matters):
2180
- * 1. Strip incomplete tag at end
2181
- * 2. Strip complete <style>, <script>, and <head> blocks
2182
- * 3. Strip incomplete <style>/<script>/<head> blocks
2183
- * 4. Strip incomplete HTML entities
2184
- * 5. Extract body content (or use full string if no <body>)
2185
- */
2186
- function processPartialHtml(html) {
2187
- let result = html;
2188
- result = result.replace(/<[^>]*$/, "");
2189
- result = result.replace(/<(style|script|head)\b[^>]*>[\s\S]*?<\/\1>/gi, "");
2190
- result = result.replace(/<(style|script|head)\b[^>]*>[\s\S]*$/gi, "");
2191
- result = result.replace(/&[a-zA-Z0-9#]*$/, "");
2192
- const bodyMatch = result.match(/<body[^>]*>([\s\S]*)/i);
2193
- if (bodyMatch) {
2194
- result = bodyMatch[1];
2195
- result = result.replace(/<\/body>[\s\S]*/i, "");
1903
+ function LicenseWarningBanner({ type, featureName, expiryDate, graceRemaining, onDismiss }) {
1904
+ switch (type) {
1905
+ case "no_license": return /* @__PURE__ */ jsx(BannerShell, {
1906
+ severity: "info",
1907
+ message: "Powered by CopilotKit",
1908
+ actionLabel: "Get a license",
1909
+ actionUrl: "https://copilotkit.ai/pricing",
1910
+ onDismiss
1911
+ });
1912
+ case "feature_unlicensed": return /* @__PURE__ */ jsx(BannerShell, {
1913
+ severity: "warning",
1914
+ message: `⚠ The "${featureName}" feature requires a CopilotKit license.`,
1915
+ actionLabel: "Get a license",
1916
+ actionUrl: "https://copilotkit.ai/pricing",
1917
+ onDismiss
1918
+ });
1919
+ case "expiring": return /* @__PURE__ */ jsx(BannerShell, {
1920
+ severity: "warning",
1921
+ message: `Your CopilotKit license expires in ${graceRemaining} day${graceRemaining !== 1 ? "s" : ""}. Please renew.`,
1922
+ actionLabel: "Renew",
1923
+ actionUrl: "https://cloud.copilotkit.ai",
1924
+ onDismiss
1925
+ });
1926
+ case "expired": return /* @__PURE__ */ jsx(BannerShell, {
1927
+ severity: "critical",
1928
+ message: `Your CopilotKit license expired${expiryDate ? ` on ${expiryDate}` : ""}. Please renew at copilotkit.ai/pricing`,
1929
+ actionLabel: "Renew now",
1930
+ actionUrl: "https://copilotkit.ai/pricing",
1931
+ onDismiss
1932
+ });
1933
+ case "invalid": return /* @__PURE__ */ jsx(BannerShell, {
1934
+ severity: "critical",
1935
+ message: "Invalid CopilotKit license token. Please check your configuration.",
1936
+ actionLabel: "Get a license",
1937
+ actionUrl: "https://copilotkit.ai/pricing",
1938
+ onDismiss
1939
+ });
1940
+ default: return null;
2196
1941
  }
2197
- return result;
1942
+ }
1943
+ function InlineFeatureWarning({ featureName }) {
1944
+ return /* @__PURE__ */ jsxs("div", {
1945
+ style: {
1946
+ padding: "8px 12px",
1947
+ backgroundColor: "#fffbeb",
1948
+ border: "1px solid #fbbf24",
1949
+ borderRadius: "6px",
1950
+ fontSize: "13px",
1951
+ color: "#92400e",
1952
+ fontFamily: "-apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif"
1953
+ },
1954
+ children: [
1955
+ "⚠ The \"",
1956
+ featureName,
1957
+ "\" feature requires a CopilotKit license.",
1958
+ " ",
1959
+ /* @__PURE__ */ jsx("a", {
1960
+ href: "https://copilotkit.ai/pricing",
1961
+ target: "_blank",
1962
+ rel: "noopener noreferrer",
1963
+ style: {
1964
+ color: "#b45309",
1965
+ textDecoration: "underline"
1966
+ },
1967
+ children: "Get one at copilotkit.ai/pricing"
1968
+ })
1969
+ ]
1970
+ });
2198
1971
  }
2199
1972
 
2200
1973
  //#endregion
2201
- //#region src/v2/components/OpenGenerativeUIRenderer.tsx
2202
- const OpenGenerativeUIActivityType = "open-generative-ui";
2203
- const OpenGenerativeUIContentSchema = z.object({
2204
- initialHeight: z.number().optional(),
2205
- generating: z.boolean().optional(),
2206
- css: z.string().optional(),
2207
- cssComplete: z.boolean().optional(),
2208
- html: z.array(z.string()).optional(),
2209
- htmlComplete: z.boolean().optional(),
2210
- jsFunctions: z.string().optional(),
2211
- jsFunctionsComplete: z.boolean().optional(),
2212
- jsExpressions: z.array(z.string()).optional(),
2213
- jsExpressionsComplete: z.boolean().optional()
2214
- });
2215
- /**
2216
- * Schema for the generateSandboxedUi tool call arguments.
2217
- * Used by the frontend tool renderer to display placeholder messages.
2218
- */
2219
- const GenerateSandboxedUiArgsSchema = z.object({
2220
- initialHeight: z.number().optional(),
2221
- placeholderMessages: z.array(z.string()).optional(),
2222
- css: z.string().optional(),
2223
- html: z.string().optional(),
2224
- jsFunctions: z.string().optional(),
2225
- jsExpressions: z.array(z.string()).optional()
1974
+ //#region src/v2/components/MCPAppsActivityRenderer.tsx
1975
+ const PROTOCOL_VERSION = "2025-06-18";
1976
+ function buildSandboxHTML(extraCspDomains) {
1977
+ const baseScriptSrc = "'self' 'wasm-unsafe-eval' 'unsafe-inline' 'unsafe-eval' blob: data: http://localhost:* https://localhost:*";
1978
+ const baseFrameSrc = "* blob: data: http://localhost:* https://localhost:*";
1979
+ const extra = extraCspDomains?.length ? " " + extraCspDomains.join(" ") : "";
1980
+ return `<!doctype html>
1981
+ <html>
1982
+ <head>
1983
+ <meta charset="utf-8" />
1984
+ <meta http-equiv="Content-Security-Policy" content="default-src 'self'; img-src * data: blob: 'unsafe-inline'; media-src * blob: data:; font-src * blob: data:; script-src ${baseScriptSrc + extra}; style-src * blob: data: 'unsafe-inline'; connect-src *; frame-src ${baseFrameSrc + extra}; base-uri 'self';" />
1985
+ <style>html,body{margin:0;padding:0;height:100%;width:100%;overflow:hidden}*{box-sizing:border-box}iframe{background-color:transparent;border:none;padding:0;overflow:hidden;width:100%;height:100%}</style>
1986
+ </head>
1987
+ <body>
1988
+ <script>
1989
+ if(window.self===window.top){throw new Error("This file must be used in an iframe.")}
1990
+ const inner=document.createElement("iframe");
1991
+ inner.style="width:100%;height:100%;border:none;";
1992
+ inner.setAttribute("sandbox","allow-scripts allow-same-origin allow-forms");
1993
+ document.body.appendChild(inner);
1994
+ window.addEventListener("message",async(event)=>{
1995
+ if(event.source===window.parent){
1996
+ if(event.data&&event.data.method==="ui/notifications/sandbox-resource-ready"){
1997
+ const{html,sandbox}=event.data.params;
1998
+ if(typeof sandbox==="string")inner.setAttribute("sandbox",sandbox);
1999
+ if(typeof html==="string")inner.srcdoc=html;
2000
+ }else if(inner&&inner.contentWindow){
2001
+ inner.contentWindow.postMessage(event.data,"*");
2002
+ }
2003
+ }else if(event.source===inner.contentWindow){
2004
+ window.parent.postMessage(event.data,"*");
2005
+ }
2226
2006
  });
2227
- const THROTTLE_MS = 1e3;
2007
+ window.parent.postMessage({jsonrpc:"2.0",method:"ui/notifications/sandbox-proxy-ready",params:{}},"*");
2008
+ <\/script>
2009
+ </body>
2010
+ </html>`;
2011
+ }
2228
2012
  /**
2229
- * Returns true when the inner component should re-render immediately
2230
- * (no throttle delay).
2013
+ * Queue for serializing MCP app requests to an agent.
2014
+ * Ensures requests wait for the agent to stop running and are processed one at a time.
2231
2015
  */
2232
- function shouldFlushImmediately(prev, next) {
2233
- if (next.cssComplete && (!prev || !prev.cssComplete)) return true;
2234
- if (next.htmlComplete) return true;
2235
- if (next.generating === false) return true;
2236
- if (next.jsFunctions && (!prev || !prev.jsFunctions)) return true;
2237
- if ((next.jsExpressions?.length ?? 0) > (prev?.jsExpressions?.length ?? 0)) return true;
2238
- if (next.html?.length && (!prev || !prev.html?.length)) return true;
2239
- return false;
2240
- }
2241
- /**
2242
- * Outer wrapper absorbs every parent re-render but only forwards
2243
- * throttled content snapshots to the memoized inner component.
2244
- */
2245
- const OpenGenerativeUIActivityRenderer = function OpenGenerativeUIActivityRenderer({ content }) {
2246
- const latestContentRef = useRef(content);
2247
- latestContentRef.current = content;
2248
- const [throttledContent, setThrottledContent] = useState(content);
2249
- const throttledContentRef = useRef(throttledContent);
2250
- const timerRef = useRef(null);
2251
- if (throttledContentRef.current !== content) {
2252
- if (shouldFlushImmediately(throttledContentRef.current, content)) {
2253
- if (timerRef.current !== null) {
2254
- clearTimeout(timerRef.current);
2255
- timerRef.current = null;
2016
+ var MCPAppsRequestQueue = class {
2017
+ constructor() {
2018
+ this.queues = /* @__PURE__ */ new Map();
2019
+ this.processing = /* @__PURE__ */ new Map();
2020
+ }
2021
+ /**
2022
+ * Add a request to the queue for a specific agent thread.
2023
+ * Returns a promise that resolves when the request completes.
2024
+ */
2025
+ async enqueue(agent, request) {
2026
+ const threadId = agent.threadId || "default";
2027
+ return new Promise((resolve, reject) => {
2028
+ let queue = this.queues.get(threadId);
2029
+ if (!queue) {
2030
+ queue = [];
2031
+ this.queues.set(threadId, queue);
2256
2032
  }
2257
- throttledContentRef.current = content;
2258
- setThrottledContent(content);
2033
+ queue.push({
2034
+ execute: request,
2035
+ resolve,
2036
+ reject
2037
+ });
2038
+ this.processQueue(threadId, agent);
2039
+ });
2040
+ }
2041
+ async processQueue(threadId, agent) {
2042
+ if (this.processing.get(threadId)) return;
2043
+ this.processing.set(threadId, true);
2044
+ try {
2045
+ const queue = this.queues.get(threadId);
2046
+ if (!queue) return;
2047
+ while (queue.length > 0) {
2048
+ const item = queue[0];
2049
+ try {
2050
+ await this.waitForAgentIdle(agent);
2051
+ const result = await item.execute();
2052
+ item.resolve(result);
2053
+ } catch (error) {
2054
+ item.reject(error instanceof Error ? error : new Error(String(error)));
2055
+ }
2056
+ queue.shift();
2057
+ }
2058
+ } finally {
2059
+ this.processing.set(threadId, false);
2259
2060
  }
2260
2061
  }
2261
- const flush = useCallback(() => {
2262
- timerRef.current = null;
2263
- const latest = latestContentRef.current;
2264
- throttledContentRef.current = latest;
2265
- setThrottledContent(latest);
2266
- }, []);
2267
- useEffect(() => {
2268
- if (throttledContentRef.current === content) return;
2269
- if (timerRef.current === null) timerRef.current = setTimeout(flush, THROTTLE_MS);
2270
- }, [content, flush]);
2271
- useEffect(() => {
2272
- return () => {
2273
- if (timerRef.current !== null) clearTimeout(timerRef.current);
2274
- };
2275
- }, []);
2276
- return /* @__PURE__ */ jsx(OpenGenerativeUIActivityRendererInner, { content: throttledContent });
2062
+ waitForAgentIdle(agent) {
2063
+ return new Promise((resolve) => {
2064
+ if (!agent.isRunning) {
2065
+ resolve();
2066
+ return;
2067
+ }
2068
+ let done = false;
2069
+ const finish = () => {
2070
+ if (done) return;
2071
+ done = true;
2072
+ clearInterval(checkInterval);
2073
+ sub.unsubscribe();
2074
+ resolve();
2075
+ };
2076
+ const sub = agent.subscribe({
2077
+ onRunFinalized: finish,
2078
+ onRunFailed: finish
2079
+ });
2080
+ const checkInterval = setInterval(() => {
2081
+ if (!agent.isRunning) finish();
2082
+ }, 500);
2083
+ });
2084
+ }
2277
2085
  };
2278
- function ensureHead(html) {
2279
- if (/<head[\s>]/i.test(html)) return html;
2280
- return `<head></head>${html}`;
2086
+ const mcpAppsRequestQueue = new MCPAppsRequestQueue();
2087
+ /**
2088
+ * Activity type for MCP Apps events - must match the middleware's MCPAppsActivityType
2089
+ */
2090
+ const MCPAppsActivityType = "mcp-apps";
2091
+ const MCPAppsActivityContentSchema = z.object({
2092
+ result: z.object({
2093
+ content: z.array(z.any()).optional(),
2094
+ structuredContent: z.any().optional(),
2095
+ isError: z.boolean().optional()
2096
+ }),
2097
+ resourceUri: z.string(),
2098
+ serverHash: z.string(),
2099
+ serverId: z.string().optional(),
2100
+ toolInput: z.record(z.unknown()).optional()
2101
+ });
2102
+ function isRequest(msg) {
2103
+ return "id" in msg && "method" in msg;
2281
2104
  }
2282
- function injectCssIntoHtml(html, css) {
2283
- const headCloseIdx = html.indexOf("</head>");
2284
- if (headCloseIdx !== -1) return html.slice(0, headCloseIdx) + `<style>${css}</style>` + html.slice(headCloseIdx);
2285
- return `<head><style>${css}</style></head>${html}`;
2105
+ function isNotification(msg) {
2106
+ return !("id" in msg) && "method" in msg;
2286
2107
  }
2287
- const OpenGenerativeUIActivityRendererInner = React.memo(function OpenGenerativeUIActivityRendererInner({ content }) {
2288
- const initialHeight = content.initialHeight ?? 200;
2289
- const [autoHeight, setAutoHeight] = useState(null);
2290
- const sandboxFunctions = useSandboxFunctions();
2291
- const localApi = useMemo(() => {
2292
- const api = {};
2293
- for (const fn of sandboxFunctions) api[fn.name] = fn.handler;
2294
- return api;
2295
- }, [sandboxFunctions]);
2296
- const fullHtml = content.htmlComplete && content.html?.length ? content.html.join("") : void 0;
2297
- const css = content.cssComplete ? content.css : void 0;
2298
- const cssReady = !!content.cssComplete;
2299
- const partialHtml = !content.htmlComplete && content.html?.length ? content.html.join("") : void 0;
2300
- const previewBody = partialHtml ? processPartialHtml(partialHtml) : void 0;
2301
- const previewStyles = partialHtml ? extractCompleteStyles(partialHtml) : "";
2302
- const hasPreview = cssReady && !!previewBody?.trim();
2303
- const hasVisibleSandbox = !!fullHtml || hasPreview;
2108
+ /**
2109
+ * MCP Apps Extension Activity Renderer
2110
+ *
2111
+ * Renders MCP Apps UI in a sandboxed iframe with full protocol support.
2112
+ * Fetches resource content on-demand via proxied MCP requests.
2113
+ */
2114
+ const MCPAppsActivityRenderer = function MCPAppsActivityRenderer({ content, agent }) {
2115
+ const { copilotkit } = useCopilotKit();
2304
2116
  const containerRef = useRef(null);
2305
- const sandboxRef = useRef(null);
2306
- const previewSandboxRef = useRef(null);
2307
- const previewReadyRef = useRef(false);
2308
- const sandboxReadyRef = useRef(false);
2309
- const executedIndexRef = useRef(0);
2310
- const pendingQueueRef = useRef([]);
2311
- const jsFunctionsInjectedRef = useRef(false);
2312
- useEffect(() => {
2313
- const container = containerRef.current;
2314
- if (!container || fullHtml || !hasPreview || previewSandboxRef.current) return;
2315
- let cancelled = false;
2316
- import("@jetbrains/websandbox").then((mod) => {
2317
- if (cancelled) return;
2318
- const sandbox = (mod.default?.default ?? mod.default).create({}, {
2319
- frameContainer: container,
2320
- frameContent: "<head></head><body></body>",
2321
- allowAdditionalAttributes: ""
2322
- });
2323
- previewSandboxRef.current = sandbox;
2324
- sandbox.iframe.style.width = "100%";
2325
- sandbox.iframe.style.height = "100%";
2326
- sandbox.iframe.style.border = "none";
2327
- sandbox.iframe.style.backgroundColor = "transparent";
2328
- sandbox.promise.then(() => {
2329
- if (cancelled) return;
2330
- previewReadyRef.current = true;
2331
- sandbox.run(`
2332
- var s = document.createElement('style');
2333
- s.textContent = 'html, body { overflow: hidden !important; }';
2334
- document.head.appendChild(s);
2335
- `);
2336
- const headParts = [];
2337
- if (css) headParts.push(`<style>${css}</style>`);
2338
- if (previewStyles) headParts.push(previewStyles);
2339
- if (headParts.length) sandbox.run(`document.head.innerHTML = ${JSON.stringify(headParts.join(""))}`);
2340
- if (previewBody) sandbox.run(`document.body.innerHTML = ${JSON.stringify(previewBody)}`);
2341
- });
2342
- }).catch((err) => {
2343
- console.error("[OpenGenerativeUI] Failed to load sandbox module:", err);
2117
+ const iframeRef = useRef(null);
2118
+ const [iframeReady, setIframeReady] = useState(false);
2119
+ const [error, setError] = useState(null);
2120
+ const [isLoading, setIsLoading] = useState(true);
2121
+ const [iframeSize, setIframeSize] = useState({});
2122
+ const [fetchedResource, setFetchedResource] = useState(null);
2123
+ const contentRef = useRef(content);
2124
+ contentRef.current = content;
2125
+ const agentRef = useRef(agent);
2126
+ agentRef.current = agent;
2127
+ const fetchStateRef = useRef({
2128
+ inProgress: false,
2129
+ promise: null,
2130
+ resourceUri: null
2131
+ });
2132
+ const sendToIframe = useCallback((msg) => {
2133
+ if (iframeRef.current?.contentWindow) {
2134
+ console.log("[MCPAppsRenderer] Sending to iframe:", msg);
2135
+ iframeRef.current.contentWindow.postMessage(msg, "*");
2136
+ }
2137
+ }, []);
2138
+ const sendResponse = useCallback((id, result) => {
2139
+ sendToIframe({
2140
+ jsonrpc: "2.0",
2141
+ id,
2142
+ result
2344
2143
  });
2345
- return () => {
2346
- cancelled = true;
2347
- };
2348
- }, [hasPreview, fullHtml]);
2144
+ }, [sendToIframe]);
2145
+ const sendErrorResponse = useCallback((id, code, message) => {
2146
+ sendToIframe({
2147
+ jsonrpc: "2.0",
2148
+ id,
2149
+ error: {
2150
+ code,
2151
+ message
2152
+ }
2153
+ });
2154
+ }, [sendToIframe]);
2155
+ const sendNotification = useCallback((method, params) => {
2156
+ sendToIframe({
2157
+ jsonrpc: "2.0",
2158
+ method,
2159
+ params: params || {}
2160
+ });
2161
+ }, [sendToIframe]);
2349
2162
  useEffect(() => {
2350
- if (!previewSandboxRef.current || !previewReadyRef.current) return;
2351
- const headParts = [];
2352
- if (css) headParts.push(`<style>${css}</style>`);
2353
- if (previewStyles) headParts.push(previewStyles);
2354
- if (headParts.length) previewSandboxRef.current.run(`document.head.innerHTML = ${JSON.stringify(headParts.join(""))}`);
2355
- if (!previewBody) return;
2356
- previewSandboxRef.current.run(`document.body.innerHTML = ${JSON.stringify(previewBody)}`);
2357
- }, [
2358
- previewBody,
2359
- previewStyles,
2360
- css
2361
- ]);
2362
- useEffect(() => {
2363
- const container = containerRef.current;
2364
- if (!container || !fullHtml) return;
2365
- if (previewSandboxRef.current) {
2366
- previewSandboxRef.current.destroy();
2367
- previewSandboxRef.current = null;
2368
- previewReadyRef.current = false;
2369
- }
2370
- let cancelled = false;
2371
- executedIndexRef.current = 0;
2372
- jsFunctionsInjectedRef.current = false;
2373
- sandboxReadyRef.current = false;
2374
- pendingQueueRef.current = [];
2375
- const htmlContent = css ? injectCssIntoHtml(fullHtml, css) : fullHtml;
2376
- import("@jetbrains/websandbox").then((mod) => {
2377
- if (cancelled) return;
2378
- const sandbox = (mod.default?.default ?? mod.default).create(localApi, {
2379
- frameContainer: container,
2380
- frameContent: ensureHead(htmlContent),
2381
- allowAdditionalAttributes: ""
2382
- });
2383
- sandboxRef.current = sandbox;
2384
- sandbox.iframe.style.width = "100%";
2385
- sandbox.iframe.style.height = "100%";
2386
- sandbox.iframe.style.border = "none";
2387
- sandbox.iframe.style.backgroundColor = "transparent";
2388
- sandbox.promise.then(() => {
2389
- if (cancelled) return;
2390
- sandboxReadyRef.current = true;
2391
- sandbox.run(`
2392
- var s = document.createElement('style');
2393
- s.textContent = 'html, body { overflow: hidden !important; }';
2394
- document.head.appendChild(s);
2395
- `);
2396
- const queue = pendingQueueRef.current;
2397
- pendingQueueRef.current = [];
2398
- for (const code of queue) sandbox.run(code);
2163
+ const { resourceUri, serverHash, serverId } = content;
2164
+ if (fetchStateRef.current.inProgress && fetchStateRef.current.resourceUri === resourceUri) {
2165
+ fetchStateRef.current.promise?.then((resource) => {
2166
+ if (resource) {
2167
+ setFetchedResource(resource);
2168
+ setIsLoading(false);
2169
+ }
2170
+ }).catch((err) => {
2171
+ setError(err instanceof Error ? err : new Error(String(err)));
2172
+ setIsLoading(false);
2399
2173
  });
2400
- }).catch((err) => {
2401
- console.error("[OpenGenerativeUI] Failed to load sandbox module:", err);
2402
- });
2403
- return () => {
2404
- cancelled = true;
2405
- if (previewSandboxRef.current) {
2406
- previewSandboxRef.current.destroy();
2407
- previewSandboxRef.current = null;
2408
- previewReadyRef.current = false;
2409
- }
2410
- if (sandboxRef.current) {
2411
- sandboxRef.current.destroy();
2412
- sandboxRef.current = null;
2174
+ return;
2175
+ }
2176
+ if (!agent) {
2177
+ setError(/* @__PURE__ */ new Error("No agent available to fetch resource"));
2178
+ setIsLoading(false);
2179
+ return;
2180
+ }
2181
+ fetchStateRef.current.inProgress = true;
2182
+ fetchStateRef.current.resourceUri = resourceUri;
2183
+ const fetchPromise = (async () => {
2184
+ try {
2185
+ const resource = (await mcpAppsRequestQueue.enqueue(agent, () => agent.runAgent({ forwardedProps: { __proxiedMCPRequest: {
2186
+ serverHash,
2187
+ serverId,
2188
+ method: "resources/read",
2189
+ params: { uri: resourceUri }
2190
+ } } }))).result?.contents?.[0];
2191
+ if (!resource) throw new Error("No resource content in response");
2192
+ return resource;
2193
+ } catch (err) {
2194
+ console.error("[MCPAppsRenderer] Failed to fetch resource:", err);
2195
+ throw err;
2196
+ } finally {
2197
+ fetchStateRef.current.inProgress = false;
2413
2198
  }
2414
- sandboxReadyRef.current = false;
2415
- setAutoHeight(null);
2416
- };
2417
- }, [
2418
- fullHtml,
2419
- css,
2420
- localApi
2421
- ]);
2422
- useEffect(() => {
2423
- if (!content.jsFunctions || jsFunctionsInjectedRef.current) return;
2424
- jsFunctionsInjectedRef.current = true;
2425
- const sandbox = sandboxRef.current;
2426
- if (sandboxReadyRef.current && sandbox) sandbox.run(content.jsFunctions);
2427
- else pendingQueueRef.current.push(content.jsFunctions);
2428
- }, [content.jsFunctions]);
2429
- useEffect(() => {
2430
- const expressions = content.jsExpressions;
2431
- if (!expressions || expressions.length === 0) return;
2432
- const startIndex = executedIndexRef.current;
2433
- if (startIndex >= expressions.length) return;
2434
- const newExprs = expressions.slice(startIndex);
2435
- executedIndexRef.current = expressions.length;
2436
- const sandbox = sandboxRef.current;
2437
- if (sandboxReadyRef.current && sandbox) (async () => {
2438
- for (const expr of newExprs) await sandbox.run(expr);
2439
2199
  })();
2440
- else pendingQueueRef.current.push(...newExprs);
2441
- }, [content.jsExpressions?.length]);
2442
- const generationDone = content.generating === false;
2443
- useEffect(() => {
2444
- const sandbox = sandboxRef.current;
2445
- if (!generationDone || !sandbox) return;
2446
- let handled = false;
2447
- const onMessage = (e) => {
2448
- if (handled) return;
2449
- if (e.source === sandbox.iframe.contentWindow && e.data?.type === "__ck_resize") {
2450
- handled = true;
2451
- setAutoHeight(e.data.height);
2452
- window.removeEventListener("message", onMessage);
2200
+ fetchStateRef.current.promise = fetchPromise;
2201
+ fetchPromise.then((resource) => {
2202
+ if (resource) {
2203
+ setFetchedResource(resource);
2204
+ setIsLoading(false);
2453
2205
  }
2454
- };
2455
- window.addEventListener("message", onMessage);
2456
- const measureOnce = `
2457
- (function() {
2458
- var s = document.createElement('style');
2459
- s.textContent = 'body { height: auto !important; min-height: 0 !important; }';
2460
- document.head.appendChild(s);
2461
- var h = document.body.scrollHeight;
2462
- var cs = getComputedStyle(document.body);
2463
- h += parseFloat(cs.marginTop) || 0;
2464
- h += parseFloat(cs.marginBottom) || 0;
2465
- s.remove();
2466
- parent.postMessage({ type: "__ck_resize", height: Math.ceil(h) }, "*");
2467
- })();
2468
- `;
2469
- if (sandboxReadyRef.current) sandbox.run(measureOnce);
2470
- else pendingQueueRef.current.push(measureOnce);
2471
- return () => {
2472
- window.removeEventListener("message", onMessage);
2473
- };
2474
- }, [generationDone]);
2475
- const height = autoHeight ?? initialHeight;
2476
- const isGenerating = content.generating !== false;
2477
- return /* @__PURE__ */ jsx("div", {
2478
- ref: containerRef,
2479
- style: {
2480
- position: "relative",
2481
- width: "100%",
2482
- height: `${height}px`,
2483
- borderRadius: "8px",
2484
- backgroundColor: hasVisibleSandbox ? "transparent" : "#f5f5f5",
2485
- border: hasVisibleSandbox ? "none" : "1px solid #e0e0e0",
2486
- display: hasVisibleSandbox ? "block" : "flex",
2487
- alignItems: hasVisibleSandbox ? void 0 : "center",
2488
- justifyContent: hasVisibleSandbox ? void 0 : "center",
2489
- overflow: "hidden"
2490
- },
2491
- children: isGenerating && /* @__PURE__ */ jsxs("div", {
2492
- style: {
2493
- position: "absolute",
2494
- inset: 0,
2495
- zIndex: 10,
2496
- pointerEvents: "all",
2497
- backgroundColor: "rgba(255, 255, 255, 0.5)",
2498
- display: "flex",
2499
- alignItems: "center",
2500
- justifyContent: "center"
2501
- },
2502
- children: [/* @__PURE__ */ jsxs("svg", {
2503
- width: "48",
2504
- height: "48",
2505
- viewBox: "0 0 24 24",
2506
- fill: "none",
2507
- style: { animation: "ck-spin 1s linear infinite" },
2508
- children: [/* @__PURE__ */ jsx("circle", {
2509
- cx: "12",
2510
- cy: "12",
2511
- r: "10",
2512
- stroke: "#e0e0e0",
2513
- strokeWidth: "3"
2514
- }), /* @__PURE__ */ jsx("path", {
2515
- d: "M12 2a10 10 0 0 1 10 10",
2516
- stroke: "#999",
2517
- strokeWidth: "3",
2518
- strokeLinecap: "round"
2519
- })]
2520
- }), /* @__PURE__ */ jsx("style", { children: `@keyframes ck-spin { to { transform: rotate(360deg) } }` })]
2521
- })
2522
- });
2523
- }, (prev, next) => prev.content === next.content);
2524
- /**
2525
- * Frontend tool renderer for generateSandboxedUi.
2526
- * Displays placeholder messages while the UI is being generated.
2527
- */
2528
- const OpenGenerativeUIToolRenderer = function OpenGenerativeUIToolRenderer(props) {
2529
- const [visibleMessageIndex, setVisibleMessageIndex] = useState(0);
2530
- const prevMessageCountRef = useRef(0);
2531
- const messages = props.args.placeholderMessages;
2206
+ }).catch((err) => {
2207
+ setError(err instanceof Error ? err : new Error(String(err)));
2208
+ setIsLoading(false);
2209
+ });
2210
+ }, [agent, content]);
2532
2211
  useEffect(() => {
2533
- if (!messages || messages.length === 0) return;
2534
- if (messages.length !== prevMessageCountRef.current) {
2535
- prevMessageCountRef.current = messages.length;
2536
- setVisibleMessageIndex(messages.length - 1);
2537
- }
2538
- if (props.status === ToolCallStatus.Complete) return;
2539
- const timer = setInterval(() => {
2540
- setVisibleMessageIndex((i) => (i + 1) % messages.length);
2541
- }, 5e3);
2542
- return () => clearInterval(timer);
2543
- }, [messages?.length, props.status]);
2544
- if (props.status === ToolCallStatus.Complete) return null;
2545
- if (!messages || messages.length === 0) return null;
2546
- return /* @__PURE__ */ jsx("div", {
2547
- style: {
2548
- padding: "8px 12px",
2549
- color: "#999",
2550
- fontSize: "14px"
2551
- },
2552
- children: messages[visibleMessageIndex] ?? messages[0]
2553
- });
2554
- };
2555
-
2556
- //#endregion
2557
- //#region src/v2/a2ui/A2UIMessageRenderer.tsx
2558
- /**
2559
- * The container key used to wrap A2UI operations for explicit detection.
2560
- * Must match A2UI_OPERATIONS_KEY in @ag-ui/a2ui-middleware and copilotkit.a2ui (Python).
2561
- */
2562
- const A2UI_OPERATIONS_KEY = "a2ui_operations";
2563
- let initialized = false;
2564
- function ensureInitialized() {
2565
- if (!initialized) {
2566
- initializeDefaultCatalog();
2567
- injectStyles();
2568
- initialized = true;
2569
- }
2570
- }
2571
- function createA2UIMessageRenderer(options) {
2572
- const { theme, catalog, loadingComponent } = options;
2573
- return {
2574
- activityType: "a2ui-surface",
2575
- content: z.any(),
2576
- render: ({ content, agent }) => {
2577
- ensureInitialized();
2578
- const [operations, setOperations] = useState([]);
2579
- const { copilotkit } = useCopilotKit();
2212
+ if (isLoading || !fetchedResource) return;
2213
+ const container = containerRef.current;
2214
+ if (!container) return;
2215
+ let mounted = true;
2216
+ let messageHandler = null;
2217
+ let initialListener = null;
2218
+ let createdIframe = null;
2219
+ const setup = async () => {
2220
+ try {
2221
+ const iframe = document.createElement("iframe");
2222
+ createdIframe = iframe;
2223
+ iframe.style.width = "100%";
2224
+ iframe.style.height = "100px";
2225
+ iframe.style.border = "none";
2226
+ iframe.style.backgroundColor = "transparent";
2227
+ iframe.style.display = "block";
2228
+ iframe.setAttribute("sandbox", "allow-scripts allow-same-origin allow-forms");
2229
+ const sandboxReady = new Promise((resolve) => {
2230
+ initialListener = (event) => {
2231
+ if (event.source === iframe.contentWindow) {
2232
+ if (event.data?.method === "ui/notifications/sandbox-proxy-ready") {
2233
+ if (initialListener) {
2234
+ window.removeEventListener("message", initialListener);
2235
+ initialListener = null;
2236
+ }
2237
+ resolve();
2238
+ }
2239
+ }
2240
+ };
2241
+ window.addEventListener("message", initialListener);
2242
+ });
2243
+ if (!mounted) {
2244
+ if (initialListener) {
2245
+ window.removeEventListener("message", initialListener);
2246
+ initialListener = null;
2247
+ }
2248
+ return;
2249
+ }
2250
+ const cspDomains = fetchedResource._meta?.ui?.csp?.resourceDomains;
2251
+ iframe.srcdoc = buildSandboxHTML(cspDomains);
2252
+ iframeRef.current = iframe;
2253
+ container.appendChild(iframe);
2254
+ await sandboxReady;
2255
+ if (!mounted) return;
2256
+ console.log("[MCPAppsRenderer] Sandbox proxy ready");
2257
+ messageHandler = async (event) => {
2258
+ if (event.source !== iframe.contentWindow) return;
2259
+ const msg = event.data;
2260
+ if (!msg || typeof msg !== "object" || msg.jsonrpc !== "2.0") return;
2261
+ console.log("[MCPAppsRenderer] Received from iframe:", msg);
2262
+ if (isRequest(msg)) switch (msg.method) {
2263
+ case "ui/initialize":
2264
+ sendResponse(msg.id, {
2265
+ protocolVersion: PROTOCOL_VERSION,
2266
+ hostInfo: {
2267
+ name: "CopilotKit MCP Apps Host",
2268
+ version: "1.0.0"
2269
+ },
2270
+ hostCapabilities: {
2271
+ openLinks: {},
2272
+ logging: {}
2273
+ },
2274
+ hostContext: {
2275
+ theme: "light",
2276
+ platform: "web"
2277
+ }
2278
+ });
2279
+ break;
2280
+ case "ui/message": {
2281
+ const currentAgent = agentRef.current;
2282
+ if (!currentAgent) {
2283
+ console.warn("[MCPAppsRenderer] ui/message: No agent available");
2284
+ sendResponse(msg.id, { isError: false });
2285
+ break;
2286
+ }
2287
+ try {
2288
+ const params = msg.params;
2289
+ const role = params.role || "user";
2290
+ const textContent = params.content?.filter((c) => c.type === "text" && c.text).map((c) => c.text).join("\n") || "";
2291
+ if (textContent) currentAgent.addMessage({
2292
+ id: crypto.randomUUID(),
2293
+ role,
2294
+ content: textContent
2295
+ });
2296
+ sendResponse(msg.id, { isError: false });
2297
+ if ((params.followUp ?? role === "user") && textContent) mcpAppsRequestQueue.enqueue(currentAgent, () => copilotkit.runAgent({ agent: currentAgent })).catch((err) => console.error("[MCPAppsRenderer] ui/message agent run failed:", err));
2298
+ } catch (err) {
2299
+ console.error("[MCPAppsRenderer] ui/message error:", err);
2300
+ sendResponse(msg.id, { isError: true });
2301
+ }
2302
+ break;
2303
+ }
2304
+ case "ui/open-link": {
2305
+ const url = msg.params?.url;
2306
+ if (url) {
2307
+ window.open(url, "_blank", "noopener,noreferrer");
2308
+ sendResponse(msg.id, { isError: false });
2309
+ } else sendErrorResponse(msg.id, -32602, "Missing url parameter");
2310
+ break;
2311
+ }
2312
+ case "tools/call": {
2313
+ const { serverHash, serverId } = contentRef.current;
2314
+ const currentAgent = agentRef.current;
2315
+ if (!serverHash) {
2316
+ sendErrorResponse(msg.id, -32603, "No server hash available for proxying");
2317
+ break;
2318
+ }
2319
+ if (!currentAgent) {
2320
+ sendErrorResponse(msg.id, -32603, "No agent available for proxying");
2321
+ break;
2322
+ }
2323
+ try {
2324
+ const runResult = await mcpAppsRequestQueue.enqueue(currentAgent, () => currentAgent.runAgent({ forwardedProps: { __proxiedMCPRequest: {
2325
+ serverHash,
2326
+ serverId,
2327
+ method: "tools/call",
2328
+ params: msg.params
2329
+ } } }));
2330
+ sendResponse(msg.id, runResult.result || {});
2331
+ } catch (err) {
2332
+ console.error("[MCPAppsRenderer] tools/call error:", err);
2333
+ sendErrorResponse(msg.id, -32603, String(err));
2334
+ }
2335
+ break;
2336
+ }
2337
+ default: sendErrorResponse(msg.id, -32601, `Method not found: ${msg.method}`);
2338
+ }
2339
+ if (isNotification(msg)) switch (msg.method) {
2340
+ case "ui/notifications/initialized":
2341
+ console.log("[MCPAppsRenderer] Inner iframe initialized");
2342
+ if (mounted) setIframeReady(true);
2343
+ break;
2344
+ case "ui/notifications/size-changed": {
2345
+ const { width, height } = msg.params || {};
2346
+ console.log("[MCPAppsRenderer] Size change:", {
2347
+ width,
2348
+ height
2349
+ });
2350
+ if (mounted) setIframeSize({
2351
+ width: typeof width === "number" ? width : void 0,
2352
+ height: typeof height === "number" ? height : void 0
2353
+ });
2354
+ break;
2355
+ }
2356
+ case "notifications/message":
2357
+ console.log("[MCPAppsRenderer] App log:", msg.params);
2358
+ break;
2359
+ }
2360
+ };
2361
+ window.addEventListener("message", messageHandler);
2362
+ let html;
2363
+ if (fetchedResource.text) html = fetchedResource.text;
2364
+ else if (fetchedResource.blob) html = atob(fetchedResource.blob);
2365
+ else throw new Error("Resource has no text or blob content");
2366
+ sendNotification("ui/notifications/sandbox-resource-ready", { html });
2367
+ } catch (err) {
2368
+ console.error("[MCPAppsRenderer] Setup error:", err);
2369
+ if (mounted) setError(err instanceof Error ? err : new Error(String(err)));
2370
+ }
2371
+ };
2372
+ setup();
2373
+ return () => {
2374
+ mounted = false;
2375
+ if (initialListener) {
2376
+ window.removeEventListener("message", initialListener);
2377
+ initialListener = null;
2378
+ }
2379
+ if (messageHandler) window.removeEventListener("message", messageHandler);
2380
+ if (createdIframe) {
2381
+ createdIframe.remove();
2382
+ createdIframe = null;
2383
+ }
2384
+ iframeRef.current = null;
2385
+ };
2386
+ }, [
2387
+ isLoading,
2388
+ fetchedResource,
2389
+ sendNotification,
2390
+ sendResponse,
2391
+ sendErrorResponse
2392
+ ]);
2393
+ useEffect(() => {
2394
+ if (iframeRef.current) {
2395
+ if (iframeSize.width !== void 0) {
2396
+ iframeRef.current.style.minWidth = `min(${iframeSize.width}px, 100%)`;
2397
+ iframeRef.current.style.width = "100%";
2398
+ }
2399
+ if (iframeSize.height !== void 0) iframeRef.current.style.height = `${iframeSize.height}px`;
2400
+ }
2401
+ }, [iframeSize]);
2402
+ useEffect(() => {
2403
+ if (iframeReady && content.toolInput) {
2404
+ console.log("[MCPAppsRenderer] Sending tool input:", content.toolInput);
2405
+ sendNotification("ui/notifications/tool-input", { arguments: content.toolInput });
2406
+ }
2407
+ }, [
2408
+ iframeReady,
2409
+ content.toolInput,
2410
+ sendNotification
2411
+ ]);
2412
+ useEffect(() => {
2413
+ if (iframeReady && content.result) {
2414
+ console.log("[MCPAppsRenderer] Sending tool result:", content.result);
2415
+ sendNotification("ui/notifications/tool-result", content.result);
2416
+ }
2417
+ }, [
2418
+ iframeReady,
2419
+ content.result,
2420
+ sendNotification
2421
+ ]);
2422
+ const borderStyle = fetchedResource?._meta?.ui?.prefersBorder === true ? {
2423
+ borderRadius: "8px",
2424
+ backgroundColor: "#f9f9f9",
2425
+ border: "1px solid #e0e0e0"
2426
+ } : {};
2427
+ return /* @__PURE__ */ jsxs("div", {
2428
+ ref: containerRef,
2429
+ style: {
2430
+ width: "100%",
2431
+ height: iframeSize.height ? `${iframeSize.height}px` : "auto",
2432
+ minHeight: "100px",
2433
+ overflow: "hidden",
2434
+ position: "relative",
2435
+ ...borderStyle
2436
+ },
2437
+ children: [isLoading && /* @__PURE__ */ jsx("div", {
2438
+ style: {
2439
+ padding: "1rem",
2440
+ color: "#666"
2441
+ },
2442
+ children: "Loading..."
2443
+ }), error && /* @__PURE__ */ jsxs("div", {
2444
+ style: {
2445
+ color: "red",
2446
+ padding: "1rem"
2447
+ },
2448
+ children: ["Error: ", error.message]
2449
+ })]
2450
+ });
2451
+ };
2452
+
2453
+ //#endregion
2454
+ //#region src/v2/providers/SandboxFunctionsContext.ts
2455
+ const SandboxFunctionsContext = createContext([]);
2456
+ function useSandboxFunctions() {
2457
+ return useContext(SandboxFunctionsContext);
2458
+ }
2459
+
2460
+ //#endregion
2461
+ //#region src/v2/lib/processPartialHtml.ts
2462
+ /**
2463
+ * Extracts all complete `<style>` blocks from the raw HTML.
2464
+ * Returns the concatenated style tags, suitable for injection into `<head>`.
2465
+ */
2466
+ function extractCompleteStyles(html) {
2467
+ const matches = html.match(/<style\b[^>]*>[\s\S]*?<\/style>/gi);
2468
+ return matches ? matches.join("") : "";
2469
+ }
2470
+ /**
2471
+ * Processes raw accumulated HTML for safe preview via innerHTML injection.
2472
+ * Pure function, no DOM dependencies.
2473
+ *
2474
+ * Pipeline (order matters):
2475
+ * 1. Strip incomplete tag at end
2476
+ * 2. Strip complete <style>, <script>, and <head> blocks
2477
+ * 3. Strip incomplete <style>/<script>/<head> blocks
2478
+ * 4. Strip incomplete HTML entities
2479
+ * 5. Extract body content (or use full string if no <body>)
2480
+ */
2481
+ function processPartialHtml(html) {
2482
+ let result = html;
2483
+ result = result.replace(/<[^>]*$/, "");
2484
+ result = result.replace(/<(style|script|head)\b[^>]*>[\s\S]*?<\/\1>/gi, "");
2485
+ result = result.replace(/<(style|script|head)\b[^>]*>[\s\S]*$/gi, "");
2486
+ result = result.replace(/&[a-zA-Z0-9#]*$/, "");
2487
+ const bodyMatch = result.match(/<body[^>]*>([\s\S]*)/i);
2488
+ if (bodyMatch) {
2489
+ result = bodyMatch[1];
2490
+ result = result.replace(/<\/body>[\s\S]*/i, "");
2491
+ }
2492
+ return result;
2493
+ }
2494
+
2495
+ //#endregion
2496
+ //#region src/v2/components/OpenGenerativeUIRenderer.tsx
2497
+ const OpenGenerativeUIActivityType = "open-generative-ui";
2498
+ const OpenGenerativeUIContentSchema = z.object({
2499
+ initialHeight: z.number().optional(),
2500
+ generating: z.boolean().optional(),
2501
+ css: z.string().optional(),
2502
+ cssComplete: z.boolean().optional(),
2503
+ html: z.array(z.string()).optional(),
2504
+ htmlComplete: z.boolean().optional(),
2505
+ jsFunctions: z.string().optional(),
2506
+ jsFunctionsComplete: z.boolean().optional(),
2507
+ jsExpressions: z.array(z.string()).optional(),
2508
+ jsExpressionsComplete: z.boolean().optional()
2509
+ });
2510
+ /**
2511
+ * Schema for the generateSandboxedUi tool call arguments.
2512
+ * Used by the frontend tool renderer to display placeholder messages.
2513
+ */
2514
+ const GenerateSandboxedUiArgsSchema = z.object({
2515
+ initialHeight: z.number().optional(),
2516
+ placeholderMessages: z.array(z.string()).optional(),
2517
+ css: z.string().optional(),
2518
+ html: z.string().optional(),
2519
+ jsFunctions: z.string().optional(),
2520
+ jsExpressions: z.array(z.string()).optional()
2521
+ });
2522
+ const THROTTLE_MS = 1e3;
2523
+ /**
2524
+ * Returns true when the inner component should re-render immediately
2525
+ * (no throttle delay).
2526
+ */
2527
+ function shouldFlushImmediately(prev, next) {
2528
+ if (next.cssComplete && (!prev || !prev.cssComplete)) return true;
2529
+ if (next.htmlComplete) return true;
2530
+ if (next.generating === false) return true;
2531
+ if (next.jsFunctions && (!prev || !prev.jsFunctions)) return true;
2532
+ if ((next.jsExpressions?.length ?? 0) > (prev?.jsExpressions?.length ?? 0)) return true;
2533
+ if (next.html?.length && (!prev || !prev.html?.length)) return true;
2534
+ return false;
2535
+ }
2536
+ /**
2537
+ * Outer wrapper — absorbs every parent re-render but only forwards
2538
+ * throttled content snapshots to the memoized inner component.
2539
+ */
2540
+ const OpenGenerativeUIActivityRenderer = function OpenGenerativeUIActivityRenderer({ content }) {
2541
+ const latestContentRef = useRef(content);
2542
+ latestContentRef.current = content;
2543
+ const [throttledContent, setThrottledContent] = useState(content);
2544
+ const throttledContentRef = useRef(throttledContent);
2545
+ const timerRef = useRef(null);
2546
+ if (throttledContentRef.current !== content) {
2547
+ if (shouldFlushImmediately(throttledContentRef.current, content)) {
2548
+ if (timerRef.current !== null) {
2549
+ clearTimeout(timerRef.current);
2550
+ timerRef.current = null;
2551
+ }
2552
+ throttledContentRef.current = content;
2553
+ setThrottledContent(content);
2554
+ }
2555
+ }
2556
+ const flush = useCallback(() => {
2557
+ timerRef.current = null;
2558
+ const latest = latestContentRef.current;
2559
+ throttledContentRef.current = latest;
2560
+ setThrottledContent(latest);
2561
+ }, []);
2562
+ useEffect(() => {
2563
+ if (throttledContentRef.current === content) return;
2564
+ if (timerRef.current === null) timerRef.current = setTimeout(flush, THROTTLE_MS);
2565
+ }, [content, flush]);
2566
+ useEffect(() => {
2567
+ return () => {
2568
+ if (timerRef.current !== null) clearTimeout(timerRef.current);
2569
+ };
2570
+ }, []);
2571
+ return /* @__PURE__ */ jsx(OpenGenerativeUIActivityRendererInner, { content: throttledContent });
2572
+ };
2573
+ function ensureHead(html) {
2574
+ if (/<head[\s>]/i.test(html)) return html;
2575
+ return `<head></head>${html}`;
2576
+ }
2577
+ function injectCssIntoHtml(html, css) {
2578
+ const headCloseIdx = html.indexOf("</head>");
2579
+ if (headCloseIdx !== -1) return html.slice(0, headCloseIdx) + `<style>${css}</style>` + html.slice(headCloseIdx);
2580
+ return `<head><style>${css}</style></head>${html}`;
2581
+ }
2582
+ const OpenGenerativeUIActivityRendererInner = React.memo(function OpenGenerativeUIActivityRendererInner({ content }) {
2583
+ const initialHeight = content.initialHeight ?? 200;
2584
+ const [autoHeight, setAutoHeight] = useState(null);
2585
+ const sandboxFunctions = useSandboxFunctions();
2586
+ const localApi = useMemo(() => {
2587
+ const api = {};
2588
+ for (const fn of sandboxFunctions) api[fn.name] = fn.handler;
2589
+ return api;
2590
+ }, [sandboxFunctions]);
2591
+ const fullHtml = content.htmlComplete && content.html?.length ? content.html.join("") : void 0;
2592
+ const css = content.cssComplete ? content.css : void 0;
2593
+ const cssReady = !!content.cssComplete;
2594
+ const partialHtml = !content.htmlComplete && content.html?.length ? content.html.join("") : void 0;
2595
+ const previewBody = partialHtml ? processPartialHtml(partialHtml) : void 0;
2596
+ const previewStyles = partialHtml ? extractCompleteStyles(partialHtml) : "";
2597
+ const hasPreview = cssReady && !!previewBody?.trim();
2598
+ const hasVisibleSandbox = !!fullHtml || hasPreview;
2599
+ const containerRef = useRef(null);
2600
+ const sandboxRef = useRef(null);
2601
+ const previewSandboxRef = useRef(null);
2602
+ const previewReadyRef = useRef(false);
2603
+ const sandboxReadyRef = useRef(false);
2604
+ const executedIndexRef = useRef(0);
2605
+ const pendingQueueRef = useRef([]);
2606
+ const jsFunctionsInjectedRef = useRef(false);
2607
+ useEffect(() => {
2608
+ const container = containerRef.current;
2609
+ if (!container || fullHtml || !hasPreview || previewSandboxRef.current) return;
2610
+ let cancelled = false;
2611
+ import("@jetbrains/websandbox").then((mod) => {
2612
+ if (cancelled) return;
2613
+ const sandbox = (mod.default?.default ?? mod.default).create({}, {
2614
+ frameContainer: container,
2615
+ frameContent: "<head></head><body></body>",
2616
+ allowAdditionalAttributes: ""
2617
+ });
2618
+ previewSandboxRef.current = sandbox;
2619
+ sandbox.iframe.style.width = "100%";
2620
+ sandbox.iframe.style.height = "100%";
2621
+ sandbox.iframe.style.border = "none";
2622
+ sandbox.iframe.style.backgroundColor = "transparent";
2623
+ sandbox.promise.then(() => {
2624
+ if (cancelled) return;
2625
+ previewReadyRef.current = true;
2626
+ sandbox.run(`
2627
+ var s = document.createElement('style');
2628
+ s.textContent = 'html, body { overflow: hidden !important; }';
2629
+ document.head.appendChild(s);
2630
+ `);
2631
+ const headParts = [];
2632
+ if (css) headParts.push(`<style>${css}</style>`);
2633
+ if (previewStyles) headParts.push(previewStyles);
2634
+ if (headParts.length) sandbox.run(`document.head.innerHTML = ${JSON.stringify(headParts.join(""))}`);
2635
+ if (previewBody) sandbox.run(`document.body.innerHTML = ${JSON.stringify(previewBody)}`);
2636
+ });
2637
+ }).catch((err) => {
2638
+ console.error("[OpenGenerativeUI] Failed to load sandbox module:", err);
2639
+ });
2640
+ return () => {
2641
+ cancelled = true;
2642
+ };
2643
+ }, [hasPreview, fullHtml]);
2644
+ useEffect(() => {
2645
+ if (!previewSandboxRef.current || !previewReadyRef.current) return;
2646
+ const headParts = [];
2647
+ if (css) headParts.push(`<style>${css}</style>`);
2648
+ if (previewStyles) headParts.push(previewStyles);
2649
+ if (headParts.length) previewSandboxRef.current.run(`document.head.innerHTML = ${JSON.stringify(headParts.join(""))}`);
2650
+ if (!previewBody) return;
2651
+ previewSandboxRef.current.run(`document.body.innerHTML = ${JSON.stringify(previewBody)}`);
2652
+ }, [
2653
+ previewBody,
2654
+ previewStyles,
2655
+ css
2656
+ ]);
2657
+ useEffect(() => {
2658
+ const container = containerRef.current;
2659
+ if (!container || !fullHtml) return;
2660
+ if (previewSandboxRef.current) {
2661
+ previewSandboxRef.current.destroy();
2662
+ previewSandboxRef.current = null;
2663
+ previewReadyRef.current = false;
2664
+ }
2665
+ let cancelled = false;
2666
+ executedIndexRef.current = 0;
2667
+ jsFunctionsInjectedRef.current = false;
2668
+ sandboxReadyRef.current = false;
2669
+ pendingQueueRef.current = [];
2670
+ const htmlContent = css ? injectCssIntoHtml(fullHtml, css) : fullHtml;
2671
+ import("@jetbrains/websandbox").then((mod) => {
2672
+ if (cancelled) return;
2673
+ const sandbox = (mod.default?.default ?? mod.default).create(localApi, {
2674
+ frameContainer: container,
2675
+ frameContent: ensureHead(htmlContent),
2676
+ allowAdditionalAttributes: ""
2677
+ });
2678
+ sandboxRef.current = sandbox;
2679
+ sandbox.iframe.style.width = "100%";
2680
+ sandbox.iframe.style.height = "100%";
2681
+ sandbox.iframe.style.border = "none";
2682
+ sandbox.iframe.style.backgroundColor = "transparent";
2683
+ sandbox.promise.then(() => {
2684
+ if (cancelled) return;
2685
+ sandboxReadyRef.current = true;
2686
+ sandbox.run(`
2687
+ var s = document.createElement('style');
2688
+ s.textContent = 'html, body { overflow: hidden !important; }';
2689
+ document.head.appendChild(s);
2690
+ `);
2691
+ const queue = pendingQueueRef.current;
2692
+ pendingQueueRef.current = [];
2693
+ for (const code of queue) sandbox.run(code);
2694
+ });
2695
+ }).catch((err) => {
2696
+ console.error("[OpenGenerativeUI] Failed to load sandbox module:", err);
2697
+ });
2698
+ return () => {
2699
+ cancelled = true;
2700
+ if (previewSandboxRef.current) {
2701
+ previewSandboxRef.current.destroy();
2702
+ previewSandboxRef.current = null;
2703
+ previewReadyRef.current = false;
2704
+ }
2705
+ if (sandboxRef.current) {
2706
+ sandboxRef.current.destroy();
2707
+ sandboxRef.current = null;
2708
+ }
2709
+ sandboxReadyRef.current = false;
2710
+ setAutoHeight(null);
2711
+ };
2712
+ }, [
2713
+ fullHtml,
2714
+ css,
2715
+ localApi
2716
+ ]);
2717
+ useEffect(() => {
2718
+ if (!content.jsFunctions || jsFunctionsInjectedRef.current) return;
2719
+ jsFunctionsInjectedRef.current = true;
2720
+ const sandbox = sandboxRef.current;
2721
+ if (sandboxReadyRef.current && sandbox) sandbox.run(content.jsFunctions);
2722
+ else pendingQueueRef.current.push(content.jsFunctions);
2723
+ }, [content.jsFunctions]);
2724
+ useEffect(() => {
2725
+ const expressions = content.jsExpressions;
2726
+ if (!expressions || expressions.length === 0) return;
2727
+ const startIndex = executedIndexRef.current;
2728
+ if (startIndex >= expressions.length) return;
2729
+ const newExprs = expressions.slice(startIndex);
2730
+ executedIndexRef.current = expressions.length;
2731
+ const sandbox = sandboxRef.current;
2732
+ if (sandboxReadyRef.current && sandbox) (async () => {
2733
+ for (const expr of newExprs) await sandbox.run(expr);
2734
+ })();
2735
+ else pendingQueueRef.current.push(...newExprs);
2736
+ }, [content.jsExpressions?.length]);
2737
+ const generationDone = content.generating === false;
2738
+ useEffect(() => {
2739
+ const sandbox = sandboxRef.current;
2740
+ if (!generationDone || !sandbox) return;
2741
+ let handled = false;
2742
+ const onMessage = (e) => {
2743
+ if (handled) return;
2744
+ if (e.source === sandbox.iframe.contentWindow && e.data?.type === "__ck_resize") {
2745
+ handled = true;
2746
+ setAutoHeight(e.data.height);
2747
+ window.removeEventListener("message", onMessage);
2748
+ }
2749
+ };
2750
+ window.addEventListener("message", onMessage);
2751
+ const measureOnce = `
2752
+ (function() {
2753
+ var s = document.createElement('style');
2754
+ s.textContent = 'body { height: auto !important; min-height: 0 !important; }';
2755
+ document.head.appendChild(s);
2756
+ var h = document.body.scrollHeight;
2757
+ var cs = getComputedStyle(document.body);
2758
+ h += parseFloat(cs.marginTop) || 0;
2759
+ h += parseFloat(cs.marginBottom) || 0;
2760
+ s.remove();
2761
+ parent.postMessage({ type: "__ck_resize", height: Math.ceil(h) }, "*");
2762
+ })();
2763
+ `;
2764
+ if (sandboxReadyRef.current) sandbox.run(measureOnce);
2765
+ else pendingQueueRef.current.push(measureOnce);
2766
+ return () => {
2767
+ window.removeEventListener("message", onMessage);
2768
+ };
2769
+ }, [generationDone]);
2770
+ const height = autoHeight ?? initialHeight;
2771
+ const isGenerating = content.generating !== false;
2772
+ return /* @__PURE__ */ jsx("div", {
2773
+ ref: containerRef,
2774
+ style: {
2775
+ position: "relative",
2776
+ width: "100%",
2777
+ height: `${height}px`,
2778
+ borderRadius: "8px",
2779
+ backgroundColor: hasVisibleSandbox ? "transparent" : "#f5f5f5",
2780
+ border: hasVisibleSandbox ? "none" : "1px solid #e0e0e0",
2781
+ display: hasVisibleSandbox ? "block" : "flex",
2782
+ alignItems: hasVisibleSandbox ? void 0 : "center",
2783
+ justifyContent: hasVisibleSandbox ? void 0 : "center",
2784
+ overflow: "hidden"
2785
+ },
2786
+ children: isGenerating && /* @__PURE__ */ jsxs("div", {
2787
+ style: {
2788
+ position: "absolute",
2789
+ inset: 0,
2790
+ zIndex: 10,
2791
+ pointerEvents: "all",
2792
+ backgroundColor: "rgba(255, 255, 255, 0.5)",
2793
+ display: "flex",
2794
+ alignItems: "center",
2795
+ justifyContent: "center"
2796
+ },
2797
+ children: [/* @__PURE__ */ jsxs("svg", {
2798
+ width: "48",
2799
+ height: "48",
2800
+ viewBox: "0 0 24 24",
2801
+ fill: "none",
2802
+ style: { animation: "ck-spin 1s linear infinite" },
2803
+ children: [/* @__PURE__ */ jsx("circle", {
2804
+ cx: "12",
2805
+ cy: "12",
2806
+ r: "10",
2807
+ stroke: "#e0e0e0",
2808
+ strokeWidth: "3"
2809
+ }), /* @__PURE__ */ jsx("path", {
2810
+ d: "M12 2a10 10 0 0 1 10 10",
2811
+ stroke: "#999",
2812
+ strokeWidth: "3",
2813
+ strokeLinecap: "round"
2814
+ })]
2815
+ }), /* @__PURE__ */ jsx("style", { children: `@keyframes ck-spin { to { transform: rotate(360deg) } }` })]
2816
+ })
2817
+ });
2818
+ }, (prev, next) => prev.content === next.content);
2819
+ /**
2820
+ * Frontend tool renderer for generateSandboxedUi.
2821
+ * Displays placeholder messages while the UI is being generated.
2822
+ */
2823
+ const OpenGenerativeUIToolRenderer = function OpenGenerativeUIToolRenderer(props) {
2824
+ const [visibleMessageIndex, setVisibleMessageIndex] = useState(0);
2825
+ const prevMessageCountRef = useRef(0);
2826
+ const messages = props.args.placeholderMessages;
2827
+ useEffect(() => {
2828
+ if (!messages || messages.length === 0) return;
2829
+ if (messages.length !== prevMessageCountRef.current) {
2830
+ prevMessageCountRef.current = messages.length;
2831
+ setVisibleMessageIndex(messages.length - 1);
2832
+ }
2833
+ if (props.status === ToolCallStatus.Complete) return;
2834
+ const timer = setInterval(() => {
2835
+ setVisibleMessageIndex((i) => (i + 1) % messages.length);
2836
+ }, 5e3);
2837
+ return () => clearInterval(timer);
2838
+ }, [messages?.length, props.status]);
2839
+ if (props.status === ToolCallStatus.Complete) return null;
2840
+ if (!messages || messages.length === 0) return null;
2841
+ return /* @__PURE__ */ jsx("div", {
2842
+ style: {
2843
+ padding: "8px 12px",
2844
+ color: "#999",
2845
+ fontSize: "14px"
2846
+ },
2847
+ children: messages[visibleMessageIndex] ?? messages[0]
2848
+ });
2849
+ };
2850
+
2851
+ //#endregion
2852
+ //#region src/v2/a2ui/A2UIMessageRenderer.tsx
2853
+ /**
2854
+ * The container key used to wrap A2UI operations for explicit detection.
2855
+ * Must match A2UI_OPERATIONS_KEY in @ag-ui/a2ui-middleware and copilotkit.a2ui (Python).
2856
+ */
2857
+ const A2UI_OPERATIONS_KEY = "a2ui_operations";
2858
+ let initialized = false;
2859
+ function ensureInitialized() {
2860
+ if (!initialized) {
2861
+ initializeDefaultCatalog();
2862
+ injectStyles();
2863
+ initialized = true;
2864
+ }
2865
+ }
2866
+ function createA2UIMessageRenderer(options) {
2867
+ const { theme, catalog, loadingComponent } = options;
2868
+ return {
2869
+ activityType: "a2ui-surface",
2870
+ content: z.any(),
2871
+ render: ({ content, agent }) => {
2872
+ ensureInitialized();
2873
+ const [operations, setOperations] = useState([]);
2874
+ const { copilotkit } = useCopilotKit();
2580
2875
  const lastContentRef = useRef(null);
2581
2876
  useEffect(() => {
2582
2877
  if (content === lastContentRef.current) return;
@@ -2728,18 +3023,6 @@ function getOperationSurfaceId(operation) {
2728
3023
  return operation?.createSurface?.surfaceId ?? operation?.updateComponents?.surfaceId ?? operation?.updateDataModel?.surfaceId ?? operation?.deleteSurface?.surfaceId ?? null;
2729
3024
  }
2730
3025
 
2731
- //#endregion
2732
- //#region src/v2/types/defineToolCallRenderer.ts
2733
- function defineToolCallRenderer(def) {
2734
- const argsSchema = def.name === "*" && !def.args ? z.any() : def.args;
2735
- return {
2736
- name: def.name,
2737
- args: argsSchema,
2738
- render: def.render,
2739
- ...def.agentId ? { agentId: def.agentId } : {}
2740
- };
2741
- }
2742
-
2743
3026
  //#endregion
2744
3027
  //#region src/v2/a2ui/A2UIToolCallRenderer.tsx
2745
3028
  /**
@@ -3439,215 +3722,53 @@ const CopilotKitProvider = ({ children, runtimeUrl, headers: headersProp = {}, c
3439
3722
  name: fn.name,
3440
3723
  description: fn.description,
3441
3724
  parameters: schemaToJsonSchema(fn.parameters, { zodToJsonSchema })
3442
- })));
3443
- }, [sandboxFunctionsList]);
3444
- useLayoutEffect(() => {
3445
- if (!copilotkit || !sandboxFunctionsDescriptors || !openGenUIActive) return;
3446
- const id = copilotkit.addContext({
3447
- description: "Sandbox functions available in generated sandboxed UI code. Call via: await Websandbox.connection.remote.<functionName>(args)",
3448
- value: sandboxFunctionsDescriptors
3449
- });
3450
- return () => {
3451
- copilotkit.removeContext(id);
3452
- };
3453
- }, [
3454
- copilotkit,
3455
- sandboxFunctionsDescriptors,
3456
- openGenUIActive
3457
- ]);
3458
- const contextValue = useMemo(() => ({
3459
- copilotkit,
3460
- executingToolCallIds
3461
- }), [copilotkit, executingToolCallIds]);
3462
- const licenseContextValue = useMemo(() => createLicenseContextValue(null), []);
3463
- return /* @__PURE__ */ jsx(SandboxFunctionsContext.Provider, {
3464
- value: sandboxFunctionsList,
3465
- children: /* @__PURE__ */ jsx(CopilotKitContext.Provider, {
3466
- value: contextValue,
3467
- children: /* @__PURE__ */ jsxs(LicenseContext.Provider, {
3468
- value: licenseContextValue,
3469
- children: [
3470
- runtimeA2UIEnabled && /* @__PURE__ */ jsx(A2UIBuiltInToolCallRenderer, {}),
3471
- runtimeA2UIEnabled && /* @__PURE__ */ jsx(A2UICatalogContext, {
3472
- catalog: a2ui?.catalog,
3473
- includeSchema: a2ui?.includeSchema
3474
- }),
3475
- children,
3476
- shouldRenderInspector ? /* @__PURE__ */ jsx(CopilotKitInspector, {
3477
- core: copilotkit,
3478
- defaultAnchor: inspectorDefaultAnchor
3479
- }) : null,
3480
- runtimeLicenseStatus === "none" && !resolvedPublicKey && /* @__PURE__ */ jsx(LicenseWarningBanner, { type: "no_license" }),
3481
- runtimeLicenseStatus === "expired" && /* @__PURE__ */ jsx(LicenseWarningBanner, { type: "expired" }),
3482
- runtimeLicenseStatus === "invalid" && /* @__PURE__ */ jsx(LicenseWarningBanner, { type: "invalid" }),
3483
- runtimeLicenseStatus === "expiring" && /* @__PURE__ */ jsx(LicenseWarningBanner, { type: "expiring" })
3484
- ]
3485
- })
3486
- })
3487
- });
3488
- };
3489
-
3490
- //#endregion
3491
- //#region src/v2/hooks/use-agent.tsx
3492
- let UseAgentUpdate = /* @__PURE__ */ function(UseAgentUpdate) {
3493
- UseAgentUpdate["OnMessagesChanged"] = "OnMessagesChanged";
3494
- UseAgentUpdate["OnStateChanged"] = "OnStateChanged";
3495
- UseAgentUpdate["OnRunStatusChanged"] = "OnRunStatusChanged";
3496
- return UseAgentUpdate;
3497
- }({});
3498
- const ALL_UPDATES = [
3499
- UseAgentUpdate.OnMessagesChanged,
3500
- UseAgentUpdate.OnStateChanged,
3501
- UseAgentUpdate.OnRunStatusChanged
3502
- ];
3503
- /**
3504
- * Clone a registry agent for per-thread isolation.
3505
- * Copies agent configuration (transport, headers, etc.) but resets conversation
3506
- * state (messages, threadId, state) so each thread starts fresh.
3507
- */
3508
- function cloneForThread(source, threadId, headers) {
3509
- const clone = source.clone();
3510
- if (clone === source) throw new Error(`useAgent: ${source.constructor.name}.clone() returned the same instance. clone() must return a new, independent object.`);
3511
- clone.threadId = threadId;
3512
- clone.setMessages([]);
3513
- clone.setState({});
3514
- if (clone instanceof HttpAgent) clone.headers = { ...headers };
3515
- return clone;
3516
- }
3517
- /**
3518
- * Module-level WeakMap: registryAgent → (threadId → clone).
3519
- * Shared across all useAgent() calls so that every component using the same
3520
- * (agentId, threadId) pair receives the same agent instance. Using WeakMap
3521
- * ensures the clone map is garbage-collected when the registry agent is
3522
- * replaced (e.g. after reconnect or hot-reload).
3523
- */
3524
- const globalThreadCloneMap = /* @__PURE__ */ new WeakMap();
3525
- /**
3526
- * Look up an existing per-thread clone without creating one.
3527
- * Returns undefined when no clone has been created yet for this pair.
3528
- */
3529
- function getThreadClone(registryAgent, threadId) {
3530
- if (!registryAgent || !threadId) return void 0;
3531
- return globalThreadCloneMap.get(registryAgent)?.get(threadId);
3532
- }
3533
- function getOrCreateThreadClone(existing, threadId, headers) {
3534
- let byThread = globalThreadCloneMap.get(existing);
3535
- if (!byThread) {
3536
- byThread = /* @__PURE__ */ new Map();
3537
- globalThreadCloneMap.set(existing, byThread);
3538
- }
3539
- const cached = byThread.get(threadId);
3540
- if (cached) return cached;
3541
- const clone = cloneForThread(existing, threadId, headers);
3542
- byThread.set(threadId, clone);
3543
- return clone;
3544
- }
3545
- function useAgent({ agentId, threadId, updates, throttleMs } = {}) {
3546
- agentId ??= DEFAULT_AGENT_ID;
3547
- const { copilotkit } = useCopilotKit();
3548
- const providerThrottleMs = copilotkit.defaultThrottleMs;
3549
- const chatConfig = useCopilotChatConfiguration();
3550
- threadId ??= chatConfig?.threadId;
3551
- const [, forceUpdate] = useReducer((x) => x + 1, 0);
3552
- const updateFlags = useMemo(() => updates ?? ALL_UPDATES, [JSON.stringify(updates)]);
3553
- const provisionalAgentCache = useRef(/* @__PURE__ */ new Map());
3554
- const agent = useMemo(() => {
3555
- const cacheKey = threadId ? `${agentId}:${threadId}` : agentId;
3556
- const existing = copilotkit.getAgent(agentId);
3557
- if (existing) {
3558
- provisionalAgentCache.current.delete(cacheKey);
3559
- provisionalAgentCache.current.delete(agentId);
3560
- if (!threadId) return existing;
3561
- return getOrCreateThreadClone(existing, threadId, copilotkit.headers);
3562
- }
3563
- const isRuntimeConfigured = copilotkit.runtimeUrl !== void 0;
3564
- const status = copilotkit.runtimeConnectionStatus;
3565
- if (isRuntimeConfigured && (status === CopilotKitCoreRuntimeConnectionStatus.Disconnected || status === CopilotKitCoreRuntimeConnectionStatus.Connecting)) {
3566
- const cached = provisionalAgentCache.current.get(cacheKey);
3567
- if (cached) {
3568
- cached.headers = { ...copilotkit.headers };
3569
- return cached;
3570
- }
3571
- const provisional = new ProxiedCopilotRuntimeAgent({
3572
- runtimeUrl: copilotkit.runtimeUrl,
3573
- agentId,
3574
- transport: copilotkit.runtimeTransport,
3575
- runtimeMode: "pending"
3576
- });
3577
- provisional.headers = { ...copilotkit.headers };
3578
- if (threadId) provisional.threadId = threadId;
3579
- provisionalAgentCache.current.set(cacheKey, provisional);
3580
- return provisional;
3581
- }
3582
- if (isRuntimeConfigured && status === CopilotKitCoreRuntimeConnectionStatus.Error) {
3583
- const cached = provisionalAgentCache.current.get(cacheKey);
3584
- if (cached) {
3585
- cached.headers = { ...copilotkit.headers };
3586
- return cached;
3587
- }
3588
- const provisional = new ProxiedCopilotRuntimeAgent({
3589
- runtimeUrl: copilotkit.runtimeUrl,
3590
- agentId,
3591
- transport: copilotkit.runtimeTransport,
3592
- runtimeMode: "pending"
3593
- });
3594
- provisional.headers = { ...copilotkit.headers };
3595
- if (threadId) provisional.threadId = threadId;
3596
- provisionalAgentCache.current.set(cacheKey, provisional);
3597
- return provisional;
3598
- }
3599
- const knownAgents = Object.keys(copilotkit.agents ?? {});
3600
- const runtimePart = isRuntimeConfigured ? `runtimeUrl=${copilotkit.runtimeUrl}` : "no runtimeUrl";
3601
- throw new Error(`useAgent: Agent '${agentId}' not found after runtime sync (${runtimePart}). ` + (knownAgents.length ? `Known agents: [${knownAgents.join(", ")}]` : "No agents registered.") + " Verify your runtime /info and/or agents__unsafe_dev_only.");
3602
- }, [
3603
- agentId,
3604
- threadId,
3605
- copilotkit.agents,
3606
- copilotkit.runtimeConnectionStatus,
3607
- copilotkit.runtimeUrl,
3608
- copilotkit.runtimeTransport,
3609
- JSON.stringify(copilotkit.headers)
3610
- ]);
3611
- useEffect(() => {
3612
- if (updateFlags.length === 0) return;
3613
- let active = true;
3614
- const handlers = {};
3615
- let batchScheduled = false;
3616
- const batchedForceUpdate = () => {
3617
- if (!active) return;
3618
- if (!batchScheduled) {
3619
- batchScheduled = true;
3620
- queueMicrotask(() => {
3621
- batchScheduled = false;
3622
- if (active) forceUpdate();
3623
- });
3624
- }
3625
- };
3626
- if (updateFlags.includes(UseAgentUpdate.OnMessagesChanged)) handlers.onMessagesChanged = batchedForceUpdate;
3627
- if (updateFlags.includes(UseAgentUpdate.OnStateChanged)) handlers.onStateChanged = batchedForceUpdate;
3628
- if (updateFlags.includes(UseAgentUpdate.OnRunStatusChanged)) {
3629
- handlers.onRunInitialized = batchedForceUpdate;
3630
- handlers.onRunFinalized = batchedForceUpdate;
3631
- handlers.onRunFailed = batchedForceUpdate;
3632
- handlers.onRunErrorEvent = batchedForceUpdate;
3633
- }
3634
- const subscription = copilotkit.subscribeToAgentWithOptions(agent, handlers, { throttleMs });
3725
+ })));
3726
+ }, [sandboxFunctionsList]);
3727
+ useLayoutEffect(() => {
3728
+ if (!copilotkit || !sandboxFunctionsDescriptors || !openGenUIActive) return;
3729
+ const id = copilotkit.addContext({
3730
+ description: "Sandbox functions available in generated sandboxed UI code. Call via: await Websandbox.connection.remote.<functionName>(args)",
3731
+ value: sandboxFunctionsDescriptors
3732
+ });
3635
3733
  return () => {
3636
- active = false;
3637
- subscription.unsubscribe();
3734
+ copilotkit.removeContext(id);
3638
3735
  };
3639
3736
  }, [
3640
- agent,
3641
- forceUpdate,
3642
- throttleMs,
3643
- providerThrottleMs,
3644
- updateFlags
3737
+ copilotkit,
3738
+ sandboxFunctionsDescriptors,
3739
+ openGenUIActive
3645
3740
  ]);
3646
- useEffect(() => {
3647
- if (agent instanceof HttpAgent) agent.headers = { ...copilotkit.headers };
3648
- }, [agent, JSON.stringify(copilotkit.headers)]);
3649
- return { agent };
3650
- }
3741
+ const contextValue = useMemo(() => ({
3742
+ copilotkit,
3743
+ executingToolCallIds
3744
+ }), [copilotkit, executingToolCallIds]);
3745
+ const licenseContextValue = useMemo(() => createLicenseContextValue(null), []);
3746
+ return /* @__PURE__ */ jsx(SandboxFunctionsContext.Provider, {
3747
+ value: sandboxFunctionsList,
3748
+ children: /* @__PURE__ */ jsx(CopilotKitContext.Provider, {
3749
+ value: contextValue,
3750
+ children: /* @__PURE__ */ jsxs(LicenseContext.Provider, {
3751
+ value: licenseContextValue,
3752
+ children: [
3753
+ runtimeA2UIEnabled && /* @__PURE__ */ jsx(A2UIBuiltInToolCallRenderer, {}),
3754
+ runtimeA2UIEnabled && /* @__PURE__ */ jsx(A2UICatalogContext, {
3755
+ catalog: a2ui?.catalog,
3756
+ includeSchema: a2ui?.includeSchema
3757
+ }),
3758
+ children,
3759
+ shouldRenderInspector ? /* @__PURE__ */ jsx(CopilotKitInspector, {
3760
+ core: copilotkit,
3761
+ defaultAnchor: inspectorDefaultAnchor
3762
+ }) : null,
3763
+ runtimeLicenseStatus === "none" && !resolvedPublicKey && /* @__PURE__ */ jsx(LicenseWarningBanner, { type: "no_license" }),
3764
+ runtimeLicenseStatus === "expired" && /* @__PURE__ */ jsx(LicenseWarningBanner, { type: "expired" }),
3765
+ runtimeLicenseStatus === "invalid" && /* @__PURE__ */ jsx(LicenseWarningBanner, { type: "invalid" }),
3766
+ runtimeLicenseStatus === "expiring" && /* @__PURE__ */ jsx(LicenseWarningBanner, { type: "expiring" })
3767
+ ]
3768
+ })
3769
+ })
3770
+ });
3771
+ };
3651
3772
 
3652
3773
  //#endregion
3653
3774
  //#region src/v2/hooks/use-render-custom-messages.tsx
@@ -3666,8 +3787,7 @@ function useRenderCustomMessages() {
3666
3787
  const { message, position } = params;
3667
3788
  const resolvedRunId = copilotkit.getRunIdForMessage(agentId, threadId, message.id) ?? copilotkit.getRunIdsForThread(agentId, threadId).slice(-1)[0];
3668
3789
  const runId = resolvedRunId ?? `missing-run-id:${message.id}`;
3669
- const registryAgent = copilotkit.getAgent(agentId);
3670
- const agent = getThreadClone(registryAgent, threadId) ?? registryAgent;
3790
+ const agent = copilotkit.getAgent(agentId);
3671
3791
  if (!agent) return null;
3672
3792
  const messagesIdsInRun = resolvedRunId ? agent.messages.filter((msg) => copilotkit.getRunIdForMessage(agentId, threadId, msg.id) === resolvedRunId).map((msg) => msg.id) : [message.id];
3673
3793
  const rawMessageIndex = agent.messages.findIndex((msg) => msg.id === message.id);
@@ -3676,426 +3796,161 @@ function useRenderCustomMessages() {
3676
3796
  const numberOfMessagesInRun = resolvedRunId ? messagesIdsInRun.length : 1;
3677
3797
  const stateSnapshot = resolvedRunId ? copilotkit.getStateByRun(agentId, threadId, resolvedRunId) : void 0;
3678
3798
  let result = null;
3679
- for (const renderer of customMessageRenderers) {
3680
- if (!renderer.render) continue;
3681
- const Component = renderer.render;
3682
- result = /* @__PURE__ */ jsx(Component, {
3683
- message,
3684
- position,
3685
- runId,
3686
- messageIndex,
3687
- messageIndexInRun,
3688
- numberOfMessagesInRun,
3689
- agentId,
3690
- stateSnapshot
3691
- }, `${runId}-${message.id}-${position}`);
3692
- if (result) break;
3693
- }
3694
- return result;
3695
- };
3696
- }
3697
-
3698
- //#endregion
3699
- //#region src/v2/hooks/use-render-activity-message.tsx
3700
- function useRenderActivityMessage() {
3701
- const { copilotkit } = useCopilotKit();
3702
- const config = useCopilotChatConfiguration();
3703
- const agentId = config?.agentId ?? DEFAULT_AGENT_ID;
3704
- const renderers = copilotkit.renderActivityMessages;
3705
- const findRenderer = useCallback((activityType) => {
3706
- if (!renderers.length) return null;
3707
- const matches = renderers.filter((renderer) => renderer.activityType === activityType);
3708
- return matches.find((candidate) => candidate.agentId === agentId) ?? matches.find((candidate) => candidate.agentId === void 0) ?? renderers.find((candidate) => candidate.activityType === "*") ?? null;
3709
- }, [agentId, renderers]);
3710
- const renderActivityMessage = useCallback((message) => {
3711
- const renderer = findRenderer(message.activityType);
3712
- if (!renderer) return null;
3713
- const parseResult = renderer.content.safeParse(message.content);
3714
- if (!parseResult.success) {
3715
- console.warn(`Failed to parse content for activity message '${message.activityType}':`, parseResult.error);
3716
- return null;
3717
- }
3718
- const Component = renderer.render;
3719
- const registryAgent = copilotkit.getAgent(agentId);
3720
- const agent = getThreadClone(registryAgent, config?.threadId) ?? registryAgent;
3721
- return /* @__PURE__ */ jsx(Component, {
3722
- activityType: message.activityType,
3723
- content: parseResult.data,
3724
- message,
3725
- agent
3726
- }, message.id);
3727
- }, [
3728
- agentId,
3729
- config?.threadId,
3730
- copilotkit,
3731
- findRenderer
3732
- ]);
3733
- return useMemo(() => ({
3734
- renderActivityMessage,
3735
- findRenderer
3736
- }), [renderActivityMessage, findRenderer]);
3737
- }
3738
-
3739
- //#endregion
3740
- //#region src/v2/hooks/use-frontend-tool.tsx
3741
- const EMPTY_DEPS$1 = [];
3742
- function useFrontendTool(tool, deps) {
3743
- const { copilotkit } = useCopilotKit();
3744
- const extraDeps = deps ?? EMPTY_DEPS$1;
3745
- useEffect(() => {
3746
- const name = tool.name;
3747
- if (copilotkit.getTool({
3748
- toolName: name,
3749
- agentId: tool.agentId
3750
- })) {
3751
- console.warn(`Tool '${name}' already exists for agent '${tool.agentId || "global"}'. Overriding with latest registration.`);
3752
- copilotkit.removeTool(name, tool.agentId);
3753
- }
3754
- copilotkit.addTool(tool);
3755
- if (tool.render) copilotkit.addHookRenderToolCall({
3756
- name,
3757
- args: tool.parameters,
3758
- agentId: tool.agentId,
3759
- render: tool.render
3760
- });
3761
- return () => {
3762
- copilotkit.removeTool(name, tool.agentId);
3763
- };
3764
- }, [
3765
- tool.name,
3766
- tool.available,
3767
- copilotkit,
3768
- JSON.stringify(extraDeps)
3769
- ]);
3770
- }
3771
-
3772
- //#endregion
3773
- //#region src/v2/hooks/use-component.tsx
3774
- /**
3775
- * Registers a React component as a frontend tool renderer in chat.
3776
- *
3777
- * This hook is a convenience wrapper around `useFrontendTool` that:
3778
- * - builds a model-facing tool description,
3779
- * - forwards optional schema parameters (any Standard Schema V1 compatible library),
3780
- * - renders your component with tool call parameters.
3781
- *
3782
- * Use this when you want to display a typed visual component for a tool call
3783
- * without manually wiring a full frontend tool object.
3784
- *
3785
- * When `parameters` is provided, render props are inferred from the schema.
3786
- * When omitted, the render component may accept any props.
3787
- *
3788
- * @typeParam TSchema - Schema describing tool parameters, or `undefined` when no schema is given.
3789
- * @param config - Tool registration config.
3790
- * @param deps - Optional dependencies to refresh registration (same semantics as `useEffect`).
3791
- *
3792
- * @example
3793
- * ```tsx
3794
- * // Without parameters — render accepts any props
3795
- * useComponent({
3796
- * name: "showGreeting",
3797
- * render: ({ message }: { message: string }) => <div>{message}</div>,
3798
- * });
3799
- * ```
3800
- *
3801
- * @example
3802
- * ```tsx
3803
- * // With parameters — render props inferred from schema
3804
- * useComponent({
3805
- * name: "showWeatherCard",
3806
- * parameters: z.object({ city: z.string() }),
3807
- * render: ({ city }) => <div>{city}</div>,
3808
- * });
3809
- * ```
3810
- *
3811
- * @example
3812
- * ```tsx
3813
- * useComponent(
3814
- * {
3815
- * name: "renderProfile",
3816
- * parameters: z.object({ userId: z.string() }),
3817
- * render: ProfileCard,
3818
- * agentId: "support-agent",
3819
- * },
3820
- * [selectedAgentId],
3821
- * );
3822
- * ```
3823
- */
3824
- function useComponent(config, deps) {
3825
- const prefix = `Use this tool to display the "${config.name}" component in the chat. This tool renders a visual UI component for the user.`;
3826
- const fullDescription = config.description ? `${prefix}\n\n${config.description}` : prefix;
3827
- useFrontendTool({
3828
- name: config.name,
3829
- description: fullDescription,
3830
- parameters: config.parameters,
3831
- render: ({ args }) => {
3832
- const Component = config.render;
3833
- return /* @__PURE__ */ jsx(Component, { ...args });
3834
- },
3835
- agentId: config.agentId
3836
- }, deps);
3837
- }
3838
-
3839
- //#endregion
3840
- //#region src/v2/hooks/use-render-tool.tsx
3841
- const EMPTY_DEPS = [];
3842
- /**
3843
- * Registers a renderer entry in CopilotKit's `renderToolCalls` registry.
3844
- *
3845
- * Key behavior:
3846
- * - deduplicates by `agentId:name` (latest registration wins),
3847
- * - keeps renderer entries on cleanup so historical chat tool calls can still render,
3848
- * - refreshes registration when `deps` change.
3849
- *
3850
- * @typeParam S - Schema type describing tool call parameters.
3851
- * @param config - Renderer config for wildcard or named tools.
3852
- * @param deps - Optional dependencies to refresh registration.
3853
- *
3854
- * @example
3855
- * ```tsx
3856
- * useRenderTool(
3857
- * {
3858
- * name: "searchDocs",
3859
- * parameters: z.object({ query: z.string() }),
3860
- * render: ({ status, parameters, result }) => {
3861
- * if (status === "executing") return <div>Searching {parameters.query}</div>;
3862
- * if (status === "complete") return <div>{result}</div>;
3863
- * return <div>Preparing...</div>;
3864
- * },
3865
- * },
3866
- * [],
3867
- * );
3868
- * ```
3869
- *
3870
- * @example
3871
- * ```tsx
3872
- * useRenderTool(
3873
- * {
3874
- * name: "summarize",
3875
- * parameters: z.object({ text: z.string() }),
3876
- * agentId: "research-agent",
3877
- * render: ({ name, status }) => <div>{name}: {status}</div>,
3878
- * },
3879
- * [selectedAgentId],
3880
- * );
3881
- * ```
3882
- */
3883
- function useRenderTool(config, deps) {
3799
+ for (const renderer of customMessageRenderers) {
3800
+ if (!renderer.render) continue;
3801
+ const Component = renderer.render;
3802
+ result = /* @__PURE__ */ jsx(Component, {
3803
+ message,
3804
+ position,
3805
+ runId,
3806
+ messageIndex,
3807
+ messageIndexInRun,
3808
+ numberOfMessagesInRun,
3809
+ agentId,
3810
+ stateSnapshot
3811
+ }, `${runId}-${message.id}-${position}`);
3812
+ if (result) break;
3813
+ }
3814
+ return result;
3815
+ };
3816
+ }
3817
+
3818
+ //#endregion
3819
+ //#region src/v2/hooks/use-render-activity-message.tsx
3820
+ function useRenderActivityMessage() {
3884
3821
  const { copilotkit } = useCopilotKit();
3885
- const extraDeps = deps ?? EMPTY_DEPS;
3886
- useEffect(() => {
3887
- const renderer = config.name === "*" && !config.parameters ? defineToolCallRenderer({
3888
- name: "*",
3889
- render: (props) => config.render({
3890
- ...props,
3891
- parameters: props.args
3892
- }),
3893
- ...config.agentId ? { agentId: config.agentId } : {}
3894
- }) : defineToolCallRenderer({
3895
- name: config.name,
3896
- args: config.parameters,
3897
- render: (props) => config.render({
3898
- ...props,
3899
- parameters: props.args
3900
- }),
3901
- ...config.agentId ? { agentId: config.agentId } : {}
3902
- });
3903
- copilotkit.addHookRenderToolCall(renderer);
3822
+ const agentId = useCopilotChatConfiguration()?.agentId ?? DEFAULT_AGENT_ID;
3823
+ const renderers = copilotkit.renderActivityMessages;
3824
+ const findRenderer = useCallback((activityType) => {
3825
+ if (!renderers.length) return null;
3826
+ const matches = renderers.filter((renderer) => renderer.activityType === activityType);
3827
+ return matches.find((candidate) => candidate.agentId === agentId) ?? matches.find((candidate) => candidate.agentId === void 0) ?? renderers.find((candidate) => candidate.activityType === "*") ?? null;
3828
+ }, [agentId, renderers]);
3829
+ const renderActivityMessage = useCallback((message) => {
3830
+ const renderer = findRenderer(message.activityType);
3831
+ if (!renderer) return null;
3832
+ const parseResult = renderer.content.safeParse(message.content);
3833
+ if (!parseResult.success) {
3834
+ console.warn(`Failed to parse content for activity message '${message.activityType}':`, parseResult.error);
3835
+ return null;
3836
+ }
3837
+ const Component = renderer.render;
3838
+ const agent = copilotkit.getAgent(agentId);
3839
+ return /* @__PURE__ */ jsx(Component, {
3840
+ activityType: message.activityType,
3841
+ content: parseResult.data,
3842
+ message,
3843
+ agent
3844
+ }, message.id);
3904
3845
  }, [
3905
- config.name,
3846
+ agentId,
3906
3847
  copilotkit,
3907
- JSON.stringify(extraDeps)
3848
+ findRenderer
3908
3849
  ]);
3909
- }
3910
-
3911
- //#endregion
3912
- //#region src/v2/hooks/use-default-render-tool.tsx
3913
- /**
3914
- * Registers a wildcard (`"*"`) tool-call renderer via `useRenderTool`.
3915
- *
3916
- * - Call with no config to use CopilotKit's built-in default tool-call card.
3917
- * - Pass `config.render` to replace the default UI with your own fallback renderer.
3918
- *
3919
- * This is useful when you want a generic renderer for tools that do not have a
3920
- * dedicated `useRenderTool({ name: "..." })` registration.
3921
- *
3922
- * @param config - Optional custom wildcard render function.
3923
- * @param deps - Optional dependencies to refresh registration.
3924
- *
3925
- * @example
3926
- * ```tsx
3927
- * useDefaultRenderTool();
3928
- * ```
3929
- *
3930
- * @example
3931
- * ```tsx
3932
- * useDefaultRenderTool({
3933
- * render: ({ name, status }) => <div>{name}: {status}</div>,
3934
- * });
3935
- * ```
3936
- *
3937
- * @example
3938
- * ```tsx
3939
- * useDefaultRenderTool(
3940
- * {
3941
- * render: ({ name, result }) => (
3942
- * <ToolEventRow title={name} payload={result} compact={compactMode} />
3943
- * ),
3944
- * },
3945
- * [compactMode],
3946
- * );
3947
- * ```
3948
- */
3949
- function useDefaultRenderTool(config, deps) {
3950
- useRenderTool({
3951
- name: "*",
3952
- render: config?.render ?? DefaultToolCallRenderer
3953
- }, deps);
3954
- }
3955
- function DefaultToolCallRenderer({ name, parameters, status, result }) {
3956
- const [isExpanded, setIsExpanded] = useState(false);
3957
- const statusString = String(status);
3958
- const isActive = statusString === "inProgress" || statusString === "executing";
3959
- const isComplete = statusString === "complete";
3960
- return /* @__PURE__ */ jsx("div", {
3961
- style: {
3962
- marginTop: "8px",
3963
- paddingBottom: "8px"
3964
- },
3965
- children: /* @__PURE__ */ jsxs("div", {
3966
- style: {
3967
- borderRadius: "12px",
3968
- border: "1px solid #e4e4e7",
3969
- backgroundColor: "#fafafa",
3970
- padding: "14px 16px"
3971
- },
3972
- children: [/* @__PURE__ */ jsxs("div", {
3973
- onClick: () => setIsExpanded(!isExpanded),
3974
- style: {
3975
- display: "flex",
3976
- alignItems: "center",
3977
- justifyContent: "space-between",
3978
- gap: "10px",
3979
- cursor: "pointer",
3980
- userSelect: "none"
3981
- },
3982
- children: [/* @__PURE__ */ jsxs("div", {
3983
- style: {
3984
- display: "flex",
3985
- alignItems: "center",
3986
- gap: "8px",
3987
- minWidth: 0
3988
- },
3989
- children: [
3990
- /* @__PURE__ */ jsx("svg", {
3991
- style: {
3992
- height: "14px",
3993
- width: "14px",
3994
- color: "#71717a",
3995
- transition: "transform 0.15s",
3996
- transform: isExpanded ? "rotate(90deg)" : "rotate(0deg)",
3997
- flexShrink: 0
3998
- },
3999
- fill: "none",
4000
- viewBox: "0 0 24 24",
4001
- strokeWidth: 2,
4002
- stroke: "currentColor",
4003
- children: /* @__PURE__ */ jsx("path", {
4004
- strokeLinecap: "round",
4005
- strokeLinejoin: "round",
4006
- d: "M8.25 4.5l7.5 7.5-7.5 7.5"
4007
- })
4008
- }),
4009
- /* @__PURE__ */ jsx("span", { style: {
4010
- display: "inline-block",
4011
- height: "8px",
4012
- width: "8px",
4013
- borderRadius: "50%",
4014
- backgroundColor: isActive ? "#f59e0b" : isComplete ? "#10b981" : "#a1a1aa",
4015
- flexShrink: 0
4016
- } }),
4017
- /* @__PURE__ */ jsx("span", {
4018
- style: {
4019
- fontSize: "13px",
4020
- fontWeight: 600,
4021
- color: "#18181b",
4022
- overflow: "hidden",
4023
- textOverflow: "ellipsis",
4024
- whiteSpace: "nowrap"
4025
- },
4026
- children: name
4027
- })
4028
- ]
4029
- }), /* @__PURE__ */ jsx("span", {
4030
- style: {
4031
- display: "inline-flex",
4032
- alignItems: "center",
4033
- borderRadius: "9999px",
4034
- padding: "2px 8px",
4035
- fontSize: "11px",
4036
- fontWeight: 500,
4037
- backgroundColor: isActive ? "#fef3c7" : isComplete ? "#d1fae5" : "#f4f4f5",
4038
- color: isActive ? "#92400e" : isComplete ? "#065f46" : "#3f3f46",
4039
- flexShrink: 0
4040
- },
4041
- children: isActive ? "Running" : isComplete ? "Done" : status
4042
- })]
4043
- }), isExpanded && /* @__PURE__ */ jsxs("div", {
4044
- style: {
4045
- marginTop: "12px",
4046
- display: "grid",
4047
- gap: "12px"
4048
- },
4049
- children: [/* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("div", {
4050
- style: {
4051
- fontSize: "10px",
4052
- textTransform: "uppercase",
4053
- letterSpacing: "0.05em",
4054
- color: "#71717a"
4055
- },
4056
- children: "Arguments"
4057
- }), /* @__PURE__ */ jsx("pre", {
4058
- style: {
4059
- marginTop: "6px",
4060
- maxHeight: "200px",
4061
- overflow: "auto",
4062
- borderRadius: "6px",
4063
- backgroundColor: "#f4f4f5",
4064
- padding: "10px",
4065
- fontSize: "11px",
4066
- lineHeight: 1.6,
4067
- color: "#27272a",
4068
- whiteSpace: "pre-wrap",
4069
- wordBreak: "break-word"
4070
- },
4071
- children: JSON.stringify(parameters ?? {}, null, 2)
4072
- })] }), result !== void 0 && /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("div", {
4073
- style: {
4074
- fontSize: "10px",
4075
- textTransform: "uppercase",
4076
- letterSpacing: "0.05em",
4077
- color: "#71717a"
4078
- },
4079
- children: "Result"
4080
- }), /* @__PURE__ */ jsx("pre", {
4081
- style: {
4082
- marginTop: "6px",
4083
- maxHeight: "200px",
4084
- overflow: "auto",
4085
- borderRadius: "6px",
4086
- backgroundColor: "#f4f4f5",
4087
- padding: "10px",
4088
- fontSize: "11px",
4089
- lineHeight: 1.6,
4090
- color: "#27272a",
4091
- whiteSpace: "pre-wrap",
4092
- wordBreak: "break-word"
4093
- },
4094
- children: typeof result === "string" ? result : JSON.stringify(result, null, 2)
4095
- })] })]
4096
- })]
4097
- })
4098
- });
3850
+ return useMemo(() => ({
3851
+ renderActivityMessage,
3852
+ findRenderer
3853
+ }), [renderActivityMessage, findRenderer]);
3854
+ }
3855
+
3856
+ //#endregion
3857
+ //#region src/v2/hooks/use-frontend-tool.tsx
3858
+ const EMPTY_DEPS = [];
3859
+ function useFrontendTool(tool, deps) {
3860
+ const { copilotkit } = useCopilotKit();
3861
+ const extraDeps = deps ?? EMPTY_DEPS;
3862
+ useEffect(() => {
3863
+ const name = tool.name;
3864
+ if (copilotkit.getTool({
3865
+ toolName: name,
3866
+ agentId: tool.agentId
3867
+ })) {
3868
+ console.warn(`Tool '${name}' already exists for agent '${tool.agentId || "global"}'. Overriding with latest registration.`);
3869
+ copilotkit.removeTool(name, tool.agentId);
3870
+ }
3871
+ copilotkit.addTool(tool);
3872
+ if (tool.render) copilotkit.addHookRenderToolCall({
3873
+ name,
3874
+ args: tool.parameters,
3875
+ agentId: tool.agentId,
3876
+ render: tool.render
3877
+ });
3878
+ return () => {
3879
+ copilotkit.removeTool(name, tool.agentId);
3880
+ };
3881
+ }, [
3882
+ tool.name,
3883
+ tool.available,
3884
+ copilotkit,
3885
+ JSON.stringify(extraDeps)
3886
+ ]);
3887
+ }
3888
+
3889
+ //#endregion
3890
+ //#region src/v2/hooks/use-component.tsx
3891
+ /**
3892
+ * Registers a React component as a frontend tool renderer in chat.
3893
+ *
3894
+ * This hook is a convenience wrapper around `useFrontendTool` that:
3895
+ * - builds a model-facing tool description,
3896
+ * - forwards optional schema parameters (any Standard Schema V1 compatible library),
3897
+ * - renders your component with tool call parameters.
3898
+ *
3899
+ * Use this when you want to display a typed visual component for a tool call
3900
+ * without manually wiring a full frontend tool object.
3901
+ *
3902
+ * When `parameters` is provided, render props are inferred from the schema.
3903
+ * When omitted, the render component may accept any props.
3904
+ *
3905
+ * @typeParam TSchema - Schema describing tool parameters, or `undefined` when no schema is given.
3906
+ * @param config - Tool registration config.
3907
+ * @param deps - Optional dependencies to refresh registration (same semantics as `useEffect`).
3908
+ *
3909
+ * @example
3910
+ * ```tsx
3911
+ * // Without parameters — render accepts any props
3912
+ * useComponent({
3913
+ * name: "showGreeting",
3914
+ * render: ({ message }: { message: string }) => <div>{message}</div>,
3915
+ * });
3916
+ * ```
3917
+ *
3918
+ * @example
3919
+ * ```tsx
3920
+ * // With parameters — render props inferred from schema
3921
+ * useComponent({
3922
+ * name: "showWeatherCard",
3923
+ * parameters: z.object({ city: z.string() }),
3924
+ * render: ({ city }) => <div>{city}</div>,
3925
+ * });
3926
+ * ```
3927
+ *
3928
+ * @example
3929
+ * ```tsx
3930
+ * useComponent(
3931
+ * {
3932
+ * name: "renderProfile",
3933
+ * parameters: z.object({ userId: z.string() }),
3934
+ * render: ProfileCard,
3935
+ * agentId: "support-agent",
3936
+ * },
3937
+ * [selectedAgentId],
3938
+ * );
3939
+ * ```
3940
+ */
3941
+ function useComponent(config, deps) {
3942
+ const prefix = `Use this tool to display the "${config.name}" component in the chat. This tool renders a visual UI component for the user.`;
3943
+ const fullDescription = config.description ? `${prefix}\n\n${config.description}` : prefix;
3944
+ useFrontendTool({
3945
+ name: config.name,
3946
+ description: fullDescription,
3947
+ parameters: config.parameters,
3948
+ render: ({ args }) => {
3949
+ const Component = config.render;
3950
+ return /* @__PURE__ */ jsx(Component, { ...args });
3951
+ },
3952
+ agentId: config.agentId
3953
+ }, deps);
4099
3954
  }
4100
3955
 
4101
3956
  //#endregion
@@ -4164,6 +4019,118 @@ function useHumanInTheLoop(tool, deps) {
4164
4019
  ]);
4165
4020
  }
4166
4021
 
4022
+ //#endregion
4023
+ //#region src/v2/hooks/use-agent.tsx
4024
+ let UseAgentUpdate = /* @__PURE__ */ function(UseAgentUpdate) {
4025
+ UseAgentUpdate["OnMessagesChanged"] = "OnMessagesChanged";
4026
+ UseAgentUpdate["OnStateChanged"] = "OnStateChanged";
4027
+ UseAgentUpdate["OnRunStatusChanged"] = "OnRunStatusChanged";
4028
+ return UseAgentUpdate;
4029
+ }({});
4030
+ const ALL_UPDATES = [
4031
+ UseAgentUpdate.OnMessagesChanged,
4032
+ UseAgentUpdate.OnStateChanged,
4033
+ UseAgentUpdate.OnRunStatusChanged
4034
+ ];
4035
+ function useAgent({ agentId, updates, throttleMs } = {}) {
4036
+ agentId ??= DEFAULT_AGENT_ID;
4037
+ const { copilotkit } = useCopilotKit();
4038
+ const providerThrottleMs = copilotkit.defaultThrottleMs;
4039
+ const [, forceUpdate] = useReducer((x) => x + 1, 0);
4040
+ const updateFlags = useMemo(() => updates ?? ALL_UPDATES, [JSON.stringify(updates)]);
4041
+ const provisionalAgentCache = useRef(/* @__PURE__ */ new Map());
4042
+ const agent = useMemo(() => {
4043
+ const existing = copilotkit.getAgent(agentId);
4044
+ if (existing) {
4045
+ provisionalAgentCache.current.delete(agentId);
4046
+ return existing;
4047
+ }
4048
+ const isRuntimeConfigured = copilotkit.runtimeUrl !== void 0;
4049
+ const status = copilotkit.runtimeConnectionStatus;
4050
+ if (isRuntimeConfigured && (status === CopilotKitCoreRuntimeConnectionStatus.Disconnected || status === CopilotKitCoreRuntimeConnectionStatus.Connecting)) {
4051
+ const cached = provisionalAgentCache.current.get(agentId);
4052
+ if (cached) {
4053
+ cached.headers = { ...copilotkit.headers };
4054
+ return cached;
4055
+ }
4056
+ const provisional = new ProxiedCopilotRuntimeAgent({
4057
+ runtimeUrl: copilotkit.runtimeUrl,
4058
+ agentId,
4059
+ transport: copilotkit.runtimeTransport,
4060
+ runtimeMode: "pending"
4061
+ });
4062
+ provisional.headers = { ...copilotkit.headers };
4063
+ provisionalAgentCache.current.set(agentId, provisional);
4064
+ return provisional;
4065
+ }
4066
+ if (isRuntimeConfigured && status === CopilotKitCoreRuntimeConnectionStatus.Error) {
4067
+ const cached = provisionalAgentCache.current.get(agentId);
4068
+ if (cached) {
4069
+ cached.headers = { ...copilotkit.headers };
4070
+ return cached;
4071
+ }
4072
+ const provisional = new ProxiedCopilotRuntimeAgent({
4073
+ runtimeUrl: copilotkit.runtimeUrl,
4074
+ agentId,
4075
+ transport: copilotkit.runtimeTransport,
4076
+ runtimeMode: "pending"
4077
+ });
4078
+ provisional.headers = { ...copilotkit.headers };
4079
+ provisionalAgentCache.current.set(agentId, provisional);
4080
+ return provisional;
4081
+ }
4082
+ const knownAgents = Object.keys(copilotkit.agents ?? {});
4083
+ const runtimePart = isRuntimeConfigured ? `runtimeUrl=${copilotkit.runtimeUrl}` : "no runtimeUrl";
4084
+ throw new Error(`useAgent: Agent '${agentId}' not found after runtime sync (${runtimePart}). ` + (knownAgents.length ? `Known agents: [${knownAgents.join(", ")}]` : "No agents registered.") + " Verify your runtime /info and/or agents__unsafe_dev_only.");
4085
+ }, [
4086
+ agentId,
4087
+ copilotkit.agents,
4088
+ copilotkit.runtimeConnectionStatus,
4089
+ copilotkit.runtimeUrl,
4090
+ copilotkit.runtimeTransport,
4091
+ JSON.stringify(copilotkit.headers)
4092
+ ]);
4093
+ useEffect(() => {
4094
+ if (updateFlags.length === 0) return;
4095
+ let active = true;
4096
+ const handlers = {};
4097
+ let batchScheduled = false;
4098
+ const batchedForceUpdate = () => {
4099
+ if (!active) return;
4100
+ if (!batchScheduled) {
4101
+ batchScheduled = true;
4102
+ queueMicrotask(() => {
4103
+ batchScheduled = false;
4104
+ if (active) forceUpdate();
4105
+ });
4106
+ }
4107
+ };
4108
+ if (updateFlags.includes(UseAgentUpdate.OnMessagesChanged)) handlers.onMessagesChanged = batchedForceUpdate;
4109
+ if (updateFlags.includes(UseAgentUpdate.OnStateChanged)) handlers.onStateChanged = batchedForceUpdate;
4110
+ if (updateFlags.includes(UseAgentUpdate.OnRunStatusChanged)) {
4111
+ handlers.onRunInitialized = batchedForceUpdate;
4112
+ handlers.onRunFinalized = batchedForceUpdate;
4113
+ handlers.onRunFailed = batchedForceUpdate;
4114
+ handlers.onRunErrorEvent = batchedForceUpdate;
4115
+ }
4116
+ const subscription = copilotkit.subscribeToAgentWithOptions(agent, handlers, { throttleMs });
4117
+ return () => {
4118
+ active = false;
4119
+ subscription.unsubscribe();
4120
+ };
4121
+ }, [
4122
+ agent,
4123
+ forceUpdate,
4124
+ throttleMs,
4125
+ providerThrottleMs,
4126
+ updateFlags
4127
+ ]);
4128
+ useEffect(() => {
4129
+ if (agent instanceof HttpAgent) agent.headers = { ...copilotkit.headers };
4130
+ }, [agent, JSON.stringify(copilotkit.headers)]);
4131
+ return { agent };
4132
+ }
4133
+
4167
4134
  //#endregion
4168
4135
  //#region src/v2/hooks/use-capabilities.tsx
4169
4136
  /**
@@ -5629,6 +5596,190 @@ CopilotChatSuggestionView.displayName = "CopilotChatSuggestionView";
5629
5596
  */
5630
5597
  const ScrollElementContext = React.createContext(null);
5631
5598
 
5599
+ //#endregion
5600
+ //#region src/v2/components/intelligence-indicator/IntelligenceIndicator.tsx
5601
+ /**
5602
+ * Grace window before showing the spinner. A matching tool call must
5603
+ * remain unresolved (no `tool`-role result message in `agent.messages`)
5604
+ * for at least this long before the pill appears. This filters out
5605
+ * history-replay flashes — during `connectAgent` replay, tool calls and
5606
+ * their results arrive back-to-back in sub-millisecond bursts, so the
5607
+ * timer is cancelled before it fires. Live runs cross the threshold
5608
+ * easily because the tool actually has to execute.
5609
+ */
5610
+ const PENDING_THRESHOLD_MS = 100;
5611
+ /** Hold the checkmark briefly before fading out. */
5612
+ const CHECK_HOLD_MS = 800;
5613
+ /**
5614
+ * Duration of the fade-out animation. Must match
5615
+ * `cpk-intelligence-pill-fade-out` keyframes in `v2/styles/globals.css`.
5616
+ */
5617
+ const FADE_OUT_ANIMATION_MS = 480;
5618
+ /**
5619
+ * Tool-name regex patterns that trigger the indicator. Currently
5620
+ * hardcoded to the Intelligence MCP server's canonical tool name. If
5621
+ * we add per-instance customization later (e.g. a `CopilotKitProvider`
5622
+ * prop or a runtime-info field), this constant becomes the fallback.
5623
+ */
5624
+ const DEFAULT_TOOL_PATTERNS = [/^copilotkit_knowledge_base_shell$/];
5625
+ const isMatchingToolCallName = (name) => typeof name === "string" && DEFAULT_TOOL_PATTERNS.some((p) => p.test(name));
5626
+ /**
5627
+ * "Tool-call-like" messages do NOT count as a real follow-up: tool
5628
+ * result messages, assistant messages that carry tool calls, and
5629
+ * empty-content assistant messages (which some providers emit as a
5630
+ * standalone wrapper around a batch of tool calls). A real follow-up
5631
+ * is anything else — most importantly an assistant message with prose
5632
+ * content, or a fresh user message.
5633
+ */
5634
+ const isToolCallLikeMessage = (m) => {
5635
+ if (m.role === "tool") return true;
5636
+ if (m.role === "assistant") {
5637
+ if ((Array.isArray(m.toolCalls) ? m.toolCalls : []).length > 0) return true;
5638
+ const content = m.content;
5639
+ return typeof content !== "string" || content.trim().length === 0;
5640
+ }
5641
+ return false;
5642
+ };
5643
+ /**
5644
+ * The "Using CopilotKit Intelligence" pill. Auto-mounted by
5645
+ * `CopilotChatMessageView` for every message slot when
5646
+ * `copilotkit.intelligence` is configured — callers do not register
5647
+ * this themselves. Self-gates so only the canonical message renders a
5648
+ * pill.
5649
+ *
5650
+ * Render gates (all must hold):
5651
+ * 1. `copilotkit.intelligence !== undefined`
5652
+ * 2. The message is an assistant message with at least one tool call
5653
+ * whose name matches {@link DEFAULT_TOOL_PATTERNS}
5654
+ * 3. The message is the *latest* such matching-assistant message in
5655
+ * `agent.messages` — tool-result messages and prose-only assistant
5656
+ * messages don't invalidate the slot, so the pill stays
5657
+ * continuously through a multi-step tool chain.
5658
+ * 4. The phase machine is past `idle` (the pending-grace timer fired)
5659
+ * and not yet `hidden`.
5660
+ *
5661
+ * Phase machine (per-instance, all timers local):
5662
+ * - Starts in `idle` — nothing rendered.
5663
+ * - `idle → spinner` once a matching tool call has been pending
5664
+ * (no `tool`-role result with a matching `toolCallId`) for
5665
+ * {@link PENDING_THRESHOLD_MS}. Replay flashes (tool call + result
5666
+ * in the same tick) never cross this threshold.
5667
+ * - `spinner → check` as soon as EITHER `agent.isRunning` flips
5668
+ * false OR a non-tool-call-like message appears later in
5669
+ * `agent.messages` (i.e. the agent has produced a "real"
5670
+ * follow-up — prose answer or a new user turn).
5671
+ * - `check → fading` after {@link CHECK_HOLD_MS}.
5672
+ * - `fading → hidden` after {@link FADE_OUT_ANIMATION_MS}.
5673
+ *
5674
+ * Once `hidden`, the phase is sticky — a finished pill never re-spawns
5675
+ * on the same message. New runs mount fresh indicator instances on
5676
+ * their own assistant messages.
5677
+ *
5678
+ * The "exactly one pill at a time" guarantee is structural: only one
5679
+ * message satisfies the latest-matching-assistant gate at any moment.
5680
+ */
5681
+ function IntelligenceIndicator(props) {
5682
+ const { message, agentId, label = "Using CopilotKit Intelligence" } = props;
5683
+ const { copilotkit } = useCopilotKit();
5684
+ const config = useCopilotChatConfiguration();
5685
+ const { agent } = useAgent({
5686
+ agentId,
5687
+ updates: [UseAgentUpdate.OnRunStatusChanged, UseAgentUpdate.OnMessagesChanged]
5688
+ });
5689
+ const matchingToolCallIds = useMemo(() => {
5690
+ if (message.role !== "assistant") return [];
5691
+ const tcs = Array.isArray(message.toolCalls) ? message.toolCalls : [];
5692
+ const ids = [];
5693
+ for (const tc of tcs) if (isMatchingToolCallName(tc?.function?.name) && tc?.id) ids.push(tc.id);
5694
+ return ids;
5695
+ }, [message]);
5696
+ const hasPending = useMemo(() => {
5697
+ if (matchingToolCallIds.length === 0) return false;
5698
+ const resolved = /* @__PURE__ */ new Set();
5699
+ for (const m of agent.messages) if (m.role === "tool" && m.toolCallId) resolved.add(m.toolCallId);
5700
+ return matchingToolCallIds.some((id) => !resolved.has(id));
5701
+ }, [matchingToolCallIds, agent.messages]);
5702
+ const sawRealFollowup = useMemo(() => {
5703
+ const idx = agent.messages.findIndex((m) => m.id === message.id);
5704
+ if (idx < 0) return false;
5705
+ for (let i = idx + 1; i < agent.messages.length; i += 1) if (!isToolCallLikeMessage(agent.messages[i])) return true;
5706
+ return false;
5707
+ }, [agent.messages, message.id]);
5708
+ const [phase, setPhase] = useState("idle");
5709
+ useEffect(() => {
5710
+ if (phase !== "idle") return void 0;
5711
+ if (!hasPending) return void 0;
5712
+ const t = setTimeout(() => setPhase("spinner"), PENDING_THRESHOLD_MS);
5713
+ return () => clearTimeout(t);
5714
+ }, [phase, hasPending]);
5715
+ useEffect(() => {
5716
+ if (phase !== "spinner") return void 0;
5717
+ if (!agent.isRunning || sawRealFollowup) setPhase("check");
5718
+ }, [
5719
+ phase,
5720
+ agent.isRunning,
5721
+ sawRealFollowup
5722
+ ]);
5723
+ useEffect(() => {
5724
+ if (phase !== "check") return void 0;
5725
+ const t = setTimeout(() => setPhase("fading"), CHECK_HOLD_MS);
5726
+ return () => clearTimeout(t);
5727
+ }, [phase]);
5728
+ useEffect(() => {
5729
+ if (phase !== "fading") return void 0;
5730
+ const t = setTimeout(() => setPhase("hidden"), FADE_OUT_ANIMATION_MS);
5731
+ return () => clearTimeout(t);
5732
+ }, [phase]);
5733
+ if (copilotkit.intelligence === void 0) return null;
5734
+ if (!config) return null;
5735
+ if (phase === "idle" || phase === "hidden") return null;
5736
+ if (message.role !== "assistant") return null;
5737
+ if (!(Array.isArray(message.toolCalls) ? message.toolCalls : []).some((tc) => isMatchingToolCallName(tc?.function?.name))) return null;
5738
+ let latestMatchingAssistantId;
5739
+ for (let i = agent.messages.length - 1; i >= 0; i -= 1) {
5740
+ const m = agent.messages[i];
5741
+ if (m.role !== "assistant") continue;
5742
+ if ((Array.isArray(m.toolCalls) ? m.toolCalls : []).some((tc) => isMatchingToolCallName(tc?.function?.name))) {
5743
+ latestMatchingAssistantId = m.id;
5744
+ break;
5745
+ }
5746
+ }
5747
+ if (latestMatchingAssistantId !== message.id) return null;
5748
+ const showSpinner = phase === "spinner";
5749
+ const isFading = phase === "fading";
5750
+ return /* @__PURE__ */ jsxs("span", {
5751
+ className: "cpk-intelligence-pill" + (isFading ? " cpk-intelligence-pill--fading" : ""),
5752
+ role: "status",
5753
+ "aria-live": "polite",
5754
+ "aria-hidden": isFading || void 0,
5755
+ "data-testid": `cpk-intelligence-pill-${message.id}`,
5756
+ title: label,
5757
+ children: [/* @__PURE__ */ jsxs("svg", {
5758
+ className: "cpk-intelligence-pill__icon",
5759
+ viewBox: "0 0 24 24",
5760
+ width: "14",
5761
+ height: "14",
5762
+ "aria-hidden": "true",
5763
+ children: [/* @__PURE__ */ jsx("circle", {
5764
+ cx: "12",
5765
+ cy: "12",
5766
+ r: "9",
5767
+ fill: "none",
5768
+ strokeWidth: "2.5",
5769
+ strokeLinecap: "round",
5770
+ className: "cpk-intelligence-pill__ring" + (showSpinner ? "" : " cpk-intelligence-pill__ring--done")
5771
+ }), /* @__PURE__ */ jsx("path", {
5772
+ d: "M8 12.5l3 3 5-6",
5773
+ fill: "none",
5774
+ strokeWidth: "2.5",
5775
+ strokeLinecap: "round",
5776
+ strokeLinejoin: "round",
5777
+ className: "cpk-intelligence-pill__check" + (showSpinner ? "" : " cpk-intelligence-pill__check--shown")
5778
+ })]
5779
+ }), /* @__PURE__ */ jsx("span", { children: label })]
5780
+ });
5781
+ }
5782
+
5632
5783
  //#endregion
5633
5784
  //#region src/v2/components/chat/CopilotChatMessageView.tsx
5634
5785
  /**
@@ -5786,14 +5937,12 @@ function CopilotChatMessageView({ messages = [], assistantMessage, userMessage,
5786
5937
  const [, forceUpdate] = useReducer((x) => x + 1, 0);
5787
5938
  useEffect(() => {
5788
5939
  if (!config?.agentId) return;
5789
- const registryAgent = copilotkit.getAgent(config.agentId);
5790
- const agent = getThreadClone(registryAgent, config.threadId) ?? registryAgent;
5940
+ const agent = copilotkit.getAgent(config.agentId);
5791
5941
  if (!agent) return;
5792
5942
  const subscription = agent.subscribe({ onStateChanged: forceUpdate });
5793
5943
  return () => subscription.unsubscribe();
5794
5944
  }, [
5795
5945
  config?.agentId,
5796
- config?.threadId,
5797
5946
  copilotkit,
5798
5947
  forceUpdate
5799
5948
  ]);
@@ -5876,6 +6025,10 @@ function CopilotChatMessageView({ messages = [], assistantMessage, userMessage,
5876
6025
  renderCustomMessage,
5877
6026
  stateSnapshot
5878
6027
  }, `${message.id}-custom-after`));
6028
+ if (copilotkit.intelligence !== void 0 && message.role === "assistant") elements.push(/* @__PURE__ */ jsx(IntelligenceIndicator, {
6029
+ message,
6030
+ agentId: config?.agentId ?? DEFAULT_AGENT_ID
6031
+ }, `${message.id}-intelligence`));
5879
6032
  return elements.filter(Boolean);
5880
6033
  };
5881
6034
  const messageElements = shouldVirtualize ? [] : deduplicatedMessages.flatMap(renderMessageBlock);
@@ -6824,7 +6977,6 @@ function CopilotChat({ agentId, threadId, labels, chatView, isModalDefaultOpen,
6824
6977
  const hasExplicitThreadId = !!threadId || !!existingConfig?.hasExplicitThreadId;
6825
6978
  const { agent } = useAgent({
6826
6979
  agentId: resolvedAgentId,
6827
- threadId: resolvedThreadId,
6828
6980
  throttleMs
6829
6981
  });
6830
6982
  const { copilotkit } = useCopilotKit();
@@ -6866,6 +7018,7 @@ function CopilotChat({ agentId, threadId, labels, chatView, isModalDefaultOpen,
6866
7018
  let detached = false;
6867
7019
  const connectAbortController = new AbortController();
6868
7020
  if (agent instanceof HttpAgent) agent.abortController = connectAbortController;
7021
+ agent.threadId = resolvedThreadId;
6869
7022
  const connect = async (agent) => {
6870
7023
  try {
6871
7024
  await copilotkit.connectAgent({ agent });
@@ -7274,18 +7427,19 @@ CopilotModalHeader.CloseButton.displayName = "CopilotModalHeader.CloseButton";
7274
7427
  //#region src/v2/components/chat/CopilotSidebarView.tsx
7275
7428
  const DEFAULT_SIDEBAR_WIDTH = 480;
7276
7429
  const SIDEBAR_TRANSITION_MS = 260;
7277
- function CopilotSidebarView({ header, toggleButton, width, defaultOpen = true, ...props }) {
7430
+ function CopilotSidebarView({ header, toggleButton, width, defaultOpen = true, position = "right", ...props }) {
7278
7431
  return /* @__PURE__ */ jsx(CopilotChatConfigurationProvider, {
7279
7432
  isModalDefaultOpen: defaultOpen,
7280
7433
  children: /* @__PURE__ */ jsx(CopilotSidebarViewInternal, {
7281
7434
  header,
7282
7435
  toggleButton,
7283
7436
  width,
7437
+ position,
7284
7438
  ...props
7285
7439
  })
7286
7440
  });
7287
7441
  }
7288
- function CopilotSidebarViewInternal({ header, toggleButton, width, ...props }) {
7442
+ function CopilotSidebarViewInternal({ header, toggleButton, width, position = "right", ...props }) {
7289
7443
  const isSidebarOpen = useCopilotChatConfiguration()?.isModalOpen ?? false;
7290
7444
  const sidebarRef = useRef(null);
7291
7445
  const [sidebarWidth, setSidebarWidth] = useState(width ?? DEFAULT_SIDEBAR_WIDTH);
@@ -7318,26 +7472,33 @@ function CopilotSidebarViewInternal({ header, toggleButton, width, ...props }) {
7318
7472
  useLayoutEffect(() => {
7319
7473
  if (typeof window === "undefined" || typeof window.matchMedia !== "function") return;
7320
7474
  if (!window.matchMedia("(min-width: 768px)").matches) return;
7475
+ const marginStyleProp = position === "left" ? "marginInlineStart" : "marginInlineEnd";
7476
+ const transitionCssProp = position === "left" ? "margin-inline-start" : "margin-inline-end";
7321
7477
  if (isSidebarOpen) {
7322
- if (hasMounted.current) document.body.style.transition = `margin-inline-end ${SIDEBAR_TRANSITION_MS}ms ease`;
7323
- document.body.style.marginInlineEnd = widthToMargin(sidebarWidth);
7478
+ if (hasMounted.current) document.body.style.transition = `${transitionCssProp} ${SIDEBAR_TRANSITION_MS}ms ease`;
7479
+ document.body.style[marginStyleProp] = widthToMargin(sidebarWidth);
7324
7480
  } else if (hasMounted.current) {
7325
- document.body.style.transition = `margin-inline-end ${SIDEBAR_TRANSITION_MS}ms ease`;
7326
- document.body.style.marginInlineEnd = "";
7481
+ document.body.style.transition = `${transitionCssProp} ${SIDEBAR_TRANSITION_MS}ms ease`;
7482
+ document.body.style[marginStyleProp] = "";
7327
7483
  }
7328
7484
  hasMounted.current = true;
7329
7485
  return () => {
7330
- document.body.style.marginInlineEnd = "";
7486
+ document.body.style[marginStyleProp] = "";
7331
7487
  document.body.style.transition = "";
7332
7488
  };
7333
- }, [isSidebarOpen, sidebarWidth]);
7489
+ }, [
7490
+ isSidebarOpen,
7491
+ sidebarWidth,
7492
+ position
7493
+ ]);
7334
7494
  const headerElement = renderSlot(header, CopilotModalHeader, {});
7335
- return /* @__PURE__ */ jsxs(Fragment$1, { children: [renderSlot(toggleButton, CopilotChatToggleButton, {}), /* @__PURE__ */ jsx("aside", {
7495
+ return /* @__PURE__ */ jsxs(Fragment$1, { children: [renderSlot(toggleButton, CopilotChatToggleButton, position === "left" ? { className: "cpk:left-6 cpk:right-auto" } : {}), /* @__PURE__ */ jsx("aside", {
7336
7496
  ref: sidebarRef,
7337
7497
  "data-copilotkit": true,
7338
7498
  "data-testid": "copilot-sidebar",
7339
7499
  "data-copilot-sidebar": true,
7340
- 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"),
7500
+ "data-position": position,
7501
+ 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"),
7341
7502
  style: {
7342
7503
  ["--sidebar-width"]: widthToCss(sidebarWidth),
7343
7504
  paddingTop: "env(safe-area-inset-top)",
@@ -7544,7 +7705,7 @@ var CopilotPopupView_default = CopilotPopupView;
7544
7705
 
7545
7706
  //#endregion
7546
7707
  //#region src/v2/components/chat/CopilotSidebar.tsx
7547
- function CopilotSidebar({ header, toggleButton, defaultOpen, width, ...chatProps }) {
7708
+ function CopilotSidebar({ header, toggleButton, defaultOpen, width, position, ...chatProps }) {
7548
7709
  const { checkFeature } = useLicenseContext();
7549
7710
  const isSidebarLicensed = checkFeature("sidebar");
7550
7711
  useEffect(() => {
@@ -7552,13 +7713,14 @@ function CopilotSidebar({ header, toggleButton, defaultOpen, width, ...chatProps
7552
7713
  }, [isSidebarLicensed]);
7553
7714
  const SidebarViewOverride = useMemo(() => {
7554
7715
  const Component = (viewProps) => {
7555
- const { header: viewHeader, toggleButton: viewToggleButton, width: viewWidth, defaultOpen: viewDefaultOpen, ...restProps } = viewProps;
7716
+ const { header: viewHeader, toggleButton: viewToggleButton, width: viewWidth, defaultOpen: viewDefaultOpen, position: viewPosition, ...restProps } = viewProps;
7556
7717
  return /* @__PURE__ */ jsx(CopilotSidebarView, {
7557
7718
  ...restProps,
7558
7719
  header: header ?? viewHeader,
7559
7720
  toggleButton: toggleButton ?? viewToggleButton,
7560
7721
  width: width ?? viewWidth,
7561
- defaultOpen: defaultOpen ?? viewDefaultOpen
7722
+ defaultOpen: defaultOpen ?? viewDefaultOpen,
7723
+ position: position ?? viewPosition
7562
7724
  });
7563
7725
  };
7564
7726
  return Object.assign(Component, CopilotChatView_default);
@@ -7566,7 +7728,8 @@ function CopilotSidebar({ header, toggleButton, defaultOpen, width, ...chatProps
7566
7728
  header,
7567
7729
  toggleButton,
7568
7730
  width,
7569
- defaultOpen
7731
+ defaultOpen,
7732
+ position
7570
7733
  ]);
7571
7734
  return /* @__PURE__ */ jsxs(Fragment$1, { children: [!isSidebarLicensed && /* @__PURE__ */ jsx(InlineFeatureWarning, { featureName: "Sidebar" }), /* @__PURE__ */ jsx(CopilotChat, {
7572
7735
  welcomeScreen: CopilotSidebarView.WelcomeScreen,
@@ -9806,5 +9969,5 @@ function validateProps(props) {
9806
9969
  }
9807
9970
 
9808
9971
  //#endregion
9809
- export { useAgentContext as $, CopilotChatSuggestionView as A, useConfigureSuggestions as B, CopilotChatToggleButton as C, CopilotChatView_default as D, CopilotChat as E, CopilotChatAssistantMessage_default as F, useRenderTool as G, useCapabilities as H, CopilotChatToolCallsView as I, useRenderActivityMessage as J, useComponent as K, useAttachments as L, CopilotChatReasoningMessage_default as M, CopilotChatUserMessage_default as N, CopilotChatAttachmentQueue as O, CopilotChatAttachmentRenderer as P, CopilotKitProvider as Q, useThreads$1 as R, CopilotModalHeader as S, DefaultOpenIcon as T, useHumanInTheLoop as U, useSuggestions as V, useDefaultRenderTool as W, UseAgentUpdate as X, useRenderCustomMessages as Y, useAgent as Z, WildcardToolCallRender as _, ThreadsProvider as a, MCPAppsActivityRenderer as at, CopilotPopupView as b, CoAgentStateRendersProvider as c, useRenderToolCall as ct, shouldShowDevConsole as d, CopilotChatInput_default as dt, defineToolCallRenderer as et, useToast as f, AudioRecorderError as ft, useCopilotContext as g, CopilotContext as h, useCopilotChatConfiguration as ht, ThreadsContext as i, MCPAppsActivityContentSchema as it, CopilotChatSuggestionPill as j, CopilotChatMessageView as k, useCoAgentStateRenders as l, useCopilotKit as lt, useCopilotMessagesContext as m, CopilotChatConfigurationProvider as mt, defaultCopilotContextCategories as n, SandboxFunctionsContext as nt, useThreads as o, MCPAppsActivityType as ot, CopilotMessagesContext as p, CopilotChatAudioRecorder as pt, useFrontendTool as q, CoAgentStateRenderBridge as r, useSandboxFunctions as rt, CoAgentStateRendersContext as s, CopilotKitInspector as st, CopilotKit as t, createA2UIMessageRenderer as tt, useAsyncCallback as u, CopilotKitCoreReact as ut, CopilotPopup as v, DefaultCloseIcon as w, CopilotSidebarView as x, CopilotSidebar as y, useInterrupt as z };
9810
- //# sourceMappingURL=copilotkit-N0YiBG5S.mjs.map
9972
+ 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 };
9973
+ //# sourceMappingURL=copilotkit-3XTEoVQO.mjs.map