-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Fix automatic quota display priority for exhausted windows & Antigravity session preference #2352
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
2087fb9
183ee8a
d5b4062
deba799
ff22cdf
2adda1c
cbbd748
01b89a6
94e168c
aae6f71
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -117,21 +117,6 @@ enum MenuBarMetricWindowResolver { | |
| now: Date) | ||
| -> RateWindow? | ||
| { | ||
| if provider == .antigravity { | ||
| if antigravityPrioritizeExhaustedQuotas, | ||
| let window = antigravityQuotaSummaryRankingWindow(snapshot: snapshot, now: now) | ||
| { | ||
| return window | ||
| } | ||
| if let window = mostConstrainedAntigravityQuotaSummaryWindow(snapshot: snapshot) { | ||
| return window | ||
| } | ||
| return self.mostConstrainedWindow( | ||
| primary: snapshot.primary, | ||
| secondary: snapshot.secondary, | ||
| tertiary: snapshot.tertiary) | ||
| ?? self.mostConstrainedAntigravityLegacyExtraWindow(snapshot: snapshot) | ||
| } | ||
| if provider == .perplexity { | ||
| return snapshot.automaticPerplexityWindow() | ||
| } | ||
|
|
@@ -141,10 +126,11 @@ enum MenuBarMetricWindowResolver { | |
| secondary: snapshot.tertiary, | ||
| tertiary: nil) ?? snapshot.secondary | ||
| } | ||
| if provider == .factory || provider == .kimi { | ||
| return snapshot.secondary ?? snapshot.primary | ||
| } | ||
| if provider == .litellm { | ||
| if provider == .factory || provider == .kimi || provider == .litellm { | ||
| let windows = [snapshot.primary, snapshot.secondary].compactMap(\.self) | ||
| if let exhausted = windows.first(where: { $0.usedPercent >= 100 }) { | ||
| return exhausted | ||
| } | ||
| return snapshot.secondary ?? snapshot.primary | ||
| } | ||
| if provider == .copilot, | ||
|
|
@@ -154,150 +140,29 @@ enum MenuBarMetricWindowResolver { | |
| return primary.usedPercent >= secondary.usedPercent ? primary : secondary | ||
| } | ||
| if provider == .cursor { | ||
| return Self.mostConstrainedCursorWindow( | ||
| return self.mostConstrainedCursorWindow( | ||
| total: snapshot.primary, | ||
| auto: snapshot.secondary, | ||
| api: snapshot.tertiary) | ||
| } | ||
| if provider == .minimax { | ||
| return Self.mostConstrainedWindow( | ||
| return self.mostConstrainedWindow( | ||
| primary: snapshot.primary, | ||
| secondary: snapshot.secondary, | ||
| tertiary: snapshot.tertiary) | ||
| } | ||
| if provider == .claude, let spendLimit = Self.claudeSpendLimitWindow(snapshot: snapshot) { | ||
| if provider == .claude, let spendLimit = claudeSpendLimitWindow(snapshot: snapshot) { | ||
| return spendLimit | ||
| } | ||
| return snapshot.primary ?? snapshot.secondary | ||
| } | ||
|
|
||
| private static let antigravityQuotaSummaryWindowIDPrefix = "antigravity-quota-summary-" | ||
| private static let antigravityCompactFallbackWindowIDPrefix = "antigravity-compact-fallback-" | ||
|
|
||
| private static func mostConstrainedAntigravityQuotaSummaryWindow(snapshot: UsageSnapshot) -> RateWindow? { | ||
| let windows = snapshot.extraRateWindows? | ||
| .filter { $0.usageKnown && $0.id.hasPrefix(Self.antigravityQuotaSummaryWindowIDPrefix) } | ||
| .map(\.window) ?? [] | ||
| guard !windows.isEmpty else { return nil } | ||
|
|
||
| let usableWindows = windows.filter { $0.usedPercent < 100 } | ||
| if let maxUsable = usableWindows.max(by: { $0.usedPercent < $1.usedPercent }) { | ||
| return maxUsable | ||
| } | ||
| return windows.max(by: { $0.usedPercent < $1.usedPercent }) | ||
| } | ||
|
|
||
| /// Picks the binding supported quota-summary lane for the exhausted-first opt-in. | ||
| static func antigravityQuotaSummaryRankingWindow( | ||
| snapshot: UsageSnapshot, | ||
| now: Date) | ||
| -> RateWindow? | ||
| { | ||
| let candidates = Self.antigravityQuotaSummaryRows(snapshot: snapshot) | ||
| .filter { | ||
| $0.usageKnown && | ||
| $0.window.usedPercent.isFinite && | ||
| Self.isSupportedAntigravityQuotaCadence($0.window.windowMinutes) | ||
| } | ||
| return candidates.max { lhs, rhs in | ||
| if lhs.window.usedPercent != rhs.window.usedPercent { | ||
| return lhs.window.usedPercent < rhs.window.usedPercent | ||
| } | ||
|
|
||
| let lhsFutureReset = lhs.window.resetsAt.flatMap { $0 > now ? $0 : nil } | ||
| let rhsFutureReset = rhs.window.resetsAt.flatMap { $0 > now ? $0 : nil } | ||
| if (lhsFutureReset != nil) != (rhsFutureReset != nil) { | ||
| return lhsFutureReset == nil | ||
| } | ||
| if let lhsFutureReset, let rhsFutureReset, lhsFutureReset != rhsFutureReset { | ||
| return lhsFutureReset > rhsFutureReset | ||
| } | ||
| return lhs.id < rhs.id | ||
| }?.window | ||
| } | ||
|
|
||
| /// True only when every fully understood quota family has an exhausted binding lane. | ||
| /// Any incomplete or unfamiliar summary row fails open so automatic provider rotation | ||
| /// does not hide quota that CodexBar cannot classify safely. | ||
| static func antigravityQuotaSummaryFamiliesAreAllBlocked(snapshot: UsageSnapshot) -> Bool { | ||
| let rows = Self.antigravityQuotaSummaryRows(snapshot: snapshot) | ||
| guard !rows.isEmpty else { return false } | ||
|
|
||
| var familyBlocked: [String: Bool] = [:] | ||
| for row in rows { | ||
| guard row.usageKnown, | ||
| row.window.usedPercent.isFinite, | ||
| Self.isSupportedAntigravityQuotaCadence(row.window.windowMinutes), | ||
| let family = Self.antigravityQuotaFamily(for: row) | ||
| else { | ||
| return false | ||
| } | ||
| familyBlocked[family, default: false] = | ||
| familyBlocked[family, default: false] || row.window.usedPercent >= 100 | ||
| } | ||
| return !familyBlocked.isEmpty && familyBlocked.values.allSatisfy(\.self) | ||
| } | ||
|
|
||
| private static let antigravitySupportedQuotaCadences: Set<Int> = [300, 10080] | ||
|
|
||
| private static func isSupportedAntigravityQuotaCadence(_ windowMinutes: Int?) -> Bool { | ||
| guard let windowMinutes else { return false } | ||
| return Self.antigravitySupportedQuotaCadences.contains(windowMinutes) | ||
| } | ||
|
|
||
| private static func antigravityQuotaSummaryRows(snapshot: UsageSnapshot) -> [NamedRateWindow] { | ||
| snapshot.extraRateWindows?.filter { | ||
| $0.id.hasPrefix(Self.antigravityQuotaSummaryWindowIDPrefix) | ||
| } ?? [] | ||
| } | ||
|
|
||
| private static func antigravityQuotaFamily(for row: NamedRateWindow) -> String? { | ||
| let suffix = row.id.dropFirst(Self.antigravityQuotaSummaryWindowIDPrefix.count) | ||
| var normalizedSuffix = suffix | ||
| .lowercased() | ||
| .replacingOccurrences(of: "_", with: "-") | ||
| if normalizedSuffix.hasSuffix(" limit") { | ||
| normalizedSuffix.removeLast(" limit".count) | ||
| } | ||
| let cadenceSuffixes: [String] | ||
| switch row.window.windowMinutes { | ||
| case 300: | ||
| cadenceSuffixes = ["-session", "-5h", "-5-hour", "-five hour", "-five-hour"] | ||
| case 10080: | ||
| cadenceSuffixes = ["-weekly"] | ||
| default: | ||
| return nil | ||
| } | ||
|
|
||
| guard let cadenceSuffix = cadenceSuffixes.first(where: normalizedSuffix.hasSuffix) else { | ||
| return nil | ||
| // Default path: | ||
| // 1. Prioritize any exhausted window (usedPercent >= 100) | ||
| let windows = [snapshot.primary, snapshot.secondary, snapshot.tertiary].compactMap(\.self) | ||
| if let exhausted = windows.filter({ $0.usedPercent >= 100 }).max(by: { $0.usedPercent < $1.usedPercent }) { | ||
| return exhausted | ||
|
Comment on lines
+160
to
+162
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
For default providers with independent lanes, such as Gemini's Pro/Flash/Flash Lite quotas, this exhausted-first branch now lets an exhausted lower-priority tertiary lane win automatic selection even though Gemini's automatic behavior is expected to stay on the primary lane (see Useful? React with 👍 / 👎. |
||
| } | ||
| let family = normalizedSuffix | ||
| .dropLast(cadenceSuffix.count) | ||
| .trimmingCharacters(in: .whitespacesAndNewlines) | ||
| guard !family.isEmpty, | ||
| family.first != "-", | ||
| family.last != "-", | ||
| family.allSatisfy({ $0.isLetter || $0.isNumber || $0 == "-" || $0 == "_" || $0 == "." }) | ||
| else { | ||
| return nil | ||
| } | ||
| return family | ||
| } | ||
|
|
||
| private static func mostConstrainedAntigravityLegacyExtraWindow(snapshot: UsageSnapshot) -> RateWindow? { | ||
| let windows = snapshot.extraRateWindows? | ||
| .filter { | ||
| $0.usageKnown && $0.id.hasPrefix(Self.antigravityCompactFallbackWindowIDPrefix) | ||
| } | ||
| .map(\.window) ?? [] | ||
| guard !windows.isEmpty else { return nil } | ||
|
|
||
| let usableWindows = windows.filter { $0.usedPercent < 100 } | ||
| if let maxUsable = usableWindows.max(by: { $0.usedPercent < $1.usedPercent }) { | ||
| return maxUsable | ||
| } | ||
| return windows.max(by: { $0.usedPercent < $1.usedPercent }) | ||
| // 2. Otherwise, fall back to the default order | ||
| return snapshot.primary ?? snapshot.secondary | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
For Antigravity local probes with only an unrecognized selectable text model, Useful? React with 👍 / 👎. |
||
| } | ||
|
|
||
| private static func requestedWindow( | ||
|
|
@@ -306,9 +171,6 @@ enum MenuBarMetricWindowResolver { | |
| lanes: [Lane]) -> RateWindow? | ||
| { | ||
| self.window(in: snapshot, following: lanes) | ||
| ?? (provider == .antigravity | ||
| ? self.mostConstrainedAntigravityLegacyExtraWindow(snapshot: snapshot) | ||
| : nil) | ||
| } | ||
|
|
||
| private static func window(in snapshot: UsageSnapshot, following lanes: [Lane]) -> RateWindow? { | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -39,8 +39,9 @@ enum UsagePaceText { | |
|
|
||
| static func sessionEquivalentDetail(forecast: SessionEquivalentForecast) -> SessionEquivalentDetail { | ||
| let displayedEstimate = Self.boundedFullWindowCount(forecast.estimatedWindowsToExhaustWeekly) | ||
| let numberText = String.localizedStringWithFormat( | ||
| L("≈%d full 5h windows of weekly left · %d windows until reset"), | ||
| let numberText = String( | ||
| format: L("≈%d full 5h windows of weekly left · %d windows until reset"), | ||
| locale: codexBarLocalizedLocale(), | ||
|
Comment on lines
+42
to
+44
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
These strings are backed by Useful? React with 👍 / 👎. |
||
| displayedEstimate, | ||
| forecast.windowsUntilReset) | ||
| let verdictText: String | ||
|
|
@@ -49,8 +50,9 @@ enum UsagePaceText { | |
| } else { | ||
| let windowsEarly = Self.boundedWindowCount( | ||
| forecast.availableWindowsUntilReset - forecast.estimatedWindowsToExhaustWeekly) | ||
| verdictText = String.localizedStringWithFormat( | ||
| L("Weekly can run out ≈%d windows early"), | ||
| verdictText = String( | ||
| format: L("Weekly can run out ≈%d windows early"), | ||
| locale: codexBarLocalizedLocale(), | ||
| max(1, windowsEarly)) | ||
| } | ||
| return SessionEquivalentDetail( | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -9,8 +9,8 @@ public enum AntigravityProviderDescriptor { | |
| metadata: ProviderMetadata( | ||
| id: .antigravity, | ||
| displayName: "Antigravity", | ||
| sessionLabel: "Gemini Models", | ||
| weeklyLabel: "Claude and GPT", | ||
| sessionLabel: "Gemini 5-hour", | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
When Antigravity uses the remote/local Useful? React with 👍 / 👎. |
||
| weeklyLabel: "Gemini weekly", | ||
| opusLabel: nil, | ||
| supportsOpus: false, | ||
| supportsCredits: false, | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When Antigravity is in Automatic mode with the default
antigravityPrioritizeExhaustedQuotas == false, this default branch is still reached and returns any 100% lane before the normal session/primary preference; the setting is no longer consulted. In accounts with one exhausted Antigravity lane and another still usable, the “Prioritize exhausted quotas” toggle now behaves as if it is always enabled for the menu bar and highest-usage ranking, so gate this exhausted-first return for Antigravity on the setting or restore the provider-specific non-exhausted selection.Useful? React with 👍 / 👎.