UNPKG

@copilotkit/react-core

Version:

<img src="https://github.com/user-attachments/assets/0a6b64d9-e193-4940-a3f6-60334ac34084" alt="banner" style="border-radius: 12px; border: 2px solid #d6d4fa;" />

1,540 lines 54.7 kB
import { CopilotKitCoreReact, useCopilotKit, useCopilotKit as useCopilotKit$1 } from "@copilotkit/react-core/v2/context";
import React, { createContext, useCallback, useContext, useEffect, useLayoutEffect, useMemo, useReducer, useRef, useState, useSyncExternalStore } from "react";
import { DEFAULT_AGENT_ID, partialJSONParse, randomUUID } from "@copilotkit/shared";
import { jsx } from "react/jsx-runtime";
import { HttpAgent, randomUUID as randomUUID$1 } from "@ag-ui/client";
import { CopilotKitCoreRuntimeConnectionStatus, ProxiedCopilotRuntimeAgent, ToolCallStatus, ɵInterruptState, ɵcreateThreadStore, ɵselectFetchMoreError, ɵselectHasNextPage, ɵselectIsFetchingNextPage, ɵselectIsMutating, ɵselectThreads, ɵselectThreadsError, ɵselectThreadsIsLoading } from "@copilotkit/core";
import { z } from "zod";

//#region src/v2/lib/shallow-stable-ref.ts
/**
* Shallow equality comparison for objects.
*/
function shallowEqual(obj1, obj2) {
	const keys1 = Object.keys(obj1);
	const keys2 = Object.keys(obj2);
	if (keys1.length !== keys2.length) return false;
	for (const key of keys1) if (obj1[key] !== obj2[key]) return false;
	return true;
}
/**
* Returns true only for plain JS objects (`{}`), excluding arrays, Dates,
* class instances, and other exotic objects that happen to have typeof "object".
*/
function isPlainObject(obj) {
	return obj !== null && typeof obj === "object" && Object.prototype.toString.call(obj) === "[object Object]";
}
/**
* Returns the same reference as long as the value is shallowly equal to the
* previous render's value.
*
* - Identical references bail out immediately (O(1)).
* - Plain objects ({}) are shallow-compared key-by-key.
* - Arrays, Dates, class instances, functions, and primitives are compared by
*   reference only — shallowEqual is never called on non-plain objects, which
*   avoids incorrect equality for e.g. [1,2] vs [1,2] (different arrays).
*
* Typical use: stabilize inline slot props so MemoizedSlotWrapper's shallow
* equality check isn't defeated by a new object reference on every render.
*/
function useShallowStableRef(value) {
	const ref = useRef(value);
	if (ref.current === value) return ref.current;
	if (isPlainObject(ref.current) && isPlainObject(value)) {
		if (shallowEqual(ref.current, value)) return ref.current;
	}
	ref.current = value;
	return ref.current;
}

