@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
@@ -1,10 +1,10 @@
1
1
  import * as React$1 from "react";
2
2
  import React, { createContext, forwardRef, memo, useCallback, useContext, useEffect, useId, useImperativeHandle, useLayoutEffect, useMemo, useReducer, useRef, useState, useSyncExternalStore } from "react";
3
- import { CopilotKitCore, CopilotKitCoreRuntimeConnectionStatus, ProxiedCopilotRuntimeAgent, ToolCallStatus, isRunCompletionAware, ɵcreateThreadStore, ɵselectHasNextPage, ɵselectIsFetchingNextPage, ɵselectThreads, ɵselectThreadsError, ɵselectThreadsIsLoading } from "@copilotkit/core";
4
- import { HttpAgent } from "@ag-ui/client";
3
+ import { CopilotKitCore, CopilotKitCoreRuntimeConnectionStatus, ProxiedCopilotRuntimeAgent, ToolCallStatus, isRunCompletionAware, ɵcreateThreadStore, ɵselectHasNextPage, ɵselectIsFetchingNextPage, ɵselectIsMutating, ɵselectThreads, ɵselectThreadsError, ɵselectThreadsIsLoading } from "@copilotkit/core";
4
+ import { HttpAgent, buildResumeArray, isInterruptExpired, randomUUID } from "@ag-ui/client";
5
5
  import { extendTailwindMerge, twMerge } from "tailwind-merge";
6
- import { ArrowUp, Check, ChevronDown, ChevronLeft, ChevronRight, ChevronRightIcon, Copy, Edit, Loader2, MessageCircle, Mic, Play, Plus, RefreshCw, Square, ThumbsDown, ThumbsUp, Upload, Volume2, X } from "lucide-react";
7
- import { A2UI_DEFAULT_DESIGN_GUIDELINES, A2UI_DEFAULT_GENERATION_GUIDELINES, COPILOT_CLOUD_API_URL, COPILOT_CLOUD_CHAT_URL, COPILOT_CLOUD_PUBLIC_API_KEY_HEADER, ConfigurationError, CopilotKitAgentDiscoveryError, CopilotKitApiDiscoveryError, CopilotKitError, CopilotKitErrorCode, CopilotKitLowLevelError, CopilotKitRemoteEndpointDiscoveryError, DEFAULT_AGENT_ID, ErrorVisibility, MissingPublicApiKeyError, Severity, TranscriptionErrorCode, TranscriptionErrorCode as TranscriptionErrorCode$1, copyToClipboard, createLicenseContextValue, dataToUUID, exceedsMaxSize, formatFileSize, generateVideoThumbnail, getDocumentIcon, getModalityFromMimeType, getSourceUrl, matchesAcceptFilter, parseJson, partialJSONParse, randomId, randomUUID, readFileAsBase64, schemaToJsonSchema } from "@copilotkit/shared";
6
+ import { ArrowUp, Check, ChevronDown, ChevronLeft, ChevronRight, ChevronRightIcon, Copy, Edit, Loader2, MessageCircle, Mic, PanelLeftOpen, Play, Plus, RefreshCw, Square, ThumbsDown, ThumbsUp, Upload, Volume2, X } from "lucide-react";
7
+ import { A2UI_DEFAULT_DESIGN_GUIDELINES, A2UI_DEFAULT_GENERATION_GUIDELINES, COPILOT_CLOUD_API_URL, COPILOT_CLOUD_CHAT_URL, COPILOT_CLOUD_PUBLIC_API_KEY_HEADER, ConfigurationError, CopilotKitAgentDiscoveryError, CopilotKitApiDiscoveryError, CopilotKitError, CopilotKitErrorCode, CopilotKitLowLevelError, CopilotKitRemoteEndpointDiscoveryError, DEFAULT_AGENT_ID, ErrorVisibility, MissingPublicApiKeyError, Severity, TranscriptionErrorCode, TranscriptionErrorCode as TranscriptionErrorCode$1, copyToClipboard, createLicenseContextValue, dataToUUID, exceedsMaxSize, formatFileSize, generateVideoThumbnail, getDocumentIcon, getModalityFromMimeType, getSourceUrl, matchesAcceptFilter, parseJson, partialJSONParse, randomId, randomUUID as randomUUID$1, readFileAsBase64, schemaToJsonSchema } from "@copilotkit/shared";
8
8
  import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
9
9
  import { Slot } from "@radix-ui/react-slot";
10
10
  import { cva } from "class-variance-authority";
@@ -19,6 +19,7 @@ import { zodToJsonSchema } from "zod-to-json-schema";
19
19
  import { createPortal, flushSync } from "react-dom";
20
20
  import { useVirtualizer } from "@tanstack/react-virtual";
21
21
  import { StickToBottom, useStickToBottomContext } from "use-stick-to-bottom";
22
+ import { COPILOTKIT_THREADS_DRAWER_TAG, defineCopilotKitThreadsDrawer } from "@copilotkit/web-components/threads-drawer";
22
23
  import ReactMarkdown from "react-markdown";
23
24
 
24
25
  //#region src/v2/lib/slots.tsx
@@ -144,6 +145,25 @@ const CopilotChatDefaultLabels = {
144
145
  modalHeaderTitle: "CopilotKit Chat",
145
146
  welcomeMessageText: "How can I help you today?"
146
147
  };
148
+ /**
149
+ * Mobile breakpoint below which the chat modal and the thread-list drawer are
150
+ * mutually exclusive. At or above this width both surfaces may coexist. This
151
+ * mirrors the `(max-width: 767px)` / `(min-width: 768px)` split already used by
152
+ * CopilotChatInput and CopilotSidebarView.
153
+ */
154
+ const MOBILE_MAX_WIDTH_PX = 767;
155
+ /**
156
+ * Reports whether the current viewport is in the mobile range (`<768px`), where
157
+ * the chat modal and drawer must not be open simultaneously. SSR-safe and
158
+ * defensive against environments without `matchMedia` (treated as desktop, so
159
+ * no mutual-exclusion constraint is applied).
160
+ *
161
+ * @returns `true` when the viewport is mobile-width, `false` otherwise.
162
+ */
163
+ function isMobileViewport() {
164
+ if (typeof window === "undefined" || typeof window.matchMedia !== "function") return false;
165
+ return window.matchMedia(`(max-width: ${MOBILE_MAX_WIDTH_PX}px)`).matches;
166
+ }
147
167
  const CopilotChatConfiguration = createContext(null);
148
168
  const CopilotChatConfigurationProvider = ({ children, labels, agentId, threadId, hasExplicitThreadId, isModalDefaultOpen }) => {
149
169
  const parentConfig = useContext(CopilotChatConfiguration);
@@ -154,12 +174,22 @@ const CopilotChatConfigurationProvider = ({ children, labels, agentId, threadId,
154
174
  ...stableLabels
155
175
  }), [stableLabels, parentConfig?.labels]);
156
176
  const resolvedAgentId = agentId ?? parentConfig?.agentId ?? DEFAULT_AGENT_ID;
177
+ const threadIdPropIsAuthoritative = threadId !== void 0 && hasExplicitThreadId !== false;
178
+ const isThreadIdControlled = threadIdPropIsAuthoritative;
179
+ const [activeThreadOverride, setActiveThreadOverride] = useState(null);
157
180
  const resolvedThreadId = useMemo(() => {
158
- if (threadId) return threadId;
181
+ if (threadIdPropIsAuthoritative) return threadId;
182
+ if (activeThreadOverride) return activeThreadOverride.threadId;
159
183
  if (parentConfig?.threadId) return parentConfig.threadId;
160
- return randomUUID();
161
- }, [threadId, parentConfig?.threadId]);
162
- const resolvedHasExplicitThreadId = (hasExplicitThreadId !== void 0 ? hasExplicitThreadId : !!threadId) || !!parentConfig?.hasExplicitThreadId;
184
+ if (threadId) return threadId;
185
+ return randomUUID$1();
186
+ }, [
187
+ threadIdPropIsAuthoritative,
188
+ threadId,
189
+ parentConfig?.threadId,
190
+ activeThreadOverride
191
+ ]);
192
+ const resolvedHasExplicitThreadId = (threadIdPropIsAuthoritative ? true : activeThreadOverride?.explicit ?? hasExplicitThreadId ?? false) || !!parentConfig?.hasExplicitThreadId;
163
193
  const [internalModalOpen, setInternalModalOpen] = useState(isModalDefaultOpen ?? true);
164
194
  const hasExplicitDefault = isModalDefaultOpen !== void 0;
