fix(status-bar): show Antigravity's model-group pools instead of an empty segment (#24074)

The verbose bucket allowlist was written for Gemini's experimental models, and
its fallback window was session-or-monthly. Antigravity reports one pool per
model group and some tiers meter weekly only, so a signed-in account with an
exhausted pool rendered an icon and no number.

Antigravity bypasses the allowlist by provider, because its group names come
from the account's tier and cannot be enumerated. Cursor stays name-matched so
an unrecognised pool still falls back to the plan total.

Refs #22511
Refs #16704
This commit is contained in:
Neil
2026-09-30 20:03:24 -07:00
committed by GitHub
parent a76a0bcddc
commit 78daf71268
3 changed files with 145 additions and 6 deletions
@@ -165,9 +165,16 @@ function getProviderLetter(provider: ProviderRateLimits['provider']): string {
// Why: Gemini exposes extra experimental buckets that made the pre-existing verbose footer noisy.
const STATUS_BAR_BUCKET_NAMES = new Set(['Flash', 'Pro', '1.5 Pro'])
// Why: the allowlist above is Gemini's. Cursor's pools are its whole meter — filtering
// them out leaves a signed-in account with an icon and no number at all.
function isVisibleStatusBarBucket(name: string): boolean {
/**
* Why Antigravity is matched by provider and not by name: its pools are one per model group, and the
* group names come from the account's own tier ("Gemini Models", "Claude and GPT models" today), so
* there is no list to allow. Cursor stays name-matched on purpose — a pool Orca does not recognise
* is filtered so the segment can fall back to the plan total instead of showing an unlabelled row.
*/
function isVisibleStatusBarBucket(name: string, provider: ProviderRateLimits['provider']): boolean {
if (provider === 'antigravity') {
return true
}
return STATUS_BAR_BUCKET_NAMES.has(name) || isCursorUsageBucket(name)
}
@@ -179,10 +186,15 @@ function VerboseProviderUsage({
display: UsagePercentageDisplay
}): React.JSX.Element {
if (p.buckets && p.buckets.length > 0) {
const visibleBuckets = p.buckets.filter((bucket) => isVisibleStatusBarBucket(bucket.name))
const visibleBuckets = p.buckets.filter((bucket) =>
isVisibleStatusBarBucket(bucket.name, p.provider)
)
// Why: a provider whose buckets are all filtered out still has a headline
// window worth showing rather than rendering an empty segment.
const fallbackWindow = p.session ?? p.monthly ?? null
// Why weekly is in the chain: a tier metered weekly only (Antigravity reports no 5h pool on
// some tiers) has no session window, and omitting weekly rendered an empty segment for an
// account that does have a limit worth showing.
const fallbackWindow = p.session ?? p.monthly ?? p.weekly ?? null
return (
<>
{visibleBuckets.map((bucket, index) => (
@@ -0,0 +1,123 @@
import { renderToStaticMarkup } from 'react-dom/server'
import { describe, expect, it, vi } from 'vitest'
import type { ProviderRateLimits, RateLimitWindow } from '../../../../shared/rate-limit-types'
vi.mock('@/i18n/i18n', () => ({
i18n: { language: 'en' },
translate: (_key: string, fallback: string, values?: Record<string, string>) => {
let result = fallback
for (const [key, value] of Object.entries(values ?? {})) {
result = result.replace(`{{${key}}}`, value)
}
return result
}
}))
vi.mock('@/lib/agent-catalog', () => ({
AgentIcon: () => null
}))
vi.mock('../../store', () => ({
useAppStore: (selector: (state: { usagePercentageDisplay: 'used' | 'remaining' }) => unknown) =>
selector({ usagePercentageDisplay: 'used' })
}))
const WEEKLY_MINUTES = 10_080
function weeklyWindow(usedPercent: number): RateLimitWindow {
return { usedPercent, windowMinutes: WEEKLY_MINUTES, resetsAt: null, resetDescription: null }
}
/**
* The record `agy -p "/usage"` produces for a tier metered weekly only, with the Gemini pool
* genuinely exhausted — captured from agy 1.2.11.
*/
function antigravityLimits(overrides: Partial<ProviderRateLimits> = {}): ProviderRateLimits {
return {
provider: 'antigravity',
session: null,
weekly: weeklyWindow(100),
buckets: [
{ name: 'Gemini Models', ...weeklyWindow(100) },
{ name: 'Claude and GPT models', ...weeklyWindow(0) }
],
updatedAt: Date.now(),
error: null,
status: 'ok',
...overrides
}
}
describe('Antigravity status-bar segment', () => {
it('renders both model-group pools by name', async () => {
// Why: the verbose bucket allowlist was written for Gemini's experimental models, so
// Antigravity's pools — whose names come from the account's tier and cannot be enumerated
// ahead of time — were filtered out and the segment showed no number at all.
const { ProviderSegment } = await import('./StatusBar')
const markup = renderToStaticMarkup(
<ProviderSegment p={antigravityLimits()} compact={false} display="used" mode="verbose" />
)
expect(markup).toContain('Gemini Models')
expect(markup).toContain('Claude and GPT models')
expect(markup).toContain('100%')
})
it('shows the weekly window when a tier reports no session pool', async () => {
// Why: the verbose fallback chain was `session ?? monthly`, so a weekly-only provider with a
// real limit rendered an empty segment.
const { ProviderSegment } = await import('./StatusBar')
const markup = renderToStaticMarkup(
<ProviderSegment
p={antigravityLimits({ buckets: [] })}
compact={false}
display="used"
mode="verbose"
/>
)
expect(markup).toContain('100%')
})
it('still renders a reading when only the weekly window is known', async () => {
const { ProviderSegment } = await import('./StatusBar')
const markup = renderToStaticMarkup(
<ProviderSegment
p={antigravityLimits({ buckets: undefined })}
compact={false}
display="used"
mode="verbose"
/>
)
expect(markup).toContain('100%')
})
it('does not widen the allowlist for providers that rely on it', async () => {
// Why: Gemini's experimental buckets are still filtered; only providers whose buckets are the
// whole meter bypass the allowlist.
const { ProviderSegment } = await import('./StatusBar')
const markup = renderToStaticMarkup(
<ProviderSegment
p={{
provider: 'gemini',
session: weeklyWindow(12),
weekly: null,
buckets: [
{ name: 'Pro', ...weeklyWindow(30) },
{ name: 'Some Experimental Model', ...weeklyWindow(80) }
],
updatedAt: Date.now(),
error: null,
status: 'ok'
}}
compact={false}
display="used"
mode="verbose"
/>
)
expect(markup).toContain('Pro')
expect(markup).not.toContain('Some Experimental Model')
})
})
@@ -137,7 +137,11 @@ function createPendingProviderSnapshot(providerId: UsageProviderId): ProviderRat
session: null,
weekly: null,
...(providerId === 'opencode-go' ? { monthly: null } : {}),
...(providerId === 'gemini' || providerId === 'cursor' ? { buckets: [] } : {}),
// Why antigravity joins these: it reports one pool per model group, so its pending skeleton
// has to be bucket-shaped too or the segment changes shape once the first reading lands.
...(providerId === 'gemini' || providerId === 'cursor' || providerId === 'antigravity'
? { buckets: [] }
: {}),
updatedAt: 0,
error: null,
status: 'fetching'