Documentation
¶
Overview ¶
Package usage counts what a run actually cost.
Claude Code hooks are handed a transcript_path and nothing else: no tokens, no cost. The numbers exist only inside the JSONL transcript, so we parse it.
The load-bearing fact — and the entire reason this package is not a twenty-line loop over one file — is that SUBAGENTS DO NOT APPEAR IN THE PARENT TRANSCRIPT. They are written to <dir>/<sessionId>/subagents/agent-<agentId>.jsonl, and the parent's Agent-tool result carries no token totals at all. A parser that reads only transcript_path therefore undercounts a fan-out run by however much the fan-out did — and fan-out IS batten's workload. We walk both.
The format is not a public API. Every record is best-effort: a line that does not parse is skipped, never fatal, and partial results always beat an error.
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func Parse ¶
func Parse(transcriptPath, runID string, seen map[string]bool) (rows []store.Usage, unknownModels []string, err error)
Parse walks the session transcript AND its subagents/*.jsonl, prices every request, and returns rows ready for store.RecordUsage. Rows whose RequestID is in seen are skipped. AgentID is set for subagent rows and empty for the parent; the caller maps it to a node. UnknownModels lists model ids we had no price for (their rows carry ImputedUSD == 0).
The returned error reports that the PARENT transcript could not be read; rows gathered from the subagents are still returned alongside it. A missing subagents directory is not an error — most sessions never fan out.
func Price ¶
func Price(model, speed string, inTok, outTok, cw5m, cw1h, cacheRead, webSearches int64, geoUS bool) (usd float64, ok bool)
Price returns the imputed USD for one request: what it WOULD have cost on the API. On a subscription this is not a bill — it is the value pulled out of the plan. ok == false means the model is unpriced; cost is then 0.
This prices at TODAY's rate. Parse does not call it: Parse prices each record at the record's own timestamp (see priceAt), so re-parsing an old transcript stays correct across the Sonnet 5 repricing. Use Price for "what would this cost me now" questions.
Types ¶
This section is empty.