Why monotonic?
Most memory systems update facts in place: “User works at Google” becomes “User works at Anthropic.” The old fact is gone. But in doing so, you lose the history: when the user changed jobs, what they said about it, what the context was. People change. They switch jobs, move cities, change preferences, update opinions. A memory system that overwrites facts can only tell you the current state. A monotonic graph tells you the full story.- “Where did the user work before Anthropic?”
- “When did the user move to Austin?”
- “What did the user think about VS Code before switching?”
valid_from timestamp. Recent relationships get a boost, older ones decay over time but never reach zero. So “User works at Anthropic” (May 2025) outranks “User works at Google” (March 2025) in a query about the user’s current job, but both remain discoverable when the question is about the past.
Temporal metadata
Every edge carries two timestamps:
This distinction matters. A user might say “I started my new job in May” on June 3rd.
valid_from is May 1st. recorded_at is June 3rd. Temporal queries use valid_from. Recency ranking uses recorded_at. They serve different purposes.
No physical deletion
Memories are never hard-deleted. The “forget” operation setsforgotten_at on the memory and its connected edges. This hides them from retrieval while preserving the complete audit trail.
Connected facts, not isolated text
Flat text memories can’t reason about structure. You can search them with vectors or keywords, but you can’t follow a chain of relationships. You don’t know that “Mercury” in one conversation is the same person in another. You can’t traverse: “User works at Stripe” → “Stripe uses PostgreSQL” → “PostgreSQL has extension pgvector” → “User might know about vector search.” Every piece of knowledge is stored as a relationship between two entities, with confidence scores and temporal grounding. The entity graph is shared across all memories in a space. Entities are unique nodes that multiple memories can reference.Three seeds for graph retrieval
To search the graph, you first need to find the right entry points: the entities most likely connected to relevant memories. Crosmos uses three independent strategies, then fuses them together. Each catches something the others miss.Why all three
A single strategy has blind spots. Memory seeds miss semantically distant but structurally connected entities. Entity embedding seeds miss differently phrased names. Entity name seeds miss entities with no text overlap. Running all three and fusing them maximizes coverage. Entities found by multiple seeds get a combined boost. Consider a user asking “What editor does the user use?”- Memory seeds find memories about editors (weak match)
- Entity name seeds find “Neovim” and “VS Code” via keyword match
- Entity embedding seeds find “code editor” concept entities
- The graph traverses from those entities through
USESandPREFERSedges - The memory “User uses Neovim as primary code editor after switching from VS Code” surfaces, even though the query never said “Neovim”
Full retrieval pipeline
The graph signal is one of four retrieval signals that run in parallel:- Semantic search: Searching for similar context embeddings
- Keyword search: full-text matching with relevance scoring
- Graph traversal: Traversal through the knowledge graph for mulit-hop reasoning
- Temporal search: activated when a query contains time references, ranks memories by proximity to the extracted date window
No single signal dominates. Each covers the others’ blind spots: memories are found by meaning, by keywords, and by graph structure, then ranked by relevance, freshness, and precision.