feat(statusline): add current-month total cost placeholder across all agent tools - #55
feat(statusline): add current-month total cost placeholder across all agent tools#55Brusdeylins wants to merge 5 commits into
Conversation
| directory and recomputed at most once per *--month-cost-ttl* window | ||
| by a detached background process, so a render never blocks on the | ||
| transcript scan; missing or empty logs simply suppress the | ||
| placeholder. When run inside a *tmux* pane, the resolved task id is also |
There was a problem hiding this comment.
Here need a blank line, I think. It is very confusing that the tmux stuff is at the end of the %Y stuff.
| "claude-opus-4-6": { input: 5.00, output: 25.00 }, | ||
| "claude-sonnet-4-6": { input: 3.00, output: 15.00 }, | ||
| "claude-haiku-4-5": { input: 1.00, output: 5.00 } | ||
| } |
There was a problem hiding this comment.
This both can become a maintenance issue (when new models occur or prices change) and does not support any models in Github Copilot.
| catch (_e) { | ||
| user = process.env.USER ?? "default" | ||
| } | ||
| return path.join(os.tmpdir(), `ase-statusline-month-cost-${user}.json`) |
There was a problem hiding this comment.
Really under the OS temp directory like /tmp or $TMPDIR? I would have expected ~/.ase/xxx or something like this but not the temp directory.
| emit(`${prefix("$", "cost")}${c.bold(formatCostUsd(sessCost))}`) | ||
| }, | ||
| Y: () => { | ||
| const monthCost = monthCostForRender(new Date(), opts.monthCostTtl) |
There was a problem hiding this comment.
At least here we would have to check the "agent harness": "claude" vs. "copilot". For copilot this at least should expand to the empty string or perhaps even "N.A." or something like this.
94364e5 to
fc0f453
Compare
b55504d to
5d92e26
Compare
5d92e26 to
cd440d6
Compare
5aaf7fa to
9eae350
Compare
9eae350 to
8c2d82f
Compare
Add a new %Y statusline placeholder that shows the cumulative cost (USD) across ALL Claude Code sessions of the current calendar month, complementing %X which only reflects the current session. - new tool/src/ase-statusline-cost.ts: scans ~/.claude/projects/**/*.jsonl (honoring CLAUDE_CONFIG_DIR), sums each entry's message.usage tokens times the per-model price, filtered to the current month (local time). Pricing is cross-checked against the LiteLLM price table (the same source ccusage uses): input/output plus cache-read (0.1x), 5-minute cache-write (1.25x) and 1-hour cache-write (2x) derived from each model's input price. Duplicate messages (same id+requestId across session logs) are de-duplicated. - performance: the transcript scan never runs in the render path. The result is cached in the temp directory and, when stale/missing, refreshed by a detached background process (ase statusline --refresh-month-cost); the render serves the last cached value immediately. TTL is configurable via --month-cost-ttl (default 300s). - robustness: missing/empty logs or unknown models simply suppress the placeholder; no errors. - docs: document %Y, its data source, caching, and --month-cost-ttl in docs/usage-tool.md and extend the statusline argument hint. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add Claude Fable 5 (model id `claude-fable-5`, $10/MTok input, $50/MTok output) to the `%Y` cumulative-month-cost price table. Cache-token costs derive from the standard prompt-caching multipliers as for the other models (read 0.1x, 5-min write 1.25x, 1-hour write 2x), which match the published Fable 5 cache rates. Prices verified against the official Anthropic pricing and models-overview documentation. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…onth cost Claude Code rewrites a message's log entry with progressively growing usage counts while the response streams. The previous first-wins de-duplication kept whichever snapshot was encountered first, often a partial one, systematically undercounting the month total by ~0.5%. Now the most expensive snapshot per message-id/request-id key is kept, which reflects the final billed state. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… all agent tools The %Y current-month cost so far relied on a hand-maintained price table covering seven Claude models and scanned Anthropic Claude Code logs only. Both limits produced silently wrong figures: an unlisted model contributed nothing at all (claude-opus-5 was missing, understating the month by more than half), a stale entry mispriced its model (claude-sonnet-5 was carried at 3/15 instead of 2/10 USD per million tokens), and users of the other agent tools got no figure at all. Prices now come from a snapshot of the LiteLLM price database, reduced to the chat models of the vendors reachable through the supported agent tools and generated by "npm start prices-update" into a checked-in source module, so build and rendering stay offline. Model ids resolve through the bare id, a dot-normalized id, the id without its vendor prefix, and finally the longest matching id prefix, which maps dated snapshots onto their base entry. The scan covers Anthropic Claude Code, OpenAI Codex CLI, and GitHub Copilot CLI, each with its own log layout, usage field names, and identity for de-duplication; Codex reports its cached tokens inside the input total and Copilot aggregates cumulatively per session, both of which are normalized before pricing. The month boundary moved to UTC, matching the day on which the vendors bill and reset their usage windows, and the on-disk cache carries a scheme version so a value written by an older ASE is discarded instead of being rendered as if it were current.
8c2d82f to
6ef49b7
Compare
Update: month cost is now priced from a LiteLLM snapshot and covers all agent toolsThe branch was rebased onto ASE 0.9.52 and extended substantially. The PR title and description have been rewritten accordingly; this comment summarizes what changed since the previous review state. Why: the previous revision priced models from a hand-maintained table of seven Claude models, and a model missing from that table contributed nothing at all — no error, no warning. On my archive What changed:
Verification: lint + tsc green; a hand-computed fixture total of $0.045500 for Codex + Copilot matched exactly (including a planted previous-month record that must be excluded, and a duplicated cumulative Copilot record that must not double-count); real archive renders Caveat — Vibe-Coded: the Codex and Copilot parsers follow the field names of 🤖 Generated with Claude Code |
Summary
Adds a new
%Ystatusline placeholder showing the cumulative cost (USD) across all agent sessions of the current calendar month — complementing%X, which only reflects the current session (cost.total_cost_usd).Example:
∑ month: $791.20Data sources
The figure is computed locally, without any network access, from the session logs of every agent tool ASE supports:
~/.claude/projects/**/*.jsonlCLAUDE_CONFIG_DIR~/.codex/{sessions,archived_sessions}/**/rollout-*.jsonlCODEX_HOME~/.copilot/session-state/**/*.jsonlCOPILOT_CONFIG_DIREach tool records usage differently, so each is normalized before pricing:
message.usageper API call, with cache writes split intoephemeral_5m/ephemeral_1h.event_msg/token_countrecord whoselast_token_usageis the delta of that call; itsinput_tokensincludes the cached tokens, so the uncached remainder is recovered, andreasoning_output_tokensare folded into the output.modelMetrics; itsinputTokensis cache-inclusive as well, and because the aggregate is cumulative, all records of one session and model share a key so only the final one is billed.Pricing
Prices come from a snapshot of the LiteLLM price database (
model_prices_and_context_window.json— the same sourceccusageandcodeburnuse), reduced to the chat/responses models of the vendors reachable through the supported agent tools (Anthropic, OpenAI, Gemini, xAI, DeepSeek, Mistral) — 273 models.tool/etc/litellm-prices.mjsinto the checked-in source moduletool/src/ase-statusline-prices.tsvia the new build targetnpm start prices-update. Only that target needs network; build and rendering stay fully offline.claude-sonnet-4.5) → id without vendor prefix (anthropic/claude-opus-5) → longest matching id prefix, which maps dated snapshots such asclaude-opus-5-20260401onto their base entry.Why this replaces the previous hand-maintained table: that table silently returned
$0for any model it did not list.claude-opus-5was missing andclaude-sonnet-5was carried at $3/$15 instead of $2/$10 — on the author's archive the month figure came out as $355.03 instead of ~$790, i.e. wrong by more than half, with no error and no warning.Month boundary
Entries are bucketed on UTC months, matching the day on which the model vendors bill and reset their usage windows. (A local-time boundary attributes late-evening calls to the wrong month; for the author's archive that is a ~$125 difference.)
De-duplication
A call logged more than once — while its response streams, or after a session was resumed or forked — is billed once, keeping the most expensive snapshot, since usage counts grow monotonically while a response streams.
Performance (non-blocking)
The statusline renders very frequently and a full scan takes ~1.5–3 s, so the scan never runs in the render path:
MonthCostCache: scheme version / month / cost / timestamp).ase statusline --refresh-month-cost) that recomputes and rewrites the cache.--month-cost-ttl <seconds>(default300).stat-skipped, keeping the scan off the archive.Robustness
Missing or empty logs, unreadable files, truncated trailing JSON lines of a live session, and unknown models all degrade gracefully — the placeholder simply renders nothing, never an error.
Verification
npm --prefix tool start build— lint + tsc green (against ASE 0.9.52).CODEX_HOME/COPILOT_CONFIG_DIR(with an emptyCLAUDE_CONFIG_DIR); hand-computed total $0.045500 (Codex $0.006350 + Copilot $0.039150) matchedcomputeMonthCost()exactly. The fixture exercises the cached-input subtraction, reasoning tokens folded into output, the dot-normalized model id, and the cache-write rate. A deliberately planted previous-month record of 10M tokens was correctly excluded — without the month filter the result would be off by orders of magnitude.session.shutdownrecord leaves the total unchanged at $0.045500.codeburn: at an identical (local-time) month definition this implementation reports $913.86 over 2763 calls versus codeburn's $844.84 over 2573 calls. The ~190 additional calls map 1:1 onto distinctrequestIds, i.e. they are real API requests codeburn drops; codeburn's own figure also moves between $822.54 (warm cache) and $834.63 (cold cache).--refresh-month-costwrites the cache,%X %Ythen renderscost: $1.23 month: $791.20; empty logs suppress%Y.Docs
%Y, its data sources per agent tool, the UTC boundary, the price snapshot, the caching behavior, and--month-cost-ttlare documented indocs/usage-tool.md; the newprices-updatebuild target is listed inAGENTS.md.Caveat
The Codex and Copilot parsers are Vibe-Coded: their log layouts and field names were taken from the provider implementations of
codeburn(MIT) and are verified only against the fixtures described above — neither tool has produced logs on the author's machine, so they have not been validated against real-world session data. The Claude Code path is verified against a real archive.🤖 Generated with Claude Code