For a billing period that has recorded no usage, xAI omits `creditUsagePercent` and `productUsage` from the credits snapshot instead of reporting zero. The adapter treated the resulting empty window list as an upstream failure, so a freshly rolled-over period surfaced as 'usage is temporarily unavailable' on the device.
Emit the period window with no percentage and an explicit detail line whenever the period bounds are known: the window, its label, and its reset time are real, only the figure is absent. A payload carrying neither a figure nor period bounds still reports unavailable, so a genuinely broken upstream contract is not masked.
Verified against a rolled-over weekly period that previously produced the error state; the surface now reports the window with its reset time.
Hosts signed in with `xai-oauth` have a Grok subscription whose windows are
only served by xAI's CLI proxy, not the public API, so the provider-neutral
usage surface could not see them.
Add a `supergrok` adapter that reads the account identity and then the credits
billing snapshot over the pinned `cli-chat-proxy.grok.com` contract with the
host-side OAuth bearer: the current billing period, per-product usage, and
on-demand credit state map onto the existing window/detail shape. Hosts with
no `xai-oauth` credential report `not_configured`, upstream failures degrade to
`unavailable`, and the bearer never enters the response.
Preserve WebSocket query strings (auth tickets) when proxying dashboard
and Gateway sockets, disable nested permessage-deflate on the upstream
leg, scope dashboard login HTML/JSON and redirects under /dashboard
without double-prefixing, register bare /api and /dashboard routes, and
include version on /relay/health for route probes.
Every android_* tool returned `401 Client Error: Unauthorized` on a phone
that was paired, connected and healthy (`/bridge/status` reported
phone_connected: true, accessibility_granted: true).
Cause: `_bridge_token()` read ANDROID_BRIDGE_TOKEN from the process
environment only. The env is snapshotted when the host starts, so a token
written afterwards — by `android_setup`, by `hermes-pair`, or by hand —
stayed invisible and `_require_bearer_session` rejected every bridge
dispatch until a full restart.
Resolution order is now env -> ~/.hermes/.env -> most-recently-seen paired
session in ~/.hermes/hermes-relay-sessions.json, with the disk fallbacks
cached for 30s so we don't stat two files on every bridge call. The process
environment still wins when set, and HERMES_HOME is honoured.
The documented recovery path was itself broken: android_setup's schema
required `pairing_code` while the signature had been renamed to
`bridge_session_token`, and the dispatcher calls func(**call_args) — so the
schema-conformant call raised TypeError and the canonical one was rejected
by the validator. The tool could not be invoked at all. Both spellings are
now accepted (canonical wins), neither is schema-required, a missing token
returns a structured error, and the description no longer mislabels the
value as a "6-character pairing code" or claims the tool performs pairing.
Also pin TestSetup to a temporary home. It exercises the real
android_setup, which persists ANDROID_BRIDGE_* to ~/.hermes/.env — running
the suite on a real host overwrote the machine's live paired session token
with a fixture value.
Verified: 43 passed (test_android_tool.py + test_android_tool_device_selector.py),
and against the physical device with ANDROID_BRIDGE_TOKEN unset from the
environment — /ping and /current_app both 200 via the new disk fallback.
Commit-message generation reuses the upstream async LLM helper via the plugin's deferred-import pattern; empty staged diffs never call the model and failures degrade to an empty message plus notice. stash_checkout auto-stashes a dirty tree before switching (recoverable; stash surfaced as a notice). Push-after-commit toggle auto-starts the push confirmation flow without bypassing the confirmation token. Truncation caps consistent across all bounded endpoints. New UI strings localized across the 12-catalog parity gate.
Plugin endpoints under /api/plugins/hermes-relay/git/* backed by a scanned-repo allowlist with configurable base path; bounded responses with truncation flags; traversal-rejected file reads returning working-tree content with clear binary/non-UTF-8 errors; remote URLs scrubbed of userinfo; zero shell interpolation. Registers the Git mobile plugin page and a read-only dashboard tab; Android renders the surface via a dedicated Compose screen with view model and unit tests; all locale catalogs refreshed.