/** * cacheRecorder() — observability for the v2.6 cache layer. * * Subscribes to: * - `FlowRecorder.onDecision` — captures CacheGate routing decisions * (apply-markers / no-markers + the rule that fired + evidence * from `decide()`). Read directly from `event.evidence.rules[matched]` * since footprintjs already auto-captures predicate `inputs[]`. * - `agentfootprint.stream.llm_end` events — read provider's `usage` * and call the agent's CacheStrategy.extractMetrics() to normalize * into CacheMetrics (cacheReadTokens / cacheWriteTokens / fresh). * * Produces: * - per-iteration `agentfootprint.cache.applied` events (markers * applied this iter or empty if skipped) — for Lens trace * - per-iteration `agentfootprint.cache.metrics` events (hit/write * token counts + estimated dollars via PricingTable) — for * dashboards * - a turn-end summary printable via `recorder.report()` — * numeric tally plus dollars saved * * v2.6 LIMITATION: doesn't yet write `scope.recentHitRate` back into * agent state. CacheGate's hit-rate-floor rule won't fire automatically; * consumers can manually wire feedback via `Agent.create(...).attach(rec)`. * Full feedback loop deferred to v2.7 (needs an agent-side accessor * convention since recorders don't normally write to scope). */ import type { CombinedRecorder } from 'footprintjs'; import type { CacheMetrics, CacheStrategy } from './types.js'; import type { PricingTable } from '../adapters/types.js'; interface PerIterEntry { readonly iteration: number; readonly branch: 'apply-markers' | 'no-markers'; readonly rule?: string; readonly metrics?: CacheMetrics; readonly dollarsSpent: number; readonly dollarsSavedVsNoCache: number; } interface CacheReportSummary { readonly totalIterations: number; readonly applyMarkersIterations: number; readonly noMarkersIterations: number; readonly cacheReadTokensTotal: number; readonly cacheWriteTokensTotal: number; readonly freshInputTokensTotal: number; readonly hitRate: number; readonly estimatedDollarsSpent: number; readonly estimatedDollarsSavedVsNoCache: number; readonly perIter: readonly PerIterEntry[]; } export interface CacheRecorderOptions { /** * The agent's CacheStrategy. Required for `extractMetrics` — * normalizes provider-specific `usage` shapes into CacheMetrics. * If not provided, recorder logs the raw usage and skips dollar math. */ readonly strategy?: CacheStrategy; /** * PricingTable for dollar estimates. Falls back to token-count-only * reporting when omitted. Looks up `'input'` / `'cacheRead'` / * `'cacheWrite'` token kinds (PricingTable already supports these * as of v2.5). */ readonly pricing?: PricingTable; /** * Model id for pricing lookup. Defaults to a placeholder; set to * the actual model the agent is using for accurate dollar math. */ readonly model?: string; } export interface CacheRecorderHandle extends CombinedRecorder { /** * Build a per-turn report. Call after `agent.run()` completes. * Returns a frozen snapshot — recorder keeps accumulating but the * report you held is stable. */ report(): CacheReportSummary; /** * Reset accumulated state. Call between turns if you want * per-turn rather than per-session reporting. */ reset(): void; } export declare function cacheRecorder(options?: CacheRecorderOptions): CacheRecorderHandle; export {};