Skip to content

Turn on only the providers a new install finds - #89

Open
poweruserz wants to merge 3 commits into
mainfrom
feat/detect-providers
Open

poweruserz wants to merge 3 commits into
mainfrom
feat/detect-providers

Conversation

@poweruserz

Copy link
Copy Markdown
Owner

What

Until now a new install turned on Claude and Codex for everyone, so someone without them opened the app to two "Not logged in" cards.

A new install now starts with every provider off and probes each one once. A provider is turned on when its probe finds something:

  • usage;
  • or any error other than "nothing set up". An expired login, a bad key or a 429 still means the user has that provider, so the card shows with its fix.

"Nothing set up" means no login, no API key or not installed (isNotSetUpError in src/lib/plugin-errors.ts). The detector is each plugin's own login lookup, the same code that fills its card, so no second list of paths has to be kept in step with the plugins.

Copilot is the exception. It signs in with the GitHub CLI's login, which any GitHub user has, and what copilot_internal/user answers for an account without Copilot isn't documented. So Copilot turns on only when the answer carries a Copilot plan.

Changes:

  • normalizePluginSettings no longer keeps claude and codex on (DEFAULT_ENABLED_PLUGINS is gone).
  • New use-provider-detection.ts probes the given ids and turns on what was found. It decides each provider once, so a card the user turns off later stays off. A provider the user switches during detection is left to them.
  • On a new install the settings are saved once, after every provider is decided. A start cut short (quit, crash, or probes that can't start) saves nothing, so the next start detects again. use-settings-bootstrap.ts therefore skips its usual first save on a new install.
  • handlePluginsChanged reads the settings after its list_plugins await, not before, and detection reads them from the store at the moment it writes. Before, a WSL card that arrived during detection (the detection batch itself starts the WSL rescan) could undo a provider detection had just turned on, or the other way round.
  • The local API (local_http_api/cache.rs) treats nothing as on before the plugin settings are first saved, instead of Claude and Codex.

Unchanged:

  • Existing installs keep their settings.
  • A provider added by an update still starts off.
  • Extra-account and WSL cards still turn on when they are added.

Left as is:

  • While detection runs (usually a few seconds), and when nothing is found, the panel shows the existing "No providers enabled" text.
  • Debug builds load plugins/mock, which always returns data, so a fresh dev profile turns it on. Release builds don't ship it.
  • If an earlier start lost Claude or Codex from the saved order (a failed plugin copy), they now come back off like any other provider, where before they came back on.

Testing

  • plugins/not-set-up.test.js: with nothing set up, all 18 shipped plugins throw an error that isNotSetUpError knows, and none makes a request. Removing one key from the list makes it fail (checked: Amp).
  • use-provider-detection.test.ts:
    • found, not set up, an expired login;
    • Copilot with and without a plan;
    • saved only when everything is decided, including when nothing is found;
    • a card the user turns off later stays off;
    • a provider the user switched while it was probing is left alone;
    • a WSL card added during detection is kept;
    • probes that fail to start save nothing.
  • use-settings-plugin-actions.test.ts: a provider turned on while list_plugins is pending stays on. The test fails when the read is moved back before the await.
  • The Copilot test fails without the Copilot rule.
  • An App test runs a new install end to end: probes for all ids, the found card shows, the other stays off, one save.
  • bunx tsc --noEmit: clean.
  • bun run test: 1582 passed.
  • cargo check: clean.
  • cargo test --lib local_http_api: 27 passed.
  • Not run against a real empty profile: that would mean moving the owner's settings.json aside.

🤖 Generated with Claude Code

https://claude.ai/code/session_01KHc1HaoK4c5kUmgtCmKuwT

A new install had Claude and Codex on for everyone, so users without
them saw two "Not logged in" cards. It now starts with every provider
off, probes each once and turns on those whose own login lookup found
something. Copilot needs a Copilot plan, since its login is the GitHub
CLI's. Settings are saved once everything is decided, so a start cut
short detects again.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KHc1HaoK4c5kUmgtCmKuwT
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Nov 12, 2026 •

Copy link
Copy Markdown
Codex Review Summary: ✅ Completed

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-11-12T16:21:45.714638Z fb8b664 New commits
🔒 Security Review ✅ Completed 2026-11-12T16:21:53.310197Z fb8b664 New commits
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: b831b1d0b2

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/hooks/app/use-settings-bootstrap.ts Resolved
Comment thread src/lib/plugin-errors.ts Resolved
@greptile-apps

greptile-apps Bot commented Nov 12, 2026 •

Copy link
Copy Markdown

RetriggerView in GreptileConfidence Score: 5/5

[Medium impact] The PR appears safe to merge, with a non-blocking Antigravity error-message issue.

Fix All in Claude CodeFindings

  1. P2 Working login reported expired ▶
Fix with agent prompt
### Issue 1
plugins/antigravity/plugin.js:1151
When a saved access token has expired, a refresh can succeed but the following quota requests can return 5xx. The outer `tokens` array stays empty, so this check reports “Session expired” even though Google just renewed the login. Users are told to sign in again when they only need to retry later.

Track whether a login was actually rejected, and show the temporary-unavailable message when quota requests fail after a successful refresh.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Summary

New installs start with providers off, probe them, and enable those found.

  • New installs probe providers and turn on the ones they find.
  • The local usage API waits for saved settings before showing provider snapshots.

Diagram

%%{init: {'theme': 'neutral'}}%%
flowchart TD
  A[Load settings] --> B{Detection needed?}
  B -->|No| C[Probe enabled providers]
  B -->|Yes| D[Save pending flag]
  D --> E[Probe all providers]
  E --> F[Enable providers found]
  F --> G[Save final settings]
  G --> H[Clear pending flag]
Loading

Reviews (3) · Last reviewed commit: "Keep a refused Antigravity login visible..." · Reviewed by Greptile

Comment thread src/hooks/app/use-settings-bootstrap.ts Resolved
@github-actions

github-actions Bot commented Nov 12, 2026 •

Copy link
Copy Markdown

Claude review

Commit fb8b664

VERDICT: FINDINGS
1. [P1] plugins/antigravity/plugin.js:1151 — `tokens.length === 0` only counts SQLite and cached tokens, so a login that could not be checked is reported as expired. For a login made only with `agy` (keychain token, no Antigravity state DB), `tokens` is always empty. For a DB login whose access token expired, `refreshAccessToken` returns null on a network error or 5xx exactly as it does on invalid_grant. This contradicts docs/providers/antigravity.md:42 from this same PR, which says a login that couldn't be checked (5xx, timeouts, no models) gets "quota data unavailable". The old LOGIN_MESSAGE matched `startAppOrRun`, which `needsSignIn` in src/hooks/app/use-login-recovery.ts skips; `sessionExpiredRun` carries a `cmd`, so `useLoginRecovery` now treats the error as a lost sign-in. If the card worked earlier in the run, it sends the "sign in" notification and plays the alert sound (usage alerts are on by default) — Input: no language server, no SQLite tokens, and the agy keychain holds a valid access token; every Cloud Code request returns 503 or times out (for example offline after wake). Outcome: `probeAgyKeychain` returns null, and probe throws "Session expired. Run `agy` to log in again." with a false sign-in notification, instead of "Antigravity quota data unavailable. Try again later." — Fix: throw "Session expired" only when something was actually refused: `sawAuthFailure`, `ccResult._authFailed` (already set from `probeAgyKeychain` when the agy token is refused), or a refresh that Google refused. That last case needs `refreshAccessToken` to report a 4xx such as invalid_grant separately from exceptions and 5xx, which takes a new marker I did not see in the code. Every other failure should throw the "unavailable" message — Test: none today. Add to plugins/antigravity/plugin.test.js a copy of "uses the agy keychain account when no local server or SQLite credentials work" where `http.request` returns `{ status: 503, bodyText: "" }` for every URL, asserting `toThrow("Antigravity quota data unavailable. Try again later.")`; today it throws "Session expired. Run `agy` to log in again." — `if (sawAuthFailure || ccResult || tokens.length === 0) throw "Session expired. Run `agy` to log in again."`
2. [P1] src/hooks/app/use-settings-bootstrap.ts:214 — The `providerDetectionPending` flag survives a settings-backup import. `restoreSettingsBackup` in src/lib/settings-backup.ts writes the backup's `plugins` and then calls `relaunch()`, but the flag is not one of its `BACKUP_KEYS`, so it stays true and the next start runs `detectProviders` over the imported list — Input: on a new install, the user imports a backup (Settings, Backup, Import) before every first probe has finished; detection can take up to the 30 s probe timeout per provider, with 4 probes at a time. Outcome: after the relaunch, every provider the backup had turned off but this PC has (for example a logged-in Claude) is turned on and saved, overriding the imported choice. If the last probe lands between the restore's writes and the relaunch, the detection save also overwrites the restored `plugins` with the pre-import list — Fix: when `restoreSettingsBackup` writes `plugins`, also clear the flag (`saveProviderDetectionPending(false)`, or `backupStore.delete("providerDetectionPending")` before `backupStore.save()`), because an imported list is the user's decision — Test: none today. Add to src/lib/settings-backup.test.ts: `storeState.set("providerDetectionPending", true)`, restore `backup({ plugins: { order: ["claude"], disabled: ["claude"] } })`, and expect `await loadProviderDetectionPending()` to be false; it is true today — `const detect = storedSettings.order.length === 0 || (await loadProviderDetectionPending())`
Not reviewed: none

Other settings saves during detection (a WSL card, a toggle) made the
next start look finished, so a flag now marks detection as pending
until every provider is decided. Devin, Cursor and Kimi said "not
logged in" for a login the server rejected, which detection reads as
nothing set up; they now say the session expired.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KHc1HaoK4c5kUmgtCmKuwT
Comment thread src/hooks/app/use-settings-bootstrap.ts Resolved
Antigravity said "Start Antigravity or run agy" for a saved login that
was refused or couldn't be checked, which a new install reads as
nothing set up. That message now means no login was found; a refused
login says the session expired, and one that couldn't be checked says
the quota data is unavailable.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KHc1HaoK4c5kUmgtCmKuwT
@github-actions github-actions Bot added the docs label Nov 12, 2026

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: fb8b66470d

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

throw LOGIN_MESSAGE
// LOGIN_MESSAGE says no login was found; a saved one that was refused or couldn't be checked is the
// user's, and a new install shows its card (use-provider-detection.ts).
if (tokens.length === 0 && refreshTokens.length === 0 && !agy) throw LOGIN_MESSAGE

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Count expired saved tokens as an existing Antigravity login

On a fresh install with an expired Antigravity SQLite access token and no refresh token, this condition incorrectly reports that no login exists. The loop above excludes expired access tokens from tokens, even though dbTokenCandidates still contains the saved credential. Reproduced by probing with that credential: it throws LOGIN_MESSAGE, so the new detector leaves Antigravity disabled and saves that decision instead of showing the expired-login card. Check whether credentials were found before filtering for usable tokens, and return the session-expired error for this case.

Useful? React with 👍 / 👎.

// LOGIN_MESSAGE says no login was found; a saved one that was refused or couldn't be checked is the
// user's, and a new install shows its card (use-provider-detection.ts).
if (tokens.length === 0 && refreshTokens.length === 0 && !agy) throw LOGIN_MESSAGE
if (sawAuthFailure || ccResult || tokens.length === 0) throw "Session expired. Run `agy` to log in again."

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Working login reported expired

When a saved access token has expired, a refresh can succeed but the following quota requests can return 5xx. The outer tokens array stays empty, so this check reports “Session expired” even though Google just renewed the login. Users are told to sign in again when they only need to retry later.

Track whether a login was actually rejected, and show the temporary-unavailable message when quota requests fail after a successful refresh.

Prompt To Fix With AI
This is a comment left during a code review.
Path: plugins/antigravity/plugin.js
Line: 1151

Comment:
**Working login reported expired**

When a saved access token has expired, a refresh can succeed but the following quota requests can return 5xx. The outer `tokens` array stays empty, so this check reports “Session expired” even though Google just renewed the login. Users are told to sign in again when they only need to retry later.

Track whether a login was actually rejected, and show the temporary-unavailable message when quota requests fail after a successful refresh.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Fix in Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant