@plures/superlocalmemory-mcp 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,228 @@
1
+ # @plures/superlocalmemory-mcp
2
+
3
+ MCP (Model Context Protocol) server for **superlocalmemory** — a **local-first, persistent vector memory** for AI coding assistants.
4
+
5
+ It exposes a small set of MCP **tools** and **resources** so editors like VS Code (Copilot MCP), Cursor, Continue, and Claude Desktop can store and recall long-term memory during coding sessions.
6
+
7
+ - Storage: local SQLite file (better-sqlite3)
8
+ - Retrieval: semantic vector search
9
+ - Embeddings: **Transformers.js (default, zero-config)** or OpenAI (optional)
10
+
11
+ ## ✨ Zero-Config Usage
12
+
13
+ **No API keys required!** Just run:
14
+
15
+ ```bash
16
+ npx @plures/superlocalmemory-mcp
17
+ ```
18
+
19
+ The server uses **Transformers.js** to run embeddings locally in-process with the `bge-small-en-v1.5` model (384 dimensions).
20
+
21
+ > **Note:** First run will download the model (~100MB) to `~/.cache/superlocalmemory/transformers`. Subsequent runs are instant and fully offline.
22
+
23
+ ## Install
24
+
25
+ ```bash
26
+ # Run directly (recommended):
27
+ npx @plures/superlocalmemory-mcp
28
+
29
+ # Or install globally:
30
+ npm install -g @plures/superlocalmemory-mcp
31
+ ```
32
+
33
+ ## Configuration
34
+
35
+ All configuration is **optional**:
36
+
37
+ ### Environment Variables
38
+
39
+ - `SUPERLOCALMEMORY_DB_PATH` (optional) — SQLite DB path (default: `~/.superlocalmemory/mcp.db`)
40
+ - `SUPERLOCALMEMORY_DEBUG` (optional) — set to `true` for debug logs to stderr
41
+ - `SUPERLOCALMEMORY_CACHE_DIR` (optional) — Transformers.js model cache directory (default: `~/.cache/superlocalmemory/transformers`)
42
+ - `OPENAI_API_KEY` (optional) — use OpenAI embeddings instead of local Transformers.js
43
+ - `OPENAI_EMBEDDING_MODEL` (optional) — OpenAI model to use (default: `text-embedding-3-small`)
44
+
45
+ ### Using OpenAI (Optional)
46
+
47
+ If you prefer OpenAI embeddings over local Transformers.js:
48
+
49
+ 1. Set `OPENAI_API_KEY` in your environment
50
+ 2. The server will use OpenAI with 1536-dim embeddings
51
+
52
+ ## Migration / Breaking Changes
53
+
54
+ > **Important:** Embedding dimensions are **not** interchangeable. Transformers.js uses **384‑dim** embeddings, while OpenAI uses **1536‑dim** embeddings by default.
55
+
56
+ If you already have an existing database:
57
+
58
+ - **Databases created with OpenAI embeddings must continue using `OPENAI_API_KEY`.**
59
+ - Do **not** switch that database to Transformers.js; the stored vectors will be incompatible.
60
+ - The server will detect dimension mismatches and provide a clear error message.
61
+ - There is **no automatic migration** between 384‑dim and 1536‑dim embeddings.
62
+ - To change providers (OpenAI ⇄ Transformers.js), you must either:
63
+ - Point `SUPERLOCALMEMORY_DB_PATH` to a **new database**, or
64
+ - Delete/recreate the existing DB and **re-index all memories**.
65
+ ## Editor setup
66
+
67
+ ### Zero-Config Examples
68
+
69
+ These examples require **no environment variables** and work out of the box:
70
+
71
+ #### VS Code (Copilot) — `mcp.json`
72
+
73
+ ```json
74
+ {
75
+ "mcpServers": {
76
+ "superlocalmemory": {
77
+ "command": "npx",
78
+ "args": ["@plures/superlocalmemory-mcp"]
79
+ }
80
+ }
81
+ }
82
+ ```
83
+
84
+ #### Cursor — `settings.json`
85
+
86
+ ```json
87
+ {
88
+ "mcpServers": {
89
+ "superlocalmemory": {
90
+ "command": "npx",
91
+ "args": ["@plures/superlocalmemory-mcp"]
92
+ }
93
+ }
94
+ }
95
+ ```
96
+
97
+ #### Continue.dev — `config.json`
98
+
99
+ ```json
100
+ {
101
+ "mcpServers": [
102
+ {
103
+ "name": "superlocalmemory",
104
+ "command": "npx",
105
+ "args": ["@plures/superlocalmemory-mcp"]
106
+ }
107
+ ]
108
+ }
109
+ ```
110
+
111
+ #### Claude Desktop — `claude_desktop_config.json`
112
+
113
+ ```json
114
+ {
115
+ "mcpServers": {
116
+ "superlocalmemory": {
117
+ "command": "npx",
118
+ "args": ["@plures/superlocalmemory-mcp"]
119
+ }
120
+ }
121
+ }
122
+ ```
123
+
124
+ ### With OpenAI (Optional)
125
+
126
+ If you prefer OpenAI embeddings:
127
+
128
+ ```json
129
+ {
130
+ "mcpServers": {
131
+ "superlocalmemory": {
132
+ "command": "npx",
133
+ "args": ["@plures/superlocalmemory-mcp"],
134
+ "env": {
135
+ "OPENAI_API_KEY": "your-key"
136
+ }
137
+ }
138
+ }
139
+ }
140
+ ```
141
+
142
+ ## Tools
143
+
144
+ ### `memory_store`
145
+ Store a memory.
146
+
147
+ **Input**
148
+ - `content` (string, required)
149
+ - `tags` (string[], optional)
150
+ - `category` (string, optional)
151
+ - `source` (string, optional)
152
+
153
+ ### `memory_search`
154
+ Semantic search.
155
+
156
+ **Input**
157
+ - `query` (string, required)
158
+ - `limit` (number, optional, default 5)
159
+ - `minScore` (number, optional, default 0.3)
160
+
161
+ ### `memory_forget`
162
+ Delete by UUID `id` or by semantic `query`.
163
+
164
+ **Input**
165
+ - `id` (string, optional)
166
+ - `query` (string, optional)
167
+ - `threshold` (number, optional, default 0.8)
168
+
169
+ ### `memory_profile`
170
+ Return the stored user profile summary (if any).
171
+
172
+ ### `memory_index`
173
+ Index a directory by storing file contents as memories.
174
+
175
+ **Input**
176
+ - `directory` (string, required)
177
+ - `maxFiles` (number, optional, default 500)
178
+ - `maxBytesPerFile` (number, optional, default 200000)
179
+ - `category` (string, optional, default `project-context`)
180
+ - `tags` (string[], optional)
181
+
182
+ ### `memory_stats`
183
+ Return database stats.
184
+
185
+ ## Resources
186
+
187
+ - `memory://profile` — JSON user profile (if available)
188
+ - `memory://recent` — markdown list of the 20 most recent memory contents
189
+ - `memory://stats` — JSON stats
190
+
191
+ ## How it works
192
+
193
+ This server provides local-first persistent memory with:
194
+
195
+ - **Local SQLite database** stores memory rows with embeddings
196
+ - **Embeddings**:
197
+ - Default: Transformers.js (`bge-small-en-v1.5`, 384-dim) — runs in-process, zero-config
198
+ - Optional: OpenAI API (`text-embedding-3-small`, 1536-dim) — requires API key
199
+ - **Vector search**: In-process cosine similarity against stored embeddings
200
+ - **Indexing**: `memory_index` walks a directory, reads text files, and stores them for later retrieval
201
+
202
+ ## Privacy
203
+
204
+ All memory data is stored **locally** on your machine at `SUPERLOCALMEMORY_DB_PATH` (default: `~/.superlocalmemory/mcp.db`).
205
+
206
+ ### Network Usage
207
+
208
+ - **Transformers.js (default)**: One-time model download (~100MB) on first run, then 100% offline
209
+ - **OpenAI (optional)**: API calls for each embedding when `OPENAI_API_KEY` is set
210
+
211
+ No memory content is ever sent to external services except for embedding generation when using OpenAI.
212
+
213
+ ## Development
214
+
215
+ ```bash
216
+ npm install
217
+ npm run dev
218
+ ```
219
+
220
+ Build:
221
+
222
+ ```bash
223
+ npm run build
224
+ ```
225
+
226
+ ## License
227
+
228
+ AGPL-3.0
@@ -0,0 +1,8 @@
1
+ export interface McpConfig {
2
+ dbPath: string;
3
+ openaiApiKey?: string;
4
+ openaiModel?: string;
5
+ debug: boolean;
6
+ }
7
+ export declare function loadConfig(env?: NodeJS.ProcessEnv): McpConfig;
8
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,SAAS;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,OAAO,CAAC;CAChB;AASD,wBAAgB,UAAU,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,SAAS,CAO1E"}
package/dist/config.js ADDED
@@ -0,0 +1,19 @@
1
+ import os from "node:os";
2
+ import path from "node:path";
3
+ function expandHome(p) {
4
+ if (!p)
5
+ return p;
6
+ if (p === "~")
7
+ return os.homedir();
8
+ if (p.startsWith("~/"))
9
+ return path.join(os.homedir(), p.slice(2));
10
+ return p;
11
+ }
12
+ export function loadConfig(env = process.env) {
13
+ const dbPath = expandHome(env.SUPERLOCALMEMORY_DB_PATH ?? "~/.superlocalmemory/mcp.db");
14
+ const openaiApiKey = env.OPENAI_API_KEY;
15
+ const openaiModel = env.OPENAI_EMBEDDING_MODEL;
16
+ const debug = (env.SUPERLOCALMEMORY_DEBUG ?? "").toLowerCase() === "true";
17
+ return { dbPath, openaiApiKey, openaiModel, debug };
18
+ }
19
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAS7B,SAAS,UAAU,CAAC,CAAS;IAC3B,IAAI,CAAC,CAAC;QAAE,OAAO,CAAC,CAAC;IACjB,IAAI,CAAC,KAAK,GAAG;QAAE,OAAO,EAAE,CAAC,OAAO,EAAE,CAAC;IACnC,IAAI,CAAC,CAAC,UAAU,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IACnE,OAAO,CAAC,CAAC;AACX,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,MAAyB,OAAO,CAAC,GAAG;IAC7D,MAAM,MAAM,GAAG,UAAU,CAAC,GAAG,CAAC,wBAAwB,IAAI,4BAA4B,CAAC,CAAC;IACxF,MAAM,YAAY,GAAG,GAAG,CAAC,cAAc,CAAC;IACxC,MAAM,WAAW,GAAG,GAAG,CAAC,sBAAsB,CAAC;IAC/C,MAAM,KAAK,GAAG,CAAC,GAAG,CAAC,sBAAsB,IAAI,EAAE,CAAC,CAAC,WAAW,EAAE,KAAK,MAAM,CAAC;IAE1E,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,WAAW,EAAE,KAAK,EAAE,CAAC;AACtD,CAAC"}
@@ -0,0 +1,95 @@
1
+ export interface MemoryEntry {
2
+ id: string;
3
+ content: string;
4
+ embedding: string;
5
+ tags: string[];
6
+ category?: string;
7
+ source: string;
8
+ created_at: number;
9
+ }
10
+ export interface StoreOptions {
11
+ tags?: string[];
12
+ category?: string;
13
+ source?: string;
14
+ dedupeThreshold?: number;
15
+ }
16
+ export interface StoreResult {
17
+ entry: MemoryEntry;
18
+ isDuplicate: boolean;
19
+ updatedId?: string;
20
+ }
21
+ export interface SearchResult {
22
+ entry: MemoryEntry;
23
+ score: number;
24
+ }
25
+ /**
26
+ * Local SQLite-based vector memory database.
27
+ * Supports storage, cosine similarity search, and CRUD operations.
28
+ */
29
+ export declare class MemoryDB {
30
+ private db;
31
+ private dimension;
32
+ constructor(dbPath: string, dimension: number);
33
+ private initTables;
34
+ /**
35
+ * Validate that existing embeddings in the database match the configured dimension.
36
+ * This prevents dimension mismatches when switching between embedding providers.
37
+ */
38
+ private validateDimension;
39
+ /**
40
+ * Calculate cosine similarity between two vectors
41
+ */
42
+ private cosineSimilarity;
43
+ /**
44
+ * Store a memory with deduplication
45
+ */
46
+ store(content: string, embedding: number[], options?: StoreOptions): Promise<StoreResult>;
47
+ /**
48
+ * Find a similar memory above threshold (for deduplication)
49
+ */
50
+ private findSimilar;
51
+ /**
52
+ * Vector search using cosine similarity.
53
+ *
54
+ * Performance note: This implementation loads all embeddings into memory and performs
55
+ * brute-force cosine similarity computation. For large databases (>10,000 memories),
56
+ * this may have performance implications. Future optimizations could include:
57
+ * - Approximate nearest neighbor search (ANN)
58
+ * - Vector indexing (e.g., HNSW, IVF)
59
+ * - Limiting search scope with filters
60
+ */
61
+ vectorSearch(queryEmbedding: number[], limit?: number, minScore?: number): Promise<SearchResult[]>;
62
+ /**
63
+ * Delete a memory by ID
64
+ */
65
+ delete(id: string): void;
66
+ /**
67
+ * Delete memories matching a query above threshold
68
+ */
69
+ deleteByQuery(queryEmbedding: number[], threshold?: number): Promise<number>;
70
+ /**
71
+ * Get user profile (stub - not implemented in core)
72
+ */
73
+ getProfile(): string | null;
74
+ /**
75
+ * Get all memory content (limited)
76
+ */
77
+ getAllContent(limit?: number): string[];
78
+ /**
79
+ * Get database statistics
80
+ */
81
+ stats(): {
82
+ totalMemories: number;
83
+ captureCount: number;
84
+ dimension: number;
85
+ };
86
+ /**
87
+ * Increment capture count
88
+ */
89
+ incrementCaptureCount(): void;
90
+ /**
91
+ * Close database connection
92
+ */
93
+ close(): void;
94
+ }
95
+ //# sourceMappingURL=memory.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"memory.d.ts","sourceRoot":"","sources":["../../src/db/memory.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,WAAW,CAAC;IACnB,WAAW,EAAE,OAAO,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,WAAW,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;;GAGG;AACH,qBAAa,QAAQ;IACnB,OAAO,CAAC,EAAE,CAAoB;IAC9B,OAAO,CAAC,SAAS,CAAS;gBAEd,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM;IAQ7C,OAAO,CAAC,UAAU;IAiClB;;;OAGG;IACH,OAAO,CAAC,iBAAiB;IA0BzB;;OAEG;IACH,OAAO,CAAC,gBAAgB;IAmBxB;;OAEG;IACG,KAAK,CACT,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,MAAM,EAAE,EACnB,OAAO,GAAE,YAAiB,GACzB,OAAO,CAAC,WAAW,CAAC;IAwEvB;;OAEG;YACW,WAAW;IAQzB;;;;;;;;;OASG;IACG,YAAY,CAChB,cAAc,EAAE,MAAM,EAAE,EACxB,KAAK,SAAI,EACT,QAAQ,SAAM,GACb,OAAO,CAAC,YAAY,EAAE,CAAC;IAkD1B;;OAEG;IACH,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI;IAIxB;;OAEG;IACG,aAAa,CACjB,cAAc,EAAE,MAAM,EAAE,EACxB,SAAS,SAAM,GACd,OAAO,CAAC,MAAM,CAAC;IAYlB;;OAEG;IACH,UAAU,IAAI,MAAM,GAAG,IAAI;IAM3B;;OAEG;IACH,aAAa,CAAC,KAAK,SAAK,GAAG,MAAM,EAAE;IAQnC;;OAEG;IACH,KAAK,IAAI;QACP,aAAa,EAAE,MAAM,CAAC;QACtB,YAAY,EAAE,MAAM,CAAC;QACrB,SAAS,EAAE,MAAM,CAAC;KACnB;IAkBD;;OAEG;IACH,qBAAqB,IAAI,IAAI;IAO7B;;OAEG;IACH,KAAK,IAAI,IAAI;CAGd"}
@@ -0,0 +1,245 @@
1
+ import Database from "better-sqlite3";
2
+ import { randomUUID } from "node:crypto";
3
+ /**
4
+ * Local SQLite-based vector memory database.
5
+ * Supports storage, cosine similarity search, and CRUD operations.
6
+ */
7
+ export class MemoryDB {
8
+ db;
9
+ dimension;
10
+ constructor(dbPath, dimension) {
11
+ this.dimension = dimension;
12
+ this.db = new Database(dbPath);
13
+ this.db.pragma("journal_mode = WAL");
14
+ this.initTables();
15
+ this.validateDimension();
16
+ }
17
+ initTables() {
18
+ // Create memories table
19
+ this.db.exec(`
20
+ CREATE TABLE IF NOT EXISTS memories (
21
+ id TEXT PRIMARY KEY,
22
+ content TEXT NOT NULL,
23
+ embedding TEXT NOT NULL,
24
+ tags TEXT,
25
+ category TEXT,
26
+ source TEXT,
27
+ created_at INTEGER NOT NULL
28
+ )
29
+ `);
30
+ // Create metadata table for stats
31
+ this.db.exec(`
32
+ CREATE TABLE IF NOT EXISTS metadata (
33
+ key TEXT PRIMARY KEY,
34
+ value TEXT NOT NULL
35
+ )
36
+ `);
37
+ // Initialize capture count if not exists
38
+ const stmt = this.db.prepare("SELECT value FROM metadata WHERE key = ?");
39
+ const row = stmt.get("capture_count");
40
+ if (!row) {
41
+ this.db.prepare("INSERT INTO metadata (key, value) VALUES (?, ?)").run("capture_count", "0");
42
+ }
43
+ }
44
+ /**
45
+ * Validate that existing embeddings in the database match the configured dimension.
46
+ * This prevents dimension mismatches when switching between embedding providers.
47
+ */
48
+ validateDimension() {
49
+ const stmt = this.db.prepare("SELECT embedding FROM memories LIMIT 1");
50
+ const row = stmt.get();
51
+ if (row) {
52
+ try {
53
+ const existingEmbedding = JSON.parse(row.embedding);
54
+ if (existingEmbedding.length !== this.dimension) {
55
+ throw new Error(`Database dimension mismatch: Database contains ${existingEmbedding.length}-dim embeddings, ` +
56
+ `but configured provider uses ${this.dimension}-dim embeddings. ` +
57
+ `To fix this, either:\n` +
58
+ ` 1. Set OPENAI_API_KEY to use OpenAI (1536-dim) if database was created with OpenAI\n` +
59
+ ` 2. Use a new database path with SUPERLOCALMEMORY_DB_PATH\n` +
60
+ ` 3. Delete the existing database to start fresh with ${this.dimension}-dim embeddings`);
61
+ }
62
+ }
63
+ catch (err) {
64
+ if (err instanceof Error && err.message.includes("Database dimension mismatch")) {
65
+ throw err;
66
+ }
67
+ // Ignore JSON parse errors for corrupted data - will be handled during search
68
+ }
69
+ }
70
+ }
71
+ /**
72
+ * Calculate cosine similarity between two vectors
73
+ */
74
+ cosineSimilarity(a, b) {
75
+ if (a.length !== b.length) {
76
+ throw new Error(`Vector dimension mismatch: ${a.length} vs ${b.length}`);
77
+ }
78
+ let dotProduct = 0;
79
+ let normA = 0;
80
+ let normB = 0;
81
+ for (let i = 0; i < a.length; i++) {
82
+ dotProduct += a[i] * b[i];
83
+ normA += a[i] * a[i];
84
+ normB += b[i] * b[i];
85
+ }
86
+ const denom = Math.sqrt(normA) * Math.sqrt(normB);
87
+ return denom === 0 ? 0 : dotProduct / denom;
88
+ }
89
+ /**
90
+ * Store a memory with deduplication
91
+ */
92
+ async store(content, embedding, options = {}) {
93
+ const { tags = [], category, source = "", dedupeThreshold = 0.95, } = options;
94
+ // Check for duplicates
95
+ const existing = await this.findSimilar(embedding, dedupeThreshold);
96
+ if (existing) {
97
+ // Update existing entry
98
+ const stmt = this.db.prepare("UPDATE memories SET content = ?, embedding = ?, tags = ?, category = ?, source = ?, created_at = ? WHERE id = ?");
99
+ stmt.run(content, JSON.stringify(embedding), JSON.stringify(tags), category || null, source, Date.now(), existing.id);
100
+ return {
101
+ entry: {
102
+ ...existing,
103
+ content,
104
+ embedding: JSON.stringify(embedding),
105
+ tags,
106
+ category,
107
+ source,
108
+ created_at: Date.now(),
109
+ },
110
+ isDuplicate: true,
111
+ updatedId: existing.id,
112
+ };
113
+ }
114
+ // Insert new entry
115
+ const id = randomUUID();
116
+ const created_at = Date.now();
117
+ const stmt = this.db.prepare("INSERT INTO memories (id, content, embedding, tags, category, source, created_at) VALUES (?, ?, ?, ?, ?, ?, ?)");
118
+ stmt.run(id, content, JSON.stringify(embedding), JSON.stringify(tags), category || null, source, created_at);
119
+ return {
120
+ entry: {
121
+ id,
122
+ content,
123
+ embedding: JSON.stringify(embedding),
124
+ tags,
125
+ category,
126
+ source,
127
+ created_at,
128
+ },
129
+ isDuplicate: false,
130
+ };
131
+ }
132
+ /**
133
+ * Find a similar memory above threshold (for deduplication)
134
+ */
135
+ async findSimilar(embedding, threshold) {
136
+ const results = await this.vectorSearch(embedding, 1, threshold);
137
+ return results.length > 0 ? results[0].entry : null;
138
+ }
139
+ /**
140
+ * Vector search using cosine similarity.
141
+ *
142
+ * Performance note: This implementation loads all embeddings into memory and performs
143
+ * brute-force cosine similarity computation. For large databases (>10,000 memories),
144
+ * this may have performance implications. Future optimizations could include:
145
+ * - Approximate nearest neighbor search (ANN)
146
+ * - Vector indexing (e.g., HNSW, IVF)
147
+ * - Limiting search scope with filters
148
+ */
149
+ async vectorSearch(queryEmbedding, limit = 5, minScore = 0.3) {
150
+ const stmt = this.db.prepare("SELECT * FROM memories");
151
+ const rows = stmt.all();
152
+ const results = [];
153
+ for (const row of rows) {
154
+ let embedding;
155
+ try {
156
+ embedding = JSON.parse(row.embedding);
157
+ }
158
+ catch (err) {
159
+ console.warn(`Skipping memory entry with invalid embedding JSON (id=${row.id}):`, err);
160
+ continue;
161
+ }
162
+ const score = this.cosineSimilarity(queryEmbedding, embedding);
163
+ if (score >= minScore) {
164
+ results.push({
165
+ entry: {
166
+ id: row.id,
167
+ content: row.content,
168
+ embedding: row.embedding,
169
+ tags: JSON.parse(row.tags || "[]"),
170
+ category: row.category || undefined,
171
+ source: row.source,
172
+ created_at: row.created_at,
173
+ },
174
+ score,
175
+ });
176
+ }
177
+ }
178
+ // Sort by score descending
179
+ results.sort((a, b) => b.score - a.score);
180
+ // Limit results
181
+ return results.slice(0, limit);
182
+ }
183
+ /**
184
+ * Delete a memory by ID
185
+ */
186
+ delete(id) {
187
+ this.db.prepare("DELETE FROM memories WHERE id = ?").run(id);
188
+ }
189
+ /**
190
+ * Delete memories matching a query above threshold
191
+ */
192
+ async deleteByQuery(queryEmbedding, threshold = 0.8) {
193
+ const matches = await this.vectorSearch(queryEmbedding, 100, threshold);
194
+ let deleted = 0;
195
+ for (const match of matches) {
196
+ this.delete(match.entry.id);
197
+ deleted++;
198
+ }
199
+ return deleted;
200
+ }
201
+ /**
202
+ * Get user profile (stub - not implemented in core)
203
+ */
204
+ getProfile() {
205
+ const stmt = this.db.prepare("SELECT value FROM metadata WHERE key = ?");
206
+ const row = stmt.get("profile");
207
+ return row ? row.value : null;
208
+ }
209
+ /**
210
+ * Get all memory content (limited)
211
+ */
212
+ getAllContent(limit = 20) {
213
+ const stmt = this.db.prepare("SELECT content FROM memories ORDER BY created_at DESC LIMIT ?");
214
+ const rows = stmt.all(limit);
215
+ return rows.map((r) => r.content);
216
+ }
217
+ /**
218
+ * Get database statistics
219
+ */
220
+ stats() {
221
+ const countStmt = this.db.prepare("SELECT COUNT(*) as count FROM memories");
222
+ const countRow = countStmt.get();
223
+ const captureStmt = this.db.prepare("SELECT value FROM metadata WHERE key = ?");
224
+ const captureRow = captureStmt.get("capture_count");
225
+ return {
226
+ totalMemories: countRow.count,
227
+ captureCount: captureRow ? parseInt(captureRow.value, 10) : 0,
228
+ dimension: this.dimension,
229
+ };
230
+ }
231
+ /**
232
+ * Increment capture count
233
+ */
234
+ incrementCaptureCount() {
235
+ const stmt = this.db.prepare("UPDATE metadata SET value = CAST((CAST(value AS INTEGER) + 1) AS TEXT) WHERE key = ?");
236
+ stmt.run("capture_count");
237
+ }
238
+ /**
239
+ * Close database connection
240
+ */
241
+ close() {
242
+ this.db.close();
243
+ }
244
+ }
245
+ //# sourceMappingURL=memory.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"memory.js","sourceRoot":"","sources":["../../src/db/memory.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ import { TransformersEmbeddings } from "./transformers.js";
2
+ import { OpenAIEmbeddings } from "./openai.js";
3
+ import type { EmbeddingProvider } from "./transformers.js";
4
+ export type { EmbeddingProvider };
5
+ export { TransformersEmbeddings, OpenAIEmbeddings };
6
+ export interface EmbeddingConfig {
7
+ openaiApiKey?: string;
8
+ openaiModel?: string;
9
+ debug?: boolean;
10
+ }
11
+ /**
12
+ * Create an embedding provider based on available configuration.
13
+ * Defaults to Transformers.js (zero-config), with OpenAI as optional override.
14
+ */
15
+ export declare function createEmbeddings(config?: EmbeddingConfig): Promise<EmbeddingProvider>;
16
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/embeddings/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,sBAAsB,EAAE,MAAM,mBAAmB,CAAC;AAC3D,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAC/C,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAE3D,YAAY,EAAE,iBAAiB,EAAE,CAAC;AAClC,OAAO,EAAE,sBAAsB,EAAE,gBAAgB,EAAE,CAAC;AAEpD,MAAM,WAAW,eAAe;IAC9B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED;;;GAGG;AACH,wBAAsB,gBAAgB,CACpC,MAAM,GAAE,eAAoB,GAC3B,OAAO,CAAC,iBAAiB,CAAC,CAmB5B"}
@@ -0,0 +1,26 @@
1
+ import { TransformersEmbeddings } from "./transformers.js";
2
+ import { OpenAIEmbeddings } from "./openai.js";
3
+ export { TransformersEmbeddings, OpenAIEmbeddings };
4
+ /**
5
+ * Create an embedding provider based on available configuration.
6
+ * Defaults to Transformers.js (zero-config), with OpenAI as optional override.
7
+ */
8
+ export async function createEmbeddings(config = {}) {
9
+ const { openaiApiKey, openaiModel, debug = false } = config;
10
+ // If OpenAI key is provided, use OpenAI (fail fast on error)
11
+ if (openaiApiKey) {
12
+ if (debug) {
13
+ console.error("[Embeddings] Using OpenAI provider");
14
+ }
15
+ // When an explicit OpenAI API key is provided, fail fast instead of silently
16
+ // falling back to a different provider with incompatible dimensions.
17
+ // This prevents dimension mismatch errors with existing databases.
18
+ return new OpenAIEmbeddings(openaiApiKey, openaiModel, debug);
19
+ }
20
+ // Default: Transformers.js (zero-config)
21
+ if (debug) {
22
+ console.error("[Embeddings] Using Transformers.js provider (zero-config)");
23
+ }
24
+ return new TransformersEmbeddings(debug);
25
+ }
26
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/embeddings/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,sBAAsB,EAAE,MAAM,mBAAmB,CAAC;AAC3D,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAI/C,OAAO,EAAE,sBAAsB,EAAE,gBAAgB,EAAE,CAAC;AAQpD;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACpC,SAA0B,EAAE;IAE5B,MAAM,EAAE,YAAY,EAAE,WAAW,EAAE,KAAK,GAAG,KAAK,EAAE,GAAG,MAAM,CAAC;IAE5D,6DAA6D;IAC7D,IAAI,YAAY,EAAE,CAAC;QACjB,IAAI,KAAK,EAAE,CAAC;YACV,OAAO,CAAC,KAAK,CAAC,oCAAoC,CAAC,CAAC;QACtD,CAAC;QACD,6EAA6E;QAC7E,qEAAqE;QACrE,mEAAmE;QACnE,OAAO,IAAI,gBAAgB,CAAC,YAAY,EAAE,WAAW,EAAE,KAAK,CAAC,CAAC;IAChE,CAAC;IAED,yCAAyC;IACzC,IAAI,KAAK,EAAE,CAAC;QACV,OAAO,CAAC,KAAK,CAAC,2DAA2D,CAAC,CAAC;IAC7E,CAAC;IACD,OAAO,IAAI,sBAAsB,CAAC,KAAK,CAAC,CAAC;AAC3C,CAAC"}