recallite
v0.0.3
Published
SQLite memory for agents that learn from outcomes.
Maintainers
Readme
Recallite 🧠⚡
SQLite memory for agents that learn from outcomes.
Recallite gives Node.js AI agents, automations, and self-improving applications persistent memory, hybrid retrieval, and outcome-aware learning in a single SQLite database.
What Recallite Is
Recallite is an opinionated, lightweight memory layer for Node.js AI agents built on top of SQLite (FTS5 full-text search + Float32 BLOB vector storage).
Unlike basic vector stores that only perform static semantic similarity searches, Recallite tracks the real-world outcomes of past agent actions and reinforces or weakens memory usefulness over time:
$$\text{Remember} \longrightarrow \text{Retrieve} \longrightarrow \text{Act} \longrightarrow \text{Observe Outcome} \longrightarrow \text{Reinforce / Weaken} \longrightarrow \text{Recall Better Next Time}$$
- Vector Storage & Engine: Float32 vectors are stored in SQLite BLOB columns and searched using Recallite's exact cosine-similarity engine (
InlineScalarEngineorWorkerBlobScanEngine).
What Recallite Is Not
[!IMPORTANT] Recallite does not retrain language models. It improves future agent decision-making by dynamically updating memory confidence, usefulness scores, lifecycle states, and hybrid retrieval ranking signals.
🎯 Which API Mode Should You Use?
Recallite supports both a Lite API for fast, simple SQLite memory and an Outcome-Aware API for complex AI agents:
| Goal / Scenario | Recommended Mode | Key Methods | Runnable Example |
| :--- | :--- | :--- | :--- |
| Personal AI Assistant / Chatbot | Lite API | createRecallite(), add(), context() | npm run example:lite |
| Self-Improving Support / Coding Bot | Outcome API | remember(), recall(), feedback() | npm run example:outcome |
| Policy Enforcement & Safety Rules | Safety API | type: 'constraint', relate('contradicts') | npm run example:safety |
| Autonomous Admin Agent / Ops | Full Agent API | defineProfile(), consolidate(), explainRecall() | npm run example:admin |
| Claude / Antigravity / Cursor IDE | MCP Server | npx recallite-mcp --db ./memory.db | npm run mcp |
⚡ Quick Start: Lite API (5 Seconds)
For chatbots, personal assistants, or CLI tools where you just want a clean SQLite memory store without boilerplate:
import { createRecallite } from 'recallite';
// 1. Connect in 1 line (pass DB path or omit for in-memory SQLite)
const memory = await createRecallite('./my-bot.db');
// 2. Add memories with plain text strings
await memory.add("User prefers dark mode UI and concise Markdown explanations.");
await memory.add("User works as a Senior Software Engineer specializing in Node.js.");
// 3. Get ready-to-inject LLM prompt context in 1 line
const promptContext = await memory.context("User preferences");
console.log(promptContext);
// Output:
// [OBSERVATION] User prefers dark mode UI and concise Markdown explanations.
// User prefers dark mode UI and concise Markdown explanations.🔌 Model Context Protocol (MCP) Server
Recallite includes a zero-dependency, stdio-based MCP Server (recallite-mcp) allowing AI tools (Claude Desktop, Antigravity IDE, Cursor, Windsurf, etc.) to plug into Recallite as an external persistent memory engine.
Quick Launch via CLI
# Run MCP server against local SQLite database
npx recallite-mcp --db ./agent-memory.dbIDE / Claude Desktop Configuration
Add the following to your MCP client config (e.g. claude_desktop_config.json or Antigravity MCP settings):
{
"mcpServers": {
"recallite": {
"command": "npx",
"args": ["recallite-mcp", "--db", "./agent-memory.db"]
}
}
}Exposed MCP Tools
| Tool Name | Description | Key Arguments |
| :--- | :--- | :--- |
| recallite_remember | Store a new memory into SQLite | content, title, type, metadata |
| recallite_recall | Perform hybrid keyword + vector search | query, limit, profile |
| recallite_context | Get ready-to-inject LLM prompt context | query, format (markdown/compact) |
| recallite_feedback | Submit outcome feedback (success/failure) | retrievalId, outcome, score, usedMemoryIds |
| recallite_stats | Retrieve system health, memory metrics & prompt optimization token savings | format (summary/json) |
| recallite_explain | Inspect RRF score breakdown for a recall session | retrievalId |
🔄 MCP Agent Lifecycle (Kullanım Döngüsü)
When integrating Recallite MCP Server into AI agent workflows (Cursor, Claude, Antigravity, AutoGPT), agents follow a 4-stage autonomous memory loop:
graph TD
A[1. Task Starts] --> B[2. Auto-Recall: call recallite_context]
B --> C[3. Agent Executes Task with Context]
C --> D{New Rule / Solution Learned?}
D -- Yes --> E[4. Auto-Remember: call recallite_remember]
D -- No --> F[5. Task Finish]
E --> F
F --> G[6. Outcome Feedback: call recallite_feedback]- Context Retrieval (
recallite_context): At task initialization, the agent retrieves relevant rules, constraints, and past bug fixes formatted into a token-budgeted prompt context. - Task Execution: The agent performs code generation or planning adhering to retrieved constraints.
- Memory Capture (
recallite_remember): If a new rule, user preference, or bug solution is discovered, the agent automatically persists it into SQLite. - Outcome Learning (
recallite_feedback): The agent submits execution feedback (successorfailure), dynamically boosting or weakening memory scores for future sessions.
📋 Example .agents / System Prompt Configurations
To make your AI agent automatically use Recallite MCP without manual prompting, add one of the following prompt snippets to your project:
Option 1: .agents/AGENTS.md (or Cursor .cursorrules / Antigravity)
Create a file at .agents/AGENTS.md (see template in examples/mcp-agent-rules.md):
# Persistent Memory Rules (Recallite MCP)
You are connected to the Recallite MCP memory server. Follow these rules for all tasks:
## 1. Task Initialization (Auto-Recall)
- **Rule**: At the start of any non-trivial task, always execute `recallite_context` with your task query.
- **Goal**: Inject relevant user preferences, coding guidelines, and past bug fixes into your working context before writing code.
## 2. Automatic Knowledge Capture (Auto-Remember)
- **Rule**: Automatically call `recallite_remember` whenever you discover or create:
- User Preferences -> `type: "observation"`
- Hard Constraints & Safety Rules -> `type: "constraint"`
- Bug Fixes & Lessons -> `type: "lesson"`
- Reusable Procedures -> `type: "procedure"`
## 3. Outcome Feedback (Auto-Feedback)
- **Rule**: After completing a task or when a test/build fails, invoke `recallite_feedback`:
- Success: `outcome: "success"`, `score: 0.9` -> Reinforces helpful memories.
- Failure: `outcome: "failure"`, `score: 0.1` -> Weakens incorrect or outdated memories.Option 2: Claude Desktop / System Prompt Snippet
You have access to the Recallite MCP server tools:
- recallite_context: Call this FIRST at the start of any conversation to retrieve user preferences and past project lessons.
- recallite_remember: Call this whenever you learn a new preference, constraint, or solution during the chat.
- recallite_feedback: Call this to reinforce memories when a task succeeds or fails.
- recallite_stats: Call this to check memory health and prompt token savings metrics.🚀 Advanced Quick Start: Outcome-Aware AI Agent
For autonomous agents that generate solutions and need to learn from real-world execution results (success vs. failure):
import { createRecallite } from 'recallite';
// 1. Initialize Recallite with embedding function
const recallite = await createRecallite({
database: './agent-memory.db',
dimensions: 1536,
embed: async (texts) => await myEmbeddingProvider.embed(texts),
});
// 2. Remember an episode or solution
const episode = await recallite.remember({
type: 'episode',
title: 'Public Guestbook Spam Fix',
content: 'Public guestbook feature received high spam and was reverted after 3 days.',
source: { type: 'system-event', id: 'deployment-42' },
metadata: { category: 'moderation', outcome: 'reverted' },
});
// 3. Recall relevant memories for a new task
const result = await recallite.recall('Add user public comments', {
profile: 'planner',
types: ['lesson', 'procedure', 'episode'],
limit: 5,
});
// 4. Format recalled memories directly into LLM context snippet
const contextPrompt = recallite.formatContext(result, {
maxTokens: 2000,
format: 'markdown',
});
// 5. Outcome feedback with explicit memory attribution
// Real-world execution succeeded -> Boost memory usefulness score!
await recallite.feedback(result.retrievalId, {
outcome: 'success',
score: 0.9,
usedMemoryIds: [result.memories[0].memory.id],
reason: 'Prevented repeated security issue by enforcing moderation',
});How Retrieval & Scoring Work
Recallite uses Reciprocal Rank Fusion (RRF) to combine FTS5 full-text search, Float32 BLOB vector similarity, and metadata filters.
RRF Formula
$$RRFScore = \frac{w_{fts}}{60 + Rank_{fts}} + \frac{w_{vec}}{60 + Rank_{vec}}$$
If a memory is only matched in FTS or only in Vector search, the un-matched channel contribution is $0$.
Final Scoring Formula
To ensure brand-new memories (with default 0.5 confidence and 0.5 usefulness) get retrieved, tested, and reinforced without collapsing to zero, Recallite uses an additive quality multiplier:
$$QualityMultiplier = 0.45 + 0.25 \times Confidence + 0.20 \times Usefulness + 0.10 \times Importance$$
$$FinalScore = RRFScore \times StatusMultiplier \times ProfileMultiplier \times QualityMultiplier \times ContradictionMultiplier$$
Memory Reliability & Metrics
Recallite does not trust generated memories automatically.
usefulnessScore: Updated dynamically via outcome feedback (reinforce/feedback).confidence: Updated when evidence is attached, validation occurs (validate), or contradiction state changes.evidenceCount: Derived automatically from uniquesupportsandderived_fromrelations.contradictionCount: Derived automatically from activecontradictsrelations.
[!NOTE] Confidence and relevance are separate concepts. A memory may be highly reliable but irrelevant to the current task. Feedback events never automatically alter a memory's lifecycle status.
Memory Lifecycle & Source Trust Policy
flowchart TD
Candidate["candidate (LLM Generated / Draft)"]
Validated["validated (Verified / Approved)"]
Active["active (Participating in Default Retrieval)"]
Deprecated["deprecated (Soft Deleted / Excluded)"]
Superseded["superseded (Replaced by Newer Memory)"]
Candidate -->|validate| Validated
Candidate -->|deprecate| Deprecated
Validated -->|activate| Active
Active -->|deprecate| Deprecated
Active -->|supersede| SupersededSource Trust Level Policy
Memory lifecycle status is determined by evaluating both memory type and source trust level:
| Source Trust Level | Memory Types | Default Status | Rationale |
| :--- | :--- | :--- | :--- |
| Trusted Sourceshuman, system, system-event, system-metric | episode, observation, constraint, procedure | active | Ground-truth facts, metrics, human rules, and real execution events. |
| Trusted Sources | lesson, reflection | candidate | Requires validation before participating in default active retrieval. |
| Untrusted Sourcesllm, imported, unknown | episode, observation, lesson, procedure, reflection, constraint | candidate | Generated/unverified items are held in draft state (candidate) until validated. |
| All Sources | exemplar | validated | Pre-approved gold-standard benchmark examples. |
Safety Guard for Status Overrides
If an untrusted source (llm or imported) attempts to force status: 'active', Recallite safety guards force the status to 'candidate' unless explicitly overridden with allowUnsafeStatusOverride: true:
// Safely defaults to 'candidate' status
await recallite.remember({
type: 'constraint',
title: 'No CAPTCHA on checkout',
content: 'CAPTCHA drops conversion',
source: { type: 'llm' }, // Untrusted LLM source -> candidate status
});
// Explicit unsafe override (requires permission flag)
await recallite.remember({
type: 'constraint',
title: 'Force active rule',
content: 'Direct rule enforcement',
source: { type: 'llm' },
status: 'active',
allowUnsafeStatusOverride: true, // Express permission required
});Outcome Feedback & Contradiction Warnings
Contradiction Expansion & Warnings
Contradiction expansion does not consume the primary result quota. When a recalled memory conflicts with another memory via a contradicts relation, Recallite attaches a warning while preserving your primary result count limit:
const result = await recallite.recall("Animations UI optimization");
console.log(result.memories[0]);
/*
{
memory: { title: "Animations boost user engagement", ... },
score: 0.78,
reasons: ["semantic-match", "validated-memory"],
warnings: [
{
type: "contradiction",
memoryId: "mem_42",
summary: "Contradicts memory \"Animations increase mobile CPU usage\""
}
]
}
*/Contradictions Rendered in LLM Context
When using formatContext(), contradiction warnings are rendered directly into the prompt string so LLMs receive explicit warning signals:
[LESSON] Animations boost user engagement (85% conf)
Content: Animations increase user retention by 15%.
⚠️ Contradiction: Contradicts memory "Animations increase mobile CPU usage"API at a Glance
| Method | Purpose |
| :--- | :--- |
| remember(input) | Store a new memory with source metadata |
| rememberMany(inputs) | Bulk insert memories in a single SQLite transaction |
| recall(query, options) | Retrieve RRF-ranked memories returning RecallResponse (supports signal for AbortSignal) |
| formatContext(response, options) | Format a recall response directly into prompt context |
| feedback(retrievalId, options) | Record outcome feedback with explicit memory attribution |
| reinforce(memoryId, options) | Update score for a single specific memory directly |
| relate(sourceId, targetId, options) | Create evidence (supports) or contradicts links |
| consolidate(options) | Group experiences & synthesize candidate lessons |
| validate(memoryId) | Mark a candidate memory as verified |
| activate(memoryId) | Include a validated memory in default active retrieval |
| supersede(oldId, newId) | Atomically replace an old memory with a new rule |
| explainRecall(retrievalId) | Debug tool returning detailed selection reasons and vector execution info |
| health() | Inspect SQLite integrity, FTS sync, and vector dimensions |
| prune(options) | Clean up old deprecated memories safely (dryRun supported) |
| close(options) | Gracefully drain worker pool (drainTimeout: 5000) and close SQLite connection cleanly |
Performance, Workers & Concurrency Model
For high-throughput AI agents and server backends, Recallite decouples single-threaded SQLite write transactions on the main application thread from CPU-intensive Float32 vector scans using a dedicated Worker Thread Pool.
sequenceDiagram
autonumber
actor Agent as AI Agent / Orchestrator
participant Main as Main Thread (Recallite)
participant Embed as Application Embedding API
participant Worker as Worker Thread Pool (WorkerBlobScanEngine)
participant DB as SQLite DB (WAL Mode)
Agent->>Main: recall("Add user comments", { profile: "planner", signal })
Main->>Embed: embed(["Add user comments"])
Embed-->>Main: Return Query Vector [Float32Array]
Main->>DB: Execute FTS5 Keyword Search (bm25)
DB-->>Main: Return FTS Candidate Ranks
alt Total Vectors >= minVectorCount OR Scan Ops >= minScanOperations
Main->>Worker: Dispatch vector-search task (queryVector, dbPath)
Worker->>DB: Open Read-Only DB Handle (query_only = ON) & Scan Float32 BLOBs
DB-->>Worker: Return Raw BLOB Rows
Worker-->>Main: Return Top-50 Memory IDs + Cosine Scores
else Small Vector Store (Inline Execution)
Main->>DB: Execute InlineScalarEngine (cosine_similarity C Function)
DB-->>Main: Return Top-50 Ranks
end
Main->>Main: Compute RRF Fusion, Status & Contradiction Multipliers
Main-->>Agent: Return Ranked Memories & Contradiction WarningsWorker Concurrency Model
- Main Thread Write Ownership: The main application thread retains exclusive ownership of all SQLite write operations (
INSERT,UPDATE,DELETE, lifecycle transitions, relations, and migrations). - Independent Read Connections: Each worker thread opens its own separate read-only connection (
readonly: true,query_only = ON,busy_timeout = 5000) to a database already configured in WAL mode by the main connection. - Task-Level Parallelism: Worker pool offloads full
recallvector scans for concurrent requests across workers. Individual vector queries are not split across worker shards. - Scan Concurrency Control:
maxConcurrentVectorScans(default: 2) prevents excessive simultaneous BLOB scans from overwhelming CPU bandwidth. - Non-Blocking WAL Reads: SQLite WAL mode permits worker read scans to run concurrently while the main thread executes short write transactions.
Worker Configuration & Capping
Worker options can be configured as a boolean, 'auto' string, or detailed options object:
const recallite = await createRecallite({
database: './memory.db',
workers: {
enabled: true,
size: 'auto', // Capped: min(max(1, availableParallelism - 1), 4)
minVectorCount: 2000, // Offload threshold by vector count
minScanOperations: 1000000, // Offload threshold by estimated operations (count * dimensions)
maxConcurrentVectorScans: 2, // Scan concurrency semaphore
taskTimeout: 10000, // 10s task execution timeout
maxQueueSize: 100, // Queue capacity limit
fallback: 'error', // 'error' (default) throws RECALLITE_WORKER_TIMEOUT; 'inline' enables main thread fallback
},
});[!IMPORTANT]
size: "auto"is capped to a maximum of 4 workers (Math.min(Math.max(1, availableParallelism() - 1), 4)) to prevent excessive parallel read scans against the same SQLite database file.
Worker Failure Behavior & Cancellation
Worker mode prevents CPU-heavy exact vector scans from blocking the Node.js event loop and improves responsiveness under concurrent workloads.
- Task Timeout: Tasks exceeding
taskTimeoutfail withRECALLITE_WORKER_TIMEOUT. Main-thread inline fallback is disabled by default (fallback: 'error') to prevent event-loop freezing after timeouts. - Queue Overflow: Requests exceeding
maxQueueSizefail withRECALLITE_WORKER_QUEUE_FULL. - Query Cancellation: Passing
signal: AbortSignaltorecall()immediately cancels queued or active worker scans and rejects withRECALLITE_ABORTED. - Graceful Teardown:
recallite.close({ drainTimeout: 5000 })drains pending active tasks before terminating worker pool threads.
📚 Runnable Scenario Examples
Recallite includes 4 complete, self-contained, real-world scenario examples in examples/:
1. Lite API Mode (Personal AI Chatbot Memory)
Demonstrates 1-line setup, plain text string memory addition, and 1-line LLM prompt context injection.
npm run example:lite2. Outcome-Aware Agent Mode (Self-Improving Support Bot)
Demonstrates recording troubleshooting solutions, calling feedback() on real-world outcomes, and observing how successful fixes get automatically boosted for future queries.
npm run example:outcome3. Policy & Safety Enforcement Mode (Contradiction Warnings)
Demonstrates enforcing system constraints, linking conflicting legacy vs. updated rules via relate('contradicts'), and detecting contradiction warnings during retrieval.
npm run example:safety4. Production Autonomous Admin Agent (Full Architecture)
Demonstrates a production-grade Marketplace Moderation & Operations Admin Agent with two-stage retrieval, precise attribution, multi-factor decision logic, and experience consolidation (consolidate()).
# Run in Inline Engine Mode
npm run example:admin
# Run in Multi-Core Worker Pool Mode
npm run example:admin -- --workerPerformance Benchmarks
Run benchmarks anytime using npm run bench:
📌 Benchmark Environment & Hardware Context:
- Hardware: Apple Silicon (M-series Mac)
- Node.js: v23.1.0 | SQLite Driver: better-sqlite3
- Vector Storage: Float32 BLOB columns in SQLite
- Vector Engine: Recallite exact cosine-similarity engine (Float32 BLOB scalar calculation)
- Search Mode: Exact k-NN Search (O(N) linear scan)
- Cache Mode: Warm Cache (SQLite WAL + MMAP enabled)
- Worker Pool: Enabled (Pool size: 2, minVectorCount: 2000, minScanOperations: 1,000,000)
- Benchmark Vector Dimensions: 128
==================================================================================================================================
| Operation | Count | Total Time | Batch Throughput | Item Throughput | Avg Latency | p95 Latency |
==================================================================================================================================
| remember (single item insert) | 50 ops | 108.81 ms | 459.5 batch/s | 459.5 items/s | 2.176 ms/op | 9.288 ms p95 |
| rememberMany (100 items/batch) | 10 ops | 302.40 ms | 33.1 batch/s | 3306.8 items/s | 30.240 ms/op | 70.451 ms p95 |
| recall @ 10,000 memories (InlineScalarEngine) | 10 ops | 2443.25 ms | 4.1 batch/s | 4.1 items/s | 244.325 ms/op | 421.661 ms p95 |
| recall @ 10,000 memories (WorkerBlobScanEngine) | 10 ops | 1732.37 ms | 5.8 batch/s | 5.8 items/s | 173.237 ms/op | 327.841 ms p95 |
| recall @ 10,000 memories (10 Concurrent Requests) | 2 ops | 3162.90 ms | 0.6 batch/s | 6.3 items/s | 1581.449 ms/op | 1639.192 ms p95 |
| formatContext (Markdown format) | 100 ops | 2.35 ms | 42630.3 batch/s | 42630.3 items/s | 0.023 ms/op | 0.055 ms p95 |
| reinforce (score update) | 300 ops | 1292.22 ms | 232.2 batch/s | 232.2 items/s | 4.307 ms/op | 7.826 ms p95 |
==================================================================================================================================Project Status
| Feature / Subsystem | Status | Description | | :--- | :--- | :--- | | SQLite Persistence & WAL | 🟢 Stable | Production ready with parameterized security. | | FTS5 Full-Text Retrieval | 🟢 Stable | BM25 keyword matching with sanitization. | | Lifecycle & Status Tools | 🟢 Stable | Candidate, Validated, Active, Deprecated, Superseded. | | Outcome Reinforcement | 🟢 Stable | Non-linear EMA score updates and attribution. | | Worker Thread Vector Engine | 🟢 Stable | Off-main-thread vector scans maintaining event-loop responsiveness under load. | | Exact Vector Search Engine | 🟢 Stable (Small/Medium Stores) | Exact $O(N)$ linear scan benchmarked at ~115ms for 3,000 memories with Worker Pool. Practical up to tens of thousands of memories depending on hardware and latency tolerance. | | Automated LLM Consolidation | 🟡 Beta | Requires external LLM callback & candidate validation. |
Limitations
- Recallite does not retrain language models.
- The default vector engine performs exact search ($O(N)$) and is intended for small-to-medium local memory collections. Large-scale collections (>50,000 memories) may require a custom approximate vector-search adapter.
- Retrieval quality depends on the embedding provider.
- Automatic consolidation requires validation.
- SQLite is best suited for local and single-node deployments.
- Semantic similarity does not guarantee factual correctness.
📜 License
MIT License © 2026 Ahmet & Recallite Contributors.
