@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;" />
12,496 lines • 492 kB
JavaScript
//#region \0rolldown/runtime.js
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
key = keys[i];
if (!__hasOwnProp.call(to, key) && key !== except) {
__defProp(to, key, {
get: ((k) => from[k]).bind(null, key),
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
});
}
}
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
value: mod,
enumerable: true
}) : target, mod));
//#endregion
let react = require("react");
react = __toESM(react);
let _copilotkit_core = require("@copilotkit/core");
let _ag_ui_client = require("@ag-ui/client");
let tailwind_merge = require("tailwind-merge");
let lucide_react = require("lucide-react");
let _copilotkit_shared = require("@copilotkit/shared");
let react_jsx_runtime = require("react/jsx-runtime");
let _radix_ui_react_slot = require("@radix-ui/react-slot");
let class_variance_authority = require("class-variance-authority");
let clsx = require("clsx");
let _radix_ui_react_tooltip = require("@radix-ui/react-tooltip");
_radix_ui_react_tooltip = __toESM(_radix_ui_react_tooltip);
let _radix_ui_react_dropdown_menu = require("@radix-ui/react-dropdown-menu");
_radix_ui_react_dropdown_menu = __toESM(_radix_ui_react_dropdown_menu);
let streamdown = require("streamdown");
let _copilotkit_react_core_v2_context = require("@copilotkit/react-core/v2/context");
let zod = require("zod");
let _lit_labs_react = require("@lit-labs/react");
let _copilotkit_a2ui_renderer = require("@copilotkit/a2ui-renderer");
let zod_to_json_schema = require("zod-to-json-schema");
let react_dom = require("react-dom");
let _tanstack_react_virtual = require("@tanstack/react-virtual");
let use_stick_to_bottom = require("use-stick-to-bottom");
let _copilotkit_web_components_threads_drawer = require("@copilotkit/web-components/threads-drawer");
let react_markdown = require("react-markdown");
react_markdown = __toESM(react_markdown);
//#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 = (0, react.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 = (0, react.createContext)(null);
const CopilotChatConfigurationProvider = ({ children, labels, agentId, threadId, hasExplicitThreadId, isModalDefaultOpen }) => {
const parentConfig = (0, react.useContext)(CopilotChatConfiguration);
const stableLabels = useShallowStableRef(labels);
const mergedLabels = (0, react.useMemo)(() => ({
...CopilotChatDefaultLabels,
...parentConfig?.labels,
...stableLabels
}), [stableLabels, parentConfig?.labels]);
const resolvedAgentId = agentId ?? parentConfig?.agentId ?? _copilotkit_shared.DEFAULT_AGENT_ID;
const threadIdPropIsAuthoritative = threadId !== void 0 && hasExplicitThreadId !== false;
const isThreadIdControlled = threadIdPropIsAuthoritative;
const [activeThreadOverride, setActiveThreadOverride] = (0, react.useState)(null);
const resolvedThreadId = (0, react.useMemo)(() => {
if (threadIdPropIsAuthoritative) return threadId;
if (activeThreadOverride) return activeThreadOverride.threadId;
if (parentConfig?.threadId) return parentConfig.threadId;
if (threadId) return threadId;
return (0, _copilotkit_shared.randomUUID)();
}, [
threadIdPropIsAuthoritative,
threadId,
parentConfig?.threadId,
activeThreadOverride
]);
const resolvedHasExplicitThreadId = (threadIdPropIsAuthoritative ? true : activeThreadOverride?.explicit ?? hasExplicitThreadId ?? false) || !!parentConfig?.hasExplicitThreadId;
const [internalModalOpen, setInternalModalOpen] = (0, react.useState)(isModalDefaultOpen ?? true);
const hasExplicitDefault = isModalDefaultOpen !== void 0;
const setAndSync = (0, react.useCallback)((open) => {
setInternalModalOpen(open);
parentConfig?.setModalOpen(open);
}, [parentConfig?.setModalOpen]);
const isMounted = (0, react.useRef)(false);
(0, react.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] = (0, react.useState)(false);
const [ownDrawerCount, setOwnDrawerCount] = (0, react.useState)(0);
const modalCloseRef = (0, react.useRef)(() => {});
modalCloseRef.current = resolvedSetModalOpen;
const registeredModalClosersRef = (0, react.useRef)([]);
const ownRegisterModalCloser = (0, react.useCallback)((closeModal) => {
registeredModalClosersRef.current.push(closeModal);
return () => {
registeredModalClosersRef.current = registeredModalClosersRef.current.filter((entry) => entry !== closeModal);
};
}, []);
const ownSetDrawerOpen = (0, react.useCallback)((open) => {
setOwnDrawerOpen(open);
if (open && isMobileViewport()) {
const registered = registeredModalClosersRef.current;
(registered.length > 0 ? registered[registered.length - 1] : modalCloseRef.current)(false);
}
}, []);
const ownRegisterDrawer = (0, react.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;
(0, react.useEffect)(() => {
if (!hasExplicitDefault) return;
return resolvedRegisterModalCloser(resolvedSetModalOpen);
}, [
hasExplicitDefault,
resolvedRegisterModalCloser,
resolvedSetModalOpen
]);
const isThreadIdControlledRef = (0, react.useRef)(isThreadIdControlled);
isThreadIdControlledRef.current = isThreadIdControlled;
const ownSetActiveThreadId = (0, react.useCallback)((id, options) => {
setActiveThreadOverride({
threadId: id,
explicit: options?.explicit ?? true
});
}, []);
const ownStartNewThread = (0, react.useCallback)(() => {
setActiveThreadOverride({
threadId: (0, _copilotkit_shared.randomUUID)(),
explicit: false
});
}, []);
const parentSetActiveThreadId = parentConfig?.setActiveThreadId;
const parentStartNewThread = parentConfig?.startNewThread;
const resolvedSetActiveThreadId = (0, react.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 = (0, react.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 = (0, react.useCallback)((open) => {
if (open && isMobileViewport()) resolvedSetDrawerOpen(false);
resolvedSetModalOpen(open);
}, [resolvedSetModalOpen, resolvedSetDrawerOpen]);
const configurationValue = (0, react.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__ */ (0, react_jsx_runtime.jsx)(CopilotChatConfiguration.Provider, {
value: configurationValue,
children
});
};
const useCopilotChatConfiguration = () => {
return (0, react.useContext)(CopilotChatConfiguration);
};
//#endregion
//#region src/v2/lib/utils.ts
const twMerge$9 = (0, tailwind_merge.extendTailwindMerge)({ prefix: "cpk" });
function cn(...inputs) {
return twMerge$9((0, clsx.clsx)(inputs));
}
//#endregion
//#region src/v2/components/ui/button.tsx
const buttonVariants = (0, class_variance_authority.cva)("cpk:inline-flex cpk:items-center cpk:justify-center cpk:gap-2 cpk:whitespace-nowrap cpk:rounded-md cpk:text-sm cpk:font-medium cpk:transition-all cpk:disabled:pointer-events-none cpk:disabled:opacity-50 cpk:[&_svg]:pointer-events-none cpk:[&_svg:not([class*='size-'])]:size-4 cpk:shrink-0 cpk:[&_svg]:shrink-0 cpk:outline-none cpk:focus-visible:border-ring cpk:focus-visible:ring-ring/50 cpk:focus-visible:ring-[3px] cpk:aria-invalid:ring-destructive/20 cpk:dark:aria-invalid:ring-destructive/40 cpk:aria-invalid:border-destructive", {
variants: {
variant: {
default: "cpk:bg-primary cpk:text-primary-foreground cpk:shadow-xs cpk:hover:bg-primary/90",
destructive: "cpk:bg-destructive cpk:text-white cpk:shadow-xs cpk:hover:bg-destructive/90 cpk:focus-visible:ring-destructive/20 cpk:dark:focus-visible:ring-destructive/40 cpk:dark:bg-destructive/60",
outline: "cpk:border cpk:bg-background cpk:shadow-xs cpk:hover:bg-accent cpk:hover:text-accent-foreground cpk:dark:bg-input/30 cpk:dark:border-input cpk:dark:hover:bg-input/50",
secondary: "cpk:bg-secondary cpk:text-secondary-foreground cpk:shadow-xs cpk:hover:bg-secondary/80",
ghost: "cpk:hover:bg-accent cpk:hover:text-accent-foreground cpk:dark:hover:bg-accent/50 cpk:cursor-pointer",
link: "cpk:text-primary cpk:underline-offset-4 cpk:hover:underline",
assistantMessageToolbarButton: [
"cpk:cursor-pointer",
"cpk:p-0 cpk:text-[rgb(93,93,93)] cpk:hover:bg-[#E8E8E8]",
"cpk:dark:text-[rgb(243,243,243)] cpk:dark:hover:bg-[#303030]",
"cpk:h-8 cpk:w-8",
"cpk:transition-colors",
"cpk:hover:text-[rgb(93,93,93)]",
"cpk:dark:hover:text-[rgb(243,243,243)]"
],
chatInputToolbarPrimary: [
"cpk:cursor-pointer",
"cpk:bg-black cpk:text-white",
"cpk:dark:bg-white cpk:dark:text-black cpk:dark:focus-visible:outline-white",
"cpk:rounded-full",
"cpk:transition-colors",
"cpk:focus:outline-none",
"cpk:hover:opacity-70 cpk:disabled:hover:opacity-100",
"cpk:disabled:cursor-not-allowed cpk:disabled:bg-[#00000014] cpk:disabled:text-[rgb(13,13,13)]",
"cpk:dark:disabled:bg-[#454545] cpk:dark:disabled:text-white "
],
chatInputToolbarSecondary: [
"cpk:cursor-pointer",
"cpk:bg-transparent cpk:text-[#444444]",
"cpk:dark:text-white cpk:dark:border-[#404040]",
"cpk:rounded-full",
"cpk:transition-colors",
"cpk:focus:outline-none",
"cpk:hover:bg-[#f8f8f8] cpk:hover:text-[#333333]",
"cpk:dark:hover:bg-[#404040] cpk:dark:hover:text-[#FFFFFF]",
"cpk:disabled:cursor-not-allowed cpk:disabled:opacity-50",
"cpk:disabled:hover:bg-transparent cpk:disabled:hover:text-[#444444]",
"cpk:dark:disabled:hover:bg-transparent cpk:dark:disabled:hover:text-[#CCCCCC]"
]
},
size: {
default: "cpk:h-9 cpk:px-4 cpk:py-2 cpk:has-[>svg]:px-3",
sm: "cpk:h-8 cpk:rounded-md cpk:gap-1.5 cpk:px-3 cpk:has-[>svg]:px-2.5",
lg: "cpk:h-10 cpk:rounded-md cpk:px-6 cpk:has-[>svg]:px-4",
icon: "cpk:size-9",
chatInputToolbarIcon: ["cpk:h-9 cpk:w-9 cpk:rounded-full"],
chatInputToolbarIconLabel: [
"cpk:h-9 cpk:px-3 cpk:rounded-full",
"cpk:gap-2",
"cpk:font-normal"
]
}
},
defaultVariants: {
variant: "default",
size: "default"
}
});
const Button = react.forwardRef(function Button({ className, variant, size, asChild = false, ...props }, ref) {
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(asChild ? _radix_ui_react_slot.Slot : "button", {
ref,
"data-slot": "button",
className: cn(buttonVariants({
variant,
size,
className
})),
...props
});
});
//#endregion
//#region src/v2/components/ui/tooltip.tsx
function TooltipProvider({ delayDuration = 0, ...props }) {
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_radix_ui_react_tooltip.Provider, {
"data-slot": "tooltip-provider",
delayDuration,
...props
});
}
function Tooltip({ ...props }) {
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(TooltipProvider, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_radix_ui_react_tooltip.Root, {
"data-slot": "tooltip",
...props
}) });
}
const TooltipTrigger = react.forwardRef(function TooltipTrigger({ ...props }, ref) {
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_radix_ui_react_tooltip.Trigger, {
ref,
"data-slot": "tooltip-trigger",
...props
});
});
function TooltipContent({ className, sideOffset = 0, children, ...props }) {
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_radix_ui_react_tooltip.Portal, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_radix_ui_react_tooltip.Content, {
"data-copilotkit": true,
"data-slot": "tooltip-content",
sideOffset,
className: cn("cpk:bg-primary cpk:text-primary-foreground cpk:animate-in cpk:fade-in-0 cpk:zoom-in-95 cpk:data-[state=closed]:animate-out cpk:data-[state=closed]:fade-out-0 cpk:data-[state=closed]:zoom-out-95 cpk:data-[side=bottom]:slide-in-from-top-2 cpk:data-[side=left]:slide-in-from-right-2 cpk:data-[side=right]:slide-in-from-left-2 cpk:data-[side=top]:slide-in-from-bottom-2 cpk:z-50 cpk:w-fit cpk:origin-(--radix-tooltip-content-transform-origin) cpk:rounded-md cpk:px-3 cpk:py-1.5 cpk:text-xs cpk:text-balance", className),
...props,
children: [children, /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_radix_ui_react_tooltip.Arrow, { className: "cpk:bg-primary cpk:fill-primary cpk:z-50 cpk:size-2.5 cpk:translate-y-[calc(-50%_-_2px)] cpk:rotate-45 cpk:rounded-[2px]" })]
}) });
}
//#endregion
//#region src/v2/components/ui/dropdown-menu.tsx
function DropdownMenu({ ...props }) {
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_radix_ui_react_dropdown_menu.Root, {
"data-slot": "dropdown-menu",
...props
});
}
const DropdownMenuTrigger = react.forwardRef(function DropdownMenuTrigger({ ...props }, ref) {
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_radix_ui_react_dropdown_menu.Trigger, {
ref,
"data-slot": "dropdown-menu-trigger",
...props
});
});
function DropdownMenuContent({ className, sideOffset = 4, ...props }) {
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_radix_ui_react_dropdown_menu.Portal, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_radix_ui_react_dropdown_menu.Content, {
"data-copilotkit": true,
"data-slot": "dropdown-menu-content",
sideOffset,
className: cn("cpk:bg-popover cpk:text-popover-foreground cpk:data-[state=open]:animate-in cpk:data-[state=closed]:animate-out cpk:data-[state=closed]:fade-out-0 cpk:data-[state=open]:fade-in-0 cpk:data-[state=closed]:zoom-out-95 cpk:data-[state=open]:zoom-in-95 cpk:data-[side=bottom]:slide-in-from-top-2 cpk:data-[side=left]:slide-in-from-right-2 cpk:data-[side=right]:slide-in-from-left-2 cpk:data-[side=top]:slide-in-from-bottom-2 cpk:z-50 cpk:max-h-(--radix-dropdown-menu-content-available-height) cpk:min-w-[8rem] cpk:origin-(--radix-dropdown-menu-content-transform-origin) cpk:overflow-x-hidden cpk:overflow-y-auto cpk:rounded-md cpk:border cpk:p-1 cpk:shadow-md", className),
...props
}) });
}
function DropdownMenuItem({ className, inset, variant = "default", ...props }) {
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_radix_ui_react_dropdown_menu.Item, {
"data-slot": "dropdown-menu-item",
"data-inset": inset,
"data-variant": variant,
className: cn("cpk:focus:bg-accent cpk:focus:text-accent-foreground cpk:data-[variant=destructive]:text-destructive cpk:data-[variant=destructive]:focus:bg-destructive/10 cpk:dark:data-[variant=destructive]:focus:bg-destructive/20 cpk:data-[variant=destructive]:focus:text-destructive cpk:data-[variant=destructive]:*:[svg]:!text-destructive cpk:[&_svg:not([class*='text-'])]:text-muted-foreground cpk:relative cpk:flex cpk:cursor-default cpk:items-center cpk:gap-2 cpk:rounded-sm cpk:px-2 cpk:py-1.5 cpk:text-sm cpk:outline-hidden cpk:select-none cpk:data-[disabled]:pointer-events-none cpk:data-[disabled]:opacity-50 cpk:data-[inset]:pl-8 cpk:[&_svg]:pointer-events-none cpk:[&_svg]:shrink-0 cpk:[&_svg:not([class*='size-'])]:size-4", className),
...props
});
}
function DropdownMenuSeparator({ className, ...props }) {
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_radix_ui_react_dropdown_menu.Separator, {
"data-slot": "dropdown-menu-separator",
className: cn("cpk:bg-border cpk:-mx-1 cpk:my-1 cpk:h-px", className),
...props
});
}
function DropdownMenuSub({ ...props }) {
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_radix_ui_react_dropdown_menu.Sub, {
"data-slot": "dropdown-menu-sub",
...props
});
}
function DropdownMenuSubTrigger({ className, inset, children, ...props }) {
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_radix_ui_react_dropdown_menu.SubTrigger, {
"data-slot": "dropdown-menu-sub-trigger",
"data-inset": inset,
className: cn("cpk:focus:bg-accent cpk:focus:text-accent-foreground cpk:data-[state=open]:bg-accent cpk:data-[state=open]:text-accent-foreground cpk:flex cpk:cursor-default cpk:items-center cpk:rounded-sm cpk:px-2 cpk:py-1.5 cpk:text-sm cpk:outline-hidden cpk:select-none cpk:data-[inset]:pl-8", className),
...props,
children: [children, /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.ChevronRightIcon, { className: "cpk:ml-auto cpk:size-4" })]
});
}
function DropdownMenuSubContent({ className, ...props }) {
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_radix_ui_react_dropdown_menu.SubContent, {
"data-slot": "dropdown-menu-sub-content",
className: cn("cpk:bg-popover cpk:text-popover-foreground cpk:data-[state=open]:animate-in cpk:data-[state=closed]:animate-out cpk:data-[state=closed]:fade-out-0 cpk:data-[state=open]:fade-in-0 cpk:data-[state=closed]:zoom-out-95 cpk:data-[state=open]:zoom-in-95 cpk:data-[side=bottom]:slide-in-from-top-2 cpk:data-[side=left]:slide-in-from-right-2 cpk:data-[side=right]:slide-in-from-left-2 cpk:data-[side=top]:slide-in-from-bottom-2 cpk:z-50 cpk:min-w-[8rem] cpk:origin-(--radix-dropdown-menu-content-transform-origin) cpk:overflow-hidden cpk:rounded-md cpk:border cpk:p-1 cpk:shadow-lg", className),
...props
});
}
//#endregion
//#region src/v2/components/chat/CopilotChatAudioRecorder.tsx
/** Error subclass so callers can `instanceof`-guard recorder failures */
var AudioRecorderError = class extends Error {
constructor(message) {
super(message);
this.name = "AudioRecorderError";
}
};
const CopilotChatAudioRecorder = (0, react.forwardRef)((props, ref) => {
const { className, ...divProps } = props;
const canvasRef = (0, react.useRef)(null);
const [recorderState, setRecorderState] = (0, react.useState)("idle");
const mediaRecorderRef = (0, react.useRef)(null);
const audioChunksRef = (0, react.useRef)([]);
const streamRef = (0, react.useRef)(null);
const analyserRef = (0, react.useRef)(null);
const audioContextRef = (0, react.useRef)(null);
const animationIdRef = (0, react.useRef)(null);
const amplitudeHistoryRef = (0, react.useRef)([]);
const frameCountRef = (0, react.useRef)(0);
const scrollOffsetRef = (0, react.useRef)(0);
const smoothedAmplitudeRef = (0, react.useRef)(0);
const fadeOpacityRef = (0, react.useRef)(0);
const cleanup = (0, react.useCallback)(() => {
if (animationIdRef.current) {
cancelAnimationFrame(animationIdRef.current);
animationIdRef.current = null;
}
if (mediaRecorderRef.current && mediaRecorderRef.current.state !== "inactive") try {
mediaRecorderRef.current.stop();
} catch {}
if (streamRef.current) {
streamRef.current.getTracks().forEach((track) => track.stop());
streamRef.current = null;
}
if (audioContextRef.current && audioContextRef.current.state !== "closed") {
audioContextRef.current.close().catch(() => {});
audioContextRef.current = null;
}
mediaRecorderRef.current = null;
analyserRef.current = null;
audioChunksRef.current = [];
amplitudeHistoryRef.current = [];
frameCountRef.current = 0;
scrollOffsetRef.current = 0;
smoothedAmplitudeRef.current = 0;
fadeOpacityRef.current = 0;
}, []);
const start = (0, react.useCallback)(async () => {
if (recorderState !== "idle") throw new AudioRecorderError("Recorder is already active");
try {
const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
streamRef.current = stream;
const audioContext = new AudioContext();
audioContextRef.current = audioContext;
const source = audioContext.createMediaStreamSource(stream);
const analyser = audioContext.createAnalyser();
analyser.fftSize = 2048;
source.connect(analyser);
analyserRef.current = analyser;
const mimeType = MediaRecorder.isTypeSupported("audio/webm;codecs=opus") ? "audio/webm;codecs=opus" : MediaRecorder.isTypeSupported("audio/webm") ? "audio/webm" : MediaRecorder.isTypeSupported("audio/mp4") ? "audio/mp4" : "";
const options = mimeType ? { mimeType } : {};
const mediaRecorder = new MediaRecorder(stream, options);
mediaRecorderRef.current = mediaRecorder;
audioChunksRef.current = [];
mediaRecorder.ondataavailable = (event) => {
if (event.data.size > 0) audioChunksRef.current.push(event.data);
};
mediaRecorder.start(100);
setRecorderState("recording");
} catch (error) {
cleanup();
if (error instanceof Error && error.name === "NotAllowedError") throw new AudioRecorderError("Microphone permission denied");
if (error instanceof Error && error.name === "NotFoundError") throw new AudioRecorderError("No microphone found");
throw new AudioRecorderError(error instanceof Error ? error.message : "Failed to start recording");
}
}, [recorderState, cleanup]);
const stop = (0, react.useCallback)(() => {
return new Promise((resolve, reject) => {
const mediaRecorder = mediaRecorderRef.current;
if (!mediaRecorder || recorderState !== "recording") {
reject(new AudioRecorderError("No active recording"));
return;
}
setRecorderState("processing");
mediaRecorder.onstop = () => {
const mimeType = mediaRecorder.mimeType || "audio/webm";
const audioBlob = new Blob(audioChunksRef.current, { type: mimeType });
cleanup();
setRecorderState("idle");
resolve(audioBlob);
};
mediaRecorder.onerror = () => {
cleanup();
setRecorderState("idle");
reject(new AudioRecorderError("Recording failed"));
};
mediaRecorder.stop();
});
}, [recorderState, cleanup]);
const calculateAmplitude = (dataArray) => {
let sum = 0;
for (let i = 0; i < dataArray.length; i++) {
const sample = (dataArray[i] ?? 128) / 128 - 1;
sum += sample * sample;
}
return Math.sqrt(sum / dataArray.length);
};
(0, react.useEffect)(() => {
const canvas = canvasRef.current;
if (!canvas) return;
const ctx = canvas.getContext("2d");
if (!ctx) return;
const barWidth = 2;
const barSpacing = barWidth + 1;
const scrollSpeed = 1 / 3;
const draw = () => {
const rect = canvas.getBoundingClientRect();
const dpr = window.devicePixelRatio || 1;
if (canvas.width !== rect.width * dpr || canvas.height !== rect.height * dpr) {
canvas.width = rect.width * dpr;
canvas.height = rect.height * dpr;
ctx.scale(dpr, dpr);
}
const maxBars = Math.floor(rect.width / barSpacing) + 2;
if (analyserRef.current && recorderState === "recording") {
if (amplitudeHistoryRef.current.length === 0) amplitudeHistoryRef.current = new Array(maxBars).fill(0);
if (fadeOpacityRef.current < 1) fadeOpacityRef.current = Math.min(1, fadeOpacityRef.current + .03);
scrollOffsetRef.current += scrollSpeed;
const bufferLength = analyserRef.current.fftSize;
const dataArray = new Uint8Array(bufferLength);
analyserRef.current.getByteTimeDomainData(dataArray);
const rawAmplitude = calculateAmplitude(dataArray);
const speed = rawAmplitude > smoothedAmplitudeRef.current ? .12 : .08;
smoothedAmplitudeRef.current += (rawAmplitude - smoothedAmplitudeRef.current) * speed;
if (scrollOffsetRef.current >= barSpacing) {
scrollOffsetRef.current -= barSpacing;
amplitudeHistoryRef.current.push(smoothedAmplitudeRef.current);
if (amplitudeHistoryRef.current.length > maxBars) amplitudeHistoryRef.current = amplitudeHistoryRef.current.slice(-maxBars);
}
}
ctx.clearRect(0, 0, rect.width, rect.height);
ctx.fillStyle = getComputedStyle(canvas).color;
ctx.globalAlpha = fadeOpacityRef.current;
const centerY = rect.height / 2;
const maxAmplitude = rect.height / 2 - 2;
const history = amplitudeHistoryRef.current;
if (history.length > 0) {
const offset = scrollOffsetRef.current;
const edgeFadeWidth = 12;
for (let i = 0; i < history.length; i++) {
const amplitude = history[i] ?? 0;
const scaledAmplitude = Math.min(amplitude * 4, 1);
const barHeight = Math.max(2, scaledAmplitude * maxAmplitude * 2);
const x = rect.width - (history.length - i) * barSpacing - offset;
const y = centerY - barHeight / 2;
if (x + barWidth > 0 && x < rect.width) {
let edgeOpacity = 1;
if (x < edgeFadeWidth) edgeOpacity = Math.max(0, x / edgeFadeWidth);
else if (x > rect.width - edgeFadeWidth) edgeOpacity = Math.max(0, (rect.width - x) / edgeFadeWidth);
ctx.globalAlpha = fadeOpacityRef.current * edgeOpacity;
ctx.fillRect(x, y, barWidth, barHeight);
}
}
}
animationIdRef.current = requestAnimationFrame(draw);
};
draw();
return () => {
if (animationIdRef.current) cancelAnimationFrame(animationIdRef.current);
};
}, [recorderState]);
(0, react.useEffect)(() => {
return cleanup;
}, [cleanup]);
(0, react.useImperativeHandle)(ref, () => ({
get state() {
return recorderState;
},
start,
stop,
dispose: cleanup
}), [
recorderState,
start,
stop,
cleanup
]);
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
className: (0, tailwind_merge.twMerge)("cpk:w-full cpk:py-3 cpk:px-5", className),
...divProps,
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("canvas", {
ref: canvasRef,
className: "cpk:block cpk:w-full cpk:h-[26px]"
})
});
});
CopilotChatAudioRecorder.displayName = "CopilotChatAudioRecorder";
//#endregion
//#region src/v2/lib/slots.tsx
/**
* Check if a value is a React component type (function, class, forwardRef, memo, etc.)
*/
function isReactComponentType(value) {
if (typeof value === "function") return true;
if (value && typeof value === "object" && "$$typeof" in value && !react.default.isValidElement(value)) return true;
return false;
}
/**
* Internal function to render a slot value as a React element (non-memoized).
*/
function renderSlotElement(slot, DefaultComponent, props) {
if (typeof slot === "string") {
const existingClassName = props.className;
return react.default.createElement(DefaultComponent, {
...props,
className: (0, tailwind_merge.twMerge)(existingClassName, slot)
});
}
if (isReactComponentType(slot)) return react.default.createElement(slot, props);
if (slot && typeof slot === "object" && !react.default.isValidElement(slot)) return react.default.createElement(DefaultComponent, {
...props,
...slot
});
return react.default.createElement(DefaultComponent, props);
}
/**
* Internal memoized wrapper component for renderSlot.
* Uses forwardRef to support ref forwarding.
*/
const MemoizedSlotWrapper = react.default.memo(react.default.forwardRef(function MemoizedSlotWrapper(props, ref) {
const { $slot, $component, ...rest } = props;
return renderSlotElement($slot, $component, ref !== null ? {
...rest,
ref
} : rest);
}), (prev, next) => {
if (prev.$slot !== next.$slot) return false;
if (prev.$component !== next.$component) return false;
const { $slot: _ps, $component: _pc, ...prevRest } = prev;
const { $slot: _ns, $component: _nc, ...nextRest } = next;
return shallowEqual(prevRest, nextRest);
});
/**
* Renders a slot value as a memoized React element.
* Automatically prevents unnecessary re-renders using shallow prop comparison.
* Supports ref forwarding.
*
* @example
* renderSlot(customInput, CopilotChatInput, { onSubmit: handleSubmit })
*/
function renderSlot(slot, DefaultComponent, props) {
return react.default.createElement(MemoizedSlotWrapper, {
...props,
$slot: slot,
$component: DefaultComponent
});
}
//#endregion
//#region src/v2/components/chat/CopilotChatInput.tsx
const SLASH_MENU_MAX_VISIBLE_ITEMS = 5;
const SLASH_MENU_ITEM_HEIGHT_PX = 40;
function CopilotChatInput({ mode = "input", onSubmitMessage, onStop, isRunning = false, onStartTranscribe, onCancelTranscribe, onFinishTranscribe, onFinishTranscribeWithAudio, onAddFile, onChange, value, toolsMenu, autoFocus = false, positioning = "static", keyboardHeight = 0, containerRef, showDisclaimer, bottomAnchored = false, textArea, sendButton, startTranscribeButton, cancelTranscribeButton, finishTranscribeButton, addMenuButton, audioRecorder, disclaimer, children, className, ...props }) {
const isControlled = value !== void 0;
const [internalValue, setInternalValue] = (0, react.useState)(() => value ?? "");
(0, react.useEffect)(() => {
if (!isControlled && value !== void 0) setInternalValue(value);
}, [isControlled, value]);
const resolvedValue = isControlled ? value ?? "" : internalValue;
const [layout, setLayout] = (0, react.useState)("compact");
const ignoreResizeRef = (0, react.useRef)(false);
const resizeEvaluationRafRef = (0, react.useRef)(null);
const isExpanded = mode === "input" && layout === "expanded";
const [commandQuery, setCommandQuery] = (0, react.useState)(null);
const [slashHighlightIndex, setSlashHighlightIndex] = (0, react.useState)(0);
const inputRef = (0, react.useRef)(null);
const gridRef = (0, react.useRef)(null);
const addButtonContainerRef = (0, react.useRef)(null);
const actionsContainerRef = (0, react.useRef)(null);
const audioRecorderRef = (0, react.useRef)(null);
const slashMenuRef = (0, react.useRef)(null);
const config = useCopilotChatConfiguration();
const labels = config?.labels ?? CopilotChatDefaultLabels;
const previousModalStateRef = (0, react.useRef)(void 0);
const measurementCanvasRef = (0, react.useRef)(null);
const measurementsRef = (0, react.useRef)({
singleLineHeight: 0,
maxHeight: 0,
paddingLeft: 0,
paddingRight: 0
});
const containerCacheRef = (0, react.useRef)(null);
const commandItems = (0, react.useMemo)(() => {
const entries = [];
const seen = /* @__PURE__ */ new Set();
const pushItem = (item) => {
if (item === "-") return;
if (item.items && item.items.length > 0) {
for (const nested of item.items) pushItem(nested);
return;
}
if (!seen.has(item.label)) {
seen.add(item.label);
entries.push(item);
}
};
if (onAddFile) pushItem({
label: labels.chatInputToolbarAddButtonLabel,
action: onAddFile
});
if (toolsMenu && toolsMenu.length > 0) for (const item of toolsMenu) pushItem(item);
return entries;
}, [
labels.chatInputToolbarAddButtonLabel,
onAddFile,
toolsMenu
]);
const filteredCommands = (0, react.useMemo)(() => {
if (commandQuery === null) return [];
if (commandItems.length === 0) return [];
const query = commandQuery.trim().toLowerCase();
if (query.length === 0) return commandItems;
const startsWith = [];
const contains = [];
for (const item of commandItems) {
const label = item.label.toLowerCase();
if (label.startsWith(query)) startsWith.push(item);
else if (label.includes(query)) contains.push(item);
}
return [...startsWith, ...contains];
}, [commandItems, commandQuery]);
(0, react.useEffect)(() => {
if (!autoFocus) {
previousModalStateRef.current = config?.isModalOpen;
return;
}
if (config?.isModalOpen && !previousModalStateRef.current) inputRef.current?.focus({ preventScroll: true });
previousModalStateRef.current = config?.isModalOpen;
}, [config?.isModalOpen, autoFocus]);
(0, react.useEffect)(() => {
if (commandItems.length === 0 && commandQuery !== null) setCommandQuery(null);
}, [commandItems.length, commandQuery]);
const previousCommandQueryRef = (0, react.useRef)(null);
(0, react.useEffect)(() => {
if (commandQuery !== null && commandQuery !== previousCommandQueryRef.current && filteredCommands.length > 0) setSlashHighlightIndex(0);
previousCommandQueryRef.current = commandQuery;
}, [commandQuery, filteredCommands.length]);
(0, react.useEffect)(() => {
if (commandQuery === null) {
setSlashHighlightIndex(0);
return;
}
if (filteredCommands.length === 0) setSlashHighlightIndex(-1);
else if (slashHighlightIndex < 0 || slashHighlightIndex >= filteredCommands.length) setSlashHighlightIndex(0);
}, [
commandQuery,
filteredCommands,
slashHighlightIndex
]);
(0, react.useEffect)(() => {
const recorder = audioRecorderRef.current;
if (!recorder) return;
if (mode === "transcribe") recorder.start().catch(console.error);
else if (recorder.state === "recording") recorder.stop().catch(console.error);
}, [mode]);
(0, react.useEffect)(() => {
if (mode !== "input") {
setLayout("compact");
setCommandQuery(null);
}
}, [mode]);
const updateSlashState = (0, react.useCallback)((value) => {
if (commandItems.length === 0) {
setCommandQuery((prev) => prev === null ? prev : null);
return;
}
if (value.startsWith("/")) {
const query = (value.split(/\r?\n/, 1)[0] ?? "").slice(1);
setCommandQuery((prev) => prev === query ? prev : query);
} else setCommandQuery((prev) => prev === null ? prev : null);
}, [commandItems.length]);
(0, react.useEffect)(() => {
updateSlashState(resolvedValue);
}, [resolvedValue, updateSlashState]);
const handleChange = (e) => {
const nextValue = e.target.value;
if (!isControlled) setInternalValue(nextValue);
onChange?.(nextValue);
updateSlashState(nextValue);
};
const clearInputValue = (0, react.useCallback)(() => {
if (!isControlled) setInternalValue("");
if (onChange) onChange("");
}, [isControlled, onChange]);
const runCommand = (0, react.useCallback)((item) => {
clearInputValue();
item.action?.();
setCommandQuery(null);
setSlashHighlightIndex(0);
requestAnimationFrame(() => {
inputRef.current?.focus();
});
}, [clearInputValue]);
const handleKeyDown = (e) => {
if (e.nativeEvent.isComposing || e.keyCode === 229) return;
if (commandQuery !== null && mode === "input") {
if (e.key === "ArrowDown") {
if (filteredCommands.length > 0) {
e.preventDefault();
setSlashHighlightIndex((prev) => {
if (filteredCommands.length === 0) return prev;
return prev === -1 ? 0 : (prev + 1) % filteredCommands.length;
});
}
return;
}
if (e.key === "ArrowUp") {
if (filteredCommands.length > 0) {
e.preventDefault();
setSlashHighlightIndex((prev) => {
if (filteredCommands.length === 0) return prev;
if (prev === -1) return filteredCommands.length - 1;
return prev <= 0 ? filteredCommands.length - 1 : prev - 1;
});
}
return;
}
if (e.key === "Enter") {
const selected = slashHighlightIndex >= 0 ? filteredCommands[slashHighlightIndex] : void 0;
if (selected) {
e.preventDefault();
runCommand(selected);
return;
}
}
if (e.key === "Escape") {
e.preventDefault();
setCommandQuery(null);
return;
}
}
if (e.key === "Enter" && !e.shiftKey) {
e.preventDefault();
if (isProcessing && !canSend) onStop?.();
else send();
}
};
const send = () => {
if (!onSubmitMessage) return;
const trimmed = resolvedValue.trim();
if (!trimmed) return;
onSubmitMessage(trimmed);
if (!isControlled) setInternalValue("");
onChange?.("");
if (inputRef.current) inputRef.current.focus();
};
const BoundTextArea = renderSlot(textArea, CopilotChatInput.TextArea, {
ref: inputRef,
value: resolvedValue,
onChange: handleChange,
onKeyDown: handleKeyDown,
onCompositionStart: () => {
isComposingRef.current = true;
},
onCompositionEnd: () => {
isComposingRef.current = false;
},
autoFocus,
className: (0, tailwind_merge.twMerge)("cpk:w-full cpk:py-3", isExpanded ? "cpk:px-5" : "cpk:pr-5")
});
const isProcessing = mode !== "transcribe" && isRunning;
const canSend = resolvedValue.trim().length > 0 && !!onSubmitMessage;
const canStop = !!onStop;
const handleSendButtonClick = () => {
if (isProcessing) {
onStop?.();
return;
}
send();
};
const BoundAudioRecorder = renderSlot(audioRecorder, CopilotChatAudioRecorder, { ref: audioRecorderRef });
const BoundSendButton = renderSlot(sendButton, CopilotChatInput.SendButton, {
onClick: handleSendButtonClick,
disabled: isProcessing ? !canStop : !canSend,
children: isProcessing && canStop ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.Square, { className: "cpk:size-[18px] cpk:fill-current" }) : void 0
});
const BoundStartTranscribeButton = renderSlot(startTranscribeButton, CopilotChatInput.StartTranscribeButton, { onClick: onStartTranscribe });
const BoundCancelTranscribeButton = renderSlot(cancelTranscribeButton, CopilotChatInput.CancelTranscribeButton, { onClick: onCancelTranscribe });
const handleFinishTranscribe = (0, react.useCallback)(async () => {
const recorder = audioRecorderRef.current;
if (recorder && recorder.state === "recording") try {
const audioBlob = await recorder.stop();
if (onFinishTranscribeWithAudio) await onFinishTranscribeWithAudio(audioBlob);
} catch (error) {
console.error("Failed to stop recording:", error);
}
onFinishTranscribe?.();
}, [onFinishTranscribe, onFinishTranscribeWithAudio]);
const BoundFinishTranscribeButton = renderSlot(finishTranscribeButton, CopilotChatInput.FinishTranscribeButton, { onClick: handleFinishTranscribe });
const BoundAddMenuButton = renderSlot(addMenuButton, CopilotChatInput.AddMenuButton, {
disabled: mode === "transcribe",
onAddFile,
toolsMenu
});
const BoundDisclaimer = renderSlot(disclaimer, CopilotChatInput.Disclaimer, {});
const shouldShowDisclaimer = showDisclaimer ?? positioning === "absolute";
if (children) {
const childProps = {
textArea: BoundTextArea,
audioRecorder: BoundAudioRecorder,
sendButton: BoundSendButton,
startTranscribeButton: BoundStartTranscribeButton,
cancelTranscribeButton: BoundCancelTranscribeButton,
finishTranscribeButton: BoundFinishTranscribeButton,
addMenuButton: BoundAddMenuButton,
disclaimer: BoundDisclaimer,
onSubmitMessage,
onStop,
isRunning,
onStartTranscribe,
onCancelTranscribe,
onFinishTranscribe,
onAddFile,
mode,
toolsMenu,
autoFocus,
positioning,
keyboardHeight,
showDisclaimer: shouldShowDisclaimer,
containerRef
};
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
"data-copilotkit": true,
style: { display: "contents" },
children: children(childProps)
});
}
const handleContainerClick = (e) => {
const target = e.target;
if (target.tagName !== "BUTTON" && !target.closest("button") && inputRef.current && mode === "input") inputRef.current.focus();
};
const isComposingRef = (0, react.useRef)(false);
const ensureMeasurements = (0, react.useCallback)(() => {
const textarea = inputRef.current;
if (!textarea || isComposingRef.current) return;
const previousValue = textarea.value;
const previousHeight = textarea.style.height;
textarea.style.height = "auto";
const computedStyle = window.getComputedStyle(textarea);
const paddingLeft = parseFloat(computedStyle.paddingLeft) || 0;
const paddingRight = parseFloat(computedStyle.paddingRight) || 0;
const paddingTop = parseFloat(computedStyle.paddingTop) || 0;
const paddingBottom = parseFloat(computedStyle.paddingBottom) || 0;
textarea.value = "";
const singleLineHeight = textarea.scrollHeight;
textarea.value = previousValue;
const maxHeight = (singleLineHeight - paddingTop - paddingBottom) * 5 + paddingTop + paddingBottom;
measurementsRef.current = {
singleLineHeight,
maxHeight,
paddingLeft,
paddingRight
};
textarea.style.height = previousHeight;
textarea.style.maxHeight = `${maxHeight}px`;
}, []);
const adjustTextareaHeight = (0, react.useCallback)(() => {
const textarea = inputRef.current;
if (!textarea) return 0;
if (measurementsRef.current.singleLineHeight === 0) ensureMeasurements();
const { maxHeight } = measurementsRef.current;
if (maxHeight) textarea.style.maxHeight = `${maxHeight}px`;
textarea.style.height = "auto";
const scrollHeight = textarea.scrollHeight;
if (maxHeight) textarea.style.height = `${Math.min(scrollHeight, maxHeight)}px`;
else textarea.style.height = `${scrollHeight}px`;
return scrollHeight;
}, [ensureMeasurements]);
const updateLayout = (0, react.useCallback)((nextLayout) => {
setLayout((prev) => {
if (prev === nextLayout) return prev;
ignoreResizeRef.current = true;
return nextLayout;
});
}, []);
const updateContainerCache = (0, react.useCallback)(() => {
const grid = gridRef.current;
const addContainer = addButtonContainerRef.current;
const actionsContainer = actionsContainerRef.current;
if (!grid || !addContainer || !actionsContainer) return null;
const gridStyles = window.getComputedStyle(grid);
const paddingLeft = parseFloat(gridStyles.paddingLeft) || 0;
const paddingRight = parseFloat(gridStyles.paddingRight) || 0;
const columnGap = parseFloat(gridStyles.columnGap) || 0;
const gridAvailableWidth = grid.clientWidth - paddingLeft - paddingRight;
if (gridAvailableWidth <= 0) return null;
const addWidth = addContainer.getBoundingClientRect().width;
const actionsWidth = actionsContainer.getBoundingClientRect().width;
const compactWidth = Math.max(gridAvailableWidth - addWidth - actionsWidth - columnGap * 2, 0);
if (compactWidth <= 0) return null;
const result = { compactWidth };
containerCacheRef.current = result;
return result;
}, []);
const evaluateLayout = (0, react.useCallback)(() => {
if (mode !== "input") {
updateLayout("compact");
return;
}
if (typeof window !== "undefined" && typeof window.matchMedia === "function") {
if (window.matchMedia("(max-width: 767px)").matches) {
adjustTextareaHeight();
updateLayout("expanded");
return;
}
}
const textarea = inputRef.current;
const grid = gridRef.current;
const addContainer = addButtonContainerRef.current;
const actionsContainer = actionsContainerRef.current;
if (!textarea || !grid || !addContainer || !actionsContainer) return;
if (measurementsRef.current.singleLineHeight === 0) ensureMeasurements();
const scrollHeight = adjustTextareaHeight();
const baseline = measurementsRef.current.singleLineHeight;
const hasExplicitBreak = resolvedValue.includes("\n");
const renderedMultiline = baseline > 0 ? scrollHeight > baseline + 1 : false;
let shouldExpand = hasExplicitBreak || renderedMultiline;
if (!shouldExpand) {
const cache = containerCacheRef.current ?? updateContainerCache();
if (cache && cache.compactWidth > 0) {
const compactInnerWidth = Math.max(cache.compactWidth - (measurementsRef.current.paddingLeft || 0) - (measurementsRef.current.paddingRight || 0), 0);
if (compactInnerWidth > 0) {
const textareaStyles = window.getComputedStyle(textarea);
let font = textareaStyles.font;
if (!font) {
const { fontStyle, fontVariant, fontWeight, fontSize, lineHeight, fontFamily } = textareaStyles;
if (fontSize && fontFamily) font = `${fontStyle} ${fontVariant} ${fontWeight} ${fontSize}/${lineHeight} ${fontFamily}`;
}
if (font?.trim()) {
const canvas = measurementCanvasRef.current ?? document.createElement("canvas");
if (!measurementCanvasRef.current) measurementCanvasRef.current = canvas;
const context = canvas.getContext("2d");
if (context) {
context.font = font;
const lines = resolvedValue.length > 0 ? resolvedValue.split("\n") : [""];
let longestWidth = 0;
for (const line of lines) {
const metrics = context.measureText(line || " ");
if (metrics.width > longestWidth) longestWidth = metrics.width;
}
if (longestWidth > compactInnerWidth) shouldExpand = true;
} else if (process.env.NODE_ENV !== "production") console.warn("[CopilotChatInput] canvas.getContext('2d') returned null. Text-width-based expansion will be unavailable.");
} else if (process.env.NODE_ENV !== "production") console.warn("[CopilotChatInput] Could not resolve textarea font for layout measurement. Text-width-based expansion will be skipped until the next evaluation.");
}
}
}
updateLayout(shouldExpand ? "expanded" : "compact");
}, [
adjustTextareaHeight,
ensureMeasurements,
mode,
resolvedValue,
updateContainerCache,
updateLayout
]);
(0, react.useLayoutEffect)(() => {
evaluateLayout();
}, [evaluateLayout]);
(0, react.useEffect)(() => {
if (typeof ResizeObserver === "undefined") return;
const textarea = inputRef.current;
const grid = gridRef.current;
const addContainer = addButtonContainerRef.current;
const actionsContainer = actionsContainerRef.current;
if (!textarea || !grid || !addContainer || !actionsContainer) return;
const containerTargets = new Set([
grid,
addContainer,
actionsContainer
]);
const scheduleEvaluation = (invalidateCache) => {
if (ignoreResizeRef.current) {
ignoreResizeRef.current = false;
return;
}
if (invalidateCache) containerCacheRef.current = null;
if (typeof window === "undefined") {
evaluateLayout();
return;
}
if (resizeEvaluationRafRef.current !== null) cancelAnimationFrame(resizeEvaluationRafRef.current);
resizeEvaluationRafRef.current = window.requestAnimationFrame(() => {
resizeEvaluationRafRef.current = null;
evaluateLayout();
});
};
const observer = new ResizeObserver((entries) => {
let shouldInvalidate = false;
for (const entry of entries) if (containerTargets.has(entry.target)) {
shouldInvalidate = true;
break;
}
scheduleEvaluation(shouldInvalidate);
});
observer.observe(grid);
observer.observe(addContainer);
observer.observe(actionsContainer);
observer.observe(textarea);
return () => {
observer.disconnect();
if (typeof window !== "undefined" && resizeEvaluationRafRef.current !== null) {
cancelAnimationFrame(resizeEvaluationRafRef.current);
resizeEvaluationRafRef.current = null;
}
};
}, [evaluateLayout]);
const slashMenuVisible = commandQuery !== null && commandItems.length > 0;
(0, react.useEffect)(() => {
if (!slashMenuVisible || slashHighlightIndex < 0) return;
(slashMenuRef.current?.querySelector(`[data-slash-index="${slashHighlightIndex}"]`))?.scrollIntoView({ block: "nearest" });
}, [slashMenuVisible, slashHighlightIndex]);
const slashMenu = slashMenuVisible ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
"data-testid": "copilot-slash-menu",
role: "listbox",
"aria-label": "Slash commands",
ref: slashMenuRef,
className: "cpk:absolute cpk:bottom-full cpk:left-0 cpk:right-0 cpk:z-30 cpk:mb-2 cpk:max-h-64 cpk:overflow-y-auto cpk:rounded-lg cpk:border cpk:border-border cpk:bg-white cpk:shadow-lg cpk:dark:border-[#3a3a3a] cpk:dark:bg-[#1f1f1f]",
style: { maxHeight: `${SLASH_MENU_MAX_VISIBLE_ITEMS * SLASH_MENU_ITEM_HEIGHT_PX}px` },
children: filteredCommands.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
className: "cpk:px-3 cpk:py-2 cpk:text-sm cpk:text-muted-foreground",
children: "No commands found"
}) : filteredCommands.map((item, index) => {
const isActive = index === slashHighlightIndex;
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
type: "button",
role: "option",
"aria-selected": isActive,
"data-active": isActive ? "true" : void 0,
"data-slash-index": index,
className: (0, tailwind_merge.twMerge)("cpk:w-full cpk:px-3 cpk:py-2 cpk:text-left cpk:text-sm cpk:transition-colors", "cpk:hover:bg-muted cpk:dark:hover:bg-[#2f2f2f]", isActive ? "cpk:bg-muted cpk:dark:bg-[#2f2f2f]" : "cpk:bg-transparent"),
onMouseEnter: () => setSlashHighlightIndex(index),
onMouseDown: (event) => {
event.preventDefault();
runCommand(item);
},
children: item.label
}, `${item.label}-${index}`);
})
}) : null;
const inputPill = /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
"data-testid": "copilot-chat-input",
className: (0, tailwind_merge.twMerge)("copilotKitInput", "cpk:flex cpk:w-full cpk:flex-col cpk:items-center cpk:justify-center", "cpk:cursor-text", "cpk:overflow-visible cpk:bg-clip-padding cpk:contain-inline-size", "cpk:bg-white cpk:dark:bg-[#303030]", "cpk:shadow-[0_4px_4px_0_#0000000a,0_0_1px_0_#0000009e] cpk:rounded-[28px]"),
onClick: handleContainerClick,
"data-layout": isExpanded ? "expanded" : "compact",
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
ref: gridRef,
className: (0, tailwind_merge.twMerge)("cpk:grid cpk:w-full cpk:gap-x-3 cpk:gap-y-3 cpk:px-3 cpk:py-2", isExpanded ? "cpk:grid-cols-[auto_minmax(0,1fr)_auto] cpk:grid-rows-[auto_auto]" : "cpk:grid-cols-[auto_minmax(0,1fr)_auto] cpk:items-center"),
"data-layout": isExpanded ? "expanded" : "compact",
children: [
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
ref: addButtonContainerRef,
className: (0, tailwind_merge.twMerge)("cpk:flex cpk:items-center", isExpanded ? "cpk:row-start-2" : "cpk:row-start-1", "cpk:col-start-1"),
children: BoundAddMenuButton
}),
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
className: (0, tailwind_merge.twMerge)("cpk:relative cpk:flex cpk:min-w-0 cpk:flex-col cpk:min-h-[50px] cpk:justify-center", isExpanded ? "cpk:col-span-3 cpk:row-start-1" : "cpk:col-start-2 cpk:row-start-1"),
children: mode === "transcribe" ? BoundAudioRecorder : mode === "processing" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
className: "cpk:flex cpk:w-full cpk:items-center cpk:justify-center cpk:py-3 cpk:px-5",
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.Loader2, { className: "cpk:size-[26px] cpk:animate-spin cpk:text-muted-foreground" })
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [BoundTextArea, slashMenu] })
}),
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
ref: actionsContainerRef,
className: (0, tailwind_merge.twMerge)("cpk:flex cpk:items-center cpk:justify-end cpk:gap-2", isExpanded ? "cpk:col-start-3 cpk:row-start-2" : "cpk:col-start-3 cpk:row-start-1"),
children: mode === "transcribe" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [onCancelTranscribe && BoundCancelTranscribeButton, onFinishTranscribe && BoundFinishTranscribeButton] }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [onStartTranscribe && BoundStartTranscribeButton, BoundSendButton] })
})
]
})
});
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
"data-copilotkit": true,
ref: containerRef,
className: cn("cpk:pointer-events-none cpk:relative cpk:z-20", positioning === "absolute" && "cpk:absolute cpk:bottom-0 cpk:left-0 cpk:right-0", className),
style: {
transform: keyboardHeight > 0 ? `translateY(-${keyboardHeight}px)` : void 0,
transition: "transform 0.2s ease-out",
...positioning === "absolute" || bottomAnchored ? { paddingBottom: "var(--copilotkit-license-banner-offset, 0px)" } : {}
},
...props,
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
className: "cpk:max-w-3xl cpk:mx-auto cpk:py-0 cpk:px-4 cpk:@3xl:px-0 cpk:[div[data-sidebar-chat]_&]:px-8 cpk:[div[data-popup-chat]_&]:px-4 cpk:pointer-events-auto",
children: inputPill
}), shouldShowDisclaimer && BoundDisclaimer]
});
}
(function(_CopilotChatInput) {
_CopilotChatInput.SendButton = ({ className, children, ...props }) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
className: "cpk:mr-[10px]",
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Button, {
type: "button",
"data-testid": "copilot-send-button",
variant: "chatInputToolbarPrimary",
size: "chatInputToolbarIcon",
className,
...props,
children: children ?? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.ArrowUp, { className: "cpk:size-[18px]" })
})
});
const ToolbarButton = _CopilotChatInput.ToolbarButton = ({ icon, labelKey, defaultClassName, className, ...props }) => {
const labels = useCopilotChatConfiguration()?.labels ?? CopilotChatDefaultLabels;
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(Tooltip, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(TooltipTrigger, {
asChild: true,
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Button, {
type: "button",
variant: "chatInputToolbarSecondary",
size: "chatInputToolbarIcon",
className: (0, tailwind_merge.twMerge)(defaultClassName, className),
...props,
children: icon
})
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(TooltipContent, {
side: "bottom",
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: labels[labelKey] })
})] });
};
_CopilotChatInput.StartTranscribeButton = (props) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ToolbarButton, {
"data-testid": "copilot-start-transcribe-button",
icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.Mic, { className: "cpk:size-[18px]" }),
labelKey: "chatInputToolbarStartTranscribeButtonLabel",
defaultClassName: "cpk:mr-2",
...props
});
_CopilotChatInput.CancelTranscribeButton = (props) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ToolbarButton, {
"data-testid": "copilot-cancel-transcribe-button",
icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.X, { className: "cpk:size-[18px]" }),
labelKey: "chatInputToolbarCancelTranscribeButtonLabel",
defaultClassName: "cpk:mr-2",
...props
});
_CopilotChatInput.FinishTranscribeButton = (props) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ToolbarButton, {
"data-testid": "copilot-finish-transcribe-button",
icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.Check, { className: "cpk:size-[18px]" }),
labelKey: "chatInputToolbarFinishTranscribeButtonLabel",
defaultClassName: "cpk:mr-[10px]",
...props
});
_CopilotChatInput.AddMenuButton = ({ className, toolsMenu, onAddFile, disabled, ...props }) => {
const labels = useCopilotChatConfiguration()?.labels ?? CopilotChatDefaultLabels;
const [mounted, setMounted] = (0, react.useState)(false);
(0, react.useEffect)(() => setMounted(true), []);
const menuItems = (0, react.useMemo)(() => {
const items = [];
if (onAddFile) items.push({
label: labels.chatInputToolbarAddButtonLabel,
action: onAddFile
});
if (toolsMenu && toolsMenu.length > 0) {
if (items.length > 0) items.push("-");
for (const item of toolsMenu) if (item === "-") {
if (items.length === 0 || items[items.length - 1] === "-") continue;
items.push(item);
} else items.push(item);
while (items.length > 0 && items[items.length - 1] === "-") items.pop();
}
return items;
}, [
onAddFile,
toolsMenu,
labels.chatInputToolbarAddButtonLabel
]);
const renderMenuItems = (0, react.useCallback)((items) => items.map((item, index) => {
if (item === "-") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DropdownMenuSeparator, {}, `separator-${index}`);
if (item.items && item.items.length > 0) return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(DropdownMenuSub, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(DropdownMenuSubTrigger, { children: item.label }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DropdownMenuSubContent, { children: renderMenuItems(item.items) })] }, `group-${index}`);
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DropdownMenuItem, {
onClick: item.action,
children: item.label
}, `item-${index}`);
}), []);
const hasMenuItems = menuItems.length > 0;
const isDisabled = disabled || !hasMenuItems;
const button = /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Button, {
type: "button",
"data-testid": "copilot-add-menu-button",
variant: "chatInputToolbarSecondary",
size: "chatInputToolbarIcon",
className: (0, tailwind_merge.twMerge)("cpk:ml-1", className),
disabled: isDisabled,
...props,
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.Plus, { className: "cpk:size-[20px]" })
});
if (!mounted) return button;
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(DropdownMenu, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)(Tooltip, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(TooltipTrigger, {
asChild: true,
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DropdownMenuTrigger, {
asChild: true,
children: button
})
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(TooltipContent, {
side: "bottom",
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", {
className: "cpk:flex cpk:items-center cpk:gap-1 cpk:text-xs cpk:font-medium",
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: "Add attachments" }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("code", {
className: "cpk:rounded cpk:bg-[#4a4a4a] cpk:px-1 cpk:py-[1px] cpk:font-mono cpk:text-[11px] cpk:text-white cpk:dark:bg-[#e0e0e0] cpk:dark:text-black",
children: "/"
})]
})
})] }), hasMenuItems && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DropdownMenuContent, {
side: "top",
align: "start",
children: renderMenuItems(menuItems)
})] });
};
_CopilotChatInput.TextArea = (0, react.forwardRef)(function TextArea({ style, className, autoFocus, placeholder, ...props }, ref) {
const internalTextareaRef = (0, react.useRef)(null);
const labels = useCopilotChatConfiguration()?.labels ?? CopilotChatDefaultLabels;
(0, react.useImperativeHandle)(ref, () => internalTextareaRef.current);
(0, react.useEffect)(() => {
if (autoFocus) internalTextareaRef.current?.focus({ preventScroll: true });
}, [autoFocus]);
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("textarea", {
ref: internalTextareaRef,
"data-testid": "copilot-chat-textarea",
placeholder: placeholder ?? labels.chatInputPlaceholder,
className: (0, tailwind_merge.twMerge)("cpk:bg-transparent cpk:outline-none cpk:antialiased cpk:font-regular cpk:leading-relaxed cpk:text-[16px] cpk:placeholder:text-[#00000077] cpk:dark:placeholder:text-[#fffc]", className),
style: {
overflow: "auto",
resize: "none",
...style
},
rows: 1,
...props
});
});
_CopilotChatInput.AudioRecorder = CopilotChatAudioRecorder;
_CopilotChatInput.Disclaimer = ({ className, ...props }) => {
const labels = useCopilotChatConfiguration()?.labels ?? CopilotChatDefaultLabels;
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
className: cn("cpk:text-center cpk:text-xs cpk:text-muted-foreground cpk:py-3 cpk:px-4 cpk:max-w-3xl cpk:mx-auto", className),
...props,
children: labels.chatDisclaimerText
});
};
})(CopilotChatInput || (CopilotChatInput = {}));
CopilotChatInput.TextArea.displayName = "CopilotChatInput.TextArea";
CopilotChatInput.SendButton.displayName = "CopilotChatInput.SendButton";
CopilotChatInput.ToolbarButton.displayName = "CopilotChatInput.ToolbarButton";
CopilotChatInput.StartTranscribeButton.displayName = "CopilotChatInput.StartTranscribeButton";
CopilotChatInput.CancelTranscribeButton.displayName = "CopilotChatInput.CancelTranscribeButton";
CopilotChatInput.FinishTranscribeButton.displayName = "CopilotChatInput.FinishTranscribeButton";
CopilotChatInput.AddMenuButton.displayName = "CopilotChatInput.AddMenuButton";
CopilotChatInput.Disclaimer.displayName = "CopilotChatInput.Disclaimer";
var CopilotChatInput_default = CopilotChatInput;
//#endregion
//#region src/v2/hooks/useKatexStyles.ts
let injected = false;
/**
* Dynamically injects KaTeX CSS at runtime to avoid the Next.js
* "Global CSS cannot be imported from within node_modules" build error.
*
* Uses a singleton flag so the stylesheet is only injected once.
*/
function useKatexStyles() {
(0, react.useEffect)(() => {
if (injected || typeof document === "undefined") return;
injected = true;
import("katex/dist/katex.min.css").catch(() => {
console.warn("[CopilotKit] Failed to load katex styles — math content may render without formatting");
});
}, []);
}
//#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.default.memo(function ToolCallRenderer({ toolCall, toolMessage, RenderComponent, isExecuting }) {
const args = (0, react.useMemo)(() => (0, _copilotkit_shared.partialJSONParse)(toolCall.function.arguments), [toolCall.function.arguments]);
const toolName = toolCall.function.name;
if (toolMessage) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RenderComponent, {
name: toolName,
toolCallId: toolCall.id,
args,
status: _copilotkit_core.ToolCallStatus.Complete,
result: toolMessage.content
});
else if (isExecuting) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RenderComponent, {
name: toolName,
toolCallId: toolCall.id,
args,
status: _copilotkit_core.ToolCallStatus.Executing,
result: void 0
});
else return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RenderComponent, {
name: toolName,
toolCallId: toolCall.id,
args,
status: _copilotkit_core.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 } = (0, _copilotkit_react_core_v2_context.useCopilotKit)();
const agentId = useCopilotChatConfiguration()?.agentId ?? _copilotkit_shared.DEFAULT_AGENT_ID;
const renderToolCalls = (0, react.useSyncExternalStore)((callback) => {
return copilotkit.subscribe({ onRenderToolCallsChanged: callback }).unsubscribe;
}, () => copilotkit.renderToolCalls, () => copilotkit.renderToolCalls);
return (0, react.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__ */ (0, react_jsx_runtime.jsx)(ToolCallRenderer, {
toolCall,
toolMessage,
RenderComponent,
isExecuting: executingToolCallIds.has(toolCall.id)
}, toolCall.id);
}, [
renderToolCalls,
executingToolCallIds,
agentId
]);
}
//#endregion
//#region src/v2/components/CopilotKitInspector.tsx
const CopilotKitInspector = ({ core, openRequest, ...rest }) => {
const [InspectorComponent, setInspectorComponent] = react.useState(null);
const inspectorRef = react.useRef(null);
react.useEffect(() => {
let mounted = true;
import("@copilotkit/web-inspector").then((mod) => {
mod.defineWebInspector?.();
const Component = (0, _lit_labs_react.createComponent)({
tagName: mod.WEB_INSPECTOR_TAG,
elementClass: mod.WebInspectorElement,
react
});
if (mounted) setInspectorComponent(() => Component);
});
return () => {
mounted = false;
};
}, []);
react.useEffect(() => {
if (openRequest) inspectorRef.current?.openInspector("message_toolbar", openRequest);
}, [InspectorComponent, openRequest]);
if (!InspectorComponent) return null;
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(InspectorComponent, {
ref: inspectorRef,
...rest,
core: core ?? null
});
};
CopilotKitInspector.displayName = "CopilotKitInspector";
//#endregion
//#region src/v2/components/CopilotKitInspectorContext.tsx
const CopilotKitInspectorContext = react.createContext({
isLocalInspectorEnabled: false,
openInspector: () => void 0
});
const CopilotKitInspectorContextProvider = CopilotKitInspectorContext.Provider;
function useCopilotKitInspector() {
return react.useContext(CopilotKitInspectorContext);
}
//#endregion
//#region src/v2/components/license-warning-banner.tsx
const LICENSE_BANNER_OFFSET_PX = 52;
const LICENSE_BANNER_OFFSET_VAR = "--copilotkit-license-banner-offset";
const BANNER_STYLES = {
base: {
position: "fixed",
bottom: "8px",
left: "50%",
transform: "translateX(-50%)",
zIndex: 99999,
display: "inline-flex",
alignItems: "center",
gap: "12px",
whiteSpace: "nowrap",
padding: "8px 16px",
fontSize: "13px",
fontFamily: "-apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif",
borderRadius: "6px",
boxShadow: "0 2px 8px rgba(0, 0, 0, 0.15)"
},
info: {
backgroundColor: "#eff6ff",
border: "1px solid #93c5fd",
color: "#1e40af"
},
warning: {
backgroundColor: "#fffbeb",
border: "1px solid #fbbf24",
color: "#92400e"
},
critical: {
backgroundColor: "#fef2f2",
border: "1px solid #fca5a5",
color: "#991b1b"
}
};
function getSeverityStyle(severity) {
switch (severity) {
case "warning": return BANNER_STYLES.warning;
case "critical": return BANNER_STYLES.critical;
default: return BANNER_STYLES.info;
}
}
function BannerShell({ severity, message, actionLabel, actionUrl, onDismiss }) {
(0, react.useEffect)(() => {
if (typeof document === "undefined") return;
const root = document.documentElement;
root.style.setProperty(LICENSE_BANNER_OFFSET_VAR, `${LICENSE_BANNER_OFFSET_PX}px`);
return () => {
root.style.removeProperty(LICENSE_BANNER_OFFSET_VAR);
};
}, []);
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
style: {
...BANNER_STYLES.base,
...getSeverityStyle(severity)
},
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: message }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
style: {
display: "flex",
gap: "8px",
alignItems: "center"
},
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("a", {
href: actionUrl,
target: "_blank",
rel: "noopener noreferrer",
style: {
fontWeight: 600,
textDecoration: "underline",
color: "inherit"
},
children: actionLabel
}), onDismiss && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
onClick: onDismiss,
style: {
background: "none",
border: "none",
cursor: "pointer",
color: "inherit",
fontSize: "16px"
},
children: "×"
})]
})]
});
}
function LicenseWarningBanner({ type, featureName, expiryDate, graceRemaining, onDismiss }) {
switch (type) {
case "no_license": return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(BannerShell, {
severity: "info",
message: "Powered by CopilotKit",
actionLabel: "Get a license",
actionUrl: "https://copilotkit.ai/pricing",
onDismiss
});
case "feature_unlicensed": return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(BannerShell, {
severity: "warning",
message: `⚠ The "${featureName}" feature requires a CopilotKit license.`,
actionLabel: "Get a license",
actionUrl: "https://copilotkit.ai/pricing",
onDismiss
});
case "expiring": return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(BannerShell, {
severity: "warning",
message: `Your CopilotKit license expires in ${graceRemaining} day${graceRemaining !== 1 ? "s" : ""}. Please renew.`,
actionLabel: "Renew",
actionUrl: "https://dashboard.operations.copilotkit.ai",
onDismiss
});
case "expired": return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(BannerShell, {
severity: "critical",
message: `Your CopilotKit license expired${expiryDate ? ` on ${expiryDate}` : ""}. Please renew at copilotkit.ai/pricing`,
actionLabel: "Renew now",
actionUrl: "https://copilotkit.ai/pricing",
onDismiss
});
case "invalid": return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(BannerShell, {
severity: "critical",
message: "Invalid CopilotKit license token. Please check your configuration.",
actionLabel: "Get a license",
actionUrl: "https://copilotkit.ai/pricing",
onDismiss
});
default: return null;
}
}
function InlineFeatureWarning({ featureName }) {
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
style: {
padding: "8px 12px",
backgroundColor: "#fffbeb",
border: "1px solid #fbbf24",
borderRadius: "6px",
fontSize: "13px",
color: "#92400e",
fontFamily: "-apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif"
},
children: [
"⚠ The \"",
featureName,
"\" feature requires a CopilotKit license.",
" ",
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("a", {
href: "https://copilotkit.ai/pricing",
target: "_blank",
rel: "noopener noreferrer",
style: {
color: "#b45309",
textDecoration: "underline"
},
children: "Get one at copilotkit.ai/pricing"
})
]
});
}
//#endregion
//#region src/v2/components/MCPAppsActivityRenderer.tsx
/**
* Run an MCP app `ui/message` follow-up, scoped to the thread it was enqueued
* for (issue #5819).
*
* The MCP request queue delays follow-up work until the agent is idle. There is
* a single shared registry agent per id, and switching threads overwrites its
* `threadId`/`messages` in place. So if the host switches threads while a
* follow-up is queued, running it now would execute against — and stream into —
* the now-foreground thread.
*
* - **Same thread** (the common case): run on the shared agent, unchanged.
* - **Thread changed**: the shared agent has moved on, so the follow-up can no
* longer run in its originating thread's context. Drop it rather than leak it
* into the current thread. (The MCP app already received its `ui/message` ack
* at enqueue time; only the optional agent turn is skipped.)
*
* @internal exported for testing.
*/
async function ɵrunMcpFollowUp({ host, agent, capturedThreadId }) {
const currentThreadId = agent.threadId || "default";
const originThreadId = capturedThreadId || "default";
if (currentThreadId === originThreadId) return host.runAgent({ agent });
console.warn(`[MCPAppsRenderer] ui/message follow-up dropped: the thread changed (${originThreadId} → ${currentThreadId}) between enqueue and execution, so running it would leak into the now-foreground thread.`);
return {
result: void 0,
newMessages: []
};
}
const PROTOCOL_VERSION = "2025-06-18";
function buildSandboxHTML(extraCspDomains) {
const baseScriptSrc = "'self' 'wasm-unsafe-eval' 'unsafe-inline' 'unsafe-eval' blob: data: http://localhost:* https://localhost:*";
const baseFrameSrc = "* blob: data: http://localhost:* https://localhost:*";
const extra = extraCspDomains?.length ? " " + extraCspDomains.join(" ") : "";
return `<!doctype html>
<html>
<head>
<meta charset="utf-8" />
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; img-src * data: blob: 'unsafe-inline'; media-src * blob: data:; font-src * blob: data:; script-src ${baseScriptSrc + extra}; style-src * blob: data: 'unsafe-inline'; connect-src *; frame-src ${baseFrameSrc + extra}; base-uri 'self';" />
<style>html,body{margin:0;padding:0;height:100%;width:100%;overflow:hidden}*{box-sizing:border-box}iframe{background-color:transparent;border:none;padding:0;overflow:hidden;width:100%;height:100%}</style>
</head>
<body>
<script>
if(window.self===window.top){throw new Error("This file must be used in an iframe.")}
const inner=document.createElement("iframe");
inner.style="width:100%;height:100%;border:none;";
inner.setAttribute("sandbox","allow-scripts allow-same-origin allow-forms");
document.body.appendChild(inner);
window.addEventListener("message",async(event)=>{
if(event.source===window.parent){
if(event.data&&event.data.method==="ui/notifications/sandbox-resource-ready"){
const{html,sandbox}=event.data.params;
if(typeof sandbox==="string")inner.setAttribute("sandbox",sandbox);
if(typeof html==="string")inner.srcdoc=html;
}else if(inner&&inner.contentWindow){
inner.contentWindow.postMessage(event.data,"*");
}
}else if(event.source===inner.contentWindow){
window.parent.postMessage(event.data,"*");
}
});
window.parent.postMessage({jsonrpc:"2.0",method:"ui/notifications/sandbox-proxy-ready",params:{}},"*");
<\/script>
</body>
</html>`;
}
/**
* Queue for serializing MCP app requests to an agent.
* Ensures requests wait for the agent to stop running and are processed one at a time.
*/
var MCPAppsRequestQueue = class {
constructor() {
this.queues = /* @__PURE__ */ new Map();
this.processing = /* @__PURE__ */ new Map();
}
/**
* Add a request to the queue for a specific agent thread.
* Returns a promise that resolves when the request completes.
*/
async enqueue(agent, request) {
const threadId = agent.threadId || "default";
return new Promise((resolve, reject) => {
let queue = this.queues.get(threadId);
if (!queue) {
queue = [];
this.queues.set(threadId, queue);
}
queue.push({
execute: request,
resolve,
reject
});
this.processQueue(threadId, agent);
});
}
async processQueue(threadId, agent) {
if (this.processing.get(threadId)) return;
this.processing.set(threadId, true);
try {
const queue = this.queues.get(threadId);
if (!queue) return;
while (queue.length > 0) {
const item = queue[0];
try {
await this.waitForAgentIdle(agent);
const result = await item.execute();
item.resolve(result);
} catch (error) {
item.reject(error instanceof Error ? error : new Error(String(error)));
}
queue.shift();
}
} finally {
this.processing.set(threadId, false);
}
}
waitForAgentIdle(agent) {
return new Promise((resolve) => {
if (!agent.isRunning) {
resolve();
return;
}
let done = false;
const finish = () => {
if (done) return;
done = true;
clearInterval(checkInterval);
sub.unsubscribe();
resolve();
};
const sub = agent.subscribe({
onRunFinalized: finish,
onRunFailed: finish
});
const checkInterval = setInterval(() => {
if (!agent.isRunning) finish();
}, 500);
});
}
};
const mcpAppsRequestQueue = new MCPAppsRequestQueue();
/**
* Activity type for MCP Apps events - must match the middleware's MCPAppsActivityType
*/
const MCPAppsActivityType = "mcp-apps";
const MCPAppsActivityContentSchema = zod.z.object({
result: zod.z.object({
content: zod.z.array(zod.z.any()).optional(),
structuredContent: zod.z.any().optional(),
isError: zod.z.boolean().optional()
}),
resourceUri: zod.z.string(),
serverHash: zod.z.string(),
serverId: zod.z.string().optional(),
toolInput: zod.z.record(zod.z.string(), zod.z.unknown()).optional()
});
function isRequest(msg) {
return "id" in msg && "method" in msg;
}
function isNotification(msg) {
return !("id" in msg) && "method" in msg;
}
/**
* MCP Apps Extension Activity Renderer
*
* Renders MCP Apps UI in a sandboxed iframe with full protocol support.
* Fetches resource content on-demand via proxied MCP requests.
*/
const MCPAppsActivityRenderer = function MCPAppsActivityRenderer({ content, agent }) {
const { copilotkit } = (0, _copilotkit_react_core_v2_context.useCopilotKit)();
const containerRef = (0, react.useRef)(null);
const iframeRef = (0, react.useRef)(null);
const [iframeReady, setIframeReady] = (0, react.useState)(false);
const [error, setError] = (0, react.useState)(null);
const [isLoading, setIsLoading] = (0, react.useState)(true);
const [iframeSize, setIframeSize] = (0, react.useState)({});
const [fetchedResource, setFetchedResource] = (0, react.useState)(null);
const contentRef = (0, react.useRef)(content);
contentRef.current = content;
const agentRef = (0, react.useRef)(agent);
agentRef.current = agent;
const fetchStateRef = (0, react.useRef)({
inProgress: false,
promise: null,
resourceUri: null
});
const sendToIframe = (0, react.useCallback)((msg) => {
if (iframeRef.current?.contentWindow) {
console.log("[MCPAppsRenderer] Sending to iframe:", msg);
iframeRef.current.contentWindow.postMessage(msg, "*");
}
}, []);
const sendResponse = (0, react.useCallback)((id, result) => {
sendToIframe({
jsonrpc: "2.0",
id,
result
});
}, [sendToIframe]);
const sendErrorResponse = (0, react.useCallback)((id, code, message) => {
sendToIframe({
jsonrpc: "2.0",
id,
error: {
code,
message
}
});
}, [sendToIframe]);
const sendNotification = (0, react.useCallback)((method, params) => {
sendToIframe({
jsonrpc: "2.0",
method,
params: params || {}
});
}, [sendToIframe]);
(0, react.useEffect)(() => {
const { resourceUri, serverHash, serverId } = content;
if (fetchStateRef.current.inProgress && fetchStateRef.current.resourceUri === resourceUri) {
fetchStateRef.current.promise?.then((resource) => {
if (resource) {
setFetchedResource(resource);
setIsLoading(false);
}
}).catch((err) => {
setError(err instanceof Error ? err : new Error(String(err)));
setIsLoading(false);
});
return;
}
if (!agent) {
setError(/* @__PURE__ */ new Error("No agent available to fetch resource"));
setIsLoading(false);
return;
}
fetchStateRef.current.inProgress = true;
fetchStateRef.current.resourceUri = resourceUri;
const fetchPromise = (async () => {
try {
const resource = (await mcpAppsRequestQueue.enqueue(agent, () => agent.runAgent({ forwardedProps: { __proxiedMCPRequest: {
serverHash,
serverId,
method: "resources/read",
params: { uri: resourceUri }
} } }))).result?.contents?.[0];
if (!resource) throw new Error("No resource content in response");
return resource;
} catch (err) {
console.error("[MCPAppsRenderer] Failed to fetch resource:", err);
throw err;
} finally {
fetchStateRef.current.inProgress = false;
}
})();
fetchStateRef.current.promise = fetchPromise;
fetchPromise.then((resource) => {
if (resource) {
setFetchedResource(resource);
setIsLoading(false);
}
}).catch((err) => {
setError(err instanceof Error ? err : new Error(String(err)));
setIsLoading(false);
});
}, [agent, content]);
(0, react.useEffect)(() => {
if (isLoading || !fetchedResource) return;
const container = containerRef.current;
if (!container) return;
let mounted = true;
let messageHandler = null;
let initialListener = null;
let createdIframe = null;
const setup = async () => {
try {
const iframe = document.createElement("iframe");
createdIframe = iframe;
iframe.style.width = "100%";
iframe.style.height = "100px";
iframe.style.border = "none";
iframe.style.backgroundColor = "transparent";
iframe.style.display = "block";
iframe.setAttribute("sandbox", "allow-scripts allow-same-origin allow-forms");
iframe.setAttribute("data-testid", "mcp-app-iframe");
iframe.setAttribute("title", "Interactive MCP application");
const sandboxReady = new Promise((resolve) => {
initialListener = (event) => {
if (event.source === iframe.contentWindow) {
if (event.data?.method === "ui/notifications/sandbox-proxy-ready") {
if (initialListener) {
window.removeEventListener("message", initialListener);
initialListener = null;
}
resolve();
}
}
};
window.addEventListener("message", initialListener);
});
if (!mounted) {
if (initialListener) {
window.removeEventListener("message", initialListener);
initialListener = null;
}
return;
}
const cspDomains = fetchedResource._meta?.ui?.csp?.resourceDomains;
iframe.srcdoc = buildSandboxHTML(cspDomains);
iframeRef.current = iframe;
container.appendChild(iframe);
await sandboxReady;
if (!mounted) return;
console.log("[MCPAppsRenderer] Sandbox proxy ready");
messageHandler = async (event) => {
if (event.source !== iframe.contentWindow) return;
const msg = event.data;
if (!msg || typeof msg !== "object" || msg.jsonrpc !== "2.0") return;
console.log("[MCPAppsRenderer] Received from iframe:", msg);
if (isRequest(msg)) switch (msg.method) {
case "ui/initialize":
sendResponse(msg.id, {
protocolVersion: PROTOCOL_VERSION,
hostInfo: {
name: "CopilotKit MCP Apps Host",
version: "1.0.0"
},
hostCapabilities: {
openLinks: {},
logging: {}
},
hostContext: {
theme: "light",
platform: "web"
}
});
break;
case "ui/message": {
const currentAgent = agentRef.current;
if (!currentAgent) {
console.warn("[MCPAppsRenderer] ui/message: No agent available");
sendResponse(msg.id, { isError: false });
break;
}
try {
const params = msg.params;
const role = params.role || "user";
const textContent = params.content?.filter((c) => c.type === "text" && c.text).map((c) => c.text).join("\n") || "";
if (textContent) currentAgent.addMessage({
id: crypto.randomUUID(),
role,
content: textContent
});
sendResponse(msg.id, { isError: false });
if ((params.followUp ?? role === "user") && textContent) {
const capturedThreadId = currentAgent.threadId || "default";
mcpAppsRequestQueue.enqueue(currentAgent, () => ɵrunMcpFollowUp({
host: copilotkit,
agent: currentAgent,
capturedThreadId
})).catch((err) => console.error("[MCPAppsRenderer] ui/message agent run failed:", err));
}
} catch (err) {
console.error("[MCPAppsRenderer] ui/message error:", err);
sendResponse(msg.id, { isError: true });
}
break;
}
case "ui/open-link": {
const url = msg.params?.url;
if (url) {
window.open(url, "_blank", "noopener,noreferrer");
sendResponse(msg.id, { isError: false });
} else sendErrorResponse(msg.id, -32602, "Missing url parameter");
break;
}
case "tools/call": {
const { serverHash, serverId } = contentRef.current;
const currentAgent = agentRef.current;
if (!serverHash) {
sendErrorResponse(msg.id, -32603, "No server hash available for proxying");
break;
}
if (!currentAgent) {
sendErrorResponse(msg.id, -32603, "No agent available for proxying");
break;
}
try {
const runResult = await mcpAppsRequestQueue.enqueue(currentAgent, () => currentAgent.runAgent({ forwardedProps: { __proxiedMCPRequest: {
serverHash,
serverId,
method: "tools/call",
params: msg.params
} } }));
sendResponse(msg.id, runResult.result || {});
} catch (err) {
console.error("[MCPAppsRenderer] tools/call error:", err);
sendErrorResponse(msg.id, -32603, String(err));
}
break;
}
default: sendErrorResponse(msg.id, -32601, `Method not found: ${msg.method}`);
}
if (isNotification(msg)) switch (msg.method) {
case "ui/notifications/initialized":
console.log("[MCPAppsRenderer] Inner iframe initialized");
if (mounted) setIframeReady(true);
break;
case "ui/notifications/size-changed": {
const { width, height } = msg.params || {};
console.log("[MCPAppsRenderer] Size change:", {
width,
height
});
if (mounted) setIframeSize({
width: typeof width === "number" ? width : void 0,
height: typeof height === "number" ? height : void 0
});
break;
}
case "notifications/message":
console.log("[MCPAppsRenderer] App log:", msg.params);
break;
}
};
window.addEventListener("message", messageHandler);
let html;
if (fetchedResource.text) html = fetchedResource.text;
else if (fetchedResource.blob) html = atob(fetchedResource.blob);
else throw new Error("Resource has no text or blob content");
sendNotification("ui/notifications/sandbox-resource-ready", { html });
} catch (err) {
console.error("[MCPAppsRenderer] Setup error:", err);
if (mounted) setError(err instanceof Error ? err : new Error(String(err)));
}
};
setup();
return () => {
mounted = false;
if (initialListener) {
window.removeEventListener("message", initialListener);
initialListener = null;
}
if (messageHandler) window.removeEventListener("message", messageHandler);
if (createdIframe) {
createdIframe.remove();
createdIframe = null;
}
iframeRef.current = null;
};
}, [
isLoading,
fetchedResource,
sendNotification,
sendResponse,
sendErrorResponse
]);
(0, react.useEffect)(() => {
if (iframeRef.current) {
if (iframeSize.width !== void 0) {
iframeRef.current.style.minWidth = `min(${iframeSize.width}px, 100%)`;
iframeRef.current.style.width = "100%";
}
if (iframeSize.height !== void 0) iframeRef.current.style.height = `${iframeSize.height}px`;
}
}, [iframeSize]);
(0, react.useEffect)(() => {
if (iframeReady && content.toolInput) {
console.log("[MCPAppsRenderer] Sending tool input:", content.toolInput);
sendNotification("ui/notifications/tool-input", { arguments: content.toolInput });
}
}, [
iframeReady,
content.toolInput,
sendNotification
]);
(0, react.useEffect)(() => {
if (iframeReady && content.result) {
console.log("[MCPAppsRenderer] Sending tool result:", content.result);
sendNotification("ui/notifications/tool-result", content.result);
}
}, [
iframeReady,
content.result,
sendNotification
]);
const borderStyle = fetchedResource?._meta?.ui?.prefersBorder === true ? {
borderRadius: "8px",
backgroundColor: "#f9f9f9",
border: "1px solid #e0e0e0"
} : {};
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
ref: containerRef,
style: {
width: "100%",
height: iframeSize.height ? `${iframeSize.height}px` : "auto",
minHeight: "100px",
overflow: "hidden",
position: "relative",
...borderStyle
},
children: [isLoading && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
style: {
padding: "1rem",
color: "#666"
},
children: "Loading..."
}), error && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
style: {
color: "red",
padding: "1rem"
},
children: ["Error: ", error.message]
})]
});
};
//#endregion
//#region src/v2/providers/SandboxFunctionsContext.ts
const SandboxFunctionsContext = (0, react.createContext)([]);
function useSandboxFunctions() {
return (0, react.useContext)(SandboxFunctionsContext);
}
//#endregion
//#region src/v2/lib/processPartialHtml.ts
/**
* Extracts all complete `<style>` blocks from the raw HTML.
* Returns the concatenated style tags, suitable for injection into `<head>`.
*/
function extractCompleteStyles(html) {
const matches = html.match(/<style\b[^>]*>[\s\S]*?<\/style>/gi);
return matches ? matches.join("") : "";
}
/**
* Processes raw accumulated HTML for safe preview via innerHTML injection.
* Pure function, no DOM dependencies.
*
* Pipeline (order matters):
* 1. Strip incomplete tag at end
* 2. Strip complete <style>, <script>, and <head> blocks
* 3. Strip incomplete <style>/<script>/<head> blocks
* 4. Strip incomplete HTML entities
* 5. Extract body content (or use full string if no <body>)
*/
function processPartialHtml(html) {
let result = html;
result = result.replace(/<[^>]*$/, "");
result = result.replace(/<(style|script|head)\b[^>]*>[\s\S]*?<\/\1>/gi, "");
result = result.replace(/<(style|script|head)\b[^>]*>[\s\S]*$/gi, "");
result = result.replace(/&[a-zA-Z0-9#]*$/, "");
const bodyMatch = result.match(/<body[^>]*>([\s\S]*)/i);
if (bodyMatch) {
result = bodyMatch[1];
result = result.replace(/<\/body>[\s\S]*/i, "");
}
return result;
}
//#endregion
//#region src/v2/components/OpenGenerativeUIRenderer.tsx
const OpenGenerativeUIActivityType = "open-generative-ui";
const OpenGenerativeUIContentSchema = zod.z.object({
initialHeight: zod.z.number().optional(),
generating: zod.z.boolean().optional(),
css: zod.z.string().optional(),
cssComplete: zod.z.boolean().optional(),
html: zod.z.array(zod.z.string()).optional(),
htmlComplete: zod.z.boolean().optional(),
jsFunctions: zod.z.string().optional(),
jsFunctionsComplete: zod.z.boolean().optional(),
jsExpressions: zod.z.array(zod.z.string()).optional(),
jsExpressionsComplete: zod.z.boolean().optional()
});
/**
* Schema for the generateSandboxedUi tool call arguments.
* Used by the frontend tool renderer to display placeholder messages.
*/
const GenerateSandboxedUiArgsSchema = zod.z.object({
initialHeight: zod.z.number().optional(),
placeholderMessages: zod.z.array(zod.z.string()).optional(),
css: zod.z.string().optional(),
html: zod.z.string().optional(),
jsFunctions: zod.z.string().optional(),
jsExpressions: zod.z.array(zod.z.string()).optional()
});
const THROTTLE_MS = 1e3;
/**
* Returns true when the inner component should re-render immediately
* (no throttle delay).
*/
function shouldFlushImmediately(prev, next) {
if (next.cssComplete && (!prev || !prev.cssComplete)) return true;
if (next.htmlComplete) return true;
if (next.generating === false) return true;
if (next.jsFunctions && (!prev || !prev.jsFunctions)) return true;
if ((next.jsExpressions?.length ?? 0) > (prev?.jsExpressions?.length ?? 0)) return true;
if (next.html?.length && (!prev || !prev.html?.length)) return true;
return false;
}
/**
* Outer wrapper — absorbs every parent re-render but only forwards
* throttled content snapshots to the memoized inner component.
*/
const OpenGenerativeUIActivityRenderer = function OpenGenerativeUIActivityRenderer({ content }) {
const latestContentRef = (0, react.useRef)(content);
latestContentRef.current = content;
const [throttledContent, setThrottledContent] = (0, react.useState)(content);
const throttledContentRef = (0, react.useRef)(throttledContent);
const timerRef = (0, react.useRef)(null);
if (throttledContentRef.current !== content) {
if (shouldFlushImmediately(throttledContentRef.current, content)) {
if (timerRef.current !== null) {
clearTimeout(timerRef.current);
timerRef.current = null;
}
throttledContentRef.current = content;
setThrottledContent(content);
}
}
const flush = (0, react.useCallback)(() => {
timerRef.current = null;
const latest = latestContentRef.current;
throttledContentRef.current = latest;
setThrottledContent(latest);
}, []);
(0, react.useEffect)(() => {
if (throttledContentRef.current === content) return;
if (timerRef.current === null) timerRef.current = setTimeout(flush, THROTTLE_MS);
}, [content, flush]);
(0, react.useEffect)(() => {
return () => {
if (timerRef.current !== null) clearTimeout(timerRef.current);
};
}, []);
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(OpenGenerativeUIActivityRendererInner, { content: throttledContent });
};
function ensureHead(html) {
if (/<head[\s>]/i.test(html)) return html;
return `<head></head>${html}`;
}
function injectCssIntoHtml(html, css) {
const headCloseIdx = html.indexOf("</head>");
if (headCloseIdx !== -1) return html.slice(0, headCloseIdx) + `<style>${css}</style>` + html.slice(headCloseIdx);
return `<head><style>${css}</style></head>${html}`;
}
const OpenGenerativeUIActivityRendererInner = react.default.memo(function OpenGenerativeUIActivityRendererInner({ content }) {
const initialHeight = content.initialHeight ?? 200;
const [autoHeight, setAutoHeight] = (0, react.useState)(null);
const sandboxFunctions = useSandboxFunctions();
const localApi = (0, react.useMemo)(() => {
const api = {};
for (const fn of sandboxFunctions) api[fn.name] = fn.handler;
return api;
}, [sandboxFunctions]);
const fullHtml = content.htmlComplete && content.html?.length ? content.html.join("") : void 0;
const css = content.cssComplete ? content.css : void 0;
const cssReady = !!content.cssComplete;
const partialHtml = !content.htmlComplete && content.html?.length ? content.html.join("") : void 0;
const previewBody = partialHtml ? processPartialHtml(partialHtml) : void 0;
const previewStyles = partialHtml ? extractCompleteStyles(partialHtml) : "";
const hasPreview = cssReady && !!previewBody?.trim();
const hasVisibleSandbox = !!fullHtml || hasPreview;
const containerRef = (0, react.useRef)(null);
const sandboxRef = (0, react.useRef)(null);
const previewSandboxRef = (0, react.useRef)(null);
const previewReadyRef = (0, react.useRef)(false);
const sandboxReadyRef = (0, react.useRef)(false);
const executedIndexRef = (0, react.useRef)(0);
const pendingQueueRef = (0, react.useRef)([]);
const jsFunctionsInjectedRef = (0, react.useRef)(false);
(0, react.useEffect)(() => {
const container = containerRef.current;
if (!container || fullHtml || !hasPreview || previewSandboxRef.current) return;
let cancelled = false;
import("@jetbrains/websandbox").then((mod) => {
if (cancelled) return;
const sandbox = (mod.default?.default ?? mod.default).create({}, {
frameContainer: container,
frameContent: "<head></head><body></body>",
allowAdditionalAttributes: ""
});
previewSandboxRef.current = sandbox;
sandbox.iframe.style.width = "100%";
sandbox.iframe.style.height = "100%";
sandbox.iframe.style.border = "none";
sandbox.iframe.style.backgroundColor = "transparent";
sandbox.promise.then(() => {
if (cancelled) return;
previewReadyRef.current = true;
sandbox.run(`
var s = document.createElement('style');
s.textContent = 'html, body { overflow: hidden !important; }';
document.head.appendChild(s);
`);
const headParts = [];
if (css) headParts.push(`<style>${css}</style>`);
if (previewStyles) headParts.push(previewStyles);
if (headParts.length) sandbox.run(`document.head.innerHTML = ${JSON.stringify(headParts.join(""))}`);
if (previewBody) sandbox.run(`document.body.innerHTML = ${JSON.stringify(previewBody)}`);
});
}).catch((err) => {
console.error("[OpenGenerativeUI] Failed to load sandbox module:", err);
});
return () => {
cancelled = true;
};
}, [hasPreview, fullHtml]);
(0, react.useEffect)(() => {
if (!previewSandboxRef.current || !previewReadyRef.current) return;
const headParts = [];
if (css) headParts.push(`<style>${css}</style>`);
if (previewStyles) headParts.push(previewStyles);
if (headParts.length) previewSandboxRef.current.run(`document.head.innerHTML = ${JSON.stringify(headParts.join(""))}`);
if (!previewBody) return;
previewSandboxRef.current.run(`document.body.innerHTML = ${JSON.stringify(previewBody)}`);
}, [
previewBody,
previewStyles,
css
]);
(0, react.useEffect)(() => {
const container = containerRef.current;
if (!container || !fullHtml) return;
if (previewSandboxRef.current) {
previewSandboxRef.current.destroy();
previewSandboxRef.current = null;
previewReadyRef.current = false;
}
let cancelled = false;
executedIndexRef.current = 0;
jsFunctionsInjectedRef.current = false;
sandboxReadyRef.current = false;
pendingQueueRef.current = [];
const htmlContent = css ? injectCssIntoHtml(fullHtml, css) : fullHtml;
import("@jetbrains/websandbox").then((mod) => {
if (cancelled) return;
const sandbox = (mod.default?.default ?? mod.default).create(localApi, {
frameContainer: container,
frameContent: ensureHead(htmlContent),
allowAdditionalAttributes: ""
});
sandboxRef.current = sandbox;
sandbox.iframe.style.width = "100%";
sandbox.iframe.style.height = "100%";
sandbox.iframe.style.border = "none";
sandbox.iframe.style.backgroundColor = "transparent";
sandbox.promise.then(() => {
if (cancelled) return;
sandboxReadyRef.current = true;
sandbox.run(`
var s = document.createElement('style');
s.textContent = 'html, body { overflow: hidden !important; }';
document.head.appendChild(s);
`);
const queue = pendingQueueRef.current;
pendingQueueRef.current = [];
for (const code of queue) sandbox.run(code);
});
}).catch((err) => {
console.error("[OpenGenerativeUI] Failed to load sandbox module:", err);
});
return () => {
cancelled = true;
if (previewSandboxRef.current) {
previewSandboxRef.current.destroy();
previewSandboxRef.current = null;
previewReadyRef.current = false;
}
if (sandboxRef.current) {
sandboxRef.current.destroy();
sandboxRef.current = null;
}
sandboxReadyRef.current = false;
setAutoHeight(null);
};
}, [
fullHtml,
css,
localApi
]);
(0, react.useEffect)(() => {
if (!content.jsFunctions || jsFunctionsInjectedRef.current) return;
jsFunctionsInjectedRef.current = true;
const sandbox = sandboxRef.current;
if (sandboxReadyRef.current && sandbox) sandbox.run(content.jsFunctions);
else pendingQueueRef.current.push(content.jsFunctions);
}, [content.jsFunctions]);
(0, react.useEffect)(() => {
const expressions = content.jsExpressions;
if (!expressions || expressions.length === 0) return;
const startIndex = executedIndexRef.current;
if (startIndex >= expressions.length) return;
const newExprs = expressions.slice(startIndex);
executedIndexRef.current = expressions.length;
const sandbox = sandboxRef.current;
if (sandboxReadyRef.current && sandbox) (async () => {
for (const expr of newExprs) await sandbox.run(expr);
})();
else pendingQueueRef.current.push(...newExprs);
}, [content.jsExpressions?.length]);
const generationDone = content.generating === false;
(0, react.useEffect)(() => {
const sandbox = sandboxRef.current;
if (!generationDone || !sandbox) return;
let handled = false;
const onMessage = (e) => {
if (handled) return;
if (e.source === sandbox.iframe.contentWindow && e.data?.type === "__ck_resize") {
handled = true;
setAutoHeight(e.data.height);
window.removeEventListener("message", onMessage);
}
};
window.addEventListener("message", onMessage);
const measureOnce = `
(function() {
var s = document.createElement('style');
s.textContent = 'body { height: auto !important; min-height: 0 !important; }';
document.head.appendChild(s);
var h = document.body.scrollHeight;
var cs = getComputedStyle(document.body);
h += parseFloat(cs.marginTop) || 0;
h += parseFloat(cs.marginBottom) || 0;
s.remove();
parent.postMessage({ type: "__ck_resize", height: Math.ceil(h) }, "*");
})();
`;
if (sandboxReadyRef.current) sandbox.run(measureOnce);
else pendingQueueRef.current.push(measureOnce);
return () => {
window.removeEventListener("message", onMessage);
};
}, [generationDone]);
const height = autoHeight ?? initialHeight;
const isGenerating = content.generating !== false;
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
ref: containerRef,
style: {
position: "relative",
width: "100%",
height: `${height}px`,
borderRadius: "8px",
backgroundColor: hasVisibleSandbox ? "transparent" : "#f5f5f5",
border: hasVisibleSandbox ? "none" : "1px solid #e0e0e0",
display: hasVisibleSandbox ? "block" : "flex",
alignItems: hasVisibleSandbox ? void 0 : "center",
justifyContent: hasVisibleSandbox ? void 0 : "center",
overflow: "hidden"
},
children: isGenerating && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
style: {
position: "absolute",
inset: 0,
zIndex: 10,
pointerEvents: "all",
backgroundColor: "rgba(255, 255, 255, 0.5)",
display: "flex",
alignItems: "center",
justifyContent: "center"
},
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("svg", {
width: "48",
height: "48",
viewBox: "0 0 24 24",
fill: "none",
style: { animation: "ck-spin 1s linear infinite" },
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("circle", {
cx: "12",
cy: "12",
r: "10",
stroke: "#e0e0e0",
strokeWidth: "3"
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
d: "M12 2a10 10 0 0 1 10 10",
stroke: "#999",
strokeWidth: "3",
strokeLinecap: "round"
})]
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("style", { children: `@keyframes ck-spin { to { transform: rotate(360deg) } }` })]
})
});
}, (prev, next) => prev.content === next.content);
/**
* Frontend tool renderer for generateSandboxedUi.
* Displays placeholder messages while the UI is being generated.
*/
const OpenGenerativeUIToolRenderer = function OpenGenerativeUIToolRenderer(props) {
const [visibleMessageIndex, setVisibleMessageIndex] = (0, react.useState)(0);
const prevMessageCountRef = (0, react.useRef)(0);
const messages = props.args.placeholderMessages;
(0, react.useEffect)(() => {
if (!messages || messages.length === 0) return;
if (messages.length !== prevMessageCountRef.current) {
prevMessageCountRef.current = messages.length;
setVisibleMessageIndex(messages.length - 1);
}
if (props.status === _copilotkit_core.ToolCallStatus.Complete) return;
const timer = setInterval(() => {
setVisibleMessageIndex((i) => (i + 1) % messages.length);
}, 5e3);
return () => clearInterval(timer);
}, [messages?.length, props.status]);
if (props.status === _copilotkit_core.ToolCallStatus.Complete) return null;
if (!messages || messages.length === 0) return null;
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
style: {
padding: "8px 12px",
color: "#999",
fontSize: "14px"
},
children: messages[visibleMessageIndex] ?? messages[0]
});
};
//#endregion
//#region src/v2/a2ui/A2UIRecoveryStates.tsx
/**
* The pre-paint lifecycle fields the middleware stamps onto the `a2ui-surface`
* activity content (alongside `a2ui_operations` on paint). `.passthrough()` keeps
* `a2ui_operations` and any future fields intact.
*/
const A2UILifecycleFields = {
status: zod.z.enum([
"building",
"retrying",
"failed"
]).optional(),
attempt: zod.z.number().optional(),
maxAttempts: zod.z.number().optional(),
progressTokens: zod.z.number().optional(),
error: zod.z.string().optional(),
errors: zod.z.array(zod.z.any()).optional(),
attempts: zod.z.array(zod.z.any()).optional(),
debugExposure: zod.z.enum([
"hidden",
"collapsed",
"verbose"
]).optional()
};
/** Server-stamped debugExposure wins; else the client option; else "collapsed". */
function resolveDebugExposure(content, optionDebugExposure) {
return content?.debugExposure ?? optionDebugExposure;
}
/** building: the generic skeleton + optional live token count. */
function A2UIBuildingState({ content }) {
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(A2UIGeneratingSkeleton, {
label: "Building interface",
tokens: typeof content?.progressTokens === "number" ? content.progressTokens : void 0
});
}
/**
* retrying: stays the generic skeleton through fast/transient retries; only once
* the retry is perceptible (after `showAfterMs`, or once `attempt` crosses
* `showAfterAttempts`) does the sub-label reveal "Retrying generation… (N/M)".
*/
function A2UIRetryingState({ content, showAfterMs, showAfterAttempts, debugExposure }) {
const attempt = typeof content?.attempt === "number" ? content.attempt : void 0;
const maxAttempts = typeof content?.maxAttempts === "number" ? content.maxAttempts : void 0;
const immediate = attempt !== void 0 && attempt >= showAfterAttempts;
const [revealed, setRevealed] = (0, react.useState)(immediate);
(0, react.useEffect)(() => {
if (immediate) {
setRevealed(true);
return;
}
const timer = setTimeout(() => setRevealed(true), showAfterMs);
return () => clearTimeout(timer);
}, [immediate, showAfterMs]);
const tokens = typeof content?.progressTokens === "number" ? content.progressTokens : void 0;
if (!revealed) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(A2UIGeneratingSkeleton, {
label: "Building interface",
tokens
});
const label = attempt !== void 0 && maxAttempts !== void 0 ? `Retrying generation… (${attempt}/${maxAttempts} attempts)` : "Retrying generation…";
const errors = Array.isArray(content?.errors) ? content.errors : [];
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(A2UIGeneratingSkeleton, {
label,
tokens,
children: debugExposure !== "hidden" && errors.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(A2UIDebugDetails, {
label: "validation issues",
open: debugExposure === "verbose",
payload: {
attempt: content?.attempt,
errors
}
})
});
}
/** failed: a clean hard-failure card that replaces the skeleton in place. */
function A2UIRecoveryFailure({ content, debugExposure }) {
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
className: "cpk:rounded-lg cpk:border cpk:border-amber-200 cpk:bg-amber-50 cpk:p-3 cpk:text-sm cpk:text-amber-800",
children: [
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
className: "cpk:font-medium",
children: "Couldn't generate the UI"
}),
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
className: "cpk:mt-1 cpk:text-xs cpk:text-amber-700",
children: "Something went wrong rendering this. You can keep chatting and try again."
}),
debugExposure !== "hidden" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(A2UIDebugDetails, {
label: "developer details",
open: debugExposure === "verbose",
payload: {
error: content?.error,
attempts: content?.attempts
}
})
]
});
}
/**
* Animated wireframe skeleton with a label, an optional live token count, and an
* optional debug-detail slot below it. Pure CSS animation (no data dependency).
* The `tokens` count drives a progressive reveal of skeleton rows.
*/
function A2UIGeneratingSkeleton({ label, tokens, children }) {
const phase = tokens == null ? 3 : tokens < 50 ? 0 : tokens < 200 ? 1 : tokens < 400 ? 2 : 3;
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
style: {
margin: "12px 0",
maxWidth: 320
},
children: [
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
style: {
position: "relative",
overflow: "hidden",
borderRadius: 12,
border: "1px solid rgba(228,228,231,0.8)",
backgroundColor: "#fff",
boxShadow: "0 1px 2px rgba(0,0,0,0.04)",
padding: "16px 18px 14px"
},
children: [
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
style: {
display: "flex",
alignItems: "center",
gap: 8,
marginBottom: 12
},
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
style: {
display: "flex",
gap: 4
},
children: [
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Dot, {}),
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Dot, {}),
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Dot, {})
]
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Bar, {
w: 64,
h: 6,
bg: "#e4e4e7",
opacity: phase >= 1 ? 1 : .4,
transition: "opacity 0.5s"
})]
}),
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
style: {
display: "grid",
gap: 7
},
children: [
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)(Row, {
show: phase >= 0,
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Bar, {
w: 36,
h: 7,
bg: "rgba(147,197,253,0.7)",
anim: 0
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Bar, {
w: 80,
h: 7,
bg: "rgba(219,234,254,0.8)",
anim: .2
})]
}),
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)(Row, {
show: phase >= 0,
delay: .1,
children: [
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Spacer, {}),
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Dot, {}),
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Bar, {
w: 100,
h: 7,
bg: "rgba(24,24,27,0.2)",
anim: .3
})
]
}),
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)(Row, {
show: phase >= 1,
delay: .15,
children: [
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Spacer, {}),
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Bar, {
w: 48,
h: 7,
bg: "rgba(24,24,27,0.15)",
anim: .1
}),
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Bar, {
w: 40,
h: 7,
bg: "rgba(153,246,228,0.6)",
anim: .5
}),
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Bar, {
w: 56,
h: 7,
bg: "rgba(147,197,253,0.6)",
anim: .3
})
]
}),
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)(Row, {
show: phase >= 1,
delay: .2,
children: [
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Spacer, {}),
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Dot, {}),
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Bar, {
w: 60,
h: 7,
bg: "rgba(24,24,27,0.15)",
anim: .4
})
]
}),
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)(Row, {
show: phase >= 2,
delay: .25,
children: [
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Bar, {
w: 40,
h: 7,
bg: "rgba(153,246,228,0.5)",
anim: .2
}),
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Dot, {}),
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Bar, {
w: 48,
h: 7,
bg: "rgba(24,24,27,0.15)",
anim: .6
}),
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Bar, {
w: 64,
h: 7,
bg: "rgba(147,197,253,0.5)",
anim: .1
})
]
}),
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)(Row, {
show: phase >= 2,
delay: .3,
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Bar, {
w: 36,
h: 7,
bg: "rgba(147,197,253,0.6)",
anim: .5
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Bar, {
w: 36,
h: 7,
bg: "rgba(24,24,27,0.12)",
anim: .7
})]
}),
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)(Row, {
show: phase >= 3,
delay: .35,
children: [
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Dot, {}),
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Bar, {
w: 44,
h: 7,
bg: "rgba(24,24,27,0.18)",
anim: .3
}),
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Dot, {}),
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Bar, {
w: 56,
h: 7,
bg: "rgba(153,246,228,0.5)",
anim: .8
}),
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Bar, {
w: 48,
h: 7,
bg: "rgba(147,197,253,0.5)",
anim: .4
})
]
})
]
}),
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { style: {
pointerEvents: "none",
position: "absolute",
inset: 0,
background: "linear-gradient(105deg, transparent 0%, transparent 40%, rgba(255,255,255,0.6) 50%, transparent 60%, transparent 100%)",
backgroundSize: "250% 100%",
animation: "cpk-a2ui-sweep 3s ease-in-out infinite"
} })
]
}),
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
style: {
display: "flex",
alignItems: "center",
justifyContent: "center",
gap: 8,
marginTop: 8
},
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
style: {
fontSize: 12,
color: "#a1a1aa",
letterSpacing: "0.025em"
},
children: label
}), typeof tokens === "number" && tokens > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
style: {
fontSize: 11,
color: "#d4d4d8",
fontVariantNumeric: "tabular-nums"
},
children: [
"~",
tokens.toLocaleString(),
" tokens"
]
})]
}),
children,
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("style", { children: `
@keyframes cpk-a2ui-fade {
0%, 100% { opacity: 1; }
50% { opacity: 0.5; }
}
@keyframes cpk-a2ui-sweep {
0% { background-position: 250% 0; }
100% { background-position: -250% 0; }
}
` })
]
});
}
function A2UIDebugDetails({ label, open, payload }) {
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("details", {
open,
className: "cpk:mt-2 cpk:text-xs",
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("summary", {
className: "cpk:cursor-pointer cpk:text-gray-500",
children: label
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("pre", {
className: "cpk:mt-1 cpk:overflow-auto cpk:rounded cpk:bg-gray-100 cpk:p-2 cpk:text-gray-700",
style: { fontSize: 11 },
children: JSON.stringify(payload, null, 2)
})]
});
}
function Dot() {
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { style: {
width: 7,
height: 7,
borderRadius: "50%",
backgroundColor: "#d4d4d8",
flexShrink: 0
} });
}
function Spacer() {
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { style: { width: 12 } });
}
function Bar({ w, h, bg, anim, opacity, transition }) {
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { style: {
width: w,
height: h,
borderRadius: 9999,
backgroundColor: bg,
...anim !== void 0 ? { animation: `cpk-a2ui-fade 2.4s ease-in-out ${anim}s infinite` } : {},
...opacity !== void 0 ? { opacity } : {},
...transition ? { transition } : {}
} });
}
function Row({ children, show, delay = 0 }) {
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
style: {
display: "flex",
alignItems: "center",
gap: 6,
opacity: show ? 1 : 0,
transition: `opacity 0.4s ${delay}s`
},
children
});
}
//#endregion
//#region src/v2/a2ui/A2UIMessageRenderer.tsx
/**
* The container key used to wrap A2UI operations for explicit detection.
* Must match A2UI_OPERATIONS_KEY in @ag-ui/a2ui-middleware and copilotkit.a2ui (Python).
*/
const A2UI_OPERATIONS_KEY = "a2ui_operations";
let initialized = false;
function ensureInitialized() {
if (!initialized) {
(0, _copilotkit_a2ui_renderer.initializeDefaultCatalog)();
(0, _copilotkit_a2ui_renderer.injectStyles)();
initialized = true;
}
}
/**
* The `a2ui-surface` activity carries the WHOLE generative-UI lifecycle on one
* stable messageId (OSS-162): pre-paint `status` ("building" | "retrying" |
* "failed") with recovery detail, then `a2ui_operations` on paint. The states
* swap in place, so the painted surface replaces the skeleton with no extra
* coordination. `.passthrough()` preserves operations + any future fields.
*/
const A2UISurfaceContentSchema = zod.z.object({
a2ui_operations: zod.z.array(zod.z.any()).optional(),
...A2UILifecycleFields
}).passthrough();
function createA2UIMessageRenderer(options) {
const { theme, catalog, loadingComponent, recovery, onAction } = options;
const showAfterMs = recovery?.showAfterMs ?? 2e3;
const showAfterAttempts = recovery?.showAfterAttempts ?? 2;
const optionDebugExposure = recovery?.debugExposure ?? "collapsed";
return {
activityType: "a2ui-surface",
content: A2UISurfaceContentSchema,
render: ({ content, agent }) => {
ensureInitialized();
const [operations, setOperations] = (0, react.useState)([]);
const { copilotkit } = (0, _copilotkit_react_core_v2_context.useCopilotKit)();
const lastContentRef = (0, react.useRef)(null);
(0, react.useEffect)(() => {
if (content === lastContentRef.current) return;
lastContentRef.current = content;
const incoming = content?.[A2UI_OPERATIONS_KEY];
if (!content || !Array.isArray(incoming)) {
setOperations([]);
return;
}
setOperations(incoming);
}, [content]);
const groupedOperations = (0, react.useMemo)(() => {
const groups = /* @__PURE__ */ new Map();
for (const operation of operations) {
const surfaceId = getOperationSurfaceId(operation) ?? _copilotkit_a2ui_renderer.DEFAULT_SURFACE_ID;
if (!groups.has(surfaceId)) groups.set(surfaceId, []);
groups.get(surfaceId).push(operation);
}
return groups;
}, [operations]);
const hasOps = groupedOperations.size > 0;
const renderLifecycle = (c) => {
const status = c?.status;
const debugExposure = resolveDebugExposure(c, optionDebugExposure);
if (status === "failed") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(A2UIRecoveryFailure, {
content: c,
debugExposure
});
if (status === "retrying") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(A2UIRetryingState, {
content: c,
showAfterMs,
showAfterAttempts,
debugExposure
});
if (loadingComponent) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(loadingComponent, {});
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(A2UIBuildingState, { content: c });
};
const lastLoaderContentRef = (0, react.useRef)(null);
if (!(Array.isArray(content?.[A2UI_OPERATIONS_KEY]) && content[A2UI_OPERATIONS_KEY].length > 0)) lastLoaderContentRef.current = content;
const [surfaceReady, setSurfaceReady] = (0, react.useState)(false);
const readyRef = (0, react.useRef)(false);
const markSurfaceReady = (0, react.useCallback)(() => {
if (readyRef.current) return;
readyRef.current = true;
requestAnimationFrame(() => setSurfaceReady(true));
}, []);
(0, react.useEffect)(() => {
if (!hasOps) {
setSurfaceReady(false);
readyRef.current = false;
return;
}
const t = setTimeout(() => setSurfaceReady(true), 8e3);
return () => clearTimeout(t);
}, [hasOps]);
if (!hasOps) return renderLifecycle(content);
const surfaces = /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
className: "cpk:flex cpk:min-h-0 cpk:flex-1 cpk:flex-col cpk:gap-6 cpk:overflow-auto cpk:py-6",
children: Array.from(groupedOperations.entries()).map(([surfaceId, ops]) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ReactSurfaceHost, {
surfaceId,
operations: ops,
theme,
agent,
copilotkit,
catalog,
onAction,
onReady: markSurfaceReady
}, surfaceId))
});
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
style: { position: "relative" },
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
"aria-hidden": !surfaceReady,
style: surfaceReady ? void 0 : {
position: "absolute",
inset: 0,
opacity: 0,
pointerEvents: "none"
},
children: surfaces
}), !surfaceReady && renderLifecycle(lastLoaderContentRef.current ?? content)]
});
}
};
}
/**
* Orchestrates a single A2UI user action: runs the optional `onAction`
* interceptor first, then forwards to the agent unless the interceptor
* suppressed it (returned `null`). Exported for unit testing; the wiring lives
* in {@link ReactSurfaceHost}.
*/
async function runA2UIAction({ message, agent, copilotkit, onAction }) {
if (!agent) return;
const action = message.userAction;
const forward = async (forwardAction) => {
const a2uiAction = forwardAction !== void 0 ? {
...message,
userAction: forwardAction
} : message;
try {
copilotkit.setProperties({
...copilotkit.properties,
a2uiAction
});
await copilotkit.runAgent({ agent });
} finally {
if (copilotkit.properties) {
const { a2uiAction: _omit, ...rest } = copilotkit.properties;
copilotkit.setProperties(rest);
}
}
};
if (onAction && action) {
const result = await onAction(action, forward);
if (result === null) return;
if (result) {
await forward(result);
return;
}
}
await forward();
}
/**
* Renders a single A2UI surface using the React renderer.
* Wraps A2UIProvider + A2UIRenderer and bridges actions back to CopilotKit.
*/
function ReactSurfaceHost({ surfaceId, operations, theme, agent, copilotkit, catalog, onAction, onReady }) {
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
className: "cpk:flex cpk:w-full cpk:flex-none cpk:flex-col cpk:gap-4",
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_copilotkit_a2ui_renderer.A2UIProvider, {
onAction: (0, react.useCallback)((message) => runA2UIAction({
message,
agent,
copilotkit,
onAction
}), [
agent,
copilotkit,
onAction
]),
theme,
catalog,
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(SurfaceMessageProcessor, {
surfaceId,
operations,
onReady
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(A2UISurfaceOrError, { surfaceId })]
})
});
}
/**
* Renders the A2UI surface, or an error message if processing failed.
* Must be a child of A2UIProvider to access the error state.
*/
function A2UISurfaceOrError({ surfaceId }) {
const error = (0, _copilotkit_a2ui_renderer.useA2UIError)();
if (error) return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
className: "cpk:rounded-lg cpk:border cpk:border-red-200 cpk:bg-red-50 cpk:p-3 cpk:text-sm cpk:text-red-700",
children: ["A2UI render error: ", error]
});
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_copilotkit_a2ui_renderer.A2UIRenderer, {
surfaceId,
className: "cpk:flex cpk:flex-1"
});
}
/**
* Processes A2UI operations into the provider's message processor.
* Must be a child of A2UIProvider to access the actions context.
*/
function SurfaceMessageProcessor({ surfaceId, operations, onReady }) {
const { processMessages, getSurface } = (0, _copilotkit_a2ui_renderer.useA2UIActions)();
const lastHashRef = (0, react.useRef)("");
(0, react.useEffect)(() => {
const hash = JSON.stringify(operations);
if (hash === lastHashRef.current) return;
lastHashRef.current = hash;
processMessages(getSurface(surfaceId) ? operations.filter((op) => !op?.createSurface) : operations);
if (onReady && surfaceHasRenderableContent(operations)) onReady();
}, [
processMessages,
getSurface,
surfaceId,
operations,
onReady
]);
return null;
}
/**
* Whether the surface's operations are enough to paint a visible card yet.
* A data-bound surface references its data via `path` and renders nothing until
* the data model has ≥1 value; a static surface (no path refs) paints from its
* components alone. Used to time the loader→surface cross-over to actual content
* arrival rather than a fixed delay. (OSS-162)
*/
function surfaceHasRenderableContent(operations) {
const componentOps = operations.filter((o) => o?.updateComponents);
if (!componentOps.length) return false;
if (!JSON.stringify(componentOps).includes("\"path\"")) return true;
return operations.some((o) => {
const v = o?.updateDataModel?.value;
if (!v || typeof v !== "object") return false;
return Object.values(v).some((x) => Array.isArray(x) ? x.length > 0 : x !== null && x !== void 0 && x !== "");
});
}
function getOperationSurfaceId(operation) {
if (!operation || typeof operation !== "object") return null;
if (typeof operation.surfaceId === "string") return operation.surfaceId;
return operation?.createSurface?.surfaceId ?? operation?.updateComponents?.surfaceId ?? operation?.updateDataModel?.surfaceId ?? operation?.deleteSurface?.surfaceId ?? null;
}
//#endregion
//#region src/v2/types/defineToolCallRenderer.ts
function defineToolCallRenderer(def) {
const argsSchema = def.name === "*" && !def.args ? zod.z.any() : def.args;
return {
name: def.name,
args: argsSchema,
render: def.render,
...def.agentId ? { agentId: def.agentId } : {}
};
}
//#endregion
//#region src/v2/a2ui/A2UIToolCallRenderer.tsx
/**
* Tool name used by the dynamic A2UI generation secondary LLM.
*/
const RENDER_A2UI_TOOL_NAME = "render_a2ui";
/**
* Registers a no-op renderer for the `render_a2ui` tool call so its raw streamed
* args are never surfaced in the transcript.
*
* The generation skeleton / retry / failure UX is NO LONGER owned here (OSS-162):
* the A2UI middleware drives the whole lifecycle on the `a2ui-surface` activity
* (one stable messageId, building → retrying → failed → painted), rendered in
* place by `createA2UIMessageRenderer`. Owning a skeleton per tool call caused a
* duplicate skeleton on retries / multi-call generations and a skeleton that
* lingered after the surface painted — both fixed by retiring it here.
*
* Users can still override with their own `useRenderTool({ name: "render_a2ui" })`
* (hook-based entries take priority over this prop-based registration).
*/
function A2UIBuiltInToolCallRenderer() {
const { copilotkit } = (0, _copilotkit_react_core_v2_context.useCopilotKit)();
(0, react.useEffect)(() => {
const renderer = defineToolCallRenderer({
name: RENDER_A2UI_TOOL_NAME,
args: zod.z.any(),
render: () => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_jsx_runtime.Fragment, {})
});
const existing = copilotkit._renderToolCalls ?? [];
copilotkit.setRenderToolCalls([...existing.filter((rc) => rc.name !== RENDER_A2UI_TOOL_NAME), renderer]);
}, [copilotkit]);
return null;
}
//#endregion
//#region src/v2/a2ui/A2UICatalogContext.tsx
/**
* Renders agent context describing the available A2UI catalog and custom components.
* Only mount this component when A2UI is enabled.
*
* The entries are scoped to the agents the runtime applies A2UI to
* (`copilotkit.a2uiAgents`, #5369), so agents outside that list don't receive
* the catalog/schema/guidelines payload on their runs.
*
* When `includeSchema` is true, the full component schemas (JSON Schema) are also
* sent as context using the same description key as the A2UI middleware, so the
* middleware can optionally overwrite it with a server-side schema.
*/
function A2UICatalogContext({ catalog, includeSchema }) {
const { copilotkit } = (0, _copilotkit_react_core_v2_context.useCopilotKit)();
const capabilitiesValue = (0, react.useMemo)(() => (0, _copilotkit_a2ui_renderer.buildCatalogContextValue)(catalog), [catalog]);
const schemaValue = (0, react.useMemo)(() => includeSchema !== false ? JSON.stringify((0, _copilotkit_a2ui_renderer.extractCatalogComponentSchemas)(catalog)) : null, [catalog, includeSchema]);
const a2uiAgentsKey = copilotkit?.a2uiAgents?.join(",");
(0, react.useLayoutEffect)(() => {
if (!copilotkit) return;
const agentIds = copilotkit.a2uiAgents;
const scope = agentIds ? { agentIds } : {};
const ids = [];
ids.push(copilotkit.addContext({
description: "A2UI catalog capabilities: available catalog IDs and custom component definitions the client can render.",
value: capabilitiesValue,
...scope
}));
if (schemaValue) {
ids.push(copilotkit.addContext({
description: _copilotkit_a2ui_renderer.A2UI_SCHEMA_CONTEXT_DESCRIPTION,
value: schemaValue,
...scope
}));
ids.push(copilotkit.addContext({
description: "A2UI generation guidelines — protocol rules, tool arguments, path rules, data model format, and form/two-way-binding instructions.",
value: _copilotkit_shared.A2UI_DEFAULT_GENERATION_GUIDELINES,
...scope
}));
ids.push(copilotkit.addContext({
description: "A2UI design guidelines — visual design rules, component hierarchy tips, and action handler patterns.",
value: _copilotkit_shared.A2UI_DEFAULT_DESIGN_GUIDELINES,
...scope
}));
}
return () => {
for (const id of ids) copilotkit.removeContext(id);
};
}, [
copilotkit,
capabilitiesValue,
schemaValue,
a2uiAgentsKey
]);
return null;
}
//#endregion
//#region src/v2/lib/react-core.ts
var CopilotKitCoreReact = class extends _copilotkit_core.CopilotKitCore {
constructor(config) {
super(config);
this._renderToolCalls = [];
this._hookRenderToolCalls = /* @__PURE__ */ new Map();
this._cachedMergedRenderToolCalls = null;
this._renderCustomMessages = [];
this._renderActivityMessages = [];
this._interruptElement = null;
this._renderToolCalls = config.renderToolCalls ?? [];
this._renderCustomMessages = config.renderCustomMessages ?? [];
this._renderActivityMessages = config.renderActivityMessages ?? [];
}
get renderCustomMessages() {
return this._renderCustomMessages;
}
get renderActivityMessages() {
return this._renderActivityMessages;
}
get renderToolCalls() {
if (this._hookRenderToolCalls.size === 0) return this._renderToolCalls;
if (this._cachedMergedRenderToolCalls) return this._cachedMergedRenderToolCalls;
const merged = /* @__PURE__ */ new Map();
for (const rc of this._renderToolCalls) merged.set(`${rc.agentId ?? ""}:${rc.name}`, rc);
for (const [key, rc] of this._hookRenderToolCalls) merged.set(key, rc);
this._cachedMergedRenderToolCalls = Array.from(merged.values());
return this._cachedMergedRenderToolCalls;
}
setRenderActivityMessages(renderers) {
this._renderActivityMessages = renderers;
}
setRenderCustomMessages(renderers) {
this._renderCustomMessages = renderers;
}
setRenderToolCalls(renderToolCalls) {
this._renderToolCalls = renderToolCalls;
this._cachedMergedRenderToolCalls = null;
this._notifyRenderToolCallsChanged();
}
addHookRenderToolCall(entry) {
const key = `${entry.agentId ?? ""}:${entry.name}`;
this._hookRenderToolCalls.set(key, entry);
this._cachedMergedRenderToolCalls = null;
this._notifyRenderToolCallsChanged();
}
removeHookRenderToolCall(name, agentId) {
const key = `${agentId ?? ""}:${name}`;
if (this._hookRenderToolCalls.delete(key)) {
this._cachedMergedRenderToolCalls = null;
this._notifyRenderToolCallsChanged();
}
}
_notifyRenderToolCallsChanged() {
this.notifySubscribers((subscriber) => {
const reactSubscriber = subscriber;
if (reactSubscriber.onRenderToolCallsChanged) reactSubscriber.onRenderToolCallsChanged({
copilotkit: this,
renderToolCalls: this.renderToolCalls
});
}, "Subscriber onRenderToolCallsChanged error:");
}
get interruptElement() {
return this._interruptElement;
}
setInterruptElement(element) {
this._interruptElement = element;
this.notifySubscribers((subscriber) => {
subscriber.onInterruptElementChanged?.({
copilotkit: this,
interruptElement: this._interruptElement
});
}, "Subscriber onInterruptElementChanged error:");
}
subscribe(subscriber) {
return super.subscribe(subscriber);
}
/**
* Wait for pending React state updates before the follow-up agent run.
*
* When a frontend tool handler calls setState(), React 18 batches the update
* and schedules a commit via its internal scheduler (MessageChannel). The
* useAgentContext hook registers context via useLayoutEffect, which runs
* synchronously after React commits that batch.
*
* Awaiting a zero-delay timeout yields to the macrotask queue. React's
* MessageChannel task runs first, committing the pending state and running
* useLayoutEffect (which updates the context store). The follow-up runAgent
* call then reads fresh context.
*/
async waitForPendingFrameworkUpdates() {
await new Promise((resolve) => setTimeout(resolve, 0));
}
};
//#endregion
//#region src/v2/providers/CopilotKitProvider.tsx
const zodToJsonSchemaAdapter = (schema, options) => {
const refStrategy = options?.$refStrategy;
return (0, zod_to_json_schema.zodToJsonSchema)(schema, refStrategy === "root" || refStrategy === "relative" || refStrategy === "none" || refStrategy === "seen" ? { $refStrategy: refStrategy } : {});
};
const HEADER_NAME = "X-CopilotCloud-Public-Api-Key";
const COPILOT_CLOUD_CHAT_URL$1 = "https://api.cloud.copilotkit.ai/copilotkit/v1";
const EMPTY_HEADERS = Object.freeze({});
const EMPTY_PROPERTIES = Object.freeze({});
const EMPTY_AGENTS = Object.freeze({});
const DEFAULT_DESIGN_SKILL = `When generating UI with generateSandboxedUi, follow these design principles inspired by shadcn/ui:
- Use a minimal, flat aesthetic. Avoid drop shadows and gradients — rely on subtle borders (1px solid, light gray like #e5e7eb) to define surfaces.
- Neutral base palette: white backgrounds, zinc/slate gray text (#09090b for headings, #71717a for secondary text). One accent color for interactive elements.
- Use system font stacks (system-ui, -apple-system, sans-serif) at readable sizes (14px body, 600 weight for headings). Tight line-heights.
- Small, consistent border-radius (6–8px). Cards and containers use border, not shadow, for separation.
- Buttons: solid fill for primary (dark bg, white text), outline for secondary (border + transparent bg). Subtle hover state (slight opacity or background shift).
- Use CSS Grid or Flexbox for layout. Ensure the UI looks good at any width.
- Minimal transitions (150ms) for hover/focus states only. No decorative animations.
- Keep the UI focused and dense — avoid excessive padding. Use compact spacing (8–12px gaps, 10–14px padding in controls).`;
const GENERATE_SANDBOXED_UI_DESCRIPTION = "Generate sandboxed UI. IMPORTANT: The generated code runs in a sandboxed iframe WITHOUT same-origin access. Do NOT use localStorage, sessionStorage, document.cookie, IndexedDB, or fetch/XMLHttpRequest to same-origin URLs. To communicate with the host application, use Websandbox.connection.remote.<functionName>(args) which returns a Promise.\n\nYou CAN use external libraries from CDNs by including <script> or <link> tags in the HTML <head> (e.g., Chart.js, D3, Three.js, x-data-spreadsheet, etc.). CDN resources load normally inside the sandbox.\n\nPARAMETER ORDER IS CRITICAL — generate parameters in exactly this order:\n1. initialHeight + placeholderMessages (shown to user while generating)\n2. css (all styles FIRST — the user sees a placeholder until CSS is complete)\n3. html (streams in live — the user watches the UI build as HTML is generated)\n4. jsFunctions (reusable helper functions)\n5. jsExpressions (applied one-by-one — the user sees each expression take effect)";
function useStableArrayProp(prop, warningMessage, isMeaningfulChange) {
const empty = (0, react.useMemo)(() => [], []);
const value = prop ?? empty;
const initial = (0, react.useRef)(value);
(0, react.useEffect)(() => {
if (warningMessage && value !== initial.current && (isMeaningfulChange ? isMeaningfulChange(initial.current, value) : true)) console.error(warningMessage);
}, [value, warningMessage]);
return value;
}
const CopilotKitProvider = ({ children, runtimeUrl, headers: headersProp = EMPTY_HEADERS, credentials, publicApiKey, publicLicenseKey, licenseToken, properties = EMPTY_PROPERTIES, agents__unsafe_dev_only: agents = EMPTY_AGENTS, selfManagedAgents = EMPTY_AGENTS, renderToolCalls, renderActivityMessages, renderCustomMessages, frontendTools, humanInTheLoop, openGenerativeUI, showDevConsole = false, useSingleEndpoint, onError, a2ui, defaultThrottleMs, inspectorDefaultAnchor, debug }) => {
const [shouldRenderInspector, setShouldRenderInspector] = (0, react.useState)(false);
const [isLocalInspectorEnabled, setIsLocalInspectorEnabled] = (0, react.useState)(false);
const [inspectorOpenRequest, setInspectorOpenRequest] = (0, react.useState)(null);
const [runtimeA2UIEnabled, setRuntimeA2UIEnabled] = (0, react.useState)(false);
const [runtimeOpenGenUIEnabled, setRuntimeOpenGenUIEnabled] = (0, react.useState)(false);
const [catalogToggleVersion, setCatalogToggleVersion] = (0, react.useState)(0);
const openGenUIActive = runtimeOpenGenUIEnabled || !!openGenerativeUI;
const a2uiCatalogProvided = !!a2ui?.catalog;
const a2uiActive = runtimeA2UIEnabled || a2uiCatalogProvided;
const [runtimeLicenseStatus, setRuntimeLicenseStatus] = (0, react.useState)(void 0);
(0, react.useEffect)(() => {
if (typeof window === "undefined") return;
const isLocalhost = new Set(["localhost", "127.0.0.1"]).has(window.location?.hostname ?? "");
const canShowLocalInspectorAction = process.env.NODE_ENV === "development" && isLocalhost;
if (showDevConsole === true) {
setShouldRenderInspector(true);
setIsLocalInspectorEnabled(canShowLocalInspectorAction);
} else if (showDevConsole === "auto") if (isLocalhost) {
setShouldRenderInspector(true);
setIsLocalInspectorEnabled(canShowLocalInspectorAction);
} else {
setShouldRenderInspector(false);
setIsLocalInspectorEnabled(false);
}
else {
setShouldRenderInspector(false);
setIsLocalInspectorEnabled(false);
}
}, [showDevConsole]);
const requestInspectorOpen = (0, react.useCallback)((request) => {
setInspectorOpenRequest({ ...request });
}, []);
const inspectorContextValue = (0, react.useMemo)(() => ({
isLocalInspectorEnabled,
openInspector: requestInspectorOpen
}), [isLocalInspectorEnabled, requestInspectorOpen]);
const renderToolCallsList = useStableArrayProp(renderToolCalls, "renderToolCalls must be a stable array. If you want to dynamically add or remove tools, use `useFrontendTool` instead.", (initial, next) => {
const key = (rc) => `${rc?.agentId ?? ""}:${rc?.name ?? ""}`;
const setFrom = (arr) => new Set(arr.map(key));
const a = setFrom(initial);
const b = setFrom(next);
if (a.size !== b.size) return true;
for (const k of a) if (!b.has(k)) return true;
return false;
});
const renderCustomMessagesList = useStableArrayProp(renderCustomMessages, "renderCustomMessages must be a stable array.");
const renderActivityMessagesList = useStableArrayProp(renderActivityMessages, "renderActivityMessages must be a stable array.");
const copilotkitRef = (0, react.useRef)(null);
const rawCatalog = a2ui?.catalog;
const filteredCatalog = (0, react.useMemo)(() => {
if (!rawCatalog) return rawCatalog;
if (!(rawCatalog instanceof _copilotkit_a2ui_renderer.Catalog)) return rawCatalog;
const core = copilotkitRef.current;
return (0, _copilotkit_a2ui_renderer.filterCatalog)(rawCatalog, (name) => core ? core.isCatalogComponentEnabled(name) : true);
}, [rawCatalog, catalogToggleVersion]);
const builtInActivityRenderers = (0, react.useMemo)(() => {
const renderers = [{
activityType: MCPAppsActivityType,
content: MCPAppsActivityContentSchema,
render: MCPAppsActivityRenderer
}];
if (openGenUIActive) renderers.push({
activityType: OpenGenerativeUIActivityType,
content: OpenGenerativeUIContentSchema,
render: OpenGenerativeUIActivityRenderer
});
if (a2uiActive) renderers.unshift(createA2UIMessageRenderer({
theme: a2ui?.theme ?? _copilotkit_a2ui_renderer.viewerTheme,
catalog: filteredCatalog,
loadingComponent: a2ui?.loadingComponent,
recovery: a2ui?.recovery
}));
return renderers;
}, [
a2uiActive,
openGenUIActive,
a2ui,
filteredCatalog
]);
const allActivityRenderers = (0, react.useMemo)(() => {
return [...renderActivityMessagesList, ...builtInActivityRenderers];
}, [renderActivityMessagesList, builtInActivityRenderers]);
const resolvedPublicKey = publicApiKey ?? publicLicenseKey;
const mergedAgents = (0, react.useMemo)(() => ({
...agents,
...selfManagedAgents
}), [agents, selfManagedAgents]);
const hasLocalAgents = mergedAgents && Object.keys(mergedAgents).length > 0;
const hasSelfManagedAgents = Object.keys(selfManagedAgents).length > 0;
(0, react.useEffect)(() => {
if (hasSelfManagedAgents && !resolvedPublicKey) console.warn("[CopilotKit] `selfManagedAgents` is part of CopilotKit's Enterprise Intelligence offering. Provide a `publicLicenseKey` for production use — contact the CopilotKit team about licensing.");
}, [hasSelfManagedAgents, resolvedPublicKey]);
const headers = typeof headersProp === "function" ? headersProp() : headersProp;
const mergedHeaders = (0, react.useMemo)(() => {
if (!resolvedPublicKey) return headers;
if (headers[HEADER_NAME]) return headers;
return {
...headers,
[HEADER_NAME]: resolvedPublicKey
};
}, [headers, resolvedPublicKey]);
if (!runtimeUrl && !resolvedPublicKey && !hasLocalAgents) {
const message = "Missing required prop: 'runtimeUrl' or 'publicApiKey' or 'publicLicenseKey'";
if (process.env.NODE_ENV === "production") throw new Error(message);
else console.warn(message);
}
const chatApiEndpoint = runtimeUrl ?? (resolvedPublicKey ? COPILOT_CLOUD_CHAT_URL$1 : void 0);
const frontendToolsList = useStableArrayProp(frontendTools, "frontendTools must be a stable array. If you want to dynamically add or remove tools, use `useFrontendTool` instead.");
const humanInTheLoopList = useStableArrayProp(humanInTheLoop, "humanInTheLoop must be a stable array. If you want to dynamically add or remove human-in-the-loop tools, use `useHumanInTheLoop` instead.");
const sandboxFunctionsList = useStableArrayProp(openGenerativeUI?.sandboxFunctions, "openGenerativeUI.sandboxFunctions must be a stable array.");
const processedHumanInTheLoopTools = (0, react.useMemo)(() => {
const processedTools = [];
const processedRenderToolCalls = [];
humanInTheLoopList.forEach((tool) => {
const frontendTool = {
name: tool.name,
description: tool.description,
parameters: tool.parameters,
followUp: tool.followUp,
...tool.agentId && { agentId: tool.agentId },
handler: async () => {
return new Promise((resolve) => {
console.warn(`Human-in-the-loop tool '${tool.name}' called but no interactive handler is set up.`);
resolve(void 0);
});
}
};
processedTools.push(frontendTool);
if (tool.render) processedRenderToolCalls.push({
name: tool.name,
args: tool.parameters,
render: tool.render,
...tool.agentId && { agentId: tool.agentId }
});
});
return {
tools: processedTools,
renderToolCalls: processedRenderToolCalls
};
}, [humanInTheLoopList]);
const builtInFrontendTools = (0, react.useMemo)(() => {
if (!openGenUIActive) return [];
return [{
name: "generateSandboxedUi",
description: GENERATE_SANDBOXED_UI_DESCRIPTION,
parameters: GenerateSandboxedUiArgsSchema,
handler: async () => "UI generated",
followUp: true,
render: OpenGenerativeUIToolRenderer
}];
}, [openGenUIActive]);
const allTools = (0, react.useMemo)(() => {
const tools = [];
tools.push(...frontendToolsList);
tools.push(...builtInFrontendTools);
tools.push(...processedHumanInTheLoopTools.tools);
return tools;
}, [
frontendToolsList,
builtInFrontendTools,
processedHumanInTheLoopTools
]);
const allRenderToolCalls = (0, react.useMemo)(() => {
const combined = [...renderToolCallsList];
[...frontendToolsList, ...builtInFrontendTools].forEach((tool) => {
if (tool.render) {
const args = tool.parameters || (tool.name === "*" ? zod.z.any() : void 0);
if (args) combined.push({
name: tool.name,
args,
render: tool.render
});
}
});
combined.push(...processedHumanInTheLoopTools.renderToolCalls);
return combined;
}, [
renderToolCallsList,
frontendToolsList,
builtInFrontendTools,
processedHumanInTheLoopTools
]);
if (copilotkitRef.current === null) {
copilotkitRef.current = new CopilotKitCoreReact({
runtimeUrl: chatApiEndpoint,
deferInitialConnection: true,
runtimeTransport: useSingleEndpoint === true ? "single" : useSingleEndpoint === false ? "rest" : "auto",
headers: mergedHeaders,
credentials,
properties,
agents__unsafe_dev_only: mergedAgents,
tools: allTools,
renderToolCalls: allRenderToolCalls,
renderActivityMessages: allActivityRenderers,
renderCustomMessages: renderCustomMessagesList,
debug
});
if (defaultThrottleMs !== void 0) copilotkitRef.current.setDefaultThrottleMs(defaultThrottleMs);
}
const copilotkit = copilotkitRef.current;
(0, react.useEffect)(() => {
if (!rawCatalog) return;
if (!(rawCatalog instanceof _copilotkit_a2ui_renderer.Catalog)) return;
const components = Array.from(rawCatalog.components.values()).map((comp) => ({
name: comp.name,
description: void 0,
schema: comp.schema
}));
copilotkit.setCatalogComponents(components);
const subscription = copilotkit.subscribe({ onCatalogComponentsChanged: () => {
setCatalogToggleVersion((v) => v + 1);
} });
return () => subscription.unsubscribe();
}, [copilotkit, rawCatalog]);
(0, react.useEffect)(() => {
const syncRuntimeInfo = () => {
setRuntimeA2UIEnabled(copilotkit.a2uiEnabled);
setRuntimeOpenGenUIEnabled(copilotkit.openGenerativeUIEnabled);
setRuntimeLicenseStatus(copilotkit.licenseStatus);
};
const subscription = copilotkit.subscribe({ onRuntimeConnectionStatusChanged: syncRuntimeInfo });
syncRuntimeInfo();
return () => {
subscription.unsubscribe();
};
}, [copilotkit]);
const [, forceUpdate] = (0, react.useReducer)((x) => x + 1, 0);
(0, react.useEffect)(() => {
const subscription = copilotkit.subscribe({ onRenderToolCallsChanged: () => {
forceUpdate();
} });
return () => {
subscription.unsubscribe();
};
}, [copilotkit]);
const [executingToolCallIds, setExecutingToolCallIds] = (0, react.useState)(() => /* @__PURE__ */ new Set());
(0, react.useEffect)(() => {
const subscription = copilotkit.subscribe({
onToolExecutionStart: ({ toolCallId }) => {
setExecutingToolCallIds((prev) => {
if (prev.has(toolCallId)) return prev;
const next = new Set(prev);
next.add(toolCallId);
return next;
});
},
onToolExecutionEnd: ({ toolCallId }) => {
setExecutingToolCallIds((prev) => {
if (!prev.has(toolCallId)) return prev;
const next = new Set(prev);
next.delete(toolCallId);
return next;
});
}
});
return () => {
subscription.unsubscribe();
};
}, [copilotkit]);
const onErrorRef = (0, react.useRef)(onError);
(0, react.useEffect)(() => {
onErrorRef.current = onError;
}, [onError]);
(0, react.useEffect)(() => {
const subscription = copilotkit.subscribe({ onError: (event) => {
if (onErrorRef.current) onErrorRef.current(event);
else console.error(`[CopilotKit] Error (${event.code}):`, event.error, event.context ?? {});
} });
return () => {
subscription.unsubscribe();
};
}, [copilotkit]);
(0, react.useEffect)(() => {
copilotkit.setRuntimeUrl(chatApiEndpoint);
copilotkit.setRuntimeTransport(useSingleEndpoint === true ? "single" : useSingleEndpoint === false ? "rest" : "auto");
copilotkit.setHeaders(mergedHeaders);
copilotkit.setCredentials(credentials);
copilotkit.setProperties(a2uiCatalogProvided ? {
...properties,
a2uiCatalogAvailable: true
} : properties);
copilotkit.setAgents__unsafe_dev_only(mergedAgents);
copilotkit.setDebug(debug);
copilotkit.connect();
}, [
copilotkit,
chatApiEndpoint,
mergedHeaders,
credentials,
properties,
a2uiCatalogProvided,
mergedAgents,
useSingleEndpoint,
debug
]);
const didMountRef = (0, react.useRef)(false);
(0, react.useEffect)(() => {
if (!didMountRef.current) return;
copilotkit.setTools(allTools);
}, [copilotkit, allTools]);
(0, react.useEffect)(() => {
if (!didMountRef.current) return;
copilotkit.setRenderToolCalls(allRenderToolCalls);
}, [copilotkit, allRenderToolCalls]);
(0, react.useEffect)(() => {
if (!didMountRef.current) return;
copilotkit.setRenderActivityMessages(allActivityRenderers);
}, [copilotkit, allActivityRenderers]);
(0, react.useEffect)(() => {
if (!didMountRef.current) return;
copilotkit.setRenderCustomMessages(renderCustomMessagesList);
}, [copilotkit, renderCustomMessagesList]);
(0, react.useEffect)(() => {
didMountRef.current = true;
}, []);
(0, react.useEffect)(() => {
copilotkit.setDefaultThrottleMs(defaultThrottleMs);
}, [copilotkit, defaultThrottleMs]);
const designSkill = openGenerativeUI?.designSkill ?? DEFAULT_DESIGN_SKILL;
(0, react.useLayoutEffect)(() => {
if (!copilotkit || !openGenUIActive) return;
const id = copilotkit.addContext({
description: "Design guidelines for the generateSandboxedUi tool. Follow these when building UI.",
value: designSkill
});
return () => {
copilotkit.removeContext(id);
};
}, [
copilotkit,
designSkill,
openGenUIActive
]);
const sandboxFunctionsDescriptors = (0, react.useMemo)(() => {
if (sandboxFunctionsList.length === 0) return null;
return JSON.stringify(sandboxFunctionsList.map((fn) => ({
name: fn.name,
description: fn.description,
parameters: (0, _copilotkit_shared.schemaToJsonSchema)(fn.parameters, { zodToJsonSchema: zodToJsonSchemaAdapter })
})));
}, [sandboxFunctionsList]);
(0, react.useLayoutEffect)(() => {
if (!copilotkit || !sandboxFunctionsDescriptors || !openGenUIActive) return;
const id = copilotkit.addContext({
description: "Sandbox functions available in generated sandboxed UI code. Call via: await Websandbox.connection.remote.<functionName>(args)",
value: sandboxFunctionsDescriptors
});
return () => {
copilotkit.removeContext(id);
};
}, [
copilotkit,
sandboxFunctionsDescriptors,
openGenUIActive
]);
const contextValue = (0, react.useMemo)(() => ({
copilotkit,
executingToolCallIds
}), [copilotkit, executingToolCallIds]);
const licenseContextValue = (0, react.useMemo)(() => (0, _copilotkit_shared.createLicenseContextValue)(runtimeLicenseStatus), [runtimeLicenseStatus]);
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SandboxFunctionsContext.Provider, {
value: sandboxFunctionsList,
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_copilotkit_react_core_v2_context.CopilotKitContext.Provider, {
value: contextValue,
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_copilotkit_react_core_v2_context.LicenseContext.Provider, {
value: licenseContextValue,
children: [
a2uiActive && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(A2UIBuiltInToolCallRenderer, {}),
a2uiActive && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(A2UICatalogContext, {
catalog: filteredCatalog,
includeSchema: a2ui?.includeSchema
}),
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)(CopilotKitInspectorContextProvider, {
value: inspectorContextValue,
children: [children, shouldRenderInspector ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CopilotKitInspector, {
core: copilotkit,
defaultAnchor: inspectorDefaultAnchor,
openRequest: inspectorOpenRequest
}) : null]
}),
runtimeLicenseStatus === "none" && !resolvedPublicKey && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(LicenseWarningBanner, { type: "no_license" }),
runtimeLicenseStatus === "expired" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(LicenseWarningBanner, { type: "expired" }),
runtimeLicenseStatus === "invalid" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(LicenseWarningBanner, { type: "invalid" }),
runtimeLicenseStatus === "expiring" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(LicenseWarningBanner, { type: "expiring" })
]
})
})
});
};
//#endregion
//#region src/v2/hooks/use-render-custom-messages.tsx
function useRenderCustomMessages() {
const { copilotkit } = (0, _copilotkit_react_core_v2_context.useCopilotKit)();
const config = useCopilotChatConfiguration();
if (!config) return null;
const { agentId, threadId } = config;
const customMessageRenderers = copilotkit.renderCustomMessages.filter((renderer) => renderer.agentId === void 0 || renderer.agentId === agentId).sort((a, b) => {
const aHasAgent = a.agentId !== void 0;
if (aHasAgent === (b.agentId !== void 0)) return 0;
return aHasAgent ? -1 : 1;
});
return function(params) {
if (!customMessageRenderers.length) return null;
const { message, position } = params;
const resolvedRunId = copilotkit.getRunIdForMessage(agentId, threadId, message.id) ?? copilotkit.getRunIdsForThread(agentId, threadId).slice(-1)[0];
const runId = resolvedRunId ?? `missing-run-id:${message.id}`;
const agent = copilotkit.getAgent(agentId);
if (!agent) return null;
const messagesIdsInRun = resolvedRunId ? agent.messages.filter((msg) => copilotkit.getRunIdForMessage(agentId, threadId, msg.id) === resolvedRunId).map((msg) => msg.id) : [message.id];
const rawMessageIndex = agent.messages.findIndex((msg) => msg.id === message.id);
const messageIndex = rawMessageIndex >= 0 ? rawMessageIndex : 0;
const messageIndexInRun = resolvedRunId ? Math.max(messagesIdsInRun.indexOf(message.id), 0) : 0;
const numberOfMessagesInRun = resolvedRunId ? messagesIdsInRun.length : 1;
const stateSnapshot = resolvedRunId ? copilotkit.getStateByRun(agentId, threadId, resolvedRunId) : void 0;
let result = null;
for (const renderer of customMessageRenderers) {
if (!renderer.render) continue;
const Component = renderer.render;
result = /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Component, {
message,
position,
runId,
messageIndex,
messageIndexInRun,
numberOfMessagesInRun,
agentId,
stateSnapshot
}, `${runId}-${message.id}-${position}`);
if (result) break;
}
return result;
};
}
//#endregion
//#region src/v2/hooks/use-render-activity-message.tsx
function useRenderActivityMessage() {
const { copilotkit } = (0, _copilotkit_react_core_v2_context.useCopilotKit)();
const agentId = useCopilotChatConfiguration()?.agentId ?? _copilotkit_shared.DEFAULT_AGENT_ID;
const renderers = copilotkit.renderActivityMessages;
const findRenderer = (0, react.useCallback)((activityType) => {
if (!renderers.length) return null;
const matches = renderers.filter((renderer) => renderer.activityType === activityType);
return matches.find((candidate) => candidate.agentId === agentId) ?? matches.find((candidate) => candidate.agentId === void 0) ?? renderers.find((candidate) => candidate.activityType === "*") ?? null;
}, [agentId, renderers]);
const renderActivityMessage = (0, react.useCallback)((message) => {
const renderer = findRenderer(message.activityType);
if (!renderer) return null;
const parseResult = renderer.content["~standard"].validate(message.content);
if (parseResult instanceof Promise) {
console.warn(`Async content validation is not supported for activity message '${message.activityType}'`);
return null;
}
if (parseResult.issues) {
console.warn(`Failed to parse content for activity message '${message.activityType}':`, parseResult.issues);
return null;
}
const Component = renderer.render;
const agent = copilotkit.getAgent(agentId);
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Component, {
activityType: message.activityType,
content: parseResult.value,
message,
agent
}, message.id);
}, [
agentId,
copilotkit,
findRenderer
]);
return (0, react.useMemo)(() => ({
renderActivityMessage,
findRenderer
}), [renderActivityMessage, findRenderer]);
}
//#endregion
//#region src/v2/hooks/use-frontend-tool.tsx
const EMPTY_DEPS$1 = [];
function useFrontendTool(tool, deps) {
const { copilotkit } = (0, _copilotkit_react_core_v2_context.useCopilotKit)();
const extraDeps = deps ?? EMPTY_DEPS$1;
(0, react.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__ */ (0, react_jsx_runtime.jsx)(Component, { ...args });
},
agentId: config.agentId,
followUp: config.followUp
}, deps);
}
//#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 } = (0, _copilotkit_react_core_v2_context.useCopilotKit)();
const extraDeps = deps ?? EMPTY_DEPS;
(0, react.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 === _copilotkit_core.ToolCallStatus.InProgress) return config.render({
...props,
parameters: props.args
});
if (props.status === _copilotkit_core.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-default-render-tool.tsx
/**
* Module-level dedup set so an unknown status value only emits a console
* warning the FIRST time we encounter it. Otherwise a stuck/unmapped status
* would log on every re-render (potentially many per second).
*/
const warnedUnknownStatuses = /* @__PURE__ */ new Set();
/**
* Map a {@link ToolCallStatus} enum value to the documented string-union
* status the {@link DefaultRenderProps} contract exposes. Unknown / future
* enum members log a warning (once per distinct value) and fall back to
* `"inProgress"`.
*/
function mapToolCallStatus(status) {
switch (status) {
case _copilotkit_core.ToolCallStatus.Complete: return "complete";
case _copilotkit_core.ToolCallStatus.Executing: return "executing";
case _copilotkit_core.ToolCallStatus.InProgress: return "inProgress";
default: {
const key = String(status);
if (!warnedUnknownStatuses.has(key)) {
warnedUnknownStatuses.add(key);
console.warn(`[CopilotKit] Unknown ToolCallStatus "${key}" in default tool-call renderer; falling back to "inProgress".`);
}
return "inProgress";
}
}
}
/**
* Convert the framework-internal renderer props (`args`, enum status) into
* the documented {@link DefaultRenderProps} shape (`parameters`, string-union
* status) so a user `config.render` always sees the documented contract.
*/
function adaptRendererProps(props) {
return {
name: props.name,
toolCallId: props.toolCallId,
parameters: props.args,
status: mapToolCallStatus(props.status),
result: props.result
};
}
/**
* Registers a wildcard (`"*"`) tool-call renderer via `useRenderTool`.
*
* - Call with no config to use CopilotKit's built-in default tool-call card.
* - Pass `config.render` to replace the default UI with your own fallback renderer.
*
* This is useful when you want a generic renderer for tools that do not have a
* dedicated `useRenderTool({ name: "..." })` registration.
*
* @param config - Optional custom wildcard render function.
* @param deps - Optional dependencies to refresh registration.
*
* @example
* ```tsx
* useDefaultRenderTool();
* ```
*
* @example
* ```tsx
* useDefaultRenderTool({
* render: ({ name, status }) => <div>{name}: {status}</div>,
* });
* ```
*
* @example
* ```tsx
* useDefaultRenderTool(
* {
* render: ({ name, result }) => (
* <ToolEventRow title={name} payload={result} compact={compactMode} />
* ),
* },
* [compactMode],
* );
* ```
*/
function useDefaultRenderTool(config, deps) {
const userRender = config?.render;
useRenderTool({
name: "*",
render: userRender ? (raw) => userRender(adaptRendererProps(raw)) : (raw) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DefaultToolCallRenderer, { ...adaptRendererProps(raw) })
}, deps);
}
/**
* Guarded JSON.stringify used inside the expanded `<pre>` blocks. A circular
* reference would otherwise crash the entire React tree on render.
*/
function safeStringifyForPre(value) {
try {
return JSON.stringify(value, null, 2);
} catch (err) {
console.warn("[CopilotKit] Failed to JSON.stringify tool-call payload for default renderer; falling back to String():", err);
try {
return String(value);
} catch (innerErr) {
console.warn("[CopilotKit] safeStringifyForPre: value could not be stringified:", innerErr);
return "[unserializable]";
}
}
}
function DefaultToolCallRenderer({ name, toolCallId, parameters, status, result }) {
const [isExpanded, setIsExpanded] = (0, react.useState)(false);
const isActive = status === "inProgress" || status === "executing";
const isComplete = status === "complete";
const statusLabel = isActive ? "Running" : isComplete ? "Done" : status;
const dotClassName = isActive ? "cpk:bg-amber-500" : isComplete ? "cpk:bg-emerald-500" : "cpk:bg-zinc-400";
const badgeClassName = isActive ? "cpk:bg-amber-100 cpk:text-amber-800 cpk:dark:bg-amber-500/15 cpk:dark:text-amber-400" : isComplete ? "cpk:bg-emerald-100 cpk:text-emerald-800 cpk:dark:bg-emerald-500/15 cpk:dark:text-emerald-400" : "cpk:bg-zinc-100 cpk:text-zinc-800 cpk:dark:bg-zinc-700/40 cpk:dark:text-zinc-300";
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
"data-testid": "copilot-tool-render",
"data-tool-name": name,
"data-tool-call-id": toolCallId,
"data-status": status,
"data-args": safeStringifyForAttr(parameters),
"data-result": safeStringifyForAttr(result),
className: "cpk:mt-2 cpk:pb-2",
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
className: "cpk:rounded-xl cpk:border cpk:border-zinc-200/60 cpk:bg-white/70 cpk:p-4 cpk:shadow-sm cpk:backdrop-blur cpk:dark:border-zinc-800/60 cpk:dark:bg-zinc-900/50",
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
type: "button",
"aria-expanded": isExpanded,
onClick: () => setIsExpanded(!isExpanded),
className: "cpk:flex cpk:w-full cpk:cursor-pointer cpk:select-none cpk:items-center cpk:justify-between cpk:gap-2.5 cpk:border-none cpk:bg-transparent cpk:p-0 cpk:m-0 cpk:text-left cpk:text-inherit",
style: { font: "inherit" },
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
className: "cpk:flex cpk:min-w-0 cpk:items-center cpk:gap-2",
children: [
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
className: `cpk:h-3.5 cpk:w-3.5 cpk:flex-shrink-0 cpk:text-zinc-500 cpk:transition-transform cpk:dark:text-zinc-400 ${isExpanded ? "cpk:rotate-90" : ""}`,
fill: "none",
viewBox: "0 0 24 24",
strokeWidth: 2,
stroke: "currentColor",
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
strokeLinecap: "round",
strokeLinejoin: "round",
d: "M8.25 4.5l7.5 7.5-7.5 7.5"
})
}),
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: `cpk:inline-block cpk:h-2 cpk:w-2 cpk:flex-shrink-0 cpk:rounded-full ${dotClassName}` }),
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
"data-testid": "copilot-tool-render-name",
className: "cpk:truncate cpk:text-[13px] cpk:font-semibold cpk:text-zinc-900 cpk:dark:text-zinc-100",
children: name
})
]
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
"data-testid": "copilot-tool-render-status",
className: `cpk:inline-flex cpk:flex-shrink-0 cpk:items-center cpk:rounded-full cpk:px-2 cpk:py-0.5 cpk:text-[11px] cpk:font-medium ${badgeClassName}`,
children: statusLabel
})]
}), isExpanded && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
className: "cpk:mt-3 cpk:grid cpk:gap-3",
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
className: "cpk:text-[10px] cpk:uppercase cpk:text-zinc-500 cpk:dark:text-zinc-400",
children: "Arguments"
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("pre", {
className: "cpk:mt-1.5 cpk:max-h-[200px] cpk:overflow-auto cpk:rounded-md cpk:bg-zinc-100 cpk:p-2.5 cpk:text-[11px] cpk:leading-relaxed cpk:text-zinc-800 cpk:whitespace-pre-wrap cpk:break-words cpk:dark:bg-zinc-800/60 cpk:dark:text-zinc-200",
children: safeStringifyForPre(parameters ?? {})
})] }), result !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
className: "cpk:text-[10px] cpk:uppercase cpk:text-zinc-500 cpk:dark:text-zinc-400",
children: "Result"
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("pre", {
className: "cpk:mt-1.5 cpk:max-h-[200px] cpk:overflow-auto cpk:rounded-md cpk:bg-zinc-100 cpk:p-2.5 cpk:text-[11px] cpk:leading-relaxed cpk:text-zinc-800 cpk:whitespace-pre-wrap cpk:break-words cpk:dark:bg-zinc-800/60 cpk:dark:text-zinc-200",
children: typeof result === "string" ? result : safeStringifyForPre(result)
})] })]
})]
})
});
}
function safeStringifyForAttr(value) {
if (value === void 0 || value === null) return "";
if (typeof value === "string") return value;
try {
return JSON.stringify(value);
} catch (err) {
console.warn("[CopilotKit] Failed to JSON.stringify tool-call payload for data-* attribute; falling back to String():", err);
try {
return String(value);
} catch (innerErr) {
console.warn("[CopilotKit] safeStringifyForAttr: value could not be stringified:", innerErr);
return "";
}
}
}
//#endregion
//#region src/v2/hooks/use-human-in-the-loop.tsx
function useHumanInTheLoop(tool, deps) {
const { copilotkit } = (0, _copilotkit_react_core_v2_context.useCopilotKit)();
const resolvePromiseRef = (0, react.useRef)(null);
const cleanupAbortRef = (0, react.useRef)(null);
const respond = (0, react.useCallback)(async (result) => {
if (resolvePromiseRef.current) {
cleanupAbortRef.current?.();
cleanupAbortRef.current = null;
resolvePromiseRef.current(result);
resolvePromiseRef.current = null;
}
}, []);
const handler = (0, react.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 = (0, react.useCallback)((props) => {
const ToolComponent = tool.render;
if (props.status === _copilotkit_core.ToolCallStatus.InProgress) {
const enhancedProps = {
...props,
name: tool.name,
description: tool.description || "",
agentId: tool.agentId,
respond: void 0
};
return react.default.createElement(ToolComponent, enhancedProps);
} else if (props.status === _copilotkit_core.ToolCallStatus.Executing) {
const enhancedProps = {
...props,
name: tool.name,
description: tool.description || "",
agentId: tool.agentId,
respond
};
return react.default.createElement(ToolComponent, enhancedProps);
} else if (props.status === _copilotkit_core.ToolCallStatus.Complete) {
const enhancedProps = {
...props,
name: tool.name,
description: tool.description || "",
agentId: tool.agentId,
respond: void 0
};
return react.default.createElement(ToolComponent, enhancedProps);
}
return props;
}, [
tool.render,
tool.name,
tool.description,
tool.agentId,
respond
]);
useFrontendTool({
...tool,
handler,
render: RenderComponent
}, deps);
(0, react.useEffect)(() => {
return () => {
copilotkit.removeHookRenderToolCall(tool.name, tool.agentId);
};
}, [
copilotkit,
tool.name,
tool.agentId
]);
}
//#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 "${_copilotkit_shared.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 ?? _copilotkit_shared.DEFAULT_AGENT_ID;
const { copilotkit } = (0, _copilotkit_react_core_v2_context.useCopilotKit)();
const providerThrottleMs = copilotkit.defaultThrottleMs;
const [, forceUpdate] = (0, react.useReducer)((x) => x + 1, 0);
const updateFlags = (0, react.useMemo)(() => updates ?? ALL_UPDATES, [JSON.stringify(updates)]);
const provisionalAgentCache = (0, react.useRef)(/* @__PURE__ */ new Map());
const [registeredProxyAgent, setRegisteredProxyAgent] = (0, react.useState)(null);
(0, react.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 } = (0, react.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 _copilotkit_core.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 === _copilotkit_core.CopilotKitCoreRuntimeConnectionStatus.Disconnected || status === _copilotkit_core.CopilotKitCoreRuntimeConnectionStatus.Connecting)) {
const cached = provisionalAgentCache.current.get(resolvedAgentId);
if (cached) return {
agent: cached,
isReady: false
};
const provisional = new _copilotkit_core.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 === _copilotkit_core.CopilotKitCoreRuntimeConnectionStatus.Error) {
const cached = provisionalAgentCache.current.get(resolvedAgentId);
if (cached) return {
agent: cached,
isReady: false
};
const provisional = new _copilotkit_core.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)
]);
(0, react.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
]);
(0, react.useEffect)(() => {
if (agent instanceof _ag_ui_client.HttpAgent) copilotkit.applyHeadersToAgent(agent);
if (agent instanceof _copilotkit_core.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);
(0, react.useEffect)(() => {
if (!resolvedThreadId) return;
agent.threadId = resolvedThreadId;
}, [agent, resolvedThreadId]);
return {
agent,
isReady
};
}
//#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
//#region src/v2/hooks/use-agent-context.tsx
function useAgentContext(context) {
const { description, value } = context;
const { copilotkit } = (0, _copilotkit_react_core_v2_context.useCopilotKit)();
const stringValue = (0, react.useMemo)(() => {
if (typeof value === "string") return value;
return JSON.stringify(value);
}, [value]);
(0, react.useLayoutEffect)(() => {
if (!copilotkit) return;
const id = copilotkit.addContext({
description,
value: stringValue
});
return () => {
copilotkit.removeContext(id);
};
}, [
description,
stringValue,
copilotkit
]);
}
//#endregion
//#region src/v2/hooks/use-suggestions.tsx
function useSuggestions({ agentId } = {}) {
const { copilotkit } = (0, _copilotkit_react_core_v2_context.useCopilotKit)();
const config = useCopilotChatConfiguration();
const resolvedAgentId = (0, react.useMemo)(() => agentId ?? config?.agentId ?? _copilotkit_shared.DEFAULT_AGENT_ID, [agentId, config?.agentId]);
const [suggestions, setSuggestions] = (0, react.useState)(() => {
return copilotkit.getSuggestions(resolvedAgentId).suggestions;
});
const [isLoading, setIsLoading] = (0, react.useState)(() => {
return copilotkit.getSuggestions(resolvedAgentId).isLoading;
});
(0, react.useEffect)(() => {
const result = copilotkit.getSuggestions(resolvedAgentId);
setSuggestions(result.suggestions);
setIsLoading(result.isLoading);
}, [copilotkit, resolvedAgentId]);
(0, react.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: (0, react.useCallback)(() => {
copilotkit.reloadSuggestions(resolvedAgentId);
}, [copilotkit, resolvedAgentId]),
clearSuggestions: (0, react.useCallback)(() => {
copilotkit.clearSuggestions(resolvedAgentId);
}, [copilotkit, resolvedAgentId]),
isLoading
};
}
//#endregion
//#region src/v2/hooks/use-configure-suggestions.tsx
function useConfigureSuggestions(config, deps) {
const { copilotkit } = (0, _copilotkit_react_core_v2_context.useCopilotKit)();
const chatConfig = useCopilotChatConfiguration();
const extraDeps = deps ?? [];
const resolvedConsumerAgentId = (0, react.useMemo)(() => chatConfig?.agentId ?? _copilotkit_shared.DEFAULT_AGENT_ID, [chatConfig?.agentId]);
const rawConsumerAgentId = (0, react.useMemo)(() => config ? config.consumerAgentId : void 0, [config]);
const normalizationCacheRef = (0, react.useRef)({
serialized: null,
config: null
});
const { normalizedConfig, serializedConfig } = (0, react.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 = (0, react.useRef)(null);
latestConfigRef.current = normalizedConfig;
const previousSerializedConfigRef = (0, react.useRef)(null);
const targetAgentId = (0, react.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 = (0, react.useMemo)(() => !!normalizedConfig && "instructions" in normalizedConfig, [normalizedConfig]);
const requestReload = (0, react.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
]);
(0, react.useEffect)(() => {
if (!serializedConfig || !latestConfigRef.current) return;
const id = copilotkit.addSuggestionsConfig(latestConfigRef.current);
requestReload();
return () => {
copilotkit.removeSuggestionsConfig(id);
};
}, [
copilotkit,
serializedConfig,
requestReload
]);
(0, react.useEffect)(() => {
if (!normalizedConfig) {
previousSerializedConfigRef.current = null;
return;
}
if (serializedConfig && previousSerializedConfigRef.current === serializedConfig) return;
if (serializedConfig) previousSerializedConfigRef.current = serializedConfig;
requestReload();
}, [
normalizedConfig,
requestReload,
serializedConfig
]);
(0, react.useEffect)(() => {
if (!normalizedConfig || extraDeps.length === 0) return;
requestReload();
}, [
extraDeps.length,
normalizedConfig,
requestReload,
...extraDeps
]);
(0, react.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-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 } = (0, _copilotkit_react_core_v2_context.useCopilotKit)();
const { agent } = useAgent({ agentId: config.agentId });
const [pending, setPending] = (0, react.useState)(null);
const pendingRef = (0, react.useRef)(pending);
pendingRef.current = pending;
const [handlerResult, setHandlerResult] = (0, react.useState)(null);
const interruptStateRef = (0, react.useRef)(new _copilotkit_core.ɵInterruptState());
const interruptRunIdsRef = (0, react.useRef)(/* @__PURE__ */ new Map());
const legacyRunIdRef = (0, react.useRef)(void 0);
(0, react.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 = (0, react.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: (0, _ag_ui_client.randomUUID)(),
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 = (0, react.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: (0, _ag_ui_client.randomUUID)(),
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 = (0, react.useRef)(config.render);
renderRef.current = config.render;
const enabledRef = (0, react.useRef)(config.enabled);
enabledRef.current = config.enabled;
const handlerRef = (0, react.useRef)(config.handler);
handlerRef.current = config.handler;
const resolveRef = (0, react.useRef)(resolve);
resolveRef.current = resolve;
const cancelRef = (0, react.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;
}
};
(0, react.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 = (0, react.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
]);
(0, react.useEffect)(() => {
if (config.renderInChat === false) return;
copilotkit.setInterruptElement(element);
}, [
element,
config.renderInChat,
copilotkit
]);
(0, react.useEffect)(() => {
if (config.renderInChat === false) return;
return () => {
copilotkit.setInterruptElement(null);
};
}, []);
if (config.renderInChat === false) return element;
}
//#endregion
//#region src/v2/hooks/use-threads.tsx
function useThreadStoreSelector(store, selector) {
return (0, react.useSyncExternalStore)((0, react.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$1({ agentId, includeArchived, limit, enabled = true }) {
const { copilotkit } = (0, _copilotkit_react_core_v2_context.useCopilotKit)();
const [store] = (0, react.useState)(() => (0, _copilotkit_core.ɵcreateThreadStore)({ fetch: globalThis.fetch }));
const coreThreads = useThreadStoreSelector(store, _copilotkit_core.ɵselectThreads);
const threads = (0, react.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, _copilotkit_core.ɵselectThreadsIsLoading);
const storeError = useThreadStoreSelector(store, _copilotkit_core.ɵselectThreadsError);
const fetchMoreError = useThreadStoreSelector(store, _copilotkit_core.ɵselectFetchMoreError);
const hasMoreThreads = useThreadStoreSelector(store, _copilotkit_core.ɵselectHasNextPage);
const isFetchingMoreThreads = useThreadStoreSelector(store, _copilotkit_core.ɵselectIsFetchingNextPage);
const isMutating = useThreadStoreSelector(store, _copilotkit_core.ɵselectIsMutating);
const headersKey = (0, react.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 === _copilotkit_core.CopilotKitCoreRuntimeConnectionStatus.Connected && !threadListEndpointSupported;
const runtimeError = (0, react.useMemo)(() => {
if (copilotkit.runtimeUrl) return null;
return /* @__PURE__ */ new Error("Runtime URL is not configured");
}, [copilotkit.runtimeUrl]);
const threadEndpointsError = (0, react.useMemo)(() => {
if (!threadEndpointsUnavailable) return null;
return /* @__PURE__ */ new Error("Thread endpoints are not available on this CopilotKit runtime");
}, [threadEndpointsUnavailable]);
const threadMutationsError = (0, react.useMemo)(() => {
if (threadMutationsSupported) return null;
return /* @__PURE__ */ new Error("Thread mutations are not available on this CopilotKit runtime");
}, [threadMutationsSupported]);
const [hasDispatchedContext, setHasDispatchedContext] = (0, react.useState)(false);
const preConnectLoading = enabled && !!copilotkit.runtimeUrl && !threadEndpointsUnavailable && !hasDispatchedContext;
const [configErrorDismissed, setConfigErrorDismissed] = (0, react.useState)(false);
(0, react.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;
(0, react.useEffect)(() => {
store.start();
return () => {
store.stop();
};
}, [store]);
(0, react.useEffect)(() => {
if (!enabled) return;
copilotkit.registerThreadStore(agentId, store);
return () => {
copilotkit.unregisterThreadStore(agentId);
};
}, [
copilotkit,
agentId,
store,
enabled
]);
(0, react.useEffect)(() => {
if (!enabled) {
store.setContext(null);
setHasDispatchedContext(false);
return;
}
if (!copilotkit.runtimeUrl) {
store.setContext(null);
setHasDispatchedContext(false);
return;
}
if (runtimeStatus !== _copilotkit_core.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 = (0, react.useCallback)((mutation) => {
return (...args) => {
if (threadMutationsError) return Promise.reject(threadMutationsError);
return mutation(...args);
};
}, [threadMutationsError]);
const renameThread = (0, react.useMemo)(() => guardMutation((threadId, name) => store.renameThread(threadId, name)), [store, guardMutation]);
const archiveThread = (0, react.useMemo)(() => guardMutation((threadId) => store.archiveThread(threadId)), [store, guardMutation]);
const unarchiveThread = (0, react.useMemo)(() => guardMutation((threadId) => store.unarchiveThread(threadId)), [store, guardMutation]);
const deleteThread = (0, react.useMemo)(() => guardMutation((threadId) => store.deleteThread(threadId)), [store, guardMutation]);
return {
threads,
isLoading,
error,
listError,
fetchMoreError,
hasMoreThreads,
isFetchingMoreThreads,
isMutating,
fetchMoreThreads: (0, react.useCallback)(() => store.fetchNextPage(), [store]),
refetchThreads: (0, react.useCallback)(() => store.refetchThreads(), [store]),
startNewThread: (0, react.useCallback)(() => {
setConfigErrorDismissed(true);
store.startNewThread();
}, [store]),
renameThread,
archiveThread,
unarchiveThread,
deleteThread
};
}
//#endregion
//#region src/v2/hooks/use-memories.tsx
function useMemoryStoreSelector(store, selector) {
return (0, react.useSyncExternalStore)((0, react.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 platform memories.
*
* Reads the memory store owned and wired by `CopilotKitCore`. On mount the
* hook exposes the live list plus stable `addMemory` / `updateMemory` /
* `removeMemory` / `refresh` callbacks. Mutations are server-authoritative:
* each resolves once the platform confirms the operation and rejects with an
* `Error` on failure.
*
* Realtime updates are automatic: the core's memory store opens its own
* `user_meta:memories:<joinCode>` channel and applies `memory_metadata` deltas
* to the list. You can still call `refresh()` to re-pull the REST snapshot on
* demand.
*
* @returns Memory list state and stable mutation callbacks.
*
* @example
* ```tsx
* import { useMemories } from "@copilotkit/react-core";
*
* function MemoryList() {
* const { memories, isLoading, isAvailable, addMemory, removeMemory } =
* useMemories();
*
* if (!isAvailable) return null;
* if (isLoading) return <p>Loading…</p>;
*
* return (
* <ul>
* {memories.map((m) => (
* <li key={m.id}>
* {m.content}
* <button onClick={() => removeMemory(m.id)}>Delete</button>
* </li>
* ))}
* </ul>
* );
* }
* ```
*/
function useMemories() {
const { copilotkit } = (0, _copilotkit_react_core_v2_context.useCopilotKit)();
const store = copilotkit.getMemoryStore();
return {
memories: useMemoryStoreSelector(store, _copilotkit_core.ɵselectMemories),
isLoading: useMemoryStoreSelector(store, _copilotkit_core.ɵselectMemoriesIsLoading),
error: useMemoryStoreSelector(store, _copilotkit_core.ɵselectMemoriesError),
isAvailable: useMemoryStoreSelector(store, _copilotkit_core.ɵselectMemoriesAvailable),
realtimeStatus: useMemoryStoreSelector(store, _copilotkit_core.ɵselectMemoriesRealtimeStatus),
refresh: (0, react.useCallback)(() => store.refresh(), [store]),
addMemory: (0, react.useCallback)((input) => store.addMemory(input), [store]),
updateMemory: (0, react.useCallback)((id, changes) => store.updateMemory(id, changes), [store]),
removeMemory: (0, react.useCallback)((id) => store.removeMemory(id), [store])
};
}
//#endregion
//#region src/v2/lib/record-annotation.ts
/**
* Low-level function that posts an arbitrary annotation to the CopilotKit
* runtime's general annotation endpoint (`POST /annotate`).
*
* This is the single transport entry point for annotations. Higher-level
* hooks such as `useLearnFromUserAction` build the `type`/`payload` pair
* for their specific annotation shape and delegate the HTTP call here.
*
* The function uses the same transport as `useLearnFromUserAction`:
* - `runtimeUrl` from `copilotkit.runtimeUrl` (BFF proxies to the platform)
* - `headers` from `copilotkit.headers` (customer auth forwarded to BFF)
* - `clientEventId` auto-generated via `randomUUID()` when omitted
* - `userId` is resolved server-side by the runtime; the client never sends it
* - Errors propagate to the caller (fire-and-propagate, not fire-and-forget)
*
* @param args - Transport dependencies plus annotation fields.
* @returns The platform result containing the annotation row `id` and a
* `duplicate` flag.
* @throws When the network request fails or the runtime returns a non-2xx
* status. Callers that want fire-and-forget behavior should `.catch`
* at the call site.
*/
async function recordAnnotation(args) {
const { runtimeUrl, headers, type, payload, threadId, occurredAt } = args;
const body = {
type,
threadId,
clientEventId: args.clientEventId ?? (0, _copilotkit_shared.randomUUID)(),
...payload !== void 0 ? { payload } : {},
...occurredAt !== void 0 ? { occurredAt } : {}
};
const response = await fetch(`${runtimeUrl}/annotate`, {
method: "POST",
headers: {
"Content-Type": "application/json",
...headers
},
body: JSON.stringify(body)
});
if (!response.ok) {
const text = await response.text().catch(() => "");
throw new Error(`recordAnnotation: request failed (${response.status})${text ? `: ${text}` : ""}`);
}
const text = await response.text();
if (!text) throw new Error(`recordAnnotation: runtime ${runtimeUrl}/annotate returned ${response.status} with an empty body`);
try {
return JSON.parse(text);
} catch {
throw new Error(`recordAnnotation: runtime ${runtimeUrl}/annotate returned a non-JSON body (status ${response.status})`);
}
}
//#endregion
//#region src/v2/hooks/use-learn-from-user-action.tsx
/**
* Record a user UI interaction in the Intelligence platform's user-actions
* stream. The platform's auto-curated knowledge base agent reads these
* (alongside finished agent runs) and writes free-form Obsidian-flavored
* markdown to `/project`, where any agent in the same project can later
* read it via the `copilotkit_knowledge_base_shell` MCP tool.
*
* The hook returns a stable function. Calling it issues a request to the
* customer's CopilotKit runtime (`POST ${runtimeUrl}/annotate`), which
* resolves the Intel user from the BFF's auth and forwards to the
* platform — the Intel API key never reaches the browser.
*
* If `clientEventId` is omitted `recordAnnotation` generates a UUID per call,
* so a naive double-call (e.g. React 18 strict-mode double-mount, or a retry
* after a network blip on a fresh Promise) is naturally safe. Supply your
* own key when a single semantic event must remain idempotent across
* multiple `learnFromUserAction(...)` calls.
*
* @example
* ```tsx
* import { useLearnFromUserAction } from "@copilotkit/react-core";
*
* function SettingsPage({ threadId }) {
* const learnFromUserAction = useLearnFromUserAction();
*
* const onRename = (oldName: string, newName: string) => {
* void learnFromUserAction({
* threadId,
* title: "Renamed project",
* data: { previous: { name: oldName }, next: { name: newName } },
* });
* };
* }
* ```
*/
function useLearnFromUserAction() {
const { copilotkit } = (0, _copilotkit_react_core_v2_context.useCopilotKit)();
return (0, react.useCallback)(async (input) => {
const runtimeUrl = copilotkit.runtimeUrl;
if (!runtimeUrl) throw new Error("useLearnFromUserAction: runtimeUrl is not configured. Set it on <CopilotKitProvider runtimeUrl=...>.");
const payload = {
...input.title !== void 0 ? { title: input.title } : {},
...input.description !== void 0 ? { description: input.description } : {},
...input.data !== void 0 ? { data: input.data } : {}
};
return recordAnnotation({
runtimeUrl,
headers: copilotkit.headers ?? {},
type: "user_action",
payload: Object.keys(payload).length > 0 ? payload : void 0,
threadId: input.threadId,
clientEventId: input.clientEventId,
occurredAt: input.occurredAt
});
}, [copilotkit]);
}
//#endregion
//#region src/v2/hooks/use-learn-from-user-action-in-current-thread.tsx
/**
* Record a user UI interaction against the **current chat's** thread. The
* `threadId` is sourced from the surrounding
* `<CopilotChatConfigurationProvider>` (the same provider `<CopilotChat>`,
* `<CopilotSidebar>`, and friends set up), so callers in a chat-aware
* subtree don't need to thread an id through manually.
*
* Throws on **call** (not on mount) when there is no chat-config provider
* in scope — matches the "throw on call when runtimeUrl is missing"
* behavior of {@link useLearnFromUserAction}. Mounting the hook in a branch
* that never fires is harmless.
*
* The recorder does NOT accept a `threadId` override. If you need to
* record against an explicit thread, use {@link useLearnFromUserAction}
* directly — two hooks, two crisp contracts, no mode confusion.
*
* This hook always uses `config.threadId`, regardless of whether the
* surrounding chat config minted it internally or received one from
* the caller. Auto-minted threads simply mean the action lands under
* a thread the platform never saw — the writer agent still distills
* user-action-only threads (it does not require the thread to exist
* in `cpki.threads`), so the loop keeps learning.
*
* @example
* ```tsx
* import { useLearnFromUserActionInCurrentThread } from "@copilotkit/react-core";
*
* function SettingsPanel() {
* const learnFromUserAction = useLearnFromUserActionInCurrentThread();
*
* const onRename = (oldName: string, newName: string) => {
* void learnFromUserAction({
* title: "Renamed project",
* data: { previous: { name: oldName }, next: { name: newName } },
* });
* };
*
* // ...
* }
* ```
*/
function useLearnFromUserActionInCurrentThread() {
const config = useCopilotChatConfiguration();
const learnFromUserAction = useLearnFromUserAction();
return (0, react.useCallback)(async (input) => {
const threadId = config?.threadId;
if (!threadId) throw new Error("useLearnFromUserActionInCurrentThread: no CopilotChatConfigurationProvider in scope. Wrap the call site in <CopilotChat>, <CopilotSidebar>, or <CopilotChatConfigurationProvider>, or use `useLearnFromUserAction()` and pass `threadId` explicitly.");
return learnFromUserAction({
...input,
threadId
});
}, [config?.threadId, learnFromUserAction]);
}
//#endregion
//#region src/v2/hooks/use-attachments.tsx
/**
* Hook that manages file attachment state — uploads, drag-and-drop, paste,
* and lifecycle. All returned callbacks are referentially stable across
* renders (via useCallback) to avoid destabilizing downstream memoization.
*/
function useAttachments({ config }) {
const enabled = config?.enabled ?? false;
const [attachments, setAttachments] = (0, react.useState)([]);
const [dragOver, setDragOver] = (0, react.useState)(false);
const fileInputRef = (0, react.useRef)(null);
const containerRef = (0, react.useRef)(null);
const configRef = (0, react.useRef)(config);
configRef.current = config;
const attachmentsRef = (0, react.useRef)([]);
attachmentsRef.current = attachments;
const processFiles = (0, react.useCallback)(async (files) => {
const cfg = configRef.current;
const accept = cfg?.accept ?? "*/*";
const maxSize = cfg?.maxSize ?? 20 * 1024 * 1024;
const rejectedFiles = files.filter((file) => !(0, _copilotkit_shared.matchesAcceptFilter)(file, accept));
for (const file of rejectedFiles) cfg?.onUploadFailed?.({
reason: "invalid-type",
file,
message: `File "${file.name}" is not accepted. Supported types: ${accept}`
});
const validFiles = files.filter((file) => (0, _copilotkit_shared.matchesAcceptFilter)(file, accept));
for (const file of validFiles) {
if ((0, _copilotkit_shared.exceedsMaxSize)(file, maxSize)) {
cfg?.onUploadFailed?.({
reason: "file-too-large",
file,
message: `File "${file.name}" exceeds the maximum size of ${(0, _copilotkit_shared.formatFileSize)(maxSize)}`
});
continue;
}
const modality = (0, _copilotkit_shared.getModalityFromMimeType)(file.type);
const placeholderId = (0, _copilotkit_shared.randomUUID)();
const placeholder = {
id: placeholderId,
type: modality,
source: {
type: "data",
value: "",
mimeType: file.type
},
filename: file.name,
size: file.size,
status: "uploading"
};
setAttachments((prev) => [...prev, placeholder]);
try {
let source;
let uploadMetadata;
if (cfg?.onUpload) {
const { metadata: meta, ...uploadSource } = await cfg.onUpload(file);
source = uploadSource;
uploadMetadata = meta;
} else source = {
type: "data",
value: await (0, _copilotkit_shared.readFileAsBase64)(file),
mimeType: file.type
};
let thumbnail;
if (modality === "video") thumbnail = await (0, _copilotkit_shared.generateVideoThumbnail)(file);
setAttachments((prev) => prev.map((att) => att.id === placeholderId ? {
...att,
source,
status: "ready",
thumbnail,
metadata: uploadMetadata
} : att));
} catch (error) {
setAttachments((prev) => prev.filter((att) => att.id !== placeholderId));
console.error(`[CopilotKit] Failed to upload "${file.name}":`, error);
cfg?.onUploadFailed?.({
reason: "upload-failed",
file,
message: error instanceof Error ? error.message : `Failed to upload "${file.name}"`
});
}
}
}, []);
const handleFileUpload = (0, react.useCallback)(async (e) => {
if (!e.target.files?.length) return;
try {
await processFiles(Array.from(e.target.files));
} catch (error) {
console.error("[CopilotKit] Upload error:", error);
}
}, [processFiles]);
const handleDragOver = (0, react.useCallback)((e) => {
if (!configRef.current?.enabled) return;
e.preventDefault();
e.stopPropagation();
setDragOver(true);
}, []);
const handleDragLeave = (0, react.useCallback)((e) => {
e.preventDefault();
e.stopPropagation();
setDragOver(false);
}, []);
const handleDrop = (0, react.useCallback)(async (e) => {
e.preventDefault();
e.stopPropagation();
setDragOver(false);
if (!configRef.current?.enabled) return;
const files = Array.from(e.dataTransfer.files);
if (files.length > 0) try {
await processFiles(files);
} catch (error) {
console.error("[CopilotKit] Drop error:", error);
}
}, [processFiles]);
(0, react.useEffect)(() => {
if (!enabled) return;
const handlePaste = async (e) => {
const target = e.target;
if (!target || !containerRef.current?.contains(target)) return;
const accept = configRef.current?.accept ?? "*/*";
const fileItems = Array.from(e.clipboardData?.items || []).filter((item) => item.kind === "file" && item.getAsFile() !== null && (0, _copilotkit_shared.matchesAcceptFilter)(item.getAsFile(), accept));
if (fileItems.length === 0) return;
e.preventDefault();
const files = fileItems.map((item) => item.getAsFile()).filter((f) => f !== null);
try {
await processFiles(files);
} catch (error) {
console.error("[CopilotKit] Paste error:", error);
}
};
document.addEventListener("paste", handlePaste);
return () => document.removeEventListener("paste", handlePaste);
}, [enabled, processFiles]);
return {
attachments,
enabled,
dragOver,
fileInputRef,
containerRef,
processFiles,
handleFileUpload,
handleDragOver,
handleDragLeave,
handleDrop,
removeAttachment: (0, react.useCallback)((id) => {
setAttachments((prev) => prev.filter((a) => a.id !== id));
}, []),
consumeAttachments: (0, react.useCallback)(() => {
const ready = attachmentsRef.current.filter((a) => a.status === "ready");
if (ready.length === 0) return ready;
setAttachments((prev) => prev.filter((a) => a.status !== "ready"));
if (fileInputRef.current) fileInputRef.current.value = "";
return ready;
}, [])
};
}
//#endregion
//#region src/v2/hooks/use-learning-containers.tsx
/** The default learning containers value. Matches the backend default. */
const DEFAULT_CONTAINERS = ["project"];
/**
* Legacy compatibility hook that keeps a thread's plural learning-container
* annotation in sync by emitting
* `set_learning_containers` annotations via the CopilotKit runtime annotate
* endpoint (`POST ${runtimeUrl}/annotate`).
*
* **Emit rules:**
* - On mount with `["project"]` (the backend default) → does NOT emit.
* Absence of an annotation equals the default, so the round-trip is skipped.
* - On mount with any other value → emits immediately.
* - On any subsequent content change (including a switch back to
* `["project"]`) → emits (a deliberate switch is always recorded).
* - On unmount or threadId change → emits a reset to `["project"]`
* so the backend is left in a clean state for the next consumer.
* Changing `learningContainers` within the same thread does NOT reset the
* thread; only the new value is emitted.
*
* Content-equality is evaluated via `JSON.stringify` so a fresh array literal
* with the same items does NOT trigger a redundant emit.
*
* If `runtimeUrl` is absent, all emits are silently skipped.
*
* @example
* ```tsx
* function ThreadPane({ threadId, userScope }: Props) {
* useLearningContainers({
* threadId,
* learningContainers: [userScope],
* });
* // ...
* }
* ```
*
* @deprecated Legacy plural-container annotation compatibility only. New
* Intelligence runtimes assign one container with `ɵlearning.containerId` on
* `CopilotRuntime`.
*/
function useLearningContainers({ threadId, learningContainers }) {
const { copilotkit } = (0, _copilotkit_react_core_v2_context.useCopilotKit)();
/**
* Tracks the last-synced container list so content-identical rerenders
* (fresh array, same values) do not fire a redundant emit.
* `null` = nothing synced yet (initial state or after a threadId reset).
*/
const lastSyncedRef = (0, react.useRef)(null);
/** Guards the missing-runtimeUrl warning so it fires at most once per hook instance. */
const warnedMissingUrlRef = (0, react.useRef)(false);
const runtimeUrlRef = (0, react.useRef)(copilotkit.runtimeUrl);
const headersRef = (0, react.useRef)(copilotkit.headers ?? {});
runtimeUrlRef.current = copilotkit.runtimeUrl;
headersRef.current = copilotkit.headers ?? {};
const key = JSON.stringify(learningContainers);
const defaultKey = JSON.stringify(DEFAULT_CONTAINERS);
(0, react.useEffect)(() => {
const runtimeUrl = copilotkit.runtimeUrl;
const headers = copilotkit.headers ?? {};
/**
* Fire-and-forget emit; errors must not surface in render.
* Failures are logged as warnings so they are diagnosable without
* propagating into the React render cycle.
*/
const emit = (containers) => {
if (!runtimeUrl) {
if (!warnedMissingUrlRef.current) {
warnedMissingUrlRef.current = true;
console.warn("useLearningContainers: runtimeUrl not configured; learning-container sync disabled");
}
return;
}
recordAnnotation({
runtimeUrl,
headers,
type: "set_learning_containers",
payload: { containers },
threadId
}).catch((err) => {
console.warn("useLearningContainers: failed to record set_learning_containers", err);
});
};
if (lastSyncedRef.current === null) {
if (key === defaultKey) {
lastSyncedRef.current = learningContainers;
return;
}
emit(learningContainers);
lastSyncedRef.current = learningContainers;
} else if (key !== JSON.stringify(lastSyncedRef.current)) {
emit(learningContainers);
lastSyncedRef.current = learningContainers;
}
}, [threadId, key]);
(0, react.useEffect)(() => {
const capturedThreadId = threadId;
return () => {
const capturedRuntimeUrl = runtimeUrlRef.current;
const capturedHeaders = headersRef.current;
if (capturedRuntimeUrl) recordAnnotation({
runtimeUrl: capturedRuntimeUrl,
headers: capturedHeaders,
type: "set_learning_containers",
payload: { containers: DEFAULT_CONTAINERS },
threadId: capturedThreadId
}).catch((err) => {
console.warn("useLearningContainers: failed to record set_learning_containers", err);
});
lastSyncedRef.current = null;
};
}, [threadId]);
}
//#endregion
//#region src/v2/hooks/use-learning-containers-in-current-thread.tsx
/**
* Legacy compatibility hook that keeps the **current chat thread's** plural
* learning-container annotation in sync. The `threadId` is sourced from the surrounding
* `<CopilotChatConfigurationProvider>` (the same provider `<CopilotChat>`,
* `<CopilotSidebar>`, and friends set up), so callers in a chat-aware
* subtree don't need to thread an id through manually.
*
* **Throws on render** when there is no chat-config provider in scope or
* when the provider does not yet have an active `threadId`. Mount the hook
* inside a subtree that is guaranteed to have a thread context.
*
* If you need to manage an explicit thread, use {@link useLearningContainers}
* directly — two hooks, two crisp contracts, no mode confusion.
*
* @throws When no `CopilotChatConfigurationProvider` is in scope or when the
* active `threadId` is absent/empty.
*
* @example
* ```tsx
* function ThreadPanel({ scope }: Props) {
* useLearningContainersInCurrentThread({
* learningContainers: [scope],
* });
* // ...
* }
* ```
*
* @deprecated Legacy plural-container annotation compatibility only. New
* Intelligence runtimes assign one container with `ɵlearning.containerId` on
* `CopilotRuntime`.
*/
function useLearningContainersInCurrentThread({ learningContainers }) {
const threadId = useCopilotChatConfiguration()?.threadId;
if (!threadId) throw new Error("useLearningContainersInCurrentThread must be used within a thread context (no active threadId). Wrap the component in <CopilotChat>, <CopilotSidebar>, or <CopilotChatConfigurationProvider>, or use `useLearningContainers()` and pass `threadId` explicitly.");
useLearningContainers({
threadId,
learningContainers
});
}
//#endregion
//#region src/v2/components/chat/CopilotChatToolCallsView.tsx
function CopilotChatToolCallsView({ message, messages = [] }) {
const renderToolCall = useRenderToolCall();
if (!message.toolCalls || message.toolCalls.length === 0) return null;
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_jsx_runtime.Fragment, { children: message.toolCalls.map((toolCall) => {
const toolMessage = messages.find((m) => m.role === "tool" && m.toolCallId === toolCall.id);
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react.default.Fragment, { children: renderToolCall({
toolCall,
toolMessage
}) }, toolCall.id);
}) });
}
//#endregion
//#region src/v2/components/chat/CopilotChatAssistantMessage.tsx
function CopilotChatAssistantMessage({ message, messages, isRunning, onThumbsUp, onThumbsDown, onReadAloud, onRegenerate, additionalToolbarItems, toolbarVisible = true, markdownRenderer, toolbar, copyButton, inspectorButton, thumbsUpButton, thumbsDownButton, readAloudButton, regenerateButton, toolCallsView, children, className, ...props }) {
useKatexStyles();
const { isLocalInspectorEnabled, openInspector } = useCopilotKitInspector();
const chatConfiguration = useCopilotChatConfiguration();
const boundMarkdownRenderer = renderSlot(markdownRenderer, CopilotChatAssistantMessage.MarkdownRenderer, { content: message.content || "" });
const boundCopyButton = renderSlot(copyButton, CopilotChatAssistantMessage.CopyButton, { onClick: async () => {
if (message.content) return await (0, _copilotkit_shared.copyToClipboard)(message.content);
return false;
} });
const boundThumbsUpButton = renderSlot(thumbsUpButton, CopilotChatAssistantMessage.ThumbsUpButton, { onClick: onThumbsUp ? () => onThumbsUp(message) : void 0 });
const boundInspectorButton = renderSlot(inspectorButton, CopilotChatAssistantMessage.InspectorButton, { onClick: () => openInspector({
messageId: message.id,
threadId: chatConfiguration?.threadId,
agentId: chatConfiguration?.agentId
}) });
const boundThumbsDownButton = renderSlot(thumbsDownButton, CopilotChatAssistantMessage.ThumbsDownButton, { onClick: onThumbsDown ? () => onThumbsDown(message) : void 0 });
const boundReadAloudButton = renderSlot(readAloudButton, CopilotChatAssistantMessage.ReadAloudButton, { onClick: onReadAloud ? () => onReadAloud(message) : void 0 });
const boundRegenerateButton = renderSlot(regenerateButton, CopilotChatAssistantMessage.RegenerateButton, { onClick: onRegenerate ? () => onRegenerate(message) : void 0 });
const boundToolbar = renderSlot(toolbar, CopilotChatAssistantMessage.Toolbar, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
className: "cpk:flex cpk:items-center cpk:gap-1",
children: [
boundCopyButton,
isLocalInspectorEnabled && boundInspectorButton,
(onThumbsUp || thumbsUpButton) && boundThumbsUpButton,
(onThumbsDown || thumbsDownButton) && boundThumbsDownButton,
(onReadAloud || readAloudButton) && boundReadAloudButton,
(onRegenerate || regenerateButton) && boundRegenerateButton,
additionalToolbarItems
]
}) });
const boundToolCallsView = renderSlot(toolCallsView, CopilotChatToolCallsView, {
message,
messages
});
const hasContent = !!(message.content && message.content.trim().length > 0);
const isLatestAssistantMessage = message.role === "assistant" && messages?.[messages.length - 1]?.id === message.id;
const shouldShowToolbar = toolbarVisible && hasContent && !(isRunning && isLatestAssistantMessage);
if (children) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
"data-copilotkit": true,
style: { display: "contents" },
children: children({
markdownRenderer: boundMarkdownRenderer,
toolbar: boundToolbar,
toolCallsView: boundToolCallsView,
copyButton: boundCopyButton,
inspectorButton: boundInspectorButton,
thumbsUpButton: boundThumbsUpButton,
thumbsDownButton: boundThumbsDownButton,
readAloudButton: boundReadAloudButton,
regenerateButton: boundRegenerateButton,
message,
messages,
isRunning,
onThumbsUp,
onThumbsDown,
onReadAloud,
onRegenerate,
additionalToolbarItems,
toolbarVisible: shouldShowToolbar
})
});
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
"data-copilotkit": true,
"data-testid": "copilot-assistant-message",
className: (0, tailwind_merge.twMerge)("copilotKitMessage copilotKitAssistantMessage", className),
...props,
"data-message-id": message.id,
children: [
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
className: "cpk:prose cpk:max-w-full cpk:break-words cpk:dark:prose-invert",
children: boundMarkdownRenderer
}),
boundToolCallsView,
shouldShowToolbar && boundToolbar
]
});
}
function CopilotKitColoredIcon() {
const gradientId = (0, react.useId)().replace(/:/g, "");
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("svg", {
"aria-hidden": "true",
className: "cpk:size-5",
"data-testid": "copilot-inspector-icon",
viewBox: "0 0 24 24",
children: [
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
d: "M8.162 7.758c2.093-2.738 3.831-5.445 4.498-7.63a.093.093 0 01.14-.051c2.324 1.539 6.558 2.552 10.301 2.576a.09.09 0 01.085.124c-1.243 3.158-2.765 8.817-2.823 15.28-.001.095-.135.13-.183.046-2.131-3.729-8.955-8.968-11.982-10.205a.09.09 0 01-.036-.14z",
fill: `url(#${gradientId}-purple)`
}),
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
d: "M15.223 6.083A61.492 61.492 0 018.25 7.827c-.045.008-.055.071-.012.089 3.05 1.267 9.84 6.492 11.952 10.206a.017.017 0 00.022.007.018.018 0 00.01-.024l-4.999-12.02z",
fill: `url(#${gradientId}-blue)`
}),
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
d: "M12.81.07c2.8 1.528 6.037 2.214 10.33 2.575.028.002.036.039.012.051-.55.282-3.695 1.883-6.03 2.74-.626.23-1.256.443-1.876.64a.028.028 0 01-.033-.016L12.746.128c-.017-.04.027-.078.065-.058z",
fill: `url(#${gradientId}-light-blue)`
}),
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
className: "cpk:fill-[#513C9F] cpk:dark:fill-[#B99AE8]",
d: "M12.725.075c.046-.019.1.003.119.05l7.514 17.923a.091.091 0 01-.148.1l-.02-.03L12.675.195a.091.091 0 01.049-.12z"
}),
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
className: "cpk:fill-[#513C9F] cpk:dark:fill-[#B99AE8]",
d: "M23.06 2.66c.044-.025.1-.01.125.034.025.044.009.1-.035.124v.001l-.008.004-.025.015-.1.054a41.384 41.384 0 01-1.811.92A47.05 47.05 0 0116.33 5.82c-1.954.674-3.97 1.197-5.497 1.552a66.27 66.27 0 01-2.38.507l-.138.026-.036.007h-.01l-.002.002a.091.091 0 11-.033-.18l.016.09-.015-.09h.002l.01-.002.035-.007.137-.025a66.16 66.16 0 002.373-.506c1.524-.354 3.533-.876 5.479-1.547a46.857 46.857 0 006.276-2.709c.166-.087.295-.156.381-.204l.099-.054.024-.014.008-.004z"
}),
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
className: "cpk:fill-[#ABABAB] cpk:dark:fill-[#D4D4D4]",
d: "M13.838 2.272a.16.16 0 01.107.2l-2.72 9.055h6.4l.061.013a.16.16 0 010 .295l-.061.013h-6.541L.679 24.099l-.05.04a.16.16 0 01-.194-.245l10.43-12.285 2.773-9.23a.16.16 0 01.2-.107z"
}),
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
d: "M7.809 21.461l-1.232.173c.638 1.69 1.949 2.427 3.514 2.427 3.831 0 2.661-4.334 4.883-4.334 1.61 0 .956 3.513 4.423 3.513 2.116 0 2.326-2.131 1.966-3.048l-.008-.016-.567-.868c-.037-.058-.127-.036-.133.032l-.106 1.053a1.01 1.01 0 00.003.219c.088.727.144 2.491-1.155 2.491-1.37 0-1.7-3.467-4.423-3.467-3.196 0-2.785 4.289-4.747 4.289-1.294 0-2.28-1.46-2.418-2.464z",
fill: `url(#${gradientId}-tail)`
}),
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("defs", { children: [
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("linearGradient", {
gradientUnits: "userSpaceOnUse",
id: `${gradientId}-purple`,
x1: "17.852",
x2: "14.202",
y1: "1.467",
y2: "11.504",
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("stop", { className: "cpk:[stop-color:#6430AB] cpk:dark:[stop-color:#B792F0]" }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("stop", {
className: "cpk:[stop-color:#AA89D8] cpk:dark:[stop-color:#D3BDF7]",
offset: "1"
})]
}),
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("linearGradient", {
gradientUnits: "userSpaceOnUse",
id: `${gradientId}-blue`,
x1: "15.024",
x2: "10.324",
y1: "7.125",
y2: "16.204",
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("stop", { className: "cpk:[stop-color:#005DBB] cpk:dark:[stop-color:#4D9FEF]" }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("stop", {
className: "cpk:[stop-color:#3D92E8] cpk:dark:[stop-color:#84C0FA]",
offset: "1"
})]
}),
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("linearGradient", {
gradientUnits: "userSpaceOnUse",
id: `${gradientId}-light-blue`,
x1: "17.122",
x2: "15.707",
y1: "1.467",
y2: "5.892",
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("stop", { className: "cpk:[stop-color:#1B70C4] cpk:dark:[stop-color:#61ACF2]" }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("stop", {
className: "cpk:[stop-color:#54A4F2] cpk:dark:[stop-color:#9ACDFF]",
offset: "1"
})]
}),
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("linearGradient", {
gradientUnits: "userSpaceOnUse",
id: `${gradientId}-tail`,
x1: "6.577",
x2: "21.506",
y1: "21.758",
y2: "21.758",
children: [
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("stop", { className: "cpk:[stop-color:#4497EA] cpk:dark:[stop-color:#79BCF5]" }),
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("stop", {
className: "cpk:[stop-color:#1463B2] cpk:dark:[stop-color:#4594D8]",
offset: ".255"
}),
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("stop", {
className: "cpk:[stop-color:#0A437D] cpk:dark:[stop-color:#347CB7]",
offset: ".499"
}),
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("stop", {
className: "cpk:[stop-color:#2476C8] cpk:dark:[stop-color:#58A4E5]",
offset: ".667"
}),
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("stop", {
className: "cpk:[stop-color:#0C549A] cpk:dark:[stop-color:#3C87C7]",
offset: ".973"
})
]
})
] })
]
});
}
(function(_CopilotChatAssistantMessage) {
_CopilotChatAssistantMessage.MarkdownRenderer = ({ content, className, ...props }) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(streamdown.Streamdown, {
className,
...props,
children: content ?? ""
});
_CopilotChatAssistantMessage.Toolbar = ({ className, ...props }) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
"data-testid": "copilot-assistant-toolbar",
className: (0, tailwind_merge.twMerge)("cpk:w-full cpk:bg-transparent cpk:flex cpk:items-center cpk:-ml-[5px] cpk:-mt-[0px]", className),
...props
});
const ToolbarButton = _CopilotChatAssistantMessage.ToolbarButton = ({ title, tooltip, children, ...props }) => {
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(Tooltip, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(TooltipTrigger, {
asChild: true,
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Button, {
type: "button",
variant: "assistantMessageToolbarButton",
"aria-label": title,
...props,
children
})
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(TooltipContent, {
side: "bottom",
children: tooltip ?? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: title })
})] });
};
_CopilotChatAssistantMessage.CopyButton = ({ className, title, onClick, ...props }) => {
const labels = useCopilotChatConfiguration()?.labels ?? CopilotChatDefaultLabels;
const [copied, setCopied] = (0, react.useState)(false);
const timerRef = (0, react.useRef)(null);
(0, react.useEffect)(() => {
return () => {
if (timerRef.current !== null) clearTimeout(timerRef.current);
};
}, []);
const handleClick = async (event) => {
let success = false;
if (onClick) success = await Promise.resolve(onClick(event)) === true;
if (success) {
setCopied(true);
if (timerRef.current !== null) clearTimeout(timerRef.current);
timerRef.current = setTimeout(() => {
timerRef.current = null;
setCopied(false);
}, 2e3);
}
};
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ToolbarButton, {
"data-testid": "copilot-copy-button",
title: title || labels.assistantMessageToolbarCopyMessageLabel,
onClick: handleClick,
className,
...props,
children: copied ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.Check, { className: "cpk:size-[18px]" }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.Copy, { className: "cpk:size-[18px]" })
});
};
_CopilotChatAssistantMessage.InspectorButton = ({ title, ...props }) => {
const labels = useCopilotChatConfiguration()?.labels ?? CopilotChatDefaultLabels;
const primaryLabel = title || labels.assistantMessageToolbarInspectorLabel;
const localOnlyLabel = labels.assistantMessageToolbarInspectorLocalOnlyLabel;
const accessibleLabel = `${primaryLabel} (${localOnlyLabel})`;
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ToolbarButton, {
"data-testid": "copilot-inspector-button",
title: accessibleLabel,
tooltip: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
className: "cpk:flex cpk:flex-col cpk:gap-0.5",
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: primaryLabel }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
className: "cpk:text-[10px] cpk:opacity-65",
children: localOnlyLabel
})]
}),
...props,
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CopilotKitColoredIcon, {})
});
};
_CopilotChatAssistantMessage.ThumbsUpButton = ({ title, ...props }) => {
const labels = useCopilotChatConfiguration()?.labels ?? CopilotChatDefaultLabels;
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ToolbarButton, {
"data-testid": "copilot-thumbs-up-button",
title: title || labels.assistantMessageToolbarThumbsUpLabel,
...props,
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.ThumbsUp, { className: "cpk:size-[18px]" })
});
};
_CopilotChatAssistantMessage.ThumbsDownButton = ({ title, ...props }) => {
const labels = useCopilotChatConfiguration()?.labels ?? CopilotChatDefaultLabels;
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ToolbarButton, {
"data-testid": "copilot-thumbs-down-button",
title: title || labels.assistantMessageToolbarThumbsDownLabel,
...props,
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.ThumbsDown, { className: "cpk:size-[18px]" })
});
};
_CopilotChatAssistantMessage.ReadAloudButton = ({ title, ...props }) => {
const labels = useCopilotChatConfiguration()?.labels ?? CopilotChatDefaultLabels;
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ToolbarButton, {
"data-testid": "copilot-read-aloud-button",
title: title || labels.assistantMessageToolbarReadAloudLabel,
...props,
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.Volume2, { className: "cpk:size-[20px]" })
});
};
_CopilotChatAssistantMessage.RegenerateButton = ({ title, ...props }) => {
const labels = useCopilotChatConfiguration()?.labels ?? CopilotChatDefaultLabels;
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ToolbarButton, {
"data-testid": "copilot-regenerate-button",
title: title || labels.assistantMessageToolbarRegenerateLabel,
...props,
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.RefreshCw, { className: "cpk:size-[18px]" })
});
};
})(CopilotChatAssistantMessage || (CopilotChatAssistantMessage = {}));
CopilotChatAssistantMessage.MarkdownRenderer.displayName = "CopilotChatAssistantMessage.MarkdownRenderer";
CopilotChatAssistantMessage.Toolbar.displayName = "CopilotChatAssistantMessage.Toolbar";
CopilotChatAssistantMessage.CopyButton.displayName = "CopilotChatAssistantMessage.CopyButton";
CopilotChatAssistantMessage.InspectorButton.displayName = "CopilotChatAssistantMessage.InspectorButton";
CopilotChatAssistantMessage.ThumbsUpButton.displayName = "CopilotChatAssistantMessage.ThumbsUpButton";
CopilotChatAssistantMessage.ThumbsDownButton.displayName = "CopilotChatAssistantMessage.ThumbsDownButton";
CopilotChatAssistantMessage.ReadAloudButton.displayName = "CopilotChatAssistantMessage.ReadAloudButton";
CopilotChatAssistantMessage.RegenerateButton.displayName = "CopilotChatAssistantMessage.RegenerateButton";
var CopilotChatAssistantMessage_default = CopilotChatAssistantMessage;
//#endregion
//#region src/v2/components/chat/Lightbox.tsx
function Lightbox({ onClose, children }) {
(0, react.useEffect)(() => {
const handleKey = (e) => {
if (e.key === "Escape") onClose();
};
document.addEventListener("keydown", handleKey);
return () => document.removeEventListener("keydown", handleKey);
}, [onClose]);
if (typeof document === "undefined") return null;
return (0, react_dom.createPortal)(/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
className: "cpk:fixed cpk:inset-0 cpk:z-[9999] cpk:flex cpk:items-center cpk:justify-center cpk:bg-black/80 cpk:animate-fade-in",
onClick: onClose,
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
onClick: onClose,
className: "cpk:absolute cpk:top-4 cpk:right-4 cpk:text-white cpk:bg-white/10 cpk:hover:bg-white/20 cpk:rounded-full cpk:w-10 cpk:h-10 cpk:flex cpk:items-center cpk:justify-center cpk:cursor-pointer cpk:border-none cpk:z-10",
"aria-label": "Close preview",
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.X, { className: "cpk:w-5 cpk:h-5" })
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
onClick: (e) => e.stopPropagation(),
children
})]
}), document.body);
}
/**
* Hook that manages lightbox open/close and uses the View Transition API to
* morph the thumbnail into fullscreen content.
*
* The trick: `view-transition-name` must live on exactly ONE element at a time.
* - Old state (thumbnail visible): name is on the thumbnail.
* - New state (lightbox visible): name moves to the lightbox content.
* `flushSync` ensures React commits the DOM change synchronously inside the
* `startViewTransition` callback so the API can snapshot old → new correctly.
*/
function useLightbox() {
const thumbnailRef = (0, react.useRef)(null);
const [open, setOpen] = (0, react.useState)(false);
const vtName = (0, react.useId)();
return {
thumbnailRef,
vtName,
open,
openLightbox: (0, react.useCallback)(() => {
const thumb = thumbnailRef.current;
const doc = document;
if (doc.startViewTransition && thumb) {
thumb.style.viewTransitionName = vtName;
doc.startViewTransition(() => {
thumb.style.viewTransitionName = "";
(0, react_dom.flushSync)(() => setOpen(true));
});
} else setOpen(true);
}, [vtName]),
closeLightbox: (0, react.useCallback)(() => {
const thumb = thumbnailRef.current;
const doc = document;
if (doc.startViewTransition && thumb) doc.startViewTransition(() => {
(0, react_dom.flushSync)(() => setOpen(false));
thumb.style.viewTransitionName = vtName;
}).finished.then(() => {
thumb.style.viewTransitionName = "";
}).catch(() => {
thumb.style.viewTransitionName = "";
});
else setOpen(false);
}, [vtName])
};
}
//#endregion
//#region src/v2/components/chat/CopilotChatAttachmentRenderer.tsx
const ImageAttachment = (0, react.memo)(function ImageAttachment({ src, className }) {
const [error, setError] = (0, react.useState)(false);
const { thumbnailRef, vtName, open, openLightbox, closeLightbox } = useLightbox();
if (error) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
className: cn("cpk:flex cpk:flex-col cpk:items-center cpk:justify-center cpk:rounded-lg cpk:bg-muted cpk:p-4 cpk:text-sm cpk:text-muted-foreground", className),
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: "Failed to load image" })
});
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("img", {
ref: thumbnailRef,
src,
alt: "Image attachment",
className: cn("cpk:max-w-[80px] cpk:max-h-[80px] cpk:w-auto cpk:h-auto cpk:rounded-xl cpk:object-cover cpk:cursor-pointer cpk:bg-muted", className),
onClick: openLightbox,
onError: () => setError(true)
}), open && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Lightbox, {
onClose: closeLightbox,
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("img", {
style: { viewTransitionName: vtName },
src,
alt: "Image attachment",
className: "cpk:max-w-[90vw] cpk:max-h-[90vh] cpk:object-contain cpk:rounded-lg"
})
})] });
});
const AudioAttachment = (0, react.memo)(function AudioAttachment({ src, filename, className }) {
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
className: cn("cpk:flex cpk:flex-col cpk:gap-1", className),
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("audio", {
src,
controls: true,
preload: "metadata",
className: "cpk:max-w-[300px] cpk:w-full cpk:h-10"
}), filename && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
className: "cpk:text-xs cpk:text-muted-foreground cpk:truncate cpk:max-w-[300px]",
children: filename
})]
});
});
const VideoAttachment = (0, react.memo)(function VideoAttachment({ src, className }) {
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("video", {
src,
controls: true,
preload: "metadata",
className: cn("cpk:max-w-[400px] cpk:w-full cpk:rounded-lg", className)
});
});
const DocumentAttachment = (0, react.memo)(function DocumentAttachment({ source, filename, className }) {
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
className: cn("cpk:inline-flex cpk:items-center cpk:gap-2 cpk:px-3 cpk:py-2 cpk:border cpk:border-border cpk:rounded-lg cpk:bg-muted", className),
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
className: "cpk:text-xs cpk:font-bold cpk:uppercase",
children: (0, _copilotkit_shared.getDocumentIcon)(source.mimeType ?? "")
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
className: "cpk:text-sm cpk:text-muted-foreground cpk:truncate",
children: filename || source.mimeType || "Unknown type"
})]
});
});
const CopilotChatAttachmentRenderer = ({ type, source, filename, className }) => {
const src = (0, _copilotkit_shared.getSourceUrl)(source);
switch (type) {
case "image": return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ImageAttachment, {
src,
className
});
case "audio": return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(AudioAttachment, {
src,
filename,
className
});
case "video": return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(VideoAttachment, {
src,
className
});
case "document": return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DocumentAttachment, {
source,
filename,
className
});
}
};
//#endregion
//#region src/v2/components/chat/CopilotChatUserMessage.tsx
function flattenUserMessageContent(content) {
if (!content) return "";
if (typeof content === "string") return content;
return content.map((part) => {
if (part && typeof part === "object" && "type" in part && part.type === "text" && typeof part.text === "string") return part.text;
return "";
}).filter((text) => text.length > 0).join("\n");
}
function getMediaParts(content) {
if (!content || typeof content === "string") return [];
return content.filter((part) => part.type === "image" || part.type === "audio" || part.type === "video" || part.type === "document");
}
function getFilename(part) {
const meta = part.metadata;
if (meta != null && typeof meta === "object" && "filename" in meta && typeof meta.filename === "string") return meta.filename;
}
function CopilotChatUserMessage({ message, onEditMessage, branchIndex, numberOfBranches, onSwitchToBranch, additionalToolbarItems, messageRenderer, toolbar, copyButton, editButton, branchNavigation, children, className, ...props }) {
const flattenedContent = (0, react.useMemo)(() => flattenUserMessageContent(message.content), [message.content]);
const mediaParts = (0, react.useMemo)(() => getMediaParts(message.content), [message.content]);
const BoundMessageRenderer = renderSlot(messageRenderer, CopilotChatUserMessage.MessageRenderer, { content: flattenedContent });
const BoundCopyButton = renderSlot(copyButton, CopilotChatUserMessage.CopyButton, { onClick: async () => {
if (flattenedContent) return await (0, _copilotkit_shared.copyToClipboard)(flattenedContent);
return false;
} });
const BoundEditButton = renderSlot(editButton, CopilotChatUserMessage.EditButton, { onClick: () => onEditMessage?.({ message }) });
const BoundBranchNavigation = renderSlot(branchNavigation, CopilotChatUserMessage.BranchNavigation, {
currentBranch: branchIndex,
numberOfBranches,
onSwitchToBranch,
message
});
const showBranchNavigation = numberOfBranches && numberOfBranches > 1 && onSwitchToBranch;
const BoundToolbar = renderSlot(toolbar, CopilotChatUserMessage.Toolbar, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
className: "cpk:flex cpk:items-center cpk:gap-1 cpk:justify-end",
children: [
additionalToolbarItems,
BoundCopyButton,
onEditMessage && BoundEditButton,
showBranchNavigation && BoundBranchNavigation
]
}) });
if (children) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
"data-copilotkit": true,
style: { display: "contents" },
children: children({
messageRenderer: BoundMessageRenderer,
toolbar: BoundToolbar,
copyButton: BoundCopyButton,
editButton: BoundEditButton,
branchNavigation: BoundBranchNavigation,
message,
branchIndex,
numberOfBranches,
additionalToolbarItems
})
});
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
"data-copilotkit": true,
"data-testid": "copilot-user-message",
className: (0, tailwind_merge.twMerge)("copilotKitMessage copilotKitUserMessage cpk:flex cpk:flex-col cpk:items-end cpk:group cpk:pt-10", className),
"data-message-id": message.id,
...props,
children: [
mediaParts.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
className: "cpk:flex cpk:flex-row cpk:flex-wrap cpk:justify-end cpk:gap-2 cpk:mb-2",
children: mediaParts.map((part, index) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CopilotChatAttachmentRenderer, {
type: part.type,
source: part.source,
filename: getFilename(part)
}, index))
}),
BoundMessageRenderer,
BoundToolbar
]
});
}
(function(_CopilotChatUserMessage) {
_CopilotChatUserMessage.Container = ({ children, className, ...props }) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
className: (0, tailwind_merge.twMerge)("cpk:flex cpk:flex-col cpk:items-end cpk:group", className),
...props,
children
});
_CopilotChatUserMessage.MessageRenderer = ({ content, className }) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
className: (0, tailwind_merge.twMerge)("cpk:prose cpk:dark:prose-invert cpk:bg-muted cpk:relative cpk:max-w-[80%] cpk:rounded-[18px] cpk:px-4 cpk:py-1.5 cpk:data-[multiline]:py-3 cpk:inline-block cpk:whitespace-pre-wrap", className),
children: content
});
_CopilotChatUserMessage.Toolbar = ({ className, ...props }) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
"data-testid": "copilot-user-toolbar",
className: (0, tailwind_merge.twMerge)("cpk:w-full cpk:bg-transparent cpk:flex cpk:items-center cpk:justify-end cpk:-mr-[5px] cpk:mt-[4px] cpk:invisible cpk:group-hover:visible", className),
...props
});
const ToolbarButton = _CopilotChatUserMessage.ToolbarButton = ({ title, children, className, ...props }) => {
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(Tooltip, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(TooltipTrigger, {
asChild: true,
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Button, {
type: "button",
variant: "assistantMessageToolbarButton",
"aria-label": title,
className: (0, tailwind_merge.twMerge)(className),
...props,
children
})
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(TooltipContent, {
side: "bottom",
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: title })
})] });
};
_CopilotChatUserMessage.CopyButton = ({ className, title, onClick, ...props }) => {
const labels = useCopilotChatConfiguration()?.labels ?? CopilotChatDefaultLabels;
const [copied, setCopied] = (0, react.useState)(false);
const handleClick = async (event) => {
let success = false;
if (onClick) success = await Promise.resolve(onClick(event)) === true;
if (success) {
setCopied(true);
setTimeout(() => setCopied(false), 2e3);
}
};
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ToolbarButton, {
"data-testid": "copilot-user-copy-button",
title: title || labels.userMessageToolbarCopyMessageLabel,
onClick: handleClick,
className,
...props,
children: copied ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.Check, { className: "cpk:size-[18px]" }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.Copy, { className: "cpk:size-[18px]" })
});
};
_CopilotChatUserMessage.EditButton = ({ className, title, ...props }) => {
const labels = useCopilotChatConfiguration()?.labels ?? CopilotChatDefaultLabels;
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ToolbarButton, {
"data-testid": "copilot-edit-button",
title: title || labels.userMessageToolbarEditMessageLabel,
className,
...props,
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.Edit, { className: "cpk:size-[18px]" })
});
};
_CopilotChatUserMessage.BranchNavigation = ({ className, currentBranch = 0, numberOfBranches = 1, onSwitchToBranch, message, ...props }) => {
if (!numberOfBranches || numberOfBranches <= 1 || !onSwitchToBranch) return null;
const canGoPrev = currentBranch > 0;
const canGoNext = currentBranch < numberOfBranches - 1;
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
"data-testid": "copilot-branch-navigation",
className: (0, tailwind_merge.twMerge)("cpk:flex cpk:items-center cpk:gap-1", className),
...props,
children: [
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Button, {
type: "button",
variant: "assistantMessageToolbarButton",
onClick: () => onSwitchToBranch?.({
branchIndex: currentBranch - 1,
numberOfBranches,
message
}),
disabled: !canGoPrev,
className: "cpk:h-6 cpk:w-6 cpk:p-0",
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.ChevronLeft, { className: "cpk:size-[20px]" })
}),
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
className: "cpk:text-sm cpk:text-muted-foreground cpk:px-0 cpk:font-medium",
children: [
currentBranch + 1,
"/",
numberOfBranches
]
}),
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Button, {
type: "button",
variant: "assistantMessageToolbarButton",
onClick: () => onSwitchToBranch?.({
branchIndex: currentBranch + 1,
numberOfBranches,
message
}),
disabled: !canGoNext,
className: "cpk:h-6 cpk:w-6 cpk:p-0",
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.ChevronRight, { className: "cpk:size-[20px]" })
})
]
});
};
})(CopilotChatUserMessage || (CopilotChatUserMessage = {}));
CopilotChatUserMessage.Container.displayName = "CopilotChatUserMessage.Container";
CopilotChatUserMessage.MessageRenderer.displayName = "CopilotChatUserMessage.MessageRenderer";
CopilotChatUserMessage.Toolbar.displayName = "CopilotChatUserMessage.Toolbar";
CopilotChatUserMessage.ToolbarButton.displayName = "CopilotChatUserMessage.ToolbarButton";
CopilotChatUserMessage.CopyButton.displayName = "CopilotChatUserMessage.CopyButton";
CopilotChatUserMessage.EditButton.displayName = "CopilotChatUserMessage.EditButton";
CopilotChatUserMessage.BranchNavigation.displayName = "CopilotChatUserMessage.BranchNavigation";
var CopilotChatUserMessage_default = CopilotChatUserMessage;
//#endregion
//#region src/v2/components/chat/CopilotChatReasoningMessage.tsx
/**
* Formats an elapsed duration (in seconds) to a human-readable string.
*/
function formatDuration(seconds) {
if (seconds < 1) return "a few seconds";
if (seconds < 60) return `${Math.round(seconds)} seconds`;
const mins = Math.floor(seconds / 60);
const secs = Math.round(seconds % 60);
if (secs === 0) return `${mins} minute${mins > 1 ? "s" : ""}`;
return `${mins}m ${secs}s`;
}
function CopilotChatReasoningMessage({ message, messages, isRunning, header, contentView, toggle, children, className, ...props }) {
const isLatest = messages?.[messages.length - 1]?.id === message.id;
const isStreaming = !!(isRunning && isLatest);
const hasContent = !!(message.content && message.content.length > 0);
const startTimeRef = (0, react.useRef)(null);
const [elapsed, setElapsed] = (0, react.useState)(0);
(0, react.useEffect)(() => {
if (isStreaming && startTimeRef.current === null) startTimeRef.current = Date.now();
if (!isStreaming && startTimeRef.current !== null) {
setElapsed((Date.now() - startTimeRef.current) / 1e3);
return;
}
if (!isStreaming) return;
const timer = setInterval(() => {
if (startTimeRef.current !== null) setElapsed((Date.now() - startTimeRef.current) / 1e3);
}, 1e3);
return () => clearInterval(timer);
}, [isStreaming]);
const [isOpen, setIsOpen] = (0, react.useState)(isStreaming);
const userToggledRef = (0, react.useRef)(false);
(0, react.useEffect)(() => {
if (isStreaming) {
userToggledRef.current = false;
setIsOpen(true);
} else if (!userToggledRef.current) setIsOpen(false);
}, [isStreaming]);
const handleToggle = hasContent ? () => {
userToggledRef.current = true;
setIsOpen((prev) => !prev);
} : void 0;
const label = isStreaming ? "Thinking…" : `Thought for ${formatDuration(elapsed)}`;
const boundHeader = renderSlot(header, CopilotChatReasoningMessage.Header, {
isOpen,
label,
hasContent,
isStreaming,
onClick: handleToggle
});
const boundContent = renderSlot(contentView, CopilotChatReasoningMessage.Content, {
isStreaming,
hasContent,
children: message.content
});
const boundToggle = renderSlot(toggle, CopilotChatReasoningMessage.Toggle, {
isOpen,
children: boundContent
});
if (children) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
"data-copilotkit": true,
style: { display: "contents" },
children: children({
header: boundHeader,
contentView: boundContent,
toggle: boundToggle,
message,
messages,
isRunning
})
});
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
className: (0, tailwind_merge.twMerge)("cpk:my-1", className),
"data-message-id": message.id,
...props,
children: [boundHeader, boundToggle]
});
}
(function(_CopilotChatReasoningMessage) {
_CopilotChatReasoningMessage.Header = ({ isOpen, label = "Thoughts", hasContent, isStreaming, className, children: headerChildren, ...headerProps }) => {
const isExpandable = !!hasContent;
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
type: "button",
className: (0, tailwind_merge.twMerge)("cpk:inline-flex cpk:items-center cpk:gap-1 cpk:py-1 cpk:text-sm cpk:text-muted-foreground cpk:transition-colors cpk:select-none", isExpandable ? "cpk:hover:text-foreground cpk:cursor-pointer" : "cpk:cursor-default", className),
"aria-expanded": isExpandable ? isOpen : void 0,
...headerProps,
children: [
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
className: "cpk:font-medium",
children: label
}),
isStreaming && !hasContent && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
className: "cpk:inline-flex cpk:items-center cpk:ml-1",
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: "cpk:w-1.5 cpk:h-1.5 cpk:rounded-full cpk:bg-muted-foreground cpk:animate-pulse" })
}),
headerChildren,
isExpandable && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.ChevronRight, { className: (0, tailwind_merge.twMerge)("cpk:size-3.5 cpk:shrink-0 cpk:transition-transform cpk:duration-200", isOpen && "cpk:rotate-90") })
]
});
};
_CopilotChatReasoningMessage.Content = ({ isStreaming, hasContent, className, children: contentChildren, ...contentProps }) => {
if (!hasContent && !isStreaming) return null;
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
className: (0, tailwind_merge.twMerge)("cpk:pb-2 cpk:pt-1", className),
...contentProps,
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
className: "cpk:text-sm cpk:text-muted-foreground",
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(streamdown.Streamdown, { children: typeof contentChildren === "string" ? contentChildren : "" }), isStreaming && hasContent && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
className: "cpk:inline-flex cpk:items-center cpk:ml-1 cpk:align-middle",
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: "cpk:w-2 cpk:h-2 cpk:rounded-full cpk:bg-muted-foreground cpk:animate-pulse-cursor" })
})]
})
});
};
_CopilotChatReasoningMessage.Toggle = ({ isOpen, className, children: toggleChildren, ...toggleProps }) => {
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
className: (0, tailwind_merge.twMerge)("cpk:grid cpk:transition-[grid-template-rows] cpk:duration-200 cpk:ease-in-out", className),
style: { gridTemplateRows: isOpen ? "1fr" : "0fr" },
...toggleProps,
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
className: "cpk:overflow-hidden",
children: toggleChildren
})
});
};
})(CopilotChatReasoningMessage || (CopilotChatReasoningMessage = {}));
CopilotChatReasoningMessage.Header.displayName = "CopilotChatReasoningMessage.Header";
CopilotChatReasoningMessage.Content.displayName = "CopilotChatReasoningMessage.Content";
CopilotChatReasoningMessage.Toggle.displayName = "CopilotChatReasoningMessage.Toggle";
var CopilotChatReasoningMessage_default = CopilotChatReasoningMessage;
//#endregion
//#region src/v2/components/chat/CopilotChatSuggestionPill.tsx
const baseClasses = "group cpk:inline-flex cpk:h-7 cpk:sm:h-8 cpk:items-center cpk:gap-1 cpk:sm:gap-1.5 cpk:rounded-full cpk:border cpk:border-border/60 cpk:bg-background cpk:px-2.5 cpk:sm:px-3 cpk:text-[11px] cpk:sm:text-xs cpk:leading-none cpk:text-foreground cpk:transition-colors cpk:cursor-pointer cpk:hover:bg-accent/60 cpk:hover:text-foreground cpk:focus-visible:outline-none cpk:focus-visible:ring-2 cpk:focus-visible:ring-ring cpk:focus-visible:ring-offset-2 cpk:focus-visible:ring-offset-background cpk:disabled:cursor-not-allowed cpk:disabled:text-muted-foreground cpk:disabled:hover:bg-background cpk:disabled:hover:text-muted-foreground cpk:pointer-events-auto";
const labelClasses = "cpk:whitespace-nowrap cpk:font-medium cpk:leading-none";
const CopilotChatSuggestionPill = react.default.forwardRef(function CopilotChatSuggestionPill({ className, children, icon, isLoading, type, ...props }, ref) {
const showIcon = !isLoading && icon;
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
ref,
"data-copilotkit": true,
"data-testid": "copilot-suggestion",
"data-slot": "suggestion-pill",
className: cn(baseClasses, className),
type: type ?? "button",
"aria-busy": isLoading || void 0,
disabled: isLoading || props.disabled,
...props,
children: [isLoading ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
className: "cpk:flex cpk:h-3.5 cpk:sm:h-4 cpk:w-3.5 cpk:sm:w-4 cpk:items-center cpk:justify-center cpk:text-muted-foreground",
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.Loader2, {
className: "cpk:h-3.5 cpk:sm:h-4 cpk:w-3.5 cpk:sm:w-4 cpk:animate-spin",
"aria-hidden": "true"
})
}) : showIcon && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
className: "cpk:flex cpk:h-3.5 cpk:sm:h-4 cpk:w-3.5 cpk:sm:w-4 cpk:items-center cpk:justify-center cpk:text-muted-foreground",
children: icon
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
className: labelClasses,
children
})]
});
});
CopilotChatSuggestionPill.displayName = "CopilotChatSuggestionPill";
//#endregion
//#region src/v2/components/chat/CopilotChatSuggestionView.tsx
const DefaultContainer = react.default.forwardRef(function DefaultContainer({ className, ...props }, ref) {
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
ref,
"data-copilotkit": true,
"data-testid": "copilot-suggestions",
className: cn("cpk:flex cpk:flex-wrap cpk:items-center cpk:gap-1.5 cpk:sm:gap-2 cpk:pl-0 cpk:pr-4 cpk:@3xl:px-0 cpk:pointer-events-none", className),
...props
});
});
const CopilotChatSuggestionView = react.default.forwardRef(function CopilotChatSuggestionView({ suggestions, onSelectSuggestion, loadingIndexes, container, suggestion: suggestionSlot, className, children, ...restProps }, ref) {
const loadingSet = react.default.useMemo(() => {
if (!loadingIndexes || loadingIndexes.length === 0) return /* @__PURE__ */ new Set();
return new Set(loadingIndexes);
}, [loadingIndexes]);
const ContainerElement = renderSlot(container, DefaultContainer, {
ref,
className,
...restProps
});
const suggestionElements = suggestions.map((suggestion, index) => {
const isLoading = loadingSet.has(index) || suggestion.isLoading === true;
const pill = renderSlot(suggestionSlot, CopilotChatSuggestionPill, {
children: suggestion.title,
className: suggestion.className,
isLoading,
type: "button",
onClick: () => onSelectSuggestion?.(suggestion, index)
});
return react.default.cloneElement(pill, { key: `${suggestion.title}-${index}` });
});
const boundContainer = react.default.cloneElement(ContainerElement, void 0, suggestionElements);
if (typeof children === "function") {
const sampleSuggestion = renderSlot(suggestionSlot, CopilotChatSuggestionPill, {
children: suggestions[0]?.title ?? "",
isLoading: suggestions.length > 0 ? loadingSet.has(0) || suggestions[0]?.isLoading === true : false,
type: "button"
});
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
"data-copilotkit": true,
style: { display: "contents" },
children: children({
container: boundContainer,
suggestion: sampleSuggestion,
suggestions,
onSelectSuggestion,
loadingIndexes,
className,
...restProps
})
});
}
if (children) return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
"data-copilotkit": true,
style: { display: "contents" },
children: [boundContainer, children]
});
return boundContainer;
});
CopilotChatSuggestionView.displayName = "CopilotChatSuggestionView";
//#endregion
//#region src/v2/components/chat/scroll-element-context.ts
/**
* Provides the scroll container element to child components that need it for
* virtualization. Set by CopilotChatView.ScrollView; consumed by
* CopilotChatMessageView to feed useVirtualizer's getScrollElement.
*
* Carries the element itself (not a ref) so that context consumers re-render
* reactively when the scroll container is first mounted.
*/
const ScrollElementContext = react.default.createContext(null);
//#endregion
//#region src/v2/components/intelligence-indicator/IntelligenceIndicatorView.tsx
/**
* The presentational "CopilotKit Intelligence" face — the default
* rendered by the {@link IntelligenceIndicator} brain and the default
* value for the `intelligenceIndicator` slot.
*
* Layout: a glassmorphism pill (the `__chrome` layer) wrapping an icon
* and a label. The icon is two overlaid SVG paths — a spinner arc and a
* checkmark — whose geometry lives in each path's `d` ATTRIBUTE so it
* renders in every browser (the CSS `d:` property is Chrome-only).
*
* Two states, driven by the `data-status` attribute (see globals.css
* for the exact timing):
* 1. **In-progress.** The arc spins (CSS rotation) inside the pill and
* the checkmark is hidden. Label + icon are a saturated purple.
* 2. **Finished.** The arc fades out mid-spin while the checkmark draws
* itself in upright (animated `stroke-dashoffset`); the pill chrome
* fades away; and the label + icon settle from purple to a neutral
* gray, with the label slanting slightly (a `transform: skewX`
* faux-italic, so it interpolates with the color instead of snapping
* and never reflows). The result reads as quiet "history metadata"
* rather than an active spinner. The label text itself never changes
* — the static check plus the color/slant shift carry the "done"
* meaning, so no wording change is needed.
*
* All motion is gated behind `prefers-reduced-motion` (globals.css):
* when reduced motion is requested the arc does not spin and the two
* states swap instantly, without transitions.
*
* Customize via the `intelligenceIndicator` slot on `CopilotChat`:
* a className string restyles the wrapper, a props object tweaks
* the default (`{ label }`), and a component replaces it entirely
* with full control over visuals and timing.
*/
function IntelligenceIndicatorView({ message, status, label, className, ...rest }) {
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
className: (0, tailwind_merge.twMerge)("cpk-intelligence-indicator", className),
role: "status",
"aria-live": "polite",
"data-testid": `cpk-intelligence-indicator-${message.id}`,
"data-status": status,
title: label,
...rest,
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
className: "cpk-intelligence-indicator__chrome",
"aria-hidden": "true"
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
className: "cpk-intelligence-indicator__content",
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("svg", {
className: "cpk-intelligence-indicator__icon",
viewBox: "0 0 24 24",
width: "14",
height: "14",
"aria-hidden": "true",
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
className: "cpk-intelligence-indicator__icon-arc",
pathLength: 1,
d: "M 12 3 C 17 3 21 7 21 12 C 21 17 17 21 12 21 C 7 21 3 17 3 12 C 3 7 7 3 12 3"
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
className: "cpk-intelligence-indicator__icon-check",
pathLength: 1,
d: "M 5 12.5 L 9 16.5 L 19 6.5"
})]
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
className: "cpk-intelligence-indicator__label",
children: label
})]
})]
});
}
//#endregion
//#region src/v2/components/intelligence-indicator/IntelligenceIndicator.tsx
/**
* Grace window before showing the spinner. A matching tool call must
* remain unresolved (no `tool`-role result message in `agent.messages`)
* for at least this long before the indicator transitions out of
* `hidden`. This filters out history-replay flashes — during
* `connectAgent` replay, tool calls and their results arrive
* back-to-back in sub-millisecond bursts, so the timer is cancelled
* before it fires. Live runs cross the threshold easily because the
* tool actually has to execute.
*/
const PENDING_THRESHOLD_MS = 100;
/**
* Tool-name regex patterns that trigger the indicator. Matches any tool
* name *containing* the Intelligence MCP server's canonical tool name, so
* both the bare `copilotkit_knowledge_base_shell` and the namespaced
* `mcp__<server>__copilotkit_knowledge_base_shell` form (emitted by
* `@ag-ui/mcp-middleware`) light up the pill. If we add per-instance
* customization later (e.g. a `CopilotKitProvider` prop or a runtime-info
* field), this constant becomes the fallback.
*/
const DEFAULT_TOOL_PATTERNS = [/copilotkit_knowledge_base_shell/];
/**
* Phase to start in when an indicator first mounts. A turn that is already
* complete at mount jumps straight to `finished` — no `hidden` flash, no
* spinner blip — which is what makes scrolled-back / replayed history render
* its indicators directly in the finished state.
*
* Pure and timing-free on purpose: the grace window ({@link
* PENDING_THRESHOLD_MS}) only controls *when* the live transition is applied;
* these functions decide *what* it resolves to, so the decision can be unit
* tested deterministically without any timers.
*/
function initialIndicatorPhase(turnComplete) {
return turnComplete ? "finished" : "hidden";
}
/**
* Phase the grace window resolves to once it elapses:
* - completed turn → `finished` (replay-flash suppression: a tool whose
* result lands within the window skips the spinner entirely),
* - a still-pending matching tool call → `spinner`,
* - otherwise stay `hidden` (the matching tool call hasn't landed yet).
*/
function resolveGracePhase(turnComplete, hasPending) {
if (turnComplete) return "finished";
if (hasPending) return "spinner";
return "hidden";
}
const isMatchingToolCallName = (name) => typeof name === "string" && DEFAULT_TOOL_PATTERNS.some((p) => p.test(name));
const messageHasMatchingToolCall = (m) => {
if (m.role !== "assistant") return false;
return (Array.isArray(m.toolCalls) ? m.toolCalls : []).some((tc) => isMatchingToolCallName(tc?.function?.name));
};
/**
* Stable turn id for the messages that precede the first user message (a turn
* with no opening user message of its own). Used as the React key so the
* indicator for that turn never collides with a real user-message id.
*/
const INTELLIGENCE_TURN_HEAD = "__cpk_turn_head__";
/**
* Map each Intelligence-using turn to its anchor message — the FIRST bash-using
* assistant message of the turn — and a stable turn id (the id of the user
* message that opened the turn, or {@link INTELLIGENCE_TURN_HEAD} for the
* pre-first-user turn). Returns `Map<anchorMessageId, turnId>`.
*
* Anchoring to the FIRST (not last) bash-using message keeps the indicator
* fixed in place for the whole turn: later bash steps don't reposition it, so
* the spinner never abruptly jumps mid-turn (bug 1). `CopilotChatMessageView`
* emits exactly one `IntelligenceIndicator` per entry, keyed by the turn id and
* positioned at the anchor; the per-turn key also lets every past turn keep its
* own indicator in scroll-back.
*/
function getIntelligenceTurnAnchors(messages) {
const anchors = /* @__PURE__ */ new Map();
let turnId = INTELLIGENCE_TURN_HEAD;
let anchorId = null;
const commit = () => {
if (anchorId !== null) anchors.set(anchorId, turnId);
anchorId = null;
};
for (const m of messages) {
if (m.role === "user") {
commit();
turnId = m.id;
continue;
}
if (anchorId === null && messageHasMatchingToolCall(m)) anchorId = m.id;
}
commit();
return anchors;
}
/**
* "Tool-call-like" messages do NOT count as a real follow-up: tool
* result messages, assistant messages that carry tool calls, and
* empty-content assistant messages (which some providers emit as a
* standalone wrapper around a batch of tool calls). A real follow-up
* is anything else — most importantly an assistant message with prose
* content, or a fresh user message.
*/
const isToolCallLikeMessage = (m) => {
if (m.role === "tool") return true;
if (m.role === "assistant") {
if ((Array.isArray(m.toolCalls) ? m.toolCalls : []).length > 0) return true;
const content = m.content;
return typeof content !== "string" || content.trim().length === 0;
}
return false;
};
/**
* The "Using CopilotKit Intelligence" indicator brain. Auto-mounted by
* `CopilotChatMessageView` — once per Intelligence-using turn, at that
* turn's anchor message and keyed by the turn id (see
* {@link getIntelligenceTurnAnchors}). Callers do not register this
* themselves. It owns the run subscription and the phase machine and
* renders its swappable face via the `intelligenceIndicator` slot.
*
* Placement (which message anchors the turn) is decided by the view, so
* this component does not self-gate its own placement; it only derives
* in-progress/finished for the turn it was mounted on.
*
* Render gates (all must hold):
* 1. `copilotkit.intelligence !== undefined`
* 2. The (anchor) message is an assistant message with at least one
* tool call whose name matches {@link DEFAULT_TOOL_PATTERNS}.
* 3. The phase machine is past `hidden`.
*
* Because the view keys each indicator by its turn id, the instance moves
* with the anchor across a hand-off (no remount, no spinner restart), and
* every prior Intelligence-using turn keeps its own persistent indicator
* in chat history.
*
* Phase machine (per-instance, all timers local):
* - Starts in `hidden`, unless the message mounts onto an
* already-completed turn (no pending work, agent stopped or a
* real follow-up already present), in which case the lazy
* `useState` initializer starts directly in `finished`. This is
* what avoids a "hidden flash" on history replay.
* - `hidden → spinner` once a matching tool call has been pending
* (no `tool`-role result with a matching `toolCallId`) for
* {@link PENDING_THRESHOLD_MS}. Replay flashes (tool call + result
* in the same tick) never cross this threshold.
* - `hidden → finished` if after the grace window the turn is
* already complete (no pending work AND
* `sawRealFollowup || !agent.isRunning`). Handles very fast tools
* whose result lands within the grace window.
* - `spinner → finished` as soon as EITHER `agent.isRunning` flips
* false OR a non-tool-call-like message appears later in
* `agent.messages` (i.e. the agent produced a "real" follow-up —
* prose answer or a new user turn).
* - `finished` is terminal: the indicator settles into its
* persistent tag form and stays mounted.
*/
function IntelligenceIndicator(props) {
const { message, agentId, label = "CopilotKit Intelligence", intelligenceIndicator } = props;
const { copilotkit } = (0, _copilotkit_react_core_v2_context.useCopilotKit)();
const config = useCopilotChatConfiguration();
const { agent } = useAgent({
agentId,
updates: [UseAgentUpdate.OnRunStatusChanged, UseAgentUpdate.OnMessagesChanged]
});
const matchingToolCallIds = (0, react.useMemo)(() => {
if (message.role !== "assistant") return [];
const tcs = Array.isArray(message.toolCalls) ? message.toolCalls : [];
const ids = [];
for (const tc of tcs) if (isMatchingToolCallName(tc?.function?.name) && tc?.id) ids.push(tc.id);
return ids;
}, [message]);
const hasPending = (0, react.useMemo)(() => {
if (matchingToolCallIds.length === 0) return false;
const resolved = /* @__PURE__ */ new Set();
for (const m of agent.messages) if (m.role === "tool" && m.toolCallId) resolved.add(m.toolCallId);
return matchingToolCallIds.some((id) => !resolved.has(id));
}, [matchingToolCallIds, agent.messages]);
const turnComplete = (0, react.useMemo)(() => {
const idx = agent.messages.findIndex((m) => m.id === message.id);
if (idx < 0) return false;
for (let i = idx + 1; i < agent.messages.length; i += 1) if (!isToolCallLikeMessage(agent.messages[i])) return true;
return false;
}, [agent.messages, message.id]) || !agent.isRunning;
const [phase, setPhase] = (0, react.useState)(() => initialIndicatorPhase(turnComplete));
(0, react.useEffect)(() => {
if (phase !== "hidden") return void 0;
const t = setTimeout(() => {
setPhase(resolveGracePhase(turnComplete, hasPending));
}, PENDING_THRESHOLD_MS);
return () => clearTimeout(t);
}, [
phase,
hasPending,
turnComplete
]);
(0, react.useEffect)(() => {
if (phase !== "spinner") return void 0;
if (turnComplete) setPhase("finished");
}, [phase, turnComplete]);
if (copilotkit.intelligence === void 0) return null;
if (!config) return null;
if (phase === "hidden") return null;
if (message.role !== "assistant") return null;
if (!messageHasMatchingToolCall(message)) return null;
return renderSlot(intelligenceIndicator, IntelligenceIndicatorView, {
message,
status: phase === "finished" ? "finished" : "in-progress",
label
});
}
//#endregion
//#region src/v2/components/chat/CopilotChatMessageView.tsx
/**
* Resolves a slot value into a { Component, slotProps } pair, handling the three
* slot forms: a component type, a className string, or a partial-props object.
*/
function resolveSlotComponent(slot, DefaultComponent) {
if (isReactComponentType(slot)) return {
Component: slot,
slotProps: void 0
};
if (typeof slot === "string") return {
Component: DefaultComponent,
slotProps: { className: slot }
};
if (slot && typeof slot === "object") return {
Component: DefaultComponent,
slotProps: slot
};
return {
Component: DefaultComponent,
slotProps: void 0
};
}
/**
* Memoized wrapper for assistant messages to prevent re-renders when other messages change.
*/
const MemoizedAssistantMessage = react.default.memo(function MemoizedAssistantMessage({ message, messages, isRunning, AssistantMessageComponent, slotProps }) {
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(AssistantMessageComponent, {
message,
messages,
isRunning,
...slotProps
});
}, (prevProps, nextProps) => {
if (prevProps.message.id !== nextProps.message.id) return false;
if (prevProps.message.content !== nextProps.message.content) return false;
const prevToolCalls = prevProps.message.toolCalls;
const nextToolCalls = nextProps.message.toolCalls;
if (prevToolCalls?.length !== nextToolCalls?.length) return false;
if (prevToolCalls && nextToolCalls) for (let i = 0; i < prevToolCalls.length; i++) {
const prevTc = prevToolCalls[i];
const nextTc = nextToolCalls[i];
if (prevTc.id !== nextTc.id) return false;
if (prevTc.function.arguments !== nextTc.function.arguments) return false;
}
if (prevToolCalls && prevToolCalls.length > 0) {
const toolCallIds = new Set(prevToolCalls.map((tc) => tc.id));
const prevToolResults = prevProps.messages.filter((m) => m.role === "tool" && toolCallIds.has(m.toolCallId));
const nextToolResults = nextProps.messages.filter((m) => m.role === "tool" && toolCallIds.has(m.toolCallId));
if (prevToolResults.length !== nextToolResults.length) return false;
for (let i = 0; i < prevToolResults.length; i++) if (prevToolResults[i].content !== nextToolResults[i].content) return false;
}
if (nextProps.messages[nextProps.messages.length - 1]?.id === nextProps.message.id && prevProps.isRunning !== nextProps.isRunning) return false;
if (prevProps.AssistantMessageComponent !== nextProps.AssistantMessageComponent) return false;
if (prevProps.slotProps !== nextProps.slotProps) return false;
return true;
});
/**
* Memoized wrapper for user messages to prevent re-renders when other messages change.
*/
const MemoizedUserMessage = react.default.memo(function MemoizedUserMessage({ message, UserMessageComponent, slotProps }) {
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(UserMessageComponent, {
message,
...slotProps
});
}, (prevProps, nextProps) => {
if (prevProps.message.id !== nextProps.message.id) return false;
if (prevProps.message.content !== nextProps.message.content) return false;
if (prevProps.UserMessageComponent !== nextProps.UserMessageComponent) return false;
if (prevProps.slotProps !== nextProps.slotProps) return false;
return true;
});
/**
* Memoized wrapper for activity messages to prevent re-renders when other messages change.
*/
const MemoizedActivityMessage = react.default.memo(function MemoizedActivityMessage({ message, renderActivityMessage }) {
return renderActivityMessage(message);
}, (prevProps, nextProps) => {
if (prevProps.message.id !== nextProps.message.id) return false;
if (prevProps.message.activityType !== nextProps.message.activityType) return false;
if (JSON.stringify(prevProps.message.content) !== JSON.stringify(nextProps.message.content)) return false;
return true;
});
/**
* Memoized wrapper for reasoning messages to prevent re-renders when other messages change.
*/
const MemoizedReasoningMessage = react.default.memo(function MemoizedReasoningMessage({ message, messages, isRunning, ReasoningMessageComponent, slotProps }) {
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ReasoningMessageComponent, {
message,
messages,
isRunning,
...slotProps
});
}, (prevProps, nextProps) => {
if (prevProps.message.id !== nextProps.message.id) return false;
if (prevProps.message.content !== nextProps.message.content) return false;
const prevIsLatest = prevProps.messages[prevProps.messages.length - 1]?.id === prevProps.message.id;
const nextIsLatest = nextProps.messages[nextProps.messages.length - 1]?.id === nextProps.message.id;
if (prevIsLatest !== nextIsLatest) return false;
if (nextIsLatest && prevProps.isRunning !== nextProps.isRunning) return false;
if (prevProps.ReasoningMessageComponent !== nextProps.ReasoningMessageComponent) return false;
if (prevProps.slotProps !== nextProps.slotProps) return false;
return true;
});
/**
* Memoized wrapper for custom messages to prevent re-renders when other messages change.
*/
const MemoizedCustomMessage = react.default.memo(function MemoizedCustomMessage({ message, position, renderCustomMessage }) {
return renderCustomMessage({
message,
position
});
}, (prevProps, nextProps) => {
if (prevProps.message.id !== nextProps.message.id) return false;
if (prevProps.position !== nextProps.position) return false;
if (prevProps.message.content !== nextProps.message.content) return false;
if (prevProps.message.role !== nextProps.message.role) return false;
if (JSON.stringify(prevProps.stateSnapshot) !== JSON.stringify(nextProps.stateSnapshot)) return false;
return true;
});
/**
* Deduplicates messages by ID. For assistant messages, merges occurrences:
* recovers non-empty content from any earlier occurrence if the latest wiped it
* (empty string means the streaming update cleared the field, not blank text),
* and similarly recovers toolCalls from earlier occurrences if the latest is
* undefined (an empty array [] is treated as intentional and kept as-is).
* For all other roles, keeps the last entry.
*
* @internal Exported for unit testing only — not part of the public API.
*/
/**
* Collapse tool calls that share an id, keeping first-seen order.
*
* AG-UI's TOOL_CALL_START handler appends to the parent message's `toolCalls`
* without checking whether that id is already present. So whenever a start
* event is applied twice — which the human-in-the-loop flow triggers when the
* run syncs after `respond()` — the message ends up carrying the same call
* twice. The second copy has EMPTY `arguments`, because a start event carries
* none; the args arrive later as TOOL_CALL_ARGS deltas addressed to the first
* copy.
*
* Left alone that renders the same tool call twice: once populated and once
* blank (an approval card with no transaction id, offering live buttons for an
* action already taken), and React warns "Encountered two children with the
* same key" because the call id is the render key.
*
* The empty copy is the redundant one, so prefer whichever entry actually
* carries arguments rather than blindly taking the first.
*/
function dedupeToolCalls(toolCalls) {
const byId = /* @__PURE__ */ new Map();
for (const toolCall of toolCalls) {
const existing = byId.get(toolCall.id);
if (!existing) {
byId.set(toolCall.id, toolCall);
continue;
}
if (!existing.function?.arguments && toolCall.function?.arguments) byId.set(toolCall.id, toolCall);
}
return byId.size === toolCalls.length ? toolCalls : [...byId.values()];
}
function deduplicateMessages(messages) {
const acc = /* @__PURE__ */ new Map();
for (const message of messages) {
const existing = acc.get(message.id);
if (existing && message.role === "assistant" && existing.role === "assistant") {
const content = message.content || existing.content;
const toolCalls = message.toolCalls ?? existing.toolCalls;
acc.set(message.id, {
...existing,
...message,
content,
toolCalls: toolCalls ? dedupeToolCalls(toolCalls) : toolCalls
});
} else if (message.role === "assistant" && message.toolCalls) acc.set(message.id, {
...message,
toolCalls: dedupeToolCalls(message.toolCalls)
});
else acc.set(message.id, message);
}
return [...acc.values()];
}
const VIRTUALIZE_THRESHOLD = 50;
function CopilotChatMessageView({ messages = [], assistantMessage, userMessage, reasoningMessage, cursor, intelligenceIndicator, isRunning = false, children, className, ...props }) {
const renderCustomMessage = useRenderCustomMessages();
const { renderActivityMessage } = useRenderActivityMessage();
const { copilotkit } = (0, _copilotkit_react_core_v2_context.useCopilotKit)();
const config = useCopilotChatConfiguration();
const [, forceUpdate] = (0, react.useReducer)((x) => x + 1, 0);
(0, react.useEffect)(() => {
if (!config?.agentId) return;
const agent = copilotkit.getAgent(config.agentId);
if (!agent) return;
const subscription = agent.subscribe({ onStateChanged: forceUpdate });
return () => subscription.unsubscribe();
}, [
config?.agentId,
copilotkit,
forceUpdate
]);
const [interruptElement, setInterruptElement] = (0, react.useState)(null);
(0, react.useEffect)(() => {
setInterruptElement(copilotkit.interruptElement);
const subscription = copilotkit.subscribe({ onInterruptElementChanged: ({ interruptElement }) => {
setInterruptElement(interruptElement);
} });
return () => subscription.unsubscribe();
}, [copilotkit]);
const getStateSnapshotForMessage = (messageId) => {
if (!config) return void 0;
const resolvedRunId = copilotkit.getRunIdForMessage(config.agentId, config.threadId, messageId) ?? copilotkit.getRunIdsForThread(config.agentId, config.threadId).slice(-1)[0];
if (!resolvedRunId) return void 0;
return copilotkit.getStateByRun(config.agentId, config.threadId, resolvedRunId);
};
const deduplicatedMessages = (0, react.useMemo)(() => deduplicateMessages(messages), [messages]);
if (process.env.NODE_ENV === "development" && deduplicatedMessages.length < messages.length) console.warn(`CopilotChatMessageView: Merged ${messages.length - deduplicatedMessages.length} message(s) with duplicate IDs.`);
const { Component: AssistantComponent, slotProps: assistantSlotProps } = (0, react.useMemo)(() => resolveSlotComponent(assistantMessage, CopilotChatAssistantMessage_default), [assistantMessage]);
const agentId = config?.agentId;
const threadId = config?.threadId;
const assistantSlotPropsWithFeedback = (0, react.useMemo)(() => {
const onThumbsUp = assistantSlotProps?.onThumbsUp;
const onThumbsDown = assistantSlotProps?.onThumbsDown;
if (!onThumbsUp && !onThumbsDown) return assistantSlotProps;
const withRawEvent = (message) => {
const rawEvent = agentId === void 0 || threadId === void 0 ? void 0 : copilotkit.getRawEventForMessage(agentId, threadId, message.id);
return rawEvent === void 0 ? message : {
...message,
rawEvent
};
};
return {
...assistantSlotProps,
...onThumbsUp && { onThumbsUp: (message) => onThumbsUp(withRawEvent(message)) },
...onThumbsDown && { onThumbsDown: (message) => onThumbsDown(withRawEvent(message)) }
};
}, [
assistantSlotProps,
agentId,
threadId,
copilotkit
]);
const { Component: UserComponent, slotProps: userSlotProps } = (0, react.useMemo)(() => resolveSlotComponent(userMessage, CopilotChatUserMessage_default), [userMessage]);
const { Component: ReasoningComponent, slotProps: reasoningSlotProps } = (0, react.useMemo)(() => resolveSlotComponent(reasoningMessage, CopilotChatReasoningMessage_default), [reasoningMessage]);
const scrollElementFromCtx = (0, react.useContext)(ScrollElementContext);
const scrollElement = scrollElementFromCtx && scrollElementFromCtx.clientHeight > 0 ? scrollElementFromCtx : null;
(0, react.useEffect)(() => {
if (process.env.NODE_ENV !== "production" && scrollElementFromCtx && scrollElementFromCtx.clientHeight === 0) console.warn("[CopilotKit] Chat scroll container has clientHeight=0 — virtualization disabled. Ensure the chat is rendered in a visible container with a non-zero height.");
}, [scrollElementFromCtx]);
const shouldVirtualize = !!scrollElement && !children && deduplicatedMessages.length > VIRTUALIZE_THRESHOLD;
const virtualizer = (0, _tanstack_react_virtual.useVirtualizer)({
count: shouldVirtualize ? deduplicatedMessages.length : 0,
getScrollElement: () => scrollElement,
estimateSize: () => 100,
overscan: 5,
measureElement: (el) => el?.getBoundingClientRect().height ?? 0,
initialRect: {
width: 0,
height: 600
}
});
const firstMessageId = deduplicatedMessages[0]?.id;
(0, react.useLayoutEffect)(() => {
if (!shouldVirtualize || !deduplicatedMessages.length) return;
virtualizer.scrollToIndex(deduplicatedMessages.length - 1, { align: "end" });
}, [shouldVirtualize, firstMessageId]);
const intelligenceTurnAnchors = (0, react.useMemo)(() => getIntelligenceTurnAnchors(deduplicatedMessages), [deduplicatedMessages]);
const renderMessageBlock = (message) => {
const elements = [];
const stateSnapshot = getStateSnapshotForMessage(message.id);
if (renderCustomMessage) elements.push(/* @__PURE__ */ (0, react_jsx_runtime.jsx)(MemoizedCustomMessage, {
message,
position: "before",
renderCustomMessage,
stateSnapshot
}, `${message.id}-custom-before`));
if (message.role === "assistant") elements.push(/* @__PURE__ */ (0, react_jsx_runtime.jsx)(MemoizedAssistantMessage, {
message,
messages,
isRunning,
AssistantMessageComponent: AssistantComponent,
slotProps: assistantSlotPropsWithFeedback
}, message.id));
else if (message.role === "user") elements.push(/* @__PURE__ */ (0, react_jsx_runtime.jsx)(MemoizedUserMessage, {
message,
UserMessageComponent: UserComponent,
slotProps: userSlotProps
}, message.id));
else if (message.role === "activity") elements.push(/* @__PURE__ */ (0, react_jsx_runtime.jsx)(MemoizedActivityMessage, {
message,
renderActivityMessage
}, message.id));
else if (message.role === "reasoning") elements.push(/* @__PURE__ */ (0, react_jsx_runtime.jsx)(MemoizedReasoningMessage, {
message,
messages,
isRunning,
ReasoningMessageComponent: ReasoningComponent,
slotProps: reasoningSlotProps
}, message.id));
if (renderCustomMessage) elements.push(/* @__PURE__ */ (0, react_jsx_runtime.jsx)(MemoizedCustomMessage, {
message,
position: "after",
renderCustomMessage,
stateSnapshot
}, `${message.id}-custom-after`));
const intelligenceTurnId = intelligenceTurnAnchors.get(message.id);
if (intelligenceTurnId !== void 0) elements.push(/* @__PURE__ */ (0, react_jsx_runtime.jsx)(IntelligenceIndicator, {
message,
agentId: config?.agentId ?? _copilotkit_shared.DEFAULT_AGENT_ID,
intelligenceIndicator
}, `intelligence-${intelligenceTurnId}`));
return elements.filter(Boolean);
};
const messageElements = shouldVirtualize ? [] : deduplicatedMessages.flatMap(renderMessageBlock);
if (children) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
"data-copilotkit": true,
style: { display: "contents" },
children: children({
messageElements,
messages,
isRunning,
interruptElement
})
});
const lastMessage = messages[messages.length - 1];
const showCursor = isRunning && lastMessage?.role !== "reasoning";
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
"data-copilotkit": true,
"data-testid": "copilot-message-list",
className: (0, tailwind_merge.twMerge)("copilotKitMessages cpk:flex cpk:flex-col", className),
...props,
children: [
shouldVirtualize ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
style: {
height: virtualizer.getTotalSize(),
position: "relative"
},
children: virtualizer.getVirtualItems().map((virtualItem) => {
const message = deduplicatedMessages[virtualItem.index];
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
"data-index": virtualItem.index,
ref: virtualizer.measureElement,
style: {
position: "absolute",
top: 0,
left: 0,
width: "100%",
transform: `translateY(${virtualItem.start}px)`
},
children: renderMessageBlock(message)
}, message.id);
})
}) : messageElements,
interruptElement,
showCursor && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
className: "cpk:mt-2",
children: renderSlot(cursor, CopilotChatMessageView.Cursor, {})
})
]
});
}
CopilotChatMessageView.Cursor = function Cursor({ className, ...props }) {
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
"data-testid": "copilot-loading-cursor",
className: (0, tailwind_merge.twMerge)("cpk:w-[11px] cpk:h-[11px] cpk:rounded-full cpk:bg-foreground cpk:animate-pulse-cursor cpk:ml-1", className),
...props
});
};
//#endregion
//#region src/v2/components/chat/CopilotChatAttachmentQueue.tsx
const CopilotChatAttachmentQueue = ({ attachments, onRemoveAttachment, className }) => {
if (attachments.length === 0) return null;
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
"data-testid": "copilot-attachment-queue",
className: cn("cpk:flex cpk:flex-wrap cpk:gap-2 cpk:p-2", className),
children: attachments.map((attachment) => {
const isMedia = attachment.type === "image" || attachment.type === "video";
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
className: cn("cpk:relative cpk:inline-flex cpk:rounded-lg cpk:overflow-hidden cpk:border cpk:border-border", isMedia ? "cpk:w-[72px] cpk:h-[72px]" : attachment.type === "audio" ? "cpk:min-w-[200px] cpk:max-w-[280px] cpk:flex-col cpk:p-1 cpk:pr-8" : "cpk:p-2 cpk:px-3 cpk:pr-8 cpk:max-w-[240px]"),
children: [
attachment.status === "uploading" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(UploadingOverlay, {}),
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(AttachmentPreview, { attachment }),
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
onClick: () => onRemoveAttachment(attachment.id),
className: cn("cpk:absolute cpk:bg-black/60 cpk:text-white cpk:border-none cpk:rounded-full cpk:w-5 cpk:h-5 cpk:flex cpk:items-center cpk:justify-center cpk:cursor-pointer cpk:text-[10px] cpk:z-20", isMedia ? "cpk:top-1 cpk:right-1" : "cpk:top-1.5 cpk:right-1.5"),
"aria-label": "Remove attachment",
children: "✕"
})
]
}, attachment.id);
})
});
};
function UploadingOverlay() {
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
className: "cpk:absolute cpk:inset-0 cpk:flex cpk:items-center cpk:justify-center cpk:bg-black/40 cpk:z-10",
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { className: "cpk:w-5 cpk:h-5 cpk:border-2 cpk:border-white cpk:border-t-transparent cpk:rounded-full cpk:animate-spin" })
});
}
function AttachmentPreview({ attachment }) {
if (attachment.status === "uploading") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { className: "cpk:w-full cpk:h-full" });
switch (attachment.type) {
case "image": return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ImagePreview, { attachment });
case "audio": return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(AudioPreview, { attachment });
case "video": return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(VideoPreview, { attachment });
case "document": return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DocumentPreview, { attachment });
}
}
function ImagePreview({ attachment }) {
const src = (0, _copilotkit_shared.getSourceUrl)(attachment.source);
const { thumbnailRef, vtName, open, openLightbox, closeLightbox } = useLightbox();
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("img", {
ref: thumbnailRef,
src,
alt: attachment.filename || "Image attachment",
className: "cpk:w-full cpk:h-full cpk:object-cover cpk:cursor-pointer",
onClick: openLightbox
}), open && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Lightbox, {
onClose: closeLightbox,
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("img", {
style: { viewTransitionName: vtName },
src,
alt: attachment.filename || "Image attachment",
className: "cpk:max-w-[90vw] cpk:max-h-[90vh] cpk:object-contain cpk:rounded-lg"
})
})] });
}
function AudioPreview({ attachment }) {
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
className: "cpk:flex cpk:flex-col cpk:gap-1 cpk:w-full",
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("audio", {
src: (0, _copilotkit_shared.getSourceUrl)(attachment.source),
controls: true,
preload: "metadata",
className: "cpk:w-full cpk:h-8"
}), attachment.filename && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
className: "cpk:text-xs cpk:font-medium cpk:overflow-hidden cpk:text-ellipsis cpk:whitespace-nowrap",
children: attachment.filename
})]
});
}
function VideoPreview({ attachment }) {
const src = (0, _copilotkit_shared.getSourceUrl)(attachment.source);
const { thumbnailRef, vtName, open, openLightbox, closeLightbox } = useLightbox();
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
ref: thumbnailRef,
className: "cpk:w-full cpk:h-full",
children: attachment.thumbnail ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("img", {
src: attachment.thumbnail,
alt: attachment.filename || "Video thumbnail",
className: "cpk:w-full cpk:h-full cpk:object-cover"
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("video", {
src,
preload: "metadata",
muted: true,
className: "cpk:w-full cpk:h-full cpk:object-cover"
})
}),
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
onClick: openLightbox,
className: "cpk:absolute cpk:inset-0 cpk:flex cpk:items-center cpk:justify-center cpk:z-10 cpk:cursor-pointer cpk:bg-black/20 cpk:border-none cpk:p-0",
"aria-label": "Play video",
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
className: "cpk:w-8 cpk:h-8 cpk:rounded-full cpk:bg-black/60 cpk:flex cpk:items-center cpk:justify-center",
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.Play, { className: "cpk:w-4 cpk:h-4 cpk:text-white cpk:ml-0.5" })
})
}),
open && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Lightbox, {
onClose: closeLightbox,
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("video", {
style: { viewTransitionName: vtName },
src,
controls: true,
autoPlay: true,
className: "cpk:max-w-[90vw] cpk:max-h-[90vh] cpk:rounded-lg"
})
})
] });
}
function isPdf(mimeType) {
return !!mimeType && mimeType.includes("pdf");
}
function isText(mimeType) {
return !!mimeType && mimeType.startsWith("text/");
}
function canPreviewInBrowser(mimeType) {
return isPdf(mimeType) || isText(mimeType);
}
/**
* Convert a base64-encoded data source to a blob: URL that browsers will
* render inside an iframe (data: URLs are blocked for PDFs in most browsers).
*/
function useBlobUrl(attachment) {
const [url, setUrl] = (0, react.useState)(null);
(0, react.useEffect)(() => {
if (attachment.source.type !== "data") return;
try {
const binary = atob(attachment.source.value);
const bytes = new Uint8Array(binary.length);
for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);
const blob = new Blob([bytes], { type: attachment.source.mimeType || "application/octet-stream" });
const blobUrl = URL.createObjectURL(blob);
setUrl(blobUrl);
return () => URL.revokeObjectURL(blobUrl);
} catch (error) {
console.error("[CopilotKit] Failed to decode attachment data:", error);
setUrl(null);
}
}, [
attachment.source.type,
attachment.source.value,
attachment.source.mimeType
]);
if (attachment.source.type === "url") return attachment.source.value;
return url;
}
function DocumentLightboxContent({ attachment, vtName }) {
const mimeType = attachment.source.mimeType;
const blobUrl = useBlobUrl(attachment);
if (isPdf(mimeType)) {
if (!blobUrl) return null;
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("iframe", {
style: { viewTransitionName: vtName },
src: blobUrl,
title: attachment.filename || "PDF preview",
className: "cpk:w-[90vw] cpk:h-[90vh] cpk:max-w-[1000px] cpk:rounded-lg cpk:bg-white"
});
}
if (isText(mimeType)) {
const textContent = attachment.source.type === "data" ? (() => {
try {
return atob(attachment.source.value);
} catch {
return attachment.source.value;
}
})() : null;
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
style: { viewTransitionName: vtName },
className: "cpk:w-[90vw] cpk:max-w-[800px] cpk:max-h-[90vh] cpk:overflow-auto cpk:rounded-lg cpk:bg-white cpk:dark:bg-gray-900 cpk:p-6",
children: [attachment.filename && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
className: "cpk:text-sm cpk:font-medium cpk:text-gray-500 cpk:dark:text-gray-400 cpk:mb-4 cpk:pb-2 cpk:border-b cpk:border-gray-200 cpk:dark:border-gray-700",
children: attachment.filename
}), textContent ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("pre", {
className: "cpk:text-sm cpk:whitespace-pre-wrap cpk:break-words cpk:text-gray-800 cpk:dark:text-gray-200 cpk:font-mono cpk:m-0",
children: textContent
}) : blobUrl ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("iframe", {
src: blobUrl,
title: attachment.filename || "Text preview",
className: "cpk:w-full cpk:h-[80vh] cpk:border-none"
}) : null]
});
}
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
style: { viewTransitionName: vtName },
className: "cpk:flex cpk:flex-col cpk:items-center cpk:gap-4 cpk:p-8 cpk:rounded-lg cpk:bg-white cpk:dark:bg-gray-900",
children: [
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
className: "cpk:w-16 cpk:h-16 cpk:rounded-xl cpk:bg-primary cpk:text-primary-foreground cpk:flex cpk:items-center cpk:justify-center cpk:text-xl cpk:font-bold",
children: (0, _copilotkit_shared.getDocumentIcon)(mimeType ?? "")
}),
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
className: "cpk:text-center",
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
className: "cpk:text-base cpk:font-medium cpk:text-gray-800 cpk:dark:text-gray-200",
children: attachment.filename || "Document"
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
className: "cpk:text-sm cpk:text-gray-500 cpk:dark:text-gray-400 cpk:mt-1",
children: [mimeType || "Unknown type", attachment.size != null && ` · ${(0, _copilotkit_shared.formatFileSize)(attachment.size)}`]
})]
}),
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
className: "cpk:text-xs cpk:text-gray-400 cpk:dark:text-gray-500",
children: "No preview available for this file type"
})
]
});
}
function DocumentPreview({ attachment }) {
const { thumbnailRef, vtName, open, openLightbox, closeLightbox } = useLightbox();
const mimeType = attachment.source.mimeType;
const previewable = canPreviewInBrowser(mimeType);
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
ref: thumbnailRef,
className: cn("cpk:flex cpk:items-center cpk:gap-2", previewable && "cpk:cursor-pointer"),
onClick: previewable ? openLightbox : void 0,
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
className: "cpk:w-8 cpk:h-8 cpk:rounded-md cpk:bg-primary cpk:text-primary-foreground cpk:flex cpk:items-center cpk:justify-center cpk:text-[10px] cpk:font-semibold cpk:shrink-0",
children: (0, _copilotkit_shared.getDocumentIcon)(mimeType ?? "")
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
className: "cpk:flex cpk:flex-col cpk:min-w-0",
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
className: "cpk:text-xs cpk:font-medium cpk:break-all cpk:leading-tight",
children: attachment.filename || "Document"
}), attachment.size != null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
className: "cpk:text-[11px] cpk:text-muted-foreground",
children: (0, _copilotkit_shared.formatFileSize)(attachment.size)
})]
})]
}), open && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Lightbox, {
onClose: closeLightbox,
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DocumentLightboxContent, {
attachment,
vtName
})
})] });
}
//#endregion
//#region src/v2/hooks/use-keyboard-height.tsx
/**
* Hook to detect mobile keyboard appearance and calculate available viewport height.
* Uses the Visual Viewport API to track keyboard state on mobile devices.
*
* @returns KeyboardState object with keyboard information
*/
function useKeyboardHeight() {
const [keyboardState, setKeyboardState] = (0, react.useState)({
isKeyboardOpen: false,
keyboardHeight: 0,
availableHeight: typeof window !== "undefined" ? window.innerHeight : 0,
viewportHeight: typeof window !== "undefined" ? window.innerHeight : 0
});
(0, react.useEffect)(() => {
if (typeof window === "undefined") return;
const visualViewport = window.visualViewport;
if (!visualViewport) return;
const updateKeyboardState = () => {
const layoutHeight = window.innerHeight;
const visualHeight = visualViewport.height;
const keyboardHeight = Math.max(0, layoutHeight - visualHeight);
setKeyboardState({
isKeyboardOpen: keyboardHeight > 150,
keyboardHeight,
availableHeight: visualHeight,
viewportHeight: layoutHeight
});
};
updateKeyboardState();
visualViewport.addEventListener("resize", updateKeyboardState);
visualViewport.addEventListener("scroll", updateKeyboardState);
return () => {
visualViewport.removeEventListener("resize", updateKeyboardState);
visualViewport.removeEventListener("scroll", updateKeyboardState);
};
}, []);
return keyboardState;
}
//#endregion
//#region src/v2/components/chat/normalize-auto-scroll.ts
const VALID = [
"pin-to-bottom",
"pin-to-send",
"none"
];
function normalizeAutoScroll(value) {
if (value === void 0) return "pin-to-bottom";
if (value === true) return "pin-to-bottom";
if (value === false) return "none";
if (VALID.includes(value)) return value;
return "pin-to-bottom";
}
//#endregion
//#region src/v2/components/chat/last-user-message-context.ts
const LastUserMessageContext = react.default.createContext({
id: null,
sendNonce: 0
});
//#endregion
//#region src/v2/hooks/use-pin-to-send.ts
function usePinToSend({ scrollRef, contentRef, spacerRef, topOffset = 16 }) {
const { id, sendNonce } = (0, react.useContext)(LastUserMessageContext);
const lastNonceRef = (0, react.useRef)(-1);
(0, react.useEffect)(() => {
if (sendNonce === lastNonceRef.current) return;
lastNonceRef.current = sendNonce;
if (!id) return;
const scrollEl = scrollRef.current;
const contentEl = contentRef.current;
const spacerEl = spacerRef.current;
if (!scrollEl || !contentEl || !spacerEl) return;
const escaped = typeof CSS !== "undefined" && CSS.escape ? CSS.escape(id) : id.replace(/[!"#$%&'()*+,./:;<=>?@[\\\]^`{|}~]/g, "\\$&");
const targetEl = contentEl.querySelector(`[data-message-id="${escaped}"]`);
if (!targetEl) return;
const viewportHeight = scrollEl.clientHeight;
const userMessageHeight = targetEl.getBoundingClientRect().height;
const paddingTop = parseFloat(getComputedStyle(targetEl).paddingTop) || 0;
const bubbleHeight = Math.max(0, userMessageHeight - paddingTop);
const spacerHeight = Math.max(0, viewportHeight - bubbleHeight - topOffset);
spacerEl.style.height = `${spacerHeight}px`;
const raf = requestAnimationFrame(() => {
const targetTop = computeOffsetTop(targetEl, scrollEl) + paddingTop - topOffset;
scrollEl.scrollTo({
top: Math.max(0, targetTop),
behavior: "smooth"
});
});
const ro = new ResizeObserver(() => {
if (!contentEl || !spacerEl || !scrollEl) return;
const consumedBelow = contentEl.getBoundingClientRect().height - computeOffsetTop(targetEl, contentEl) - userMessageHeight;
const remaining = Math.max(0, spacerHeight - consumedBelow);
spacerEl.style.height = `${remaining}px`;
});
ro.observe(contentEl);
return () => {
cancelAnimationFrame(raf);
ro.disconnect();
};
}, [
id,
sendNonce,
scrollRef,
contentRef,
spacerRef,
topOffset
]);
}
function computeOffsetTop(el, stopAt) {
const elRect = el.getBoundingClientRect();
const stopRect = stopAt.getBoundingClientRect();
return elRect.top - stopRect.top + stopAt.scrollTop;
}
//#endregion
//#region src/v2/components/chat/CopilotChatView.tsx
const SCROLL_BUTTON_OFFSET = 16;
function DropOverlay() {
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
className: cn("cpk:absolute cpk:inset-0 cpk:z-50 cpk:pointer-events-none", "cpk:flex cpk:items-center cpk:justify-center", "cpk:bg-primary/5 cpk:backdrop-blur-[2px]", "cpk:border-2 cpk:border-dashed cpk:border-primary/40 cpk:rounded-lg cpk:m-2"),
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
className: "cpk:flex cpk:flex-col cpk:items-center cpk:gap-2 cpk:text-primary/70",
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.Upload, { className: "cpk:w-8 cpk:h-8" }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
className: "cpk:text-sm cpk:font-medium",
children: "Drop files here"
})]
})
});
}
function CopilotChatView({ messageView, input, scrollView, suggestionView, welcomeScreen, messages = [], autoScroll = true, isRunning = false, suggestions, suggestionLoadingIndexes, onSelectSuggestion, onSubmitMessage, onStop, inputMode, inputValue, onInputChange, onStartTranscribe, onCancelTranscribe, onFinishTranscribe, onFinishTranscribeWithAudio, attachments, onRemoveAttachment, onAddFile, dragOver, onDragOver, onDragLeave, onDrop, isConnecting = false, hasExplicitThreadId = false, disclaimer, intelligenceIndicator, children, className, ...props }) {
const [inputContainerEl, setInputContainerEl] = (0, react.useState)(null);
const [inputContainerHeight, setInputContainerHeight] = (0, react.useState)(0);
const [isResizing, setIsResizing] = (0, react.useState)(false);
const resizeTimeoutRef = (0, react.useRef)(null);
const { isKeyboardOpen, keyboardHeight, availableHeight } = useKeyboardHeight();
(0, react.useEffect)(() => {
const element = inputContainerEl;
if (!element) {
setInputContainerHeight(0);
return;
}
const resizeObserver = new ResizeObserver((entries) => {
for (const entry of entries) {
const newHeight = entry.contentRect.height;
setInputContainerHeight((prevHeight) => {
if (newHeight !== prevHeight) {
setIsResizing(true);
if (resizeTimeoutRef.current) clearTimeout(resizeTimeoutRef.current);
resizeTimeoutRef.current = setTimeout(() => {
setIsResizing(false);
}, 250);
return newHeight;
}
return prevHeight;
});
}
});
resizeObserver.observe(element);
setInputContainerHeight(element.offsetHeight);
return () => {
resizeObserver.disconnect();
if (resizeTimeoutRef.current) clearTimeout(resizeTimeoutRef.current);
};
}, [inputContainerEl]);
const BoundMessageView = renderSlot(messageView, CopilotChatMessageView, {
messages,
isRunning,
intelligenceIndicator
});
const BoundInput = renderSlot(input, CopilotChatInput_default, {
onSubmitMessage,
onStop,
mode: inputMode,
value: inputValue,
onChange: onInputChange,
isRunning,
onStartTranscribe,
onCancelTranscribe,
onFinishTranscribe,
onFinishTranscribeWithAudio,
onAddFile,
positioning: "static",
keyboardHeight: isKeyboardOpen ? keyboardHeight : 0,
showDisclaimer: true,
bottomAnchored: true,
...disclaimer !== void 0 ? { disclaimer } : {}
});
const hasSuggestions = !isConnecting && !isRunning && Array.isArray(suggestions) && suggestions.length > 0;
const BoundSuggestionView = hasSuggestions ? renderSlot(suggestionView, CopilotChatSuggestionView, {
suggestions,
loadingIndexes: suggestionLoadingIndexes,
onSelectSuggestion,
className: "cpk:mb-3 cpk:lg:ml-4 cpk:lg:mr-4 cpk:ml-0 cpk:mr-0"
}) : null;
const BoundScrollView = renderSlot(scrollView, CopilotChatView.ScrollView, {
autoScroll,
inputContainerHeight,
isResizing,
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
"data-testid": "copilot-scroll-content",
style: { paddingBottom: `${inputContainerHeight + (hasSuggestions ? 4 : 32)}px` },
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
className: "cpk:max-w-3xl cpk:mx-auto",
children: [BoundMessageView, hasSuggestions ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
className: "cpk:pl-0 cpk:pr-4 cpk:@3xl:px-0 cpk:mt-4",
children: BoundSuggestionView
}) : null]
})
})
});
if (messages.length === 0 && !(welcomeScreen === false) && !isConnecting && !hasExplicitThreadId) {
const BoundInputForWelcome = renderSlot(input, CopilotChatInput_default, {
onSubmitMessage,
onStop,
mode: inputMode,
value: inputValue,
onChange: onInputChange,
isRunning,
onStartTranscribe,
onCancelTranscribe,
onFinishTranscribe,
onFinishTranscribeWithAudio,
onAddFile,
positioning: "static",
showDisclaimer: true,
...disclaimer !== void 0 ? { disclaimer } : {}
});
const welcomeScreenSlot = welcomeScreen === true ? void 0 : welcomeScreen;
const inputWithAttachments = /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
className: "cpk:w-full",
children: [attachments && attachments.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CopilotChatAttachmentQueue, {
attachments,
onRemoveAttachment: (id) => onRemoveAttachment?.(id),
className: "cpk:mb-2"
}), BoundInputForWelcome]
});
const BoundWelcomeScreen = renderSlot(welcomeScreenSlot, CopilotChatView.WelcomeScreen, {
input: inputWithAttachments,
suggestionView: BoundSuggestionView ?? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_jsx_runtime.Fragment, {})
});
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
"data-copilotkit": true,
"data-testid": "copilot-chat",
"data-copilot-running": isRunning ? "true" : "false",
onDragOver,
onDragLeave,
onDrop,
className: cn("copilotKitChat cpk:@container cpk:relative cpk:h-full cpk:flex cpk:flex-col", className),
...props,
children: [dragOver && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DropOverlay, {}), BoundWelcomeScreen]
});
}
if (children) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
"data-copilotkit": true,
style: { display: "contents" },
children: children({
messageView: BoundMessageView,
input: BoundInput,
scrollView: BoundScrollView,
suggestionView: BoundSuggestionView ?? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_jsx_runtime.Fragment, {})
})
});
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
"data-copilotkit": true,
"data-testid": "copilot-chat",
"data-copilot-running": isRunning ? "true" : "false",
onDragOver,
onDragLeave,
onDrop,
className: cn("copilotKitChat cpk:@container cpk:relative cpk:h-full cpk:flex cpk:flex-col", className),
...props,
children: [
dragOver && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DropOverlay, {}),
BoundScrollView,
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
ref: setInputContainerEl,
"data-testid": "copilot-input-overlay",
className: "cpk:absolute cpk:bottom-0 cpk:left-0 cpk:right-0 cpk:z-20 cpk:pointer-events-none",
children: [attachments && attachments.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
className: "cpk:max-w-3xl cpk:mx-auto cpk:w-full cpk:pointer-events-auto",
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CopilotChatAttachmentQueue, {
attachments,
onRemoveAttachment: (id) => onRemoveAttachment?.(id),
className: "cpk:px-4"
})
}), BoundInput]
})
]
});
}
(function(_CopilotChatView) {
const ScrollContent = ({ children, scrollToBottomButton, feather, inputContainerHeight, isResizing }) => {
const { isAtBottom, scrollToBottom, scrollRef } = (0, use_stick_to_bottom.useStickToBottomContext)();
const [scrollEl, setScrollEl] = (0, react.useState)(null);
(0, react.useLayoutEffect)(() => {
setScrollEl(scrollRef.current ?? null);
}, []);
const BoundFeather = renderSlot(feather, CopilotChatView.Feather, {});
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ScrollElementContext.Provider, {
value: scrollEl,
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(use_stick_to_bottom.StickToBottom.Content, {
className: "cpk:overflow-y-auto cpk:overflow-x-hidden",
style: {
flex: "1 1 0%",
minHeight: 0
},
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
className: "cpk:px-4 cpk:@3xl:px-0 cpk:[div[data-sidebar-chat]_&]:px-8 cpk:[div[data-popup-chat]_&]:px-6",
children
})
}),
BoundFeather,
!isAtBottom && !isResizing && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
className: "cpk:absolute cpk:inset-x-0 cpk:flex cpk:justify-center cpk:z-30 cpk:pointer-events-none",
style: { bottom: `${inputContainerHeight + SCROLL_BUTTON_OFFSET}px` },
children: renderSlot(scrollToBottomButton, CopilotChatView.ScrollToBottomButton, { onClick: () => scrollToBottom() })
})
] })
});
};
const PinToSendScrollContainer = ({ children, scrollRef, contentRef, scrollToBottom, scrollToBottomButton, feather, inputContainerHeight, isResizing, nonAutoScrollEl, nonAutoScrollRefCallback, showScrollButton, className, ...props }) => {
const spacerRef = (0, react.useRef)(null);
usePinToSend({
scrollRef,
contentRef,
spacerRef,
topOffset: 16
});
const BoundFeather = renderSlot(feather, CopilotChatView.Feather, {});
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ScrollElementContext.Provider, {
value: nonAutoScrollEl,
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
className: cn("cpk:h-full cpk:max-h-full cpk:flex cpk:flex-col cpk:min-h-0 cpk:relative", className),
children: [
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
ref: nonAutoScrollRefCallback,
className: "cpk:flex-1 cpk:min-h-0 cpk:overflow-y-auto cpk:overflow-x-hidden",
...props,
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
ref: contentRef,
className: "cpk:px-4 cpk:@3xl:px-0 cpk:[div[data-sidebar-chat]_&]:px-8 cpk:[div[data-popup-chat]_&]:px-6",
children
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
ref: spacerRef,
"data-pin-to-send-spacer": true,
"aria-hidden": "true",
style: {
height: 0,
flex: "0 0 auto"
}
})]
}),
BoundFeather,
showScrollButton && !isResizing && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
className: "cpk:absolute cpk:inset-x-0 cpk:flex cpk:justify-center cpk:z-30 cpk:pointer-events-none",
style: { bottom: `${inputContainerHeight + SCROLL_BUTTON_OFFSET}px` },
children: renderSlot(scrollToBottomButton, CopilotChatView.ScrollToBottomButton, { onClick: () => scrollToBottom() })
})
]
})
});
};
_CopilotChatView.ScrollView = ({ children, autoScroll = "pin-to-bottom", scrollToBottomButton, feather, inputContainerHeight = 0, isResizing = false, className, ...props }) => {
const mode = normalizeAutoScroll(autoScroll);
const [hasMounted, setHasMounted] = (0, react.useState)(false);
const scrollRef = (0, react.useRef)(null);
const contentRef = (0, react.useRef)(null);
const scrollToBottom = (0, react.useCallback)(() => {
const el = scrollRef.current;
if (el) el.scrollTo({
top: el.scrollHeight,
behavior: "smooth"
});
}, []);
const [showScrollButton, setShowScrollButton] = (0, react.useState)(false);
const [nonAutoScrollEl, setNonAutoScrollEl] = (0, react.useState)(null);
const nonAutoScrollRefCallback = (0, react.useCallback)((el) => {
scrollRef.current = el;
setNonAutoScrollEl(el);
}, []);
(0, react.useEffect)(() => {
setHasMounted(true);
}, []);
(0, react.useEffect)(() => {
if (mode === "pin-to-bottom") return;
const scrollElement = nonAutoScrollEl;
if (!scrollElement) return;
const checkScroll = () => {
setShowScrollButton(!(scrollElement.scrollHeight - scrollElement.scrollTop - scrollElement.clientHeight < 10));
};
checkScroll();
scrollElement.addEventListener("scroll", checkScroll);
const resizeObserver = new ResizeObserver(checkScroll);
resizeObserver.observe(scrollElement);
return () => {
scrollElement.removeEventListener("scroll", checkScroll);
resizeObserver.disconnect();
};
}, [nonAutoScrollEl, mode]);
if (!hasMounted) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
className: "cpk:h-full cpk:max-h-full cpk:flex cpk:flex-col cpk:min-h-0 cpk:overflow-y-auto cpk:overflow-x-hidden",
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
className: "cpk:px-4 cpk:@3xl:px-0 cpk:[div[data-sidebar-chat]_&]:px-8 cpk:[div[data-popup-chat]_&]:px-6",
children
})
});
if (mode === "none") {
const BoundFeather = renderSlot(feather, CopilotChatView.Feather, {});
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ScrollElementContext.Provider, {
value: nonAutoScrollEl,
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
ref: nonAutoScrollRefCallback,
className: cn("cpk:h-full cpk:max-h-full cpk:flex cpk:flex-col cpk:min-h-0 cpk:overflow-y-auto cpk:overflow-x-hidden cpk:relative", className),
...props,
children: [
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
ref: contentRef,
className: "cpk:px-4 cpk:@3xl:px-0 cpk:[div[data-sidebar-chat]_&]:px-8 cpk:[div[data-popup-chat]_&]:px-6",
children
}),
BoundFeather,
showScrollButton && !isResizing && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
className: "cpk:absolute cpk:inset-x-0 cpk:flex cpk:justify-center cpk:z-30 cpk:pointer-events-none",
style: { bottom: `${inputContainerHeight + SCROLL_BUTTON_OFFSET}px` },
children: renderSlot(scrollToBottomButton, CopilotChatView.ScrollToBottomButton, { onClick: () => scrollToBottom() })
})
]
})
});
}
if (mode === "pin-to-send") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PinToSendScrollContainer, {
scrollRef,
contentRef,
scrollToBottom,
scrollToBottomButton,
feather,
inputContainerHeight,
isResizing,
nonAutoScrollEl,
nonAutoScrollRefCallback,
showScrollButton,
className,
...props,
children
});
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(use_stick_to_bottom.StickToBottom, {
className: cn("cpk:flex-1 cpk:max-h-full cpk:flex cpk:flex-col cpk:min-h-0", className),
resize: "smooth",
initial: "smooth",
...props,
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ScrollContent, {
scrollToBottomButton,
feather,
inputContainerHeight,
isResizing,
children
})
});
};
_CopilotChatView.ScrollToBottomButton = ({ className, ...props }) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Button, {
"data-testid": "copilot-scroll-to-bottom",
variant: "outline",
size: "sm",
className: (0, tailwind_merge.twMerge)("cpk:rounded-full cpk:w-10 cpk:h-10 cpk:p-0 cpk:pointer-events-auto", "cpk:bg-white cpk:dark:bg-gray-900", "cpk:shadow-lg cpk:border cpk:border-gray-200 cpk:dark:border-gray-700", "cpk:hover:bg-gray-50 cpk:dark:hover:bg-gray-800", "cpk:flex cpk:items-center cpk:justify-center cpk:cursor-pointer", className),
...props,
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.ChevronDown, { className: "cpk:w-4 cpk:h-4 cpk:text-gray-600 cpk:dark:text-white" })
});
_CopilotChatView.Feather = ({ className, ...props }) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
className,
...props
});
_CopilotChatView.WelcomeMessage = ({ className, ...props }) => {
const labels = useCopilotChatConfiguration()?.labels ?? CopilotChatDefaultLabels;
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h1", {
className: cn("cpk:text-xl cpk:sm:text-2xl cpk:font-medium cpk:text-foreground cpk:text-center", className),
...props,
children: labels.welcomeMessageText
});
};
_CopilotChatView.WelcomeScreen = ({ welcomeMessage, input, suggestionView, className, children, ...props }) => {
const BoundWelcomeMessage = renderSlot(welcomeMessage, CopilotChatView.WelcomeMessage, {});
if (children) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
"data-copilotkit": true,
style: { display: "contents" },
children: children({
welcomeMessage: BoundWelcomeMessage,
input,
suggestionView,
className,
...props
})
});
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
"data-testid": "copilot-welcome-screen",
className: cn("cpk:flex-1 cpk:flex cpk:flex-col cpk:items-center cpk:justify-center cpk:px-4", className),
...props,
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
className: "cpk:w-full cpk:max-w-3xl cpk:flex cpk:flex-col cpk:items-center",
children: [
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
className: "cpk:mb-6",
children: BoundWelcomeMessage
}),
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
className: "cpk:w-full",
children: input
}),
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
className: "cpk:mt-4 cpk:flex cpk:justify-center",
children: suggestionView
})
]
})
});
};
})(CopilotChatView || (CopilotChatView = {}));
var CopilotChatView_default = CopilotChatView;
//#endregion
//#region src/v2/lib/transcription-client.ts
/**
* Convert a Blob to a base64 string
*/
async function blobToBase64(blob) {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onloadend = () => {
const base64 = reader.result.split(",")[1];
resolve(base64 ?? "");
};
reader.onerror = () => reject(/* @__PURE__ */ new Error("Failed to read audio data"));
reader.readAsDataURL(blob);
});
}
/**
* Check if an error response matches our expected format
*/
function isTranscriptionErrorResponse(data) {
return typeof data === "object" && data !== null && "error" in data && "message" in data && typeof data.error === "string" && typeof data.message === "string";
}
/**
* Parse error info from a transcription error response
*/
function parseTranscriptionError(response) {
return {
code: response.error,
message: response.message,
retryable: response.retryable ?? false
};
}
/**
* Custom error type for transcription failures.
* Extends Error with transcription-specific info for contextual error handling.
*/
var TranscriptionError = class extends Error {
constructor(info) {
super(info.message);
this.name = "TranscriptionError";
this.info = info;
}
};
/**
* Transcribe an audio blob using the CopilotKit runtime
*
* Supports both REST mode (multipart/form-data) and single-endpoint mode (base64 JSON)
*
* @throws {TranscriptionError} When transcription fails with typed error information
*/
async function transcribeAudio(core, audioBlob, filename = "recording.webm") {
const runtimeUrl = core.runtimeUrl;
if (!runtimeUrl) throw new TranscriptionError({
code: _copilotkit_shared.TranscriptionErrorCode.INVALID_REQUEST,
message: "Runtime URL is not configured",
retryable: false
});
const headers = { ...core.headers };
let response;
try {
if (core.runtimeTransport === "single") {
const base64Audio = await blobToBase64(audioBlob);
headers["Content-Type"] = "application/json";
response = await fetch(runtimeUrl, {
method: "POST",
headers,
body: JSON.stringify({
method: "transcribe",
body: {
audio: base64Audio,
mimeType: audioBlob.type || "audio/webm",
filename
}
})
});
} else {
delete headers["Content-Type"];
const formData = new FormData();
formData.append("audio", audioBlob, filename);
response = await fetch(`${runtimeUrl}/transcribe`, {
method: "POST",
headers,
body: formData
});
}
} catch (error) {
throw new TranscriptionError({
code: _copilotkit_shared.TranscriptionErrorCode.NETWORK_ERROR,
message: error instanceof Error ? error.message : "Network request failed",
retryable: true
});
}
if (!response.ok) {
let errorData;
try {
errorData = await response.json();
} catch {
throw new TranscriptionError({
code: _copilotkit_shared.TranscriptionErrorCode.PROVIDER_ERROR,
message: `HTTP ${response.status}: ${response.statusText}`,
retryable: response.status >= 500
});
}
if (isTranscriptionErrorResponse(errorData)) throw new TranscriptionError(parseTranscriptionError(errorData));
throw new TranscriptionError({
code: _copilotkit_shared.TranscriptionErrorCode.PROVIDER_ERROR,
message: typeof errorData === "object" && errorData !== null && "message" in errorData ? String(errorData.message) : "Transcription failed",
retryable: response.status >= 500
});
}
return await response.json();
}
//#endregion
//#region src/v2/components/chat/CopilotChat.tsx
function CopilotChat({ agentId, threadId, labels, chatView, isModalDefaultOpen, attachments: attachmentsConfig, onError, throttleMs, ...props }) {
const existingConfig = useCopilotChatConfiguration();
const resolvedAgentId = agentId ?? existingConfig?.agentId ?? _copilotkit_shared.DEFAULT_AGENT_ID;
const providedThreadId = threadId ?? existingConfig?.threadId;
const resolvedThreadId = (0, react.useMemo)(() => providedThreadId ?? (0, _copilotkit_shared.randomUUID)(), [providedThreadId]);
const hasExplicitThreadId = !!threadId || !!existingConfig?.hasExplicitThreadId;
const { agent, isReady } = useAgent({
agentId: resolvedAgentId,
throttleMs
});
const { copilotkit } = (0, _copilotkit_react_core_v2_context.useCopilotKit)();
const { suggestions: autoSuggestions } = useSuggestions({ agentId: resolvedAgentId });
const { checkFeature } = (0, _copilotkit_react_core_v2_context.useLicenseContext)();
const isChatLicensed = checkFeature("chat");
(0, react.useEffect)(() => {
if (!isChatLicensed) console.warn("[CopilotKit] Warning: \"chat\" feature is not licensed. Visit copilotkit.ai/pricing");
}, [isChatLicensed]);
const onErrorRef = (0, react.useRef)(onError);
(0, react.useEffect)(() => {
onErrorRef.current = onError;
}, [onError]);
(0, react.useEffect)(() => {
if (!onErrorRef.current) return;
const subscription = copilotkit.subscribe({ onError: (event) => {
if (event.context?.agentId === resolvedAgentId || !event.context?.agentId) onErrorRef.current?.({
error: event.error,
code: event.code,
context: event.context
});
} });
return () => {
subscription.unsubscribe();
};
}, [copilotkit, resolvedAgentId]);
const [transcribeMode, setTranscribeMode] = (0, react.useState)("input");
const [inputValue, setInputValue] = (0, react.useState)("");
const [transcriptionError, setTranscriptionError] = (0, react.useState)(null);
const [isTranscribing, setIsTranscribing] = (0, react.useState)(false);
const { attachments: selectedAttachments, enabled: attachmentsEnabled, dragOver, fileInputRef, containerRef: chatContainerRef, handleFileUpload, handleDragOver, handleDragLeave, handleDrop, removeAttachment, consumeAttachments } = useAttachments({ config: attachmentsConfig });
const selectedAttachmentsRef = (0, react.useRef)(selectedAttachments);
(0, react.useEffect)(() => {
selectedAttachmentsRef.current = selectedAttachments;
}, [selectedAttachments]);
const isTranscriptionEnabled = copilotkit.audioFileTranscriptionEnabled;
const isMediaRecorderSupported = typeof window !== "undefined" && typeof MediaRecorder !== "undefined";
const { messageView: providedMessageView, suggestionView: providedSuggestionView, onStop: providedStopHandler, ...restProps } = props;
const [lastConnectedThreadId, setLastConnectedThreadId] = (0, react.useState)(null);
const isConnecting = hasExplicitThreadId && lastConnectedThreadId !== resolvedThreadId;
const activeConnectCountRef = (0, react.useRef)(0);
const pendingRunActivityReconnectRef = (0, react.useRef)(false);
const runActivityReconnectGenerationRef = (0, react.useRef)(0);
const activeLocalRunIdsRef = (0, react.useRef)(/* @__PURE__ */ new Set());
const recentlyLocalRunIdsRef = (0, react.useRef)(/* @__PURE__ */ new Map());
const activeWakeRunIdsRef = (0, react.useRef)(/* @__PURE__ */ new Set());
const recentlyWakeRunIdsRef = (0, react.useRef)(/* @__PURE__ */ new Map());
const pendingWakeRunIdRef = (0, react.useRef)(void 0);
const startRunActivityReconnectRef = (0, react.useRef)(null);
const runtimeStatus = copilotkit.runtimeConnectionStatus === _copilotkit_core.CopilotKitCoreRuntimeConnectionStatus.Connected ? "Connected" : copilotkit.runtimeConnectionStatus;
const hasNativeIntelligenceRunActivity = hasExplicitThreadId && runtimeStatus === "Connected" && !!copilotkit.intelligence?.wsUrl && copilotkit.threadEndpoints?.realtimeMetadata === true;
const [standaloneRunActivityStore] = (0, react.useState)(() => (0, _copilotkit_core.ɵcreateThreadStore)({ fetch: globalThis.fetch }));
const previousThreadIdRef = (0, react.useRef)(null);
const hasExplicitThreadIdRef = (0, react.useRef)(hasExplicitThreadId);
hasExplicitThreadIdRef.current = hasExplicitThreadId;
const rememberRecentlyLocalRunId = (0, react.useCallback)((runId) => {
const existingTimeout = recentlyLocalRunIdsRef.current.get(runId);
if (existingTimeout) clearTimeout(existingTimeout);
const timeout = setTimeout(() => {
recentlyLocalRunIdsRef.current.delete(runId);
}, 3e4);
recentlyLocalRunIdsRef.current.set(runId, timeout);
}, []);
const rememberRecentlyWakeRunId = (0, react.useCallback)((runId) => {
const existingTimeout = recentlyWakeRunIdsRef.current.get(runId);
if (existingTimeout) clearTimeout(existingTimeout);
const timeout = setTimeout(() => {
recentlyWakeRunIdsRef.current.delete(runId);
}, 3e4);
recentlyWakeRunIdsRef.current.set(runId, timeout);
}, []);
const isLocalActiveRunActivity = (0, react.useCallback)((notification) => {
if (notification.agentId && notification.agentId !== resolvedAgentId) return false;
if (!notification.runId || !activeLocalRunIdsRef.current.has(notification.runId) && !recentlyLocalRunIdsRef.current.has(notification.runId)) return false;
const eventType = notification.eventType.toUpperCase();
return eventType === "RUN_STARTED" || eventType === "RUN_FINISHED" || eventType === "RUN_ERROR";
}, [resolvedAgentId]);
(0, react.useEffect)(() => {
const recentlyLocalRunIds = recentlyLocalRunIdsRef.current;
const recentlyWakeRunIds = recentlyWakeRunIdsRef.current;
return () => {
recentlyLocalRunIds.forEach((timeout) => {
clearTimeout(timeout);
});
recentlyLocalRunIds.clear();
recentlyWakeRunIds.forEach((timeout) => {
clearTimeout(timeout);
});
recentlyWakeRunIds.clear();
};
}, []);
(0, react.useEffect)(() => {
const threadChanged = previousThreadIdRef.current !== resolvedThreadId;
previousThreadIdRef.current = resolvedThreadId;
agent.threadId = resolvedThreadId;
if (!hasExplicitThreadId) {
if (threadChanged && agent.messages.length > 0) agent.setMessages([]);
return;
}
let detached = false;
const connectAbortController = new AbortController();
if (agent instanceof _ag_ui_client.HttpAgent) agent.abortController = connectAbortController;
const connect = async (agentToConnect) => {
activeConnectCountRef.current += 1;
try {
await copilotkit.connectAgent({ agent: agentToConnect });
} catch (error) {
if (detached) return;
console.error("CopilotChat: connectAgent failed", error);
} finally {
if (!detached) (typeof requestAnimationFrame === "function" ? requestAnimationFrame : (cb) => setTimeout(cb, 16))(() => {
if (!detached) setLastConnectedThreadId(resolvedThreadId);
});
else if (!hasExplicitThreadIdRef.current) agentToConnect.setMessages([]);
activeConnectCountRef.current = Math.max(0, activeConnectCountRef.current - 1);
if (!detached && activeConnectCountRef.current === 0) {
const startReconnect = startRunActivityReconnectRef.current;
if (pendingRunActivityReconnectRef.current && startReconnect) {
pendingRunActivityReconnectRef.current = false;
startReconnect(runActivityReconnectGenerationRef.current);
}
}
}
};
connect(agent);
return () => {
detached = true;
connectAbortController.abort();
agent.detachActiveRun().catch(() => {});
};
}, [
resolvedThreadId,
agent,
resolvedAgentId,
hasExplicitThreadId
]);
(0, react.useEffect)(() => {
if (!hasNativeIntelligenceRunActivity) return;
const registeredThreadStore = copilotkit.getThreadStore(resolvedAgentId);
const threadStore = registeredThreadStore ?? standaloneRunActivityStore;
if (!threadStore?.subscribeToRunActivity) return;
const ownsStandaloneStore = registeredThreadStore === void 0;
if (ownsStandaloneStore) {
threadStore.start();
const context = copilotkit.runtimeUrl ? {
runtimeUrl: copilotkit.runtimeUrl,
headers: { ...copilotkit.headers },
wsUrl: copilotkit.intelligence?.wsUrl,
agentId: resolvedAgentId
} : null;
threadStore.setContext(context);
}
const generation = runActivityReconnectGenerationRef.current + 1;
runActivityReconnectGenerationRef.current = generation;
let detached = false;
let wakeReconnectActive = false;
let pendingAgentIdleDrain = null;
const hasActiveAgentRun = () => activeLocalRunIdsRef.current.size > 0 || agent.isRunning;
const scheduleAgentIdleDrain = () => {
if (pendingAgentIdleDrain !== null) return;
pendingAgentIdleDrain = setTimeout(() => {
pendingAgentIdleDrain = null;
if (detached || runActivityReconnectGenerationRef.current !== generation || !pendingRunActivityReconnectRef.current) return;
if (hasActiveAgentRun()) {
scheduleAgentIdleDrain();
return;
}
startRunActivityReconnectRef.current?.(generation);
}, 10);
};
const connect = async () => {
activeConnectCountRef.current += 1;
wakeReconnectActive = true;
const wakeRunId = pendingWakeRunIdRef.current;
pendingWakeRunIdRef.current = void 0;
if (wakeRunId) activeWakeRunIdsRef.current.add(wakeRunId);
let didConnect = false;
try {
await copilotkit.connectAgent({ agent });
didConnect = true;
} catch (error) {
if (!detached) console.error("CopilotChat: run activity reconnect failed", error);
} finally {
if (wakeRunId) {
activeWakeRunIdsRef.current.delete(wakeRunId);
if (didConnect) rememberRecentlyWakeRunId(wakeRunId);
}
activeConnectCountRef.current = Math.max(0, activeConnectCountRef.current - 1);
wakeReconnectActive = false;
if (!detached && runActivityReconnectGenerationRef.current === generation && activeConnectCountRef.current === 0 && pendingRunActivityReconnectRef.current) {
pendingRunActivityReconnectRef.current = false;
connect();
}
}
};
startRunActivityReconnectRef.current = (requestedGeneration) => {
if (detached || requestedGeneration !== generation || runActivityReconnectGenerationRef.current !== generation) return;
if (hasActiveAgentRun()) {
pendingRunActivityReconnectRef.current = true;
scheduleAgentIdleDrain();
return;
}
if (activeConnectCountRef.current > 0) {
if (!wakeReconnectActive) pendingRunActivityReconnectRef.current = true;
return;
}
pendingRunActivityReconnectRef.current = false;
connect();
};
const subscription = threadStore.subscribeToRunActivity((notification) => {
if (notification.threadId !== resolvedThreadId) return;
if (notification.agentId && notification.agentId !== resolvedAgentId) return;
if (isLocalActiveRunActivity(notification)) return;
if (notification.runId && (activeWakeRunIdsRef.current.has(notification.runId) || recentlyWakeRunIdsRef.current.has(notification.runId))) return;
pendingWakeRunIdRef.current = notification.runId;
startRunActivityReconnectRef.current?.(generation);
});
return () => {
detached = true;
pendingRunActivityReconnectRef.current = false;
pendingWakeRunIdRef.current = void 0;
if (pendingAgentIdleDrain !== null) {
clearTimeout(pendingAgentIdleDrain);
pendingAgentIdleDrain = null;
}
if (startRunActivityReconnectRef.current) startRunActivityReconnectRef.current = null;
if (wakeReconnectActive) agent.detachActiveRun().catch(() => {});
activeWakeRunIdsRef.current.clear();
subscription.unsubscribe();
if (ownsStandaloneStore) {
threadStore.setContext(null);
threadStore.stop();
}
};
}, [
agent,
resolvedAgentId,
resolvedThreadId,
hasExplicitThreadId,
hasNativeIntelligenceRunActivity,
copilotkit.runtimeConnectionStatus,
copilotkit.runtimeUrl,
copilotkit.headers,
copilotkit.intelligence?.wsUrl,
copilotkit.threadEndpoints?.realtimeMetadata,
standaloneRunActivityStore,
isLocalActiveRunActivity,
rememberRecentlyWakeRunId
]);
const waitForActiveRunToSettle = (0, react.useCallback)(async () => {
const maybeAware = agent;
const activeRunCompletionPromise = (0, _copilotkit_core.isRunCompletionAware)(maybeAware) ? maybeAware.activeRunCompletionPromise : void 0;
if (agent.isRunning && activeRunCompletionPromise) try {
await activeRunCompletionPromise;
} catch (error) {
console.error("CopilotChat: in-flight run rejected while queuing send", error);
}
}, [agent]);
const onSubmitInput = (0, react.useCallback)(async (value) => {
if (selectedAttachmentsRef.current.some((a) => a.status === "uploading")) {
console.error("[CopilotKit] Cannot send while attachments are uploading (pre-await guard)");
setTranscriptionError("Cannot send while attachments are uploading.");
return;
}
setInputValue("");
await waitForActiveRunToSettle();
if (selectedAttachmentsRef.current.some((a) => a.status === "uploading")) {
console.error("[CopilotKit] Cannot send while attachments are uploading (post-await re-check)");
setTranscriptionError("Cannot send while attachments are uploading.");
setInputValue(value);
return;
}
const readyAttachments = consumeAttachments();
if (readyAttachments.length > 0) {
const contentParts = [];
if (value.trim()) contentParts.push({
type: "text",
text: value
});
for (const att of readyAttachments) contentParts.push({
type: att.type,
source: att.source,
metadata: {
...att.filename ? { filename: att.filename } : {},
...att.metadata
}
});
agent.addMessage({
id: (0, _copilotkit_shared.randomUUID)(),
role: "user",
content: contentParts
});
} else agent.addMessage({
id: (0, _copilotkit_shared.randomUUID)(),
role: "user",
content: value
});
const localRunId = hasNativeIntelligenceRunActivity ? (0, _copilotkit_shared.randomUUID)() : void 0;
if (localRunId) activeLocalRunIdsRef.current.add(localRunId);
try {
await copilotkit.runAgent({
agent,
...localRunId !== void 0 ? { runId: localRunId } : {}
});
} catch (error) {
console.error("CopilotChat: runAgent failed", error);
} finally {
if (localRunId) {
activeLocalRunIdsRef.current.delete(localRunId);
rememberRecentlyLocalRunId(localRunId);
}
if (pendingRunActivityReconnectRef.current && activeLocalRunIdsRef.current.size === 0 && activeConnectCountRef.current === 0) {
const startReconnect = startRunActivityReconnectRef.current;
if (startReconnect) {
pendingRunActivityReconnectRef.current = false;
startReconnect(runActivityReconnectGenerationRef.current);
}
}
}
}, [
agent,
consumeAttachments,
waitForActiveRunToSettle,
hasNativeIntelligenceRunActivity,
rememberRecentlyLocalRunId
]);
const handleSelectSuggestion = (0, react.useCallback)(async (suggestion) => {
await waitForActiveRunToSettle();
agent.addMessage({
id: (0, _copilotkit_shared.randomUUID)(),
role: "user",
content: suggestion.message
});
const localRunId = hasNativeIntelligenceRunActivity ? (0, _copilotkit_shared.randomUUID)() : void 0;
if (localRunId) activeLocalRunIdsRef.current.add(localRunId);
try {
await copilotkit.runAgent({
agent,
...localRunId !== void 0 ? { runId: localRunId } : {}
});
} catch (error) {
console.error("CopilotChat: runAgent failed after selecting suggestion", error);
} finally {
if (localRunId) {
activeLocalRunIdsRef.current.delete(localRunId);
rememberRecentlyLocalRunId(localRunId);
}
if (pendingRunActivityReconnectRef.current && activeLocalRunIdsRef.current.size === 0 && activeConnectCountRef.current === 0) {
const startReconnect = startRunActivityReconnectRef.current;
if (startReconnect) {
pendingRunActivityReconnectRef.current = false;
startReconnect(runActivityReconnectGenerationRef.current);
}
}
}
}, [
agent,
waitForActiveRunToSettle,
hasNativeIntelligenceRunActivity,
rememberRecentlyLocalRunId
]);
const stopCurrentRun = (0, react.useCallback)(() => {
try {
copilotkit.stopAgent({ agent });
} catch (error) {
console.error("CopilotChat: stopAgent failed", error);
try {
agent.abortRun();
} catch (abortError) {
console.error("CopilotChat: abortRun fallback failed", abortError);
}
}
}, [agent]);
const handleStartTranscribe = (0, react.useCallback)(() => {
setTranscriptionError(null);
setTranscribeMode("transcribe");
}, []);
const handleCancelTranscribe = (0, react.useCallback)(() => {
setTranscriptionError(null);
setTranscribeMode("input");
}, []);
const handleFinishTranscribe = (0, react.useCallback)(() => {
setTranscribeMode("input");
}, []);
const handleFinishTranscribeWithAudio = (0, react.useCallback)(async (audioBlob) => {
setIsTranscribing(true);
try {
setTranscriptionError(null);
const result = await transcribeAudio(copilotkit, audioBlob);
setInputValue((prev) => {
const trimmedPrev = prev.trim();
if (trimmedPrev) return `${trimmedPrev} ${result.text}`;
return result.text;
});
} catch (error) {
console.error("CopilotChat: Transcription failed", error);
if (error instanceof TranscriptionError) {
const { code, retryable, message } = error.info;
switch (code) {
case _copilotkit_shared.TranscriptionErrorCode.RATE_LIMITED:
setTranscriptionError("Too many requests. Please wait a moment.");
break;
case _copilotkit_shared.TranscriptionErrorCode.AUTH_FAILED:
setTranscriptionError("Authentication error. Please check your configuration.");
break;
case _copilotkit_shared.TranscriptionErrorCode.AUDIO_TOO_LONG:
setTranscriptionError("Recording is too long. Please try a shorter recording.");
break;
case _copilotkit_shared.TranscriptionErrorCode.AUDIO_TOO_SHORT:
setTranscriptionError("Recording is too short. Please try again.");
break;
case _copilotkit_shared.TranscriptionErrorCode.INVALID_AUDIO_FORMAT:
setTranscriptionError("Audio format not supported.");
break;
case _copilotkit_shared.TranscriptionErrorCode.SERVICE_NOT_CONFIGURED:
setTranscriptionError("Transcription service is not available.");
break;
case _copilotkit_shared.TranscriptionErrorCode.NETWORK_ERROR:
setTranscriptionError("Network error. Please check your connection.");
break;
default: setTranscriptionError(retryable ? "Transcription failed. Please try again." : message);
}
} else setTranscriptionError("Transcription failed. Please try again.");
} finally {
setIsTranscribing(false);
}
}, []);
(0, react.useEffect)(() => {
if (transcriptionError) {
const timer = setTimeout(() => {
setTranscriptionError(null);
}, 5e3);
return () => clearTimeout(timer);
}
}, [transcriptionError]);
const stableMessageView = useShallowStableRef(typeof providedMessageView === "string" ? { className: providedMessageView } : providedMessageView);
const stableSuggestionView = useShallowStableRef(providedSuggestionView);
const handleAddFile = (0, react.useCallback)(() => {
setTimeout(() => {
fileInputRef.current?.click();
}, 100);
}, []);
const mergedProps = {
isRunning: agent.isRunning,
suggestions: isReady ? autoSuggestions : [],
onSelectSuggestion: isReady ? handleSelectSuggestion : void 0,
suggestionView: stableSuggestionView,
...restProps
};
if (stableMessageView !== void 0) mergedProps.messageView = stableMessageView;
const hasMessages = agent.messages.length > 0;
const effectiveStopHandler = agent.isRunning && hasMessages ? providedStopHandler ?? stopCurrentRun : providedStopHandler;
const showTranscription = isTranscriptionEnabled && isMediaRecorderSupported;
const effectiveMode = isTranscribing ? "processing" : transcribeMode;
const messages = (0, react.useMemo)(() => [...agent.messages], [agent.messages.map((m) => {
const contentKey = typeof m.content === "string" ? m.content.length : Array.isArray(m.content) ? m.content.length : m.content && typeof m.content === "object" ? JSON.stringify(m.content) : 0;
const toolCallsKey = "toolCalls" in m && Array.isArray(m.toolCalls) ? m.toolCalls.map((tc) => `${tc.id}:${tc.function?.arguments?.length ?? 0}`).join(";") : "";
return `${m.id}:${m.role}:${contentKey}:${toolCallsKey}`;
}).join(",")]);
const lastUserMessageId = (0, react.useMemo)(() => {
for (let i = messages.length - 1; i >= 0; i--) if (messages[i].role === "user") return messages[i].id;
return null;
}, [messages]);
const [sendNonce, setSendNonce] = (0, react.useState)(0);
const prevLastUserMessageIdRef = (0, react.useRef)(lastUserMessageId);
(0, react.useEffect)(() => {
if (lastUserMessageId && lastUserMessageId !== prevLastUserMessageIdRef.current) {
setSendNonce((n) => n + 1);
prevLastUserMessageIdRef.current = lastUserMessageId;
}
}, [lastUserMessageId]);
const lastUserMessageState = (0, react.useMemo)(() => ({
id: lastUserMessageId,
sendNonce
}), [lastUserMessageId, sendNonce]);
const RenderedChatView = renderSlot(chatView, CopilotChatView, {
...mergedProps,
messages,
onSubmitMessage: isReady ? onSubmitInput : void 0,
onStop: effectiveStopHandler,
inputMode: effectiveMode,
inputValue,
onInputChange: setInputValue,
onStartTranscribe: showTranscription ? handleStartTranscribe : void 0,
onCancelTranscribe: showTranscription ? handleCancelTranscribe : void 0,
onFinishTranscribe: showTranscription ? handleFinishTranscribe : void 0,
onFinishTranscribeWithAudio: showTranscription ? handleFinishTranscribeWithAudio : void 0,
attachments: selectedAttachments,
onRemoveAttachment: removeAttachment,
onAddFile: attachmentsEnabled ? handleAddFile : void 0,
dragOver,
onDragOver: handleDragOver,
onDragLeave: handleDragLeave,
onDrop: handleDrop,
isConnecting,
hasExplicitThreadId
});
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CopilotChatConfigurationProvider, {
agentId: resolvedAgentId,
threadId: resolvedThreadId,
hasExplicitThreadId,
labels,
isModalDefaultOpen,
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
ref: chatContainerRef,
style: { display: "contents" },
children: [
attachmentsEnabled && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
type: "file",
multiple: true,
ref: fileInputRef,
onChange: handleFileUpload,
accept: attachmentsConfig?.accept ?? "*/*",
style: { display: "none" }
}),
!isChatLicensed && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(InlineFeatureWarning, { featureName: "Chat" }),
transcriptionError && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
style: {
position: "absolute",
bottom: "100px",
left: "50%",
transform: "translateX(-50%)",
backgroundColor: "#ef4444",
color: "white",
padding: "8px 16px",
borderRadius: "8px",
fontSize: "14px",
zIndex: 50
},
children: transcriptionError
}),
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(LastUserMessageContext.Provider, {
value: lastUserMessageState,
children: RenderedChatView
})
]
})
});
}
(function(_CopilotChat) {
_CopilotChat.View = CopilotChatView;
})(CopilotChat || (CopilotChat = {}));
//#endregion
//#region src/v2/components/chat/CopilotChatToggleButton.tsx
const DefaultOpenIcon = ({ className, ...props }) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.MessageCircle, {
className: cn("cpk:h-6 cpk:w-6", className),
strokeWidth: 1.75,
fill: "currentColor",
...props
});
const DefaultCloseIcon = ({ className, ...props }) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.X, {
className: cn("cpk:h-6 cpk:w-6", className),
strokeWidth: 1.75,
...props
});
DefaultOpenIcon.displayName = "CopilotChatToggleButton.OpenIcon";
DefaultCloseIcon.displayName = "CopilotChatToggleButton.CloseIcon";
const ICON_TRANSITION_STYLE = Object.freeze({ transition: "opacity 120ms ease-out, transform 260ms cubic-bezier(0.22, 1, 0.36, 1)" });
const ICON_WRAPPER_BASE = "cpk:pointer-events-none cpk:absolute cpk:inset-0 cpk:flex cpk:items-center cpk:justify-center cpk:will-change-transform";
const BUTTON_BASE_CLASSES = cn("copilotKitButton", "cpk:fixed cpk:bottom-6 cpk:right-6 cpk:z-[1100] cpk:flex cpk:h-14 cpk:w-14 cpk:items-center cpk:justify-center", "cpk:rounded-full cpk:border cpk:border-primary cpk:bg-primary cpk:text-primary-foreground", "cpk:shadow-sm cpk:transition-all cpk:duration-200 cpk:ease-out", "cpk:hover:scale-[1.04] cpk:hover:shadow-md", "cpk:cursor-pointer", "cpk:active:scale-[0.96]", "cpk:focus-visible:outline-none cpk:focus-visible:ring-2 cpk:focus-visible:ring-primary/50 cpk:focus-visible:ring-offset-2 cpk:focus-visible:ring-offset-background", "cpk:disabled:pointer-events-none cpk:disabled:opacity-60");
const CopilotChatToggleButton = react.default.forwardRef(function CopilotChatToggleButton({ openIcon, closeIcon, className, ...buttonProps }, ref) {
const { onClick, type, disabled, ...restProps } = buttonProps;
const configuration = useCopilotChatConfiguration();
const labels = configuration?.labels ?? CopilotChatDefaultLabels;
const [fallbackOpen, setFallbackOpen] = (0, react.useState)(false);
const isOpen = configuration?.isModalOpen ?? fallbackOpen;
const setModalOpen = configuration?.setModalOpen ?? setFallbackOpen;
const handleClick = (event) => {
if (disabled) return;
if (onClick) onClick(event);
if (event.defaultPrevented) return;
setModalOpen(!isOpen);
};
const renderedOpenIcon = renderSlot(openIcon, DefaultOpenIcon, {
className: "cpk:h-6 cpk:w-6",
"aria-hidden": true,
focusable: false
});
const renderedCloseIcon = renderSlot(closeIcon, DefaultCloseIcon, {
className: "cpk:h-6 cpk:w-6",
"aria-hidden": true,
focusable: false
});
const openIconElement = /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
"aria-hidden": "true",
"data-slot": "chat-toggle-button-open-icon",
className: ICON_WRAPPER_BASE,
style: {
...ICON_TRANSITION_STYLE,
opacity: isOpen ? 0 : 1,
transform: `scale(${isOpen ? .75 : 1}) rotate(${isOpen ? 90 : 0}deg)`
},
children: renderedOpenIcon
});
const closeIconElement = /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
"aria-hidden": "true",
"data-slot": "chat-toggle-button-close-icon",
className: ICON_WRAPPER_BASE,
style: {
...ICON_TRANSITION_STYLE,
opacity: isOpen ? 1 : 0,
transform: `scale(${isOpen ? 1 : .75}) rotate(${isOpen ? 0 : -90}deg)`
},
children: renderedCloseIcon
});
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
ref,
type: type ?? "button",
"data-copilotkit": true,
"data-testid": "copilot-chat-toggle",
"data-slot": "chat-toggle-button",
"data-state": isOpen ? "open" : "closed",
className: cn(BUTTON_BASE_CLASSES, className),
"aria-label": isOpen ? labels.chatToggleCloseLabel : labels.chatToggleOpenLabel,
"aria-pressed": isOpen,
disabled,
onClick: handleClick,
...restProps,
children: [openIconElement, closeIconElement]
});
});
CopilotChatToggleButton.displayName = "CopilotChatToggleButton";
//#endregion
//#region src/v2/components/chat/CopilotModalHeader.tsx
/**
* Reactively tracks whether the viewport is in the mobile range (≤767px) — the
* same breakpoint the drawer + chat coordination use. SSR-safe: starts `false`
* (desktop) so the server render and first client render agree, then syncs on
* mount and on resize.
*/
function useIsMobileViewport() {
const [isMobile, setIsMobile] = (0, react.useState)(false);
(0, react.useEffect)(() => {
if (typeof window === "undefined" || typeof window.matchMedia !== "function") return;
const mql = window.matchMedia("(max-width: 767px)");
const update = () => setIsMobile(mql.matches);
update();
mql.addEventListener("change", update);
return () => mql.removeEventListener("change", update);
}, []);
return isMobile;
}
function CopilotModalHeader({ title, titleContent, closeButton, drawerLauncher, children, className, ...rest }) {
const configuration = useCopilotChatConfiguration();
const fallbackTitle = configuration?.labels.modalHeaderTitle ?? CopilotChatDefaultLabels.modalHeaderTitle;
const resolvedTitle = title ?? fallbackTitle;
const isMobile = useIsMobileViewport();
const drawerRegistered = (configuration?.drawerRegistered ?? false) && isMobile;
const handleClose = (0, react.useCallback)(() => {
configuration?.setModalOpen?.(false);
}, [configuration]);
const handleToggleDrawer = (0, react.useCallback)(() => {
configuration?.setDrawerOpen?.(!configuration.drawerOpen);
}, [configuration]);
const BoundTitle = renderSlot(titleContent, CopilotModalHeader.Title, { children: resolvedTitle });
const BoundCloseButton = renderSlot(closeButton, CopilotModalHeader.CloseButton, { onClick: handleClose });
const BoundDrawerLauncher = drawerRegistered ? renderSlot(drawerLauncher, CopilotModalHeader.DrawerLauncher, {
onClick: handleToggleDrawer,
"aria-expanded": configuration?.drawerOpen ?? false
}) : null;
if (children) return children({
titleContent: BoundTitle,
closeButton: BoundCloseButton,
drawerLauncher: BoundDrawerLauncher,
title: resolvedTitle,
...rest
});
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("header", {
"data-testid": "copilot-modal-header",
"data-slot": "copilot-modal-header",
className: cn("copilotKitHeader", "cpk:flex cpk:items-center cpk:justify-between cpk:border-b cpk:border-border cpk:px-4 cpk:py-4", "cpk:bg-background/95 cpk:backdrop-blur cpk:supports-[backdrop-filter]:bg-background/80", className),
...rest,
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
className: "cpk:flex cpk:w-full cpk:items-center cpk:gap-2",
children: [
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
className: "cpk:flex cpk:flex-1 cpk:justify-start",
children: BoundDrawerLauncher ?? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { "aria-hidden": "true" })
}),
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
className: "cpk:flex cpk:flex-1 cpk:justify-center cpk:text-center",
children: BoundTitle
}),
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
className: "cpk:flex cpk:flex-1 cpk:justify-end",
children: BoundCloseButton
})
]
})
});
}
CopilotModalHeader.displayName = "CopilotModalHeader";
(function(_CopilotModalHeader) {
_CopilotModalHeader.Title = ({ children, className, ...props }) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
"data-testid": "copilot-header-title",
className: cn("cpk:w-full cpk:text-base cpk:font-medium cpk:leading-none cpk:tracking-tight cpk:text-foreground", className),
...props,
children
});
_CopilotModalHeader.CloseButton = ({ className, ...props }) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
type: "button",
"data-testid": "copilot-close-button",
className: cn("cpk:inline-flex cpk:size-8 cpk:items-center cpk:justify-center cpk:rounded-full cpk:text-muted-foreground cpk:transition cpk:cursor-pointer", "cpk:hover:bg-muted cpk:hover:text-foreground cpk:focus-visible:outline-none cpk:focus-visible:ring-2 cpk:focus-visible:ring-ring", className),
"aria-label": "Close",
...props,
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.X, {
className: "cpk:h-4 cpk:w-4",
"aria-hidden": "true"
})
});
_CopilotModalHeader.DrawerLauncher = ({ className, ...props }) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
type: "button",
"data-testid": "copilot-threads-drawer-launcher",
className: cn("cpk:inline-flex cpk:size-8 cpk:items-center cpk:justify-center cpk:rounded-full cpk:text-muted-foreground cpk:transition cpk:cursor-pointer", "cpk:hover:bg-muted cpk:hover:text-foreground cpk:focus-visible:outline-none cpk:focus-visible:ring-2 cpk:focus-visible:ring-ring", className),
"aria-label": "Open threads",
...props,
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.PanelLeftOpen, {
className: "cpk:h-4 cpk:w-4",
"aria-hidden": "true"
})
});
})(CopilotModalHeader || (CopilotModalHeader = {}));
CopilotModalHeader.Title.displayName = "CopilotModalHeader.Title";
CopilotModalHeader.CloseButton.displayName = "CopilotModalHeader.CloseButton";
CopilotModalHeader.DrawerLauncher.displayName = "CopilotModalHeader.DrawerLauncher";
//#endregion
//#region src/v2/components/chat/CopilotSidebarView.tsx
const DEFAULT_SIDEBAR_WIDTH = 480;
const SIDEBAR_TRANSITION_MS = 260;
function CopilotSidebarView({ header, toggleButton, width, defaultOpen = true, position = "right", ...props }) {
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CopilotChatConfigurationProvider, {
isModalDefaultOpen: defaultOpen,
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CopilotSidebarViewInternal, {
header,
toggleButton,
width,
position,
...props
})
});
}
function CopilotSidebarViewInternal({ header, toggleButton, width, position = "right", ...props }) {
const isSidebarOpen = useCopilotChatConfiguration()?.isModalOpen ?? false;
const sidebarRef = (0, react.useRef)(null);
const [sidebarWidth, setSidebarWidth] = (0, react.useState)(width ?? DEFAULT_SIDEBAR_WIDTH);
const widthToCss = (w) => {
return typeof w === "number" ? `${w}px` : w;
};
const widthToMargin = (w) => {
if (typeof w === "number") return `${w}px`;
return w;
};
(0, react.useEffect)(() => {
if (width !== void 0) return;
if (typeof window === "undefined") return;
const element = sidebarRef.current;
if (!element) return;
const updateWidth = () => {
const rect = element.getBoundingClientRect();
if (rect.width > 0) setSidebarWidth(rect.width);
};
updateWidth();
if (typeof ResizeObserver !== "undefined") {
const observer = new ResizeObserver(() => updateWidth());
observer.observe(element);
return () => observer.disconnect();
}
window.addEventListener("resize", updateWidth);
return () => window.removeEventListener("resize", updateWidth);
}, [width]);
const hasMounted = (0, react.useRef)(false);
(0, react.useLayoutEffect)(() => {
if (typeof window === "undefined" || typeof window.matchMedia !== "function") return;
if (!window.matchMedia("(min-width: 768px)").matches) return;
const marginStyleProp = position === "left" ? "marginInlineStart" : "marginInlineEnd";
const transitionCssProp = position === "left" ? "margin-inline-start" : "margin-inline-end";
if (isSidebarOpen) {
if (hasMounted.current) document.body.style.transition = `${transitionCssProp} ${SIDEBAR_TRANSITION_MS}ms ease`;
document.body.style[marginStyleProp] = widthToMargin(sidebarWidth);
} else if (hasMounted.current) {
document.body.style.transition = `${transitionCssProp} ${SIDEBAR_TRANSITION_MS}ms ease`;
document.body.style[marginStyleProp] = "";
}
hasMounted.current = true;
return () => {
document.body.style[marginStyleProp] = "";
document.body.style.transition = "";
};
}, [
isSidebarOpen,
sidebarWidth,
position
]);
const headerElement = renderSlot(header, CopilotModalHeader, {});
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [renderSlot(toggleButton, CopilotChatToggleButton, position === "left" ? { className: "cpk:left-6 cpk:right-auto" } : {}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("aside", {
ref: sidebarRef,
"data-copilotkit": true,
"data-testid": "copilot-sidebar",
"data-copilot-sidebar": true,
"data-position": position,
className: cn("copilotKitSidebar copilotKitWindow", "cpk:fixed cpk:top-0 cpk:z-[1200] cpk:flex", position === "left" ? "cpk:left-0" : "cpk:right-0", "cpk:h-[100vh] cpk:h-[100dvh] cpk:max-h-screen", "cpk:w-full", position === "left" ? "cpk:border-r" : "cpk:border-l", "cpk:border-border cpk:bg-background cpk:text-foreground cpk:shadow-xl", "cpk:transition-transform cpk:duration-300 cpk:ease-out", isSidebarOpen ? "cpk:translate-x-0" : position === "left" ? "cpk:-translate-x-full cpk:pointer-events-none" : "cpk:translate-x-full cpk:pointer-events-none"),
style: {
["--sidebar-width"]: widthToCss(sidebarWidth),
paddingTop: "env(safe-area-inset-top)",
paddingBottom: "env(safe-area-inset-bottom)"
},
"aria-hidden": !isSidebarOpen,
"aria-label": "Copilot chat sidebar",
role: "complementary",
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
className: "cpk:flex cpk:h-full cpk:w-full cpk:flex-col cpk:overflow-hidden",
children: [headerElement, /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
className: "cpk:flex-1 cpk:overflow-hidden",
"data-sidebar-chat": true,
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CopilotChatView_default, { ...props })
})]
})
})] });
}
CopilotSidebarView.displayName = "CopilotSidebarView";
(function(_CopilotSidebarView) {
_CopilotSidebarView.WelcomeScreen = ({ welcomeMessage, input, suggestionView, className, children, ...props }) => {
const BoundWelcomeMessage = renderSlot(welcomeMessage, CopilotChatView_default.WelcomeMessage, {});
if (children) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
"data-copilotkit": true,
style: { display: "contents" },
children: children({
welcomeMessage: BoundWelcomeMessage,
input,
suggestionView,
className,
...props
})
});
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
className: cn("cpk:h-full cpk:flex cpk:flex-col", className),
...props,
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
className: "cpk:flex-1 cpk:flex cpk:flex-col cpk:items-center cpk:justify-center cpk:px-4",
children: BoundWelcomeMessage
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
className: "cpk:px-8 cpk:pb-4",
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
className: "cpk:max-w-3xl cpk:mx-auto",
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
className: "cpk:mb-4 cpk:flex cpk:justify-center",
children: suggestionView
}), input]
})
})]
});
};
})(CopilotSidebarView || (CopilotSidebarView = {}));
//#endregion
//#region src/v2/components/chat/CopilotPopupView.tsx
const DEFAULT_POPUP_WIDTH = 420;
const DEFAULT_POPUP_HEIGHT = 560;
const dimensionToCss = (value, fallback) => {
if (typeof value === "number" && Number.isFinite(value)) return `${value}px`;
if (typeof value === "string" && value.trim().length > 0) return value;
return `${fallback}px`;
};
function CopilotPopupView({ header, toggleButton, width, height, clickOutsideToClose, defaultOpen = true, className, ...restProps }) {
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CopilotChatConfigurationProvider, {
isModalDefaultOpen: defaultOpen,
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CopilotPopupViewInternal, {
header,
toggleButton,
width,
height,
clickOutsideToClose,
className,
...restProps
})
});
}
function CopilotPopupViewInternal({ header, toggleButton, width, height, clickOutsideToClose, className, ...restProps }) {
const configuration = useCopilotChatConfiguration();
const isPopupOpen = configuration?.isModalOpen ?? false;
const setModalOpen = configuration?.setModalOpen;
const labels = configuration?.labels ?? CopilotChatDefaultLabels;
const containerRef = (0, react.useRef)(null);
const [isRendered, setIsRendered] = (0, react.useState)(isPopupOpen);
const [isAnimatingOut, setIsAnimatingOut] = (0, react.useState)(false);
(0, react.useEffect)(() => {
if (isPopupOpen) {
setIsRendered(true);
setIsAnimatingOut(false);
return;
}
if (!isRendered) return;
setIsAnimatingOut(true);
const timeout = setTimeout(() => {
setIsRendered(false);
setIsAnimatingOut(false);
}, 200);
return () => clearTimeout(timeout);
}, [isPopupOpen, isRendered]);
(0, react.useEffect)(() => {
if (!isPopupOpen) return;
if (typeof window === "undefined") return;
const handleKeyDown = (event) => {
if (event.key === "Escape") {
event.preventDefault();
setModalOpen?.(false);
}
};
window.addEventListener("keydown", handleKeyDown);
return () => window.removeEventListener("keydown", handleKeyDown);
}, [isPopupOpen, setModalOpen]);
(0, react.useEffect)(() => {
if (!isPopupOpen) return;
const focusTimer = setTimeout(() => {
const container = containerRef.current;
if (container && !container.contains(document.activeElement)) container.focus({ preventScroll: true });
}, 200);
return () => clearTimeout(focusTimer);
}, [isPopupOpen]);
(0, react.useEffect)(() => {
if (!isPopupOpen || !clickOutsideToClose) return;
if (typeof document === "undefined") return;
const handlePointerDown = (event) => {
const target = event.target;
if (!target) return;
if (containerRef.current?.contains(target)) return;
const toggleButton = document.querySelector("[data-slot='chat-toggle-button']");
if (toggleButton && toggleButton.contains(target)) return;
setModalOpen?.(false);
};
document.addEventListener("pointerdown", handlePointerDown);
return () => document.removeEventListener("pointerdown", handlePointerDown);
}, [
isPopupOpen,
clickOutsideToClose,
setModalOpen
]);
const headerElement = (0, react.useMemo)(() => renderSlot(header, CopilotModalHeader, {}), [header]);
const toggleButtonElement = (0, react.useMemo)(() => renderSlot(toggleButton, CopilotChatToggleButton, {}), [toggleButton]);
const resolvedWidth = dimensionToCss(width, DEFAULT_POPUP_WIDTH);
const resolvedHeight = dimensionToCss(height, DEFAULT_POPUP_HEIGHT);
const popupStyle = (0, react.useMemo)(() => ({
"--copilot-popup-width": resolvedWidth,
"--copilot-popup-height": resolvedHeight,
"--copilot-popup-max-width": "calc(100vw - 3rem)",
"--copilot-popup-max-height": "calc(100dvh - 7.5rem)",
paddingTop: "env(safe-area-inset-top)",
paddingBottom: "env(safe-area-inset-bottom)",
paddingLeft: "env(safe-area-inset-left)",
paddingRight: "env(safe-area-inset-right)"
}), [resolvedHeight, resolvedWidth]);
const popupAnimationClass = isPopupOpen && !isAnimatingOut ? "cpk:pointer-events-auto cpk:translate-y-0 cpk:opacity-100 cpk:md:scale-100" : "cpk:pointer-events-none cpk:translate-y-4 cpk:opacity-0 cpk:md:translate-y-5 cpk:md:scale-[0.95]";
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [toggleButtonElement, isRendered ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
"data-copilotkit": true,
className: cn("cpk:fixed cpk:inset-0 cpk:z-[1200] cpk:flex cpk:max-w-full cpk:flex-col cpk:items-stretch", "cpk:md:inset-auto cpk:md:bottom-24 cpk:md:right-6 cpk:md:items-end cpk:md:gap-4"),
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
ref: containerRef,
tabIndex: -1,
role: "dialog",
"aria-label": labels.modalHeaderTitle,
"data-testid": "copilot-popup",
"data-copilot-popup": true,
className: cn("copilotKitPopup copilotKitWindow", "cpk:relative cpk:flex cpk:h-full cpk:w-full cpk:flex-col cpk:overflow-hidden cpk:bg-background cpk:text-foreground", "cpk:origin-bottom cpk:focus:outline-none cpk:transform-gpu cpk:transition-transform cpk:transition-opacity cpk:duration-200 cpk:ease-out", "cpk:md:transition-transform cpk:md:transition-opacity", "cpk:rounded-none cpk:border cpk:border-border/0 cpk:shadow-none cpk:ring-0", "cpk:md:h-[var(--copilot-popup-height)] cpk:md:w-[var(--copilot-popup-width)]", "cpk:md:max-h-[var(--copilot-popup-max-height)] cpk:md:max-w-[var(--copilot-popup-max-width)]", "cpk:md:origin-bottom-right cpk:md:rounded-2xl cpk:md:border-border cpk:md:shadow-xl cpk:md:ring-1 cpk:md:ring-border/40", popupAnimationClass),
style: popupStyle,
children: [headerElement, /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
className: "cpk:flex-1 cpk:overflow-hidden",
"data-popup-chat": true,
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CopilotChatView_default, {
...restProps,
className: cn("cpk:h-full cpk:min-h-0", className)
})
})]
})
}) : null] });
}
CopilotPopupView.displayName = "CopilotPopupView";
(function(_CopilotPopupView) {
_CopilotPopupView.WelcomeScreen = ({ welcomeMessage, input, suggestionView, className, children, ...props }) => {
const BoundWelcomeMessage = renderSlot(welcomeMessage, CopilotChatView_default.WelcomeMessage, {});
if (children) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
"data-copilotkit": true,
style: { display: "contents" },
children: children({
welcomeMessage: BoundWelcomeMessage,
input,
suggestionView,
className,
...props
})
});
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
className: cn("cpk:h-full cpk:flex cpk:flex-col", className),
...props,
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
className: "cpk:flex-1 cpk:flex cpk:flex-col cpk:items-center cpk:justify-center cpk:px-4",
children: BoundWelcomeMessage
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
className: "cpk:mb-4 cpk:flex cpk:justify-center cpk:px-4",
children: suggestionView
}), input] })]
});
};
})(CopilotPopupView || (CopilotPopupView = {}));
var CopilotPopupView_default = CopilotPopupView;
//#endregion
//#region src/v2/components/chat/CopilotSidebar.tsx
function CopilotSidebar({ header, toggleButton, defaultOpen, width, position, ...chatProps }) {
const { checkFeature } = (0, _copilotkit_react_core_v2_context.useLicenseContext)();
const isSidebarLicensed = checkFeature("sidebar");
(0, react.useEffect)(() => {
if (!isSidebarLicensed) console.warn("[CopilotKit] Warning: \"sidebar\" feature is not licensed. Visit copilotkit.ai/pricing");
}, [isSidebarLicensed]);
const SidebarViewOverride = (0, react.useMemo)(() => {
const Component = (viewProps) => {
const { header: viewHeader, toggleButton: viewToggleButton, width: viewWidth, defaultOpen: viewDefaultOpen, position: viewPosition, ...restProps } = viewProps;
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CopilotSidebarView, {
...restProps,
header: header ?? viewHeader,
toggleButton: toggleButton ?? viewToggleButton,
width: width ?? viewWidth,
defaultOpen: defaultOpen ?? viewDefaultOpen,
position: position ?? viewPosition
});
};
return Object.assign(Component, CopilotChatView_default);
}, [
header,
toggleButton,
width,
defaultOpen,
position
]);
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [!isSidebarLicensed && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(InlineFeatureWarning, { featureName: "Sidebar" }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CopilotChat, {
welcomeScreen: CopilotSidebarView.WelcomeScreen,
...chatProps,
isModalDefaultOpen: defaultOpen,
chatView: SidebarViewOverride
})] });
}
CopilotSidebar.displayName = "CopilotSidebar";
//#endregion
//#region src/v2/components/chat/CopilotPopup.tsx
const PopupShellPropsContext = react.default.createContext({});
const PopupViewOverride = (viewProps) => {
const { header: viewHeader, toggleButton: viewToggleButton, width: viewWidth, height: viewHeight, clickOutsideToClose: viewClickOutsideToClose, defaultOpen: viewDefaultOpen, ...restProps } = viewProps;
const shell = (0, react.useContext)(PopupShellPropsContext);
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CopilotPopupView_default, {
...restProps,
header: shell.header ?? viewHeader,
toggleButton: shell.toggleButton ?? viewToggleButton,
width: shell.width ?? viewWidth,
height: shell.height ?? viewHeight,
clickOutsideToClose: shell.clickOutsideToClose ?? viewClickOutsideToClose,
defaultOpen: shell.defaultOpen ?? viewDefaultOpen
});
};
const PopupViewOverrideWithStatics = Object.assign(PopupViewOverride, CopilotChatView_default);
function CopilotPopup({ header, toggleButton, defaultOpen, width, height, clickOutsideToClose, ...chatProps }) {
const { checkFeature } = (0, _copilotkit_react_core_v2_context.useLicenseContext)();
const isPopupLicensed = checkFeature("popup");
(0, react.useEffect)(() => {
if (!isPopupLicensed) console.warn("[CopilotKit] Warning: \"popup\" feature is not licensed. Visit copilotkit.ai/pricing");
}, [isPopupLicensed]);
const shellProps = (0, react.useMemo)(() => ({
header,
toggleButton,
width,
height,
clickOutsideToClose,
defaultOpen
}), [
clickOutsideToClose,
header,
toggleButton,
height,
width,
defaultOpen
]);
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [!isPopupLicensed && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(InlineFeatureWarning, { featureName: "Popup" }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PopupShellPropsContext.Provider, {
value: shellProps,
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CopilotChat, {
welcomeScreen: CopilotPopupView_default.WelcomeScreen,
...chatProps,
isModalDefaultOpen: defaultOpen,
chatView: PopupViewOverrideWithStatics
})
})] });
}
CopilotPopup.displayName = "CopilotPopup";
//#endregion
//#region src/v2/components/chat/CopilotThreadsDrawer.tsx
/**
* Maps a {@link Thread} from {@link useThreads} to the element's
* {@link DrawerThread} view shape. The shapes are structurally compatible; this
* narrows to exactly the fields the element renders so the element never sees
* platform-internal fields.
*/
function toDrawerThread(thread) {
return {
id: thread.id,
name: thread.name,
archived: thread.archived,
createdAt: thread.createdAt,
updatedAt: thread.updatedAt,
...thread.lastRunAt !== void 0 ? { lastRunAt: thread.lastRunAt } : {}
};
}
/** The chat input textarea's documented `data-testid`. */
const CHAT_INPUT_TESTID = "copilot-chat-textarea";
/** The chat view container's documented `data-testid`. */
const CHAT_CONTAINER_TESTID = "copilot-chat";
/**
* Returns the chat input element for focus-return after a thread is selected.
*
* Best-effort and SCOPED: walks up from the drawer element looking for an
* ancestor that contains a chat-view container (`data-testid="copilot-chat"`),
* then returns the chat input within that subtree. This avoids focusing the
* wrong composer on a page hosting more than one chat (multi-chat dashboards),
* where a document-global lookup would grab whichever input appears first in
* DOM order rather than the one this drawer drives.
*
* Falls back to a document-global lookup when no scoping ancestor is found
* (e.g. the drawer and chat share no common container, or headless usage),
* and returns `null` when there is no chat input at all.
*
* @param origin - The drawer element to scope the search from.
*/
function findChatInput(origin) {
if (typeof document === "undefined") return null;
const container = origin?.closest?.(`[data-testid="${CHAT_CONTAINER_TESTID}"]`);
if (container) {
const scoped = container.querySelector(`[data-testid="${CHAT_INPUT_TESTID}"]`);
if (scoped) return scoped;
}
return document.querySelector(`[data-testid="${CHAT_INPUT_TESTID}"]`);
}
/**
* React wrapper for the shadow-DOM `<copilotkit-threads-drawer>` threads drawer.
*
* Responsibilities:
* - Registers the custom element on the client (SSR-safe; nothing renders
* during prerender to avoid hydration mismatch).
* - Feeds the element domain data: `threads`, `loading`, `error`,
* `activeThreadId`, `licensed`, fetch-more state.
* - Routes the element's outbound events to core thread operations
* ({@link useThreads}) and chat-configuration changes.
* - Registers with the surrounding chat configuration so the header
* thread-list launcher appears, and binds the element `open` state to the
* configuration's `drawerOpen`.
*
* License gating is two-pronged: the locked view shows when no license is configured
* (the runtime reported no license status) OR the `threads` feature is
* explicitly unlicensed. While unlicensed, the thread fetch is skipped entirely
* so an unlicensed drawer issues no network requests.
*
* Thread switching needs no host wiring: when `onThreadSelect`/`onNewThread`
* are omitted, the wrapper drives the surrounding chat configuration directly
* ({@link CopilotChatConfigurationValue.setActiveThreadId} /
* {@link CopilotChatConfigurationValue.startNewThread}), so a bare drawer
* connects to the picked thread and shows the welcome screen on "+ New". Pass
* the callbacks only to take control yourself.
*
* @example
* ```tsx
* // Callback-free: the drawer drives the chat configuration itself.
* <CopilotKitProvider runtimeUrl="/api/copilotkit" publicLicenseKey="ck_pub_...">
* <CopilotChat />
* <CopilotThreadsDrawer />
* </CopilotKitProvider>
* ```
*/
function CopilotThreadsDrawer({ agentId, onThreadSelect, onNewThread, onLicensed, licenseUrl, renderRow, label, recentLabel, collapsible, onCollapseChange, limit, "data-testid": dataTestId = "copilot-threads-drawer" }) {
const configuration = useCopilotChatConfiguration();
const { status, checkFeature } = (0, _copilotkit_react_core_v2_context.useLicenseContext)();
const licensePresent = status === "valid" || status === "expiring";
const featureLicensed = checkFeature("threads");
const licensed = licensePresent && featureLicensed;
const licensePending = status === null;
const resolvedAgentId = agentId ?? configuration?.agentId ?? _copilotkit_shared.DEFAULT_AGENT_ID;
const activeThreadId = configuration?.threadId ?? null;
const { threads, isLoading, listError, fetchMoreError, hasMoreThreads, isFetchingMoreThreads, archiveThread, unarchiveThread, deleteThread, fetchMoreThreads, refetchThreads, startNewThread } = useThreads$1({
agentId: resolvedAgentId,
includeArchived: true,
enabled: licensed,
...limit !== void 0 ? { limit } : {}
});
const drawerThreads = (0, react.useMemo)(() => threads.map(toDrawerThread), [threads]);
const elementRef = (0, react.useRef)(null);
const [mounted, setMounted] = (0, react.useState)(false);
(0, react.useEffect)(() => {
(0, _copilotkit_web_components_threads_drawer.defineCopilotKitThreadsDrawer)();
setMounted(true);
}, []);
const registerDrawer = configuration?.registerDrawer;
(0, react.useEffect)(() => {
if (!registerDrawer) return;
return registerDrawer();
}, [registerDrawer]);
const [localDrawerOpen, setLocalDrawerOpen] = (0, react.useState)(false);
const drawerOpen = configuration ? configuration.drawerOpen : localDrawerOpen;
const setDrawerOpen = configuration ? configuration.setDrawerOpen : setLocalDrawerOpen;
const setActiveThreadId = configuration?.setActiveThreadId;
const startNewThreadConfig = configuration?.startNewThread;
const handleThreadSelected = (0, react.useCallback)((threadId) => {
if (onThreadSelect) onThreadSelect(threadId);
else setActiveThreadId?.(threadId, { explicit: true });
findChatInput(elementRef.current)?.focus();
}, [onThreadSelect, setActiveThreadId]);
const handleNewThread = (0, react.useCallback)(() => {
startNewThread();
if (onNewThread) onNewThread();
else startNewThreadConfig?.();
}, [
startNewThread,
onNewThread,
startNewThreadConfig
]);
const handleArchive = (0, react.useCallback)((threadId) => {
archiveThread(threadId).catch((err) => {
console.error("CopilotThreadsDrawer: archiveThread failed", err);
});
}, [archiveThread]);
const handleUnarchive = (0, react.useCallback)((threadId) => {
unarchiveThread(threadId).catch((err) => {
console.error("CopilotThreadsDrawer: unarchiveThread failed", err);
});
}, [unarchiveThread]);
const handleDelete = (0, react.useCallback)((threadId) => {
const isActive = threadId === activeThreadId;
deleteThread(threadId).then(() => {
if (isActive) {
startNewThread();
if (onNewThread) onNewThread();
else startNewThreadConfig?.();
}
}).catch((err) => {
console.error("CopilotThreadsDrawer: deleteThread failed", err);
});
}, [
deleteThread,
activeThreadId,
startNewThread,
onNewThread,
startNewThreadConfig
]);
const handleFilterChange = (0, react.useCallback)(() => {
refetchThreads();
}, [refetchThreads]);
const handleRetry = (0, react.useCallback)((scope) => {
if (scope === "fetch-more") fetchMoreThreads();
else refetchThreads();
}, [fetchMoreThreads, refetchThreads]);
const handleOpenChange = (0, react.useCallback)((open) => {
setDrawerOpen(open);
}, [setDrawerOpen]);
const handleLicensed = (0, react.useCallback)(() => {
onLicensed?.();
}, [onLicensed]);
const handleLoadMore = (0, react.useCallback)(() => {
fetchMoreThreads();
}, [fetchMoreThreads]);
const handleCollapseChange = (0, react.useCallback)((collapsed) => {
onCollapseChange?.(collapsed);
}, [onCollapseChange]);
const handlersRef = (0, react.useRef)({
handleThreadSelected,
handleNewThread,
handleArchive,
handleUnarchive,
handleDelete,
handleFilterChange,
handleRetry,
handleOpenChange,
handleLicensed,
handleLoadMore,
handleCollapseChange
});
handlersRef.current = {
handleThreadSelected,
handleNewThread,
handleArchive,
handleUnarchive,
handleDelete,
handleFilterChange,
handleRetry,
handleOpenChange,
handleLicensed,
handleLoadMore,
handleCollapseChange
};
(0, react.useEffect)(() => {
const el = elementRef.current;
if (!el) return;
const onThreadSelected = (event) => {
const detail = event.detail;
handlersRef.current.handleThreadSelected(detail.threadId);
};
const onNewThreadEvent = () => handlersRef.current.handleNewThread();
const onArchive = (event) => {
const detail = event.detail;
handlersRef.current.handleArchive(detail.threadId);
};
const onUnarchive = (event) => {
const detail = event.detail;
handlersRef.current.handleUnarchive(detail.threadId);
};
const onDelete = (event) => {
const detail = event.detail;
handlersRef.current.handleDelete(detail.threadId);
};
const onFilterChange = (_event) => {
handlersRef.current.handleFilterChange();
};
const onOpenChangeEvent = (event) => {
const detail = event.detail;
handlersRef.current.handleOpenChange(detail.open);
};
const onRetry = (event) => {
const detail = event.detail;
handlersRef.current.handleRetry(detail.scope);
};
const onLicensedEvent = () => handlersRef.current.handleLicensed();
const onLoadMore = () => handlersRef.current.handleLoadMore();
const onCollapseChangeEvent = (event) => {
const detail = event.detail;
handlersRef.current.handleCollapseChange(detail.collapsed);
};
el.addEventListener("thread-selected", onThreadSelected);
el.addEventListener("new-thread", onNewThreadEvent);
el.addEventListener("archive", onArchive);
el.addEventListener("unarchive", onUnarchive);
el.addEventListener("delete", onDelete);
el.addEventListener("filter-change", onFilterChange);
el.addEventListener("open-change", onOpenChangeEvent);
el.addEventListener("retry", onRetry);
el.addEventListener("licensed", onLicensedEvent);
el.addEventListener("load-more", onLoadMore);
el.addEventListener("collapse-change", onCollapseChangeEvent);
return () => {
el.removeEventListener("thread-selected", onThreadSelected);
el.removeEventListener("new-thread", onNewThreadEvent);
el.removeEventListener("archive", onArchive);
el.removeEventListener("unarchive", onUnarchive);
el.removeEventListener("delete", onDelete);
el.removeEventListener("filter-change", onFilterChange);
el.removeEventListener("open-change", onOpenChangeEvent);
el.removeEventListener("retry", onRetry);
el.removeEventListener("licensed", onLicensedEvent);
el.removeEventListener("load-more", onLoadMore);
el.removeEventListener("collapse-change", onCollapseChangeEvent);
};
}, [mounted]);
(0, react.useEffect)(() => {
const el = elementRef.current;
if (!el) return;
el.threads = drawerThreads;
}, [drawerThreads, mounted]);
(0, react.useEffect)(() => {
const el = elementRef.current;
if (!el) return;
el.loading = isLoading || licensePending;
el.error = listError ? listError.message : null;
el.activeThreadId = activeThreadId;
el.licensed = licensed || licensePending;
el.hasMore = hasMoreThreads;
el.fetchingMore = isFetchingMoreThreads;
el.fetchMoreError = fetchMoreError ? fetchMoreError.message : null;
}, [
isLoading,
listError,
fetchMoreError,
activeThreadId,
licensed,
licensePending,
hasMoreThreads,
isFetchingMoreThreads,
mounted
]);
(0, react.useEffect)(() => {
const el = elementRef.current;
if (!el) return;
el.open = drawerOpen;
}, [drawerOpen, mounted]);
(0, react.useEffect)(() => {
const el = elementRef.current;
if (!el) return;
if (label !== void 0) el.label = label;
}, [label, mounted]);
(0, react.useEffect)(() => {
const el = elementRef.current;
if (!el) return;
if (licenseUrl !== void 0) el.licenseUrl = licenseUrl;
}, [licenseUrl, mounted]);
(0, react.useEffect)(() => {
const el = elementRef.current;
if (!el) return;
if (collapsible !== void 0) el.collapsible = collapsible;
}, [collapsible, mounted]);
const rowChildren = (0, react.useMemo)(() => {
if (!renderRow) return null;
return drawerThreads.map((drawerThread) => {
const fullThread = threads.find((t) => t.id === drawerThread.id);
if (!fullThread) return null;
const content = renderRow(fullThread);
if (content === null || content === void 0) return null;
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
slot: `row:${drawerThread.id}`,
children: content
}, drawerThread.id);
});
}, [
renderRow,
drawerThreads,
threads
]);
if (!mounted) return null;
return react.default.createElement(_copilotkit_web_components_threads_drawer.COPILOTKIT_THREADS_DRAWER_TAG, {
ref: elementRef,
"data-testid": dataTestId,
...recentLabel !== void 0 ? { "recent-label": recentLabel } : {}
}, rowChildren);
}
CopilotThreadsDrawer.displayName = "CopilotThreadsDrawer";
//#endregion
//#region src/v2/components/WildcardToolCallRender.tsx
const WildcardToolCallRender = defineToolCallRenderer({
name: "*",
render: ({ args, result, name, status }) => {
const [isExpanded, setIsExpanded] = (0, react.useState)(false);
const statusString = String(status);
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
className: "cpk:mt-2 cpk:pb-2",
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
className: "cpk:rounded-xl cpk:border cpk:border-zinc-200/60 cpk:dark:border-zinc-800/60 cpk:bg-white/70 cpk:dark:bg-zinc-900/50 cpk:shadow-sm cpk:backdrop-blur cpk:p-4",
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
className: "cpk:flex cpk:items-center cpk:justify-between cpk:gap-3 cpk:cursor-pointer",
onClick: () => setIsExpanded(!isExpanded),
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
className: "cpk:flex cpk:items-center cpk:gap-2 cpk:min-w-0",
children: [
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
className: `cpk:h-4 cpk:w-4 cpk:text-zinc-500 cpk:dark:text-zinc-400 cpk:transition-transform ${isExpanded ? "cpk:rotate-90" : ""}`,
fill: "none",
viewBox: "0 0 24 24",
strokeWidth: 2,
stroke: "currentColor",
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
strokeLinecap: "round",
strokeLinejoin: "round",
d: "M8.25 4.5l7.5 7.5-7.5 7.5"
})
}),
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: "cpk:inline-block cpk:h-2 cpk:w-2 cpk:rounded-full cpk:bg-blue-500" }),
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
className: "cpk:truncate cpk:text-sm cpk:font-medium cpk:text-zinc-900 cpk:dark:text-zinc-100",
children: name
})
]
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
className: `cpk:inline-flex cpk:items-center cpk:rounded-full cpk:px-2 cpk:py-1 cpk:text-xs cpk:font-medium ${statusString === "inProgress" || statusString === "executing" ? "cpk:bg-amber-100 cpk:text-amber-800 cpk:dark:bg-amber-500/15 cpk:dark:text-amber-400" : statusString === "complete" ? "cpk:bg-emerald-100 cpk:text-emerald-800 cpk:dark:bg-emerald-500/15 cpk:dark:text-emerald-400" : "cpk:bg-zinc-100 cpk:text-zinc-800 cpk:dark:bg-zinc-700/40 cpk:dark:text-zinc-300"}`,
children: String(status)
})]
}), isExpanded && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
className: "cpk:mt-3 cpk:grid cpk:gap-4",
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
className: "cpk:text-xs cpk:uppercase cpk:tracking-wide cpk:text-zinc-500 cpk:dark:text-zinc-400",
children: "Arguments"
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("pre", {
className: "cpk:mt-2 cpk:max-h-64 cpk:overflow-auto cpk:rounded-md cpk:bg-zinc-50 cpk:dark:bg-zinc-800/60 cpk:p-3 cpk:text-xs cpk:leading-relaxed cpk:text-zinc-800 cpk:dark:text-zinc-200 cpk:whitespace-pre-wrap cpk:break-words",
children: JSON.stringify(args ?? {}, null, 2)
})] }), result !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
className: "cpk:text-xs cpk:uppercase cpk:tracking-wide cpk:text-zinc-500 cpk:dark:text-zinc-400",
children: "Result"
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("pre", {
className: "cpk:mt-2 cpk:max-h-64 cpk:overflow-auto cpk:rounded-md cpk:bg-zinc-50 cpk:dark:bg-zinc-800/60 cpk:p-3 cpk:text-xs cpk:leading-relaxed cpk:text-zinc-800 cpk:dark:text-zinc-200 cpk:whitespace-pre-wrap cpk:break-words",
children: typeof result === "string" ? result : JSON.stringify(result, null, 2)
})] })]
})]
})
});
}
});
//#endregion
//#region src/context/copilot-context.tsx
const emptyCopilotContext$1 = {
actions: {},
setAction: () => {},
removeAction: () => {},
setRegisteredActions: () => "",
removeRegisteredAction: () => {},
chatComponentsCache: { current: {
actions: {},
coAgentStateRenders: {}
} },
getContextString: (documents, categories) => returnAndThrowInDebug(""),
addContext: () => "",
removeContext: () => {},
getAllContext: () => [],
getFunctionCallHandler: () => returnAndThrowInDebug(async () => {}),
isLoading: false,
setIsLoading: () => returnAndThrowInDebug(false),
chatInstructions: "",
setChatInstructions: () => returnAndThrowInDebug(""),
additionalInstructions: [],
setAdditionalInstructions: () => returnAndThrowInDebug([]),
getDocumentsContext: (categories) => returnAndThrowInDebug([]),
addDocumentContext: () => returnAndThrowInDebug(""),
removeDocumentContext: () => {},
copilotApiConfig: new class {
get chatApiEndpoint() {
throw new Error("Remember to wrap your app in a `<CopilotKit> {...} </CopilotKit>` !!!");
}
get headers() {
return {};
}
get body() {
return {};
}
}(),
chatSuggestionConfiguration: {},
addChatSuggestionConfiguration: () => {},
removeChatSuggestionConfiguration: () => {},
showDevConsole: false,
coagentStates: {},
setCoagentStates: () => {},
coagentStatesRef: { current: {} },
setCoagentStatesWithRef: () => {},
agentSession: null,
setAgentSession: () => {},
forwardedParameters: {},
agentLock: null,
threadId: "",
setThreadId: () => {},
runId: null,
setRunId: () => {},
chatAbortControllerRef: { current: null },
availableAgents: [],
extensions: {},
setExtensions: () => {},
interruptActions: {},
setInterruptAction: () => {},
removeInterruptAction: () => {},
interruptEventQueue: {},
addInterruptEvent: () => {},
resolveInterruptEvent: () => {},
onError: () => {},
bannerError: null,
setBannerError: () => {},
internalErrorHandlers: {},
setInternalErrorHandler: () => {},
removeInternalErrorHandler: () => {}
};
const CopilotContext = react.default.createContext(emptyCopilotContext$1);
function useCopilotContext() {
const context = react.default.useContext(CopilotContext);
if (context === emptyCopilotContext$1) throw new Error("Remember to wrap your app in a `<CopilotKit> {...} </CopilotKit>` !!!");
return context;
}
function returnAndThrowInDebug(_value) {
throw new Error("Remember to wrap your app in a `<CopilotKit> {...} </CopilotKit>` !!!");
}
//#endregion
//#region src/hooks/use-tree.ts
const removeNode = (nodes, id) => {
return nodes.reduce((result, node) => {
if (node.id !== id) {
const newNode = {
...node,
children: removeNode(node.children, id)
};
result.push(newNode);
}
return result;
}, []);
};
const addNode = (nodes, newNode, parentId) => {
if (!parentId) return [...nodes, newNode];
return nodes.map((node) => {
if (node.id === parentId) return {
...node,
children: [...node.children, newNode]
};
else if (node.children.length) return {
...node,
children: addNode(node.children, newNode, parentId)
};
return node;
});
};
const treeIndentationRepresentation = (index, indentLevel) => {
if (indentLevel === 0) return (index + 1).toString();
else if (indentLevel === 1) return String.fromCharCode(65 + index);
else if (indentLevel === 2) return String.fromCharCode(97 + index);
else return "-";
};
const printNode = (node, prefix = "", indentLevel = 0) => {
const indent = " ".repeat(3).repeat(indentLevel);
const prefixPlusIndentLength = prefix.length + indent.length;
const subsequentLinesPrefix = " ".repeat(prefixPlusIndentLength);
const valueLines = node.value.split("\n");
const outputFirstLine = `${indent}${prefix}${valueLines[0]}`;
const outputSubsequentLines = valueLines.slice(1).map((line) => `${subsequentLinesPrefix}${line}`).join("\n");
let output = `${outputFirstLine}\n`;
if (outputSubsequentLines) output += `${outputSubsequentLines}\n`;
const childPrePrefix = " ".repeat(prefix.length);
node.children.forEach((child, index) => output += printNode(child, `${childPrePrefix}${treeIndentationRepresentation(index, indentLevel + 1)}. `, indentLevel + 1));
return output;
};
function treeReducer(state, action) {
switch (action.type) {
case "ADD_NODE": {
const { value, parentId, id: newNodeId } = action;
const newNode = {
id: newNodeId,
value,
children: [],
categories: new Set(action.categories)
};
try {
return addNode(state, newNode, parentId);
} catch (error) {
console.error(`Error while adding node with id ${newNodeId}: ${error}`);
return state;
}
}
case "REMOVE_NODE": return removeNode(state, action.id);
default: return state;
}
}
const useTree = () => {
const [tree, dispatch] = (0, react.useReducer)(treeReducer, []);
const addElement = (0, react.useCallback)((value, categories, parentId) => {
const newNodeId = (0, _copilotkit_shared.randomId)();
dispatch({
type: "ADD_NODE",
value,
parentId,
id: newNodeId,
categories
});
return newNodeId;
}, []);
const removeElement = (0, react.useCallback)((id) => {
dispatch({
type: "REMOVE_NODE",
id
});
}, []);
const getAllElements = (0, react.useCallback)(() => {
return tree;
}, [tree]);
return {
tree,
addElement,
printTree: (0, react.useCallback)((categories) => {
const categoriesSet = new Set(categories);
let output = "";
tree.forEach((node, index) => {
if (!setsHaveIntersection$1(categoriesSet, node.categories)) return;
if (index !== 0) output += "\n";
output += printNode(node, `${treeIndentationRepresentation(index, 0)}. `);
});
return output;
}, [tree]),
removeElement,
getAllElements
};
};
function setsHaveIntersection$1(setA, setB) {
const [smallerSet, largerSet] = setA.size <= setB.size ? [setA, setB] : [setB, setA];
for (let item of smallerSet) if (largerSet.has(item)) return true;
return false;
}
//#endregion
//#region src/hooks/use-flat-category-store.ts
const useFlatCategoryStore = () => {
const [elements, dispatch] = (0, react.useReducer)(flatCategoryStoreReducer, /* @__PURE__ */ new Map());
return {
addElement: (0, react.useCallback)((value, categories) => {
const newId = (0, _copilotkit_shared.randomId)();
dispatch({
type: "ADD_ELEMENT",
value,
id: newId,
categories
});
return newId;
}, []),
removeElement: (0, react.useCallback)((id) => {
dispatch({
type: "REMOVE_ELEMENT",
id
});
}, []),
allElements: (0, react.useCallback)((categories) => {
const categoriesSet = new Set(categories);
const result = [];
elements.forEach((element) => {
if (setsHaveIntersection(categoriesSet, element.categories)) result.push(element.value);
});
return result;
}, [elements])
};
};
function flatCategoryStoreReducer(state, action) {
switch (action.type) {
case "ADD_ELEMENT": {
const { value, id, categories } = action;
const newElement = {
id,
value,
categories: new Set(categories)
};
const newState = new Map(state);
newState.set(id, newElement);
return newState;
}
case "REMOVE_ELEMENT": {
const newState = new Map(state);
newState.delete(action.id);
return newState;
}
default: return state;
}
}
function setsHaveIntersection(setA, setB) {
const [smallerSet, largerSet] = setA.size <= setB.size ? [setA, setB] : [setB, setA];
for (let item of smallerSet) if (largerSet.has(item)) return true;
return false;
}
//#endregion
//#region src/context/copilot-messages-context.tsx
const emptyCopilotContext = {
messages: [],
setMessages: () => [],
suggestions: [],
setSuggestions: () => []
};
const CopilotMessagesContext = react.default.createContext(emptyCopilotContext);
function useCopilotMessagesContext() {
const context = react.default.useContext(CopilotMessagesContext);
if (context === emptyCopilotContext) throw new Error("A messages consuming component was not wrapped with `<CopilotMessages> {...} </CopilotMessages>`");
return context;
}
//#endregion
//#region src/components/toast/toast-provider.tsx
const ToastContext = (0, react.createContext)(void 0);
function getErrorSeverity(error) {
if (error.severity) switch (error.severity) {
case _copilotkit_shared.Severity.CRITICAL: return "critical";
case _copilotkit_shared.Severity.WARNING: return "warning";
case _copilotkit_shared.Severity.INFO: return "info";
default: return "info";
}
const message = error.message.toLowerCase();
if (message.includes("api key") || message.includes("401") || message.includes("unauthorized") || message.includes("authentication") || message.includes("incorrect api key")) return "critical";
return "info";
}
function getErrorColors(severity) {
switch (severity) {
case "critical": return {
background: "#fee2e2",
border: "#dc2626",
text: "#7f1d1d",
icon: "#dc2626"
};
case "warning": return {
background: "#fef3c7",
border: "#d97706",
text: "#78350f",
icon: "#d97706"
};
case "info": return {
background: "#dbeafe",
border: "#2563eb",
text: "#1e3a8a",
icon: "#2563eb"
};
}
}
function useToast() {
const context = (0, react.useContext)(ToastContext);
if (!context) throw new Error("useToast must be used within a ToastProvider");
return context;
}
function formatBannerMessage(message) {
const jsonMatch = message.match(/'message':\s*'([^']+)'/);
if (jsonMatch) return jsonMatch[1];
let cleaned = message.split(" - ")[0];
cleaned = cleaned.split(": Error code")[0];
cleaned = cleaned.replace(/:\s*\d{3}$/, "");
cleaned = cleaned.replace(/See more:.*$/g, "");
cleaned = cleaned.trim();
return cleaned || "An error occurred.";
}
function extractUrl(message) {
const markdownMatch = /\[([^\]]+)\]\(([^)]+)\)/.exec(message);
if (markdownMatch) return {
url: markdownMatch[2],
text: "See More"
};
const plainMatch = /(https?:\/\/[^\s)]+)/.exec(message);
if (plainMatch) return {
url: plainMatch[0].replace(/[.,;:'"]*$/, ""),
text: "See More"
};
return null;
}
function BannerErrorDisplay({ bannerError, onDismiss }) {
const [detailsExpanded, setDetailsExpanded] = (0, react.useState)(false);
const colors = getErrorColors(getErrorSeverity(bannerError));
const details = bannerError.details;
const link = extractUrl(bannerError.message);
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
"data-testid": "copilot-error-banner",
style: {
position: "fixed",
bottom: "20px",
left: "50%",
transform: "translateX(-50%)",
zIndex: 9999,
backgroundColor: colors.background,
border: `1px solid ${colors.border}`,
borderLeft: `4px solid ${colors.border}`,
borderRadius: "8px",
padding: "12px 16px",
fontSize: "13px",
boxShadow: "0 4px 12px rgba(0, 0, 0, 0.15)",
backdropFilter: "blur(8px)",
maxWidth: "min(90vw, 700px)",
width: "100%",
boxSizing: "border-box",
overflow: "hidden"
},
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
style: {
display: "flex",
justifyContent: "space-between",
alignItems: "center",
gap: "10px"
},
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
style: {
display: "flex",
alignItems: "center",
gap: "8px",
flex: 1,
minWidth: 0
},
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { style: {
width: "12px",
height: "12px",
borderRadius: "50%",
backgroundColor: colors.border,
flexShrink: 0
} }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
style: {
display: "flex",
alignItems: "center",
gap: "10px",
flex: 1,
minWidth: 0
},
children: [
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
style: {
color: colors.text,
lineHeight: "1.4",
fontWeight: "400",
fontSize: "13px",
flex: 1,
wordBreak: "break-all",
overflowWrap: "break-word",
maxWidth: "550px",
overflow: "hidden",
display: "-webkit-box",
WebkitLineClamp: 10,
WebkitBoxOrient: "vertical"
},
children: formatBannerMessage(bannerError.message)
}),
link && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
onClick: () => window.open(link.url, "_blank", "noopener,noreferrer"),
style: {
background: colors.border,
color: "white",
border: "none",
borderRadius: "5px",
padding: "4px 10px",
fontSize: "11px",
fontWeight: "500",
cursor: "pointer",
transition: "all 0.2s ease",
flexShrink: 0
},
onMouseEnter: (e) => {
e.currentTarget.style.opacity = "0.9";
e.currentTarget.style.transform = "translateY(-1px)";
},
onMouseLeave: (e) => {
e.currentTarget.style.opacity = "1";
e.currentTarget.style.transform = "translateY(0)";
},
children: link.text
}),
details && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
onClick: () => setDetailsExpanded(!detailsExpanded),
style: {
background: "transparent",
border: `1px solid ${colors.border}`,
borderRadius: "5px",
padding: "4px 10px",
fontSize: "11px",
fontWeight: "500",
cursor: "pointer",
color: colors.text,
flexShrink: 0,
transition: "all 0.2s ease"
},
onMouseEnter: (e) => {
e.currentTarget.style.background = "rgba(0, 0, 0, 0.05)";
},
onMouseLeave: (e) => {
e.currentTarget.style.background = "transparent";
},
children: detailsExpanded ? "Hide Details" : "Show Details"
})
]
})]
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
onClick: onDismiss,
style: {
background: "transparent",
border: "none",
color: colors.text,
cursor: "pointer",
padding: "2px",
borderRadius: "3px",
fontSize: "14px",
lineHeight: "1",
opacity: .6,
transition: "all 0.2s ease",
flexShrink: 0
},
title: "Dismiss",
onMouseEnter: (e) => {
e.currentTarget.style.opacity = "1";
e.currentTarget.style.background = "rgba(0, 0, 0, 0.05)";
},
onMouseLeave: (e) => {
e.currentTarget.style.opacity = "0.6";
e.currentTarget.style.background = "transparent";
},
children: "x"
})]
}), detailsExpanded && details && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
style: {
marginTop: "10px",
padding: "10px",
background: "rgba(0, 0, 0, 0.04)",
borderRadius: "6px",
fontSize: "11px",
fontFamily: "monospace",
color: colors.text,
lineHeight: "1.5",
maxHeight: "200px",
overflowY: "auto",
whiteSpace: "pre-wrap",
wordBreak: "break-all"
},
children: [
details.code && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: "Code:" }),
" ",
details.code
] }),
details.originalMessage && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
style: { marginTop: "4px" },
children: [
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: "Message:" }),
" ",
details.originalMessage
]
}),
details.context && Object.keys(details.context).length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
style: { marginTop: "4px" },
children: [
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: "Context:" }),
" ",
JSON.stringify(details.context, null, 2)
]
}),
details.stack && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
style: {
marginTop: "4px",
opacity: .7
},
children: [
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: "Stack:" }),
"\n",
details.stack
]
})
]
})]
});
}
function ToastProvider({ enabled, children }) {
const [toasts, setToasts] = (0, react.useState)([]);
const [bannerError, setBannerErrorState] = (0, react.useState)(null);
const removeToast = (0, react.useCallback)((id) => {
setToasts((prev) => prev.filter((toast) => toast.id !== id));
}, []);
const addToast = (0, react.useCallback)((toast) => {
if (!enabled) return;
const id = toast.id ?? Math.random().toString(36).slice(2, 9);
setToasts((currentToasts) => {
if (currentToasts.find((toast) => toast.id === id)) return currentToasts;
return [...currentToasts, {
...toast,
id
}];
});
if (toast.duration) setTimeout(() => {
removeToast(id);
}, toast.duration);
}, [enabled, removeToast]);
const setBannerError = (0, react.useCallback)((error) => {
if (!enabled && error !== null) return;
setBannerErrorState(error);
}, [enabled]);
const value = {
toasts,
addToast,
addGraphQLErrorsToast: (0, react.useCallback)((errors) => {
console.warn("addGraphQLErrorsToast is deprecated. All errors now show as banners.");
}, []),
removeToast,
enabled,
bannerError,
setBannerError
};
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(ToastContext.Provider, {
value,
children: [bannerError && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(BannerErrorDisplay, {
bannerError,
onDismiss: () => setBannerError(null)
}), children]
});
}
//#endregion
//#region src/utils/dev-console.ts
function isLocalhost() {
if (typeof window === "undefined") return false;
return window.location.hostname === "localhost" || window.location.hostname === "127.0.0.1" || window.location.hostname === "0.0.0.0";
}
function shouldShowDevConsole(showDevConsole) {
if (showDevConsole !== void 0) return showDevConsole;
return isLocalhost();
}
//#endregion
//#region src/components/copilot-provider/copilot-messages.tsx
/**
* An internal context to separate the messages state (which is constantly changing) from the rest of CopilotKit context
*/
/**
* Determine whether a GraphQL error should be suppressed based on its visibility
* and whether the dev console is active.
*
* Returns `null` when the error should be surfaced to the UI, or a log prefix
* string when the error should be suppressed (logged to console only).
*
* Exported for unit testing.
*/
function getErrorSuppression(visibility, isDev) {
if (visibility === _copilotkit_shared.ErrorVisibility.SILENT) return "CopilotKit Silent Error:";
if (!isDev && visibility === _copilotkit_shared.ErrorVisibility.DEV_ONLY) return "CopilotKit Error (hidden in production):";
return null;
}
const MessagesTapContext = (0, react.createContext)(null);
function useMessagesTap() {
const tap = (0, react.useContext)(MessagesTapContext);
if (!tap) throw new Error("useMessagesTap must be used inside <MessagesTapProvider>");
return tap;
}
function MessagesTapProvider({ children }) {
const messagesRef = (0, react.useRef)([]);
const tapRef = (0, react.useRef)({
getMessagesFromTap: () => messagesRef.current,
updateTapMessages: (messages) => {
messagesRef.current = messages;
}
});
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(MessagesTapContext.Provider, {
value: tapRef.current,
children
});
}
/**
* CopilotKit messages context.
*/
function CopilotMessages({ children }) {
const [messages, setMessages] = (0, react.useState)([]);
(0, react.useRef)(void 0);
(0, react.useRef)(void 0);
(0, react.useRef)(void 0);
const { updateTapMessages } = useMessagesTap();
const { threadId, agentSession, showDevConsole, onError, copilotApiConfig } = useCopilotContext();
const { setBannerError } = useToast();
const traceUIError = (0, react.useCallback)(async (error, originalError) => {
if (!onError || !copilotApiConfig.publicApiKey) return;
try {
await onError({
type: "error",
timestamp: Date.now(),
context: {
source: "ui",
request: {
operation: "loadAgentState",
url: copilotApiConfig.chatApiEndpoint,
startTime: Date.now()
},
technical: {
environment: "browser",
userAgent: typeof navigator !== "undefined" ? navigator.userAgent : void 0,
stackTrace: originalError instanceof Error ? originalError.stack : void 0
}
},
error
});
} catch (traceError) {
console.error("Error in CopilotMessages onError handler:", traceError);
}
}, [
onError,
copilotApiConfig.publicApiKey,
copilotApiConfig.chatApiEndpoint
]);
const createStructuredError = (gqlError) => {
const extensions = gqlError.extensions;
const originalError = extensions?.originalError;
if (originalError?.stack) {
if (originalError.stack.includes("CopilotApiDiscoveryError")) return new _copilotkit_shared.CopilotKitApiDiscoveryError({ message: originalError.message });
if (originalError.stack.includes("CopilotKitRemoteEndpointDiscoveryError")) return new _copilotkit_shared.CopilotKitRemoteEndpointDiscoveryError({ message: originalError.message });
if (originalError.stack.includes("CopilotKitAgentDiscoveryError")) return new _copilotkit_shared.CopilotKitAgentDiscoveryError({
agentName: "",
availableAgents: []
});
}
const message = originalError?.message || gqlError.message;
const code = extensions?.code;
if (code) return new _copilotkit_shared.CopilotKitError({
message,
code
});
return null;
};
(0, react.useCallback)((error) => {
if (error.graphQLErrors?.length) {
const graphQLErrors = error.graphQLErrors;
const routeError = (gqlError) => {
const visibility = gqlError.extensions?.visibility;
const suppression = getErrorSuppression(visibility, shouldShowDevConsole(showDevConsole));
if (suppression) {
console.error(suppression, gqlError.message);
return;
}
const ckError = createStructuredError(gqlError);
if (ckError) {
setBannerError(ckError);
traceUIError(ckError, gqlError);
} else {
const fallbackError = new _copilotkit_shared.CopilotKitError({
message: gqlError.message,
code: _copilotkit_shared.CopilotKitErrorCode.UNKNOWN
});
setBannerError(fallbackError);
traceUIError(fallbackError, gqlError);
}
};
graphQLErrors.forEach(routeError);
} else {
const fallbackError = new _copilotkit_shared.CopilotKitError({
message: error?.message || String(error),
code: _copilotkit_shared.CopilotKitErrorCode.UNKNOWN
});
setBannerError(fallbackError);
traceUIError(fallbackError, error);
}
}, [
setBannerError,
showDevConsole,
traceUIError
]);
(0, react.useEffect)(() => {
updateTapMessages(messages);
}, [messages, updateTapMessages]);
const memoizedChildren = (0, react.useMemo)(() => children, [children]);
const [suggestions, setSuggestions] = (0, react.useState)([]);
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CopilotMessagesContext.Provider, {
value: {
messages,
setMessages,
suggestions,
setSuggestions
},
children: memoizedChildren
});
}
//#endregion
//#region src/components/usage-banner.tsx
function UsageBanner({ severity = _copilotkit_shared.Severity.CRITICAL, message = "", onClose, actions }) {
if (!message || !severity) return null;
const theme = {
[_copilotkit_shared.Severity.INFO]: {
bg: "#f8fafc",
border: "#e2e8f0",
text: "#475569",
accent: "#3b82f6"
},
[_copilotkit_shared.Severity.WARNING]: {
bg: "#fffbeb",
border: "#fbbf24",
text: "#92400e",
accent: "#f59e0b"
},
[_copilotkit_shared.Severity.CRITICAL]: {
bg: "#fef2f2",
border: "#fecaca",
text: "#dc2626",
accent: "#ef4444"
}
}[severity];
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("style", { children: `
@keyframes slideUp {
from { opacity: 0; transform: translateX(-50%) translateY(8px); }
to { opacity: 1; transform: translateX(-50%) translateY(0); }
}
.usage-banner {
position: fixed;
bottom: 24px;
left: 50%;
transform: translateX(-50%);
width: min(600px, calc(100vw - 32px));
z-index: 10000;
animation: slideUp 0.2s cubic-bezier(0.16, 1, 0.3, 1);
}
.banner-content {
background: linear-gradient(135deg, ${theme.bg} 0%, ${theme.bg}f5 100%);
border: 1px solid ${theme.border};
border-radius: 12px;
padding: 18px 20px;
box-shadow:
0 4px 24px rgba(0, 0, 0, 0.08),
0 2px 8px rgba(0, 0, 0, 0.04),
inset 0 1px 0 rgba(255, 255, 255, 0.7);
display: flex;
align-items: center;
gap: 16px;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif;
backdrop-filter: blur(12px);
position: relative;
overflow: hidden;
}
.banner-content::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
height: 1px;
background: linear-gradient(90deg, transparent, ${theme.accent}40, transparent);
}
.banner-message {
color: ${theme.text};
font-size: 14px;
line-height: 1.5;
font-weight: 500;
flex: 1;
letter-spacing: -0.01em;
}
.close-btn {
background: rgba(0, 0, 0, 0.05);
border: none;
color: ${theme.text};
cursor: pointer;
padding: 0;
border-radius: 6px;
opacity: 0.6;
transition: all 0.15s cubic-bezier(0.16, 1, 0.3, 1);
font-size: 14px;
line-height: 1;
flex-shrink: 0;
width: 24px;
height: 24px;
display: flex;
align-items: center;
justify-content: center;
}
.close-btn:hover {
opacity: 1;
background: rgba(0, 0, 0, 0.08);
transform: scale(1.05);
}
.btn-primary {
background: linear-gradient(135deg, ${theme.accent} 0%, ${theme.accent}e6 100%);
color: white;
border: none;
border-radius: 8px;
padding: 10px 18px;
font-size: 13px;
font-weight: 600;
cursor: pointer;
transition: all 0.15s cubic-bezier(0.16, 1, 0.3, 1);
font-family: inherit;
flex-shrink: 0;
box-shadow:
0 2px 8px ${theme.accent}30,
inset 0 1px 0 rgba(255, 255, 255, 0.2);
letter-spacing: -0.01em;
}
.btn-primary:hover {
transform: translateY(-1px) scale(1.02);
box-shadow:
0 4px 12px ${theme.accent}40,
inset 0 1px 0 rgba(255, 255, 255, 0.25);
}
.btn-primary:active {
transform: translateY(0) scale(0.98);
transition: all 0.08s cubic-bezier(0.16, 1, 0.3, 1);
}
@media (max-width: 640px) {
.usage-banner {
width: calc(100vw - 24px);
}
.banner-content {
padding: 16px;
gap: 12px;
}
.banner-message {
font-size: 13px;
line-height: 1.45;
}
.btn-primary {
padding: 8px 14px;
font-size: 12px;
}
.close-btn {
width: 22px;
height: 22px;
font-size: 12px;
}
}
` }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
className: "usage-banner",
"data-testid": "copilot-error-banner",
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
className: "banner-content",
children: [
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
className: "banner-message",
children: message
}),
actions?.primary && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
className: "btn-primary",
onClick: actions.primary.onClick,
children: actions.primary.label
}),
onClose && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
className: "close-btn",
onClick: onClose,
title: "Close",
children: "×"
})
]
})
})] });
}
const getErrorActions = (error) => {
switch (error.code) {
case _copilotkit_shared.CopilotKitErrorCode.MISSING_PUBLIC_API_KEY_ERROR: return { primary: {
label: "Show me how",
onClick: () => window.open("https://docs.copilotkit.ai/premium/overview#getting-access", "_blank", "noopener,noreferrer")
} };
case _copilotkit_shared.CopilotKitErrorCode.UPGRADE_REQUIRED_ERROR: return { primary: {
label: "Upgrade",
onClick: () => window.open("https://dashboard.operations.copilotkit.ai", "_blank", "noopener,noreferrer")
} };
default: return;
}
};
//#endregion
//#region src/lib/status-checker.ts
const STATUS_CHECK_INTERVAL = 1e3 * 60 * 5;
var StatusChecker = class {
constructor() {
this.activeKey = null;
this.intervalId = null;
this.instanceCount = 0;
this.lastResponse = null;
}
async start(publicApiKey, onUpdate) {
this.instanceCount++;
if (this.activeKey === publicApiKey) return;
if (this.intervalId) clearInterval(this.intervalId);
const checkStatus = async () => {
try {
const response = await fetch(`${_copilotkit_shared.COPILOT_CLOUD_API_URL}/ciu`, {
method: "GET",
headers: { [_copilotkit_shared.COPILOT_CLOUD_PUBLIC_API_KEY_HEADER]: publicApiKey }
}).then((response) => response.json());
this.lastResponse = response;
onUpdate?.(response);
return response;
} catch (error) {
return null;
}
};
const initialResponse = await checkStatus();
this.intervalId = setInterval(checkStatus, STATUS_CHECK_INTERVAL);
this.activeKey = publicApiKey;
return initialResponse;
}
getLastResponse() {
return this.lastResponse;
}
stop() {
this.instanceCount--;
if (this.instanceCount === 0) {
if (this.intervalId) {
clearInterval(this.intervalId);
this.intervalId = null;
this.activeKey = null;
this.lastResponse = null;
}
}
}
};
//#endregion
//#region src/components/toast/exclamation-mark-icon.tsx
const ExclamationMarkIcon = ({ className, style }) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("svg", {
xmlns: "http://www.w3.org/2000/svg",
width: "24",
height: "24",
viewBox: "0 0 24 24",
fill: "none",
stroke: "currentColor",
strokeWidth: "2",
strokeLinecap: "round",
strokeLinejoin: "round",
className: `lucide lucide-circle-alert ${className ? className : ""}`,
style,
children: [
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("circle", {
cx: "12",
cy: "12",
r: "10"
}),
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("line", {
x1: "12",
x2: "12",
y1: "8",
y2: "12"
}),
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("line", {
x1: "12",
x2: "12.01",
y1: "16",
y2: "16"
})
]
});
//#endregion
//#region src/components/error-boundary/error-utils.tsx
function ErrorToast({ errors }) {
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
style: {
fontSize: "13px",
maxWidth: "600px"
},
children: [errors.map((error, idx) => {
const message = ("extensions" in error ? error.extensions?.originalError : {})?.message ?? error.message;
const code = "extensions" in error ? error.extensions?.code : null;
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
style: {
marginTop: idx === 0 ? 0 : 10,
marginBottom: 14
},
children: [
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ExclamationMarkIcon, { style: { marginBottom: 4 } }),
code && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
style: {
fontWeight: "600",
marginBottom: 4
},
children: [
"Copilot Runtime Error:",
" ",
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
style: {
fontFamily: "monospace",
fontWeight: "normal"
},
children: code
})
]
}),
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_markdown.default, { children: message })
]
}, idx);
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
style: {
fontSize: "11px",
opacity: .75
},
children: "NOTE: This error only displays during local development."
})]
});
}
function useErrorToast() {
const { addToast } = useToast();
return (0, react.useCallback)((errors) => {
addToast({
type: "error",
id: errors.map((err) => {
const message = "extensions" in err ? (err.extensions?.originalError)?.message || err.message : err.message;
const stack = err.stack || "";
return btoa(message + stack).slice(0, 32);
}).join("|"),
message: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ErrorToast, { errors })
});
}, [addToast]);
}
function useAsyncCallback(callback, deps) {
const addErrorToast = useErrorToast();
return (0, react.useCallback)(async (...args) => {
try {
return await callback(...args);
} catch (error) {
console.error("Error in async callback:", error);
addErrorToast([error]);
throw error;
}
}, deps);
}
//#endregion
//#region src/components/error-boundary/error-boundary.tsx
const statusChecker = new StatusChecker();
var CopilotErrorBoundary = class extends react.default.Component {
constructor(props) {
super(props);
this.state = { hasError: false };
}
static getDerivedStateFromError(error) {
return {
hasError: true,
error
};
}
componentDidMount() {
if (this.props.publicApiKey) statusChecker.start(this.props.publicApiKey, (newStatus) => {
this.setState((prevState) => {
if (newStatus?.severity !== prevState.status?.severity) return { status: newStatus ?? void 0 };
return null;
});
});
}
componentWillUnmount() {
statusChecker.stop();
}
componentDidCatch(error, errorInfo) {
console.error("CopilotKit Error:", error, errorInfo);
}
render() {
if (this.state.hasError) {
if (this.state.error instanceof _copilotkit_shared.CopilotKitError) return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [this.props.children, this.props.showUsageBanner && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(UsageBanner, {
severity: this.state.status?.severity ?? this.state.error.severity,
message: this.state.status?.message ?? this.state.error.message,
actions: getErrorActions(this.state.error)
})] });
throw this.state.error;
}
return this.props.children;
}
};
//#endregion
//#region src/context/coagent-state-renders-context.tsx
const CoAgentStateRendersContext = (0, react.createContext)(void 0);
function CoAgentStateRendersProvider({ children }) {
const [coAgentStateRenders, setCoAgentStateRenders] = (0, react.useState)({});
const setCoAgentStateRender = (0, react.useCallback)((id, stateRender) => {
setCoAgentStateRenders((prevPoints) => ({
...prevPoints,
[id]: stateRender
}));
}, []);
const removeCoAgentStateRender = (0, react.useCallback)((id) => {
setCoAgentStateRenders((prevPoints) => {
const newPoints = { ...prevPoints };
delete newPoints[id];
return newPoints;
});
}, []);
const claimsRef = (0, react.useRef)({});
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CoAgentStateRendersContext.Provider, {
value: {
coAgentStateRenders,
setCoAgentStateRender,
removeCoAgentStateRender,
claimsRef
},
children
});
}
function useCoAgentStateRenders() {
const context = (0, react.useContext)(CoAgentStateRendersContext);
if (!context) throw new Error("useCoAgentStateRenders must be used within CoAgentStateRendersProvider");
return context;
}
//#endregion
//#region src/context/threads-context.tsx
const ThreadsContext = (0, react.createContext)(void 0);
function ThreadsProvider({ children, threadId: explicitThreadId }) {
const [internalThreadId, setInternalThreadId] = (0, react.useState)(() => (0, _copilotkit_shared.randomUUID)());
const [internalIsExplicit, setInternalIsExplicit] = (0, react.useState)(false);
const threadId = explicitThreadId ?? internalThreadId;
const isThreadIdExplicit = explicitThreadId != null || internalIsExplicit;
const setThreadId = (0, react.useCallback)((value) => {
setInternalThreadId(value);
setInternalIsExplicit(true);
}, []);
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ThreadsContext.Provider, {
value: {
threadId,
setThreadId,
isThreadIdExplicit
},
children
});
}
function useThreads() {
const context = (0, react.useContext)(ThreadsContext);
if (!context) throw new Error("useThreads must be used within ThreadsProvider");
return context;
}
//#endregion
//#region src/hooks/use-coagent-state-render-bridge.helpers.ts
let RenderStatus = /* @__PURE__ */ function(RenderStatus) {
RenderStatus["InProgress"] = "inProgress";
RenderStatus["Complete"] = "complete";
return RenderStatus;
}({});
let ClaimAction = /* @__PURE__ */ function(ClaimAction) {
ClaimAction["Create"] = "create";
ClaimAction["Override"] = "override";
ClaimAction["Existing"] = "existing";
ClaimAction["Block"] = "block";
return ClaimAction;
}({});
function getStateWithoutConstantKeys(state) {
if (!state) return {};
const { messages, tools, copilotkit, ...stateWithoutConstantKeys } = state;
return stateWithoutConstantKeys;
}
function areStatesEquals(a, b) {
if (a && !b || !a && b) return false;
const { messages, tools, copilotkit, ...aWithoutConstantKeys } = a;
const { messages: bMessages, tools: bTools, copilotkit: bCopilotkit, ...bWithoutConstantKeys } = b;
return JSON.stringify(aWithoutConstantKeys) === JSON.stringify(bWithoutConstantKeys);
}
function isPlaceholderMessageId(messageId) {
return !!messageId && messageId.startsWith("coagent-state-render-");
}
function isPlaceholderMessageName(messageName) {
return messageName === "coagent-state-render";
}
function readCachedMessageEntry(entry) {
if (!entry || typeof entry !== "object") return {
snapshot: entry,
runId: void 0
};
return {
snapshot: "snapshot" in entry ? entry.snapshot : entry,
runId: "runId" in entry ? entry.runId : void 0
};
}
function getEffectiveRunId({ existingClaimRunId, cachedMessageRunId, runId }) {
return existingClaimRunId || cachedMessageRunId || runId || "pending";
}
/**
* Resolve whether a message can claim a render slot.
* This is a pure decision function; the caller applies claim mutations.
*/
function resolveClaim({ claims, context, stateSnapshot }) {
const { messageId, stateRenderId, runId, messageIndex } = context;
const existing = claims[messageId];
if (existing) {
const canRender = existing.stateRenderId === stateRenderId;
const shouldUpdateRunId = canRender && runId && (!existing.runId || existing.runId === "pending");
return {
canRender,
action: canRender ? ClaimAction.Existing : ClaimAction.Block,
updateRunId: shouldUpdateRunId ? runId : void 0
};
}
const normalizedRunId = runId ?? "pending";
const renderClaimedByOtherMessageEntry = Object.entries(claims).find(([, claim]) => claim.stateRenderId === stateRenderId && (claim.runId ?? "pending") === normalizedRunId && (0, _copilotkit_shared.dataToUUID)(getStateWithoutConstantKeys(claim.stateSnapshot)) === (0, _copilotkit_shared.dataToUUID)(getStateWithoutConstantKeys(stateSnapshot)));
const renderClaimedByOtherMessage = renderClaimedByOtherMessageEntry?.[1];
const claimedMessageId = renderClaimedByOtherMessageEntry?.[0];
if (renderClaimedByOtherMessage) {
if (messageIndex !== void 0 && renderClaimedByOtherMessage.messageIndex !== void 0 && messageIndex > renderClaimedByOtherMessage.messageIndex) return {
canRender: true,
action: ClaimAction.Override,
nextClaim: {
stateRenderId,
runId,
messageIndex
},
lockOthers: runId === renderClaimedByOtherMessage.runId || isPlaceholderMessageId(claimedMessageId)
};
if (runId && renderClaimedByOtherMessage.runId && runId !== renderClaimedByOtherMessage.runId) return {
canRender: true,
action: ClaimAction.Override,
nextClaim: {
stateRenderId,
runId,
messageIndex
},
lockOthers: isPlaceholderMessageId(claimedMessageId)
};
if (isPlaceholderMessageId(claimedMessageId)) return {
canRender: true,
action: ClaimAction.Override,
nextClaim: {
stateRenderId,
runId,
messageIndex
},
lockOthers: true
};
if (stateSnapshot && renderClaimedByOtherMessage.stateSnapshot && !areStatesEquals(renderClaimedByOtherMessage.stateSnapshot, stateSnapshot)) return {
canRender: true,
action: ClaimAction.Override,
nextClaim: {
stateRenderId,
runId
}
};
return {
canRender: false,
action: ClaimAction.Block
};
}
if (!runId) return {
canRender: false,
action: ClaimAction.Block
};
return {
canRender: true,
action: ClaimAction.Create,
nextClaim: {
stateRenderId,
runId,
messageIndex
}
};
}
/**
* Select the best snapshot to render for this message.
* Priority order is:
* 1) explicit message snapshot
* 2) live agent state (latest assistant only)
* 3) cached snapshot for message
* 4) cached snapshot for stateRenderId+runId
* 5) last cached snapshot for stateRenderId
*/
function selectSnapshot({ messageId, messageName, allowLiveState, skipLatestCache, stateRenderId, effectiveRunId, stateSnapshotProp, agentState, agentMessages, existingClaim, caches }) {
const lastAssistantId = agentMessages ? [...agentMessages].toReversed().find((msg) => msg.role === "assistant")?.id : void 0;
const latestSnapshot = stateRenderId !== void 0 ? caches.byStateRenderAndRun[`${stateRenderId}::latest`] : void 0;
const messageIndex = agentMessages ? agentMessages.findIndex((msg) => msg.id === messageId) : -1;
const messageRole = messageIndex >= 0 && agentMessages ? agentMessages[messageIndex]?.role : void 0;
let previousUserMessageId;
if (messageIndex > 0 && agentMessages) {
for (let i = messageIndex - 1; i >= 0; i -= 1) if (agentMessages[i]?.role === "user") {
previousUserMessageId = agentMessages[i]?.id;
break;
}
}
const liveStateIsStale = stateSnapshotProp === void 0 && latestSnapshot !== void 0 && agentState !== void 0 && areStatesEquals(latestSnapshot, agentState);
const shouldUseLiveState = (Boolean(allowLiveState) || !lastAssistantId || messageId === lastAssistantId) && !liveStateIsStale;
const snapshot = stateSnapshotProp ? (0, _copilotkit_shared.parseJson)(stateSnapshotProp, stateSnapshotProp) : shouldUseLiveState ? agentState : void 0;
const hasSnapshotKeys = !!(snapshot && Object.keys(snapshot).length > 0);
const allowEmptySnapshot = snapshot !== void 0 && !hasSnapshotKeys && (stateSnapshotProp !== void 0 || shouldUseLiveState);
const messageCacheEntry = caches.byMessageId[messageId];
const cachedMessageSnapshot = readCachedMessageEntry(messageCacheEntry).snapshot;
const cacheKey = stateRenderId !== void 0 ? `${stateRenderId}::${effectiveRunId}` : void 0;
let cachedSnapshot = cachedMessageSnapshot ?? caches.byMessageId[messageId];
if (cachedSnapshot === void 0 && cacheKey && caches.byStateRenderAndRun[cacheKey] !== void 0) cachedSnapshot = caches.byStateRenderAndRun[cacheKey];
if (cachedSnapshot === void 0 && stateRenderId && previousUserMessageId && caches.byStateRenderAndRun[`${stateRenderId}::pending:${previousUserMessageId}`] !== void 0) cachedSnapshot = caches.byStateRenderAndRun[`${stateRenderId}::pending:${previousUserMessageId}`];
if (cachedSnapshot === void 0 && !skipLatestCache && stateRenderId && messageRole !== "assistant" && (stateSnapshotProp !== void 0 || agentState && Object.keys(agentState).length > 0)) cachedSnapshot = caches.byStateRenderAndRun[`${stateRenderId}::latest`];
const snapshotForClaim = existingClaim?.locked ? existingClaim.stateSnapshot ?? cachedSnapshot : hasSnapshotKeys ? snapshot : existingClaim?.stateSnapshot ?? cachedSnapshot;
return {
snapshot,
hasSnapshotKeys,
cachedSnapshot,
allowEmptySnapshot,
snapshotForClaim
};
}
//#endregion
//#region src/hooks/use-coagent-state-render-registry.ts
const LAST_SNAPSHOTS_BY_RENDER_AND_RUN = "__lastSnapshotsByStateRenderIdAndRun";
const LAST_SNAPSHOTS_BY_MESSAGE = "__lastSnapshotsByMessageId";
function getClaimsStore(claimsRef) {
return claimsRef.current;
}
function getSnapshotCaches(claimsRef) {
const store = getClaimsStore(claimsRef);
return {
byStateRenderAndRun: store[LAST_SNAPSHOTS_BY_RENDER_AND_RUN] ?? {},
byMessageId: store[LAST_SNAPSHOTS_BY_MESSAGE] ?? {}
};
}
function useStateRenderRegistry({ agentId, stateRenderId, message, messageIndex, stateSnapshot, agentState, agentMessages, claimsRef }) {
const store = getClaimsStore(claimsRef);
const runId = message.runId;
const cachedMessageEntry = store[LAST_SNAPSHOTS_BY_MESSAGE]?.[message.id];
const { runId: cachedMessageRunId } = readCachedMessageEntry(cachedMessageEntry);
const existingClaimRunId = claimsRef.current[message.id]?.runId;
const effectiveRunId = getEffectiveRunId({
existingClaimRunId,
cachedMessageRunId,
runId
});
(0, react.useEffect)(() => {
return () => {
const existingClaim = claimsRef.current[message.id];
if (existingClaim?.stateSnapshot && Object.keys(existingClaim.stateSnapshot).length > 0) {
const snapshotCache = { ...store[LAST_SNAPSHOTS_BY_RENDER_AND_RUN] };
const cacheKey = `${existingClaim.stateRenderId}::${existingClaim.runId ?? "pending"}`;
snapshotCache[cacheKey] = existingClaim.stateSnapshot;
snapshotCache[`${existingClaim.stateRenderId}::latest`] = existingClaim.stateSnapshot;
store[LAST_SNAPSHOTS_BY_RENDER_AND_RUN] = snapshotCache;
const messageCache = { ...store[LAST_SNAPSHOTS_BY_MESSAGE] };
messageCache[message.id] = {
snapshot: existingClaim.stateSnapshot,
runId: existingClaim.runId ?? effectiveRunId
};
store[LAST_SNAPSHOTS_BY_MESSAGE] = messageCache;
}
delete claimsRef.current[message.id];
};
}, [
claimsRef,
effectiveRunId,
message.id
]);
if (!stateRenderId) return { canRender: false };
const caches = getSnapshotCaches(claimsRef);
const existingClaim = claimsRef.current[message.id];
const { snapshot, hasSnapshotKeys, allowEmptySnapshot, snapshotForClaim } = selectSnapshot({
messageId: message.id,
messageName: message.name,
allowLiveState: isPlaceholderMessageName(message.name) || isPlaceholderMessageId(message.id),
skipLatestCache: isPlaceholderMessageName(message.name) || isPlaceholderMessageId(message.id),
stateRenderId,
effectiveRunId,
stateSnapshotProp: stateSnapshot,
agentState,
agentMessages,
existingClaim,
caches
});
const resolution = resolveClaim({
claims: claimsRef.current,
context: {
agentId,
messageId: message.id,
stateRenderId,
runId: effectiveRunId,
messageIndex
},
stateSnapshot: snapshotForClaim
});
if (resolution.action === ClaimAction.Block) return { canRender: false };
if (resolution.updateRunId && claimsRef.current[message.id]) claimsRef.current[message.id].runId = resolution.updateRunId;
if (resolution.nextClaim) claimsRef.current[message.id] = resolution.nextClaim;
if (resolution.lockOthers) Object.entries(claimsRef.current).forEach(([id, claim]) => {
if (id !== message.id && claim.stateRenderId === stateRenderId) claim.locked = true;
});
if (existingClaim && !existingClaim.locked && agentMessages?.length) {
const indexInAgentMessages = agentMessages.findIndex((msg) => msg.id === message.id);
if (indexInAgentMessages >= 0 && indexInAgentMessages < agentMessages.length - 1) existingClaim.locked = true;
}
const existingSnapshot = claimsRef.current[message.id].stateSnapshot;
const snapshotChanged = stateSnapshot && existingSnapshot !== void 0 && !areStatesEquals(existingSnapshot, snapshot);
if (snapshot && (stateSnapshot || hasSnapshotKeys || allowEmptySnapshot) && (!claimsRef.current[message.id].locked || snapshotChanged)) {
if (!claimsRef.current[message.id].locked || snapshotChanged) {
claimsRef.current[message.id].stateSnapshot = snapshot;
const snapshotCache = { ...store[LAST_SNAPSHOTS_BY_RENDER_AND_RUN] };
const cacheKey = `${stateRenderId}::${effectiveRunId}`;
snapshotCache[cacheKey] = snapshot;
snapshotCache[`${stateRenderId}::latest`] = snapshot;
store[LAST_SNAPSHOTS_BY_RENDER_AND_RUN] = snapshotCache;
const messageCache = { ...store[LAST_SNAPSHOTS_BY_MESSAGE] };
messageCache[message.id] = {
snapshot,
runId: effectiveRunId
};
store[LAST_SNAPSHOTS_BY_MESSAGE] = messageCache;
if (stateSnapshot) claimsRef.current[message.id].locked = true;
}
} else if (snapshotForClaim) {
if (!claimsRef.current[message.id].stateSnapshot) {
claimsRef.current[message.id].stateSnapshot = snapshotForClaim;
const snapshotCache = { ...store[LAST_SNAPSHOTS_BY_RENDER_AND_RUN] };
const cacheKey = `${stateRenderId}::${effectiveRunId}`;
snapshotCache[cacheKey] = snapshotForClaim;
snapshotCache[`${stateRenderId}::latest`] = snapshotForClaim;
store[LAST_SNAPSHOTS_BY_RENDER_AND_RUN] = snapshotCache;
const messageCache = { ...store[LAST_SNAPSHOTS_BY_MESSAGE] };
messageCache[message.id] = {
snapshot: snapshotForClaim,
runId: effectiveRunId
};
store[LAST_SNAPSHOTS_BY_MESSAGE] = messageCache;
}
}
return { canRender: true };
}
//#endregion
//#region src/hooks/use-coagent-state-render-bridge.tsx
function useCoagentStateRenderBridge(agentId, props) {
const { stateSnapshot, message } = props;
const { coAgentStateRenders, claimsRef } = useCoAgentStateRenders();
const { agent } = useAgent({ agentId });
const [nodeName, setNodeName] = (0, react.useState)(void 0);
const [, forceUpdate] = (0, react.useState)(0);
(0, react.useEffect)(() => {
if (!agent) return;
const { unsubscribe } = agent.subscribe({
onStateChanged: () => {
forceUpdate((value) => value + 1);
},
onStepStartedEvent: ({ event }) => {
if (event.stepName !== nodeName) setNodeName(event.stepName);
},
onStepFinishedEvent: ({ event }) => {
if (event.stepName === nodeName) setNodeName(void 0);
}
});
return () => {
unsubscribe();
};
}, [agentId, nodeName]);
const getStateRender = (0, react.useCallback)((messageId) => {
return Object.entries(coAgentStateRenders).find(([stateRenderId, stateRender]) => {
if (claimsRef.current[messageId]) return stateRenderId === claimsRef.current[messageId].stateRenderId;
const matchingAgentName = stateRender.name === agentId;
const matchesNodeContext = stateRender.nodeName ? stateRender.nodeName === nodeName : true;
return matchingAgentName && matchesNodeContext;
});
}, [
coAgentStateRenders,
nodeName,
agentId
]);
const stateRenderEntry = (0, react.useMemo)(() => getStateRender(message.id), [getStateRender, message.id]);
const stateRenderId = stateRenderEntry?.[0];
const stateRender = stateRenderEntry?.[1];
const { canRender } = useStateRenderRegistry({
agentId,
stateRenderId,
message: {
...message,
runId: props.runId ?? message.runId
},
messageIndex: props.messageIndex,
stateSnapshot,
agentState: agent?.state,
agentMessages: agent?.messages,
claimsRef
});
return (0, react.useMemo)(() => {
if (!stateRender || !stateRenderId) return null;
if (!canRender) return null;
if (stateRender.handler) stateRender.handler({
state: stateSnapshot ? (0, _copilotkit_shared.parseJson)(stateSnapshot, stateSnapshot) : agent?.state ?? {},
nodeName: nodeName ?? ""
});
if (stateRender.render) {
const status = agent?.isRunning ? RenderStatus.InProgress : RenderStatus.Complete;
if (typeof stateRender.render === "string") return stateRender.render;
return stateRender.render({
status,
state: claimsRef.current[message.id].stateSnapshot ?? {},
nodeName: nodeName ?? ""
});
}
}, [
stateRender,
stateRenderId,
agent?.state,
agent?.isRunning,
nodeName,
message.id,
stateSnapshot,
canRender
]);
}
function CoAgentStateRenderBridge(props) {
return useCoagentStateRenderBridge(props.agentId, props);
}
//#endregion
//#region src/components/CopilotListeners.tsx
const usePredictStateSubscription = (agent) => {
const predictStateToolsRef = (0, react.useRef)([]);
const getSubscriber = (0, react.useCallback)((agent) => ({
onCustomEvent: ({ event }) => {
if (event.name === "PredictState") predictStateToolsRef.current = event.value;
},
onToolCallArgsEvent: ({ partialToolCallArgs, toolCallName }) => {
predictStateToolsRef.current.forEach((t) => {
if (t?.tool !== toolCallName) return;
const emittedState = typeof partialToolCallArgs === "string" ? (0, _copilotkit_shared.parseJson)(partialToolCallArgs, partialToolCallArgs) : partialToolCallArgs;
agent.setState({ [t.state_key]: emittedState[t.state_key] });
});
}
}), []);
(0, react.useEffect)(() => {
if (!agent) return;
const subscriber = getSubscriber(agent);
const { unsubscribe } = agent.subscribe(subscriber);
return () => {
unsubscribe();
};
}, [agent, getSubscriber]);
};
function CopilotListenersAgentSubscription() {
const { copilotkit } = (0, _copilotkit_react_core_v2_context.useCopilotKit)();
const configAgentId = useCopilotChatConfiguration()?.agentId;
const { agent } = useAgent({ agentId: (0, react.useMemo)(() => {
const requested = configAgentId ?? _copilotkit_shared.DEFAULT_AGENT_ID;
const registered = copilotkit.agents ?? {};
if (registered[requested]) return requested;
if (requested === _copilotkit_shared.DEFAULT_AGENT_ID) {
const firstRegistered = Object.keys(registered)[0];
if (firstRegistered) return firstRegistered;
}
return requested;
}, [configAgentId, copilotkit.agents]) });
usePredictStateSubscription(agent);
return null;
}
function CopilotListeners() {
const { copilotkit } = (0, _copilotkit_react_core_v2_context.useCopilotKit)();
const { setBannerError } = useToast();
const hasAgents = Object.keys(copilotkit.agents ?? {}).length > 0;
const hasRuntime = copilotkit.runtimeUrl !== void 0;
(0, react.useEffect)(() => {
const subscription = copilotkit.subscribe({ onError: ({ error, code, context }) => {
if (error.name === "AbortError" || error.message === "Fetch is aborted" || error.message === "signal is aborted without reason" || error.message === "component unmounted" || !error.message) return;
if (process.env.NODE_ENV === "development") console.error("[CopilotKit] Agent error:", error.message, "\n Code:", code, "\n Context:", context, "\n Stack:", error.stack);
const ckError = new _copilotkit_shared.CopilotKitLowLevelError({
error,
message: error.message,
url: typeof window !== "undefined" ? window.location.href : ""
});
ckError.details = {
code,
context,
stack: error.stack,
originalMessage: error.message
};
setBannerError(ckError);
} });
return () => {
subscription.unsubscribe();
};
}, [copilotkit?.subscribe]);
return hasAgents || hasRuntime ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CopilotListenersAgentSubscription, {}) : null;
}
//#endregion
//#region src/components/copilot-provider/copilotkit.tsx
function CopilotKit({ children, ...props }) {
const enabled = shouldShowDevConsole(props.showDevConsole);
const showInspector = shouldShowDevConsole(props.enableInspector);
const publicApiKey = props.publicApiKey || props.publicLicenseKey;
const renderArr = (0, react.useMemo)(() => [{ render: CoAgentStateRenderBridge }], []);
const { onError: _onError, ...v2Props } = props;
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ToastProvider, {
enabled,
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CopilotErrorBoundary, {
publicApiKey,
showUsageBanner: enabled,
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ThreadsProvider, {
threadId: props.threadId,
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CopilotKitProvider, {
...v2Props,
showDevConsole: showInspector,
renderCustomMessages: renderArr,
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CopilotKitInternal, {
...props,
children
})
})
})
})
});
}
/**
* Bridge component that subscribes to v2.x copilotkit core error events
* and forwards them to v1.x error handling system.
* This ensures only ONE subscription exists regardless of how many times
* Chat components are rendered.
*/
function CopilotKitErrorBridge() {
const { copilotkit } = (0, _copilotkit_react_core_v2_context.useCopilotKit)();
const { onError, copilotApiConfig } = useCopilotContext();
(0, react.useEffect)(() => {
if (!copilotkit) return;
const subscription = copilotkit.subscribe({ onError: async (event) => {
const errorEvent = {
type: "error",
timestamp: Date.now(),
context: {
source: "agent",
request: {
operation: event.code || "unknown",
url: copilotApiConfig?.chatApiEndpoint,
startTime: Date.now()
},
technical: {
environment: "browser",
userAgent: typeof navigator !== "undefined" ? navigator.userAgent : void 0,
stackTrace: event.error.stack
},
...event.context
},
error: event.error
};
try {
await onError(errorEvent);
} catch (handlerError) {
console.error("Error in onError handler:", handlerError);
}
} });
return () => {
subscription.unsubscribe();
};
}, [
copilotkit,
onError,
copilotApiConfig
]);
return null;
}
function CopilotKitInternal(cpkProps) {
const { children, ...props } = cpkProps;
/**
* This will throw an error if the props are invalid.
*/
validateProps(cpkProps);
const publicApiKey = props.publicLicenseKey || props.publicApiKey;
const chatApiEndpoint = props.runtimeUrl || _copilotkit_shared.COPILOT_CLOUD_CHAT_URL;
const [actions, setActions] = (0, react.useState)({});
const [registeredActionConfigs, setRegisteredActionConfigs] = (0, react.useState)(/* @__PURE__ */ new Map());
const chatComponentsCache = (0, react.useRef)({
actions: {},
coAgentStateRenders: {}
});
const { addElement, removeElement, printTree, getAllElements } = useTree();
const [isLoading, setIsLoading] = (0, react.useState)(false);
const [chatInstructions, setChatInstructions] = (0, react.useState)("");
const [authStates, setAuthStates] = (0, react.useState)({});
const [extensions, setExtensions] = (0, react.useState)({});
const [additionalInstructions, setAdditionalInstructions] = (0, react.useState)([]);
const { addElement: addDocument, removeElement: removeDocument, allElements: allDocuments } = useFlatCategoryStore();
const setAction = (0, react.useCallback)((id, action) => {
setActions((prevPoints) => {
return {
...prevPoints,
[id]: action
};
});
}, []);
const removeAction = (0, react.useCallback)((id) => {
setActions((prevPoints) => {
const newPoints = { ...prevPoints };
delete newPoints[id];
return newPoints;
});
}, []);
const getContextString = (0, react.useCallback)((documents, categories) => {
return `${documents.map((document) => {
return `${document.name} (${document.sourceApplication}):\n${document.getContents()}`;
}).join("\n\n")}\n\n${printTree(categories)}`;
}, [printTree]);
const addContext = (0, react.useCallback)((context, parentId, categories = defaultCopilotContextCategories) => {
return addElement(context, categories, parentId);
}, [addElement]);
const removeContext = (0, react.useCallback)((id) => {
removeElement(id);
}, [removeElement]);
const getAllContext = (0, react.useCallback)(() => {
return getAllElements();
}, [getAllElements]);
const getFunctionCallHandler = (0, react.useCallback)((customEntryPoints) => {
return entryPointsToFunctionCallHandler(Object.values(customEntryPoints || actions));
}, [actions]);
const getDocumentsContext = (0, react.useCallback)((categories) => {
return allDocuments(categories);
}, [allDocuments]);
const addDocumentContext = (0, react.useCallback)((documentPointer, categories = defaultCopilotContextCategories) => {
return addDocument(documentPointer, categories);
}, [addDocument]);
const removeDocumentContext = (0, react.useCallback)((documentId) => {
removeDocument(documentId);
}, [removeDocument]);
const copilotApiConfig = (0, react.useMemo)(() => {
let cloud = void 0;
if (publicApiKey) cloud = { guardrails: { input: { restrictToTopic: {
enabled: Boolean(props.guardrails_c),
validTopics: props.guardrails_c?.validTopics || [],
invalidTopics: props.guardrails_c?.invalidTopics || []
} } } };
return {
publicApiKey,
...cloud ? { cloud } : {},
chatApiEndpoint,
headers: typeof props.headers === "function" ? props.headers() : props.headers || {},
properties: props.properties || {},
transcribeAudioUrl: props.transcribeAudioUrl,
textToSpeechUrl: props.textToSpeechUrl,
credentials: props.credentials
};
}, [
publicApiKey,
props.headers,
props.properties,
props.transcribeAudioUrl,
props.textToSpeechUrl,
props.credentials,
props.cloudRestrictToTopic,
props.guardrails_c
]);
(0, react.useMemo)(() => {
const authHeaders = Object.values(authStates || {}).reduce((acc, state) => {
if (state.status === "authenticated" && state.authHeaders) return {
...acc,
...Object.entries(state.authHeaders).reduce((headers, [key, value]) => ({
...headers,
[key.startsWith("X-Custom-") ? key : `X-Custom-${key}`]: value
}), {})
};
return acc;
}, {});
return {
...copilotApiConfig.headers,
...copilotApiConfig.publicApiKey ? { [_copilotkit_shared.COPILOT_CLOUD_PUBLIC_API_KEY_HEADER]: copilotApiConfig.publicApiKey } : {},
...authHeaders
};
}, [
copilotApiConfig.headers,
copilotApiConfig.publicApiKey,
authStates
]);
const [internalErrorHandlers, _setInternalErrorHandler] = (0, react.useState)({});
const setInternalErrorHandler = (0, react.useCallback)((handler) => {
_setInternalErrorHandler((prev) => ({
...prev,
...handler
}));
}, []);
const removeInternalErrorHandler = (0, react.useCallback)((key) => {
_setInternalErrorHandler((prev) => {
const { [key]: _removed, ...rest } = prev;
return rest;
});
}, []);
const onErrorRef = (0, react.useRef)(props.onError);
(0, react.useEffect)(() => {
onErrorRef.current = props.onError;
}, [props.onError]);
const internalHandlersRef = (0, react.useRef)({});
(0, react.useEffect)(() => {
internalHandlersRef.current = internalErrorHandlers;
}, [internalErrorHandlers]);
const handleErrors = (0, react.useCallback)(async (error) => {
if (copilotApiConfig.publicApiKey && onErrorRef.current) try {
await onErrorRef.current(error);
} catch (e) {
console.error("Error in public onError handler:", e);
}
const handlers = Object.values(internalHandlersRef.current);
await Promise.all(handlers.map((h) => Promise.resolve(h(error)).catch((e) => console.error("Error in internal error handler:", e))));
}, [copilotApiConfig.publicApiKey]);
const [chatSuggestionConfiguration, setChatSuggestionConfiguration] = (0, react.useState)({});
const addChatSuggestionConfiguration = (0, react.useCallback)((id, suggestion) => {
setChatSuggestionConfiguration((prev) => ({
...prev,
[id]: suggestion
}));
}, [setChatSuggestionConfiguration]);
const removeChatSuggestionConfiguration = (0, react.useCallback)((id) => {
setChatSuggestionConfiguration((prev) => {
const { [id]: _, ...rest } = prev;
return rest;
});
}, [setChatSuggestionConfiguration]);
const [availableAgents, setAvailableAgents] = (0, react.useState)([]);
const [coagentStates, setCoagentStates] = (0, react.useState)({});
const coagentStatesRef = (0, react.useRef)({});
const setCoagentStatesWithRef = (0, react.useCallback)((value) => {
const newValue = typeof value === "function" ? value(coagentStatesRef.current) : value;
coagentStatesRef.current = newValue;
setCoagentStates((prev) => {
return newValue;
});
}, []);
let initialAgentSession = null;
if (props.agent) initialAgentSession = { agentName: props.agent };
const [agentSession, setAgentSession] = (0, react.useState)(initialAgentSession);
(0, react.useEffect)(() => {
if (props.agent) setAgentSession({ agentName: props.agent });
else setAgentSession(null);
}, [props.agent]);
const { threadId, setThreadId: setInternalThreadId, isThreadIdExplicit } = useThreads();
const setThreadId = (0, react.useCallback)((value) => {
if (props.threadId) throw new Error("Cannot call setThreadId() when threadId is provided via props.");
setInternalThreadId(value);
}, [props.threadId]);
const [runId, setRunId] = (0, react.useState)(null);
const chatAbortControllerRef = (0, react.useRef)(null);
const showDevConsole = shouldShowDevConsole(props.showDevConsole);
const [interruptActions, _setInterruptActions] = (0, react.useState)({});
const setInterruptAction = (0, react.useCallback)((action) => {
_setInterruptActions((prev) => {
if (action == null || !action.id) return prev;
return {
...prev,
[action.id]: {
...prev[action.id],
...action
}
};
});
}, []);
const removeInterruptAction = (0, react.useCallback)((actionId) => {
_setInterruptActions((prev) => {
const { [actionId]: _, ...rest } = prev;
return rest;
});
}, []);
const [interruptEventQueue, setInterruptEventQueue] = (0, react.useState)({});
const addInterruptEvent = (0, react.useCallback)((queuedEvent) => {
setInterruptEventQueue((prev) => {
const threadQueue = prev[queuedEvent.threadId] || [];
return {
...prev,
[queuedEvent.threadId]: [...threadQueue, queuedEvent]
};
});
}, []);
const resolveInterruptEvent = (0, react.useCallback)((threadId, eventId, response) => {
setInterruptEventQueue((prev) => {
const threadQueue = prev[threadId] || [];
return {
...prev,
[threadId]: threadQueue.map((queuedEvent) => queuedEvent.eventId === eventId ? {
...queuedEvent,
event: {
...queuedEvent.event,
response
}
} : queuedEvent)
};
});
}, []);
const memoizedChildren = (0, react.useMemo)(() => children, [children]);
const [bannerError, setBannerError] = (0, react.useState)(null);
const agentLock = (0, react.useMemo)(() => props.agent ?? null, [props.agent]);
const forwardedParameters = (0, react.useMemo)(() => props.forwardedParameters ?? {}, [props.forwardedParameters]);
const updateExtensions = (0, react.useCallback)((newExtensions) => {
setExtensions((prev) => {
const resolved = typeof newExtensions === "function" ? newExtensions(prev) : newExtensions;
return Object.keys(resolved).length === Object.keys(prev).length && Object.entries(resolved).every(([key, value]) => prev[key] === value) ? prev : resolved;
});
}, [setExtensions]);
const updateAuthStates = (0, react.useCallback)((newAuthStates) => {
setAuthStates((prev) => {
const resolved = typeof newAuthStates === "function" ? newAuthStates(prev) : newAuthStates;
return Object.keys(resolved).length === Object.keys(prev).length && Object.entries(resolved).every(([key, value]) => prev[key] === value) ? prev : resolved;
});
}, [setAuthStates]);
const handleSetRegisteredActions = (0, react.useCallback)((actionConfig) => {
const key = actionConfig.action.name || (0, _copilotkit_shared.randomUUID)();
setRegisteredActionConfigs((prev) => {
const newMap = new Map(prev);
newMap.set(key, actionConfig);
return newMap;
});
return key;
}, []);
const handleRemoveRegisteredAction = (0, react.useCallback)((actionKey) => {
setRegisteredActionConfigs((prev) => {
const newMap = new Map(prev);
newMap.delete(actionKey);
return newMap;
});
}, []);
const RegisteredActionsRenderer = (0, react.useMemo)(() => {
return () => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_jsx_runtime.Fragment, { children: Array.from(registeredActionConfigs.entries()).map(([key, config]) => {
const Component = config.component;
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Component, { action: config.action }, key);
}) });
}, [registeredActionConfigs]);
const copilotContextValue = (0, react.useMemo)(() => ({
actions,
chatComponentsCache,
getFunctionCallHandler,
setAction,
removeAction,
setRegisteredActions: handleSetRegisteredActions,
removeRegisteredAction: handleRemoveRegisteredAction,
getContextString,
addContext,
removeContext,
getAllContext,
getDocumentsContext,
addDocumentContext,
removeDocumentContext,
copilotApiConfig,
isLoading,
setIsLoading,
chatSuggestionConfiguration,
addChatSuggestionConfiguration,
removeChatSuggestionConfiguration,
chatInstructions,
setChatInstructions,
additionalInstructions,
setAdditionalInstructions,
showDevConsole,
coagentStates,
setCoagentStates,
coagentStatesRef,
setCoagentStatesWithRef,
agentSession,
setAgentSession,
forwardedParameters,
agentLock,
threadId,
setThreadId,
runId,
setRunId,
chatAbortControllerRef,
availableAgents,
authConfig_c: props.authConfig_c,
authStates_c: authStates,
setAuthStates_c: updateAuthStates,
extensions,
setExtensions: updateExtensions,
interruptActions,
setInterruptAction,
removeInterruptAction,
interruptEventQueue,
addInterruptEvent,
resolveInterruptEvent,
bannerError,
setBannerError,
onError: handleErrors,
internalErrorHandlers,
setInternalErrorHandler,
removeInternalErrorHandler
}), [
actions,
chatComponentsCache,
getFunctionCallHandler,
setAction,
removeAction,
handleSetRegisteredActions,
handleRemoveRegisteredAction,
getContextString,
addContext,
removeContext,
getAllContext,
getDocumentsContext,
addDocumentContext,
removeDocumentContext,
copilotApiConfig,
isLoading,
chatSuggestionConfiguration,
addChatSuggestionConfiguration,
removeChatSuggestionConfiguration,
chatInstructions,
additionalInstructions,
showDevConsole,
coagentStates,
setCoagentStatesWithRef,
agentSession,
setAgentSession,
forwardedParameters,
agentLock,
threadId,
setThreadId,
runId,
availableAgents,
props.authConfig_c,
authStates,
updateAuthStates,
extensions,
updateExtensions,
interruptActions,
setInterruptAction,
removeInterruptAction,
interruptEventQueue,
addInterruptEvent,
resolveInterruptEvent,
bannerError,
handleErrors,
internalErrorHandlers,
setInternalErrorHandler,
removeInternalErrorHandler
]);
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CopilotChatConfigurationProvider, {
agentId: props.agent ?? "default",
threadId,
hasExplicitThreadId: isThreadIdExplicit,
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(CopilotContext.Provider, {
value: copilotContextValue,
children: [
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(CopilotListeners, {}),
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(CopilotKitErrorBridge, {}),
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)(CoAgentStateRendersProvider, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(MessagesTapProvider, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(CopilotMessages, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(react.default.Fragment, { children: memoizedChildren }, "children"), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RegisteredActionsRenderer, {}, "actions")] }) }), bannerError && showDevConsole && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(UsageBanner, {
severity: bannerError.severity,
message: bannerError.message,
onClose: () => setBannerError(null),
actions: getErrorActions(bannerError)
})] })
]
})
});
}
const defaultCopilotContextCategories = ["global"];
function entryPointsToFunctionCallHandler(actions) {
return async ({ name, args }) => {
let actionsByFunctionName = {};
for (let action of actions) actionsByFunctionName[action.name] = action;
const action = actionsByFunctionName[name];
let result = void 0;
if (action) {
await new Promise((resolve, reject) => {
(0, react_dom.flushSync)(async () => {
try {
result = await action.handler?.(args);
resolve();
} catch (error) {
reject(error);
}
});
});
await new Promise((resolve) => setTimeout(resolve, 20));
}
return result;
};
}
function formatFeatureName(featureName) {
return featureName.replace(/_c$/, "").split("_").map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()).join(" ");
}
function validateProps(props) {
const cloudFeatures = Object.keys(props).filter((key) => key.endsWith("_c"));
const hasApiKey = props.publicApiKey || props.publicLicenseKey;
const hasLocalAgents = Object.keys({
...props.agents__unsafe_dev_only,
...props.selfManagedAgents
}).length > 0;
if (!props.runtimeUrl && !hasApiKey && !hasLocalAgents) throw new _copilotkit_shared.ConfigurationError("Missing required prop: 'runtimeUrl' or 'publicApiKey' or 'publicLicenseKey'");
if (cloudFeatures.length > 0 && !hasApiKey) throw new _copilotkit_shared.MissingPublicApiKeyError(`Missing required prop: 'publicApiKey' or 'publicLicenseKey' to use cloud features: ${cloudFeatures.map(formatFeatureName).join(", ")}`);
}
//#endregion
Object.defineProperty(exports, 'AudioRecorderError', {
enumerable: true,
get: function () {
return AudioRecorderError;
}
});
Object.defineProperty(exports, 'CoAgentStateRenderBridge', {
enumerable: true,
get: function () {
return CoAgentStateRenderBridge;
}
});
Object.defineProperty(exports, 'CoAgentStateRendersContext', {
enumerable: true,
get: function () {
return CoAgentStateRendersContext;
}
});
Object.defineProperty(exports, 'CoAgentStateRendersProvider', {
enumerable: true,
get: function () {
return CoAgentStateRendersProvider;
}
});
Object.defineProperty(exports, 'CopilotChat', {
enumerable: true,
get: function () {
return CopilotChat;
}
});
Object.defineProperty(exports, 'CopilotChatAssistantMessage_default', {
enumerable: true,
get: function () {
return CopilotChatAssistantMessage_default;
}
});
Object.defineProperty(exports, 'CopilotChatAttachmentQueue', {
enumerable: true,
get: function () {
return CopilotChatAttachmentQueue;
}
});
Object.defineProperty(exports, 'CopilotChatAttachmentRenderer', {
enumerable: true,
get: function () {
return CopilotChatAttachmentRenderer;
}
});
Object.defineProperty(exports, 'CopilotChatAudioRecorder', {
enumerable: true,
get: function () {
return CopilotChatAudioRecorder;
}
});
Object.defineProperty(exports, 'CopilotChatConfigurationProvider', {
enumerable: true,
get: function () {
return CopilotChatConfigurationProvider;
}
});
Object.defineProperty(exports, 'CopilotChatInput_default', {
enumerable: true,
get: function () {
return CopilotChatInput_default;
}
});
Object.defineProperty(exports, 'CopilotChatMessageView', {
enumerable: true,
get: function () {
return CopilotChatMessageView;
}
});
Object.defineProperty(exports, 'CopilotChatReasoningMessage_default', {
enumerable: true,
get: function () {
return CopilotChatReasoningMessage_default;
}
});
Object.defineProperty(exports, 'CopilotChatSuggestionPill', {
enumerable: true,
get: function () {
return CopilotChatSuggestionPill;
}
});
Object.defineProperty(exports, 'CopilotChatSuggestionView', {
enumerable: true,
get: function () {
return CopilotChatSuggestionView;
}
});
Object.defineProperty(exports, 'CopilotChatToggleButton', {
enumerable: true,
get: function () {
return CopilotChatToggleButton;
}
});
Object.defineProperty(exports, 'CopilotChatToolCallsView', {
enumerable: true,
get: function () {
return CopilotChatToolCallsView;
}
});
Object.defineProperty(exports, 'CopilotChatUserMessage_default', {
enumerable: true,
get: function () {
return CopilotChatUserMessage_default;
}
});
Object.defineProperty(exports, 'CopilotChatView_default', {
enumerable: true,
get: function () {
return CopilotChatView_default;
}
});
Object.defineProperty(exports, 'CopilotContext', {
enumerable: true,
get: function () {
return CopilotContext;
}
});
Object.defineProperty(exports, 'CopilotKit', {
enumerable: true,
get: function () {
return CopilotKit;
}
});
Object.defineProperty(exports, 'CopilotKitCoreReact', {
enumerable: true,
get: function () {
return CopilotKitCoreReact;
}
});
Object.defineProperty(exports, 'CopilotKitInspector', {
enumerable: true,
get: function () {
return CopilotKitInspector;
}
});
Object.defineProperty(exports, 'CopilotKitProvider', {
enumerable: true,
get: function () {
return CopilotKitProvider;
}
});
Object.defineProperty(exports, 'CopilotMessagesContext', {
enumerable: true,
get: function () {
return CopilotMessagesContext;
}
});
Object.defineProperty(exports, 'CopilotModalHeader', {
enumerable: true,
get: function () {
return CopilotModalHeader;
}
});
Object.defineProperty(exports, 'CopilotPopup', {
enumerable: true,
get: function () {
return CopilotPopup;
}
});
Object.defineProperty(exports, 'CopilotPopupView', {
enumerable: true,
get: function () {
return CopilotPopupView;
}
});
Object.defineProperty(exports, 'CopilotSidebar', {
enumerable: true,
get: function () {
return CopilotSidebar;
}
});
Object.defineProperty(exports, 'CopilotSidebarView', {
enumerable: true,
get: function () {
return CopilotSidebarView;
}
});
Object.defineProperty(exports, 'CopilotThreadsDrawer', {
enumerable: true,
get: function () {
return CopilotThreadsDrawer;
}
});
Object.defineProperty(exports, 'DefaultCloseIcon', {
enumerable: true,
get: function () {
return DefaultCloseIcon;
}
});
Object.defineProperty(exports, 'DefaultOpenIcon', {
enumerable: true,
get: function () {
return DefaultOpenIcon;
}
});
Object.defineProperty(exports, 'GenerateSandboxedUiArgsSchema', {
enumerable: true,
get: function () {
return GenerateSandboxedUiArgsSchema;
}
});
Object.defineProperty(exports, 'INTELLIGENCE_TURN_HEAD', {
enumerable: true,
get: function () {
return INTELLIGENCE_TURN_HEAD;
}
});
Object.defineProperty(exports, 'IntelligenceIndicator', {
enumerable: true,
get: function () {
return IntelligenceIndicator;
}
});
Object.defineProperty(exports, 'IntelligenceIndicatorView', {
enumerable: true,
get: function () {
return IntelligenceIndicatorView;
}
});
Object.defineProperty(exports, 'MCPAppsActivityContentSchema', {
enumerable: true,
get: function () {
return MCPAppsActivityContentSchema;
}
});
Object.defineProperty(exports, 'MCPAppsActivityRenderer', {
enumerable: true,
get: function () {
return MCPAppsActivityRenderer;
}
});
Object.defineProperty(exports, 'MCPAppsActivityType', {
enumerable: true,
get: function () {
return MCPAppsActivityType;
}
});
Object.defineProperty(exports, 'OpenGenerativeUIActivityRenderer', {
enumerable: true,
get: function () {
return OpenGenerativeUIActivityRenderer;
}
});
Object.defineProperty(exports, 'OpenGenerativeUIActivityType', {
enumerable: true,
get: function () {
return OpenGenerativeUIActivityType;
}
});
Object.defineProperty(exports, 'OpenGenerativeUIContentSchema', {
enumerable: true,
get: function () {
return OpenGenerativeUIContentSchema;
}
});
Object.defineProperty(exports, 'OpenGenerativeUIToolRenderer', {
enumerable: true,
get: function () {
return OpenGenerativeUIToolRenderer;
}
});
Object.defineProperty(exports, 'SandboxFunctionsContext', {
enumerable: true,
get: function () {
return SandboxFunctionsContext;
}
});
Object.defineProperty(exports, 'ThreadsContext', {
enumerable: true,
get: function () {
return ThreadsContext;
}
});
Object.defineProperty(exports, 'ThreadsProvider', {
enumerable: true,
get: function () {
return ThreadsProvider;
}
});
Object.defineProperty(exports, 'UseAgentUpdate', {
enumerable: true,
get: function () {
return UseAgentUpdate;
}
});
Object.defineProperty(exports, 'WildcardToolCallRender', {
enumerable: true,
get: function () {
return WildcardToolCallRender;
}
});
Object.defineProperty(exports, '__toESM', {
enumerable: true,
get: function () {
return __toESM;
}
});
Object.defineProperty(exports, 'createA2UIMessageRenderer', {
enumerable: true,
get: function () {
return createA2UIMessageRenderer;
}
});
Object.defineProperty(exports, 'defaultCopilotContextCategories', {
enumerable: true,
get: function () {
return defaultCopilotContextCategories;
}
});
Object.defineProperty(exports, 'defineToolCallRenderer', {
enumerable: true,
get: function () {
return defineToolCallRenderer;
}
});
Object.defineProperty(exports, 'getIntelligenceTurnAnchors', {
enumerable: true,
get: function () {
return getIntelligenceTurnAnchors;
}
});
Object.defineProperty(exports, 'shouldShowDevConsole', {
enumerable: true,
get: function () {
return shouldShowDevConsole;
}
});
Object.defineProperty(exports, 'useAgent', {
enumerable: true,
get: function () {
return useAgent;
}
});
Object.defineProperty(exports, 'useAgentContext', {
enumerable: true,
get: function () {
return useAgentContext;
}
});
Object.defineProperty(exports, 'useAsyncCallback', {
enumerable: true,
get: function () {
return useAsyncCallback;
}
});
Object.defineProperty(exports, 'useAttachments', {
enumerable: true,
get: function () {
return useAttachments;
}
});
Object.defineProperty(exports, 'useCapabilities', {
enumerable: true,
get: function () {
return useCapabilities;
}
});
Object.defineProperty(exports, 'useCoAgentStateRenders', {
enumerable: true,
get: function () {
return useCoAgentStateRenders;
}
});
Object.defineProperty(exports, 'useComponent', {
enumerable: true,
get: function () {
return useComponent;
}
});
Object.defineProperty(exports, 'useConfigureSuggestions', {
enumerable: true,
get: function () {
return useConfigureSuggestions;
}
});
Object.defineProperty(exports, 'useCopilotChatConfiguration', {
enumerable: true,
get: function () {
return useCopilotChatConfiguration;
}
});
Object.defineProperty(exports, 'useCopilotContext', {
enumerable: true,
get: function () {
return useCopilotContext;
}
});
Object.defineProperty(exports, 'useCopilotMessagesContext', {
enumerable: true,
get: function () {
return useCopilotMessagesContext;
}
});
Object.defineProperty(exports, 'useDefaultRenderTool', {
enumerable: true,
get: function () {
return useDefaultRenderTool;
}
});
Object.defineProperty(exports, 'useFrontendTool', {
enumerable: true,
get: function () {
return useFrontendTool;
}
});
Object.defineProperty(exports, 'useHumanInTheLoop', {
enumerable: true,
get: function () {
return useHumanInTheLoop;
}
});
Object.defineProperty(exports, 'useInterrupt', {
enumerable: true,
get: function () {
return useInterrupt;
}
});
Object.defineProperty(exports, 'useLearnFromUserAction', {
enumerable: true,
get: function () {
return useLearnFromUserAction;
}
});
Object.defineProperty(exports, 'useLearnFromUserActionInCurrentThread', {
enumerable: true,
get: function () {
return useLearnFromUserActionInCurrentThread;
}
});
Object.defineProperty(exports, 'useLearningContainers', {
enumerable: true,
get: function () {
return useLearningContainers;
}
});
Object.defineProperty(exports, 'useLearningContainersInCurrentThread', {
enumerable: true,
get: function () {
return useLearningContainersInCurrentThread;
}
});
Object.defineProperty(exports, 'useMemories', {
enumerable: true,
get: function () {
return useMemories;
}
});
Object.defineProperty(exports, 'useRenderActivityMessage', {
enumerable: true,
get: function () {
return useRenderActivityMessage;
}
});
Object.defineProperty(exports, 'useRenderCustomMessages', {
enumerable: true,
get: function () {
return useRenderCustomMessages;
}
});
Object.defineProperty(exports, 'useRenderTool', {
enumerable: true,
get: function () {
return useRenderTool;
}
});
Object.defineProperty(exports, 'useRenderToolCall', {
enumerable: true,
get: function () {
return useRenderToolCall;
}
});
Object.defineProperty(exports, 'useSandboxFunctions', {
enumerable: true,
get: function () {
return useSandboxFunctions;
}
});
Object.defineProperty(exports, 'useSuggestions', {
enumerable: true,
get: function () {
return useSuggestions;
}
});
Object.defineProperty(exports, 'useThreads', {
enumerable: true,
get: function () {
return useThreads;
}
});
Object.defineProperty(exports, 'useThreads$1', {
enumerable: true,
get: function () {
return useThreads$1;
}
});
Object.defineProperty(exports, 'useToast', {
enumerable: true,
get: function () {
return useToast;
}
});
Object.defineProperty(exports, 'ɵrunMcpFollowUp', {
enumerable: true,
get: function () {
return ɵrunMcpFollowUp;
}
});
//# sourceMappingURL=copilotkit-DOMr3DRO.cjs.map