fix(worker): record real raw token usage per run, not the uncached remainder

task_runs.tokens_in only ever held the API's uncached "input_tokens" field
(off by a factor of ~400,000 on a resumed session), and tokens_out summed
only the last result event instead of the whole session. TaskRunner now
reads each run's cache-read/cache-write/input/output totals from the
session transcript via a new ITranscriptUsageReader.ReadSessionTotalsAsync,
storing the delta against prior runs on the same session so a --resume
doesn't double-count. New task_runs.cache_read_tokens/cache_write_tokens
columns; the Session tab now shows the raw total (what actually counts
against the 5h/7d usage limit) with a breakdown tooltip.
This commit is contained in:
mika kuns
2026-08-05 15:44:20 +02:00
parent 83ea429b8a
commit 7d3d6d7b54
28 changed files with 1318 additions and 20 deletions
@@ -219,6 +219,7 @@ public sealed partial class DetailsIslandViewModel : ViewModelBase, IDisposable
[ObservableProperty] private string? _branchLine;
[ObservableProperty] private int _turns;
[ObservableProperty] private int _tokens;
[ObservableProperty] private string? _tokensBreakdown;
[ObservableProperty] private int _diffAdditions;
[ObservableProperty] private int _diffDeletions;
[ObservableProperty] private int _commitsOnBranch;
@@ -619,7 +620,15 @@ public sealed partial class DetailsIslandViewModel : ViewModelBase, IDisposable
// Restore turn/token counts from the last run so a reloaded terminal task
// shows its real turns instead of "0/max".
Turns = latestRun?.TurnCount ?? 0;
Tokens = (latestRun?.TokensIn ?? 0) + (latestRun?.TokensOut ?? 0);
// Raw total = what actually counts against the 5h/7d Claude usage limit: cached
// context resend (cache-read + cache-write) dwarfs fresh input/output on a
// resumed session, so it must be included, not just the uncached input/output.
var tokensIn = latestRun?.TokensIn ?? 0;
var tokensOut = latestRun?.TokensOut ?? 0;
var cacheRead = latestRun?.CacheReadTokens ?? 0;
var cacheWrite = latestRun?.CacheWriteTokens ?? 0;
Tokens = tokensIn + tokensOut + cacheRead + cacheWrite;
TokensBreakdown = $"in {tokensIn} · out {tokensOut} · cache-read {cacheRead} · cache-write {cacheWrite}";
Monitor.ApplyOutcome(entity.Result, latestRun?.ErrorMarkdown);
Monitor.SetTaskId(row.Id);
@@ -115,6 +115,11 @@
Foreground="{DynamicResource TextMuteBrush}" />
<TextBlock Classes="meta" Text="·"
Foreground="{DynamicResource TextFaintBrush}" />
<TextBlock Classes="meta" Text="{Binding TokensFormatted}"
Foreground="{DynamicResource TextMuteBrush}"
ToolTip.Tip="{Binding TokensBreakdown}" />
<TextBlock Classes="meta" Text="·"
Foreground="{DynamicResource TextFaintBrush}" />
<TextBlock Classes="diff-add" Text="{Binding DiffAddText}" />
<TextBlock Classes="diff-del" Text="{Binding DiffDelText}" />
</StackPanel>