@copilotkit/react-core 1.61.2 → 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 (34) hide show
  1. package/dist/{copilotkit-Be-o2UaU.mjs → copilotkit-BtRkFsNR.mjs} +941 -404
  2. package/dist/copilotkit-BtRkFsNR.mjs.map +1 -0
  3. package/dist/{copilotkit-CTCjVxkH.cjs → copilotkit-CdpDZi3i.cjs} +943 -400
  4. package/dist/copilotkit-CdpDZi3i.cjs.map +1 -0
  5. package/dist/{copilotkit-ClqbUuGX.d.cts → copilotkit-Cga6AHO0.d.cts} +429 -149
  6. package/dist/copilotkit-Cga6AHO0.d.cts.map +1 -0
  7. package/dist/{copilotkit-Bpt1c_-q.d.mts → copilotkit-DnLpJ2Cl.d.mts} +429 -149
  8. package/dist/copilotkit-DnLpJ2Cl.d.mts.map +1 -0
  9. package/dist/index.cjs +1 -1
  10. package/dist/index.d.cts +1 -1
  11. package/dist/index.d.mts +1 -1
  12. package/dist/index.mjs +1 -1
  13. package/dist/index.umd.js +193 -176
  14. package/dist/index.umd.js.map +1 -1
  15. package/dist/v2/headless.cjs +156 -11
  16. package/dist/v2/headless.cjs.map +1 -1
  17. package/dist/v2/headless.d.cts +111 -1
  18. package/dist/v2/headless.d.cts.map +1 -1
  19. package/dist/v2/headless.d.mts +111 -1
  20. package/dist/v2/headless.d.mts.map +1 -1
  21. package/dist/v2/headless.mjs +157 -12
  22. package/dist/v2/headless.mjs.map +1 -1
  23. package/dist/v2/index.cjs +2 -1
  24. package/dist/v2/index.css +1 -1
  25. package/dist/v2/index.d.cts +2 -2
  26. package/dist/v2/index.d.mts +2 -2
  27. package/dist/v2/index.mjs +2 -2
  28. package/dist/v2/index.umd.js +949 -412
  29. package/dist/v2/index.umd.js.map +1 -1
  30. package/package.json +8 -6
  31. package/dist/copilotkit-Be-o2UaU.mjs.map +0 -1
  32. package/dist/copilotkit-Bpt1c_-q.d.mts.map +0 -1
  33. package/dist/copilotkit-CTCjVxkH.cjs.map +0 -1
  34. package/dist/copilotkit-ClqbUuGX.d.cts.map +0 -1
@@ -1,10 +1,10 @@
1
1
  "use client";
2
2
 
3
3
  (function(global, factory) {
4
- typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('./index.css'), require('@copilotkit/core'), require('@ag-ui/client'), require('react'), require('tailwind-merge'), require('lucide-react'), require('@copilotkit/shared'), require('react/jsx-runtime'), require('@radix-ui/react-slot'), require('class-variance-authority'), require('clsx'), require('@radix-ui/react-tooltip'), require('@radix-ui/react-dropdown-menu'), require('streamdown'), require('zod'), require('@lit-labs/react'), require('@copilotkit/a2ui-renderer'), require('zod-to-json-schema'), require('react-dom'), require('@tanstack/react-virtual'), require('use-stick-to-bottom')) :
5
- typeof define === 'function' && define.amd ? define(['exports', './index.css', '@copilotkit/core', '@ag-ui/client', 'react', 'tailwind-merge', 'lucide-react', '@copilotkit/shared', 'react/jsx-runtime', '@radix-ui/react-slot', 'class-variance-authority', 'clsx', '@radix-ui/react-tooltip', '@radix-ui/react-dropdown-menu', 'streamdown', 'zod', '@lit-labs/react', '@copilotkit/a2ui-renderer', 'zod-to-json-schema', 'react-dom', '@tanstack/react-virtual', 'use-stick-to-bottom'], factory) :
6
- (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory((global.CopilotKitReactCoreV2 = {}), global.src_v2_index_css,global.CopilotKitCore,global.AgUIClient,global.React,global.tailwindMerge,global.lucideReact,global.CopilotKitShared,global.ReactJsxRuntime,global.RadixReactSlot,global.classVarianceAuthority,global.clsx,global.RadixReactTooltip,global.RadixReactDropdownMenu,global.streamdown,global.Zod,global.LitLabsReact,global.CopilotKitA2UIRenderer,global.zod_to_json_schema,global.ReactDOM,global._tanstack_react_virtual,global.useStickToBottom));
7
- })(this, function(exports, src_v2_index_css, _copilotkit_core, _ag_ui_client, react, tailwind_merge, lucide_react, _copilotkit_shared, react_jsx_runtime, _radix_ui_react_slot, class_variance_authority, clsx, _radix_ui_react_tooltip, _radix_ui_react_dropdown_menu, streamdown, zod, _lit_labs_react, _copilotkit_a2ui_renderer, zod_to_json_schema, react_dom, _tanstack_react_virtual, use_stick_to_bottom) {
4
+ typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('./index.css'), require('@copilotkit/core'), require('@ag-ui/client'), require('react'), require('tailwind-merge'), require('lucide-react'), require('@copilotkit/shared'), require('react/jsx-runtime'), require('@radix-ui/react-slot'), require('class-variance-authority'), require('clsx'), require('@radix-ui/react-tooltip'), require('@radix-ui/react-dropdown-menu'), require('streamdown'), require('zod'), require('@lit-labs/react'), require('@copilotkit/a2ui-renderer'), require('zod-to-json-schema'), require('react-dom'), require('@tanstack/react-virtual'), require('use-stick-to-bottom'), require('@copilotkit/web-components/threads-drawer')) :
5
+ typeof define === 'function' && define.amd ? define(['exports', './index.css', '@copilotkit/core', '@ag-ui/client', 'react', 'tailwind-merge', 'lucide-react', '@copilotkit/shared', 'react/jsx-runtime', '@radix-ui/react-slot', 'class-variance-authority', 'clsx', '@radix-ui/react-tooltip', '@radix-ui/react-dropdown-menu', 'streamdown', 'zod', '@lit-labs/react', '@copilotkit/a2ui-renderer', 'zod-to-json-schema', 'react-dom', '@tanstack/react-virtual', 'use-stick-to-bottom', '@copilotkit/web-components/threads-drawer'], factory) :
6
+ (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory((global.CopilotKitReactCoreV2 = {}), global.src_v2_index_css,global.CopilotKitCore,global.AgUIClient,global.React,global.tailwindMerge,global.lucideReact,global.CopilotKitShared,global.ReactJsxRuntime,global.RadixReactSlot,global.classVarianceAuthority,global.clsx,global.RadixReactTooltip,global.RadixReactDropdownMenu,global.streamdown,global.Zod,global.LitLabsReact,global.CopilotKitA2UIRenderer,global.zod_to_json_schema,global.ReactDOM,global._tanstack_react_virtual,global.useStickToBottom,global._copilotkit_web_components_threads_drawer));
7
+ })(this, function(exports, src_v2_index_css, _copilotkit_core, _ag_ui_client, react, tailwind_merge, lucide_react, _copilotkit_shared, react_jsx_runtime, _radix_ui_react_slot, class_variance_authority, clsx, _radix_ui_react_tooltip, _radix_ui_react_dropdown_menu, streamdown, zod, _lit_labs_react, _copilotkit_a2ui_renderer, zod_to_json_schema, react_dom, _tanstack_react_virtual, use_stick_to_bottom, _copilotkit_web_components_threads_drawer) {
8
8
  Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
9
9
  //#region \0rolldown/runtime.js
10
10
  var __create = Object.create;
@@ -160,6 +160,25 @@ _radix_ui_react_dropdown_menu = __toESM(_radix_ui_react_dropdown_menu);
160
160
  modalHeaderTitle: "CopilotKit Chat",
161
161
  welcomeMessageText: "How can I help you today?"
162
162
  };
163
+ /**
164
+ * Mobile breakpoint below which the chat modal and the thread-list drawer are
165
+ * mutually exclusive. At or above this width both surfaces may coexist. This
166
+ * mirrors the `(max-width: 767px)` / `(min-width: 768px)` split already used by
167
+ * CopilotChatInput and CopilotSidebarView.
168
+ */
169
+ const MOBILE_MAX_WIDTH_PX = 767;
170
+ /**
171
+ * Reports whether the current viewport is in the mobile range (`<768px`), where
172
+ * the chat modal and drawer must not be open simultaneously. SSR-safe and
173
+ * defensive against environments without `matchMedia` (treated as desktop, so
174
+ * no mutual-exclusion constraint is applied).
175
+ *
176
+ * @returns `true` when the viewport is mobile-width, `false` otherwise.
177
+ */
178
+ function isMobileViewport() {
179
+ if (typeof window === "undefined" || typeof window.matchMedia !== "function") return false;
180
+ return window.matchMedia(`(max-width: ${MOBILE_MAX_WIDTH_PX}px)`).matches;
181
+ }
163
182
  const CopilotChatConfiguration = (0, react.createContext)(null);
164
183
  const CopilotChatConfigurationProvider = ({ children, labels, agentId, threadId, hasExplicitThreadId, isModalDefaultOpen }) => {
165
184
  const parentConfig = (0, react.useContext)(CopilotChatConfiguration);
@@ -170,12 +189,22 @@ _radix_ui_react_dropdown_menu = __toESM(_radix_ui_react_dropdown_menu);
170
189
  ...stableLabels
171
190
  }), [stableLabels, parentConfig?.labels]);
172
191
  const resolvedAgentId = agentId ?? parentConfig?.agentId ?? _copilotkit_shared.DEFAULT_AGENT_ID;
192
+ const threadIdPropIsAuthoritative = threadId !== void 0 && hasExplicitThreadId !== false;
193
+ const isThreadIdControlled = threadIdPropIsAuthoritative;
194
+ const [activeThreadOverride, setActiveThreadOverride] = (0, react.useState)(null);
173
195
  const resolvedThreadId = (0, react.useMemo)(() => {
174
- if (threadId) return threadId;
196
+ if (threadIdPropIsAuthoritative) return threadId;
197
+ if (activeThreadOverride) return activeThreadOverride.threadId;
175
198
  if (parentConfig?.threadId) return parentConfig.threadId;
199
+ if (threadId) return threadId;
176
200
  return (0, _copilotkit_shared.randomUUID)();
177
- }, [threadId, parentConfig?.threadId]);
178
- const resolvedHasExplicitThreadId = (hasExplicitThreadId !== void 0 ? hasExplicitThreadId : !!threadId) || !!parentConfig?.hasExplicitThreadId;
201
+ }, [
202
+ threadIdPropIsAuthoritative,
203
+ threadId,
204
+ parentConfig?.threadId,
205
+ activeThreadOverride
206
+ ]);
207
+ const resolvedHasExplicitThreadId = (threadIdPropIsAuthoritative ? true : activeThreadOverride?.explicit ?? hasExplicitThreadId ?? false) || !!parentConfig?.hasExplicitThreadId;
179
208
  const [internalModalOpen, setInternalModalOpen] = (0, react.useState)(isModalDefaultOpen ?? true);
180
209
  const hasExplicitDefault = isModalDefaultOpen !== void 0;
181
210
  const setAndSync = (0, react.useCallback)((open) => {
@@ -194,20 +223,113 @@ _radix_ui_react_dropdown_menu = __toESM(_radix_ui_react_dropdown_menu);
194
223
  }, [parentConfig?.isModalOpen, hasExplicitDefault]);
