Skip to content

feat: subagent attribution + optional ccusage integration (5h windows, burn rate, multi-agent)#140

Closed
john988 wants to merge 8 commits into
phuryn:mainfrom
john988:feat/subagent-attribution
Closed

feat: subagent attribution + optional ccusage integration (5h windows, burn rate, multi-agent)#140
john988 wants to merge 8 commits into
phuryn:mainfrom
john988:feat/subagent-attribution

Conversation

@john988

@john988 john988 commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

Summary

Two things, both built on top of v1.4.0:

  1. Subagent attribution — re-ported onto the current codebase. The scanner now flags subagent turns and captures dispatch metadata, and the dashboard/CLI surface where your tokens actually go (Task/Agent subagents vs the main thread).
  2. Optional ccusage integration (WRAP mode) — when Node/npx + ccusage are present, we ingest its 5-hour billing windows, burn rate, projections, and per-source totals for other agent CLIs. Everything degrades gracefully when they're not.

The native, stdlib-only tool keeps working exactly as before with zero new required dependenciesccusage is strictly optional enrichment.

What's new

Scanner (scanner.py)

  • turns.is_subagent + turns.agent_id columns and an agents dispatch table (additive migrations — existing DBs upgrade in place, no rebuild).
  • Subagent detection via isSidechain / agentId / a subagents/ transcript path; dispatch identity (agentType, stats) captured from the parent toolUseResult. Wired into both the full and incremental scan paths.

ccusage bridge (ccusage_bridge.py, new, optional)

  • detect_runtime() (probes npx.cmd first so Windows shell=False works), a UTF-8 / LOG_LEVEL=0 subprocess runner that never raises, and idempotent upserts.
  • New tables billing_windows + ccusage_daily_cache, kept separate from the native tables so the two sources never conflict.
  • compute_p90_limit() (P90 of your own completed 5h windows — a personal baseline, since Anthropic's hard limit isn't exposed; same approach as Claude-Code-Usage-Monitor).

Dashboard (dashboard.py)

  • "Subagent Tokens by Type" chart + "Top Subagent Dispatches" table + a Subagent Tokens stat card.
  • "Current 5h Billing Window" card: progress bar vs the P90 baseline (green/amber/red), burn rate, time remaining, projected end-of-window tokens/cost, cost so far. Falls back to a slim install prompt when ccusage data is absent.
  • "Other Agent CLIs (via ccusage)" chart for non-Claude sources (Claude Code is excluded here to avoid double-counting).
  • All new dynamic values escaped via esc().

CLI (cli.py)

  • today / stats print subagent token + turn summaries.
  • scan enriches via ccusage when available; never blocks or fails a native scan.

Verification

  • 145 tests pass (python -m unittest discover -s tests), including 33 new tests covering subagent parsing/scan, the dashboard subagent/billing data, the ccusage transforms/upserts/P90, and the CLI summaries. The bridge tests are hermetic (no Node required).
  • Embedded dashboard JS passes node --check.
  • Verified end-to-end against real transcripts + a real ccusage run (into a throwaway DB):
    • 11,428 subagent turns, 438 agent dispatches attributed.
    • 129 billing windows ingested; active window burn rate ~760k tok/min with live projection.
    • Multi-agent: surfaced Codex usage alongside the native Claude Code views.

Notes / limitations

  • No screenshot in this PR (CI/sandbox couldn't install a browser); run python cli.py dashboard to see the new cards locally.
  • The billing progress bar is measured against your P90 window baseline, not Anthropic's (unpublished) hard token limit.
  • ccusage cost figures use ccusage's own pricing snapshot; native cost uses this repo's PRICING. The two sources are shown side by side, never summed together.
  • Versioning / CHANGELOG bump intentionally left to the maintainer (per the DEV→main release flow).

john988 added 8 commits June 17, 2026 10:06
…s table)

