fix(usage): derive window names and unify pin selection (#5903)

* fix(usage): derive window names and unify pin selection

Quota slots do not identify a period. Derive numeric windows and IDs from reported duration and quota scope; keep unknown lengths neutral. Resolve source defaults and explicit pins through one model, with a persisted empty selection distinct from defaults.

* fix(plugins): declare the duration helper host requirement

* fix(usage): verify persisted selections at the storage boundary
This commit is contained in:
Mohamed Boudra
2026-10-02 11:18:13 +01:00
committed by GitHub
parent 6c985769be
commit 73d25317a4
27 changed files with 815 additions and 172 deletions
+128 -9
View File
@@ -39,7 +39,9 @@ function response(headers: HeadersInit, accountId?: string): Promise<Response> {
new Response(
JSON.stringify({
plan_type: "plus",
rate_limit: { primary_window: { used_percent: 30, reset_at: 1700000000 } },
rate_limit: {
primary_window: { used_percent: 30, limit_window_seconds: 18000, reset_at: 1700000000 },
},
}),
{ status: 200 },
),
@@ -62,7 +64,7 @@ test("explicit route reads Codex auth and preserves the usage request", async ()
expect(report).toMatchObject({
status: "available",
planLabel: "plus",
windows: [{ id: "session", usedPct: 30 }],
windows: [{ id: "five_hour", usedPct: 30 }],
});
} finally {
await rm(home, { recursive: true, force: true });
@@ -95,8 +97,8 @@ test("coerces credit balance and marks a 96 percent window dangerous", async ()
new Response(
JSON.stringify({
rate_limit: {
primary_window: { used_percent: 12 },
secondary_window: { used_percent: 96 },
primary_window: { used_percent: 12, limit_window_seconds: 18000 },
secondary_window: { used_percent: 96, limit_window_seconds: 604800 },
},
credits: { balance: "0" },
}),
@@ -105,7 +107,7 @@ test("coerces credit balance and marks a 96 percent window dangerous", async ()
);
expect(report.windows).toEqual(
expect.arrayContaining([
expect.objectContaining({ id: "session", tone: "ok" }),
expect.objectContaining({ id: "five_hour", tone: "ok" }),
expect.objectContaining({ id: "weekly", tone: "danger" }),
]),
);
@@ -121,8 +123,8 @@ test("summarizes the session and weekly windows by default, not code review", as
new Response(
JSON.stringify({
rate_limit: {
primary_window: { used_percent: 12 },
secondary_window: { used_percent: 40 },
primary_window: { used_percent: 12, limit_window_seconds: 18000 },
secondary_window: { used_percent: 40, limit_window_seconds: 604800 },
},
code_review_rate_limit: { primary_window: { used_percent: 5 } },
}),
@@ -130,9 +132,9 @@ test("summarizes the session and weekly windows by default, not code review", as
),
);
expect(report.windows.map((window) => [window.id, window.summary ?? false])).toEqual([
["session", true],
["five_hour", true],
["weekly", true],
["code_review", false],
["code_review:unknown_primary", false],
]);
});
@@ -260,3 +262,120 @@ test.each([401, 403])("reports a Codex login rejected with HTTP %i", async (stat
await fetchUsage(authInput(fixtureHome), async () => new Response(null, { status })),
).toEqual({ status: "unavailable", problem: { kind: "rejected", status, refreshedBy: "codex" } });
});
// Issue #4165: the primary slot contains the only weekly window.
test("names a seven-day-only primary window from its reported duration", async () => {
const report = await fetchUsage(authInput(fixtureHome), async () =>
Response.json({
rate_limit: {
primary_window: { used_percent: 1, limit_window_seconds: 604800 },
secondary_window: null,
},
}),
);
expect(report).toMatchObject({
status: "available",
windows: [{ id: "weekly", label: "Weekly", shortLabel: "wk", usedPct: 1 }],
});
});
test("reports every named additional limit with its own duration identity", async () => {
const report = await fetchUsage(authInput(fixtureHome), async () =>
Response.json({
additional_rate_limits: [
{
limit_name: "GPT-5.3-Codex-Spark",
metered_feature: "codex_bengalfox",
rate_limit: {
primary_window: { used_percent: 0, limit_window_seconds: 18000 },
secondary_window: { used_percent: 0, limit_window_seconds: 604800 },
},
},
{
limit_name: "Another limit",
metered_feature: "another",
rate_limit: {
primary_window: { used_percent: 4, limit_window_seconds: 7200 },
},
},
],
}),
);
expect(report).toMatchObject({
status: "available",
windows: [
{
id: "limit:codex_bengalfox:five_hour",
label: "GPT-5.3-Codex-Spark · 5-hour",
shortLabel: "GPT-5.3-Codex-Spark 5h",
},
{
id: "limit:codex_bengalfox:weekly",
label: "GPT-5.3-Codex-Spark · Weekly",
shortLabel: "GPT-5.3-Codex-Spark wk",
},
{ id: "limit:another:7200s", label: "Another limit · 2-hour" },
],
});
});
test("a weekly pin means weekly after the provider moves it between slots", async () => {
const { resolvePinnedUsage } = await import("../../../packages/app/src/usage/pinned.js");
const preferences = {
displayAs: "used" as const,
serverId: null,
pins: [{ sourceId: "codex", windowId: "weekly" }],
};
for (const primaryWeekly of [false, true]) {
const weekly = { used_percent: 11, limit_window_seconds: 604800 };
const report = await fetchUsage(authInput(fixtureHome), async () =>
Response.json({
rate_limit: {
primary_window: primaryWeekly ? weekly : { used_percent: 4, limit_window_seconds: 18000 },
secondary_window: primaryWeekly ? null : weekly,
},
}),
);
const entry = {
id: "codex:account",
sourceId: "codex",
sourceLabel: "Codex",
account: { key: "account" },
fetchedAt: "2026-10-02T00:00:00Z",
report,
};
expect(resolvePinnedUsage([entry], preferences)).toMatchObject([
{ windows: [{ key: "codex:account/weekly", percentText: "11%", shortLabel: "wk" }] },
]);
// The old slot ID is ambiguous: do not silently retarget a saved Session pin.
expect(
resolvePinnedUsage([entry], {
...preferences,
pins: [{ sourceId: "codex", windowId: "session" }],
}),
).toEqual([]);
}
});
test("unknown lengths and code review never get a duration from their slot", async () => {
const report = await fetchUsage(authInput(fixtureHome), async () =>
Response.json({
rate_limit: {
primary_window: { used_percent: 11 },
secondary_window: { used_percent: 22, limit_window_seconds: null },
},
code_review_rate_limit: {
primary_window: { used_percent: 3, limit_window_seconds: 604800 },
secondary_window: { used_percent: 4, limit_window_seconds: 18000 },
},
}),
);
expect(report).toMatchObject({
windows: [
{ id: "unknown_primary", label: "Primary limit", shortLabel: "" },
{ id: "unknown_secondary", label: "Secondary limit", shortLabel: "" },
{ id: "code_review:weekly", label: "Code review · Weekly", shortLabel: "Code review wk" },
{ id: "code_review:five_hour", label: "Code review · 5-hour", shortLabel: "Code review 5h" },
],
});
});
+68 -29
View File
@@ -7,7 +7,7 @@ import {
unavailable,
type UsageAccount,
toneFromUsedPct,
windowFromUsedPct,
windowFromReportedDuration,
type UsageReport,
type UsageWindow,
} from "@getpaseo/plugin/server/usage";
@@ -24,14 +24,29 @@ const authSchema = z.object({
.optional(),
});
const number = z.coerce.number().finite();
const windowSchema = z.object({ used_percent: number.optional(), reset_at: number.optional() });
const windowSchema = z.object({
used_percent: number.optional(),
reset_at: number.optional(),
limit_window_seconds: number.nullish(),
});
const rateLimitSchema = z.object({
primary_window: windowSchema.nullish(),
secondary_window: windowSchema.nullish(),
});
const responseSchema = z.object({
plan_type: z.string().optional(),
email: z.string().optional(),
rate_limit: z
.object({ primary_window: windowSchema.nullish(), secondary_window: windowSchema.nullish() })
rate_limit: rateLimitSchema.nullish(),
additional_rate_limits: z
.array(
z.object({
limit_name: z.string().optional(),
metered_feature: z.string().optional(),
rate_limit: rateLimitSchema.nullish(),
}),
)
.nullish(),
code_review_rate_limit: z.object({ primary_window: windowSchema.nullish() }).nullish(),
code_review_rate_limit: rateLimitSchema.nullish(),
credits: z.object({ balance: number.optional() }).nullish(),
});
@@ -90,17 +105,37 @@ export async function readAuth(
};
}
function usageWindow(
spec: { id: string; label: string; shortLabel: string; summary?: boolean },
value: z.infer<typeof windowSchema> | null | undefined,
): UsageWindow | null {
if (!value) return null;
const usedPct = value.used_percent ?? 0;
return windowFromUsedPct({
...spec,
utilizationPct: usedPct,
resetsAt: value.reset_at != null ? new Date(value.reset_at * 1000).toISOString() : null,
tone: toneFromUsedPct(usedPct),
function usageWindows({
rateLimit,
scope,
summary = false,
}: {
rateLimit: z.infer<typeof rateLimitSchema> | null | undefined;
scope?: { id: string; label: string };
summary?: boolean;
}): UsageWindow[] {
if (!rateLimit) return [];
return (["primary_window", "secondary_window"] as const).flatMap((slot) => {
const value = rateLimit[slot];
if (!value) return [];
const primary = slot === "primary_window";
// Codex does not always report a length. A slot is then only an unknown limit,
// never evidence of a five-hour or weekly period.
return [
windowFromReportedDuration({
durationSeconds: value.limit_window_seconds ?? null,
unknown: {
id: primary ? "unknown_primary" : "unknown_secondary",
label: primary ? "Primary limit" : "Secondary limit",
shortLabel: "",
},
scope,
summary,
utilizationPct: value.used_percent,
resetsAt: value.reset_at != null ? new Date(value.reset_at * 1000).toISOString() : null,
tone: toneFromUsedPct(value.used_percent),
}),
];
});
}
@@ -135,19 +170,23 @@ export async function fetchUsage(
if (text.trim().startsWith("<")) throw new Error("Codex usage API returned HTML");
const usage = responseSchema.parse(JSON.parse(text));
const windows = [
usageWindow(
{ id: "session", label: "Session", shortLabel: "5h", summary: true },
usage.rate_limit?.primary_window,
),
usageWindow(
{ id: "weekly", label: "Weekly", shortLabel: "wk", summary: true },
usage.rate_limit?.secondary_window,
),
usageWindow(
{ id: "code_review", label: "Code review", shortLabel: "review" },
usage.code_review_rate_limit?.primary_window,
),
].filter((window): window is UsageWindow => window !== null);
...usageWindows({ rateLimit: usage.rate_limit, summary: true }),
...(usage.additional_rate_limits ?? []).flatMap((limit) => {
const identity = limit.metered_feature || limit.limit_name;
if (!identity) return []; // An unnamed limit has no stable quota identity.
return usageWindows({
rateLimit: limit.rate_limit,
scope: {
id: `limit:${encodeURIComponent(identity)}`,
label: limit.limit_name || identity,
},
});
}),
...usageWindows({
rateLimit: usage.code_review_rate_limit,
scope: { id: "code_review", label: "Code review" },
}),
];
const balance = usage.credits?.balance;
return {
status: "available",