Summary
Buzz discovers a harness's models by reading configOptions from the session/new
result. ACP agents that instead report models via the models field
(result.models.availableModels) — and emit no configOptions — are invisible to
that code path. The UI falls back to "Using built-in model options. Could not load
live models for this provider."
Hermes Agent (a bundled preset harness, hermes-acp) is affected: it returns 154
models in result.models.availableModels and no configOptions at all, so
Buzz shows zero live models for it.
Both mechanisms are legitimate ACP. configOptions is the generic
session-configuration mechanism; models / availableModels is the dedicated model
field. Buzz currently supports only the former.
Environment
|
|
| Buzz Desktop |
v0.5.2 (Linux, extracted AppImage) |
| OS |
Fedora Linux 44.1.7 (Sway Atomic), Wayland |
| Hermes Agent |
v0.19.0 (2026.7.20) |
| OpenCode |
1.18.10 |
| Claude Code |
2.1.220 via @agentclientprotocol/claude-agent-acp 0.64.0 |
| ACP protocolVersion |
1 (all three agents) |
Expected vs actual
Expected: With Default harness = Hermes Agent, the Model dropdown lists Hermes's
available models.
Actual: Dropdown is empty except built-in options, with:
Using built-in model options. Could not load live models for this provider.
Reached via Settings → Agents → Agent defaults → Default harness = "Hermes Agent".
Root cause
Diffing the session/new response across the three harnesses installed here:
| Harness |
session/new result keys |
models.availableModels |
configOptions |
Dropdown populates |
OpenCode (opencode acp) |
sessionId, configOptions |
0 |
✅ present (id: "model", category: "model") |
✅ yes |
Claude Code (claude-agent-acp) |
sessionId, modes, configOptions |
0 |
✅ present |
✅ yes |
Hermes (hermes-acp) |
_meta, models, modes, sessionId |
154 |
❌ absent |
❌ no |
The two harnesses that work both expose models through configOptions. The one that
fails exposes them through models.availableModels. This lines up with
resolve_model_switch_method() reading the configId key from each session/new
configOptions entry (see commit 925a9a7, "fix(buzz-acp): accept id-keyed config
options when resolving model switch") — there appears to be no corresponding reader
for the models field.
Raw responses
OpenCode — models under configOptions:
{
"sessionId": "…",
"configOptions": [
{
"id": "model",
"name": "Model",
"category": "model",
"type": "select",
"currentValue": "opencode/big-pickle",
"options": [
{ "value": "opencode-go/deepseek-v4-flash", "name": "OpenCode Go/DeepSeek V4 Flash (New)" },
{ "value": "opencode-go/deepseek-v4-pro", "name": "OpenCode Go/DeepSeek V4 Pro" }
]
}
]
}
Hermes — models under models, no configOptions:
{
"sessionId": "…",
"_meta": { "hermes": { "sessionProvenance": { … } } },
"modes": { … },
"models": {
"currentModelId": "nous:tencent/hy3:free",
"availableModels": [
{ "modelId": "nous:anthropic/claude-opus-5", "name": "Nous Portal · anthropic/claude-opus-5", "description": "Provider: Nous Portal" },
{ "modelId": "nous:anthropic/claude-sonnet-5", "name": "Nous Portal · anthropic/claude-sonnet-5", "description": "Provider: Nous Portal" }
]
}
}
Reproduction
Minimal ACP stdio client — no Buzz required:
import json, subprocess, time
p = subprocess.Popen(["hermes-acp"], stdin=subprocess.PIPE, stdout=subprocess.PIPE,
text=True, bufsize=1)
def send(o): p.stdin.write(json.dumps(o) + "\n"); p.stdin.flush()
def rd(i):
while True:
m = json.loads(p.stdout.readline())
if m.get("id") == i: return m
send({"jsonrpc":"2.0","id":1,"method":"initialize",
"params":{"protocolVersion":1,"clientCapabilities":{}}}); rd(1)
send({"jsonrpc":"2.0","id":2,"method":"session/new",
"params":{"cwd":".","mcpServers":[]}})
r = rd(2)["result"]
print("keys: ", list(r.keys()))
print("availableModels:", len(r.get("models", {}).get("availableModels", [])))
print("configOptions: ", r.get("configOptions", "ABSENT"))
Output here:
keys: ['_meta', 'models', 'modes', 'sessionId']
availableModels: 154
configOptions: ABSENT
Swap hermes-acp for opencode acp to see the inverse.
Ruled out
These were each tested and are not the cause:
- Discovery timeout. Hermes's ACP
initialize was slow (6.1s) because it connects
MCP servers during init. Reducing that to 0.65s init / 2.0s total — faster than
OpenCode's 2.6s, which works — did not fix the dropdown.
- Network/DNS. All model endpoints resolve and respond normally.
authMethods gating. OpenCode also advertises a non-empty authMethods and
populates fine, so a non-empty authMethods is not what blocks Hermes.
- Binary resolution.
hermes-acp resolves on the login-shell PATH and the
handshake succeeds; Buzz spawns it without error.
Suggested fix
In the model-discovery path, fall back to result.models when configOptions has no
category: "model" entry:
- Read
result.models.availableModels[].modelId / .name for the option list.
- Read
result.models.currentModelId for the current selection.
- For switching, ACP defines
session/set_model (Buzz already logs
session/set_model: model overridden model_id=… for its own buzz-agent runtime),
so the switch path may largely exist already.
Supporting both would fix Hermes and any other agent using the dedicated models
field, without affecting the configOptions harnesses.
Workaround
The "Custom model…" option plus a hand-typed model ID (e.g.
nous:anthropic/claude-opus-5) does work — Buzz passes it through even though it
can't enumerate it. So this is a discovery/enumeration gap only, not a plumbing one.
Summary
Buzz discovers a harness's models by reading
configOptionsfrom thesession/newresult. ACP agents that instead report models via the
modelsfield(
result.models.availableModels) — and emit noconfigOptions— are invisible tothat code path. The UI falls back to "Using built-in model options. Could not load
live models for this provider."
Hermes Agent (a bundled preset harness,
hermes-acp) is affected: it returns 154models in
result.models.availableModelsand noconfigOptionsat all, soBuzz shows zero live models for it.
Both mechanisms are legitimate ACP.
configOptionsis the genericsession-configuration mechanism;
models/availableModelsis the dedicated modelfield. Buzz currently supports only the former.
Environment
@agentclientprotocol/claude-agent-acp0.64.0Expected vs actual
Expected: With Default harness = Hermes Agent, the Model dropdown lists Hermes's
available models.
Actual: Dropdown is empty except built-in options, with:
Reached via Settings → Agents → Agent defaults → Default harness = "Hermes Agent".
Root cause
Diffing the
session/newresponse across the three harnesses installed here:session/newresult keysmodels.availableModelsconfigOptionsopencode acp)sessionId,configOptionsid: "model",category: "model")claude-agent-acp)sessionId,modes,configOptionshermes-acp)_meta,models,modes,sessionIdThe two harnesses that work both expose models through
configOptions. The one thatfails exposes them through
models.availableModels. This lines up withresolve_model_switch_method()reading theconfigIdkey from eachsession/newconfigOptionsentry (see commit 925a9a7, "fix(buzz-acp): accept id-keyed configoptions when resolving model switch") — there appears to be no corresponding reader
for the
modelsfield.Raw responses
OpenCode — models under
configOptions:{ "sessionId": "…", "configOptions": [ { "id": "model", "name": "Model", "category": "model", "type": "select", "currentValue": "opencode/big-pickle", "options": [ { "value": "opencode-go/deepseek-v4-flash", "name": "OpenCode Go/DeepSeek V4 Flash (New)" }, { "value": "opencode-go/deepseek-v4-pro", "name": "OpenCode Go/DeepSeek V4 Pro" } ] } ] }Hermes — models under
models, noconfigOptions:{ "sessionId": "…", "_meta": { "hermes": { "sessionProvenance": { … } } }, "modes": { … }, "models": { "currentModelId": "nous:tencent/hy3:free", "availableModels": [ { "modelId": "nous:anthropic/claude-opus-5", "name": "Nous Portal · anthropic/claude-opus-5", "description": "Provider: Nous Portal" }, { "modelId": "nous:anthropic/claude-sonnet-5", "name": "Nous Portal · anthropic/claude-sonnet-5", "description": "Provider: Nous Portal" } ] } }Reproduction
Minimal ACP stdio client — no Buzz required:
Output here:
Swap
hermes-acpforopencode acpto see the inverse.Ruled out
These were each tested and are not the cause:
initializewas slow (6.1s) because it connectsMCP servers during init. Reducing that to 0.65s init / 2.0s total — faster than
OpenCode's 2.6s, which works — did not fix the dropdown.
authMethodsgating. OpenCode also advertises a non-emptyauthMethodsandpopulates fine, so a non-empty
authMethodsis not what blocks Hermes.hermes-acpresolves on the login-shell PATH and thehandshake succeeds; Buzz spawns it without error.
Suggested fix
In the model-discovery path, fall back to
result.modelswhenconfigOptionshas nocategory: "model"entry:result.models.availableModels[].modelId/.namefor the option list.result.models.currentModelIdfor the current selection.session/set_model(Buzz already logssession/set_model: model overridden model_id=…for its ownbuzz-agentruntime),so the switch path may largely exist already.
Supporting both would fix Hermes and any other agent using the dedicated
modelsfield, without affecting the
configOptionsharnesses.Workaround
The "Custom model…" option plus a hand-typed model ID (e.g.
nous:anthropic/claude-opus-5) does work — Buzz passes it through even though itcan't enumerate it. So this is a discovery/enumeration gap only, not a plumbing one.