@copilotkit/react-core 1.55.3 → 1.56.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 (41) hide show
  1. package/dist/{copilotkit-Cd-NrDyp.mjs → copilotkit-BebqQrYT.mjs} +94 -44
  2. package/dist/copilotkit-BebqQrYT.mjs.map +1 -0
  3. package/dist/{copilotkit-Dgdpbqjt.cjs → copilotkit-Cvb6WpAX.cjs} +98 -42
  4. package/dist/copilotkit-Cvb6WpAX.cjs.map +1 -0
  5. package/dist/{copilotkit-dwDWYpya.d.cts → copilotkit-Dv8zU8_U.d.cts} +47 -10
  6. package/dist/copilotkit-Dv8zU8_U.d.cts.map +1 -0
  7. package/dist/{copilotkit-BuhSUZHb.d.mts → copilotkit-f2Uq0RwG.d.mts} +47 -10
  8. package/dist/copilotkit-f2Uq0RwG.d.mts.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 +41 -17
  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 +92 -42
  20. package/dist/v2/index.umd.js.map +1 -1
  21. package/package.json +6 -6
  22. package/src/components/copilot-provider/__tests__/error-visibility-prod.test.tsx +70 -0
  23. package/src/components/copilot-provider/copilot-messages.tsx +39 -24
  24. package/src/components/copilot-provider/copilotkit-props.tsx +26 -6
  25. package/src/components/copilot-provider/copilotkit.tsx +4 -1
  26. package/src/v2/components/chat/CopilotChatAssistantMessage.tsx +18 -15
  27. package/src/v2/components/chat/CopilotChatReasoningMessage.tsx +17 -4
  28. package/src/v2/components/chat/CopilotChatUserMessage.tsx +13 -10
  29. package/src/v2/components/chat/__tests__/CopilotChat.e2e.test.tsx +131 -5
  30. package/src/v2/components/chat/__tests__/CopilotChatAssistantMessage.test.tsx +1 -1
  31. package/src/v2/components/chat/__tests__/CopilotChatCopyButton.clipboard.test.tsx +241 -0
  32. package/src/v2/hooks/__tests__/use-agent-throttle.test.tsx +10 -10
  33. package/src/v2/hooks/__tests__/use-capabilities.test.tsx +76 -0
  34. package/src/v2/hooks/index.ts +1 -0
  35. package/src/v2/hooks/use-agent.tsx +23 -4
  36. package/src/v2/hooks/use-capabilities.tsx +25 -0
  37. package/src/v2/providers/CopilotKitProvider.tsx +15 -2
  38. package/dist/copilotkit-BuhSUZHb.d.mts.map +0 -1
  39. package/dist/copilotkit-Cd-NrDyp.mjs.map +0 -1
  40. package/dist/copilotkit-Dgdpbqjt.cjs.map +0 -1
  41. package/dist/copilotkit-dwDWYpya.d.cts.map +0 -1
@@ -3072,7 +3072,7 @@ function useStableArrayProp(prop, warningMessage, isMeaningfulChange) {
3072
3072
  }, [value, warningMessage]);
3073
3073
  return value;
3074
3074
  }
3075
- 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 }) => {
3075
+ 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 }) => {
3076
3076
  const [shouldRenderInspector, setShouldRenderInspector] = (0, react.useState)(false);
3077
3077
  const [runtimeA2UIEnabled, setRuntimeA2UIEnabled] = (0, react.useState)(false);
3078
3078
  const [runtimeOpenGenUIEnabled, setRuntimeOpenGenUIEnabled] = (0, react.useState)(false);
@@ -3127,6 +3127,7 @@ const CopilotKitProvider = ({ children, runtimeUrl, headers = {}, credentials, p
3127
3127
  ...selfManagedAgents
3128
3128
  }), [agents, selfManagedAgents]);
3129
3129
  const hasLocalAgents = mergedAgents && Object.keys(mergedAgents).length > 0;
