Track sub-agent type per message; add "by agent" breakdown and filter#6
Open
ludwigehlert-gif wants to merge 5 commits into
Open
Track sub-agent type per message; add "by agent" breakdown and filter#6ludwigehlert-gif wants to merge 5 commits into
ludwigehlert-gif wants to merge 5 commits into
Conversation
Adds totals.cost_breakdown to /api/stats with per-bucket cost (input, output, cache_read, cache_write_5m, cache_write_1h), grouped per (tool, model) so the correct rates are applied. The UI surfaces four new cards — cache read, cache write, output, fresh input — each with its share of the total cost. For Claude Code sessions this makes it obvious that cache reads (re-sending the conversation each turn at 10% input rate) and 1h cache writes dominate the bill, not fresh input or output. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Each Claude Code sub-agent JSONL has a sibling agent-<id>.meta.json
sidecar that names the agentType (Explore, Plan, general-purpose, ...)
and a per-invocation description. The parser now reads it and tags
every message row with agent_type / agent_desc. A lightweight ALTER
TABLE migration adds the columns to existing DBs, and a one-shot
backfill on ingest tags rows that were already imported before this
change.
New UI surface:
- "agent" filter dropdown (with a "main" pseudo-value to isolate
top-level conversation turns).
- "by agent" breakdown toggle that groups cost / tokens / msgs by
agent_type (NULL bucketed as "main session").
- Session timeline now shows the agent column so it's clear which
turns came from which sub-agent invocation, with the description
to disambiguate multiple invocations of the same agent type.
All existing filtered endpoints (/api/stats, /api/sessions,
/api/session/{id}, /api/mcp, /api/breakdown_series) now accept an
`agent` query parameter; MCP views match it via the assistant turn
that issued the call, consistent with the existing model filter.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The dashboard is a local dev tool, so reload-on-edit beats having to remember to kill+restart after every code change. Watches only tracker/ and web/ to avoid noisy restarts. Set RELOAD=0 to opt out (useful if/when running under launchd). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Track agent_id (the unique hash from agent-<id>.jsonl, what shows up in the dashboard as the sub-agent "PID") on each message row, with an ALTER TABLE migration and a one-shot backfill from the existing sub-agent filenames. The "by agent" breakdown now groups by (agent_type, agent_id, agent_desc) so each Task invocation is its own row — e.g. you can finally see that one specific "End-to-end SAP→Dataiku migration V3" general-purpose run was $93.71 instead of just "general-purpose: $209.76 across 24 runs". The chart for group=agent plots top-5 invocations over time instead of top-5 agent types. UI tightening: - Cards split into two locked-width groups: "overview" (6 tiles) and "where the $ goes" (4 cost-bucket tiles). Cards never wrap to a second row — they shrink uniformly to fit, with text-overflow ellipsis on labels/values and a full-text tooltip on hover. - Session timeline shows agent_type, an 8-char agent_id slug, and the per-invocation description for sub-agent turns. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…kdown Every Claude Code JSONL record carries an "entrypoint" field — "cli" for the interactive REPL, "sdk-cli" for SDK-spawned runs, "claude-desktop" for the desktop app. Codex sessions are tagged "codex" by convention. Without it, an SDK-spawned migration is indistinguishable from an interactive session: both bucket into "main session" with no way to scope cost to one or the other. - sessions.entrypoint column + ALTER TABLE migration in init(). - Claude parser captures the field from any record that has it (first non-meta lines do); codex parser stamps "codex". - ingest.run() one-shot backfill: walks each session's first ~30 JSONL lines to recover entrypoint for rows ingested before this change. Sessions whose source JSONL has been purged by Claude Code's housekeeping stay (unknown) — there's no source to read. - API: new entrypoint query param on every filtered endpoint; /api/filters returns distinct entrypoints; /api/stats adds by_entrypoint; /api/breakdown_series supports group=entrypoint. - UI: entrypoint filter dropdown; "by entrypoint" breakdown toggle; entrypoint column on the by-session table so sdk-cli rows are visible at a glance. 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
Each Claude Code sub-agent JSONL (
<project>/<sessionId>/subagents/agent-*.jsonl) has a siblingagent-*.meta.jsonwith theagentType(Explore, Plan, general-purpose, claude-code-guide, …) and a per-invocationdescription. After #4 those files are ingested, but every row showed up as just "the parent session" with no way to tell whose turns cost what.This PR makes that visible:
agent_type+agent_desccolumns tomessages, with a lightweightALTER TABLEmigration ininit()and a one-shot backfill iningest.run()that tags rows ingested before this change..meta.jsonsidecar when parsing a sub-agent file and stamps every message row.agentquery parameter (agent=mainselects the top-level conversation only);/api/statsreturns aby_agentaggregate;/api/breakdown_seriessupportsgroup=agent; MCP views filter via the assistant turn that issued the call, matching the existingmodelfilter shape.agentfilter dropdown, new by agent toggle in the breakdown row, and the session timeline now shows an "agent" column so it's clear which turns came from which sub-agent invocation (with the per-invocation description rendered to disambiguate multiple invocations of the same agent type).Example output on my data:
Test plan
uv run python -m tracker.ingest— schema migration adds columns; sub-agent rows ingested as before.SELECT COUNT(*) FROM messages WHERE agent_type IS NOT NULLis nonzero.agentfilter to e.g. "Explore" — all other panels (totals, charts, by-model, by-project, MCP views, session list, session detail) re-scope correctly.agentType · descriptionper row.🤖 Generated with Claude Code