@@ -93,24 +93,24 @@ local function cmd_to_func_tool(tool)
9393end
9494
9595--- @class CodeCompanion.Tools.Orchestrator
96- --- @field tools CodeCompanion.Tools
97- --- @field handlers table<string , function>
9896--- @field id number The id of the tools coordinator
9997--- @field index number The index of the current command
98+ --- @field handlers table<string , function>
10099--- @field output table<string , function>
101- --- @field tool CodeCompanion.Tools.Tool
102100--- @field queue CodeCompanion.Tools.Orchestrator.Queue
103- --- @field status string
104- --- @field current_tool_stdout table Output accumulator for the current tool only (reset per-tool )
101+ --- @field status string The status of the tool execution " success" | " error"
102+ --- @field tool CodeCompanion.Tools.Tool The current tool being executed
103+ --- @field tool_output table ? The output collected from the tool
104+ --- @field tools CodeCompanion.Tools
105105local Orchestrator = {}
106106
107107--- @param tools CodeCompanion.Tools
108108--- @param id number
109109function Orchestrator .new (tools , id )
110110 local self = setmetatable ({
111- tools = tools ,
112111 id = id ,
113112 queue = Queue .new (),
113+ tools = tools ,
114114 }, { __index = Orchestrator })
115115
116116 _G .codecompanion_cancel_tool = false
120120
121121--- Add the tool's handlers to the executor
122122--- @return nil
123- function Orchestrator :setup_handlers ()
123+ function Orchestrator :_setup_handlers ()
124124 self .handlers = {
125125 setup = function ()
126126 if not self .tool then
@@ -212,7 +212,7 @@ function Orchestrator:setup_handlers()
212212 end
213213
214214 if self .tool .output and self .tool .output .success then
215- self .tool .output .success (self .tool , self .tools , cmd , self .current_tool_stdout or {})
215+ self .tool .output .success (self .tool , self .tools , cmd , self .tool_output or {})
216216 else
217217 send_response_to_chat (self , fmt (" Executed `%s`" , self .tool .name ))
218218 end
@@ -223,48 +223,45 @@ end
223223--- When the tools coordinator is finished, finalize it via an autocmd
224224--- @param self CodeCompanion.Tools.Orchestrator
225225--- @return nil
226- local function finalize_tools ( self )
226+ function Orchestrator : _finalize_tools ( )
227227 return utils .fire (" ToolsFinished" , { id = self .id , bufnr = self .tools .bufnr })
228228end
229229
230230--- Setup the tool to be executed
231231--- @param input ? any
232232--- @return nil
233- function Orchestrator :setup (input )
233+ function Orchestrator :setup_next_tool (input )
234234 if self .queue :is_empty () then
235- return finalize_tools ( self )
235+ return self : _finalize_tools ( )
236236 end
237237
238238 -- Get the next tool to run
239239 self .tool = self .queue :pop ()
240- self .current_tool_stdout = {}
240+ self .tool_output = {}
241241
242- -- Setup the handlers
243- self :setup_handlers ()
242+ self :_setup_handlers ()
244243 self .handlers .setup () -- Call this early as cmd_runner needs to setup its cmds dynamically
245244
246- self .tool = cmd_to_func_tool (self .tool ) -- transform cmd-based tools to func-based
245+ -- Transform cmd-based tools to func-based
246+ self .tool = cmd_to_func_tool (self .tool )
247247
248248 -- Get the first command to run
249249 local cmd = self .tool .cmds [1 ]
250- log :debug (" Orchestrator:execute - `%s` tool" , self .tool .name )
250+ log :debug (" [ Orchestrator::setup_next_tool] `%s` tool" , self .tool .name )
251251
252252 -- Check if the tool requires approval
253253 if self .tool .opts and not vim .g .codecompanion_yolo_mode then
254254 local require_approval_before = self .tool .opts .require_approval_before
255255
256- -- Users can set this to be a function if necessary
257256 if require_approval_before and type (require_approval_before ) == " function" then
258257 require_approval_before = require_approval_before (self .tool , self .tools )
259258 end
260-
261- -- Anything that isn't a boolean will get evaluated with a prompt condition
262259 if require_approval_before and type (require_approval_before ) ~= " boolean" then
263260 require_approval_before = self .handlers .prompt_condition ()
264261 end
265262
266263 if require_approval_before then
267- log :debug (" Orchestrator:execute - Asking for approval" )
264+ log :debug (" [ Orchestrator::setup_next_tool] Asking for approval" )
268265
269266 local prompt = self .output .prompt ()
270267 if prompt == nil or prompt == " " then
@@ -274,43 +271,39 @@ function Orchestrator:setup(input)
274271 -- Schedule the confirmation dialog to ensure UI is ready
275272 vim .schedule (function ()
276273 local choice = ui_utils .confirm (prompt , { " 1 Allow always" , " 2 Allow once" , " 3 Reject" , " 4 Cancel" })
277- log :debug (" Orchestrator:execute - User choice: %s" , choice )
274+ log :debug (" [ Orchestrator::setup_next_tool] User choice: %s" , choice )
278275
279276 -- Handle invalid/failed dialog (returns 0 or nil)
280277 if not choice or choice == 0 then
281- log :warn (" Orchestrator:execute - Confirm dialog failed, rejecting tool and continuing" )
282278 self .output .rejected (cmd , { reason = " Confirmation dialog failed" })
283- self :close ()
284- return self :setup ()
279+ self :finalize_tool ()
280+ return self :setup_next_tool ()
285281 end
286282
287283 if choice == 1 or choice == 2 then
288- log :debug (" Orchestrator:execute - Tool approved" )
289284 if choice == 1 then
290285 vim .g .codecompanion_yolo_mode = true
291286 end
292- return self :execute ( cmd , input )
287+ return self :execute_tool ({ cmd = cmd , input = input } )
293288 elseif choice == 3 then
294- log :debug (" Orchestrator:execute - Tool rejected" )
295289 ui_utils .input ({ prompt = fmt (" Reason for rejecting `%s`" , self .tool .name ) }, function (i )
296290 self .output .rejected (cmd , { reason = i })
297- return self :setup ()
291+ return self :setup_next_tool ()
298292 end )
299293 else
300- log :debug (" Orchestrator:execute - Tool cancelled" )
301294 -- NOTE: Cancel current tool, then cancel all queued tools
302295 self .output .cancelled (cmd )
303- self :close ()
296+ self :finalize_tool ()
304297 self :cancel_pending_tools ()
305- return self :setup ()
298+ return self :_finalize_tools ()
306299 end
307300 end )
308301 else
309- return self :execute ( cmd , input )
302+ return self :execute_tool ({ cmd = cmd , input = input } )
310303 end
311304 else
312- log :debug (" Orchestrator:execute - No tool approval required" )
313- return self :execute ( cmd , input )
305+ log :debug (" [ Orchestrator::setup_next_tool] No tool approval required" )
306+ return self :execute_tool ({ cmd = cmd , input = input } )
314307 end
315308end
316309
@@ -322,41 +315,40 @@ function Orchestrator:cancel_pending_tools()
322315 self .tool = pending_tool
323316
324317 -- Prepare handlers/output first
325- self :setup_handlers ()
318+ self :_setup_handlers ()
326319 local first_cmd = pending_tool .cmds and pending_tool .cmds [1 ] or nil
327320
328321 local ok , err = pcall (function ()
329322 self .output .cancelled (first_cmd )
330323 end )
331324 if not ok then
332- log :error (" Failed to run cancelled handler for tool %s: %s" , tostring (pending_tool .name ), err )
325+ return log :error (" Failed to run cancelled handler for tool %s: %s" , tostring (pending_tool .name ), err )
333326 end
334327 end
335328end
336329
337330--- Execute the tool command
338- --- @param cmd function
339- --- @param input ? any
331+ --- @param args { cmd : function , input ?: any }
340332--- @return nil
341- function Orchestrator :execute ( cmd , input )
333+ function Orchestrator :execute_tool ( args )
342334 utils .fire (" ToolStarted" , { id = self .id , tool = self .tool .name , bufnr = self .tools .bufnr })
343335
344336 pcall (function ()
345337 local edit_tracker = require (" codecompanion.interactions.chat.edit_tracker" )
346338 self .execution_id = edit_tracker .start_tool_monitoring (self .tool .name , self .tools .chat , self .tool .args )
347339 end )
348340
349- return Runner .new (self , cmd , 1 ):setup (input )
341+ return Runner .new ({ index = 1 , orchestrator = self , cmd = args . cmd } ):setup (args . input )
350342end
351343
352344--- Handle an error from a tool
353- --- @param action table
354- --- @param error ? any
345+ --- @param args { action : table , error ?: any }
355346--- @return nil
356- function Orchestrator :error (action , error )
357- log :debug (" Orchestrator:error" )
347+ function Orchestrator :error (args )
358348 self .tools .status = self .tools .constants .STATUS_ERROR
359- table.insert (self .tools .stderr , error )
349+ if args .error then
350+ table.insert (self .tools .stderr , args .error )
351+ end
360352
361353 if self .tool and self .tool .name then
362354 -- Wrap this in error handling to avoid breaking the tool execution
@@ -367,25 +359,25 @@ function Orchestrator:error(action, error)
367359 end
368360
369361 local ok , err = pcall (function ()
370- self .output .error (action )
362+ self .output .error (args . action )
371363 end )
372364 if not ok then
373- log :error (" Internal error with the %s error handler: %s" , self .tool .name , err )
374365 if self .tool and self .tool .function_call then
375- self .tools .chat :add_tool_output (self .tool , string.format (" Internal error with `%s` tool" , self .tool .name ))
366+ self .tools .chat :add_tool_output (
367+ self .tool ,
368+ string.format (" Internal error with `%s` tool: %s" , self .tool .name , err )
369+ )
376370 end
377371 end
378372
379- self :close ()
380- self :setup ()
373+ self :finalize_tool ()
374+ self :setup_next_tool ()
381375end
382376
383377--- Handle a successful completion of a tool
384- --- @param action table
385- --- @param output ? any
378+ --- @param args { action : table , output ?: any }
386379--- @return nil
387- function Orchestrator :success (action , output )
388- log :debug (" Orchestrator:success" )
380+ function Orchestrator :success (args )
389381 self .tools .status = self .tools .constants .STATUS_SUCCESS
390382
391383 if self .tool and self .tool .name then
@@ -395,15 +387,15 @@ function Orchestrator:success(action, output)
395387 end )
396388 end
397389
398- if output then
399- table.insert (self .tools .stdout , output )
400- if not self .current_tool_stdout then
401- self .current_tool_stdout = {}
390+ if args . output then
391+ table.insert (self .tools .stdout , args . output )
392+ if not self .tool_output then
393+ self .tool_output = {}
402394 end
403- table.insert (self .current_tool_stdout , output )
395+ table.insert (self .tool_output , args . output )
404396 end
405397 local ok , err = pcall (function ()
406- self .output .success (action )
398+ self .output .success (args . action )
407399 end )
408400
409401 if not ok then
@@ -414,11 +406,10 @@ function Orchestrator:success(action, output)
414406 end
415407end
416408
417- --- Close the execution of the tool
409+ --- Finalize the execution of the tool
418410--- @return nil
419- function Orchestrator :close ()
411+ function Orchestrator :finalize_tool ()
420412 if self .tool then
421- log :debug (" Orchestrator:close" )
422413 pcall (function ()
423414 self .handlers .on_exit ()
424415 end )
0 commit comments