@copilotkit/react-core 1.55.0-next.8 → 1.55.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 (94) hide show
  1. package/CHANGELOG.md +48 -5
  2. package/dist/{copilotkit-DNYSFuz5.mjs → copilotkit-BY5S1-0P.mjs} +2772 -858
  3. package/dist/copilotkit-BY5S1-0P.mjs.map +1 -0
  4. package/dist/{copilotkit-Dy5w3qEV.d.mts → copilotkit-BuhSUZHb.d.mts} +230 -17
  5. package/dist/copilotkit-BuhSUZHb.d.mts.map +1 -0
  6. package/dist/{copilotkit-B3Mb1yVE.cjs → copilotkit-Bz5-ImDl.cjs} +2776 -832
  7. package/dist/copilotkit-Bz5-ImDl.cjs.map +1 -0
  8. package/dist/{copilotkit-DBzgOMby.d.cts → copilotkit-dwDWYpya.d.cts} +230 -17
  9. package/dist/copilotkit-dwDWYpya.d.cts.map +1 -0
  10. package/dist/index.cjs +9 -4
  11. package/dist/index.cjs.map +1 -1
  12. package/dist/index.d.cts +1 -1
  13. package/dist/index.d.mts +1 -1
  14. package/dist/index.mjs +9 -4
  15. package/dist/index.mjs.map +1 -1
  16. package/dist/index.umd.js +1624 -396
  17. package/dist/index.umd.js.map +1 -1
  18. package/dist/v2/index.cjs +13 -1
  19. package/dist/v2/index.css +1 -1
  20. package/dist/v2/index.d.cts +3 -3
  21. package/dist/v2/index.d.mts +3 -3
  22. package/dist/v2/index.mjs +3 -2
  23. package/dist/v2/index.umd.js +2746 -790
  24. package/dist/v2/index.umd.js.map +1 -1
  25. package/package.json +62 -54
  26. package/scripts/scope-preflight.mjs +1 -2
  27. package/src/components/CopilotListeners.tsx +41 -8
  28. package/src/components/copilot-provider/__tests__/copilot-messages-key.test.tsx +92 -0
  29. package/src/components/copilot-provider/copilotkit-props.tsx +4 -2
  30. package/src/components/copilot-provider/copilotkit.tsx +3 -3
  31. package/src/components/toast/toast-provider.tsx +269 -194
  32. package/src/hooks/__tests__/use-copilot-chat-internal-connect.test.tsx +27 -16
  33. package/src/hooks/use-copilot-chat_internal.ts +15 -4
  34. package/src/v2/__tests__/A2UIMessageRenderer.test.tsx +86 -22
  35. package/src/v2/__tests__/utils/test-helpers.tsx +107 -7
  36. package/src/v2/a2ui/A2UICatalogContext.tsx +79 -0
  37. package/src/v2/a2ui/A2UIMessageRenderer.tsx +125 -37
  38. package/src/v2/a2ui/A2UIToolCallRenderer.tsx +290 -0
  39. package/src/v2/components/CopilotKitInspector.tsx +2 -0
  40. package/src/v2/components/OpenGenerativeUIRenderer.tsx +598 -0
  41. package/src/v2/components/__tests__/OpenGenerativeUIRenderer.test.tsx +665 -0
  42. package/src/v2/components/chat/CopilotChat.tsx +197 -52
  43. package/src/v2/components/chat/CopilotChatAssistantMessage.tsx +17 -2
  44. package/src/v2/components/chat/CopilotChatAttachmentQueue.tsx +481 -0
  45. package/src/v2/components/chat/CopilotChatAttachmentRenderer.tsx +139 -0
  46. package/src/v2/components/chat/CopilotChatInput.tsx +146 -77
  47. package/src/v2/components/chat/CopilotChatMessageView.tsx +260 -151
  48. package/src/v2/components/chat/CopilotChatSuggestionView.tsx +1 -0
  49. package/src/v2/components/chat/CopilotChatUserMessage.tsx +54 -0
  50. package/src/v2/components/chat/CopilotChatView.tsx +179 -66
  51. package/src/v2/components/chat/__tests__/CopilotChat.attachments.test.tsx +168 -0
  52. package/src/v2/components/chat/__tests__/CopilotChatActivityRendering.e2e.test.tsx +63 -2
  53. package/src/v2/components/chat/__tests__/CopilotChatInput.test.tsx +544 -1
  54. package/src/v2/components/chat/__tests__/CopilotChatPerf.e2e.test.tsx +268 -0
  55. package/src/v2/components/chat/__tests__/CopilotChatPropsRerender.e2e.test.tsx +249 -0
  56. package/src/v2/components/chat/__tests__/CopilotChatToolRendering.e2e.test.tsx +5 -2
  57. package/src/v2/components/chat/__tests__/CopilotChatToolRerenders.e2e.test.tsx +5 -2
  58. package/src/v2/components/chat/__tests__/MCPAppsActivityRenderer.e2e.test.tsx +60 -3
  59. package/src/v2/components/chat/__tests__/copilot-chat-throttle.test.tsx +138 -0
  60. package/src/v2/components/chat/index.ts +9 -0
  61. package/src/v2/components/chat/scroll-element-context.ts +13 -0
  62. package/src/v2/hooks/__tests__/use-agent-context-timing.e2e.test.tsx +8 -0
  63. package/src/v2/hooks/__tests__/use-agent-thread-isolation.test.tsx +327 -0
  64. package/src/v2/hooks/__tests__/use-agent-throttle.test.tsx +1003 -0
  65. package/src/v2/hooks/__tests__/use-agent.e2e.test.tsx +13 -2
  66. package/src/v2/hooks/__tests__/use-attachments.test.tsx +169 -0
  67. package/src/v2/hooks/__tests__/use-frontend-tool.e2e.test.tsx +23 -4
  68. package/src/v2/hooks/__tests__/use-threads.test.tsx +54 -0
  69. package/src/v2/hooks/index.ts +5 -0
  70. package/src/v2/hooks/use-agent.tsx +220 -15
  71. package/src/v2/hooks/use-attachments.tsx +269 -0
  72. package/src/v2/hooks/use-frontend-tool.tsx +5 -2
  73. package/src/v2/hooks/use-render-activity-message.tsx +9 -2
  74. package/src/v2/hooks/use-render-custom-messages.tsx +6 -1
  75. package/src/v2/hooks/use-threads.tsx +35 -15
  76. package/src/v2/index.ts +5 -1
  77. package/src/v2/lib/__tests__/processPartialHtml.test.ts +112 -0
  78. package/src/v2/lib/__tests__/slots.test.ts +56 -0
  79. package/src/v2/lib/processPartialHtml.ts +45 -0
  80. package/src/v2/lib/slots.tsx +42 -1
  81. package/src/v2/providers/CopilotChatConfigurationProvider.tsx +9 -3
  82. package/src/v2/providers/CopilotKitProvider.tsx +268 -32
  83. package/src/v2/providers/SandboxFunctionsContext.ts +10 -0
  84. package/src/v2/providers/__tests__/CopilotKitProvider.sandboxFunctions.test.tsx +198 -0
  85. package/src/v2/providers/__tests__/CopilotKitProvider.test.tsx +71 -0
  86. package/src/v2/providers/index.ts +7 -0
  87. package/src/v2/styles/globals.css +2 -1
  88. package/src/v2/types/index.ts +1 -0
  89. package/src/v2/types/sandbox-function.ts +11 -0
  90. package/dist/copilotkit-B3Mb1yVE.cjs.map +0 -1
  91. package/dist/copilotkit-DBzgOMby.d.cts.map +0 -1
  92. package/dist/copilotkit-DNYSFuz5.mjs.map +0 -1
  93. package/dist/copilotkit-Dy5w3qEV.d.mts.map +0 -1
  94. package/src/v2/components/__tests__/license-warning-banner.test.tsx +0 -46
@@ -44,19 +44,120 @@ let streamdown = require("streamdown");
44
44
  let zod = require("zod");
45
45
  let _lit_labs_react = require("@lit-labs/react");
46
46
  let _copilotkit_a2ui_renderer = require("@copilotkit/a2ui-renderer");
47
- let use_stick_to_bottom = require("use-stick-to-bottom");
48
- let ts_deepmerge = require("ts-deepmerge");
47
+ let zod_to_json_schema = require("zod-to-json-schema");
48
+ let _tanstack_react_virtual = require("@tanstack/react-virtual");
49
49
  let react_dom = require("react-dom");
50
+ let use_stick_to_bottom = require("use-stick-to-bottom");
50
51
  let react_markdown = require("react-markdown");
51
52
  react_markdown = __toESM(react_markdown);
52
53
 
54
+ //#region src/v2/lib/slots.tsx
55
+ /**
56
+ * Shallow equality comparison for objects.
57
+ */
58
+ function shallowEqual(obj1, obj2) {
59
+ const keys1 = Object.keys(obj1);
60
+ const keys2 = Object.keys(obj2);
61
+ if (keys1.length !== keys2.length) return false;
62
+ for (const key of keys1) if (obj1[key] !== obj2[key]) return false;
63
+ return true;
64
+ }
65
+ /**
66
+ * Returns true only for plain JS objects (`{}`), excluding arrays, Dates,
67
+ * class instances, and other exotic objects that happen to have typeof "object".
68
+ */
69
+ function isPlainObject(obj) {
70
+ return obj !== null && typeof obj === "object" && Object.prototype.toString.call(obj) === "[object Object]";
71
+ }
72
+ /**
73
+ * Returns the same reference as long as the value is shallowly equal to the
74
+ * previous render's value.
75
+ *
76
+ * - Identical references bail out immediately (O(1)).
77
+ * - Plain objects ({}) are shallow-compared key-by-key.
78
+ * - Arrays, Dates, class instances, functions, and primitives are compared by
79
+ * reference only — shallowEqual is never called on non-plain objects, which
80
+ * avoids incorrect equality for e.g. [1,2] vs [1,2] (different arrays).
81
+ *
82
+ * Typical use: stabilize inline slot props so MemoizedSlotWrapper's shallow
83
+ * equality check isn't defeated by a new object reference on every render.
84
+ */
85
+ function useShallowStableRef(value) {
86
+ const ref = (0, react.useRef)(value);
87
+ if (ref.current === value) return ref.current;
88
+ if (isPlainObject(ref.current) && isPlainObject(value)) {
89
+ if (shallowEqual(ref.current, value)) return ref.current;
90
+ }
91
+ ref.current = value;
92
+ return ref.current;
93
+ }
94
+ /**
95
+ * Check if a value is a React component type (function, class, forwardRef, memo, etc.)
96
+ */
97
+ function isReactComponentType(value) {
98
+ if (typeof value === "function") return true;
99
+ if (value && typeof value === "object" && "$$typeof" in value && !react.default.isValidElement(value)) return true;
100
+ return false;
101
+ }
102
+ /**
103
+ * Internal function to render a slot value as a React element (non-memoized).
104
+ */
105
+ function renderSlotElement(slot, DefaultComponent, props) {
106
+ if (typeof slot === "string") {
107
+ const existingClassName = props.className;
108
+ return react.default.createElement(DefaultComponent, {
109
+ ...props,
110
+ className: (0, tailwind_merge.twMerge)(existingClassName, slot)
111
+ });
112
+ }
113
+ if (isReactComponentType(slot)) return react.default.createElement(slot, props);
114
+ if (slot && typeof slot === "object" && !react.default.isValidElement(slot)) return react.default.createElement(DefaultComponent, {
115
+ ...props,
116
+ ...slot
117
+ });
118
+ return react.default.createElement(DefaultComponent, props);
119
+ }
120
+ /**
121
+ * Internal memoized wrapper component for renderSlot.
122
+ * Uses forwardRef to support ref forwarding.
123
+ */
124
+ const MemoizedSlotWrapper = react.default.memo(react.default.forwardRef(function MemoizedSlotWrapper(props, ref) {
125
+ const { $slot, $component, ...rest } = props;
126
+ return renderSlotElement($slot, $component, ref !== null ? {
127
+ ...rest,
128
+ ref
129
+ } : rest);
130
+ }), (prev, next) => {
131
+ if (prev.$slot !== next.$slot) return false;
132
+ if (prev.$component !== next.$component) return false;
133
+ const { $slot: _ps, $component: _pc, ...prevRest } = prev;
134
+ const { $slot: _ns, $component: _nc, ...nextRest } = next;
135
+ return shallowEqual(prevRest, nextRest);
136
+ });
137
+ /**
138
+ * Renders a slot value as a memoized React element.
139
+ * Automatically prevents unnecessary re-renders using shallow prop comparison.
140
+ * Supports ref forwarding.
141
+ *
142
+ * @example
143
+ * renderSlot(customInput, CopilotChatInput, { onSubmit: handleSubmit })
144
+ */
145
+ function renderSlot(slot, DefaultComponent, props) {
146
+ return react.default.createElement(MemoizedSlotWrapper, {
147
+ ...props,
148
+ $slot: slot,
149
+ $component: DefaultComponent
150
+ });
151
+ }
152
+
153
+ //#endregion
53
154
  //#region src/v2/providers/CopilotChatConfigurationProvider.tsx
54
155
  const CopilotChatDefaultLabels = {
55
156
  chatInputPlaceholder: "Type a message...",
56
157
  chatInputToolbarStartTranscribeButtonLabel: "Transcribe",
57
158
  chatInputToolbarCancelTranscribeButtonLabel: "Cancel",
58
159
  chatInputToolbarFinishTranscribeButtonLabel: "Finish",
59
- chatInputToolbarAddButtonLabel: "Add photos or files",
160
+ chatInputToolbarAddButtonLabel: "Add attachments",
60
161
  chatInputToolbarToolsButtonLabel: "Tools",
61
162
  assistantMessageToolbarCopyCodeLabel: "Copy",
62
163
  assistantMessageToolbarCopyCodeCopiedLabel: "Copied",
@@ -76,11 +177,12 @@ const CopilotChatDefaultLabels = {
76
177
  const CopilotChatConfiguration = (0, react.createContext)(null);
77
178
  const CopilotChatConfigurationProvider = ({ children, labels, agentId, threadId, isModalDefaultOpen }) => {
78
179
  const parentConfig = (0, react.useContext)(CopilotChatConfiguration);
180
+ const stableLabels = useShallowStableRef(labels);
79
181
  const mergedLabels = (0, react.useMemo)(() => ({
80
182
  ...CopilotChatDefaultLabels,
81
183
  ...parentConfig?.labels ?? {},
82
- ...labels ?? {}
83
- }), [labels, parentConfig?.labels]);
184
+ ...stableLabels ?? {}
185
+ }), [stableLabels, parentConfig?.labels]);
84
186
  const resolvedAgentId = agentId ?? parentConfig?.agentId ?? _copilotkit_shared.DEFAULT_AGENT_ID;
