From 54186c40a7309d30939bfcf7f3e579e158b033e0 Mon Sep 17 00:00:00 2001 From: BingZ Date: Fri, 10 Jul 2026 16:17:21 +0800 Subject: [PATCH] fix(status-bar): show usage after mid-session installs --- .../src/components/status-bar/StatusBar.tsx | 26 +++++++++++-------- .../status-bar-agent-gating.test.ts | 5 ++++ .../status-bar/status-bar-agent-gating.ts | 7 +++-- 3 files changed, 25 insertions(+), 13 deletions(-) diff --git a/src/renderer/src/components/status-bar/StatusBar.tsx b/src/renderer/src/components/status-bar/StatusBar.tsx index 3db1b663f24..d9fe8d98d31 100644 --- a/src/renderer/src/components/status-bar/StatusBar.tsx +++ b/src/renderer/src/components/status-bar/StatusBar.tsx @@ -60,7 +60,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 { shouldOpenStatusBarContextMenu } from './status-bar-context-menu-policy' import { TOGGLE_FLOATING_TERMINAL_EVENT } from '@/lib/floating-terminal' @@ -1933,26 +1937,26 @@ 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)) // Why: MiniMax is a cookie-auth provider, not a CLI on PATH, so detection-gating // doesn't apply (same rationale as OpenCode Go below). const showMiniMax = visibleMiniMax !== null && statusBarItems.includes('minimax') 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) @@ -2174,7 +2178,7 @@ function StatusBarInner({ floatingTerminalOpen }: StatusBarProps): React.JSX.Ele /> - {isStatusBarItemAvailable('claude', detectedAgentIds) && ( + {isStatusBarItemAvailable('claude', detectedAgentIds, isProviderConfigured(claude)) && ( { @@ -2186,7 +2190,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)) && ( { @@ -2198,7 +2202,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)) && ( { @@ -2220,7 +2224,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)) && ( { @@ -2242,7 +2246,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 b888a52fb05..42444c3afb2 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 @@ -34,4 +34,9 @@ describe('isStatusBarItemAvailable', () => { expect(isStatusBarItemAvailable('gemini', ['gemini'])).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 805d7079cee..41d768dfff2 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 @@ -16,7 +16,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 @@ -24,5 +25,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) }