acp: Support specifying settings for extensions (#43177)

This allows you to specify default_model and default_mode for ACP
extensions, e.g.
```
"auggie": {
  "default_model": "gpt-5",
  "default_mode": "default",
  "type": "extension"
},
```

Release Notes:

- Added support for specifying settings for ACP extensions
(`default_mode`, `default_model`)
This commit is contained in:
Bennet Bo Fenner
2025-11-20 17:12:00 +00:00
committed by GitHub
parent a5c3267b3e
commit 2a40dcfd77
10 changed files with 317 additions and 113 deletions
+6
View File
@@ -141,3 +141,9 @@ pub(crate) mod m_2025_11_12 {
pub(crate) use settings::SETTINGS_PATTERNS;
}
pub(crate) mod m_2025_11_20 {
mod settings;
pub(crate) use settings::SETTINGS_PATTERNS;
}
@@ -0,0 +1,76 @@
use std::ops::Range;
use tree_sitter::{Query, QueryMatch};
use crate::MigrationPatterns;
pub const SETTINGS_PATTERNS: MigrationPatterns = &[(
SETTINGS_AGENT_SERVERS_CUSTOM_PATTERN,
migrate_custom_agent_settings,
)];
const SETTINGS_AGENT_SERVERS_CUSTOM_PATTERN: &str = r#"(document
(object
(pair
key: (string (string_content) @agent-servers)
value: (object
(pair
key: (string (string_content) @server-name)
value: (object) @server-settings
)
)
)
)
(#eq? @agent-servers "agent_servers")
)"#;
fn migrate_custom_agent_settings(
contents: &str,
mat: &QueryMatch,
query: &Query,
) -> Option<(Range<usize>, String)> {
let server_name_index = query.capture_index_for_name("server-name")?;
let server_name = mat.nodes_for_capture_index(server_name_index).next()?;
let server_name_text = &contents[server_name.byte_range()];
if matches!(server_name_text, "gemini" | "claude" | "codex") {
return None;
}
let server_settings_index = query.capture_index_for_name("server-settings")?;
let server_settings = mat.nodes_for_capture_index(server_settings_index).next()?;
let mut column = None;
// Parse the server settings to check what keys it contains
let mut cursor = server_settings.walk();
for child in server_settings.children(&mut cursor) {
if child.kind() == "pair" {
if let Some(key_node) = child.child_by_field_name("key") {
if let (None, Some(quote_content)) = (column, key_node.child(0)) {
column = Some(quote_content.start_position().column);
}
if let Some(string_content) = key_node.child(1) {
let key = &contents[string_content.byte_range()];
match key {
// If it already has a type key, don't modify it
"type" => return None,
_ => {}
}
}
}
}
}
// Insert the type key at the beginning of the object
let start = server_settings.start_byte() + 1;
let indent = " ".repeat(column.unwrap_or(12));
Some((
start..start,
format!(
r#"
{indent}"type": "custom","#
),
))
}
+65
View File
@@ -219,6 +219,10 @@ pub fn migrate_settings(text: &str) -> Result<Option<String>> {
migrations::m_2025_11_12::SETTINGS_PATTERNS,
&SETTINGS_QUERY_2025_11_12,
),
MigrationType::TreeSitter(
migrations::m_2025_11_20::SETTINGS_PATTERNS,
&SETTINGS_QUERY_2025_11_20,
),
];
run_migrations(text, migrations)
}
@@ -341,6 +345,10 @@ define_query!(
SETTINGS_QUERY_2025_11_12,
migrations::m_2025_11_12::SETTINGS_PATTERNS
);
define_query!(
SETTINGS_QUERY_2025_11_20,
migrations::m_2025_11_20::SETTINGS_PATTERNS
);
// custom query
static EDIT_PREDICTION_SETTINGS_MIGRATION_QUERY: LazyLock<Query> = LazyLock::new(|| {
@@ -1192,6 +1200,63 @@ mod tests {
);
}
#[test]
fn test_custom_agent_server_settings_migration() {
assert_migrate_settings_with_migrations(
&[MigrationType::TreeSitter(
migrations::m_2025_11_20::SETTINGS_PATTERNS,
&SETTINGS_QUERY_2025_11_20,
)],
r#"{
"agent_servers": {
"gemini": {
"default_model": "gemini-1.5-pro"
},
"claude": {},
"codex": {},
"my-custom-agent": {
"command": "/path/to/agent",
"args": ["--foo"],
"default_model": "my-model"
},
"already-migrated-agent": {
"type": "custom",
"command": "/path/to/agent"
},
"future-extension-agent": {
"type": "extension",
"default_model": "ext-model"
}
}
}"#,
Some(
r#"{
"agent_servers": {
"gemini": {
"default_model": "gemini-1.5-pro"
},
"claude": {},
"codex": {},
"my-custom-agent": {
"type": "custom",
"command": "/path/to/agent",
"args": ["--foo"],
"default_model": "my-model"
},
"already-migrated-agent": {
"type": "custom",
"command": "/path/to/agent"
},
"future-extension-agent": {
"type": "extension",
"default_model": "ext-model"
}
}
}"#,
),
);
}
#[test]
fn test_remove_version_fields() {
assert_migrate_settings(