diff --git a/packages/ai/src/usage/xai-oauth.ts b/packages/ai/src/usage/xai-oauth.ts index 9a6e172a8..40aaa08ed 100644 --- a/packages/ai/src/usage/xai-oauth.ts +++ b/packages/ai/src/usage/xai-oauth.ts @@ -138,11 +138,14 @@ function parseWeeklyBillingConfig(raw: Record): XaiWeeklyBillin } // Fresh weekly periods (or accounts with 0 usage) omit creditUsagePercent; - // default to 0 when a valid weekly period exists. - const creditUsagePercent = - raw.creditUsagePercent === undefined || raw.creditUsagePercent === null - ? 0 - : parsePercent(raw.creditUsagePercent); + // default to 0 only when the weekly period is active (end > now). + // Expired periods without explicit usage data are rejected to retain last good cache. + let creditUsagePercent: number | undefined; + if (raw.creditUsagePercent === undefined || raw.creditUsagePercent === null) { + creditUsagePercent = end > Date.now() ? 0 : undefined; + } else { + creditUsagePercent = parsePercent(raw.creditUsagePercent); + } if (creditUsagePercent === undefined) return null; const productUsage: XaiProductUsage[] = []; diff --git a/packages/ai/test/xai-oauth-usage.test.ts b/packages/ai/test/xai-oauth-usage.test.ts index 9be58536f..1330f2128 100644 --- a/packages/ai/test/xai-oauth-usage.test.ts +++ b/packages/ai/test/xai-oauth-usage.test.ts @@ -224,6 +224,26 @@ describe("xai-oauth usage provider", () => { expect(report?.limits[0]?.id).toBe("xai-oauth:credits:1w"); expect(report?.limits[0]?.window?.resetsAt).toBe(Date.parse(periodEnd)); }); + it("rejects an expired weekly period when creditUsagePercent is omitted", async () => { + const periodEnd = new Date(Date.now() - 60_000).toISOString(); + const periodStart = new Date(Date.now() - 8 * 24 * 60 * 60 * 1000).toISOString(); + const report = await xaiOauthUsageProvider.fetchUsage( + { provider: "xai-oauth", credential: makeCredential() }, + { + fetch: capturingFetch({ + config: { + currentPeriod: { + end: periodEnd, + start: periodStart, + type: "USAGE_PERIOD_TYPE_WEEKLY", + }, + }, + }).fetch, + }, + ); + + expect(report).toBeNull(); + }); it("reports zero usage when weekly period is active but creditUsagePercent is omitted (fresh reset)", async () => { const periodEnd = new Date(Date.now() + 6 * 24 * 60 * 60 * 1000).toISOString();