2025-11-15 06:54:36 +00:00
import z from "zod"
import { Tool } from "./tool"
import DESCRIPTION from "./batch.txt"
2025-12-17 16:23:35 +00:00
const DISALLOWED = new Set ( [ "batch" ] )
2025-11-15 06:54:36 +00:00
const FILTERED_FROM_SUGGESTIONS = new Set ( [ "invalid" , "patch" , . . . DISALLOWED ] )
export const BatchTool = Tool . define ( "batch" , async ( ) = > {
return {
description : DESCRIPTION ,
parameters : z.object ( {
tool_calls : z
. array (
z . object ( {
tool : z.string ( ) . describe ( "The name of the tool to execute" ) ,
parameters : z.object ( { } ) . loose ( ) . describe ( "Parameters for the tool" ) ,
} ) ,
)
. min ( 1 , "Provide at least one tool call" )
. describe ( "Array of tool calls to execute in parallel" ) ,
} ) ,
formatValidationError ( error ) {
const formattedErrors = error . issues
. map ( ( issue ) = > {
const path = issue . path . length > 0 ? issue . path . join ( "." ) : "root"
return ` - ${ path } : ${ issue . message } `
} )
. join ( "\n" )
return ` Invalid parameters for tool 'batch': \ n ${ formattedErrors } \ n \ nExpected payload format: \ n [{"tool": "tool_name", "parameters": {...}}, {...}] `
} ,
async execute ( params , ctx ) {
2025-11-16 22:31:41 +00:00
const { Session } = await import ( "../session" )
2025-11-15 06:54:36 +00:00
const { Identifier } = await import ( "../id/id" )
2026-01-19 21:44:58 +00:00
const toolCalls = params . tool_calls . slice ( 0 , 25 )
const discardedCalls = params . tool_calls . slice ( 25 )
2025-11-15 06:54:36 +00:00
const { ToolRegistry } = await import ( "./registry" )
2026-01-18 06:35:09 +00:00
const availableTools = await ToolRegistry . tools ( { modelID : "" , providerID : "" } )
2025-11-15 06:54:36 +00:00
const toolMap = new Map ( availableTools . map ( ( t ) = > [ t . id , t ] ) )
const executeCall = async ( call : ( typeof toolCalls ) [ 0 ] ) = > {
2025-11-16 22:31:41 +00:00
const callStartTime = Date . now ( )
2025-11-17 06:02:05 +00:00
const partID = Identifier . ascending ( "part" )
2025-11-15 06:54:36 +00:00
try {
2025-11-16 22:31:41 +00:00
if ( DISALLOWED . has ( call . tool ) ) {
throw new Error (
` Tool ' ${ call . tool } ' is not allowed in batch. Disallowed tools: ${ Array . from ( DISALLOWED ) . join ( ", " ) } ` ,
)
}
2025-11-15 06:54:36 +00:00
const tool = toolMap . get ( call . tool )
if ( ! tool ) {
const availableToolsList = Array . from ( toolMap . keys ( ) ) . filter ( ( name ) = > ! FILTERED_FROM_SUGGESTIONS . has ( name ) )
2025-12-17 16:23:35 +00:00
throw new Error (
` Tool ' ${ call . tool } ' not in registry. External tools (MCP, environment) cannot be batched - call them directly. Available tools: ${ availableToolsList . join ( ", " ) } ` ,
)
2025-11-15 06:54:36 +00:00
}
const validatedParams = tool . parameters . parse ( call . parameters )
2025-11-17 06:02:05 +00:00
await Session . updatePart ( {
id : partID ,
messageID : ctx.messageID ,
sessionID : ctx.sessionID ,
type : "tool" ,
tool : call.tool ,
callID : partID ,
state : {
status : "running" ,
input : call.parameters ,
time : {
start : callStartTime ,
} ,
} ,
} )
2025-11-15 06:54:36 +00:00
const result = await tool . execute ( validatedParams , { . . . ctx , callID : partID } )
2026-01-30 21:55:00 +00:00
const attachments = result . attachments ? . map ( ( attachment ) = > ( {
. . . attachment ,
id : Identifier.ascending ( "part" ) ,
messageID : ctx.messageID ,
sessionID : ctx.sessionID ,
} ) )
2025-11-15 06:54:36 +00:00
2025-11-16 22:31:41 +00:00
await Session . updatePart ( {
id : partID ,
messageID : ctx.messageID ,
sessionID : ctx.sessionID ,
type : "tool" ,
tool : call.tool ,
callID : partID ,
state : {
status : "completed" ,
input : call.parameters ,
output : result.output ,
title : result.title ,
metadata : result.metadata ,
2026-01-30 21:55:00 +00:00
attachments ,
2025-11-16 22:31:41 +00:00
time : {
start : callStartTime ,
end : Date.now ( ) ,
} ,
} ,
} )
2025-11-15 06:54:36 +00:00
return { success : true as const , tool : call.tool , result }
} catch ( error ) {
2025-11-16 22:31:41 +00:00
await Session . updatePart ( {
id : partID ,
messageID : ctx.messageID ,
sessionID : ctx.sessionID ,
type : "tool" ,
tool : call.tool ,
callID : partID ,
state : {
status : "error" ,
input : call.parameters ,
error : error instanceof Error ? error.message : String ( error ) ,
time : {
start : callStartTime ,
end : Date.now ( ) ,
} ,
} ,
} )
2025-11-15 06:54:36 +00:00
return { success : false as const , tool : call.tool , error }
}
}
2025-11-16 22:31:41 +00:00
const results = await Promise . all ( toolCalls . map ( ( call ) = > executeCall ( call ) ) )
2025-11-17 06:02:05 +00:00
// Add discarded calls as errors
const now = Date . now ( )
for ( const call of discardedCalls ) {
const partID = Identifier . ascending ( "part" )
await Session . updatePart ( {
id : partID ,
messageID : ctx.messageID ,
sessionID : ctx.sessionID ,
type : "tool" ,
tool : call.tool ,
callID : partID ,
state : {
status : "error" ,
input : call.parameters ,
2026-01-19 21:44:58 +00:00
error : "Maximum of 25 tools allowed in batch" ,
2025-11-17 06:02:05 +00:00
time : { start : now , end : now } ,
} ,
} )
results . push ( {
success : false as const ,
tool : call.tool ,
2026-01-19 21:44:58 +00:00
error : new Error ( "Maximum of 25 tools allowed in batch" ) ,
2025-11-17 06:02:05 +00:00
} )
}
2025-11-15 06:54:36 +00:00
2025-11-17 06:02:05 +00:00
const successfulCalls = results . filter ( ( r ) = > r . success ) . length
const failedCalls = results . length - successfulCalls
2025-11-15 06:54:36 +00:00
const outputMessage =
failedCalls > 0
2025-11-17 06:02:05 +00:00
? ` Executed ${ successfulCalls } / ${ results . length } tools successfully. ${ failedCalls } failed. `
: ` All ${ successfulCalls } tools executed successfully. \ n \ nKeep using the batch tool for optimal performance in your next response! `
2025-11-15 06:54:36 +00:00
return {
2025-11-17 06:02:05 +00:00
title : ` Batch execution ( ${ successfulCalls } / ${ results . length } successful) ` ,
2025-11-15 06:54:36 +00:00
output : outputMessage ,
attachments : results.filter ( ( result ) = > result . success ) . flatMap ( ( r ) = > r . result . attachments ? ? [ ] ) ,
metadata : {
2025-11-17 06:02:05 +00:00
totalCalls : results.length ,
2025-11-15 06:54:36 +00:00
successful : successfulCalls ,
failed : failedCalls ,
2025-11-17 06:02:05 +00:00
tools : params.tool_calls.map ( ( c ) = > c . tool ) ,
2025-11-15 06:54:36 +00:00
details : results.map ( ( r ) = > ( { tool : r.tool , success : r.success } ) ) ,
} ,
}
} ,
}
} )