@copilotkit/react-core 1.55.0-next.9 → 1.55.1-next.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 (81) hide show
  1. package/CHANGELOG.md +46 -6
  2. package/dist/{copilotkit-DeOzjPsb.mjs → copilotkit-BY5S1-0P.mjs} +2402 -552
  3. package/dist/copilotkit-BY5S1-0P.mjs.map +1 -0
  4. package/dist/{copilotkit-BqcyhQjT.d.mts → copilotkit-BuhSUZHb.d.mts} +228 -17
  5. package/dist/copilotkit-BuhSUZHb.d.mts.map +1 -0
  6. package/dist/{copilotkit-BDNjFNmk.cjs → copilotkit-Bz5-ImDl.cjs} +2421 -541
  7. package/dist/copilotkit-Bz5-ImDl.cjs.map +1 -0
  8. package/dist/{copilotkit-l-IBF4Xp.d.cts → copilotkit-dwDWYpya.d.cts} +228 -17
  9. package/dist/copilotkit-dwDWYpya.d.cts.map +1 -0
  10. package/dist/index.cjs +1 -1
  11. package/dist/index.d.cts +1 -1
  12. package/dist/index.d.mts +1 -1
  13. package/dist/index.mjs +1 -1
  14. package/dist/index.umd.js +1400 -238
  15. package/dist/index.umd.js.map +1 -1
  16. package/dist/v2/index.cjs +13 -1
  17. package/dist/v2/index.css +1 -1
  18. package/dist/v2/index.d.cts +3 -3
  19. package/dist/v2/index.d.mts +3 -3
  20. package/dist/v2/index.mjs +3 -2
  21. package/dist/v2/index.umd.js +2442 -552
  22. package/dist/v2/index.umd.js.map +1 -1
  23. package/package.json +62 -54
  24. package/scripts/scope-preflight.mjs +1 -2
  25. package/src/components/CopilotListeners.tsx +41 -8
  26. package/src/components/copilot-provider/copilotkit-props.tsx +4 -2
  27. package/src/components/toast/toast-provider.tsx +269 -194
  28. package/src/v2/__tests__/A2UIMessageRenderer.test.tsx +86 -22
  29. package/src/v2/__tests__/utils/test-helpers.tsx +67 -0
  30. package/src/v2/a2ui/A2UICatalogContext.tsx +79 -0
  31. package/src/v2/a2ui/A2UIMessageRenderer.tsx +125 -37
  32. package/src/v2/a2ui/A2UIToolCallRenderer.tsx +290 -0
  33. package/src/v2/components/CopilotKitInspector.tsx +2 -0
  34. package/src/v2/components/OpenGenerativeUIRenderer.tsx +598 -0
  35. package/src/v2/components/__tests__/OpenGenerativeUIRenderer.test.tsx +665 -0
  36. package/src/v2/components/chat/CopilotChat.tsx +193 -50
  37. package/src/v2/components/chat/CopilotChatAssistantMessage.tsx +17 -2
  38. package/src/v2/components/chat/CopilotChatAttachmentQueue.tsx +481 -0
  39. package/src/v2/components/chat/CopilotChatAttachmentRenderer.tsx +139 -0
  40. package/src/v2/components/chat/CopilotChatInput.tsx +146 -77
  41. package/src/v2/components/chat/CopilotChatMessageView.tsx +253 -149
  42. package/src/v2/components/chat/CopilotChatSuggestionView.tsx +1 -0
  43. package/src/v2/components/chat/CopilotChatUserMessage.tsx +54 -0
  44. package/src/v2/components/chat/CopilotChatView.tsx +179 -66
  45. package/src/v2/components/chat/__tests__/CopilotChat.attachments.test.tsx +168 -0
  46. package/src/v2/components/chat/__tests__/CopilotChatActivityRendering.e2e.test.tsx +63 -2
  47. package/src/v2/components/chat/__tests__/CopilotChatInput.test.tsx +544 -1
  48. package/src/v2/components/chat/__tests__/CopilotChatPerf.e2e.test.tsx +268 -0
  49. package/src/v2/components/chat/__tests__/CopilotChatPropsRerender.e2e.test.tsx +249 -0
  50. package/src/v2/components/chat/__tests__/MCPAppsActivityRenderer.e2e.test.tsx +43 -2
  51. package/src/v2/components/chat/__tests__/copilot-chat-throttle.test.tsx +138 -0
  52. package/src/v2/components/chat/index.ts +9 -0
  53. package/src/v2/components/chat/scroll-element-context.ts +13 -0
  54. package/src/v2/hooks/__tests__/use-agent-throttle.test.tsx +1003 -0
  55. package/src/v2/hooks/__tests__/use-attachments.test.tsx +169 -0
  56. package/src/v2/hooks/__tests__/use-threads.test.tsx +54 -0
  57. package/src/v2/hooks/index.ts +5 -0
  58. package/src/v2/hooks/use-agent.tsx +95 -10
  59. package/src/v2/hooks/use-attachments.tsx +269 -0
  60. package/src/v2/hooks/use-frontend-tool.tsx +5 -2
  61. package/src/v2/hooks/use-render-activity-message.tsx +9 -2
  62. package/src/v2/hooks/use-threads.tsx +35 -15
  63. package/src/v2/index.ts +5 -1
  64. package/src/v2/lib/__tests__/processPartialHtml.test.ts +112 -0
  65. package/src/v2/lib/__tests__/slots.test.ts +56 -0
  66. package/src/v2/lib/processPartialHtml.ts +45 -0
  67. package/src/v2/lib/slots.tsx +42 -1
  68. package/src/v2/providers/CopilotChatConfigurationProvider.tsx +9 -3
  69. package/src/v2/providers/CopilotKitProvider.tsx +268 -32
  70. package/src/v2/providers/SandboxFunctionsContext.ts +10 -0
  71. package/src/v2/providers/__tests__/CopilotKitProvider.sandboxFunctions.test.tsx +198 -0
  72. package/src/v2/providers/__tests__/CopilotKitProvider.test.tsx +71 -0
  73. package/src/v2/providers/index.ts +7 -0
  74. package/src/v2/styles/globals.css +2 -1
  75. package/src/v2/types/index.ts +1 -0
  76. package/src/v2/types/sandbox-function.ts +11 -0
  77. package/dist/copilotkit-BDNjFNmk.cjs.map +0 -1
  78. package/dist/copilotkit-BqcyhQjT.d.mts.map +0 -1
  79. package/dist/copilotkit-DeOzjPsb.mjs.map +0 -1
  80. package/dist/copilotkit-l-IBF4Xp.d.cts.map +0 -1
  81. package/src/v2/components/__tests__/license-warning-banner.test.tsx +0 -46
@@ -1,10 +1,10 @@
1
1
  import * as React$1 from "react";
2
- import React, { createContext, forwardRef, useCallback, useContext, useEffect, useImperativeHandle, useLayoutEffect, useMemo, useReducer, useRef, useState, useSyncExternalStore } from "react";
2
+ import React, { createContext, forwardRef, memo, useCallback, useContext, useEffect, useId, useImperativeHandle, useLayoutEffect, useMemo, useReducer, useRef, useState, useSyncExternalStore } from "react";
3
3
  import { CopilotKitCore, CopilotKitCoreRuntimeConnectionStatus, ProxiedCopilotRuntimeAgent, ToolCallStatus, ɵcreateThreadStore, ɵselectHasNextPage, ɵselectIsFetchingNextPage, ɵselectThreads, ɵselectThreadsError, ɵselectThreadsIsLoading } from "@copilotkit/core";
4
4
  import { HttpAgent } from "@ag-ui/client";
5
5
  import { extendTailwindMerge, twMerge } from "tailwind-merge";
6
- import { ArrowUp, Check, ChevronDown, ChevronLeft, ChevronRight, ChevronRightIcon, Copy, Edit, Loader2, MessageCircle, Mic, Plus, RefreshCw, Square, ThumbsDown, ThumbsUp, Volume2, X } from "lucide-react";
7
- import { COPILOT_CLOUD_API_URL, COPILOT_CLOUD_CHAT_URL, COPILOT_CLOUD_PUBLIC_API_KEY_HEADER, ConfigurationError, CopilotKitAgentDiscoveryError, CopilotKitApiDiscoveryError, CopilotKitError, CopilotKitErrorCode, CopilotKitLowLevelError, CopilotKitRemoteEndpointDiscoveryError, DEFAULT_AGENT_ID, ErrorVisibility, MissingPublicApiKeyError, Severity, TranscriptionErrorCode, TranscriptionErrorCode as TranscriptionErrorCode$1, createLicenseContextValue, dataToUUID, parseJson, partialJSONParse, randomId, randomUUID } from "@copilotkit/shared";
6
+ import { ArrowUp, Check, ChevronDown, ChevronLeft, ChevronRight, ChevronRightIcon, Copy, Edit, Loader2, MessageCircle, Mic, Play, Plus, RefreshCw, Square, ThumbsDown, ThumbsUp, Upload, Volume2, X } from "lucide-react";
7
+ import { A2UI_DEFAULT_DESIGN_GUIDELINES, A2UI_DEFAULT_GENERATION_GUIDELINES, COPILOT_CLOUD_API_URL, COPILOT_CLOUD_CHAT_URL, COPILOT_CLOUD_PUBLIC_API_KEY_HEADER, ConfigurationError, CopilotKitAgentDiscoveryError, CopilotKitApiDiscoveryError, CopilotKitError, CopilotKitErrorCode, CopilotKitLowLevelError, CopilotKitRemoteEndpointDiscoveryError, DEFAULT_AGENT_ID, ErrorVisibility, MissingPublicApiKeyError, Severity, TranscriptionErrorCode, TranscriptionErrorCode as TranscriptionErrorCode$1, createLicenseContextValue, dataToUUID, exceedsMaxSize, formatFileSize, generateVideoThumbnail, getDocumentIcon, getModalityFromMimeType, getSourceUrl, matchesAcceptFilter, parseJson, partialJSONParse, randomId, randomUUID, readFileAsBase64, schemaToJsonSchema } from "@copilotkit/shared";
8
8
  import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
9
9
  import { Slot } from "@radix-ui/react-slot";
10
10
  import { cva } from "class-variance-authority";
@@ -14,19 +14,120 @@ import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu";
14
14
  import { Streamdown } from "streamdown";
15
15
  import { z } from "zod";
16
16
  import { createComponent } from "@lit-labs/react";
17
- import { A2UIProvider, A2UIRenderer, DEFAULT_SURFACE_ID, initializeDefaultCatalog, injectStyles, useA2UIActions, viewerTheme } from "@copilotkit/a2ui-renderer";
17
+ import { A2UIProvider, A2UIRenderer, A2UI_SCHEMA_CONTEXT_DESCRIPTION, DEFAULT_SURFACE_ID, buildCatalogContextValue, extractCatalogComponentSchemas, initializeDefaultCatalog, injectStyles, useA2UIActions, useA2UIError, viewerTheme } from "@copilotkit/a2ui-renderer";
18
+ import { zodToJsonSchema } from "zod-to-json-schema";
19
+ import { useVirtualizer } from "@tanstack/react-virtual";
20
+ import { createPortal, flushSync } from "react-dom";
18
21
  import { StickToBottom, useStickToBottom, useStickToBottomContext } from "use-stick-to-bottom";
19
- import { merge } from "ts-deepmerge";
20
- import { flushSync } from "react-dom";
21
22
  import ReactMarkdown from "react-markdown";
22
23
 
24
+ //#region src/v2/lib/slots.tsx
25
+ /**
26
+ * Shallow equality comparison for objects.
27
+ */
28
+ function shallowEqual(obj1, obj2) {
29
+ const keys1 = Object.keys(obj1);
30
+ const keys2 = Object.keys(obj2);
31
+ if (keys1.length !== keys2.length) return false;
32
+ for (const key of keys1) if (obj1[key] !== obj2[key]) return false;
33
+ return true;
34
+ }
35
+ /**
36
+ * Returns true only for plain JS objects (`{}`), excluding arrays, Dates,
37
+ * class instances, and other exotic objects that happen to have typeof "object".
38
+ */
39
+ function isPlainObject(obj) {
40
+ return obj !== null && typeof obj === "object" && Object.prototype.toString.call(obj) === "[object Object]";
41
+ }
42
+ /**
43
+ * Returns the same reference as long as the value is shallowly equal to the
44
+ * previous render's value.
45
+ *
46
+ * - Identical references bail out immediately (O(1)).
47
+ * - Plain objects ({}) are shallow-compared key-by-key.
48
+ * - Arrays, Dates, class instances, functions, and primitives are compared by
49
+ * reference only — shallowEqual is never called on non-plain objects, which
50
+ * avoids incorrect equality for e.g. [1,2] vs [1,2] (different arrays).
51
+ *
52
+ * Typical use: stabilize inline slot props so MemoizedSlotWrapper's shallow
53
+ * equality check isn't defeated by a new object reference on every render.
54
+ */
55
+ function useShallowStableRef(value) {
56
+ const ref = useRef(value);
57
+ if (ref.current === value) return ref.current;
58
+ if (isPlainObject(ref.current) && isPlainObject(value)) {
59
+ if (shallowEqual(ref.current, value)) return ref.current;
60
+ }
61
+ ref.current = value;
62
+ return ref.current;
63
+ }
64
+ /**
65
+ * Check if a value is a React component type (function, class, forwardRef, memo, etc.)
66
+ */
67
+ function isReactComponentType(value) {
68
+ if (typeof value === "function") return true;
69
+ if (value && typeof value === "object" && "$$typeof" in value && !React.isValidElement(value)) return true;
70
+ return false;
71
+ }
72
+ /**
73
+ * Internal function to render a slot value as a React element (non-memoized).
74
+ */
75
+ function renderSlotElement(slot, DefaultComponent, props) {
76
+ if (typeof slot === "string") {
77
+ const existingClassName = props.className;
78
+ return React.createElement(DefaultComponent, {
79
+ ...props,
80
+ className: twMerge(existingClassName, slot)
81
+ });
82
+ }
83
+ if (isReactComponentType(slot)) return React.createElement(slot, props);
84
+ if (slot && typeof slot === "object" && !React.isValidElement(slot)) return React.createElement(DefaultComponent, {
85
+ ...props,
86
+ ...slot
87
+ });
88
+ return React.createElement(DefaultComponent, props);
89
+ }
90
+ /**
91
+ * Internal memoized wrapper component for renderSlot.
92
+ * Uses forwardRef to support ref forwarding.
93
+ */
94
+ const MemoizedSlotWrapper = React.memo(React.forwardRef(function MemoizedSlotWrapper(props, ref) {
95
+ const { $slot, $component, ...rest } = props;
96
+ return renderSlotElement($slot, $component, ref !== null ? {
97
+ ...rest,
98
+ ref
99
+ } : rest);
100
+ }), (prev, next) => {
101
+ if (prev.$slot !== next.$slot) return false;
102
+ if (prev.$component !== next.$component) return false;
103
+ const { $slot: _ps, $component: _pc, ...prevRest } = prev;
104
+ const { $slot: _ns, $component: _nc, ...nextRest } = next;
105
+ return shallowEqual(prevRest, nextRest);
106
+ });
107
+ /**
108
+ * Renders a slot value as a memoized React element.
109
+ * Automatically prevents unnecessary re-renders using shallow prop comparison.
110
+ * Supports ref forwarding.
111
+ *
112
+ * @example
113
+ * renderSlot(customInput, CopilotChatInput, { onSubmit: handleSubmit })
114
+ */
115
+ function renderSlot(slot, DefaultComponent, props) {
116
+ return React.createElement(MemoizedSlotWrapper, {
117
+ ...props,
118
+ $slot: slot,
119
+ $component: DefaultComponent
120
+ });
121
+ }
122
+
123
+ //#endregion
23
124
  //#region src/v2/providers/CopilotChatConfigurationProvider.tsx
24
125
  const CopilotChatDefaultLabels = {
25
126
  chatInputPlaceholder: "Type a message...",
26
127
  chatInputToolbarStartTranscribeButtonLabel: "Transcribe",
27
128
  chatInputToolbarCancelTranscribeButtonLabel: "Cancel",
28
129
  chatInputToolbarFinishTranscribeButtonLabel: "Finish",
29
- chatInputToolbarAddButtonLabel: "Add photos or files",
130
+ chatInputToolbarAddButtonLabel: "Add attachments",
30
131
  chatInputToolbarToolsButtonLabel: "Tools",
31
132
  assistantMessageToolbarCopyCodeLabel: "Copy",
32
133
  assistantMessageToolbarCopyCodeCopiedLabel: "Copied",
@@ -46,11 +147,12 @@ const CopilotChatDefaultLabels = {
46
147
  const CopilotChatConfiguration = createContext(null);
47
148
  const CopilotChatConfigurationProvider = ({ children, labels, agentId, threadId, isModalDefaultOpen }) => {
48
149
  const parentConfig = useContext(CopilotChatConfiguration);
150
+ const stableLabels = useShallowStableRef(labels);
49
151
  const mergedLabels = useMemo(() => ({
50
152
  ...CopilotChatDefaultLabels,
51
153
  ...parentConfig?.labels ?? {},
52
- ...labels ?? {}
53
- }), [labels, parentConfig?.labels]);
154
+ ...stableLabels ?? {}
155
+ }), [stableLabels, parentConfig?.labels]);
54
156
  const resolvedAgentId = agentId ?? parentConfig?.agentId ?? DEFAULT_AGENT_ID;
