From 7b3a3be57a412fd31083b24d8c5fe8db90f00eca Mon Sep 17 00:00:00 2001 From: Samuel Soeller Date: Thu, 8 Oct 2026 14:16:12 +0200 Subject: [PATCH 1/3] fix(server): omit unstarted Claude session usage windows Avoid showing an empty five-hour session as 100% available when the weekly allowance is exhausted. Apply the same mapping to native Claude probes and CLIProxyAPI reads. Co-authored-by: Codex --- .../src/provider/claudeUsageLimits.test.ts | 36 +++++++++++++++++++ apps/server/src/provider/claudeUsageLimits.ts | 3 ++ apps/server/src/usage/cliproxyApi.test.ts | 36 +++++++++++++++++++ 3 files changed, 75 insertions(+) diff --git a/apps/server/src/provider/claudeUsageLimits.test.ts b/apps/server/src/provider/claudeUsageLimits.test.ts index a3d326a7dcb21..4cefefcf9398a 100644 --- a/apps/server/src/provider/claudeUsageLimits.test.ts +++ b/apps/server/src/provider/claudeUsageLimits.test.ts @@ -95,6 +95,42 @@ describe("claudeUsageResponseToLimits", () => { ).toEqual({ checkedAt, windows: [], unavailable: { reason: "unsupported" } }); }); + it("omits an unstarted session when the weekly allowance is exhausted", () => { + const { limits } = claudeUsageResponseToLimits({ + checkedAt, + response: { + rate_limits_available: true, + rate_limits: { + five_hour: { utilization: 0, resets_at: null }, + seven_day: { utilization: 100, resets_at: "2026-07-24T08:59:00Z" }, + }, + }, + }); + expect(limits.windows).toEqual([ + { + id: "seven_day", + kind: "weekly", + label: "Weekly", + usedPercent: 100, + windowDurationMins: 10080, + resetsAt: "2026-07-24T08:59:00.000Z", + }, + ]); + }); + + it.each([ + { utilization: 0, resets_at: "2026-07-18T14:39:00Z" }, + { utilization: 54, resets_at: null }, + ])("keeps a session with usage or a reset time: %j", (five_hour) => { + const { limits } = claudeUsageResponseToLimits({ + checkedAt, + response: { rate_limits_available: true, rate_limits: { five_hour } }, + }); + expect(limits.windows).toMatchObject([ + { id: "five_hour", kind: "session", usedPercent: five_hour.utilization }, + ]); + }); + it("skips a window the endpoint reports without a utilization", () => { expect( claudeUsageResponseToLimits({ diff --git a/apps/server/src/provider/claudeUsageLimits.ts b/apps/server/src/provider/claudeUsageLimits.ts index 3fae307afb563..37f7018baac61 100644 --- a/apps/server/src/provider/claudeUsageLimits.ts +++ b/apps/server/src/provider/claudeUsageLimits.ts @@ -169,6 +169,9 @@ export function claudeUsageResponseToLimits(input: { for (const id of Object.keys(WINDOWS)) { const window = response.rate_limits[id as "five_hour" | "seven_day"]; if (!window || typeof window.utilization !== "number") continue; + // An unstarted session has no reset and zero usage. Showing it as 100% + // left is misleading when the weekly allowance already blocks requests. + if (id === "five_hour" && window.utilization === 0 && window.resets_at === null) continue; windows.push(makeWindow(id, window.utilization, isoFromString(window.resets_at))); } // The CLI filters `model_scoped` to the overage-included allowlist, which diff --git a/apps/server/src/usage/cliproxyApi.test.ts b/apps/server/src/usage/cliproxyApi.test.ts index 5de08f9a0dc95..9fa77f4724c12 100644 --- a/apps/server/src/usage/cliproxyApi.test.ts +++ b/apps/server/src/usage/cliproxyApi.test.ts @@ -213,6 +213,42 @@ describe("CLIProxyAPI built-in management API", () => { }), ); + it.effect("reports only the exhausted weekly allowance for an unstarted Claude session", () => + Effect.gen(function* () { + const test = fixture({ + accounts: [{ ...accounts[0]!, provider: "claude" }], + upstream: () => ({ + status: 200, + body: { + five_hour: { utilization: 0, resets_at: null }, + seven_day: { utilization: 100, resets_at: "2099-01-01T00:00:00Z" }, + limits: [ + { kind: "session", percent: 0, resets_at: null, is_active: false }, + { + kind: "weekly_all", + percent: 100, + resets_at: "2099-01-01T00:00:00Z", + is_active: true, + }, + ], + }, + }), + }); + const api = yield* test.api; + const result = yield* api.readAccounts(config); + expect(result[0]?.usageLimits.windows).toEqual([ + { + id: "seven_day", + kind: "weekly", + label: "Weekly", + usedPercent: 100, + windowDurationMins: 10080, + resetsAt: "2099-01-01T00:00:00.000Z", + }, + ]); + }), + ); + it.effect("pins redemption to the displayed credit and clears only that account's cooldown", () => Effect.gen(function* () { const test = fixture(); From 160d95a6e0b493b2c41b18e6e6a6fb83e69e0ae3 Mon Sep 17 00:00:00 2001 From: Julius Marminge <51714798+juliusmarminge@users.noreply.github.com> Date: Sun, 18 Oct 2026 17:01:45 -0700 Subject: [PATCH 2/3] fix(server): retain focused coverage for unstarted Claude usage windows --- .../src/provider/claudeUsageLimits.test.ts | 2 +- apps/server/src/provider/claudeUsageLimits.ts | 5 +-- apps/server/src/usage/cliproxyApi.test.ts | 36 ------------------- 3 files changed, 4 insertions(+), 39 deletions(-) diff --git a/apps/server/src/provider/claudeUsageLimits.test.ts b/apps/server/src/provider/claudeUsageLimits.test.ts index 8eec45724bab3..d41fb418d97c5 100644 --- a/apps/server/src/provider/claudeUsageLimits.test.ts +++ b/apps/server/src/provider/claudeUsageLimits.test.ts @@ -95,7 +95,7 @@ describe("claudeUsageResponseToLimits", () => { ).toEqual({ checkedAt, windows: [], unavailable: { reason: "unsupported" } }); }); - it("omits an unstarted session when the weekly allowance is exhausted", () => { + it("omits an unstarted session so an exhausted weekly allowance is the only row", () => { const { limits } = claudeUsageResponseToLimits({ checkedAt, response: { diff --git a/apps/server/src/provider/claudeUsageLimits.ts b/apps/server/src/provider/claudeUsageLimits.ts index 15f760550485d..1a5cb62e7ddd7 100644 --- a/apps/server/src/provider/claudeUsageLimits.ts +++ b/apps/server/src/provider/claudeUsageLimits.ts @@ -150,8 +150,9 @@ export function claudeUsageResponseToLimits(input: { for (const id of Object.keys(WINDOWS)) { const window = response.rate_limits[id as "five_hour" | "seven_day"]; if (!window || typeof window.utilization !== "number") continue; - // An unstarted session has no reset and zero usage. Showing it as 100% - // left is misleading when the weekly allowance already blocks requests. + // The endpoint reports a session that never started as zero usage with + // no reset. Drawing it as "100% left" above an exhausted weekly row makes + // a blocked account look usable, so only started sessions get a row. if (id === "five_hour" && window.utilization === 0 && window.resets_at === null) continue; windows.push(makeWindow(id, window.utilization, isoFromString(window.resets_at))); } diff --git a/apps/server/src/usage/cliproxyApi.test.ts b/apps/server/src/usage/cliproxyApi.test.ts index 9a216eeed8503..82521ac8c6450 100644 --- a/apps/server/src/usage/cliproxyApi.test.ts +++ b/apps/server/src/usage/cliproxyApi.test.ts @@ -334,42 +334,6 @@ describe("CLIProxyAPI built-in management API", () => { }), ); - it.effect("reports only the exhausted weekly allowance for an unstarted Claude session", () => - Effect.gen(function* () { - const test = fixture({ - accounts: [{ ...accounts[0]!, provider: "claude" }], - upstream: () => ({ - status: 200, - body: { - five_hour: { utilization: 0, resets_at: null }, - seven_day: { utilization: 100, resets_at: "2099-01-01T00:00:00Z" }, - limits: [ - { kind: "session", percent: 0, resets_at: null, is_active: false }, - { - kind: "weekly_all", - percent: 100, - resets_at: "2099-01-01T00:00:00Z", - is_active: true, - }, - ], - }, - }), - }); - const api = yield* test.api; - const result = yield* api.readAccounts(config); - expect(result[0]?.usageLimits.windows).toEqual([ - { - id: "seven_day", - kind: "weekly", - label: "Weekly", - usedPercent: 100, - windowDurationMins: 10080, - resetsAt: "2099-01-01T00:00:00.000Z", - }, - ]); - }), - ); - it.effect("pins redemption to the displayed credit and clears only that account's cooldown", () => Effect.gen(function* () { const test = fixture(); From 1f6a50099a778072213df45aae6bdd74a478b299 Mon Sep 17 00:00:00 2001 From: Julius Marminge <51714798+juliusmarminge@users.noreply.github.com> Date: Sun, 18 Oct 2026 17:09:15 -0700 Subject: [PATCH 3/3] test(server): expect omitted session after Claude reset --- apps/server/src/provider/ProviderInstanceRegistry.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/server/src/provider/ProviderInstanceRegistry.test.ts b/apps/server/src/provider/ProviderInstanceRegistry.test.ts index 479ec05f0fb49..22757a1cc384c 100644 --- a/apps/server/src/provider/ProviderInstanceRegistry.test.ts +++ b/apps/server/src/provider/ProviderInstanceRegistry.test.ts @@ -650,7 +650,7 @@ describe("ProviderInstanceRegistry — multi-instance codex slice", () => { usageFailsAfterClaim: false, }); expect(outcome).toMatchObject({ _tag: "Success", success: "reset" }); - expect(after.usageLimits?.windows[0]?.usedPercent).toBe(0); + expect(after.usageLimits?.windows).toEqual([]); }), );