@copilotkit/react-core 1.61.1 → 1.62.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 (42) hide show
  1. package/dist/{copilotkit-UY-H6Kx7.mjs → copilotkit-BtRkFsNR.mjs} +1227 -599
  2. package/dist/copilotkit-BtRkFsNR.mjs.map +1 -0
  3. package/dist/{copilotkit-BCJDP8qd.cjs → copilotkit-CdpDZi3i.cjs} +1219 -585
  4. package/dist/copilotkit-CdpDZi3i.cjs.map +1 -0
  5. package/dist/{copilotkit-CEdu_aie.d.cts → copilotkit-Cga6AHO0.d.cts} +497 -209
  6. package/dist/copilotkit-Cga6AHO0.d.cts.map +1 -0
  7. package/dist/{copilotkit-M1FiciGd.d.mts → copilotkit-DnLpJ2Cl.d.mts} +497 -209
  8. package/dist/copilotkit-DnLpJ2Cl.d.mts.map +1 -0
  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 +360 -270
  18. package/dist/index.umd.js.map +1 -1
  19. package/dist/v2/context.d.cts +5 -1
  20. package/dist/v2/context.d.cts.map +1 -1
  21. package/dist/v2/context.d.mts +5 -1
  22. package/dist/v2/context.d.mts.map +1 -1
  23. package/dist/v2/headless.cjs +323 -104
  24. package/dist/v2/headless.cjs.map +1 -1
  25. package/dist/v2/headless.d.cts +187 -69
  26. package/dist/v2/headless.d.cts.map +1 -1
  27. package/dist/v2/headless.d.mts +187 -69
  28. package/dist/v2/headless.d.mts.map +1 -1
  29. package/dist/v2/headless.mjs +325 -106
  30. package/dist/v2/headless.mjs.map +1 -1
  31. package/dist/v2/index.cjs +2 -1
  32. package/dist/v2/index.css +1 -1
  33. package/dist/v2/index.d.cts +2 -2
  34. package/dist/v2/index.d.mts +2 -2
  35. package/dist/v2/index.mjs +2 -2
  36. package/dist/v2/index.umd.js +1112 -484
  37. package/dist/v2/index.umd.js.map +1 -1
  38. package/package.json +8 -6
  39. package/dist/copilotkit-BCJDP8qd.cjs.map +0 -1
  40. package/dist/copilotkit-CEdu_aie.d.cts.map +0 -1
  41. package/dist/copilotkit-M1FiciGd.d.mts.map +0 -1
  42. package/dist/copilotkit-UY-H6Kx7.mjs.map +0 -1
@@ -48,6 +48,7 @@ let zod_to_json_schema = require("zod-to-json-schema");
48
48
  let react_dom = require("react-dom");
49
49
  let _tanstack_react_virtual = require("@tanstack/react-virtual");
50
50
  let use_stick_to_bottom = require("use-stick-to-bottom");
51
+ let _copilotkit_web_components_threads_drawer = require("@copilotkit/web-components/threads-drawer");
51
52
  let react_markdown = require("react-markdown");
52
53
  react_markdown = __toESM(react_markdown);
53
54
 
@@ -174,6 +175,25 @@ const CopilotChatDefaultLabels = {
174
175
  modalHeaderTitle: "CopilotKit Chat",
175
176
  welcomeMessageText: "How can I help you today?"
176
177
  };
178
+ /**
179
+ * Mobile breakpoint below which the chat modal and the thread-list drawer are
180
+ * mutually exclusive. At or above this width both surfaces may coexist. This
181
+ * mirrors the `(max-width: 767px)` / `(min-width: 768px)` split already used by
182
+ * CopilotChatInput and CopilotSidebarView.
183
+ */
184
+ const MOBILE_MAX_WIDTH_PX = 767;
185
+ /**
186
+ * Reports whether the current viewport is in the mobile range (`<768px`), where
187
+ * the chat modal and drawer must not be open simultaneously. SSR-safe and
188
+ * defensive against environments without `matchMedia` (treated as desktop, so
189
+ * no mutual-exclusion constraint is applied).
190
+ *
191
+ * @returns `true` when the viewport is mobile-width, `false` otherwise.
192
+ */
193
+ function isMobileViewport() {
194
+ if (typeof window === "undefined" || typeof window.matchMedia !== "function") return false;
195
+ return window.matchMedia(`(max-width: ${MOBILE_MAX_WIDTH_PX}px)`).matches;
196
+ }
177
197
  const CopilotChatConfiguration = (0, react.createContext)(null);
178
198
  const CopilotChatConfigurationProvider = ({ children, labels, agentId, threadId, hasExplicitThreadId, isModalDefaultOpen }) => {
179
199
  const parentConfig = (0, react.useContext)(CopilotChatConfiguration);
@@ -184,12 +204,22 @@ const CopilotChatConfigurationProvider = ({ children, labels, agentId, threadId,
184
204
  ...stableLabels
185
205
  }), [stableLabels, parentConfig?.labels]);
186
206
  const resolvedAgentId = agentId ?? parentConfig?.agentId ?? _copilotkit_shared.DEFAULT_AGENT_ID;
207
+ const threadIdPropIsAuthoritative = threadId !== void 0 && hasExplicitThreadId !== false;
208
+ const isThreadIdControlled = threadIdPropIsAuthoritative;
209
+ const [activeThreadOverride, setActiveThreadOverride] = (0, react.useState)(null);
187
210
  const resolvedThreadId = (0, react.useMemo)(() => {
188
- if (threadId) return threadId;
211
+ if (threadIdPropIsAuthoritative) return threadId;
212
+ if (activeThreadOverride) return activeThreadOverride.threadId;
189
213
  if (parentConfig?.threadId) return parentConfig.threadId;
214
+ if (threadId) return threadId;
190
215
  return (0, _copilotkit_shared.randomUUID)();
191
- }, [threadId, parentConfig?.threadId]);
192
- const resolvedHasExplicitThreadId = (hasExplicitThreadId !== void 0 ? hasExplicitThreadId : !!threadId) || !!parentConfig?.hasExplicitThreadId;
216
+ }, [
217
+ threadIdPropIsAuthoritative,
218
+ threadId,
219
+ parentConfig?.threadId,
220
+ activeThreadOverride
221
+ ]);
222
+ const resolvedHasExplicitThreadId = (threadIdPropIsAuthoritative ? true : activeThreadOverride?.explicit ?? hasExplicitThreadId ?? false) || !!parentConfig?.hasExplicitThreadId;
193
223
  const [internalModalOpen, setInternalModalOpen] = (0, react.useState)(isModalDefaultOpen ?? true);
194
224
  const hasExplicitDefault = isModalDefaultOpen !== void 0;
195
225
  const setAndSync = (0, react.useCallback)((open) => {
@@ -208,20 +238,113 @@ const CopilotChatConfigurationProvider = ({ children, labels, agentId, threadId,
208
238
  }, [parentConfig?.isModalOpen, hasExplicitDefault]);
209
239
  const resolvedIsModalOpen = hasExplicitDefault ? internalModalOpen : parentConfig?.isModalOpen ?? internalModalOpen;
210
240
  const resolvedSetModalOpen = hasExplicitDefault ? setAndSync : parentConfig?.setModalOpen ?? setInternalModalOpen;
241
+ const [ownDrawerOpen, setOwnDrawerOpen] = (0, react.useState)(false);
242
+ const [ownDrawerCount, setOwnDrawerCount] = (0, react.useState)(0);
243
+ const modalCloseRef = (0, react.useRef)(() => {});
244
+ modalCloseRef.current = resolvedSetModalOpen;
245
+ const registeredModalClosersRef = (0, react.useRef)([]);
246
+ const ownRegisterModalCloser = (0, react.useCallback)((closeModal) => {
247
+ registeredModalClosersRef.current.push(closeModal);
248
+ return () => {
249
+ registeredModalClosersRef.current = registeredModalClosersRef.current.filter((entry) => entry !== closeModal);
250
+ };
251
+ }, []);
252
+ const ownSetDrawerOpen = (0, react.useCallback)((open) => {
253
+ setOwnDrawerOpen(open);
254
+ if (open && isMobileViewport()) {
255
+ const registered = registeredModalClosersRef.current;
256
+ (registered.length > 0 ? registered[registered.length - 1] : modalCloseRef.current)(false);
257
+ }
258
+ }, []);
259
+ const ownRegisterDrawer = (0, react.useCallback)(() => {
260
+ setOwnDrawerCount((count) => count + 1);
261
+ return () => {
262
+ setOwnDrawerCount((count) => Math.max(0, count - 1));
263
+ };
264
+ }, []);
265
+ const resolvedDrawerOpen = parentConfig ? parentConfig.drawerOpen : ownDrawerOpen;
266
+ const resolvedSetDrawerOpen = parentConfig ? parentConfig.setDrawerOpen : ownSetDrawerOpen;
267
+ const resolvedDrawerRegistered = parentConfig ? parentConfig.drawerRegistered : ownDrawerCount > 0;
268
+ const resolvedRegisterDrawer = parentConfig ? parentConfig.registerDrawer : ownRegisterDrawer;
269
+ const resolvedRegisterModalCloser = parentConfig ? parentConfig.ɵregisterModalCloser : ownRegisterModalCloser;
270
+ (0, react.useEffect)(() => {
271
+ if (!hasExplicitDefault) return;
272
+ return resolvedRegisterModalCloser(resolvedSetModalOpen);
273
+ }, [
274
+ hasExplicitDefault,
275
+ resolvedRegisterModalCloser,
276
+ resolvedSetModalOpen
277
+ ]);
278
+ const isThreadIdControlledRef = (0, react.useRef)(isThreadIdControlled);
279
+ isThreadIdControlledRef.current = isThreadIdControlled;
280
+ const ownSetActiveThreadId = (0, react.useCallback)((id, options) => {
281
+ setActiveThreadOverride({
282
+ threadId: id,
283
+ explicit: options?.explicit ?? true
284
+ });
285
+ }, []);
286
+ const ownStartNewThread = (0, react.useCallback)(() => {
287
+ setActiveThreadOverride({
288
+ threadId: (0, _copilotkit_shared.randomUUID)(),
289
+ explicit: false
290
+ });
291
+ }, []);
292
+ const parentSetActiveThreadId = parentConfig?.setActiveThreadId;
293
+ const parentStartNewThread = parentConfig?.startNewThread;
294
+ const resolvedSetActiveThreadId = (0, react.useCallback)((id, options) => {
295
+ if (isThreadIdControlledRef.current) {
296
+ console.warn("[CopilotKit] Ignoring setActiveThreadId(): threadId is controlled via the `threadId` prop on CopilotChatConfigurationProvider.");
297
+ return;
298
+ }
299
+ if (parentSetActiveThreadId) {
300
+ parentSetActiveThreadId(id, options);
301
+ return;
302
+ }
303
+ ownSetActiveThreadId(id, options);
304
+ }, [parentSetActiveThreadId, ownSetActiveThreadId]);
305
+ const resolvedStartNewThread = (0, react.useCallback)(() => {
306
+ if (isThreadIdControlledRef.current) {
307
+ console.warn("[CopilotKit] Ignoring startNewThread(): threadId is controlled via the `threadId` prop on CopilotChatConfigurationProvider.");
308
+ return;
309
+ }
310
+ if (parentStartNewThread) {
311
+ parentStartNewThread();
312
+ return;
313
+ }
314
+ ownStartNewThread();
315
+ }, [parentStartNewThread, ownStartNewThread]);
316
+ const setModalOpenWithDrawerExclusion = (0, react.useCallback)((open) => {
317
+ if (open && isMobileViewport()) resolvedSetDrawerOpen(false);
318
+ resolvedSetModalOpen(open);
319
+ }, [resolvedSetModalOpen, resolvedSetDrawerOpen]);
211
320
  const configurationValue = (0, react.useMemo)(() => ({
212
321
  labels: mergedLabels,
213
322
  agentId: resolvedAgentId,
214
323
  threadId: resolvedThreadId,
215
324
  hasExplicitThreadId: resolvedHasExplicitThreadId,
216
325
  isModalOpen: resolvedIsModalOpen,
217
- setModalOpen: resolvedSetModalOpen
326
+ setModalOpen: setModalOpenWithDrawerExclusion,
327
+ drawerOpen: resolvedDrawerOpen,
328
+ setDrawerOpen: resolvedSetDrawerOpen,
329
+ drawerRegistered: resolvedDrawerRegistered,
330
+ registerDrawer: resolvedRegisterDrawer,
331
+ ɵregisterModalCloser: resolvedRegisterModalCloser,
332
+ setActiveThreadId: resolvedSetActiveThreadId,
333
+ startNewThread: resolvedStartNewThread
218
334
  }), [
219
335
  mergedLabels,
220
336
  resolvedAgentId,
221
337
  resolvedThreadId,
222
338
  resolvedHasExplicitThreadId,
223
339
  resolvedIsModalOpen,
224
- resolvedSetModalOpen
340
+ setModalOpenWithDrawerExclusion,
341
+ resolvedDrawerOpen,
342
+ resolvedSetDrawerOpen,
343
+ resolvedDrawerRegistered,
344
+ resolvedRegisterDrawer,
345
+ resolvedRegisterModalCloser,
346
+ resolvedSetActiveThreadId,
347
+ resolvedStartNewThread
225
348
  ]);
226
349
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CopilotChatConfiguration.Provider, {
227
350
  value: configurationValue,
@@ -1142,7 +1265,7 @@ function CopilotChatInput({ mode = "input", onSubmitMessage, onStop, isRunning =
1142
1265
  },
1143
1266
  ...props,
1144
1267
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1145
- className: "cpk:max-w-3xl cpk:mx-auto cpk:py-0 cpk:px-4 cpk:sm:px-0 cpk:[div[data-sidebar-chat]_&]:px-8 cpk:[div[data-popup-chat]_&]:px-4 cpk:pointer-events-auto",
1268
+ className: "cpk:max-w-3xl cpk:mx-auto cpk:py-0 cpk:px-4 cpk:@3xl:px-0 cpk:[div[data-sidebar-chat]_&]:px-8 cpk:[div[data-popup-chat]_&]:px-4 cpk:pointer-events-auto",
1146
1269
  children: inputPill
1147
1270
  }), shouldShowDisclaimer && BoundDisclaimer]
1148
1271
  });
