Skip to content

fix(providers): mint a Zed LLM token for zed-hosted model discovery - #9628

Merged
diegosouzapw merged 3 commits into
cheaper-inference:release/v3.8.50from
r2gi:fix/zed-hosted-model-discovery
Aug 7, 2026
Merged

diegosouzapw merged 3 commits into
cheaper-inference:release/v3.8.50from
r2gi:fix/zed-hosted-model-discovery

Conversation

@ARC345

@ARC345 ARC345 commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Problem

Model discovery for the zed-hosted provider always failed in the dashboard — refreshing the
model list returned 401 Invalid Authorization header and the connection was left with no
catalog.

Root cause

zed-hosted ships models: [] + modelsUrl: "https://cloud.zed.dev/models" in the registry
(open-sse/config/providers/registry/zed-hosted/index.ts), so the route fell through to
deriveConfigFromRegistryModelsUrl(). That generic path hardcodes Authorization: Bearer <accessToken>.

Zed's catalog endpoint does not accept the account access token. It requires an LLM token
minted by POST /client/llm_tokens, which is itself authorized with Zed's own
<userId> <accessToken> scheme — a two-step exchange the generic path cannot express, because
ProviderModelsConfigEntry.buildHeaders is synchronous and the mint is async.

Fix

A dedicated provider === "zed-hosted" branch in src/app/api/providers/[id]/models/route.ts
that reuses the executor's own resolveZedModels() from open-sse/shared/zedAuth.ts — the same
code path the completion executor already uses, so discovery and execution can no longer
disagree about how to authenticate. It handles the providerSpecificData string/object shape,
maps the Zed catalog onto the route's model shape, and on failure degrades to the cached/local
catalog instead of surfacing a raw error.

Validation (Hard Rule #18)

tests/unit/zed-hosted-models-discovery-route.test.ts — 3 cases, verified red before the fix
and green after
(fix reverted in place with git checkout HEAD~1 -- <route>, same worktree):

Case pre-fix post-fix
mints LLM token, lists live catalog ✖ 401 !== 200 ✔
resolves org via /client/users/me when connection has none ✖ ✔
degrades to local catalog when Zed rejects the token ✖ 500 ✔

The first case asserts the catalog request carries the minted Bearer <llmToken> and never
the account access token — that assertion is what reproduces the reported bug.

Regression suites (zed-*, mitm-handler-zed, provider-models-route,
provider-models-route-codex, provider-models-discovery-split, provider-models-config):
161 / 162 pass. The single failure is provider models route retries Antigravity discovery endpoints before returning remote models, which fails identically with this fix reverted — see
inherited base-red below.

typecheck:core: 8 errors, all in untouched open-sse/ files, none in the changed route.
ESLint on the changed route: 1 error, the pre-existing @/lib/localDb barrel import at line 14
(frozen in config/quality/eslint-suppressions.json). Prettier clean.

⚠️ base-red inherited: #9298

release/v3.8.50 is not green. Two inherited failures show up on this branch and are not
introduced by it:

  1. Migration version collision — 134_ccr_blocks.sql vs 134_proxy_logs_egress_ip.sql
    (added by fix(sse): back the CCR block store with a durable tier (#9061) #9198 and fix(db): persist account egress IP into proxy_logs #9291). getMigrationFiles() throws on the collision, so every
    DB-touching unit test aborts before its first assertion. Reproducing the test results above
    required renaming one file locally; that rename is deliberately not part of this PR — per
    the base-green policy a base-red fix belongs in its own freeze-gated
    fix/release-v3.8.50-basereds PR.
  2. Antigravity discovery retry test — pre-existing, fails with this fix reverted.

Notes

  • The branch was cut from an older base that still carried the 135_/136_ collision fixed by
    f2e36ad; release/v3.8.50 has been merged in, so this branch now only carries the 134_
    collision above.

root and others added 3 commits August 6, 2026 15:39
Model discovery for `zed-hosted` always failed with
`Failed to fetch models: 401` / "Invalid Authorization header".

The registry entry (open-sse/config/providers/registry/zed-hosted) sets
`modelsUrl`, so discovery falls through to
deriveConfigFromRegistryModelsUrl(), which hardcodes `Bearer <token>` and
sends the Zed *account* access token at cloud.zed.dev/models.

Zed requires two-step auth there: the account token only authorizes
`/client/*` under Zed's own `<userId> <accessToken>` scheme, and /models
needs an LLM token minted by POST /client/llm_tokens. Verified against a
live account — Bearer with a non-LLM token returns "Invalid Authorization
header", while the two-step flow returns the full 16-model catalog.

ProviderModelsConfigEntry.buildHeaders is synchronous, so the token
exchange cannot be expressed in the generic config. Add a dedicated
branch that reuses the executor's own resolveZedModels(), which already
implements the exchange via zedLlmFetch.

Note: `zed-hosted` was merged in cheaper-inference#6118 titled "NEEDS LIVE OAUTH
VALIDATION"; this is the discovery half of that validation. Inference
still returns 500 and is not addressed here.

Claude-Session: https://claude.ai/code/session_01WDAGfwSnbiWJmjsbz6zUC5
Regression guard for the zed-hosted branch in the provider models route
(Hard Rule #8/#18). Three cases, all red before the fix and green after:

- mints an LLM token via POST /client/llm_tokens (Zed's `<userId>
  <accessToken>` scheme) and asserts GET /models carries the minted
  `Bearer <llmToken>`, never the account access token. Without the fix the
  route falls through to the registry `modelsUrl` path, which sends
  `Bearer <accessToken>` at cloud.zed.dev/models and 401s.
- resolves the organization via /client/users/me when the connection has
  none — the OAuth import stores `organizationId: tokens.organization_id
  || undefined`, so a connection can legitimately land without one.
- degrades to the local catalog (no 500, no stack trace in the body) when
  Zed rejects the token.

Caches in open-sse/shared/zedAuth.ts are module-level and keyed by
`<userId>:<organizationId>:<token suffix>`, so each case uses a distinct
access token and clearZedCaches() runs between tests.
@ARC345
ARC345 requested a review from diegosouzapw as a code owner August 6, 2026 16:00
@diegosouzapw
diegosouzapw merged commit e224165 into cheaper-inference:release/v3.8.50 Aug 7, 2026
3 checks passed
muhamadgalihsaputra pushed a commit to niyatna/NiyatnaRoute that referenced this pull request Sep 27, 2026
…heaper-inference#9628)

Merge-train validated (tip 6ce4effef8). Vitest failures confirmed as base-red (cheaper-inference#9679).
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.

2 participants