/cost, close and restart the accounting period with /cost reset, review past sessions with /cost last and /cost sessions, export machine-readable snapshots with /cost export, and configure spending limits — including an optional hard stop.
The /cost Command
/cost renders the live session summary; four subcommands manage the accounting period:
The summary is rendered as a boxed panel (values below are self-consistent for
claude-sonnet-5):
The Source row reports whether the session has real API usage; each model line in the cost breakdown additionally carries its own tag —
(API) for provider-reported counts, (estimate) for character-based estimation — because one session can mix both. Reasoning tokens (o-series / GPT-5 / Gemini thinking) get their own informational line when present; they are already billed inside Output./cost lists them explicitly (No pricing table for: provider/model — this spend is NOT in the total.), so an under-reported total is always visible as such.
Prompt Caching by Provider
ChatCLI arranges every request so the part that does not change between turns comes first (system prompt, attached contexts, tool catalog), and then asks the provider to cache it wherever the API offers a handle for that. The Prompt cache line in/cost and the cache NN% figure in the chat/agent footer are provider-neutral: they are computed from the cache fields every provider reports, on both accounting schemas (Anthropic/Bedrock count cache reads alongside input; OpenAI/Gemini/Grok/Kimi count them as a subset of the prompt).
- requests — how many calls reported cache activity.
- % of input from cache — share of the session’s input served from cache.
- misses — requests that re-processed a meaningful chunk (≥ 2,000 tokens and > 5%) of what the cache already held: the signature of a stable prefix that changed (model or effort switch, MCP tool set churn, refreshed attachment). Three misses in a row print a one-shot notice.
- expected rebuilds — misses ChatCLI itself caused by changing the prefix on purpose: rewriting the conversation (auto-compact,
/compact, microcompact, skill aging, overflow recovery, provider context edits) or switching what the prefix holds (/switchprovider or model,/agent attach|detach,/context attach|detach,/session load|attach, MCP server start/stop/restart/reload/login/logout, skillpin|unpin). Not a problem. - per provider — “first request” and the hit ratio are tracked per provider (each has its own cache and schema), so switching providers never spends the other’s first, and the ratio shown is the last-used provider’s own. On the subset-schema providers (OpenAI, Gemini, xAI, Kimi) a prompt at or above the cacheable minimum served with
cached_tokens: 0after that provider’s first request counts as a miss — so the 3-miss alert works there too. Bedrock reports the TTL the marker actually carried (1h on Claude 4.5+ when configured). - warm / cold — whether the last activity is still within the cache TTL.
Byte-stable prefix and the turn context message
Every provider caches by prefix, so a system message that changes between turns makes every breakpoint after it miss. ChatCLI keeps the system message byte-stable for the session: everything that changes per turn — the date (day resolution), proactive memory and session recall, auto-activated and manual skills, MCP channel pushes, the watcher snapshot, retrieved passages — rides as one user-role message flaggedturn_context, placed right before your turn and persisted with the conversation, so the next request replays identical bytes up to the previous breakpoint. Chat, the RPC surfaces and agent/coder share the mechanism; the injected message is labeled in exports and ignored by memory extraction and recall hints. The OpenAI prompt_cache_key hashes only the cache-marked stable parts, and the prefix budget freezes its chars-per-token ratio per session so cached sections do not fold and unfold between turns.
Explicit cache resources (Gemini)
Implicit caching is free and automatic. Gemini also exposes the cache as a resource (cachedContents) with a guaranteed discount on every read, billed as storage per token-hour (Flash 4.50/M/h, 3.x Flash $0.50/M/h). Because that charge never appears in a response, it is opt-in: CHATCLI_PROMPT_CACHE_EXPLICIT=true. The policy is conservative by design:
- The resource holds the entire system instruction; the request references it and omits
system_instruction. - Only prompts of roughly 4K+ tokens qualify, and the same prompt must be seen on two consecutive requests before a resource is created — a one-shot never pays for storage.
- Lifetime is
CHATCLI_PROMPT_CACHE_TTL(5m,1horauto), extended when less than half remains; the resource is deleted when the prompt changes and at shutdown. - A resource the API rejects (expired, deleted, floor too low) is dropped and the turn is retried inline — a cache can never fail a turn. Refusals back the prompt off for 10 minutes, and a “too small” answer teaches the floor.
/costprints the storage bought (Cache storage: N resource(s) · $x) and counts it in the session total; the amount is persisted with the cost snapshot.
Embedding cost
Every embedding call of the configured provider (knowledge retrieval and warm-ups, memory vectors, HyDE) is metered: tokens are estimated from characters at the provider’s list rate per million tokens, and/cost prints Embeddings: N call(s) · ~T tokens (estimated from characters) · $x. Ollama meters at $0. The amount joins the session total and persists with the snapshot.
Background spend and attribution
Every request is booked to the call that made it. The Level 2 summarizer’s usage is read from its own call (an external context engine, an early return or a failure books nothing; a retried summary bills both calls). The memory worker (extraction, rollups, memory compaction) runs on its own client —CHATCLI_COMPACT_MODEL when set, otherwise a dedicated instance of the session model — so it never clobbers the interactive turn’s usage; it honors CHATCLI_BUDGET_HARD_STOP like every other caller and /cost prints Memory worker: N call(s) · $x. A streamed reply that ends without a usage block is booked as a character estimate (flagged as such) rather than as free, and providers reset their usage before each request so a response without usage can never re-book the previous call. Gemini thinking tokens (thoughtsTokenCount) are billed on top of the candidates count; OpenAI reasoning tokens are already inside completion_tokens and stay informational.
Compaction cost
/cost also prints Compactions: N (level 3: M) · summarizer $x once the history was compacted: how many times, how many landed at Level 3 (emergency truncation) and what the Level 2 summarizer consumed. The summarizer’s request is a real request on the session route (or on CHATCLI_COMPACT_MODEL), so it joins the session totals like any turn; the counters persist with the session. See context recovery for the back-off that stops paying the summarizer when it cannot converge.
Moonshot/Kimi context caching is fully automatic on the current platform (no cache ids, no management API; the previous request must exceed 256 prompt tokens), so nothing is created there — the cached_tokens split is what the tracker prices.
Token counting by provider
The footerctx %, the compaction budget and /context status share one estimate with four categories — system prompt, history, native tool definitions and the answer reserve (max_tokens as sent, capped at a quarter of the window) — the same breakdown Claude Code’s /context shows. The footer renders the reserve apart — ctx 2% (+14% reserve) — so a fresh conversation never reads as a capped window: the first number is what already occupies the window, the second is the space the next request must leave free for the answer (providers require input + max_tokens ≤ window; lower /max-tokens shrinks it). /context status prints the two last lines, and the compactor reserves the prompt and the tool definitions next to the history it measures. The retrieved-passages budget scales with the window (at most 24K chars, 15% of the window in chars, 4K floor). The chars-per-token ratio behind that estimate is learned from every provider’s real usage (the same 14-provider coverage as the table below), so it is exact after the first turn everywhere. Providers that also expose a counting API, and the GPT family through a local tokenizer, anchor the ratio before a request goes out — one free count every 8 chat turns and on /context status, which then prints the provider-counted size of the live history:
A failed or slow count (10 s bound) never affects a turn: the learned ratio simply stands.
The learned ratios are persisted in
~/.chatcli/calibration.json (atomic write, flushed at exit) and reloaded on the next start, so a new process does not begin at 4 chars per token again. Under the gateway the file lives under each tenant’s root, so tenants never share ratios. Every estimate in the CLI reads the same learned ratio: the file-processing budget, /metrics, /context list and attach feedback, the compression-savings footer, the compaction budget and the context manager’s chunker, validator and digests.
Real API Data — Provider Coverage
The tracker prefers real usage from the provider’s response and falls back to a character estimate (chars/4, marked IsReal=false) only when the provider reports nothing:
Modes covered
Every LLM call a session makes is accounted for and attributed to the model that actually served it (skill hints and@model route overrides included):
- Chat — streaming, buffered, and the
/asktool-exception path - Agent / Coder — every ReAct turn, plus dispatched workers/subagents (each worker’s client records every LLM call live, under the worker’s own provider+model — and the budget hard stop gates each call)
- MoA panels — native-tools, XML and plain paths, attributed per participant
- One-shot (
-p), RPC/Gateway/ACP turns and scheduled runs (the scheduler runs on a dedicated client and receives token/cost figures back — a deliberate self-contained estimate, immune to races with interactive turns)
Pricing Tables
Prices in USD per 1M tokens, matchingcli/cost_tracker.go (pinned by cost_tracker_pricing_test.go). Matching is by model-id substring, most specific first.
Anthropic (cache write = 1.25× input, cache read = 10% of input — 2.5% on Fable 5.1)
OpenAI (cache read = 50% of input, no write surcharge)
Google (cache read = 25% of input)
xAI (Grok) — cached input = 25% on grok-4.6, 15% on grok-4.5, 16% elsewhere; no write surcharge
Z.AI (GLM)
DeepSeek (peak price; cache read = 25% of input)
Moonshot (Kimi) — cache-miss price, cache read = 17% of input
Others
Cache Accounting
Cache tokens are priced per family — and, crucially, with the correct semantics per provider:- Anthropic / Bedrock Anthropic report cache tokens alongside
input_tokens(additive): write is billed at 1.25× input, read at 10% (2.5% on Fable 5.1). - OpenAI, xAI, Gemini, DeepSeek, Moonshot report cache reads as a subset of the prompt count: the cached slice is carved out of the input and billed once at the discounted rate — never full price plus discount.
Cache Tokens block shows the real dollar savings of the session (~ $X saved vs uncached), computed from each model’s actual cache-read discount — not a fixed percentage.
Session Persistence
Cost data is written through to disk as the session runs (throttled, atomic writes):-p), the gateway daemon and the MCP/ACP servers persist the cost snapshot and the daily spend and release paid provider caches on exit, exactly like the REPL’s cleanup; embedding spend counts toward the daily budget. When the budget hard stop trips inside the agent loop the run is parked (resume at the next local midnight, or /parked resume once the budget is raised) instead of dying with the work lost.
Long-context tiers, subscriptions and the daily budget. A call past the provider’s long-context threshold is priced at its tier per call — Claude over 200K context (2× input, 1.5× output), Gemini 2.5 Pro over 200K (2× / 1.5×), Grok 4 over 128K (2× / 2×) — and booked as a billed amount so the aggregate never averages it away; the footer estimate agrees. GitHub Copilot is a subscription (premium requests, not tokens) and is booked as known-zero. Every known-zero model that carried tokens is named under the total (No per-token rate known for: …) so a $0 never reads as free when it means unknown, and CHATCLI_MODEL_PRICING attributes a rate to it. Amazon Nova Micro/Lite/Pro/Premier carry their on-demand list rows; Nova 2 generations are reported as unpriced until their list price is verified rather than guessed. A daily-only budget (CHATCLI_DAILY_BUDGET_USD without a session limit) announces its own warning and exceeded notices, and when both limits are set the daily one speaks when it tripped; /cost export path.csv writes one CSV row per provider/model plus a total.
The snapshot (SessionCostData) carries per-model usage records, totals, timestamps and the bound session name when one is set. Snapshots are also saved on shutdown and on /cost reset, and pruned after 90 days.
/cost last— the previous session’s (or period’s) spend/cost sessions— the 10 most recent snapshots with date, requests, tokens and total/cost export [path]— current snapshot as JSON, for pipelines and audits
Session Budget
All three are re-read on
/reload — no restart needed to change a budget mid-session.
Budget Levels
The notice is proactive: chat, agent and coder print it on the turn the session crosses a level — you don’t have to remember to run
/cost. Without CHATCLI_BUDGET_HARD_STOP, exceeding the budget never interrupts the session; with it, new turns (chat, agent loop, dispatched squad workers mid-wave, MoA participants, scheduled runs, RPC/gateway) are blocked until the limit is raised or /cost reset starts a new period.
Next Steps
Conversation Control
Use /compact to reduce tokens and costs.
One-Shot Mode
Monitor costs in automated pipelines.
Token Efficiency
The optimizations /cost lets you verify.
Model Routing
Per-model attribution of every routed turn.