AI Assistant

Cost & History

Claude Code tracks token usage and costs per session and persists prompt history across sessions for recall and search.

Cost Tracker

The cost tracking system (cost-tracker.ts) maintains per-model usage statistics and aggregated session costs.

Tracked Metrics

The cost tracker exposes these aggregated metrics through the bootstrap state:

totalCostUSDnumber
Total session cost in USD, summed across all models.
totalInputTokensnumber
Total input tokens across all API calls.
totalOutputTokensnumber
Total output tokens across all API calls.
totalCacheReadInputTokensnumber
Tokens read from the prompt cache.
totalCacheCreationInputTokensnumber
Tokens written to the prompt cache.
totalWebSearchRequestsnumber
Number of web search tool invocations.
totalAPIDurationnumber
Total time spent in API calls (ms).
totalAPIDurationWithoutRetriesnumber
API duration excluding retried requests.
totalToolDurationnumber
Total time spent executing tools (ms).
totalLinesAddednumber
Lines added to files during the session.
totalLinesRemovednumber
Lines removed from files during the session.

Per-Model Usage

Usage is tracked per model via ModelUsage:

type ModelUsage = {
  inputTokens: number
  outputTokens: number
  cacheReadInputTokens: number
  cacheCreationInputTokens: number
  webSearchRequests: number
  costUSD: number
  contextWindow: number
  maxOutputTokens: number
}

The getModelUsage() function returns the full usage map, and getUsageForModel(model) returns usage for a specific model. When formatting, usage is aggregated by canonical model name (e.g., different versions of the same model family are grouped).

Cost Calculation

USD cost is calculated via calculateUSDCost() using per-model pricing tables. When a model's pricing is unknown, setHasUnknownModelCost(true) is called, and the UI displays a warning.

Cost Formatting

function formatCost(cost: number, maxDecimalPlaces?: number): string

Costs above $0.50 are rounded to two decimal places. Smaller costs show up to four decimal places for precision.

Session Cost Persistence

Costs are persisted to the project config file so they survive session restores:

// Save current costs
function saveCurrentSessionCosts(fpsMetrics?: FpsMetrics): void

// Restore costs for a resumed session
function restoreCostStateForSession(sessionId: string): boolean

// Read stored costs without overwriting
function getStoredSessionCosts(sessionId: string): StoredCostState | undefined

The stored state includes:

type StoredCostState = {
  totalCostUSD: number
  totalAPIDuration: number
  totalAPIDurationWithoutRetries: number
  totalToolDuration: number
  totalLinesAdded: number
  totalLinesRemoved: number
  lastDuration: number | undefined
  modelUsage: { [modelName: string]: ModelUsage } | undefined
}

Costs are only restored when the lastSessionId in the project config matches the session being resumed, preventing cross-session cost contamination.

History Management

Prompt history is stored in a global JSONL file and provides per-project recall.

Storage

History entries are appended to ~/.claude/history.jsonl as newline-delimited JSON:

type LogEntry = {
  display: string                              // Display text for history list
  pastedContents: Record<number, StoredPastedContent>
  timestamp: number
  project: string                              // Project root path
  sessionId?: string
}

The maximum history size is MAX_HISTORY_ITEMS = 100 entries per project.

Pasted Content Storage

Pasted content is tracked alongside history entries. Small pastes are stored inline; large pastes (over MAX_PASTED_CONTENT_LENGTH = 1024 characters) are stored externally in a paste store and referenced by hash:

type StoredPastedContent = {
  id: number
  type: 'text' | 'image'
  content?: string       // Inline for small pastes
  contentHash?: string   // Hash reference for large pastes
  mediaType?: string
  filename?: string
}

Paste references in prompts follow these formats:

  • Text: [Pasted text #1 +10 lines]
  • Image: [Image #2]

The expandPastedTextRefs() function resolves these placeholders back to their content.

History Retrieval

Returns history entries for the current project with session-aware ordering. Current session entries appear first, followed by entries from other sessions:

async function* getHistory(): AsyncGenerator<HistoryEntry>

This prevents concurrent sessions from interleaving their up-arrow history.

Writing History

History entries are written via addToHistory(), which:

  1. Stores large paste content in the external paste store
  2. Appends the entry to the pending buffer
  3. Flushes pending entries to ~/.claude/history.jsonl using file locking via lock() to prevent corruption from concurrent sessions

Removing History

The removeLastFromHistory() function marks the most recent entry for the current session as skipped. If the entry is still in the pending buffer, it is removed directly. If already flushed to disk, its timestamp is added to a skippedTimestamps set and filtered during reads.

File Reading

History is read from disk using readLinesReverse(), which reads the JSONL file from the end for efficient newest-first iteration. Malformed lines are skipped with a debug log rather than throwing errors, providing resilience against file corruption.