Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
16ed912
Add types for boolean config options and boolean capability
carlos-algms Jul 13, 2026
ace4237
Correct config option union ingress types
carlos-algms Jul 13, 2026
a50c628
Advertise boolean session config option capability
carlos-algms Jul 13, 2026
a86a721
Send boolean variant from set_config_option
carlos-algms Jul 13, 2026
0c6f89b
Retain all config options in an ordered list
carlos-algms Jul 13, 2026
a42cd53
Correct ordered option ingress annotations
carlos-algms Jul 13, 2026
952c538
Add generic config option change handler
carlos-algms Jul 13, 2026
3d1b573
Add settings modal floating window
carlos-algms Jul 13, 2026
c7a9971
Guard select dispatch after session changes
carlos-algms Jul 13, 2026
c733305
Add settings modal keymap and docs
carlos-algms Jul 13, 2026
40266cc
Expose config option selector
carlos-algms Jul 13, 2026
4e6d033
Cover unknown config option logging
carlos-algms Jul 13, 2026
c20fbeb
Simplify config option APIs and modal coupling
carlos-algms Jul 14, 2026
1b0c0e6
Prefix select values with dropdown icon
carlos-algms Jul 14, 2026
bda0a4e
Notify when a select option has no values
carlos-algms Jul 15, 2026
10623ab
Merge remote-tracking branch 'origin/main' into feat/acp-config-setti…
carlos-algms Jul 19, 2026
064c7c6
Format settings modal rows with descriptions and j/k nav
carlos-algms Jul 19, 2026
2400c6f
Rename settings modal to options across code and docs
carlos-algms Jul 19, 2026
543bb35
Pick nearest option row for j/k from non-option rows
carlos-algms Jul 19, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -725,6 +725,7 @@ These keybindings are automatically set in Agentic buffers:
| `<localLeader>s` | n | Switch ACP provider (preserves chat history) |
| `<localLeader>m` | n | Switch model without (preserves chat history) |
| `<localLeader>t` | n | Select thought effort level (model-dependent on Claude) |
| `<localLeader>o` | n | Open options modal |
| `q` | n | Close chat widget |
| `d` | n | Remove file, code selection, or diagnostic at cursor |
| `d` | v | Remove multiple selected files, code selections, or diagnostics |
Expand Down Expand Up @@ -758,6 +759,7 @@ your setup:
switch_provider = "<localLeader>s", -- Switch ACP provider
switch_model = "<localLeader>m", -- Switch model
change_thought_level = "<localLeader>t", -- Select thought effort level
open_options = "<localLeader>o", -- Open options modal
},

-- Keybindings for the prompt buffer only
Expand Down
2 changes: 2 additions & 0 deletions doc/agentic.txt
Original file line number Diff line number Diff line change
Expand Up @@ -577,6 +577,7 @@ These keybindings are automatically set in Agentic buffers:
<localLeader>s n Switch ACP provider (keeps history)
<localLeader>m n Switch model (keeps history)
<localLeader>t n Select thought effort level (model-dependent)
<localLeader>o n Open options modal
q n Close chat widget
d n/v Remove file/selection/diagnostic
]] n Next chat heading
Expand Down Expand Up @@ -612,6 +613,7 @@ Override default keybindings via the `keymaps` config option:
switch_provider = "<localLeader>s",
switch_model = "<localLeader>m",
change_thought_level = "<localLeader>t",
open_options = "<localLeader>o",
},
prompt = {
submit = {
Expand Down
22 changes: 7 additions & 15 deletions lua/agentic/acp/acp_client.lua
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,11 @@ function ACPClient:new(config, on_ready)
writeTextFile = false,
},
terminal = false,
session = {
configOptions = {
boolean = vim.empty_dict(),
Comment thread
carlos-algms marked this conversation as resolved.
},
},
},
auth_methods = {},
ready_listeners = {},
Expand Down Expand Up @@ -780,22 +785,9 @@ function ACPClient:set_mode(session_id, mode_id, callback)
end

--- Set a config option value for a session
--- @param session_id string
--- @param config_id string
--- @param config_value string
--- @param params agentic.acp.SetConfigOptionParams
--- @param callback fun(result: table|nil, err: agentic.acp.ACPError|nil)
function ACPClient:set_config_option(
session_id,
config_id,
config_value,
callback
)
local params = {
sessionId = session_id,
configId = config_id,
value = config_value,
}

function ACPClient:set_config_option(params, callback)
self:_send_request("session/set_config_option", params, callback)
end

