import type { CollectOptions, CollectResult, CollectorAvailability, UsageCollector } from '../../models/usage-record.js'; import { type CostService } from '../../services/cost-service.js'; /** * Bumped whenever the collector starts reading something new from transcripts, * so files ingested by an older reader are read again and the new fields filled * in. 2: `gitBranch` and `ai-title` lines (0.12). * * A migration cannot do this job alone. Two servers of different versions can * share one database -- Claude Code's and OpenCode's, updated separately -- and * an older one re-syncing after the migration writes a fresh cursor with its own * reader, after which the newer one would skip every file as unchanged and never * see the new fields. A cursor without this version is treated as no cursor. */ export declare const CLAUDE_CURSOR_VERSION = 2; export interface ClaudeCursor { version: typeof CLAUDE_CURSOR_VERSION; /** Per transcript path: size + mtime at the time it was fully ingested. */ files: Record; } /** * Reads token usage from Claude Code's JSONL session transcripts. * * Grain: one record per *API request*, keyed on `requestId` + `message.id`. * * The dedupe is the whole ballgame. Claude Code writes one line per content * block (thinking, tool_use, text) and repeats the same `usage` object on each, * with `output_tokens` growing to its final cumulative value on the line that * carries a `stop_reason`. On the machine this was verified against, 11,860 lines * carrying usage collapse to 5,006 real requests -- summing the lines naively * inflates every number by roughly 2.4x. * * Reduction rule: take the MAX of each field across the group. * - input / cache_read / cache_creation are identical on every line of a group, * so max is a no-op for them. * - output_tokens is cumulative, so max selects the final value. This agreed * with the `stop_reason`-bearing line in 4,886 of 4,889 groups; the 3 * exceptions were replayed lines with all-zero usage, where max is the * correct choice and picking "the last line" would have been wrong. * - `usage.iterations[]` is NOT summed: its entries are already included in the * top-level totals (verified on 8,257 of 8,262 groups; the rest were empty * arrays). */ export declare class ClaudeCodeCollector implements UsageCollector { private readonly costService; readonly name = "claude-code"; readonly client: "claude-code"; constructor(costService: CostService); isAvailable(): Promise; collect(options: CollectOptions): Promise; /** Streams one transcript and reduces its lines to unique API requests. */ private readTranscript; private toRecord; }