feat: subagent attribution + optional ccusage integration (5h windows, burn rate, multi-agent)#140
feat: subagent attribution + optional ccusage integration (5h windows, burn rate, multi-agent)#140john988 wants to merge 8 commits into
Conversation
…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.
Scanner shrink-skip, today/week UTC dates, and dashboard /api/data caching are resolved; note the residual stale-turn limitation on compaction.
…/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>
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>
|
Thanks for this, @john988 — and for re-porting the subagent work onto current The subagent attribution half shipped in v1.5.0: The optional ccusage integration we're passing on for now — it adds an external Node/ 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! 🙏 |
Summary
Two things, both built on top of
v1.4.0:ccusageintegration (WRAP mode) — when Node/npx+ccusageare 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 dependencies —
ccusageis strictly optional enrichment.What's new
Scanner (
scanner.py)turns.is_subagent+turns.agent_idcolumns and anagentsdispatch table (additive migrations — existing DBs upgrade in place, no rebuild).isSidechain/agentId/ asubagents/transcript path; dispatch identity (agentType, stats) captured from the parenttoolUseResult. Wired into both the full and incremental scan paths.ccusage bridge (
ccusage_bridge.py, new, optional)detect_runtime()(probesnpx.cmdfirst so Windowsshell=Falseworks), a UTF-8 /LOG_LEVEL=0subprocess runner that never raises, and idempotent upserts.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)esc().CLI (
cli.py)today/statsprint subagent token + turn summaries.scanenriches via ccusage when available; never blocks or fails a native scan.Verification
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).node --check.ccusagerun (into a throwaway DB):Notes / limitations
python cli.py dashboardto see the new cards locally.PRICING. The two sources are shown side by side, never summed together.