Memories don't all live forever. Working notes expire in hours. Project context survives a week. Durable knowledge stays until you say otherwise. ai-memory's Short / Mid / Long tier system mirrors human working / episodic / long-term memory and lets agents forget noise without losing what matters.
Every memory carries a tier. The default is Mid — the sweet spot for most session-spanning facts. Short is for ephemeral working memory that should age out fast. Long is for durable knowledge that should never auto-expire. Choose explicitly on write, or let the default carry you.
Each tier has its own decay slope. Short bleeds out in hours. Mid in days. Long holds. Access bumps the expiry forward by a tier-specific extend interval, so memories that matter get used and earn more time.
The slopes are illustrative — the actual retention curve is binary in v0.6.3 (memory either exists past TTL or has been GC'd / archived). v0.7+ may add probabilistic decay if telemetry shows operators want it.
The Short / Mid / Long division isn't arbitrary — it borrows the cognitive-psychology distinction between working memory (seconds-minutes), episodic memory (hours-days, recent context), and long-term memory (durable knowledge that survives consolidation). AI agents that reason like humans benefit from a memory system that ages like one.
A memory's tier can change. The most important transition is Mid → Long — the moment something stops being session context and starts being durable knowledge. ai-memory routes promotion through governance, so the right caller (or the right approver) decides what lives forever.
memory_promote with the target id. The daemon resolves the memory's namespace.policy.promote from the namespace standard. Default Any. Other levels: Registered, Owner, Approve.Allow → promote. Deny(reason) → reject with the reason. Pending(id) → queue for approval, return 202.long. expires_at cleared. Federation fanout via quorum write.Tier-downgrade refused at SQL. The schema's tier_rank() function and a GREATEST(tier_rank(...)) precedence rule (Postgres + SQLite) refuse Long → * and Mid → Short at the storage layer. The HTTP / MCP path can't accidentally regress a memory's durability through a malformed update.
Tier defaults cover most cases. When they don't, every dimension is a knob you can override per memory or per namespace.
When a memory's expires_at passes, GC sweeps it on the next cycle. Two paths: hard delete, or archive-on-GC (configurable). Archived memories are restorable; deleted ones are not. Default is archive — recoverable by design.
memories WHERE expires_at < now(). Default cadence configurable; can also be invoked on-demand via the MCP memory_gc tool.archived_memories with full metadata. Restorable via memory_archive_restore. Purge-able via memory_archive_purge when you really want it gone.memories. FTS index updated. HNSW vector evicted. Federation fanout deletes peer copies.expires_at means GC's WHERE clause never matches. Long memories survive every sweep until something else (delete, forget, archive) touches them.Forget vs. delete. memory_forget is a pattern-based bulk operation — same effect as GC's archive path but on demand. memory_delete is per-id and goes through governance. Either way the federation fanout keeps peers consistent.
pending_actions if the policy required approval, or a stamped updated_at on the row.