195
224
  const resolvedIsModalOpen = hasExplicitDefault ? internalModalOpen : parentConfig?.isModalOpen ?? internalModalOpen;
196
225
  const resolvedSetModalOpen = hasExplicitDefault ? setAndSync : parentConfig?.setModalOpen ?? setInternalModalOpen;
226
+ const [ownDrawerOpen, setOwnDrawerOpen] = (0, react.useState)(false);
227
+ const [ownDrawerCount, setOwnDrawerCount] = (0, react.useState)(0);
228
+ const modalCloseRef = (0, react.useRef)(() => {});
229
+ modalCloseRef.current = resolvedSetModalOpen;
230
+ const registeredModalClosersRef = (0, react.useRef)([]);
231
+ const ownRegisterModalCloser = (0, react.useCallback)((closeModal) => {
232
+ registeredModalClosersRef.current.push(closeModal);
233
+ return () => {
234
+ registeredModalClosersRef.current = registeredModalClosersRef.current.filter((entry) => entry !== closeModal);
235
+ };
236
+ }, []);
237
+ const ownSetDrawerOpen = (0, react.useCallback)((open) => {
238
+ setOwnDrawerOpen(open);
239
+ if (open && isMobileViewport()) {
240
+ const registered = registeredModalClosersRef.current;
241
+ (registered.length > 0 ? registered[registered.length - 1] : modalCloseRef.current)(false);
242
+ }
243
+ }, []);
244
+ const ownRegisterDrawer = (0, react.useCallback)(() => {
245
+ setOwnDrawerCount((count) => count + 1);
246
+ return () => {
247
+ setOwnDrawerCount((count) => Math.max(0, count - 1));
248
+ };
249
+ }, []);
250
+ const resolvedDrawerOpen = parentConfig ? parentConfig.drawerOpen : ownDrawerOpen;
251
+ const resolvedSetDrawerOpen = parentConfig ? parentConfig.setDrawerOpen : ownSetDrawerOpen;
252
+ const resolvedDrawerRegistered = parentConfig ? parentConfig.drawerRegistered : ownDrawerCount > 0;
253
+ const resolvedRegisterDrawer = parentConfig ? parentConfig.registerDrawer : ownRegisterDrawer;
254
+ const resolvedRegisterModalCloser = parentConfig ? parentConfig.ɵregisterModalCloser : ownRegisterModalCloser;
255
+ (0, react.useEffect)(() => {
256
+ if (!hasExplicitDefault) return;
257
+ return resolvedRegisterModalCloser(resolvedSetModalOpen);
258
+ }, [
259
+ hasExplicitDefault,
260
+ resolvedRegisterModalCloser,
261
+ resolvedSetModalOpen
262
+ ]);
263
+ const isThreadIdControlledRef = (0, react.useRef)(isThreadIdControlled);
264
+ isThreadIdControlledRef.current = isThreadIdControlled;
265
+ const ownSetActiveThreadId = (0, react.useCallback)((id, options) => {
266
+ setActiveThreadOverride({
267
+ threadId: id,
268
+ explicit: options?.explicit ?? true
269
+ });
270
+ }, []);
271
+ const ownStartNewThread = (0, react.useCallback)(() => {
272
+ setActiveThreadOverride({
273
+ threadId: (0, _copilotkit_shared.randomUUID)(),
274
+ explicit: false
275
+ });
276
+ }, []);
277
+ const parentSetActiveThreadId = parentConfig?.setActiveThreadId;
278
+ const parentStartNewThread = parentConfig?.startNewThread;
279
+ const resolvedSetActiveThreadId = (0, react.useCallback)((id, options) => {
280
+ if (isThreadIdControlledRef.current) {
281
+ console.warn("[CopilotKit] Ignoring setActiveThreadId(): threadId is controlled via the `threadId` prop on CopilotChatConfigurationProvider.");
282
+ return;
283
+ }
284
+ if (parentSetActiveThreadId) {
285
+ parentSetActiveThreadId(id, options);
286
+ return;
287
+ }
288
+ ownSetActiveThreadId(id, options);
289
+ }, [parentSetActiveThreadId, ownSetActiveThreadId]);
290
+ const resolvedStartNewThread = (0, react.useCallback)(() => {
291
+ if (isThreadIdControlledRef.current) {
292
+ console.warn("[CopilotKit] Ignoring startNewThread(): threadId is controlled via the `threadId` prop on CopilotChatConfigurationProvider.");
293
+ return;
294
+ }
295
+ if (parentStartNewThread) {
296
+ parentStartNewThread();
297
+ return;
298
+ }
299
+ ownStartNewThread();
300
+ }, [parentStartNewThread, ownStartNewThread]);
301
+ const setModalOpenWithDrawerExclusion = (0, react.useCallback)((open) => {
302
+ if (open && isMobileViewport()) resolvedSetDrawerOpen(false);
303
+ resolvedSetModalOpen(open);
304
+ }, [resolvedSetModalOpen, resolvedSetDrawerOpen]);
197
305
  const configurationValue = (0, react.useMemo)(() => ({
198
306
  labels: mergedLabels,
199
307
  agentId: resolvedAgentId,
200
308
  threadId: resolvedThreadId,
201
309
  hasExplicitThreadId: resolvedHasExplicitThreadId,
202
310
  isModalOpen: resolvedIsModalOpen,
203
- setModalOpen: resolvedSetModalOpen
311
+ setModalOpen: setModalOpenWithDrawerExclusion,
312
+ drawerOpen: resolvedDrawerOpen,
313
+ setDrawerOpen: resolvedSetDrawerOpen,
314
+ drawerRegistered: resolvedDrawerRegistered,
315
+ registerDrawer: resolvedRegisterDrawer,
316
+ ɵregisterModalCloser: resolvedRegisterModalCloser,
317
+ setActiveThreadId: resolvedSetActiveThreadId,
318
+ startNewThread: resolvedStartNewThread
204
319
  }), [
205
320
  mergedLabels,
206
321
  resolvedAgentId,
207
322
  resolvedThreadId,
208
323
  resolvedHasExplicitThreadId,
209
324
  resolvedIsModalOpen,
210
- resolvedSetModalOpen
325
+ setModalOpenWithDrawerExclusion,
326
+ resolvedDrawerOpen,
327
+ resolvedSetDrawerOpen,
328
+ resolvedDrawerRegistered,
329
+ resolvedRegisterDrawer,
330
+ resolvedRegisterModalCloser,
331
+ resolvedSetActiveThreadId,
332
+ resolvedStartNewThread
211
333
  ]);
212
334
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CopilotChatConfiguration.Provider, {
213
335
  value: configurationValue,
@@ -1128,7 +1250,7 @@ _radix_ui_react_dropdown_menu = __toESM(_radix_ui_react_dropdown_menu);
1128
1250
  },
1129
1251
  ...props,
1130
1252
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1131
- 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",
1253
+ 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",
1132
1254
  children: inputPill
1133
1255
  }), shouldShowDisclaimer && BoundDisclaimer]
1134
1256
  });
@@ -1398,325 +1520,43 @@ _radix_ui_react_dropdown_menu = __toESM(_radix_ui_react_dropdown_menu);
1398
1520
  *
1399
1521
  * Awaiting a zero-delay timeout yields to the macrotask queue. React's
1400
1522
  * MessageChannel task runs first, committing the pending state and running
