@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
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import * as React$1 from "react";
|
|
2
2
|
import React, { createContext, forwardRef, memo, useCallback, useContext, useEffect, useId, useImperativeHandle, useLayoutEffect, useMemo, useReducer, useRef, useState, useSyncExternalStore } from "react";
|
|
3
|
-
import { CopilotKitCore, CopilotKitCoreRuntimeConnectionStatus, ProxiedCopilotRuntimeAgent, ToolCallStatus, isRunCompletionAware, ɵcreateThreadStore, ɵselectHasNextPage, ɵselectIsFetchingNextPage, ɵselectThreads, ɵselectThreadsError, ɵselectThreadsIsLoading } from "@copilotkit/core";
|
|
3
|
+
import { CopilotKitCore, CopilotKitCoreRuntimeConnectionStatus, ProxiedCopilotRuntimeAgent, ToolCallStatus, isRunCompletionAware, ɵcreateThreadStore, ɵselectHasNextPage, ɵselectIsFetchingNextPage, ɵselectIsMutating, ɵselectThreads, ɵselectThreadsError, ɵselectThreadsIsLoading } from "@copilotkit/core";
|
|
4
4
|
import { HttpAgent, buildResumeArray, isInterruptExpired, randomUUID } from "@ag-ui/client";
|
|
5
5
|
import { extendTailwindMerge, twMerge } from "tailwind-merge";
|
|
6
|
-
import { ArrowUp, Check, ChevronDown, ChevronLeft, ChevronRight, ChevronRightIcon, Copy, Edit, Loader2, MessageCircle, Mic, Play, Plus, RefreshCw, Square, ThumbsDown, ThumbsUp, Upload, Volume2, X } from "lucide-react";
|
|
6
|
+
import { ArrowUp, Check, ChevronDown, ChevronLeft, ChevronRight, ChevronRightIcon, Copy, Edit, Loader2, MessageCircle, Mic, PanelLeftOpen, Play, Plus, RefreshCw, Square, ThumbsDown, ThumbsUp, Upload, Volume2, X } from "lucide-react";
|
|
7
7
|
import { A2UI_DEFAULT_DESIGN_GUIDELINES, A2UI_DEFAULT_GENERATION_GUIDELINES, COPILOT_CLOUD_API_URL, COPILOT_CLOUD_CHAT_URL, COPILOT_CLOUD_PUBLIC_API_KEY_HEADER, ConfigurationError, CopilotKitAgentDiscoveryError, CopilotKitApiDiscoveryError, CopilotKitError, CopilotKitErrorCode, CopilotKitLowLevelError, CopilotKitRemoteEndpointDiscoveryError, DEFAULT_AGENT_ID, ErrorVisibility, MissingPublicApiKeyError, Severity, TranscriptionErrorCode, TranscriptionErrorCode as TranscriptionErrorCode$1, copyToClipboard, createLicenseContextValue, dataToUUID, exceedsMaxSize, formatFileSize, generateVideoThumbnail, getDocumentIcon, getModalityFromMimeType, getSourceUrl, matchesAcceptFilter, parseJson, partialJSONParse, randomId, randomUUID as randomUUID$1, readFileAsBase64, schemaToJsonSchema } from "@copilotkit/shared";
|
|
8
8
|
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
9
9
|
import { Slot } from "@radix-ui/react-slot";
|
|
@@ -19,6 +19,7 @@ import { zodToJsonSchema } from "zod-to-json-schema";
|
|
|
19
19
|
import { createPortal, flushSync } from "react-dom";
|
|
20
20
|
import { useVirtualizer } from "@tanstack/react-virtual";
|
|
21
21
|
import { StickToBottom, useStickToBottomContext } from "use-stick-to-bottom";
|
|
22
|
+
import { COPILOTKIT_THREADS_DRAWER_TAG, defineCopilotKitThreadsDrawer } from "@copilotkit/web-components/threads-drawer";
|
|
22
23
|
import ReactMarkdown from "react-markdown";
|
|
23
24
|
|
|
24
25
|
//#region src/v2/lib/slots.tsx
|
|
@@ -144,6 +145,25 @@ const CopilotChatDefaultLabels = {
|
|
|
144
145
|
modalHeaderTitle: "CopilotKit Chat",
|
|
145
146
|
welcomeMessageText: "How can I help you today?"
|
|
146
147
|
};
|
|
148
|
+
/**
|
|
149
|
+
* Mobile breakpoint below which the chat modal and the thread-list drawer are
|
|
150
|
+
* mutually exclusive. At or above this width both surfaces may coexist. This
|
|
151
|
+
* mirrors the `(max-width: 767px)` / `(min-width: 768px)` split already used by
|
|
152
|
+
* CopilotChatInput and CopilotSidebarView.
|
|
153
|
+
*/
|
|
154
|
+
const MOBILE_MAX_WIDTH_PX = 767;
|
|
155
|
+
/**
|
|
156
|
+
* Reports whether the current viewport is in the mobile range (`<768px`), where
|
|
157
|
+
* the chat modal and drawer must not be open simultaneously. SSR-safe and
|
|
158
|
+
* defensive against environments without `matchMedia` (treated as desktop, so
|
|
159
|
+
* no mutual-exclusion constraint is applied).
|
|
160
|
+
*
|
|
161
|
+
* @returns `true` when the viewport is mobile-width, `false` otherwise.
|
|
162
|
+
*/
|
|
163
|
+
function isMobileViewport() {
|
|
164
|
+
if (typeof window === "undefined" || typeof window.matchMedia !== "function") return false;
|
|
165
|
+
return window.matchMedia(`(max-width: ${MOBILE_MAX_WIDTH_PX}px)`).matches;
|
|
166
|
+
}
|
|
147
167
|
const CopilotChatConfiguration = createContext(null);
|
|
148
168
|
const CopilotChatConfigurationProvider = ({ children, labels, agentId, threadId, hasExplicitThreadId, isModalDefaultOpen }) => {
|
|
149
169
|
const parentConfig = useContext(CopilotChatConfiguration);
|
|
@@ -154,12 +174,22 @@ const CopilotChatConfigurationProvider = ({ children, labels, agentId, threadId,
|
|
|
154
174
|
...stableLabels
|
|
155
175
|
}), [stableLabels, parentConfig?.labels]);
|
|
156
176
|
const resolvedAgentId = agentId ?? parentConfig?.agentId ?? DEFAULT_AGENT_ID;
|
|
177
|
+
const threadIdPropIsAuthoritative = threadId !== void 0 && hasExplicitThreadId !== false;
|
|
178
|
+
const isThreadIdControlled = threadIdPropIsAuthoritative;
|
|
179
|
+
const [activeThreadOverride, setActiveThreadOverride] = useState(null);
|
|
157
180
|
const resolvedThreadId = useMemo(() => {
|
|
158
|
-
if (
|
|
181
|
+
if (threadIdPropIsAuthoritative) return threadId;
|
|
182
|
+
if (activeThreadOverride) return activeThreadOverride.threadId;
|
|
159
183
|
if (parentConfig?.threadId) return parentConfig.threadId;
|
|
184
|
+
if (threadId) return threadId;
|
|
160
185
|
return randomUUID$1();
|
|
161
|
-
}, [
|
|
162
|
-
|
|
186
|
+
}, [
|
|
187
|
+
threadIdPropIsAuthoritative,
|
|
188
|
+
threadId,
|
|
189
|
+
parentConfig?.threadId,
|
|
190
|
+
activeThreadOverride
|
|
191
|
+
]);
|
|
192
|
+
const resolvedHasExplicitThreadId = (threadIdPropIsAuthoritative ? true : activeThreadOverride?.explicit ?? hasExplicitThreadId ?? false) || !!parentConfig?.hasExplicitThreadId;
|
|
163
193
|
const [internalModalOpen, setInternalModalOpen] = useState(isModalDefaultOpen ?? true);
|
|
164
194
|
const hasExplicitDefault = isModalDefaultOpen !== void 0;
|
|
165
195
|
const setAndSync = useCallback((open) => {
|
|
@@ -178,20 +208,113 @@ const CopilotChatConfigurationProvider = ({ children, labels, agentId, threadId,
|
|
|
178
208
|
}, [parentConfig?.isModalOpen, hasExplicitDefault]);
|
|
179
209
|
const resolvedIsModalOpen = hasExplicitDefault ? internalModalOpen : parentConfig?.isModalOpen ?? internalModalOpen;
|
|
180
210
|
const resolvedSetModalOpen = hasExplicitDefault ? setAndSync : parentConfig?.setModalOpen ?? setInternalModalOpen;
|
|
211
|
+
const [ownDrawerOpen, setOwnDrawerOpen] = useState(false);
|
|
212
|
+
const [ownDrawerCount, setOwnDrawerCount] = useState(0);
|
|
213
|
+
const modalCloseRef = useRef(() => {});
|
|
214
|
+
modalCloseRef.current = resolvedSetModalOpen;
|
|
215
|
+
const registeredModalClosersRef = useRef([]);
|
|
216
|
+
const ownRegisterModalCloser = useCallback((closeModal) => {
|
|
217
|
+
registeredModalClosersRef.current.push(closeModal);
|
|
218
|
+
return () => {
|
|
219
|
+
registeredModalClosersRef.current = registeredModalClosersRef.current.filter((entry) => entry !== closeModal);
|
|
220
|
+
};
|
|
221
|
+
}, []);
|
|
222
|
+
const ownSetDrawerOpen = useCallback((open) => {
|
|
223
|
+
setOwnDrawerOpen(open);
|
|
224
|
+
if (open && isMobileViewport()) {
|
|
225
|
+
const registered = registeredModalClosersRef.current;
|
|
226
|
+
(registered.length > 0 ? registered[registered.length - 1] : modalCloseRef.current)(false);
|
|
227
|
+
}
|
|
228
|
+
}, []);
|
|
229
|
+
const ownRegisterDrawer = useCallback(() => {
|
|
230
|
+
setOwnDrawerCount((count) => count + 1);
|
|
231
|
+
return () => {
|
|
232
|
+
setOwnDrawerCount((count) => Math.max(0, count - 1));
|
|
233
|
+
};
|
|
234
|
+
}, []);
|
|
235
|
+
const resolvedDrawerOpen = parentConfig ? parentConfig.drawerOpen : ownDrawerOpen;
|
|
236
|
+
const resolvedSetDrawerOpen = parentConfig ? parentConfig.setDrawerOpen : ownSetDrawerOpen;
|
|
237
|
+
const resolvedDrawerRegistered = parentConfig ? parentConfig.drawerRegistered : ownDrawerCount > 0;
|
|
238
|
+
const resolvedRegisterDrawer = parentConfig ? parentConfig.registerDrawer : ownRegisterDrawer;
|
|
239
|
+
const resolvedRegisterModalCloser = parentConfig ? parentConfig.ɵregisterModalCloser : ownRegisterModalCloser;
|
|
240
|
+
useEffect(() => {
|
|
241
|
+
if (!hasExplicitDefault) return;
|
|
242
|
+
return resolvedRegisterModalCloser(resolvedSetModalOpen);
|
|
243
|
+
}, [
|
|
244
|
+
hasExplicitDefault,
|
|
245
|
+
resolvedRegisterModalCloser,
|
|
246
|
+
resolvedSetModalOpen
|
|
247
|
+
]);
|
|
248
|
+
const isThreadIdControlledRef = useRef(isThreadIdControlled);
|
|
249
|
+
isThreadIdControlledRef.current = isThreadIdControlled;
|
|
250
|
+
const ownSetActiveThreadId = useCallback((id, options) => {
|
|
251
|
+
setActiveThreadOverride({
|
|
252
|
+
threadId: id,
|
|
253
|
+
explicit: options?.explicit ?? true
|
|
254
|
+
});
|
|
255
|
+
}, []);
|
|
256
|
+
const ownStartNewThread = useCallback(() => {
|
|
257
|
+
setActiveThreadOverride({
|
|
258
|
+
threadId: randomUUID$1(),
|
|
259
|
+
explicit: false
|
|
260
|
+
});
|
|
261
|
+
}, []);
|
|
262
|
+
const parentSetActiveThreadId = parentConfig?.setActiveThreadId;
|
|
263
|
+
const parentStartNewThread = parentConfig?.startNewThread;
|
|
264
|
+
const resolvedSetActiveThreadId = useCallback((id, options) => {
|
|
265
|
+
if (isThreadIdControlledRef.current) {
|
|
266
|
+
console.warn("[CopilotKit] Ignoring setActiveThreadId(): threadId is controlled via the `threadId` prop on CopilotChatConfigurationProvider.");
|
|
267
|
+
return;
|
|
268
|
+
}
|
|
269
|
+
if (parentSetActiveThreadId) {
|
|
270
|
+
parentSetActiveThreadId(id, options);
|
|
271
|
+
return;
|
|
272
|
+
}
|
|
273
|
+
ownSetActiveThreadId(id, options);
|
|
274
|
+
}, [parentSetActiveThreadId, ownSetActiveThreadId]);
|
|
275
|
+
const resolvedStartNewThread = useCallback(() => {
|
|
276
|
+
if (isThreadIdControlledRef.current) {
|
|
277
|
+
console.warn("[CopilotKit] Ignoring startNewThread(): threadId is controlled via the `threadId` prop on CopilotChatConfigurationProvider.");
|
|
278
|
+
return;
|
|
279
|
+
}
|
|
280
|
+
if (parentStartNewThread) {
|
|
281
|
+
parentStartNewThread();
|
|
282
|
+
return;
|
|
283
|
+
}
|
|
284
|
+
ownStartNewThread();
|
|
285
|
+
}, [parentStartNewThread, ownStartNewThread]);
|
|
286
|
+
const setModalOpenWithDrawerExclusion = useCallback((open) => {
|
|
287
|
+
if (open && isMobileViewport()) resolvedSetDrawerOpen(false);
|
|
288
|
+
resolvedSetModalOpen(open);
|
|
289
|
+
}, [resolvedSetModalOpen, resolvedSetDrawerOpen]);
|
|
181
290
|
const configurationValue = useMemo(() => ({
|
|
182
291
|
labels: mergedLabels,
|
|
183
292
|
agentId: resolvedAgentId,
|
|
184
293
|
threadId: resolvedThreadId,
|
|
185
294
|
hasExplicitThreadId: resolvedHasExplicitThreadId,
|
|
186
295
|
isModalOpen: resolvedIsModalOpen,
|
|
187
|
-
setModalOpen:
|
|
296
|
+
setModalOpen: setModalOpenWithDrawerExclusion,
|
|
297
|
+
drawerOpen: resolvedDrawerOpen,
|
|
298
|
+
setDrawerOpen: resolvedSetDrawerOpen,
|
|
299
|
+
drawerRegistered: resolvedDrawerRegistered,
|
|
300
|
+
registerDrawer: resolvedRegisterDrawer,
|
|
301
|
+
ɵregisterModalCloser: resolvedRegisterModalCloser,
|
|
302
|
+
setActiveThreadId: resolvedSetActiveThreadId,
|
|
303
|
+
startNewThread: resolvedStartNewThread
|
|
188
304
|
}), [
|
|
189
305
|
mergedLabels,
|
|
190
306
|
resolvedAgentId,
|
|
191
307
|
resolvedThreadId,
|
|
192
308
|
resolvedHasExplicitThreadId,
|
|
193
309
|
resolvedIsModalOpen,
|
|
194
|
-
|
|
310
|
+
setModalOpenWithDrawerExclusion,
|
|
311
|
+
resolvedDrawerOpen,
|
|
312
|
+
resolvedSetDrawerOpen,
|
|
313
|
+
resolvedDrawerRegistered,
|
|
314
|
+
resolvedRegisterDrawer,
|
|
315
|
+
resolvedRegisterModalCloser,
|
|
316
|
+
resolvedSetActiveThreadId,
|
|
317
|
+
resolvedStartNewThread
|
|
195
318
|
]);
|
|
196
319
|
return /* @__PURE__ */ jsx(CopilotChatConfiguration.Provider, {
|
|
197
320
|
value: configurationValue,
|
|
@@ -1112,7 +1235,7 @@ function CopilotChatInput({ mode = "input", onSubmitMessage, onStop, isRunning =
|
|
|
1112
1235
|
},
|
|
1113
1236
|
...props,
|
|
1114
1237
|
children: [/* @__PURE__ */ jsx("div", {
|
|
1115
|
-
className: "cpk:max-w-3xl cpk:mx-auto cpk:py-0 cpk:px-4 cpk:
|
|
1238
|
+
className: "cpk:max-w-3xl cpk:mx-auto cpk:py-0 cpk:px-4 cpk:@3xl:px-0 cpk:[div[data-sidebar-chat]_&]:px-8 cpk:[div[data-popup-chat]_&]:px-4 cpk:pointer-events-auto",
|
|
1116
1239
|
children: inputPill
|
|
1117
1240
|
}), shouldShowDisclaimer && BoundDisclaimer]
|
|
1118
1241
|
});
|
|
@@ -1400,307 +1523,25 @@ const useCopilotKit = () => {
|
|
|
1400
1523
|
useEffect(() => {
|
|
1401
1524
|
const subscription = context.copilotkit.subscribe({
|
|
1402
1525
|
onRuntimeConnectionStatusChanged: () => {
|
|
1403
|
-
forceUpdate();
|
|
1404
|
-
},
|
|
1405
|
-
onHeadersChanged: () => {
|
|
1406
|
-
forceUpdate();
|
|
1407
|
-
}
|
|
1408
|
-
});
|
|
1409
|
-
return () => {
|
|
1410
|
-
subscription.unsubscribe();
|
|
1411
|
-
};
|
|
1412
|
-
}, []);
|
|
1413
|
-
return context;
|
|
1414
|
-
};
|
|
1415
|
-
const LicenseContext = createContext({
|
|
1416
|
-
status: null,
|
|
1417
|
-
license: null,
|
|
1418
|
-
checkFeature: () => true,
|
|
1419
|
-
getLimit: () => null
|
|
1420
|
-
});
|
|
1421
|
-
const useLicenseContext = () => useContext(LicenseContext);
|
|
1422
|
-
|
|
1423
|
-
//#endregion
|
|
1424
|
-
//#region src/v2/types/defineToolCallRenderer.ts
|
|
1425
|
-
function defineToolCallRenderer(def) {
|
|
1426
|
-
const argsSchema = def.name === "*" && !def.args ? z.any() : def.args;
|
|
1427
|
-
return {
|
|
1428
|
-
name: def.name,
|
|
1429
|
-
args: argsSchema,
|
|
1430
|
-
render: def.render,
|
|
1431
|
-
...def.agentId ? { agentId: def.agentId } : {}
|
|
1432
|
-
};
|
|
1433
|
-
}
|
|
1434
|
-
|
|
1435
|
-
//#endregion
|
|
1436
|
-
//#region src/v2/hooks/use-render-tool.tsx
|
|
1437
|
-
const EMPTY_DEPS$1 = [];
|
|
1438
|
-
/**
|
|
1439
|
-
* Registers a renderer entry in CopilotKit's `renderToolCalls` registry.
|
|
1440
|
-
*
|
|
1441
|
-
* Key behavior:
|
|
1442
|
-
* - deduplicates by `agentId:name` (latest registration wins),
|
|
1443
|
-
* - keeps renderer entries on cleanup so historical chat tool calls can still render,
|
|
1444
|
-
* - refreshes registration when `deps` change.
|
|
1445
|
-
*
|
|
1446
|
-
* @typeParam S - Schema type describing tool call parameters.
|
|
1447
|
-
* @param config - Renderer config for wildcard or named tools.
|
|
1448
|
-
* @param deps - Optional dependencies to refresh registration.
|
|
1449
|
-
*
|
|
1450
|
-
* @example
|
|
1451
|
-
* ```tsx
|
|
1452
|
-
* useRenderTool(
|
|
1453
|
-
* {
|
|
1454
|
-
* name: "searchDocs",
|
|
1455
|
-
* parameters: z.object({ query: z.string() }),
|
|
1456
|
-
* render: ({ status, parameters, result }) => {
|
|
1457
|
-
* if (status === "executing") return <div>Searching {parameters.query}</div>;
|
|
1458
|
-
* if (status === "complete") return <div>{result}</div>;
|
|
1459
|
-
* return <div>Preparing...</div>;
|
|
1460
|
-
* },
|
|
1461
|
-
* },
|
|
1462
|
-
* [],
|
|
1463
|
-
* );
|
|
1464
|
-
* ```
|
|
1465
|
-
*
|
|
1466
|
-
* @example
|
|
1467
|
-
* ```tsx
|
|
1468
|
-
* useRenderTool(
|
|
1469
|
-
* {
|
|
1470
|
-
* name: "summarize",
|
|
1471
|
-
* parameters: z.object({ text: z.string() }),
|
|
1472
|
-
* agentId: "research-agent",
|
|
1473
|
-
* render: ({ name, status }) => <div>{name}: {status}</div>,
|
|
1474
|
-
* },
|
|
1475
|
-
* [selectedAgentId],
|
|
1476
|
-
* );
|
|
1477
|
-
* ```
|
|
1478
|
-
*/
|
|
1479
|
-
function useRenderTool(config, deps) {
|
|
1480
|
-
const { copilotkit } = useCopilotKit();
|
|
1481
|
-
const extraDeps = deps ?? EMPTY_DEPS$1;
|
|
1482
|
-
useEffect(() => {
|
|
1483
|
-
const renderer = config.name === "*" && !config.parameters ? defineToolCallRenderer({
|
|
1484
|
-
name: "*",
|
|
1485
|
-
render: (props) => config.render({
|
|
1486
|
-
...props,
|
|
1487
|
-
parameters: props.args
|
|
1488
|
-
}),
|
|
1489
|
-
...config.agentId ? { agentId: config.agentId } : {}
|
|
1490
|
-
}) : defineToolCallRenderer({
|
|
1491
|
-
name: config.name,
|
|
1492
|
-
args: config.parameters,
|
|
1493
|
-
render: (props) => {
|
|
1494
|
-
if (props.status === ToolCallStatus.InProgress) return config.render({
|
|
1495
|
-
...props,
|
|
1496
|
-
parameters: props.args
|
|
1497
|
-
});
|
|
1498
|
-
if (props.status === ToolCallStatus.Executing) return config.render({
|
|
1499
|
-
...props,
|
|
1500
|
-
parameters: props.args
|
|
1501
|
-
});
|
|
1502
|
-
return config.render({
|
|
1503
|
-
...props,
|
|
1504
|
-
parameters: props.args
|
|
1505
|
-
});
|
|
1506
|
-
},
|
|
1507
|
-
...config.agentId ? { agentId: config.agentId } : {}
|
|
1508
|
-
});
|
|
1509
|
-
copilotkit.addHookRenderToolCall(renderer);
|
|
1510
|
-
}, [
|
|
1511
|
-
config.name,
|
|
1512
|
-
copilotkit,
|
|
1513
|
-
JSON.stringify(extraDeps)
|
|
1514
|
-
]);
|
|
1515
|
-
}
|
|
1516
|
-
|
|
1517
|
-
//#endregion
|
|
1518
|
-
//#region src/v2/hooks/use-default-render-tool.tsx
|
|
1519
|
-
/**
|
|
1520
|
-
* Module-level dedup set so an unknown status value only emits a console
|
|
1521
|
-
* warning the FIRST time we encounter it. Otherwise a stuck/unmapped status
|
|
1522
|
-
* would log on every re-render (potentially many per second).
|
|
1523
|
-
*/
|
|
1524
|
-
const warnedUnknownStatuses = /* @__PURE__ */ new Set();
|
|
1525
|
-
/**
|
|
1526
|
-
* Map a {@link ToolCallStatus} enum value to the documented string-union
|
|
1527
|
-
* status the {@link DefaultRenderProps} contract exposes. Unknown / future
|
|
1528
|
-
* enum members log a warning (once per distinct value) and fall back to
|
|
1529
|
-
* `"inProgress"`.
|
|
1530
|
-
*/
|
|
1531
|
-
function mapToolCallStatus(status) {
|
|
1532
|
-
switch (status) {
|
|
1533
|
-
case ToolCallStatus.Complete: return "complete";
|
|
1534
|
-
case ToolCallStatus.Executing: return "executing";
|
|
1535
|
-
case ToolCallStatus.InProgress: return "inProgress";
|
|
1536
|
-
default: {
|
|
1537
|
-
const key = String(status);
|
|
1538
|
-
if (!warnedUnknownStatuses.has(key)) {
|
|
1539
|
-
warnedUnknownStatuses.add(key);
|
|
1540
|
-
console.warn(`[CopilotKit] Unknown ToolCallStatus "${key}" in default tool-call renderer; falling back to "inProgress".`);
|
|
1541
|
-
}
|
|
1542
|
-
return "inProgress";
|
|
1543
|
-
}
|
|
1544
|
-
}
|
|
1545
|
-
}
|
|
1546
|
-
/**
|
|
1547
|
-
* Convert the framework-internal renderer props (`args`, enum status) into
|
|
1548
|
-
* the documented {@link DefaultRenderProps} shape (`parameters`, string-union
|
|
1549
|
-
* status) so a user `config.render` always sees the documented contract.
|
|
1550
|
-
*/
|
|
1551
|
-
function adaptRendererProps(props) {
|
|
1552
|
-
return {
|
|
1553
|
-
name: props.name,
|
|
1554
|
-
toolCallId: props.toolCallId,
|
|
1555
|
-
parameters: props.args,
|
|
1556
|
-
status: mapToolCallStatus(props.status),
|
|
1557
|
-
result: props.result
|
|
1558
|
-
};
|
|
1559
|
-
}
|
|
1560
|
-
/**
|
|
1561
|
-
* Registers a wildcard (`"*"`) tool-call renderer via `useRenderTool`.
|
|
1562
|
-
*
|
|
1563
|
-
* - Call with no config to use CopilotKit's built-in default tool-call card.
|
|
1564
|
-
* - Pass `config.render` to replace the default UI with your own fallback renderer.
|
|
1565
|
-
*
|
|
1566
|
-
* This is useful when you want a generic renderer for tools that do not have a
|
|
1567
|
-
* dedicated `useRenderTool({ name: "..." })` registration.
|
|
1568
|
-
*
|
|
1569
|
-
* @param config - Optional custom wildcard render function.
|
|
1570
|
-
* @param deps - Optional dependencies to refresh registration.
|
|
1571
|
-
*
|
|
1572
|
-
* @example
|
|
1573
|
-
* ```tsx
|
|
1574
|
-
* useDefaultRenderTool();
|
|
1575
|
-
* ```
|
|
1576
|
-
*
|
|
1577
|
-
* @example
|
|
1578
|
-
* ```tsx
|
|
1579
|
-
* useDefaultRenderTool({
|
|
1580
|
-
* render: ({ name, status }) => <div>{name}: {status}</div>,
|
|
1581
|
-
* });
|
|
1582
|
-
* ```
|
|
1583
|
-
*
|
|
1584
|
-
* @example
|
|
1585
|
-
* ```tsx
|
|
1586
|
-
* useDefaultRenderTool(
|
|
1587
|
-
* {
|
|
1588
|
-
* render: ({ name, result }) => (
|
|
1589
|
-
* <ToolEventRow title={name} payload={result} compact={compactMode} />
|
|
1590
|
-
* ),
|
|
1591
|
-
* },
|
|
1592
|
-
* [compactMode],
|
|
1593
|
-
* );
|
|
1594
|
-
* ```
|
|
1595
|
-
*/
|
|
1596
|
-
function useDefaultRenderTool(config, deps) {
|
|
1597
|
-
const userRender = config?.render;
|
|
1598
|
-
useRenderTool({
|
|
1599
|
-
name: "*",
|
|
1600
|
-
render: userRender ? (raw) => userRender(adaptRendererProps(raw)) : (raw) => /* @__PURE__ */ jsx(DefaultToolCallRenderer, { ...adaptRendererProps(raw) })
|
|
1601
|
-
}, deps);
|
|
1602
|
-
}
|
|
1603
|
-
/**
|
|
1604
|
-
* Guarded JSON.stringify used inside the expanded `<pre>` blocks. A circular
|
|
1605
|
-
* reference would otherwise crash the entire React tree on render.
|
|
1606
|
-
*/
|
|
1607
|
-
function safeStringifyForPre(value) {
|
|
1608
|
-
try {
|
|
1609
|
-
return JSON.stringify(value, null, 2);
|
|
1610
|
-
} catch (err) {
|
|
1611
|
-
console.warn("[CopilotKit] Failed to JSON.stringify tool-call payload for default renderer; falling back to String():", err);
|
|
1612
|
-
try {
|
|
1613
|
-
return String(value);
|
|
1614
|
-
} catch (innerErr) {
|
|
1615
|
-
console.warn("[CopilotKit] safeStringifyForPre: value could not be stringified:", innerErr);
|
|
1616
|
-
return "[unserializable]";
|
|
1617
|
-
}
|
|
1618
|
-
}
|
|
1619
|
-
}
|
|
1620
|
-
function DefaultToolCallRenderer({ name, toolCallId, parameters, status, result }) {
|
|
1621
|
-
const [isExpanded, setIsExpanded] = useState(false);
|
|
1622
|
-
const isActive = status === "inProgress" || status === "executing";
|
|
1623
|
-
const isComplete = status === "complete";
|
|
1624
|
-
const statusLabel = isActive ? "Running" : isComplete ? "Done" : status;
|
|
1625
|
-
const dotClassName = isActive ? "cpk:bg-amber-500" : isComplete ? "cpk:bg-emerald-500" : "cpk:bg-zinc-400";
|
|
1626
|
-
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";
|
|
1627
|
-
return /* @__PURE__ */ jsx("div", {
|
|
1628
|
-
"data-testid": "copilot-tool-render",
|
|
1629
|
-
"data-tool-name": name,
|
|
1630
|
-
"data-tool-call-id": toolCallId,
|
|
1631
|
-
"data-status": status,
|
|
1632
|
-
"data-args": safeStringifyForAttr(parameters),
|
|
1633
|
-
"data-result": safeStringifyForAttr(result),
|
|
1634
|
-
className: "cpk:mt-2 cpk:pb-2",
|
|
1635
|
-
children: /* @__PURE__ */ jsxs("div", {
|
|
1636
|
-
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",
|
|
1637
|
-
children: [/* @__PURE__ */ jsxs("button", {
|
|
1638
|
-
type: "button",
|
|
1639
|
-
"aria-expanded": isExpanded,
|
|
1640
|
-
onClick: () => setIsExpanded(!isExpanded),
|
|
1641
|
-
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",
|
|
1642
|
-
style: { font: "inherit" },
|
|
1643
|
-
children: [/* @__PURE__ */ jsxs("div", {
|
|
1644
|
-
className: "cpk:flex cpk:min-w-0 cpk:items-center cpk:gap-2",
|
|
1645
|
-
children: [
|
|
1646
|
-
/* @__PURE__ */ jsx("svg", {
|
|
1647
|
-
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" : ""}`,
|
|
1648
|
-
fill: "none",
|
|
1649
|
-
viewBox: "0 0 24 24",
|
|
1650
|
-
strokeWidth: 2,
|
|
1651
|
-
stroke: "currentColor",
|
|
1652
|
-
children: /* @__PURE__ */ jsx("path", {
|
|
1653
|
-
strokeLinecap: "round",
|
|
1654
|
-
strokeLinejoin: "round",
|
|
1655
|
-
d: "M8.25 4.5l7.5 7.5-7.5 7.5"
|
|
1656
|
-
})
|
|
1657
|
-
}),
|
|
1658
|
-
/* @__PURE__ */ jsx("span", { className: `cpk:inline-block cpk:h-2 cpk:w-2 cpk:flex-shrink-0 cpk:rounded-full ${dotClassName}` }),
|
|
1659
|
-
/* @__PURE__ */ jsx("span", {
|
|
1660
|
-
"data-testid": "copilot-tool-render-name",
|
|
1661
|
-
className: "cpk:truncate cpk:text-[13px] cpk:font-semibold cpk:text-zinc-900 cpk:dark:text-zinc-100",
|
|
1662
|
-
children: name
|
|
1663
|
-
})
|
|
1664
|
-
]
|
|
1665
|
-
}), /* @__PURE__ */ jsx("span", {
|
|
1666
|
-
"data-testid": "copilot-tool-render-status",
|
|
1667
|
-
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}`,
|
|
1668
|
-
children: statusLabel
|
|
1669
|
-
})]
|
|
1670
|
-
}), isExpanded && /* @__PURE__ */ jsxs("div", {
|
|
1671
|
-
className: "cpk:mt-3 cpk:grid cpk:gap-3",
|
|
1672
|
-
children: [/* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("div", {
|
|
1673
|
-
className: "cpk:text-[10px] cpk:uppercase cpk:text-zinc-500 cpk:dark:text-zinc-400",
|
|
1674
|
-
children: "Arguments"
|
|
1675
|
-
}), /* @__PURE__ */ jsx("pre", {
|
|
1676
|
-
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",
|
|
1677
|
-
children: safeStringifyForPre(parameters ?? {})
|
|
1678
|
-
})] }), result !== void 0 && /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("div", {
|
|
1679
|
-
className: "cpk:text-[10px] cpk:uppercase cpk:text-zinc-500 cpk:dark:text-zinc-400",
|
|
1680
|
-
children: "Result"
|
|
1681
|
-
}), /* @__PURE__ */ jsx("pre", {
|
|
1682
|
-
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",
|
|
1683
|
-
children: typeof result === "string" ? result : safeStringifyForPre(result)
|
|
1684
|
-
})] })]
|
|
1685
|
-
})]
|
|
1686
|
-
})
|
|
1687
|
-
});
|
|
1688
|
-
}
|
|
1689
|
-
function safeStringifyForAttr(value) {
|
|
1690
|
-
if (value === void 0 || value === null) return "";
|
|
1691
|
-
if (typeof value === "string") return value;
|
|
1692
|
-
try {
|
|
1693
|
-
return JSON.stringify(value);
|
|
1694
|
-
} catch (err) {
|
|
1695
|
-
console.warn("[CopilotKit] Failed to JSON.stringify tool-call payload for data-* attribute; falling back to String():", err);
|
|
1696
|
-
try {
|
|
1697
|
-
return String(value);
|
|
1698
|
-
} catch (innerErr) {
|
|
1699
|
-
console.warn("[CopilotKit] safeStringifyForAttr: value could not be stringified:", innerErr);
|
|
1700
|
-
return "";
|
|
1701
|
-
}
|
|
1702
|
-
}
|
|
1703
|
-
}
|
|
1526
|
+
forceUpdate();
|
|
1527
|
+
},
|
|
1528
|
+
onHeadersChanged: () => {
|
|
1529
|
+
forceUpdate();
|
|
1530
|
+
}
|
|
1531
|
+
});
|
|
1532
|
+
return () => {
|
|
1533
|
+
subscription.unsubscribe();
|
|
1534
|
+
};
|
|
1535
|
+
}, []);
|
|
1536
|
+
return context;
|
|
1537
|
+
};
|
|
1538
|
+
const LicenseContext = createContext({
|
|
1539
|
+
status: null,
|
|
1540
|
+
license: null,
|
|
1541
|
+
checkFeature: () => true,
|
|
1542
|
+
getLimit: () => null
|
|
1543
|
+
});
|
|
1544
|
+
const useLicenseContext = () => useContext(LicenseContext);
|
|
1704
1545
|
|
|
1705
1546
|
//#endregion
|
|
1706
1547
|
//#region src/v2/hooks/use-render-tool-call.tsx
|
|
@@ -1756,10 +1597,13 @@ function useRenderToolCall() {
|
|
|
1756
1597
|
}, () => copilotkit.renderToolCalls, () => copilotkit.renderToolCalls);
|
|
1757
1598
|
return useCallback(({ toolCall, toolMessage }) => {
|
|
1758
1599
|
const exactMatches = renderToolCalls.filter((rc) => rc.name === toolCall.function.name);
|
|
1600
|
+
const renderConfig = exactMatches.find((rc) => rc.agentId === agentId) || exactMatches.find((rc) => !rc.agentId) || exactMatches[0] || renderToolCalls.find((rc) => rc.name === "*");
|
|
1601
|
+
if (!renderConfig) return null;
|
|
1602
|
+
const RenderComponent = renderConfig.render;
|
|
1759
1603
|
return /* @__PURE__ */ jsx(ToolCallRenderer, {
|
|
1760
1604
|
toolCall,
|
|
1761
1605
|
toolMessage,
|
|
1762
|
-
RenderComponent
|
|
1606
|
+
RenderComponent,
|
|
1763
1607
|
isExecuting: executingToolCallIds.has(toolCall.id)
|
|
1764
1608
|
}, toolCall.id);
|
|
1765
1609
|
}, [
|
|
@@ -1768,15 +1612,6 @@ function useRenderToolCall() {
|
|
|
1768
1612
|
agentId
|
|
1769
1613
|
]);
|
|
1770
1614
|
}
|
|
1771
|
-
function defaultToolCallRenderAdapter(props) {
|
|
1772
|
-
return /* @__PURE__ */ jsx(DefaultToolCallRenderer, {
|
|
1773
|
-
name: props.name,
|
|
1774
|
-
toolCallId: props.toolCallId,
|
|
1775
|
-
parameters: props.args,
|
|
1776
|
-
status: mapToolCallStatus(props.status),
|
|
1777
|
-
result: props.result
|
|
1778
|
-
});
|
|
1779
|
-
}
|
|
1780
1615
|
|
|
1781
1616
|
//#endregion
|
|
1782
1617
|
//#region src/v2/components/CopilotKitInspector.tsx
|
|
@@ -3258,7 +3093,7 @@ const A2UISurfaceContentSchema = z.object({
|
|
|
3258
3093
|
...A2UILifecycleFields
|
|
3259
3094
|
}).passthrough();
|
|
3260
3095
|
function createA2UIMessageRenderer(options) {
|
|
3261
|
-
const { theme, catalog, loadingComponent, recovery } = options;
|
|
3096
|
+
const { theme, catalog, loadingComponent, recovery, onAction } = options;
|
|
3262
3097
|
const showAfterMs = recovery?.showAfterMs ?? 2e3;
|
|
3263
3098
|
const showAfterAttempts = recovery?.showAfterAttempts ?? 2;
|
|
3264
3099
|
const optionDebugExposure = recovery?.debugExposure ?? "collapsed";
|
|
@@ -3334,6 +3169,7 @@ function createA2UIMessageRenderer(options) {
|
|
|
3334
3169
|
agent,
|
|
3335
3170
|
copilotkit,
|
|
3336
3171
|
catalog,
|
|
3172
|
+
onAction,
|
|
3337
3173
|
onReady: markSurfaceReady
|
|
3338
3174
|
}, surfaceId))
|
|
3339
3175
|
});
|
|
@@ -3354,29 +3190,60 @@ function createA2UIMessageRenderer(options) {
|
|
|
3354
3190
|
};
|
|
3355
3191
|
}
|
|
3356
3192
|
/**
|
|
3193
|
+
* Orchestrates a single A2UI user action: runs the optional `onAction`
|
|
3194
|
+
* interceptor first, then forwards to the agent unless the interceptor
|
|
3195
|
+
* suppressed it (returned `null`). Exported for unit testing; the wiring lives
|
|
3196
|
+
* in {@link ReactSurfaceHost}.
|
|
3197
|
+
*/
|
|
3198
|
+
async function runA2UIAction({ message, agent, copilotkit, onAction }) {
|
|
3199
|
+
if (!agent) return;
|
|
3200
|
+
const action = message.userAction;
|
|
3201
|
+
const forward = async (forwardAction) => {
|
|
3202
|
+
const a2uiAction = forwardAction !== void 0 ? {
|
|
3203
|
+
...message,
|
|
3204
|
+
userAction: forwardAction
|
|
3205
|
+
} : message;
|
|
3206
|
+
try {
|
|
3207
|
+
copilotkit.setProperties({
|
|
3208
|
+
...copilotkit.properties,
|
|
3209
|
+
a2uiAction
|
|
3210
|
+
});
|
|
3211
|
+
await copilotkit.runAgent({ agent });
|
|
3212
|
+
} finally {
|
|
3213
|
+
if (copilotkit.properties) {
|
|
3214
|
+
const { a2uiAction: _omit, ...rest } = copilotkit.properties;
|
|
3215
|
+
copilotkit.setProperties(rest);
|
|
3216
|
+
}
|
|
3217
|
+
}
|
|
3218
|
+
};
|
|
3219
|
+
if (onAction && action) {
|
|
3220
|
+
const result = await onAction(action, forward);
|
|
3221
|
+
if (result === null) return;
|
|
3222
|
+
if (result) {
|
|
3223
|
+
await forward(result);
|
|
3224
|
+
return;
|
|
3225
|
+
}
|
|
3226
|
+
}
|
|
3227
|
+
await forward();
|
|
3228
|
+
}
|
|
3229
|
+
/**
|
|
3357
3230
|
* Renders a single A2UI surface using the React renderer.
|
|
3358
3231
|
* Wraps A2UIProvider + A2UIRenderer and bridges actions back to CopilotKit.
|
|
3359
3232
|
*/
|
|
3360
|
-
function ReactSurfaceHost({ surfaceId, operations, theme, agent, copilotkit, catalog, onReady }) {
|
|
3233
|
+
function ReactSurfaceHost({ surfaceId, operations, theme, agent, copilotkit, catalog, onAction, onReady }) {
|
|
3361
3234
|
return /* @__PURE__ */ jsx("div", {
|
|
3362
3235
|
className: "cpk:flex cpk:w-full cpk:flex-none cpk:flex-col cpk:gap-4",
|
|
3363
3236
|
children: /* @__PURE__ */ jsxs(A2UIProvider, {
|
|
3364
|
-
onAction: useCallback(
|
|
3365
|
-
|
|
3366
|
-
|
|
3367
|
-
|
|
3368
|
-
|
|
3369
|
-
|
|
3370
|
-
|
|
3371
|
-
|
|
3372
|
-
|
|
3373
|
-
|
|
3374
|
-
if (copilotkit.properties) {
|
|
3375
|
-
const { a2uiAction, ...rest } = copilotkit.properties;
|
|
3376
|
-
copilotkit.setProperties(rest);
|
|
3377
|
-
}
|
|
3378
|
-
}
|
|
3379
|
-
}, [agent, copilotkit]),
|
|
3237
|
+
onAction: useCallback((message) => runA2UIAction({
|
|
3238
|
+
message,
|
|
3239
|
+
agent,
|
|
3240
|
+
copilotkit,
|
|
3241
|
+
onAction
|
|
3242
|
+
}), [
|
|
3243
|
+
agent,
|
|
3244
|
+
copilotkit,
|
|
3245
|
+
onAction
|
|
3246
|
+
]),
|
|
3380
3247
|
theme,
|
|
3381
3248
|
catalog,
|
|
3382
3249
|
children: [/* @__PURE__ */ jsx(SurfaceMessageProcessor, {
|
|
@@ -3447,6 +3314,18 @@ function getOperationSurfaceId(operation) {
|
|
|
3447
3314
|
return operation?.createSurface?.surfaceId ?? operation?.updateComponents?.surfaceId ?? operation?.updateDataModel?.surfaceId ?? operation?.deleteSurface?.surfaceId ?? null;
|
|
3448
3315
|
}
|
|
3449
3316
|
|
|
3317
|
+
//#endregion
|
|
3318
|
+
//#region src/v2/types/defineToolCallRenderer.ts
|
|
3319
|
+
function defineToolCallRenderer(def) {
|
|
3320
|
+
const argsSchema = def.name === "*" && !def.args ? z.any() : def.args;
|
|
3321
|
+
return {
|
|
3322
|
+
name: def.name,
|
|
3323
|
+
args: argsSchema,
|
|
3324
|
+
render: def.render,
|
|
3325
|
+
...def.agentId ? { agentId: def.agentId } : {}
|
|
3326
|
+
};
|
|
3327
|
+
}
|
|
3328
|
+
|
|
3450
3329
|
//#endregion
|
|
3451
3330
|
//#region src/v2/a2ui/A2UIToolCallRenderer.tsx
|
|
3452
3331
|
/**
|
|
@@ -4000,10 +3879,10 @@ function useRenderActivityMessage() {
|
|
|
4000
3879
|
|
|
4001
3880
|
//#endregion
|
|
4002
3881
|
//#region src/v2/hooks/use-frontend-tool.tsx
|
|
4003
|
-
const EMPTY_DEPS = [];
|
|
3882
|
+
const EMPTY_DEPS$1 = [];
|
|
4004
3883
|
function useFrontendTool(tool, deps) {
|
|
4005
3884
|
const { copilotkit } = useCopilotKit();
|
|
4006
|
-
const extraDeps = deps ?? EMPTY_DEPS;
|
|
3885
|
+
const extraDeps = deps ?? EMPTY_DEPS$1;
|
|
4007
3886
|
useEffect(() => {
|
|
4008
3887
|
const name = tool.name;
|
|
4009
3888
|
if (copilotkit.getTool({
|
|
@@ -4030,74 +3909,344 @@ function useFrontendTool(tool, deps) {
|
|
|
4030
3909
|
JSON.stringify(extraDeps)
|
|
4031
3910
|
]);
|
|
4032
3911
|
}
|
|
4033
|
-
|
|
4034
|
-
//#endregion
|
|
4035
|
-
//#region src/v2/hooks/use-component.tsx
|
|
3912
|
+
|
|
3913
|
+
//#endregion
|
|
3914
|
+
//#region src/v2/hooks/use-component.tsx
|
|
3915
|
+
/**
|
|
3916
|
+
* Registers a React component as a frontend tool renderer in chat.
|
|
3917
|
+
*
|
|
3918
|
+
* This hook is a convenience wrapper around `useFrontendTool` that:
|
|
3919
|
+
* - builds a model-facing tool description,
|
|
3920
|
+
* - forwards optional schema parameters (any Standard Schema V1 compatible library),
|
|
3921
|
+
* - renders your component with tool call parameters.
|
|
3922
|
+
*
|
|
3923
|
+
* Use this when you want to display a typed visual component for a tool call
|
|
3924
|
+
* without manually wiring a full frontend tool object.
|
|
3925
|
+
*
|
|
3926
|
+
* When `parameters` is provided, render props are inferred from the schema.
|
|
3927
|
+
* When omitted, the render component may accept any props.
|
|
3928
|
+
*
|
|
3929
|
+
* @typeParam TSchema - Schema describing tool parameters, or `undefined` when no schema is given.
|
|
3930
|
+
* @param config - Tool registration config.
|
|
3931
|
+
* @param deps - Optional dependencies to refresh registration (same semantics as `useEffect`).
|
|
3932
|
+
*
|
|
3933
|
+
* @example
|
|
3934
|
+
* ```tsx
|
|
3935
|
+
* // Without parameters — render accepts any props
|
|
3936
|
+
* useComponent({
|
|
3937
|
+
* name: "showGreeting",
|
|
3938
|
+
* render: ({ message }: { message: string }) => <div>{message}</div>,
|
|
3939
|
+
* });
|
|
3940
|
+
* ```
|
|
3941
|
+
*
|
|
3942
|
+
* @example
|
|
3943
|
+
* ```tsx
|
|
3944
|
+
* // With parameters — render props inferred from schema
|
|
3945
|
+
* useComponent({
|
|
3946
|
+
* name: "showWeatherCard",
|
|
3947
|
+
* parameters: z.object({ city: z.string() }),
|
|
3948
|
+
* render: ({ city }) => <div>{city}</div>,
|
|
3949
|
+
* });
|
|
3950
|
+
* ```
|
|
3951
|
+
*
|
|
3952
|
+
* @example
|
|
3953
|
+
* ```tsx
|
|
3954
|
+
* useComponent(
|
|
3955
|
+
* {
|
|
3956
|
+
* name: "renderProfile",
|
|
3957
|
+
* parameters: z.object({ userId: z.string() }),
|
|
3958
|
+
* render: ProfileCard,
|
|
3959
|
+
* agentId: "support-agent",
|
|
3960
|
+
* },
|
|
3961
|
+
* [selectedAgentId],
|
|
3962
|
+
* );
|
|
3963
|
+
* ```
|
|
3964
|
+
*/
|
|
3965
|
+
function useComponent(config, deps) {
|
|
3966
|
+
const prefix = `Use this tool to display the "${config.name}" component in the chat. This tool renders a visual UI component for the user.`;
|
|
3967
|
+
const fullDescription = config.description ? `${prefix}\n\n${config.description}` : prefix;
|
|
3968
|
+
useFrontendTool({
|
|
3969
|
+
name: config.name,
|
|
3970
|
+
description: fullDescription,
|
|
3971
|
+
parameters: config.parameters,
|
|
3972
|
+
render: ({ args }) => {
|
|
3973
|
+
const Component = config.render;
|
|
3974
|
+
return /* @__PURE__ */ jsx(Component, { ...args });
|
|
3975
|
+
},
|
|
3976
|
+
agentId: config.agentId,
|
|
3977
|
+
followUp: config.followUp
|
|
3978
|
+
}, deps);
|
|
3979
|
+
}
|
|
3980
|
+
|
|
3981
|
+
//#endregion
|
|
3982
|
+
//#region src/v2/hooks/use-render-tool.tsx
|
|
3983
|
+
const EMPTY_DEPS = [];
|
|
3984
|
+
/**
|
|
3985
|
+
* Registers a renderer entry in CopilotKit's `renderToolCalls` registry.
|
|
3986
|
+
*
|
|
3987
|
+
* Key behavior:
|
|
3988
|
+
* - deduplicates by `agentId:name` (latest registration wins),
|
|
3989
|
+
* - keeps renderer entries on cleanup so historical chat tool calls can still render,
|
|
3990
|
+
* - refreshes registration when `deps` change.
|
|
3991
|
+
*
|
|
3992
|
+
* @typeParam S - Schema type describing tool call parameters.
|
|
3993
|
+
* @param config - Renderer config for wildcard or named tools.
|
|
3994
|
+
* @param deps - Optional dependencies to refresh registration.
|
|
3995
|
+
*
|
|
3996
|
+
* @example
|
|
3997
|
+
* ```tsx
|
|
3998
|
+
* useRenderTool(
|
|
3999
|
+
* {
|
|
4000
|
+
* name: "searchDocs",
|
|
4001
|
+
* parameters: z.object({ query: z.string() }),
|
|
4002
|
+
* render: ({ status, parameters, result }) => {
|
|
4003
|
+
* if (status === "executing") return <div>Searching {parameters.query}</div>;
|
|
4004
|
+
* if (status === "complete") return <div>{result}</div>;
|
|
4005
|
+
* return <div>Preparing...</div>;
|
|
4006
|
+
* },
|
|
4007
|
+
* },
|
|
4008
|
+
* [],
|
|
4009
|
+
* );
|
|
4010
|
+
* ```
|
|
4011
|
+
*
|
|
4012
|
+
* @example
|
|
4013
|
+
* ```tsx
|
|
4014
|
+
* useRenderTool(
|
|
4015
|
+
* {
|
|
4016
|
+
* name: "summarize",
|
|
4017
|
+
* parameters: z.object({ text: z.string() }),
|
|
4018
|
+
* agentId: "research-agent",
|
|
4019
|
+
* render: ({ name, status }) => <div>{name}: {status}</div>,
|
|
4020
|
+
* },
|
|
4021
|
+
* [selectedAgentId],
|
|
4022
|
+
* );
|
|
4023
|
+
* ```
|
|
4024
|
+
*/
|
|
4025
|
+
function useRenderTool(config, deps) {
|
|
4026
|
+
const { copilotkit } = useCopilotKit();
|
|
4027
|
+
const extraDeps = deps ?? EMPTY_DEPS;
|
|
4028
|
+
useEffect(() => {
|
|
4029
|
+
const renderer = config.name === "*" && !config.parameters ? defineToolCallRenderer({
|
|
4030
|
+
name: "*",
|
|
4031
|
+
render: (props) => config.render({
|
|
4032
|
+
...props,
|
|
4033
|
+
parameters: props.args
|
|
4034
|
+
}),
|
|
4035
|
+
...config.agentId ? { agentId: config.agentId } : {}
|
|
4036
|
+
}) : defineToolCallRenderer({
|
|
4037
|
+
name: config.name,
|
|
4038
|
+
args: config.parameters,
|
|
4039
|
+
render: (props) => {
|
|
4040
|
+
if (props.status === ToolCallStatus.InProgress) return config.render({
|
|
4041
|
+
...props,
|
|
4042
|
+
parameters: props.args
|
|
4043
|
+
});
|
|
4044
|
+
if (props.status === ToolCallStatus.Executing) return config.render({
|
|
4045
|
+
...props,
|
|
4046
|
+
parameters: props.args
|
|
4047
|
+
});
|
|
4048
|
+
return config.render({
|
|
4049
|
+
...props,
|
|
4050
|
+
parameters: props.args
|
|
4051
|
+
});
|
|
4052
|
+
},
|
|
4053
|
+
...config.agentId ? { agentId: config.agentId } : {}
|
|
4054
|
+
});
|
|
4055
|
+
copilotkit.addHookRenderToolCall(renderer);
|
|
4056
|
+
}, [
|
|
4057
|
+
config.name,
|
|
4058
|
+
copilotkit,
|
|
4059
|
+
JSON.stringify(extraDeps)
|
|
4060
|
+
]);
|
|
4061
|
+
}
|
|
4062
|
+
|
|
4063
|
+
//#endregion
|
|
4064
|
+
//#region src/v2/hooks/use-default-render-tool.tsx
|
|
4065
|
+
/**
|
|
4066
|
+
* Module-level dedup set so an unknown status value only emits a console
|
|
4067
|
+
* warning the FIRST time we encounter it. Otherwise a stuck/unmapped status
|
|
4068
|
+
* would log on every re-render (potentially many per second).
|
|
4069
|
+
*/
|
|
4070
|
+
const warnedUnknownStatuses = /* @__PURE__ */ new Set();
|
|
4071
|
+
/**
|
|
4072
|
+
* Map a {@link ToolCallStatus} enum value to the documented string-union
|
|
4073
|
+
* status the {@link DefaultRenderProps} contract exposes. Unknown / future
|
|
4074
|
+
* enum members log a warning (once per distinct value) and fall back to
|
|
4075
|
+
* `"inProgress"`.
|
|
4076
|
+
*/
|
|
4077
|
+
function mapToolCallStatus(status) {
|
|
4078
|
+
switch (status) {
|
|
4079
|
+
case ToolCallStatus.Complete: return "complete";
|
|
4080
|
+
case ToolCallStatus.Executing: return "executing";
|
|
4081
|
+
case ToolCallStatus.InProgress: return "inProgress";
|
|
4082
|
+
default: {
|
|
4083
|
+
const key = String(status);
|
|
4084
|
+
if (!warnedUnknownStatuses.has(key)) {
|
|
4085
|
+
warnedUnknownStatuses.add(key);
|
|
4086
|
+
console.warn(`[CopilotKit] Unknown ToolCallStatus "${key}" in default tool-call renderer; falling back to "inProgress".`);
|
|
4087
|
+
}
|
|
4088
|
+
return "inProgress";
|
|
4089
|
+
}
|
|
4090
|
+
}
|
|
4091
|
+
}
|
|
4092
|
+
/**
|
|
4093
|
+
* Convert the framework-internal renderer props (`args`, enum status) into
|
|
4094
|
+
* the documented {@link DefaultRenderProps} shape (`parameters`, string-union
|
|
4095
|
+
* status) so a user `config.render` always sees the documented contract.
|
|
4096
|
+
*/
|
|
4097
|
+
function adaptRendererProps(props) {
|
|
4098
|
+
return {
|
|
4099
|
+
name: props.name,
|
|
4100
|
+
toolCallId: props.toolCallId,
|
|
4101
|
+
parameters: props.args,
|
|
4102
|
+
status: mapToolCallStatus(props.status),
|
|
4103
|
+
result: props.result
|
|
4104
|
+
};
|
|
4105
|
+
}
|
|
4036
4106
|
/**
|
|
4037
|
-
* Registers a
|
|
4038
|
-
*
|
|
4039
|
-
* This hook is a convenience wrapper around `useFrontendTool` that:
|
|
4040
|
-
* - builds a model-facing tool description,
|
|
4041
|
-
* - forwards optional schema parameters (any Standard Schema V1 compatible library),
|
|
4042
|
-
* - renders your component with tool call parameters.
|
|
4107
|
+
* Registers a wildcard (`"*"`) tool-call renderer via `useRenderTool`.
|
|
4043
4108
|
*
|
|
4044
|
-
*
|
|
4045
|
-
*
|
|
4109
|
+
* - Call with no config to use CopilotKit's built-in default tool-call card.
|
|
4110
|
+
* - Pass `config.render` to replace the default UI with your own fallback renderer.
|
|
4046
4111
|
*
|
|
4047
|
-
*
|
|
4048
|
-
*
|
|
4112
|
+
* This is useful when you want a generic renderer for tools that do not have a
|
|
4113
|
+
* dedicated `useRenderTool({ name: "..." })` registration.
|
|
4049
4114
|
*
|
|
4050
|
-
* @
|
|
4051
|
-
* @param
|
|
4052
|
-
* @param deps - Optional dependencies to refresh registration (same semantics as `useEffect`).
|
|
4115
|
+
* @param config - Optional custom wildcard render function.
|
|
4116
|
+
* @param deps - Optional dependencies to refresh registration.
|
|
4053
4117
|
*
|
|
4054
4118
|
* @example
|
|
4055
4119
|
* ```tsx
|
|
4056
|
-
*
|
|
4057
|
-
* useComponent({
|
|
4058
|
-
* name: "showGreeting",
|
|
4059
|
-
* render: ({ message }: { message: string }) => <div>{message}</div>,
|
|
4060
|
-
* });
|
|
4120
|
+
* useDefaultRenderTool();
|
|
4061
4121
|
* ```
|
|
4062
4122
|
*
|
|
4063
4123
|
* @example
|
|
4064
4124
|
* ```tsx
|
|
4065
|
-
*
|
|
4066
|
-
*
|
|
4067
|
-
* name: "showWeatherCard",
|
|
4068
|
-
* parameters: z.object({ city: z.string() }),
|
|
4069
|
-
* render: ({ city }) => <div>{city}</div>,
|
|
4125
|
+
* useDefaultRenderTool({
|
|
4126
|
+
* render: ({ name, status }) => <div>{name}: {status}</div>,
|
|
4070
4127
|
* });
|
|
4071
4128
|
* ```
|
|
4072
4129
|
*
|
|
4073
4130
|
* @example
|
|
4074
4131
|
* ```tsx
|
|
4075
|
-
*
|
|
4132
|
+
* useDefaultRenderTool(
|
|
4076
4133
|
* {
|
|
4077
|
-
*
|
|
4078
|
-
*
|
|
4079
|
-
*
|
|
4080
|
-
* agentId: "support-agent",
|
|
4134
|
+
* render: ({ name, result }) => (
|
|
4135
|
+
* <ToolEventRow title={name} payload={result} compact={compactMode} />
|
|
4136
|
+
* ),
|
|
4081
4137
|
* },
|
|
4082
|
-
* [
|
|
4138
|
+
* [compactMode],
|
|
4083
4139
|
* );
|
|
4084
4140
|
* ```
|
|
4085
4141
|
*/
|
|
4086
|
-
function
|
|
4087
|
-
const
|
|
4088
|
-
|
|
4089
|
-
|
|
4090
|
-
|
|
4091
|
-
description: fullDescription,
|
|
4092
|
-
parameters: config.parameters,
|
|
4093
|
-
render: ({ args }) => {
|
|
4094
|
-
const Component = config.render;
|
|
4095
|
-
return /* @__PURE__ */ jsx(Component, { ...args });
|
|
4096
|
-
},
|
|
4097
|
-
agentId: config.agentId,
|
|
4098
|
-
followUp: config.followUp
|
|
4142
|
+
function useDefaultRenderTool(config, deps) {
|
|
4143
|
+
const userRender = config?.render;
|
|
4144
|
+
useRenderTool({
|
|
4145
|
+
name: "*",
|
|
4146
|
+
render: userRender ? (raw) => userRender(adaptRendererProps(raw)) : (raw) => /* @__PURE__ */ jsx(DefaultToolCallRenderer, { ...adaptRendererProps(raw) })
|
|
4099
4147
|
}, deps);
|
|
4100
4148
|
}
|
|
4149
|
+
/**
|
|
4150
|
+
* Guarded JSON.stringify used inside the expanded `<pre>` blocks. A circular
|
|
4151
|
+
* reference would otherwise crash the entire React tree on render.
|
|
4152
|
+
*/
|
|
4153
|
+
function safeStringifyForPre(value) {
|
|
4154
|
+
try {
|
|
4155
|
+
return JSON.stringify(value, null, 2);
|
|
4156
|
+
} catch (err) {
|
|
4157
|
+
console.warn("[CopilotKit] Failed to JSON.stringify tool-call payload for default renderer; falling back to String():", err);
|
|
4158
|
+
try {
|
|
4159
|
+
return String(value);
|
|
4160
|
+
} catch (innerErr) {
|
|
4161
|
+
console.warn("[CopilotKit] safeStringifyForPre: value could not be stringified:", innerErr);
|
|
4162
|
+
return "[unserializable]";
|
|
4163
|
+
}
|
|
4164
|
+
}
|
|
4165
|
+
}
|
|
4166
|
+
function DefaultToolCallRenderer({ name, toolCallId, parameters, status, result }) {
|
|
4167
|
+
const [isExpanded, setIsExpanded] = useState(false);
|
|
4168
|
+
const isActive = status === "inProgress" || status === "executing";
|
|
4169
|
+
const isComplete = status === "complete";
|
|
4170
|
+
const statusLabel = isActive ? "Running" : isComplete ? "Done" : status;
|
|
4171
|
+
const dotClassName = isActive ? "cpk:bg-amber-500" : isComplete ? "cpk:bg-emerald-500" : "cpk:bg-zinc-400";
|
|
4172
|
+
const badgeClassName = isActive ? "cpk:bg-amber-100 cpk:text-amber-800 cpk:dark:bg-amber-500/15 cpk:dark:text-amber-400" : isComplete ? "cpk:bg-emerald-100 cpk:text-emerald-800 cpk:dark:bg-emerald-500/15 cpk:dark:text-emerald-400" : "cpk:bg-zinc-100 cpk:text-zinc-800 cpk:dark:bg-zinc-700/40 cpk:dark:text-zinc-300";
|
|
4173
|
+
return /* @__PURE__ */ jsx("div", {
|
|
4174
|
+
"data-testid": "copilot-tool-render",
|
|
4175
|
+
"data-tool-name": name,
|
|
4176
|
+
"data-tool-call-id": toolCallId,
|
|
4177
|
+
"data-status": status,
|
|
4178
|
+
"data-args": safeStringifyForAttr(parameters),
|
|
4179
|
+
"data-result": safeStringifyForAttr(result),
|
|
4180
|
+
className: "cpk:mt-2 cpk:pb-2",
|
|
4181
|
+
children: /* @__PURE__ */ jsxs("div", {
|
|
4182
|
+
className: "cpk:rounded-xl cpk:border cpk:border-zinc-200/60 cpk:bg-white/70 cpk:p-4 cpk:shadow-sm cpk:backdrop-blur cpk:dark:border-zinc-800/60 cpk:dark:bg-zinc-900/50",
|
|
4183
|
+
children: [/* @__PURE__ */ jsxs("button", {
|
|
4184
|
+
type: "button",
|
|
4185
|
+
"aria-expanded": isExpanded,
|
|
4186
|
+
onClick: () => setIsExpanded(!isExpanded),
|
|
4187
|
+
className: "cpk:flex cpk:w-full cpk:cursor-pointer cpk:select-none cpk:items-center cpk:justify-between cpk:gap-2.5 cpk:border-none cpk:bg-transparent cpk:p-0 cpk:m-0 cpk:text-left cpk:text-inherit",
|
|
4188
|
+
style: { font: "inherit" },
|
|
4189
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
4190
|
+
className: "cpk:flex cpk:min-w-0 cpk:items-center cpk:gap-2",
|
|
4191
|
+
children: [
|
|
4192
|
+
/* @__PURE__ */ jsx("svg", {
|
|
4193
|
+
className: `cpk:h-3.5 cpk:w-3.5 cpk:flex-shrink-0 cpk:text-zinc-500 cpk:transition-transform cpk:dark:text-zinc-400 ${isExpanded ? "cpk:rotate-90" : ""}`,
|
|
4194
|
+
fill: "none",
|
|
4195
|
+
viewBox: "0 0 24 24",
|
|
4196
|
+
strokeWidth: 2,
|
|
4197
|
+
stroke: "currentColor",
|
|
4198
|
+
children: /* @__PURE__ */ jsx("path", {
|
|
4199
|
+
strokeLinecap: "round",
|
|
4200
|
+
strokeLinejoin: "round",
|
|
4201
|
+
d: "M8.25 4.5l7.5 7.5-7.5 7.5"
|
|
4202
|
+
})
|
|
4203
|
+
}),
|
|
4204
|
+
/* @__PURE__ */ jsx("span", { className: `cpk:inline-block cpk:h-2 cpk:w-2 cpk:flex-shrink-0 cpk:rounded-full ${dotClassName}` }),
|
|
4205
|
+
/* @__PURE__ */ jsx("span", {
|
|
4206
|
+
"data-testid": "copilot-tool-render-name",
|
|
4207
|
+
className: "cpk:truncate cpk:text-[13px] cpk:font-semibold cpk:text-zinc-900 cpk:dark:text-zinc-100",
|
|
4208
|
+
children: name
|
|
4209
|
+
})
|
|
4210
|
+
]
|
|
4211
|
+
}), /* @__PURE__ */ jsx("span", {
|
|
4212
|
+
"data-testid": "copilot-tool-render-status",
|
|
4213
|
+
className: `cpk:inline-flex cpk:flex-shrink-0 cpk:items-center cpk:rounded-full cpk:px-2 cpk:py-0.5 cpk:text-[11px] cpk:font-medium ${badgeClassName}`,
|
|
4214
|
+
children: statusLabel
|
|
4215
|
+
})]
|
|
4216
|
+
}), isExpanded && /* @__PURE__ */ jsxs("div", {
|
|
4217
|
+
className: "cpk:mt-3 cpk:grid cpk:gap-3",
|
|
4218
|
+
children: [/* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("div", {
|
|
4219
|
+
className: "cpk:text-[10px] cpk:uppercase cpk:text-zinc-500 cpk:dark:text-zinc-400",
|
|
4220
|
+
children: "Arguments"
|
|
4221
|
+
}), /* @__PURE__ */ jsx("pre", {
|
|
4222
|
+
className: "cpk:mt-1.5 cpk:max-h-[200px] cpk:overflow-auto cpk:rounded-md cpk:bg-zinc-100 cpk:p-2.5 cpk:text-[11px] cpk:leading-relaxed cpk:text-zinc-800 cpk:whitespace-pre-wrap cpk:break-words cpk:dark:bg-zinc-800/60 cpk:dark:text-zinc-200",
|
|
4223
|
+
children: safeStringifyForPre(parameters ?? {})
|
|
4224
|
+
})] }), result !== void 0 && /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("div", {
|
|
4225
|
+
className: "cpk:text-[10px] cpk:uppercase cpk:text-zinc-500 cpk:dark:text-zinc-400",
|
|
4226
|
+
children: "Result"
|
|
4227
|
+
}), /* @__PURE__ */ jsx("pre", {
|
|
4228
|
+
className: "cpk:mt-1.5 cpk:max-h-[200px] cpk:overflow-auto cpk:rounded-md cpk:bg-zinc-100 cpk:p-2.5 cpk:text-[11px] cpk:leading-relaxed cpk:text-zinc-800 cpk:whitespace-pre-wrap cpk:break-words cpk:dark:bg-zinc-800/60 cpk:dark:text-zinc-200",
|
|
4229
|
+
children: typeof result === "string" ? result : safeStringifyForPre(result)
|
|
4230
|
+
})] })]
|
|
4231
|
+
})]
|
|
4232
|
+
})
|
|
4233
|
+
});
|
|
4234
|
+
}
|
|
4235
|
+
function safeStringifyForAttr(value) {
|
|
4236
|
+
if (value === void 0 || value === null) return "";
|
|
4237
|
+
if (typeof value === "string") return value;
|
|
4238
|
+
try {
|
|
4239
|
+
return JSON.stringify(value);
|
|
4240
|
+
} catch (err) {
|
|
4241
|
+
console.warn("[CopilotKit] Failed to JSON.stringify tool-call payload for data-* attribute; falling back to String():", err);
|
|
4242
|
+
try {
|
|
4243
|
+
return String(value);
|
|
4244
|
+
} catch (innerErr) {
|
|
4245
|
+
console.warn("[CopilotKit] safeStringifyForAttr: value could not be stringified:", innerErr);
|
|
4246
|
+
return "";
|
|
4247
|
+
}
|
|
4248
|
+
}
|
|
4249
|
+
}
|
|
4101
4250
|
|
|
4102
4251
|
//#endregion
|
|
4103
4252
|
//#region src/v2/hooks/use-human-in-the-loop.tsx
|
|
@@ -4835,7 +4984,7 @@ function useThreadStoreSelector(store, selector) {
|
|
|
4835
4984
|
return useSyncExternalStore(useCallback((onStoreChange) => {
|
|
4836
4985
|
const subscription = store.select(selector).subscribe(onStoreChange);
|
|
4837
4986
|
return () => subscription.unsubscribe();
|
|
4838
|
-
}, [store, selector]), () => selector(store.getState()));
|
|
4987
|
+
}, [store, selector]), () => selector(store.getState()), () => selector(store.getServerState()));
|
|
4839
4988
|
}
|
|
4840
4989
|
/**
|
|
4841
4990
|
* React hook for listing and managing Intelligence platform threads.
|
|
@@ -4878,7 +5027,7 @@ function useThreadStoreSelector(store, selector) {
|
|
|
4878
5027
|
* }
|
|
4879
5028
|
* ```
|
|
4880
5029
|
*/
|
|
4881
|
-
function useThreads$1({ agentId, includeArchived, limit }) {
|
|
5030
|
+
function useThreads$1({ agentId, includeArchived, limit, enabled = true }) {
|
|
4882
5031
|
const { copilotkit } = useCopilotKit();
|
|
4883
5032
|
const [store] = useState(() => ɵcreateThreadStore({ fetch: globalThis.fetch }));
|
|
4884
5033
|
const coreThreads = useThreadStoreSelector(store, ɵselectThreads);
|
|
@@ -4895,6 +5044,7 @@ function useThreads$1({ agentId, includeArchived, limit }) {
|
|
|
4895
5044
|
const storeError = useThreadStoreSelector(store, ɵselectThreadsError);
|
|
4896
5045
|
const hasMoreThreads = useThreadStoreSelector(store, ɵselectHasNextPage);
|
|
4897
5046
|
const isFetchingMoreThreads = useThreadStoreSelector(store, ɵselectIsFetchingNextPage);
|
|
5047
|
+
const isMutating = useThreadStoreSelector(store, ɵselectIsMutating);
|
|
4898
5048
|
const headersKey = useMemo(() => {
|
|
4899
5049
|
return JSON.stringify(Object.entries(copilotkit.headers ?? {}).sort(([left], [right]) => left.localeCompare(right)));
|
|
4900
5050
|
}, [copilotkit.headers]);
|
|
@@ -4915,9 +5065,16 @@ function useThreads$1({ agentId, includeArchived, limit }) {
|
|
|
4915
5065
|
return /* @__PURE__ */ new Error("Thread mutations are not available on this CopilotKit runtime");
|
|
4916
5066
|
}, [threadMutationsSupported]);
|
|
4917
5067
|
const [hasDispatchedContext, setHasDispatchedContext] = useState(false);
|
|
4918
|
-
const preConnectLoading = !!copilotkit.runtimeUrl && !threadEndpointsUnavailable && !hasDispatchedContext;
|
|
4919
|
-
const
|
|
4920
|
-
|
|
5068
|
+
const preConnectLoading = enabled && !!copilotkit.runtimeUrl && !threadEndpointsUnavailable && !hasDispatchedContext;
|
|
5069
|
+
const [configErrorDismissed, setConfigErrorDismissed] = useState(false);
|
|
5070
|
+
useEffect(() => {
|
|
5071
|
+
setConfigErrorDismissed(false);
|
|
5072
|
+
}, [runtimeError, threadEndpointsError]);
|
|
5073
|
+
const activeRuntimeError = configErrorDismissed ? null : runtimeError;
|
|
5074
|
+
const activeThreadEndpointsError = configErrorDismissed ? null : threadEndpointsError;
|
|
5075
|
+
const isLoading = activeRuntimeError || activeThreadEndpointsError ? false : preConnectLoading || storeIsLoading;
|
|
5076
|
+
const error = activeRuntimeError ?? activeThreadEndpointsError ?? storeError;
|
|
5077
|
+
const listError = storeError;
|
|
4921
5078
|
useEffect(() => {
|
|
4922
5079
|
store.start();
|
|
4923
5080
|
return () => {
|
|
@@ -4925,6 +5082,7 @@ function useThreads$1({ agentId, includeArchived, limit }) {
|
|
|
4925
5082
|
};
|
|
4926
5083
|
}, [store]);
|
|
4927
5084
|
useEffect(() => {
|
|
5085
|
+
if (!enabled) return;
|
|
4928
5086
|
copilotkit.registerThreadStore(agentId, store);
|
|
4929
5087
|
return () => {
|
|
4930
5088
|
copilotkit.unregisterThreadStore(agentId);
|
|
@@ -4932,9 +5090,15 @@ function useThreads$1({ agentId, includeArchived, limit }) {
|
|
|
4932
5090
|
}, [
|
|
4933
5091
|
copilotkit,
|
|
4934
5092
|
agentId,
|
|
4935
|
-
store
|
|
5093
|
+
store,
|
|
5094
|
+
enabled
|
|
4936
5095
|
]);
|
|
4937
5096
|
useEffect(() => {
|
|
5097
|
+
if (!enabled) {
|
|
5098
|
+
store.setContext(null);
|
|
5099
|
+
setHasDispatchedContext(false);
|
|
5100
|
+
return;
|
|
5101
|
+
}
|
|
4938
5102
|
if (!copilotkit.runtimeUrl) {
|
|
4939
5103
|
store.setContext(null);
|
|
4940
5104
|
setHasDispatchedContext(false);
|
|
@@ -4958,6 +5122,7 @@ function useThreads$1({ agentId, includeArchived, limit }) {
|
|
|
4958
5122
|
setHasDispatchedContext(true);
|
|
4959
5123
|
}, [
|
|
4960
5124
|
store,
|
|
5125
|
+
enabled,
|
|
4961
5126
|
copilotkit.runtimeUrl,
|
|
4962
5127
|
runtimeStatus,
|
|
4963
5128
|
headersKey,
|
|
@@ -4981,9 +5146,16 @@ function useThreads$1({ agentId, includeArchived, limit }) {
|
|
|
4981
5146
|
threads,
|
|
4982
5147
|
isLoading,
|
|
4983
5148
|
error,
|
|
5149
|
+
listError,
|
|
4984
5150
|
hasMoreThreads,
|
|
4985
5151
|
isFetchingMoreThreads,
|
|
5152
|
+
isMutating,
|
|
4986
5153
|
fetchMoreThreads: useCallback(() => store.fetchNextPage(), [store]),
|
|
5154
|
+
refetchThreads: useCallback(() => store.refetchThreads(), [store]),
|
|
5155
|
+
startNewThread: useCallback(() => {
|
|
5156
|
+
setConfigErrorDismissed(true);
|
|
5157
|
+
store.startNewThread();
|
|
5158
|
+
}, [store]),
|
|
4987
5159
|
renameThread,
|
|
4988
5160
|
archiveThread,
|
|
4989
5161
|
unarchiveThread,
|
|
@@ -6170,7 +6342,7 @@ const DefaultContainer = React.forwardRef(function DefaultContainer({ className,
|
|
|
6170
6342
|
ref,
|
|
6171
6343
|
"data-copilotkit": true,
|
|
6172
6344
|
"data-testid": "copilot-suggestions",
|
|
6173
|
-
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:
|
|
6345
|
+
className: cn("cpk:flex cpk:flex-wrap cpk:items-center cpk:gap-1.5 cpk:sm:gap-2 cpk:pl-0 cpk:pr-4 cpk:@3xl:px-0 cpk:pointer-events-none", className),
|
|
6174
6346
|
...props
|
|
6175
6347
|
});
|
|
6176
6348
|
});
|
|
@@ -7275,7 +7447,7 @@ function CopilotChatView({ messageView, input, scrollView, suggestionView, welco
|
|
|
7275
7447
|
children: /* @__PURE__ */ jsxs("div", {
|
|
7276
7448
|
className: "cpk:max-w-3xl cpk:mx-auto",
|
|
7277
7449
|
children: [BoundMessageView, hasSuggestions ? /* @__PURE__ */ jsx("div", {
|
|
7278
|
-
className: "cpk:pl-0 cpk:pr-4 cpk:
|
|
7450
|
+
className: "cpk:pl-0 cpk:pr-4 cpk:@3xl:px-0 cpk:mt-4",
|
|
7279
7451
|
children: BoundSuggestionView
|
|
7280
7452
|
}) : null]
|
|
7281
7453
|
})
|
|
@@ -7318,7 +7490,7 @@ function CopilotChatView({ messageView, input, scrollView, suggestionView, welco
|
|
|
7318
7490
|
onDragOver,
|
|
7319
7491
|
onDragLeave,
|
|
7320
7492
|
onDrop,
|
|
7321
|
-
className: cn("copilotKitChat cpk:relative cpk:h-full cpk:flex cpk:flex-col", className),
|
|
7493
|
+
className: cn("copilotKitChat cpk:@container cpk:relative cpk:h-full cpk:flex cpk:flex-col", className),
|
|
7322
7494
|
...props,
|
|
7323
7495
|
children: [dragOver && /* @__PURE__ */ jsx(DropOverlay, {}), BoundWelcomeScreen]
|
|
7324
7496
|
});
|
|
@@ -7340,7 +7512,7 @@ function CopilotChatView({ messageView, input, scrollView, suggestionView, welco
|
|
|
7340
7512
|
onDragOver,
|
|
7341
7513
|
onDragLeave,
|
|
7342
7514
|
onDrop,
|
|
7343
|
-
className: cn("copilotKitChat cpk:relative cpk:h-full cpk:flex cpk:flex-col", className),
|
|
7515
|
+
className: cn("copilotKitChat cpk:@container cpk:relative cpk:h-full cpk:flex cpk:flex-col", className),
|
|
7344
7516
|
...props,
|
|
7345
7517
|
children: [
|
|
7346
7518
|
dragOver && /* @__PURE__ */ jsx(DropOverlay, {}),
|
|
@@ -7379,7 +7551,7 @@ function CopilotChatView({ messageView, input, scrollView, suggestionView, welco
|
|
|
7379
7551
|
minHeight: 0
|
|
7380
7552
|
},
|
|
7381
7553
|
children: /* @__PURE__ */ jsx("div", {
|
|
7382
|
-
className: "cpk:px-4 cpk:
|
|
7554
|
+
className: "cpk:px-4 cpk:@3xl:px-0 cpk:[div[data-sidebar-chat]_&]:px-8 cpk:[div[data-popup-chat]_&]:px-6",
|
|
7383
7555
|
children
|
|
7384
7556
|
})
|
|
7385
7557
|
}),
|
|
@@ -7412,7 +7584,7 @@ function CopilotChatView({ messageView, input, scrollView, suggestionView, welco
|
|
|
7412
7584
|
...props,
|
|
7413
7585
|
children: [/* @__PURE__ */ jsx("div", {
|
|
7414
7586
|
ref: contentRef,
|
|
7415
|
-
className: "cpk:px-4 cpk:
|
|
7587
|
+
className: "cpk:px-4 cpk:@3xl:px-0 cpk:[div[data-sidebar-chat]_&]:px-8 cpk:[div[data-popup-chat]_&]:px-6",
|
|
7416
7588
|
children
|
|
7417
7589
|
}), /* @__PURE__ */ jsx("div", {
|
|
7418
7590
|
ref: spacerRef,
|
|
@@ -7474,7 +7646,7 @@ function CopilotChatView({ messageView, input, scrollView, suggestionView, welco
|
|
|
7474
7646
|
if (!hasMounted) return /* @__PURE__ */ jsx("div", {
|
|
7475
7647
|
className: "cpk:h-full cpk:max-h-full cpk:flex cpk:flex-col cpk:min-h-0 cpk:overflow-y-auto cpk:overflow-x-hidden",
|
|
7476
7648
|
children: /* @__PURE__ */ jsx("div", {
|
|
7477
|
-
className: "cpk:px-4 cpk:
|
|
7649
|
+
className: "cpk:px-4 cpk:@3xl:px-0 cpk:[div[data-sidebar-chat]_&]:px-8 cpk:[div[data-popup-chat]_&]:px-6",
|
|
7478
7650
|
children
|
|
7479
7651
|
})
|
|
7480
7652
|
});
|
|
@@ -7489,7 +7661,7 @@ function CopilotChatView({ messageView, input, scrollView, suggestionView, welco
|
|
|
7489
7661
|
children: [
|
|
7490
7662
|
/* @__PURE__ */ jsx("div", {
|
|
7491
7663
|
ref: contentRef,
|
|
7492
|
-
className: "cpk:px-4 cpk:
|
|
7664
|
+
className: "cpk:px-4 cpk:@3xl:px-0 cpk:[div[data-sidebar-chat]_&]:px-8 cpk:[div[data-popup-chat]_&]:px-6",
|
|
7493
7665
|
children
|
|
7494
7666
|
}),
|
|
7495
7667
|
BoundFeather,
|
|
@@ -7753,9 +7925,17 @@ function CopilotChat({ agentId, threadId, labels, chatView, isModalDefaultOpen,
|
|
|
7753
7925
|
const { messageView: providedMessageView, suggestionView: providedSuggestionView, onStop: providedStopHandler, ...restProps } = props;
|
|
7754
7926
|
const [lastConnectedThreadId, setLastConnectedThreadId] = useState(null);
|
|
7755
7927
|
const isConnecting = hasExplicitThreadId && lastConnectedThreadId !== resolvedThreadId;
|
|
7928
|
+
const previousThreadIdRef = useRef(null);
|
|
7929
|
+
const hasExplicitThreadIdRef = useRef(hasExplicitThreadId);
|
|
7930
|
+
hasExplicitThreadIdRef.current = hasExplicitThreadId;
|
|
7756
7931
|
useEffect(() => {
|
|
7932
|
+
const threadChanged = previousThreadIdRef.current !== resolvedThreadId;
|
|
7933
|
+
previousThreadIdRef.current = resolvedThreadId;
|
|
7757
7934
|
agent.threadId = resolvedThreadId;
|
|
7758
|
-
if (!hasExplicitThreadId)
|
|
7935
|
+
if (!hasExplicitThreadId) {
|
|
7936
|
+
if (threadChanged && agent.messages.length > 0) agent.setMessages([]);
|
|
7937
|
+
return;
|
|
7938
|
+
}
|
|
7759
7939
|
let detached = false;
|
|
7760
7940
|
const connectAbortController = new AbortController();
|
|
7761
7941
|
if (agent instanceof HttpAgent) agent.abortController = connectAbortController;
|
|
@@ -7769,6 +7949,7 @@ function CopilotChat({ agentId, threadId, labels, chatView, isModalDefaultOpen,
|
|
|
7769
7949
|
if (!detached) (typeof requestAnimationFrame === "function" ? requestAnimationFrame : (cb) => setTimeout(cb, 16))(() => {
|
|
7770
7950
|
if (!detached) setLastConnectedThreadId(resolvedThreadId);
|
|
7771
7951
|
});
|
|
7952
|
+
else if (!hasExplicitThreadIdRef.current) agentToConnect.setMessages([]);
|
|
7772
7953
|
}
|
|
7773
7954
|
};
|
|
7774
7955
|
connect(agent);
|
|
@@ -8119,18 +8300,46 @@ CopilotChatToggleButton.displayName = "CopilotChatToggleButton";
|
|
|
8119
8300
|
|
|
8120
8301
|
//#endregion
|
|
8121
8302
|
//#region src/v2/components/chat/CopilotModalHeader.tsx
|
|
8122
|
-
|
|
8303
|
+
/**
|
|
8304
|
+
* Reactively tracks whether the viewport is in the mobile range (≤767px) — the
|
|
8305
|
+
* same breakpoint the drawer + chat coordination use. SSR-safe: starts `false`
|
|
8306
|
+
* (desktop) so the server render and first client render agree, then syncs on
|
|
8307
|
+
* mount and on resize.
|
|
8308
|
+
*/
|
|
8309
|
+
function useIsMobileViewport() {
|
|
8310
|
+
const [isMobile, setIsMobile] = useState(false);
|
|
8311
|
+
useEffect(() => {
|
|
8312
|
+
if (typeof window === "undefined" || typeof window.matchMedia !== "function") return;
|
|
8313
|
+
const mql = window.matchMedia("(max-width: 767px)");
|
|
8314
|
+
const update = () => setIsMobile(mql.matches);
|
|
8315
|
+
update();
|
|
8316
|
+
mql.addEventListener("change", update);
|
|
8317
|
+
return () => mql.removeEventListener("change", update);
|
|
8318
|
+
}, []);
|
|
8319
|
+
return isMobile;
|
|
8320
|
+
}
|
|
8321
|
+
function CopilotModalHeader({ title, titleContent, closeButton, drawerLauncher, children, className, ...rest }) {
|
|
8123
8322
|
const configuration = useCopilotChatConfiguration();
|
|
8124
8323
|
const fallbackTitle = configuration?.labels.modalHeaderTitle ?? CopilotChatDefaultLabels.modalHeaderTitle;
|
|
8125
8324
|
const resolvedTitle = title ?? fallbackTitle;
|
|
8325
|
+
const isMobile = useIsMobileViewport();
|
|
8326
|
+
const drawerRegistered = (configuration?.drawerRegistered ?? false) && isMobile;
|
|
8126
8327
|
const handleClose = useCallback(() => {
|
|
8127
8328
|
configuration?.setModalOpen?.(false);
|
|
8128
8329
|
}, [configuration]);
|
|
8330
|
+
const handleToggleDrawer = useCallback(() => {
|
|
8331
|
+
configuration?.setDrawerOpen?.(!configuration.drawerOpen);
|
|
8332
|
+
}, [configuration]);
|
|
8129
8333
|
const BoundTitle = renderSlot(titleContent, CopilotModalHeader.Title, { children: resolvedTitle });
|
|
8130
8334
|
const BoundCloseButton = renderSlot(closeButton, CopilotModalHeader.CloseButton, { onClick: handleClose });
|
|
8335
|
+
const BoundDrawerLauncher = drawerRegistered ? renderSlot(drawerLauncher, CopilotModalHeader.DrawerLauncher, {
|
|
8336
|
+
onClick: handleToggleDrawer,
|
|
8337
|
+
"aria-expanded": configuration?.drawerOpen ?? false
|
|
8338
|
+
}) : null;
|
|
8131
8339
|
if (children) return children({
|
|
8132
8340
|
titleContent: BoundTitle,
|
|
8133
8341
|
closeButton: BoundCloseButton,
|
|
8342
|
+
drawerLauncher: BoundDrawerLauncher,
|
|
8134
8343
|
title: resolvedTitle,
|
|
8135
8344
|
...rest
|
|
8136
8345
|
});
|
|
@@ -8143,8 +8352,8 @@ function CopilotModalHeader({ title, titleContent, closeButton, children, classN
|
|
|
8143
8352
|
className: "cpk:flex cpk:w-full cpk:items-center cpk:gap-2",
|
|
8144
8353
|
children: [
|
|
8145
8354
|
/* @__PURE__ */ jsx("div", {
|
|
8146
|
-
className: "cpk:flex-1",
|
|
8147
|
-
"aria-hidden": "true"
|
|
8355
|
+
className: "cpk:flex cpk:flex-1 cpk:justify-start",
|
|
8356
|
+
children: BoundDrawerLauncher ?? /* @__PURE__ */ jsx("span", { "aria-hidden": "true" })
|
|
8148
8357
|
}),
|
|
8149
8358
|
/* @__PURE__ */ jsx("div", {
|
|
8150
8359
|
className: "cpk:flex cpk:flex-1 cpk:justify-center cpk:text-center",
|
|
@@ -8177,9 +8386,21 @@ CopilotModalHeader.displayName = "CopilotModalHeader";
|
|
|
8177
8386
|
"aria-hidden": "true"
|
|
8178
8387
|
})
|
|
8179
8388
|
});
|
|
8389
|
+
_CopilotModalHeader.DrawerLauncher = ({ className, ...props }) => /* @__PURE__ */ jsx("button", {
|
|
8390
|
+
type: "button",
|
|
8391
|
+
"data-testid": "copilot-threads-drawer-launcher",
|
|
8392
|
+
className: cn("cpk:inline-flex cpk:size-8 cpk:items-center cpk:justify-center cpk:rounded-full cpk:text-muted-foreground cpk:transition cpk:cursor-pointer", "cpk:hover:bg-muted cpk:hover:text-foreground cpk:focus-visible:outline-none cpk:focus-visible:ring-2 cpk:focus-visible:ring-ring", className),
|
|
8393
|
+
"aria-label": "Open threads",
|
|
8394
|
+
...props,
|
|
8395
|
+
children: /* @__PURE__ */ jsx(PanelLeftOpen, {
|
|
8396
|
+
className: "cpk:h-4 cpk:w-4",
|
|
8397
|
+
"aria-hidden": "true"
|
|
8398
|
+
})
|
|
8399
|
+
});
|
|
8180
8400
|
})(CopilotModalHeader || (CopilotModalHeader = {}));
|
|
8181
8401
|
CopilotModalHeader.Title.displayName = "CopilotModalHeader.Title";
|
|
8182
8402
|
CopilotModalHeader.CloseButton.displayName = "CopilotModalHeader.CloseButton";
|
|
8403
|
+
CopilotModalHeader.DrawerLauncher.displayName = "CopilotModalHeader.DrawerLauncher";
|
|
8183
8404
|
|
|
8184
8405
|
//#endregion
|
|
8185
8406
|
//#region src/v2/components/chat/CopilotSidebarView.tsx
|
|
@@ -8537,6 +8758,322 @@ function CopilotPopup({ header, toggleButton, defaultOpen, width, height, clickO
|
|
|
8537
8758
|
}
|
|
8538
8759
|
CopilotPopup.displayName = "CopilotPopup";
|
|
8539
8760
|
|
|
8761
|
+
//#endregion
|
|
8762
|
+
//#region src/v2/components/chat/CopilotThreadsDrawer.tsx
|
|
8763
|
+
/**
|
|
8764
|
+
* Maps a {@link Thread} from {@link useThreads} to the element's
|
|
8765
|
+
* {@link DrawerThread} view shape. The shapes are structurally compatible; this
|
|
8766
|
+
* narrows to exactly the fields the element renders so the element never sees
|
|
8767
|
+
* platform-internal fields.
|
|
8768
|
+
*/
|
|
8769
|
+
function toDrawerThread(thread) {
|
|
8770
|
+
return {
|
|
8771
|
+
id: thread.id,
|
|
8772
|
+
name: thread.name,
|
|
8773
|
+
archived: thread.archived,
|
|
8774
|
+
createdAt: thread.createdAt,
|
|
8775
|
+
updatedAt: thread.updatedAt,
|
|
8776
|
+
...thread.lastRunAt !== void 0 ? { lastRunAt: thread.lastRunAt } : {}
|
|
8777
|
+
};
|
|
8778
|
+
}
|
|
8779
|
+
/** The chat input textarea's documented `data-testid`. */
|
|
8780
|
+
const CHAT_INPUT_TESTID = "copilot-chat-textarea";
|
|
8781
|
+
/** The chat view container's documented `data-testid`. */
|
|
8782
|
+
const CHAT_CONTAINER_TESTID = "copilot-chat";
|
|
8783
|
+
/**
|
|
8784
|
+
* Returns the chat input element for focus-return after a thread is selected.
|
|
8785
|
+
*
|
|
8786
|
+
* Best-effort and SCOPED: walks up from the drawer element looking for an
|
|
8787
|
+
* ancestor that contains a chat-view container (`data-testid="copilot-chat"`),
|
|
8788
|
+
* then returns the chat input within that subtree. This avoids focusing the
|
|
8789
|
+
* wrong composer on a page hosting more than one chat (multi-chat dashboards),
|
|
8790
|
+
* where a document-global lookup would grab whichever input appears first in
|
|
8791
|
+
* DOM order rather than the one this drawer drives.
|
|
8792
|
+
*
|
|
8793
|
+
* Falls back to a document-global lookup when no scoping ancestor is found
|
|
8794
|
+
* (e.g. the drawer and chat share no common container, or headless usage),
|
|
8795
|
+
* and returns `null` when there is no chat input at all.
|
|
8796
|
+
*
|
|
8797
|
+
* @param origin - The drawer element to scope the search from.
|
|
8798
|
+
*/
|
|
8799
|
+
function findChatInput(origin) {
|
|
8800
|
+
if (typeof document === "undefined") return null;
|
|
8801
|
+
const container = origin?.closest?.(`[data-testid="${CHAT_CONTAINER_TESTID}"]`);
|
|
8802
|
+
if (container) {
|
|
8803
|
+
const scoped = container.querySelector(`[data-testid="${CHAT_INPUT_TESTID}"]`);
|
|
8804
|
+
if (scoped) return scoped;
|
|
8805
|
+
}
|
|
8806
|
+
return document.querySelector(`[data-testid="${CHAT_INPUT_TESTID}"]`);
|
|
8807
|
+
}
|
|
8808
|
+
/**
|
|
8809
|
+
* React wrapper for the shadow-DOM `<copilotkit-threads-drawer>` threads drawer.
|
|
8810
|
+
*
|
|
8811
|
+
* Responsibilities:
|
|
8812
|
+
* - Registers the custom element on the client (SSR-safe; nothing renders
|
|
8813
|
+
* during prerender to avoid hydration mismatch).
|
|
8814
|
+
* - Feeds the element domain data: `threads`, `loading`, `error`,
|
|
8815
|
+
* `activeThreadId`, `licensed`, fetch-more state.
|
|
8816
|
+
* - Routes the element's nine outbound events to core thread operations
|
|
8817
|
+
* ({@link useThreads}) and chat-configuration changes.
|
|
8818
|
+
* - Registers with the surrounding chat configuration so the header
|
|
8819
|
+
* thread-list launcher appears, and binds the element `open` state to the
|
|
8820
|
+
* configuration's `drawerOpen`.
|
|
8821
|
+
*
|
|
8822
|
+
* License gating is two-pronged: the locked view shows when no license is configured
|
|
8823
|
+
* (the runtime reported no license status) OR the `threads` feature is
|
|
8824
|
+
* explicitly unlicensed. While unlicensed, the thread fetch is skipped entirely
|
|
8825
|
+
* so an unlicensed drawer issues no network requests.
|
|
8826
|
+
*
|
|
8827
|
+
* Thread switching needs no host wiring: when `onThreadSelect`/`onNewThread`
|
|
8828
|
+
* are omitted, the wrapper drives the surrounding chat configuration directly
|
|
8829
|
+
* ({@link CopilotChatConfigurationValue.setActiveThreadId} /
|
|
8830
|
+
* {@link CopilotChatConfigurationValue.startNewThread}), so a bare drawer
|
|
8831
|
+
* connects to the picked thread and shows the welcome screen on "+ New". Pass
|
|
8832
|
+
* the callbacks only to take control yourself.
|
|
8833
|
+
*
|
|
8834
|
+
* @example
|
|
8835
|
+
* ```tsx
|
|
8836
|
+
* // Callback-free: the drawer drives the chat configuration itself.
|
|
8837
|
+
* <CopilotKitProvider runtimeUrl="/api/copilotkit" publicLicenseKey="ck_pub_...">
|
|
8838
|
+
* <CopilotChat />
|
|
8839
|
+
* <CopilotThreadsDrawer />
|
|
8840
|
+
* </CopilotKitProvider>
|
|
8841
|
+
* ```
|
|
8842
|
+
*/
|
|
8843
|
+
function CopilotThreadsDrawer({ agentId, onThreadSelect, onNewThread, onLicensed, licenseUrl, renderRow, label, limit, "data-testid": dataTestId = "copilot-threads-drawer" }) {
|
|
8844
|
+
const configuration = useCopilotChatConfiguration();
|
|
8845
|
+
const { status, checkFeature } = useLicenseContext();
|
|
8846
|
+
const licensePresent = status === "valid" || status === "expiring";
|
|
8847
|
+
const featureLicensed = checkFeature("threads");
|
|
8848
|
+
const licensed = licensePresent && featureLicensed;
|
|
8849
|
+
const licensePending = status === null;
|
|
8850
|
+
const resolvedAgentId = agentId ?? configuration?.agentId ?? "default";
|
|
8851
|
+
const activeThreadId = configuration?.threadId ?? null;
|
|
8852
|
+
const { threads, isLoading, listError, hasMoreThreads, isFetchingMoreThreads, archiveThread, unarchiveThread, deleteThread, fetchMoreThreads, refetchThreads, startNewThread } = useThreads$1({
|
|
8853
|
+
agentId: resolvedAgentId,
|
|
8854
|
+
includeArchived: true,
|
|
8855
|
+
enabled: licensed,
|
|
8856
|
+
...limit !== void 0 ? { limit } : {}
|
|
8857
|
+
});
|
|
8858
|
+
const drawerThreads = useMemo(() => threads.map(toDrawerThread), [threads]);
|
|
8859
|
+
const elementRef = useRef(null);
|
|
8860
|
+
const [mounted, setMounted] = useState(false);
|
|
8861
|
+
useEffect(() => {
|
|
8862
|
+
defineCopilotKitThreadsDrawer();
|
|
8863
|
+
setMounted(true);
|
|
8864
|
+
}, []);
|
|
8865
|
+
const registerDrawer = configuration?.registerDrawer;
|
|
8866
|
+
useEffect(() => {
|
|
8867
|
+
if (!registerDrawer) return;
|
|
8868
|
+
return registerDrawer();
|
|
8869
|
+
}, [registerDrawer]);
|
|
8870
|
+
const [localDrawerOpen, setLocalDrawerOpen] = useState(false);
|
|
8871
|
+
const drawerOpen = configuration ? configuration.drawerOpen : localDrawerOpen;
|
|
8872
|
+
const setDrawerOpen = configuration ? configuration.setDrawerOpen : setLocalDrawerOpen;
|
|
8873
|
+
const setActiveThreadId = configuration?.setActiveThreadId;
|
|
8874
|
+
const startNewThreadConfig = configuration?.startNewThread;
|
|
8875
|
+
const handleThreadSelected = useCallback((threadId) => {
|
|
8876
|
+
if (onThreadSelect) onThreadSelect(threadId);
|
|
8877
|
+
else setActiveThreadId?.(threadId, { explicit: true });
|
|
8878
|
+
findChatInput(elementRef.current)?.focus();
|
|
8879
|
+
}, [onThreadSelect, setActiveThreadId]);
|
|
8880
|
+
const handleNewThread = useCallback(() => {
|
|
8881
|
+
startNewThread();
|
|
8882
|
+
if (onNewThread) onNewThread();
|
|
8883
|
+
else startNewThreadConfig?.();
|
|
8884
|
+
}, [
|
|
8885
|
+
startNewThread,
|
|
8886
|
+
onNewThread,
|
|
8887
|
+
startNewThreadConfig
|
|
8888
|
+
]);
|
|
8889
|
+
const handleArchive = useCallback((threadId) => {
|
|
8890
|
+
archiveThread(threadId).catch((err) => {
|
|
8891
|
+
console.error("CopilotThreadsDrawer: archiveThread failed", err);
|
|
8892
|
+
});
|
|
8893
|
+
}, [archiveThread]);
|
|
8894
|
+
const handleUnarchive = useCallback((threadId) => {
|
|
8895
|
+
unarchiveThread(threadId).catch((err) => {
|
|
8896
|
+
console.error("CopilotThreadsDrawer: unarchiveThread failed", err);
|
|
8897
|
+
});
|
|
8898
|
+
}, [unarchiveThread]);
|
|
8899
|
+
const handleDelete = useCallback((threadId) => {
|
|
8900
|
+
const isActive = threadId === activeThreadId;
|
|
8901
|
+
deleteThread(threadId).then(() => {
|
|
8902
|
+
if (isActive) {
|
|
8903
|
+
startNewThread();
|
|
8904
|
+
if (onNewThread) onNewThread();
|
|
8905
|
+
else startNewThreadConfig?.();
|
|
8906
|
+
}
|
|
8907
|
+
}).catch((err) => {
|
|
8908
|
+
console.error("CopilotThreadsDrawer: deleteThread failed", err);
|
|
8909
|
+
});
|
|
8910
|
+
}, [
|
|
8911
|
+
deleteThread,
|
|
8912
|
+
activeThreadId,
|
|
8913
|
+
startNewThread,
|
|
8914
|
+
onNewThread,
|
|
8915
|
+
startNewThreadConfig
|
|
8916
|
+
]);
|
|
8917
|
+
const handleFilterChange = useCallback(() => {
|
|
8918
|
+
refetchThreads();
|
|
8919
|
+
}, [refetchThreads]);
|
|
8920
|
+
const handleRetry = useCallback((scope) => {
|
|
8921
|
+
if (scope === "fetch-more") fetchMoreThreads();
|
|
8922
|
+
else refetchThreads();
|
|
8923
|
+
}, [fetchMoreThreads, refetchThreads]);
|
|
8924
|
+
const handleOpenChange = useCallback((open) => {
|
|
8925
|
+
setDrawerOpen(open);
|
|
8926
|
+
}, [setDrawerOpen]);
|
|
8927
|
+
const handleLicensed = useCallback(() => {
|
|
8928
|
+
onLicensed?.();
|
|
8929
|
+
}, [onLicensed]);
|
|
8930
|
+
const handleLoadMore = useCallback(() => {
|
|
8931
|
+
fetchMoreThreads();
|
|
8932
|
+
}, [fetchMoreThreads]);
|
|
8933
|
+
const handlersRef = useRef({
|
|
8934
|
+
handleThreadSelected,
|
|
8935
|
+
handleNewThread,
|
|
8936
|
+
handleArchive,
|
|
8937
|
+
handleUnarchive,
|
|
8938
|
+
handleDelete,
|
|
8939
|
+
handleFilterChange,
|
|
8940
|
+
handleRetry,
|
|
8941
|
+
handleOpenChange,
|
|
8942
|
+
handleLicensed,
|
|
8943
|
+
handleLoadMore
|
|
8944
|
+
});
|
|
8945
|
+
handlersRef.current = {
|
|
8946
|
+
handleThreadSelected,
|
|
8947
|
+
handleNewThread,
|
|
8948
|
+
handleArchive,
|
|
8949
|
+
handleUnarchive,
|
|
8950
|
+
handleDelete,
|
|
8951
|
+
handleFilterChange,
|
|
8952
|
+
handleRetry,
|
|
8953
|
+
handleOpenChange,
|
|
8954
|
+
handleLicensed,
|
|
8955
|
+
handleLoadMore
|
|
8956
|
+
};
|
|
8957
|
+
useEffect(() => {
|
|
8958
|
+
const el = elementRef.current;
|
|
8959
|
+
if (!el) return;
|
|
8960
|
+
const onThreadSelected = (event) => {
|
|
8961
|
+
const detail = event.detail;
|
|
8962
|
+
handlersRef.current.handleThreadSelected(detail.threadId);
|
|
8963
|
+
};
|
|
8964
|
+
const onNewThreadEvent = () => handlersRef.current.handleNewThread();
|
|
8965
|
+
const onArchive = (event) => {
|
|
8966
|
+
const detail = event.detail;
|
|
8967
|
+
handlersRef.current.handleArchive(detail.threadId);
|
|
8968
|
+
};
|
|
8969
|
+
const onUnarchive = (event) => {
|
|
8970
|
+
const detail = event.detail;
|
|
8971
|
+
handlersRef.current.handleUnarchive(detail.threadId);
|
|
8972
|
+
};
|
|
8973
|
+
const onDelete = (event) => {
|
|
8974
|
+
const detail = event.detail;
|
|
8975
|
+
handlersRef.current.handleDelete(detail.threadId);
|
|
8976
|
+
};
|
|
8977
|
+
const onFilterChange = (_event) => {
|
|
8978
|
+
handlersRef.current.handleFilterChange();
|
|
8979
|
+
};
|
|
8980
|
+
const onOpenChangeEvent = (event) => {
|
|
8981
|
+
const detail = event.detail;
|
|
8982
|
+
handlersRef.current.handleOpenChange(detail.open);
|
|
8983
|
+
};
|
|
8984
|
+
const onRetry = (event) => {
|
|
8985
|
+
const detail = event.detail;
|
|
8986
|
+
handlersRef.current.handleRetry(detail.scope);
|
|
8987
|
+
};
|
|
8988
|
+
const onLicensedEvent = () => handlersRef.current.handleLicensed();
|
|
8989
|
+
const onLoadMore = () => handlersRef.current.handleLoadMore();
|
|
8990
|
+
el.addEventListener("thread-selected", onThreadSelected);
|
|
8991
|
+
el.addEventListener("new-thread", onNewThreadEvent);
|
|
8992
|
+
el.addEventListener("archive", onArchive);
|
|
8993
|
+
el.addEventListener("unarchive", onUnarchive);
|
|
8994
|
+
el.addEventListener("delete", onDelete);
|
|
8995
|
+
el.addEventListener("filter-change", onFilterChange);
|
|
8996
|
+
el.addEventListener("open-change", onOpenChangeEvent);
|
|
8997
|
+
el.addEventListener("retry", onRetry);
|
|
8998
|
+
el.addEventListener("licensed", onLicensedEvent);
|
|
8999
|
+
el.addEventListener("load-more", onLoadMore);
|
|
9000
|
+
return () => {
|
|
9001
|
+
el.removeEventListener("thread-selected", onThreadSelected);
|
|
9002
|
+
el.removeEventListener("new-thread", onNewThreadEvent);
|
|
9003
|
+
el.removeEventListener("archive", onArchive);
|
|
9004
|
+
el.removeEventListener("unarchive", onUnarchive);
|
|
9005
|
+
el.removeEventListener("delete", onDelete);
|
|
9006
|
+
el.removeEventListener("filter-change", onFilterChange);
|
|
9007
|
+
el.removeEventListener("open-change", onOpenChangeEvent);
|
|
9008
|
+
el.removeEventListener("retry", onRetry);
|
|
9009
|
+
el.removeEventListener("licensed", onLicensedEvent);
|
|
9010
|
+
el.removeEventListener("load-more", onLoadMore);
|
|
9011
|
+
};
|
|
9012
|
+
}, [mounted]);
|
|
9013
|
+
useEffect(() => {
|
|
9014
|
+
const el = elementRef.current;
|
|
9015
|
+
if (!el) return;
|
|
9016
|
+
el.threads = drawerThreads;
|
|
9017
|
+
}, [drawerThreads, mounted]);
|
|
9018
|
+
useEffect(() => {
|
|
9019
|
+
const el = elementRef.current;
|
|
9020
|
+
if (!el) return;
|
|
9021
|
+
el.loading = isLoading || licensePending;
|
|
9022
|
+
el.error = listError ? listError.message : null;
|
|
9023
|
+
el.activeThreadId = activeThreadId;
|
|
9024
|
+
el.licensed = licensed || licensePending;
|
|
9025
|
+
el.hasMore = hasMoreThreads;
|
|
9026
|
+
el.fetchingMore = isFetchingMoreThreads;
|
|
9027
|
+
}, [
|
|
9028
|
+
isLoading,
|
|
9029
|
+
listError,
|
|
9030
|
+
activeThreadId,
|
|
9031
|
+
licensed,
|
|
9032
|
+
licensePending,
|
|
9033
|
+
hasMoreThreads,
|
|
9034
|
+
isFetchingMoreThreads,
|
|
9035
|
+
mounted
|
|
9036
|
+
]);
|
|
9037
|
+
useEffect(() => {
|
|
9038
|
+
const el = elementRef.current;
|
|
9039
|
+
if (!el) return;
|
|
9040
|
+
el.open = drawerOpen;
|
|
9041
|
+
}, [drawerOpen, mounted]);
|
|
9042
|
+
useEffect(() => {
|
|
9043
|
+
const el = elementRef.current;
|
|
9044
|
+
if (!el) return;
|
|
9045
|
+
if (label !== void 0) el.label = label;
|
|
9046
|
+
}, [label, mounted]);
|
|
9047
|
+
useEffect(() => {
|
|
9048
|
+
const el = elementRef.current;
|
|
9049
|
+
if (!el) return;
|
|
9050
|
+
if (licenseUrl !== void 0) el.licenseUrl = licenseUrl;
|
|
9051
|
+
}, [licenseUrl, mounted]);
|
|
9052
|
+
const rowChildren = useMemo(() => {
|
|
9053
|
+
if (!renderRow) return null;
|
|
9054
|
+
return drawerThreads.map((drawerThread) => {
|
|
9055
|
+
const fullThread = threads.find((t) => t.id === drawerThread.id);
|
|
9056
|
+
if (!fullThread) return null;
|
|
9057
|
+
const content = renderRow(fullThread);
|
|
9058
|
+
if (content === null || content === void 0) return null;
|
|
9059
|
+
return /* @__PURE__ */ jsx("div", {
|
|
9060
|
+
slot: `row:${drawerThread.id}`,
|
|
9061
|
+
children: content
|
|
9062
|
+
}, drawerThread.id);
|
|
9063
|
+
});
|
|
9064
|
+
}, [
|
|
9065
|
+
renderRow,
|
|
9066
|
+
drawerThreads,
|
|
9067
|
+
threads
|
|
9068
|
+
]);
|
|
9069
|
+
if (!mounted) return null;
|
|
9070
|
+
return React.createElement(COPILOTKIT_THREADS_DRAWER_TAG, {
|
|
9071
|
+
ref: elementRef,
|
|
9072
|
+
"data-testid": dataTestId
|
|
9073
|
+
}, rowChildren);
|
|
9074
|
+
}
|
|
9075
|
+
CopilotThreadsDrawer.displayName = "CopilotThreadsDrawer";
|
|
9076
|
+
|
|
8540
9077
|
//#endregion
|
|
8541
9078
|
//#region src/v2/components/WildcardToolCallRender.tsx
|
|
8542
9079
|
const WildcardToolCallRender = defineToolCallRenderer({
|
|
@@ -10719,5 +11256,5 @@ function validateProps(props) {
|
|
|
10719
11256
|
}
|
|
10720
11257
|
|
|
10721
11258
|
//#endregion
|
|
10722
|
-
export {
|
|
10723
|
-
//# sourceMappingURL=copilotkit-
|
|
11259
|
+
export { UseAgentUpdate as $, CopilotChatMessageView as A, CopilotChatAssistantMessage_default as B, CopilotModalHeader as C, CopilotChatConfigurationProvider as Ct, CopilotChat as D, DefaultOpenIcon as E, CopilotChatSuggestionView as F, useLearnFromUserActionInCurrentThread as G, useLearningContainersInCurrentThread as H, CopilotChatSuggestionPill as I, useInterrupt as J, useLearnFromUserAction as K, CopilotChatReasoningMessage_default as L, IntelligenceIndicator as M, getIntelligenceTurnAnchors as N, CopilotChatView_default as O, IntelligenceIndicatorView as P, useCapabilities as Q, CopilotChatUserMessage_default as R, CopilotSidebarView as S, CopilotChatAudioRecorder as St, DefaultCloseIcon as T, useLearningContainers as U, CopilotChatToolCallsView as V, useAttachments as W, useSuggestions as X, useConfigureSuggestions as Y, useAgentContext as Z, WildcardToolCallRender as _, useRenderToolCall as _t, ThreadsProvider as a, useFrontendTool as at, CopilotSidebar as b, CopilotChatInput_default as bt, CoAgentStateRendersProvider as c, CopilotKitProvider as ct, shouldShowDevConsole as d, SandboxFunctionsContext as dt, useAgent as et, useToast as f, useSandboxFunctions as ft, useCopilotContext as g, CopilotKitInspector as gt, CopilotContext as h, MCPAppsActivityType as ht, ThreadsContext as i, useComponent as it, INTELLIGENCE_TURN_HEAD as j, CopilotChatAttachmentQueue as k, useCoAgentStateRenders as l, defineToolCallRenderer as lt, useCopilotMessagesContext as m, MCPAppsActivityRenderer as mt, defaultCopilotContextCategories as n, useDefaultRenderTool as nt, useThreads as o, useRenderActivityMessage as ot, CopilotMessagesContext as p, MCPAppsActivityContentSchema as pt, useThreads$1 as q, CoAgentStateRenderBridge as r, useRenderTool as rt, CoAgentStateRendersContext as s, useRenderCustomMessages as st, CopilotKit as t, useHumanInTheLoop as tt, useAsyncCallback as u, createA2UIMessageRenderer as ut, CopilotThreadsDrawer as v, useCopilotKit as vt, CopilotChatToggleButton as w, useCopilotChatConfiguration as wt, CopilotPopupView as x, AudioRecorderError as xt, CopilotPopup as y, CopilotKitCoreReact as yt, CopilotChatAttachmentRenderer as z };
|
|
11260
|
+
//# sourceMappingURL=copilotkit-BtRkFsNR.mjs.map
|