@copilotkit/react-core 1.61.0 → 1.61.2
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-BCxdKlMw.mjs → copilotkit-Be-o2UaU.mjs} +262 -128
- package/dist/copilotkit-Be-o2UaU.mjs.map +1 -0
- package/dist/{copilotkit-M1FiciGd.d.mts → copilotkit-Bpt1c_-q.d.mts} +79 -71
- package/dist/copilotkit-Bpt1c_-q.d.mts.map +1 -0
- package/dist/{copilotkit-DtPCrXXd.cjs → copilotkit-CTCjVxkH.cjs} +251 -117
- package/dist/copilotkit-CTCjVxkH.cjs.map +1 -0
- package/dist/{copilotkit-CEdu_aie.d.cts → copilotkit-ClqbUuGX.d.cts} +79 -71
- package/dist/copilotkit-ClqbUuGX.d.cts.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 +188 -96
- 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 +219 -102
- package/dist/v2/headless.cjs.map +1 -1
- package/dist/v2/headless.d.cts +76 -68
- package/dist/v2/headless.d.cts.map +1 -1
- package/dist/v2/headless.d.mts +76 -68
- package/dist/v2/headless.d.mts.map +1 -1
- package/dist/v2/headless.mjs +220 -103
- package/dist/v2/headless.mjs.map +1 -1
- package/dist/v2/index.cjs +1 -1
- package/dist/v2/index.d.cts +2 -2
- package/dist/v2/index.d.mts +2 -2
- package/dist/v2/index.mjs +1 -1
- package/dist/v2/index.umd.js +250 -116
- package/dist/v2/index.umd.js.map +1 -1
- package/package.json +6 -6
- package/skills/react-core/references/provider-setup.md +20 -2
- package/dist/copilotkit-BCxdKlMw.mjs.map +0 -1
- package/dist/copilotkit-CEdu_aie.d.cts.map +0 -1
- package/dist/copilotkit-DtPCrXXd.cjs.map +0 -1
- package/dist/copilotkit-M1FiciGd.d.mts.map +0 -1
package/dist/v2/headless.mjs
CHANGED
|
@@ -3,7 +3,7 @@ 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";
|
|
6
|
+
import { HttpAgent, buildResumeArray, isInterruptExpired, randomUUID as randomUUID$1 } from "@ag-ui/client";
|
|
7
7
|
import { CopilotKitCoreRuntimeConnectionStatus, ProxiedCopilotRuntimeAgent, ToolCallStatus, ɵcreateThreadStore, ɵselectHasNextPage, ɵselectIsFetchingNextPage, ɵselectThreads, ɵselectThreadsError, ɵselectThreadsIsLoading } from "@copilotkit/core";
|
|
8
8
|
import { z } from "zod";
|
|
9
9
|
|
|
@@ -204,7 +204,7 @@ function useAgent({ agentId, updates, throttleMs } = {}) {
|
|
|
204
204
|
if (isRuntimeConfigured && (status === CopilotKitCoreRuntimeConnectionStatus.Disconnected || status === CopilotKitCoreRuntimeConnectionStatus.Connecting)) {
|
|
205
205
|
const cached = provisionalAgentCache.current.get(agentId);
|
|
206
206
|
if (cached) {
|
|
207
|
-
|
|
207
|
+
copilotkit.applyHeadersToAgent(cached);
|
|
208
208
|
return cached;
|
|
209
209
|
}
|
|
210
210
|
const provisional = new ProxiedCopilotRuntimeAgent({
|
|
@@ -213,14 +213,14 @@ function useAgent({ agentId, updates, throttleMs } = {}) {
|
|
|
213
213
|
transport: copilotkit.runtimeTransport,
|
|
214
214
|
runtimeMode: "pending"
|
|
215
215
|
});
|
|
216
|
-
|
|
216
|
+
copilotkit.applyHeadersToAgent(provisional);
|
|
217
217
|
provisionalAgentCache.current.set(agentId, provisional);
|
|
218
218
|
return provisional;
|
|
219
219
|
}
|
|
220
220
|
if (isRuntimeConfigured && status === CopilotKitCoreRuntimeConnectionStatus.Error) {
|
|
221
221
|
const cached = provisionalAgentCache.current.get(agentId);
|
|
222
222
|
if (cached) {
|
|
223
|
-
|
|
223
|
+
copilotkit.applyHeadersToAgent(cached);
|
|
224
224
|
return cached;
|
|
225
225
|
}
|
|
226
226
|
const provisional = new ProxiedCopilotRuntimeAgent({
|
|
@@ -229,7 +229,7 @@ function useAgent({ agentId, updates, throttleMs } = {}) {
|
|
|
229
229
|
transport: copilotkit.runtimeTransport,
|
|
230
230
|
runtimeMode: "pending"
|
|
231
231
|
});
|
|
232
|
-
|
|
232
|
+
copilotkit.applyHeadersToAgent(provisional);
|
|
233
233
|
provisionalAgentCache.current.set(agentId, provisional);
|
|
234
234
|
return provisional;
|
|
235
235
|
}
|
|
@@ -280,7 +280,7 @@ function useAgent({ agentId, updates, throttleMs } = {}) {
|
|
|
280
280
|
updateFlags
|
|
281
281
|
]);
|
|
282
282
|
useEffect(() => {
|
|
283
|
-
if (agent instanceof HttpAgent)
|
|
283
|
+
if (agent instanceof HttpAgent) copilotkit.applyHeadersToAgent(agent);
|
|
284
284
|
}, [agent, JSON.stringify(copilotkit.headers)]);
|
|
285
285
|
const chatConfig = useCopilotChatConfiguration();
|
|
286
286
|
const configThreadId = chatConfig?.threadId;
|
|
@@ -402,15 +402,34 @@ function useComponent(config, deps) {
|
|
|
402
402
|
function useHumanInTheLoop(tool, deps) {
|
|
403
403
|
const { copilotkit } = useCopilotKit();
|
|
404
404
|
const resolvePromiseRef = useRef(null);
|
|
405
|
+
const cleanupAbortRef = useRef(null);
|
|
405
406
|
const respond = useCallback(async (result) => {
|
|
406
407
|
if (resolvePromiseRef.current) {
|
|
408
|
+
cleanupAbortRef.current?.();
|
|
409
|
+
cleanupAbortRef.current = null;
|
|
407
410
|
resolvePromiseRef.current(result);
|
|
408
411
|
resolvePromiseRef.current = null;
|
|
409
412
|
}
|
|
410
413
|
}, []);
|
|
411
|
-
const handler = useCallback(async () => {
|
|
412
|
-
|
|
414
|
+
const handler = useCallback(async (_args, context) => {
|
|
415
|
+
const signal = context?.signal;
|
|
416
|
+
return new Promise((resolve, reject) => {
|
|
417
|
+
if (signal?.aborted) {
|
|
418
|
+
reject(/* @__PURE__ */ new Error("Human-in-the-loop interaction aborted"));
|
|
419
|
+
return;
|
|
420
|
+
}
|
|
413
421
|
resolvePromiseRef.current = resolve;
|
|
422
|
+
if (signal) {
|
|
423
|
+
const onAbort = () => {
|
|
424
|
+
cleanupAbortRef.current = null;
|
|
425
|
+
resolvePromiseRef.current = null;
|
|
426
|
+
reject(/* @__PURE__ */ new Error("Human-in-the-loop interaction aborted"));
|
|
427
|
+
};
|
|
428
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
429
|
+
cleanupAbortRef.current = () => {
|
|
430
|
+
signal.removeEventListener("abort", onAbort);
|
|
431
|
+
};
|
|
432
|
+
}
|
|
414
433
|
});
|
|
415
434
|
}, []);
|
|
416
435
|
const RenderComponent = useCallback((props) => {
|
|
@@ -473,114 +492,165 @@ const INTERRUPT_EVENT_NAME = "on_interrupt";
|
|
|
473
492
|
function isPromiseLike(value) {
|
|
474
493
|
return (typeof value === "object" || typeof value === "function") && value !== null && typeof Reflect.get(value, "then") === "function";
|
|
475
494
|
}
|
|
495
|
+
/** Derive the legacy-compatible `event` for any pending interrupt. */
|
|
496
|
+
function toLegacyEvent(pending) {
|
|
497
|
+
if (pending.kind === "legacy") return pending.event;
|
|
498
|
+
return {
|
|
499
|
+
name: INTERRUPT_EVENT_NAME,
|
|
500
|
+
value: pending.interrupts[0]
|
|
501
|
+
};
|
|
502
|
+
}
|
|
476
503
|
/**
|
|
477
|
-
* Handles agent interrupts
|
|
478
|
-
*
|
|
479
|
-
* The hook listens to custom events on the active agent, stores interrupt payloads per run,
|
|
480
|
-
* and surfaces a render callback once the run finalizes. Call `resolve` from your UI to resume
|
|
481
|
-
* execution with user-provided data.
|
|
504
|
+
* Handles agent interrupts with optional filtering, preprocessing, and resume behavior.
|
|
482
505
|
*
|
|
483
|
-
*
|
|
484
|
-
*
|
|
485
|
-
*
|
|
486
|
-
*
|
|
487
|
-
*
|
|
488
|
-
*
|
|
489
|
-
*
|
|
490
|
-
* @param config - Interrupt configuration (renderer, optional handler/filter, and render mode).
|
|
491
|
-
* @returns When `renderInChat` is `false`, returns the interrupt element (or `null` when idle).
|
|
492
|
-
* Otherwise returns `void` and publishes the element into chat. In `render`, `result` is always
|
|
493
|
-
* either the handler's resolved return value or `null` (including when no handler is provided,
|
|
494
|
-
* when filtering skips the interrupt, or when handler execution fails).
|
|
495
|
-
*
|
|
496
|
-
* @example
|
|
497
|
-
* ```tsx
|
|
498
|
-
* import { useInterrupt } from "@copilotkit/react-core/v2";
|
|
499
|
-
*
|
|
500
|
-
* function InterruptUI() {
|
|
501
|
-
* useInterrupt({
|
|
502
|
-
* render: ({ event, resolve }) => (
|
|
503
|
-
* <div>
|
|
504
|
-
* <p>{event.value.question}</p>
|
|
505
|
-
* <button onClick={() => resolve({ approved: true })}>Approve</button>
|
|
506
|
-
* <button onClick={() => resolve({ approved: false })}>Reject</button>
|
|
507
|
-
* </div>
|
|
508
|
-
* ),
|
|
509
|
-
* });
|
|
506
|
+
* Supports both the AG-UI standard interrupt flow (`RUN_FINISHED` with
|
|
507
|
+
* `outcome.type === "interrupt"`) and the legacy custom-event flow
|
|
508
|
+
* (`on_interrupt`). For standard interrupts, `render` receives `interrupt`
|
|
509
|
+
* (the primary one) and `interrupts` (the full open set); call `resolve(payload)`
|
|
510
|
+
* to resume or `cancel()` to cancel. Resuming addresses the targeted interrupt
|
|
511
|
+
* and, once every open interrupt is addressed, submits a single spec `resume`
|
|
512
|
+
* array via `copilotkit.runAgent`.
|
|
510
513
|
*
|
|
511
|
-
*
|
|
512
|
-
*
|
|
513
|
-
* ```
|
|
514
|
+
* - `renderInChat: true` (default): the element is published into `<CopilotChat>`; returns `void`.
|
|
515
|
+
* - `renderInChat: false`: the hook returns the interrupt element for manual placement.
|
|
514
516
|
*
|
|
515
517
|
* @example
|
|
516
518
|
* ```tsx
|
|
517
|
-
*
|
|
518
|
-
*
|
|
519
|
-
*
|
|
520
|
-
*
|
|
521
|
-
*
|
|
522
|
-
*
|
|
523
|
-
*
|
|
524
|
-
*
|
|
525
|
-
*
|
|
526
|
-
* <strong>{result?.label ?? ""}</strong>
|
|
527
|
-
* <button onClick={() => resolve({ value: event.value })}>Continue</button>
|
|
528
|
-
* </aside>
|
|
529
|
-
* ),
|
|
530
|
-
* });
|
|
531
|
-
*
|
|
532
|
-
* return <>{interruptElement}</>;
|
|
533
|
-
* }
|
|
519
|
+
* useInterrupt({
|
|
520
|
+
* render: ({ interrupt, resolve, cancel }) => (
|
|
521
|
+
* <div>
|
|
522
|
+
* <p>{interrupt?.message}</p>
|
|
523
|
+
* <button onClick={() => resolve({ approved: true })}>Approve</button>
|
|
524
|
+
* <button onClick={() => cancel()}>Cancel</button>
|
|
525
|
+
* </div>
|
|
526
|
+
* ),
|
|
527
|
+
* });
|
|
534
528
|
* ```
|
|
535
529
|
*/
|
|
536
530
|
function useInterrupt(config) {
|
|
537
531
|
const { copilotkit } = useCopilotKit();
|
|
538
532
|
const { agent } = useAgent({ agentId: config.agentId });
|
|
539
|
-
const [
|
|
540
|
-
const
|
|
541
|
-
|
|
533
|
+
const [pending, setPending] = useState(null);
|
|
534
|
+
const pendingRef = useRef(pending);
|
|
535
|
+
pendingRef.current = pending;
|
|
542
536
|
const [handlerResult, setHandlerResult] = useState(null);
|
|
537
|
+
const responsesRef = useRef({});
|
|
543
538
|
useEffect(() => {
|
|
544
|
-
let
|
|
539
|
+
let localLegacy = null;
|
|
540
|
+
let localStandard = null;
|
|
545
541
|
const subscription = agent.subscribe({
|
|
546
542
|
onCustomEvent: ({ event }) => {
|
|
547
|
-
if (event.name === INTERRUPT_EVENT_NAME)
|
|
543
|
+
if (event.name === INTERRUPT_EVENT_NAME) localLegacy = {
|
|
548
544
|
name: event.name,
|
|
549
545
|
value: event.value
|
|
550
546
|
};
|
|
551
547
|
},
|
|
548
|
+
onRunFinishedEvent: (params) => {
|
|
549
|
+
if (params.outcome === "interrupt") localStandard = params.interrupts;
|
|
550
|
+
},
|
|
552
551
|
onRunStartedEvent: () => {
|
|
553
|
-
|
|
554
|
-
|
|
552
|
+
localLegacy = null;
|
|
553
|
+
localStandard = null;
|
|
554
|
+
responsesRef.current = {};
|
|
555
|
+
setPending(null);
|
|
555
556
|
},
|
|
556
557
|
onRunFinalized: () => {
|
|
557
|
-
if (
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
}
|
|
558
|
+
if (localStandard && localStandard.length > 0) setPending({
|
|
559
|
+
kind: "standard",
|
|
560
|
+
interrupts: localStandard
|
|
561
|
+
});
|
|
562
|
+
else if (localLegacy) setPending({
|
|
563
|
+
kind: "legacy",
|
|
564
|
+
event: localLegacy
|
|
565
|
+
});
|
|
566
|
+
localLegacy = null;
|
|
567
|
+
localStandard = null;
|
|
561
568
|
},
|
|
562
569
|
onRunFailed: () => {
|
|
563
|
-
|
|
564
|
-
|
|
570
|
+
localLegacy = null;
|
|
571
|
+
localStandard = null;
|
|
572
|
+
responsesRef.current = {};
|
|
573
|
+
setPending(null);
|
|
565
574
|
}
|
|
566
575
|
});
|
|
567
576
|
return () => subscription.unsubscribe();
|
|
568
577
|
}, [agent]);
|
|
569
|
-
const
|
|
578
|
+
const submitStandardIfComplete = useCallback(async (interrupts) => {
|
|
579
|
+
if (!interrupts.every((i) => responsesRef.current[i.id])) return;
|
|
580
|
+
const expired = interrupts.find((i) => isInterruptExpired(i));
|
|
581
|
+
if (expired) {
|
|
582
|
+
console.error(`[CopilotKit] useInterrupt: interrupt ${expired.id} expired at ${expired.expiresAt}; not resuming.`);
|
|
583
|
+
responsesRef.current = {};
|
|
584
|
+
setPending(null);
|
|
585
|
+
return;
|
|
586
|
+
}
|
|
587
|
+
const resume = buildResumeArray(interrupts, responsesRef.current);
|
|
588
|
+
for (const i of interrupts) {
|
|
589
|
+
if (!i.toolCallId) continue;
|
|
590
|
+
const response = responsesRef.current[i.id];
|
|
591
|
+
const content = response.status === "cancelled" ? { status: "cancelled" } : response.payload ?? { status: "resolved" };
|
|
592
|
+
agent.addMessage({
|
|
593
|
+
id: randomUUID$1(),
|
|
594
|
+
role: "tool",
|
|
595
|
+
toolCallId: i.toolCallId,
|
|
596
|
+
content: JSON.stringify(content)
|
|
597
|
+
});
|
|
598
|
+
}
|
|
599
|
+
responsesRef.current = {};
|
|
570
600
|
try {
|
|
601
|
+
return await copilotkit.runAgent({
|
|
602
|
+
agent,
|
|
603
|
+
resume
|
|
604
|
+
});
|
|
605
|
+
} catch (err) {
|
|
606
|
+
console.error("[CopilotKit] useInterrupt resolve: runAgent rejected; clearing pending + rethrowing", err);
|
|
607
|
+
setPending(null);
|
|
608
|
+
throw err;
|
|
609
|
+
}
|
|
610
|
+
}, [agent, copilotkit]);
|
|
611
|
+
const resolve = useCallback(async (payload, interruptId) => {
|
|
612
|
+
const current = pendingRef.current;
|
|
613
|
+
if (!current) return;
|
|
614
|
+
if (current.kind === "legacy") try {
|
|
571
615
|
return await copilotkit.runAgent({
|
|
572
616
|
agent,
|
|
573
617
|
forwardedProps: { command: {
|
|
574
|
-
resume:
|
|
575
|
-
interruptEvent:
|
|
618
|
+
resume: payload,
|
|
619
|
+
interruptEvent: current.event.value
|
|
576
620
|
} }
|
|
577
621
|
});
|
|
578
622
|
} catch (err) {
|
|
579
623
|
console.error("[CopilotKit] useInterrupt resolve: runAgent rejected; clearing pending + rethrowing", err);
|
|
580
|
-
|
|
624
|
+
setPending(null);
|
|
581
625
|
throw err;
|
|
582
626
|
}
|
|
583
|
-
|
|
627
|
+
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.`);
|
|
628
|
+
const id = interruptId ?? current.interrupts[0]?.id;
|
|
629
|
+
if (!id) return;
|
|
630
|
+
responsesRef.current[id] = {
|
|
631
|
+
status: "resolved",
|
|
632
|
+
payload
|
|
633
|
+
};
|
|
634
|
+
return submitStandardIfComplete(current.interrupts);
|
|
635
|
+
}, [
|
|
636
|
+
agent,
|
|
637
|
+
copilotkit,
|
|
638
|
+
submitStandardIfComplete
|
|
639
|
+
]);
|
|
640
|
+
const cancel = useCallback(async (interruptId) => {
|
|
641
|
+
const current = pendingRef.current;
|
|
642
|
+
if (!current) return;
|
|
643
|
+
if (current.kind === "legacy") {
|
|
644
|
+
console.warn("[CopilotKit] useInterrupt: cancel() is not supported for legacy on_interrupt interrupts; dismissing.");
|
|
645
|
+
setPending(null);
|
|
646
|
+
return;
|
|
647
|
+
}
|
|
648
|
+
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.`);
|
|
649
|
+
const id = interruptId ?? current.interrupts[0]?.id;
|
|
650
|
+
if (!id) return;
|
|
651
|
+
responsesRef.current[id] = { status: "cancelled" };
|
|
652
|
+
return submitStandardIfComplete(current.interrupts);
|
|
653
|
+
}, [submitStandardIfComplete]);
|
|
584
654
|
const renderRef = useRef(config.render);
|
|
585
655
|
renderRef.current = config.render;
|
|
586
656
|
const enabledRef = useRef(config.enabled);
|
|
@@ -589,6 +659,8 @@ function useInterrupt(config) {
|
|
|
589
659
|
handlerRef.current = config.handler;
|
|
590
660
|
const resolveRef = useRef(resolve);
|
|
591
661
|
resolveRef.current = resolve;
|
|
662
|
+
const cancelRef = useRef(cancel);
|
|
663
|
+
cancelRef.current = cancel;
|
|
592
664
|
const isEnabled = (event) => {
|
|
593
665
|
const predicate = enabledRef.current;
|
|
594
666
|
if (!predicate) return true;
|
|
@@ -600,11 +672,12 @@ function useInterrupt(config) {
|
|
|
600
672
|
}
|
|
601
673
|
};
|
|
602
674
|
useEffect(() => {
|
|
603
|
-
if (!
|
|
675
|
+
if (!pending) {
|
|
604
676
|
setHandlerResult(null);
|
|
605
677
|
return;
|
|
606
678
|
}
|
|
607
|
-
|
|
679
|
+
const legacyEvent = toLegacyEvent(pending);
|
|
680
|
+
if (!isEnabled(legacyEvent)) {
|
|
608
681
|
setHandlerResult(null);
|
|
609
682
|
return;
|
|
610
683
|
}
|
|
@@ -617,8 +690,11 @@ function useInterrupt(config) {
|
|
|
617
690
|
let maybePromise;
|
|
618
691
|
try {
|
|
619
692
|
maybePromise = handler({
|
|
620
|
-
event:
|
|
621
|
-
|
|
693
|
+
event: legacyEvent,
|
|
694
|
+
interrupt: pending.kind === "standard" ? pending.interrupts[0] : null,
|
|
695
|
+
interrupts: pending.kind === "standard" ? pending.interrupts : [],
|
|
696
|
+
resolve: resolveRef.current,
|
|
697
|
+
cancel: cancelRef.current
|
|
622
698
|
});
|
|
623
699
|
} catch (err) {
|
|
624
700
|
console.error("[CopilotKit] useInterrupt handler threw; result will be null:", err);
|
|
@@ -637,19 +713,24 @@ function useInterrupt(config) {
|
|
|
637
713
|
return () => {
|
|
638
714
|
cancelled = true;
|
|
639
715
|
};
|
|
640
|
-
}, [
|
|
716
|
+
}, [pending]);
|
|
641
717
|
const element = useMemo(() => {
|
|
642
|
-
if (!
|
|
643
|
-
|
|
718
|
+
if (!pending) return null;
|
|
719
|
+
const legacyEvent = toLegacyEvent(pending);
|
|
720
|
+
if (!isEnabled(legacyEvent)) return null;
|
|
644
721
|
return renderRef.current({
|
|
645
|
-
event:
|
|
722
|
+
event: legacyEvent,
|
|
723
|
+
interrupt: pending.kind === "standard" ? pending.interrupts[0] : null,
|
|
724
|
+
interrupts: pending.kind === "standard" ? pending.interrupts : [],
|
|
646
725
|
result: handlerResult,
|
|
647
|
-
resolve
|
|
726
|
+
resolve,
|
|
727
|
+
cancel
|
|
648
728
|
});
|
|
649
729
|
}, [
|
|
650
|
-
|
|
730
|
+
pending,
|
|
651
731
|
handlerResult,
|
|
652
|
-
resolve
|
|
732
|
+
resolve,
|
|
733
|
+
cancel
|
|
653
734
|
]);
|
|
654
735
|
useEffect(() => {
|
|
655
736
|
if (config.renderInChat === false) return;
|
|
@@ -922,9 +1003,9 @@ function useThreadStoreSelector(store, selector) {
|
|
|
922
1003
|
* current without polling — thread creates, renames, archives, and deletes
|
|
923
1004
|
* from any client are reflected immediately.
|
|
924
1005
|
*
|
|
925
|
-
* Mutation methods (`renameThread`, `archiveThread`, `
|
|
926
|
-
* promises that resolve once the platform confirms the
|
|
927
|
-
* with an `Error` on failure.
|
|
1006
|
+
* Mutation methods (`renameThread`, `archiveThread`, `unarchiveThread`,
|
|
1007
|
+
* `deleteThread`) return promises that resolve once the platform confirms the
|
|
1008
|
+
* operation and reject with an `Error` on failure.
|
|
928
1009
|
*
|
|
929
1010
|
* @param input - Agent identifier and optional list controls.
|
|
930
1011
|
* @returns Thread list state and stable mutation callbacks.
|
|
@@ -974,21 +1055,32 @@ function useThreads({ agentId, includeArchived, limit }) {
|
|
|
974
1055
|
const headersKey = useMemo(() => {
|
|
975
1056
|
return JSON.stringify(Object.entries(copilotkit.headers ?? {}).sort(([left], [right]) => left.localeCompare(right)));
|
|
976
1057
|
}, [copilotkit.headers]);
|
|
1058
|
+
const runtimeStatus = copilotkit.runtimeConnectionStatus;
|
|
1059
|
+
const threadListEndpointSupported = copilotkit.threadEndpoints?.list !== false;
|
|
1060
|
+
const threadMutationsSupported = copilotkit.threadEndpoints?.mutations !== false;
|
|
1061
|
+
const threadEndpointsUnavailable = !!copilotkit.runtimeUrl && runtimeStatus === CopilotKitCoreRuntimeConnectionStatus.Connected && !threadListEndpointSupported;
|
|
977
1062
|
const runtimeError = useMemo(() => {
|
|
978
1063
|
if (copilotkit.runtimeUrl) return null;
|
|
979
1064
|
return /* @__PURE__ */ new Error("Runtime URL is not configured");
|
|
980
1065
|
}, [copilotkit.runtimeUrl]);
|
|
1066
|
+
const threadEndpointsError = useMemo(() => {
|
|
1067
|
+
if (!threadEndpointsUnavailable) return null;
|
|
1068
|
+
return /* @__PURE__ */ new Error("Thread endpoints are not available on this CopilotKit runtime");
|
|
1069
|
+
}, [threadEndpointsUnavailable]);
|
|
1070
|
+
const threadMutationsError = useMemo(() => {
|
|
1071
|
+
if (threadMutationsSupported) return null;
|
|
1072
|
+
return /* @__PURE__ */ new Error("Thread mutations are not available on this CopilotKit runtime");
|
|
1073
|
+
}, [threadMutationsSupported]);
|
|
981
1074
|
const [hasDispatchedContext, setHasDispatchedContext] = useState(false);
|
|
982
|
-
const preConnectLoading = !!copilotkit.runtimeUrl && !hasDispatchedContext;
|
|
983
|
-
const isLoading = runtimeError ? false : preConnectLoading || storeIsLoading;
|
|
984
|
-
const error = runtimeError ?? storeError;
|
|
1075
|
+
const preConnectLoading = !!copilotkit.runtimeUrl && !threadEndpointsUnavailable && !hasDispatchedContext;
|
|
1076
|
+
const isLoading = runtimeError || threadEndpointsError ? false : preConnectLoading || storeIsLoading;
|
|
1077
|
+
const error = runtimeError ?? threadEndpointsError ?? storeError;
|
|
985
1078
|
useEffect(() => {
|
|
986
1079
|
store.start();
|
|
987
1080
|
return () => {
|
|
988
1081
|
store.stop();
|
|
989
1082
|
};
|
|
990
1083
|
}, [store]);
|
|
991
|
-
const runtimeStatus = copilotkit.runtimeConnectionStatus;
|
|
992
1084
|
useEffect(() => {
|
|
993
1085
|
copilotkit.registerThreadStore(agentId, store);
|
|
994
1086
|
return () => {
|
|
@@ -1002,9 +1094,15 @@ function useThreads({ agentId, includeArchived, limit }) {
|
|
|
1002
1094
|
useEffect(() => {
|
|
1003
1095
|
if (!copilotkit.runtimeUrl) {
|
|
1004
1096
|
store.setContext(null);
|
|
1097
|
+
setHasDispatchedContext(false);
|
|
1005
1098
|
return;
|
|
1006
1099
|
}
|
|
1007
1100
|
if (runtimeStatus !== CopilotKitCoreRuntimeConnectionStatus.Connected) return;
|
|
1101
|
+
if (!threadListEndpointSupported) {
|
|
1102
|
+
store.setContext(null);
|
|
1103
|
+
setHasDispatchedContext(false);
|
|
1104
|
+
return;
|
|
1105
|
+
}
|
|
1008
1106
|
const context = {
|
|
1009
1107
|
runtimeUrl: copilotkit.runtimeUrl,
|
|
1010
1108
|
headers: { ...copilotkit.headers },
|
|
@@ -1021,13 +1119,21 @@ function useThreads({ agentId, includeArchived, limit }) {
|
|
|
1021
1119
|
runtimeStatus,
|
|
1022
1120
|
headersKey,
|
|
1023
1121
|
copilotkit.intelligence?.wsUrl,
|
|
1122
|
+
threadListEndpointSupported,
|
|
1024
1123
|
agentId,
|
|
1025
1124
|
includeArchived,
|
|
1026
1125
|
limit
|
|
1027
1126
|
]);
|
|
1028
|
-
const
|
|
1029
|
-
|
|
1030
|
-
|
|
1127
|
+
const guardMutation = useCallback((mutation) => {
|
|
1128
|
+
return (...args) => {
|
|
1129
|
+
if (threadMutationsError) return Promise.reject(threadMutationsError);
|
|
1130
|
+
return mutation(...args);
|
|
1131
|
+
};
|
|
1132
|
+
}, [threadMutationsError]);
|
|
1133
|
+
const renameThread = useMemo(() => guardMutation((threadId, name) => store.renameThread(threadId, name)), [store, guardMutation]);
|
|
1134
|
+
const archiveThread = useMemo(() => guardMutation((threadId) => store.archiveThread(threadId)), [store, guardMutation]);
|
|
1135
|
+
const unarchiveThread = useMemo(() => guardMutation((threadId) => store.unarchiveThread(threadId)), [store, guardMutation]);
|
|
1136
|
+
const deleteThread = useMemo(() => guardMutation((threadId) => store.deleteThread(threadId)), [store, guardMutation]);
|
|
1031
1137
|
return {
|
|
1032
1138
|
threads,
|
|
1033
1139
|
isLoading,
|
|
@@ -1037,6 +1143,7 @@ function useThreads({ agentId, includeArchived, limit }) {
|
|
|
1037
1143
|
fetchMoreThreads: useCallback(() => store.fetchNextPage(), [store]),
|
|
1038
1144
|
renameThread,
|
|
1039
1145
|
archiveThread,
|
|
1146
|
+
unarchiveThread,
|
|
1040
1147
|
deleteThread
|
|
1041
1148
|
};
|
|
1042
1149
|
}
|
|
@@ -1111,10 +1218,20 @@ function useRenderTool(config, deps) {
|
|
|
1111
1218
|
}) : defineToolCallRenderer({
|
|
1112
1219
|
name: config.name,
|
|
1113
1220
|
args: config.parameters,
|
|
1114
|
-
render: (props) =>
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1221
|
+
render: (props) => {
|
|
1222
|
+
if (props.status === ToolCallStatus.InProgress) return config.render({
|
|
1223
|
+
...props,
|
|
1224
|
+
parameters: props.args
|
|
1225
|
+
});
|
|
1226
|
+
if (props.status === ToolCallStatus.Executing) return config.render({
|
|
1227
|
+
...props,
|
|
1228
|
+
parameters: props.args
|
|
1229
|
+
});
|
|
1230
|
+
return config.render({
|
|
1231
|
+
...props,
|
|
1232
|
+
parameters: props.args
|
|
1233
|
+
});
|
|
1234
|
+
},
|
|
1118
1235
|
...config.agentId ? { agentId: config.agentId } : {}
|
|
1119
1236
|
});
|
|
1120
1237
|
copilotkit.addHookRenderToolCall(renderer);
|