feat: default to Altimate Base with no consent gate; stop falling back to keyless Zen
Issue for this PR
Closes #1358
Type of change
- Bug fix
- New feature
- Refactor / code improvement
- Documentation
What does this PR do?
Since 2026-09-17, OpenCode Zen rejects keyless requests from Altimate Code ("OpenCode's free tier can only be used from within OpenCode"). A user with no model of their own falls back to a keyless Zen model, so every request fails. Altimate Base was the working free option, but only after a consent dialog. This PR makes Base the default that works out of the box. The commits, grouped:
-
feat: auto-register Altimate Base.FreeTier.autoRegister()runs before provider state is built inserve, the TUI,run(not--attach), ACP andweb. It waits at most 3s and never throws.- It skips when
ALTIMATE_BASE_AUTO_REGISTER=0is set, the user logged out of Base (checked inside the registration lock), no gateway is configured, or valid credentials already exist. - Default model resolution (server, ACP, TUI) never picks keyless Zen when Base is registered. A stale saved Zen/Big Pickle selection is replaced by Base. A model on a real key is never overridden, and
enabled_providers/disabled_providersstill exclude Base.
-
fix: clear Zen error and retry on the token limit.- The Zen rejection now reads "The free Zen models no longer work in Altimate Code. Switch to Altimate Base (free) with /models…".
- A Base per-minute token (TPM) 429 is now retried through the existing retry path, honoring Retry-After and capped at 60s. 413 and the daily budget stay non-retryable.
-
refactor: remove the consent dialog and capability machinery.- Picking Base in any picker registers and selects it directly.
- The disclosure is shown once as a toast that doesn't block anything, with the same wording as before.
POST /altimate/base/registeraccepts but ignores the old disclosure hash, so shipped VS Code extensions keep working. The browser-Origin check stays.
-
test: coverage for the no-dialog picker flow and for showing the notice only once. -
fix: TUI fallback order and picker retry.- The TUI's implicit fallback follows the server's order: the user's own providers, then Base, never keyless Zen.
- A failed "pick Base" can be retried in all three pickers. Before, the row went inert. This mattered most on the first-run welcome screen.
-
Review fixes (41dc4d4, 2ef2c8a, 367201a and two marker-only commits):
run,acp,webandserveprint the disclosure to stderr once per install on the first automatic registration. The security FAQ, providers, quickstart, network docs and README now describe automatic registration and its opt-outs.- A failed automatic registration leaves a backoff file next to the credential store, so later launches don't repeat the 3s wait (1h, or a longer 429 Retry-After).
- Every Retry-After format is capped at 60s.
- The register route disposes instances only when a credential changed.
- An explicit registration no longer joins an automatic attempt.
- The registration check in
lastModel()is best-effort. - An explicit
--modelor configmodelis never replaced. - The attached-TUI register call sends the SDK's auth headers.
- The picker stops if it was dismissed while setup was running.
Known limitation (intentional): if registration takes longer than 3s, it finishes in the background and applies on the next launch. Applying it mid-launch would mean disposing instances, which aborts running prompts. A user who hits it sees the new Zen message and can switch to Base with /models.
How did you verify your code works?
bun run typecheck: all 13 packages clean. Marker guard (analyze.ts --markers --base origin/main --strict): clean.- Affected tests on the final head: 429 pass / 0 fail across the opencode set (including
bridge-mergeandupstream-merge-guard), 49 pass / 0 fail across 7 TUI files. The branding audit finds 0 leaks. The new and changed tests were run 5–10 times in a row to rule out flakiness: fixed sleeps were replaced with condition waits, and each test uses an isolated state dir. - End to end from source with a fresh HOME and no credentials in the environment. Two consecutive headless runs printed the disclosure once, on the first run only.
altimate run "…"registered Base, loggeddefaulting to altimate-free/altimate-base, and answered.- With
ALTIMATE_BASE_AUTO_REGISTER=0, it fell back to Zen and printed the new message:Error: APIError (status 403): The free Zen models no longer work in Altimate Code…. - TUI, recorded with vhs (screenshots below): no picker, Base selected, the notice toast shown, and a real answer in 21.7s.
- Codex review of the branch found three issues, all fixed in commit 5 except the late-registration trade-off above. It found nothing on the paid-user switch, the register-route Origin check, credential leakage or markers.
Not verified: Windows/Linux, and an upgrade over an existing install with a persisted consent decline. The decline is now ignored, which is intended.
Screenshots / recordings
A fresh user (no credentials) opening the TUI and asking "In one sentence, what is a dbt staging model?":
| Before (released 0.12.2) | After (this PR) |
|---|---|
| A provider picker blocks the first prompt. The default underneath it is keyless Zen (Nemotron 3.5 Lightning Free), which now always fails | No picker. Altimate Base is selected, and the disclosure shows once as a toast |
![]() | ![]() |
| Typing goes into the model search. The status bar shows the Zen default | Answered by Altimate Base in 21.7s |
![]() | ![]() |
The screenshots live on the pr-assets/altimate-base-default branch, so they aren't in the diff. Delete that branch after merge.
Takeover (8ed38f8) — claims and residuals
Taken over from @anandgupta42 to land alongside the extension counterpart, AltimateAI/vscode-altimate-mcp-server#474 (merged). 6f92853 through 8ed38f8 fix the review findings on daa773cb0 and later heads; each thread has a reply. The claims below describe the PR as a whole.
Claims
- C1 Every install that is not yet registered registers Altimate Base at startup (
serve, TUI,run, ACP,web), waiting at most 3 s and never throwing. Skipped withALTIMATE_BASE_AUTO_REGISTER=0, after logging out of Base, with no gateway configured, or inside a persisted failure backoff. - C2 When Base is registered, the default model is never keyless Zen; a credentialed or paid model, an explicit
--modeland a configmodelare never overridden, and neither is a deliberate picker, cycle or favorite choice for the rest of that launch, including an explicit keyless-Zen pick, across switching conversations and when switching to an agent without its own model. - C3 The headless disclosure is printed exactly once per install, including when registration finishes in the background: it prints as soon as that registration lands, in the same launch;
serveunderALTIMATE_CLI_CLIENT=datamatesprints nothing (the extension shows its own). - C4
POST /altimate/base/registerreloads both provider registries for every directory unless this process has already reloaded for the credential now on disk; concurrent calls are serialized, so they reload at most once. The identity checked is key, URL, expiry, rejected flag and logout nonce. The first call after a background registration, a renewal that only changes the expiry, and a reissue after a logout elsewhere therefore always reload. - C5 A failed automatic registration records a persisted backoff (1 h, or the 429's Retry-After up to 24 h), updated under its own file lock; explicit registration ignores it.
- C6 Picking Base registers and selects it with no dialog, and first-run picker registrations emit
altimate_base_register_result. - C7 The register route accepts and ignores
acceptedDisclosureSha256, andGET /altimate/base/disclosurestill returnsregisteredand the hash, so shipped extensions keep working. - C8 A retryable Base per-minute 429 is retried with every Retry-After form capped at 60 s.
Residuals (known, not changed here)
- R1 Registration also happens for installs that have a model of their own (documented). A gate would need provider state that doesn't exist yet at startup.
- R2 A registration slower than 3 s applies on the next launch for the TUI and
run;serverecovers mid-session through the register route (C4), which the extension calls. - R3 The TUI's once-per-install toast flag lives in the per-workspace kv and is not claimed atomically across two TUIs started at the same moment.
- R4 The register route is reachable on a server started without a password, like the other routes; C4 limits repeated calls to no-ops after the first reload.
- R5 The TUI toast and the headless stderr notice keep separate once-flags, so one install can see the disclosure once per surface.
- R6
altimate agent createandaltimate reviewdon't auto-register; on a fresh install where one of them runs first, Base arrives with the nexttui,run,serve,acporweblaunch. - R7 A headless notice for a registration that finishes after the startup wait prints as soon as it lands, but a request that builds its provider cache in the same instant could reach Base just before it.
- R8 Across a restart, a keyless-Zen entry in
recentis treated as a stale pick and the launch default goes to registered Base, in the TUI and in the server's headless default resolution alike. Both read onlyrecent, and an earlier round reverted making the TUI alone honorexplicitDefaultbecause the two could then disagree.--modeland a configmodelstay authoritative across launches. - R9 An explicit keyless-Zen pick is remembered per model for the launch, so another conversation recorded on that same model also keeps Zen, and gets Zen's own error, instead of being repaired to Base.
Verified at 8ed38f8: typecheck, marker guard and suites. End-to-end runs at 16873f0 (results), plus a late-registration serve run at 0565811 (in the disposition comment). The runs listed below are the earlier ones at 6f92853.
bun run typecheckclean; marker guard clean. opencode and TUI suites pass apart from three tests (mcp HttpApi > serves status endpoint,Trace.finalizeSync > applies maxFiles pruning synchronously,scan gate: pressing y chooses scan) that fail identically onmain.- E2E with a Linux build of this head and the gateway set explicitly (local builds don't embed it):
- Fresh HOME,
altimate run: registered, disclosure printed once to stderr, answered byaltimate-free/altimate-base; a second run answered with no disclosure. - Gateway traffic delayed 2.5 s/packet under
serve: registration finished in the background, Base absent from/provider; afterPOST /altimate/base/register, present (C4). The next headlessrunprinted the disclosure once and answered (C3). ALTIMATE_BASE_AUTO_REGISTER=0: no registration and the new Zen message.- The merged extension on this CLI, fresh user: the CLI registered Base before the server reported ready, the extension found it loaded (no reload), the chat panel showed the notice once,
serveprinted nothing, and the reply came from Base.
- Fresh HOME,
Checklist
- I have tested my changes locally
- I have not included unrelated changes in this PR
🤖 Generated with Claude Code
Summary by CodeRabbit
- New Features
- Altimate Base registers automatically at startup on unregistered installs. You can disable automatic registration with
ALTIMATE_BASE_AUTO_REGISTER=0; logging out also stops future automatic registration. - Selecting Altimate Base from a model or provider picker registers and selects it directly. A one-time notice appears when it first becomes active, or after headless registration.
- Altimate Base registers automatically at startup on unregistered installs. You can disable automatic registration with
- Improvements
- Registered Altimate Base can serve as a fallback instead of stale keyless OpenCode Zen models, even when other providers are configured. Explicit model choices remain unchanged.
- Rate-limit errors are retryable, with waits capped at 60 seconds. Automatic registration retries use backoff of up to 24 hours.
[!NOTE]
High Risk
Changes default LLM registration and routing for all entrypoints, sends install metadata to the Base gateway unless opted out, and alters security/privacy expectations documented for automatic registration.
Overview
Altimate Base becomes the working free default now that keyless OpenCode Zen rejects Altimate Code traffic. Fresh installs auto-register Base at startup (serve, TUI, run, acp, web) with a short wait, persisted failure backoff, and opt-out via ALTIMATE_BASE_AUTO_REGISTER=0 or logout — no consent dialog or capability tokens.
Registration moves from registerAfterConsent to unconditional register() / autoRegister(), with separate in-flight dedupe for explicit vs automatic paths. Disclosure text is shown once (TUI toast or headless stderr; serve skips duplicate notice for the VS Code extension).
Default-model and session logic prefer registered Base over stale keyless Zen recents; declinedManagedBaseDefault is ignored. ACP no longer strips Base when unrelated config.provider entries exist. POST /altimate/base/register ignores the old disclosure hash but reloads provider caches only when credentials actually change.
User-facing errors improve for broken Zen; Base rate limits are retryable with Retry-After capped at 60s. Docs, telemetry, and tests reflect the new flow.
Reviewed by Cursor Bugbot for commit e31844dc76321ffa1d890fa003511fb1b40a6955. Bugbot is set up for automated code reviews on this repo. Configure here.



