fix(quota): align AUTH window labels with usage API and clarify 503 (#12884)

Um `ALL_TARGETS_SKIPPED` 503 que não diz qual janela esgotou é opaco justamente no momento em que o operador mais precisa saber. Alinhar os rótulos de janela AUTH com os da API de uso fecha a outra metade: dois nomes para a mesma coisa fazem o dashboard e o erro parecerem discordar.

Revalidei sobre o tip: **6/6**, typecheck:core limpo, check-file-size OK.

**Dois consertos meus na sua branch.**

1. `typecheck:core` falhava com `TS2345` em `comboAttemptLoop.ts` (linhas 130 e 416): o `QuotaSkipTarget` declarava `connectionId?: string`, mas o `ResolvedComboTarget` carrega `string | null` para alvo não-pinado. Alarguei para `string | null` no tipo de diagnóstico em vez de estreitar o call site — o módulo só **lê** o campo e a linha 29 já narrowa com `typeof === "string"`, então null não custa nada ali. Isso apareceu porque o `comboAttemptLoop` mudou de forma no #12746/#12811, mergeados nesta mesma campanha depois que você cortou a branch.

2. O `roundRobinCombo.ts` foi de 1198 para 1205 e cruzou o teto de 1200 para arquivo novo. Congelei com justificativa: o arquivo já nasceu em 1198 quando o #12811 o levantou de dentro do `combo.ts`, e os diagnósticos em si vivem no `quotaSkipDiagnostics.ts`, sob o cap. Registrei que a próxima extração natural é o corpo do attempt loop, mas que ele acabou de ser movido e deve assentar antes de ser cortado de novo.
This commit is contained in:
Ravi Tharuma
2026-09-10 15:47:11 +02:00
committed by GitHub
parent 4c10baa644
commit d6a61074dc
9 changed files with 326 additions and 20 deletions

View File

@@ -469,7 +469,8 @@
"src/sse/services/auth.ts": 3450,
"tests/unit/account-fallback-service.test.ts": 2453,
"tests/unit/provider-validation-specialty.test.ts": 4656,
"open-sse/services/autoCombo/virtualFactory.ts": 1219
"open-sse/services/autoCombo/virtualFactory.ts": 1219,
"open-sse/services/combo/roundRobinCombo.ts": 1205
},
"_rebaseline_base_2026_08_10_proxyfetch": "Base-red fix (green-prs sweep, issue #9985): open-sse/utils/proxyFetch.ts 1207 > cap 1000 — new proxied-TLS fetch helper introduced by the Fal reference-image work. Owner-authorized quick rebaseline to green; structural slim tracked for v3.9.0.",
"_rebaseline_2026_07_27_v3849_train2": "Merge-train 2 (7 PRs) — owner-approved 2026-07-27. Single entry: chatCore.ts 4955->5006 (#8595, Responses multi-turn image compaction before the context hard-reject). Genuine irreducible growth at the existing compaction chokepoint in handleChatCore — the PR adds a last-resort retry against the concrete budget plus the estimateFinalInputTokens helper, both wired at the pre-existing call site rather than a new branch. Covered by tests/unit/8560-responses-image-compaction.test.ts (4 tests).",
@@ -656,5 +657,6 @@
"_rebaseline_2026_09_05_12671_combos_usage_guide_external_store": "combos/page.tsx 5018 -> 5066: #12671 replaces the effect-based localStorage read with useSyncExternalStore; the +48 lines are the store helpers (subscribe/getSnapshot/getServerSnapshot/emit) hoisted to module scope, which is the sanctioned shape and what let the react-hooks/set-state-in-effect suppression be dropped.",
"_rebaseline_2026_09_07_chatcore_nonstreaming_regression_fixes": "Own growth: open-sse/handlers/chatCore.ts 5984->6021 (+37). Two of my own PRs on top of #12867: #12963 pins the ok variant of the non-streaming leg result in its own binding (the discriminated-union narrowing was lost across the tool-loop reassignment, 13 TS2339 under tsconfig.typecheck-api.json), and #12990 restores four behaviours the same refactor dropped — abort classification through isLocalStreamLifecycleError, the omitted synthetic clientResponse, the claudePromptCacheLogMeta rebuild on the leg path, and the lazy fail-closed fence identity. Irreducible at the existing chokepoints: each edit sits where chatCore already owns the decision, and the helpers themselves (nonStreamingProviderLeg.ts, serverOwnedToolLoopWire.ts) are under cap. Covered by tests/unit/chatcore-translation-paths.test.ts (72/74; the 2 open are issue #13043).",
"_rebaseline_2026_09_07_virtualfactory_crosses_the_new_file_cap": "open-sse/services/autoCombo/virtualFactory.ts crosses the 1200 new-file cap for the first time (1187 on the pre-wave tip, 1219 after the wave). Growth is spread across the routing/free-tier wave, not one extractable block: #12794 feeds observed breaker state and model quality into snapshot scoring instead of neutral constants, #12792 adds the reliability factor the snapshot path was still ignoring and the pooled-latency bootstrap, and #12744 tightens the free-model predicate the factory consumes, and #12795 records which filter stage emptied an auto/* pool. FROZEN RATHER THAN SPLIT, deliberately, and this is debt: two cohesive extraction candidates are ready when someone owns the move — computeSnapshotWeights (~85 lines) and the credential-eligibility group hasUsableOAuthToken/hasProviderSpecificSessionData/isKeylessEligibleConnection/hasUsableConnectionCredential (~70 lines). Either alone clears 1200 from here. Splitting three contributors' just-merged work mid-batch was the larger risk.",
"_rebaseline_2026_09_07_streaming_wave": "Stacked growth from the SSE/streaming wave. open-sse/handlers/chatCore.ts 6021->6026 (+5): #12854 seeds the in-memory pending continuation state synchronously, before saveCallLogOperation's first await, closing the window where resolvePreviousResponseState finds nothing because the artifact write has not landed yet. open-sse/utils/stream.ts 3080->3098 (+18): #12828 emits the trailing usage-estimate chunk on the translate flush (#12151 had only covered passthrough, so translate-mode clients never saw token counts) and #12718 stops rebuilding a truncated summary from the collector's cap-dropped event array. Irreducible at the existing chokepoints — both are the flush/finalization points themselves. Covered by the continuation-store, translate-usage and collector-truncation suites."
"_rebaseline_2026_09_07_streaming_wave": "Stacked growth from the SSE/streaming wave. open-sse/handlers/chatCore.ts 6021->6026 (+5): #12854 seeds the in-memory pending continuation state synchronously, before saveCallLogOperation's first await, closing the window where resolvePreviousResponseState finds nothing because the artifact write has not landed yet. open-sse/utils/stream.ts 3080->3098 (+18): #12828 emits the trailing usage-estimate chunk on the translate flush (#12151 had only covered passthrough, so translate-mode clients never saw token counts) and #12718 stops rebuilding a truncated summary from the collector's cap-dropped event array. Irreducible at the existing chokepoints — both are the flush/finalization points themselves. Covered by the continuation-store, translate-usage and collector-truncation suites.",
"_rebaseline_2026_09_07_roundrobin_crosses_new_file_cap": "open-sse/services/combo/roundRobinCombo.ts 1198->1205, crossing the 1200 new-file cap. #12884 wires the quota-skip diagnostics into the round-robin attempt path so an ALL_TARGETS_SKIPPED 503 names which windows were exhausted instead of returning an opaque skip. The file was already at 1198 when #12811 lifted it out of combo.ts, so seven lines cross it; the diagnostics themselves live in quotaSkipDiagnostics.ts, under cap. Frozen rather than split: the natural next extraction is the attempt-loop body, which #12746/#12811 just moved and should settle before being cut again."
}