@@ -1451,353 +1574,75 @@ const LicenseContext = (0, react.createContext)({
1451
1574
  const useLicenseContext = () => (0, react.useContext)(LicenseContext);
1452
1575
 
1453
1576
  //#endregion
1454
- //#region src/v2/types/defineToolCallRenderer.ts
1455
- function defineToolCallRenderer(def) {
1456
- const argsSchema = def.name === "*" && !def.args ? zod.z.any() : def.args;
1457
- return {
1458
- name: def.name,
1459
- args: argsSchema,
1460
- render: def.render,
1461
- ...def.agentId ? { agentId: def.agentId } : {}
1462
- };
1463
- }
1464
-
1465
- //#endregion
1466
- //#region src/v2/hooks/use-render-tool.tsx
1467
- const EMPTY_DEPS$1 = [];
1577
+ //#region src/v2/hooks/use-render-tool-call.tsx
1468
1578
  /**
1469
- * Registers a renderer entry in CopilotKit's `renderToolCalls` registry.
1470
- *
1471
- * Key behavior:
1472
- * - deduplicates by `agentId:name` (latest registration wins),
1473
- * - keeps renderer entries on cleanup so historical chat tool calls can still render,
1474
- * - refreshes registration when `deps` change.
1475
- *
1476
- * @typeParam S - Schema type describing tool call parameters.
1477
- * @param config - Renderer config for wildcard or named tools.
1478
- * @param deps - Optional dependencies to refresh registration.
1479
- *
1480
- * @example
1481
- * ```tsx
1482
- * useRenderTool(
1483
- * {
1484
- * name: "searchDocs",
1485
- * parameters: z.object({ query: z.string() }),
1486
- * render: ({ status, parameters, result }) => {
1487
- * if (status === "executing") return <div>Searching {parameters.query}</div>;
1488
- * if (status === "complete") return <div>{result}</div>;
1489
- * return <div>Preparing...</div>;
1490
- * },
1491
- * },
1492
- * [],
1493
- * );
1494
- * ```
1579
+ * Memoized component that renders a single tool call.
1580
+ * This prevents unnecessary re-renders when parent components update
1581
+ * but the tool call data hasn't changed.
1582
+ */
1583
+ const ToolCallRenderer = react.default.memo(function ToolCallRenderer({ toolCall, toolMessage, RenderComponent, isExecuting }) {
1584
+ const args = (0, react.useMemo)(() => (0, _copilotkit_shared.partialJSONParse)(toolCall.function.arguments), [toolCall.function.arguments]);
1585
+ const toolName = toolCall.function.name;
1586
+ if (toolMessage) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RenderComponent, {
1587
+ name: toolName,
1588
+ toolCallId: toolCall.id,
1589
+ args,
1590
+ status: _copilotkit_core.ToolCallStatus.Complete,
1591
+ result: toolMessage.content
1592
+ });
1593
+ else if (isExecuting) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RenderComponent, {
1594
+ name: toolName,
1595
+ toolCallId: toolCall.id,
1596
+ args,
1597
+ status: _copilotkit_core.ToolCallStatus.Executing,
1598
+ result: void 0
1599
+ });
1600
+ else return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RenderComponent, {
1601
+ name: toolName,
1602
+ toolCallId: toolCall.id,
1603
+ args,
1604
+ status: _copilotkit_core.ToolCallStatus.InProgress,
1605
+ result: void 0
1606
+ });
1607
+ }, (prevProps, nextProps) => {
1608
+ if (prevProps.toolCall.id !== nextProps.toolCall.id) return false;
1609
+ if (prevProps.toolCall.function.name !== nextProps.toolCall.function.name) return false;
1610
+ if (prevProps.toolCall.function.arguments !== nextProps.toolCall.function.arguments) return false;
1611
+ if (prevProps.toolMessage?.content !== nextProps.toolMessage?.content) return false;
1612
+ if (prevProps.isExecuting !== nextProps.isExecuting) return false;
1613
+ if (prevProps.RenderComponent !== nextProps.RenderComponent) return false;
1614
+ return true;
1615
+ });
1616
+ /**
1617
+ * Hook that returns a function to render tool calls based on the render functions
1618
+ * defined in CopilotKitProvider.
1495
1619
  *
1496
- * @example
1497
- * ```tsx
1498
- * useRenderTool(
1499
- * {
1500
- * name: "summarize",
1501
- * parameters: z.object({ text: z.string() }),
1502
- * agentId: "research-agent",
1503
- * render: ({ name, status }) => <div>{name}: {status}</div>,
1504
- * },
1505
- * [selectedAgentId],
1506
- * );
1507
- * ```
1620
+ * @returns A function that takes a tool call and optional tool message and returns the rendered component
1508
1621
  */
1509
- function useRenderTool(config, deps) {
1510
- const { copilotkit } = useCopilotKit();
1511
- const extraDeps = deps ?? EMPTY_DEPS$1;
1512
- (0, react.useEffect)(() => {
1513
- const renderer = config.name === "*" && !config.parameters ? defineToolCallRenderer({
1514
- name: "*",
1515
- render: (props) => config.render({
1516
- ...props,
1517
- parameters: props.args
1518
- }),
1519
- ...config.agentId ? { agentId: config.agentId } : {}
1520
- }) : defineToolCallRenderer({
1521
- name: config.name,
1522
- args: config.parameters,
1523
- render: (props) => config.render({
1524
- ...props,
1525
- parameters: props.args
1526
- }),
1527
- ...config.agentId ? { agentId: config.agentId } : {}
1528
- });
1529
- copilotkit.addHookRenderToolCall(renderer);
1622
+ function useRenderToolCall() {
1623
+ const { copilotkit, executingToolCallIds } = useCopilotKit();
1624
+ const agentId = useCopilotChatConfiguration()?.agentId ?? _copilotkit_shared.DEFAULT_AGENT_ID;
1625
+ const renderToolCalls = (0, react.useSyncExternalStore)((callback) => {
1626
+ return copilotkit.subscribe({ onRenderToolCallsChanged: callback }).unsubscribe;
1627
+ }, () => copilotkit.renderToolCalls, () => copilotkit.renderToolCalls);
1628
+ return (0, react.useCallback)(({ toolCall, toolMessage }) => {
1629
+ const exactMatches = renderToolCalls.filter((rc) => rc.name === toolCall.function.name);
1630
+ const renderConfig = exactMatches.find((rc) => rc.agentId === agentId) || exactMatches.find((rc) => !rc.agentId) || exactMatches[0] || renderToolCalls.find((rc) => rc.name === "*");
1631
+ if (!renderConfig) return null;
1632
+ const RenderComponent = renderConfig.render;
1633
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ToolCallRenderer, {
1634
+ toolCall,
1635
+ toolMessage,
1636
+ RenderComponent,
1637
+ isExecuting: executingToolCallIds.has(toolCall.id)
1638
+ }, toolCall.id);
1530
1639
  }, [
1531
- config.name,
1532
- copilotkit,
1533
- JSON.stringify(extraDeps)
1640
+ renderToolCalls,
1641
+ executingToolCallIds,
1642
+ agentId
1534
1643
  ]);
1535
1644
  }
1536
1645
 
1537
- //#endregion
1538
- //#region src/v2/hooks/use-default-render-tool.tsx
1539
- /**
1540
- * Module-level dedup set so an unknown status value only emits a console
1541
- * warning the FIRST time we encounter it. Otherwise a stuck/unmapped status
1542
- * would log on every re-render (potentially many per second).
1543
- */
1544
- const warnedUnknownStatuses = /* @__PURE__ */ new Set();
1545
- /**
1546
- * Map a {@link ToolCallStatus} enum value to the documented string-union
1547
- * status the {@link DefaultRenderProps} contract exposes. Unknown / future
1548
- * enum members log a warning (once per distinct value) and fall back to
1549
- * `"inProgress"`.
1550
- */
1551
- function mapToolCallStatus(status) {
1552
- switch (status) {
1553
- case _copilotkit_core.ToolCallStatus.Complete: return "complete";
1554
- case _copilotkit_core.ToolCallStatus.Executing: return "executing";
1555
- case _copilotkit_core.ToolCallStatus.InProgress: return "inProgress";
1556
- default: {
1557
- const key = String(status);
1558
- if (!warnedUnknownStatuses.has(key)) {
1559
- warnedUnknownStatuses.add(key);
1560
- console.warn(`[CopilotKit] Unknown ToolCallStatus "${key}" in default tool-call renderer; falling back to "inProgress".`);
1561
- }
1562
- return "inProgress";
1563
- }
1564
- }
1565
- }
1566
- /**
1567
- * Convert the framework-internal renderer props (`args`, enum status) into
1568
- * the documented {@link DefaultRenderProps} shape (`parameters`, string-union
1569
- * status) so a user `config.render` always sees the documented contract.
1570
- */
1571
- function adaptRendererProps(props) {
1572
- return {
1573
- name: props.name,
1574
- toolCallId: props.toolCallId,
1575
- parameters: props.args,
1576
- status: mapToolCallStatus(props.status),
1577
- result: props.result
1578
- };
1579
- }
1580
- /**
1581
- * Registers a wildcard (`"*"`) tool-call renderer via `useRenderTool`.
1582
- *
1583
- * - Call with no config to use CopilotKit's built-in default tool-call card.
1584
- * - Pass `config.render` to replace the default UI with your own fallback renderer.
1585
- *
1586
- * This is useful when you want a generic renderer for tools that do not have a
1587
- * dedicated `useRenderTool({ name: "..." })` registration.
1588
- *
1589
- * @param config - Optional custom wildcard render function.
1590
- * @param deps - Optional dependencies to refresh registration.
1591
- *
1592
- * @example
1593
- * ```tsx
1594
- * useDefaultRenderTool();
1595
- * ```
1596
- *
1597
- * @example
1598
- * ```tsx
1599
- * useDefaultRenderTool({
1600
- * render: ({ name, status }) => <div>{name}: {status}</div>,
1601
- * });
1602
- * ```
1603
- *
1604
- * @example
1605
- * ```tsx
1606
- * useDefaultRenderTool(
1607
- * {
1608
- * render: ({ name, result }) => (
1609
- * <ToolEventRow title={name} payload={result} compact={compactMode} />
1610
- * ),
1611
- * },
1612
- * [compactMode],
1613
- * );
1614
- * ```
1615
- */
1616
- function useDefaultRenderTool(config, deps) {
1617
- const userRender = config?.render;
1618
- useRenderTool({
1619
- name: "*",
1620
- render: userRender ? (raw) => userRender(adaptRendererProps(raw)) : (raw) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DefaultToolCallRenderer, { ...adaptRendererProps(raw) })
1621
- }, deps);
1622
- }
1623
- /**
1624
- * Guarded JSON.stringify used inside the expanded `<pre>` blocks. A circular
1625
- * reference would otherwise crash the entire React tree on render.
1626
- */
1627
- function safeStringifyForPre(value) {
1628
- try {
1629
- return JSON.stringify(value, null, 2);
1630
- } catch (err) {
1631
- console.warn("[CopilotKit] Failed to JSON.stringify tool-call payload for default renderer; falling back to String():", err);
1632
- try {
1633
- return String(value);
1634
- } catch (innerErr) {
1635
- console.warn("[CopilotKit] safeStringifyForPre: value could not be stringified:", innerErr);
1636
- return "[unserializable]";
1637
- }
1638
- }
1639
- }
1640
- function DefaultToolCallRenderer({ name, toolCallId, parameters, status, result }) {
1641
- const [isExpanded, setIsExpanded] = (0, react.useState)(false);
1642
- const isActive = status === "inProgress" || status === "executing";
1643
- const isComplete = status === "complete";
1644
- const statusLabel = isActive ? "Running" : isComplete ? "Done" : status;
1645
- const dotClassName = isActive ? "cpk:bg-amber-500" : isComplete ? "cpk:bg-emerald-500" : "cpk:bg-zinc-400";
1646
- const badgeClassName = isActive ? "cpk:bg-amber-100 cpk:text-amber-800 cpk:dark:bg-amber-500/15 cpk:dark:text-amber-400" : isComplete ? "cpk:bg-emerald-100 cpk:text-emerald-800 cpk:dark:bg-emerald-500/15 cpk:dark:text-emerald-400" : "cpk:bg-zinc-100 cpk:text-zinc-800 cpk:dark:bg-zinc-700/40 cpk:dark:text-zinc-300";
1647
- return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1648
- "data-testid": "copilot-tool-render",
1649
- "data-tool-name": name,
1650
- "data-tool-call-id": toolCallId,
1651
- "data-status": status,
1652
- "data-args": safeStringifyForAttr(parameters),
1653
- "data-result": safeStringifyForAttr(result),
1654
- className: "cpk:mt-2 cpk:pb-2",
1655
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1656
- className: "cpk:rounded-xl cpk:border cpk:border-zinc-200/60 cpk:bg-white/70 cpk:p-4 cpk:shadow-sm cpk:backdrop-blur cpk:dark:border-zinc-800/60 cpk:dark:bg-zinc-900/50",
1657
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
1658
- type: "button",
1659
- "aria-expanded": isExpanded,
1660
- onClick: () => setIsExpanded(!isExpanded),
1661
- className: "cpk:flex cpk:w-full cpk:cursor-pointer cpk:select-none cpk:items-center cpk:justify-between cpk:gap-2.5 cpk:border-none cpk:bg-transparent cpk:p-0 cpk:m-0 cpk:text-left cpk:text-inherit",
1662
- style: { font: "inherit" },
1663
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1664
- className: "cpk:flex cpk:min-w-0 cpk:items-center cpk:gap-2",
1665
- children: [
1666
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
1667
- className: `cpk:h-3.5 cpk:w-3.5 cpk:flex-shrink-0 cpk:text-zinc-500 cpk:transition-transform cpk:dark:text-zinc-400 ${isExpanded ? "cpk:rotate-90" : ""}`,
1668
- fill: "none",
1669
- viewBox: "0 0 24 24",
1670
- strokeWidth: 2,
1671
- stroke: "currentColor",
1672
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
1673
- strokeLinecap: "round",
1674
- strokeLinejoin: "round",
1675
- d: "M8.25 4.5l7.5 7.5-7.5 7.5"
1676
- })
1677
- }),
1678
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: `cpk:inline-block cpk:h-2 cpk:w-2 cpk:flex-shrink-0 cpk:rounded-full ${dotClassName}` }),
1679
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1680
- "data-testid": "copilot-tool-render-name",
1681
- className: "cpk:truncate cpk:text-[13px] cpk:font-semibold cpk:text-zinc-900 cpk:dark:text-zinc-100",
1682
- children: name
1683
- })
1684
- ]
1685
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1686
- "data-testid": "copilot-tool-render-status",
1687
- className: `cpk:inline-flex cpk:flex-shrink-0 cpk:items-center cpk:rounded-full cpk:px-2 cpk:py-0.5 cpk:text-[11px] cpk:font-medium ${badgeClassName}`,
1688
- children: statusLabel
1689
- })]
1690
- }), isExpanded && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1691
- className: "cpk:mt-3 cpk:grid cpk:gap-3",
1692
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1693
- className: "cpk:text-[10px] cpk:uppercase cpk:text-zinc-500 cpk:dark:text-zinc-400",
1694
- children: "Arguments"
1695
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("pre", {
1696
- className: "cpk:mt-1.5 cpk:max-h-[200px] cpk:overflow-auto cpk:rounded-md cpk:bg-zinc-100 cpk:p-2.5 cpk:text-[11px] cpk:leading-relaxed cpk:text-zinc-800 cpk:whitespace-pre-wrap cpk:break-words cpk:dark:bg-zinc-800/60 cpk:dark:text-zinc-200",
1697
- children: safeStringifyForPre(parameters ?? {})
1698
- })] }), result !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1699
- className: "cpk:text-[10px] cpk:uppercase cpk:text-zinc-500 cpk:dark:text-zinc-400",
1700
- children: "Result"
1701
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("pre", {
1702
- className: "cpk:mt-1.5 cpk:max-h-[200px] cpk:overflow-auto cpk:rounded-md cpk:bg-zinc-100 cpk:p-2.5 cpk:text-[11px] cpk:leading-relaxed cpk:text-zinc-800 cpk:whitespace-pre-wrap cpk:break-words cpk:dark:bg-zinc-800/60 cpk:dark:text-zinc-200",
1703
- children: typeof result === "string" ? result : safeStringifyForPre(result)
1704
- })] })]
1705
- })]
1706
- })
1707
- });
1708
- }
1709
- function safeStringifyForAttr(value) {
1710
- if (value === void 0 || value === null) return "";
1711
- if (typeof value === "string") return value;
1712
- try {
1713
- return JSON.stringify(value);
1714
- } catch (err) {
1715
- console.warn("[CopilotKit] Failed to JSON.stringify tool-call payload for data-* attribute; falling back to String():", err);
1716
- try {
1717
- return String(value);
1718
- } catch (innerErr) {
1719
- console.warn("[CopilotKit] safeStringifyForAttr: value could not be stringified:", innerErr);
1720
- return "";
1721
- }
1722
- }
1723
- }
1724
-
1725
- //#endregion
1726
- //#region src/v2/hooks/use-render-tool-call.tsx
1727
- /**
1728
- * Memoized component that renders a single tool call.
1729
- * This prevents unnecessary re-renders when parent components update
1730
- * but the tool call data hasn't changed.
1731
- */
1732
- const ToolCallRenderer = react.default.memo(function ToolCallRenderer({ toolCall, toolMessage, RenderComponent, isExecuting }) {
1733
- const args = (0, react.useMemo)(() => (0, _copilotkit_shared.partialJSONParse)(toolCall.function.arguments), [toolCall.function.arguments]);
1734
- const toolName = toolCall.function.name;
1735
- if (toolMessage) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RenderComponent, {
1736
- name: toolName,
1737
- toolCallId: toolCall.id,
1738
- args,
1739
- status: _copilotkit_core.ToolCallStatus.Complete,
1740
- result: toolMessage.content
1741
- });
1742
- else if (isExecuting) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RenderComponent, {
1743
- name: toolName,
1744
- toolCallId: toolCall.id,
1745
- args,
1746
- status: _copilotkit_core.ToolCallStatus.Executing,
1747
- result: void 0
1748
- });
1749
- else return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RenderComponent, {
1750
- name: toolName,
1751
- toolCallId: toolCall.id,
1752
- args,
1753
- status: _copilotkit_core.ToolCallStatus.InProgress,
1754
- result: void 0
1755
- });
1756
- }, (prevProps, nextProps) => {
1757
- if (prevProps.toolCall.id !== nextProps.toolCall.id) return false;
1758
- if (prevProps.toolCall.function.name !== nextProps.toolCall.function.name) return false;
1759
- if (prevProps.toolCall.function.arguments !== nextProps.toolCall.function.arguments) return false;
1760
- if (prevProps.toolMessage?.content !== nextProps.toolMessage?.content) return false;
1761
- if (prevProps.isExecuting !== nextProps.isExecuting) return false;
1762
- if (prevProps.RenderComponent !== nextProps.RenderComponent) return false;
1763
- return true;
1764
- });
1765
- /**
1766
- * Hook that returns a function to render tool calls based on the render functions
1767
- * defined in CopilotKitProvider.
1768
- *
1769
- * @returns A function that takes a tool call and optional tool message and returns the rendered component
1770
- */
1771
- function useRenderToolCall() {
1772
- const { copilotkit, executingToolCallIds } = useCopilotKit();
1773
- const agentId = useCopilotChatConfiguration()?.agentId ?? _copilotkit_shared.DEFAULT_AGENT_ID;
1774
- const renderToolCalls = (0, react.useSyncExternalStore)((callback) => {
1775
- return copilotkit.subscribe({ onRenderToolCallsChanged: callback }).unsubscribe;
1776
- }, () => copilotkit.renderToolCalls, () => copilotkit.renderToolCalls);
1777
- return (0, react.useCallback)(({ toolCall, toolMessage }) => {
1778
- const exactMatches = renderToolCalls.filter((rc) => rc.name === toolCall.function.name);
1779
- return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ToolCallRenderer, {
1780
- toolCall,
1781
- toolMessage,
1782
- RenderComponent: (exactMatches.find((rc) => rc.agentId === agentId) || exactMatches.find((rc) => !rc.agentId) || exactMatches[0] || renderToolCalls.find((rc) => rc.name === "*"))?.render ?? defaultToolCallRenderAdapter,
1783
- isExecuting: executingToolCallIds.has(toolCall.id)
1784
- }, toolCall.id);
1785
- }, [
1786
- renderToolCalls,
1787
- executingToolCallIds,
1788
- agentId
1789
- ]);
1790
- }
1791
- function defaultToolCallRenderAdapter(props) {
1792
- return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DefaultToolCallRenderer, {
1793
- name: props.name,
1794
- toolCallId: props.toolCallId,
1795
- parameters: props.args,
1796
- status: mapToolCallStatus(props.status),
1797
- result: props.result
1798
- });
1799
- }
1800
-
1801
1646
  //#endregion
