Memory
Memory is the substrate agents use to remember. A memory is a typed, namespaced,
confidence-scored record (src/memory/types.ts:78-104). Records carry temporal validity, a
TTL, tiered bodies, and typed edges to other records — together they form a knowledge graph
with a semantic-search pipeline over the top.
This page describes the data model and the default memory.query ranking. The retrieval
enhancements (query router, hybrid keyword, reranker) all ship off — see the caution at
the end.
The 11 memory types
Section titled “The 11 memory types”memory_type is a closed enum of 11 (src/memory/types.ts:12-24):
| Type | Type | Type |
|---|---|---|
semantic |
episodic |
emotional |
reflective |
charter |
skill |
instruction |
resource |
decision |
policy |
protocol |
One legacy alias is normalized on input: procedural → skill (types.ts:28-30,34-46). Four of
these — semantic, episodic, emotional, reflective — are the “general” types
(isGeneralMemoryType, types.ts:52-55); they back the library /memories section. charter
is special: one per ring, admin-only to write (enforced in memory.ts:450-474).
Namespaces
Section titled “Namespaces”Every memory lives in exactly one namespace, and the namespace is the access boundary. There
are four kinds (src/memory/acl.ts:10-17):
| Namespace | Shape | Who can read |
|---|---|---|
| Private | agent:{identity_id} |
the owning identity only |
| Team | ring:{ring_id} |
members of that ring |
| Org | org:{org_id} |
active members of that org |
| Federated | federated:{source_org}:{source_claw} |
identities with an explicit share grant |
Unknown formats are denied by default. Human-readable aliases resolve by name —
agent:sophie, ring:core-team, org:my-org (acl.ts:19-24). All ACL checks delegate to
RingScope in src/identity/ring-scope.ts; the acl.ts exports are thin wrappers for
non-Hono callers (acl.ts:1-8).
The graph
Section titled “The graph”Memories connect with five typed edge kinds (src/memory/types.ts:198-204), each carrying a
strength in 0..1:
supports · contradicts · derives_from · supersedes · related_to
supersedes is load-bearing: memory.list and memory.recall follow these edges to the
supersession tip and drop the superseded rows (gap-1 §4). Build edges with
memory.relate, or let memory.remember classify them automatically (auto-edges are opt-in
per call; see gap-1 §2).
Confidence decays with age. Decay is query-time only and never written back
(src/memory/decay.ts:28-42):
decayed_confidence = confidence × e^(−ln2/halfLife × ageDays)Half-life is 30 days by default (MEMORY_DECAY_HALF_LIFE_DAYS, decay.ts:51-55). A
future-dated created_at returns the original confidence unchanged (decay.ts:37-38). The
decay floor (MEMORY_DECAY_FLOOR, default 0.05) is a GC/audit threshold — it is not a
query clamp (gap-1 §3). The garbage collector deletes memories that are TTL-expired
(expires_at < now) or decayed below the floor (gap-1 §4).
Each memory has three body tiers — L0, L1, L2 (src/memory/types.ts:75). A read picks
a tier from the request intent (gap-1 §5):
| Intent | Tier preference |
|---|---|
preview |
L0 → L1 → L2 |
default |
L1 → L2 → L0 |
full |
L2 → L1 → L0 |
L0/L1 are the tier_l0/tier_l1 summary columns; L2 is the full body (object storage
via body_key, else inline content).
How memory.query ranks (default)
Section titled “How memory.query ranks (default)”memory.query is the semantic-search pipeline (src/commands/handlers/memory.ts:614-818).
It embeds the query, over-fetches k = limit × 3 candidates by vector
(KNN_OVERFETCH_MULTIPLIER=3, acl.ts:34), post-filters by namespace/category/circle, scores,
sorts, and returns the top limit. The default score is:
score = min(1, cosine_similarity × decayed_confidence)(memory.ts:695-716, with the enhancement multipliers all equal to 1 by default.) Pass
include_trace=true for a per-match breakdown — but note the trace’s recency field is just
an alias of decay_factor, and coactivation is always null (gap-1 F9). Neither is an
independent signal.
memory.recall is the entity-keyed sibling: it fans out across every accessible namespace and
orders by created_at (newest first) — recency, not relevance, and no embedding
(src/db/surreal-adapter.ts:2183, ORDER BY created_at DESC). decayed_confidence is computed
per match and reported as the score field (src/commands/handlers/memory.ts:851-872), but it
does not drive the rank.