UNPKG

@copilotkit/react-core

Version:

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

1,370 lines (1,355 loc) 263 kB
"use client"; (function(global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('react'), require('@copilotkit/core'), require('@ag-ui/client'), require('tailwind-merge'), require('@copilotkit/shared'), require('react/jsx-runtime'), require('zod'), require('@lit-labs/react'), require('@copilotkit/a2ui-renderer'), require('zod-to-json-schema'), require('react-dom'), require('react-markdown'), require('@copilotkit/runtime-client-gql')) : typeof define === 'function' && define.amd ? define(['exports', 'react', '@copilotkit/core', '@ag-ui/client', 'tailwind-merge', '@copilotkit/shared', 'react/jsx-runtime', 'zod', '@lit-labs/react', '@copilotkit/a2ui-renderer', 'zod-to-json-schema', 'react-dom', 'react-markdown', '@copilotkit/runtime-client-gql'], factory) : (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory((global.CopilotKitReactCore = {}), global.React,global.CopilotKitCore,global.AgUIClient,global.tailwind_merge,global.CopilotKitShared,global.ReactJsxRuntime,global.Zod,global._lit_labs_react,global.CopilotKitA2UIRenderer,global.zod_to_json_schema,global.ReactDOM,global.ReactMarkdown,global.CopilotKitRuntimeClientGQL)); })(this, function(exports, react, _copilotkit_core, _ag_ui_client, tailwind_merge, _copilotkit_shared, react_jsx_runtime, zod, _lit_labs_react, _copilotkit_a2ui_renderer, zod_to_json_schema, react_dom, react_markdown, _copilotkit_runtime_client_gql) { Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); //#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 react = __toESM(react); react_markdown = __toESM(react_markdown); //#region src/v2/lib/slots.tsx /** * 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; } /** * 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); }); //#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", 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?" }; 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 resolvedThreadId = (0, react.useMemo)(() => { if (threadId) return threadId; if (parentConfig?.threadId) return parentConfig.threadId; return (0, _copilotkit_shared.randomUUID)(); }, [threadId, parentConfig?.threadId]); const resolvedHasExplicitThreadId = (hasExplicitThreadId !== void 0 ? hasExplicitThreadId : !!threadId) || !!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 configurationValue = (0, react.useMemo)(() => ({ labels: mergedLabels, agentId: resolvedAgentId, threadId: resolvedThreadId, hasExplicitThreadId: resolvedHasExplicitThreadId, isModalOpen: resolvedIsModalOpen, setModalOpen: resolvedSetModalOpen }), [ mergedLabels, resolvedAgentId, resolvedThreadId, resolvedHasExplicitThreadId, resolvedIsModalOpen, resolvedSetModalOpen ]); return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CopilotChatConfiguration.Provider, { value: configurationValue, children }); }; const useCopilotChatConfiguration = () => { return (0, react.useContext)(CopilotChatConfiguration); }; //#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/context.ts const CopilotKitContext = (0, react.createContext)(null); const useCopilotKit = () => { const context = (0, react.useContext)(CopilotKitContext); const [, forceUpdate] = (0, react.useReducer)((x) => x + 1, 0); if (!context) throw new Error("useCopilotKit must be used within CopilotKitProvider"); (0, react.useEffect)(() => { const subscription = context.copilotkit.subscribe({ onRuntimeConnectionStatusChanged: () => { forceUpdate(); } }); return () => { subscription.unsubscribe(); }; }, []); return context; }; const LicenseContext = (0, react.createContext)({ status: null, license: null, checkFeature: () => true, getLimit: () => 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/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"; } } } /** * 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 dotColor = isActive ? "#f59e0b" : isComplete ? "#10b981" : "#a1a1aa"; const badgeBg = isActive ? "#fef3c7" : isComplete ? "#d1fae5" : "#f4f4f5"; const badgeColor = isActive ? "#92400e" : isComplete ? "#065f46" : "#3f3f46"; 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), style: { marginTop: "8px", paddingBottom: "8px" }, children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { style: { borderRadius: "12px", border: "1px solid #e4e4e7", backgroundColor: "#fafafa", padding: "14px 16px" }, children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", { type: "button", "aria-expanded": isExpanded, onClick: () => setIsExpanded(!isExpanded), style: { display: "flex", alignItems: "center", justifyContent: "space-between", gap: "10px", cursor: "pointer", userSelect: "none", width: "100%", border: "none", padding: 0, margin: 0, background: "transparent", textAlign: "left", font: "inherit", color: "inherit" }, children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: "8px", minWidth: 0 }, children: [ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", { style: { height: "14px", width: "14px", color: "#71717a", transition: "transform 0.15s", transform: isExpanded ? "rotate(90deg)" : "rotate(0deg)", flexShrink: 0 }, 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", { style: { display: "inline-block", height: "8px", width: "8px", borderRadius: "50%", backgroundColor: dotColor, flexShrink: 0 } }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { "data-testid": "copilot-tool-render-name", style: { fontSize: "13px", fontWeight: 600, color: "#18181b", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }, children: name }) ] }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { "data-testid": "copilot-tool-render-status", style: { display: "inline-flex", alignItems: "center", borderRadius: "9999px", padding: "2px 8px", fontSize: "11px", fontWeight: 500, backgroundColor: badgeBg, color: badgeColor, flexShrink: 0 }, children: statusLabel })] }), isExpanded && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { style: { marginTop: "12px", display: "grid", gap: "12px" }, children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { style: { fontSize: "10px", textTransform: "uppercase", letterSpacing: "0.05em", color: "#71717a" }, children: "Arguments" }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("pre", { style: { marginTop: "6px", maxHeight: "200px", overflow: "auto", borderRadius: "6px", backgroundColor: "#f4f4f5", padding: "10px", fontSize: "11px", lineHeight: 1.6, color: "#27272a", whiteSpace: "pre-wrap", wordBreak: "break-word" }, children: safeStringifyForPre(parameters ?? {}) })] }), result !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { style: { fontSize: "10px", textTransform: "uppercase", letterSpacing: "0.05em", color: "#71717a" }, children: "Result" }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("pre", { style: { marginTop: "6px", maxHeight: "200px", overflow: "auto", borderRadius: "6px", backgroundColor: "#f4f4f5", padding: "10px", fontSize: "11px", lineHeight: 1.6, color: "#27272a", whiteSpace: "pre-wrap", wordBreak: "break-word" }, 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-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$1() { const { copilotkit, executingToolCallIds } = 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); return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ToolCallRenderer, { toolCall, toolMessage, RenderComponent: (exactMatches.find((rc) => rc.agentId === agentId) || exactMatches.find((rc) => !rc.agentId) || exactMatches[0] || renderToolCalls.find((rc) => rc.name === "*"))?.render ?? defaultToolCallRenderAdapter, isExecuting: executingToolCallIds.has(toolCall.id) }, toolCall.id); }, [ renderToolCalls, executingToolCallIds, agentId ]); } function defaultToolCallRenderAdapter(props) { return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DefaultToolCallRenderer, { name: props.name, toolCallId: props.toolCallId, parameters: props.args, status: mapToolCallStatus(props.status), result: props.result }); } //#endregion //#region src/v2/components/CopilotKitInspector.tsx const CopilotKitInspector = ({ core, ...rest }) => { const [InspectorComponent, setInspectorComponent] = react.useState(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; }; }, []); if (!InspectorComponent) return null; return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(InspectorComponent, { ...rest, core: core ?? null }); }; CopilotKitInspector.displayName = "CopilotKitInspector"; //#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://cloud.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; } } //#endregion //#region src/v2/components/MCPAppsActivityRenderer.tsx 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 } = 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"); 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) mcpAppsRequestQueue.enqueue(currentAgent, () => copilotkit.runAgent({ agent: currentAgent })).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) {