Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 16 additions & 11 deletions apps/desktop-tauri/src-tauri/src/capacity_events.rs
Original file line number Diff line number Diff line change
Expand Up @@ -525,7 +525,11 @@ fn inactive_windows(snapshot: &ProviderUsageSnapshot) -> HashMap<String, String>
.collect()
}

fn ignored_capacity_window(snapshot: &ProviderUsageSnapshot, id: &str, title: &str) -> bool {
pub(crate) fn ignored_capacity_window(
snapshot: &ProviderUsageSnapshot,
id: &str,
title: &str,
) -> bool {
if snapshot.provider_id != "cursor" {
return false;
}
Expand All @@ -535,7 +539,7 @@ fn ignored_capacity_window(snapshot: &ProviderUsageSnapshot, id: &str, title: &s
|| identity.contains("ondemand")
}

fn semantic_inactive_window_id(provider_id: &str, id: &str, title: &str) -> String {
pub(crate) fn semantic_inactive_window_id(provider_id: &str, id: &str, title: &str) -> String {
let title_id = normalize_window_id(title);
if let Some(core_id) = core_window_id(&title_id) {
return core_id.to_string();
Expand Down Expand Up @@ -592,7 +596,7 @@ fn to_observed_window(label: &str, window: &RateWindowSnapshot) -> Option<Observ
})
}

fn semantic_window_id(label: &str, window_minutes: Option<u32>) -> String {
pub(crate) fn semantic_window_id(label: &str, window_minutes: Option<u32>) -> String {
let normalized = normalize_window_id(label);
if matches!(
normalized.as_str(),
Expand Down Expand Up @@ -624,15 +628,15 @@ fn normalize_window_id(value: &str) -> String {
.to_string()
}

fn observation_scope(snapshot: &ProviderUsageSnapshot) -> String {
let identity = snapshot
.account_email
.as_deref()
.or(snapshot.account_organization.as_deref())
.unwrap_or("anonymous");
pub(crate) fn observation_scope(snapshot: &ProviderUsageSnapshot) -> String {
// Scope by BOTH account identifiers, not either/or: the same email can
// belong to different organizations (e.g. a personal vs a business
// workspace) with distinct limits, and those must never share a baseline.
let email = snapshot.account_email.as_deref().unwrap_or("");
let organization = snapshot.account_organization.as_deref().unwrap_or("");
let raw = format!(
"{}|{}|{}",
snapshot.provider_id, snapshot.source_label, identity
"{}|{}|{}|{}",
snapshot.provider_id, snapshot.source_label, email, organization
);
format!("{}:{:016x}", snapshot.provider_id, fnv1a64(raw.as_bytes()))
}
Expand Down Expand Up @@ -729,6 +733,7 @@ mod tests {
id: id.into(),
title: title.into(),
description: "Not currently limited".into(),
state: "notEnforced".into(),
});
snapshot
}
Expand Down
4 changes: 4 additions & 0 deletions apps/desktop-tauri/src-tauri/src/commands/bridge.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,9 @@ pub struct InactiveRateWindowSnapshot {
pub id: String,
pub title: String,
pub description: String,
/// "notEnforced" (provider reported no active limit) or "unavailable" (the
/// window dropped out of an otherwise-successful response).
pub state: String,
}

#[derive(Debug, Clone, Serialize)]
Expand Down Expand Up @@ -275,6 +278,7 @@ impl ProviderUsageSnapshot {
id: window.id.clone(),
title: window.title.clone(),
description: window.description.clone(),
state: window.state.as_str().to_string(),
})
.collect(),
promo_signals: usage
Expand Down
12 changes: 11 additions & 1 deletion apps/desktop-tauri/src-tauri/src/commands/providers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -286,8 +286,18 @@ async fn refresh_provider(app: tauri::AppHandle, id: ProviderId, ctx: FetchConte

let state = app.state::<Mutex<AppState>>();
let (snapshot, capacity_events, notifications_armed) = if let Ok(mut guard) = state.lock() {
let snapshot = preserve_last_good_transient_failure(&mut guard, id, snapshot);
let mut snapshot = preserve_last_good_transient_failure(&mut guard, id, snapshot);
// Detect resets/lifts against the real response first, so the synthetic
// `unavailable` rows added next are never mistaken for a lifted window.
let capacity_events = guard.capacity_event_observer.observe(&snapshot);
let unavailable = guard.enforcement_tracker.annotate(&mut snapshot);
if !unavailable.is_empty() {
tracing::debug!(
provider = %snapshot.provider_id,
windows = ?unavailable,
"windows dropped out of a successful response; marked unavailable"
);
}
let notifications_armed = guard.notification_manager.notifications_are_armed();
upsert_provider_cache(&mut guard.provider_cache, snapshot.clone());
(snapshot, capacity_events, notifications_armed)
Expand Down
Loading