[Bug]: Context meter stays pinned after a successful compaction once the condenser has its own usage entry
Steps to Reproduce
Run method: npm run dev (equally visible on app.all-hands.dev/canvas)
- Start a conversation and let the context grow large.
- Use "Compact context" and wait for it to finish.
- Open the Usage drawer and look at the context meter.
Unit-level repro of the merge logic: feed combineUsageMetrics two usage entries - default with per_turn_token: 2000 (agent, post-compaction) and condenser with per_turn_token: 8000 (its last read of the full pre-compaction history). The combined usage reports 8000 while the agent's context is 2000.
Actual Behavior
After a successful compaction, the context meter keeps showing the pre-compaction fill, and the compaction flow reports that no tokens were freed. This starts with the first compaction in a conversation and repeats on every later one.
Root cause
stats.usage_to_metrics carries one entry per LLM usage id - the agent's own entry (default) plus secondary ones such as condenser. Both frontend paths that combine them take per_turn_token as a max across entries:
combineUsageMetricsinsrc/utils/conversation-metrics.ts- the stats reducer in
src/contexts/conversation-websocket-context.tsx(updateMetricsFromStats)
The condenser's accumulated_token_usage.per_turn_token is the size of the condenser LLM's own last call. That call reads the full pre-compaction conversation, so after the first condensation the condenser entry sits at roughly the old context size and stays there until the next condensation. Once the agent compacts and its own per_turn_token drops, the max keeps reporting the condenser's stale, larger value.
Two visible consequences:
- The context meter (
ContextMeterreads the merged value viauseContextWindowUsage) keeps showing the pre-compaction fill. useAwaitContextCompactiononly reports "compacted" when the storedper_turn_tokenfalls below the snapshot taken when compaction was requested. The merged value never drops, so a successful compaction is reported as "no_change".
This is the flip side of #13939, which moved the merge from last-wins to max to fix a dormant (all-zero) condenser entry pinning the meter at 0%. Max fixes the dormant case but breaks the post-compaction case: a condenser that has run once is no longer zero, it is stale-large.
Expected Behavior
The context meter and the compaction result reflect the agent's own context size after a compaction.
Acceptance Criteria
- After a successful "Compact context", the context meter shows the post-compaction fill.
- A successful compaction is reported as compacted (not "no_change").
- The dormant all-zero condenser case fixed in #13939 still behaves (meter not pinned at 0% before the first compaction).
Suggested fix
Take per_turn_token (that field only) from the primary default usage entry when it is present, and keep the current max as the fallback when it is not. Token sums and context_window are unaffected.
Source: OpenHands/OpenHands