View File

@@ -4,6 +4,7 @@ import {
CODEX_SPARK_QUOTA_WEEKLY,
isCodexSparkLimitDescriptor,
} from "../config/codexQuotaScopes.ts";
import { inferWindowFamilyLabel } from "./quotaWindowLabel.ts";
type JsonRecord = Record<string, unknown>;
@@ -110,6 +111,7 @@ function buildPercentageQuota(window: JsonRecord, displayName?: string): CodexUs
// duration instead of assuming primary=session / secondary=weekly by position.
const WEEKLY_MIN_WINDOW_SECONDS = 6 * 24 * 3600; // >= ~6d
const SESSION_MAX_WINDOW_SECONDS = 6 * 3600; // <= ~6h
const MONTHLY_MIN_WINDOW_SECONDS = 20 * 24 * 3600; // >= ~20d (ChatGPT 30d plans)
/**
* A never-started window: `used_percent === 0` and the reset still spans the
@@ -133,12 +135,15 @@ function isLatentWindow(window: JsonRecord): boolean {
* e.g. a 7-day `primary_window` is labeled "Weekly" rather than "Session".
* Returns undefined for durations that don't clearly map to either bucket.
*/
function windowDurationLabel(window: JsonRecord): "Session" | "Weekly" | undefined {
function windowDurationLabel(window: JsonRecord): "Session" | "Weekly" | "Monthly" | undefined {
const limitWindow = toNumber(
getFieldValue(window, "limit_window_seconds", "limitWindowSeconds"),
0
);
if (limitWindow <= 0) return undefined;
const inferred = inferWindowFamilyLabel(limitWindow);
if (inferred === "Monthly" || inferred === "Weekly" || inferred === "Session") return inferred;
if (limitWindow >= MONTHLY_MIN_WINDOW_SECONDS) return "Monthly";
if (limitWindow >= WEEKLY_MIN_WINDOW_SECONDS) return "Weekly";
if (limitWindow <= SESSION_MAX_WINDOW_SECONDS) return "Session";
return undefined;
@@ -280,7 +285,7 @@ export function buildCodexUsageQuotas(dataValue: unknown): {
const primaryLabel = windowDurationLabel(primaryWindow);
quotas.session = buildPercentageQuota(
primaryWindow,
primaryLabel === "Weekly" ? primaryLabel : undefined
primaryLabel === "Weekly" || primaryLabel === "Monthly" ? primaryLabel : undefined
);
}

View File

@@ -14,6 +14,7 @@ import type { ComboDiagnostics } from "../../utils/error.ts";
import { COMBO_FAILURE_THRESHOLD, recordComboFailure } from "./failureTracker.ts";
import { buildNoUpstreamResponseDiagnostics, buildRecoveryHint } from "./pinRecovery.ts";
import { formatExhaustedConnectionKey } from "./comboDiagFormat.ts";
import { collectQuotaWindowExclusions, formatQuotaSkipMessage } from "./quotaSkipDiagnostics.ts";
import { recordComboRequest } from "../comboMetrics.ts";
import { notifyWebhookEvent } from "../../../src/lib/webhookDispatcher.ts";
import { parseModel } from "../model.ts";
@@ -125,6 +126,9 @@ export async function dispatchWithCooldownRetry(opts: {
excluded: [
...[...state.exhaustedProviders].map((p) => ({ provider: p, reason: "exhausted" })),
...[...state.exhaustedConnections].map((c) => formatExhaustedConnectionKey(String(c))),
...(terminalReason === "all_targets_skipped"
? collectQuotaWindowExclusions(state.orderedTargets)
: []),
],
attemptOrder: state.comboAttemptOrder,
terminalReason,
@@ -408,10 +412,15 @@ export async function dispatchWithCooldownRetry(opts: {
latencyMs,
fallbackCount: state.fallbackCount,
});
const quotaSkip = formatQuotaSkipMessage(
collectQuotaWindowExclusions(state.orderedTargets)
);
return withQuotaExhaustionClassification(
errorResponseWithComboDiagnostics(
503,
"Service temporarily unavailable: all targets were skipped by pre-dispatch filters",
quotaSkip
? `Service temporarily unavailable: all targets were skipped by pre-dispatch filters (${quotaSkip})`
: "Service temporarily unavailable: all targets were skipped by pre-dispatch filters",
buildComboDiag("all_targets_skipped"),
{ code: "ALL_TARGETS_SKIPPED", type: "service_unavailable" }
),

View File

@@ -0,0 +1,75 @@
/**
* Redacted-safe quota-window skip rows for ALL_TARGETS_SKIPPED 503 bodies.
* Connection ids are prefix-only (8 chars). Labels match AUTH / usage API.
*/
import { getQuotaCache } from "@/domain/quotaCache";
import { formatQuotaWindowLabel } from "../quotaWindowLabel.ts";
export type QuotaSkipTarget = {
provider?: string;
model?: string;
modelStr?: string;
// `string | null`, not `string | undefined`: ResolvedComboTarget carries null for an
// unpinned target, and this module only reads the field. Line 29 already narrows with
// `typeof === "string"`, so null costs nothing here (TS2345 under typecheck:core).
connectionId?: string | null;
};
export type QuotaSkipExclusion = {
provider: string;
model?: string;
reason: string;
};
const EXHAUSTED_USED_PERCENT = 99;
export function collectQuotaWindowExclusions(targets: QuotaSkipTarget[]): QuotaSkipExclusion[] {
const seen = new Set<string>();
const out: QuotaSkipExclusion[] = [];
for (const target of targets) {
const connectionId = typeof target.connectionId === "string" ? target.connectionId : "";
if (!connectionId) continue;
const entry = getQuotaCache(connectionId);
if (!entry?.quotas) continue;
const provider =
typeof target.provider === "string" && target.provider ? target.provider : "unknown";
const model =
(typeof target.modelStr === "string" && target.modelStr) ||
(typeof target.model === "string" && target.model) ||
undefined;
const connPrefix = connectionId.slice(0, 8);
for (const [key, quota] of Object.entries(entry.quotas)) {
const remaining = quota.remainingPercentage;
if (typeof remaining !== "number" || !Number.isFinite(remaining)) continue;
const used = Math.max(0, Math.min(100, 100 - remaining));
if (used < EXHAUSTED_USED_PERCENT) continue;
const label = formatQuotaWindowLabel({
key,
displayName: quota.displayName,
windowSeconds: quota.windowSeconds,
});
const reason = `quota:${label} ${Math.round(used)}% conn:${connPrefix}`;
const dedupe = `${provider}|${model ?? ""}|${reason}`;
if (seen.has(dedupe)) continue;
seen.add(dedupe);
out.push({
provider,
...(model ? { model } : {}),
reason,
});
}
}
return out;
}
export function formatQuotaSkipMessage(exclusions: QuotaSkipExclusion[]): string | null {
if (exclusions.length === 0) return null;
const windows = [...new Set(exclusions.map((row) => row.reason.replace(/^quota:/, "")))];
return windows.slice(0, 4).join("; ");
}

View File

@@ -15,6 +15,7 @@ import {
} from "../../utils/error.ts";
import { buildRecoveryHint } from "./pinRecovery.ts";
import { formatExhaustedConnectionKey } from "./comboDiagFormat.ts";
import { collectQuotaWindowExclusions, formatQuotaSkipMessage } from "./quotaSkipDiagnostics.ts";
import { recordComboRequest } from "../comboMetrics.ts";
import {
expandComboSystemPromptIfPresent,
@@ -1152,16 +1153,21 @@ export async function handleRoundRobinCombo({
if (!lastStatus) {
if (recordedAttempts === 0) {
return new Response(
JSON.stringify({
error: {
message:
"Service temporarily unavailable: all targets were skipped by pre-dispatch filters",
type: "service_unavailable",
code: "ALL_TARGETS_SKIPPED",
},
}),
{ status: 503, headers: { "Content-Type": "application/json" } }
const quotaExcluded = collectQuotaWindowExclusions(filteredTargets);
const quotaSkip = formatQuotaSkipMessage(quotaExcluded);
return errorResponseWithComboDiagnostics(
503,
quotaSkip
? `Service temporarily unavailable: all targets were skipped by pre-dispatch filters (${quotaSkip})`
: "Service temporarily unavailable: all targets were skipped by pre-dispatch filters",
{
poolSize: filteredTargets.length,
attempted: 0,
excluded: quotaExcluded,
attemptOrder: [],
terminalReason: "all_targets_skipped",
},
{ code: "ALL_TARGETS_SKIPPED", type: "service_unavailable" }
);
}
return new Response(

View File

@@ -0,0 +1,73 @@
/**
* Shared duration-aware quota window labels for AUTH preflight and /api/usage.
* Routing keys (`session`, `weekly`) stay position-based; only the display
* string follows the real window length so a 30-day primary window is not
* reported as "session (5h)".
*/
const SESSION_MAX_SECONDS = 6 * 3600;
const WEEKLY_MAX_SECONDS = 8 * 24 * 3600;
const MONTHLY_MAX_SECONDS = 40 * 24 * 3600;
const GENERIC_FAMILY = new Set(["session", "weekly", "monthly"]);
export type QuotaWindowLabelInput = {
key: string;
displayName?: string | null;
windowSeconds?: number | null;
};
function toPositiveSeconds(value: unknown): number | null {
if (typeof value === "number" && Number.isFinite(value) && value > 0) return value;
if (typeof value === "string" && value.trim().length > 0) {
const parsed = Number(value);
if (Number.isFinite(parsed) && parsed > 0) return parsed;
}
return null;
}
export function formatWindowDuration(windowSeconds: number): string {
if (windowSeconds % 86400 === 0) return `${windowSeconds / 86400}d`;
if (windowSeconds % 3600 === 0) return `${windowSeconds / 3600}h`;
return `${Math.round(windowSeconds)}s`;
}
export function inferWindowFamilyLabel(windowSeconds: number): string | undefined {
if (windowSeconds <= SESSION_MAX_SECONDS) return "Session";
if (windowSeconds <= WEEKLY_MAX_SECONDS) return "Weekly";
if (windowSeconds <= MONTHLY_MAX_SECONDS) return "Monthly";
return undefined;
}
function normalizeFamily(value: string): string {
return value.trim().toLowerCase();
}
/**
* Prefer a duration-aware label. When usage already stamped a generic family
* (`Weekly`) on a 30-day window, replace it with Monthly so AUTH and the
* usage API agree on the reset window.
*/
export function formatQuotaWindowLabel(input: QuotaWindowLabelInput): string {
const seconds = toPositiveSeconds(input.windowSeconds);
const duration = seconds != null ? formatWindowDuration(seconds) : null;
const inferred = seconds != null ? inferWindowFamilyLabel(seconds) : undefined;
const named = typeof input.displayName === "string" ? input.displayName.trim() : "";
const key = typeof input.key === "string" && input.key.trim() ? input.key.trim() : "quota";
let base = named || inferred || key;
if (inferred && named && GENERIC_FAMILY.has(normalizeFamily(named)) && inferred !== named) {
base = inferred;
}
if (duration && !base.toLowerCase().includes(duration.toLowerCase())) {
return `${base} (${duration})`;
}
return base;
}
export function formatQuotaUsageReason(
input: QuotaWindowLabelInput,
usedPercentage: number
): string {
return `${formatQuotaWindowLabel(input)} usage ${Math.round(usedPercentage)}%`;
}

View File

@@ -53,6 +53,8 @@ interface QuotaInfo {
// percentage; `false` means "unknown", so callers must not treat the
// defaulted-to-0 `remainingPercentage` as genuine exhaustion.
fractionReported?: boolean;
displayName?: string;
windowSeconds?: number | null;
}
interface QuotaCacheEntry {
@@ -70,6 +72,8 @@ interface QuotaWindowStatus {
usedPercentage: number;
resetAt: string | null;
reachedThreshold: boolean;
displayName?: string;
windowSeconds?: number | null;
}
export interface QuotaWindowObservation {
@@ -250,6 +254,12 @@ function normalizeQuotas(rawQuotas: Record<string, any>): Record<string, QuotaIn
const result: Record<string, QuotaInfo> = {};
for (const [key, q] of Object.entries(rawQuotas)) {
if (q && typeof q === "object") {
const windowSeconds =
typeof q.windowSeconds === "number" && Number.isFinite(q.windowSeconds)
? q.windowSeconds
: typeof q.window_seconds === "number" && Number.isFinite(q.window_seconds)
? q.window_seconds
: null;
result[key] = {
remainingPercentage:
safePercentage(q.remainingPercentage) ??
@@ -258,6 +268,10 @@ function normalizeQuotas(rawQuotas: Record<string, any>): Record<string, QuotaIn
// #10095 — thread through the "did upstream actually report this
// window's fraction" signal (see UsageQuota in usage/quota.ts).
fractionReported: q.fractionReported === false ? false : undefined,
...(typeof q.displayName === "string" && q.displayName.trim()
? { displayName: q.displayName.trim() }
: {}),
...(windowSeconds != null ? { windowSeconds } : {}),
};
}
}
@@ -642,6 +656,8 @@ export function getQuotaWindowStatus(
: remainingPercentage <= 0
? true
: usedPercentage >= thresholdPercent,
...(window.displayName ? { displayName: window.displayName } : {}),
...(window.windowSeconds != null ? { windowSeconds: window.windowSeconds } : {}),
};
}

View File

@@ -52,7 +52,10 @@ import {
getQuotaScopeLabelForProvider,
isAntigravityQuotaProvider,
} from "@omniroute/open-sse/services/antigravityQuotaFamily.ts";
import { rehydrateAntigravityFamilyLocksForConnections, persistAntigravityFamilyCooldownIfQuota } from "@omniroute/open-sse/services/antigravityFamilyCooldown.ts";
import {
rehydrateAntigravityFamilyLocksForConnections,
persistAntigravityFamilyCooldownIfQuota,
} from "@omniroute/open-sse/services/antigravityFamilyCooldown.ts";
import { markQuotaPreflightAccountUnavailable } from "./quotaPreflightUnavailable.ts";
import { getCreditsMode } from "@omniroute/open-sse/services/antigravityCredits.ts";
import { preferAntigravityConnectionsWithStoredProject } from "@omniroute/open-sse/services/antigravityProjectPersistence.ts";
@@ -112,6 +115,7 @@ import {
toCodexBaseQuotaWindowName,
toCodexScopedQuotaWindowName,
} from "@omniroute/open-sse/config/codexQuotaScopes.ts";
import { formatQuotaUsageReason } from "@omniroute/open-sse/services/quotaWindowLabel.ts";
import {
getCodexChildCooldown,
isCodexChildUnavailable,
@@ -380,7 +384,16 @@ export function evaluateQuotaLimitPolicy(
policy.thresholdPercent
);
if (!status?.reachedThreshold) continue;
reasons.push(`${effectiveWindowName} usage ${Math.round(status.usedPercentage)}%`);
reasons.push(
formatQuotaUsageReason(
{
key: effectiveWindowName,
displayName: status.displayName,
windowSeconds: status.windowSeconds,
},
status.usedPercentage
)
);
resetCandidates.push(status.resetAt);
}
@@ -2428,7 +2441,7 @@ export function isAgentrouterConnectionQuotaScope(
}
async function resolveDailyResetForProvider(
provider: string | null,
provider: string | null
): Promise<{ timezone?: unknown; hour?: unknown } | null> {
if (!provider) return null;
try {
@@ -2666,7 +2679,7 @@ export async function markAccountUnavailable(
effectiveProviderProfile,
null,
null,
await resolveDailyResetForProvider(provider),
await resolveDailyResetForProvider(provider)
);
// T-PROBE: probe-origin failures (model test-all) must never remove the
@@ -2920,7 +2933,13 @@ export async function markAccountUnavailable(
"AUTH",
`Model-only lockout for ${provider}:${model}${status} ${reason} ${Math.ceil(lockout.cooldownMs / 1000)}s (failureCount=${lockout.failureCount}, connection stays active)`
);
persistAntigravityFamilyCooldownIfQuota({ provider, connectionId, model, cooldownMs: lockout.cooldownMs, reason });
persistAntigravityFamilyCooldownIfQuota({
provider,
connectionId,
model,
cooldownMs: lockout.cooldownMs,
reason,
});
return { shouldFallback: true, cooldownMs: lockout.cooldownMs };
}
const result = fallbackResult;

View File

@@ -0,0 +1,101 @@
import test from "node:test";
import assert from "node:assert/strict";
import {
formatQuotaUsageReason,
formatQuotaWindowLabel,
} from "../../open-sse/services/quotaWindowLabel.ts";
import { collectQuotaWindowExclusions } from "../../open-sse/services/combo/quotaSkipDiagnostics.ts";
import { __clearForTests, setQuotaCache } from "../../src/domain/quotaCache.ts";
import { evaluateQuotaLimitPolicy } from "../../src/sse/services/auth.ts";
import { buildCodexUsageQuotas } from "../../open-sse/services/codexUsageQuotas.ts";
test("formatQuotaWindowLabel keeps the routing key when no duration metadata exists", () => {
assert.equal(formatQuotaWindowLabel({ key: "session" }), "session");
assert.equal(formatQuotaUsageReason({ key: "session" }, 100), "session usage 100%");
});
test("formatQuotaWindowLabel prefers Monthly for a 30-day window labeled Weekly", () => {
assert.equal(
formatQuotaWindowLabel({
key: "session",
displayName: "Weekly",
windowSeconds: 2_592_000,
}),
"Monthly (30d)"
);
});
test("formatQuotaWindowLabel keeps a genuine 5h session label", () => {
assert.equal(
formatQuotaWindowLabel({
key: "session",
displayName: "Session",
windowSeconds: 18_000,
}),
"Session (5h)"
);
});
test("buildCodexUsageQuotas labels a 30-day primary window Monthly, not Weekly", () => {
const { quotas } = buildCodexUsageQuotas({
rate_limit: {
primary_window: {
used_percent: 100,
limit_window_seconds: 2_592_000,
reset_after_seconds: 100_000,
},
},
});
assert.ok(quotas.session);
assert.equal(quotas.session.displayName, "Monthly");
assert.equal(quotas.session.windowSeconds, 2_592_000);
});
test("AUTH reasons match usage duration labels for a 30-day Codex session window", () => {
__clearForTests();
const connectionId = "quota-label-conn-30d";
setQuotaCache(connectionId, "codex", {
session: {
remainingPercentage: 0,
resetAt: new Date(Date.now() + 86_400_000).toISOString(),
displayName: "Weekly",
windowSeconds: 2_592_000,
},
});
const evaluation = evaluateQuotaLimitPolicy("codex", {
id: connectionId,
providerSpecificData: {
limitPolicy: { enabled: true, thresholdPercent: 99, windows: ["session"] },
codexLimitPolicy: { use5h: true, useWeekly: false },
},
} as never);
assert.equal(evaluation.blocked, true);
assert.deepEqual(evaluation.reasons, ["Monthly (30d) usage 100%"]);
});
test("collectQuotaWindowExclusions lists redacted-safe quota windows for 503 bodies", () => {
__clearForTests();
const connectionId = "abcd1234-ffff-4000-8000-000000000001";
setQuotaCache(connectionId, "codex", {
session: {
remainingPercentage: 0,
resetAt: new Date(Date.now() + 86_400_000).toISOString(),
displayName: "Weekly",
windowSeconds: 2_592_000,
},
});
const rows = collectQuotaWindowExclusions([
{ provider: "codex", modelStr: "gpt-5.6-sol", connectionId },
]);
assert.equal(rows.length, 1);
assert.equal(rows[0].provider, "codex");
assert.equal(rows[0].model, "gpt-5.6-sol");
assert.match(rows[0].reason, /quota:Monthly \(30d\) 100%/);
assert.match(rows[0].reason, /conn:abcd1234/);
assert.equal(rows[0].reason.includes(connectionId), false);
});