Problem
The TUI status line permanently degrades to ctx ?/<window> (#3371 fallback) even for providers that report per-step usage, because the stored TokenUsageMessage never carries contextRemaining.
In packages/runtime/src/ai-sdk-backend.ts the turn-end usage write has two asymmetric paths:
The TUI rebuilds usage from stored messages in replaceTranscriptWithStoredMessages (packages/cli/src/pi-transcript.ts), so after any transcript reload (abort refresh, session resume, reconnect) contextRemaining is washed back to undefined and the status line falls back to ctx ?/<window> — even though the runtime event ledger (runtime_events.actions.tokenUsage) proves the value was computed (e.g. contextRemaining: 230068 for a 262144-window model).
cacheHit/cacheMiss survive the same rebuild (they are persisted), which is why cache NN% stays correct while ctx reverts to ?.
Expected
The persisted TokenUsageMessage carries contextRemaining (same conditional-spread value as the live event), so the ctx segment survives transcript rebuilds.
Scope
- Move
contextRemainingForUsage computation above the stored tu literal and add the field to it (schema already allows it: packages/core/src/usage-record-schema.ts, packages/core/src/session.ts; replay/read-model paths already preserve it: runtime-event-backfill.ts, runtime-event-read-model.ts).
- Extend the ai-sdk-backend usage tests to assert the stored message includes
contextRemaining.
Refs: #1067 (origin), #3371 (degrade-to-? fallback), #1422 (preserve token usage fields across replay).
Problem
The TUI status line permanently degrades to
ctx ?/<window>(#3371 fallback) even for providers that report per-step usage, because the storedTokenUsageMessagenever carriescontextRemaining.In
packages/runtime/src/ai-sdk-backend.tsthe turn-end usage write has two asymmetric paths:TokenUsageEventpushed to the session queue includescontextRemaining(added by fix(runtime): emit contextRemaining in token_usage events #1072).TokenUsageMessagewritten viaappendMessage(tu)does not — the field is absent from the object literal.The TUI rebuilds
usagefrom stored messages inreplaceTranscriptWithStoredMessages(packages/cli/src/pi-transcript.ts), so after any transcript reload (abort refresh, session resume, reconnect)contextRemainingis washed back toundefinedand the status line falls back toctx ?/<window>— even though the runtime event ledger (runtime_events.actions.tokenUsage) proves the value was computed (e.g.contextRemaining: 230068for a262144-window model).cacheHit/cacheMisssurvive the same rebuild (they are persisted), which is whycache NN%stays correct whilectxreverts to?.Expected
The persisted
TokenUsageMessagecarriescontextRemaining(same conditional-spread value as the live event), so the ctx segment survives transcript rebuilds.Scope
contextRemainingForUsagecomputation above the storedtuliteral and add the field to it (schema already allows it:packages/core/src/usage-record-schema.ts,packages/core/src/session.ts; replay/read-model paths already preserve it:runtime-event-backfill.ts,runtime-event-read-model.ts).contextRemaining.Refs: #1067 (origin), #3371 (degrade-to-
?fallback), #1422 (preserve token usage fields across replay).