Preserve usage limit windows in turn and compaction analytics (#48174)

## Why

Usage limit errors discarded the server-selected window responsible for the limit, leaving turn and compaction analytics unable to distinguish five-hour limits from weekly limits.

## What changed

Preserve optional `limit_window_minutes` from usage limit error responses in `UsageLimitReachedError` and report it as `usage_limit_window_minutes` in turn and compaction events. Accept unsigned integers that fit in `u16`; treat missing, null, malformed, or out-of-range values as unknown. Events for other error kinds report `null`.

## Testing

Add coverage for HTTP and wrapped WebSocket error mapping, analytics serialization and reduction, and app-server propagation to turn and local/remote compaction events. Cases include five-hour and weekly windows, missing or invalid values, and unrelated errors.

GitOrigin-RevId: a32066a007559d91a3c868a756bfd0f976777176
This commit is contained in:
rhan-oai
2026-09-25 19:05:15 +00:00
committed by copyberry
parent f6a4bd81e3
commit 44a9bfa145
15 changed files with 371 additions and 0 deletions
+7
View File
@@ -204,6 +204,12 @@ fn map_api_error_details(err: ApiError) -> CodexErr {
return CodexErr::UsageLimitReached(UsageLimitReachedError {
plan_type: err.error.plan_type,
resets_at,
limit_window_minutes: err
.error
.limit_window_minutes
.as_ref()
.and_then(Value::as_u64)
.and_then(|minutes| u16::try_from(minutes).ok()),
rate_limits: rate_limits.map(Box::new),
promo_message,
rate_limit_reached_type,
@@ -340,4 +346,5 @@ struct UsageErrorBody {
error_type: Option<String>,
plan_type: Option<PlanType>,
resets_at: Option<i64>,
limit_window_minutes: Option<Value>,
}