165
195
  const setAndSync = useCallback((open) => {
@@ -178,20 +208,113 @@ const CopilotChatConfigurationProvider = ({ children, labels, agentId, threadId,
178
208
  }, [parentConfig?.isModalOpen, hasExplicitDefault]);
179
209
  const resolvedIsModalOpen = hasExplicitDefault ? internalModalOpen : parentConfig?.isModalOpen ?? internalModalOpen;
180
210
  const resolvedSetModalOpen = hasExplicitDefault ? setAndSync : parentConfig?.setModalOpen ?? setInternalModalOpen;
211
+ const [ownDrawerOpen, setOwnDrawerOpen] = useState(false);
212
+ const [ownDrawerCount, setOwnDrawerCount] = useState(0);
213
+ const modalCloseRef = useRef(() => {});
214
+ modalCloseRef.current = resolvedSetModalOpen;
215
+ const registeredModalClosersRef = useRef([]);
216
+ const ownRegisterModalCloser = useCallback((closeModal) => {
217
+ registeredModalClosersRef.current.push(closeModal);
218
+ return () => {
219
+ registeredModalClosersRef.current = registeredModalClosersRef.current.filter((entry) => entry !== closeModal);
220
+ };
221
+ }, []);
222
+ const ownSetDrawerOpen = useCallback((open) => {
223
+ setOwnDrawerOpen(open);
224
+ if (open && isMobileViewport()) {
225
+ const registered = registeredModalClosersRef.current;
226
+ (registered.length > 0 ? registered[registered.length - 1] : modalCloseRef.current)(false);
227
+ }
228
+ }, []);
229
+ const ownRegisterDrawer = useCallback(() => {
230
+ setOwnDrawerCount((count) => count + 1);
231
+ return () => {
232
+ setOwnDrawerCount((count) => Math.max(0, count - 1));
233
+ };
234
+ }, []);
235
+ const resolvedDrawerOpen = parentConfig ? parentConfig.drawerOpen : ownDrawerOpen;
236
+ const resolvedSetDrawerOpen = parentConfig ? parentConfig.setDrawerOpen : ownSetDrawerOpen;
237
+ const resolvedDrawerRegistered = parentConfig ? parentConfig.drawerRegistered : ownDrawerCount > 0;
238
+ const resolvedRegisterDrawer = parentConfig ? parentConfig.registerDrawer : ownRegisterDrawer;
239
+ const resolvedRegisterModalCloser = parentConfig ? parentConfig.ɵregisterModalCloser : ownRegisterModalCloser;
240
+ useEffect(() => {
241
+ if (!hasExplicitDefault) return;
242
+ return resolvedRegisterModalCloser(resolvedSetModalOpen);
243
+ }, [
244
+ hasExplicitDefault,
245
+ resolvedRegisterModalCloser,
246
+ resolvedSetModalOpen
247
+ ]);
248
+ const isThreadIdControlledRef = useRef(isThreadIdControlled);
249
+ isThreadIdControlledRef.current = isThreadIdControlled;
250
+ const ownSetActiveThreadId = useCallback((id, options) => {
251
+ setActiveThreadOverride({
252
+ threadId: id,
253
+ explicit: options?.explicit ?? true
254
+ });
255
+ }, []);
256
+ const ownStartNewThread = useCallback(() => {
257
+ setActiveThreadOverride({
258
+ threadId: randomUUID$1(),
259
+ explicit: false
260
+ });
261
+ }, []);
262
+ const parentSetActiveThreadId = parentConfig?.setActiveThreadId;
263
+ const parentStartNewThread = parentConfig?.startNewThread;
264
+ const resolvedSetActiveThreadId = useCallback((id, options) => {
265
+ if (isThreadIdControlledRef.current) {
266
+ console.warn("[CopilotKit] Ignoring setActiveThreadId(): threadId is controlled via the `threadId` prop on CopilotChatConfigurationProvider.");
267
+ return;
268
+ }
269
+ if (parentSetActiveThreadId) {
270
+ parentSetActiveThreadId(id, options);
271
+ return;
272
+ }
273
+ ownSetActiveThreadId(id, options);
274
+ }, [parentSetActiveThreadId, ownSetActiveThreadId]);
275
+ const resolvedStartNewThread = useCallback(() => {
276
+ if (isThreadIdControlledRef.current) {
277
+ console.warn("[CopilotKit] Ignoring startNewThread(): threadId is controlled via the `threadId` prop on CopilotChatConfigurationProvider.");
278
+ return;
279
+ }
280
+ if (parentStartNewThread) {
281
+ parentStartNewThread();
282
+ return;
283
+ }
284
+ ownStartNewThread();
285
+ }, [parentStartNewThread, ownStartNewThread]);
286
+ const setModalOpenWithDrawerExclusion = useCallback((open) => {
287
+ if (open && isMobileViewport()) resolvedSetDrawerOpen(false);
288
+ resolvedSetModalOpen(open);
289
+ }, [resolvedSetModalOpen, resolvedSetDrawerOpen]);
181
290
  const configurationValue = useMemo(() => ({
182
291
  labels: mergedLabels,
183
292
  agentId: resolvedAgentId,
184
293
  threadId: resolvedThreadId,
185
294
  hasExplicitThreadId: resolvedHasExplicitThreadId,
186
295
  isModalOpen: resolvedIsModalOpen,
187
- setModalOpen: resolvedSetModalOpen
296
+ setModalOpen: setModalOpenWithDrawerExclusion,
297
+ drawerOpen: resolvedDrawerOpen,
298
+ setDrawerOpen: resolvedSetDrawerOpen,
299
+ drawerRegistered: resolvedDrawerRegistered,
300
+ registerDrawer: resolvedRegisterDrawer,
301
+ ɵregisterModalCloser: resolvedRegisterModalCloser,
302
+ setActiveThreadId: resolvedSetActiveThreadId,
303
+ startNewThread: resolvedStartNewThread
188
304
  }), [
189
305
  mergedLabels,
190
306
  resolvedAgentId,
191
307
  resolvedThreadId,
192
308
  resolvedHasExplicitThreadId,
193
309
  resolvedIsModalOpen,
194
- resolvedSetModalOpen
310
+ setModalOpenWithDrawerExclusion,
311
+ resolvedDrawerOpen,
312
+ resolvedSetDrawerOpen,
313
+ resolvedDrawerRegistered,
314
+ resolvedRegisterDrawer,
315
+ resolvedRegisterModalCloser,
316
+ resolvedSetActiveThreadId,
317
+ resolvedStartNewThread
195
318
  ]);
196
319
  return /* @__PURE__ */ jsx(CopilotChatConfiguration.Provider, {
197
320
  value: configurationValue,
@@ -1112,7 +1235,7 @@ function CopilotChatInput({ mode = "input", onSubmitMessage, onStop, isRunning =
1112
1235
  },
1113
1236
  ...props,
1114
1237
  children: [/* @__PURE__ */ jsx("div", {
1115
- 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",
1238
+ 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",
1116
1239
  children: inputPill
1117
1240
  }), shouldShowDisclaimer && BoundDisclaimer]
1118
1241
  });
@@ -1421,353 +1544,75 @@ const LicenseContext = createContext({
1421
1544
  const useLicenseContext = () => useContext(LicenseContext);
1422
1545
 
1423
1546
  //#endregion
1424
- //#region src/v2/types/defineToolCallRenderer.ts
1425
- function defineToolCallRenderer(def) {
1426
- const argsSchema = def.name === "*" && !def.args ? z.any() : def.args;
1427
- return {
1428
- name: def.name,
1429
- args: argsSchema,
1430
- render: def.render,
1431
- ...def.agentId ? { agentId: def.agentId } : {}
1432
- };
1433
- }
1434
-
1435
- //#endregion
1436
- //#region src/v2/hooks/use-render-tool.tsx
1437
- const EMPTY_DEPS$1 = [];
1547
+ //#region src/v2/hooks/use-render-tool-call.tsx
1438
1548
  /**
1439
- * Registers a renderer entry in CopilotKit's `renderToolCalls` registry.
1440
- *
1441
- * Key behavior:
1442
- * - deduplicates by `agentId:name` (latest registration wins),
1443
- * - keeps renderer entries on cleanup so historical chat tool calls can still render,
1444
- * - refreshes registration when `deps` change.
1445
- *
1446
- * @typeParam S - Schema type describing tool call parameters.
1447
- * @param config - Renderer config for wildcard or named tools.
1448
- * @param deps - Optional dependencies to refresh registration.
1449
- *
1450
- * @example
1451
- * ```tsx
1452
- * useRenderTool(
1453
- * {
1454
- * name: "searchDocs",
1455
- * parameters: z.object({ query: z.string() }),
1456
- * render: ({ status, parameters, result }) => {
1457
- * if (status === "executing") return <div>Searching {parameters.query}</div>;
1458
- * if (status === "complete") return <div>{result}</div>;
1459
- * return <div>Preparing...</div>;
1460
- * },
1461
- * },
1462
- * [],
1463
- * );
1464
- * ```
1549
+ * Memoized component that renders a single tool call.
1550
+ * This prevents unnecessary re-renders when parent components update
1551
+ * but the tool call data hasn't changed.
1552
+ */
1553
+ const ToolCallRenderer = React.memo(function ToolCallRenderer({ toolCall, toolMessage, RenderComponent, isExecuting }) {
1554
+ const args = useMemo(() => partialJSONParse(toolCall.function.arguments), [toolCall.function.arguments]);
1555
+ const toolName = toolCall.function.name;
1556
+ if (toolMessage) return /* @__PURE__ */ jsx(RenderComponent, {
1557
+ name: toolName,
1558
+ toolCallId: toolCall.id,
1559
+ args,
1560
+ status: ToolCallStatus.Complete,
1561
+ result: toolMessage.content
1562
+ });
1563
+ else if (isExecuting) return /* @__PURE__ */ jsx(RenderComponent, {
1564
+ name: toolName,
1565
+ toolCallId: toolCall.id,
1566
+ args,
1567
+ status: ToolCallStatus.Executing,
1568
+ result: void 0
1569
+ });
1570
+ else return /* @__PURE__ */ jsx(RenderComponent, {
1571
+ name: toolName,
1572
+ toolCallId: toolCall.id,
1573
+ args,
1574
+ status: ToolCallStatus.InProgress,
1575
+ result: void 0
1576
+ });
1577
+ }, (prevProps, nextProps) => {
1578
+ if (prevProps.toolCall.id !== nextProps.toolCall.id) return false;
1579
+ if (prevProps.toolCall.function.name !== nextProps.toolCall.function.name) return false;
1580
+ if (prevProps.toolCall.function.arguments !== nextProps.toolCall.function.arguments) return false;
1581
+ if (prevProps.toolMessage?.content !== nextProps.toolMessage?.content) return false;
1582
+ if (prevProps.isExecuting !== nextProps.isExecuting) return false;
1583
+ if (prevProps.RenderComponent !== nextProps.RenderComponent) return false;
1584
+ return true;
1585
+ });
1586
+ /**
1587
+ * Hook that returns a function to render tool calls based on the render functions
1588
+ * defined in CopilotKitProvider.
1465
1589
  *
1466
- * @example
1467
- * ```tsx
1468
- * useRenderTool(
1469
- * {
1470
- * name: "summarize",
1471
- * parameters: z.object({ text: z.string() }),
1472
- * agentId: "research-agent",
1473
- * render: ({ name, status }) => <div>{name}: {status}</div>,
1474
- * },
1475
- * [selectedAgentId],
1476
- * );
1477
- * ```
1590
+ * @returns A function that takes a tool call and optional tool message and returns the rendered component
1478
1591
  */
1479
- function useRenderTool(config, deps) {
1480
- const { copilotkit } = useCopilotKit();
1481
- const extraDeps = deps ?? EMPTY_DEPS$1;
1482
- useEffect(() => {
1483
- const renderer = config.name === "*" && !config.parameters ? defineToolCallRenderer({
1484
- name: "*",
1485
- render: (props) => config.render({
1486
- ...props,
1487
- parameters: props.args
1488
- }),
1489
- ...config.agentId ? { agentId: config.agentId } : {}
1490
- }) : defineToolCallRenderer({
1491
- name: config.name,
1492
- args: config.parameters,
1493
- render: (props) => config.render({
1494
- ...props,
1495
- parameters: props.args
1496
- }),
1497
- ...config.agentId ? { agentId: config.agentId } : {}
1498
- });
1499
- copilotkit.addHookRenderToolCall(renderer);
1592
+ function useRenderToolCall() {
1593
+ const { copilotkit, executingToolCallIds } = useCopilotKit();
1594
+ const agentId = useCopilotChatConfiguration()?.agentId ?? DEFAULT_AGENT_ID;
1595
+ const renderToolCalls = useSyncExternalStore((callback) => {
1596
+ return copilotkit.subscribe({ onRenderToolCallsChanged: callback }).unsubscribe;
1597
+ }, () => copilotkit.renderToolCalls, () => copilotkit.renderToolCalls);
1598
+ return useCallback(({ toolCall, toolMessage }) => {
1599
+ const exactMatches = renderToolCalls.filter((rc) => rc.name === toolCall.function.name);
1600
+ const renderConfig = exactMatches.find((rc) => rc.agentId === agentId) || exactMatches.find((rc) => !rc.agentId) || exactMatches[0] || renderToolCalls.find((rc) => rc.name === "*");
1601
+ if (!renderConfig) return null;
1602
+ const RenderComponent = renderConfig.render;
1603
+ return /* @__PURE__ */ jsx(ToolCallRenderer, {
1604
+ toolCall,
1605
+ toolMessage,
1606
+ RenderComponent,
1607
+ isExecuting: executingToolCallIds.has(toolCall.id)
1608
+ }, toolCall.id);
1500
1609
  }, [
1501
- config.name,
1502
- copilotkit,
1503
- JSON.stringify(extraDeps)
1610
+ renderToolCalls,
1611
+ executingToolCallIds,
1612
+ agentId
1504
1613
  ]);
1505
1614
  }
1506
1615
 
1507
- //#endregion
1508
- //#region src/v2/hooks/use-default-render-tool.tsx
1509
- /**
1510
- * Module-level dedup set so an unknown status value only emits a console
1511
- * warning the FIRST time we encounter it. Otherwise a stuck/unmapped status
1512
- * would log on every re-render (potentially many per second).
1513
- */
1514
- const warnedUnknownStatuses = /* @__PURE__ */ new Set();
1515
- /**
1516
- * Map a {@link ToolCallStatus} enum value to the documented string-union
1517
- * status the {@link DefaultRenderProps} contract exposes. Unknown / future
1518
- * enum members log a warning (once per distinct value) and fall back to
1519
- * `"inProgress"`.
1520
- */
1521
- function mapToolCallStatus(status) {
1522
- switch (status) {
1523
- case ToolCallStatus.Complete: return "complete";
1524
- case ToolCallStatus.Executing: return "executing";
1525
- case ToolCallStatus.InProgress: return "inProgress";
1526
- default: {
1527
- const key = String(status);
1528
- if (!warnedUnknownStatuses.has(key)) {
1529
- warnedUnknownStatuses.add(key);
1530
- console.warn(`[CopilotKit] Unknown ToolCallStatus "${key}" in default tool-call renderer; falling back to "inProgress".`);
1531
- }
1532
- return "inProgress";
1533
- }
1534
- }
1535
- }
1536
- /**
1537
- * Convert the framework-internal renderer props (`args`, enum status) into
1538
- * the documented {@link DefaultRenderProps} shape (`parameters`, string-union
1539
- * status) so a user `config.render` always sees the documented contract.
1540
- */
1541
- function adaptRendererProps(props) {
1542
- return {
1543
- name: props.name,
1544
- toolCallId: props.toolCallId,
1545
- parameters: props.args,
1546
- status: mapToolCallStatus(props.status),
1547
- result: props.result
1548
- };
1549
- }
1550
- /**
1551
- * Registers a wildcard (`"*"`) tool-call renderer via `useRenderTool`.
1552
- *
1553
- * - Call with no config to use CopilotKit's built-in default tool-call card.
1554
- * - Pass `config.render` to replace the default UI with your own fallback renderer.
1555
- *
1556
- * This is useful when you want a generic renderer for tools that do not have a
1557
- * dedicated `useRenderTool({ name: "..." })` registration.
1558
- *
1559
- * @param config - Optional custom wildcard render function.
1560
- * @param deps - Optional dependencies to refresh registration.
1561
- *
1562
- * @example
1563
- * ```tsx
1564
- * useDefaultRenderTool();
1565
- * ```
1566
- *
1567
- * @example
1568
- * ```tsx
1569
- * useDefaultRenderTool({
1570
- * render: ({ name, status }) => <div>{name}: {status}</div>,
1571
- * });
1572
- * ```
1573
- *
1574
- * @example
1575
- * ```tsx
1576
- * useDefaultRenderTool(
1577
- * {
1578
- * render: ({ name, result }) => (
1579
- * <ToolEventRow title={name} payload={result} compact={compactMode} />
1580
- * ),
1581
- * },
1582
- * [compactMode],
1583
- * );
1584
- * ```
1585
- */
1586
- function useDefaultRenderTool(config, deps) {
1587
- const userRender = config?.render;
1588
- useRenderTool({
1589
- name: "*",
1590
- render: userRender ? (raw) => userRender(adaptRendererProps(raw)) : (raw) => /* @__PURE__ */ jsx(DefaultToolCallRenderer, { ...adaptRendererProps(raw) })
1591
- }, deps);
1592
- }
1593
- /**
1594
- * Guarded JSON.stringify used inside the expanded `<pre>` blocks. A circular
1595
- * reference would otherwise crash the entire React tree on render.
1596
- */
1597
- function safeStringifyForPre(value) {
1598
- try {
1599
- return JSON.stringify(value, null, 2);
1600
- } catch (err) {
1601
- console.warn("[CopilotKit] Failed to JSON.stringify tool-call payload for default renderer; falling back to String():", err);
1602
- try {
1603
- return String(value);
1604
- } catch (innerErr) {
1605
- console.warn("[CopilotKit] safeStringifyForPre: value could not be stringified:", innerErr);
1606
- return "[unserializable]";
1607
- }
1608
- }
1609
- }
1610
- function DefaultToolCallRenderer({ name, toolCallId, parameters, status, result }) {
1611
- const [isExpanded, setIsExpanded] = useState(false);
1612
- const isActive = status === "inProgress" || status === "executing";
1613
- const isComplete = status === "complete";
1614
- const statusLabel = isActive ? "Running" : isComplete ? "Done" : status;
1615
- const dotClassName = isActive ? "cpk:bg-amber-500" : isComplete ? "cpk:bg-emerald-500" : "cpk:bg-zinc-400";
1616
- 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";
1617
- return /* @__PURE__ */ jsx("div", {
1618
- "data-testid": "copilot-tool-render",
1619
- "data-tool-name": name,
1620
- "data-tool-call-id": toolCallId,
1621
- "data-status": status,
1622
- "data-args": safeStringifyForAttr(parameters),
1623
- "data-result": safeStringifyForAttr(result),
1624
- className: "cpk:mt-2 cpk:pb-2",
1625
- children: /* @__PURE__ */ jsxs("div", {
1626
- 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",
1627
- children: [/* @__PURE__ */ jsxs("button", {
1628
- type: "button",
1629
- "aria-expanded": isExpanded,
1630
- onClick: () => setIsExpanded(!isExpanded),
1631
- 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",
1632
- style: { font: "inherit" },
1633
- children: [/* @__PURE__ */ jsxs("div", {
1634
- className: "cpk:flex cpk:min-w-0 cpk:items-center cpk:gap-2",
1635
- children: [
1636
- /* @__PURE__ */ jsx("svg", {
1637
- 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" : ""}`,
1638
- fill: "none",
1639
- viewBox: "0 0 24 24",
1640
- strokeWidth: 2,
1641
- stroke: "currentColor",
1642
- children: /* @__PURE__ */ jsx("path", {
1643
- strokeLinecap: "round",
1644
- strokeLinejoin: "round",
1645
- d: "M8.25 4.5l7.5 7.5-7.5 7.5"
1646
- })
1647
- }),
1648
- /* @__PURE__ */ jsx("span", { className: `cpk:inline-block cpk:h-2 cpk:w-2 cpk:flex-shrink-0 cpk:rounded-full ${dotClassName}` }),
1649
- /* @__PURE__ */ jsx("span", {
1650
- "data-testid": "copilot-tool-render-name",
1651
- className: "cpk:truncate cpk:text-[13px] cpk:font-semibold cpk:text-zinc-900 cpk:dark:text-zinc-100",
1652
- children: name
1653
- })
1654
- ]
1655
- }), /* @__PURE__ */ jsx("span", {
1656
- "data-testid": "copilot-tool-render-status",
1657
- 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}`,
1658
- children: statusLabel
1659
- })]
1660
- }), isExpanded && /* @__PURE__ */ jsxs("div", {
1661
- className: "cpk:mt-3 cpk:grid cpk:gap-3",
1662
- children: [/* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("div", {
1663
- className: "cpk:text-[10px] cpk:uppercase cpk:text-zinc-500 cpk:dark:text-zinc-400",
1664
- children: "Arguments"
1665
- }), /* @__PURE__ */ jsx("pre", {
1666
- 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",
1667
- children: safeStringifyForPre(parameters ?? {})
1668
- })] }), result !== void 0 && /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("div", {
1669
- className: "cpk:text-[10px] cpk:uppercase cpk:text-zinc-500 cpk:dark:text-zinc-400",
1670
- children: "Result"
1671
- }), /* @__PURE__ */ jsx("pre", {
1672
- 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",
1673
- children: typeof result === "string" ? result : safeStringifyForPre(result)
1674
- })] })]
1675
- })]
1676
- })
1677
- });
1678
- }
1679
- function safeStringifyForAttr(value) {
1680
- if (value === void 0 || value === null) return "";
1681
- if (typeof value === "string") return value;
1682
- try {
1683
- return JSON.stringify(value);
1684
- } catch (err) {
1685
- console.warn("[CopilotKit] Failed to JSON.stringify tool-call payload for data-* attribute; falling back to String():", err);
1686
- try {
1687
- return String(value);
1688
- } catch (innerErr) {
1689
- console.warn("[CopilotKit] safeStringifyForAttr: value could not be stringified:", innerErr);
1690
- return "";
1691
- }
1692
- }
1693
- }
1694
-
1695
- //#endregion
1696
- //#region src/v2/hooks/use-render-tool-call.tsx
1697
- /**
1698
- * Memoized component that renders a single tool call.
1699
- * This prevents unnecessary re-renders when parent components update
1700
- * but the tool call data hasn't changed.
1701
- */
1702
- const ToolCallRenderer = React.memo(function ToolCallRenderer({ toolCall, toolMessage, RenderComponent, isExecuting }) {
1703
- const args = useMemo(() => partialJSONParse(toolCall.function.arguments), [toolCall.function.arguments]);
1704
- const toolName = toolCall.function.name;
1705
- if (toolMessage) return /* @__PURE__ */ jsx(RenderComponent, {
1706
- name: toolName,
1707
- toolCallId: toolCall.id,
1708
- args,
1709
- status: ToolCallStatus.Complete,
1710
- result: toolMessage.content
1711
- });
1712
- else if (isExecuting) return /* @__PURE__ */ jsx(RenderComponent, {
1713
- name: toolName,
1714
- toolCallId: toolCall.id,
1715
- args,
1716
- status: ToolCallStatus.Executing,
1717
- result: void 0
1718
- });
1719
- else return /* @__PURE__ */ jsx(RenderComponent, {
1720
- name: toolName,
1721
- toolCallId: toolCall.id,
1722
- args,
1723
- status: ToolCallStatus.InProgress,
1724
- result: void 0
1725
- });
1726
- }, (prevProps, nextProps) => {
1727
- if (prevProps.toolCall.id !== nextProps.toolCall.id) return false;
1728
- if (prevProps.toolCall.function.name !== nextProps.toolCall.function.name) return false;
1729
- if (prevProps.toolCall.function.arguments !== nextProps.toolCall.function.arguments) return false;
1730
- if (prevProps.toolMessage?.content !== nextProps.toolMessage?.content) return false;
1731
- if (prevProps.isExecuting !== nextProps.isExecuting) return false;
1732
- if (prevProps.RenderComponent !== nextProps.RenderComponent) return false;
1733
- return true;
1734
- });
1735
- /**
1736
- * Hook that returns a function to render tool calls based on the render functions
1737
- * defined in CopilotKitProvider.
1738
- *
1739
- * @returns A function that takes a tool call and optional tool message and returns the rendered component
1740
- */
1741
- function useRenderToolCall() {
1742
- const { copilotkit, executingToolCallIds } = useCopilotKit();
1743
- const agentId = useCopilotChatConfiguration()?.agentId ?? DEFAULT_AGENT_ID;
1744
- const renderToolCalls = useSyncExternalStore((callback) => {
1745
- return copilotkit.subscribe({ onRenderToolCallsChanged: callback }).unsubscribe;
1746
- }, () => copilotkit.renderToolCalls, () => copilotkit.renderToolCalls);
1747
- return useCallback(({ toolCall, toolMessage }) => {
1748
- const exactMatches = renderToolCalls.filter((rc) => rc.name === toolCall.function.name);
1749
- return /* @__PURE__ */ jsx(ToolCallRenderer, {
1750
- toolCall,
1751
- toolMessage,
1752
- RenderComponent: (exactMatches.find((rc) => rc.agentId === agentId) || exactMatches.find((rc) => !rc.agentId) || exactMatches[0] || renderToolCalls.find((rc) => rc.name === "*"))?.render ?? defaultToolCallRenderAdapter,
1753
- isExecuting: executingToolCallIds.has(toolCall.id)
1754
- }, toolCall.id);
1755
- }, [
1756
- renderToolCalls,
1757
- executingToolCallIds,
1758
- agentId
1759
- ]);
1760
- }
1761
- function defaultToolCallRenderAdapter(props) {
1762
- return /* @__PURE__ */ jsx(DefaultToolCallRenderer, {
1763
- name: props.name,
1764
- toolCallId: props.toolCallId,
1765
- parameters: props.args,
1766
- status: mapToolCallStatus(props.status),
1767
- result: props.result
1768
- });
1769
- }
1770
-
1771
1616
  //#endregion
1772
1617
  //#region src/v2/components/CopilotKitInspector.tsx
1773
1618
  const CopilotKitInspector = ({ core, ...rest }) => {
@@ -3248,7 +3093,7 @@ const A2UISurfaceContentSchema = z.object({
3248
3093
  ...A2UILifecycleFields
3249
3094
  }).passthrough();
3250
3095
  function createA2UIMessageRenderer(options) {
3251
- const { theme, catalog, loadingComponent, recovery } = options;
3096
+ const { theme, catalog, loadingComponent, recovery, onAction } = options;
3252
3097
  const showAfterMs = recovery?.showAfterMs ?? 2e3;
3253
3098
  const showAfterAttempts = recovery?.showAfterAttempts ?? 2;
3254
3099
  const optionDebugExposure = recovery?.debugExposure ?? "collapsed";
@@ -3324,6 +3169,7 @@ function createA2UIMessageRenderer(options) {
3324
3169
  agent,
3325
3170
  copilotkit,
3326
3171
  catalog,
3172
+ onAction,
3327
3173
  onReady: markSurfaceReady
3328
3174
  }, surfaceId))
3329
3175
  });
@@ -3344,29 +3190,60 @@ function createA2UIMessageRenderer(options) {
3344
3190
  };
3345
3191
  }
3346
3192
  /**
3193
+ * Orchestrates a single A2UI user action: runs the optional `onAction`
3194
+ * interceptor first, then forwards to the agent unless the interceptor
3195
+ * suppressed it (returned `null`). Exported for unit testing; the wiring lives
3196
+ * in {@link ReactSurfaceHost}.
3197
+ */
3198
+ async function runA2UIAction({ message, agent, copilotkit, onAction }) {
3199
+ if (!agent) return;
3200
+ const action = message.userAction;
3201
+ const forward = async (forwardAction) => {
3202
+ const a2uiAction = forwardAction !== void 0 ? {
3203
+ ...message,
3204
+ userAction: forwardAction
3205
+ } : message;
3206
+ try {
3207
+ copilotkit.setProperties({
3208
+ ...copilotkit.properties,
3209
+ a2uiAction
3210
+ });
3211
+ await copilotkit.runAgent({ agent });
3212
+ } finally {
3213
+ if (copilotkit.properties) {
3214
+ const { a2uiAction: _omit, ...rest } = copilotkit.properties;
3215
+ copilotkit.setProperties(rest);
3216
+ }
3217
+ }
3218
+ };
3219
+ if (onAction && action) {
3220
+ const result = await onAction(action, forward);
3221
+ if (result === null) return;
3222
+ if (result) {
3223
+ await forward(result);
3224
+ return;
3225
+ }
3226
+ }
3227
+ await forward();
3228
+ }
3229
+ /**
3347
3230
  * Renders a single A2UI surface using the React renderer.
3348
3231
  * Wraps A2UIProvider + A2UIRenderer and bridges actions back to CopilotKit.
3349
3232
  */
3350
- function ReactSurfaceHost({ surfaceId, operations, theme, agent, copilotkit, catalog, onReady }) {
3233
+ function ReactSurfaceHost({ surfaceId, operations, theme, agent, copilotkit, catalog, onAction, onReady }) {
3351
3234
  return /* @__PURE__ */ jsx("div", {
3352
3235
  className: "cpk:flex cpk:w-full cpk:flex-none cpk:flex-col cpk:gap-4",
3353
3236
  children: /* @__PURE__ */ jsxs(A2UIProvider, {
3354
- onAction: useCallback(async (message) => {
3355
- if (!agent) return;
3356
- message.userAction;
3357
- try {
3358
- copilotkit.setProperties({
3359
- ...copilotkit.properties,
3360
- a2uiAction: message
3361
- });
3362
- await copilotkit.runAgent({ agent });
3363
- } finally {
3364
- if (copilotkit.properties) {
3365
- const { a2uiAction, ...rest } = copilotkit.properties;
3366
- copilotkit.setProperties(rest);
3367
- }
3368
- }
3369
- }, [agent, copilotkit]),
3237
+ onAction: useCallback((message) => runA2UIAction({
3238
+ message,
3239
+ agent,
3240
+ copilotkit,
3241
+ onAction
3242
+ }), [
3243
+ agent,
3244
+ copilotkit,
3245
+ onAction
3246
+ ]),
3370
3247
  theme,
3371
3248
  catalog,
3372
3249
  children: [/* @__PURE__ */ jsx(SurfaceMessageProcessor, {
@@ -3437,6 +3314,18 @@ function getOperationSurfaceId(operation) {
3437
3314
  return operation?.createSurface?.surfaceId ?? operation?.updateComponents?.surfaceId ?? operation?.updateDataModel?.surfaceId ?? operation?.deleteSurface?.surfaceId ?? null;
3438
3315
  }
3439
3316
 
3317
+ //#endregion
3318
+ //#region src/v2/types/defineToolCallRenderer.ts
3319
+ function defineToolCallRenderer(def) {
3320
+ const argsSchema = def.name === "*" && !def.args ? z.any() : def.args;
3321
+ return {
3322
+ name: def.name,
3323
+ args: argsSchema,
3324
+ render: def.render,
3325
+ ...def.agentId ? { agentId: def.agentId } : {}
3326
+ };
3327
+ }
3328
+
3440
3329
  //#endregion
3441
3330
  //#region src/v2/a2ui/A2UIToolCallRenderer.tsx
3442
3331
  /**
@@ -3531,6 +3420,10 @@ function A2UICatalogContext({ catalog, includeSchema }) {
3531
3420
 
3532
3421
  //#endregion
3533
3422
  //#region src/v2/providers/CopilotKitProvider.tsx
3423
+ const zodToJsonSchemaAdapter = (schema, options) => {
3424
+ const refStrategy = options?.$refStrategy;
3425
+ return zodToJsonSchema(schema, refStrategy === "root" || refStrategy === "relative" || refStrategy === "none" || refStrategy === "seen" ? { $refStrategy: refStrategy } : {});
3426
+ };
3534
3427
  const HEADER_NAME = "X-CopilotCloud-Public-Api-Key";
3535
3428
  const COPILOT_CLOUD_CHAT_URL$1 = "https://api.cloud.copilotkit.ai/copilotkit/v1";
3536
3429
  const EMPTY_HEADERS = Object.freeze({});
@@ -3561,6 +3454,8 @@ const CopilotKitProvider = ({ children, runtimeUrl, headers: headersProp = EMPTY
3561
3454
  const [runtimeA2UIEnabled, setRuntimeA2UIEnabled] = useState(false);
3562
3455
  const [runtimeOpenGenUIEnabled, setRuntimeOpenGenUIEnabled] = useState(false);
3563
3456
  const openGenUIActive = runtimeOpenGenUIEnabled || !!openGenerativeUI;
3457
+ const a2uiCatalogProvided = !!a2ui?.catalog;
3458
+ const a2uiActive = runtimeA2UIEnabled || a2uiCatalogProvided;
3564
3459
  const [runtimeLicenseStatus, setRuntimeLicenseStatus] = useState(void 0);
3565
3460
  useEffect(() => {
3566
3461
  if (typeof window === "undefined") return;
@@ -3591,7 +3486,7 @@ const CopilotKitProvider = ({ children, runtimeUrl, headers: headersProp = EMPTY
3591
3486
  content: OpenGenerativeUIContentSchema,
3592
3487
  render: OpenGenerativeUIActivityRenderer
3593
3488
  });
3594
- if (runtimeA2UIEnabled) renderers.unshift(createA2UIMessageRenderer({
3489
+ if (a2uiActive) renderers.unshift(createA2UIMessageRenderer({
3595
3490
  theme: a2ui?.theme ?? viewerTheme,
3596
3491
  catalog: a2ui?.catalog,
3597
3492
  loadingComponent: a2ui?.loadingComponent,
@@ -3599,7 +3494,7 @@ const CopilotKitProvider = ({ children, runtimeUrl, headers: headersProp = EMPTY
3599
3494
  }));
3600
3495
  return renderers;
3601
3496
  }, [
3602
- runtimeA2UIEnabled,
3497
+ a2uiActive,
3603
3498
  openGenUIActive,
3604
3499
  a2ui
3605
3500
  ]);
@@ -3786,7 +3681,10 @@ const CopilotKitProvider = ({ children, runtimeUrl, headers: headersProp = EMPTY
3786
3681
  copilotkit.setRuntimeTransport(useSingleEndpoint === true ? "single" : useSingleEndpoint === false ? "rest" : "auto");
3787
3682
  copilotkit.setHeaders(mergedHeaders);
3788
3683
  copilotkit.setCredentials(credentials);
3789
- copilotkit.setProperties(properties);
3684
+ copilotkit.setProperties(a2uiCatalogProvided ? {
3685
+ ...properties,
3686
+ a2uiCatalogAvailable: true
3687
+ } : properties);
3790
3688
  copilotkit.setAgents__unsafe_dev_only(mergedAgents);
3791
3689
  copilotkit.setDebug(debug);
3792
3690
  }, [
@@ -3795,6 +3693,7 @@ const CopilotKitProvider = ({ children, runtimeUrl, headers: headersProp = EMPTY
3795
3693
  mergedHeaders,
3796
3694
  credentials,
3797
3695
  properties,
3696
+ a2uiCatalogProvided,
3798
3697
  mergedAgents,
3799
3698
  useSingleEndpoint,
3800
3699
  debug
@@ -3842,7 +3741,7 @@ const CopilotKitProvider = ({ children, runtimeUrl, headers: headersProp = EMPTY
3842
3741
  return JSON.stringify(sandboxFunctionsList.map((fn) => ({
3843
3742
  name: fn.name,
3844
3743
  description: fn.description,
3845
- parameters: schemaToJsonSchema(fn.parameters, { zodToJsonSchema })
3744
+ parameters: schemaToJsonSchema(fn.parameters, { zodToJsonSchema: zodToJsonSchemaAdapter })
3846
3745
  })));
3847
3746
  }, [sandboxFunctionsList]);
3848
3747
  useLayoutEffect(() => {
@@ -3863,7 +3762,7 @@ const CopilotKitProvider = ({ children, runtimeUrl, headers: headersProp = EMPTY
3863
3762
  copilotkit,
3864
3763
  executingToolCallIds
3865
3764
  }), [copilotkit, executingToolCallIds]);
3866
- const licenseContextValue = useMemo(() => createLicenseContextValue(null), []);
3765
+ const licenseContextValue = useMemo(() => createLicenseContextValue(runtimeLicenseStatus), [runtimeLicenseStatus]);
3867
3766
  return /* @__PURE__ */ jsx(SandboxFunctionsContext.Provider, {
3868
3767
  value: sandboxFunctionsList,
3869
3768
  children: /* @__PURE__ */ jsx(CopilotKitContext.Provider, {
@@ -3871,8 +3770,8 @@ const CopilotKitProvider = ({ children, runtimeUrl, headers: headersProp = EMPTY
3871
3770
  children: /* @__PURE__ */ jsxs(LicenseContext.Provider, {
3872
3771
  value: licenseContextValue,
3873
3772
  children: [
3874
- runtimeA2UIEnabled && /* @__PURE__ */ jsx(A2UIBuiltInToolCallRenderer, {}),
3875
- runtimeA2UIEnabled && /* @__PURE__ */ jsx(A2UICatalogContext, {
3773
+ a2uiActive && /* @__PURE__ */ jsx(A2UIBuiltInToolCallRenderer, {}),
3774
+ a2uiActive && /* @__PURE__ */ jsx(A2UICatalogContext, {
3876
3775
  catalog: a2ui?.catalog,
3877
3776
  includeSchema: a2ui?.includeSchema
3878
3777
  }),
@@ -3950,16 +3849,20 @@ function useRenderActivityMessage() {
3950
3849
  const renderActivityMessage = useCallback((message) => {
3951
3850
  const renderer = findRenderer(message.activityType);
3952
3851
  if (!renderer) return null;
3953
- const parseResult = renderer.content.safeParse(message.content);
3954
- if (!parseResult.success) {
3955
- console.warn(`Failed to parse content for activity message '${message.activityType}':`, parseResult.error);
3852
+ const parseResult = renderer.content["~standard"].validate(message.content);
3853
+ if (parseResult instanceof Promise) {
3854
+ console.warn(`Async content validation is not supported for activity message '${message.activityType}'`);
3855
+ return null;
3856
+ }
3857
+ if (parseResult.issues) {
3858
+ console.warn(`Failed to parse content for activity message '${message.activityType}':`, parseResult.issues);
3956
3859
  return null;
3957
3860
  }
3958
3861
  const Component = renderer.render;
3959
3862
  const agent = copilotkit.getAgent(agentId);
3960
3863
  return /* @__PURE__ */ jsx(Component, {
3961
3864
  activityType: message.activityType,
3962
- content: parseResult.data,
3865
+ content: parseResult.value,
3963
3866
  message,
3964
3867
  agent
3965
3868
  }, message.id);
@@ -3973,106 +3876,376 @@ function useRenderActivityMessage() {
3973
3876
  findRenderer
3974
3877
  }), [renderActivityMessage, findRenderer]);
3975
3878
  }
3976
-
3977
- //#endregion
3978
- //#region src/v2/hooks/use-frontend-tool.tsx
3979
- const EMPTY_DEPS = [];
3980
- function useFrontendTool(tool, deps) {
3981
- const { copilotkit } = useCopilotKit();
3982
- const extraDeps = deps ?? EMPTY_DEPS;
3983
- useEffect(() => {
3984
- const name = tool.name;
3985
- if (copilotkit.getTool({
3986
- toolName: name,
3987
- agentId: tool.agentId
3988
- })) {
3989
- console.warn(`Tool '${name}' already exists for agent '${tool.agentId || "global"}'. Overriding with latest registration.`);
3990
- copilotkit.removeTool(name, tool.agentId);
3879
+
3880
+ //#endregion
3881
+ //#region src/v2/hooks/use-frontend-tool.tsx
3882
+ const EMPTY_DEPS$1 = [];
3883
+ function useFrontendTool(tool, deps) {
3884
+ const { copilotkit } = useCopilotKit();
3885
+ const extraDeps = deps ?? EMPTY_DEPS$1;
3886
+ useEffect(() => {
3887
+ const name = tool.name;
3888
+ if (copilotkit.getTool({
3889
+ toolName: name,
3890
+ agentId: tool.agentId
3891
+ })) {
3892
+ console.warn(`Tool '${name}' already exists for agent '${tool.agentId || "global"}'. Overriding with latest registration.`);
3893
+ copilotkit.removeTool(name, tool.agentId);
3894
+ }
3895
+ copilotkit.addTool(tool);
3896
+ if (tool.render) copilotkit.addHookRenderToolCall({
3897
+ name,
3898
+ args: tool.parameters,
3899
+ agentId: tool.agentId,
3900
+ render: tool.render
3901
+ });
3902
+ return () => {
3903
+ copilotkit.removeTool(name, tool.agentId);
3904
+ };
3905
+ }, [
3906
+ tool.name,
3907
+ tool.available,
3908
+ copilotkit,
3909
+ JSON.stringify(extraDeps)
3910
+ ]);
3911
+ }
3912
+
3913
+ //#endregion
3914
+ //#region src/v2/hooks/use-component.tsx
3915
+ /**
3916
+ * Registers a React component as a frontend tool renderer in chat.
3917
+ *
3918
+ * This hook is a convenience wrapper around `useFrontendTool` that:
3919
+ * - builds a model-facing tool description,
3920
+ * - forwards optional schema parameters (any Standard Schema V1 compatible library),
3921
+ * - renders your component with tool call parameters.
3922
+ *
3923
+ * Use this when you want to display a typed visual component for a tool call
3924
+ * without manually wiring a full frontend tool object.
3925
+ *
3926
+ * When `parameters` is provided, render props are inferred from the schema.
3927
+ * When omitted, the render component may accept any props.
3928
+ *
3929
+ * @typeParam TSchema - Schema describing tool parameters, or `undefined` when no schema is given.
3930
+ * @param config - Tool registration config.
3931
+ * @param deps - Optional dependencies to refresh registration (same semantics as `useEffect`).
3932
+ *
3933
+ * @example
3934
+ * ```tsx
3935
+ * // Without parameters — render accepts any props
3936
+ * useComponent({
3937
+ * name: "showGreeting",
3938
+ * render: ({ message }: { message: string }) => <div>{message}</div>,
3939
+ * });
3940
+ * ```
3941
+ *
3942
+ * @example
3943
+ * ```tsx
3944
+ * // With parameters — render props inferred from schema
3945
+ * useComponent({
3946
+ * name: "showWeatherCard",
3947
+ * parameters: z.object({ city: z.string() }),
3948
+ * render: ({ city }) => <div>{city}</div>,
3949
+ * });
3950
+ * ```
3951
+ *
3952
+ * @example
3953
+ * ```tsx
3954
+ * useComponent(
3955
+ * {
3956
+ * name: "renderProfile",
3957
+ * parameters: z.object({ userId: z.string() }),
3958
+ * render: ProfileCard,
3959
+ * agentId: "support-agent",
3960
+ * },
3961
+ * [selectedAgentId],
3962
+ * );
3963
+ * ```
3964
+ */
3965
+ function useComponent(config, deps) {
3966
+ const prefix = `Use this tool to display the "${config.name}" component in the chat. This tool renders a visual UI component for the user.`;
3967
+ const fullDescription = config.description ? `${prefix}\n\n${config.description}` : prefix;
3968
+ useFrontendTool({
3969
+ name: config.name,
3970
+ description: fullDescription,
3971
+ parameters: config.parameters,
3972
+ render: ({ args }) => {
3973
+ const Component = config.render;
3974
+ return /* @__PURE__ */ jsx(Component, { ...args });
3975
+ },
3976
+ agentId: config.agentId,
3977
+ followUp: config.followUp
3978
+ }, deps);
3979
+ }
3980
+
3981
+ //#endregion
3982
+ //#region src/v2/hooks/use-render-tool.tsx
3983
+ const EMPTY_DEPS = [];
3984
+ /**
3985
+ * Registers a renderer entry in CopilotKit's `renderToolCalls` registry.
3986
+ *
3987
+ * Key behavior:
3988
+ * - deduplicates by `agentId:name` (latest registration wins),
3989
+ * - keeps renderer entries on cleanup so historical chat tool calls can still render,
3990
+ * - refreshes registration when `deps` change.
3991
+ *
3992
+ * @typeParam S - Schema type describing tool call parameters.
3993
+ * @param config - Renderer config for wildcard or named tools.
3994
+ * @param deps - Optional dependencies to refresh registration.
3995
+ *
3996
+ * @example
3997
+ * ```tsx
3998
+ * useRenderTool(
3999
+ * {
4000
+ * name: "searchDocs",
4001
+ * parameters: z.object({ query: z.string() }),
4002
+ * render: ({ status, parameters, result }) => {
4003
+ * if (status === "executing") return <div>Searching {parameters.query}</div>;
4004
+ * if (status === "complete") return <div>{result}</div>;
4005
+ * return <div>Preparing...</div>;
4006
+ * },
4007
+ * },
4008
+ * [],
4009
+ * );
4010
+ * ```
4011
+ *
4012
+ * @example
4013
+ * ```tsx
4014
+ * useRenderTool(
4015
+ * {
4016
+ * name: "summarize",
4017
+ * parameters: z.object({ text: z.string() }),
4018
+ * agentId: "research-agent",
4019
+ * render: ({ name, status }) => <div>{name}: {status}</div>,
4020
+ * },
4021
+ * [selectedAgentId],
4022
+ * );
4023
+ * ```
4024
+ */
4025
+ function useRenderTool(config, deps) {
4026
+ const { copilotkit } = useCopilotKit();
4027
+ const extraDeps = deps ?? EMPTY_DEPS;
4028
+ useEffect(() => {
4029
+ const renderer = config.name === "*" && !config.parameters ? defineToolCallRenderer({
4030
+ name: "*",
4031
+ render: (props) => config.render({
4032
+ ...props,
4033
+ parameters: props.args
4034
+ }),
4035
+ ...config.agentId ? { agentId: config.agentId } : {}
4036
+ }) : defineToolCallRenderer({
4037
+ name: config.name,
4038
+ args: config.parameters,
4039
+ render: (props) => {
4040
+ if (props.status === ToolCallStatus.InProgress) return config.render({
4041
+ ...props,
4042
+ parameters: props.args
4043
+ });
4044
+ if (props.status === ToolCallStatus.Executing) return config.render({
4045
+ ...props,
4046
+ parameters: props.args
4047
+ });
4048
+ return config.render({
4049
+ ...props,
4050
+ parameters: props.args
4051
+ });
4052
+ },
4053
+ ...config.agentId ? { agentId: config.agentId } : {}
4054
+ });
4055
+ copilotkit.addHookRenderToolCall(renderer);
4056
+ }, [
4057
+ config.name,
4058
+ copilotkit,
4059
+ JSON.stringify(extraDeps)
4060
+ ]);
4061
+ }
4062
+
4063
+ //#endregion
4064
+ //#region src/v2/hooks/use-default-render-tool.tsx
4065
+ /**
4066
+ * Module-level dedup set so an unknown status value only emits a console
4067
+ * warning the FIRST time we encounter it. Otherwise a stuck/unmapped status
4068
+ * would log on every re-render (potentially many per second).
4069
+ */
4070
+ const warnedUnknownStatuses = /* @__PURE__ */ new Set();
4071
+ /**
4072
+ * Map a {@link ToolCallStatus} enum value to the documented string-union
4073
+ * status the {@link DefaultRenderProps} contract exposes. Unknown / future
4074
+ * enum members log a warning (once per distinct value) and fall back to
4075
+ * `"inProgress"`.
4076
+ */
4077
+ function mapToolCallStatus(status) {
4078
+ switch (status) {
4079
+ case ToolCallStatus.Complete: return "complete";
4080
+ case ToolCallStatus.Executing: return "executing";
4081
+ case ToolCallStatus.InProgress: return "inProgress";
4082
+ default: {
4083
+ const key = String(status);
4084
+ if (!warnedUnknownStatuses.has(key)) {
4085
+ warnedUnknownStatuses.add(key);
4086
+ console.warn(`[CopilotKit] Unknown ToolCallStatus "${key}" in default tool-call renderer; falling back to "inProgress".`);
4087
+ }
4088
+ return "inProgress";
4089
+ }
4090
+ }
4091
+ }
4092
+ /**
4093
+ * Convert the framework-internal renderer props (`args`, enum status) into
4094
+ * the documented {@link DefaultRenderProps} shape (`parameters`, string-union
4095
+ * status) so a user `config.render` always sees the documented contract.
4096
+ */
4097
+ function adaptRendererProps(props) {
4098
+ return {
4099
+ name: props.name,
4100
+ toolCallId: props.toolCallId,
4101
+ parameters: props.args,
4102
+ status: mapToolCallStatus(props.status),
4103
+ result: props.result
4104
+ };
4105
+ }
4106
+ /**
4107
+ * Registers a wildcard (`"*"`) tool-call renderer via `useRenderTool`.
4108
+ *
4109
+ * - Call with no config to use CopilotKit's built-in default tool-call card.
4110
+ * - Pass `config.render` to replace the default UI with your own fallback renderer.
4111
+ *
4112
+ * This is useful when you want a generic renderer for tools that do not have a
4113
+ * dedicated `useRenderTool({ name: "..." })` registration.
4114
+ *
4115
+ * @param config - Optional custom wildcard render function.
4116
+ * @param deps - Optional dependencies to refresh registration.
4117
+ *
4118
+ * @example
4119
+ * ```tsx
4120
+ * useDefaultRenderTool();
4121
+ * ```
4122
+ *
4123
+ * @example
4124
+ * ```tsx
4125
+ * useDefaultRenderTool({
4126
+ * render: ({ name, status }) => <div>{name}: {status}</div>,
4127
+ * });
4128
+ * ```
4129
+ *
4130
+ * @example
4131
+ * ```tsx
4132
+ * useDefaultRenderTool(
4133
+ * {
4134
+ * render: ({ name, result }) => (
4135
+ * <ToolEventRow title={name} payload={result} compact={compactMode} />
4136
+ * ),
4137
+ * },
4138
+ * [compactMode],
4139
+ * );
4140
+ * ```
4141
+ */
4142
+ function useDefaultRenderTool(config, deps) {
4143
+ const userRender = config?.render;
4144
+ useRenderTool({
4145
+ name: "*",
4146
+ render: userRender ? (raw) => userRender(adaptRendererProps(raw)) : (raw) => /* @__PURE__ */ jsx(DefaultToolCallRenderer, { ...adaptRendererProps(raw) })
4147
+ }, deps);
4148
+ }
4149
+ /**
4150
+ * Guarded JSON.stringify used inside the expanded `<pre>` blocks. A circular
4151
+ * reference would otherwise crash the entire React tree on render.
4152
+ */
4153
+ function safeStringifyForPre(value) {
4154
+ try {
4155
+ return JSON.stringify(value, null, 2);
4156
+ } catch (err) {
4157
+ console.warn("[CopilotKit] Failed to JSON.stringify tool-call payload for default renderer; falling back to String():", err);
4158
+ try {
4159
+ return String(value);
4160
+ } catch (innerErr) {
4161
+ console.warn("[CopilotKit] safeStringifyForPre: value could not be stringified:", innerErr);
4162
+ return "[unserializable]";
4163
+ }
4164
+ }
4165
+ }
4166
+ function DefaultToolCallRenderer({ name, toolCallId, parameters, status, result }) {
4167
+ const [isExpanded, setIsExpanded] = useState(false);
4168
+ const isActive = status === "inProgress" || status === "executing";
4169
+ const isComplete = status === "complete";
4170
+ const statusLabel = isActive ? "Running" : isComplete ? "Done" : status;
4171
+ const dotClassName = isActive ? "cpk:bg-amber-500" : isComplete ? "cpk:bg-emerald-500" : "cpk:bg-zinc-400";
4172
+ 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";
4173
+ return /* @__PURE__ */ jsx("div", {
4174
+ "data-testid": "copilot-tool-render",
4175
+ "data-tool-name": name,
4176
+ "data-tool-call-id": toolCallId,
4177
+ "data-status": status,
4178
+ "data-args": safeStringifyForAttr(parameters),
4179
+ "data-result": safeStringifyForAttr(result),
4180
+ className: "cpk:mt-2 cpk:pb-2",
4181
+ children: /* @__PURE__ */ jsxs("div", {
4182
+ 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",
4183
+ children: [/* @__PURE__ */ jsxs("button", {
4184
+ type: "button",
4185
+ "aria-expanded": isExpanded,
4186
+ onClick: () => setIsExpanded(!isExpanded),
4187
+ 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",
4188
+ style: { font: "inherit" },
4189
+ children: [/* @__PURE__ */ jsxs("div", {
4190
+ className: "cpk:flex cpk:min-w-0 cpk:items-center cpk:gap-2",
4191
+ children: [
4192
+ /* @__PURE__ */ jsx("svg", {
4193
+ 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" : ""}`,
4194
+ fill: "none",
4195
+ viewBox: "0 0 24 24",
4196
+ strokeWidth: 2,
4197
+ stroke: "currentColor",
4198
+ children: /* @__PURE__ */ jsx("path", {
4199
+ strokeLinecap: "round",
4200
+ strokeLinejoin: "round",
4201
+ d: "M8.25 4.5l7.5 7.5-7.5 7.5"
4202
+ })
4203
+ }),
4204
+ /* @__PURE__ */ jsx("span", { className: `cpk:inline-block cpk:h-2 cpk:w-2 cpk:flex-shrink-0 cpk:rounded-full ${dotClassName}` }),
4205
+ /* @__PURE__ */ jsx("span", {
4206
+ "data-testid": "copilot-tool-render-name",
4207
+ className: "cpk:truncate cpk:text-[13px] cpk:font-semibold cpk:text-zinc-900 cpk:dark:text-zinc-100",
4208
+ children: name
4209
+ })
4210
+ ]
4211
+ }), /* @__PURE__ */ jsx("span", {
4212
+ "data-testid": "copilot-tool-render-status",
4213
+ 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}`,
4214
+ children: statusLabel
4215
+ })]
4216
+ }), isExpanded && /* @__PURE__ */ jsxs("div", {
4217
+ className: "cpk:mt-3 cpk:grid cpk:gap-3",
4218
+ children: [/* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("div", {
4219
+ className: "cpk:text-[10px] cpk:uppercase cpk:text-zinc-500 cpk:dark:text-zinc-400",
4220
+ children: "Arguments"
4221
+ }), /* @__PURE__ */ jsx("pre", {
4222
+ 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",
4223
+ children: safeStringifyForPre(parameters ?? {})
4224
+ })] }), result !== void 0 && /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("div", {
4225
+ className: "cpk:text-[10px] cpk:uppercase cpk:text-zinc-500 cpk:dark:text-zinc-400",
4226
+ children: "Result"
4227
+ }), /* @__PURE__ */ jsx("pre", {
4228
+ 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",
4229
+ children: typeof result === "string" ? result : safeStringifyForPre(result)
4230
+ })] })]
4231
+ })]
4232
+ })
4233
+ });
4234
+ }
4235
+ function safeStringifyForAttr(value) {
4236
+ if (value === void 0 || value === null) return "";
4237
+ if (typeof value === "string") return value;
4238
+ try {
4239
+ return JSON.stringify(value);
4240
+ } catch (err) {
4241
+ console.warn("[CopilotKit] Failed to JSON.stringify tool-call payload for data-* attribute; falling back to String():", err);
4242
+ try {
4243
+ return String(value);
4244
+ } catch (innerErr) {
4245
+ console.warn("[CopilotKit] safeStringifyForAttr: value could not be stringified:", innerErr);
4246
+ return "";
3991
4247
  }
3992
- copilotkit.addTool(tool);
3993
- if (tool.render) copilotkit.addHookRenderToolCall({
3994
- name,
3995
- args: tool.parameters,
3996
- agentId: tool.agentId,
3997
- render: tool.render
3998
- });
3999
- return () => {
4000
- copilotkit.removeTool(name, tool.agentId);
4001
- };
4002
- }, [
4003
- tool.name,
4004
- tool.available,
4005
- copilotkit,
4006
- JSON.stringify(extraDeps)
4007
- ]);
4008
- }
4009
-
4010
- //#endregion
4011
- //#region src/v2/hooks/use-component.tsx
4012
- /**
4013
- * Registers a React component as a frontend tool renderer in chat.
4014
- *
4015
- * This hook is a convenience wrapper around `useFrontendTool` that:
4016
- * - builds a model-facing tool description,
4017
- * - forwards optional schema parameters (any Standard Schema V1 compatible library),
4018
- * - renders your component with tool call parameters.
4019
- *
4020
- * Use this when you want to display a typed visual component for a tool call
4021
- * without manually wiring a full frontend tool object.
4022
- *
4023
- * When `parameters` is provided, render props are inferred from the schema.
4024
- * When omitted, the render component may accept any props.
4025
- *
4026
- * @typeParam TSchema - Schema describing tool parameters, or `undefined` when no schema is given.
4027
- * @param config - Tool registration config.
4028
- * @param deps - Optional dependencies to refresh registration (same semantics as `useEffect`).
4029
- *
4030
- * @example
4031
- * ```tsx
4032
- * // Without parameters — render accepts any props
4033
- * useComponent({
4034
- * name: "showGreeting",
4035
- * render: ({ message }: { message: string }) => <div>{message}</div>,
4036
- * });
4037
- * ```
4038
- *
4039
- * @example
4040
- * ```tsx
4041
- * // With parameters — render props inferred from schema
4042
- * useComponent({
4043
- * name: "showWeatherCard",
4044
- * parameters: z.object({ city: z.string() }),
4045
- * render: ({ city }) => <div>{city}</div>,
4046
- * });
4047
- * ```
4048
- *
4049
- * @example
4050
- * ```tsx
4051
- * useComponent(
4052
- * {
4053
- * name: "renderProfile",
4054
- * parameters: z.object({ userId: z.string() }),
4055
- * render: ProfileCard,
4056
- * agentId: "support-agent",
4057
- * },
4058
- * [selectedAgentId],
4059
- * );
4060
- * ```
4061
- */
4062
- function useComponent(config, deps) {
4063
- const prefix = `Use this tool to display the "${config.name}" component in the chat. This tool renders a visual UI component for the user.`;
4064
- const fullDescription = config.description ? `${prefix}\n\n${config.description}` : prefix;
4065
- useFrontendTool({
4066
- name: config.name,
4067
- description: fullDescription,
4068
- parameters: config.parameters,
4069
- render: ({ args }) => {
4070
- const Component = config.render;
4071
- return /* @__PURE__ */ jsx(Component, { ...args });
4072
- },
4073
- agentId: config.agentId,
4074
- followUp: config.followUp
4075
- }, deps);
4248
+ }
4076
4249
  }
4077
4250
 
4078
4251
  //#endregion
@@ -4195,7 +4368,7 @@ function useAgent({ agentId, updates, throttleMs } = {}) {
4195
4368
  if (isRuntimeConfigured && (status === CopilotKitCoreRuntimeConnectionStatus.Disconnected || status === CopilotKitCoreRuntimeConnectionStatus.Connecting)) {
4196
4369
  const cached = provisionalAgentCache.current.get(agentId);
4197
4370
  if (cached) {
4198
- cached.headers = { ...copilotkit.headers };
4371
+ copilotkit.applyHeadersToAgent(cached);
4199
4372
  return cached;
4200
4373
  }
4201
4374
  const provisional = new ProxiedCopilotRuntimeAgent({
@@ -4204,14 +4377,14 @@ function useAgent({ agentId, updates, throttleMs } = {}) {
4204
4377
  transport: copilotkit.runtimeTransport,
4205
4378
  runtimeMode: "pending"
4206
4379
  });
4207
- provisional.headers = { ...copilotkit.headers };
4380
+ copilotkit.applyHeadersToAgent(provisional);
4208
4381
  provisionalAgentCache.current.set(agentId, provisional);
4209
4382
  return provisional;
4210
4383
  }
4211
4384
  if (isRuntimeConfigured && status === CopilotKitCoreRuntimeConnectionStatus.Error) {
4212
4385
  const cached = provisionalAgentCache.current.get(agentId);
4213
4386
  if (cached) {
4214
- cached.headers = { ...copilotkit.headers };
4387
+ copilotkit.applyHeadersToAgent(cached);
4215
4388
  return cached;
4216
4389
  }
4217
4390
  const provisional = new ProxiedCopilotRuntimeAgent({
@@ -4220,7 +4393,7 @@ function useAgent({ agentId, updates, throttleMs } = {}) {
4220
4393
  transport: copilotkit.runtimeTransport,
4221
4394
  runtimeMode: "pending"
4222
4395
  });
4223
- provisional.headers = { ...copilotkit.headers };
4396
+ copilotkit.applyHeadersToAgent(provisional);
4224
4397
  provisionalAgentCache.current.set(agentId, provisional);
4225
4398
  return provisional;
4226
4399
  }
@@ -4271,7 +4444,7 @@ function useAgent({ agentId, updates, throttleMs } = {}) {
4271
4444
  updateFlags
4272
4445
  ]);
4273
4446
  useEffect(() => {
4274
- if (agent instanceof HttpAgent) agent.headers = { ...copilotkit.headers };
4447
+ if (agent instanceof HttpAgent) copilotkit.applyHeadersToAgent(agent);
4275
4448
  }, [agent, JSON.stringify(copilotkit.headers)]);
4276
4449
  const chatConfig = useCopilotChatConfiguration();
4277
4450
  const configThreadId = chatConfig?.threadId;
@@ -4548,114 +4721,165 @@ const INTERRUPT_EVENT_NAME = "on_interrupt";
4548
4721
  function isPromiseLike(value) {
4549
4722
  return (typeof value === "object" || typeof value === "function") && value !== null && typeof Reflect.get(value, "then") === "function";
4550
4723
  }
4724
+ /** Derive the legacy-compatible `event` for any pending interrupt. */
4725
+ function toLegacyEvent(pending) {
4726
+ if (pending.kind === "legacy") return pending.event;
4727
+ return {
4728
+ name: INTERRUPT_EVENT_NAME,
4729
+ value: pending.interrupts[0]
4730
+ };
4731
+ }
4551
4732
  /**
4552
- * Handles agent interrupts (`on_interrupt`) with optional filtering, preprocessing, and resume behavior.
4733
+ * Handles agent interrupts with optional filtering, preprocessing, and resume behavior.
4553
4734
  *
4554
- * The hook listens to custom events on the active agent, stores interrupt payloads per run,
4555
- * and surfaces a render callback once the run finalizes. Call `resolve` from your UI to resume
4556
- * execution with user-provided data.
4557
- *
4558
- * - `renderInChat: true` (default): the element is published into `<CopilotChat>` and this hook returns `void`.
4559
- * - `renderInChat: false`: the hook returns the interrupt element so you can place it anywhere in your component tree.
4560
- *
4561
- * `event.value` is typed as `any` since the interrupt payload shape depends on your agent.
4562
- * Type-narrow it in your callbacks (e.g. `handler`, `enabled`, `render`) as needed.
4563
- *
4564
- * @typeParam TResult - Inferred from `handler` return type. Exposed as `result` in `render`.
4565
- * @param config - Interrupt configuration (renderer, optional handler/filter, and render mode).
4566
- * @returns When `renderInChat` is `false`, returns the interrupt element (or `null` when idle).
4567
- * Otherwise returns `void` and publishes the element into chat. In `render`, `result` is always
4568
- * either the handler's resolved return value or `null` (including when no handler is provided,
4569
- * when filtering skips the interrupt, or when handler execution fails).
4570
- *
4571
- * @example
4572
- * ```tsx
4573
- * import { useInterrupt } from "@copilotkit/react-core/v2";
4574
- *
4575
- * function InterruptUI() {
4576
- * useInterrupt({
4577
- * render: ({ event, resolve }) => (
4578
- * <div>
4579
- * <p>{event.value.question}</p>
4580
- * <button onClick={() => resolve({ approved: true })}>Approve</button>
4581
- * <button onClick={() => resolve({ approved: false })}>Reject</button>
4582
- * </div>
4583
- * ),
4584
- * });
4735
+ * Supports both the AG-UI standard interrupt flow (`RUN_FINISHED` with
4736
+ * `outcome.type === "interrupt"`) and the legacy custom-event flow
4737
+ * (`on_interrupt`). For standard interrupts, `render` receives `interrupt`
4738
+ * (the primary one) and `interrupts` (the full open set); call `resolve(payload)`
4739
+ * to resume or `cancel()` to cancel. Resuming addresses the targeted interrupt
4740
+ * and, once every open interrupt is addressed, submits a single spec `resume`
4741
+ * array via `copilotkit.runAgent`.
4585
4742
  *
4586
- * return null;
4587
- * }
4588
- * ```
4743
+ * - `renderInChat: true` (default): the element is published into `<CopilotChat>`; returns `void`.
4744
+ * - `renderInChat: false`: the hook returns the interrupt element for manual placement.
4589
4745
  *
4590
4746
  * @example
4591
4747
  * ```tsx
4592
- * import { useInterrupt } from "@copilotkit/react-core/v2";
4593
- *
4594
- * function CustomPanel() {
4595
- * const interruptElement = useInterrupt({
4596
- * renderInChat: false,
4597
- * enabled: (event) => event.value.startsWith("approval:"),
4598
- * handler: async ({ event }) => ({ label: event.value.toUpperCase() }),
4599
- * render: ({ event, result, resolve }) => (
4600
- * <aside>
4601
- * <strong>{result?.label ?? ""}</strong>
4602
- * <button onClick={() => resolve({ value: event.value })}>Continue</button>
4603
- * </aside>
4604
- * ),
4605
- * });
4606
- *
4607
- * return <>{interruptElement}</>;
4608
- * }
4748
+ * useInterrupt({
4749
+ * render: ({ interrupt, resolve, cancel }) => (
4750
+ * <div>
4751
+ * <p>{interrupt?.message}</p>
4752
+ * <button onClick={() => resolve({ approved: true })}>Approve</button>
4753
+ * <button onClick={() => cancel()}>Cancel</button>
4754
+ * </div>
4755
+ * ),
4756
+ * });
4609
4757
  * ```
4610
4758
  */
4611
4759
  function useInterrupt(config) {
4612
4760
  const { copilotkit } = useCopilotKit();
4613
4761
  const { agent } = useAgent({ agentId: config.agentId });
4614
- const [pendingEvent, setPendingEvent] = useState(null);
4615
- const pendingEventRef = useRef(pendingEvent);
4616
- pendingEventRef.current = pendingEvent;
4762
+ const [pending, setPending] = useState(null);
4763
+ const pendingRef = useRef(pending);
4764
+ pendingRef.current = pending;
4617
4765
  const [handlerResult, setHandlerResult] = useState(null);
4766
+ const responsesRef = useRef({});
4618
4767
  useEffect(() => {
4619
- let localInterrupt = null;
4768
+ let localLegacy = null;
4769
+ let localStandard = null;
4620
4770
  const subscription = agent.subscribe({
4621
4771
  onCustomEvent: ({ event }) => {
4622
- if (event.name === INTERRUPT_EVENT_NAME) localInterrupt = {
4772
+ if (event.name === INTERRUPT_EVENT_NAME) localLegacy = {
4623
4773
  name: event.name,
4624
4774
  value: event.value
4625
4775
  };
4626
4776
  },
4777
+ onRunFinishedEvent: (params) => {
4778
+ if (params.outcome === "interrupt") localStandard = params.interrupts;
4779
+ },
4627
4780
  onRunStartedEvent: () => {
4628
- localInterrupt = null;
4629
- setPendingEvent(null);
4781
+ localLegacy = null;
4782
+ localStandard = null;
4783
+ responsesRef.current = {};
4784
+ setPending(null);
4630
4785
  },
4631
4786
  onRunFinalized: () => {
4632
- if (localInterrupt) {
4633
- setPendingEvent(localInterrupt);
4634
- localInterrupt = null;
4635
- }
4787
+ if (localStandard && localStandard.length > 0) setPending({
4788
+ kind: "standard",
4789
+ interrupts: localStandard
4790
+ });
4791
+ else if (localLegacy) setPending({
4792
+ kind: "legacy",
4793
+ event: localLegacy
4794
+ });
4795
+ localLegacy = null;
4796
+ localStandard = null;
4636
4797
  },
4637
4798
  onRunFailed: () => {
4638
- localInterrupt = null;
4639
- setPendingEvent(null);
4799
+ localLegacy = null;
4800
+ localStandard = null;
4801
+ responsesRef.current = {};
4802
+ setPending(null);
4640
4803
  }
4641
4804
  });
4642
4805
  return () => subscription.unsubscribe();
4643
4806
  }, [agent]);
4644
- const resolve = useCallback(async (response) => {
4807
+ const submitStandardIfComplete = useCallback(async (interrupts) => {
4808
+ if (!interrupts.every((i) => responsesRef.current[i.id])) return;
4809
+ const expired = interrupts.find((i) => isInterruptExpired(i));
4810
+ if (expired) {
4811
+ console.error(`[CopilotKit] useInterrupt: interrupt ${expired.id} expired at ${expired.expiresAt}; not resuming.`);
4812
+ responsesRef.current = {};
4813
+ setPending(null);
4814
+ return;
4815
+ }
4816
+ const resume = buildResumeArray(interrupts, responsesRef.current);
4817
+ for (const i of interrupts) {
4818
+ if (!i.toolCallId) continue;
4819
+ const response = responsesRef.current[i.id];
4820
+ const content = response.status === "cancelled" ? { status: "cancelled" } : response.payload ?? { status: "resolved" };
4821
+ agent.addMessage({
4822
+ id: randomUUID(),
4823
+ role: "tool",
4824
+ toolCallId: i.toolCallId,
4825
+ content: JSON.stringify(content)
4826
+ });
4827
+ }
4828
+ responsesRef.current = {};
4645
4829
  try {
4830
+ return await copilotkit.runAgent({
4831
+ agent,
4832
+ resume
4833
+ });
4834
+ } catch (err) {
4835
+ console.error("[CopilotKit] useInterrupt resolve: runAgent rejected; clearing pending + rethrowing", err);
4836
+ setPending(null);
4837
+ throw err;
4838
+ }
4839
+ }, [agent, copilotkit]);
4840
+ const resolve = useCallback(async (payload, interruptId) => {
4841
+ const current = pendingRef.current;
4842
+ if (!current) return;
4843
+ if (current.kind === "legacy") try {
4646
4844
  return await copilotkit.runAgent({
4647
4845
  agent,
4648
4846
  forwardedProps: { command: {
4649
- resume: response,
4650
- interruptEvent: pendingEventRef.current?.value
4847
+ resume: payload,
4848
+ interruptEvent: current.event.value
4651
4849
  } }
4652
4850
  });
4653
4851
  } catch (err) {
4654
4852
  console.error("[CopilotKit] useInterrupt resolve: runAgent rejected; clearing pending + rethrowing", err);
4655
- setPendingEvent(null);
4853
+ setPending(null);
4656
4854
  throw err;
4657
4855
  }
4658
- }, [agent, copilotkit]);
4856
+ 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.`);
4857
+ const id = interruptId ?? current.interrupts[0]?.id;
4858
+ if (!id) return;
4859
+ responsesRef.current[id] = {
4860
+ status: "resolved",
4861
+ payload
4862
+ };
4863
+ return submitStandardIfComplete(current.interrupts);
4864
+ }, [
4865
+ agent,
4866
+ copilotkit,
4867
+ submitStandardIfComplete
4868
+ ]);
4869
+ const cancel = useCallback(async (interruptId) => {
4870
+ const current = pendingRef.current;
4871
+ if (!current) return;
4872
+ if (current.kind === "legacy") {
4873
+ console.warn("[CopilotKit] useInterrupt: cancel() is not supported for legacy on_interrupt interrupts; dismissing.");
4874
+ setPending(null);
4875
+ return;
4876
+ }
4877
+ 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.`);
4878
+ const id = interruptId ?? current.interrupts[0]?.id;
4879
+ if (!id) return;
4880
+ responsesRef.current[id] = { status: "cancelled" };
4881
+ return submitStandardIfComplete(current.interrupts);
4882
+ }, [submitStandardIfComplete]);
4659
4883
  const renderRef = useRef(config.render);
4660
4884
  renderRef.current = config.render;
4661
4885
  const enabledRef = useRef(config.enabled);
@@ -4664,6 +4888,8 @@ function useInterrupt(config) {
4664
4888
  handlerRef.current = config.handler;
4665
4889
  const resolveRef = useRef(resolve);
4666
4890
  resolveRef.current = resolve;
4891
+ const cancelRef = useRef(cancel);
4892
+ cancelRef.current = cancel;
4667
4893
  const isEnabled = (event) => {
4668
4894
  const predicate = enabledRef.current;
4669
4895
  if (!predicate) return true;
@@ -4675,11 +4901,12 @@ function useInterrupt(config) {
4675
4901
  }
4676
4902
  };
4677
4903
  useEffect(() => {
4678
- if (!pendingEvent) {
4904
+ if (!pending) {
4679
4905
  setHandlerResult(null);
4680
4906
  return;
4681
4907
  }
4682
- if (!isEnabled(pendingEvent)) {
4908
+ const legacyEvent = toLegacyEvent(pending);
4909
+ if (!isEnabled(legacyEvent)) {
4683
4910
  setHandlerResult(null);
4684
4911
  return;
4685
4912
  }
@@ -4692,8 +4919,11 @@ function useInterrupt(config) {
4692
4919
  let maybePromise;
4693
4920
  try {
4694
4921
  maybePromise = handler({
4695
- event: pendingEvent,
4696
- resolve: resolveRef.current
4922
+ event: legacyEvent,
4923
+ interrupt: pending.kind === "standard" ? pending.interrupts[0] : null,
4924
+ interrupts: pending.kind === "standard" ? pending.interrupts : [],
4925
+ resolve: resolveRef.current,
4926
+ cancel: cancelRef.current
4697
4927
  });
4698
4928
  } catch (err) {
4699
4929
  console.error("[CopilotKit] useInterrupt handler threw; result will be null:", err);
@@ -4712,19 +4942,24 @@ function useInterrupt(config) {
4712
4942
  return () => {
4713
4943
  cancelled = true;
4714
4944
  };
4715
- }, [pendingEvent]);
4945
+ }, [pending]);
4716
4946
  const element = useMemo(() => {
4717
- if (!pendingEvent) return null;
4718
- if (!isEnabled(pendingEvent)) return null;
4947
+ if (!pending) return null;
4948
+ const legacyEvent = toLegacyEvent(pending);
4949
+ if (!isEnabled(legacyEvent)) return null;
4719
4950
  return renderRef.current({
4720
- event: pendingEvent,
4951
+ event: legacyEvent,
4952
+ interrupt: pending.kind === "standard" ? pending.interrupts[0] : null,
4953
+ interrupts: pending.kind === "standard" ? pending.interrupts : [],
4721
4954
  result: handlerResult,
4722
- resolve
4955
+ resolve,
4956
+ cancel
4723
4957
  });
4724
4958
  }, [
4725
- pendingEvent,
4959
+ pending,
4726
4960
  handlerResult,
4727
- resolve
4961
+ resolve,
4962
+ cancel
4728
4963
  ]);
4729
4964
  useEffect(() => {
4730
4965
  if (config.renderInChat === false) return;
@@ -4749,7 +4984,7 @@ function useThreadStoreSelector(store, selector) {
4749
4984
  return useSyncExternalStore(useCallback((onStoreChange) => {
4750
4985
  const subscription = store.select(selector).subscribe(onStoreChange);
4751
4986
  return () => subscription.unsubscribe();
4752
- }, [store, selector]), () => selector(store.getState()));
4987
+ }, [store, selector]), () => selector(store.getState()), () => selector(store.getServerState()));
4753
4988
  }
4754
4989
  /**
4755
4990
  * React hook for listing and managing Intelligence platform threads.
@@ -4760,9 +4995,9 @@ function useThreadStoreSelector(store, selector) {
4760
4995
  * current without polling — thread creates, renames, archives, and deletes
4761
4996
  * from any client are reflected immediately.
4762
4997
  *
4763
- * Mutation methods (`renameThread`, `archiveThread`, `deleteThread`) return
4764
- * promises that resolve once the platform confirms the operation and reject
4765
- * with an `Error` on failure.
4998
+ * Mutation methods (`renameThread`, `archiveThread`, `unarchiveThread`,
4999
+ * `deleteThread`) return promises that resolve once the platform confirms the
5000
+ * operation and reject with an `Error` on failure.
4766
5001
  *
4767
5002
  * @param input - Agent identifier and optional list controls.
4768
5003
  * @returns Thread list state and stable mutation callbacks.
@@ -4792,7 +5027,7 @@ function useThreadStoreSelector(store, selector) {
4792
5027
  * }
4793
5028
  * ```
4794
5029
  */
4795
- function useThreads$1({ agentId, includeArchived, limit }) {
5030
+ function useThreads$1({ agentId, includeArchived, limit, enabled = true }) {
4796
5031
  const { copilotkit } = useCopilotKit();
4797
5032
  const [store] = useState(() => ɵcreateThreadStore({ fetch: globalThis.fetch }));
4798
5033
  const coreThreads = useThreadStoreSelector(store, ɵselectThreads);
@@ -4809,6 +5044,7 @@ function useThreads$1({ agentId, includeArchived, limit }) {
4809
5044
  const storeError = useThreadStoreSelector(store, ɵselectThreadsError);
4810
5045
  const hasMoreThreads = useThreadStoreSelector(store, ɵselectHasNextPage);
4811
5046
  const isFetchingMoreThreads = useThreadStoreSelector(store, ɵselectIsFetchingNextPage);
5047
+ const isMutating = useThreadStoreSelector(store, ɵselectIsMutating);
4812
5048
  const headersKey = useMemo(() => {
4813
5049
  return JSON.stringify(Object.entries(copilotkit.headers ?? {}).sort(([left], [right]) => left.localeCompare(right)));
4814
5050
  }, [copilotkit.headers]);
@@ -4829,9 +5065,16 @@ function useThreads$1({ agentId, includeArchived, limit }) {
4829
5065
  return /* @__PURE__ */ new Error("Thread mutations are not available on this CopilotKit runtime");
4830
5066
  }, [threadMutationsSupported]);
4831
5067
  const [hasDispatchedContext, setHasDispatchedContext] = useState(false);
4832
- const preConnectLoading = !!copilotkit.runtimeUrl && !threadEndpointsUnavailable && !hasDispatchedContext;
4833
- const isLoading = runtimeError || threadEndpointsError ? false : preConnectLoading || storeIsLoading;
4834
- const error = runtimeError ?? threadEndpointsError ?? storeError;
5068
+ const preConnectLoading = enabled && !!copilotkit.runtimeUrl && !threadEndpointsUnavailable && !hasDispatchedContext;
5069
+ const [configErrorDismissed, setConfigErrorDismissed] = useState(false);
5070
+ useEffect(() => {
5071
+ setConfigErrorDismissed(false);
5072
+ }, [runtimeError, threadEndpointsError]);
5073
+ const activeRuntimeError = configErrorDismissed ? null : runtimeError;
5074
+ const activeThreadEndpointsError = configErrorDismissed ? null : threadEndpointsError;
5075
+ const isLoading = activeRuntimeError || activeThreadEndpointsError ? false : preConnectLoading || storeIsLoading;
5076
+ const error = activeRuntimeError ?? activeThreadEndpointsError ?? storeError;
5077
+ const listError = storeError;
4835
5078
  useEffect(() => {
4836
5079
  store.start();
4837
5080
  return () => {
@@ -4839,6 +5082,7 @@ function useThreads$1({ agentId, includeArchived, limit }) {
4839
5082
  };
4840
5083
  }, [store]);
4841
5084
  useEffect(() => {
5085
+ if (!enabled) return;
4842
5086
  copilotkit.registerThreadStore(agentId, store);
4843
5087
  return () => {
4844
5088
  copilotkit.unregisterThreadStore(agentId);
@@ -4846,9 +5090,15 @@ function useThreads$1({ agentId, includeArchived, limit }) {
4846
5090
  }, [
4847
5091
  copilotkit,
4848
5092
  agentId,
4849
- store
5093
+ store,
5094
+ enabled
4850
5095
  ]);
4851
5096
  useEffect(() => {
5097
+ if (!enabled) {
5098
+ store.setContext(null);
5099
+ setHasDispatchedContext(false);
5100
+ return;
5101
+ }
4852
5102
  if (!copilotkit.runtimeUrl) {
4853
5103
  store.setContext(null);
4854
5104
  setHasDispatchedContext(false);
@@ -4872,6 +5122,7 @@ function useThreads$1({ agentId, includeArchived, limit }) {
4872
5122
  setHasDispatchedContext(true);
4873
5123
  }, [
4874
5124
  store,
5125
+ enabled,
4875
5126
  copilotkit.runtimeUrl,
4876
5127
  runtimeStatus,
4877
5128
  headersKey,
@@ -4889,16 +5140,25 @@ function useThreads$1({ agentId, includeArchived, limit }) {
4889
5140
  }, [threadMutationsError]);
4890
5141
  const renameThread = useMemo(() => guardMutation((threadId, name) => store.renameThread(threadId, name)), [store, guardMutation]);
4891
5142
  const archiveThread = useMemo(() => guardMutation((threadId) => store.archiveThread(threadId)), [store, guardMutation]);
5143
+ const unarchiveThread = useMemo(() => guardMutation((threadId) => store.unarchiveThread(threadId)), [store, guardMutation]);
4892
5144
  const deleteThread = useMemo(() => guardMutation((threadId) => store.deleteThread(threadId)), [store, guardMutation]);
4893
5145
  return {
4894
5146
  threads,
4895
5147
  isLoading,
4896
5148
  error,
5149
+ listError,
4897
5150
  hasMoreThreads,
4898
5151
  isFetchingMoreThreads,
5152
+ isMutating,
4899
5153
  fetchMoreThreads: useCallback(() => store.fetchNextPage(), [store]),
5154
+ refetchThreads: useCallback(() => store.refetchThreads(), [store]),
5155
+ startNewThread: useCallback(() => {
5156
+ setConfigErrorDismissed(true);
5157
+ store.startNewThread();
5158
+ }, [store]),
4900
5159
  renameThread,
4901
5160
  archiveThread,
5161
+ unarchiveThread,
4902
5162
  deleteThread
4903
5163
  };
4904
5164
  }
@@ -4932,7 +5192,7 @@ async function recordAnnotation(args) {
4932
5192
  const body = {
4933
5193
  type,
4934
5194
  threadId,
4935
- clientEventId: args.clientEventId ?? randomUUID(),
5195
+ clientEventId: args.clientEventId ?? randomUUID$1(),
4936
5196
  ...payload !== void 0 ? { payload } : {},
4937
5197
  ...occurredAt !== void 0 ? { occurredAt } : {}
4938
5198
  };
@@ -5110,7 +5370,7 @@ function useAttachments({ config }) {
5110
5370
  continue;
5111
5371
  }
5112
5372
  const modality = getModalityFromMimeType(file.type);
5113
- const placeholderId = randomUUID();
5373
+ const placeholderId = randomUUID$1();
5114
5374
  const placeholder = {
5115
5375
  id: placeholderId,
5116
5376
  type: modality,
@@ -6082,7 +6342,7 @@ const DefaultContainer = React.forwardRef(function DefaultContainer({ className,
6082
6342
  ref,
6083
6343
  "data-copilotkit": true,
6084
6344
  "data-testid": "copilot-suggestions",
6085
- 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),
6345
+ 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),
6086
6346
  ...props
6087
6347
  });
6088
6348
  });
@@ -7187,7 +7447,7 @@ function CopilotChatView({ messageView, input, scrollView, suggestionView, welco
7187
7447
  children: /* @__PURE__ */ jsxs("div", {
7188
7448
  className: "cpk:max-w-3xl cpk:mx-auto",
7189
7449
  children: [BoundMessageView, hasSuggestions ? /* @__PURE__ */ jsx("div", {
7190
- className: "cpk:pl-0 cpk:pr-4 cpk:sm:px-0 cpk:mt-4",
7450
+ className: "cpk:pl-0 cpk:pr-4 cpk:@3xl:px-0 cpk:mt-4",
7191
7451
  children: BoundSuggestionView
7192
7452
  }) : null]
7193
7453
  })
@@ -7230,7 +7490,7 @@ function CopilotChatView({ messageView, input, scrollView, suggestionView, welco
7230
7490
  onDragOver,
7231
7491
  onDragLeave,
7232
7492
  onDrop,
7233
- className: cn("copilotKitChat cpk:relative cpk:h-full cpk:flex cpk:flex-col", className),
7493
+ className: cn("copilotKitChat cpk:@container cpk:relative cpk:h-full cpk:flex cpk:flex-col", className),
7234
7494
  ...props,
7235
7495
  children: [dragOver && /* @__PURE__ */ jsx(DropOverlay, {}), BoundWelcomeScreen]
7236
7496
  });
@@ -7252,7 +7512,7 @@ function CopilotChatView({ messageView, input, scrollView, suggestionView, welco
7252
7512
  onDragOver,
7253
7513
  onDragLeave,
7254
7514
  onDrop,
7255
- className: cn("copilotKitChat cpk:relative cpk:h-full cpk:flex cpk:flex-col", className),
7515
+ className: cn("copilotKitChat cpk:@container cpk:relative cpk:h-full cpk:flex cpk:flex-col", className),
7256
7516
  ...props,
7257
7517
  children: [
7258
7518
  dragOver && /* @__PURE__ */ jsx(DropOverlay, {}),
@@ -7291,7 +7551,7 @@ function CopilotChatView({ messageView, input, scrollView, suggestionView, welco
7291
7551
  minHeight: 0
7292
7552
  },
7293
7553
  children: /* @__PURE__ */ jsx("div", {
7294
- className: "cpk:px-4 cpk:sm:px-0 cpk:[div[data-sidebar-chat]_&]:px-8 cpk:[div[data-popup-chat]_&]:px-6",
7554
+ className: "cpk:px-4 cpk:@3xl:px-0 cpk:[div[data-sidebar-chat]_&]:px-8 cpk:[div[data-popup-chat]_&]:px-6",
7295
7555
  children
7296
7556
  })
7297
7557
  }),
@@ -7324,7 +7584,7 @@ function CopilotChatView({ messageView, input, scrollView, suggestionView, welco
7324
7584
  ...props,
7325
7585
  children: [/* @__PURE__ */ jsx("div", {
7326
7586
  ref: contentRef,
7327
- className: "cpk:px-4 cpk:sm:px-0 cpk:[div[data-sidebar-chat]_&]:px-8 cpk:[div[data-popup-chat]_&]:px-6",
7587
+ className: "cpk:px-4 cpk:@3xl:px-0 cpk:[div[data-sidebar-chat]_&]:px-8 cpk:[div[data-popup-chat]_&]:px-6",
7328
7588
  children
7329
7589
  }), /* @__PURE__ */ jsx("div", {
7330
7590
  ref: spacerRef,
@@ -7386,7 +7646,7 @@ function CopilotChatView({ messageView, input, scrollView, suggestionView, welco
7386
7646
  if (!hasMounted) return /* @__PURE__ */ jsx("div", {
7387
7647
  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",
7388
7648
  children: /* @__PURE__ */ jsx("div", {
7389
- className: "cpk:px-4 cpk:sm:px-0 cpk:[div[data-sidebar-chat]_&]:px-8 cpk:[div[data-popup-chat]_&]:px-6",
7649
+ className: "cpk:px-4 cpk:@3xl:px-0 cpk:[div[data-sidebar-chat]_&]:px-8 cpk:[div[data-popup-chat]_&]:px-6",
7390
7650
  children
7391
7651
  })
7392
7652
  });
@@ -7401,7 +7661,7 @@ function CopilotChatView({ messageView, input, scrollView, suggestionView, welco
7401
7661
  children: [
7402
7662
  /* @__PURE__ */ jsx("div", {
7403
7663
  ref: contentRef,
7404
- className: "cpk:px-4 cpk:sm:px-0 cpk:[div[data-sidebar-chat]_&]:px-8 cpk:[div[data-popup-chat]_&]:px-6",
7664
+ className: "cpk:px-4 cpk:@3xl:px-0 cpk:[div[data-sidebar-chat]_&]:px-8 cpk:[div[data-popup-chat]_&]:px-6",
7405
7665
  children
7406
7666
  }),
7407
7667
  BoundFeather,
@@ -7621,7 +7881,7 @@ function CopilotChat({ agentId, threadId, labels, chatView, isModalDefaultOpen,
7621
7881
  const existingConfig = useCopilotChatConfiguration();
7622
7882
  const resolvedAgentId = agentId ?? existingConfig?.agentId ?? DEFAULT_AGENT_ID;
7623
7883
  const providedThreadId = threadId ?? existingConfig?.threadId;
7624
- const resolvedThreadId = useMemo(() => providedThreadId ?? randomUUID(), [providedThreadId]);
7884
+ const resolvedThreadId = useMemo(() => providedThreadId ?? randomUUID$1(), [providedThreadId]);
7625
7885
  const hasExplicitThreadId = !!threadId || !!existingConfig?.hasExplicitThreadId;
7626
7886
  const { agent } = useAgent({
7627
7887
  agentId: resolvedAgentId,
@@ -7665,9 +7925,17 @@ function CopilotChat({ agentId, threadId, labels, chatView, isModalDefaultOpen,
7665
7925
  const { messageView: providedMessageView, suggestionView: providedSuggestionView, onStop: providedStopHandler, ...restProps } = props;
7666
7926
  const [lastConnectedThreadId, setLastConnectedThreadId] = useState(null);
7667
7927
  const isConnecting = hasExplicitThreadId && lastConnectedThreadId !== resolvedThreadId;
7928
+ const previousThreadIdRef = useRef(null);
7929
+ const hasExplicitThreadIdRef = useRef(hasExplicitThreadId);
7930
+ hasExplicitThreadIdRef.current = hasExplicitThreadId;
7668
7931
  useEffect(() => {
7932
+ const threadChanged = previousThreadIdRef.current !== resolvedThreadId;
7933
+ previousThreadIdRef.current = resolvedThreadId;
7669
7934
  agent.threadId = resolvedThreadId;
7670
- if (!hasExplicitThreadId) return;
7935
+ if (!hasExplicitThreadId) {
7936
+ if (threadChanged && agent.messages.length > 0) agent.setMessages([]);
7937
+ return;
7938
+ }
7671
7939
  let detached = false;
7672
7940
  const connectAbortController = new AbortController();
7673
7941
  if (agent instanceof HttpAgent) agent.abortController = connectAbortController;
@@ -7681,6 +7949,7 @@ function CopilotChat({ agentId, threadId, labels, chatView, isModalDefaultOpen,
7681
7949
  if (!detached) (typeof requestAnimationFrame === "function" ? requestAnimationFrame : (cb) => setTimeout(cb, 16))(() => {
7682
7950
  if (!detached) setLastConnectedThreadId(resolvedThreadId);
7683
7951
  });
7952
+ else if (!hasExplicitThreadIdRef.current) agentToConnect.setMessages([]);
7684
7953
  }
7685
7954
  };
7686
7955
  connect(agent);
@@ -7696,8 +7965,10 @@ function CopilotChat({ agentId, threadId, labels, chatView, isModalDefaultOpen,
7696
7965
  hasExplicitThreadId
7697
7966
  ]);
7698
7967
  const waitForActiveRunToSettle = useCallback(async () => {
7699
- if (agent.isRunning && isRunCompletionAware(agent) && agent.activeRunCompletionPromise) try {
7700
- await agent.activeRunCompletionPromise;
7968
+ const maybeAware = agent;
7969
+ const activeRunCompletionPromise = isRunCompletionAware(maybeAware) ? maybeAware.activeRunCompletionPromise : void 0;
7970
+ if (agent.isRunning && activeRunCompletionPromise) try {
7971
+ await activeRunCompletionPromise;
7701
7972
  } catch (error) {
7702
7973
  console.error("CopilotChat: in-flight run rejected while queuing send", error);
7703
7974
  }
@@ -7732,12 +8003,12 @@ function CopilotChat({ agentId, threadId, labels, chatView, isModalDefaultOpen,
7732
8003
  }
7733
8004
  });
7734
8005
  agent.addMessage({
7735
- id: randomUUID(),
8006
+ id: randomUUID$1(),
7736
8007
  role: "user",
7737
8008
  content: contentParts
7738
8009
  });
7739
8010
  } else agent.addMessage({
7740
- id: randomUUID(),
8011
+ id: randomUUID$1(),
7741
8012
  role: "user",
7742
8013
  content: value
7743
8014
  });
@@ -7754,7 +8025,7 @@ function CopilotChat({ agentId, threadId, labels, chatView, isModalDefaultOpen,
7754
8025
  const handleSelectSuggestion = useCallback(async (suggestion) => {
7755
8026
  await waitForActiveRunToSettle();
7756
8027
  agent.addMessage({
7757
- id: randomUUID(),
8028
+ id: randomUUID$1(),
7758
8029
  role: "user",
7759
8030
  content: suggestion.message
7760
8031
  });
@@ -8029,18 +8300,46 @@ CopilotChatToggleButton.displayName = "CopilotChatToggleButton";
8029
8300
 
8030
8301
  //#endregion
8031
8302
  //#region src/v2/components/chat/CopilotModalHeader.tsx
8032
- function CopilotModalHeader({ title, titleContent, closeButton, children, className, ...rest }) {
8303
+ /**
8304
+ * Reactively tracks whether the viewport is in the mobile range (≤767px) — the
8305
+ * same breakpoint the drawer + chat coordination use. SSR-safe: starts `false`
8306
+ * (desktop) so the server render and first client render agree, then syncs on
8307
+ * mount and on resize.
8308
+ */
8309
+ function useIsMobileViewport() {
8310
+ const [isMobile, setIsMobile] = useState(false);
8311
+ useEffect(() => {
8312
+ if (typeof window === "undefined" || typeof window.matchMedia !== "function") return;
8313
+ const mql = window.matchMedia("(max-width: 767px)");
8314
+ const update = () => setIsMobile(mql.matches);
8315
+ update();
8316
+ mql.addEventListener("change", update);
8317
+ return () => mql.removeEventListener("change", update);
8318
+ }, []);
8319
+ return isMobile;
8320
+ }
8321
+ function CopilotModalHeader({ title, titleContent, closeButton, drawerLauncher, children, className, ...rest }) {
8033
8322
  const configuration = useCopilotChatConfiguration();
8034
8323
  const fallbackTitle = configuration?.labels.modalHeaderTitle ?? CopilotChatDefaultLabels.modalHeaderTitle;
8035
8324
  const resolvedTitle = title ?? fallbackTitle;
8325
+ const isMobile = useIsMobileViewport();
8326
+ const drawerRegistered = (configuration?.drawerRegistered ?? false) && isMobile;
8036
8327
  const handleClose = useCallback(() => {
8037
8328
  configuration?.setModalOpen?.(false);
8038
8329
  }, [configuration]);
8330
+ const handleToggleDrawer = useCallback(() => {
8331
+ configuration?.setDrawerOpen?.(!configuration.drawerOpen);
8332
+ }, [configuration]);
8039
8333
  const BoundTitle = renderSlot(titleContent, CopilotModalHeader.Title, { children: resolvedTitle });
8040
8334
  const BoundCloseButton = renderSlot(closeButton, CopilotModalHeader.CloseButton, { onClick: handleClose });
8335
+ const BoundDrawerLauncher = drawerRegistered ? renderSlot(drawerLauncher, CopilotModalHeader.DrawerLauncher, {
8336
+ onClick: handleToggleDrawer,
8337
+ "aria-expanded": configuration?.drawerOpen ?? false
8338
+ }) : null;
8041
8339
  if (children) return children({
8042
8340
  titleContent: BoundTitle,
8043
8341
  closeButton: BoundCloseButton,
8342
+ drawerLauncher: BoundDrawerLauncher,
8044
8343
  title: resolvedTitle,
8045
8344
  ...rest
8046
8345
  });
@@ -8053,8 +8352,8 @@ function CopilotModalHeader({ title, titleContent, closeButton, children, classN
8053
8352
  className: "cpk:flex cpk:w-full cpk:items-center cpk:gap-2",
8054
8353
  children: [
8055
8354
  /* @__PURE__ */ jsx("div", {
8056
- className: "cpk:flex-1",
8057
- "aria-hidden": "true"
8355
+ className: "cpk:flex cpk:flex-1 cpk:justify-start",
8356
+ children: BoundDrawerLauncher ?? /* @__PURE__ */ jsx("span", { "aria-hidden": "true" })
8058
8357
  }),
8059
8358
  /* @__PURE__ */ jsx("div", {
8060
8359
  className: "cpk:flex cpk:flex-1 cpk:justify-center cpk:text-center",
@@ -8087,9 +8386,21 @@ CopilotModalHeader.displayName = "CopilotModalHeader";
8087
8386
  "aria-hidden": "true"
8088
8387
  })
8089
8388
  });
8389
+ _CopilotModalHeader.DrawerLauncher = ({ className, ...props }) => /* @__PURE__ */ jsx("button", {
8390
+ type: "button",
8391
+ "data-testid": "copilot-threads-drawer-launcher",
8392
+ 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),
8393
+ "aria-label": "Open threads",
8394
+ ...props,
8395
+ children: /* @__PURE__ */ jsx(PanelLeftOpen, {
8396
+ className: "cpk:h-4 cpk:w-4",
8397
+ "aria-hidden": "true"
8398
+ })
8399
+ });
8090
8400
  })(CopilotModalHeader || (CopilotModalHeader = {}));
8091
8401
  CopilotModalHeader.Title.displayName = "CopilotModalHeader.Title";
8092
8402
  CopilotModalHeader.CloseButton.displayName = "CopilotModalHeader.CloseButton";
8403
+ CopilotModalHeader.DrawerLauncher.displayName = "CopilotModalHeader.DrawerLauncher";
8093
8404
 
8094
8405
  //#endregion
8095
8406
  //#region src/v2/components/chat/CopilotSidebarView.tsx
@@ -8447,6 +8758,322 @@ function CopilotPopup({ header, toggleButton, defaultOpen, width, height, clickO
8447
8758
  }
8448
8759
  CopilotPopup.displayName = "CopilotPopup";
8449
8760
 
8761
+ //#endregion
8762
+ //#region src/v2/components/chat/CopilotThreadsDrawer.tsx
8763
+ /**
8764
+ * Maps a {@link Thread} from {@link useThreads} to the element's
8765
+ * {@link DrawerThread} view shape. The shapes are structurally compatible; this
8766
+ * narrows to exactly the fields the element renders so the element never sees
8767
+ * platform-internal fields.
8768
+ */
8769
+ function toDrawerThread(thread) {
8770
+ return {
8771
+ id: thread.id,
8772
+ name: thread.name,
8773
+ archived: thread.archived,
8774
+ createdAt: thread.createdAt,
8775
+ updatedAt: thread.updatedAt,
8776
+ ...thread.lastRunAt !== void 0 ? { lastRunAt: thread.lastRunAt } : {}
8777
+ };
8778
+ }
8779
+ /** The chat input textarea's documented `data-testid`. */
8780
+ const CHAT_INPUT_TESTID = "copilot-chat-textarea";
8781
+ /** The chat view container's documented `data-testid`. */
8782
+ const CHAT_CONTAINER_TESTID = "copilot-chat";
8783
+ /**
8784
+ * Returns the chat input element for focus-return after a thread is selected.
8785
+ *
8786
+ * Best-effort and SCOPED: walks up from the drawer element looking for an
8787
+ * ancestor that contains a chat-view container (`data-testid="copilot-chat"`),
8788
+ * then returns the chat input within that subtree. This avoids focusing the
8789
+ * wrong composer on a page hosting more than one chat (multi-chat dashboards),
8790
+ * where a document-global lookup would grab whichever input appears first in
8791
+ * DOM order rather than the one this drawer drives.
8792
+ *
8793
+ * Falls back to a document-global lookup when no scoping ancestor is found
8794
+ * (e.g. the drawer and chat share no common container, or headless usage),
8795
+ * and returns `null` when there is no chat input at all.
8796
+ *
8797
+ * @param origin - The drawer element to scope the search from.
8798
+ */
8799
+ function findChatInput(origin) {
8800
+ if (typeof document === "undefined") return null;
8801
+ const container = origin?.closest?.(`[data-testid="${CHAT_CONTAINER_TESTID}"]`);
8802
+ if (container) {
8803
+ const scoped = container.querySelector(`[data-testid="${CHAT_INPUT_TESTID}"]`);
8804
+ if (scoped) return scoped;
8805
+ }
8806
+ return document.querySelector(`[data-testid="${CHAT_INPUT_TESTID}"]`);
8807
+ }
8808
+ /**
8809
+ * React wrapper for the shadow-DOM `<copilotkit-threads-drawer>` threads drawer.
8810
+ *
8811
+ * Responsibilities:
8812
+ * - Registers the custom element on the client (SSR-safe; nothing renders
8813
+ * during prerender to avoid hydration mismatch).
8814
+ * - Feeds the element domain data: `threads`, `loading`, `error`,
8815
+ * `activeThreadId`, `licensed`, fetch-more state.
8816
+ * - Routes the element's nine outbound events to core thread operations
8817
+ * ({@link useThreads}) and chat-configuration changes.
8818
+ * - Registers with the surrounding chat configuration so the header
8819
+ * thread-list launcher appears, and binds the element `open` state to the
8820
+ * configuration's `drawerOpen`.
8821
+ *
8822
+ * License gating is two-pronged: the locked view shows when no license is configured
8823
+ * (the runtime reported no license status) OR the `threads` feature is
8824
+ * explicitly unlicensed. While unlicensed, the thread fetch is skipped entirely
8825
+ * so an unlicensed drawer issues no network requests.
8826
+ *
8827
+ * Thread switching needs no host wiring: when `onThreadSelect`/`onNewThread`
8828
+ * are omitted, the wrapper drives the surrounding chat configuration directly
8829
+ * ({@link CopilotChatConfigurationValue.setActiveThreadId} /
8830
+ * {@link CopilotChatConfigurationValue.startNewThread}), so a bare drawer
8831
+ * connects to the picked thread and shows the welcome screen on "+ New". Pass
8832
+ * the callbacks only to take control yourself.
8833
+ *
8834
+ * @example
8835
+ * ```tsx
8836
+ * // Callback-free: the drawer drives the chat configuration itself.
8837
+ * <CopilotKitProvider runtimeUrl="/api/copilotkit" publicLicenseKey="ck_pub_...">
8838
+ * <CopilotChat />
8839
+ * <CopilotThreadsDrawer />
8840
+ * </CopilotKitProvider>
8841
+ * ```
8842
+ */
8843
+ function CopilotThreadsDrawer({ agentId, onThreadSelect, onNewThread, onLicensed, licenseUrl, renderRow, label, limit, "data-testid": dataTestId = "copilot-threads-drawer" }) {
8844
+ const configuration = useCopilotChatConfiguration();
8845
+ const { status, checkFeature } = useLicenseContext();
8846
+ const licensePresent = status === "valid" || status === "expiring";
8847
+ const featureLicensed = checkFeature("threads");
8848
+ const licensed = licensePresent && featureLicensed;
8849
+ const licensePending = status === null;
8850
+ const resolvedAgentId = agentId ?? configuration?.agentId ?? "default";
8851
+ const activeThreadId = configuration?.threadId ?? null;
8852
+ const { threads, isLoading, listError, hasMoreThreads, isFetchingMoreThreads, archiveThread, unarchiveThread, deleteThread, fetchMoreThreads, refetchThreads, startNewThread } = useThreads$1({
8853
+ agentId: resolvedAgentId,
8854
+ includeArchived: true,
8855
+ enabled: licensed,
8856
+ ...limit !== void 0 ? { limit } : {}
8857
+ });
8858
+ const drawerThreads = useMemo(() => threads.map(toDrawerThread), [threads]);
8859
+ const elementRef = useRef(null);
8860
+ const [mounted, setMounted] = useState(false);
8861
+ useEffect(() => {
8862
+ defineCopilotKitThreadsDrawer();
8863
+ setMounted(true);
8864
+ }, []);
8865
+ const registerDrawer = configuration?.registerDrawer;
8866
+ useEffect(() => {
8867
+ if (!registerDrawer) return;
8868
+ return registerDrawer();
8869
+ }, [registerDrawer]);
8870
+ const [localDrawerOpen, setLocalDrawerOpen] = useState(false);
8871
+ const drawerOpen = configuration ? configuration.drawerOpen : localDrawerOpen;
8872
+ const setDrawerOpen = configuration ? configuration.setDrawerOpen : setLocalDrawerOpen;
8873
+ const setActiveThreadId = configuration?.setActiveThreadId;
8874
+ const startNewThreadConfig = configuration?.startNewThread;
8875
+ const handleThreadSelected = useCallback((threadId) => {
8876
+ if (onThreadSelect) onThreadSelect(threadId);
8877
+ else setActiveThreadId?.(threadId, { explicit: true });
8878
+ findChatInput(elementRef.current)?.focus();
8879
+ }, [onThreadSelect, setActiveThreadId]);
8880
+ const handleNewThread = useCallback(() => {
8881
+ startNewThread();
8882
+ if (onNewThread) onNewThread();
8883
+ else startNewThreadConfig?.();
8884
+ }, [
8885
+ startNewThread,
8886
+ onNewThread,
8887
+ startNewThreadConfig
8888
+ ]);
8889
+ const handleArchive = useCallback((threadId) => {
8890
+ archiveThread(threadId).catch((err) => {
8891
+ console.error("CopilotThreadsDrawer: archiveThread failed", err);
8892
+ });
8893
+ }, [archiveThread]);
8894
+ const handleUnarchive = useCallback((threadId) => {
8895
+ unarchiveThread(threadId).catch((err) => {
8896
+ console.error("CopilotThreadsDrawer: unarchiveThread failed", err);
8897
+ });
8898
+ }, [unarchiveThread]);
8899
+ const handleDelete = useCallback((threadId) => {
8900
+ const isActive = threadId === activeThreadId;
8901
+ deleteThread(threadId).then(() => {
8902
+ if (isActive) {
8903
+ startNewThread();
8904
+ if (onNewThread) onNewThread();
8905
+ else startNewThreadConfig?.();
8906
+ }
8907
+ }).catch((err) => {
8908
+ console.error("CopilotThreadsDrawer: deleteThread failed", err);
8909
+ });
8910
+ }, [
8911
+ deleteThread,
8912
+ activeThreadId,
8913
+ startNewThread,
8914
+ onNewThread,
8915
+ startNewThreadConfig
8916
+ ]);
8917
+ const handleFilterChange = useCallback(() => {
8918
+ refetchThreads();
8919
+ }, [refetchThreads]);
8920
+ const handleRetry = useCallback((scope) => {
8921
+ if (scope === "fetch-more") fetchMoreThreads();
8922
+ else refetchThreads();
8923
+ }, [fetchMoreThreads, refetchThreads]);
8924
+ const handleOpenChange = useCallback((open) => {
8925
+ setDrawerOpen(open);
8926
+ }, [setDrawerOpen]);
8927
+ const handleLicensed = useCallback(() => {
8928
+ onLicensed?.();
8929
+ }, [onLicensed]);
8930
+ const handleLoadMore = useCallback(() => {
8931
+ fetchMoreThreads();
8932
+ }, [fetchMoreThreads]);
8933
+ const handlersRef = useRef({
8934
+ handleThreadSelected,
8935
+ handleNewThread,
8936
+ handleArchive,
8937
+ handleUnarchive,
8938
+ handleDelete,
8939
+ handleFilterChange,
8940
+ handleRetry,
8941
+ handleOpenChange,
8942
+ handleLicensed,
8943
+ handleLoadMore
8944
+ });
8945
+ handlersRef.current = {
8946
+ handleThreadSelected,
8947
+ handleNewThread,
8948
+ handleArchive,
8949
+ handleUnarchive,
8950
+ handleDelete,
8951
+ handleFilterChange,
8952
+ handleRetry,
8953
+ handleOpenChange,
8954
+ handleLicensed,
8955
+ handleLoadMore
8956
+ };
8957
+ useEffect(() => {
8958
+ const el = elementRef.current;
8959
+ if (!el) return;
8960
+ const onThreadSelected = (event) => {
8961
+ const detail = event.detail;
8962
+ handlersRef.current.handleThreadSelected(detail.threadId);
8963
+ };
8964
+ const onNewThreadEvent = () => handlersRef.current.handleNewThread();
8965
+ const onArchive = (event) => {
8966
+ const detail = event.detail;
8967
+ handlersRef.current.handleArchive(detail.threadId);
8968
+ };
8969
+ const onUnarchive = (event) => {
8970
+ const detail = event.detail;
8971
+ handlersRef.current.handleUnarchive(detail.threadId);
8972
+ };
8973
+ const onDelete = (event) => {
8974
+ const detail = event.detail;
8975
+ handlersRef.current.handleDelete(detail.threadId);
8976
+ };
8977
+ const onFilterChange = (_event) => {
8978
+ handlersRef.current.handleFilterChange();
8979
+ };
8980
+ const onOpenChangeEvent = (event) => {
8981
+ const detail = event.detail;
8982
+ handlersRef.current.handleOpenChange(detail.open);
8983
+ };
8984
+ const onRetry = (event) => {
8985
+ const detail = event.detail;
8986
+ handlersRef.current.handleRetry(detail.scope);
8987
+ };
8988
+ const onLicensedEvent = () => handlersRef.current.handleLicensed();
8989
+ const onLoadMore = () => handlersRef.current.handleLoadMore();
8990
+ el.addEventListener("thread-selected", onThreadSelected);
8991
+ el.addEventListener("new-thread", onNewThreadEvent);
8992
+ el.addEventListener("archive", onArchive);
8993
+ el.addEventListener("unarchive", onUnarchive);
8994
+ el.addEventListener("delete", onDelete);
8995
+ el.addEventListener("filter-change", onFilterChange);
8996
+ el.addEventListener("open-change", onOpenChangeEvent);
8997
+ el.addEventListener("retry", onRetry);
8998
+ el.addEventListener("licensed", onLicensedEvent);
8999
+ el.addEventListener("load-more", onLoadMore);
9000
+ return () => {
9001
+ el.removeEventListener("thread-selected", onThreadSelected);
9002
+ el.removeEventListener("new-thread", onNewThreadEvent);
9003
+ el.removeEventListener("archive", onArchive);
9004
+ el.removeEventListener("unarchive", onUnarchive);
9005
+ el.removeEventListener("delete", onDelete);
9006
+ el.removeEventListener("filter-change", onFilterChange);
9007
+ el.removeEventListener("open-change", onOpenChangeEvent);
9008
+ el.removeEventListener("retry", onRetry);
9009
+ el.removeEventListener("licensed", onLicensedEvent);
9010
+ el.removeEventListener("load-more", onLoadMore);
9011
+ };
9012
+ }, [mounted]);
9013
+ useEffect(() => {
9014
+ const el = elementRef.current;
9015
+ if (!el) return;
9016
+ el.threads = drawerThreads;
9017
+ }, [drawerThreads, mounted]);
9018
+ useEffect(() => {
9019
+ const el = elementRef.current;
9020
+ if (!el) return;
9021
+ el.loading = isLoading || licensePending;
9022
+ el.error = listError ? listError.message : null;
9023
+ el.activeThreadId = activeThreadId;
9024
+ el.licensed = licensed || licensePending;
9025
+ el.hasMore = hasMoreThreads;
9026
+ el.fetchingMore = isFetchingMoreThreads;
9027
+ }, [
9028
+ isLoading,
9029
+ listError,
9030
+ activeThreadId,
9031
+ licensed,
9032
+ licensePending,
9033
+ hasMoreThreads,
9034
+ isFetchingMoreThreads,
9035
+ mounted
9036
+ ]);
9037
+ useEffect(() => {
9038
+ const el = elementRef.current;
9039
+ if (!el) return;
9040
+ el.open = drawerOpen;
9041
+ }, [drawerOpen, mounted]);
9042
+ useEffect(() => {
9043
+ const el = elementRef.current;
9044
+ if (!el) return;
9045
+ if (label !== void 0) el.label = label;
9046
+ }, [label, mounted]);
9047
+ useEffect(() => {
9048
+ const el = elementRef.current;
9049
+ if (!el) return;
9050
+ if (licenseUrl !== void 0) el.licenseUrl = licenseUrl;
9051
+ }, [licenseUrl, mounted]);
9052
+ const rowChildren = useMemo(() => {
9053
+ if (!renderRow) return null;
9054
+ return drawerThreads.map((drawerThread) => {
9055
+ const fullThread = threads.find((t) => t.id === drawerThread.id);
9056
+ if (!fullThread) return null;
9057
+ const content = renderRow(fullThread);
9058
+ if (content === null || content === void 0) return null;
9059
+ return /* @__PURE__ */ jsx("div", {
9060
+ slot: `row:${drawerThread.id}`,
9061
+ children: content
9062
+ }, drawerThread.id);
9063
+ });
9064
+ }, [
9065
+ renderRow,
9066
+ drawerThreads,
9067
+ threads
9068
+ ]);
9069
+ if (!mounted) return null;
9070
+ return React.createElement(COPILOTKIT_THREADS_DRAWER_TAG, {
9071
+ ref: elementRef,
9072
+ "data-testid": dataTestId
9073
+ }, rowChildren);
9074
+ }
9075
+ CopilotThreadsDrawer.displayName = "CopilotThreadsDrawer";
9076
+
8450
9077
  //#endregion
8451
9078
  //#region src/v2/components/WildcardToolCallRender.tsx
8452
9079
  const WildcardToolCallRender = defineToolCallRenderer({
@@ -9675,7 +10302,7 @@ function useCoAgentStateRenders() {
9675
10302
  //#region src/context/threads-context.tsx
9676
10303
  const ThreadsContext = createContext(void 0);
9677
10304
  function ThreadsProvider({ children, threadId: explicitThreadId }) {
9678
- const [internalThreadId, setInternalThreadId] = useState(() => randomUUID());
10305
+ const [internalThreadId, setInternalThreadId] = useState(() => randomUUID$1());
9679
10306
  const [internalIsExplicit, setInternalIsExplicit] = useState(false);
9680
10307
  const threadId = explicitThreadId ?? internalThreadId;
9681
10308
  const isThreadIdExplicit = explicitThreadId != null || internalIsExplicit;
@@ -10136,6 +10763,7 @@ function CopilotKit({ children, ...props }) {
10136
10763
  const showInspector = shouldShowDevConsole(props.enableInspector);
10137
10764
  const publicApiKey = props.publicApiKey || props.publicLicenseKey;
10138
10765
  const renderArr = useMemo(() => [{ render: CoAgentStateRenderBridge }], []);
10766
+ const { onError: _onError, ...v2Props } = props;
10139
10767
  return /* @__PURE__ */ jsx(ToastProvider, {
10140
10768
  enabled,
10141
10769
  children: /* @__PURE__ */ jsx(CopilotErrorBoundary, {
@@ -10144,7 +10772,7 @@ function CopilotKit({ children, ...props }) {
10144
10772
  children: /* @__PURE__ */ jsx(ThreadsProvider, {
10145
10773
  threadId: props.threadId,
10146
10774
  children: /* @__PURE__ */ jsx(CopilotKitProvider, {
10147
- ...props,
10775
+ ...v2Props,
10148
10776
  showDevConsole: showInspector,
10149
10777
  renderCustomMessages: renderArr,
10150
10778
  useSingleEndpoint: props.useSingleEndpoint ?? true,
@@ -10443,7 +11071,7 @@ function CopilotKitInternal(cpkProps) {
10443
11071
  });
10444
11072
  }, [setAuthStates]);
10445
11073
  const handleSetRegisteredActions = useCallback((actionConfig) => {
10446
- const key = actionConfig.action.name || randomUUID();
11074
+ const key = actionConfig.action.name || randomUUID$1();
10447
11075
  setRegisteredActionConfigs((prev) => {
10448
11076
  const newMap = new Map(prev);
10449
11077
  newMap.set(key, actionConfig);
@@ -10628,5 +11256,5 @@ function validateProps(props) {
10628
11256
  }
10629
11257
 
10630
11258
  //#endregion
10631
- export { useAgent as $, INTELLIGENCE_TURN_HEAD as A, CopilotChatToolCallsView as B, CopilotChatToggleButton as C, useCopilotChatConfiguration as Ct, CopilotChatView_default as D, CopilotChat as E, CopilotChatSuggestionPill as F, useLearnFromUserAction as G, useLearningContainers as H, CopilotChatReasoningMessage_default as I, useConfigureSuggestions as J, useThreads$1 as K, CopilotChatUserMessage_default as L, getIntelligenceTurnAnchors as M, IntelligenceIndicatorView as N, CopilotChatAttachmentQueue as O, CopilotChatSuggestionView as P, UseAgentUpdate as Q, CopilotChatAttachmentRenderer as R, CopilotModalHeader as S, CopilotChatConfigurationProvider as St, DefaultOpenIcon as T, useAttachments as U, useLearningContainersInCurrentThread as V, useLearnFromUserActionInCurrentThread as W, useAgentContext as X, useSuggestions as Y, useCapabilities as Z, WildcardToolCallRender as _, useCopilotKit as _t, ThreadsProvider as a, CopilotKitProvider as at, CopilotPopupView as b, AudioRecorderError as bt, CoAgentStateRendersProvider as c, useSandboxFunctions as ct, shouldShowDevConsole as d, MCPAppsActivityType as dt, useHumanInTheLoop as et, useToast as f, CopilotKitInspector as ft, useCopilotContext as g, defineToolCallRenderer as gt, CopilotContext as h, useRenderTool as ht, ThreadsContext as i, useRenderCustomMessages as it, IntelligenceIndicator as j, CopilotChatMessageView as k, useCoAgentStateRenders as l, MCPAppsActivityContentSchema as lt, useCopilotMessagesContext as m, useDefaultRenderTool as mt, defaultCopilotContextCategories as n, useFrontendTool as nt, useThreads as o, createA2UIMessageRenderer as ot, CopilotMessagesContext as p, useRenderToolCall as pt, useInterrupt as q, CoAgentStateRenderBridge as r, useRenderActivityMessage as rt, CoAgentStateRendersContext as s, SandboxFunctionsContext as st, CopilotKit as t, useComponent as tt, useAsyncCallback as u, MCPAppsActivityRenderer as ut, CopilotPopup as v, CopilotKitCoreReact as vt, DefaultCloseIcon as w, CopilotSidebarView as x, CopilotChatAudioRecorder as xt, CopilotSidebar as y, CopilotChatInput_default as yt, CopilotChatAssistantMessage_default as z };
10632
- //# sourceMappingURL=copilotkit-UY-H6Kx7.mjs.map
11259
+ export { UseAgentUpdate as $, CopilotChatMessageView as A, CopilotChatAssistantMessage_default as B, CopilotModalHeader as C, CopilotChatConfigurationProvider as Ct, CopilotChat as D, DefaultOpenIcon as E, CopilotChatSuggestionView as F, useLearnFromUserActionInCurrentThread as G, useLearningContainersInCurrentThread as H, CopilotChatSuggestionPill as I, useInterrupt as J, useLearnFromUserAction as K, CopilotChatReasoningMessage_default as L, IntelligenceIndicator as M, getIntelligenceTurnAnchors as N, CopilotChatView_default as O, IntelligenceIndicatorView as P, useCapabilities as Q, CopilotChatUserMessage_default as R, CopilotSidebarView as S, CopilotChatAudioRecorder as St, DefaultCloseIcon as T, useLearningContainers as U, CopilotChatToolCallsView as V, useAttachments as W, useSuggestions as X, useConfigureSuggestions as Y, useAgentContext as Z, WildcardToolCallRender as _, useRenderToolCall as _t, ThreadsProvider as a, useFrontendTool as at, CopilotSidebar as b, CopilotChatInput_default as bt, CoAgentStateRendersProvider as c, CopilotKitProvider as ct, shouldShowDevConsole as d, SandboxFunctionsContext as dt, useAgent as et, useToast as f, useSandboxFunctions as ft, useCopilotContext as g, CopilotKitInspector as gt, CopilotContext as h, MCPAppsActivityType as ht, ThreadsContext as i, useComponent as it, INTELLIGENCE_TURN_HEAD as j, CopilotChatAttachmentQueue as k, useCoAgentStateRenders as l, defineToolCallRenderer as lt, useCopilotMessagesContext as m, MCPAppsActivityRenderer as mt, defaultCopilotContextCategories as n, useDefaultRenderTool as nt, useThreads as o, useRenderActivityMessage as ot, CopilotMessagesContext as p, MCPAppsActivityContentSchema as pt, useThreads$1 as q, CoAgentStateRenderBridge as r, useRenderTool as rt, CoAgentStateRendersContext as s, useRenderCustomMessages as st, CopilotKit as t, useHumanInTheLoop as tt, useAsyncCallback as u, createA2UIMessageRenderer as ut, CopilotThreadsDrawer as v, useCopilotKit as vt, CopilotChatToggleButton as w, useCopilotChatConfiguration as wt, CopilotPopupView as x, AudioRecorderError as xt, CopilotPopup as y, CopilotKitCoreReact as yt, CopilotChatAttachmentRenderer as z };
11260
+ //# sourceMappingURL=copilotkit-BtRkFsNR.mjs.map