@melandlabs/memory-store 0.1.6 → 0.2.1
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/dist/{config-D6MzksfT.d.ts → config-BYfTzG3h.d.ts} +17 -0
- package/dist/contracts.d.ts +7 -0
- package/dist/contracts.js +0 -2
- package/dist/http.d.ts +1 -1
- package/dist/http.js +355 -67
- package/dist/index.d.ts +2 -2
- package/dist/index.js +287 -52
- package/dist/mcp.d.ts +1 -1
- package/dist/mcp.js +313 -60
- package/dist/policies/memory-graph-correction-policy.js +0 -2
- package/dist/policies/memory-graph-write-policy.js +0 -2
- package/dist/search/unified-search.d.ts +39 -3
- package/dist/search/unified-search.js +311 -56
- package/dist/server/cli-http.js +360 -66
- package/dist/server/cli-mcp.js +318 -59
- package/dist/server/cli-shared.d.ts +1 -1
- package/dist/server/cli-shared.js +0 -2
- package/dist/storage/chroma-memory-index.js +0 -2
- package/dist/storage/postgres-raw-message-factory.d.ts +1 -1
- package/dist/storage/postgres-raw-message-factory.js +0 -2
- package/dist/storage/raw-message-store.d.ts +9 -1
- package/dist/storage/raw-message-store.js +0 -2
- package/dist/storage/sqlite-raw-message-store.d.ts +15 -2
- package/dist/storage/sqlite-raw-message-store.js +8 -3
- package/dist/storage/sqlite-vector-index.d.ts +1 -1
- package/dist/storage/sqlite-vector-index.js +0 -2
- package/package.json +81 -93
- package/dist/contracts.js.map +0 -1
- package/dist/http.js.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/mcp.js.map +0 -1
- package/dist/policies/memory-graph-correction-policy.js.map +0 -1
- package/dist/policies/memory-graph-write-policy.js.map +0 -1
- package/dist/search/unified-search.js.map +0 -1
- package/dist/server/cli-http.js.map +0 -1
- package/dist/server/cli-mcp.js.map +0 -1
- package/dist/server/cli-shared.js.map +0 -1
- package/dist/storage/chroma-memory-index.js.map +0 -1
- package/dist/storage/postgres-raw-message-factory.js.map +0 -1
- package/dist/storage/raw-message-store.js.map +0 -1
- package/dist/storage/sqlite-raw-message-store.js.map +0 -1
- package/dist/storage/sqlite-vector-index.js.map +0 -1
package/dist/server/cli-mcp.js
CHANGED
|
@@ -1,13 +1,23 @@
|
|
|
1
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
2
|
import { buildMemoryRecordEmbeddingDocument } from '@melandlabs/ai/memory';
|
|
6
3
|
import { ChromaVectorStore } from '@melandlabs/rag';
|
|
7
4
|
import { mkdirSync } from 'fs';
|
|
8
5
|
import { homedir } from 'os';
|
|
9
|
-
import {
|
|
6
|
+
import { dirname, join } from 'path';
|
|
10
7
|
import { SQLiteRawMessageManager } from '@melandlabs/sqlite';
|
|
8
|
+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
9
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
10
|
+
import { z } from 'zod';
|
|
11
|
+
|
|
12
|
+
var __defProp = Object.defineProperty;
|
|
13
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
14
|
+
var __esm = (fn, res) => function __init() {
|
|
15
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
16
|
+
};
|
|
17
|
+
var __export = (target, all) => {
|
|
18
|
+
for (var name in all)
|
|
19
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
20
|
+
};
|
|
11
21
|
|
|
12
22
|
// src/contracts.ts
|
|
13
23
|
function normalizeTimestampToMs(value) {
|
|
@@ -31,14 +41,10 @@ function rawMessageToMemoryRecord(message) {
|
|
|
31
41
|
embeddingContentHash: message.embeddingContentHash
|
|
32
42
|
};
|
|
33
43
|
}
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
"RAW_MESSAGE_VECTOR_STORE_BACKEND",
|
|
39
|
-
"MEMORY_VECTOR_STORE_BACKEND",
|
|
40
|
-
"VECTOR_STORE_BACKEND"
|
|
41
|
-
];
|
|
44
|
+
var init_contracts = __esm({
|
|
45
|
+
"src/contracts.ts"() {
|
|
46
|
+
}
|
|
47
|
+
});
|
|
42
48
|
function getBackend(keys) {
|
|
43
49
|
for (const key of keys) {
|
|
44
50
|
const value = process.env[key]?.trim().toLowerCase();
|
|
@@ -141,21 +147,51 @@ async function searchRawMessagesWithChroma(input) {
|
|
|
141
147
|
metadata: result.metadata ?? {}
|
|
142
148
|
})).slice(0, input.limit);
|
|
143
149
|
}
|
|
150
|
+
var DEFAULT_RAW_MESSAGES_COLLECTION, RAW_VECTOR_BACKEND_ENV_KEYS;
|
|
151
|
+
var init_chroma_memory_index = __esm({
|
|
152
|
+
"src/storage/chroma-memory-index.ts"() {
|
|
153
|
+
init_contracts();
|
|
154
|
+
DEFAULT_RAW_MESSAGES_COLLECTION = "opencontext_raw_messages";
|
|
155
|
+
RAW_VECTOR_BACKEND_ENV_KEYS = [
|
|
156
|
+
"RAW_MESSAGE_VECTOR_STORE_BACKEND",
|
|
157
|
+
"MEMORY_VECTOR_STORE_BACKEND",
|
|
158
|
+
"VECTOR_STORE_BACKEND"
|
|
159
|
+
];
|
|
160
|
+
}
|
|
161
|
+
});
|
|
144
162
|
|
|
145
163
|
// src/storage/postgres-raw-message-factory.ts
|
|
146
|
-
var factory = null;
|
|
147
164
|
async function resolvePostgresFactory(env) {
|
|
148
|
-
return null;
|
|
165
|
+
if (!factory) return null;
|
|
166
|
+
return await factory(env);
|
|
149
167
|
}
|
|
150
168
|
function hasPostgresFactory() {
|
|
151
169
|
return factory !== null;
|
|
152
170
|
}
|
|
153
|
-
var
|
|
154
|
-
var
|
|
171
|
+
var factory;
|
|
172
|
+
var init_postgres_raw_message_factory = __esm({
|
|
173
|
+
"src/storage/postgres-raw-message-factory.ts"() {
|
|
174
|
+
factory = null;
|
|
175
|
+
}
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
// src/storage/sqlite-raw-message-store.ts
|
|
179
|
+
var sqlite_raw_message_store_exports = {};
|
|
180
|
+
__export(sqlite_raw_message_store_exports, {
|
|
181
|
+
__resetSQLiteRawMessageManagerForTests: () => __resetSQLiteRawMessageManagerForTests,
|
|
182
|
+
closeSQLiteRawMessageManager: () => closeSQLiteRawMessageManager,
|
|
183
|
+
getSQLiteRawMessageManager: () => getSQLiteRawMessageManager,
|
|
184
|
+
isSQLiteRawMessageStorageAvailable: () => isSQLiteRawMessageStorageAvailable,
|
|
185
|
+
lexicalSearchRawMessages: () => lexicalSearchRawMessages,
|
|
186
|
+
resolveSQLiteRawMessageDbPath: () => resolveSQLiteRawMessageDbPath
|
|
187
|
+
});
|
|
155
188
|
function resolveSQLiteRawMessageDbPath() {
|
|
156
189
|
const fromEnv = process.env.MEMORY_STORE_DB_PATH?.trim();
|
|
157
190
|
return fromEnv && fromEnv.length > 0 ? fromEnv : DEFAULT_SQLITE_PATH;
|
|
158
191
|
}
|
|
192
|
+
function isSQLiteRawMessageStorageAvailable(_env) {
|
|
193
|
+
return true;
|
|
194
|
+
}
|
|
159
195
|
async function getSQLiteRawMessageManager(_env) {
|
|
160
196
|
if (!manager) {
|
|
161
197
|
const dbPath = resolveSQLiteRawMessageDbPath();
|
|
@@ -175,8 +211,35 @@ async function closeSQLiteRawMessageManager() {
|
|
|
175
211
|
await manager.close();
|
|
176
212
|
manager = null;
|
|
177
213
|
}
|
|
214
|
+
function __resetSQLiteRawMessageManagerForTests() {
|
|
215
|
+
manager = null;
|
|
216
|
+
}
|
|
217
|
+
async function lexicalSearchRawMessages(input) {
|
|
218
|
+
const mgr = await getSQLiteRawMessageManager();
|
|
219
|
+
if (typeof mgr.lexicalSearchMessages !== "function") {
|
|
220
|
+
return [];
|
|
221
|
+
}
|
|
222
|
+
return mgr.lexicalSearchMessages(input);
|
|
223
|
+
}
|
|
224
|
+
var manager, DEFAULT_SQLITE_PATH;
|
|
225
|
+
var init_sqlite_raw_message_store = __esm({
|
|
226
|
+
"src/storage/sqlite-raw-message-store.ts"() {
|
|
227
|
+
init_chroma_memory_index();
|
|
228
|
+
manager = null;
|
|
229
|
+
DEFAULT_SQLITE_PATH = join(homedir(), ".opencontext", "memory", "store.db");
|
|
230
|
+
}
|
|
231
|
+
});
|
|
178
232
|
|
|
179
233
|
// src/storage/raw-message-store.ts
|
|
234
|
+
var raw_message_store_exports = {};
|
|
235
|
+
__export(raw_message_store_exports, {
|
|
236
|
+
closeRawMessageStore: () => closeRawMessageStore,
|
|
237
|
+
configureRawMessageStore: () => configureRawMessageStore,
|
|
238
|
+
createRawMessageStore: () => createRawMessageStore,
|
|
239
|
+
getRawMessageManager: () => getRawMessageManager,
|
|
240
|
+
getRawMessageStorageBackend: () => getRawMessageStorageBackend,
|
|
241
|
+
isRawMessageStorageAvailable: () => isRawMessageStorageAvailable
|
|
242
|
+
});
|
|
180
243
|
function resolveBackend(env) {
|
|
181
244
|
const explicit = process.env.OPENCONTEXT_MEMORY_STORE_BACKEND?.trim().toLowerCase();
|
|
182
245
|
if (explicit === "postgres") return "postgres";
|
|
@@ -186,7 +249,7 @@ function resolveEnv(_env) {
|
|
|
186
249
|
return {};
|
|
187
250
|
}
|
|
188
251
|
function createRawMessageStore(options = {}) {
|
|
189
|
-
resolveEnv(options.env);
|
|
252
|
+
const env = resolveEnv(options.env);
|
|
190
253
|
return {
|
|
191
254
|
getBackend() {
|
|
192
255
|
return resolveBackend();
|
|
@@ -196,7 +259,7 @@ function createRawMessageStore(options = {}) {
|
|
|
196
259
|
},
|
|
197
260
|
async getManager() {
|
|
198
261
|
if (resolveBackend() === "postgres") {
|
|
199
|
-
const pg = await resolvePostgresFactory();
|
|
262
|
+
const pg = await resolvePostgresFactory(env);
|
|
200
263
|
if (!pg) {
|
|
201
264
|
throw new Error(
|
|
202
265
|
"OPENCONTEXT_MEMORY_STORE_BACKEND=postgres but no Postgres factory is registered. Call registerPostgresFactory() at startup, or unset the env var to use the default SQLite backend."
|
|
@@ -211,14 +274,47 @@ function createRawMessageStore(options = {}) {
|
|
|
211
274
|
}
|
|
212
275
|
};
|
|
213
276
|
}
|
|
214
|
-
|
|
215
|
-
|
|
277
|
+
function configureRawMessageStore(config) {
|
|
278
|
+
moduleConfig = config;
|
|
279
|
+
moduleStore = createRawMessageStore({ env: config.env });
|
|
280
|
+
return moduleStore;
|
|
281
|
+
}
|
|
282
|
+
function getRawMessageStorageBackend() {
|
|
283
|
+
if (!moduleStore) {
|
|
284
|
+
moduleStore = createRawMessageStore({ env: moduleConfig?.env });
|
|
285
|
+
}
|
|
286
|
+
return moduleStore.getBackend();
|
|
287
|
+
}
|
|
216
288
|
function isRawMessageStorageAvailable() {
|
|
217
289
|
if (!moduleStore) {
|
|
218
290
|
moduleStore = createRawMessageStore({ env: moduleConfig?.env });
|
|
219
291
|
}
|
|
220
292
|
return moduleStore.isAvailable();
|
|
221
293
|
}
|
|
294
|
+
async function getRawMessageManager() {
|
|
295
|
+
if (!moduleStore) {
|
|
296
|
+
moduleStore = createRawMessageStore({ env: moduleConfig?.env });
|
|
297
|
+
}
|
|
298
|
+
return moduleStore.getManager();
|
|
299
|
+
}
|
|
300
|
+
async function closeRawMessageStore() {
|
|
301
|
+
if (!moduleStore) return;
|
|
302
|
+
await moduleStore.close();
|
|
303
|
+
moduleStore = null;
|
|
304
|
+
}
|
|
305
|
+
var moduleStore, moduleConfig;
|
|
306
|
+
var init_raw_message_store = __esm({
|
|
307
|
+
"src/storage/raw-message-store.ts"() {
|
|
308
|
+
init_postgres_raw_message_factory();
|
|
309
|
+
init_sqlite_raw_message_store();
|
|
310
|
+
moduleStore = null;
|
|
311
|
+
moduleConfig = null;
|
|
312
|
+
}
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
// src/search/unified-search.ts
|
|
316
|
+
init_chroma_memory_index();
|
|
317
|
+
init_raw_message_store();
|
|
222
318
|
|
|
223
319
|
// src/search/utilities.ts
|
|
224
320
|
var DEFAULT_LIMIT = 10;
|
|
@@ -248,7 +344,14 @@ function clampUnifiedMemorySearchThreshold(threshold) {
|
|
|
248
344
|
}
|
|
249
345
|
return Math.min(1, Math.max(-1, parsed));
|
|
250
346
|
}
|
|
251
|
-
function mergeUnifiedMemorySearchResults(results, limit) {
|
|
347
|
+
function mergeUnifiedMemorySearchResults(results, limit, options = {}) {
|
|
348
|
+
const strategy = normalizeUnifiedMemoryMergeStrategy(options.strategy);
|
|
349
|
+
if (strategy === "rrf") {
|
|
350
|
+
const lists = options.rankedLists ?? [
|
|
351
|
+
{ name: "memory-semantic", hits: results }
|
|
352
|
+
];
|
|
353
|
+
return mergeUnifiedMemorySearchResultsRrf(lists, limit);
|
|
354
|
+
}
|
|
252
355
|
return [...results].sort((a, b) => {
|
|
253
356
|
const scoreDelta = b.similarity - a.similarity;
|
|
254
357
|
if (scoreDelta !== 0) {
|
|
@@ -257,6 +360,42 @@ function mergeUnifiedMemorySearchResults(results, limit) {
|
|
|
257
360
|
return a.type.localeCompare(b.type) || a.id.localeCompare(b.id);
|
|
258
361
|
}).slice(0, limit);
|
|
259
362
|
}
|
|
363
|
+
function normalizeUnifiedMemoryMergeStrategy(value) {
|
|
364
|
+
return value === "rrf" ? "rrf" : "similarity";
|
|
365
|
+
}
|
|
366
|
+
var DEFAULT_RRF_K = 60;
|
|
367
|
+
function mergeUnifiedMemorySearchResultsRrf(lists, limit, k = DEFAULT_RRF_K) {
|
|
368
|
+
if (!Array.isArray(lists) || lists.length === 0) {
|
|
369
|
+
return [];
|
|
370
|
+
}
|
|
371
|
+
const safeK = Number.isFinite(k) && k > 0 ? k : DEFAULT_RRF_K;
|
|
372
|
+
const scores = /* @__PURE__ */ new Map();
|
|
373
|
+
const order = [];
|
|
374
|
+
for (const list of lists) {
|
|
375
|
+
for (let index = 0; index < list.hits.length; index += 1) {
|
|
376
|
+
const hit = list.hits[index];
|
|
377
|
+
const key = `${hit.type}::${hit.id}`;
|
|
378
|
+
const contribution = 1 / (safeK + index + 1);
|
|
379
|
+
const existing = scores.get(key);
|
|
380
|
+
if (existing) {
|
|
381
|
+
existing.rrf += contribution;
|
|
382
|
+
} else {
|
|
383
|
+
scores.set(key, { hit, rrf: contribution });
|
|
384
|
+
order.push(key);
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
const ranked = order.map((key) => scores.get(key)).sort((a, b) => {
|
|
389
|
+
if (b.rrf !== a.rrf) {
|
|
390
|
+
return b.rrf - a.rrf;
|
|
391
|
+
}
|
|
392
|
+
return a.hit.type.localeCompare(b.hit.type) || a.hit.id.localeCompare(b.hit.id);
|
|
393
|
+
}).slice(0, limit);
|
|
394
|
+
return ranked.map(({ hit, rrf }) => ({
|
|
395
|
+
...hit,
|
|
396
|
+
metadata: { ...hit.metadata, rrfScore: rrf }
|
|
397
|
+
}));
|
|
398
|
+
}
|
|
260
399
|
function toKnowledgeResult(result) {
|
|
261
400
|
return {
|
|
262
401
|
type: "knowledge",
|
|
@@ -288,6 +427,9 @@ function isRawMemorySemanticResult(result) {
|
|
|
288
427
|
}
|
|
289
428
|
|
|
290
429
|
// src/search/unified-search.ts
|
|
430
|
+
function deriveLexicalKeywords(query) {
|
|
431
|
+
return query.toLowerCase().split(/[^\p{L}\p{N}]+/u).filter((token) => token.length >= 2).slice(0, 16);
|
|
432
|
+
}
|
|
291
433
|
function createUnifiedSearch(deps = {}) {
|
|
292
434
|
const logger = deps.logger ?? console;
|
|
293
435
|
async function searchUnifiedMemory(input) {
|
|
@@ -295,8 +437,8 @@ function createUnifiedSearch(deps = {}) {
|
|
|
295
437
|
const sources = normalizeUnifiedMemorySearchSources(input.sources);
|
|
296
438
|
const limit = clampUnifiedMemorySearchLimit(input.limit);
|
|
297
439
|
const threshold = clampUnifiedMemorySearchThreshold(input.threshold);
|
|
440
|
+
const mergeStrategy = normalizeUnifiedMemoryMergeStrategy(input.mergeStrategy);
|
|
298
441
|
const warnings = [];
|
|
299
|
-
const results = [];
|
|
300
442
|
if (!query) {
|
|
301
443
|
return {
|
|
302
444
|
query,
|
|
@@ -306,17 +448,17 @@ function createUnifiedSearch(deps = {}) {
|
|
|
306
448
|
warnings
|
|
307
449
|
};
|
|
308
450
|
}
|
|
451
|
+
let memorySubs;
|
|
309
452
|
if (sources.includes("memory")) {
|
|
310
453
|
if (isRawMessageStorageAvailable()) {
|
|
311
454
|
try {
|
|
312
|
-
|
|
313
|
-
results.push(...memoryHits);
|
|
455
|
+
memorySubs = await runMemorySource(input, limit, threshold, warnings);
|
|
314
456
|
} catch (error) {
|
|
315
457
|
logger.warn?.("[memory-store] memory source failed:", error);
|
|
316
458
|
warnings.push({
|
|
317
459
|
source: "memory",
|
|
318
460
|
code: "memory_search_failed",
|
|
319
|
-
message: error.message ?? "
|
|
461
|
+
message: `Memory search failed: ${error.message ?? "Unknown error"}. Using keyword search fallback.`
|
|
320
462
|
});
|
|
321
463
|
}
|
|
322
464
|
} else {
|
|
@@ -327,9 +469,10 @@ function createUnifiedSearch(deps = {}) {
|
|
|
327
469
|
});
|
|
328
470
|
}
|
|
329
471
|
}
|
|
472
|
+
const insightHits = [];
|
|
330
473
|
if (sources.includes("insights") && typeof deps.searchInsights === "function") {
|
|
331
474
|
try {
|
|
332
|
-
const
|
|
475
|
+
const hits = await deps.searchInsights({
|
|
333
476
|
userId: input.userId,
|
|
334
477
|
query: input.query,
|
|
335
478
|
limit,
|
|
@@ -338,8 +481,8 @@ function createUnifiedSearch(deps = {}) {
|
|
|
338
481
|
includeArchived: input.includeArchivedInsights,
|
|
339
482
|
authToken: input.authToken
|
|
340
483
|
});
|
|
341
|
-
for (const hit of
|
|
342
|
-
|
|
484
|
+
for (const hit of hits) {
|
|
485
|
+
insightHits.push({
|
|
343
486
|
type: "insight",
|
|
344
487
|
id: hit.id,
|
|
345
488
|
content: hit.content,
|
|
@@ -355,16 +498,11 @@ function createUnifiedSearch(deps = {}) {
|
|
|
355
498
|
message: error.message ?? "insights_search_failed"
|
|
356
499
|
});
|
|
357
500
|
}
|
|
358
|
-
} else if (sources.includes("insights"))
|
|
359
|
-
|
|
360
|
-
source: "insights",
|
|
361
|
-
code: "insights_search_not_configured",
|
|
362
|
-
message: "Insights search is not configured for this host."
|
|
363
|
-
});
|
|
364
|
-
}
|
|
501
|
+
} else if (sources.includes("insights")) ;
|
|
502
|
+
const knowledgeHits = [];
|
|
365
503
|
if (sources.includes("knowledge") && typeof deps.searchKnowledge === "function") {
|
|
366
504
|
try {
|
|
367
|
-
const
|
|
505
|
+
const hits = await deps.searchKnowledge({
|
|
368
506
|
userId: input.userId,
|
|
369
507
|
query: input.query,
|
|
370
508
|
options: {
|
|
@@ -374,8 +512,8 @@ function createUnifiedSearch(deps = {}) {
|
|
|
374
512
|
},
|
|
375
513
|
authToken: input.authToken
|
|
376
514
|
});
|
|
377
|
-
for (const hit of
|
|
378
|
-
|
|
515
|
+
for (const hit of hits) {
|
|
516
|
+
knowledgeHits.push(toKnowledgeResult(hit));
|
|
379
517
|
}
|
|
380
518
|
} catch (error) {
|
|
381
519
|
logger.warn?.("[memory-store] knowledge source failed:", error);
|
|
@@ -385,14 +523,14 @@ function createUnifiedSearch(deps = {}) {
|
|
|
385
523
|
message: error.message ?? "knowledge_search_failed"
|
|
386
524
|
});
|
|
387
525
|
}
|
|
388
|
-
} else if (sources.includes("knowledge"))
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
526
|
+
} else if (sources.includes("knowledge")) ;
|
|
527
|
+
const merged = mergeAcrossSources({
|
|
528
|
+
memorySubs,
|
|
529
|
+
insightHits,
|
|
530
|
+
knowledgeHits,
|
|
531
|
+
limit,
|
|
532
|
+
strategy: mergeStrategy
|
|
533
|
+
});
|
|
396
534
|
return {
|
|
397
535
|
query: input.query,
|
|
398
536
|
sources,
|
|
@@ -401,9 +539,35 @@ function createUnifiedSearch(deps = {}) {
|
|
|
401
539
|
warnings
|
|
402
540
|
};
|
|
403
541
|
}
|
|
404
|
-
async function runMemorySource(input, limit, threshold) {
|
|
542
|
+
async function runMemorySource(input, limit, threshold, warnings) {
|
|
405
543
|
if (typeof deps.embedQuery !== "function") {
|
|
406
|
-
|
|
544
|
+
warnings.push({
|
|
545
|
+
source: "memory",
|
|
546
|
+
code: "memory_lexical_search_fallback",
|
|
547
|
+
message: "Semantic search not configured, using keyword search as fallback"
|
|
548
|
+
});
|
|
549
|
+
const { lexicalSearchRawMessages: lexicalSearchRawMessages2 } = await Promise.resolve().then(() => (init_sqlite_raw_message_store(), sqlite_raw_message_store_exports));
|
|
550
|
+
const keywords = deriveLexicalKeywords(input.query);
|
|
551
|
+
let lexical2 = [];
|
|
552
|
+
if (keywords.length > 0) {
|
|
553
|
+
try {
|
|
554
|
+
const filters2 = input.botIds && input.botIds.length > 0 ? input.botIds : [void 0];
|
|
555
|
+
const results = await Promise.all(
|
|
556
|
+
filters2.map(
|
|
557
|
+
(botId) => lexicalSearchRawMessages2({
|
|
558
|
+
userId: input.userId,
|
|
559
|
+
keywords,
|
|
560
|
+
limit: Math.ceil(limit / filters2.length),
|
|
561
|
+
botId
|
|
562
|
+
})
|
|
563
|
+
)
|
|
564
|
+
);
|
|
565
|
+
lexical2 = results.flat().filter(Boolean).map(toMemoryResult);
|
|
566
|
+
} catch (error) {
|
|
567
|
+
logger.warn?.("[memory-store] Default lexical search failed:", error);
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
return { semantic: [], lexical: lexical2 };
|
|
407
571
|
}
|
|
408
572
|
const queryEmbedding = await deps.embedQuery({
|
|
409
573
|
userId: input.userId,
|
|
@@ -411,9 +575,10 @@ function createUnifiedSearch(deps = {}) {
|
|
|
411
575
|
authToken: input.authToken
|
|
412
576
|
});
|
|
413
577
|
const filters = input.botIds && input.botIds.length > 0 ? input.botIds.map((botId) => ({ botId })) : [{}];
|
|
578
|
+
let semantic = [];
|
|
414
579
|
if (isRawMessageChromaEnabled()) {
|
|
415
580
|
try {
|
|
416
|
-
|
|
581
|
+
semantic = (await Promise.all(
|
|
417
582
|
filters.map((filter) => {
|
|
418
583
|
const botId = "botId" in filter ? filter.botId : void 0;
|
|
419
584
|
return searchRawMessagesWithChroma({
|
|
@@ -428,18 +593,16 @@ function createUnifiedSearch(deps = {}) {
|
|
|
428
593
|
logger.log?.("[memory-store] Raw message semantic search completed", {
|
|
429
594
|
backend: "chroma",
|
|
430
595
|
dimensions: queryEmbedding.length,
|
|
431
|
-
count:
|
|
596
|
+
count: semantic.length
|
|
432
597
|
});
|
|
433
|
-
return chromaHits;
|
|
434
598
|
} catch (error) {
|
|
435
599
|
logger.warn?.(
|
|
436
600
|
"[memory-store] Chroma raw message search failed; falling back to database search:",
|
|
437
601
|
error
|
|
438
602
|
);
|
|
439
603
|
}
|
|
440
|
-
}
|
|
441
|
-
|
|
442
|
-
const rows = (await Promise.all(
|
|
604
|
+
} else if (typeof deps.searchRawMessagesAnn === "function") {
|
|
605
|
+
semantic = (await Promise.all(
|
|
443
606
|
filters.map(
|
|
444
607
|
(filter) => deps.searchRawMessagesAnn({
|
|
445
608
|
userId: input.userId,
|
|
@@ -450,19 +613,113 @@ function createUnifiedSearch(deps = {}) {
|
|
|
450
613
|
})
|
|
451
614
|
)
|
|
452
615
|
)).flat().filter(isRawMemorySemanticResult).map(toMemoryResult);
|
|
453
|
-
return rows;
|
|
454
616
|
}
|
|
455
|
-
|
|
617
|
+
if (semantic.length === 0) {
|
|
618
|
+
try {
|
|
619
|
+
const { getRawMessageManager: getRawMessageManager2 } = await Promise.resolve().then(() => (init_raw_message_store(), raw_message_store_exports));
|
|
620
|
+
const manager2 = await getRawMessageManager2();
|
|
621
|
+
if (typeof manager2.searchMessagesSemantically === "function") {
|
|
622
|
+
const results = await manager2.searchMessagesSemantically({
|
|
623
|
+
userId: input.userId,
|
|
624
|
+
queryEmbedding,
|
|
625
|
+
limit
|
|
626
|
+
});
|
|
627
|
+
semantic = results.map((r) => ({
|
|
628
|
+
type: "memory",
|
|
629
|
+
id: r.id,
|
|
630
|
+
content: r.content,
|
|
631
|
+
similarity: r.similarity,
|
|
632
|
+
metadata: r.metadata ?? {}
|
|
633
|
+
}));
|
|
634
|
+
}
|
|
635
|
+
} catch (error) {
|
|
636
|
+
logger.warn?.("[memory-store] SQLite semantic search failed:", error);
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
let lexical = [];
|
|
640
|
+
if (typeof deps.searchRawMessagesLexical === "function") {
|
|
641
|
+
const keywords = deriveLexicalKeywords(input.query);
|
|
642
|
+
if (keywords.length > 0) {
|
|
643
|
+
try {
|
|
644
|
+
const lexFilters = input.botIds && input.botIds.length > 0 ? input.botIds : [void 0];
|
|
645
|
+
lexical = (await Promise.all(
|
|
646
|
+
lexFilters.map(
|
|
647
|
+
(botId) => deps.searchRawMessagesLexical({
|
|
648
|
+
userId: input.userId,
|
|
649
|
+
keywords,
|
|
650
|
+
limit,
|
|
651
|
+
botId
|
|
652
|
+
})
|
|
653
|
+
)
|
|
654
|
+
)).flat().filter(isRawMemorySemanticResult).map((hit) => ({
|
|
655
|
+
...toMemoryResult(hit),
|
|
656
|
+
metadata: { ...hit.metadata, scoring: "bm25" }
|
|
657
|
+
}));
|
|
658
|
+
} catch (error) {
|
|
659
|
+
logger.warn?.("[memory-store] lexical memory search failed:", error);
|
|
660
|
+
warnings.push({
|
|
661
|
+
source: "memory",
|
|
662
|
+
code: "memory_lexical_search_failed",
|
|
663
|
+
message: error.message ?? "memory_lexical_search_failed"
|
|
664
|
+
});
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
} else if (input.mergeStrategy === "rrf") {
|
|
668
|
+
warnings.push({
|
|
669
|
+
source: "memory",
|
|
670
|
+
code: "memory_lexical_search_not_configured",
|
|
671
|
+
message: "RRF merge requested but lexical search is not configured; falling back to semantic-only RRF."
|
|
672
|
+
});
|
|
673
|
+
}
|
|
674
|
+
return { semantic, lexical };
|
|
456
675
|
}
|
|
457
676
|
async function searchRawMemorySemantically(input) {
|
|
458
677
|
const limit = clampUnifiedMemorySearchLimit(input.limit);
|
|
459
678
|
const threshold = clampUnifiedMemorySearchThreshold(input.threshold);
|
|
460
|
-
|
|
679
|
+
const sub = await runMemorySource(input, limit, threshold, []);
|
|
680
|
+
return mergeAcrossSources({
|
|
681
|
+
memorySubs: sub,
|
|
682
|
+
insightHits: [],
|
|
683
|
+
knowledgeHits: [],
|
|
684
|
+
limit,
|
|
685
|
+
strategy: normalizeUnifiedMemoryMergeStrategy(input.mergeStrategy)
|
|
686
|
+
});
|
|
461
687
|
}
|
|
462
688
|
return { searchUnifiedMemory, searchRawMemorySemantically };
|
|
463
689
|
}
|
|
690
|
+
function mergeAcrossSources(input) {
|
|
691
|
+
const memorySubs = input.memorySubs ?? { semantic: [], lexical: [] };
|
|
692
|
+
const all = [
|
|
693
|
+
...memorySubs.semantic,
|
|
694
|
+
...memorySubs.lexical,
|
|
695
|
+
...input.insightHits,
|
|
696
|
+
...input.knowledgeHits
|
|
697
|
+
];
|
|
698
|
+
if (input.strategy !== "rrf") {
|
|
699
|
+
return mergeUnifiedMemorySearchResults(all, input.limit);
|
|
700
|
+
}
|
|
701
|
+
const lists = [];
|
|
702
|
+
if (memorySubs.semantic.length > 0) {
|
|
703
|
+
lists.push({ name: "memory-semantic", hits: memorySubs.semantic });
|
|
704
|
+
}
|
|
705
|
+
if (memorySubs.lexical.length > 0) {
|
|
706
|
+
lists.push({ name: "memory-bm25", hits: memorySubs.lexical });
|
|
707
|
+
}
|
|
708
|
+
if (input.insightHits.length > 0) {
|
|
709
|
+
lists.push({ name: "insights", hits: input.insightHits });
|
|
710
|
+
}
|
|
711
|
+
if (input.knowledgeHits.length > 0) {
|
|
712
|
+
lists.push({ name: "knowledge", hits: input.knowledgeHits });
|
|
713
|
+
}
|
|
714
|
+
if (lists.length <= 1) {
|
|
715
|
+
return mergeUnifiedMemorySearchResults(all, input.limit);
|
|
716
|
+
}
|
|
717
|
+
return mergeUnifiedMemorySearchResultsRrf(lists, input.limit);
|
|
718
|
+
}
|
|
464
719
|
|
|
465
720
|
// src/mcp.ts
|
|
721
|
+
init_chroma_memory_index();
|
|
722
|
+
init_raw_message_store();
|
|
466
723
|
var DEFAULT_NAME = "@melandlabs/memory-store";
|
|
467
724
|
var DEFAULT_VERSION = "0.1.0";
|
|
468
725
|
async function embedMissingMessages(messages, deps) {
|
|
@@ -621,7 +878,11 @@ async function startMcpServer(options = {}) {
|
|
|
621
878
|
return server;
|
|
622
879
|
}
|
|
623
880
|
|
|
881
|
+
// src/server/cli-shared.ts
|
|
882
|
+
init_raw_message_store();
|
|
883
|
+
|
|
624
884
|
// src/storage/sqlite-vector-index.ts
|
|
885
|
+
init_sqlite_raw_message_store();
|
|
625
886
|
function getMemoryVectorStoreBackend() {
|
|
626
887
|
const backend = (process.env.INSIGHT_VECTOR_STORE_BACKEND || process.env.MEMORY_VECTOR_STORE_BACKEND || process.env.VECTOR_STORE_BACKEND || "sqlite-vec").toLowerCase();
|
|
627
888
|
return backend === "chroma" ? "chroma" : "sqlite-vec";
|
|
@@ -976,5 +1237,3 @@ main().catch((error) => {
|
|
|
976
1237
|
console.error("[memory-store/mcp] fatal:", error);
|
|
977
1238
|
process.exit(1);
|
|
978
1239
|
});
|
|
979
|
-
//# sourceMappingURL=cli-mcp.js.map
|
|
980
|
-
//# sourceMappingURL=cli-mcp.js.map
|
|
@@ -245,5 +245,3 @@ async function searchInsightsWithChroma(input) {
|
|
|
245
245
|
}
|
|
246
246
|
|
|
247
247
|
export { isInsightChromaEnabled, isRawMessageChromaEnabled, searchInsightsWithChroma, searchRawMessagesWithChroma, upsertInsightsToChroma, upsertRawMessagesToChroma };
|
|
248
|
-
//# sourceMappingURL=chroma-memory-index.js.map
|
|
249
|
-
//# sourceMappingURL=chroma-memory-index.js.map
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { RawMessageStorageManager } from '@melandlabs/indexeddb/storage';
|
|
2
|
-
import { b as MemoryStoreEnv, M as MemoryStoreConfig } from '../config-
|
|
2
|
+
import { b as MemoryStoreEnv, M as MemoryStoreConfig } from '../config-BYfTzG3h.js';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Top-level raw-message store facade.
|
|
@@ -34,6 +34,14 @@ type RawMessageStorageManagerWithSearch = RawMessageStorageManager & {
|
|
|
34
34
|
startTime?: number;
|
|
35
35
|
endTime?: number;
|
|
36
36
|
}) => Promise<unknown[]>;
|
|
37
|
+
lexicalSearchMessages?: (input: {
|
|
38
|
+
userId: string;
|
|
39
|
+
keywords: string[];
|
|
40
|
+
limit?: number;
|
|
41
|
+
includeArchived?: boolean;
|
|
42
|
+
platform?: string;
|
|
43
|
+
botId?: string;
|
|
44
|
+
}) => Promise<unknown[]>;
|
|
37
45
|
};
|
|
38
46
|
interface CreateRawMessageStoreOptions {
|
|
39
47
|
env?: MemoryStoreEnv;
|
|
@@ -125,5 +125,3 @@ async function closeRawMessageStore() {
|
|
|
125
125
|
}
|
|
126
126
|
|
|
127
127
|
export { closeRawMessageStore, configureRawMessageStore, createRawMessageStore, getRawMessageManager, getRawMessageStorageBackend, isRawMessageStorageAvailable };
|
|
128
|
-
//# sourceMappingURL=raw-message-store.js.map
|
|
129
|
-
//# sourceMappingURL=raw-message-store.js.map
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { SQLiteRawMessageManager } from '@melandlabs/sqlite';
|
|
2
|
-
import { b as MemoryStoreEnv } from '../config-
|
|
2
|
+
import { b as MemoryStoreEnv } from '../config-BYfTzG3h.js';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* SQLite-backed raw message store.
|
|
@@ -30,5 +30,18 @@ declare function closeSQLiteRawMessageManager(): Promise<void>;
|
|
|
30
30
|
* Test-only: reset the singleton. Used by vitest between test cases.
|
|
31
31
|
*/
|
|
32
32
|
declare function __resetSQLiteRawMessageManagerForTests(): void;
|
|
33
|
+
/**
|
|
34
|
+
* BM25 lexical proxy over the SQLite FTS5 index. Mirrors
|
|
35
|
+
* `SQLiteRawMessageManager.lexicalSearchMessages` so callers downstream of the
|
|
36
|
+
* raw-message facade can opt in to a second ranking signal.
|
|
37
|
+
*/
|
|
38
|
+
declare function lexicalSearchRawMessages(input: {
|
|
39
|
+
userId: string;
|
|
40
|
+
keywords: string[];
|
|
41
|
+
limit?: number;
|
|
42
|
+
includeArchived?: boolean;
|
|
43
|
+
platform?: string;
|
|
44
|
+
botId?: string;
|
|
45
|
+
}): Promise<unknown[]>;
|
|
33
46
|
|
|
34
|
-
export { __resetSQLiteRawMessageManagerForTests, closeSQLiteRawMessageManager, getSQLiteRawMessageManager, isSQLiteRawMessageStorageAvailable, resolveSQLiteRawMessageDbPath };
|
|
47
|
+
export { __resetSQLiteRawMessageManagerForTests, closeSQLiteRawMessageManager, getSQLiteRawMessageManager, isSQLiteRawMessageStorageAvailable, lexicalSearchRawMessages, resolveSQLiteRawMessageDbPath };
|