Re-port of the subagent-attribution feature onto v1.4.0. Adds per-turn
is_subagent / agent_id columns and an agents dispatch table (agent_id ->
agent_type, captured from the parent's toolUseResult), populated in both the
full and incremental scan paths. Subagents are detected via isSidechain /
agentId / a transcript path under a subagents/ directory. Schema changes are
additive (ALTER columns + new table), so existing DBs migrate in place.

Tests: 124 pass (8 new in tests/test_subagent.py). Verified on real
transcripts: 11,428 subagent turns and 438 agent dispatches detected.
Dashboard: get_dashboard_data now returns subagent_by_type and top_dispatches
(JOIN turns->agents, acompact-* -> auto-compact, else unknown). Adds a
"Subagent Tokens by Type" stacked bar chart, a "Top Subagent Dispatches"
table, a Subagent Tokens stat card, and an agent-type colour palette — all
filtered by the existing model + range controls. Dynamic values are escaped
via esc() (no innerHTML injection).

CLI: `today` and `stats` print subagent token/turn summaries (included in
totals).

Tests: 130 pass (+6 for dashboard/CLI subagent data). Verified on real
transcripts (75 type-rows, 50 dispatches) and inline JS passes `node --check`.
…daily totals

Adds ccusage_bridge.py wrapping the optional `ccusage` CLI as a data source:
detect_runtime() (probes npx.cmd first so Windows shell=False works), a
UTF-8 / LOG_LEVEL=0 subprocess runner that never raises, pure transforms
(blocks_to_rows / daily_to_rows mapping ccusage's camelCase + nested
burnRate/projection), and idempotent upserts. New SQLite tables
billing_windows and ccusage_daily_cache (additive; kept separate from the
native turns/sessions tables so the two sources never conflict). cmd_scan now
enriches via ccusage when available and degrades gracefully when not.

Tests: 140 pass (+10 hermetic bridge tests, no Node required). Verified
end-to-end against real ccusage 20.0.9: 129 billing windows (1 active,
762k tok/min, 140 min remaining) + 34 daily rows.
…data

ccusage_bridge gains compute_p90_limit() (90th-percentile of your completed 5h
window totals — a personal "typical heavy window" baseline, since Anthropic's
hard token limit isn't exposed; same approach as Claude-Code-Usage-Monitor) and
summarize_billing() (active window + window count + P90 + recent history, or
{available: False} so the UI can show an install prompt). get_dashboard_data
returns a "billing" key, guarded so a bridge issue can never break the page.

Tests: 145 pass (+5). Verified on real data: 129 windows, P90 99M, active
window 131% of baseline at 798k tok/min.
Adds a "Current 5h Billing Window" card above the charts: a progress bar of the
active window's tokens vs the P90 baseline (green/amber/red at 80%/100%), plus
burn rate, time remaining, projected end-of-window tokens/cost, and cost so far.
Falls back to a slim ccusage/Node install prompt when billing data is absent —
the rest of the dashboard is unaffected.

Verified: inline JS passes node --check; 145 tests pass.
Bridge ingests per-source daily totals for non-Claude agent CLIs
(CCUSAGE_EXTRA_SOURCES) into ccusage_daily_cache; Claude Code is excluded here
(covered natively) so it's never double-counted. get_dashboard_data exposes
ccusage_daily (per-source, sans the unified 'ccusage-all'), and a new
"Other Agent CLIs (via ccusage)" chart shows non-Claude token usage by source,
range-filtered, hidden when there's nothing to show.

Tests: 145 pass. Verified end-to-end: real ccusage run surfaced codex usage
(665.8M tokens, ~$694) alongside the native Claude Code views.
…release v1.5.0

P6 — single source of pricing:
- Move PRICING + get_pricing + calc_cost into pricing.py, imported by cli.py and
  served via /api/data. The dashboard JS reads rawData.pricing at runtime
  (its embedded table is now only a cold-start fallback), so the Python and JS
  tables can no longer drift.

P7 — limit events + transparency:
- Detect "Claude AI usage limit reached" events into a new limit_events table,
  gated on isApiErrorMessage so ordinary text mentioning a limit isn't
  misdetected (parse_jsonl_file now returns limit events; both scan paths upsert).
- Footer notes that figures are transcript-derived estimates that may not match
  Anthropic billing, and that native vs ccusage numbers are shown separately.

Release: bump to v1.5.0 across scanner.VERSION, CHANGELOG, and the extension
package.json (parity test enforces all three).

Tests: 150 pass (+5 limit-event tests); cli.calc_cost is pricing.calc_cost;
inline JS passes node --check.
…hboard cache)

- Scanner: the no-growth branch now also updates processed_files.lines, so a
  transcript rewritten shorter (compaction) isn't skipped forever and later
  appends are still ingested.
- CLI: `today` / `week` compute their window in UTC to match UTC transcript
  timestamps (drop now-unused `date` import).
- Dashboard: cache the /api/data payload keyed on DB path + mtime so the 30s
  poll doesn't re-run every query when nothing changed; getRangeBounds now uses
  UTC date math (week/month/relative) for consistency with the data.

Tests: 154 pass (+4). Inline JS passes node --check.
john988 added a commit to john988/claude-usage that referenced this pull request Jun 17, 2026
Scanner shrink-skip, today/week UTC dates, and dashboard /api/data caching are
resolved; note the residual stale-turn limitation on compaction.
phuryn added a commit that referenced this pull request Jun 21, 2026
…/data

The subagent attribution split adds an `agents`-table query to
get_dashboard_data. The /api/data handler called it with no argument, so
it used the default db_path frozen to the module global at def time —
ignoring a monkey-patched dashboard.DB_PATH and reading the real
~/.claude/usage.db. On a pre-migration DB (no `agents` table yet) that
raised "no such table: agents", failing TestDashboardHTTP locally (CI
passed only because no real DB exists there).

Pass DB_PATH explicitly so the handler resolves the module global at call
time — same contract already documented for /api/rescan. Behaviour is
unchanged for real users; tests that patch dashboard.DB_PATH now work.

Follow-up to the subagent-attribution split (PR #140, thanks @john988).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
phuryn added a commit that referenced this pull request Jun 21, 2026
Bump scanner.VERSION + vscode-extension/package.json to 1.5.0 and add the
v1.5.0 — TBD CHANGELOG heading covering the subagent-attribution split
(#140, @john988) and Docker support (#143, @RafikFarhad). Minor bump:
both are non-breaking user-visible features.

Date stays TBD and the DEV->main release merge is left to the maintainer
per the documented release flow.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@phuryn

phuryn commented Jun 21, 2026

Copy link
Copy Markdown
Owner

Thanks for this, @john988 — and for re-porting the subagent work onto current main, that made it easy to pick up.

The subagent attribution half shipped in v1.5.0: turns.is_subagent/agent_id + an agents dispatch table (additive in-place migrations), detection via isSidechain/agentId/subagents/ path, the "Subagent Tokens by Type" chart, the "Top Subagent Dispatches" table, the stat card, and the today/stats summaries. It was re-implemented on main rather than merged commit-for-commit, but you're credited in the CHANGELOG and the v1.5.0 release (#140, thanks @john988).

The optional ccusage integration we're passing on for now — it adds an external Node/npx + ccusage dependency and a good deal of surface area (the bridge, extra tables, billing cards) that sits outside this project's "three files, stdlib-only, zero install" scope. It's cleanly optional in your PR; it's just more than we want to maintain right now. If there's appetite later, it'd be best as its own focused PR so it can be weighed on its own.

Closing since the attribution piece has landed and the ccusage piece is out of scope for now — not a quality judgment at all, the work was excellent. Thank you! 🙏

@phuryn phuryn closed this Jun 21, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants