/**
 * Two-phase prefix-cache-break detector (design/31). Each turn it records a structural fingerprint of
 * the prefix (system prompt / per-tool schema / tool set / model namespace) and compares the turn's
 * `cacheRead` against the prior warm turn; on a confirmed drop it attributes the break to a root cause
 * (model switch / a specific tool's drift / tool-set change / system-prefix change / server-or-TTL).
 *
 * The strict superset of the task-end "low hit-rate" heuristic: it pinpoints the BREAK TURN and names
 * the cause, instead of only reporting an aggregate at the end. Provider-agnostic — the only provider
 * input is `cacheRead` (OpenAI `cached_tokens` / Anthropic `cache_read_input_tokens`, already normalized).
 *
 * v1 is task-scoped (compares turns within one run; system/tools are stable so the high-value per-tool
 * attribution mostly fires on a model switch or across runs). A per-session v2 would feed a persisted
 * prior snapshot — the `observe` contract is already shaped for that.
 */
export interface CacheBreakFinding {
    turn: number;
    cacheReadBefore: number;
    cacheReadAfter: number;
    cause: "model-switch" | "tool-schema" | "tool-set" | "system-prefix" | "server-or-ttl";
    detail: string;
}
export interface ToolFingerprintInput {
    name: string;
    description: string;
    parameters: unknown;
}
/**
 * Project a tool list into the structural inputs the detector fingerprints. Shared by the initial
 * snapshot in `prepareTask` AND the per-materialization refresh in tool-disclosure (design/36): both
 * MUST derive the fingerprint the same way, or a deferred tool going placeholder→full schema would
 * be hashed inconsistently. Keep this the single source of "what counts as the cacheable tool prefix".
 */
export declare function toolsToFingerprintInputs(tools: ReadonlyArray<{
    name: string;
    description: string;
    parameters: unknown;
}>): ToolFingerprintInput[];
export declare class CacheBreakDetector {
    private prev;
    /**
     * Reset the baseline. **Call ONLY after a SUCCESSFUL compaction** (design/31 red line #11): a legit
     * prefix shrink isn't a break, but resetting when compaction did NOT happen opens a false-negative
     * window (a real break right after would be missed).
     */
    notifyCompaction(): void;
    /**
     * Record this turn's fingerprint + compare cacheRead to the prior warm turn. Returns a finding on a
     * confirmed break. Hashes are computed here per call **by design**, taking the raw prefix rather than
     * pre-computed hashes. NOTE (design/36): the inputs are NOT necessarily stable within a run — deferred
     * tools materialize placeholder→full schema mid-task, so the caller refreshes the fingerprint at
     * materialization (via `toolsToFingerprintInputs`). A resulting `tool-schema`/`tool-set` finding on
     * that turn is EXPECTED and truthful (the cacheable prefix genuinely changed); the bug this guards
     * against is the opposite — a frozen fingerprint hiding that change (a false negative).
     */
    observe(input: {
        turn: number;
        systemPrompt: string;
        tools: ToolFingerprintInput[];
        modelKey: string;
        cacheRead: number;
    }): CacheBreakFinding | undefined;
}