//#endregion
//#region src/v2/providers/CopilotChatConfigurationProvider.tsx
const CopilotChatDefaultLabels = {
	chatInputPlaceholder: "Type a message...",
	chatInputToolbarStartTranscribeButtonLabel: "Transcribe",
	chatInputToolbarCancelTranscribeButtonLabel: "Cancel",
	chatInputToolbarFinishTranscribeButtonLabel: "Finish",
	chatInputToolbarAddButtonLabel: "Add attachments",
	chatInputToolbarToolsButtonLabel: "Tools",
	assistantMessageToolbarCopyCodeLabel: "Copy",
	assistantMessageToolbarCopyCodeCopiedLabel: "Copied",
	assistantMessageToolbarCopyMessageLabel: "Copy",
	assistantMessageToolbarInspectorLabel: "View in Inspector",
	assistantMessageToolbarInspectorLocalOnlyLabel: "Local Only",
	assistantMessageToolbarThumbsUpLabel: "Good response",
	assistantMessageToolbarThumbsDownLabel: "Bad response",
	assistantMessageToolbarReadAloudLabel: "Read aloud",
	assistantMessageToolbarRegenerateLabel: "Regenerate",
	userMessageToolbarCopyMessageLabel: "Copy",
	userMessageToolbarEditMessageLabel: "Edit",
	chatDisclaimerText: "AI can make mistakes. Please verify important information.",
	chatToggleOpenLabel: "Open chat",
	chatToggleCloseLabel: "Close chat",
	modalHeaderTitle: "CopilotKit Chat",
	welcomeMessageText: "How can I help you today?"
};
/**
* Mobile breakpoint below which the chat modal and the thread-list drawer are
* mutually exclusive. At or above this width both surfaces may coexist. This
* mirrors the `(max-width: 767px)` / `(min-width: 768px)` split already used by
* CopilotChatInput and CopilotSidebarView.
*/
const MOBILE_MAX_WIDTH_PX = 767;
/**
* Reports whether the current viewport is in the mobile range (`<768px`), where
* the chat modal and drawer must not be open simultaneously. SSR-safe and
* defensive against environments without `matchMedia` (treated as desktop, so
* no mutual-exclusion constraint is applied).
*
* @returns `true` when the viewport is mobile-width, `false` otherwise.
*/
function isMobileViewport() {
	if (typeof window === "undefined" || typeof window.matchMedia !== "function") return false;
	return window.matchMedia(`(max-width: ${MOBILE_MAX_WIDTH_PX}px)`).matches;
}
const CopilotChatConfiguration = createContext(null);
const CopilotChatConfigurationProvider = ({ children, labels, agentId, threadId, hasExplicitThreadId, isModalDefaultOpen }) => {
	const parentConfig = useContext(CopilotChatConfiguration);
	const stableLabels = useShallowStableRef(labels);
	const mergedLabels = useMemo(() => ({
		...CopilotChatDefaultLabels,
		...parentConfig?.labels,
		...stableLabels
	}), [stableLabels, parentConfig?.labels]);
	const resolvedAgentId = agentId ?? parentConfig?.agentId ?? DEFAULT_AGENT_ID;
	const threadIdPropIsAuthoritative = threadId !== void 0 && hasExplicitThreadId !== false;
	const isThreadIdControlled = threadIdPropIsAuthoritative;
	const [activeThreadOverride, setActiveThreadOverride] = useState(null);
	const resolvedThreadId = useMemo(() => {
		if (threadIdPropIsAuthoritative) return threadId;
		if (activeThreadOverride) return activeThreadOverride.threadId;
		if (parentConfig?.threadId) return parentConfig.threadId;
		if (threadId) return threadId;
		return randomUUID();
	}, [
		threadIdPropIsAuthoritative,
		threadId,
		parentConfig?.threadId,
		activeThreadOverride
	]);
	const resolvedHasExplicitThreadId = (threadIdPropIsAuthoritative ? true : activeThreadOverride?.explicit ?? hasExplicitThreadId ?? false) || !!parentConfig?.hasExplicitThreadId;
	const [internalModalOpen, setInternalModalOpen] = useState(isModalDefaultOpen ?? true);
	const hasExplicitDefault = isModalDefaultOpen !== void 0;
	const setAndSync = useCallback((open) => {
		setInternalModalOpen(open);
		parentConfig?.setModalOpen(open);
	}, [parentConfig?.setModalOpen]);
	const isMounted = useRef(false);
	useEffect(() => {
		if (!hasExplicitDefault) return;
		if (!isMounted.current) {
			isMounted.current = true;
			return;
		}
		if (parentConfig?.isModalOpen === void 0) return;
		setInternalModalOpen(parentConfig.isModalOpen);
	}, [parentConfig?.isModalOpen, hasExplicitDefault]);
	const resolvedIsModalOpen = hasExplicitDefault ? internalModalOpen : parentConfig?.isModalOpen ?? internalModalOpen;
	const resolvedSetModalOpen = hasExplicitDefault ? setAndSync : parentConfig?.setModalOpen ?? setInternalModalOpen;
	const [ownDrawerOpen, setOwnDrawerOpen] = useState(false);
	const [ownDrawerCount, setOwnDrawerCount] = useState(0);
	const modalCloseRef = useRef(() => {});
	modalCloseRef.current = resolvedSetModalOpen;
	const registeredModalClosersRef = useRef([]);
	const ownRegisterModalCloser = useCallback((closeModal) => {
		registeredModalClosersRef.current.push(closeModal);
		return () => {
			registeredModalClosersRef.current = registeredModalClosersRef.current.filter((entry) => entry !== closeModal);
		};
	}, []);
	const ownSetDrawerOpen = useCallback((open) => {
		setOwnDrawerOpen(open);
		if (open && isMobileViewport()) {
			const registered = registeredModalClosersRef.current;
			(registered.length > 0 ? registered[registered.length - 1] : modalCloseRef.current)(false);
		}
	}, []);
	const ownRegisterDrawer = useCallback(() => {
		setOwnDrawerCount((count) => count + 1);
		return () => {
			setOwnDrawerCount((count) => Math.max(0, count - 1));
		};
	}, []);
	const resolvedDrawerOpen = parentConfig ? parentConfig.drawerOpen : ownDrawerOpen;
	const resolvedSetDrawerOpen = parentConfig ? parentConfig.setDrawerOpen : ownSetDrawerOpen;
	const resolvedDrawerRegistered = parentConfig ? parentConfig.drawerRegistered : ownDrawerCount > 0;
	const resolvedRegisterDrawer = parentConfig ? parentConfig.registerDrawer : ownRegisterDrawer;
	const resolvedRegisterModalCloser = parentConfig ? parentConfig.ɵregisterModalCloser : ownRegisterModalCloser;
	useEffect(() => {
		if (!hasExplicitDefault) return;
		return resolvedRegisterModalCloser(resolvedSetModalOpen);
	}, [
		hasExplicitDefault,
		resolvedRegisterModalCloser,
		resolvedSetModalOpen
	]);
	const isThreadIdControlledRef = useRef(isThreadIdControlled);
	isThreadIdControlledRef.current = isThreadIdControlled;
	const ownSetActiveThreadId = useCallback((id, options) => {
		setActiveThreadOverride({
			threadId: id,
			explicit: options?.explicit ?? true
		});
	}, []);
	const ownStartNewThread = useCallback(() => {
		setActiveThreadOverride({
			threadId: randomUUID(),
			explicit: false
		});
	}, []);
	const parentSetActiveThreadId = parentConfig?.setActiveThreadId;
	const parentStartNewThread = parentConfig?.startNewThread;
	const resolvedSetActiveThreadId = useCallback((id, options) => {
		if (isThreadIdControlledRef.current) {
			console.warn("[CopilotKit] Ignoring setActiveThreadId(): threadId is controlled via the `threadId` prop on CopilotChatConfigurationProvider.");
			return;
		}
		if (parentSetActiveThreadId) {
			parentSetActiveThreadId(id, options);
			return;
		}
		ownSetActiveThreadId(id, options);
	}, [parentSetActiveThreadId, ownSetActiveThreadId]);
	const resolvedStartNewThread = useCallback(() => {
		if (isThreadIdControlledRef.current) {
			console.warn("[CopilotKit] Ignoring startNewThread(): threadId is controlled via the `threadId` prop on CopilotChatConfigurationProvider.");
			return;
		}
		if (parentStartNewThread) {
			parentStartNewThread();
			return;
		}
		ownStartNewThread();
	}, [parentStartNewThread, ownStartNewThread]);
	const setModalOpenWithDrawerExclusion = useCallback((open) => {
		if (open && isMobileViewport()) resolvedSetDrawerOpen(false);
		resolvedSetModalOpen(open);
	}, [resolvedSetModalOpen, resolvedSetDrawerOpen]);
	const configurationValue = useMemo(() => ({
		labels: mergedLabels,
		agentId: resolvedAgentId,
		threadId: resolvedThreadId,
		hasExplicitThreadId: resolvedHasExplicitThreadId,
		isModalOpen: resolvedIsModalOpen,
		setModalOpen: setModalOpenWithDrawerExclusion,
		drawerOpen: resolvedDrawerOpen,
		setDrawerOpen: resolvedSetDrawerOpen,
		drawerRegistered: resolvedDrawerRegistered,
		registerDrawer: resolvedRegisterDrawer,
		ɵregisterModalCloser: resolvedRegisterModalCloser,
		setActiveThreadId: resolvedSetActiveThreadId,
		startNewThread: resolvedStartNewThread
	}), [
		mergedLabels,
		resolvedAgentId,
		resolvedThreadId,
		resolvedHasExplicitThreadId,
		resolvedIsModalOpen,
		setModalOpenWithDrawerExclusion,
		resolvedDrawerOpen,
		resolvedSetDrawerOpen,
		resolvedDrawerRegistered,
		resolvedRegisterDrawer,
		resolvedRegisterModalCloser,
		resolvedSetActiveThreadId,
		resolvedStartNewThread
	]);
	return /* @__PURE__ */ jsx(CopilotChatConfiguration.Provider, {
		value: configurationValue,
		children
	});
};
const useCopilotChatConfiguration = () => {
	return useContext(CopilotChatConfiguration);
};