85
187
  const resolvedThreadId = (0, react.useMemo)(() => {
86
188
  if (threadId) return threadId;
@@ -129,9 +231,9 @@ const useCopilotChatConfiguration = () => {
129
231
 
130
232
  //#endregion
131
233
  //#region src/v2/lib/utils.ts
132
- const twMerge$8 = (0, tailwind_merge.extendTailwindMerge)({ prefix: "cpk" });
234
+ const twMerge$7 = (0, tailwind_merge.extendTailwindMerge)({ prefix: "cpk" });
133
235
  function cn(...inputs) {
134
- return twMerge$8((0, clsx.clsx)(inputs));
236
+ return twMerge$7((0, clsx.clsx)(inputs));
135
237
  }
136
238
 
137
239
  //#endregion
@@ -503,82 +605,11 @@ const CopilotChatAudioRecorder = (0, react.forwardRef)((props, ref) => {
503
605
  });
504
606
  CopilotChatAudioRecorder.displayName = "CopilotChatAudioRecorder";
505
607
 
506
- //#endregion
507
- //#region src/v2/lib/slots.tsx
508
- /**
509
- * Shallow equality comparison for objects.
510
- */
511
- function shallowEqual(obj1, obj2) {
512
- const keys1 = Object.keys(obj1);
513
- const keys2 = Object.keys(obj2);
514
- if (keys1.length !== keys2.length) return false;
515
- for (const key of keys1) if (obj1[key] !== obj2[key]) return false;
516
- return true;
517
- }
518
- /**
519
- * Check if a value is a React component type (function, class, forwardRef, memo, etc.)
520
- */
521
- function isReactComponentType(value) {
522
- if (typeof value === "function") return true;
523
- if (value && typeof value === "object" && "$$typeof" in value && !react.default.isValidElement(value)) return true;
524
- return false;
525
- }
526
- /**
527
- * Internal function to render a slot value as a React element (non-memoized).
528
- */
529
- function renderSlotElement(slot, DefaultComponent, props) {
530
- if (typeof slot === "string") {
531
- const existingClassName = props.className;
532
- return react.default.createElement(DefaultComponent, {
533
- ...props,
534
- className: (0, tailwind_merge.twMerge)(existingClassName, slot)
535
- });
536
- }
537
- if (isReactComponentType(slot)) return react.default.createElement(slot, props);
538
- if (slot && typeof slot === "object" && !react.default.isValidElement(slot)) return react.default.createElement(DefaultComponent, {
539
- ...props,
540
- ...slot
541
- });
542
- return react.default.createElement(DefaultComponent, props);
543
- }
544
- /**
545
- * Internal memoized wrapper component for renderSlot.
546
- * Uses forwardRef to support ref forwarding.
547
- */
548
- const MemoizedSlotWrapper = react.default.memo(react.default.forwardRef(function MemoizedSlotWrapper(props, ref) {
549
- const { $slot, $component, ...rest } = props;
550
- return renderSlotElement($slot, $component, ref !== null ? {
551
- ...rest,
552
- ref
553
- } : rest);
554
- }), (prev, next) => {
555
- if (prev.$slot !== next.$slot) return false;
556
- if (prev.$component !== next.$component) return false;
557
- const { $slot: _ps, $component: _pc, ...prevRest } = prev;
558
- const { $slot: _ns, $component: _nc, ...nextRest } = next;
559
- return shallowEqual(prevRest, nextRest);
560
- });
561
- /**
562
- * Renders a slot value as a memoized React element.
563
- * Automatically prevents unnecessary re-renders using shallow prop comparison.
564
- * Supports ref forwarding.
565
- *
566
- * @example
567
- * renderSlot(customInput, CopilotChatInput, { onSubmit: handleSubmit })
568
- */
569
- function renderSlot(slot, DefaultComponent, props) {
570
- return react.default.createElement(MemoizedSlotWrapper, {
571
- ...props,
572
- $slot: slot,
573
- $component: DefaultComponent
574
- });
575
- }
576
-
577
608
  //#endregion
578
609
  //#region src/v2/components/chat/CopilotChatInput.tsx
579
610
  const SLASH_MENU_MAX_VISIBLE_ITEMS = 5;
580
611
  const SLASH_MENU_ITEM_HEIGHT_PX = 40;
581
- function CopilotChatInput({ mode = "input", onSubmitMessage, onStop, isRunning = false, onStartTranscribe, onCancelTranscribe, onFinishTranscribe, onFinishTranscribeWithAudio, onAddFile, onChange, value, toolsMenu, autoFocus = true, positioning = "static", keyboardHeight = 0, containerRef, showDisclaimer, textArea, sendButton, startTranscribeButton, cancelTranscribeButton, finishTranscribeButton, addMenuButton, audioRecorder, disclaimer, children, className, ...props }) {
612
+ function CopilotChatInput({ mode = "input", onSubmitMessage, onStop, isRunning = false, onStartTranscribe, onCancelTranscribe, onFinishTranscribe, onFinishTranscribeWithAudio, onAddFile, onChange, value, toolsMenu, autoFocus = false, positioning = "static", keyboardHeight = 0, containerRef, showDisclaimer, textArea, sendButton, startTranscribeButton, cancelTranscribeButton, finishTranscribeButton, addMenuButton, audioRecorder, disclaimer, children, className, ...props }) {
582
613
  const isControlled = value !== void 0;
583
614
  const [internalValue, setInternalValue] = (0, react.useState)(() => value ?? "");
584
615
  (0, react.useEffect)(() => {
@@ -607,6 +638,7 @@ function CopilotChatInput({ mode = "input", onSubmitMessage, onStop, isRunning =
607
638
  paddingLeft: 0,
608
639
  paddingRight: 0
609
640
  });
641
+ const containerCacheRef = (0, react.useRef)(null);
610
642
  const commandItems = (0, react.useMemo)(() => {
611
643
  const entries = [];
612
644
  const seen = /* @__PURE__ */ new Set();
@@ -651,7 +683,7 @@ function CopilotChatInput({ mode = "input", onSubmitMessage, onStop, isRunning =
651
683
  previousModalStateRef.current = config?.isModalOpen;
652
684
  return;
653
685
  }
654
- if (config?.isModalOpen && !previousModalStateRef.current) inputRef.current?.focus();
686
+ if (config?.isModalOpen && !previousModalStateRef.current) inputRef.current?.focus({ preventScroll: true });
655
687
  previousModalStateRef.current = config?.isModalOpen;
656
688
  }, [config?.isModalOpen, autoFocus]);
657
689
  (0, react.useEffect)(() => {
@@ -894,6 +926,25 @@ function CopilotChatInput({ mode = "input", onSubmitMessage, onStop, isRunning =
894
926
  return nextLayout;
895
927
  });
896
928
  }, []);
929
+ const updateContainerCache = (0, react.useCallback)(() => {
930
+ const grid = gridRef.current;
931
+ const addContainer = addButtonContainerRef.current;
932
+ const actionsContainer = actionsContainerRef.current;
933
+ if (!grid || !addContainer || !actionsContainer) return null;
934
+ const gridStyles = window.getComputedStyle(grid);
935
+ const paddingLeft = parseFloat(gridStyles.paddingLeft) || 0;
936
+ const paddingRight = parseFloat(gridStyles.paddingRight) || 0;
937
+ const columnGap = parseFloat(gridStyles.columnGap) || 0;
938
+ const gridAvailableWidth = grid.clientWidth - paddingLeft - paddingRight;
939
+ if (gridAvailableWidth <= 0) return null;
940
+ const addWidth = addContainer.getBoundingClientRect().width;
941
+ const actionsWidth = actionsContainer.getBoundingClientRect().width;
942
+ const compactWidth = Math.max(gridAvailableWidth - addWidth - actionsWidth - columnGap * 2, 0);
943
+ if (compactWidth <= 0) return null;
944
+ const result = { compactWidth };
945
+ containerCacheRef.current = result;
946
+ return result;
947
+ }, []);
897
948
  const evaluateLayout = (0, react.useCallback)(() => {
898
949
  if (mode !== "input") {
899
950
  updateLayout("compact");
@@ -919,31 +970,31 @@ function CopilotChatInput({ mode = "input", onSubmitMessage, onStop, isRunning =
919
970
  const renderedMultiline = baseline > 0 ? scrollHeight > baseline + 1 : false;
920
971
  let shouldExpand = hasExplicitBreak || renderedMultiline;
921
972
  if (!shouldExpand) {
922
- const gridStyles = window.getComputedStyle(grid);
923
- const paddingLeft = parseFloat(gridStyles.paddingLeft) || 0;
924
- const paddingRight = parseFloat(gridStyles.paddingRight) || 0;
925
- const columnGap = parseFloat(gridStyles.columnGap) || 0;
926
- const gridAvailableWidth = grid.clientWidth - paddingLeft - paddingRight;
927
- if (gridAvailableWidth > 0) {
928
- const addWidth = addContainer.getBoundingClientRect().width;
929
- const actionsWidth = actionsContainer.getBoundingClientRect().width;
930
- const compactWidth = Math.max(gridAvailableWidth - addWidth - actionsWidth - columnGap * 2, 0);
931
- const canvas = measurementCanvasRef.current ?? document.createElement("canvas");
932
- if (!measurementCanvasRef.current) measurementCanvasRef.current = canvas;
933
- const context = canvas.getContext("2d");
934
- if (context) {
973
+ const cache = containerCacheRef.current ?? updateContainerCache();
974
+ if (cache && cache.compactWidth > 0) {
975
+ const compactInnerWidth = Math.max(cache.compactWidth - (measurementsRef.current.paddingLeft || 0) - (measurementsRef.current.paddingRight || 0), 0);
976
+ if (compactInnerWidth > 0) {
935
977
  const textareaStyles = window.getComputedStyle(textarea);
936
- context.font = textareaStyles.font || `${textareaStyles.fontStyle} ${textareaStyles.fontVariant} ${textareaStyles.fontWeight} ${textareaStyles.fontSize}/${textareaStyles.lineHeight} ${textareaStyles.fontFamily}`;
937
- const compactInnerWidth = Math.max(compactWidth - (measurementsRef.current.paddingLeft || 0) - (measurementsRef.current.paddingRight || 0), 0);
938
- if (compactInnerWidth > 0) {
939
- const lines = resolvedValue.length > 0 ? resolvedValue.split("\n") : [""];
940
- let longestWidth = 0;
941
- for (const line of lines) {
942
- const metrics = context.measureText(line || " ");
943
- if (metrics.width > longestWidth) longestWidth = metrics.width;
944
- }
945
- if (longestWidth > compactInnerWidth) shouldExpand = true;
978
+ let font = textareaStyles.font;
979
+ if (!font) {
980
+ const { fontStyle, fontVariant, fontWeight, fontSize, lineHeight, fontFamily } = textareaStyles;
981
+ if (fontSize && fontFamily) font = `${fontStyle} ${fontVariant} ${fontWeight} ${fontSize}/${lineHeight} ${fontFamily}`;
946
982
  }
983
+ if (font?.trim()) {
984
+ const canvas = measurementCanvasRef.current ?? document.createElement("canvas");
985
+ if (!measurementCanvasRef.current) measurementCanvasRef.current = canvas;
986
+ const context = canvas.getContext("2d");
987
+ if (context) {
988
+ context.font = font;
989
+ const lines = resolvedValue.length > 0 ? resolvedValue.split("\n") : [""];
990
+ let longestWidth = 0;
991
+ for (const line of lines) {
992
+ const metrics = context.measureText(line || " ");
993
+ if (metrics.width > longestWidth) longestWidth = metrics.width;
994
+ }
995
+ if (longestWidth > compactInnerWidth) shouldExpand = true;
996
+ } else if (process.env.NODE_ENV !== "production") console.warn("[CopilotChatInput] canvas.getContext('2d') returned null. Text-width-based expansion will be unavailable.");
997
+ } else if (process.env.NODE_ENV !== "production") console.warn("[CopilotChatInput] Could not resolve textarea font for layout measurement. Text-width-based expansion will be skipped until the next evaluation.");
947
998
  }
948
999
  }
949
1000
  }
@@ -953,6 +1004,7 @@ function CopilotChatInput({ mode = "input", onSubmitMessage, onStop, isRunning =
953
1004
  ensureMeasurements,
954
1005
  mode,
955
1006
  resolvedValue,
1007
+ updateContainerCache,
956
1008
  updateLayout
957
1009
  ]);
958
1010
  (0, react.useLayoutEffect)(() => {
@@ -965,11 +1017,17 @@ function CopilotChatInput({ mode = "input", onSubmitMessage, onStop, isRunning =
965
1017
  const addContainer = addButtonContainerRef.current;
966
1018
  const actionsContainer = actionsContainerRef.current;
967
1019
  if (!textarea || !grid || !addContainer || !actionsContainer) return;
968
- const scheduleEvaluation = () => {
1020
+ const containerTargets = new Set([
1021
+ grid,
1022
+ addContainer,
1023
+ actionsContainer
1024
+ ]);
1025
+ const scheduleEvaluation = (invalidateCache) => {
969
1026
  if (ignoreResizeRef.current) {
970
1027
  ignoreResizeRef.current = false;
971
1028
  return;
972
1029
  }
1030
+ if (invalidateCache) containerCacheRef.current = null;
973
1031
  if (typeof window === "undefined") {
974
1032
  evaluateLayout();
975
1033
  return;
@@ -980,8 +1038,13 @@ function CopilotChatInput({ mode = "input", onSubmitMessage, onStop, isRunning =
980
1038
  evaluateLayout();
981
1039
  });
982
1040
  };
983
- const observer = new ResizeObserver(() => {
984
- scheduleEvaluation();
1041
+ const observer = new ResizeObserver((entries) => {
1042
+ let shouldInvalidate = false;
1043
+ for (const entry of entries) if (containerTargets.has(entry.target)) {
1044
+ shouldInvalidate = true;
1045
+ break;
1046
+ }
1047
+ scheduleEvaluation(shouldInvalidate);
985
1048
  });
986
1049
  observer.observe(grid);
987
1050
  observer.observe(addContainer);
@@ -1126,6 +1189,8 @@ function CopilotChatInput({ mode = "input", onSubmitMessage, onStop, isRunning =
1126
1189
  });
1127
1190
  _CopilotChatInput.AddMenuButton = ({ className, toolsMenu, onAddFile, disabled, ...props }) => {
1128
1191
  const labels = useCopilotChatConfiguration()?.labels ?? CopilotChatDefaultLabels;
1192
+ const [mounted, setMounted] = (0, react.useState)(false);
1193
+ (0, react.useEffect)(() => setMounted(true), []);
1129
1194
  const menuItems = (0, react.useMemo)(() => {
1130
1195
  const items = [];
1131
1196
  if (onAddFile) items.push({
@@ -1156,26 +1221,28 @@ function CopilotChatInput({ mode = "input", onSubmitMessage, onStop, isRunning =
1156
1221
  }), []);
1157
1222
  const hasMenuItems = menuItems.length > 0;
1158
1223
  const isDisabled = disabled || !hasMenuItems;
1224
+ const button = /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Button, {
1225
+ type: "button",
1226
+ "data-testid": "copilot-add-menu-button",
1227
+ variant: "chatInputToolbarSecondary",
1228
+ size: "chatInputToolbarIcon",
1229
+ className: (0, tailwind_merge.twMerge)("cpk:ml-1", className),
1230
+ disabled: isDisabled,
1231
+ ...props,
1232
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.Plus, { className: "cpk:size-[20px]" })
1233
+ });
1234
+ if (!mounted) return button;
1159
1235
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(DropdownMenu, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)(Tooltip, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(TooltipTrigger, {
1160
1236
  asChild: true,
1161
1237
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DropdownMenuTrigger, {
1162
1238
  asChild: true,
1163
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Button, {
1164
- type: "button",
1165
- "data-testid": "copilot-add-menu-button",
1166
- variant: "chatInputToolbarSecondary",
1167
- size: "chatInputToolbarIcon",
1168
- className: (0, tailwind_merge.twMerge)("cpk:ml-1", className),
1169
- disabled: isDisabled,
1170
- ...props,
1171
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.Plus, { className: "cpk:size-[20px]" })
1172
- })
1239
+ children: button
1173
1240
  })
1174
1241
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(TooltipContent, {
1175
1242
  side: "bottom",
1176
1243
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", {
1177
1244
  className: "cpk:flex cpk:items-center cpk:gap-1 cpk:text-xs cpk:font-medium",
1178
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: "Add files and more" }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("code", {
1245
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: "Add attachments" }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("code", {
1179
1246
  className: "cpk:rounded cpk:bg-[#4a4a4a] cpk:px-1 cpk:py-[1px] cpk:font-mono cpk:text-[11px] cpk:text-white cpk:dark:bg-[#e0e0e0] cpk:dark:text-black",
1180
1247
  children: "/"
1181
1248
  })]
@@ -1191,21 +1258,7 @@ function CopilotChatInput({ mode = "input", onSubmitMessage, onStop, isRunning =
1191
1258
  const labels = useCopilotChatConfiguration()?.labels ?? CopilotChatDefaultLabels;
1192
1259
  (0, react.useImperativeHandle)(ref, () => internalTextareaRef.current);
1193
1260
  (0, react.useEffect)(() => {
1194
- const textarea = internalTextareaRef.current;
1195
- if (!textarea) return;
1196
- const handleFocus = () => {
1197
- setTimeout(() => {
1198
- textarea.scrollIntoView({
1199
- behavior: "smooth",
1200
- block: "nearest"
1201
- });
1202
- }, 300);
1203
- };
1204
- textarea.addEventListener("focus", handleFocus);
1205
- return () => textarea.removeEventListener("focus", handleFocus);
1206
- }, []);
1207
- (0, react.useEffect)(() => {
1208
- if (autoFocus) internalTextareaRef.current?.focus();
1261
+ if (autoFocus) internalTextareaRef.current?.focus({ preventScroll: true });
1209
1262
  }, [autoFocus]);
1210
1263
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("textarea", {
1211
1264
  ref: internalTextareaRef,
@@ -1913,125 +1966,981 @@ const MCPAppsActivityRenderer = function MCPAppsActivityRenderer({ content, agen
1913
1966
  };
1914
1967
 
1915
1968
  //#endregion
1916
- //#region src/v2/a2ui/A2UIMessageRenderer.tsx
1917
- let initialized = false;
1918
- function ensureInitialized() {
1919
- if (!initialized) {
1920
- (0, _copilotkit_a2ui_renderer.initializeDefaultCatalog)();
1921
- (0, _copilotkit_a2ui_renderer.injectStyles)();
1922
- initialized = true;
1923
- }
1969
+ //#region src/v2/providers/SandboxFunctionsContext.ts
1970
+ const SandboxFunctionsContext = (0, react.createContext)([]);
1971
+ function useSandboxFunctions() {
1972
+ return (0, react.useContext)(SandboxFunctionsContext);
1924
1973
  }
1925
- function createA2UIMessageRenderer(options) {
1926
- const { theme } = options;
1927
- return {
1928
- activityType: "a2ui-surface",
1929
- content: zod.z.any(),
1930
- render: ({ content, agent }) => {
1931
- ensureInitialized();
1932
- const [operations, setOperations] = (0, react.useState)([]);
1933
- const lastSignatureRef = (0, react.useRef)(null);
1934
- const { copilotkit } = useCopilotKit();
1935
- (0, react.useEffect)(() => {
1936
- if (!content || !Array.isArray(content.operations)) {
1937
- lastSignatureRef.current = null;
1938
- setOperations([]);
1939
- return;
1940
- }
1941
- const incoming = content.operations;
1942
- const signature = stringifyOperations(incoming);
1943
- if (signature && signature === lastSignatureRef.current) return;
1944
- lastSignatureRef.current = signature;
1945
- setOperations(incoming);
1946
- }, [content]);
1947
- const groupedOperations = (0, react.useMemo)(() => {
1948
- const groups = /* @__PURE__ */ new Map();
1949
- for (const operation of operations) {
1950
- const surfaceId = getOperationSurfaceId(operation) ?? _copilotkit_a2ui_renderer.DEFAULT_SURFACE_ID;
1951
- if (!groups.has(surfaceId)) groups.set(surfaceId, []);
1952
- groups.get(surfaceId).push(operation);
1953
- }
1954
- return groups;
1955
- }, [operations]);
1956
- if (!groupedOperations.size) return null;
1957
- return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1958
- className: "cpk:flex cpk:min-h-0 cpk:flex-1 cpk:flex-col cpk:gap-6 cpk:overflow-auto cpk:py-6",
1959
- children: Array.from(groupedOperations.entries()).map(([surfaceId, ops]) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ReactSurfaceHost, {
1960
- surfaceId,
1961
- operations: ops,
1962
- theme,
1963
- agent,
1964
- copilotkit
1965
- }, surfaceId))
1966
- });
1967
- }
1968
- };
1974
+
1975
+ //#endregion
1976
+ //#region src/v2/lib/processPartialHtml.ts
1977
+ /**
1978
+ * Extracts all complete `<style>` blocks from the raw HTML.
1979
+ * Returns the concatenated style tags, suitable for injection into `<head>`.
1980
+ */
1981
+ function extractCompleteStyles(html) {
1982
+ const matches = html.match(/<style\b[^>]*>[\s\S]*?<\/style>/gi);
1983
+ return matches ? matches.join("") : "";
1969
1984
  }
1970
1985
  /**
1971
- * Renders a single A2UI surface using the React renderer.
1972
- * Wraps A2UIProvider + A2UIRenderer and bridges actions back to CopilotKit.
1986
+ * Processes raw accumulated HTML for safe preview via innerHTML injection.
1987
+ * Pure function, no DOM dependencies.
1988
+ *
1989
+ * Pipeline (order matters):
1990
+ * 1. Strip incomplete tag at end
1991
+ * 2. Strip complete <style>, <script>, and <head> blocks
1992
+ * 3. Strip incomplete <style>/<script>/<head> blocks
1993
+ * 4. Strip incomplete HTML entities
1994
+ * 5. Extract body content (or use full string if no <body>)
1973
1995
  */
1974
- function ReactSurfaceHost({ surfaceId, operations, theme, agent, copilotkit }) {
1975
- return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1976
- className: "cpk:flex cpk:w-full cpk:flex-none cpk:flex-col cpk:gap-4",
1977
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_copilotkit_a2ui_renderer.A2UIProvider, {
1978
- onAction: (0, react.useCallback)(async (message) => {
1979
- if (!agent) return;
1980
- try {
1981
- console.info("[A2UI] Action dispatched", message.userAction);
1982
- copilotkit.setProperties({
1983
- ...copilotkit.properties ?? {},
1984
- a2uiAction: message
1985
- });
1986
- await copilotkit.runAgent({ agent });
1987
- } finally {
1988
- if (copilotkit.properties) {
1989
- const { a2uiAction, ...rest } = copilotkit.properties;
1990
- copilotkit.setProperties(rest);
1991
- }
1992
- }
1993
- }, [agent, copilotkit]),
1994
- theme,
1995
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(SurfaceMessageProcessor, {
1996
- surfaceId,
1997
- operations
1998
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_copilotkit_a2ui_renderer.A2UIRenderer, {
1999
- surfaceId,
2000
- className: "cpk:flex cpk:flex-1"
2001
- })]
2002
- })
2003
- });
1996
+ function processPartialHtml(html) {
1997
+ let result = html;
1998
+ result = result.replace(/<[^>]*$/, "");
1999
+ result = result.replace(/<(style|script|head)\b[^>]*>[\s\S]*?<\/\1>/gi, "");
2000
+ result = result.replace(/<(style|script|head)\b[^>]*>[\s\S]*$/gi, "");
2001
+ result = result.replace(/&[a-zA-Z0-9#]*$/, "");
2002
+ const bodyMatch = result.match(/<body[^>]*>([\s\S]*)/i);
2003
+ if (bodyMatch) {
2004
+ result = bodyMatch[1];
2005
+ result = result.replace(/<\/body>[\s\S]*/i, "");
2006
+ }
2007
+ return result;
2004
2008
  }
2009
+
2010
+ //#endregion
2011
+ //#region src/v2/components/OpenGenerativeUIRenderer.tsx
2012
+ const OpenGenerativeUIActivityType = "open-generative-ui";
2013
+ const OpenGenerativeUIContentSchema = zod.z.object({
2014
+ initialHeight: zod.z.number().optional(),
2015
+ generating: zod.z.boolean().optional(),
2016
+ css: zod.z.string().optional(),
2017
+ cssComplete: zod.z.boolean().optional(),
2018
+ html: zod.z.array(zod.z.string()).optional(),
2019
+ htmlComplete: zod.z.boolean().optional(),
2020
+ jsFunctions: zod.z.string().optional(),
2021
+ jsFunctionsComplete: zod.z.boolean().optional(),
2022
+ jsExpressions: zod.z.array(zod.z.string()).optional(),
2023
+ jsExpressionsComplete: zod.z.boolean().optional()
2024
+ });
2005
2025
  /**
2006
- * Processes A2UI operations into the provider's message processor.
2007
- * Must be a child of A2UIProvider to access the actions context.
2026
+ * Schema for the generateSandboxedUi tool call arguments.
2027
+ * Used by the frontend tool renderer to display placeholder messages.
2008
2028
  */
2009
- function SurfaceMessageProcessor({ surfaceId, operations }) {
2010
- const { processMessages } = (0, _copilotkit_a2ui_renderer.useA2UIActions)();
2011
- const lastProcessedRef = (0, react.useRef)("");
2012
- (0, react.useEffect)(() => {
2013
- const key = `${surfaceId}-${JSON.stringify(operations)}`;
2014
- if (key === lastProcessedRef.current) return;
2015
- lastProcessedRef.current = key;
2016
- processMessages(operations);
2029
+ const GenerateSandboxedUiArgsSchema = zod.z.object({
2030
+ initialHeight: zod.z.number().optional(),
2031
+ placeholderMessages: zod.z.array(zod.z.string()).optional(),
2032
+ css: zod.z.string().optional(),
2033
+ html: zod.z.string().optional(),
2034
+ jsFunctions: zod.z.string().optional(),
2035
+ jsExpressions: zod.z.array(zod.z.string()).optional()
2036
+ });
2037
+ const THROTTLE_MS = 1e3;
2038
+ /**
2039
+ * Returns true when the inner component should re-render immediately
2040
+ * (no throttle delay).
2041
+ */
2042
+ function shouldFlushImmediately(prev, next) {
2043
+ if (next.cssComplete && (!prev || !prev.cssComplete)) return true;
2044
+ if (next.htmlComplete) return true;
2045
+ if (next.generating === false) return true;
2046
+ if (next.jsFunctions && (!prev || !prev.jsFunctions)) return true;
2047
+ if ((next.jsExpressions?.length ?? 0) > (prev?.jsExpressions?.length ?? 0)) return true;
2048
+ if (next.html?.length && (!prev || !prev.html?.length)) return true;
2049
+ return false;
2050
+ }
2051
+ /**
2052
+ * Outer wrapper — absorbs every parent re-render but only forwards
2053
+ * throttled content snapshots to the memoized inner component.
2054
+ */
2055
+ const OpenGenerativeUIActivityRenderer = function OpenGenerativeUIActivityRenderer({ content }) {
2056
+ const latestContentRef = (0, react.useRef)(content);
2057
+ latestContentRef.current = content;
2058
+ const [throttledContent, setThrottledContent] = (0, react.useState)(content);
2059
+ const throttledContentRef = (0, react.useRef)(throttledContent);
2060
+ const timerRef = (0, react.useRef)(null);
2061
+ if (throttledContentRef.current !== content) {
2062
+ if (shouldFlushImmediately(throttledContentRef.current, content)) {
2063
+ if (timerRef.current !== null) {
2064
+ clearTimeout(timerRef.current);
2065
+ timerRef.current = null;
2066
+ }
2067
+ throttledContentRef.current = content;
2068
+ setThrottledContent(content);
2069
+ }
2070
+ }
2071
+ const flush = (0, react.useCallback)(() => {
2072
+ timerRef.current = null;
2073
+ const latest = latestContentRef.current;
2074
+ throttledContentRef.current = latest;
2075
+ setThrottledContent(latest);
2076
+ }, []);
2077
+ (0, react.useEffect)(() => {
2078
+ if (throttledContentRef.current === content) return;
2079
+ if (timerRef.current === null) timerRef.current = setTimeout(flush, THROTTLE_MS);
2080
+ }, [content, flush]);
2081
+ (0, react.useEffect)(() => {
2082
+ return () => {
2083
+ if (timerRef.current !== null) clearTimeout(timerRef.current);
2084
+ };
2085
+ }, []);
2086
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(OpenGenerativeUIActivityRendererInner, { content: throttledContent });
2087
+ };
2088
+ function ensureHead(html) {
2089
+ if (/<head[\s>]/i.test(html)) return html;
2090
+ return `<head></head>${html}`;
2091
+ }
2092
+ function injectCssIntoHtml(html, css) {
2093
+ const headCloseIdx = html.indexOf("</head>");
2094
+ if (headCloseIdx !== -1) return html.slice(0, headCloseIdx) + `<style>${css}</style>` + html.slice(headCloseIdx);
2095
+ return `<head><style>${css}</style></head>${html}`;
2096
+ }
2097
+ const OpenGenerativeUIActivityRendererInner = react.default.memo(function OpenGenerativeUIActivityRendererInner({ content }) {
2098
+ const initialHeight = content.initialHeight ?? 200;
2099
+ const [autoHeight, setAutoHeight] = (0, react.useState)(null);
2100
+ const sandboxFunctions = useSandboxFunctions();
2101
+ const localApi = (0, react.useMemo)(() => {
2102
+ const api = {};
2103
+ for (const fn of sandboxFunctions) api[fn.name] = fn.handler;
2104
+ return api;
2105
+ }, [sandboxFunctions]);
2106
+ const fullHtml = content.htmlComplete && content.html?.length ? content.html.join("") : void 0;
2107
+ const css = content.cssComplete ? content.css : void 0;
2108
+ const cssReady = !!content.cssComplete;
2109
+ const partialHtml = !content.htmlComplete && content.html?.length ? content.html.join("") : void 0;
2110
+ const previewBody = partialHtml ? processPartialHtml(partialHtml) : void 0;
2111
+ const previewStyles = partialHtml ? extractCompleteStyles(partialHtml) : "";
2112
+ const hasPreview = cssReady && !!previewBody?.trim();
2113
+ const hasVisibleSandbox = !!fullHtml || hasPreview;
2114
+ const containerRef = (0, react.useRef)(null);
2115
+ const sandboxRef = (0, react.useRef)(null);
2116
+ const previewSandboxRef = (0, react.useRef)(null);
2117
+ const previewReadyRef = (0, react.useRef)(false);
2118
+ const sandboxReadyRef = (0, react.useRef)(false);
2119
+ const executedIndexRef = (0, react.useRef)(0);
2120
+ const pendingQueueRef = (0, react.useRef)([]);
2121
+ const jsFunctionsInjectedRef = (0, react.useRef)(false);
2122
+ (0, react.useEffect)(() => {
2123
+ const container = containerRef.current;
2124
+ if (!container || fullHtml || !hasPreview || previewSandboxRef.current) return;
2125
+ let cancelled = false;
2126
+ import("@jetbrains/websandbox").then((mod) => {
2127
+ if (cancelled) return;
2128
+ const sandbox = (mod.default?.default ?? mod.default).create({}, {
2129
+ frameContainer: container,
2130
+ frameContent: "<head></head><body></body>",
2131
+ allowAdditionalAttributes: ""
2132
+ });
2133
+ previewSandboxRef.current = sandbox;
2134
+ sandbox.iframe.style.width = "100%";
2135
+ sandbox.iframe.style.height = "100%";
2136
+ sandbox.iframe.style.border = "none";
2137
+ sandbox.iframe.style.backgroundColor = "transparent";
2138
+ sandbox.promise.then(() => {
2139
+ if (cancelled) return;
2140
+ previewReadyRef.current = true;
2141
+ sandbox.run(`
2142
+ var s = document.createElement('style');
2143
+ s.textContent = 'html, body { overflow: hidden !important; }';
2144
+ document.head.appendChild(s);
2145
+ `);
2146
+ const headParts = [];
2147
+ if (css) headParts.push(`<style>${css}</style>`);
2148
+ if (previewStyles) headParts.push(previewStyles);
2149
+ if (headParts.length) sandbox.run(`document.head.innerHTML = ${JSON.stringify(headParts.join(""))}`);
2150
+ if (previewBody) sandbox.run(`document.body.innerHTML = ${JSON.stringify(previewBody)}`);
2151
+ });
2152
+ }).catch((err) => {
2153
+ console.error("[OpenGenerativeUI] Failed to load sandbox module:", err);
2154
+ });
2155
+ return () => {
2156
+ cancelled = true;
2157
+ };
2158
+ }, [hasPreview, fullHtml]);
2159
+ (0, react.useEffect)(() => {
2160
+ if (!previewSandboxRef.current || !previewReadyRef.current) return;
2161
+ const headParts = [];
2162
+ if (css) headParts.push(`<style>${css}</style>`);
2163
+ if (previewStyles) headParts.push(previewStyles);
2164
+ if (headParts.length) previewSandboxRef.current.run(`document.head.innerHTML = ${JSON.stringify(headParts.join(""))}`);
2165
+ if (!previewBody) return;
2166
+ previewSandboxRef.current.run(`document.body.innerHTML = ${JSON.stringify(previewBody)}`);
2167
+ }, [
2168
+ previewBody,
2169
+ previewStyles,
2170
+ css
2171
+ ]);
2172
+ (0, react.useEffect)(() => {
2173
+ const container = containerRef.current;
2174
+ if (!container || !fullHtml) return;
2175
+ if (previewSandboxRef.current) {
2176
+ previewSandboxRef.current.destroy();
2177
+ previewSandboxRef.current = null;
2178
+ previewReadyRef.current = false;
2179
+ }
2180
+ let cancelled = false;
2181
+ executedIndexRef.current = 0;
2182
+ jsFunctionsInjectedRef.current = false;
2183
+ sandboxReadyRef.current = false;
2184
+ pendingQueueRef.current = [];
2185
+ const htmlContent = css ? injectCssIntoHtml(fullHtml, css) : fullHtml;
2186
+ import("@jetbrains/websandbox").then((mod) => {
2187
+ if (cancelled) return;
2188
+ const sandbox = (mod.default?.default ?? mod.default).create(localApi, {
2189
+ frameContainer: container,
2190
+ frameContent: ensureHead(htmlContent),
2191
+ allowAdditionalAttributes: ""
2192
+ });
2193
+ sandboxRef.current = sandbox;
2194
+ sandbox.iframe.style.width = "100%";
2195
+ sandbox.iframe.style.height = "100%";
2196
+ sandbox.iframe.style.border = "none";
2197
+ sandbox.iframe.style.backgroundColor = "transparent";
2198
+ sandbox.promise.then(() => {
2199
+ if (cancelled) return;
2200
+ sandboxReadyRef.current = true;
2201
+ sandbox.run(`
2202
+ var s = document.createElement('style');
2203
+ s.textContent = 'html, body { overflow: hidden !important; }';
2204
+ document.head.appendChild(s);
2205
+ `);
2206
+ const queue = pendingQueueRef.current;
2207
+ pendingQueueRef.current = [];
2208
+ for (const code of queue) sandbox.run(code);
2209
+ });
2210
+ }).catch((err) => {
2211
+ console.error("[OpenGenerativeUI] Failed to load sandbox module:", err);
2212
+ });
2213
+ return () => {
2214
+ cancelled = true;
2215
+ if (previewSandboxRef.current) {
2216
+ previewSandboxRef.current.destroy();
2217
+ previewSandboxRef.current = null;
2218
+ previewReadyRef.current = false;
2219
+ }
2220
+ if (sandboxRef.current) {
2221
+ sandboxRef.current.destroy();
2222
+ sandboxRef.current = null;
2223
+ }
2224
+ sandboxReadyRef.current = false;
2225
+ setAutoHeight(null);
2226
+ };
2227
+ }, [
2228
+ fullHtml,
2229
+ css,
2230
+ localApi
2231
+ ]);
2232
+ (0, react.useEffect)(() => {
2233
+ if (!content.jsFunctions || jsFunctionsInjectedRef.current) return;
2234
+ jsFunctionsInjectedRef.current = true;
2235
+ const sandbox = sandboxRef.current;
2236
+ if (sandboxReadyRef.current && sandbox) sandbox.run(content.jsFunctions);
2237
+ else pendingQueueRef.current.push(content.jsFunctions);
2238
+ }, [content.jsFunctions]);
2239
+ (0, react.useEffect)(() => {
2240
+ const expressions = content.jsExpressions;
2241
+ if (!expressions || expressions.length === 0) return;
2242
+ const startIndex = executedIndexRef.current;
2243
+ if (startIndex >= expressions.length) return;
2244
+ const newExprs = expressions.slice(startIndex);
2245
+ executedIndexRef.current = expressions.length;
2246
+ const sandbox = sandboxRef.current;
2247
+ if (sandboxReadyRef.current && sandbox) (async () => {
2248
+ for (const expr of newExprs) await sandbox.run(expr);
2249
+ })();
2250
+ else pendingQueueRef.current.push(...newExprs);
2251
+ }, [content.jsExpressions?.length]);
2252
+ const generationDone = content.generating === false;
2253
+ (0, react.useEffect)(() => {
2254
+ const sandbox = sandboxRef.current;
2255
+ if (!generationDone || !sandbox) return;
2256
+ let handled = false;
2257
+ const onMessage = (e) => {
2258
+ if (handled) return;
2259
+ if (e.source === sandbox.iframe.contentWindow && e.data?.type === "__ck_resize") {
2260
+ handled = true;
2261
+ setAutoHeight(e.data.height);
2262
+ window.removeEventListener("message", onMessage);
2263
+ }
2264
+ };
2265
+ window.addEventListener("message", onMessage);
2266
+ const measureOnce = `
2267
+ (function() {
2268
+ var s = document.createElement('style');
2269
+ s.textContent = 'body { height: auto !important; min-height: 0 !important; }';
2270
+ document.head.appendChild(s);
2271
+ var h = document.body.scrollHeight;
2272
+ var cs = getComputedStyle(document.body);
2273
+ h += parseFloat(cs.marginTop) || 0;
2274
+ h += parseFloat(cs.marginBottom) || 0;
2275
+ s.remove();
2276
+ parent.postMessage({ type: "__ck_resize", height: Math.ceil(h) }, "*");
2277
+ })();
2278
+ `;
2279
+ if (sandboxReadyRef.current) sandbox.run(measureOnce);
2280
+ else pendingQueueRef.current.push(measureOnce);
2281
+ return () => {
2282
+ window.removeEventListener("message", onMessage);
2283
+ };
2284
+ }, [generationDone]);
2285
+ const height = autoHeight ?? initialHeight;
2286
+ const isGenerating = content.generating !== false;
2287
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2288
+ ref: containerRef,
2289
+ style: {
2290
+ position: "relative",
2291
+ width: "100%",
2292
+ height: `${height}px`,
2293
+ borderRadius: "8px",
2294
+ backgroundColor: hasVisibleSandbox ? "transparent" : "#f5f5f5",
2295
+ border: hasVisibleSandbox ? "none" : "1px solid #e0e0e0",
2296
+ display: hasVisibleSandbox ? "block" : "flex",
2297
+ alignItems: hasVisibleSandbox ? void 0 : "center",
2298
+ justifyContent: hasVisibleSandbox ? void 0 : "center",
2299
+ overflow: "hidden"
2300
+ },
2301
+ children: isGenerating && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2302
+ style: {
2303
+ position: "absolute",
2304
+ inset: 0,
2305
+ zIndex: 10,
2306
+ pointerEvents: "all",
2307
+ backgroundColor: "rgba(255, 255, 255, 0.5)",
2308
+ display: "flex",
2309
+ alignItems: "center",
2310
+ justifyContent: "center"
2311
+ },
2312
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("svg", {
2313
+ width: "48",
2314
+ height: "48",
2315
+ viewBox: "0 0 24 24",
2316
+ fill: "none",
2317
+ style: { animation: "ck-spin 1s linear infinite" },
2318
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("circle", {
2319
+ cx: "12",
2320
+ cy: "12",
2321
+ r: "10",
2322
+ stroke: "#e0e0e0",
2323
+ strokeWidth: "3"
2324
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
2325
+ d: "M12 2a10 10 0 0 1 10 10",
2326
+ stroke: "#999",
2327
+ strokeWidth: "3",
2328
+ strokeLinecap: "round"
2329
+ })]
2330
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("style", { children: `@keyframes ck-spin { to { transform: rotate(360deg) } }` })]
2331
+ })
2332
+ });
2333
+ }, (prev, next) => prev.content === next.content);
2334
+ /**
2335
+ * Frontend tool renderer for generateSandboxedUi.
2336
+ * Displays placeholder messages while the UI is being generated.
2337
+ */
2338
+ const OpenGenerativeUIToolRenderer = function OpenGenerativeUIToolRenderer(props) {
2339
+ const [visibleMessageIndex, setVisibleMessageIndex] = (0, react.useState)(0);
2340
+ const prevMessageCountRef = (0, react.useRef)(0);
2341
+ const messages = props.args.placeholderMessages;
2342
+ (0, react.useEffect)(() => {
2343
+ if (!messages || messages.length === 0) return;
2344
+ if (messages.length !== prevMessageCountRef.current) {
2345
+ prevMessageCountRef.current = messages.length;
2346
+ setVisibleMessageIndex(messages.length - 1);
2347
+ }
2348
+ if (props.status === _copilotkit_core.ToolCallStatus.Complete) return;
2349
+ const timer = setInterval(() => {
2350
+ setVisibleMessageIndex((i) => (i + 1) % messages.length);
2351
+ }, 5e3);
2352
+ return () => clearInterval(timer);
2353
+ }, [messages?.length, props.status]);
2354
+ if (props.status === _copilotkit_core.ToolCallStatus.Complete) return null;
2355
+ if (!messages || messages.length === 0) return null;
2356
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2357
+ style: {
2358
+ padding: "8px 12px",
2359
+ color: "#999",
2360
+ fontSize: "14px"
2361
+ },
2362
+ children: messages[visibleMessageIndex] ?? messages[0]
2363
+ });
2364
+ };
2365
+
2366
+ //#endregion
2367
+ //#region src/v2/a2ui/A2UIMessageRenderer.tsx
2368
+ /**
2369
+ * The container key used to wrap A2UI operations for explicit detection.
2370
+ * Must match A2UI_OPERATIONS_KEY in @ag-ui/a2ui-middleware and copilotkit.a2ui (Python).
2371
+ */
2372
+ const A2UI_OPERATIONS_KEY = "a2ui_operations";
2373
+ let initialized = false;
2374
+ function ensureInitialized() {
2375
+ if (!initialized) {
2376
+ (0, _copilotkit_a2ui_renderer.initializeDefaultCatalog)();
2377
+ (0, _copilotkit_a2ui_renderer.injectStyles)();
2378
+ initialized = true;
2379
+ }
2380
+ }
2381
+ function createA2UIMessageRenderer(options) {
2382
+ const { theme, catalog, loadingComponent } = options;
2383
+ return {
2384
+ activityType: "a2ui-surface",
2385
+ content: zod.z.any(),
2386
+ render: ({ content, agent }) => {
2387
+ ensureInitialized();
2388
+ const [operations, setOperations] = (0, react.useState)([]);
2389
+ const { copilotkit } = useCopilotKit();
2390
+ const lastContentRef = (0, react.useRef)(null);
2391
+ (0, react.useEffect)(() => {
2392
+ if (content === lastContentRef.current) return;
2393
+ lastContentRef.current = content;
2394
+ const incoming = content?.[A2UI_OPERATIONS_KEY];
2395
+ if (!content || !Array.isArray(incoming)) {
2396
+ setOperations([]);
2397
+ return;
2398
+ }
2399
+ setOperations(incoming);
2400
+ }, [content]);
2401
+ const groupedOperations = (0, react.useMemo)(() => {
2402
+ const groups = /* @__PURE__ */ new Map();
2403
+ for (const operation of operations) {
2404
+ const surfaceId = getOperationSurfaceId(operation) ?? _copilotkit_a2ui_renderer.DEFAULT_SURFACE_ID;
2405
+ if (!groups.has(surfaceId)) groups.set(surfaceId, []);
2406
+ groups.get(surfaceId).push(operation);
2407
+ }
2408
+ return groups;
2409
+ }, [operations]);
2410
+ if (!groupedOperations.size) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(loadingComponent ?? DefaultA2UILoading, {});
2411
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2412
+ className: "cpk:flex cpk:min-h-0 cpk:flex-1 cpk:flex-col cpk:gap-6 cpk:overflow-auto cpk:py-6",
2413
+ children: Array.from(groupedOperations.entries()).map(([surfaceId, ops]) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ReactSurfaceHost, {
2414
+ surfaceId,
2415
+ operations: ops,
2416
+ theme,
2417
+ agent,
2418
+ copilotkit,
2419
+ catalog
2420
+ }, surfaceId))
2421
+ });
2422
+ }
2423
+ };
2424
+ }
2425
+ /**
2426
+ * Renders a single A2UI surface using the React renderer.
2427
+ * Wraps A2UIProvider + A2UIRenderer and bridges actions back to CopilotKit.
2428
+ */
2429
+ function ReactSurfaceHost({ surfaceId, operations, theme, agent, copilotkit, catalog }) {
2430
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2431
+ className: "cpk:flex cpk:w-full cpk:flex-none cpk:flex-col cpk:gap-4",
2432
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_copilotkit_a2ui_renderer.A2UIProvider, {
2433
+ onAction: (0, react.useCallback)(async (message) => {
2434
+ if (!agent) return;
2435
+ message.userAction;
2436
+ try {
2437
+ copilotkit.setProperties({
2438
+ ...copilotkit.properties ?? {},
2439
+ a2uiAction: message
2440
+ });
2441
+ await copilotkit.runAgent({ agent });
2442
+ } finally {
2443
+ if (copilotkit.properties) {
2444
+ const { a2uiAction, ...rest } = copilotkit.properties;
2445
+ copilotkit.setProperties(rest);
2446
+ }
2447
+ }
2448
+ }, [agent, copilotkit]),
2449
+ theme,
2450
+ catalog,
2451
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(SurfaceMessageProcessor, {
2452
+ surfaceId,
2453
+ operations
2454
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(A2UISurfaceOrError, { surfaceId })]
2455
+ })
2456
+ });
2457
+ }
2458
+ /**
2459
+ * Renders the A2UI surface, or an error message if processing failed.
2460
+ * Must be a child of A2UIProvider to access the error state.
2461
+ */
2462
+ function A2UISurfaceOrError({ surfaceId }) {
2463
+ const error = (0, _copilotkit_a2ui_renderer.useA2UIError)();
2464
+ if (error) return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2465
+ className: "cpk:rounded-lg cpk:border cpk:border-red-200 cpk:bg-red-50 cpk:p-3 cpk:text-sm cpk:text-red-700",
2466
+ children: ["A2UI render error: ", error]
2467
+ });
2468
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_copilotkit_a2ui_renderer.A2UIRenderer, {
2469
+ surfaceId,
2470
+ className: "cpk:flex cpk:flex-1"
2471
+ });
2472
+ }
2473
+ /**
2474
+ * Processes A2UI operations into the provider's message processor.
2475
+ * Must be a child of A2UIProvider to access the actions context.
2476
+ */
2477
+ function SurfaceMessageProcessor({ surfaceId, operations }) {
2478
+ const { processMessages, getSurface } = (0, _copilotkit_a2ui_renderer.useA2UIActions)();
2479
+ const lastHashRef = (0, react.useRef)("");
2480
+ (0, react.useEffect)(() => {
2481
+ const hash = JSON.stringify(operations);
2482
+ if (hash === lastHashRef.current) return;
2483
+ lastHashRef.current = hash;
2484
+ processMessages(getSurface(surfaceId) ? operations.filter((op) => !op?.createSurface) : operations);
2017
2485
  }, [
2018
2486
  processMessages,
2487
+ getSurface,
2019
2488
  surfaceId,
2020
2489
  operations
2021
2490
  ]);
2022
2491
  return null;
2023
2492
  }
2493
+ /**
2494
+ * Default loading component shown while an A2UI surface is generating.
2495
+ * Displays an animated shimmer skeleton.
2496
+ */
2497
+ function DefaultA2UILoading() {
2498
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2499
+ className: "cpk:flex cpk:flex-col cpk:gap-3 cpk:rounded-xl cpk:border cpk:border-gray-100 cpk:bg-gray-50/50 cpk:p-5",
2500
+ style: { minHeight: 120 },
2501
+ children: [
2502
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2503
+ className: "cpk:flex cpk:items-center cpk:gap-2",
2504
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2505
+ className: "cpk:h-3 cpk:w-3 cpk:rounded-full cpk:bg-gray-200",
2506
+ style: { animation: "cpk-a2ui-pulse 1.5s ease-in-out infinite" }
2507
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2508
+ className: "cpk:text-xs cpk:font-medium cpk:text-gray-400",
2509
+ children: "Generating UI..."
2510
+ })]
2511
+ }),
2512
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2513
+ className: "cpk:flex cpk:flex-col cpk:gap-2",
2514
+ children: [
2515
+ .8,
2516
+ .6,
2517
+ .4
2518
+ ].map((width, i) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2519
+ className: "cpk:h-3 cpk:rounded cpk:bg-gray-200/70",
2520
+ style: {
2521
+ width: `${width * 100}%`,
2522
+ animation: `cpk-a2ui-pulse 1.5s ease-in-out ${i * .15}s infinite`
2523
+ }
2524
+ }, i))
2525
+ }),
2526
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("style", { children: `
2527
+ @keyframes cpk-a2ui-pulse {
2528
+ 0%, 100% { opacity: 0.4; }
2529
+ 50% { opacity: 1; }
2530
+ }
2531
+ ` })
2532
+ ]
2533
+ });
2534
+ }
2024
2535
  function getOperationSurfaceId(operation) {
2025
2536
  if (!operation || typeof operation !== "object") return null;
2026
2537
  if (typeof operation.surfaceId === "string") return operation.surfaceId;
2027
- return operation?.beginRendering?.surfaceId ?? operation?.surfaceUpdate?.surfaceId ?? operation?.dataModelUpdate?.surfaceId ?? operation?.deleteSurface?.surfaceId ?? null;
2538
+ return operation?.createSurface?.surfaceId ?? operation?.updateComponents?.surfaceId ?? operation?.updateDataModel?.surfaceId ?? operation?.deleteSurface?.surfaceId ?? null;
2028
2539
  }
2029
- function stringifyOperations(ops) {
2030
- try {
2031
- return JSON.stringify(ops);
2032
- } catch (error) {
2033
- return null;
2540
+
2541
+ //#endregion
2542
+ //#region src/v2/types/defineToolCallRenderer.ts
2543
+ function defineToolCallRenderer(def) {
2544
+ const argsSchema = def.name === "*" && !def.args ? zod.z.any() : def.args;
2545
+ return {
2546
+ name: def.name,
2547
+ args: argsSchema,
2548
+ render: def.render,
2549
+ ...def.agentId ? { agentId: def.agentId } : {}
2550
+ };
2551
+ }
2552
+
2553
+ //#endregion
2554
+ //#region src/v2/a2ui/A2UIToolCallRenderer.tsx
2555
+ /**
2556
+ * Tool name used by the dynamic A2UI generation secondary LLM.
2557
+ * This renderer is auto-registered when A2UI is enabled.
2558
+ */
2559
+ const RENDER_A2UI_TOOL_NAME = "render_a2ui";
2560
+ /**
2561
+ * Built-in progress indicator for dynamic A2UI generation.
2562
+ * Shows a skeleton wireframe that progressively reveals as tokens stream in.
2563
+ *
2564
+ * Registered automatically when A2UI is enabled. Users can override by
2565
+ * providing their own `useRenderTool({ name: "render_a2ui", ... })`.
2566
+ */
2567
+ function A2UIProgressIndicator({ parameters }) {
2568
+ const lastRef = (0, react.useRef)({
2569
+ time: 0,
2570
+ tokens: 0
2571
+ });
2572
+ const now = Date.now();
2573
+ let { tokens } = lastRef.current;
2574
+ if (now - lastRef.current.time > 200) {
2575
+ const chars = JSON.stringify(parameters ?? {}).length;
2576
+ tokens = Math.round(chars / 4);
2577
+ lastRef.current = {
2578
+ time: now,
2579
+ tokens
2580
+ };
2034
2581
  }
2582
+ const phase = tokens < 50 ? 0 : tokens < 200 ? 1 : tokens < 400 ? 2 : 3;
2583
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2584
+ style: {
2585
+ margin: "12px 0",
2586
+ maxWidth: 320
2587
+ },
2588
+ children: [
2589
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2590
+ style: {
2591
+ position: "relative",
2592
+ overflow: "hidden",
2593
+ borderRadius: 12,
2594
+ border: "1px solid rgba(228,228,231,0.8)",
2595
+ backgroundColor: "#fff",
2596
+ boxShadow: "0 1px 2px rgba(0,0,0,0.04)",
2597
+ padding: "16px 18px 14px"
2598
+ },
2599
+ children: [
2600
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2601
+ style: {
2602
+ display: "flex",
2603
+ alignItems: "center",
2604
+ gap: 8,
2605
+ marginBottom: 12
2606
+ },
2607
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2608
+ style: {
2609
+ display: "flex",
2610
+ gap: 4
2611
+ },
2612
+ children: [
2613
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Dot, {}),
2614
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Dot, {}),
2615
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Dot, {})
2616
+ ]
2617
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Bar, {
2618
+ w: 64,
2619
+ h: 6,
2620
+ bg: "#e4e4e7",
2621
+ opacity: phase >= 1 ? 1 : .4,
2622
+ transition: "opacity 0.5s"
2623
+ })]
2624
+ }),
2625
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2626
+ style: {
2627
+ display: "grid",
2628
+ gap: 7
2629
+ },
2630
+ children: [
2631
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(Row, {
2632
+ show: phase >= 0,
2633
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Bar, {
2634
+ w: 36,
2635
+ h: 7,
2636
+ bg: "rgba(147,197,253,0.7)",
2637
+ anim: 0
2638
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Bar, {
2639
+ w: 80,
2640
+ h: 7,
2641
+ bg: "rgba(219,234,254,0.8)",
2642
+ anim: .2
2643
+ })]
2644
+ }),
2645
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(Row, {
2646
+ show: phase >= 0,
2647
+ delay: .1,
2648
+ children: [
2649
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Spacer, {}),
2650
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Dot, {}),
2651
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Bar, {
2652
+ w: 100,
2653
+ h: 7,
2654
+ bg: "rgba(24,24,27,0.2)",
2655
+ anim: .3
2656
+ })
2657
+ ]
2658
+ }),
2659
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(Row, {
2660
+ show: phase >= 1,
2661
+ delay: .15,
2662
+ children: [
2663
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Spacer, {}),
2664
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Bar, {
2665
+ w: 48,
2666
+ h: 7,
2667
+ bg: "rgba(24,24,27,0.15)",
2668
+ anim: .1
2669
+ }),
2670
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Bar, {
2671
+ w: 40,
2672
+ h: 7,
2673
+ bg: "rgba(153,246,228,0.6)",
2674
+ anim: .5
2675
+ }),
2676
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Bar, {
2677
+ w: 56,
2678
+ h: 7,
2679
+ bg: "rgba(147,197,253,0.6)",
2680
+ anim: .3
2681
+ })
2682
+ ]
2683
+ }),
2684
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(Row, {
2685
+ show: phase >= 1,
2686
+ delay: .2,
2687
+ children: [
2688
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Spacer, {}),
2689
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Dot, {}),
2690
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Bar, {
2691
+ w: 60,
2692
+ h: 7,
2693
+ bg: "rgba(24,24,27,0.15)",
2694
+ anim: .4
2695
+ })
2696
+ ]
2697
+ }),
2698
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(Row, {
2699
+ show: phase >= 2,
2700
+ delay: .25,
2701
+ children: [
2702
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Bar, {
2703
+ w: 40,
2704
+ h: 7,
2705
+ bg: "rgba(153,246,228,0.5)",
2706
+ anim: .2
2707
+ }),
2708
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Dot, {}),
2709
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Bar, {
2710
+ w: 48,
2711
+ h: 7,
2712
+ bg: "rgba(24,24,27,0.15)",
2713
+ anim: .6
2714
+ }),
2715
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Bar, {
2716
+ w: 64,
2717
+ h: 7,
2718
+ bg: "rgba(147,197,253,0.5)",
2719
+ anim: .1
2720
+ })
2721
+ ]
2722
+ }),
2723
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(Row, {
2724
+ show: phase >= 2,
2725
+ delay: .3,
2726
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Bar, {
2727
+ w: 36,
2728
+ h: 7,
2729
+ bg: "rgba(147,197,253,0.6)",
2730
+ anim: .5
2731
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Bar, {
2732
+ w: 36,
2733
+ h: 7,
2734
+ bg: "rgba(24,24,27,0.12)",
2735
+ anim: .7
2736
+ })]
2737
+ }),
2738
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(Row, {
2739
+ show: phase >= 3,
2740
+ delay: .35,
2741
+ children: [
2742
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Dot, {}),
2743
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Bar, {
2744
+ w: 44,
2745
+ h: 7,
2746
+ bg: "rgba(24,24,27,0.18)",
2747
+ anim: .3
2748
+ }),
2749
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Dot, {}),
2750
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Bar, {
2751
+ w: 56,
2752
+ h: 7,
2753
+ bg: "rgba(153,246,228,0.5)",
2754
+ anim: .8
2755
+ }),
2756
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Bar, {
2757
+ w: 48,
2758
+ h: 7,
2759
+ bg: "rgba(147,197,253,0.5)",
2760
+ anim: .4
2761
+ })
2762
+ ]
2763
+ })
2764
+ ]
2765
+ }),
2766
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { style: {
2767
+ pointerEvents: "none",
2768
+ position: "absolute",
2769
+ inset: 0,
2770
+ background: "linear-gradient(105deg, transparent 0%, transparent 40%, rgba(255,255,255,0.6) 50%, transparent 60%, transparent 100%)",
2771
+ backgroundSize: "250% 100%",
2772
+ animation: "cpk-a2ui-sweep 3s ease-in-out infinite"
2773
+ } })
2774
+ ]
2775
+ }),
2776
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2777
+ style: {
2778
+ display: "flex",
2779
+ alignItems: "center",
2780
+ justifyContent: "center",
2781
+ gap: 8,
2782
+ marginTop: 8
2783
+ },
2784
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2785
+ style: {
2786
+ fontSize: 12,
2787
+ color: "#a1a1aa",
2788
+ letterSpacing: "0.025em"
2789
+ },
2790
+ children: "Building interface"
2791
+ }), tokens > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
2792
+ style: {
2793
+ fontSize: 11,
2794
+ color: "#d4d4d8",
2795
+ fontVariantNumeric: "tabular-nums"
2796
+ },
2797
+ children: [
2798
+ "~",
2799
+ tokens.toLocaleString(),
2800
+ " tokens"
2801
+ ]
2802
+ })]
2803
+ }),
2804
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("style", { children: `
2805
+ @keyframes cpk-a2ui-fade {
2806
+ 0%, 100% { opacity: 1; }
2807
+ 50% { opacity: 0.5; }
2808
+ }
2809
+ @keyframes cpk-a2ui-sweep {
2810
+ 0% { background-position: 250% 0; }
2811
+ 100% { background-position: -250% 0; }
2812
+ }
2813
+ ` })
2814
+ ]
2815
+ });
2816
+ }
2817
+ function Dot() {
2818
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { style: {
2819
+ width: 7,
2820
+ height: 7,
2821
+ borderRadius: "50%",
2822
+ backgroundColor: "#d4d4d8",
2823
+ flexShrink: 0
2824
+ } });
2825
+ }
2826
+ function Spacer() {
2827
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { style: { width: 12 } });
2828
+ }
2829
+ function Bar({ w, h, bg, anim, opacity, transition }) {
2830
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { style: {
2831
+ width: w,
2832
+ height: h,
2833
+ borderRadius: 9999,
2834
+ backgroundColor: bg,
2835
+ ...anim !== void 0 ? { animation: `cpk-a2ui-fade 2.4s ease-in-out ${anim}s infinite` } : {},
2836
+ ...opacity !== void 0 ? { opacity } : {},
2837
+ ...transition ? { transition } : {}
2838
+ } });
2839
+ }
2840
+ function Row({ children, show, delay = 0 }) {
2841
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2842
+ style: {
2843
+ display: "flex",
2844
+ alignItems: "center",
2845
+ gap: 6,
2846
+ opacity: show ? 1 : 0,
2847
+ transition: `opacity 0.4s ${delay}s`
2848
+ },
2849
+ children
2850
+ });
2851
+ }
2852
+ /**
2853
+ * Registers the built-in `render_a2ui` tool call renderer via the props-based
2854
+ * `setRenderToolCalls` mechanism (not `useRenderTool`).
2855
+ *
2856
+ * This ensures user-registered `useRenderTool({ name: "render_a2ui", ... })`
2857
+ * hooks automatically override the built-in, since the merge logic in
2858
+ * react-core.ts gives hook-based entries priority over prop-based entries.
2859
+ */
2860
+ function A2UIBuiltInToolCallRenderer() {
2861
+ const { copilotkit } = useCopilotKit();
2862
+ (0, react.useEffect)(() => {
2863
+ const renderer = defineToolCallRenderer({
2864
+ name: RENDER_A2UI_TOOL_NAME,
2865
+ args: zod.z.any(),
2866
+ render: ({ status, args: parameters }) => {
2867
+ if (status === "complete") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_jsx_runtime.Fragment, {});
2868
+ const params = parameters;
2869
+ const items = params?.items;
2870
+ if (Array.isArray(items) && items.length > 0) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_jsx_runtime.Fragment, {});
2871
+ const components = params?.components;
2872
+ if (Array.isArray(components) && components.length > 2) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_jsx_runtime.Fragment, {});
2873
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(A2UIProgressIndicator, { parameters });
2874
+ }
2875
+ });
2876
+ const existing = copilotkit._renderToolCalls ?? [];
2877
+ copilotkit.setRenderToolCalls([...existing.filter((rc) => rc.name !== RENDER_A2UI_TOOL_NAME), renderer]);
2878
+ }, [copilotkit]);
2879
+ return null;
2880
+ }
2881
+
2882
+ //#endregion
2883
+ //#region src/v2/hooks/use-agent-context.tsx
2884
+ function useAgentContext(context) {
2885
+ const { description, value } = context;
2886
+ const { copilotkit } = useCopilotKit();
2887
+ const stringValue = (0, react.useMemo)(() => {
2888
+ if (typeof value === "string") return value;
2889
+ return JSON.stringify(value);
2890
+ }, [value]);
2891
+ (0, react.useLayoutEffect)(() => {
2892
+ if (!copilotkit) return;
2893
+ const id = copilotkit.addContext({
2894
+ description,
2895
+ value: stringValue
2896
+ });
2897
+ return () => {
2898
+ copilotkit.removeContext(id);
2899
+ };
2900
+ }, [
2901
+ description,
2902
+ stringValue,
2903
+ copilotkit
2904
+ ]);
2905
+ }
2906
+
2907
+ //#endregion
2908
+ //#region src/v2/a2ui/A2UICatalogContext.tsx
2909
+ /**
2910
+ * Renders agent context describing the available A2UI catalog and custom components.
2911
+ * Only mount this component when A2UI is enabled.
2912
+ *
2913
+ * When `includeSchema` is true, the full component schemas (JSON Schema) are also
2914
+ * sent as context using the same description key as the A2UI middleware, so the
2915
+ * middleware can optionally overwrite it with a server-side schema.
2916
+ */
2917
+ function A2UICatalogContext({ catalog, includeSchema }) {
2918
+ useAgentContext({
2919
+ description: "A2UI catalog capabilities: available catalog IDs and custom component definitions the client can render.",
2920
+ value: (0, _copilotkit_a2ui_renderer.buildCatalogContextValue)(catalog)
2921
+ });
2922
+ const { copilotkit } = useCopilotKit();
2923
+ const schemaValue = (0, react.useMemo)(() => includeSchema !== false ? JSON.stringify((0, _copilotkit_a2ui_renderer.extractCatalogComponentSchemas)(catalog)) : null, [catalog, includeSchema]);
2924
+ (0, react.useLayoutEffect)(() => {
2925
+ if (!copilotkit || !schemaValue) return;
2926
+ const ids = [];
2927
+ ids.push(copilotkit.addContext({
2928
+ description: _copilotkit_a2ui_renderer.A2UI_SCHEMA_CONTEXT_DESCRIPTION,
2929
+ value: schemaValue
2930
+ }));
2931
+ ids.push(copilotkit.addContext({
2932
+ description: "A2UI generation guidelines — protocol rules, tool arguments, path rules, data model format, and form/two-way-binding instructions.",
2933
+ value: _copilotkit_shared.A2UI_DEFAULT_GENERATION_GUIDELINES
2934
+ }));
2935
+ ids.push(copilotkit.addContext({
2936
+ description: "A2UI design guidelines — visual design rules, component hierarchy tips, and action handler patterns.",
2937
+ value: _copilotkit_shared.A2UI_DEFAULT_DESIGN_GUIDELINES
2938
+ }));
2939
+ return () => {
2940
+ for (const id of ids) copilotkit.removeContext(id);
2941
+ };
2942
+ }, [copilotkit, schemaValue]);
2943
+ return null;
2035
2944
  }
