@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;" />
862 lines (860 loc) • 30.7 kB
JavaScript
import "reflect-metadata";
import { INTELLIGENCE_USER_ID_HEADER } from "../v2/runtime/intelligence-platform/client.mjs";
import { convertAISDKStream } from "./converters/aisdk.mjs";
import { convertInputToTanStackAI, convertTanStackStream } from "./converters/tanstack.mjs";
import { createOpenAI } from "@ai-sdk/openai";
import { randomUUID, safeParseToolArgs, schemaToJsonSchema } from "@copilotkit/shared";
import { Observable } from "rxjs";
import { AbstractAgent, EventType } from "@ag-ui/client";
import { jsonSchema, stepCountIs, streamText, tool } from "ai";
import { createMCPClient } from "@ai-sdk/mcp";
import { createAnthropic } from "@ai-sdk/anthropic";
import { createGoogleGenerativeAI } from "@ai-sdk/google";
import { createVertex } from "@ai-sdk/google-vertex";
import { z } from "zod";
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
import { SSEClientTransport } from "@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 createOpenAI({ apiKey: apiKey || process.env.OPENAI_API_KEY })(model);
case "anthropic": return createAnthropic({ apiKey: apiKey || process.env.ANTHROPIC_API_KEY })(model);
case "google":
case "gemini":
case "google-gemini": return createGoogleGenerativeAI({ apiKey: apiKey || process.env.GOOGLE_API_KEY })(model);
case "vertex": return createVertex()(model);
default: throw new Error(`Unknown provider "${provider}" in "${spec}". Supported: openai, anthropic, google (gemini).`);
}
}
/**
* 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
};
}
/**
* 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: 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$1(jsonSchema, required) {
if (!jsonSchema.type) return required ? z.object({}) : z.object({}).optional();
if (jsonSchema.type === "object") {
const spec = {};
if (!jsonSchema.properties || !Object.keys(jsonSchema.properties).length) return !required ? z.object(spec).optional() : z.object(spec);
for (const [key, value] of Object.entries(jsonSchema.properties)) spec[key] = convertJsonSchemaToZodSchema$1(value, jsonSchema.required ? jsonSchema.required.includes(key) : false);
const schema = 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 = z.enum(jsonSchema.enum).describe(jsonSchema.description ?? "");
return required ? schema : schema.optional();
}
const schema = z.string().describe(jsonSchema.description ?? "");
return required ? schema : schema.optional();
} else if (jsonSchema.type === "number" || jsonSchema.type === "integer") {
const schema = z.number().describe(jsonSchema.description ?? "");
return required ? schema : schema.optional();
} else if (jsonSchema.type === "boolean") {
const schema = 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$1(jsonSchema.items, true);
const schema = 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);
}
function convertToolsToVercelAITools(tools) {
const result = {};
for (const tool$1 of tools) {
if (!isJsonSchema(tool$1.parameters)) throw new Error(`Invalid JSON schema for tool ${tool$1.name}`);
const zodSchema = convertJsonSchemaToZodSchema$1(tool$1.parameters, true);
result[tool$1.name] = tool({
description: tool$1.description,
inputSchema: 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$2 of tools) if (isZodSchema(tool$2.parameters)) result[tool$2.name] = tool({
description: tool$2.description,
inputSchema: tool$2.parameters,
execute: tool$2.execute
});
else {
const jsonSchemaObj = schemaToJsonSchema(tool$2.parameters);
result[tool$2.name] = tool({
description: tool$2.description,
inputSchema: jsonSchema(jsonSchemaObj),
execute: tool$2.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 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 }
};
if (!this.config.capabilities) return inferred;
return {
...inferred,
...this.config.capabilities
};
}
run(input) {
if (isFactoryConfig(this.config)) return this.runFactory(input, 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 Observable((subscriber) => {
const startEvent = {
type: EventType.RUN_STARTED,
threadId: input.threadId,
runId: input.runId
};
subscriber.next(startEvent);
const model = resolveModel(this.config.model, this.config.apiKey);
let systemPrompt = void 0;
const hasPrompt = !!this.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(this.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: this.config.forwardSystemMessages,
forwardDeveloperMessages: this.config.forwardDeveloperMessages
});
if (systemPrompt) messages.unshift({
role: "system",
content: systemPrompt
});
let allTools = convertToolsToVercelAITools(input.tools);
if (this.config.tools && this.config.tools.length > 0) {
const configTools = convertToolDefinitionsToVercelAITools(this.config.tools);
allTools = {
...allTools,
...configTools
};
}
const streamTextParams = {
model,
messages,
tools: allTools,
toolChoice: this.config.toolChoice,
stopWhen: this.config.maxSteps ? stepCountIs(this.config.maxSteps) : void 0,
maxOutputTokens: this.config.maxOutputTokens,
temperature: this.config.temperature,
topP: this.config.topP,
topK: this.config.topK,
presencePenalty: this.config.presencePenalty,
frequencyPenalty: this.config.frequencyPenalty,
stopSequences: this.config.stopSequences,
seed: this.config.seed,
providerOptions: this.config.providerOptions,
maxRetries: this.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, this.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 = randomUUID();
let reasoningMessageId = randomUUID();
let isInReasoning = false;
const closeReasoningIfOpen = () => {
if (!isInReasoning) return;
isInReasoning = false;
const reasoningMsgEnd = {
type: EventType.REASONING_MESSAGE_END,
messageId: reasoningMessageId
};
subscriber.next(reasoningMsgEnd);
const reasoningEnd = {
type: EventType.REASONING_END,
messageId: reasoningMessageId
};
subscriber.next(reasoningEnd);
};
try {
streamTextParams.tools = {
...streamTextParams.tools,
AGUISendStateSnapshot: tool({
description: "Replace the entire application state with a new snapshot",
inputSchema: z.object({ snapshot: z.any().describe("The complete new state object") }),
execute: async ({ snapshot }) => {
return {
success: true,
snapshot
};
}
}),
AGUISendStateDelta: tool({
description: "Apply incremental updates to application state using JSON Patch operations",
inputSchema: z.object({ delta: z.array(z.object({
op: z.enum([
"add",
"replace",
"remove"
]).describe("The operation to perform"),
path: z.string().describe("JSON Pointer path (e.g., '/foo/bar')"),
value: 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 (this.config.mcpClients && this.config.mcpClients.length > 0) for (const client of this.config.mcpClients) {
const mcpTools = await client.tools();
streamTextParams.tools = {
...streamTextParams.tools,
...mcpTools
};
}
const allMcpServers = [...this.config.mcpServers ?? []];
const cpki = (input.forwardedProps?.auth)?.copilotkitIntelligence;
const cpkiUserId = typeof cpki?.userId === "string" ? cpki.userId : void 0;
const cpkiApiKey = typeof cpki?.apiKey === "string" ? cpki.apiKey : void 0;
const cpkiMcpUrl = typeof cpki?.mcpUrl === "string" ? cpki.mcpUrl : void 0;
if (cpkiUserId && cpkiApiKey && cpkiMcpUrl && !allMcpServers.some((s) => s.type === "http" && s.url === cpkiMcpUrl)) allMcpServers.push({
type: "http",
url: cpkiMcpUrl,
options: { fetch: async (req, init) => {
const headers = new Headers(init?.headers);
headers.set("Authorization", `Bearer ${cpkiApiKey}`);
headers.set(INTELLIGENCE_USER_ID_HEADER, cpkiUserId);
return globalThis.fetch(req, {
...init,
headers
});
} }
});
if (allMcpServers.length > 0) for (const serverConfig of allMcpServers) {
let transport;
if (serverConfig.type === "http") transport = new StreamableHTTPClientTransport(new URL(serverConfig.url), serverConfig.options);
else if (serverConfig.type === "sse") transport = new SSEClientTransport(new URL(serverConfig.url), serverConfig.headers);
if (transport) {
const mcpClient = await createMCPClient({ transport });
mcpClients.push(mcpClient);
const mcpTools = await mcpClient.tools();
streamTextParams.tools = {
...streamTextParams.tools,
...mcpTools
};
}
}
const response = streamText({
...streamTextParams,
abortSignal: abortController.signal
});
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: 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) ? randomUUID() : providedId;
const reasoningStartEvent = {
type: EventType.REASONING_START,
messageId: reasoningMessageId
};
subscriber.next(reasoningStartEvent);
const reasoningMessageStart = {
type: 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: 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: 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: 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) ? randomUUID() : providedId;
break;
}
case "text-delta": {
const textDelta = "text" in part ? part.text : "";
const textEvent = {
type: 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: 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: EventType.TOOL_CALL_ARGS,
toolCallId,
delta: serializedInput
};
subscriber.next(argsEvent);
state.hasArgsDelta = true;
}
}
if (!state.ended) {
state.ended = true;
const endEvent = {
type: EventType.TOOL_CALL_END,
toolCallId
};
subscriber.next(endEvent);
}
break;
}
case "tool-result": {
const toolResult = "output" in part ? part.output : "result" in part ? part.result : null;
const toolName = "toolName" in part ? part.toolName : "";
toolCallStates.delete(part.toolCallId);
if (toolName === "AGUISendStateSnapshot" && toolResult && typeof toolResult === "object") {
const snapshot = toolResult.snapshot;
if (snapshot !== void 0) {
const stateSnapshotEvent = {
type: 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: 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: EventType.TOOL_CALL_RESULT,
role: "tool",
messageId: randomUUID(),
toolCallId: part.toolCallId,
content: serializedResult
};
subscriber.next(resultEvent);
break;
}
case "finish": {
const finishedEvent = {
type: EventType.RUN_FINISHED,
threadId: input.threadId,
runId: input.runId
};
subscriber.next(finishedEvent);
terminalEventEmitted = true;
subscriber.complete();
break;
}
case "error": {
if (abortController.signal.aborted) break;
const err = part.error ?? part.message ?? part.cause;
const runErrorEvent = {
type: 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: EventType.RUN_FINISHED,
threadId: input.threadId,
runId: input.runId
};
subscriber.next(finishedEvent);
}
terminalEventEmitted = true;
subscriber.complete();
}
} catch (error) {
closeReasoningIfOpen();
if (abortController.signal.aborted) subscriber.complete();
else {
const runErrorEvent = {
type: 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 Observable((subscriber) => {
const startEvent = {
type: EventType.RUN_STARTED,
threadId: input.threadId,
runId: input.runId
};
subscriber.next(startEvent);
const ctx = {
input,
abortController: controller,
abortSignal: controller.signal
};
(async () => {
try {
let events;
switch (config.type) {
case "aisdk":
events = convertAISDKStream((await config.factory(ctx)).fullStream, controller.signal);
break;
case "tanstack":
events = convertTanStackStream(await config.factory(ctx), controller.signal);
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 (!controller.signal.aborted) {
const finishedEvent = {
type: EventType.RUN_FINISHED,
threadId: input.threadId,
runId: input.runId
};
subscriber.next(finishedEvent);
}
subscriber.complete();
} catch (error) {
if (controller.signal.aborted) subscriber.complete();
else {
const runErrorEvent = {
type: 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
export { BasicAgent, BuiltInAgent, convertJsonSchemaToZodSchema$1 as convertJsonSchemaToZodSchema, convertMessagesToVercelAISDKMessages, convertToolDefinitionsToVercelAITools, convertToolsToVercelAITools, defineTool, resolveModel };
//# sourceMappingURL=index.mjs.map