diff --git a/src/renderer/src/components/status-bar/StatusBar.tsx b/src/renderer/src/components/status-bar/StatusBar.tsx index b3cc8a922c1..d9bcff0957d 100644 --- a/src/renderer/src/components/status-bar/StatusBar.tsx +++ b/src/renderer/src/components/status-bar/StatusBar.tsx @@ -61,7 +61,11 @@ import { formatWindowLabel } from '@/lib/window-label-formatter' import { markLiveCodexSessionsForRestart } from '@/lib/codex-session-restart' import { UpdateStatusSegment } from './UpdateStatusSegment' import { isStatusBarItemAvailable } from './status-bar-agent-gating' -import { getVisibleUsageProvider, isUsageEmptyState } from './status-bar-provider-visibility' +import { + getVisibleUsageProvider, + isProviderConfigured, + isUsageEmptyState +} from './status-bar-provider-visibility' import { StatusBarUsageEmptyCta } from './StatusBarUsageEmptyCta' import { UsagePercentageDisplayChangeNotice } from './UsagePercentageDisplayChangeNotice' import { shouldOpenStatusBarContextMenu } from './status-bar-context-menu-policy' @@ -1983,19 +1987,19 @@ function StatusBarInner({ floatingTerminalOpen }: StatusBarProps): React.JSX.Ele const showClaude = visibleClaude !== null && statusBarItems.includes('claude') && - isStatusBarItemAvailable('claude', detectedAgentIds) + isStatusBarItemAvailable('claude', detectedAgentIds, isProviderConfigured(claude)) const showCodex = visibleCodex !== null && statusBarItems.includes('codex') && - isStatusBarItemAvailable('codex', detectedAgentIds) + isStatusBarItemAvailable('codex', detectedAgentIds, isProviderConfigured(codex)) const showGemini = visibleGemini !== null && statusBarItems.includes('gemini') && - isStatusBarItemAvailable('gemini', detectedAgentIds) + isStatusBarItemAvailable('gemini', detectedAgentIds, isProviderConfigured(gemini)) const showKimi = visibleKimi !== null && statusBarItems.includes('kimi') && - isStatusBarItemAvailable('kimi', detectedAgentIds) + isStatusBarItemAvailable('kimi', detectedAgentIds, isProviderConfigured(kimi)) const showAntigravity = visibleAntigravity !== null && statusBarItems.includes('antigravity') && @@ -2006,7 +2010,7 @@ function StatusBarInner({ floatingTerminalOpen }: StatusBarProps): React.JSX.Ele const showGrok = visibleGrok !== null && statusBarItems.includes('grok') && - isStatusBarItemAvailable('grok', detectedAgentIds) + isStatusBarItemAvailable('grok', detectedAgentIds, isProviderConfigured(grok)) // Why: OpenCode Go is a web/cookie-auth provider, not a CLI on PATH, so // detection-gating doesn't apply. const visibleOpencodeGo = getVisibleUsageProvider('opencode-go', opencodeGo, usageSettings) @@ -2246,7 +2250,7 @@ function StatusBarInner({ floatingTerminalOpen }: StatusBarProps): React.JSX.Ele /> - {isStatusBarItemAvailable('claude', detectedAgentIds) && ( + {isStatusBarItemAvailable('claude', detectedAgentIds, isProviderConfigured(claude)) && ( { @@ -2258,7 +2262,7 @@ function StatusBarInner({ floatingTerminalOpen }: StatusBarProps): React.JSX.Ele {translate('auto.components.status.bar.StatusBar.3885eb74d8', 'Claude Usage')} )} - {isStatusBarItemAvailable('codex', detectedAgentIds) && ( + {isStatusBarItemAvailable('codex', detectedAgentIds, isProviderConfigured(codex)) && ( { @@ -2270,7 +2274,7 @@ function StatusBarInner({ floatingTerminalOpen }: StatusBarProps): React.JSX.Ele {translate('auto.components.status.bar.StatusBar.c0909c686e', 'Codex Usage')} )} - {isStatusBarItemAvailable('gemini', detectedAgentIds) && ( + {isStatusBarItemAvailable('gemini', detectedAgentIds, isProviderConfigured(gemini)) && ( { @@ -2307,7 +2311,7 @@ function StatusBarInner({ floatingTerminalOpen }: StatusBarProps): React.JSX.Ele {translate('auto.components.status.bar.StatusBar.8c86cd77b0', 'OpenCode Go Usage')} - {isStatusBarItemAvailable('kimi', detectedAgentIds) && ( + {isStatusBarItemAvailable('kimi', detectedAgentIds, isProviderConfigured(kimi)) && ( { @@ -2329,7 +2333,7 @@ function StatusBarInner({ floatingTerminalOpen }: StatusBarProps): React.JSX.Ele {translate('auto.components.status.bar.StatusBar.3bbf140864', 'MiniMax Usage')} - {isStatusBarItemAvailable('grok', detectedAgentIds) && ( + {isStatusBarItemAvailable('grok', detectedAgentIds, isProviderConfigured(grok)) && ( { diff --git a/src/renderer/src/components/status-bar/status-bar-agent-gating.test.ts b/src/renderer/src/components/status-bar/status-bar-agent-gating.test.ts index cd98315df50..bd67981b190 100644 --- a/src/renderer/src/components/status-bar/status-bar-agent-gating.test.ts +++ b/src/renderer/src/components/status-bar/status-bar-agent-gating.test.ts @@ -37,4 +37,9 @@ describe('isStatusBarItemAvailable', () => { expect(isStatusBarItemAvailable('antigravity', ['antigravity'])).toBe(true) expect(isStatusBarItemAvailable('grok', ['grok'])).toBe(true) }) + + it('lets live provider evidence override a cached negative PATH result', () => { + expect(isStatusBarItemAvailable('codex', [], true)).toBe(true) + expect(isStatusBarItemAvailable('codex', [], false)).toBe(false) + }) }) diff --git a/src/renderer/src/components/status-bar/status-bar-agent-gating.ts b/src/renderer/src/components/status-bar/status-bar-agent-gating.ts index 4ded63147db..c4fc4f64871 100644 --- a/src/renderer/src/components/status-bar/status-bar-agent-gating.ts +++ b/src/renderer/src/components/status-bar/status-bar-agent-gating.ts @@ -17,7 +17,8 @@ const CLI_GATED_ITEMS: ReadonlySet = new Set([ export function isStatusBarItemAvailable( id: StatusBarItem, - detectedAgentIds: TuiAgent[] | null + detectedAgentIds: TuiAgent[] | null, + hasProviderEvidence = false ): boolean { if (!CLI_GATED_ITEMS.has(id)) { return true @@ -25,5 +26,7 @@ export function isStatusBarItemAvailable( if (detectedAgentIds === null) { return true } - return detectedAgentIds.includes(id as TuiAgent) + // Why: PATH detection is a cached negative snapshot. A later successful + // usage fetch proves a CLI was installed mid-session and must win over it. + return hasProviderEvidence || detectedAgentIds.includes(id as TuiAgent) }