@@ -34,7 +34,13 @@ enum CLIRenderer {
3434 context: context,
3535 now: now,
3636 lines: & lines)
37- self . appendTertiaryLines ( snapshot: snapshot, labels: labels, context: context, now: now, lines: & lines)
37+ self . appendTertiaryLines (
38+ provider: provider,
39+ snapshot: snapshot,
40+ labels: labels,
41+ context: context,
42+ now: now,
43+ lines: & lines)
3844 self . appendMiMoBalanceLine ( snapshot: snapshot, useColor: context. useColor, lines: & lines)
3945 self . appendClawRouterUsageLines ( snapshot: snapshot, useColor: context. useColor, lines: & lines)
4046 self . appendSub2APIUsageLines ( snapshot: snapshot, useColor: context. useColor, lines: & lines)
@@ -102,7 +108,13 @@ enum CLIRenderer {
102108 context: context,
103109 now: now,
104110 lines: & lines)
105- self . appendTertiaryLines ( snapshot: snapshot, labels: labels, context: context, now: now, lines: & lines)
111+ self . appendTertiaryLines (
112+ provider: provider,
113+ snapshot: snapshot,
114+ labels: labels,
115+ context: context,
116+ now: now,
117+ lines: & lines)
106118 self . appendMiMoBalanceLine ( snapshot: snapshot, useColor: context. useColor, lines: & lines)
107119 self . appendClawRouterUsageLines ( snapshot: snapshot, useColor: context. useColor, lines: & lines)
108120 self . appendSub2APIUsageLines ( snapshot: snapshot, useColor: context. useColor, lines: & lines)
@@ -574,8 +586,16 @@ enum CLIRenderer {
574586 weeklyWorkDays: weeklyWorkDays,
575587 now: now)
576588 }
577- guard primary != nil || secondary != nil else { return nil }
578- return ProviderPacePayload ( primary: primary, secondary: secondary)
589+ let tertiary = snapshot. tertiary. flatMap {
590+ self . pacePayload (
591+ provider: provider,
592+ window: $0,
593+ kind: self . paceKind ( provider: provider, fallback: . weekly) ,
594+ weeklyWorkDays: weeklyWorkDays,
595+ now: now)
596+ }
597+ guard primary != nil || secondary != nil || tertiary != nil else { return nil }
598+ return ProviderPacePayload ( primary: primary, secondary: secondary, tertiary: tertiary)
579599 }
580600
581601 static func rateLine( title: String , window: RateWindow , useColor: Bool ) -> String {
@@ -765,16 +785,28 @@ enum CLIRenderer {
765785 }
766786 }
767787
788+ // swiftlint:disable:next function_parameter_count
768789 private static func appendTertiaryLines(
790+ provider: UsageProvider ,
769791 snapshot: UsageSnapshot ,
770792 labels: RateWindowLabels ,
771793 context: RenderContext ,
772794 now: Date ,
773795 lines: inout [ String ] )
774796 {
775- guard labels. showsTertiary, let opus = snapshot. tertiary else { return }
776- lines. append ( self . rateLine ( title: labels. tertiary, window: opus, useColor: context. useColor) )
777- if let reset = self . resetLine ( for: opus, style: context. resetStyle, now: now) {
797+ guard labels. showsTertiary, let tertiary = snapshot. tertiary else { return }
798+ lines. append ( self . rateLine ( title: labels. tertiary, window: tertiary, useColor: context. useColor) )
799+ if let pace = self . paceLine (
800+ provider: provider,
801+ window: tertiary,
802+ kind: self . paceKind ( provider: provider, fallback: . weekly) ,
803+ weeklyWorkDays: context. weeklyWorkDays,
804+ useColor: context. useColor,
805+ now: now)
806+ {
807+ lines. append ( pace)
808+ }
809+ if let reset = self . resetLine ( for: tertiary, style: context. resetStyle, now: now) {
778810 lines. append ( self . subtleLine ( reset, useColor: context. useColor) )
779811 }
780812 }
@@ -1098,8 +1130,9 @@ enum CLIRenderer {
10981130
10991131 /// .session mirrors the GUI's session pace (5h window, real session windows only); .weekly reads
11001132 /// weeklyProgressWorkDays from the GUI's UserDefaults (same key) and passes it to UsagePace.weekly,
1101- /// so the baseline matches the menu bar when the setting is configured. Codex historical refinement
1102- /// is not applied (fixed allowlist only), so it can still differ from the menu for Codex accounts.
1133+ /// so the baseline matches the menu bar when the setting is configured. Descriptor-backed reset windows
1134+ /// also use .weekly wording. Codex historical refinement is not applied, so it can still differ from the
1135+ /// menu for Codex accounts.
11031136 private enum PaceKind {
11041137 case session
11051138 case weekly
@@ -1111,7 +1144,7 @@ enum CLIRenderer {
11111144 }
11121145 }
11131146
1114- func supports ( provider: UsageProvider ) -> Bool {
1147+ func supportsStandardPace ( provider: UsageProvider ) -> Bool {
11151148 switch self {
11161149 case . session:
11171150 provider == . codex || provider == . claude || provider == . ollama || provider == . kimi
@@ -1122,6 +1155,11 @@ enum CLIRenderer {
11221155 }
11231156 }
11241157
1158+ private struct PaceComputation {
1159+ let pace : UsagePace
1160+ let kind : PaceKind
1161+ }
1162+
11251163 private static func paceKind( provider: UsageProvider , fallback: PaceKind ) -> PaceKind {
11261164 guard provider == . kimi else { return fallback }
11271165 // Kimi stores weekly in primary and its rate limit in secondary, opposite the legacy CLI slot convention.
@@ -1138,36 +1176,45 @@ enum CLIRenderer {
11381176 window: RateWindow ,
11391177 kind: PaceKind ,
11401178 weeklyWorkDays: Int ? = nil ,
1141- now: Date ) -> UsagePace ?
1179+ now: Date ) -> PaceComputation ?
11421180 {
1143- guard kind. supports ( provider: provider) else { return nil }
1181+ let capability = ProviderDescriptorRegistry . descriptor ( for: provider) . pace
1182+ let paceWindow : RateWindow
1183+ let resolvedKind : PaceKind
1184+ if capability. supportsResetWindowPace ( window: window, now: now) {
1185+ paceWindow = capability. resolvedResetWindowForPace ( window)
1186+ resolvedKind = . weekly
1187+ } else {
1188+ guard kind. supportsStandardPace ( provider: provider) else { return nil }
1189+ paceWindow = window
1190+ resolvedKind = kind
1191+ }
11441192 if provider == . kimi {
1145- let supportsWindow = switch kind {
1193+ let supportsWindow = switch resolvedKind {
11461194 case . session:
1147- window . windowMinutes == KimiProviderDescriptor . sessionWindowMinutes
1195+ paceWindow . windowMinutes == KimiProviderDescriptor . sessionWindowMinutes
11481196 case . weekly:
1149- ProviderDescriptorRegistry . descriptor ( for: provider) . pace
1150- . supportsResetWindowPace ( window: window, now: now)
1197+ capability. supportsResetWindowPace ( window: paceWindow, now: now)
11511198 }
11521199 guard supportsWindow else { return nil }
11531200 }
11541201 // Only pace a real session window here; Claude w/o 5-hour data falls a 7-day window into primary.
1155- if case . session = kind , let minutes = window . windowMinutes, minutes > 300 {
1202+ if case . session = resolvedKind , let minutes = paceWindow . windowMinutes, minutes > 300 {
11561203 return nil
11571204 }
1158- if provider == . ollama, window . windowMinutes == nil {
1205+ if provider == . ollama, paceWindow . windowMinutes == nil {
11591206 return nil
11601207 }
1161- guard window . remainingPercent > 0 else { return nil }
1208+ guard paceWindow . remainingPercent > 0 else { return nil }
11621209 // workDays applies only to the weekly (10 080-min) window; UsagePace.weekly ignores it for other durations.
1163- let workDays = kind == . weekly ? weeklyWorkDays : nil
1210+ let workDays = resolvedKind == . weekly ? weeklyWorkDays : nil
11641211 guard let pace = UsagePace . weekly (
1165- window: window ,
1212+ window: paceWindow ,
11661213 now: now,
1167- defaultWindowMinutes: kind . defaultWindowMinutes,
1214+ defaultWindowMinutes: resolvedKind . defaultWindowMinutes,
11681215 workDays: workDays) else { return nil }
11691216 guard pace. expectedUsedPercent >= Self . paceMinimumExpectedPercent else { return nil }
1170- return pace
1217+ return PaceComputation ( pace: pace , kind : resolvedKind )
11711218 }
11721219
11731220 private static func paceSummary(
@@ -1194,14 +1241,19 @@ enum CLIRenderer {
11941241 useColor: Bool ,
11951242 now: Date ) -> String ?
11961243 {
1197- guard let pace = self . computePace (
1244+ guard let computation = self . computePace (
11981245 provider: provider,
11991246 window: window,
12001247 kind: kind,
12011248 weeklyWorkDays: weeklyWorkDays,
12021249 now: now) else { return nil }
12031250 let label = self . label ( " Pace " , useColor: useColor)
1204- return " \( label) : \( self . paceSummary ( provider: provider, for: pace, kind: kind, now: now) ) "
1251+ let summary = self . paceSummary (
1252+ provider: provider,
1253+ for: computation. pace,
1254+ kind: computation. kind,
1255+ now: now)
1256+ return " \( label) : \( summary) "
12051257 }
12061258
12071259 private static func pacePayload(
@@ -1211,20 +1263,24 @@ enum CLIRenderer {
12111263 weeklyWorkDays: Int ? = nil ,
12121264 now: Date ) -> PacePayload ?
12131265 {
1214- guard let pace = self . computePace (
1266+ guard let computation = self . computePace (
12151267 provider: provider,
12161268 window: window,
12171269 kind: kind,
12181270 weeklyWorkDays: weeklyWorkDays,
12191271 now: now) else { return nil }
12201272 return PacePayload (
1221- stage: Self . stageString ( pace. stage) ,
1222- deltaPercent: pace. deltaPercent. rounded ( ) ,
1223- expectedUsedPercent: pace. expectedUsedPercent. rounded ( ) ,
1224- willLastToReset: pace. willLastToReset,
1225- etaSeconds: pace. etaSeconds. map { $0. rounded ( ) } ,
1226- runOutProbability: pace. runOutProbability,
1227- summary: self . paceSummary ( provider: provider, for: pace, kind: kind, now: now) )
1273+ stage: Self . stageString ( computation. pace. stage) ,
1274+ deltaPercent: computation. pace. deltaPercent. rounded ( ) ,
1275+ expectedUsedPercent: computation. pace. expectedUsedPercent. rounded ( ) ,
1276+ willLastToReset: computation. pace. willLastToReset,
1277+ etaSeconds: computation. pace. etaSeconds. map { $0. rounded ( ) } ,
1278+ runOutProbability: computation. pace. runOutProbability,
1279+ summary: self . paceSummary (
1280+ provider: provider,
1281+ for: computation. pace,
1282+ kind: computation. kind,
1283+ now: now) )
12281284 }
12291285
12301286 private static func stageString( _ stage: UsagePace . Stage ) -> String {
0 commit comments