Hmbown / Hmbown/Codewhale

feat(tui): usage & tool diagnostics — token accounting (per-component, per-model + cache hit rate, per-tool sinks, compaction cost) and tool-call error patterns

Open
#6,011 8 comments 0 reactions 0 assignees View on GitHub
enhancement
Dominant language
Rust
Stars
41k
Forks
3.6k
Avg merge
13h 59m
Merged PRs (30d)
299

Description

## Combined Core execution: C11

[Core execution plan]() owns order and scope. This issue contributes to C11; the linked plan owns the complete packet and any outstanding candidate acceptance. Record this issue's path claims and implementation evidence here.

**Owner: diagnostics owner. Dependencies: C00; coordinate domain paths**
**with C03/C08.** Finish
[SHA-6414 / GitHub #6011]()'s seven admitted areas:
component/model/cache/tool/compaction/fleet/error diagnostics and persistence.
Credit existing counters and the retained usage-checkpoint implementation;
neither covers the full request. Implement against the current owning domain
and migrate the consumer with that domain, not a second reporting store.

**Completion evidence required:** every requested area has attributable receipts, persistence
and presentation tests; unknown usage/cost remains unknown; actual provider
charges are not inferred from estimates or completion timestamps.

---

## Original issue and contributor history

## Problem

Session cost and context are a black box. In-session surfaces exist (`/tokens`, `/cost`, `/context report`, the TUI Context panel) but they are **session-scoped and live only**: after the session ends there is no way to answer "where did my tokens/cache/money go over the last week", "which model is burning cache", or "which tool calls fail constantly". When a session eats 2M tokens you cannot tell whether it was replayed `read` output, cache-thrashing tool-schema churn, thinking spend, or a model re-running the same failing tool call. When the model "keeps making the same mistake" there is no report that names the tool + error signature so you can fix the tool description or the global `AGENTS.md`.

Affected: everyone who debugs token spend, tunes prompt-cache layout, or wants data-driven fixes to tool descriptions / `AGENTS.md`.

## Proposed solution

A global CLI `codewhale stats` that aggregates **persisted session data** (already on disk — see code refs) into token/cost/cache/tool reports, mirroring `opencode stats`:

```
codewhale stats [--days N] [--models N] [--tools N] [--project ]
```

* `--days N` — window (default: all time)
* `--models N` — top-N model usage breakdown (default: all)
* `--tools N` — top-N tool usage (default: all)
* `--project ` — filter by project/worktree

Report sections (mirroring opencode, plus improvements):

**A. Request composition (per-session, best-effort)**

1. **Per-component breakdown of the latest request**: system prompt, tool schemas (wire payload), message history, tool results, reasoning/ledger — mirroring VTCode's `token_budget_breakdown` and Claude Code's `/context`. Uses the existing conservative estimator (`estimate_input_tokens_conservative`); no tokenizer dependency required for the first pass.

**B. Lifetime accounting (global)**
2\. **Per-model usage + cost + cache hit rate**: input / output / cache read / cache write + $ per model + **hit rate %** (`cache_read/(cache_read+input+cache_write)`), plus **reasoning/thinking tokens as their own line** (Codewhale already tracks thinking replay tokens; opencode hides reasoning inside output — we can do better).
3\. **Prompt-cache statistics** (per session, aggregate): requests, share of input served from cache, misses with likely cause, expected rebuilds (compaction / tool-result clearing), warm/cold — mirroring Claude Code's `Prompt cache (main)` line. `/cache` today is per-turn DeepSeek-specific.
4\. **Per-tool / per-command token sinks**: `read`, `grep`, `bash`, `git diff`, `webfetch`, … — calls, tokens added to history, and how much was truncated/spooled (recoverable) vs replayed verbatim (the real leak). Sizes/truncation decisions already exist (`truncate.rs`, `fetch_url.rs`); missing is the ledger + aggregation.
5\. **Compaction cost as a metered line**: count, trigger (manual/auto/threshold), summarizer input/output tokens + cost, history tokens dropped. Codewhale already attributes compaction to a turn owner (`runtime_cost_owner`); this is reporting, not new plumbing — compaction looks "free" today but costs tokens+cache (Claude Code documents it).

**C. Reliability analytics (global)**
6\. **Tool-call error analytics**: per tool (and per model/provider): calls, failures, failure rate, error taxonomy (schema/validation, execution, timeout, policy/permission denial, empty/unusable result, parse failure), **retry recovery rate** (does the model correct itself or repeat the identical mistake), and recurring failure signatures across sessions. Actionable: fix a confusing tool description; put repeated bash mistakes into global `AGENTS.md`. Data exists (`ToolResult` error status, lifecycle outbox events); missing is the aggregation + patterns view.

**D. Sub-agent / fleet statistics (from the durable fleet ledger)**
7\. **Per-subagent and per-fleet analytics**: aggregate worker usage by **role** (built-in roles + custom names from `[fleet.roles]`) and per run/worker:

* calls, tokens, cost, and status counts (queued / running / completed / partial / failed / restarted / escalated / cancelled / stale);
* **failure sources** (verifier / transport / task) and retry/recovery rate — same "where do agents keep failing" signal as slice 6, but per delegated worker;
* grouping survives custom roles: the ledger records the role name at run time, so aggregation keys on that name even when `[fleet.roles]` changes later.
* Data already exists: `.codewhale/fleet.jsonl` ledger, `codewhale fleet status|inspect|logs|artifacts`, Runtime API `/v1/fleet/runs/{run_id}/workers`, and `SessionCostSnapshot.subagent_cost_usd/cny` for money already attributed to sub-agents.

## Use case

* `codewhale stats --days 30 --tools 10` → `bash` shows 60% of replayed tokens → switch to spooling/truncation or smaller commands.
* `codewhale stats --days 30 --models 5` → hit rate dropped 96% → 70% last week → something (tool-catalog churn, model/effort switches, compaction) is invalidating the cache; slice 3 names the cause.
* "The model keeps failing at X" → `codewhale stats --days 30 --tools 10` shows `bash` failing 30% with the same usage error → fix lands in `AGENTS.md`, next sessions stop repeating it.
* Extended thinking on → per-model reasoning split shows thinking is 40% of spend → tune `reasoning_effort`.
* `codewhale stats --days 30 --roles 5` (or `codewhale fleet stats`) → role `explore` shows 70% of sub-agent cost with 30% verifier-failure receipts → fix the explore role instructions / verifier scorers.

## Alternatives considered

* **opencode** `stats` — per-model tokens/cost/cache read-write with `--days/--tools/--models/--project`; but no cache hit rate (must hand-compute), no per-component request breakdown, no tool errors. Real output below — this is the target shape.
* **Claude Code** `/usage` **+** `/context` — closest: per-model usage, prompt-cache line with misses/expected rebuilds, category breakdown with optimization suggestions. Missing: per-tool sinks, compaction cost ledger, error analytics; and it's in-session only.
* **VTCode** `token_budget_breakdown` — per-request component metrics + spooling; closest on slice 1. Requires HF tokenizers for per-component tracking (follow-up option).
* **rtk-style shell hooks** — measure *pre-agent* trimming savings; cannot see actual in-context cost of built-in tools, no error patterns, requires an external wrapper. Real output below.
* Chosen: **persist + aggregate inside the runtime** — covers all built-in tools, measures real context cost, adds reliability signals no competitor has. In-session TUI `/tokens` enrichments remain an **open question** (see below).

## Impact

Every session; high value for token-spend debugging, cache tuning, and turning "model keeps failing" from anecdote into a data-driven fix. **All source data already exists and persists** — this is aggregation + presentation, not new capture.

## Additional context

**Precedent: opencode (real output,** `opencode stats --days 30 --models 8`**)**

This is the actual current output of `opencode stats` — per-model Cache Read / Cache Write are already rendered. **But there is no cache hit rate anywhere** — you must compute it by hand per model (e.g. deepseek-v4-flash: 3473.7 / (3473.7+110.1) ≈ **96.9%**):

```text
┌────────────────────────────────────────────────────────┐
│ OVERVIEW │
├────────────────────────────────────────────────────────┤
│Sessions 976 │
│Messages 68,219 │
│Days 30 │
└────────────────────────────────────────────────────────┘

┌────────────────────────────────────────────────────────┐
│ COST & TOKENS │
├────────────────────────────────────────────────────────┤
│Total Cost $14.49 │
│Avg Cost/Day $0.48 │
│Avg Tokens/Session 9.1M │
│Median Tokens/Session 1.4M │
│Input 445.4M │
│Output 23.8M │
│Cache Read 8402.2M │
│Cache Write 0 │
└────────────────────────────────────────────────────────┘

┌────────────────────────────────────────────────────────┐
│ MODEL USAGE │
├────────────────────────────────────────────────────────┤
│ commandcode/deepseek/deepseek-v4-flash │
│ Messages 25,371 │
│ Input Tokens 110.1M │
│ Output Tokens 14.7M │
│ Cache Read 3473.7M │ → hit ≈ 96.9%
│ Cache Write 0 │
│ Cost $0.0000 │
├────────────────────────────────────────────────────────┤
│ opencode/x-preview-f-free │
│ Messages 17,109 │
│ Input Tokens 162.6M │
│ Output Tokens 10.1M │
│ Cache Read 1941.9M │ → hit ≈ 92.3%
│ Cache Write 0 │
│ Cost $0.0000 │
├────────────────────────────────────────────────────────┤
│ opencode-go/deepseek-v4-flash │
│ Messages 10,460 │
│ Input Tokens 85.9M │
│ Output Tokens 7.9M │
│ Cache Read 1946.7M │ → hit ≈ 95.8%
│ Cache Write 0 │
│ Cost $14.4830 │
├────────────────────────────────────────────────────────┤
```

(Hand-computed hit rates annotated inline with `→`; opencode prints no such column.)

**Precedent: rtk hook (real output)**

Users already track token leakage manually with shell-wrapper hooks. This real report shows where tokens actually go across 89k commands (hook-side savings, i.e. what the wrapper trimmed before the agent saw it):

```text
RTK Token Savings (Global Scope)
════════════════════════════════════════════════════════════
Total commands: 89010
Input tokens: 2006.2M
Output tokens: 576.9M
Tokens saved: 1429.3M (71.2%)
Efficiency meter: █████████████████░░░░░░░ 71.2%

By Command
─────────────────────────────────────────────────────────────────────────
# Command Count Saved Avg% Time Impact
1. rtk read 2343 1030.7M 23.2% 45ms ██████████
2. rtk grep 12298 158.4M 23.9% 197ms ██░░░░░░░░
3. rtk git diff output/p... 3 105.2M 100.0% 1.2s █░░░░░░░░░
4. rtk git diff -- outpu... 1 36.5M 100.0% 1.2s ░░░░░░░░░░
5. rtk diff 27 35.2M 64.3% 153ms ░░░░░░░░░░
6. rtk find 535 24.3M 12.7% 789ms ░░░░░░░░░░
7. rtk:toml ps aux 286 8.9M 97.5% 51ms ░░░░░░░░░░
8. rtk:toml jq ... 5 4.4M 100.0% 288ms ░░░░░░░░░░
9. rtk:toml jq ... 11 3.2M 99.9% 75ms ░░░░░░░░░░
10. rtk psql postgresql:/... 1 2.6M 100.0% 652ms ░░░░░░░░░░
─────────────────────────────────────────────────────────────────────────
```

Key insight: **read** is the single biggest sink (1030.7M = 72% of all savings), then grep (158M), then git diff (105M) — the same tools dominate both hook-side savings and (we should verify) in-context cost. CW can measure the *actual* in-context footprint of its own built-in tools without any external hook.

**Code refs (data already exists)**

* Persisted session stats: `crates/tui/src/session_manager.rs` — `SavedSession` (`total_tokens`, `cost: SessionCostSnapshot` incl. session/subagent USD+CNY, high-water), sessions dir + journal (`#5262`).
* In-session surfaces: `/tokens`/`/cost` (`crates/tui/src/commands/groups/debug/tokens.rs`), `/cache` (`.../debug/cache.rs`), TUI Context panel (`crates/tui/src/tui/app.rs:1826` `context_panel`, `work_surface/views.rs`).
* Tool-result sizes/truncation: `crates/tui/src/tools/truncate.rs` (spillover + bounded preview), `crates/tui/src/tools/web/extract.rs` (`format=markdown|text|raw`).
* Compaction ownership: `crates/tui/src/core/engine.rs` (`runtime_cost_owner`), `crates/tui/src/runtime_threads.rs`.
* Thinking replay tokens: `crates/tui/src/client.rs` ("multi-turn thinking-mode conversation should report replay tokens").

**Related work**

* Shipped: [#5624]() (live session token totals), [#5623]() (post-compaction input tokens)
* Open, related: #5977 (tok/s audit), #5620 (agent doesn't react to context pressure), #5581 (event-granularity audit — feeds error analytics), #5479 (fleet/agents TUI view)
* Outbox/events (can feed error analytics): lifecycle outbox `[lifecycle_outbox]` in `docs/CONFIGURATION.md`
* Fleet ledger / status: `codewhale fleet status|inspect|logs|artifacts`, durable ledger `.codewhale/fleet.jsonl`, Runtime API `/v1/fleet/runs`, `/v1/fleet/runs/{id}/workers` (docs/FLEET.md "Status Surfaces"); `SessionCostSnapshot.subagent_cost_usd/cny` in `session_manager.rs`; `/fleet workers` = session sub-agents view (`/subagents` compat).

## Open questions (decide during implementation)

* **TUI surface**: `/tokens` (and subcommands like `/tokens models`, `/tokens tools`, `/tokens errors`) as in-TUI commands are an **open question** — the TUI Context panel already covers live session view, and the confirmed deliverable here is the **global** `codewhale stats` **CLI**. Whether the same reports should also be reachable from inside the TUI can be decided separately.
* **Duplicate / re-send waste ("token survival")** — how many tokens re-enter history verbatim (same file read twice, `git diff` + `read` of same files)? Measurable via content hashing (VTCode drops duplicate reads during compaction). Worth the complexity? Measure first, optionally dedupe later.
* **Tokenizer-estimate accuracy (unique)** — Codewhale uses \~4 chars/token and marks windows `(unverified)`. Accumulate estimate-vs-provider-actual bias per request. Nobody ships this; great for budget debugging. How much is derivable from TurnUsage receipts?

## Acceptance criteria

- [ ] `codewhale stats --days N` prints: sessions, messages, total cost, tokens (input/output/cache read/write), per-session average/median tokens
- [ ] `codewhale stats --models N` prints per-model rows: input/output, cache read/write, cost, **cache hit %**, **reasoning split**
- [ ] `codewhale stats --tools N` prints per-tool rows: calls, tokens added to history, truncated/spooled share, **failure rate + top error**
- [ ] `codewhale stats --project ` filters by project
- [ ] compaction ledger (count, trigger, summarizer tokens/cost, history dropped) visible in stats or a dedicated subcommand
- [ ] `--days`/`--models`/`--tools`/`--project` behave like opencode's flags
- [ ] sub-agent/fleet stats: per-role (incl. custom) and per-worker calls, tokens, cost, status counts, failure sources — in `stats --roles N` or `codewhale fleet stats`

Contributor guide

Open the contributing guide

Research direction

Start at the proposed `codewhale stats` CLI entry point and trace the retained usage-checkpoint implementation, `truncate.rs`, `fetch_url.rs`, `.codewhale/fleet.jsonl`, the fleet Runtime API, and `SessionCostSnapshot`. Map each of the seven diagnostic areas to existing persistence and presentation paths; done means every area has attributable receipts, persistence and presentation tests, with unknown usage and costs remaining unknown.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
analytics, cli, observability
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Active
Clarity
Needs clarification
Newbie friendliness
25/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.