2036
2945
 
2037
2946
  //#endregion
@@ -2134,6 +3043,17 @@ var CopilotKitCoreReact = class extends _copilotkit_core.CopilotKitCore {
2134
3043
  //#region src/v2/providers/CopilotKitProvider.tsx
2135
3044
  const HEADER_NAME = "X-CopilotCloud-Public-Api-Key";
2136
3045
  const COPILOT_CLOUD_CHAT_URL$1 = "https://api.cloud.copilotkit.ai/copilotkit/v1";
3046
+ const DEFAULT_DESIGN_SKILL = `When generating UI with generateSandboxedUi, follow these design principles inspired by shadcn/ui:
3047
+
3048
+ - Use a minimal, flat aesthetic. Avoid drop shadows and gradients — rely on subtle borders (1px solid, light gray like #e5e7eb) to define surfaces.
3049
+ - Neutral base palette: white backgrounds, zinc/slate gray text (#09090b for headings, #71717a for secondary text). One accent color for interactive elements.
3050
+ - Use system font stacks (system-ui, -apple-system, sans-serif) at readable sizes (14px body, 600 weight for headings). Tight line-heights.
3051
+ - Small, consistent border-radius (6–8px). Cards and containers use border, not shadow, for separation.
3052
+ - Buttons: solid fill for primary (dark bg, white text), outline for secondary (border + transparent bg). Subtle hover state (slight opacity or background shift).
3053
+ - Use CSS Grid or Flexbox for layout. Ensure the UI looks good at any width.
3054
+ - Minimal transitions (150ms) for hover/focus states only. No decorative animations.
3055
+ - Keep the UI focused and dense — avoid excessive padding. Use compact spacing (8–12px gaps, 10–14px padding in controls).`;
3056
+ const GENERATE_SANDBOXED_UI_DESCRIPTION = "Generate sandboxed UI. IMPORTANT: The generated code runs in a sandboxed iframe WITHOUT same-origin access. Do NOT use localStorage, sessionStorage, document.cookie, IndexedDB, or fetch/XMLHttpRequest to same-origin URLs. To communicate with the host application, use Websandbox.connection.remote.<functionName>(args) which returns a Promise.\n\nYou CAN use external libraries from CDNs by including <script> or <link> tags in the HTML <head> (e.g., Chart.js, D3, Three.js, x-data-spreadsheet, etc.). CDN resources load normally inside the sandbox.\n\nPARAMETER ORDER IS CRITICAL — generate parameters in exactly this order:\n1. initialHeight + placeholderMessages (shown to user while generating)\n2. css (all styles FIRST — the user sees a placeholder until CSS is complete)\n3. html (streams in live — the user watches the UI build as HTML is generated)\n4. jsFunctions (reusable helper functions)\n5. jsExpressions (applied one-by-one — the user sees each expression take effect)";
2137
3057
  const CopilotKitContext = (0, react.createContext)({
2138
3058
  copilotkit: null,
2139
3059
  executingToolCallIds: /* @__PURE__ */ new Set()
@@ -2149,9 +3069,11 @@ function useStableArrayProp(prop, warningMessage, isMeaningfulChange) {
2149
3069
  }, [value, warningMessage]);
2150
3070
  return value;
2151
3071
  }
2152
- const CopilotKitProvider = ({ children, runtimeUrl, headers = {}, credentials, publicApiKey, publicLicenseKey, licenseToken, properties = {}, agents__unsafe_dev_only: agents = {}, selfManagedAgents = {}, renderToolCalls, renderActivityMessages, renderCustomMessages, frontendTools, humanInTheLoop, showDevConsole = false, useSingleEndpoint = false, onError, a2ui }) => {
3072
+ const CopilotKitProvider = ({ children, runtimeUrl, headers = {}, credentials, publicApiKey, publicLicenseKey, licenseToken, properties = {}, agents__unsafe_dev_only: agents = {}, selfManagedAgents = {}, renderToolCalls, renderActivityMessages, renderCustomMessages, frontendTools, humanInTheLoop, openGenerativeUI, showDevConsole = false, useSingleEndpoint, onError, a2ui, defaultThrottleMs, inspectorDefaultAnchor }) => {
2153
3073
  const [shouldRenderInspector, setShouldRenderInspector] = (0, react.useState)(false);
2154
3074
  const [runtimeA2UIEnabled, setRuntimeA2UIEnabled] = (0, react.useState)(false);
3075
+ const [runtimeOpenGenUIEnabled, setRuntimeOpenGenUIEnabled] = (0, react.useState)(false);
3076
+ const openGenUIActive = runtimeOpenGenUIEnabled || !!openGenerativeUI;
2155
3077
  const [runtimeLicenseStatus, setRuntimeLicenseStatus] = (0, react.useState)(void 0);
2156
3078
  (0, react.useEffect)(() => {
2157
3079
  if (typeof window === "undefined") return;
@@ -2177,9 +3099,22 @@ const CopilotKitProvider = ({ children, runtimeUrl, headers = {}, credentials, p
2177
3099
  content: MCPAppsActivityContentSchema,
2178
3100
  render: MCPAppsActivityRenderer
2179
3101
  }];
2180
- if (runtimeA2UIEnabled) renderers.unshift(createA2UIMessageRenderer({ theme: a2ui?.theme ?? _copilotkit_a2ui_renderer.viewerTheme }));
3102
+ if (openGenUIActive) renderers.push({
3103
+ activityType: OpenGenerativeUIActivityType,
3104
+ content: OpenGenerativeUIContentSchema,
3105
+ render: OpenGenerativeUIActivityRenderer
3106
+ });
3107
+ if (runtimeA2UIEnabled) renderers.unshift(createA2UIMessageRenderer({
3108
+ theme: a2ui?.theme ?? _copilotkit_a2ui_renderer.viewerTheme,
3109
+ catalog: a2ui?.catalog,
3110
+ loadingComponent: a2ui?.loadingComponent
3111
+ }));
2181
3112
  return renderers;
2182
- }, [runtimeA2UIEnabled, a2ui]);
3113
+ }, [
3114
+ runtimeA2UIEnabled,
3115
+ openGenUIActive,
3116
+ a2ui
3117
+ ]);
2183
3118
  const allActivityRenderers = (0, react.useMemo)(() => {
2184
3119
  return [...renderActivityMessagesList, ...builtInActivityRenderers];
2185
3120
  }, [renderActivityMessagesList, builtInActivityRenderers]);
@@ -2205,6 +3140,7 @@ const CopilotKitProvider = ({ children, runtimeUrl, headers = {}, credentials, p
2205
3140
  const chatApiEndpoint = runtimeUrl ?? (resolvedPublicKey ? COPILOT_CLOUD_CHAT_URL$1 : void 0);
2206
3141
  const frontendToolsList = useStableArrayProp(frontendTools, "frontendTools must be a stable array. If you want to dynamically add or remove tools, use `useFrontendTool` instead.");
2207
3142
  const humanInTheLoopList = useStableArrayProp(humanInTheLoop, "humanInTheLoop must be a stable array. If you want to dynamically add or remove human-in-the-loop tools, use `useHumanInTheLoop` instead.");
3143
+ const sandboxFunctionsList = useStableArrayProp(openGenerativeUI?.sandboxFunctions, "openGenerativeUI.sandboxFunctions must be a stable array.");
2208
3144
  const processedHumanInTheLoopTools = (0, react.useMemo)(() => {
2209
3145
  const processedTools = [];
2210
3146
  const processedRenderToolCalls = [];
@@ -2235,15 +3171,31 @@ const CopilotKitProvider = ({ children, runtimeUrl, headers = {}, credentials, p
2235
3171
  renderToolCalls: processedRenderToolCalls
2236
3172
  };
2237
3173
  }, [humanInTheLoopList]);
3174
+ const builtInFrontendTools = (0, react.useMemo)(() => {
3175
+ if (!openGenUIActive) return [];
3176
+ return [{
3177
+ name: "generateSandboxedUi",
3178
+ description: GENERATE_SANDBOXED_UI_DESCRIPTION,
3179
+ parameters: GenerateSandboxedUiArgsSchema,
3180
+ handler: async () => "UI generated",
3181
+ followUp: true,
3182
+ render: OpenGenerativeUIToolRenderer
3183
+ }];
3184
+ }, [openGenUIActive]);
2238
3185
  const allTools = (0, react.useMemo)(() => {
2239
3186
  const tools = [];
2240
3187
  tools.push(...frontendToolsList);
3188
+ tools.push(...builtInFrontendTools);
2241
3189
  tools.push(...processedHumanInTheLoopTools.tools);
2242
3190
  return tools;
2243
- }, [frontendToolsList, processedHumanInTheLoopTools]);
3191
+ }, [
3192
+ frontendToolsList,
3193
+ builtInFrontendTools,
3194
+ processedHumanInTheLoopTools
3195
+ ]);
2244
3196
  const allRenderToolCalls = (0, react.useMemo)(() => {
2245
3197
  const combined = [...renderToolCallsList];
2246
- frontendToolsList.forEach((tool) => {
3198
+ [...frontendToolsList, ...builtInFrontendTools].forEach((tool) => {
2247
3199
  if (tool.render) {
2248
3200
  const args = tool.parameters || (tool.name === "*" ? zod.z.any() : void 0);
2249
3201
  if (args) combined.push({
@@ -2258,25 +3210,31 @@ const CopilotKitProvider = ({ children, runtimeUrl, headers = {}, credentials, p
2258
3210
  }, [
2259
3211
  renderToolCallsList,
2260
3212
  frontendToolsList,
3213
+ builtInFrontendTools,
2261
3214
  processedHumanInTheLoopTools
2262
3215
  ]);
2263
3216
  const copilotkitRef = (0, react.useRef)(null);
2264
- if (copilotkitRef.current === null) copilotkitRef.current = new CopilotKitCoreReact({
2265
- runtimeUrl: chatApiEndpoint,
2266
- runtimeTransport: useSingleEndpoint ? "single" : "rest",
2267
- headers: mergedHeaders,
2268
- credentials,
2269
- properties,
2270
- agents__unsafe_dev_only: mergedAgents,
2271
- tools: allTools,
2272
- renderToolCalls: allRenderToolCalls,
2273
- renderActivityMessages: allActivityRenderers,
2274
- renderCustomMessages: renderCustomMessagesList
2275
- });
3217
+ if (copilotkitRef.current === null) {
3218
+ copilotkitRef.current = new CopilotKitCoreReact({
3219
+ runtimeUrl: chatApiEndpoint,
3220
+ runtimeTransport: useSingleEndpoint === true ? "single" : useSingleEndpoint === false ? "rest" : "auto",
3221
+ headers: mergedHeaders,
3222
+ credentials,
3223
+ properties,
3224
+ agents__unsafe_dev_only: mergedAgents,
3225
+ tools: allTools,
3226
+ renderToolCalls: allRenderToolCalls,
3227
+ renderActivityMessages: allActivityRenderers,
3228
+ renderCustomMessages: renderCustomMessagesList
3229
+ });
3230
+ if (defaultThrottleMs !== void 0) copilotkitRef.current.setDefaultThrottleMs(defaultThrottleMs);
3231
+ }
2276
3232
  const copilotkit = copilotkitRef.current;
2277
3233
  (0, react.useEffect)(() => {
3234
+ setRuntimeA2UIEnabled(copilotkit.a2uiEnabled);
2278
3235
  const subscription = copilotkit.subscribe({ onRuntimeConnectionStatusChanged: () => {
2279
3236
  setRuntimeA2UIEnabled(copilotkit.a2uiEnabled);
3237
+ setRuntimeOpenGenUIEnabled(copilotkit.openGenerativeUIEnabled);
2280
3238
  setRuntimeLicenseStatus(copilotkit.licenseStatus);
2281
3239
  } });
2282
3240
  return () => {
@@ -2335,7 +3293,7 @@ const CopilotKitProvider = ({ children, runtimeUrl, headers = {}, credentials, p
2335
3293
  }, [copilotkit]);
2336
3294
  (0, react.useEffect)(() => {
2337
3295
  copilotkit.setRuntimeUrl(chatApiEndpoint);
2338
- copilotkit.setRuntimeTransport(useSingleEndpoint ? "single" : "rest");
3296
+ copilotkit.setRuntimeTransport(useSingleEndpoint === true ? "single" : useSingleEndpoint === false ? "rest" : "auto");
2339
3297
  copilotkit.setHeaders(mergedHeaders);
2340
3298
  copilotkit.setCredentials(credentials);
2341
3299
  copilotkit.setProperties(properties);
@@ -2369,23 +3327,75 @@ const CopilotKitProvider = ({ children, runtimeUrl, headers = {}, credentials, p
2369
3327
  (0, react.useEffect)(() => {
2370
3328
  didMountRef.current = true;
2371
3329
  }, []);
3330
+ (0, react.useEffect)(() => {
3331
+ if (defaultThrottleMs !== void 0 && (!Number.isFinite(defaultThrottleMs) || defaultThrottleMs < 0)) console.error(`CopilotKitProvider: defaultThrottleMs must be a non-negative finite number, got ${defaultThrottleMs}. useAgent hooks without an explicit throttleMs will fall back to unthrottled.`);
3332
+ copilotkit.setDefaultThrottleMs(defaultThrottleMs);
3333
+ }, [copilotkit, defaultThrottleMs]);
3334
+ const designSkill = openGenerativeUI?.designSkill ?? DEFAULT_DESIGN_SKILL;
3335
+ (0, react.useLayoutEffect)(() => {
3336
+ if (!copilotkit || !openGenUIActive) return;
3337
+ const id = copilotkit.addContext({
3338
+ description: "Design guidelines for the generateSandboxedUi tool. Follow these when building UI.",
3339
+ value: designSkill
3340
+ });
3341
+ return () => {
3342
+ copilotkit.removeContext(id);
3343
+ };
3344
+ }, [
3345
+ copilotkit,
3346
+ designSkill,
3347
+ openGenUIActive
3348
+ ]);
3349
+ const sandboxFunctionsDescriptors = (0, react.useMemo)(() => {
3350
+ if (sandboxFunctionsList.length === 0) return null;
3351
+ return JSON.stringify(sandboxFunctionsList.map((fn) => ({
3352
+ name: fn.name,
3353
+ description: fn.description,
3354
+ parameters: (0, _copilotkit_shared.schemaToJsonSchema)(fn.parameters, { zodToJsonSchema: zod_to_json_schema.zodToJsonSchema })
3355
+ })));
3356
+ }, [sandboxFunctionsList]);
3357
+ (0, react.useLayoutEffect)(() => {
3358
+ if (!copilotkit || !sandboxFunctionsDescriptors || !openGenUIActive) return;
3359
+ const id = copilotkit.addContext({
3360
+ description: "Sandbox functions available in generated sandboxed UI code. Call via: await Websandbox.connection.remote.<functionName>(args)",
3361
+ value: sandboxFunctionsDescriptors
3362
+ });
3363
+ return () => {
3364
+ copilotkit.removeContext(id);
3365
+ };
3366
+ }, [
3367
+ copilotkit,
3368
+ sandboxFunctionsDescriptors,
3369
+ openGenUIActive
3370
+ ]);
2372
3371
  const contextValue = (0, react.useMemo)(() => ({
2373
3372
  copilotkit,
2374
3373
  executingToolCallIds
2375
3374
  }), [copilotkit, executingToolCallIds]);
2376
3375
  const licenseContextValue = (0, react.useMemo)(() => (0, _copilotkit_shared.createLicenseContextValue)(null), []);
2377
- return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CopilotKitContext.Provider, {
2378
- value: contextValue,
2379
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(LicenseContext.Provider, {
2380
- value: licenseContextValue,
2381
- children: [
2382
- children,
2383
- shouldRenderInspector ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CopilotKitInspector, { core: copilotkit }) : null,
2384
- runtimeLicenseStatus === "none" && !resolvedPublicKey && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(LicenseWarningBanner, { type: "no_license" }),
2385
- runtimeLicenseStatus === "expired" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(LicenseWarningBanner, { type: "expired" }),
2386
- runtimeLicenseStatus === "invalid" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(LicenseWarningBanner, { type: "invalid" }),
2387
- runtimeLicenseStatus === "expiring" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(LicenseWarningBanner, { type: "expiring" })
2388
- ]
3376
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SandboxFunctionsContext.Provider, {
3377
+ value: sandboxFunctionsList,
3378
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CopilotKitContext.Provider, {
3379
+ value: contextValue,
3380
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(LicenseContext.Provider, {
3381
+ value: licenseContextValue,
3382
+ children: [
3383
+ runtimeA2UIEnabled && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(A2UIBuiltInToolCallRenderer, {}),
3384
+ runtimeA2UIEnabled && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(A2UICatalogContext, {
3385
+ catalog: a2ui?.catalog,
3386
+ includeSchema: a2ui?.includeSchema
3387
+ }),
3388
+ children,
3389
+ shouldRenderInspector ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CopilotKitInspector, {
3390
+ core: copilotkit,
3391
+ defaultAnchor: inspectorDefaultAnchor
3392
+ }) : null,
3393
+ runtimeLicenseStatus === "none" && !resolvedPublicKey && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(LicenseWarningBanner, { type: "no_license" }),
3394
+ runtimeLicenseStatus === "expired" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(LicenseWarningBanner, { type: "expired" }),
3395
+ runtimeLicenseStatus === "invalid" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(LicenseWarningBanner, { type: "invalid" }),
3396
+ runtimeLicenseStatus === "expiring" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(LicenseWarningBanner, { type: "expiring" })
3397
+ ]
3398
+ })
2389
3399
  })
2390
3400
  });
2391
3401
  };
@@ -2394,81 +3404,264 @@ const useCopilotKit = () => {
2394
3404
  const [, forceUpdate] = (0, react.useReducer)((x) => x + 1, 0);
2395
3405
  if (!context) throw new Error("useCopilotKit must be used within CopilotKitProvider");
2396
3406
  (0, react.useEffect)(() => {
2397
- const subscription = context.copilotkit.subscribe({ onRuntimeConnectionStatusChanged: () => {
2398
- forceUpdate();
2399
- } });
3407
+ const subscription = context.copilotkit.subscribe({ onRuntimeConnectionStatusChanged: () => {
3408
+ forceUpdate();
3409
+ } });
3410
+ return () => {
3411
+ subscription.unsubscribe();
3412
+ };
3413
+ }, []);
3414
+ return context;
3415
+ };
3416
+
3417
+ //#endregion
3418
+ //#region src/v2/hooks/use-render-tool-call.tsx
3419
+ /**
3420
+ * Memoized component that renders a single tool call.
3421
+ * This prevents unnecessary re-renders when parent components update
3422
+ * but the tool call data hasn't changed.
3423
+ */
3424
+ const ToolCallRenderer = react.default.memo(function ToolCallRenderer({ toolCall, toolMessage, RenderComponent, isExecuting }) {
3425
+ const args = (0, react.useMemo)(() => (0, _copilotkit_shared.partialJSONParse)(toolCall.function.arguments), [toolCall.function.arguments]);
3426
+ const toolName = toolCall.function.name;
3427
+ if (toolMessage) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RenderComponent, {
3428
+ name: toolName,
3429
+ args,
3430
+ status: _copilotkit_core.ToolCallStatus.Complete,
3431
+ result: toolMessage.content
3432
+ });
3433
+ else if (isExecuting) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RenderComponent, {
3434
+ name: toolName,
3435
+ args,
3436
+ status: _copilotkit_core.ToolCallStatus.Executing,
3437
+ result: void 0
3438
+ });
3439
+ else return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RenderComponent, {
3440
+ name: toolName,
3441
+ args,
3442
+ status: _copilotkit_core.ToolCallStatus.InProgress,
3443
+ result: void 0
3444
+ });
3445
+ }, (prevProps, nextProps) => {
3446
+ if (prevProps.toolCall.id !== nextProps.toolCall.id) return false;
3447
+ if (prevProps.toolCall.function.name !== nextProps.toolCall.function.name) return false;
3448
+ if (prevProps.toolCall.function.arguments !== nextProps.toolCall.function.arguments) return false;
3449
+ if (prevProps.toolMessage?.content !== nextProps.toolMessage?.content) return false;
3450
+ if (prevProps.isExecuting !== nextProps.isExecuting) return false;
3451
+ if (prevProps.RenderComponent !== nextProps.RenderComponent) return false;
3452
+ return true;
3453
+ });
3454
+ /**
3455
+ * Hook that returns a function to render tool calls based on the render functions
3456
+ * defined in CopilotKitProvider.
3457
+ *
3458
+ * @returns A function that takes a tool call and optional tool message and returns the rendered component
3459
+ */
3460
+ function useRenderToolCall() {
3461
+ const { copilotkit, executingToolCallIds } = useCopilotKit();
3462
+ const agentId = useCopilotChatConfiguration()?.agentId ?? _copilotkit_shared.DEFAULT_AGENT_ID;
3463
+ const renderToolCalls = (0, react.useSyncExternalStore)((callback) => {
3464
+ return copilotkit.subscribe({ onRenderToolCallsChanged: callback }).unsubscribe;
3465
+ }, () => copilotkit.renderToolCalls, () => copilotkit.renderToolCalls);
3466
+ return (0, react.useCallback)(({ toolCall, toolMessage }) => {
3467
+ const exactMatches = renderToolCalls.filter((rc) => rc.name === toolCall.function.name);
3468
+ const renderConfig = exactMatches.find((rc) => rc.agentId === agentId) || exactMatches.find((rc) => !rc.agentId) || exactMatches[0] || renderToolCalls.find((rc) => rc.name === "*");
3469
+ if (!renderConfig) return null;
3470
+ const RenderComponent = renderConfig.render;
3471
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ToolCallRenderer, {
3472
+ toolCall,
3473
+ toolMessage,
3474
+ RenderComponent,
3475
+ isExecuting: executingToolCallIds.has(toolCall.id)
3476
+ }, toolCall.id);
3477
+ }, [
3478
+ renderToolCalls,
3479
+ executingToolCallIds,
3480
+ agentId
3481
+ ]);
3482
+ }
3483
+
3484
+ //#endregion
3485
+ //#region src/v2/hooks/use-agent.tsx
3486
+ let UseAgentUpdate = /* @__PURE__ */ function(UseAgentUpdate) {
3487
+ UseAgentUpdate["OnMessagesChanged"] = "OnMessagesChanged";
3488
+ UseAgentUpdate["OnStateChanged"] = "OnStateChanged";
3489
+ UseAgentUpdate["OnRunStatusChanged"] = "OnRunStatusChanged";
3490
+ return UseAgentUpdate;
3491
+ }({});
3492
+ const ALL_UPDATES = [
3493
+ UseAgentUpdate.OnMessagesChanged,
3494
+ UseAgentUpdate.OnStateChanged,
3495
+ UseAgentUpdate.OnRunStatusChanged
3496
+ ];
3497
+ /**
3498
+ * Clone a registry agent for per-thread isolation.
3499
+ * Copies agent configuration (transport, headers, etc.) but resets conversation
3500
+ * state (messages, threadId, state) so each thread starts fresh.
3501
+ */
3502
+ function cloneForThread(source, threadId, headers) {
3503
+ const clone = source.clone();
3504
+ if (clone === source) throw new Error(`useAgent: ${source.constructor.name}.clone() returned the same instance. clone() must return a new, independent object.`);
3505
+ clone.threadId = threadId;
3506
+ clone.setMessages([]);
3507
+ clone.setState({});
3508
+ if (clone instanceof _ag_ui_client.HttpAgent) clone.headers = { ...headers };
3509
+ return clone;
3510
+ }
3511
+ /**
3512
+ * Module-level WeakMap: registryAgent → (threadId → clone).
3513
+ * Shared across all useAgent() calls so that every component using the same
3514
+ * (agentId, threadId) pair receives the same agent instance. Using WeakMap
3515
+ * ensures the clone map is garbage-collected when the registry agent is
3516
+ * replaced (e.g. after reconnect or hot-reload).
3517
+ */
3518
+ const globalThreadCloneMap = /* @__PURE__ */ new WeakMap();
3519
+ /**
3520
+ * Look up an existing per-thread clone without creating one.
3521
+ * Returns undefined when no clone has been created yet for this pair.
3522
+ */
3523
+ function getThreadClone(registryAgent, threadId) {
3524
+ if (!registryAgent || !threadId) return void 0;
3525
+ return globalThreadCloneMap.get(registryAgent)?.get(threadId);
3526
+ }
3527
+ function getOrCreateThreadClone(existing, threadId, headers) {
3528
+ let byThread = globalThreadCloneMap.get(existing);
3529
+ if (!byThread) {
3530
+ byThread = /* @__PURE__ */ new Map();
3531
+ globalThreadCloneMap.set(existing, byThread);
3532
+ }
3533
+ const cached = byThread.get(threadId);
3534
+ if (cached) return cached;
3535
+ const clone = cloneForThread(existing, threadId, headers);
3536
+ byThread.set(threadId, clone);
3537
+ return clone;
3538
+ }
3539
+ function useAgent({ agentId, threadId, updates, throttleMs } = {}) {
3540
+ agentId ??= _copilotkit_shared.DEFAULT_AGENT_ID;
3541
+ const { copilotkit } = useCopilotKit();
3542
+ const providerThrottleMs = copilotkit.defaultThrottleMs;
3543
+ const chatConfig = useCopilotChatConfiguration();
3544
+ threadId ??= chatConfig?.threadId;
3545
+ const effectiveThrottleMs = (0, react.useMemo)(() => {
3546
+ const resolved = throttleMs ?? providerThrottleMs ?? 0;
3547
+ if (!Number.isFinite(resolved) || resolved < 0) {
3548
+ const source = throttleMs !== void 0 ? "hook-level throttleMs" : "provider-level defaultThrottleMs";
3549
+ console.error(`useAgent: ${source} must be a non-negative finite number, got ${resolved}. Falling back to unthrottled.`);
3550
+ return 0;
3551
+ }
3552
+ return resolved;
3553
+ }, [throttleMs, providerThrottleMs]);
3554
+ const [, forceUpdate] = (0, react.useReducer)((x) => x + 1, 0);
3555
+ const updateFlags = (0, react.useMemo)(() => updates ?? ALL_UPDATES, [JSON.stringify(updates)]);
3556
+ const provisionalAgentCache = (0, react.useRef)(/* @__PURE__ */ new Map());
3557
+ const agent = (0, react.useMemo)(() => {
3558
+ const cacheKey = threadId ? `${agentId}:${threadId}` : agentId;
3559
+ const existing = copilotkit.getAgent(agentId);
3560
+ if (existing) {
3561
+ provisionalAgentCache.current.delete(cacheKey);
3562
+ provisionalAgentCache.current.delete(agentId);
3563
+ if (!threadId) return existing;
3564
+ return getOrCreateThreadClone(existing, threadId, copilotkit.headers);
3565
+ }
3566
+ const isRuntimeConfigured = copilotkit.runtimeUrl !== void 0;
3567
+ const status = copilotkit.runtimeConnectionStatus;
3568
+ if (isRuntimeConfigured && (status === _copilotkit_core.CopilotKitCoreRuntimeConnectionStatus.Disconnected || status === _copilotkit_core.CopilotKitCoreRuntimeConnectionStatus.Connecting)) {
3569
+ const cached = provisionalAgentCache.current.get(cacheKey);
3570
+ if (cached) {
3571
+ cached.headers = { ...copilotkit.headers };
3572
+ return cached;
3573
+ }
3574
+ const provisional = new _copilotkit_core.ProxiedCopilotRuntimeAgent({
3575
+ runtimeUrl: copilotkit.runtimeUrl,
3576
+ agentId,
3577
+ transport: copilotkit.runtimeTransport,
3578
+ runtimeMode: "pending"
3579
+ });
3580
+ provisional.headers = { ...copilotkit.headers };
3581
+ if (threadId) provisional.threadId = threadId;
3582
+ provisionalAgentCache.current.set(cacheKey, provisional);
3583
+ return provisional;
3584
+ }
3585
+ if (isRuntimeConfigured && status === _copilotkit_core.CopilotKitCoreRuntimeConnectionStatus.Error) {
3586
+ const cached = provisionalAgentCache.current.get(cacheKey);
3587
+ if (cached) {
3588
+ cached.headers = { ...copilotkit.headers };
3589
+ return cached;
3590
+ }
3591
+ const provisional = new _copilotkit_core.ProxiedCopilotRuntimeAgent({
3592
+ runtimeUrl: copilotkit.runtimeUrl,
3593
+ agentId,
3594
+ transport: copilotkit.runtimeTransport,
3595
+ runtimeMode: "pending"
3596
+ });
3597
+ provisional.headers = { ...copilotkit.headers };
3598
+ if (threadId) provisional.threadId = threadId;
3599
+ provisionalAgentCache.current.set(cacheKey, provisional);
3600
+ return provisional;
3601
+ }
3602
+ const knownAgents = Object.keys(copilotkit.agents ?? {});
3603
+ const runtimePart = isRuntimeConfigured ? `runtimeUrl=${copilotkit.runtimeUrl}` : "no runtimeUrl";
3604
+ throw new Error(`useAgent: Agent '${agentId}' not found after runtime sync (${runtimePart}). ` + (knownAgents.length ? `Known agents: [${knownAgents.join(", ")}]` : "No agents registered.") + " Verify your runtime /info and/or agents__unsafe_dev_only.");
3605
+ }, [
3606
+ agentId,
3607
+ threadId,
3608
+ copilotkit.agents,
3609
+ copilotkit.runtimeConnectionStatus,
3610
+ copilotkit.runtimeUrl,
3611
+ copilotkit.runtimeTransport,
3612
+ JSON.stringify(copilotkit.headers)
3613
+ ]);
3614
+ (0, react.useEffect)(() => {
3615
+ if (updateFlags.length === 0) return;
3616
+ const handlers = {};
3617
+ let timerId = null;
3618
+ let active = true;
3619
+ if (updateFlags.includes(UseAgentUpdate.OnMessagesChanged)) {
3620
+ const ms = effectiveThrottleMs;
3621
+ if (ms > 0) {
3622
+ let throttleActive = false;
3623
+ let pending = false;
3624
+ const throttledNotify = () => {
3625
+ if (!active) return;
3626
+ if (!throttleActive) {
3627
+ throttleActive = true;
3628
+ pending = false;
3629
+ forceUpdate();
3630
+ timerId = setTimeout(function trailingEdge() {
3631
+ timerId = null;
3632
+ if (active && pending) {
3633
+ pending = false;
3634
+ forceUpdate();
3635
+ timerId = setTimeout(trailingEdge, ms);
3636
+ } else throttleActive = false;
3637
+ }, ms);
3638
+ } else pending = true;
3639
+ };
3640
+ handlers.onMessagesChanged = throttledNotify;
3641
+ } else handlers.onMessagesChanged = forceUpdate;
3642
+ }
3643
+ if (updateFlags.includes(UseAgentUpdate.OnStateChanged)) handlers.onStateChanged = forceUpdate;
3644
+ if (updateFlags.includes(UseAgentUpdate.OnRunStatusChanged)) {
3645
+ handlers.onRunInitialized = forceUpdate;
3646
+ handlers.onRunFinalized = forceUpdate;
3647
+ handlers.onRunFailed = forceUpdate;
3648
+ }
3649
+ const subscription = agent.subscribe(handlers);
2400
3650
  return () => {
3651
+ active = false;
3652
+ if (timerId !== null) clearTimeout(timerId);
2401
3653
  subscription.unsubscribe();
2402
3654
  };
2403
- }, []);
2404
- return context;
2405
- };
2406
-
2407
- //#endregion
2408
- //#region src/v2/hooks/use-render-tool-call.tsx
2409
- /**
2410
- * Memoized component that renders a single tool call.
2411
- * This prevents unnecessary re-renders when parent components update
2412
- * but the tool call data hasn't changed.
2413
- */
2414
- const ToolCallRenderer = react.default.memo(function ToolCallRenderer({ toolCall, toolMessage, RenderComponent, isExecuting }) {
2415
- const args = (0, react.useMemo)(() => (0, _copilotkit_shared.partialJSONParse)(toolCall.function.arguments), [toolCall.function.arguments]);
2416
- const toolName = toolCall.function.name;
2417
- if (toolMessage) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RenderComponent, {
2418
- name: toolName,
2419
- args,
2420
- status: _copilotkit_core.ToolCallStatus.Complete,
2421
- result: toolMessage.content
2422
- });
2423
- else if (isExecuting) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RenderComponent, {
2424
- name: toolName,
2425
- args,
2426
- status: _copilotkit_core.ToolCallStatus.Executing,
2427
- result: void 0
2428
- });
2429
- else return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RenderComponent, {
2430
- name: toolName,
2431
- args,
2432
- status: _copilotkit_core.ToolCallStatus.InProgress,
2433
- result: void 0
2434
- });
2435
- }, (prevProps, nextProps) => {
2436
- if (prevProps.toolCall.id !== nextProps.toolCall.id) return false;
2437
- if (prevProps.toolCall.function.name !== nextProps.toolCall.function.name) return false;
2438
- if (prevProps.toolCall.function.arguments !== nextProps.toolCall.function.arguments) return false;
2439
- if (prevProps.toolMessage?.content !== nextProps.toolMessage?.content) return false;
2440
- if (prevProps.isExecuting !== nextProps.isExecuting) return false;
2441
- if (prevProps.RenderComponent !== nextProps.RenderComponent) return false;
2442
- return true;
2443
- });
2444
- /**
2445
- * Hook that returns a function to render tool calls based on the render functions
2446
- * defined in CopilotKitProvider.
2447
- *
2448
- * @returns A function that takes a tool call and optional tool message and returns the rendered component
2449
- */
2450
- function useRenderToolCall() {
2451
- const { copilotkit, executingToolCallIds } = useCopilotKit();
2452
- const agentId = useCopilotChatConfiguration()?.agentId ?? _copilotkit_shared.DEFAULT_AGENT_ID;
2453
- const renderToolCalls = (0, react.useSyncExternalStore)((callback) => {
2454
- return copilotkit.subscribe({ onRenderToolCallsChanged: callback }).unsubscribe;
2455
- }, () => copilotkit.renderToolCalls, () => copilotkit.renderToolCalls);
2456
- return (0, react.useCallback)(({ toolCall, toolMessage }) => {
2457
- const exactMatches = renderToolCalls.filter((rc) => rc.name === toolCall.function.name);
2458
- const renderConfig = exactMatches.find((rc) => rc.agentId === agentId) || exactMatches.find((rc) => !rc.agentId) || exactMatches[0] || renderToolCalls.find((rc) => rc.name === "*");
2459
- if (!renderConfig) return null;
2460
- const RenderComponent = renderConfig.render;
2461
- return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ToolCallRenderer, {
2462
- toolCall,
2463
- toolMessage,
2464
- RenderComponent,
2465
- isExecuting: executingToolCallIds.has(toolCall.id)
2466
- }, toolCall.id);
2467
3655
  }, [
2468
- renderToolCalls,
2469
- executingToolCallIds,
2470
- agentId
3656
+ agent,
3657
+ forceUpdate,
3658
+ effectiveThrottleMs,
3659
+ updateFlags
2471
3660
  ]);
3661
+ (0, react.useEffect)(() => {
3662
+ if (agent instanceof _ag_ui_client.HttpAgent) agent.headers = { ...copilotkit.headers };
3663
+ }, [agent, JSON.stringify(copilotkit.headers)]);
3664
+ return { agent };
2472
3665
  }
2473
3666
 
2474
3667
  //#endregion
@@ -2488,7 +3681,8 @@ function useRenderCustomMessages() {
2488
3681
  const { message, position } = params;
2489
3682
  const resolvedRunId = copilotkit.getRunIdForMessage(agentId, threadId, message.id) ?? copilotkit.getRunIdsForThread(agentId, threadId).slice(-1)[0];
2490
3683
  const runId = resolvedRunId ?? `missing-run-id:${message.id}`;
2491
- const agent = copilotkit.getAgent(agentId);
3684
+ const registryAgent = copilotkit.getAgent(agentId);
3685
+ const agent = getThreadClone(registryAgent, threadId) ?? registryAgent;
2492
3686
  if (!agent) throw new Error("Agent not found");
2493
3687
  const messagesIdsInRun = resolvedRunId ? agent.messages.filter((msg) => copilotkit.getRunIdForMessage(agentId, threadId, msg.id) === resolvedRunId).map((msg) => msg.id) : [message.id];
2494
3688
  const rawMessageIndex = agent.messages.findIndex((msg) => msg.id === message.id);
@@ -2520,7 +3714,8 @@ function useRenderCustomMessages() {
2520
3714
  //#region src/v2/hooks/use-render-activity-message.tsx
2521
3715
  function useRenderActivityMessage() {
2522
3716
  const { copilotkit } = useCopilotKit();
2523
- const agentId = useCopilotChatConfiguration()?.agentId ?? _copilotkit_shared.DEFAULT_AGENT_ID;
3717
+ const config = useCopilotChatConfiguration();
3718
+ const agentId = config?.agentId ?? _copilotkit_shared.DEFAULT_AGENT_ID;
2524
3719
  const renderers = copilotkit.renderActivityMessages;
2525
3720
  const findRenderer = (0, react.useCallback)((activityType) => {
2526
3721
  if (!renderers.length) return null;
@@ -2536,7 +3731,8 @@ function useRenderActivityMessage() {
2536
3731
  return null;
2537
3732
  }
2538
3733
  const Component = renderer.render;
2539
- const agent = copilotkit.getAgent(agentId);
3734
+ const registryAgent = copilotkit.getAgent(agentId);
3735
+ const agent = getThreadClone(registryAgent, config?.threadId) ?? registryAgent;
2540
3736
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Component, {
2541
3737
  activityType: message.activityType,
2542
3738
  content: parseResult.data,
@@ -2545,6 +3741,7 @@ function useRenderActivityMessage() {
2545
3741
  }, message.id);
2546
3742
  }, [
2547
3743
  agentId,
3744
+ config?.threadId,
2548
3745
  copilotkit,
2549
3746
  findRenderer
2550
3747
  ]);
@@ -2570,7 +3767,7 @@ function useFrontendTool(tool, deps) {
2570
3767
  copilotkit.removeTool(name, tool.agentId);
2571
3768
  }
2572
3769
  copilotkit.addTool(tool);
2573
- if (tool.render && tool.parameters) copilotkit.addHookRenderToolCall({
3770
+ if (tool.render) copilotkit.addHookRenderToolCall({
2574
3771
  name,
2575
3772
  args: tool.parameters,
2576
3773
  agentId: tool.agentId,
@@ -2655,18 +3852,6 @@ function useComponent(config, deps) {
2655
3852
  }, deps);
2656
3853
  }
2657
3854
 
2658
- //#endregion
2659
- //#region src/v2/types/defineToolCallRenderer.ts
2660
- function defineToolCallRenderer(def) {
2661
- const argsSchema = def.name === "*" && !def.args ? zod.z.any() : def.args;
2662
- return {
2663
- name: def.name,
2664
- args: argsSchema,
2665
- render: def.render,
2666
- ...def.agentId ? { agentId: def.agentId } : {}
2667
- };
2668
- }
2669
-
2670
3855
  //#endregion
2671
3856
  //#region src/v2/hooks/use-render-tool.tsx
2672
3857
  const EMPTY_DEPS = [];
@@ -2920,190 +4105,79 @@ function DefaultToolCallRenderer({ name, parameters, status, result }) {
2920
4105
  fontSize: "11px",
2921
4106
  lineHeight: 1.6,
2922
4107
  color: "#27272a",
2923
- whiteSpace: "pre-wrap",
2924
- wordBreak: "break-word"
2925
- },
2926
- children: typeof result === "string" ? result : JSON.stringify(result, null, 2)
2927
- })] })]
2928
- })]
2929
- })
2930
- });
2931
- }
2932
-
2933
- //#endregion
2934
- //#region src/v2/hooks/use-human-in-the-loop.tsx
2935
- function useHumanInTheLoop(tool, deps) {
2936
- const { copilotkit } = useCopilotKit();
2937
- const resolvePromiseRef = (0, react.useRef)(null);
2938
- const respond = (0, react.useCallback)(async (result) => {
2939
- if (resolvePromiseRef.current) {
2940
- resolvePromiseRef.current(result);
2941
- resolvePromiseRef.current = null;
2942
- }
2943
- }, []);
2944
- const handler = (0, react.useCallback)(async () => {
2945
- return new Promise((resolve) => {
2946
- resolvePromiseRef.current = resolve;
2947
- });
2948
- }, []);
2949
- const RenderComponent = (0, react.useCallback)((props) => {
2950
- const ToolComponent = tool.render;
2951
- if (props.status === "inProgress") {
2952
- const enhancedProps = {
2953
- ...props,
2954
- name: tool.name,
2955
- description: tool.description || "",
2956
- respond: void 0
2957
- };
2958
- return react.default.createElement(ToolComponent, enhancedProps);
2959
- } else if (props.status === "executing") {
2960
- const enhancedProps = {
2961
- ...props,
2962
- name: tool.name,
2963
- description: tool.description || "",
2964
- respond
2965
- };
2966
- return react.default.createElement(ToolComponent, enhancedProps);
2967
- } else if (props.status === "complete") {
2968
- const enhancedProps = {
2969
- ...props,
2970
- name: tool.name,
2971
- description: tool.description || "",
2972
- respond: void 0
2973
- };
2974
- return react.default.createElement(ToolComponent, enhancedProps);
2975
- }
2976
- return react.default.createElement(ToolComponent, props);
2977
- }, [
2978
- tool.render,
2979
- tool.name,
2980
- tool.description,
2981
- respond
2982
- ]);
2983
- useFrontendTool({
2984
- ...tool,
2985
- handler,
2986
- render: RenderComponent
2987
- }, deps);
2988
- (0, react.useEffect)(() => {
2989
- return () => {
2990
- copilotkit.removeHookRenderToolCall(tool.name, tool.agentId);
2991
- };
2992
- }, [
2993
- copilotkit,
2994
- tool.name,
2995
- tool.agentId
2996
- ]);
2997
- }
2998
-
2999
- //#endregion
3000
- //#region src/v2/hooks/use-agent.tsx
3001
- let UseAgentUpdate = /* @__PURE__ */ function(UseAgentUpdate) {
3002
- UseAgentUpdate["OnMessagesChanged"] = "OnMessagesChanged";
3003
- UseAgentUpdate["OnStateChanged"] = "OnStateChanged";
3004
- UseAgentUpdate["OnRunStatusChanged"] = "OnRunStatusChanged";
3005
- return UseAgentUpdate;
3006
- }({});
3007
- const ALL_UPDATES = [
3008
- UseAgentUpdate.OnMessagesChanged,
3009
- UseAgentUpdate.OnStateChanged,
3010
- UseAgentUpdate.OnRunStatusChanged
3011
- ];
3012
- function useAgent({ agentId, updates } = {}) {
3013
- agentId ??= _copilotkit_shared.DEFAULT_AGENT_ID;
3014
- const { copilotkit } = useCopilotKit();
3015
- const [, forceUpdate] = (0, react.useReducer)((x) => x + 1, 0);
3016
- const updateFlags = (0, react.useMemo)(() => updates ?? ALL_UPDATES, [JSON.stringify(updates)]);
3017
- const provisionalAgentCache = (0, react.useRef)(/* @__PURE__ */ new Map());
3018
- const agent = (0, react.useMemo)(() => {
3019
- const existing = copilotkit.getAgent(agentId);
3020
- if (existing) {
3021
- provisionalAgentCache.current.delete(agentId);
3022
- return existing;
3023
- }
3024
- const isRuntimeConfigured = copilotkit.runtimeUrl !== void 0;
3025
- const status = copilotkit.runtimeConnectionStatus;
3026
- if (isRuntimeConfigured && (status === _copilotkit_core.CopilotKitCoreRuntimeConnectionStatus.Disconnected || status === _copilotkit_core.CopilotKitCoreRuntimeConnectionStatus.Connecting)) {
3027
- const cached = provisionalAgentCache.current.get(agentId);
3028
- if (cached) {
3029
- cached.headers = { ...copilotkit.headers };
3030
- return cached;
3031
- }
3032
- const provisional = new _copilotkit_core.ProxiedCopilotRuntimeAgent({
3033
- runtimeUrl: copilotkit.runtimeUrl,
3034
- agentId,
3035
- transport: copilotkit.runtimeTransport,
3036
- runtimeMode: "pending"
3037
- });
3038
- provisional.headers = { ...copilotkit.headers };
3039
- provisionalAgentCache.current.set(agentId, provisional);
3040
- return provisional;
3041
- }
3042
- if (isRuntimeConfigured && status === _copilotkit_core.CopilotKitCoreRuntimeConnectionStatus.Error) {
3043
- const provisional = new _copilotkit_core.ProxiedCopilotRuntimeAgent({
3044
- runtimeUrl: copilotkit.runtimeUrl,
3045
- agentId,
3046
- transport: copilotkit.runtimeTransport,
3047
- runtimeMode: "pending"
3048
- });
3049
- provisional.headers = { ...copilotkit.headers };
3050
- return provisional;
3051
- }
3052
- const knownAgents = Object.keys(copilotkit.agents ?? {});
3053
- const runtimePart = isRuntimeConfigured ? `runtimeUrl=${copilotkit.runtimeUrl}` : "no runtimeUrl";
3054
- throw new Error(`useAgent: Agent '${agentId}' not found after runtime sync (${runtimePart}). ` + (knownAgents.length ? `Known agents: [${knownAgents.join(", ")}]` : "No agents registered.") + " Verify your runtime /info and/or agents__unsafe_dev_only.");
3055
- }, [
3056
- agentId,
3057
- copilotkit.agents,
3058
- copilotkit.runtimeConnectionStatus,
3059
- copilotkit.runtimeUrl,
3060
- copilotkit.runtimeTransport,
3061
- JSON.stringify(copilotkit.headers)
3062
- ]);
3063
- (0, react.useEffect)(() => {
3064
- if (updateFlags.length === 0) return;
3065
- const handlers = {};
3066
- if (updateFlags.includes(UseAgentUpdate.OnMessagesChanged)) handlers.onMessagesChanged = () => {
3067
- forceUpdate();
3068
- };
3069
- if (updateFlags.includes(UseAgentUpdate.OnStateChanged)) handlers.onStateChanged = forceUpdate;
3070
- if (updateFlags.includes(UseAgentUpdate.OnRunStatusChanged)) {
3071
- handlers.onRunInitialized = forceUpdate;
3072
- handlers.onRunFinalized = forceUpdate;
3073
- handlers.onRunFailed = forceUpdate;
3074
- }
3075
- const subscription = agent.subscribe(handlers);
3076
- return () => subscription.unsubscribe();
3077
- }, [
3078
- agent,
3079
- forceUpdate,
3080
- JSON.stringify(updateFlags)
3081
- ]);
3082
- return { agent };
4108
+ whiteSpace: "pre-wrap",
4109
+ wordBreak: "break-word"
4110
+ },
4111
+ children: typeof result === "string" ? result : JSON.stringify(result, null, 2)
4112
+ })] })]
4113
+ })]
4114
+ })
4115
+ });
3083
4116
  }
