@melandlabs/memory-store 0.2.6 → 0.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/dist/mcp.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
- import { M as MemoryStoreConfig } from './config-BYfTzG3h.js';
2
+ import { M as MemoryStoreConfig } from './config-2Q092MUz.js';
3
3
 
4
4
  /**
5
5
  * @melandlabs/memory-store/mcp — MCP server entry.
package/dist/mcp.js CHANGED
@@ -184,16 +184,17 @@ __export(sqlite_raw_message_store_exports, {
184
184
  lexicalSearchRawMessages: () => lexicalSearchRawMessages,
185
185
  resolveSQLiteRawMessageDbPath: () => resolveSQLiteRawMessageDbPath
186
186
  });
187
- function resolveSQLiteRawMessageDbPath() {
187
+ function resolveSQLiteRawMessageDbPath(dbPath) {
188
+ if (dbPath && dbPath.length > 0) return dbPath;
188
189
  const fromEnv = process.env.MEMORY_STORE_DB_PATH?.trim();
189
190
  return fromEnv && fromEnv.length > 0 ? fromEnv : DEFAULT_SQLITE_PATH;
190
191
  }
191
192
  function isSQLiteRawMessageStorageAvailable(_env) {
192
193
  return true;
193
194
  }
194
- async function getSQLiteRawMessageManager(_env) {
195
+ async function getSQLiteRawMessageManager(options = {}) {
195
196
  if (!manager) {
196
- const dbPath = resolveSQLiteRawMessageDbPath();
197
+ const dbPath = resolveSQLiteRawMessageDbPath(options.dbPath);
197
198
  mkdirSync(dirname(dbPath), { recursive: true });
198
199
  manager = new SQLiteRawMessageManager({
199
200
  dbPath,
@@ -239,7 +240,7 @@ __export(raw_message_store_exports, {
239
240
  getRawMessageStorageBackend: () => getRawMessageStorageBackend,
240
241
  isRawMessageStorageAvailable: () => isRawMessageStorageAvailable
241
242
  });
242
- function resolveBackend(env) {
243
+ function resolveBackend(_env) {
243
244
  const explicit = process.env.OPENCONTEXT_MEMORY_STORE_BACKEND?.trim().toLowerCase();
244
245
  if (explicit === "postgres") return "postgres";
245
246
  return "sqlite";
@@ -266,33 +267,43 @@ function createRawMessageStore(options = {}) {
266
267
  }
267
268
  return pg;
268
269
  }
269
- return await getSQLiteRawMessageManager();
270
+ return await getSQLiteRawMessageManager({
271
+ dbPath: options.dbPath
272
+ });
270
273
  },
271
274
  async close() {
272
275
  await closeSQLiteRawMessageManager();
273
276
  }
274
277
  };
275
278
  }
279
+ function resolveDbPath(config) {
280
+ if (!config) return void 0;
281
+ if (config.dbPath) return config.dbPath;
282
+ if (config.db && typeof config.db === "object" && "path" in config.db) {
283
+ return config.db.path;
284
+ }
285
+ return void 0;
286
+ }
276
287
  function configureRawMessageStore(config) {
277
288
  moduleConfig = config;
278
- moduleStore = createRawMessageStore({ env: config.env });
289
+ moduleStore = createRawMessageStore({ env: config.env, dbPath: resolveDbPath(config) });
279
290
  return moduleStore;
280
291
  }
281
292
  function getRawMessageStorageBackend() {
282
293
  if (!moduleStore) {
283
- moduleStore = createRawMessageStore({ env: moduleConfig?.env });
294
+ moduleStore = createRawMessageStore({ env: moduleConfig?.env, dbPath: resolveDbPath(moduleConfig) });
284
295
  }
285
296
  return moduleStore.getBackend();
286
297
  }
287
298
  function isRawMessageStorageAvailable() {
288
299
  if (!moduleStore) {
289
- moduleStore = createRawMessageStore({ env: moduleConfig?.env });
300
+ moduleStore = createRawMessageStore({ env: moduleConfig?.env, dbPath: resolveDbPath(moduleConfig) });
290
301
  }
291
302
  return moduleStore.isAvailable();
292
303
  }
293
304
  async function getRawMessageManager() {
294
305
  if (!moduleStore) {
295
- moduleStore = createRawMessageStore({ env: moduleConfig?.env });
306
+ moduleStore = createRawMessageStore({ env: moduleConfig?.env, dbPath: resolveDbPath(moduleConfig) });
296
307
  }
297
308
  return moduleStore.getManager();
298
309
  }
@@ -362,6 +373,12 @@ function mergeUnifiedMemorySearchResults(results, limit, options = {}) {
362
373
  function normalizeUnifiedMemoryMergeStrategy(value) {
363
374
  return value === "rrf" ? "rrf" : "similarity";
364
375
  }
376
+ function normalizeUnifiedMemoryReasoningStrategy(value, defaultStrategy = "none") {
377
+ if (value === "rewrite" || value === "iterative") {
378
+ return value;
379
+ }
380
+ return defaultStrategy;
381
+ }
365
382
  var DEFAULT_RRF_K = 60;
366
383
  function mergeUnifiedMemorySearchResultsRrf(lists, limit, k = DEFAULT_RRF_K) {
367
384
  if (!Array.isArray(lists) || lists.length === 0) {
@@ -384,7 +401,13 @@ function mergeUnifiedMemorySearchResultsRrf(lists, limit, k = DEFAULT_RRF_K) {
384
401
  }
385
402
  }
386
403
  }
387
- const ranked = order.map((key) => scores.get(key)).sort((a, b) => {
404
+ const ranked = order.map((key) => {
405
+ const entry = scores.get(key);
406
+ if (!entry) {
407
+ throw new Error(`Invariant violation: missing RRF score for key "${key}"`);
408
+ }
409
+ return entry;
410
+ }).sort((a, b) => {
388
411
  if (b.rrf !== a.rrf) {
389
412
  return b.rrf - a.rrf;
390
413
  }
@@ -429,14 +452,222 @@ function isRawMemorySemanticResult(result) {
429
452
  function deriveLexicalKeywords(query) {
430
453
  return query.toLowerCase().split(/[^\p{L}\p{N}]+/u).filter((token) => token.length >= 2).slice(0, 16);
431
454
  }
455
+ function parseDateBoundary(value, role) {
456
+ const trimmed = value.trim();
457
+ if (!trimmed) {
458
+ return void 0;
459
+ }
460
+ const dateOnlyMatch = trimmed.match(/^(\d{4})-(\d{2})-(\d{2})$/);
461
+ if (dateOnlyMatch) {
462
+ const year = Number.parseInt(dateOnlyMatch[1], 10);
463
+ const month = Number.parseInt(dateOnlyMatch[2], 10) - 1;
464
+ const day = Number.parseInt(dateOnlyMatch[3], 10);
465
+ if (role === "from") {
466
+ return Date.UTC(year, month, day);
467
+ }
468
+ return Date.UTC(year, month, day + 1) - 1;
469
+ }
470
+ const parsed = Date.parse(trimmed);
471
+ if (Number.isNaN(parsed)) {
472
+ return void 0;
473
+ }
474
+ return parsed;
475
+ }
476
+ function getCandidateTimestamp(metadata) {
477
+ for (const key of ["timestamp", "createdAt", "time"]) {
478
+ const value = metadata[key];
479
+ if (typeof value === "number") {
480
+ return value;
481
+ }
482
+ if (typeof value === "string") {
483
+ const parsed = Date.parse(value);
484
+ if (!Number.isNaN(parsed)) {
485
+ return parsed;
486
+ }
487
+ }
488
+ }
489
+ return void 0;
490
+ }
491
+ function filterByDateRange(items, dateFrom, dateTo) {
492
+ if (!dateFrom && !dateTo) {
493
+ return items;
494
+ }
495
+ const fromMs = dateFrom ? parseDateBoundary(dateFrom, "from") : void 0;
496
+ const toMs = dateTo ? parseDateBoundary(dateTo, "to") : void 0;
497
+ if (fromMs === void 0 && toMs === void 0) {
498
+ return items;
499
+ }
500
+ return items.filter((item) => {
501
+ const ts = getCandidateTimestamp(item.metadata);
502
+ if (ts === void 0) {
503
+ return true;
504
+ }
505
+ if (fromMs !== void 0 && ts < fromMs) {
506
+ return false;
507
+ }
508
+ if (toMs !== void 0 && ts > toMs) {
509
+ return false;
510
+ }
511
+ return true;
512
+ });
513
+ }
514
+ async function embedQueryVariant(embedQuery, input, query) {
515
+ return embedQuery({
516
+ userId: input.userId,
517
+ query,
518
+ authToken: input.authToken
519
+ });
520
+ }
521
+ async function runSemanticSearchForEmbedding(deps, input, queryEmbedding, limit, threshold, logger) {
522
+ const filters = input.botIds && input.botIds.length > 0 ? input.botIds.map((botId) => ({ botId })) : [{}];
523
+ let semantic = [];
524
+ if (isRawMessageChromaEnabled()) {
525
+ try {
526
+ semantic = (await Promise.all(
527
+ filters.map((filter) => {
528
+ const botId = "botId" in filter ? filter.botId : void 0;
529
+ return searchRawMessagesWithChroma({
530
+ userId: input.userId,
531
+ queryEmbedding,
532
+ limit,
533
+ threshold,
534
+ botId
535
+ });
536
+ })
537
+ )).flat().map(toMemoryResult);
538
+ logger.log?.("[memory-store] Raw message semantic search completed", {
539
+ backend: "chroma",
540
+ dimensions: queryEmbedding.length,
541
+ count: semantic.length
542
+ });
543
+ } catch (error) {
544
+ logger.warn?.(
545
+ "[memory-store] Chroma raw message search failed; falling back to database search:",
546
+ error
547
+ );
548
+ }
549
+ } else if (typeof deps.searchRawMessagesAnn === "function") {
550
+ const searchRawMessagesAnn = deps.searchRawMessagesAnn;
551
+ semantic = (await Promise.all(
552
+ filters.map(
553
+ (filter) => searchRawMessagesAnn({
554
+ userId: input.userId,
555
+ queryEmbedding,
556
+ limit,
557
+ threshold,
558
+ botId: "botId" in filter ? filter.botId : void 0
559
+ })
560
+ )
561
+ )).flat().filter(isRawMemorySemanticResult).map(toMemoryResult);
562
+ }
563
+ if (semantic.length === 0) {
564
+ try {
565
+ const { getRawMessageManager: getRawMessageManager2 } = await Promise.resolve().then(() => (init_raw_message_store(), raw_message_store_exports));
566
+ const manager2 = await getRawMessageManager2();
567
+ if (typeof manager2.searchMessagesSemantically === "function") {
568
+ const results = await manager2.searchMessagesSemantically({
569
+ userId: input.userId,
570
+ queryEmbedding,
571
+ limit,
572
+ threshold
573
+ });
574
+ semantic = results.map((r) => ({
575
+ type: "memory",
576
+ id: r.id,
577
+ content: r.content,
578
+ similarity: r.similarity,
579
+ metadata: r.metadata ?? {}
580
+ }));
581
+ }
582
+ } catch (error) {
583
+ logger.warn?.("[memory-store] SQLite semantic search failed:", error);
584
+ }
585
+ }
586
+ return semantic;
587
+ }
588
+ function mergeByMaxScore(lists) {
589
+ const best = /* @__PURE__ */ new Map();
590
+ for (const list of lists) {
591
+ for (const hit of list) {
592
+ const existing = best.get(hit.id);
593
+ if (!existing || hit.similarity > existing.similarity) {
594
+ best.set(hit.id, hit);
595
+ }
596
+ }
597
+ }
598
+ return Array.from(best.values()).sort((a, b) => b.similarity - a.similarity);
599
+ }
600
+ function toIterativeRecallCandidate(hit) {
601
+ return {
602
+ id: hit.id,
603
+ content: hit.content,
604
+ similarity: hit.similarity,
605
+ metadata: hit.metadata
606
+ };
607
+ }
608
+ async function runLexicalSearchForKeywords(deps, input, keywords, limit, logger) {
609
+ if (keywords.length === 0) {
610
+ return [];
611
+ }
612
+ if (typeof deps.searchRawMessagesLexical === "function") {
613
+ try {
614
+ const filters = input.botIds && input.botIds.length > 0 ? input.botIds : [void 0];
615
+ const searchRawMessagesLexical = deps.searchRawMessagesLexical;
616
+ return (await Promise.all(
617
+ filters.map(
618
+ (botId) => searchRawMessagesLexical({
619
+ userId: input.userId,
620
+ keywords,
621
+ limit: Math.ceil(limit / filters.length),
622
+ botId
623
+ })
624
+ )
625
+ )).flat().filter(isRawMemorySemanticResult).map((hit) => ({
626
+ ...toMemoryResult(hit),
627
+ metadata: { ...hit.metadata, scoring: "bm25" }
628
+ }));
629
+ } catch (error) {
630
+ logger.warn?.("[memory-store] lexical memory search failed:", error);
631
+ }
632
+ }
633
+ try {
634
+ const { lexicalSearchRawMessages: lexicalSearchRawMessages2 } = await Promise.resolve().then(() => (init_sqlite_raw_message_store(), sqlite_raw_message_store_exports));
635
+ const filters = input.botIds && input.botIds.length > 0 ? input.botIds : [void 0];
636
+ return (await Promise.all(
637
+ filters.map(
638
+ (botId) => lexicalSearchRawMessages2({
639
+ userId: input.userId,
640
+ keywords,
641
+ limit: Math.ceil(limit / filters.length),
642
+ botId
643
+ })
644
+ )
645
+ )).flat().filter(Boolean).map(
646
+ (r) => toMemoryResult(
647
+ r
648
+ )
649
+ );
650
+ } catch (error) {
651
+ logger.warn?.("[memory-store] SQLite lexical search failed:", error);
652
+ }
653
+ return [];
654
+ }
432
655
  function createUnifiedSearch(deps = {}) {
433
656
  const logger = deps.logger ?? console;
657
+ function resolveReasoningStrategy(input) {
658
+ const requested = normalizeUnifiedMemoryReasoningStrategy(input.reasoningStrategy);
659
+ if (requested !== "none") {
660
+ return requested;
661
+ }
662
+ return normalizeUnifiedMemoryReasoningStrategy(deps.reasoning?.defaultStrategy, "none");
663
+ }
434
664
  async function searchUnifiedMemory(input) {
435
665
  const query = input.query.trim();
436
666
  const sources = normalizeUnifiedMemorySearchSources(input.sources);
437
667
  const limit = clampUnifiedMemorySearchLimit(input.limit);
438
668
  const threshold = clampUnifiedMemorySearchThreshold(input.threshold);
439
669
  const mergeStrategy = normalizeUnifiedMemoryMergeStrategy(input.mergeStrategy);
670
+ const reasoningStrategy = resolveReasoningStrategy(input);
440
671
  const warnings = [];
441
672
  if (!query) {
442
673
  return {
@@ -448,10 +679,21 @@ function createUnifiedSearch(deps = {}) {
448
679
  };
449
680
  }
450
681
  let memorySubs;
682
+ const reasoningInfo = { strategy: reasoningStrategy };
683
+ if (input.dateFrom || input.dateTo) {
684
+ reasoningInfo.dateRange = { from: input.dateFrom, to: input.dateTo };
685
+ }
451
686
  if (sources.includes("memory")) {
452
687
  if (isRawMessageStorageAvailable()) {
453
688
  try {
454
- memorySubs = await runMemorySource(input, limit, threshold, warnings);
689
+ memorySubs = await runMemorySource(
690
+ input,
691
+ limit,
692
+ threshold,
693
+ warnings,
694
+ reasoningStrategy,
695
+ reasoningInfo
696
+ );
455
697
  } catch (error) {
456
698
  logger.warn?.("[memory-store] memory source failed:", error);
457
699
  warnings.push({
@@ -530,120 +772,155 @@ function createUnifiedSearch(deps = {}) {
530
772
  limit,
531
773
  strategy: mergeStrategy
532
774
  });
533
- return {
775
+ const output = {
534
776
  query: input.query,
535
777
  sources,
536
778
  results: merged,
537
779
  count: merged.length,
538
780
  warnings
539
781
  };
782
+ if (reasoningInfo && (reasoningInfo.strategy !== "none" || reasoningInfo.dateRange !== void 0)) {
783
+ output.reasoning = reasoningInfo;
784
+ }
785
+ return output;
540
786
  }
541
- async function runMemorySource(input, limit, threshold, warnings) {
787
+ async function runMemorySource(input, limit, threshold, warnings, reasoningStrategy = "none", reasoningInfo) {
788
+ if (reasoningStrategy === "iterative" && !deps.reasoning?.iterativePlanner) {
789
+ warnings.push({
790
+ source: "memory",
791
+ code: "memory_iterative_planner_not_configured",
792
+ message: "Iterative reasoning requested but no iterativePlanner is configured; falling back to default search."
793
+ });
794
+ if (reasoningInfo) reasoningInfo.degraded = true;
795
+ }
796
+ if (reasoningStrategy === "rewrite" && !deps.reasoning?.queryRewriter) {
797
+ warnings.push({
798
+ source: "memory",
799
+ code: "memory_query_rewrite_not_configured",
800
+ message: "Rewrite reasoning requested but no queryRewriter is configured; falling back to default search."
801
+ });
802
+ if (reasoningInfo) reasoningInfo.degraded = true;
803
+ }
804
+ if (reasoningStrategy === "iterative" && deps.reasoning?.iterativePlanner) {
805
+ const planner = deps.reasoning.iterativePlanner;
806
+ const result = await planner.plan({
807
+ query: input.query,
808
+ dateFrom: input.dateFrom,
809
+ dateTo: input.dateTo,
810
+ executor: {
811
+ search: async (request) => {
812
+ const keywords2 = request.keywords.length > 0 ? request.keywords : deriveLexicalKeywords(input.query);
813
+ const lexicalHits = await runLexicalSearchForKeywords(deps, input, keywords2, limit, logger);
814
+ let semanticHits = [];
815
+ if (typeof deps.embedQuery === "function") {
816
+ try {
817
+ const semanticQuery = keywords2.join(" ");
818
+ const queryEmbedding = await embedQueryVariant(deps.embedQuery, input, semanticQuery);
819
+ semanticHits = await runSemanticSearchForEmbedding(
820
+ deps,
821
+ input,
822
+ queryEmbedding,
823
+ limit,
824
+ threshold,
825
+ logger
826
+ );
827
+ } catch (error) {
828
+ logger.warn?.(
829
+ "[memory-store] Iterative semantic search failed; using lexical results only:",
830
+ error
831
+ );
832
+ }
833
+ }
834
+ const candidates = filterByDateRange(
835
+ mergeByMaxScore([
836
+ lexicalHits.map(toIterativeRecallCandidate),
837
+ semanticHits.map(toIterativeRecallCandidate)
838
+ ]),
839
+ request.dateFrom,
840
+ request.dateTo
841
+ );
842
+ return { candidates };
843
+ }
844
+ }
845
+ });
846
+ if (reasoningInfo) {
847
+ reasoningInfo.iterations = result.stats.iterations;
848
+ reasoningInfo.evidenceCount = result.evidence.length;
849
+ if (planner.lastDegraded?.()) {
850
+ reasoningInfo.degraded = true;
851
+ }
852
+ }
853
+ const semantic2 = result.evidence.map((candidate) => ({
854
+ type: "memory",
855
+ id: candidate.id,
856
+ content: candidate.content,
857
+ similarity: candidate.similarity,
858
+ metadata: { ...candidate.metadata, reasoning: "iterative" }
859
+ }));
860
+ return { semantic: semantic2, lexical: [] };
861
+ }
542
862
  if (typeof deps.embedQuery !== "function") {
543
863
  warnings.push({
544
864
  source: "memory",
545
865
  code: "memory_lexical_search_fallback",
546
866
  message: "Semantic search not configured, using keyword search as fallback"
547
867
  });
548
- const { lexicalSearchRawMessages: lexicalSearchRawMessages2 } = await Promise.resolve().then(() => (init_sqlite_raw_message_store(), sqlite_raw_message_store_exports));
549
- const keywords = deriveLexicalKeywords(input.query);
550
- let lexical2 = [];
551
- if (keywords.length > 0) {
552
- try {
553
- const filters2 = input.botIds && input.botIds.length > 0 ? input.botIds : [void 0];
554
- const results = await Promise.all(
555
- filters2.map(
556
- (botId) => lexicalSearchRawMessages2({
557
- userId: input.userId,
558
- keywords,
559
- limit: Math.ceil(limit / filters2.length),
560
- botId
561
- })
562
- )
563
- );
564
- lexical2 = results.flat().filter(Boolean).map(toMemoryResult);
565
- } catch (error) {
566
- logger.warn?.("[memory-store] Default lexical search failed:", error);
567
- }
568
- }
868
+ const keywords2 = deriveLexicalKeywords(input.query);
869
+ const lexical2 = filterByDateRange(
870
+ await runLexicalSearchForKeywords(deps, input, keywords2, limit, logger),
871
+ input.dateFrom,
872
+ input.dateTo
873
+ );
569
874
  return { semantic: [], lexical: lexical2 };
570
875
  }
571
- const queryEmbedding = await deps.embedQuery({
572
- userId: input.userId,
573
- query: input.query,
574
- authToken: input.authToken
575
- });
576
- const filters = input.botIds && input.botIds.length > 0 ? input.botIds.map((botId) => ({ botId })) : [{}];
577
876
  let semantic = [];
578
- if (isRawMessageChromaEnabled()) {
877
+ const embedQuery = deps.embedQuery;
878
+ if (reasoningStrategy === "rewrite" && deps.reasoning?.queryRewriter) {
579
879
  try {
580
- semantic = (await Promise.all(
581
- filters.map((filter) => {
582
- const botId = "botId" in filter ? filter.botId : void 0;
583
- return searchRawMessagesWithChroma({
584
- userId: input.userId,
585
- queryEmbedding,
586
- limit,
587
- threshold,
588
- botId
589
- });
590
- })
591
- )).flat().map(toMemoryResult);
592
- logger.log?.("[memory-store] Raw message semantic search completed", {
593
- backend: "chroma",
594
- dimensions: queryEmbedding.length,
595
- count: semantic.length
880
+ const variants = await deps.reasoning.queryRewriter.rewrite({
881
+ query: input.query,
882
+ userId: input.userId,
883
+ authToken: input.authToken
596
884
  });
597
- } catch (error) {
598
- logger.warn?.(
599
- "[memory-store] Chroma raw message search failed; falling back to database search:",
600
- error
885
+ if (reasoningInfo) {
886
+ reasoningInfo.rewrittenQueries = variants;
887
+ if (deps.reasoning.queryRewriter.lastDegraded?.()) {
888
+ reasoningInfo.degraded = true;
889
+ }
890
+ }
891
+ const embeddings = await Promise.all(
892
+ variants.map((variant) => embedQueryVariant(embedQuery, input, variant))
601
893
  );
894
+ const lists = await Promise.all(
895
+ embeddings.map(
896
+ (embedding) => runSemanticSearchForEmbedding(deps, input, embedding, limit, threshold, logger)
897
+ )
898
+ );
899
+ semantic = mergeByMaxScore(lists);
900
+ } catch (error) {
901
+ logger.warn?.("[memory-store] Query rewriting failed; falling back to original query:", error);
902
+ warnings.push({
903
+ source: "memory",
904
+ code: "memory_query_rewrite_failed",
905
+ message: `Query rewriting failed: ${error.message ?? "Unknown error"}. Using original query.`
906
+ });
907
+ if (reasoningInfo) reasoningInfo.degraded = true;
602
908
  }
603
- } else if (typeof deps.searchRawMessagesAnn === "function") {
604
- semantic = (await Promise.all(
605
- filters.map(
606
- (filter) => deps.searchRawMessagesAnn({
607
- userId: input.userId,
608
- queryEmbedding,
609
- limit,
610
- threshold,
611
- botId: "botId" in filter ? filter.botId : void 0
612
- })
613
- )
614
- )).flat().filter(isRawMemorySemanticResult).map(toMemoryResult);
615
909
  }
616
910
  if (semantic.length === 0) {
617
- try {
618
- const { getRawMessageManager: getRawMessageManager2 } = await Promise.resolve().then(() => (init_raw_message_store(), raw_message_store_exports));
619
- const manager2 = await getRawMessageManager2();
620
- if (typeof manager2.searchMessagesSemantically === "function") {
621
- const results = await manager2.searchMessagesSemantically({
622
- userId: input.userId,
623
- queryEmbedding,
624
- limit
625
- });
626
- semantic = results.map((r) => ({
627
- type: "memory",
628
- id: r.id,
629
- content: r.content,
630
- similarity: r.similarity,
631
- metadata: r.metadata ?? {}
632
- }));
633
- }
634
- } catch (error) {
635
- logger.warn?.("[memory-store] SQLite semantic search failed:", error);
636
- }
911
+ const queryEmbedding = await embedQueryVariant(embedQuery, input, input.query);
912
+ semantic = await runSemanticSearchForEmbedding(deps, input, queryEmbedding, limit, threshold, logger);
637
913
  }
638
914
  let lexical = [];
639
- if (typeof deps.searchRawMessagesLexical === "function") {
640
- const keywords = deriveLexicalKeywords(input.query);
641
- if (keywords.length > 0) {
915
+ const keywords = deriveLexicalKeywords(input.query);
916
+ if (keywords.length > 0) {
917
+ if (typeof deps.searchRawMessagesLexical === "function") {
642
918
  try {
643
919
  const lexFilters = input.botIds && input.botIds.length > 0 ? input.botIds : [void 0];
920
+ const searchRawMessagesLexical = deps.searchRawMessagesLexical;
644
921
  lexical = (await Promise.all(
645
922
  lexFilters.map(
646
- (botId) => deps.searchRawMessagesLexical({
923
+ (botId) => searchRawMessagesLexical({
647
924
  userId: input.userId,
648
925
  keywords,
649
926
  limit,
@@ -662,20 +939,24 @@ function createUnifiedSearch(deps = {}) {
662
939
  message: error.message ?? "memory_lexical_search_failed"
663
940
  });
664
941
  }
942
+ } else if (input.mergeStrategy === "rrf") {
943
+ warnings.push({
944
+ source: "memory",
945
+ code: "memory_lexical_search_not_configured",
946
+ message: "RRF merge requested but lexical search is not configured; falling back to semantic-only RRF."
947
+ });
665
948
  }
666
- } else if (input.mergeStrategy === "rrf") {
667
- warnings.push({
668
- source: "memory",
669
- code: "memory_lexical_search_not_configured",
670
- message: "RRF merge requested but lexical search is not configured; falling back to semantic-only RRF."
671
- });
672
949
  }
673
- return { semantic, lexical };
950
+ return {
951
+ semantic: filterByDateRange(semantic, input.dateFrom, input.dateTo),
952
+ lexical: filterByDateRange(lexical, input.dateFrom, input.dateTo)
953
+ };
674
954
  }
675
955
  async function searchRawMemorySemantically(input) {
676
956
  const limit = clampUnifiedMemorySearchLimit(input.limit);
677
957
  const threshold = clampUnifiedMemorySearchThreshold(input.threshold);
678
- const sub = await runMemorySource(input, limit, threshold, []);
958
+ const reasoningStrategy = resolveReasoningStrategy(input);
959
+ const sub = await runMemorySource(input, limit, threshold, [], reasoningStrategy);
679
960
  return mergeAcrossSources({
680
961
  memorySubs: sub,
681
962
  insightHits: [],
@@ -802,6 +1083,7 @@ async function startMcpServer(options = {}) {
802
1083
  {
803
1084
  title: "Search Unified Memory",
804
1085
  description: "Semantic search across raw messages (always), and optionally insights and uploaded knowledge.",
1086
+ // biome-ignore lint/suspicious/noExplicitAny: MCP SDK expects Zod schema type
805
1087
  inputSchema: searchSchema
806
1088
  },
807
1089
  async (args) => {
@@ -817,6 +1099,7 @@ async function startMcpServer(options = {}) {
817
1099
  {
818
1100
  title: "Write Raw Message",
819
1101
  description: "Persist a single raw message to the user's memory store.",
1102
+ // biome-ignore lint/suspicious/noExplicitAny: MCP SDK expects Zod schema type
820
1103
  inputSchema: writeSchema
821
1104
  },
822
1105
  async (args) => {
@@ -856,6 +1139,7 @@ async function startMcpServer(options = {}) {
856
1139
  {
857
1140
  title: "Get Raw Message",
858
1141
  description: "Fetch a single raw message by its message id.",
1142
+ // biome-ignore lint/suspicious/noExplicitAny: MCP SDK expects Zod schema type
859
1143
  inputSchema: getSchema
860
1144
  },
861
1145
  async (args) => {