Memory
User and agent memory, daily logs, the entity graph, semantic search, and the six memory tools.
Sophon's memory system is what keeps the agent coherent across conversations. It stores facts, preferences, and daily activity; builds an entity graph of the people, projects, and places it knows about; and injects relevant context into every LLM call. The agent can write, read, search, forget, and explore memory as a graph during any chat.
What gets remembered
There are two persistent memory surfaces:
Long-term memory — individual entries the agent (or you) chose to keep. Each entry has content, a category (fact, preference, or observation), a scope (user or agent), and is linked into an entity graph. Examples:
- fact — "Project Atlas ships on March 15"
- preference — "User prefers dark mode"
- observation — "User opens GitHub first thing most mornings"
Short-term memory (daily logs) — timestamped summary of what happened each day. One row per interaction; the agent reads the last two days on every turn.
[2026-04-22 10:30] Discussed Atlas deadline — confirmed Mar 15
[2026-04-22 14:15] Uploaded Q1-competitors.pdf; summarized to team
[2026-04-22 16:00] Sent email summary (approved)Daily logs capture context; long-term entries capture the durable conclusions you want to survive beyond the window.
Scopes — user vs agent
Every long-term entry has a scope:
| Scope | Visible to | Typical content |
|---|---|---|
user | Every agent you own | Cross-cutting facts — where you live, who your team is, your project list |
agent | Only the writing agent | Agent-specific preferences — "Ada always uses short bullets," "Nora speaks formal German" |
When the context assembler builds the system prompt, it fetches both: all user-scoped entries + the active agent's agent-scoped entries. So if you tell the writer agent to use formal tone, the research agent is unaffected.
The entity graph
Every memory entry is automatically linked to entities (people, projects, places, dates) that appear in the content. This turns your memories into a graph you can explore.
Two extraction paths:
- LLM link extraction — when you write a memory, a fast-tier LLM scans it for named entities and wires them in.
- Wikilinks — you can write links explicitly with
[[Entity Name]]syntax and Sophon will parse them:
User is planning a trip to [[Tirana]] with [[Ermal]] for the [[Atlas launch]]That memory links to three entities — Tirana, Ermal, Atlas launch — and any future query that mentions any of those surfaces this entry too. Wikilinks also support an alias form, [[Target|Display]]: the part before the | is the canonical entity that gets linked, the part after is display text only. So [[Sarah Chen|Sarah]] reads as "Sarah" in the entry but links the entity Sarah Chen — the display text is presentation-only and is never recorded as an alias on the entity.
The Dashboard Memory page renders the graph so you can see what knows what. For the full graph story — the explorer, Mermaid export, duplicate detection and merge — see Memory Graph.
The six memory tools
The agent has six tools for working with memory:
memory.write
Saves a new entry. The agent calls this when it learns something worth keeping, or when you say "remember that…".
| Parameter | Required | Description |
|---|---|---|
content | yes | The text to remember |
category | no | fact, preference, or observation |
scope | no | user (default) or agent |
links | no | Explicit entity links to wire in addition to LLM extraction |
supersedes | no | ID (mem_...) of an older entry this one replaces — see Temporal facts |
Risk: Medium — triggers auto-extraction of entities and a write. Not gated unless part of a plan.
memory.search
Semantic + keyword hybrid search. Returns the top matches with relevance scores.
| Parameter | Required | Description |
|---|---|---|
query | yes | Natural-language query |
scope | no | Limit to user, agent, or all |
limit | no | Max results (default 10) |
Risk: None — read-only.
memory.list
Paginated listing of entries (no search). Useful for "show me everything you remember about me."
| Parameter | Required | Description |
|---|---|---|
scope | no | user, agent, or all |
limit | no | Default 50 |
cursor | no | For pagination |
Risk: None.
memory.forget
Deletes an entry by ID. Irreversible — requires user approval.
| Parameter | Required | Description |
|---|---|---|
entryId | yes | The ID from memory.list or memory.search |
Risk: Medium — triggers approval gate. "Forget everything" is a separate, Critical-risk flow handled in the Dashboard, not via this tool.
memory.graph
Unified graph inspector — one tool, four operations over the entity graph.
| Parameter | Required | Description |
|---|---|---|
operation | yes | overview (whole-graph stats + top-connected entities), neighbors (direct connections of one node), path (shortest path between two nodes), or cluster (BFS neighborhood around a seed) |
node | for neighbors | Entity or entry name/ID |
from / to | for path | Start and end name/ID |
seed | for cluster | Name or ID to expand from |
depth | no | Cluster BFS depth (default 2, max 4) |
kinds | no | Entity-kind filter (overview / cluster) |
Risk: None — read-only.
memory.diagram
Renders the graph (or a cluster around a seed) as a Mermaid flowchart. Chat renders the fenced diagram inline, theme-aware — use it when you ask the agent to "show" or "draw" the graph.
| Parameter | Required | Description |
|---|---|---|
scope | no | overview (default) or cluster |
seed | when scope=cluster | Entity name or ID |
depth | no | Cluster depth (default 2, max 4) |
direction | no | Diagram direction: LR or TD |
Risk: None — read-only.
memory.traverse is deprecated as of v1.15.0. It still works — it delegates to memory.graph with operation=cluster — but new integrations should call memory.graph directly.
Temporal facts
Facts change. Instead of deleting the old entry and writing a fresh one, pass supersedes to memory.write (or POST /api/memory/entries):
{
"content": "Project Atlas now ships on April 2",
"category": "fact",
"supersedes": "mem_abc123"
}The new entry replaces the old one. Superseded entries drop out of default views and search (their vector is deleted), but they are never destroyed — pass includeSuperseded=true to inspect them. The Dashboard shows a per-entry history, so you can see how a fact evolved over time.
Consolidation and retention
Daily logs aren't meant to pile up forever. MemoryConsolidationJob — a Quartz cron job, daily at 03:00 UTC by default — reviews recent daily logs and promotes the durable facts into long-term memory entries through the same write pipeline memory.write uses. Immediately after each sweep, it applies retention cleanup: daily logs older than RetentionDays (default 30) are removed.
| Config key | Default | Description |
|---|---|---|
Sophon:Memory:Consolidation:Enabled | true | Master switch for the consolidation job |
Sophon:Memory:Consolidation:Schedule | 0 0 3 * * ? | Quartz cron for the sweep (daily 03:00 UTC) |
Sophon:Memory:Consolidation:LookbackDays | 2 | Days of daily logs considered per run |
Sophon:Memory:Consolidation:MinLogsToConsolidate | 5 | Minimum log count before a run consolidates |
Sophon:Memory:Consolidation:MaxFactsPerRun | 20 | Cap on facts promoted per run |
Sophon:Memory:RetentionDays | 30 | Daily-log retention window |
Context assembly — what the LLM actually sees
On every turn, the context assembler builds the system prompt:
You are Sophon, an intelligent AI personal assistant.
## Shared Memory (user scope)
- User lives in Tirana, Albania
- Project "Atlas" deadline is March 15
- Prefers TypeScript over JavaScript
## Agent Memory (this agent)
- Ada uses concise responses, no emoji
- Greet user as "Enes"
## Recent Activity (last 2 days)
[2026-04-21 09:15] Reviewed PR #423, merged
[2026-04-22 10:30] Discussed Atlas deadline
[2026-04-22 14:15] Uploaded competitors.pdf, summarizedBudgets:
- Up to 50 user-scoped entries
- Up to 50 agent-scoped entries
- Last 2 days of daily logs
- Last 100 chat messages
These caps keep the prompt from ballooning. If memory grows past them, the most relevant entries float to the top — long-term entries are scored against the current message and ranked.
Dashboard — the Memory module
The Dashboard has a Memory page at /memory with four tabs:
- Entries — paginated list of long-term entries. Filter by scope, category, agent. Add, edit, or delete.
- Logs — daily activity log. Filter by agent and date range.
- Search — hybrid search UI. Results show relevance scores.
- Graph — the graph explorer v2 at
/memory/graph: force-directed layout, kind-chip filters, date window, node-cap presets, a co-mention overlay, and Mermaid export. A Duplicates review sheet surfaces likely duplicate entities for merge or dismiss. Full tour: Memory Graph.
Every entry has an ID you can reference in memory.forget or memory.graph.
CLI
sophon memory search "project deadline"
sophon memory list --scope user --limit 20
sophon memory list --agent writer
sophon memory forget mem_abc123 # prompts for confirmationStorage by tier
| Tier | DB | Vector store | Semantic search |
|---|---|---|---|
| Personal | SQLite (~/.sophon/db/sophon.db) | — | Keyword only |
| Pro | PostgreSQL | Qdrant | Full semantic |
| Enterprise | PostgreSQL | Qdrant / Milvus / pgvector | Full semantic |
Schema:
MemoryEntries— long-term, indexed on(UserId, AgentId)+ tenantDailyLogEntries— short-term, indexed on(UserId, AgentId, CreatedAt)+ tenantMemoryEntities/MemoryLinks— the graph, indexed on entity name
SQLite DateTimeOffset doesn't sort in queries, so the engine sorts client-side after fetch. Postgres sorts natively.
Legacy MEMORY.md migration
If you're upgrading from pre-database versions that stored memory in ~/.sophon/memory/MEMORY.md and daily log files, Sophon migrates on first startup:
- Reads
MEMORY.md, treats each-bullet as auser-scoped long-term entry. - Reads
memory/daily/*.mdfiles, imports as daily log rows. - Skips migration if the DB already has entries (idempotent).
No manual step is required.
Security and isolation
- User isolation — every query filters by authenticated user ID. Cross-user reads are impossible.
- Agent isolation —
agent-scoped entries are visible only to the owning agent. - Tenant isolation (Enterprise) — EF Core global query filters enforce tenant boundaries on every query.
- No secrets in memory — the credential vault and memory are separate stores. API keys, OAuth tokens, and other secrets never land in memory and never leak into LLM prompts.
- Approval for forgets —
memory.forgetand any "forget all" operation trigger the approval gate. - Vector metadata filtering — semantic search filters results server-side by user + agent before returning, so a malformed query can't return someone else's memories.
Where to go next
- Agents & SOUL — scope and routing rules
- Approval Gates — why
memory.forgetgates - Connections — services that populate memory automatically