/** * cache-first — Immutable prefix + append-only log for DeepSeek prefix-cache stability. * * Harvested from reasonix Pillar 1 (Cache-First Loop). * * DeepSeek's automatic prefix caching activates only when the exact byte prefix * of the previous request matches. Most agent loops reorder, rewrite, or inject * fresh timestamps each turn — cache hit rate in practice: <20%. * * The prefix that matters to DeepSeek is the *serialized request head*: * the system message followed by the tool definitions (payload.tools). * Conversation content appends after that stable head. If the head is * byte-identical across turns, the disk cache hits on every repeat. * * This module tracks that head and ensures messages are serialized in * append-only order so the prefix stays byte-stable across turns. */ import type { DeepSeekChatMessage } from "./types.js"; /** * Compute a stable hash for a value using a fast non-crypto algorithm. * DeepSeek's cache is byte-prefix based, so we just need a deterministic * fingerprint to detect changes. */ export declare function fastHash(value: string): string; /** * Detect whether a provider URL targets DeepSeek. */ export declare function isDeepSeekProvider(baseUrl: string): boolean; /** * Immutable prefix tracker. * * Tracks the prefix derived from the system prompt + tool definitions. * These are what determine DeepSeek's prefix-cache matching — the rest of * the conversation history just appends after the stable prefix. * * IMPORTANT: the tools hash is computed from the *tool definitions* * (the OpenAI `tools` array in the request payload), NOT from assistant * tool_calls in the message history. Tool *calls* grow every turn; hashing * them made the "prefix stable" indicator permanently red even when the * actual cache head was byte-identical (observed: 98.2% hit ratio with * `Prefix stable: ❌`). Tool *definitions* are what the cache head contains. */ export declare class PrefixGuard { private _systemHash; private _toolsHash; private _prevPrefixHash; private _prefixHash; private _stabiliseCount; /** Stabilise messages array: system first, stable prefix hash. */ stabilise(messages: DeepSeekChatMessage[], tools?: unknown[]): { messages: DeepSeekChatMessage[]; prefixHash: string; }; /** Current prefix hash for cache-diagnostics headers. */ get prefixHash(): string; /** * True when the prefix hash is stable across at least 2 successive calls. * First call always returns false (no baseline for comparison). * Seed calls after reset return false until a second comparison. */ isStable(): boolean; /** Whether the guard has been initialized (computed at least once). */ isInitialized(): boolean; /** Times stabilise() has been called since last reset. */ get callCount(): number; /** Reset (new session or context cleared). */ reset(): void; } /** * Append-only log tracker. * * Ensures that conversation history is only ever appended, never mutated or * reordered. This preserves the prefix for subsequent turns. */ export declare class AppendOnlyLog { private _entryCount; /** * Validate that the messages log has only grown (no deletions / reorders). * * Returns true when the entry count is non-decreasing. When a truncation * is detected the baseline resets to the new (smaller) count so the next * growth is validated correctly — otherwise the check would return false * forever after the first compaction (observed: `conversationTruncations` * inflating on every subsequent turn). */ validate(entries: DeepSeekChatMessage[]): boolean; reset(): void; }