You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Context visibility: accurate per-thread context breakdown ('what is eating my context') #317
The single most-upvoted cross-tool feature cluster (opencode 131+100+50 upvotes across three issues; Claude Code's statusline ecosystem exists solely for this). Also: an INACCURATE meter is worse than none — T3's context ring stuck at 5% then jumped to 100%; Conductor's stuck at 0% after model change.
Extend contextRing.ts into a per-thread breakdown (system prompt, memory, tool outputs, transcript, reserve), verify accuracy per provider against the CLI's own usage reading, show rate-limit reset times on hover (Conductor's pattern), and warn before compaction. If a provider cannot be measured accurately, show no number.
Research note (Conductor 0.75): the density pattern to copy — a hoverable context ring around the composer revealing account rate limits, reset times, session token usage, and a near-limit warning. Ambient info, zero clicks.
Shared contract landed on main first (SessionUsage.contextTokens = full prompt incl. cache tokens, SessionUsage.contextWindow = CLI-reported window), then two grok workers in parallel:
electron/: accuracy. Claude's result.usage.input_tokens omits cache_read_input_tokens/cache_creation_input_tokens, so under prompt caching the ring reads ~0-5% then jumps — the exact failure mode this issue names. Also codex total_token_usage (cumulative) being added as a per-turn delta, and model_context_window plumbed through so a model change can't stale the static catalog. Providers that can't be measured stay unset — no number rather than a wrong one.
src/: the breakdown. New pure contextBreakdown.ts (tool output / transcript / system+tooldefs remainder, chars/4 estimate, measured total authoritative), hover popover on the ring, near-limit compaction warning.
Deliberately out of this cut: account-level rate-limit/reset popover (the Conductor/Waku comments). It needs provider quota endpoints, not per-thread token accounting — separate slice. The electron worker is reporting whether the CLIs already emit rate-limit fields.
The old formula (input + output) read 185 tokens; the true prompt is 28,136. A 152× under-read — that is the "stuck at 5% then jumps to 100%" failure, in our own code. A mid-session thread in a real transcript showed input_tokens: 2 against cache_read: 154815.
Second bug, found by scanning the live store rather than the tests. Every claudemodelInfo entry was missing contextTokens, so contextWindowFor returned null and the ring never rendered for claude threads at all — 161 usage rows with a measured numerator and no denominator. Added the documented windows (1M for Fable 5 / Opus 5 / Sonnet 5, 200k for Haiku 4.5).
What landed
Claude (and grok, which runs the same claude-stream path): contextTokens = input + cache-read + cache-creation + output. Billable in/out counters unchanged.
Codex: last_token_usage is the turn delta, total_token_usage is session-cumulative and was being added as if it were a delta; model_context_window now flows into SessionUsage.contextWindow and beats the static catalog.
Kimi: reports too little to measure a full prompt, so contextTokens stays unset and the ring hides. No guessed numbers anywhere.
Renderer: contextBreakdown.ts (tool output / transcript / system+tool-defs remainder, chars÷4, labelled as an estimate — the measured total stays authoritative), hover popover on the ring, amber near-limit warning at 85%.
Tests: renderer 926 pass / 0 fail; electron 1166 pass / 1 fail (the pre-existing web-integration reconnect failure, baseline on main). Added test/contextSeam.test.ts covering the electron→renderer join, since the two slices were built in parallel against mocks of each other; it pins the regression at 14% vs the pre-fix 0%.
Not in this cut: the account-level rate-limit/reset popover from the Conductor and Waku comments. It needs provider quota endpoints (Claude OAuth usage, ChatGPT wham/usage), not per-thread token accounting — worth its own issue.
Also filed #441: electron/providers.js contains a literal NUL byte, which makes grep silently return zero matches on that file. Cost me three false conclusions while working on this.
Activity
currentbits commentedon Aug 17, 2026
Research note (Conductor 0.75): the density pattern to copy — a hoverable context ring around the composer revealing account rate limits, reset times, session token usage, and a near-limit warning. Ambient info, zero clicks.
currentbits commentedon Aug 17, 2026
Deep-dive detail (Waku): account-level plan usage popover — surface each provider's account quota (Claude OAuth usage endpoint incl. beta header, ChatGPT wham/usage) in one UI, beyond per-thread token counting.
currentbits commentedon Aug 18, 2026
Orchestration plan (thread a8891f43):
Shared contract landed on main first (
SessionUsage.contextTokens= full prompt incl. cache tokens,SessionUsage.contextWindow= CLI-reported window), then two grok workers in parallel:result.usage.input_tokensomitscache_read_input_tokens/cache_creation_input_tokens, so under prompt caching the ring reads ~0-5% then jumps — the exact failure mode this issue names. Also codextotal_token_usage(cumulative) being added as a per-turn delta, andmodel_context_windowplumbed through so a model change can't stale the static catalog. Providers that can't be measured stay unset — no number rather than a wrong one.contextBreakdown.ts(tool output / transcript / system+tooldefs remainder, chars/4 estimate, measured total authoritative), hover popover on the ring, near-limit compaction warning.Deliberately out of this cut: account-level rate-limit/reset popover (the Conductor/Waku comments). It needs provider quota endpoints, not per-thread token accounting — separate slice. The electron worker is reporting whether the CLIs already emit rate-limit fields.
currentbits commentedon Aug 18, 2026
Done on
coder/context-visibility-accurate-per-thread-c-a8891fThe core bug, measured. Captured a verbatim
result.usagefrom the live Claude CLI:{"input_tokens": 10, "cache_creation_input_tokens": 10203, "cache_read_input_tokens": 17748, "output_tokens": 175}The old formula (
input + output) read 185 tokens; the true prompt is 28,136. A 152× under-read — that is the "stuck at 5% then jumps to 100%" failure, in our own code. A mid-session thread in a real transcript showedinput_tokens: 2againstcache_read: 154815.Second bug, found by scanning the live store rather than the tests. Every
claudemodelInfoentry was missingcontextTokens, socontextWindowForreturned null and the ring never rendered for claude threads at all — 161 usage rows with a measured numerator and no denominator. Added the documented windows (1M for Fable 5 / Opus 5 / Sonnet 5, 200k for Haiku 4.5).What landed
contextTokens= input + cache-read + cache-creation + output. Billable in/out counters unchanged.last_token_usageis the turn delta,total_token_usageis session-cumulative and was being added as if it were a delta;model_context_windownow flows intoSessionUsage.contextWindowand beats the static catalog.contextTokensstays unset and the ring hides. No guessed numbers anywhere.contextBreakdown.ts(tool output / transcript / system+tool-defs remainder, chars÷4, labelled as an estimate — the measured total stays authoritative), hover popover on the ring, amber near-limit warning at 85%.Tests: renderer 926 pass / 0 fail; electron 1166 pass / 1 fail (the pre-existing web-integration reconnect failure, baseline on main). Added
test/contextSeam.test.tscovering the electron→renderer join, since the two slices were built in parallel against mocks of each other; it pins the regression at 14% vs the pre-fix 0%.Not in this cut: the account-level rate-limit/reset popover from the Conductor and Waku comments. It needs provider quota endpoints (Claude OAuth usage, ChatGPT wham/usage), not per-thread token accounting — worth its own issue.
Also filed #441:
electron/providers.jscontains a literal NUL byte, which makes grep silently return zero matches on that file. Cost me three false conclusions while working on this.