@@ -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+
2232export 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+
103149function 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-
358397function 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+
371445function formatCalendarResetTime ( resetsAtMs : number , nowMs : number ) : string {
372446 const resetDate = new Date ( resetsAtMs ) ;
373447 const resetParts = new Intl . DateTimeFormat ( "en-US" , {
0 commit comments