@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
package/dist/v2/headless.mjs
CHANGED
|
@@ -3,8 +3,8 @@ import React, { createContext, useCallback, useContext, useEffect, useLayoutEffe
|
|
|
3
3
|
import { DEFAULT_AGENT_ID, randomUUID } from "@copilotkit/shared";
|
|
4
4
|
import { twMerge } from "tailwind-merge";
|
|
5
5
|
import { jsx } from "react/jsx-runtime";
|
|
6
|
-
import { HttpAgent } from "@ag-ui/client";
|
|
7
|
-
import { CopilotKitCoreRuntimeConnectionStatus, ProxiedCopilotRuntimeAgent, ToolCallStatus, ɵcreateThreadStore, ɵselectHasNextPage, ɵselectIsFetchingNextPage, ɵselectThreads, ɵselectThreadsError, ɵselectThreadsIsLoading } from "@copilotkit/core";
|
|
6
|
+
import { HttpAgent, buildResumeArray, isInterruptExpired, randomUUID as randomUUID$1 } from "@ag-ui/client";
|
|
7
|
+
import { CopilotKitCoreRuntimeConnectionStatus, ProxiedCopilotRuntimeAgent, ToolCallStatus, ɵcreateThreadStore, ɵselectHasNextPage, ɵselectIsFetchingNextPage, ɵselectIsMutating, ɵselectThreads, ɵselectThreadsError, ɵselectThreadsIsLoading } from "@copilotkit/core";
|
|
8
8
|
import { z } from "zod";
|
|
9
9
|
|
|
10
10
|
//#region src/v2/lib/slots.tsx
|
|
@@ -115,6 +115,25 @@ const CopilotChatDefaultLabels = {
|
|
|
115
115
|
modalHeaderTitle: "CopilotKit Chat",
|
|
116
116
|
welcomeMessageText: "How can I help you today?"
|
|
117
117
|
};
|
|
118
|
+
/**
|
|
119
|
+
* Mobile breakpoint below which the chat modal and the thread-list drawer are
|
|
120
|
+
* mutually exclusive. At or above this width both surfaces may coexist. This
|
|
121
|
+
* mirrors the `(max-width: 767px)` / `(min-width: 768px)` split already used by
|
|
122
|
+
* CopilotChatInput and CopilotSidebarView.
|
|
123
|
+
*/
|
|
124
|
+
const MOBILE_MAX_WIDTH_PX = 767;
|
|
125
|
+
/**
|
|
126
|
+
* Reports whether the current viewport is in the mobile range (`<768px`), where
|
|
127
|
+
* the chat modal and drawer must not be open simultaneously. SSR-safe and
|
|
128
|
+
* defensive against environments without `matchMedia` (treated as desktop, so
|
|
129
|
+
* no mutual-exclusion constraint is applied).
|
|
130
|
+
*
|
|
131
|
+
* @returns `true` when the viewport is mobile-width, `false` otherwise.
|
|
132
|
+
*/
|
|
133
|
+
function isMobileViewport() {
|
|
134
|
+
if (typeof window === "undefined" || typeof window.matchMedia !== "function") return false;
|
|
135
|
+
return window.matchMedia(`(max-width: ${MOBILE_MAX_WIDTH_PX}px)`).matches;
|
|
136
|
+
}
|
|
118
137
|
const CopilotChatConfiguration = createContext(null);
|
|
119
138
|
const CopilotChatConfigurationProvider = ({ children, labels, agentId, threadId, hasExplicitThreadId, isModalDefaultOpen }) => {
|
|
120
139
|
const parentConfig = useContext(CopilotChatConfiguration);
|
|
@@ -125,12 +144,22 @@ const CopilotChatConfigurationProvider = ({ children, labels, agentId, threadId,
|
|
|
125
144
|
...stableLabels
|
|
126
145
|
}), [stableLabels, parentConfig?.labels]);
|
|
127
146
|
const resolvedAgentId = agentId ?? parentConfig?.agentId ?? DEFAULT_AGENT_ID;
|
|
147
|
+
const threadIdPropIsAuthoritative = threadId !== void 0 && hasExplicitThreadId !== false;
|
|
148
|
+
const isThreadIdControlled = threadIdPropIsAuthoritative;
|
|
149
|
+
const [activeThreadOverride, setActiveThreadOverride] = useState(null);
|
|
128
150
|
const resolvedThreadId = useMemo(() => {
|
|
129
|
-
if (
|
|
151
|
+
if (threadIdPropIsAuthoritative) return threadId;
|
|
152
|
+
if (activeThreadOverride) return activeThreadOverride.threadId;
|
|
130
153
|
if (parentConfig?.threadId) return parentConfig.threadId;
|
|
154
|
+
if (threadId) return threadId;
|
|
131
155
|
return randomUUID();
|
|
132
|
-
}, [
|
|
133
|
-
|
|
156
|
+
}, [
|
|
157
|
+
threadIdPropIsAuthoritative,
|
|
158
|
+
threadId,
|
|
159
|
+
parentConfig?.threadId,
|
|
160
|
+
activeThreadOverride
|
|
161
|
+
]);
|
|
162
|
+
const resolvedHasExplicitThreadId = (threadIdPropIsAuthoritative ? true : activeThreadOverride?.explicit ?? hasExplicitThreadId ?? false) || !!parentConfig?.hasExplicitThreadId;
|
|
134
163
|
const [internalModalOpen, setInternalModalOpen] = useState(isModalDefaultOpen ?? true);
|
|
135
164
|
const hasExplicitDefault = isModalDefaultOpen !== void 0;
|
|
136
165
|
const setAndSync = useCallback((open) => {
|
|
@@ -149,20 +178,113 @@ const CopilotChatConfigurationProvider = ({ children, labels, agentId, threadId,
|
|
|
149
178
|
}, [parentConfig?.isModalOpen, hasExplicitDefault]);
|
|
150
179
|
const resolvedIsModalOpen = hasExplicitDefault ? internalModalOpen : parentConfig?.isModalOpen ?? internalModalOpen;
|
|
151
180
|
const resolvedSetModalOpen = hasExplicitDefault ? setAndSync : parentConfig?.setModalOpen ?? setInternalModalOpen;
|
|
181
|
+
const [ownDrawerOpen, setOwnDrawerOpen] = useState(false);
|
|
182
|
+
const [ownDrawerCount, setOwnDrawerCount] = useState(0);
|
|
183
|
+
const modalCloseRef = useRef(() => {});
|
|
184
|
+
modalCloseRef.current = resolvedSetModalOpen;
|
|
185
|
+
const registeredModalClosersRef = useRef([]);
|
|
186
|
+
const ownRegisterModalCloser = useCallback((closeModal) => {
|
|
187
|
+
registeredModalClosersRef.current.push(closeModal);
|
|
188
|
+
return () => {
|
|
189
|
+
registeredModalClosersRef.current = registeredModalClosersRef.current.filter((entry) => entry !== closeModal);
|
|
190
|
+
};
|
|
191
|
+
}, []);
|
|
192
|
+
const ownSetDrawerOpen = useCallback((open) => {
|
|
193
|
+
setOwnDrawerOpen(open);
|
|
194
|
+
if (open && isMobileViewport()) {
|
|
195
|
+
const registered = registeredModalClosersRef.current;
|
|
196
|
+
(registered.length > 0 ? registered[registered.length - 1] : modalCloseRef.current)(false);
|
|
197
|
+
}
|
|
198
|
+
}, []);
|
|
199
|
+
const ownRegisterDrawer = useCallback(() => {
|
|
200
|
+
setOwnDrawerCount((count) => count + 1);
|
|
201
|
+
return () => {
|
|
202
|
+
setOwnDrawerCount((count) => Math.max(0, count - 1));
|
|
203
|
+
};
|
|
204
|
+
}, []);
|
|
205
|
+
const resolvedDrawerOpen = parentConfig ? parentConfig.drawerOpen : ownDrawerOpen;
|
|
206
|
+
const resolvedSetDrawerOpen = parentConfig ? parentConfig.setDrawerOpen : ownSetDrawerOpen;
|
|
207
|
+
const resolvedDrawerRegistered = parentConfig ? parentConfig.drawerRegistered : ownDrawerCount > 0;
|
|
208
|
+
const resolvedRegisterDrawer = parentConfig ? parentConfig.registerDrawer : ownRegisterDrawer;
|
|
209
|
+
const resolvedRegisterModalCloser = parentConfig ? parentConfig.ɵregisterModalCloser : ownRegisterModalCloser;
|
|
210
|
+
useEffect(() => {
|
|
211
|
+
if (!hasExplicitDefault) return;
|
|
212
|
+
return resolvedRegisterModalCloser(resolvedSetModalOpen);
|
|
213
|
+
}, [
|
|
214
|
+
hasExplicitDefault,
|
|
215
|
+
resolvedRegisterModalCloser,
|
|
216
|
+
resolvedSetModalOpen
|
|
217
|
+
]);
|
|
218
|
+
const isThreadIdControlledRef = useRef(isThreadIdControlled);
|
|
219
|
+
isThreadIdControlledRef.current = isThreadIdControlled;
|
|
220
|
+
const ownSetActiveThreadId = useCallback((id, options) => {
|
|
221
|
+
setActiveThreadOverride({
|
|
222
|
+
threadId: id,
|
|
223
|
+
explicit: options?.explicit ?? true
|
|
224
|
+
});
|
|
225
|
+
}, []);
|
|
226
|
+
const ownStartNewThread = useCallback(() => {
|
|
227
|
+
setActiveThreadOverride({
|
|
228
|
+
threadId: randomUUID(),
|
|
229
|
+
explicit: false
|
|
230
|
+
});
|
|
231
|
+
}, []);
|
|
232
|
+
const parentSetActiveThreadId = parentConfig?.setActiveThreadId;
|
|
233
|
+
const parentStartNewThread = parentConfig?.startNewThread;
|
|
234
|
+
const resolvedSetActiveThreadId = useCallback((id, options) => {
|
|
235
|
+
if (isThreadIdControlledRef.current) {
|
|
236
|
+
console.warn("[CopilotKit] Ignoring setActiveThreadId(): threadId is controlled via the `threadId` prop on CopilotChatConfigurationProvider.");
|
|
237
|
+
return;
|
|
238
|
+
}
|
|
239
|
+
if (parentSetActiveThreadId) {
|
|
240
|
+
parentSetActiveThreadId(id, options);
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
243
|
+
ownSetActiveThreadId(id, options);
|
|
244
|
+
}, [parentSetActiveThreadId, ownSetActiveThreadId]);
|
|
245
|
+
const resolvedStartNewThread = useCallback(() => {
|
|
246
|
+
if (isThreadIdControlledRef.current) {
|
|
247
|
+
console.warn("[CopilotKit] Ignoring startNewThread(): threadId is controlled via the `threadId` prop on CopilotChatConfigurationProvider.");
|
|
248
|
+
return;
|
|
249
|
+
}
|
|
250
|
+
if (parentStartNewThread) {
|
|
251
|
+
parentStartNewThread();
|
|
252
|
+
return;
|
|
253
|
+
}
|
|
254
|
+
ownStartNewThread();
|
|
255
|
+
}, [parentStartNewThread, ownStartNewThread]);
|
|
256
|
+
const setModalOpenWithDrawerExclusion = useCallback((open) => {
|
|
257
|
+
if (open && isMobileViewport()) resolvedSetDrawerOpen(false);
|
|
258
|
+
resolvedSetModalOpen(open);
|
|
259
|
+
}, [resolvedSetModalOpen, resolvedSetDrawerOpen]);
|
|
152
260
|
const configurationValue = useMemo(() => ({
|
|
153
261
|
labels: mergedLabels,
|
|
154
262
|
agentId: resolvedAgentId,
|
|
155
263
|
threadId: resolvedThreadId,
|
|
156
264
|
hasExplicitThreadId: resolvedHasExplicitThreadId,
|
|
157
265
|
isModalOpen: resolvedIsModalOpen,
|
|
158
|
-
setModalOpen:
|
|
266
|
+
setModalOpen: setModalOpenWithDrawerExclusion,
|
|
267
|
+
drawerOpen: resolvedDrawerOpen,
|
|
268
|
+
setDrawerOpen: resolvedSetDrawerOpen,
|
|
269
|
+
drawerRegistered: resolvedDrawerRegistered,
|
|
270
|
+
registerDrawer: resolvedRegisterDrawer,
|
|
271
|
+
ɵregisterModalCloser: resolvedRegisterModalCloser,
|
|
272
|
+
setActiveThreadId: resolvedSetActiveThreadId,
|
|
273
|
+
startNewThread: resolvedStartNewThread
|
|
159
274
|
}), [
|
|
160
275
|
mergedLabels,
|
|
161
276
|
resolvedAgentId,
|
|
162
277
|
resolvedThreadId,
|
|
163
278
|
resolvedHasExplicitThreadId,
|
|
164
279
|
resolvedIsModalOpen,
|
|
165
|
-
|
|
280
|
+
setModalOpenWithDrawerExclusion,
|
|
281
|
+
resolvedDrawerOpen,
|
|
282
|
+
resolvedSetDrawerOpen,
|
|
283
|
+
resolvedDrawerRegistered,
|
|
284
|
+
resolvedRegisterDrawer,
|
|
285
|
+
resolvedRegisterModalCloser,
|
|
286
|
+
resolvedSetActiveThreadId,
|
|
287
|
+
resolvedStartNewThread
|
|
166
288
|
]);
|
|
167
289
|
return /* @__PURE__ */ jsx(CopilotChatConfiguration.Provider, {
|
|
168
290
|
value: configurationValue,
|
|
@@ -204,7 +326,7 @@ function useAgent({ agentId, updates, throttleMs } = {}) {
|
|
|
204
326
|
if (isRuntimeConfigured && (status === CopilotKitCoreRuntimeConnectionStatus.Disconnected || status === CopilotKitCoreRuntimeConnectionStatus.Connecting)) {
|
|
205
327
|
const cached = provisionalAgentCache.current.get(agentId);
|
|
206
328
|
if (cached) {
|
|
207
|
-
|
|
329
|
+
copilotkit.applyHeadersToAgent(cached);
|
|
208
330
|
return cached;
|
|
209
331
|
}
|
|
210
332
|
const provisional = new ProxiedCopilotRuntimeAgent({
|
|
@@ -213,14 +335,14 @@ function useAgent({ agentId, updates, throttleMs } = {}) {
|
|
|
213
335
|
transport: copilotkit.runtimeTransport,
|
|
214
336
|
runtimeMode: "pending"
|
|
215
337
|
});
|
|
216
|
-
|
|
338
|
+
copilotkit.applyHeadersToAgent(provisional);
|
|
217
339
|
provisionalAgentCache.current.set(agentId, provisional);
|
|
218
340
|
return provisional;
|
|
219
341
|
}
|
|
220
342
|
if (isRuntimeConfigured && status === CopilotKitCoreRuntimeConnectionStatus.Error) {
|
|
221
343
|
const cached = provisionalAgentCache.current.get(agentId);
|
|
222
344
|
if (cached) {
|
|
223
|
-
|
|
345
|
+
copilotkit.applyHeadersToAgent(cached);
|
|
224
346
|
return cached;
|
|
225
347
|
}
|
|
226
348
|
const provisional = new ProxiedCopilotRuntimeAgent({
|
|
@@ -229,7 +351,7 @@ function useAgent({ agentId, updates, throttleMs } = {}) {
|
|
|
229
351
|
transport: copilotkit.runtimeTransport,
|
|
230
352
|
runtimeMode: "pending"
|
|
231
353
|
});
|
|
232
|
-
|
|
354
|
+
copilotkit.applyHeadersToAgent(provisional);
|
|
233
355
|
provisionalAgentCache.current.set(agentId, provisional);
|
|
234
356
|
return provisional;
|
|
235
357
|
}
|
|
@@ -280,7 +402,7 @@ function useAgent({ agentId, updates, throttleMs } = {}) {
|
|
|
280
402
|
updateFlags
|
|
281
403
|
]);
|
|
282
404
|
useEffect(() => {
|
|
283
|
-
if (agent instanceof HttpAgent)
|
|
405
|
+
if (agent instanceof HttpAgent) copilotkit.applyHeadersToAgent(agent);
|
|
284
406
|
}, [agent, JSON.stringify(copilotkit.headers)]);
|
|
285
407
|
const chatConfig = useCopilotChatConfiguration();
|
|
286
408
|
const configThreadId = chatConfig?.threadId;
|
|
@@ -492,114 +614,165 @@ const INTERRUPT_EVENT_NAME = "on_interrupt";
|
|
|
492
614
|
function isPromiseLike(value) {
|
|
493
615
|
return (typeof value === "object" || typeof value === "function") && value !== null && typeof Reflect.get(value, "then") === "function";
|
|
494
616
|
}
|
|
617
|
+
/** Derive the legacy-compatible `event` for any pending interrupt. */
|
|
618
|
+
function toLegacyEvent(pending) {
|
|
619
|
+
if (pending.kind === "legacy") return pending.event;
|
|
620
|
+
return {
|
|
621
|
+
name: INTERRUPT_EVENT_NAME,
|
|
622
|
+
value: pending.interrupts[0]
|
|
623
|
+
};
|
|
624
|
+
}
|
|
495
625
|
/**
|
|
496
|
-
* Handles agent interrupts
|
|
497
|
-
*
|
|
498
|
-
* The hook listens to custom events on the active agent, stores interrupt payloads per run,
|
|
499
|
-
* and surfaces a render callback once the run finalizes. Call `resolve` from your UI to resume
|
|
500
|
-
* execution with user-provided data.
|
|
501
|
-
*
|
|
502
|
-
* - `renderInChat: true` (default): the element is published into `<CopilotChat>` and this hook returns `void`.
|
|
503
|
-
* - `renderInChat: false`: the hook returns the interrupt element so you can place it anywhere in your component tree.
|
|
626
|
+
* Handles agent interrupts with optional filtering, preprocessing, and resume behavior.
|
|
504
627
|
*
|
|
505
|
-
*
|
|
506
|
-
*
|
|
628
|
+
* Supports both the AG-UI standard interrupt flow (`RUN_FINISHED` with
|
|
629
|
+
* `outcome.type === "interrupt"`) and the legacy custom-event flow
|
|
630
|
+
* (`on_interrupt`). For standard interrupts, `render` receives `interrupt`
|
|
631
|
+
* (the primary one) and `interrupts` (the full open set); call `resolve(payload)`
|
|
632
|
+
* to resume or `cancel()` to cancel. Resuming addresses the targeted interrupt
|
|
633
|
+
* and, once every open interrupt is addressed, submits a single spec `resume`
|
|
634
|
+
* array via `copilotkit.runAgent`.
|
|
507
635
|
*
|
|
508
|
-
*
|
|
509
|
-
*
|
|
510
|
-
* @returns When `renderInChat` is `false`, returns the interrupt element (or `null` when idle).
|
|
511
|
-
* Otherwise returns `void` and publishes the element into chat. In `render`, `result` is always
|
|
512
|
-
* either the handler's resolved return value or `null` (including when no handler is provided,
|
|
513
|
-
* when filtering skips the interrupt, or when handler execution fails).
|
|
636
|
+
* - `renderInChat: true` (default): the element is published into `<CopilotChat>`; returns `void`.
|
|
637
|
+
* - `renderInChat: false`: the hook returns the interrupt element for manual placement.
|
|
514
638
|
*
|
|
515
639
|
* @example
|
|
516
640
|
* ```tsx
|
|
517
|
-
*
|
|
518
|
-
*
|
|
519
|
-
*
|
|
520
|
-
*
|
|
521
|
-
*
|
|
522
|
-
* <
|
|
523
|
-
*
|
|
524
|
-
*
|
|
525
|
-
*
|
|
526
|
-
* </div>
|
|
527
|
-
* ),
|
|
528
|
-
* });
|
|
529
|
-
*
|
|
530
|
-
* return null;
|
|
531
|
-
* }
|
|
532
|
-
* ```
|
|
533
|
-
*
|
|
534
|
-
* @example
|
|
535
|
-
* ```tsx
|
|
536
|
-
* import { useInterrupt } from "@copilotkit/react-core/v2";
|
|
537
|
-
*
|
|
538
|
-
* function CustomPanel() {
|
|
539
|
-
* const interruptElement = useInterrupt({
|
|
540
|
-
* renderInChat: false,
|
|
541
|
-
* enabled: (event) => event.value.startsWith("approval:"),
|
|
542
|
-
* handler: async ({ event }) => ({ label: event.value.toUpperCase() }),
|
|
543
|
-
* render: ({ event, result, resolve }) => (
|
|
544
|
-
* <aside>
|
|
545
|
-
* <strong>{result?.label ?? ""}</strong>
|
|
546
|
-
* <button onClick={() => resolve({ value: event.value })}>Continue</button>
|
|
547
|
-
* </aside>
|
|
548
|
-
* ),
|
|
549
|
-
* });
|
|
550
|
-
*
|
|
551
|
-
* return <>{interruptElement}</>;
|
|
552
|
-
* }
|
|
641
|
+
* useInterrupt({
|
|
642
|
+
* render: ({ interrupt, resolve, cancel }) => (
|
|
643
|
+
* <div>
|
|
644
|
+
* <p>{interrupt?.message}</p>
|
|
645
|
+
* <button onClick={() => resolve({ approved: true })}>Approve</button>
|
|
646
|
+
* <button onClick={() => cancel()}>Cancel</button>
|
|
647
|
+
* </div>
|
|
648
|
+
* ),
|
|
649
|
+
* });
|
|
553
650
|
* ```
|
|
554
651
|
*/
|
|
555
652
|
function useInterrupt(config) {
|
|
556
653
|
const { copilotkit } = useCopilotKit();
|
|
557
654
|
const { agent } = useAgent({ agentId: config.agentId });
|
|
558
|
-
const [
|
|
559
|
-
const
|
|
560
|
-
|
|
655
|
+
const [pending, setPending] = useState(null);
|
|
656
|
+
const pendingRef = useRef(pending);
|
|
657
|
+
pendingRef.current = pending;
|
|
561
658
|
const [handlerResult, setHandlerResult] = useState(null);
|
|
659
|
+
const responsesRef = useRef({});
|
|
562
660
|
useEffect(() => {
|
|
563
|
-
let
|
|
661
|
+
let localLegacy = null;
|
|
662
|
+
let localStandard = null;
|
|
564
663
|
const subscription = agent.subscribe({
|
|
565
664
|
onCustomEvent: ({ event }) => {
|
|
566
|
-
if (event.name === INTERRUPT_EVENT_NAME)
|
|
665
|
+
if (event.name === INTERRUPT_EVENT_NAME) localLegacy = {
|
|
567
666
|
name: event.name,
|
|
568
667
|
value: event.value
|
|
569
668
|
};
|
|
570
669
|
},
|
|
670
|
+
onRunFinishedEvent: (params) => {
|
|
671
|
+
if (params.outcome === "interrupt") localStandard = params.interrupts;
|
|
672
|
+
},
|
|
571
673
|
onRunStartedEvent: () => {
|
|
572
|
-
|
|
573
|
-
|
|
674
|
+
localLegacy = null;
|
|
675
|
+
localStandard = null;
|
|
676
|
+
responsesRef.current = {};
|
|
677
|
+
setPending(null);
|
|
574
678
|
},
|
|
575
679
|
onRunFinalized: () => {
|
|
576
|
-
if (
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
}
|
|
680
|
+
if (localStandard && localStandard.length > 0) setPending({
|
|
681
|
+
kind: "standard",
|
|
682
|
+
interrupts: localStandard
|
|
683
|
+
});
|
|
684
|
+
else if (localLegacy) setPending({
|
|
685
|
+
kind: "legacy",
|
|
686
|
+
event: localLegacy
|
|
687
|
+
});
|
|
688
|
+
localLegacy = null;
|
|
689
|
+
localStandard = null;
|
|
580
690
|
},
|
|
581
691
|
onRunFailed: () => {
|
|
582
|
-
|
|
583
|
-
|
|
692
|
+
localLegacy = null;
|
|
693
|
+
localStandard = null;
|
|
694
|
+
responsesRef.current = {};
|
|
695
|
+
setPending(null);
|
|
584
696
|
}
|
|
585
697
|
});
|
|
586
698
|
return () => subscription.unsubscribe();
|
|
587
699
|
}, [agent]);
|
|
588
|
-
const
|
|
700
|
+
const submitStandardIfComplete = useCallback(async (interrupts) => {
|
|
701
|
+
if (!interrupts.every((i) => responsesRef.current[i.id])) return;
|
|
702
|
+
const expired = interrupts.find((i) => isInterruptExpired(i));
|
|
703
|
+
if (expired) {
|
|
704
|
+
console.error(`[CopilotKit] useInterrupt: interrupt ${expired.id} expired at ${expired.expiresAt}; not resuming.`);
|
|
705
|
+
responsesRef.current = {};
|
|
706
|
+
setPending(null);
|
|
707
|
+
return;
|
|
708
|
+
}
|
|
709
|
+
const resume = buildResumeArray(interrupts, responsesRef.current);
|
|
710
|
+
for (const i of interrupts) {
|
|
711
|
+
if (!i.toolCallId) continue;
|
|
712
|
+
const response = responsesRef.current[i.id];
|
|
713
|
+
const content = response.status === "cancelled" ? { status: "cancelled" } : response.payload ?? { status: "resolved" };
|
|
714
|
+
agent.addMessage({
|
|
715
|
+
id: randomUUID$1(),
|
|
716
|
+
role: "tool",
|
|
717
|
+
toolCallId: i.toolCallId,
|
|
718
|
+
content: JSON.stringify(content)
|
|
719
|
+
});
|
|
720
|
+
}
|
|
721
|
+
responsesRef.current = {};
|
|
589
722
|
try {
|
|
723
|
+
return await copilotkit.runAgent({
|
|
724
|
+
agent,
|
|
725
|
+
resume
|
|
726
|
+
});
|
|
727
|
+
} catch (err) {
|
|
728
|
+
console.error("[CopilotKit] useInterrupt resolve: runAgent rejected; clearing pending + rethrowing", err);
|
|
729
|
+
setPending(null);
|
|
730
|
+
throw err;
|
|
731
|
+
}
|
|
732
|
+
}, [agent, copilotkit]);
|
|
733
|
+
const resolve = useCallback(async (payload, interruptId) => {
|
|
734
|
+
const current = pendingRef.current;
|
|
735
|
+
if (!current) return;
|
|
736
|
+
if (current.kind === "legacy") try {
|
|
590
737
|
return await copilotkit.runAgent({
|
|
591
738
|
agent,
|
|
592
739
|
forwardedProps: { command: {
|
|
593
|
-
resume:
|
|
594
|
-
interruptEvent:
|
|
740
|
+
resume: payload,
|
|
741
|
+
interruptEvent: current.event.value
|
|
595
742
|
} }
|
|
596
743
|
});
|
|
597
744
|
} catch (err) {
|
|
598
745
|
console.error("[CopilotKit] useInterrupt resolve: runAgent rejected; clearing pending + rethrowing", err);
|
|
599
|
-
|
|
746
|
+
setPending(null);
|
|
600
747
|
throw err;
|
|
601
748
|
}
|
|
602
|
-
|
|
749
|
+
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.`);
|
|
750
|
+
const id = interruptId ?? current.interrupts[0]?.id;
|
|
751
|
+
if (!id) return;
|
|
752
|
+
responsesRef.current[id] = {
|
|
753
|
+
status: "resolved",
|
|
754
|
+
payload
|
|
755
|
+
};
|
|
756
|
+
return submitStandardIfComplete(current.interrupts);
|
|
757
|
+
}, [
|
|
758
|
+
agent,
|
|
759
|
+
copilotkit,
|
|
760
|
+
submitStandardIfComplete
|
|
761
|
+
]);
|
|
762
|
+
const cancel = useCallback(async (interruptId) => {
|
|
763
|
+
const current = pendingRef.current;
|
|
764
|
+
if (!current) return;
|
|
765
|
+
if (current.kind === "legacy") {
|
|
766
|
+
console.warn("[CopilotKit] useInterrupt: cancel() is not supported for legacy on_interrupt interrupts; dismissing.");
|
|
767
|
+
setPending(null);
|
|
768
|
+
return;
|
|
769
|
+
}
|
|
770
|
+
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.`);
|
|
771
|
+
const id = interruptId ?? current.interrupts[0]?.id;
|
|
772
|
+
if (!id) return;
|
|
773
|
+
responsesRef.current[id] = { status: "cancelled" };
|
|
774
|
+
return submitStandardIfComplete(current.interrupts);
|
|
775
|
+
}, [submitStandardIfComplete]);
|
|
603
776
|
const renderRef = useRef(config.render);
|
|
604
777
|
renderRef.current = config.render;
|
|
605
778
|
const enabledRef = useRef(config.enabled);
|
|
@@ -608,6 +781,8 @@ function useInterrupt(config) {
|
|
|
608
781
|
handlerRef.current = config.handler;
|
|
609
782
|
const resolveRef = useRef(resolve);
|
|
610
783
|
resolveRef.current = resolve;
|
|
784
|
+
const cancelRef = useRef(cancel);
|
|
785
|
+
cancelRef.current = cancel;
|
|
611
786
|
const isEnabled = (event) => {
|
|
612
787
|
const predicate = enabledRef.current;
|
|
613
788
|
if (!predicate) return true;
|
|
@@ -619,11 +794,12 @@ function useInterrupt(config) {
|
|
|
619
794
|
}
|
|
620
795
|
};
|
|
621
796
|
useEffect(() => {
|
|
622
|
-
if (!
|
|
797
|
+
if (!pending) {
|
|
623
798
|
setHandlerResult(null);
|
|
624
799
|
return;
|
|
625
800
|
}
|
|
626
|
-
|
|
801
|
+
const legacyEvent = toLegacyEvent(pending);
|
|
802
|
+
if (!isEnabled(legacyEvent)) {
|
|
627
803
|
setHandlerResult(null);
|
|
628
804
|
return;
|
|
629
805
|
}
|
|
@@ -636,8 +812,11 @@ function useInterrupt(config) {
|
|
|
636
812
|
let maybePromise;
|
|
637
813
|
try {
|
|
638
814
|
maybePromise = handler({
|
|
639
|
-
event:
|
|
640
|
-
|
|
815
|
+
event: legacyEvent,
|
|
816
|
+
interrupt: pending.kind === "standard" ? pending.interrupts[0] : null,
|
|
817
|
+
interrupts: pending.kind === "standard" ? pending.interrupts : [],
|
|
818
|
+
resolve: resolveRef.current,
|
|
819
|
+
cancel: cancelRef.current
|
|
641
820
|
});
|
|
642
821
|
} catch (err) {
|
|
643
822
|
console.error("[CopilotKit] useInterrupt handler threw; result will be null:", err);
|
|
@@ -656,19 +835,24 @@ function useInterrupt(config) {
|
|
|
656
835
|
return () => {
|
|
657
836
|
cancelled = true;
|
|
658
837
|
};
|
|
659
|
-
}, [
|
|
838
|
+
}, [pending]);
|
|
660
839
|
const element = useMemo(() => {
|
|
661
|
-
if (!
|
|
662
|
-
|
|
840
|
+
if (!pending) return null;
|
|
841
|
+
const legacyEvent = toLegacyEvent(pending);
|
|
842
|
+
if (!isEnabled(legacyEvent)) return null;
|
|
663
843
|
return renderRef.current({
|
|
664
|
-
event:
|
|
844
|
+
event: legacyEvent,
|
|
845
|
+
interrupt: pending.kind === "standard" ? pending.interrupts[0] : null,
|
|
846
|
+
interrupts: pending.kind === "standard" ? pending.interrupts : [],
|
|
665
847
|
result: handlerResult,
|
|
666
|
-
resolve
|
|
848
|
+
resolve,
|
|
849
|
+
cancel
|
|
667
850
|
});
|
|
668
851
|
}, [
|
|
669
|
-
|
|
852
|
+
pending,
|
|
670
853
|
handlerResult,
|
|
671
|
-
resolve
|
|
854
|
+
resolve,
|
|
855
|
+
cancel
|
|
672
856
|
]);
|
|
673
857
|
useEffect(() => {
|
|
674
858
|
if (config.renderInChat === false) return;
|
|
@@ -930,7 +1114,7 @@ function useThreadStoreSelector(store, selector) {
|
|
|
930
1114
|
return useSyncExternalStore(useCallback((onStoreChange) => {
|
|
931
1115
|
const subscription = store.select(selector).subscribe(onStoreChange);
|
|
932
1116
|
return () => subscription.unsubscribe();
|
|
933
|
-
}, [store, selector]), () => selector(store.getState()));
|
|
1117
|
+
}, [store, selector]), () => selector(store.getState()), () => selector(store.getServerState()));
|
|
934
1118
|
}
|
|
935
1119
|
/**
|
|
936
1120
|
* React hook for listing and managing Intelligence platform threads.
|
|
@@ -941,9 +1125,9 @@ function useThreadStoreSelector(store, selector) {
|
|
|
941
1125
|
* current without polling — thread creates, renames, archives, and deletes
|
|
942
1126
|
* from any client are reflected immediately.
|
|
943
1127
|
*
|
|
944
|
-
* Mutation methods (`renameThread`, `archiveThread`, `
|
|
945
|
-
* promises that resolve once the platform confirms the
|
|
946
|
-
* with an `Error` on failure.
|
|
1128
|
+
* Mutation methods (`renameThread`, `archiveThread`, `unarchiveThread`,
|
|
1129
|
+
* `deleteThread`) return promises that resolve once the platform confirms the
|
|
1130
|
+
* operation and reject with an `Error` on failure.
|
|
947
1131
|
*
|
|
948
1132
|
* @param input - Agent identifier and optional list controls.
|
|
949
1133
|
* @returns Thread list state and stable mutation callbacks.
|
|
@@ -973,7 +1157,7 @@ function useThreadStoreSelector(store, selector) {
|
|
|
973
1157
|
* }
|
|
974
1158
|
* ```
|
|
975
1159
|
*/
|
|
976
|
-
function useThreads({ agentId, includeArchived, limit }) {
|
|
1160
|
+
function useThreads({ agentId, includeArchived, limit, enabled = true }) {
|
|
977
1161
|
const { copilotkit } = useCopilotKit();
|
|
978
1162
|
const [store] = useState(() => ɵcreateThreadStore({ fetch: globalThis.fetch }));
|
|
979
1163
|
const coreThreads = useThreadStoreSelector(store, ɵselectThreads);
|
|
@@ -990,6 +1174,7 @@ function useThreads({ agentId, includeArchived, limit }) {
|
|
|
990
1174
|
const storeError = useThreadStoreSelector(store, ɵselectThreadsError);
|
|
991
1175
|
const hasMoreThreads = useThreadStoreSelector(store, ɵselectHasNextPage);
|
|
992
1176
|
const isFetchingMoreThreads = useThreadStoreSelector(store, ɵselectIsFetchingNextPage);
|
|
1177
|
+
const isMutating = useThreadStoreSelector(store, ɵselectIsMutating);
|
|
993
1178
|
const headersKey = useMemo(() => {
|
|
994
1179
|
return JSON.stringify(Object.entries(copilotkit.headers ?? {}).sort(([left], [right]) => left.localeCompare(right)));
|
|
995
1180
|
}, [copilotkit.headers]);
|
|
@@ -1010,9 +1195,16 @@ function useThreads({ agentId, includeArchived, limit }) {
|
|
|
1010
1195
|
return /* @__PURE__ */ new Error("Thread mutations are not available on this CopilotKit runtime");
|
|
1011
1196
|
}, [threadMutationsSupported]);
|
|
1012
1197
|
const [hasDispatchedContext, setHasDispatchedContext] = useState(false);
|
|
1013
|
-
const preConnectLoading = !!copilotkit.runtimeUrl && !threadEndpointsUnavailable && !hasDispatchedContext;
|
|
1014
|
-
const
|
|
1015
|
-
|
|
1198
|
+
const preConnectLoading = enabled && !!copilotkit.runtimeUrl && !threadEndpointsUnavailable && !hasDispatchedContext;
|
|
1199
|
+
const [configErrorDismissed, setConfigErrorDismissed] = useState(false);
|
|
1200
|
+
useEffect(() => {
|
|
1201
|
+
setConfigErrorDismissed(false);
|
|
1202
|
+
}, [runtimeError, threadEndpointsError]);
|
|
1203
|
+
const activeRuntimeError = configErrorDismissed ? null : runtimeError;
|
|
1204
|
+
const activeThreadEndpointsError = configErrorDismissed ? null : threadEndpointsError;
|
|
1205
|
+
const isLoading = activeRuntimeError || activeThreadEndpointsError ? false : preConnectLoading || storeIsLoading;
|
|
1206
|
+
const error = activeRuntimeError ?? activeThreadEndpointsError ?? storeError;
|
|
1207
|
+
const listError = storeError;
|
|
1016
1208
|
useEffect(() => {
|
|
1017
1209
|
store.start();
|
|
1018
1210
|
return () => {
|
|
@@ -1020,6 +1212,7 @@ function useThreads({ agentId, includeArchived, limit }) {
|
|
|
1020
1212
|
};
|
|
1021
1213
|
}, [store]);
|
|
1022
1214
|
useEffect(() => {
|
|
1215
|
+
if (!enabled) return;
|
|
1023
1216
|
copilotkit.registerThreadStore(agentId, store);
|
|
1024
1217
|
return () => {
|
|
1025
1218
|
copilotkit.unregisterThreadStore(agentId);
|
|
@@ -1027,9 +1220,15 @@ function useThreads({ agentId, includeArchived, limit }) {
|
|
|
1027
1220
|
}, [
|
|
1028
1221
|
copilotkit,
|
|
1029
1222
|
agentId,
|
|
1030
|
-
store
|
|
1223
|
+
store,
|
|
1224
|
+
enabled
|
|
1031
1225
|
]);
|
|
1032
1226
|
useEffect(() => {
|
|
1227
|
+
if (!enabled) {
|
|
1228
|
+
store.setContext(null);
|
|
1229
|
+
setHasDispatchedContext(false);
|
|
1230
|
+
return;
|
|
1231
|
+
}
|
|
1033
1232
|
if (!copilotkit.runtimeUrl) {
|
|
1034
1233
|
store.setContext(null);
|
|
1035
1234
|
setHasDispatchedContext(false);
|
|
@@ -1053,6 +1252,7 @@ function useThreads({ agentId, includeArchived, limit }) {
|
|
|
1053
1252
|
setHasDispatchedContext(true);
|
|
1054
1253
|
}, [
|
|
1055
1254
|
store,
|
|
1255
|
+
enabled,
|
|
1056
1256
|
copilotkit.runtimeUrl,
|
|
1057
1257
|
runtimeStatus,
|
|
1058
1258
|
headersKey,
|
|
@@ -1070,16 +1270,25 @@ function useThreads({ agentId, includeArchived, limit }) {
|
|
|
1070
1270
|
}, [threadMutationsError]);
|
|
1071
1271
|
const renameThread = useMemo(() => guardMutation((threadId, name) => store.renameThread(threadId, name)), [store, guardMutation]);
|
|
1072
1272
|
const archiveThread = useMemo(() => guardMutation((threadId) => store.archiveThread(threadId)), [store, guardMutation]);
|
|
1273
|
+
const unarchiveThread = useMemo(() => guardMutation((threadId) => store.unarchiveThread(threadId)), [store, guardMutation]);
|
|
1073
1274
|
const deleteThread = useMemo(() => guardMutation((threadId) => store.deleteThread(threadId)), [store, guardMutation]);
|
|
1074
1275
|
return {
|
|
1075
1276
|
threads,
|
|
1076
1277
|
isLoading,
|
|
1077
1278
|
error,
|
|
1279
|
+
listError,
|
|
1078
1280
|
hasMoreThreads,
|
|
1079
1281
|
isFetchingMoreThreads,
|
|
1282
|
+
isMutating,
|
|
1080
1283
|
fetchMoreThreads: useCallback(() => store.fetchNextPage(), [store]),
|
|
1284
|
+
refetchThreads: useCallback(() => store.refetchThreads(), [store]),
|
|
1285
|
+
startNewThread: useCallback(() => {
|
|
1286
|
+
setConfigErrorDismissed(true);
|
|
1287
|
+
store.startNewThread();
|
|
1288
|
+
}, [store]),
|
|
1081
1289
|
renameThread,
|
|
1082
1290
|
archiveThread,
|
|
1291
|
+
unarchiveThread,
|
|
1083
1292
|
deleteThread
|
|
1084
1293
|
};
|
|
1085
1294
|
}
|
|
@@ -1154,10 +1363,20 @@ function useRenderTool(config, deps) {
|
|
|
1154
1363
|
}) : defineToolCallRenderer({
|
|
1155
1364
|
name: config.name,
|
|
1156
1365
|
args: config.parameters,
|
|
1157
|
-
render: (props) =>
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1366
|
+
render: (props) => {
|
|
1367
|
+
if (props.status === ToolCallStatus.InProgress) return config.render({
|
|
1368
|
+
...props,
|
|
1369
|
+
parameters: props.args
|
|
1370
|
+
});
|
|
1371
|
+
if (props.status === ToolCallStatus.Executing) return config.render({
|
|
1372
|
+
...props,
|
|
1373
|
+
parameters: props.args
|
|
1374
|
+
});
|
|
1375
|
+
return config.render({
|
|
1376
|
+
...props,
|
|
1377
|
+
parameters: props.args
|
|
1378
|
+
});
|
|
1379
|
+
},
|
|
1161
1380
|
...config.agentId ? { agentId: config.agentId } : {}
|
|
1162
1381
|
});
|
|
1163
1382
|
copilotkit.addHookRenderToolCall(renderer);
|