1401
- * useLayoutEffect (which updates the context store). The follow-up runAgent
1402
- * call then reads fresh context.
1403
- */
1404
- async waitForPendingFrameworkUpdates() {
1405
- await new Promise((resolve) => setTimeout(resolve, 0));
1406
- }
1407
- };
1408
-
1409
- //#endregion
1410
- //#region src/v2/context.ts
1411
- const CopilotKitContext = (0, react.createContext)(null);
1412
- const useCopilotKit = () => {
1413
- const context = (0, react.useContext)(CopilotKitContext);
1414
- const [, forceUpdate] = (0, react.useReducer)((x) => x + 1, 0);
1415
- if (!context) throw new Error("useCopilotKit must be used within CopilotKitProvider");
1416
- (0, react.useEffect)(() => {
1417
- const subscription = context.copilotkit.subscribe({
1418
- onRuntimeConnectionStatusChanged: () => {
1419
- forceUpdate();
1420
- },
1421
- onHeadersChanged: () => {
1422
- forceUpdate();
1423
- }
1424
- });
1425
- return () => {
1426
- subscription.unsubscribe();
1427
- };
1428
- }, []);
1429
- return context;
1430
- };
1431
- const LicenseContext = (0, react.createContext)({
1432
- status: null,
1433
- license: null,
1434
- checkFeature: () => true,
1435
- getLimit: () => null
1436
- });
1437
- const useLicenseContext = () => (0, react.useContext)(LicenseContext);
1438
-
1439
- //#endregion
1440
- //#region src/v2/types/defineToolCallRenderer.ts
1441
- function defineToolCallRenderer(def) {
1442
- const argsSchema = def.name === "*" && !def.args ? zod.z.any() : def.args;
1443
- return {
1444
- name: def.name,
1445
- args: argsSchema,
1446
- render: def.render,
1447
- ...def.agentId ? { agentId: def.agentId } : {}
1448
- };
1449
- }
1450
-
1451
- //#endregion
1452
- //#region src/v2/hooks/use-render-tool.tsx
1453
- const EMPTY_DEPS$1 = [];
1454
- /**
1455
- * Registers a renderer entry in CopilotKit's `renderToolCalls` registry.
1456
- *
1457
- * Key behavior:
1458
- * - deduplicates by `agentId:name` (latest registration wins),
1459
- * - keeps renderer entries on cleanup so historical chat tool calls can still render,
1460
- * - refreshes registration when `deps` change.
1461
- *
1462
- * @typeParam S - Schema type describing tool call parameters.
1463
- * @param config - Renderer config for wildcard or named tools.
1464
- * @param deps - Optional dependencies to refresh registration.
1465
- *
1466
- * @example
1467
- * ```tsx
1468
- * useRenderTool(
1469
- * {
1470
- * name: "searchDocs",
1471
- * parameters: z.object({ query: z.string() }),
1472
- * render: ({ status, parameters, result }) => {
1473
- * if (status === "executing") return <div>Searching {parameters.query}</div>;
1474
- * if (status === "complete") return <div>{result}</div>;
1475
- * return <div>Preparing...</div>;
1476
- * },
1477
- * },
1478
- * [],
1479
- * );
1480
- * ```
1481
- *
1482
- * @example
1483
- * ```tsx
1484
- * useRenderTool(
1485
- * {
1486
- * name: "summarize",
1487
- * parameters: z.object({ text: z.string() }),
1488
- * agentId: "research-agent",
1489
- * render: ({ name, status }) => <div>{name}: {status}</div>,
1490
- * },
1491
- * [selectedAgentId],
1492
- * );
1493
- * ```
1494
- */
1495
- function useRenderTool(config, deps) {
1496
- const { copilotkit } = useCopilotKit();
1497
- const extraDeps = deps ?? EMPTY_DEPS$1;
1498
- (0, react.useEffect)(() => {
1499
- const renderer = config.name === "*" && !config.parameters ? defineToolCallRenderer({
1500
- name: "*",
1501
- render: (props) => config.render({
1502
- ...props,
1503
- parameters: props.args
1504
- }),
1505
- ...config.agentId ? { agentId: config.agentId } : {}
1506
- }) : defineToolCallRenderer({
1507
- name: config.name,
1508
- args: config.parameters,
1509
- render: (props) => {
1510
- if (props.status === _copilotkit_core.ToolCallStatus.InProgress) return config.render({
1511
- ...props,
1512
- parameters: props.args
1513
- });
1514
- if (props.status === _copilotkit_core.ToolCallStatus.Executing) return config.render({
1515
- ...props,
1516
- parameters: props.args
1517
- });
1518
- return config.render({
1519
- ...props,
1520
- parameters: props.args
1521
- });
1522
- },
1523
- ...config.agentId ? { agentId: config.agentId } : {}
1524
- });
1525
- copilotkit.addHookRenderToolCall(renderer);
1526
- }, [
1527
- config.name,
1528
- copilotkit,
1529
- JSON.stringify(extraDeps)
1530
- ]);
1531
- }
1532
-
1533
- //#endregion
1534
- //#region src/v2/hooks/use-default-render-tool.tsx
1535
- /**
1536
- * Module-level dedup set so an unknown status value only emits a console
1537
- * warning the FIRST time we encounter it. Otherwise a stuck/unmapped status
1538
- * would log on every re-render (potentially many per second).
1539
- */
1540
- const warnedUnknownStatuses = /* @__PURE__ */ new Set();
1541
- /**
1542
- * Map a {@link ToolCallStatus} enum value to the documented string-union
1543
- * status the {@link DefaultRenderProps} contract exposes. Unknown / future
1544
- * enum members log a warning (once per distinct value) and fall back to
1545
- * `"inProgress"`.
1546
- */
1547
- function mapToolCallStatus(status) {
1548
- switch (status) {
1549
- case _copilotkit_core.ToolCallStatus.Complete: return "complete";
1550
- case _copilotkit_core.ToolCallStatus.Executing: return "executing";
1551
- case _copilotkit_core.ToolCallStatus.InProgress: return "inProgress";
1552
- default: {
1553
- const key = String(status);
1554
- if (!warnedUnknownStatuses.has(key)) {
1555
- warnedUnknownStatuses.add(key);
1556
- console.warn(`[CopilotKit] Unknown ToolCallStatus "${key}" in default tool-call renderer; falling back to "inProgress".`);
1557
- }
1558
- return "inProgress";
1559
- }
1560
- }
1561
- }
1562
- /**
1563
- * Convert the framework-internal renderer props (`args`, enum status) into
1564
- * the documented {@link DefaultRenderProps} shape (`parameters`, string-union
1565
- * status) so a user `config.render` always sees the documented contract.
1566
- */
1567
- function adaptRendererProps(props) {
1568
- return {
1569
- name: props.name,
1570
- toolCallId: props.toolCallId,
1571
- parameters: props.args,
1572
- status: mapToolCallStatus(props.status),
1573
- result: props.result
1574
- };
1575
- }
1576
- /**
1577
- * Registers a wildcard (`"*"`) tool-call renderer via `useRenderTool`.
1578
- *
1579
- * - Call with no config to use CopilotKit's built-in default tool-call card.
1580
- * - Pass `config.render` to replace the default UI with your own fallback renderer.
1581
- *
1582
- * This is useful when you want a generic renderer for tools that do not have a
1583
- * dedicated `useRenderTool({ name: "..." })` registration.
1584
- *
1585
- * @param config - Optional custom wildcard render function.
1586
- * @param deps - Optional dependencies to refresh registration.
1587
- *
1588
- * @example
1589
- * ```tsx
1590
- * useDefaultRenderTool();
1591
- * ```
1592
- *
1593
- * @example
1594
- * ```tsx
1595
- * useDefaultRenderTool({
1596
- * render: ({ name, status }) => <div>{name}: {status}</div>,
1597
- * });
1598
- * ```
1599
- *
1600
- * @example
1601
- * ```tsx
1602
- * useDefaultRenderTool(
1603
- * {
1604
- * render: ({ name, result }) => (
1605
- * <ToolEventRow title={name} payload={result} compact={compactMode} />
1606
- * ),
1607
- * },
1608
- * [compactMode],
1609
- * );
1610
- * ```
1611
- */
1612
- function useDefaultRenderTool(config, deps) {
1613
- const userRender = config?.render;
1614
- useRenderTool({
1615
- name: "*",
1616
- render: userRender ? (raw) => userRender(adaptRendererProps(raw)) : (raw) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DefaultToolCallRenderer, { ...adaptRendererProps(raw) })
1617
- }, deps);
1618
- }
1619
- /**
1620
- * Guarded JSON.stringify used inside the expanded `<pre>` blocks. A circular
1621
- * reference would otherwise crash the entire React tree on render.
1622
- */
1623
- function safeStringifyForPre(value) {
1624
- try {
1625
- return JSON.stringify(value, null, 2);
1626
- } catch (err) {
1627
- console.warn("[CopilotKit] Failed to JSON.stringify tool-call payload for default renderer; falling back to String():", err);
1628
- try {
1629
- return String(value);
1630
- } catch (innerErr) {
1631
- console.warn("[CopilotKit] safeStringifyForPre: value could not be stringified:", innerErr);
1632
- return "[unserializable]";
1633
- }
1634
- }
1635
- }
1636
- function DefaultToolCallRenderer({ name, toolCallId, parameters, status, result }) {
1637
- const [isExpanded, setIsExpanded] = (0, react.useState)(false);
1638
- const isActive = status === "inProgress" || status === "executing";
1639
- const isComplete = status === "complete";
1640
- const statusLabel = isActive ? "Running" : isComplete ? "Done" : status;
1641
- const dotClassName = isActive ? "cpk:bg-amber-500" : isComplete ? "cpk:bg-emerald-500" : "cpk:bg-zinc-400";
1642
- 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";
1643
- return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1644
- "data-testid": "copilot-tool-render",
1645
- "data-tool-name": name,
1646
- "data-tool-call-id": toolCallId,
1647
- "data-status": status,
1648
- "data-args": safeStringifyForAttr(parameters),
1649
- "data-result": safeStringifyForAttr(result),
1650
- className: "cpk:mt-2 cpk:pb-2",
1651
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1652
- 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",
1653
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
1654
- type: "button",
1655
- "aria-expanded": isExpanded,
1656
- onClick: () => setIsExpanded(!isExpanded),
1657
- 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",
1658
- style: { font: "inherit" },
1659
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1660
- className: "cpk:flex cpk:min-w-0 cpk:items-center cpk:gap-2",
1661
- children: [
1662
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
1663
- 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" : ""}`,
1664
- fill: "none",
1665
- viewBox: "0 0 24 24",
1666
- strokeWidth: 2,
1667
- stroke: "currentColor",
1668
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
1669
- strokeLinecap: "round",
1670
- strokeLinejoin: "round",
1671
- d: "M8.25 4.5l7.5 7.5-7.5 7.5"
1672
- })
1673
- }),
1674
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: `cpk:inline-block cpk:h-2 cpk:w-2 cpk:flex-shrink-0 cpk:rounded-full ${dotClassName}` }),
1675
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1676
- "data-testid": "copilot-tool-render-name",
1677
- className: "cpk:truncate cpk:text-[13px] cpk:font-semibold cpk:text-zinc-900 cpk:dark:text-zinc-100",
1678
- children: name
1679
- })
1680
- ]
1681
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1682
- "data-testid": "copilot-tool-render-status",
1683
- 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}`,
1684
- children: statusLabel
1685
- })]
1686
- }), isExpanded && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1687
- className: "cpk:mt-3 cpk:grid cpk:gap-3",
1688
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1689
- className: "cpk:text-[10px] cpk:uppercase cpk:text-zinc-500 cpk:dark:text-zinc-400",
1690
- children: "Arguments"
1691
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("pre", {
1692
- 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",
1693
- children: safeStringifyForPre(parameters ?? {})
1694
- })] }), result !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1695
- className: "cpk:text-[10px] cpk:uppercase cpk:text-zinc-500 cpk:dark:text-zinc-400",
1696
- children: "Result"
1697
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("pre", {
1698
- 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",
1699
- children: typeof result === "string" ? result : safeStringifyForPre(result)
1700
- })] })]
1701
- })]
1702
- })
1703
- });
1704
- }
1705
- function safeStringifyForAttr(value) {
1706
- if (value === void 0 || value === null) return "";
1707
- if (typeof value === "string") return value;
1708
- try {
1709
- return JSON.stringify(value);
1710
- } catch (err) {
1711
- console.warn("[CopilotKit] Failed to JSON.stringify tool-call payload for data-* attribute; falling back to String():", err);
1712
- try {
1713
- return String(value);
1714
- } catch (innerErr) {
1715
- console.warn("[CopilotKit] safeStringifyForAttr: value could not be stringified:", innerErr);
1716
- return "";
1717
- }
1523
+ * useLayoutEffect (which updates the context store). The follow-up runAgent
1524
+ * call then reads fresh context.
1525
+ */
1526
+ async waitForPendingFrameworkUpdates() {
1527
+ await new Promise((resolve) => setTimeout(resolve, 0));
1718
1528
  }
1719
- }
1529
+ };
1530
+
1531
+ //#endregion
1532
+ //#region src/v2/context.ts
1533
+ const CopilotKitContext = (0, react.createContext)(null);
1534
+ const useCopilotKit = () => {
1535
+ const context = (0, react.useContext)(CopilotKitContext);
1536
+ const [, forceUpdate] = (0, react.useReducer)((x) => x + 1, 0);
1537
+ if (!context) throw new Error("useCopilotKit must be used within CopilotKitProvider");
1538
+ (0, react.useEffect)(() => {
1539
+ const subscription = context.copilotkit.subscribe({
1540
+ onRuntimeConnectionStatusChanged: () => {
1541
+ forceUpdate();
1542
+ },
1543
+ onHeadersChanged: () => {
1544
+ forceUpdate();
1545
+ }
1546
+ });
1547
+ return () => {
1548
+ subscription.unsubscribe();
1549
+ };
1550
+ }, []);
1551
+ return context;
1552
+ };
1553
+ const LicenseContext = (0, react.createContext)({
1554
+ status: null,
1555
+ license: null,
1556
+ checkFeature: () => true,
1557
+ getLimit: () => null
1558
+ });
1559
+ const useLicenseContext = () => (0, react.useContext)(LicenseContext);
1720
1560
 
1721
1561
  //#endregion
1722
1562
  //#region src/v2/hooks/use-render-tool-call.tsx
@@ -1772,10 +1612,13 @@ _radix_ui_react_dropdown_menu = __toESM(_radix_ui_react_dropdown_menu);
1772
1612
  }, () => copilotkit.renderToolCalls, () => copilotkit.renderToolCalls);
1773
1613
  return (0, react.useCallback)(({ toolCall, toolMessage }) => {
1774
1614
  const exactMatches = renderToolCalls.filter((rc) => rc.name === toolCall.function.name);
1615
+ const renderConfig = exactMatches.find((rc) => rc.agentId === agentId) || exactMatches.find((rc) => !rc.agentId) || exactMatches[0] || renderToolCalls.find((rc) => rc.name === "*");
1616
+ if (!renderConfig) return null;
1617
+ const RenderComponent = renderConfig.render;
1775
1618
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ToolCallRenderer, {
1776
1619
  toolCall,
1777
1620
  toolMessage,
1778
- RenderComponent: (exactMatches.find((rc) => rc.agentId === agentId) || exactMatches.find((rc) => !rc.agentId) || exactMatches[0] || renderToolCalls.find((rc) => rc.name === "*"))?.render ?? defaultToolCallRenderAdapter,
1621
+ RenderComponent,
1779
1622
  isExecuting: executingToolCallIds.has(toolCall.id)
1780
1623
  }, toolCall.id);
1781
1624
  }, [
@@ -1784,15 +1627,6 @@ _radix_ui_react_dropdown_menu = __toESM(_radix_ui_react_dropdown_menu);
1784
1627
  agentId
1785
1628
  ]);
1786
1629
  }
1787
- function defaultToolCallRenderAdapter(props) {
1788
- return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DefaultToolCallRenderer, {
1789
- name: props.name,
1790
- toolCallId: props.toolCallId,
1791
- parameters: props.args,
1792
- status: mapToolCallStatus(props.status),
1793
- result: props.result
1794
- });
1795
- }
1796
1630
 
1797
1631
  //#endregion
1798
1632
  //#region src/v2/components/CopilotKitInspector.tsx
@@ -3274,7 +3108,7 @@ window.parent.postMessage({jsonrpc:"2.0",method:"ui/notifications/sandbox-proxy-
3274
3108
  ...A2UILifecycleFields
3275
3109
  }).passthrough();
3276
3110
  function createA2UIMessageRenderer(options) {
3277
- const { theme, catalog, loadingComponent, recovery } = options;
3111
+ const { theme, catalog, loadingComponent, recovery, onAction } = options;
3278
3112
  const showAfterMs = recovery?.showAfterMs ?? 2e3;
3279
3113
  const showAfterAttempts = recovery?.showAfterAttempts ?? 2;
3280
3114
  const optionDebugExposure = recovery?.debugExposure ?? "collapsed";
@@ -3350,6 +3184,7 @@ window.parent.postMessage({jsonrpc:"2.0",method:"ui/notifications/sandbox-proxy-
3350
3184
  agent,
3351
3185
  copilotkit,
3352
3186
  catalog,
3187
+ onAction,
3353
3188
  onReady: markSurfaceReady
3354
3189
  }, surfaceId))
3355
3190
  });
@@ -3370,29 +3205,60 @@ window.parent.postMessage({jsonrpc:"2.0",method:"ui/notifications/sandbox-proxy-
3370
3205
  };
3371
3206
  }
3372
3207
  /**
3208
+ * Orchestrates a single A2UI user action: runs the optional `onAction`
3209
+ * interceptor first, then forwards to the agent unless the interceptor
3210
+ * suppressed it (returned `null`). Exported for unit testing; the wiring lives
3211
+ * in {@link ReactSurfaceHost}.
3212
+ */
3213
+ async function runA2UIAction({ message, agent, copilotkit, onAction }) {
3214
+ if (!agent) return;
3215
+ const action = message.userAction;
3216
+ const forward = async (forwardAction) => {
3217
+ const a2uiAction = forwardAction !== void 0 ? {
3218
+ ...message,
3219
+ userAction: forwardAction
3220
+ } : message;
3221
+ try {
3222
+ copilotkit.setProperties({
3223
+ ...copilotkit.properties,
3224
+ a2uiAction
3225
+ });
3226
+ await copilotkit.runAgent({ agent });
3227
+ } finally {
3228
+ if (copilotkit.properties) {
3229
+ const { a2uiAction: _omit, ...rest } = copilotkit.properties;
3230
+ copilotkit.setProperties(rest);
3231
+ }
3232
+ }
3233
+ };
3234
+ if (onAction && action) {
3235
+ const result = await onAction(action, forward);
3236
+ if (result === null) return;
3237
+ if (result) {
3238
+ await forward(result);
3239
+ return;
3240
+ }
3241
+ }
3242
+ await forward();
3243
+ }
3244
+ /**
3373
3245
  * Renders a single A2UI surface using the React renderer.
3374
3246
  * Wraps A2UIProvider + A2UIRenderer and bridges actions back to CopilotKit.
3375
3247
  */
3376
- function ReactSurfaceHost({ surfaceId, operations, theme, agent, copilotkit, catalog, onReady }) {
3248
+ function ReactSurfaceHost({ surfaceId, operations, theme, agent, copilotkit, catalog, onAction, onReady }) {
3377
3249
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3378
3250
  className: "cpk:flex cpk:w-full cpk:flex-none cpk:flex-col cpk:gap-4",
3379
3251
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_copilotkit_a2ui_renderer.A2UIProvider, {
3380
- onAction: (0, react.useCallback)(async (message) => {
3381
- if (!agent) return;
3382
- message.userAction;
3383
- try {
3384
- copilotkit.setProperties({
3385
- ...copilotkit.properties,
3386
- a2uiAction: message
3387
- });
3388
- await copilotkit.runAgent({ agent });
3389
- } finally {
3390
- if (copilotkit.properties) {
3391
- const { a2uiAction, ...rest } = copilotkit.properties;
3392
- copilotkit.setProperties(rest);
3393
- }
3394
- }
3395
- }, [agent, copilotkit]),
3252
+ onAction: (0, react.useCallback)((message) => runA2UIAction({
3253
+ message,
3254
+ agent,
3255
+ copilotkit,
3256
+ onAction
3257
+ }), [
3258
+ agent,
3259
+ copilotkit,
3260
+ onAction
3261
+ ]),
3396
3262
  theme,
3397
3263
  catalog,
3398
3264
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(SurfaceMessageProcessor, {
@@ -3463,6 +3329,18 @@ window.parent.postMessage({jsonrpc:"2.0",method:"ui/notifications/sandbox-proxy-
3463
3329
  return operation?.createSurface?.surfaceId ?? operation?.updateComponents?.surfaceId ?? operation?.updateDataModel?.surfaceId ?? operation?.deleteSurface?.surfaceId ?? null;
3464
3330
  }
3465
3331
 
3332
+ //#endregion
3333
+ //#region src/v2/types/defineToolCallRenderer.ts
3334
+ function defineToolCallRenderer(def) {
3335
+ const argsSchema = def.name === "*" && !def.args ? zod.z.any() : def.args;
3336
+ return {
3337
+ name: def.name,
3338
+ args: argsSchema,
3339
+ render: def.render,
3340
+ ...def.agentId ? { agentId: def.agentId } : {}
3341
+ };
3342
+ }
3343
+
3466
3344
  //#endregion
3467
3345
  //#region src/v2/a2ui/A2UIToolCallRenderer.tsx
3468
3346
  /**
@@ -4016,10 +3894,10 @@ window.parent.postMessage({jsonrpc:"2.0",method:"ui/notifications/sandbox-proxy-
4016
3894
 
4017
3895
  //#endregion
4018
3896
  //#region src/v2/hooks/use-frontend-tool.tsx
4019
- const EMPTY_DEPS = [];
3897
+ const EMPTY_DEPS$1 = [];
4020
3898
  function useFrontendTool(tool, deps) {
4021
3899
  const { copilotkit } = useCopilotKit();
4022
- const extraDeps = deps ?? EMPTY_DEPS;
3900
+ const extraDeps = deps ?? EMPTY_DEPS$1;
4023
3901
  (0, react.useEffect)(() => {
4024
3902
  const name = tool.name;
4025
3903
  if (copilotkit.getTool({
@@ -4057,63 +3935,333 @@ window.parent.postMessage({jsonrpc:"2.0",method:"ui/notifications/sandbox-proxy-
4057
3935
  * - forwards optional schema parameters (any Standard Schema V1 compatible library),
4058
3936
  * - renders your component with tool call parameters.
4059
3937
  *
4060
- * Use this when you want to display a typed visual component for a tool call
4061
- * without manually wiring a full frontend tool object.
3938
+ * Use this when you want to display a typed visual component for a tool call
3939
+ * without manually wiring a full frontend tool object.
3940
+ *
3941
+ * When `parameters` is provided, render props are inferred from the schema.
3942
+ * When omitted, the render component may accept any props.
3943
+ *
3944
+ * @typeParam TSchema - Schema describing tool parameters, or `undefined` when no schema is given.
3945
+ * @param config - Tool registration config.
3946
+ * @param deps - Optional dependencies to refresh registration (same semantics as `useEffect`).
3947
+ *
3948
+ * @example
3949
+ * ```tsx
3950
+ * // Without parameters — render accepts any props
3951
+ * useComponent({
3952
+ * name: "showGreeting",
3953
+ * render: ({ message }: { message: string }) => <div>{message}</div>,
3954
+ * });
3955
+ * ```
3956
+ *
3957
+ * @example
3958
+ * ```tsx
3959
+ * // With parameters — render props inferred from schema
3960
+ * useComponent({
3961
+ * name: "showWeatherCard",
3962
+ * parameters: z.object({ city: z.string() }),
3963
+ * render: ({ city }) => <div>{city}</div>,
3964
+ * });
3965
+ * ```
3966
+ *
3967
+ * @example
3968
+ * ```tsx
3969
+ * useComponent(
3970
+ * {
3971
+ * name: "renderProfile",
3972
+ * parameters: z.object({ userId: z.string() }),
3973
+ * render: ProfileCard,
3974
+ * agentId: "support-agent",
3975
+ * },
3976
+ * [selectedAgentId],
3977
+ * );
3978
+ * ```
3979
+ */
3980
+ function useComponent(config, deps) {
3981
+ const prefix = `Use this tool to display the "${config.name}" component in the chat. This tool renders a visual UI component for the user.`;
3982
+ const fullDescription = config.description ? `${prefix}\n\n${config.description}` : prefix;
3983
+ useFrontendTool({
3984
+ name: config.name,
3985
+ description: fullDescription,
3986
+ parameters: config.parameters,
3987
+ render: ({ args }) => {
3988
+ const Component = config.render;
3989
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Component, { ...args });
3990
+ },
3991
+ agentId: config.agentId,
3992
+ followUp: config.followUp
3993
+ }, deps);
3994
+ }
3995
+
3996
+ //#endregion
3997
+ //#region src/v2/hooks/use-render-tool.tsx
3998
+ const EMPTY_DEPS = [];
3999
+ /**
4000
+ * Registers a renderer entry in CopilotKit's `renderToolCalls` registry.
4001
+ *
4002
+ * Key behavior:
4003
+ * - deduplicates by `agentId:name` (latest registration wins),
4004
+ * - keeps renderer entries on cleanup so historical chat tool calls can still render,
4005
+ * - refreshes registration when `deps` change.
4006
+ *
4007
+ * @typeParam S - Schema type describing tool call parameters.
4008
+ * @param config - Renderer config for wildcard or named tools.
4009
+ * @param deps - Optional dependencies to refresh registration.
4010
+ *
4011
+ * @example
4012
+ * ```tsx
4013
+ * useRenderTool(
4014
+ * {
4015
+ * name: "searchDocs",
4016
+ * parameters: z.object({ query: z.string() }),
4017
+ * render: ({ status, parameters, result }) => {
4018
+ * if (status === "executing") return <div>Searching {parameters.query}</div>;
4019
+ * if (status === "complete") return <div>{result}</div>;
4020
+ * return <div>Preparing...</div>;
4021
+ * },
4022
+ * },
4023
+ * [],
4024
+ * );
4025
+ * ```
4026
+ *
4027
+ * @example
4028
+ * ```tsx
4029
+ * useRenderTool(
4030
+ * {
4031
+ * name: "summarize",
4032
+ * parameters: z.object({ text: z.string() }),
4033
+ * agentId: "research-agent",
4034
+ * render: ({ name, status }) => <div>{name}: {status}</div>,
4035
+ * },
4036
+ * [selectedAgentId],
4037
+ * );
4038
+ * ```
4039
+ */
4040
+ function useRenderTool(config, deps) {
4041
+ const { copilotkit } = useCopilotKit();
4042
+ const extraDeps = deps ?? EMPTY_DEPS;
4043
+ (0, react.useEffect)(() => {
4044
+ const renderer = config.name === "*" && !config.parameters ? defineToolCallRenderer({
4045
+ name: "*",
4046
+ render: (props) => config.render({
4047
+ ...props,
4048
+ parameters: props.args
4049
+ }),
4050
+ ...config.agentId ? { agentId: config.agentId } : {}
4051
+ }) : defineToolCallRenderer({
4052
+ name: config.name,
4053
+ args: config.parameters,
4054
+ render: (props) => {
4055
+ if (props.status === _copilotkit_core.ToolCallStatus.InProgress) return config.render({
4056
+ ...props,
4057
+ parameters: props.args
4058
+ });
4059
+ if (props.status === _copilotkit_core.ToolCallStatus.Executing) return config.render({
4060
+ ...props,
4061
+ parameters: props.args
4062
+ });
4063
+ return config.render({
4064
+ ...props,
4065
+ parameters: props.args
4066
+ });
4067
+ },
4068
+ ...config.agentId ? { agentId: config.agentId } : {}
4069
+ });
4070
+ copilotkit.addHookRenderToolCall(renderer);
4071
+ }, [
4072
+ config.name,
4073
+ copilotkit,
4074
+ JSON.stringify(extraDeps)
4075
+ ]);
4076
+ }
4077
+
4078
+ //#endregion
4079
+ //#region src/v2/hooks/use-default-render-tool.tsx
4080
+ /**
4081
+ * Module-level dedup set so an unknown status value only emits a console
4082
+ * warning the FIRST time we encounter it. Otherwise a stuck/unmapped status
4083
+ * would log on every re-render (potentially many per second).
4084
+ */
4085
+ const warnedUnknownStatuses = /* @__PURE__ */ new Set();
4086
+ /**
4087
+ * Map a {@link ToolCallStatus} enum value to the documented string-union
4088
+ * status the {@link DefaultRenderProps} contract exposes. Unknown / future
4089
+ * enum members log a warning (once per distinct value) and fall back to
4090
+ * `"inProgress"`.
4091
+ */
4092
+ function mapToolCallStatus(status) {
4093
+ switch (status) {
4094
+ case _copilotkit_core.ToolCallStatus.Complete: return "complete";
4095
+ case _copilotkit_core.ToolCallStatus.Executing: return "executing";
4096
+ case _copilotkit_core.ToolCallStatus.InProgress: return "inProgress";
4097
+ default: {
4098
+ const key = String(status);
4099
+ if (!warnedUnknownStatuses.has(key)) {
4100
+ warnedUnknownStatuses.add(key);
4101
+ console.warn(`[CopilotKit] Unknown ToolCallStatus "${key}" in default tool-call renderer; falling back to "inProgress".`);
4102
+ }
4103
+ return "inProgress";
4104
+ }
4105
+ }
4106
+ }
4107
+ /**
4108
+ * Convert the framework-internal renderer props (`args`, enum status) into
4109
+ * the documented {@link DefaultRenderProps} shape (`parameters`, string-union
4110
+ * status) so a user `config.render` always sees the documented contract.
4111
+ */
4112
+ function adaptRendererProps(props) {
4113
+ return {
4114
+ name: props.name,
4115
+ toolCallId: props.toolCallId,
4116
+ parameters: props.args,
4117
+ status: mapToolCallStatus(props.status),
4118
+ result: props.result
4119
+ };
4120
+ }
4121
+ /**
4122
+ * Registers a wildcard (`"*"`) tool-call renderer via `useRenderTool`.
4123
+ *
4124
+ * - Call with no config to use CopilotKit's built-in default tool-call card.
4125
+ * - Pass `config.render` to replace the default UI with your own fallback renderer.
4062
4126
  *
4063
- * When `parameters` is provided, render props are inferred from the schema.
4064
- * When omitted, the render component may accept any props.
4127
+ * This is useful when you want a generic renderer for tools that do not have a
4128
+ * dedicated `useRenderTool({ name: "..." })` registration.
4065
4129
  *
4066
- * @typeParam TSchema - Schema describing tool parameters, or `undefined` when no schema is given.
4067
- * @param config - Tool registration config.
4068
- * @param deps - Optional dependencies to refresh registration (same semantics as `useEffect`).
4130
+ * @param config - Optional custom wildcard render function.
4131
+ * @param deps - Optional dependencies to refresh registration.
4069
4132
  *
4070
4133
  * @example
4071
4134
  * ```tsx