//#endregion
//#region src/v2/hooks/use-agent.tsx
let UseAgentUpdate = /* @__PURE__ */ function(UseAgentUpdate) {
	UseAgentUpdate["OnMessagesChanged"] = "OnMessagesChanged";
	UseAgentUpdate["OnStateChanged"] = "OnStateChanged";
	UseAgentUpdate["OnRunStatusChanged"] = "OnRunStatusChanged";
	return UseAgentUpdate;
}({});
const ALL_UPDATES = [
	UseAgentUpdate.OnMessagesChanged,
	UseAgentUpdate.OnStateChanged,
	UseAgentUpdate.OnRunStatusChanged
];
function useAgent({ agentId, threadId, runtimeAgentId, updates, throttleMs } = {}) {
	if (threadId != null && runtimeAgentId == null) throw new Error(`useAgent: \`threadId\` requires \`runtimeAgentId\`. A threadId is written onto a single agent, but an agent resolved by agentId alone is shared, so scoping a thread to it would clobber other useAgent callers. Pass a distinct local \`agentId\` and the runtime agent to route to, e.g. useAgent({ agentId: "chat-1", runtimeAgentId: "${agentId ?? "default"}", threadId }).`);
	if (runtimeAgentId != null && threadId == null) throw new Error(`useAgent: \`runtimeAgentId\` requires \`threadId\`. A proxied agent exists to scope a thread to a private instance; without a threadId it behaves like the shared agent while adding a registration and a local agentId to keep unique. Either pass the thread, e.g. useAgent({ agentId: "${agentId ?? "chat-1"}", runtimeAgentId: "${runtimeAgentId}", threadId }), or bind to the agent directly with useAgent({ agentId: "${runtimeAgentId}" }).`);
	if (runtimeAgentId != null && agentId == null) throw new Error(`useAgent: \`runtimeAgentId\` requires an explicit \`agentId\`. The proxied agent is registered under \`agentId\`, and the usual fallbacks (chat configuration, then "${DEFAULT_AGENT_ID}") name agents that already exist — registering over one throws or shadows it. Pick a local id for this hook, e.g. useAgent({ agentId: "chat-1", runtimeAgentId: "${runtimeAgentId}", threadId }).`);
	const chatConfig = useCopilotChatConfiguration();
	const resolvedAgentId = agentId ?? chatConfig?.agentId ?? DEFAULT_AGENT_ID;
	const { copilotkit } = useCopilotKit$1();
	const providerThrottleMs = copilotkit.defaultThrottleMs;
	const [, forceUpdate] = useReducer((x) => x + 1, 0);
	const updateFlags = useMemo(() => updates ?? ALL_UPDATES, [JSON.stringify(updates)]);
	const provisionalAgentCache = useRef(/* @__PURE__ */ new Map());
	const [registeredProxyAgent, setRegisteredProxyAgent] = useState(null);
	useEffect(() => {
		if (runtimeAgentId == null) {
			setRegisteredProxyAgent(null);
			return;
		}
		const { agent: proxy, unregister } = copilotkit.registerProxiedAgent({
			agentId: resolvedAgentId,
			runtimeAgentId
		});
		provisionalAgentCache.current.delete(resolvedAgentId);
		setRegisteredProxyAgent(proxy);
		return () => {
			unregister();
			setRegisteredProxyAgent(null);
		};
	}, [
		copilotkit,
		resolvedAgentId,
		runtimeAgentId
	]);
	const { agent, isReady } = useMemo(() => {
		if (runtimeAgentId != null) {
			if (registeredProxyAgent) {
				provisionalAgentCache.current.delete(resolvedAgentId);
				return {
					agent: registeredProxyAgent,
					isReady: true
				};
			}
			const cached = provisionalAgentCache.current.get(resolvedAgentId);
			if (cached) {
				copilotkit.applyHeadersToAgent(cached);
				return {
					agent: cached,
					isReady: false
				};
			}
			const provisional = new ProxiedCopilotRuntimeAgent({
				runtimeUrl: copilotkit.runtimeUrl,
				agentId: resolvedAgentId,
				runtimeAgentId,
				transport: copilotkit.runtimeTransport,
				runtimeMode: "pending"
			});
			copilotkit.applyHeadersToAgent(provisional);
			provisionalAgentCache.current.set(resolvedAgentId, provisional);
			return {
				agent: provisional,
				isReady: false
			};
		}
		const existing = copilotkit.getAgent(resolvedAgentId);
		if (existing) {
			provisionalAgentCache.current.delete(resolvedAgentId);
			return {
				agent: existing,
				isReady: true
			};
		}
		const isRuntimeConfigured = copilotkit.runtimeUrl !== void 0;
		const status = copilotkit.runtimeConnectionStatus;
		if (isRuntimeConfigured && (status === CopilotKitCoreRuntimeConnectionStatus.Disconnected || status === CopilotKitCoreRuntimeConnectionStatus.Connecting)) {
			const cached = provisionalAgentCache.current.get(resolvedAgentId);
			if (cached) return {
				agent: cached,
				isReady: false
			};
			const provisional = new ProxiedCopilotRuntimeAgent({
				runtimeUrl: copilotkit.runtimeUrl,
				agentId: resolvedAgentId,
				transport: copilotkit.runtimeTransport,
				credentials: copilotkit.credentials,
				runtimeMode: "pending"
			});
			copilotkit.applyHeadersToAgent(provisional);
			provisionalAgentCache.current.set(resolvedAgentId, provisional);
			return {
				agent: provisional,
				isReady: false
			};
		}
		if (isRuntimeConfigured && status === CopilotKitCoreRuntimeConnectionStatus.Error) {
			const cached = provisionalAgentCache.current.get(resolvedAgentId);
			if (cached) return {
				agent: cached,
				isReady: false
			};
			const provisional = new ProxiedCopilotRuntimeAgent({
				runtimeUrl: copilotkit.runtimeUrl,
				agentId: resolvedAgentId,
				transport: copilotkit.runtimeTransport,
				credentials: copilotkit.credentials,
				runtimeMode: "pending"
			});
			copilotkit.applyHeadersToAgent(provisional);
			provisionalAgentCache.current.set(resolvedAgentId, provisional);
			return {
				agent: provisional,
				isReady: false
			};
		}
		const knownAgents = Object.keys(copilotkit.agents ?? {});
		const runtimePart = isRuntimeConfigured ? `runtimeUrl=${copilotkit.runtimeUrl}` : "no runtimeUrl";
		throw new Error(`useAgent: Agent '${resolvedAgentId}' not found after runtime sync (${runtimePart}). ` + (knownAgents.length ? `Known agents: [${knownAgents.join(", ")}]` : "No agents registered.") + " Verify your runtime /info and/or agents__unsafe_dev_only.");
	}, [
		resolvedAgentId,
		runtimeAgentId,
		registeredProxyAgent,
		copilotkit.agents,
		copilotkit.runtimeConnectionStatus,
		copilotkit.runtimeUrl,
		copilotkit.runtimeTransport,
		copilotkit.credentials,
		JSON.stringify(copilotkit.headers)
	]);
	useEffect(() => {
		if (updateFlags.length === 0) return;
		let active = true;
		const handlers = {};
		let batchScheduled = false;
		const batchedForceUpdate = () => {
			if (!active) return;
			if (!batchScheduled) {
				batchScheduled = true;
				queueMicrotask(() => {
					batchScheduled = false;
					if (active) forceUpdate();
				});
			}
		};
		if (updateFlags.includes(UseAgentUpdate.OnMessagesChanged)) handlers.onMessagesChanged = batchedForceUpdate;
		if (updateFlags.includes(UseAgentUpdate.OnStateChanged)) handlers.onStateChanged = batchedForceUpdate;
		if (updateFlags.includes(UseAgentUpdate.OnRunStatusChanged)) {
			handlers.onRunInitialized = batchedForceUpdate;
			handlers.onRunFinalized = batchedForceUpdate;
			handlers.onRunFailed = batchedForceUpdate;
			handlers.onRunErrorEvent = batchedForceUpdate;
		}
		const subscription = copilotkit.subscribeToAgentWithOptions(agent, handlers, { throttleMs });
		return () => {
			active = false;
			subscription.unsubscribe();
		};
	}, [
		agent,
		forceUpdate,
		throttleMs,
		providerThrottleMs,
		updateFlags
	]);
	useEffect(() => {
		if (agent instanceof HttpAgent) copilotkit.applyHeadersToAgent(agent);
		if (agent instanceof ProxiedCopilotRuntimeAgent) agent.credentials = copilotkit.credentials;
	}, [
		agent,
		JSON.stringify(copilotkit.headers),
		copilotkit.credentials
	]);
	const configThreadId = chatConfig?.threadId;
	const configHasExplicitThreadId = chatConfig?.hasExplicitThreadId;
	const resolvedThreadId = threadId ?? (configHasExplicitThreadId ? configThreadId : void 0);
	useEffect(() => {
		if (!resolvedThreadId) return;
		agent.threadId = resolvedThreadId;
	}, [agent, resolvedThreadId]);
	return {
		agent,
		isReady
	};
}

