Skip to content
Merged
1 change: 1 addition & 0 deletions apps/desktop-tauri/src-tauri/src/auto_resume_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ mod tests {
reserve_description: None,
reserve_will_last_to_reset: false,
reserve_eta_seconds: None,
pace: None,
monthly_limit_block: None,
description_is_detail: false,
}
Expand Down
44 changes: 30 additions & 14 deletions apps/desktop-tauri/src-tauri/src/commands/bridge.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ pub struct RateWindowSnapshot {
pub reserve_will_last_to_reset: bool,
#[serde(default)]
pub reserve_eta_seconds: Option<f64>,
#[serde(default)]
pub pace: Option<pace::WindowPaceSnapshot>,
/// Set while a longer exhausted pool (Kimi's monthly membership) blocks
/// this window; presentation only, the raw percentages above stay as is.
#[serde(default)]
Expand All @@ -67,6 +69,7 @@ impl RateWindowSnapshot {
reserve_description: None,
reserve_will_last_to_reset: false,
reserve_eta_seconds: None,
pace: None,
monthly_limit_block: None,
description_is_detail: rw.description_is_detail,
}
Expand Down Expand Up @@ -404,7 +407,8 @@ impl ProviderUsageSnapshot {
});
let primary_pace = primary_pace.flatten();

