feat(advisor): TUI status bar + legacy REPL show advisor token spend separately - #191
Merged
Merged
Conversation
…us bar
The bottom toolbar already shows `tokens: N in / N out` for the
worker (haiku-4-5 in the user's setup). Now it also shows
`(advisor: N in / N out)` next to it whenever the advisor has
been consulted, so the user can see how much of the spend went to
the reviewer (opus-4-7) versus the worker.
## Display
```
openai · claude-haiku-4-5 · ~/workspace/clawcodex · advisor: opus-4-7 (client) · turns: 2 · tokens: 5400 in / 320 out (advisor: 1326 in / 282 out)
```
The `(advisor: N in / N out)` segment is suppressed when zero so the
toolbar stays compact for users who haven't enabled the advisor or
haven't triggered it yet this session.
## Data flow
1. `execute_client_advisor` (in `src/utils/advisor.py`) now returns
a 3-tuple `(ok, text, usage)` instead of `(ok, text)`. The
`usage` dict has `input_tokens` / `output_tokens` keys pulled
off the provider's ChatResponse. Failure paths return zero usage.
2. `AdvisorTool._advisor_call` (in `src/tool_system/tools/advisor.py`)
reads the 3-tuple and ACCUMULATES the counts onto
`tool_context.advisor_input_tokens` / `advisor_output_tokens`.
Multiple consultations in one session add together (not replace).
3. `ToolContext` gains two new int fields with sane defaults; the
bookkeeping is wrapped in try/except so a status-bar concern can
never break a tool result.
4. `src/repl/core.py:_bottom_toolbar` reads the ctx fields and
appends the advisor segment when non-zero.
## What didn't change
* TUI StatusLine (`src/tui/widgets/status_line.py`) — left as a
follow-up. The TUI reads token counts from AppState.usage which
doesn't yet have an advisor-specific key. Adding the AppState
field + watcher is a separate ~3-file edit. Mention it if it's
needed.
* Server-side advisor path — the worker's response already includes
`usage.iterations[]` with per-model breakdowns (per the
Anthropic API spec in the user-shared example). We don't parse
those yet; if the user moves to a 1P Anthropic main + server-side
advisor, the counts won't show. Followup.
## Tests
* New: `test_call_accumulates_advisor_tokens_onto_ctx` —
verifies two sequential AdvisorTool.call invocations sum the
token counts rather than overwriting.
* Updated 8 destructuring sites + 2 mock returns in
`test_advisor_client_side.py` for the 3-tuple signature.
* Full advisor (117) + TUI (525) suites pass.
## Live verified
```
>>> ok, text, usage = execute_client_advisor('claude-opus-4-7', fwd, main_provider=provider)
ok=True, usage={'input_tokens': 663, 'output_tokens': 141}
```
Real opus-4-7 call via litellm.singula.ai returned correct usage.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
PR #190's content (cherry-picked from advisor/separate-token-counts — `AdvisorTool` accumulates client-side advisor tokens onto `tool_context.advisor_input_tokens/advisor_output_tokens`; the legacy REPL bottom toolbar reads them) provides the data. This commit adds TUI parity: the StatusLine widget shows the same counts. ## What this adds on top of PR #190 1. `src/tui/agent_bridge.py` (`_run_agent_in_thread`) — after the run completes, mirrors `tool_context.advisor_input_tokens` / `advisor_output_tokens` into `state.usage["advisor_input_tokens"]` / `["advisor_output_tokens"]` so the StatusLine widget can read them. Cumulative across the session (the TUI's tool_context persists across runs). 2. `src/tui/widgets/status_line.py` (`_compose_text`) — reads the two new keys off `state.usage` and appends `advisor N/N` to the right-side bits when non-zero. Worker-token display (`tokens TOTAL`) is unchanged to keep the diff minimal. ## Display TUI status line now shows e.g.: ``` openai · haiku-4-5 · advisor: opus-4-7 (client) Synthesizing 4s ~/workspace turn 2 · tokens 5720 · advisor 1326/282 ``` ## What's NOT in this PR (intentional) Server-side advisor token attribution. The user-shared `security_audit_advisor.py` example claims `usage.iterations[*].type == "advisor_message"` distinguishes advisor iterations — but the Python SDK 0.88.0 we depend on only defines `"message"` and `"compaction"` discriminators on `BetaIterationsUsageItem` (verified in `.venv/lib/.../beta/beta_iterations_usage.py` + `beta_message_iteration_usage.py`). And `BetaServerToolUsage` only has `web_fetch_requests` / `web_search_requests` — no advisor field. I drafted a parser for the `"advisor_message"` discriminator earlier in this PR but the critic correctly flagged it as non-functional — Pydantic's tagged-union discriminator would reject any value outside the two literals. Removed. When the SDK gains the advisor discriminator OR when we move to direct HTTP parsing of the API response (bypassing typed Pydantic parsing), add a second accumulator alongside the client-side flow. The TUI display logic is already in place — it just needs a second data source to feed `state.usage["advisor_*"]`. ## Critic-driven fixes applied * **Bundled PR #190** into this PR per the user's "both followups, in one PR" instruction. Cherry-picked the commit verbatim; this PR builds on top. * **Dropped server-side iteration parsing** after verifying the SDK shape doesn't match the speculative `"advisor_message"` literal. * **Kept the existing `tokens TOTAL` format** in the TUI (didn't switch to `tokens N/N`) — avoids a user-visible format change in an unrelated part of the status line. The new advisor segment is `advisor N/N` for compactness; the asymmetry vs the worker total is acceptable as long as it's the only thing changing. ## Tests * 665 passed across full advisor + adapter + TUI + helpers + smoke suites. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This was referenced May 21, 2026
singlaamitesh
pushed a commit
to singlaamitesh/clawcodex
that referenced
this pull request
Jul 7, 2026
…okens-and-iterations feat(advisor): TUI status bar + legacy REPL show advisor token spend separately
singlaamitesh
pushed a commit
to singlaamitesh/clawcodex
that referenced
this pull request
Jul 7, 2026
…fficient /advisor) Two new entries at the top of the README News section: 1. Codebase stats: 890 files / 183,768 lines (up from 894 / 177,428 on 2026-05-16; net +6.3k lines / -4 files in five days). The file delta is the agent_loop.py consolidation; the line additions are the /advisor multi-provider rewrite + status-bar cost work. 2. /advisor mode as a token-efficient coding agent — narrates the PR agentforce314#181 → agentforce314#193 arc as one story: - Cheap worker (haiku-4-5) + expensive reviewer (opus-4-7) only at decision points ≈ 6× cheaper than opus-only on typical sessions - Explicit <provider>:<model> syntax (agentforce314#192) - Cross-provider routing verified (deepseek worker + opus advisor via litellm, agentforce314#182/agentforce314#184/agentforce314#192) - Reviewer-quality prompt (agentforce314#188) — Gaps/Risks/Do-next format - Live cost + token visibility in status bar (agentforce314#190/agentforce314#191/agentforce314#193) - /advisor slash command + dedicated TUI row (agentforce314#181) Docs-only — no code or test changes. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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
Bundles two followups from PR #189 into one PR per user request:
Both surfaces now display advisor token spend whenever the client-side advisor has been consulted this session.
Display
Legacy REPL bottom toolbar (from PR #190's cherry-picked content):
```
openai · haiku-4-5 · ~/workspace/clawcodex · advisor: opus-4-7 (client) · turns: 2 · tokens: 5400 in / 320 out (advisor: 1326 in / 282 out)
```
TUI status line (new):
```
openai · haiku-4-5 · advisor: opus-4-7 (client) Synthesizing 4s ~/workspace turn 2 · tokens 5720 · advisor 1326/282
```
Both segments hidden when zero.
Data flow
`AdvisorTool._advisor_call` (PR #190 content) accumulates per-consultation token counts onto `tool_context.advisor_input_tokens` / `advisor_output_tokens`. Two surfaces consume them:
What I attempted and dropped
I initially also implemented server-side advisor token attribution — parsing `usage.iterations[]` for entries with `type == "advisor_message"`, per the user-shared `security_audit_advisor.py` reference + the Anthropic API spec. Critic verified the SDK shape and found:
The reference example was either documentation for a future SDK release or a different parser layer. With the SDK as shipped, my parser would never match a real response. Dropped the speculative iteration parser + the test for it; left a NOTE in `agent_bridge.py` documenting what's needed when the SDK gains the discriminator.
Tests
Closes
🤖 Generated with Claude Code