1802
1647
  //#region src/v2/components/CopilotKitInspector.tsx
1803
1648
  const CopilotKitInspector = ({ core, ...rest }) => {
@@ -3278,7 +3123,7 @@ const A2UISurfaceContentSchema = zod.z.object({
3278
3123
  ...A2UILifecycleFields
3279
3124
  }).passthrough();
3280
3125
  function createA2UIMessageRenderer(options) {
3281
- const { theme, catalog, loadingComponent, recovery } = options;
3126
+ const { theme, catalog, loadingComponent, recovery, onAction } = options;
3282
3127
  const showAfterMs = recovery?.showAfterMs ?? 2e3;
3283
3128
  const showAfterAttempts = recovery?.showAfterAttempts ?? 2;
3284
3129
  const optionDebugExposure = recovery?.debugExposure ?? "collapsed";
@@ -3354,6 +3199,7 @@ function createA2UIMessageRenderer(options) {
3354
3199
  agent,
3355
3200
  copilotkit,
3356
3201
  catalog,
3202
+ onAction,
3357
3203
  onReady: markSurfaceReady
3358
3204
  }, surfaceId))
3359
3205
  });
@@ -3374,29 +3220,60 @@ function createA2UIMessageRenderer(options) {
3374
3220
  };
3375
3221
  }
3376
3222
  /**
3223
+ * Orchestrates a single A2UI user action: runs the optional `onAction`
3224
+ * interceptor first, then forwards to the agent unless the interceptor
3225
+ * suppressed it (returned `null`). Exported for unit testing; the wiring lives
3226
+ * in {@link ReactSurfaceHost}.
3227
+ */
3228
+ async function runA2UIAction({ message, agent, copilotkit, onAction }) {
3229
+ if (!agent) return;
3230
+ const action = message.userAction;
3231
+ const forward = async (forwardAction) => {
3232
+ const a2uiAction = forwardAction !== void 0 ? {
3233
+ ...message,
3234
+ userAction: forwardAction
3235
+ } : message;
3236
+ try {
3237
+ copilotkit.setProperties({
3238
+ ...copilotkit.properties,
3239
+ a2uiAction
3240
+ });
3241
+ await copilotkit.runAgent({ agent });
3242
+ } finally {
3243
+ if (copilotkit.properties) {
3244
+ const { a2uiAction: _omit, ...rest } = copilotkit.properties;
3245
+ copilotkit.setProperties(rest);
3246
+ }
3247
+ }
3248
+ };
3249
+ if (onAction && action) {
3250
+ const result = await onAction(action, forward);
3251
+ if (result === null) return;
3252
+ if (result) {
3253
+ await forward(result);
3254
+ return;
3255
+ }
3256
+ }
3257
+ await forward();
3258
+ }
3259
+ /**
3377
3260
  * Renders a single A2UI surface using the React renderer.
3378
3261
  * Wraps A2UIProvider + A2UIRenderer and bridges actions back to CopilotKit.
3379
3262
  */
