[FEATURE] Per-model breakdown of the session cost in $.session.usage() / session.measure and the status line JSON
Preflight Checklist
- [x] I have searched existing requests and this feature hasn't been requested yet
- [x] This is a single feature request (not multiple features)
Problem Statement
The session cost reaches integrations as one number. On 2.1.290:
- status line JSON:
cost.total_cost_usd(with durations and lines added / removed) - function hooks:
SessionCostis{ usd }(from$.session.usage()andsession.measure)
A session usually spends on more than one model: the main loop, subagents that run on another model, a fallback model, and internal calls such as titles and compaction. Neither surface says how the total splits between them.
An integration that wants "how much of this went to model X" has to rebuild it:
- read the main transcript and every subagent transcript,
- reduce the records to one per API request,
- price each request with its own copy of the price table.
Each step is fragile outside the engine. The price table has to follow new models, fast mode and any managed modelPricing. Subagent transcripts have known inconsistencies (#93620, #97978). In one session I compared, the transcript count came out about 9.5% below the status line's total because the subagent requests live in other files.
Function hooks don't close the gap either. turn.step resolves with the token counts and the model id of each request, but no cost, and the cache-write count is not split by TTL, so a plugin cannot price a request exactly.
The engine already has the split:
- the SDK result message carries
modelUsage, documented as per-model totals for every model call including the main loop, subagents and internal calls, next tototal_cost_usd; - the OpenTelemetry
api_requestevent carriesmodelandcost_usd, and theclaude_code.cost.usagemetric has amodelattribute.
It just isn't passed to the status line or to function hooks.
Related: #87330 asked for per-model cost in the /usage screen and at /model switch time (closed as not planned). This request is narrower: pass the breakdown the engine already keeps to the two integration surfaces. #95837 describes a usage panel that already shows a per-model breakdown.
Proposed Solution
Add the per-model totals behind the existing total:
- function hooks:
SessionCost.byModel, a map from model id to{ usd }(token counts too if that is whatmodelUsageholds), covering the same calls asusd; - status line JSON:
cost.by_modelwith the same content.
The entries sum to the existing total. Absent where the host keeps no cost ledger, so existing readers are unaffected.
Alternative Solutions
- OpenTelemetry: point the OTLP exporter at a local collector and sum
api_requestbymodel. Exact, but it needs a listening port and takes the one exporter endpoint, which conflicts with a real collector. - Transcripts plus an own price table, as described above.
turn.steptoken counts plus an own price table. Same table problem, and no TTL split for cache writes.
Priority
Low - Nice to have
Feature Category
Developer tools/SDK
Use Case Example
A usage display keeps a per-day record of spend by model and shows what share went to the most expensive model. That share is the figure a user acts on: it says whether switching the main loop, or moving subagents to a smaller model, would change the bill.
Today the display computes it from transcripts with its own price table, and corrects it with OpenTelemetry where that is configured. With byModel it would add up the engine's own figures, and a new model would need no table update on the day it ships.
Additional Context
- Claude Code 2.1.290, Windows 11. Checked in a CLI session (terminal) and in a VS Code extension session.
SessionCostandturn.stepare as declared in theclaude-code.d.tswritten by 2.1.290; the status line keys are from the actual stdin of a status line command on the same build.modelUsageand the telemetry attributes are as described in the Agent SDK reference and the monitoring documentation.