Long-term user memory: facts extracted from conversations, stored in SQLite, injected into every session.
navi/memory/store.py)Three tables in navi.db:
| Table | Purpose |
|---|---|
memory_facts |
Individual facts: (category, key, value) — unique on (category, key) |
memory_summary |
Single-row narrative summary generated from all facts |
session_memory_state |
Tracks which sessions have been processed (by extracted_at) |
MemoryStore is initialized synchronously (creates tables), all operations are async via aiosqlite.
| Method | Description |
|---|---|
upsert_fact(category, key, value) |
Insert or update a fact |
search_facts(query, limit=15) |
Full-text search across category/key/value (OR across terms) |
delete_fact(key, category=None) |
Delete by key, optionally filtered by category |
get_all_facts(limit=None) |
All facts ordered by (category, updated_at DESC) |
get_summary() |
Current narrative summary text |
set_summary(content) |
Replace the summary |
mark_session_extracted(session_id) |
Record extraction timestamp |
get_extracted_at(session_id) |
Check if/when a session was processed |
navi/memory/extractor.py)Facts are extracted from stale sessions automatically.
Trigger: POST /sessions (create new session) fires _process_stale_sessions() as a background task.
Stale criterion: session.last_active < now - 30 minutes AND not yet extracted (or extracted before last activity).
Extraction process:
category: key = value lines.memory_facts.memory_summary from all current facts.At the start of each run_stream() / run() / run_ephemeral() call, _memory_msg() is called:
async def _memory_msg(self) -> Message | None:
summary = await self._memory.get_summary()
if not summary:
return None
return Message(role="system", content=f"## What I remember about the user\n\n{summary}")
This message is inserted after the main system message but before conversation history. The agent reads it on every turn.
memory_search — searches facts by keyword query. Returns matching facts with category/key/value. Agent should call this when the user mentions something personal that may already be known.
memory_forget — deletes facts matching a key (optionally filtered by category). Agent calls this when the user explicitly asks to forget something or when a fact is clearly outdated.
Call memory_search when:
Do NOT call memory_search reflexively at the start of every session — only when context warrants it.
Call memory_forget only when the user explicitly asks, or when a stored fact is clearly wrong or outdated.