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