Antigravity Usage and Cost Capture#
What is "Antigravity"?#
"Antigravity" is the internal code name for a Google coding CLI (analogous to Claude Code or Codex). In the decant codebase, Antigravity support is surfaced as Gemini CLI ingestion β the same tool referred to publicly as gemini in decant's source model .
Why Transcripts Alone Cannot Produce Exact Cost#
Gemini CLI stores sessions under ~/.gemini/tmp/<project-hash>/chats/session-*.jsonl . These JSONL event logs carry no model, token-usage, or cost fields at the file level; token data appears only on individual gemini-type message records, in a per-turn tokens object .
From the tokens record, decant extracts:
| Field | Meaning |
|---|---|
input | Prompt tokens (includes cache reads; decant subtracts cached) |
output | Candidate tokens |
cached | Cache-read tokens |
thoughts | Reasoning tokens (billed as output, reported separately) |
Gaps in the transcript-only picture:
- Cache creation tokens are never recorded by Gemini CLI; decant leaves
cacheCreationat zero rather than guessing . - No session-level result record exists equivalent to Claude Code's
type: resultaggregate. Claude's parser uses a terminalresultrecord for authoritative session totals ; Gemini has no such record, so decant sums per-turn usage instead . - Cost is not embedded in any Gemini CLI transcript field. Decant computes it post-ingest from first-party published API rates via
estimateCost(model, usage, pricing).
Data Sources for Usage and Cost#
1. Per-Message Token Records (Primary β both interactive and headless)#
Gemini messages carry a tokens object on every assistant turn. Decant sums these across turns to produce session totals . This is the authoritative source for input/output/cache-read/reasoning tokens for all Gemini runs.
2. Status-Line cost.estimated Sidecar (Interactive Runs)#
Since Antigravity CLI 1.1.21, the CLI's custom status-line state payload exposes a field cost.estimated containing an unrounded cumulative session cost estimate, plus conversation_id, transcript_path, model, and context-window token counters. For interactive sessions, a sanitized per-conversation sidecar persisted from this payload is the recommended way to capture a local-first cost estimate without re-implementing Google's billing math.
Claude Code uses an analogous pattern: decant reads a .meta.json sidecar written beside each session JSONL file via readClaudeSidecarMeta(), then passes the parsed metadata as sidecarMeta to the session parser .
3. Terminal Result Events (Headless Runs)#
For headless (non-interactive) Antigravity runs, the CLI emits a terminal JSON or stream-json result event containing cumulative token counts: input_tokens, output_tokens, thinking_tokens, cache_read_tokens, and total_tokens. Cost is not included in this payload β it must still be derived from token counts Γ published rates. Persisting these terminal result events is the recommended capture path for headless pipelines.
Why Hooks Are Insufficient#
Antigravity hooks expose conversationId, transcriptPath, and modelName β enough to locate a session, but no token usage or cost. They cannot substitute for transcript-derived per-turn token records or a status-line sidecar.
Why the Per-Conversation SQLite Protobuf Metadata Is Not a Safe Contract#
Antigravity stores per-conversation data in a local SQLite database that embeds protobuf-encoded metadata. This metadata appears to contain usage and model information, but Google does not publish the protobuf schema. Relying on it as a primary cost/usage source is fragile: schema changes are unversioned and silent, and decoding requires reverse-engineering private binary formats. Decant's ingest pipeline deliberately avoids this path; all sources are ingested from their plaintext JSONL logs .
How Decant Computes Cost from Available Data#
Once token counts are captured, cost is computed deterministically:
- Model canonicalization:
estimateCost(model, usage, pricing)normalizes the model string to a pricing key; unrecognized models return cost 0 rather than guessing . - First-party rates: Gemini models are priced at published Google API rates, versioned by date in
docs/pricing.md. Each flash-lite variant has its own rate; image/TTS/live/transcribe variants remain unpriced because they bill per output unit, not per token . - Cache token adjustments: Gemini's
inputtoken count already includes cached tokens. Decant subtractscachedfrominputbefore pricing, then pricescacheReadseparately .
Key Source Files#
| File | Role |
|---|---|
src/sources/gemini.ts | Gemini/Antigravity session parser β event log replay, token extraction, usage accumulation |
src/sources/claude.ts | Reference parser showing sidecar meta and type:result aggregate patterns |
src/ingest.ts | readClaudeSidecarMeta() (lines 1203β1219) β sidecar read pattern to adapt for Antigravity |
src/cost.ts | estimateCost() β model-keyed pricing lookup |
src/model.ts | TokenUsage interface and emptyUsage() β canonical token shape |