3130
+ const headers = typeof headersProp === "function" ? headersProp() : headersProp;
3130
3131
  const mergedHeaders = (0, react.useMemo)(() => {
3131
3132
  if (!resolvedPublicKey) return headers;
3132
3133
  if (headers[HEADER_NAME]) return headers;
@@ -3228,7 +3229,8 @@ const CopilotKitProvider = ({ children, runtimeUrl, headers = {}, credentials, p
3228
3229
  tools: allTools,
3229
3230
  renderToolCalls: allRenderToolCalls,
3230
3231
  renderActivityMessages: allActivityRenderers,
3231
- renderCustomMessages: renderCustomMessagesList
3232
+ renderCustomMessages: renderCustomMessagesList,
3233
+ debug
3232
3234
  });
3233
3235
  if (defaultThrottleMs !== void 0) copilotkitRef.current.setDefaultThrottleMs(defaultThrottleMs);
3234
3236
  }
@@ -3301,6 +3303,7 @@ const CopilotKitProvider = ({ children, runtimeUrl, headers = {}, credentials, p
3301
3303
  copilotkit.setCredentials(credentials);
3302
3304
  copilotkit.setProperties(properties);
3303
3305
  copilotkit.setAgents__unsafe_dev_only(mergedAgents);
3306
+ copilotkit.setDebug(debug);
3304
3307
  }, [
3305
3308
  copilotkit,
3306
3309
  chatApiEndpoint,
@@ -3308,7 +3311,8 @@ const CopilotKitProvider = ({ children, runtimeUrl, headers = {}, credentials, p
3308
3311
  credentials,
3309
3312
  properties,
3310
3313
  mergedAgents,
3311
- useSingleEndpoint
3314
+ useSingleEndpoint,
3315
+ debug
3312
3316
  ]);
3313
3317
  const didMountRef = (0, react.useRef)(false);
3314
3318
  (0, react.useEffect)(() => {
@@ -3619,6 +3623,17 @@ function useAgent({ agentId, threadId, updates, throttleMs } = {}) {
3619
3623
  const handlers = {};
3620
3624
  let timerId = null;
3621
3625
  let active = true;
3626
+ let batchScheduled = false;
3627
+ const batchedForceUpdate = () => {
3628
+ if (!active) return;
3629
+ if (!batchScheduled) {
3630
+ batchScheduled = true;
3631
+ queueMicrotask(() => {
3632
+ batchScheduled = false;
3633
+ if (active) forceUpdate();
3634
+ });
3635
+ }
3636
+ };
3622
3637
  if (updateFlags.includes(UseAgentUpdate.OnMessagesChanged)) {
3623
3638
  const ms = effectiveThrottleMs;
3624
3639
  if (ms > 0) {
@@ -3643,11 +3658,11 @@ function useAgent({ agentId, threadId, updates, throttleMs } = {}) {
3643
3658
  handlers.onMessagesChanged = throttledNotify;
3644
3659
  } else handlers.onMessagesChanged = forceUpdate;
3645
3660
  }
3646
- if (updateFlags.includes(UseAgentUpdate.OnStateChanged)) handlers.onStateChanged = forceUpdate;
3661
+ if (updateFlags.includes(UseAgentUpdate.OnStateChanged)) handlers.onStateChanged = batchedForceUpdate;
3647
3662
  if (updateFlags.includes(UseAgentUpdate.OnRunStatusChanged)) {
3648
- handlers.onRunInitialized = forceUpdate;
3649
- handlers.onRunFinalized = forceUpdate;
3650
- handlers.onRunFailed = forceUpdate;
3663
+ handlers.onRunInitialized = batchedForceUpdate;
3664
+ handlers.onRunFinalized = batchedForceUpdate;
3665
+ handlers.onRunFailed = batchedForceUpdate;
3651
3666
  }
3652
3667
  const subscription = agent.subscribe(handlers);
3653
3668
  return () => {
@@ -4184,6 +4199,24 @@ function useHumanInTheLoop(tool, deps) {
4184
4199
  ]);
4185
4200
  }
4186
4201
 
4202
+ //#endregion
4203
+ //#region src/v2/hooks/use-capabilities.tsx
4204
+ /**
4205
+ * Returns the capabilities declared by the given agent (or the default agent).
4206
+ * Capabilities are populated from the runtime `/info` response at connection
4207
+ * time. The hook reads them synchronously from the agent instance — there is
4208
+ * no separate loading state, but the value will be `undefined` until the
4209
+ * runtime handshake completes.
4210
+ *
4211
+ * @param agentId - Optional agent ID. If omitted, uses the default agent.
4212
+ * @returns The agent's capabilities, or `undefined` if the agent doesn't
4213
+ * declare capabilities.
4214
+ */
4215
+ function useCapabilities(agentId) {
4216
+ const { agent } = useAgent({ agentId });
4217
+ if (agent && "capabilities" in agent) return agent.capabilities;
4218
+ }
4219
+
4187
4220
  //#endregion
4188
4221
  //#region src/v2/hooks/use-suggestions.tsx
4189
4222
  function useSuggestions({ agentId } = {}) {
@@ -4835,11 +4868,8 @@ function CopilotChatAssistantMessage({ message, messages, isRunning, onThumbsUp,
4835
4868
  useKatexStyles();
4836
4869
  const boundMarkdownRenderer = renderSlot(markdownRenderer, CopilotChatAssistantMessage.MarkdownRenderer, { content: message.content || "" });
4837
4870
  const boundCopyButton = renderSlot(copyButton, CopilotChatAssistantMessage.CopyButton, { onClick: async () => {
4838
- if (message.content) try {
4839
- await navigator.clipboard.writeText(message.content);
4840
- } catch (err) {
4841
- console.error("Failed to copy message:", err);
4842
- }
4871
+ if (message.content) return await (0, _copilotkit_shared.copyToClipboard)(message.content);
4872
+ return false;
4843
4873
  } });
4844
4874
  const boundThumbsUpButton = renderSlot(thumbsUpButton, CopilotChatAssistantMessage.ThumbsUpButton, { onClick: onThumbsUp });
4845
4875
  const boundThumbsDownButton = renderSlot(thumbsDownButton, CopilotChatAssistantMessage.ThumbsDownButton, { onClick: onThumbsDown });
@@ -4937,14 +4967,17 @@ function CopilotChatAssistantMessage({ message, messages, isRunning, onThumbsUp,
4937
4967
  if (timerRef.current !== null) clearTimeout(timerRef.current);
4938
4968
  };
4939
4969
  }, []);
4940
- const handleClick = (event) => {
4941
- setCopied(true);
4942
- if (timerRef.current !== null) clearTimeout(timerRef.current);
4943
- timerRef.current = setTimeout(() => {
4944
- timerRef.current = null;
4945
- setCopied(false);
4946
- }, 2e3);
4947
- if (onClick) onClick(event);
4970
+ const handleClick = async (event) => {
4971
+ let success = false;
4972
+ if (onClick) success = await Promise.resolve(onClick(event)) === true;
4973
+ if (success) {
4974
+ setCopied(true);
4975
+ if (timerRef.current !== null) clearTimeout(timerRef.current);
4976
+ timerRef.current = setTimeout(() => {
4977
+ timerRef.current = null;
4978
+ setCopied(false);
4979
+ }, 2e3);
4980
+ }
4948
4981
  };
4949
4982
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ToolbarButton, {
4950
4983
  "data-testid": "copilot-copy-button",
@@ -5097,11 +5130,8 @@ function CopilotChatUserMessage({ message, onEditMessage, branchIndex, numberOfB
5097
5130
  const mediaParts = (0, react.useMemo)(() => getMediaParts(message.content), [message.content]);
5098
5131
  const BoundMessageRenderer = renderSlot(messageRenderer, CopilotChatUserMessage.MessageRenderer, { content: flattenedContent });
5099
5132
  const BoundCopyButton = renderSlot(copyButton, CopilotChatUserMessage.CopyButton, { onClick: async () => {
5100
- if (flattenedContent) try {
5101
- await navigator.clipboard.writeText(flattenedContent);
5102
- } catch (err) {
5103
- console.error("Failed to copy message:", err);
5104
- }
5133
+ if (flattenedContent) return await (0, _copilotkit_shared.copyToClipboard)(flattenedContent);
5134
+ return false;
5105
5135
  } });
5106
5136
  const BoundEditButton = renderSlot(editButton, CopilotChatUserMessage.EditButton, { onClick: () => onEditMessage?.({ message }) });
5107
5137
  const BoundBranchNavigation = renderSlot(branchNavigation, CopilotChatUserMessage.BranchNavigation, {
@@ -5189,10 +5219,13 @@ function CopilotChatUserMessage({ message, onEditMessage, branchIndex, numberOfB
5189
5219
  _CopilotChatUserMessage.CopyButton = ({ className, title, onClick, ...props }) => {
5190
5220
  const labels = useCopilotChatConfiguration()?.labels ?? CopilotChatDefaultLabels;
5191
5221
  const [copied, setCopied] = (0, react.useState)(false);
5192
- const handleClick = (event) => {
5193
- setCopied(true);
5194
- setTimeout(() => setCopied(false), 2e3);
5195
- if (onClick) onClick(event);
5222
+ const handleClick = async (event) => {
5223
+ let success = false;
5224
+ if (onClick) success = await Promise.resolve(onClick(event)) === true;
5225
+ if (success) {
5226
+ setCopied(true);
5227
+ setTimeout(() => setCopied(false), 2e3);
5228
+ }
5196
5229
  };
5197
5230
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ToolbarButton, {
5198
5231
  "data-testid": "copilot-user-copy-button",
@@ -5299,17 +5332,24 @@ function CopilotChatReasoningMessage({ message, messages, isRunning, header, con
5299
5332
  return () => clearInterval(timer);
5300
5333
  }, [isStreaming]);
5301
5334
  const [isOpen, setIsOpen] = (0, react.useState)(isStreaming);
5335
+ const userToggledRef = (0, react.useRef)(false);
5302
5336
  (0, react.useEffect)(() => {
5303
- if (isStreaming) setIsOpen(true);
5304
- else setIsOpen(false);
5337
+ if (isStreaming) {
5338
+ userToggledRef.current = false;
5339
+ setIsOpen(true);
5340
+ } else if (!userToggledRef.current) setIsOpen(false);
5305
5341
  }, [isStreaming]);
5342
+ const handleToggle = hasContent ? () => {
5343
+ userToggledRef.current = true;
5344
+ setIsOpen((prev) => !prev);
5345
+ } : void 0;
5306
5346
  const label = isStreaming ? "Thinking…" : `Thought for ${formatDuration(elapsed)}`;
5307
5347
  const boundHeader = renderSlot(header, CopilotChatReasoningMessage.Header, {
5308
5348
  isOpen,
5309
5349
  label,
5310
5350
  hasContent,
5311
5351
  isStreaming,
5312
- onClick: hasContent ? () => setIsOpen((prev) => !prev) : void 0
5352
+ onClick: handleToggle
5313
5353
  });
5314
5354
  const boundContent = renderSlot(contentView, CopilotChatReasoningMessage.Content, {
5315
5355
  isStreaming,
@@ -8013,6 +8053,20 @@ function shouldShowDevConsole(showDevConsole) {
8013
8053
  /**
8014
8054
  * An internal context to separate the messages state (which is constantly changing) from the rest of CopilotKit context
8015
8055
  */
8056
+ /**
8057
+ * Determine whether a GraphQL error should be suppressed based on its visibility
8058
+ * and whether the dev console is active.
8059
+ *
8060
+ * Returns `null` when the error should be surfaced to the UI, or a log prefix
8061
+ * string when the error should be suppressed (logged to console only).
8062
+ *
8063
+ * Exported for unit testing.
8064
+ */
8065
+ function getErrorSuppression(visibility, isDev) {
8066
+ if (visibility === _copilotkit_shared.ErrorVisibility.SILENT) return "CopilotKit Silent Error:";
8067
+ if (!isDev && visibility === _copilotkit_shared.ErrorVisibility.DEV_ONLY) return "CopilotKit Error (hidden in production):";
8068
+ return null;
8069
+ }
8016
8070
  const MessagesTapContext = (0, react.createContext)(null);
8017
8071
  function useMessagesTap() {
8018
8072
  const tap = (0, react.useContext)(MessagesTapContext);
@@ -8096,12 +8150,9 @@ function CopilotMessages({ children }) {
8096
8150
  const graphQLErrors = error.graphQLErrors;
8097
8151
  const routeError = (gqlError) => {
8098
8152
  const visibility = gqlError.extensions?.visibility;
8099
- if (!shouldShowDevConsole(showDevConsole)) {
8100
- console.error("CopilotKit Error (hidden in production):", gqlError.message);
8101
- return;
8102
- }
8103
- if (visibility === _copilotkit_shared.ErrorVisibility.SILENT) {
8104
- console.error("CopilotKit Silent Error:", gqlError.message);
8153
+ const suppression = getErrorSuppression(visibility, shouldShowDevConsole(showDevConsole));
8154
+ if (suppression) {
8155
+ console.error(suppression, gqlError.message);
8105
8156
  return;
8106
8157
  }
8107
8158
  const ckError = createStructuredError(gqlError);
@@ -8118,8 +8169,7 @@ function CopilotMessages({ children }) {
8118
8169
  }
8119
8170
  };
8120
8171
  graphQLErrors.forEach(routeError);
8121
- } else if (!shouldShowDevConsole(showDevConsole)) console.error("CopilotKit Error (hidden in production):", error);
8122
- else {
8172
+ } else {
8123
8173
  const fallbackError = new _copilotkit_shared.CopilotKitError({
8124
8174
  message: error?.message || String(error),
8125
8175
  code: _copilotkit_shared.CopilotKitErrorCode.UNKNOWN
@@ -9186,7 +9236,7 @@ function CopilotKitInternal(cpkProps) {
9186
9236
  publicApiKey,
9187
9237
  ...cloud ? { cloud } : {},
9188
9238
  chatApiEndpoint,
9189
- headers: props.headers || {},
9239
+ headers: typeof props.headers === "function" ? props.headers() : props.headers || {},
9190
9240
  properties: props.properties || {},
9191
9241
  transcribeAudioUrl: props.transcribeAudioUrl,
9192
9242
  textToSpeechUrl: props.textToSpeechUrl,
@@ -9826,6 +9876,12 @@ Object.defineProperty(exports, 'useAttachments', {
9826
9876
  return useAttachments;
9827
9877
  }
9828
9878
  });
9879
+ Object.defineProperty(exports, 'useCapabilities', {
9880
+ enumerable: true,
9881
+ get: function () {
9882
+ return useCapabilities;
9883
+ }
9884
+ });
9829
9885
  Object.defineProperty(exports, 'useCoAgentStateRenders', {
9830
9886
  enumerable: true,
9831
9887
  get: function () {
@@ -9946,4 +10002,4 @@ Object.defineProperty(exports, 'useToast', {
9946
10002
  return useToast;
9947
10003
  }
9948
10004
  });
9949
- //# sourceMappingURL=copilotkit-Dgdpbqjt.cjs.map
10005
+ //# sourceMappingURL=copilotkit-Cvb6WpAX.cjs.map