3084
4117
 
3085
4118
  //#endregion
3086
- //#region src/v2/hooks/use-agent-context.tsx
3087
- function useAgentContext(context) {
3088
- const { description, value } = context;
4119
+ //#region src/v2/hooks/use-human-in-the-loop.tsx
4120
+ function useHumanInTheLoop(tool, deps) {
3089
4121
  const { copilotkit } = useCopilotKit();
3090
- const stringValue = (0, react.useMemo)(() => {
3091
- if (typeof value === "string") return value;
3092
- return JSON.stringify(value);
3093
- }, [value]);
3094
- (0, react.useLayoutEffect)(() => {
3095
- if (!copilotkit) return;
3096
- const id = copilotkit.addContext({
3097
- description,
3098
- value: stringValue
4122
+ const resolvePromiseRef = (0, react.useRef)(null);
4123
+ const respond = (0, react.useCallback)(async (result) => {
4124
+ if (resolvePromiseRef.current) {
4125
+ resolvePromiseRef.current(result);
4126
+ resolvePromiseRef.current = null;
4127
+ }
4128
+ }, []);
4129
+ const handler = (0, react.useCallback)(async () => {
4130
+ return new Promise((resolve) => {
4131
+ resolvePromiseRef.current = resolve;
3099
4132
  });
4133
+ }, []);
4134
+ const RenderComponent = (0, react.useCallback)((props) => {
4135
+ const ToolComponent = tool.render;
4136
+ if (props.status === "inProgress") {
4137
+ const enhancedProps = {
4138
+ ...props,
4139
+ name: tool.name,
4140
+ description: tool.description || "",
4141
+ respond: void 0
4142
+ };
4143
+ return react.default.createElement(ToolComponent, enhancedProps);
4144
+ } else if (props.status === "executing") {
4145
+ const enhancedProps = {
4146
+ ...props,
4147
+ name: tool.name,
4148
+ description: tool.description || "",
4149
+ respond
4150
+ };
4151
+ return react.default.createElement(ToolComponent, enhancedProps);
4152
+ } else if (props.status === "complete") {
4153
+ const enhancedProps = {
4154
+ ...props,
4155
+ name: tool.name,
4156
+ description: tool.description || "",
4157
+ respond: void 0
4158
+ };
4159
+ return react.default.createElement(ToolComponent, enhancedProps);
4160
+ }
4161
+ return react.default.createElement(ToolComponent, props);
4162
+ }, [
4163
+ tool.render,
4164
+ tool.name,
4165
+ tool.description,
4166
+ respond
4167
+ ]);
4168
+ useFrontendTool({
4169
+ ...tool,
4170
+ handler,
4171
+ render: RenderComponent
4172
+ }, deps);
4173
+ (0, react.useEffect)(() => {
3100
4174
  return () => {
3101
- copilotkit.removeContext(id);
4175
+ copilotkit.removeHookRenderToolCall(tool.name, tool.agentId);
3102
4176
  };
3103
4177
  }, [
3104
- description,
3105
- stringValue,
3106
- copilotkit
4178
+ copilotkit,
4179
+ tool.name,
4180
+ tool.agentId
3107
4181
  ]);
3108
4182
  }
3109
4183
 
@@ -3516,11 +4590,19 @@ function useThreadStoreSelector(store, selector) {
3516
4590
  function useThreads$1({ agentId, includeArchived, limit }) {
3517
4591
  const { copilotkit } = useCopilotKit();
3518
4592
  const [store] = (0, react.useState)(() => (0, _copilotkit_core.ɵcreateThreadStore)({ fetch: globalThis.fetch }));
3519
- const threads = useThreadStoreSelector(store, _copilotkit_core.ɵselectThreads);
4593
+ const coreThreads = useThreadStoreSelector(store, _copilotkit_core.ɵselectThreads);
4594
+ const threads = (0, react.useMemo)(() => coreThreads.map(({ id, agentId, name, archived, createdAt, updatedAt }) => ({
4595
+ id,
4596
+ agentId,
4597
+ name,
4598
+ archived,
4599
+ createdAt,
4600
+ updatedAt
4601
+ })), [coreThreads]);
3520
4602
  const storeIsLoading = useThreadStoreSelector(store, _copilotkit_core.ɵselectThreadsIsLoading);
3521
4603
  const storeError = useThreadStoreSelector(store, _copilotkit_core.ɵselectThreadsError);
3522
- const hasNextPage = useThreadStoreSelector(store, _copilotkit_core.ɵselectHasNextPage);
3523
- const isFetchingNextPage = useThreadStoreSelector(store, _copilotkit_core.ɵselectIsFetchingNextPage);
4604
+ const hasMoreThreads = useThreadStoreSelector(store, _copilotkit_core.ɵselectHasNextPage);
4605
+ const isFetchingMoreThreads = useThreadStoreSelector(store, _copilotkit_core.ɵselectIsFetchingNextPage);
3524
4606
  const headersKey = (0, react.useMemo)(() => {
3525
4607
  return JSON.stringify(Object.entries(copilotkit.headers ?? {}).sort(([left], [right]) => left.localeCompare(right)));
3526
4608
  }, [copilotkit.headers]);
@@ -3563,15 +4645,173 @@ function useThreads$1({ agentId, includeArchived, limit }) {
3563
4645
  threads,
3564
4646
  isLoading,
3565
4647
  error,
3566
- hasNextPage,
3567
- isFetchingNextPage,
3568
- fetchNextPage: (0, react.useCallback)(() => store.fetchNextPage(), [store]),
4648
+ hasMoreThreads,
4649
+ isFetchingMoreThreads,
4650
+ fetchMoreThreads: (0, react.useCallback)(() => store.fetchNextPage(), [store]),
3569
4651
  renameThread,
3570
4652
  archiveThread,
3571
4653
  deleteThread
3572
4654
  };
3573
4655
  }
3574
4656
 
4657
+ //#endregion
4658
+ //#region src/v2/hooks/use-attachments.tsx
4659
+ /**
4660
+ * Hook that manages file attachment state — uploads, drag-and-drop, paste,
4661
+ * and lifecycle. All returned callbacks are referentially stable across
4662
+ * renders (via useCallback) to avoid destabilizing downstream memoization.
4663
+ */
4664
+ function useAttachments({ config }) {
4665
+ const enabled = config?.enabled ?? false;
4666
+ const [attachments, setAttachments] = (0, react.useState)([]);
4667
+ const [dragOver, setDragOver] = (0, react.useState)(false);
4668
+ const fileInputRef = (0, react.useRef)(null);
4669
+ const containerRef = (0, react.useRef)(null);
4670
+ const configRef = (0, react.useRef)(config);
4671
+ configRef.current = config;
4672
+ const attachmentsRef = (0, react.useRef)([]);
4673
+ attachmentsRef.current = attachments;
4674
+ const processFiles = (0, react.useCallback)(async (files) => {
4675
+ const cfg = configRef.current;
4676
+ const accept = cfg?.accept ?? "*/*";
4677
+ const maxSize = cfg?.maxSize ?? 20 * 1024 * 1024;
4678
+ const rejectedFiles = files.filter((file) => !(0, _copilotkit_shared.matchesAcceptFilter)(file, accept));
4679
+ for (const file of rejectedFiles) cfg?.onUploadFailed?.({
4680
+ reason: "invalid-type",
4681
+ file,
4682
+ message: `File "${file.name}" is not accepted. Supported types: ${accept}`
4683
+ });
4684
+ const validFiles = files.filter((file) => (0, _copilotkit_shared.matchesAcceptFilter)(file, accept));
4685
+ for (const file of validFiles) {
4686
+ if ((0, _copilotkit_shared.exceedsMaxSize)(file, maxSize)) {
4687
+ cfg?.onUploadFailed?.({
4688
+ reason: "file-too-large",
4689
+ file,
4690
+ message: `File "${file.name}" exceeds the maximum size of ${(0, _copilotkit_shared.formatFileSize)(maxSize)}`
4691
+ });
4692
+ continue;
4693
+ }
4694
+ const modality = (0, _copilotkit_shared.getModalityFromMimeType)(file.type);
4695
+ const placeholderId = (0, _copilotkit_shared.randomUUID)();
4696
+ const placeholder = {
4697
+ id: placeholderId,
4698
+ type: modality,
4699
+ source: {
4700
+ type: "data",
4701
+ value: "",
4702
+ mimeType: file.type
4703
+ },
4704
+ filename: file.name,
4705
+ size: file.size,
4706
+ status: "uploading"
4707
+ };
4708
+ setAttachments((prev) => [...prev, placeholder]);
4709
+ try {
4710
+ let source;
4711
+ let uploadMetadata;
4712
+ if (cfg?.onUpload) {
4713
+ const { metadata: meta, ...uploadSource } = await cfg.onUpload(file);
4714
+ source = uploadSource;
4715
+ uploadMetadata = meta;
4716
+ } else source = {
4717
+ type: "data",
4718
+ value: await (0, _copilotkit_shared.readFileAsBase64)(file),
4719
+ mimeType: file.type
4720
+ };
4721
+ let thumbnail;
4722
+ if (modality === "video") thumbnail = await (0, _copilotkit_shared.generateVideoThumbnail)(file);
4723
+ setAttachments((prev) => prev.map((att) => att.id === placeholderId ? {
4724
+ ...att,
4725
+ source,
4726
+ status: "ready",
4727
+ thumbnail,
4728
+ metadata: uploadMetadata
4729
+ } : att));
4730
+ } catch (error) {
4731
+ setAttachments((prev) => prev.filter((att) => att.id !== placeholderId));
4732
+ console.error(`[CopilotKit] Failed to upload "${file.name}":`, error);
4733
+ cfg?.onUploadFailed?.({
4734
+ reason: "upload-failed",
4735
+ file,
4736
+ message: error instanceof Error ? error.message : `Failed to upload "${file.name}"`
4737
+ });
4738
+ }
4739
+ }
4740
+ }, []);
4741
+ const handleFileUpload = (0, react.useCallback)(async (e) => {
4742
+ if (!e.target.files?.length) return;
4743
+ try {
4744
+ await processFiles(Array.from(e.target.files));
4745
+ } catch (error) {
4746
+ console.error("[CopilotKit] Upload error:", error);
4747
+ }
4748
+ }, [processFiles]);
4749
+ const handleDragOver = (0, react.useCallback)((e) => {
4750
+ if (!configRef.current?.enabled) return;
4751
+ e.preventDefault();
4752
+ e.stopPropagation();
4753
+ setDragOver(true);
4754
+ }, []);
4755
+ const handleDragLeave = (0, react.useCallback)((e) => {
4756
+ e.preventDefault();
4757
+ e.stopPropagation();
4758
+ setDragOver(false);
4759
+ }, []);
4760
+ const handleDrop = (0, react.useCallback)(async (e) => {
4761
+ e.preventDefault();
4762
+ e.stopPropagation();
4763
+ setDragOver(false);
4764
+ if (!configRef.current?.enabled) return;
4765
+ const files = Array.from(e.dataTransfer.files);
4766
+ if (files.length > 0) try {
4767
+ await processFiles(files);
4768
+ } catch (error) {
4769
+ console.error("[CopilotKit] Drop error:", error);
4770
+ }
4771
+ }, [processFiles]);
4772
+ (0, react.useEffect)(() => {
4773
+ if (!enabled) return;
4774
+ const handlePaste = async (e) => {
4775
+ const target = e.target;
4776
+ if (!target || !containerRef.current?.contains(target)) return;
4777
+ const accept = configRef.current?.accept ?? "*/*";
4778
+ const fileItems = Array.from(e.clipboardData?.items || []).filter((item) => item.kind === "file" && item.getAsFile() !== null && (0, _copilotkit_shared.matchesAcceptFilter)(item.getAsFile(), accept));
4779
+ if (fileItems.length === 0) return;
4780
+ e.preventDefault();
4781
+ const files = fileItems.map((item) => item.getAsFile()).filter((f) => f !== null);
4782
+ try {
4783
+ await processFiles(files);
4784
+ } catch (error) {
4785
+ console.error("[CopilotKit] Paste error:", error);
4786
+ }
4787
+ };
4788
+ document.addEventListener("paste", handlePaste);
4789
+ return () => document.removeEventListener("paste", handlePaste);
4790
+ }, [enabled, processFiles]);
4791
+ return {
4792
+ attachments,
4793
+ enabled,
4794
+ dragOver,
4795
+ fileInputRef,
4796
+ containerRef,
4797
+ processFiles,
4798
+ handleFileUpload,
4799
+ handleDragOver,
4800
+ handleDragLeave,
4801
+ handleDrop,
4802
+ removeAttachment: (0, react.useCallback)((id) => {
4803
+ setAttachments((prev) => prev.filter((a) => a.id !== id));
4804
+ }, []),
4805
+ consumeAttachments: (0, react.useCallback)(() => {
4806
+ const ready = attachmentsRef.current.filter((a) => a.status === "ready");
4807
+ if (ready.length === 0) return ready;
4808
+ setAttachments((prev) => prev.filter((a) => a.status !== "ready"));
4809
+ if (fileInputRef.current) fileInputRef.current.value = "";
4810
+ return ready;
4811
+ }, [])
4812
+ };
4813
+ }
4814
+
3575
4815
  //#endregion
3576
4816
  //#region src/v2/components/chat/CopilotChatToolCallsView.tsx
3577
4817
  function CopilotChatToolCallsView({ message, messages = [] }) {
@@ -3688,9 +4928,19 @@ function CopilotChatAssistantMessage({ message, messages, isRunning, onThumbsUp,
3688
4928
  _CopilotChatAssistantMessage.CopyButton = ({ className, title, onClick, ...props }) => {
3689
4929
  const labels = useCopilotChatConfiguration()?.labels ?? CopilotChatDefaultLabels;
3690
4930
  const [copied, setCopied] = (0, react.useState)(false);
4931
+ const timerRef = (0, react.useRef)(null);
4932
+ (0, react.useEffect)(() => {
4933
+ return () => {
4934
+ if (timerRef.current !== null) clearTimeout(timerRef.current);
4935
+ };
4936
+ }, []);
3691
4937
  const handleClick = (event) => {
3692
4938
  setCopied(true);
3693
- setTimeout(() => setCopied(false), 2e3);
4939
+ if (timerRef.current !== null) clearTimeout(timerRef.current);
4940
+ timerRef.current = setTimeout(() => {
4941
+ timerRef.current = null;
4942
+ setCopied(false);
4943
+ }, 2e3);
3694
4944
  if (onClick) onClick(event);
3695
4945
  };
3696
4946
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ToolbarButton, {
@@ -3748,6 +4998,79 @@ CopilotChatAssistantMessage.ReadAloudButton.displayName = "CopilotChatAssistantM
3748
4998
  CopilotChatAssistantMessage.RegenerateButton.displayName = "CopilotChatAssistantMessage.RegenerateButton";
3749
4999
  var CopilotChatAssistantMessage_default = CopilotChatAssistantMessage;
3750
5000
 
5001
+ //#endregion
5002
+ //#region src/v2/components/chat/CopilotChatAttachmentRenderer.tsx
5003
+ const ImageAttachment = (0, react.memo)(function ImageAttachment({ src, className }) {
5004
+ const [error, setError] = (0, react.useState)(false);
5005
+ if (error) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
5006
+ className: cn("cpk:flex cpk:flex-col cpk:items-center cpk:justify-center cpk:rounded-lg cpk:bg-muted cpk:p-4 cpk:text-sm cpk:text-muted-foreground", className),
5007
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: "Failed to load image" })
5008
+ });
5009
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("img", {
5010
+ src,
5011
+ alt: "Image attachment",
5012
+ className: cn("cpk:max-w-full cpk:h-auto cpk:rounded-lg", className),
5013
+ onError: () => setError(true)
5014
+ });
5015
+ });
5016
+ const AudioAttachment = (0, react.memo)(function AudioAttachment({ src, filename, className }) {
5017
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
5018
+ className: cn("cpk:flex cpk:flex-col cpk:gap-1", className),
5019
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("audio", {
5020
+ src,
5021
+ controls: true,
5022
+ preload: "metadata",
5023
+ className: "cpk:max-w-[300px] cpk:w-full cpk:h-10"
5024
+ }), filename && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
5025
+ className: "cpk:text-xs cpk:text-muted-foreground cpk:truncate cpk:max-w-[300px]",
5026
+ children: filename
5027
+ })]
5028
+ });
5029
+ });
5030
+ const VideoAttachment = (0, react.memo)(function VideoAttachment({ src, className }) {
5031
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("video", {
5032
+ src,
5033
+ controls: true,
5034
+ preload: "metadata",
5035
+ className: cn("cpk:max-w-[400px] cpk:w-full cpk:rounded-lg", className)
5036
+ });
5037
+ });
5038
+ const DocumentAttachment = (0, react.memo)(function DocumentAttachment({ source, filename, className }) {
5039
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
5040
+ className: cn("cpk:inline-flex cpk:items-center cpk:gap-2 cpk:px-3 cpk:py-2 cpk:border cpk:border-border cpk:rounded-lg cpk:bg-muted", className),
5041
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
5042
+ className: "cpk:text-xs cpk:font-bold cpk:uppercase",
5043
+ children: (0, _copilotkit_shared.getDocumentIcon)(source.mimeType ?? "")
5044
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
5045
+ className: "cpk:text-sm cpk:text-muted-foreground cpk:truncate",
5046
+ children: filename || source.mimeType || "Unknown type"
5047
+ })]
5048
+ });
5049
+ });
5050
+ const CopilotChatAttachmentRenderer = ({ type, source, filename, className }) => {
5051
+ const src = (0, _copilotkit_shared.getSourceUrl)(source);
5052
+ switch (type) {
5053
+ case "image": return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ImageAttachment, {
5054
+ src,
5055
+ className
5056
+ });
5057
+ case "audio": return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(AudioAttachment, {
5058
+ src,
5059
+ filename,
5060
+ className
5061
+ });
5062
+ case "video": return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(VideoAttachment, {
5063
+ src,
5064
+ className
5065
+ });
5066
+ case "document": return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DocumentAttachment, {
5067
+ source,
5068
+ filename,
5069
+ className
5070
+ });
5071
+ }
5072
+ };
5073
+
3751
5074
  //#endregion
