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:
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): stringCosts 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 | undefinedThe 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:
- Stores large paste content in the external paste store
- Appends the entry to the pending buffer
- Flushes pending entries to
~/.claude/history.jsonlusing file locking vialock()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.