Agent SDK: rate_limit_event already carries per-bucket unifiedWindows utilization at status=allowed — declare it in SDKRateLimitInfo and add a zero-token structured path (follow-up to #50518)
Summary
#50518 asked for per-bucket 5h/7d utilization for headless Agent SDK consumers and was auto-closed as inactive (and is now locked, so this is the follow-up it directs to). On the current CLI the data is already on the wire — but undocumented, and only reachable by spending a model turn.
Environment
- Claude Code 2.1.259 (native install, linux-x64)
@anthropic-ai/claude-agent-sdk0.3.257 (bundled by@agentclientprotocol/claude-agent-acp0.73.0)- claude.ai subscription; utilization well below any warning threshold
Observed
One turn into a fresh session (prompt: Reply with the single word: done), the rate_limit_event's rate_limit_info — as the ACP adapter forwards it in usage_update._meta["_claude/rateLimit"]:
{
"status": "allowed",
"resetsAt": 1788465600,
"rateLimitType": "five_hour",
"overageStatus": "allowed",
"overageResetsAt": 1790812800,
"isUsingOverage": false,
"unifiedWindows": {
"five_hour": { "utilization": 0.24, "resetsAt": 1788465600 },
"seven_day": { "utilization": 0.13, "resetsAt": 1789005600 }
}
}
- Top-level
utilizationis absent atstatus: "allowed", exactly as #50518 described. unifiedWindowsis present and populated at normal utilization — this looks like #50518's "Option A" per-bucket snapshot, shipped.unifiedWindowsis not declared onSDKRateLimitInfoinsdk.d.ts(0.3.257), so typed consumers cannot rely on it (#26392 asked for these types to be documented).- A session that only runs
/usage(a local command; no API call) emits norate_limit_event. A headless process that wants the numbers without spending a turn therefore has no structured source —/usageis rendered text.
Asks
- Declare
unifiedWindows(with its per-bucket shape) onSDKRateLimitInfoand document it, so it is a contract rather than a wire observation. - A zero-token structured path: either a control request returning the current rate-limit snapshot (
getUsage()is marked EXPERIMENTAL and is not exposed over ACP), or a machine-readable form of/usage.
Why it matters
I run a usage-aware governor that seats sessions by 5h/7d headroom. Sessions launched over ACP (Zed) have no statusline, so today the only headless source is spawning the CLI and parsing /usage text hourly. It works, but it couples to UI wording; (1) plus (2) would replace it with a passive, structured source.
Reproduction: any ACP client — send one prompt and log usage_update._meta["_claude/rateLimit"]. Or via the SDK directly: log messages with type === "rate_limit_event" and inspect rate_limit_info.unifiedWindows.