mirror of
https://github.com/block/buzz.git
synced 2026-10-30 05:51:46 +00:00
fix(buzz-acp): accept id-keyed config options when resolving model switch (#2795)
## Summary Fixes #2794. Related: #2692. `resolve_model_switch_method()` reads the `configId` key from each `session/new` `configOptions` entry and skips entries that lack it. `claude-agent-acp` (v0.61.0) keys its entries with `id`, so every model-category entry was skipped, the desired model never matched, and Claude Code sessions fell back to the CLI default from the user's `~/.claude/settings.json`. The only trace was a `pool::model` WARN that never reaches the per-agent log files. This is the ACP-side half of the symptom reported in #2692. The open desktop-side PRs (#2695, #2701, #2696) inject `ANTHROPIC_MODEL` at spawn, which masks the problem for spawn-time selection but leaves the config-option switch path broken. ## Changes - `resolve_model_switch_method()` accepts either `configId` or `id` when extracting the config id. The set request is unchanged: the ACP SDK schema takes `configId` as the request param and the adapter resolves it against its `id`-keyed entries, so only the read side needed fixing. - Regression test with an `id`-keyed `configOptions` payload mirroring the real adapter response (including `models: null`, so the unstable fallback path cannot rescue the match). - Doc comment on `extract_model_config_options()` notes the key drift. ## Testing `cargo test -p buzz-acp --lib`: 599 passed, 0 failed. The new test fails on main and passes with this change. Verified against the real adapter: a stdio JSON-RPC probe of the bundled `claude-agent-acp` 0.61.0 confirms `session/new` returns `id`-keyed config options with `opus[1m]` present as a value, and the SDK's `SetSessionConfigOptionRequest` schema accepts `{sessionId, configId, value}` as sent by `session_set_config_option()`. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Signed-off-by: chillerno1 <gh.chiller@pm.me> Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1854,7 +1854,8 @@ pub enum ModelSwitchMethod {
|
||||
|
||||
/// Extract `configOptions` entries with `category == "model"` from a `session/new` result.
|
||||
///
|
||||
/// Returns the raw JSON array entries. Each entry has `configId`, `displayName`,
|
||||
/// Returns the raw JSON array entries. Each entry has `configId` (spelled `id`
|
||||
/// by some adapters, e.g. claude-agent-acp), `displayName`,
|
||||
/// `options: [{ value, displayName }]`, etc.
|
||||
pub fn extract_model_config_options(result: &serde_json::Value) -> Vec<serde_json::Value> {
|
||||
result["configOptions"]
|
||||
@@ -1888,7 +1889,14 @@ pub fn resolve_model_switch_method(
|
||||
// 1. Search stable configOptions for a "model"-category entry whose
|
||||
// options contain a value matching desired_model.
|
||||
for config_opt in extract_model_config_options(session_new_result) {
|
||||
let config_id = match config_opt.get("configId").and_then(|v| v.as_str()) {
|
||||
// Adapters disagree on the key: the ACP spec says `configId`, but
|
||||
// claude-agent-acp emits `id`. Accept both; the set request always
|
||||
// uses `configId` on the wire.
|
||||
let config_id = match config_opt
|
||||
.get("configId")
|
||||
.or_else(|| config_opt.get("id"))
|
||||
.and_then(|v| v.as_str())
|
||||
{
|
||||
Some(id) => id,
|
||||
None => continue,
|
||||
};
|
||||
@@ -2464,6 +2472,36 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn resolve_accepts_id_keyed_config_options() {
|
||||
// claude-agent-acp (observed on v0.61.0) keys config options with
|
||||
// `id` instead of the spec's `configId`. Payload mirrors its real
|
||||
// `session/new` response.
|
||||
let result = serde_json::json!({
|
||||
"configOptions": [{
|
||||
"id": "model",
|
||||
"name": "Model",
|
||||
"category": "model",
|
||||
"type": "select",
|
||||
"currentValue": "default",
|
||||
"options": [
|
||||
{ "value": "default", "name": "Default" },
|
||||
{ "value": "opus[1m]", "name": "Opus" },
|
||||
{ "value": "sonnet", "name": "Sonnet" }
|
||||
]
|
||||
}],
|
||||
"models": null
|
||||
});
|
||||
let method = super::resolve_model_switch_method(&result, "opus[1m]");
|
||||
assert_eq!(
|
||||
method,
|
||||
Some(super::ModelSwitchMethod::ConfigOption {
|
||||
config_id: "model".to_string(),
|
||||
option_value: "opus[1m]".to_string(),
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn resolve_falls_back_to_unstable() {
|
||||
let result = serde_json::json!({
|
||||
|
||||
Reference in New Issue
Block a user