Skip to content

fix(acp): wait for a cold location's plugins before the first catalog read - #53358

Merged
nexxeln merged 1 commit into
v2from
acp-catalog-fresh
Oct 6, 2026
Merged

nexxeln merged 1 commit into
v2from
acp-catalog-fresh

Conversation

@nexxeln

@nexxeln nexxeln commented Oct 5, 2026 •

Copy link
Copy Markdown
Member

#52729 | #52472

Why the change

The first session/new in a fresh opencode acp process returned a model list taken while the location's plugins were still registering providers, so it missed models (#52729) and selecting a model from opencode.json right away failed with -32602 model not found (#52472).

Special things to note

  • This brings back a bounded client-side wait that 8905af5074 removed on purpose ("Activation timing is an internal server concern"). It reads plugin.list, which stays empty until activation finishes (core/src/plugin.ts:42, filled at :172, then plugin.updated at :175). That timing is not documented. If the server changes it, the wait ends on the first poll and ACP behaves as it does today. It cannot hang: it stops after 5 seconds and ignores errors. The server-side alternative is to have model.list wait for activation, which reverses that decision. Worth a quick OK from Dax.
  • Catalog events can't fix ACP: first session/new per process omits the built-in opencode provider from configOptions #52729 on their own. The bug is in the first session/new response itself, and a later config_option_update can't change that response.
  • Cost: about 100 ms more on the first session/new for a cold location, measured against a real server. Warm locations pay one extra local request. Providers added later still arrive through catalog events, as before. That covers plugins installed from npm on first run (a second activation pass) and models found by background discovery.

Change outline

The catalog waits for the location's plugins before its first read, then reads as before. A probe of a cold location against a real server showed why. It returned 0 plugins and 10 models with no config provider, then 87 plugins and 11 models with it. The model list did not change after the plugin list filled.

 ACPCatalog.load(client, cwd)
+  poll plugin.list every 25 ms until it is non-empty, up to 5 s, ignoring errors
   read models, default model, agents, commands
     retry while there is no default model or agent, every 25 ms, up to 5 s

load also runs on reloads. Locations idle for an hour are dropped and recreated cold, so the wait stays in load rather than only on first use.

The new wire test lets the fake server finish activating 200 ms after start. It checks that the first session/new lists the late model and that selecting it right away works. Without the fix it fails with -32602 model not found: test/second-model.

packages/cli/
├── src/acp/catalog.ts          # waits for plugins before the first read
└── test/acp/
    ├── catalog.test.ts         # first session after a cold location activates
    └── wire-fixture.ts         # fake server answers /api/plugin

… read

`opencode acp` runs a private server, so the first catalog read for a cwd boots
its Location. Plugins activate after that boot, and model.list answers with
whatever providers have registered so far. The first session/new then missed
providers from later plugins (opencode/*, config providers), and selecting one
right away failed with -32602.

The plugin inventory stays empty until a Location finishes activating, so the
catalog now polls it (25 ms, up to 5 s) before reading. Warm locations answer
on the first poll; a timeout falls back to the current snapshot.

Fixes #52729
Fixes #52472
@nexxeln
nexxeln merged commit fb72e25 into v2 Oct 6, 2026
13 checks passed
@nexxeln
nexxeln deleted the acp-catalog-fresh branch October 6, 2026 11:40
eshatalov added a commit to eshatalov/subturn that referenced this pull request Oct 10, 2026
OpenCode's ACP catalog used to be read before the location's plugins had registered their providers, so a model from OpenCode Console (`opencode-go/*`, `opencode/*`) or from `opencode.json` selected right after `session/new` was refused with -32602. Subturn set the model again after each `config_option_update` for up to 5 s.

OpenCode 2.0.25 reads the catalog after the plugins have loaded (anomalyco/opencode#53358, closing #52472 and #52729), so the retry has nothing left to do. It is removed from the ACP turn, together with the fake agent's `latemodel` and `nomodel` modes and their two tests; `configure` takes the client and the session id again. README names 2.0.25 as the minimum OpenCode version.

Verified on OpenCode 2.0.26: typecheck and the offline suite (34 pass, 3 live skipped); `subturn status` lists `opencode-go/*` from the first catalog; live smoke + resume on `zai-coding-plan/glm-5.3-flash` and `opencode-go/muse-spark-1.3-contributor`, where the model was acknowledged 19 ms after `session/new`; the user's `opencode.db` gained no session.

Claude Fable 5.1 in Claude Code.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant