@melandlabs/memory-store 0.2.7 → 0.3.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.
@@ -2,8 +2,8 @@
2
2
  import { buildMemoryRecordEmbeddingDocument } from '@melandlabs/ai/memory';
3
3
  import { ChromaVectorStore } from '@melandlabs/rag';
4
4
  import { mkdirSync } from 'fs';
5
- import { homedir } from 'os';
6
- import { dirname, join } from 'path';
5
+ import { dirname } from 'path';
6
+ import { getOpenContextPath } from '@melandlabs/env-config';
7
7
  import { SQLiteRawMessageManager } from '@melandlabs/sqlite';
8
8
  import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
9
9
  import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
@@ -188,7 +188,7 @@ __export(sqlite_raw_message_store_exports, {
188
188
  function resolveSQLiteRawMessageDbPath(dbPath) {
189
189
  if (dbPath && dbPath.length > 0) return dbPath;
190
190
  const fromEnv = process.env.MEMORY_STORE_DB_PATH?.trim();
191
- return fromEnv && fromEnv.length > 0 ? fromEnv : DEFAULT_SQLITE_PATH;
191
+ return fromEnv && fromEnv.length > 0 ? fromEnv : getOpenContextPath("memory", "store.db");
192
192
  }
193
193
  function isSQLiteRawMessageStorageAvailable(_env) {
194
194
  return true;
@@ -222,12 +222,11 @@ async function lexicalSearchRawMessages(input) {
222
222
  }
223
223
  return mgr.lexicalSearchMessages(input);
224
224
  }
225
- var manager, DEFAULT_SQLITE_PATH;
225
+ var manager;
226
226
  var init_sqlite_raw_message_store = __esm({
227
227
  "src/storage/sqlite-raw-message-store.ts"() {
228
228
  init_chroma_memory_index();
229
229
  manager = null;
230
- DEFAULT_SQLITE_PATH = join(homedir(), ".opencontext", "memory", "store.db");
231
230
  }
232
231
  });
233
232
 
@@ -241,7 +240,7 @@ __export(raw_message_store_exports, {
241
240
  getRawMessageStorageBackend: () => getRawMessageStorageBackend,
242
241
  isRawMessageStorageAvailable: () => isRawMessageStorageAvailable
243
242
  });
244
- function resolveBackend(env) {
243
+ function resolveBackend(_env) {
245
244
  const explicit = process.env.OPENCONTEXT_MEMORY_STORE_BACKEND?.trim().toLowerCase();
246
245
  if (explicit === "postgres") return "postgres";
247
246
  return "sqlite";
@@ -374,6 +373,12 @@ function mergeUnifiedMemorySearchResults(results, limit, options = {}) {
374
373
  function normalizeUnifiedMemoryMergeStrategy(value) {
375
374
  return value === "rrf" ? "rrf" : "similarity";
376
375
  }
376
+ function normalizeUnifiedMemoryReasoningStrategy(value, defaultStrategy = "none") {
377
+ if (value === "rewrite" || value === "iterative") {
378
+ return value;
379
+ }
380
+ return defaultStrategy;
381
+ }
377
382
  var DEFAULT_RRF_K = 60;
378
383
  function mergeUnifiedMemorySearchResultsRrf(lists, limit, k = DEFAULT_RRF_K) {
379
384
  if (!Array.isArray(lists) || lists.length === 0) {
@@ -396,7 +401,13 @@ function mergeUnifiedMemorySearchResultsRrf(lists, limit, k = DEFAULT_RRF_K) {
396
401
  }
397
402
  }
398
403
  }
399
- 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) => {
400
411
  if (b.rrf !== a.rrf) {
401
412
  return b.rrf - a.rrf;
402
413
  }
@@ -441,14 +452,222 @@ function isRawMemorySemanticResult(result) {
441
452
  function deriveLexicalKeywords(query) {
442
453
  return query.toLowerCase().split(/[^\p{L}\p{N}]+/u).filter((token) => token.length >= 2).slice(0, 16);
443
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
+ }
444
655
  function createUnifiedSearch(deps = {}) {
445
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
+ }
446
664
  async function searchUnifiedMemory(input) {
447
665
  const query = input.query.trim();
448
666
  const sources = normalizeUnifiedMemorySearchSources(input.sources);
449
667
  const limit = clampUnifiedMemorySearchLimit(input.limit);
450
668
  const threshold = clampUnifiedMemorySearchThreshold(input.threshold);
451
669
  const mergeStrategy = normalizeUnifiedMemoryMergeStrategy(input.mergeStrategy);
670
+ const reasoningStrategy = resolveReasoningStrategy(input);
452
671
  const warnings = [];
453
672
  if (!query) {
454
673
  return {
@@ -460,10 +679,21 @@ function createUnifiedSearch(deps = {}) {
460
679
  };
461
680
  }
462
681
  let memorySubs;
682
+ const reasoningInfo = { strategy: reasoningStrategy };
683
+ if (input.dateFrom || input.dateTo) {
684
+ reasoningInfo.dateRange = { from: input.dateFrom, to: input.dateTo };
685
+ }
463
686
  if (sources.includes("memory")) {
464
687
  if (isRawMessageStorageAvailable()) {
465
688
  try {
466
- memorySubs = await runMemorySource(input, limit, threshold, warnings);
689
+ memorySubs = await runMemorySource(
690
+ input,
691
+ limit,
692
+ threshold,
693
+ warnings,
694
+ reasoningStrategy,
695
+ reasoningInfo
696
+ );
467
697
  } catch (error) {
468
698
  logger.warn?.("[memory-store] memory source failed:", error);
469
699
  warnings.push({
@@ -542,121 +772,155 @@ function createUnifiedSearch(deps = {}) {
542
772
  limit,
543
773
  strategy: mergeStrategy
544
774
  });
545
- return {
775
+ const output = {
546
776
  query: input.query,
547
777
  sources,
548
778
  results: merged,
549
779
  count: merged.length,
550
780
  warnings
551
781
  };
782
+ if (reasoningInfo && (reasoningInfo.strategy !== "none" || reasoningInfo.dateRange !== void 0)) {
783
+ output.reasoning = reasoningInfo;
784
+ }
785
+ return output;
552
786
  }
553
- 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
+ }
554
862
  if (typeof deps.embedQuery !== "function") {
555
863
  warnings.push({
556
864
  source: "memory",
557
865
  code: "memory_lexical_search_fallback",
558
866
  message: "Semantic search not configured, using keyword search as fallback"
559
867
  });
560
- const { lexicalSearchRawMessages: lexicalSearchRawMessages2 } = await Promise.resolve().then(() => (init_sqlite_raw_message_store(), sqlite_raw_message_store_exports));
561
- const keywords = deriveLexicalKeywords(input.query);
562
- let lexical2 = [];
563
- if (keywords.length > 0) {
564
- try {
565
- const filters2 = input.botIds && input.botIds.length > 0 ? input.botIds : [void 0];
566
- const results = await Promise.all(
567
- filters2.map(
568
- (botId) => lexicalSearchRawMessages2({
569
- userId: input.userId,
570
- keywords,
571
- limit: Math.ceil(limit / filters2.length),
572
- botId
573
- })
574
- )
575
- );
576
- lexical2 = results.flat().filter(Boolean).map(toMemoryResult);
577
- } catch (error) {
578
- logger.warn?.("[memory-store] Default lexical search failed:", error);
579
- }
580
- }
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
+ );
581
874
  return { semantic: [], lexical: lexical2 };
582
875
  }
583
- const queryEmbedding = await deps.embedQuery({
584
- userId: input.userId,
585
- query: input.query,
586
- authToken: input.authToken
587
- });
588
- const filters = input.botIds && input.botIds.length > 0 ? input.botIds.map((botId) => ({ botId })) : [{}];
589
876
  let semantic = [];
590
- if (isRawMessageChromaEnabled()) {
877
+ const embedQuery = deps.embedQuery;
878
+ if (reasoningStrategy === "rewrite" && deps.reasoning?.queryRewriter) {
591
879
  try {
592
- semantic = (await Promise.all(
593
- filters.map((filter) => {
594
- const botId = "botId" in filter ? filter.botId : void 0;
595
- return searchRawMessagesWithChroma({
596
- userId: input.userId,
597
- queryEmbedding,
598
- limit,
599
- threshold,
600
- botId
601
- });
602
- })
603
- )).flat().map(toMemoryResult);
604
- logger.log?.("[memory-store] Raw message semantic search completed", {
605
- backend: "chroma",
606
- dimensions: queryEmbedding.length,
607
- count: semantic.length
880
+ const variants = await deps.reasoning.queryRewriter.rewrite({
881
+ query: input.query,
882
+ userId: input.userId,
883
+ authToken: input.authToken
608
884
  });
609
- } catch (error) {
610
- logger.warn?.(
611
- "[memory-store] Chroma raw message search failed; falling back to database search:",
612
- 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))
893
+ );
894
+ const lists = await Promise.all(
895
+ embeddings.map(
896
+ (embedding) => runSemanticSearchForEmbedding(deps, input, embedding, limit, threshold, logger)
897
+ )
613
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;
614
908
  }
615
- } else if (typeof deps.searchRawMessagesAnn === "function") {
616
- semantic = (await Promise.all(
617
- filters.map(
618
- (filter) => deps.searchRawMessagesAnn({
619
- userId: input.userId,
620
- queryEmbedding,
621
- limit,
622
- threshold,
623
- botId: "botId" in filter ? filter.botId : void 0
624
- })
625
- )
626
- )).flat().filter(isRawMemorySemanticResult).map(toMemoryResult);
627
909
  }
628
910
  if (semantic.length === 0) {
629
- try {
630
- const { getRawMessageManager: getRawMessageManager2 } = await Promise.resolve().then(() => (init_raw_message_store(), raw_message_store_exports));
631
- const manager2 = await getRawMessageManager2();
632
- if (typeof manager2.searchMessagesSemantically === "function") {
633
- const results = await manager2.searchMessagesSemantically({
634
- userId: input.userId,
635
- queryEmbedding,
636
- limit,
637
- threshold
638
- });
639
- semantic = results.map((r) => ({
640
- type: "memory",
641
- id: r.id,
642
- content: r.content,
643
- similarity: r.similarity,
644
- metadata: r.metadata ?? {}
645
- }));
646
- }
647
- } catch (error) {
648
- logger.warn?.("[memory-store] SQLite semantic search failed:", error);
649
- }
911
+ const queryEmbedding = await embedQueryVariant(embedQuery, input, input.query);
912
+ semantic = await runSemanticSearchForEmbedding(deps, input, queryEmbedding, limit, threshold, logger);
650
913
  }
651
914
  let lexical = [];
652
- if (typeof deps.searchRawMessagesLexical === "function") {
653
- const keywords = deriveLexicalKeywords(input.query);
654
- if (keywords.length > 0) {
915
+ const keywords = deriveLexicalKeywords(input.query);
916
+ if (keywords.length > 0) {
917
+ if (typeof deps.searchRawMessagesLexical === "function") {
655
918
  try {
656
919
  const lexFilters = input.botIds && input.botIds.length > 0 ? input.botIds : [void 0];
920
+ const searchRawMessagesLexical = deps.searchRawMessagesLexical;
657
921
  lexical = (await Promise.all(
658
922
  lexFilters.map(
659
- (botId) => deps.searchRawMessagesLexical({
923
+ (botId) => searchRawMessagesLexical({
660
924
  userId: input.userId,
661
925
  keywords,
662
926
  limit,
@@ -675,20 +939,24 @@ function createUnifiedSearch(deps = {}) {
675
939
  message: error.message ?? "memory_lexical_search_failed"
676
940
  });
677
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
+ });
678
948
  }
679
- } else if (input.mergeStrategy === "rrf") {
680
- warnings.push({
681
- source: "memory",
682
- code: "memory_lexical_search_not_configured",
683
- message: "RRF merge requested but lexical search is not configured; falling back to semantic-only RRF."
684
- });
685
949
  }
686
- return { semantic, lexical };
950
+ return {
951
+ semantic: filterByDateRange(semantic, input.dateFrom, input.dateTo),
952
+ lexical: filterByDateRange(lexical, input.dateFrom, input.dateTo)
953
+ };
687
954
  }
688
955
  async function searchRawMemorySemantically(input) {
689
956
  const limit = clampUnifiedMemorySearchLimit(input.limit);
690
957
  const threshold = clampUnifiedMemorySearchThreshold(input.threshold);
691
- const sub = await runMemorySource(input, limit, threshold, []);
958
+ const reasoningStrategy = resolveReasoningStrategy(input);
959
+ const sub = await runMemorySource(input, limit, threshold, [], reasoningStrategy);
692
960
  return mergeAcrossSources({
693
961
  memorySubs: sub,
694
962
  insightHits: [],
@@ -815,6 +1083,7 @@ async function startMcpServer(options = {}) {
815
1083
  {
816
1084
  title: "Search Unified Memory",
817
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
818
1087
  inputSchema: searchSchema
819
1088
  },
820
1089
  async (args) => {
@@ -830,6 +1099,7 @@ async function startMcpServer(options = {}) {
830
1099
  {
831
1100
  title: "Write Raw Message",
832
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
833
1103
  inputSchema: writeSchema
834
1104
  },
835
1105
  async (args) => {
@@ -869,6 +1139,7 @@ async function startMcpServer(options = {}) {
869
1139
  {
870
1140
  title: "Get Raw Message",
871
1141
  description: "Fetch a single raw message by its message id.",
1142
+ // biome-ignore lint/suspicious/noExplicitAny: MCP SDK expects Zod schema type
872
1143
  inputSchema: getSchema
873
1144
  },
874
1145
  async (args) => {
@@ -1077,6 +1348,9 @@ async function buildUnified(args) {
1077
1348
  } else if (args.memoryBackend === "chroma") {
1078
1349
  if (!args.chromaUrl)
1079
1350
  throw new Error("--memory-backend=chroma requires --chroma-url <url> (or CHROMA_URL env)");
1351
+ if (!aiRag) {
1352
+ throw new Error("ai-rag modules not loaded");
1353
+ }
1080
1354
  const store = new aiRag.ChromaVectorStore({
1081
1355
  url: args.chromaUrl,
1082
1356
  collectionName: "opencontext_raw_messages"
@@ -1115,6 +1389,9 @@ async function buildUnified(args) {
1115
1389
  throw new Error("--insights-backend=chroma requires --chroma-url <url> (or CHROMA_URL env)");
1116
1390
  if (!unified.embedQuery)
1117
1391
  throw new Error("--insights-backend=chroma requires --embedding-provider local|openrouter");
1392
+ if (!aiRag) {
1393
+ throw new Error("ai-rag modules not loaded");
1394
+ }
1118
1395
  const store = new aiRag.ChromaVectorStore({
1119
1396
  url: args.chromaUrl,
1120
1397
  collectionName: args.insightsCollection
@@ -1132,6 +1409,9 @@ async function buildUnified(args) {
1132
1409
  throw new Error("--knowledge-backend=chroma requires --chroma-url <url> (or CHROMA_URL env)");
1133
1410
  if (!unified.embedQuery)
1134
1411
  throw new Error("--knowledge-backend=chroma requires --embedding-provider local|openrouter");
1412
+ if (!aiRag) {
1413
+ throw new Error("ai-rag modules not loaded");
1414
+ }
1135
1415
  const store = new aiRag.ChromaVectorStore({
1136
1416
  url: args.chromaUrl,
1137
1417
  collectionName: args.knowledgeCollection
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { d as UnifiedSearchDeps } from '../config-Ll6ktm4D.js';
2
+ import { H as UnifiedSearchDeps } from '../config-2Q092MUz.js';
3
3
 
4
4
  /**
5
5
  * Shared argv parsing + `unified.*` wiring for the memory-store CLI bins.