@copilotkit/react-core 1.55.3 → 1.56.1

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 (53) hide show
  1. package/dist/{copilotkit-dwDWYpya.d.cts → copilotkit-BtP7w7cT.d.cts} +56 -10
  2. package/dist/copilotkit-BtP7w7cT.d.cts.map +1 -0
  3. package/dist/{copilotkit-BuhSUZHb.d.mts → copilotkit-CCbxm6JM.d.mts} +56 -10
  4. package/dist/copilotkit-CCbxm6JM.d.mts.map +1 -0
  5. package/dist/{copilotkit-Dgdpbqjt.cjs → copilotkit-CSJw5BG8.cjs} +129 -58
  6. package/dist/copilotkit-CSJw5BG8.cjs.map +1 -0
  7. package/dist/{copilotkit-Cd-NrDyp.mjs → copilotkit-Cj2ZIxVr.mjs} +125 -60
  8. package/dist/copilotkit-Cj2ZIxVr.mjs.map +1 -0
  9. package/dist/index.cjs +1 -1
  10. package/dist/index.d.cts +1 -1
  11. package/dist/index.d.mts +1 -1
  12. package/dist/index.mjs +1 -1
  13. package/dist/index.umd.js +55 -23
  14. package/dist/index.umd.js.map +1 -1
  15. package/dist/v2/index.cjs +2 -1
  16. package/dist/v2/index.d.cts +2 -2
  17. package/dist/v2/index.d.mts +2 -2
  18. package/dist/v2/index.mjs +2 -2
  19. package/dist/v2/index.umd.js +124 -59
  20. package/dist/v2/index.umd.js.map +1 -1
  21. package/package.json +6 -6
  22. package/src/components/CopilotListeners.tsx +15 -4
  23. package/src/components/__tests__/CopilotListeners.test.tsx +38 -0
  24. package/src/components/copilot-provider/__tests__/error-visibility-prod.test.tsx +70 -0
  25. package/src/components/copilot-provider/copilot-messages.tsx +39 -24
  26. package/src/components/copilot-provider/copilotkit-props.tsx +26 -6
  27. package/src/components/copilot-provider/copilotkit.tsx +4 -1
  28. package/src/v2/components/chat/CopilotChatAssistantMessage.tsx +22 -19
  29. package/src/v2/components/chat/CopilotChatInput.tsx +21 -2
  30. package/src/v2/components/chat/CopilotChatReasoningMessage.tsx +17 -4
  31. package/src/v2/components/chat/CopilotChatUserMessage.tsx +13 -10
  32. package/src/v2/components/chat/__tests__/CopilotChat.e2e.test.tsx +131 -5
  33. package/src/v2/components/chat/__tests__/CopilotChatAssistantMessage.test.tsx +1 -1
  34. package/src/v2/components/chat/__tests__/CopilotChatAssistantMessage.thumbs.test.tsx +72 -0
  35. package/src/v2/components/chat/__tests__/CopilotChatCopyButton.clipboard.test.tsx +241 -0
  36. package/src/v2/components/chat/__tests__/CopilotChatInput.test.tsx +38 -0
  37. package/src/v2/components/ui/button.tsx +12 -11
  38. package/src/v2/hooks/__tests__/use-agent-throttle.test.tsx +10 -10
  39. package/src/v2/hooks/__tests__/use-capabilities.test.tsx +76 -0
  40. package/src/v2/hooks/__tests__/use-render-custom-messages.test.tsx +55 -0
  41. package/src/v2/hooks/index.ts +1 -0
  42. package/src/v2/hooks/use-agent.tsx +23 -4
  43. package/src/v2/hooks/use-capabilities.tsx +25 -0
  44. package/src/v2/hooks/use-render-custom-messages.tsx +1 -1
  45. package/src/v2/hooks/use-render-tool-call.tsx +3 -0
  46. package/src/v2/hooks/use-render-tool.tsx +3 -0
  47. package/src/v2/providers/CopilotKitProvider.tsx +15 -2
  48. package/src/v2/types/defineToolCallRenderer.ts +3 -0
  49. package/src/v2/types/react-tool-call-renderer.ts +3 -0
  50. package/dist/copilotkit-BuhSUZHb.d.mts.map +0 -1
  51. package/dist/copilotkit-Cd-NrDyp.mjs.map +0 -1
  52. package/dist/copilotkit-Dgdpbqjt.cjs.map +0 -1
  53. package/dist/copilotkit-dwDWYpya.d.cts.map +0 -1
