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