Metadata-Version: 2.4
Name: alt-memory
Version: 4.5.3
Summary: Local-first AI memory system — realms, domains, entities, hybrid search, entity graph, MCP
Author: Alt Memory
License: Apache-2.0
Project-URL: Homepage, https://github.com/kilvz/alt-memory
Project-URL: Source, https://github.com/kilvz/alt-memory
Project-URL: BugTracker, https://github.com/kilvz/alt-memory/issues
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: faiss-cpu>=1.7.0
Requires-Dist: numpy>=1.24.0
Requires-Dist: scipy>=1.10.0
Requires-Dist: pyyaml>=6.0
Requires-Dist: pathspec>=0.10.0
Requires-Dist: tokenizers>=0.15.0
Provides-Extra: chroma
Requires-Dist: chromadb<2,>=1.5.4; extra == "chroma"
Provides-Extra: onnx
Requires-Dist: onnxruntime>=1.15.0; extra == "onnx"
Requires-Dist: huggingface_hub>=0.20.0; extra == "onnx"
Requires-Dist: transformers>=4.35.0; extra == "onnx"
Provides-Extra: all
Requires-Dist: alt-memory[chroma]; extra == "all"
Requires-Dist: alt-memory[onnx]; extra == "all"
Requires-Dist: sentence-transformers>=3.0.0; extra == "all"
Dynamic: license-file

# Alt Memory

**Local-first persistent memory for AI agents.** Store, search, and manage structured memory with hybrid vector/keyword search, a knowledge graph, agent diaries, personas, file mining, and an MCP server — all offline, no external services.

```bash
pip install alt-memory
```

Benchmarked at ~8ms/tool (FAISS backend, numpy embedder) — roughly 7x faster than the predecessor system.

---

## Table of Contents

