diff --git a/lib/codex-manager/formatters/quota-formatters.ts b/lib/codex-manager/formatters/quota-formatters.ts index 1700815a..210e15fc 100644 --- a/lib/codex-manager/formatters/quota-formatters.ts +++ b/lib/codex-manager/formatters/quota-formatters.ts @@ -67,7 +67,8 @@ export function formatQuotaSnapshotForDashboard( now = Date.now(), ): string { if (!settings.showQuotaDetails) return "live session OK"; - return `live session OK (${formatCompactQuotaSnapshot(snapshot, now, { showReset: true })})`; + const summary = formatCompactQuotaSnapshot(snapshot, now, { showReset: true }); + return summary ? `live session OK (${summary})` : "live session OK"; } export function quotaCacheEntryToSnapshot( @@ -101,6 +102,23 @@ function formatCompactQuotaWindowLabel( return `${windowMinutes}m`; } +function isUninformativeFullQuotaWindow( + windowMinutes: number | undefined, + usedPercent: number | undefined, + resetAtMs: number | undefined, + now: number, +): boolean { + if (formatCompactQuotaWindowLabel(windowMinutes) !== "quota") return false; + if (typeof usedPercent !== "number" || !Number.isFinite(usedPercent)) { + return false; + } + const left = quotaLeftPercentFromUsed(usedPercent); + return ( + !formatQuotaResetAt(resetAtMs, now) && + (left === undefined || left >= 100) + ); +} + /** * Options shared by the compact quota formatters. * @@ -124,10 +142,21 @@ function formatCompactQuotaPart( return null; } const left = quotaLeftPercentFromUsed(usedPercent); + // The reset validity is computed regardless of showReset: it also feeds the + // uninformative-window check below, so hiding stays consistent across the + // compact (no-reset) and check (with-reset) surfaces. + const reset = formatQuotaResetAt(resetAtMs, now); + // An unlabeled window ("quota" — upstream reported no duration) that sits at + // 100% left with no reset timestamp carries no actionable signal; since the + // 2026-07/08 upstream limit changes it renders as a permanent "quota 100%" + // on every account. Hide exactly that shape: the segment reappears as soon + // as the window reports a duration, any depletion, or a reset time. + if (!reset && label === "quota" && (left === undefined || left >= 100)) { + return null; + } const part = `${label} ${left}%`; if (!options.showReset) return part; // A missing or malformed reset timestamp must never drop the percentage. - const reset = formatQuotaResetAt(resetAtMs, now); return reset ? `${part}, resets ${reset}` : part; } @@ -163,6 +192,22 @@ export function formatCompactQuotaSnapshot( if (parts.length > 0) { return parts.join(" | "); } + if ( + isUninformativeFullQuotaWindow( + snapshot.primary.windowMinutes, + snapshot.primary.usedPercent, + snapshot.primary.resetAtMs, + now, + ) || + isUninformativeFullQuotaWindow( + snapshot.secondary.windowMinutes, + snapshot.secondary.usedPercent, + snapshot.secondary.resetAtMs, + now, + ) + ) { + return ""; + } return formatQuotaSnapshotLine(snapshot); } @@ -198,5 +243,21 @@ export function formatAccountQuotaSummary( if (parts.length > 0) { return parts.join(" | "); } + if ( + isUninformativeFullQuotaWindow( + entry.primary.windowMinutes, + entry.primary.usedPercent, + entry.primary.resetAtMs, + now, + ) || + isUninformativeFullQuotaWindow( + entry.secondary.windowMinutes, + entry.secondary.usedPercent, + entry.secondary.resetAtMs, + now, + ) + ) { + return ""; + } return formatQuotaSnapshotLine(quotaCacheEntryToSnapshot(entry)); } diff --git a/test/codex-manager-formatters.test.ts b/test/codex-manager-formatters.test.ts index 55fd2eb2..16203ff3 100644 --- a/test/codex-manager-formatters.test.ts +++ b/test/codex-manager-formatters.test.ts @@ -250,6 +250,69 @@ describe("compact quota reset timestamps", () => { ); }); + it("hides an unlabeled full quota window with no reset data", () => { + const unlabeledFull = { + status: 200, + planType: "plus", + model: "gpt-5.3-codex", + primary: { usedPercent: 65, windowMinutes: 10080, resetAtMs: SAME_DAY }, + secondary: { usedPercent: 0 }, + } satisfies CodexQuotaSnapshot; + expect( + formatCompactQuotaSnapshot(unlabeledFull, NOW, { showReset: true }), + ).toBe(`7d 35%, resets ${formatQuotaResetAt(SAME_DAY, NOW)}`); + expect(formatCompactQuotaSnapshot(unlabeledFull, NOW)).toBe("7d 35%"); + }); + + it("keeps an unlabeled window once it reports depletion or a reset", () => { + const depleted = { + status: 200, + planType: "plus", + model: "gpt-5.3-codex", + primary: { usedPercent: 65, windowMinutes: 10080 }, + secondary: { usedPercent: 12 }, + } satisfies CodexQuotaSnapshot; + expect(formatCompactQuotaSnapshot(depleted, NOW)).toBe("7d 35% | quota 88%"); + + const fullWithReset = { + status: 200, + planType: "plus", + model: "gpt-5.3-codex", + primary: { usedPercent: 65, windowMinutes: 10080 }, + secondary: { usedPercent: 0, resetAtMs: SAME_DAY }, + } satisfies CodexQuotaSnapshot; + expect(formatCompactQuotaSnapshot(fullWithReset, NOW)).toBe( + "7d 35% | quota 100%", + ); + expect( + formatCompactQuotaSnapshot(fullWithReset, NOW, { showReset: true }), + ).toBe( + `7d 35% | quota 100%, resets ${formatQuotaResetAt(SAME_DAY, NOW)}`, + ); + }); + + it("does not restore quota text when both windows are uninformative", () => { + const allHidden = { + status: 200, + planType: "plus", + model: "gpt-5.3-codex", + primary: { usedPercent: 0 }, + secondary: { usedPercent: 0 }, + } satisfies CodexQuotaSnapshot; + + expect(formatCompactQuotaSnapshot(allHidden, NOW)).toBe(""); + expect( + formatCompactQuotaSnapshot(allHidden, NOW, { showReset: true }), + ).toBe(""); + expect( + formatQuotaSnapshotForDashboard( + allHidden, + { showQuotaDetails: true } as DashboardDisplaySettings, + NOW, + ), + ).toBe("live session OK"); + }); + it("formatQuotaSnapshotForDashboard is the check line and shows resets", () => { const display = { showQuotaDetails: true,