//#endregion
//#region src/v2/hooks/use-frontend-tool.tsx
const EMPTY_DEPS$1 = [];
function useFrontendTool(tool, deps) {
	const { copilotkit } = useCopilotKit$1();
	const extraDeps = deps ?? EMPTY_DEPS$1;
	useEffect(() => {
		const name = tool.name;
		if (copilotkit.getTool({
			toolName: name,
			agentId: tool.agentId
		})) {
			console.warn(`Tool '${name}' already exists for agent '${tool.agentId || "global"}'. Overriding with latest registration.`);
			copilotkit.removeTool(name, tool.agentId);
		}
		copilotkit.addTool(tool);
		if (tool.render) copilotkit.addHookRenderToolCall({
			name,
			args: tool.parameters,
			agentId: tool.agentId,
			render: tool.render
		});
		return () => {
			copilotkit.removeTool(name, tool.agentId);
		};
	}, [
		tool.name,
		tool.available,
		copilotkit,
		JSON.stringify(extraDeps)
	]);
}

//#endregion
//#region src/v2/hooks/use-component.tsx
/**
* Registers a React component as a frontend tool renderer in chat.
*
* This hook is a convenience wrapper around `useFrontendTool` that:
* - builds a model-facing tool description,
* - forwards optional schema parameters (any Standard Schema V1 compatible library),
* - renders your component with tool call parameters.
*
* Use this when you want to display a typed visual component for a tool call
* without manually wiring a full frontend tool object.
*
* When `parameters` is provided, render props are inferred from the schema.
* When omitted, the render component may accept any props.
*
* @typeParam TSchema - Schema describing tool parameters, or `undefined` when no schema is given.
* @param config - Tool registration config.
* @param deps - Optional dependencies to refresh registration (same semantics as `useEffect`).
*
* @example
* ```tsx
* // Without parameters — render accepts any props
* useComponent({
*   name: "showGreeting",
*   render: ({ message }: { message: string }) => <div>{message}</div>,
* });
* ```
*
* @example
* ```tsx
* // With parameters — render props inferred from schema
* useComponent({
*   name: "showWeatherCard",
*   parameters: z.object({ city: z.string() }),
*   render: ({ city }) => <div>{city}</div>,
* });
* ```
*
* @example
* ```tsx
* useComponent(
*   {
*     name: "renderProfile",
*     parameters: z.object({ userId: z.string() }),
*     render: ProfileCard,
*     agentId: "support-agent",
*   },
*   [selectedAgentId],
* );
* ```
*/
function useComponent(config, deps) {
	const prefix = `Use this tool to display the "${config.name}" component in the chat. This tool renders a visual UI component for the user.`;
	const fullDescription = config.description ? `${prefix}\n\n${config.description}` : prefix;
	useFrontendTool({
		name: config.name,
		description: fullDescription,
		parameters: config.parameters,
		render: ({ args }) => {
			const Component = config.render;
			return /* @__PURE__ */ jsx(Component, { ...args });
		},
		agentId: config.agentId,
		followUp: config.followUp
	}, deps);
}

//#endregion
//#region src/v2/hooks/use-human-in-the-loop.tsx
function useHumanInTheLoop(tool, deps) {
	const { copilotkit } = useCopilotKit$1();
	const resolvePromiseRef = useRef(null);
	const cleanupAbortRef = useRef(null);
	const respond = useCallback(async (result) => {
		if (resolvePromiseRef.current) {
			cleanupAbortRef.current?.();
			cleanupAbortRef.current = null;
			resolvePromiseRef.current(result);
			resolvePromiseRef.current = null;
		}
	}, []);
	const handler = useCallback(async (_args, context) => {
		const signal = context?.signal;
		return new Promise((resolve, reject) => {
			if (signal?.aborted) {
				reject(/* @__PURE__ */ new Error("Human-in-the-loop interaction aborted"));
				return;
			}
			resolvePromiseRef.current = resolve;
			if (signal) {
				const onAbort = () => {
					cleanupAbortRef.current = null;
					resolvePromiseRef.current = null;
					reject(/* @__PURE__ */ new Error("Human-in-the-loop interaction aborted"));
				};
				signal.addEventListener("abort", onAbort, { once: true });
				cleanupAbortRef.current = () => {
					signal.removeEventListener("abort", onAbort);
				};
			}
		});
	}, []);
	const RenderComponent = useCallback((props) => {
		const ToolComponent = tool.render;
		if (props.status === ToolCallStatus.InProgress) {
			const enhancedProps = {
				...props,
				name: tool.name,
				description: tool.description || "",
				agentId: tool.agentId,
				respond: void 0
			};
			return React.createElement(ToolComponent, enhancedProps);
		} else if (props.status === ToolCallStatus.Executing) {
			const enhancedProps = {
				...props,
				name: tool.name,
				description: tool.description || "",
				agentId: tool.agentId,
				respond
			};
			return React.createElement(ToolComponent, enhancedProps);
		} else if (props.status === ToolCallStatus.Complete) {
			const enhancedProps = {
				...props,
				name: tool.name,
				description: tool.description || "",
				agentId: tool.agentId,
				respond: void 0
			};
			return React.createElement(ToolComponent, enhancedProps);
		}
		return props;
	}, [
		tool.render,
		tool.name,
		tool.description,
		tool.agentId,
		respond
	]);
	useFrontendTool({
		...tool,
		handler,
		render: RenderComponent
	}, deps);
	useEffect(() => {
		return () => {
			copilotkit.removeHookRenderToolCall(tool.name, tool.agentId);
		};
	}, [
		copilotkit,
		tool.name,
		tool.agentId
	]);
}

