Skip to content

Commit 505b765

Browse files
committed
fix(acp): support session/set_config_option for models
1 parent eadd050 commit 505b765

4 files changed

Lines changed: 129 additions & 20 deletions

File tree

lua/codecompanion/acp/init.lua

Lines changed: 92 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ local uv = vim.uv
5252
---@field _on_session_update function|nil
5353
---@field _modes {currentModeId: string, availableModes: table[]}|nil
5454
---@field _models {currentModelId: string, availableModels: table[]}|nil
55+
---@field _model_config_id string|nil The configOptions ID for the model selector
5556
---@field methods table
5657
local Connection = {}
5758

@@ -367,9 +368,9 @@ function Connection:_establish_session()
367368
self._modes = session_data.modes
368369
log:debug("[acp::_establish_session] %s modes: %s", source, session_data.modes)
369370
end
370-
if session_data.models then
371-
self._models = session_data.models
372-
log:debug("[acp::_establish_session] %s models: %s", source, session_data.models)
371+
if session_data.configOptions then
372+
self:_apply_config_options(session_data.configOptions)
373+
log:debug("[acp::_establish_session] %s config options applied", source)
373374
end
374375
end
375376

@@ -700,6 +701,8 @@ local DISPATCH = {
700701
self:handle_available_commands_update(m.params.sessionId, m.params.update.availableCommands)
701702
elseif m.params.update and m.params.update.sessionUpdate == "current_mode_update" then
702703
self:handle_current_mode_update(m.params.sessionId, m.params.update.modeId)
704+
elseif m.params.update and m.params.update.sessionUpdate == "config_option_update" then
705+
self:handle_config_option_update(m.params.sessionId, m.params.update.configOptions)
703706
elseif m.params.update and m.params.update.sessionUpdate == "session_info_update" then
704707
self:handle_session_info_update(m.params.sessionId, m.params.update)
705708
elseif self._loading_session and self._on_session_update then
@@ -854,6 +857,47 @@ function Connection:handle_available_commands_update(session_id, commands)
854857
acp_commands.register_commands(session_id, commands)
855858
end
856859

860+
---Extract model information from ACP configOptions
861+
---Ref: https://agentclientprotocol.com/protocol/schema#setsessionconfigoptionrequest
862+
---@param config_options table[] Array of SessionConfigOption
863+
function Connection:_apply_config_options(config_options)
864+
for _, opt in ipairs(config_options) do
865+
if opt.category == "model" and opt.type == "select" then
866+
self._model_config_id = opt.id
867+
868+
local available = {}
869+
for _, item in ipairs(opt.options or {}) do
870+
if item.group then
871+
for _, model in ipairs(item.options or {}) do
872+
table.insert(available, { modelId = model.value, name = model.name })
873+
end
874+
else
875+
table.insert(available, { modelId = item.value, name = item.name })
876+
end
877+
end
878+
879+
self._models = {
880+
availableModels = available,
881+
currentModelId = opt.currentValue,
882+
}
883+
break
884+
end
885+
end
886+
end
887+
888+
---Handle config_option_update notification
889+
---@param session_id string
890+
---@param config_options table[]|nil
891+
---@return nil
892+
function Connection:handle_config_option_update(session_id, config_options)
893+
if not session_id or session_id ~= self.session_id then
894+
return
895+
end
896+
if type(config_options) == "table" then
897+
self:_apply_config_options(config_options)
898+
end
899+
end
900+
857901
---Handle current_mode_update notification
858902
---@param session_id string
859903
---@param mode_id string
@@ -960,20 +1004,56 @@ function Connection:get_models()
9601004
return self._models
9611005
end
9621006

963-
---Set a model
1007+
---Set a model via session/set_config_option
9641008
---@param model_id string The ID of the model to switch to
9651009
---@return boolean success
9661010
function Connection:set_model(model_id)
967-
return self:_set_session_property({
1011+
if not self.session_id then
1012+
log:error("[acp::set_model] Connection not established")
1013+
return false
1014+
end
1015+
1016+
if not self._models or not self._model_config_id then
1017+
log:error("[acp::set_model] Agent does not support changing models")
1018+
return false
1019+
end
1020+
1021+
local valid = false
1022+
for _, item in ipairs(self._models.availableModels or {}) do
1023+
if item.modelId == model_id then
1024+
valid = true
1025+
break
1026+
end
1027+
end
1028+
1029+
if not valid then
1030+
log:error("[acp::set_model] Invalid model ID: %s", model_id)
1031+
return false
1032+
end
1033+
1034+
if model_id == self._models.currentModelId then
1035+
return true
1036+
end
1037+
1038+
local result = self:send_rpc_request(METHODS.SESSION_SET_CONFIG_OPTION, {
1039+
sessionId = self.session_id,
1040+
configId = self._model_config_id,
9681041
value = model_id,
969-
collection = self._models,
970-
items_key = "availableModels",
971-
current_key = "currentModelId",
972-
id_key = "modelId",
973-
rpc_method = METHODS.SESSION_SET_MODEL,
974-
rpc_param_key = "modelId",
975-
label = "model",
9761042
})
1043+
1044+
if not result then
1045+
log:error("[acp::set_model] Failed to set model to %s", model_id)
1046+
return false
1047+
end
1048+
1049+
if result.configOptions then
1050+
self:_apply_config_options(result.configOptions)
1051+
else
1052+
self._models.currentModelId = model_id
1053+
end
1054+
1055+
log:debug("[acp::set_model] Changed model to %s", model_id)
1056+
return true
9771057
end
9781058

9791059
---Shared helper to validate and set a session property (mode or model)

lua/codecompanion/acp/methods.lua

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@ return {
77
SESSION_NEW = "session/new",
88
SESSION_PROMPT = "session/prompt",
99
SESSION_REQUEST_PERMISSION = "session/request_permission",
10+
SESSION_SET_CONFIG_OPTION = "session/set_config_option",
1011
SESSION_SET_MODE = "session/set_mode",
11-
SESSION_SET_MODEL = "session/set_model",
1212
SESSION_UPDATE = "session/update",
1313
FS_READ_TEXT_FILE = "fs/read_text_file",
1414
FS_WRITE_TEXT_FILE = "fs/write_text_file",

lua/codecompanion/adapters/acp/init.lua

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,18 @@ function Adapter.resolve(adapter, opts)
106106
if not config.adapters.acp or not config.adapters.acp[adapter] then
107107
return log:error("[adapters::acp::resolve] Adapter not found: %s", adapter)
108108
end
109-
adapter = Adapter.extend(config.adapters.acp[adapter] or adapter, opts)
109+
adapter = Adapter.extend(config.adapters.acp[adapter] or adapter)
110+
111+
if opts.model then
112+
adapter = vim.tbl_deep_extend("force", adapter, {
113+
defaults = { model = opts.model },
114+
})
115+
end
116+
if opts.mode then
117+
adapter = vim.tbl_deep_extend("force", adapter, {
118+
defaults = { mode = opts.mode },
119+
})
120+
end
110121
elseif type(adapter) == "function" then
111122
adapter = adapter()
112123
end

tests/acp/test_acp.lua

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,16 @@ T["ACP Connection"]["session/load restores modes and models metadata"] = functio
199199
elseif method == "session/load" then
200200
return {
201201
modes = { { id = "code", name = "Code" }, { id = "ask", name = "Ask" } },
202-
models = { { id = "claude-sonnet", name = "Claude Sonnet" } },
202+
configOptions = {
203+
{
204+
type = "select",
205+
id = "model",
206+
name = "Model",
207+
category = "model",
208+
currentValue = "claude-sonnet",
209+
options = { { value = "claude-sonnet", name = "Claude Sonnet" } },
210+
},
211+
},
203212
}
204213
end
205214
end
@@ -215,8 +224,8 @@ T["ACP Connection"]["session/load restores modes and models metadata"] = functio
215224
h.eq(result.ok, true)
216225
h.eq(#result.modes, 2)
217226
h.eq(result.modes[1].id, "code")
218-
h.eq(#result.models, 1)
219-
h.eq(result.models[1].id, "claude-sonnet")
227+
h.eq(#result.models.availableModels, 1)
228+
h.eq(result.models.availableModels[1].modelId, "claude-sonnet")
220229
end
221230

222231
T["ACP Connection"]["session/new stores modes and models metadata"] = function()
@@ -229,7 +238,16 @@ T["ACP Connection"]["session/new stores modes and models metadata"] = function()
229238
return {
230239
sessionId = "new-session",
231240
modes = { { id = "agent", name = "Agent" } },
232-
models = { { id = "claude-opus", name = "Claude Opus" } },
241+
configOptions = {
242+
{
243+
type = "select",
244+
id = "model",
245+
name = "Model",
246+
category = "model",
247+
currentValue = "claude-opus",
248+
options = { { value = "claude-opus", name = "Claude Opus" } },
249+
},
250+
},
233251
}
234252
end
235253
end
@@ -245,8 +263,8 @@ T["ACP Connection"]["session/new stores modes and models metadata"] = function()
245263
h.eq(result.ok, true)
246264
h.eq(#result.modes, 1)
247265
h.eq(result.modes[1].id, "agent")
248-
h.eq(#result.models, 1)
249-
h.eq(result.models[1].id, "claude-opus")
266+
h.eq(#result.models.availableModels, 1)
267+
h.eq(result.models.availableModels[1].modelId, "claude-opus")
250268
end
251269

252270
T["ACP Connection"]["falls back to session/new if session/load fails"] = function()

0 commit comments

Comments
 (0)