Session status card: show the usage limits acpmux reports instead of Not reported
Fixes an internal report: "First display structured provider-reported window/reset information ... Unknown reset time ... no guessed countdown ... shows the timestamp/time zone clearly".
Problem
acpmux sends a Claude Code chat's plan usage limits as an object in the session detail (claude_stdio/usage_limits.rs, hub/views.rs sets usageLimits, from #24293): { windows: [{ id: "session" | "weekly" | "weekly_opus" | "weekly_sonnet", usedPercent: 0-100, resetsAt?: unix seconds }] }. The pane dropped it: direct.ts built the summary with usageLimits: text(summary.usageLimits), which keeps only strings, and model.ts typed it string. So /status always showed "Usage limits: Not reported" for a Claude chat whose limits acpmux did report. The gallery hid this with a made-up string ("Weekly: 62% remaining").
What changes
model.ts: a typedAcpmuxUsageLimits; the summary field isAcpmuxUsageLimits | string.usageLimits.ts(new):usageLimitsOfkeeps a valid object and drops windows without a string id or a finiteusedPercent. A non-arraywindows, or no window left, isundefined, so the card still says Not reported. AresetsAtis kept only when it is a finite positive number.usageLimitsLabelformats the card value.direct.ts:usageLimits: usageLimitsOf(summary.usageLimits) ?? text(summary.usageLimits). A harness that sends plain text keeps it.HarnessStatusCard.tsx: the Usage limits row shows each window as "Session (5 hours): 38% used, resets Oct 31, 10:00 PM UTC · Weekly: 12.5% used". The reset time usesIntl.DateTimeFormatin the pane's language (usePaneLanguage) withtimeZoneName: "short". A window with noresetsAtgets no reset text and no guessed time. An unknown window id shows the raw id. Copy all and the per-value copy use the same rendered string.- Gallery
agent-pane.harness-status: Claude now uses acpmux's real object shape (a session window with a reset time, a weekly window without one). Codex has no usage limits, because acpmux doesn't map Codex's yet, so it reads Not reported. OpenCode keeps its "Not reported by ACP" string. - Six new pane strings in
acpmux/Localizable.xcstrings(21 locales:enandjatranslated, the restneeds_reviewwith real translations): the four window names, "{name}: {percent}% used" and "{name}: {percent}% used, resets {time}".
Out of scope: the rate-limited error card, retry and auto-resume are unchanged (failureCopy.ts untouched).
Tests
Red first: test-only commit 968cdbe9d17538911d6b530b4fd4e34d21889696, then the fix.
HarnessStatusCard.dom.test.tsx: the object renders the window names and percents, never "Not reported". The time zone is shown whenresetsAtis present, with exactly one "resets", and the weekly window gets none. Copy all's line equals the rendered value. An unknown id shows raw. Four malformed shapes read Not reported, and a plain string shows as it is.test/agent-pane-session-start.test.ts: the attach detail'susageLimitsreaches the summary. Bad windows are dropped. Malformed values (windowsnot an array, no valid window,null, a number) becomeundefined. A string is kept.
Command (from webviews/, Blacksmith Linux through cmux-ci test/lane-gh ci and again locally):
bun test src/agent-session/acpmux/HarnessStatusCard.dom.test.tsx test/agent-pane-session-start.test.ts src/agent-session/acpmux/HarnessStatusCard.test.ts
- Red at the test-only commit
968cdbe9d17538911d6b530b4fd4e34d21889696, with the fix files checked back to that commit (run 40980663198): 17 pass, 3 fail. "the usage limits acpmux reports show each window's use..." fails withExpected to not contain: "Not reported", "a usage limit window the card does not know shows its raw id" fails withReceived: "Not reported", and the summary plumbing test fails because acpmux's object was dropped. - Green at
9979e4dea7df(run 40981080457): 20 pass, 0 fail. Green again locally at the final head16235be2680e: 20 pass, 0 fail, 65 expect() calls. The final head adds only a formatting change to the test file.
Observed / Not run
- Observed:
scripts/web-lint.shpasses at the final head (Oxfmt, Oxlint, type check, layer and catalog-key checks; "web lint passed in 31.3s (webviews)"). The new xcstrings JSON parses, and each key carries all 21 locales. ./scripts/localize-changes --base origin/feat-cmux-nextran: "Prepared 0 new macOS key(s) ... Changed web English message keys: 0". The pane catalog is not one of its macOS targets, so I added the six keys by hand. Its "parity errors over the debt baseline" note comes from base's macOS catalogs; this PR touches none of them.- Not run: the full webviews suite (CI runs it), and a live Claude Code chat on a subscription account. The input shape comes from acpmux's
usage_limits.rsand #24293's hub tests.
Before / after
Not captured. scripts/gallery-compare.sh --entry agent-pane.harness-status cannot run from the lane VM that built this PR: it needs to reach the gallery host, and this VM cannot. Run it from an origin host to get a live before/after. The gallery entry is agent-pane.harness-status, variant claude.
- Before: the Claude variant's Usage limits row showed a made-up string ("Weekly: 62% remaining"). The real app showed "Not reported" for every Claude chat.
- After (the formatter's text output for the gallery's fixed input in an
en/ UTC run):Session (5 hours): 38% used, resets Oct 31, 10:00 PM UTC · Weekly: 12.5% used. The weekly window carries no reset text. Codex reads "Not reported". OpenCode keeps "Not reported by ACP".
Changelog
Fixed: the Session status card (/status) shows a Claude chat's plan usage limits (each window's share used, and its reset time with time zone when reported) instead of "Not reported".
Risk: risk:normal. Webview-only display change in one card plus a defensive parse; no protocol change.
Agent session: agent session
🤖 Generated with Claude Code