Auto-acquire the built-in ACP agents, and let users turn them off - #161
Merged
Conversation
Cursor, OpenCode and Kilo were presented as "Built-in" but Atlas bundled
nothing and offered no way to install them: their specs shell out to bare
CLIs (`cursor-agent acp`, `opencode acp`, `kilo acp`) that most machines
don't have. The spawn failed, the error was swallowed, and the only visible
symptom was a chat with no model picker. Claude and Codex were unaffected
because `npx -y` self-installs their adapter.
Atlas now does for these three what npx does for the other two.
Acquisition (cache-first, the model Zed uses)
- `RegistryStore::ensure_builtin` downloads the official per-platform
binary from the ACP registry manifest on first use and caches it,
reusing the existing versioned-cache/sha256/prune machinery. Verified
against sha256 where the manifest publishes one (kilo, opencode);
Cursor ships none upstream, so it installs unverified, as in Zed.
- Hooked into the pre-flight `agents_spawn` already ran for registry
externals. Never fatal: any failure (no manifest, unsupported platform,
offline) falls back to the bare PATH command, i.e. the old behaviour.
- `AUTO_MANAGED_BUILTIN_IDS` marks the three, and `known_specs` lets their
acquired binary replace the bare command IN PLACE — same id, same slot,
same display name, so the plugin catalogue never sees two entries.
- Built-in specs are emitted after installed ones so a manifest-current
binary outranks a frozen install record (a machine that installed
OpenCode from the marketplace before it shipped built-in has both).
First-use progress
- The composer's boot pill reads "Setting up Cursor... 42%" instead of
stalling silently through a 77 MB download.
- Progress is throttled to whole-percent changes: the registry callback
fires once per HTTP chunk (~56k times for Cursor), and one IPC event
each would flood the bridge. 101 events per download, test-pinned.
- Its own `atlas:agent-acquire:*` events rather than the marketplace's
install events, which only its install flow clears.
Sign-in
- These adapters advertise an auth method but no `_meta.terminal-auth`,
and their CLI now lives in Atlas's app-data dir rather than on PATH, so
"run `cursor-agent login`" was advice no one could follow.
`enrich_auth_methods` synthesises the missing spec from the managed
binary; an adapter-supplied spec always wins.
- Cursor rejects `session/new` when signed out, so this surfaces at BIND
time and never reaches the turn-failure route that raises
`atlas:auth-required`. Both paths now offer the same one-click "Sign
in", and the bind path retries itself once credentials land.
- Bind failures used to be a bare console.warn; they are now reported.
Turning them off
- `AppSettings::disabled_builtin_agents` + a switch on the three built-in
marketplace cards. Off means hidden from the picker and never spawned —
including both background pre-warms and a resume of an older session.
- The Rust guard is the authority and hard-filters against
AUTO_MANAGED_BUILTIN_IDS, so a hand-edited state.json cannot disable
Claude, Codex or Cersei. Chat history is never hidden.
Verified against the real binaries, not just mocks: Cursor cold-downloaded
(77 MB, ~19 s), extracted, and answered an ACP initialize with protocol v1
and the `cursor_login` auth method; each agent's login argv was read off
its own `--help`.
Fixes #160
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #160.
Cursor, OpenCode and Kilo were shown as "Built-in" while Atlas bundled nothing and offered no way to install them. Their specs shell out to bare CLIs (
cursor-agent acp,opencode acp,kilo acp); on a machine without those, the spawn failed, the error was swallowed, and the only visible symptom was a chat with no model picker. Claude and Codex were fine becausenpx -yself-installs their adapter.This makes Atlas do for those three what npx already does for the other two — then lets you turn them off if you don't want them.
Acquisition — cache-first, the model Zed uses
RegistryStore::ensure_builtindownloads the official per-platform binary from the ACP registry manifest on first use and caches it, reusing the existing versioned-cache / sha256 / prune machinery rather than adding a parallel one. It hangs off the pre-flightagents_spawnalready ran for registry externals.Never fatal. No manifest yet, unsupported platform, offline, bad checksum — every failure falls back to the bare PATH command, which is exactly today's behaviour. sha256 is verified where the manifest publishes one (kilo, opencode); Cursor ships none upstream, so it installs unverified, as it does in Zed.
AUTO_MANAGED_BUILTIN_IDSmarks the three, andknown_specslets an acquired binary replace the bare command in place — same id, same slot, same display name — so the plugin catalogue never sees two entries for one agent. Built-in specs are emitted after installed ones, so a manifest-current binary outranks a frozen install record; a machine that installed OpenCode from the marketplace before it shipped built-in has both, and that case has a test.First-use progress
The composer's boot pill reads "Setting up Cursor… 42%" instead of stalling silently through a 77 MB download.
Progress is throttled to whole-percent changes. The registry's callback fires once per HTTP chunk — ~56k times for Cursor's archive — and one IPC event each would flood the bridge and jank the UI.
acquire_pct_to_emitcollapses that to 101 events, pinned by a test that replays the real byte/chunk counts.These get their own
atlas:agent-acquire:*events rather than reusing the marketplace's install events: only the install flow clears that progress map, so feeding it from here would leak entries, and the built-in cards short-circuit to the "Built-in" badge before the progress branch anyway.Sign-in
These adapters advertise an auth method but ship no
_meta.terminal-auth, so Atlas's existing terminal-auth runner had nothing to execute — and since the CLI now lives in Atlas's app-data dir rather than on PATH, "runcursor-agent login" was advice a user could not follow.enrich_auth_methodssynthesises the missing spec from the managed binary for both the list and run commands, so what the UI offers is exactly what executes. An adapter-supplied spec always wins.Worth knowing for future work: Cursor rejects
session/new, not the prompt. An unauthenticated agent therefore dies at bind time and never reaches the turn-failure route that raisesatlas:auth-required. Both paths now offer the same one-click "Sign in", and the bind path retries itself once credentials land. Bind failures were previously a bareconsole.warn, so they are now actually reported.Turning them off
AppSettings::disabled_builtin_agents, with a switch on the three built-in marketplace cards. Off means hidden from the picker and never spawned — including both background pre-warms and a resume of an older session recorded against that agent.The Rust guard is the authority and hard-filters against
AUTO_MANAGED_BUILTIN_IDS, so a hand-editedstate.jsoncannot switch off Claude, Codex or Cersei. Chat history is never hidden.Verification
Verified against the real binaries, not just mocks:
initializewith protocol v1 and thecursor_loginauth method — i.e. it now spawns on a machine with no Cursor CLI.--help(cursor login;opencode/kilo auth login), not assumed.-32000 Authentication requiredresponse was reproduced against the CLI and is pinned verbatim in a regression test.Automated: 512 frontend tests, 230 Rust app tests, 14 atlas-acp, 12 atlas-registry; typecheck and lint clean.
Not covered
authenticate()→ rebind needs a real account.auth loginis an interactive TUI (provider picker, then an API-key prompt) and the auth runner spawns with stdin closed. Cursor is unaffected (browser OAuth). Setting a key works today via the CLI in a terminal, or via the provider env vars the binary reads (ANTHROPIC_API_KEY,OPENAI_API_KEY,OPENROUTER_API_KEY,GEMINI_API_KEY,GOOGLE_GENERATIVE_AI_API_KEY). The clean follow-up is to inject Atlas's existing BYOK keys into those agents' env at spawn.adaptiveSuggestionsexists in the frontendAppSettingsbut has no field in the Rust struct, so serde drops it on every save and it does not survive a restart.🤖 Generated with Claude Code