- [Installation](#installation)
- [Quick Start — CLI](#quick-start--cli)
- [Core Concepts](#core-concepts)
- [Full CLI Reference](#full-cli-reference)
- [Python API](#python-api)
- [MCP Server](#mcp-server)
- [Search Architecture](#search-architecture)
- [Embedder System](#embedder-system)
- [Backend Switching](#backend-switching)
- [Knowledge Graph](#knowledge-graph)
- [Agent Records / Diaries](#agent-records--diaries)
- [File Mining](#file-mining)
- [Personas](#personas)
- [AAAK Compression](#aaak-compression)
- [Palace Graph (Tunnels)](#palace-graph-tunnels)
- [Sync & Maintenance](#sync--maintenance)
- [Configuration](#configuration)
- [Docker](#docker)
- [Architecture](#architecture)
- [License](#license)

---

## Installation

```bash
pip install alt-memory
```

This gives you FAISS + `tokenizers` (for numpy BERT embedder). On first use, it auto-selects the best embedder available.

| Install | Backend | Embedder |
|---------|---------|----------|
| `pip install alt-memory` | FAISS | numpy BERT (all-MiniLM-L6-v2 via pure numpy) |
| `pip install alt-memory[chroma]` | + ChromaDB | same |
| `pip install alt-memory[onnx]` | same | + ONNX MiniLM / Gemma / BERT |
| `pip install alt-memory[all]` | + ChromaDB | + ONNX + sentence-transformers |

### Extras explained

| Extra | Packages |
|-------|----------|
| `[chroma]` | `chromadb>=1.5.4` |
| `[onnx]` | `onnxruntime`, `huggingface_hub`, `transformers` |
| `[all]` | chroma + onnx + `sentence-transformers` |

Requirements: Python ≥ 3.10, Windows/Linux/macOS.

---

## Quick Start — CLI

```bash
# Initialize a new dimension
alt-memory init

# Check status
alt-memory status

# Store a memory (realm=work, domain=bugs)
alt-memory add --realm work --domain bugs --content "Login freezes on Safari 18.2"

# Search — hybrid by default (vector + keyword)
alt-memory search "login safari" --limit 5

# Browse
alt-memory list --realm work
alt-memory realms
alt-memory rooms

# Knowledge graph
alt-memory kg-add --subject LoginBug --predicate affects --object Safari
alt-memory kg-query LoginBug
alt-memory kg-stats

# Agent diary
alt-memory record --agent claude --entry "Investigated the Safari freeze"

# Mine a file (auto-chunk, extract entities)
alt-memory mine --realm work --domain code src/auth.py

# MCP server
alt-memory mcp --transport stdio
```

### Init with project detection

```bash
# Run init in a project directory — auto-detects corpus origin, entities, domains
alt-memory init /path/to/project --llm-provider ollama --llm-model qwen2.5
```

---

## Core Concepts

| Concept | What it is | Default MCP prefix | SQL analogy |
|---------|-----------|-------------------|-------------|
| **Dimension** | A directory on disk containing SQLite + FAISS/Chroma + config | (implicit) | database server |
| **Realm** | Top-level bucket (e.g. `work`, `personal`, `agent_claude`) | `*_realm` | database |
| **Domain** | Category within a realm (e.g. `bugs`, `ideas`, `code`) | `*_domain` | table |
| **Entity** | Individual memory item with content + metadata | `*_entity` | row |
| **KG fact** | Relationship triple (`subject → predicate → object`) with temporal validity | `kg_*` | edge |
| **Node** | Cross-reference entry connecting source files to entity references | `*_node` | index |
| **Tunnel** | Cross-realm link between domains (the "palace graph") | `*_tunnel` | foreign key |
| **Record** | Per-agent temporal diary entry | `record_*` / `memory_*` | log |
| **Persona** | Character definition (system prompt + name) with isolated realm | `*_persona` | schema |

Hybrid search combines FAISS/Chroma vector similarity with SQLite FTS5 keyword matching, re-ranked by weighted BM25.

### On-disk layout

```
~/.alt-memory/
├── dimension.db          # SQLite + FTS5 (all entities, metadata, KG)
├── index.faiss           # FAISS vector index (or chroma.sqlite3 for ChromaDB)
├── dimension.json        # Config (backend, embedder, model settings)
├── persona.json          # Persona registry
├── nodes/                # Packed cross-reference node storage
├── entity_registry.json  # Entity name → ID mappings
├── knowledge_graph.json  # Relationship store (backup/sync)
└── aaak_cache.json       # AAAK compression cache
```

All data is portable — copy `~/.alt-memory` to another machine and it works.

---

## Full CLI Reference

30+ commands. The dimension path defaults to `~/.alt-memory`; override with `--dimension`.

### Init & Status

| Command | Description |
|---------|-------------|
| `alt-memory init [dir]` | Initialize dimension; optionally scan a project directory for entities/domains |
| `alt-memory status` | Show dimension status (JSON: entity count, realms, domains, embedder) |
| `alt-memory repair-status` | Quick health check (entity count, SQLite integrity) |

### CRUD

| Command | Description |
|---------|-------------|
| `alt-memory add -w <realm> -r <domain> -c <content>` | Add an entity |
| `alt-memory get <entity_id>` | Get an entity by ID |
| `alt-memory list [-w <realm>] [-r <domain>] [-l <limit>] [-o <offset>]` | List entities with pagination |
| `alt-memory delete <entity_id>` | Delete an entity |
| `alt-memory realms [--verbose]` | List all realms |
| `alt-memory rooms [--realm]` | List all domains (optionally filtered) |

### Search

| Command | Description |
|---------|-------------|
| `alt-memory search <query> [-w <realm>] [-r <domain>] [--mode <mode>]` | Search. Modes: `hybrid` (default), `vector`, `keyword` |
| `alt-memory check-dup <content> [--threshold 0.9]` | Check for duplicate content |

### Knowledge Graph

| Command | Description |
|---------|-------------|
| `alt-memory kg-add -s <subject> -p <predicate> -o <object>` | Add a KG fact |
| `alt-memory kg-query [entity] [--predicate] [--as-of] [--direction]` | Query KG facts |
| `alt-memory kg-invalidate -s <subject> -p <predicate> -o <object>` | Mark a fact as ended |
| `alt-memory kg-stats` | KG statistics |

### Records (Agent Diaries)

| Command | Description |
|---------|-------------|
| `alt-memory record -a <agent> -e <entry> [-t <topic>]` | Write a diary entry |
| `alt-memory record-read -a <agent> [--last-n 10]` | Read diary entries |
| `alt-memory record-ingest --dir <path>` | Ingest daily summary files into the dimension |

### Mining

| Command | Description |
|---------|-------------|
| `alt-memory mine <file>` | Mine a single file into the dimension |
| `alt-memory mine <dir> [--mode projects\|convos\|extract]` | Mine a directory of files |
| `alt-memory sweep <path>` | Sweep `.jsonl` files (message-granular mining) |
| `alt-memory split [--source] [--output-dir]` | Split mega-files into per-session files |

### MCP Server

| Command | Description |
|---------|-------------|
| `alt-memory mcp --transport stdio` | Run MCP server over stdio (for AI coding agents) |
| `alt-memory mcp --transport sse --port 8316` | Run MCP server over HTTP SSE |

### Maintenance

| Command | Description |
|---------|-------------|
| `alt-memory sync [--apply] [--project-dir DIR]` | Prune stale entities (gitignored/deleted sources) |
| `alt-memory rebuild-fts` | Rebuild FTS5 full-text search index |
| `alt-memory migrate [--rebuild-faiss] [--status]` | Schema migration and FAISS rebuild |
| `alt-memory repair [--integrity] [--vacuum] [--rebuild-fts]` | Repair utilities |
| `alt-memory rebuild-from-sqlite` | Rebuild FAISS index from SQLite ground truth |
| `alt-memory aaak <text>` | Compress text to AAAK format |

### Other

| Command | Description |
|---------|-------------|
| `alt-memory wake-up [--agent <name>]` | Show L0+L1 wake-up context |
| `alt-memory hook run --hook <h> --harness <h>` | Run a hook (claude-code / codex) |
| `alt-memory instructions <topic>` | Output skill instructions for AI agents |

---

## Python API

```python
from alt_memory import Dimension

d = Dimension(path="~/.alt-memory")  # or Dimension(path="./my-dim", backend="faiss")
d.init()
```

### Core CRUD

```python
# Add an entity
eid = d.add_entity("work", "bugs", "Login freezes on Safari",
                   metadata={"priority": "high"}, source_file="src/auth.py")

# Get by ID
entity = d.get_entity(eid)

# List with filters
entities = d.list_entities(realm="work", domain="bugs", limit=20, offset=0)

# Update
d.update_entity(eid, content="Updated description", metadata={"status": "fixed"})

# Delete
d.delete_entity(eid)
d.delete_entities([eid1, eid2])

# Batch add
ids = d.batch_add_entities([
    ("work", "bugs", "Bug one", {}, "", None),
    ("work", "bugs", "Bug two", {}, "", None),
])
```

### Search

```python
# Hybrid (default) — vector + keyword, re-ranked
results = d.search("login safari", n_results=10, mode="hybrid")

# Vector-only — semantic similarity
results = d.search("authentication timeout", mode="vector")

# Keyword-only — exact match via FTS5
results = d.search("Safari 18.2", mode="keyword")

# Filter by realm/domain
results = d.search("login", realm="work", domain="bugs")

# Programmatic search (used by MCP)
results = d.search_memories("login", n_results=5, realm="work",
                            max_distance=0.5, candidate_strategy="union")
```

Each `SearchResult` has: `id`, `text`, `distance`, `metadata`, `realm`, `domain`.

### Realm & Domain Management

```python
# Realms
d.list_realms()                           # → [{"name": "work", "entity_count": 42, ...}]
d.get_or_create_realm("work", "Work stuff")
d.delete_realm("work")                    # deletes all domains and entities within

# Domains
d.list_domains(realm="work")              # → [{"name": "bugs", "entity_count": 10, ...}]
d.get_or_create_domain("work", "bugs", "Bug tracking")
d.delete_domain("work", "bugs")
```

### Knowledge Graph

```python
# Add facts
d.kg.add("LoginBug", "affects", "Safari", valid_from="2026-05-01")
d.kg.add("LoginBug", "priority", "high")

# Query
facts = d.kg.query("LoginBug")                           # all facts about LoginBug
facts = d.kg.query("LoginBug", as_of="2026-05-15")        # temporal query
facts = d.kg.query("LoginBug", direction="outgoing")      # outgoing edges only

# Invalidate
d.kg.invalidate("LoginBug", "affects", "Safari", ended="2026-06-01")

# Stats & timeline
d.kg.stats()
d.kg.timeline(entity="LoginBug")          # chronological story of an entity
```

### Agent Records (Diaries)

```python
# Write entries at different layers
d.diary_write("claude", "DEBUG: found race condition in auth retry", topic="debug")
d.diary_write("claude", "SESSION:2026-05-31|fixed.auth|★★★", topic="summary")

# Read
entries = d.diary_read("claude", last_n=5)

# List all agents with records
d.list_agents()  # Note: standalone function, see API note below
```

> **Note on standalone functions:** Some operations are standalone functions in their respective modules, not `Dimension` methods. These include `sync` (`sync_dimension` in `sync.py`), traversal functions (`traverse`, `build_graph`, `create_tunnel` in `dim_graph.py`), and mining functions (`mine_file_into_dimension`, `mine_text_into_dimension`, `batch_mine` in `miner.py`). They accept a dimension path string or Dimension object. See the [MCP tools table](#all-mcp-tools) for the full list — all operations are available via the MCP server regardless.

### Backend & Embedder Control

```python
# Switch vector store backend
d.set_backend("chroma")        # FAISS → ChromaDB
d.set_backend("faiss")         # ChromaDB → FAISS

# Switch embedding model (auto-reindexes all entities)
d.set_embedder("sentence")     # sentence-transformers (best quality)
d.set_embedder("numpy")        # TF-IDF+SVD (lightest)
d.set_embedder("minilm")       # ONNX MiniLM
d.set_embedder("spacy")        # spaCy GloVe
d.set_embedder("embeddinggemma")  # Google Gemma via ONNX

# Device control for ONNX embedders
d.set_embedder("minilm", device="cpu")
d.set_embedder("minilm", device="cuda")
d.set_embedder("minilm", device="dml")    # DirectML on Windows
```

### Status & Maintenance

```python
# Status
status = d.status()         # → {entities, realms, domains, embedder, ...}
taxonomy = d.get_taxonomy() # → {realm: {domain: count, ...}, ...}

# FTS rebuild
d.rebuild_fts()

# Duplicate check
dup = d.check_duplicate("new content to check", threshold=0.9)

# Reconnect after external changes
d.reconnect()

# Memories filed away check
d.memories_filed_away()
```

### Export / Import

```python
# Export all entities as JSON-serializable list
export = d.export_collection()
export = d.export_collection(realm="work", domain="bugs")  # filtered

# Import entities from list
count = d.import_entities(export, overwrite=False)
```

### Personas

See the full dedicated docs at [`docs/personas.md`](docs/personas.md).

```python
# Quick reference
d.create_persona("coder", system_prompt="You are an expert Python developer.")
d.set_persona("coder")
d.get_persona()                        # → active persona dict
d.get_persona_character()              # → "You are an expert Python developer."
d.get_persona_character("coder")       # → same, by name
d.list_personas()                      # → all registered personas
d.delete_persona("coder")              # → True/False
d.switch_persona("architect")          # alias for set_persona
```

---

## MCP Server

The MCP server exposes **65 tools** over stdio (for coding agents) or SSE (for remote access).

```bash
# Stdio mode — for AI coding agents (Claude Code, Codex, Cline)
alt-memory mcp --transport stdio

# SSE mode — for remote/local HTTP access
alt-memory mcp --transport sse --port 8316
```

### AI Agent Configuration

Configure in your MCP client:

```json
{
  "mcpServers": {
    "alt-memory": {
      "command": "alt-memory",
      "args": ["mcp", "--transport", "stdio"]
    }
  }
}
```

### All 65 MCP Tools

| Category | Tool | Required Params | Optional Params | Description |
|----------|------|----------------|----------------|-------------|
| **Search** | `search` | `query` | `n_results`, `realm`, `domain`, `mode` | Hybrid/vector/keyword search |
| | `check_duplicate` | `content` | `threshold` | Check if content exists |
| **CRUD** | `add_entity` | `realm`, `domain`, `content` | `metadata`, `source_file`, `entity_id` | Store a memory |
| | `get_entity` | `entity_id` | — | Fetch by ID |
| | `update_entity` | `entity_id` | `content`, `metadata`, `realm`, `domain` | Update fields |
| | `delete_entity` | `entity_id` | — | Delete one |
| | `delete_entities` | `entity_ids` (array) | — | Bulk delete |
| | `list_entities` | — | `realm`, `domain`, `limit`, `offset` | List with pagination |
| | `batch_add_entities` | `entities` (array) | — | Bulk add |
| | `import_entities` | `entities` (array) | `overwrite` | Import from JSON |
| | `export_collection` | — | `realm`, `domain` | Export as JSON |
| **Realms** | `create_realm` | `name` | `description` | Create top-level bucket |
| | `delete_realm` | `name` | — | Delete realm + contents |
| | `list_realms` | — | — | List all |
| | `get_taxonomy` | — | — | Realm → domain → count |
| | `get_status` | — | — | Entity count, embedder info |
| **Domains** | `create_domain` | `realm`, `name` | `description` | Create domain |
| | `delete_domain` | `realm`, `name` | — | Delete domain |
| | `list_domains` | — | `realm` | List domains |
| **KG** | `kg_add` | `subject`, `predicate`, `object` | `valid_from`, `valid_to`, `source` | Add fact |
| | `kg_query` | — | `entity`, `predicate`, `as_of`, `all`, `direction` | Query facts |
| | `kg_invalidate` | `subject`, `predicate`, `object` | `ended` | End a fact |
| | `kg_stats` | — | — | KG statistics |
| | `kg_timeline` | — | `entity` | Chronological story |
| **Records** | `record_write` | `agent`, `entry` | `topic`, `realm` | Diary entry |
| | `record_read` | `agent` | `last_n`, `realm` | Read diary |
| | `list_agents` | — | — | All agents with records |
| **Mine** | `mine_file` | `filepath`, `realm`, `domain` | — | Mine a single file |
| | `mine_text` | `text`, `realm`, `domain` | `source`, `chunk` | Mine text content |
| | `batch_mine` | `directory` | `realm`, `pattern` | Mine a directory |
| | `sync` | — | `project_dir`, `realm`, `apply` | Prune stale entities |
| **Graph** | `create_tunnel` | `source_realm`, `source_domain`, `target_realm`, `target_domain` | `label`, `source_entity_id`, `target_entity_id` | Link domains |
| | `delete_tunnel` | `tunnel_id` | — | Remove a tunnel |
| | `list_tunnels` | — | `realm` | List all tunnels |
| | `find_tunnels` | — | `realm_a`, `realm_b` | Find bridges between realms |
| | `follow_tunnels` | `realm`, `domain` | — | Follow connections |
| | `traverse` | `start_domain` | `max_hops` | Walk the graph |
| | `graph_stats` | — | — | Overview of connections |
| **Backend** | `set_backend` | `backend` (faiss\|chroma) | `reindex` | Switch vector store |
| | `get_backend` | — | — | Current backend name |
| **Embedder** | `set_embedder` | `model` | `device`, `reindex` | Switch embedder |
| | `set_default_embedder` | `model` | — | Set global default |
| | `get_default_embedder` | — | — | Get global default |
| **Persona** | `get_persona` | — | — | Active persona |
| | `set_persona` | `name` | `system_prompt`, `description`, `metadata` | Set/switch persona |
| | `switch_persona` | `name` | `system_prompt`, `description`, `metadata` | Alias for set |
| | `create_persona` | `name` | `system_prompt`, `description`, `metadata` | Create without activating |
| | `list_personas` | — | — | All registered |
| | `delete_persona` | `name` | — | Remove from registry |
| | `get_persona_character` | — | `name` | Get system prompt string |
| **AAAK** | `aaak_compress` | `text` | `max_len` | Compress to AAAK |
| | `aaak_decompress` | `text` | — | Decompress AAAK |
| | `aaak_parse` | `text` | — | Parse AAAK entry |
| | `get_aaak_spec` | — | — | Full AAAK specification |
| **Other** | `hook_settings` | — | `silent_save`, `desktop_toast` | Configure hooks |
| | `memories_filed_away` | — | — | Checkpoint status |
| | `rebuild_fts` | — | — | Rebuild FTS5 index |
| | `reconnect` | — | — | Reconnect to database |
| | `get_people_map` | — | — | Name variant mappings |
| | `set_people_map` | `map` | — | Set name variant mappings |
| | `init_dimension` | — | — | Initialize dimension |
| | `close_dimension` | — | — | Close dimension gracefully |
| | `ping` | — | — | Health check |

All 65 tools are available as JSON-RPC calls. Example:

```json
{"name": "add_entity", "arguments": {"realm": "work", "domain": "bugs", "content": "Login freezes on Safari 18.2"}}
{"name": "search", "arguments": {"query": "login safari", "n_results": 5}}
{"name": "set_embedder", "arguments": {"model": "minilm"}}
{"name": "set_backend", "arguments": {"backend": "chroma"}}
```

---

## Search Architecture

### Three Search Modes

| Mode | Method | How it works | Best for |
|------|--------|-------------|----------|
| **Vector** | `mode="vector"` | Embed query → FAISS/Chroma cosine distance ranking | Semantic similarity ("find related concepts") |
| **Keyword** | `mode="keyword"` | FTS5 token match → BM25 ranking | Exact term lookup ("find where I wrote 'login bug'") |
| **Hybrid** | `mode="hybrid"` (default) | Vector + keyword merged, re-ranked by `0.6 vector + 0.4 BM25` | General-purpose |

### Search Pipeline

```
search(query, realm, domain, mode)
│
├── mode="keyword" ──▶ _build_fts_query(query)
│                      │  "login safari" → "login* AND safari*"
│                      │  "login AND safari" → passed through
│                      │  "\"login safari\"" → phrase query
│                      ▼
│                      FTS5 MATCH on entities_fts
│                      JOIN entities for realm/domain filter
│                      ORDER BY rank (BM25)
│                      Fallback to vector search if FTS5 fails
│
├── mode="vector" ────▶ embedder.embed(query) → np.ndarray
│                       FAISS IndexIDMap.search() or ChromaDB query()
│                       Cosine distance → filter by realm/domain → top-k
│
└── mode="hybrid" ────▶ Over-fetch 2× from both vector + keyword
                        Union dedup by entity ID (vector first)
                        Final score = 0.6 × vec_sim + 0.4 × BM25_norm
                        Return top-n_results
```

### Full Pipeline (`search_memories`)

Used by the MCP `search` tool for richer results:

1. Over-fetch 3× candidates from vector + keyword
2. Apply `max_distance` threshold (filter out distant results)
3. Merge candidates — `"vector"` strategy (default) or `"union"` (append BM25-only results)
4. **Node boost** — entities from files with cross-reference nodes get priority
5. **Hydration** — for boosted results, expand with ±1 neighbor chunks for context
6. BM25 re-rank with weights `0.6 vector + 0.4 BM25`
7. Attach `node_preview` text to boosted results
8. Return top-n_results dict

### FTS5 Query Construction

| Input | Output | Rule |
|-------|--------|------|
| `"login safari"` | `"login* AND safari*"` | Bare terms → prefix wildcard + AND |
| `"login AND safari"` | passed through | Operators (AND, OR, NOT, NEAR) verbatim |
| `"\"login safari\""` | passed through | Phrase queries verbatim |
| `"multi-part"` | `"multi* AND part*"` | Hyphenated terms split on `-` |

### Direct Lookups (non-search)

```python
d.get_entity("entity_id")          # SQL PK lookup
d.list_entities(realm="...")       # SQL with filters, ORDER BY created_at DESC
```

---

## Embedder System

### Auto-Detection

Alt Memory automatically picks the best embedder at runtime. No configuration needed.

```
Priority: sentence-transformers → numpy BERT → TF-IDF+SVD
Quality:  ★★★★★                 → ★★★★      → ★★
Speed:    ★★★★                  → ★★★       → ★★★★★
Deps:     PyTorch (~800MB)       → tokenizers → none
```

### Embedder Reference

| Name | Quality | Speed | Dependencies | Platform |
|------|---------|-------|-------------|----------|
| `sentence` | ★★★★★ | ★★★★ | sentence-transformers + PyTorch | all |
| `numpy_bert` | ★★★★ | ★★★ | tokenizers (included) | **all incl. Alpine** |
| `minilm` | ★★★★ | ★★★★★ | onnxruntime `[onnx]` | glibc |
| `embeddinggemma` | ★★★★★ | ★★★★ | onnxruntime `[onnx]` | glibc |
| `bert` | ★★★★ | ★★★★ | onnxruntime or tokenizers `[onnx]` | all |
| `spacy` | ★★★ | ★★★ | spacy + en_core_web_md | all |
| `numpy` | ★★ | ★★★★★ | none (always available) | all |

All embedders produce 384-dimensional vectors.

### Switching Embedders

```bash
# CLI
export ALT_DEFAULT_EMBEDDER=minilm      # set global default
export ALT_DEFAULT_EMBEDDER=numpy       # TF-IDF+SVD (lightest)

# Python
d.set_embedder("sentence")              # auto-reindexes all entities
d.set_embedder("numpy", reindex=False)  # skip reindex (existing vectors stale)

# MCP
{"name": "set_embedder", "arguments": {"model": "minilm", "device": "cpu"}}
{"name": "set_default_embedder", "arguments": {"model": "numpy"}}
```

**Device control** for ONNX embedders:
- `"auto"` — probe CUDA → CoreML → DirectML → CPU (default)
- `"cpu"` — force CPU execution
- `"cuda"` — NVIDIA GPU via CUDA
- `"coreml"` — Apple Silicon
- `"dml"` — Windows DirectML

When you change the embedder, all entities are automatically re-embedded. On large dimensions this can take time.

---

## Backend Switching

Swap between FAISS and ChromaDB at runtime. All data persists in SQLite — only the vector index changes.

```bash
# CLI
alt-memory backend chroma   # Note: use MCP or Python API for switching

# Python
d = Dimension(path="~/.alt-memory", backend="chroma")  # initial backend
d.set_backend("faiss")        # hot-swap — moves vectors between backends
d.set_backend("chroma")

# MCP
{"name": "set_backend", "arguments": {"backend": "chroma"}}
{"name": "get_backend", "arguments": {}}
```

When `reindex=True` (default), all entities are re-embedded into the new store.

---

## Knowledge Graph

The KG stores structured relationship triples with temporal validity.

### Concepts

- **Subject** — the entity doing/being something (e.g. `LoginBug`)
- **Predicate** — the relationship type (e.g. `affects`, `priority`, `found_in`)
- **Object** — the connected entity (e.g. `Safari`, `high`, `v4.3.0`)
- **Temporal validity** — `valid_from` / `valid_to` dates let you answer "what was true at a given time"

### MCP Usage

```json
{"name": "kg_add", "arguments": {"subject": "LoginBug", "predicate": "affects", "object": "Safari"}}
{"name": "kg_add", "arguments": {"subject": "LoginBug", "predicate": "priority", "object": "high", "valid_from": "2026-05-01"}}
{"name": "kg_query", "arguments": {"entity": "LoginBug"}}
{"name": "kg_query", "arguments": {"entity": "LoginBug", "as_of": "2026-05-15"}}
{"name": "kg_invalidate", "arguments": {"subject": "LoginBug", "predicate": "priority", "object": "high", "ended": "2026-06-01"}}
{"name": "kg_stats", "arguments": {}}
{"name": "kg_timeline", "arguments": {"entity": "LoginBug"}}
```

---

## Agent Records / Diaries

Per-agent temporal entries with three memory layers.

### L0 — Immediate Records (per-exchanges)

```python
d.diary_write("claude", "DEBUG: found race condition in auth retry", topic="debug")
d.diary_write("claude", "User prefers async Python with asyncio", topic="preference")
```

### L1 — Daily Summaries (end of session)

```python
d.diary_write("claude", "SESSION:2026-05-31|fixed.auth.race.cond|★★★", topic="summary")
```

### L2 — Patterns (weekly/ongoing)

```python
d.diary_write("claude", "PATTERN: auth.timeouts→rate.limit.insufficient|★★★★★", topic="pattern")
```

### Reading

```bash
alt-memory wake-up --agent claude --last-n 5   # L0+L1 context
alt-memory record-read --agent claude --last-n 10
```

```python
entries = d.diary_read("claude", last_n=5)
```

### MCP

```json
{"name": "record_write", "arguments": {"agent": "claude", "entry": "Fixed login bug", "topic": "debug"}}
{"name": "record_read", "arguments": {"agent": "claude", "last_n": 5}}
{"name": "list_agents", "arguments": {}}
```

---

## File Mining

Mine source files, conversations, and text into the dimension. Auto-chunks, extracts metadata, and stores entities with source file references.

### CLI

```bash
# Mine a single file
alt-memory mine src/auth.py --realm myproject --domain code

# Mine a directory (auto-globs, respects .gitignore)
alt-memory mine /path/to/project --realm myproject

# Conversation mode
alt-memory mine /path/to/convos --mode convos

# Extraction mode
alt-memory mine /path/to/formats --mode extract

# Preview without filing
alt-memory mine /path/to/project --dry-run

# Sweep .jsonl files
alt-memory sweep /path/to/file.jsonl
```

### Python

```python
from alt_memory.miner import mine_file_into_dimension, batch_mine, mine_text_into_dimension

# Single file
mine_file_into_dimension(dim, "src/auth.py", "myproject", "code")

# Directory (batch)
batch_mine(dim, "/path/to/project", realm="myproject",
           respect_gitignore=True, file_limit=100)

# Text content
mine_text_into_dimension(dim, text, "myproject", "notes", source="clipboard")
```

### MCP

```json
{"name": "mine_file", "arguments": {"filepath": "src/auth.py", "realm": "myproject", "domain": "code"}}
{"name": "batch_mine", "arguments": {"directory": "/path/to/project", "realm": "myproject"}}
{"name": "mine_text", "arguments": {"text": "raw text...", "realm": "myproject", "domain": "notes"}}
```

---

## Personas

Full documentation: [`docs/personas.md`](docs/personas.md)

A persona is a **character definition** — a system prompt with a name, matching the [Eternal AI `.txt` file model](https://github.com/eternalai-org/eternal-ai). Model and framework are deployment choices, not part of the character definition.

Each persona gets an isolated `persona_<name>` realm for memories, KG facts, and diary entries.

```python
d.create_persona("donald_trump", system_prompt="Act as if you are Donald Trump...")
d.set_persona("donald_trump")
prompt = d.get_persona_character()
# → "Act as if you are Donald Trump..."
# Inject as {"role": "system", "content": prompt} in your LLM call
```

7 MCP tools: `get_persona`, `set_persona`, `switch_persona`, `create_persona`, `list_personas`, `delete_persona`, `get_persona_character`.

---

## AAAK Compression

AAAK is a compressed memory dialect — readable by humans and LLMs without decoding. Uses 3-letter entity codes, emotion markers, pipe-separated fields, and importance ratings.

```bash
# CLI
alt-memory aaak "Alice loves Jordan, they have two kids: Riley (18, into sports)"
# → FAM: ALC→♡JOR | 2D(kids): RIL(18,sports) | ★★★★

# Decompress / parse
alt-memory aaak "FAM: ALC→♡JOR" --output-format json
```

```python
from alt_memory import aaak_compress, aaak_decompress, aaak_parse_entry

compressed = aaak_compress("Long text here")
parsed = aaak_parse_entry(compressed)
original = aaak_decompress(compressed)
```

```json
{"name": "aaak_compress", "arguments": {"text": "Your long text here"}}
{"name": "aaak_decompress", "arguments": {"text": "FAM: ALC→♡JOR..."}}
{"name": "aaak_parse", "arguments": {"text": "FAM: ALC→♡JOR..."}}
{"name": "get_aaak_spec", "arguments": {}}
```

---

## Palace Graph (Tunnels)

The palace graph connects domains across realms via **tunnels** — explicit cross-realm relationships.

```python
from alt_memory.dim_graph import create_tunnel, traverse, graph_stats, find_tunnels

# Link two domains
create_tunnel(
    source_realm="work", source_domain="bugs",
    target_realm="personal", target_domain="learnings",
    label="Same root cause",
)

# Walk the graph from a domain
traverse(start_domain="bugs", max_hops=2)

# Find bridges between realms
find_tunnels(realm_a="work", realm_b="personal")
```

```json
{"name": "create_tunnel", "arguments": {
  "source_realm": "work", "source_domain": "bugs",
  "target_realm": "personal", "target_domain": "learnings",
  "label": "Same root cause"
}}
{"name": "traverse", "arguments": {"start_domain": "bugs", "max_hops": 2}}
{"name": "graph_stats", "arguments": {}}
{"name": "follow_tunnels", "arguments": {"realm": "work", "domain": "bugs"}}
```

---

## Sync & Maintenance

### Sync (prune stale entities)

Removes entities whose source files were deleted or moved:

```bash
# Dry-run preview
alt-memory sync --project-dir /path/to/repo

# Actually delete
alt-memory sync --project-dir /path/to/repo --apply

# Limited to one realm
alt-memory sync --project-dir /path/to/repo --realm myproject --apply
```

### Repair

```bash
# Quick health check
alt-memory repair-status

# Check SQLite integrity
alt-memory repair --integrity

# Run VACUUM
alt-memory repair --vacuum

# Rebuild FTS5 index
alt-memory repair --rebuild-fts

# Interactive repair mode
alt-memory repair --mode status
alt-memory repair --mode scan
alt-memory repair --mode prune
alt-memory repair --mode rebuild

# Full rebuild from SQLite (covers FAISS corruption)
alt-memory rebuild-from-sqlite
```

### Migration

```bash
# Check schema status
alt-memory migrate --status

# Apply pending migrations
alt-memory migrate

# Rebuild FAISS from SQLite
alt-memory migrate --rebuild-faiss

# Dry run
alt-memory migrate --dry-run
```

### Maintenance

```bash
# Rebuild just the FTS5 index
alt-memory rebuild-fts

# Check for duplicate content
alt-memory check-dup "content to check"
```

---

## Configuration

Global configuration is stored in `~/.alt-memory/config.json`.

### Environment Variables

| Variable | Default | Description |
|----------|---------|-------------|
| `ALT_DIM_PATH` | `~/.alt-memory` | Default dimension path |
| `ALT_DEFAULT_EMBEDDER` | `auto` | Force a specific embedder (`numpy`, `numpy_bert`, `sentence`, `minilm`, `spacy`) |
| `ALT_EMBEDDING_DEVICE` | `auto` | ONNX device (`cpu`, `cuda`, `coreml`, `dml`) |

### Config File (`~/.alt-memory/config.json`)

```json
{
  "dim_path": "~/.alt-memory",
  "default_embedder": "numpy_bert",
  "embedding_device": "auto",
  "people_map": {"Alex": "Alexander", "Beth": "Elizabeth"},
  "hooks_auto_save": true,
  "hook_silent_save": true,
  "hook_desktop_toast": false
}
```

### Hook Settings (via MCP)

```json
{"name": "hook_settings", "arguments": {"silent_save": true, "desktop_toast": false}}
```

---

## Docker

```bash
# FAISS + numpy BERT (Alpine, ~117MB content)
docker run -v ~/.alt-memory:/root/.alt-memory kilv/alt-memory:alpine mcp

# Full install (Debian, includes chroma + onnx)
docker run -v ~/.alt-memory:/root/.alt-memory kilv/alt-memory:latest mcp
```

Tags:
- `kilv/alt-memory:latest` — Debian-based, full install
- `kilv/alt-memory:4.5.3` — versioned Debian

  - `kilv/alt-memory:4.5.3-alpine` — versioned Alpine

---

## Architecture

```
┌──────────────────────────────────────────────────────┐
│              MCP Server (stdio / SSE)                 │
│           65 JSON-RPC tools for AI agents             │
├──────────────────────────────────────────────────────┤
│                     Dimension                         │
│         orchestrates realms, domains, entities        │
├──────────────────────┬───────────────────────────────┤
│    Vector Store       │    SQLite + FTS5              │
│  (FAISS / ChromaDB)   │  (entities, KG, metadata)    │
├──────────────────────┴───────────────────────────────┤
│              Embedder Layer (pluggable)                │
│  numpy (TF-IDF+SVD) ── numpy_bert ── minilm (ONNX)    │
│  sentence-transformers ── spaCy ── embeddinggemma      │
└──────────────────────────────────────────────────────┘
```

### Key files

| File | Purpose |
|------|---------|
| `alt_memory/dimension.py` | Main `Dimension` class — all CRUD, search, status, personas |
| `alt_memory/mcp_server.py` | MCP server — 65 JSON-RPC tools over stdio/SSE |
| `alt_memory/cli.py` | CLI — 30+ commands |
| `alt_memory/backends/embedder.py` | Embedder factory — numpy, ONNX, sentence, spaCy |
| `alt_memory/backends/faiss_store.py` | FAISS vector store |
| `alt_memory/backends/chroma_store.py` | ChromaDB vector store |
| `alt_memory/backends/knowledge_graph.py` | Knowledge Graph (temporal triples) |
| `alt_memory/searcher.py` | BM25 ranking, hybrid re-rank, neighbor expansion |
| `alt_memory/dim_graph.py` | Palace graph — tunnels, traversal |
| `alt_memory/miner.py` | File mining — auto-chunk, entity extraction |
| `alt_memory/sync.py` | Sync — prune stale entities |
| `alt_memory/dialect.py` | AAAK compression dialect |
| `alt_memory/layers.py` | L0/L1/L2 memory stack |
| `alt_memory/config.py` | Configuration manager |

---

## License

MIT
