Skip to content

Commit e310824

Browse files
pashpashpashsteipete
authored andcommitted
fix(codex): explain account auth fallback
1 parent 3ae2799 commit e310824

6 files changed

Lines changed: 828 additions & 24 deletions

File tree

extensions/codex/src/app-server/rate-limits.ts

Lines changed: 91 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,16 @@ type RateLimitWindowEntry = {
1919
window: RateLimitReset;
2020
};
2121

22+
export type CodexAccountUsageSummary = {
23+
usageLine?: string;
24+
blocked: boolean;
25+
blockedUntilMs?: number;
26+
blockedUntilText?: string;
27+
blockedResetRelative?: string;
28+
blockingPeriod?: string;
29+
blockingReason?: string;
30+
};
31+
2232
export function formatCodexUsageLimitErrorMessage(params: {
2333
message?: string | null;
2434
codexErrorInfo?: JsonValue | null;
@@ -74,22 +84,20 @@ export function summarizeCodexAccountRateLimits(
7484
value: JsonValue | undefined,
7585
nowMs = Date.now(),
7686
): string[] | undefined {
77-
const snapshots = collectCodexRateLimitSnapshots(value);
78-
if (snapshots.length === 0) {
87+
const summary = summarizeCodexAccountUsage(value, nowMs);
88+
if (!summary) {
7989
return undefined;
8090
}
81-
const blockedSnapshots = snapshots.filter(snapshotHasLimitBlock);
82-
const blockingSnapshot =
83-
blockedSnapshots.find(isCodexLimitSnapshot) ?? blockedSnapshots[0] ?? undefined;
84-
if (!blockingSnapshot) {
91+
if (!summary.blocked) {
8592
return ["Codex is available."];
8693
}
87-
const blockingReset = selectSnapshotBlockingReset(blockingSnapshot, nowMs);
8894
return [
89-
blockingReset
90-
? `Codex is paused until ${formatAccountResetTime(blockingReset.resetsAtMs, nowMs)}.`
95+
summary.blockedUntilText
96+
? `Codex is paused until ${summary.blockedUntilText}.`
9197
: "Codex is paused by a usage limit.",
92-
formatBlockingLimitReason(blockingReset),
98+
summary.blockingReason
99+
? `Your ${summary.blockingReason}.`
100+
: "Your Codex usage limit is reached.",
93101
];
94102
}
95103

@@ -100,6 +108,44 @@ export function resolveCodexUsageLimitResetAtMs(
100108
return selectNextRateLimitReset(value, nowMs)?.resetsAtMs;
101109
}
102110

111+
export function summarizeCodexAccountUsage(
112+
value: JsonValue | undefined,
113+
nowMs = Date.now(),
114+
): CodexAccountUsageSummary | undefined {
115+
const snapshots = collectCodexRateLimitSnapshots(value);
116+
if (snapshots.length === 0) {
117+
return undefined;
118+
}
119+
const usageSnapshot = snapshots.find(isCodexLimitSnapshot) ?? snapshots[0];
120+
const blockedSnapshots = snapshots.filter(snapshotHasLimitBlock);
121+
const blockingSnapshot =
122+
blockedSnapshots.find(isCodexLimitSnapshot) ?? blockedSnapshots[0] ?? undefined;
123+
const blockingReset = blockingSnapshot
124+
? selectSnapshotBlockingReset(blockingSnapshot, nowMs)
125+
: undefined;
126+
const blockingPeriod = formatBlockingLimitPeriod(blockingReset?.windowDurationMins);
127+
const blockedUntilText = blockingReset
128+
? formatAccountResetTime(blockingReset.resetsAtMs, nowMs)
129+
: undefined;
130+
const blockedResetRelative = blockingReset
131+
? `in ${formatRelativeDuration(blockingReset.resetsAtMs - nowMs)}`
132+
: undefined;
133+
const blockingReason = blockingPeriod
134+
? `${blockingPeriod} Codex usage limit is reached`
135+
: blockingSnapshot
136+
? "Codex usage limit is reached"
137+
: undefined;
138+
return {
139+
usageLine: formatUsageLine(usageSnapshot),
140+
blocked: Boolean(blockingSnapshot),
141+
...(blockingReset ? { blockedUntilMs: blockingReset.resetsAtMs } : {}),
142+
...(blockedUntilText ? { blockedUntilText } : {}),
143+
...(blockedResetRelative ? { blockedResetRelative } : {}),
144+
...(blockingPeriod ? { blockingPeriod } : {}),
145+
...(blockingReason ? { blockingReason } : {}),
146+
};
147+
}
148+
103149
function isCodexUsageLimitError(
104150
codexErrorInfo: JsonValue | null | undefined,
105151
message: string | undefined,
@@ -348,13 +394,6 @@ function readWindowEntries(snapshot: JsonObject): RateLimitWindowEntry[] {
348394
});
349395
}
350396

351-
function formatBlockingLimitReason(window: RateLimitReset | undefined): string {
352-
const period = formatBlockingLimitPeriod(window?.windowDurationMins);
353-
return period
354-
? `Your ${period} Codex usage limit is reached.`
355-
: "Your Codex usage limit is reached.";
356-
}
357-
358397
function formatBlockingLimitPeriod(minutes: number | undefined): string | undefined {
359398
if (minutes === 7 * 24 * 60) {
360399
return "weekly";
@@ -368,6 +407,41 @@ function formatBlockingLimitPeriod(minutes: number | undefined): string | undefi
368407
return undefined;
369408
}
370409

410+
function formatUsageLine(snapshot: JsonObject): string | undefined {
411+
const windows = readWindowEntries(snapshot)
412+
.filter((entry) => entry.window.usedPercent !== undefined)
413+
.toSorted(
414+
(left, right) =>
415+
(right.window.windowDurationMins ?? 0) - (left.window.windowDurationMins ?? 0),
416+
)
417+
.map((entry) => {
418+
const label = formatUsageWindowLabel(entry.window.windowDurationMins);
419+
return `${label} ${Math.round(entry.window.usedPercent ?? 0)}%`;
420+
});
421+
return windows.length > 0 ? windows.join(" \u00b7 ") : undefined;
422+
}
423+
424+
function formatUsageWindowLabel(minutes: number | undefined): string {
425+
if (minutes === 7 * 24 * 60) {
426+
return "weekly";
427+
}
428+
if (minutes === 24 * 60) {
429+
return "daily";
430+
}
431+
if (minutes !== undefined && minutes > 0 && minutes < 24 * 60) {
432+
return "short-term";
433+
}
434+
if (minutes !== undefined && minutes > 0 && minutes % (24 * 60) === 0) {
435+
const days = minutes / (24 * 60);
436+
return `${days}-day`;
437+
}
438+
if (minutes !== undefined && minutes > 0 && minutes % 60 === 0) {
439+
const hours = minutes / 60;
440+
return `${hours}-hour`;
441+
}
442+
return "usage";
443+
}
444+
371445
function formatCalendarResetTime(resetsAtMs: number, nowMs: number): string {
372446
const resetDate = new Date(resetsAtMs);
373447
const resetParts = new Intl.DateTimeFormat("en-US", {

0 commit comments

Comments
 (0)