mcp-rag-memory 2.3.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/.env.example +35 -0
- package/README.md +475 -0
- package/dist/db/database.d.ts +22 -0
- package/dist/db/database.d.ts.map +1 -0
- package/dist/db/database.js +144 -0
- package/dist/db/database.js.map +1 -0
- package/dist/env.js +48 -0
- package/dist/index.js +12 -0
- package/dist/mcp/rag-server.d.ts +3 -0
- package/dist/mcp/rag-server.d.ts.map +1 -0
- package/dist/mcp/rag-server.js +275 -0
- package/dist/mcp/rag-server.js.map +1 -0
- package/dist/memory/memory.d.ts +69 -0
- package/dist/memory/memory.d.ts.map +1 -0
- package/dist/memory/memory.js +221 -0
- package/dist/memory/memory.js.map +1 -0
- package/dist/rag/chunker.d.ts +16 -0
- package/dist/rag/chunker.d.ts.map +1 -0
- package/dist/rag/chunker.js +109 -0
- package/dist/rag/chunker.js.map +1 -0
- package/dist/rag/embedder.d.ts +19 -0
- package/dist/rag/embedder.d.ts.map +1 -0
- package/dist/rag/embedder.js +96 -0
- package/dist/rag/embedder.js.map +1 -0
- package/dist/rag/embeddings.d.ts +14 -0
- package/dist/rag/embeddings.d.ts.map +1 -0
- package/dist/rag/embeddings.js +71 -0
- package/dist/rag/embeddings.js.map +1 -0
- package/dist/rag/pipeline.d.ts +49 -0
- package/dist/rag/pipeline.d.ts.map +1 -0
- package/dist/rag/pipeline.js +181 -0
- package/dist/rag/pipeline.js.map +1 -0
- package/dist/rag/vector-search.d.ts +39 -0
- package/dist/rag/vector-search.d.ts.map +1 -0
- package/dist/rag/vector-search.js +164 -0
- package/dist/rag/vector-search.js.map +1 -0
- package/dist/session/transcript.d.ts +75 -0
- package/dist/session/transcript.d.ts.map +1 -0
- package/dist/session/transcript.js +268 -0
- package/dist/session/transcript.js.map +1 -0
- package/package.json +56 -0
package/dist/env.js
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Zero-dependency `.env` loader.
|
|
3
|
+
*
|
|
4
|
+
* Loads KEY=VALUE pairs from `RAG_ENV_FILE` (if set) or `<cwd>/.env` into
|
|
5
|
+
* process.env. Existing environment variables are never overridden, so host
|
|
6
|
+
* config (MCP clients, CI, shell) always wins. Runs synchronously at import —
|
|
7
|
+
* must be imported before any module that reads `process.env.*` at load time.
|
|
8
|
+
* Supports `#` comments, `export KEY=` prefixes, and single/double-quoted values.
|
|
9
|
+
*/
|
|
10
|
+
import * as fs from "fs";
|
|
11
|
+
import * as path from "path";
|
|
12
|
+
function loadDotenv(file) {
|
|
13
|
+
let raw;
|
|
14
|
+
try {
|
|
15
|
+
raw = fs.readFileSync(file, "utf8");
|
|
16
|
+
}
|
|
17
|
+
catch {
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
for (const line of raw.split(/\r?\n/)) {
|
|
21
|
+
let t = line.trim();
|
|
22
|
+
if (!t || t.startsWith("#"))
|
|
23
|
+
continue;
|
|
24
|
+
if (t.startsWith("export "))
|
|
25
|
+
t = t.slice(7).trimStart();
|
|
26
|
+
const eq = t.indexOf("=");
|
|
27
|
+
if (eq <= 0)
|
|
28
|
+
continue;
|
|
29
|
+
const key = t.slice(0, eq).trim();
|
|
30
|
+
let value = t.slice(eq + 1).trim();
|
|
31
|
+
// Strip trailing inline comments for unquoted values (e.g. `KEY=val # note`).
|
|
32
|
+
if (!value.startsWith('"') && !value.startsWith("'")) {
|
|
33
|
+
const hash = value.indexOf(" #");
|
|
34
|
+
if (hash >= 0)
|
|
35
|
+
value = value.slice(0, hash).trimEnd();
|
|
36
|
+
}
|
|
37
|
+
if (value.length >= 2 && ((value.startsWith('"') && value.endsWith('"')) || (value.startsWith("'") && value.endsWith("'")))) {
|
|
38
|
+
value = value.slice(1, -1);
|
|
39
|
+
}
|
|
40
|
+
if (key && process.env[key] === undefined)
|
|
41
|
+
process.env[key] = value;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
const envFile = process.env.RAG_ENV_FILE ?? path.join(process.cwd(), ".env");
|
|
45
|
+
if (process.env.RAG_ENV_FILE || fs.existsSync(envFile)) {
|
|
46
|
+
loadDotenv(envFile);
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=env.js.map
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* MCP server entry point.
|
|
4
|
+
*
|
|
5
|
+
* Loads `.env` (from RAG_ENV_FILE or <cwd>/.env) before any module that reads
|
|
6
|
+
* `process.env.*` at import time, then boots the stdio MCP server. This keeps
|
|
7
|
+
* `npx mcp-rag-memory` configurable from a plain `.env` file in addition to
|
|
8
|
+
* environment variables set by the host MCP client.
|
|
9
|
+
*/
|
|
10
|
+
import "./env.js";
|
|
11
|
+
await import("./mcp/rag-server.js");
|
|
12
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rag-server.d.ts","sourceRoot":"","sources":["../../src/mcp/rag-server.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
3
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
4
|
+
import { z } from "zod";
|
|
5
|
+
import * as path from "path";
|
|
6
|
+
import { createRequire } from "node:module";
|
|
7
|
+
import { STORAGE_DIR } from "../db/database.js";
|
|
8
|
+
import { embeddingInfo } from "../rag/embeddings.js";
|
|
9
|
+
import { ingestText, ingestFile, ingestDirectory, searchDocs, retrieve, listDocuments, deleteDocument, documentStats, } from "../rag/pipeline.js";
|
|
10
|
+
import { ingestSession as syncSession, ingestLatest as syncLatest, } from "../session/transcript.js";
|
|
11
|
+
import { remember, recall, listMemories, getMemory, updateMemory, forget, consolidate, memoryStats, contextPrompt, MEMORY_TYPES, } from "../memory/memory.js";
|
|
12
|
+
const require = createRequire(import.meta.url);
|
|
13
|
+
const serverVersion = require("../../package.json").version;
|
|
14
|
+
const server = new McpServer({
|
|
15
|
+
name: "rag-memory-server",
|
|
16
|
+
version: serverVersion,
|
|
17
|
+
});
|
|
18
|
+
/* ------------------------------------------------------------------ */
|
|
19
|
+
/* System */
|
|
20
|
+
/* ------------------------------------------------------------------ */
|
|
21
|
+
server.registerTool("system_stats", {
|
|
22
|
+
description: "Get overall system stats: DB location, documents, chunks, memories, tokens, embedding backend",
|
|
23
|
+
inputSchema: {},
|
|
24
|
+
}, async () => {
|
|
25
|
+
const docs = documentStats();
|
|
26
|
+
const mem = memoryStats();
|
|
27
|
+
return {
|
|
28
|
+
content: [
|
|
29
|
+
{
|
|
30
|
+
type: "text",
|
|
31
|
+
text: JSON.stringify({ dbDir: STORAGE_DIR, embedding: embeddingInfo(), documents: docs, memories: mem }, null, 2),
|
|
32
|
+
},
|
|
33
|
+
],
|
|
34
|
+
};
|
|
35
|
+
});
|
|
36
|
+
/* ------------------------------------------------------------------ */
|
|
37
|
+
/* RAG: ingest */
|
|
38
|
+
/* ------------------------------------------------------------------ */
|
|
39
|
+
server.registerTool("rag_ingest_text", {
|
|
40
|
+
description: "Store raw text as a knowledge document (chunked + embedded for later retrieval)",
|
|
41
|
+
inputSchema: {
|
|
42
|
+
content: z.string().min(1),
|
|
43
|
+
title: z.string().describe("Document title"),
|
|
44
|
+
contentType: z.string().optional().describe("e.g. markdown, typescript, text, json"),
|
|
45
|
+
metadata: z.record(z.string(), z.unknown()).optional(),
|
|
46
|
+
},
|
|
47
|
+
}, async (args) => {
|
|
48
|
+
const res = await ingestText(args.content, args.title, {
|
|
49
|
+
contentType: args.contentType,
|
|
50
|
+
metadata: args.metadata,
|
|
51
|
+
});
|
|
52
|
+
return { content: [{ type: "text", text: JSON.stringify(res, null, 2) }] };
|
|
53
|
+
});
|
|
54
|
+
server.registerTool("rag_ingest_file", {
|
|
55
|
+
description: "Read a file and store it as a knowledge document",
|
|
56
|
+
inputSchema: {
|
|
57
|
+
path: z.string().describe("Absolute path to the file"),
|
|
58
|
+
metadata: z.record(z.string(), z.unknown()).optional(),
|
|
59
|
+
},
|
|
60
|
+
}, async (args) => {
|
|
61
|
+
const res = await ingestFile(args.path, { metadata: args.metadata });
|
|
62
|
+
return { content: [{ type: "text", text: JSON.stringify(res, null, 2) }] };
|
|
63
|
+
});
|
|
64
|
+
server.registerTool("rag_ingest_dir", {
|
|
65
|
+
description: "Recursively ingest supported source files from a directory",
|
|
66
|
+
inputSchema: {
|
|
67
|
+
path: z.string().describe("Absolute path to the directory"),
|
|
68
|
+
recursive: z.boolean().optional().default(true),
|
|
69
|
+
extensions: z.array(z.string()).optional().describe("File extensions to include, e.g. ['.ts', '.md']"),
|
|
70
|
+
},
|
|
71
|
+
}, async (args) => {
|
|
72
|
+
const res = await ingestDirectory(args.path, { recursive: args.recursive, extensions: args.extensions });
|
|
73
|
+
return { content: [{ type: "text", text: JSON.stringify(res, null, 2) }] };
|
|
74
|
+
});
|
|
75
|
+
/* ------------------------------------------------------------------ */
|
|
76
|
+
/* RAG: search / retrieve */
|
|
77
|
+
/* ------------------------------------------------------------------ */
|
|
78
|
+
server.registerTool("rag_search", {
|
|
79
|
+
description: "Hybrid semantic + keyword search over stored documents. Returns chunks ranked by relevance with scores.",
|
|
80
|
+
inputSchema: {
|
|
81
|
+
query: z.string().min(1),
|
|
82
|
+
top_k: z.number().int().min(1).max(50).optional().default(10),
|
|
83
|
+
min_score: z.number().min(0).max(1).optional().default(0.08),
|
|
84
|
+
},
|
|
85
|
+
}, async (args) => {
|
|
86
|
+
const hits = await searchDocs(args.query, args.top_k ?? 10, args.min_score ?? 0.08);
|
|
87
|
+
return {
|
|
88
|
+
content: [{ type: "text", text: JSON.stringify(hits, null, 2) }],
|
|
89
|
+
};
|
|
90
|
+
});
|
|
91
|
+
server.registerTool("rag_retrieve", {
|
|
92
|
+
description: "Retrieve relevant context chunks as a ready-to-inject context block for the LLM. Prefer this over rag_search when the result will be used as context.",
|
|
93
|
+
inputSchema: {
|
|
94
|
+
query: z.string().min(1),
|
|
95
|
+
top_k: z.number().int().min(1).max(20).optional().default(6),
|
|
96
|
+
min_score: z.number().min(0).max(1).optional().default(0.08),
|
|
97
|
+
},
|
|
98
|
+
}, async (args) => {
|
|
99
|
+
const res = await retrieve(args.query, args.top_k ?? 6, args.min_score ?? 0.08);
|
|
100
|
+
if (res.chunks.length === 0) {
|
|
101
|
+
return { content: [{ type: "text", text: "No relevant context found." }] };
|
|
102
|
+
}
|
|
103
|
+
const lines = res.chunks.map((c) => `--- [${c.docTitle}#${c.chunkIndex}] (score ${c.score.toFixed(3)}) ---\n${c.content}`);
|
|
104
|
+
return {
|
|
105
|
+
content: [
|
|
106
|
+
{
|
|
107
|
+
type: "text",
|
|
108
|
+
text: `[Context block: ${res.chunks.length} chunks, ~${res.totalTokens} tokens]\n\n${lines.join("\n\n")}`,
|
|
109
|
+
},
|
|
110
|
+
],
|
|
111
|
+
};
|
|
112
|
+
});
|
|
113
|
+
/* ------------------------------------------------------------------ */
|
|
114
|
+
/* RAG: document management */
|
|
115
|
+
/* ------------------------------------------------------------------ */
|
|
116
|
+
server.registerTool("rag_list_documents", {
|
|
117
|
+
description: "List all stored knowledge documents",
|
|
118
|
+
inputSchema: {},
|
|
119
|
+
}, async () => {
|
|
120
|
+
const docs = listDocuments();
|
|
121
|
+
return { content: [{ type: "text", text: JSON.stringify(docs, null, 2) }] };
|
|
122
|
+
});
|
|
123
|
+
server.registerTool("rag_document_stats", {
|
|
124
|
+
description: "Document storage statistics",
|
|
125
|
+
inputSchema: {},
|
|
126
|
+
}, async () => {
|
|
127
|
+
return { content: [{ type: "text", text: JSON.stringify(documentStats(), null, 2) }] };
|
|
128
|
+
});
|
|
129
|
+
server.registerTool("rag_delete_document", {
|
|
130
|
+
description: "Delete a document and all its chunks by ID",
|
|
131
|
+
inputSchema: {
|
|
132
|
+
doc_id: z.string().min(1),
|
|
133
|
+
},
|
|
134
|
+
}, async (args) => {
|
|
135
|
+
const res = deleteDocument(args.doc_id);
|
|
136
|
+
return { content: [{ type: "text", text: JSON.stringify(res) }] };
|
|
137
|
+
});
|
|
138
|
+
/* ------------------------------------------------------------------ */
|
|
139
|
+
/* Context Management: memory */
|
|
140
|
+
/* ------------------------------------------------------------------ */
|
|
141
|
+
server.registerTool("rag_sync_session", {
|
|
142
|
+
description: "Ingest an opencode session's user inputs (or the latest active session) into the RAG store as searchable documents. Use with the session id from opencode, or omit session_id and pass directory to auto-pick the most recent session in that workspace.",
|
|
143
|
+
inputSchema: {
|
|
144
|
+
session_id: z.string().optional().describe("opencode session id; omit to use the latest active session"),
|
|
145
|
+
directory: z.string().optional().describe("Workspace path used to pick the latest session when session_id is omitted"),
|
|
146
|
+
limit: z.number().int().min(1).optional().describe("Only ingest the first N user messages"),
|
|
147
|
+
},
|
|
148
|
+
}, async (args) => {
|
|
149
|
+
const res = args.session_id
|
|
150
|
+
? await syncSession(args.session_id, { limit: args.limit })
|
|
151
|
+
: await syncLatest({ limit: args.limit, directory: args.directory });
|
|
152
|
+
return { content: [{ type: "text", text: JSON.stringify(res, null, 2) }] };
|
|
153
|
+
});
|
|
154
|
+
server.registerTool("memory_remember", {
|
|
155
|
+
description: "Store a piece of long-term memory (fact, decision, preference, instruction, task, insight, conversation). Use this to persist things that should be remembered across sessions.",
|
|
156
|
+
inputSchema: {
|
|
157
|
+
content: z.string().min(1).describe("The memory content, written as a self-contained statement"),
|
|
158
|
+
type: z.enum(MEMORY_TYPES).optional().default("fact"),
|
|
159
|
+
importance: z.number().min(0).max(1).optional().default(0.5).describe("0..1, higher = keep longer"),
|
|
160
|
+
tags: z.array(z.string()).optional().describe("Tags for filtering"),
|
|
161
|
+
},
|
|
162
|
+
}, async (args) => {
|
|
163
|
+
const rec = await remember({
|
|
164
|
+
content: args.content,
|
|
165
|
+
type: args.type,
|
|
166
|
+
importance: args.importance,
|
|
167
|
+
tags: args.tags,
|
|
168
|
+
});
|
|
169
|
+
return { content: [{ type: "text", text: JSON.stringify(rec, null, 2) }] };
|
|
170
|
+
});
|
|
171
|
+
server.registerTool("memory_recall", {
|
|
172
|
+
description: "Semantically search long-term memories relevant to a query. This is how you remember past context.",
|
|
173
|
+
inputSchema: {
|
|
174
|
+
query: z.string().min(1),
|
|
175
|
+
top_k: z.number().int().min(1).max(30).optional().default(8),
|
|
176
|
+
min_score: z.number().min(0).max(1).optional().default(0.1),
|
|
177
|
+
},
|
|
178
|
+
}, async (args) => {
|
|
179
|
+
const hits = await recall(args.query, args.top_k ?? 8, args.min_score ?? 0.1);
|
|
180
|
+
return { content: [{ type: "text", text: JSON.stringify(hits, null, 2) }] };
|
|
181
|
+
});
|
|
182
|
+
server.registerTool("memory_list", {
|
|
183
|
+
description: "List memories, optionally filtered by type, tag, or minimum importance",
|
|
184
|
+
inputSchema: {
|
|
185
|
+
type: z.enum(MEMORY_TYPES).optional(),
|
|
186
|
+
tag: z.string().optional(),
|
|
187
|
+
min_importance: z.number().min(0).max(1).optional(),
|
|
188
|
+
limit: z.number().int().min(1).max(500).optional().default(100),
|
|
189
|
+
},
|
|
190
|
+
}, async (args) => {
|
|
191
|
+
const recs = listMemories({
|
|
192
|
+
type: args.type,
|
|
193
|
+
tag: args.tag,
|
|
194
|
+
minImportance: args.min_importance,
|
|
195
|
+
limit: args.limit,
|
|
196
|
+
});
|
|
197
|
+
return { content: [{ type: "text", text: JSON.stringify(recs, null, 2) }] };
|
|
198
|
+
});
|
|
199
|
+
server.registerTool("memory_get", {
|
|
200
|
+
description: "Get a single memory by ID",
|
|
201
|
+
inputSchema: {
|
|
202
|
+
id: z.string().min(1),
|
|
203
|
+
},
|
|
204
|
+
}, async (args) => {
|
|
205
|
+
const rec = getMemory(args.id);
|
|
206
|
+
if (!rec)
|
|
207
|
+
return { content: [{ type: "text", text: `No memory found with id ${args.id}` }] };
|
|
208
|
+
return { content: [{ type: "text", text: JSON.stringify(rec, null, 2) }] };
|
|
209
|
+
});
|
|
210
|
+
server.registerTool("memory_update", {
|
|
211
|
+
description: "Update content / type / importance / tags of an existing memory",
|
|
212
|
+
inputSchema: {
|
|
213
|
+
id: z.string().min(1),
|
|
214
|
+
content: z.string().optional(),
|
|
215
|
+
type: z.enum(MEMORY_TYPES).optional(),
|
|
216
|
+
importance: z.number().min(0).max(1).optional(),
|
|
217
|
+
tags: z.array(z.string()).optional(),
|
|
218
|
+
},
|
|
219
|
+
}, async (args) => {
|
|
220
|
+
const rec = await updateMemory(args.id, {
|
|
221
|
+
content: args.content,
|
|
222
|
+
type: args.type,
|
|
223
|
+
importance: args.importance,
|
|
224
|
+
tags: args.tags,
|
|
225
|
+
});
|
|
226
|
+
if (!rec)
|
|
227
|
+
return { content: [{ type: "text", text: `No memory found with id ${args.id}` }] };
|
|
228
|
+
return { content: [{ type: "text", text: JSON.stringify(rec, null, 2) }] };
|
|
229
|
+
});
|
|
230
|
+
server.registerTool("memory_forget", {
|
|
231
|
+
description: "Delete a memory by ID",
|
|
232
|
+
inputSchema: {
|
|
233
|
+
id: z.string().min(1),
|
|
234
|
+
},
|
|
235
|
+
}, async (args) => {
|
|
236
|
+
const res = forget(args.id);
|
|
237
|
+
return { content: [{ type: "text", text: JSON.stringify(res) }] };
|
|
238
|
+
});
|
|
239
|
+
server.registerTool("memory_consolidate", {
|
|
240
|
+
description: "Deduplicate near-identical memories, boost frequently-used ones, and optionally prune stale memories (enable with env RAG_PRUNE=1)",
|
|
241
|
+
inputSchema: {},
|
|
242
|
+
}, async () => {
|
|
243
|
+
const res = await consolidate();
|
|
244
|
+
return { content: [{ type: "text", text: JSON.stringify(res, null, 2) }] };
|
|
245
|
+
});
|
|
246
|
+
server.registerTool("memory_stats", {
|
|
247
|
+
description: "Long-term memory statistics (count, tokens, importance, by type)",
|
|
248
|
+
inputSchema: {},
|
|
249
|
+
}, async () => {
|
|
250
|
+
return { content: [{ type: "text", text: JSON.stringify(memoryStats(), null, 2) }] };
|
|
251
|
+
});
|
|
252
|
+
server.registerTool("memory_context", {
|
|
253
|
+
description: "Build a compact context block from the most relevant memories for a given topic. Best for injecting memory into prompts.",
|
|
254
|
+
inputSchema: {
|
|
255
|
+
topic: z.string().min(1),
|
|
256
|
+
top_k: z.number().int().min(1).max(20).optional().default(6),
|
|
257
|
+
},
|
|
258
|
+
}, async (args) => {
|
|
259
|
+
const { context, sources } = await contextPrompt(args.topic, args.top_k ?? 6);
|
|
260
|
+
if (!context)
|
|
261
|
+
return { content: [{ type: "text", text: "No relevant memories found." }] };
|
|
262
|
+
return {
|
|
263
|
+
content: [
|
|
264
|
+
{
|
|
265
|
+
type: "text",
|
|
266
|
+
text: `[Memory context: ${sources.length} sources]\n\n${context}`,
|
|
267
|
+
},
|
|
268
|
+
],
|
|
269
|
+
};
|
|
270
|
+
});
|
|
271
|
+
/* ------------------------------------------------------------------ */
|
|
272
|
+
const transport = new StdioServerTransport();
|
|
273
|
+
await server.connect(transport);
|
|
274
|
+
console.error(`RAG + Context Management MCP server running (db: ${path.resolve(STORAGE_DIR)})`);
|
|
275
|
+
//# sourceMappingURL=rag-server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rag-server.js","sourceRoot":"","sources":["../../src/mcp/rag-server.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
export declare const MEMORY_TYPES: readonly ["fact", "preference", "decision", "instruction", "task", "insight", "conversation"];
|
|
2
|
+
export type MemoryType = (typeof MEMORY_TYPES)[number];
|
|
3
|
+
export interface MemoryInput {
|
|
4
|
+
content: string;
|
|
5
|
+
type?: MemoryType;
|
|
6
|
+
importance?: number;
|
|
7
|
+
tags?: string[];
|
|
8
|
+
}
|
|
9
|
+
export interface MemoryRecord {
|
|
10
|
+
id: string;
|
|
11
|
+
type: MemoryType;
|
|
12
|
+
content: string;
|
|
13
|
+
importance: number;
|
|
14
|
+
tags: string[];
|
|
15
|
+
recallCount: number;
|
|
16
|
+
lastRecalled: number | null;
|
|
17
|
+
createdAt: number;
|
|
18
|
+
updatedAt: number;
|
|
19
|
+
}
|
|
20
|
+
export interface MemoryRecallHit extends MemoryRecord {
|
|
21
|
+
score: number;
|
|
22
|
+
/** Exponential decay factor applied to the raw similarity (0..1, based on staleness). */
|
|
23
|
+
decay: number;
|
|
24
|
+
}
|
|
25
|
+
export declare function remember(input: MemoryInput): Promise<MemoryRecord>;
|
|
26
|
+
export declare function recall(query: string, topK?: number, minScore?: number): Promise<MemoryRecallHit[]>;
|
|
27
|
+
export declare function listMemories(opts?: {
|
|
28
|
+
type?: MemoryType;
|
|
29
|
+
tag?: string;
|
|
30
|
+
minImportance?: number;
|
|
31
|
+
limit?: number;
|
|
32
|
+
}): MemoryRecord[];
|
|
33
|
+
export declare function getMemory(id: string): MemoryRecord | null;
|
|
34
|
+
export declare function updateMemory(id: string, patch: {
|
|
35
|
+
content?: string;
|
|
36
|
+
type?: MemoryType;
|
|
37
|
+
importance?: number;
|
|
38
|
+
tags?: string[];
|
|
39
|
+
}): Promise<MemoryRecord | null>;
|
|
40
|
+
export declare function forget(id: string): {
|
|
41
|
+
deleted: boolean;
|
|
42
|
+
};
|
|
43
|
+
export interface ConsolidationReport {
|
|
44
|
+
removedDuplicates: number;
|
|
45
|
+
promoted: number;
|
|
46
|
+
pruned: number;
|
|
47
|
+
totals: {
|
|
48
|
+
memories: number;
|
|
49
|
+
tokens: number;
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* - Removes near-duplicate memories (dot product > 0.92) keeping the highest-importance one.
|
|
54
|
+
* - Promotes importance of frequently-needed memories (recall_count >= 5) by +0.1.
|
|
55
|
+
* - Optionally prunes stale, low-importance, never-recalled memories (RAG_PRUNE=1,
|
|
56
|
+
* RAG_PRUNE_IMPORTANCE=0.2, RAG_PRUNE_AGE_DAYS=90). Off by default — deletes are irreversible.
|
|
57
|
+
*/
|
|
58
|
+
export declare function consolidate(): Promise<ConsolidationReport>;
|
|
59
|
+
export declare function memoryStats(): {
|
|
60
|
+
memories: number;
|
|
61
|
+
tokens: number;
|
|
62
|
+
avgImportance: number;
|
|
63
|
+
byType: Record<string, number>;
|
|
64
|
+
};
|
|
65
|
+
export declare function contextPrompt(query: string, topK?: number): Promise<{
|
|
66
|
+
context: string;
|
|
67
|
+
sources: MemoryRecallHit[];
|
|
68
|
+
}>;
|
|
69
|
+
//# sourceMappingURL=memory.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"memory.d.ts","sourceRoot":"","sources":["../../src/memory/memory.ts"],"names":[],"mappings":"AAMA,eAAO,MAAM,YAAY,+FAQf,CAAC;AAEX,MAAM,MAAM,UAAU,GAAG,CAAC,OAAO,YAAY,CAAC,CAAC,MAAM,CAAC,CAAC;AAKvD,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,UAAU,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;CACjB;AAED,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,UAAU,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,eAAgB,SAAQ,YAAY;IACnD,KAAK,EAAE,MAAM,CAAC;IACd,yFAAyF;IACzF,KAAK,EAAE,MAAM,CAAC;CACf;AAED,wBAAsB,QAAQ,CAAC,KAAK,EAAE,WAAW,GAAG,OAAO,CAAC,YAAY,CAAC,CAgBxE;AAED,wBAAsB,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,SAAI,EAAE,QAAQ,SAAM,GAAG,OAAO,CAAC,eAAe,EAAE,CAAC,CAwBhG;AAED,wBAAgB,YAAY,CAAC,IAAI,GAAE;IAAE,IAAI,CAAC,EAAE,UAAU,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IAAC,aAAa,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAO,GAAG,YAAY,EAAE,CAyBnI;AAED,wBAAgB,SAAS,CAAC,EAAE,EAAE,MAAM,GAAG,YAAY,GAAG,IAAI,CA6BzD;AAED,wBAAsB,YAAY,CAChC,EAAE,EAAE,MAAM,EACV,KAAK,EAAE;IAAE,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,UAAU,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,EAAE,CAAA;CAAE,GACnF,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CAkB9B;AAED,wBAAgB,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG;IAAE,OAAO,EAAE,OAAO,CAAA;CAAE,CAKvD;AAED,MAAM,WAAW,mBAAmB;IAClC,iBAAiB,EAAE,MAAM,CAAC;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;CAC9C;AAED;;;;;GAKG;AACH,wBAAsB,WAAW,IAAI,OAAO,CAAC,mBAAmB,CAAC,CA0EhE;AAED,wBAAgB,WAAW,IAAI;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAAE,CAczH;AAED,wBAAsB,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,SAAI,GAAG,OAAO,CAAC;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,eAAe,EAAE,CAAA;CAAE,CAAC,CAKrH"}
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
import { getDB, newId, nowMs, packVector, unpackVector } from "../db/database.js";
|
|
2
|
+
import { dotProduct } from "../rag/embedder.js";
|
|
3
|
+
import { estimateTokens } from "../rag/chunker.js";
|
|
4
|
+
import { embedText } from "../rag/embeddings.js";
|
|
5
|
+
import { vectorSearch, invalidateVectorCache } from "../rag/vector-search.js";
|
|
6
|
+
export const MEMORY_TYPES = [
|
|
7
|
+
"fact",
|
|
8
|
+
"preference",
|
|
9
|
+
"decision",
|
|
10
|
+
"instruction",
|
|
11
|
+
"task",
|
|
12
|
+
"insight",
|
|
13
|
+
"conversation",
|
|
14
|
+
];
|
|
15
|
+
/** Half-life in days for the recall decay curve. */
|
|
16
|
+
const DECAY_HALF_LIFE_DAYS = Number(process.env.RAG_MEMORY_HALF_LIFE_DAYS ?? 14);
|
|
17
|
+
export async function remember(input) {
|
|
18
|
+
const db = getDB();
|
|
19
|
+
const id = newId();
|
|
20
|
+
const ts = nowMs();
|
|
21
|
+
const type = input.type ?? "fact";
|
|
22
|
+
const importance = clampImportance(input.importance ?? 0.5);
|
|
23
|
+
const tags = JSON.stringify(input.tags ?? []);
|
|
24
|
+
const vec = await embedText(input.content);
|
|
25
|
+
db.prepare(`INSERT INTO memories (id, type, content, importance, embedding, tags, recall_count, last_recalled, created_at, updated_at)
|
|
26
|
+
VALUES (?, ?, ?, ?, ?, ?, 0, NULL, ?, ?)`).run(id, type, input.content, importance, packVector(vec), tags, ts, ts);
|
|
27
|
+
invalidateVectorCache();
|
|
28
|
+
return getMemory(id);
|
|
29
|
+
}
|
|
30
|
+
export async function recall(query, topK = 8, minScore = 0.1) {
|
|
31
|
+
const db = getDB();
|
|
32
|
+
const q = await embedText(query);
|
|
33
|
+
const hits = vectorSearch(q, { topK: topK * 2, minScore, collection: "memories" });
|
|
34
|
+
const ts = nowMs();
|
|
35
|
+
const bump = db.prepare("UPDATE memories SET recall_count = recall_count + 1, last_recalled = ? WHERE id = ?");
|
|
36
|
+
const results = [];
|
|
37
|
+
for (const h of hits) {
|
|
38
|
+
const rec = getMemory(h.key);
|
|
39
|
+
if (!rec)
|
|
40
|
+
continue;
|
|
41
|
+
bump.run(ts, rec.id);
|
|
42
|
+
const ageMs = ts - (rec.lastRecalled ?? rec.createdAt);
|
|
43
|
+
const decay = Math.pow(0.5, ageMs / (DECAY_HALF_LIFE_DAYS * 86_400_000));
|
|
44
|
+
results.push({
|
|
45
|
+
...rec,
|
|
46
|
+
recallCount: rec.recallCount + 1,
|
|
47
|
+
lastRecalled: ts,
|
|
48
|
+
score: h.score * decay,
|
|
49
|
+
decay,
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
results.sort((a, b) => b.score - a.score);
|
|
53
|
+
return results.slice(0, topK);
|
|
54
|
+
}
|
|
55
|
+
export function listMemories(opts = {}) {
|
|
56
|
+
const db = getDB();
|
|
57
|
+
const clauses = [];
|
|
58
|
+
const params = [];
|
|
59
|
+
if (opts.type) {
|
|
60
|
+
clauses.push("type = ?");
|
|
61
|
+
params.push(opts.type);
|
|
62
|
+
}
|
|
63
|
+
if (opts.tag) {
|
|
64
|
+
clauses.push("EXISTS (SELECT 1 FROM json_each(memories.tags) AS _tg WHERE _tg.value = ?)");
|
|
65
|
+
params.push(opts.tag);
|
|
66
|
+
}
|
|
67
|
+
if (opts.minImportance !== undefined) {
|
|
68
|
+
clauses.push("importance >= ?");
|
|
69
|
+
params.push(opts.minImportance);
|
|
70
|
+
}
|
|
71
|
+
const where = clauses.length ? ` WHERE ${clauses.join(" AND ")}` : "";
|
|
72
|
+
const limit = Math.min(opts.limit ?? 100, 500);
|
|
73
|
+
const rows = db
|
|
74
|
+
.prepare(`SELECT id FROM memories${where} ORDER BY updated_at DESC LIMIT ${limit}`)
|
|
75
|
+
.all(...params);
|
|
76
|
+
return rows.map((r) => getMemory(r.id)).filter(Boolean);
|
|
77
|
+
}
|
|
78
|
+
export function getMemory(id) {
|
|
79
|
+
const db = getDB();
|
|
80
|
+
const row = db
|
|
81
|
+
.prepare("SELECT * FROM memories WHERE id = ?")
|
|
82
|
+
.get(id);
|
|
83
|
+
if (!row)
|
|
84
|
+
return null;
|
|
85
|
+
return {
|
|
86
|
+
id: row.id,
|
|
87
|
+
type: row.type,
|
|
88
|
+
content: row.content,
|
|
89
|
+
importance: row.importance,
|
|
90
|
+
tags: JSON.parse(row.tags),
|
|
91
|
+
recallCount: row.recall_count,
|
|
92
|
+
lastRecalled: row.last_recalled,
|
|
93
|
+
createdAt: row.created_at,
|
|
94
|
+
updatedAt: row.updated_at,
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
export async function updateMemory(id, patch) {
|
|
98
|
+
const db = getDB();
|
|
99
|
+
const existing = getMemory(id);
|
|
100
|
+
if (!existing)
|
|
101
|
+
return null;
|
|
102
|
+
const content = patch.content ?? existing.content;
|
|
103
|
+
const type = patch.type ?? existing.type;
|
|
104
|
+
const importance = patch.importance !== undefined ? clampImportance(patch.importance) : existing.importance;
|
|
105
|
+
const tags = patch.tags ?? existing.tags;
|
|
106
|
+
const vec = await embedText(content);
|
|
107
|
+
const ts = nowMs();
|
|
108
|
+
db.prepare("UPDATE memories SET content = ?, type = ?, importance = ?, tags = ?, embedding = ?, updated_at = ? WHERE id = ?").run(content, type, importance, JSON.stringify(tags), packVector(vec), ts, id);
|
|
109
|
+
invalidateVectorCache();
|
|
110
|
+
return getMemory(id);
|
|
111
|
+
}
|
|
112
|
+
export function forget(id) {
|
|
113
|
+
const db = getDB();
|
|
114
|
+
const res = db.prepare("DELETE FROM memories WHERE id = ?").run(id);
|
|
115
|
+
invalidateVectorCache();
|
|
116
|
+
return { deleted: Number(res.changes) > 0 };
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* - Removes near-duplicate memories (dot product > 0.92) keeping the highest-importance one.
|
|
120
|
+
* - Promotes importance of frequently-needed memories (recall_count >= 5) by +0.1.
|
|
121
|
+
* - Optionally prunes stale, low-importance, never-recalled memories (RAG_PRUNE=1,
|
|
122
|
+
* RAG_PRUNE_IMPORTANCE=0.2, RAG_PRUNE_AGE_DAYS=90). Off by default — deletes are irreversible.
|
|
123
|
+
*/
|
|
124
|
+
export async function consolidate() {
|
|
125
|
+
const db = getDB();
|
|
126
|
+
const rows = db.prepare("SELECT id, embedding FROM memories").all();
|
|
127
|
+
const records = rows
|
|
128
|
+
.map((r) => ({ id: r.id, rec: getMemory(r.id) }))
|
|
129
|
+
.filter((r) => r.rec !== null && r.rec !== undefined);
|
|
130
|
+
const vectors = [];
|
|
131
|
+
for (const r of records) {
|
|
132
|
+
const vec = unpackVector(rows.find((row) => row.id === r.id)?.embedding ?? null);
|
|
133
|
+
if (vec)
|
|
134
|
+
vectors.push({ id: r.id, vec });
|
|
135
|
+
}
|
|
136
|
+
let removedDuplicates = 0;
|
|
137
|
+
const toDelete = new Set();
|
|
138
|
+
for (let i = 0; i < vectors.length; i++) {
|
|
139
|
+
if (toDelete.has(vectors[i].id))
|
|
140
|
+
continue;
|
|
141
|
+
for (let j = i + 1; j < vectors.length; j++) {
|
|
142
|
+
if (toDelete.has(vectors[j].id))
|
|
143
|
+
continue;
|
|
144
|
+
const sim = dotProduct(vectors[i].vec, vectors[j].vec);
|
|
145
|
+
if (sim >= 0.92) {
|
|
146
|
+
const a = records.find((r) => r.id === vectors[i].id).rec;
|
|
147
|
+
const b = records.find((r) => r.id === vectors[j].id).rec;
|
|
148
|
+
if (a.importance >= b.importance) {
|
|
149
|
+
toDelete.add(vectors[j].id);
|
|
150
|
+
}
|
|
151
|
+
else {
|
|
152
|
+
toDelete.add(vectors[i].id);
|
|
153
|
+
const tmpId = vectors[i].id;
|
|
154
|
+
vectors[i].id = vectors[j].id;
|
|
155
|
+
vectors[j].id = tmpId;
|
|
156
|
+
}
|
|
157
|
+
removedDuplicates++;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
const delStmt = db.prepare("DELETE FROM memories WHERE id = ?");
|
|
162
|
+
for (const id of toDelete)
|
|
163
|
+
delStmt.run(id);
|
|
164
|
+
let promoted = 0;
|
|
165
|
+
const bumpPromotion = async () => {
|
|
166
|
+
for (const rec of listMemories()) {
|
|
167
|
+
if (rec.recallCount >= 5 && rec.importance < 1) {
|
|
168
|
+
await updateMemory(rec.id, { importance: Math.min(1, rec.importance + 0.1) });
|
|
169
|
+
promoted++;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
};
|
|
173
|
+
let pruned = 0;
|
|
174
|
+
const pruneEnabled = process.env.RAG_PRUNE === "1" || process.env.RAG_PRUNE === "true";
|
|
175
|
+
if (pruneEnabled) {
|
|
176
|
+
const minImp = Number(process.env.RAG_PRUNE_IMPORTANCE ?? 0.2);
|
|
177
|
+
const maxAgeDays = Number(process.env.RAG_PRUNE_AGE_DAYS ?? 90);
|
|
178
|
+
const cutoff = nowMs() - maxAgeDays * 86_400_000;
|
|
179
|
+
const stale = db
|
|
180
|
+
.prepare("SELECT id FROM memories WHERE importance < ? AND recall_count = 0 AND created_at < ?")
|
|
181
|
+
.all(minImp, cutoff);
|
|
182
|
+
for (const s of stale) {
|
|
183
|
+
delStmt.run(s.id);
|
|
184
|
+
pruned++;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
await bumpPromotion();
|
|
188
|
+
invalidateVectorCache();
|
|
189
|
+
const stats = memoryStats();
|
|
190
|
+
return {
|
|
191
|
+
removedDuplicates,
|
|
192
|
+
promoted,
|
|
193
|
+
pruned,
|
|
194
|
+
totals: { memories: stats.memories, tokens: stats.tokens },
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
export function memoryStats() {
|
|
198
|
+
const db = getDB();
|
|
199
|
+
const count = db.prepare("SELECT COUNT(*) AS c FROM memories").get();
|
|
200
|
+
const avg = db.prepare("SELECT COALESCE(AVG(importance),0) AS a FROM memories").get();
|
|
201
|
+
const byTypeRows = db.prepare("SELECT type, COUNT(*) AS c FROM memories GROUP BY type").all();
|
|
202
|
+
const byType = {};
|
|
203
|
+
for (const r of byTypeRows)
|
|
204
|
+
byType[r.type] = r.c;
|
|
205
|
+
let totalTokens = 0;
|
|
206
|
+
for (const r of db.prepare("SELECT content FROM memories").all()) {
|
|
207
|
+
totalTokens += estimateTokens(r.content);
|
|
208
|
+
}
|
|
209
|
+
return { memories: count.c, tokens: totalTokens, avgImportance: avg.a, byType };
|
|
210
|
+
}
|
|
211
|
+
export async function contextPrompt(query, topK = 6) {
|
|
212
|
+
const hits = await recall(query, topK);
|
|
213
|
+
if (hits.length === 0)
|
|
214
|
+
return { context: "", sources: [] };
|
|
215
|
+
const lines = hits.map((h) => `[${h.type}, importance ${h.importance.toFixed(2)}] ${h.content}`);
|
|
216
|
+
return { context: lines.join("\n"), sources: hits };
|
|
217
|
+
}
|
|
218
|
+
function clampImportance(v) {
|
|
219
|
+
return Math.min(1, Math.max(0, v));
|
|
220
|
+
}
|
|
221
|
+
//# sourceMappingURL=memory.js.map
|