/**
 * CacheGate — runtime decider that gates cache-marker application.
 *
 * Runs every iteration AFTER the CacheDecision subflow produces
 * `scope.cacheMarkers` and BEFORE the BuildLLMRequest stage applies
 * them. Three rules can fall through to "no-markers" (skip caching);
 * default branch is "apply-markers" (proceed with caching).
 *
 * Why a decider stage and not a function: footprintjs's `decide()`
 * captures evidence on `FlowRecorder.onDecision` natively. The
 * `cacheRecorder()` (Phase 9) reads
 * `event.evidence.rules.find(r => r.matched).inputs[]` to surface
 * WHY caching was applied or skipped each iter. Same channel
 * footprintjs uses for every other decision; same renderer in Lens.
 *
 * Three rules (evaluated top-down; first match wins):
 *   1. Kill switch — `Agent.create({ caching: 'off' })` was set
 *   2. Hit-rate floor — recent hit rate < 30%; cache writes outpacing
 *      reads, auto-disable to avoid the cache-write penalty
 *   3. Skill churn — active skills changing too rapidly for caching
 *      to amortize (Anthropic LLM expert's concern from Phase 4 review)
 *
 * Default branch (no rule matches): `'apply-markers'`.
 */
import { type DecisionResult, type TypedScope } from 'footprintjs';
/**
 * Subset of agent scope state the CacheGate decider reads.
 * Phase 6 wires these keys into the agent's main chart.
 */
export interface CacheGateState {
    /**
     * Global kill switch. When `true`, decider skips caching
     * unconditionally. Set via `Agent.create({ caching: 'off' })`.
     */
    readonly cachingDisabled: boolean;
    /**
     * Running cache hit rate from the last N iterations (0..1).
     * `undefined` when no cache events have fired yet (e.g., iter 1 of
     * the first turn — no history). The cacheRecorder (Phase 9)
     * computes this from `CacheMetrics` events.
     */
    readonly recentHitRate: number | undefined;
    /**
     * Rolling window of active-skill IDs across recent iterations,
     * one per iteration (latest LAST). Maintained by the
     * UpdateSkillHistory function stage (Phase 6 mount).
     *
     * `undefined` entry = no skill active that iteration.
     */
    readonly skillHistory: readonly (string | undefined)[];
}
/**
 * Hit-rate floor below which we auto-disable caching. The 30% number
 * is calibrated for Anthropic's pricing: cache write costs +25%
 * premium, cache read costs 90% off. Break-even at ~25% hit rate.
 * 30% gives a buffer; below that we're losing money on writes that
 * never recoup.
 *
 * Reasoning: if hit rate is X, cost-per-token vs no caching is
 *   (1 - X) * 1.0 + X * 0.1                                    // baseline
 *   minus
 *   write_iters * 1.25 + read_iters * 0.1                       // with caching
 * Solving for break-even gives X ≈ 0.25 for typical agent shapes.
 */
export declare const HIT_RATE_FLOOR = 0.3;
/**
 * Window size for skill-churn detection. Last 5 iterations of
 * active skill IDs are inspected.
 */
export declare const SKILL_CHURN_WINDOW = 5;
/**
 * Threshold above which skill churn is considered detected: this many
 * UNIQUE skills in the rolling window. With window=5 and threshold=3,
 * the pattern A → B → A → C still triggers (3 unique skills in 4 iters).
 */
export declare const SKILL_CHURN_THRESHOLD = 3;
/**
 * Pure helper: detect skill churn given a rolling history.
 * Exported for direct testing without decider/scope ceremony.
 */
export declare function detectSkillChurn(history: readonly (string | undefined)[], windowSize?: number, threshold?: number): boolean;
/**
 * Branch routing keys for the CacheGate decider. Two outcomes:
 * apply markers (proceed with cache) or skip (no markers this iter).
 */
export type CacheGateBranch = 'apply-markers' | 'no-markers';
/**
 * The decider function. Mounted via `addDeciderFunction` in the
 * agent's main chart in Phase 6.
 *
 * Returns a `DecisionResult` (footprintjs's `decide()` helper output)
 * which the engine unwraps via `.branch` for routing AND publishes
 * `evidence.rules[matched].inputs[]` to FlowRecorder.onDecision.
 * cacheRecorder (Phase 9) subscribes to that channel for the audit trail.
 *
 * For non-routing consumers (testing the decision in isolation), read
 * the `.branch` field of the returned DecisionResult.
 */
export declare function cacheGateDecide(scope: TypedScope<CacheGateState>): DecisionResult;
/**
 * Update the skill-history rolling window. Called as a function
 * stage BEFORE the CacheGate decider. Reads the current iteration's
 * active skill (the MOST-RECENTLY activated one — the TAIL of
 * `activatedInjectionIds`) and appends it to the `skillHistory` array.
 *
 * WHY THE TAIL, NOT THE HEAD: `read_skill` APPENDS each newly-activated
 * skill to the end of `activatedInjectionIds`, and the list is cumulative
 * + deduped per turn (reset only at seed). So the HEAD is frozen at the
 * FIRST skill activated and never changes mid-turn — sampling it made the
 * window record one constant value, so `detectSkillChurn` could never fire
 * (the skill-churn cache rule was effectively dead). The tail tracks what
 * the agent just switched to, which is the churn signal we actually want.
 *
 * KNOWN LIMITATION: if several skills are activated in the SAME iteration,
 * only the last (tail) is recorded for that iteration — churn can
 * under-count a multi-skill burst. A fully order-independent signal would
 * read `activatedInjectionIds.length` directly in the gate; that is a
 * larger change deferred for now.
 *
 * Window length is bounded at `SKILL_CHURN_WINDOW * 2` so the array
 * doesn't grow unboundedly across long agent runs. Old entries
 * fall off the front naturally.
 */
export declare function updateSkillHistory(scope: TypedScope<{
    activatedInjectionIds?: readonly string[];
    skillHistory: readonly (string | undefined)[];
}>): void;
