90e6651cd7
* Detect Google's live Gemini consumer-tier shutdown Google answers the June 2026 consumer shutdown with an HTTP 200 loadCodeAssist body — no currentTier, the consumer tier listed under ineligibleTiers with UNSUPPORTED_CLIENT — and then fails retrieveUserQuota with a 403 SUBSCRIPTION_REQUIRED that carries no migration wording. The probe only scanned non-200 bodies for the migration signal, so the shutdown surfaced as a bare "HTTP 403", the Antigravity migration guidance never appeared, and the login action still deleted ~/.gemini/oauth_creds.json to launch a Gemini CLI sign-in that Google rejects. Read ineligibleTiers on the 200 body, and map the quota 403 to consumerTierDeprecated only when that flag was set and the account is not on standard-tier, so licensed Standard/Enterprise accounts keep their generic error. Record which sentinel fired in UsageStore.geminiMigrationObservation: either one keeps driving the settings action, while only Google's own response guards the login action — the local Antigravity handoff must still be able to relaunch Gemini CLI, which is the fix for that case. Claude-Session: https://claude.ai/code/session_019jCyMcSJBKdDW9UQ7xxqc6 * Let a named paid tier outrank the unsupported-client listing resolveAccountPlan treats paidTier.name as authoritative even when currentTier is missing, and GeminiStatusProbePlanTests pins that with `paid tier name survives missing current tier`. The new unsupported-client branch ran before that resolver, so a response carrying paidTier.name, no currentTier, and the ineligible consumer tier would have been read as a consumer shutdown — clearing the paid snapshot and arming the login guard. Fold the paid signal into isConsumerClientUnsupported so it gates the deprecation throw and the quota-403 mapping alike. Google's captured shutdown response carries no paidTier, so the consumer path is unchanged. Claude-Session: https://claude.ai/code/session_01SjKShndyQTKankQekQdBcJ * Keep Workspace accounts out of the consumer shutdown path resolveAccountPlan reads free-tier plus an `hd` claim as Workspace, and docs/gemini.md states Workspace and education accounts stay on Gemini. The unsupported-client branch runs before that resolver and never saw the claim, so a hosted-domain account carrying Google's ineligible consumer-tier entry could be classified as shut down — clearing its snapshot and arming the login guard against a sign-in that still works. Fold the hosted domain into isConsumerClientUnsupported next to the paid-tier signal, so both the loadCodeAssist throw and the quota-403 mapping skip accounts the June 2026 shutdown does not cover. The claim is already extracted before loadCodeAssistStatus runs, so it only needed threading through. Move the response parsing into the file's parsing extension to stay under type_body_length. Claude-Session: https://claude.ai/code/session_01SjKShndyQTKankQekQdBcJ * Warn instead of block when Gemini login hits the shutdown The login guard returned before the credential-clearing account switch, and the observation only clears on a successful Gemini refresh or a provider reset — neither reachable from the blocked action. A user moving off an affected consumer account to a Workspace, education, or Code Assist Standard account was stuck with migration guidance for the rest of the session, and the alert never said so. Offer "Switch Account…" beside Cancel, with Cancel as the default since confirming clears the stored credentials. Confirming re-runs the ordinary login without the guard. The observation is left alone, so the settings action stays available and the next attempt warns again. Claude-Session: https://claude.ai/code/session_01SjKShndyQTKankQekQdBcJ * Take main changelog; add #3139 entry --------- Co-authored-by: Peter Steinberger <steipete@gmail.com>
7.3 KiB
7.3 KiB
summary, read_when
| summary | read_when | |||
|---|---|---|---|---|
| Gemini provider data sources: OAuth-backed quota APIs, token refresh, and tier detection. |
|
Gemini provider
Gemini uses the Gemini CLI OAuth credentials and private quota APIs. No browser cookies.
Data sources + fallback order
-
OAuth-backed quota API (only path used in
fetch())- Reads auth type from
~/.gemini/settings.json. - Supported:
oauth-personal(or unknown → try OAuth creds). - Unsupported:
api-key,vertex-ai(hard error).
- Reads auth type from
-
Legacy CLI parsing (parser exists but not used in current fetch path)
GeminiStatusProbe.parse(text:)can parse/statsoutput.
OAuth credentials
- File:
~/.gemini/oauth_creds.json. - Required fields:
access_token,refresh_token(optional),id_token,expiry_date. - If access token is expired, we refresh via Google OAuth using client ID/secret extracted from the Gemini CLI install (see below).
OAuth client ID/secret extraction
- Resolution order:
GEMINI_OAUTH_CLIENT_ID+GEMINI_OAUTH_CLIENT_SECRETenvironment override.GEMINI_OAUTH2_JS_PATHpointing at a readableoauth2.jsfile.- Installed Gemini CLI package (
oauth2.js/ bundle regex extraction). - Known global Gemini CLI install paths (Homebrew npm prefix layouts, then
Homebrew Cellar/
optlibexecpackage roots when the GUI cannot resolve thegeminibinary).
- We locate the installed
geminibinary, then search for:- Homebrew nested path:
.../libexec/lib/node_modules/@google/gemini-cli/node_modules/@google/gemini-cli-core/dist/src/code_assist/oauth2.js
- Homebrew Cellar/opt package root (no-binary fallback):
/opt/homebrew/Cellar/gemini-cli/<version>/libexec/lib/node_modules/@google/gemini-cli/opt/homebrew/opt/gemini-cli/libexec/lib/node_modules/@google/gemini-cli- same under
/usr/local
- Bun/npm sibling path:
.../node_modules/@google/gemini-cli-core/dist/src/code_assist/oauth2.js
- Homebrew nested path:
- Regex extraction:
OAUTH_CLIENT_IDandOAUTH_CLIENT_SECRETfromoauth2.jsor Homebrew bundle chunks.
API endpoints
- Quota:
POST https://cloudcode-pa.googleapis.com/v1internal:retrieveUserQuota- Body:
{ "project": "<projectId>" }(or{}if unknown) - Header:
Authorization: Bearer <access_token>
- Project discovery (quota project ID):
- Primary:
cloudaicompanionProjectfromloadCodeAssist. - Fallback:
GET https://cloudresourcemanager.googleapis.com/v1/projects- Picks
gen-lang-client*or labelgenerative-language.
- Picks
- Primary:
- Tier detection:
POST https://cloudcode-pa.googleapis.com/v1internal:loadCodeAssist- Body:
{ "metadata": { "ideType": "GEMINI_CLI", "pluginType": "GEMINI" } }
- Token refresh:
POST https://oauth2.googleapis.com/token- Form body:
client_id,client_secret,refresh_token,grant_type=refresh_token.
Parsing + mapping
- Quota buckets:
remainingFraction,resetTime,modelId.- For each model, lowest
remainingFractionwins. percentLeft = remainingFraction * 100.
- Reset:
resetTimeparsed as ISO-8601, formatted as "Resets in Xh Ym".
- UI mapping:
- Primary: Pro models (lowest percent left).
- Secondary: Flash models (lowest percent left).
Plan detection
- Tier from
loadCodeAssist:paidTier.name→ paid subscription label from Google, preferred whenever presentstandard-tier→ "Paid" (fallback whenpaidTier.nameis absent)free-tier+hdclaim → "Workspace" (fallback whenpaidTier.nameis absent)free-tier→ "Free"legacy-tier→ "Legacy"
- Email from
id_tokenJWT claims.
Consumer-tier migration (June 2026)
- Google stopped serving Gemini CLI OAuth for individual, AI Pro, and Ultra accounts on 2026-06-18. Standard and Enterprise subscriptions remain supported; paid API-key access is outside CodexBar's OAuth-backed Gemini provider.
- When quota,
loadCodeAssist, or token-refresh responses include Google's unsupported-client migration signal (UNSUPPORTED_CLIENT,IneligibleTierError, or Antigravity migration copy), CodexBar surfacesconsumerTierDeprecatedwith guidance to use the Antigravity provider. - Google's live shape is an HTTP 200
loadCodeAssistbody with nocurrentTierand the consumer tier listed underineligibleTiers[].reasonCode == "UNSUPPORTED_CLIENT"; the follow-upretrieveUserQuotacall then fails with HTTP 403SUBSCRIPTION_REQUIREDand no migration wording. CodexBar reads the 200 body'sineligibleTiersdirectly, and maps that 403 toconsumerTierDeprecatedonly when the same fetch saw the unsupported-client flag and the account is not onstandard-tier— a licensed account's 403 staysHTTP 403. - The unsupported-client flag itself is suppressed for accounts the shutdown does not cover: a named
paidTier.name(authoritative even withoutcurrentTier) and anhdclaim (Workspace/education, whichresolveAccountPlanreads as Workspace when paired withfree-tier). Both would otherwise be pre-empted by the earlierloadCodeAssistbranch, which runs before the plan resolver. UsageStore.geminiMigrationObservationrecords which sentinel the last refresh produced (none/localAntigravityHandoff/googleConsumerTierShutdown); a later local-tooling failure never downgrades a shutdown already seen.geminiObservedConsumerTierDeprecation(either sentinel) drives the settings action; the narrowergeminiObservedGoogleConsumerTierShutdowndrives the login guard. While the narrow one is set for this session, the Gemini login action stops clearing~/.gemini/oauth_creds.jsonand launching Gemini CLI — whose OAuth step fails with the same message — and shows the Antigravity guidance instead. The localoauthCredentialsUnavailableWithAntigravityhandoff deliberately does not guard login: there, reinstalling or relaunching Gemini CLI is the fix, and Workspace accounts must keep that path.- The guard warns rather than blocks: its alert offers Switch Account… next to Cancel (Cancel is the default, since confirming clears credentials). Confirming re-runs the ordinary login without the guard, which is how a user moves from a shut-down consumer account to a Workspace, education, or Code Assist Standard/Enterprise one. The observation is not cleared by confirming, so the settings action stays put and the next attempt warns again.
- Settings shows an Enable Antigravity provider action only after CodexBar observes
consumerTierDeprecatedduring a Gemini refresh (typed sentinel state, not user-facing text matching). - The action is explicit: CodexBar never automatically enables Antigravity or falls back to it.
- Ordinary Gemini login,
notLoggedIn, and Antigravity setup errors remain unchanged. CodexBar does not capture TerminalgeminiOAuth output, so Terminal-only failures cannot activate the migration action. - Workspace and education Google accounts are outside the June 2026 consumer shutdown; keep using the Gemini provider. Antigravity remains the consumer replacement path for individual, AI Pro, and Ultra.
Key files
Sources/CodexBarCore/Providers/Gemini/GeminiStatusProbe.swift