import { ICacheDiffResult, INormalizedMessage } from "./chatDebugCacheDiff.js"; /** Severity of a single cache finding. Rendered as a codicon + color. */ export declare enum CacheInsightSeverity { /** Expected, healthy behavior (e.g. new turn appended). */ Ok = "ok", /** Context that helps interpretation but isn't actionable. */ Info = "info", /** Suspicious but not definitively avoidable (e.g. likely expiration). */ Warning = "warning", /** An avoidable cache break the user should investigate. */ Critical = "critical" } /** * Coarse classification of what broke (or didn't break) the cache for one * request pair. Used for rail chips and cross-turn aggregation. */ export declare enum CacheBreakCategory { /** Pure append or fully stable prefix — the cache behaved as designed. */ Healthy = "healthy", /** Prefix byte-identical but the cache still missed — TTL/eviction. */ Expiration = "expiration", Model = "model", Tools = "tools", System = "system", Options = "options", /** Conversation history rewritten or truncated in place. */ History = "history", /** Continuations and other pairs we can't classify. */ Unknown = "unknown" } /** One human-readable finding about the cache behavior of the current request. */ export interface ICacheInsight { readonly severity: CacheInsightSeverity; /** Short, scannable summary — doubles as the headline verdict for the first non-OK finding. */ readonly title: string; /** Evidence: what exactly differs and by how much. */ readonly detail?: string; /** Actionable guidance derived from how prefix caches work. */ readonly hint?: string; /** Name of the Components entry this finding refers to (e.g. `system`, `tools`, `messages[7]`). */ readonly component?: string; /** Break category this finding contributes to, for cross-turn aggregation. */ readonly category?: CacheBreakCategory; } /** A request-option change, pre-formatted by the caller for display. */ export interface ICacheInsightOptionDelta { readonly key: string; readonly previousLabel: string; readonly currentLabel: string; } export interface ICacheInsightsInput { readonly aModel: string | undefined; readonly bModel: string | undefined; readonly aSystem: string | undefined; readonly bSystem: string | undefined; readonly aTools: string | undefined; readonly bTools: string | undefined; readonly aMessages: readonly INormalizedMessage[]; readonly bMessages: readonly INormalizedMessage[]; readonly diff: ICacheDiffResult; readonly optionsDiff: readonly ICacheInsightOptionDelta[]; /** Cache hit percentage (0-100) reported for the current request. */ readonly hitPct: number; /** Total input tokens reported for the current request (0 when unknown). */ readonly inputTokens: number; /** Minutes elapsed between the start of the previous and the current request. */ readonly minutesSincePrevious: number | undefined; /** True when the current request is a Responses API continuation (delta-only wire input). */ readonly isContinuation: boolean; /** True when the previous request is a Responses API continuation. */ readonly previousIsContinuation: boolean; /** False when message-level positional diffing is suppressed (either side is a continuation). */ readonly compareInputMessages: boolean; } /** How two unequal strings relate to each other structurally. */ export declare enum StringDivergenceShape { /** B is a strict suffix of A — leading bytes were removed. */ LeadingRemoved = "leadingRemoved", /** A is a strict suffix of B — bytes were prepended. */ LeadingAdded = "leadingAdded", /** B is a strict prefix of A — trailing bytes were removed. */ TrailingRemoved = "trailingRemoved", /** A is a strict prefix of B — bytes were appended. */ TrailingAdded = "trailingAdded", /** The change happens somewhere in the middle. */ InnerEdit = "innerEdit" } export interface IStringDivergence { readonly shape: StringDivergenceShape; /** Number of leading chars shared by both sides. */ readonly commonPrefix: number; /** Number of trailing chars shared by both sides (disjoint from the prefix). */ readonly commonSuffix: number; readonly aLength: number; readonly bLength: number; /** Excerpt of the changed region on the A side (capped). */ readonly aChanged: string; /** Excerpt of the changed region on the B side (capped). */ readonly bChanged: string; } /** * Locate where two strings diverge: the shared leading/trailing spans and * the changed region in between. Returns `undefined` when the strings are * equal. The common prefix and suffix never overlap, so * `commonPrefix + commonSuffix <= min(aLength, bLength)`. */ export declare function analyzeStringDivergence(a: string, b: string): IStringDivergence | undefined; /** One-line human-readable description of a string divergence. */ export declare function describeStringDivergence(d: IStringDivergence): string; /** Categories of volatile values that classically break prompt caches. */ export declare enum VolatileValueKind { Timestamp = "timestamp", Uuid = "uuid", Counter = "counter" } /** * Detect whether the changed region on both sides carries the *same kind* of * volatile value (timestamp, UUID, epoch-like counter) with *different* * contents — the classic silent cache invalidator: `datetime.now()` or a * request id interpolated into the prompt. */ export declare function detectVolatileValue(aChanged: string, bChanged: string): VolatileValueKind | undefined; /** Structured comparison of two JSON tool catalogs. */ export interface IToolCatalogDelta { readonly added: readonly string[]; readonly removed: readonly string[]; /** Tools present on both sides whose definition bytes differ. */ readonly modified: readonly string[]; /** True when the set and every definition match — only the order differs. */ readonly reorderedOnly: boolean; readonly aCount: number; readonly bCount: number; } /** * Compare two tool catalogs at the tool level: which tools were added, * removed, or had their definition change — or whether the catalog was * merely reordered (same tools, same bytes, different order). Returns * `undefined` when either side isn't a parseable JSON array, in which case * callers should fall back to byte-level divergence. */ export declare function analyzeToolCatalog(aTools: string | undefined, bTools: string | undefined): IToolCatalogDelta | undefined; /** The highest severity present in a findings list (Ok when empty). */ export declare function maxInsightSeverity(insights: readonly ICacheInsight[]): CacheInsightSeverity; /** The first warning-or-worse finding — the headline verdict. */ export declare function primaryInsight(insights: readonly ICacheInsight[]): ICacheInsight | undefined; /** * Produce the ordered findings list for an A→B request comparison. * * Findings are emitted in provider cache-key order (model, tools, system, * options, messages) so the first critical finding is the earliest byte * change — the one that actually broke the cache; later changes are * recomputed regardless. */ export declare function computeCacheInsights(input: ICacheInsightsInput): ICacheInsight[]; /** Resolve the break category for one request pair from its findings. */ export declare function categorizeCacheBreak(insights: readonly ICacheInsight[]): CacheBreakCategory; /** Human-readable label for a break category. */ export declare function cacheBreakCategoryLabel(category: CacheBreakCategory): string; /** The outcome of analyzing one consecutive request pair in a session. */ export interface ISessionPairOutcome { /** Index of the current (B) turn in the session's turn list. */ readonly turnIndex: number; readonly category: CacheBreakCategory; /** Input tokens of the B request that were not served from cache. */ readonly lostTokens: number; } /** Aggregate stats for one break category across a session. */ export interface ISessionCategoryStat { readonly category: CacheBreakCategory; readonly count: number; readonly lostTokens: number; } /** Token counts for one turn, used for the token-weighted overall hit rate. */ export interface ISessionTurnTokens { readonly inputTokens: number; readonly cachedTokens: number; } /** * Token-weighted cache hit across a whole session. Per-request percentages * overweight small utility calls; weighting by input tokens shows the real * cost picture. */ export interface ISessionOverallHit { readonly inputTokens: number; readonly cachedTokens: number; /** `cachedTokens / inputTokens` as a percentage (0-100). */ readonly hitPct: number; /** Number of turns that reported token usage. */ readonly turnCount: number; } /** Cross-turn cache health report for a whole session. */ export interface ISessionCacheReport { readonly pairCount: number; readonly healthyCount: number; /** Uncached tokens across pairs whose break was avoidable. */ readonly avoidableLostTokens: number; /** Token-weighted overall hit rate; undefined when no turn reported usage. */ readonly overall: ISessionOverallHit | undefined; /** Per-category stats, sorted by lost tokens descending (healthy excluded). */ readonly byCategory: readonly ISessionCategoryStat[]; /** Per-turn category, for rail decoration. */ readonly causeByTurnIndex: ReadonlyMap; /** Session-level findings: recurring invalidators and the overall verdict. */ readonly findings: readonly ICacheInsight[]; } /** * Aggregate per-pair outcomes into a session-level report: which break * categories recur (a one-off break is a curiosity; a recurring one is a * bug), how many tokens each cost, and the token-weighted overall hit rate * across all turns (`turnTokens` covers every turn, including the first, * which has no pair). */ export declare function buildSessionCacheReport(pairs: readonly ISessionPairOutcome[], turnTokens?: readonly ISessionTurnTokens[]): ISessionCacheReport;