4072
- * // Without parameters — render accepts any props
4073
- * useComponent({
4074
- * name: "showGreeting",
4075
- * render: ({ message }: { message: string }) => <div>{message}</div>,
4076
- * });
4135
+ * useDefaultRenderTool();
4077
4136
  * ```
4078
4137
  *
4079
4138
  * @example
4080
4139
  * ```tsx
4081
- * // With parameters — render props inferred from schema
4082
- * useComponent({
4083
- * name: "showWeatherCard",
4084
- * parameters: z.object({ city: z.string() }),
4085
- * render: ({ city }) => <div>{city}</div>,
4140
+ * useDefaultRenderTool({
4141
+ * render: ({ name, status }) => <div>{name}: {status}</div>,
4086
4142
  * });
4087
4143
  * ```
4088
4144
  *
4089
4145
  * @example
4090
4146
  * ```tsx
4091
- * useComponent(
4147
+ * useDefaultRenderTool(
4092
4148
  * {
4093
- * name: "renderProfile",
4094
- * parameters: z.object({ userId: z.string() }),
4095
- * render: ProfileCard,
4096
- * agentId: "support-agent",
4149
+ * render: ({ name, result }) => (
4150
+ * <ToolEventRow title={name} payload={result} compact={compactMode} />
4151
+ * ),
4097
4152
  * },
4098
- * [selectedAgentId],
4153
+ * [compactMode],
4099
4154
  * );
4100
4155
  * ```
4101
4156
  */
4102
- function useComponent(config, deps) {
4103
- const prefix = `Use this tool to display the "${config.name}" component in the chat. This tool renders a visual UI component for the user.`;
4104
- const fullDescription = config.description ? `${prefix}\n\n${config.description}` : prefix;
4105
- useFrontendTool({
4106
- name: config.name,
4107
- description: fullDescription,
4108
- parameters: config.parameters,
4109
- render: ({ args }) => {
4110
- const Component = config.render;
4111
- return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Component, { ...args });
4112
- },
4113
- agentId: config.agentId,
4114
- followUp: config.followUp
4157
+ function useDefaultRenderTool(config, deps) {
4158
+ const userRender = config?.render;
4159
+ useRenderTool({
4160
+ name: "*",
4161
+ render: userRender ? (raw) => userRender(adaptRendererProps(raw)) : (raw) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DefaultToolCallRenderer, { ...adaptRendererProps(raw) })
4115
4162
  }, deps);
4116
4163
  }
4164
+ /**
4165
+ * Guarded JSON.stringify used inside the expanded `<pre>` blocks. A circular
4166
+ * reference would otherwise crash the entire React tree on render.
4167
+ */
4168
+ function safeStringifyForPre(value) {
4169
+ try {
4170
+ return JSON.stringify(value, null, 2);
4171
+ } catch (err) {
4172
+ console.warn("[CopilotKit] Failed to JSON.stringify tool-call payload for default renderer; falling back to String():", err);
4173
+ try {
4174
+ return String(value);
4175
+ } catch (innerErr) {
4176
+ console.warn("[CopilotKit] safeStringifyForPre: value could not be stringified:", innerErr);
4177
+ return "[unserializable]";
4178
+ }
4179
+ }
4180
+ }
4181
+ function DefaultToolCallRenderer({ name, toolCallId, parameters, status, result }) {
4182
+ const [isExpanded, setIsExpanded] = (0, react.useState)(false);
4183
+ const isActive = status === "inProgress" || status === "executing";
4184
+ const isComplete = status === "complete";
4185
+ const statusLabel = isActive ? "Running" : isComplete ? "Done" : status;
4186
+ const dotClassName = isActive ? "cpk:bg-amber-500" : isComplete ? "cpk:bg-emerald-500" : "cpk:bg-zinc-400";
4187
+ 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";
4188
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
4189
+ "data-testid": "copilot-tool-render",
4190
+ "data-tool-name": name,
4191
+ "data-tool-call-id": toolCallId,
4192
+ "data-status": status,
4193
+ "data-args": safeStringifyForAttr(parameters),
4194
+ "data-result": safeStringifyForAttr(result),
4195
+ className: "cpk:mt-2 cpk:pb-2",
4196
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4197
+ 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",
4198
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
4199
+ type: "button",
4200
+ "aria-expanded": isExpanded,
4201
+ onClick: () => setIsExpanded(!isExpanded),
4202
+ 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",
4203
+ style: { font: "inherit" },
4204
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4205
+ className: "cpk:flex cpk:min-w-0 cpk:items-center cpk:gap-2",
4206
+ children: [
4207
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
4208
+ 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" : ""}`,
4209
+ fill: "none",
4210
+ viewBox: "0 0 24 24",
4211
+ strokeWidth: 2,
4212
+ stroke: "currentColor",
4213
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
4214
+ strokeLinecap: "round",
4215
+ strokeLinejoin: "round",
4216
+ d: "M8.25 4.5l7.5 7.5-7.5 7.5"
4217
+ })
4218
+ }),
4219
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: `cpk:inline-block cpk:h-2 cpk:w-2 cpk:flex-shrink-0 cpk:rounded-full ${dotClassName}` }),
4220
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
4221
+ "data-testid": "copilot-tool-render-name",
4222
+ className: "cpk:truncate cpk:text-[13px] cpk:font-semibold cpk:text-zinc-900 cpk:dark:text-zinc-100",
4223
+ children: name
4224
+ })
4225
+ ]
4226
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
4227
+ "data-testid": "copilot-tool-render-status",
4228
+ 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}`,
4229
+ children: statusLabel
4230
+ })]
4231
+ }), isExpanded && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4232
+ className: "cpk:mt-3 cpk:grid cpk:gap-3",
4233
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
4234
+ className: "cpk:text-[10px] cpk:uppercase cpk:text-zinc-500 cpk:dark:text-zinc-400",
4235
+ children: "Arguments"
4236
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("pre", {
4237
+ 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",
4238
+ children: safeStringifyForPre(parameters ?? {})
4239
+ })] }), result !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
4240
+ className: "cpk:text-[10px] cpk:uppercase cpk:text-zinc-500 cpk:dark:text-zinc-400",
4241
+ children: "Result"
4242
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("pre", {
4243
+ 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",
4244
+ children: typeof result === "string" ? result : safeStringifyForPre(result)
4245
+ })] })]
4246
+ })]
4247
+ })
4248
+ });
4249
+ }
4250
+ function safeStringifyForAttr(value) {
4251
+ if (value === void 0 || value === null) return "";
4252
+ if (typeof value === "string") return value;
4253
+ try {
4254
+ return JSON.stringify(value);
4255
+ } catch (err) {
4256
+ console.warn("[CopilotKit] Failed to JSON.stringify tool-call payload for data-* attribute; falling back to String():", err);
4257
+ try {
4258
+ return String(value);
4259
+ } catch (innerErr) {
4260
+ console.warn("[CopilotKit] safeStringifyForAttr: value could not be stringified:", innerErr);
4261
+ return "";
4262
+ }
4263
+ }
4264
+ }
4117
4265
 
4118
4266
  //#endregion
4119
4267
  //#region src/v2/hooks/use-human-in-the-loop.tsx
@@ -4851,7 +4999,7 @@ window.parent.postMessage({jsonrpc:"2.0",method:"ui/notifications/sandbox-proxy-
4851
4999
  return (0, react.useSyncExternalStore)((0, react.useCallback)((onStoreChange) => {
4852
5000
  const subscription = store.select(selector).subscribe(onStoreChange);
4853
5001
  return () => subscription.unsubscribe();
4854
- }, [store, selector]), () => selector(store.getState()));
5002
+ }, [store, selector]), () => selector(store.getState()), () => selector(store.getServerState()));
4855
5003
  }
4856
5004
  /**
4857
5005
  * React hook for listing and managing Intelligence platform threads.
@@ -4894,7 +5042,7 @@ window.parent.postMessage({jsonrpc:"2.0",method:"ui/notifications/sandbox-proxy-
4894
5042
  * }
4895
5043
  * ```