3752
5075
  //#region src/v2/components/chat/CopilotChatUserMessage.tsx
3753
5076
  function flattenUserMessageContent(content) {
@@ -3758,8 +5081,17 @@ function flattenUserMessageContent(content) {
3758
5081
  return "";
3759
5082
  }).filter((text) => text.length > 0).join("\n");
3760
5083
  }
5084
+ function getMediaParts(content) {
5085
+ if (!content || typeof content === "string") return [];
5086
+ return content.filter((part) => part.type === "image" || part.type === "audio" || part.type === "video" || part.type === "document");
5087
+ }
5088
+ function getFilename(part) {
5089
+ const meta = part.metadata;
5090
+ if (meta != null && typeof meta === "object" && "filename" in meta && typeof meta.filename === "string") return meta.filename;
5091
+ }
3761
5092
  function CopilotChatUserMessage({ message, onEditMessage, branchIndex, numberOfBranches, onSwitchToBranch, additionalToolbarItems, messageRenderer, toolbar, copyButton, editButton, branchNavigation, children, className, ...props }) {
3762
5093
  const flattenedContent = (0, react.useMemo)(() => flattenUserMessageContent(message.content), [message.content]);
5094
+ const mediaParts = (0, react.useMemo)(() => getMediaParts(message.content), [message.content]);
3763
5095
  const BoundMessageRenderer = renderSlot(messageRenderer, CopilotChatUserMessage.MessageRenderer, { content: flattenedContent });
3764
5096
  const BoundCopyButton = renderSlot(copyButton, CopilotChatUserMessage.CopyButton, { onClick: async () => {
3765
5097
  if (flattenedContent) try {
@@ -3806,7 +5138,18 @@ function CopilotChatUserMessage({ message, onEditMessage, branchIndex, numberOfB
3806
5138
  className: (0, tailwind_merge.twMerge)("copilotKitMessage copilotKitUserMessage cpk:flex cpk:flex-col cpk:items-end cpk:group cpk:pt-10", className),
3807
5139
  "data-message-id": message.id,
3808
5140
  ...props,
3809
- children: [BoundMessageRenderer, BoundToolbar]
5141
+ children: [
5142
+ BoundMessageRenderer,
5143
+ mediaParts.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
5144
+ className: "cpk:flex cpk:flex-col cpk:items-end cpk:gap-2 cpk:mt-2",
5145
+ children: mediaParts.map((part, index) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CopilotChatAttachmentRenderer, {
5146
+ type: part.type,
5147
+ source: part.source,
5148
+ filename: getFilename(part)
5149
+ }, index))
5150
+ }),
5151
+ BoundToolbar
5152
+ ]
3810
5153
  });
3811
5154
  }
3812
5155
  (function(_CopilotChatUserMessage) {
@@ -4104,6 +5447,7 @@ const CopilotChatSuggestionView = react.default.forwardRef(function CopilotChatS
4104
5447
  const isLoading = loadingSet.has(index) || suggestion.isLoading === true;
4105
5448
  const pill = renderSlot(suggestionSlot, CopilotChatSuggestionPill, {
4106
5449
  children: suggestion.title,
5450
+ className: suggestion.className,
4107
5451
  isLoading,
4108
5452
  type: "button",
4109
5453
  onClick: () => onSelectSuggestion?.(suggestion, index)
@@ -4140,9 +5484,43 @@ const CopilotChatSuggestionView = react.default.forwardRef(function CopilotChatS
4140
5484
  });
4141
5485
  CopilotChatSuggestionView.displayName = "CopilotChatSuggestionView";
4142
5486
 
5487
+ //#endregion
5488
+ //#region src/v2/components/chat/scroll-element-context.ts
5489
+ /**
5490
+ * Provides the scroll container element to child components that need it for
5491
+ * virtualization. Set by CopilotChatView.ScrollView; consumed by
5492
+ * CopilotChatMessageView to feed useVirtualizer's getScrollElement.
5493
+ *
5494
+ * Carries the element itself (not a ref) so that context consumers re-render
5495
+ * reactively when the scroll container is first mounted.
5496
+ */
5497
+ const ScrollElementContext = react.default.createContext(null);
5498
+
4143
5499
  //#endregion
4144
5500
  //#region src/v2/components/chat/CopilotChatMessageView.tsx
4145
5501
  /**
5502
+ * Resolves a slot value into a { Component, slotProps } pair, handling the three
5503
+ * slot forms: a component type, a className string, or a partial-props object.
5504
+ */
5505
+ function resolveSlotComponent(slot, DefaultComponent) {
5506
+ if (isReactComponentType(slot)) return {
5507
+ Component: slot,
5508
+ slotProps: void 0
5509
+ };
5510
+ if (typeof slot === "string") return {
5511
+ Component: DefaultComponent,
5512
+ slotProps: { className: slot }
5513
+ };
5514
+ if (slot && typeof slot === "object") return {
5515
+ Component: DefaultComponent,
5516
+ slotProps: slot
5517
+ };
5518
+ return {
5519
+ Component: DefaultComponent,
5520
+ slotProps: void 0
5521
+ };
5522
+ }
5523
+ /**
4146
5524
  * Memoized wrapper for assistant messages to prevent re-renders when other messages change.
4147
5525
  */
4148
5526
  const MemoizedAssistantMessage = react.default.memo(function MemoizedAssistantMessage({ message, messages, isRunning, AssistantMessageComponent, slotProps }) {
@@ -4161,7 +5539,6 @@ const MemoizedAssistantMessage = react.default.memo(function MemoizedAssistantMe
4161
5539
  if (prevToolCalls && nextToolCalls) for (let i = 0; i < prevToolCalls.length; i++) {
4162
5540
  const prevTc = prevToolCalls[i];
4163
5541
  const nextTc = nextToolCalls[i];
4164
- if (!prevTc || !nextTc) return false;
4165
5542
  if (prevTc.id !== nextTc.id) return false;
4166
5543
  if (prevTc.function.arguments !== nextTc.function.arguments) return false;
4167
5544
  }
@@ -4240,6 +5617,7 @@ const MemoizedCustomMessage = react.default.memo(function MemoizedCustomMessage(
4240
5617
  if (JSON.stringify(prevProps.stateSnapshot) !== JSON.stringify(nextProps.stateSnapshot)) return false;
4241
5618
  return true;
4242
5619
  });
5620
+ const VIRTUALIZE_THRESHOLD = 50;
4243
5621
  function CopilotChatMessageView({ messages = [], assistantMessage, userMessage, reasoningMessage, cursor, isRunning = false, children, className, ...props }) {
4244
5622
  const renderCustomMessage = useRenderCustomMessages();
4245
5623
  const { renderActivityMessage } = useRenderActivityMessage();
@@ -4248,12 +5626,14 @@ function CopilotChatMessageView({ messages = [], assistantMessage, userMessage,
4248
5626
  const [, forceUpdate] = (0, react.useReducer)((x) => x + 1, 0);
4249
5627
  (0, react.useEffect)(() => {
4250
5628
  if (!config?.agentId) return;
4251
- const agent = copilotkit.getAgent(config.agentId);
5629
+ const registryAgent = copilotkit.getAgent(config.agentId);
5630
+ const agent = getThreadClone(registryAgent, config.threadId) ?? registryAgent;
4252
5631
  if (!agent) return;
4253
5632
  const subscription = agent.subscribe({ onStateChanged: forceUpdate });
4254
5633
  return () => subscription.unsubscribe();
4255
5634
  }, [
4256
5635
  config?.agentId,
5636
+ config?.threadId,
4257
5637
  copilotkit,
4258
5638
  forceUpdate
4259
5639
  ]);
@@ -4271,9 +5651,34 @@ function CopilotChatMessageView({ messages = [], assistantMessage, userMessage,
4271
5651
  if (!resolvedRunId) return void 0;
4272
5652
  return copilotkit.getStateByRun(config.agentId, config.threadId, resolvedRunId);
4273
5653
  };
4274
- const deduplicatedMessages = [...new Map(messages.map((m) => [m.id, m])).values()];
5654
+ const deduplicatedMessages = (0, react.useMemo)(() => [...new Map(messages.map((m) => [m.id, m])).values()], [messages]);
4275
5655
  if (process.env.NODE_ENV === "development" && deduplicatedMessages.length < messages.length) console.warn(`CopilotChatMessageView: Deduplicated ${messages.length - deduplicatedMessages.length} message(s) with duplicate IDs.`);
4276
- const messageElements = deduplicatedMessages.flatMap((message) => {
5656
+ const { Component: AssistantComponent, slotProps: assistantSlotProps } = (0, react.useMemo)(() => resolveSlotComponent(assistantMessage, CopilotChatAssistantMessage_default), [assistantMessage]);
5657
+ const { Component: UserComponent, slotProps: userSlotProps } = (0, react.useMemo)(() => resolveSlotComponent(userMessage, CopilotChatUserMessage_default), [userMessage]);
5658
+ const { Component: ReasoningComponent, slotProps: reasoningSlotProps } = (0, react.useMemo)(() => resolveSlotComponent(reasoningMessage, CopilotChatReasoningMessage_default), [reasoningMessage]);
5659
+ const scrollElementFromCtx = (0, react.useContext)(ScrollElementContext);
5660
+ const scrollElement = scrollElementFromCtx && scrollElementFromCtx.clientHeight > 0 ? scrollElementFromCtx : null;
5661
+ (0, react.useEffect)(() => {
5662
+ if (process.env.NODE_ENV !== "production" && scrollElementFromCtx && scrollElementFromCtx.clientHeight === 0) console.warn("[CopilotKit] Chat scroll container has clientHeight=0 — virtualization disabled. Ensure the chat is rendered in a visible container with a non-zero height.");
5663
+ }, [scrollElementFromCtx]);
5664
+ const shouldVirtualize = !!scrollElement && !children && deduplicatedMessages.length > VIRTUALIZE_THRESHOLD;
5665
+ const virtualizer = (0, _tanstack_react_virtual.useVirtualizer)({
5666
+ count: shouldVirtualize ? deduplicatedMessages.length : 0,
5667
+ getScrollElement: () => scrollElement,
5668
+ estimateSize: () => 100,
5669
+ overscan: 5,
5670
+ measureElement: (el) => el?.getBoundingClientRect().height ?? 0,
5671
+ initialRect: {
5672
+ width: 0,
5673
+ height: 600
5674
+ }
5675
+ });
5676
+ const firstMessageId = deduplicatedMessages[0]?.id;
5677
+ (0, react.useLayoutEffect)(() => {
5678
+ if (!shouldVirtualize || !deduplicatedMessages.length) return;
5679
+ virtualizer.scrollToIndex(deduplicatedMessages.length - 1, { align: "end" });
5680
+ }, [shouldVirtualize, firstMessageId]);
5681
+ const renderMessageBlock = (message) => {
4277
5682
  const elements = [];
4278
5683
  const stateSnapshot = getStateSnapshotForMessage(message.id);
4279
5684
  if (renderCustomMessage) elements.push(/* @__PURE__ */ (0, react_jsx_runtime.jsx)(MemoizedCustomMessage, {
@@ -4282,58 +5687,38 @@ function CopilotChatMessageView({ messages = [], assistantMessage, userMessage,
4282
5687
  renderCustomMessage,
4283
5688
  stateSnapshot
4284
5689
  }, `${message.id}-custom-before`));
4285
- if (message.role === "assistant") {
4286
- let AssistantComponent = CopilotChatAssistantMessage_default;
4287
- let assistantSlotProps;
4288
- if (isReactComponentType(assistantMessage)) AssistantComponent = assistantMessage;
4289
- else if (typeof assistantMessage === "string") assistantSlotProps = { className: assistantMessage };
4290
- else if (assistantMessage && typeof assistantMessage === "object") assistantSlotProps = assistantMessage;
4291
- elements.push(/* @__PURE__ */ (0, react_jsx_runtime.jsx)(MemoizedAssistantMessage, {
4292
- message,
4293
- messages,
4294
- isRunning,
4295
- AssistantMessageComponent: AssistantComponent,
4296
- slotProps: assistantSlotProps
4297
- }, message.id));
4298
- } else if (message.role === "user") {
4299
- let UserComponent = CopilotChatUserMessage_default;
4300
- let userSlotProps;
4301
- if (isReactComponentType(userMessage)) UserComponent = userMessage;
4302
- else if (typeof userMessage === "string") userSlotProps = { className: userMessage };
4303
- else if (userMessage && typeof userMessage === "object") userSlotProps = userMessage;
4304
- elements.push(/* @__PURE__ */ (0, react_jsx_runtime.jsx)(MemoizedUserMessage, {
4305
- message,
4306
- UserMessageComponent: UserComponent,
4307
- slotProps: userSlotProps
4308
- }, message.id));
4309
- } else if (message.role === "activity") {
4310
- const activityMsg = message;
4311
- elements.push(/* @__PURE__ */ (0, react_jsx_runtime.jsx)(MemoizedActivityMessage, {
4312
- message: activityMsg,
4313
- renderActivityMessage
4314
- }, message.id));
4315
- } else if (message.role === "reasoning") {
4316
- let ReasoningComponent = CopilotChatReasoningMessage_default;
4317
- let reasoningSlotProps;
4318
- if (isReactComponentType(reasoningMessage)) ReasoningComponent = reasoningMessage;
4319
- else if (typeof reasoningMessage === "string") reasoningSlotProps = { className: reasoningMessage };
4320
- else if (reasoningMessage && typeof reasoningMessage === "object") reasoningSlotProps = reasoningMessage;
4321
- elements.push(/* @__PURE__ */ (0, react_jsx_runtime.jsx)(MemoizedReasoningMessage, {
4322
- message,
4323
- messages,
4324
- isRunning,
4325
- ReasoningMessageComponent: ReasoningComponent,
4326
- slotProps: reasoningSlotProps
4327
- }, message.id));
4328
- }
5690
+ if (message.role === "assistant") elements.push(/* @__PURE__ */ (0, react_jsx_runtime.jsx)(MemoizedAssistantMessage, {
5691
+ message,
5692
+ messages,
5693
+ isRunning,
5694
+ AssistantMessageComponent: AssistantComponent,
5695
+ slotProps: assistantSlotProps
5696
+ }, message.id));
5697
+ else if (message.role === "user") elements.push(/* @__PURE__ */ (0, react_jsx_runtime.jsx)(MemoizedUserMessage, {
5698
+ message,
5699
+ UserMessageComponent: UserComponent,
5700
+ slotProps: userSlotProps
5701
+ }, message.id));
5702
+ else if (message.role === "activity") elements.push(/* @__PURE__ */ (0, react_jsx_runtime.jsx)(MemoizedActivityMessage, {
5703
+ message,
5704
+ renderActivityMessage
5705
+ }, message.id));
5706
+ else if (message.role === "reasoning") elements.push(/* @__PURE__ */ (0, react_jsx_runtime.jsx)(MemoizedReasoningMessage, {
5707
+ message,
5708
+ messages,
5709
+ isRunning,
5710
+ ReasoningMessageComponent: ReasoningComponent,
5711
+ slotProps: reasoningSlotProps
5712
+ }, message.id));
4329
5713
  if (renderCustomMessage) elements.push(/* @__PURE__ */ (0, react_jsx_runtime.jsx)(MemoizedCustomMessage, {
4330
5714
  message,
4331
5715
  position: "after",
4332
5716
  renderCustomMessage,
4333
5717
  stateSnapshot
4334
5718
  }, `${message.id}-custom-after`));
4335
- return elements;
4336
- }).filter(Boolean);
5719
+ return elements.filter(Boolean);
5720
+ };
5721
+ const messageElements = shouldVirtualize ? [] : deduplicatedMessages.flatMap(renderMessageBlock);
4337
5722
  if (children) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
4338
5723
  "data-copilotkit": true,
4339
5724
  style: { display: "contents" },
@@ -4347,27 +5732,353 @@ function CopilotChatMessageView({ messages = [], assistantMessage, userMessage,
4347
5732
  const lastMessage = messages[messages.length - 1];
4348
5733
  const showCursor = isRunning && lastMessage?.role !== "reasoning";
4349
5734
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4350
- "data-copilotkit": true,
4351
- "data-testid": "copilot-message-list",
4352
- className: (0, tailwind_merge.twMerge)("copilotKitMessages cpk:flex cpk:flex-col", className),
4353
- ...props,
5735
+ "data-copilotkit": true,
5736
+ "data-testid": "copilot-message-list",
5737
+ className: (0, tailwind_merge.twMerge)("copilotKitMessages cpk:flex cpk:flex-col", className),
5738
+ ...props,
5739
+ children: [
5740
+ shouldVirtualize ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
5741
+ style: {
5742
+ height: virtualizer.getTotalSize(),
5743
+ position: "relative"
5744
+ },
5745
+ children: virtualizer.getVirtualItems().map((virtualItem) => {
5746
+ const message = deduplicatedMessages[virtualItem.index];
5747
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
5748
+ "data-index": virtualItem.index,
5749
+ ref: virtualizer.measureElement,
5750
+ style: {
5751
+ position: "absolute",
5752
+ top: 0,
5753
+ left: 0,
5754
+ width: "100%",
5755
+ transform: `translateY(${virtualItem.start}px)`
5756
+ },
5757
+ children: renderMessageBlock(message)
5758
+ }, message.id);
5759
+ })
5760
+ }) : messageElements,
5761
+ interruptElement,
5762
+ showCursor && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
5763
+ className: "cpk:mt-2",
5764
+ children: renderSlot(cursor, CopilotChatMessageView.Cursor, {})
5765
+ })
5766
+ ]
5767
+ });
5768
+ }
5769
+ CopilotChatMessageView.Cursor = function Cursor({ className, ...props }) {
5770
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
5771
+ "data-testid": "copilot-loading-cursor",
5772
+ className: (0, tailwind_merge.twMerge)("cpk:w-[11px] cpk:h-[11px] cpk:rounded-full cpk:bg-foreground cpk:animate-pulse-cursor cpk:ml-1", className),
5773
+ ...props
5774
+ });
5775
+ };
5776
+
5777
+ //#endregion
5778
+ //#region src/v2/components/chat/CopilotChatAttachmentQueue.tsx
5779
+ const CopilotChatAttachmentQueue = ({ attachments, onRemoveAttachment, className }) => {
5780
+ if (attachments.length === 0) return null;
5781
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
5782
+ className: cn("cpk:flex cpk:flex-wrap cpk:gap-2 cpk:p-2", className),
5783
+ children: attachments.map((attachment) => {
5784
+ const isMedia = attachment.type === "image" || attachment.type === "video";
5785
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
5786
+ className: cn("cpk:relative cpk:inline-flex cpk:rounded-lg cpk:overflow-hidden cpk:border cpk:border-border", isMedia ? "cpk:w-[72px] cpk:h-[72px]" : attachment.type === "audio" ? "cpk:min-w-[200px] cpk:max-w-[280px] cpk:flex-col cpk:p-1 cpk:pr-8" : "cpk:p-2 cpk:px-3 cpk:pr-8 cpk:max-w-[240px]"),
5787
+ children: [
5788
+ attachment.status === "uploading" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(UploadingOverlay, {}),
5789
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(AttachmentPreview, { attachment }),
5790
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
5791
+ onClick: () => onRemoveAttachment(attachment.id),
5792
+ className: cn("cpk:absolute cpk:bg-black/60 cpk:text-white cpk:border-none cpk:rounded-full cpk:w-5 cpk:h-5 cpk:flex cpk:items-center cpk:justify-center cpk:cursor-pointer cpk:text-[10px] cpk:z-20", isMedia ? "cpk:top-1 cpk:right-1" : "cpk:top-1.5 cpk:right-1.5"),
5793
+ "aria-label": "Remove attachment",
5794
+ children: "✕"
5795
+ })
5796
+ ]
5797
+ }, attachment.id);
5798
+ })
5799
+ });
5800
+ };
5801
+ function UploadingOverlay() {
5802
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
5803
+ className: "cpk:absolute cpk:inset-0 cpk:flex cpk:items-center cpk:justify-center cpk:bg-black/40 cpk:z-10",
5804
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { className: "cpk:w-5 cpk:h-5 cpk:border-2 cpk:border-white cpk:border-t-transparent cpk:rounded-full cpk:animate-spin" })
5805
+ });
5806
+ }
5807
+ function AttachmentPreview({ attachment }) {
5808
+ if (attachment.status === "uploading") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { className: "cpk:w-full cpk:h-full" });
5809
+ switch (attachment.type) {
5810
+ case "image": return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ImagePreview, { attachment });
5811
+ case "audio": return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(AudioPreview, { attachment });
5812
+ case "video": return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(VideoPreview, { attachment });
5813
+ case "document": return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DocumentPreview, { attachment });
5814
+ }
5815
+ }
5816
+ function Lightbox({ onClose, children }) {
5817
+ (0, react.useEffect)(() => {
5818
+ const handleKey = (e) => {
5819
+ if (e.key === "Escape") onClose();
5820
+ };
5821
+ document.addEventListener("keydown", handleKey);
5822
+ return () => document.removeEventListener("keydown", handleKey);
5823
+ }, [onClose]);
5824
+ if (typeof document === "undefined") return null;
5825
+ return (0, react_dom.createPortal)(/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
5826
+ className: "cpk:fixed cpk:inset-0 cpk:z-[9999] cpk:flex cpk:items-center cpk:justify-center cpk:bg-black/80 cpk:animate-fade-in",
5827
+ onClick: onClose,
5828
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
5829
+ onClick: onClose,
5830
+ className: "cpk:absolute cpk:top-4 cpk:right-4 cpk:text-white cpk:bg-white/10 cpk:hover:bg-white/20 cpk:rounded-full cpk:w-10 cpk:h-10 cpk:flex cpk:items-center cpk:justify-center cpk:cursor-pointer cpk:border-none cpk:z-10",
5831
+ "aria-label": "Close preview",
5832
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.X, { className: "cpk:w-5 cpk:h-5" })
5833
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
5834
+ onClick: (e) => e.stopPropagation(),
5835
+ children
5836
+ })]
5837
+ }), document.body);
5838
+ }
5839
+ /**
5840
+ * Hook that manages lightbox open/close and uses the View Transition API to
5841
+ * morph the thumbnail into fullscreen content.
5842
+ *
5843
+ * The trick: `view-transition-name` must live on exactly ONE element at a time.
5844
+ * - Old state (thumbnail visible): name is on the thumbnail.
5845
+ * - New state (lightbox visible): name moves to the lightbox content.
5846
+ * `flushSync` ensures React commits the DOM change synchronously inside the
5847
+ * `startViewTransition` callback so the API can snapshot old → new correctly.
5848
+ */
5849
+ function useLightbox() {
5850
+ const thumbnailRef = (0, react.useRef)(null);
5851
+ const [open, setOpen] = (0, react.useState)(false);
5852
+ const vtName = (0, react.useId)();
5853
+ return {
5854
+ thumbnailRef,
5855
+ vtName,
5856
+ open,
5857
+ openLightbox: (0, react.useCallback)(() => {
5858
+ const thumb = thumbnailRef.current;
5859
+ const doc = document;
5860
+ if (doc.startViewTransition && thumb) {
5861
+ thumb.style.viewTransitionName = vtName;
5862
+ doc.startViewTransition(() => {
5863
+ thumb.style.viewTransitionName = "";
5864
+ (0, react_dom.flushSync)(() => setOpen(true));
5865
+ });
5866
+ } else setOpen(true);
5867
+ }, []),
5868
+ closeLightbox: (0, react.useCallback)(() => {
5869
+ const thumb = thumbnailRef.current;
5870
+ const doc = document;
5871
+ if (doc.startViewTransition && thumb) doc.startViewTransition(() => {
5872
+ (0, react_dom.flushSync)(() => setOpen(false));
5873
+ thumb.style.viewTransitionName = vtName;
5874
+ }).finished.then(() => {
5875
+ thumb.style.viewTransitionName = "";
5876
+ }).catch(() => {
5877
+ thumb.style.viewTransitionName = "";
5878
+ });
5879
+ else setOpen(false);
5880
+ }, [])
5881
+ };
5882
+ }
5883
+ function ImagePreview({ attachment }) {
5884
+ const src = (0, _copilotkit_shared.getSourceUrl)(attachment.source);
5885
+ const { thumbnailRef, vtName, open, openLightbox, closeLightbox } = useLightbox();
5886
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("img", {
5887
+ ref: thumbnailRef,
5888
+ src,
5889
+ alt: attachment.filename || "Image attachment",
5890
+ className: "cpk:w-full cpk:h-full cpk:object-cover cpk:cursor-pointer",
5891
+ onClick: openLightbox
5892
+ }), open && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Lightbox, {
5893
+ onClose: closeLightbox,
5894
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("img", {
5895
+ style: { viewTransitionName: vtName },
5896
+ src,
5897
+ alt: attachment.filename || "Image attachment",
5898
+ className: "cpk:max-w-[90vw] cpk:max-h-[90vh] cpk:object-contain cpk:rounded-lg"
5899
+ })
5900
+ })] });
5901
+ }
5902
+ function AudioPreview({ attachment }) {
5903
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
5904
+ className: "cpk:flex cpk:flex-col cpk:gap-1 cpk:w-full",
5905
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("audio", {
5906
+ src: (0, _copilotkit_shared.getSourceUrl)(attachment.source),
5907
+ controls: true,
5908
+ preload: "metadata",
5909
+ className: "cpk:w-full cpk:h-8"
5910
+ }), attachment.filename && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
5911
+ className: "cpk:text-xs cpk:font-medium cpk:overflow-hidden cpk:text-ellipsis cpk:whitespace-nowrap",
5912
+ children: attachment.filename
5913
+ })]
5914
+ });
5915
+ }
5916
+ function VideoPreview({ attachment }) {
5917
+ const src = (0, _copilotkit_shared.getSourceUrl)(attachment.source);
5918
+ const { thumbnailRef, vtName, open, openLightbox, closeLightbox } = useLightbox();
5919
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
5920
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
5921
+ ref: thumbnailRef,
5922
+ className: "cpk:w-full cpk:h-full",
5923
+ children: attachment.thumbnail ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("img", {
5924
+ src: attachment.thumbnail,
5925
+ alt: attachment.filename || "Video thumbnail",
5926
+ className: "cpk:w-full cpk:h-full cpk:object-cover"
5927
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("video", {
5928
+ src,
5929
+ preload: "metadata",
5930
+ muted: true,
5931
+ className: "cpk:w-full cpk:h-full cpk:object-cover"
5932
+ })
5933
+ }),
5934
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
5935
+ onClick: openLightbox,
5936
+ className: "cpk:absolute cpk:inset-0 cpk:flex cpk:items-center cpk:justify-center cpk:z-10 cpk:cursor-pointer cpk:bg-black/20 cpk:border-none cpk:p-0",
5937
+ "aria-label": "Play video",
5938
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
5939
+ className: "cpk:w-8 cpk:h-8 cpk:rounded-full cpk:bg-black/60 cpk:flex cpk:items-center cpk:justify-center",
5940
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.Play, { className: "cpk:w-4 cpk:h-4 cpk:text-white cpk:ml-0.5" })
5941
+ })
5942
+ }),
5943
+ open && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Lightbox, {
5944
+ onClose: closeLightbox,
5945
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("video", {
5946
+ style: { viewTransitionName: vtName },
5947
+ src,
5948
+ controls: true,
5949
+ autoPlay: true,
5950
+ className: "cpk:max-w-[90vw] cpk:max-h-[90vh] cpk:rounded-lg"
5951
+ })
5952
+ })
5953
+ ] });
5954
+ }
5955
+ function isPdf(mimeType) {
5956
+ return !!mimeType && mimeType.includes("pdf");
5957
+ }
5958
+ function isText(mimeType) {
5959
+ return !!mimeType && mimeType.startsWith("text/");
5960
+ }
5961
+ function canPreviewInBrowser(mimeType) {
5962
+ return isPdf(mimeType) || isText(mimeType);
5963
+ }
5964
+ /**
5965
+ * Convert a base64-encoded data source to a blob: URL that browsers will
5966
+ * render inside an iframe (data: URLs are blocked for PDFs in most browsers).
5967
+ */
5968
+ function useBlobUrl(attachment) {
5969
+ const [url, setUrl] = (0, react.useState)(null);
5970
+ (0, react.useEffect)(() => {
5971
+ if (attachment.source.type !== "data") return;
5972
+ try {
5973
+ const binary = atob(attachment.source.value);
5974
+ const bytes = new Uint8Array(binary.length);
5975
+ for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);
5976
+ const blob = new Blob([bytes], { type: attachment.source.mimeType || "application/octet-stream" });
5977
+ const blobUrl = URL.createObjectURL(blob);
5978
+ setUrl(blobUrl);
5979
+ return () => URL.revokeObjectURL(blobUrl);
5980
+ } catch (error) {
5981
+ console.error("[CopilotKit] Failed to decode attachment data:", error);
5982
+ setUrl(null);
5983
+ }
5984
+ }, [
5985
+ attachment.source.type,
5986
+ attachment.source.value,
5987
+ attachment.source.mimeType
5988
+ ]);
5989
+ if (attachment.source.type === "url") return attachment.source.value;
5990
+ return url;
5991
+ }
5992
+ function DocumentLightboxContent({ attachment, vtName }) {
5993
+ const mimeType = attachment.source.mimeType;
5994
+ const blobUrl = useBlobUrl(attachment);
5995
+ if (isPdf(mimeType)) {
5996
+ if (!blobUrl) return null;
5997
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("iframe", {
5998
+ style: { viewTransitionName: vtName },
5999
+ src: blobUrl,
6000
+ title: attachment.filename || "PDF preview",
6001
+ className: "cpk:w-[90vw] cpk:h-[90vh] cpk:max-w-[1000px] cpk:rounded-lg cpk:bg-white"
6002
+ });
6003
+ }
6004
+ if (isText(mimeType)) {
6005
+ const textContent = attachment.source.type === "data" ? (() => {
6006
+ try {
6007
+ return atob(attachment.source.value);
6008
+ } catch {
6009
+ return attachment.source.value;
6010
+ }
6011
+ })() : null;
6012
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
6013
+ style: { viewTransitionName: vtName },
6014
+ className: "cpk:w-[90vw] cpk:max-w-[800px] cpk:max-h-[90vh] cpk:overflow-auto cpk:rounded-lg cpk:bg-white cpk:dark:bg-gray-900 cpk:p-6",
6015
+ children: [attachment.filename && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
6016
+ className: "cpk:text-sm cpk:font-medium cpk:text-gray-500 cpk:dark:text-gray-400 cpk:mb-4 cpk:pb-2 cpk:border-b cpk:border-gray-200 cpk:dark:border-gray-700",
6017
+ children: attachment.filename
6018
+ }), textContent ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("pre", {
6019
+ className: "cpk:text-sm cpk:whitespace-pre-wrap cpk:break-words cpk:text-gray-800 cpk:dark:text-gray-200 cpk:font-mono cpk:m-0",
6020
+ children: textContent
6021
+ }) : blobUrl ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("iframe", {
6022
+ src: blobUrl,
6023
+ title: attachment.filename || "Text preview",
6024
+ className: "cpk:w-full cpk:h-[80vh] cpk:border-none"
6025
+ }) : null]
6026
+ });
6027
+ }
6028
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
6029
+ style: { viewTransitionName: vtName },
6030
+ className: "cpk:flex cpk:flex-col cpk:items-center cpk:gap-4 cpk:p-8 cpk:rounded-lg cpk:bg-white cpk:dark:bg-gray-900",
4354
6031
  children: [
4355
- messageElements,
4356
- interruptElement,
4357
- showCursor && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
4358
- className: "cpk:mt-2",
4359
- children: renderSlot(cursor, CopilotChatMessageView.Cursor, {})
6032
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
6033
+ className: "cpk:w-16 cpk:h-16 cpk:rounded-xl cpk:bg-primary cpk:text-primary-foreground cpk:flex cpk:items-center cpk:justify-center cpk:text-xl cpk:font-bold",
6034
+ children: (0, _copilotkit_shared.getDocumentIcon)(mimeType ?? "")
6035
+ }),
6036
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
6037
+ className: "cpk:text-center",
6038
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
6039
+ className: "cpk:text-base cpk:font-medium cpk:text-gray-800 cpk:dark:text-gray-200",
6040
+ children: attachment.filename || "Document"
6041
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
6042
+ className: "cpk:text-sm cpk:text-gray-500 cpk:dark:text-gray-400 cpk:mt-1",
6043
+ children: [mimeType || "Unknown type", attachment.size != null && ` · ${(0, _copilotkit_shared.formatFileSize)(attachment.size)}`]
6044
+ })]
6045
+ }),
6046
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
6047
+ className: "cpk:text-xs cpk:text-gray-400 cpk:dark:text-gray-500",
6048
+ children: "No preview available for this file type"
4360
6049
  })
