fix(usage): measure server-side fallback replies by the last run (#877)

IC
7h ago
+432-3

Approved issue: #877

Harness coverage (required)

For each entry select: covered / not applicable — surface does not exist there / NOT covered.

  • OpenCode 1: NOT covered. The message and part events that reach the usage handler on OpenCode 1 do not carry the reasoning signature, so the marker is not available without another database read. The issue discussion agreed to keep OpenCode 1 out of this PR.
  • OpenCode 2: covered.
  • Pi: not applicable — Pi does not use server-side fallback, so Pi steps are never summed.
  • OMP: not applicable — same as Pi.
  • Rust module: NOT covered — this change is in the OpenCode 2 TypeScript usage path (v2/hooks/usage-reading.ts, called from v2/hooks/context.ts). The module was not changed or checked.

What changes

Anthropic's server-side fallback reruns a refused request on the fallback model and reports the usage of both runs added together. Magic Context recorded that sum as context pressure.

On OpenCode 2, resolveUsageReading now reads the reasoning signature of the reply:

Reply carriesRecorded pressure
Per-run usage marker (anthropic-auth 2.5.8 and later)input_tokens + cache_read_input_tokens + cache_creation_input_tokens of the last fallback_message entry. A missing cache count is 0.
Boundary marker only (older anthropic-auth)No change to the previous reading. Only the response time moves, the same as a completed reply without input usage.
Per-run usage marker that is malformed, or that has no fallback_message entrySame as boundary only, with one log line.
No markerUnchanged.

The billed top-level usage is not changed, so cost stays correct. The signature is read from native.signature (OpenCode 2.2 and later) and from state.signature (OpenCode 2.0.x). The two prefixes are named constants in shared/provider-response-completion.ts.

Commits

  1. fix(usage): ignore summed usage from server-side fallback replies: the boundary-only rule.
  2. fix(usage): read fallback pressure from per-run usage: the per-run usage rule from the issue discussion.

Tests

  • Parser: the usage marker in native.signature and in state.signature, the last fallback_message entry wins, missing cache counts are 0, and five malformed payloads take the boundary-only path.
  • Persistence: a usage-marker reply records the last-run pressure and the map entry, not the sum. A boundary-only reply keeps the previous reading and moves the response time. The next normal reply records normally.
  • Actual OpenCode 2 hook (server-fallback-usage.test.ts): replies with the boundary marker, the usage marker in each location, invalid JSON and a missing fallback_message entry admit at the per-run pressure without execute. The control reply without a marker still enters the force band.
  • The new tests failed before the change: 14 failures in the focused set, 4 in the hook file. When the usage prefix was changed to a wrong value, 16 tests failed, including both hook cases with the usage marker.

Results on this branch (Bun 1.4.0):

  • provider-response-completion, usage-reading, usage-persist, server-fallback-usage: 34 pass, 0 fail.
  • src/v2/hooks and src/v2/fold: 605 pass, 5 skip, 0 fail.
  • bun run typecheck, Biome on the changed files, and git diff --check: clean.

Summary by cubic

Fixes #877: context pressure on OpenCode 2 coming from Anthropic server-side fallback replies now reflects the last run's usage instead of the billed sum, so sessions no longer jump into the force band.

Changes

  • On anthropic-auth 2.5.8 and later, the reply carries a per-run usage marker in native.signature (OpenCode 2.2+) or state.signature (2.0.x); the pressure is the last fallback_message entry's input + cache read + cache write, with missing cache counts defaulting to 0.
  • Replies with only the older boundary marker, or with a malformed usage marker, keep the previous pressure reading and move only the response time, logging one line.
  • Replies without a marker are unchanged, and the billed top-level usage is unchanged. OpenCode 1 is not covered because its message events lack the marker.

RetriggerView in GreptileConfidence Score: 5/5

The PR appears safe to merge; no actionable issues were found.

Summary

OpenCode 2 now measures fallback replies using the last fallback_message, rather than the billed total across runs.

  • OpenCode 2 measures fallback replies by the last run.

Diagram

%%{init: {'theme': 'neutral'}}%%
flowchart TD
  A[Latest stored assistant reply] --> B{Reasoning signature}
  B -->|Valid per-run usage| C[Read last fallback_message]
  C --> D[Save pressure and reply time]
  B -->|Boundary or invalid usage| E[Keep pressure and update reply time]
  B -->|No marker| F[Use normal token counts]
  F --> D

Reviews (1) · Last reviewed commit: "fix(usage): read fallback pressure from ..." · Reviewed by Greptile