block / block/buzz

Model dropdown stays empty for ACP harnesses that report models via `session/new` → `models.availableModels` (e.g. Hermes Agent)

Open
#3,934 1 comment 0 reactions 0 assignees View on GitHub
Dominant language
Rust
Stars
32.7k
Forks
4.3k
Avg merge
1d 13h
Merged PRs (30d)
253

Description

## 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`:

```json
{
"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`:

```json
{
"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:

```python
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.

Contributor guide

Open the contributing guide

Research direction

Start in Buzz’s model-discovery path, especially the code that reads session/new configOptions and resolve_model_switch_method(); compare that flow with the reproduced Hermes response.models.availableModels shape. Done means the model dropdown lists modelId/name values and uses currentModelId while preserving the existing configOptions behavior; verify with Hermes and a configOptions-based harness.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
desktop
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
55/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.