3380
- function ReactSurfaceHost({ surfaceId, operations, theme, agent, copilotkit, catalog, onReady }) {
3263
+ function ReactSurfaceHost({ surfaceId, operations, theme, agent, copilotkit, catalog, onAction, onReady }) {
3381
3264
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3382
3265
  className: "cpk:flex cpk:w-full cpk:flex-none cpk:flex-col cpk:gap-4",
3383
3266
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_copilotkit_a2ui_renderer.A2UIProvider, {
3384
- onAction: (0, react.useCallback)(async (message) => {
3385
- if (!agent) return;
3386
- message.userAction;
3387
- try {
3388
- copilotkit.setProperties({
3389
- ...copilotkit.properties,
3390
- a2uiAction: message
3391
- });
3392
- await copilotkit.runAgent({ agent });
3393
- } finally {
3394
- if (copilotkit.properties) {
3395
- const { a2uiAction, ...rest } = copilotkit.properties;
3396
- copilotkit.setProperties(rest);
3397
- }
3398
- }
3399
- }, [agent, copilotkit]),
3267
+ onAction: (0, react.useCallback)((message) => runA2UIAction({
3268
+ message,
3269
+ agent,
3270
+ copilotkit,
3271
+ onAction
3272
+ }), [
3273
+ agent,
3274
+ copilotkit,
3275
+ onAction
3276
+ ]),
3400
3277
  theme,
3401
3278
  catalog,
3402
3279
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(SurfaceMessageProcessor, {
@@ -3467,6 +3344,18 @@ function getOperationSurfaceId(operation) {
3467
3344
  return operation?.createSurface?.surfaceId ?? operation?.updateComponents?.surfaceId ?? operation?.updateDataModel?.surfaceId ?? operation?.deleteSurface?.surfaceId ?? null;
3468
3345
  }
3469
3346
 
3347
+ //#endregion
3348
+ //#region src/v2/types/defineToolCallRenderer.ts
3349
+ function defineToolCallRenderer(def) {
3350
+ const argsSchema = def.name === "*" && !def.args ? zod.z.any() : def.args;
3351
+ return {
3352
+ name: def.name,
3353
+ args: argsSchema,
3354
+ render: def.render,
3355
+ ...def.agentId ? { agentId: def.agentId } : {}
3356
+ };
3357
+ }
3358
+
3470
3359
  //#endregion
3471
3360
  //#region src/v2/a2ui/A2UIToolCallRenderer.tsx
3472
3361
  /**
@@ -3561,6 +3450,10 @@ function A2UICatalogContext({ catalog, includeSchema }) {
3561
3450
 
3562
3451
  //#endregion
3563
3452
  //#region src/v2/providers/CopilotKitProvider.tsx
3453
+ const zodToJsonSchemaAdapter = (schema, options) => {
3454
+ const refStrategy = options?.$refStrategy;
3455
+ return (0, zod_to_json_schema.zodToJsonSchema)(schema, refStrategy === "root" || refStrategy === "relative" || refStrategy === "none" || refStrategy === "seen" ? { $refStrategy: refStrategy } : {});
3456
+ };
3564
3457
  const HEADER_NAME = "X-CopilotCloud-Public-Api-Key";
3565
3458
  const COPILOT_CLOUD_CHAT_URL$1 = "https://api.cloud.copilotkit.ai/copilotkit/v1";
3566
3459
  const EMPTY_HEADERS = Object.freeze({});
@@ -3591,6 +3484,8 @@ const CopilotKitProvider = ({ children, runtimeUrl, headers: headersProp = EMPTY
3591
3484
  const [runtimeA2UIEnabled, setRuntimeA2UIEnabled] = (0, react.useState)(false);
3592
3485
  const [runtimeOpenGenUIEnabled, setRuntimeOpenGenUIEnabled] = (0, react.useState)(false);
3593
3486
  const openGenUIActive = runtimeOpenGenUIEnabled || !!openGenerativeUI;
3487
+ const a2uiCatalogProvided = !!a2ui?.catalog;
3488
+ const a2uiActive = runtimeA2UIEnabled || a2uiCatalogProvided;
3594
3489
  const [runtimeLicenseStatus, setRuntimeLicenseStatus] = (0, react.useState)(void 0);
3595
3490
  (0, react.useEffect)(() => {
3596
3491
  if (typeof window === "undefined") return;
@@ -3621,7 +3516,7 @@ const CopilotKitProvider = ({ children, runtimeUrl, headers: headersProp = EMPTY
3621
3516
  content: OpenGenerativeUIContentSchema,
3622
3517
  render: OpenGenerativeUIActivityRenderer
3623
3518
  });
3624
- if (runtimeA2UIEnabled) renderers.unshift(createA2UIMessageRenderer({
3519
+ if (a2uiActive) renderers.unshift(createA2UIMessageRenderer({
3625
3520
  theme: a2ui?.theme ?? _copilotkit_a2ui_renderer.viewerTheme,
3626
3521
  catalog: a2ui?.catalog,
3627
3522
  loadingComponent: a2ui?.loadingComponent,
@@ -3629,7 +3524,7 @@ const CopilotKitProvider = ({ children, runtimeUrl, headers: headersProp = EMPTY
3629
3524
  }));
3630
3525
  return renderers;
3631
3526
  }, [
3632
- runtimeA2UIEnabled,
3527
+ a2uiActive,
3633
3528
  openGenUIActive,
3634
3529
  a2ui
3635
3530
  ]);
@@ -3816,7 +3711,10 @@ const CopilotKitProvider = ({ children, runtimeUrl, headers: headersProp = EMPTY
3816
3711
  copilotkit.setRuntimeTransport(useSingleEndpoint === true ? "single" : useSingleEndpoint === false ? "rest" : "auto");
3817
3712
  copilotkit.setHeaders(mergedHeaders);
3818
3713
  copilotkit.setCredentials(credentials);
3819
- copilotkit.setProperties(properties);
3714
+ copilotkit.setProperties(a2uiCatalogProvided ? {
3715
+ ...properties,
3716
+ a2uiCatalogAvailable: true
3717
+ } : properties);
3820
3718
  copilotkit.setAgents__unsafe_dev_only(mergedAgents);
3821
3719
  copilotkit.setDebug(debug);
3822
3720
  }, [
@@ -3825,6 +3723,7 @@ const CopilotKitProvider = ({ children, runtimeUrl, headers: headersProp = EMPTY
3825
3723
  mergedHeaders,
3826
3724
  credentials,
3827
3725
  properties,
3726
+ a2uiCatalogProvided,
3828
3727
  mergedAgents,
3829
3728
  useSingleEndpoint,
3830
3729
  debug
@@ -3872,7 +3771,7 @@ const CopilotKitProvider = ({ children, runtimeUrl, headers: headersProp = EMPTY
3872
3771
  return JSON.stringify(sandboxFunctionsList.map((fn) => ({
3873
3772
  name: fn.name,
3874
3773
  description: fn.description,
3875
- parameters: (0, _copilotkit_shared.schemaToJsonSchema)(fn.parameters, { zodToJsonSchema: zod_to_json_schema.zodToJsonSchema })
3774
+ parameters: (0, _copilotkit_shared.schemaToJsonSchema)(fn.parameters, { zodToJsonSchema: zodToJsonSchemaAdapter })
3876
3775
  })));
3877
3776
  }, [sandboxFunctionsList]);
3878
3777
  (0, react.useLayoutEffect)(() => {
@@ -3893,7 +3792,7 @@ const CopilotKitProvider = ({ children, runtimeUrl, headers: headersProp = EMPTY
3893
3792
  copilotkit,
3894
3793
  executingToolCallIds
3895
3794
  }), [copilotkit, executingToolCallIds]);
3896
- const licenseContextValue = (0, react.useMemo)(() => (0, _copilotkit_shared.createLicenseContextValue)(null), []);
3795
+ const licenseContextValue = (0, react.useMemo)(() => (0, _copilotkit_shared.createLicenseContextValue)(runtimeLicenseStatus), [runtimeLicenseStatus]);
3897
3796
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SandboxFunctionsContext.Provider, {
3898
3797
  value: sandboxFunctionsList,
3899
3798
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CopilotKitContext.Provider, {
@@ -3901,8 +3800,8 @@ const CopilotKitProvider = ({ children, runtimeUrl, headers: headersProp = EMPTY
3901
3800
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(LicenseContext.Provider, {
3902
3801
  value: licenseContextValue,
3903
3802
  children: [
3904
- runtimeA2UIEnabled && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(A2UIBuiltInToolCallRenderer, {}),
3905
- runtimeA2UIEnabled && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(A2UICatalogContext, {
3803
+ a2uiActive && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(A2UIBuiltInToolCallRenderer, {}),
3804
+ a2uiActive && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(A2UICatalogContext, {
3906
3805
  catalog: a2ui?.catalog,
3907
3806
  includeSchema: a2ui?.includeSchema
3908
3807
  }),
@@ -3980,16 +3879,20 @@ function useRenderActivityMessage() {
3980
3879
  const renderActivityMessage = (0, react.useCallback)((message) => {
3981
3880
  const renderer = findRenderer(message.activityType);
3982
3881
  if (!renderer) return null;
3983
- const parseResult = renderer.content.safeParse(message.content);
3984
- if (!parseResult.success) {
3985
- console.warn(`Failed to parse content for activity message '${message.activityType}':`, parseResult.error);
3882
+ const parseResult = renderer.content["~standard"].validate(message.content);
3883
+ if (parseResult instanceof Promise) {
3884
+ console.warn(`Async content validation is not supported for activity message '${message.activityType}'`);
3885
+ return null;
3886
+ }
3887
+ if (parseResult.issues) {
3888
+ console.warn(`Failed to parse content for activity message '${message.activityType}':`, parseResult.issues);
3986
3889
  return null;
3987
3890
  }
3988
3891
  const Component = renderer.render;
3989
3892
  const agent = copilotkit.getAgent(agentId);
3990
3893
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Component, {
3991
3894
  activityType: message.activityType,
3992
- content: parseResult.data,
3895
+ content: parseResult.value,
3993
3896
  message,
3994
3897
  agent
3995
3898
  }, message.id);
@@ -4003,106 +3906,376 @@ function useRenderActivityMessage() {
4003
3906
  findRenderer
4004
3907
  }), [renderActivityMessage, findRenderer]);
4005
3908
  }
4006
-
4007
- //#endregion
4008
- //#region src/v2/hooks/use-frontend-tool.tsx
4009
- const EMPTY_DEPS = [];
4010
- function useFrontendTool(tool, deps) {
4011
- const { copilotkit } = useCopilotKit();
4012
- const extraDeps = deps ?? EMPTY_DEPS;
4013
- (0, react.useEffect)(() => {
4014
- const name = tool.name;
4015
- if (copilotkit.getTool({
4016
- toolName: name,
4017
- agentId: tool.agentId
4018
- })) {
4019
- console.warn(`Tool '${name}' already exists for agent '${tool.agentId || "global"}'. Overriding with latest registration.`);
4020
- copilotkit.removeTool(name, tool.agentId);
3909
+
3910
+ //#endregion
3911
+ //#region src/v2/hooks/use-frontend-tool.tsx
3912
+ const EMPTY_DEPS$1 = [];
3913
+ function useFrontendTool(tool, deps) {
3914
+ const { copilotkit } = useCopilotKit();
3915
+ const extraDeps = deps ?? EMPTY_DEPS$1;
3916
+ (0, react.useEffect)(() => {
3917
+ const name = tool.name;
3918
+ if (copilotkit.getTool({
3919
+ toolName: name,
3920
+ agentId: tool.agentId
3921
+ })) {
3922
+ console.warn(`Tool '${name}' already exists for agent '${tool.agentId || "global"}'. Overriding with latest registration.`);
3923
+ copilotkit.removeTool(name, tool.agentId);
3924
+ }
3925
+ copilotkit.addTool(tool);
3926
+ if (tool.render) copilotkit.addHookRenderToolCall({
3927
+ name,
3928
+ args: tool.parameters,
3929
+ agentId: tool.agentId,
3930
+ render: tool.render
3931
+ });
3932
+ return () => {
3933
+ copilotkit.removeTool(name, tool.agentId);
3934
+ };
3935
+ }, [
3936
+ tool.name,
3937
+ tool.available,
3938
+ copilotkit,
3939
+ JSON.stringify(extraDeps)
3940
+ ]);
3941
+ }
3942
+
3943
+ //#endregion
3944
+ //#region src/v2/hooks/use-component.tsx
3945
+ /**
3946
+ * Registers a React component as a frontend tool renderer in chat.
3947
+ *
3948
+ * This hook is a convenience wrapper around `useFrontendTool` that:
3949
+ * - builds a model-facing tool description,
3950
+ * - forwards optional schema parameters (any Standard Schema V1 compatible library),
3951
+ * - renders your component with tool call parameters.
3952
+ *
3953
+ * Use this when you want to display a typed visual component for a tool call
3954
+ * without manually wiring a full frontend tool object.
3955
+ *
3956
+ * When `parameters` is provided, render props are inferred from the schema.
3957
+ * When omitted, the render component may accept any props.
3958
+ *
3959
+ * @typeParam TSchema - Schema describing tool parameters, or `undefined` when no schema is given.
3960
+ * @param config - Tool registration config.
3961
+ * @param deps - Optional dependencies to refresh registration (same semantics as `useEffect`).
3962
+ *
3963
+ * @example
3964
+ * ```tsx
3965
+ * // Without parameters — render accepts any props
3966
+ * useComponent({
3967
+ * name: "showGreeting",
3968
+ * render: ({ message }: { message: string }) => <div>{message}</div>,
3969
+ * });
3970
+ * ```
3971
+ *
3972
+ * @example
3973
+ * ```tsx
3974
+ * // With parameters — render props inferred from schema
3975
+ * useComponent({
3976
+ * name: "showWeatherCard",
3977
+ * parameters: z.object({ city: z.string() }),
3978
+ * render: ({ city }) => <div>{city}</div>,
3979
+ * });
3980
+ * ```
3981
+ *
3982
+ * @example
3983
+ * ```tsx
3984
+ * useComponent(
3985
+ * {
3986
+ * name: "renderProfile",
3987
+ * parameters: z.object({ userId: z.string() }),
3988
+ * render: ProfileCard,
3989
+ * agentId: "support-agent",
3990
+ * },
3991
+ * [selectedAgentId],
3992
+ * );
3993
+ * ```
3994
+ */
3995
+ function useComponent(config, deps) {
3996
+ const prefix = `Use this tool to display the "${config.name}" component in the chat. This tool renders a visual UI component for the user.`;
3997
+ const fullDescription = config.description ? `${prefix}\n\n${config.description}` : prefix;
3998
+ useFrontendTool({
3999
+ name: config.name,
4000
+ description: fullDescription,
4001
+ parameters: config.parameters,
4002
+ render: ({ args }) => {
4003
+ const Component = config.render;
4004
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Component, { ...args });
4005
+ },
4006
+ agentId: config.agentId,
4007
+ followUp: config.followUp
4008
+ }, deps);
4009
+ }
4010
+
4011
+ //#endregion
4012
+ //#region src/v2/hooks/use-render-tool.tsx
4013
+ const EMPTY_DEPS = [];
4014
+ /**
4015
+ * Registers a renderer entry in CopilotKit's `renderToolCalls` registry.
4016
+ *
4017
+ * Key behavior:
4018
+ * - deduplicates by `agentId:name` (latest registration wins),
4019
+ * - keeps renderer entries on cleanup so historical chat tool calls can still render,
4020
+ * - refreshes registration when `deps` change.
4021
+ *
4022
+ * @typeParam S - Schema type describing tool call parameters.
4023
+ * @param config - Renderer config for wildcard or named tools.
4024
+ * @param deps - Optional dependencies to refresh registration.
4025
+ *
4026
+ * @example
4027
+ * ```tsx
4028
+ * useRenderTool(
4029
+ * {
4030
+ * name: "searchDocs",
4031
+ * parameters: z.object({ query: z.string() }),
4032
+ * render: ({ status, parameters, result }) => {
4033
+ * if (status === "executing") return <div>Searching {parameters.query}</div>;
4034
+ * if (status === "complete") return <div>{result}</div>;
4035
+ * return <div>Preparing...</div>;
4036
+ * },
4037
+ * },
4038
+ * [],
4039
+ * );
4040
+ * ```
4041
+ *
4042
+ * @example
4043
+ * ```tsx
4044
+ * useRenderTool(
4045
+ * {
4046
+ * name: "summarize",
4047
+ * parameters: z.object({ text: z.string() }),
4048
+ * agentId: "research-agent",
4049
+ * render: ({ name, status }) => <div>{name}: {status}</div>,
4050
+ * },
4051
+ * [selectedAgentId],
4052
+ * );
4053
+ * ```
4054
+ */
4055
+ function useRenderTool(config, deps) {
4056
+ const { copilotkit } = useCopilotKit();
4057
+ const extraDeps = deps ?? EMPTY_DEPS;
4058
+ (0, react.useEffect)(() => {
4059
+ const renderer = config.name === "*" && !config.parameters ? defineToolCallRenderer({
4060
+ name: "*",
4061
+ render: (props) => config.render({
4062
+ ...props,
4063
+ parameters: props.args
4064
+ }),
4065
+ ...config.agentId ? { agentId: config.agentId } : {}
4066
+ }) : defineToolCallRenderer({
4067
+ name: config.name,
4068
+ args: config.parameters,
4069
+ render: (props) => {
4070
+ if (props.status === _copilotkit_core.ToolCallStatus.InProgress) return config.render({
4071
+ ...props,
4072
+ parameters: props.args
4073
+ });
4074
+ if (props.status === _copilotkit_core.ToolCallStatus.Executing) return config.render({
4075
+ ...props,
4076
+ parameters: props.args
4077
+ });
4078
+ return config.render({
4079
+ ...props,
4080
+ parameters: props.args
4081
+ });
4082
+ },
4083
+ ...config.agentId ? { agentId: config.agentId } : {}
4084
+ });
4085
+ copilotkit.addHookRenderToolCall(renderer);
4086
+ }, [
4087
+ config.name,
4088
+ copilotkit,
4089
+ JSON.stringify(extraDeps)
4090
+ ]);
4091
+ }
4092
+
4093
+ //#endregion
4094
+ //#region src/v2/hooks/use-default-render-tool.tsx
4095
+ /**
4096
+ * Module-level dedup set so an unknown status value only emits a console
4097
+ * warning the FIRST time we encounter it. Otherwise a stuck/unmapped status
4098
+ * would log on every re-render (potentially many per second).
4099
+ */
4100
+ const warnedUnknownStatuses = /* @__PURE__ */ new Set();
4101
+ /**
4102
+ * Map a {@link ToolCallStatus} enum value to the documented string-union
4103
+ * status the {@link DefaultRenderProps} contract exposes. Unknown / future
4104
+ * enum members log a warning (once per distinct value) and fall back to
4105
+ * `"inProgress"`.
4106
+ */
4107
+ function mapToolCallStatus(status) {
4108
+ switch (status) {
4109
+ case _copilotkit_core.ToolCallStatus.Complete: return "complete";
4110
+ case _copilotkit_core.ToolCallStatus.Executing: return "executing";
4111
+ case _copilotkit_core.ToolCallStatus.InProgress: return "inProgress";
4112
+ default: {
4113
+ const key = String(status);
4114
+ if (!warnedUnknownStatuses.has(key)) {
4115
+ warnedUnknownStatuses.add(key);
4116
+ console.warn(`[CopilotKit] Unknown ToolCallStatus "${key}" in default tool-call renderer; falling back to "inProgress".`);
4117
+ }
4118
+ return "inProgress";
4119
+ }
4120
+ }
4121
+ }
4122
+ /**
4123
+ * Convert the framework-internal renderer props (`args`, enum status) into
4124
+ * the documented {@link DefaultRenderProps} shape (`parameters`, string-union
4125
+ * status) so a user `config.render` always sees the documented contract.
4126
+ */
4127
+ function adaptRendererProps(props) {
4128
+ return {
4129
+ name: props.name,
4130
+ toolCallId: props.toolCallId,
4131
+ parameters: props.args,
4132
+ status: mapToolCallStatus(props.status),
4133
+ result: props.result
4134
+ };
4135
+ }
4136
+ /**
4137
+ * Registers a wildcard (`"*"`) tool-call renderer via `useRenderTool`.
4138
+ *
4139
+ * - Call with no config to use CopilotKit's built-in default tool-call card.
4140
+ * - Pass `config.render` to replace the default UI with your own fallback renderer.
4141
+ *
4142
+ * This is useful when you want a generic renderer for tools that do not have a
4143
+ * dedicated `useRenderTool({ name: "..." })` registration.
4144
+ *
4145
+ * @param config - Optional custom wildcard render function.
4146
+ * @param deps - Optional dependencies to refresh registration.
4147
+ *
4148
+ * @example
4149
+ * ```tsx
4150
+ * useDefaultRenderTool();
4151
+ * ```
4152
+ *
4153
+ * @example
4154
+ * ```tsx
4155
+ * useDefaultRenderTool({
4156
+ * render: ({ name, status }) => <div>{name}: {status}</div>,
4157
+ * });
4158
+ * ```
4159
+ *
4160
+ * @example
4161
+ * ```tsx
4162
+ * useDefaultRenderTool(
4163
+ * {
4164
+ * render: ({ name, result }) => (
4165
+ * <ToolEventRow title={name} payload={result} compact={compactMode} />
4166
+ * ),
4167
+ * },
4168
+ * [compactMode],
4169
+ * );
4170
+ * ```
4171
+ */
4172
+ function useDefaultRenderTool(config, deps) {
4173
+ const userRender = config?.render;
4174
+ useRenderTool({
4175
+ name: "*",
4176
+ render: userRender ? (raw) => userRender(adaptRendererProps(raw)) : (raw) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DefaultToolCallRenderer, { ...adaptRendererProps(raw) })
4177
+ }, deps);
4178
+ }
4179
+ /**
4180
+ * Guarded JSON.stringify used inside the expanded `<pre>` blocks. A circular
4181
+ * reference would otherwise crash the entire React tree on render.
4182
+ */
4183
+ function safeStringifyForPre(value) {
4184
+ try {
4185
+ return JSON.stringify(value, null, 2);
4186
+ } catch (err) {
4187
+ console.warn("[CopilotKit] Failed to JSON.stringify tool-call payload for default renderer; falling back to String():", err);
4188
+ try {
4189
+ return String(value);
4190
+ } catch (innerErr) {
4191
+ console.warn("[CopilotKit] safeStringifyForPre: value could not be stringified:", innerErr);
4192
+ return "[unserializable]";
4193
+ }
4194
+ }
4195
+ }
4196
+ function DefaultToolCallRenderer({ name, toolCallId, parameters, status, result }) {
4197
+ const [isExpanded, setIsExpanded] = (0, react.useState)(false);
4198
+ const isActive = status === "inProgress" || status === "executing";
4199
+ const isComplete = status === "complete";
4200
+ const statusLabel = isActive ? "Running" : isComplete ? "Done" : status;
4201
+ const dotClassName = isActive ? "cpk:bg-amber-500" : isComplete ? "cpk:bg-emerald-500" : "cpk:bg-zinc-400";
4202
+ const badgeClassName = isActive ? "cpk:bg-amber-100 cpk:text-amber-800 cpk:dark:bg-amber-500/15 cpk:dark:text-amber-400" : isComplete ? "cpk:bg-emerald-100 cpk:text-emerald-800 cpk:dark:bg-emerald-500/15 cpk:dark:text-emerald-400" : "cpk:bg-zinc-100 cpk:text-zinc-800 cpk:dark:bg-zinc-700/40 cpk:dark:text-zinc-300";
4203
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
4204
+ "data-testid": "copilot-tool-render",
4205
+ "data-tool-name": name,
4206
+ "data-tool-call-id": toolCallId,
4207
+ "data-status": status,
4208
+ "data-args": safeStringifyForAttr(parameters),
4209
+ "data-result": safeStringifyForAttr(result),
4210
+ className: "cpk:mt-2 cpk:pb-2",
4211
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4212
+ className: "cpk:rounded-xl cpk:border cpk:border-zinc-200/60 cpk:bg-white/70 cpk:p-4 cpk:shadow-sm cpk:backdrop-blur cpk:dark:border-zinc-800/60 cpk:dark:bg-zinc-900/50",
4213
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
4214
+ type: "button",
4215
+ "aria-expanded": isExpanded,
4216
+ onClick: () => setIsExpanded(!isExpanded),
4217
+ className: "cpk:flex cpk:w-full cpk:cursor-pointer cpk:select-none cpk:items-center cpk:justify-between cpk:gap-2.5 cpk:border-none cpk:bg-transparent cpk:p-0 cpk:m-0 cpk:text-left cpk:text-inherit",
4218
+ style: { font: "inherit" },
4219
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4220
+ className: "cpk:flex cpk:min-w-0 cpk:items-center cpk:gap-2",
4221
+ children: [
4222
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
4223
+ className: `cpk:h-3.5 cpk:w-3.5 cpk:flex-shrink-0 cpk:text-zinc-500 cpk:transition-transform cpk:dark:text-zinc-400 ${isExpanded ? "cpk:rotate-90" : ""}`,
4224
+ fill: "none",
4225
+ viewBox: "0 0 24 24",
4226
+ strokeWidth: 2,
4227
+ stroke: "currentColor",
4228
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
4229
+ strokeLinecap: "round",
4230
+ strokeLinejoin: "round",
4231
+ d: "M8.25 4.5l7.5 7.5-7.5 7.5"
4232
+ })
4233
+ }),
4234
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: `cpk:inline-block cpk:h-2 cpk:w-2 cpk:flex-shrink-0 cpk:rounded-full ${dotClassName}` }),
4235
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
4236
+ "data-testid": "copilot-tool-render-name",
4237
+ className: "cpk:truncate cpk:text-[13px] cpk:font-semibold cpk:text-zinc-900 cpk:dark:text-zinc-100",
4238
+ children: name
4239
+ })
4240
+ ]
4241
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
4242
+ "data-testid": "copilot-tool-render-status",
4243
+ className: `cpk:inline-flex cpk:flex-shrink-0 cpk:items-center cpk:rounded-full cpk:px-2 cpk:py-0.5 cpk:text-[11px] cpk:font-medium ${badgeClassName}`,
4244
+ children: statusLabel
4245
+ })]
4246
+ }), isExpanded && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4247
+ className: "cpk:mt-3 cpk:grid cpk:gap-3",
4248
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
4249
+ className: "cpk:text-[10px] cpk:uppercase cpk:text-zinc-500 cpk:dark:text-zinc-400",
4250
+ children: "Arguments"
4251
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("pre", {
4252
+ className: "cpk:mt-1.5 cpk:max-h-[200px] cpk:overflow-auto cpk:rounded-md cpk:bg-zinc-100 cpk:p-2.5 cpk:text-[11px] cpk:leading-relaxed cpk:text-zinc-800 cpk:whitespace-pre-wrap cpk:break-words cpk:dark:bg-zinc-800/60 cpk:dark:text-zinc-200",
4253
+ children: safeStringifyForPre(parameters ?? {})
4254
+ })] }), result !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
4255
+ className: "cpk:text-[10px] cpk:uppercase cpk:text-zinc-500 cpk:dark:text-zinc-400",
4256
+ children: "Result"
4257
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("pre", {
4258
+ className: "cpk:mt-1.5 cpk:max-h-[200px] cpk:overflow-auto cpk:rounded-md cpk:bg-zinc-100 cpk:p-2.5 cpk:text-[11px] cpk:leading-relaxed cpk:text-zinc-800 cpk:whitespace-pre-wrap cpk:break-words cpk:dark:bg-zinc-800/60 cpk:dark:text-zinc-200",
4259
+ children: typeof result === "string" ? result : safeStringifyForPre(result)
4260
+ })] })]
4261
+ })]
4262
+ })
4263
+ });
4264
+ }
4265
+ function safeStringifyForAttr(value) {
4266
+ if (value === void 0 || value === null) return "";
4267
+ if (typeof value === "string") return value;
4268
+ try {
4269
+ return JSON.stringify(value);
4270
+ } catch (err) {
4271
+ console.warn("[CopilotKit] Failed to JSON.stringify tool-call payload for data-* attribute; falling back to String():", err);
4272
+ try {
4273
+ return String(value);
4274
+ } catch (innerErr) {
4275
+ console.warn("[CopilotKit] safeStringifyForAttr: value could not be stringified:", innerErr);
4276
+ return "";
4021
4277
  }
4022
- copilotkit.addTool(tool);
4023
- if (tool.render) copilotkit.addHookRenderToolCall({
4024
- name,
4025
- args: tool.parameters,
4026
- agentId: tool.agentId,
4027
- render: tool.render
4028
- });
4029
- return () => {
4030
- copilotkit.removeTool(name, tool.agentId);
4031
- };
4032
- }, [
4033
- tool.name,
4034
- tool.available,
4035
- copilotkit,
4036
- JSON.stringify(extraDeps)
4037
- ]);
4038
- }
4039
-
4040
- //#endregion
4041
- //#region src/v2/hooks/use-component.tsx
4042
- /**
4043
- * Registers a React component as a frontend tool renderer in chat.
4044
- *
4045
- * This hook is a convenience wrapper around `useFrontendTool` that:
4046
- * - builds a model-facing tool description,
4047
- * - forwards optional schema parameters (any Standard Schema V1 compatible library),
4048
- * - renders your component with tool call parameters.
4049
- *
4050
- * Use this when you want to display a typed visual component for a tool call
4051
- * without manually wiring a full frontend tool object.
4052
- *
4053
- * When `parameters` is provided, render props are inferred from the schema.
4054
- * When omitted, the render component may accept any props.
4055
- *
4056
- * @typeParam TSchema - Schema describing tool parameters, or `undefined` when no schema is given.
4057
- * @param config - Tool registration config.
4058
- * @param deps - Optional dependencies to refresh registration (same semantics as `useEffect`).
4059
- *
4060
- * @example
4061
- * ```tsx
4062
- * // Without parameters — render accepts any props
4063
- * useComponent({
4064
- * name: "showGreeting",
4065
- * render: ({ message }: { message: string }) => <div>{message}</div>,
4066
- * });
4067
- * ```
4068
- *
4069
- * @example
4070
- * ```tsx
4071
- * // With parameters — render props inferred from schema
4072
- * useComponent({
4073
- * name: "showWeatherCard",
4074
- * parameters: z.object({ city: z.string() }),
4075
- * render: ({ city }) => <div>{city}</div>,
4076
- * });
4077
- * ```
4078
- *
4079
- * @example
4080
- * ```tsx
4081
- * useComponent(
4082
- * {
4083
- * name: "renderProfile",
4084
- * parameters: z.object({ userId: z.string() }),
4085
- * render: ProfileCard,
4086
- * agentId: "support-agent",
4087
- * },
4088
- * [selectedAgentId],
4089
- * );
4090
- * ```
4091
- */
4092
- function useComponent(config, deps) {
4093
- const prefix = `Use this tool to display the "${config.name}" component in the chat. This tool renders a visual UI component for the user.`;
4094
- const fullDescription = config.description ? `${prefix}\n\n${config.description}` : prefix;
4095
- useFrontendTool({
4096
- name: config.name,
4097
- description: fullDescription,
4098
- parameters: config.parameters,
4099
- render: ({ args }) => {
4100
- const Component = config.render;
4101
- return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Component, { ...args });
4102
- },
4103
- agentId: config.agentId,
4104
- followUp: config.followUp
4105
- }, deps);
4278
+ }
4106
4279
  }
