sqlite-local-ai-memory 0.0.0-stage ā 1.4.4
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 +66 -0
- package/BENCHMARKS.md +93 -0
- package/LICENSE +21 -0
- package/README.md +512 -3
- package/dist/MemoryEngine.d.ts +101 -0
- package/dist/MemoryEngine.d.ts.map +1 -0
- package/dist/MemoryEngine.js +365 -0
- package/dist/cognitive_agents.d.ts +84 -0
- package/dist/cognitive_agents.d.ts.map +1 -0
- package/dist/cognitive_agents.js +1087 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +8 -0
- package/dist/mcp_server.d.ts +3 -0
- package/dist/mcp_server.d.ts.map +1 -0
- package/dist/mcp_server.js +258 -0
- package/dist/multi_env_mcp_test.d.ts +2 -0
- package/dist/multi_env_mcp_test.d.ts.map +1 -0
- package/dist/multi_env_mcp_test.js +139 -0
- package/dist/sqlite_store.d.ts +40 -0
- package/dist/sqlite_store.d.ts.map +1 -0
- package/dist/sqlite_store.js +409 -0
- package/dist/stress_test.d.ts +2 -0
- package/dist/stress_test.d.ts.map +1 -0
- package/dist/stress_test.js +149 -0
- package/dist/test_autodream_dual.d.ts +2 -0
- package/dist/test_autodream_dual.d.ts.map +1 -0
- package/dist/test_autodream_dual.js +38 -0
- package/dist/test_autodream_history.d.ts +2 -0
- package/dist/test_autodream_history.d.ts.map +1 -0
- package/dist/test_autodream_history.js +131 -0
- package/dist/test_multitopic_dashboard.d.ts +2 -0
- package/dist/test_multitopic_dashboard.d.ts.map +1 -0
- package/dist/test_multitopic_dashboard.js +82 -0
- package/dist/types.d.ts +134 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +16 -0
- package/package.json +71 -4
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ============================================================================
|
|
3
|
+
* FILE: src/index.ts
|
|
4
|
+
* RESPONSIBILITY: Main Entry Point for sqlite-local-ai-memory library.
|
|
5
|
+
* ============================================================================
|
|
6
|
+
*/
|
|
7
|
+
export { MemoryEngine } from "./MemoryEngine.js";
|
|
8
|
+
export * from "./types.js";
|
|
9
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,cAAc,YAAY,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ============================================================================
|
|
3
|
+
* FILE: src/index.ts
|
|
4
|
+
* RESPONSIBILITY: Main Entry Point for sqlite-local-ai-memory library.
|
|
5
|
+
* ============================================================================
|
|
6
|
+
*/
|
|
7
|
+
export { MemoryEngine } from "./MemoryEngine.js";
|
|
8
|
+
export * from "./types.js";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcp_server.d.ts","sourceRoot":"","sources":["../src/mcp_server.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* ============================================================================
|
|
4
|
+
* FILE: src/memory/mcp_server.ts
|
|
5
|
+
* RESPONSIBILITY: Modern Model Context Protocol (MCP) High-Level Server over Stdio.
|
|
6
|
+
* EXPOSES: save_fact, search_memory, get_current_state, and consolidate (AutoDream).
|
|
7
|
+
* INCLUDES: Graceful Shutdown (SIGINT/SIGTERM) and Stdio stream protection.
|
|
8
|
+
* ============================================================================
|
|
9
|
+
*/
|
|
10
|
+
import os from "os";
|
|
11
|
+
import path from "path";
|
|
12
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
13
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
14
|
+
import { z } from "zod";
|
|
15
|
+
import { MemoryEngine } from "./MemoryEngine.js";
|
|
16
|
+
const defaultDbPath = path.join(os.homedir(), ".local-ai-memory", "memoria.db");
|
|
17
|
+
const isDebug = process.argv.includes("--debug") || process.env.MEMORY_DEBUG === "true" || process.env.DEBUG === "true";
|
|
18
|
+
const memory = new MemoryEngine({
|
|
19
|
+
dbPath: process.env.SQLITE_MEM_PATH || defaultDbPath,
|
|
20
|
+
userName: process.env.MEMORY_USER_NAME || "User",
|
|
21
|
+
userId: process.env.MEMORY_USER_ID || "user_default",
|
|
22
|
+
debug: isDebug,
|
|
23
|
+
});
|
|
24
|
+
const server = new McpServer({
|
|
25
|
+
name: "sqlite-local-ai-memory",
|
|
26
|
+
version: "1.4.4",
|
|
27
|
+
});
|
|
28
|
+
function safeErrorMessage(error, fallbackContext) {
|
|
29
|
+
if (!error)
|
|
30
|
+
return `Error during ${fallbackContext}.`;
|
|
31
|
+
let msg = typeof error.message === "string" ? error.message : String(error);
|
|
32
|
+
// FIX R3-1.7: redact all known API key formats to prevent credential leakage.
|
|
33
|
+
msg = msg.replace(/AIzaSy[A-Za-z0-9_-]{33}/g, "AIzaSy***"); // Gemini
|
|
34
|
+
msg = msg.replace(/\bsk-or-[A-Za-z0-9_-]+/g, "sk-or-***"); // OpenRouter
|
|
35
|
+
msg = msg.replace(/\bsk-[A-Za-z0-9_-]{20,}/g, "sk-***"); // OpenAI
|
|
36
|
+
msg = msg.replace(/Bearer\s+[A-Za-z0-9_.\-]+/gi, "Bearer ***"); // Generic Bearer
|
|
37
|
+
const firstLine = msg.split("\n")[0].trim();
|
|
38
|
+
return firstLine || `Error during ${fallbackContext}.`;
|
|
39
|
+
}
|
|
40
|
+
// FIX R3-1.4: source must be alphanumeric + underscore/dash only (max 64 chars).
|
|
41
|
+
// Prevents bracket injection that could corrupt the tag-parsing regex in runAutoDream.
|
|
42
|
+
const SOURCE_TAG = z
|
|
43
|
+
.string()
|
|
44
|
+
.regex(/^[A-Za-z0-9_-]{1,64}$/, "source must be alphanumeric (A-Z, 0-9, _ or -), max 64 characters.")
|
|
45
|
+
.optional();
|
|
46
|
+
// Tool 1: save_fact
|
|
47
|
+
server.tool("save_fact", "Stores a new fact, preference, or technical knowledge directly in memory.", {
|
|
48
|
+
fact: z.string().min(3, "Fact is too short to save.").max(2000).describe("The fact or information to save."),
|
|
49
|
+
userId: z.string().optional().describe("Optional user ID to scope the memory fact."),
|
|
50
|
+
source: SOURCE_TAG.describe("Source or project tag, e.g. 'TOTALCONNECT', 'SQLITE_MEMORY' (optional, alphanumeric only)."),
|
|
51
|
+
}, async ({ fact, userId, source }, { signal }) => {
|
|
52
|
+
try {
|
|
53
|
+
if (signal?.aborted) {
|
|
54
|
+
throw new Error("Request aborted by MCP client");
|
|
55
|
+
}
|
|
56
|
+
await memory.saveFact(fact, userId, { signal, source });
|
|
57
|
+
return {
|
|
58
|
+
content: [
|
|
59
|
+
{
|
|
60
|
+
type: "text",
|
|
61
|
+
text: `ā
Fact successfully saved to memory: "${fact}"`,
|
|
62
|
+
},
|
|
63
|
+
],
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
catch (error) {
|
|
67
|
+
if (error.name === "AbortError" || signal?.aborted) {
|
|
68
|
+
return {
|
|
69
|
+
content: [{ type: "text", text: "ā¹ļø Operation aborted by MCP client." }],
|
|
70
|
+
isError: true,
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
return {
|
|
74
|
+
content: [
|
|
75
|
+
{
|
|
76
|
+
type: "text",
|
|
77
|
+
text: `ā Error saving fact: ${safeErrorMessage(error, "save_fact")}`,
|
|
78
|
+
},
|
|
79
|
+
],
|
|
80
|
+
isError: true,
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
// Tool 2: search_memory
|
|
85
|
+
server.tool("search_memory", "Searches long-term memory for relevant facts or historical context using hybrid FTS5 BM25 + Int8 Vector search. Optionally pass recent conversation history to improve query disambiguation.", {
|
|
86
|
+
query: z.string().min(1, "Search query cannot be empty.").describe("Search terms or topic to recall."),
|
|
87
|
+
limit: z.number().int().min(1).max(20).optional().default(5).describe("Maximum number of results to return (default: 5, max: 20)."),
|
|
88
|
+
userId: z.string().optional().describe("Optional user ID to scope the search."),
|
|
89
|
+
history: z.string().max(4000).optional().describe("Optional recent conversation history (e.g. 'U: hi\\nA: hello\\nU: what was that thing?'). When provided, the Query Expander uses it to resolve ambiguous references."),
|
|
90
|
+
}, async ({ query, limit, userId, history }, { signal }) => {
|
|
91
|
+
try {
|
|
92
|
+
if (signal?.aborted) {
|
|
93
|
+
throw new Error("Request aborted by MCP client");
|
|
94
|
+
}
|
|
95
|
+
const hits = await memory.search(query, limit, userId, { signal, history });
|
|
96
|
+
if (hits.length === 0) {
|
|
97
|
+
return {
|
|
98
|
+
content: [
|
|
99
|
+
{
|
|
100
|
+
type: "text",
|
|
101
|
+
text: `No relevant memories found for: "${query}"`,
|
|
102
|
+
},
|
|
103
|
+
],
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
const formatted = hits
|
|
107
|
+
.map((h, i) => `[${i + 1}] (Score: ${(h.score ?? 0).toFixed(2)}) ${h.data}`)
|
|
108
|
+
.join("\n");
|
|
109
|
+
return {
|
|
110
|
+
content: [
|
|
111
|
+
{
|
|
112
|
+
type: "text",
|
|
113
|
+
text: `š Relevant memories found for "${query}":\n\n${formatted}`,
|
|
114
|
+
},
|
|
115
|
+
],
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
catch (error) {
|
|
119
|
+
if (error.name === "AbortError" || signal?.aborted) {
|
|
120
|
+
return {
|
|
121
|
+
content: [{ type: "text", text: "ā¹ļø Operation aborted by MCP client." }],
|
|
122
|
+
isError: true,
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
return {
|
|
126
|
+
content: [
|
|
127
|
+
{
|
|
128
|
+
type: "text",
|
|
129
|
+
text: `ā Error searching memory: ${safeErrorMessage(error, "search_memory")}`,
|
|
130
|
+
},
|
|
131
|
+
],
|
|
132
|
+
isError: true,
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
// Tool 3: get_current_state
|
|
137
|
+
server.tool("get_current_state", "Retrieves the active, current working state summary (Dashboard).", {
|
|
138
|
+
userId: z.string().optional().describe("Optional user ID to read the dashboard for."),
|
|
139
|
+
}, async ({ userId }, { signal }) => {
|
|
140
|
+
try {
|
|
141
|
+
if (signal?.aborted) {
|
|
142
|
+
throw new Error("Request aborted by MCP client");
|
|
143
|
+
}
|
|
144
|
+
const dash = memory.getDashboard(userId);
|
|
145
|
+
return {
|
|
146
|
+
content: [
|
|
147
|
+
{
|
|
148
|
+
type: "text",
|
|
149
|
+
text: dash
|
|
150
|
+
? `š Active Working Dashboard (Updated: ${dash.updated_at}):\n\n${dash.data}`
|
|
151
|
+
: "š Working dashboard is empty.",
|
|
152
|
+
},
|
|
153
|
+
],
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
catch (error) {
|
|
157
|
+
return {
|
|
158
|
+
content: [
|
|
159
|
+
{
|
|
160
|
+
type: "text",
|
|
161
|
+
text: `ā Error reading active working state: ${safeErrorMessage(error, "get_current_state")}`,
|
|
162
|
+
},
|
|
163
|
+
],
|
|
164
|
+
isError: true,
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
});
|
|
168
|
+
// Tool 4: consolidate (AutoDream)
|
|
169
|
+
server.tool("consolidate", "Runs background memory maintenance (AutoDream): summarizes recent progress, cleans up outdated notes, saves key technical takeaways, and updates the active project board.", {
|
|
170
|
+
userId: z.string().optional().describe("User ID to consolidate (optional, default: user_default)."),
|
|
171
|
+
source: SOURCE_TAG.describe("Current project or source being consolidated (e.g. 'TOTALCONNECT'). Preserves items from other projects in dashboard. Alphanumeric only."),
|
|
172
|
+
}, async ({ userId, source }, { signal }) => {
|
|
173
|
+
try {
|
|
174
|
+
if (signal?.aborted) {
|
|
175
|
+
throw new Error("Request aborted by MCP client");
|
|
176
|
+
}
|
|
177
|
+
const result = await memory.consolidate(userId, source, { signal });
|
|
178
|
+
const triageSection = result.triageMemory && result.triageMemory.length > 0
|
|
179
|
+
? `\n\nš¦ Injected Triage Cards (${result.triageMemory.length}):\n` +
|
|
180
|
+
result.triageMemory.map((t, i) => `[Card ${i + 1} - ${t.type}]\n${t.fact}`).join("\n\n")
|
|
181
|
+
: "";
|
|
182
|
+
return {
|
|
183
|
+
content: [
|
|
184
|
+
{
|
|
185
|
+
type: "text",
|
|
186
|
+
text: `š AutoDream State Consolidation Complete:\n\n` +
|
|
187
|
+
`⢠Executive Summary:\n"${result.narrativeSummary}"\n\n` +
|
|
188
|
+
`⢠Active Dashboard Items: ${result.totalActive}\n` +
|
|
189
|
+
`⢠Consolidated Facts Injected: ${result.triageMemory.length}\n` +
|
|
190
|
+
`⢠Unresolved Incubator Cases: ${result.openCases.length}\n` +
|
|
191
|
+
`⢠Status: ${result.statusMessage}` + triageSection,
|
|
192
|
+
},
|
|
193
|
+
],
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
catch (error) {
|
|
197
|
+
if (error.name === "AbortError" || signal?.aborted) {
|
|
198
|
+
return {
|
|
199
|
+
content: [{ type: "text", text: "ā¹ļø Operation aborted by MCP client." }],
|
|
200
|
+
isError: true,
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
return {
|
|
204
|
+
content: [
|
|
205
|
+
{
|
|
206
|
+
type: "text",
|
|
207
|
+
text: `ā Error during AutoDream consolidation: ${safeErrorMessage(error, "consolidate")}`,
|
|
208
|
+
},
|
|
209
|
+
],
|
|
210
|
+
isError: true,
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
});
|
|
214
|
+
const SHUTDOWN_TIMEOUT_MS = 5000;
|
|
215
|
+
let isShuttingDown = false;
|
|
216
|
+
// FIX R3-1.5: Accept exitCode so supervisors (systemd, pm2, Docker) can distinguish
|
|
217
|
+
// clean shutdowns (0) from crashes (1). Escalate if an error occurs during shutdown itself.
|
|
218
|
+
async function gracefulShutdown(signal, exitCode = 0) {
|
|
219
|
+
if (isShuttingDown)
|
|
220
|
+
return;
|
|
221
|
+
isShuttingDown = true;
|
|
222
|
+
console.error(`\nš [MCP_SERVER] Received ${signal} signal. Gracefully closing server and database...`);
|
|
223
|
+
const forceExitTimer = setTimeout(() => {
|
|
224
|
+
console.error("ā ļø [MCP_SERVER] Forced exit: shutdown timeout exceeded (5s).");
|
|
225
|
+
process.exit(1);
|
|
226
|
+
}, SHUTDOWN_TIMEOUT_MS);
|
|
227
|
+
forceExitTimer.unref();
|
|
228
|
+
try {
|
|
229
|
+
await server.close();
|
|
230
|
+
memory.close();
|
|
231
|
+
}
|
|
232
|
+
catch (err) {
|
|
233
|
+
console.error("ā ļø [MCP_SERVER] Error during shutdown:", err.message);
|
|
234
|
+
exitCode = exitCode === 0 ? 1 : exitCode; // escalate clean->error, keep crash code
|
|
235
|
+
}
|
|
236
|
+
finally {
|
|
237
|
+
clearTimeout(forceExitTimer);
|
|
238
|
+
process.exit(exitCode);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
process.on("SIGINT", () => gracefulShutdown("SIGINT", 0));
|
|
242
|
+
process.on("SIGTERM", () => gracefulShutdown("SIGTERM", 0));
|
|
243
|
+
process.on("uncaughtException", (err) => {
|
|
244
|
+
console.error("š„ [MCP_SERVER] uncaughtException:", err);
|
|
245
|
+
gracefulShutdown("uncaughtException", 1); // exit 1 so supervisors know this was a crash
|
|
246
|
+
});
|
|
247
|
+
process.on("unhandledRejection", (reason) => {
|
|
248
|
+
console.error("š„ [MCP_SERVER] unhandledRejection:", reason);
|
|
249
|
+
});
|
|
250
|
+
async function main() {
|
|
251
|
+
const transport = new StdioServerTransport();
|
|
252
|
+
await server.connect(transport);
|
|
253
|
+
console.error("š sqlite-local-ai-memory MCP server running on stdio.");
|
|
254
|
+
}
|
|
255
|
+
main().catch((err) => {
|
|
256
|
+
console.error("ā [MCP_SERVER] Fatal error starting server:", err);
|
|
257
|
+
process.exit(1);
|
|
258
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"multi_env_mcp_test.d.ts","sourceRoot":"","sources":["../src/multi_env_mcp_test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ============================================================================
|
|
3
|
+
* FILE: src/memory/multi_env_mcp_test.ts
|
|
4
|
+
* RESPONSIBILITY: Comprehensive 20-test suite for MCP Client and Server
|
|
5
|
+
* across 4 environment scenarios (Full Stack, Cloud Only, Ollama Only, Zero-AI).
|
|
6
|
+
* ============================================================================
|
|
7
|
+
*/
|
|
8
|
+
import fs from "fs";
|
|
9
|
+
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
10
|
+
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
|
|
11
|
+
const results = [];
|
|
12
|
+
function cleanupDb(dbPath) {
|
|
13
|
+
for (const ext of ["", "-wal", "-shm"]) {
|
|
14
|
+
if (fs.existsSync(dbPath + ext)) {
|
|
15
|
+
try {
|
|
16
|
+
fs.unlinkSync(dbPath + ext);
|
|
17
|
+
}
|
|
18
|
+
catch (_) { }
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
async function runMcpEnvTest(envName, dbPath, envVars, startTestNum) {
|
|
23
|
+
console.log(`\n=================================================================`);
|
|
24
|
+
console.log(`š§Ŗ TESTING ENVIRONMENT: ${envName}`);
|
|
25
|
+
console.log(`=================================================================`);
|
|
26
|
+
cleanupDb(dbPath);
|
|
27
|
+
const transport = new StdioClientTransport({
|
|
28
|
+
command: "node",
|
|
29
|
+
args: ["./dist/mcp_server.js"],
|
|
30
|
+
env: {
|
|
31
|
+
...process.env,
|
|
32
|
+
SQLITE_MEM_PATH: dbPath,
|
|
33
|
+
...envVars,
|
|
34
|
+
},
|
|
35
|
+
});
|
|
36
|
+
const client = new Client({ name: `test-client-${envName.toLowerCase().replace(/[^a-z0-9]/g, "-")}`, version: "1.0.0" }, { capabilities: {} });
|
|
37
|
+
let testCount = startTestNum;
|
|
38
|
+
try {
|
|
39
|
+
await client.connect(transport);
|
|
40
|
+
// Test 1: tools/list
|
|
41
|
+
const tools = await client.listTools();
|
|
42
|
+
const hasTools = tools.tools.length === 4;
|
|
43
|
+
results.push({
|
|
44
|
+
num: testCount++,
|
|
45
|
+
env: envName,
|
|
46
|
+
name: "JSON-RPC Handshake & tools/list",
|
|
47
|
+
passed: hasTools,
|
|
48
|
+
details: `${tools.tools.length} registered tools`,
|
|
49
|
+
});
|
|
50
|
+
// Test 2: get_current_state (Initial)
|
|
51
|
+
const state1 = await client.callTool({ name: "get_current_state", arguments: {} });
|
|
52
|
+
const isText1 = Array.isArray(state1.content) && state1.content.length > 0;
|
|
53
|
+
results.push({
|
|
54
|
+
num: testCount++,
|
|
55
|
+
env: envName,
|
|
56
|
+
name: "get_current_state (Initial State)",
|
|
57
|
+
passed: isText1,
|
|
58
|
+
details: `Response received in ${envName}`,
|
|
59
|
+
});
|
|
60
|
+
// Test 3: save_fact
|
|
61
|
+
const factText = `[FACT_${envName}] Primary server active on IP 10.0.0.50`;
|
|
62
|
+
const saveRes = await client.callTool({ name: "save_fact", arguments: { fact: factText } });
|
|
63
|
+
const saveSuccess = Array.isArray(saveRes.content) && (String(saveRes.content[0].text).includes("successfully saved") || String(saveRes.content[0].text).includes("registrado exitosamente"));
|
|
64
|
+
results.push({
|
|
65
|
+
num: testCount++,
|
|
66
|
+
env: envName,
|
|
67
|
+
name: "save_fact (Fact Ingestion)",
|
|
68
|
+
passed: saveSuccess,
|
|
69
|
+
details: String(saveRes.content[0]?.text || "").slice(0, 60),
|
|
70
|
+
});
|
|
71
|
+
// Test 4: search_memory
|
|
72
|
+
const searchRes = await client.callTool({ name: "search_memory", arguments: { query: "Proxmox", limit: 3 } });
|
|
73
|
+
const searchSuccess = Array.isArray(searchRes.content) && searchRes.content.length > 0;
|
|
74
|
+
results.push({
|
|
75
|
+
num: testCount++,
|
|
76
|
+
env: envName,
|
|
77
|
+
name: "search_memory (RAG/FTS5 Search)",
|
|
78
|
+
passed: searchSuccess,
|
|
79
|
+
details: String(searchRes.content[0]?.text || "").slice(0, 60),
|
|
80
|
+
});
|
|
81
|
+
// Test 5: get_current_state (Post-Ingestion)
|
|
82
|
+
const state2 = await client.callTool({ name: "get_current_state", arguments: {} });
|
|
83
|
+
const isText2 = Array.isArray(state2.content) && state2.content.length > 0;
|
|
84
|
+
results.push({
|
|
85
|
+
num: testCount++,
|
|
86
|
+
env: envName,
|
|
87
|
+
name: "get_current_state (Updated Dashboard)",
|
|
88
|
+
passed: isText2,
|
|
89
|
+
details: `Dashboard returned in ${envName}`,
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
catch (err) {
|
|
93
|
+
console.error(`ā Error testing ${envName}:`, err.message);
|
|
94
|
+
results.push({
|
|
95
|
+
num: testCount,
|
|
96
|
+
env: envName,
|
|
97
|
+
name: "Environment Execution",
|
|
98
|
+
passed: false,
|
|
99
|
+
details: err.message,
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
finally {
|
|
103
|
+
try {
|
|
104
|
+
await client.close();
|
|
105
|
+
}
|
|
106
|
+
catch (_) { }
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
async function main() {
|
|
110
|
+
console.log("š [MULTI_ENV_TEST] Starting 20 Empirical MCP Tests across 4 Environments...");
|
|
111
|
+
const activeGeminiKey = process.env.GEMINI_API_KEY || "";
|
|
112
|
+
const defaultOllamaUrl = process.env.OLLAMA_URL || "http://localhost:11434/api";
|
|
113
|
+
// ENVIRONMENT 1: Full Stack (Ollama + Cloud Gemini)
|
|
114
|
+
await runMcpEnvTest("1. Full Stack (Ollama + Cloud)", "./memoria_env_full.db", { OLLAMA_URL: defaultOllamaUrl, GEMINI_API_KEY: activeGeminiKey }, 1);
|
|
115
|
+
// ENVIRONMENT 2: Cloud Only (Gemini Cloud + Embeddings)
|
|
116
|
+
await runMcpEnvTest("2. Cloud Gemini Only (Ollama Off)", "./memoria_env_cloud.db", { OLLAMA_URL: "http://127.0.0.1:9999", GEMINI_API_KEY: activeGeminiKey }, 6);
|
|
117
|
+
// ENVIRONMENT 3: Local Only (Ollama Local, Cloud Off)
|
|
118
|
+
await runMcpEnvTest("3. Local Ollama Only (Cloud Off)", "./memoria_env_ollama.db", { GEMINI_API_KEY: "", OLLAMA_URL: defaultOllamaUrl }, 11);
|
|
119
|
+
// ENVIRONMENT 4: Zero-AI Fallback (No Ollama, No Cloud)
|
|
120
|
+
await runMcpEnvTest("4. Zero-AI Extreme Fallback", "./memoria_env_zero.db", { OLLAMA_URL: "http://127.0.0.1:9999", GEMINI_API_KEY: "" }, 16);
|
|
121
|
+
console.log("\n=================================================================");
|
|
122
|
+
console.log("š EMPIRICAL TEST SUMMARY (20 MCP TESTS):");
|
|
123
|
+
console.log("=================================================================");
|
|
124
|
+
let successful = 0;
|
|
125
|
+
for (const r of results) {
|
|
126
|
+
if (r.passed)
|
|
127
|
+
successful++;
|
|
128
|
+
console.log(`${r.passed ? "ā
" : "ā"} [TEST ${r.num}] [${r.env}] ${r.name}: ${r.details}`);
|
|
129
|
+
}
|
|
130
|
+
console.log(`\nš GLOBAL RESULT: ${successful} / ${results.length} TESTS PASSED (${((successful / results.length) * 100).toFixed(1)}% Success)`);
|
|
131
|
+
if (successful < results.length) {
|
|
132
|
+
console.error(`š„ ${results.length - successful} test(s) failed.`);
|
|
133
|
+
process.exit(1);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
main().catch((err) => {
|
|
137
|
+
console.error("ā Fatal error in test runner:", err);
|
|
138
|
+
process.exit(1);
|
|
139
|
+
});
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { type MemoryHit } from "./types.js";
|
|
2
|
+
export declare const DASHBOARD_UUID = "00000000-0000-0000-0000-000000000000";
|
|
3
|
+
export declare class SqliteStore {
|
|
4
|
+
private db;
|
|
5
|
+
private dbPath;
|
|
6
|
+
constructor(targetPath?: string);
|
|
7
|
+
private expandHome;
|
|
8
|
+
private resolveDatabasePath;
|
|
9
|
+
private initDatabase;
|
|
10
|
+
quantizeVectorInt8(vec: number[]): Buffer;
|
|
11
|
+
cosineSimilarityInt8(queryVec: number[], blob: Buffer): number;
|
|
12
|
+
/** Static stopword set for ES/EN ā filters common words that dilute FTS5 BM25 ranking. */
|
|
13
|
+
private static readonly STOPWORDS;
|
|
14
|
+
searchFTS5(query: string, limit?: number, userId?: string): MemoryHit[];
|
|
15
|
+
searchVectorInt8(queryVec: number[], limit?: number, userId?: string): MemoryHit[];
|
|
16
|
+
insertMemoryFact(id: string, data: string, userId?: string, vector?: number[], source?: string): boolean;
|
|
17
|
+
deactivateMemoryFact(id: string): boolean;
|
|
18
|
+
getDashboardFact(userId?: string): {
|
|
19
|
+
data: string;
|
|
20
|
+
memory?: string;
|
|
21
|
+
updated_at: string;
|
|
22
|
+
} | null;
|
|
23
|
+
saveDashboardFact(dashboardJson: string, userId?: string, vector?: number[]): boolean;
|
|
24
|
+
/**
|
|
25
|
+
* Retrieves recent memory facts created on or after the specified timestamp.
|
|
26
|
+
* Excludes the Dashboard global singleton record (DASHBOARD_UUID).
|
|
27
|
+
*
|
|
28
|
+
* @param sinceIso - ISO 8601 timestamp string marking the start of the consolidation window.
|
|
29
|
+
* @param userId - Target user identifier (defaults to 'user_default').
|
|
30
|
+
* @param sourceFilter - Optional case-insensitive source label filter (e.g. 'TOTALCONNECT').
|
|
31
|
+
* @returns Array of objects containing the fact text (`data`) and its origin (`source`).
|
|
32
|
+
*/
|
|
33
|
+
getRecentFactsSince(sinceIso: string, userId?: string, sourceFilter?: string): Array<{
|
|
34
|
+
data: string;
|
|
35
|
+
source: string;
|
|
36
|
+
}>;
|
|
37
|
+
backfillMissingVectors(getEmbeddingFn: (text: string) => Promise<number[]>, userId?: string): Promise<number>;
|
|
38
|
+
close(): void;
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=sqlite_store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sqlite_store.d.ts","sourceRoot":"","sources":["../src/sqlite_store.ts"],"names":[],"mappings":"AAkBA,OAAO,EAAE,KAAK,SAAS,EAAiB,MAAM,YAAY,CAAC;AAG3D,eAAO,MAAM,cAAc,yCAAyC,CAAC;AAErE,qBAAa,WAAW;IACtB,OAAO,CAAC,EAAE,CAAe;IACzB,OAAO,CAAC,MAAM,CAAS;gBAEX,UAAU,CAAC,EAAE,MAAM;IAM/B,OAAO,CAAC,UAAU;IASlB,OAAO,CAAC,mBAAmB;IAiB3B,OAAO,CAAC,YAAY;IA6Db,kBAAkB,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,MAAM;IAezC,oBAAoB,CAAC,QAAQ,EAAE,MAAM,EAAE,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM;IAkBrE,0FAA0F;IAC1F,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,SAAS,CAgB9B;IAEI,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,GAAE,MAAW,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,EAAE;IA8C3E,gBAAgB,CAAC,QAAQ,EAAE,MAAM,EAAE,EAAE,KAAK,GAAE,MAAW,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,EAAE;IA0DtF,gBAAgB,CACrB,EAAE,EAAE,MAAM,EACV,IAAI,EAAE,MAAM,EACZ,MAAM,GAAE,MAAuB,EAC/B,MAAM,CAAC,EAAE,MAAM,EAAE,EACjB,MAAM,GAAE,MAAiB,GACxB,OAAO;IA+BH,oBAAoB,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO;IAkBzC,gBAAgB,CAAC,MAAM,GAAE,MAAuB,GAAG;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI;IAqB/G,iBAAiB,CAAC,aAAa,EAAE,MAAM,EAAE,MAAM,GAAE,MAAuB,EAAE,MAAM,CAAC,EAAE,MAAM,EAAE,GAAG,OAAO;IAiB5G;;;;;;;;OAQG;IACI,mBAAmB,CACxB,QAAQ,EAAE,MAAM,EAChB,MAAM,GAAE,MAAuB,EAC/B,YAAY,CAAC,EAAE,MAAM,GACpB,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IA2B7B,sBAAsB,CAAC,cAAc,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,EAAE,CAAC,EAAE,MAAM,GAAE,MAAuB,GAAG,OAAO,CAAC,MAAM,CAAC;IA+BnI,KAAK,IAAI,IAAI;CAKrB"}
|