@@ -4,7 +4,7 @@ import { CopilotKitCore, CopilotKitCoreRuntimeConnectionStatus, ProxiedCopilotRu
4
4
  import { HttpAgent } from "@ag-ui/client";
5
5
  import { extendTailwindMerge, twMerge } from "tailwind-merge";
6
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";
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, copyToClipboard, 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";
@@ -269,8 +269,9 @@ const buttonVariants = cva("cpk:inline-flex cpk:items-center cpk:justify-center
269
269
  size: "default"
270
270
  }
271
271
  });
272
- function Button({ className, variant, size, asChild = false, ...props }) {
272
+ const Button = React$1.forwardRef(function Button({ className, variant, size, asChild = false, ...props }, ref) {
273
273
  return /* @__PURE__ */ jsx(asChild ? Slot : "button", {
274
+ ref,
274
275
  "data-slot": "button",
275
276
  className: cn(buttonVariants({
276
277
  variant,
@@ -279,7 +280,7 @@ function Button({ className, variant, size, asChild = false, ...props }) {
279
280
  })),
280
281
  ...props
281
282
  });
282
- }
283
+ });
283
284
 
284
285
  //#endregion
285
286
  //#region src/v2/components/ui/tooltip.tsx
@@ -721,6 +722,7 @@ function CopilotChatInput({ mode = "input", onSubmitMessage, onStop, isRunning =
721
722
  });
722
723
  }, [clearInputValue]);
