The Girdle of Memory
Overview
Melian's memory system is built on three distinct layers, each serving a different purpose in how she understands and recalls your world.
Core Memory
Core memory is always present. It lives in Melian's system prompt, informing every response. This is where your identity, preferences, and the things that define your relationship with Melian are stored.
Core memory is small and curated. It holds what matters most: your name, your communication style, your key preferences, and the personality traits that make Melian yours. Stored as a flat key-value JSON object at ~/.melian/memory/core.json, it is read on every agent turn and injected directly into the system prompt.
Archival Memory
Archival memory is the deep store: a Qdrant vector database that holds long-term knowledge. When Melian learns something important but it doesn't need to be in every conversation, it goes here.
Archival memory is searched semantically. Melian doesn't need to remember the exact words; she searches by meaning using 768-dimensional embeddings and cosine similarity, finding relevant memories even when the phrasing is different. The collection is named archival_memory.
Recall Memory
Recall memory is conversation history, stored in SQLite with FTS5 full-text search and WAL mode enabled. It's the running thread of what you've discussed, what decisions were made, and what context was established. Each message carries an optional embedding blob for future semantic search over history.
Architecture
┌─────────────────────────────────────────┐
│ Agent Router │
│ (always has core memory) │
├──────────┬──────────┬───────────────────┤
│ Core │ Archival │ Recall │
│ Memory │ Memory │ Memory │
│ (JSON) │ (Qdrant) │ (SQLite) │
└──────────┴──────────┴───────────────────┘Interfaces
export interface CoreMemory {
[section: string]: string;
}
export interface ArchivalEntry {
id: string;
text: string;
tags: string[];
timestamp: number;
}
export interface RecallMessage {
id: number;
external_id: string;
conversation_id: string;
role: string;
content: string;
timestamp: number;
}Tools
| Tool | Parameter | Type | Required | Default | Description |
|---|---|---|---|---|---|
core_memory_read |
section |
string | yes | - | Section key to read |
core_memory_update |
section |
string | yes | - | Section key to update |
value |
string | yes | - | New value for the section | |
core_memory_clear |
section |
string | yes | - | Section key to clear |
archival_memory_insert |
text |
string | yes | - | Text content to store |
tags |
string[] | no | - | Optional tags for filtering | |
archival_memory_search |
query |
string | yes | - | Semantic search query |
limit |
number | no | 10 | Max results to return | |
scope |
"archival" | "knowledge" | "all" |
no | "all" |
Which collections to search | |
archival_memory_delete |
id |
string | yes | - | Entry ID to delete |
recall_memory_search |
query |
string | yes | - | FTS5 full-text query |
after |
number | no | - | Epoch milliseconds lower bound | |
before |
number | no | - | Epoch milliseconds upper bound | |
recall_memory_list |
conversation_id |
string | no | - | Filter to one conversation |
limit |
number | no | 50 | Max messages to return |
API
| Method | Path | Body / Params | Description |
|---|---|---|---|
| GET | /memory/core |
- | Return all core memory sections |
| PUT | /memory/core/:section |
{ value: string } |
Write a core memory section |
| POST | /memory/archival/search |
{ query, limit? } |
Semantic search archival store |
| POST | /memory/archival |
{ text, tags? } |
Insert an archival entry |
| DELETE | /memory/archival/:id |
- | Delete an archival entry by ID |
| POST | /memory/recall/search |
{ query, after?, before? } |
FTS5 search over recall messages |
Configuration
Storage locations:
| Layer | Backend | Location |
|---|---|---|
| Core | JSON file | ~/.melian/memory/core.json |
| Archival | Qdrant | localhost:6333, collection archival_memory, 768-dim cosine |
| Recall | SQLite | alongside main data store, WAL mode, FTS5 |
Recall schema:
CREATE TABLE messages (
id INTEGER PRIMARY KEY AUTOINCREMENT,
external_id TEXT UNIQUE NOT NULL,
conversation_id TEXT NOT NULL,
role TEXT NOT NULL,
content TEXT NOT NULL,
timestamp INTEGER NOT NULL,
embedding BLOB
);
CREATE VIRTUAL TABLE messages_fts USING fts5(
content,
content=messages,
content_rowid=id
);