2121local METHODS = require (" codecompanion.acp.methods" )
2222local PromptBuilder = require (" codecompanion.acp.prompt_builder" )
2323local adapter_utils = require (" codecompanion.utils.adapters" )
24+ local async = require (" codecompanion.utils.async" )
2425local config = require (" codecompanion.config" )
2526local jsonrpc = require (" codecompanion.utils.jsonrpc" )
2627local log = require (" codecompanion.utils.log" )
@@ -53,6 +54,7 @@ local uv = vim.uv
5354--- @field _modes { currentModeId : string , availableModes : table[] }| nil
5455--- @field _models { currentModelId : string , availableModels : table[] }| nil
5556--- @field _model_config_id string | nil The configOptions ID for the model selector
57+ --- @field _pending_callbacks table<number , function> Async callbacks keyed by request ID
5658--- @field methods table
5759local Connection = {}
5860
@@ -86,12 +88,13 @@ function Connection.new(args)
8688 local self = setmetatable ({
8789 adapter = args .adapter ,
8890 adapter_modified = {},
91+ methods = methods ,
8992 pending_responses = {},
9093 session_id = args .session_id ,
91- methods = methods ,
92- _initialized = false ,
9394 _authenticated = false ,
95+ _initialized = false ,
9496 _modes = nil ,
97+ _pending_callbacks = {},
9598 _state = { handle = nil , id_gen = jsonrpc .IdGenerator .new (), line_buffer = jsonrpc .LineBuffer .new () },
9699 }, { __index = Connection }) --- @cast self CodeCompanion.ACP.Connection
97100
@@ -544,7 +547,7 @@ function Connection:start_agent_process()
544547 return true
545548end
546549
547- --- Send a synchronous request and wait for response
550+ --- If called inside an async coroutine, yields until the response arrives, or fallsback to sync
548551--- @param method string
549552--- @param params table
550553--- @return table | nil
@@ -560,6 +563,14 @@ function Connection:send_rpc_request(method, params)
560563 return nil
561564 end
562565
566+ -- Async path: yield and let store_rpc_response resume us
567+ if coroutine.running () then
568+ return async .wait (function (callback )
569+ self ._pending_callbacks [id ] = callback
570+ end )
571+ end
572+
573+ -- Sync fallback
563574 return self :wait_for_rpc_response (id )
564575end
565576
@@ -664,24 +675,36 @@ function Connection:handle_rpc_message(line)
664675 end
665676end
666677
667- --- Handle response to our request
678+ --- Handles the response to the request
668679--- @param response table
669680function Connection :store_rpc_response (response )
670- if response .error then
671- self .pending_responses [response .id ] = { nil , response .error }
681+ local function forward_error_to_prompt ()
682+ if not response .error or not self ._active_prompt or not self ._active_prompt .handle_error then
683+ return
684+ end
685+ self .methods .schedule (function ()
686+ local error_msg = response .error .message or " Unknown error"
687+ if response .error .data and response .error .data .error then
688+ error_msg = response .error .data .error
689+ end
690+ self ._active_prompt :handle_error (error_msg )
691+ end )
692+ end
672693
673- -- Sometimes errors are passed as part of the response so we need to handle them
674- if self ._active_prompt and self ._active_prompt .handle_error then
675- self .methods .schedule (function ()
676- local error_msg = response .error .message or " Unknown error"
677- if response .error .data and response .error .data .error then
678- error_msg = response .error .data .error
679- end
694+ -- Async path: resume the waiting coroutine via its callback
695+ local cb = self ._pending_callbacks [response .id ]
696+ if cb then
697+ self ._pending_callbacks [response .id ] = nil
698+ self .methods .schedule (function ()
699+ cb ((not response .error ) and response .result or nil )
700+ end )
701+ return forward_error_to_prompt ()
702+ end
680703
681- self . _active_prompt : handle_error ( error_msg )
682- end )
683- end
684- return
704+ -- Sync path: store for polling
705+ if response . error then
706+ self . pending_responses [ response . id ] = { nil , response . error }
707+ return forward_error_to_prompt ()
685708 end
686709 self .pending_responses [response .id ] = { response .result , nil }
687710end
@@ -951,12 +974,19 @@ function Connection:handle_process_exit(code, signal)
951974 self .adapter_modified .handlers .on_exit (self .adapter_modified , code )
952975 end
953976
977+ -- Fire any pending async callbacks so coroutines don't hang
978+ for id , cb in pairs (self ._pending_callbacks ) do
979+ self ._pending_callbacks [id ] = nil
980+ pcall (cb , nil )
981+ end
982+
954983 -- Always clean up state
955984 self .adapter_modified = nil
956- self ._initialized = false
957985 self ._authenticated = false
958- self .session_id = nil
986+ self ._initialized = false
987+ self ._pending_callbacks = {}
959988 self .pending_responses = {}
989+ self .session_id = nil
960990
961991 if self ._active_prompt and self ._active_prompt .handle_done then
962992 pcall (function ()
0 commit comments