723
724
  const handleKeyDown = (e) => {
725
+ if (e.nativeEvent.isComposing || e.keyCode === 229) return;
724
726
  if (commandQuery !== null && mode === "input") {
725
727
  if (e.key === "ArrowDown") {
726
728
  if (filteredCommands.length > 0) {
@@ -768,10 +770,8 @@ function CopilotChatInput({ mode = "input", onSubmitMessage, onStop, isRunning =
768
770
  const trimmed = resolvedValue.trim();
769
771
  if (!trimmed) return;
770
772
  onSubmitMessage(trimmed);
771
- if (!isControlled) {
772
- setInternalValue("");
773
- onChange?.("");
774
- }
773
+ if (!isControlled) setInternalValue("");
774
+ onChange?.("");
775
775
  if (inputRef.current) inputRef.current.focus();
776
776
  };
777
777
  const BoundTextArea = renderSlot(textArea, CopilotChatInput.TextArea, {
@@ -779,6 +779,12 @@ function CopilotChatInput({ mode = "input", onSubmitMessage, onStop, isRunning =
779
779
  value: resolvedValue,
780
780
  onChange: handleChange,
781
781
  onKeyDown: handleKeyDown,
782
+ onCompositionStart: () => {
783
+ isComposingRef.current = true;
784
+ },
785
+ onCompositionEnd: () => {
786
+ isComposingRef.current = false;
787
+ },
782
788
  autoFocus,
783
789
  className: twMerge("cpk:w-full cpk:py-3", isExpanded ? "cpk:px-5" : "cpk:pr-5")
784
790
  });
@@ -853,9 +859,10 @@ function CopilotChatInput({ mode = "input", onSubmitMessage, onStop, isRunning =
853
859
  const target = e.target;
854
860
  if (target.tagName !== "BUTTON" && !target.closest("button") && inputRef.current && mode === "input") inputRef.current.focus();
855
861
  };
862
+ const isComposingRef = useRef(false);
856
863
  const ensureMeasurements = useCallback(() => {
857
864
  const textarea = inputRef.current;
858
- if (!textarea) return;
865
+ if (!textarea || isComposingRef.current) return;
859
866
  const previousValue = textarea.value;
860
867
  const previousHeight = textarea.style.height;
861
868
  textarea.style.height = "auto";
@@ -3042,7 +3049,7 @@ function useStableArrayProp(prop, warningMessage, isMeaningfulChange) {
3042
3049
  }, [value, warningMessage]);
3043
3050
  return value;
3044
3051
  }
3045
- 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 }) => {
3052
+ const CopilotKitProvider = ({ children, runtimeUrl, headers: headersProp = {}, credentials, publicApiKey, publicLicenseKey, licenseToken, properties = {}, agents__unsafe_dev_only: agents = {}, selfManagedAgents = {}, renderToolCalls, renderActivityMessages, renderCustomMessages, frontendTools, humanInTheLoop, openGenerativeUI, showDevConsole = false, useSingleEndpoint, onError, a2ui, defaultThrottleMs, inspectorDefaultAnchor, debug }) => {
3046
3053
  const [shouldRenderInspector, setShouldRenderInspector] = useState(false);
3047
3054
  const [runtimeA2UIEnabled, setRuntimeA2UIEnabled] = useState(false);
3048
3055
  const [runtimeOpenGenUIEnabled, setRuntimeOpenGenUIEnabled] = useState(false);
@@ -3097,6 +3104,7 @@ const CopilotKitProvider = ({ children, runtimeUrl, headers = {}, credentials, p
3097
3104
  ...selfManagedAgents
3098
3105
  }), [agents, selfManagedAgents]);
3099
3106
  const hasLocalAgents = mergedAgents && Object.keys(mergedAgents).length > 0;
3107
+ const headers = typeof headersProp === "function" ? headersProp() : headersProp;
3100
3108
  const mergedHeaders = useMemo(() => {
3101
3109
  if (!resolvedPublicKey) return headers;
3102
3110
  if (headers[HEADER_NAME]) return headers;
@@ -3198,7 +3206,8 @@ const CopilotKitProvider = ({ children, runtimeUrl, headers = {}, credentials, p
3198
3206
  tools: allTools,
3199
3207
  renderToolCalls: allRenderToolCalls,
3200
3208
  renderActivityMessages: allActivityRenderers,
3201
- renderCustomMessages: renderCustomMessagesList
3209
+ renderCustomMessages: renderCustomMessagesList,
3210
+ debug
3202
3211
  });
3203
3212
  if (defaultThrottleMs !== void 0) copilotkitRef.current.setDefaultThrottleMs(defaultThrottleMs);
3204
3213
  }
@@ -3271,6 +3280,7 @@ const CopilotKitProvider = ({ children, runtimeUrl, headers = {}, credentials, p
3271
3280
  copilotkit.setCredentials(credentials);
3272
3281
  copilotkit.setProperties(properties);
3273
3282
  copilotkit.setAgents__unsafe_dev_only(mergedAgents);
3283
+ copilotkit.setDebug(debug);
3274
3284
  }, [
3275
3285
  copilotkit,
3276
3286
  chatApiEndpoint,
@@ -3278,7 +3288,8 @@ const CopilotKitProvider = ({ children, runtimeUrl, headers = {}, credentials, p
3278
3288
  credentials,
3279
3289
  properties,
3280
3290
  mergedAgents,
3281
- useSingleEndpoint
3291
+ useSingleEndpoint,
3292
+ debug
3282
3293
  ]);
3283
3294
  const didMountRef = useRef(false);
3284
3295
  useEffect(() => {
@@ -3399,18 +3410,21 @@ const ToolCallRenderer = React.memo(function ToolCallRenderer({ toolCall, toolMe
3399
3410
  const toolName = toolCall.function.name;
3400
3411
  if (toolMessage) return /* @__PURE__ */ jsx(RenderComponent, {
3401
3412
  name: toolName,
3413
+ toolCallId: toolCall.id,
3402
3414
  args,
3403
3415
  status: ToolCallStatus.Complete,
3404
3416
  result: toolMessage.content
3405
3417
  });
3406
3418
  else if (isExecuting) return /* @__PURE__ */ jsx(RenderComponent, {
3407
3419
  name: toolName,
3420
+ toolCallId: toolCall.id,
3408
3421
  args,
3409
3422
  status: ToolCallStatus.Executing,
3410
3423
  result: void 0
3411
3424
  });
3412
3425
  else return /* @__PURE__ */ jsx(RenderComponent, {
3413
3426
  name: toolName,
3427
+ toolCallId: toolCall.id,
3414
3428
  args,
3415
3429
  status: ToolCallStatus.InProgress,
3416
3430
  result: void 0
@@ -3589,6 +3603,17 @@ function useAgent({ agentId, threadId, updates, throttleMs } = {}) {
3589
3603
  const handlers = {};
3590
3604
  let timerId = null;
3591
3605
  let active = true;
3606
+ let batchScheduled = false;
3607
+ const batchedForceUpdate = () => {
3608
+ if (!active) return;
3609
+ if (!batchScheduled) {
3610
+ batchScheduled = true;
3611
+ queueMicrotask(() => {
3612
+ batchScheduled = false;
3613
+ if (active) forceUpdate();
3614
+ });
3615
+ }
3616
+ };
3592
3617
  if (updateFlags.includes(UseAgentUpdate.OnMessagesChanged)) {
3593
3618
  const ms = effectiveThrottleMs;
3594
3619
  if (ms > 0) {
@@ -3613,11 +3638,11 @@ function useAgent({ agentId, threadId, updates, throttleMs } = {}) {
3613
3638
  handlers.onMessagesChanged = throttledNotify;
3614
3639
  } else handlers.onMessagesChanged = forceUpdate;
3615
3640
  }
3616
- if (updateFlags.includes(UseAgentUpdate.OnStateChanged)) handlers.onStateChanged = forceUpdate;
3641
+ if (updateFlags.includes(UseAgentUpdate.OnStateChanged)) handlers.onStateChanged = batchedForceUpdate;
3617
3642
  if (updateFlags.includes(UseAgentUpdate.OnRunStatusChanged)) {
3618
- handlers.onRunInitialized = forceUpdate;
3619
- handlers.onRunFinalized = forceUpdate;
3620
- handlers.onRunFailed = forceUpdate;
3643
+ handlers.onRunInitialized = batchedForceUpdate;
3644
+ handlers.onRunFinalized = batchedForceUpdate;
3645
+ handlers.onRunFailed = batchedForceUpdate;
3621
3646
  }
3622
3647
  const subscription = agent.subscribe(handlers);
3623
3648
  return () => {
@@ -3656,7 +3681,7 @@ function useRenderCustomMessages() {
3656
3681
  const runId = resolvedRunId ?? `missing-run-id:${message.id}`;
3657
3682
  const registryAgent = copilotkit.getAgent(agentId);
3658
3683
  const agent = getThreadClone(registryAgent, threadId) ?? registryAgent;
3659
- if (!agent) throw new Error("Agent not found");
3684
+ if (!agent) return null;
3660
3685
  const messagesIdsInRun = resolvedRunId ? agent.messages.filter((msg) => copilotkit.getRunIdForMessage(agentId, threadId, msg.id) === resolvedRunId).map((msg) => msg.id) : [message.id];
3661
3686
  const rawMessageIndex = agent.messages.findIndex((msg) => msg.id === message.id);
3662
3687
  const messageIndex = rawMessageIndex >= 0 ? rawMessageIndex : 0;
@@ -4154,6 +4179,24 @@ function useHumanInTheLoop(tool, deps) {
4154
4179
  ]);
4155
4180
  }
4156
4181
 
4182
+ //#endregion
4183
+ //#region src/v2/hooks/use-capabilities.tsx
4184
+ /**
4185
+ * Returns the capabilities declared by the given agent (or the default agent).
4186
+ * Capabilities are populated from the runtime `/info` response at connection
4187
+ * time. The hook reads them synchronously from the agent instance — there is
4188
+ * no separate loading state, but the value will be `undefined` until the
4189
+ * runtime handshake completes.
4190
+ *
4191
+ * @param agentId - Optional agent ID. If omitted, uses the default agent.
4192
+ * @returns The agent's capabilities, or `undefined` if the agent doesn't
4193
+ * declare capabilities.
4194
+ */
4195
+ function useCapabilities(agentId) {
4196
+ const { agent } = useAgent({ agentId });
4197
+ if (agent && "capabilities" in agent) return agent.capabilities;
4198
+ }
4199
+
4157
4200
  //#endregion
4158
4201
  //#region src/v2/hooks/use-suggestions.tsx
4159
4202
  function useSuggestions({ agentId } = {}) {
@@ -4805,16 +4848,13 @@ function CopilotChatAssistantMessage({ message, messages, isRunning, onThumbsUp,
4805
4848
  useKatexStyles();
4806
4849
  const boundMarkdownRenderer = renderSlot(markdownRenderer, CopilotChatAssistantMessage.MarkdownRenderer, { content: message.content || "" });
4807
4850
  const boundCopyButton = renderSlot(copyButton, CopilotChatAssistantMessage.CopyButton, { onClick: async () => {
4808
- if (message.content) try {
4809
- await navigator.clipboard.writeText(message.content);
4810
- } catch (err) {
4811
- console.error("Failed to copy message:", err);
4812
- }
4851
+ if (message.content) return await copyToClipboard(message.content);
4852
+ return false;
4813
4853
  } });
4814
- const boundThumbsUpButton = renderSlot(thumbsUpButton, CopilotChatAssistantMessage.ThumbsUpButton, { onClick: onThumbsUp });
4815
- const boundThumbsDownButton = renderSlot(thumbsDownButton, CopilotChatAssistantMessage.ThumbsDownButton, { onClick: onThumbsDown });
4816
- const boundReadAloudButton = renderSlot(readAloudButton, CopilotChatAssistantMessage.ReadAloudButton, { onClick: onReadAloud });
4817
- const boundRegenerateButton = renderSlot(regenerateButton, CopilotChatAssistantMessage.RegenerateButton, { onClick: onRegenerate });
4854
+ const boundThumbsUpButton = renderSlot(thumbsUpButton, CopilotChatAssistantMessage.ThumbsUpButton, { onClick: onThumbsUp ? () => onThumbsUp(message) : void 0 });
4855
+ const boundThumbsDownButton = renderSlot(thumbsDownButton, CopilotChatAssistantMessage.ThumbsDownButton, { onClick: onThumbsDown ? () => onThumbsDown(message) : void 0 });
4856
+ const boundReadAloudButton = renderSlot(readAloudButton, CopilotChatAssistantMessage.ReadAloudButton, { onClick: onReadAloud ? () => onReadAloud(message) : void 0 });
4857
+ const boundRegenerateButton = renderSlot(regenerateButton, CopilotChatAssistantMessage.RegenerateButton, { onClick: onRegenerate ? () => onRegenerate(message) : void 0 });
4818
4858
  const boundToolbar = renderSlot(toolbar, CopilotChatAssistantMessage.Toolbar, { children: /* @__PURE__ */ jsxs("div", {
4819
4859
  className: "cpk:flex cpk:items-center cpk:gap-1",
4820
4860
  children: [
@@ -4907,14 +4947,17 @@ function CopilotChatAssistantMessage({ message, messages, isRunning, onThumbsUp,
4907
4947
  if (timerRef.current !== null) clearTimeout(timerRef.current);
4908
4948
  };
4909
4949
  }, []);
4910
- const handleClick = (event) => {
4911
- setCopied(true);
4912
- if (timerRef.current !== null) clearTimeout(timerRef.current);
4913
- timerRef.current = setTimeout(() => {
4914
- timerRef.current = null;
4915
- setCopied(false);
4916
- }, 2e3);
4917
- if (onClick) onClick(event);
4950
+ const handleClick = async (event) => {
4951
+ let success = false;
4952
+ if (onClick) success = await Promise.resolve(onClick(event)) === true;
4953
+ if (success) {
4954
+ setCopied(true);
4955
+ if (timerRef.current !== null) clearTimeout(timerRef.current);
4956
+ timerRef.current = setTimeout(() => {
4957
+ timerRef.current = null;
4958
+ setCopied(false);
4959
+ }, 2e3);
4960
+ }
4918
4961
  };
4919
4962
  return /* @__PURE__ */ jsx(ToolbarButton, {
4920
4963
  "data-testid": "copilot-copy-button",
@@ -5067,11 +5110,8 @@ function CopilotChatUserMessage({ message, onEditMessage, branchIndex, numberOfB
5067
5110
  const mediaParts = useMemo(() => getMediaParts(message.content), [message.content]);
5068
5111
  const BoundMessageRenderer = renderSlot(messageRenderer, CopilotChatUserMessage.MessageRenderer, { content: flattenedContent });
5069
5112
  const BoundCopyButton = renderSlot(copyButton, CopilotChatUserMessage.CopyButton, { onClick: async () => {
5070
- if (flattenedContent) try {
5071
- await navigator.clipboard.writeText(flattenedContent);
5072
- } catch (err) {
5073
- console.error("Failed to copy message:", err);
5074
- }
5113
+ if (flattenedContent) return await copyToClipboard(flattenedContent);
5114
+ return false;
5075
5115
  } });
5076
5116
  const BoundEditButton = renderSlot(editButton, CopilotChatUserMessage.EditButton, { onClick: () => onEditMessage?.({ message }) });
5077
5117
  const BoundBranchNavigation = renderSlot(branchNavigation, CopilotChatUserMessage.BranchNavigation, {
@@ -5159,10 +5199,13 @@ function CopilotChatUserMessage({ message, onEditMessage, branchIndex, numberOfB
5159
5199
  _CopilotChatUserMessage.CopyButton = ({ className, title, onClick, ...props }) => {
5160
5200
  const labels = useCopilotChatConfiguration()?.labels ?? CopilotChatDefaultLabels;
5161
5201
  const [copied, setCopied] = useState(false);
5162
- const handleClick = (event) => {
5163
- setCopied(true);
5164
- setTimeout(() => setCopied(false), 2e3);
5165
- if (onClick) onClick(event);
5202
+ const handleClick = async (event) => {
5203
+ let success = false;
5204
+ if (onClick) success = await Promise.resolve(onClick(event)) === true;
5205
+ if (success) {
5206
+ setCopied(true);
5207
+ setTimeout(() => setCopied(false), 2e3);
5208
+ }
5166
5209
  };
5167
5210
  return /* @__PURE__ */ jsx(ToolbarButton, {
5168
5211
  "data-testid": "copilot-user-copy-button",
@@ -5269,17 +5312,24 @@ function CopilotChatReasoningMessage({ message, messages, isRunning, header, con
5269
5312
  return () => clearInterval(timer);
5270
5313
  }, [isStreaming]);
5271
5314
  const [isOpen, setIsOpen] = useState(isStreaming);
5315
+ const userToggledRef = useRef(false);
5272
5316
  useEffect(() => {
5273
- if (isStreaming) setIsOpen(true);
5274
- else setIsOpen(false);
5317
+ if (isStreaming) {
5318
+ userToggledRef.current = false;
5319
+ setIsOpen(true);
5320
+ } else if (!userToggledRef.current) setIsOpen(false);
5275
5321
  }, [isStreaming]);
5322
+ const handleToggle = hasContent ? () => {
5323
+ userToggledRef.current = true;
5324
+ setIsOpen((prev) => !prev);
5325
+ } : void 0;
5276
5326
  const label = isStreaming ? "Thinking…" : `Thought for ${formatDuration(elapsed)}`;
5277
5327
  const boundHeader = renderSlot(header, CopilotChatReasoningMessage.Header, {
5278
5328
  isOpen,
5279
5329
  label,
5280
5330
  hasContent,
5281
5331
  isStreaming,
5282
- onClick: hasContent ? () => setIsOpen((prev) => !prev) : void 0
5332
+ onClick: handleToggle
5283
5333
  });
5284
5334
  const boundContent = renderSlot(contentView, CopilotChatReasoningMessage.Content, {
5285
5335
  isStreaming,
@@ -7983,6 +8033,20 @@ function shouldShowDevConsole(showDevConsole) {
7983
8033
  /**
7984
8034
  * An internal context to separate the messages state (which is constantly changing) from the rest of CopilotKit context
7985
8035
  */
8036
+ /**
8037
+ * Determine whether a GraphQL error should be suppressed based on its visibility
8038
+ * and whether the dev console is active.
8039
+ *
8040
+ * Returns `null` when the error should be surfaced to the UI, or a log prefix
8041
+ * string when the error should be suppressed (logged to console only).
8042
+ *
8043
+ * Exported for unit testing.
8044
+ */
8045
+ function getErrorSuppression(visibility, isDev) {
8046
+ if (visibility === ErrorVisibility.SILENT) return "CopilotKit Silent Error:";
8047
+ if (!isDev && visibility === ErrorVisibility.DEV_ONLY) return "CopilotKit Error (hidden in production):";
8048
+ return null;
8049
+ }
7986
8050
  const MessagesTapContext = createContext(null);
7987
8051
  function useMessagesTap() {
7988
8052
  const tap = useContext(MessagesTapContext);
@@ -8066,12 +8130,9 @@ function CopilotMessages({ children }) {
8066
8130
  const graphQLErrors = error.graphQLErrors;
8067
8131
  const routeError = (gqlError) => {
8068
8132
  const visibility = gqlError.extensions?.visibility;
8069
- if (!shouldShowDevConsole(showDevConsole)) {
8070
- console.error("CopilotKit Error (hidden in production):", gqlError.message);
8071
- return;
8072
- }
8073
- if (visibility === ErrorVisibility.SILENT) {
8074
- console.error("CopilotKit Silent Error:", gqlError.message);
8133
+ const suppression = getErrorSuppression(visibility, shouldShowDevConsole(showDevConsole));
8134
+ if (suppression) {
8135
+ console.error(suppression, gqlError.message);
8075
8136
  return;
8076
8137
  }
8077
8138
  const ckError = createStructuredError(gqlError);
@@ -8088,8 +8149,7 @@ function CopilotMessages({ children }) {
8088
8149
  }
8089
8150
  };
8090
8151
  graphQLErrors.forEach(routeError);
8091
- } else if (!shouldShowDevConsole(showDevConsole)) console.error("CopilotKit Error (hidden in production):", error);
8092
- else {
8152
+ } else {
8093
8153
  const fallbackError = new CopilotKitError({
8094
8154
  message: error?.message || String(error),
8095
8155
  code: CopilotKitErrorCode.UNKNOWN
@@ -8963,12 +9023,17 @@ const usePredictStateSubscription = (agent) => {
8963
9023
  };
8964
9024
  }, [agent, getSubscriber]);
8965
9025
  };
8966
- function CopilotListeners() {
8967
- const { copilotkit } = useCopilotKit();
9026
+ function CopilotListenersAgentSubscription() {
8968
9027
  const resolvedAgentId = useCopilotChatConfiguration()?.agentId;
8969
- const { setBannerError } = useToast();
8970
9028
  const { agent } = useAgent({ agentId: resolvedAgentId });
8971
9029
  usePredictStateSubscription(agent);
9030
+ return null;
9031
+ }
9032
+ function CopilotListeners() {
9033
+ const { copilotkit } = useCopilotKit();
9034
+ const { setBannerError } = useToast();
9035
+ const hasAgents = Object.keys(copilotkit.agents ?? {}).length > 0;
9036
+ const hasRuntime = copilotkit.runtimeUrl !== void 0;
8972
9037
  useEffect(() => {
8973
9038
  const subscription = copilotkit.subscribe({ onError: ({ error, code, context }) => {
8974
9039
  if (error.name === "AbortError" || error.message === "Fetch is aborted" || error.message === "signal is aborted without reason" || error.message === "component unmounted" || !error.message) return;
@@ -8990,7 +9055,7 @@ function CopilotListeners() {
8990
9055
  subscription.unsubscribe();
8991
9056
  };
8992
9057
  }, [copilotkit?.subscribe]);
8993
- return null;
9058
+ return hasAgents || hasRuntime ? /* @__PURE__ */ jsx(CopilotListenersAgentSubscription, {}) : null;
8994
9059
  }
8995
9060
 
8996
9061
  //#endregion
@@ -9156,7 +9221,7 @@ function CopilotKitInternal(cpkProps) {
9156
9221
  publicApiKey,
9157
9222
  ...cloud ? { cloud } : {},
9158
9223
  chatApiEndpoint,
9159
- headers: props.headers || {},
9224
+ headers: typeof props.headers === "function" ? props.headers() : props.headers || {},
9160
9225
  properties: props.properties || {},
9161
9226
  transcribeAudioUrl: props.transcribeAudioUrl,
9162
9227
  textToSpeechUrl: props.textToSpeechUrl,
@@ -9502,5 +9567,5 @@ function validateProps(props) {
9502
9567
  }
9503
9568
 
9504
9569
  //#endregion
9505
- 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 };
9506
- //# sourceMappingURL=copilotkit-Cd-NrDyp.mjs.map
9570
+ export { CopilotKitProvider as $, CopilotChatSuggestionView as A, useConfigureSuggestions as B, CopilotChatToggleButton as C, CopilotChatView_default as D, CopilotChat as E, CopilotChatAssistantMessage_default as F, useRenderTool as G, useCapabilities as H, CopilotChatToolCallsView as I, useRenderActivityMessage as J, useComponent as K, useAttachments as L, CopilotChatReasoningMessage_default as M, CopilotChatUserMessage_default as N, CopilotChatAttachmentQueue as O, CopilotChatAttachmentRenderer as P, useRenderToolCall as Q, useThreads$1 as R, CopilotModalHeader as S, DefaultOpenIcon as T, useHumanInTheLoop as U, useSuggestions as V, useDefaultRenderTool as W, UseAgentUpdate as X, useRenderCustomMessages as Y, useAgent as Z, WildcardToolCallRender as _, ThreadsProvider as a, SandboxFunctionsContext as at, CopilotPopupView as b, CoAgentStateRendersProvider as c, MCPAppsActivityRenderer as ct, shouldShowDevConsole as d, CopilotChatInput_default as dt, useCopilotKit as et, useToast as f, AudioRecorderError as ft, useCopilotContext as g, CopilotContext as h, useCopilotChatConfiguration as ht, ThreadsContext as i, createA2UIMessageRenderer as it, CopilotChatSuggestionPill as j, CopilotChatMessageView as k, useCoAgentStateRenders as l, MCPAppsActivityType as lt, useCopilotMessagesContext as m, CopilotChatConfigurationProvider as mt, defaultCopilotContextCategories as n, useAgentContext as nt, useThreads as o, useSandboxFunctions as ot, CopilotMessagesContext as p, CopilotChatAudioRecorder as pt, useFrontendTool as q, CoAgentStateRenderBridge as r, defineToolCallRenderer as rt, CoAgentStateRendersContext as s, MCPAppsActivityContentSchema as st, CopilotKit as t, CopilotKitCoreReact as tt, useAsyncCallback as u, CopilotKitInspector as ut, CopilotPopup as v, DefaultCloseIcon as w, CopilotSidebarView as x, CopilotSidebar as y, useInterrupt as z };
9571
+ //# sourceMappingURL=copilotkit-Cj2ZIxVr.mjs.map