Session status card: show the usage limits acpmux reports instead of Not reported

TE
6d ago
+237-9

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 typed AcpmuxUsageLimits; the summary field is AcpmuxUsageLimits | string.
  • usageLimits.ts (new): usageLimitsOf keeps a valid object and drops windows without a string id or a finite usedPercent. A non-array windows, or no window left, is undefined, so the card still says Not reported. A resetsAt is kept only when it is a finite positive number. usageLimitsLabel formats 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 uses Intl.DateTimeFormat in the pane's language (usePaneLanguage) with timeZoneName: "short". A window with no resetsAt gets 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: en and ja translated, the rest needs_review with 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 when resetsAt is 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's usageLimits reaches the summary. Bad windows are dropped. Malformed values (windows not an array, no valid window, null, a number) become undefined. 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 with Expected to not contain: "Not reported", "a usage limit window the card does not know shows its raw id" fails with Received: "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 head 16235be2680e: 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.sh passes 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-next ran: "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.rs and #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