@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.
@@ -19399,6 +19399,17 @@ var init_mmr = __esm({
19399
19399
  }
19400
19400
  });
19401
19401
 
19402
+ // src/lib/memory/recallConstants.ts
19403
+ var RECALL_TOOL_NAME, RECALL_MAX_LIMIT, EMBEDDINGS_DEGRADED_EMPTY;
19404
+ var init_recallConstants = __esm({
19405
+ "src/lib/memory/recallConstants.ts"() {
19406
+ "use strict";
19407
+ RECALL_TOOL_NAME = "recall_memory";
19408
+ RECALL_MAX_LIMIT = 50;
19409
+ 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.";
19410
+ }
19411
+ });
19412
+
19402
19413
  // src/lib/memory/recency.ts
19403
19414
  function recencyMultiplier(updatedAt, opts = {}) {
19404
19415
  const noDate = opts.noDateMultiplier ?? DEFAULT_NO_DATE_MULTIPLIER;
@@ -20717,6 +20728,9 @@ async function recall(query, ctx, options = {}) {
20717
20728
  let vaultSize;
20718
20729
  let didRerank = false;
20719
20730
  let hadV2Head = false;
20731
+ let embeddingsUnavailable = false;
20732
+ let chunkEmbedFailed = false;
20733
+ let factLaneRankedOnCosine = false;
20720
20734
  const emitDiagnostics = (candidateCount) => {
20721
20735
  const cb = options.onDiagnostics;
20722
20736
  if (!cb) return;
@@ -20725,6 +20739,7 @@ async function recall(query, ctx, options = {}) {
20725
20739
  degraded.push("rerank-unavailable");
20726
20740
  }
20727
20741
  if (flags.decompose && !decomposeAvailable) degraded.push("decompose-unavailable");
20742
+ if (embeddingsUnavailable) degraded.push("embeddings-unavailable");
20728
20743
  const diagnostics = {
20729
20744
  usedBudget,
20730
20745
  reranked: didRerank,
@@ -20755,7 +20770,26 @@ async function recall(query, ctx, options = {}) {
20755
20770
  const graphRefiner = flags.traverse && options.graphRefine && options.decomposeOptions ? createLlmNeighborRefiner(options.decomposeOptions) : void 0;
20756
20771
  const prepStart = nowMs();
20757
20772
  const [queryEmbedding, entityRanking, temporalRanking] = await Promise.all([
20758
- needsChunkEmbedding ? generateEmbedding(query, ctx.embeddingOptions) : Promise.resolve(void 0),
20773
+ // The chunk lane is cosine-only — `searchChunksOp` needs a real vector, and
20774
+ // there is no lexical fallback for it — so an embeddings outage must SKIP the
20775
+ // lane, not reject this shared Promise.all and take the primary fact lane
20776
+ // (which BM25 can still serve) down with it. Mirrors safeLane's posture.
20777
+ needsChunkEmbedding ? generateEmbedding(query, ctx.embeddingOptions).then((vec) => {
20778
+ if (vec.length === 0) {
20779
+ getLogger().warn(
20780
+ "[memory/recall] chunk-lane query embedding came back empty; skipping the chunk lane"
20781
+ );
20782
+ chunkEmbedFailed = true;
20783
+ return void 0;
20784
+ }
20785
+ return vec;
20786
+ }).catch((err) => {
20787
+ getLogger().warn(
20788
+ `[memory/recall] chunk-lane query embedding failed; skipping the chunk lane: ${err instanceof Error ? err.message : String(err)}`
20789
+ );
20790
+ chunkEmbedFailed = true;
20791
+ return void 0;
20792
+ }) : Promise.resolve(void 0),
20759
20793
  // The graph + temporal lanes are AUXILIARY (RRF side-signals). A transient
20760
20794
  // WatermelonDB throw in either must NOT reject this Promise.all and take
20761
20795
  // PRIMARY cosine/BM25 recall down with it — degrade the failing lane to an
@@ -20780,7 +20814,9 @@ async function recall(query, ctx, options = {}) {
20780
20814
  results,
20781
20815
  vaultSize: size,
20782
20816
  reranked,
20783
- hadV2Head: v2Head
20817
+ hadV2Head: v2Head,
20818
+ embeddingsUnavailable: factEmbeddingsUnavailable,
20819
+ rankedOnCosine: factRankedOnCosine
20784
20820
  } = await searchVaultMemoriesWithSize(
20785
20821
  query,
20786
20822
  ctx.vaultCtx,
@@ -20837,8 +20873,11 @@ async function recall(query, ctx, options = {}) {
20837
20873
  vaultSize = size;
20838
20874
  didRerank = reranked;
20839
20875
  hadV2Head = v2Head;
20876
+ if (factEmbeddingsUnavailable) embeddingsUnavailable = true;
20877
+ factLaneRankedOnCosine = factRankedOnCosine;
20840
20878
  factLaneMs = nowMs() - factStart;
20841
20879
  }
20880
+ if (chunkEmbedFailed && !factLaneRankedOnCosine) embeddingsUnavailable = true;
20842
20881
  if (types.includes("chunk") && ctx.storageCtx && queryEmbedding) {
20843
20882
  const chunkStart = nowMs();
20844
20883
  const chunkMinScore = options.minScore ?? DEFAULT_CHUNK_MIN_SCORE;
@@ -21601,6 +21640,24 @@ async function eagerEmbedContent(content, embeddingOptions, cache, vaultCtx, mem
21601
21640
  );
21602
21641
  }
21603
21642
  }
21643
+ async function embedQueryOrDegrade(query, embeddingOptions, onDegraded) {
21644
+ try {
21645
+ const embedding = await generateEmbedding(query, embeddingOptions);
21646
+ if (embedding.length === 0) {
21647
+ getLogger().warn(
21648
+ "memoryVault: query embedding came back empty \u2014 falling back to BM25-only ranking for this search"
21649
+ );
21650
+ onDegraded?.();
21651
+ }
21652
+ return embedding;
21653
+ } catch (err) {
21654
+ getLogger().warn(
21655
+ "memoryVault: query embedding failed \u2014 falling back to BM25-only ranking for this search: " + (err instanceof Error ? err.message : String(err))
21656
+ );
21657
+ onDegraded?.();
21658
+ return [];
21659
+ }
21660
+ }
21604
21661
  function admitVaultProjections(queryEmbedding, vectored, k) {
21605
21662
  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);
21606
21663
  }
@@ -21612,23 +21669,36 @@ async function buildProjectedCorpus(query, vaultCtx, embeddingOptions, cache, qu
21612
21669
  const hydrateOpts = queryOpts.includeArchived ? { includeArchived: true } : void 0;
21613
21670
  const vaultSize = keys.length;
21614
21671
  if (keys.length === 0) {
21615
- return { memories: [], embeddedItems: [], queryEmbedding: [], vaultSize: 0 };
21672
+ return {
21673
+ memories: [],
21674
+ embeddedItems: [],
21675
+ queryEmbedding: [],
21676
+ vaultSize: 0,
21677
+ laneEmbedFailed: false
21678
+ };
21616
21679
  }
21617
- const queryEmbedding = await generateEmbedding(query, embeddingOptions);
21680
+ const queryEmbedding = await embedQueryOrDegrade(
21681
+ query,
21682
+ embeddingOptions,
21683
+ opts.onEmbeddingDegraded
21684
+ );
21685
+ const embeddingsDegraded = queryEmbedding.length === 0;
21618
21686
  const currentModel = embeddingOptions.model ?? DEFAULT_API_EMBEDDING_MODEL;
21619
21687
  const vectored = [];
21620
21688
  const missIds = [];
21621
21689
  const noVectorIds = [];
21622
21690
  const keyById = new Map(keys.map((k2) => [k2.uniqueId, k2]));
21623
- for (const k2 of keys) {
21624
- const cached = cache.get(k2.uniqueId);
21625
- if (cached && cached.length === queryEmbedding.length) {
21626
- vectored.push({ uniqueId: k2.uniqueId, embedding: cached, updatedAt: k2.updatedAt });
21627
- continue;
21691
+ if (!embeddingsDegraded) {
21692
+ for (const k2 of keys) {
21693
+ const cached = cache.get(k2.uniqueId);
21694
+ if (cached && cached.length === queryEmbedding.length) {
21695
+ vectored.push({ uniqueId: k2.uniqueId, embedding: cached, updatedAt: k2.updatedAt });
21696
+ continue;
21697
+ }
21698
+ const modelCompatible = (k2.embeddingModel ?? currentModel) === currentModel;
21699
+ if (modelCompatible) missIds.push(k2.uniqueId);
21700
+ else noVectorIds.push(k2.uniqueId);
21628
21701
  }
21629
- const modelCompatible = (k2.embeddingModel ?? currentModel) === currentModel;
21630
- if (modelCompatible) missIds.push(k2.uniqueId);
21631
- else noVectorIds.push(k2.uniqueId);
21632
21702
  }
21633
21703
  if (missIds.length > 0) {
21634
21704
  const rows = await getVaultEmbeddingsByIdsOp(vaultCtx, missIds, hydrateOpts);
@@ -21649,6 +21719,7 @@ async function buildProjectedCorpus(query, vaultCtx, embeddingOptions, cache, qu
21649
21719
  }
21650
21720
  for (const id of missIds) if (!gotVector.has(id)) noVectorIds.push(id);
21651
21721
  }
21722
+ let laneEmbedFailed = false;
21652
21723
  if (noVectorIds.length > 0) {
21653
21724
  const laneIds = noVectorIds.slice(0, opts.unembeddedCap);
21654
21725
  if (noVectorIds.length > laneIds.length) {
@@ -21660,26 +21731,44 @@ async function buildProjectedCorpus(query, vaultCtx, embeddingOptions, cache, qu
21660
21731
  (m) => !isEncrypted(m.content)
21661
21732
  );
21662
21733
  if (laneRows.length > 0) {
21663
- const laneVecs = await generateEmbeddings(
21664
- laneRows.map((m) => m.content),
21665
- embeddingOptions
21666
- );
21667
- laneRows.forEach((m, i) => {
21668
- const vec = Float32Array.from(laneVecs[i]);
21669
- cache.set(m.uniqueId, vec);
21670
- vectored.push({ uniqueId: m.uniqueId, embedding: vec, updatedAt: m.updatedAt });
21671
- updateVaultMemoryEmbeddingOp(
21672
- vaultCtx,
21673
- m.uniqueId,
21674
- JSON.stringify(laneVecs[i]),
21675
- currentModel
21676
- ).catch(() => {
21734
+ let laneVecs;
21735
+ try {
21736
+ laneVecs = await generateEmbeddings(
21737
+ laneRows.map((m) => m.content),
21738
+ embeddingOptions
21739
+ );
21740
+ } catch (err) {
21741
+ laneEmbedFailed = true;
21742
+ getLogger().warn(
21743
+ `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))
21744
+ );
21745
+ }
21746
+ if (laneVecs) {
21747
+ const vecs = laneVecs;
21748
+ laneRows.forEach((m, i) => {
21749
+ const vec = Float32Array.from(vecs[i]);
21750
+ cache.set(m.uniqueId, vec);
21751
+ vectored.push({ uniqueId: m.uniqueId, embedding: vec, updatedAt: m.updatedAt });
21752
+ updateVaultMemoryEmbeddingOp(
21753
+ vaultCtx,
21754
+ m.uniqueId,
21755
+ JSON.stringify(vecs[i]),
21756
+ currentModel
21757
+ ).catch(() => {
21758
+ });
21677
21759
  });
21678
- });
21760
+ }
21679
21761
  }
21680
21762
  }
21681
21763
  const k = Math.max(opts.limit * opts.admitFactor, opts.admitFloor);
21682
21764
  const admittedIds = admitVaultProjections(queryEmbedding, vectored, k);
21765
+ if ((embeddingsDegraded || laneEmbedFailed) && admittedIds.length < k) {
21766
+ const already = new Set(admittedIds);
21767
+ for (const key of [...keys].sort((a, b) => b.updatedAt.getTime() - a.updatedAt.getTime())) {
21768
+ if (admittedIds.length >= k) break;
21769
+ if (!already.has(key.uniqueId)) admittedIds.push(key.uniqueId);
21770
+ }
21771
+ }
21683
21772
  const admissionSet = new Set(admittedIds);
21684
21773
  if (opts.forceIncludeIds) {
21685
21774
  for (const id of opts.forceIncludeIds) {
@@ -21707,11 +21796,18 @@ async function buildProjectedCorpus(query, vaultCtx, embeddingOptions, cache, qu
21707
21796
  eventTimeKind: normalizeEventTimeKind(m.eventTimeKind),
21708
21797
  factType: m.factType
21709
21798
  }));
21710
- return { memories, embeddedItems, queryEmbedding, vaultSize };
21799
+ return { memories, embeddedItems, queryEmbedding, vaultSize, laneEmbedFailed };
21711
21800
  }
21712
21801
  async function prepareVaultCandidates(query, vaultCtx, embeddingOptions, cache, searchOptions) {
21713
21802
  if (!query || typeof query !== "string") {
21714
- return { memories: [], embeddedItems: [], queryEmbedding: [], vaultSize: 0 };
21803
+ return {
21804
+ memories: [],
21805
+ embeddedItems: [],
21806
+ queryEmbedding: [],
21807
+ vaultSize: 0,
21808
+ embeddingsUnavailable: false,
21809
+ embeddingFailure: false
21810
+ };
21715
21811
  }
21716
21812
  const limit = searchOptions?.limit ?? 5;
21717
21813
  const scopes = searchOptions?.scopes;
@@ -21728,6 +21824,12 @@ async function prepareVaultCandidates(query, vaultCtx, embeddingOptions, cache,
21728
21824
  let embeddedItems;
21729
21825
  let queryEmbedding;
21730
21826
  let vaultSize;
21827
+ let embeddingsUnavailable = false;
21828
+ let embeddingFailure = false;
21829
+ const onEmbeddingDegraded = () => {
21830
+ embeddingsUnavailable = true;
21831
+ embeddingFailure = true;
21832
+ };
21731
21833
  if (searchOptions?.decryptLast) {
21732
21834
  const forceIncludeIds = [
21733
21835
  ...searchOptions.entityRanking ?? [],
@@ -21738,14 +21840,30 @@ async function prepareVaultCandidates(query, vaultCtx, embeddingOptions, cache,
21738
21840
  admitFactor: ADMIT_FACTOR,
21739
21841
  admitFloor: ADMIT_FLOOR,
21740
21842
  unembeddedCap: UNEMBEDDED_CAP,
21741
- ...forceIncludeIds.length > 0 && { forceIncludeIds }
21843
+ ...forceIncludeIds.length > 0 && { forceIncludeIds },
21844
+ onEmbeddingDegraded
21742
21845
  });
21743
21846
  if (corpus.vaultSize === 0) {
21744
- return { memories: [], embeddedItems: [], queryEmbedding: [], vaultSize: 0 };
21847
+ return {
21848
+ memories: [],
21849
+ embeddedItems: [],
21850
+ queryEmbedding: [],
21851
+ vaultSize: 0,
21852
+ embeddingsUnavailable: false,
21853
+ embeddingFailure: false
21854
+ };
21745
21855
  }
21746
21856
  ({ memories, embeddedItems, queryEmbedding, vaultSize } = corpus);
21857
+ if (corpus.laneEmbedFailed) embeddingFailure = true;
21747
21858
  if (memories.length === 0) {
21748
- return { memories: [], embeddedItems: [], queryEmbedding: [], vaultSize };
21859
+ return {
21860
+ memories: [],
21861
+ embeddedItems: [],
21862
+ queryEmbedding: [],
21863
+ vaultSize,
21864
+ embeddingsUnavailable,
21865
+ embeddingFailure
21866
+ };
21749
21867
  }
21750
21868
  } else {
21751
21869
  const loaded = await getAllVaultMemoriesOp(
@@ -21760,14 +21878,21 @@ async function prepareVaultCandidates(query, vaultCtx, embeddingOptions, cache,
21760
21878
  );
21761
21879
  }
21762
21880
  if (memories.length === 0) {
21763
- return { memories: [], embeddedItems: [], queryEmbedding: [], vaultSize: loaded.length };
21881
+ return {
21882
+ memories: [],
21883
+ embeddedItems: [],
21884
+ queryEmbedding: [],
21885
+ vaultSize: loaded.length,
21886
+ embeddingsUnavailable: false,
21887
+ embeddingFailure: false
21888
+ };
21764
21889
  }
21765
- queryEmbedding = await generateEmbedding(query, embeddingOptions);
21890
+ queryEmbedding = await embedQueryOrDegrade(query, embeddingOptions, onEmbeddingDegraded);
21766
21891
  const currentModel = embeddingOptions.model ?? DEFAULT_API_EMBEDDING_MODEL;
21767
21892
  const uncachedTexts = [];
21768
21893
  const uncachedIndices = [];
21769
21894
  let staleReembedCount = 0;
21770
- for (let i = 0; i < memories.length; i++) {
21895
+ for (let i = 0; queryEmbedding.length > 0 && i < memories.length; i++) {
21771
21896
  const content = memories[i].content;
21772
21897
  const memoryId = memories[i].uniqueId;
21773
21898
  const cached = cache.get(memoryId);
@@ -21795,20 +21920,30 @@ async function prepareVaultCandidates(query, vaultCtx, embeddingOptions, cache,
21795
21920
  );
21796
21921
  }
21797
21922
  if (uncachedTexts.length > 0) {
21798
- const newEmbeddings = await generateEmbeddings(uncachedTexts, embeddingOptions);
21799
- for (let j = 0; j < uncachedTexts.length; j++) {
21800
- cache.set(memories[uncachedIndices[j]].uniqueId, Float32Array.from(newEmbeddings[j]));
21801
- updateVaultMemoryEmbeddingOp(
21802
- vaultCtx,
21803
- memories[uncachedIndices[j]].uniqueId,
21804
- JSON.stringify(newEmbeddings[j]),
21805
- currentModel
21806
- ).catch(
21807
- // Silently swallow – SDK must not use console.*; embedding will be retried on next search
21808
- () => {
21809
- }
21923
+ let newEmbeddings;
21924
+ try {
21925
+ newEmbeddings = await generateEmbeddings(uncachedTexts, embeddingOptions);
21926
+ } catch (err) {
21927
+ embeddingFailure = true;
21928
+ getLogger().warn(
21929
+ `memoryVault: (re)embedding ${uncachedTexts.length} vault rows failed \u2014 they rank on BM25 alone for this search: ` + (err instanceof Error ? err.message : String(err))
21810
21930
  );
21811
21931
  }
21932
+ if (newEmbeddings) {
21933
+ for (let j = 0; j < uncachedTexts.length; j++) {
21934
+ cache.set(memories[uncachedIndices[j]].uniqueId, Float32Array.from(newEmbeddings[j]));
21935
+ updateVaultMemoryEmbeddingOp(
21936
+ vaultCtx,
21937
+ memories[uncachedIndices[j]].uniqueId,
21938
+ JSON.stringify(newEmbeddings[j]),
21939
+ currentModel
21940
+ ).catch(
21941
+ // Silently swallow – SDK must not use console.*; embedding will be retried on next search
21942
+ () => {
21943
+ }
21944
+ );
21945
+ }
21946
+ }
21812
21947
  }
21813
21948
  embeddedItems = memories.map((m) => ({
21814
21949
  id: m.uniqueId,
@@ -21825,12 +21960,22 @@ async function prepareVaultCandidates(query, vaultCtx, embeddingOptions, cache,
21825
21960
  factType: m.factType
21826
21961
  }));
21827
21962
  }
21828
- return { memories, embeddedItems, queryEmbedding, vaultSize };
21963
+ if (!embeddingsUnavailable && !embeddedItems.some((it) => it.embedding.length > 0)) {
21964
+ embeddingsUnavailable = true;
21965
+ }
21966
+ return {
21967
+ memories,
21968
+ embeddedItems,
21969
+ queryEmbedding,
21970
+ vaultSize,
21971
+ embeddingsUnavailable,
21972
+ embeddingFailure
21973
+ };
21829
21974
  }
21830
21975
  async function rankPreparedVaultCandidates(query, prepared, embeddingOptions, searchOptions) {
21831
21976
  const limit = searchOptions?.limit ?? 5;
21832
21977
  const minSimilarity = searchOptions?.minSimilarity ?? 0.1;
21833
- const { memories, embeddedItems, queryEmbedding, vaultSize } = prepared;
21978
+ const { memories, embeddedItems, queryEmbedding, vaultSize, embeddingsUnavailable } = prepared;
21834
21979
  if (queryEmbedding.length > 0) {
21835
21980
  const mismatched = embeddedItems.filter(
21836
21981
  (it) => it.embedding.length > 0 && it.embedding.length !== queryEmbedding.length
@@ -21867,7 +22012,8 @@ async function rankPreparedVaultCandidates(query, prepared, embeddingOptions, se
21867
22012
  results: results2,
21868
22013
  vaultSize: out.vaultSize,
21869
22014
  reranked: out.reranked ?? false,
21870
- hadV2Head: out.hadV2Head ?? false
22015
+ hadV2Head: out.hadV2Head ?? false,
22016
+ embeddingsUnavailable
21871
22017
  };
21872
22018
  };
21873
22019
  const rerankStats = { applied: false };
@@ -21890,36 +22036,46 @@ async function rankPreparedVaultCandidates(query, prepared, embeddingOptions, se
21890
22036
  ...searchOptions?.rrfK !== void 0 && { rrfK: searchOptions.rrfK },
21891
22037
  ...searchOptions?.factTypeWeights && { factTypeWeights: searchOptions.factTypeWeights }
21892
22038
  };
21893
- if (useFusion && searchOptions?.decompose === "llm" && searchOptions.decomposeOptions) {
22039
+ if (useFusion && !embeddingsUnavailable && searchOptions?.decompose === "llm" && searchOptions.decomposeOptions) {
21894
22040
  const decomp = await decomposeQuery(query, searchOptions.decomposeOptions);
21895
22041
  if (decomp.mode === "composite") {
21896
- const subEmbeddings = await generateEmbeddings(decomp.subQueries, embeddingOptions);
21897
- const subQueries = decomp.subQueries.map((sq, i) => ({
21898
- query: sq,
21899
- embedding: subEmbeddings[i]
21900
- }));
21901
- const v2HeadStats = { hadResults: false };
21902
- const composite = await rankComposite(query, queryEmbedding, subQueries, embeddedItems, {
21903
- limit,
21904
- minSimilarity,
21905
- rerank: !!searchOptions.rerank,
21906
- ...searchOptions.rerankTopN !== void 0 && {
21907
- rerankTopN: searchOptions.rerankTopN
21908
- },
21909
- ...searchOptions.ceWeight !== void 0 && { ceWeight: searchOptions.ceWeight },
21910
- ...searchOptions.mmr !== void 0 && { mmr: searchOptions.mmr },
21911
- ...tuning,
21912
- ...searchOptions.entityRanking && { entityRanking: searchOptions.entityRanking },
21913
- ...searchOptions.temporalRanking && { temporalRanking: searchOptions.temporalRanking },
21914
- rerankStats,
21915
- v2HeadStats
21916
- });
21917
- return stampTimestamps({
21918
- results: composite,
21919
- vaultSize,
21920
- reranked: rerankStats.applied,
21921
- hadV2Head: v2HeadStats.hadResults
21922
- });
22042
+ let subEmbeddings;
22043
+ try {
22044
+ subEmbeddings = await generateEmbeddings(decomp.subQueries, embeddingOptions);
22045
+ } catch (err) {
22046
+ getLogger().warn(
22047
+ "memoryVault: sub-query embedding failed \u2014 falling back to single-query ranking: " + (err instanceof Error ? err.message : String(err))
22048
+ );
22049
+ subEmbeddings = [];
22050
+ }
22051
+ if (subEmbeddings.length > 0) {
22052
+ const subQueries = decomp.subQueries.map((sq, i) => ({
22053
+ query: sq,
22054
+ embedding: subEmbeddings[i]
22055
+ }));
22056
+ const v2HeadStats = { hadResults: false };
22057
+ const composite = await rankComposite(query, queryEmbedding, subQueries, embeddedItems, {
22058
+ limit,
22059
+ minSimilarity,
22060
+ rerank: !!searchOptions.rerank,
22061
+ ...searchOptions.rerankTopN !== void 0 && {
22062
+ rerankTopN: searchOptions.rerankTopN
22063
+ },
22064
+ ...searchOptions.ceWeight !== void 0 && { ceWeight: searchOptions.ceWeight },
22065
+ ...searchOptions.mmr !== void 0 && { mmr: searchOptions.mmr },
22066
+ ...tuning,
22067
+ ...searchOptions.entityRanking && { entityRanking: searchOptions.entityRanking },
22068
+ ...searchOptions.temporalRanking && { temporalRanking: searchOptions.temporalRanking },
22069
+ rerankStats,
22070
+ v2HeadStats
22071
+ });
22072
+ return stampTimestamps({
22073
+ results: composite,
22074
+ vaultSize,
22075
+ reranked: rerankStats.applied,
22076
+ hadV2Head: v2HeadStats.hadResults
22077
+ });
22078
+ }
21923
22079
  }
21924
22080
  }
21925
22081
  if (useFusion && searchOptions?.rerank) {
@@ -21970,7 +22126,14 @@ async function rankPreparedVaultCandidates(query, prepared, embeddingOptions, se
21970
22126
  }
21971
22127
  async function searchVaultMemoriesWithSize(query, vaultCtx, embeddingOptions, cache, searchOptions) {
21972
22128
  if (!query || typeof query !== "string") {
21973
- return { results: [], vaultSize: 0, reranked: false, hadV2Head: false };
22129
+ return {
22130
+ results: [],
22131
+ vaultSize: 0,
22132
+ reranked: false,
22133
+ hadV2Head: false,
22134
+ embeddingsUnavailable: false,
22135
+ rankedOnCosine: false
22136
+ };
21974
22137
  }
21975
22138
  const prepared = await prepareVaultCandidates(
21976
22139
  query,
@@ -21980,12 +22143,32 @@ async function searchVaultMemoriesWithSize(query, vaultCtx, embeddingOptions, ca
21980
22143
  searchOptions
21981
22144
  );
21982
22145
  if (prepared.vaultSize === 0) {
21983
- return { results: [], vaultSize: 0, reranked: false, hadV2Head: false };
22146
+ return {
22147
+ results: [],
22148
+ vaultSize: 0,
22149
+ reranked: false,
22150
+ hadV2Head: false,
22151
+ embeddingsUnavailable: prepared.embeddingsUnavailable,
22152
+ rankedOnCosine: false
22153
+ };
21984
22154
  }
21985
22155
  if (prepared.memories.length === 0) {
21986
- return { results: [], vaultSize: prepared.vaultSize, reranked: false, hadV2Head: false };
22156
+ return {
22157
+ results: [],
22158
+ vaultSize: prepared.vaultSize,
22159
+ reranked: false,
22160
+ hadV2Head: false,
22161
+ embeddingsUnavailable: prepared.embeddingsUnavailable,
22162
+ rankedOnCosine: false
22163
+ };
21987
22164
  }
21988
- return rankPreparedVaultCandidates(query, prepared, embeddingOptions, searchOptions);
22165
+ const ranked = await rankPreparedVaultCandidates(
22166
+ query,
22167
+ prepared,
22168
+ embeddingOptions,
22169
+ searchOptions
22170
+ );
22171
+ return { ...ranked, rankedOnCosine: !ranked.embeddingsUnavailable };
21989
22172
  }
21990
22173
  async function searchVaultMemories(query, vaultCtx, embeddingOptions, cache, searchOptions) {
21991
22174
  const { results } = await searchVaultMemoriesWithSize(
@@ -22059,26 +22242,36 @@ function createMemoryVaultSearchTool(vaultCtx, embeddingOptions, cache, searchOp
22059
22242
  const budget = searchOptions?.decompose === "llm" && searchOptions.decomposeOptions ? "high" : searchOptions?.rerank ? "mid" : "low";
22060
22243
  const folderId = searchOptions?.folderId ?? argFolderId;
22061
22244
  if (searchOptions?.useFusion === false) {
22062
- const legacy = await searchVaultMemories(query, vaultCtx, embeddingOptions, cache, {
22063
- limit: requestLimit,
22064
- minSimilarity,
22065
- useFusion: false,
22066
- ...tuningForward,
22067
- ...folderId !== void 0 && { folderId },
22068
- ...searchOptions?.scopes && { scopes: searchOptions.scopes }
22069
- });
22245
+ const { results: legacy, embeddingsUnavailable } = await searchVaultMemoriesWithSize(
22246
+ query,
22247
+ vaultCtx,
22248
+ embeddingOptions,
22249
+ cache,
22250
+ {
22251
+ limit: requestLimit,
22252
+ minSimilarity,
22253
+ useFusion: false,
22254
+ ...tuningForward,
22255
+ ...folderId !== void 0 && { folderId },
22256
+ ...searchOptions?.scopes && { scopes: searchOptions.scopes }
22257
+ }
22258
+ );
22070
22259
  if (legacy.length === 0) {
22071
- return "No relevant memories found in the vault.";
22260
+ return embeddingsUnavailable ? EMBEDDINGS_DEGRADED_EMPTY_NO_LEXICAL : "No relevant memories found in the vault.";
22072
22261
  }
22073
22262
  return formatVaultHits(
22074
22263
  legacy.map((r) => ({ id: r.uniqueId, content: r.content, score: r.similarity }))
22075
22264
  );
22076
22265
  }
22077
22266
  const { recall: recall2 } = await Promise.resolve().then(() => (init_recall(), recall_exports));
22267
+ let recallDegraded = [];
22078
22268
  const result = await recall2(
22079
22269
  query,
22080
22270
  { vaultCtx, embeddingOptions, vaultCache: cache },
22081
22271
  {
22272
+ onDiagnostics: (d) => {
22273
+ recallDegraded = d.degraded;
22274
+ },
22082
22275
  types: ["fact"],
22083
22276
  limit: requestLimit,
22084
22277
  minScore: minSimilarity,
@@ -22099,7 +22292,7 @@ function createMemoryVaultSearchTool(vaultCtx, embeddingOptions, cache, searchOp
22099
22292
  return "The memory vault is empty. No memories have been saved yet.";
22100
22293
  }
22101
22294
  if (result.memories.length === 0) {
22102
- return "No relevant memories found in the vault.";
22295
+ return recallDegraded.includes("embeddings-unavailable") ? EMBEDDINGS_DEGRADED_EMPTY : "No relevant memories found in the vault.";
22103
22296
  }
22104
22297
  const formatted = formatVaultHits(
22105
22298
  result.memories.map((m) => ({
@@ -22118,7 +22311,7 @@ ${formatted}`;
22118
22311
  }
22119
22312
  };
22120
22313
  }
22121
- var SUPERSESSION_SIMILARITY_THRESHOLD, SUPERSESSION_MIN_AGE_GAP_MS, SUPERSESSION_BOOST_FACTOR, SUPERSESSION_MAX_WINDOW;
22314
+ var SUPERSESSION_SIMILARITY_THRESHOLD, SUPERSESSION_MIN_AGE_GAP_MS, SUPERSESSION_BOOST_FACTOR, SUPERSESSION_MAX_WINDOW, EMBEDDINGS_DEGRADED_EMPTY_NO_LEXICAL;
22122
22315
  var init_searchTool = __esm({
22123
22316
  "src/lib/memoryVault/searchTool.ts"() {
22124
22317
  "use strict";
@@ -22126,6 +22319,7 @@ var init_searchTool = __esm({
22126
22319
  init_operations3();
22127
22320
  init_logger();
22128
22321
  init_mmr();
22322
+ init_recallConstants();
22129
22323
  init_recency();
22130
22324
  init_reranker();
22131
22325
  init_rrf();
@@ -22139,6 +22333,7 @@ var init_searchTool = __esm({
22139
22333
  SUPERSESSION_MIN_AGE_GAP_MS = 30 * 24 * 60 * 60 * 1e3;
22140
22334
  SUPERSESSION_BOOST_FACTOR = 0.8;
22141
22335
  SUPERSESSION_MAX_WINDOW = 50;
22336
+ 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.";
22142
22337
  }
22143
22338
  });
22144
22339
 
@@ -27835,6 +28030,11 @@ async function retain(content, ctx, options = {}) {
27835
28030
  ...options.folderId !== void 0 && { folderId: options.folderId }
27836
28031
  }
27837
28032
  );
28033
+ if (prepared.embeddingFailure || prepared.embeddingsUnavailable) {
28034
+ throw new Error(
28035
+ "retain: embeddings unavailable \u2014 refusing to auto-merge against an inert cosine lane, which would create a duplicate instead of merging into the existing memory. Retry when embeddings recover, or pass enableAutoMerge: false to force a create."
28036
+ );
28037
+ }
27838
28038
  if (options.consolidateOptions) {
27839
28039
  const outcome = await tryConsolidate(trimmed, ctx, options, prepared);
27840
28040
  if (outcome) {
@@ -29086,12 +29286,7 @@ init_recall();
29086
29286
 
29087
29287
  // src/lib/memory/recallTool.ts
29088
29288
  init_recall();
29089
-
29090
- // src/lib/memory/recallConstants.ts
29091
- var RECALL_TOOL_NAME = "recall_memory";
29092
- var RECALL_MAX_LIMIT = 50;
29093
-
29094
- // src/lib/memory/recallTool.ts
29289
+ init_recallConstants();
29095
29290
  var DEFAULT_LIMIT2 = 8;
29096
29291
  var DEFAULT_BUDGET2 = "low";
29097
29292
  function formatEventTime(start, end, kind) {
@@ -29241,7 +29436,11 @@ function createRecallTool(ctx, toolOptions, callbacks) {
29241
29436
  conversationMemories += effectiveLimit;
29242
29437
  let surfaced = 0;
29243
29438
  try {
29439
+ let recallDegraded = [];
29244
29440
  const recallOpts = {
29441
+ onDiagnostics: (d) => {
29442
+ recallDegraded = d.degraded;
29443
+ },
29245
29444
  types: defaultTypes,
29246
29445
  limit: effectiveLimit,
29247
29446
  budget: defaultBudget,
@@ -29273,6 +29472,9 @@ function createRecallTool(ctx, toolOptions, callbacks) {
29273
29472
  }
29274
29473
  }
29275
29474
  surfaced = result.memories.length;
29475
+ if (result.memories.length === 0 && recallDegraded.includes("embeddings-unavailable")) {
29476
+ return EMBEDDINGS_DEGRADED_EMPTY;
29477
+ }
29276
29478
  const formatted = formatRecallResult(result.memories);
29277
29479
  return truncatedByBudget && surfaced > 0 ? `${formatted}
29278
29480
 
@@ -29453,6 +29655,7 @@ init_embeddings();
29453
29655
  init_vector();
29454
29656
  init_observationTrend();
29455
29657
  init_recall();
29658
+ init_recallConstants();
29456
29659
  var DEFAULT_SYNTHESIS_MODEL = "inclusionai/ling-2.6-flash";
29457
29660
  var DEFAULT_FACET_RECALL_LIMIT = 20;
29458
29661
  var DEFAULT_FACET_MAX_TOKENS = 512;
@@ -32749,6 +32952,9 @@ var CompletionsStrategy = class {
32749
32952
  }
32750
32953
  }
32751
32954
  }
32955
+ if (choice.finish_reason) {
32956
+ accumulator.finishReason = choice.finish_reason;
32957
+ }
32752
32958
  if (choice.finish_reason === "tool_calls" || choice.finish_reason === "stop") {
32753
32959
  if (accumulator.toolCalls.size > 0) {
32754
32960
  accumulator.content = mergeXaiInlineParameterTags(
@@ -32831,7 +33037,9 @@ var CompletionsStrategy = class {
32831
33037
  content: finalContent,
32832
33038
  ...toolCalls && { tool_calls: toolCalls }
32833
33039
  },
32834
- finish_reason: toolCalls ? "tool_calls" : "stop"
33040
+ // Prefer what the provider actually said; fall back to the derived
33041
+ // value only when the stream carried no finish_reason at all.
33042
+ finish_reason: accumulator.finishReason ?? (toolCalls ? "tool_calls" : "stop")
32835
33043
  }
32836
33044
  ],
32837
33045
  ...usage && { usage },
@@ -33502,7 +33710,7 @@ var ResponsesStrategy = class {
33502
33710
  }
33503
33711
  return result;
33504
33712
  }
33505
- if (typedChunk.type === "response.completed") {
33713
+ if (typedChunk.type === "response.completed" || typedChunk.type === "response.incomplete") {
33506
33714
  if (typedChunk.response?.usage) {
33507
33715
  const u = typedChunk.response.usage;
33508
33716
  const promptTokens = u.input_tokens ?? u.prompt_tokens ?? 0;
@@ -33518,6 +33726,11 @@ var ResponsesStrategy = class {
33518
33726
  ...creditsExhausted !== void 0 && { credits_exhausted: creditsExhausted }
33519
33727
  };
33520
33728
  }
33729
+ const resp = typedChunk.response;
33730
+ const looksIncomplete = typedChunk.type === "response.incomplete" || resp?.status === "incomplete";
33731
+ if (looksIncomplete && resp?.incomplete_details?.reason === "max_output_tokens") {
33732
+ accumulator.finishReason = "length";
33733
+ }
33521
33734
  if (typedChunk.response?.tools_checksum && !accumulator.toolsChecksum) {
33522
33735
  accumulator.toolsChecksum = typedChunk.response.tools_checksum;
33523
33736
  }
@@ -35054,7 +35267,17 @@ Executing tool: ${toolInfo}
35054
35267
  flushDeAnon();
35055
35268
  currentAccumulator.content += tip;
35056
35269
  }
35270
+ const truncatedToNothing = (acc) => acc.finishReason === "length" && acc.toolCalls.size === 0 && acc.content.trim() === "";
35271
+ const TRUNCATION_ERROR = "Model response was truncated at the output-token limit before it produced any usable content or tool call. Retry with a higher `maxOutputTokens`, or prompt for fewer/smaller tool calls per turn.";
35057
35272
  if (toolIteration > 0) {
35273
+ if (truncatedToNothing(currentAccumulator)) {
35274
+ await fireRunError({ error: TRUNCATION_ERROR, stage: "model" });
35275
+ return {
35276
+ data: buildResponseFinal(currentAccumulator),
35277
+ error: TRUNCATION_ERROR,
35278
+ toolsChecksum: currentAccumulator.toolsChecksum
35279
+ };
35280
+ }
35058
35281
  const finalResponse = buildResponseFinal(currentAccumulator);
35059
35282
  if (onFinish) onFinish(finalResponse);
35060
35283
  await fireRunEnd({
@@ -35068,6 +35291,14 @@ Executing tool: ${toolInfo}
35068
35291
  autoExecutedToolResults: accumulatedToolResults
35069
35292
  };
35070
35293
  }
35294
+ if (truncatedToNothing(accumulator)) {
35295
+ await fireRunError({ error: TRUNCATION_ERROR, stage: "model" });
35296
+ return {
35297
+ data: response,
35298
+ error: TRUNCATION_ERROR,
35299
+ toolsChecksum: accumulator.toolsChecksum
35300
+ };
35301
+ }
35071
35302
  if (onFinish) onFinish(response);
35072
35303
  await fireRunEnd({
35073
35304
  finalContent: finalContentText(accumulator),