@anuma/sdk 3.11.1 → 3.11.3

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.
@@ -18464,6 +18464,11 @@ function applyMMR(candidates, k, lambda = 0.5) {
18464
18464
  return selected;
18465
18465
  }
18466
18466
 
18467
+ // src/lib/memory/recallConstants.ts
18468
+ var RECALL_TOOL_NAME = "recall_memory";
18469
+ var RECALL_MAX_LIMIT = 50;
18470
+ var EMBEDDINGS_DEGRADED_EMPTY = "Semantic memory search is temporarily unavailable, so only keyword matching ran and it found no matches. Do not conclude the user has no such memory \u2014 say the memory lookup was degraded, or retry with different keywords.";
18471
+
18467
18472
  // src/lib/memory/recency.ts
18468
18473
  var DAY_MS = 1e3 * 60 * 60 * 24;
18469
18474
  var DEFAULT_PER_YEAR_DECAY = 0.2;
@@ -19446,6 +19451,24 @@ async function eagerEmbedContent(content, embeddingOptions, cache, vaultCtx, mem
19446
19451
  );
19447
19452
  }
19448
19453
  }
19454
+ async function embedQueryOrDegrade(query, embeddingOptions, onDegraded) {
19455
+ try {
19456
+ const embedding = await generateEmbedding(query, embeddingOptions);
19457
+ if (embedding.length === 0) {
19458
+ getLogger().warn(
19459
+ "memoryVault: query embedding came back empty \u2014 falling back to BM25-only ranking for this search"
19460
+ );
19461
+ onDegraded?.();
19462
+ }
19463
+ return embedding;
19464
+ } catch (err) {
19465
+ getLogger().warn(
19466
+ "memoryVault: query embedding failed \u2014 falling back to BM25-only ranking for this search: " + (err instanceof Error ? err.message : String(err))
19467
+ );
19468
+ onDegraded?.();
19469
+ return [];
19470
+ }
19471
+ }
19449
19472
  function admitVaultProjections(queryEmbedding, vectored, k) {
19450
19473
  return vectored.map((it) => ({ it, score: cosineSimilarity(queryEmbedding, it.embedding) })).sort((a, b) => b.score - a.score || b.it.updatedAt.getTime() - a.it.updatedAt.getTime()).slice(0, k).map((s) => s.it.uniqueId);
19451
19474
  }
