What the cost figures mean
Claude, Codex, and Gemini costs are estimates: tokens multiplied by published per-token rates, taken from built-in tables and falling back to the LiteLLM database (cached for 24 hours). OpenCode and Pi already persist provider-calculated costs, which cctop reads directly — except where the figure is $0.00 against tokens that were spent, which is what both write for a provider they have no rates for rather than a model that was genuinely free; those turns are estimated like the rest.
Gemini records a per-turn token breakdown but no cost. Its cached count is the
part of the prompt served from context cache rather than an addition to it, so
only the uncached remainder is priced at the full input rate; thinking tokens are
priced as output, which is how Google bills them. A Gemini session on a bundled
Code Assist tier will still show an estimate — the transcript carries nothing
that says which tier it ran on, and the figure is honest about what the tokens
would cost at retail.
Cursor native-agent transcripts expose projects, conversation activity, and
tool calls. The model, the title, the real folder and the context occupancy come
from Cursor’s own state.vscdb when cctop finds it. Tokens and cost are
recorded nowhere — not in the transcript, not in the database — so they display
as unavailable rather than as free, and there is no dedicated per-session
process: live status is read off the transcript’s last turn and how recently it
changed.
Windsurf goes further: its workspace state records the conversation and its tool calls but no model, tokens, or cost at all — the credit accounting lives on Codeium’s servers. Those sessions report cost as unavailable rather than as free, and their timestamps come from the workspace database’s mtime, which is the only clock Windsurf leaves behind.
Devin is the third harness with no figure of its own, and the only one that
still fills every other column: its ATIF transcript records per-step token
metrics and its database records which model ran them, so CTX%, TOOLS,
TOKENS and ERR% are all real. Neither store holds a dollar amount, so a
Devin row’s cost reads as unavailable rather than as an estimate — the price of
its tokens is not something cctop can know, only guess, and a guess printed in
the $ column would be indistinguishable from one. --plan max counts Devin as
bundled, but that changes nothing on screen: incl stands for a figure cctop
computed and then withheld, and a Devin row has no figure to withhold.
Mistral Vibe is estimated: its token totals at Mistral’s published API rates
(docs.mistral.ai/inference/pricing), built into cctop because LiteLLM lists
Mistral’s models only through resellers and under other names. A model that
table does not carry falls back to LiteLLM, and to nothing after that. Vibe’s
Python CLI wrote a session_cost of its own, but priced from whatever rates the
user’s Vibe config carries, and its newer session store writes none, so cctop
uses neither and prices both layouts the same way. Vibe records no time on
any message, so a session’s cost counts toward “today” and the month on the day
it was last active.
Subscription plans — Claude Max, Pro, Team — are flat-rate or bundle tokens
differently, so these numbers will not match your invoice. Treat the $ column
as a measure of resource consumption, not as billing. Use --plan max or
--plan included to display bundled usage as incl instead.
Today’s model mix
The Overview’s Models row is today’s spend split by model: a short stacked bar, then each model’s name in its segment’s colour with its share of the day. Colours go by rank, not vendor, since the mix worth seeing is usually two Claude models against each other; past the fourth, models share one dim tail so the bar is always the whole of today. Like the Today figure, it counts every session rather than only the ones a filter leaves on the table. The names are placed first and the bar takes what they leave, up to sixteen cells; when that is fewer than eight the bar is left off and the row is the plain list of percentages.
Where each provider’s data comes from
| Source | Path |
|---|---|
| Claude Code (CLI) | ~/.claude/projects/<slug>/<uuid>.jsonl |
| Claude for Mac | ~/Library/Application Support/Claude/{claude-code,local-agent-mode}-sessions/ |
| Codex | ~/.codex/sessions/YYYY/MM/DD/rollout-*.jsonl |
| Cursor | ~/.cursor/projects/*/agent-transcripts/*/*.jsonl |
| Gemini CLI | ~/.gemini/tmp/<project>/chats/session-*.json{,l} |
| Devin | ~/.local/share/devin/cli/sessions.db (platform data directory), with transcripts/<id>.json beside it |
| OpenCode | ~/.local/share/opencode/opencode*.db (platform data directory) |
| Pi | ~/.pi/agent/sessions/**/*.jsonl |
| Mistral Vibe | ~/.vibe/logs/session/{session_*,unified/*}/meta.json |
| Windsurf | <Windsurf User dir>/workspaceStorage/*/state.vscdb |
CLAUDE_CONFIG_DIR, CODEX_HOME, CURSOR_HOME, GEMINI_DIR,
OPENCODE_DATA_DIR, PI_CODING_AGENT_DIR, PI_CODING_AGENT_SESSION_DIR,
VIBE_HOME and WINDSURF_USER_DIR are honoured, along with OPENCODE_CONFIG_DIR for the
plugin --install-hooks writes. CHISEL_SESSION_DB moves Devin’s, and is the
only one that names a file rather than a directory: cctop takes the parent
of the database it points at, because the transcript has to sit beside it.
Caches live in ~/.cache/cctop/.
Gemini and Windsurf sessions are read from disk but not matched to a running process: neither takes a session id on its command line, so there is nothing to tie a PID to a transcript. Their rows always read as stopped, and the CPU and memory columns stay blank rather than showing another session’s figures.
The left status dot is green while an agent is working, amber after its latest
response is waiting for your input, and red when the newest transcript event is
an API error. A hollow grey dot is a stopped session, and a filled ◉ is the
session that rang in the last 30 seconds.