4107
4280
 
4108
4281
  //#endregion
@@ -4225,7 +4398,7 @@ function useAgent({ agentId, updates, throttleMs } = {}) {
4225
4398
  if (isRuntimeConfigured && (status === _copilotkit_core.CopilotKitCoreRuntimeConnectionStatus.Disconnected || status === _copilotkit_core.CopilotKitCoreRuntimeConnectionStatus.Connecting)) {
4226
4399
  const cached = provisionalAgentCache.current.get(agentId);
4227
4400
  if (cached) {
4228
- cached.headers = { ...copilotkit.headers };
4401
+ copilotkit.applyHeadersToAgent(cached);
4229
4402
  return cached;
4230
4403
  }
4231
4404
  const provisional = new _copilotkit_core.ProxiedCopilotRuntimeAgent({
@@ -4234,14 +4407,14 @@ function useAgent({ agentId, updates, throttleMs } = {}) {
4234
4407
  transport: copilotkit.runtimeTransport,
4235
4408
  runtimeMode: "pending"
4236
4409
  });
4237
- provisional.headers = { ...copilotkit.headers };
4410
+ copilotkit.applyHeadersToAgent(provisional);
4238
4411
  provisionalAgentCache.current.set(agentId, provisional);
4239
4412
  return provisional;
4240
4413
  }
4241
4414
  if (isRuntimeConfigured && status === _copilotkit_core.CopilotKitCoreRuntimeConnectionStatus.Error) {
4242
4415
  const cached = provisionalAgentCache.current.get(agentId);
4243
4416
  if (cached) {
4244
- cached.headers = { ...copilotkit.headers };
4417
+ copilotkit.applyHeadersToAgent(cached);
4245
4418
  return cached;
4246
4419
  }
4247
4420
  const provisional = new _copilotkit_core.ProxiedCopilotRuntimeAgent({
@@ -4250,7 +4423,7 @@ function useAgent({ agentId, updates, throttleMs } = {}) {
4250
4423
  transport: copilotkit.runtimeTransport,
4251
4424
  runtimeMode: "pending"
4252
4425
  });
4253
- provisional.headers = { ...copilotkit.headers };
4426
+ copilotkit.applyHeadersToAgent(provisional);
4254
4427
  provisionalAgentCache.current.set(agentId, provisional);
4255
4428
  return provisional;
4256
4429
  }
@@ -4301,7 +4474,7 @@ function useAgent({ agentId, updates, throttleMs } = {}) {
4301
4474
  updateFlags
4302
4475
  ]);
4303
4476
  (0, react.useEffect)(() => {
4304
- if (agent instanceof _ag_ui_client.HttpAgent) agent.headers = { ...copilotkit.headers };
4477
+ if (agent instanceof _ag_ui_client.HttpAgent) copilotkit.applyHeadersToAgent(agent);
4305
4478
  }, [agent, JSON.stringify(copilotkit.headers)]);
4306
4479
  const chatConfig = useCopilotChatConfiguration();
4307
4480
  const configThreadId = chatConfig?.threadId;
@@ -4578,114 +4751,165 @@ const INTERRUPT_EVENT_NAME = "on_interrupt";
4578
4751
  function isPromiseLike(value) {
4579
4752
  return (typeof value === "object" || typeof value === "function") && value !== null && typeof Reflect.get(value, "then") === "function";
4580
4753
  }
4754
+ /** Derive the legacy-compatible `event` for any pending interrupt. */
4755
+ function toLegacyEvent(pending) {
4756
+ if (pending.kind === "legacy") return pending.event;
4757
+ return {
4758
+ name: INTERRUPT_EVENT_NAME,
4759
+ value: pending.interrupts[0]
4760
+ };
4761
+ }
4581
4762
  /**
4582
- * Handles agent interrupts (`on_interrupt`) with optional filtering, preprocessing, and resume behavior.
4583
- *
4584
- * The hook listens to custom events on the active agent, stores interrupt payloads per run,
4585
- * and surfaces a render callback once the run finalizes. Call `resolve` from your UI to resume
4586
- * execution with user-provided data.
4587
- *
4588
- * - `renderInChat: true` (default): the element is published into `<CopilotChat>` and this hook returns `void`.
4589
- * - `renderInChat: false`: the hook returns the interrupt element so you can place it anywhere in your component tree.
4763
+ * Handles agent interrupts with optional filtering, preprocessing, and resume behavior.
4590
4764
  *
4591
- * `event.value` is typed as `any` since the interrupt payload shape depends on your agent.
4592
- * Type-narrow it in your callbacks (e.g. `handler`, `enabled`, `render`) as needed.
4765
+ * Supports both the AG-UI standard interrupt flow (`RUN_FINISHED` with
4766
+ * `outcome.type === "interrupt"`) and the legacy custom-event flow
4767
+ * (`on_interrupt`). For standard interrupts, `render` receives `interrupt`
4768
+ * (the primary one) and `interrupts` (the full open set); call `resolve(payload)`
4769
+ * to resume or `cancel()` to cancel. Resuming addresses the targeted interrupt
4770
+ * and, once every open interrupt is addressed, submits a single spec `resume`
4771
+ * array via `copilotkit.runAgent`.
4593
4772
  *
4594
- * @typeParam TResult - Inferred from `handler` return type. Exposed as `result` in `render`.
4595
- * @param config - Interrupt configuration (renderer, optional handler/filter, and render mode).
4596
- * @returns When `renderInChat` is `false`, returns the interrupt element (or `null` when idle).
4597
- * Otherwise returns `void` and publishes the element into chat. In `render`, `result` is always
4598
- * either the handler's resolved return value or `null` (including when no handler is provided,
4599
- * when filtering skips the interrupt, or when handler execution fails).
4600
- *
4601
- * @example
4602
- * ```tsx
4603
- * import { useInterrupt } from "@copilotkit/react-core/v2";
4604
- *
4605
- * function InterruptUI() {
4606
- * useInterrupt({
4607
- * render: ({ event, resolve }) => (
4608
- * <div>
4609
- * <p>{event.value.question}</p>
4610
- * <button onClick={() => resolve({ approved: true })}>Approve</button>
4611
- * <button onClick={() => resolve({ approved: false })}>Reject</button>
4612
- * </div>
4613
- * ),
4614
- * });
4615
- *
4616
- * return null;
4617
- * }
4618
- * ```
4773
+ * - `renderInChat: true` (default): the element is published into `<CopilotChat>`; returns `void`.
4774
+ * - `renderInChat: false`: the hook returns the interrupt element for manual placement.
4619
4775
  *
4620
4776
  * @example
4621
4777
  * ```tsx
4622
- * import { useInterrupt } from "@copilotkit/react-core/v2";
4623
- *
4624
- * function CustomPanel() {
4625
- * const interruptElement = useInterrupt({
4626
- * renderInChat: false,
4627
- * enabled: (event) => event.value.startsWith("approval:"),
4628
- * handler: async ({ event }) => ({ label: event.value.toUpperCase() }),
4629
- * render: ({ event, result, resolve }) => (
4630
- * <aside>
4631
- * <strong>{result?.label ?? ""}</strong>
4632
- * <button onClick={() => resolve({ value: event.value })}>Continue</button>
4633
- * </aside>
4634
- * ),
4635
- * });
4636
- *
4637
- * return <>{interruptElement}</>;
4638
- * }
4778
+ * useInterrupt({
4779
+ * render: ({ interrupt, resolve, cancel }) => (
4780
+ * <div>
4781
+ * <p>{interrupt?.message}</p>
4782
+ * <button onClick={() => resolve({ approved: true })}>Approve</button>
4783
+ * <button onClick={() => cancel()}>Cancel</button>
4784
+ * </div>
4785
+ * ),
4786
+ * });
4639
4787
  * ```
4640
4788
  */
4641
4789
  function useInterrupt(config) {
4642
4790
  const { copilotkit } = useCopilotKit();
4643
4791
  const { agent } = useAgent({ agentId: config.agentId });
4644
- const [pendingEvent, setPendingEvent] = (0, react.useState)(null);
4645
- const pendingEventRef = (0, react.useRef)(pendingEvent);
4646
- pendingEventRef.current = pendingEvent;
4792
+ const [pending, setPending] = (0, react.useState)(null);
4793
+ const pendingRef = (0, react.useRef)(pending);
4794
+ pendingRef.current = pending;
4647
4795
  const [handlerResult, setHandlerResult] = (0, react.useState)(null);
4796
+ const responsesRef = (0, react.useRef)({});
4648
4797
  (0, react.useEffect)(() => {
4649
- let localInterrupt = null;
4798
+ let localLegacy = null;
4799
+ let localStandard = null;
4650
4800
  const subscription = agent.subscribe({
4651
4801
  onCustomEvent: ({ event }) => {
4652
- if (event.name === INTERRUPT_EVENT_NAME) localInterrupt = {
4802
+ if (event.name === INTERRUPT_EVENT_NAME) localLegacy = {
4653
4803
  name: event.name,
4654
4804
  value: event.value
4655
4805
  };
4656
4806
  },
4807
+ onRunFinishedEvent: (params) => {
4808
+ if (params.outcome === "interrupt") localStandard = params.interrupts;
4809
+ },
4657
4810
  onRunStartedEvent: () => {
4658
- localInterrupt = null;
4659
- setPendingEvent(null);
4811
+ localLegacy = null;
4812
+ localStandard = null;
4813
+ responsesRef.current = {};
4814
+ setPending(null);
4660
4815
  },
4661
4816
  onRunFinalized: () => {
4662
- if (localInterrupt) {
4663
- setPendingEvent(localInterrupt);
4664
- localInterrupt = null;
4665
- }
4817
+ if (localStandard && localStandard.length > 0) setPending({
4818
+ kind: "standard",
4819
+ interrupts: localStandard
4820
+ });
4821
+ else if (localLegacy) setPending({
4822
+ kind: "legacy",
4823
+ event: localLegacy
4824
+ });
4825
+ localLegacy = null;
4826
+ localStandard = null;
4666
4827
  },
4667
4828
  onRunFailed: () => {
4668
- localInterrupt = null;
4669
- setPendingEvent(null);
4829
+ localLegacy = null;
4830
+ localStandard = null;
4831
+ responsesRef.current = {};
4832
+ setPending(null);
4670
4833
  }
4671
4834
  });
4672
4835
  return () => subscription.unsubscribe();
4673
4836
  }, [agent]);
4674
- const resolve = (0, react.useCallback)(async (response) => {
4837
+ const submitStandardIfComplete = (0, react.useCallback)(async (interrupts) => {
4838
+ if (!interrupts.every((i) => responsesRef.current[i.id])) return;
4839
+ const expired = interrupts.find((i) => (0, _ag_ui_client.isInterruptExpired)(i));
4840
+ if (expired) {
4841
+ console.error(`[CopilotKit] useInterrupt: interrupt ${expired.id} expired at ${expired.expiresAt}; not resuming.`);
4842
+ responsesRef.current = {};
4843
+ setPending(null);
4844
+ return;
4845
+ }
4846
+ const resume = (0, _ag_ui_client.buildResumeArray)(interrupts, responsesRef.current);
4847
+ for (const i of interrupts) {
4848
+ if (!i.toolCallId) continue;
4849
+ const response = responsesRef.current[i.id];
4850
+ const content = response.status === "cancelled" ? { status: "cancelled" } : response.payload ?? { status: "resolved" };
4851
+ agent.addMessage({
4852
+ id: (0, _ag_ui_client.randomUUID)(),
4853
+ role: "tool",
4854
+ toolCallId: i.toolCallId,
4855
+ content: JSON.stringify(content)
4856
+ });
4857
+ }
4858
+ responsesRef.current = {};
4675
4859
  try {
4860
+ return await copilotkit.runAgent({
4861
+ agent,
4862
+ resume
4863
+ });
4864
+ } catch (err) {
4865
+ console.error("[CopilotKit] useInterrupt resolve: runAgent rejected; clearing pending + rethrowing", err);
4866
+ setPending(null);
4867
+ throw err;
4868
+ }
4869
+ }, [agent, copilotkit]);
4870
+ const resolve = (0, react.useCallback)(async (payload, interruptId) => {
4871
+ const current = pendingRef.current;
4872
+ if (!current) return;
4873
+ if (current.kind === "legacy") try {
4676
4874
  return await copilotkit.runAgent({
4677
4875
  agent,
4678
4876
  forwardedProps: { command: {
4679
- resume: response,
4680
- interruptEvent: pendingEventRef.current?.value
4877
+ resume: payload,
4878
+ interruptEvent: current.event.value
4681
4879
  } }
4682
4880
  });
4683
4881
  } catch (err) {
4684
4882
  console.error("[CopilotKit] useInterrupt resolve: runAgent rejected; clearing pending + rethrowing", err);
4685
- setPendingEvent(null);
4883
+ setPending(null);
4686
4884
  throw err;
4687
4885
  }
4688
- }, [agent, copilotkit]);
4886
+ if (current.interrupts.length > 1 && interruptId === void 0) console.warn(`[CopilotKit] useInterrupt: resolve()/cancel() called without an interruptId while ${current.interrupts.length} interrupts are open; defaulting to the first. Pass an interruptId to address a specific interrupt.`);
4887
+ const id = interruptId ?? current.interrupts[0]?.id;
4888
+ if (!id) return;
4889
+ responsesRef.current[id] = {
4890
+ status: "resolved",
4891
+ payload
4892
+ };
4893
+ return submitStandardIfComplete(current.interrupts);
4894
+ }, [
4895
+ agent,
4896
+ copilotkit,
4897
+ submitStandardIfComplete
4898
+ ]);
4899
+ const cancel = (0, react.useCallback)(async (interruptId) => {
4900
+ const current = pendingRef.current;
4901
+ if (!current) return;
4902
+ if (current.kind === "legacy") {
4903
+ console.warn("[CopilotKit] useInterrupt: cancel() is not supported for legacy on_interrupt interrupts; dismissing.");
4904
+ setPending(null);
4905
+ return;
4906
+ }
4907
+ if (current.interrupts.length > 1 && interruptId === void 0) console.warn(`[CopilotKit] useInterrupt: resolve()/cancel() called without an interruptId while ${current.interrupts.length} interrupts are open; defaulting to the first. Pass an interruptId to address a specific interrupt.`);
4908
+ const id = interruptId ?? current.interrupts[0]?.id;
4909
+ if (!id) return;
4910
+ responsesRef.current[id] = { status: "cancelled" };
4911
+ return submitStandardIfComplete(current.interrupts);
4912
+ }, [submitStandardIfComplete]);
4689
4913
  const renderRef = (0, react.useRef)(config.render);
4690
4914
  renderRef.current = config.render;
4691
4915
  const enabledRef = (0, react.useRef)(config.enabled);
@@ -4694,6 +4918,8 @@ function useInterrupt(config) {
4694
4918
  handlerRef.current = config.handler;
4695
4919
  const resolveRef = (0, react.useRef)(resolve);
4696
4920
  resolveRef.current = resolve;
4921
+ const cancelRef = (0, react.useRef)(cancel);
4922
+ cancelRef.current = cancel;
4697
4923
  const isEnabled = (event) => {
4698
4924
  const predicate = enabledRef.current;
4699
4925
  if (!predicate) return true;
@@ -4705,11 +4931,12 @@ function useInterrupt(config) {
4705
4931
  }
4706
4932
  };
4707
4933
  (0, react.useEffect)(() => {
4708
- if (!pendingEvent) {
4934
+ if (!pending) {
4709
4935
  setHandlerResult(null);
4710
4936
  return;
4711
4937
  }
4712
- if (!isEnabled(pendingEvent)) {
4938
+ const legacyEvent = toLegacyEvent(pending);
4939
+ if (!isEnabled(legacyEvent)) {
4713
4940
  setHandlerResult(null);
4714
4941
  return;
4715
4942
  }
@@ -4722,8 +4949,11 @@ function useInterrupt(config) {
4722
4949
  let maybePromise;
4723
4950
  try {
4724
4951
  maybePromise = handler({
4725
- event: pendingEvent,
4726
- resolve: resolveRef.current
4952
+ event: legacyEvent,
4953
+ interrupt: pending.kind === "standard" ? pending.interrupts[0] : null,
4954
+ interrupts: pending.kind === "standard" ? pending.interrupts : [],
4955
+ resolve: resolveRef.current,
4956
+ cancel: cancelRef.current
4727
4957
  });
4728
4958
  } catch (err) {
4729
4959
  console.error("[CopilotKit] useInterrupt handler threw; result will be null:", err);
@@ -4742,19 +4972,24 @@ function useInterrupt(config) {
4742
4972
  return () => {
4743
4973
  cancelled = true;
4744
4974
  };
4745
- }, [pendingEvent]);
4975
+ }, [pending]);
4746
4976
  const element = (0, react.useMemo)(() => {
4747
- if (!pendingEvent) return null;
4748
- if (!isEnabled(pendingEvent)) return null;
4977
+ if (!pending) return null;
4978
+ const legacyEvent = toLegacyEvent(pending);
4979
+ if (!isEnabled(legacyEvent)) return null;
4749
4980
  return renderRef.current({
4750
- event: pendingEvent,
4981
+ event: legacyEvent,
4982
+ interrupt: pending.kind === "standard" ? pending.interrupts[0] : null,
4983
+ interrupts: pending.kind === "standard" ? pending.interrupts : [],
4751
4984
  result: handlerResult,
4752
- resolve
4985
+ resolve,
4986
+ cancel
4753
4987
  });
4754
4988
  }, [
4755
- pendingEvent,
4989
+ pending,
4756
4990
  handlerResult,
4757
- resolve
4991
+ resolve,
4992
+ cancel
4758
4993
  ]);
4759
4994
  (0, react.useEffect)(() => {
4760
4995
  if (config.renderInChat === false) return;
@@ -4779,7 +5014,7 @@ function useThreadStoreSelector(store, selector) {
4779
5014
  return (0, react.useSyncExternalStore)((0, react.useCallback)((onStoreChange) => {
4780
5015
  const subscription = store.select(selector).subscribe(onStoreChange);
4781
5016
  return () => subscription.unsubscribe();
4782
- }, [store, selector]), () => selector(store.getState()));
5017
+ }, [store, selector]), () => selector(store.getState()), () => selector(store.getServerState()));
4783
5018
  }
4784
5019
  /**
4785
5020
  * React hook for listing and managing Intelligence platform threads.
@@ -4790,9 +5025,9 @@ function useThreadStoreSelector(store, selector) {
4790
5025
  * current without polling — thread creates, renames, archives, and deletes
4791
5026
  * from any client are reflected immediately.
4792
5027
  *
4793
- * Mutation methods (`renameThread`, `archiveThread`, `deleteThread`) return
4794
- * promises that resolve once the platform confirms the operation and reject
4795
- * with an `Error` on failure.
5028
+ * Mutation methods (`renameThread`, `archiveThread`, `unarchiveThread`,
5029
+ * `deleteThread`) return promises that resolve once the platform confirms the
5030
+ * operation and reject with an `Error` on failure.
4796
5031
  *
4797
5032
  * @param input - Agent identifier and optional list controls.
4798
5033
  * @returns Thread list state and stable mutation callbacks.
@@ -4822,7 +5057,7 @@ function useThreadStoreSelector(store, selector) {
4822
5057
  * }
4823
5058
  * ```
4824
5059
  */
4825
- function useThreads$1({ agentId, includeArchived, limit }) {
5060
+ function useThreads$1({ agentId, includeArchived, limit, enabled = true }) {
4826
5061
  const { copilotkit } = useCopilotKit();
4827
5062
  const [store] = (0, react.useState)(() => (0, _copilotkit_core.ɵcreateThreadStore)({ fetch: globalThis.fetch }));
4828
5063
  const coreThreads = useThreadStoreSelector(store, _copilotkit_core.ɵselectThreads);
@@ -4839,6 +5074,7 @@ function useThreads$1({ agentId, includeArchived, limit }) {
4839
5074
  const storeError = useThreadStoreSelector(store, _copilotkit_core.ɵselectThreadsError);
4840
5075
  const hasMoreThreads = useThreadStoreSelector(store, _copilotkit_core.ɵselectHasNextPage);
4841
5076
  const isFetchingMoreThreads = useThreadStoreSelector(store, _copilotkit_core.ɵselectIsFetchingNextPage);
5077
+ const isMutating = useThreadStoreSelector(store, _copilotkit_core.ɵselectIsMutating);
4842
5078
  const headersKey = (0, react.useMemo)(() => {
4843
5079
  return JSON.stringify(Object.entries(copilotkit.headers ?? {}).sort(([left], [right]) => left.localeCompare(right)));
4844
5080
  }, [copilotkit.headers]);
@@ -4859,9 +5095,16 @@ function useThreads$1({ agentId, includeArchived, limit }) {
4859
5095
  return /* @__PURE__ */ new Error("Thread mutations are not available on this CopilotKit runtime");
4860
5096
  }, [threadMutationsSupported]);
4861
5097
  const [hasDispatchedContext, setHasDispatchedContext] = (0, react.useState)(false);
4862
- const preConnectLoading = !!copilotkit.runtimeUrl && !threadEndpointsUnavailable && !hasDispatchedContext;
4863
- const isLoading = runtimeError || threadEndpointsError ? false : preConnectLoading || storeIsLoading;
4864
- const error = runtimeError ?? threadEndpointsError ?? storeError;
5098
+ const preConnectLoading = enabled && !!copilotkit.runtimeUrl && !threadEndpointsUnavailable && !hasDispatchedContext;
5099
+ const [configErrorDismissed, setConfigErrorDismissed] = (0, react.useState)(false);
5100
+ (0, react.useEffect)(() => {
5101
+ setConfigErrorDismissed(false);
5102
+ }, [runtimeError, threadEndpointsError]);
5103
+ const activeRuntimeError = configErrorDismissed ? null : runtimeError;
5104
+ const activeThreadEndpointsError = configErrorDismissed ? null : threadEndpointsError;
5105
+ const isLoading = activeRuntimeError || activeThreadEndpointsError ? false : preConnectLoading || storeIsLoading;
5106
+ const error = activeRuntimeError ?? activeThreadEndpointsError ?? storeError;
5107
+ const listError = storeError;
4865
5108
  (0, react.useEffect)(() => {
4866
5109
  store.start();
4867
5110
  return () => {
@@ -4869,6 +5112,7 @@ function useThreads$1({ agentId, includeArchived, limit }) {
4869
5112
  };
4870
5113
  }, [store]);
4871
5114
  (0, react.useEffect)(() => {
5115
+ if (!enabled) return;
4872
5116
  copilotkit.registerThreadStore(agentId, store);
4873
5117
  return () => {
4874
5118
  copilotkit.unregisterThreadStore(agentId);
@@ -4876,9 +5120,15 @@ function useThreads$1({ agentId, includeArchived, limit }) {
4876
5120
  }, [
4877
5121
  copilotkit,
4878
5122
  agentId,
4879
- store
5123
+ store,
5124
+ enabled
4880
5125
  ]);
4881
5126
  (0, react.useEffect)(() => {
5127
+ if (!enabled) {
5128
+ store.setContext(null);
5129
+ setHasDispatchedContext(false);
5130
+ return;
5131
+ }
4882
5132
  if (!copilotkit.runtimeUrl) {
4883
5133
  store.setContext(null);
4884
5134
  setHasDispatchedContext(false);
@@ -4902,6 +5152,7 @@ function useThreads$1({ agentId, includeArchived, limit }) {
4902
5152
  setHasDispatchedContext(true);
4903
5153
  }, [
4904
5154
  store,
5155
+ enabled,
4905
5156
  copilotkit.runtimeUrl,
4906
5157
  runtimeStatus,
4907
5158
  headersKey,
@@ -4919,16 +5170,25 @@ function useThreads$1({ agentId, includeArchived, limit }) {
4919
5170
  }, [threadMutationsError]);
4920
5171
  const renameThread = (0, react.useMemo)(() => guardMutation((threadId, name) => store.renameThread(threadId, name)), [store, guardMutation]);
4921
5172
  const archiveThread = (0, react.useMemo)(() => guardMutation((threadId) => store.archiveThread(threadId)), [store, guardMutation]);
5173
+ const unarchiveThread = (0, react.useMemo)(() => guardMutation((threadId) => store.unarchiveThread(threadId)), [store, guardMutation]);
4922
5174
  const deleteThread = (0, react.useMemo)(() => guardMutation((threadId) => store.deleteThread(threadId)), [store, guardMutation]);
4923
5175
  return {
4924
5176
  threads,
4925
5177
  isLoading,
4926
5178
  error,
5179
+ listError,
4927
5180
  hasMoreThreads,
4928
5181
  isFetchingMoreThreads,
5182
+ isMutating,
4929
5183
  fetchMoreThreads: (0, react.useCallback)(() => store.fetchNextPage(), [store]),
5184
+ refetchThreads: (0, react.useCallback)(() => store.refetchThreads(), [store]),
5185
+ startNewThread: (0, react.useCallback)(() => {
5186
+ setConfigErrorDismissed(true);
5187
+ store.startNewThread();
5188
+ }, [store]),
4930
5189
  renameThread,
4931
5190
  archiveThread,
5191
+ unarchiveThread,
4932
5192
  deleteThread
4933
5193
  };
4934
5194
  }
@@ -6112,7 +6372,7 @@ const DefaultContainer = react.default.forwardRef(function DefaultContainer({ cl
6112
6372
  ref,
6113
6373
  "data-copilotkit": true,
6114
6374
  "data-testid": "copilot-suggestions",
6115
- className: cn("cpk:flex cpk:flex-wrap cpk:items-center cpk:gap-1.5 cpk:sm:gap-2 cpk:pl-0 cpk:pr-4 cpk:sm:px-0 cpk:pointer-events-none", className),
6375
+ className: cn("cpk:flex cpk:flex-wrap cpk:items-center cpk:gap-1.5 cpk:sm:gap-2 cpk:pl-0 cpk:pr-4 cpk:@3xl:px-0 cpk:pointer-events-none", className),
6116
6376
  ...props
6117
6377
  });
6118
6378
  });
@@ -7217,7 +7477,7 @@ function CopilotChatView({ messageView, input, scrollView, suggestionView, welco
7217
7477
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
7218
7478
  className: "cpk:max-w-3xl cpk:mx-auto",
7219
7479
  children: [BoundMessageView, hasSuggestions ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
7220
- className: "cpk:pl-0 cpk:pr-4 cpk:sm:px-0 cpk:mt-4",
7480
+ className: "cpk:pl-0 cpk:pr-4 cpk:@3xl:px-0 cpk:mt-4",
7221
7481
  children: BoundSuggestionView
7222
7482
  }) : null]
7223
7483
  })
@@ -7260,7 +7520,7 @@ function CopilotChatView({ messageView, input, scrollView, suggestionView, welco
7260
7520
  onDragOver,
7261
7521
  onDragLeave,
7262
7522
  onDrop,
7263
- className: cn("copilotKitChat cpk:relative cpk:h-full cpk:flex cpk:flex-col", className),
7523
+ className: cn("copilotKitChat cpk:@container cpk:relative cpk:h-full cpk:flex cpk:flex-col", className),
7264
7524
  ...props,
7265
7525
  children: [dragOver && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DropOverlay, {}), BoundWelcomeScreen]
7266
7526
  });
@@ -7282,7 +7542,7 @@ function CopilotChatView({ messageView, input, scrollView, suggestionView, welco
7282
7542
  onDragOver,
7283
7543
  onDragLeave,
7284
7544
  onDrop,
7285
- className: cn("copilotKitChat cpk:relative cpk:h-full cpk:flex cpk:flex-col", className),
7545
+ className: cn("copilotKitChat cpk:@container cpk:relative cpk:h-full cpk:flex cpk:flex-col", className),
7286
7546
  ...props,
7287
7547
  children: [
7288
7548
  dragOver && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DropOverlay, {}),
@@ -7321,7 +7581,7 @@ function CopilotChatView({ messageView, input, scrollView, suggestionView, welco
7321
7581
  minHeight: 0
7322
7582
  },
7323
7583
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
7324
- className: "cpk:px-4 cpk:sm:px-0 cpk:[div[data-sidebar-chat]_&]:px-8 cpk:[div[data-popup-chat]_&]:px-6",
7584
+ className: "cpk:px-4 cpk:@3xl:px-0 cpk:[div[data-sidebar-chat]_&]:px-8 cpk:[div[data-popup-chat]_&]:px-6",
7325
7585
  children
7326
7586
  })
7327
7587
  }),
@@ -7354,7 +7614,7 @@ function CopilotChatView({ messageView, input, scrollView, suggestionView, welco
7354
7614
  ...props,
7355
7615
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
7356
7616
  ref: contentRef,
7357
- className: "cpk:px-4 cpk:sm:px-0 cpk:[div[data-sidebar-chat]_&]:px-8 cpk:[div[data-popup-chat]_&]:px-6",
7617
+ className: "cpk:px-4 cpk:@3xl:px-0 cpk:[div[data-sidebar-chat]_&]:px-8 cpk:[div[data-popup-chat]_&]:px-6",
7358
7618
  children
7359
7619
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
7360
7620
  ref: spacerRef,
@@ -7416,7 +7676,7 @@ function CopilotChatView({ messageView, input, scrollView, suggestionView, welco
7416
7676
  if (!hasMounted) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
7417
7677
  className: "cpk:h-full cpk:max-h-full cpk:flex cpk:flex-col cpk:min-h-0 cpk:overflow-y-auto cpk:overflow-x-hidden",
7418
7678
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
7419
- className: "cpk:px-4 cpk:sm:px-0 cpk:[div[data-sidebar-chat]_&]:px-8 cpk:[div[data-popup-chat]_&]:px-6",
7679
+ className: "cpk:px-4 cpk:@3xl:px-0 cpk:[div[data-sidebar-chat]_&]:px-8 cpk:[div[data-popup-chat]_&]:px-6",
7420
7680
  children
7421
7681
  })
7422
7682
  });
@@ -7431,7 +7691,7 @@ function CopilotChatView({ messageView, input, scrollView, suggestionView, welco
7431
7691
  children: [
7432
7692
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
7433
7693
  ref: contentRef,
7434
- className: "cpk:px-4 cpk:sm:px-0 cpk:[div[data-sidebar-chat]_&]:px-8 cpk:[div[data-popup-chat]_&]:px-6",
7694
+ className: "cpk:px-4 cpk:@3xl:px-0 cpk:[div[data-sidebar-chat]_&]:px-8 cpk:[div[data-popup-chat]_&]:px-6",
7435
7695
  children
7436
7696
  }),
7437
7697
  BoundFeather,
@@ -7695,9 +7955,17 @@ function CopilotChat({ agentId, threadId, labels, chatView, isModalDefaultOpen,
7695
7955
  const { messageView: providedMessageView, suggestionView: providedSuggestionView, onStop: providedStopHandler, ...restProps } = props;
7696
7956
  const [lastConnectedThreadId, setLastConnectedThreadId] = (0, react.useState)(null);
7697
7957
  const isConnecting = hasExplicitThreadId && lastConnectedThreadId !== resolvedThreadId;
7958
+ const previousThreadIdRef = (0, react.useRef)(null);
7959
+ const hasExplicitThreadIdRef = (0, react.useRef)(hasExplicitThreadId);
7960
+ hasExplicitThreadIdRef.current = hasExplicitThreadId;
7698
7961
  (0, react.useEffect)(() => {
7962
+ const threadChanged = previousThreadIdRef.current !== resolvedThreadId;
7963
+ previousThreadIdRef.current = resolvedThreadId;
7699
7964
  agent.threadId = resolvedThreadId;
7700
- if (!hasExplicitThreadId) return;
7965
+ if (!hasExplicitThreadId) {
7966
+ if (threadChanged && agent.messages.length > 0) agent.setMessages([]);
7967
+ return;
7968
+ }
7701
7969
  let detached = false;
7702
7970
  const connectAbortController = new AbortController();
7703
7971
  if (agent instanceof _ag_ui_client.HttpAgent) agent.abortController = connectAbortController;
@@ -7711,6 +7979,7 @@ function CopilotChat({ agentId, threadId, labels, chatView, isModalDefaultOpen,
7711
7979
  if (!detached) (typeof requestAnimationFrame === "function" ? requestAnimationFrame : (cb) => setTimeout(cb, 16))(() => {
7712
7980
  if (!detached) setLastConnectedThreadId(resolvedThreadId);
7713
7981
  });
7982
+ else if (!hasExplicitThreadIdRef.current) agentToConnect.setMessages([]);
7714
7983
  }
7715
7984
  };
7716
7985
  connect(agent);
@@ -7726,8 +7995,10 @@ function CopilotChat({ agentId, threadId, labels, chatView, isModalDefaultOpen,
7726
7995
  hasExplicitThreadId
7727
7996
  ]);
7728
7997
  const waitForActiveRunToSettle = (0, react.useCallback)(async () => {
7729
- if (agent.isRunning && (0, _copilotkit_core.isRunCompletionAware)(agent) && agent.activeRunCompletionPromise) try {
7730
- await agent.activeRunCompletionPromise;
7998
+ const maybeAware = agent;
7999
+ const activeRunCompletionPromise = (0, _copilotkit_core.isRunCompletionAware)(maybeAware) ? maybeAware.activeRunCompletionPromise : void 0;
8000
+ if (agent.isRunning && activeRunCompletionPromise) try {
8001
+ await activeRunCompletionPromise;
7731
8002
  } catch (error) {
7732
8003
  console.error("CopilotChat: in-flight run rejected while queuing send", error);
7733
8004
  }
@@ -8059,18 +8330,46 @@ CopilotChatToggleButton.displayName = "CopilotChatToggleButton";
8059
8330
 
8060
8331
  //#endregion
8061
8332
  //#region src/v2/components/chat/CopilotModalHeader.tsx
8062
- function CopilotModalHeader({ title, titleContent, closeButton, children, className, ...rest }) {
8333
+ /**
8334
+ * Reactively tracks whether the viewport is in the mobile range (≤767px) — the
8335
+ * same breakpoint the drawer + chat coordination use. SSR-safe: starts `false`
8336
+ * (desktop) so the server render and first client render agree, then syncs on
8337
+ * mount and on resize.
8338
+ */
8339
+ function useIsMobileViewport() {
8340
+ const [isMobile, setIsMobile] = (0, react.useState)(false);
8341
+ (0, react.useEffect)(() => {
8342
+ if (typeof window === "undefined" || typeof window.matchMedia !== "function") return;
8343
+ const mql = window.matchMedia("(max-width: 767px)");
8344
+ const update = () => setIsMobile(mql.matches);
8345
+ update();
8346
+ mql.addEventListener("change", update);
8347
+ return () => mql.removeEventListener("change", update);
8348
+ }, []);
8349
+ return isMobile;
8350
+ }
8351
+ function CopilotModalHeader({ title, titleContent, closeButton, drawerLauncher, children, className, ...rest }) {
8063
8352
  const configuration = useCopilotChatConfiguration();
8064
8353
  const fallbackTitle = configuration?.labels.modalHeaderTitle ?? CopilotChatDefaultLabels.modalHeaderTitle;
8065
8354
  const resolvedTitle = title ?? fallbackTitle;
8355
+ const isMobile = useIsMobileViewport();
8356
+ const drawerRegistered = (configuration?.drawerRegistered ?? false) && isMobile;
8066
8357
  const handleClose = (0, react.useCallback)(() => {
8067
8358
  configuration?.setModalOpen?.(false);
8068
8359
  }, [configuration]);
8360
+ const handleToggleDrawer = (0, react.useCallback)(() => {
8361
+ configuration?.setDrawerOpen?.(!configuration.drawerOpen);
8362
+ }, [configuration]);
8069
8363
  const BoundTitle = renderSlot(titleContent, CopilotModalHeader.Title, { children: resolvedTitle });
8070
8364
  const BoundCloseButton = renderSlot(closeButton, CopilotModalHeader.CloseButton, { onClick: handleClose });
8365
+ const BoundDrawerLauncher = drawerRegistered ? renderSlot(drawerLauncher, CopilotModalHeader.DrawerLauncher, {
8366
+ onClick: handleToggleDrawer,
8367
+ "aria-expanded": configuration?.drawerOpen ?? false
8368
+ }) : null;
8071
8369
  if (children) return children({
8072
8370
  titleContent: BoundTitle,
8073
8371
  closeButton: BoundCloseButton,
8372
+ drawerLauncher: BoundDrawerLauncher,
8074
8373
  title: resolvedTitle,
8075
8374
  ...rest
8076
8375
  });
@@ -8083,8 +8382,8 @@ function CopilotModalHeader({ title, titleContent, closeButton, children, classN
8083
8382
  className: "cpk:flex cpk:w-full cpk:items-center cpk:gap-2",
8084
8383
  children: [
8085
8384
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
8086
- className: "cpk:flex-1",
8087
- "aria-hidden": "true"
8385
+ className: "cpk:flex cpk:flex-1 cpk:justify-start",
8386
+ children: BoundDrawerLauncher ?? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { "aria-hidden": "true" })
8088
8387
  }),
8089
8388
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
8090
8389
  className: "cpk:flex cpk:flex-1 cpk:justify-center cpk:text-center",
@@ -8117,9 +8416,21 @@ CopilotModalHeader.displayName = "CopilotModalHeader";
8117
8416
  "aria-hidden": "true"
8118
8417
  })
8119
8418
  });
8419
+ _CopilotModalHeader.DrawerLauncher = ({ className, ...props }) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
8420
+ type: "button",
8421
+ "data-testid": "copilot-threads-drawer-launcher",
8422
+ className: cn("cpk:inline-flex cpk:size-8 cpk:items-center cpk:justify-center cpk:rounded-full cpk:text-muted-foreground cpk:transition cpk:cursor-pointer", "cpk:hover:bg-muted cpk:hover:text-foreground cpk:focus-visible:outline-none cpk:focus-visible:ring-2 cpk:focus-visible:ring-ring", className),
8423
+ "aria-label": "Open threads",
8424
+ ...props,
8425
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.PanelLeftOpen, {
8426
+ className: "cpk:h-4 cpk:w-4",
8427
+ "aria-hidden": "true"
8428
+ })
8429
+ });
8120
8430
  })(CopilotModalHeader || (CopilotModalHeader = {}));
8121
8431
  CopilotModalHeader.Title.displayName = "CopilotModalHeader.Title";
8122
8432
  CopilotModalHeader.CloseButton.displayName = "CopilotModalHeader.CloseButton";
8433
+ CopilotModalHeader.DrawerLauncher.displayName = "CopilotModalHeader.DrawerLauncher";
8123
8434
 
8124
8435
  //#endregion
8125
8436
  //#region src/v2/components/chat/CopilotSidebarView.tsx
@@ -8477,6 +8788,322 @@ function CopilotPopup({ header, toggleButton, defaultOpen, width, height, clickO
8477
8788
  }
8478
8789
  CopilotPopup.displayName = "CopilotPopup";
8479
8790
 
8791
+ //#endregion
8792
+ //#region src/v2/components/chat/CopilotThreadsDrawer.tsx
8793
+ /**
8794
+ * Maps a {@link Thread} from {@link useThreads} to the element's
8795
+ * {@link DrawerThread} view shape. The shapes are structurally compatible; this
8796
+ * narrows to exactly the fields the element renders so the element never sees
8797
+ * platform-internal fields.
8798
+ */
8799
+ function toDrawerThread(thread) {
8800
+ return {
8801
+ id: thread.id,
8802
+ name: thread.name,
8803
+ archived: thread.archived,
8804
+ createdAt: thread.createdAt,
8805
+ updatedAt: thread.updatedAt,
8806
+ ...thread.lastRunAt !== void 0 ? { lastRunAt: thread.lastRunAt } : {}
8807
+ };
8808
+ }
8809
+ /** The chat input textarea's documented `data-testid`. */
8810
+ const CHAT_INPUT_TESTID = "copilot-chat-textarea";
8811
+ /** The chat view container's documented `data-testid`. */
8812
+ const CHAT_CONTAINER_TESTID = "copilot-chat";
8813
+ /**
8814
+ * Returns the chat input element for focus-return after a thread is selected.
8815
+ *
8816
+ * Best-effort and SCOPED: walks up from the drawer element looking for an
8817
+ * ancestor that contains a chat-view container (`data-testid="copilot-chat"`),
8818
+ * then returns the chat input within that subtree. This avoids focusing the
8819
+ * wrong composer on a page hosting more than one chat (multi-chat dashboards),
8820
+ * where a document-global lookup would grab whichever input appears first in
8821
+ * DOM order rather than the one this drawer drives.
8822
+ *
8823
+ * Falls back to a document-global lookup when no scoping ancestor is found
8824
+ * (e.g. the drawer and chat share no common container, or headless usage),
8825
+ * and returns `null` when there is no chat input at all.
8826
+ *
8827
+ * @param origin - The drawer element to scope the search from.
8828
+ */
8829
+ function findChatInput(origin) {
8830
+ if (typeof document === "undefined") return null;
8831
+ const container = origin?.closest?.(`[data-testid="${CHAT_CONTAINER_TESTID}"]`);
8832
+ if (container) {
8833
+ const scoped = container.querySelector(`[data-testid="${CHAT_INPUT_TESTID}"]`);
8834
+ if (scoped) return scoped;
8835
+ }
8836
+ return document.querySelector(`[data-testid="${CHAT_INPUT_TESTID}"]`);
8837
+ }
8838
+ /**
8839
+ * React wrapper for the shadow-DOM `<copilotkit-threads-drawer>` threads drawer.
8840
+ *
8841
+ * Responsibilities:
8842
+ * - Registers the custom element on the client (SSR-safe; nothing renders
8843
+ * during prerender to avoid hydration mismatch).
8844
+ * - Feeds the element domain data: `threads`, `loading`, `error`,
8845
+ * `activeThreadId`, `licensed`, fetch-more state.
8846
+ * - Routes the element's nine outbound events to core thread operations
8847
+ * ({@link useThreads}) and chat-configuration changes.
8848
+ * - Registers with the surrounding chat configuration so the header
8849
+ * thread-list launcher appears, and binds the element `open` state to the
8850
+ * configuration's `drawerOpen`.
8851
+ *
8852
+ * License gating is two-pronged: the locked view shows when no license is configured
8853
+ * (the runtime reported no license status) OR the `threads` feature is
8854
+ * explicitly unlicensed. While unlicensed, the thread fetch is skipped entirely
8855
+ * so an unlicensed drawer issues no network requests.
8856
+ *
8857
+ * Thread switching needs no host wiring: when `onThreadSelect`/`onNewThread`
8858
+ * are omitted, the wrapper drives the surrounding chat configuration directly
8859
+ * ({@link CopilotChatConfigurationValue.setActiveThreadId} /
8860
+ * {@link CopilotChatConfigurationValue.startNewThread}), so a bare drawer
8861
+ * connects to the picked thread and shows the welcome screen on "+ New". Pass
8862
+ * the callbacks only to take control yourself.
8863
+ *
8864
+ * @example
8865
+ * ```tsx
8866
+ * // Callback-free: the drawer drives the chat configuration itself.
8867
+ * <CopilotKitProvider runtimeUrl="/api/copilotkit" publicLicenseKey="ck_pub_...">
8868
+ * <CopilotChat />
8869
+ * <CopilotThreadsDrawer />
8870
+ * </CopilotKitProvider>
8871
+ * ```
8872
+ */
8873
+ function CopilotThreadsDrawer({ agentId, onThreadSelect, onNewThread, onLicensed, licenseUrl, renderRow, label, limit, "data-testid": dataTestId = "copilot-threads-drawer" }) {
8874
+ const configuration = useCopilotChatConfiguration();
8875
+ const { status, checkFeature } = useLicenseContext();
8876
+ const licensePresent = status === "valid" || status === "expiring";
8877
+ const featureLicensed = checkFeature("threads");
8878
+ const licensed = licensePresent && featureLicensed;
8879
+ const licensePending = status === null;
8880
+ const resolvedAgentId = agentId ?? configuration?.agentId ?? "default";
8881
+ const activeThreadId = configuration?.threadId ?? null;
8882
+ const { threads, isLoading, listError, hasMoreThreads, isFetchingMoreThreads, archiveThread, unarchiveThread, deleteThread, fetchMoreThreads, refetchThreads, startNewThread } = useThreads$1({
8883
+ agentId: resolvedAgentId,
8884
+ includeArchived: true,
8885
+ enabled: licensed,
8886
+ ...limit !== void 0 ? { limit } : {}
8887
+ });
8888
+ const drawerThreads = (0, react.useMemo)(() => threads.map(toDrawerThread), [threads]);
8889
+ const elementRef = (0, react.useRef)(null);
8890
+ const [mounted, setMounted] = (0, react.useState)(false);
8891
+ (0, react.useEffect)(() => {
8892
+ (0, _copilotkit_web_components_threads_drawer.defineCopilotKitThreadsDrawer)();
8893
+ setMounted(true);
8894
+ }, []);
8895
+ const registerDrawer = configuration?.registerDrawer;
8896
+ (0, react.useEffect)(() => {
8897
+ if (!registerDrawer) return;
8898
+ return registerDrawer();
8899
+ }, [registerDrawer]);
8900
+ const [localDrawerOpen, setLocalDrawerOpen] = (0, react.useState)(false);
8901
+ const drawerOpen = configuration ? configuration.drawerOpen : localDrawerOpen;
8902
+ const setDrawerOpen = configuration ? configuration.setDrawerOpen : setLocalDrawerOpen;
8903
+ const setActiveThreadId = configuration?.setActiveThreadId;
8904
+ const startNewThreadConfig = configuration?.startNewThread;
8905
+ const handleThreadSelected = (0, react.useCallback)((threadId) => {
8906
+ if (onThreadSelect) onThreadSelect(threadId);
8907
+ else setActiveThreadId?.(threadId, { explicit: true });
8908
+ findChatInput(elementRef.current)?.focus();
8909
+ }, [onThreadSelect, setActiveThreadId]);
8910
+ const handleNewThread = (0, react.useCallback)(() => {
8911
+ startNewThread();
8912
+ if (onNewThread) onNewThread();
8913
+ else startNewThreadConfig?.();
8914
+ }, [
8915
+ startNewThread,
8916
+ onNewThread,
8917
+ startNewThreadConfig
8918
+ ]);
8919
+ const handleArchive = (0, react.useCallback)((threadId) => {
8920
+ archiveThread(threadId).catch((err) => {
8921
+ console.error("CopilotThreadsDrawer: archiveThread failed", err);
8922
+ });
8923
+ }, [archiveThread]);
8924
+ const handleUnarchive = (0, react.useCallback)((threadId) => {
8925
+ unarchiveThread(threadId).catch((err) => {
8926
+ console.error("CopilotThreadsDrawer: unarchiveThread failed", err);
8927
+ });
8928
+ }, [unarchiveThread]);
8929
+ const handleDelete = (0, react.useCallback)((threadId) => {
8930
+ const isActive = threadId === activeThreadId;
8931
+ deleteThread(threadId).then(() => {
8932
+ if (isActive) {
8933
+ startNewThread();
8934
+ if (onNewThread) onNewThread();
8935
+ else startNewThreadConfig?.();
8936
+ }
8937
+ }).catch((err) => {
8938
+ console.error("CopilotThreadsDrawer: deleteThread failed", err);
8939
+ });
8940
+ }, [
8941
+ deleteThread,
8942
+ activeThreadId,
8943
+ startNewThread,
8944
+ onNewThread,
8945
+ startNewThreadConfig
8946
+ ]);
8947
+ const handleFilterChange = (0, react.useCallback)(() => {
8948
+ refetchThreads();
8949
+ }, [refetchThreads]);
8950
+ const handleRetry = (0, react.useCallback)((scope) => {
8951
+ if (scope === "fetch-more") fetchMoreThreads();
8952
+ else refetchThreads();
8953
+ }, [fetchMoreThreads, refetchThreads]);
8954
+ const handleOpenChange = (0, react.useCallback)((open) => {
8955
+ setDrawerOpen(open);
8956
+ }, [setDrawerOpen]);
8957
+ const handleLicensed = (0, react.useCallback)(() => {
8958
+ onLicensed?.();
8959
+ }, [onLicensed]);
8960
+ const handleLoadMore = (0, react.useCallback)(() => {
8961
+ fetchMoreThreads();
8962
+ }, [fetchMoreThreads]);
8963
+ const handlersRef = (0, react.useRef)({
8964
+ handleThreadSelected,
8965
+ handleNewThread,
8966
+ handleArchive,
8967
+ handleUnarchive,
8968
+ handleDelete,
8969
+ handleFilterChange,
8970
+ handleRetry,
8971
+ handleOpenChange,
8972
+ handleLicensed,
8973
+ handleLoadMore
8974
+ });
8975
+ handlersRef.current = {
8976
+ handleThreadSelected,
8977
+ handleNewThread,
8978
+ handleArchive,
8979
+ handleUnarchive,
8980
+ handleDelete,
8981
+ handleFilterChange,
8982
+ handleRetry,
8983
+ handleOpenChange,
8984
+ handleLicensed,
8985
+ handleLoadMore
8986
+ };
8987
+ (0, react.useEffect)(() => {
8988
+ const el = elementRef.current;
8989
+ if (!el) return;
8990
+ const onThreadSelected = (event) => {
8991
+ const detail = event.detail;
8992
+ handlersRef.current.handleThreadSelected(detail.threadId);
8993
+ };
8994
+ const onNewThreadEvent = () => handlersRef.current.handleNewThread();
8995
+ const onArchive = (event) => {
8996
+ const detail = event.detail;
8997
+ handlersRef.current.handleArchive(detail.threadId);
8998
+ };
8999
+ const onUnarchive = (event) => {
9000
+ const detail = event.detail;
9001
+ handlersRef.current.handleUnarchive(detail.threadId);
9002
+ };
9003
+ const onDelete = (event) => {
9004
+ const detail = event.detail;
9005
+ handlersRef.current.handleDelete(detail.threadId);
9006
+ };
9007
+ const onFilterChange = (_event) => {
9008
+ handlersRef.current.handleFilterChange();
9009
+ };
9010
+ const onOpenChangeEvent = (event) => {
9011
+ const detail = event.detail;
9012
+ handlersRef.current.handleOpenChange(detail.open);
9013
+ };
9014
+ const onRetry = (event) => {
9015
+ const detail = event.detail;
9016
+ handlersRef.current.handleRetry(detail.scope);
9017
+ };
9018
+ const onLicensedEvent = () => handlersRef.current.handleLicensed();
9019
+ const onLoadMore = () => handlersRef.current.handleLoadMore();
9020
+ el.addEventListener("thread-selected", onThreadSelected);
9021
+ el.addEventListener("new-thread", onNewThreadEvent);
9022
+ el.addEventListener("archive", onArchive);
9023
+ el.addEventListener("unarchive", onUnarchive);
9024
+ el.addEventListener("delete", onDelete);
9025
+ el.addEventListener("filter-change", onFilterChange);
9026
+ el.addEventListener("open-change", onOpenChangeEvent);
9027
+ el.addEventListener("retry", onRetry);
9028
+ el.addEventListener("licensed", onLicensedEvent);
9029
+ el.addEventListener("load-more", onLoadMore);
9030
+ return () => {
9031
+ el.removeEventListener("thread-selected", onThreadSelected);
9032
+ el.removeEventListener("new-thread", onNewThreadEvent);
9033
+ el.removeEventListener("archive", onArchive);
9034
+ el.removeEventListener("unarchive", onUnarchive);
9035
+ el.removeEventListener("delete", onDelete);
9036
+ el.removeEventListener("filter-change", onFilterChange);
9037
+ el.removeEventListener("open-change", onOpenChangeEvent);
9038
+ el.removeEventListener("retry", onRetry);
9039
+ el.removeEventListener("licensed", onLicensedEvent);
9040
+ el.removeEventListener("load-more", onLoadMore);
9041
+ };
9042
+ }, [mounted]);
9043
+ (0, react.useEffect)(() => {
9044
+ const el = elementRef.current;
9045
+ if (!el) return;
9046
+ el.threads = drawerThreads;
9047
+ }, [drawerThreads, mounted]);
9048
+ (0, react.useEffect)(() => {
9049
+ const el = elementRef.current;
9050
+ if (!el) return;
9051
+ el.loading = isLoading || licensePending;
9052
+ el.error = listError ? listError.message : null;
9053
+ el.activeThreadId = activeThreadId;
9054
+ el.licensed = licensed || licensePending;
9055
+ el.hasMore = hasMoreThreads;
9056
+ el.fetchingMore = isFetchingMoreThreads;
9057
+ }, [
9058
+ isLoading,
9059
+ listError,
9060
+ activeThreadId,
9061
+ licensed,
9062
+ licensePending,
9063
+ hasMoreThreads,
9064
+ isFetchingMoreThreads,
9065
+ mounted
9066
+ ]);
9067
+ (0, react.useEffect)(() => {
9068
+ const el = elementRef.current;
9069
+ if (!el) return;
9070
+ el.open = drawerOpen;
9071
+ }, [drawerOpen, mounted]);
9072
+ (0, react.useEffect)(() => {
9073
+ const el = elementRef.current;
9074
+ if (!el) return;
9075
+ if (label !== void 0) el.label = label;
9076
+ }, [label, mounted]);
9077
+ (0, react.useEffect)(() => {
9078
+ const el = elementRef.current;
9079
+ if (!el) return;
9080
+ if (licenseUrl !== void 0) el.licenseUrl = licenseUrl;
9081
+ }, [licenseUrl, mounted]);
9082
+ const rowChildren = (0, react.useMemo)(() => {
9083
+ if (!renderRow) return null;
9084
+ return drawerThreads.map((drawerThread) => {
9085
+ const fullThread = threads.find((t) => t.id === drawerThread.id);
9086
+ if (!fullThread) return null;
9087
+ const content = renderRow(fullThread);
9088
+ if (content === null || content === void 0) return null;
9089
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
9090
+ slot: `row:${drawerThread.id}`,
9091
+ children: content
9092
+ }, drawerThread.id);
9093
+ });
9094
+ }, [
9095
+ renderRow,
9096
+ drawerThreads,
9097
+ threads
9098
+ ]);
9099
+ if (!mounted) return null;
9100
+ return react.default.createElement(_copilotkit_web_components_threads_drawer.COPILOTKIT_THREADS_DRAWER_TAG, {
9101
+ ref: elementRef,
9102
+ "data-testid": dataTestId
9103
+ }, rowChildren);
9104
+ }
9105
+ CopilotThreadsDrawer.displayName = "CopilotThreadsDrawer";
9106
+
8480
9107
  //#endregion
8481
9108
  //#region src/v2/components/WildcardToolCallRender.tsx
8482
9109
  const WildcardToolCallRender = defineToolCallRenderer({
@@ -10166,6 +10793,7 @@ function CopilotKit({ children, ...props }) {
10166
10793
  const showInspector = shouldShowDevConsole(props.enableInspector);
10167
10794
  const publicApiKey = props.publicApiKey || props.publicLicenseKey;
10168
10795
  const renderArr = (0, react.useMemo)(() => [{ render: CoAgentStateRenderBridge }], []);
10796
+ const { onError: _onError, ...v2Props } = props;
10169
10797
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ToastProvider, {
10170
10798
  enabled,
10171
10799
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CopilotErrorBoundary, {
@@ -10174,7 +10802,7 @@ function CopilotKit({ children, ...props }) {
10174
10802
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ThreadsProvider, {
10175
10803
  threadId: props.threadId,
10176
10804
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CopilotKitProvider, {
10177
- ...props,
10805
+ ...v2Props,
10178
10806
  showDevConsole: showInspector,
10179
10807
  renderCustomMessages: renderArr,
10180
10808
  useSingleEndpoint: props.useSingleEndpoint ?? true,
@@ -10838,6 +11466,12 @@ Object.defineProperty(exports, 'CopilotSidebarView', {
10838
11466
  return CopilotSidebarView;
10839
11467
  }
10840
11468
  });
11469
+ Object.defineProperty(exports, 'CopilotThreadsDrawer', {
11470
+ enumerable: true,
11471
+ get: function () {
11472
+ return CopilotThreadsDrawer;
11473
+ }
11474
+ });
10841
11475
  Object.defineProperty(exports, 'DefaultCloseIcon', {
10842
11476
  enumerable: true,
10843
11477
  get: function () {
@@ -11126,4 +11760,4 @@ Object.defineProperty(exports, 'useToast', {
11126
11760
  return useToast;
11127
11761
  }
11128
11762
  });
11129
- //# sourceMappingURL=copilotkit-BCJDP8qd.cjs.map
11763
+ //# sourceMappingURL=copilotkit-CdpDZi3i.cjs.map