diff --git a/src/infra/provider-usage.fetch.codex.test.ts b/src/infra/provider-usage.fetch.codex.test.ts index 6078e2a9bd4..e74d0f25f65 100644 --- a/src/infra/provider-usage.fetch.codex.test.ts +++ b/src/infra/provider-usage.fetch.codex.test.ts @@ -79,4 +79,32 @@ describe("fetchCodexUsage", () => { { label: "Week", usedPercent: 10, resetAt: 1_700_500_000_000 }, ]); }); + + it("labels secondary window as Week when reset cadence clearly exceeds one day", async () => { + const primaryReset = 1_700_000_000; + const weeklyLikeSecondaryReset = primaryReset + 5 * 24 * 60 * 60; + const mockFetch = createProviderUsageFetch(async () => + makeResponse(200, { + rate_limit: { + primary_window: { + limit_window_seconds: 10_800, + used_percent: 14, + reset_at: primaryReset, + }, + secondary_window: { + // Observed in production: API reports 24h, but dashboard shows a weekly window. + limit_window_seconds: 86_400, + used_percent: 20, + reset_at: weeklyLikeSecondaryReset, + }, + }, + }), + ); + + const result = await fetchCodexUsage("token", undefined, 5000, mockFetch); + expect(result.windows).toEqual([ + { label: "3h", usedPercent: 14, resetAt: 1_700_000_000_000 }, + { label: "Week", usedPercent: 20, resetAt: weeklyLikeSecondaryReset * 1000 }, + ]); + }); }); diff --git a/src/infra/provider-usage.fetch.codex.ts b/src/infra/provider-usage.fetch.codex.ts index 28d155a6b57..0f37417dd18 100644 --- a/src/infra/provider-usage.fetch.codex.ts +++ b/src/infra/provider-usage.fetch.codex.ts @@ -19,6 +19,31 @@ type CodexUsageResponse = { credits?: { balance?: number | string | null }; }; +const WEEKLY_RESET_GAP_SECONDS = 3 * 24 * 60 * 60; + +function resolveSecondaryWindowLabel(params: { + windowHours: number; + secondaryResetAt?: number; + primaryResetAt?: number; +}): string { + if (params.windowHours >= 168) { + return "Week"; + } + if (params.windowHours < 24) { + return `${params.windowHours}h`; + } + // Codex occasionally reports a 24h secondary window while exposing a + // weekly reset cadence in reset timestamps. Prefer cadence in that case. + if ( + typeof params.secondaryResetAt === "number" && + typeof params.primaryResetAt === "number" && + params.secondaryResetAt - params.primaryResetAt >= WEEKLY_RESET_GAP_SECONDS + ) { + return "Week"; + } + return "Day"; +} + export async function fetchCodexUsage( token: string, accountId: string | undefined, @@ -65,7 +90,11 @@ export async function fetchCodexUsage( if (data.rate_limit?.secondary_window) { const sw = data.rate_limit.secondary_window; const windowHours = Math.round((sw.limit_window_seconds || 86400) / 3600); - const label = windowHours >= 168 ? "Week" : windowHours >= 24 ? "Day" : `${windowHours}h`; + const label = resolveSecondaryWindowLabel({ + windowHours, + primaryResetAt: data.rate_limit?.primary_window?.reset_at, + secondaryResetAt: sw.reset_at, + }); windows.push({ label, usedPercent: clampPercent(sw.used_percent || 0),