//#endregion
//#region src/v2/hooks/use-interrupt.tsx
const INTERRUPT_EVENT_NAME = "on_interrupt";
function isPromiseLike(value) {
	return (typeof value === "object" || typeof value === "function") && value !== null && typeof Reflect.get(value, "then") === "function";
}
/** Derive the legacy-compatible `event` for any pending interrupt. */
function toLegacyEvent(pending) {
	if (pending.kind === "legacy") return pending.event;
	return {
		name: INTERRUPT_EVENT_NAME,
		value: pending.interrupts[0]
	};
}
/**
* Handles agent interrupts with optional filtering, preprocessing, and resume behavior.
*
* Supports both the AG-UI standard interrupt flow (`RUN_FINISHED` with
* `outcome.type === "interrupt"`) and the legacy custom-event flow
* (`on_interrupt`). For standard interrupts, `render` receives `interrupt`
* (the primary one) and `interrupts` (the full open set); call `resolve(payload)`
* to resume or `cancel()` to cancel. Resuming addresses the targeted interrupt
* and, once every open interrupt is addressed, submits a single spec `resume`
* array via `copilotkit.runAgent`.
*
* - `renderInChat: true` (default): the element is published into `<CopilotChat>`; returns `void`.
* - `renderInChat: false`: the hook returns the interrupt element for manual placement.
*
* @example
* ```tsx
* useInterrupt({
*   render: ({ interrupt, resolve, cancel }) => (
*     <div>
*       <p>{interrupt?.message}</p>
*       <button onClick={() => resolve({ approved: true })}>Approve</button>
*       <button onClick={() => cancel()}>Cancel</button>
*     </div>
*   ),
* });
* ```
*/
function useInterrupt(config) {
	const { copilotkit } = useCopilotKit$1();
	const { agent } = useAgent({ agentId: config.agentId });
	const [pending, setPending] = useState(null);
	const pendingRef = useRef(pending);
	pendingRef.current = pending;
	const [handlerResult, setHandlerResult] = useState(null);
	const interruptStateRef = useRef(new ɵInterruptState());
	const interruptRunIdsRef = useRef(/* @__PURE__ */ new Map());
	const legacyRunIdRef = useRef(void 0);
	useEffect(() => {
		const interruptState = interruptStateRef.current;
		let localLegacy = null;
		let localStandard = null;
		const subscription = agent.subscribe({
			onCustomEvent: ({ event }) => {
				if (event.name === INTERRUPT_EVENT_NAME) localLegacy = {
					name: event.name,
					value: event.value
				};
			},
			onRunFinishedEvent: (params) => {
				if (params.outcome === "interrupt") {
					const runId = params.input.runId;
					for (const interrupt of params.interrupts) interruptRunIdsRef.current.set(interrupt.id, runId);
					localStandard = params.interrupts;
				}
			},
			onRunStartedEvent: () => {
				localLegacy = null;
				localStandard = null;
				interruptRunIdsRef.current.clear();
				legacyRunIdRef.current = void 0;
				interruptState.clear();
				setPending(null);
			},
			onRunFinalized: (params) => {
				if (localStandard && localStandard.length > 0) {
					interruptState.setStandard(localStandard);
					setPending(interruptState.pending);
				} else if (localLegacy) {
					legacyRunIdRef.current = params.input.runId;
					interruptState.setLegacy(localLegacy);
					setPending(interruptState.pending);
				}
				localLegacy = null;
				localStandard = null;
			},
			onRunFailed: () => {
				localLegacy = null;
				localStandard = null;
				interruptRunIdsRef.current.clear();
				legacyRunIdRef.current = void 0;
				interruptState.clear();
				setPending(null);
			}
		});
		return () => {
			subscription.unsubscribe();
			interruptState.clear();
		};
	}, [agent]);
	const resolve = useCallback(async (payload, interruptId) => {
		const current = pendingRef.current;
		if (!current) return;
		if (current.kind === "standard" && 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.`);
		const decision = interruptStateRef.current.resolve(payload, interruptId);
		if (decision.kind === "legacy-resume") {
			const runId = legacyRunIdRef.current;
			try {
				return await copilotkit.runAgent({
					agent,
					...runId !== void 0 ? { runId } : {},
					forwardedProps: { command: {
						resume: decision.payload,
						interruptEvent: decision.interruptValue
					} }
				});
			} catch (err) {
				console.error("[CopilotKit] useInterrupt resolve: runAgent rejected; clearing pending + rethrowing", err);
				setPending(null);
				throw err;
			}
		}
		if (decision.kind === "expired") {
			console.error(`[CopilotKit] useInterrupt: interrupt ${decision.interrupt.id} expired at ${decision.interrupt.expiresAt}; not resuming.`);
			interruptStateRef.current.clear();
			setPending(null);
			return;
		}
		if (decision.kind !== "resume") return;
		const runId = decision.resume.map((entry) => interruptRunIdsRef.current.get(entry.interruptId)).find((candidate) => candidate !== void 0);
		for (const toolResult of decision.toolResults) agent.addMessage({
			id: randomUUID$1(),
			role: "tool",
			toolCallId: toolResult.toolCallId,
			content: toolResult.content
		});
		try {
			return await copilotkit.runAgent({
				agent,
				resume: decision.resume,
				...runId !== void 0 ? { runId } : {}
			});
		} catch (err) {
			console.error("[CopilotKit] useInterrupt resolve: runAgent rejected; clearing pending + rethrowing", err);
			interruptStateRef.current.clear();
			setPending(null);
			throw err;
		}
	}, [agent, copilotkit]);
	const cancel = useCallback(async (interruptId) => {
		const current = pendingRef.current;
		if (!current) return;
		if (current.kind === "standard" && 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.`);
		const decision = interruptStateRef.current.cancel(interruptId);
		if (decision.kind === "dismiss") {
			console.warn("[CopilotKit] useInterrupt: cancel() is not supported for legacy on_interrupt interrupts; dismissing.");
			interruptStateRef.current.clear();
			setPending(null);
			return;
		}
		if (decision.kind === "expired") {
			console.error(`[CopilotKit] useInterrupt: interrupt ${decision.interrupt.id} expired at ${decision.interrupt.expiresAt}; not resuming.`);
			interruptStateRef.current.clear();
			setPending(null);
			return;
		}
		if (decision.kind !== "resume") return;
		const runId = decision.resume.map((entry) => interruptRunIdsRef.current.get(entry.interruptId)).find((candidate) => candidate !== void 0);
		for (const toolResult of decision.toolResults) agent.addMessage({
			id: randomUUID$1(),
			role: "tool",
			toolCallId: toolResult.toolCallId,
			content: toolResult.content
		});
		try {
			return await copilotkit.runAgent({
				agent,
				resume: decision.resume,
				...runId !== void 0 ? { runId } : {}
			});
		} catch (err) {
			console.error("[CopilotKit] useInterrupt resolve: runAgent rejected; clearing pending + rethrowing", err);
			interruptStateRef.current.clear();
			setPending(null);
			throw err;
		}
	}, [agent, copilotkit]);
	const renderRef = useRef(config.render);
	renderRef.current = config.render;
	const enabledRef = useRef(config.enabled);
	enabledRef.current = config.enabled;
	const handlerRef = useRef(config.handler);
	handlerRef.current = config.handler;
	const resolveRef = useRef(resolve);
	resolveRef.current = resolve;
	const cancelRef = useRef(cancel);
	cancelRef.current = cancel;
	const isEnabled = (event) => {
		const predicate = enabledRef.current;
		if (!predicate) return true;
		try {
			return predicate(event);
		} catch (err) {
			console.error("[CopilotKit] useInterrupt enabled predicate threw; treating interrupt as disabled:", err);
			return false;
		}
	};
	useEffect(() => {
		if (!pending) {
			setHandlerResult(null);
			return;
		}
		const legacyEvent = toLegacyEvent(pending);
		if (!isEnabled(legacyEvent)) {
			setHandlerResult(null);
			return;
		}
		const handler = handlerRef.current;
		if (!handler) {
			setHandlerResult(null);
			return;
		}
		let cancelled = false;
		let maybePromise;
		try {
			maybePromise = handler({
				event: legacyEvent,
				interrupt: pending.kind === "standard" ? pending.interrupts[0] : null,
				interrupts: pending.kind === "standard" ? [...pending.interrupts] : [],
				resolve: resolveRef.current,
				cancel: cancelRef.current
			});
		} catch (err) {
			console.error("[CopilotKit] useInterrupt handler threw; result will be null:", err);
			if (!cancelled) setHandlerResult(null);
			return () => {
				cancelled = true;
			};
		}
		if (isPromiseLike(maybePromise)) Promise.resolve(maybePromise).then((resolved) => {
			if (!cancelled) setHandlerResult(resolved);
		}).catch((err) => {
			console.error("[CopilotKit] useInterrupt handler rejected; result will be null:", err);
			if (!cancelled) setHandlerResult(null);
		});
		else setHandlerResult(maybePromise);
		return () => {
			cancelled = true;
		};
	}, [pending]);
	const element = useMemo(() => {
		if (!pending) return null;
		const legacyEvent = toLegacyEvent(pending);
		if (!isEnabled(legacyEvent)) return null;
		return renderRef.current({
			event: legacyEvent,
			interrupt: pending.kind === "standard" ? pending.interrupts[0] : null,
			interrupts: pending.kind === "standard" ? [...pending.interrupts] : [],
			result: handlerResult,
			resolve,
			cancel
		});
	}, [
		pending,
		handlerResult,
		resolve,
		cancel
	]);
	useEffect(() => {
		if (config.renderInChat === false) return;
		copilotkit.setInterruptElement(element);
	}, [
		element,
		config.renderInChat,
		copilotkit
	]);
	useEffect(() => {
		if (config.renderInChat === false) return;
		return () => {
			copilotkit.setInterruptElement(null);
		};
	}, []);
	if (config.renderInChat === false) return element;
}

