feat(usage): Anthropic-console-style token dashboard (M17-2) #159
No reviewers
Labels
No labels
area:agents
area:dashboard
area:database
area:design
area:design-review
area:flows
area:infra
area:meta
area:security
area:sessions
area:webhook
area:workdir
security
type:bug
type:chore
type:meta
type:user-story
No milestone
No project
No assignees
2 participants
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
charles/claude-hooks!159
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "boss/153"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
Capture per-task token counts from the Claude Agent SDK's
result.usageblock and surface them as an Anthropic-console-style Usage tab on the
dashboard, backed by a new
GET /usageendpoint. Closes #153.Data capture
TaskRecord/persistTaskgrow four new columns —input_tokens,output_tokens,cache_creation_tokens,cache_read_tokens. Migration intask-store::ensureSchemarunsidempotent
ALTER TABLEstatements so existing rows stay readablewith
NULLcolumns (treated as zero in aggregates).logSDKMessagelifts the usage block out of the terminalresultmessage;
registerWorker's runTask correlates those numbers into thein-memory record so
onFinishforwards them topersistTask./usageendpointGET /usage[?window=week|day|all], defaultweek(or theusage_resetconfig override — new top-level key inconfig/agents.json).reset_atis the following Monday. Day window = 24h from today00:00 UTC; all window = unbounded,
reset_at: null./ tasks / turns),
by_agentranked by tokens consumed, aby_daysparkline-compatible array, and
threshold_tokensechoed from theoptional
usage_threshold_tokensconfig key.Dashboard Usage tab
input/output/cache bar, circular SVG threshold ring.
red >95 % of
usage_threshold_tokens. Ring stays blank when thethreshold is unconfigured (Anthropic publishes no hard Pro-Max cap).
/stats.stats-day-striprecipe. Window chip-strip +5-minute auto-refresh while the tab is active.
Tests
src/usage.test.ts— 16 tests covering endpoint shape, windowboundaries (Monday 00:00 UTC anchor, 24h day span, unbounded all),
ranking order, NULL-token backward compatibility.
src/dashboard-smoke.test.ts— 7 structural checks for the new tabelements and threshold-bucket JS helper.
src/dashboard-browser.test.ts— 6 happy-dom tests for herorendering, by-agent ranking, window refetch, blank-ring behaviour,
green→yellow→orange→red colour transitions, and empty state.
Docs
/usagesection with the full response shape and configreference.
task-store.tsupdated to mention thenew
computeUsagehelper.openapi.yml—/usagepath +UsageResultschema.Test plan
bun test— all 587 existing tests plus the 29 new ones passbun x tsc --noEmitgreenbun x biome check src/green/dashboard→ Usage tab → verify hero, ring,stacked bar, agent table, day sparkline
usage_threshold_tokensinconfig/agents.json, restart,watch the ring fill and hero colour change with task volume
d41c3abfb53c30f4a217CI still pending at review time (run #1749, sha
3c30f4a). Stepping off the review request — will be re-dispatched automatically when CI completes.Review — PR #159: feat(usage): Anthropic-console-style token dashboard (M17-2)
CI: green ✅ (run #1749, 3m20s, sha
3c30f4a)Round: 1 (no prior reviews)
Acceptance criteria check
All criteria from issue #153 are met:
TaskRecordextended withinput_tokens,output_tokens,cache_creation_tokens,cache_read_tokenspersistTaskcorrectly writes them;onFinishand the cancel path both forward the fieldsALTER TABLEmigrations — existing rows keepNULL, aggregation usesCOALESCE(SUM(col), 0)GET /usage[?window=week|day|all]wired inmain.ts, defaultweekwindow.{reset_at, since, kind},totals.{tasks, turns, input_tokens, output_tokens, cache_read, cache_creation},by_agent,by_day,threshold_tokens(dayOfWeek + 6) % 7is correct, tested for Mon/Wed/Sun boundary casesusage_threshold_tokensis absentusage_resetandusage_threshold_tokensconfig keys parsed with graceful fallback + warning on invalid values/usagesection, CLAUDE.md Modules table,openapi.ymlOne minor finding (non-blocking)
src/main.ts~line 239 —?? 0stores zeros instead of NULL when the SDK emits no usage blockWhen
msg.usageisnull/undefined(e.g. a very early abort before the SDK makes any API call), all four token fields coalesce to0. InrunTask,typeof 0 === "number"is truthy, sorecord.input_tokensgets set to0. ThenpersistTaskstores0rather thanNULL.The
PersistTaskInputdocstring says "null when the SDK reported no usage block", but the code stores0. Aggregate correctness is unaffected —COALESCE(SUM(col), 0)treats NULL and 0 identically. In practice the SDK'sNonNullableUsagetype guaranteesmsg.usageis always present on result messages, so this path is theoretical. Not blocking.Suggested fix:
Overall
Clean implementation. SQL, boundary math, config parsing, migration, and tests are all correct. Ready to merge.