4896
5044
  */
4897
- function useThreads({ agentId, includeArchived, limit }) {
5045
+ function useThreads({ agentId, includeArchived, limit, enabled = true }) {
4898
5046
  const { copilotkit } = useCopilotKit();
4899
5047
  const [store] = (0, react.useState)(() => (0, _copilotkit_core.ɵcreateThreadStore)({ fetch: globalThis.fetch }));
4900
5048
  const coreThreads = useThreadStoreSelector(store, _copilotkit_core.ɵselectThreads);
@@ -4911,6 +5059,7 @@ window.parent.postMessage({jsonrpc:"2.0",method:"ui/notifications/sandbox-proxy-
4911
5059
  const storeError = useThreadStoreSelector(store, _copilotkit_core.ɵselectThreadsError);
4912
5060
  const hasMoreThreads = useThreadStoreSelector(store, _copilotkit_core.ɵselectHasNextPage);
4913
5061
  const isFetchingMoreThreads = useThreadStoreSelector(store, _copilotkit_core.ɵselectIsFetchingNextPage);
5062
+ const isMutating = useThreadStoreSelector(store, _copilotkit_core.ɵselectIsMutating);
4914
5063
  const headersKey = (0, react.useMemo)(() => {
4915
5064
  return JSON.stringify(Object.entries(copilotkit.headers ?? {}).sort(([left], [right]) => left.localeCompare(right)));
4916
5065
  }, [copilotkit.headers]);
@@ -4931,9 +5080,16 @@ window.parent.postMessage({jsonrpc:"2.0",method:"ui/notifications/sandbox-proxy-
4931
5080
  return /* @__PURE__ */ new Error("Thread mutations are not available on this CopilotKit runtime");
4932
5081
  }, [threadMutationsSupported]);
4933
5082
  const [hasDispatchedContext, setHasDispatchedContext] = (0, react.useState)(false);
4934
- const preConnectLoading = !!copilotkit.runtimeUrl && !threadEndpointsUnavailable && !hasDispatchedContext;
4935
- const isLoading = runtimeError || threadEndpointsError ? false : preConnectLoading || storeIsLoading;
4936
- const error = runtimeError ?? threadEndpointsError ?? storeError;
5083
+ const preConnectLoading = enabled && !!copilotkit.runtimeUrl && !threadEndpointsUnavailable && !hasDispatchedContext;
5084
+ const [configErrorDismissed, setConfigErrorDismissed] = (0, react.useState)(false);
5085
+ (0, react.useEffect)(() => {
5086
+ setConfigErrorDismissed(false);
5087
+ }, [runtimeError, threadEndpointsError]);
5088
+ const activeRuntimeError = configErrorDismissed ? null : runtimeError;
5089
+ const activeThreadEndpointsError = configErrorDismissed ? null : threadEndpointsError;
5090
+ const isLoading = activeRuntimeError || activeThreadEndpointsError ? false : preConnectLoading || storeIsLoading;
5091
+ const error = activeRuntimeError ?? activeThreadEndpointsError ?? storeError;
5092
+ const listError = storeError;
4937
5093
  (0, react.useEffect)(() => {
4938
5094
  store.start();
4939
5095
  return () => {
@@ -4941,6 +5097,7 @@ window.parent.postMessage({jsonrpc:"2.0",method:"ui/notifications/sandbox-proxy-
4941
5097
  };
4942
5098
  }, [store]);
4943
5099
  (0, react.useEffect)(() => {
5100
+ if (!enabled) return;
4944
5101
  copilotkit.registerThreadStore(agentId, store);
4945
5102
  return () => {
4946
5103
  copilotkit.unregisterThreadStore(agentId);
@@ -4948,9 +5105,15 @@ window.parent.postMessage({jsonrpc:"2.0",method:"ui/notifications/sandbox-proxy-
4948
5105
  }, [
4949
5106
  copilotkit,
4950
5107
  agentId,
4951
- store
5108
+ store,
5109
+ enabled
4952
5110
  ]);
4953
5111
  (0, react.useEffect)(() => {
5112
+ if (!enabled) {
5113
+ store.setContext(null);
5114
+ setHasDispatchedContext(false);
5115
+ return;
5116
+ }
4954
5117
  if (!copilotkit.runtimeUrl) {
4955
5118
  store.setContext(null);
4956
5119
  setHasDispatchedContext(false);
@@ -4974,6 +5137,7 @@ window.parent.postMessage({jsonrpc:"2.0",method:"ui/notifications/sandbox-proxy-
4974
5137
  setHasDispatchedContext(true);
4975
5138
  }, [
4976
5139
  store,
5140
+ enabled,
4977
5141
  copilotkit.runtimeUrl,
4978
5142
  runtimeStatus,
4979
5143
  headersKey,
@@ -4997,9 +5161,16 @@ window.parent.postMessage({jsonrpc:"2.0",method:"ui/notifications/sandbox-proxy-
4997
5161
  threads,
4998
5162
  isLoading,
4999
5163
  error,
5164
+ listError,
5000
5165
  hasMoreThreads,
5001
5166
  isFetchingMoreThreads,
5167
+ isMutating,
5002
5168
  fetchMoreThreads: (0, react.useCallback)(() => store.fetchNextPage(), [store]),
5169
+ refetchThreads: (0, react.useCallback)(() => store.refetchThreads(), [store]),
5170
+ startNewThread: (0, react.useCallback)(() => {
5171
+ setConfigErrorDismissed(true);
5172
+ store.startNewThread();
5173
+ }, [store]),
5003
5174
  renameThread,
5004
5175
  archiveThread,
5005
5176
  unarchiveThread,
@@ -6186,7 +6357,7 @@ window.parent.postMessage({jsonrpc:"2.0",method:"ui/notifications/sandbox-proxy-
6186
6357
  ref,
6187
6358
  "data-copilotkit": true,
6188
6359
  "data-testid": "copilot-suggestions",
6189
- 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),
6360
+ 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),
6190
6361
  ...props
6191
6362
  });
6192
6363
  });
@@ -7291,7 +7462,7 @@ window.parent.postMessage({jsonrpc:"2.0",method:"ui/notifications/sandbox-proxy-
7291
7462
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
7292
7463
  className: "cpk:max-w-3xl cpk:mx-auto",
7293
7464
  children: [BoundMessageView, hasSuggestions ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
7294
- className: "cpk:pl-0 cpk:pr-4 cpk:sm:px-0 cpk:mt-4",
7465
+ className: "cpk:pl-0 cpk:pr-4 cpk:@3xl:px-0 cpk:mt-4",
7295
7466
  children: BoundSuggestionView
7296
7467
  }) : null]
7297
7468
  })
@@ -7334,7 +7505,7 @@ window.parent.postMessage({jsonrpc:"2.0",method:"ui/notifications/sandbox-proxy-
7334
7505
  onDragOver,
7335
7506
  onDragLeave,
7336
7507
  onDrop,
7337
- className: cn("copilotKitChat cpk:relative cpk:h-full cpk:flex cpk:flex-col", className),
7508
+ className: cn("copilotKitChat cpk:@container cpk:relative cpk:h-full cpk:flex cpk:flex-col", className),
7338
7509
  ...props,
7339
7510
  children: [dragOver && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DropOverlay, {}), BoundWelcomeScreen]
7340
7511
  });
@@ -7356,7 +7527,7 @@ window.parent.postMessage({jsonrpc:"2.0",method:"ui/notifications/sandbox-proxy-
7356
7527
  onDragOver,
7357
7528
  onDragLeave,
7358
7529
  onDrop,
7359
- className: cn("copilotKitChat cpk:relative cpk:h-full cpk:flex cpk:flex-col", className),
7530
+ className: cn("copilotKitChat cpk:@container cpk:relative cpk:h-full cpk:flex cpk:flex-col", className),
7360
7531
  ...props,
7361
7532
  children: [
7362
7533
  dragOver && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DropOverlay, {}),
@@ -7395,7 +7566,7 @@ window.parent.postMessage({jsonrpc:"2.0",method:"ui/notifications/sandbox-proxy-
7395
7566
  minHeight: 0
7396
7567
  },
7397
7568
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
7398
- className: "cpk:px-4 cpk:sm:px-0 cpk:[div[data-sidebar-chat]_&]:px-8 cpk:[div[data-popup-chat]_&]:px-6",
7569
+ className: "cpk:px-4 cpk:@3xl:px-0 cpk:[div[data-sidebar-chat]_&]:px-8 cpk:[div[data-popup-chat]_&]:px-6",
7399
7570
  children
7400
7571
  })
7401
7572
  }),
@@ -7428,7 +7599,7 @@ window.parent.postMessage({jsonrpc:"2.0",method:"ui/notifications/sandbox-proxy-
7428
7599
  ...props,
7429
7600
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
7430
7601
  ref: contentRef,
7431
- className: "cpk:px-4 cpk:sm:px-0 cpk:[div[data-sidebar-chat]_&]:px-8 cpk:[div[data-popup-chat]_&]:px-6",
7602
+ className: "cpk:px-4 cpk:@3xl:px-0 cpk:[div[data-sidebar-chat]_&]:px-8 cpk:[div[data-popup-chat]_&]:px-6",
7432
7603
  children
7433
7604
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
7434
7605
  ref: spacerRef,
@@ -7490,7 +7661,7 @@ window.parent.postMessage({jsonrpc:"2.0",method:"ui/notifications/sandbox-proxy-
7490
7661
  if (!hasMounted) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
7491
7662
  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",
7492
7663
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
7493
- 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",
7494
7665
  children
7495
7666
  })
7496
7667
  });
@@ -7505,7 +7676,7 @@ window.parent.postMessage({jsonrpc:"2.0",method:"ui/notifications/sandbox-proxy-
7505
7676
  children: [
7506
7677
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
7507
7678
  ref: contentRef,
7508
- className: "cpk:px-4 cpk:sm:px-0 cpk:[div[data-sidebar-chat]_&]:px-8 cpk:[div[data-popup-chat]_&]:px-6",
7679
+ className: "cpk:px-4 cpk:@3xl:px-0 cpk:[div[data-sidebar-chat]_&]:px-8 cpk:[div[data-popup-chat]_&]:px-6",
7509
7680
  children
7510
7681
  }),
7511
7682
  BoundFeather,
@@ -7769,9 +7940,17 @@ window.parent.postMessage({jsonrpc:"2.0",method:"ui/notifications/sandbox-proxy-
7769
7940
  const { messageView: providedMessageView, suggestionView: providedSuggestionView, onStop: providedStopHandler, ...restProps } = props;
7770
7941
  const [lastConnectedThreadId, setLastConnectedThreadId] = (0, react.useState)(null);
7771
7942
  const isConnecting = hasExplicitThreadId && lastConnectedThreadId !== resolvedThreadId;
7943
+ const previousThreadIdRef = (0, react.useRef)(null);
7944
+ const hasExplicitThreadIdRef = (0, react.useRef)(hasExplicitThreadId);
7945
+ hasExplicitThreadIdRef.current = hasExplicitThreadId;
7772
7946
  (0, react.useEffect)(() => {
7947
+ const threadChanged = previousThreadIdRef.current !== resolvedThreadId;
7948
+ previousThreadIdRef.current = resolvedThreadId;
7773
7949
  agent.threadId = resolvedThreadId;
7774
- if (!hasExplicitThreadId) return;
7950
+ if (!hasExplicitThreadId) {
7951
+ if (threadChanged && agent.messages.length > 0) agent.setMessages([]);
7952
+ return;
7953
+ }
7775
7954
  let detached = false;
7776
7955
  const connectAbortController = new AbortController();
7777
7956
  if (agent instanceof _ag_ui_client.HttpAgent) agent.abortController = connectAbortController;
@@ -7785,6 +7964,7 @@ window.parent.postMessage({jsonrpc:"2.0",method:"ui/notifications/sandbox-proxy-
7785
7964
  if (!detached) (typeof requestAnimationFrame === "function" ? requestAnimationFrame : (cb) => setTimeout(cb, 16))(() => {
7786
7965
  if (!detached) setLastConnectedThreadId(resolvedThreadId);
7787
7966
  });
7967
+ else if (!hasExplicitThreadIdRef.current) agentToConnect.setMessages([]);
7788
7968
  }
7789
7969
  };
7790
7970
  connect(agent);
@@ -8135,18 +8315,46 @@ window.parent.postMessage({jsonrpc:"2.0",method:"ui/notifications/sandbox-proxy-
8135
8315
 
8136
8316
  //#endregion
8137
8317
  //#region src/v2/components/chat/CopilotModalHeader.tsx
8138
- function CopilotModalHeader({ title, titleContent, closeButton, children, className, ...rest }) {
8318
+ /**
8319
+ * Reactively tracks whether the viewport is in the mobile range (≤767px) — the
8320
+ * same breakpoint the drawer + chat coordination use. SSR-safe: starts `false`
8321
+ * (desktop) so the server render and first client render agree, then syncs on
8322
+ * mount and on resize.
8323
+ */
8324
+ function useIsMobileViewport() {
8325
+ const [isMobile, setIsMobile] = (0, react.useState)(false);
8326
+ (0, react.useEffect)(() => {
8327
+ if (typeof window === "undefined" || typeof window.matchMedia !== "function") return;
8328
+ const mql = window.matchMedia("(max-width: 767px)");
8329
+ const update = () => setIsMobile(mql.matches);
8330
+ update();
8331
+ mql.addEventListener("change", update);
8332
+ return () => mql.removeEventListener("change", update);
8333
+ }, []);
8334
+ return isMobile;
8335
+ }
8336
+ function CopilotModalHeader({ title, titleContent, closeButton, drawerLauncher, children, className, ...rest }) {
8139
8337
  const configuration = useCopilotChatConfiguration();
8140
8338
  const fallbackTitle = configuration?.labels.modalHeaderTitle ?? CopilotChatDefaultLabels.modalHeaderTitle;
8141
8339
  const resolvedTitle = title ?? fallbackTitle;
8340
+ const isMobile = useIsMobileViewport();
8341
+ const drawerRegistered = (configuration?.drawerRegistered ?? false) && isMobile;
8142
8342
  const handleClose = (0, react.useCallback)(() => {
8143
8343
  configuration?.setModalOpen?.(false);
8144
8344
  }, [configuration]);
8345
+ const handleToggleDrawer = (0, react.useCallback)(() => {
8346
+ configuration?.setDrawerOpen?.(!configuration.drawerOpen);
8347
+ }, [configuration]);
8145
8348
  const BoundTitle = renderSlot(titleContent, CopilotModalHeader.Title, { children: resolvedTitle });
8146
8349
  const BoundCloseButton = renderSlot(closeButton, CopilotModalHeader.CloseButton, { onClick: handleClose });
8350
+ const BoundDrawerLauncher = drawerRegistered ? renderSlot(drawerLauncher, CopilotModalHeader.DrawerLauncher, {
8351
+ onClick: handleToggleDrawer,
8352
+ "aria-expanded": configuration?.drawerOpen ?? false
8353
+ }) : null;
8147
8354
  if (children) return children({
8148
8355
  titleContent: BoundTitle,
8149
8356
  closeButton: BoundCloseButton,
8357
+ drawerLauncher: BoundDrawerLauncher,
8150
8358
  title: resolvedTitle,
8151
8359
  ...rest
8152
8360
  });
@@ -8159,8 +8367,8 @@ window.parent.postMessage({jsonrpc:"2.0",method:"ui/notifications/sandbox-proxy-
8159
8367
  className: "cpk:flex cpk:w-full cpk:items-center cpk:gap-2",
8160
8368
  children: [
8161
8369
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
8162
- className: "cpk:flex-1",
8163
- "aria-hidden": "true"
8370
+ className: "cpk:flex cpk:flex-1 cpk:justify-start",
8371
+ children: BoundDrawerLauncher ?? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { "aria-hidden": "true" })
8164
8372
  }),
8165
8373
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
8166
8374
  className: "cpk:flex cpk:flex-1 cpk:justify-center cpk:text-center",
@@ -8193,9 +8401,21 @@ window.parent.postMessage({jsonrpc:"2.0",method:"ui/notifications/sandbox-proxy-
8193
8401
  "aria-hidden": "true"
8194
8402
  })
8195
8403
  });
8404
+ _CopilotModalHeader.DrawerLauncher = ({ className, ...props }) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
8405
+ type: "button",
8406
+ "data-testid": "copilot-threads-drawer-launcher",
8407
+ 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),
8408
+ "aria-label": "Open threads",
8409
+ ...props,
8410
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.PanelLeftOpen, {
8411
+ className: "cpk:h-4 cpk:w-4",
8412
+ "aria-hidden": "true"
8413
+ })
8414
+ });
8196
8415
  })(CopilotModalHeader || (CopilotModalHeader = {}));
8197
8416
  CopilotModalHeader.Title.displayName = "CopilotModalHeader.Title";
8198
8417
  CopilotModalHeader.CloseButton.displayName = "CopilotModalHeader.CloseButton";
8418
+ CopilotModalHeader.DrawerLauncher.displayName = "CopilotModalHeader.DrawerLauncher";
8199
8419
 
8200
8420
  //#endregion
8201
8421
  //#region src/v2/components/chat/CopilotSidebarView.tsx
@@ -8553,6 +8773,322 @@ window.parent.postMessage({jsonrpc:"2.0",method:"ui/notifications/sandbox-proxy-
8553
8773
  }
8554
8774
  CopilotPopup.displayName = "CopilotPopup";
8555
8775
 
8776
+ //#endregion
8777
+ //#region src/v2/components/chat/CopilotThreadsDrawer.tsx
8778
+ /**
8779
+ * Maps a {@link Thread} from {@link useThreads} to the element's
8780
+ * {@link DrawerThread} view shape. The shapes are structurally compatible; this
8781
+ * narrows to exactly the fields the element renders so the element never sees
8782
+ * platform-internal fields.
8783
+ */
8784
+ function toDrawerThread(thread) {
8785
+ return {
8786
+ id: thread.id,
8787
+ name: thread.name,
8788
+ archived: thread.archived,
8789
+ createdAt: thread.createdAt,
8790
+ updatedAt: thread.updatedAt,
8791
+ ...thread.lastRunAt !== void 0 ? { lastRunAt: thread.lastRunAt } : {}
8792
+ };
8793
+ }
8794
+ /** The chat input textarea's documented `data-testid`. */
8795
+ const CHAT_INPUT_TESTID = "copilot-chat-textarea";
8796
+ /** The chat view container's documented `data-testid`. */
8797
+ const CHAT_CONTAINER_TESTID = "copilot-chat";
8798
+ /**
8799
+ * Returns the chat input element for focus-return after a thread is selected.
8800
+ *
8801
+ * Best-effort and SCOPED: walks up from the drawer element looking for an
8802
+ * ancestor that contains a chat-view container (`data-testid="copilot-chat"`),
8803
+ * then returns the chat input within that subtree. This avoids focusing the
8804
+ * wrong composer on a page hosting more than one chat (multi-chat dashboards),
8805
+ * where a document-global lookup would grab whichever input appears first in
8806
+ * DOM order rather than the one this drawer drives.
8807
+ *
8808
+ * Falls back to a document-global lookup when no scoping ancestor is found
8809
+ * (e.g. the drawer and chat share no common container, or headless usage),
8810
+ * and returns `null` when there is no chat input at all.
8811
+ *
8812
+ * @param origin - The drawer element to scope the search from.
8813
+ */
8814
+ function findChatInput(origin) {
8815
+ if (typeof document === "undefined") return null;
8816
+ const container = origin?.closest?.(`[data-testid="${CHAT_CONTAINER_TESTID}"]`);
8817
+ if (container) {
8818
+ const scoped = container.querySelector(`[data-testid="${CHAT_INPUT_TESTID}"]`);
8819
+ if (scoped) return scoped;
8820
+ }
8821
+ return document.querySelector(`[data-testid="${CHAT_INPUT_TESTID}"]`);
8822
+ }
8823
+ /**
8824
+ * React wrapper for the shadow-DOM `<copilotkit-threads-drawer>` threads drawer.
8825
+ *
8826
+ * Responsibilities:
8827
+ * - Registers the custom element on the client (SSR-safe; nothing renders
8828
+ * during prerender to avoid hydration mismatch).
8829
+ * - Feeds the element domain data: `threads`, `loading`, `error`,
8830
+ * `activeThreadId`, `licensed`, fetch-more state.
8831
+ * - Routes the element's nine outbound events to core thread operations
8832
+ * ({@link useThreads}) and chat-configuration changes.
8833
+ * - Registers with the surrounding chat configuration so the header
8834
+ * thread-list launcher appears, and binds the element `open` state to the
8835
+ * configuration's `drawerOpen`.
8836
+ *
8837
+ * License gating is two-pronged: the locked view shows when no license is configured
8838
+ * (the runtime reported no license status) OR the `threads` feature is
8839
+ * explicitly unlicensed. While unlicensed, the thread fetch is skipped entirely
8840
+ * so an unlicensed drawer issues no network requests.
8841
+ *
8842
+ * Thread switching needs no host wiring: when `onThreadSelect`/`onNewThread`
8843
+ * are omitted, the wrapper drives the surrounding chat configuration directly
8844
+ * ({@link CopilotChatConfigurationValue.setActiveThreadId} /
8845
+ * {@link CopilotChatConfigurationValue.startNewThread}), so a bare drawer
8846
+ * connects to the picked thread and shows the welcome screen on "+ New". Pass
8847
+ * the callbacks only to take control yourself.
8848
+ *
8849
+ * @example
8850
+ * ```tsx
8851
+ * // Callback-free: the drawer drives the chat configuration itself.
8852
+ * <CopilotKitProvider runtimeUrl="/api/copilotkit" publicLicenseKey="ck_pub_...">
8853
+ * <CopilotChat />
8854
+ * <CopilotThreadsDrawer />
8855
+ * </CopilotKitProvider>
8856
+ * ```
8857
+ */
8858
+ function CopilotThreadsDrawer({ agentId, onThreadSelect, onNewThread, onLicensed, licenseUrl, renderRow, label, limit, "data-testid": dataTestId = "copilot-threads-drawer" }) {
8859
+ const configuration = useCopilotChatConfiguration();
8860
+ const { status, checkFeature } = useLicenseContext();
8861
+ const licensePresent = status === "valid" || status === "expiring";
8862
+ const featureLicensed = checkFeature("threads");
8863
+ const licensed = licensePresent && featureLicensed;
8864
+ const licensePending = status === null;
8865
+ const resolvedAgentId = agentId ?? configuration?.agentId ?? "default";
8866
+ const activeThreadId = configuration?.threadId ?? null;
8867
+ const { threads, isLoading, listError, hasMoreThreads, isFetchingMoreThreads, archiveThread, unarchiveThread, deleteThread, fetchMoreThreads, refetchThreads, startNewThread } = useThreads({
8868
+ agentId: resolvedAgentId,
8869
+ includeArchived: true,
8870
+ enabled: licensed,
8871
+ ...limit !== void 0 ? { limit } : {}
8872
+ });
8873
+ const drawerThreads = (0, react.useMemo)(() => threads.map(toDrawerThread), [threads]);
8874
+ const elementRef = (0, react.useRef)(null);
8875
+ const [mounted, setMounted] = (0, react.useState)(false);
8876
+ (0, react.useEffect)(() => {
8877
+ (0, _copilotkit_web_components_threads_drawer.defineCopilotKitThreadsDrawer)();
8878
+ setMounted(true);
8879
+ }, []);
8880
+ const registerDrawer = configuration?.registerDrawer;
8881
+ (0, react.useEffect)(() => {
8882
+ if (!registerDrawer) return;
8883
+ return registerDrawer();
8884
+ }, [registerDrawer]);
8885
+ const [localDrawerOpen, setLocalDrawerOpen] = (0, react.useState)(false);
8886
+ const drawerOpen = configuration ? configuration.drawerOpen : localDrawerOpen;
8887
+ const setDrawerOpen = configuration ? configuration.setDrawerOpen : setLocalDrawerOpen;
8888
+ const setActiveThreadId = configuration?.setActiveThreadId;
8889
+ const startNewThreadConfig = configuration?.startNewThread;
8890
+ const handleThreadSelected = (0, react.useCallback)((threadId) => {
8891
+ if (onThreadSelect) onThreadSelect(threadId);
8892
+ else setActiveThreadId?.(threadId, { explicit: true });
8893
+ findChatInput(elementRef.current)?.focus();
8894
+ }, [onThreadSelect, setActiveThreadId]);
8895
+ const handleNewThread = (0, react.useCallback)(() => {
8896
+ startNewThread();
8897
+ if (onNewThread) onNewThread();
8898
+ else startNewThreadConfig?.();
8899
+ }, [
8900
+ startNewThread,
8901
+ onNewThread,
8902
+ startNewThreadConfig
8903
+ ]);
8904
+ const handleArchive = (0, react.useCallback)((threadId) => {
8905
+ archiveThread(threadId).catch((err) => {
8906
+ console.error("CopilotThreadsDrawer: archiveThread failed", err);
8907
+ });
8908
+ }, [archiveThread]);
8909
+ const handleUnarchive = (0, react.useCallback)((threadId) => {
8910
+ unarchiveThread(threadId).catch((err) => {
8911
+ console.error("CopilotThreadsDrawer: unarchiveThread failed", err);
8912
+ });
8913
+ }, [unarchiveThread]);
8914
+ const handleDelete = (0, react.useCallback)((threadId) => {
8915
+ const isActive = threadId === activeThreadId;
8916
+ deleteThread(threadId).then(() => {
8917
+ if (isActive) {
8918
+ startNewThread();
8919
+ if (onNewThread) onNewThread();
8920
+ else startNewThreadConfig?.();
8921
+ }
8922
+ }).catch((err) => {
8923
+ console.error("CopilotThreadsDrawer: deleteThread failed", err);
8924
+ });
8925
+ }, [
8926
+ deleteThread,
8927
+ activeThreadId,
8928
+ startNewThread,
8929
+ onNewThread,
8930
+ startNewThreadConfig
8931
+ ]);
8932
+ const handleFilterChange = (0, react.useCallback)(() => {
8933
+ refetchThreads();
8934
+ }, [refetchThreads]);
8935
+ const handleRetry = (0, react.useCallback)((scope) => {
8936
+ if (scope === "fetch-more") fetchMoreThreads();
8937
+ else refetchThreads();
8938
+ }, [fetchMoreThreads, refetchThreads]);
8939
+ const handleOpenChange = (0, react.useCallback)((open) => {
8940
+ setDrawerOpen(open);
8941
+ }, [setDrawerOpen]);
8942
+ const handleLicensed = (0, react.useCallback)(() => {
8943
+ onLicensed?.();
8944
+ }, [onLicensed]);
8945
+ const handleLoadMore = (0, react.useCallback)(() => {
8946
+ fetchMoreThreads();
8947
+ }, [fetchMoreThreads]);
8948
+ const handlersRef = (0, react.useRef)({
8949
+ handleThreadSelected,
8950
+ handleNewThread,
8951
+ handleArchive,
8952
+ handleUnarchive,
8953
+ handleDelete,
8954
+ handleFilterChange,
8955
+ handleRetry,
8956
+ handleOpenChange,
8957
+ handleLicensed,
8958
+ handleLoadMore
8959
+ });
8960
+ handlersRef.current = {
8961
+ handleThreadSelected,
8962
+ handleNewThread,
8963
+ handleArchive,
8964
+ handleUnarchive,
8965
+ handleDelete,
8966
+ handleFilterChange,
8967
+ handleRetry,
8968
+ handleOpenChange,
8969
+ handleLicensed,
8970
+ handleLoadMore
8971
+ };
8972
+ (0, react.useEffect)(() => {
8973
+ const el = elementRef.current;
8974
+ if (!el) return;
8975
+ const onThreadSelected = (event) => {
8976
+ const detail = event.detail;
8977
+ handlersRef.current.handleThreadSelected(detail.threadId);
8978
+ };
8979
+ const onNewThreadEvent = () => handlersRef.current.handleNewThread();
8980
+ const onArchive = (event) => {
8981
+ const detail = event.detail;
8982
+ handlersRef.current.handleArchive(detail.threadId);
8983
+ };
8984
+ const onUnarchive = (event) => {
8985
+ const detail = event.detail;
8986
+ handlersRef.current.handleUnarchive(detail.threadId);
8987
+ };
8988
+ const onDelete = (event) => {
8989
+ const detail = event.detail;
8990
+ handlersRef.current.handleDelete(detail.threadId);
8991
+ };
8992
+ const onFilterChange = (_event) => {
8993
+ handlersRef.current.handleFilterChange();
8994
+ };
8995
+ const onOpenChangeEvent = (event) => {
8996
+ const detail = event.detail;
8997
+ handlersRef.current.handleOpenChange(detail.open);
8998
+ };
8999
+ const onRetry = (event) => {
9000
+ const detail = event.detail;
9001
+ handlersRef.current.handleRetry(detail.scope);
9002
+ };
9003
+ const onLicensedEvent = () => handlersRef.current.handleLicensed();
9004
+ const onLoadMore = () => handlersRef.current.handleLoadMore();
9005
+ el.addEventListener("thread-selected", onThreadSelected);
9006
+ el.addEventListener("new-thread", onNewThreadEvent);
9007
+ el.addEventListener("archive", onArchive);
9008
+ el.addEventListener("unarchive", onUnarchive);
9009
+ el.addEventListener("delete", onDelete);
9010
+ el.addEventListener("filter-change", onFilterChange);
9011
+ el.addEventListener("open-change", onOpenChangeEvent);
9012
+ el.addEventListener("retry", onRetry);
9013
+ el.addEventListener("licensed", onLicensedEvent);
9014
+ el.addEventListener("load-more", onLoadMore);
9015
+ return () => {
9016
+ el.removeEventListener("thread-selected", onThreadSelected);
9017
+ el.removeEventListener("new-thread", onNewThreadEvent);
9018
+ el.removeEventListener("archive", onArchive);
9019
+ el.removeEventListener("unarchive", onUnarchive);
9020
+ el.removeEventListener("delete", onDelete);
9021
+ el.removeEventListener("filter-change", onFilterChange);
9022
+ el.removeEventListener("open-change", onOpenChangeEvent);
9023
+ el.removeEventListener("retry", onRetry);
9024
+ el.removeEventListener("licensed", onLicensedEvent);
9025
+ el.removeEventListener("load-more", onLoadMore);
9026
+ };
9027
+ }, [mounted]);
9028
+ (0, react.useEffect)(() => {
9029
+ const el = elementRef.current;
9030
+ if (!el) return;
9031
+ el.threads = drawerThreads;
9032
+ }, [drawerThreads, mounted]);
9033
+ (0, react.useEffect)(() => {
9034
+ const el = elementRef.current;
9035
+ if (!el) return;
9036
+ el.loading = isLoading || licensePending;
9037
+ el.error = listError ? listError.message : null;
9038
+ el.activeThreadId = activeThreadId;
9039
+ el.licensed = licensed || licensePending;
9040
+ el.hasMore = hasMoreThreads;
9041
+ el.fetchingMore = isFetchingMoreThreads;
9042
+ }, [
9043
+ isLoading,
9044
+ listError,
9045
+ activeThreadId,
9046
+ licensed,
9047
+ licensePending,
9048
+ hasMoreThreads,
9049
+ isFetchingMoreThreads,
9050
+ mounted
9051
+ ]);
9052
+ (0, react.useEffect)(() => {
9053
+ const el = elementRef.current;
9054
+ if (!el) return;
9055
+ el.open = drawerOpen;
9056
+ }, [drawerOpen, mounted]);
9057
+ (0, react.useEffect)(() => {
9058
+ const el = elementRef.current;
9059
+ if (!el) return;
9060
+ if (label !== void 0) el.label = label;
9061
+ }, [label, mounted]);
9062
+ (0, react.useEffect)(() => {
9063
+ const el = elementRef.current;
9064
+ if (!el) return;
9065
+ if (licenseUrl !== void 0) el.licenseUrl = licenseUrl;
9066
+ }, [licenseUrl, mounted]);
9067
+ const rowChildren = (0, react.useMemo)(() => {
9068
+ if (!renderRow) return null;
9069
+ return drawerThreads.map((drawerThread) => {
9070
+ const fullThread = threads.find((t) => t.id === drawerThread.id);
9071
+ if (!fullThread) return null;
9072
+ const content = renderRow(fullThread);
9073
+ if (content === null || content === void 0) return null;
9074
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
9075
+ slot: `row:${drawerThread.id}`,
9076
+ children: content
9077
+ }, drawerThread.id);
9078
+ });
9079
+ }, [
9080
+ renderRow,
9081
+ drawerThreads,
9082
+ threads
9083
+ ]);
9084
+ if (!mounted) return null;
9085
+ return react.default.createElement(_copilotkit_web_components_threads_drawer.COPILOTKIT_THREADS_DRAWER_TAG, {
9086
+ ref: elementRef,
9087
+ "data-testid": dataTestId
9088
+ }, rowChildren);
9089
+ }
9090
+ CopilotThreadsDrawer.displayName = "CopilotThreadsDrawer";
9091
+
8556
9092
  //#endregion
8557
9093
  //#region src/v2/components/WildcardToolCallRender.tsx
8558
9094
  const WildcardToolCallRender = defineToolCallRenderer({
@@ -10668,6 +11204,7 @@ Object.defineProperty(exports, 'CopilotSidebarView', {
10668
11204
  return CopilotSidebarView;
10669
11205
  }
10670
11206
  });
11207
+ exports.CopilotThreadsDrawer = CopilotThreadsDrawer;
10671
11208
  exports.INTELLIGENCE_TURN_HEAD = INTELLIGENCE_TURN_HEAD;
10672
11209
  exports.IntelligenceIndicator = IntelligenceIndicator;
10673
11210
  exports.IntelligenceIndicatorView = IntelligenceIndicatorView;