Skip to content

fix(acp): follow server defaults and refresh the session catalog - #52286

Merged
nexxeln merged 2 commits into
v2from
acp-live-catalog
Sep 30, 2026
Merged

nexxeln merged 2 commits into
v2from
acp-live-catalog

Conversation

@nexxeln

@nexxeln nexxeln commented Sep 30, 2026 •

Copy link
Copy Markdown
Member

Closes #50236 | Closes #51819 | Closes #50378 | Closes #49630 | Refs #52284, #52259, #51478 | Supersedes #50619

Why the change

Since 8905af5074 removed the plugin activation wait (first shipped in v2.0.4), opencode acp reads its model/agent/command catalog before config and plugins have loaded, caches that snapshot for the life of the process, and locks new sessions to its stale defaults, so config-defined agents, default_agent, providers, the configured model, and agent permission rules silently don't apply; after this change ACP sessions use the server's real defaults and the client catalog catches up as soon as those plugins finish loading.

Special things to note

Change outline

Catalog loading moves out of the service into its own module. Each cwd gets one live catalog object that sessions point at, instead of each session holding its own copy.

 packages/cli/src/acp/
+├── catalog.ts    # Catalog type, load() (moved), one event subscription, per-cwd reload
 └── service.ts    # sessions point at a live catalog, follow server defaults, retry on unknown ids
 type Attached = {
-  catalog: Catalog
-  model: ModelRef
-  modeID: string
+  readonly catalog: ACPCatalog.Live   // { cwd, current: Catalog }, shared per cwd
+  model?: ModelRef                    // unset = follow server default
+  modeID?: string                     // unset = follow server default
 }

Session creation stops locking in the snapshot defaults.

 newSession(cwd)
   await catalogs.get(cwd)
-  session.create({ location, agent: catalog.defaultModeID, model: catalog.defaultModel })
+  session.create({ location })
   attach(...)

The catalog stays live instead of being a one-time snapshot.

catalogs.get(cwd)                          first use of a cwd
  subscribe()                              one /api/event subscription per ACP connection, before the first read
  load(cwd)                                unchanged polling read

on model.updated | agent.updated | command.updated  (for that location, or all when unscoped)
  reload(live)                             one reload in flight + one queued per cwd
    live.current = load(cwd)
    changed(live, previous)                per attached session:
      config_option_update                   if its options changed
      available_commands_update              if commands changed

set_config_option / set_mode fails with InvalidModel | InvalidMode | InvalidEffort
  reload(live) once, then try again         fixes "model not found" for config models

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