@copilotkit/react-core 1.79.1 → 1.80.0

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 (42) hide show
  1. package/dist/{copilotkit-CD3uPUN3.mjs → copilotkit-BNLtgOyD.mjs} +1855 -221
  2. package/dist/copilotkit-BNLtgOyD.mjs.map +1 -0
  3. package/dist/{copilotkit-Yz8qXx00.d.mts → copilotkit-CWOSXhRO.d.mts} +277 -40
  4. package/dist/copilotkit-CWOSXhRO.d.mts.map +1 -0
  5. package/dist/{copilotkit-DEaA0Cat.cjs → copilotkit-DKICi3nC.cjs} +1843 -196
  6. package/dist/copilotkit-DKICi3nC.cjs.map +1 -0
  7. package/dist/{copilotkit-k7IYk2AN.d.cts → copilotkit-Dfw9HzO9.d.cts} +277 -40
  8. package/dist/copilotkit-Dfw9HzO9.d.cts.map +1 -0
  9. package/dist/index.cjs +15 -3
  10. package/dist/index.cjs.map +1 -1
  11. package/dist/index.d.cts +1 -1
  12. package/dist/index.d.cts.map +1 -1
  13. package/dist/index.d.mts +1 -1
  14. package/dist/index.d.mts.map +1 -1
  15. package/dist/index.mjs +15 -3
  16. package/dist/index.mjs.map +1 -1
  17. package/dist/index.umd.js +127 -16
  18. package/dist/index.umd.js.map +1 -1
  19. package/dist/v2/context.d.cts +7 -0
  20. package/dist/v2/context.d.cts.map +1 -1
  21. package/dist/v2/context.d.mts +7 -0
  22. package/dist/v2/context.d.mts.map +1 -1
  23. package/dist/v2/headless.cjs +106 -16
  24. package/dist/v2/headless.cjs.map +1 -1
  25. package/dist/v2/headless.d.cts +20 -2
  26. package/dist/v2/headless.d.cts.map +1 -1
  27. package/dist/v2/headless.d.mts +20 -2
  28. package/dist/v2/headless.d.mts.map +1 -1
  29. package/dist/v2/headless.mjs +106 -16
  30. package/dist/v2/headless.mjs.map +1 -1
  31. package/dist/v2/index.cjs +3 -1
  32. package/dist/v2/index.css +1 -1
  33. package/dist/v2/index.d.cts +2 -2
  34. package/dist/v2/index.d.mts +2 -2
  35. package/dist/v2/index.mjs +2 -2
  36. package/dist/v2/index.umd.js +1858 -222
  37. package/dist/v2/index.umd.js.map +1 -1
  38. package/package.json +11 -8
  39. package/dist/copilotkit-CD3uPUN3.mjs.map +0 -1
  40. package/dist/copilotkit-DEaA0Cat.cjs.map +0 -1
  41. package/dist/copilotkit-Yz8qXx00.d.mts.map +0 -1
  42. package/dist/copilotkit-k7IYk2AN.d.cts.map +0 -1
@@ -1,10 +1,10 @@
1
1
  "use client";
2
2
 
3
3
  (function(global, factory) {
4
- typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('./index.css'), require('@copilotkit/core'), require('@ag-ui/client'), require('react'), require('tailwind-merge'), require('lucide-react'), require('@copilotkit/shared'), require('react/jsx-runtime'), require('@radix-ui/react-slot'), require('class-variance-authority'), require('clsx'), require('@radix-ui/react-tooltip'), require('@radix-ui/react-dropdown-menu'), require('streamdown'), require('@ag-ui/core'), require('zod'), require('@copilotkit/mcp-apps-renderer/activity'), require('@copilotkit/a2ui-renderer'), require('zod-to-json-schema'), require('react-dom'), require('@tanstack/react-virtual'), require('use-stick-to-bottom'), require('@copilotkit/web-components/threads-drawer')) :
5
- typeof define === 'function' && define.amd ? define(['exports', './index.css', '@copilotkit/core', '@ag-ui/client', 'react', 'tailwind-merge', 'lucide-react', '@copilotkit/shared', 'react/jsx-runtime', '@radix-ui/react-slot', 'class-variance-authority', 'clsx', '@radix-ui/react-tooltip', '@radix-ui/react-dropdown-menu', 'streamdown', '@ag-ui/core', 'zod', '@copilotkit/mcp-apps-renderer/activity', '@copilotkit/a2ui-renderer', 'zod-to-json-schema', 'react-dom', '@tanstack/react-virtual', 'use-stick-to-bottom', '@copilotkit/web-components/threads-drawer'], factory) :
6
- (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory((global.CopilotKitReactCoreV2 = {}), global.src_v2_index_css,global.CopilotKitCore,global.AgUIClient,global.React,global.tailwindMerge,global.lucideReact,global.CopilotKitShared,global.ReactJsxRuntime,global.RadixReactSlot,global.classVarianceAuthority,global.clsx,global.RadixReactTooltip,global.RadixReactDropdownMenu,global.streamdown,global.AgUICore,global.Zod,global.CopilotKitMcpAppsRendererActivity,global.CopilotKitA2UIRenderer,global.zod_to_json_schema,global.ReactDOM,global._tanstack_react_virtual,global.useStickToBottom,global._copilotkit_web_components_threads_drawer));
7
- })(this, function(exports, src_v2_index_css, _copilotkit_core, _ag_ui_client, react, tailwind_merge, lucide_react, _copilotkit_shared, react_jsx_runtime, _radix_ui_react_slot, class_variance_authority, clsx, _radix_ui_react_tooltip, _radix_ui_react_dropdown_menu, streamdown, _ag_ui_core, zod, _copilotkit_mcp_apps_renderer_activity, _copilotkit_a2ui_renderer, zod_to_json_schema, react_dom, _tanstack_react_virtual, use_stick_to_bottom, _copilotkit_web_components_threads_drawer) {
4
+ typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('./index.css'), require('@copilotkit/core'), require('@ag-ui/client'), require('react'), require('tailwind-merge'), require('lucide-react'), require('@copilotkit/shared'), require('react/jsx-runtime'), require('@radix-ui/react-slot'), require('class-variance-authority'), require('clsx'), require('@radix-ui/react-tooltip'), require('@radix-ui/react-dropdown-menu'), require('streamdown'), require('@ag-ui/core'), require('zod'), require('@copilotkit/mcp-apps-renderer/activity'), require('@copilotkit/a2ui-renderer'), require('zod-to-json-schema'), require('@radix-ui/react-popover'), require('react-dom'), require('@tanstack/react-virtual'), require('use-stick-to-bottom'), require('@copilotkit/web-components/threads-drawer')) :
5
+ typeof define === 'function' && define.amd ? define(['exports', './index.css', '@copilotkit/core', '@ag-ui/client', 'react', 'tailwind-merge', 'lucide-react', '@copilotkit/shared', 'react/jsx-runtime', '@radix-ui/react-slot', 'class-variance-authority', 'clsx', '@radix-ui/react-tooltip', '@radix-ui/react-dropdown-menu', 'streamdown', '@ag-ui/core', 'zod', '@copilotkit/mcp-apps-renderer/activity', '@copilotkit/a2ui-renderer', 'zod-to-json-schema', '@radix-ui/react-popover', 'react-dom', '@tanstack/react-virtual', 'use-stick-to-bottom', '@copilotkit/web-components/threads-drawer'], factory) :
6
+ (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory((global.CopilotKitReactCoreV2 = {}), global.src_v2_index_css,global.CopilotKitCore,global.AgUIClient,global.React,global.tailwindMerge,global.lucideReact,global.CopilotKitShared,global.ReactJsxRuntime,global.RadixReactSlot,global.classVarianceAuthority,global.clsx,global.RadixReactTooltip,global.RadixReactDropdownMenu,global.streamdown,global.AgUICore,global.Zod,global.CopilotKitMcpAppsRendererActivity,global.CopilotKitA2UIRenderer,global.zod_to_json_schema,global._radix_ui_react_popover,global.ReactDOM,global._tanstack_react_virtual,global.useStickToBottom,global._copilotkit_web_components_threads_drawer));
7
+ })(this, function(exports, src_v2_index_css, _copilotkit_core, _ag_ui_client, react, tailwind_merge, lucide_react, _copilotkit_shared, react_jsx_runtime, _radix_ui_react_slot, class_variance_authority, clsx, _radix_ui_react_tooltip, _radix_ui_react_dropdown_menu, streamdown, _ag_ui_core, zod, _copilotkit_mcp_apps_renderer_activity, _copilotkit_a2ui_renderer, zod_to_json_schema, _radix_ui_react_popover, react_dom, _tanstack_react_virtual, use_stick_to_bottom, _copilotkit_web_components_threads_drawer) {
8
8
  Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
9
9
  //#region \0rolldown/runtime.js
10
10
  var __create = Object.create;
@@ -36,6 +36,7 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
36
36
  react = __toESM(react);
37
37
  _radix_ui_react_tooltip = __toESM(_radix_ui_react_tooltip);
38
38
  _radix_ui_react_dropdown_menu = __toESM(_radix_ui_react_dropdown_menu);
39
+ _radix_ui_react_popover = __toESM(_radix_ui_react_popover);
39
40
 
40
41
  //#region src/v2/lib/react-core.ts
41
42
  var CopilotKitCoreReact = class extends _copilotkit_core.CopilotKitCore {
@@ -180,7 +181,7 @@ _radix_ui_react_dropdown_menu = __toESM(_radix_ui_react_dropdown_menu);
180
181
  * Returns true only for plain JS objects (`{}`), excluding arrays, Dates,
181
182
  * class instances, and other exotic objects that happen to have typeof "object".
182
183
  */
183
- function isPlainObject(obj) {
184
+ function isPlainObject$1(obj) {
184
185
  return obj !== null && typeof obj === "object" && Object.prototype.toString.call(obj) === "[object Object]";
185
186
  }
186
187
  /**
@@ -199,7 +200,7 @@ _radix_ui_react_dropdown_menu = __toESM(_radix_ui_react_dropdown_menu);
199
200
  function useShallowStableRef(value) {
200
201
  const ref = (0, react.useRef)(value);
201
202
  if (ref.current === value) return ref.current;
202
- if (isPlainObject(ref.current) && isPlainObject(value)) {
203
+ if (isPlainObject$1(ref.current) && isPlainObject$1(value)) {
203
204
  if (shallowEqual(ref.current, value)) return ref.current;
204
205
  }
205
206
  ref.current = value;
@@ -215,6 +216,8 @@ _radix_ui_react_dropdown_menu = __toESM(_radix_ui_react_dropdown_menu);
215
216
  chatInputToolbarFinishTranscribeButtonLabel: "Finish",
216
217
  chatInputToolbarAddButtonLabel: "Add attachments",
217
218
  chatInputToolbarToolsButtonLabel: "Tools",
219
+ chatInputToolbarSendButtonLabel: "Send message",
220
+ chatInputToolbarStopButtonLabel: "Stop",
218
221
  assistantMessageToolbarCopyCodeLabel: "Copy",
219
222
  assistantMessageToolbarCopyCodeCopiedLabel: "Copied",
220
223
  assistantMessageToolbarCopyMessageLabel: "Copy",
@@ -227,13 +230,18 @@ _radix_ui_react_dropdown_menu = __toESM(_radix_ui_react_dropdown_menu);
227
230
  assistantMessageToolbarInspectorHideDescription: "Until you reload this page",
228
231
  assistantMessageToolbarThumbsUpLabel: "Good response",
229
232
  assistantMessageToolbarThumbsDownLabel: "Bad response",
233
+ assistantMessageFeedbackCommentPlaceholder: "Add a comment (optional)",
234
+ assistantMessageFeedbackCommentSubmitLabel: "Send comment",
230
235
  assistantMessageToolbarReadAloudLabel: "Read aloud",
231
236
  assistantMessageToolbarRegenerateLabel: "Regenerate",
232
237
  userMessageToolbarCopyMessageLabel: "Copy",
233
238
  userMessageToolbarEditMessageLabel: "Edit",
239
+ userMessageToolbarPreviousBranchLabel: "Previous branch",
240
+ userMessageToolbarNextBranchLabel: "Next branch",
234
241
  chatDisclaimerText: "AI can make mistakes. Please verify important information.",
235
242
  chatToggleOpenLabel: "Open chat",
236
243
  chatToggleCloseLabel: "Close chat",
244
+ chatScrollToBottomLabel: "Scroll to bottom",
237
245
  modalHeaderTitle: "CopilotKit Chat",
238
246
  welcomeMessageText: "How can I help you today?"
239
247
  };
@@ -495,9 +503,9 @@ _radix_ui_react_dropdown_menu = __toESM(_radix_ui_react_dropdown_menu);
495
503
 
496
504
  //#endregion
497
505
  //#region src/v2/lib/utils.ts
498
- const twMerge$10 = (0, tailwind_merge.extendTailwindMerge)({ prefix: "cpk" });
506
+ const twMerge$12 = (0, tailwind_merge.extendTailwindMerge)({ prefix: "cpk" });
499
507
  function cn(...inputs) {
500
- return twMerge$10((0, clsx.clsx)(inputs));
508
+ return twMerge$12((0, clsx.clsx)(inputs));
501
509
  }
502
510
 
503
511
  //#endregion
@@ -945,6 +953,11 @@ _radix_ui_react_dropdown_menu = __toESM(_radix_ui_react_dropdown_menu);
945
953
  //#region src/v2/components/chat/CopilotChatInput.tsx
946
954
  const SLASH_MENU_MAX_VISIBLE_ITEMS = 5;
947
955
  const SLASH_MENU_ITEM_HEIGHT_PX = 40;
956
+ /**
957
+ * The chat composer: a text area with the add-menu, voice and send/stop
958
+ * buttons, and a slash-command menu that lists `toolsMenu` items once the text
959
+ * starts with "/".
960
+ */
948
961
  function CopilotChatInput({ mode = "input", onSubmitMessage, onStop, isRunning = false, onStartTranscribe, onCancelTranscribe, onFinishTranscribe, onFinishTranscribeWithAudio, onAddFile, onChange, value, toolsMenu, autoFocus = false, positioning = "static", keyboardHeight = 0, containerRef, showDisclaimer, bottomAnchored = false, textArea, sendButton, startTranscribeButton, cancelTranscribeButton, finishTranscribeButton, addMenuButton, audioRecorder, disclaimer, children, className, ...props }) {
949
962
  const isControlled = value !== void 0;
950
963
  const [internalValue, setInternalValue] = (0, react.useState)(() => value ?? "");
@@ -1164,10 +1177,12 @@ _radix_ui_react_dropdown_menu = __toESM(_radix_ui_react_dropdown_menu);
1164
1177
  send();
1165
1178
  };
1166
1179
  const BoundAudioRecorder = renderSlot(audioRecorder, CopilotChatAudioRecorder, { ref: audioRecorderRef });
1180
+ const showsStop = isProcessing && canStop;
1167
1181
  const BoundSendButton = renderSlot(sendButton, CopilotChatInput.SendButton, {
1168
1182
  onClick: handleSendButtonClick,
1169
1183
  disabled: isProcessing ? !canStop : !canSend,
1170
- children: isProcessing && canStop ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.Square, { className: "cpk:size-[18px] cpk:fill-current" }) : void 0
1184
+ "aria-label": showsStop ? labels.chatInputToolbarStopButtonLabel : labels.chatInputToolbarSendButtonLabel,
1185
+ children: showsStop ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.Square, { className: "cpk:size-[18px] cpk:fill-current" }) : void 0
1171
1186
  });
1172
1187
  const BoundStartTranscribeButton = renderSlot(startTranscribeButton, CopilotChatInput.StartTranscribeButton, { onClick: onStartTranscribe });
1173
1188
  const BoundCancelTranscribeButton = renderSlot(cancelTranscribeButton, CopilotChatInput.CancelTranscribeButton, { onClick: onCancelTranscribe });
@@ -1479,18 +1494,22 @@ _radix_ui_react_dropdown_menu = __toESM(_radix_ui_react_dropdown_menu);
1479
1494
  });
1480
1495
  }
1481
1496
  (function(_CopilotChatInput) {
1482
- _CopilotChatInput.SendButton = ({ className, children, ...props }) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1483
- className: "cpk:mr-[10px]",
1484
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Button, {
1485
- type: "button",
1486
- "data-testid": "copilot-send-button",
1487
- variant: "chatInputToolbarPrimary",
1488
- size: "chatInputToolbarIcon",
1489
- className,
1490
- ...props,
1491
- children: children ?? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.ArrowUp, { className: "cpk:size-[18px]" })
1492
- })
1493
- });
1497
+ _CopilotChatInput.SendButton = ({ className, children, ...props }) => {
1498
+ const labels = useCopilotChatConfiguration()?.labels ?? CopilotChatDefaultLabels;
1499
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1500
+ className: "cpk:mr-[10px]",
1501
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Button, {
1502
+ type: "button",
1503
+ "data-testid": "copilot-send-button",
1504
+ variant: "chatInputToolbarPrimary",
1505
+ size: "chatInputToolbarIcon",
1506
+ className,
1507
+ "aria-label": labels.chatInputToolbarSendButtonLabel,
1508
+ ...props,
1509
+ children: children ?? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.ArrowUp, { className: "cpk:size-[18px]" })
1510
+ })
1511
+ });
1512
+ };
1494
1513
  const ToolbarButton = _CopilotChatInput.ToolbarButton = ({ icon, labelKey, defaultClassName, className, ...props }) => {
1495
1514
  const labels = useCopilotChatConfiguration()?.labels ?? CopilotChatDefaultLabels;
1496
1515
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(Tooltip, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(TooltipTrigger, {
@@ -1500,6 +1519,7 @@ _radix_ui_react_dropdown_menu = __toESM(_radix_ui_react_dropdown_menu);
1500
1519
  variant: "chatInputToolbarSecondary",
1501
1520
  size: "chatInputToolbarIcon",
1502
1521
  className: (0, tailwind_merge.twMerge)(defaultClassName, className),
1522
+ "aria-label": labels[labelKey],
1503
1523
  ...props,
1504
1524
  children: icon
1505
1525
  })
@@ -1570,6 +1590,7 @@ _radix_ui_react_dropdown_menu = __toESM(_radix_ui_react_dropdown_menu);
1570
1590
  size: "chatInputToolbarIcon",
1571
1591
  className: (0, tailwind_merge.twMerge)("cpk:ml-1", className),
1572
1592
  disabled: isDisabled,
1593
+ "aria-label": labels.chatInputToolbarAddButtonLabel,
1573
1594
  ...props,
1574
1595
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.Plus, { className: "cpk:size-[20px]" })
1575
1596
  });
@@ -1719,6 +1740,32 @@ _radix_ui_react_dropdown_menu = __toESM(_radix_ui_react_dropdown_menu);
1719
1740
  }
1720
1741
  }
1721
1742
  /**
1743
+ * The tool-call renderers registered on the provider, kept current.
1744
+ * @internal
1745
+ */
1746
+ function useToolCallRenderers() {
1747
+ const { copilotkit } = useCopilotKit();
1748
+ return (0, react.useSyncExternalStore)((callback) => copilotkit.subscribe({ onRenderToolCallsChanged: callback }).unsubscribe, () => copilotkit.renderToolCalls, () => copilotkit.renderToolCalls);
1749
+ }
1750
+ /**
1751
+ * Which tool names have a renderer of their own, whether a wildcard (`"*"`)
1752
+ * renderer is registered, and which of them opted into grouping.
1753
+ * @internal
1754
+ */
1755
+ function useToolCallRendererNames() {
1756
+ const renderers = useToolCallRenderers();
1757
+ return (0, react.useMemo)(() => {
1758
+ const named = renderers.filter((rc) => rc.name !== "*");
1759
+ const wildcards = renderers.filter((rc) => rc.name === "*");
1760
+ return {
1761
+ named: new Set(named.map((rc) => rc.name)),
1762
+ groupedNames: new Set(named.filter((rc) => rc.group).map((rc) => rc.name)),
1763
+ wildcard: wildcards.length > 0,
1764
+ wildcardGroups: wildcards.some((rc) => rc.group)
1765
+ };
1766
+ }, [renderers]);
1767
+ }
1768
+ /**
1722
1769
  * Hook that returns a function to render tool calls based on the render functions
1723
1770
  * defined in CopilotKitProvider.
1724
1771
  *
@@ -1729,9 +1776,7 @@ _radix_ui_react_dropdown_menu = __toESM(_radix_ui_react_dropdown_menu);
1729
1776
  const agentId = useCopilotChatConfiguration()?.agentId ?? _copilotkit_shared.DEFAULT_AGENT_ID;
1730
1777
  const unrenderedToolNames = (0, react.useRef)(/* @__PURE__ */ new Set());
1731
1778
  const warnedToolNames = (0, react.useRef)(/* @__PURE__ */ new Set());
1732
- const renderToolCalls = (0, react.useSyncExternalStore)((callback) => {
1733
- return copilotkit.subscribe({ onRenderToolCallsChanged: callback }).unsubscribe;
1734
- }, () => copilotkit.renderToolCalls, () => copilotkit.renderToolCalls);
1779
+ const renderToolCalls = useToolCallRenderers();
1735
1780
  const renderToolCall = (0, react.useCallback)(({ toolCall, toolMessage }) => {
1736
1781
  const exactMatches = renderToolCalls.filter((rc) => rc.name === toolCall.function.name);
1737
1782
  const renderConfig = exactMatches.find((rc) => rc.agentId === agentId) || exactMatches.find((rc) => !rc.agentId) || exactMatches[0] || renderToolCalls.find((rc) => rc.name === "*");
@@ -1766,7 +1811,7 @@ _radix_ui_react_dropdown_menu = __toESM(_radix_ui_react_dropdown_menu);
1766
1811
 
1767
1812
  //#endregion
1768
1813
  //#region package.json
1769
- var version = "1.79.1";
1814
+ var version = "1.80.0";
1770
1815
 
1771
1816
  //#endregion
1772
1817
  //#region src/v2/components/CopilotKitInspector.tsx
@@ -2002,7 +2047,7 @@ _radix_ui_react_dropdown_menu = __toESM(_radix_ui_react_dropdown_menu);
2002
2047
  * Fetches resource content on-demand via proxied MCP requests. The React shell
2003
2048
  * owns the iframe; `bindMcpApp` owns the protocol.
2004
2049
  */
2005
- const MCPAppsActivityRenderer = function MCPAppsActivityRenderer({ content, message, agent }) {
2050
+ const MCPAppsActivityRenderer = function MCPAppsActivityRenderer({ content, message, agent, allowSameOriginFallback }) {
2006
2051
  const { copilotkit } = useCopilotKit();
2007
2052
  const messageId = message?.id;
2008
2053
  const containerRef = (0, react.useRef)(null);
@@ -2052,6 +2097,7 @@ _radix_ui_react_dropdown_menu = __toESM(_radix_ui_react_dropdown_menu);
2052
2097
  getAgent: () => agentRef.current,
2053
2098
  host: copilotkit,
2054
2099
  messageId,
2100
+ options: { allowSameOriginFallback },
2055
2101
  hooks: {
2056
2102
  onResource: (resource) => {
2057
2103
  if (!mounted) return;
@@ -2093,6 +2139,7 @@ _radix_ui_react_dropdown_menu = __toESM(_radix_ui_react_dropdown_menu);
2093
2139
  agent,
2094
2140
  copilotkit,
2095
2141
  messageId,
2142
+ allowSameOriginFallback,
2096
2143
  content.resourceUri,
2097
2144
  content.serverHash,
2098
2145
  content.serverId
@@ -3307,7 +3354,8 @@ _radix_ui_react_dropdown_menu = __toESM(_radix_ui_react_dropdown_menu);
3307
3354
  name: def.name,
3308
3355
  args: argsSchema,
3309
3356
  render: def.render,
3310
- ...def.agentId ? { agentId: def.agentId } : {}
3357
+ ...def.agentId ? { agentId: def.agentId } : {},
3358
+ ...def.group ? { group: true } : {}
3311
3359
  };
3312
3360
  }
3313
3361
 
@@ -3427,6 +3475,11 @@ _radix_ui_react_dropdown_menu = __toESM(_radix_ui_react_dropdown_menu);
3427
3475
  - Use CSS Grid or Flexbox for layout. Ensure the UI looks good at any width.
3428
3476
  - Minimal transitions (150ms) for hover/focus states only. No decorative animations.
3429
3477
  - Keep the UI focused and dense — avoid excessive padding. Use compact spacing (8–12px gaps, 10–14px padding in controls).`;
3478
+ /** The MCP Apps renderer with `mcpApps.allowSameOriginFallback` applied. */
3479
+ const SameOriginMCPAppsActivityRenderer = (props) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(MCPAppsActivityRenderer, {
3480
+ ...props,
3481
+ allowSameOriginFallback: true
3482
+ });
3430
3483
  const DEFAULT_LEARNING = Object.freeze({});
3431
3484
  function toLearningConfig(learning) {
3432
3485
  if (learning === true) return DEFAULT_LEARNING;
@@ -3443,7 +3496,8 @@ _radix_ui_react_dropdown_menu = __toESM(_radix_ui_react_dropdown_menu);
3443
3496
  }, [value, warningMessage]);
3444
3497
  return value;
3445
3498
  }
3446
- const CopilotKitProvider = ({ children, runtimeUrl, headers: headersProp = EMPTY_HEADERS, credentials, messageFilter, publicApiKey, publicLicenseKey, licenseToken, properties = EMPTY_PROPERTIES, agents__unsafe_dev_only: agents = EMPTY_AGENTS, selfManagedAgents = EMPTY_AGENTS, renderToolCalls, renderActivityMessages, renderCustomMessages, frontendTools, humanInTheLoop, openGenerativeUI, enableInspector, agentId, showIntelligenceIndicator = true, useSingleEndpoint, onError, a2ui, defaultThrottleMs, debug, learning }) => {
3499
+ /** Provide a shared core instance and synchronize runtime, tool, and UI configuration. */
3500
+ const CopilotKitProvider = ({ children, runtimeUrl, headers: headersProp = EMPTY_HEADERS, credentials, messageFilter, publicApiKey, publicLicenseKey, licenseToken, properties = EMPTY_PROPERTIES, agents__unsafe_dev_only: agents = EMPTY_AGENTS, selfManagedAgents = EMPTY_AGENTS, renderToolCalls, renderActivityMessages, renderCustomMessages, frontendTools, humanInTheLoop, openGenerativeUI, enableInspector, agentId, showIntelligenceIndicator = true, useSingleEndpoint, onError, a2ui, mcpApps, defaultThrottleMs, debug, learning }) => {
3447
3501
  const learningConfig = (0, react.useMemo)(() => toLearningConfig(learning), [learning]);
3448
3502
  const [shouldRenderInspector, setShouldRenderInspector] = (0, react.useState)(false);
3449
3503
  const [inspectorVisible, setInspectorVisible] = (0, react.useState)(false);
@@ -3500,11 +3554,12 @@ _radix_ui_react_dropdown_menu = __toESM(_radix_ui_react_dropdown_menu);
3500
3554
  const core = copilotkitRef.current;
3501
3555
  return (0, _copilotkit_a2ui_renderer.filterCatalog)(rawCatalog, (name) => core ? core.isCatalogComponentEnabled(name) : true);
3502
3556
  }, [rawCatalog, catalogToggleVersion]);
3557
+ const mcpAppsAllowSameOrigin = mcpApps?.allowSameOriginFallback === true;
3503
3558
  const builtInActivityRenderers = (0, react.useMemo)(() => {
3504
3559
  const renderers = [{
3505
3560
  activityType: _copilotkit_mcp_apps_renderer_activity.MCPAppsActivityType,
3506
3561
  content: _copilotkit_mcp_apps_renderer_activity.MCPAppsActivityContentSchema,
3507
- render: MCPAppsActivityRenderer
3562
+ render: mcpAppsAllowSameOrigin ? SameOriginMCPAppsActivityRenderer : MCPAppsActivityRenderer
3508
3563
  }];
3509
3564
  if (openGenUIActive) renderers.push({
3510
3565
  activityType: OpenGenerativeUIActivityType,
@@ -3522,7 +3577,8 @@ _radix_ui_react_dropdown_menu = __toESM(_radix_ui_react_dropdown_menu);
3522
3577
  a2uiActive,
3523
3578
  openGenUIActive,
3524
3579
  a2ui,
3525
- filteredCatalog
3580
+ filteredCatalog,
3581
+ mcpAppsAllowSameOrigin
3526
3582
  ]);
3527
3583
  const allActivityRenderers = (0, react.useMemo)(() => {
3528
3584
  return [...renderActivityMessagesList, ...builtInActivityRenderers];
@@ -3630,6 +3686,7 @@ _radix_ui_react_dropdown_menu = __toESM(_radix_ui_react_dropdown_menu);
3630
3686
  description: _copilotkit_shared.GENERATE_SANDBOXED_UI_DESCRIPTION,
3631
3687
  parameters: GenerateSandboxedUiArgsSchema,
3632
3688
  handler: async () => "UI generated",
3689
+ reconnectBehavior: "resume-pending",
3633
3690
  followUp: true,
3634
3691
  render: OpenGenerativeUIToolRenderer
3635
3692
  }];
@@ -4035,12 +4092,86 @@ _radix_ui_react_dropdown_menu = __toESM(_radix_ui_react_dropdown_menu);
4035
4092
  }), [renderActivityMessage, findRenderer]);
4036
4093
  }
4037
4094
 
4095
+ //#endregion
4096
+ //#region src/v2/hooks/tool-registration.ts
4097
+ /**
4098
+ * Deps comparison shared by `useFrontendTool` (and through it
4099
+ * `useHumanInTheLoop` and `useComponent`) and `useRenderTool`. Not part of the
4100
+ * public API.
4101
+ */
4102
+ const EMPTY_DEPS$1 = [];
4103
+ function isPlainObject(value) {
4104
+ const proto = Object.getPrototypeOf(value);
4105
+ return proto === Object.prototype || proto === null;
4106
+ }
4107
+ /**
4108
+ * Plain data (primitives, plain objects, arrays, dates) compares by value.
4109
+ * Functions always compare equal: these hooks used to compare
4110
+ * `JSON.stringify(deps)`, which turns every function into `null`, and a
4111
+ * function that counted as changed would re-register on every render when it
4112
+ * is written inline (and loop in a component that also reads
4113
+ * `useRenderToolCall`). Everything else (class instances, maps, sets) compares
4114
+ * by identity. A cycle inside plain data is safe: a pair already being
4115
+ * compared counts as equal for that branch.
4116
+ */
4117
+ function depEqual(a, b, seen) {
4118
+ if (Object.is(a, b)) return true;
4119
+ if (typeof a === "function" && typeof b === "function") return true;
4120
+ if (typeof a !== "object" || typeof b !== "object" || a === null || b === null) return false;
4121
+ if (a instanceof Date && b instanceof Date) return Object.is(a.getTime(), b.getTime());
4122
+ const aIsArray = Array.isArray(a);
4123
+ if (aIsArray !== Array.isArray(b)) return false;
4124
+ if (!aIsArray && !(isPlainObject(a) && isPlainObject(b))) return false;
4125
+ if (seen.get(a) === b) return true;
4126
+ seen.set(a, b);
4127
+ if (aIsArray) {
4128
+ const left = a;
4129
+ const right = b;
4130
+ if (left.length !== right.length) return false;
4131
+ for (let i = 0; i < left.length; i++) if (!depEqual(left[i], right[i], seen)) return false;
4132
+ return true;
4133
+ }
4134
+ const left = a;
4135
+ const right = b;
4136
+ const leftKeys = Object.keys(left);
4137
+ if (leftKeys.length !== Object.keys(right).length) return false;
4138
+ for (const key of leftKeys) {
4139
+ if (!Object.prototype.hasOwnProperty.call(right, key)) return false;
4140
+ if (!depEqual(left[key], right[key], seen)) return false;
4141
+ }
4142
+ return true;
4143
+ }
4144
+ /** Whether two deps arrays are equal. A change in length is always a change. */
4145
+ function depsEqual(prev, next) {
4146
+ if (prev.length !== next.length) return false;
4147
+ const seen = /* @__PURE__ */ new Map();
4148
+ for (let i = 0; i < prev.length; i++) if (!depEqual(prev[i], next[i], seen)) return false;
4149
+ return true;
4150
+ }
4151
+ /**
4152
+ * Turns a caller's `deps` array into a number that changes exactly when the
4153
+ * deps change, by the rules of {@link depsEqual}. Put the number, not the
4154
+ * array, in the registration effect's dependency list: that keeps the list a
4155
+ * constant length and never serializes a value.
4156
+ */
4157
+ function useDepsVersion(deps) {
4158
+ const next = deps ?? EMPTY_DEPS$1;
4159
+ const ref = (0, react.useRef)({
4160
+ deps: next,
4161
+ version: 0
4162
+ });
4163
+ if (ref.current.deps !== next && !depsEqual(ref.current.deps, next)) ref.current = {
4164
+ deps: next,
4165
+ version: ref.current.version + 1
4166
+ };
4167
+ return ref.current.version;
4168
+ }
4169
+
4038
4170
  //#endregion
4039
4171
  //#region src/v2/hooks/use-frontend-tool.tsx
4040
- const EMPTY_DEPS$2 = [];
4041
4172
  function useFrontendTool(tool, deps) {
4042
4173
  const { copilotkit } = useCopilotKit();
4043
- const extraDeps = deps ?? EMPTY_DEPS$2;
4174
+ const depsVersion = useDepsVersion(deps);
4044
4175
  (0, react.useLayoutEffect)(() => {
4045
4176
  const name = tool.name;
4046
4177
  if (copilotkit.getTool({
@@ -4064,14 +4195,14 @@ _radix_ui_react_dropdown_menu = __toESM(_radix_ui_react_dropdown_menu);
4064
4195
  tool.name,
4065
4196
  tool.available,
4066
4197
  copilotkit,
4067
- JSON.stringify(extraDeps),
4198
+ depsVersion,
4068
4199
  JSON.stringify(tool.webmcp ?? null)
4069
4200
  ]);
4070
4201
  }
4071
4202
 
4072
4203
  //#endregion
4073
4204
  //#region src/v2/hooks/use-frontend-tools.tsx
4074
- const EMPTY_DEPS$1 = [];
4205
+ const EMPTY_DEPS = [];
4075
4206
  const EMPTY_TOOLS = [];
4076
4207
  /**
4077
4208
  * Register a list of frontend tools whose length can change between renders.
@@ -4125,7 +4256,7 @@ _radix_ui_react_dropdown_menu = __toESM(_radix_ui_react_dropdown_menu);
4125
4256
  function useFrontendTools(tools, deps) {
4126
4257
  const { copilotkit } = useCopilotKit();
4127
4258
  const toolList = tools ?? EMPTY_TOOLS;
4128
- const extraDeps = deps ?? EMPTY_DEPS$1;
4259
+ const extraDeps = deps ?? EMPTY_DEPS;
4129
4260
  (0, react.useLayoutEffect)(() => {
4130
4261
  const deduped = /* @__PURE__ */ new Map();
4131
4262
  for (const tool of toolList) {
@@ -4236,8 +4367,7 @@ _radix_ui_react_dropdown_menu = __toESM(_radix_ui_react_dropdown_menu);
4236
4367
 
4237
4368
  //#endregion
4238
4369
  //#region src/v2/hooks/use-render-tool.tsx
4239
- const EMPTY_DEPS = [];
4240
- /**
4370
+ /**
4241
4371
  * Registers a renderer entry in CopilotKit's `renderToolCalls` registry.
4242
4372
  *
4243
4373
  * Key behavior:
@@ -4280,7 +4410,7 @@ _radix_ui_react_dropdown_menu = __toESM(_radix_ui_react_dropdown_menu);
4280
4410
  */
4281
4411
  function useRenderTool(config, deps) {
4282
4412
  const { copilotkit } = useCopilotKit();
4283
- const extraDeps = deps ?? EMPTY_DEPS;
4413
+ const depsVersion = useDepsVersion(deps);
4284
4414
  (0, react.useEffect)(() => {
4285
4415
  const renderer = config.name === "*" && !config.parameters ? defineToolCallRenderer({
4286
4416
  name: "*",
@@ -4288,7 +4418,8 @@ _radix_ui_react_dropdown_menu = __toESM(_radix_ui_react_dropdown_menu);
4288
4418
  ...props,
4289
4419
  parameters: props.args
4290
4420
  }),
4291
- ...config.agentId ? { agentId: config.agentId } : {}
4421
+ ...config.agentId ? { agentId: config.agentId } : {},
4422
+ group: config.group
4292
4423
  }) : defineToolCallRenderer({
4293
4424
  name: config.name,
4294
4425
  args: config.parameters,
@@ -4306,16 +4437,395 @@ _radix_ui_react_dropdown_menu = __toESM(_radix_ui_react_dropdown_menu);
4306
4437
  parameters: props.args
4307
4438
  });
4308
4439
  },
4309
- ...config.agentId ? { agentId: config.agentId } : {}
4440
+ ...config.agentId ? { agentId: config.agentId } : {},
4441
+ group: config.group
4310
4442
  });
4311
4443
  copilotkit.addHookRenderToolCall(renderer);
4312
4444
  }, [
4313
4445
  config.name,
4446
+ config.group,
4314
4447
  copilotkit,
4315
- JSON.stringify(extraDeps)
4448
+ depsVersion
4316
4449
  ]);
4317
4450
  }
4318
4451
 
4452
+ //#endregion
4453
+ //#region src/v2/components/chat/CopilotChatToolCallGroup.tsx
4454
+ /**
4455
+ * Set by a group around the calls it shows, so CopilotKit's built-in tool card
4456
+ * draws itself as a compact row that fits the group instead of a card.
4457
+ * @internal
4458
+ */
4459
+ const ToolCallGroupContext = (0, react.createContext)(null);
4460
+ /**
4461
+ * A call without a result is running only while the group is active: once
4462
+ * the run has moved on or ended, its result is never coming.
4463
+ */
4464
+ const entryStatus = ({ toolMessage }, active) => toolMessage ? toolMessage.error ? "failed" : "done" : active ? "running" : "stopped";
4465
+ /** @internal */
4466
+ const statusLabels = {
4467
+ running: "Running",
4468
+ done: "Done",
4469
+ failed: "Failed",
4470
+ stopped: "Stopped"
4471
+ };
4472
+ /** The first non-empty string argument: usually a command, path or query. */
4473
+ function argumentPreview(parameters) {
4474
+ if (!parameters || typeof parameters !== "object") return void 0;
4475
+ return Object.values(parameters).find((v) => typeof v === "string" && v.trim().length > 0)?.replace(/\s+/g, " ");
4476
+ }
4477
+ const entryPreview = (entry) => argumentPreview((0, _copilotkit_shared.partialJSONParse)(entry.toolCall.function.arguments));
4478
+ /** "run_command ×2, read_file" */
4479
+ function toolNames(calls) {
4480
+ const counts = /* @__PURE__ */ new Map();
4481
+ for (const { toolCall } of calls) {
4482
+ const name = toolCall.function.name;
4483
+ counts.set(name, (counts.get(name) ?? 0) + 1);
4484
+ }
4485
+ return [...counts].map(([name, count]) => count > 1 ? `${name} ×${count}` : name).join(", ");
4486
+ }
4487
+ const pluralize = (count, noun) => `${count} ${noun}${count === 1 ? "" : "s"}`;
4488
+ /**
4489
+ * Height transition between closed and its content's height. Closed content
4490
+ * stays mounted (so its state survives) but is inert.
4491
+ * @internal
4492
+ */
4493
+ function Collapsible({ open, id, className, children }) {
4494
+ const ref = (0, react.useRef)(null);
4495
+ (0, react.useLayoutEffect)(() => {
4496
+ ref.current?.toggleAttribute("inert", !open);
4497
+ }, [open]);
4498
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
4499
+ ref,
4500
+ id,
4501
+ className: (0, tailwind_merge.twMerge)("cpk:grid cpk:transition-[grid-template-rows] cpk:duration-300 cpk:ease-[cubic-bezier(0.16,1,0.3,1)] cpk:motion-reduce:transition-none", className),
4502
+ style: { gridTemplateRows: open ? "1fr" : "0fr" },
4503
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
4504
+ className: "cpk:min-h-0 cpk:overflow-hidden",
4505
+ children
4506
+ })
4507
+ });
4508
+ }
4509
+ function StatusIcon({ status, className }) {
4510
+ const Icon = {
4511
+ running: lucide_react.Loader2,
4512
+ done: lucide_react.Check,
4513
+ failed: lucide_react.X,
4514
+ stopped: lucide_react.Circle
4515
+ }[status];
4516
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Icon, {
4517
+ "aria-hidden": "true",
4518
+ className: (0, tailwind_merge.twMerge)("cpk:size-3.5 cpk:shrink-0 cpk:text-muted-foreground", status === "running" && "cpk:animate-spin", status === "failed" && "cpk:text-destructive", className)
4519
+ });
4520
+ }
4521
+ function Chevron({ open, className }) {
4522
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.ChevronRight, {
4523
+ "aria-hidden": "true",
4524
+ className: (0, tailwind_merge.twMerge)("cpk:size-3.5 cpk:shrink-0 cpk:transition-transform cpk:duration-200", open && "cpk:rotate-90", className)
4525
+ });
4526
+ }
4527
+ function FailedCount({ calls }) {
4528
+ const failed = calls.filter((call) => call.toolMessage?.error).length;
4529
+ if (!failed) return null;
4530
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
4531
+ className: "cpk:shrink-0 cpk:text-destructive",
4532
+ children: [failed, " failed"]
4533
+ });
4534
+ }
4535
+ /**
4536
+ * Guarded JSON.stringify for the payload blocks. A circular reference would
4537
+ * otherwise crash the entire React tree on render.
4538
+ */
4539
+ function stringifyPayload(value) {
4540
+ if (typeof value === "string") return value;
4541
+ try {
4542
+ return JSON.stringify(value, null, 2);
4543
+ } catch (err) {
4544
+ console.warn("[CopilotKit] Failed to JSON.stringify tool-call payload; falling back to String():", err);
4545
+ try {
4546
+ return String(value);
4547
+ } catch {
4548
+ return "[unserializable]";
4549
+ }
4550
+ }
4551
+ }
4552
+ function Payload({ label, value }) {
4553
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
4554
+ className: "cpk:text-[11px] cpk:font-medium cpk:uppercase cpk:tracking-wide cpk:text-muted-foreground",
4555
+ children: label
4556
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("pre", {
4557
+ className: "cpk:m-0 cpk:mt-1.5 cpk:max-h-[200px] cpk:overflow-auto cpk:rounded-lg cpk:bg-muted cpk:p-2.5 cpk:font-mono cpk:text-xs cpk:leading-relaxed cpk:text-foreground cpk:whitespace-pre-wrap cpk:break-words",
4558
+ children: stringifyPayload(value)
4559
+ })] });
4560
+ }
4561
+ /**
4562
+ * A tool call's arguments and, once it has one, its result.
4563
+ * @internal
4564
+ */
4565
+ function ToolCallPayloads({ parameters, result, className }) {
4566
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4567
+ className: (0, tailwind_merge.twMerge)("cpk:grid cpk:gap-3", className),
4568
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Payload, {
4569
+ label: "Arguments",
4570
+ value: parameters ?? {}
4571
+ }), result !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Payload, {
4572
+ label: "Result",
4573
+ value: result
4574
+ })]
4575
+ });
4576
+ }
4577
+ /**
4578
+ * The built-in tool card drawn as a row inside a group: status, name and main
4579
+ * argument, opening to the payloads.
4580
+ * @internal
4581
+ */
4582
+ function ToolCallRow({ name, parameters, result, status, appearance, className, ...props }) {
4583
+ const [open, setOpen] = (0, react.useState)(false);
4584
+ const detailsId = (0, react.useId)();
4585
+ const preview = argumentPreview(parameters);
4586
+ const line = appearance === "line";
4587
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4588
+ "data-copilotkit": true,
4589
+ className: (0, tailwind_merge.twMerge)(!line && "cpk:border-t cpk:border-border", className),
4590
+ ...props,
4591
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
4592
+ type: "button",
4593
+ "aria-expanded": open,
4594
+ "aria-controls": detailsId,
4595
+ onClick: () => setOpen((value) => !value),
4596
+ className: (0, tailwind_merge.twMerge)("cpk:group/row cpk:m-0 cpk:flex cpk:min-w-0 cpk:cursor-pointer cpk:items-center cpk:gap-2 cpk:border-none cpk:bg-transparent cpk:text-left cpk:text-[13px] cpk:text-inherit cpk:transition-colors cpk:hover:bg-accent/60 cpk:focus-visible:outline-2 cpk:focus-visible:-outline-offset-2 cpk:focus-visible:outline-ring", line ? "cpk:-mx-1.5 cpk:w-[calc(100%+0.75rem)] cpk:rounded-md cpk:px-1.5 cpk:py-1" : "cpk:w-full cpk:px-3.5 cpk:py-2"),
4597
+ children: [
4598
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(StatusIcon, { status }),
4599
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
4600
+ "data-testid": "copilot-tool-render-name",
4601
+ className: (0, tailwind_merge.twMerge)("cpk:shrink-0 cpk:font-mono cpk:text-[13px] cpk:font-medium cpk:text-foreground", status === "running" && "cpk-shimmer"),
4602
+ children: name
4603
+ }),
4604
+ preview && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
4605
+ className: "cpk:min-w-0 cpk:truncate cpk:font-mono cpk:text-[13px] cpk:text-muted-foreground",
4606
+ children: preview
4607
+ }),
4608
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
4609
+ "data-testid": "copilot-tool-render-status",
4610
+ className: "cpk:sr-only",
4611
+ children: statusLabels[status]
4612
+ }),
4613
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Chevron, {
4614
+ open,
4615
+ className: (0, tailwind_merge.twMerge)("cpk:ml-auto cpk:text-muted-foreground", !open && "cpk:opacity-0 cpk:group-hover/row:opacity-100 cpk:group-focus-visible/row:opacity-100")
4616
+ })
4617
+ ]
4618
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Collapsible, {
4619
+ open,
4620
+ id: detailsId,
4621
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ToolCallPayloads, {
4622
+ parameters,
4623
+ result,
4624
+ className: line ? "cpk:mt-1 cpk:mb-2 cpk:ml-5.5" : "cpk:px-3.5 cpk:pt-1 cpk:pb-3"
4625
+ })
4626
+ })]
4627
+ });
4628
+ }
4629
+ /**
4630
+ * Tool calls made in a row, collapsed into one summary. While a call runs the
4631
+ * summary names it ("Running read_file src/app.ts"); once they're all done it
4632
+ * reads "Used 3 tools" and opens to the calls.
4633
+ *
4634
+ * Each call is drawn by its registered renderer. CopilotKit's built-in card
4635
+ * (`useDefaultRenderTool`) draws a compact row inside the card and line
4636
+ * appearances; a custom wildcard renderer draws whatever it draws.
4637
+ */
4638
+ function CopilotChatToolCallGroup({ calls, renderCall, appearance = "card", isActive = false, className, ...props }) {
4639
+ const [open, setOpen] = (0, react.useState)(false);
4640
+ const bodyId = (0, react.useId)();
4641
+ const latest = calls[calls.length - 1];
4642
+ if (!latest) return null;
4643
+ const current = isActive ? calls.find((call) => !call.toolMessage) ?? latest : void 0;
4644
+ const preview = current ? entryPreview(current) : void 0;
4645
+ const done = calls.filter((call) => call.toolMessage).length;
4646
+ const statuses = calls.map((call) => entryStatus(call, isActive));
4647
+ const failedIds = new Set(calls.filter((_, index) => statuses[index] === "failed").map((call) => call.toolCall.id));
4648
+ const status = current ? "running" : failedIds.size > 0 ? "failed" : statuses.includes("stopped") ? "stopped" : "done";
4649
+ const rows = (rowAppearance) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ToolCallGroupContext.Provider, {
4650
+ value: {
4651
+ appearance: rowAppearance,
4652
+ failedIds,
4653
+ active: isActive
4654
+ },
4655
+ children: calls.map((call) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react.default.Fragment, { children: renderCall(call) }, call.toolCall.id))
4656
+ });
4657
+ const groupProps = {
4658
+ "data-copilotkit": true,
4659
+ "data-testid": "copilot-tool-call-group",
4660
+ "data-appearance": appearance,
4661
+ "data-state": status,
4662
+ ...props
4663
+ };
4664
+ const runningLabel = current && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
4665
+ className: "cpk:shrink-0 cpk:font-medium cpk-shimmer",
4666
+ children: ["Running ", current.toolCall.function.name]
4667
+ }), preview && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
4668
+ className: "cpk:min-w-0 cpk:truncate cpk:font-mono cpk:text-xs cpk:opacity-70",
4669
+ children: preview
4670
+ })] });
4671
+ if (appearance === "line") {
4672
+ if (calls.length === 1 && !current) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
4673
+ ...groupProps,
4674
+ className: (0, tailwind_merge.twMerge)("cpk:my-2", className),
4675
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ToolCallGroupContext.Provider, {
4676
+ value: {
4677
+ appearance: "line",
4678
+ failedIds,
4679
+ active: isActive
4680
+ },
4681
+ children: renderCall(latest)
4682
+ })
4683
+ });
4684
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4685
+ ...groupProps,
4686
+ className: (0, tailwind_merge.twMerge)("cpk:my-3", className),
4687
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
4688
+ type: "button",
4689
+ "aria-expanded": open,
4690
+ "aria-controls": bodyId,
4691
+ onClick: () => setOpen((value) => !value),
4692
+ className: "cpk:m-0 cpk:inline-flex cpk:max-w-full cpk:min-w-0 cpk:cursor-pointer cpk:items-center cpk:gap-1.5 cpk:rounded-md cpk:border-none cpk:bg-transparent cpk:p-0 cpk:py-0.5 cpk:text-sm cpk:text-muted-foreground cpk:transition-colors cpk:hover:text-foreground cpk:focus-visible:outline-2 cpk:focus-visible:outline-ring",
4693
+ children: [runningLabel || /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
4694
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
4695
+ className: "cpk:shrink-0 cpk:font-medium",
4696
+ children: ["Used ", pluralize(calls.length, "tool")]
4697
+ }),
4698
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
4699
+ className: "cpk:min-w-0 cpk:truncate cpk:font-mono cpk:text-xs cpk:opacity-70",
4700
+ children: toolNames(calls)
4701
+ }),
4702
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(FailedCount, { calls })
4703
+ ] }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Chevron, { open })]
4704
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Collapsible, {
4705
+ open,
4706
+ id: bodyId,
4707
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
4708
+ className: "cpk:mt-1 cpk:ml-1 cpk:border-l-2 cpk:border-border cpk:py-0.5 cpk:pl-3",
4709
+ children: rows("line")
4710
+ })
4711
+ })]
4712
+ });
4713
+ }
4714
+ if (appearance === "stack") {
4715
+ if (calls.length === 1) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
4716
+ ...groupProps,
4717
+ className: (0, tailwind_merge.twMerge)("cpk:py-1", className),
4718
+ children: renderCall(latest)
4719
+ });
4720
+ const behind = open ? 0 : Math.min(calls.length - 1, 2);
4721
+ const layer = "cpk:rounded-xl cpk:border cpk:border-border cpk:bg-card cpk:text-card-foreground";
4722
+ const latestStatus = entryStatus(latest, isActive);
4723
+ const latestPreview = entryPreview(latest);
4724
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4725
+ ...groupProps,
4726
+ className: (0, tailwind_merge.twMerge)("cpk:my-3", className),
4727
+ style: {
4728
+ paddingBottom: behind * 6,
4729
+ ...props.style
4730
+ },
4731
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4732
+ className: "cpk:relative cpk:isolate",
4733
+ children: [Array.from({ length: behind }, (_, index) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
4734
+ "aria-hidden": "true",
4735
+ className: (0, tailwind_merge.twMerge)(layer, "cpk:absolute cpk:inset-0"),
4736
+ style: {
4737
+ transform: `translateY(${(index + 1) * 6}px) scaleX(${1 - (index + 1) * .045})`,
4738
+ zIndex: -1 - index,
4739
+ opacity: 1 - (index + 1) * .28
4740
+ }
4741
+ }, index)), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
4742
+ type: "button",
4743
+ "aria-expanded": open,
4744
+ "aria-controls": open ? bodyId : void 0,
4745
+ onClick: () => setOpen((value) => !value),
4746
+ className: (0, tailwind_merge.twMerge)("cpk:relative cpk:m-0 cpk:flex cpk:min-w-0 cpk:cursor-pointer cpk:items-center cpk:text-left cpk:transition-colors cpk:focus-visible:outline-2 cpk:focus-visible:outline-ring", open ? "cpk:mb-1 cpk:gap-1.5 cpk:rounded-md cpk:border-none cpk:bg-transparent cpk:p-0 cpk:py-0.5 cpk:text-xs cpk:text-muted-foreground cpk:hover:text-foreground" : (0, tailwind_merge.twMerge)(layer, "cpk:w-full cpk:gap-2 cpk:px-3.5 cpk:py-2.5 cpk:text-[13px] cpk:shadow-[0_1px_2px_rgb(0_0_0/0.04)] cpk:hover:bg-accent")),
4747
+ children: open ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
4748
+ pluralize(calls.length, "tool call"),
4749
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(FailedCount, { calls }),
4750
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Chevron, {
4751
+ open: true,
4752
+ className: "cpk:-rotate-90"
4753
+ }),
4754
+ "Collapse"
4755
+ ] }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
4756
+ className: "cpk:flex cpk:min-w-0 cpk:flex-1 cpk:items-center cpk:gap-2 cpk:animate-[cpk-stack-drop_260ms_cubic-bezier(0.16,1,0.3,1)] cpk:motion-reduce:animate-none",
4757
+ children: [
4758
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(StatusIcon, { status: latestStatus }),
4759
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
4760
+ className: (0, tailwind_merge.twMerge)("cpk:shrink-0 cpk:font-mono cpk:font-medium cpk:text-foreground", latestStatus === "running" && "cpk-shimmer"),
4761
+ children: latest.toolCall.function.name
4762
+ }),
4763
+ latestPreview && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
4764
+ className: "cpk:min-w-0 cpk:truncate cpk:font-mono cpk:text-muted-foreground",
4765
+ children: latestPreview
4766
+ })
4767
+ ]
4768
+ }, latest.toolCall.id), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
4769
+ className: "cpk:ml-2 cpk:inline-flex cpk:shrink-0 cpk:items-center cpk:gap-2 cpk:text-xs cpk:text-muted-foreground",
4770
+ children: [
4771
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(FailedCount, { calls }),
4772
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
4773
+ className: "cpk:tabular-nums",
4774
+ children: pluralize(calls.length, "call")
4775
+ }),
4776
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Chevron, {
4777
+ open: false,
4778
+ className: "cpk:rotate-90"
4779
+ })
4780
+ ]
4781
+ })] })
4782
+ })]
4783
+ }), open && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
4784
+ id: bodyId,
4785
+ className: "cpk:[&>*]:animate-[cpk-stack-fan_300ms_cubic-bezier(0.16,1,0.3,1)_both] cpk:motion-reduce:[&>*]:animate-none",
4786
+ children: calls.map((call) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react.default.Fragment, { children: renderCall(call) }, call.toolCall.id))
4787
+ })]
4788
+ });
4789
+ }
4790
+ if (calls.length === 1) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
4791
+ ...groupProps,
4792
+ className: (0, tailwind_merge.twMerge)("cpk:py-1", className),
4793
+ children: renderCall(latest)
4794
+ });
4795
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4796
+ ...groupProps,
4797
+ className: (0, tailwind_merge.twMerge)("cpk:my-3 cpk:overflow-hidden cpk:rounded-xl cpk:border cpk:border-border cpk:bg-card cpk:text-card-foreground", className),
4798
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
4799
+ type: "button",
4800
+ "aria-expanded": open,
4801
+ "aria-controls": bodyId,
4802
+ onClick: () => setOpen((value) => !value),
4803
+ className: "cpk:m-0 cpk:flex cpk:w-full cpk:min-w-0 cpk:cursor-pointer cpk:items-center cpk:gap-2.5 cpk:border-none cpk:bg-transparent cpk:px-3.5 cpk:py-2.5 cpk:text-left cpk:text-[13px] cpk:text-inherit cpk:transition-colors cpk:hover:bg-accent/60 cpk:focus-visible:outline-2 cpk:focus-visible:-outline-offset-2 cpk:focus-visible:outline-ring",
4804
+ children: [
4805
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(StatusIcon, { status }),
4806
+ runningLabel || /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
4807
+ className: "cpk:shrink-0 cpk:font-medium cpk:text-foreground",
4808
+ children: ["Used ", pluralize(calls.length, "tool")]
4809
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
4810
+ className: "cpk:min-w-0 cpk:truncate cpk:font-mono cpk:text-xs cpk:text-muted-foreground",
4811
+ children: toolNames(calls)
4812
+ })] }),
4813
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
4814
+ className: "cpk:ml-auto cpk:flex cpk:shrink-0 cpk:items-center cpk:gap-2 cpk:pl-2 cpk:text-xs cpk:text-muted-foreground",
4815
+ children: [status === "failed" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(FailedCount, { calls }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
4816
+ className: "cpk:tabular-nums",
4817
+ children: status === "running" ? `${done} done` : statusLabels[status]
4818
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Chevron, { open })]
4819
+ })
4820
+ ]
4821
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Collapsible, {
4822
+ open,
4823
+ id: bodyId,
4824
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { children: rows("card") })
4825
+ })]
4826
+ });
4827
+ }
4828
+
4319
4829
  //#endregion
4320
4830
  //#region src/v2/hooks/use-default-render-tool.tsx
4321
4831
  /**
@@ -4399,7 +4909,8 @@ _radix_ui_react_dropdown_menu = __toESM(_radix_ui_react_dropdown_menu);
4399
4909
  const userRender = config?.render;
4400
4910
  useRenderTool({
4401
4911
  name: "*",
4402
- render: userRender ? (raw) => userRender(adaptRendererProps(raw)) : (raw) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DefaultToolCallRenderer, { ...adaptRendererProps(raw) })
4912
+ render: userRender ? (raw) => userRender(adaptRendererProps(raw)) : (raw) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DefaultToolCallRenderer, { ...adaptRendererProps(raw) }),
4913
+ group: userRender ? config?.group === true : true
4403
4914
  }, deps);
4404
4915
  }
4405
4916
  /**
@@ -4420,9 +4931,23 @@ _radix_ui_react_dropdown_menu = __toESM(_radix_ui_react_dropdown_menu);
4420
4931
  }
4421
4932
  }
4422
4933
  function DefaultToolCallRenderer({ name, toolCallId, parameters, status, result }) {
4934
+ const group = (0, react.useContext)(ToolCallGroupContext);
4423
4935
  const [isExpanded, setIsExpanded] = (0, react.useState)(false);
4424
4936
  const isActive = status === "inProgress" || status === "executing";
4425
4937
  const isComplete = status === "complete";
4938
+ if (group) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ToolCallRow, {
4939
+ "data-testid": "copilot-tool-render",
4940
+ "data-tool-name": name,
4941
+ "data-tool-call-id": toolCallId,
4942
+ "data-status": status,
4943
+ "data-args": safeStringifyForAttr(parameters),
4944
+ "data-result": safeStringifyForAttr(result),
4945
+ name,
4946
+ parameters,
4947
+ result,
4948
+ status: isComplete ? group.failedIds.has(toolCallId) ? "failed" : "done" : group.active ? "running" : "stopped",
4949
+ appearance: group.appearance
4950
+ });
4426
4951
  const statusLabel = isActive ? "Running" : isComplete ? "Done" : status;
4427
4952
  const dotClassName = isActive ? "cpk:bg-amber-500" : isComplete ? "cpk:bg-emerald-500" : "cpk:bg-zinc-400";
4428
4953
  const badgeClassName = isActive ? "cpk:bg-amber-100 cpk:text-amber-800 cpk:dark:bg-amber-500/15 cpk:dark:text-amber-400" : isComplete ? "cpk:bg-emerald-100 cpk:text-emerald-800 cpk:dark:bg-emerald-500/15 cpk:dark:text-emerald-400" : "cpk:bg-zinc-100 cpk:text-zinc-800 cpk:dark:bg-zinc-700/40 cpk:dark:text-zinc-300";
@@ -6119,38 +6644,227 @@ _radix_ui_react_dropdown_menu = __toESM(_radix_ui_react_dropdown_menu);
6119
6644
  });
6120
6645
  }
6121
6646
 
6647
+ //#endregion
6648
+ //#region src/v2/components/chat/tool-call-display-context.ts
6649
+ /** @internal */
6650
+ const ToolCallDisplayContext = (0, react.createContext)(null);
6651
+
6652
+ //#endregion
6653
+ //#region src/v2/components/chat/tool-call-layout.ts
6654
+ function contentLength(content) {
6655
+ return typeof content === "string" ? content.length : JSON.stringify(content ?? "").length;
6656
+ }
6657
+ const entrySignature = ({ toolCall, toolMessage }) => [
6658
+ toolCall.id,
6659
+ toolCall.function.name,
6660
+ toolCall.function.arguments.length,
6661
+ toolMessage ? `${contentLength(toolMessage.content)}${toolMessage.error ? "!" : ""}` : "-"
6662
+ ].join(":");
6663
+ function signatureOf(segments) {
6664
+ return segments.map((segment) => segment.kind === "call" ? `c(${entrySignature(segment.call)})` : `g${segment.trailing ? "*" : ""}(${segment.calls.map(entrySignature).join(",")})`).join("|");
6665
+ }
6666
+ /** Index of tool results by the call they answer. */
6667
+ function getToolResults(messages) {
6668
+ const results = /* @__PURE__ */ new Map();
6669
+ for (const message of messages) if (message.role === "tool") results.set(message.toolCallId, message);
6670
+ return results;
6671
+ }
6672
+ /**
6673
+ * Lays out the tool calls of every assistant message in `rendered`.
6674
+ *
6675
+ * Calls drawn by a renderer that opted into grouping join the group that's
6676
+ * open; anything else that shows between them closes it: assistant text, a
6677
+ * user, reasoning or activity message, or a call drawn by any other renderer. A group belongs to
6678
+ * the message where it starts, so later messages whose calls joined it render
6679
+ * no tool calls themselves.
6680
+ *
6681
+ * @param rendered - The messages as the chat renders them, in order.
6682
+ * @param all - Every message, for finding tool results (a transform may hide them).
6683
+ * @param renderers - The registered renderers.
6684
+ */
6685
+ function getToolCallLayouts(rendered, all, renderers) {
6686
+ const results = getToolResults(all);
6687
+ const layouts = /* @__PURE__ */ new Map();
6688
+ let open;
6689
+ for (const message of rendered) {
6690
+ if (message.role === "tool") continue;
6691
+ if (message.role !== "assistant") {
6692
+ open = void 0;
6693
+ continue;
6694
+ }
6695
+ if (typeof message.content === "string" && message.content.trim()) open = void 0;
6696
+ const segments = [];
6697
+ layouts.set(message.id, segments);
6698
+ const toolCalls = /* @__PURE__ */ new Map();
6699
+ for (const toolCall of message.toolCalls ?? []) toolCalls.set(toolCall.id, toolCall);
6700
+ for (const toolCall of toolCalls.values()) {
6701
+ const entry = {
6702
+ toolCall,
6703
+ toolMessage: results.get(toolCall.id)
6704
+ };
6705
+ const name = toolCall.function.name;
6706
+ const named = renderers.named.has(name);
6707
+ const rendered = named || renderers.wildcard;
6708
+ const groups = named ? renderers.groupedNames.has(name) : renderers.wildcardGroups;
6709
+ if (!rendered || !groups) {
6710
+ if (rendered) open = void 0;
6711
+ segments.push({
6712
+ kind: "call",
6713
+ id: toolCall.id,
6714
+ call: entry,
6715
+ rendered
6716
+ });
6717
+ } else if (open) open.calls.push(entry);
6718
+ else {
6719
+ open = {
6720
+ kind: "group",
6721
+ id: toolCall.id,
6722
+ calls: [entry],
6723
+ trailing: false
6724
+ };
6725
+ segments.push(open);
6726
+ }
6727
+ }
6728
+ }
6729
+ if (open) open.trailing = true;
6730
+ const layout = /* @__PURE__ */ new Map();
6731
+ for (const [id, segments] of layouts) layout.set(id, {
6732
+ segments,
6733
+ signature: signatureOf(segments)
6734
+ });
6735
+ return layout;
6736
+ }
6737
+ /**
6738
+ * Finds the working phase of every reply in `rendered`: its messages up to the
6739
+ * last one that calls a tool. Text in there was narration; what follows is the
6740
+ * reply itself. Replies that called no tools are omitted, except the latest,
6741
+ * which may be about to.
6742
+ */
6743
+ function getTurnWork(rendered, layouts) {
6744
+ const turns = /* @__PURE__ */ new Map();
6745
+ let start = 0;
6746
+ while (start < rendered.length) {
6747
+ let end = start;
6748
+ while (end < rendered.length && rendered[end].role !== "user") end++;
6749
+ const key = start > 0 ? rendered[start - 1].id : "start";
6750
+ const visible = rendered.slice(start, end).filter((message) => message.role !== "tool");
6751
+ const isLatest = end === rendered.length;
6752
+ const calls = [];
6753
+ for (const message of visible) for (const segment of layouts.get(message.id)?.segments ?? []) if (segment.kind === "group") calls.push(...segment.calls);
6754
+ else if (segment.rendered) calls.push(segment.call);
6755
+ const shown = new Set(calls.map((entry) => entry.toolCall.id));
6756
+ let lastWork = -1;
6757
+ visible.forEach((message, index) => {
6758
+ if (message.role === "assistant" && message.toolCalls?.some((call) => shown.has(call.id))) lastWork = index;
6759
+ });
6760
+ const first = visible[0];
6761
+ if (first && (calls.length > 0 || isLatest)) {
6762
+ let lastAssistant = -1;
6763
+ visible.forEach((message, index) => {
6764
+ if (message.role === "assistant") lastAssistant = index;
6765
+ });
6766
+ const replyText = visible[lastAssistant]?.content;
6767
+ const endsWithWork = lastAssistant >= 0 && lastAssistant <= lastWork && typeof replyText === "string" && replyText.trim().length > 0;
6768
+ turns.set(key, {
6769
+ key,
6770
+ firstMessageId: first.id,
6771
+ workMessageIds: new Set(visible.slice(0, lastWork + 1).map((message) => message.id)),
6772
+ replyTextMessageId: endsWithWork ? visible[lastAssistant].id : void 0,
6773
+ calls,
6774
+ isLatest
6775
+ });
6776
+ }
6777
+ start = end + 1;
6778
+ }
6779
+ return turns;
6780
+ }
6781
+
6122
6782
  //#endregion
6123
6783
  //#region src/v2/components/chat/CopilotChatToolCallsView.tsx
6124
- function CopilotChatToolCallsView({ message, messages = [] }) {
6784
+ const appearances = {
6785
+ grouped: "card",
6786
+ line: "line",
6787
+ stacked: "stack"
6788
+ };
6789
+ function CopilotChatToolCallsView({ message, messages = [], display: displayProp, group }) {
6125
6790
  const renderToolCall = useRenderToolCall();
6791
+ const rendererNames = useToolCallRendererNames();
6126
6792
  const { copilotkit } = useCopilotKit();
6127
6793
  const subscribe = (0, react.useCallback)((onChange) => copilotkit.ɵsubscribeToLearningConfigured(onChange), [copilotkit]);
6128
6794
  const getSnapshot = () => copilotkit.ɵlearningConfigured;
6129
6795
  const learningConfigured = (0, react.useSyncExternalStore)(subscribe, getSnapshot, getSnapshot);
6796
+ const context = (0, react.useContext)(ToolCallDisplayContext);
6797
+ const display = displayProp ?? context?.display ?? "grouped";
6798
+ const viewLayout = context?.layouts.get(message.id);
6799
+ const layout = (0, react.useMemo)(() => {
6800
+ if (display === "separate") return void 0;
6801
+ return viewLayout ?? getToolCallLayouts([message], messages, rendererNames).get(message.id);
6802
+ }, [
6803
+ display,
6804
+ viewLayout,
6805
+ message,
6806
+ messages,
6807
+ rendererNames
6808
+ ]);
6130
6809
  if (!message.toolCalls || message.toolCalls.length === 0) return null;
6131
- return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_jsx_runtime.Fragment, { children: message.toolCalls.map((toolCall) => {
6810
+ const renderCall = (entry) => {
6132
6811
  const rendered = renderToolCall({
6133
- toolCall,
6134
- toolMessage: messages.find((m) => m.role === "tool" && m.toolCallId === toolCall.id)
6812
+ toolCall: entry.toolCall,
6813
+ toolMessage: entry.toolMessage
6135
6814
  });
6136
- if (rendered === null) return null;
6137
- if (!learningConfigured) return rendered;
6815
+ if (rendered === null || !learningConfigured) return rendered;
6138
6816
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
6139
- "data-tool-call-id": toolCall.id,
6817
+ "data-tool-call-id": entry.toolCall.id,
6140
6818
  style: { display: "contents" },
6141
6819
  children: rendered
6142
- }, toolCall.id);
6820
+ });
6821
+ };
6822
+ if (!layout) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_jsx_runtime.Fragment, { children: message.toolCalls.map((toolCall) => {
6823
+ const toolMessage = messages.find((m) => m.role === "tool" && m.toolCallId === toolCall.id);
6824
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react.default.Fragment, { children: renderCall({
6825
+ toolCall,
6826
+ toolMessage
6827
+ }) }, toolCall.id);
6828
+ }) });
6829
+ const workOpen = context?.workOpen.get(message.id);
6830
+ const groupActive = (trailing) => trailing && (context ? context.isRunning : true);
6831
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_jsx_runtime.Fragment, { children: layout.segments.map((segment) => {
6832
+ if (segment.kind === "call") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react.default.Fragment, { children: renderCall(segment.call) }, segment.id);
6833
+ if (display === "turn") {
6834
+ const failedIds = new Set(segment.calls.filter((call) => call.toolMessage?.error).map((call) => call.toolCall.id));
6835
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Collapsible, {
6836
+ open: workOpen ?? true,
6837
+ className: "cpk:-mx-2",
6838
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
6839
+ className: "cpk:px-2 cpk:py-1.5",
6840
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ToolCallGroupContext.Provider, {
6841
+ value: {
6842
+ appearance: "line",
6843
+ failedIds,
6844
+ active: groupActive(segment.trailing)
6845
+ },
6846
+ children: segment.calls.map((call) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react.default.Fragment, { children: renderCall(call) }, call.toolCall.id))
6847
+ })
6848
+ })
6849
+ }, segment.id);
6850
+ }
6851
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react.default.Fragment, { children: renderSlot(group, CopilotChatToolCallGroup, {
6852
+ calls: segment.calls,
6853
+ renderCall,
6854
+ appearance: appearances[display] ?? "card",
6855
+ isActive: groupActive(segment.trailing)
6856
+ }) }, segment.id);
6143
6857
  }) });
6144
6858
  }
6145
6859
 
6146
6860
  //#endregion
6147
6861
  //#region src/v2/components/chat/CopilotChatInspectorButton.tsx
6148
6862
  let shortcutsHidden = false;
6149
- const listeners = /* @__PURE__ */ new Set();
6863
+ const listeners$1 = /* @__PURE__ */ new Set();
6150
6864
  const subscribe = (listener) => {
6151
- listeners.add(listener);
6865
+ listeners$1.add(listener);
6152
6866
  return () => {
6153
- listeners.delete(listener);
6867
+ listeners$1.delete(listener);
6154
6868
  };
6155
6869
  };
6156
6870
  const getSnapshot = () => shortcutsHidden;
@@ -6160,7 +6874,7 @@ _radix_ui_react_dropdown_menu = __toESM(_radix_ui_react_dropdown_menu);
6160
6874
  }
6161
6875
  function hideShortcuts() {
6162
6876
  shortcutsHidden = true;
6163
- listeners.forEach((listener) => listener());
6877
+ listeners$1.forEach((listener) => listener());
6164
6878
  }
6165
6879
  function InspectorKiteIcon({ className, ...props }) {
6166
6880
  const id = (0, react.useId)();
@@ -6427,47 +7141,544 @@ _radix_ui_react_dropdown_menu = __toESM(_radix_ui_react_dropdown_menu);
6427
7141
  }
6428
7142
 
6429
7143
  //#endregion
6430
- //#region src/v2/components/chat/CopilotChatAssistantMessage.tsx
6431
- /**
6432
- * Binds the Inspector action to the active chat without making the full
6433
- * assistant message subscribe to chat configuration changes.
6434
- */
6435
- function BoundInspectorButton({ inspectorButton, messageId, openInspector, ...props }) {
6436
- const chatConfiguration = useCopilotChatConfiguration();
6437
- return renderSlot(inspectorButton, CopilotChatAssistantMessage.InspectorButton, {
6438
- ...props,
6439
- onClick: () => openInspector({
6440
- messageId,
6441
- threadId: chatConfiguration?.threadId,
6442
- agentId: chatConfiguration?.agentId
6443
- })
7144
+ //#region src/v2/components/ui/popover.tsx
7145
+ function Popover({ ...props }) {
7146
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_radix_ui_react_popover.Root, {
7147
+ "data-slot": "popover",
7148
+ ...props
6444
7149
  });
6445
7150
  }
6446
- function CopilotChatAssistantMessage({ message, messages, isRunning, isLatest, onThumbsUp, onThumbsDown, onReadAloud, onRegenerate, additionalToolbarItems, toolbarVisible = true, markdownRenderer, toolbar, copyButton, inspectorButton, thumbsUpButton, thumbsDownButton, readAloudButton, regenerateButton, toolCallsView, children, className, ...props }) {
6447
- useKatexStyles();
6448
- const { isInspectorEnabled, openInspector } = useCopilotKitInspector();
6449
- const shortcutsHidden = useInspectorShortcutsHidden();
6450
- const showInspectorShortcut = isInspectorEnabled && !shortcutsHidden;
6451
- const boundMarkdownRenderer = renderSlot(markdownRenderer, CopilotChatAssistantMessage.MarkdownRenderer, { content: message.content || "" });
6452
- const boundCopyButton = renderSlot(copyButton, CopilotChatAssistantMessage.CopyButton, { onClick: async () => {
6453
- if (message.content) return await (0, _copilotkit_shared.copyToClipboard)(message.content);
6454
- return false;
7151
+ function PopoverAnchor({ ...props }) {
7152
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_radix_ui_react_popover.Anchor, {
7153
+ "data-slot": "popover-anchor",
7154
+ ...props
7155
+ });
7156
+ }
7157
+ /**
7158
+ * Popover content in a portal, so it floats over the page instead of taking
7159
+ * room in the layout, with the CopilotKit style scope on it.
7160
+ */
7161
+ const PopoverContent = react.forwardRef(({ className, align = "start", sideOffset = 6, ...props }, ref) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_radix_ui_react_popover.Portal, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_radix_ui_react_popover.Content, {
7162
+ ref,
7163
+ "data-copilotkit": true,
7164
+ "data-copilotkit-popover": true,
7165
+ "data-slot": "popover-content",
7166
+ align,
7167
+ sideOffset,
7168
+ className: cn("cpk:z-50 cpk:outline-none cpk:data-[state=open]:animate-in cpk:data-[state=open]:fade-in-0 cpk:data-[state=open]:zoom-in-95 cpk:data-[side=bottom]:slide-in-from-top-1 cpk:data-[side=top]:slide-in-from-bottom-1", className),
7169
+ ...props
7170
+ }) }));
7171
+ PopoverContent.displayName = "PopoverContent";
7172
+
7173
+ //#endregion
7174
+ //#region src/v2/lib/message-feedback.ts
7175
+ /** Trajectory event name for a rating of one assistant message. */
7176
+ const MESSAGE_FEEDBACK_EVENT_NAME = "message.feedback";
7177
+ /** Longest comment kept, in characters; the rest is cut so the event stays small. */
7178
+ const MESSAGE_FEEDBACK_MAX_COMMENT_LENGTH = 1e3;
7179
+ /** Longest excerpt of the rated response sent with the rating, in characters. */
7180
+ const RESPONSE_EXCERPT_LENGTH = 500;
7181
+ const ratings = /* @__PURE__ */ new Map();
7182
+ const listeners = /* @__PURE__ */ new Map();
7183
+ const threadKey = (agentId, threadId) => JSON.stringify([agentId, threadId]);
7184
+ const messageKey = (agentId, threadId, messageId) => JSON.stringify([
7185
+ agentId,
7186
+ threadId,
7187
+ messageId
7188
+ ]);
7189
+ /** Cuts `text` to `max` characters without splitting a surrogate pair. */
7190
+ const clip = (text, max) => {
7191
+ const characters = Array.from(text);
7192
+ return characters.length > max ? characters.slice(0, max).join("") : text;
7193
+ };
7194
+ /** The comment as kept and sent: trimmed and capped, or none. */
7195
+ const normalizeComment = (comment) => {
7196
+ if (typeof comment !== "string") return void 0;
7197
+ const text = clip(comment.trim(), MESSAGE_FEEDBACK_MAX_COMMENT_LENGTH);
7198
+ return text === "" ? void 0 : text;
7199
+ };
7200
+ /** The rating this page gave one message, if any. */
7201
+ function getMessageFeedback(agentId, threadId, messageId) {
7202
+ return ratings.get(messageKey(agentId, threadId, messageId));
7203
+ }
7204
+ /** Calls `listener` whenever a rating in this Thread changes. */
7205
+ function subscribeToMessageFeedback(agentId, threadId, listener) {
7206
+ const key = threadKey(agentId, threadId);
7207
+ let set = listeners.get(key);
7208
+ if (set === void 0) {
7209
+ set = /* @__PURE__ */ new Set();
7210
+ listeners.set(key, set);
7211
+ }
7212
+ set.add(listener);
7213
+ return () => {
7214
+ set.delete(listener);
7215
+ if (set.size === 0) listeners.delete(key);
7216
+ };
7217
+ }
7218
+ /**
7219
+ * Sets (or, with `null`, clears) this page's rating of one message and sends
7220
+ * it to the active Trajectory. Every change is a new event; the newest event
7221
+ * for a `feedbackId` is the current one. Like any Trajectory event, a change
7222
+ * made while no Trajectory captures, for example during a reconnect, is not
7223
+ * sent; the page still shows it.
7224
+ */
7225
+ function setMessageFeedback(params) {
7226
+ const { copilotkit, agentId, threadId, message } = params;
7227
+ const comment = normalizeComment(params.feedback?.comment);
7228
+ const feedback = params.feedback && {
7229
+ rating: params.feedback.rating,
7230
+ ...comment ? { comment } : {}
7231
+ };
7232
+ const key = messageKey(agentId, threadId, message.id);
7233
+ if (feedback) ratings.set(key, feedback);
7234
+ else ratings.delete(key);
7235
+ for (const listener of listeners.get(threadKey(agentId, threadId)) ?? []) listener();
7236
+ const runId = copilotkit.getRunIdForMessage(agentId, threadId, message.id);
7237
+ copilotkit.emitTrajectoryEvent(MESSAGE_FEEDBACK_EVENT_NAME, {
7238
+ feedbackId: `${threadId}:${message.id}`,
7239
+ threadId,
7240
+ messageId: message.id,
7241
+ agentId,
7242
+ ...runId ? { runId } : {},
7243
+ rating: feedback?.rating ?? null,
7244
+ ...feedback?.comment ? { comment: feedback.comment } : {},
7245
+ ...copilotkit.ɵlearningCapturesAgentText ? { response: clip(message.content ?? "", RESPONSE_EXCERPT_LENGTH) } : {}
7246
+ });
7247
+ }
7248
+
7249
+ //#endregion
7250
+ //#region src/v2/components/chat/message-feedback.tsx
7251
+ /**
7252
+ * Set by the message view while Learning is on for this chat. Assistant
7253
+ * messages without it show thumbs only when the app passes its own handlers
7254
+ * or thumb slots.
7255
+ */
7256
+ const MessageFeedbackScopeContext = (0, react.createContext)(null);
7257
+ const capturedCores = /* @__PURE__ */ new WeakSet();
7258
+ const rememberCapture = (copilotkit) => {
7259
+ if (capturedCores.has(copilotkit)) return;
7260
+ capturedCores.add(copilotkit);
7261
+ const unsubscribe = copilotkit.ɵsubscribeToLearningConfigured(() => {
7262
+ if (copilotkit.ɵlearningConfigured) return;
7263
+ capturedCores.delete(copilotkit);
7264
+ unsubscribe();
7265
+ });
7266
+ };
7267
+ /**
7268
+ * Whether the thumbs record feedback for Learning. True once a Trajectory of
7269
+ * this Core has started and as long as Learning stays configured, also while
7270
+ * capture reconnects or is stopped, so the buttons do not flicker. Ratings
7271
+ * given meanwhile still show but, like any Trajectory event, are not
7272
+ * sent. A capture that never starts (for example without a license) leaves it
7273
+ * false.
7274
+ */
7275
+ function useLearningFeedbackEnabled(copilotkit) {
7276
+ const configured = (0, react.useSyncExternalStore)((0, react.useCallback)((onChange) => copilotkit.ɵsubscribeToLearningConfigured(onChange), [copilotkit]), () => copilotkit.ɵlearningConfigured, () => false);
7277
+ const capturing = (0, react.useSyncExternalStore)((0, react.useCallback)((onChange) => copilotkit.subscribe({ onTrajectoryChanged: onChange }).unsubscribe, [copilotkit]), () => copilotkit.trajectoryId !== null, () => false);
7278
+ (0, react.useEffect)(() => {
7279
+ if (capturing) rememberCapture(copilotkit);
7280
+ }, [capturing, copilotkit]);
7281
+ return configured && (capturing || capturedCores.has(copilotkit));
7282
+ }
7283
+ const noSubscription = () => () => {};
7284
+ /** Feedback controls for `message`, or `null` when this chat collects none. */
7285
+ function useMessageFeedback(message) {
7286
+ const scope = (0, react.useContext)(MessageFeedbackScopeContext);
7287
+ const subscribe = (0, react.useCallback)((onChange) => scope ? subscribeToMessageFeedback(scope.agentId, scope.threadId, onChange) : noSubscription(), [scope]);
7288
+ const getSnapshot = () => scope ? getMessageFeedback(scope.agentId, scope.threadId, message.id) : void 0;
7289
+ const feedback = (0, react.useSyncExternalStore)(subscribe, getSnapshot, getSnapshot);
7290
+ if (!scope) return null;
7291
+ const save = (next) => setMessageFeedback({
7292
+ ...scope,
7293
+ message,
7294
+ feedback: next
7295
+ });
7296
+ return {
7297
+ feedback,
7298
+ rate: (rating) => {
7299
+ if (feedback?.rating !== rating) save({ rating });
7300
+ },
7301
+ clear: () => {
7302
+ if (feedback) save(null);
7303
+ },
7304
+ comment: (comment) => {
7305
+ if (feedback) save({
7306
+ rating: feedback.rating,
7307
+ comment
7308
+ });
7309
+ }
7310
+ };
7311
+ }
7312
+ /**
7313
+ * How long the pointer may be away from the rating and its comment before the
7314
+ * comment closes: enough to cross the gap between them, short enough that
7315
+ * passing over a rating does not leave it open.
7316
+ */
7317
+ const FEEDBACK_COMMENT_CLOSE_DELAY_MS = 150;
7318
+ /**
7319
+ * Drives the comment popover of a rated message.
7320
+ *
7321
+ * It opens when a rating is given and whenever the pointer or keyboard focus
7322
+ * reaches the chosen thumb, with the saved comment in the field. It closes by
7323
+ * itself shortly after the pointer leaves the thumb and the popover, unless
7324
+ * focus is in the field: then it stays until focus moves away, Enter, or
7325
+ * Escape. Closing saves a changed comment; Escape, a click on a thumb, and
7326
+ * removing the rating discard it.
7327
+ */
7328
+ function useFeedbackComment(controls) {
7329
+ const [open, setOpen] = (0, react.useState)(false);
7330
+ const [draft, setDraft] = (0, react.useState)("");
7331
+ const [focusRequest, setFocusRequest] = (0, react.useState)(0);
7332
+ const inputRef = (0, react.useRef)(null);
7333
+ const timer = (0, react.useRef)(void 0);
7334
+ const editing = (0, react.useRef)(false);
7335
+ const focusWaiting = (0, react.useRef)(false);
7336
+ const pointerOver = (0, react.useRef)(false);
7337
+ const returning = (0, react.useRef)(false);
7338
+ const latest = (0, react.useRef)({
7339
+ controls,
7340
+ draft,
7341
+ open
7342
+ });
7343
+ latest.current = {
7344
+ ...latest.current,
7345
+ controls,
7346
+ draft,
7347
+ open
7348
+ };
7349
+ const takeFocusRequest = (0, react.useCallback)(() => {
7350
+ const waiting = focusWaiting.current;
7351
+ focusWaiting.current = false;
7352
+ return waiting;
7353
+ }, []);
7354
+ const cancelClose = (0, react.useCallback)(() => {
7355
+ if (timer.current === void 0) return;
7356
+ clearTimeout(timer.current);
7357
+ timer.current = void 0;
7358
+ }, []);
7359
+ (0, react.useEffect)(() => cancelClose, [cancelClose]);
7360
+ const savedComment = (0, react.useCallback)(() => latest.current.controls?.feedback?.comment ?? "", []);
7361
+ (0, react.useEffect)(() => () => {
7362
+ const { open: wasOpen, controls: current, draft: text } = latest.current;
7363
+ if (wasOpen && current?.feedback && text.trim() !== savedComment()) current.comment(text);
7364
+ }, []);
7365
+ const close = (0, react.useCallback)(({ save = true } = {}) => {
7366
+ cancelClose();
7367
+ focusWaiting.current = false;
7368
+ pointerOver.current = false;
7369
+ if (!latest.current.open) return;
7370
+ const current = latest.current.controls;
7371
+ if (save && current?.feedback && latest.current.draft.trim() !== savedComment()) current.comment(latest.current.draft);
7372
+ editing.current = false;
7373
+ latest.current = {
7374
+ ...latest.current,
7375
+ open: false
7376
+ };
7377
+ setOpen(false);
7378
+ }, [cancelClose, savedComment]);
7379
+ const show = ({ comment, focus }) => {
7380
+ cancelClose();
7381
+ latest.current = {
7382
+ ...latest.current,
7383
+ open: true,
7384
+ draft: comment
7385
+ };
7386
+ setDraft(comment);
7387
+ if (focus) {
7388
+ focusWaiting.current = true;
7389
+ setFocusRequest((request) => request + 1);
7390
+ }
7391
+ setOpen(true);
7392
+ };
7393
+ return {
7394
+ open,
7395
+ draft,
7396
+ setDraft,
7397
+ focusRequest,
7398
+ takeFocusRequest,
7399
+ inputRef,
7400
+ show,
7401
+ enter: (via = "focus") => {
7402
+ cancelClose();
7403
+ if (via === "pointer") pointerOver.current = true;
7404
+ if (returning.current) return;
7405
+ if (!latest.current.open && latest.current.controls?.feedback) show({
7406
+ comment: savedComment(),
7407
+ focus: false
7408
+ });
7409
+ },
7410
+ leave: (via = "focus") => {
7411
+ if (via === "pointer") pointerOver.current = false;
7412
+ else if (pointerOver.current) return;
7413
+ cancelClose();
7414
+ timer.current = setTimeout(() => {
7415
+ timer.current = void 0;
7416
+ if (!editing.current) close();
7417
+ }, FEEDBACK_COMMENT_CLOSE_DELAY_MS);
7418
+ },
7419
+ setEditing: (value) => {
7420
+ editing.current = value;
7421
+ },
7422
+ close,
7423
+ returnFocus: (target) => {
7424
+ returning.current = true;
7425
+ try {
7426
+ target?.focus();
7427
+ } finally {
7428
+ returning.current = false;
7429
+ }
7430
+ }
7431
+ };
7432
+ }
7433
+
7434
+ //#endregion
7435
+ //#region src/v2/components/chat/CopilotChatAssistantMessage.tsx
7436
+ /**
7437
+ * In the `"turn"` tool-call display, text in a reply's working phase was
7438
+ * narration: it fades to muted once a call follows it, and folds away with the
7439
+ * rest of the work when the reply is done. The wrapper is there for every
7440
+ * message in that display, so text that turns into narration keeps its node
7441
+ * (and its place) rather than remounting.
7442
+ */
7443
+ function WorkText({ messageId, children }) {
7444
+ const context = (0, react.useContext)(ToolCallDisplayContext);
7445
+ if (context?.display !== "turn") return children;
7446
+ const open = context.workTextOpen.get(messageId);
7447
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Collapsible, {
7448
+ open: open ?? true,
7449
+ className: "cpk:-mx-2",
7450
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
7451
+ className: "cpk:px-2",
7452
+ "data-work-text": open === void 0 ? "reply" : "narration",
7453
+ children
7454
+ })
7455
+ });
7456
+ }
7457
+ /**
7458
+ * Binds the Inspector action to the active chat without making the full
7459
+ * assistant message subscribe to chat configuration changes.
7460
+ */
7461
+ function BoundInspectorButton({ inspectorButton, messageId, openInspector, ...props }) {
7462
+ const chatConfiguration = useCopilotChatConfiguration();
7463
+ return renderSlot(inspectorButton, CopilotChatAssistantMessage.InspectorButton, {
7464
+ ...props,
7465
+ onClick: () => openInspector({
7466
+ messageId,
7467
+ threadId: chatConfiguration?.threadId,
7468
+ agentId: chatConfiguration?.agentId
7469
+ })
7470
+ });
7471
+ }
7472
+ /** Whether focus came from the keyboard; a pointer click also focuses buttons. */
7473
+ function isFocusVisible(element) {
7474
+ try {
7475
+ return element.matches(":focus-visible");
7476
+ } catch {
7477
+ return false;
7478
+ }
7479
+ }
7480
+ const HIDDEN_TOO = {
7481
+ checkVisibilityCSS: true,
7482
+ visibilityProperty: true
7483
+ };
7484
+ const FOCUSABLE = [
7485
+ "a[href]",
7486
+ "button:not([disabled])",
7487
+ "input:not([disabled])",
7488
+ "select:not([disabled])",
7489
+ "textarea:not([disabled])",
7490
+ "[tabindex]"
7491
+ ].map((selector) => `${selector}:not([tabindex="-1"]):not([data-radix-focus-guard])`).join(", ");
7492
+ /** Approximately the element Tab reaches after `element`, outside `skip`. */
7493
+ function nextFocusable(element, skip) {
7494
+ const all = Array.from(element.ownerDocument.querySelectorAll(FOCUSABLE)).filter((candidate) => candidate === element || !skip?.contains(candidate) && !candidate.closest("[hidden], [inert]"));
7495
+ const index = all.indexOf(element);
7496
+ return index === -1 ? null : all[index + 1] ?? null;
7497
+ }
7498
+ /**
7499
+ * The comment popover of a rated message, pointing at the chosen thumb. Kept
7500
+ * apart so only it, not the whole message, follows chat configuration.
7501
+ *
7502
+ * The popover lives in a portal, so it keeps keyboard order by hand: Shift+Tab
7503
+ * from its start returns to the thumb, Tab from its end moves on to the
7504
+ * control after the thumb, and Enter or Escape inside it returns to the thumb.
7505
+ */
7506
+ function FeedbackCommentPopover({ feedbackComment, comment, savedComment, rating, anchor, messageId }) {
7507
+ const labels = useCopilotChatConfiguration()?.labels ?? CopilotChatDefaultLabels;
7508
+ const contentRef = (0, react.useRef)(null);
7509
+ const focusInside = () => contentRef.current?.contains(document.activeElement) ?? false;
7510
+ const { focusRequest, takeFocusRequest, inputRef } = comment;
7511
+ const focusField = (0, react.useCallback)(() => {
7512
+ if (inputRef.current && takeFocusRequest()) inputRef.current.focus();
7513
+ }, [inputRef, takeFocusRequest]);
7514
+ (0, react.useEffect)(() => focusField(), [focusRequest, focusField]);
7515
+ /** Whether `target` is a thumb of this message, whose click decides itself. */
7516
+ const isOwnThumb = (target) => {
7517
+ return (target instanceof Element ? target.closest("[data-copilotkit-rating]") : null)?.closest("[data-message-id]")?.getAttribute("data-message-id") === messageId;
7518
+ };
7519
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(Popover, {
7520
+ open: comment.open,
7521
+ onOpenChange: (open) => {
7522
+ if (!open) comment.close();
7523
+ },
7524
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(PopoverAnchor, { virtualRef: anchor }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PopoverContent, {
7525
+ ref: contentRef,
7526
+ side: "bottom",
7527
+ collisionPadding: 8,
7528
+ hideWhenDetached: true,
7529
+ "aria-label": labels.assistantMessageFeedbackCommentPlaceholder,
7530
+ "data-copilotkit-ignore": true,
7531
+ onOpenAutoFocus: (event) => {
7532
+ event.preventDefault();
7533
+ focusField();
7534
+ },
7535
+ onCloseAutoFocus: (event) => event.preventDefault(),
7536
+ onInteractOutside: (event) => {
7537
+ if (isOwnThumb(event.target)) event.preventDefault();
7538
+ },
7539
+ onEscapeKeyDown: (event) => {
7540
+ const returnFocus = focusInside();
7541
+ if (returnFocus) event.stopPropagation();
7542
+ comment.close({ save: false });
7543
+ if (returnFocus) comment.returnFocus(anchor.current);
7544
+ },
7545
+ onKeyDown: (event) => {
7546
+ if (event.key !== "Tab") return;
7547
+ const focusables = Array.from(contentRef.current?.querySelectorAll(FOCUSABLE) ?? []);
7548
+ if (!(event.shiftKey ? document.activeElement === focusables[0] : document.activeElement === focusables[focusables.length - 1]) || !anchor.current?.isConnected) return;
7549
+ event.preventDefault();
7550
+ const target = event.shiftKey ? anchor.current : nextFocusable(anchor.current, contentRef.current) ?? anchor.current;
7551
+ comment.close();
7552
+ if (target === anchor.current) comment.returnFocus(target);
7553
+ else target.focus();
7554
+ },
7555
+ onPointerEnter: () => comment.enter("pointer"),
7556
+ onPointerLeave: () => comment.leave("pointer"),
7557
+ className: "cpk:z-[1300]",
7558
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react.Fragment, { children: renderSlot(feedbackComment, CopilotChatAssistantMessage.FeedbackComment, {
7559
+ value: comment.draft,
7560
+ savedComment,
7561
+ inputRef: comment.inputRef,
7562
+ onChange: comment.setDraft,
7563
+ onSubmit: () => {
7564
+ comment.close();
7565
+ comment.returnFocus(anchor.current);
7566
+ },
7567
+ onEditingChange: comment.setEditing,
7568
+ onLeave: (next) => {
7569
+ if (next && (contentRef.current?.contains(next) || isOwnThumb(next))) return;
7570
+ comment.leave();
7571
+ }
7572
+ }) }, rating)
7573
+ })]
7574
+ });
7575
+ }
7576
+ function CopilotChatAssistantMessage({ message, messages, isRunning, isLatest, onThumbsUp, onThumbsDown, onReadAloud, onRegenerate, additionalToolbarItems, toolbarVisible = true, markdownRenderer, toolbar, copyButton, inspectorButton, thumbsUpButton, thumbsDownButton, readAloudButton, regenerateButton, feedbackComment: feedbackCommentSlot, toolCallsView, children, className, ...props }) {
7577
+ useKatexStyles();
7578
+ const { isInspectorEnabled, openInspector } = useCopilotKitInspector();
7579
+ const shortcutsHidden = useInspectorShortcutsHidden();
7580
+ const showInspectorShortcut = isInspectorEnabled && !shortcutsHidden;
7581
+ const feedback = useMessageFeedback(message);
7582
+ const comment = useFeedbackComment(feedback);
7583
+ const rating = feedback?.feedback?.rating;
7584
+ const root = (0, react.useRef)(null);
7585
+ const commentAnchor = (0, react.useRef)(null);
7586
+ const commentRating = (0, react.useRef)(rating);
7587
+ (0, react.useLayoutEffect)(() => {
7588
+ if (comment.open && !commentAnchor.current?.isConnected && rating) commentAnchor.current = root.current?.querySelector(`[data-copilotkit-rating="${rating}"]`) ?? root.current?.querySelector("button[aria-pressed=\"true\"]") ?? null;
7589
+ });
7590
+ /**
7591
+ * Thumbs are toggles: a new rating is saved and opens its comment, and
7592
+ * clicking the chosen thumb again removes the rating. A keyboard click
7593
+ * (`detail` 0) moves focus into the comment, as there is no pointer.
7594
+ * Custom thumb slots may call `onClick` without an event.
7595
+ *
7596
+ * @returns Whether the app's handler should run: true for a new rating and
7597
+ * whenever this chat does not record ratings, false when one is removed.
7598
+ */
7599
+ const rate = (value, event) => {
7600
+ if (!feedback) return true;
7601
+ if (rating === value) {
7602
+ comment.close({ save: false });
7603
+ feedback.clear();
7604
+ return false;
7605
+ }
7606
+ feedback.rate(value);
7607
+ commentRating.current = value;
7608
+ commentAnchor.current = event?.currentTarget ?? null;
7609
+ comment.show({
7610
+ comment: "",
7611
+ focus: event?.detail === 0
7612
+ });
7613
+ return true;
7614
+ };
7615
+ /** Props that let the chosen thumb open and point at its comment. */
7616
+ const thumbFeedbackProps = (value) => {
7617
+ if (!feedback) return {};
7618
+ const thumb = { "data-copilotkit-rating": value };
7619
+ if (rating !== value) return {
7620
+ ...thumb,
7621
+ "aria-pressed": false
7622
+ };
7623
+ const enter = (target, via) => {
7624
+ if (!comment.open) commentRating.current = value;
7625
+ commentAnchor.current = target;
7626
+ comment.enter(via);
7627
+ };
7628
+ return {
7629
+ ...thumb,
7630
+ "aria-pressed": true,
7631
+ "aria-haspopup": "dialog",
7632
+ "aria-expanded": comment.open,
7633
+ onPointerEnter: (event) => {
7634
+ if (event.pointerType !== "touch") enter(event.currentTarget, "pointer");
7635
+ },
7636
+ onPointerLeave: (event) => {
7637
+ if (event.pointerType !== "touch") comment.leave("pointer");
7638
+ },
7639
+ onFocus: (event) => {
7640
+ if (isFocusVisible(event.currentTarget)) enter(event.currentTarget, "focus");
7641
+ },
7642
+ onBlur: () => comment.leave(),
7643
+ onKeyDown: (event) => {
7644
+ const input = comment.inputRef.current;
7645
+ if (event.key === "Tab" && !event.shiftKey && comment.open && input && input.checkVisibility?.(HIDDEN_TOO) !== false) {
7646
+ event.preventDefault();
7647
+ input.focus();
7648
+ }
7649
+ }
7650
+ };
7651
+ };
7652
+ const boundMarkdownRenderer = renderSlot(markdownRenderer, CopilotChatAssistantMessage.MarkdownRenderer, { content: message.content || "" });
7653
+ const boundCopyButton = renderSlot(copyButton, CopilotChatAssistantMessage.CopyButton, { onClick: async () => {
7654
+ if (message.content) return await (0, _copilotkit_shared.copyToClipboard)(message.content);
7655
+ return false;
6455
7656
  } });
6456
- const boundThumbsUpButton = renderSlot(thumbsUpButton, CopilotChatAssistantMessage.ThumbsUpButton, { onClick: onThumbsUp ? () => onThumbsUp(message) : void 0 });
7657
+ const boundThumbsUpButton = renderSlot(thumbsUpButton, CopilotChatAssistantMessage.ThumbsUpButton, {
7658
+ onClick: onThumbsUp || feedback ? (event) => {
7659
+ if (rate("up", event)) onThumbsUp?.(message);
7660
+ } : void 0,
7661
+ ...thumbFeedbackProps("up")
7662
+ });
6457
7663
  const boundInspectorButton = showInspectorShortcut ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(BoundInspectorButton, {
6458
7664
  inspectorButton,
6459
7665
  messageId: message.id,
6460
7666
  openInspector
6461
7667
  }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_jsx_runtime.Fragment, {});
6462
- const boundThumbsDownButton = renderSlot(thumbsDownButton, CopilotChatAssistantMessage.ThumbsDownButton, { onClick: onThumbsDown ? () => onThumbsDown(message) : void 0 });
7668
+ const boundThumbsDownButton = renderSlot(thumbsDownButton, CopilotChatAssistantMessage.ThumbsDownButton, {
7669
+ onClick: onThumbsDown || feedback ? (event) => {
7670
+ if (rate("down", event)) onThumbsDown?.(message);
7671
+ } : void 0,
7672
+ ...thumbFeedbackProps("down")
7673
+ });
6463
7674
  const boundReadAloudButton = renderSlot(readAloudButton, CopilotChatAssistantMessage.ReadAloudButton, { onClick: onReadAloud ? () => onReadAloud(message) : void 0 });
6464
7675
  const boundRegenerateButton = renderSlot(regenerateButton, CopilotChatAssistantMessage.RegenerateButton, { onClick: onRegenerate ? () => onRegenerate(message) : void 0 });
6465
7676
  const boundToolbar = renderSlot(toolbar, CopilotChatAssistantMessage.Toolbar, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
6466
7677
  className: "cpk:flex cpk:w-full cpk:items-center cpk:gap-1",
6467
7678
  children: [
6468
7679
  boundCopyButton,
6469
- (onThumbsUp || thumbsUpButton) && boundThumbsUpButton,
6470
- (onThumbsDown || thumbsDownButton) && boundThumbsDownButton,
7680
+ (onThumbsUp || thumbsUpButton || feedback) && boundThumbsUpButton,
7681
+ (onThumbsDown || thumbsDownButton || feedback) && boundThumbsDownButton,
6471
7682
  (onReadAloud || readAloudButton) && boundReadAloudButton,
6472
7683
  (onRegenerate || regenerateButton) && boundRegenerateButton,
6473
7684
  additionalToolbarItems,
@@ -6481,11 +7692,31 @@ _radix_ui_react_dropdown_menu = __toESM(_radix_ui_react_dropdown_menu);
6481
7692
  const hasContent = !!(message.content && message.content.trim().length > 0);
6482
7693
  const isLatestAssistantMessage = message.role === "assistant" && (isLatest ?? messages?.[messages.length - 1]?.id === message.id);
6483
7694
  const shouldShowToolbar = toolbarVisible && (hasContent || showInspectorShortcut) && !(isRunning && isLatestAssistantMessage);
6484
- if (children) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
7695
+ const { open: commentOpen, close: closeComment } = comment;
7696
+ (0, react.useEffect)(() => {
7697
+ if (!commentOpen) return;
7698
+ if (rating !== commentRating.current) closeComment({ save: false });
7699
+ else if (!shouldShowToolbar) closeComment();
7700
+ }, [
7701
+ commentOpen,
7702
+ shouldShowToolbar,
7703
+ rating,
7704
+ closeComment
7705
+ ]);
7706
+ const feedbackComment = feedback?.feedback && shouldShowToolbar && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(FeedbackCommentPopover, {
7707
+ feedbackComment: feedbackCommentSlot,
7708
+ comment,
7709
+ savedComment: feedback.feedback.comment ?? "",
7710
+ rating: feedback.feedback.rating,
7711
+ anchor: commentAnchor,
7712
+ messageId: message.id
7713
+ });
7714
+ if (children) return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
7715
+ ref: root,
6485
7716
  "data-copilotkit": true,
6486
7717
  "data-message-id": message.id,
6487
7718
  style: { display: "contents" },
6488
- children: children({
7719
+ children: [children({
6489
7720
  markdownRenderer: boundMarkdownRenderer,
6490
7721
  toolbar: boundToolbar,
6491
7722
  toolCallsView: boundToolCallsView,
@@ -6504,21 +7735,26 @@ _radix_ui_react_dropdown_menu = __toESM(_radix_ui_react_dropdown_menu);
6504
7735
  onRegenerate,
6505
7736
  additionalToolbarItems,
6506
7737
  toolbarVisible: shouldShowToolbar
6507
- })
7738
+ }), feedbackComment]
6508
7739
  });
6509
7740
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
7741
+ ref: root,
6510
7742
  "data-copilotkit": true,
6511
7743
  "data-testid": "copilot-assistant-message",
6512
7744
  className: (0, tailwind_merge.twMerge)("copilotKitMessage copilotKitAssistantMessage", className),
6513
7745
  ...props,
6514
7746
  "data-message-id": message.id,
6515
7747
  children: [
6516
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
6517
- className: "cpk:prose cpk:max-w-full cpk:break-words cpk:dark:prose-invert",
6518
- children: boundMarkdownRenderer
7748
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(WorkText, {
7749
+ messageId: message.id,
7750
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
7751
+ className: "cpk:prose cpk:max-w-full cpk:break-words cpk:dark:prose-invert",
7752
+ children: boundMarkdownRenderer
7753
+ })
6519
7754
  }),
6520
7755
  boundToolCallsView,
6521
- shouldShowToolbar && boundToolbar
7756
+ shouldShowToolbar && boundToolbar,
7757
+ feedbackComment
6522
7758
  ]
6523
7759
  });
6524
7760
  }
@@ -6534,20 +7770,27 @@ _radix_ui_react_dropdown_menu = __toESM(_radix_ui_react_dropdown_menu);
6534
7770
  ...props
6535
7771
  });
6536
7772
  const ToolbarButton = _CopilotChatAssistantMessage.ToolbarButton = ({ title, tooltip, tooltipClassName, children, ...props }) => {
6537
- return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(Tooltip, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(TooltipTrigger, {
6538
- asChild: true,
6539
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Button, {
6540
- type: "button",
6541
- variant: "assistantMessageToolbarButton",
6542
- "aria-label": title,
6543
- ...props,
6544
- children
6545
- })
6546
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(TooltipContent, {
6547
- side: "bottom",
6548
- className: tooltipClassName,
6549
- children: tooltip ?? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: title })
6550
- })] });
7773
+ const [tooltipOpen, setTooltipOpen] = (0, react.useState)(false);
7774
+ const popupOpen = props["aria-expanded"] === true;
7775
+ if (popupOpen && tooltipOpen) setTooltipOpen(false);
7776
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(Tooltip, {
7777
+ open: tooltipOpen && !popupOpen,
7778
+ onOpenChange: (open) => setTooltipOpen(open && !popupOpen),
7779
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(TooltipTrigger, {
7780
+ asChild: true,
7781
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Button, {
7782
+ type: "button",
7783
+ variant: "assistantMessageToolbarButton",
7784
+ "aria-label": title,
7785
+ ...props,
7786
+ children
7787
+ })
7788
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(TooltipContent, {
7789
+ side: "bottom",
7790
+ className: tooltipClassName,
7791
+ children: tooltip ?? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: title })
7792
+ })]
7793
+ });
6551
7794
  };
6552
7795
  _CopilotChatAssistantMessage.CopyButton = ({ className, title, onClick, ...props }) => {
6553
7796
  const labels = useCopilotChatConfiguration()?.labels ?? CopilotChatDefaultLabels;
@@ -6586,7 +7829,10 @@ _radix_ui_react_dropdown_menu = __toESM(_radix_ui_react_dropdown_menu);
6586
7829
  "data-testid": "copilot-thumbs-up-button",
6587
7830
  title: title || labels.assistantMessageToolbarThumbsUpLabel,
6588
7831
  ...props,
6589
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.ThumbsUp, { className: "cpk:size-[18px]" })
7832
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.ThumbsUp, {
7833
+ className: "cpk:size-[18px]",
7834
+ fill: props["aria-pressed"] === true ? "currentColor" : "none"
7835
+ })
6590
7836
  });
6591
7837
  };
6592
7838
  _CopilotChatAssistantMessage.ThumbsDownButton = ({ title, ...props }) => {
@@ -6595,7 +7841,45 @@ _radix_ui_react_dropdown_menu = __toESM(_radix_ui_react_dropdown_menu);
6595
7841
  "data-testid": "copilot-thumbs-down-button",
6596
7842
  title: title || labels.assistantMessageToolbarThumbsDownLabel,
6597
7843
  ...props,
6598
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.ThumbsDown, { className: "cpk:size-[18px]" })
7844
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.ThumbsDown, {
7845
+ className: "cpk:size-[18px]",
7846
+ fill: props["aria-pressed"] === true ? "currentColor" : "none"
7847
+ })
7848
+ });
7849
+ };
7850
+ _CopilotChatAssistantMessage.FeedbackComment = ({ value, savedComment, inputRef, onChange, onSubmit, onEditingChange, onLeave, className }) => {
7851
+ const labels = useCopilotChatConfiguration()?.labels ?? CopilotChatDefaultLabels;
7852
+ const changed = value.trim() !== savedComment;
7853
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("form", {
7854
+ "data-testid": "copilot-feedback-comment",
7855
+ className: (0, tailwind_merge.twMerge)("cpk:flex cpk:w-80 cpk:max-w-[calc(100vw-1rem)] cpk:items-center cpk:gap-1 cpk:rounded-full cpk:border cpk:border-border cpk:bg-background cpk:py-0.5 cpk:pl-3 cpk:pr-0.5 cpk:text-sm cpk:shadow-md", className),
7856
+ onSubmit: (event) => {
7857
+ event.preventDefault();
7858
+ onSubmit();
7859
+ },
7860
+ onFocus: () => onEditingChange?.(true),
7861
+ onBlur: (event) => {
7862
+ const next = event.relatedTarget instanceof Element ? event.relatedTarget : null;
7863
+ if (event.currentTarget.contains(next)) return;
7864
+ if (typeof document !== "undefined" && !document.hasFocus()) return;
7865
+ onEditingChange?.(false);
7866
+ onLeave?.(next);
7867
+ },
7868
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
7869
+ ref: inputRef,
7870
+ value,
7871
+ maxLength: MESSAGE_FEEDBACK_MAX_COMMENT_LENGTH,
7872
+ onChange: (event) => onChange(event.target.value),
7873
+ placeholder: labels.assistantMessageFeedbackCommentPlaceholder,
7874
+ "aria-label": labels.assistantMessageFeedbackCommentPlaceholder,
7875
+ className: "cpk:min-w-0 cpk:flex-1 cpk:bg-transparent cpk:py-1 cpk:text-foreground cpk:outline-none cpk:placeholder:text-muted-foreground"
7876
+ }), changed && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Button, {
7877
+ type: "submit",
7878
+ variant: "assistantMessageToolbarButton",
7879
+ "aria-label": labels.assistantMessageFeedbackCommentSubmitLabel,
7880
+ title: labels.assistantMessageFeedbackCommentSubmitLabel,
7881
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.ArrowUp, { className: "cpk:size-[16px]" })
7882
+ })]
6599
7883
  });
6600
7884
  };
6601
7885
  _CopilotChatAssistantMessage.ReadAloudButton = ({ title, ...props }) => {
@@ -6623,6 +7907,7 @@ _radix_ui_react_dropdown_menu = __toESM(_radix_ui_react_dropdown_menu);
6623
7907
  CopilotChatAssistantMessage.InspectorButton.displayName = "CopilotChatAssistantMessage.InspectorButton";
6624
7908
  CopilotChatAssistantMessage.ThumbsUpButton.displayName = "CopilotChatAssistantMessage.ThumbsUpButton";
6625
7909
  CopilotChatAssistantMessage.ThumbsDownButton.displayName = "CopilotChatAssistantMessage.ThumbsDownButton";
7910
+ CopilotChatAssistantMessage.FeedbackComment.displayName = "CopilotChatAssistantMessage.FeedbackComment";
6626
7911
  CopilotChatAssistantMessage.ReadAloudButton.displayName = "CopilotChatAssistantMessage.ReadAloudButton";
6627
7912
  CopilotChatAssistantMessage.RegenerateButton.displayName = "CopilotChatAssistantMessage.RegenerateButton";
6628
7913
  var CopilotChatAssistantMessage_default = CopilotChatAssistantMessage;
@@ -6928,6 +8213,7 @@ _radix_ui_react_dropdown_menu = __toESM(_radix_ui_react_dropdown_menu);
6928
8213
  });
6929
8214
  };
6930
8215
  _CopilotChatUserMessage.BranchNavigation = ({ className, currentBranch = 0, numberOfBranches = 1, onSwitchToBranch, message, ...props }) => {
8216
+ const labels = useCopilotChatConfiguration()?.labels ?? CopilotChatDefaultLabels;
6931
8217
  if (!numberOfBranches || numberOfBranches <= 1 || !onSwitchToBranch) return null;
6932
8218
  const canGoPrev = currentBranch > 0;
6933
8219
  const canGoNext = currentBranch < numberOfBranches - 1;
@@ -6946,6 +8232,7 @@ _radix_ui_react_dropdown_menu = __toESM(_radix_ui_react_dropdown_menu);
6946
8232
  }),
6947
8233
  disabled: !canGoPrev,
6948
8234
  className: "cpk:h-6 cpk:w-6 cpk:p-0",
8235
+ "aria-label": labels.userMessageToolbarPreviousBranchLabel,
6949
8236
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.ChevronLeft, { className: "cpk:size-[20px]" })
6950
8237
  }),
6951
8238
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
@@ -6966,6 +8253,7 @@ _radix_ui_react_dropdown_menu = __toESM(_radix_ui_react_dropdown_menu);
6966
8253
  }),
6967
8254
  disabled: !canGoNext,
6968
8255
  className: "cpk:h-6 cpk:w-6 cpk:p-0",
8256
+ "aria-label": labels.userMessageToolbarNextBranchLabel,
6969
8257
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.ChevronRight, { className: "cpk:size-[20px]" })
6970
8258
  })
6971
8259
  ]
@@ -6986,7 +8274,7 @@ _radix_ui_react_dropdown_menu = __toESM(_radix_ui_react_dropdown_menu);
6986
8274
  /**
6987
8275
  * Formats an elapsed duration (in seconds) to a human-readable string.
6988
8276
  */
6989
- function formatDuration(seconds) {
8277
+ function formatDuration$1(seconds) {
6990
8278
  if (seconds < 1) return "a few seconds";
6991
8279
  if (seconds < 60) return `${Math.round(seconds)} seconds`;
6992
8280
  const mins = Math.floor(seconds / 60);
@@ -7024,7 +8312,7 @@ _radix_ui_react_dropdown_menu = __toESM(_radix_ui_react_dropdown_menu);
7024
8312
  userToggledRef.current = true;
7025
8313
  setIsOpen((prev) => !prev);
7026
8314
  } : void 0;
7027
- const label = isStreaming ? "Thinking…" : `Thought for ${formatDuration(elapsed)}`;
8315
+ const label = isStreaming ? "Thinking…" : `Thought for ${formatDuration$1(elapsed)}`;
7028
8316
  const boundHeader = renderSlot(header, CopilotChatReasoningMessage.Header, {
7029
8317
  isOpen,
7030
8318
  label,
@@ -7246,6 +8534,71 @@ _radix_ui_react_dropdown_menu = __toESM(_radix_ui_react_dropdown_menu);
7246
8534
  */
7247
8535
  const ScrollPinnedContext = react.default.createContext(false);
7248
8536
 
8537
+ //#endregion
8538
+ //#region src/v2/components/chat/CopilotChatWorkSummary.tsx
8539
+ /** "45s", "1m 5s", "2m". Used for the live timer and the final duration. */
8540
+ function formatDuration(ms) {
8541
+ const seconds = Math.max(0, Math.floor(ms / 1e3));
8542
+ if (seconds < 60) return `${seconds}s`;
8543
+ const minutes = Math.floor(seconds / 60);
8544
+ const rest = seconds % 60;
8545
+ return rest ? `${minutes}m ${rest}s` : `${minutes}m`;
8546
+ }
8547
+ /** Re-renders every second while `active`, returning the current time. */
8548
+ function useNow(active) {
8549
+ const [now, setNow] = (0, react.useState)(() => Date.now());
8550
+ (0, react.useEffect)(() => {
8551
+ if (!active) return;
8552
+ setNow(Date.now());
8553
+ const timer = setInterval(() => setNow(Date.now()), 1e3);
8554
+ return () => clearInterval(timer);
8555
+ }, [active]);
8556
+ return now;
8557
+ }
8558
+ /**
8559
+ * The line at the top of a reply in the `"turn"` tool-call display. While the
8560
+ * agent works it reads "Working 4s"; once the reply is done it reads "Worked
8561
+ * for 9s · 7 tools" and toggles the work it folded away.
8562
+ */
8563
+ function CopilotChatWorkSummary({ isWorking, isOpen, calls, elapsedMs, workingSince, className, ...props }) {
8564
+ const now = useNow(isWorking);
8565
+ const failed = calls.filter((call) => call.toolMessage?.error).length;
8566
+ const liveMs = elapsedMs !== void 0 && workingSince !== void 0 ? elapsedMs + Math.max(0, now - workingSince) : void 0;
8567
+ const duration = elapsedMs !== void 0 ? formatDuration(Math.max(1e3, elapsedMs)) : void 0;
8568
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
8569
+ type: "button",
8570
+ "data-copilotkit": true,
8571
+ "data-testid": "copilot-work-summary",
8572
+ "data-state": isWorking ? "working" : "done",
8573
+ "aria-expanded": isOpen,
8574
+ className: (0, tailwind_merge.twMerge)("cpk:m-0 cpk:inline-flex cpk:max-w-full cpk:cursor-pointer cpk:items-center cpk:gap-1.5 cpk:rounded-md cpk:border-none cpk:bg-transparent cpk:p-0 cpk:py-0.5 cpk:text-sm cpk:text-muted-foreground cpk:transition-colors cpk:hover:text-foreground cpk:focus-visible:outline-2 cpk:focus-visible:outline-ring", className),
8575
+ ...props,
8576
+ children: [isWorking ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
8577
+ className: "cpk:font-medium cpk-shimmer",
8578
+ children: "Working"
8579
+ }), liveMs !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
8580
+ className: "cpk:text-xs cpk:tabular-nums cpk:opacity-70",
8581
+ children: formatDuration(liveMs)
8582
+ })] }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
8583
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
8584
+ className: "cpk:font-medium",
8585
+ children: duration ? `Worked for ${duration}` : `Used ${pluralize(calls.length, "tool")}`
8586
+ }),
8587
+ duration && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
8588
+ className: "cpk:text-xs cpk:opacity-70",
8589
+ children: pluralize(calls.length, "tool")
8590
+ }),
8591
+ failed > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
8592
+ className: "cpk:text-destructive",
8593
+ children: [failed, " failed"]
8594
+ })
8595
+ ] }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.ChevronRight, {
8596
+ "aria-hidden": "true",
8597
+ className: (0, tailwind_merge.twMerge)("cpk:size-3.5 cpk:shrink-0 cpk:transition-transform cpk:duration-200", isOpen && "cpk:rotate-90")
8598
+ })]
8599
+ });
8600
+ }
8601
+
7249
8602
  //#endregion
7250
8603
  //#region src/v2/components/intelligence-indicator/IntelligenceIndicatorView.tsx
7251
8604
  /**
@@ -7732,7 +9085,7 @@ _radix_ui_react_dropdown_menu = __toESM(_radix_ui_react_dropdown_menu);
7732
9085
  }
7733
9086
  const GROUP_ROW_KEY_PREFIX = "copilotkit-group:";
7734
9087
  const VIRTUALIZE_THRESHOLD = 50;
7735
- function CopilotChatMessageView({ messages = [], assistantMessage, userMessage, reasoningMessage, cursor, intelligenceIndicator, isRunning = false, transformMessages, groupMessages, children, className, ...props }) {
9088
+ function CopilotChatMessageView({ messages = [], assistantMessage, userMessage, reasoningMessage, cursor, intelligenceIndicator, workSummary, isRunning = false, transformMessages, groupMessages, messageFeedback = true, toolCallDisplay = "grouped", children, className, ...props }) {
7736
9089
  const isPinnedToBottom = (0, react.useContext)(ScrollPinnedContext);
7737
9090
  const renderCustomMessage = useRenderCustomMessages();
7738
9091
  const { renderActivityMessage } = useRenderActivityMessage();
@@ -7773,6 +9126,100 @@ _radix_ui_react_dropdown_menu = __toESM(_radix_ui_react_dropdown_menu);
7773
9126
  transformedMessages
7774
9127
  ]);
7775
9128
  const latestRenderedId = renderedMessages[renderedMessages.length - 1]?.id;
9129
+ const rendererNames = useToolCallRendererNames();
9130
+ const toolCallLayouts = (0, react.useMemo)(() => toolCallDisplay === "separate" ? /* @__PURE__ */ new Map() : getToolCallLayouts(renderedMessages, messages, rendererNames), [
9131
+ toolCallDisplay,
9132
+ renderedMessages,
9133
+ messages,
9134
+ rendererNames
9135
+ ]);
9136
+ const turnWork = (0, react.useMemo)(() => toolCallDisplay === "turn" ? getTurnWork(renderedMessages, toolCallLayouts) : void 0, [
9137
+ toolCallDisplay,
9138
+ renderedMessages,
9139
+ toolCallLayouts
9140
+ ]);
9141
+ const turnByFirstMessage = (0, react.useMemo)(() => {
9142
+ const byFirst = /* @__PURE__ */ new Map();
9143
+ for (const turn of turnWork?.values() ?? []) byFirst.set(turn.firstMessageId, turn);
9144
+ return byFirst;
9145
+ }, [turnWork]);
9146
+ let latestTurnKey = "start";
9147
+ for (let i = renderedMessages.length - 1; i >= 0; i--) if (renderedMessages[i].role === "user") {
9148
+ latestTurnKey = renderedMessages[i].id;
9149
+ break;
9150
+ }
9151
+ const isWorkingOn = (turnKey) => isRunning && turnKey === latestTurnKey;
9152
+ const [toggledTurns, setToggledTurns] = (0, react.useState)(() => /* @__PURE__ */ new Map());
9153
+ const isWorkOpen = (turnKey) => toggledTurns.get(turnKey) ?? isWorkingOn(turnKey);
9154
+ const workTimesRef = (0, react.useRef)(/* @__PURE__ */ new Map());
9155
+ if (toolCallDisplay === "turn") {
9156
+ const now = Date.now();
9157
+ const workTimes = workTimesRef.current;
9158
+ if (isRunning) {
9159
+ const times = workTimes.get(latestTurnKey);
9160
+ if (times) times.workingSince ?? (times.workingSince = now);
9161
+ else if (renderedMessages[renderedMessages.length - 1]?.role === "user") workTimes.set(latestTurnKey, {
9162
+ elapsedMs: 0,
9163
+ workingSince: now
9164
+ });
9165
+ } else for (const times of workTimes.values()) {
9166
+ if (times.workingSince === void 0) continue;
9167
+ times.elapsedMs += now - times.workingSince;
9168
+ times.workingSince = void 0;
9169
+ }
9170
+ }
9171
+ const { workOpen, workTextOpen } = (0, react.useMemo)(() => {
9172
+ const open = /* @__PURE__ */ new Map();
9173
+ const textOpen = /* @__PURE__ */ new Map();
9174
+ for (const turn of turnWork?.values() ?? []) {
9175
+ const turnOpen = toggledTurns.get(turn.key) ?? (isRunning && turn.key === latestTurnKey);
9176
+ for (const id of turn.workMessageIds) {
9177
+ open.set(id, turnOpen);
9178
+ if (id !== turn.replyTextMessageId) textOpen.set(id, turnOpen);
9179
+ }
9180
+ }
9181
+ return {
9182
+ workOpen: open,
9183
+ workTextOpen: textOpen
9184
+ };
9185
+ }, [
9186
+ turnWork,
9187
+ toggledTurns,
9188
+ isRunning,
9189
+ latestTurnKey
9190
+ ]);
9191
+ const toolCallDisplayContext = (0, react.useMemo)(() => ({
9192
+ display: toolCallDisplay,
9193
+ isRunning,
9194
+ layouts: toolCallLayouts,
9195
+ workOpen,
9196
+ workTextOpen
9197
+ }), [
9198
+ toolCallDisplay,
9199
+ isRunning,
9200
+ toolCallLayouts,
9201
+ workOpen,
9202
+ workTextOpen
9203
+ ]);
9204
+ const renderWorkSummary = (turnKey, calls) => {
9205
+ const working = isWorkingOn(turnKey);
9206
+ const open = isWorkOpen(turnKey);
9207
+ const times = workTimesRef.current.get(turnKey);
9208
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Collapsible, {
9209
+ open: working || calls.length > 0,
9210
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
9211
+ className: "cpk:pt-1 cpk:pb-2",
9212
+ children: renderSlot(workSummary, CopilotChatWorkSummary, {
9213
+ isWorking: working,
9214
+ isOpen: open,
9215
+ calls,
9216
+ elapsedMs: times?.elapsedMs,
9217
+ workingSince: times?.workingSince,
9218
+ onClick: () => setToggledTurns((toggled) => new Map(toggled).set(turnKey, !open))
9219
+ })
9220
+ })
9221
+ }, `work-summary-${turnKey}`);
9222
+ };
7776
9223
  const transformDuplicateId = (0, react.useMemo)(() => {
7777
9224
  if (process.env.NODE_ENV === "production" || !transformMessages && !groupMessages) return;
7778
9225
  const seen = /* @__PURE__ */ new Set();
@@ -7838,6 +9285,18 @@ _radix_ui_react_dropdown_menu = __toESM(_radix_ui_react_dropdown_menu);
7838
9285
  const { Component: AssistantComponent, slotProps: assistantSlotProps } = (0, react.useMemo)(() => resolveSlotComponent(assistantMessage, CopilotChatAssistantMessage_default), [assistantMessage]);
7839
9286
  const agentId = config?.agentId;
7840
9287
  const threadId = config?.threadId;
9288
+ const learningFeedbackEnabled = useLearningFeedbackEnabled(copilotkit);
9289
+ const feedbackScope = (0, react.useMemo)(() => messageFeedback && learningFeedbackEnabled && agentId !== void 0 && threadId !== void 0 ? {
9290
+ copilotkit,
9291
+ agentId,
9292
+ threadId
9293
+ } : null, [
9294
+ messageFeedback,
9295
+ learningFeedbackEnabled,
9296
+ copilotkit,
9297
+ agentId,
9298
+ threadId
9299
+ ]);
7841
9300
  const assistantSlotPropsWithFeedback = (0, react.useMemo)(() => {
7842
9301
  const onThumbsUp = assistantSlotProps?.onThumbsUp;
7843
9302
  const onThumbsDown = assistantSlotProps?.onThumbsDown;
@@ -7904,7 +9363,7 @@ _radix_ui_react_dropdown_menu = __toESM(_radix_ui_react_dropdown_menu);
7904
9363
  measuredRef.current.total = total - (sizes.get(index) ?? 0) + height;
7905
9364
  sizes.set(index, height);
7906
9365
  }
7907
- return height;
9366
+ return height > 0 ? height : measuredRef.current.sizes.get(index) ?? 100;
7908
9367
  }, []);
7909
9368
  const isPinnedToBottomRef = react.default.useRef(isPinnedToBottom);
7910
9369
  const shouldAdjustScrollOnResize = react.default.useCallback((item, _delta, instance) => {
@@ -7914,7 +9373,7 @@ _radix_ui_react_dropdown_menu = __toESM(_radix_ui_react_dropdown_menu);
7914
9373
  }, []);
7915
9374
  const virtualizer = (0, _tanstack_react_virtual.useVirtualizer)({
7916
9375
  count: shouldVirtualize ? rows.length : 0,
7917
- getScrollElement: () => scrollElement,
9376
+ getScrollElement: () => shouldVirtualize ? scrollElement : null,
7918
9377
  estimateSize: estimateRowSize,
7919
9378
  overscan: 5,
7920
9379
  measureElement: measureRowElement,
@@ -7925,8 +9384,11 @@ _radix_ui_react_dropdown_menu = __toESM(_radix_ui_react_dropdown_menu);
7925
9384
  });
7926
9385
  isPinnedToBottomRef.current = isPinnedToBottom;
7927
9386
  virtualizer.shouldAdjustScrollPositionOnItemSizeChange = shouldAdjustScrollOnResize;
9387
+ const lastEndAlignedFirstMessageRef = react.default.useRef(void 0);
7928
9388
  (0, react.useLayoutEffect)(() => {
7929
9389
  if (!shouldVirtualize || !rows.length) return;
9390
+ if (lastEndAlignedFirstMessageRef.current === firstMessageId) return;
9391
+ lastEndAlignedFirstMessageRef.current = firstMessageId;
7930
9392
  virtualizer.scrollToIndex(rows.length - 1, { align: "end" });
7931
9393
  }, [shouldVirtualize, firstMessageId]);
7932
9394
  const intelligenceTurnAnchors = (0, react.useMemo)(() => getIntelligenceTurnAnchors(deduplicatedMessages, renderedMessages), [deduplicatedMessages, renderedMessages]);
@@ -7934,6 +9396,8 @@ _radix_ui_react_dropdown_menu = __toESM(_radix_ui_react_dropdown_menu);
7934
9396
  const elements = [];
7935
9397
  const stateSnapshot = renderCustomMessage ? getStateSnapshotForMessage(message.id) : void 0;
7936
9398
  const rowKey = rowRenderKeys.get(message.id) ?? message.id;
9399
+ const turn = turnByFirstMessage.get(message.id);
9400
+ if (turn) elements.push(renderWorkSummary(turn.key, turn.calls));
7937
9401
  if (renderCustomMessage) elements.push(/* @__PURE__ */ (0, react_jsx_runtime.jsx)(MemoizedCustomMessage, {
7938
9402
  message,
7939
9403
  position: "before",
@@ -7957,14 +9421,20 @@ _radix_ui_react_dropdown_menu = __toESM(_radix_ui_react_dropdown_menu);
7957
9421
  message,
7958
9422
  renderActivityMessage
7959
9423
  }, rowKey));
7960
- else if (message.role === "reasoning") elements.push(/* @__PURE__ */ (0, react_jsx_runtime.jsx)(MemoizedReasoningMessage, {
7961
- message,
7962
- messages,
7963
- isRunning,
7964
- isLatest: message.id === latestRenderedId,
7965
- ReasoningMessageComponent: ReasoningComponent,
7966
- slotProps: reasoningSlotProps
7967
- }, rowKey));
9424
+ else if (message.role === "reasoning") {
9425
+ const reasoning = /* @__PURE__ */ (0, react_jsx_runtime.jsx)(MemoizedReasoningMessage, {
9426
+ message,
9427
+ messages,
9428
+ isRunning,
9429
+ isLatest: message.id === latestRenderedId,
9430
+ ReasoningMessageComponent: ReasoningComponent,
9431
+ slotProps: reasoningSlotProps
9432
+ }, rowKey);
9433
+ elements.push(toolCallDisplay === "turn" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Collapsible, {
9434
+ open: workOpen.get(message.id) ?? true,
9435
+ children: reasoning
9436
+ }, rowKey) : reasoning);
9437
+ }
7968
9438
  if (renderCustomMessage) elements.push(/* @__PURE__ */ (0, react_jsx_runtime.jsx)(MemoizedCustomMessage, {
7969
9439
  message,
7970
9440
  position: "after",
@@ -7992,52 +9462,157 @@ _radix_ui_react_dropdown_menu = __toESM(_radix_ui_react_dropdown_menu);
7992
9462
  children: row.messages.flatMap(renderMessageBlock)
7993
9463
  }, rowKey(row))];
7994
9464
  };
9465
+ const rowKeysRef = (0, react.useRef)([]);
9466
+ rowKeysRef.current = rows.map(rowKey);
9467
+ const virtualModeRef = (0, react.useRef)(shouldVirtualize);
9468
+ virtualModeRef.current = shouldVirtualize;
9469
+ (0, react.useEffect)(() => {
9470
+ const element = scrollElementFromCtx;
9471
+ if (!element) return;
9472
+ let anchor = null;
9473
+ let suspended = false;
9474
+ let frame = 0;
9475
+ const visibleRows = () => Array.from(element.querySelectorAll("[data-scroll-anchor]"));
9476
+ const remember = () => {
9477
+ if (suspended || element.clientHeight === 0 || !virtualModeRef.current) return;
9478
+ if (isPinnedToBottomRef.current) {
9479
+ anchor = null;
9480
+ return;
9481
+ }
9482
+ const viewport = element.getBoundingClientRect();
9483
+ const row = visibleRows().find((row) => {
9484
+ const bounds = row.getBoundingClientRect();
9485
+ return bounds.bottom > viewport.top && bounds.top < viewport.bottom;
9486
+ });
9487
+ if (row) anchor = {
9488
+ key: row.dataset.scrollAnchor,
9489
+ offset: row.getBoundingClientRect().top - viewport.top
9490
+ };
9491
+ };
9492
+ const observer = new ResizeObserver(() => {
9493
+ if (element.clientHeight === 0) {
9494
+ suspended = true;
9495
+ cancelAnimationFrame(frame);
9496
+ return;
9497
+ }
9498
+ if (!suspended || !anchor) {
9499
+ suspended = false;
9500
+ remember();
9501
+ return;
9502
+ }
9503
+ let attempts = 0;
9504
+ let stableFrames = 0;
9505
+ const restore = () => {
9506
+ if (!anchor || element.clientHeight === 0) return;
9507
+ const index = rowKeysRef.current.indexOf(anchor.key);
9508
+ if (index < 0) {
9509
+ anchor = null;
9510
+ suspended = false;
9511
+ return;
9512
+ }
9513
+ const row = visibleRows().find((row) => row.dataset.scrollAnchor === anchor.key);
9514
+ if (row) {
9515
+ const delta = row.getBoundingClientRect().top - element.getBoundingClientRect().top - anchor.offset;
9516
+ element.scrollTop += delta;
9517
+ stableFrames = Math.abs(delta) < 1 ? stableFrames + 1 : 0;
9518
+ } else {
9519
+ const offset = virtualizer.getOffsetForIndex(index, "start")?.[0];
9520
+ if (offset !== void 0) virtualizer.scrollToOffset(offset - anchor.offset);
9521
+ stableFrames = 0;
9522
+ }
9523
+ if (++attempts < 60 && stableFrames < 3) frame = requestAnimationFrame(restore);
9524
+ else {
9525
+ suspended = false;
9526
+ remember();
9527
+ }
9528
+ };
9529
+ frame = requestAnimationFrame(restore);
9530
+ });
9531
+ const interrupt = () => {
9532
+ cancelAnimationFrame(frame);
9533
+ anchor = null;
9534
+ suspended = false;
9535
+ };
9536
+ element.addEventListener("scroll", remember, { passive: true });
9537
+ element.addEventListener("wheel", interrupt, { passive: true });
9538
+ element.addEventListener("touchstart", interrupt, { passive: true });
9539
+ element.addEventListener("keydown", interrupt);
9540
+ observer.observe(element);
9541
+ remember();
9542
+ return () => {
9543
+ cancelAnimationFrame(frame);
9544
+ observer.disconnect();
9545
+ element.removeEventListener("scroll", remember);
9546
+ element.removeEventListener("wheel", interrupt);
9547
+ element.removeEventListener("touchstart", interrupt);
9548
+ element.removeEventListener("keydown", interrupt);
9549
+ };
9550
+ }, [
9551
+ scrollElementFromCtx,
9552
+ virtualizer,
9553
+ firstMessageId
9554
+ ]);
7995
9555
  const messageElements = shouldVirtualize ? [] : rows.flatMap(renderRow);
7996
- if (children) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
7997
- "data-copilotkit": true,
7998
- style: { display: "contents" },
7999
- children: children({
8000
- messageElements,
8001
- messages,
8002
- isRunning,
8003
- interruptElement
9556
+ if (children) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(MessageFeedbackScopeContext.Provider, {
9557
+ value: feedbackScope,
9558
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ToolCallDisplayContext.Provider, {
9559
+ value: toolCallDisplayContext,
9560
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
9561
+ "data-copilotkit": true,
9562
+ style: { display: "contents" },
9563
+ children: children({
9564
+ messageElements,
9565
+ messages,
9566
+ isRunning,
9567
+ interruptElement
9568
+ })
9569
+ })
8004
9570
  })
8005
9571
  });
8006
9572
  const lastMessage = renderedMessages[renderedMessages.length - 1];
8007
- const showCursor = isRunning && lastMessage?.role !== "reasoning";
8008
- return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
8009
- "data-copilotkit": true,
8010
- "data-testid": "copilot-message-list",
8011
- className: (0, tailwind_merge.twMerge)("copilotKitMessages cpk:flex cpk:flex-col", className),
8012
- ...props,
8013
- children: [
8014
- shouldVirtualize ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
8015
- style: {
8016
- height: virtualizer.getTotalSize(),
8017
- position: "relative"
8018
- },
8019
- children: virtualizer.getVirtualItems().map((virtualItem) => {
8020
- const row = rows[virtualItem.index];
8021
- return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
8022
- "data-index": virtualItem.index,
8023
- ref: virtualizer.measureElement,
9573
+ const showCursor = isRunning && lastMessage?.role !== "reasoning" && toolCallDisplay !== "turn";
9574
+ const pendingWorkSummary = toolCallDisplay === "turn" && isRunning && lastMessage?.role === "user";
9575
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(MessageFeedbackScopeContext.Provider, {
9576
+ value: feedbackScope,
9577
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ToolCallDisplayContext.Provider, {
9578
+ value: toolCallDisplayContext,
9579
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
9580
+ "data-copilotkit": true,
9581
+ "data-testid": "copilot-message-list",
9582
+ className: (0, tailwind_merge.twMerge)("copilotKitMessages cpk:flex cpk:flex-col", className),
9583
+ ...props,
9584
+ children: [
9585
+ shouldVirtualize ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
8024
9586
  style: {
8025
- position: "absolute",
8026
- top: 0,
8027
- left: 0,
8028
- width: "100%",
8029
- transform: `translateY(${virtualItem.start}px)`
9587
+ height: virtualizer.getTotalSize(),
9588
+ position: "relative"
8030
9589
  },
8031
- children: renderRow(row)
8032
- }, rowKey(row));
8033
- })
8034
- }) : messageElements,
8035
- interruptElement,
8036
- showCursor && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
8037
- className: "cpk:mt-2",
8038
- children: renderSlot(cursor, CopilotChatMessageView.Cursor, {})
9590
+ children: virtualizer.getVirtualItems().map((virtualItem) => {
9591
+ const row = rows[virtualItem.index];
9592
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
9593
+ "data-index": virtualItem.index,
9594
+ "data-scroll-anchor": rowKey(row),
9595
+ ref: virtualizer.measureElement,
9596
+ style: {
9597
+ position: "absolute",
9598
+ top: 0,
9599
+ left: 0,
9600
+ width: "100%",
9601
+ transform: `translateY(${virtualItem.start}px)`
9602
+ },
9603
+ children: renderRow(row)
9604
+ }, rowKey(row));
9605
+ })
9606
+ }) : messageElements,
9607
+ interruptElement,
9608
+ pendingWorkSummary && renderWorkSummary(latestTurnKey, []),
9609
+ showCursor && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
9610
+ className: "cpk:mt-2",
9611
+ children: renderSlot(cursor, CopilotChatMessageView.Cursor, {})
9612
+ })
9613
+ ]
8039
9614
  })
8040
- ]
9615
+ })
8041
9616
  });
8042
9617
  }
8043
9618
  CopilotChatMessageView.Cursor = function Cursor({ className, ...props }) {
@@ -8422,6 +9997,16 @@ _radix_ui_react_dropdown_menu = __toESM(_radix_ui_react_dropdown_menu);
8422
9997
  });
8423
9998
  }
8424
9999
  function CopilotChatView({ messageView, input, scrollView, suggestionView, welcomeScreen, messages = [], autoScroll = true, isRunning = false, suggestions, suggestionLoadingIndexes, onSelectSuggestion, onSubmitMessage, onStop, inputMode, inputValue, onInputChange, onStartTranscribe, onCancelTranscribe, onFinishTranscribe, onFinishTranscribeWithAudio, attachments, onRemoveAttachment, onAddFile, dragOver, onDragOver, onDragLeave, onDrop, isConnecting = false, hasExplicitThreadId = false, disclaimer, intelligenceIndicator, children, className, ...props }) {
10000
+ const configuredThreadId = useCopilotChatConfiguration()?.threadId;
10001
+ const [fallbackThreadId, setFallbackThreadId] = (0, react.useState)(messages[0]?.id);
10002
+ const previousMessageIds = (0, react.useRef)(/* @__PURE__ */ new Set());
10003
+ (0, react.useEffect)(() => {
10004
+ if (messages.length === 0) return;
10005
+ const overlaps = messages.some((message) => previousMessageIds.current.has(message.id));
10006
+ previousMessageIds.current = new Set(messages.map((message) => message.id));
10007
+ if (!overlaps) setFallbackThreadId(messages[0].id);
10008
+ }, [messages]);
10009
+ const scrollThreadId = configuredThreadId ?? fallbackThreadId;
8425
10010
  const [inputContainerEl, setInputContainerEl] = (0, react.useState)(null);
8426
10011
  const [inputContainerHeight, setInputContainerHeight] = (0, react.useState)(0);
8427
10012
  const [isResizing, setIsResizing] = (0, react.useState)(false);
@@ -8488,6 +10073,7 @@ _radix_ui_react_dropdown_menu = __toESM(_radix_ui_react_dropdown_menu);
8488
10073
  }) : null;
8489
10074
  const BoundScrollView = renderSlot(scrollView, CopilotChatView.ScrollView, {
8490
10075
  autoScroll,
10076
+ scrollThreadId,
8491
10077
  inputContainerHeight,
8492
10078
  isResizing,
8493
10079
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
@@ -8583,12 +10169,102 @@ _radix_ui_react_dropdown_menu = __toESM(_radix_ui_react_dropdown_menu);
8583
10169
  });
8584
10170
  }
8585
10171
  (function(_CopilotChatView) {
8586
- const ScrollContent = ({ children, scrollToBottomButton, feather, inputContainerHeight, isResizing }) => {
8587
- const { isAtBottom, scrollToBottom, scrollRef, state } = (0, use_stick_to_bottom.useStickToBottomContext)();
10172
+ const ScrollContent = ({ children, scrollToBottomButton, feather, inputContainerHeight, isResizing, scrollThreadId }) => {
10173
+ const { isAtBottom, scrollToBottom, stopScroll, scrollRef, state } = (0, use_stick_to_bottom.useStickToBottomContext)();
8588
10174
  const [scrollEl, setScrollEl] = (0, react.useState)(null);
10175
+ const readingOffsetRef = (0, react.useRef)(null);
10176
+ const pinIntentRef = (0, react.useRef)(false);
10177
+ const stateRef = (0, react.useRef)(state);
10178
+ stateRef.current = state;
8589
10179
  (0, react.useLayoutEffect)(() => {
8590
10180
  setScrollEl(scrollRef.current ?? null);
8591
10181
  }, []);
10182
+ (0, react.useEffect)(() => {
10183
+ if (!scrollEl) return;
10184
+ let hidden = false;
10185
+ let resumeBottom = false;
10186
+ let pendingFrame = 0;
10187
+ let resumeFrame = 0;
10188
+ let settleFrame = 0;
10189
+ readingOffsetRef.current = null;
10190
+ const rememberScroll = () => {
10191
+ if (hidden || scrollEl.clientHeight === 0) return;
10192
+ if (resumeBottom) {
10193
+ readingOffsetRef.current = null;
10194
+ return;
10195
+ }
10196
+ if (pinIntentRef.current) if (stateRef.current.escapedFromLock) pinIntentRef.current = false;
10197
+ else {
10198
+ readingOffsetRef.current = null;
10199
+ if (!stateRef.current.isNearBottom) return;
10200
+ pinIntentRef.current = false;
10201
+ }
10202
+ readingOffsetRef.current = !stateRef.current.isAtBottom && (stateRef.current.escapedFromLock || !stateRef.current.isNearBottom) ? scrollEl.scrollTop : null;
10203
+ };
10204
+ const onScroll = () => {
10205
+ rememberScroll();
10206
+ cancelAnimationFrame(pendingFrame);
10207
+ pendingFrame = requestAnimationFrame(rememberScroll);
10208
+ };
10209
+ const onWheel = (event) => {
10210
+ if (event.deltaY < 0) {
10211
+ pinIntentRef.current = false;
10212
+ resumeBottom = false;
10213
+ }
10214
+ };
10215
+ const resizeObserver = new ResizeObserver(() => {
10216
+ if (scrollEl.clientHeight === 0) {
10217
+ hidden = true;
10218
+ resumeBottom = pinIntentRef.current || readingOffsetRef.current === null;
10219
+ if (!resumeBottom) stopScroll();
10220
+ } else if (hidden) {
10221
+ hidden = false;
10222
+ if (resumeBottom) {
10223
+ stopScroll();
10224
+ resumeFrame = requestAnimationFrame(() => {
10225
+ settleFrame = requestAnimationFrame(() => {
10226
+ if (scrollEl.clientHeight > 0 && resumeBottom) {
10227
+ resumeBottom = false;
10228
+ pinIntentRef.current = true;
10229
+ scrollToBottom({ animation: "instant" });
10230
+ }
10231
+ });
10232
+ });
10233
+ } else if (readingOffsetRef.current !== null) {
10234
+ stopScroll();
10235
+ scrollEl.scrollTop = readingOffsetRef.current;
10236
+ }
10237
+ }
10238
+ });
10239
+ rememberScroll();
10240
+ scrollEl.addEventListener("scroll", onScroll);
10241
+ scrollEl.addEventListener("wheel", onWheel, { passive: true });
10242
+ resizeObserver.observe(scrollEl);
10243
+ return () => {
10244
+ scrollEl.removeEventListener("scroll", onScroll);
10245
+ scrollEl.removeEventListener("wheel", onWheel);
10246
+ cancelAnimationFrame(pendingFrame);
10247
+ cancelAnimationFrame(resumeFrame);
10248
+ cancelAnimationFrame(settleFrame);
10249
+ resizeObserver.disconnect();
10250
+ };
10251
+ }, [
10252
+ scrollEl,
10253
+ scrollToBottom,
10254
+ stopScroll,
10255
+ scrollThreadId
10256
+ ]);
10257
+ const startBottomPin = (0, react.useCallback)((instant = false) => {
10258
+ readingOffsetRef.current = null;
10259
+ pinIntentRef.current = true;
10260
+ scrollToBottom(instant ? { animation: "instant" } : void 0);
10261
+ }, [scrollToBottom]);
10262
+ const previousScrollThreadId = (0, react.useRef)(scrollThreadId);
10263
+ (0, react.useEffect)(() => {
10264
+ if (previousScrollThreadId.current === scrollThreadId) return;
10265
+ previousScrollThreadId.current = scrollThreadId;
10266
+ startBottomPin(true);
10267
+ }, [scrollThreadId, startBottomPin]);
8592
10268
  const BoundFeather = renderSlot(feather, CopilotChatView.Feather, {});
8593
10269
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ScrollElementContext.Provider, {
8594
10270
  value: scrollEl,
@@ -8610,7 +10286,7 @@ _radix_ui_react_dropdown_menu = __toESM(_radix_ui_react_dropdown_menu);
8610
10286
  !isAtBottom && !isResizing && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
8611
10287
  className: "cpk:absolute cpk:inset-x-0 cpk:flex cpk:justify-center cpk:z-30 cpk:pointer-events-none",
8612
10288
  style: { bottom: `${inputContainerHeight + SCROLL_BUTTON_OFFSET}px` },
8613
- children: renderSlot(scrollToBottomButton, CopilotChatView.ScrollToBottomButton, { onClick: () => scrollToBottom() })
10289
+ children: renderSlot(scrollToBottomButton, CopilotChatView.ScrollToBottomButton, { onClick: () => startBottomPin() })
8614
10290
  })
8615
10291
  ] })
8616
10292
  })
@@ -8658,7 +10334,7 @@ _radix_ui_react_dropdown_menu = __toESM(_radix_ui_react_dropdown_menu);
8658
10334
  })
8659
10335
  });
8660
10336
  };
8661
- _CopilotChatView.ScrollView = ({ children, autoScroll = "pin-to-bottom", scrollToBottomButton, feather, inputContainerHeight = 0, isResizing = false, className, ...props }) => {
10337
+ _CopilotChatView.ScrollView = ({ children, autoScroll = "pin-to-bottom", scrollThreadId, scrollToBottomButton, feather, inputContainerHeight = 0, isResizing = false, className, ...props }) => {
8662
10338
  const mode = normalizeAutoScroll(autoScroll);
8663
10339
  const [hasMounted, setHasMounted] = (0, react.useState)(false);
8664
10340
  const scrollRef = (0, react.useRef)(null);
@@ -8747,6 +10423,7 @@ _radix_ui_react_dropdown_menu = __toESM(_radix_ui_react_dropdown_menu);
8747
10423
  initial: "smooth",
8748
10424
  ...props,
8749
10425
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ScrollContent, {
10426
+ scrollThreadId,
8750
10427
  scrollToBottomButton,
8751
10428
  feather,
8752
10429
  inputContainerHeight,
@@ -8755,14 +10432,18 @@ _radix_ui_react_dropdown_menu = __toESM(_radix_ui_react_dropdown_menu);
8755
10432
  })
8756
10433
  });
8757
10434
  };
8758
- _CopilotChatView.ScrollToBottomButton = ({ className, ...props }) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Button, {
8759
- "data-testid": "copilot-scroll-to-bottom",
8760
- variant: "outline",
8761
- size: "sm",
8762
- className: (0, tailwind_merge.twMerge)("cpk:rounded-full cpk:w-10 cpk:h-10 cpk:p-0 cpk:pointer-events-auto", "cpk:bg-white cpk:dark:bg-gray-900", "cpk:shadow-lg cpk:border cpk:border-gray-200 cpk:dark:border-gray-700", "cpk:hover:bg-gray-50 cpk:dark:hover:bg-gray-800", "cpk:flex cpk:items-center cpk:justify-center cpk:cursor-pointer", className),
8763
- ...props,
8764
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.ChevronDown, { className: "cpk:w-4 cpk:h-4 cpk:text-gray-600 cpk:dark:text-white" })
8765
- });
10435
+ _CopilotChatView.ScrollToBottomButton = ({ className, ...props }) => {
10436
+ const labels = useCopilotChatConfiguration()?.labels ?? CopilotChatDefaultLabels;
10437
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Button, {
10438
+ "data-testid": "copilot-scroll-to-bottom",
10439
+ variant: "outline",
10440
+ size: "sm",
10441
+ className: (0, tailwind_merge.twMerge)("cpk:rounded-full cpk:w-10 cpk:h-10 cpk:p-0 cpk:pointer-events-auto", "cpk:bg-white cpk:dark:bg-gray-900", "cpk:shadow-lg cpk:border cpk:border-gray-200 cpk:dark:border-gray-700", "cpk:hover:bg-gray-50 cpk:dark:hover:bg-gray-800", "cpk:flex cpk:items-center cpk:justify-center cpk:cursor-pointer", className),
10442
+ "aria-label": labels.chatScrollToBottomLabel,
10443
+ ...props,
10444
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.ChevronDown, { className: "cpk:w-4 cpk:h-4 cpk:text-gray-600 cpk:dark:text-white" })
10445
+ });
10446
+ };
8766
10447
  _CopilotChatView.Feather = ({ className, ...props }) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
8767
10448
  className,
8768
10449
  ...props
@@ -10145,6 +11826,7 @@ _radix_ui_react_dropdown_menu = __toESM(_radix_ui_react_dropdown_menu);
10145
11826
  if (containerRef.current?.contains(target)) return;
10146
11827
  const toggleButton = document.querySelector("[data-slot='chat-toggle-button']");
10147
11828
  if (toggleButton && toggleButton.contains(target)) return;
11829
+ if (target instanceof Element && target.closest("[data-copilotkit-popover]")) return;
10148
11830
  setModalOpen?.(false);
10149
11831
  };
10150
11832
  document.addEventListener("pointerdown", handlePointerDown);
@@ -10655,62 +12337,14 @@ _radix_ui_react_dropdown_menu = __toESM(_radix_ui_react_dropdown_menu);
10655
12337
 
10656
12338
  //#endregion
10657
12339
  //#region src/v2/components/WildcardToolCallRender.tsx
12340
+ /**
12341
+ * Catch-all (`"*"`) tool-call renderer that shows CopilotKit's default
12342
+ * tool-call card for any tool without a dedicated renderer.
12343
+ */
10658
12344
  const WildcardToolCallRender = defineToolCallRenderer({
10659
12345
  name: "*",
10660
- render: ({ args, result, name, status }) => {
10661
- const [isExpanded, setIsExpanded] = (0, react.useState)(false);
10662
- const statusString = String(status);
10663
- return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
10664
- className: "cpk:mt-2 cpk:pb-2",
10665
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
10666
- className: "cpk:rounded-xl cpk:border cpk:border-zinc-200/60 cpk:dark:border-zinc-800/60 cpk:bg-white/70 cpk:dark:bg-zinc-900/50 cpk:shadow-sm cpk:backdrop-blur cpk:p-4",
10667
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
10668
- className: "cpk:flex cpk:items-center cpk:justify-between cpk:gap-3 cpk:cursor-pointer",
10669
- onClick: () => setIsExpanded(!isExpanded),
10670
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
10671
- className: "cpk:flex cpk:items-center cpk:gap-2 cpk:min-w-0",
10672
- children: [
10673
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
10674
- className: `cpk:h-4 cpk:w-4 cpk:text-zinc-500 cpk:dark:text-zinc-400 cpk:transition-transform ${isExpanded ? "cpk:rotate-90" : ""}`,
10675
- fill: "none",
10676
- viewBox: "0 0 24 24",
10677
- strokeWidth: 2,
10678
- stroke: "currentColor",
10679
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
10680
- strokeLinecap: "round",
10681
- strokeLinejoin: "round",
10682
- d: "M8.25 4.5l7.5 7.5-7.5 7.5"
10683
- })
10684
- }),
10685
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: "cpk:inline-block cpk:h-2 cpk:w-2 cpk:rounded-full cpk:bg-blue-500" }),
10686
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
10687
- className: "cpk:truncate cpk:text-sm cpk:font-medium cpk:text-zinc-900 cpk:dark:text-zinc-100",
10688
- children: name
10689
- })
10690
- ]
10691
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
10692
- className: `cpk:inline-flex cpk:items-center cpk:rounded-full cpk:px-2 cpk:py-1 cpk:text-xs cpk:font-medium ${statusString === "inProgress" || statusString === "executing" ? "cpk:bg-amber-100 cpk:text-amber-800 cpk:dark:bg-amber-500/15 cpk:dark:text-amber-400" : statusString === "complete" ? "cpk:bg-emerald-100 cpk:text-emerald-800 cpk:dark:bg-emerald-500/15 cpk:dark:text-emerald-400" : "cpk:bg-zinc-100 cpk:text-zinc-800 cpk:dark:bg-zinc-700/40 cpk:dark:text-zinc-300"}`,
10693
- children: String(status)
10694
- })]
10695
- }), isExpanded && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
10696
- className: "cpk:mt-3 cpk:grid cpk:gap-4",
10697
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
10698
- className: "cpk:text-xs cpk:uppercase cpk:tracking-wide cpk:text-zinc-500 cpk:dark:text-zinc-400",
10699
- children: "Arguments"
10700
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("pre", {
10701
- className: "cpk:mt-2 cpk:max-h-64 cpk:overflow-auto cpk:rounded-md cpk:bg-zinc-50 cpk:dark:bg-zinc-800/60 cpk:p-3 cpk:text-xs cpk:leading-relaxed cpk:text-zinc-800 cpk:dark:text-zinc-200 cpk:whitespace-pre-wrap cpk:break-words",
10702
- children: JSON.stringify(args ?? {}, null, 2)
10703
- })] }), result !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
10704
- className: "cpk:text-xs cpk:uppercase cpk:tracking-wide cpk:text-zinc-500 cpk:dark:text-zinc-400",
10705
- children: "Result"
10706
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("pre", {
10707
- className: "cpk:mt-2 cpk:max-h-64 cpk:overflow-auto cpk:rounded-md cpk:bg-zinc-50 cpk:dark:bg-zinc-800/60 cpk:p-3 cpk:text-xs cpk:leading-relaxed cpk:text-zinc-800 cpk:dark:text-zinc-200 cpk:whitespace-pre-wrap cpk:break-words",
10708
- children: typeof result === "string" ? result : JSON.stringify(result, null, 2)
10709
- })] })]
10710
- })]
10711
- })
10712
- });
10713
- }
12346
+ group: true,
12347
+ render: (props) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DefaultToolCallRenderer, { ...adaptRendererProps(props) })
10714
12348
  });
10715
12349
 
10716
12350
  //#endregion
@@ -12955,9 +14589,11 @@ exports.CopilotChatSuggestionView = CopilotChatSuggestionView;
12955
14589
  exports.CopilotChatToggleButton = CopilotChatToggleButton;
12956
14590
  exports.CopilotChatToggleButtonCloseIcon = DefaultCloseIcon;
12957
14591
  exports.CopilotChatToggleButtonOpenIcon = DefaultOpenIcon;
14592
+ exports.CopilotChatToolCallGroup = CopilotChatToolCallGroup;
12958
14593
  exports.CopilotChatToolCallsView = CopilotChatToolCallsView;
12959
14594
  exports.CopilotChatUserMessage = CopilotChatUserMessage_default;
12960
14595
  exports.CopilotChatView = CopilotChatView_default;
14596
+ exports.CopilotChatWorkSummary = CopilotChatWorkSummary;
12961
14597
  exports.CopilotKit = CopilotKit;
12962
14598
  Object.defineProperty(exports, 'CopilotKitCore', {
12963
14599
  enumerable: true,