let blocked = BlockedWindows::evaluate(id, usage, chrono::Utc::now());
let now = chrono::Utc::now();
let blocked = BlockedWindows::evaluate(id, usage, now);
let pace = primary_pace.as_ref().map(|p| PaceSnapshot {
stage: pace::stage_str(p.stage).to_string(),
delta_percent: p.delta_percent,
Expand All @@ -424,12 +428,22 @@ impl ProviderUsageSnapshot {
});
let secondary_pace = secondary_pace.flatten();

let primary_snap = RateWindowSnapshot::from_rate_window(&usage.primary)
.with_quota_block(blocked.primary, &blocked);
let lane =
|window: &RateWindow, is_blocked: bool, default_minutes: u32| RateWindowSnapshot {
pace: allows_pace
.then(|| pace::WindowPaceSnapshot::for_window(window, default_minutes, now))
.flatten(),
..RateWindowSnapshot::from_rate_window(window)
.with_quota_block(is_blocked, &blocked)
};
let primary_snap = lane(
&usage.primary,
blocked.primary,
pace::SESSION_FALLBACK_MINUTES,
);

let secondary_snap = usage.secondary.as_ref().map(|sw| {
let mut s = RateWindowSnapshot::from_rate_window(sw)
.with_quota_block(blocked.secondary, &blocked);
let mut s = lane(sw, blocked.secondary, pace::WEEKLY_FALLBACK_MINUTES);
if let Some(ref p) = secondary_pace {
s = s.with_pace_reserve(p);
}
Expand Down Expand Up @@ -471,13 +485,14 @@ impl ProviderUsageSnapshot {
.clone()
.unwrap_or_else(|| metadata.weekly_label.to_string())
}),
model_specific: usage.model_specific.as_ref().map(|w| {
RateWindowSnapshot::from_rate_window(w)
.with_quota_block(blocked.model_specific, &blocked)
}),
tertiary: usage.tertiary.as_ref().map(|w| {
RateWindowSnapshot::from_rate_window(w).with_quota_block(blocked.tertiary, &blocked)
}),
model_specific: usage
.model_specific
.as_ref()
.map(|w| lane(w, blocked.model_specific, pace::WEEKLY_FALLBACK_MINUTES)),
tertiary: usage
.tertiary
.as_ref()
.map(|w| lane(w, blocked.tertiary, pace::WEEKLY_FALLBACK_MINUTES)),
// F5 (upstream 0.48.0): label the tertiary lane by its duration cadence
// so surfaces (MenuCard, CLI, tray) can show "Monthly" instead of the
// generic "DetailWindowTertiary" slot key.
Expand All @@ -496,8 +511,7 @@ impl ProviderUsageSnapshot {
.map(|(extra, &is_blocked)| NamedRateWindowSnapshot {
id: extra.id.clone(),
title: extra.title.clone(),
window: RateWindowSnapshot::from_rate_window(&extra.window)
.with_quota_block(is_blocked, &blocked),
window: lane(&extra.window, is_blocked, pace::WEEKLY_FALLBACK_MINUTES),
fallback_lane: extra.fallback_lane,
icon_fallback: extra.icon_fallback,
})
Expand Down Expand Up @@ -600,6 +614,7 @@ impl ProviderUsageSnapshot {
reserve_description: None,
reserve_will_last_to_reset: false,
reserve_eta_seconds: None,
pace: None,
monthly_limit_block: None,
description_is_detail: false,
},
Expand Down Expand Up @@ -1157,6 +1172,7 @@ mod tests {
reserve_description: None,
reserve_will_last_to_reset: false,
reserve_eta_seconds: None,
pace: None,
monthly_limit_block: None,
description_is_detail: false,
}
Expand Down
184 changes: 184 additions & 0 deletions apps/desktop-tauri/src-tauri/src/commands/bridge/pace.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
use super::*;
use chrono::{DateTime, Utc};

pub(crate) const SESSION_FALLBACK_MINUTES: u32 = 300;
pub(crate) const WEEKLY_FALLBACK_MINUTES: u32 = 10_080;

pub(crate) fn stage_str(stage: codexbar::core::PaceStage) -> &'static str {
use codexbar::core::PaceStage;
match stage {
Expand All @@ -10,3 +16,181 @@ pub(crate) fn stage_str(stage: codexbar::core::PaceStage) -> &'static str {
PaceStage::FarBehind => "far_behind",
}
}

/// Pace of one rate window against the time elapsed in it.
/// `delta_percent` is actual minus expected used percent, so a positive delta
/// is a deficit and a negative one a reserve.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct WindowPaceSnapshot {
pub stage: String,
pub delta_percent: f64,
pub expected_used_percent: f64,
pub actual_used_percent: f64,
#[serde(default)]
pub eta_seconds: Option<f64>,
#[serde(default)]
pub will_last_to_reset: bool,
}

impl WindowPaceSnapshot {
pub(crate) fn for_window(
window: &RateWindow,
default_window_minutes: u32,
now: DateTime<Utc>,
) -> Option<Self> {
if window.is_informational {
return None;
}
let pace = codexbar::core::UsagePace::weekly(window, Some(now), default_window_minutes)?;
Some(Self {
stage: stage_str(pace.stage).to_string(),
delta_percent: pace.delta_percent,
expected_used_percent: pace.expected_used_percent,
actual_used_percent: pace.actual_used_percent,
eta_seconds: pace.eta_seconds,
will_last_to_reset: pace.will_last_to_reset,
})
}
}

#[cfg(test)]
mod tests {
use super::*;
use chrono::{Duration, TimeZone};
use codexbar::core::UsageSnapshot;

fn fixed_now() -> DateTime<Utc> {
Utc.with_ymd_and_hms(2026, 10, 10, 12, 0, 0).unwrap()
}

fn resetting_in(
now: DateTime<Utc>,
used: f64,
window: Option<u32>,
minutes: i64,
) -> RateWindow {
RateWindow::with_details(used, window, Some(now + Duration::minutes(minutes)), None)
}

fn snapshot(usage: UsageSnapshot, pace_authoritative: bool) -> ProviderUsageSnapshot {
let metadata = instantiate_provider(ProviderId::Claude).metadata().clone();
let mut result = ProviderFetchResult::new(usage, "web");
if !pace_authoritative {
result = result.with_non_authoritative_pace();
}
ProviderUsageSnapshot::from_fetch_result(ProviderId::Claude, &metadata, &result, None)
}

fn expected_used(window: &RateWindowSnapshot) -> f64 {
window
.pace
.as_ref()
.expect("lane pace")
.expected_used_percent
}

#[test]
fn reserve_is_a_negative_delta_that_lasts_to_reset() {
let window = resetting_in(fixed_now(), 30.0, Some(10_080), 5_040);

assert_eq!(
WindowPaceSnapshot::for_window(&window, WEEKLY_FALLBACK_MINUTES, fixed_now()),
Some(WindowPaceSnapshot {
stage: "far_behind".to_string(),
delta_percent: -20.0,
expected_used_percent: 50.0,
actual_used_percent: 30.0,
eta_seconds: None,
will_last_to_reset: true,
})
);
}

#[test]
fn deficit_is_a_positive_delta_with_an_eta() {
let window = resetting_in(fixed_now(), 55.0, Some(10_080), 5_040);

let pace = WindowPaceSnapshot::for_window(&window, WEEKLY_FALLBACK_MINUTES, fixed_now())
.expect("pace");

assert_eq!(pace.stage, "slightly_ahead");
assert_eq!(pace.delta_percent, 5.0);
assert!(!pace.will_last_to_reset);
assert!((pace.eta_seconds.expect("eta") - 247_418.18).abs() < 0.01);
}

#[test]
fn informational_window_has_no_pace() {
let mut window = resetting_in(fixed_now(), 30.0, Some(10_080), 5_040);
window.is_informational = true;

assert_eq!(
WindowPaceSnapshot::for_window(&window, WEEKLY_FALLBACK_MINUTES, fixed_now()),
None
);
}

#[test]
fn session_lane_defaults_to_a_300_minute_window() {
let primary = resetting_in(Utc::now(), 60.0, None, 150);

let snap = snapshot(UsageSnapshot::new(primary), true);

assert!((expected_used(&snap.primary) - 50.0).abs() < 0.1);
assert_eq!(snap.primary.pace.as_ref().unwrap().stage, "ahead");
}

#[test]
fn weekly_lanes_default_to_a_10080_minute_window() {
let now = Utc::now();
let mut usage = UsageSnapshot::new(RateWindow::new(10.0))
.with_secondary(resetting_in(now, 40.0, None, 5_040))
.with_tertiary(resetting_in(now, 45.0, None, 5_040))
.with_extra_rate_window("sonnet", "Sonnet", resetting_in(now, 50.0, None, 5_040));
usage.model_specific = Some(resetting_in(now, 55.0, None, 5_040));

let snap = snapshot(usage, true);

assert!((expected_used(snap.secondary.as_ref().unwrap()) - 50.0).abs() < 0.1);
assert!((expected_used(snap.tertiary.as_ref().unwrap()) - 50.0).abs() < 0.1);
assert!((expected_used(snap.model_specific.as_ref().unwrap()) - 50.0).abs() < 0.1);
assert!((expected_used(&snap.extra_rate_windows[0].window) - 50.0).abs() < 0.1);
}

#[test]
fn informational_primary_keeps_the_weekly_lane_pace() {
let usage = UsageSnapshot::new(RateWindow::informational("No session limit"))
.with_secondary(resetting_in(Utc::now(), 30.0, Some(10_080), 5_040));

let snap = snapshot(usage, true);

assert_eq!(snap.primary.pace, None);
let weekly = snap.secondary.as_ref().unwrap().pace.as_ref().unwrap();
assert_eq!(weekly.stage, "far_behind");
assert!(weekly.will_last_to_reset);
}

#[test]
fn non_authoritative_pace_leaves_every_lane_empty() {
let now = Utc::now();
let usage = UsageSnapshot::new(resetting_in(now, 60.0, Some(300), 150))
.with_secondary(resetting_in(now, 30.0, Some(10_080), 5_040));

let snap = snapshot(usage, false);

assert_eq!(snap.primary.pace, None);
assert_eq!(snap.secondary.as_ref().unwrap().pace, None);
}

#[test]
fn window_pace_serializes_in_camel_case() {
let window = resetting_in(fixed_now(), 30.0, Some(10_080), 5_040);
let pace = WindowPaceSnapshot::for_window(&window, WEEKLY_FALLBACK_MINUTES, fixed_now());

assert_eq!(
serde_json::to_string(&pace).unwrap(),
r#"{"stage":"far_behind","deltaPercent":-20.0,"expectedUsedPercent":50.0,"actualUsedPercent":30.0,"etaSeconds":null,"willLastToReset":true}"#
);
}
}
1 change: 1 addition & 0 deletions apps/desktop-tauri/src-tauri/src/commands/providers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1618,6 +1618,7 @@ mod reset_backfill_tests {
reserve_description: None,
reserve_will_last_to_reset: false,
reserve_eta_seconds: None,
pace: None,
description_is_detail: false,
monthly_limit_block: None,
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ mod tests {
reserve_description: None,
reserve_will_last_to_reset: false,
reserve_eta_seconds: None,
pace: None,
monthly_limit_block: None,
description_is_detail: false,
}
Expand Down
1 change: 1 addition & 0 deletions apps/desktop-tauri/src-tauri/src/powertoys.rs
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,7 @@ mod tests {
reserve_percent: None,
reserve_description: None,
reserve_eta_seconds: None,
pace: None,
monthly_limit_block: None,
description_is_detail: false,
reserve_will_last_to_reset: false,
Expand Down
Loading