4361
6050
  ]
4362
6051
  });
4363
6052
  }
4364
- CopilotChatMessageView.Cursor = function Cursor({ className, ...props }) {
4365
- return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
4366
- "data-testid": "copilot-loading-cursor",
4367
- className: (0, tailwind_merge.twMerge)("cpk:w-[11px] cpk:h-[11px] cpk:rounded-full cpk:bg-foreground cpk:animate-pulse-cursor cpk:ml-1", className),
4368
- ...props
4369
- });
4370
- };
6053
+ function DocumentPreview({ attachment }) {
6054
+ const { thumbnailRef, vtName, open, openLightbox, closeLightbox } = useLightbox();
6055
+ const mimeType = attachment.source.mimeType;
6056
+ const previewable = canPreviewInBrowser(mimeType);
6057
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
6058
+ ref: thumbnailRef,
6059
+ className: cn("cpk:flex cpk:items-center cpk:gap-2", previewable && "cpk:cursor-pointer"),
6060
+ onClick: previewable ? openLightbox : void 0,
6061
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
6062
+ className: "cpk:w-8 cpk:h-8 cpk:rounded-md cpk:bg-primary cpk:text-primary-foreground cpk:flex cpk:items-center cpk:justify-center cpk:text-[10px] cpk:font-semibold cpk:shrink-0",
6063
+ children: (0, _copilotkit_shared.getDocumentIcon)(mimeType ?? "")
6064
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
6065
+ className: "cpk:flex cpk:flex-col cpk:min-w-0",
6066
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
6067
+ className: "cpk:text-xs cpk:font-medium cpk:break-all cpk:leading-tight",
6068
+ children: attachment.filename || "Document"
6069
+ }), attachment.size != null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
6070
+ className: "cpk:text-[11px] cpk:text-muted-foreground",
6071
+ children: (0, _copilotkit_shared.formatFileSize)(attachment.size)
6072
+ })]
6073
+ })]
6074
+ }), open && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Lightbox, {
6075
+ onClose: closeLightbox,
6076
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DocumentLightboxContent, {
6077
+ attachment,
6078
+ vtName
6079
+ })
6080
+ })] });
6081
+ }
4371
6082
 
