@copilotkit/react-core 1.55.2 → 1.55.3-canary.1776243725

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-Bm4ox8G0.mjs} +89 -42
  2. package/dist/copilotkit-Bm4ox8G0.mjs.map +1 -0
  3. package/dist/{copilotkit-Dgdpbqjt.cjs → copilotkit-BoOnQHlE.cjs} +93 -40
  4. package/dist/copilotkit-BoOnQHlE.cjs.map +1 -0
  5. package/dist/{copilotkit-dwDWYpya.d.cts → copilotkit-EfopO2gn.d.cts} +27 -9
  6. package/dist/copilotkit-EfopO2gn.d.cts.map +1 -0
  7. package/dist/{copilotkit-BuhSUZHb.d.mts → copilotkit-opur-20s.d.mts} +27 -9
  8. package/dist/copilotkit-opur-20s.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 +36 -15
  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 +87 -40
  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 +9 -5
  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 +6 -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 }) => {
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;
@@ -3619,6 +3620,17 @@ function useAgent({ agentId, threadId, updates, throttleMs } = {}) {
3619
3620
  const handlers = {};
3620
3621
  let timerId = null;
3621
3622
  let active = true;
3623
+ let batchScheduled = false;
3624
+ const batchedForceUpdate = () => {
3625
+ if (!active) return;
3626
+ if (!batchScheduled) {
3627
+ batchScheduled = true;
3628
+ queueMicrotask(() => {
3629
+ batchScheduled = false;
3630
+ if (active) forceUpdate();
3631
+ });
3632
+ }
3633
+ };
3622
3634
  if (updateFlags.includes(UseAgentUpdate.OnMessagesChanged)) {
3623
3635
  const ms = effectiveThrottleMs;
3624
3636
  if (ms > 0) {
@@ -3643,11 +3655,11 @@ function useAgent({ agentId, threadId, updates, throttleMs } = {}) {
3643
3655
  handlers.onMessagesChanged = throttledNotify;
3644
3656
  } else handlers.onMessagesChanged = forceUpdate;
3645
3657
  }
3646
- if (updateFlags.includes(UseAgentUpdate.OnStateChanged)) handlers.onStateChanged = forceUpdate;
3658
+ if (updateFlags.includes(UseAgentUpdate.OnStateChanged)) handlers.onStateChanged = batchedForceUpdate;
3647
3659
  if (updateFlags.includes(UseAgentUpdate.OnRunStatusChanged)) {
3648
- handlers.onRunInitialized = forceUpdate;
3649
- handlers.onRunFinalized = forceUpdate;
3650
- handlers.onRunFailed = forceUpdate;
3660
+ handlers.onRunInitialized = batchedForceUpdate;
3661
+ handlers.onRunFinalized = batchedForceUpdate;
3662
+ handlers.onRunFailed = batchedForceUpdate;
3651
3663
  }
3652
3664
  const subscription = agent.subscribe(handlers);
3653
3665
  return () => {
@@ -4184,6 +4196,24 @@ function useHumanInTheLoop(tool, deps) {
4184
4196
  ]);
4185
4197
  }
4186
4198
 
4199
+ //#endregion
4200
+ //#region src/v2/hooks/use-capabilities.tsx
4201
+ /**
4202
+ * Returns the capabilities declared by the given agent (or the default agent).
4203
+ * Capabilities are populated from the runtime `/info` response at connection
4204
+ * time. The hook reads them synchronously from the agent instance — there is
4205
+ * no separate loading state, but the value will be `undefined` until the
4206
+ * runtime handshake completes.
4207
+ *
4208
+ * @param agentId - Optional agent ID. If omitted, uses the default agent.
4209
+ * @returns The agent's capabilities, or `undefined` if the agent doesn't
4210
+ * declare capabilities.
4211
+ */
4212
+ function useCapabilities(agentId) {
4213
+ const { agent } = useAgent({ agentId });
4214
+ if (agent && "capabilities" in agent) return agent.capabilities;
4215
+ }
4216
+
4187
4217
  //#endregion
4188
4218
  //#region src/v2/hooks/use-suggestions.tsx
4189
4219
  function useSuggestions({ agentId } = {}) {
@@ -4835,11 +4865,8 @@ function CopilotChatAssistantMessage({ message, messages, isRunning, onThumbsUp,
4835
4865
  useKatexStyles();
4836
4866
  const boundMarkdownRenderer = renderSlot(markdownRenderer, CopilotChatAssistantMessage.MarkdownRenderer, { content: message.content || "" });
4837
4867
  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
- }
4868
+ if (message.content) return await (0, _copilotkit_shared.copyToClipboard)(message.content);
4869
+ return false;
4843
4870
  } });
4844
4871
  const boundThumbsUpButton = renderSlot(thumbsUpButton, CopilotChatAssistantMessage.ThumbsUpButton, { onClick: onThumbsUp });
4845
4872
  const boundThumbsDownButton = renderSlot(thumbsDownButton, CopilotChatAssistantMessage.ThumbsDownButton, { onClick: onThumbsDown });
@@ -4937,14 +4964,17 @@ function CopilotChatAssistantMessage({ message, messages, isRunning, onThumbsUp,
4937
4964
  if (timerRef.current !== null) clearTimeout(timerRef.current);
4938
4965
  };
4939
4966
  }, []);
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);
4967
+ const handleClick = async (event) => {
4968
+ let success = false;
4969
+ if (onClick) success = await Promise.resolve(onClick(event)) === true;
4970
+ if (success) {
4971
+ setCopied(true);
4972
+ if (timerRef.current !== null) clearTimeout(timerRef.current);
4973
+ timerRef.current = setTimeout(() => {
4974
+ timerRef.current = null;
4975
+ setCopied(false);
4976
+ }, 2e3);
4977
+ }
4948
4978
  };
