import type { TurnRow } from '../db/repositories/usage-repository.js'; import { type CostService } from './cost-service.js'; export interface CacheBreakOptions { /** Minimum spike ratio (writes / rolling baseline) to flag as a break. Default 10. */ spikeThreshold?: number; /** Minimum absolute cache writes on a turn to consider. Default 5000. */ minCacheWrites?: number; /** Number of previous turns in the same stream averaged for the baseline. Default 5. */ baselineWindow?: number; } export interface CacheBreakEvent { /** Position of the turn within the session's analyzed turns (0-based). */ turnIndex: number; timestamp: string; model: string; turnKind: TurnRow['turnKind']; cacheWriteTokens: number; cacheReadTokens: number; /** Tokens the previous turn in the same stream had cached (its reads + writes). */ previousPrefixTokens: number; writeSpikeRatio: number; /** Rolling average of cache writes over the previous turns in the same stream. */ baselineWriteAvg: number; /** Idle time since the previous turn in the same stream, in ms. */ idleMs: number; /** * API-equivalent cost of writing this turn's cache instead of reading it, from * the versioned pricing table. Absent when the model has no price: unavailable, * never 0. */ estimatedExtraCost?: number; explanation: string; } /** Prices one break's write premium; undefined when the model cannot be priced. */ export type BreakPricer = (turn: TurnRow) => number | undefined; /** * A break's cost is the write premium: what the rewritten prefix cost at * cache-write rates, less what reading the same tokens back would have cost. */ export declare function writePremiumPricer(costService: CostService): BreakPricer; /** * Finds turns where the prefix cache was invalidated. * * A write spike alone is not a break: reading a large file writes a lot of new * cache while still reading the whole existing prefix. A break is a write spike * AND a prefix that was not reused -- the turn read back less than half of what * the previous turn had cached. * * Main turns and subagent turns keep separate caches, so each is baselined * against its own stream; mixing them would read every switch between the two * as a break. */ export declare function detectCacheBreaks(turns: TurnRow[], options?: CacheBreakOptions, priceBreak?: BreakPricer): CacheBreakEvent[];