//#endregion
//#region src/v2/hooks/use-suggestions.tsx
function useSuggestions({ agentId } = {}) {
	const { copilotkit } = useCopilotKit$1();
	const config = useCopilotChatConfiguration();
	const resolvedAgentId = useMemo(() => agentId ?? config?.agentId ?? DEFAULT_AGENT_ID, [agentId, config?.agentId]);
	const [suggestions, setSuggestions] = useState(() => {
		return copilotkit.getSuggestions(resolvedAgentId).suggestions;
	});
	const [isLoading, setIsLoading] = useState(() => {
		return copilotkit.getSuggestions(resolvedAgentId).isLoading;
	});
	useEffect(() => {
		const result = copilotkit.getSuggestions(resolvedAgentId);
		setSuggestions(result.suggestions);
		setIsLoading(result.isLoading);
	}, [copilotkit, resolvedAgentId]);
	useEffect(() => {
		const subscription = copilotkit.subscribe({
			onSuggestionsChanged: ({ agentId: changedAgentId, suggestions }) => {
				if (changedAgentId !== resolvedAgentId) return;
				setSuggestions(suggestions);
			},
			onSuggestionsStartedLoading: ({ agentId: changedAgentId }) => {
				if (changedAgentId !== resolvedAgentId) return;
				setIsLoading(true);
			},
			onSuggestionsFinishedLoading: ({ agentId: changedAgentId }) => {
				if (changedAgentId !== resolvedAgentId) return;
				setIsLoading(false);
			},
			onSuggestionsConfigChanged: () => {
				const result = copilotkit.getSuggestions(resolvedAgentId);
				setSuggestions(result.suggestions);
				setIsLoading(result.isLoading);
			}
		});
		return () => {
			subscription.unsubscribe();
		};
	}, [copilotkit, resolvedAgentId]);
	return {
		suggestions,
		reloadSuggestions: useCallback(() => {
			copilotkit.reloadSuggestions(resolvedAgentId);
		}, [copilotkit, resolvedAgentId]),
		clearSuggestions: useCallback(() => {
			copilotkit.clearSuggestions(resolvedAgentId);
		}, [copilotkit, resolvedAgentId]),
		isLoading
	};
}

//#endregion
//#region src/v2/hooks/use-configure-suggestions.tsx
function useConfigureSuggestions(config, deps) {
	const { copilotkit } = useCopilotKit$1();
	const chatConfig = useCopilotChatConfiguration();
	const extraDeps = deps ?? [];
	const resolvedConsumerAgentId = useMemo(() => chatConfig?.agentId ?? DEFAULT_AGENT_ID, [chatConfig?.agentId]);
	const rawConsumerAgentId = useMemo(() => config ? config.consumerAgentId : void 0, [config]);
	const normalizationCacheRef = useRef({
		serialized: null,
		config: null
	});
	const { normalizedConfig, serializedConfig } = useMemo(() => {
		if (!config) {
			normalizationCacheRef.current = {
				serialized: null,
				config: null
			};
			return {
				normalizedConfig: null,
				serializedConfig: null
			};
		}
		if (config.available === "disabled") {
			normalizationCacheRef.current = {
				serialized: null,
				config: null
			};
			return {
				normalizedConfig: null,
				serializedConfig: null
			};
		}
		let built;
		if (isDynamicConfig(config)) built = { ...config };
		else {
			const normalizedSuggestions = normalizeStaticSuggestions(config.suggestions);
			built = {
				...config,
				suggestions: normalizedSuggestions
			};
		}
		const serialized = JSON.stringify(built);
		const cache = normalizationCacheRef.current;
		if (cache.serialized === serialized && cache.config) return {
			normalizedConfig: cache.config,
			serializedConfig: serialized
		};
		normalizationCacheRef.current = {
			serialized,
			config: built
		};
		return {
			normalizedConfig: built,
			serializedConfig: serialized
		};
	}, [
		config,
		resolvedConsumerAgentId,
		...extraDeps
	]);
	const latestConfigRef = useRef(null);
	latestConfigRef.current = normalizedConfig;
	const previousSerializedConfigRef = useRef(null);
	const targetAgentId = useMemo(() => {
		if (!normalizedConfig) return resolvedConsumerAgentId;
		const consumer = normalizedConfig.consumerAgentId;
		if (!consumer || consumer === "*") return resolvedConsumerAgentId;
		return consumer;
	}, [normalizedConfig, resolvedConsumerAgentId]);
	const isGlobalConfig = rawConsumerAgentId === void 0 || rawConsumerAgentId === "*";
	const isDynamicConfigType = useMemo(() => !!normalizedConfig && "instructions" in normalizedConfig, [normalizedConfig]);
	const requestReload = useCallback(() => {
		if (!normalizedConfig) return;
		if (isGlobalConfig) {
			const seen = /* @__PURE__ */ new Set();
			const agents = Object.values(copilotkit.agents ?? {});
			for (const entry of agents) {
				const agentId = entry.agentId;
				if (!agentId) continue;
				seen.add(agentId);
				if (!entry.isRunning) copilotkit.reloadSuggestions(agentId);
			}
			if (targetAgentId && !seen.has(targetAgentId)) copilotkit.reloadSuggestions(targetAgentId);
			return;
		}
		if (!targetAgentId) return;
		copilotkit.reloadSuggestions(targetAgentId);
	}, [
		copilotkit,
		isGlobalConfig,
		normalizedConfig,
		targetAgentId
	]);
	useEffect(() => {
		if (!serializedConfig || !latestConfigRef.current) return;
		const id = copilotkit.addSuggestionsConfig(latestConfigRef.current);
		requestReload();
		return () => {
			copilotkit.removeSuggestionsConfig(id);
		};
	}, [
		copilotkit,
		serializedConfig,
		requestReload
	]);
	useEffect(() => {
		if (!normalizedConfig) {
			previousSerializedConfigRef.current = null;
			return;
		}
		if (serializedConfig && previousSerializedConfigRef.current === serializedConfig) return;
		if (serializedConfig) previousSerializedConfigRef.current = serializedConfig;
		requestReload();
	}, [
		normalizedConfig,
		requestReload,
		serializedConfig
	]);
	useEffect(() => {
		if (!normalizedConfig || extraDeps.length === 0) return;
		requestReload();
	}, [
		extraDeps.length,
		normalizedConfig,
		requestReload,
		...extraDeps
	]);
	useEffect(() => {
		if (!normalizedConfig || !isDynamicConfigType) return;
		if (!targetAgentId) return;
		if (!!copilotkit.getAgent(targetAgentId)) return;
		const subscription = copilotkit.subscribe({ onAgentsChanged: () => {
			if (copilotkit.getAgent(targetAgentId)) {
				requestReload();
				subscription.unsubscribe();
			}
		} });
		return () => {
			subscription.unsubscribe();
		};
	}, [
		copilotkit,
		normalizedConfig,
		isDynamicConfigType,
		targetAgentId,
		requestReload
	]);
}
function isDynamicConfig(config) {
	return "instructions" in config;
}
function normalizeStaticSuggestions(suggestions) {
	return suggestions.map((suggestion) => ({
		...suggestion,
		isLoading: suggestion.isLoading ?? false
	}));
}