4372
6083
  //#endregion
4373
6084
  //#region src/v2/hooks/use-keyboard-height.tsx
@@ -4413,7 +6124,19 @@ function useKeyboardHeight() {
4413
6124
  //#endregion
4414
6125
  //#region src/v2/components/chat/CopilotChatView.tsx
4415
6126
  const FEATHER_HEIGHT = 96;
4416
- function CopilotChatView({ messageView, input, scrollView, suggestionView, welcomeScreen, messages = [], autoScroll = true, isRunning = false, suggestions, suggestionLoadingIndexes, onSelectSuggestion, onSubmitMessage, onStop, inputMode, inputValue, onInputChange, onStartTranscribe, onCancelTranscribe, onFinishTranscribe, onFinishTranscribeWithAudio, disclaimer, children, className, ...props }) {
6127
+ function DropOverlay() {
6128
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
6129
+ className: cn("cpk:absolute cpk:inset-0 cpk:z-50 cpk:pointer-events-none", "cpk:flex cpk:items-center cpk:justify-center", "cpk:bg-primary/5 cpk:backdrop-blur-[2px]", "cpk:border-2 cpk:border-dashed cpk:border-primary/40 cpk:rounded-lg cpk:m-2"),
6130
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
6131
+ className: "cpk:flex cpk:flex-col cpk:items-center cpk:gap-2 cpk:text-primary/70",
6132
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.Upload, { className: "cpk:w-8 cpk:h-8" }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
6133
+ className: "cpk:text-sm cpk:font-medium",
6134
+ children: "Drop files here"
6135
+ })]
6136
+ })
6137
+ });
6138
+ }
6139
+ 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, disclaimer, children, className, ...props }) {
4417
6140
  const inputContainerRef = (0, react.useRef)(null);
4418
6141
  const [inputContainerHeight, setInputContainerHeight] = (0, react.useState)(0);
4419
6142
  const [isResizing, setIsResizing] = (0, react.useState)(false);
@@ -4460,6 +6183,7 @@ function CopilotChatView({ messageView, input, scrollView, suggestionView, welco
4460
6183
  onCancelTranscribe,
4461
6184
  onFinishTranscribe,
4462
6185
  onFinishTranscribeWithAudio,
6186
+ onAddFile,
4463
6187
  positioning: "static",
4464
6188
  keyboardHeight: isKeyboardOpen ? keyboardHeight : 0,
4465
6189
  containerRef: inputContainerRef,
@@ -4500,21 +6224,34 @@ function CopilotChatView({ messageView, input, scrollView, suggestionView, welco
4500
6224
  onCancelTranscribe,
4501
6225
  onFinishTranscribe,
4502
6226
  onFinishTranscribeWithAudio,
6227
+ onAddFile,
4503
6228
  positioning: "static",
4504
6229
  showDisclaimer: true,
4505
6230
  ...disclaimer !== void 0 ? { disclaimer } : {}
4506
6231
  });
4507
- const BoundWelcomeScreen = renderSlot(welcomeScreen === true ? void 0 : welcomeScreen, CopilotChatView.WelcomeScreen, {
4508
- input: BoundInputForWelcome,
6232
+ const welcomeScreenSlot = welcomeScreen === true ? void 0 : welcomeScreen;
6233
+ const inputWithAttachments = /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
6234
+ className: "cpk:w-full",
6235
+ children: [attachments && attachments.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CopilotChatAttachmentQueue, {
6236
+ attachments,
6237
+ onRemoveAttachment: (id) => onRemoveAttachment?.(id),
6238
+ className: "cpk:mb-2"
6239
+ }), BoundInputForWelcome]
6240
+ });
6241
+ const BoundWelcomeScreen = renderSlot(welcomeScreenSlot, CopilotChatView.WelcomeScreen, {
6242
+ input: inputWithAttachments,
4509
6243
  suggestionView: BoundSuggestionView ?? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_jsx_runtime.Fragment, {})
4510
6244
  });
4511
- return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
6245
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4512
6246
  "data-copilotkit": true,
4513
6247
  "data-testid": "copilot-chat",
4514
6248
  "data-copilot-running": isRunning ? "true" : "false",
4515
- className: (0, tailwind_merge.twMerge)("copilotKitChat cpk:relative cpk:h-full cpk:flex cpk:flex-col", className),
6249
+ onDragOver,
6250
+ onDragLeave,
6251
+ onDrop,
6252
+ className: cn("copilotKitChat cpk:relative cpk:h-full cpk:flex cpk:flex-col", className),
4516
6253
  ...props,
4517
- children: BoundWelcomeScreen
6254
+ children: [dragOver && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DropOverlay, {}), BoundWelcomeScreen]
4518
6255
  });
4519
6256
  }
4520
6257
  if (children) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
@@ -4531,39 +6268,66 @@ function CopilotChatView({ messageView, input, scrollView, suggestionView, welco
4531
6268
  "data-copilotkit": true,
4532
6269
  "data-testid": "copilot-chat",
4533
6270
  "data-copilot-running": isRunning ? "true" : "false",
4534
- className: (0, tailwind_merge.twMerge)("copilotKitChat cpk:relative cpk:h-full cpk:flex cpk:flex-col", className),
6271
+ onDragOver,
6272
+ onDragLeave,
6273
+ onDrop,
6274
+ className: cn("copilotKitChat cpk:relative cpk:h-full cpk:flex cpk:flex-col", className),
4535
6275
  ...props,
4536
- children: [BoundScrollView, BoundInput]
6276
+ children: [
6277
+ dragOver && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DropOverlay, {}),
6278
+ BoundScrollView,
6279
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
6280
+ className: "cpk:max-w-3xl cpk:mx-auto cpk:w-full",
6281
+ children: attachments && attachments.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CopilotChatAttachmentQueue, {
6282
+ attachments,
6283
+ onRemoveAttachment: (id) => onRemoveAttachment?.(id),
6284
+ className: "cpk:px-4"
6285
+ })
6286
+ }),
6287
+ BoundInput
6288
+ ]
4537
6289
  });
4538
6290
  }
4539
6291
  (function(_CopilotChatView) {
4540
6292
  const ScrollContent = ({ children, scrollToBottomButton, feather, inputContainerHeight, isResizing }) => {
4541
- const { isAtBottom, scrollToBottom } = (0, use_stick_to_bottom.useStickToBottomContext)();
6293
+ const { isAtBottom, scrollToBottom, scrollRef } = (0, use_stick_to_bottom.useStickToBottomContext)();
6294
+ const [scrollEl, setScrollEl] = (0, react.useState)(null);
6295
+ (0, react.useLayoutEffect)(() => {
6296
+ setScrollEl(scrollRef.current ?? null);
6297
+ }, []);
4542
6298
  const BoundFeather = renderSlot(feather, CopilotChatView.Feather, {});
4543
- return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
4544
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(use_stick_to_bottom.StickToBottom.Content, {
4545
- className: "cpk:overflow-y-auto cpk:overflow-x-hidden",
4546
- style: {
4547
- flex: "1 1 0%",
4548
- minHeight: 0
4549
- },
4550
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
4551
- className: "cpk:px-4 cpk:sm:px-0 cpk:[div[data-sidebar-chat]_&]:px-8 cpk:[div[data-popup-chat]_&]:px-6",
4552
- children
6299
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ScrollElementContext.Provider, {
6300
+ value: scrollEl,
6301
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
6302
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(use_stick_to_bottom.StickToBottom.Content, {
6303
+ className: "cpk:overflow-y-auto cpk:overflow-x-hidden",
6304
+ style: {
6305
+ flex: "1 1 0%",
6306
+ minHeight: 0
6307
+ },
6308
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
6309
+ className: "cpk:px-4 cpk:sm:px-0 cpk:[div[data-sidebar-chat]_&]:px-8 cpk:[div[data-popup-chat]_&]:px-6",
6310
+ children
6311
+ })
6312
+ }),
6313
+ BoundFeather,
6314
+ !isAtBottom && !isResizing && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
6315
+ className: "cpk:absolute cpk:inset-x-0 cpk:flex cpk:justify-center cpk:z-30 cpk:pointer-events-none",
6316
+ style: { bottom: `${inputContainerHeight + FEATHER_HEIGHT + 16}px` },
6317
+ children: renderSlot(scrollToBottomButton, CopilotChatView.ScrollToBottomButton, { onClick: () => scrollToBottom() })
4553
6318
  })
4554
- }),
4555
- BoundFeather,
4556
- !isAtBottom && !isResizing && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
4557
- className: "cpk:absolute cpk:inset-x-0 cpk:flex cpk:justify-center cpk:z-30 cpk:pointer-events-none",
4558
- style: { bottom: `${inputContainerHeight + FEATHER_HEIGHT + 16}px` },
4559
- children: renderSlot(scrollToBottomButton, CopilotChatView.ScrollToBottomButton, { onClick: () => scrollToBottom() })
4560
- })
4561
- ] });
6319
+ ] })
6320
+ });
4562
6321
  };
4563
6322
  _CopilotChatView.ScrollView = ({ children, autoScroll = true, scrollToBottomButton, feather, inputContainerHeight = 0, isResizing = false, className, ...props }) => {
4564
6323
  const [hasMounted, setHasMounted] = (0, react.useState)(false);
4565
6324
  const { scrollRef, contentRef, scrollToBottom } = (0, use_stick_to_bottom.useStickToBottom)();
4566
6325
  const [showScrollButton, setShowScrollButton] = (0, react.useState)(false);
6326
+ const [nonAutoScrollEl, setNonAutoScrollEl] = (0, react.useState)(null);
6327
+ const nonAutoScrollRefCallback = (0, react.useCallback)((el) => {
6328
+ scrollRef.current = el;
6329
+ setNonAutoScrollEl(el);
6330
+ }, []);
4567
6331
  (0, react.useEffect)(() => {
4568
6332
  setHasMounted(true);
4569
6333
  }, []);
@@ -4592,23 +6356,26 @@ function CopilotChatView({ messageView, input, scrollView, suggestionView, welco
4592
6356
  });
4593
6357
  if (!autoScroll) {
4594
6358
  const BoundFeather = renderSlot(feather, CopilotChatView.Feather, {});
4595
- return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4596
- ref: scrollRef,
4597
- className: cn("cpk:h-full cpk:max-h-full cpk:flex cpk:flex-col cpk:min-h-0 cpk:overflow-y-auto cpk:overflow-x-hidden cpk:relative", className),
4598
- ...props,
4599
- children: [
4600
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
4601
- ref: contentRef,
4602
- className: "cpk:px-4 cpk:sm:px-0 cpk:[div[data-sidebar-chat]_&]:px-8 cpk:[div[data-popup-chat]_&]:px-6",
4603
- children
4604
- }),
4605
- BoundFeather,
4606
- showScrollButton && !isResizing && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
4607
- className: "cpk:absolute cpk:inset-x-0 cpk:flex cpk:justify-center cpk:z-30 cpk:pointer-events-none",
4608
- style: { bottom: `${inputContainerHeight + FEATHER_HEIGHT + 16}px` },
4609
- children: renderSlot(scrollToBottomButton, CopilotChatView.ScrollToBottomButton, { onClick: () => scrollToBottom() })
4610
- })
4611
- ]
6359
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ScrollElementContext.Provider, {
6360
+ value: nonAutoScrollEl,
6361
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
6362
+ ref: nonAutoScrollRefCallback,
6363
+ className: cn("cpk:h-full cpk:max-h-full cpk:flex cpk:flex-col cpk:min-h-0 cpk:overflow-y-auto cpk:overflow-x-hidden cpk:relative", className),
6364
+ ...props,
6365
+ children: [
6366
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
6367
+ ref: contentRef,
6368
+ className: "cpk:px-4 cpk:sm:px-0 cpk:[div[data-sidebar-chat]_&]:px-8 cpk:[div[data-popup-chat]_&]:px-6",
6369
+ children
6370
+ }),
6371
+ BoundFeather,
6372
+ showScrollButton && !isResizing && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
6373
+ className: "cpk:absolute cpk:inset-x-0 cpk:flex cpk:justify-center cpk:z-30 cpk:pointer-events-none",
6374
+ style: { bottom: `${inputContainerHeight + FEATHER_HEIGHT + 16}px` },
6375
+ children: renderSlot(scrollToBottomButton, CopilotChatView.ScrollToBottomButton, { onClick: () => scrollToBottom() })
6376
+ })
6377
+ ]
6378
+ })
4612
6379
  });
4613
6380
  }
4614
6381
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(use_stick_to_bottom.StickToBottom, {
@@ -4800,11 +6567,15 @@ async function transcribeAudio(core, audioBlob, filename = "recording.webm") {
4800
6567
 
4801
6568
  //#endregion
4802
6569
  //#region src/v2/components/chat/CopilotChat.tsx
4803
- function CopilotChat({ agentId, threadId, labels, chatView, isModalDefaultOpen, onError, ...props }) {
6570
+ function CopilotChat({ agentId, threadId, labels, chatView, isModalDefaultOpen, attachments: attachmentsConfig, onError, throttleMs, ...props }) {
4804
6571
  const existingConfig = useCopilotChatConfiguration();
4805
6572
  const resolvedAgentId = agentId ?? existingConfig?.agentId ?? _copilotkit_shared.DEFAULT_AGENT_ID;
4806
6573
  const resolvedThreadId = (0, react.useMemo)(() => threadId ?? existingConfig?.threadId ?? (0, _copilotkit_shared.randomUUID)(), [threadId, existingConfig?.threadId]);
4807
- const { agent } = useAgent({ agentId: resolvedAgentId });
6574
+ const { agent } = useAgent({
6575
+ agentId: resolvedAgentId,
6576
+ threadId: resolvedThreadId,
6577
+ throttleMs
6578
+ });
4808
6579
  const { copilotkit } = useCopilotKit();
4809
6580
  const { suggestions: autoSuggestions } = useSuggestions({ agentId: resolvedAgentId });
4810
6581
  const { checkFeature } = useLicenseContext();
@@ -4833,6 +6604,7 @@ function CopilotChat({ agentId, threadId, labels, chatView, isModalDefaultOpen,
4833
6604
  const [inputValue, setInputValue] = (0, react.useState)("");
4834
6605
  const [transcriptionError, setTranscriptionError] = (0, react.useState)(null);
4835
6606
  const [isTranscribing, setIsTranscribing] = (0, react.useState)(false);
6607
+ const { attachments: selectedAttachments, enabled: attachmentsEnabled, dragOver, fileInputRef, containerRef: chatContainerRef, handleFileUpload, handleDragOver, handleDragLeave, handleDrop, removeAttachment, consumeAttachments } = useAttachments({ config: attachmentsConfig });
4836
6608
  const isTranscriptionEnabled = copilotkit.audioFileTranscriptionEnabled;
4837
6609
  const isMediaRecorderSupported = typeof window !== "undefined" && typeof MediaRecorder !== "undefined";
4838
6610
  const { messageView: providedMessageView, suggestionView: providedSuggestionView, onStop: providedStopHandler, ...restProps } = props;
@@ -4848,7 +6620,6 @@ function CopilotChat({ agentId, threadId, labels, chatView, isModalDefaultOpen,
4848
6620
  console.error("CopilotChat: connectAgent failed", error);
4849
6621
  }
4850
6622
  };
4851
- agent.threadId = resolvedThreadId;
4852
6623
  connect(agent);
4853
6624
  return () => {
4854
6625
  detached = true;
@@ -4861,7 +6632,31 @@ function CopilotChat({ agentId, threadId, labels, chatView, isModalDefaultOpen,
4861
6632
  resolvedAgentId
4862
6633
  ]);
4863
6634
  const onSubmitInput = (0, react.useCallback)(async (value) => {
4864
- agent.addMessage({
6635
+ if (selectedAttachments.some((a) => a.status === "uploading")) {
6636
+ console.error("[CopilotKit] Cannot send while attachments are uploading");
6637
+ return;
6638
+ }
6639
+ const readyAttachments = consumeAttachments();
6640
+ if (readyAttachments.length > 0) {
6641
+ const contentParts = [];
6642
+ if (value.trim()) contentParts.push({
6643
+ type: "text",
6644
+ text: value
6645
+ });
6646
+ for (const att of readyAttachments) contentParts.push({
6647
+ type: att.type,
6648
+ source: att.source,
6649
+ metadata: {
6650
+ ...att.filename ? { filename: att.filename } : {},
6651
+ ...att.metadata
6652
+ }
6653
+ });
6654
+ agent.addMessage({
6655
+ id: (0, _copilotkit_shared.randomUUID)(),
6656
+ role: "user",
6657
+ content: contentParts
6658
+ });
6659
+ } else agent.addMessage({
4865
6660
  id: (0, _copilotkit_shared.randomUUID)(),
4866
6661
  role: "user",
4867
6662
  content: value
@@ -4872,7 +6667,11 @@ function CopilotChat({ agentId, threadId, labels, chatView, isModalDefaultOpen,
4872
6667
  } catch (error) {
4873
6668
  console.error("CopilotChat: runAgent failed", error);
4874
6669
  }
4875
- }, [agent]);
6670
+ }, [
6671
+ agent,
6672
+ selectedAttachments,
6673
+ consumeAttachments
6674
+ ]);
4876
6675
  const handleSelectSuggestion = (0, react.useCallback)(async (suggestion) => {
4877
6676
  agent.addMessage({
4878
6677
  id: (0, _copilotkit_shared.randomUUID)(),
@@ -4959,21 +6758,33 @@ function CopilotChat({ agentId, threadId, labels, chatView, isModalDefaultOpen,
4959
6758
  return () => clearTimeout(timer);
4960
6759
  }
4961
6760
  }, [transcriptionError]);
4962
- const mergedProps = (0, ts_deepmerge.merge)({
6761
+ const stableMessageView = useShallowStableRef(typeof providedMessageView === "string" ? { className: providedMessageView } : providedMessageView);
6762
+ const stableSuggestionView = useShallowStableRef(providedSuggestionView);
6763
+ const handleAddFile = (0, react.useCallback)(() => {
6764
+ setTimeout(() => {
6765
+ fileInputRef.current?.click();
6766
+ }, 100);
6767
+ }, []);
6768
+ const mergedProps = {
4963
6769
  isRunning: agent.isRunning,
4964
6770
  suggestions: autoSuggestions,
4965
6771
  onSelectSuggestion: handleSelectSuggestion,
4966
- suggestionView: providedSuggestionView
4967
- }, {
4968
- ...restProps,
4969
- ...typeof providedMessageView === "string" ? { messageView: { className: providedMessageView } } : providedMessageView !== void 0 ? { messageView: providedMessageView } : {}
4970
- });
6772
+ suggestionView: stableSuggestionView,
6773
+ ...restProps
6774
+ };
6775
+ if (stableMessageView !== void 0) mergedProps.messageView = stableMessageView;
4971
6776
  const hasMessages = agent.messages.length > 0;
4972
6777
  const effectiveStopHandler = agent.isRunning && hasMessages ? providedStopHandler ?? stopCurrentRun : providedStopHandler;
4973
6778
  const showTranscription = isTranscriptionEnabled && isMediaRecorderSupported;
4974
6779
  const effectiveMode = isTranscribing ? "processing" : transcribeMode;
4975
- const RenderedChatView = renderSlot(chatView, CopilotChatView, (0, ts_deepmerge.merge)(mergedProps, {
4976
- messages: (0, react.useMemo)(() => [...agent.messages], [JSON.stringify(agent.messages)]),
6780
+ const messages = (0, react.useMemo)(() => [...agent.messages], [agent.messages.map((m) => {
6781
+ const contentKey = typeof m.content === "string" ? m.content.length : Array.isArray(m.content) ? m.content.length : 0;
6782
+ const toolCallsKey = "toolCalls" in m && Array.isArray(m.toolCalls) ? m.toolCalls.map((tc) => `${tc.id}:${tc.function?.arguments?.length ?? 0}`).join(";") : "";
6783
+ return `${m.id}:${m.role}:${contentKey}:${toolCallsKey}`;
6784
+ }).join(",")]);
6785
+ const RenderedChatView = renderSlot(chatView, CopilotChatView, {
6786
+ ...mergedProps,
6787
+ messages,
4977
6788
  onSubmitMessage: onSubmitInput,
4978
6789
  onStop: effectiveStopHandler,
4979
6790
  inputMode: effectiveMode,
@@ -4982,32 +6793,51 @@ function CopilotChat({ agentId, threadId, labels, chatView, isModalDefaultOpen,
4982
6793
  onStartTranscribe: showTranscription ? handleStartTranscribe : void 0,
4983
6794
  onCancelTranscribe: showTranscription ? handleCancelTranscribe : void 0,
4984
6795
  onFinishTranscribe: showTranscription ? handleFinishTranscribe : void 0,
4985
- onFinishTranscribeWithAudio: showTranscription ? handleFinishTranscribeWithAudio : void 0
4986
- }));
4987
- return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(CopilotChatConfigurationProvider, {
6796
+ onFinishTranscribeWithAudio: showTranscription ? handleFinishTranscribeWithAudio : void 0,
6797
+ attachments: selectedAttachments,
6798
+ onRemoveAttachment: removeAttachment,
6799
+ onAddFile: attachmentsEnabled ? handleAddFile : void 0,
6800
+ dragOver,
6801
+ onDragOver: handleDragOver,
6802
+ onDragLeave: handleDragLeave,
6803
+ onDrop: handleDrop
6804
+ });
6805
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CopilotChatConfigurationProvider, {
4988
6806
  agentId: resolvedAgentId,
4989
6807
  threadId: resolvedThreadId,
4990
6808
  labels,
4991
6809
  isModalDefaultOpen,
4992
- children: [
4993
- !isChatLicensed && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(InlineFeatureWarning, { featureName: "Chat" }),
4994
- transcriptionError && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
4995
- style: {
4996
- position: "absolute",
4997
- bottom: "100px",
4998
- left: "50%",
4999
- transform: "translateX(-50%)",
5000
- backgroundColor: "#ef4444",
5001
- color: "white",
5002
- padding: "8px 16px",
5003
- borderRadius: "8px",
5004
- fontSize: "14px",
5005
- zIndex: 50
5006
- },
5007
- children: transcriptionError
5008
- }),
5009
- RenderedChatView
5010
- ]
6810
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
6811
+ ref: chatContainerRef,
6812
+ style: { display: "contents" },
6813
+ children: [
6814
+ attachmentsEnabled && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
6815
+ type: "file",
6816
+ multiple: true,
6817
+ ref: fileInputRef,
6818
+ onChange: handleFileUpload,
6819
+ accept: attachmentsConfig?.accept ?? "*/*",
6820
+ style: { display: "none" }
6821
+ }),
6822
+ !isChatLicensed && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(InlineFeatureWarning, { featureName: "Chat" }),
6823
+ transcriptionError && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
6824
+ style: {
6825
+ position: "absolute",
6826
+ bottom: "100px",
6827
+ left: "50%",
6828
+ transform: "translateX(-50%)",
6829
+ backgroundColor: "#ef4444",
6830
+ color: "white",
6831
+ padding: "8px 16px",
6832
+ borderRadius: "8px",
6833
+ fontSize: "14px",
6834
+ zIndex: 50
6835
+ },
6836
+ children: transcriptionError
6837
+ }),
6838
+ RenderedChatView
6839
+ ]
6840
+ })
5011
6841
  });
5012
6842
  }
5013
6843
  (function(_CopilotChat) {
@@ -5872,6 +7702,227 @@ function useToast() {
5872
7702
  if (!context) throw new Error("useToast must be used within a ToastProvider");
5873
7703
  return context;
5874
7704
  }
7705
+ function formatBannerMessage(message) {
7706
+ const jsonMatch = message.match(/'message':\s*'([^']+)'/);
7707
+ if (jsonMatch) return jsonMatch[1];
7708
+ let cleaned = message.split(" - ")[0];
7709
+ cleaned = cleaned.split(": Error code")[0];
7710
+ cleaned = cleaned.replace(/:\s*\d{3}$/, "");
7711
+ cleaned = cleaned.replace(/See more:.*$/g, "");
7712
+ cleaned = cleaned.trim();
7713
+ return cleaned || "An error occurred.";
7714
+ }
7715
+ function extractUrl(message) {
7716
+ const markdownMatch = /\[([^\]]+)\]\(([^)]+)\)/.exec(message);
7717
+ if (markdownMatch) return {
7718
+ url: markdownMatch[2],
7719
+ text: "See More"
7720
+ };
7721
+ const plainMatch = /(https?:\/\/[^\s)]+)/.exec(message);
7722
+ if (plainMatch) return {
7723
+ url: plainMatch[0].replace(/[.,;:'"]*$/, ""),
7724
+ text: "See More"
7725
+ };
7726
+ return null;
7727
+ }
7728
+ function BannerErrorDisplay({ bannerError, onDismiss }) {
7729
+ const [detailsExpanded, setDetailsExpanded] = (0, react.useState)(false);
7730
+ const colors = getErrorColors(getErrorSeverity(bannerError));
7731
+ const details = bannerError.details;
7732
+ const link = extractUrl(bannerError.message);
7733
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
7734
+ style: {
7735
+ position: "fixed",
7736
+ bottom: "20px",
7737
+ left: "50%",
7738
+ transform: "translateX(-50%)",
7739
+ zIndex: 9999,
7740
+ backgroundColor: colors.background,
7741
+ border: `1px solid ${colors.border}`,
7742
+ borderLeft: `4px solid ${colors.border}`,
7743
+ borderRadius: "8px",
7744
+ padding: "12px 16px",
7745
+ fontSize: "13px",
7746
+ boxShadow: "0 4px 12px rgba(0, 0, 0, 0.15)",
7747
+ backdropFilter: "blur(8px)",
7748
+ maxWidth: "min(90vw, 700px)",
7749
+ width: "100%",
7750
+ boxSizing: "border-box",
7751
+ overflow: "hidden"
7752
+ },
7753
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
7754
+ style: {
7755
+ display: "flex",
7756
+ justifyContent: "space-between",
7757
+ alignItems: "center",
7758
+ gap: "10px"
7759
+ },
7760
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
7761
+ style: {
7762
+ display: "flex",
7763
+ alignItems: "center",
7764
+ gap: "8px",
7765
+ flex: 1,
7766
+ minWidth: 0
7767
+ },
7768
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { style: {
7769
+ width: "12px",
7770
+ height: "12px",
7771
+ borderRadius: "50%",
7772
+ backgroundColor: colors.border,
7773
+ flexShrink: 0
7774
+ } }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
7775
+ style: {
7776
+ display: "flex",
7777
+ alignItems: "center",
7778
+ gap: "10px",
7779
+ flex: 1,
7780
+ minWidth: 0
7781
+ },
7782
+ children: [
7783
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
7784
+ style: {
7785
+ color: colors.text,
7786
+ lineHeight: "1.4",
7787
+ fontWeight: "400",
7788
+ fontSize: "13px",
7789
+ flex: 1,
7790
+ wordBreak: "break-all",
7791
+ overflowWrap: "break-word",
7792
+ maxWidth: "550px",
7793
+ overflow: "hidden",
7794
+ display: "-webkit-box",
7795
+ WebkitLineClamp: 10,
7796
+ WebkitBoxOrient: "vertical"
7797
+ },
7798
+ children: formatBannerMessage(bannerError.message)
7799
+ }),
7800
+ link && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
7801
+ onClick: () => window.open(link.url, "_blank", "noopener,noreferrer"),
7802
+ style: {
7803
+ background: colors.border,
7804
+ color: "white",
7805
+ border: "none",
7806
+ borderRadius: "5px",
7807
+ padding: "4px 10px",
7808
+ fontSize: "11px",
7809
+ fontWeight: "500",
7810
+ cursor: "pointer",
7811
+ transition: "all 0.2s ease",
7812
+ flexShrink: 0
7813
+ },
7814
+ onMouseEnter: (e) => {
7815
+ e.currentTarget.style.opacity = "0.9";
7816
+ e.currentTarget.style.transform = "translateY(-1px)";
7817
+ },
7818
+ onMouseLeave: (e) => {
7819
+ e.currentTarget.style.opacity = "1";
7820
+ e.currentTarget.style.transform = "translateY(0)";
7821
+ },
7822
+ children: link.text
7823
+ }),
7824
+ details && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
7825
+ onClick: () => setDetailsExpanded(!detailsExpanded),
7826
+ style: {
7827
+ background: "transparent",
7828
+ border: `1px solid ${colors.border}`,
7829
+ borderRadius: "5px",
7830
+ padding: "4px 10px",
7831
+ fontSize: "11px",
7832
+ fontWeight: "500",
7833
+ cursor: "pointer",
7834
+ color: colors.text,
7835
+ flexShrink: 0,
7836
+ transition: "all 0.2s ease"
7837
+ },
7838
+ onMouseEnter: (e) => {
7839
+ e.currentTarget.style.background = "rgba(0, 0, 0, 0.05)";
7840
+ },
7841
+ onMouseLeave: (e) => {
7842
+ e.currentTarget.style.background = "transparent";
7843
+ },
7844
+ children: detailsExpanded ? "Hide Details" : "Show Details"
7845
+ })
7846
+ ]
7847
+ })]
7848
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
7849
+ onClick: onDismiss,
7850
+ style: {
7851
+ background: "transparent",
7852
+ border: "none",
7853
+ color: colors.text,
7854
+ cursor: "pointer",
7855
+ padding: "2px",
7856
+ borderRadius: "3px",
7857
+ fontSize: "14px",
7858
+ lineHeight: "1",
7859
+ opacity: .6,
7860
+ transition: "all 0.2s ease",
7861
+ flexShrink: 0
7862
+ },
7863
+ title: "Dismiss",
7864
+ onMouseEnter: (e) => {
7865
+ e.currentTarget.style.opacity = "1";
7866
+ e.currentTarget.style.background = "rgba(0, 0, 0, 0.05)";
7867
+ },
7868
+ onMouseLeave: (e) => {
7869
+ e.currentTarget.style.opacity = "0.6";
7870
+ e.currentTarget.style.background = "transparent";
7871
+ },
7872
+ children: "x"
7873
+ })]
7874
+ }), detailsExpanded && details && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
7875
+ style: {
7876
+ marginTop: "10px",
7877
+ padding: "10px",
7878
+ background: "rgba(0, 0, 0, 0.04)",
7879
+ borderRadius: "6px",
7880
+ fontSize: "11px",
7881
+ fontFamily: "monospace",
7882
+ color: colors.text,
7883
+ lineHeight: "1.5",
7884
+ maxHeight: "200px",
7885
+ overflowY: "auto",
7886
+ whiteSpace: "pre-wrap",
7887
+ wordBreak: "break-all"
7888
+ },
7889
+ children: [
7890
+ details.code && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [
7891
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: "Code:" }),
7892
+ " ",
7893
+ details.code
7894
+ ] }),
7895
+ details.originalMessage && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
7896
+ style: { marginTop: "4px" },
7897
+ children: [
7898
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: "Message:" }),
7899
+ " ",
7900
+ details.originalMessage
7901
+ ]
7902
+ }),
7903
+ details.context && Object.keys(details.context).length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
7904
+ style: { marginTop: "4px" },
7905
+ children: [
7906
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: "Context:" }),
7907
+ " ",
7908
+ JSON.stringify(details.context, null, 2)
7909
+ ]
7910
+ }),
7911
+ details.stack && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
7912
+ style: {
7913
+ marginTop: "4px",
7914
+ opacity: .7
7915
+ },
7916
+ children: [
7917
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: "Stack:" }),
7918
+ "\n",
7919
+ details.stack
7920
+ ]
7921
+ })
7922
+ ]
7923
+ })]
7924
+ });
7925
+ }
5875
7926
  function ToastProvider({ enabled, children }) {
5876
7927
  const [toasts, setToasts] = (0, react.useState)([]);
5877
7928
  const [bannerError, setBannerErrorState] = (0, react.useState)(null);
@@ -5909,156 +7960,10 @@ function ToastProvider({ enabled, children }) {
5909
7960
  };
5910
7961
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(ToastContext.Provider, {
5911
7962
  value,
5912
- children: [bannerError && (() => {
5913
- const colors = getErrorColors(getErrorSeverity(bannerError));
5914
- return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
5915
- style: {
5916
- position: "fixed",
5917
- bottom: "20px",
5918
- left: "50%",
5919
- transform: "translateX(-50%)",
5920
- zIndex: 9999,
5921
- backgroundColor: colors.background,
5922
- border: `1px solid ${colors.border}`,
5923
- borderLeft: `4px solid ${colors.border}`,
5924
- borderRadius: "8px",
5925
- padding: "12px 16px",
5926
- fontSize: "13px",
5927
- boxShadow: "0 4px 12px rgba(0, 0, 0, 0.15)",
5928
- backdropFilter: "blur(8px)",
5929
- maxWidth: "min(90vw, 700px)",
5930
- width: "100%",
5931
- boxSizing: "border-box",
5932
- overflow: "hidden"
5933
- },
5934
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
5935
- style: {
5936
- display: "flex",
5937
- justifyContent: "space-between",
5938
- alignItems: "center",
5939
- gap: "10px"
5940
- },
5941
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
5942
- style: {
5943
- display: "flex",
5944
- alignItems: "center",
5945
- gap: "8px",
5946
- flex: 1,
5947
- minWidth: 0
5948
- },
5949
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { style: {
5950
- width: "12px",
5951
- height: "12px",
5952
- borderRadius: "50%",
5953
- backgroundColor: colors.border,
5954
- flexShrink: 0
5955
- } }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
5956
- style: {
5957
- display: "flex",
5958
- alignItems: "center",
5959
- gap: "10px",
5960
- flex: 1,
5961
- minWidth: 0
5962
- },
5963
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
5964
- style: {
5965
- color: colors.text,
5966
- lineHeight: "1.4",
5967
- fontWeight: "400",
5968
- fontSize: "13px",
5969
- flex: 1,
5970
- wordBreak: "break-all",
5971
- overflowWrap: "break-word",
5972
- maxWidth: "550px",
5973
- overflow: "hidden",
5974
- display: "-webkit-box",
5975
- WebkitLineClamp: 10,
5976
- WebkitBoxOrient: "vertical"
5977
- },
5978
- children: (() => {
5979
- let message = bannerError.message;
5980
- const jsonMatch = message.match(/'message':\s*'([^']+)'/);
5981
- if (jsonMatch) return jsonMatch[1];
5982
- message = message.split(" - ")[0];
5983
- message = message.split(": Error code")[0];
5984
- message = message.replace(/:\s*\d{3}$/, "");
5985
- message = message.replace(/See more:.*$/g, "");
5986
- message = message.trim();
5987
- return message || "Configuration error occurred.";
5988
- })()
5989
- }), (() => {
5990
- const message = bannerError.message;
5991
- const markdownLinkRegex = /\[([^\]]+)\]\(([^)]+)\)/g;
5992
- const plainUrlRegex = /(https?:\/\/[^\s)]+)/g;
5993
- let url = null;
5994
- let buttonText = "See More";
5995
- const markdownMatch = markdownLinkRegex.exec(message);
5996
- if (markdownMatch) {
5997
- url = markdownMatch[2];
5998
- buttonText = "See More";
5999
- } else {
6000
- const urlMatch = plainUrlRegex.exec(message);
6001
- if (urlMatch) {
6002
- url = urlMatch[0].replace(/[.,;:'"]*$/, "");
6003
- buttonText = "See More";
6004
- }
6005
- }
6006
- if (!url) return null;
6007
- return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
6008
- onClick: () => window.open(url, "_blank", "noopener,noreferrer"),
6009
- style: {
6010
- background: colors.border,
6011
- color: "white",
6012
- border: "none",
6013
- borderRadius: "5px",
6014
- padding: "4px 10px",
6015
- fontSize: "11px",
6016
- fontWeight: "500",
6017
- cursor: "pointer",
6018
- transition: "all 0.2s ease",
6019
- flexShrink: 0
6020
- },
6021
- onMouseEnter: (e) => {
6022
- e.currentTarget.style.opacity = "0.9";
6023
- e.currentTarget.style.transform = "translateY(-1px)";
6024
- },
6025
- onMouseLeave: (e) => {
6026
- e.currentTarget.style.opacity = "1";
6027
- e.currentTarget.style.transform = "translateY(0)";
6028
- },
6029
- children: buttonText
6030
- });
6031
- })()]
6032
- })]
6033
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
6034
- onClick: () => setBannerError(null),
6035
- style: {
6036
- background: "transparent",
6037
- border: "none",
6038
- color: colors.text,
6039
- cursor: "pointer",
6040
- padding: "2px",
6041
- borderRadius: "3px",
6042
- fontSize: "14px",
6043
- lineHeight: "1",
6044
- opacity: .6,
6045
- transition: "all 0.2s ease",
6046
- flexShrink: 0
6047
- },
6048
- title: "Dismiss",
6049
- onMouseEnter: (e) => {
6050
- e.currentTarget.style.opacity = "1";
6051
- e.currentTarget.style.background = "rgba(0, 0, 0, 0.05)";
6052
- },
6053
- onMouseLeave: (e) => {
6054
- e.currentTarget.style.opacity = "0.6";
6055
- e.currentTarget.style.background = "transparent";
6056
- },
6057
- children: "×"
6058
- })]
6059
- })
6060
- });
6061
- })(), children]
7963
+ children: [bannerError && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(BannerErrorDisplay, {
7964
+ bannerError,
7965
+ onDismiss: () => setBannerError(null)
7966
+ }), children]
6062
7967
  });
6063
7968
  }
6064
7969
 
@@ -7065,12 +8970,21 @@ function CopilotListeners() {
7065
8970
  const { agent } = useAgent({ agentId: resolvedAgentId });
7066
8971
  usePredictStateSubscription(agent);
7067
8972
  (0, react.useEffect)(() => {
7068
- const subscription = copilotkit.subscribe({ onError: ({ error }) => {
7069
- setBannerError(new _copilotkit_shared.CopilotKitLowLevelError({
8973
+ const subscription = copilotkit.subscribe({ onError: ({ error, code, context }) => {
8974
+ if (error.name === "AbortError" || error.message === "Fetch is aborted" || error.message === "signal is aborted without reason" || error.message === "component unmounted" || !error.message) return;
8975
+ if (process.env.NODE_ENV === "development") console.error("[CopilotKit] Agent error:", error.message, "\n Code:", code, "\n Context:", context, "\n Stack:", error.stack);
8976
+ const ckError = new _copilotkit_shared.CopilotKitLowLevelError({
7070
8977
  error,
7071
8978
  message: error.message,
7072
8979
  url: typeof window !== "undefined" ? window.location.href : ""
7073
- }));
8980
+ });
8981
+ ckError.details = {
8982
+ code,
8983
+ context,
8984
+ stack: error.stack,
8985
+ originalMessage: error.message
8986
+ };
8987
+ setBannerError(ckError);
7074
8988
  } });
7075
8989
  return () => {
7076
8990
  subscription.unsubscribe();
@@ -7544,7 +9458,7 @@ function CopilotKitInternal(cpkProps) {
7544
9458
  children: [
7545
9459
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CopilotListeners, {}),
7546
9460
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CopilotKitErrorBridge, {}),
7547
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(CoAgentStateRendersProvider, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(MessagesTapProvider, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(CopilotMessages, { children: [memoizedChildren, /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RegisteredActionsRenderer, {})] }) }), bannerError && showDevConsole && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(UsageBanner, {
9461
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(CoAgentStateRendersProvider, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(MessagesTapProvider, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(CopilotMessages, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(react.default.Fragment, { children: memoizedChildren }, "children"), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RegisteredActionsRenderer, {}, "actions")] }) }), bannerError && showDevConsole && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(UsageBanner, {
7548
9462
  severity: bannerError.severity,
7549
9463
  message: bannerError.message,
7550
9464
  onClose: () => setBannerError(null),
@@ -7624,6 +9538,18 @@ Object.defineProperty(exports, 'CopilotChatAssistantMessage_default', {
7624
9538
  return CopilotChatAssistantMessage_default;
7625
9539
  }
7626
9540
  });
9541
+ Object.defineProperty(exports, 'CopilotChatAttachmentQueue', {
9542
+ enumerable: true,
9543
+ get: function () {
9544
+ return CopilotChatAttachmentQueue;
9545
+ }
9546
+ });
9547
+ Object.defineProperty(exports, 'CopilotChatAttachmentRenderer', {
9548
+ enumerable: true,
9549
+ get: function () {
9550
+ return CopilotChatAttachmentRenderer;
9551
+ }
9552
+ });
7627
9553
  Object.defineProperty(exports, 'CopilotChatAudioRecorder', {
7628
9554
  enumerable: true,
7629
9555
  get: function () {
@@ -7786,6 +9712,12 @@ Object.defineProperty(exports, 'MCPAppsActivityType', {
7786
9712
  return MCPAppsActivityType;
7787
9713
  }
7788
9714
  });
9715
+ Object.defineProperty(exports, 'SandboxFunctionsContext', {
9716
+ enumerable: true,
9717
+ get: function () {
9718
+ return SandboxFunctionsContext;
9719
+ }
9720
+ });
7789
9721
  Object.defineProperty(exports, 'ThreadsContext', {
7790
9722
  enumerable: true,
7791
9723
  get: function () {
@@ -7858,6 +9790,12 @@ Object.defineProperty(exports, 'useAsyncCallback', {
7858
9790
  return useAsyncCallback;
7859
9791
  }
7860
9792
  });
9793
+ Object.defineProperty(exports, 'useAttachments', {
9794
+ enumerable: true,
9795
+ get: function () {
9796
+ return useAttachments;
9797
+ }
9798
+ });
7861
9799
  Object.defineProperty(exports, 'useCoAgentStateRenders', {
7862
9800
  enumerable: true,
7863
9801
  get: function () {
@@ -7948,6 +9886,12 @@ Object.defineProperty(exports, 'useRenderToolCall', {
7948
9886
  return useRenderToolCall;
7949
9887
  }
7950
9888
  });
9889
+ Object.defineProperty(exports, 'useSandboxFunctions', {
9890
+ enumerable: true,
9891
+ get: function () {
9892
+ return useSandboxFunctions;
9893
+ }
9894
+ });
7951
9895
  Object.defineProperty(exports, 'useSuggestions', {
7952
9896
  enumerable: true,
7953
9897
  get: function () {
@@ -7972,4 +9916,4 @@ Object.defineProperty(exports, 'useToast', {
7972
9916
  return useToast;
7973
9917
  }
7974
9918
  });
7975
- //# sourceMappingURL=copilotkit-B3Mb1yVE.cjs.map
9919
+ //# sourceMappingURL=copilotkit-Bz5-ImDl.cjs.map