import Foundation public struct RateWindow: Codable, Equatable, Sendable { public let usedPercent: Double public let windowMinutes: Int? public let resetsAt: Date? /// Optional textual reset description (used by Claude CLI UI scrape). public let resetDescription: String? /// Optional percent restored on the next regeneration tick for providers with rolling recovery. public let nextRegenPercent: Double? /// Whether this window was synthesized to stand in for a quota lane the provider did not actually /// report, rather than being a real zero-usage window. /// /// Claude web returns a `0%` five-hour window when `five_hour` is `null` (an account with no live /// session but a real weekly lane). Lane classifiers — e.g. the combined "Session + Weekly" menu-bar /// metric — must treat such a window as "no session lane present" instead of surfacing a phantom /// `5h 0%`/`5h 100%` session. A genuine session, even one freshly reset to 0%, is NOT a placeholder. /// Missing values decode as `false` for older cached payloads. public let isSyntheticPlaceholder: Bool public init( usedPercent: Double, windowMinutes: Int?, resetsAt: Date?, resetDescription: String?, nextRegenPercent: Double? = nil, isSyntheticPlaceholder: Bool = false) { self.usedPercent = usedPercent self.windowMinutes = windowMinutes self.resetsAt = resetsAt self.resetDescription = resetDescription self.nextRegenPercent = nextRegenPercent self.isSyntheticPlaceholder = isSyntheticPlaceholder } private enum CodingKeys: String, CodingKey { case usedPercent case windowMinutes case resetsAt case resetDescription case nextRegenPercent case isSyntheticPlaceholder } public init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) self.usedPercent = try container.decode(Double.self, forKey: .usedPercent) self.windowMinutes = try container.decodeIfPresent(Int.self, forKey: .windowMinutes) self.resetsAt = try container.decodeIfPresent(Date.self, forKey: .resetsAt) self.resetDescription = try container.decodeIfPresent(String.self, forKey: .resetDescription) self.nextRegenPercent = try container.decodeIfPresent(Double.self, forKey: .nextRegenPercent) self.isSyntheticPlaceholder = try container.decodeIfPresent(Bool.self, forKey: .isSyntheticPlaceholder) ?? false } public func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(self.usedPercent, forKey: .usedPercent) try container.encodeIfPresent(self.windowMinutes, forKey: .windowMinutes) try container.encodeIfPresent(self.resetsAt, forKey: .resetsAt) try container.encodeIfPresent(self.resetDescription, forKey: .resetDescription) try container.encodeIfPresent(self.nextRegenPercent, forKey: .nextRegenPercent) // Only persist the flag when set, keeping payloads identical for the common (real-window) case. if self.isSyntheticPlaceholder { try container.encode(true, forKey: .isSyntheticPlaceholder) } } public var remainingPercent: Double { max(0, 100 - self.usedPercent) } public func backfillingResetTime(from cached: RateWindow?, now: Date = .init()) -> RateWindow { if self.resetsAt != nil { return self } guard let cachedReset = cached?.resetsAt, cachedReset > now else { return self } let windowMinutes = if let windowMinutes = self.windowMinutes, windowMinutes > 0 { windowMinutes } else { cached?.windowMinutes } return RateWindow( usedPercent: self.usedPercent, windowMinutes: windowMinutes, resetsAt: cachedReset, resetDescription: self.resetDescription ?? cached?.resetDescription, nextRegenPercent: self.nextRegenPercent, // Preserve the placeholder marker: backfilling a stale reset onto Claude web's null-session // placeholder must not let it masquerade as a real session lane. isSyntheticPlaceholder: self.isSyntheticPlaceholder) }} public struct NamedRateWindow: Codable, Equatable, Sendable { public let id: String public let title: String public let window: RateWindow /// Whether `window.usedPercent` reflects known quota usage. /// /// Some providers expose reset metadata for a named quota window before /// they expose remaining usage. Keep those windows visible for reset/debug /// context, but mark them so clients do not render `usedPercent` as a real /// exhausted quota. Missing values decode as `true` for older cached payloads. public let usageKnown: Bool public init(id: String, title: String, window: RateWindow, usageKnown: Bool = true) { self.id = id self.title = title self.window = window self.usageKnown = usageKnown } private enum CodingKeys: String, CodingKey { case id case title case window case usageKnown } public init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) self.id = try container.decode(String.self, forKey: .id) self.title = try container.decode(String.self, forKey: .title) self.window = try container.decode(RateWindow.self, forKey: .window) self.usageKnown = try container.decodeIfPresent(Bool.self, forKey: .usageKnown) ?? true } public func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(self.id, forKey: .id) try container.encode(self.title, forKey: .title) try container.encode(self.window, forKey: .window) if !self.usageKnown { try container.encode(false, forKey: .usageKnown) } }} public struct ProviderIdentitySnapshot: Codable, Sendable { public let providerID: UsageProvider? public let accountEmail: String? public let accountOrganization: String? public let loginMethod: String? public let accountID: String? public init( providerID: UsageProvider?, accountEmail: String?, accountOrganization: String?, loginMethod: String?, accountID: String? = nil) { self.providerID = providerID self.accountEmail = accountEmail self.accountOrganization = accountOrganization self.loginMethod = loginMethod self.accountID = accountID } public func scoped(to provider: UsageProvider) -> ProviderIdentitySnapshot { if self.providerID == provider { return self } return ProviderIdentitySnapshot( providerID: provider, accountEmail: self.accountEmail, accountOrganization: self.accountOrganization, loginMethod: self.loginMethod, accountID: self.accountID) }} public enum UsageDataConfidence: String, Codable, Equatable, Sendable { case exact case estimated case percentOnly case unknown} public struct UsageSnapshot: Codable, Sendable { public let primary: RateWindow? public let secondary: RateWindow? public let tertiary: RateWindow? public let extraRateWindows: [NamedRateWindow]? public let providerCost: ProviderCostSnapshot? public let kiroUsage: KiroUsageDetails? public let ampUsage: AmpUsageDetails? public let zaiUsage: ZaiUsageSnapshot? public let minimaxUsage: MiniMaxUsageSnapshot? public let deepseekUsage: DeepSeekUsageSummary? public let deepseekDetailedUsageState: DeepSeekDetailedUsageState public let deepseekPlatformProfiles: [DeepSeekPlatformProfile] public let opencodegoUsage: OpenCodeGoUsageSnapshot? public let mimoUsage: MiMoUsageSnapshot? public let openRouterUsage: OpenRouterUsageSnapshot? public let sakanaPayAsYouGo: SakanaPayAsYouGoSnapshot? public let clawRouterUsage: ClawRouterUsageSnapshot? public let sub2APIUsage: Sub2APIUsageDetails? public let wayfinderUsage: WayfinderUsageSnapshot? public let openAIAPIUsage: OpenAIAPIUsageSnapshot? public let groqConsoleUsage: GroqConsoleUsageSnapshot? public let codexResetCredits: CodexRateLimitResetCreditsSnapshot? public let claudeAdminAPIUsage: ClaudeAdminAPIUsageSnapshot? public let mistralUsage: MistralUsageSnapshot? public let deepgramUsage: DeepgramUsageSnapshot? public let poeUsage: PoeUsageHistorySnapshot? public let cursorRequests: CursorRequestUsage? /// Live-only marker for optional Command Code subscription lookup failure. public let commandCodeSubscriptionEnrichmentUnavailable: Bool /// Live-only marker that Command Code returned a recognized subscription plan. public let commandCodeHasSubscriptionPlan: Bool /// Live-only marker that Command Code's monthly grant has no remaining credits. public let commandCodeMonthlyGrantDepleted: Bool public let subscriptionExpiresAt: Date? public let subscriptionRenewsAt: Date? public let updatedAt: Date public let identity: ProviderIdentitySnapshot? public let dataConfidence: UsageDataConfidence private enum CodingKeys: String, CodingKey { case primary case secondary case tertiary case extraRateWindows case providerCost case kiroUsage case ampUsage case mimoUsage case openRouterUsage case sakanaPayAsYouGo case clawRouterUsage case sub2APIUsage case wayfinderUsage case openAIAPIUsage case groqConsoleUsage case codexResetCredits case claudeAdminAPIUsage case mistralUsage case deepgramUsage case poeUsage case subscriptionExpiresAt case subscriptionRenewsAt case updatedAt case identity case dataConfidence case accountEmail case accountOrganization case loginMethod } public init( primary: RateWindow?, secondary: RateWindow?, tertiary: RateWindow? = nil, extraRateWindows: [NamedRateWindow]? = nil, kiroUsage: KiroUsageDetails? = nil, ampUsage: AmpUsageDetails? = nil, providerCost: ProviderCostSnapshot? = nil, zaiUsage: ZaiUsageSnapshot? = nil, minimaxUsage: MiniMaxUsageSnapshot? = nil, deepseekUsage: DeepSeekUsageSummary? = nil, deepseekDetailedUsageState: DeepSeekDetailedUsageState = .notRequested, deepseekPlatformProfiles: [DeepSeekPlatformProfile] = [], opencodegoUsage: OpenCodeGoUsageSnapshot? = nil, mimoUsage: MiMoUsageSnapshot? = nil, openRouterUsage: OpenRouterUsageSnapshot? = nil, sakanaPayAsYouGo: SakanaPayAsYouGoSnapshot? = nil, clawRouterUsage: ClawRouterUsageSnapshot? = nil, sub2APIUsage: Sub2APIUsageDetails? = nil, wayfinderUsage: WayfinderUsageSnapshot? = nil, openAIAPIUsage: OpenAIAPIUsageSnapshot? = nil, groqConsoleUsage: GroqConsoleUsageSnapshot? = nil, codexResetCredits: CodexRateLimitResetCreditsSnapshot? = nil, claudeAdminAPIUsage: ClaudeAdminAPIUsageSnapshot? = nil, mistralUsage: MistralUsageSnapshot? = nil, deepgramUsage: DeepgramUsageSnapshot? = nil, poeUsage: PoeUsageHistorySnapshot? = nil, cursorRequests: CursorRequestUsage? = nil, commandCodeSubscriptionEnrichmentUnavailable: Bool = false, commandCodeHasSubscriptionPlan: Bool = false, commandCodeMonthlyGrantDepleted: Bool = false, subscriptionExpiresAt: Date? = nil, subscriptionRenewsAt: Date? = nil, updatedAt: Date, identity: ProviderIdentitySnapshot? = nil, dataConfidence: UsageDataConfidence = .unknown) { self.primary = primary self.secondary = secondary self.tertiary = tertiary self.extraRateWindows = extraRateWindows self.kiroUsage = kiroUsage self.ampUsage = ampUsage self.providerCost = providerCost self.zaiUsage = zaiUsage self.minimaxUsage = minimaxUsage self.deepseekUsage = deepseekUsage self.deepseekDetailedUsageState = deepseekDetailedUsageState self.deepseekPlatformProfiles = deepseekPlatformProfiles self.opencodegoUsage = opencodegoUsage self.mimoUsage = mimoUsage self.openRouterUsage = openRouterUsage self.sakanaPayAsYouGo = sakanaPayAsYouGo self.clawRouterUsage = clawRouterUsage self.sub2APIUsage = sub2APIUsage self.wayfinderUsage = wayfinderUsage self.openAIAPIUsage = openAIAPIUsage self.groqConsoleUsage = groqConsoleUsage self.codexResetCredits = codexResetCredits self.claudeAdminAPIUsage = claudeAdminAPIUsage self.mistralUsage = mistralUsage self.deepgramUsage = deepgramUsage self.poeUsage = poeUsage self.cursorRequests = cursorRequests self.commandCodeSubscriptionEnrichmentUnavailable = commandCodeSubscriptionEnrichmentUnavailable self.commandCodeHasSubscriptionPlan = commandCodeHasSubscriptionPlan self.commandCodeMonthlyGrantDepleted = commandCodeMonthlyGrantDepleted self.subscriptionExpiresAt = subscriptionExpiresAt self.subscriptionRenewsAt = subscriptionRenewsAt self.updatedAt = updatedAt self.identity = identity self.dataConfidence = dataConfidence } public func with(extraRateWindows: [NamedRateWindow]?) -> UsageSnapshot { self.replacing(extraRateWindows: .value(extraRateWindows)) } public func withCodexResetCredits(_ resetCredits: CodexRateLimitResetCreditsSnapshot?) -> UsageSnapshot { self.replacing(codexResetCredits: .value(resetCredits)) } public func with(primary: RateWindow?, secondary: RateWindow?) -> UsageSnapshot { self.replacing( primary: .value(primary), secondary: .value(secondary)) } public init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) self.primary = try container.decodeIfPresent(RateWindow.self, forKey: .primary) self.secondary = try container.decodeIfPresent(RateWindow.self, forKey: .secondary) self.tertiary = try container.decodeIfPresent(RateWindow.self, forKey: .tertiary) self.extraRateWindows = try container.decodeIfPresent([NamedRateWindow].self, forKey: .extraRateWindows) self.providerCost = try container.decodeIfPresent(ProviderCostSnapshot.self, forKey: .providerCost) self.kiroUsage = try container.decodeIfPresent(KiroUsageDetails.self, forKey: .kiroUsage) self.ampUsage = try container.decodeIfPresent(AmpUsageDetails.self, forKey: .ampUsage) self.zaiUsage = nil // Not persisted, fetched fresh each time self.minimaxUsage = nil // Not persisted, fetched fresh each time self.deepseekUsage = nil // Not persisted, fetched fresh each time self.deepseekDetailedUsageState = .notRequested // Live-only fetch state self.deepseekPlatformProfiles = [] // Live-only browser profile catalog self.opencodegoUsage = nil // Not persisted, fetched fresh each time self.mimoUsage = try container.decodeIfPresent(MiMoUsageSnapshot.self, forKey: .mimoUsage) self.openRouterUsage = try container.decodeIfPresent(OpenRouterUsageSnapshot.self, forKey: .openRouterUsage) self.sakanaPayAsYouGo = try container.decodeIfPresent( SakanaPayAsYouGoSnapshot.self, forKey: .sakanaPayAsYouGo) self.clawRouterUsage = try container.decodeIfPresent(ClawRouterUsageSnapshot.self, forKey: .clawRouterUsage) self.sub2APIUsage = try container.decodeIfPresent(Sub2APIUsageDetails.self, forKey: .sub2APIUsage) self.wayfinderUsage = try container.decodeIfPresent(WayfinderUsageSnapshot.self, forKey: .wayfinderUsage) self.openAIAPIUsage = try container.decodeIfPresent(OpenAIAPIUsageSnapshot.self, forKey: .openAIAPIUsage) self.groqConsoleUsage = try container.decodeIfPresent( GroqConsoleUsageSnapshot.self, forKey: .groqConsoleUsage) self.codexResetCredits = try container.decodeIfPresent( CodexRateLimitResetCreditsSnapshot.self, forKey: .codexResetCredits) self.claudeAdminAPIUsage = try container.decodeIfPresent( ClaudeAdminAPIUsageSnapshot.self, forKey: .claudeAdminAPIUsage) self.mistralUsage = try container.decodeIfPresent(MistralUsageSnapshot.self, forKey: .mistralUsage) self.deepgramUsage = try container.decodeIfPresent(DeepgramUsageSnapshot.self, forKey: .deepgramUsage) self.poeUsage = try container.decodeIfPresent(PoeUsageHistorySnapshot.self, forKey: .poeUsage) self.cursorRequests = nil // Not persisted, fetched fresh each time self.commandCodeSubscriptionEnrichmentUnavailable = false // Live-only fetch state self.commandCodeHasSubscriptionPlan = false // Live-only fetch state self.commandCodeMonthlyGrantDepleted = false // Live-only fetch state self.subscriptionExpiresAt = try container.decodeIfPresent(Date.self, forKey: .subscriptionExpiresAt) self.subscriptionRenewsAt = try container.decodeIfPresent(Date.self, forKey: .subscriptionRenewsAt) self.updatedAt = try container.decode(Date.self, forKey: .updatedAt) if let dataConfidence = try container.decodeIfPresent(String.self, forKey: .dataConfidence) { self.dataConfidence = UsageDataConfidence(rawValue: dataConfidence) ?? .unknown } else { self.dataConfidence = .unknown } if let identity = try container.decodeIfPresent(ProviderIdentitySnapshot.self, forKey: .identity) { self.identity = identity } else { let email = try container.decodeIfPresent(String.self, forKey: .accountEmail) let organization = try container.decodeIfPresent(String.self, forKey: .accountOrganization) let loginMethod = try container.decodeIfPresent(String.self, forKey: .loginMethod) if email != nil || organization != nil || loginMethod != nil { self.identity = ProviderIdentitySnapshot( providerID: nil, accountEmail: email, accountOrganization: organization, loginMethod: loginMethod) } else { self.identity = nil } } } public func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) // Stable JSON schema: keep window keys present (encode `nil` as `null`). try container.encode(self.primary, forKey: .primary) try container.encode(self.secondary, forKey: .secondary) try container.encode(self.tertiary, forKey: .tertiary) try container.encodeIfPresent(self.extraRateWindows, forKey: .extraRateWindows) try container.encodeIfPresent(self.providerCost, forKey: .providerCost) try container.encodeIfPresent(self.kiroUsage, forKey: .kiroUsage) try container.encodeIfPresent(self.ampUsage, forKey: .ampUsage) try container.encodeIfPresent(self.mimoUsage, forKey: .mimoUsage) try container.encodeIfPresent(self.openRouterUsage, forKey: .openRouterUsage) try container.encodeIfPresent(self.sakanaPayAsYouGo, forKey: .sakanaPayAsYouGo) try container.encodeIfPresent(self.clawRouterUsage, forKey: .clawRouterUsage) try container.encodeIfPresent(self.sub2APIUsage, forKey: .sub2APIUsage) try container.encodeIfPresent(self.wayfinderUsage, forKey: .wayfinderUsage) try container.encodeIfPresent(self.openAIAPIUsage, forKey: .openAIAPIUsage) try container.encodeIfPresent(self.groqConsoleUsage, forKey: .groqConsoleUsage) try container.encodeIfPresent(self.codexResetCredits, forKey: .codexResetCredits) try container.encodeIfPresent(self.claudeAdminAPIUsage, forKey: .claudeAdminAPIUsage) try container.encodeIfPresent(self.mistralUsage, forKey: .mistralUsage) try container.encodeIfPresent(self.deepgramUsage, forKey: .deepgramUsage) try container.encodeIfPresent(self.poeUsage, forKey: .poeUsage) try container.encodeIfPresent(self.subscriptionExpiresAt, forKey: .subscriptionExpiresAt) try container.encodeIfPresent(self.subscriptionRenewsAt, forKey: .subscriptionRenewsAt) try container.encode(self.updatedAt, forKey: .updatedAt) try container.encodeIfPresent(self.identity, forKey: .identity) if self.dataConfidence != .unknown { try container.encode(self.dataConfidence, forKey: .dataConfidence) } try container.encodeIfPresent(self.identity?.accountEmail, forKey: .accountEmail) try container.encodeIfPresent(self.identity?.accountOrganization, forKey: .accountOrganization) try container.encodeIfPresent(self.identity?.loginMethod, forKey: .loginMethod) } public func identity(for provider: UsageProvider) -> ProviderIdentitySnapshot? { guard let identity, identity.providerID == provider else { return nil } return identity } public func automaticPerplexityWindow() -> RateWindow? { let fallbackWindows = self.orderedPerplexityFallbackWindows() guard let primary = self.primary else { return fallbackWindows.first } if primary.remainingPercent > 0 || fallbackWindows.isEmpty { return primary } return fallbackWindows.first } public func orderedPerplexityDisplayWindows() -> [RateWindow] { let fallbackWindows = self.orderedPerplexityFallbackWindows() guard let primary = self.primary else { return fallbackWindows } if primary.remainingPercent > 0 || fallbackWindows.isEmpty { return [primary] + fallbackWindows } return fallbackWindows + [primary] } public func switcherWeeklyWindow(for provider: UsageProvider, showUsed: Bool) -> RateWindow? { switch provider { case .factory: // Factory prefers secondary window return self.secondary ?? self.primary case .perplexity: return self.automaticPerplexityWindow() case .cursor: // Cursor: fall back to on-demand budget when the included plan is exhausted (only in // "show remaining" mode). The secondary/tertiary lanes are Total/Auto/API breakdowns, // not extra capacity, so they should not replace the remaining paid quota indicator. if !showUsed, let primary = self.primary, primary.remainingPercent <= 0, let providerCost = self.providerCost, providerCost.limit > 0 { let usedPercent = max(0, min(100, (providerCost.used / providerCost.limit) * 100)) return RateWindow( usedPercent: usedPercent, windowMinutes: nil, resetsAt: providerCost.resetsAt, resetDescription: nil) } return self.primary ?? self.secondary default: return self.primary ?? self.secondary } } public func accountEmail(for provider: UsageProvider) -> String? { self.identity(for: provider)?.accountEmailLines 1–500 of 1679. Load more
import Foundation public struct RateWindow: Codable, Equatable, Sendable { public let usedPercent: Double public let windowMinutes: Int? public let resetsAt: Date? /// Optional textual reset description (used by Claude CLI UI scrape). public let resetDescription: String? /// Optional percent restored on the next regeneration tick for providers with rolling recovery. public let nextRegenPercent: Double? /// Whether this window was synthesized to stand in for a quota lane the provider did not actually /// report, rather than being a real zero-usage window. /// /// Claude web returns a `0%` five-hour window when `five_hour` is `null` (an account with no live /// session but a real weekly lane). Lane classifiers — e.g. the combined "Session + Weekly" menu-bar /// metric — must treat such a window as "no session lane present" instead of surfacing a phantom /// `5h 0%`/`5h 100%` session. A genuine session, even one freshly reset to 0%, is NOT a placeholder. /// Missing values decode as `false` for older cached payloads. public let isSyntheticPlaceholder: Bool public init( usedPercent: Double, windowMinutes: Int?, resetsAt: Date?, resetDescription: String?, nextRegenPercent: Double? = nil, isSyntheticPlaceholder: Bool = false) { self.usedPercent = usedPercent self.windowMinutes = windowMinutes self.resetsAt = resetsAt self.resetDescription = resetDescription self.nextRegenPercent = nextRegenPercent self.isSyntheticPlaceholder = isSyntheticPlaceholder } private enum CodingKeys: String, CodingKey { case usedPercent case windowMinutes case resetsAt case resetDescription case nextRegenPercent case isSyntheticPlaceholder } public init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) self.usedPercent = try container.decode(Double.self, forKey: .usedPercent) self.windowMinutes = try container.decodeIfPresent(Int.self, forKey: .windowMinutes) self.resetsAt = try container.decodeIfPresent(Date.self, forKey: .resetsAt) self.resetDescription = try container.decodeIfPresent(String.self, forKey: .resetDescription) self.nextRegenPercent = try container.decodeIfPresent(Double.self, forKey: .nextRegenPercent) self.isSyntheticPlaceholder = try container.decodeIfPresent(Bool.self, forKey: .isSyntheticPlaceholder) ?? false } public func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(self.usedPercent, forKey: .usedPercent) try container.encodeIfPresent(self.windowMinutes, forKey: .windowMinutes) try container.encodeIfPresent(self.resetsAt, forKey: .resetsAt) try container.encodeIfPresent(self.resetDescription, forKey: .resetDescription) try container.encodeIfPresent(self.nextRegenPercent, forKey: .nextRegenPercent) // Only persist the flag when set, keeping payloads identical for the common (real-window) case. if self.isSyntheticPlaceholder { try container.encode(true, forKey: .isSyntheticPlaceholder) } } public var remainingPercent: Double { max(0, 100 - self.usedPercent) } public func backfillingResetTime(from cached: RateWindow?, now: Date = .init()) -> RateWindow { if self.resetsAt != nil { return self } guard let cachedReset = cached?.resetsAt, cachedReset > now else { return self } let windowMinutes = if let windowMinutes = self.windowMinutes, windowMinutes > 0 { windowMinutes } else { cached?.windowMinutes } return RateWindow( usedPercent: self.usedPercent, windowMinutes: windowMinutes, resetsAt: cachedReset, resetDescription: self.resetDescription ?? cached?.resetDescription, nextRegenPercent: self.nextRegenPercent, // Preserve the placeholder marker: backfilling a stale reset onto Claude web's null-session // placeholder must not let it masquerade as a real session lane. isSyntheticPlaceholder: self.isSyntheticPlaceholder) } } public struct NamedRateWindow: Codable, Equatable, Sendable { public let id: String public let title: String public let window: RateWindow /// Whether `window.usedPercent` reflects known quota usage. /// /// Some providers expose reset metadata for a named quota window before /// they expose remaining usage. Keep those windows visible for reset/debug /// context, but mark them so clients do not render `usedPercent` as a real /// exhausted quota. Missing values decode as `true` for older cached payloads. public let usageKnown: Bool public init(id: String, title: String, window: RateWindow, usageKnown: Bool = true) { self.id = id self.title = title self.window = window self.usageKnown = usageKnown } private enum CodingKeys: String, CodingKey { case id case title case window case usageKnown } public init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) self.id = try container.decode(String.self, forKey: .id) self.title = try container.decode(String.self, forKey: .title) self.window = try container.decode(RateWindow.self, forKey: .window) self.usageKnown = try container.decodeIfPresent(Bool.self, forKey: .usageKnown) ?? true } public func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(self.id, forKey: .id) try container.encode(self.title, forKey: .title) try container.encode(self.window, forKey: .window) if !self.usageKnown { try container.encode(false, forKey: .usageKnown) } } } public struct ProviderIdentitySnapshot: Codable, Sendable { public let providerID: UsageProvider? public let accountEmail: String? public let accountOrganization: String? public let loginMethod: String? public let accountID: String? public init( providerID: UsageProvider?, accountEmail: String?, accountOrganization: String?, loginMethod: String?, accountID: String? = nil) { self.providerID = providerID self.accountEmail = accountEmail self.accountOrganization = accountOrganization self.loginMethod = loginMethod self.accountID = accountID } public func scoped(to provider: UsageProvider) -> ProviderIdentitySnapshot { if self.providerID == provider { return self } return ProviderIdentitySnapshot( providerID: provider, accountEmail: self.accountEmail, accountOrganization: self.accountOrganization, loginMethod: self.loginMethod, accountID: self.accountID) } } public enum UsageDataConfidence: String, Codable, Equatable, Sendable { case exact case estimated case percentOnly case unknown } public struct UsageSnapshot: Codable, Sendable { public let primary: RateWindow? public let secondary: RateWindow? public let tertiary: RateWindow? public let extraRateWindows: [NamedRateWindow]? public let providerCost: ProviderCostSnapshot? public let kiroUsage: KiroUsageDetails? public let ampUsage: AmpUsageDetails? public let zaiUsage: ZaiUsageSnapshot? public let minimaxUsage: MiniMaxUsageSnapshot? public let deepseekUsage: DeepSeekUsageSummary? public let deepseekDetailedUsageState: DeepSeekDetailedUsageState public let deepseekPlatformProfiles: [DeepSeekPlatformProfile] public let opencodegoUsage: OpenCodeGoUsageSnapshot? public let mimoUsage: MiMoUsageSnapshot? public let openRouterUsage: OpenRouterUsageSnapshot? public let sakanaPayAsYouGo: SakanaPayAsYouGoSnapshot? public let clawRouterUsage: ClawRouterUsageSnapshot? public let sub2APIUsage: Sub2APIUsageDetails? public let wayfinderUsage: WayfinderUsageSnapshot? public let openAIAPIUsage: OpenAIAPIUsageSnapshot? public let groqConsoleUsage: GroqConsoleUsageSnapshot? public let codexResetCredits: CodexRateLimitResetCreditsSnapshot? public let claudeAdminAPIUsage: ClaudeAdminAPIUsageSnapshot? public let mistralUsage: MistralUsageSnapshot? public let deepgramUsage: DeepgramUsageSnapshot? public let poeUsage: PoeUsageHistorySnapshot? public let cursorRequests: CursorRequestUsage? /// Live-only marker for optional Command Code subscription lookup failure. public let commandCodeSubscriptionEnrichmentUnavailable: Bool /// Live-only marker that Command Code returned a recognized subscription plan. public let commandCodeHasSubscriptionPlan: Bool /// Live-only marker that Command Code's monthly grant has no remaining credits. public let commandCodeMonthlyGrantDepleted: Bool public let subscriptionExpiresAt: Date? public let subscriptionRenewsAt: Date? public let updatedAt: Date public let identity: ProviderIdentitySnapshot? public let dataConfidence: UsageDataConfidence private enum CodingKeys: String, CodingKey { case primary case secondary case tertiary case extraRateWindows case providerCost case kiroUsage case ampUsage case mimoUsage case openRouterUsage case sakanaPayAsYouGo case clawRouterUsage case sub2APIUsage case wayfinderUsage case openAIAPIUsage case groqConsoleUsage case codexResetCredits case claudeAdminAPIUsage case mistralUsage case deepgramUsage case poeUsage case subscriptionExpiresAt case subscriptionRenewsAt case updatedAt case identity case dataConfidence case accountEmail case accountOrganization case loginMethod } public init( primary: RateWindow?, secondary: RateWindow?, tertiary: RateWindow? = nil, extraRateWindows: [NamedRateWindow]? = nil, kiroUsage: KiroUsageDetails? = nil, ampUsage: AmpUsageDetails? = nil, providerCost: ProviderCostSnapshot? = nil, zaiUsage: ZaiUsageSnapshot? = nil, minimaxUsage: MiniMaxUsageSnapshot? = nil, deepseekUsage: DeepSeekUsageSummary? = nil, deepseekDetailedUsageState: DeepSeekDetailedUsageState = .notRequested, deepseekPlatformProfiles: [DeepSeekPlatformProfile] = [], opencodegoUsage: OpenCodeGoUsageSnapshot? = nil, mimoUsage: MiMoUsageSnapshot? = nil, openRouterUsage: OpenRouterUsageSnapshot? = nil, sakanaPayAsYouGo: SakanaPayAsYouGoSnapshot? = nil, clawRouterUsage: ClawRouterUsageSnapshot? = nil, sub2APIUsage: Sub2APIUsageDetails? = nil, wayfinderUsage: WayfinderUsageSnapshot? = nil, openAIAPIUsage: OpenAIAPIUsageSnapshot? = nil, groqConsoleUsage: GroqConsoleUsageSnapshot? = nil, codexResetCredits: CodexRateLimitResetCreditsSnapshot? = nil, claudeAdminAPIUsage: ClaudeAdminAPIUsageSnapshot? = nil, mistralUsage: MistralUsageSnapshot? = nil, deepgramUsage: DeepgramUsageSnapshot? = nil, poeUsage: PoeUsageHistorySnapshot? = nil, cursorRequests: CursorRequestUsage? = nil, commandCodeSubscriptionEnrichmentUnavailable: Bool = false, commandCodeHasSubscriptionPlan: Bool = false, commandCodeMonthlyGrantDepleted: Bool = false, subscriptionExpiresAt: Date? = nil, subscriptionRenewsAt: Date? = nil, updatedAt: Date, identity: ProviderIdentitySnapshot? = nil, dataConfidence: UsageDataConfidence = .unknown) { self.primary = primary self.secondary = secondary self.tertiary = tertiary self.extraRateWindows = extraRateWindows self.kiroUsage = kiroUsage self.ampUsage = ampUsage self.providerCost = providerCost self.zaiUsage = zaiUsage self.minimaxUsage = minimaxUsage self.deepseekUsage = deepseekUsage self.deepseekDetailedUsageState = deepseekDetailedUsageState self.deepseekPlatformProfiles = deepseekPlatformProfiles self.opencodegoUsage = opencodegoUsage self.mimoUsage = mimoUsage self.openRouterUsage = openRouterUsage self.sakanaPayAsYouGo = sakanaPayAsYouGo self.clawRouterUsage = clawRouterUsage self.sub2APIUsage = sub2APIUsage self.wayfinderUsage = wayfinderUsage self.openAIAPIUsage = openAIAPIUsage self.groqConsoleUsage = groqConsoleUsage self.codexResetCredits = codexResetCredits self.claudeAdminAPIUsage = claudeAdminAPIUsage self.mistralUsage = mistralUsage self.deepgramUsage = deepgramUsage self.poeUsage = poeUsage self.cursorRequests = cursorRequests self.commandCodeSubscriptionEnrichmentUnavailable = commandCodeSubscriptionEnrichmentUnavailable self.commandCodeHasSubscriptionPlan = commandCodeHasSubscriptionPlan self.commandCodeMonthlyGrantDepleted = commandCodeMonthlyGrantDepleted self.subscriptionExpiresAt = subscriptionExpiresAt self.subscriptionRenewsAt = subscriptionRenewsAt self.updatedAt = updatedAt self.identity = identity self.dataConfidence = dataConfidence } public func with(extraRateWindows: [NamedRateWindow]?) -> UsageSnapshot { self.replacing(extraRateWindows: .value(extraRateWindows)) } public func withCodexResetCredits(_ resetCredits: CodexRateLimitResetCreditsSnapshot?) -> UsageSnapshot { self.replacing(codexResetCredits: .value(resetCredits)) } public func with(primary: RateWindow?, secondary: RateWindow?) -> UsageSnapshot { self.replacing( primary: .value(primary), secondary: .value(secondary)) } public init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) self.primary = try container.decodeIfPresent(RateWindow.self, forKey: .primary) self.secondary = try container.decodeIfPresent(RateWindow.self, forKey: .secondary) self.tertiary = try container.decodeIfPresent(RateWindow.self, forKey: .tertiary) self.extraRateWindows = try container.decodeIfPresent([NamedRateWindow].self, forKey: .extraRateWindows) self.providerCost = try container.decodeIfPresent(ProviderCostSnapshot.self, forKey: .providerCost) self.kiroUsage = try container.decodeIfPresent(KiroUsageDetails.self, forKey: .kiroUsage) self.ampUsage = try container.decodeIfPresent(AmpUsageDetails.self, forKey: .ampUsage) self.zaiUsage = nil // Not persisted, fetched fresh each time self.minimaxUsage = nil // Not persisted, fetched fresh each time self.deepseekUsage = nil // Not persisted, fetched fresh each time self.deepseekDetailedUsageState = .notRequested // Live-only fetch state self.deepseekPlatformProfiles = [] // Live-only browser profile catalog self.opencodegoUsage = nil // Not persisted, fetched fresh each time self.mimoUsage = try container.decodeIfPresent(MiMoUsageSnapshot.self, forKey: .mimoUsage) self.openRouterUsage = try container.decodeIfPresent(OpenRouterUsageSnapshot.self, forKey: .openRouterUsage) self.sakanaPayAsYouGo = try container.decodeIfPresent( SakanaPayAsYouGoSnapshot.self, forKey: .sakanaPayAsYouGo) self.clawRouterUsage = try container.decodeIfPresent(ClawRouterUsageSnapshot.self, forKey: .clawRouterUsage) self.sub2APIUsage = try container.decodeIfPresent(Sub2APIUsageDetails.self, forKey: .sub2APIUsage) self.wayfinderUsage = try container.decodeIfPresent(WayfinderUsageSnapshot.self, forKey: .wayfinderUsage) self.openAIAPIUsage = try container.decodeIfPresent(OpenAIAPIUsageSnapshot.self, forKey: .openAIAPIUsage) self.groqConsoleUsage = try container.decodeIfPresent( GroqConsoleUsageSnapshot.self, forKey: .groqConsoleUsage) self.codexResetCredits = try container.decodeIfPresent( CodexRateLimitResetCreditsSnapshot.self, forKey: .codexResetCredits) self.claudeAdminAPIUsage = try container.decodeIfPresent( ClaudeAdminAPIUsageSnapshot.self, forKey: .claudeAdminAPIUsage) self.mistralUsage = try container.decodeIfPresent(MistralUsageSnapshot.self, forKey: .mistralUsage) self.deepgramUsage = try container.decodeIfPresent(DeepgramUsageSnapshot.self, forKey: .deepgramUsage) self.poeUsage = try container.decodeIfPresent(PoeUsageHistorySnapshot.self, forKey: .poeUsage) self.cursorRequests = nil // Not persisted, fetched fresh each time self.commandCodeSubscriptionEnrichmentUnavailable = false // Live-only fetch state self.commandCodeHasSubscriptionPlan = false // Live-only fetch state self.commandCodeMonthlyGrantDepleted = false // Live-only fetch state self.subscriptionExpiresAt = try container.decodeIfPresent(Date.self, forKey: .subscriptionExpiresAt) self.subscriptionRenewsAt = try container.decodeIfPresent(Date.self, forKey: .subscriptionRenewsAt) self.updatedAt = try container.decode(Date.self, forKey: .updatedAt) if let dataConfidence = try container.decodeIfPresent(String.self, forKey: .dataConfidence) { self.dataConfidence = UsageDataConfidence(rawValue: dataConfidence) ?? .unknown } else { self.dataConfidence = .unknown } if let identity = try container.decodeIfPresent(ProviderIdentitySnapshot.self, forKey: .identity) { self.identity = identity } else { let email = try container.decodeIfPresent(String.self, forKey: .accountEmail) let organization = try container.decodeIfPresent(String.self, forKey: .accountOrganization) let loginMethod = try container.decodeIfPresent(String.self, forKey: .loginMethod) if email != nil || organization != nil || loginMethod != nil { self.identity = ProviderIdentitySnapshot( providerID: nil, accountEmail: email, accountOrganization: organization, loginMethod: loginMethod) } else { self.identity = nil } } } public func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) // Stable JSON schema: keep window keys present (encode `nil` as `null`). try container.encode(self.primary, forKey: .primary) try container.encode(self.secondary, forKey: .secondary) try container.encode(self.tertiary, forKey: .tertiary) try container.encodeIfPresent(self.extraRateWindows, forKey: .extraRateWindows) try container.encodeIfPresent(self.providerCost, forKey: .providerCost) try container.encodeIfPresent(self.kiroUsage, forKey: .kiroUsage) try container.encodeIfPresent(self.ampUsage, forKey: .ampUsage) try container.encodeIfPresent(self.mimoUsage, forKey: .mimoUsage) try container.encodeIfPresent(self.openRouterUsage, forKey: .openRouterUsage) try container.encodeIfPresent(self.sakanaPayAsYouGo, forKey: .sakanaPayAsYouGo) try container.encodeIfPresent(self.clawRouterUsage, forKey: .clawRouterUsage) try container.encodeIfPresent(self.sub2APIUsage, forKey: .sub2APIUsage) try container.encodeIfPresent(self.wayfinderUsage, forKey: .wayfinderUsage) try container.encodeIfPresent(self.openAIAPIUsage, forKey: .openAIAPIUsage) try container.encodeIfPresent(self.groqConsoleUsage, forKey: .groqConsoleUsage) try container.encodeIfPresent(self.codexResetCredits, forKey: .codexResetCredits) try container.encodeIfPresent(self.claudeAdminAPIUsage, forKey: .claudeAdminAPIUsage) try container.encodeIfPresent(self.mistralUsage, forKey: .mistralUsage) try container.encodeIfPresent(self.deepgramUsage, forKey: .deepgramUsage) try container.encodeIfPresent(self.poeUsage, forKey: .poeUsage) try container.encodeIfPresent(self.subscriptionExpiresAt, forKey: .subscriptionExpiresAt) try container.encodeIfPresent(self.subscriptionRenewsAt, forKey: .subscriptionRenewsAt) try container.encode(self.updatedAt, forKey: .updatedAt) try container.encodeIfPresent(self.identity, forKey: .identity) if self.dataConfidence != .unknown { try container.encode(self.dataConfidence, forKey: .dataConfidence) } try container.encodeIfPresent(self.identity?.accountEmail, forKey: .accountEmail) try container.encodeIfPresent(self.identity?.accountOrganization, forKey: .accountOrganization) try container.encodeIfPresent(self.identity?.loginMethod, forKey: .loginMethod) } public func identity(for provider: UsageProvider) -> ProviderIdentitySnapshot? { guard let identity, identity.providerID == provider else { return nil } return identity } public func automaticPerplexityWindow() -> RateWindow? { let fallbackWindows = self.orderedPerplexityFallbackWindows() guard let primary = self.primary else { return fallbackWindows.first } if primary.remainingPercent > 0 || fallbackWindows.isEmpty { return primary } return fallbackWindows.first } public func orderedPerplexityDisplayWindows() -> [RateWindow] { let fallbackWindows = self.orderedPerplexityFallbackWindows() guard let primary = self.primary else { return fallbackWindows } if primary.remainingPercent > 0 || fallbackWindows.isEmpty { return [primary] + fallbackWindows } return fallbackWindows + [primary] } public func switcherWeeklyWindow(for provider: UsageProvider, showUsed: Bool) -> RateWindow? { switch provider { case .factory: // Factory prefers secondary window return self.secondary ?? self.primary case .perplexity: return self.automaticPerplexityWindow() case .cursor: // Cursor: fall back to on-demand budget when the included plan is exhausted (only in // "show remaining" mode). The secondary/tertiary lanes are Total/Auto/API breakdowns, // not extra capacity, so they should not replace the remaining paid quota indicator. if !showUsed, let primary = self.primary, primary.remainingPercent <= 0, let providerCost = self.providerCost, providerCost.limit > 0 { let usedPercent = max(0, min(100, (providerCost.used / providerCost.limit) * 100)) return RateWindow( usedPercent: usedPercent, windowMinutes: nil, resetsAt: providerCost.resetsAt, resetDescription: nil) } return self.primary ?? self.secondary default: return self.primary ?? self.secondary } } public func accountEmail(for provider: UsageProvider) -> String? { self.identity(for: provider)?.accountEmail