55
157
  const resolvedThreadId = useMemo(() => {
56
158
  if (threadId) return threadId;
@@ -473,82 +575,11 @@ const CopilotChatAudioRecorder = forwardRef((props, ref) => {
473
575
  });
474
576
  CopilotChatAudioRecorder.displayName = "CopilotChatAudioRecorder";
475
577
 
476
- //#endregion
477
- //#region src/v2/lib/slots.tsx
478
- /**
479
- * Shallow equality comparison for objects.
480
- */
481
- function shallowEqual(obj1, obj2) {
482
- const keys1 = Object.keys(obj1);
483
- const keys2 = Object.keys(obj2);
484
- if (keys1.length !== keys2.length) return false;
485
- for (const key of keys1) if (obj1[key] !== obj2[key]) return false;
486
- return true;
487
- }
488
- /**
489
- * Check if a value is a React component type (function, class, forwardRef, memo, etc.)
490
- */
491
- function isReactComponentType(value) {
492
- if (typeof value === "function") return true;
493
- if (value && typeof value === "object" && "$$typeof" in value && !React.isValidElement(value)) return true;
494
- return false;
495
- }
496
- /**
497
- * Internal function to render a slot value as a React element (non-memoized).
498
- */
499
- function renderSlotElement(slot, DefaultComponent, props) {
500
- if (typeof slot === "string") {
501
- const existingClassName = props.className;
502
- return React.createElement(DefaultComponent, {
503
- ...props,
504
- className: twMerge(existingClassName, slot)
505
- });
506
- }
507
- if (isReactComponentType(slot)) return React.createElement(slot, props);
508
- if (slot && typeof slot === "object" && !React.isValidElement(slot)) return React.createElement(DefaultComponent, {
509
- ...props,
510
- ...slot
511
- });
512
- return React.createElement(DefaultComponent, props);
513
- }
514
- /**
515
- * Internal memoized wrapper component for renderSlot.
516
- * Uses forwardRef to support ref forwarding.
517
- */
518
- const MemoizedSlotWrapper = React.memo(React.forwardRef(function MemoizedSlotWrapper(props, ref) {
519
- const { $slot, $component, ...rest } = props;
520
- return renderSlotElement($slot, $component, ref !== null ? {
521
- ...rest,
522
- ref
523
- } : rest);
524
- }), (prev, next) => {
525
- if (prev.$slot !== next.$slot) return false;
526
- if (prev.$component !== next.$component) return false;
527
- const { $slot: _ps, $component: _pc, ...prevRest } = prev;
528
- const { $slot: _ns, $component: _nc, ...nextRest } = next;
529
- return shallowEqual(prevRest, nextRest);
530
- });
531
- /**
532
- * Renders a slot value as a memoized React element.
533
- * Automatically prevents unnecessary re-renders using shallow prop comparison.
534
- * Supports ref forwarding.
535
- *
536
- * @example
537
- * renderSlot(customInput, CopilotChatInput, { onSubmit: handleSubmit })
538
- */
539
- function renderSlot(slot, DefaultComponent, props) {
540
- return React.createElement(MemoizedSlotWrapper, {
541
- ...props,
542
- $slot: slot,
543
- $component: DefaultComponent
544
- });
545
- }
546
-
547
578
  //#endregion
548
579
  //#region src/v2/components/chat/CopilotChatInput.tsx
549
580
  const SLASH_MENU_MAX_VISIBLE_ITEMS = 5;
550
581
  const SLASH_MENU_ITEM_HEIGHT_PX = 40;
551
- 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 }) {
582
+ 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 }) {
552
583
  const isControlled = value !== void 0;
553
584
  const [internalValue, setInternalValue] = useState(() => value ?? "");
554
585
  useEffect(() => {
@@ -577,6 +608,7 @@ function CopilotChatInput({ mode = "input", onSubmitMessage, onStop, isRunning =
577
608
  paddingLeft: 0,
578
609
  paddingRight: 0
579
610
  });
611
+ const containerCacheRef = useRef(null);
580
612
  const commandItems = useMemo(() => {
581
613
  const entries = [];
582
614
  const seen = /* @__PURE__ */ new Set();
@@ -621,7 +653,7 @@ function CopilotChatInput({ mode = "input", onSubmitMessage, onStop, isRunning =
621
653
  previousModalStateRef.current = config?.isModalOpen;
622
654
  return;
623
655
  }
624
- if (config?.isModalOpen && !previousModalStateRef.current) inputRef.current?.focus();
656
+ if (config?.isModalOpen && !previousModalStateRef.current) inputRef.current?.focus({ preventScroll: true });
625
657
  previousModalStateRef.current = config?.isModalOpen;
626
658
  }, [config?.isModalOpen, autoFocus]);
627
659
  useEffect(() => {
@@ -864,6 +896,25 @@ function CopilotChatInput({ mode = "input", onSubmitMessage, onStop, isRunning =
864
896
  return nextLayout;
865
897
  });
866
898
  }, []);
899
+ const updateContainerCache = useCallback(() => {
900
+ const grid = gridRef.current;
901
+ const addContainer = addButtonContainerRef.current;
902
+ const actionsContainer = actionsContainerRef.current;
903
+ if (!grid || !addContainer || !actionsContainer) return null;
904
+ const gridStyles = window.getComputedStyle(grid);
905
+ const paddingLeft = parseFloat(gridStyles.paddingLeft) || 0;
906
+ const paddingRight = parseFloat(gridStyles.paddingRight) || 0;
907
+ const columnGap = parseFloat(gridStyles.columnGap) || 0;
908
+ const gridAvailableWidth = grid.clientWidth - paddingLeft - paddingRight;
909
+ if (gridAvailableWidth <= 0) return null;
910
+ const addWidth = addContainer.getBoundingClientRect().width;
911
+ const actionsWidth = actionsContainer.getBoundingClientRect().width;
912
+ const compactWidth = Math.max(gridAvailableWidth - addWidth - actionsWidth - columnGap * 2, 0);
913
+ if (compactWidth <= 0) return null;
914
+ const result = { compactWidth };
915
+ containerCacheRef.current = result;
916
+ return result;
917
+ }, []);
867
918
  const evaluateLayout = useCallback(() => {
868
919
  if (mode !== "input") {
869
920
  updateLayout("compact");
@@ -889,31 +940,31 @@ function CopilotChatInput({ mode = "input", onSubmitMessage, onStop, isRunning =
889
940
  const renderedMultiline = baseline > 0 ? scrollHeight > baseline + 1 : false;
890
941
  let shouldExpand = hasExplicitBreak || renderedMultiline;
891
942
  if (!shouldExpand) {
892
- const gridStyles = window.getComputedStyle(grid);
893
- const paddingLeft = parseFloat(gridStyles.paddingLeft) || 0;
894
- const paddingRight = parseFloat(gridStyles.paddingRight) || 0;
895
- const columnGap = parseFloat(gridStyles.columnGap) || 0;
896
- const gridAvailableWidth = grid.clientWidth - paddingLeft - paddingRight;
897
- if (gridAvailableWidth > 0) {
898
- const addWidth = addContainer.getBoundingClientRect().width;
899
- const actionsWidth = actionsContainer.getBoundingClientRect().width;
900
- const compactWidth = Math.max(gridAvailableWidth - addWidth - actionsWidth - columnGap * 2, 0);
901
- const canvas = measurementCanvasRef.current ?? document.createElement("canvas");
902
- if (!measurementCanvasRef.current) measurementCanvasRef.current = canvas;
903
- const context = canvas.getContext("2d");
904
- if (context) {
943
+ const cache = containerCacheRef.current ?? updateContainerCache();
944
+ if (cache && cache.compactWidth > 0) {
945
+ const compactInnerWidth = Math.max(cache.compactWidth - (measurementsRef.current.paddingLeft || 0) - (measurementsRef.current.paddingRight || 0), 0);
946
+ if (compactInnerWidth > 0) {
905
947
  const textareaStyles = window.getComputedStyle(textarea);
906
- context.font = textareaStyles.font || `${textareaStyles.fontStyle} ${textareaStyles.fontVariant} ${textareaStyles.fontWeight} ${textareaStyles.fontSize}/${textareaStyles.lineHeight} ${textareaStyles.fontFamily}`;
907
- const compactInnerWidth = Math.max(compactWidth - (measurementsRef.current.paddingLeft || 0) - (measurementsRef.current.paddingRight || 0), 0);
908
- if (compactInnerWidth > 0) {
909
- const lines = resolvedValue.length > 0 ? resolvedValue.split("\n") : [""];
910
- let longestWidth = 0;
911
- for (const line of lines) {
912
- const metrics = context.measureText(line || " ");
913
- if (metrics.width > longestWidth) longestWidth = metrics.width;
914
- }
915
- if (longestWidth > compactInnerWidth) shouldExpand = true;
948
+ let font = textareaStyles.font;
949
+ if (!font) {
950
+ const { fontStyle, fontVariant, fontWeight, fontSize, lineHeight, fontFamily } = textareaStyles;
951
+ if (fontSize && fontFamily) font = `${fontStyle} ${fontVariant} ${fontWeight} ${fontSize}/${lineHeight} ${fontFamily}`;
916
952
  }
953
+ if (font?.trim()) {
954
+ const canvas = measurementCanvasRef.current ?? document.createElement("canvas");
955
+ if (!measurementCanvasRef.current) measurementCanvasRef.current = canvas;
956
+ const context = canvas.getContext("2d");
957
+ if (context) {
958
+ context.font = font;
959
+ const lines = resolvedValue.length > 0 ? resolvedValue.split("\n") : [""];
960
+ let longestWidth = 0;
961
+ for (const line of lines) {
962
+ const metrics = context.measureText(line || " ");
963
+ if (metrics.width > longestWidth) longestWidth = metrics.width;
964
+ }
965
+ if (longestWidth > compactInnerWidth) shouldExpand = true;
966
+ } else if (process.env.NODE_ENV !== "production") console.warn("[CopilotChatInput] canvas.getContext('2d') returned null. Text-width-based expansion will be unavailable.");
967
+ } 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.");
917
968
  }
918
969
  }
919
970
  }
@@ -923,6 +974,7 @@ function CopilotChatInput({ mode = "input", onSubmitMessage, onStop, isRunning =
923
974
  ensureMeasurements,
924
975
  mode,
925
976
  resolvedValue,
977
+ updateContainerCache,
926
978
  updateLayout
927
979
  ]);
928
980
  useLayoutEffect(() => {
@@ -935,11 +987,17 @@ function CopilotChatInput({ mode = "input", onSubmitMessage, onStop, isRunning =
935
987
  const addContainer = addButtonContainerRef.current;
936
988
  const actionsContainer = actionsContainerRef.current;
937
989
  if (!textarea || !grid || !addContainer || !actionsContainer) return;
938
- const scheduleEvaluation = () => {
990
+ const containerTargets = new Set([
991
+ grid,
992
+ addContainer,
993
+ actionsContainer
994
+ ]);
995
+ const scheduleEvaluation = (invalidateCache) => {
939
996
  if (ignoreResizeRef.current) {
940
997
  ignoreResizeRef.current = false;
941
998
  return;
942
999
  }
1000
+ if (invalidateCache) containerCacheRef.current = null;
943
1001
  if (typeof window === "undefined") {
944
1002
  evaluateLayout();
945
1003
  return;
@@ -950,8 +1008,13 @@ function CopilotChatInput({ mode = "input", onSubmitMessage, onStop, isRunning =
950
1008
  evaluateLayout();
951
1009
  });
952
1010
  };
953
- const observer = new ResizeObserver(() => {
954
- scheduleEvaluation();
1011
+ const observer = new ResizeObserver((entries) => {
1012
+ let shouldInvalidate = false;
1013
+ for (const entry of entries) if (containerTargets.has(entry.target)) {
1014
+ shouldInvalidate = true;
1015
+ break;
1016
+ }
1017
+ scheduleEvaluation(shouldInvalidate);
955
1018
  });
956
1019
  observer.observe(grid);
957
1020
  observer.observe(addContainer);
@@ -1096,6 +1159,8 @@ function CopilotChatInput({ mode = "input", onSubmitMessage, onStop, isRunning =
1096
1159
  });
1097
1160
  _CopilotChatInput.AddMenuButton = ({ className, toolsMenu, onAddFile, disabled, ...props }) => {
1098
1161
  const labels = useCopilotChatConfiguration()?.labels ?? CopilotChatDefaultLabels;
1162
+ const [mounted, setMounted] = useState(false);
1163
+ useEffect(() => setMounted(true), []);
1099
1164
  const menuItems = useMemo(() => {
1100
1165
  const items = [];
1101
1166
  if (onAddFile) items.push({
@@ -1126,26 +1191,28 @@ function CopilotChatInput({ mode = "input", onSubmitMessage, onStop, isRunning =
1126
1191
  }), []);
1127
1192
  const hasMenuItems = menuItems.length > 0;
1128
1193
  const isDisabled = disabled || !hasMenuItems;
1194
+ const button = /* @__PURE__ */ jsx(Button, {
1195
+ type: "button",
1196
+ "data-testid": "copilot-add-menu-button",
1197
+ variant: "chatInputToolbarSecondary",
1198
+ size: "chatInputToolbarIcon",
1199
+ className: twMerge("cpk:ml-1", className),
1200
+ disabled: isDisabled,
1201
+ ...props,
1202
+ children: /* @__PURE__ */ jsx(Plus, { className: "cpk:size-[20px]" })
1203
+ });
1204
+ if (!mounted) return button;
1129
1205
  return /* @__PURE__ */ jsxs(DropdownMenu, { children: [/* @__PURE__ */ jsxs(Tooltip, { children: [/* @__PURE__ */ jsx(TooltipTrigger, {
1130
1206
  asChild: true,
1131
1207
  children: /* @__PURE__ */ jsx(DropdownMenuTrigger, {
1132
1208
  asChild: true,
1133
- children: /* @__PURE__ */ jsx(Button, {
1134
- type: "button",
1135
- "data-testid": "copilot-add-menu-button",
1136
- variant: "chatInputToolbarSecondary",
1137
- size: "chatInputToolbarIcon",
1138
- className: twMerge("cpk:ml-1", className),
1139
- disabled: isDisabled,
1140
- ...props,
1141
- children: /* @__PURE__ */ jsx(Plus, { className: "cpk:size-[20px]" })
1142
- })
1209
+ children: button
1143
1210
  })
1144
1211
  }), /* @__PURE__ */ jsx(TooltipContent, {
1145
1212
  side: "bottom",
1146
1213
  children: /* @__PURE__ */ jsxs("p", {
1147
1214
  className: "cpk:flex cpk:items-center cpk:gap-1 cpk:text-xs cpk:font-medium",
1148
- children: [/* @__PURE__ */ jsx("span", { children: "Add files and more" }), /* @__PURE__ */ jsx("code", {
1215
+ children: [/* @__PURE__ */ jsx("span", { children: "Add attachments" }), /* @__PURE__ */ jsx("code", {
1149
1216
  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",
1150
1217
  children: "/"
1151
1218
  })]
@@ -1161,21 +1228,7 @@ function CopilotChatInput({ mode = "input", onSubmitMessage, onStop, isRunning =
1161
1228
  const labels = useCopilotChatConfiguration()?.labels ?? CopilotChatDefaultLabels;
1162
1229
  useImperativeHandle(ref, () => internalTextareaRef.current);
1163
1230
  useEffect(() => {
1164
- const textarea = internalTextareaRef.current;
1165
- if (!textarea) return;
1166
- const handleFocus = () => {
1167
- setTimeout(() => {
1168
- textarea.scrollIntoView({
1169
- behavior: "smooth",
1170
- block: "nearest"
1171
- });
1172
- }, 300);
1173
- };
1174
- textarea.addEventListener("focus", handleFocus);
1175
- return () => textarea.removeEventListener("focus", handleFocus);
1176
- }, []);
1177
- useEffect(() => {
1178
- if (autoFocus) internalTextareaRef.current?.focus();
1231
+ if (autoFocus) internalTextareaRef.current?.focus({ preventScroll: true });
1179
1232
  }, [autoFocus]);
1180
1233
  return /* @__PURE__ */ jsx("textarea", {
1181
1234
  ref: internalTextareaRef,
@@ -1882,8 +1935,411 @@ const MCPAppsActivityRenderer = function MCPAppsActivityRenderer({ content, agen
1882
1935
  });
1883
1936
  };
1884
1937
 
1938
+ //#endregion
1939
+ //#region src/v2/providers/SandboxFunctionsContext.ts
1940
+ const SandboxFunctionsContext = createContext([]);
1941
+ function useSandboxFunctions() {
1942
+ return useContext(SandboxFunctionsContext);
1943
+ }
1944
+
1945
+ //#endregion
1946
+ //#region src/v2/lib/processPartialHtml.ts
1947
+ /**
1948
+ * Extracts all complete `<style>` blocks from the raw HTML.
1949
+ * Returns the concatenated style tags, suitable for injection into `<head>`.
1950
+ */
1951
+ function extractCompleteStyles(html) {
1952
+ const matches = html.match(/<style\b[^>]*>[\s\S]*?<\/style>/gi);
1953
+ return matches ? matches.join("") : "";
1954
+ }
1955
+ /**
1956
+ * Processes raw accumulated HTML for safe preview via innerHTML injection.
1957
+ * Pure function, no DOM dependencies.
1958
+ *
1959
+ * Pipeline (order matters):
1960
+ * 1. Strip incomplete tag at end
1961
+ * 2. Strip complete <style>, <script>, and <head> blocks
1962
+ * 3. Strip incomplete <style>/<script>/<head> blocks
1963
+ * 4. Strip incomplete HTML entities
1964
+ * 5. Extract body content (or use full string if no <body>)
1965
+ */
1966
+ function processPartialHtml(html) {
1967
+ let result = html;
1968
+ result = result.replace(/<[^>]*$/, "");
1969
+ result = result.replace(/<(style|script|head)\b[^>]*>[\s\S]*?<\/\1>/gi, "");
1970
+ result = result.replace(/<(style|script|head)\b[^>]*>[\s\S]*$/gi, "");
1971
+ result = result.replace(/&[a-zA-Z0-9#]*$/, "");
1972
+ const bodyMatch = result.match(/<body[^>]*>([\s\S]*)/i);
1973
+ if (bodyMatch) {
1974
+ result = bodyMatch[1];
1975
+ result = result.replace(/<\/body>[\s\S]*/i, "");
1976
+ }
1977
+ return result;
1978
+ }
1979
+
1980
+ //#endregion
1981
+ //#region src/v2/components/OpenGenerativeUIRenderer.tsx
1982
+ const OpenGenerativeUIActivityType = "open-generative-ui";
1983
+ const OpenGenerativeUIContentSchema = z.object({
1984
+ initialHeight: z.number().optional(),
1985
+ generating: z.boolean().optional(),
1986
+ css: z.string().optional(),
1987
+ cssComplete: z.boolean().optional(),
1988
+ html: z.array(z.string()).optional(),
1989
+ htmlComplete: z.boolean().optional(),
1990
+ jsFunctions: z.string().optional(),
1991
+ jsFunctionsComplete: z.boolean().optional(),
1992
+ jsExpressions: z.array(z.string()).optional(),
1993
+ jsExpressionsComplete: z.boolean().optional()
1994
+ });
1995
+ /**
1996
+ * Schema for the generateSandboxedUi tool call arguments.
1997
+ * Used by the frontend tool renderer to display placeholder messages.
1998
+ */
1999
+ const GenerateSandboxedUiArgsSchema = z.object({
2000
+ initialHeight: z.number().optional(),
2001
+ placeholderMessages: z.array(z.string()).optional(),
2002
+ css: z.string().optional(),
2003
+ html: z.string().optional(),
2004
+ jsFunctions: z.string().optional(),
2005
+ jsExpressions: z.array(z.string()).optional()
2006
+ });
2007
+ const THROTTLE_MS = 1e3;
2008
+ /**
2009
+ * Returns true when the inner component should re-render immediately
2010
+ * (no throttle delay).
2011
+ */
2012
+ function shouldFlushImmediately(prev, next) {
2013
+ if (next.cssComplete && (!prev || !prev.cssComplete)) return true;
2014
+ if (next.htmlComplete) return true;
2015
+ if (next.generating === false) return true;
2016
+ if (next.jsFunctions && (!prev || !prev.jsFunctions)) return true;
2017
+ if ((next.jsExpressions?.length ?? 0) > (prev?.jsExpressions?.length ?? 0)) return true;
2018
+ if (next.html?.length && (!prev || !prev.html?.length)) return true;
2019
+ return false;
2020
+ }
2021
+ /**
2022
+ * Outer wrapper — absorbs every parent re-render but only forwards
2023
+ * throttled content snapshots to the memoized inner component.
2024
+ */
2025
+ const OpenGenerativeUIActivityRenderer = function OpenGenerativeUIActivityRenderer({ content }) {
2026
+ const latestContentRef = useRef(content);
2027
+ latestContentRef.current = content;
2028
+ const [throttledContent, setThrottledContent] = useState(content);
2029
+ const throttledContentRef = useRef(throttledContent);
2030
+ const timerRef = useRef(null);
2031
+ if (throttledContentRef.current !== content) {
2032
+ if (shouldFlushImmediately(throttledContentRef.current, content)) {
2033
+ if (timerRef.current !== null) {
2034
+ clearTimeout(timerRef.current);
2035
+ timerRef.current = null;
2036
+ }
2037
+ throttledContentRef.current = content;
2038
+ setThrottledContent(content);
2039
+ }
2040
+ }
2041
+ const flush = useCallback(() => {
2042
+ timerRef.current = null;
2043
+ const latest = latestContentRef.current;
2044
+ throttledContentRef.current = latest;
2045
+ setThrottledContent(latest);
2046
+ }, []);
2047
+ useEffect(() => {
2048
+ if (throttledContentRef.current === content) return;
2049
+ if (timerRef.current === null) timerRef.current = setTimeout(flush, THROTTLE_MS);
2050
+ }, [content, flush]);
2051
+ useEffect(() => {
2052
+ return () => {
2053
+ if (timerRef.current !== null) clearTimeout(timerRef.current);
2054
+ };
2055
+ }, []);
2056
+ return /* @__PURE__ */ jsx(OpenGenerativeUIActivityRendererInner, { content: throttledContent });
2057
+ };
2058
+ function ensureHead(html) {
2059
+ if (/<head[\s>]/i.test(html)) return html;
2060
+ return `<head></head>${html}`;
2061
+ }
2062
+ function injectCssIntoHtml(html, css) {
2063
+ const headCloseIdx = html.indexOf("</head>");
2064
+ if (headCloseIdx !== -1) return html.slice(0, headCloseIdx) + `<style>${css}</style>` + html.slice(headCloseIdx);
2065
+ return `<head><style>${css}</style></head>${html}`;
2066
+ }
2067
+ const OpenGenerativeUIActivityRendererInner = React.memo(function OpenGenerativeUIActivityRendererInner({ content }) {
2068
+ const initialHeight = content.initialHeight ?? 200;
2069
+ const [autoHeight, setAutoHeight] = useState(null);
2070
+ const sandboxFunctions = useSandboxFunctions();
2071
+ const localApi = useMemo(() => {
2072
+ const api = {};
2073
+ for (const fn of sandboxFunctions) api[fn.name] = fn.handler;
2074
+ return api;
2075
+ }, [sandboxFunctions]);
2076
+ const fullHtml = content.htmlComplete && content.html?.length ? content.html.join("") : void 0;
2077
+ const css = content.cssComplete ? content.css : void 0;
2078
+ const cssReady = !!content.cssComplete;
2079
+ const partialHtml = !content.htmlComplete && content.html?.length ? content.html.join("") : void 0;
2080
+ const previewBody = partialHtml ? processPartialHtml(partialHtml) : void 0;
2081
+ const previewStyles = partialHtml ? extractCompleteStyles(partialHtml) : "";
2082
+ const hasPreview = cssReady && !!previewBody?.trim();
2083
+ const hasVisibleSandbox = !!fullHtml || hasPreview;
2084
+ const containerRef = useRef(null);
2085
+ const sandboxRef = useRef(null);
2086
+ const previewSandboxRef = useRef(null);
2087
+ const previewReadyRef = useRef(false);
2088
+ const sandboxReadyRef = useRef(false);
2089
+ const executedIndexRef = useRef(0);
2090
+ const pendingQueueRef = useRef([]);
2091
+ const jsFunctionsInjectedRef = useRef(false);
2092
+ useEffect(() => {
2093
+ const container = containerRef.current;
2094
+ if (!container || fullHtml || !hasPreview || previewSandboxRef.current) return;
2095
+ let cancelled = false;
2096
+ import("@jetbrains/websandbox").then((mod) => {
2097
+ if (cancelled) return;
2098
+ const sandbox = (mod.default?.default ?? mod.default).create({}, {
2099
+ frameContainer: container,
2100
+ frameContent: "<head></head><body></body>",
2101
+ allowAdditionalAttributes: ""
2102
+ });
2103
+ previewSandboxRef.current = sandbox;
2104
+ sandbox.iframe.style.width = "100%";
2105
+ sandbox.iframe.style.height = "100%";
2106
+ sandbox.iframe.style.border = "none";
2107
+ sandbox.iframe.style.backgroundColor = "transparent";
2108
+ sandbox.promise.then(() => {
2109
+ if (cancelled) return;
2110
+ previewReadyRef.current = true;
2111
+ sandbox.run(`
2112
+ var s = document.createElement('style');
2113
+ s.textContent = 'html, body { overflow: hidden !important; }';
2114
+ document.head.appendChild(s);
2115
+ `);
2116
+ const headParts = [];
2117
+ if (css) headParts.push(`<style>${css}</style>`);
2118
+ if (previewStyles) headParts.push(previewStyles);
2119
+ if (headParts.length) sandbox.run(`document.head.innerHTML = ${JSON.stringify(headParts.join(""))}`);
2120
+ if (previewBody) sandbox.run(`document.body.innerHTML = ${JSON.stringify(previewBody)}`);
2121
+ });
2122
+ }).catch((err) => {
2123
+ console.error("[OpenGenerativeUI] Failed to load sandbox module:", err);
2124
+ });
2125
+ return () => {
2126
+ cancelled = true;
2127
+ };
2128
+ }, [hasPreview, fullHtml]);
2129
+ useEffect(() => {
2130
+ if (!previewSandboxRef.current || !previewReadyRef.current) return;
2131
+ const headParts = [];
2132
+ if (css) headParts.push(`<style>${css}</style>`);
2133
+ if (previewStyles) headParts.push(previewStyles);
2134
+ if (headParts.length) previewSandboxRef.current.run(`document.head.innerHTML = ${JSON.stringify(headParts.join(""))}`);
2135
+ if (!previewBody) return;
2136
+ previewSandboxRef.current.run(`document.body.innerHTML = ${JSON.stringify(previewBody)}`);
2137
+ }, [
2138
+ previewBody,
2139
+ previewStyles,
2140
+ css
2141
+ ]);
2142
+ useEffect(() => {
2143
+ const container = containerRef.current;
2144
+ if (!container || !fullHtml) return;
2145
+ if (previewSandboxRef.current) {
2146
+ previewSandboxRef.current.destroy();
2147
+ previewSandboxRef.current = null;
2148
+ previewReadyRef.current = false;
2149
+ }
2150
+ let cancelled = false;
2151
+ executedIndexRef.current = 0;
2152
+ jsFunctionsInjectedRef.current = false;
2153
+ sandboxReadyRef.current = false;
2154
+ pendingQueueRef.current = [];
2155
+ const htmlContent = css ? injectCssIntoHtml(fullHtml, css) : fullHtml;
2156
+ import("@jetbrains/websandbox").then((mod) => {
2157
+ if (cancelled) return;
2158
+ const sandbox = (mod.default?.default ?? mod.default).create(localApi, {
2159
+ frameContainer: container,
2160
+ frameContent: ensureHead(htmlContent),
2161
+ allowAdditionalAttributes: ""
2162
+ });
2163
+ sandboxRef.current = sandbox;
2164
+ sandbox.iframe.style.width = "100%";
2165
+ sandbox.iframe.style.height = "100%";
2166
+ sandbox.iframe.style.border = "none";
2167
+ sandbox.iframe.style.backgroundColor = "transparent";
2168
+ sandbox.promise.then(() => {
2169
+ if (cancelled) return;
2170
+ sandboxReadyRef.current = true;
2171
+ sandbox.run(`
2172
+ var s = document.createElement('style');
2173
+ s.textContent = 'html, body { overflow: hidden !important; }';
2174
+ document.head.appendChild(s);
2175
+ `);
2176
+ const queue = pendingQueueRef.current;
2177
+ pendingQueueRef.current = [];
2178
+ for (const code of queue) sandbox.run(code);
2179
+ });
2180
+ }).catch((err) => {
2181
+ console.error("[OpenGenerativeUI] Failed to load sandbox module:", err);
2182
+ });
2183
+ return () => {
2184
+ cancelled = true;
2185
+ if (previewSandboxRef.current) {
2186
+ previewSandboxRef.current.destroy();
2187
+ previewSandboxRef.current = null;
2188
+ previewReadyRef.current = false;
2189
+ }
2190
+ if (sandboxRef.current) {
2191
+ sandboxRef.current.destroy();
2192
+ sandboxRef.current = null;
2193
+ }
2194
+ sandboxReadyRef.current = false;
2195
+ setAutoHeight(null);
2196
+ };
2197
+ }, [
2198
+ fullHtml,
2199
+ css,
2200
+ localApi
2201
+ ]);
2202
+ useEffect(() => {
2203
+ if (!content.jsFunctions || jsFunctionsInjectedRef.current) return;
2204
+ jsFunctionsInjectedRef.current = true;
2205
+ const sandbox = sandboxRef.current;
2206
+ if (sandboxReadyRef.current && sandbox) sandbox.run(content.jsFunctions);
2207
+ else pendingQueueRef.current.push(content.jsFunctions);
2208
+ }, [content.jsFunctions]);
2209
+ useEffect(() => {
2210
+ const expressions = content.jsExpressions;
2211
+ if (!expressions || expressions.length === 0) return;
2212
+ const startIndex = executedIndexRef.current;
2213
+ if (startIndex >= expressions.length) return;
2214
+ const newExprs = expressions.slice(startIndex);
2215
+ executedIndexRef.current = expressions.length;
2216
+ const sandbox = sandboxRef.current;
2217
+ if (sandboxReadyRef.current && sandbox) (async () => {
2218
+ for (const expr of newExprs) await sandbox.run(expr);
2219
+ })();
2220
+ else pendingQueueRef.current.push(...newExprs);
2221
+ }, [content.jsExpressions?.length]);
2222
+ const generationDone = content.generating === false;
2223
+ useEffect(() => {
2224
+ const sandbox = sandboxRef.current;
2225
+ if (!generationDone || !sandbox) return;
2226
+ let handled = false;
2227
+ const onMessage = (e) => {
2228
+ if (handled) return;
2229
+ if (e.source === sandbox.iframe.contentWindow && e.data?.type === "__ck_resize") {
2230
+ handled = true;
2231
+ setAutoHeight(e.data.height);
2232
+ window.removeEventListener("message", onMessage);
2233
+ }
2234
+ };
2235
+ window.addEventListener("message", onMessage);
2236
+ const measureOnce = `
2237
+ (function() {
2238
+ var s = document.createElement('style');
2239
+ s.textContent = 'body { height: auto !important; min-height: 0 !important; }';
2240
+ document.head.appendChild(s);
2241
+ var h = document.body.scrollHeight;
2242
+ var cs = getComputedStyle(document.body);
2243
+ h += parseFloat(cs.marginTop) || 0;
2244
+ h += parseFloat(cs.marginBottom) || 0;
2245
+ s.remove();
2246
+ parent.postMessage({ type: "__ck_resize", height: Math.ceil(h) }, "*");
2247
+ })();
2248
+ `;
2249
+ if (sandboxReadyRef.current) sandbox.run(measureOnce);
2250
+ else pendingQueueRef.current.push(measureOnce);
2251
+ return () => {
2252
+ window.removeEventListener("message", onMessage);
2253
+ };
2254
+ }, [generationDone]);
2255
+ const height = autoHeight ?? initialHeight;
2256
+ const isGenerating = content.generating !== false;
2257
+ return /* @__PURE__ */ jsx("div", {
2258
+ ref: containerRef,
2259
+ style: {
2260
+ position: "relative",
2261
+ width: "100%",
2262
+ height: `${height}px`,
2263
+ borderRadius: "8px",
2264
+ backgroundColor: hasVisibleSandbox ? "transparent" : "#f5f5f5",
2265
+ border: hasVisibleSandbox ? "none" : "1px solid #e0e0e0",
2266
+ display: hasVisibleSandbox ? "block" : "flex",
2267
+ alignItems: hasVisibleSandbox ? void 0 : "center",
2268
+ justifyContent: hasVisibleSandbox ? void 0 : "center",
2269
+ overflow: "hidden"
2270
+ },
2271
+ children: isGenerating && /* @__PURE__ */ jsxs("div", {
2272
+ style: {
2273
+ position: "absolute",
2274
+ inset: 0,
2275
+ zIndex: 10,
2276
+ pointerEvents: "all",
2277
+ backgroundColor: "rgba(255, 255, 255, 0.5)",
2278
+ display: "flex",
2279
+ alignItems: "center",
2280
+ justifyContent: "center"
2281
+ },
2282
+ children: [/* @__PURE__ */ jsxs("svg", {
2283
+ width: "48",
2284
+ height: "48",
2285
+ viewBox: "0 0 24 24",
2286
+ fill: "none",
2287
+ style: { animation: "ck-spin 1s linear infinite" },
2288
+ children: [/* @__PURE__ */ jsx("circle", {
2289
+ cx: "12",
2290
+ cy: "12",
2291
+ r: "10",
2292
+ stroke: "#e0e0e0",
2293
+ strokeWidth: "3"
2294
+ }), /* @__PURE__ */ jsx("path", {
2295
+ d: "M12 2a10 10 0 0 1 10 10",
2296
+ stroke: "#999",
2297
+ strokeWidth: "3",
2298
+ strokeLinecap: "round"
2299
+ })]
2300
+ }), /* @__PURE__ */ jsx("style", { children: `@keyframes ck-spin { to { transform: rotate(360deg) } }` })]
2301
+ })
2302
+ });
2303
+ }, (prev, next) => prev.content === next.content);
2304
+ /**
2305
+ * Frontend tool renderer for generateSandboxedUi.
2306
+ * Displays placeholder messages while the UI is being generated.
2307
+ */
2308
+ const OpenGenerativeUIToolRenderer = function OpenGenerativeUIToolRenderer(props) {
2309
+ const [visibleMessageIndex, setVisibleMessageIndex] = useState(0);
2310
+ const prevMessageCountRef = useRef(0);
2311
+ const messages = props.args.placeholderMessages;
2312
+ useEffect(() => {
2313
+ if (!messages || messages.length === 0) return;
2314
+ if (messages.length !== prevMessageCountRef.current) {
2315
+ prevMessageCountRef.current = messages.length;
2316
+ setVisibleMessageIndex(messages.length - 1);
2317
+ }
2318
+ if (props.status === ToolCallStatus.Complete) return;
2319
+ const timer = setInterval(() => {
2320
+ setVisibleMessageIndex((i) => (i + 1) % messages.length);
2321
+ }, 5e3);
2322
+ return () => clearInterval(timer);
2323
+ }, [messages?.length, props.status]);
2324
+ if (props.status === ToolCallStatus.Complete) return null;
2325
+ if (!messages || messages.length === 0) return null;
2326
+ return /* @__PURE__ */ jsx("div", {
2327
+ style: {
2328
+ padding: "8px 12px",
2329
+ color: "#999",
2330
+ fontSize: "14px"
2331
+ },
2332
+ children: messages[visibleMessageIndex] ?? messages[0]
2333
+ });
2334
+ };
2335
+
1885
2336
  //#endregion
1886
2337
  //#region src/v2/a2ui/A2UIMessageRenderer.tsx
2338
+ /**
2339
+ * The container key used to wrap A2UI operations for explicit detection.
2340
+ * Must match A2UI_OPERATIONS_KEY in @ag-ui/a2ui-middleware and copilotkit.a2ui (Python).
2341
+ */
2342
+ const A2UI_OPERATIONS_KEY = "a2ui_operations";
1887
2343
  let initialized = false;
1888
2344
  function ensureInitialized() {
1889
2345
  if (!initialized) {
@@ -1893,25 +2349,23 @@ function ensureInitialized() {
1893
2349
  }
1894
2350
  }
1895
2351
  function createA2UIMessageRenderer(options) {
1896
- const { theme } = options;
2352
+ const { theme, catalog, loadingComponent } = options;
1897
2353
  return {
1898
2354
  activityType: "a2ui-surface",
1899
2355
  content: z.any(),
1900
2356
  render: ({ content, agent }) => {
1901
2357
  ensureInitialized();
1902
2358
  const [operations, setOperations] = useState([]);
1903
- const lastSignatureRef = useRef(null);
1904
2359
  const { copilotkit } = useCopilotKit();
2360
+ const lastContentRef = useRef(null);
1905
2361
  useEffect(() => {
1906
- if (!content || !Array.isArray(content.operations)) {
1907
- lastSignatureRef.current = null;
2362
+ if (content === lastContentRef.current) return;
2363
+ lastContentRef.current = content;
2364
+ const incoming = content?.[A2UI_OPERATIONS_KEY];
2365
+ if (!content || !Array.isArray(incoming)) {
1908
2366
  setOperations([]);
1909
2367
  return;
1910
2368
  }
1911
- const incoming = content.operations;
1912
- const signature = stringifyOperations(incoming);
1913
- if (signature && signature === lastSignatureRef.current) return;
1914
- lastSignatureRef.current = signature;
1915
2369
  setOperations(incoming);
1916
2370
  }, [content]);
1917
2371
  const groupedOperations = useMemo(() => {
@@ -1923,7 +2377,7 @@ function createA2UIMessageRenderer(options) {
1923
2377
  }
1924
2378
  return groups;
1925
2379
  }, [operations]);
1926
- if (!groupedOperations.size) return null;
2380
+ if (!groupedOperations.size) return /* @__PURE__ */ jsx(loadingComponent ?? DefaultA2UILoading, {});
1927
2381
  return /* @__PURE__ */ jsx("div", {
1928
2382
  className: "cpk:flex cpk:min-h-0 cpk:flex-1 cpk:flex-col cpk:gap-6 cpk:overflow-auto cpk:py-6",
1929
2383
  children: Array.from(groupedOperations.entries()).map(([surfaceId, ops]) => /* @__PURE__ */ jsx(ReactSurfaceHost, {
@@ -1931,7 +2385,8 @@ function createA2UIMessageRenderer(options) {
1931
2385
  operations: ops,
1932
2386
  theme,
1933
2387
  agent,
1934
- copilotkit
2388
+ copilotkit,
2389
+ catalog
1935
2390
  }, surfaceId))
1936
2391
  });
1937
2392
  }
@@ -1941,14 +2396,14 @@ function createA2UIMessageRenderer(options) {
1941
2396
  * Renders a single A2UI surface using the React renderer.
1942
2397
  * Wraps A2UIProvider + A2UIRenderer and bridges actions back to CopilotKit.
1943
2398
  */
1944
- function ReactSurfaceHost({ surfaceId, operations, theme, agent, copilotkit }) {
2399
+ function ReactSurfaceHost({ surfaceId, operations, theme, agent, copilotkit, catalog }) {
1945
2400
  return /* @__PURE__ */ jsx("div", {
1946
2401
  className: "cpk:flex cpk:w-full cpk:flex-none cpk:flex-col cpk:gap-4",
1947
2402
  children: /* @__PURE__ */ jsxs(A2UIProvider, {
1948
2403
  onAction: useCallback(async (message) => {
1949
2404
  if (!agent) return;
2405
+ message.userAction;
1950
2406
  try {
1951
- console.info("[A2UI] Action dispatched", message.userAction);
1952
2407
  copilotkit.setProperties({
1953
2408
  ...copilotkit.properties ?? {},
1954
2409
  a2uiAction: message
@@ -1962,47 +2417,501 @@ function ReactSurfaceHost({ surfaceId, operations, theme, agent, copilotkit }) {
1962
2417
  }
1963
2418
  }, [agent, copilotkit]),
1964
2419
  theme,
2420
+ catalog,
1965
2421
  children: [/* @__PURE__ */ jsx(SurfaceMessageProcessor, {
1966
2422
  surfaceId,
1967
2423
  operations
1968
- }), /* @__PURE__ */ jsx(A2UIRenderer, {
1969
- surfaceId,
1970
- className: "cpk:flex cpk:flex-1"
1971
- })]
2424
+ }), /* @__PURE__ */ jsx(A2UISurfaceOrError, { surfaceId })]
1972
2425
  })
1973
2426
  });
1974
2427
  }
1975
2428
  /**
2429
+ * Renders the A2UI surface, or an error message if processing failed.
2430
+ * Must be a child of A2UIProvider to access the error state.
2431
+ */
2432
+ function A2UISurfaceOrError({ surfaceId }) {
2433
+ const error = useA2UIError();
2434
+ if (error) return /* @__PURE__ */ jsxs("div", {
2435
+ className: "cpk:rounded-lg cpk:border cpk:border-red-200 cpk:bg-red-50 cpk:p-3 cpk:text-sm cpk:text-red-700",
2436
+ children: ["A2UI render error: ", error]
2437
+ });
2438
+ return /* @__PURE__ */ jsx(A2UIRenderer, {
2439
+ surfaceId,
2440
+ className: "cpk:flex cpk:flex-1"
2441
+ });
2442
+ }
2443
+ /**
1976
2444
  * Processes A2UI operations into the provider's message processor.
1977
2445
  * Must be a child of A2UIProvider to access the actions context.
1978
2446
  */
1979
2447
  function SurfaceMessageProcessor({ surfaceId, operations }) {
1980
- const { processMessages } = useA2UIActions();
1981
- const lastProcessedRef = useRef("");
2448
+ const { processMessages, getSurface } = useA2UIActions();
2449
+ const lastHashRef = useRef("");
1982
2450
  useEffect(() => {
1983
- const key = `${surfaceId}-${JSON.stringify(operations)}`;
1984
- if (key === lastProcessedRef.current) return;
1985
- lastProcessedRef.current = key;
1986
- processMessages(operations);
2451
+ const hash = JSON.stringify(operations);
2452
+ if (hash === lastHashRef.current) return;
2453
+ lastHashRef.current = hash;
2454
+ processMessages(getSurface(surfaceId) ? operations.filter((op) => !op?.createSurface) : operations);
1987
2455
  }, [
1988
2456
  processMessages,
2457
+ getSurface,
1989
2458
  surfaceId,
1990
2459
  operations
1991
2460
  ]);
1992
2461
  return null;
1993
2462
  }
1994
- function getOperationSurfaceId(operation) {
1995
- if (!operation || typeof operation !== "object") return null;
1996
- if (typeof operation.surfaceId === "string") return operation.surfaceId;
1997
- return operation?.beginRendering?.surfaceId ?? operation?.surfaceUpdate?.surfaceId ?? operation?.dataModelUpdate?.surfaceId ?? operation?.deleteSurface?.surfaceId ?? null;
1998
- }
1999
- function stringifyOperations(ops) {
2000
- try {
2001
- return JSON.stringify(ops);
2002
- } catch (error) {
2003
- return null;
2004
- }
2005
- }
2463
+ /**
2464
+ * Default loading component shown while an A2UI surface is generating.
2465
+ * Displays an animated shimmer skeleton.
2466
+ */
2467
+ function DefaultA2UILoading() {
2468
+ return /* @__PURE__ */ jsxs("div", {
2469
+ 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",
2470
+ style: { minHeight: 120 },
2471
+ children: [
2472
+ /* @__PURE__ */ jsxs("div", {
2473
+ className: "cpk:flex cpk:items-center cpk:gap-2",
2474
+ children: [/* @__PURE__ */ jsx("div", {
2475
+ className: "cpk:h-3 cpk:w-3 cpk:rounded-full cpk:bg-gray-200",
2476
+ style: { animation: "cpk-a2ui-pulse 1.5s ease-in-out infinite" }
2477
+ }), /* @__PURE__ */ jsx("span", {
2478
+ className: "cpk:text-xs cpk:font-medium cpk:text-gray-400",
2479
+ children: "Generating UI..."
2480
+ })]
2481
+ }),
2482
+ /* @__PURE__ */ jsx("div", {
2483
+ className: "cpk:flex cpk:flex-col cpk:gap-2",
2484
+ children: [
2485
+ .8,
2486
+ .6,
2487
+ .4
2488
+ ].map((width, i) => /* @__PURE__ */ jsx("div", {
2489
+ className: "cpk:h-3 cpk:rounded cpk:bg-gray-200/70",
2490
+ style: {
2491
+ width: `${width * 100}%`,
2492
+ animation: `cpk-a2ui-pulse 1.5s ease-in-out ${i * .15}s infinite`
2493
+ }
2494
+ }, i))
2495
+ }),
2496
+ /* @__PURE__ */ jsx("style", { children: `
2497
+ @keyframes cpk-a2ui-pulse {
2498
+ 0%, 100% { opacity: 0.4; }
2499
+ 50% { opacity: 1; }
2500
+ }
2501
+ ` })
2502
+ ]
2503
+ });
2504
+ }
2505
+ function getOperationSurfaceId(operation) {
2506
+ if (!operation || typeof operation !== "object") return null;
2507
+ if (typeof operation.surfaceId === "string") return operation.surfaceId;
2508
+ return operation?.createSurface?.surfaceId ?? operation?.updateComponents?.surfaceId ?? operation?.updateDataModel?.surfaceId ?? operation?.deleteSurface?.surfaceId ?? null;
2509
+ }
2510
+
2511
+ //#endregion
2512
+ //#region src/v2/types/defineToolCallRenderer.ts
2513
+ function defineToolCallRenderer(def) {
2514
+ const argsSchema = def.name === "*" && !def.args ? z.any() : def.args;
2515
+ return {
2516
+ name: def.name,
2517
+ args: argsSchema,
2518
+ render: def.render,
2519
+ ...def.agentId ? { agentId: def.agentId } : {}
2520
+ };
2521
+ }
2522
+
2523
+ //#endregion
2524
+ //#region src/v2/a2ui/A2UIToolCallRenderer.tsx
2525
+ /**
2526
+ * Tool name used by the dynamic A2UI generation secondary LLM.
2527
+ * This renderer is auto-registered when A2UI is enabled.
2528
+ */
2529
+ const RENDER_A2UI_TOOL_NAME = "render_a2ui";
2530
+ /**
2531
+ * Built-in progress indicator for dynamic A2UI generation.
2532
+ * Shows a skeleton wireframe that progressively reveals as tokens stream in.
2533
+ *
2534
+ * Registered automatically when A2UI is enabled. Users can override by
2535
+ * providing their own `useRenderTool({ name: "render_a2ui", ... })`.
2536
+ */
2537
+ function A2UIProgressIndicator({ parameters }) {
2538
+ const lastRef = useRef({
2539
+ time: 0,
2540
+ tokens: 0
2541
+ });
2542
+ const now = Date.now();
2543
+ let { tokens } = lastRef.current;
2544
+ if (now - lastRef.current.time > 200) {
2545
+ const chars = JSON.stringify(parameters ?? {}).length;
2546
+ tokens = Math.round(chars / 4);
2547
+ lastRef.current = {
2548
+ time: now,
2549
+ tokens
2550
+ };
2551
+ }
2552
+ const phase = tokens < 50 ? 0 : tokens < 200 ? 1 : tokens < 400 ? 2 : 3;
2553
+ return /* @__PURE__ */ jsxs("div", {
2554
+ style: {
2555
+ margin: "12px 0",
2556
+ maxWidth: 320
2557
+ },
2558
+ children: [
2559
+ /* @__PURE__ */ jsxs("div", {
2560
+ style: {
2561
+ position: "relative",
2562
+ overflow: "hidden",
2563
+ borderRadius: 12,
2564
+ border: "1px solid rgba(228,228,231,0.8)",
2565
+ backgroundColor: "#fff",
2566
+ boxShadow: "0 1px 2px rgba(0,0,0,0.04)",
2567
+ padding: "16px 18px 14px"
2568
+ },
2569
+ children: [
2570
+ /* @__PURE__ */ jsxs("div", {
2571
+ style: {
2572
+ display: "flex",
2573
+ alignItems: "center",
2574
+ gap: 8,
2575
+ marginBottom: 12
2576
+ },
2577
+ children: [/* @__PURE__ */ jsxs("div", {
2578
+ style: {
2579
+ display: "flex",
2580
+ gap: 4
2581
+ },
2582
+ children: [
2583
+ /* @__PURE__ */ jsx(Dot, {}),
2584
+ /* @__PURE__ */ jsx(Dot, {}),
2585
+ /* @__PURE__ */ jsx(Dot, {})
2586
+ ]
2587
+ }), /* @__PURE__ */ jsx(Bar, {
2588
+ w: 64,
2589
+ h: 6,
2590
+ bg: "#e4e4e7",
2591
+ opacity: phase >= 1 ? 1 : .4,
2592
+ transition: "opacity 0.5s"
2593
+ })]
2594
+ }),
2595
+ /* @__PURE__ */ jsxs("div", {
2596
+ style: {
2597
+ display: "grid",
2598
+ gap: 7
2599
+ },
2600
+ children: [
2601
+ /* @__PURE__ */ jsxs(Row, {
2602
+ show: phase >= 0,
2603
+ children: [/* @__PURE__ */ jsx(Bar, {
2604
+ w: 36,
2605
+ h: 7,
2606
+ bg: "rgba(147,197,253,0.7)",
2607
+ anim: 0
2608
+ }), /* @__PURE__ */ jsx(Bar, {
2609
+ w: 80,
2610
+ h: 7,
2611
+ bg: "rgba(219,234,254,0.8)",
2612
+ anim: .2
2613
+ })]
2614
+ }),
2615
+ /* @__PURE__ */ jsxs(Row, {
2616
+ show: phase >= 0,
2617
+ delay: .1,
2618
+ children: [
2619
+ /* @__PURE__ */ jsx(Spacer, {}),
2620
+ /* @__PURE__ */ jsx(Dot, {}),
2621
+ /* @__PURE__ */ jsx(Bar, {
2622
+ w: 100,
2623
+ h: 7,
2624
+ bg: "rgba(24,24,27,0.2)",
2625
+ anim: .3
2626
+ })
2627
+ ]
2628
+ }),
2629
+ /* @__PURE__ */ jsxs(Row, {
2630
+ show: phase >= 1,
2631
+ delay: .15,
2632
+ children: [
2633
+ /* @__PURE__ */ jsx(Spacer, {}),
2634
+ /* @__PURE__ */ jsx(Bar, {
2635
+ w: 48,
2636
+ h: 7,
2637
+ bg: "rgba(24,24,27,0.15)",
2638
+ anim: .1
2639
+ }),
2640
+ /* @__PURE__ */ jsx(Bar, {
2641
+ w: 40,
2642
+ h: 7,
2643
+ bg: "rgba(153,246,228,0.6)",
2644
+ anim: .5
2645
+ }),
2646
+ /* @__PURE__ */ jsx(Bar, {
2647
+ w: 56,
2648
+ h: 7,
2649
+ bg: "rgba(147,197,253,0.6)",
2650
+ anim: .3
2651
+ })
2652
+ ]
2653
+ }),
2654
+ /* @__PURE__ */ jsxs(Row, {
2655
+ show: phase >= 1,
2656
+ delay: .2,
2657
+ children: [
2658
+ /* @__PURE__ */ jsx(Spacer, {}),
2659
+ /* @__PURE__ */ jsx(Dot, {}),
2660
+ /* @__PURE__ */ jsx(Bar, {
2661
+ w: 60,
2662
+ h: 7,
2663
+ bg: "rgba(24,24,27,0.15)",
2664
+ anim: .4
2665
+ })
2666
+ ]
2667
+ }),
2668
+ /* @__PURE__ */ jsxs(Row, {
2669
+ show: phase >= 2,
2670
+ delay: .25,
2671
+ children: [
2672
+ /* @__PURE__ */ jsx(Bar, {
2673
+ w: 40,
2674
+ h: 7,
2675
+ bg: "rgba(153,246,228,0.5)",
2676
+ anim: .2
2677
+ }),
2678
+ /* @__PURE__ */ jsx(Dot, {}),
2679
+ /* @__PURE__ */ jsx(Bar, {
2680
+ w: 48,
2681
+ h: 7,
2682
+ bg: "rgba(24,24,27,0.15)",
2683
+ anim: .6
2684
+ }),
2685
+ /* @__PURE__ */ jsx(Bar, {
2686
+ w: 64,
2687
+ h: 7,
2688
+ bg: "rgba(147,197,253,0.5)",
2689
+ anim: .1
2690
+ })
2691
+ ]
2692
+ }),
2693
+ /* @__PURE__ */ jsxs(Row, {
2694
+ show: phase >= 2,
2695
+ delay: .3,
2696
+ children: [/* @__PURE__ */ jsx(Bar, {
2697
+ w: 36,
2698
+ h: 7,
2699
+ bg: "rgba(147,197,253,0.6)",
2700
+ anim: .5
2701
+ }), /* @__PURE__ */ jsx(Bar, {
2702
+ w: 36,
2703
+ h: 7,
2704
+ bg: "rgba(24,24,27,0.12)",
2705
+ anim: .7
2706
+ })]
2707
+ }),
2708
+ /* @__PURE__ */ jsxs(Row, {
2709
+ show: phase >= 3,
2710
+ delay: .35,
2711
+ children: [
2712
+ /* @__PURE__ */ jsx(Dot, {}),
2713
+ /* @__PURE__ */ jsx(Bar, {
2714
+ w: 44,
2715
+ h: 7,
2716
+ bg: "rgba(24,24,27,0.18)",
2717
+ anim: .3
2718
+ }),
2719
+ /* @__PURE__ */ jsx(Dot, {}),
2720
+ /* @__PURE__ */ jsx(Bar, {
2721
+ w: 56,
2722
+ h: 7,
2723
+ bg: "rgba(153,246,228,0.5)",
2724
+ anim: .8
2725
+ }),
2726
+ /* @__PURE__ */ jsx(Bar, {
2727
+ w: 48,
2728
+ h: 7,
2729
+ bg: "rgba(147,197,253,0.5)",
2730
+ anim: .4
2731
+ })
2732
+ ]
2733
+ })
2734
+ ]
2735
+ }),
2736
+ /* @__PURE__ */ jsx("div", { style: {
2737
+ pointerEvents: "none",
2738
+ position: "absolute",
2739
+ inset: 0,
2740
+ background: "linear-gradient(105deg, transparent 0%, transparent 40%, rgba(255,255,255,0.6) 50%, transparent 60%, transparent 100%)",
2741
+ backgroundSize: "250% 100%",
2742
+ animation: "cpk-a2ui-sweep 3s ease-in-out infinite"
2743
+ } })
2744
+ ]
2745
+ }),
2746
+ /* @__PURE__ */ jsxs("div", {
2747
+ style: {
2748
+ display: "flex",
2749
+ alignItems: "center",
2750
+ justifyContent: "center",
2751
+ gap: 8,
2752
+ marginTop: 8
2753
+ },
2754
+ children: [/* @__PURE__ */ jsx("span", {
2755
+ style: {
2756
+ fontSize: 12,
2757
+ color: "#a1a1aa",
2758
+ letterSpacing: "0.025em"
2759
+ },
2760
+ children: "Building interface"
2761
+ }), tokens > 0 && /* @__PURE__ */ jsxs("span", {
2762
+ style: {
2763
+ fontSize: 11,
2764
+ color: "#d4d4d8",
2765
+ fontVariantNumeric: "tabular-nums"
2766
+ },
2767
+ children: [
2768
+ "~",
2769
+ tokens.toLocaleString(),
2770
+ " tokens"
2771
+ ]
2772
+ })]
2773
+ }),
2774
+ /* @__PURE__ */ jsx("style", { children: `
2775
+ @keyframes cpk-a2ui-fade {
2776
+ 0%, 100% { opacity: 1; }
2777
+ 50% { opacity: 0.5; }
2778
+ }
2779
+ @keyframes cpk-a2ui-sweep {
2780
+ 0% { background-position: 250% 0; }
2781
+ 100% { background-position: -250% 0; }
2782
+ }
2783
+ ` })
2784
+ ]
2785
+ });
2786
+ }
2787
+ function Dot() {
2788
+ return /* @__PURE__ */ jsx("div", { style: {
2789
+ width: 7,
2790
+ height: 7,
2791
+ borderRadius: "50%",
2792
+ backgroundColor: "#d4d4d8",
2793
+ flexShrink: 0
2794
+ } });
2795
+ }
2796
+ function Spacer() {
2797
+ return /* @__PURE__ */ jsx("div", { style: { width: 12 } });
2798
+ }
2799
+ function Bar({ w, h, bg, anim, opacity, transition }) {
2800
+ return /* @__PURE__ */ jsx("div", { style: {
2801
+ width: w,
2802
+ height: h,
2803
+ borderRadius: 9999,
2804
+ backgroundColor: bg,
2805
+ ...anim !== void 0 ? { animation: `cpk-a2ui-fade 2.4s ease-in-out ${anim}s infinite` } : {},
2806
+ ...opacity !== void 0 ? { opacity } : {},
2807
+ ...transition ? { transition } : {}
2808
+ } });
2809
+ }
2810
+ function Row({ children, show, delay = 0 }) {
2811
+ return /* @__PURE__ */ jsx("div", {
2812
+ style: {
2813
+ display: "flex",
2814
+ alignItems: "center",
2815
+ gap: 6,
2816
+ opacity: show ? 1 : 0,
2817
+ transition: `opacity 0.4s ${delay}s`
2818
+ },
2819
+ children
2820
+ });
2821
+ }
2822
+ /**
2823
+ * Registers the built-in `render_a2ui` tool call renderer via the props-based
2824
+ * `setRenderToolCalls` mechanism (not `useRenderTool`).
2825
+ *
2826
+ * This ensures user-registered `useRenderTool({ name: "render_a2ui", ... })`
2827
+ * hooks automatically override the built-in, since the merge logic in
2828
+ * react-core.ts gives hook-based entries priority over prop-based entries.
2829
+ */
2830
+ function A2UIBuiltInToolCallRenderer() {
2831
+ const { copilotkit } = useCopilotKit();
2832
+ useEffect(() => {
2833
+ const renderer = defineToolCallRenderer({
2834
+ name: RENDER_A2UI_TOOL_NAME,
2835
+ args: z.any(),
2836
+ render: ({ status, args: parameters }) => {
2837
+ if (status === "complete") return /* @__PURE__ */ jsx(Fragment$1, {});
2838
+ const params = parameters;
2839
+ const items = params?.items;
2840
+ if (Array.isArray(items) && items.length > 0) return /* @__PURE__ */ jsx(Fragment$1, {});
2841
+ const components = params?.components;
2842
+ if (Array.isArray(components) && components.length > 2) return /* @__PURE__ */ jsx(Fragment$1, {});
2843
+ return /* @__PURE__ */ jsx(A2UIProgressIndicator, { parameters });
2844
+ }
2845
+ });
2846
+ const existing = copilotkit._renderToolCalls ?? [];
2847
+ copilotkit.setRenderToolCalls([...existing.filter((rc) => rc.name !== RENDER_A2UI_TOOL_NAME), renderer]);
2848
+ }, [copilotkit]);
2849
+ return null;
2850
+ }
2851
+
2852
+ //#endregion
2853
+ //#region src/v2/hooks/use-agent-context.tsx
2854
+ function useAgentContext(context) {
2855
+ const { description, value } = context;
2856
+ const { copilotkit } = useCopilotKit();
2857
+ const stringValue = useMemo(() => {
2858
+ if (typeof value === "string") return value;
2859
+ return JSON.stringify(value);
2860
+ }, [value]);
2861
+ useLayoutEffect(() => {
2862
+ if (!copilotkit) return;
2863
+ const id = copilotkit.addContext({
2864
+ description,
2865
+ value: stringValue
2866
+ });
2867
+ return () => {
2868
+ copilotkit.removeContext(id);
2869
+ };
2870
+ }, [
2871
+ description,
2872
+ stringValue,
2873
+ copilotkit
2874
+ ]);
2875
+ }
2876
+
2877
+ //#endregion
2878
+ //#region src/v2/a2ui/A2UICatalogContext.tsx
2879
+ /**
2880
+ * Renders agent context describing the available A2UI catalog and custom components.
2881
+ * Only mount this component when A2UI is enabled.
2882
+ *
2883
+ * When `includeSchema` is true, the full component schemas (JSON Schema) are also
2884
+ * sent as context using the same description key as the A2UI middleware, so the
2885
+ * middleware can optionally overwrite it with a server-side schema.
2886
+ */
2887
+ function A2UICatalogContext({ catalog, includeSchema }) {
2888
+ useAgentContext({
2889
+ description: "A2UI catalog capabilities: available catalog IDs and custom component definitions the client can render.",
2890
+ value: buildCatalogContextValue(catalog)
2891
+ });
2892
+ const { copilotkit } = useCopilotKit();
2893
+ const schemaValue = useMemo(() => includeSchema !== false ? JSON.stringify(extractCatalogComponentSchemas(catalog)) : null, [catalog, includeSchema]);
2894
+ useLayoutEffect(() => {
2895
+ if (!copilotkit || !schemaValue) return;
2896
+ const ids = [];
2897
+ ids.push(copilotkit.addContext({
2898
+ description: A2UI_SCHEMA_CONTEXT_DESCRIPTION,
2899
+ value: schemaValue
2900
+ }));
2901
+ ids.push(copilotkit.addContext({
2902
+ description: "A2UI generation guidelines — protocol rules, tool arguments, path rules, data model format, and form/two-way-binding instructions.",
2903
+ value: A2UI_DEFAULT_GENERATION_GUIDELINES
2904
+ }));
2905
+ ids.push(copilotkit.addContext({
2906
+ description: "A2UI design guidelines — visual design rules, component hierarchy tips, and action handler patterns.",
2907
+ value: A2UI_DEFAULT_DESIGN_GUIDELINES
2908
+ }));
2909
+ return () => {
2910
+ for (const id of ids) copilotkit.removeContext(id);
2911
+ };
2912
+ }, [copilotkit, schemaValue]);
2913
+ return null;
2914
+ }
2006
2915
 
2007
2916
  //#endregion
2008
2917
  //#region src/v2/lib/react-core.ts
@@ -2104,6 +3013,17 @@ var CopilotKitCoreReact = class extends CopilotKitCore {
2104
3013
  //#region src/v2/providers/CopilotKitProvider.tsx
2105
3014
  const HEADER_NAME = "X-CopilotCloud-Public-Api-Key";
2106
3015
  const COPILOT_CLOUD_CHAT_URL$1 = "https://api.cloud.copilotkit.ai/copilotkit/v1";
3016
+ const DEFAULT_DESIGN_SKILL = `When generating UI with generateSandboxedUi, follow these design principles inspired by shadcn/ui:
3017
+
3018
+ - Use a minimal, flat aesthetic. Avoid drop shadows and gradients — rely on subtle borders (1px solid, light gray like #e5e7eb) to define surfaces.
3019
+ - Neutral base palette: white backgrounds, zinc/slate gray text (#09090b for headings, #71717a for secondary text). One accent color for interactive elements.
3020
+ - Use system font stacks (system-ui, -apple-system, sans-serif) at readable sizes (14px body, 600 weight for headings). Tight line-heights.
3021
+ - Small, consistent border-radius (6–8px). Cards and containers use border, not shadow, for separation.
3022
+ - Buttons: solid fill for primary (dark bg, white text), outline for secondary (border + transparent bg). Subtle hover state (slight opacity or background shift).
3023
+ - Use CSS Grid or Flexbox for layout. Ensure the UI looks good at any width.
3024
+ - Minimal transitions (150ms) for hover/focus states only. No decorative animations.
3025
+ - Keep the UI focused and dense — avoid excessive padding. Use compact spacing (8–12px gaps, 10–14px padding in controls).`;
3026
+ 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)";
2107
3027
  const CopilotKitContext = createContext({
2108
3028
  copilotkit: null,
2109
3029
  executingToolCallIds: /* @__PURE__ */ new Set()
@@ -2119,9 +3039,11 @@ function useStableArrayProp(prop, warningMessage, isMeaningfulChange) {
2119
3039
  }, [value, warningMessage]);
2120
3040
  return value;
2121
3041
  }
2122
- 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 }) => {
3042
+ 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 }) => {
2123
3043
  const [shouldRenderInspector, setShouldRenderInspector] = useState(false);
2124
3044
  const [runtimeA2UIEnabled, setRuntimeA2UIEnabled] = useState(false);
3045
+ const [runtimeOpenGenUIEnabled, setRuntimeOpenGenUIEnabled] = useState(false);
3046
+ const openGenUIActive = runtimeOpenGenUIEnabled || !!openGenerativeUI;
2125
3047
  const [runtimeLicenseStatus, setRuntimeLicenseStatus] = useState(void 0);
2126
3048
  useEffect(() => {
2127
3049
  if (typeof window === "undefined") return;
@@ -2147,9 +3069,22 @@ const CopilotKitProvider = ({ children, runtimeUrl, headers = {}, credentials, p
2147
3069
  content: MCPAppsActivityContentSchema,
2148
3070
  render: MCPAppsActivityRenderer
2149
3071
  }];
2150
- if (runtimeA2UIEnabled) renderers.unshift(createA2UIMessageRenderer({ theme: a2ui?.theme ?? viewerTheme }));
3072
+ if (openGenUIActive) renderers.push({
3073
+ activityType: OpenGenerativeUIActivityType,
3074
+ content: OpenGenerativeUIContentSchema,
3075
+ render: OpenGenerativeUIActivityRenderer
3076
+ });
3077
+ if (runtimeA2UIEnabled) renderers.unshift(createA2UIMessageRenderer({
3078
+ theme: a2ui?.theme ?? viewerTheme,
3079
+ catalog: a2ui?.catalog,
3080
+ loadingComponent: a2ui?.loadingComponent
3081
+ }));
2151
3082
  return renderers;
2152
- }, [runtimeA2UIEnabled, a2ui]);
3083
+ }, [
3084
+ runtimeA2UIEnabled,
3085
+ openGenUIActive,
3086
+ a2ui
3087
+ ]);
2153
3088
  const allActivityRenderers = useMemo(() => {
2154
3089
  return [...renderActivityMessagesList, ...builtInActivityRenderers];
2155
3090
  }, [renderActivityMessagesList, builtInActivityRenderers]);
@@ -2175,6 +3110,7 @@ const CopilotKitProvider = ({ children, runtimeUrl, headers = {}, credentials, p
2175
3110
  const chatApiEndpoint = runtimeUrl ?? (resolvedPublicKey ? COPILOT_CLOUD_CHAT_URL$1 : void 0);
2176
3111
  const frontendToolsList = useStableArrayProp(frontendTools, "frontendTools must be a stable array. If you want to dynamically add or remove tools, use `useFrontendTool` instead.");
2177
3112
  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.");
3113
+ const sandboxFunctionsList = useStableArrayProp(openGenerativeUI?.sandboxFunctions, "openGenerativeUI.sandboxFunctions must be a stable array.");
2178
3114
  const processedHumanInTheLoopTools = useMemo(() => {
2179
3115
  const processedTools = [];
2180
3116
  const processedRenderToolCalls = [];
@@ -2205,15 +3141,31 @@ const CopilotKitProvider = ({ children, runtimeUrl, headers = {}, credentials, p
2205
3141
  renderToolCalls: processedRenderToolCalls
2206
3142
  };
2207
3143
  }, [humanInTheLoopList]);
3144
+ const builtInFrontendTools = useMemo(() => {
3145
+ if (!openGenUIActive) return [];
3146
+ return [{
3147
+ name: "generateSandboxedUi",
3148
+ description: GENERATE_SANDBOXED_UI_DESCRIPTION,
3149
+ parameters: GenerateSandboxedUiArgsSchema,
3150
+ handler: async () => "UI generated",
3151
+ followUp: true,
3152
+ render: OpenGenerativeUIToolRenderer
3153
+ }];
3154
+ }, [openGenUIActive]);
2208
3155
  const allTools = useMemo(() => {
2209
3156
  const tools = [];
2210
3157
  tools.push(...frontendToolsList);
3158
+ tools.push(...builtInFrontendTools);
2211
3159
  tools.push(...processedHumanInTheLoopTools.tools);
2212
3160
  return tools;
2213
- }, [frontendToolsList, processedHumanInTheLoopTools]);
3161
+ }, [
3162
+ frontendToolsList,
3163
+ builtInFrontendTools,
3164
+ processedHumanInTheLoopTools
3165
+ ]);
2214
3166
  const allRenderToolCalls = useMemo(() => {
2215
3167
  const combined = [...renderToolCallsList];
2216
- frontendToolsList.forEach((tool) => {
3168
+ [...frontendToolsList, ...builtInFrontendTools].forEach((tool) => {
2217
3169
  if (tool.render) {
2218
3170
  const args = tool.parameters || (tool.name === "*" ? z.any() : void 0);
2219
3171
  if (args) combined.push({
@@ -2228,25 +3180,31 @@ const CopilotKitProvider = ({ children, runtimeUrl, headers = {}, credentials, p
2228
3180
  }, [
2229
3181
  renderToolCallsList,
2230
3182
  frontendToolsList,
3183
+ builtInFrontendTools,
2231
3184
  processedHumanInTheLoopTools
2232
3185
  ]);
2233
3186
  const copilotkitRef = useRef(null);
2234
- if (copilotkitRef.current === null) copilotkitRef.current = new CopilotKitCoreReact({
2235
- runtimeUrl: chatApiEndpoint,
2236
- runtimeTransport: useSingleEndpoint ? "single" : "rest",
2237
- headers: mergedHeaders,
2238
- credentials,
2239
- properties,
2240
- agents__unsafe_dev_only: mergedAgents,
2241
- tools: allTools,
2242
- renderToolCalls: allRenderToolCalls,
2243
- renderActivityMessages: allActivityRenderers,
2244
- renderCustomMessages: renderCustomMessagesList
2245
- });
3187
+ if (copilotkitRef.current === null) {
3188
+ copilotkitRef.current = new CopilotKitCoreReact({
3189
+ runtimeUrl: chatApiEndpoint,
3190
+ runtimeTransport: useSingleEndpoint === true ? "single" : useSingleEndpoint === false ? "rest" : "auto",
3191
+ headers: mergedHeaders,
3192
+ credentials,
3193
+ properties,
3194
+ agents__unsafe_dev_only: mergedAgents,
3195
+ tools: allTools,
3196
+ renderToolCalls: allRenderToolCalls,
3197
+ renderActivityMessages: allActivityRenderers,
3198
+ renderCustomMessages: renderCustomMessagesList
3199
+ });
3200
+ if (defaultThrottleMs !== void 0) copilotkitRef.current.setDefaultThrottleMs(defaultThrottleMs);
3201
+ }
2246
3202
  const copilotkit = copilotkitRef.current;
2247
3203
  useEffect(() => {
3204
+ setRuntimeA2UIEnabled(copilotkit.a2uiEnabled);
2248
3205
  const subscription = copilotkit.subscribe({ onRuntimeConnectionStatusChanged: () => {
2249
3206
  setRuntimeA2UIEnabled(copilotkit.a2uiEnabled);
3207
+ setRuntimeOpenGenUIEnabled(copilotkit.openGenerativeUIEnabled);
2250
3208
  setRuntimeLicenseStatus(copilotkit.licenseStatus);
2251
3209
  } });
2252
3210
  return () => {
@@ -2305,7 +3263,7 @@ const CopilotKitProvider = ({ children, runtimeUrl, headers = {}, credentials, p
2305
3263
  }, [copilotkit]);
2306
3264
  useEffect(() => {
2307
3265
  copilotkit.setRuntimeUrl(chatApiEndpoint);
2308
- copilotkit.setRuntimeTransport(useSingleEndpoint ? "single" : "rest");
3266
+ copilotkit.setRuntimeTransport(useSingleEndpoint === true ? "single" : useSingleEndpoint === false ? "rest" : "auto");
2309
3267
  copilotkit.setHeaders(mergedHeaders);
2310
3268
  copilotkit.setCredentials(credentials);
2311
3269
  copilotkit.setProperties(properties);
@@ -2339,23 +3297,75 @@ const CopilotKitProvider = ({ children, runtimeUrl, headers = {}, credentials, p
2339
3297
  useEffect(() => {
2340
3298
  didMountRef.current = true;
2341
3299
  }, []);
3300
+ useEffect(() => {
3301
+ 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.`);
3302
+ copilotkit.setDefaultThrottleMs(defaultThrottleMs);
3303
+ }, [copilotkit, defaultThrottleMs]);
3304
+ const designSkill = openGenerativeUI?.designSkill ?? DEFAULT_DESIGN_SKILL;
3305
+ useLayoutEffect(() => {
3306
+ if (!copilotkit || !openGenUIActive) return;
3307
+ const id = copilotkit.addContext({
3308
+ description: "Design guidelines for the generateSandboxedUi tool. Follow these when building UI.",
3309
+ value: designSkill
3310
+ });
3311
+ return () => {
3312
+ copilotkit.removeContext(id);
3313
+ };
3314
+ }, [
3315
+ copilotkit,
3316
+ designSkill,
3317
+ openGenUIActive
3318
+ ]);
3319
+ const sandboxFunctionsDescriptors = useMemo(() => {
3320
+ if (sandboxFunctionsList.length === 0) return null;
3321
+ return JSON.stringify(sandboxFunctionsList.map((fn) => ({
3322
+ name: fn.name,
3323
+ description: fn.description,
3324
+ parameters: schemaToJsonSchema(fn.parameters, { zodToJsonSchema })
3325
+ })));
3326
+ }, [sandboxFunctionsList]);
3327
+ useLayoutEffect(() => {
3328
+ if (!copilotkit || !sandboxFunctionsDescriptors || !openGenUIActive) return;
3329
+ const id = copilotkit.addContext({
3330
+ description: "Sandbox functions available in generated sandboxed UI code. Call via: await Websandbox.connection.remote.<functionName>(args)",
3331
+ value: sandboxFunctionsDescriptors
3332
+ });
3333
+ return () => {
3334
+ copilotkit.removeContext(id);
3335
+ };
3336
+ }, [
3337
+ copilotkit,
3338
+ sandboxFunctionsDescriptors,
3339
+ openGenUIActive
3340
+ ]);
2342
3341
  const contextValue = useMemo(() => ({
2343
3342
  copilotkit,
2344
3343
  executingToolCallIds
2345
3344
  }), [copilotkit, executingToolCallIds]);
2346
3345
  const licenseContextValue = useMemo(() => createLicenseContextValue(null), []);
2347
- return /* @__PURE__ */ jsx(CopilotKitContext.Provider, {
2348
- value: contextValue,
2349
- children: /* @__PURE__ */ jsxs(LicenseContext.Provider, {
2350
- value: licenseContextValue,
2351
- children: [
2352
- children,
2353
- shouldRenderInspector ? /* @__PURE__ */ jsx(CopilotKitInspector, { core: copilotkit }) : null,
2354
- runtimeLicenseStatus === "none" && !resolvedPublicKey && /* @__PURE__ */ jsx(LicenseWarningBanner, { type: "no_license" }),
2355
- runtimeLicenseStatus === "expired" && /* @__PURE__ */ jsx(LicenseWarningBanner, { type: "expired" }),
2356
- runtimeLicenseStatus === "invalid" && /* @__PURE__ */ jsx(LicenseWarningBanner, { type: "invalid" }),
2357
- runtimeLicenseStatus === "expiring" && /* @__PURE__ */ jsx(LicenseWarningBanner, { type: "expiring" })
2358
- ]
3346
+ return /* @__PURE__ */ jsx(SandboxFunctionsContext.Provider, {
3347
+ value: sandboxFunctionsList,
3348
+ children: /* @__PURE__ */ jsx(CopilotKitContext.Provider, {
3349
+ value: contextValue,
3350
+ children: /* @__PURE__ */ jsxs(LicenseContext.Provider, {
3351
+ value: licenseContextValue,
3352
+ children: [
3353
+ runtimeA2UIEnabled && /* @__PURE__ */ jsx(A2UIBuiltInToolCallRenderer, {}),
3354
+ runtimeA2UIEnabled && /* @__PURE__ */ jsx(A2UICatalogContext, {
3355
+ catalog: a2ui?.catalog,
3356
+ includeSchema: a2ui?.includeSchema
3357
+ }),
3358
+ children,
3359
+ shouldRenderInspector ? /* @__PURE__ */ jsx(CopilotKitInspector, {
3360
+ core: copilotkit,
3361
+ defaultAnchor: inspectorDefaultAnchor
3362
+ }) : null,
3363
+ runtimeLicenseStatus === "none" && !resolvedPublicKey && /* @__PURE__ */ jsx(LicenseWarningBanner, { type: "no_license" }),
3364
+ runtimeLicenseStatus === "expired" && /* @__PURE__ */ jsx(LicenseWarningBanner, { type: "expired" }),
3365
+ runtimeLicenseStatus === "invalid" && /* @__PURE__ */ jsx(LicenseWarningBanner, { type: "invalid" }),
3366
+ runtimeLicenseStatus === "expiring" && /* @__PURE__ */ jsx(LicenseWarningBanner, { type: "expiring" })
3367
+ ]
3368
+ })
2359
3369
  })
2360
3370
  });
2361
3371
  };
@@ -2496,11 +3506,21 @@ function getOrCreateThreadClone(existing, threadId, headers) {
2496
3506
  byThread.set(threadId, clone);
2497
3507
  return clone;
2498
3508
  }
2499
- function useAgent({ agentId, threadId, updates } = {}) {
3509
+ function useAgent({ agentId, threadId, updates, throttleMs } = {}) {
2500
3510
  agentId ??= DEFAULT_AGENT_ID;
2501
3511
  const { copilotkit } = useCopilotKit();
3512
+ const providerThrottleMs = copilotkit.defaultThrottleMs;
2502
3513
  const chatConfig = useCopilotChatConfiguration();
2503
3514
  threadId ??= chatConfig?.threadId;
3515
+ const effectiveThrottleMs = useMemo(() => {
3516
+ const resolved = throttleMs ?? providerThrottleMs ?? 0;
3517
+ if (!Number.isFinite(resolved) || resolved < 0) {
3518
+ const source = throttleMs !== void 0 ? "hook-level throttleMs" : "provider-level defaultThrottleMs";
3519
+ console.error(`useAgent: ${source} must be a non-negative finite number, got ${resolved}. Falling back to unthrottled.`);
3520
+ return 0;
3521
+ }
3522
+ return resolved;
3523
+ }, [throttleMs, providerThrottleMs]);
2504
3524
  const [, forceUpdate] = useReducer((x) => x + 1, 0);
2505
3525
  const updateFlags = useMemo(() => updates ?? ALL_UPDATES, [JSON.stringify(updates)]);
2506
3526
  const provisionalAgentCache = useRef(/* @__PURE__ */ new Map());
@@ -2564,9 +3584,32 @@ function useAgent({ agentId, threadId, updates } = {}) {
2564
3584
  useEffect(() => {
2565
3585
  if (updateFlags.length === 0) return;
2566
3586
  const handlers = {};
2567
- if (updateFlags.includes(UseAgentUpdate.OnMessagesChanged)) handlers.onMessagesChanged = () => {
2568
- forceUpdate();
2569
- };
3587
+ let timerId = null;
3588
+ let active = true;
3589
+ if (updateFlags.includes(UseAgentUpdate.OnMessagesChanged)) {
3590
+ const ms = effectiveThrottleMs;
3591
+ if (ms > 0) {
3592
+ let throttleActive = false;
3593
+ let pending = false;
3594
+ const throttledNotify = () => {
3595
+ if (!active) return;
3596
+ if (!throttleActive) {
3597
+ throttleActive = true;
3598
+ pending = false;
3599
+ forceUpdate();
3600
+ timerId = setTimeout(function trailingEdge() {
3601
+ timerId = null;
3602
+ if (active && pending) {
3603
+ pending = false;
3604
+ forceUpdate();
3605
+ timerId = setTimeout(trailingEdge, ms);
3606
+ } else throttleActive = false;
3607
+ }, ms);
3608
+ } else pending = true;
3609
+ };
3610
+ handlers.onMessagesChanged = throttledNotify;
3611
+ } else handlers.onMessagesChanged = forceUpdate;
3612
+ }
2570
3613
  if (updateFlags.includes(UseAgentUpdate.OnStateChanged)) handlers.onStateChanged = forceUpdate;
2571
3614
  if (updateFlags.includes(UseAgentUpdate.OnRunStatusChanged)) {
2572
3615
  handlers.onRunInitialized = forceUpdate;
@@ -2574,11 +3617,16 @@ function useAgent({ agentId, threadId, updates } = {}) {
2574
3617
  handlers.onRunFailed = forceUpdate;
2575
3618
  }
2576
3619
  const subscription = agent.subscribe(handlers);
2577
- return () => subscription.unsubscribe();
3620
+ return () => {
3621
+ active = false;
3622
+ if (timerId !== null) clearTimeout(timerId);
3623
+ subscription.unsubscribe();
3624
+ };
2578
3625
  }, [
2579
3626
  agent,
2580
3627
  forceUpdate,
2581
- JSON.stringify(updateFlags)
3628
+ effectiveThrottleMs,
3629
+ updateFlags
2582
3630
  ]);
2583
3631
  useEffect(() => {
2584
3632
  if (agent instanceof HttpAgent) agent.headers = { ...copilotkit.headers };
@@ -2636,7 +3684,8 @@ function useRenderCustomMessages() {
2636
3684
  //#region src/v2/hooks/use-render-activity-message.tsx
2637
3685
  function useRenderActivityMessage() {
2638
3686
  const { copilotkit } = useCopilotKit();
2639
- const agentId = useCopilotChatConfiguration()?.agentId ?? DEFAULT_AGENT_ID;
3687
+ const config = useCopilotChatConfiguration();
3688
+ const agentId = config?.agentId ?? DEFAULT_AGENT_ID;
2640
3689
  const renderers = copilotkit.renderActivityMessages;
2641
3690
  const findRenderer = useCallback((activityType) => {
2642
3691
  if (!renderers.length) return null;
@@ -2652,7 +3701,8 @@ function useRenderActivityMessage() {
2652
3701
  return null;
2653
3702
  }
2654
3703
  const Component = renderer.render;
2655
- const agent = copilotkit.getAgent(agentId);
3704
+ const registryAgent = copilotkit.getAgent(agentId);
3705
+ const agent = getThreadClone(registryAgent, config?.threadId) ?? registryAgent;
2656
3706
  return /* @__PURE__ */ jsx(Component, {
2657
3707
  activityType: message.activityType,
2658
3708
  content: parseResult.data,
@@ -2661,6 +3711,7 @@ function useRenderActivityMessage() {
2661
3711
  }, message.id);
2662
3712
  }, [
2663
3713
  agentId,
3714
+ config?.threadId,
2664
3715
  copilotkit,
2665
3716
  findRenderer
2666
3717
  ]);
@@ -2686,7 +3737,7 @@ function useFrontendTool(tool, deps) {
2686
3737
  copilotkit.removeTool(name, tool.agentId);
2687
3738
  }
2688
3739
  copilotkit.addTool(tool);
2689
- if (tool.render && tool.parameters) copilotkit.addHookRenderToolCall({
3740
+ if (tool.render) copilotkit.addHookRenderToolCall({
2690
3741
  name,
2691
3742
  args: tool.parameters,
2692
3743
  agentId: tool.agentId,
@@ -2771,18 +3822,6 @@ function useComponent(config, deps) {
2771
3822
  }, deps);
2772
3823
  }
2773
3824
 
2774
- //#endregion
2775
- //#region src/v2/types/defineToolCallRenderer.ts
2776
- function defineToolCallRenderer(def) {
2777
- const argsSchema = def.name === "*" && !def.args ? z.any() : def.args;
2778
- return {
2779
- name: def.name,
2780
- args: argsSchema,
2781
- render: def.render,
2782
- ...def.agentId ? { agentId: def.agentId } : {}
2783
- };
2784
- }
2785
-
2786
3825
  //#endregion
2787
3826
  //#region src/v2/hooks/use-render-tool.tsx
2788
3827
  const EMPTY_DEPS = [];
@@ -3112,31 +4151,6 @@ function useHumanInTheLoop(tool, deps) {
3112
4151
  ]);
3113
4152
  }
3114
4153
 
3115
- //#endregion
3116
- //#region src/v2/hooks/use-agent-context.tsx
3117
- function useAgentContext(context) {
3118
- const { description, value } = context;
3119
- const { copilotkit } = useCopilotKit();
3120
- const stringValue = useMemo(() => {
3121
- if (typeof value === "string") return value;
3122
- return JSON.stringify(value);
3123
- }, [value]);
3124
- useLayoutEffect(() => {
3125
- if (!copilotkit) return;
3126
- const id = copilotkit.addContext({
3127
- description,
3128
- value: stringValue
3129
- });
3130
- return () => {
3131
- copilotkit.removeContext(id);
3132
- };
3133
- }, [
3134
- description,
3135
- stringValue,
3136
- copilotkit
3137
- ]);
3138
- }
3139
-
3140
4154
  //#endregion
3141
4155
  //#region src/v2/hooks/use-suggestions.tsx
3142
4156
  function useSuggestions({ agentId } = {}) {
@@ -3546,11 +4560,19 @@ function useThreadStoreSelector(store, selector) {
3546
4560
  function useThreads$1({ agentId, includeArchived, limit }) {
3547
4561
  const { copilotkit } = useCopilotKit();
3548
4562
  const [store] = useState(() => ɵcreateThreadStore({ fetch: globalThis.fetch }));
3549
- const threads = useThreadStoreSelector(store, ɵselectThreads);
4563
+ const coreThreads = useThreadStoreSelector(store, ɵselectThreads);
4564
+ const threads = useMemo(() => coreThreads.map(({ id, agentId, name, archived, createdAt, updatedAt }) => ({
4565
+ id,
4566
+ agentId,
4567
+ name,
4568
+ archived,
4569
+ createdAt,
4570
+ updatedAt
4571
+ })), [coreThreads]);
3550
4572
  const storeIsLoading = useThreadStoreSelector(store, ɵselectThreadsIsLoading);
3551
4573
  const storeError = useThreadStoreSelector(store, ɵselectThreadsError);
3552
- const hasNextPage = useThreadStoreSelector(store, ɵselectHasNextPage);
3553
- const isFetchingNextPage = useThreadStoreSelector(store, ɵselectIsFetchingNextPage);
4574
+ const hasMoreThreads = useThreadStoreSelector(store, ɵselectHasNextPage);
4575
+ const isFetchingMoreThreads = useThreadStoreSelector(store, ɵselectIsFetchingNextPage);
3554
4576
  const headersKey = useMemo(() => {
3555
4577
  return JSON.stringify(Object.entries(copilotkit.headers ?? {}).sort(([left], [right]) => left.localeCompare(right)));
3556
4578
  }, [copilotkit.headers]);
@@ -3593,15 +4615,173 @@ function useThreads$1({ agentId, includeArchived, limit }) {
3593
4615
  threads,
3594
4616
  isLoading,
3595
4617
  error,
3596
- hasNextPage,
3597
- isFetchingNextPage,
3598
- fetchNextPage: useCallback(() => store.fetchNextPage(), [store]),
4618
+ hasMoreThreads,
4619
+ isFetchingMoreThreads,
4620
+ fetchMoreThreads: useCallback(() => store.fetchNextPage(), [store]),
3599
4621
  renameThread,
3600
4622
  archiveThread,
3601
4623
  deleteThread
3602
4624
  };
3603
4625
  }
3604
4626
 
4627
+ //#endregion
4628
+ //#region src/v2/hooks/use-attachments.tsx
4629
+ /**
4630
+ * Hook that manages file attachment state — uploads, drag-and-drop, paste,
4631
+ * and lifecycle. All returned callbacks are referentially stable across
4632
+ * renders (via useCallback) to avoid destabilizing downstream memoization.
4633
+ */
4634
+ function useAttachments({ config }) {
4635
+ const enabled = config?.enabled ?? false;
4636
+ const [attachments, setAttachments] = useState([]);
4637
+ const [dragOver, setDragOver] = useState(false);
4638
+ const fileInputRef = useRef(null);
4639
+ const containerRef = useRef(null);
4640
+ const configRef = useRef(config);
4641
+ configRef.current = config;
4642
+ const attachmentsRef = useRef([]);
4643
+ attachmentsRef.current = attachments;
4644
+ const processFiles = useCallback(async (files) => {
4645
+ const cfg = configRef.current;
4646
+ const accept = cfg?.accept ?? "*/*";
4647
+ const maxSize = cfg?.maxSize ?? 20 * 1024 * 1024;
4648
+ const rejectedFiles = files.filter((file) => !matchesAcceptFilter(file, accept));
4649
+ for (const file of rejectedFiles) cfg?.onUploadFailed?.({
4650
+ reason: "invalid-type",
4651
+ file,
4652
+ message: `File "${file.name}" is not accepted. Supported types: ${accept}`
4653
+ });
4654
+ const validFiles = files.filter((file) => matchesAcceptFilter(file, accept));
4655
+ for (const file of validFiles) {
4656
+ if (exceedsMaxSize(file, maxSize)) {
4657
+ cfg?.onUploadFailed?.({
4658
+ reason: "file-too-large",
4659
+ file,
4660
+ message: `File "${file.name}" exceeds the maximum size of ${formatFileSize(maxSize)}`
4661
+ });
4662
+ continue;
4663
+ }
4664
+ const modality = getModalityFromMimeType(file.type);
4665
+ const placeholderId = randomUUID();
4666
+ const placeholder = {
4667
+ id: placeholderId,
4668
+ type: modality,
4669
+ source: {
4670
+ type: "data",
4671
+ value: "",
4672
+ mimeType: file.type
4673
+ },
4674
+ filename: file.name,
4675
+ size: file.size,
4676
+ status: "uploading"
4677
+ };
4678
+ setAttachments((prev) => [...prev, placeholder]);
4679
+ try {
4680
+ let source;
4681
+ let uploadMetadata;
4682
+ if (cfg?.onUpload) {
4683
+ const { metadata: meta, ...uploadSource } = await cfg.onUpload(file);
4684
+ source = uploadSource;
4685
+ uploadMetadata = meta;
4686
+ } else source = {
4687
+ type: "data",
4688
+ value: await readFileAsBase64(file),
4689
+ mimeType: file.type
4690
+ };
4691
+ let thumbnail;
4692
+ if (modality === "video") thumbnail = await generateVideoThumbnail(file);
4693
+ setAttachments((prev) => prev.map((att) => att.id === placeholderId ? {
4694
+ ...att,
4695
+ source,
4696
+ status: "ready",
4697
+ thumbnail,
4698
+ metadata: uploadMetadata
4699
+ } : att));
4700
+ } catch (error) {
4701
+ setAttachments((prev) => prev.filter((att) => att.id !== placeholderId));
4702
+ console.error(`[CopilotKit] Failed to upload "${file.name}":`, error);
4703
+ cfg?.onUploadFailed?.({
4704
+ reason: "upload-failed",
4705
+ file,
4706
+ message: error instanceof Error ? error.message : `Failed to upload "${file.name}"`
4707
+ });
4708
+ }
4709
+ }
4710
+ }, []);
4711
+ const handleFileUpload = useCallback(async (e) => {
4712
+ if (!e.target.files?.length) return;
4713
+ try {
4714
+ await processFiles(Array.from(e.target.files));
4715
+ } catch (error) {
4716
+ console.error("[CopilotKit] Upload error:", error);
4717
+ }
4718
+ }, [processFiles]);
4719
+ const handleDragOver = useCallback((e) => {
4720
+ if (!configRef.current?.enabled) return;
4721
+ e.preventDefault();
4722
+ e.stopPropagation();
4723
+ setDragOver(true);
4724
+ }, []);
4725
+ const handleDragLeave = useCallback((e) => {
4726
+ e.preventDefault();
4727
+ e.stopPropagation();
4728
+ setDragOver(false);
4729
+ }, []);
4730
+ const handleDrop = useCallback(async (e) => {
4731
+ e.preventDefault();
4732
+ e.stopPropagation();
4733
+ setDragOver(false);
4734
+ if (!configRef.current?.enabled) return;
4735
+ const files = Array.from(e.dataTransfer.files);
4736
+ if (files.length > 0) try {
4737
+ await processFiles(files);
4738
+ } catch (error) {
4739
+ console.error("[CopilotKit] Drop error:", error);
4740
+ }
4741
+ }, [processFiles]);
4742
+ useEffect(() => {
4743
+ if (!enabled) return;
4744
+ const handlePaste = async (e) => {
4745
+ const target = e.target;
4746
+ if (!target || !containerRef.current?.contains(target)) return;
4747
+ const accept = configRef.current?.accept ?? "*/*";
4748
+ const fileItems = Array.from(e.clipboardData?.items || []).filter((item) => item.kind === "file" && item.getAsFile() !== null && matchesAcceptFilter(item.getAsFile(), accept));
4749
+ if (fileItems.length === 0) return;
4750
+ e.preventDefault();
4751
+ const files = fileItems.map((item) => item.getAsFile()).filter((f) => f !== null);
4752
+ try {
4753
+ await processFiles(files);
4754
+ } catch (error) {
4755
+ console.error("[CopilotKit] Paste error:", error);
4756
+ }
4757
+ };
4758
+ document.addEventListener("paste", handlePaste);
4759
+ return () => document.removeEventListener("paste", handlePaste);
4760
+ }, [enabled, processFiles]);
4761
+ return {
4762
+ attachments,
4763
+ enabled,
4764
+ dragOver,
4765
+ fileInputRef,
4766
+ containerRef,
4767
+ processFiles,
4768
+ handleFileUpload,
4769
+ handleDragOver,
4770
+ handleDragLeave,
4771
+ handleDrop,
4772
+ removeAttachment: useCallback((id) => {
4773
+ setAttachments((prev) => prev.filter((a) => a.id !== id));
4774
+ }, []),
4775
+ consumeAttachments: useCallback(() => {
4776
+ const ready = attachmentsRef.current.filter((a) => a.status === "ready");
4777
+ if (ready.length === 0) return ready;
4778
+ setAttachments((prev) => prev.filter((a) => a.status !== "ready"));
4779
+ if (fileInputRef.current) fileInputRef.current.value = "";
4780
+ return ready;
4781
+ }, [])
4782
+ };
4783
+ }
4784
+
3605
4785
  //#endregion
3606
4786
  //#region src/v2/components/chat/CopilotChatToolCallsView.tsx
3607
4787
  function CopilotChatToolCallsView({ message, messages = [] }) {
@@ -3718,9 +4898,19 @@ function CopilotChatAssistantMessage({ message, messages, isRunning, onThumbsUp,
3718
4898
  _CopilotChatAssistantMessage.CopyButton = ({ className, title, onClick, ...props }) => {
3719
4899
  const labels = useCopilotChatConfiguration()?.labels ?? CopilotChatDefaultLabels;
3720
4900
  const [copied, setCopied] = useState(false);
4901
+ const timerRef = useRef(null);
4902
+ useEffect(() => {
4903
+ return () => {
4904
+ if (timerRef.current !== null) clearTimeout(timerRef.current);
4905
+ };
4906
+ }, []);
3721
4907
  const handleClick = (event) => {
3722
4908
  setCopied(true);
3723
- setTimeout(() => setCopied(false), 2e3);
4909
+ if (timerRef.current !== null) clearTimeout(timerRef.current);
4910
+ timerRef.current = setTimeout(() => {
4911
+ timerRef.current = null;
4912
+ setCopied(false);
4913
+ }, 2e3);
3724
4914
  if (onClick) onClick(event);
3725
4915
  };
3726
4916
  return /* @__PURE__ */ jsx(ToolbarButton, {
@@ -3778,6 +4968,79 @@ CopilotChatAssistantMessage.ReadAloudButton.displayName = "CopilotChatAssistantM
3778
4968
  CopilotChatAssistantMessage.RegenerateButton.displayName = "CopilotChatAssistantMessage.RegenerateButton";
3779
4969
  var CopilotChatAssistantMessage_default = CopilotChatAssistantMessage;
3780
4970
 
4971
+ //#endregion
4972
+ //#region src/v2/components/chat/CopilotChatAttachmentRenderer.tsx
4973
+ const ImageAttachment = memo(function ImageAttachment({ src, className }) {
4974
+ const [error, setError] = useState(false);
4975
+ if (error) return /* @__PURE__ */ jsx("div", {
4976
+ 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),
4977
+ children: /* @__PURE__ */ jsx("span", { children: "Failed to load image" })
4978
+ });
4979
+ return /* @__PURE__ */ jsx("img", {
4980
+ src,
4981
+ alt: "Image attachment",
4982
+ className: cn("cpk:max-w-full cpk:h-auto cpk:rounded-lg", className),
4983
+ onError: () => setError(true)
4984
+ });
4985
+ });
4986
+ const AudioAttachment = memo(function AudioAttachment({ src, filename, className }) {
4987
+ return /* @__PURE__ */ jsxs("div", {
4988
+ className: cn("cpk:flex cpk:flex-col cpk:gap-1", className),
4989
+ children: [/* @__PURE__ */ jsx("audio", {
4990
+ src,
4991
+ controls: true,
4992
+ preload: "metadata",
4993
+ className: "cpk:max-w-[300px] cpk:w-full cpk:h-10"
4994
+ }), filename && /* @__PURE__ */ jsx("span", {
4995
+ className: "cpk:text-xs cpk:text-muted-foreground cpk:truncate cpk:max-w-[300px]",
4996
+ children: filename
4997
+ })]
4998
+ });
4999
+ });
5000
+ const VideoAttachment = memo(function VideoAttachment({ src, className }) {
5001
+ return /* @__PURE__ */ jsx("video", {
5002
+ src,
5003
+ controls: true,
5004
+ preload: "metadata",
5005
+ className: cn("cpk:max-w-[400px] cpk:w-full cpk:rounded-lg", className)
5006
+ });
5007
+ });
5008
+ const DocumentAttachment = memo(function DocumentAttachment({ source, filename, className }) {
5009
+ return /* @__PURE__ */ jsxs("div", {
5010
+ 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),
5011
+ children: [/* @__PURE__ */ jsx("span", {
5012
+ className: "cpk:text-xs cpk:font-bold cpk:uppercase",
5013
+ children: getDocumentIcon(source.mimeType ?? "")
5014
+ }), /* @__PURE__ */ jsx("span", {
5015
+ className: "cpk:text-sm cpk:text-muted-foreground cpk:truncate",
5016
+ children: filename || source.mimeType || "Unknown type"
5017
+ })]
5018
+ });
5019
+ });
5020
+ const CopilotChatAttachmentRenderer = ({ type, source, filename, className }) => {
5021
+ const src = getSourceUrl(source);
5022
+ switch (type) {
5023
+ case "image": return /* @__PURE__ */ jsx(ImageAttachment, {
5024
+ src,
5025
+ className
5026
+ });
5027
+ case "audio": return /* @__PURE__ */ jsx(AudioAttachment, {
5028
+ src,
5029
+ filename,
5030
+ className
5031
+ });
5032
+ case "video": return /* @__PURE__ */ jsx(VideoAttachment, {
5033
+ src,
5034
+ className
5035
+ });
5036
+ case "document": return /* @__PURE__ */ jsx(DocumentAttachment, {
5037
+ source,
5038
+ filename,
5039
+ className
5040
+ });
5041
+ }
5042
+ };
5043
+
3781
5044
  //#endregion
3782
5045
  //#region src/v2/components/chat/CopilotChatUserMessage.tsx
3783
5046
  function flattenUserMessageContent(content) {
@@ -3788,8 +5051,17 @@ function flattenUserMessageContent(content) {
3788
5051
  return "";
3789
5052
  }).filter((text) => text.length > 0).join("\n");
3790
5053
  }
5054
+ function getMediaParts(content) {
5055
+ if (!content || typeof content === "string") return [];
5056
+ return content.filter((part) => part.type === "image" || part.type === "audio" || part.type === "video" || part.type === "document");
5057
+ }
5058
+ function getFilename(part) {
5059
+ const meta = part.metadata;
5060
+ if (meta != null && typeof meta === "object" && "filename" in meta && typeof meta.filename === "string") return meta.filename;
5061
+ }
3791
5062
  function CopilotChatUserMessage({ message, onEditMessage, branchIndex, numberOfBranches, onSwitchToBranch, additionalToolbarItems, messageRenderer, toolbar, copyButton, editButton, branchNavigation, children, className, ...props }) {
3792
5063
  const flattenedContent = useMemo(() => flattenUserMessageContent(message.content), [message.content]);
5064
+ const mediaParts = useMemo(() => getMediaParts(message.content), [message.content]);
3793
5065
  const BoundMessageRenderer = renderSlot(messageRenderer, CopilotChatUserMessage.MessageRenderer, { content: flattenedContent });
3794
5066
  const BoundCopyButton = renderSlot(copyButton, CopilotChatUserMessage.CopyButton, { onClick: async () => {
3795
5067
  if (flattenedContent) try {
@@ -3836,7 +5108,18 @@ function CopilotChatUserMessage({ message, onEditMessage, branchIndex, numberOfB
3836
5108
  className: twMerge("copilotKitMessage copilotKitUserMessage cpk:flex cpk:flex-col cpk:items-end cpk:group cpk:pt-10", className),
3837
5109
  "data-message-id": message.id,
3838
5110
  ...props,
3839
- children: [BoundMessageRenderer, BoundToolbar]
5111
+ children: [
5112
+ BoundMessageRenderer,
5113
+ mediaParts.length > 0 && /* @__PURE__ */ jsx("div", {
5114
+ className: "cpk:flex cpk:flex-col cpk:items-end cpk:gap-2 cpk:mt-2",
5115
+ children: mediaParts.map((part, index) => /* @__PURE__ */ jsx(CopilotChatAttachmentRenderer, {
5116
+ type: part.type,
5117
+ source: part.source,
5118
+ filename: getFilename(part)
5119
+ }, index))
5120
+ }),
5121
+ BoundToolbar
5122
+ ]
3840
5123
  });
3841
5124
  }
3842
5125
  (function(_CopilotChatUserMessage) {
@@ -4134,6 +5417,7 @@ const CopilotChatSuggestionView = React.forwardRef(function CopilotChatSuggestio
4134
5417
  const isLoading = loadingSet.has(index) || suggestion.isLoading === true;
4135
5418
  const pill = renderSlot(suggestionSlot, CopilotChatSuggestionPill, {
4136
5419
  children: suggestion.title,
5420
+ className: suggestion.className,
4137
5421
  isLoading,
4138
5422
  type: "button",
4139
5423
  onClick: () => onSelectSuggestion?.(suggestion, index)
@@ -4170,9 +5454,43 @@ const CopilotChatSuggestionView = React.forwardRef(function CopilotChatSuggestio
4170
5454
  });
4171
5455
  CopilotChatSuggestionView.displayName = "CopilotChatSuggestionView";
4172
5456
 
5457
+ //#endregion
5458
+ //#region src/v2/components/chat/scroll-element-context.ts
5459
+ /**
5460
+ * Provides the scroll container element to child components that need it for
5461
+ * virtualization. Set by CopilotChatView.ScrollView; consumed by
5462
+ * CopilotChatMessageView to feed useVirtualizer's getScrollElement.
5463
+ *
5464
+ * Carries the element itself (not a ref) so that context consumers re-render
5465
+ * reactively when the scroll container is first mounted.
5466
+ */
5467
+ const ScrollElementContext = React.createContext(null);
5468
+
4173
5469
  //#endregion
4174
5470
  //#region src/v2/components/chat/CopilotChatMessageView.tsx
4175
5471
  /**
5472
+ * Resolves a slot value into a { Component, slotProps } pair, handling the three
5473
+ * slot forms: a component type, a className string, or a partial-props object.
5474
+ */
5475
+ function resolveSlotComponent(slot, DefaultComponent) {
5476
+ if (isReactComponentType(slot)) return {
5477
+ Component: slot,
5478
+ slotProps: void 0
5479
+ };
5480
+ if (typeof slot === "string") return {
5481
+ Component: DefaultComponent,
5482
+ slotProps: { className: slot }
5483
+ };
5484
+ if (slot && typeof slot === "object") return {
5485
+ Component: DefaultComponent,
5486
+ slotProps: slot
5487
+ };
5488
+ return {
5489
+ Component: DefaultComponent,
5490
+ slotProps: void 0
5491
+ };
5492
+ }
5493
+ /**
4176
5494
  * Memoized wrapper for assistant messages to prevent re-renders when other messages change.
4177
5495
  */
4178
5496
  const MemoizedAssistantMessage = React.memo(function MemoizedAssistantMessage({ message, messages, isRunning, AssistantMessageComponent, slotProps }) {
@@ -4191,7 +5509,6 @@ const MemoizedAssistantMessage = React.memo(function MemoizedAssistantMessage({
4191
5509
  if (prevToolCalls && nextToolCalls) for (let i = 0; i < prevToolCalls.length; i++) {
4192
5510
  const prevTc = prevToolCalls[i];
4193
5511
  const nextTc = nextToolCalls[i];
4194
- if (!prevTc || !nextTc) return false;
4195
5512
  if (prevTc.id !== nextTc.id) return false;
4196
5513
  if (prevTc.function.arguments !== nextTc.function.arguments) return false;
4197
5514
  }
@@ -4270,6 +5587,7 @@ const MemoizedCustomMessage = React.memo(function MemoizedCustomMessage({ messag
4270
5587
  if (JSON.stringify(prevProps.stateSnapshot) !== JSON.stringify(nextProps.stateSnapshot)) return false;
4271
5588
  return true;
4272
5589
  });
5590
+ const VIRTUALIZE_THRESHOLD = 50;
4273
5591
  function CopilotChatMessageView({ messages = [], assistantMessage, userMessage, reasoningMessage, cursor, isRunning = false, children, className, ...props }) {
4274
5592
  const renderCustomMessage = useRenderCustomMessages();
4275
5593
  const { renderActivityMessage } = useRenderActivityMessage();
@@ -4303,9 +5621,34 @@ function CopilotChatMessageView({ messages = [], assistantMessage, userMessage,
4303
5621
  if (!resolvedRunId) return void 0;
4304
5622
  return copilotkit.getStateByRun(config.agentId, config.threadId, resolvedRunId);
4305
5623
  };
4306
- const deduplicatedMessages = [...new Map(messages.map((m) => [m.id, m])).values()];
5624
+ const deduplicatedMessages = useMemo(() => [...new Map(messages.map((m) => [m.id, m])).values()], [messages]);
4307
5625
  if (process.env.NODE_ENV === "development" && deduplicatedMessages.length < messages.length) console.warn(`CopilotChatMessageView: Deduplicated ${messages.length - deduplicatedMessages.length} message(s) with duplicate IDs.`);
4308
- const messageElements = deduplicatedMessages.flatMap((message) => {
5626
+ const { Component: AssistantComponent, slotProps: assistantSlotProps } = useMemo(() => resolveSlotComponent(assistantMessage, CopilotChatAssistantMessage_default), [assistantMessage]);
5627
+ const { Component: UserComponent, slotProps: userSlotProps } = useMemo(() => resolveSlotComponent(userMessage, CopilotChatUserMessage_default), [userMessage]);
5628
+ const { Component: ReasoningComponent, slotProps: reasoningSlotProps } = useMemo(() => resolveSlotComponent(reasoningMessage, CopilotChatReasoningMessage_default), [reasoningMessage]);
5629
+ const scrollElementFromCtx = useContext(ScrollElementContext);
5630
+ const scrollElement = scrollElementFromCtx && scrollElementFromCtx.clientHeight > 0 ? scrollElementFromCtx : null;
5631
+ useEffect(() => {
5632
+ 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.");
5633
+ }, [scrollElementFromCtx]);
5634
+ const shouldVirtualize = !!scrollElement && !children && deduplicatedMessages.length > VIRTUALIZE_THRESHOLD;
5635
+ const virtualizer = useVirtualizer({
5636
+ count: shouldVirtualize ? deduplicatedMessages.length : 0,
5637
+ getScrollElement: () => scrollElement,
5638
+ estimateSize: () => 100,
5639
+ overscan: 5,
5640
+ measureElement: (el) => el?.getBoundingClientRect().height ?? 0,
5641
+ initialRect: {
5642
+ width: 0,
5643
+ height: 600
5644
+ }
5645
+ });
5646
+ const firstMessageId = deduplicatedMessages[0]?.id;
5647
+ useLayoutEffect(() => {
5648
+ if (!shouldVirtualize || !deduplicatedMessages.length) return;
5649
+ virtualizer.scrollToIndex(deduplicatedMessages.length - 1, { align: "end" });
5650
+ }, [shouldVirtualize, firstMessageId]);
5651
+ const renderMessageBlock = (message) => {
4309
5652
  const elements = [];
4310
5653
  const stateSnapshot = getStateSnapshotForMessage(message.id);
4311
5654
  if (renderCustomMessage) elements.push(/* @__PURE__ */ jsx(MemoizedCustomMessage, {
@@ -4314,58 +5657,38 @@ function CopilotChatMessageView({ messages = [], assistantMessage, userMessage,
4314
5657
  renderCustomMessage,
4315
5658
  stateSnapshot
4316
5659
  }, `${message.id}-custom-before`));
4317
- if (message.role === "assistant") {
4318
- let AssistantComponent = CopilotChatAssistantMessage_default;
4319
- let assistantSlotProps;
4320
- if (isReactComponentType(assistantMessage)) AssistantComponent = assistantMessage;
4321
- else if (typeof assistantMessage === "string") assistantSlotProps = { className: assistantMessage };
4322
- else if (assistantMessage && typeof assistantMessage === "object") assistantSlotProps = assistantMessage;
4323
- elements.push(/* @__PURE__ */ jsx(MemoizedAssistantMessage, {
4324
- message,
4325
- messages,
4326
- isRunning,
4327
- AssistantMessageComponent: AssistantComponent,
4328
- slotProps: assistantSlotProps
4329
- }, message.id));
4330
- } else if (message.role === "user") {
4331
- let UserComponent = CopilotChatUserMessage_default;
4332
- let userSlotProps;
4333
- if (isReactComponentType(userMessage)) UserComponent = userMessage;
4334
- else if (typeof userMessage === "string") userSlotProps = { className: userMessage };
4335
- else if (userMessage && typeof userMessage === "object") userSlotProps = userMessage;
4336
- elements.push(/* @__PURE__ */ jsx(MemoizedUserMessage, {
4337
- message,
4338
- UserMessageComponent: UserComponent,
4339
- slotProps: userSlotProps
4340
- }, message.id));
4341
- } else if (message.role === "activity") {
4342
- const activityMsg = message;
4343
- elements.push(/* @__PURE__ */ jsx(MemoizedActivityMessage, {
4344
- message: activityMsg,
4345
- renderActivityMessage
4346
- }, message.id));
4347
- } else if (message.role === "reasoning") {
4348
- let ReasoningComponent = CopilotChatReasoningMessage_default;
4349
- let reasoningSlotProps;
4350
- if (isReactComponentType(reasoningMessage)) ReasoningComponent = reasoningMessage;
4351
- else if (typeof reasoningMessage === "string") reasoningSlotProps = { className: reasoningMessage };
4352
- else if (reasoningMessage && typeof reasoningMessage === "object") reasoningSlotProps = reasoningMessage;
4353
- elements.push(/* @__PURE__ */ jsx(MemoizedReasoningMessage, {
4354
- message,
4355
- messages,
4356
- isRunning,
4357
- ReasoningMessageComponent: ReasoningComponent,
4358
- slotProps: reasoningSlotProps
4359
- }, message.id));
4360
- }
5660
+ if (message.role === "assistant") elements.push(/* @__PURE__ */ jsx(MemoizedAssistantMessage, {
5661
+ message,
5662
+ messages,
5663
+ isRunning,
5664
+ AssistantMessageComponent: AssistantComponent,
5665
+ slotProps: assistantSlotProps
5666
+ }, message.id));
5667
+ else if (message.role === "user") elements.push(/* @__PURE__ */ jsx(MemoizedUserMessage, {
5668
+ message,
5669
+ UserMessageComponent: UserComponent,
5670
+ slotProps: userSlotProps
5671
+ }, message.id));
5672
+ else if (message.role === "activity") elements.push(/* @__PURE__ */ jsx(MemoizedActivityMessage, {
5673
+ message,
5674
+ renderActivityMessage
5675
+ }, message.id));
5676
+ else if (message.role === "reasoning") elements.push(/* @__PURE__ */ jsx(MemoizedReasoningMessage, {
5677
+ message,
5678
+ messages,
5679
+ isRunning,
5680
+ ReasoningMessageComponent: ReasoningComponent,
5681
+ slotProps: reasoningSlotProps
5682
+ }, message.id));
4361
5683
  if (renderCustomMessage) elements.push(/* @__PURE__ */ jsx(MemoizedCustomMessage, {
4362
5684
  message,
4363
5685
  position: "after",
4364
5686
  renderCustomMessage,
4365
5687
  stateSnapshot
4366
5688
  }, `${message.id}-custom-after`));
4367
- return elements;
4368
- }).filter(Boolean);
5689
+ return elements.filter(Boolean);
5690
+ };
5691
+ const messageElements = shouldVirtualize ? [] : deduplicatedMessages.flatMap(renderMessageBlock);
4369
5692
  if (children) return /* @__PURE__ */ jsx("div", {
4370
5693
  "data-copilotkit": true,
4371
5694
  style: { display: "contents" },
@@ -4376,30 +5699,356 @@ function CopilotChatMessageView({ messages = [], assistantMessage, userMessage,
4376
5699
  interruptElement
4377
5700
  })
4378
5701
  });
4379
- const lastMessage = messages[messages.length - 1];
4380
- const showCursor = isRunning && lastMessage?.role !== "reasoning";
5702
+ const lastMessage = messages[messages.length - 1];
5703
+ const showCursor = isRunning && lastMessage?.role !== "reasoning";
5704
+ return /* @__PURE__ */ jsxs("div", {
5705
+ "data-copilotkit": true,
5706
+ "data-testid": "copilot-message-list",
5707
+ className: twMerge("copilotKitMessages cpk:flex cpk:flex-col", className),
5708
+ ...props,
5709
+ children: [
5710
+ shouldVirtualize ? /* @__PURE__ */ jsx("div", {
5711
+ style: {
5712
+ height: virtualizer.getTotalSize(),
5713
+ position: "relative"
5714
+ },
5715
+ children: virtualizer.getVirtualItems().map((virtualItem) => {
5716
+ const message = deduplicatedMessages[virtualItem.index];
5717
+ return /* @__PURE__ */ jsx("div", {
5718
+ "data-index": virtualItem.index,
5719
+ ref: virtualizer.measureElement,
5720
+ style: {
5721
+ position: "absolute",
5722
+ top: 0,
5723
+ left: 0,
5724
+ width: "100%",
5725
+ transform: `translateY(${virtualItem.start}px)`
5726
+ },
5727
+ children: renderMessageBlock(message)
5728
+ }, message.id);
5729
+ })
5730
+ }) : messageElements,
5731
+ interruptElement,
5732
+ showCursor && /* @__PURE__ */ jsx("div", {
5733
+ className: "cpk:mt-2",
5734
+ children: renderSlot(cursor, CopilotChatMessageView.Cursor, {})
5735
+ })
5736
+ ]
5737
+ });
5738
+ }
5739
+ CopilotChatMessageView.Cursor = function Cursor({ className, ...props }) {
5740
+ return /* @__PURE__ */ jsx("div", {
5741
+ "data-testid": "copilot-loading-cursor",
5742
+ className: twMerge("cpk:w-[11px] cpk:h-[11px] cpk:rounded-full cpk:bg-foreground cpk:animate-pulse-cursor cpk:ml-1", className),
5743
+ ...props
5744
+ });
5745
+ };
5746
+
5747
+ //#endregion
5748
+ //#region src/v2/components/chat/CopilotChatAttachmentQueue.tsx
5749
+ const CopilotChatAttachmentQueue = ({ attachments, onRemoveAttachment, className }) => {
5750
+ if (attachments.length === 0) return null;
5751
+ return /* @__PURE__ */ jsx("div", {
5752
+ className: cn("cpk:flex cpk:flex-wrap cpk:gap-2 cpk:p-2", className),
5753
+ children: attachments.map((attachment) => {
5754
+ const isMedia = attachment.type === "image" || attachment.type === "video";
5755
+ return /* @__PURE__ */ jsxs("div", {
5756
+ 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]"),
5757
+ children: [
5758
+ attachment.status === "uploading" && /* @__PURE__ */ jsx(UploadingOverlay, {}),
5759
+ /* @__PURE__ */ jsx(AttachmentPreview, { attachment }),
5760
+ /* @__PURE__ */ jsx("button", {
5761
+ onClick: () => onRemoveAttachment(attachment.id),
5762
+ 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"),
5763
+ "aria-label": "Remove attachment",
5764
+ children: "✕"
5765
+ })
5766
+ ]
5767
+ }, attachment.id);
5768
+ })
5769
+ });
5770
+ };
5771
+ function UploadingOverlay() {
5772
+ return /* @__PURE__ */ jsx("div", {
5773
+ className: "cpk:absolute cpk:inset-0 cpk:flex cpk:items-center cpk:justify-center cpk:bg-black/40 cpk:z-10",
5774
+ children: /* @__PURE__ */ 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" })
5775
+ });
5776
+ }
5777
+ function AttachmentPreview({ attachment }) {
5778
+ if (attachment.status === "uploading") return /* @__PURE__ */ jsx("div", { className: "cpk:w-full cpk:h-full" });
5779
+ switch (attachment.type) {
5780
+ case "image": return /* @__PURE__ */ jsx(ImagePreview, { attachment });
5781
+ case "audio": return /* @__PURE__ */ jsx(AudioPreview, { attachment });
5782
+ case "video": return /* @__PURE__ */ jsx(VideoPreview, { attachment });
5783
+ case "document": return /* @__PURE__ */ jsx(DocumentPreview, { attachment });
5784
+ }
5785
+ }
5786
+ function Lightbox({ onClose, children }) {
5787
+ useEffect(() => {
5788
+ const handleKey = (e) => {
5789
+ if (e.key === "Escape") onClose();
5790
+ };
5791
+ document.addEventListener("keydown", handleKey);
5792
+ return () => document.removeEventListener("keydown", handleKey);
5793
+ }, [onClose]);
5794
+ if (typeof document === "undefined") return null;
5795
+ return createPortal(/* @__PURE__ */ jsxs("div", {
5796
+ 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",
5797
+ onClick: onClose,
5798
+ children: [/* @__PURE__ */ jsx("button", {
5799
+ onClick: onClose,
5800
+ 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",
5801
+ "aria-label": "Close preview",
5802
+ children: /* @__PURE__ */ jsx(X, { className: "cpk:w-5 cpk:h-5" })
5803
+ }), /* @__PURE__ */ jsx("div", {
5804
+ onClick: (e) => e.stopPropagation(),
5805
+ children
5806
+ })]
5807
+ }), document.body);
5808
+ }
5809
+ /**
5810
+ * Hook that manages lightbox open/close and uses the View Transition API to
5811
+ * morph the thumbnail into fullscreen content.
5812
+ *
5813
+ * The trick: `view-transition-name` must live on exactly ONE element at a time.
5814
+ * - Old state (thumbnail visible): name is on the thumbnail.
5815
+ * - New state (lightbox visible): name moves to the lightbox content.
5816
+ * `flushSync` ensures React commits the DOM change synchronously inside the
5817
+ * `startViewTransition` callback so the API can snapshot old → new correctly.
5818
+ */
5819
+ function useLightbox() {
5820
+ const thumbnailRef = useRef(null);
5821
+ const [open, setOpen] = useState(false);
5822
+ const vtName = useId();
5823
+ return {
5824
+ thumbnailRef,
5825
+ vtName,
5826
+ open,
5827
+ openLightbox: useCallback(() => {
5828
+ const thumb = thumbnailRef.current;
5829
+ const doc = document;
5830
+ if (doc.startViewTransition && thumb) {
5831
+ thumb.style.viewTransitionName = vtName;
5832
+ doc.startViewTransition(() => {
5833
+ thumb.style.viewTransitionName = "";
5834
+ flushSync(() => setOpen(true));
5835
+ });
5836
+ } else setOpen(true);
5837
+ }, []),
5838
+ closeLightbox: useCallback(() => {
5839
+ const thumb = thumbnailRef.current;
5840
+ const doc = document;
5841
+ if (doc.startViewTransition && thumb) doc.startViewTransition(() => {
5842
+ flushSync(() => setOpen(false));
5843
+ thumb.style.viewTransitionName = vtName;
5844
+ }).finished.then(() => {
5845
+ thumb.style.viewTransitionName = "";
5846
+ }).catch(() => {
5847
+ thumb.style.viewTransitionName = "";
5848
+ });
5849
+ else setOpen(false);
5850
+ }, [])
5851
+ };
5852
+ }
5853
+ function ImagePreview({ attachment }) {
5854
+ const src = getSourceUrl(attachment.source);
5855
+ const { thumbnailRef, vtName, open, openLightbox, closeLightbox } = useLightbox();
5856
+ return /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx("img", {
5857
+ ref: thumbnailRef,
5858
+ src,
5859
+ alt: attachment.filename || "Image attachment",
5860
+ className: "cpk:w-full cpk:h-full cpk:object-cover cpk:cursor-pointer",
5861
+ onClick: openLightbox
5862
+ }), open && /* @__PURE__ */ jsx(Lightbox, {
5863
+ onClose: closeLightbox,
5864
+ children: /* @__PURE__ */ jsx("img", {
5865
+ style: { viewTransitionName: vtName },
5866
+ src,
5867
+ alt: attachment.filename || "Image attachment",
5868
+ className: "cpk:max-w-[90vw] cpk:max-h-[90vh] cpk:object-contain cpk:rounded-lg"
5869
+ })
5870
+ })] });
5871
+ }
5872
+ function AudioPreview({ attachment }) {
5873
+ return /* @__PURE__ */ jsxs("div", {
5874
+ className: "cpk:flex cpk:flex-col cpk:gap-1 cpk:w-full",
5875
+ children: [/* @__PURE__ */ jsx("audio", {
5876
+ src: getSourceUrl(attachment.source),
5877
+ controls: true,
5878
+ preload: "metadata",
5879
+ className: "cpk:w-full cpk:h-8"
5880
+ }), attachment.filename && /* @__PURE__ */ jsx("span", {
5881
+ className: "cpk:text-xs cpk:font-medium cpk:overflow-hidden cpk:text-ellipsis cpk:whitespace-nowrap",
5882
+ children: attachment.filename
5883
+ })]
5884
+ });
5885
+ }
5886
+ function VideoPreview({ attachment }) {
5887
+ const src = getSourceUrl(attachment.source);
5888
+ const { thumbnailRef, vtName, open, openLightbox, closeLightbox } = useLightbox();
5889
+ return /* @__PURE__ */ jsxs(Fragment$1, { children: [
5890
+ /* @__PURE__ */ jsx("div", {
5891
+ ref: thumbnailRef,
5892
+ className: "cpk:w-full cpk:h-full",
5893
+ children: attachment.thumbnail ? /* @__PURE__ */ jsx("img", {
5894
+ src: attachment.thumbnail,
5895
+ alt: attachment.filename || "Video thumbnail",
5896
+ className: "cpk:w-full cpk:h-full cpk:object-cover"
5897
+ }) : /* @__PURE__ */ jsx("video", {
5898
+ src,
5899
+ preload: "metadata",
5900
+ muted: true,
5901
+ className: "cpk:w-full cpk:h-full cpk:object-cover"
5902
+ })
5903
+ }),
5904
+ /* @__PURE__ */ jsx("button", {
5905
+ onClick: openLightbox,
5906
+ 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",
5907
+ "aria-label": "Play video",
5908
+ children: /* @__PURE__ */ jsx("div", {
5909
+ className: "cpk:w-8 cpk:h-8 cpk:rounded-full cpk:bg-black/60 cpk:flex cpk:items-center cpk:justify-center",
5910
+ children: /* @__PURE__ */ jsx(Play, { className: "cpk:w-4 cpk:h-4 cpk:text-white cpk:ml-0.5" })
5911
+ })
5912
+ }),
5913
+ open && /* @__PURE__ */ jsx(Lightbox, {
5914
+ onClose: closeLightbox,
5915
+ children: /* @__PURE__ */ jsx("video", {
5916
+ style: { viewTransitionName: vtName },
5917
+ src,
5918
+ controls: true,
5919
+ autoPlay: true,
5920
+ className: "cpk:max-w-[90vw] cpk:max-h-[90vh] cpk:rounded-lg"
5921
+ })
5922
+ })
5923
+ ] });
5924
+ }
5925
+ function isPdf(mimeType) {
5926
+ return !!mimeType && mimeType.includes("pdf");
5927
+ }
5928
+ function isText(mimeType) {
5929
+ return !!mimeType && mimeType.startsWith("text/");
5930
+ }
5931
+ function canPreviewInBrowser(mimeType) {
5932
+ return isPdf(mimeType) || isText(mimeType);
5933
+ }
5934
+ /**
5935
+ * Convert a base64-encoded data source to a blob: URL that browsers will
5936
+ * render inside an iframe (data: URLs are blocked for PDFs in most browsers).
5937
+ */
5938
+ function useBlobUrl(attachment) {
5939
+ const [url, setUrl] = useState(null);
5940
+ useEffect(() => {
5941
+ if (attachment.source.type !== "data") return;
5942
+ try {
5943
+ const binary = atob(attachment.source.value);
5944
+ const bytes = new Uint8Array(binary.length);
5945
+ for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);
5946
+ const blob = new Blob([bytes], { type: attachment.source.mimeType || "application/octet-stream" });
5947
+ const blobUrl = URL.createObjectURL(blob);
5948
+ setUrl(blobUrl);
5949
+ return () => URL.revokeObjectURL(blobUrl);
5950
+ } catch (error) {
5951
+ console.error("[CopilotKit] Failed to decode attachment data:", error);
5952
+ setUrl(null);
5953
+ }
5954
+ }, [
5955
+ attachment.source.type,
5956
+ attachment.source.value,
5957
+ attachment.source.mimeType
5958
+ ]);
5959
+ if (attachment.source.type === "url") return attachment.source.value;
5960
+ return url;
5961
+ }
5962
+ function DocumentLightboxContent({ attachment, vtName }) {
5963
+ const mimeType = attachment.source.mimeType;
5964
+ const blobUrl = useBlobUrl(attachment);
5965
+ if (isPdf(mimeType)) {
5966
+ if (!blobUrl) return null;
5967
+ return /* @__PURE__ */ jsx("iframe", {
5968
+ style: { viewTransitionName: vtName },
5969
+ src: blobUrl,
5970
+ title: attachment.filename || "PDF preview",
5971
+ className: "cpk:w-[90vw] cpk:h-[90vh] cpk:max-w-[1000px] cpk:rounded-lg cpk:bg-white"
5972
+ });
5973
+ }
5974
+ if (isText(mimeType)) {
5975
+ const textContent = attachment.source.type === "data" ? (() => {
5976
+ try {
5977
+ return atob(attachment.source.value);
5978
+ } catch {
5979
+ return attachment.source.value;
5980
+ }
5981
+ })() : null;
5982
+ return /* @__PURE__ */ jsxs("div", {
5983
+ style: { viewTransitionName: vtName },
5984
+ 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",
5985
+ children: [attachment.filename && /* @__PURE__ */ jsx("div", {
5986
+ 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",
5987
+ children: attachment.filename
5988
+ }), textContent ? /* @__PURE__ */ jsx("pre", {
5989
+ 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",
5990
+ children: textContent
5991
+ }) : blobUrl ? /* @__PURE__ */ jsx("iframe", {
5992
+ src: blobUrl,
5993
+ title: attachment.filename || "Text preview",
5994
+ className: "cpk:w-full cpk:h-[80vh] cpk:border-none"
5995
+ }) : null]
5996
+ });
5997
+ }
4381
5998
  return /* @__PURE__ */ jsxs("div", {
4382
- "data-copilotkit": true,
4383
- "data-testid": "copilot-message-list",
4384
- className: twMerge("copilotKitMessages cpk:flex cpk:flex-col", className),
4385
- ...props,
5999
+ style: { viewTransitionName: vtName },
6000
+ 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",
4386
6001
  children: [
4387
- messageElements,
4388
- interruptElement,
4389
- showCursor && /* @__PURE__ */ jsx("div", {
4390
- className: "cpk:mt-2",
4391
- children: renderSlot(cursor, CopilotChatMessageView.Cursor, {})
6002
+ /* @__PURE__ */ jsx("div", {
6003
+ 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",
6004
+ children: getDocumentIcon(mimeType ?? "")
6005
+ }),
6006
+ /* @__PURE__ */ jsxs("div", {
6007
+ className: "cpk:text-center",
6008
+ children: [/* @__PURE__ */ jsx("div", {
6009
+ className: "cpk:text-base cpk:font-medium cpk:text-gray-800 cpk:dark:text-gray-200",
6010
+ children: attachment.filename || "Document"
6011
+ }), /* @__PURE__ */ jsxs("div", {
6012
+ className: "cpk:text-sm cpk:text-gray-500 cpk:dark:text-gray-400 cpk:mt-1",
6013
+ children: [mimeType || "Unknown type", attachment.size != null && ` · ${formatFileSize(attachment.size)}`]
6014
+ })]
6015
+ }),
6016
+ /* @__PURE__ */ jsx("div", {
6017
+ className: "cpk:text-xs cpk:text-gray-400 cpk:dark:text-gray-500",
6018
+ children: "No preview available for this file type"
4392
6019
  })
4393
6020
  ]
4394
6021
  });
4395
6022
  }
4396
- CopilotChatMessageView.Cursor = function Cursor({ className, ...props }) {
4397
- return /* @__PURE__ */ jsx("div", {
4398
- "data-testid": "copilot-loading-cursor",
4399
- className: twMerge("cpk:w-[11px] cpk:h-[11px] cpk:rounded-full cpk:bg-foreground cpk:animate-pulse-cursor cpk:ml-1", className),
4400
- ...props
4401
- });
4402
- };
6023
+ function DocumentPreview({ attachment }) {
6024
+ const { thumbnailRef, vtName, open, openLightbox, closeLightbox } = useLightbox();
6025
+ const mimeType = attachment.source.mimeType;
6026
+ const previewable = canPreviewInBrowser(mimeType);
6027
+ return /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsxs("div", {
6028
+ ref: thumbnailRef,
6029
+ className: cn("cpk:flex cpk:items-center cpk:gap-2", previewable && "cpk:cursor-pointer"),
6030
+ onClick: previewable ? openLightbox : void 0,
6031
+ children: [/* @__PURE__ */ jsx("div", {
6032
+ 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",
6033
+ children: getDocumentIcon(mimeType ?? "")
6034
+ }), /* @__PURE__ */ jsxs("div", {
6035
+ className: "cpk:flex cpk:flex-col cpk:min-w-0",
6036
+ children: [/* @__PURE__ */ jsx("span", {
6037
+ className: "cpk:text-xs cpk:font-medium cpk:break-all cpk:leading-tight",
6038
+ children: attachment.filename || "Document"
6039
+ }), attachment.size != null && /* @__PURE__ */ jsx("span", {
6040
+ className: "cpk:text-[11px] cpk:text-muted-foreground",
6041
+ children: formatFileSize(attachment.size)
6042
+ })]
6043
+ })]
6044
+ }), open && /* @__PURE__ */ jsx(Lightbox, {
6045
+ onClose: closeLightbox,
6046
+ children: /* @__PURE__ */ jsx(DocumentLightboxContent, {
6047
+ attachment,
6048
+ vtName
6049
+ })
6050
+ })] });
6051
+ }
4403
6052
 
4404
6053
  //#endregion
4405
6054
  //#region src/v2/hooks/use-keyboard-height.tsx
@@ -4445,7 +6094,19 @@ function useKeyboardHeight() {
4445
6094
  //#endregion
4446
6095
  //#region src/v2/components/chat/CopilotChatView.tsx
4447
6096
  const FEATHER_HEIGHT = 96;
4448
- 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 }) {
6097
+ function DropOverlay() {
6098
+ return /* @__PURE__ */ jsx("div", {
6099
+ 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"),
6100
+ children: /* @__PURE__ */ jsxs("div", {
6101
+ className: "cpk:flex cpk:flex-col cpk:items-center cpk:gap-2 cpk:text-primary/70",
6102
+ children: [/* @__PURE__ */ jsx(Upload, { className: "cpk:w-8 cpk:h-8" }), /* @__PURE__ */ jsx("span", {
6103
+ className: "cpk:text-sm cpk:font-medium",
6104
+ children: "Drop files here"
6105
+ })]
6106
+ })
6107
+ });
6108
+ }
6109
+ 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 }) {
4449
6110
  const inputContainerRef = useRef(null);
4450
6111
  const [inputContainerHeight, setInputContainerHeight] = useState(0);
4451
6112
  const [isResizing, setIsResizing] = useState(false);
@@ -4492,6 +6153,7 @@ function CopilotChatView({ messageView, input, scrollView, suggestionView, welco
4492
6153
  onCancelTranscribe,
4493
6154
  onFinishTranscribe,
4494
6155
  onFinishTranscribeWithAudio,
6156
+ onAddFile,
4495
6157
  positioning: "static",
4496
6158
  keyboardHeight: isKeyboardOpen ? keyboardHeight : 0,
4497
6159
  containerRef: inputContainerRef,
@@ -4532,21 +6194,34 @@ function CopilotChatView({ messageView, input, scrollView, suggestionView, welco
4532
6194
  onCancelTranscribe,
4533
6195
  onFinishTranscribe,
4534
6196
  onFinishTranscribeWithAudio,
6197
+ onAddFile,
4535
6198
  positioning: "static",
4536
6199
  showDisclaimer: true,
4537
6200
  ...disclaimer !== void 0 ? { disclaimer } : {}
4538
6201
  });
4539
- const BoundWelcomeScreen = renderSlot(welcomeScreen === true ? void 0 : welcomeScreen, CopilotChatView.WelcomeScreen, {
4540
- input: BoundInputForWelcome,
6202
+ const welcomeScreenSlot = welcomeScreen === true ? void 0 : welcomeScreen;
6203
+ const inputWithAttachments = /* @__PURE__ */ jsxs("div", {
6204
+ className: "cpk:w-full",
6205
+ children: [attachments && attachments.length > 0 && /* @__PURE__ */ jsx(CopilotChatAttachmentQueue, {
6206
+ attachments,
6207
+ onRemoveAttachment: (id) => onRemoveAttachment?.(id),
6208
+ className: "cpk:mb-2"
6209
+ }), BoundInputForWelcome]
6210
+ });
6211
+ const BoundWelcomeScreen = renderSlot(welcomeScreenSlot, CopilotChatView.WelcomeScreen, {
6212
+ input: inputWithAttachments,
4541
6213
  suggestionView: BoundSuggestionView ?? /* @__PURE__ */ jsx(Fragment$1, {})
4542
6214
  });
4543
- return /* @__PURE__ */ jsx("div", {
6215
+ return /* @__PURE__ */ jsxs("div", {
4544
6216
  "data-copilotkit": true,
4545
6217
  "data-testid": "copilot-chat",
4546
6218
  "data-copilot-running": isRunning ? "true" : "false",
4547
- className: twMerge("copilotKitChat cpk:relative cpk:h-full cpk:flex cpk:flex-col", className),
6219
+ onDragOver,
6220
+ onDragLeave,
6221
+ onDrop,
6222
+ className: cn("copilotKitChat cpk:relative cpk:h-full cpk:flex cpk:flex-col", className),
4548
6223
  ...props,
4549
- children: BoundWelcomeScreen
6224
+ children: [dragOver && /* @__PURE__ */ jsx(DropOverlay, {}), BoundWelcomeScreen]
4550
6225
  });
4551
6226
  }
4552
6227
  if (children) return /* @__PURE__ */ jsx("div", {
@@ -4563,39 +6238,66 @@ function CopilotChatView({ messageView, input, scrollView, suggestionView, welco
4563
6238
  "data-copilotkit": true,
4564
6239
  "data-testid": "copilot-chat",
4565
6240
  "data-copilot-running": isRunning ? "true" : "false",
4566
- className: twMerge("copilotKitChat cpk:relative cpk:h-full cpk:flex cpk:flex-col", className),
6241
+ onDragOver,
6242
+ onDragLeave,
6243
+ onDrop,
6244
+ className: cn("copilotKitChat cpk:relative cpk:h-full cpk:flex cpk:flex-col", className),
4567
6245
  ...props,
4568
- children: [BoundScrollView, BoundInput]
6246
+ children: [
6247
+ dragOver && /* @__PURE__ */ jsx(DropOverlay, {}),
6248
+ BoundScrollView,
6249
+ /* @__PURE__ */ jsx("div", {
6250
+ className: "cpk:max-w-3xl cpk:mx-auto cpk:w-full",
6251
+ children: attachments && attachments.length > 0 && /* @__PURE__ */ jsx(CopilotChatAttachmentQueue, {
6252
+ attachments,
6253
+ onRemoveAttachment: (id) => onRemoveAttachment?.(id),
6254
+ className: "cpk:px-4"
6255
+ })
6256
+ }),
6257
+ BoundInput
6258
+ ]
4569
6259
  });
4570
6260
  }
4571
6261
  (function(_CopilotChatView) {
4572
6262
  const ScrollContent = ({ children, scrollToBottomButton, feather, inputContainerHeight, isResizing }) => {
4573
- const { isAtBottom, scrollToBottom } = useStickToBottomContext();
6263
+ const { isAtBottom, scrollToBottom, scrollRef } = useStickToBottomContext();
6264
+ const [scrollEl, setScrollEl] = useState(null);
6265
+ useLayoutEffect(() => {
6266
+ setScrollEl(scrollRef.current ?? null);
6267
+ }, []);
4574
6268
  const BoundFeather = renderSlot(feather, CopilotChatView.Feather, {});
4575
- return /* @__PURE__ */ jsxs(Fragment$1, { children: [
4576
- /* @__PURE__ */ jsx(StickToBottom.Content, {
4577
- className: "cpk:overflow-y-auto cpk:overflow-x-hidden",
4578
- style: {
4579
- flex: "1 1 0%",
4580
- minHeight: 0
4581
- },
4582
- children: /* @__PURE__ */ jsx("div", {
4583
- className: "cpk:px-4 cpk:sm:px-0 cpk:[div[data-sidebar-chat]_&]:px-8 cpk:[div[data-popup-chat]_&]:px-6",
4584
- children
6269
+ return /* @__PURE__ */ jsx(ScrollElementContext.Provider, {
6270
+ value: scrollEl,
6271
+ children: /* @__PURE__ */ jsxs(Fragment$1, { children: [
6272
+ /* @__PURE__ */ jsx(StickToBottom.Content, {
6273
+ className: "cpk:overflow-y-auto cpk:overflow-x-hidden",
6274
+ style: {
6275
+ flex: "1 1 0%",
6276
+ minHeight: 0
6277
+ },
6278
+ children: /* @__PURE__ */ jsx("div", {
6279
+ className: "cpk:px-4 cpk:sm:px-0 cpk:[div[data-sidebar-chat]_&]:px-8 cpk:[div[data-popup-chat]_&]:px-6",
6280
+ children
6281
+ })
6282
+ }),
6283
+ BoundFeather,
6284
+ !isAtBottom && !isResizing && /* @__PURE__ */ jsx("div", {
6285
+ className: "cpk:absolute cpk:inset-x-0 cpk:flex cpk:justify-center cpk:z-30 cpk:pointer-events-none",
6286
+ style: { bottom: `${inputContainerHeight + FEATHER_HEIGHT + 16}px` },
6287
+ children: renderSlot(scrollToBottomButton, CopilotChatView.ScrollToBottomButton, { onClick: () => scrollToBottom() })
4585
6288
  })
4586
- }),
4587
- BoundFeather,
4588
- !isAtBottom && !isResizing && /* @__PURE__ */ jsx("div", {
4589
- className: "cpk:absolute cpk:inset-x-0 cpk:flex cpk:justify-center cpk:z-30 cpk:pointer-events-none",
4590
- style: { bottom: `${inputContainerHeight + FEATHER_HEIGHT + 16}px` },
4591
- children: renderSlot(scrollToBottomButton, CopilotChatView.ScrollToBottomButton, { onClick: () => scrollToBottom() })
4592
- })
4593
- ] });
6289
+ ] })
6290
+ });
4594
6291
  };
4595
6292
  _CopilotChatView.ScrollView = ({ children, autoScroll = true, scrollToBottomButton, feather, inputContainerHeight = 0, isResizing = false, className, ...props }) => {
4596
6293
  const [hasMounted, setHasMounted] = useState(false);
4597
6294
  const { scrollRef, contentRef, scrollToBottom } = useStickToBottom();
4598
6295
  const [showScrollButton, setShowScrollButton] = useState(false);
6296
+ const [nonAutoScrollEl, setNonAutoScrollEl] = useState(null);
6297
+ const nonAutoScrollRefCallback = useCallback((el) => {
6298
+ scrollRef.current = el;
6299
+ setNonAutoScrollEl(el);
6300
+ }, []);
4599
6301
  useEffect(() => {
4600
6302
  setHasMounted(true);
4601
6303
  }, []);
@@ -4624,23 +6326,26 @@ function CopilotChatView({ messageView, input, scrollView, suggestionView, welco
4624
6326
  });
4625
6327
  if (!autoScroll) {
4626
6328
  const BoundFeather = renderSlot(feather, CopilotChatView.Feather, {});
4627
- return /* @__PURE__ */ jsxs("div", {
4628
- ref: scrollRef,
4629
- 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),
4630
- ...props,
4631
- children: [
4632
- /* @__PURE__ */ jsx("div", {
4633
- ref: contentRef,
4634
- className: "cpk:px-4 cpk:sm:px-0 cpk:[div[data-sidebar-chat]_&]:px-8 cpk:[div[data-popup-chat]_&]:px-6",
4635
- children
4636
- }),
4637
- BoundFeather,
4638
- showScrollButton && !isResizing && /* @__PURE__ */ jsx("div", {
4639
- className: "cpk:absolute cpk:inset-x-0 cpk:flex cpk:justify-center cpk:z-30 cpk:pointer-events-none",
4640
- style: { bottom: `${inputContainerHeight + FEATHER_HEIGHT + 16}px` },
4641
- children: renderSlot(scrollToBottomButton, CopilotChatView.ScrollToBottomButton, { onClick: () => scrollToBottom() })
4642
- })
4643
- ]
6329
+ return /* @__PURE__ */ jsx(ScrollElementContext.Provider, {
6330
+ value: nonAutoScrollEl,
6331
+ children: /* @__PURE__ */ jsxs("div", {
6332
+ ref: nonAutoScrollRefCallback,
6333
+ 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),
6334
+ ...props,
6335
+ children: [
6336
+ /* @__PURE__ */ jsx("div", {
6337
+ ref: contentRef,
6338
+ className: "cpk:px-4 cpk:sm:px-0 cpk:[div[data-sidebar-chat]_&]:px-8 cpk:[div[data-popup-chat]_&]:px-6",
6339
+ children
6340
+ }),
6341
+ BoundFeather,
6342
+ showScrollButton && !isResizing && /* @__PURE__ */ jsx("div", {
6343
+ className: "cpk:absolute cpk:inset-x-0 cpk:flex cpk:justify-center cpk:z-30 cpk:pointer-events-none",
6344
+ style: { bottom: `${inputContainerHeight + FEATHER_HEIGHT + 16}px` },
6345
+ children: renderSlot(scrollToBottomButton, CopilotChatView.ScrollToBottomButton, { onClick: () => scrollToBottom() })
6346
+ })
6347
+ ]
6348
+ })
4644
6349
  });
4645
6350
  }
4646
6351
  return /* @__PURE__ */ jsx(StickToBottom, {
@@ -4832,13 +6537,14 @@ async function transcribeAudio(core, audioBlob, filename = "recording.webm") {
4832
6537
 
4833
6538
  //#endregion
4834
6539
  //#region src/v2/components/chat/CopilotChat.tsx
4835
- function CopilotChat({ agentId, threadId, labels, chatView, isModalDefaultOpen, onError, ...props }) {
6540
+ function CopilotChat({ agentId, threadId, labels, chatView, isModalDefaultOpen, attachments: attachmentsConfig, onError, throttleMs, ...props }) {
4836
6541
  const existingConfig = useCopilotChatConfiguration();
4837
6542
  const resolvedAgentId = agentId ?? existingConfig?.agentId ?? DEFAULT_AGENT_ID;
4838
6543
  const resolvedThreadId = useMemo(() => threadId ?? existingConfig?.threadId ?? randomUUID(), [threadId, existingConfig?.threadId]);
4839
6544
  const { agent } = useAgent({
4840
6545
  agentId: resolvedAgentId,
4841
- threadId: resolvedThreadId
6546
+ threadId: resolvedThreadId,
6547
+ throttleMs
4842
6548
  });
4843
6549
  const { copilotkit } = useCopilotKit();
4844
6550
  const { suggestions: autoSuggestions } = useSuggestions({ agentId: resolvedAgentId });
@@ -4868,6 +6574,7 @@ function CopilotChat({ agentId, threadId, labels, chatView, isModalDefaultOpen,
4868
6574
  const [inputValue, setInputValue] = useState("");
4869
6575
  const [transcriptionError, setTranscriptionError] = useState(null);
4870
6576
  const [isTranscribing, setIsTranscribing] = useState(false);
6577
+ const { attachments: selectedAttachments, enabled: attachmentsEnabled, dragOver, fileInputRef, containerRef: chatContainerRef, handleFileUpload, handleDragOver, handleDragLeave, handleDrop, removeAttachment, consumeAttachments } = useAttachments({ config: attachmentsConfig });
4871
6578
  const isTranscriptionEnabled = copilotkit.audioFileTranscriptionEnabled;
4872
6579
  const isMediaRecorderSupported = typeof window !== "undefined" && typeof MediaRecorder !== "undefined";
4873
6580
  const { messageView: providedMessageView, suggestionView: providedSuggestionView, onStop: providedStopHandler, ...restProps } = props;
@@ -4895,7 +6602,31 @@ function CopilotChat({ agentId, threadId, labels, chatView, isModalDefaultOpen,
4895
6602
  resolvedAgentId
4896
6603
  ]);
4897
6604
  const onSubmitInput = useCallback(async (value) => {
4898
- agent.addMessage({
6605
+ if (selectedAttachments.some((a) => a.status === "uploading")) {
6606
+ console.error("[CopilotKit] Cannot send while attachments are uploading");
6607
+ return;
6608
+ }
6609
+ const readyAttachments = consumeAttachments();
6610
+ if (readyAttachments.length > 0) {
6611
+ const contentParts = [];
6612
+ if (value.trim()) contentParts.push({
6613
+ type: "text",
6614
+ text: value
6615
+ });
6616
+ for (const att of readyAttachments) contentParts.push({
6617
+ type: att.type,
6618
+ source: att.source,
6619
+ metadata: {
6620
+ ...att.filename ? { filename: att.filename } : {},
6621
+ ...att.metadata
6622
+ }
6623
+ });
6624
+ agent.addMessage({
6625
+ id: randomUUID(),
6626
+ role: "user",
6627
+ content: contentParts
6628
+ });
6629
+ } else agent.addMessage({
4899
6630
  id: randomUUID(),
4900
6631
  role: "user",
4901
6632
  content: value
@@ -4906,7 +6637,11 @@ function CopilotChat({ agentId, threadId, labels, chatView, isModalDefaultOpen,
4906
6637
  } catch (error) {
4907
6638
  console.error("CopilotChat: runAgent failed", error);
4908
6639
  }
4909
- }, [agent]);
6640
+ }, [
6641
+ agent,
6642
+ selectedAttachments,
6643
+ consumeAttachments
6644
+ ]);
4910
6645
  const handleSelectSuggestion = useCallback(async (suggestion) => {
4911
6646
  agent.addMessage({
4912
6647
  id: randomUUID(),
@@ -4993,21 +6728,33 @@ function CopilotChat({ agentId, threadId, labels, chatView, isModalDefaultOpen,
4993
6728
  return () => clearTimeout(timer);
4994
6729
  }
4995
6730
  }, [transcriptionError]);
4996
- const mergedProps = merge({
6731
+ const stableMessageView = useShallowStableRef(typeof providedMessageView === "string" ? { className: providedMessageView } : providedMessageView);
6732
+ const stableSuggestionView = useShallowStableRef(providedSuggestionView);
6733
+ const handleAddFile = useCallback(() => {
6734
+ setTimeout(() => {
6735
+ fileInputRef.current?.click();
6736
+ }, 100);
6737
+ }, []);
6738
+ const mergedProps = {
4997
6739
  isRunning: agent.isRunning,
4998
6740
  suggestions: autoSuggestions,
4999
6741
  onSelectSuggestion: handleSelectSuggestion,
5000
- suggestionView: providedSuggestionView
5001
- }, {
5002
- ...restProps,
5003
- ...typeof providedMessageView === "string" ? { messageView: { className: providedMessageView } } : providedMessageView !== void 0 ? { messageView: providedMessageView } : {}
5004
- });
6742
+ suggestionView: stableSuggestionView,
6743
+ ...restProps
6744
+ };
6745
+ if (stableMessageView !== void 0) mergedProps.messageView = stableMessageView;
5005
6746
  const hasMessages = agent.messages.length > 0;
5006
6747
  const effectiveStopHandler = agent.isRunning && hasMessages ? providedStopHandler ?? stopCurrentRun : providedStopHandler;
5007
6748
  const showTranscription = isTranscriptionEnabled && isMediaRecorderSupported;
5008
6749
  const effectiveMode = isTranscribing ? "processing" : transcribeMode;
5009
- const RenderedChatView = renderSlot(chatView, CopilotChatView, merge(mergedProps, {
5010
- messages: useMemo(() => [...agent.messages], [JSON.stringify(agent.messages)]),
6750
+ const messages = useMemo(() => [...agent.messages], [agent.messages.map((m) => {
6751
+ const contentKey = typeof m.content === "string" ? m.content.length : Array.isArray(m.content) ? m.content.length : 0;
6752
+ const toolCallsKey = "toolCalls" in m && Array.isArray(m.toolCalls) ? m.toolCalls.map((tc) => `${tc.id}:${tc.function?.arguments?.length ?? 0}`).join(";") : "";
6753
+ return `${m.id}:${m.role}:${contentKey}:${toolCallsKey}`;
6754
+ }).join(",")]);
6755
+ const RenderedChatView = renderSlot(chatView, CopilotChatView, {
6756
+ ...mergedProps,
6757
+ messages,
5011
6758
  onSubmitMessage: onSubmitInput,
5012
6759
  onStop: effectiveStopHandler,
5013
6760
  inputMode: effectiveMode,
@@ -5016,32 +6763,51 @@ function CopilotChat({ agentId, threadId, labels, chatView, isModalDefaultOpen,
5016
6763
  onStartTranscribe: showTranscription ? handleStartTranscribe : void 0,
5017
6764
  onCancelTranscribe: showTranscription ? handleCancelTranscribe : void 0,
5018
6765
  onFinishTranscribe: showTranscription ? handleFinishTranscribe : void 0,
5019
- onFinishTranscribeWithAudio: showTranscription ? handleFinishTranscribeWithAudio : void 0
5020
- }));
5021
- return /* @__PURE__ */ jsxs(CopilotChatConfigurationProvider, {
6766
+ onFinishTranscribeWithAudio: showTranscription ? handleFinishTranscribeWithAudio : void 0,
6767
+ attachments: selectedAttachments,
6768
+ onRemoveAttachment: removeAttachment,
6769
+ onAddFile: attachmentsEnabled ? handleAddFile : void 0,
6770
+ dragOver,
6771
+ onDragOver: handleDragOver,
6772
+ onDragLeave: handleDragLeave,
6773
+ onDrop: handleDrop
6774
+ });
6775
+ return /* @__PURE__ */ jsx(CopilotChatConfigurationProvider, {
5022
6776
  agentId: resolvedAgentId,
5023
6777
  threadId: resolvedThreadId,
5024
6778
  labels,
5025
6779
  isModalDefaultOpen,
5026
- children: [
5027
- !isChatLicensed && /* @__PURE__ */ jsx(InlineFeatureWarning, { featureName: "Chat" }),
5028
- transcriptionError && /* @__PURE__ */ jsx("div", {
5029
- style: {
5030
- position: "absolute",
5031
- bottom: "100px",
5032
- left: "50%",
5033
- transform: "translateX(-50%)",
5034
- backgroundColor: "#ef4444",
5035
- color: "white",
5036
- padding: "8px 16px",
5037
- borderRadius: "8px",
5038
- fontSize: "14px",
5039
- zIndex: 50
5040
- },
5041
- children: transcriptionError
5042
- }),
5043
- RenderedChatView
5044
- ]
6780
+ children: /* @__PURE__ */ jsxs("div", {
6781
+ ref: chatContainerRef,
6782
+ style: { display: "contents" },
6783
+ children: [
6784
+ attachmentsEnabled && /* @__PURE__ */ jsx("input", {
6785
+ type: "file",
6786
+ multiple: true,
6787
+ ref: fileInputRef,
6788
+ onChange: handleFileUpload,
6789
+ accept: attachmentsConfig?.accept ?? "*/*",
6790
+ style: { display: "none" }
6791
+ }),
6792
+ !isChatLicensed && /* @__PURE__ */ jsx(InlineFeatureWarning, { featureName: "Chat" }),
6793
+ transcriptionError && /* @__PURE__ */ jsx("div", {
6794
+ style: {
6795
+ position: "absolute",
6796
+ bottom: "100px",
6797
+ left: "50%",
6798
+ transform: "translateX(-50%)",
6799
+ backgroundColor: "#ef4444",
6800
+ color: "white",
6801
+ padding: "8px 16px",
6802
+ borderRadius: "8px",
6803
+ fontSize: "14px",
6804
+ zIndex: 50
6805
+ },
6806
+ children: transcriptionError
6807
+ }),
6808
+ RenderedChatView
6809
+ ]
6810
+ })
5045
6811
  });
5046
6812
  }
5047
6813
  (function(_CopilotChat) {
@@ -5906,6 +7672,227 @@ function useToast() {
5906
7672
  if (!context) throw new Error("useToast must be used within a ToastProvider");
5907
7673
  return context;
5908
7674
  }
7675
+ function formatBannerMessage(message) {
7676
+ const jsonMatch = message.match(/'message':\s*'([^']+)'/);
7677
+ if (jsonMatch) return jsonMatch[1];
7678
+ let cleaned = message.split(" - ")[0];
7679
+ cleaned = cleaned.split(": Error code")[0];
7680
+ cleaned = cleaned.replace(/:\s*\d{3}$/, "");
7681
+ cleaned = cleaned.replace(/See more:.*$/g, "");
7682
+ cleaned = cleaned.trim();
7683
+ return cleaned || "An error occurred.";
7684
+ }
7685
+ function extractUrl(message) {
7686
+ const markdownMatch = /\[([^\]]+)\]\(([^)]+)\)/.exec(message);
7687
+ if (markdownMatch) return {
7688
+ url: markdownMatch[2],
7689
+ text: "See More"
7690
+ };
7691
+ const plainMatch = /(https?:\/\/[^\s)]+)/.exec(message);
7692
+ if (plainMatch) return {
7693
+ url: plainMatch[0].replace(/[.,;:'"]*$/, ""),
7694
+ text: "See More"
7695
+ };
7696
+ return null;
7697
+ }
7698
+ function BannerErrorDisplay({ bannerError, onDismiss }) {
7699
+ const [detailsExpanded, setDetailsExpanded] = useState(false);
7700
+ const colors = getErrorColors(getErrorSeverity(bannerError));
7701
+ const details = bannerError.details;
7702
+ const link = extractUrl(bannerError.message);
7703
+ return /* @__PURE__ */ jsxs("div", {
7704
+ style: {
7705
+ position: "fixed",
7706
+ bottom: "20px",
7707
+ left: "50%",
7708
+ transform: "translateX(-50%)",
7709
+ zIndex: 9999,
7710
+ backgroundColor: colors.background,
7711
+ border: `1px solid ${colors.border}`,
7712
+ borderLeft: `4px solid ${colors.border}`,
7713
+ borderRadius: "8px",
7714
+ padding: "12px 16px",
7715
+ fontSize: "13px",
7716
+ boxShadow: "0 4px 12px rgba(0, 0, 0, 0.15)",
7717
+ backdropFilter: "blur(8px)",
7718
+ maxWidth: "min(90vw, 700px)",
7719
+ width: "100%",
7720
+ boxSizing: "border-box",
7721
+ overflow: "hidden"
7722
+ },
7723
+ children: [/* @__PURE__ */ jsxs("div", {
7724
+ style: {
7725
+ display: "flex",
7726
+ justifyContent: "space-between",
7727
+ alignItems: "center",
7728
+ gap: "10px"
7729
+ },
7730
+ children: [/* @__PURE__ */ jsxs("div", {
7731
+ style: {
7732
+ display: "flex",
7733
+ alignItems: "center",
7734
+ gap: "8px",
7735
+ flex: 1,
7736
+ minWidth: 0
7737
+ },
7738
+ children: [/* @__PURE__ */ jsx("div", { style: {
7739
+ width: "12px",
7740
+ height: "12px",
7741
+ borderRadius: "50%",
7742
+ backgroundColor: colors.border,
7743
+ flexShrink: 0
7744
+ } }), /* @__PURE__ */ jsxs("div", {
7745
+ style: {
7746
+ display: "flex",
7747
+ alignItems: "center",
7748
+ gap: "10px",
7749
+ flex: 1,
7750
+ minWidth: 0
7751
+ },
7752
+ children: [
7753
+ /* @__PURE__ */ jsx("div", {
7754
+ style: {
7755
+ color: colors.text,
7756
+ lineHeight: "1.4",
7757
+ fontWeight: "400",
7758
+ fontSize: "13px",
7759
+ flex: 1,
7760
+ wordBreak: "break-all",
7761
+ overflowWrap: "break-word",
7762
+ maxWidth: "550px",
7763
+ overflow: "hidden",
7764
+ display: "-webkit-box",
7765
+ WebkitLineClamp: 10,
7766
+ WebkitBoxOrient: "vertical"
7767
+ },
7768
+ children: formatBannerMessage(bannerError.message)
7769
+ }),
7770
+ link && /* @__PURE__ */ jsx("button", {
7771
+ onClick: () => window.open(link.url, "_blank", "noopener,noreferrer"),
7772
+ style: {
7773
+ background: colors.border,
7774
+ color: "white",
7775
+ border: "none",
7776
+ borderRadius: "5px",
7777
+ padding: "4px 10px",
7778
+ fontSize: "11px",
7779
+ fontWeight: "500",
7780
+ cursor: "pointer",
7781
+ transition: "all 0.2s ease",
7782
+ flexShrink: 0
7783
+ },
7784
+ onMouseEnter: (e) => {
7785
+ e.currentTarget.style.opacity = "0.9";
7786
+ e.currentTarget.style.transform = "translateY(-1px)";
7787
+ },
7788
+ onMouseLeave: (e) => {
7789
+ e.currentTarget.style.opacity = "1";
7790
+ e.currentTarget.style.transform = "translateY(0)";
7791
+ },
7792
+ children: link.text
7793
+ }),
7794
+ details && /* @__PURE__ */ jsx("button", {
7795
+ onClick: () => setDetailsExpanded(!detailsExpanded),
7796
+ style: {
7797
+ background: "transparent",
7798
+ border: `1px solid ${colors.border}`,
7799
+ borderRadius: "5px",
7800
+ padding: "4px 10px",
7801
+ fontSize: "11px",
7802
+ fontWeight: "500",
7803
+ cursor: "pointer",
7804
+ color: colors.text,
7805
+ flexShrink: 0,
7806
+ transition: "all 0.2s ease"
7807
+ },
7808
+ onMouseEnter: (e) => {
7809
+ e.currentTarget.style.background = "rgba(0, 0, 0, 0.05)";
7810
+ },
7811
+ onMouseLeave: (e) => {
7812
+ e.currentTarget.style.background = "transparent";
7813
+ },
7814
+ children: detailsExpanded ? "Hide Details" : "Show Details"
7815
+ })
7816
+ ]
7817
+ })]
7818
+ }), /* @__PURE__ */ jsx("button", {
7819
+ onClick: onDismiss,
7820
+ style: {
7821
+ background: "transparent",
7822
+ border: "none",
7823
+ color: colors.text,
7824
+ cursor: "pointer",
7825
+ padding: "2px",
7826
+ borderRadius: "3px",
7827
+ fontSize: "14px",
7828
+ lineHeight: "1",
7829
+ opacity: .6,
7830
+ transition: "all 0.2s ease",
7831
+ flexShrink: 0
7832
+ },
7833
+ title: "Dismiss",
7834
+ onMouseEnter: (e) => {
7835
+ e.currentTarget.style.opacity = "1";
7836
+ e.currentTarget.style.background = "rgba(0, 0, 0, 0.05)";
7837
+ },
7838
+ onMouseLeave: (e) => {
7839
+ e.currentTarget.style.opacity = "0.6";
7840
+ e.currentTarget.style.background = "transparent";
7841
+ },
7842
+ children: "x"
7843
+ })]
7844
+ }), detailsExpanded && details && /* @__PURE__ */ jsxs("div", {
7845
+ style: {
7846
+ marginTop: "10px",
7847
+ padding: "10px",
7848
+ background: "rgba(0, 0, 0, 0.04)",
7849
+ borderRadius: "6px",
7850
+ fontSize: "11px",
7851
+ fontFamily: "monospace",
7852
+ color: colors.text,
7853
+ lineHeight: "1.5",
7854
+ maxHeight: "200px",
7855
+ overflowY: "auto",
7856
+ whiteSpace: "pre-wrap",
7857
+ wordBreak: "break-all"
7858
+ },
7859
+ children: [
7860
+ details.code && /* @__PURE__ */ jsxs("div", { children: [
7861
+ /* @__PURE__ */ jsx("strong", { children: "Code:" }),
7862
+ " ",
7863
+ details.code
7864
+ ] }),
7865
+ details.originalMessage && /* @__PURE__ */ jsxs("div", {
7866
+ style: { marginTop: "4px" },
7867
+ children: [
7868
+ /* @__PURE__ */ jsx("strong", { children: "Message:" }),
7869
+ " ",
7870
+ details.originalMessage
7871
+ ]
7872
+ }),
7873
+ details.context && Object.keys(details.context).length > 0 && /* @__PURE__ */ jsxs("div", {
7874
+ style: { marginTop: "4px" },
7875
+ children: [
7876
+ /* @__PURE__ */ jsx("strong", { children: "Context:" }),
7877
+ " ",
7878
+ JSON.stringify(details.context, null, 2)
7879
+ ]
7880
+ }),
7881
+ details.stack && /* @__PURE__ */ jsxs("div", {
7882
+ style: {
7883
+ marginTop: "4px",
7884
+ opacity: .7
7885
+ },
7886
+ children: [
7887
+ /* @__PURE__ */ jsx("strong", { children: "Stack:" }),
7888
+ "\n",
7889
+ details.stack
7890
+ ]
7891
+ })
7892
+ ]
7893
+ })]
7894
+ });
7895
+ }
5909
7896
  function ToastProvider({ enabled, children }) {
5910
7897
  const [toasts, setToasts] = useState([]);
5911
7898
  const [bannerError, setBannerErrorState] = useState(null);
@@ -5943,156 +7930,10 @@ function ToastProvider({ enabled, children }) {
5943
7930
  };
5944
7931
  return /* @__PURE__ */ jsxs(ToastContext.Provider, {
5945
7932
  value,
5946
- children: [bannerError && (() => {
5947
- const colors = getErrorColors(getErrorSeverity(bannerError));
5948
- return /* @__PURE__ */ jsx("div", {
5949
- style: {
5950
- position: "fixed",
5951
- bottom: "20px",
5952
- left: "50%",
5953
- transform: "translateX(-50%)",
5954
- zIndex: 9999,
5955
- backgroundColor: colors.background,
5956
- border: `1px solid ${colors.border}`,
5957
- borderLeft: `4px solid ${colors.border}`,
5958
- borderRadius: "8px",
5959
- padding: "12px 16px",
5960
- fontSize: "13px",
5961
- boxShadow: "0 4px 12px rgba(0, 0, 0, 0.15)",
5962
- backdropFilter: "blur(8px)",
5963
- maxWidth: "min(90vw, 700px)",
5964
- width: "100%",
5965
- boxSizing: "border-box",
5966
- overflow: "hidden"
5967
- },
5968
- children: /* @__PURE__ */ jsxs("div", {
5969
- style: {
5970
- display: "flex",
5971
- justifyContent: "space-between",
5972
- alignItems: "center",
5973
- gap: "10px"
5974
- },
5975
- children: [/* @__PURE__ */ jsxs("div", {
5976
- style: {
5977
- display: "flex",
5978
- alignItems: "center",
5979
- gap: "8px",
5980
- flex: 1,
5981
- minWidth: 0
5982
- },
5983
- children: [/* @__PURE__ */ jsx("div", { style: {
5984
- width: "12px",
5985
- height: "12px",
5986
- borderRadius: "50%",
5987
- backgroundColor: colors.border,
5988
- flexShrink: 0
5989
- } }), /* @__PURE__ */ jsxs("div", {
5990
- style: {
5991
- display: "flex",
5992
- alignItems: "center",
5993
- gap: "10px",
5994
- flex: 1,
5995
- minWidth: 0
5996
- },
5997
- children: [/* @__PURE__ */ jsx("div", {
5998
- style: {
5999
- color: colors.text,
6000
- lineHeight: "1.4",
6001
- fontWeight: "400",
6002
- fontSize: "13px",
6003
- flex: 1,
6004
- wordBreak: "break-all",
6005
- overflowWrap: "break-word",
6006
- maxWidth: "550px",
6007
- overflow: "hidden",
6008
- display: "-webkit-box",
6009
- WebkitLineClamp: 10,
6010
- WebkitBoxOrient: "vertical"
6011
- },
6012
- children: (() => {
6013
- let message = bannerError.message;
6014
- const jsonMatch = message.match(/'message':\s*'([^']+)'/);
6015
- if (jsonMatch) return jsonMatch[1];
6016
- message = message.split(" - ")[0];
6017
- message = message.split(": Error code")[0];
6018
- message = message.replace(/:\s*\d{3}$/, "");
6019
- message = message.replace(/See more:.*$/g, "");
6020
- message = message.trim();
6021
- return message || "Configuration error occurred.";
6022
- })()
6023
- }), (() => {
6024
- const message = bannerError.message;
6025
- const markdownLinkRegex = /\[([^\]]+)\]\(([^)]+)\)/g;
6026
- const plainUrlRegex = /(https?:\/\/[^\s)]+)/g;
6027
- let url = null;
6028
- let buttonText = "See More";
6029
- const markdownMatch = markdownLinkRegex.exec(message);
6030
- if (markdownMatch) {
6031
- url = markdownMatch[2];
6032
- buttonText = "See More";
6033
- } else {
6034
- const urlMatch = plainUrlRegex.exec(message);
6035
- if (urlMatch) {
6036
- url = urlMatch[0].replace(/[.,;:'"]*$/, "");
6037
- buttonText = "See More";
6038
- }
6039
- }
6040
- if (!url) return null;
6041
- return /* @__PURE__ */ jsx("button", {
6042
- onClick: () => window.open(url, "_blank", "noopener,noreferrer"),
6043
- style: {
6044
- background: colors.border,
6045
- color: "white",
6046
- border: "none",
6047
- borderRadius: "5px",
6048
- padding: "4px 10px",
6049
- fontSize: "11px",
6050
- fontWeight: "500",
6051
- cursor: "pointer",
6052
- transition: "all 0.2s ease",
6053
- flexShrink: 0
6054
- },
6055
- onMouseEnter: (e) => {
6056
- e.currentTarget.style.opacity = "0.9";
6057
- e.currentTarget.style.transform = "translateY(-1px)";
6058
- },
6059
- onMouseLeave: (e) => {
6060
- e.currentTarget.style.opacity = "1";
6061
- e.currentTarget.style.transform = "translateY(0)";
6062
- },
6063
- children: buttonText
6064
- });
6065
- })()]
6066
- })]
6067
- }), /* @__PURE__ */ jsx("button", {
6068
- onClick: () => setBannerError(null),
6069
- style: {
6070
- background: "transparent",
6071
- border: "none",
6072
- color: colors.text,
6073
- cursor: "pointer",
6074
- padding: "2px",
6075
- borderRadius: "3px",
6076
- fontSize: "14px",
6077
- lineHeight: "1",
6078
- opacity: .6,
6079
- transition: "all 0.2s ease",
6080
- flexShrink: 0
6081
- },
6082
- title: "Dismiss",
6083
- onMouseEnter: (e) => {
6084
- e.currentTarget.style.opacity = "1";
6085
- e.currentTarget.style.background = "rgba(0, 0, 0, 0.05)";
6086
- },
6087
- onMouseLeave: (e) => {
6088
- e.currentTarget.style.opacity = "0.6";
6089
- e.currentTarget.style.background = "transparent";
6090
- },
6091
- children: "×"
6092
- })]
6093
- })
6094
- });
6095
- })(), children]
7933
+ children: [bannerError && /* @__PURE__ */ jsx(BannerErrorDisplay, {
7934
+ bannerError,
7935
+ onDismiss: () => setBannerError(null)
7936
+ }), children]
6096
7937
  });
6097
7938
  }
6098
7939
 
@@ -7099,12 +8940,21 @@ function CopilotListeners() {
7099
8940
  const { agent } = useAgent({ agentId: resolvedAgentId });
7100
8941
  usePredictStateSubscription(agent);
7101
8942
  useEffect(() => {
7102
- const subscription = copilotkit.subscribe({ onError: ({ error }) => {
7103
- setBannerError(new CopilotKitLowLevelError({
8943
+ const subscription = copilotkit.subscribe({ onError: ({ error, code, context }) => {
8944
+ if (error.name === "AbortError" || error.message === "Fetch is aborted" || error.message === "signal is aborted without reason" || error.message === "component unmounted" || !error.message) return;
8945
+ if (process.env.NODE_ENV === "development") console.error("[CopilotKit] Agent error:", error.message, "\n Code:", code, "\n Context:", context, "\n Stack:", error.stack);
8946
+ const ckError = new CopilotKitLowLevelError({
7104
8947
  error,
7105
8948
  message: error.message,
7106
8949
  url: typeof window !== "undefined" ? window.location.href : ""
7107
- }));
8950
+ });
8951
+ ckError.details = {
8952
+ code,
8953
+ context,
8954
+ stack: error.stack,
8955
+ originalMessage: error.message
8956
+ };
8957
+ setBannerError(ckError);
7108
8958
  } });
7109
8959
  return () => {
7110
8960
  subscription.unsubscribe();
@@ -7622,5 +9472,5 @@ function validateProps(props) {
7622
9472
  }
7623
9473
 
7624
9474
  //#endregion
7625
- export { CopilotKitCoreReact as $, CopilotChatSuggestionPill as A, useHumanInTheLoop as B, CopilotChatToggleButton as C, CopilotChatView_default as D, CopilotChat as E, useThreads$1 as F, useFrontendTool as G, useRenderTool as H, useInterrupt as I, UseAgentUpdate as J, useRenderActivityMessage as K, useConfigureSuggestions as L, CopilotChatUserMessage_default as M, CopilotChatAssistantMessage_default as N, CopilotChatMessageView as O, CopilotChatToolCallsView as P, useCopilotKit as Q, useSuggestions as R, CopilotModalHeader as S, DefaultOpenIcon as T, defineToolCallRenderer as U, useDefaultRenderTool as V, useComponent as W, useRenderToolCall as X, useAgent as Y, CopilotKitProvider as Z, WildcardToolCallRender as _, ThreadsProvider as a, CopilotChatInput_default as at, CopilotPopupView as b, CoAgentStateRendersProvider as c, CopilotChatConfigurationProvider as ct, shouldShowDevConsole as d, createA2UIMessageRenderer as et, useToast as f, useCopilotContext as g, CopilotContext as h, ThreadsContext as i, CopilotKitInspector as it, CopilotChatReasoningMessage_default as j, CopilotChatSuggestionView as k, useCoAgentStateRenders as l, useCopilotChatConfiguration as lt, useCopilotMessagesContext as m, defaultCopilotContextCategories as n, MCPAppsActivityRenderer as nt, useThreads as o, AudioRecorderError as ot, CopilotMessagesContext as p, useRenderCustomMessages as q, CoAgentStateRenderBridge as r, MCPAppsActivityType as rt, CoAgentStateRendersContext as s, CopilotChatAudioRecorder as st, CopilotKit as t, MCPAppsActivityContentSchema as tt, useAsyncCallback as u, CopilotPopup as v, DefaultCloseIcon as w, CopilotSidebarView as x, CopilotSidebar as y, useAgentContext as z };
7626
- //# sourceMappingURL=copilotkit-DeOzjPsb.mjs.map
9475
+ export { useCopilotKit as $, CopilotChatSuggestionView as A, useConfigureSuggestions as B, CopilotChatToggleButton as C, CopilotChatView_default as D, CopilotChat as E, CopilotChatAssistantMessage_default as F, useComponent as G, useHumanInTheLoop as H, CopilotChatToolCallsView as I, useRenderCustomMessages as J, useFrontendTool as K, useAttachments as L, CopilotChatReasoningMessage_default as M, CopilotChatUserMessage_default as N, CopilotChatAttachmentQueue as O, CopilotChatAttachmentRenderer as P, CopilotKitProvider as Q, useThreads$1 as R, CopilotModalHeader as S, DefaultOpenIcon as T, useDefaultRenderTool as U, useSuggestions as V, useRenderTool as W, useAgent as X, UseAgentUpdate as Y, useRenderToolCall as Z, WildcardToolCallRender as _, ThreadsProvider as a, useSandboxFunctions as at, CopilotPopupView as b, CoAgentStateRendersProvider as c, MCPAppsActivityType as ct, shouldShowDevConsole as d, AudioRecorderError as dt, CopilotKitCoreReact as et, useToast as f, CopilotChatAudioRecorder as ft, useCopilotContext as g, CopilotContext as h, ThreadsContext as i, SandboxFunctionsContext as it, CopilotChatSuggestionPill as j, CopilotChatMessageView as k, useCoAgentStateRenders as l, CopilotKitInspector as lt, useCopilotMessagesContext as m, useCopilotChatConfiguration as mt, defaultCopilotContextCategories as n, defineToolCallRenderer as nt, useThreads as o, MCPAppsActivityContentSchema as ot, CopilotMessagesContext as p, CopilotChatConfigurationProvider as pt, useRenderActivityMessage as q, CoAgentStateRenderBridge as r, createA2UIMessageRenderer as rt, CoAgentStateRendersContext as s, MCPAppsActivityRenderer as st, CopilotKit as t, useAgentContext as tt, useAsyncCallback as u, CopilotChatInput_default as ut, CopilotPopup as v, DefaultCloseIcon as w, CopilotSidebarView as x, CopilotSidebar as y, useInterrupt as z };
9476
+ //# sourceMappingURL=copilotkit-BY5S1-0P.mjs.map