From 7e5f3357a6e4833f2ec976dc710ff4d8aa9fb351 Mon Sep 17 00:00:00 2001 From: Neil Date: Fri, 11 Sep 2026 12:44:17 -0700 Subject: [PATCH] fix(status-bar): stop gating and routing Antigravity usage through Gemini The Antigravity status-bar slot required `geminiCliOAuthEnabled`, and its sign-in CTA opened Gemini's Accounts section. Both were artifacts of the deleted Gemini mirror. Antigravity now earns its slot from Antigravity signals alone (checked item + CLI on PATH) and renders its own explicit unavailable state; it no longer offers a CTA that would configure a different provider. Refs #14515 Refs #9122 --- .../status-bar-provider-visibility.test.ts | 48 ++++++++----------- .../status-bar-provider-visibility.ts | 18 +++---- .../usage-provider-settings-target.test.ts | 7 ++- .../usage-provider-settings-target.ts | 6 ++- 4 files changed, 40 insertions(+), 39 deletions(-) diff --git a/src/renderer/src/components/status-bar/status-bar-provider-visibility.test.ts b/src/renderer/src/components/status-bar/status-bar-provider-visibility.test.ts index ab2a0eca23f..4f2460a91b8 100644 --- a/src/renderer/src/components/status-bar/status-bar-provider-visibility.test.ts +++ b/src/renderer/src/components/status-bar/status-bar-provider-visibility.test.ts @@ -163,21 +163,20 @@ describe('hasUsageProviderSettingsForProvider', () => { expect(hasUsageProviderSettingsForProvider('grok', usageSettings())).toBe(false) }) - it('requires both a checked Antigravity item and Gemini OAuth as the durable Antigravity signal', () => { + it('treats a checked Antigravity item as durable on its own, with no Gemini opt-in', () => { + expect( + hasUsageProviderSettingsForProvider( + 'antigravity', + usageSettings({ antigravityUsageConfigured: true }) + ) + ).toBe(true) + // Why: an unrelated provider's opt-in must never decide whether Antigravity's slot exists (#14515). expect( hasUsageProviderSettingsForProvider( 'antigravity', usageSettings({ antigravityUsageConfigured: true, geminiCliOAuthEnabled: true }) ) ).toBe(true) - // Why: the snapshot mirrors the Gemini fetch — without the OAuth opt-in it - // is permanently unavailable, so the checked item alone is not durable. - expect( - hasUsageProviderSettingsForProvider( - 'antigravity', - usageSettings({ antigravityUsageConfigured: true }) - ) - ).toBe(false) expect( hasUsageProviderSettingsForProvider( 'antigravity', @@ -345,11 +344,11 @@ describe('getVisibleUsageProvider', () => { ).toBe(null) }) - it('keeps Antigravity visible while the snapshot is pending when checked and Gemini OAuth is on', () => { + it('keeps Antigravity visible while the snapshot is pending when its item is checked', () => { const visible = getVisibleUsageProvider( 'antigravity', null, - usageSettings({ antigravityUsageConfigured: true, geminiCliOAuthEnabled: true }) + usageSettings({ antigravityUsageConfigured: true }) ) expect(visible).toMatchObject({ provider: 'antigravity', @@ -359,26 +358,21 @@ describe('getVisibleUsageProvider', () => { }) }) - it('hides Antigravity while Gemini OAuth is off even when its status item is checked', () => { - // Why: without the OAuth opt-in the mirrored snapshot is permanently - // 'unavailable'; the default-on item must not pin a dead bar. - expect( - getVisibleUsageProvider( - 'antigravity', - null, - usageSettings({ antigravityUsageConfigured: true }) - ) - ).toBe(null) + it('surfaces the unavailable Antigravity slot instead of hiding it or borrowing Gemini', () => { + // Why: the slot is how Orca says "no readable Antigravity quota yet"; hiding it + // leaves #9122's reporter with no explanation at all. expect( getVisibleUsageProvider( 'antigravity', provider('unavailable', { provider: 'antigravity', - error: 'Gemini CLI OAuth is disabled in settings' + error: 'Antigravity usage is not available.' }), usageSettings({ antigravityUsageConfigured: true }) ) - ).toBe(null) + ).toMatchObject({ provider: 'antigravity', status: 'unavailable' }) + // Why: an unchecked or undetected CLI still hides it entirely. + expect(getVisibleUsageProvider('antigravity', null, usageSettings())).toBe(null) }) }) @@ -487,13 +481,13 @@ describe('isUsageEmptyState', () => { grok: provider('unavailable', { provider: 'grok' }), minimax: provider('unavailable', { provider: 'minimax' }) }, - usageSettings({ antigravityUsageConfigured: true, geminiCliOAuthEnabled: true }) + usageSettings({ antigravityUsageConfigured: true }) ) ).toBe(false) }) - it('still shows the setup CTA when Antigravity is checked but Gemini OAuth is off', () => { - // Why: the default-on Antigravity item is not configured usage on its own; + it('still shows the setup CTA once the Antigravity slot settles as unavailable', () => { + // Why: an Antigravity slot with no readable quota is not configured usage; // it must not hide the teaching CTA from users who set nothing up. expect( isUsageEmptyState( @@ -503,7 +497,7 @@ describe('isUsageEmptyState', () => { gemini: provider('unavailable'), opencodeGo: provider('unavailable', { provider: 'opencode-go' }), kimi: provider('unavailable', { provider: 'kimi' }), - antigravity: null, + antigravity: provider('unavailable', { provider: 'antigravity' }), grok: provider('unavailable', { provider: 'grok' }), minimax: provider('unavailable', { provider: 'minimax' }) }, diff --git a/src/renderer/src/components/status-bar/status-bar-provider-visibility.ts b/src/renderer/src/components/status-bar/status-bar-provider-visibility.ts index 19258592f1f..8e07b4ad8b6 100644 --- a/src/renderer/src/components/status-bar/status-bar-provider-visibility.ts +++ b/src/renderer/src/components/status-bar/status-bar-provider-visibility.ts @@ -10,9 +10,7 @@ export type UsageProviderSettings = Pick< > & { // Why: Antigravity has no separate persisted usage credential in Orca. The // checked status-bar item is the durable user signal; StatusBar only sets - // this after PATH detection says the agent is available. Durability further - // requires geminiCliOAuthEnabled — the snapshot mirrors the Gemini fetch, - // which never yields data while that opt-in is off. + // this after PATH detection says the agent is available. antigravityUsageConfigured: boolean // Why: MiniMax/Grok sign-in live on disk, not in settings; main sets these each poll. minimaxCookieConfigured: boolean @@ -75,8 +73,9 @@ export function hasUsageProviderSettings( (settings?.claudeManagedAccounts?.length ?? 0) > 0 || settings?.geminiCliOAuthEnabled === true || Boolean(settings?.opencodeSessionCookie?.trim()) || - // Antigravity's durable signal requires geminiCliOAuthEnabled, so it is - // already covered by the gemini term above. + // Why: Antigravity is deliberately absent — Orca has no readable Antigravity + // quota source yet (#14515), so it can never stand in for a configured provider + // and must not suppress the setup CTA for a user who set nothing up. settings?.minimaxCookieConfigured === true || settings?.minimaxApiKeyConfigured === true || settings?.grokAuthConfigured === true @@ -103,10 +102,11 @@ export function hasUsageProviderSettingsForProvider( return Boolean(settings.opencodeSessionCookie?.trim()) } if (providerId === 'antigravity') { - // Why: the Antigravity snapshot mirrors the Gemini fetch, which stays - // 'unavailable' until the user opts into Gemini CLI OAuth. Without that - // gate the default-on checked item would pin a permanently dead bar. - return settings.antigravityUsageConfigured === true && settings.geminiCliOAuthEnabled === true + // Why: Antigravity's slot is earned by Antigravity signals alone — a checked + // item plus the CLI on PATH. It has no readable quota source yet (#14515), so + // the slot renders an explicit unavailable state rather than another + // provider's numbers. + return settings.antigravityUsageConfigured === true } if (providerId === 'minimax') { return settings.minimaxCookieConfigured === true || settings.minimaxApiKeyConfigured === true diff --git a/src/renderer/src/components/status-bar/usage-provider-settings-target.test.ts b/src/renderer/src/components/status-bar/usage-provider-settings-target.test.ts index cbcfcda0492..d669cf1008e 100644 --- a/src/renderer/src/components/status-bar/usage-provider-settings-target.test.ts +++ b/src/renderer/src/components/status-bar/usage-provider-settings-target.test.ts @@ -6,7 +6,6 @@ describe('getUsageProviderAccountsSectionId', () => { expect(getUsageProviderAccountsSectionId('claude')).toBe('accounts-claude') expect(getUsageProviderAccountsSectionId('codex')).toBe('accounts-codex') expect(getUsageProviderAccountsSectionId('gemini')).toBe('accounts-gemini') - expect(getUsageProviderAccountsSectionId('antigravity')).toBe('accounts-gemini') expect(getUsageProviderAccountsSectionId('opencode-go')).toBe('accounts-opencode-go') expect(getUsageProviderAccountsSectionId('minimax')).toBe('accounts-minimax') expect(getUsageProviderAccountsSectionId('grok')).toBe('accounts-grok') @@ -15,4 +14,10 @@ describe('getUsageProviderAccountsSectionId', () => { it('does not invent an Accounts section for CLI-owned Kimi credentials', () => { expect(getUsageProviderAccountsSectionId('kimi')).toBeNull() }) + + it("does not route Antigravity into another provider's Accounts section", () => { + // Why: `accounts-gemini` configures Gemini, not Antigravity — a sign-in CTA there + // sends the user to the wrong provider (#14515). + expect(getUsageProviderAccountsSectionId('antigravity')).toBeNull() + }) }) diff --git a/src/renderer/src/components/status-bar/usage-provider-settings-target.ts b/src/renderer/src/components/status-bar/usage-provider-settings-target.ts index 59ea7104249..4adc99fd3d5 100644 --- a/src/renderer/src/components/status-bar/usage-provider-settings-target.ts +++ b/src/renderer/src/components/status-bar/usage-provider-settings-target.ts @@ -9,9 +9,11 @@ export function getUsageProviderAccountsSectionId( case 'codex': return 'accounts-codex' case 'gemini': - case 'antigravity': - // Why: Antigravity usage currently shares Gemini's OAuth configuration. return 'accounts-gemini' + case 'antigravity': + // Why: Antigravity sign-in lives in the OS keyring under the `agy` CLI; Orca has no + // Accounts section that configures it, and Gemini's would configure the wrong provider. + return null case 'opencode-go': return 'accounts-opencode-go' case 'minimax':