feat: add cost tracking dashboard#347
Conversation
Add cost tracking fields to Home struct with atomic counters for today/week totals. Header stats show today's cost when data exists. Press $ to open a full cost dashboard showing daily/weekly/monthly totals, token breakdown, top sessions by cost, and per-model costs. Cost totals refresh every tick via the existing tick handler.
…page Add handlers_costs.go with six API endpoints (summary, daily, sessions, models, export, stream SSE) and a costs HTML page handler. Register routes and cost subscriber infrastructure in server.go. Include Chart.js UMD bundle and a dark-themed costs.html dashboard with live SSE updates.
Hook payload doesn't include token usage data. Instead, on Stop events, read the last line of the Claude transcript JSONL file to extract model, input/output/cache tokens. Includes debug logging to ~/.agent-deck/cost-debug.log for validation.
agent-deck costs sync — scans managed Claude sessions' transcript files and backfills historical cost_events. Handles both assistant turns and progress entries (subagent Haiku/Sonnet usage). agent-deck costs summary — prints cost totals, top sessions, and model breakdown to stdout.
Go's time.Time.UTC() stored as "2026-03-16 21:06:37 +0000 UTC" which SQLite's date() can't parse. Format as RFC3339 instead. Add DailyBySession and CostByModelForSession query methods for the session detail web view.
Groups tab with per-group cost totals and drill-down. Clickable sessions show daily spend chart, model breakdown, and token counts. Breadcrumb navigation between views. New API endpoints: /api/costs/groups and /api/costs/session?id=.
There was a problem hiding this comment.
Pull request overview
Adds end-to-end cost tracking (collection → storage → pricing/budgets → dashboards) to Agent Deck, exposing live token/cost visibility in both the TUI and the web UI.
Changes:
- Introduces
internal/costspackage for parsing usage, pricing, storage, retention, and budget checking. - Adds web
/costsdashboard with supporting API endpoints + SSE stream. - Wires cost tracking into the TUI header/dashboard, hook handler, and CLI subcommands.
Reviewed changes
Copilot reviewed 34 out of 35 changed files in this pull request and generated 13 comments.
Show a summary per file
| File | Description |
|---|---|
| internal/web/static/styles.css | Adds styling for the web “Costs” nav button |
| internal/web/static/index.html | Adds “💰 Costs” link to /costs |
| internal/web/static/costs.html | New web cost dashboard UI (charts/tables/SSE/export) |
| internal/web/server.go | Registers /costs + cost APIs; adds cost store/subscriber fields |
| internal/web/handlers_costs.go | Implements cost summary/daily/sessions/models/groups/detail/export + SSE handlers |
| internal/ui/home.go | Adds cost totals to TUI header and $ cost dashboard toggle |
| internal/ui/help.go | Adds $ help entry for cost dashboard |
| internal/ui/cost_dashboard.go | New TUI cost dashboard view |
| internal/statedb/statedb.go | Bumps schema + adds cost_events table and indexes |
| internal/session/userconfig_cost_test.go | Adds tests for [costs] config parsing/defaults |
| internal/session/userconfig.go | Adds CostsSettings (budgets/pricing/timezone/retention) |
| internal/costs/watcher_test.go | Tests filesystem watcher for incoming cost events |
| internal/costs/watcher.go | Implements fsnotify-based cost event directory watcher |
| internal/costs/sync.go | Adds transcript backfill (costs sync) for Claude JSONL transcripts |
| internal/costs/store_test.go | Adds store/query/retention/format tests |
| internal/costs/store.go | Implements SQLite persistence + aggregation queries |
| internal/costs/pricing_test.go | Tests pricing defaults, overrides, normalization, cache I/O |
| internal/costs/pricing.go | Implements hardcoded pricing + cache + overrides + cost computation |
| internal/costs/poller_test.go | Tests tmux-output polling deduplication |
| internal/costs/poller.go | Adds deduplicating poller for non-hook tools |
| internal/costs/parser_test.go | Tests Claude/Gemini/OpenAI parsing and collector behavior |
| internal/costs/parser_openai.go | Adds OpenAI/Codex output parser |
| internal/costs/parser_gemini.go | Adds Gemini output parser + comma-int parsing |
| internal/costs/parser_claude.go | Adds Claude Stop-hook JSON parser |
| internal/costs/fetcher_test.go | Tests pricing fetcher cache behavior |
| internal/costs/fetcher.go | Adds daily pricing “fetcher” (currently writes defaults to cache) |
| internal/costs/costs.go | Defines cost event/summary types + USD formatting |
| internal/costs/collector.go | Routes tool outputs to parsers and applies pricing |
| internal/costs/budget_test.go | Adds unit tests for budget checker thresholds |
| internal/costs/budget.go | Implements budget evaluation (global/session/group) helpers |
| cmd/agent-deck/main.go | Initializes cost store/pricer/budgets/watcher; wires to TUI + web server |
| cmd/agent-deck/hook_handler.go | Writes per-turn cost event files by reading Claude transcripts on Stop hooks |
| cmd/agent-deck/costs_cmd.go | Adds `agent-deck costs sync |
| README.md | Documents new cost tracking features + config snippet |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
You can also share your feedback on Copilot code review. Take the survey.
- Fix token auth on /costs page and Costs nav link - Fix CachePath double-join (dir vs file path) - Fix readLastLine for files without trailing newline - Fix XSS in inline onclick handlers (use data attrs) - Fix TotalByDateRange timestamp format consistency - Throttle refreshCostTotals to every 10s - Add error handling in session detail handler - Gate debug logging behind AGENTDECK_DEBUG env var - Remove misleading comment in sync.go - Fix implicit event object in showTab - Close storage in CLI cost commands
There was a problem hiding this comment.
Pull request overview
Adds end-to-end cost tracking (token usage → priced cost events → budgets) across Agent Deck sessions, surfaced via a new TUI dashboard, a web dashboard, and CLI utilities, with persistence in the existing SQLite state DB.
Changes:
- Introduces a
cost_eventsSQLite table (+ store/query layer) and wiring to ingest cost events from Claude hooks and transcript backfill. - Adds pricing (hardcoded defaults + cache + config overrides) and budget checking primitives.
- Adds UI surfaces: TUI
$cost dashboard and a web/costsdashboard with APIs, SSE updates, and export.
Reviewed changes
Copilot reviewed 35 out of 36 changed files in this pull request and generated 8 comments.
Show a summary per file
| File | Description |
|---|---|
| internal/web/static/styles.css | Adds styling for a “Costs” header button in web UI. |
| internal/web/static/index.html | Adds navigation link to /costs. |
| internal/web/static/costs.html | New standalone cost dashboard page (charts, tables, SSE, exports). |
| internal/web/static/app.js | Appends auth token to the /costs link when present. |
| internal/web/server.go | Registers cost APIs/routes; adds cost store + subscriber plumbing. |
| internal/web/handlers_costs.go | Implements cost summary/daily/model/session/group/export APIs + SSE stream + /costs page handler. |
| internal/ui/home.go | Wires cost tracking into TUI header and $ key behavior; shows TUI cost dashboard. |
| internal/ui/help.go | Documents $ as “Cost Dashboard” in help overlay. |
| internal/ui/cost_dashboard.go | New TUI cost dashboard view (summary + top sessions + model breakdown). |
| internal/statedb/statedb.go | Bumps schema version; creates cost_events table + indices. |
| internal/session/userconfig_cost_test.go | Tests parsing of new [costs] config sections. |
| internal/session/userconfig.go | Adds CostsSettings (budgets, pricing overrides, retention, timezone). |
| internal/costs/watcher_test.go | Tests fsnotify-based cost event watcher behavior. |
| internal/costs/watcher.go | New directory watcher for JSON cost event files emitted by hooks. |
| internal/costs/sync.go | Adds transcript backfill (Claude JSONL parsing) into cost_events. |
| internal/costs/store_test.go | Tests for store writes/queries, retention purge, formatting. |
| internal/costs/store.go | SQLite store implementation for inserting/querying cost events + aggregates. |
| internal/costs/pricing_test.go | Tests hardcoded pricing, cost computation, cache IO, overrides, normalization. |
| internal/costs/pricing.go | Pricing resolver (override > cache > defaults) + cost computation. |
| internal/costs/poller_test.go | Tests dedup behavior for polling-based collectors. |
| internal/costs/poller.go | Adds deduped polling entrypoint for non-hook tools. |
| internal/costs/parser_test.go | Tests Claude hook parsing + Gemini/OpenAI output parsing + collector. |
| internal/costs/parser_openai.go | Parses OpenAI/Codex CLI “Tokens used …” output. |
| internal/costs/parser_gemini.go | Parses Gemini CLI “Token count …” output. |
| internal/costs/parser_claude.go | Parses Claude Code Stop hook payload usage. |
| internal/costs/fetcher_test.go | Tests pricing cache age and writing cache file. |
| internal/costs/fetcher.go | Daily pricing cache writer (currently writes defaults). |
| internal/costs/costs.go | Defines core cost event/summary structs + USD formatting helper. |
| internal/costs/collector.go | Routes tool output to parsers and applies pricing + IDs/timestamps. |
| internal/costs/budget_test.go | Tests budget checker warning/stop behavior and session limits. |
| internal/costs/budget.go | Budget checker (global/session/group; warn at 80%, stop at 100%). |
| cmd/agent-deck/main.go | Initializes cost store/pricer/budgets/watcher; wires store into web server. |
| cmd/agent-deck/hook_handler.go | Writes cost event JSON files on Claude Stop hooks by reading transcript usage. |
| cmd/agent-deck/costs_cmd.go | Adds agent-deck costs sync and agent-deck costs summary. |
| README.md | Documents cost tracking, config options, and $ shortcut. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
You can also share your feedback on Copilot code review. Take the survey.
- Format timestamps as RFC3339 in transactional queries - Fix watcher: delete file only after successful enqueue, close channel on exit - Use math.Round for float-to-microdollar budget conversion - Remove unused cost subscriber path from SSE handler - Propagate errors in budget checker instead of ignoring - Add fetch error handling in web dashboard
|
All done except: |
|
@asheshgoplani I realized now can you change |
|
Good catch @BestSithInEU. The We should move the cost dashboard to a different key (e.g. |
|
@BestSithInEU Fixed! Cost dashboard is now on |
Summary
Real-time token usage and cost tracking for all AI agent sessions with TUI dashboard, web dashboard, and CLI tools.
What's included
Test plan