feat(status): hide cache section on backends that don't report it - #238
Merged
Conversation
Per-backend `cacheMetrics` capability flag added to the `QueryBackend` interface: `"none" | "read" | "readwrite"`. Each backend declares what it can actually surface, so /status stops printing fake "0% hit Β· Read 0 Write 0" on backends that don't report cache telemetry. Shared `buildCacheDisplay()` in `src/frontend/status-context.ts`: returns `null` when `cacheMetrics === "none"`, returns a `CacheDisplay` otherwise. Frontends (Telegram, Discord, Teams, Terminal) drop the Cache block entirely when null. `showsWrite` decides whether the write count is rendered when the backend only reports reads. While restructuring, fixed a regression in the cache-hit-% formula: the new helper was using `read / (input + read + write)`, which dilutes the hit ratio with cache_write tokens β those are tokens being *written to* cache on this turn, not served from it. Now matches the canonical formula in `src/backend/shared/usage.ts:cacheHitPercent`: `read / (input + read)`. The shared usage helper, the old per-frontend inline calc on Telegram, and now `buildCacheDisplay` all agree. Backend declarations: - Claude SDK / Kilo / OpenCode β `"readwrite"` (full telemetry) - Codex / OpenAI Agents / Antigravity β `"read"` (provider reports cached input only, no write side) - agy β `"none"` (Gemini CLI doesn't expose cache counters) Tests added in `status-context.test.ts` cover all three modes plus the zero-input edge case, and explicitly assert the canonical hit-pct formula (was previously encoding the buggy 29% for readwrite mode).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
/statuscurrently printsCache 0% hit Β· Read 0 Write 0on backends that don't report cache telemetry (Codex when no real cache hits, agy entirely), which reads as "the backend has no cache hits" when the truth is "the backend doesn't report cache usage at all." This adds a per-backend capability flag and renders nothing when there's nothing to render.Changes
Capability flag on
QueryBackend(src/core/types.ts):Shared
buildCacheDisplay()(src/frontend/status-context.ts): returnsnullwhen the backend'scacheMetrics === "none", otherwise aCacheDisplay { hitPct, read, write, showsWrite }. Frontends drop the entire Cache block when null.Per-backend declarations (each
factory.ts):"readwrite"(full telemetry)"read"(provider reports cached input only, no write side)"none"(Gemini CLI doesn't expose cache counters)Frontends rewired β Telegram, Discord, Teams, Terminal all use
buildCacheDisplay().showsWritecontrols whether the write count is rendered.Hit-pct formula correction
While restructuring, caught a regression that the initial
buildCacheDisplaywould have shipped: the helper usedread / (input + read + write)as the denominator. That's the pre-PR-#11 bug βcache_writeis tokens being written to cache this turn, not served from it, and including them dilutes the hit ratio every time the cache is being warmed.Now matches the canonical formula in
src/backend/shared/usage.ts:cacheHitPercentand the existing Telegram inline calculation:read / (input + read). Three places, one rule.Tests
src/__tests__/status-context.test.tsβ 5 new cases:cacheMetrics: "none"β returnsnull(block hidden)cacheMetrics: "read"β write zeroed,showsWrite: false, correct hit-pctcacheMetrics: "readwrite"β explicit assertion thatcacheWritedoesn't dilute hit-pct (this is the formula-regression guard)"read"ignores any reported write countstatus-context.test.ts7/7 β Β·terminal-renderer.test.ts59/59 β Β·npx tsc --noEmitclean Β·npm run lintno new warnings Β·prettier --checkclean across touched files.What this fixes in practice
On Codex with
cached_input_tokens = 0(typical mid-session before any prompt-cache hits land),/statuspreviously displayedCache 0% hit Β· Read 0 Write 0β visually identical to "I have a working cache that just isn't being hit." Now Codex still shows the Cache block (it does report reads when they happen), but on agy β which exposes nothing β the block disappears entirely instead of lying.For Claude SDK / Kilo / OpenCode the display is unchanged in behaviour (full read+write), but the hit-pct formula correction makes it match the shared usage helper and the historical 5560eea fix.
Closes the "Maybe we can open a PR so that the caching information will show dynamically if the backend advertises support for it" ask from chat earlier today.