Skip to content

Memory tiers

Engram models memory the way agents actually use it: most facts are only relevant for minutes or hours, a few deserve to persist. Two tiers encode that:

  • STM (short-term) — cheap to write, expires on its own, no embedding required. The default landing zone for observations.
  • LTM (long-term) — durable, embedded, semantically searchable, subject to lifecycle jobs (decay, dedup, consolidation).

The split is behavioral, not structural — both tiers share the same Memory model. What differs is where rows live, how long they live, and which jobs touch them.

PostgresStmAdapter (packages/memory-stm) stores short-term memories as rows in the shared memories table — type: 'short-term' with an expiresAt timestamp — injected through the STM_PROVIDER token.

Every STM write carries a TTL — default 86 400 s (24 h), clamped to [60 s, 7 d] — recorded as expiresAt. Nothing an agent does has to garbage-collect stale session facts; they evaporate. This is why transient state belongs in STM: forgetting is free.

Two details matter for the rest of the system:

  • Access counting. Every STM read increments the memory’s accessCount. That counter is the promotion signal: a “transient” fact an agent keeps re-reading is evidently not transient. See Consolidation & decay for the promotion pass (STM_CONSOLIDATION_ACCESS_THRESHOLD, default 3).
  • One table, swept clean. STM rows live in the same memories table as LTM (one model, two lifecycles), so promotion is a field update, not a cross-store copy. Expiry is the only STM-specific mechanic: reads exclude rows past expiresAt, and StmSweepService (STM_SWEEP_INTERVAL_MS) deletes them in the background.

MemoryLtmService (packages/memory-ltm) persists long-term memories to Postgres — the system’s source of truth — and runs the write-time ingest pipeline: exact dedup, semantic dedup, contradiction detection, and importance scoring happen before the row lands, and embedding/indexing follow asynchronously. LTM rows are the only rows that:

  • get embeddings and participate in semantic recall,
  • are subject to decay, pruning, and corpus consolidation,
  • carry the audit trail and version CAS guarantees.

Both profiles persist LTM to the same Postgres store; the profile only changes whether the multi-tenant auth stack sits in front of it.

There are three ways a memory moves or lands between tiers:

  1. Explicitcreate_memory with type: 'long-term', or promote_memory on an existing STM row.
  2. Heuristicremember with type: 'auto' (the default posture for agents) routes to a tier from content signals and TTL hints.
  3. Automatic — the consolidation pass promotes STM rows whose accessCount and importance clear the thresholds.

Promotion preserves the memory’s id and content; the row simply becomes type: 'long-term', loses expiresAt, and enters the LTM lifecycle (embedding, recall, decay).

A single durable tier forces a bad choice on every write: either everything persists (and recall drowns in expired session noise, while the corpus grows unboundedly) or the agent must decide durability perfectly at write time (it cannot). The two-tier design lets writes default to cheap-and-forgettable, and uses observed access behavior — not write-time guesses — to earn durability. Decay then applies the same principle in reverse to LTM: memories that stop being accessed lose importance and are eventually marked stale or pruned. Between promotion and decay, the corpus tracks what is actually useful.