@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.
- package/dist/{copilotkit-Be-o2UaU.mjs → copilotkit-BtRkFsNR.mjs} +941 -404
- package/dist/copilotkit-BtRkFsNR.mjs.map +1 -0
- package/dist/{copilotkit-CTCjVxkH.cjs → copilotkit-CdpDZi3i.cjs} +943 -400
- package/dist/copilotkit-CdpDZi3i.cjs.map +1 -0
- package/dist/{copilotkit-ClqbUuGX.d.cts → copilotkit-Cga6AHO0.d.cts} +429 -149
- package/dist/copilotkit-Cga6AHO0.d.cts.map +1 -0
- package/dist/{copilotkit-Bpt1c_-q.d.mts → copilotkit-DnLpJ2Cl.d.mts} +429 -149
- package/dist/copilotkit-DnLpJ2Cl.d.mts.map +1 -0
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.mts +1 -1
- package/dist/index.mjs +1 -1
- package/dist/index.umd.js +193 -176
- package/dist/index.umd.js.map +1 -1
- package/dist/v2/headless.cjs +156 -11
- package/dist/v2/headless.cjs.map +1 -1
- package/dist/v2/headless.d.cts +111 -1
- package/dist/v2/headless.d.cts.map +1 -1
- package/dist/v2/headless.d.mts +111 -1
- package/dist/v2/headless.d.mts.map +1 -1
- package/dist/v2/headless.mjs +157 -12
- package/dist/v2/headless.mjs.map +1 -1
- package/dist/v2/index.cjs +2 -1
- package/dist/v2/index.css +1 -1
- package/dist/v2/index.d.cts +2 -2
- package/dist/v2/index.d.mts +2 -2
- package/dist/v2/index.mjs +2 -2
- package/dist/v2/index.umd.js +949 -412
- package/dist/v2/index.umd.js.map +1 -1
- package/package.json +8 -6
- package/dist/copilotkit-Be-o2UaU.mjs.map +0 -1
- package/dist/copilotkit-Bpt1c_-q.d.mts.map +0 -1
- package/dist/copilotkit-CTCjVxkH.cjs.map +0 -1
- package/dist/copilotkit-ClqbUuGX.d.cts.map +0 -1
|
@@ -48,6 +48,7 @@ let zod_to_json_schema = require("zod-to-json-schema");
|
|
|
48
48
|
let react_dom = require("react-dom");
|
|
49
49
|
let _tanstack_react_virtual = require("@tanstack/react-virtual");
|
|
50
50
|
let use_stick_to_bottom = require("use-stick-to-bottom");
|
|
51
|
+
let _copilotkit_web_components_threads_drawer = require("@copilotkit/web-components/threads-drawer");
|
|
51
52
|
let react_markdown = require("react-markdown");
|
|
52
53
|
react_markdown = __toESM(react_markdown);
|
|
53
54
|
|
|
@@ -174,6 +175,25 @@ const CopilotChatDefaultLabels = {
|
|
|
174
175
|
modalHeaderTitle: "CopilotKit Chat",
|
|
175
176
|
welcomeMessageText: "How can I help you today?"
|
|
176
177
|
};
|
|
178
|
+
/**
|
|
179
|
+
* Mobile breakpoint below which the chat modal and the thread-list drawer are
|
|
180
|
+
* mutually exclusive. At or above this width both surfaces may coexist. This
|
|
181
|
+
* mirrors the `(max-width: 767px)` / `(min-width: 768px)` split already used by
|
|
182
|
+
* CopilotChatInput and CopilotSidebarView.
|
|
183
|
+
*/
|
|
184
|
+
const MOBILE_MAX_WIDTH_PX = 767;
|
|
185
|
+
/**
|
|
186
|
+
* Reports whether the current viewport is in the mobile range (`<768px`), where
|
|
187
|
+
* the chat modal and drawer must not be open simultaneously. SSR-safe and
|
|
188
|
+
* defensive against environments without `matchMedia` (treated as desktop, so
|
|
189
|
+
* no mutual-exclusion constraint is applied).
|
|
190
|
+
*
|
|
191
|
+
* @returns `true` when the viewport is mobile-width, `false` otherwise.
|
|
192
|
+
*/
|
|
193
|
+
function isMobileViewport() {
|
|
194
|
+
if (typeof window === "undefined" || typeof window.matchMedia !== "function") return false;
|
|
195
|
+
return window.matchMedia(`(max-width: ${MOBILE_MAX_WIDTH_PX}px)`).matches;
|
|
196
|
+
}
|
|
177
197
|
const CopilotChatConfiguration = (0, react.createContext)(null);
|
|
178
198
|
const CopilotChatConfigurationProvider = ({ children, labels, agentId, threadId, hasExplicitThreadId, isModalDefaultOpen }) => {
|
|
179
199
|
const parentConfig = (0, react.useContext)(CopilotChatConfiguration);
|
|
@@ -184,12 +204,22 @@ const CopilotChatConfigurationProvider = ({ children, labels, agentId, threadId,
|
|
|
184
204
|
...stableLabels
|
|
185
205
|
}), [stableLabels, parentConfig?.labels]);
|
|
186
206
|
const resolvedAgentId = agentId ?? parentConfig?.agentId ?? _copilotkit_shared.DEFAULT_AGENT_ID;
|
|
207
|
+
const threadIdPropIsAuthoritative = threadId !== void 0 && hasExplicitThreadId !== false;
|
|
208
|
+
const isThreadIdControlled = threadIdPropIsAuthoritative;
|
|
209
|
+
const [activeThreadOverride, setActiveThreadOverride] = (0, react.useState)(null);
|
|
187
210
|
const resolvedThreadId = (0, react.useMemo)(() => {
|
|
188
|
-
if (
|
|
211
|
+
if (threadIdPropIsAuthoritative) return threadId;
|
|
212
|
+
if (activeThreadOverride) return activeThreadOverride.threadId;
|
|
189
213
|
if (parentConfig?.threadId) return parentConfig.threadId;
|
|
214
|
+
if (threadId) return threadId;
|
|
190
215
|
return (0, _copilotkit_shared.randomUUID)();
|
|
191
|
-
}, [
|
|
192
|
-
|
|
216
|
+
}, [
|
|
217
|
+
threadIdPropIsAuthoritative,
|
|
218
|
+
threadId,
|
|
219
|
+
parentConfig?.threadId,
|
|
220
|
+
activeThreadOverride
|
|
221
|
+
]);
|
|
222
|
+
const resolvedHasExplicitThreadId = (threadIdPropIsAuthoritative ? true : activeThreadOverride?.explicit ?? hasExplicitThreadId ?? false) || !!parentConfig?.hasExplicitThreadId;
|
|
193
223
|
const [internalModalOpen, setInternalModalOpen] = (0, react.useState)(isModalDefaultOpen ?? true);
|
|
194
224
|
const hasExplicitDefault = isModalDefaultOpen !== void 0;
|
|
195
225
|
const setAndSync = (0, react.useCallback)((open) => {
|
|
@@ -208,20 +238,113 @@ const CopilotChatConfigurationProvider = ({ children, labels, agentId, threadId,
|
|
|
208
238
|
}, [parentConfig?.isModalOpen, hasExplicitDefault]);
|
|
209
239
|
const resolvedIsModalOpen = hasExplicitDefault ? internalModalOpen : parentConfig?.isModalOpen ?? internalModalOpen;
|
|
210
240
|
const resolvedSetModalOpen = hasExplicitDefault ? setAndSync : parentConfig?.setModalOpen ?? setInternalModalOpen;
|
|
241
|
+
const [ownDrawerOpen, setOwnDrawerOpen] = (0, react.useState)(false);
|
|
242
|
+
const [ownDrawerCount, setOwnDrawerCount] = (0, react.useState)(0);
|
|
243
|
+
const modalCloseRef = (0, react.useRef)(() => {});
|
|
244
|
+
modalCloseRef.current = resolvedSetModalOpen;
|
|
245
|
+
const registeredModalClosersRef = (0, react.useRef)([]);
|
|
246
|
+
const ownRegisterModalCloser = (0, react.useCallback)((closeModal) => {
|
|
247
|
+
registeredModalClosersRef.current.push(closeModal);
|
|
248
|
+
return () => {
|
|
249
|
+
registeredModalClosersRef.current = registeredModalClosersRef.current.filter((entry) => entry !== closeModal);
|
|
250
|
+
};
|
|
251
|
+
}, []);
|
|
252
|
+
const ownSetDrawerOpen = (0, react.useCallback)((open) => {
|
|
253
|
+
setOwnDrawerOpen(open);
|
|
254
|
+
if (open && isMobileViewport()) {
|
|
255
|
+
const registered = registeredModalClosersRef.current;
|
|
256
|
+
(registered.length > 0 ? registered[registered.length - 1] : modalCloseRef.current)(false);
|
|
257
|
+
}
|
|
258
|
+
}, []);
|
|
259
|
+
const ownRegisterDrawer = (0, react.useCallback)(() => {
|
|
260
|
+
setOwnDrawerCount((count) => count + 1);
|
|
261
|
+
return () => {
|
|
262
|
+
setOwnDrawerCount((count) => Math.max(0, count - 1));
|
|
263
|
+
};
|
|
264
|
+
}, []);
|
|
265
|
+
const resolvedDrawerOpen = parentConfig ? parentConfig.drawerOpen : ownDrawerOpen;
|
|
266
|
+
const resolvedSetDrawerOpen = parentConfig ? parentConfig.setDrawerOpen : ownSetDrawerOpen;
|
|
267
|
+
const resolvedDrawerRegistered = parentConfig ? parentConfig.drawerRegistered : ownDrawerCount > 0;
|
|
268
|
+
const resolvedRegisterDrawer = parentConfig ? parentConfig.registerDrawer : ownRegisterDrawer;
|
|
269
|
+
const resolvedRegisterModalCloser = parentConfig ? parentConfig.ɵregisterModalCloser : ownRegisterModalCloser;
|
|
270
|
+
(0, react.useEffect)(() => {
|
|
271
|
+
if (!hasExplicitDefault) return;
|
|
272
|
+
return resolvedRegisterModalCloser(resolvedSetModalOpen);
|
|
273
|
+
}, [
|
|
274
|
+
hasExplicitDefault,
|
|
275
|
+
resolvedRegisterModalCloser,
|
|
276
|
+
resolvedSetModalOpen
|
|
277
|
+
]);
|
|
278
|
+
const isThreadIdControlledRef = (0, react.useRef)(isThreadIdControlled);
|
|
279
|
+
isThreadIdControlledRef.current = isThreadIdControlled;
|
|
280
|
+
const ownSetActiveThreadId = (0, react.useCallback)((id, options) => {
|
|
281
|
+
setActiveThreadOverride({
|
|
282
|
+
threadId: id,
|
|
283
|
+
explicit: options?.explicit ?? true
|
|
284
|
+
});
|
|
285
|
+
}, []);
|
|
286
|
+
const ownStartNewThread = (0, react.useCallback)(() => {
|
|
287
|
+
setActiveThreadOverride({
|
|
288
|
+
threadId: (0, _copilotkit_shared.randomUUID)(),
|
|
289
|
+
explicit: false
|
|
290
|
+
});
|
|
291
|
+
}, []);
|
|
292
|
+
const parentSetActiveThreadId = parentConfig?.setActiveThreadId;
|
|
293
|
+
const parentStartNewThread = parentConfig?.startNewThread;
|
|
294
|
+
const resolvedSetActiveThreadId = (0, react.useCallback)((id, options) => {
|
|
295
|
+
if (isThreadIdControlledRef.current) {
|
|
296
|
+
console.warn("[CopilotKit] Ignoring setActiveThreadId(): threadId is controlled via the `threadId` prop on CopilotChatConfigurationProvider.");
|
|
297
|
+
return;
|
|
298
|
+
}
|
|
299
|
+
if (parentSetActiveThreadId) {
|
|
300
|
+
parentSetActiveThreadId(id, options);
|
|
301
|
+
return;
|
|
302
|
+
}
|
|
303
|
+
ownSetActiveThreadId(id, options);
|
|
304
|
+
}, [parentSetActiveThreadId, ownSetActiveThreadId]);
|
|
305
|
+
const resolvedStartNewThread = (0, react.useCallback)(() => {
|
|
306
|
+
if (isThreadIdControlledRef.current) {
|
|
307
|
+
console.warn("[CopilotKit] Ignoring startNewThread(): threadId is controlled via the `threadId` prop on CopilotChatConfigurationProvider.");
|
|
308
|
+
return;
|
|
309
|
+
}
|
|
310
|
+
if (parentStartNewThread) {
|
|
311
|
+
parentStartNewThread();
|
|
312
|
+
return;
|
|
313
|
+
}
|
|
314
|
+
ownStartNewThread();
|
|
315
|
+
}, [parentStartNewThread, ownStartNewThread]);
|
|
316
|
+
const setModalOpenWithDrawerExclusion = (0, react.useCallback)((open) => {
|
|
317
|
+
if (open && isMobileViewport()) resolvedSetDrawerOpen(false);
|
|
318
|
+
resolvedSetModalOpen(open);
|
|
319
|
+
}, [resolvedSetModalOpen, resolvedSetDrawerOpen]);
|
|
211
320
|
const configurationValue = (0, react.useMemo)(() => ({
|
|
212
321
|
labels: mergedLabels,
|
|
213
322
|
agentId: resolvedAgentId,
|
|
214
323
|
threadId: resolvedThreadId,
|
|
215
324
|
hasExplicitThreadId: resolvedHasExplicitThreadId,
|
|
216
325
|
isModalOpen: resolvedIsModalOpen,
|
|
217
|
-
setModalOpen:
|
|
326
|
+
setModalOpen: setModalOpenWithDrawerExclusion,
|
|
327
|
+
drawerOpen: resolvedDrawerOpen,
|
|
328
|
+
setDrawerOpen: resolvedSetDrawerOpen,
|
|
329
|
+
drawerRegistered: resolvedDrawerRegistered,
|
|
330
|
+
registerDrawer: resolvedRegisterDrawer,
|
|
331
|
+
ɵregisterModalCloser: resolvedRegisterModalCloser,
|
|
332
|
+
setActiveThreadId: resolvedSetActiveThreadId,
|
|
333
|
+
startNewThread: resolvedStartNewThread
|
|
218
334
|
}), [
|
|
219
335
|
mergedLabels,
|
|
220
336
|
resolvedAgentId,
|
|
221
337
|
resolvedThreadId,
|
|
222
338
|
resolvedHasExplicitThreadId,
|
|
223
339
|
resolvedIsModalOpen,
|
|
224
|
-
|
|
340
|
+
setModalOpenWithDrawerExclusion,
|
|
341
|
+
resolvedDrawerOpen,
|
|
342
|
+
resolvedSetDrawerOpen,
|
|
343
|
+
resolvedDrawerRegistered,
|
|
344
|
+
resolvedRegisterDrawer,
|
|
345
|
+
resolvedRegisterModalCloser,
|
|
346
|
+
resolvedSetActiveThreadId,
|
|
347
|
+
resolvedStartNewThread
|
|
225
348
|
]);
|
|
226
349
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CopilotChatConfiguration.Provider, {
|
|
227
350
|
value: configurationValue,
|
|
@@ -1142,7 +1265,7 @@ function CopilotChatInput({ mode = "input", onSubmitMessage, onStop, isRunning =
|
|
|
1142
1265
|
},
|
|
1143
1266
|
...props,
|
|
1144
1267
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1145
|
-
className: "cpk:max-w-3xl cpk:mx-auto cpk:py-0 cpk:px-4 cpk:
|
|
1268
|
+
className: "cpk:max-w-3xl cpk:mx-auto cpk:py-0 cpk:px-4 cpk:@3xl:px-0 cpk:[div[data-sidebar-chat]_&]:px-8 cpk:[div[data-popup-chat]_&]:px-4 cpk:pointer-events-auto",
|
|
1146
1269
|
children: inputPill
|
|
1147
1270
|
}), shouldShowDisclaimer && BoundDisclaimer]
|
|
1148
1271
|
});
|
|
@@ -1428,309 +1551,27 @@ const useCopilotKit = () => {
|
|
|
1428
1551
|
const [, forceUpdate] = (0, react.useReducer)((x) => x + 1, 0);
|
|
1429
1552
|
if (!context) throw new Error("useCopilotKit must be used within CopilotKitProvider");
|
|
1430
1553
|
(0, react.useEffect)(() => {
|
|
1431
|
-
const subscription = context.copilotkit.subscribe({
|
|
1432
|
-
onRuntimeConnectionStatusChanged: () => {
|
|
1433
|
-
forceUpdate();
|
|
1434
|
-
},
|
|
1435
|
-
onHeadersChanged: () => {
|
|
1436
|
-
forceUpdate();
|
|
1437
|
-
}
|
|
1438
|
-
});
|
|
1439
|
-
return () => {
|
|
1440
|
-
subscription.unsubscribe();
|
|
1441
|
-
};
|
|
1442
|
-
}, []);
|
|
1443
|
-
return context;
|
|
1444
|
-
};
|
|
1445
|
-
const LicenseContext = (0, react.createContext)({
|
|
1446
|
-
status: null,
|
|
1447
|
-
license: null,
|
|
1448
|
-
checkFeature: () => true,
|
|
1449
|
-
getLimit: () => null
|
|
1450
|
-
});
|
|
1451
|
-
const useLicenseContext = () => (0, react.useContext)(LicenseContext);
|
|
1452
|
-
|
|
1453
|
-
//#endregion
|
|
1454
|
-
//#region src/v2/types/defineToolCallRenderer.ts
|
|
1455
|
-
function defineToolCallRenderer(def) {
|
|
1456
|
-
const argsSchema = def.name === "*" && !def.args ? zod.z.any() : def.args;
|
|
1457
|
-
return {
|
|
1458
|
-
name: def.name,
|
|
1459
|
-
args: argsSchema,
|
|
1460
|
-
render: def.render,
|
|
1461
|
-
...def.agentId ? { agentId: def.agentId } : {}
|
|
1462
|
-
};
|
|
1463
|
-
}
|
|
1464
|
-
|
|
1465
|
-
//#endregion
|
|
1466
|
-
//#region src/v2/hooks/use-render-tool.tsx
|
|
1467
|
-
const EMPTY_DEPS$1 = [];
|
|
1468
|
-
/**
|
|
1469
|
-
* Registers a renderer entry in CopilotKit's `renderToolCalls` registry.
|
|
1470
|
-
*
|
|
1471
|
-
* Key behavior:
|
|
1472
|
-
* - deduplicates by `agentId:name` (latest registration wins),
|
|
1473
|
-
* - keeps renderer entries on cleanup so historical chat tool calls can still render,
|
|
1474
|
-
* - refreshes registration when `deps` change.
|
|
1475
|
-
*
|
|
1476
|
-
* @typeParam S - Schema type describing tool call parameters.
|
|
1477
|
-
* @param config - Renderer config for wildcard or named tools.
|
|
1478
|
-
* @param deps - Optional dependencies to refresh registration.
|
|
1479
|
-
*
|
|
1480
|
-
* @example
|
|
1481
|
-
* ```tsx
|
|
1482
|
-
* useRenderTool(
|
|
1483
|
-
* {
|
|
1484
|
-
* name: "searchDocs",
|
|
1485
|
-
* parameters: z.object({ query: z.string() }),
|
|
1486
|
-
* render: ({ status, parameters, result }) => {
|
|
1487
|
-
* if (status === "executing") return <div>Searching {parameters.query}</div>;
|
|
1488
|
-
* if (status === "complete") return <div>{result}</div>;
|
|
1489
|
-
* return <div>Preparing...</div>;
|
|
1490
|
-
* },
|
|
1491
|
-
* },
|
|
1492
|
-
* [],
|
|
1493
|
-
* );
|
|
1494
|
-
* ```
|
|
1495
|
-
*
|
|
1496
|
-
* @example
|
|
1497
|
-
* ```tsx
|
|
1498
|
-
* useRenderTool(
|
|
1499
|
-
* {
|
|
1500
|
-
* name: "summarize",
|
|
1501
|
-
* parameters: z.object({ text: z.string() }),
|
|
1502
|
-
* agentId: "research-agent",
|
|
1503
|
-
* render: ({ name, status }) => <div>{name}: {status}</div>,
|
|
1504
|
-
* },
|
|
1505
|
-
* [selectedAgentId],
|
|
1506
|
-
* );
|
|
1507
|
-
* ```
|
|
1508
|
-
*/
|
|
1509
|
-
function useRenderTool(config, deps) {
|
|
1510
|
-
const { copilotkit } = useCopilotKit();
|
|
1511
|
-
const extraDeps = deps ?? EMPTY_DEPS$1;
|
|
1512
|
-
(0, react.useEffect)(() => {
|
|
1513
|
-
const renderer = config.name === "*" && !config.parameters ? defineToolCallRenderer({
|
|
1514
|
-
name: "*",
|
|
1515
|
-
render: (props) => config.render({
|
|
1516
|
-
...props,
|
|
1517
|
-
parameters: props.args
|
|
1518
|
-
}),
|
|
1519
|
-
...config.agentId ? { agentId: config.agentId } : {}
|
|
1520
|
-
}) : defineToolCallRenderer({
|
|
1521
|
-
name: config.name,
|
|
1522
|
-
args: config.parameters,
|
|
1523
|
-
render: (props) => {
|
|
1524
|
-
if (props.status === _copilotkit_core.ToolCallStatus.InProgress) return config.render({
|
|
1525
|
-
...props,
|
|
1526
|
-
parameters: props.args
|
|
1527
|
-
});
|
|
1528
|
-
if (props.status === _copilotkit_core.ToolCallStatus.Executing) return config.render({
|
|
1529
|
-
...props,
|
|
1530
|
-
parameters: props.args
|
|
1531
|
-
});
|
|
1532
|
-
return config.render({
|
|
1533
|
-
...props,
|
|
1534
|
-
parameters: props.args
|
|
1535
|
-
});
|
|
1536
|
-
},
|
|
1537
|
-
...config.agentId ? { agentId: config.agentId } : {}
|
|
1538
|
-
});
|
|
1539
|
-
copilotkit.addHookRenderToolCall(renderer);
|
|
1540
|
-
}, [
|
|
1541
|
-
config.name,
|
|
1542
|
-
copilotkit,
|
|
1543
|
-
JSON.stringify(extraDeps)
|
|
1544
|
-
]);
|
|
1545
|
-
}
|
|
1546
|
-
|
|
1547
|
-
//#endregion
|
|
1548
|
-
//#region src/v2/hooks/use-default-render-tool.tsx
|
|
1549
|
-
/**
|
|
1550
|
-
* Module-level dedup set so an unknown status value only emits a console
|
|
1551
|
-
* warning the FIRST time we encounter it. Otherwise a stuck/unmapped status
|
|
1552
|
-
* would log on every re-render (potentially many per second).
|
|
1553
|
-
*/
|
|
1554
|
-
const warnedUnknownStatuses = /* @__PURE__ */ new Set();
|
|
1555
|
-
/**
|
|
1556
|
-
* Map a {@link ToolCallStatus} enum value to the documented string-union
|
|
1557
|
-
* status the {@link DefaultRenderProps} contract exposes. Unknown / future
|
|
1558
|
-
* enum members log a warning (once per distinct value) and fall back to
|
|
1559
|
-
* `"inProgress"`.
|
|
1560
|
-
*/
|
|
1561
|
-
function mapToolCallStatus(status) {
|
|
1562
|
-
switch (status) {
|
|
1563
|
-
case _copilotkit_core.ToolCallStatus.Complete: return "complete";
|
|
1564
|
-
case _copilotkit_core.ToolCallStatus.Executing: return "executing";
|
|
1565
|
-
case _copilotkit_core.ToolCallStatus.InProgress: return "inProgress";
|
|
1566
|
-
default: {
|
|
1567
|
-
const key = String(status);
|
|
1568
|
-
if (!warnedUnknownStatuses.has(key)) {
|
|
1569
|
-
warnedUnknownStatuses.add(key);
|
|
1570
|
-
console.warn(`[CopilotKit] Unknown ToolCallStatus "${key}" in default tool-call renderer; falling back to "inProgress".`);
|
|
1571
|
-
}
|
|
1572
|
-
return "inProgress";
|
|
1573
|
-
}
|
|
1574
|
-
}
|
|
1575
|
-
}
|
|
1576
|
-
/**
|
|
1577
|
-
* Convert the framework-internal renderer props (`args`, enum status) into
|
|
1578
|
-
* the documented {@link DefaultRenderProps} shape (`parameters`, string-union
|
|
1579
|
-
* status) so a user `config.render` always sees the documented contract.
|
|
1580
|
-
*/
|
|
1581
|
-
function adaptRendererProps(props) {
|
|
1582
|
-
return {
|
|
1583
|
-
name: props.name,
|
|
1584
|
-
toolCallId: props.toolCallId,
|
|
1585
|
-
parameters: props.args,
|
|
1586
|
-
status: mapToolCallStatus(props.status),
|
|
1587
|
-
result: props.result
|
|
1588
|
-
};
|
|
1589
|
-
}
|
|
1590
|
-
/**
|
|
1591
|
-
* Registers a wildcard (`"*"`) tool-call renderer via `useRenderTool`.
|
|
1592
|
-
*
|
|
1593
|
-
* - Call with no config to use CopilotKit's built-in default tool-call card.
|
|
1594
|
-
* - Pass `config.render` to replace the default UI with your own fallback renderer.
|
|
1595
|
-
*
|
|
1596
|
-
* This is useful when you want a generic renderer for tools that do not have a
|
|
1597
|
-
* dedicated `useRenderTool({ name: "..." })` registration.
|
|
1598
|
-
*
|
|
1599
|
-
* @param config - Optional custom wildcard render function.
|
|
1600
|
-
* @param deps - Optional dependencies to refresh registration.
|
|
1601
|
-
*
|
|
1602
|
-
* @example
|
|
1603
|
-
* ```tsx
|
|
1604
|
-
* useDefaultRenderTool();
|
|
1605
|
-
* ```
|
|
1606
|
-
*
|
|
1607
|
-
* @example
|
|
1608
|
-
* ```tsx
|
|
1609
|
-
* useDefaultRenderTool({
|
|
1610
|
-
* render: ({ name, status }) => <div>{name}: {status}</div>,
|
|
1611
|
-
* });
|
|
1612
|
-
* ```
|
|
1613
|
-
*
|
|
1614
|
-
* @example
|
|
1615
|
-
* ```tsx
|
|
1616
|
-
* useDefaultRenderTool(
|
|
1617
|
-
* {
|
|
1618
|
-
* render: ({ name, result }) => (
|
|
1619
|
-
* <ToolEventRow title={name} payload={result} compact={compactMode} />
|
|
1620
|
-
* ),
|
|
1621
|
-
* },
|
|
1622
|
-
* [compactMode],
|
|
1623
|
-
* );
|
|
1624
|
-
* ```
|
|
1625
|
-
*/
|
|
1626
|
-
function useDefaultRenderTool(config, deps) {
|
|
1627
|
-
const userRender = config?.render;
|
|
1628
|
-
useRenderTool({
|
|
1629
|
-
name: "*",
|
|
1630
|
-
render: userRender ? (raw) => userRender(adaptRendererProps(raw)) : (raw) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DefaultToolCallRenderer, { ...adaptRendererProps(raw) })
|
|
1631
|
-
}, deps);
|
|
1632
|
-
}
|
|
1633
|
-
/**
|
|
1634
|
-
* Guarded JSON.stringify used inside the expanded `<pre>` blocks. A circular
|
|
1635
|
-
* reference would otherwise crash the entire React tree on render.
|
|
1636
|
-
*/
|
|
1637
|
-
function safeStringifyForPre(value) {
|
|
1638
|
-
try {
|
|
1639
|
-
return JSON.stringify(value, null, 2);
|
|
1640
|
-
} catch (err) {
|
|
1641
|
-
console.warn("[CopilotKit] Failed to JSON.stringify tool-call payload for default renderer; falling back to String():", err);
|
|
1642
|
-
try {
|
|
1643
|
-
return String(value);
|
|
1644
|
-
} catch (innerErr) {
|
|
1645
|
-
console.warn("[CopilotKit] safeStringifyForPre: value could not be stringified:", innerErr);
|
|
1646
|
-
return "[unserializable]";
|
|
1647
|
-
}
|
|
1648
|
-
}
|
|
1649
|
-
}
|
|
1650
|
-
function DefaultToolCallRenderer({ name, toolCallId, parameters, status, result }) {
|
|
1651
|
-
const [isExpanded, setIsExpanded] = (0, react.useState)(false);
|
|
1652
|
-
const isActive = status === "inProgress" || status === "executing";
|
|
1653
|
-
const isComplete = status === "complete";
|
|
1654
|
-
const statusLabel = isActive ? "Running" : isComplete ? "Done" : status;
|
|
1655
|
-
const dotClassName = isActive ? "cpk:bg-amber-500" : isComplete ? "cpk:bg-emerald-500" : "cpk:bg-zinc-400";
|
|
1656
|
-
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";
|
|
1657
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1658
|
-
"data-testid": "copilot-tool-render",
|
|
1659
|
-
"data-tool-name": name,
|
|
1660
|
-
"data-tool-call-id": toolCallId,
|
|
1661
|
-
"data-status": status,
|
|
1662
|
-
"data-args": safeStringifyForAttr(parameters),
|
|
1663
|
-
"data-result": safeStringifyForAttr(result),
|
|
1664
|
-
className: "cpk:mt-2 cpk:pb-2",
|
|
1665
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1666
|
-
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",
|
|
1667
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
1668
|
-
type: "button",
|
|
1669
|
-
"aria-expanded": isExpanded,
|
|
1670
|
-
onClick: () => setIsExpanded(!isExpanded),
|
|
1671
|
-
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",
|
|
1672
|
-
style: { font: "inherit" },
|
|
1673
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1674
|
-
className: "cpk:flex cpk:min-w-0 cpk:items-center cpk:gap-2",
|
|
1675
|
-
children: [
|
|
1676
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
|
|
1677
|
-
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" : ""}`,
|
|
1678
|
-
fill: "none",
|
|
1679
|
-
viewBox: "0 0 24 24",
|
|
1680
|
-
strokeWidth: 2,
|
|
1681
|
-
stroke: "currentColor",
|
|
1682
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
|
|
1683
|
-
strokeLinecap: "round",
|
|
1684
|
-
strokeLinejoin: "round",
|
|
1685
|
-
d: "M8.25 4.5l7.5 7.5-7.5 7.5"
|
|
1686
|
-
})
|
|
1687
|
-
}),
|
|
1688
|
-
/* @__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}` }),
|
|
1689
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1690
|
-
"data-testid": "copilot-tool-render-name",
|
|
1691
|
-
className: "cpk:truncate cpk:text-[13px] cpk:font-semibold cpk:text-zinc-900 cpk:dark:text-zinc-100",
|
|
1692
|
-
children: name
|
|
1693
|
-
})
|
|
1694
|
-
]
|
|
1695
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1696
|
-
"data-testid": "copilot-tool-render-status",
|
|
1697
|
-
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}`,
|
|
1698
|
-
children: statusLabel
|
|
1699
|
-
})]
|
|
1700
|
-
}), isExpanded && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1701
|
-
className: "cpk:mt-3 cpk:grid cpk:gap-3",
|
|
1702
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1703
|
-
className: "cpk:text-[10px] cpk:uppercase cpk:text-zinc-500 cpk:dark:text-zinc-400",
|
|
1704
|
-
children: "Arguments"
|
|
1705
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("pre", {
|
|
1706
|
-
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",
|
|
1707
|
-
children: safeStringifyForPre(parameters ?? {})
|
|
1708
|
-
})] }), result !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1709
|
-
className: "cpk:text-[10px] cpk:uppercase cpk:text-zinc-500 cpk:dark:text-zinc-400",
|
|
1710
|
-
children: "Result"
|
|
1711
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("pre", {
|
|
1712
|
-
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",
|
|
1713
|
-
children: typeof result === "string" ? result : safeStringifyForPre(result)
|
|
1714
|
-
})] })]
|
|
1715
|
-
})]
|
|
1716
|
-
})
|
|
1717
|
-
});
|
|
1718
|
-
}
|
|
1719
|
-
function safeStringifyForAttr(value) {
|
|
1720
|
-
if (value === void 0 || value === null) return "";
|
|
1721
|
-
if (typeof value === "string") return value;
|
|
1722
|
-
try {
|
|
1723
|
-
return JSON.stringify(value);
|
|
1724
|
-
} catch (err) {
|
|
1725
|
-
console.warn("[CopilotKit] Failed to JSON.stringify tool-call payload for data-* attribute; falling back to String():", err);
|
|
1726
|
-
try {
|
|
1727
|
-
return String(value);
|
|
1728
|
-
} catch (innerErr) {
|
|
1729
|
-
console.warn("[CopilotKit] safeStringifyForAttr: value could not be stringified:", innerErr);
|
|
1730
|
-
return "";
|
|
1731
|
-
}
|
|
1732
|
-
}
|
|
1733
|
-
}
|
|
1554
|
+
const subscription = context.copilotkit.subscribe({
|
|
1555
|
+
onRuntimeConnectionStatusChanged: () => {
|
|
1556
|
+
forceUpdate();
|
|
1557
|
+
},
|
|
1558
|
+
onHeadersChanged: () => {
|
|
1559
|
+
forceUpdate();
|
|
1560
|
+
}
|
|
1561
|
+
});
|
|
1562
|
+
return () => {
|
|
1563
|
+
subscription.unsubscribe();
|
|
1564
|
+
};
|
|
1565
|
+
}, []);
|
|
1566
|
+
return context;
|
|
1567
|
+
};
|
|
1568
|
+
const LicenseContext = (0, react.createContext)({
|
|
1569
|
+
status: null,
|
|
1570
|
+
license: null,
|
|
1571
|
+
checkFeature: () => true,
|
|
1572
|
+
getLimit: () => null
|
|
1573
|
+
});
|
|
1574
|
+
const useLicenseContext = () => (0, react.useContext)(LicenseContext);
|
|
1734
1575
|
|
|
1735
1576
|
//#endregion
|
|
1736
1577
|
//#region src/v2/hooks/use-render-tool-call.tsx
|
|
@@ -1786,10 +1627,13 @@ function useRenderToolCall() {
|
|
|
1786
1627
|
}, () => copilotkit.renderToolCalls, () => copilotkit.renderToolCalls);
|
|
1787
1628
|
return (0, react.useCallback)(({ toolCall, toolMessage }) => {
|
|
1788
1629
|
const exactMatches = renderToolCalls.filter((rc) => rc.name === toolCall.function.name);
|
|
1630
|
+
const renderConfig = exactMatches.find((rc) => rc.agentId === agentId) || exactMatches.find((rc) => !rc.agentId) || exactMatches[0] || renderToolCalls.find((rc) => rc.name === "*");
|
|
1631
|
+
if (!renderConfig) return null;
|
|
1632
|
+
const RenderComponent = renderConfig.render;
|
|
1789
1633
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ToolCallRenderer, {
|
|
1790
1634
|
toolCall,
|
|
1791
1635
|
toolMessage,
|
|
1792
|
-
RenderComponent
|
|
1636
|
+
RenderComponent,
|
|
1793
1637
|
isExecuting: executingToolCallIds.has(toolCall.id)
|
|
1794
1638
|
}, toolCall.id);
|
|
1795
1639
|
}, [
|
|
@@ -1798,15 +1642,6 @@ function useRenderToolCall() {
|
|
|
1798
1642
|
agentId
|
|
1799
1643
|
]);
|
|
1800
1644
|
}
|
|
1801
|
-
function defaultToolCallRenderAdapter(props) {
|
|
1802
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DefaultToolCallRenderer, {
|
|
1803
|
-
name: props.name,
|
|
1804
|
-
toolCallId: props.toolCallId,
|
|
1805
|
-
parameters: props.args,
|
|
1806
|
-
status: mapToolCallStatus(props.status),
|
|
1807
|
-
result: props.result
|
|
1808
|
-
});
|
|
1809
|
-
}
|
|
1810
1645
|
|
|
1811
1646
|
//#endregion
|
|
1812
1647
|
//#region src/v2/components/CopilotKitInspector.tsx
|
|
@@ -3288,7 +3123,7 @@ const A2UISurfaceContentSchema = zod.z.object({
|
|
|
3288
3123
|
...A2UILifecycleFields
|
|
3289
3124
|
}).passthrough();
|
|
3290
3125
|
function createA2UIMessageRenderer(options) {
|
|
3291
|
-
const { theme, catalog, loadingComponent, recovery } = options;
|
|
3126
|
+
const { theme, catalog, loadingComponent, recovery, onAction } = options;
|
|
3292
3127
|
const showAfterMs = recovery?.showAfterMs ?? 2e3;
|
|
3293
3128
|
const showAfterAttempts = recovery?.showAfterAttempts ?? 2;
|
|
3294
3129
|
const optionDebugExposure = recovery?.debugExposure ?? "collapsed";
|
|
@@ -3364,6 +3199,7 @@ function createA2UIMessageRenderer(options) {
|
|
|
3364
3199
|
agent,
|
|
3365
3200
|
copilotkit,
|
|
3366
3201
|
catalog,
|
|
3202
|
+
onAction,
|
|
3367
3203
|
onReady: markSurfaceReady
|
|
3368
3204
|
}, surfaceId))
|
|
3369
3205
|
});
|
|
@@ -3384,29 +3220,60 @@ function createA2UIMessageRenderer(options) {
|
|
|
3384
3220
|
};
|
|
3385
3221
|
}
|
|
3386
3222
|
/**
|
|
3223
|
+
* Orchestrates a single A2UI user action: runs the optional `onAction`
|
|
3224
|
+
* interceptor first, then forwards to the agent unless the interceptor
|
|
3225
|
+
* suppressed it (returned `null`). Exported for unit testing; the wiring lives
|
|
3226
|
+
* in {@link ReactSurfaceHost}.
|
|
3227
|
+
*/
|
|
3228
|
+
async function runA2UIAction({ message, agent, copilotkit, onAction }) {
|
|
3229
|
+
if (!agent) return;
|
|
3230
|
+
const action = message.userAction;
|
|
3231
|
+
const forward = async (forwardAction) => {
|
|
3232
|
+
const a2uiAction = forwardAction !== void 0 ? {
|
|
3233
|
+
...message,
|
|
3234
|
+
userAction: forwardAction
|
|
3235
|
+
} : message;
|
|
3236
|
+
try {
|
|
3237
|
+
copilotkit.setProperties({
|
|
3238
|
+
...copilotkit.properties,
|
|
3239
|
+
a2uiAction
|
|
3240
|
+
});
|
|
3241
|
+
await copilotkit.runAgent({ agent });
|
|
3242
|
+
} finally {
|
|
3243
|
+
if (copilotkit.properties) {
|
|
3244
|
+
const { a2uiAction: _omit, ...rest } = copilotkit.properties;
|
|
3245
|
+
copilotkit.setProperties(rest);
|
|
3246
|
+
}
|
|
3247
|
+
}
|
|
3248
|
+
};
|
|
3249
|
+
if (onAction && action) {
|
|
3250
|
+
const result = await onAction(action, forward);
|
|
3251
|
+
if (result === null) return;
|
|
3252
|
+
if (result) {
|
|
3253
|
+
await forward(result);
|
|
3254
|
+
return;
|
|
3255
|
+
}
|
|
3256
|
+
}
|
|
3257
|
+
await forward();
|
|
3258
|
+
}
|
|
3259
|
+
/**
|
|
3387
3260
|
* Renders a single A2UI surface using the React renderer.
|
|
3388
3261
|
* Wraps A2UIProvider + A2UIRenderer and bridges actions back to CopilotKit.
|
|
3389
3262
|
*/
|
|
3390
|
-
function ReactSurfaceHost({ surfaceId, operations, theme, agent, copilotkit, catalog, onReady }) {
|
|
3263
|
+
function ReactSurfaceHost({ surfaceId, operations, theme, agent, copilotkit, catalog, onAction, onReady }) {
|
|
3391
3264
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3392
3265
|
className: "cpk:flex cpk:w-full cpk:flex-none cpk:flex-col cpk:gap-4",
|
|
3393
3266
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_copilotkit_a2ui_renderer.A2UIProvider, {
|
|
3394
|
-
onAction: (0, react.useCallback)(
|
|
3395
|
-
|
|
3396
|
-
|
|
3397
|
-
|
|
3398
|
-
|
|
3399
|
-
|
|
3400
|
-
|
|
3401
|
-
|
|
3402
|
-
|
|
3403
|
-
|
|
3404
|
-
if (copilotkit.properties) {
|
|
3405
|
-
const { a2uiAction, ...rest } = copilotkit.properties;
|
|
3406
|
-
copilotkit.setProperties(rest);
|
|
3407
|
-
}
|
|
3408
|
-
}
|
|
3409
|
-
}, [agent, copilotkit]),
|
|
3267
|
+
onAction: (0, react.useCallback)((message) => runA2UIAction({
|
|
3268
|
+
message,
|
|
3269
|
+
agent,
|
|
3270
|
+
copilotkit,
|
|
3271
|
+
onAction
|
|
3272
|
+
}), [
|
|
3273
|
+
agent,
|
|
3274
|
+
copilotkit,
|
|
3275
|
+
onAction
|
|
3276
|
+
]),
|
|
3410
3277
|
theme,
|
|
3411
3278
|
catalog,
|
|
3412
3279
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(SurfaceMessageProcessor, {
|
|
@@ -3477,6 +3344,18 @@ function getOperationSurfaceId(operation) {
|
|
|
3477
3344
|
return operation?.createSurface?.surfaceId ?? operation?.updateComponents?.surfaceId ?? operation?.updateDataModel?.surfaceId ?? operation?.deleteSurface?.surfaceId ?? null;
|
|
3478
3345
|
}
|
|
3479
3346
|
|
|
3347
|
+
//#endregion
|
|
3348
|
+
//#region src/v2/types/defineToolCallRenderer.ts
|
|
3349
|
+
function defineToolCallRenderer(def) {
|
|
3350
|
+
const argsSchema = def.name === "*" && !def.args ? zod.z.any() : def.args;
|
|
3351
|
+
return {
|
|
3352
|
+
name: def.name,
|
|
3353
|
+
args: argsSchema,
|
|
3354
|
+
render: def.render,
|
|
3355
|
+
...def.agentId ? { agentId: def.agentId } : {}
|
|
3356
|
+
};
|
|
3357
|
+
}
|
|
3358
|
+
|
|
3480
3359
|
//#endregion
|
|
3481
3360
|
//#region src/v2/a2ui/A2UIToolCallRenderer.tsx
|
|
3482
3361
|
/**
|
|
@@ -4030,10 +3909,10 @@ function useRenderActivityMessage() {
|
|
|
4030
3909
|
|
|
4031
3910
|
//#endregion
|
|
4032
3911
|
//#region src/v2/hooks/use-frontend-tool.tsx
|
|
4033
|
-
const EMPTY_DEPS = [];
|
|
3912
|
+
const EMPTY_DEPS$1 = [];
|
|
4034
3913
|
function useFrontendTool(tool, deps) {
|
|
4035
3914
|
const { copilotkit } = useCopilotKit();
|
|
4036
|
-
const extraDeps = deps ?? EMPTY_DEPS;
|
|
3915
|
+
const extraDeps = deps ?? EMPTY_DEPS$1;
|
|
4037
3916
|
(0, react.useEffect)(() => {
|
|
4038
3917
|
const name = tool.name;
|
|
4039
3918
|
if (copilotkit.getTool({
|
|
@@ -4064,70 +3943,340 @@ function useFrontendTool(tool, deps) {
|
|
|
4064
3943
|
//#endregion
|
|
4065
3944
|
//#region src/v2/hooks/use-component.tsx
|
|
4066
3945
|
/**
|
|
4067
|
-
* Registers a React component as a frontend tool renderer in chat.
|
|
4068
|
-
*
|
|
4069
|
-
* This hook is a convenience wrapper around `useFrontendTool` that:
|
|
4070
|
-
* - builds a model-facing tool description,
|
|
4071
|
-
* - forwards optional schema parameters (any Standard Schema V1 compatible library),
|
|
4072
|
-
* - renders your component with tool call parameters.
|
|
3946
|
+
* Registers a React component as a frontend tool renderer in chat.
|
|
3947
|
+
*
|
|
3948
|
+
* This hook is a convenience wrapper around `useFrontendTool` that:
|
|
3949
|
+
* - builds a model-facing tool description,
|
|
3950
|
+
* - forwards optional schema parameters (any Standard Schema V1 compatible library),
|
|
3951
|
+
* - renders your component with tool call parameters.
|
|
3952
|
+
*
|
|
3953
|
+
* Use this when you want to display a typed visual component for a tool call
|
|
3954
|
+
* without manually wiring a full frontend tool object.
|
|
3955
|
+
*
|
|
3956
|
+
* When `parameters` is provided, render props are inferred from the schema.
|
|
3957
|
+
* When omitted, the render component may accept any props.
|
|
3958
|
+
*
|
|
3959
|
+
* @typeParam TSchema - Schema describing tool parameters, or `undefined` when no schema is given.
|
|
3960
|
+
* @param config - Tool registration config.
|
|
3961
|
+
* @param deps - Optional dependencies to refresh registration (same semantics as `useEffect`).
|
|
3962
|
+
*
|
|
3963
|
+
* @example
|
|
3964
|
+
* ```tsx
|
|
3965
|
+
* // Without parameters — render accepts any props
|
|
3966
|
+
* useComponent({
|
|
3967
|
+
* name: "showGreeting",
|
|
3968
|
+
* render: ({ message }: { message: string }) => <div>{message}</div>,
|
|
3969
|
+
* });
|
|
3970
|
+
* ```
|
|
3971
|
+
*
|
|
3972
|
+
* @example
|
|
3973
|
+
* ```tsx
|
|
3974
|
+
* // With parameters — render props inferred from schema
|
|
3975
|
+
* useComponent({
|
|
3976
|
+
* name: "showWeatherCard",
|
|
3977
|
+
* parameters: z.object({ city: z.string() }),
|
|
3978
|
+
* render: ({ city }) => <div>{city}</div>,
|
|
3979
|
+
* });
|
|
3980
|
+
* ```
|
|
3981
|
+
*
|
|
3982
|
+
* @example
|
|
3983
|
+
* ```tsx
|
|
3984
|
+
* useComponent(
|
|
3985
|
+
* {
|
|
3986
|
+
* name: "renderProfile",
|
|
3987
|
+
* parameters: z.object({ userId: z.string() }),
|
|
3988
|
+
* render: ProfileCard,
|
|
3989
|
+
* agentId: "support-agent",
|
|
3990
|
+
* },
|
|
3991
|
+
* [selectedAgentId],
|
|
3992
|
+
* );
|
|
3993
|
+
* ```
|
|
3994
|
+
*/
|
|
3995
|
+
function useComponent(config, deps) {
|
|
3996
|
+
const prefix = `Use this tool to display the "${config.name}" component in the chat. This tool renders a visual UI component for the user.`;
|
|
3997
|
+
const fullDescription = config.description ? `${prefix}\n\n${config.description}` : prefix;
|
|
3998
|
+
useFrontendTool({
|
|
3999
|
+
name: config.name,
|
|
4000
|
+
description: fullDescription,
|
|
4001
|
+
parameters: config.parameters,
|
|
4002
|
+
render: ({ args }) => {
|
|
4003
|
+
const Component = config.render;
|
|
4004
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Component, { ...args });
|
|
4005
|
+
},
|
|
4006
|
+
agentId: config.agentId,
|
|
4007
|
+
followUp: config.followUp
|
|
4008
|
+
}, deps);
|
|
4009
|
+
}
|
|
4010
|
+
|
|
4011
|
+
//#endregion
|
|
4012
|
+
//#region src/v2/hooks/use-render-tool.tsx
|
|
4013
|
+
const EMPTY_DEPS = [];
|
|
4014
|
+
/**
|
|
4015
|
+
* Registers a renderer entry in CopilotKit's `renderToolCalls` registry.
|
|
4016
|
+
*
|
|
4017
|
+
* Key behavior:
|
|
4018
|
+
* - deduplicates by `agentId:name` (latest registration wins),
|
|
4019
|
+
* - keeps renderer entries on cleanup so historical chat tool calls can still render,
|
|
4020
|
+
* - refreshes registration when `deps` change.
|
|
4021
|
+
*
|
|
4022
|
+
* @typeParam S - Schema type describing tool call parameters.
|
|
4023
|
+
* @param config - Renderer config for wildcard or named tools.
|
|
4024
|
+
* @param deps - Optional dependencies to refresh registration.
|
|
4025
|
+
*
|
|
4026
|
+
* @example
|
|
4027
|
+
* ```tsx
|
|
4028
|
+
* useRenderTool(
|
|
4029
|
+
* {
|
|
4030
|
+
* name: "searchDocs",
|
|
4031
|
+
* parameters: z.object({ query: z.string() }),
|
|
4032
|
+
* render: ({ status, parameters, result }) => {
|
|
4033
|
+
* if (status === "executing") return <div>Searching {parameters.query}</div>;
|
|
4034
|
+
* if (status === "complete") return <div>{result}</div>;
|
|
4035
|
+
* return <div>Preparing...</div>;
|
|
4036
|
+
* },
|
|
4037
|
+
* },
|
|
4038
|
+
* [],
|
|
4039
|
+
* );
|
|
4040
|
+
* ```
|
|
4041
|
+
*
|
|
4042
|
+
* @example
|
|
4043
|
+
* ```tsx
|
|
4044
|
+
* useRenderTool(
|
|
4045
|
+
* {
|
|
4046
|
+
* name: "summarize",
|
|
4047
|
+
* parameters: z.object({ text: z.string() }),
|
|
4048
|
+
* agentId: "research-agent",
|
|
4049
|
+
* render: ({ name, status }) => <div>{name}: {status}</div>,
|
|
4050
|
+
* },
|
|
4051
|
+
* [selectedAgentId],
|
|
4052
|
+
* );
|
|
4053
|
+
* ```
|
|
4054
|
+
*/
|
|
4055
|
+
function useRenderTool(config, deps) {
|
|
4056
|
+
const { copilotkit } = useCopilotKit();
|
|
4057
|
+
const extraDeps = deps ?? EMPTY_DEPS;
|
|
4058
|
+
(0, react.useEffect)(() => {
|
|
4059
|
+
const renderer = config.name === "*" && !config.parameters ? defineToolCallRenderer({
|
|
4060
|
+
name: "*",
|
|
4061
|
+
render: (props) => config.render({
|
|
4062
|
+
...props,
|
|
4063
|
+
parameters: props.args
|
|
4064
|
+
}),
|
|
4065
|
+
...config.agentId ? { agentId: config.agentId } : {}
|
|
4066
|
+
}) : defineToolCallRenderer({
|
|
4067
|
+
name: config.name,
|
|
4068
|
+
args: config.parameters,
|
|
4069
|
+
render: (props) => {
|
|
4070
|
+
if (props.status === _copilotkit_core.ToolCallStatus.InProgress) return config.render({
|
|
4071
|
+
...props,
|
|
4072
|
+
parameters: props.args
|
|
4073
|
+
});
|
|
4074
|
+
if (props.status === _copilotkit_core.ToolCallStatus.Executing) return config.render({
|
|
4075
|
+
...props,
|
|
4076
|
+
parameters: props.args
|
|
4077
|
+
});
|
|
4078
|
+
return config.render({
|
|
4079
|
+
...props,
|
|
4080
|
+
parameters: props.args
|
|
4081
|
+
});
|
|
4082
|
+
},
|
|
4083
|
+
...config.agentId ? { agentId: config.agentId } : {}
|
|
4084
|
+
});
|
|
4085
|
+
copilotkit.addHookRenderToolCall(renderer);
|
|
4086
|
+
}, [
|
|
4087
|
+
config.name,
|
|
4088
|
+
copilotkit,
|
|
4089
|
+
JSON.stringify(extraDeps)
|
|
4090
|
+
]);
|
|
4091
|
+
}
|
|
4092
|
+
|
|
4093
|
+
//#endregion
|
|
4094
|
+
//#region src/v2/hooks/use-default-render-tool.tsx
|
|
4095
|
+
/**
|
|
4096
|
+
* Module-level dedup set so an unknown status value only emits a console
|
|
4097
|
+
* warning the FIRST time we encounter it. Otherwise a stuck/unmapped status
|
|
4098
|
+
* would log on every re-render (potentially many per second).
|
|
4099
|
+
*/
|
|
4100
|
+
const warnedUnknownStatuses = /* @__PURE__ */ new Set();
|
|
4101
|
+
/**
|
|
4102
|
+
* Map a {@link ToolCallStatus} enum value to the documented string-union
|
|
4103
|
+
* status the {@link DefaultRenderProps} contract exposes. Unknown / future
|
|
4104
|
+
* enum members log a warning (once per distinct value) and fall back to
|
|
4105
|
+
* `"inProgress"`.
|
|
4106
|
+
*/
|
|
4107
|
+
function mapToolCallStatus(status) {
|
|
4108
|
+
switch (status) {
|
|
4109
|
+
case _copilotkit_core.ToolCallStatus.Complete: return "complete";
|
|
4110
|
+
case _copilotkit_core.ToolCallStatus.Executing: return "executing";
|
|
4111
|
+
case _copilotkit_core.ToolCallStatus.InProgress: return "inProgress";
|
|
4112
|
+
default: {
|
|
4113
|
+
const key = String(status);
|
|
4114
|
+
if (!warnedUnknownStatuses.has(key)) {
|
|
4115
|
+
warnedUnknownStatuses.add(key);
|
|
4116
|
+
console.warn(`[CopilotKit] Unknown ToolCallStatus "${key}" in default tool-call renderer; falling back to "inProgress".`);
|
|
4117
|
+
}
|
|
4118
|
+
return "inProgress";
|
|
4119
|
+
}
|
|
4120
|
+
}
|
|
4121
|
+
}
|
|
4122
|
+
/**
|
|
4123
|
+
* Convert the framework-internal renderer props (`args`, enum status) into
|
|
4124
|
+
* the documented {@link DefaultRenderProps} shape (`parameters`, string-union
|
|
4125
|
+
* status) so a user `config.render` always sees the documented contract.
|
|
4126
|
+
*/
|
|
4127
|
+
function adaptRendererProps(props) {
|
|
4128
|
+
return {
|
|
4129
|
+
name: props.name,
|
|
4130
|
+
toolCallId: props.toolCallId,
|
|
4131
|
+
parameters: props.args,
|
|
4132
|
+
status: mapToolCallStatus(props.status),
|
|
4133
|
+
result: props.result
|
|
4134
|
+
};
|
|
4135
|
+
}
|
|
4136
|
+
/**
|
|
4137
|
+
* Registers a wildcard (`"*"`) tool-call renderer via `useRenderTool`.
|
|
4073
4138
|
*
|
|
4074
|
-
*
|
|
4075
|
-
*
|
|
4139
|
+
* - Call with no config to use CopilotKit's built-in default tool-call card.
|
|
4140
|
+
* - Pass `config.render` to replace the default UI with your own fallback renderer.
|
|
4076
4141
|
*
|
|
4077
|
-
*
|
|
4078
|
-
*
|
|
4142
|
+
* This is useful when you want a generic renderer for tools that do not have a
|
|
4143
|
+
* dedicated `useRenderTool({ name: "..." })` registration.
|
|
4079
4144
|
*
|
|
4080
|
-
* @
|
|
4081
|
-
* @param
|
|
4082
|
-
* @param deps - Optional dependencies to refresh registration (same semantics as `useEffect`).
|
|
4145
|
+
* @param config - Optional custom wildcard render function.
|
|
4146
|
+
* @param deps - Optional dependencies to refresh registration.
|
|
4083
4147
|
*
|
|
4084
4148
|
* @example
|
|
4085
4149
|
* ```tsx
|
|
4086
|
-
*
|
|
4087
|
-
* useComponent({
|
|
4088
|
-
* name: "showGreeting",
|
|
4089
|
-
* render: ({ message }: { message: string }) => <div>{message}</div>,
|
|
4090
|
-
* });
|
|
4150
|
+
* useDefaultRenderTool();
|
|
4091
4151
|
* ```
|
|
4092
4152
|
*
|
|
4093
4153
|
* @example
|
|
4094
4154
|
* ```tsx
|
|
4095
|
-
*
|
|
4096
|
-
*
|
|
4097
|
-
* name: "showWeatherCard",
|
|
4098
|
-
* parameters: z.object({ city: z.string() }),
|
|
4099
|
-
* render: ({ city }) => <div>{city}</div>,
|
|
4155
|
+
* useDefaultRenderTool({
|
|
4156
|
+
* render: ({ name, status }) => <div>{name}: {status}</div>,
|
|
4100
4157
|
* });
|
|
4101
4158
|
* ```
|
|
4102
4159
|
*
|
|
4103
4160
|
* @example
|
|
4104
4161
|
* ```tsx
|
|
4105
|
-
*
|
|
4162
|
+
* useDefaultRenderTool(
|
|
4106
4163
|
* {
|
|
4107
|
-
*
|
|
4108
|
-
*
|
|
4109
|
-
*
|
|
4110
|
-
* agentId: "support-agent",
|
|
4164
|
+
* render: ({ name, result }) => (
|
|
4165
|
+
* <ToolEventRow title={name} payload={result} compact={compactMode} />
|
|
4166
|
+
* ),
|
|
4111
4167
|
* },
|
|
4112
|
-
* [
|
|
4168
|
+
* [compactMode],
|
|
4113
4169
|
* );
|
|
4114
4170
|
* ```
|
|
4115
4171
|
*/
|
|
4116
|
-
function
|
|
4117
|
-
const
|
|
4118
|
-
|
|
4119
|
-
|
|
4120
|
-
|
|
4121
|
-
description: fullDescription,
|
|
4122
|
-
parameters: config.parameters,
|
|
4123
|
-
render: ({ args }) => {
|
|
4124
|
-
const Component = config.render;
|
|
4125
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Component, { ...args });
|
|
4126
|
-
},
|
|
4127
|
-
agentId: config.agentId,
|
|
4128
|
-
followUp: config.followUp
|
|
4172
|
+
function useDefaultRenderTool(config, deps) {
|
|
4173
|
+
const userRender = config?.render;
|
|
4174
|
+
useRenderTool({
|
|
4175
|
+
name: "*",
|
|
4176
|
+
render: userRender ? (raw) => userRender(adaptRendererProps(raw)) : (raw) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DefaultToolCallRenderer, { ...adaptRendererProps(raw) })
|
|
4129
4177
|
}, deps);
|
|
4130
4178
|
}
|
|
4179
|
+
/**
|
|
4180
|
+
* Guarded JSON.stringify used inside the expanded `<pre>` blocks. A circular
|
|
4181
|
+
* reference would otherwise crash the entire React tree on render.
|
|
4182
|
+
*/
|
|
4183
|
+
function safeStringifyForPre(value) {
|
|
4184
|
+
try {
|
|
4185
|
+
return JSON.stringify(value, null, 2);
|
|
4186
|
+
} catch (err) {
|
|
4187
|
+
console.warn("[CopilotKit] Failed to JSON.stringify tool-call payload for default renderer; falling back to String():", err);
|
|
4188
|
+
try {
|
|
4189
|
+
return String(value);
|
|
4190
|
+
} catch (innerErr) {
|
|
4191
|
+
console.warn("[CopilotKit] safeStringifyForPre: value could not be stringified:", innerErr);
|
|
4192
|
+
return "[unserializable]";
|
|
4193
|
+
}
|
|
4194
|
+
}
|
|
4195
|
+
}
|
|
4196
|
+
function DefaultToolCallRenderer({ name, toolCallId, parameters, status, result }) {
|
|
4197
|
+
const [isExpanded, setIsExpanded] = (0, react.useState)(false);
|
|
4198
|
+
const isActive = status === "inProgress" || status === "executing";
|
|
4199
|
+
const isComplete = status === "complete";
|
|
4200
|
+
const statusLabel = isActive ? "Running" : isComplete ? "Done" : status;
|
|
4201
|
+
const dotClassName = isActive ? "cpk:bg-amber-500" : isComplete ? "cpk:bg-emerald-500" : "cpk:bg-zinc-400";
|
|
4202
|
+
const badgeClassName = isActive ? "cpk:bg-amber-100 cpk:text-amber-800 cpk:dark:bg-amber-500/15 cpk:dark:text-amber-400" : isComplete ? "cpk:bg-emerald-100 cpk:text-emerald-800 cpk:dark:bg-emerald-500/15 cpk:dark:text-emerald-400" : "cpk:bg-zinc-100 cpk:text-zinc-800 cpk:dark:bg-zinc-700/40 cpk:dark:text-zinc-300";
|
|
4203
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
4204
|
+
"data-testid": "copilot-tool-render",
|
|
4205
|
+
"data-tool-name": name,
|
|
4206
|
+
"data-tool-call-id": toolCallId,
|
|
4207
|
+
"data-status": status,
|
|
4208
|
+
"data-args": safeStringifyForAttr(parameters),
|
|
4209
|
+
"data-result": safeStringifyForAttr(result),
|
|
4210
|
+
className: "cpk:mt-2 cpk:pb-2",
|
|
4211
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4212
|
+
className: "cpk:rounded-xl cpk:border cpk:border-zinc-200/60 cpk:bg-white/70 cpk:p-4 cpk:shadow-sm cpk:backdrop-blur cpk:dark:border-zinc-800/60 cpk:dark:bg-zinc-900/50",
|
|
4213
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
4214
|
+
type: "button",
|
|
4215
|
+
"aria-expanded": isExpanded,
|
|
4216
|
+
onClick: () => setIsExpanded(!isExpanded),
|
|
4217
|
+
className: "cpk:flex cpk:w-full cpk:cursor-pointer cpk:select-none cpk:items-center cpk:justify-between cpk:gap-2.5 cpk:border-none cpk:bg-transparent cpk:p-0 cpk:m-0 cpk:text-left cpk:text-inherit",
|
|
4218
|
+
style: { font: "inherit" },
|
|
4219
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4220
|
+
className: "cpk:flex cpk:min-w-0 cpk:items-center cpk:gap-2",
|
|
4221
|
+
children: [
|
|
4222
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
|
|
4223
|
+
className: `cpk:h-3.5 cpk:w-3.5 cpk:flex-shrink-0 cpk:text-zinc-500 cpk:transition-transform cpk:dark:text-zinc-400 ${isExpanded ? "cpk:rotate-90" : ""}`,
|
|
4224
|
+
fill: "none",
|
|
4225
|
+
viewBox: "0 0 24 24",
|
|
4226
|
+
strokeWidth: 2,
|
|
4227
|
+
stroke: "currentColor",
|
|
4228
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
|
|
4229
|
+
strokeLinecap: "round",
|
|
4230
|
+
strokeLinejoin: "round",
|
|
4231
|
+
d: "M8.25 4.5l7.5 7.5-7.5 7.5"
|
|
4232
|
+
})
|
|
4233
|
+
}),
|
|
4234
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: `cpk:inline-block cpk:h-2 cpk:w-2 cpk:flex-shrink-0 cpk:rounded-full ${dotClassName}` }),
|
|
4235
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4236
|
+
"data-testid": "copilot-tool-render-name",
|
|
4237
|
+
className: "cpk:truncate cpk:text-[13px] cpk:font-semibold cpk:text-zinc-900 cpk:dark:text-zinc-100",
|
|
4238
|
+
children: name
|
|
4239
|
+
})
|
|
4240
|
+
]
|
|
4241
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4242
|
+
"data-testid": "copilot-tool-render-status",
|
|
4243
|
+
className: `cpk:inline-flex cpk:flex-shrink-0 cpk:items-center cpk:rounded-full cpk:px-2 cpk:py-0.5 cpk:text-[11px] cpk:font-medium ${badgeClassName}`,
|
|
4244
|
+
children: statusLabel
|
|
4245
|
+
})]
|
|
4246
|
+
}), isExpanded && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4247
|
+
className: "cpk:mt-3 cpk:grid cpk:gap-3",
|
|
4248
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
4249
|
+
className: "cpk:text-[10px] cpk:uppercase cpk:text-zinc-500 cpk:dark:text-zinc-400",
|
|
4250
|
+
children: "Arguments"
|
|
4251
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("pre", {
|
|
4252
|
+
className: "cpk:mt-1.5 cpk:max-h-[200px] cpk:overflow-auto cpk:rounded-md cpk:bg-zinc-100 cpk:p-2.5 cpk:text-[11px] cpk:leading-relaxed cpk:text-zinc-800 cpk:whitespace-pre-wrap cpk:break-words cpk:dark:bg-zinc-800/60 cpk:dark:text-zinc-200",
|
|
4253
|
+
children: safeStringifyForPre(parameters ?? {})
|
|
4254
|
+
})] }), result !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
4255
|
+
className: "cpk:text-[10px] cpk:uppercase cpk:text-zinc-500 cpk:dark:text-zinc-400",
|
|
4256
|
+
children: "Result"
|
|
4257
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("pre", {
|
|
4258
|
+
className: "cpk:mt-1.5 cpk:max-h-[200px] cpk:overflow-auto cpk:rounded-md cpk:bg-zinc-100 cpk:p-2.5 cpk:text-[11px] cpk:leading-relaxed cpk:text-zinc-800 cpk:whitespace-pre-wrap cpk:break-words cpk:dark:bg-zinc-800/60 cpk:dark:text-zinc-200",
|
|
4259
|
+
children: typeof result === "string" ? result : safeStringifyForPre(result)
|
|
4260
|
+
})] })]
|
|
4261
|
+
})]
|
|
4262
|
+
})
|
|
4263
|
+
});
|
|
4264
|
+
}
|
|
4265
|
+
function safeStringifyForAttr(value) {
|
|
4266
|
+
if (value === void 0 || value === null) return "";
|
|
4267
|
+
if (typeof value === "string") return value;
|
|
4268
|
+
try {
|
|
4269
|
+
return JSON.stringify(value);
|
|
4270
|
+
} catch (err) {
|
|
4271
|
+
console.warn("[CopilotKit] Failed to JSON.stringify tool-call payload for data-* attribute; falling back to String():", err);
|
|
4272
|
+
try {
|
|
4273
|
+
return String(value);
|
|
4274
|
+
} catch (innerErr) {
|
|
4275
|
+
console.warn("[CopilotKit] safeStringifyForAttr: value could not be stringified:", innerErr);
|
|
4276
|
+
return "";
|
|
4277
|
+
}
|
|
4278
|
+
}
|
|
4279
|
+
}
|
|
4131
4280
|
|
|
4132
4281
|
//#endregion
|
|
4133
4282
|
//#region src/v2/hooks/use-human-in-the-loop.tsx
|
|
@@ -4865,7 +5014,7 @@ function useThreadStoreSelector(store, selector) {
|
|
|
4865
5014
|
return (0, react.useSyncExternalStore)((0, react.useCallback)((onStoreChange) => {
|
|
4866
5015
|
const subscription = store.select(selector).subscribe(onStoreChange);
|
|
4867
5016
|
return () => subscription.unsubscribe();
|
|
4868
|
-
}, [store, selector]), () => selector(store.getState()));
|
|
5017
|
+
}, [store, selector]), () => selector(store.getState()), () => selector(store.getServerState()));
|
|
4869
5018
|
}
|
|
4870
5019
|
/**
|
|
4871
5020
|
* React hook for listing and managing Intelligence platform threads.
|
|
@@ -4908,7 +5057,7 @@ function useThreadStoreSelector(store, selector) {
|
|
|
4908
5057
|
* }
|
|
4909
5058
|
* ```
|
|
4910
5059
|
*/
|
|
4911
|
-
function useThreads$1({ agentId, includeArchived, limit }) {
|
|
5060
|
+
function useThreads$1({ agentId, includeArchived, limit, enabled = true }) {
|
|
4912
5061
|
const { copilotkit } = useCopilotKit();
|
|
4913
5062
|
const [store] = (0, react.useState)(() => (0, _copilotkit_core.ɵcreateThreadStore)({ fetch: globalThis.fetch }));
|
|
4914
5063
|
const coreThreads = useThreadStoreSelector(store, _copilotkit_core.ɵselectThreads);
|
|
@@ -4925,6 +5074,7 @@ function useThreads$1({ agentId, includeArchived, limit }) {
|
|
|
4925
5074
|
const storeError = useThreadStoreSelector(store, _copilotkit_core.ɵselectThreadsError);
|
|
4926
5075
|
const hasMoreThreads = useThreadStoreSelector(store, _copilotkit_core.ɵselectHasNextPage);
|
|
4927
5076
|
const isFetchingMoreThreads = useThreadStoreSelector(store, _copilotkit_core.ɵselectIsFetchingNextPage);
|
|
5077
|
+
const isMutating = useThreadStoreSelector(store, _copilotkit_core.ɵselectIsMutating);
|
|
4928
5078
|
const headersKey = (0, react.useMemo)(() => {
|
|
4929
5079
|
return JSON.stringify(Object.entries(copilotkit.headers ?? {}).sort(([left], [right]) => left.localeCompare(right)));
|
|
4930
5080
|
}, [copilotkit.headers]);
|
|
@@ -4945,9 +5095,16 @@ function useThreads$1({ agentId, includeArchived, limit }) {
|
|
|
4945
5095
|
return /* @__PURE__ */ new Error("Thread mutations are not available on this CopilotKit runtime");
|
|
4946
5096
|
}, [threadMutationsSupported]);
|
|
4947
5097
|
const [hasDispatchedContext, setHasDispatchedContext] = (0, react.useState)(false);
|
|
4948
|
-
const preConnectLoading = !!copilotkit.runtimeUrl && !threadEndpointsUnavailable && !hasDispatchedContext;
|
|
4949
|
-
const
|
|
4950
|
-
|
|
5098
|
+
const preConnectLoading = enabled && !!copilotkit.runtimeUrl && !threadEndpointsUnavailable && !hasDispatchedContext;
|
|
5099
|
+
const [configErrorDismissed, setConfigErrorDismissed] = (0, react.useState)(false);
|
|
5100
|
+
(0, react.useEffect)(() => {
|
|
5101
|
+
setConfigErrorDismissed(false);
|
|
5102
|
+
}, [runtimeError, threadEndpointsError]);
|
|
5103
|
+
const activeRuntimeError = configErrorDismissed ? null : runtimeError;
|
|
5104
|
+
const activeThreadEndpointsError = configErrorDismissed ? null : threadEndpointsError;
|
|
5105
|
+
const isLoading = activeRuntimeError || activeThreadEndpointsError ? false : preConnectLoading || storeIsLoading;
|
|
5106
|
+
const error = activeRuntimeError ?? activeThreadEndpointsError ?? storeError;
|
|
5107
|
+
const listError = storeError;
|
|
4951
5108
|
(0, react.useEffect)(() => {
|
|
4952
5109
|
store.start();
|
|
4953
5110
|
return () => {
|
|
@@ -4955,6 +5112,7 @@ function useThreads$1({ agentId, includeArchived, limit }) {
|
|
|
4955
5112
|
};
|
|
4956
5113
|
}, [store]);
|
|
4957
5114
|
(0, react.useEffect)(() => {
|
|
5115
|
+
if (!enabled) return;
|
|
4958
5116
|
copilotkit.registerThreadStore(agentId, store);
|
|
4959
5117
|
return () => {
|
|
4960
5118
|
copilotkit.unregisterThreadStore(agentId);
|
|
@@ -4962,9 +5120,15 @@ function useThreads$1({ agentId, includeArchived, limit }) {
|
|
|
4962
5120
|
}, [
|
|
4963
5121
|
copilotkit,
|
|
4964
5122
|
agentId,
|
|
4965
|
-
store
|
|
5123
|
+
store,
|
|
5124
|
+
enabled
|
|
4966
5125
|
]);
|
|
4967
5126
|
(0, react.useEffect)(() => {
|
|
5127
|
+
if (!enabled) {
|
|
5128
|
+
store.setContext(null);
|
|
5129
|
+
setHasDispatchedContext(false);
|
|
5130
|
+
return;
|
|
5131
|
+
}
|
|
4968
5132
|
if (!copilotkit.runtimeUrl) {
|
|
4969
5133
|
store.setContext(null);
|
|
4970
5134
|
setHasDispatchedContext(false);
|
|
@@ -4988,6 +5152,7 @@ function useThreads$1({ agentId, includeArchived, limit }) {
|
|
|
4988
5152
|
setHasDispatchedContext(true);
|
|
4989
5153
|
}, [
|
|
4990
5154
|
store,
|
|
5155
|
+
enabled,
|
|
4991
5156
|
copilotkit.runtimeUrl,
|
|
4992
5157
|
runtimeStatus,
|
|
4993
5158
|
headersKey,
|
|
@@ -5011,9 +5176,16 @@ function useThreads$1({ agentId, includeArchived, limit }) {
|
|
|
5011
5176
|
threads,
|
|
5012
5177
|
isLoading,
|
|
5013
5178
|
error,
|
|
5179
|
+
listError,
|
|
5014
5180
|
hasMoreThreads,
|
|
5015
5181
|
isFetchingMoreThreads,
|
|
5182
|
+
isMutating,
|
|
5016
5183
|
fetchMoreThreads: (0, react.useCallback)(() => store.fetchNextPage(), [store]),
|
|
5184
|
+
refetchThreads: (0, react.useCallback)(() => store.refetchThreads(), [store]),
|
|
5185
|
+
startNewThread: (0, react.useCallback)(() => {
|
|
5186
|
+
setConfigErrorDismissed(true);
|
|
5187
|
+
store.startNewThread();
|
|
5188
|
+
}, [store]),
|
|
5017
5189
|
renameThread,
|
|
5018
5190
|
archiveThread,
|
|
5019
5191
|
unarchiveThread,
|
|
@@ -6200,7 +6372,7 @@ const DefaultContainer = react.default.forwardRef(function DefaultContainer({ cl
|
|
|
6200
6372
|
ref,
|
|
6201
6373
|
"data-copilotkit": true,
|
|
6202
6374
|
"data-testid": "copilot-suggestions",
|
|
6203
|
-
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:
|
|
6375
|
+
className: cn("cpk:flex cpk:flex-wrap cpk:items-center cpk:gap-1.5 cpk:sm:gap-2 cpk:pl-0 cpk:pr-4 cpk:@3xl:px-0 cpk:pointer-events-none", className),
|
|
6204
6376
|
...props
|
|
6205
6377
|
});
|
|
6206
6378
|
});
|
|
@@ -7305,7 +7477,7 @@ function CopilotChatView({ messageView, input, scrollView, suggestionView, welco
|
|
|
7305
7477
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
7306
7478
|
className: "cpk:max-w-3xl cpk:mx-auto",
|
|
7307
7479
|
children: [BoundMessageView, hasSuggestions ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
7308
|
-
className: "cpk:pl-0 cpk:pr-4 cpk:
|
|
7480
|
+
className: "cpk:pl-0 cpk:pr-4 cpk:@3xl:px-0 cpk:mt-4",
|
|
7309
7481
|
children: BoundSuggestionView
|
|
7310
7482
|
}) : null]
|
|
7311
7483
|
})
|
|
@@ -7348,7 +7520,7 @@ function CopilotChatView({ messageView, input, scrollView, suggestionView, welco
|
|
|
7348
7520
|
onDragOver,
|
|
7349
7521
|
onDragLeave,
|
|
7350
7522
|
onDrop,
|
|
7351
|
-
className: cn("copilotKitChat cpk:relative cpk:h-full cpk:flex cpk:flex-col", className),
|
|
7523
|
+
className: cn("copilotKitChat cpk:@container cpk:relative cpk:h-full cpk:flex cpk:flex-col", className),
|
|
7352
7524
|
...props,
|
|
7353
7525
|
children: [dragOver && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DropOverlay, {}), BoundWelcomeScreen]
|
|
7354
7526
|
});
|
|
@@ -7370,7 +7542,7 @@ function CopilotChatView({ messageView, input, scrollView, suggestionView, welco
|
|
|
7370
7542
|
onDragOver,
|
|
7371
7543
|
onDragLeave,
|
|
7372
7544
|
onDrop,
|
|
7373
|
-
className: cn("copilotKitChat cpk:relative cpk:h-full cpk:flex cpk:flex-col", className),
|
|
7545
|
+
className: cn("copilotKitChat cpk:@container cpk:relative cpk:h-full cpk:flex cpk:flex-col", className),
|
|
7374
7546
|
...props,
|
|
7375
7547
|
children: [
|
|
7376
7548
|
dragOver && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DropOverlay, {}),
|
|
@@ -7409,7 +7581,7 @@ function CopilotChatView({ messageView, input, scrollView, suggestionView, welco
|
|
|
7409
7581
|
minHeight: 0
|
|
7410
7582
|
},
|
|
7411
7583
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
7412
|
-
className: "cpk:px-4 cpk:
|
|
7584
|
+
className: "cpk:px-4 cpk:@3xl:px-0 cpk:[div[data-sidebar-chat]_&]:px-8 cpk:[div[data-popup-chat]_&]:px-6",
|
|
7413
7585
|
children
|
|
7414
7586
|
})
|
|
7415
7587
|
}),
|
|
@@ -7442,7 +7614,7 @@ function CopilotChatView({ messageView, input, scrollView, suggestionView, welco
|
|
|
7442
7614
|
...props,
|
|
7443
7615
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
7444
7616
|
ref: contentRef,
|
|
7445
|
-
className: "cpk:px-4 cpk:
|
|
7617
|
+
className: "cpk:px-4 cpk:@3xl:px-0 cpk:[div[data-sidebar-chat]_&]:px-8 cpk:[div[data-popup-chat]_&]:px-6",
|
|
7446
7618
|
children
|
|
7447
7619
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
7448
7620
|
ref: spacerRef,
|
|
@@ -7504,7 +7676,7 @@ function CopilotChatView({ messageView, input, scrollView, suggestionView, welco
|
|
|
7504
7676
|
if (!hasMounted) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
7505
7677
|
className: "cpk:h-full cpk:max-h-full cpk:flex cpk:flex-col cpk:min-h-0 cpk:overflow-y-auto cpk:overflow-x-hidden",
|
|
7506
7678
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
7507
|
-
className: "cpk:px-4 cpk:
|
|
7679
|
+
className: "cpk:px-4 cpk:@3xl:px-0 cpk:[div[data-sidebar-chat]_&]:px-8 cpk:[div[data-popup-chat]_&]:px-6",
|
|
7508
7680
|
children
|
|
7509
7681
|
})
|
|
7510
7682
|
});
|
|
@@ -7519,7 +7691,7 @@ function CopilotChatView({ messageView, input, scrollView, suggestionView, welco
|
|
|
7519
7691
|
children: [
|
|
7520
7692
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
7521
7693
|
ref: contentRef,
|
|
7522
|
-
className: "cpk:px-4 cpk:
|
|
7694
|
+
className: "cpk:px-4 cpk:@3xl:px-0 cpk:[div[data-sidebar-chat]_&]:px-8 cpk:[div[data-popup-chat]_&]:px-6",
|
|
7523
7695
|
children
|
|
7524
7696
|
}),
|
|
7525
7697
|
BoundFeather,
|
|
@@ -7783,9 +7955,17 @@ function CopilotChat({ agentId, threadId, labels, chatView, isModalDefaultOpen,
|
|
|
7783
7955
|
const { messageView: providedMessageView, suggestionView: providedSuggestionView, onStop: providedStopHandler, ...restProps } = props;
|
|
7784
7956
|
const [lastConnectedThreadId, setLastConnectedThreadId] = (0, react.useState)(null);
|
|
7785
7957
|
const isConnecting = hasExplicitThreadId && lastConnectedThreadId !== resolvedThreadId;
|
|
7958
|
+
const previousThreadIdRef = (0, react.useRef)(null);
|
|
7959
|
+
const hasExplicitThreadIdRef = (0, react.useRef)(hasExplicitThreadId);
|
|
7960
|
+
hasExplicitThreadIdRef.current = hasExplicitThreadId;
|
|
7786
7961
|
(0, react.useEffect)(() => {
|
|
7962
|
+
const threadChanged = previousThreadIdRef.current !== resolvedThreadId;
|
|
7963
|
+
previousThreadIdRef.current = resolvedThreadId;
|
|
7787
7964
|
agent.threadId = resolvedThreadId;
|
|
7788
|
-
if (!hasExplicitThreadId)
|
|
7965
|
+
if (!hasExplicitThreadId) {
|
|
7966
|
+
if (threadChanged && agent.messages.length > 0) agent.setMessages([]);
|
|
7967
|
+
return;
|
|
7968
|
+
}
|
|
7789
7969
|
let detached = false;
|
|
7790
7970
|
const connectAbortController = new AbortController();
|
|
7791
7971
|
if (agent instanceof _ag_ui_client.HttpAgent) agent.abortController = connectAbortController;
|
|
@@ -7799,6 +7979,7 @@ function CopilotChat({ agentId, threadId, labels, chatView, isModalDefaultOpen,
|
|
|
7799
7979
|
if (!detached) (typeof requestAnimationFrame === "function" ? requestAnimationFrame : (cb) => setTimeout(cb, 16))(() => {
|
|
7800
7980
|
if (!detached) setLastConnectedThreadId(resolvedThreadId);
|
|
7801
7981
|
});
|
|
7982
|
+
else if (!hasExplicitThreadIdRef.current) agentToConnect.setMessages([]);
|
|
7802
7983
|
}
|
|
7803
7984
|
};
|
|
7804
7985
|
connect(agent);
|
|
@@ -8149,18 +8330,46 @@ CopilotChatToggleButton.displayName = "CopilotChatToggleButton";
|
|
|
8149
8330
|
|
|
8150
8331
|
//#endregion
|
|
8151
8332
|
//#region src/v2/components/chat/CopilotModalHeader.tsx
|
|
8152
|
-
|
|
8333
|
+
/**
|
|
8334
|
+
* Reactively tracks whether the viewport is in the mobile range (≤767px) — the
|
|
8335
|
+
* same breakpoint the drawer + chat coordination use. SSR-safe: starts `false`
|
|
8336
|
+
* (desktop) so the server render and first client render agree, then syncs on
|
|
8337
|
+
* mount and on resize.
|
|
8338
|
+
*/
|
|
8339
|
+
function useIsMobileViewport() {
|
|
8340
|
+
const [isMobile, setIsMobile] = (0, react.useState)(false);
|
|
8341
|
+
(0, react.useEffect)(() => {
|
|
8342
|
+
if (typeof window === "undefined" || typeof window.matchMedia !== "function") return;
|
|
8343
|
+
const mql = window.matchMedia("(max-width: 767px)");
|
|
8344
|
+
const update = () => setIsMobile(mql.matches);
|
|
8345
|
+
update();
|
|
8346
|
+
mql.addEventListener("change", update);
|
|
8347
|
+
return () => mql.removeEventListener("change", update);
|
|
8348
|
+
}, []);
|
|
8349
|
+
return isMobile;
|
|
8350
|
+
}
|
|
8351
|
+
function CopilotModalHeader({ title, titleContent, closeButton, drawerLauncher, children, className, ...rest }) {
|
|
8153
8352
|
const configuration = useCopilotChatConfiguration();
|
|
8154
8353
|
const fallbackTitle = configuration?.labels.modalHeaderTitle ?? CopilotChatDefaultLabels.modalHeaderTitle;
|
|
8155
8354
|
const resolvedTitle = title ?? fallbackTitle;
|
|
8355
|
+
const isMobile = useIsMobileViewport();
|
|
8356
|
+
const drawerRegistered = (configuration?.drawerRegistered ?? false) && isMobile;
|
|
8156
8357
|
const handleClose = (0, react.useCallback)(() => {
|
|
8157
8358
|
configuration?.setModalOpen?.(false);
|
|
8158
8359
|
}, [configuration]);
|
|
8360
|
+
const handleToggleDrawer = (0, react.useCallback)(() => {
|
|
8361
|
+
configuration?.setDrawerOpen?.(!configuration.drawerOpen);
|
|
8362
|
+
}, [configuration]);
|
|
8159
8363
|
const BoundTitle = renderSlot(titleContent, CopilotModalHeader.Title, { children: resolvedTitle });
|
|
8160
8364
|
const BoundCloseButton = renderSlot(closeButton, CopilotModalHeader.CloseButton, { onClick: handleClose });
|
|
8365
|
+
const BoundDrawerLauncher = drawerRegistered ? renderSlot(drawerLauncher, CopilotModalHeader.DrawerLauncher, {
|
|
8366
|
+
onClick: handleToggleDrawer,
|
|
8367
|
+
"aria-expanded": configuration?.drawerOpen ?? false
|
|
8368
|
+
}) : null;
|
|
8161
8369
|
if (children) return children({
|
|
8162
8370
|
titleContent: BoundTitle,
|
|
8163
8371
|
closeButton: BoundCloseButton,
|
|
8372
|
+
drawerLauncher: BoundDrawerLauncher,
|
|
8164
8373
|
title: resolvedTitle,
|
|
8165
8374
|
...rest
|
|
8166
8375
|
});
|
|
@@ -8173,8 +8382,8 @@ function CopilotModalHeader({ title, titleContent, closeButton, children, classN
|
|
|
8173
8382
|
className: "cpk:flex cpk:w-full cpk:items-center cpk:gap-2",
|
|
8174
8383
|
children: [
|
|
8175
8384
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
8176
|
-
className: "cpk:flex-1",
|
|
8177
|
-
"aria-hidden": "true"
|
|
8385
|
+
className: "cpk:flex cpk:flex-1 cpk:justify-start",
|
|
8386
|
+
children: BoundDrawerLauncher ?? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { "aria-hidden": "true" })
|
|
8178
8387
|
}),
|
|
8179
8388
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
8180
8389
|
className: "cpk:flex cpk:flex-1 cpk:justify-center cpk:text-center",
|
|
@@ -8207,9 +8416,21 @@ CopilotModalHeader.displayName = "CopilotModalHeader";
|
|
|
8207
8416
|
"aria-hidden": "true"
|
|
8208
8417
|
})
|
|
8209
8418
|
});
|
|
8419
|
+
_CopilotModalHeader.DrawerLauncher = ({ className, ...props }) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
8420
|
+
type: "button",
|
|
8421
|
+
"data-testid": "copilot-threads-drawer-launcher",
|
|
8422
|
+
className: cn("cpk:inline-flex cpk:size-8 cpk:items-center cpk:justify-center cpk:rounded-full cpk:text-muted-foreground cpk:transition cpk:cursor-pointer", "cpk:hover:bg-muted cpk:hover:text-foreground cpk:focus-visible:outline-none cpk:focus-visible:ring-2 cpk:focus-visible:ring-ring", className),
|
|
8423
|
+
"aria-label": "Open threads",
|
|
8424
|
+
...props,
|
|
8425
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.PanelLeftOpen, {
|
|
8426
|
+
className: "cpk:h-4 cpk:w-4",
|
|
8427
|
+
"aria-hidden": "true"
|
|
8428
|
+
})
|
|
8429
|
+
});
|
|
8210
8430
|
})(CopilotModalHeader || (CopilotModalHeader = {}));
|
|
8211
8431
|
CopilotModalHeader.Title.displayName = "CopilotModalHeader.Title";
|
|
8212
8432
|
CopilotModalHeader.CloseButton.displayName = "CopilotModalHeader.CloseButton";
|
|
8433
|
+
CopilotModalHeader.DrawerLauncher.displayName = "CopilotModalHeader.DrawerLauncher";
|
|
8213
8434
|
|
|
8214
8435
|
//#endregion
|
|
8215
8436
|
//#region src/v2/components/chat/CopilotSidebarView.tsx
|
|
@@ -8567,6 +8788,322 @@ function CopilotPopup({ header, toggleButton, defaultOpen, width, height, clickO
|
|
|
8567
8788
|
}
|
|
8568
8789
|
CopilotPopup.displayName = "CopilotPopup";
|
|
8569
8790
|
|
|
8791
|
+
//#endregion
|
|
8792
|
+
//#region src/v2/components/chat/CopilotThreadsDrawer.tsx
|
|
8793
|
+
/**
|
|
8794
|
+
* Maps a {@link Thread} from {@link useThreads} to the element's
|
|
8795
|
+
* {@link DrawerThread} view shape. The shapes are structurally compatible; this
|
|
8796
|
+
* narrows to exactly the fields the element renders so the element never sees
|
|
8797
|
+
* platform-internal fields.
|
|
8798
|
+
*/
|
|
8799
|
+
function toDrawerThread(thread) {
|
|
8800
|
+
return {
|
|
8801
|
+
id: thread.id,
|
|
8802
|
+
name: thread.name,
|
|
8803
|
+
archived: thread.archived,
|
|
8804
|
+
createdAt: thread.createdAt,
|
|
8805
|
+
updatedAt: thread.updatedAt,
|
|
8806
|
+
...thread.lastRunAt !== void 0 ? { lastRunAt: thread.lastRunAt } : {}
|
|
8807
|
+
};
|
|
8808
|
+
}
|
|
8809
|
+
/** The chat input textarea's documented `data-testid`. */
|
|
8810
|
+
const CHAT_INPUT_TESTID = "copilot-chat-textarea";
|
|
8811
|
+
/** The chat view container's documented `data-testid`. */
|
|
8812
|
+
const CHAT_CONTAINER_TESTID = "copilot-chat";
|
|
8813
|
+
/**
|
|
8814
|
+
* Returns the chat input element for focus-return after a thread is selected.
|
|
8815
|
+
*
|
|
8816
|
+
* Best-effort and SCOPED: walks up from the drawer element looking for an
|
|
8817
|
+
* ancestor that contains a chat-view container (`data-testid="copilot-chat"`),
|
|
8818
|
+
* then returns the chat input within that subtree. This avoids focusing the
|
|
8819
|
+
* wrong composer on a page hosting more than one chat (multi-chat dashboards),
|
|
8820
|
+
* where a document-global lookup would grab whichever input appears first in
|
|
8821
|
+
* DOM order rather than the one this drawer drives.
|
|
8822
|
+
*
|
|
8823
|
+
* Falls back to a document-global lookup when no scoping ancestor is found
|
|
8824
|
+
* (e.g. the drawer and chat share no common container, or headless usage),
|
|
8825
|
+
* and returns `null` when there is no chat input at all.
|
|
8826
|
+
*
|
|
8827
|
+
* @param origin - The drawer element to scope the search from.
|
|
8828
|
+
*/
|
|
8829
|
+
function findChatInput(origin) {
|
|
8830
|
+
if (typeof document === "undefined") return null;
|
|
8831
|
+
const container = origin?.closest?.(`[data-testid="${CHAT_CONTAINER_TESTID}"]`);
|
|
8832
|
+
if (container) {
|
|
8833
|
+
const scoped = container.querySelector(`[data-testid="${CHAT_INPUT_TESTID}"]`);
|
|
8834
|
+
if (scoped) return scoped;
|
|
8835
|
+
}
|
|
8836
|
+
return document.querySelector(`[data-testid="${CHAT_INPUT_TESTID}"]`);
|
|
8837
|
+
}
|
|
8838
|
+
/**
|
|
8839
|
+
* React wrapper for the shadow-DOM `<copilotkit-threads-drawer>` threads drawer.
|
|
8840
|
+
*
|
|
8841
|
+
* Responsibilities:
|
|
8842
|
+
* - Registers the custom element on the client (SSR-safe; nothing renders
|
|
8843
|
+
* during prerender to avoid hydration mismatch).
|
|
8844
|
+
* - Feeds the element domain data: `threads`, `loading`, `error`,
|
|
8845
|
+
* `activeThreadId`, `licensed`, fetch-more state.
|
|
8846
|
+
* - Routes the element's nine outbound events to core thread operations
|
|
8847
|
+
* ({@link useThreads}) and chat-configuration changes.
|
|
8848
|
+
* - Registers with the surrounding chat configuration so the header
|
|
8849
|
+
* thread-list launcher appears, and binds the element `open` state to the
|
|
8850
|
+
* configuration's `drawerOpen`.
|
|
8851
|
+
*
|
|
8852
|
+
* License gating is two-pronged: the locked view shows when no license is configured
|
|
8853
|
+
* (the runtime reported no license status) OR the `threads` feature is
|
|
8854
|
+
* explicitly unlicensed. While unlicensed, the thread fetch is skipped entirely
|
|
8855
|
+
* so an unlicensed drawer issues no network requests.
|
|
8856
|
+
*
|
|
8857
|
+
* Thread switching needs no host wiring: when `onThreadSelect`/`onNewThread`
|
|
8858
|
+
* are omitted, the wrapper drives the surrounding chat configuration directly
|
|
8859
|
+
* ({@link CopilotChatConfigurationValue.setActiveThreadId} /
|
|
8860
|
+
* {@link CopilotChatConfigurationValue.startNewThread}), so a bare drawer
|
|
8861
|
+
* connects to the picked thread and shows the welcome screen on "+ New". Pass
|
|
8862
|
+
* the callbacks only to take control yourself.
|
|
8863
|
+
*
|
|
8864
|
+
* @example
|
|
8865
|
+
* ```tsx
|
|
8866
|
+
* // Callback-free: the drawer drives the chat configuration itself.
|
|
8867
|
+
* <CopilotKitProvider runtimeUrl="/api/copilotkit" publicLicenseKey="ck_pub_...">
|
|
8868
|
+
* <CopilotChat />
|
|
8869
|
+
* <CopilotThreadsDrawer />
|
|
8870
|
+
* </CopilotKitProvider>
|
|
8871
|
+
* ```
|
|
8872
|
+
*/
|
|
8873
|
+
function CopilotThreadsDrawer({ agentId, onThreadSelect, onNewThread, onLicensed, licenseUrl, renderRow, label, limit, "data-testid": dataTestId = "copilot-threads-drawer" }) {
|
|
8874
|
+
const configuration = useCopilotChatConfiguration();
|
|
8875
|
+
const { status, checkFeature } = useLicenseContext();
|
|
8876
|
+
const licensePresent = status === "valid" || status === "expiring";
|
|
8877
|
+
const featureLicensed = checkFeature("threads");
|
|
8878
|
+
const licensed = licensePresent && featureLicensed;
|
|
8879
|
+
const licensePending = status === null;
|
|
8880
|
+
const resolvedAgentId = agentId ?? configuration?.agentId ?? "default";
|
|
8881
|
+
const activeThreadId = configuration?.threadId ?? null;
|
|
8882
|
+
const { threads, isLoading, listError, hasMoreThreads, isFetchingMoreThreads, archiveThread, unarchiveThread, deleteThread, fetchMoreThreads, refetchThreads, startNewThread } = useThreads$1({
|
|
8883
|
+
agentId: resolvedAgentId,
|
|
8884
|
+
includeArchived: true,
|
|
8885
|
+
enabled: licensed,
|
|
8886
|
+
...limit !== void 0 ? { limit } : {}
|
|
8887
|
+
});
|
|
8888
|
+
const drawerThreads = (0, react.useMemo)(() => threads.map(toDrawerThread), [threads]);
|
|
8889
|
+
const elementRef = (0, react.useRef)(null);
|
|
8890
|
+
const [mounted, setMounted] = (0, react.useState)(false);
|
|
8891
|
+
(0, react.useEffect)(() => {
|
|
8892
|
+
(0, _copilotkit_web_components_threads_drawer.defineCopilotKitThreadsDrawer)();
|
|
8893
|
+
setMounted(true);
|
|
8894
|
+
}, []);
|
|
8895
|
+
const registerDrawer = configuration?.registerDrawer;
|
|
8896
|
+
(0, react.useEffect)(() => {
|
|
8897
|
+
if (!registerDrawer) return;
|
|
8898
|
+
return registerDrawer();
|
|
8899
|
+
}, [registerDrawer]);
|
|
8900
|
+
const [localDrawerOpen, setLocalDrawerOpen] = (0, react.useState)(false);
|
|
8901
|
+
const drawerOpen = configuration ? configuration.drawerOpen : localDrawerOpen;
|
|
8902
|
+
const setDrawerOpen = configuration ? configuration.setDrawerOpen : setLocalDrawerOpen;
|
|
8903
|
+
const setActiveThreadId = configuration?.setActiveThreadId;
|
|
8904
|
+
const startNewThreadConfig = configuration?.startNewThread;
|
|
8905
|
+
const handleThreadSelected = (0, react.useCallback)((threadId) => {
|
|
8906
|
+
if (onThreadSelect) onThreadSelect(threadId);
|
|
8907
|
+
else setActiveThreadId?.(threadId, { explicit: true });
|
|
8908
|
+
findChatInput(elementRef.current)?.focus();
|
|
8909
|
+
}, [onThreadSelect, setActiveThreadId]);
|
|
8910
|
+
const handleNewThread = (0, react.useCallback)(() => {
|
|
8911
|
+
startNewThread();
|
|
8912
|
+
if (onNewThread) onNewThread();
|
|
8913
|
+
else startNewThreadConfig?.();
|
|
8914
|
+
}, [
|
|
8915
|
+
startNewThread,
|
|
8916
|
+
onNewThread,
|
|
8917
|
+
startNewThreadConfig
|
|
8918
|
+
]);
|
|
8919
|
+
const handleArchive = (0, react.useCallback)((threadId) => {
|
|
8920
|
+
archiveThread(threadId).catch((err) => {
|
|
8921
|
+
console.error("CopilotThreadsDrawer: archiveThread failed", err);
|
|
8922
|
+
});
|
|
8923
|
+
}, [archiveThread]);
|
|
8924
|
+
const handleUnarchive = (0, react.useCallback)((threadId) => {
|
|
8925
|
+
unarchiveThread(threadId).catch((err) => {
|
|
8926
|
+
console.error("CopilotThreadsDrawer: unarchiveThread failed", err);
|
|
8927
|
+
});
|
|
8928
|
+
}, [unarchiveThread]);
|
|
8929
|
+
const handleDelete = (0, react.useCallback)((threadId) => {
|
|
8930
|
+
const isActive = threadId === activeThreadId;
|
|
8931
|
+
deleteThread(threadId).then(() => {
|
|
8932
|
+
if (isActive) {
|
|
8933
|
+
startNewThread();
|
|
8934
|
+
if (onNewThread) onNewThread();
|
|
8935
|
+
else startNewThreadConfig?.();
|
|
8936
|
+
}
|
|
8937
|
+
}).catch((err) => {
|
|
8938
|
+
console.error("CopilotThreadsDrawer: deleteThread failed", err);
|
|
8939
|
+
});
|
|
8940
|
+
}, [
|
|
8941
|
+
deleteThread,
|
|
8942
|
+
activeThreadId,
|
|
8943
|
+
startNewThread,
|
|
8944
|
+
onNewThread,
|
|
8945
|
+
startNewThreadConfig
|
|
8946
|
+
]);
|
|
8947
|
+
const handleFilterChange = (0, react.useCallback)(() => {
|
|
8948
|
+
refetchThreads();
|
|
8949
|
+
}, [refetchThreads]);
|
|
8950
|
+
const handleRetry = (0, react.useCallback)((scope) => {
|
|
8951
|
+
if (scope === "fetch-more") fetchMoreThreads();
|
|
8952
|
+
else refetchThreads();
|
|
8953
|
+
}, [fetchMoreThreads, refetchThreads]);
|
|
8954
|
+
const handleOpenChange = (0, react.useCallback)((open) => {
|
|
8955
|
+
setDrawerOpen(open);
|
|
8956
|
+
}, [setDrawerOpen]);
|
|
8957
|
+
const handleLicensed = (0, react.useCallback)(() => {
|
|
8958
|
+
onLicensed?.();
|
|
8959
|
+
}, [onLicensed]);
|
|
8960
|
+
const handleLoadMore = (0, react.useCallback)(() => {
|
|
8961
|
+
fetchMoreThreads();
|
|
8962
|
+
}, [fetchMoreThreads]);
|
|
8963
|
+
const handlersRef = (0, react.useRef)({
|
|
8964
|
+
handleThreadSelected,
|
|
8965
|
+
handleNewThread,
|
|
8966
|
+
handleArchive,
|
|
8967
|
+
handleUnarchive,
|
|
8968
|
+
handleDelete,
|
|
8969
|
+
handleFilterChange,
|
|
8970
|
+
handleRetry,
|
|
8971
|
+
handleOpenChange,
|
|
8972
|
+
handleLicensed,
|
|
8973
|
+
handleLoadMore
|
|
8974
|
+
});
|
|
8975
|
+
handlersRef.current = {
|
|
8976
|
+
handleThreadSelected,
|
|
8977
|
+
handleNewThread,
|
|
8978
|
+
handleArchive,
|
|
8979
|
+
handleUnarchive,
|
|
8980
|
+
handleDelete,
|
|
8981
|
+
handleFilterChange,
|
|
8982
|
+
handleRetry,
|
|
8983
|
+
handleOpenChange,
|
|
8984
|
+
handleLicensed,
|
|
8985
|
+
handleLoadMore
|
|
8986
|
+
};
|
|
8987
|
+
(0, react.useEffect)(() => {
|
|
8988
|
+
const el = elementRef.current;
|
|
8989
|
+
if (!el) return;
|
|
8990
|
+
const onThreadSelected = (event) => {
|
|
8991
|
+
const detail = event.detail;
|
|
8992
|
+
handlersRef.current.handleThreadSelected(detail.threadId);
|
|
8993
|
+
};
|
|
8994
|
+
const onNewThreadEvent = () => handlersRef.current.handleNewThread();
|
|
8995
|
+
const onArchive = (event) => {
|
|
8996
|
+
const detail = event.detail;
|
|
8997
|
+
handlersRef.current.handleArchive(detail.threadId);
|
|
8998
|
+
};
|
|
8999
|
+
const onUnarchive = (event) => {
|
|
9000
|
+
const detail = event.detail;
|
|
9001
|
+
handlersRef.current.handleUnarchive(detail.threadId);
|
|
9002
|
+
};
|
|
9003
|
+
const onDelete = (event) => {
|
|
9004
|
+
const detail = event.detail;
|
|
9005
|
+
handlersRef.current.handleDelete(detail.threadId);
|
|
9006
|
+
};
|
|
9007
|
+
const onFilterChange = (_event) => {
|
|
9008
|
+
handlersRef.current.handleFilterChange();
|
|
9009
|
+
};
|
|
9010
|
+
const onOpenChangeEvent = (event) => {
|
|
9011
|
+
const detail = event.detail;
|
|
9012
|
+
handlersRef.current.handleOpenChange(detail.open);
|
|
9013
|
+
};
|
|
9014
|
+
const onRetry = (event) => {
|
|
9015
|
+
const detail = event.detail;
|
|
9016
|
+
handlersRef.current.handleRetry(detail.scope);
|
|
9017
|
+
};
|
|
9018
|
+
const onLicensedEvent = () => handlersRef.current.handleLicensed();
|
|
9019
|
+
const onLoadMore = () => handlersRef.current.handleLoadMore();
|
|
9020
|
+
el.addEventListener("thread-selected", onThreadSelected);
|
|
9021
|
+
el.addEventListener("new-thread", onNewThreadEvent);
|
|
9022
|
+
el.addEventListener("archive", onArchive);
|
|
9023
|
+
el.addEventListener("unarchive", onUnarchive);
|
|
9024
|
+
el.addEventListener("delete", onDelete);
|
|
9025
|
+
el.addEventListener("filter-change", onFilterChange);
|
|
9026
|
+
el.addEventListener("open-change", onOpenChangeEvent);
|
|
9027
|
+
el.addEventListener("retry", onRetry);
|
|
9028
|
+
el.addEventListener("licensed", onLicensedEvent);
|
|
9029
|
+
el.addEventListener("load-more", onLoadMore);
|
|
9030
|
+
return () => {
|
|
9031
|
+
el.removeEventListener("thread-selected", onThreadSelected);
|
|
9032
|
+
el.removeEventListener("new-thread", onNewThreadEvent);
|
|
9033
|
+
el.removeEventListener("archive", onArchive);
|
|
9034
|
+
el.removeEventListener("unarchive", onUnarchive);
|
|
9035
|
+
el.removeEventListener("delete", onDelete);
|
|
9036
|
+
el.removeEventListener("filter-change", onFilterChange);
|
|
9037
|
+
el.removeEventListener("open-change", onOpenChangeEvent);
|
|
9038
|
+
el.removeEventListener("retry", onRetry);
|
|
9039
|
+
el.removeEventListener("licensed", onLicensedEvent);
|
|
9040
|
+
el.removeEventListener("load-more", onLoadMore);
|
|
9041
|
+
};
|
|
9042
|
+
}, [mounted]);
|
|
9043
|
+
(0, react.useEffect)(() => {
|
|
9044
|
+
const el = elementRef.current;
|
|
9045
|
+
if (!el) return;
|
|
9046
|
+
el.threads = drawerThreads;
|
|
9047
|
+
}, [drawerThreads, mounted]);
|
|
9048
|
+
(0, react.useEffect)(() => {
|
|
9049
|
+
const el = elementRef.current;
|
|
9050
|
+
if (!el) return;
|
|
9051
|
+
el.loading = isLoading || licensePending;
|
|
9052
|
+
el.error = listError ? listError.message : null;
|
|
9053
|
+
el.activeThreadId = activeThreadId;
|
|
9054
|
+
el.licensed = licensed || licensePending;
|
|
9055
|
+
el.hasMore = hasMoreThreads;
|
|
9056
|
+
el.fetchingMore = isFetchingMoreThreads;
|
|
9057
|
+
}, [
|
|
9058
|
+
isLoading,
|
|
9059
|
+
listError,
|
|
9060
|
+
activeThreadId,
|
|
9061
|
+
licensed,
|
|
9062
|
+
licensePending,
|
|
9063
|
+
hasMoreThreads,
|
|
9064
|
+
isFetchingMoreThreads,
|
|
9065
|
+
mounted
|
|
9066
|
+
]);
|
|
9067
|
+
(0, react.useEffect)(() => {
|
|
9068
|
+
const el = elementRef.current;
|
|
9069
|
+
if (!el) return;
|
|
9070
|
+
el.open = drawerOpen;
|
|
9071
|
+
}, [drawerOpen, mounted]);
|
|
9072
|
+
(0, react.useEffect)(() => {
|
|
9073
|
+
const el = elementRef.current;
|
|
9074
|
+
if (!el) return;
|
|
9075
|
+
if (label !== void 0) el.label = label;
|
|
9076
|
+
}, [label, mounted]);
|
|
9077
|
+
(0, react.useEffect)(() => {
|
|
9078
|
+
const el = elementRef.current;
|
|
9079
|
+
if (!el) return;
|
|
9080
|
+
if (licenseUrl !== void 0) el.licenseUrl = licenseUrl;
|
|
9081
|
+
}, [licenseUrl, mounted]);
|
|
9082
|
+
const rowChildren = (0, react.useMemo)(() => {
|
|
9083
|
+
if (!renderRow) return null;
|
|
9084
|
+
return drawerThreads.map((drawerThread) => {
|
|
9085
|
+
const fullThread = threads.find((t) => t.id === drawerThread.id);
|
|
9086
|
+
if (!fullThread) return null;
|
|
9087
|
+
const content = renderRow(fullThread);
|
|
9088
|
+
if (content === null || content === void 0) return null;
|
|
9089
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
9090
|
+
slot: `row:${drawerThread.id}`,
|
|
9091
|
+
children: content
|
|
9092
|
+
}, drawerThread.id);
|
|
9093
|
+
});
|
|
9094
|
+
}, [
|
|
9095
|
+
renderRow,
|
|
9096
|
+
drawerThreads,
|
|
9097
|
+
threads
|
|
9098
|
+
]);
|
|
9099
|
+
if (!mounted) return null;
|
|
9100
|
+
return react.default.createElement(_copilotkit_web_components_threads_drawer.COPILOTKIT_THREADS_DRAWER_TAG, {
|
|
9101
|
+
ref: elementRef,
|
|
9102
|
+
"data-testid": dataTestId
|
|
9103
|
+
}, rowChildren);
|
|
9104
|
+
}
|
|
9105
|
+
CopilotThreadsDrawer.displayName = "CopilotThreadsDrawer";
|
|
9106
|
+
|
|
8570
9107
|
//#endregion
|
|
8571
9108
|
//#region src/v2/components/WildcardToolCallRender.tsx
|
|
8572
9109
|
const WildcardToolCallRender = defineToolCallRenderer({
|
|
@@ -10929,6 +11466,12 @@ Object.defineProperty(exports, 'CopilotSidebarView', {
|
|
|
10929
11466
|
return CopilotSidebarView;
|
|
10930
11467
|
}
|
|
10931
11468
|
});
|
|
11469
|
+
Object.defineProperty(exports, 'CopilotThreadsDrawer', {
|
|
11470
|
+
enumerable: true,
|
|
11471
|
+
get: function () {
|
|
11472
|
+
return CopilotThreadsDrawer;
|
|
11473
|
+
}
|
|
11474
|
+
});
|
|
10932
11475
|
Object.defineProperty(exports, 'DefaultCloseIcon', {
|
|
10933
11476
|
enumerable: true,
|
|
10934
11477
|
get: function () {
|
|
@@ -11217,4 +11760,4 @@ Object.defineProperty(exports, 'useToast', {
|
|
|
11217
11760
|
return useToast;
|
|
11218
11761
|
}
|
|
11219
11762
|
});
|
|
11220
|
-
//# sourceMappingURL=copilotkit-
|
|
11763
|
+
//# sourceMappingURL=copilotkit-CdpDZi3i.cjs.map
|