Skip to content

fix: Cursor catalog gather isolation and canonical dual-mode OAuth - #23

Merged
cursor[bot] merged 1 commit into
developmentfrom
cursor/cursor-dual-mode-review-fixes-fc9f
Aug 27, 2026
Merged

fix: Cursor catalog gather isolation and canonical dual-mode OAuth#23
cursor[bot] merged 1 commit into
developmentfrom
cursor/cursor-dual-mode-review-fixes-fc9f

Conversation

@pavelhov

@pavelhov pavelhov commented Aug 27, 2026

Copy link
Copy Markdown
Owner

Findings

This PR addresses two Codex review findings from the Cursor dual-mode work (160b574). It does not rework packaging, test runners, or unrelated providers.

P1 — Cursor exchange/discovery throw rejected the whole catalog

src/codex/catalog/provider-fetch.ts materializes a Cursor dashboard key (materializeCursorRunBearer) then calls GetUsableModels. A transient exchange 500 throws before the existing { ok: false } degradation path. Providers are gathered with Promise.all, so one Cursor failure rejected the entire model catalog, including unrelated providers.

Fix: catch exchange/discovery throws (and keep the existing non-ok degradation path). Cursor falls back to stale cache, else the configured/static seed. Other providers continue to appear.

Contract: a Cursor-only exchange or discovery error must not fail catalog gather. Cursor degrades; siblings stay.

P2 — Dual-mode OAuth UI on non-canonical adapter: "cursor" rows

gui/src/provider-workspace/auth.ts treated every adapter: "cursor" row as dual-mode. Backend OAuth endpoints only accept canonical provider id cursor. A provider named custom-cursor therefore showed OAuth controls that always fail.

Fix: isCursorKeyAuthOverride is canonical providers.cursor only (name === "cursor" and adapter === "cursor"). OAuth account-pool fetches use that helper. Key-only custom cursor adapters still get the key surface; they do not get OAuth UI that cannot succeed.

Contract: canonical providers.cursor still shows accounts + API-key pool. A custom id with adapter: "cursor" does not get the dual-mode OAuth surface.

ToS warning copy is unchanged. No new Cursor private RPCs.

Tests

  • tests/cursor-hardening.test.ts: exchange throw and GetUsableModels throw/500 must not reject Promise.all; sibling providers still appear; Cursor degrades to static seed or stale cache.
  • tests/cursor-apikey-dual-mode.test.ts: custom-cursor is not dual-mode; canonical cursor still is.
  • gui/tests/cursor-apikey-dual-mode.test.tsx: custom cursor Settings shows keys only, not OAuth login.

Verified

Local (Bun 1.3.14):

  • bun test tests/cursor-hardening.test.ts tests/cursor-apikey-dual-mode.test.ts
  • cd gui && bun test tests/cursor-apikey-dual-mode.test.tsx
  • bun run typecheck
  • bun run privacy:scan
  • cd gui && bun run lint && bun run lint:i18n && bun run test && bun run build
  • bun run test:parallel (607 files passed)
  • cd docs-site && bun run build

GUI dual-mode restriction verified via ProviderAuthPanel happy-dom tests, not a live dashboard session. Canonical Cursor still renders accounts + API-key pool; custom-cursor renders keys only.

Merge target is development. Do not push to main.

Open in Web Open in Cursor 

A Cursor dashboard-key exchange or GetUsableModels throw no longer rejects
Promise.all catalog gather; Cursor degrades to cached/static models so
unrelated providers still appear.

Restrict the dual-mode OAuth surface to canonical providers.cursor. Custom
adapter:cursor rows can still use keys, but they no longer show OAuth
controls the backend cannot honor.

Co-authored-by: pavelhov <pavelhov@users.noreply.github.com>
@pavelhov
pavelhov marked this pull request as ready for review August 27, 2026 20:06
@cursor
cursor Bot merged commit 0e71b87 into development Aug 27, 2026
1 check passed
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