How it works
Most memory products store everything on someone else's servers, treat every fact the same way, and quietly forget the important things. Sibyl fixes all three by organizing memory into five tiers, each recalled the way it should be.
The five tiers
Memory is not one bucket. Each tier has a clear intent and its own part of the API.
| Intent | Tier | API |
|---|---|---|
| What you're working on right now | HOT state | set_state(key, body) / get_state(key) |
| Things the agent knows about | WARM entities | set_entity(kind, name, body) / get_entity |
| What happened, in time order | COLD journal | write_event(...) / read_events(...) |
| Documents you look up by name | REFERENCE | set_reference(key, body) / get_reference |
| Frozen things, kept out of the way | ARCHIVE | archive_entity(kind, name) |
| Search across everything | FTS5 | search_entities(query) |
The SDK in five lines
The same engine that powers the plugin. Everything is local; nothing round-trips to a server.
from sibyl_memory_client import MemoryClient
memory = MemoryClient.local("~/.sibyl-memory/memory.db")
# Remember a fact (entity)
memory.set_entity("project", "atlas", {"status": "active", "owner": "jane"})
# Recall it
print(memory.get_entity("project", "atlas"))
# Record what happened (journal)
memory.write_event(acted=["deployed atlas v1.2 to staging"])
# Search across everything
results = memory.search_entities("atlas")
Single source of truth, enforced
Each entity is unique per (category, name) inside a tenant. That is not a convention the
application politely follows; it is a UNIQUE (tenant_id, category, name) constraint at
the schema level. Two rows describing the same thing cannot exist. Drift is impossible by construction,
which is a large part of why retrieval stays clean as the store grows.
Search without embeddings
Retrieval is SQLite FTS5 full-text search across the entity, state, reference, and journal tiers. No vector index, no embedding model to host, no embedding fees, no similarity drift. The answer your agent needs is found by structure and text, and the whole thing runs on a single box.
In independent beta testing on a 191k-record corpus, retrieval stayed at 100% while reading roughly two rows and ~228 tokens per query, against engines reading 50× the context to answer worse. See Benchmarks.
Forgetting vs deleting
archive_entity(kind, name) is recoverable: it moves the entity into the archive table,
out of the active set but still on disk. delete_entity(kind, name) is a permanent hard
delete that removes the row outright. Use archive to declutter, delete to truly forget.
Multi-tenant by design
One machine can hold separate, fully isolated memory for separate identities. Every read and write is
scoped by tenant_id; one identity never sees another's rows. The same property is what lets
Sibyl Labs host the schema as a managed service without the tiers leaking across customers.