4949
4979
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ToolbarButton, {
4950
4980
  "data-testid": "copilot-copy-button",
@@ -5097,11 +5127,8 @@ function CopilotChatUserMessage({ message, onEditMessage, branchIndex, numberOfB
5097
5127
  const mediaParts = (0, react.useMemo)(() => getMediaParts(message.content), [message.content]);
5098
5128
  const BoundMessageRenderer = renderSlot(messageRenderer, CopilotChatUserMessage.MessageRenderer, { content: flattenedContent });
5099
5129
  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
- }
5130
+ if (flattenedContent) return await (0, _copilotkit_shared.copyToClipboard)(flattenedContent);
5131
+ return false;
5105
5132
  } });
5106
5133
  const BoundEditButton = renderSlot(editButton, CopilotChatUserMessage.EditButton, { onClick: () => onEditMessage?.({ message }) });
5107
5134
  const BoundBranchNavigation = renderSlot(branchNavigation, CopilotChatUserMessage.BranchNavigation, {
@@ -5189,10 +5216,13 @@ function CopilotChatUserMessage({ message, onEditMessage, branchIndex, numberOfB
5189
5216
  _CopilotChatUserMessage.CopyButton = ({ className, title, onClick, ...props }) => {
5190
5217
  const labels = useCopilotChatConfiguration()?.labels ?? CopilotChatDefaultLabels;
5191
5218
  const [copied, setCopied] = (0, react.useState)(false);
5192
- const handleClick = (event) => {
5193
- setCopied(true);
5194
- setTimeout(() => setCopied(false), 2e3);
5195
- if (onClick) onClick(event);
5219
+ const handleClick = async (event) => {
5220
+ let success = false;
5221
+ if (onClick) success = await Promise.resolve(onClick(event)) === true;
5222
+ if (success) {
5223
+ setCopied(true);
5224
+ setTimeout(() => setCopied(false), 2e3);
5225
+ }
5196
5226
  };
5197
5227
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ToolbarButton, {
5198
5228
  "data-testid": "copilot-user-copy-button",
@@ -5299,17 +5329,24 @@ function CopilotChatReasoningMessage({ message, messages, isRunning, header, con
5299
5329
  return () => clearInterval(timer);
5300
5330
  }, [isStreaming]);
5301
5331
  const [isOpen, setIsOpen] = (0, react.useState)(isStreaming);
5332
+ const userToggledRef = (0, react.useRef)(false);
5302
5333
  (0, react.useEffect)(() => {
5303
- if (isStreaming) setIsOpen(true);
5304
- else setIsOpen(false);
5334
+ if (isStreaming) {
5335
+ userToggledRef.current = false;
5336
+ setIsOpen(true);
5337
+ } else if (!userToggledRef.current) setIsOpen(false);
5305
5338
  }, [isStreaming]);
5339
+ const handleToggle = hasContent ? () => {
5340
+ userToggledRef.current = true;
5341
+ setIsOpen((prev) => !prev);
5342
+ } : void 0;
5306
5343
  const label = isStreaming ? "Thinking…" : `Thought for ${formatDuration(elapsed)}`;
5307
5344
  const boundHeader = renderSlot(header, CopilotChatReasoningMessage.Header, {
5308
5345
  isOpen,
5309
5346
  label,
5310
5347
  hasContent,
5311
5348
  isStreaming,
5312
- onClick: hasContent ? () => setIsOpen((prev) => !prev) : void 0
5349
+ onClick: handleToggle
5313
5350
  });
5314
5351
  const boundContent = renderSlot(contentView, CopilotChatReasoningMessage.Content, {
5315
5352
  isStreaming,
@@ -8013,6 +8050,20 @@ function shouldShowDevConsole(showDevConsole) {
8013
8050
  /**
8014
8051
  * An internal context to separate the messages state (which is constantly changing) from the rest of CopilotKit context
8015
8052
  */
8053
+ /**
8054
+ * Determine whether a GraphQL error should be suppressed based on its visibility
8055
+ * and whether the dev console is active.
8056
+ *
8057
+ * Returns `null` when the error should be surfaced to the UI, or a log prefix
8058
+ * string when the error should be suppressed (logged to console only).
8059
+ *
8060
+ * Exported for unit testing.
8061
+ */
8062
+ function getErrorSuppression(visibility, isDev) {
8063
+ if (visibility === _copilotkit_shared.ErrorVisibility.SILENT) return "CopilotKit Silent Error:";
8064
+ if (!isDev && visibility === _copilotkit_shared.ErrorVisibility.DEV_ONLY) return "CopilotKit Error (hidden in production):";
8065
+ return null;
8066
+ }
8016
8067
  const MessagesTapContext = (0, react.createContext)(null);
8017
8068
  function useMessagesTap() {
8018
8069
  const tap = (0, react.useContext)(MessagesTapContext);
@@ -8096,12 +8147,9 @@ function CopilotMessages({ children }) {
8096
8147
  const graphQLErrors = error.graphQLErrors;
8097
8148
  const routeError = (gqlError) => {
8098
8149
  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);
8150
+ const suppression = getErrorSuppression(visibility, shouldShowDevConsole(showDevConsole));
8151
+ if (suppression) {
8152
+ console.error(suppression, gqlError.message);
8105
8153
  return;
8106
8154
  }
8107
8155
  const ckError = createStructuredError(gqlError);
@@ -8118,8 +8166,7 @@ function CopilotMessages({ children }) {
8118
8166
  }
8119
8167
  };
8120
8168
  graphQLErrors.forEach(routeError);
8121
- } else if (!shouldShowDevConsole(showDevConsole)) console.error("CopilotKit Error (hidden in production):", error);
8122
- else {
8169
+ } else {
8123
8170
  const fallbackError = new _copilotkit_shared.CopilotKitError({
8124
8171
  message: error?.message || String(error),
8125
8172
  code: _copilotkit_shared.CopilotKitErrorCode.UNKNOWN
@@ -9186,7 +9233,7 @@ function CopilotKitInternal(cpkProps) {
9186
9233
  publicApiKey,
9187
9234
  ...cloud ? { cloud } : {},
9188
9235
  chatApiEndpoint,
9189
- headers: props.headers || {},
9236
+ headers: typeof props.headers === "function" ? props.headers() : props.headers || {},
9190
9237
  properties: props.properties || {},
9191
9238
  transcribeAudioUrl: props.transcribeAudioUrl,
9192
9239
  textToSpeechUrl: props.textToSpeechUrl,
@@ -9826,6 +9873,12 @@ Object.defineProperty(exports, 'useAttachments', {
9826
9873
  return useAttachments;
9827
9874
  }
9828
9875
  });
9876
+ Object.defineProperty(exports, 'useCapabilities', {
9877
+ enumerable: true,
9878
+ get: function () {
9879
+ return useCapabilities;
9880
+ }
9881
+ });
9829
9882
  Object.defineProperty(exports, 'useCoAgentStateRenders', {
9830
9883
  enumerable: true,
9831
9884
  get: function () {
@@ -9946,4 +9999,4 @@ Object.defineProperty(exports, 'useToast', {
9946
9999
  return useToast;
9947
10000
  }
9948
10001
  });
9949
- //# sourceMappingURL=copilotkit-Dgdpbqjt.cjs.map
10002
+ //# sourceMappingURL=copilotkit-BoOnQHlE.cjs.map