Expand Down
98 changes: 98 additions & 0 deletions lua/agentic/acp/acp_client.test.lua
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ describe("ACPClient", function()
--- @type fun(message: agentic.acp.ResponseRaw)|nil
local captured_on_message

--- @type agentic.acp.InitializeParams|nil
local captured_initialize_params

local PROMPT_CAPS =
{ image = false, audio = false, embeddedContext = false }

Expand Down Expand Up @@ -71,6 +74,7 @@ describe("ACPClient", function()
transport_send_stub:invokes(function(_self, data)
local decoded = vim.json.decode(data)
if decoded.method == "initialize" and captured_on_message then
captured_initialize_params = decoded.params
captured_on_message({
jsonrpc = "2.0",
method = "initialize",
Expand Down Expand Up @@ -118,6 +122,7 @@ describe("ACPClient", function()
before_each(function()
package.loaded["agentic.acp.acp_client"] = nil
package.loaded["agentic.acp.acp_transport"] = nil
captured_initialize_params = nil

local Logger = require("agentic.utils.logger")
logger_debug_stub = spy.stub(Logger, "debug")
Expand Down Expand Up @@ -151,6 +156,28 @@ describe("ACPClient", function()
create_transport_stub:revert()
end)

describe("initialize", function()
it("advertises boolean session config options as an object", function()
assert.equal("[]", vim.json.encode({}))
create_ready_client()

assert.is_not_nil(captured_initialize_params)
--- @cast captured_initialize_params agentic.acp.InitializeParams
assert.is_not_nil(
captured_initialize_params.clientCapabilities.session
)

local config_options =
captured_initialize_params.clientCapabilities.session.configOptions
assert.is_not_nil(config_options)
--- @cast config_options agentic.acp.SessionConfigOptionsCapabilities
assert.is_not_nil(config_options.boolean)

local encoded = vim.json.encode(captured_initialize_params)
assert.is_not_nil(encoded:find('"boolean":{}', 1, true))
end)
end)

describe("list_sessions", function()
it("sends session/list request", function()
local client = create_ready_client(LIST_CAPS)
Expand Down Expand Up @@ -278,6 +305,77 @@ describe("ACPClient", function()
end)
end)

describe("set_config_option", function()
it("sends select values without a type", function()
local client = create_ready_client()
--- @diagnostic disable-next-line: invisible
local send_request_stub = spy.stub(client, "_send_request")

client:set_config_option({
sessionId = "s1",
configId = "model",
value = "opus",
}, function() end)

local params = send_request_stub.calls[1][3]
assert.equal(
"session/set_config_option",
send_request_stub.calls[1][2]
)
assert.same({
sessionId = "s1",
configId = "model",
value = "opus",
}, params)
assert.is_nil(params.type)
end)

it("sends boolean true with the boolean type", function()
local client = create_ready_client()
--- @diagnostic disable-next-line: invisible
local send_request_stub = spy.stub(client, "_send_request")

client:set_config_option({
sessionId = "s1",
configId = "fast",
type = "boolean",
value = true,
}, function() end)

assert.equal(
"session/set_config_option",
send_request_stub.calls[1][2]
)
assert.same({
sessionId = "s1",
configId = "fast",
type = "boolean",
value = true,
}, send_request_stub.calls[1][3])
end)

it("sends boolean false with the boolean type", function()
local client = create_ready_client()
--- @diagnostic disable-next-line: invisible
local send_request_stub = spy.stub(client, "_send_request")

client:set_config_option({
sessionId = "s1",
configId = "fast",
type = "boolean",
value = false,
}, function() end)

local params = send_request_stub.calls[1][3]
assert.equal(
"session/set_config_option",
send_request_stub.calls[1][2]
)
assert.is_false(params.value)
assert.equal("boolean", params.type)
end)
end)

describe("_drain_pending_callbacks", function()
local original_schedule = vim.schedule

Expand Down
44 changes: 39 additions & 5 deletions lua/agentic/acp/acp_client_types.lua
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,16 @@
--- @field name string
--- @field version string

--- @class agentic.acp.SessionConfigOptionsCapabilities
--- @field boolean? table

--- @class agentic.acp.ClientSessionCapabilities
--- @field configOptions? agentic.acp.SessionConfigOptionsCapabilities

--- @class agentic.acp.ClientCapabilities
--- @field fs agentic.acp.FileSystemCapability
--- @field terminal boolean
--- @field session? agentic.acp.ClientSessionCapabilities

--- @class agentic.acp.InitializeParams
--- @field protocolVersion number
Expand Down Expand Up @@ -173,28 +180,55 @@
--- @field currentModelId string

--- @class agentic.acp.ConfigOption.Option
--- @field description string
--- @field description? string
--- @field name string
--- @field value string

--- @alias agentic.acp.ConfigOption.Category
--- | "mode"
--- | "model"
--- | "model_config"
--- | "thought_level"
--- | "other"

--- @class agentic.acp.ConfigOption
--- @field id string
--- @field category agentic.acp.ConfigOption.Category
--- @field category? agentic.acp.ConfigOption.Category
--- @field type? "select"
--- @field currentValue string
--- @field description string
--- @field name string
--- @field options agentic.acp.ConfigOption.Option[]
--- @field options? agentic.acp.ConfigOption.Option[]

--- @class agentic.acp.BooleanConfigOption : agentic.acp.ConfigOption
--- @field type "boolean"
--- @field currentValue boolean
--- @field description? string

--- @class agentic.acp.SelectConfigOptionParams
--- @field sessionId string
--- @field configId string
--- @field value string

--- @class agentic.acp.BooleanConfigOptionParams
--- @field sessionId string
--- @field configId string
--- @field type "boolean"
--- @field value boolean

--- @alias agentic.acp.SetConfigOptionParams
--- | agentic.acp.SelectConfigOptionParams
--- | agentic.acp.BooleanConfigOptionParams

--- @alias agentic.acp.AnyConfigOption
--- | agentic.acp.ConfigOption
--- | agentic.acp.BooleanConfigOption

--- @class agentic.acp.SessionCreationResponse
--- @field sessionId string
--- @field modes? agentic.acp.ModesInfo
--- @field models? agentic.acp.ModelsInfo
--- @field configOptions? agentic.acp.ConfigOption[]
--- @field configOptions? agentic.acp.AnyConfigOption[]

--- @alias agentic.acp.ResponseRawParams
--- | { sessionId: string, update: agentic.acp.SessionUpdateMessage }
Expand Down Expand Up @@ -257,7 +291,7 @@

--- @class agentic.acp.ConfigOptionsUpdate
--- @field sessionUpdate "config_option_update"
--- @field configOptions agentic.acp.ConfigOption[]
--- @field configOptions agentic.acp.AnyConfigOption[]

--- @alias agentic.acp.SessionUpdateMessage
--- | agentic.acp.UserMessageChunk
Expand Down
Loading
Loading