@@ -164,23 +164,30 @@ let setupTokensOut = 0;
164164
165165async function callLLM ( messages : any [ ] ) : Promise < any > {
166166 const { url, key, model } = getLLMConfig ( ) ;
167- const res = await fetch ( url , {
168- method : "POST" ,
169- headers : {
170- "Content-Type" : "application/json" ,
171- Authorization : `Bearer ${ key } ` ,
172- "X-Title" : "Vendi" ,
173- } ,
174- body : JSON . stringify ( { model, messages, tools, max_tokens : 4096 , parallel_tool_calls : true } ) ,
175- } ) ;
176- if ( ! res . ok ) throw new Error ( `LLM error ${ res . status } : ${ await res . text ( ) } ` ) ;
177- const json = await res . json ( ) ;
178- const usage = json ?. usage ;
179- if ( usage ) {
180- setupTokensIn += usage . prompt_tokens || 0 ;
181- setupTokensOut += usage . completion_tokens || 0 ;
167+ const controller = new AbortController ( ) ;
168+ const timeout = setTimeout ( ( ) => controller . abort ( ) , 120_000 ) ; // 2 min timeout
169+ try {
170+ const res = await fetch ( url , {
171+ method : "POST" ,
172+ headers : {
173+ "Content-Type" : "application/json" ,
174+ Authorization : `Bearer ${ key } ` ,
175+ "X-Title" : "Vendi" ,
176+ } ,
177+ body : JSON . stringify ( { model, messages, tools, max_tokens : 4096 , parallel_tool_calls : true } ) ,
178+ signal : controller . signal ,
179+ } ) ;
180+ if ( ! res . ok ) throw new Error ( `LLM error ${ res . status } : ${ await res . text ( ) } ` ) ;
181+ const json = await res . json ( ) ;
182+ const usage = json ?. usage ;
183+ if ( usage ) {
184+ setupTokensIn += usage . prompt_tokens || 0 ;
185+ setupTokensOut += usage . completion_tokens || 0 ;
186+ }
187+ return json ;
188+ } finally {
189+ clearTimeout ( timeout ) ;
182190 }
183- return json ;
184191}
185192
186193// ── DB persistence helpers ──────────────────────────────────────────────────
@@ -260,15 +267,24 @@ async function runAgentLoop(ctx: LoopContext): Promise<string> {
260267 ctx . llmMessages . push ( msg ) ;
261268
262269 if ( msg . tool_calls && msg . tool_calls . length > 0 ) {
263- for ( const tc of msg . tool_calls ) {
264- const { name, arguments : argsStr } = tc . function ;
265- const statusMap : Record < string , string > = { read_file : "Reading files..." , list_files : "Browsing project..." , search_code : "Searching code..." , run_command : "Running commands..." } ;
266-
267- await persistState ( ctx . projectId , { status : statusMap [ name ] || "Working..." } ) ;
270+ const toolNames = msg . tool_calls . map ( ( tc : any ) => tc . function . name ) ;
271+ const statusMsg = toolNames . length > 1
272+ ? `Running ${ toolNames . length } tools in parallel...`
273+ : { read_file : "Reading files..." , list_files : "Browsing project..." , search_code : "Searching code..." , run_command : "Running commands..." } [ toolNames [ 0 ] ] || "Working..." ;
274+ await persistState ( ctx . projectId , { status : statusMsg } ) ;
275+
276+ // Execute all tool calls in parallel
277+ const results = await Promise . all (
278+ msg . tool_calls . map ( async ( tc : any ) => {
279+ const { name, arguments : argsStr } = tc . function ;
280+ let args : Record < string , string > ;
281+ try { args = JSON . parse ( argsStr ) ; } catch { args = { } ; }
282+ const result = await executeTool ( ctx . sandbox , name , args ) ;
283+ return { tc, name, args, result } ;
284+ } )
285+ ) ;
268286
269- let args : Record < string , string > ;
270- try { args = JSON . parse ( argsStr ) ; } catch { args = { } ; }
271- const result = await executeTool ( ctx . sandbox , name , args ) ;
287+ for ( const { tc, name, args, result } of results ) {
272288 ctx . toolCalls . push ( {
273289 id : crypto . randomUUID ( ) ,
274290 name,
@@ -279,7 +295,7 @@ async function runAgentLoop(ctx: LoopContext): Promise<string> {
279295 ctx . llmMessages . push ( { role : "tool" , tool_call_id : tc . id , content : result } ) ;
280296 }
281297
282- // Persist after each LLM iteration (batch tool calls)
298+ // Persist once after all tool calls complete
283299 await persistState ( ctx . projectId , {
284300 toolCalls : ctx . toolCalls ,
285301 llmHistory : ctx . llmMessages ,
0 commit comments