@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.cjs
CHANGED
|
@@ -144,6 +144,25 @@ const CopilotChatDefaultLabels = {
|
|
|
144
144
|
modalHeaderTitle: "CopilotKit Chat",
|
|
145
145
|
welcomeMessageText: "How can I help you today?"
|
|
146
146
|
};
|
|
147
|
+
/**
|
|
148
|
+
* Mobile breakpoint below which the chat modal and the thread-list drawer are
|
|
149
|
+
* mutually exclusive. At or above this width both surfaces may coexist. This
|
|
150
|
+
* mirrors the `(max-width: 767px)` / `(min-width: 768px)` split already used by
|
|
151
|
+
* CopilotChatInput and CopilotSidebarView.
|
|
152
|
+
*/
|
|
153
|
+
const MOBILE_MAX_WIDTH_PX = 767;
|
|
154
|
+
/**
|
|
155
|
+
* Reports whether the current viewport is in the mobile range (`<768px`), where
|
|
156
|
+
* the chat modal and drawer must not be open simultaneously. SSR-safe and
|
|
157
|
+
* defensive against environments without `matchMedia` (treated as desktop, so
|
|
158
|
+
* no mutual-exclusion constraint is applied).
|
|
159
|
+
*
|
|
160
|
+
* @returns `true` when the viewport is mobile-width, `false` otherwise.
|
|
161
|
+
*/
|
|
162
|
+
function isMobileViewport() {
|
|
163
|
+
if (typeof window === "undefined" || typeof window.matchMedia !== "function") return false;
|
|
164
|
+
return window.matchMedia(`(max-width: ${MOBILE_MAX_WIDTH_PX}px)`).matches;
|
|
165
|
+
}
|
|
147
166
|
const CopilotChatConfiguration = (0, react.createContext)(null);
|
|
148
167
|
const CopilotChatConfigurationProvider = ({ children, labels, agentId, threadId, hasExplicitThreadId, isModalDefaultOpen }) => {
|
|
149
168
|
const parentConfig = (0, react.useContext)(CopilotChatConfiguration);
|
|
@@ -154,12 +173,22 @@ const CopilotChatConfigurationProvider = ({ children, labels, agentId, threadId,
|
|
|
154
173
|
...stableLabels
|
|
155
174
|
}), [stableLabels, parentConfig?.labels]);
|
|
156
175
|
const resolvedAgentId = agentId ?? parentConfig?.agentId ?? _copilotkit_shared.DEFAULT_AGENT_ID;
|
|
176
|
+
const threadIdPropIsAuthoritative = threadId !== void 0 && hasExplicitThreadId !== false;
|
|
177
|
+
const isThreadIdControlled = threadIdPropIsAuthoritative;
|
|
178
|
+
const [activeThreadOverride, setActiveThreadOverride] = (0, react.useState)(null);
|
|
157
179
|
const resolvedThreadId = (0, react.useMemo)(() => {
|
|
158
|
-
if (
|
|
180
|
+
if (threadIdPropIsAuthoritative) return threadId;
|
|
181
|
+
if (activeThreadOverride) return activeThreadOverride.threadId;
|
|
159
182
|
if (parentConfig?.threadId) return parentConfig.threadId;
|
|
183
|
+
if (threadId) return threadId;
|
|
160
184
|
return (0, _copilotkit_shared.randomUUID)();
|
|
161
|
-
}, [
|
|
162
|
-
|
|
185
|
+
}, [
|
|
186
|
+
threadIdPropIsAuthoritative,
|
|
187
|
+
threadId,
|
|
188
|
+
parentConfig?.threadId,
|
|
189
|
+
activeThreadOverride
|
|
190
|
+
]);
|
|
191
|
+
const resolvedHasExplicitThreadId = (threadIdPropIsAuthoritative ? true : activeThreadOverride?.explicit ?? hasExplicitThreadId ?? false) || !!parentConfig?.hasExplicitThreadId;
|
|
163
192
|
const [internalModalOpen, setInternalModalOpen] = (0, react.useState)(isModalDefaultOpen ?? true);
|
|
164
193
|
const hasExplicitDefault = isModalDefaultOpen !== void 0;
|
|
165
194
|
const setAndSync = (0, react.useCallback)((open) => {
|
|
@@ -178,20 +207,113 @@ const CopilotChatConfigurationProvider = ({ children, labels, agentId, threadId,
|
|
|
178
207
|
}, [parentConfig?.isModalOpen, hasExplicitDefault]);
|
|
179
208
|
const resolvedIsModalOpen = hasExplicitDefault ? internalModalOpen : parentConfig?.isModalOpen ?? internalModalOpen;
|
|
180
209
|
const resolvedSetModalOpen = hasExplicitDefault ? setAndSync : parentConfig?.setModalOpen ?? setInternalModalOpen;
|
|
210
|
+
const [ownDrawerOpen, setOwnDrawerOpen] = (0, react.useState)(false);
|
|
211
|
+
const [ownDrawerCount, setOwnDrawerCount] = (0, react.useState)(0);
|
|
212
|
+
const modalCloseRef = (0, react.useRef)(() => {});
|
|
213
|
+
modalCloseRef.current = resolvedSetModalOpen;
|
|
214
|
+
const registeredModalClosersRef = (0, react.useRef)([]);
|
|
215
|
+
const ownRegisterModalCloser = (0, react.useCallback)((closeModal) => {
|
|
216
|
+
registeredModalClosersRef.current.push(closeModal);
|
|
217
|
+
return () => {
|
|
218
|
+
registeredModalClosersRef.current = registeredModalClosersRef.current.filter((entry) => entry !== closeModal);
|
|
219
|
+
};
|
|
220
|
+
}, []);
|
|
221
|
+
const ownSetDrawerOpen = (0, react.useCallback)((open) => {
|
|
222
|
+
setOwnDrawerOpen(open);
|
|
223
|
+
if (open && isMobileViewport()) {
|
|
224
|
+
const registered = registeredModalClosersRef.current;
|
|
225
|
+
(registered.length > 0 ? registered[registered.length - 1] : modalCloseRef.current)(false);
|
|
226
|
+
}
|
|
227
|
+
}, []);
|
|
228
|
+
const ownRegisterDrawer = (0, react.useCallback)(() => {
|
|
229
|
+
setOwnDrawerCount((count) => count + 1);
|
|
230
|
+
return () => {
|
|
231
|
+
setOwnDrawerCount((count) => Math.max(0, count - 1));
|
|
232
|
+
};
|
|
233
|
+
}, []);
|
|
234
|
+
const resolvedDrawerOpen = parentConfig ? parentConfig.drawerOpen : ownDrawerOpen;
|
|
235
|
+
const resolvedSetDrawerOpen = parentConfig ? parentConfig.setDrawerOpen : ownSetDrawerOpen;
|
|
236
|
+
const resolvedDrawerRegistered = parentConfig ? parentConfig.drawerRegistered : ownDrawerCount > 0;
|
|
237
|
+
const resolvedRegisterDrawer = parentConfig ? parentConfig.registerDrawer : ownRegisterDrawer;
|
|
238
|
+
const resolvedRegisterModalCloser = parentConfig ? parentConfig.ɵregisterModalCloser : ownRegisterModalCloser;
|
|
239
|
+
(0, react.useEffect)(() => {
|
|
240
|
+
if (!hasExplicitDefault) return;
|
|
241
|
+
return resolvedRegisterModalCloser(resolvedSetModalOpen);
|
|
242
|
+
}, [
|
|
243
|
+
hasExplicitDefault,
|
|
244
|
+
resolvedRegisterModalCloser,
|
|
245
|
+
resolvedSetModalOpen
|
|
246
|
+
]);
|
|
247
|
+
const isThreadIdControlledRef = (0, react.useRef)(isThreadIdControlled);
|
|
248
|
+
isThreadIdControlledRef.current = isThreadIdControlled;
|
|
249
|
+
const ownSetActiveThreadId = (0, react.useCallback)((id, options) => {
|
|
250
|
+
setActiveThreadOverride({
|
|
251
|
+
threadId: id,
|
|
252
|
+
explicit: options?.explicit ?? true
|
|
253
|
+
});
|
|
254
|
+
}, []);
|
|
255
|
+
const ownStartNewThread = (0, react.useCallback)(() => {
|
|
256
|
+
setActiveThreadOverride({
|
|
257
|
+
threadId: (0, _copilotkit_shared.randomUUID)(),
|
|
258
|
+
explicit: false
|
|
259
|
+
});
|
|
260
|
+
}, []);
|
|
261
|
+
const parentSetActiveThreadId = parentConfig?.setActiveThreadId;
|
|
262
|
+
const parentStartNewThread = parentConfig?.startNewThread;
|
|
263
|
+
const resolvedSetActiveThreadId = (0, react.useCallback)((id, options) => {
|
|
264
|
+
if (isThreadIdControlledRef.current) {
|
|
265
|
+
console.warn("[CopilotKit] Ignoring setActiveThreadId(): threadId is controlled via the `threadId` prop on CopilotChatConfigurationProvider.");
|
|
266
|
+
return;
|
|
267
|
+
}
|
|
268
|
+
if (parentSetActiveThreadId) {
|
|
269
|
+
parentSetActiveThreadId(id, options);
|
|
270
|
+
return;
|
|
271
|
+
}
|
|
272
|
+
ownSetActiveThreadId(id, options);
|
|
273
|
+
}, [parentSetActiveThreadId, ownSetActiveThreadId]);
|
|
274
|
+
const resolvedStartNewThread = (0, react.useCallback)(() => {
|
|
275
|
+
if (isThreadIdControlledRef.current) {
|
|
276
|
+
console.warn("[CopilotKit] Ignoring startNewThread(): threadId is controlled via the `threadId` prop on CopilotChatConfigurationProvider.");
|
|
277
|
+
return;
|
|
278
|
+
}
|
|
279
|
+
if (parentStartNewThread) {
|
|
280
|
+
parentStartNewThread();
|
|
281
|
+
return;
|
|
282
|
+
}
|
|
283
|
+
ownStartNewThread();
|
|
284
|
+
}, [parentStartNewThread, ownStartNewThread]);
|
|
285
|
+
const setModalOpenWithDrawerExclusion = (0, react.useCallback)((open) => {
|
|
286
|
+
if (open && isMobileViewport()) resolvedSetDrawerOpen(false);
|
|
287
|
+
resolvedSetModalOpen(open);
|
|
288
|
+
}, [resolvedSetModalOpen, resolvedSetDrawerOpen]);
|
|
181
289
|
const configurationValue = (0, react.useMemo)(() => ({
|
|
182
290
|
labels: mergedLabels,
|
|
183
291
|
agentId: resolvedAgentId,
|
|
184
292
|
threadId: resolvedThreadId,
|
|
185
293
|
hasExplicitThreadId: resolvedHasExplicitThreadId,
|
|
186
294
|
isModalOpen: resolvedIsModalOpen,
|
|
187
|
-
setModalOpen:
|
|
295
|
+
setModalOpen: setModalOpenWithDrawerExclusion,
|
|
296
|
+
drawerOpen: resolvedDrawerOpen,
|
|
297
|
+
setDrawerOpen: resolvedSetDrawerOpen,
|
|
298
|
+
drawerRegistered: resolvedDrawerRegistered,
|
|
299
|
+
registerDrawer: resolvedRegisterDrawer,
|
|
300
|
+
ɵregisterModalCloser: resolvedRegisterModalCloser,
|
|
301
|
+
setActiveThreadId: resolvedSetActiveThreadId,
|
|
302
|
+
startNewThread: resolvedStartNewThread
|
|
188
303
|
}), [
|
|
189
304
|
mergedLabels,
|
|
190
305
|
resolvedAgentId,
|
|
191
306
|
resolvedThreadId,
|
|
192
307
|
resolvedHasExplicitThreadId,
|
|
193
308
|
resolvedIsModalOpen,
|
|
194
|
-
|
|
309
|
+
setModalOpenWithDrawerExclusion,
|
|
310
|
+
resolvedDrawerOpen,
|
|
311
|
+
resolvedSetDrawerOpen,
|
|
312
|
+
resolvedDrawerRegistered,
|
|
313
|
+
resolvedRegisterDrawer,
|
|
314
|
+
resolvedRegisterModalCloser,
|
|
315
|
+
resolvedSetActiveThreadId,
|
|
316
|
+
resolvedStartNewThread
|
|
195
317
|
]);
|
|
196
318
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CopilotChatConfiguration.Provider, {
|
|
197
319
|
value: configurationValue,
|
|
@@ -233,7 +355,7 @@ function useAgent({ agentId, updates, throttleMs } = {}) {
|
|
|
233
355
|
if (isRuntimeConfigured && (status === _copilotkit_core.CopilotKitCoreRuntimeConnectionStatus.Disconnected || status === _copilotkit_core.CopilotKitCoreRuntimeConnectionStatus.Connecting)) {
|
|
234
356
|
const cached = provisionalAgentCache.current.get(agentId);
|
|
235
357
|
if (cached) {
|
|
236
|
-
|
|
358
|
+
copilotkit.applyHeadersToAgent(cached);
|
|
237
359
|
return cached;
|
|
238
360
|
}
|
|
239
361
|
const provisional = new _copilotkit_core.ProxiedCopilotRuntimeAgent({
|
|
@@ -242,14 +364,14 @@ function useAgent({ agentId, updates, throttleMs } = {}) {
|
|
|
242
364
|
transport: copilotkit.runtimeTransport,
|
|
243
365
|
runtimeMode: "pending"
|
|
244
366
|
});
|
|
245
|
-
|
|
367
|
+
copilotkit.applyHeadersToAgent(provisional);
|
|
246
368
|
provisionalAgentCache.current.set(agentId, provisional);
|
|
247
369
|
return provisional;
|
|
248
370
|
}
|
|
249
371
|
if (isRuntimeConfigured && status === _copilotkit_core.CopilotKitCoreRuntimeConnectionStatus.Error) {
|
|
250
372
|
const cached = provisionalAgentCache.current.get(agentId);
|
|
251
373
|
if (cached) {
|
|
252
|
-
|
|
374
|
+
copilotkit.applyHeadersToAgent(cached);
|
|
253
375
|
return cached;
|
|
254
376
|
}
|
|
255
377
|
const provisional = new _copilotkit_core.ProxiedCopilotRuntimeAgent({
|
|
@@ -258,7 +380,7 @@ function useAgent({ agentId, updates, throttleMs } = {}) {
|
|
|
258
380
|
transport: copilotkit.runtimeTransport,
|
|
259
381
|
runtimeMode: "pending"
|
|
260
382
|
});
|
|
261
|
-
|
|
383
|
+
copilotkit.applyHeadersToAgent(provisional);
|
|
262
384
|
provisionalAgentCache.current.set(agentId, provisional);
|
|
263
385
|
return provisional;
|
|
264
386
|
}
|
|
@@ -309,7 +431,7 @@ function useAgent({ agentId, updates, throttleMs } = {}) {
|
|
|
309
431
|
updateFlags
|
|
310
432
|
]);
|
|
311
433
|
(0, react.useEffect)(() => {
|
|
312
|
-
if (agent instanceof _ag_ui_client.HttpAgent)
|
|
434
|
+
if (agent instanceof _ag_ui_client.HttpAgent) copilotkit.applyHeadersToAgent(agent);
|
|
313
435
|
}, [agent, JSON.stringify(copilotkit.headers)]);
|
|
314
436
|
const chatConfig = useCopilotChatConfiguration();
|
|
315
437
|
const configThreadId = chatConfig?.threadId;
|
|
@@ -521,114 +643,165 @@ const INTERRUPT_EVENT_NAME = "on_interrupt";
|
|
|
521
643
|
function isPromiseLike(value) {
|
|
522
644
|
return (typeof value === "object" || typeof value === "function") && value !== null && typeof Reflect.get(value, "then") === "function";
|
|
523
645
|
}
|
|
646
|
+
/** Derive the legacy-compatible `event` for any pending interrupt. */
|
|
647
|
+
function toLegacyEvent(pending) {
|
|
648
|
+
if (pending.kind === "legacy") return pending.event;
|
|
649
|
+
return {
|
|
650
|
+
name: INTERRUPT_EVENT_NAME,
|
|
651
|
+
value: pending.interrupts[0]
|
|
652
|
+
};
|
|
653
|
+
}
|
|
524
654
|
/**
|
|
525
|
-
* Handles agent interrupts
|
|
526
|
-
*
|
|
527
|
-
* The hook listens to custom events on the active agent, stores interrupt payloads per run,
|
|
528
|
-
* and surfaces a render callback once the run finalizes. Call `resolve` from your UI to resume
|
|
529
|
-
* execution with user-provided data.
|
|
530
|
-
*
|
|
531
|
-
* - `renderInChat: true` (default): the element is published into `<CopilotChat>` and this hook returns `void`.
|
|
532
|
-
* - `renderInChat: false`: the hook returns the interrupt element so you can place it anywhere in your component tree.
|
|
655
|
+
* Handles agent interrupts with optional filtering, preprocessing, and resume behavior.
|
|
533
656
|
*
|
|
534
|
-
*
|
|
535
|
-
*
|
|
657
|
+
* Supports both the AG-UI standard interrupt flow (`RUN_FINISHED` with
|
|
658
|
+
* `outcome.type === "interrupt"`) and the legacy custom-event flow
|
|
659
|
+
* (`on_interrupt`). For standard interrupts, `render` receives `interrupt`
|
|
660
|
+
* (the primary one) and `interrupts` (the full open set); call `resolve(payload)`
|
|
661
|
+
* to resume or `cancel()` to cancel. Resuming addresses the targeted interrupt
|
|
662
|
+
* and, once every open interrupt is addressed, submits a single spec `resume`
|
|
663
|
+
* array via `copilotkit.runAgent`.
|
|
536
664
|
*
|
|
537
|
-
*
|
|
538
|
-
*
|
|
539
|
-
* @returns When `renderInChat` is `false`, returns the interrupt element (or `null` when idle).
|
|
540
|
-
* Otherwise returns `void` and publishes the element into chat. In `render`, `result` is always
|
|
541
|
-
* either the handler's resolved return value or `null` (including when no handler is provided,
|
|
542
|
-
* when filtering skips the interrupt, or when handler execution fails).
|
|
665
|
+
* - `renderInChat: true` (default): the element is published into `<CopilotChat>`; returns `void`.
|
|
666
|
+
* - `renderInChat: false`: the hook returns the interrupt element for manual placement.
|
|
543
667
|
*
|
|
544
668
|
* @example
|
|
545
669
|
* ```tsx
|
|
546
|
-
*
|
|
547
|
-
*
|
|
548
|
-
*
|
|
549
|
-
*
|
|
550
|
-
*
|
|
551
|
-
* <
|
|
552
|
-
*
|
|
553
|
-
*
|
|
554
|
-
*
|
|
555
|
-
* </div>
|
|
556
|
-
* ),
|
|
557
|
-
* });
|
|
558
|
-
*
|
|
559
|
-
* return null;
|
|
560
|
-
* }
|
|
561
|
-
* ```
|
|
562
|
-
*
|
|
563
|
-
* @example
|
|
564
|
-
* ```tsx
|
|
565
|
-
* import { useInterrupt } from "@copilotkit/react-core/v2";
|
|
566
|
-
*
|
|
567
|
-
* function CustomPanel() {
|
|
568
|
-
* const interruptElement = useInterrupt({
|
|
569
|
-
* renderInChat: false,
|
|
570
|
-
* enabled: (event) => event.value.startsWith("approval:"),
|
|
571
|
-
* handler: async ({ event }) => ({ label: event.value.toUpperCase() }),
|
|
572
|
-
* render: ({ event, result, resolve }) => (
|
|
573
|
-
* <aside>
|
|
574
|
-
* <strong>{result?.label ?? ""}</strong>
|
|
575
|
-
* <button onClick={() => resolve({ value: event.value })}>Continue</button>
|
|
576
|
-
* </aside>
|
|
577
|
-
* ),
|
|
578
|
-
* });
|
|
579
|
-
*
|
|
580
|
-
* return <>{interruptElement}</>;
|
|
581
|
-
* }
|
|
670
|
+
* useInterrupt({
|
|
671
|
+
* render: ({ interrupt, resolve, cancel }) => (
|
|
672
|
+
* <div>
|
|
673
|
+
* <p>{interrupt?.message}</p>
|
|
674
|
+
* <button onClick={() => resolve({ approved: true })}>Approve</button>
|
|
675
|
+
* <button onClick={() => cancel()}>Cancel</button>
|
|
676
|
+
* </div>
|
|
677
|
+
* ),
|
|
678
|
+
* });
|
|
582
679
|
* ```
|
|
583
680
|
*/
|
|
584
681
|
function useInterrupt(config) {
|
|
585
682
|
const { copilotkit } = (0, _copilotkit_react_core_v2_context.useCopilotKit)();
|
|
586
683
|
const { agent } = useAgent({ agentId: config.agentId });
|
|
587
|
-
const [
|
|
588
|
-
const
|
|
589
|
-
|
|
684
|
+
const [pending, setPending] = (0, react.useState)(null);
|
|
685
|
+
const pendingRef = (0, react.useRef)(pending);
|
|
686
|
+
pendingRef.current = pending;
|
|
590
687
|
const [handlerResult, setHandlerResult] = (0, react.useState)(null);
|
|
688
|
+
const responsesRef = (0, react.useRef)({});
|
|
591
689
|
(0, react.useEffect)(() => {
|
|
592
|
-
let
|
|
690
|
+
let localLegacy = null;
|
|
691
|
+
let localStandard = null;
|
|
593
692
|
const subscription = agent.subscribe({
|
|
594
693
|
onCustomEvent: ({ event }) => {
|
|
595
|
-
if (event.name === INTERRUPT_EVENT_NAME)
|
|
694
|
+
if (event.name === INTERRUPT_EVENT_NAME) localLegacy = {
|
|
596
695
|
name: event.name,
|
|
597
696
|
value: event.value
|
|
598
697
|
};
|
|
599
698
|
},
|
|
699
|
+
onRunFinishedEvent: (params) => {
|
|
700
|
+
if (params.outcome === "interrupt") localStandard = params.interrupts;
|
|
701
|
+
},
|
|
600
702
|
onRunStartedEvent: () => {
|
|
601
|
-
|
|
602
|
-
|
|
703
|
+
localLegacy = null;
|
|
704
|
+
localStandard = null;
|
|
705
|
+
responsesRef.current = {};
|
|
706
|
+
setPending(null);
|
|
603
707
|
},
|
|
604
708
|
onRunFinalized: () => {
|
|
605
|
-
if (
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
}
|
|
709
|
+
if (localStandard && localStandard.length > 0) setPending({
|
|
710
|
+
kind: "standard",
|
|
711
|
+
interrupts: localStandard
|
|
712
|
+
});
|
|
713
|
+
else if (localLegacy) setPending({
|
|
714
|
+
kind: "legacy",
|
|
715
|
+
event: localLegacy
|
|
716
|
+
});
|
|
717
|
+
localLegacy = null;
|
|
718
|
+
localStandard = null;
|
|
609
719
|
},
|
|
610
720
|
onRunFailed: () => {
|
|
611
|
-
|
|
612
|
-
|
|
721
|
+
localLegacy = null;
|
|
722
|
+
localStandard = null;
|
|
723
|
+
responsesRef.current = {};
|
|
724
|
+
setPending(null);
|
|
613
725
|
}
|
|
614
726
|
});
|
|
615
727
|
return () => subscription.unsubscribe();
|
|
616
728
|
}, [agent]);
|
|
617
|
-
const
|
|
729
|
+
const submitStandardIfComplete = (0, react.useCallback)(async (interrupts) => {
|
|
730
|
+
if (!interrupts.every((i) => responsesRef.current[i.id])) return;
|
|
731
|
+
const expired = interrupts.find((i) => (0, _ag_ui_client.isInterruptExpired)(i));
|
|
732
|
+
if (expired) {
|
|
733
|
+
console.error(`[CopilotKit] useInterrupt: interrupt ${expired.id} expired at ${expired.expiresAt}; not resuming.`);
|
|
734
|
+
responsesRef.current = {};
|
|
735
|
+
setPending(null);
|
|
736
|
+
return;
|
|
737
|
+
}
|
|
738
|
+
const resume = (0, _ag_ui_client.buildResumeArray)(interrupts, responsesRef.current);
|
|
739
|
+
for (const i of interrupts) {
|
|
740
|
+
if (!i.toolCallId) continue;
|
|
741
|
+
const response = responsesRef.current[i.id];
|
|
742
|
+
const content = response.status === "cancelled" ? { status: "cancelled" } : response.payload ?? { status: "resolved" };
|
|
743
|
+
agent.addMessage({
|
|
744
|
+
id: (0, _ag_ui_client.randomUUID)(),
|
|
745
|
+
role: "tool",
|
|
746
|
+
toolCallId: i.toolCallId,
|
|
747
|
+
content: JSON.stringify(content)
|
|
748
|
+
});
|
|
749
|
+
}
|
|
750
|
+
responsesRef.current = {};
|
|
618
751
|
try {
|
|
752
|
+
return await copilotkit.runAgent({
|
|
753
|
+
agent,
|
|
754
|
+
resume
|
|
755
|
+
});
|
|
756
|
+
} catch (err) {
|
|
757
|
+
console.error("[CopilotKit] useInterrupt resolve: runAgent rejected; clearing pending + rethrowing", err);
|
|
758
|
+
setPending(null);
|
|
759
|
+
throw err;
|
|
760
|
+
}
|
|
761
|
+
}, [agent, copilotkit]);
|
|
762
|
+
const resolve = (0, react.useCallback)(async (payload, interruptId) => {
|
|
763
|
+
const current = pendingRef.current;
|
|
764
|
+
if (!current) return;
|
|
765
|
+
if (current.kind === "legacy") try {
|
|
619
766
|
return await copilotkit.runAgent({
|
|
620
767
|
agent,
|
|
621
768
|
forwardedProps: { command: {
|
|
622
|
-
resume:
|
|
623
|
-
interruptEvent:
|
|
769
|
+
resume: payload,
|
|
770
|
+
interruptEvent: current.event.value
|
|
624
771
|
} }
|
|
625
772
|
});
|
|
626
773
|
} catch (err) {
|
|
627
774
|
console.error("[CopilotKit] useInterrupt resolve: runAgent rejected; clearing pending + rethrowing", err);
|
|
628
|
-
|
|
775
|
+
setPending(null);
|
|
629
776
|
throw err;
|
|
630
777
|
}
|
|
631
|
-
|
|
778
|
+
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.`);
|
|
779
|
+
const id = interruptId ?? current.interrupts[0]?.id;
|
|
780
|
+
if (!id) return;
|
|
781
|
+
responsesRef.current[id] = {
|
|
782
|
+
status: "resolved",
|
|
783
|
+
payload
|
|
784
|
+
};
|
|
785
|
+
return submitStandardIfComplete(current.interrupts);
|
|
786
|
+
}, [
|
|
787
|
+
agent,
|
|
788
|
+
copilotkit,
|
|
789
|
+
submitStandardIfComplete
|
|
790
|
+
]);
|
|
791
|
+
const cancel = (0, react.useCallback)(async (interruptId) => {
|
|
792
|
+
const current = pendingRef.current;
|
|
793
|
+
if (!current) return;
|
|
794
|
+
if (current.kind === "legacy") {
|
|
795
|
+
console.warn("[CopilotKit] useInterrupt: cancel() is not supported for legacy on_interrupt interrupts; dismissing.");
|
|
796
|
+
setPending(null);
|
|
797
|
+
return;
|
|
798
|
+
}
|
|
799
|
+
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.`);
|
|
800
|
+
const id = interruptId ?? current.interrupts[0]?.id;
|
|
801
|
+
if (!id) return;
|
|
802
|
+
responsesRef.current[id] = { status: "cancelled" };
|
|
803
|
+
return submitStandardIfComplete(current.interrupts);
|
|
804
|
+
}, [submitStandardIfComplete]);
|
|
632
805
|
const renderRef = (0, react.useRef)(config.render);
|
|
633
806
|
renderRef.current = config.render;
|
|
634
807
|
const enabledRef = (0, react.useRef)(config.enabled);
|
|
@@ -637,6 +810,8 @@ function useInterrupt(config) {
|
|
|
637
810
|
handlerRef.current = config.handler;
|
|
638
811
|
const resolveRef = (0, react.useRef)(resolve);
|
|
639
812
|
resolveRef.current = resolve;
|
|
813
|
+
const cancelRef = (0, react.useRef)(cancel);
|
|
814
|
+
cancelRef.current = cancel;
|
|
640
815
|
const isEnabled = (event) => {
|
|
641
816
|
const predicate = enabledRef.current;
|
|
642
817
|
if (!predicate) return true;
|
|
@@ -648,11 +823,12 @@ function useInterrupt(config) {
|
|
|
648
823
|
}
|
|
649
824
|
};
|
|
650
825
|
(0, react.useEffect)(() => {
|
|
651
|
-
if (!
|
|
826
|
+
if (!pending) {
|
|
652
827
|
setHandlerResult(null);
|
|
653
828
|
return;
|
|
654
829
|
}
|
|
655
|
-
|
|
830
|
+
const legacyEvent = toLegacyEvent(pending);
|
|
831
|
+
if (!isEnabled(legacyEvent)) {
|
|
656
832
|
setHandlerResult(null);
|
|
657
833
|
return;
|
|
658
834
|
}
|
|
@@ -665,8 +841,11 @@ function useInterrupt(config) {
|
|
|
665
841
|
let maybePromise;
|
|
666
842
|
try {
|
|
667
843
|
maybePromise = handler({
|
|
668
|
-
event:
|
|
669
|
-
|
|
844
|
+
event: legacyEvent,
|
|
845
|
+
interrupt: pending.kind === "standard" ? pending.interrupts[0] : null,
|
|
846
|
+
interrupts: pending.kind === "standard" ? pending.interrupts : [],
|
|
847
|
+
resolve: resolveRef.current,
|
|
848
|
+
cancel: cancelRef.current
|
|
670
849
|
});
|
|
671
850
|
} catch (err) {
|
|
672
851
|
console.error("[CopilotKit] useInterrupt handler threw; result will be null:", err);
|
|
@@ -685,19 +864,24 @@ function useInterrupt(config) {
|
|
|
685
864
|
return () => {
|
|
686
865
|
cancelled = true;
|
|
687
866
|
};
|
|
688
|
-
}, [
|
|
867
|
+
}, [pending]);
|
|
689
868
|
const element = (0, react.useMemo)(() => {
|
|
690
|
-
if (!
|
|
691
|
-
|
|
869
|
+
if (!pending) return null;
|
|
870
|
+
const legacyEvent = toLegacyEvent(pending);
|
|
871
|
+
if (!isEnabled(legacyEvent)) return null;
|
|
692
872
|
return renderRef.current({
|
|
693
|
-
event:
|
|
873
|
+
event: legacyEvent,
|
|
874
|
+
interrupt: pending.kind === "standard" ? pending.interrupts[0] : null,
|
|
875
|
+
interrupts: pending.kind === "standard" ? pending.interrupts : [],
|
|
694
876
|
result: handlerResult,
|
|
695
|
-
resolve
|
|
877
|
+
resolve,
|
|
878
|
+
cancel
|
|
696
879
|
});
|
|
697
880
|
}, [
|
|
698
|
-
|
|
881
|
+
pending,
|
|
699
882
|
handlerResult,
|
|
700
|
-
resolve
|
|
883
|
+
resolve,
|
|
884
|
+
cancel
|
|
701
885
|
]);
|
|
702
886
|
(0, react.useEffect)(() => {
|
|
703
887
|
if (config.renderInChat === false) return;
|
|
@@ -959,7 +1143,7 @@ function useThreadStoreSelector(store, selector) {
|
|
|
959
1143
|
return (0, react.useSyncExternalStore)((0, react.useCallback)((onStoreChange) => {
|
|
960
1144
|
const subscription = store.select(selector).subscribe(onStoreChange);
|
|
961
1145
|
return () => subscription.unsubscribe();
|
|
962
|
-
}, [store, selector]), () => selector(store.getState()));
|
|
1146
|
+
}, [store, selector]), () => selector(store.getState()), () => selector(store.getServerState()));
|
|
963
1147
|
}
|
|
964
1148
|
/**
|
|
965
1149
|
* React hook for listing and managing Intelligence platform threads.
|
|
@@ -970,9 +1154,9 @@ function useThreadStoreSelector(store, selector) {
|
|
|
970
1154
|
* current without polling — thread creates, renames, archives, and deletes
|
|
971
1155
|
* from any client are reflected immediately.
|
|
972
1156
|
*
|
|
973
|
-
* Mutation methods (`renameThread`, `archiveThread`, `
|
|
974
|
-
* promises that resolve once the platform confirms the
|
|
975
|
-
* with an `Error` on failure.
|
|
1157
|
+
* Mutation methods (`renameThread`, `archiveThread`, `unarchiveThread`,
|
|
1158
|
+
* `deleteThread`) return promises that resolve once the platform confirms the
|
|
1159
|
+
* operation and reject with an `Error` on failure.
|
|
976
1160
|
*
|
|
977
1161
|
* @param input - Agent identifier and optional list controls.
|
|
978
1162
|
* @returns Thread list state and stable mutation callbacks.
|
|
@@ -1002,7 +1186,7 @@ function useThreadStoreSelector(store, selector) {
|
|
|
1002
1186
|
* }
|
|
1003
1187
|
* ```
|
|
1004
1188
|
*/
|
|
1005
|
-
function useThreads({ agentId, includeArchived, limit }) {
|
|
1189
|
+
function useThreads({ agentId, includeArchived, limit, enabled = true }) {
|
|
1006
1190
|
const { copilotkit } = (0, _copilotkit_react_core_v2_context.useCopilotKit)();
|
|
1007
1191
|
const [store] = (0, react.useState)(() => (0, _copilotkit_core.ɵcreateThreadStore)({ fetch: globalThis.fetch }));
|
|
1008
1192
|
const coreThreads = useThreadStoreSelector(store, _copilotkit_core.ɵselectThreads);
|
|
@@ -1019,6 +1203,7 @@ function useThreads({ agentId, includeArchived, limit }) {
|
|
|
1019
1203
|
const storeError = useThreadStoreSelector(store, _copilotkit_core.ɵselectThreadsError);
|
|
1020
1204
|
const hasMoreThreads = useThreadStoreSelector(store, _copilotkit_core.ɵselectHasNextPage);
|
|
1021
1205
|
const isFetchingMoreThreads = useThreadStoreSelector(store, _copilotkit_core.ɵselectIsFetchingNextPage);
|
|
1206
|
+
const isMutating = useThreadStoreSelector(store, _copilotkit_core.ɵselectIsMutating);
|
|
1022
1207
|
const headersKey = (0, react.useMemo)(() => {
|
|
1023
1208
|
return JSON.stringify(Object.entries(copilotkit.headers ?? {}).sort(([left], [right]) => left.localeCompare(right)));
|
|
1024
1209
|
}, [copilotkit.headers]);
|
|
@@ -1039,9 +1224,16 @@ function useThreads({ agentId, includeArchived, limit }) {
|
|
|
1039
1224
|
return /* @__PURE__ */ new Error("Thread mutations are not available on this CopilotKit runtime");
|
|
1040
1225
|
}, [threadMutationsSupported]);
|
|
1041
1226
|
const [hasDispatchedContext, setHasDispatchedContext] = (0, react.useState)(false);
|
|
1042
|
-
const preConnectLoading = !!copilotkit.runtimeUrl && !threadEndpointsUnavailable && !hasDispatchedContext;
|
|
1043
|
-
const
|
|
1044
|
-
|
|
1227
|
+
const preConnectLoading = enabled && !!copilotkit.runtimeUrl && !threadEndpointsUnavailable && !hasDispatchedContext;
|
|
1228
|
+
const [configErrorDismissed, setConfigErrorDismissed] = (0, react.useState)(false);
|
|
1229
|
+
(0, react.useEffect)(() => {
|
|
1230
|
+
setConfigErrorDismissed(false);
|
|
1231
|
+
}, [runtimeError, threadEndpointsError]);
|
|
1232
|
+
const activeRuntimeError = configErrorDismissed ? null : runtimeError;
|
|
1233
|
+
const activeThreadEndpointsError = configErrorDismissed ? null : threadEndpointsError;
|
|
1234
|
+
const isLoading = activeRuntimeError || activeThreadEndpointsError ? false : preConnectLoading || storeIsLoading;
|
|
1235
|
+
const error = activeRuntimeError ?? activeThreadEndpointsError ?? storeError;
|
|
1236
|
+
const listError = storeError;
|
|
1045
1237
|
(0, react.useEffect)(() => {
|
|
1046
1238
|
store.start();
|
|
1047
1239
|
return () => {
|
|
@@ -1049,6 +1241,7 @@ function useThreads({ agentId, includeArchived, limit }) {
|
|
|
1049
1241
|
};
|
|
1050
1242
|
}, [store]);
|
|
1051
1243
|
(0, react.useEffect)(() => {
|
|
1244
|
+
if (!enabled) return;
|
|
1052
1245
|
copilotkit.registerThreadStore(agentId, store);
|
|
1053
1246
|
return () => {
|
|
1054
1247
|
copilotkit.unregisterThreadStore(agentId);
|
|
@@ -1056,9 +1249,15 @@ function useThreads({ agentId, includeArchived, limit }) {
|
|
|
1056
1249
|
}, [
|
|
1057
1250
|
copilotkit,
|
|
1058
1251
|
agentId,
|
|
1059
|
-
store
|
|
1252
|
+
store,
|
|
1253
|
+
enabled
|
|
1060
1254
|
]);
|
|
1061
1255
|
(0, react.useEffect)(() => {
|
|
1256
|
+
if (!enabled) {
|
|
1257
|
+
store.setContext(null);
|
|
1258
|
+
setHasDispatchedContext(false);
|
|
1259
|
+
return;
|
|
1260
|
+
}
|
|
1062
1261
|
if (!copilotkit.runtimeUrl) {
|
|
1063
1262
|
store.setContext(null);
|
|
1064
1263
|
setHasDispatchedContext(false);
|
|
@@ -1082,6 +1281,7 @@ function useThreads({ agentId, includeArchived, limit }) {
|
|
|
1082
1281
|
setHasDispatchedContext(true);
|
|
1083
1282
|
}, [
|
|
1084
1283
|
store,
|
|
1284
|
+
enabled,
|
|
1085
1285
|
copilotkit.runtimeUrl,
|
|
1086
1286
|
runtimeStatus,
|
|
1087
1287
|
headersKey,
|
|
@@ -1099,16 +1299,25 @@ function useThreads({ agentId, includeArchived, limit }) {
|
|
|
1099
1299
|
}, [threadMutationsError]);
|
|
1100
1300
|
const renameThread = (0, react.useMemo)(() => guardMutation((threadId, name) => store.renameThread(threadId, name)), [store, guardMutation]);
|
|
1101
1301
|
const archiveThread = (0, react.useMemo)(() => guardMutation((threadId) => store.archiveThread(threadId)), [store, guardMutation]);
|
|
1302
|
+
const unarchiveThread = (0, react.useMemo)(() => guardMutation((threadId) => store.unarchiveThread(threadId)), [store, guardMutation]);
|
|
1102
1303
|
const deleteThread = (0, react.useMemo)(() => guardMutation((threadId) => store.deleteThread(threadId)), [store, guardMutation]);
|
|
1103
1304
|
return {
|
|
1104
1305
|
threads,
|
|
1105
1306
|
isLoading,
|
|
1106
1307
|
error,
|
|
1308
|
+
listError,
|
|
1107
1309
|
hasMoreThreads,
|
|
1108
1310
|
isFetchingMoreThreads,
|
|
1311
|
+
isMutating,
|
|
1109
1312
|
fetchMoreThreads: (0, react.useCallback)(() => store.fetchNextPage(), [store]),
|
|
1313
|
+
refetchThreads: (0, react.useCallback)(() => store.refetchThreads(), [store]),
|
|
1314
|
+
startNewThread: (0, react.useCallback)(() => {
|
|
1315
|
+
setConfigErrorDismissed(true);
|
|
1316
|
+
store.startNewThread();
|
|
1317
|
+
}, [store]),
|
|
1110
1318
|
renameThread,
|
|
1111
1319
|
archiveThread,
|
|
1320
|
+
unarchiveThread,
|
|
1112
1321
|
deleteThread
|
|
1113
1322
|
};
|
|
1114
1323
|
}
|
|
@@ -1183,10 +1392,20 @@ function useRenderTool(config, deps) {
|
|
|
1183
1392
|
}) : defineToolCallRenderer({
|
|
1184
1393
|
name: config.name,
|
|
1185
1394
|
args: config.parameters,
|
|
1186
|
-
render: (props) =>
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1395
|
+
render: (props) => {
|
|
1396
|
+
if (props.status === _copilotkit_core.ToolCallStatus.InProgress) return config.render({
|
|
1397
|
+
...props,
|
|
1398
|
+
parameters: props.args
|
|
1399
|
+
});
|
|
1400
|
+
if (props.status === _copilotkit_core.ToolCallStatus.Executing) return config.render({
|
|
1401
|
+
...props,
|
|
1402
|
+
parameters: props.args
|
|
1403
|
+
});
|
|
1404
|
+
return config.render({
|
|
1405
|
+
...props,
|
|
1406
|
+
parameters: props.args
|
|
1407
|
+
});
|
|
1408
|
+
},
|
|
1190
1409
|
...config.agentId ? { agentId: config.agentId } : {}
|
|
1191
1410
|
});
|
|
1192
1411
|
copilotkit.addHookRenderToolCall(renderer);
|