@@ -4,17 +4,14 @@ local log = require("codecompanion.utils.log")
44local utils = require (" codecompanion.utils" )
55local watch = require (" codecompanion.interactions.shared.watch" )
66
7- -- Keep a record of UI changes in the chat buffer
8-
97--- @class CodeCompanion.Chat.ACPHandler
108--- @field chat CodeCompanion.Chat
119--- @field output table Standard output message from the Agent
1210--- @field reasoning table Reasoning output from the Agent
1311--- @field tools table<string , table> Cache of tool calls by their ID
12+ --- @field ui_state table<string , table> Cache of tool call UI states (line_number , icon_id ) by tool call ID
1413local ACPHandler = {}
1514
16- local ACPHandlerUI = {} -- Cache of tool call UI states by chat buffer
17-
1815--- @param chat CodeCompanion.Chat
1916--- @return CodeCompanion.Chat.ACPHandler
2017function ACPHandler .new (chat )
@@ -23,19 +20,12 @@ function ACPHandler.new(chat)
2320 output = {},
2421 reasoning = {},
2522 tools = {},
23+ ui_state = {},
2624 }, { __index = ACPHandler })
2725
28- ACPHandlerUI [chat .bufnr ] = {}
29-
3026 return self --[[ @type CodeCompanion.Chat.ACPHandler]]
3127end
3228
33- --- Return the ACP client
34- --- @return CodeCompanion.ACP.Connection
35- local get_client = function ()
36- return require (" codecompanion.acp" )
37- end
38-
3929--- Merge an incoming tool call/update into the cache
4030--- @param existing table | nil
4131--- @param incoming table | nil
6656--- @return boolean success
6757function ACPHandler :ensure_connection ()
6858 if not self .chat .acp_connection then
69- self .chat .acp_connection = get_client ( ).new ({
59+ self .chat .acp_connection = require ( " codecompanion.acp " ).new ({
7060 adapter = self .chat .adapter , --[[ @type CodeCompanion.ACPAdapter]]
7161 })
7262
@@ -155,16 +145,16 @@ function ACPHandler:create_and_send_prompt(payload)
155145 self :handle_thought_chunk (content )
156146 end )
157147 :on_tool_call (function (tool_call )
158- self :handle_tool_call (tool_call )
148+ self :process_tool_call (tool_call )
159149 end )
160- :on_tool_update (function (tool_update )
161- self :handle_tool_update ( tool_update )
150+ :on_tool_update (function (tool_call )
151+ self :process_tool_call ( tool_call )
162152 end )
163153 :on_permission_request (function (request )
164154 self :handle_permission_request (request )
165155 end )
166- :on_complete (function (stop_reason )
167- self :handle_completion (stop_reason )
156+ :on_complete (function ()
157+ self :handle_completion ()
168158 end )
169159 :on_error (function (error )
170160 self :handle_error (error )
@@ -199,12 +189,10 @@ end
199189--- @param tool_call table
200190--- @return nil
201191function ACPHandler :process_tool_call (tool_call )
202- -- Cache the tool call to handle processing later on, such as a later permission request
203192 local id = tool_call .toolCallId
204193
205- local prev = self .tools [id ]
206- local merged = merge_tool_call (prev , tool_call )
207- tool_call = merged or tool_call
194+ local merged = merge_tool_call (self .tools [id ], tool_call )
195+ tool_call = merged
208196
209197 local ok , content = pcall (formatter .tool_message , tool_call , self .chat .adapter )
210198 if not ok then
@@ -218,31 +206,31 @@ function ACPHandler:process_tool_call(tool_call)
218206 self .tools [id ] = merged
219207 end
220208
221- -- If the tool call has already written output to the chat buffer, then we can
222- -- update it rather than adding a new line. We do this by keeping track in
223- -- a global cache, segmented by chat buffer and tool call IDs
224- if ACPHandlerUI [self .chat .bufnr ][id ] then
225- local match = ACPHandlerUI [self .chat .bufnr ][id ]
226- -- Whilst I've tried to account for all types of ACP tool output, I'm taking
227- -- a cautious approach and wrapping line updates. Any failures and we'll
228- -- just write the tool output onto a new line in the chat buffer
229- ok , _ = pcall (function ()
230- self .chat :update_buf_line (
231- match .line_number ,
232- content ,
233- { status = tool_call .status , icon_id = match .icon_id , priority = 120 , virt_text_pos = " inline" }
234- )
235- end )
209+ -- If the tool call has already written output to the chat buffer, update the
210+ -- existing line rather than adding a new one
211+ local cached = self .ui_state [id ]
212+ if cached then
213+ local update_ok , _ , new_icon_id = pcall (
214+ self .chat .update_buf_line ,
215+ self .chat ,
216+ cached .line_number ,
217+ content ,
218+ { status = tool_call .status , icon_id = cached .icon_id , priority = 120 , virt_text_pos = " inline" }
219+ )
236220
237- -- Cleanup the cache
238- if tool_call .status == " completed" then
239- ACPHandlerUI [self .chat .bufnr ][id ] = nil
221+ if update_ok then
222+ if tool_call .status == " completed" then
223+ self .ui_state [id ] = nil
224+ elseif new_icon_id then
225+ cached .icon_id = new_icon_id
226+ end
227+ return
240228 end
241229
242- if ok then
243- return
230+ if tool_call . status == " completed " then
231+ self . ui_state [ id ] = nil
244232 end
245- log :debug (" [ACP::Handler] Failed to update tool call line for toolCallId %s" , tool_call . toolCallId )
233+ log :debug (" [ACP::Handler] Failed to update tool call line for toolCallId %s" , id )
246234 end
247235
248236 table.insert (self .output , content )
@@ -257,19 +245,7 @@ function ACPHandler:process_tool_call(tool_call)
257245 type = self .chat .MESSAGE_TYPES .TOOL_MESSAGE ,
258246 })
259247
260- ACPHandlerUI [self .chat .bufnr ][id ] = { line_number = line_number , icon_id = icon_id }
261- end
262-
263- --- Handle tool call notifications
264- --- @param tool_call table
265- function ACPHandler :handle_tool_call (tool_call )
266- return self :process_tool_call (tool_call )
267- end
268-
269- --- Handle tool call updates and their respective status
270- --- @param tool_call table
271- function ACPHandler :handle_tool_update (tool_call )
272- return self :process_tool_call (tool_call )
248+ self .ui_state [id ] = { line_number = line_number , icon_id = icon_id }
273249end
274250
275251--- Handle permission requests from the agent
@@ -302,8 +278,7 @@ function ACPHandler:handle_permission_request(request)
302278end
303279
304280--- Handle completion
305- --- @param stop_reason string | nil
306- function ACPHandler :handle_completion (stop_reason )
281+ function ACPHandler :handle_completion ()
307282 if not self .chat .status or self .chat .status == " " then
308283 self .chat .status = " success"
309284 end
0 commit comments