@copilotkit/react-core 1.70.1 → 1.70.3

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 (36) hide show
  1. package/dist/{copilotkit-DiUK2Bhq.mjs → copilotkit-Ap_yisA5.mjs} +470 -98
  2. package/dist/copilotkit-Ap_yisA5.mjs.map +1 -0
  3. package/dist/{copilotkit-CxLT6zFx.cjs → copilotkit-BU3OvveB.cjs} +468 -96
  4. package/dist/copilotkit-BU3OvveB.cjs.map +1 -0
  5. package/dist/{copilotkit-DsWuxPUQ.d.mts → copilotkit-Bs98akp9.d.mts} +32 -2
  6. package/dist/{copilotkit-DsWuxPUQ.d.mts.map → copilotkit-Bs98akp9.d.mts.map} +1 -1
  7. package/dist/{copilotkit-CtF2clxZ.d.cts → copilotkit-X2eGbOwf.d.cts} +32 -2
  8. package/dist/{copilotkit-CtF2clxZ.d.cts.map → copilotkit-X2eGbOwf.d.cts.map} +1 -1
  9. package/dist/index.cjs +1 -1
  10. package/dist/index.cjs.map +1 -1
  11. package/dist/index.d.cts +1 -1
  12. package/dist/index.d.cts.map +1 -1
  13. package/dist/index.d.mts +1 -1
  14. package/dist/index.d.mts.map +1 -1
  15. package/dist/index.mjs +1 -1
  16. package/dist/index.mjs.map +1 -1
  17. package/dist/index.umd.js +273 -15
  18. package/dist/index.umd.js.map +1 -1
  19. package/dist/v2/headless.cjs +97 -2
  20. package/dist/v2/headless.cjs.map +1 -1
  21. package/dist/v2/headless.d.cts.map +1 -1
  22. package/dist/v2/headless.d.mts.map +1 -1
  23. package/dist/v2/headless.mjs +98 -3
  24. package/dist/v2/headless.mjs.map +1 -1
  25. package/dist/v2/index.cjs +1 -1
  26. package/dist/v2/index.css +1 -1
  27. package/dist/v2/index.d.cts +1 -1
  28. package/dist/v2/index.d.mts +1 -1
  29. package/dist/v2/index.mjs +1 -1
  30. package/dist/v2/index.umd.js +468 -96
  31. package/dist/v2/index.umd.js.map +1 -1
  32. package/package.json +7 -7
  33. package/skills/react-core/SKILL.md +1 -1
  34. package/skills/react-core/references/attachments.md +20 -0
  35. package/dist/copilotkit-CxLT6zFx.cjs.map +0 -1
  36. package/dist/copilotkit-DiUK2Bhq.mjs.map +0 -1
@@ -281,6 +281,62 @@ const CopilotChatConfigurationProvider = ({ children, labels, agentId, threadId,
281
281
  const useCopilotChatConfiguration = () => {
282
282
  return (0, react.useContext)(CopilotChatConfiguration);
283
283
  };
284
+ /**
285
+ * Reports modal open/close requests to the host, and — when `open` is
286
+ * supplied — makes the modal state of an already-established chat
287
+ * configuration **controlled** for the subtree it wraps.
288
+ *
289
+ * This is deliberately a scope component rather than another mode inside
290
+ * {@link CopilotChatConfigurationProvider}. The provider resolves modal state
291
+ * across a nested chain (own state, parent sync, drawer mutual-exclusion, the
292
+ * modal-closer registry); a controlled branch inside that resolution would add
293
+ * a fourth interacting mode. Overriding the context for the subtree instead
294
+ * leaves every one of those paths untouched:
295
+ *
296
+ * - `isModalOpen` is replaced with the host's `open`, so the rendered surface
297
+ * follows the prop from the very first frame (no open-then-close flash).
298
+ * - `setModalOpen` still calls the underlying setter, so the existing
299
+ * parent-sync and drawer mutual-exclusion side effects continue to run, and
300
+ * *then* reports the request through `onOpenChange`.
301
+ * - The wrapped setter is registered as the modal closer, so the drawer's
302
+ * mobile mutual-exclusion reaches the host instead of silently flipping
303
+ * state that nothing displays.
304
+ *
305
+ * A host that supplies `open` and ignores `onOpenChange` gets a modal pinned
306
+ * to `open`, which is the standard controlled-component contract. A host that
307
+ * supplies only `onOpenChange` is notified while the modal keeps managing
308
+ * itself.
309
+ *
310
+ * Renders `children` unchanged when no chat configuration is in scope.
311
+ */
312
+ const ControlledModalOpenScope = ({ children, open, onOpenChange }) => {
313
+ const parentConfig = (0, react.useContext)(CopilotChatConfiguration);
314
+ const parentSetModalOpen = parentConfig?.setModalOpen;
315
+ const registerModalCloser = parentConfig?.ɵregisterModalCloser;
316
+ const setModalOpen = (0, react.useCallback)((next) => {
317
+ parentSetModalOpen?.(next);
318
+ onOpenChange?.(next);
319
+ }, [parentSetModalOpen, onOpenChange]);
320
+ (0, react.useEffect)(() => {
321
+ if (!registerModalCloser) return;
322
+ return registerModalCloser(setModalOpen);
323
+ }, [registerModalCloser, setModalOpen]);
324
+ const configurationValue = (0, react.useMemo)(() => parentConfig ? {
325
+ ...parentConfig,
326
+ isModalOpen: open ?? parentConfig.isModalOpen,
327
+ setModalOpen
328
+ } : null, [
329
+ parentConfig,
330
+ open,
331
+ setModalOpen
332
+ ]);
333
+ if (!configurationValue) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_jsx_runtime.Fragment, { children });
334
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CopilotChatConfiguration.Provider, {
335
+ value: configurationValue,
336
+ children
337
+ });
338
+ };
339
+ ControlledModalOpenScope.displayName = "ControlledModalOpenScope";
284
340
 
