Skip to main content

CacheManager

Struct CacheManager 

Source
pub struct CacheManager { /* private fields */ }
Expand description

SHA-256 content-hash deduplication cache backed by SessionStore, with delta encoding for near-duplicate content and compaction awareness.

§Freshness model

A dedup ref is considered fresh (safe to serve instead of the full content) when the cache entry’s accessed_at timestamp in SQLite is within max_ref_age of now. When sqz is invoked from shell hooks each invocation is a short-lived process, so the freshness check must be persistent — in-memory state is gone the moment the process exits.

The previous turn-counter heuristic was in-memory only and therefore never registered freshness across hook invocations, which silently disabled the dedup feature in production. Issue found April 18 2026.

Default TTL: 30 minutes. Empirically matches a typical active coding session before a context compaction. Use [with_ref_age] to tune.

Implementations§

Source§

impl CacheManager

Source

pub fn new(store: SessionStore, max_size_bytes: u64) -> Self

Create a new cache manager backed by the given session store.

max_size_bytes controls when LRU eviction kicks in. A good default is 512 MB (512 * 1024 * 1024). Dedup refs go stale after 30 minutes of wall-clock time by default — use [with_ref_age] to tune.

Source

pub fn with_ref_age_duration( store: SessionStore, max_size_bytes: u64, max_ref_age: Duration, ) -> Self

Create a CacheManager with an explicit wall-clock ref-age cap.

Source

pub fn advance_turn(&self)

Advance the turn counter. Retained for API compatibility; not used for freshness. The context_evictor still reads current_turn for LRU scoring during sqz compact.

Source

pub fn current_turn(&self) -> u64

Get the current turn number. Used by the context_evictor for scoring.

Source

pub fn notify_compaction(&self)

Notify the cache that a context compaction has occurred.

Persists a compaction timestamp into the session store so any cache entry whose accessed_at predates the marker is considered stale by every subsequent sqz process, not just this one. The shell- hook invocation model means this method is typically called from a short-lived sqz hook precompact process, and the check runs in a different sqz compress process milliseconds later.

Call this when:

  • The harness signals a compaction event (PreCompact hook)
  • A session is resumed after being idle
  • The user runs sqz compact
Source

pub fn get_or_compress( &self, _path: &Path, content: &[u8], pipeline: &CompressionPipeline, ) -> Result<CacheResult>

Look up content in the cache with compaction awareness.

  • On exact dedup with fresh ref: return CacheResult::Dedup (~13 tokens).
  • On exact dedup with stale ref: re-compress and return CacheResult::Fresh (the original content may have been compacted out of the LLM’s context).
  • On near-duplicate: return CacheResult::Delta with a compact diff.
  • On cache miss: compress via pipeline, persist, return CacheResult::Fresh.
Source

pub fn check_dedup(&self, content: &[u8]) -> Result<Option<String>>

Check if content is already in the persistent cache (dedup lookup only).

Returns Some(inline_ref) if cached AND the ref is still fresh, None if the content is not cached or the ref is stale.

Unlike [get_or_compress], this method does not touch accessed_at until after the freshness check — otherwise every read would make itself “fresh.”

Source

pub fn store_compressed( &self, original_content: &[u8], compressed: &CompressedContent, ) -> Result<()>

Store a compressed result in the persistent cache, keyed by the SHA-256 hash of the original content.

Also records the ref as sent at the current turn for compaction tracking. Persists original_content alongside compressed so that sqz expand <prefix> can recover the raw bytes for agents that cannot parse §ref:…§ dedup tokens.

Source

pub fn expand_prefix(&self, prefix: &str) -> Result<Option<ExpandResult>>

Resolve a hex prefix (the 16-char tail of a §ref:<prefix>§ token, or any longer hex string pasted by a user) to the cached content.

Designed for the sqz expand <prefix> CLI: the agent sees a ref token it can’t parse, runs sqz expand a1b2c3d4e5f6g7h8, and gets back the raw bytes that produced the ref. This is the user-visible escape hatch SquireNed asked for.

Three outcomes:

  • Ok(Some(Original)) — the original bytes were captured when the entry was stored (new cache entries from v0.10.0+ always capture the original). Write bytes to stdout.
  • Ok(Some(CompressedOnly)) — the entry exists but its original column is NULL (pre-migration data). We still return the compressed form — always legible, always useful — and the CLI surfaces a note that tells the user to re-run their original command with --no-cache to capture a truly uncompressed copy.
  • Ok(None) — no entry matches. Usually means the ref was truncated from a different sqz database (a different machine, a wiped ~/.sqz/sessions.db, etc.).
  • Err(_) — prefix was ambiguous or the DB is broken. The error carries a user-readable message explaining what went wrong.

Touches accessed_at on hit (consistent with get_cache_entry).

Source

pub fn invalidate(&self, path: &Path) -> Result<()>

Invalidate the cache entry for path if its current content is known.

Reads the file at path, computes its hash, and removes the matching entry from the store. If the file does not exist the call is a no-op.

Source

pub fn evict_lru(&self) -> Result<u64>

Evict least-recently-used entries until total cache size is at or below max_size_bytes.

Returns the number of bytes freed.

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.