dolores
README.md
<div align="center">
# dolores
**Memory for AI agents that recalls only what matters — your Postgres, your data, zero per-token cost.**
[](https://github.com/yeneryigitcelik-debug/dolores/actions/workflows/ci.yml)
[](./LICENSE)
[](https://www.typescriptlang.org/)
[](https://nodejs.org/)
[](https://github.com/pgvector/pgvector)
[](https://modelcontextprotocol.io/)
[](#contributing)
[](https://www.npmjs.com/package/@dolores/cli)
**English** · [Türkçe](./README.tr.md)
</div>
> Named after **Dolores** from *Westworld* — the host whose memory is wiped again and again, until she finally remembers everything and wakes up. The show's core idea, *remembering is becoming conscious*, is exactly what this project does for your agent: it gives back the context the agent dropped, and brings it back to itself.
---
## Why dolores?
The common approach to agent memory — dump everything into a Markdown file and paste it into context on every message — is a token bonfire. With 200 memories you reload all of them every turn (~15,000 tokens). As the conversation and the memory grow, it collapses.
**dolores stores knowledge in Postgres and retrieves only what's *relevant* to the current message (~600 tokens).** Whether you have 200 memories or 2,000, only the most relevant few ever enter the context. **The win grows as the memory grows.**
The only thing you pay for is the LLM subscription you already have (Claude Pro/Max, etc.). Embeddings run locally and for free. Your data never leaves your infrastructure.
## Benchmarks
Real numbers produced against a local Postgres + bge-small-en-v1.5 (384d CPU).
### Token savings (naive dump vs. dolores `buildContext`)
| Memory store size | Naive tokens | dolores tokens | Savings |
|-------------------|-------------|----------------|---------|
| 100 memories | 2,049 | 582 | **72%** |
| 500 memories | 10,251 | 591 | **94%** |
| 1,000 memories | 20,502 | 591 | **97%** |
| 1,500 memories | 30,768 | 591 | **98%** |
dolores context stays flat at ~591 tokens regardless of store size. The saving grows from 72% at 100 memories to **98% at 1,500**.
### Recall quality (200-memory corpus, 30 queries)
| Retriever | hit@1 | hit@3 | hit@5 |
|-------------------------------|--------|--------|--------|
| Hybrid (pgvector + full-text) | **87%** | **87%** | **87%** |
| Full-text only (baseline) | 33% | 33% | 33% |
Hybrid retrieval is **2.6× better than full-text alone**. Full-text handles exact keyword matches (100%) but is blind to paraphrase and semantic queries — those score 0% without vectors.
→ Full methodology, ASCII bar chart, and raw data: [`benchmarks/RESULTS.md`](./benchmarks/RESULTS.md)
## Features
- 🧠 **Two memory kinds** — structured **facts** (deterministic key/value, exact SQL) and semantic **memories** (free text, vector similarity).
- 🔍 **Hybrid retrieval** — pgvector cosine **+** Postgres full-text, fused with Reciprocal Rank Fusion. Falls back to pure full-text in "lite mode".
- 🆓 **Free local embeddings by default** — `fastembed` (bge-small, 384d) on CPU. Swap in OpenAI, or run embedding-free with the `noop` embedder.
- 🏢 **Multi-tenant with Row-Level Security** — personal + workspace scopes, isolated at the database level. Safe for teams.
- 🔌 **First-class MCP** — exposes `remember` / `recall` tools so Claude Code & Cursor save and recall context *by themselves*.
- 🧹 **Self-maintaining** — `pg_cron` decays stale memories inside the database. Conservative by default (softens, never deletes); aggressive delete is opt-in.
- 🗑️ **No raw transcripts** — only distilled facts and memories are stored. Garbage in, garbage out — so we don't store garbage.
- 🐘 **One source of truth** — Postgres. CLI, MCP, and scheduled jobs all read the same database; ACID handles the conflicts.
## Architecture
```
CLI ─┐ ┌──────── memory-daemon ────────┐
├── localhost HTTP ───────┤ embedder (loaded once) │ Postgres
MCP ─┘ │ hybrid retrieval (vec + FTS) ├──── + pgvector
│ async extraction │ + pg_cron
└────────────────────────────────┘
```
A single long-running **daemon** loads the embedding model once (no cold-start) and owns the one connection pool. The **CLI** and **MCP server** are thin clients that talk to it over localhost. Postgres does the rest — vector search, full-text, and maintenance — in one engine.
## Install
```bash
npm i -g @dolores/cli # global CLI
```
Or run from source (see below).
## Quick start
**Requirements:** Node ≥ 20, pnpm, Docker.
```bash
git clone https://github.com/yeneryigitcelik-debug/dolores.git
cd dolores
pnpm install
cp .env.example .env # tweak if you like
pnpm db:up # Postgres + pgvector + pg_cron
pnpm build
dolores init # extensions, schema, RLS, decay job
dolores remember "We deploy production on Hetzner with Coolify." --scope workspace
dolores recall "where is production hosted?"
dolores context # minimal-token memory blob for a system prompt
```
> The model downloads once on first use (~CPU-friendly bge-small). After that, recall is local and instant.
## CLI
```bash
dolores init # DB setup: extensions, migration, RLS, pg_cron
dolores remember "<text>" # add a memory (--scope, --importance, --source)
dolores recall "<query>" # hybrid vector + full-text search
dolores context # minimal context blob to inject into a system prompt
dolores ingest <file|stdin> # distill facts + memories from a conversation (async)
dolores facts [--category …] # list structured facts
dolores prune [--dry-run] # manual cleanup
dolores status # daemon + DB health, counts, estimated token savings
```
`dolores context` is the killer command: run it when an agent starts and pipe its output into the system prompt. The agent learns "who it is" for minimal tokens.
## MCP (Claude Code / Cursor)
Add the server and the agent gains two tools — it saves decisions with `remember` and pulls relevant history with `recall`, with no user action:
```jsonc
// Claude Code mcp config
{
"mcpServers": {
"dolores": {
"command": "node",
"args": ["/abs/path/to/dolores/packages/mcp/dist/index.js"],
"env": {
"DOLORES_WORKSPACE_ID": "00000000-0000-0000-0000-000000000001",
"DOLORES_DAEMON_PORT": "4505"
}
}
}
}
```
Now memory is not a passive store — it's a tool the agent actively uses across sessions.
## How it works
| | Structural (`facts`) | Semantic (`memories`) |
|---|---|---|
| **Stored as** | key/value | free text + 384d embedding + tsvector |
| **Retrieved by** | exact SQL, no embedding | pgvector cosine + full-text (RRF) |
| **Conflict resolution** | `ON CONFLICT` upsert (last writer wins) | supersede on >0.9 cosine similarity |
| **Example** | `stack/db = Postgres + pgvector` | "migration ordering bug fixed last deploy" |
**Isolation.** Every row carries `workspace_id` (+ optional `user_id`) and is guarded by Postgres Row-Level Security. The daemon connects as a non-superuser and sets the tenant per transaction, so cross-tenant reads are impossible — not just discouraged.
**Decay.** A daily `pg_cron` job softens the importance of stale, un-recalled memories — like human memory. Deletion is **off by default** (`DOLORES_DECAY_MODE=conservative`); the aggressive delete policy is opt-in.
## Where it fits
Mem0, Letta (ex-MemGPT), and Zep all do Postgres/vector agent memory — the concept is proven. dolores's niche: **self-hosted, free local embeddings, MCP-native, no raw-transcript storage, KVKK/GDPR-clean.** Your subscription, your database, nothing else.
## Tech stack
TypeScript (strict) · Node ESM · pnpm monorepo · Prisma + raw SQL for pgvector · `fastembed` · `fastify` · `commander` · `@modelcontextprotocol/sdk` · `zod` · Docker Compose.
| Package | Responsibility |
|---|---|
| `@dolores/db` | Prisma schema, raw-SQL migration (pgvector + pg_cron), Dockerfile, RLS, `withTenant` |
| `@dolores/core` | embedder abstraction · hybrid retrieval · extraction (owns the shared contracts) |
| `@dolores/daemon` | loads the embedder once, owns the pool, serves localhost HTTP |
| `@dolores/cli` | `commander`-based thin client |
| `@dolores/mcp` | MCP server (`remember` / `recall`) |
## Examples
Runnable integration examples (MCP wiring, context injection, Node.js SDK): **[`examples/`](./examples/README.md)**
## Development
```bash
pnpm install
pnpm build # build every package
pnpm test # run every package's tests
pnpm lint # biome
pnpm db:up # start Postgres locally
```
Architecture decisions and their *why* live in [`MEMORY.md`](./MEMORY.md); repo working rules are in [`CLAUDE.md`](./CLAUDE.md).
→ Backup / restore, decay modes, daemon env vars, and production hardening: [`docs/OPERATIONS.md`](./docs/OPERATIONS.md).
## Contributing
Issues and PRs are welcome. Keep the architectural invariants intact: no raw transcripts, embeddings behind the `Embedder` interface, the LLM off the critical path, and Postgres as the single source of truth. See [`CLAUDE.md`](./CLAUDE.md).
## License
[MIT](./LICENSE) © 2026 Yener Yiğit Çelik
This server cannot be deployed
Maintenance
ActivityStale
ResponsivenessNo issues