285
341
  //#endregion
286
342
  //#region src/v2/hooks/use-agent.tsx
@@ -1522,6 +1578,29 @@ const ToolCallRenderer = react.default.memo(function ToolCallRenderer({ toolCall
1522
1578
  if (prevProps.RenderComponent !== nextProps.RenderComponent) return false;
1523
1579
  return true;
1524
1580
  });
1581
+ const IS_DEVELOPMENT = process.env.NODE_ENV !== "production";
1582
+ /**
1583
+ * Reports tool calls that resolved to no renderer.
1584
+ *
1585
+ * A tool call with no renderer paints an empty message container and says
1586
+ * nothing else, so the only signal a developer gets today is a blank space in
1587
+ * the chat. This names the call and the renderers that *are* registered, which
1588
+ * is the whole diagnosis when the cause is a name that does not match.
1589
+ *
1590
+ * @param toolNames - The unmatched tool names collected during render.
1591
+ * @param registered - The registry as it stands now, re-read at report time.
1592
+ * @param alreadyWarned - Names already reported, mutated to keep this once per name.
1593
+ */
1594
+ function warnAboutUnrenderedToolCalls(toolNames, registered, alreadyWarned) {
1595
+ const registeredNames = Array.from(new Set(registered.map((rc) => rc.name)));
1596
+ const hasWildcard = registeredNames.includes("*");
1597
+ for (const toolName of toolNames) {
1598
+ if (hasWildcard || registeredNames.includes(toolName)) continue;
1599
+ if (alreadyWarned.has(toolName)) continue;
1600
+ alreadyWarned.add(toolName);
1601
+ console.warn(`[CopilotKit] The agent called the tool "${toolName}", and no renderer is registered for it, so that message rendered nothing. ` + (registeredNames.length === 0 ? "No tool-call renderers are registered. " : `Registered renderers: ${registeredNames.map((name) => `"${name}"`).join(", ")}. `) + `Register one with useRenderTool({ name: "${toolName}", ... }), or call useDefaultRenderTool() for a built-in card that covers every tool the agent calls. This warning is development-only.`);
1602
+ }
1603
+ }
1525
1604
  /**
1526
1605
  * Hook that returns a function to render tool calls based on the render functions
1527
1606
  * defined in CopilotKitProvider.
@@ -1531,13 +1610,18 @@ const ToolCallRenderer = react.default.memo(function ToolCallRenderer({ toolCall
1531
1610
  function useRenderToolCall() {
1532
1611
  const { copilotkit, executingToolCallIds } = (0, _copilotkit_react_core_v2_context.useCopilotKit)();
1533
1612
  const agentId = useCopilotChatConfiguration()?.agentId ?? _copilotkit_shared.DEFAULT_AGENT_ID;
1613
+ const unrenderedToolNames = (0, react.useRef)(/* @__PURE__ */ new Set());
1614
+ const warnedToolNames = (0, react.useRef)(/* @__PURE__ */ new Set());
1534
1615
  const renderToolCalls = (0, react.useSyncExternalStore)((callback) => {
1535
1616
  return copilotkit.subscribe({ onRenderToolCallsChanged: callback }).unsubscribe;
1536
1617
  }, () => copilotkit.renderToolCalls, () => copilotkit.renderToolCalls);
1537
- return (0, react.useCallback)(({ toolCall, toolMessage }) => {
1618
+ const renderToolCall = (0, react.useCallback)(({ toolCall, toolMessage }) => {
1538
1619
  const exactMatches = renderToolCalls.filter((rc) => rc.name === toolCall.function.name);
1539
1620
  const renderConfig = exactMatches.find((rc) => rc.agentId === agentId) || exactMatches.find((rc) => !rc.agentId) || exactMatches[0] || renderToolCalls.find((rc) => rc.name === "*");
1540
- if (!renderConfig) return null;
1621
+ if (!renderConfig) {
1622
+ if (IS_DEVELOPMENT) unrenderedToolNames.current.add(toolCall.function.name);
1623
+ return null;
1624
+ }
1541
1625
  const RenderComponent = renderConfig.render;
1542
1626
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ToolCallRenderer, {
1543
1627
  toolCall,
@@ -1550,6 +1634,17 @@ function useRenderToolCall() {
1550
1634
  executingToolCallIds,
1551
1635
  agentId
1552
1636
  ]);
1637
+ (0, react.useEffect)(() => {
1638
+ if (!IS_DEVELOPMENT) return;
1639
+ if (unrenderedToolNames.current.size === 0) return;
1640
+ const timer = setTimeout(() => {
1641
+ const pending = Array.from(unrenderedToolNames.current);
1642
+ unrenderedToolNames.current.clear();
1643
+ warnAboutUnrenderedToolCalls(pending, copilotkit.renderToolCalls, warnedToolNames.current);
1644
+ }, 0);
1645
+ return () => clearTimeout(timer);
1646
+ });
1647
+ return renderToolCall;
1553
1648
  }
1554
1649
 
1555
1650
  //#endregion