UNPKG

@copilotkit/runtime

Version:

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

988 lines (986 loc) 36.6 kB
require("reflect-metadata"); const require_runtime = require('../_virtual/_rolldown/runtime.cjs'); const require_aisdk = require('./converters/aisdk.cjs'); const require_tanstack = require('./converters/tanstack.cjs'); let _ai_sdk_openai = require("@ai-sdk/openai"); let _copilotkit_shared = require("@copilotkit/shared"); let rxjs = require("rxjs"); let _ag_ui_client = require("@ag-ui/client"); let ai = require("ai"); let _ai_sdk_mcp = require("@ai-sdk/mcp"); let _ai_sdk_anthropic = require("@ai-sdk/anthropic"); let _ai_sdk_google = require("@ai-sdk/google"); let _ai_sdk_google_vertex = require("@ai-sdk/google-vertex"); let zod = require("zod"); let _modelcontextprotocol_sdk_client_streamableHttp_js = require("@modelcontextprotocol/sdk/client/streamableHttp.js"); let _modelcontextprotocol_sdk_client_sse_js = require("@modelcontextprotocol/sdk/client/sse.js"); //#region src/agent/index.ts /** * Resolves a model specifier to a LanguageModel instance * @param spec - Model string (e.g., "openai/gpt-4o") or LanguageModel instance * @param apiKey - Optional API key to use instead of environment variables * @returns LanguageModel instance */ function resolveModel(spec, apiKey) { if (typeof spec !== "string") return spec; const parts = spec.replace("/", ":").trim().split(":"); const rawProvider = parts[0]; const rest = parts.slice(1); if (!rawProvider) throw new Error(`Invalid model string "${spec}". Use "openai/gpt-5", "anthropic/claude-sonnet-4.5", or "google/gemini-2.5-pro".`); const provider = rawProvider.toLowerCase(); const model = rest.join(":").trim(); if (!model) throw new Error(`Invalid model string "${spec}". Use "openai/gpt-5", "anthropic/claude-sonnet-4.5", or "google/gemini-2.5-pro".`); switch (provider) { case "openai": return (0, _ai_sdk_openai.createOpenAI)({ apiKey: apiKey || process.env.OPENAI_API_KEY })(model); case "anthropic": return (0, _ai_sdk_anthropic.createAnthropic)({ apiKey: apiKey || process.env.ANTHROPIC_API_KEY })(model); case "google": case "gemini": case "google-gemini": return (0, _ai_sdk_google.createGoogleGenerativeAI)({ apiKey: apiKey || process.env.GOOGLE_API_KEY })(model); case "vertex": return (0, _ai_sdk_google_vertex.createVertex)()(model); default: throw new Error(`Unknown provider "${provider}" in "${spec}". Supported: openai, anthropic, google (gemini).`); } } /** * Thrown by `AgentFactoryContext.interrupt()` on a fresh (non-resume) run to * pause the factory. Caught in `runFactory` and translated into a RUN_FINISHED * event carrying `outcome:{type:"interrupt",interrupts}`. Not a real error. */ var InterruptSignal = class extends Error { constructor(interrupts) { super("CopilotKit interrupt: run paused awaiting human input"); this.interrupts = interrupts; this.name = "InterruptSignal"; } }; /** * Define a tool for use with BuiltInAgent * @param name - The name of the tool * @param description - Description of what the tool does * @param parameters - Schema for the tool's input parameters (any Standard Schema V1 compatible library: Zod, Valibot, ArkType, etc.) * @param execute - Function to execute the tool server-side * @returns Tool definition */ function defineTool(config) { return { name: config.name, description: config.description, parameters: config.parameters, execute: config.execute, interrupt: config.interrupt, interruptReason: config.interruptReason, interruptMessage: config.interruptMessage }; } /** * Converts AG-UI user message content to Vercel AI SDK UserContent format. * Handles plain strings, new modality-specific parts (image/audio/video/document), * and legacy BinaryInputContent for backward compatibility. */ function convertUserMessageContent(content) { if (!content) return ""; if (typeof content === "string") return content; const parts = []; for (const part of content) { if (!part || typeof part !== "object" || !("type" in part)) continue; switch (part.type) { case "text": { const text = part.text; if (text) parts.push({ type: "text", text }); break; } case "image": { const source = part.source; if (!source) break; if (source.type === "data") parts.push({ type: "image", image: source.value, mediaType: source.mimeType }); else if (source.type === "url") try { parts.push({ type: "image", image: new URL(source.value), mediaType: source.mimeType }); } catch { console.error(`[CopilotKit] convertUserMessageContent: invalid URL "${source.value}" in image part — skipping`); } break; } case "audio": case "video": case "document": { const source = part.source; if (!source) break; if (source.type === "data") parts.push({ type: "file", data: source.value, mediaType: source.mimeType }); else if (source.type === "url") try { parts.push({ type: "file", data: new URL(source.value), mediaType: source.mimeType ?? "application/octet-stream" }); } catch { console.error(`[CopilotKit] convertUserMessageContent: invalid URL "${source.value}" in ${part.type} part — skipping`); } break; } case "binary": { const legacy = part; const mimeType = legacy.mimeType ?? "application/octet-stream"; const isImage = mimeType.startsWith("image/"); if (legacy.data) if (isImage) parts.push({ type: "image", image: legacy.data, mediaType: mimeType }); else parts.push({ type: "file", data: legacy.data, mediaType: mimeType }); else if (legacy.url) try { const url = new URL(legacy.url); if (isImage) parts.push({ type: "image", image: url, mediaType: mimeType }); else parts.push({ type: "file", data: url, mediaType: mimeType }); } catch { console.error(`[CopilotKit] convertUserMessageContent: invalid URL "${legacy.url}" in binary part — skipping`); } break; } default: console.error(`[CopilotKit] convertUserMessageContent: unrecognized content part type "${part.type}" — skipping`); break; } } return parts.length > 0 ? parts : ""; } /** * Converts AG-UI messages to Vercel AI SDK ModelMessage format */ function convertMessagesToVercelAISDKMessages(messages, options = {}) { const result = []; for (const message of messages) if (message.role === "system" && options.forwardSystemMessages) { const systemMsg = { role: "system", content: message.content ?? "" }; result.push(systemMsg); } else if (message.role === "developer" && options.forwardDeveloperMessages) { const systemMsg = { role: "system", content: message.content ?? "" }; result.push(systemMsg); } else if (message.role === "assistant") { const parts = message.content ? [{ type: "text", text: message.content }] : []; for (const toolCall of message.toolCalls ?? []) { const toolCallPart = { type: "tool-call", toolCallId: toolCall.id, toolName: toolCall.function.name, input: (0, _copilotkit_shared.safeParseToolArgs)(toolCall.function.arguments) }; parts.push(toolCallPart); } const assistantMsg = { role: "assistant", content: parts }; result.push(assistantMsg); } else if (message.role === "user") { const userMsg = { role: "user", content: convertUserMessageContent(message.content) }; result.push(userMsg); } else if (message.role === "tool") { let toolName = "unknown"; for (const msg of messages) if (msg.role === "assistant") { for (const toolCall of msg.toolCalls ?? []) if (toolCall.id === message.toolCallId) { toolName = toolCall.function.name; break; } } const toolMsg = { role: "tool", content: [{ type: "tool-result", toolCallId: message.toolCallId, toolName, output: { type: "text", value: message.content } }] }; result.push(toolMsg); } return result; } /** * Converts JSON Schema to Zod schema */ function convertJsonSchemaToZodSchema(jsonSchema, required) { if (!jsonSchema.type) return required ? zod.z.object({}) : zod.z.object({}).optional(); if (jsonSchema.type === "object") { const spec = {}; if (!jsonSchema.properties || !Object.keys(jsonSchema.properties).length) return !required ? zod.z.object(spec).optional() : zod.z.object(spec); for (const [key, value] of Object.entries(jsonSchema.properties)) spec[key] = convertJsonSchemaToZodSchema(value, jsonSchema.required ? jsonSchema.required.includes(key) : false); const schema = zod.z.object(spec).describe(jsonSchema.description ?? ""); return required ? schema : schema.optional(); } else if (jsonSchema.type === "string") { if (jsonSchema.enum && jsonSchema.enum.length > 0) { const schema = zod.z.enum(jsonSchema.enum).describe(jsonSchema.description ?? ""); return required ? schema : schema.optional(); } const schema = zod.z.string().describe(jsonSchema.description ?? ""); return required ? schema : schema.optional(); } else if (jsonSchema.type === "number" || jsonSchema.type === "integer") { const schema = zod.z.number().describe(jsonSchema.description ?? ""); return required ? schema : schema.optional(); } else if (jsonSchema.type === "boolean") { const schema = zod.z.boolean().describe(jsonSchema.description ?? ""); return required ? schema : schema.optional(); } else if (jsonSchema.type === "array") { if (!jsonSchema.items) throw new Error("Array type must have items property"); const itemSchema = convertJsonSchemaToZodSchema(jsonSchema.items, true); const schema = zod.z.array(itemSchema).describe(jsonSchema.description ?? ""); return required ? schema : schema.optional(); } console.error("Invalid JSON schema:", JSON.stringify(jsonSchema, null, 2)); throw new Error("Invalid JSON schema"); } /** * Converts AG-UI tools to Vercel AI SDK ToolSet */ function isJsonSchema(obj) { if (typeof obj !== "object" || obj === null) return false; const schema = obj; if (Object.keys(schema).length === 0) return true; return typeof schema.type === "string" && [ "object", "string", "number", "integer", "boolean", "array" ].includes(schema.type); } /** * Type-only pass-through for handing a Zod schema to the AI SDK's `tool()`. * The raw Zod schema is returned unchanged at runtime — the SDK's `asSchema()` * converts and validates it internally exactly as before. * * The Zod type is erased through `unknown` deliberately: letting tsc relate * Zod schema types to the AI SDK's `FlexibleSchema` union (conditional types * spanning zod v3/v4) makes type instantiation explode (TS2589 / compiler * OOM) under this package's `moduleResolution: node`. */ function toLanguageModelSchema(schema) { return schema; } function convertToolsToVercelAITools(tools) { const result = {}; for (const tool of tools) { if (!isJsonSchema(tool.parameters)) throw new Error(`Invalid JSON schema for tool ${tool.name}`); const zodSchema = convertJsonSchemaToZodSchema(tool.parameters, true); result[tool.name] = (0, ai.tool)({ description: tool.description, inputSchema: toLanguageModelSchema(zodSchema) }); } return result; } /** * Check whether a schema is a Zod schema by inspecting its Standard Schema vendor. */ function isZodSchema(schema) { return schema["~standard"]?.vendor === "zod"; } /** * Converts ToolDefinition array to Vercel AI SDK ToolSet. * * For Zod schemas, passes them directly to the AI SDK (Zod satisfies FlexibleSchema). * For non-Zod schemas, converts to JSON Schema via schemaToJsonSchema() and wraps * with the AI SDK's jsonSchema() helper. */ function convertToolDefinitionsToVercelAITools(tools) { const result = {}; for (const tool of tools) if (isZodSchema(tool.parameters)) result[tool.name] = (0, ai.tool)({ description: tool.description, inputSchema: tool.parameters, execute: tool.execute }); else { const jsonSchemaObj = (0, _copilotkit_shared.schemaToJsonSchema)(tool.parameters); result[tool.name] = (0, ai.tool)({ description: tool.description, inputSchema: (0, ai.jsonSchema)(jsonSchemaObj), execute: tool.execute }); } return result; } /** * Type guard: returns true if this is a factory-mode config. */ function isFactoryConfig(config) { return "factory" in config; } var BuiltInAgent = class BuiltInAgent extends _ag_ui_client.AbstractAgent { constructor(config) { super(); this.config = config; } /** * Check if a property can be overridden by forwardedProps */ canOverride(property) { if (isFactoryConfig(this.config)) return false; return this.config?.overridableProperties?.includes(property) ?? false; } async getCapabilities() { const inferred = { tools: { supported: true, clientProvided: true }, transport: { streaming: true }, humanInTheLoop: { interrupts: true } }; const capabilities = isFactoryConfig(this.config) ? void 0 : this.config.capabilities; if (!capabilities) return inferred; return { ...inferred, ...capabilities }; } run(input) { if (isFactoryConfig(this.config)) return this.runFactory(input, this.config); const config = this.config; if (this.abortController) throw new Error("Agent is already running. Call abortRun() first or create a new instance."); this.abortController = new AbortController(); const abortController = this.abortController; return new rxjs.Observable((subscriber) => { const startEvent = { type: _ag_ui_client.EventType.RUN_STARTED, threadId: input.threadId, runId: input.runId }; subscriber.next(startEvent); const model = resolveModel(config.model, config.apiKey); let systemPrompt = void 0; const hasPrompt = !!config.prompt; const hasContext = input.context && input.context.length > 0; const hasState = input.state !== void 0 && input.state !== null && !(typeof input.state === "object" && Object.keys(input.state).length === 0); if (hasPrompt || hasContext || hasState) { const parts = []; if (hasPrompt) parts.push(config.prompt); if (hasContext) { parts.push("\n## Context from the application\n"); for (const ctx of input.context) parts.push(`${ctx.description}:\n${ctx.value}\n`); } if (hasState) parts.push(` ## Application State This is state from the application that you can edit by calling AGUISendStateSnapshot or AGUISendStateDelta. \`\`\`json\n${JSON.stringify(input.state, null, 2)}\n\`\`\`\n`); systemPrompt = parts.join(""); } const messages = convertMessagesToVercelAISDKMessages(input.messages, { forwardSystemMessages: config.forwardSystemMessages, forwardDeveloperMessages: config.forwardDeveloperMessages }); if (systemPrompt) messages.unshift({ role: "system", content: systemPrompt }); const resumeEntries = input.resume ?? []; if (resumeEntries.length > 0) { const toolNameById = /* @__PURE__ */ new Map(); for (const m of input.messages) { if (m.role !== "assistant") continue; for (const tc of m.toolCalls ?? []) if (tc.id && tc.function?.name) toolNameById.set(tc.id, tc.function.name); } const alreadyAnswered = /* @__PURE__ */ new Set(); for (const m of messages) { if (m.role !== "tool" || !Array.isArray(m.content)) continue; for (const part of m.content) if (part && typeof part === "object" && "toolCallId" in part) alreadyAnswered.add(part.toolCallId); } for (const entry of resumeEntries) { if (alreadyAnswered.has(entry.interruptId)) continue; const value = entry.status === "cancelled" ? { status: "cancelled" } : entry.payload ?? { status: "resolved" }; const toolResultMessage = { role: "tool", content: [{ type: "tool-result", toolCallId: entry.interruptId, toolName: toolNameById.get(entry.interruptId) ?? "", output: { type: "json", value } }] }; messages.push(toolResultMessage); } } let allTools = convertToolsToVercelAITools(input.tools); if (config.tools && config.tools.length > 0) { const configTools = convertToolDefinitionsToVercelAITools(config.tools); allTools = { ...allTools, ...configTools }; } const streamTextParams = { model, messages, tools: allTools, toolChoice: config.toolChoice, stopWhen: config.maxSteps ? (0, ai.stepCountIs)(config.maxSteps) : void 0, maxOutputTokens: config.maxOutputTokens, temperature: config.temperature, topP: config.topP, topK: config.topK, presencePenalty: config.presencePenalty, frequencyPenalty: config.frequencyPenalty, stopSequences: config.stopSequences, seed: config.seed, providerOptions: config.providerOptions, maxRetries: config.maxRetries }; if (input.forwardedProps && typeof input.forwardedProps === "object") { const props = input.forwardedProps; if (props.model !== void 0 && this.canOverride("model")) { if (typeof props.model === "string" || typeof props.model === "object") streamTextParams.model = resolveModel(props.model, config.apiKey); } if (props.toolChoice !== void 0 && this.canOverride("toolChoice")) { const toolChoice = props.toolChoice; if (toolChoice === "auto" || toolChoice === "required" || toolChoice === "none" || typeof toolChoice === "object" && toolChoice !== null && "type" in toolChoice && toolChoice.type === "tool") streamTextParams.toolChoice = toolChoice; } if (typeof props.maxOutputTokens === "number" && this.canOverride("maxOutputTokens")) streamTextParams.maxOutputTokens = props.maxOutputTokens; if (typeof props.temperature === "number" && this.canOverride("temperature")) streamTextParams.temperature = props.temperature; if (typeof props.topP === "number" && this.canOverride("topP")) streamTextParams.topP = props.topP; if (typeof props.topK === "number" && this.canOverride("topK")) streamTextParams.topK = props.topK; if (typeof props.presencePenalty === "number" && this.canOverride("presencePenalty")) streamTextParams.presencePenalty = props.presencePenalty; if (typeof props.frequencyPenalty === "number" && this.canOverride("frequencyPenalty")) streamTextParams.frequencyPenalty = props.frequencyPenalty; if (Array.isArray(props.stopSequences) && this.canOverride("stopSequences")) { if (props.stopSequences.every((item) => typeof item === "string")) streamTextParams.stopSequences = props.stopSequences; } if (typeof props.seed === "number" && this.canOverride("seed")) streamTextParams.seed = props.seed; if (typeof props.maxRetries === "number" && this.canOverride("maxRetries")) streamTextParams.maxRetries = props.maxRetries; if (props.providerOptions !== void 0 && this.canOverride("providerOptions")) { if (typeof props.providerOptions === "object" && props.providerOptions !== null) streamTextParams.providerOptions = props.providerOptions; } } const mcpClients = []; (async () => { let terminalEventEmitted = false; let messageId = (0, _copilotkit_shared.randomUUID)(); let reasoningMessageId = (0, _copilotkit_shared.randomUUID)(); let isInReasoning = false; const closeReasoningIfOpen = () => { if (!isInReasoning) return; isInReasoning = false; const reasoningMsgEnd = { type: _ag_ui_client.EventType.REASONING_MESSAGE_END, messageId: reasoningMessageId }; subscriber.next(reasoningMsgEnd); const reasoningEnd = { type: _ag_ui_client.EventType.REASONING_END, messageId: reasoningMessageId }; subscriber.next(reasoningEnd); }; try { streamTextParams.tools = { ...streamTextParams.tools, AGUISendStateSnapshot: (0, ai.tool)({ description: "Replace the entire application state with a new snapshot", inputSchema: toLanguageModelSchema(zod.z.object({ snapshot: zod.z.any().describe("The complete new state object") })), execute: async ({ snapshot }) => { return { success: true, snapshot }; } }), AGUISendStateDelta: (0, ai.tool)({ description: "Apply incremental updates to application state using JSON Patch operations", inputSchema: toLanguageModelSchema(zod.z.object({ delta: zod.z.array(zod.z.object({ op: zod.z.enum([ "add", "replace", "remove" ]).describe("The operation to perform"), path: zod.z.string().describe("JSON Pointer path (e.g., '/foo/bar')"), value: zod.z.any().optional().describe("The value to set. Required for 'add' and 'replace' operations, ignored for 'remove'.") })).describe("Array of JSON Patch operations") })), execute: async ({ delta }) => { return { success: true, delta }; } }) }; if (config.mcpClients && config.mcpClients.length > 0) for (const client of config.mcpClients) { const mcpTools = await client.tools(); streamTextParams.tools = { ...streamTextParams.tools, ...mcpTools }; } const allMcpServers = [...config.mcpServers ?? []]; if (allMcpServers.length > 0) for (const serverConfig of allMcpServers) { let transport; if (serverConfig.type === "http") transport = new _modelcontextprotocol_sdk_client_streamableHttp_js.StreamableHTTPClientTransport(new URL(serverConfig.url), serverConfig.options); else if (serverConfig.type === "sse") transport = new _modelcontextprotocol_sdk_client_sse_js.SSEClientTransport(new URL(serverConfig.url), serverConfig.headers); if (transport) { let mcpClient; try { mcpClient = await (0, _ai_sdk_mcp.createMCPClient)({ transport }); } catch (err) { console.error(`[CopilotKit] MCP server ${serverConfig.url} failed to connect — skipping it for this run:`, err); continue; } mcpClients.push(mcpClient); try { const mcpTools = await mcpClient.tools(); streamTextParams.tools = { ...streamTextParams.tools, ...mcpTools }; } catch (err) { console.error(`[CopilotKit] MCP server ${serverConfig.url} tools() failed — skipping its tools for this run:`, err); } } } const response = (0, ai.streamText)({ ...streamTextParams, abortSignal: abortController.signal }); const interruptToolNames = new Set((isFactoryConfig(this.config) ? [] : this.config.tools ?? []).filter((t) => t.interrupt).map((t) => t.name)); const interruptToolMeta = new Map((isFactoryConfig(this.config) ? [] : this.config.tools ?? []).filter((t) => t.interrupt).map((t) => [t.name, { reason: t.interruptReason ?? "tool_call", message: t.interruptMessage }])); const pendingInterrupts = []; const toolCallStates = /* @__PURE__ */ new Map(); const ensureToolCallState = (toolCallId) => { let state = toolCallStates.get(toolCallId); if (!state) { state = { started: false, hasArgsDelta: false, ended: false }; toolCallStates.set(toolCallId, state); } return state; }; for await (const part of response.fullStream) { if (part.type !== "reasoning-delta") closeReasoningIfOpen(); switch (part.type) { case "abort": { const abortEndEvent = { type: _ag_ui_client.EventType.RUN_FINISHED, threadId: input.threadId, runId: input.runId }; subscriber.next(abortEndEvent); terminalEventEmitted = true; subscriber.complete(); break; } case "reasoning-start": { const providedId = "id" in part ? part.id : void 0; reasoningMessageId = !providedId || providedId === "0" || /^(txt|reasoning|msg)-0$/.test(providedId) ? (0, _copilotkit_shared.randomUUID)() : providedId; const reasoningStartEvent = { type: _ag_ui_client.EventType.REASONING_START, messageId: reasoningMessageId }; subscriber.next(reasoningStartEvent); const reasoningMessageStart = { type: _ag_ui_client.EventType.REASONING_MESSAGE_START, messageId: reasoningMessageId, role: "reasoning" }; subscriber.next(reasoningMessageStart); isInReasoning = true; break; } case "reasoning-delta": { const delta = part.text ?? ""; if (!delta) break; const reasoningDeltaEvent = { type: _ag_ui_client.EventType.REASONING_MESSAGE_CONTENT, messageId: reasoningMessageId, delta }; subscriber.next(reasoningDeltaEvent); break; } case "reasoning-end": break; case "tool-input-start": { const toolCallId = part.id; const state = ensureToolCallState(toolCallId); state.toolName = part.toolName; if (!state.started) { state.started = true; const startEvent = { type: _ag_ui_client.EventType.TOOL_CALL_START, parentMessageId: messageId, toolCallId, toolCallName: part.toolName }; subscriber.next(startEvent); } break; } case "tool-input-delta": { const toolCallId = part.id; const state = ensureToolCallState(toolCallId); state.hasArgsDelta = true; const argsEvent = { type: _ag_ui_client.EventType.TOOL_CALL_ARGS, toolCallId, delta: part.delta }; subscriber.next(argsEvent); break; } case "tool-input-end": break; case "text-start": { const providedId = "id" in part ? part.id : void 0; messageId = !providedId || providedId === "0" || /^(txt|reasoning|msg)-0$/.test(providedId) ? (0, _copilotkit_shared.randomUUID)() : providedId; break; } case "text-delta": { const textDelta = "text" in part ? part.text : ""; const textEvent = { type: _ag_ui_client.EventType.TEXT_MESSAGE_CHUNK, role: "assistant", messageId, delta: textDelta }; subscriber.next(textEvent); break; } case "tool-call": { const toolCallId = part.toolCallId; const state = ensureToolCallState(toolCallId); state.toolName = part.toolName ?? state.toolName; if (!state.started) { state.started = true; const startEvent = { type: _ag_ui_client.EventType.TOOL_CALL_START, parentMessageId: messageId, toolCallId, toolCallName: part.toolName }; subscriber.next(startEvent); } if (!state.hasArgsDelta && "input" in part && part.input !== void 0) { let serializedInput = ""; if (typeof part.input === "string") serializedInput = part.input; else try { serializedInput = JSON.stringify(part.input); } catch { serializedInput = String(part.input); } if (serializedInput.length > 0) { const argsEvent = { type: _ag_ui_client.EventType.TOOL_CALL_ARGS, toolCallId, delta: serializedInput }; subscriber.next(argsEvent); state.hasArgsDelta = true; } } if (!state.ended) { state.ended = true; const endEvent = { type: _ag_ui_client.EventType.TOOL_CALL_END, toolCallId }; subscriber.next(endEvent); } if (state.toolName && interruptToolNames.has(state.toolName)) { const meta = interruptToolMeta.get(state.toolName); pendingInterrupts.push({ id: toolCallId, toolCallId, reason: meta?.reason ?? "tool_call", ...meta?.message ? { message: meta.message } : {} }); } break; } case "tool-result": { const toolName = "toolName" in part && part.toolName || toolCallStates.get(part.toolCallId)?.toolName || ""; if (toolName && interruptToolNames.has(toolName)) { toolCallStates.delete(part.toolCallId); break; } const legacyPart = part; const toolResult = "output" in part ? part.output : "result" in legacyPart ? legacyPart.result : null; toolCallStates.delete(part.toolCallId); if (toolName === "AGUISendStateSnapshot" && toolResult && typeof toolResult === "object") { const snapshot = toolResult.snapshot; if (snapshot !== void 0) { const stateSnapshotEvent = { type: _ag_ui_client.EventType.STATE_SNAPSHOT, snapshot }; subscriber.next(stateSnapshotEvent); } } else if (toolName === "AGUISendStateDelta" && toolResult && typeof toolResult === "object") { const delta = toolResult.delta; if (delta !== void 0) { const stateDeltaEvent = { type: _ag_ui_client.EventType.STATE_DELTA, delta }; subscriber.next(stateDeltaEvent); } } let serializedResult; try { serializedResult = JSON.stringify(toolResult); } catch { serializedResult = `[Unserializable tool result from ${toolName || part.toolCallId}]`; } const resultEvent = { type: _ag_ui_client.EventType.TOOL_CALL_RESULT, role: "tool", messageId: (0, _copilotkit_shared.randomUUID)(), toolCallId: part.toolCallId, content: serializedResult }; subscriber.next(resultEvent); break; } case "finish": { const finishedEvent = { type: _ag_ui_client.EventType.RUN_FINISHED, threadId: input.threadId, runId: input.runId, ...pendingInterrupts.length > 0 ? { outcome: { type: "interrupt", interrupts: pendingInterrupts } } : {} }; subscriber.next(finishedEvent); terminalEventEmitted = true; subscriber.complete(); break; } case "error": { if (abortController.signal.aborted) break; const err = part.error ?? ("message" in part ? part.message : void 0) ?? ("cause" in part ? part.cause : void 0); const runErrorEvent = { type: _ag_ui_client.EventType.RUN_ERROR, message: err instanceof Error ? err.message : typeof err === "string" ? err : `AI SDK stream error: ${JSON.stringify(part)}`, threadId: input.threadId, runId: input.runId }; subscriber.next(runErrorEvent); terminalEventEmitted = true; if (err instanceof Error) subscriber.error(err); else subscriber.error(new Error(typeof err === "string" ? err : `AI SDK stream error`)); break; } } } if (!terminalEventEmitted) { closeReasoningIfOpen(); if (abortController.signal.aborted) {} else { const finishedEvent = { type: _ag_ui_client.EventType.RUN_FINISHED, threadId: input.threadId, runId: input.runId, ...pendingInterrupts.length > 0 ? { outcome: { type: "interrupt", interrupts: pendingInterrupts } } : {} }; subscriber.next(finishedEvent); } terminalEventEmitted = true; subscriber.complete(); } } catch (error) { closeReasoningIfOpen(); if (abortController.signal.aborted) subscriber.complete(); else { const runErrorEvent = { type: _ag_ui_client.EventType.RUN_ERROR, message: error instanceof Error ? error.message : String(error), threadId: input.threadId, runId: input.runId }; subscriber.next(runErrorEvent); terminalEventEmitted = true; subscriber.error(error); } } finally { this.abortController = void 0; await Promise.all(mcpClients.map((client) => client.close())); } })(); return () => { Promise.all(mcpClients.map((client) => client.close())).catch(() => {}); }; }); } runFactory(input, config) { if (this.abortController) throw new Error("Agent is already running. Call abortRun() first or create a new instance."); this.abortController = new AbortController(); const controller = this.abortController; return new rxjs.Observable((subscriber) => { const startEvent = { type: _ag_ui_client.EventType.RUN_STARTED, threadId: input.threadId, runId: input.runId }; subscriber.next(startEvent); const ctx = { input, abortController: controller, abortSignal: controller.signal, interrupt: async (interrupts) => { const resume = input.resume ?? []; const ids = new Set(interrupts.map((i) => i.id)); const matching = resume.filter((r) => ids.has(r.interruptId)); if (interrupts.length > 0 && matching.length === interrupts.length) return matching; throw new InterruptSignal(interrupts); } }; const answeredToolCallIds = new Set(input.messages.filter((m) => m.role === "tool").map((m) => m.toolCallId).filter((id) => typeof id === "string")); const resumeToolMessages = (input.resume ?? []).filter((entry) => !answeredToolCallIds.has(entry.interruptId)).map((entry) => ({ id: (0, _copilotkit_shared.randomUUID)(), role: "tool", toolCallId: entry.interruptId, content: JSON.stringify(entry.status === "cancelled" ? { status: "cancelled" } : entry.payload ?? { status: "resolved" }) })); const factoryInput = resumeToolMessages.length > 0 && config.type !== "custom" ? { ...input, messages: [...input.messages, ...resumeToolMessages] } : input; const factoryCtx = { ...ctx, input: factoryInput }; (async () => { try { let events; const pendingInterrupts = []; switch (config.type) { case "aisdk": events = require_aisdk.convertAISDKStream((await config.factory(factoryCtx)).fullStream, controller.signal, pendingInterrupts); break; case "tanstack": events = require_tanstack.convertTanStackStream(await config.factory(factoryCtx), controller.signal, pendingInterrupts); break; case "custom": events = await config.factory(ctx); break; default: { const _exhaustive = config; throw new Error(`Unknown agent config type: ${_exhaustive.type}`); } } for await (const event of events) subscriber.next(event); if (pendingInterrupts.length > 0 && !controller.signal.aborted) throw new InterruptSignal(pendingInterrupts); if (!controller.signal.aborted) { const finishedEvent = { type: _ag_ui_client.EventType.RUN_FINISHED, threadId: input.threadId, runId: input.runId }; subscriber.next(finishedEvent); } subscriber.complete(); } catch (error) { if (error instanceof InterruptSignal) { const finishedEvent = { type: _ag_ui_client.EventType.RUN_FINISHED, threadId: input.threadId, runId: input.runId, outcome: { type: "interrupt", interrupts: error.interrupts } }; subscriber.next(finishedEvent); subscriber.complete(); } else if (controller.signal.aborted) subscriber.complete(); else { const runErrorEvent = { type: _ag_ui_client.EventType.RUN_ERROR, message: error instanceof Error ? error.message : String(error), threadId: input.threadId, runId: input.runId }; subscriber.next(runErrorEvent); subscriber.error(error); } } finally { this.abortController = void 0; } })(); return () => { controller.abort(); }; }); } clone() { const cloned = new BuiltInAgent(this.config); cloned.middlewares = [...this.middlewares]; return cloned; } abortRun() { this.abortController?.abort(); } }; /** * @deprecated Use BuiltInAgent instead */ var BasicAgent = class extends BuiltInAgent { constructor(config) { super(config); console.warn("BasicAgent is deprecated, use BuiltInAgent instead"); } }; //#endregion exports.BasicAgent = BasicAgent; exports.BuiltInAgent = BuiltInAgent; exports.InterruptSignal = InterruptSignal; exports.convertJsonSchemaToZodSchema = convertJsonSchemaToZodSchema; exports.convertMessagesToVercelAISDKMessages = convertMessagesToVercelAISDKMessages; exports.convertToolDefinitionsToVercelAITools = convertToolDefinitionsToVercelAITools; exports.convertToolsToVercelAITools = convertToolsToVercelAITools; exports.defineTool = defineTool; exports.resolveModel = resolveModel; //# sourceMappingURL=index.cjs.map