From 65ef740003537f7f0709ec6359b34fde481b4a6f Mon Sep 17 00:00:00 2001 From: can1357 Date: Thu, 30 Jul 2026 01:43:54 +0200 Subject: [PATCH] fix(coding-agent): preserved Codex quota identity --- packages/coding-agent/CHANGELOG.md | 1 + .../modes/components/codex-reset-fireworks.ts | 7 ++- .../modes/components/status-line/component.ts | 10 ++++- .../test/status-line-usage-refresh.test.ts | 43 ++++++++++++++++++- 4 files changed, 55 insertions(+), 6 deletions(-) diff --git a/packages/coding-agent/CHANGELOG.md b/packages/coding-agent/CHANGELOG.md index 0c78f3da5..478afb456 100644 --- a/packages/coding-agent/CHANGELOG.md +++ b/packages/coding-agent/CHANGELOG.md @@ -18,6 +18,7 @@ ### Fixed +- Fixed Codex reset fireworks comparing different quota tiers or plans, preventing false celebrations when usage reports switch between Spark and base weekly limits. - Fixed `/tan` agents being unable to read parent-session `local://` attachments by correctly resolving local protocol options against the parent session's artifacts. - Fixed Codex web search silently returning plain completions when the hosted web search tool was skipped. - Fixed TUI collaboration guest loader not starting when joining or reconnecting mid-turn. diff --git a/packages/coding-agent/src/modes/components/codex-reset-fireworks.ts b/packages/coding-agent/src/modes/components/codex-reset-fireworks.ts index 3c48ce115..10d7018c0 100644 --- a/packages/coding-agent/src/modes/components/codex-reset-fireworks.ts +++ b/packages/coding-agent/src/modes/components/codex-reset-fireworks.ts @@ -28,8 +28,8 @@ type FireworkColor = keyof typeof FIREWORK_THEME_COLORS; export interface CodexResetUsageSnapshot { /** When this usage report was observed, if supplied by the provider. */ observedAt?: number; - /** Weekly usage and its previously scheduled reset deadline. */ - sevenDay?: { percent: number; resetsAt?: number }; + /** Weekly usage, its quota identity, and its previously scheduled reset deadline. */ + sevenDay?: { percent: number; resetsAt?: number; tier?: string; plan?: string }; savedResets?: number; } @@ -103,6 +103,9 @@ export function detectCodexResetFireworks( } if (!previous.sevenDay || !current.sevenDay) return undefined; + if (previous.sevenDay.tier !== current.sevenDay.tier || previous.sevenDay.plan !== current.sevenDay.plan) { + return undefined; + } const previousWeeklyPercent = Math.round(Math.max(0, Math.min(100, previous.sevenDay.percent))); const currentWeeklyPercent = Math.round(Math.max(0, Math.min(100, current.sevenDay.percent))); if (previousWeeklyPercent === 0 || currentWeeklyPercent >= previousWeeklyPercent) return undefined; diff --git a/packages/coding-agent/src/modes/components/status-line/component.ts b/packages/coding-agent/src/modes/components/status-line/component.ts index 3b134724a..38d4785b9 100644 --- a/packages/coding-agent/src/modes/components/status-line/component.ts +++ b/packages/coding-agent/src/modes/components/status-line/component.ts @@ -1319,6 +1319,10 @@ export class StatusLineComponent implements Component { } if (!matchingReport) return null; + const plan = + typeof matchingReport.metadata?.planType === "string" && matchingReport.metadata.planType + ? matchingReport.metadata.planType + : undefined; let sevenDay: CodexResetUsageSnapshot["sevenDay"]; let sevenDayTier: string | undefined; for (const limit of matchingReport.limits) { @@ -1332,14 +1336,16 @@ export class StatusLineComponent implements Component { if (candidate.scope?.windowId !== "7d" || typeof fraction !== "number" || !Number.isFinite(fraction)) { continue; } - const tier = candidate.scope.tier; + const tier = typeof candidate.scope?.tier === "string" && candidate.scope.tier ? candidate.scope.tier : undefined; if (sevenDay && (sevenDayTier === undefined || tier)) continue; const resetsAt = candidate.window?.resetsAt; sevenDay = { percent: fraction * 100, resetsAt: typeof resetsAt === "number" && Number.isFinite(resetsAt) ? resetsAt : undefined, + tier, + plan, }; - sevenDayTier = tier || undefined; + sevenDayTier = tier; } const fetchedAt = matchingReport.fetchedAt; diff --git a/packages/coding-agent/test/status-line-usage-refresh.test.ts b/packages/coding-agent/test/status-line-usage-refresh.test.ts index db83fb3da..499ca96d4 100644 --- a/packages/coding-agent/test/status-line-usage-refresh.test.ts +++ b/packages/coding-agent/test/status-line-usage-refresh.test.ts @@ -63,6 +63,8 @@ interface CodexUsageState { sevenDayResetAt: number; savedResets?: number; omitFetchedAt?: boolean; + tier?: string; + plan?: string; } function codexUsageReport( @@ -75,13 +77,23 @@ function codexUsageReport( { provider: "openai-codex", ...(state.omitFetchedAt ? {} : { fetchedAt: Date.now() }), - metadata: { accountId, email, ...(orgId ? { orgId } : {}) }, + metadata: { + accountId, + email, + ...(orgId ? { orgId } : {}), + ...(state.plan ? { planType: state.plan } : {}), + }, ...(state.savedResets === undefined ? {} : { resetCredits: { availableCount: state.savedResets } }), limits: [ { id: "openai-codex:secondary", label: "Codex 7 Day", - scope: { provider: "openai-codex", accountId, windowId: "7d" }, + scope: { + provider: "openai-codex", + accountId, + windowId: "7d", + ...(state.tier ? { tier: state.tier } : {}), + }, window: { id: "7d", label: "7d", @@ -330,6 +342,33 @@ describe("StatusLineComponent usage refresh", () => { component.dispose(); }); + it("compares weekly reset drops only within the same Codex quota tier", async () => { + Settings.instance.set("tui.codexResetFireworks", true); + const sevenDayResetAt = Date.now() + 80 * 3_600_000; + let state: CodexUsageState = { + sevenDayPercent: 42, + sevenDayResetAt, + savedResets: 0, + tier: "spark", + plan: "pro", + }; + const component = new StatusLineComponent(makeCodexSession(async () => codexUsageReport(state))); + const events: CodexResetFireworksEvent[] = []; + component.setCodexResetFireworksHandler(event => events.push(event)); + + await refreshUsage(component); + state = { ...state, sevenDayPercent: 2, tier: undefined }; + await refreshUsage(component, 5 * 60_000); + expect(events).toEqual([]); + + state = { ...state, sevenDayPercent: 42, tier: "spark" }; + await refreshUsage(component, 5 * 60_000); + state = { ...state, sevenDayPercent: 2 }; + await refreshUsage(component, 5 * 60_000); + expect(events).toEqual([{ kind: "unscheduled-weekly-reset" }]); + component.dispose(); + }); + it("binds each reset snapshot to the account identity used to normalize it", async () => { Settings.instance.set("tui.codexResetFireworks", true); const sevenDayResetAt = Date.now() + 80 * 3_600_000;