@tanstack/ai 0.10.2 → 0.10.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -53,7 +53,8 @@ class ToolCallManager {
|
|
|
53
53
|
for (const [, toolCall] of this.toolCallsMap.entries()) {
|
|
54
54
|
if (toolCall.id === event.toolCallId) {
|
|
55
55
|
if (event.input !== void 0) {
|
|
56
|
-
|
|
56
|
+
const normalized = event.input && typeof event.input === "object" ? event.input : {};
|
|
57
|
+
toolCall.function.arguments = JSON.stringify(normalized);
|
|
57
58
|
}
|
|
58
59
|
break;
|
|
59
60
|
}
|
|
@@ -89,7 +90,8 @@ class ToolCallManager {
|
|
|
89
90
|
let args;
|
|
90
91
|
try {
|
|
91
92
|
const argsString = toolCall.function.arguments.trim() || "{}";
|
|
92
|
-
|
|
93
|
+
const parsed = JSON.parse(argsString);
|
|
94
|
+
args = parsed && typeof parsed === "object" ? parsed : {};
|
|
93
95
|
} catch (parseError) {
|
|
94
96
|
throw new Error(
|
|
95
97
|
`Failed to parse tool arguments as JSON: ${toolCall.function.arguments}`
|
|
@@ -294,7 +296,8 @@ async function* executeToolCalls(toolCalls, tools, approvals = /* @__PURE__ */ n
|
|
|
294
296
|
const argsStr = toolCall.function.arguments.trim() || "{}";
|
|
295
297
|
{
|
|
296
298
|
try {
|
|
297
|
-
|
|
299
|
+
const parsed = JSON.parse(argsStr);
|
|
300
|
+
input = parsed && typeof parsed === "object" ? parsed : {};
|
|
298
301
|
} catch (parseError) {
|
|
299
302
|
throw new Error(`Failed to parse tool arguments as JSON: ${argsStr}`);
|
|
300
303
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tool-calls.js","sources":["../../../../../src/activities/chat/tools/tool-calls.ts"],"sourcesContent":["import { isStandardSchema, parseWithStandardSchema } from './schema-converter'\nimport type {\n CustomEvent,\n ModelMessage,\n RunFinishedEvent,\n Tool,\n ToolCall,\n ToolCallArgsEvent,\n ToolCallEndEvent,\n ToolCallStartEvent,\n ToolExecutionContext,\n} from '../../../types'\nimport type {\n AfterToolCallInfo,\n BeforeToolCallDecision,\n} from '../middleware/types'\n\nfunction safeJsonParse(value: string): unknown {\n try {\n return JSON.parse(value)\n } catch {\n return value\n }\n}\n\n/**\n * Optional middleware hooks for tool execution.\n * When provided, these callbacks are invoked before/after each tool execution.\n */\nexport interface ToolExecutionMiddlewareHooks {\n onBeforeToolCall?: (\n toolCall: ToolCall,\n tool: Tool | undefined,\n args: unknown,\n ) => Promise<BeforeToolCallDecision>\n onAfterToolCall?: (info: AfterToolCallInfo) => Promise<void>\n}\n\n/**\n * Error thrown when middleware decides to abort the chat run during tool execution.\n */\nexport class MiddlewareAbortError extends Error {\n constructor(reason: string) {\n super(reason)\n this.name = 'MiddlewareAbortError'\n }\n}\n\n/**\n * Manages tool call accumulation and execution for the chat() method's automatic tool execution loop.\n *\n * Responsibilities:\n * - Accumulates streaming tool call events (ID, name, arguments)\n * - Validates tool calls (filters out incomplete ones)\n * - Executes tool `execute` functions with parsed arguments\n * - Emits `TOOL_CALL_END` events for client visibility\n * - Returns tool result messages for conversation history\n *\n * This class is used internally by the AI.chat() method to handle the automatic\n * tool execution loop. It can also be used independently for custom tool execution logic.\n *\n * @example\n * ```typescript\n * const manager = new ToolCallManager(tools);\n *\n * // During streaming, accumulate tool calls\n * for await (const chunk of stream) {\n * if (chunk.type === 'TOOL_CALL_START') {\n * manager.addToolCallStartEvent(chunk);\n * } else if (chunk.type === 'TOOL_CALL_ARGS') {\n * manager.addToolCallArgsEvent(chunk);\n * }\n * }\n *\n * // After stream completes, execute tools\n * if (manager.hasToolCalls()) {\n * const toolResults = yield* manager.executeTools(finishEvent);\n * messages = [...messages, ...toolResults];\n * manager.clear();\n * }\n * ```\n */\nexport class ToolCallManager {\n private toolCallsMap = new Map<number, ToolCall>()\n private tools: ReadonlyArray<Tool>\n\n constructor(tools: ReadonlyArray<Tool>) {\n this.tools = tools\n }\n\n /**\n * Add a TOOL_CALL_START event to begin tracking a tool call (AG-UI)\n */\n addToolCallStartEvent(event: ToolCallStartEvent): void {\n const index = event.index ?? this.toolCallsMap.size\n this.toolCallsMap.set(index, {\n id: event.toolCallId,\n type: 'function',\n function: {\n name: event.toolName,\n arguments: '',\n },\n ...(event.providerMetadata && {\n providerMetadata: event.providerMetadata,\n }),\n })\n }\n\n /**\n * Add a TOOL_CALL_ARGS event to accumulate arguments (AG-UI)\n */\n addToolCallArgsEvent(event: ToolCallArgsEvent): void {\n // Find the tool call by ID\n for (const [, toolCall] of this.toolCallsMap.entries()) {\n if (toolCall.id === event.toolCallId) {\n toolCall.function.arguments += event.delta\n break\n }\n }\n }\n\n /**\n * Complete a tool call with its final input\n * Called when TOOL_CALL_END is received\n */\n completeToolCall(event: ToolCallEndEvent): void {\n for (const [, toolCall] of this.toolCallsMap.entries()) {\n if (toolCall.id === event.toolCallId) {\n if (event.input !== undefined) {\n toolCall.function.arguments = JSON.stringify(event.input)\n }\n break\n }\n }\n }\n\n /**\n * Check if there are any complete tool calls to execute\n */\n hasToolCalls(): boolean {\n return this.getToolCalls().length > 0\n }\n\n /**\n * Get all complete tool calls (filtered for valid ID and name)\n */\n getToolCalls(): Array<ToolCall> {\n return Array.from(this.toolCallsMap.values()).filter(\n (tc) => tc.id && tc.function.name && tc.function.name.trim().length > 0,\n )\n }\n\n /**\n * Execute all tool calls and return tool result messages\n * Yields TOOL_CALL_END events for streaming\n * @param finishEvent - RUN_FINISHED event from the stream\n */\n async *executeTools(\n finishEvent: RunFinishedEvent,\n ): AsyncGenerator<ToolCallEndEvent, Array<ModelMessage>, void> {\n const toolCallsArray = this.getToolCalls()\n const toolResults: Array<ModelMessage> = []\n\n for (const toolCall of toolCallsArray) {\n const tool = this.tools.find((t) => t.name === toolCall.function.name)\n\n let toolResultContent: string\n if (tool?.execute) {\n try {\n // Parse arguments (normalize \"null\" to \"{}\" for empty tool_use blocks)\n let args: unknown\n try {\n const argsString = toolCall.function.arguments.trim() || '{}'\n args = JSON.parse(argsString === 'null' ? '{}' : argsString)\n } catch (parseError) {\n throw new Error(\n `Failed to parse tool arguments as JSON: ${toolCall.function.arguments}`,\n )\n }\n\n // Validate input against inputSchema (for Standard Schema compliant schemas)\n if (tool.inputSchema && isStandardSchema(tool.inputSchema)) {\n try {\n args = parseWithStandardSchema(tool.inputSchema, args)\n } catch (validationError: unknown) {\n const message =\n validationError instanceof Error\n ? validationError.message\n : 'Validation failed'\n throw new Error(\n `Input validation failed for tool ${tool.name}: ${message}`,\n )\n }\n }\n\n // Execute the tool\n let result = await tool.execute(args)\n\n // Validate output against outputSchema if provided (for Standard Schema compliant schemas)\n if (\n tool.outputSchema &&\n isStandardSchema(tool.outputSchema) &&\n result !== undefined &&\n result !== null\n ) {\n try {\n result = parseWithStandardSchema(tool.outputSchema, result)\n } catch (validationError: unknown) {\n const message =\n validationError instanceof Error\n ? validationError.message\n : 'Validation failed'\n throw new Error(\n `Output validation failed for tool ${tool.name}: ${message}`,\n )\n }\n }\n\n toolResultContent =\n typeof result === 'string' ? result : JSON.stringify(result)\n } catch (error: unknown) {\n // If tool execution fails, add error message\n const message =\n error instanceof Error ? error.message : 'Unknown error'\n toolResultContent = `Error executing tool: ${message}`\n }\n } else {\n // Tool doesn't have execute function, add placeholder\n toolResultContent = `Tool ${toolCall.function.name} does not have an execute function`\n }\n\n // Emit TOOL_CALL_END event\n yield {\n type: 'TOOL_CALL_END',\n toolCallId: toolCall.id,\n toolName: toolCall.function.name,\n model: finishEvent.model,\n timestamp: Date.now(),\n result: toolResultContent,\n }\n\n // Add tool result message\n toolResults.push({\n role: 'tool',\n content: toolResultContent,\n toolCallId: toolCall.id,\n })\n }\n\n return toolResults\n }\n\n /**\n * Clear the tool calls map for the next iteration\n */\n clear(): void {\n this.toolCallsMap.clear()\n }\n}\n\nexport interface ToolResult {\n toolCallId: string\n toolName: string\n result: any\n state?: 'output-available' | 'output-error'\n /** Duration of tool execution in milliseconds (only for server-executed tools) */\n duration?: number\n}\n\nexport interface ApprovalRequest {\n toolCallId: string\n toolName: string\n input: any\n approvalId: string\n}\n\nexport interface ClientToolRequest {\n toolCallId: string\n toolName: string\n input: any\n}\n\ninterface ExecuteToolCallsResult {\n /** Tool results ready to send to LLM */\n results: Array<ToolResult>\n /** Tools that need user approval before execution */\n needsApproval: Array<ApprovalRequest>\n /** Tools that need client-side execution */\n needsClientExecution: Array<ClientToolRequest>\n}\n\n/**\n * Helper that runs a tool execution promise while polling for pending custom events.\n * Yields any custom events that are emitted during execution, then returns the\n * execution result.\n */\nasync function* executeWithEventPolling<T>(\n executionPromise: Promise<T>,\n pendingEvents: Array<CustomEvent>,\n): AsyncGenerator<CustomEvent, T, void> {\n // Use an object to track mutable state across the async boundary\n const state = { done: false, result: undefined as T }\n const executionWithFlag = executionPromise.then((r) => {\n state.done = true\n state.result = r\n return r\n })\n\n while (!state.done) {\n // Wait for either the execution to complete or a short timeout\n await Promise.race([\n executionWithFlag,\n new Promise((resolve) => setTimeout(resolve, 10)),\n ])\n\n // Flush any pending events\n while (pendingEvents.length > 0) {\n yield pendingEvents.shift()!\n }\n }\n\n // Final flush in case events were emitted right at completion\n while (pendingEvents.length > 0) {\n yield pendingEvents.shift()!\n }\n\n return state.result\n}\n\n/**\n * Apply a middleware onBeforeToolCall decision.\n * Returns the (possibly transformed) input if execution should proceed,\n * or undefined if the tool call was skipped (result already pushed).\n * Throws MiddlewareAbortError if the decision is 'abort'.\n */\nasync function applyBeforeToolCallDecision(\n toolCall: ToolCall,\n tool: Tool,\n input: unknown,\n toolName: string,\n middlewareHooks: ToolExecutionMiddlewareHooks,\n results: Array<ToolResult>,\n): Promise<{ proceed: true; input: unknown } | { proceed: false }> {\n if (!middlewareHooks.onBeforeToolCall) {\n return { proceed: true, input }\n }\n\n const decision = await middlewareHooks.onBeforeToolCall(toolCall, tool, input)\n if (!decision) {\n return { proceed: true, input }\n }\n\n if (decision.type === 'abort') {\n throw new MiddlewareAbortError(decision.reason || 'Aborted by middleware')\n }\n\n if (decision.type === 'skip') {\n const skipResult = decision.result\n results.push({\n toolCallId: toolCall.id,\n toolName,\n result:\n typeof skipResult === 'string'\n ? safeJsonParse(skipResult)\n : skipResult || null,\n duration: 0,\n })\n if (middlewareHooks.onAfterToolCall) {\n await middlewareHooks.onAfterToolCall({\n toolCall,\n tool,\n toolName,\n toolCallId: toolCall.id,\n ok: true,\n duration: 0,\n result: skipResult,\n })\n }\n return { proceed: false }\n }\n\n return { proceed: true, input: decision.args }\n}\n\n/**\n * Execute a server-side tool with event polling, output validation, and middleware hooks.\n * Yields CustomEvent chunks during execution and pushes the result to the results array.\n */\nasync function* executeServerTool(\n toolCall: ToolCall,\n tool: Tool,\n toolName: string,\n input: unknown,\n context: ToolExecutionContext,\n pendingEvents: Array<CustomEvent>,\n results: Array<ToolResult>,\n middlewareHooks?: ToolExecutionMiddlewareHooks,\n): AsyncGenerator<CustomEvent, void, void> {\n const startTime = Date.now()\n try {\n const executionPromise = Promise.resolve(tool.execute!(input, context))\n let result = yield* executeWithEventPolling(executionPromise, pendingEvents)\n const duration = Date.now() - startTime\n\n // Flush remaining events\n while (pendingEvents.length > 0) {\n yield pendingEvents.shift()!\n }\n\n // Validate output against outputSchema if provided\n if (\n tool.outputSchema &&\n isStandardSchema(tool.outputSchema) &&\n result !== undefined &&\n result !== null\n ) {\n result = parseWithStandardSchema(tool.outputSchema, result)\n }\n\n const finalResult =\n typeof result === 'string' ? safeJsonParse(result) : result || null\n\n results.push({\n toolCallId: toolCall.id,\n toolName,\n result: finalResult,\n duration,\n })\n\n if (middlewareHooks?.onAfterToolCall) {\n await middlewareHooks.onAfterToolCall({\n toolCall,\n tool,\n toolName,\n toolCallId: toolCall.id,\n ok: true,\n duration,\n result: finalResult,\n })\n }\n } catch (error: unknown) {\n const duration = Date.now() - startTime\n\n // Flush remaining events\n while (pendingEvents.length > 0) {\n yield pendingEvents.shift()!\n }\n\n if (error instanceof MiddlewareAbortError) {\n throw error\n }\n\n const message = error instanceof Error ? error.message : 'Unknown error'\n results.push({\n toolCallId: toolCall.id,\n toolName,\n result: { error: message },\n state: 'output-error',\n duration,\n })\n\n if (middlewareHooks?.onAfterToolCall) {\n await middlewareHooks.onAfterToolCall({\n toolCall,\n tool,\n toolName,\n toolCallId: toolCall.id,\n ok: false,\n duration,\n error,\n })\n }\n }\n}\n\n/**\n * Execute tool calls based on their configuration.\n * Yields CustomEvent chunks during tool execution for real-time progress updates.\n *\n * Handles three cases:\n * 1. Client tools (no execute) - request client to execute\n * 2. Server tools with approval - check approval before executing\n * 3. Normal server tools - execute immediately\n *\n * @param toolCalls - Tool calls from the LLM\n * @param tools - Available tools with their configurations\n * @param approvals - Map of approval decisions (approval.id -> approved boolean)\n * @param clientResults - Map of client-side execution results (toolCallId -> result)\n * @param createCustomEventChunk - Factory to create CustomEvent chunks (optional)\n */\nexport async function* executeToolCalls(\n toolCalls: Array<ToolCall>,\n tools: ReadonlyArray<Tool>,\n approvals: Map<string, boolean> = new Map(),\n clientResults: Map<string, any> = new Map(),\n createCustomEventChunk?: (\n eventName: string,\n value: Record<string, any>,\n ) => CustomEvent,\n middlewareHooks?: ToolExecutionMiddlewareHooks,\n): AsyncGenerator<CustomEvent, ExecuteToolCallsResult, void> {\n const results: Array<ToolResult> = []\n const needsApproval: Array<ApprovalRequest> = []\n const needsClientExecution: Array<ClientToolRequest> = []\n\n // Create tool lookup map\n const toolMap = new Map<string, Tool>()\n for (const tool of tools) {\n toolMap.set(tool.name, tool)\n }\n\n // Batch gating: when any tool in the batch still needs an approval decision,\n // defer all execution so side effects don't happen before the user decides.\n const hasPendingApprovals = toolCalls.some((tc) => {\n const t = toolMap.get(tc.function.name)\n return t?.needsApproval && !approvals.has(`approval_${tc.id}`)\n })\n\n for (const toolCall of toolCalls) {\n const tool = toolMap.get(toolCall.function.name)\n const toolName = toolCall.function.name\n\n if (!tool) {\n // Unknown tool - return error\n results.push({\n toolCallId: toolCall.id,\n toolName,\n result: { error: `Unknown tool: ${toolName}` },\n state: 'output-error',\n })\n continue\n }\n\n // Skip non-pending tools while approvals are outstanding\n if (hasPendingApprovals) {\n if (!tool.needsApproval || approvals.has(`approval_${toolCall.id}`)) {\n continue\n }\n }\n\n // Parse arguments, throwing error if invalid JSON\n let input: unknown = {}\n const argsStr = toolCall.function.arguments.trim() || '{}'\n if (argsStr) {\n try {\n input = JSON.parse(argsStr)\n } catch (parseError) {\n // If parsing fails, throw error to fail fast\n throw new Error(`Failed to parse tool arguments as JSON: ${argsStr}`)\n }\n }\n\n // Validate input against inputSchema (for Standard Schema compliant schemas)\n if (tool.inputSchema && isStandardSchema(tool.inputSchema)) {\n try {\n input = parseWithStandardSchema(tool.inputSchema, input)\n } catch (validationError: unknown) {\n const message =\n validationError instanceof Error\n ? validationError.message\n : 'Validation failed'\n results.push({\n toolCallId: toolCall.id,\n toolName,\n result: {\n error: `Input validation failed for tool ${tool.name}: ${message}`,\n },\n state: 'output-error',\n })\n continue\n }\n }\n\n // Create a ToolExecutionContext for this tool call with event emission\n const pendingEvents: Array<CustomEvent> = []\n const context: ToolExecutionContext = {\n toolCallId: toolCall.id,\n emitCustomEvent: (eventName: string, value: Record<string, any>) => {\n if (createCustomEventChunk) {\n pendingEvents.push(\n createCustomEventChunk(eventName, {\n ...value,\n toolCallId: toolCall.id,\n }),\n )\n }\n },\n }\n\n // CASE 1: Client-side tool (no execute function)\n if (!tool.execute) {\n // Check if tool needs approval\n if (tool.needsApproval) {\n const approvalId = `approval_${toolCall.id}`\n\n // Check if approval decision exists\n if (approvals.has(approvalId)) {\n const approved = approvals.get(approvalId)\n\n if (approved) {\n // Approved - check if client has executed\n if (clientResults.has(toolCall.id)) {\n results.push({\n toolCallId: toolCall.id,\n toolName,\n result: clientResults.get(toolCall.id),\n })\n } else {\n // Approved but not executed yet - request client execution\n needsClientExecution.push({\n toolCallId: toolCall.id,\n toolName,\n input,\n })\n }\n } else {\n // User declined\n results.push({\n toolCallId: toolCall.id,\n toolName,\n result: { error: 'User declined tool execution' },\n state: 'output-error',\n })\n }\n } else {\n // Need approval first\n needsApproval.push({\n toolCallId: toolCall.id,\n toolName: toolCall.function.name,\n input,\n approvalId,\n })\n }\n } else {\n // No approval needed - check if client has executed\n if (clientResults.has(toolCall.id)) {\n results.push({\n toolCallId: toolCall.id,\n toolName,\n result: clientResults.get(toolCall.id),\n })\n } else {\n // Request client execution\n needsClientExecution.push({\n toolCallId: toolCall.id,\n toolName,\n input,\n })\n }\n }\n continue\n }\n\n // CASE 2: Server tool with approval required\n if (tool.needsApproval) {\n const approvalId = `approval_${toolCall.id}`\n\n // Check if approval decision exists\n if (approvals.has(approvalId)) {\n const approved = approvals.get(approvalId)\n\n if (approved) {\n // Apply middleware before-hook for approved tools\n if (middlewareHooks) {\n const decision = await applyBeforeToolCallDecision(\n toolCall,\n tool,\n input,\n toolName,\n middlewareHooks,\n results,\n )\n if (!decision.proceed) continue\n input = decision.input\n }\n\n yield* executeServerTool(\n toolCall,\n tool,\n toolName,\n input,\n context,\n pendingEvents,\n results,\n middlewareHooks,\n )\n } else {\n // User declined\n results.push({\n toolCallId: toolCall.id,\n toolName,\n result: { error: 'User declined tool execution' },\n state: 'output-error',\n })\n }\n } else {\n // Need approval\n needsApproval.push({\n toolCallId: toolCall.id,\n toolName,\n input,\n approvalId,\n })\n }\n continue\n }\n\n // CASE 3: Normal server tool - execute immediately\n if (middlewareHooks) {\n const decision = await applyBeforeToolCallDecision(\n toolCall,\n tool,\n input,\n toolName,\n middlewareHooks,\n results,\n )\n if (!decision.proceed) continue\n input = decision.input\n }\n\n yield* executeServerTool(\n toolCall,\n tool,\n toolName,\n input,\n context,\n pendingEvents,\n results,\n middlewareHooks,\n )\n }\n\n return { results, needsApproval, needsClientExecution }\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"tool-calls.js","sources":["../../../../../src/activities/chat/tools/tool-calls.ts"],"sourcesContent":["import { isStandardSchema, parseWithStandardSchema } from './schema-converter'\nimport type {\n CustomEvent,\n ModelMessage,\n RunFinishedEvent,\n Tool,\n ToolCall,\n ToolCallArgsEvent,\n ToolCallEndEvent,\n ToolCallStartEvent,\n ToolExecutionContext,\n} from '../../../types'\nimport type {\n AfterToolCallInfo,\n BeforeToolCallDecision,\n} from '../middleware/types'\n\nfunction safeJsonParse(value: string): unknown {\n try {\n return JSON.parse(value)\n } catch {\n return value\n }\n}\n\n/**\n * Optional middleware hooks for tool execution.\n * When provided, these callbacks are invoked before/after each tool execution.\n */\nexport interface ToolExecutionMiddlewareHooks {\n onBeforeToolCall?: (\n toolCall: ToolCall,\n tool: Tool | undefined,\n args: unknown,\n ) => Promise<BeforeToolCallDecision>\n onAfterToolCall?: (info: AfterToolCallInfo) => Promise<void>\n}\n\n/**\n * Error thrown when middleware decides to abort the chat run during tool execution.\n */\nexport class MiddlewareAbortError extends Error {\n constructor(reason: string) {\n super(reason)\n this.name = 'MiddlewareAbortError'\n }\n}\n\n/**\n * Manages tool call accumulation and execution for the chat() method's automatic tool execution loop.\n *\n * Responsibilities:\n * - Accumulates streaming tool call events (ID, name, arguments)\n * - Validates tool calls (filters out incomplete ones)\n * - Executes tool `execute` functions with parsed arguments\n * - Emits `TOOL_CALL_END` events for client visibility\n * - Returns tool result messages for conversation history\n *\n * This class is used internally by the AI.chat() method to handle the automatic\n * tool execution loop. It can also be used independently for custom tool execution logic.\n *\n * @example\n * ```typescript\n * const manager = new ToolCallManager(tools);\n *\n * // During streaming, accumulate tool calls\n * for await (const chunk of stream) {\n * if (chunk.type === 'TOOL_CALL_START') {\n * manager.addToolCallStartEvent(chunk);\n * } else if (chunk.type === 'TOOL_CALL_ARGS') {\n * manager.addToolCallArgsEvent(chunk);\n * }\n * }\n *\n * // After stream completes, execute tools\n * if (manager.hasToolCalls()) {\n * const toolResults = yield* manager.executeTools(finishEvent);\n * messages = [...messages, ...toolResults];\n * manager.clear();\n * }\n * ```\n */\nexport class ToolCallManager {\n private toolCallsMap = new Map<number, ToolCall>()\n private tools: ReadonlyArray<Tool>\n\n constructor(tools: ReadonlyArray<Tool>) {\n this.tools = tools\n }\n\n /**\n * Add a TOOL_CALL_START event to begin tracking a tool call (AG-UI)\n */\n addToolCallStartEvent(event: ToolCallStartEvent): void {\n const index = event.index ?? this.toolCallsMap.size\n this.toolCallsMap.set(index, {\n id: event.toolCallId,\n type: 'function',\n function: {\n name: event.toolName,\n arguments: '',\n },\n ...(event.providerMetadata && {\n providerMetadata: event.providerMetadata,\n }),\n })\n }\n\n /**\n * Add a TOOL_CALL_ARGS event to accumulate arguments (AG-UI)\n */\n addToolCallArgsEvent(event: ToolCallArgsEvent): void {\n // Find the tool call by ID\n for (const [, toolCall] of this.toolCallsMap.entries()) {\n if (toolCall.id === event.toolCallId) {\n toolCall.function.arguments += event.delta\n break\n }\n }\n }\n\n /**\n * Complete a tool call with its final input\n * Called when TOOL_CALL_END is received\n */\n completeToolCall(event: ToolCallEndEvent): void {\n for (const [, toolCall] of this.toolCallsMap.entries()) {\n if (toolCall.id === event.toolCallId) {\n if (event.input !== undefined) {\n // Normalize null/non-object to {} (e.g. Anthropic empty tool_use blocks)\n const normalized =\n event.input && typeof event.input === 'object' ? event.input : {}\n toolCall.function.arguments = JSON.stringify(normalized)\n }\n break\n }\n }\n }\n\n /**\n * Check if there are any complete tool calls to execute\n */\n hasToolCalls(): boolean {\n return this.getToolCalls().length > 0\n }\n\n /**\n * Get all complete tool calls (filtered for valid ID and name)\n */\n getToolCalls(): Array<ToolCall> {\n return Array.from(this.toolCallsMap.values()).filter(\n (tc) => tc.id && tc.function.name && tc.function.name.trim().length > 0,\n )\n }\n\n /**\n * Execute all tool calls and return tool result messages\n * Yields TOOL_CALL_END events for streaming\n * @param finishEvent - RUN_FINISHED event from the stream\n */\n async *executeTools(\n finishEvent: RunFinishedEvent,\n ): AsyncGenerator<ToolCallEndEvent, Array<ModelMessage>, void> {\n const toolCallsArray = this.getToolCalls()\n const toolResults: Array<ModelMessage> = []\n\n for (const toolCall of toolCallsArray) {\n const tool = this.tools.find((t) => t.name === toolCall.function.name)\n\n let toolResultContent: string\n if (tool?.execute) {\n try {\n // Parse arguments (normalize null/non-object to {} for empty tool_use blocks)\n let args: unknown\n try {\n const argsString = toolCall.function.arguments.trim() || '{}'\n const parsed = JSON.parse(argsString)\n args = parsed && typeof parsed === 'object' ? parsed : {}\n } catch (parseError) {\n throw new Error(\n `Failed to parse tool arguments as JSON: ${toolCall.function.arguments}`,\n )\n }\n\n // Validate input against inputSchema (for Standard Schema compliant schemas)\n if (tool.inputSchema && isStandardSchema(tool.inputSchema)) {\n try {\n args = parseWithStandardSchema(tool.inputSchema, args)\n } catch (validationError: unknown) {\n const message =\n validationError instanceof Error\n ? validationError.message\n : 'Validation failed'\n throw new Error(\n `Input validation failed for tool ${tool.name}: ${message}`,\n )\n }\n }\n\n // Execute the tool\n let result = await tool.execute(args)\n\n // Validate output against outputSchema if provided (for Standard Schema compliant schemas)\n if (\n tool.outputSchema &&\n isStandardSchema(tool.outputSchema) &&\n result !== undefined &&\n result !== null\n ) {\n try {\n result = parseWithStandardSchema(tool.outputSchema, result)\n } catch (validationError: unknown) {\n const message =\n validationError instanceof Error\n ? validationError.message\n : 'Validation failed'\n throw new Error(\n `Output validation failed for tool ${tool.name}: ${message}`,\n )\n }\n }\n\n toolResultContent =\n typeof result === 'string' ? result : JSON.stringify(result)\n } catch (error: unknown) {\n // If tool execution fails, add error message\n const message =\n error instanceof Error ? error.message : 'Unknown error'\n toolResultContent = `Error executing tool: ${message}`\n }\n } else {\n // Tool doesn't have execute function, add placeholder\n toolResultContent = `Tool ${toolCall.function.name} does not have an execute function`\n }\n\n // Emit TOOL_CALL_END event\n yield {\n type: 'TOOL_CALL_END',\n toolCallId: toolCall.id,\n toolName: toolCall.function.name,\n model: finishEvent.model,\n timestamp: Date.now(),\n result: toolResultContent,\n }\n\n // Add tool result message\n toolResults.push({\n role: 'tool',\n content: toolResultContent,\n toolCallId: toolCall.id,\n })\n }\n\n return toolResults\n }\n\n /**\n * Clear the tool calls map for the next iteration\n */\n clear(): void {\n this.toolCallsMap.clear()\n }\n}\n\nexport interface ToolResult {\n toolCallId: string\n toolName: string\n result: any\n state?: 'output-available' | 'output-error'\n /** Duration of tool execution in milliseconds (only for server-executed tools) */\n duration?: number\n}\n\nexport interface ApprovalRequest {\n toolCallId: string\n toolName: string\n input: any\n approvalId: string\n}\n\nexport interface ClientToolRequest {\n toolCallId: string\n toolName: string\n input: any\n}\n\ninterface ExecuteToolCallsResult {\n /** Tool results ready to send to LLM */\n results: Array<ToolResult>\n /** Tools that need user approval before execution */\n needsApproval: Array<ApprovalRequest>\n /** Tools that need client-side execution */\n needsClientExecution: Array<ClientToolRequest>\n}\n\n/**\n * Helper that runs a tool execution promise while polling for pending custom events.\n * Yields any custom events that are emitted during execution, then returns the\n * execution result.\n */\nasync function* executeWithEventPolling<T>(\n executionPromise: Promise<T>,\n pendingEvents: Array<CustomEvent>,\n): AsyncGenerator<CustomEvent, T, void> {\n // Use an object to track mutable state across the async boundary\n const state = { done: false, result: undefined as T }\n const executionWithFlag = executionPromise.then((r) => {\n state.done = true\n state.result = r\n return r\n })\n\n while (!state.done) {\n // Wait for either the execution to complete or a short timeout\n await Promise.race([\n executionWithFlag,\n new Promise((resolve) => setTimeout(resolve, 10)),\n ])\n\n // Flush any pending events\n while (pendingEvents.length > 0) {\n yield pendingEvents.shift()!\n }\n }\n\n // Final flush in case events were emitted right at completion\n while (pendingEvents.length > 0) {\n yield pendingEvents.shift()!\n }\n\n return state.result\n}\n\n/**\n * Apply a middleware onBeforeToolCall decision.\n * Returns the (possibly transformed) input if execution should proceed,\n * or undefined if the tool call was skipped (result already pushed).\n * Throws MiddlewareAbortError if the decision is 'abort'.\n */\nasync function applyBeforeToolCallDecision(\n toolCall: ToolCall,\n tool: Tool,\n input: unknown,\n toolName: string,\n middlewareHooks: ToolExecutionMiddlewareHooks,\n results: Array<ToolResult>,\n): Promise<{ proceed: true; input: unknown } | { proceed: false }> {\n if (!middlewareHooks.onBeforeToolCall) {\n return { proceed: true, input }\n }\n\n const decision = await middlewareHooks.onBeforeToolCall(toolCall, tool, input)\n if (!decision) {\n return { proceed: true, input }\n }\n\n if (decision.type === 'abort') {\n throw new MiddlewareAbortError(decision.reason || 'Aborted by middleware')\n }\n\n if (decision.type === 'skip') {\n const skipResult = decision.result\n results.push({\n toolCallId: toolCall.id,\n toolName,\n result:\n typeof skipResult === 'string'\n ? safeJsonParse(skipResult)\n : skipResult || null,\n duration: 0,\n })\n if (middlewareHooks.onAfterToolCall) {\n await middlewareHooks.onAfterToolCall({\n toolCall,\n tool,\n toolName,\n toolCallId: toolCall.id,\n ok: true,\n duration: 0,\n result: skipResult,\n })\n }\n return { proceed: false }\n }\n\n return { proceed: true, input: decision.args }\n}\n\n/**\n * Execute a server-side tool with event polling, output validation, and middleware hooks.\n * Yields CustomEvent chunks during execution and pushes the result to the results array.\n */\nasync function* executeServerTool(\n toolCall: ToolCall,\n tool: Tool,\n toolName: string,\n input: unknown,\n context: ToolExecutionContext,\n pendingEvents: Array<CustomEvent>,\n results: Array<ToolResult>,\n middlewareHooks?: ToolExecutionMiddlewareHooks,\n): AsyncGenerator<CustomEvent, void, void> {\n const startTime = Date.now()\n try {\n const executionPromise = Promise.resolve(tool.execute!(input, context))\n let result = yield* executeWithEventPolling(executionPromise, pendingEvents)\n const duration = Date.now() - startTime\n\n // Flush remaining events\n while (pendingEvents.length > 0) {\n yield pendingEvents.shift()!\n }\n\n // Validate output against outputSchema if provided\n if (\n tool.outputSchema &&\n isStandardSchema(tool.outputSchema) &&\n result !== undefined &&\n result !== null\n ) {\n result = parseWithStandardSchema(tool.outputSchema, result)\n }\n\n const finalResult =\n typeof result === 'string' ? safeJsonParse(result) : result || null\n\n results.push({\n toolCallId: toolCall.id,\n toolName,\n result: finalResult,\n duration,\n })\n\n if (middlewareHooks?.onAfterToolCall) {\n await middlewareHooks.onAfterToolCall({\n toolCall,\n tool,\n toolName,\n toolCallId: toolCall.id,\n ok: true,\n duration,\n result: finalResult,\n })\n }\n } catch (error: unknown) {\n const duration = Date.now() - startTime\n\n // Flush remaining events\n while (pendingEvents.length > 0) {\n yield pendingEvents.shift()!\n }\n\n if (error instanceof MiddlewareAbortError) {\n throw error\n }\n\n const message = error instanceof Error ? error.message : 'Unknown error'\n results.push({\n toolCallId: toolCall.id,\n toolName,\n result: { error: message },\n state: 'output-error',\n duration,\n })\n\n if (middlewareHooks?.onAfterToolCall) {\n await middlewareHooks.onAfterToolCall({\n toolCall,\n tool,\n toolName,\n toolCallId: toolCall.id,\n ok: false,\n duration,\n error,\n })\n }\n }\n}\n\n/**\n * Execute tool calls based on their configuration.\n * Yields CustomEvent chunks during tool execution for real-time progress updates.\n *\n * Handles three cases:\n * 1. Client tools (no execute) - request client to execute\n * 2. Server tools with approval - check approval before executing\n * 3. Normal server tools - execute immediately\n *\n * @param toolCalls - Tool calls from the LLM\n * @param tools - Available tools with their configurations\n * @param approvals - Map of approval decisions (approval.id -> approved boolean)\n * @param clientResults - Map of client-side execution results (toolCallId -> result)\n * @param createCustomEventChunk - Factory to create CustomEvent chunks (optional)\n */\nexport async function* executeToolCalls(\n toolCalls: Array<ToolCall>,\n tools: ReadonlyArray<Tool>,\n approvals: Map<string, boolean> = new Map(),\n clientResults: Map<string, any> = new Map(),\n createCustomEventChunk?: (\n eventName: string,\n value: Record<string, any>,\n ) => CustomEvent,\n middlewareHooks?: ToolExecutionMiddlewareHooks,\n): AsyncGenerator<CustomEvent, ExecuteToolCallsResult, void> {\n const results: Array<ToolResult> = []\n const needsApproval: Array<ApprovalRequest> = []\n const needsClientExecution: Array<ClientToolRequest> = []\n\n // Create tool lookup map\n const toolMap = new Map<string, Tool>()\n for (const tool of tools) {\n toolMap.set(tool.name, tool)\n }\n\n // Batch gating: when any tool in the batch still needs an approval decision,\n // defer all execution so side effects don't happen before the user decides.\n const hasPendingApprovals = toolCalls.some((tc) => {\n const t = toolMap.get(tc.function.name)\n return t?.needsApproval && !approvals.has(`approval_${tc.id}`)\n })\n\n for (const toolCall of toolCalls) {\n const tool = toolMap.get(toolCall.function.name)\n const toolName = toolCall.function.name\n\n if (!tool) {\n // Unknown tool - return error\n results.push({\n toolCallId: toolCall.id,\n toolName,\n result: { error: `Unknown tool: ${toolName}` },\n state: 'output-error',\n })\n continue\n }\n\n // Skip non-pending tools while approvals are outstanding\n if (hasPendingApprovals) {\n if (!tool.needsApproval || approvals.has(`approval_${toolCall.id}`)) {\n continue\n }\n }\n\n // Parse arguments, throwing error if invalid JSON\n let input: unknown = {}\n const argsStr = toolCall.function.arguments.trim() || '{}'\n if (argsStr) {\n try {\n const parsed = JSON.parse(argsStr)\n // Normalize null/non-object to {} (e.g. Anthropic empty tool_use blocks)\n input = parsed && typeof parsed === 'object' ? parsed : {}\n } catch (parseError) {\n // If parsing fails, throw error to fail fast\n throw new Error(`Failed to parse tool arguments as JSON: ${argsStr}`)\n }\n }\n\n // Validate input against inputSchema (for Standard Schema compliant schemas)\n if (tool.inputSchema && isStandardSchema(tool.inputSchema)) {\n try {\n input = parseWithStandardSchema(tool.inputSchema, input)\n } catch (validationError: unknown) {\n const message =\n validationError instanceof Error\n ? validationError.message\n : 'Validation failed'\n results.push({\n toolCallId: toolCall.id,\n toolName,\n result: {\n error: `Input validation failed for tool ${tool.name}: ${message}`,\n },\n state: 'output-error',\n })\n continue\n }\n }\n\n // Create a ToolExecutionContext for this tool call with event emission\n const pendingEvents: Array<CustomEvent> = []\n const context: ToolExecutionContext = {\n toolCallId: toolCall.id,\n emitCustomEvent: (eventName: string, value: Record<string, any>) => {\n if (createCustomEventChunk) {\n pendingEvents.push(\n createCustomEventChunk(eventName, {\n ...value,\n toolCallId: toolCall.id,\n }),\n )\n }\n },\n }\n\n // CASE 1: Client-side tool (no execute function)\n if (!tool.execute) {\n // Check if tool needs approval\n if (tool.needsApproval) {\n const approvalId = `approval_${toolCall.id}`\n\n // Check if approval decision exists\n if (approvals.has(approvalId)) {\n const approved = approvals.get(approvalId)\n\n if (approved) {\n // Approved - check if client has executed\n if (clientResults.has(toolCall.id)) {\n results.push({\n toolCallId: toolCall.id,\n toolName,\n result: clientResults.get(toolCall.id),\n })\n } else {\n // Approved but not executed yet - request client execution\n needsClientExecution.push({\n toolCallId: toolCall.id,\n toolName,\n input,\n })\n }\n } else {\n // User declined\n results.push({\n toolCallId: toolCall.id,\n toolName,\n result: { error: 'User declined tool execution' },\n state: 'output-error',\n })\n }\n } else {\n // Need approval first\n needsApproval.push({\n toolCallId: toolCall.id,\n toolName: toolCall.function.name,\n input,\n approvalId,\n })\n }\n } else {\n // No approval needed - check if client has executed\n if (clientResults.has(toolCall.id)) {\n results.push({\n toolCallId: toolCall.id,\n toolName,\n result: clientResults.get(toolCall.id),\n })\n } else {\n // Request client execution\n needsClientExecution.push({\n toolCallId: toolCall.id,\n toolName,\n input,\n })\n }\n }\n continue\n }\n\n // CASE 2: Server tool with approval required\n if (tool.needsApproval) {\n const approvalId = `approval_${toolCall.id}`\n\n // Check if approval decision exists\n if (approvals.has(approvalId)) {\n const approved = approvals.get(approvalId)\n\n if (approved) {\n // Apply middleware before-hook for approved tools\n if (middlewareHooks) {\n const decision = await applyBeforeToolCallDecision(\n toolCall,\n tool,\n input,\n toolName,\n middlewareHooks,\n results,\n )\n if (!decision.proceed) continue\n input = decision.input\n }\n\n yield* executeServerTool(\n toolCall,\n tool,\n toolName,\n input,\n context,\n pendingEvents,\n results,\n middlewareHooks,\n )\n } else {\n // User declined\n results.push({\n toolCallId: toolCall.id,\n toolName,\n result: { error: 'User declined tool execution' },\n state: 'output-error',\n })\n }\n } else {\n // Need approval\n needsApproval.push({\n toolCallId: toolCall.id,\n toolName,\n input,\n approvalId,\n })\n }\n continue\n }\n\n // CASE 3: Normal server tool - execute immediately\n if (middlewareHooks) {\n const decision = await applyBeforeToolCallDecision(\n toolCall,\n tool,\n input,\n toolName,\n middlewareHooks,\n results,\n )\n if (!decision.proceed) continue\n input = decision.input\n }\n\n yield* executeServerTool(\n toolCall,\n tool,\n toolName,\n input,\n context,\n pendingEvents,\n results,\n middlewareHooks,\n )\n }\n\n return { results, needsApproval, needsClientExecution }\n}\n"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tanstack/ai",
|
|
3
|
-
"version": "0.10.
|
|
3
|
+
"version": "0.10.3",
|
|
4
4
|
"description": "Core TanStack AI library - Open source AI SDK",
|
|
5
5
|
"author": "Tanner Linsley",
|
|
6
6
|
"license": "MIT",
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
],
|
|
47
47
|
"dependencies": {
|
|
48
48
|
"partial-json": "^0.1.7",
|
|
49
|
-
"@tanstack/ai-event-client": "0.2.
|
|
49
|
+
"@tanstack/ai-event-client": "0.2.3"
|
|
50
50
|
},
|
|
51
51
|
"devDependencies": {
|
|
52
52
|
"@standard-schema/spec": "^1.1.0",
|
|
@@ -127,7 +127,10 @@ export class ToolCallManager {
|
|
|
127
127
|
for (const [, toolCall] of this.toolCallsMap.entries()) {
|
|
128
128
|
if (toolCall.id === event.toolCallId) {
|
|
129
129
|
if (event.input !== undefined) {
|
|
130
|
-
|
|
130
|
+
// Normalize null/non-object to {} (e.g. Anthropic empty tool_use blocks)
|
|
131
|
+
const normalized =
|
|
132
|
+
event.input && typeof event.input === 'object' ? event.input : {}
|
|
133
|
+
toolCall.function.arguments = JSON.stringify(normalized)
|
|
131
134
|
}
|
|
132
135
|
break
|
|
133
136
|
}
|
|
@@ -167,11 +170,12 @@ export class ToolCallManager {
|
|
|
167
170
|
let toolResultContent: string
|
|
168
171
|
if (tool?.execute) {
|
|
169
172
|
try {
|
|
170
|
-
// Parse arguments (normalize
|
|
173
|
+
// Parse arguments (normalize null/non-object to {} for empty tool_use blocks)
|
|
171
174
|
let args: unknown
|
|
172
175
|
try {
|
|
173
176
|
const argsString = toolCall.function.arguments.trim() || '{}'
|
|
174
|
-
|
|
177
|
+
const parsed = JSON.parse(argsString)
|
|
178
|
+
args = parsed && typeof parsed === 'object' ? parsed : {}
|
|
175
179
|
} catch (parseError) {
|
|
176
180
|
throw new Error(
|
|
177
181
|
`Failed to parse tool arguments as JSON: ${toolCall.function.arguments}`,
|
|
@@ -543,7 +547,9 @@ export async function* executeToolCalls(
|
|
|
543
547
|
const argsStr = toolCall.function.arguments.trim() || '{}'
|
|
544
548
|
if (argsStr) {
|
|
545
549
|
try {
|
|
546
|
-
|
|
550
|
+
const parsed = JSON.parse(argsStr)
|
|
551
|
+
// Normalize null/non-object to {} (e.g. Anthropic empty tool_use blocks)
|
|
552
|
+
input = parsed && typeof parsed === 'object' ? parsed : {}
|
|
547
553
|
} catch (parseError) {
|
|
548
554
|
// If parsing fails, throw error to fail fast
|
|
549
555
|
throw new Error(`Failed to parse tool arguments as JSON: ${argsStr}`)
|