//#endregion
//#region src/v2/hooks/use-agent-context.tsx
function useAgentContext(context) {
	const { description, value } = context;
	const { copilotkit } = useCopilotKit$1();
	const stringValue = useMemo(() => {
		if (typeof value === "string") return value;
		return JSON.stringify(value);
	}, [value]);
	useLayoutEffect(() => {
		if (!copilotkit) return;
		const id = copilotkit.addContext({
			description,
			value: stringValue
		});
		return () => {
			copilotkit.removeContext(id);
		};
	}, [
		description,
		stringValue,
		copilotkit
	]);
}

//#endregion
//#region src/v2/hooks/use-threads.tsx
function useThreadStoreSelector(store, selector) {
	return useSyncExternalStore(useCallback((onStoreChange) => {
		const subscription = store.select(selector).subscribe(onStoreChange);
		return () => subscription.unsubscribe();
	}, [store, selector]), () => selector(store.getState()), () => selector(store.getServerState()));
}
/**
* React hook for listing and managing Intelligence platform threads.
*
* On mount the hook fetches the thread list for the runtime-authenticated user
* and the given `agentId`. When the Intelligence platform exposes a WebSocket
* URL, it also opens a realtime subscription so the `threads` array stays
* current without polling — thread creates, renames, archives, and deletes
* from any client are reflected immediately.
*
* Mutation methods (`renameThread`, `archiveThread`, `unarchiveThread`,
* `deleteThread`) return promises that resolve once the platform confirms the
* operation and reject with an `Error` on failure.
*
* @param input - Agent identifier and optional list controls.
* @returns Thread list state and stable mutation callbacks.
*
* @example
* ```tsx
* import { useThreads } from "@copilotkit/react-core";
*
* function ThreadList() {
*   const { threads, isLoading, renameThread, deleteThread } = useThreads({
*     agentId: "agent-1",
*   });
*
*   if (isLoading) return <p>Loading…</p>;
*
*   return (
*     <ul>
*       {threads.map((t) => (
*         <li key={t.id}>
*           {t.name ?? "Untitled"}
*           <button onClick={() => renameThread(t.id, "New name")}>Rename</button>
*           <button onClick={() => deleteThread(t.id)}>Delete</button>
*         </li>
*       ))}
*     </ul>
*   );
* }
* ```
*/
function useThreads({ agentId, includeArchived, limit, enabled = true }) {
	const { copilotkit } = useCopilotKit$1();
	const [store] = useState(() => ɵcreateThreadStore({ fetch: globalThis.fetch }));
	const coreThreads = useThreadStoreSelector(store, ɵselectThreads);
	const threads = useMemo(() => coreThreads.map(({ id, agentId, name, archived, createdAt, updatedAt, lastRunAt }) => ({
		id,
		agentId,
		name,
		archived,
		createdAt,
		updatedAt,
		...lastRunAt !== void 0 ? { lastRunAt } : {}
	})), [coreThreads]);
	const storeIsLoading = useThreadStoreSelector(store, ɵselectThreadsIsLoading);
	const storeError = useThreadStoreSelector(store, ɵselectThreadsError);
	const fetchMoreError = useThreadStoreSelector(store, ɵselectFetchMoreError);
	const hasMoreThreads = useThreadStoreSelector(store, ɵselectHasNextPage);
	const isFetchingMoreThreads = useThreadStoreSelector(store, ɵselectIsFetchingNextPage);
	const isMutating = useThreadStoreSelector(store, ɵselectIsMutating);
	const headersKey = useMemo(() => {
		return JSON.stringify(Object.entries(copilotkit.headers ?? {}).sort(([left], [right]) => left.localeCompare(right)));
	}, [copilotkit.headers]);
	const runtimeStatus = copilotkit.runtimeConnectionStatus;
	const threadListEndpointSupported = copilotkit.threadEndpoints?.list !== false;
	const threadMutationsSupported = copilotkit.threadEndpoints?.mutations !== false;
	const threadEndpointsUnavailable = !!copilotkit.runtimeUrl && runtimeStatus === CopilotKitCoreRuntimeConnectionStatus.Connected && !threadListEndpointSupported;
	const runtimeError = useMemo(() => {
		if (copilotkit.runtimeUrl) return null;
		return /* @__PURE__ */ new Error("Runtime URL is not configured");
	}, [copilotkit.runtimeUrl]);
	const threadEndpointsError = useMemo(() => {
		if (!threadEndpointsUnavailable) return null;
		return /* @__PURE__ */ new Error("Thread endpoints are not available on this CopilotKit runtime");
	}, [threadEndpointsUnavailable]);
	const threadMutationsError = useMemo(() => {
		if (threadMutationsSupported) return null;
		return /* @__PURE__ */ new Error("Thread mutations are not available on this CopilotKit runtime");
	}, [threadMutationsSupported]);
	const [hasDispatchedContext, setHasDispatchedContext] = useState(false);
	const preConnectLoading = enabled && !!copilotkit.runtimeUrl && !threadEndpointsUnavailable && !hasDispatchedContext;
	const [configErrorDismissed, setConfigErrorDismissed] = useState(false);
	useEffect(() => {
		setConfigErrorDismissed(false);
	}, [runtimeError, threadEndpointsError]);
	const activeRuntimeError = configErrorDismissed ? null : runtimeError;
	const activeThreadEndpointsError = configErrorDismissed ? null : threadEndpointsError;
	const isLoading = activeRuntimeError || activeThreadEndpointsError ? false : preConnectLoading || storeIsLoading;
	const error = activeRuntimeError ?? activeThreadEndpointsError ?? storeError;
	const listError = storeError;
	useEffect(() => {
		store.start();
		return () => {
			store.stop();
		};
	}, [store]);
	useEffect(() => {
		if (!enabled) return;
		copilotkit.registerThreadStore(agentId, store);
		return () => {
			copilotkit.unregisterThreadStore(agentId);
		};
	}, [
		copilotkit,
		agentId,
		store,
		enabled
	]);
	useEffect(() => {
		if (!enabled) {
			store.setContext(null);
			setHasDispatchedContext(false);
			return;
		}
		if (!copilotkit.runtimeUrl) {
			store.setContext(null);
			setHasDispatchedContext(false);
			return;
		}
		if (runtimeStatus !== CopilotKitCoreRuntimeConnectionStatus.Connected) return;
		if (!threadListEndpointSupported) {
			store.setContext(null);
			setHasDispatchedContext(false);
			return;
		}
		const context = {
			runtimeUrl: copilotkit.runtimeUrl,
			headers: { ...copilotkit.headers },
			wsUrl: copilotkit.intelligence?.wsUrl,
			agentId,
			includeArchived,
			limit
		};
		store.setContext(context);
		setHasDispatchedContext(true);
	}, [
		store,
		enabled,
		copilotkit.runtimeUrl,
		runtimeStatus,
		headersKey,
		copilotkit.intelligence?.wsUrl,
		threadListEndpointSupported,
		agentId,
		includeArchived,
		limit
	]);
	const guardMutation = useCallback((mutation) => {
		return (...args) => {
			if (threadMutationsError) return Promise.reject(threadMutationsError);
			return mutation(...args);
		};
	}, [threadMutationsError]);
	const renameThread = useMemo(() => guardMutation((threadId, name) => store.renameThread(threadId, name)), [store, guardMutation]);
	const archiveThread = useMemo(() => guardMutation((threadId) => store.archiveThread(threadId)), [store, guardMutation]);
	const unarchiveThread = useMemo(() => guardMutation((threadId) => store.unarchiveThread(threadId)), [store, guardMutation]);
	const deleteThread = useMemo(() => guardMutation((threadId) => store.deleteThread(threadId)), [store, guardMutation]);
	return {
		threads,
		isLoading,
		error,
		listError,
		fetchMoreError,
		hasMoreThreads,
		isFetchingMoreThreads,
		isMutating,
		fetchMoreThreads: useCallback(() => store.fetchNextPage(), [store]),
		refetchThreads: useCallback(() => store.refetchThreads(), [store]),
		startNewThread: useCallback(() => {
			setConfigErrorDismissed(true);
			store.startNewThread();
		}, [store]),
		renameThread,
		archiveThread,
		unarchiveThread,
		deleteThread
	};
}

