Melian

The Girdle of Memory

Three tiers of remembering: what she holds close, what she keeps in the deep places, and what drifts through like conversation on the wind.

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
);