/** * LRU+TTL cache for pre-call tool routing decisions, plus session stickiness * tracking to reduce turn-to-turn server flapping. * * The cache is keyed by a caller-supplied string (typically a normalized hash * of sessionId + routing query). On a hit the cached exclusion list is * returned, skipping the router LLM entirely. On a miss the caller resolves * normally, stores the result, and `recordSelection` is called with the * selected server ids so stickiness can suppress their exclusion for the next * N turns. * * All operations are synchronous and pure (no I/O). The clock is injected via * `now` so tests can control time without `Date.now` side effects. */ import type { ToolRoutingCacheOptions } from "../types/index.js"; export declare class ToolRoutingCache { private readonly ttlMs; private readonly maxEntries; private readonly stickyTurns; private readonly now; /** Main LRU+TTL store keyed by routing key. */ private readonly store; /** Per-session stickiness tracking keyed by session id. */ private readonly sticky; /** Monotonic counter for LRU ordering — incremented on each access. */ private accessCounter; constructor(opts?: ToolRoutingCacheOptions); /** * Returns the cached routing result for the given key, or `undefined` on a * miss or expiry. Expired entries are deleted on access (lazy eviction). */ get(key: string): { excludedToolNames: string[]; selectedServerIds: string[]; } | undefined; /** * Stores a routing result under the given key. Evicts the least-recently- * used entry when the store is at capacity. Silently no-ops on any error * (caller is responsible for fail-open behaviour). */ set(key: string, value: { excludedToolNames: string[]; selectedServerIds: string[]; }): void; /** * Records the server ids selected by the router for a session so they stay * warm for the next `stickyTurns` turns. Called after a successful * (non-cached) routing resolution. */ recordSelection(sessionId: string, serverIds: string[]): void; /** * Returns the server ids that should be kept warm (not excluded) for the * given session due to stickiness. Decrements the turn counter; when it * reaches zero the entry is removed. Returns an empty array when there is no * active sticky state for the session. */ getStickyServerIds(sessionId: string): string[]; /** * Scans the store for the entry with the lowest accessOrder and removes it. * O(n) scan is acceptable at the default maxEntries (256); very large caches * should consider an O(1) doubly-linked-list approach instead. */ private evictLRU; }