//#endregion
//#region src/v2/types/defineToolCallRenderer.ts
function defineToolCallRenderer(def) {
	const argsSchema = def.name === "*" && !def.args ? z.any() : def.args;
	return {
		name: def.name,
		args: argsSchema,
		render: def.render,
		...def.agentId ? { agentId: def.agentId } : {}
	};
}

//#endregion
//#region src/v2/hooks/use-render-tool.tsx
const EMPTY_DEPS = [];
/**
* Registers a renderer entry in CopilotKit's `renderToolCalls` registry.
*
* Key behavior:
* - deduplicates by `agentId:name` (latest registration wins),
* - keeps renderer entries on cleanup so historical chat tool calls can still render,
* - refreshes registration when `deps` change.
*
* @typeParam S - Schema type describing tool call parameters.
* @param config - Renderer config for wildcard or named tools.
* @param deps - Optional dependencies to refresh registration.
*
* @example
* ```tsx
* useRenderTool(
*   {
*     name: "searchDocs",
*     parameters: z.object({ query: z.string() }),
*     render: ({ status, parameters, result }) => {
*       if (status === "executing") return <div>Searching {parameters.query}</div>;
*       if (status === "complete") return <div>{result}</div>;
*       return <div>Preparing...</div>;
*     },
*   },
*   [],
* );
* ```
*
* @example
* ```tsx
* useRenderTool(
*   {
*     name: "summarize",
*     parameters: z.object({ text: z.string() }),
*     agentId: "research-agent",
*     render: ({ name, status }) => <div>{name}: {status}</div>,
*   },
*   [selectedAgentId],
* );
* ```
*/
function useRenderTool(config, deps) {
	const { copilotkit } = useCopilotKit$1();
	const extraDeps = deps ?? EMPTY_DEPS;
	useEffect(() => {
		const renderer = config.name === "*" && !config.parameters ? defineToolCallRenderer({
			name: "*",
			render: (props) => config.render({
				...props,
				parameters: props.args
			}),
			...config.agentId ? { agentId: config.agentId } : {}
		}) : defineToolCallRenderer({
			name: config.name,
			args: config.parameters,
			render: (props) => {
				if (props.status === ToolCallStatus.InProgress) return config.render({
					...props,
					parameters: props.args
				});
				if (props.status === ToolCallStatus.Executing) return config.render({
					...props,
					parameters: props.args
				});
				return config.render({
					...props,
					parameters: props.args
				});
			},
			...config.agentId ? { agentId: config.agentId } : {}
		});
		copilotkit.addHookRenderToolCall(renderer);
	}, [
		config.name,
		copilotkit,
		JSON.stringify(extraDeps)
	]);
}

//#endregion
//#region src/v2/hooks/use-render-tool-call.tsx
/**
* Memoized component that renders a single tool call.
* This prevents unnecessary re-renders when parent components update
* but the tool call data hasn't changed.
*/
const ToolCallRenderer = React.memo(function ToolCallRenderer({ toolCall, toolMessage, RenderComponent, isExecuting }) {
	const args = useMemo(() => partialJSONParse(toolCall.function.arguments), [toolCall.function.arguments]);
	const toolName = toolCall.function.name;
	if (toolMessage) return /* @__PURE__ */ jsx(RenderComponent, {
		name: toolName,
		toolCallId: toolCall.id,
		args,
		status: ToolCallStatus.Complete,
		result: toolMessage.content
	});
	else if (isExecuting) return /* @__PURE__ */ jsx(RenderComponent, {
		name: toolName,
		toolCallId: toolCall.id,
		args,
		status: ToolCallStatus.Executing,
		result: void 0
	});
	else return /* @__PURE__ */ jsx(RenderComponent, {
		name: toolName,
		toolCallId: toolCall.id,
		args,
		status: ToolCallStatus.InProgress,
		result: void 0
	});
}, (prevProps, nextProps) => {
	if (prevProps.toolCall.id !== nextProps.toolCall.id) return false;
	if (prevProps.toolCall.function.name !== nextProps.toolCall.function.name) return false;
	if (prevProps.toolCall.function.arguments !== nextProps.toolCall.function.arguments) return false;
	if (prevProps.toolMessage?.content !== nextProps.toolMessage?.content) return false;
	if (prevProps.isExecuting !== nextProps.isExecuting) return false;
	if (prevProps.RenderComponent !== nextProps.RenderComponent) return false;
	return true;
});
/**
* Hook that returns a function to render tool calls based on the render functions
* defined in CopilotKitProvider.
*
* @returns A function that takes a tool call and optional tool message and returns the rendered component
*/
function useRenderToolCall() {
	const { copilotkit, executingToolCallIds } = useCopilotKit$1();
	const agentId = useCopilotChatConfiguration()?.agentId ?? DEFAULT_AGENT_ID;
	const renderToolCalls = useSyncExternalStore((callback) => {
		return copilotkit.subscribe({ onRenderToolCallsChanged: callback }).unsubscribe;
	}, () => copilotkit.renderToolCalls, () => copilotkit.renderToolCalls);
	return useCallback(({ toolCall, toolMessage }) => {
		const exactMatches = renderToolCalls.filter((rc) => rc.name === toolCall.function.name);
		const renderConfig = exactMatches.find((rc) => rc.agentId === agentId) || exactMatches.find((rc) => !rc.agentId) || exactMatches[0] || renderToolCalls.find((rc) => rc.name === "*");
		if (!renderConfig) return null;
		const RenderComponent = renderConfig.render;
		return /* @__PURE__ */ jsx(ToolCallRenderer, {
			toolCall,
			toolMessage,
			RenderComponent,
			isExecuting: executingToolCallIds.has(toolCall.id)
		}, toolCall.id);
	}, [
		renderToolCalls,
		executingToolCallIds,
		agentId
	]);
}

//#endregion
//#region src/v2/hooks/use-capabilities.tsx
/**
* Returns the capabilities declared by the given agent (or the agent resolved
* from the surrounding chat configuration, falling back to the default agent).
* Capabilities are populated from the runtime `/info` response at connection
* time. The hook reads them synchronously from the agent instance — there is
* no separate loading state, but the value will be `undefined` until the
* runtime handshake completes.
*
* @param agentId - Optional agent ID. If omitted, inherits the surrounding
*          chat configuration's agent, falling back to the default agent.
* @returns The agent's capabilities, or `undefined` if the agent doesn't
*          declare capabilities.
*/
function useCapabilities(agentId) {
	const { agent } = useAgent({ agentId });
	if (agent && "capabilities" in agent) return agent.capabilities;
}

//#endregion
export { CopilotChatConfigurationProvider, CopilotChatDefaultLabels, CopilotKitCoreReact, UseAgentUpdate, defineToolCallRenderer, useAgent, useAgentContext, useCapabilities, useComponent, useConfigureSuggestions, useCopilotChatConfiguration, useCopilotKit, useFrontendTool, useHumanInTheLoop, useInterrupt, useRenderTool, useRenderToolCall, useSuggestions, useThreads };
//# sourceMappingURL=headless.mjs.map