@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
JavaScript
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