@@ -19457,23 +19480,36 @@ async function buildProjectedCorpus(query, vaultCtx, embeddingOptions, cache, qu
19457
19480
  const hydrateOpts = queryOpts.includeArchived ? { includeArchived: true } : void 0;
19458
19481
  const vaultSize = keys.length;
19459
19482
  if (keys.length === 0) {
19460
- return { memories: [], embeddedItems: [], queryEmbedding: [], vaultSize: 0 };
19483
+ return {
19484
+ memories: [],
19485
+ embeddedItems: [],
19486
+ queryEmbedding: [],
19487
+ vaultSize: 0,
19488
+ laneEmbedFailed: false
19489
+ };
19461
19490
  }
19462
- const queryEmbedding = await generateEmbedding(query, embeddingOptions);
19491
+ const queryEmbedding = await embedQueryOrDegrade(
19492
+ query,
19493
+ embeddingOptions,
19494
+ opts.onEmbeddingDegraded
19495
+ );
19496
+ const embeddingsDegraded = queryEmbedding.length === 0;
19463
19497
  const currentModel = embeddingOptions.model ?? DEFAULT_API_EMBEDDING_MODEL;
19464
19498
  const vectored = [];
19465
19499
  const missIds = [];
19466
19500
  const noVectorIds = [];
19467
19501
  const keyById = new Map(keys.map((k2) => [k2.uniqueId, k2]));
19468
- for (const k2 of keys) {
19469
- const cached = cache.get(k2.uniqueId);
19470
- if (cached && cached.length === queryEmbedding.length) {
19471
- vectored.push({ uniqueId: k2.uniqueId, embedding: cached, updatedAt: k2.updatedAt });
19472
- continue;
19502
+ if (!embeddingsDegraded) {
19503
+ for (const k2 of keys) {
19504
+ const cached = cache.get(k2.uniqueId);
19505
+ if (cached && cached.length === queryEmbedding.length) {
19506
+ vectored.push({ uniqueId: k2.uniqueId, embedding: cached, updatedAt: k2.updatedAt });
19507
+ continue;
19508
+ }
19509
+ const modelCompatible = (k2.embeddingModel ?? currentModel) === currentModel;
19510
+ if (modelCompatible) missIds.push(k2.uniqueId);
19511
+ else noVectorIds.push(k2.uniqueId);
19473
19512
  }
19474
- const modelCompatible = (k2.embeddingModel ?? currentModel) === currentModel;
19475
- if (modelCompatible) missIds.push(k2.uniqueId);
19476
- else noVectorIds.push(k2.uniqueId);
19477
19513
  }
19478
19514
  if (missIds.length > 0) {
19479
19515
  const rows = await getVaultEmbeddingsByIdsOp(vaultCtx, missIds, hydrateOpts);
@@ -19494,6 +19530,7 @@ async function buildProjectedCorpus(query, vaultCtx, embeddingOptions, cache, qu
19494
19530
  }
19495
19531
  for (const id of missIds) if (!gotVector.has(id)) noVectorIds.push(id);
19496
19532
  }
19533
+ let laneEmbedFailed = false;
19497
19534
  if (noVectorIds.length > 0) {
19498
19535
  const laneIds = noVectorIds.slice(0, opts.unembeddedCap);
19499
19536
  if (noVectorIds.length > laneIds.length) {
@@ -19505,26 +19542,44 @@ async function buildProjectedCorpus(query, vaultCtx, embeddingOptions, cache, qu
19505
19542
  (m) => !isEncrypted(m.content)
19506
19543
  );
19507
19544
  if (laneRows.length > 0) {
19508
- const laneVecs = await generateEmbeddings(
19509
- laneRows.map((m) => m.content),
19510
- embeddingOptions
19511
- );
19512
- laneRows.forEach((m, i) => {
19513
- const vec = Float32Array.from(laneVecs[i]);
19514
- cache.set(m.uniqueId, vec);
19515
- vectored.push({ uniqueId: m.uniqueId, embedding: vec, updatedAt: m.updatedAt });
19516
- updateVaultMemoryEmbeddingOp(
19517
- vaultCtx,
19518
- m.uniqueId,
19519
- JSON.stringify(laneVecs[i]),
19520
- currentModel
19521
- ).catch(() => {
19545
+ let laneVecs;
19546
+ try {
19547
+ laneVecs = await generateEmbeddings(
19548
+ laneRows.map((m) => m.content),
19549
+ embeddingOptions
19550
+ );
19551
+ } catch (err) {
19552
+ laneEmbedFailed = true;
19553
+ getLogger().warn(
19554
+ `memoryVault: un-embedded lane embedding failed for ${laneRows.length} rows \u2014 they rank on BM25 alone for this search: ` + (err instanceof Error ? err.message : String(err))
19555
+ );
19556
+ }
19557
+ if (laneVecs) {
19558
+ const vecs = laneVecs;
19559
+ laneRows.forEach((m, i) => {
19560
+ const vec = Float32Array.from(vecs[i]);
19561
+ cache.set(m.uniqueId, vec);
19562
+ vectored.push({ uniqueId: m.uniqueId, embedding: vec, updatedAt: m.updatedAt });
19563
+ updateVaultMemoryEmbeddingOp(
19564
+ vaultCtx,
19565
+ m.uniqueId,
19566
+ JSON.stringify(vecs[i]),
19567
+ currentModel
19568
+ ).catch(() => {
19569
+ });
19522
19570
  });
19523
- });
19571
+ }
19524
19572
  }
19525
19573
  }
19526
19574
  const k = Math.max(opts.limit * opts.admitFactor, opts.admitFloor);
19527
19575
  const admittedIds = admitVaultProjections(queryEmbedding, vectored, k);
19576
+ if ((embeddingsDegraded || laneEmbedFailed) && admittedIds.length < k) {
19577
+ const already = new Set(admittedIds);
19578
+ for (const key of [...keys].sort((a, b) => b.updatedAt.getTime() - a.updatedAt.getTime())) {
19579
+ if (admittedIds.length >= k) break;
19580
+ if (!already.has(key.uniqueId)) admittedIds.push(key.uniqueId);
19581
+ }
19582
+ }
19528
19583
  const admissionSet = new Set(admittedIds);
19529
19584
  if (opts.forceIncludeIds) {
19530
19585
  for (const id of opts.forceIncludeIds) {
@@ -19552,11 +19607,18 @@ async function buildProjectedCorpus(query, vaultCtx, embeddingOptions, cache, qu
19552
19607
  eventTimeKind: normalizeEventTimeKind(m.eventTimeKind),
19553
19608
  factType: m.factType
19554
19609
  }));
19555
- return { memories, embeddedItems, queryEmbedding, vaultSize };
19610
+ return { memories, embeddedItems, queryEmbedding, vaultSize, laneEmbedFailed };
19556
19611
  }
19557
19612
  async function prepareVaultCandidates(query, vaultCtx, embeddingOptions, cache, searchOptions) {
19558
19613
  if (!query || typeof query !== "string") {
19559
- return { memories: [], embeddedItems: [], queryEmbedding: [], vaultSize: 0 };
19614
+ return {
19615
+ memories: [],
19616
+ embeddedItems: [],
19617
+ queryEmbedding: [],
19618
+ vaultSize: 0,
19619
+ embeddingsUnavailable: false,
19620
+ embeddingFailure: false
19621
+ };
19560
19622
  }
19561
19623
  const limit = searchOptions?.limit ?? 5;
19562
19624
  const scopes = searchOptions?.scopes;
@@ -19573,6 +19635,12 @@ async function prepareVaultCandidates(query, vaultCtx, embeddingOptions, cache,
19573
19635
  let embeddedItems;
19574
19636
  let queryEmbedding;
19575
19637
  let vaultSize;
19638
+ let embeddingsUnavailable = false;
19639
+ let embeddingFailure = false;
19640
+ const onEmbeddingDegraded = () => {
19641
+ embeddingsUnavailable = true;
19642
+ embeddingFailure = true;
19643
+ };
19576
19644
  if (searchOptions?.decryptLast) {
19577
19645
  const forceIncludeIds = [
19578
19646
  ...searchOptions.entityRanking ?? [],
@@ -19583,14 +19651,30 @@ async function prepareVaultCandidates(query, vaultCtx, embeddingOptions, cache,
19583
19651
  admitFactor: ADMIT_FACTOR,
19584
19652
  admitFloor: ADMIT_FLOOR,
19585
19653
  unembeddedCap: UNEMBEDDED_CAP,
19586
- ...forceIncludeIds.length > 0 && { forceIncludeIds }
19654
+ ...forceIncludeIds.length > 0 && { forceIncludeIds },
19655
+ onEmbeddingDegraded
19587
19656
  });
19588
19657
  if (corpus.vaultSize === 0) {
19589
- return { memories: [], embeddedItems: [], queryEmbedding: [], vaultSize: 0 };
19658
+ return {
19659
+ memories: [],
19660
+ embeddedItems: [],
19661
+ queryEmbedding: [],
19662
+ vaultSize: 0,
19663
+ embeddingsUnavailable: false,
19664
+ embeddingFailure: false
19665
+ };
19590
19666
  }
19591
19667
  ({ memories, embeddedItems, queryEmbedding, vaultSize } = corpus);
19668
+ if (corpus.laneEmbedFailed) embeddingFailure = true;
19592
19669
  if (memories.length === 0) {
19593
- return { memories: [], embeddedItems: [], queryEmbedding: [], vaultSize };
19670
+ return {
19671
+ memories: [],
19672
+ embeddedItems: [],
19673
+ queryEmbedding: [],
19674
+ vaultSize,
19675
+ embeddingsUnavailable,
19676
+ embeddingFailure
19677
+ };
19594
19678
  }
19595
19679
  } else {
19596
19680
  const loaded = await getAllVaultMemoriesOp(
@@ -19605,14 +19689,21 @@ async function prepareVaultCandidates(query, vaultCtx, embeddingOptions, cache,
19605
19689
  );
19606
19690
  }
19607
19691
  if (memories.length === 0) {
19608
- return { memories: [], embeddedItems: [], queryEmbedding: [], vaultSize: loaded.length };
19692
+ return {
19693
+ memories: [],
19694
+ embeddedItems: [],
19695
+ queryEmbedding: [],
19696
+ vaultSize: loaded.length,
19697
+ embeddingsUnavailable: false,
19698
+ embeddingFailure: false
19699
+ };
19609
19700
  }
19610
- queryEmbedding = await generateEmbedding(query, embeddingOptions);
19701
+ queryEmbedding = await embedQueryOrDegrade(query, embeddingOptions, onEmbeddingDegraded);
19611
19702
  const currentModel = embeddingOptions.model ?? DEFAULT_API_EMBEDDING_MODEL;
19612
19703
  const uncachedTexts = [];
19613
19704
  const uncachedIndices = [];
19614
19705
  let staleReembedCount = 0;
19615
- for (let i = 0; i < memories.length; i++) {
19706
+ for (let i = 0; queryEmbedding.length > 0 && i < memories.length; i++) {
19616
19707
  const content = memories[i].content;
19617
19708
  const memoryId = memories[i].uniqueId;
19618
19709
  const cached = cache.get(memoryId);
@@ -19640,20 +19731,30 @@ async function prepareVaultCandidates(query, vaultCtx, embeddingOptions, cache,
19640
19731
  );
19641
19732
  }
19642
19733
  if (uncachedTexts.length > 0) {
19643
- const newEmbeddings = await generateEmbeddings(uncachedTexts, embeddingOptions);
19644
- for (let j = 0; j < uncachedTexts.length; j++) {
19645
- cache.set(memories[uncachedIndices[j]].uniqueId, Float32Array.from(newEmbeddings[j]));
19646
- updateVaultMemoryEmbeddingOp(
19647
- vaultCtx,
19648
- memories[uncachedIndices[j]].uniqueId,
19649
- JSON.stringify(newEmbeddings[j]),
19650
- currentModel
19651
- ).catch(
19652
- // Silently swallow – SDK must not use console.*; embedding will be retried on next search
19653
- () => {
19654
- }
19734
+ let newEmbeddings;
19735
+ try {
19736
+ newEmbeddings = await generateEmbeddings(uncachedTexts, embeddingOptions);
19737
+ } catch (err) {
19738
+ embeddingFailure = true;
19739
+ getLogger().warn(
19740
+ `memoryVault: (re)embedding ${uncachedTexts.length} vault rows failed \u2014 they rank on BM25 alone for this search: ` + (err instanceof Error ? err.message : String(err))
19655
19741
  );
19656
19742
  }
19743
+ if (newEmbeddings) {
19744
+ for (let j = 0; j < uncachedTexts.length; j++) {
19745
+ cache.set(memories[uncachedIndices[j]].uniqueId, Float32Array.from(newEmbeddings[j]));
19746
+ updateVaultMemoryEmbeddingOp(
19747
+ vaultCtx,
19748
+ memories[uncachedIndices[j]].uniqueId,
19749
+ JSON.stringify(newEmbeddings[j]),
19750
+ currentModel
19751
+ ).catch(
19752
+ // Silently swallow – SDK must not use console.*; embedding will be retried on next search
19753
+ () => {
19754
+ }
19755
+ );
19756
+ }
19757
+ }
19657
19758
  }
19658
19759
  embeddedItems = memories.map((m) => ({
19659
19760
  id: m.uniqueId,
@@ -19670,12 +19771,22 @@ async function prepareVaultCandidates(query, vaultCtx, embeddingOptions, cache,
19670
19771
  factType: m.factType
19671
19772
  }));
19672
19773
  }
19673
- return { memories, embeddedItems, queryEmbedding, vaultSize };
19774
+ if (!embeddingsUnavailable && !embeddedItems.some((it) => it.embedding.length > 0)) {
19775
+ embeddingsUnavailable = true;
19776
+ }
19777
+ return {
19778
+ memories,
19779
+ embeddedItems,
19780
+ queryEmbedding,
19781
+ vaultSize,
19782
+ embeddingsUnavailable,
19783
+ embeddingFailure
19784
+ };
19674
19785
  }
19675
19786
  async function rankPreparedVaultCandidates(query, prepared, embeddingOptions, searchOptions) {
19676
19787
  const limit = searchOptions?.limit ?? 5;
19677
19788
  const minSimilarity = searchOptions?.minSimilarity ?? 0.1;
19678
- const { memories, embeddedItems, queryEmbedding, vaultSize } = prepared;
19789
+ const { memories, embeddedItems, queryEmbedding, vaultSize, embeddingsUnavailable } = prepared;
19679
19790
  if (queryEmbedding.length > 0) {
19680
19791
  const mismatched = embeddedItems.filter(
19681
19792
  (it) => it.embedding.length > 0 && it.embedding.length !== queryEmbedding.length
@@ -19712,7 +19823,8 @@ async function rankPreparedVaultCandidates(query, prepared, embeddingOptions, se
19712
19823
  results: results2,
19713
19824
  vaultSize: out.vaultSize,
19714
19825
  reranked: out.reranked ?? false,
19715
- hadV2Head: out.hadV2Head ?? false
19826
+ hadV2Head: out.hadV2Head ?? false,
19827
+ embeddingsUnavailable
19716
19828
  };
19717
19829
  };
19718
19830
  const rerankStats = { applied: false };
@@ -19735,36 +19847,46 @@ async function rankPreparedVaultCandidates(query, prepared, embeddingOptions, se
19735
19847
  ...searchOptions?.rrfK !== void 0 && { rrfK: searchOptions.rrfK },
19736
19848
  ...searchOptions?.factTypeWeights && { factTypeWeights: searchOptions.factTypeWeights }
19737
19849
  };
19738
- if (useFusion && searchOptions?.decompose === "llm" && searchOptions.decomposeOptions) {
19850
+ if (useFusion && !embeddingsUnavailable && searchOptions?.decompose === "llm" && searchOptions.decomposeOptions) {
19739
19851
  const decomp = await decomposeQuery(query, searchOptions.decomposeOptions);
19740
19852
  if (decomp.mode === "composite") {
19741
- const subEmbeddings = await generateEmbeddings(decomp.subQueries, embeddingOptions);
19742
- const subQueries = decomp.subQueries.map((sq, i) => ({
19743
- query: sq,
19744
- embedding: subEmbeddings[i]
19745
- }));
19746
- const v2HeadStats = { hadResults: false };
19747
- const composite = await rankComposite(query, queryEmbedding, subQueries, embeddedItems, {
19748
- limit,
19749
- minSimilarity,
19750
- rerank: !!searchOptions.rerank,
19751
- ...searchOptions.rerankTopN !== void 0 && {
19752
- rerankTopN: searchOptions.rerankTopN
19753
- },
19754
- ...searchOptions.ceWeight !== void 0 && { ceWeight: searchOptions.ceWeight },
19755
- ...searchOptions.mmr !== void 0 && { mmr: searchOptions.mmr },
19756
- ...tuning,
19757
- ...searchOptions.entityRanking && { entityRanking: searchOptions.entityRanking },
19758
- ...searchOptions.temporalRanking && { temporalRanking: searchOptions.temporalRanking },
19759
- rerankStats,
19760
- v2HeadStats
19761
- });
19762
- return stampTimestamps({
19763
- results: composite,
19764
- vaultSize,
19765
- reranked: rerankStats.applied,
19766
- hadV2Head: v2HeadStats.hadResults
19767
- });
19853
+ let subEmbeddings;
19854
+ try {
19855
+ subEmbeddings = await generateEmbeddings(decomp.subQueries, embeddingOptions);
19856
+ } catch (err) {
19857
+ getLogger().warn(
19858
+ "memoryVault: sub-query embedding failed \u2014 falling back to single-query ranking: " + (err instanceof Error ? err.message : String(err))
19859
+ );
19860
+ subEmbeddings = [];
19861
+ }
19862
+ if (subEmbeddings.length > 0) {
19863
+ const subQueries = decomp.subQueries.map((sq, i) => ({
19864
+ query: sq,
19865
+ embedding: subEmbeddings[i]
19866
+ }));
19867
+ const v2HeadStats = { hadResults: false };
19868
+ const composite = await rankComposite(query, queryEmbedding, subQueries, embeddedItems, {
19869
+ limit,
19870
+ minSimilarity,
19871
+ rerank: !!searchOptions.rerank,
19872
+ ...searchOptions.rerankTopN !== void 0 && {
19873
+ rerankTopN: searchOptions.rerankTopN
19874
+ },
19875
+ ...searchOptions.ceWeight !== void 0 && { ceWeight: searchOptions.ceWeight },
19876
+ ...searchOptions.mmr !== void 0 && { mmr: searchOptions.mmr },
19877
+ ...tuning,
19878
+ ...searchOptions.entityRanking && { entityRanking: searchOptions.entityRanking },
19879
+ ...searchOptions.temporalRanking && { temporalRanking: searchOptions.temporalRanking },
19880
+ rerankStats,
19881
+ v2HeadStats
19882
+ });
19883
+ return stampTimestamps({
19884
+ results: composite,
19885
+ vaultSize,
19886
+ reranked: rerankStats.applied,
19887
+ hadV2Head: v2HeadStats.hadResults
19888
+ });
19889
+ }
19768
19890
  }
19769
19891
  }
19770
19892
  if (useFusion && searchOptions?.rerank) {
@@ -19815,7 +19937,14 @@ async function rankPreparedVaultCandidates(query, prepared, embeddingOptions, se
19815
19937
  }
19816
19938
  async function searchVaultMemoriesWithSize(query, vaultCtx, embeddingOptions, cache, searchOptions) {
19817
19939
  if (!query || typeof query !== "string") {
19818
- return { results: [], vaultSize: 0, reranked: false, hadV2Head: false };
19940
+ return {
19941
+ results: [],
19942
+ vaultSize: 0,
19943
+ reranked: false,
19944
+ hadV2Head: false,
19945
+ embeddingsUnavailable: false,
19946
+ rankedOnCosine: false
19947
+ };
19819
19948
  }
19820
19949
  const prepared = await prepareVaultCandidates(
19821
19950
  query,
@@ -19825,12 +19954,32 @@ async function searchVaultMemoriesWithSize(query, vaultCtx, embeddingOptions, ca
19825
19954
  searchOptions
19826
19955
  );
19827
19956
  if (prepared.vaultSize === 0) {
19828
- return { results: [], vaultSize: 0, reranked: false, hadV2Head: false };
19957
+ return {
19958
+ results: [],
19959
+ vaultSize: 0,
19960
+ reranked: false,
19961
+ hadV2Head: false,
19962
+ embeddingsUnavailable: prepared.embeddingsUnavailable,
19963
+ rankedOnCosine: false
19964
+ };
19829
19965
  }
19830
19966
  if (prepared.memories.length === 0) {
19831
- return { results: [], vaultSize: prepared.vaultSize, reranked: false, hadV2Head: false };
19967
+ return {
19968
+ results: [],
19969
+ vaultSize: prepared.vaultSize,
19970
+ reranked: false,
19971
+ hadV2Head: false,
19972
+ embeddingsUnavailable: prepared.embeddingsUnavailable,
19973
+ rankedOnCosine: false
19974
+ };
19832
19975
  }
19833
- return rankPreparedVaultCandidates(query, prepared, embeddingOptions, searchOptions);
19976
+ const ranked = await rankPreparedVaultCandidates(
19977
+ query,
19978
+ prepared,
19979
+ embeddingOptions,
19980
+ searchOptions
19981
+ );
19982
+ return { ...ranked, rankedOnCosine: !ranked.embeddingsUnavailable };
19834
19983
  }
19835
19984
  async function searchVaultMemories(query, vaultCtx, embeddingOptions, cache, searchOptions) {
19836
19985
  const { results } = await searchVaultMemoriesWithSize(
@@ -19842,6 +19991,7 @@ async function searchVaultMemories(query, vaultCtx, embeddingOptions, cache, sea
19842
19991
  );
19843
19992
  return results;
19844
19993
  }
19994
+ var EMBEDDINGS_DEGRADED_EMPTY_NO_LEXICAL = "Memory search is temporarily unavailable \u2014 the semantic lookup failed and this search mode has no keyword fallback, so no memory search ran at all. Do not conclude the user has no such memory; say the memory lookup was unavailable.";
19845
19995
  function formatVaultHits(hits) {
19846
19996
  return hits.map((h, i) => `[${i + 1}] (id: ${h.id}, similarity: ${h.score.toFixed(2)})
19847
19997
  ${h.content}`).join("\n\n");
@@ -19904,26 +20054,36 @@ function createMemoryVaultSearchTool(vaultCtx, embeddingOptions, cache, searchOp
19904
20054
  const budget = searchOptions?.decompose === "llm" && searchOptions.decomposeOptions ? "high" : searchOptions?.rerank ? "mid" : "low";
19905
20055
  const folderId = searchOptions?.folderId ?? argFolderId;
19906
20056
  if (searchOptions?.useFusion === false) {
19907
- const legacy = await searchVaultMemories(query, vaultCtx, embeddingOptions, cache, {
19908
- limit: requestLimit,
19909
- minSimilarity,
19910
- useFusion: false,
19911
- ...tuningForward,
19912
- ...folderId !== void 0 && { folderId },
19913
- ...searchOptions?.scopes && { scopes: searchOptions.scopes }
19914
- });
20057
+ const { results: legacy, embeddingsUnavailable } = await searchVaultMemoriesWithSize(
20058
+ query,
20059
+ vaultCtx,
20060
+ embeddingOptions,
20061
+ cache,
20062
+ {
20063
+ limit: requestLimit,
20064
+ minSimilarity,
20065
+ useFusion: false,
20066
+ ...tuningForward,
20067
+ ...folderId !== void 0 && { folderId },
20068
+ ...searchOptions?.scopes && { scopes: searchOptions.scopes }
20069
+ }
20070
+ );
19915
20071
  if (legacy.length === 0) {
19916
- return "No relevant memories found in the vault.";
20072
+ return embeddingsUnavailable ? EMBEDDINGS_DEGRADED_EMPTY_NO_LEXICAL : "No relevant memories found in the vault.";
19917
20073
  }
19918
20074
  return formatVaultHits(
19919
20075
  legacy.map((r) => ({ id: r.uniqueId, content: r.content, score: r.similarity }))
19920
20076
  );
19921
20077
  }
19922
- const { recall: recall2 } = await import("./recall-AN7HBEFS.mjs");
20078
+ const { recall: recall2 } = await import("./recall-QH3YX6T4.mjs");
20079
+ let recallDegraded = [];
19923
20080
  const result = await recall2(
19924
20081
  query,
19925
20082
  { vaultCtx, embeddingOptions, vaultCache: cache },
19926
20083
  {
20084
+ onDiagnostics: (d) => {
20085
+ recallDegraded = d.degraded;
20086
+ },
19927
20087
  types: ["fact"],
19928
20088
  limit: requestLimit,
19929
20089
  minScore: minSimilarity,
@@ -19944,7 +20104,7 @@ function createMemoryVaultSearchTool(vaultCtx, embeddingOptions, cache, searchOp
19944
20104
  return "The memory vault is empty. No memories have been saved yet.";
19945
20105
  }
19946
20106
  if (result.memories.length === 0) {
19947
- return "No relevant memories found in the vault.";
20107
+ return recallDegraded.includes("embeddings-unavailable") ? EMBEDDINGS_DEGRADED_EMPTY : "No relevant memories found in the vault.";
19948
20108
  }
19949
20109
  const formatted = formatVaultHits(
19950
20110
  result.memories.map((m) => ({
@@ -20499,6 +20659,9 @@ async function recall(query, ctx, options = {}) {
20499
20659
  let vaultSize;
20500
20660
  let didRerank = false;
20501
20661
  let hadV2Head = false;
20662
+ let embeddingsUnavailable = false;
20663
+ let chunkEmbedFailed = false;
20664
+ let factLaneRankedOnCosine = false;
20502
20665
  const emitDiagnostics = (candidateCount) => {
20503
20666
  const cb = options.onDiagnostics;
20504
20667
  if (!cb) return;
@@ -20507,6 +20670,7 @@ async function recall(query, ctx, options = {}) {
20507
20670
  degraded.push("rerank-unavailable");
20508
20671
  }
20509
20672
  if (flags.decompose && !decomposeAvailable) degraded.push("decompose-unavailable");
20673
+ if (embeddingsUnavailable) degraded.push("embeddings-unavailable");
20510
20674
  const diagnostics = {
20511
20675
  usedBudget,
20512
20676
  reranked: didRerank,
@@ -20537,7 +20701,26 @@ async function recall(query, ctx, options = {}) {
20537
20701
  const graphRefiner = flags.traverse && options.graphRefine && options.decomposeOptions ? createLlmNeighborRefiner(options.decomposeOptions) : void 0;
20538
20702
  const prepStart = nowMs();
20539
20703
  const [queryEmbedding, entityRanking, temporalRanking] = await Promise.all([
20540
- needsChunkEmbedding ? generateEmbedding(query, ctx.embeddingOptions) : Promise.resolve(void 0),
20704
+ // The chunk lane is cosine-only — `searchChunksOp` needs a real vector, and
20705
+ // there is no lexical fallback for it — so an embeddings outage must SKIP the
20706
+ // lane, not reject this shared Promise.all and take the primary fact lane
20707
+ // (which BM25 can still serve) down with it. Mirrors safeLane's posture.
20708
+ needsChunkEmbedding ? generateEmbedding(query, ctx.embeddingOptions).then((vec) => {
20709
+ if (vec.length === 0) {
20710
+ getLogger().warn(
20711
+ "[memory/recall] chunk-lane query embedding came back empty; skipping the chunk lane"
20712
+ );
20713
+ chunkEmbedFailed = true;
20714
+ return void 0;
20715
+ }
20716
+ return vec;
20717
+ }).catch((err) => {
20718
+ getLogger().warn(
20719
+ `[memory/recall] chunk-lane query embedding failed; skipping the chunk lane: ${err instanceof Error ? err.message : String(err)}`
20720
+ );
20721
+ chunkEmbedFailed = true;
20722
+ return void 0;
20723
+ }) : Promise.resolve(void 0),
20541
20724
  // The graph + temporal lanes are AUXILIARY (RRF side-signals). A transient
20542
20725
  // WatermelonDB throw in either must NOT reject this Promise.all and take
20543
20726
  // PRIMARY cosine/BM25 recall down with it — degrade the failing lane to an
@@ -20562,7 +20745,9 @@ async function recall(query, ctx, options = {}) {
20562
20745
  results,
20563
20746
  vaultSize: size,
20564
20747
  reranked,
20565
- hadV2Head: v2Head
20748
+ hadV2Head: v2Head,
20749
+ embeddingsUnavailable: factEmbeddingsUnavailable,
20750
+ rankedOnCosine: factRankedOnCosine
20566
20751
  } = await searchVaultMemoriesWithSize(
20567
20752
  query,
20568
20753
  ctx.vaultCtx,
@@ -20619,8 +20804,11 @@ async function recall(query, ctx, options = {}) {
20619
20804
  vaultSize = size;
20620
20805
  didRerank = reranked;
20621
20806
  hadV2Head = v2Head;
20807
+ if (factEmbeddingsUnavailable) embeddingsUnavailable = true;
20808
+ factLaneRankedOnCosine = factRankedOnCosine;
20622
20809
  factLaneMs = nowMs() - factStart;
20623
20810
  }
20811
+ if (chunkEmbedFailed && !factLaneRankedOnCosine) embeddingsUnavailable = true;
20624
20812
  if (types.includes("chunk") && ctx.storageCtx && queryEmbedding) {
20625
20813
  const chunkStart = nowMs();
20626
20814
  const chunkMinScore = options.minScore ?? DEFAULT_CHUNK_MIN_SCORE;
@@ -20932,6 +21120,9 @@ export {
20932
21120
  embedAllMessages,
20933
21121
  chunkAndEmbedMessage,
20934
21122
  chunkAndEmbedAllMessages,
21123
+ RECALL_TOOL_NAME,
21124
+ RECALL_MAX_LIMIT,
21125
+ EMBEDDINGS_DEGRADED_EMPTY,
20935
21126
  RerankerUnavailableError,
20936
21127
  isRerankerAvailable,
20937
21128
  DEFAULT_VAULT_CACHE_SIZE,