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 rollouts. */ export declare const CODEX_CURSOR_VERSION = 1; export interface CodexCursor { version: typeof CODEX_CURSOR_VERSION; /** Per rollout path: size + mtime at the time it was fully ingested. */ files: Record; } /** * Reads token usage from Codex CLI's session rollouts (`$CODEX_HOME/sessions`). * * Grain: one record per *model request*, read from `event_msg` lines of type * `token_count`. Each carries `info.last_token_usage` (that request) and * `info.total_token_usage` (the session's running total). * * The dedupe is the whole ballgame, again. Codex re-emits `token_count` with an * unchanged running total: once more after most requests, at the start of the * next turn, after a compaction, and with `info: null` for a rate-limit update. * On the 0.114.0 session verified, 76 `token_count` events held 36 real requests * -- summing every event's `last_token_usage` doubles the session. The compaction * repeat is worse than a copy: its `last_token_usage` is all zeros except * `total_tokens: 8376`. * * Reduction rule: a `token_count` is a new request only when its * `total_token_usage` differs from the previous event's. That is the running * total Codex itself keeps, so it moves exactly when Codex counted something: * on both sessions verified, `last_token_usage` equalled the change in the total * on every field of every counted event, and the counted events summed to the * final total exactly. * * `token_usage_record` lines (0.162 on) are NOT read here. Each is the same * request again -- the same `usage` as the `token_count` that follows it, plus a * `response_id` -- so reading both would count every request twice. `verify` * uses them as an independent check instead. * * Normalisation, which differs from both other clients: OpenAI counts cached * input INSIDE `input_tokens` and reasoning INSIDE `output_tokens` (verified: * `total_tokens === input_tokens + output_tokens` on every counted request, * with reasoning non-zero on most of them). Our `inputTokens` is uncached input, * as it is for Claude Code and OpenCode, so cached input is subtracted out of * it and stored as cache-read. Reasoning stays inside output, as for Claude Code. */ export declare class CodexCollector implements UsageCollector { private readonly costService; readonly name = "codex"; readonly client: "codex"; constructor(costService: CostService); isAvailable(): Promise; collect(options: CollectOptions): Promise; /** Streams one rollout and reduces its `token_count` events to model requests. */ private readRollout; private toRecord; }