@anuma/sdk 2.3.1 → 2.4.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.
@@ -2770,7 +2770,13 @@ async function searchMessagesOp(ctx, queryVector, options) {
2770
2770
  );
2771
2771
  }
2772
2772
  async function searchChunksOp(ctx, queryVector, options) {
2773
- const { limit = 10, minSimilarity = 0.5, conversationId, embeddingModel } = options || {};
2773
+ const {
2774
+ limit = 10,
2775
+ minSimilarity = 0.5,
2776
+ conversationId,
2777
+ embeddingModel,
2778
+ chunkCache
2779
+ } = options || {};
2774
2780
  const activeConversations = await ctx.conversationsCollection.query(import_watermelondb.Q.where("is_deleted", false)).fetch();
2775
2781
  const activeConversationIds = new Set(
2776
2782
  // Use _getRaw for reliable raw column access
@@ -2790,23 +2796,54 @@ async function searchChunksOp(ctx, queryVector, options) {
2790
2796
  continue;
2791
2797
  }
2792
2798
  }
2799
+ const version = Number(message._getRaw("updated_at")) || 0;
2800
+ const cached = chunkCache?.get(message.id);
2801
+ if (cached && cached.version === version) {
2802
+ if (cached.chunks.length > 0) {
2803
+ for (let ci = 0; ci < cached.chunks.length; ci++) {
2804
+ const vec = cached.chunks[ci];
2805
+ if (vec.length === 0) continue;
2806
+ const similarity = cosineSimilarity(queryVector, vec);
2807
+ if (similarity >= minSimilarity) {
2808
+ candidates.push({
2809
+ message,
2810
+ similarity,
2811
+ chunkTextSource: { kind: "chunk-cached", chunkIndex: ci, version }
2812
+ });
2813
+ }
2814
+ }
2815
+ } else if (cached.fallback) {
2816
+ const similarity = cosineSimilarity(queryVector, cached.fallback);
2817
+ if (similarity >= minSimilarity) {
2818
+ candidates.push({ message, similarity, chunkTextSource: { kind: "message" } });
2819
+ }
2820
+ }
2821
+ continue;
2822
+ }
2793
2823
  const chunks = await readJsonField(message, "chunks", ctx.walletAddress);
2794
2824
  if (chunks && chunks.length > 0) {
2795
- for (const chunk of chunks) {
2796
- if (!chunk.vector || chunk.vector.length === 0) continue;
2797
- const similarity = cosineSimilarity(queryVector, chunk.vector);
2825
+ const vectors = chunks.map(
2826
+ (chunk) => chunk.vector && chunk.vector.length > 0 ? Float32Array.from(chunk.vector) : new Float32Array(0)
2827
+ );
2828
+ chunkCache?.set(message.id, { version, chunks: vectors });
2829
+ for (let ci = 0; ci < chunks.length; ci++) {
2830
+ const vec = vectors[ci];
2831
+ if (vec.length === 0) continue;
2832
+ const similarity = cosineSimilarity(queryVector, vec);
2798
2833
  if (similarity >= minSimilarity) {
2799
2834
  candidates.push({
2800
2835
  message,
2801
2836
  similarity,
2802
- chunkTextSource: { kind: "chunk", text: chunk.text }
2837
+ chunkTextSource: { kind: "chunk", text: chunks[ci].text }
2803
2838
  });
2804
2839
  }
2805
2840
  }
2806
2841
  } else {
2807
2842
  const messageVector = await readJsonField(message, "vector", ctx.walletAddress);
2808
2843
  if (!messageVector || messageVector.length === 0) continue;
2809
- const similarity = cosineSimilarity(queryVector, messageVector);
2844
+ const fallback = Float32Array.from(messageVector);
2845
+ chunkCache?.set(message.id, { version, chunks: [], fallback });
2846
+ const similarity = cosineSimilarity(queryVector, fallback);
2810
2847
  if (similarity >= minSimilarity) {
2811
2848
  candidates.push({ message, similarity, chunkTextSource: { kind: "message" } });
2812
2849
  }
@@ -2833,9 +2870,31 @@ async function searchChunksOp(ctx, queryVector, options) {
2833
2870
  })
2834
2871
  )
2835
2872
  );
2873
+ const chunkStateByMsgId = /* @__PURE__ */ new Map();
2874
+ const needsChunkText = /* @__PURE__ */ new Set();
2875
+ for (const c of topK) {
2876
+ if (c.chunkTextSource.kind === "chunk-cached") needsChunkText.add(c.message.id);
2877
+ }
2878
+ await Promise.all(
2879
+ Array.from(needsChunkText, async (id) => {
2880
+ const m = uniqueMessages.get(id);
2881
+ const parsed = await readJsonField(m, "chunks", ctx.walletAddress);
2882
+ const version = Number(m._getRaw("updated_at")) || 0;
2883
+ if (parsed) chunkStateByMsgId.set(id, { version, chunks: parsed });
2884
+ })
2885
+ );
2836
2886
  return topK.map(({ message, similarity, chunkTextSource }) => {
2837
2887
  const stored = { ...storedById.get(message.id) };
2838
- const chunkText2 = chunkTextSource.kind === "chunk" ? chunkTextSource.text : stored.content;
2888
+ let chunkText2;
2889
+ if (chunkTextSource.kind === "chunk") {
2890
+ chunkText2 = chunkTextSource.text;
2891
+ } else if (chunkTextSource.kind === "chunk-cached") {
2892
+ const state = chunkStateByMsgId.get(message.id);
2893
+ const chunk = state && state.version === chunkTextSource.version ? state.chunks[chunkTextSource.chunkIndex] : void 0;
2894
+ chunkText2 = chunk?.text ?? stored.content;
2895
+ } else {
2896
+ chunkText2 = stored.content;
2897
+ }
2839
2898
  return { chunkText: chunkText2, message: stored, similarity };
2840
2899
  });
2841
2900
  }
@@ -5172,9 +5231,9 @@ function recencyMultiplier(updatedAt, opts = {}) {
5172
5231
  const updatedMs = updatedAt.getTime();
5173
5232
  if (!Number.isFinite(updatedMs)) return noDate;
5174
5233
  const now = opts.now ?? /* @__PURE__ */ new Date();
5175
- const nowMs = now.getTime();
5176
- if (!Number.isFinite(nowMs)) return noDate;
5177
- const ageDays = Math.max(0, (nowMs - updatedMs) / DAY_MS);
5234
+ const nowMs2 = now.getTime();
5235
+ if (!Number.isFinite(nowMs2)) return noDate;
5236
+ const ageDays = Math.max(0, (nowMs2 - updatedMs) / DAY_MS);
5178
5237
  const perYearDecay = opts.perYearDecay ?? DEFAULT_PER_YEAR_DECAY;
5179
5238
  const floor = opts.floor ?? DEFAULT_FLOOR;
5180
5239
  const decay = perYearDecay * (ageDays / 365);
@@ -5192,14 +5251,42 @@ var init_recency = __esm({
5192
5251
  });
5193
5252
 
5194
5253
  // src/lib/memory/reranker.ts
5254
+ function isRerankerAvailable() {
5255
+ return available;
5256
+ }
5257
+ function isModuleMissing(err) {
5258
+ let e = err;
5259
+ for (let depth = 0; depth < 5 && e !== null && e !== void 0; depth++) {
5260
+ const code = e.code;
5261
+ if (code === "ERR_MODULE_NOT_FOUND" || code === "MODULE_NOT_FOUND") return true;
5262
+ if (e instanceof Error && /cannot find (module|package)|failed to resolve|unable to resolve module|requiring unknown module/i.test(
5263
+ e.message
5264
+ )) {
5265
+ return true;
5266
+ }
5267
+ e = e.cause;
5268
+ }
5269
+ return false;
5270
+ }
5195
5271
  async function getModel() {
5272
+ if (available === false) throw new RerankerUnavailableError(void 0);
5196
5273
  if (!modelPromise) {
5197
5274
  const load = (async () => {
5198
- const transformers = await import("@huggingface/transformers");
5275
+ let transformers;
5276
+ try {
5277
+ transformers = await import("@huggingface/transformers");
5278
+ } catch (err) {
5279
+ if (isModuleMissing(err)) {
5280
+ available = false;
5281
+ throw new RerankerUnavailableError(err);
5282
+ }
5283
+ throw err;
5284
+ }
5199
5285
  const [tokenizer, model] = await Promise.all([
5200
5286
  transformers.AutoTokenizer.from_pretrained(MODEL_ID),
5201
5287
  transformers.AutoModelForSequenceClassification.from_pretrained(MODEL_ID)
5202
5288
  ]);
5289
+ available = true;
5203
5290
  return { tokenizer, model };
5204
5291
  })();
5205
5292
  modelPromise = load;
@@ -5241,11 +5328,19 @@ async function rerankPairs(query, items) {
5241
5328
  }
5242
5329
  return items.map((item, i) => ({ id: item.id, content: item.content, score: scores[i] })).sort((a, b) => b.score - a.score);
5243
5330
  }
5244
- var MODEL_ID, modelPromise;
5331
+ var MODEL_ID, RerankerUnavailableError, available, modelPromise;
5245
5332
  var init_reranker = __esm({
5246
5333
  "src/lib/memory/reranker.ts"() {
5247
5334
  "use strict";
5248
5335
  MODEL_ID = "Xenova/ms-marco-MiniLM-L-6-v2";
5336
+ RerankerUnavailableError = class extends Error {
5337
+ constructor(reason) {
5338
+ super("cross-encoder reranker unavailable (@huggingface/transformers not installed)");
5339
+ this.name = "RerankerUnavailableError";
5340
+ this.reason = reason;
5341
+ }
5342
+ };
5343
+ available = void 0;
5249
5344
  modelPromise = null;
5250
5345
  }
5251
5346
  });
@@ -5914,22 +6009,67 @@ async function recall(query, ctx, options = {}) {
5914
6009
  const flags = flagsForBudget(requestedBudget);
5915
6010
  const decomposeAvailable = flags.decompose && !!options.decomposeOptions;
5916
6011
  const usedBudget = requestedBudget === "high" && !decomposeAvailable ? "mid" : requestedBudget;
6012
+ const t0 = nowMs();
6013
+ let prepMs = 0;
6014
+ let factLaneMs = 0;
6015
+ let chunkLaneMs = 0;
6016
+ let fuseMs = 0;
6017
+ const factResults = [];
6018
+ const chunkResults = [];
6019
+ let vaultSize;
6020
+ let didRerank = false;
6021
+ let hadV2Head = false;
6022
+ const emitDiagnostics = (candidateCount) => {
6023
+ const cb = options.onDiagnostics;
6024
+ if (!cb) return;
6025
+ const degraded = [];
6026
+ if (flags.rerank && factResults.length > 0 && hadV2Head && !didRerank) {
6027
+ degraded.push("rerank-unavailable");
6028
+ }
6029
+ if (flags.decompose && !decomposeAvailable) degraded.push("decompose-unavailable");
6030
+ const diagnostics = {
6031
+ usedBudget,
6032
+ reranked: didRerank,
6033
+ candidateCount,
6034
+ ...vaultSize !== void 0 && { vaultSize },
6035
+ factCount: factResults.length,
6036
+ chunkCount: chunkResults.length,
6037
+ timings: {
6038
+ total: nowMs() - t0,
6039
+ prep: prepMs,
6040
+ factLane: factLaneMs,
6041
+ chunkLane: chunkLaneMs,
6042
+ fuse: fuseMs
6043
+ },
6044
+ degraded
6045
+ };
6046
+ try {
6047
+ cb(diagnostics);
6048
+ } catch {
6049
+ }
6050
+ };
5917
6051
  if (!query || typeof query !== "string" || query.trim().length === 0) {
6052
+ emitDiagnostics(0);
5918
6053
  return { memories: [], usedBudget, reranked: false, candidateCount: 0 };
5919
6054
  }
5920
6055
  const needsChunkEmbedding = types.includes("chunk") && ctx.storageCtx;
5921
6056
  const wantsTemporal = types.includes("fact") && ctx.vaultCtx;
6057
+ const prepStart = nowMs();
5922
6058
  const [queryEmbedding, entityRanking, temporalRanking] = await Promise.all([
5923
6059
  needsChunkEmbedding ? generateEmbedding(query, ctx.embeddingOptions) : Promise.resolve(void 0),
5924
6060
  buildGraphLaneRanking(query, ctx),
5925
6061
  wantsTemporal ? buildTemporalLaneRanking(query, ctx.vaultCtx, options.now) : Promise.resolve([])
5926
6062
  ]);
5927
- const factResults = [];
5928
- const chunkResults = [];
5929
- let vaultSize;
6063
+ prepMs = nowMs() - prepStart;
5930
6064
  if (types.includes("fact") && ctx.vaultCtx && ctx.vaultCache) {
6065
+ const factStart = nowMs();
5931
6066
  const vaultMinScore = options.minScore ?? DEFAULT_FACT_MIN_SCORE;
5932
- const { results, vaultSize: size } = await searchVaultMemoriesWithSize(
6067
+ const {
6068
+ results,
6069
+ vaultSize: size,
6070
+ reranked,
6071
+ hadV2Head: v2Head
6072
+ } = await searchVaultMemoriesWithSize(
5933
6073
  query,
5934
6074
  ctx.vaultCtx,
5935
6075
  ctx.embeddingOptions,
@@ -5980,14 +6120,19 @@ async function recall(query, ctx, options = {}) {
5980
6120
  )
5981
6121
  );
5982
6122
  vaultSize = size;
6123
+ didRerank = reranked;
6124
+ hadV2Head = v2Head;
6125
+ factLaneMs = nowMs() - factStart;
5983
6126
  }
5984
6127
  if (types.includes("chunk") && ctx.storageCtx && queryEmbedding) {
6128
+ const chunkStart = nowMs();
5985
6129
  const chunkMinScore = options.minScore ?? DEFAULT_CHUNK_MIN_SCORE;
5986
6130
  const results = await searchChunksOp(ctx.storageCtx, queryEmbedding, {
5987
6131
  limit: types.includes("fact") ? Math.max(limit * 2, 16) : limit,
5988
6132
  minSimilarity: chunkMinScore,
5989
6133
  embeddingModel: ctx.embeddingOptions.model ?? DEFAULT_API_EMBEDDING_MODEL,
5990
- ...options.conversationId && { conversationId: options.conversationId }
6134
+ ...options.conversationId && { conversationId: options.conversationId },
6135
+ ...ctx.chunkCache && { chunkCache: ctx.chunkCache }
5991
6136
  });
5992
6137
  chunkResults.push(
5993
6138
  ...dedupeBy(
@@ -5997,18 +6142,23 @@ async function recall(query, ctx, options = {}) {
5997
6142
  (r) => r.chunkText.trim()
5998
6143
  )
5999
6144
  );
6145
+ chunkLaneMs = nowMs() - chunkStart;
6000
6146
  }
6147
+ const fuseStart = nowMs();
6001
6148
  if (types.length === 1 || factResults.length === 0 || chunkResults.length === 0) {
6002
6149
  const memories2 = [
6003
6150
  ...factResults.map(toFactMemory),
6004
6151
  ...chunkResults.map(toChunkMemory)
6005
6152
  ];
6006
6153
  memories2.sort((a, b) => b.score - a.score);
6154
+ const candidateCount = factResults.length + chunkResults.length;
6155
+ fuseMs = nowMs() - fuseStart;
6156
+ emitDiagnostics(candidateCount);
6007
6157
  return {
6008
6158
  memories: memories2.slice(0, limit),
6009
6159
  usedBudget,
6010
- reranked: flags.rerank,
6011
- candidateCount: factResults.length + chunkResults.length,
6160
+ reranked: didRerank,
6161
+ candidateCount,
6012
6162
  ...vaultSize !== void 0 && { vaultSize }
6013
6163
  };
6014
6164
  }
@@ -6057,10 +6207,12 @@ async function recall(query, ctx, options = {}) {
6057
6207
  suppressed = next;
6058
6208
  memories = selectWith(suppressed);
6059
6209
  }
6210
+ fuseMs = nowMs() - fuseStart;
6211
+ emitDiagnostics(byId.size);
6060
6212
  return {
6061
6213
  memories,
6062
6214
  usedBudget,
6063
- reranked: flags.rerank,
6215
+ reranked: didRerank,
6064
6216
  candidateCount: byId.size,
6065
6217
  ...vaultSize !== void 0 && { vaultSize }
6066
6218
  };
@@ -6124,7 +6276,7 @@ function toChunkMemory(r) {
6124
6276
  updatedAt: r.message.createdAt
6125
6277
  };
6126
6278
  }
6127
- var DEFAULT_LIMIT, DEFAULT_BUDGET, DEFAULT_FACT_MIN_SCORE, DEFAULT_CHUNK_MIN_SCORE;
6279
+ var DEFAULT_LIMIT, DEFAULT_BUDGET, DEFAULT_FACT_MIN_SCORE, nowMs, DEFAULT_CHUNK_MIN_SCORE;
6128
6280
  var init_recall = __esm({
6129
6281
  "src/lib/memory/recall.ts"() {
6130
6282
  "use strict";
@@ -6140,6 +6292,7 @@ var init_recall = __esm({
6140
6292
  DEFAULT_LIMIT = 8;
6141
6293
  DEFAULT_BUDGET = "low";
6142
6294
  DEFAULT_FACT_MIN_SCORE = 0.1;
6295
+ nowMs = () => typeof performance !== "undefined" && typeof performance.now === "function" ? performance.now() : Date.now();
6143
6296
  DEFAULT_CHUNK_MIN_SCORE = 0.5;
6144
6297
  }
6145
6298
  });
@@ -6339,6 +6492,9 @@ async function rankFusedVaultMemoriesAsync(query, queryEmbedding, items, options
6339
6492
  bm25AdmissionDivisor: options?.bm25AdmissionDivisor,
6340
6493
  rrfK: options?.rrfK
6341
6494
  });
6495
+ if (options?.v2HeadStats) {
6496
+ options.v2HeadStats.hadResults = v2Ranked.length > 0;
6497
+ }
6342
6498
  let combined;
6343
6499
  let tailSlice = [];
6344
6500
  if (options?.rerank) {
@@ -6359,8 +6515,13 @@ async function rankFusedVaultMemoriesAsync(query, queryEmbedding, items, options
6359
6515
  return { ...r, similarity: v2 * (1 + ceWeight * ce) };
6360
6516
  });
6361
6517
  combined.sort((a, b) => b.similarity - a.similarity);
6518
+ if (headSlice.length > 0 && options.rerankStats) options.rerankStats.applied = true;
6362
6519
  } catch (err) {
6363
- getLogger().warn("[memory/search] cross-encoder rerank failed; using V2 ranking", err);
6520
+ if (err instanceof RerankerUnavailableError) {
6521
+ getLogger().debug("[memory/search] cross-encoder unavailable; using V2 ranking");
6522
+ } else {
6523
+ getLogger().warn("[memory/search] cross-encoder rerank failed; using V2 ranking", err);
6524
+ }
6364
6525
  combined = v2Ranked;
6365
6526
  }
6366
6527
  } else {
@@ -6494,6 +6655,9 @@ async function rankComposite(originalQuery, originalQueryEmbedding, subQueries,
6494
6655
  (r) => r !== null
6495
6656
  );
6496
6657
  combined.sort((a, b) => b.similarity - a.similarity);
6658
+ if (options?.v2HeadStats) {
6659
+ options.v2HeadStats.hadResults = combined.length > 0;
6660
+ }
6497
6661
  if (options?.includeUnrankedTail) {
6498
6662
  const fusedIds = new Set(combined.map((r) => r.uniqueId));
6499
6663
  for (const item of items) {
@@ -6529,11 +6693,16 @@ async function rankComposite(originalQuery, originalQueryEmbedding, subQueries,
6529
6693
  }));
6530
6694
  head.sort((a, b) => b.similarity - a.similarity);
6531
6695
  combined = [...head, ...tailSlice];
6696
+ if (headSlice.length > 0 && options.rerankStats) options.rerankStats.applied = true;
6532
6697
  } catch (err) {
6533
- getLogger().warn(
6534
- "[memory/search] composite cross-encoder rerank failed; using fused ranking",
6535
- err
6536
- );
6698
+ if (err instanceof RerankerUnavailableError) {
6699
+ getLogger().debug("[memory/search] cross-encoder unavailable; using fused ranking");
6700
+ } else {
6701
+ getLogger().warn(
6702
+ "[memory/search] composite cross-encoder rerank failed; using fused ranking",
6703
+ err
6704
+ );
6705
+ }
6537
6706
  }
6538
6707
  }
6539
6708
  if (options?.mmr) {
@@ -6625,7 +6794,7 @@ async function searchVaultMemoriesWithSize(query, vaultCtx, embeddingOptions, ca
6625
6794
  const minSimilarity = searchOptions?.minSimilarity ?? 0.1;
6626
6795
  const scopes = searchOptions?.scopes;
6627
6796
  if (!query || typeof query !== "string") {
6628
- return { results: [], vaultSize: 0 };
6797
+ return { results: [], vaultSize: 0, reranked: false, hadV2Head: false };
6629
6798
  }
6630
6799
  const folderId = searchOptions?.folderId;
6631
6800
  const queryOpts = {};
@@ -6642,7 +6811,7 @@ async function searchVaultMemoriesWithSize(query, vaultCtx, embeddingOptions, ca
6642
6811
  );
6643
6812
  }
6644
6813
  if (memories.length === 0) {
6645
- return { results: [], vaultSize: loaded.length };
6814
+ return { results: [], vaultSize: loaded.length, reranked: false, hadV2Head: false };
6646
6815
  }
6647
6816
  const queryEmbedding = await generateEmbedding(query, embeddingOptions);
6648
6817
  const currentModel = embeddingOptions.model ?? DEFAULT_API_EMBEDDING_MODEL;
@@ -6718,12 +6887,18 @@ async function searchVaultMemoriesWithSize(query, vaultCtx, embeddingOptions, ca
6718
6887
  memories.map((m) => [m.uniqueId, { createdAt: m.createdAt, updatedAt: m.updatedAt }])
6719
6888
  );
6720
6889
  const stampTimestamps = (out) => {
6721
- out.results = out.results.map((r) => {
6890
+ const results2 = out.results.map((r) => {
6722
6891
  const ts = timestampById.get(r.uniqueId);
6723
6892
  return ts ? { ...r, createdAt: ts.createdAt, updatedAt: ts.updatedAt } : r;
6724
6893
  });
6725
- return out;
6894
+ return {
6895
+ results: results2,
6896
+ vaultSize: out.vaultSize,
6897
+ reranked: out.reranked ?? false,
6898
+ hadV2Head: out.hadV2Head ?? false
6899
+ };
6726
6900
  };
6901
+ const rerankStats = { applied: false };
6727
6902
  const useFusion = searchOptions?.useFusion ?? true;
6728
6903
  const tuning = {
6729
6904
  ...searchOptions?.recencyAlpha !== void 0 && { recencyAlpha: searchOptions.recencyAlpha },
@@ -6750,6 +6925,7 @@ async function searchVaultMemoriesWithSize(query, vaultCtx, embeddingOptions, ca
6750
6925
  query: sq,
6751
6926
  embedding: subEmbeddings[i]
6752
6927
  }));
6928
+ const v2HeadStats = { hadResults: false };
6753
6929
  const composite = await rankComposite(query, queryEmbedding, subQueries, embeddedItems, {
6754
6930
  limit,
6755
6931
  minSimilarity,
@@ -6761,12 +6937,20 @@ async function searchVaultMemoriesWithSize(query, vaultCtx, embeddingOptions, ca
6761
6937
  ...searchOptions.mmr !== void 0 && { mmr: searchOptions.mmr },
6762
6938
  ...tuning,
6763
6939
  ...searchOptions.entityRanking && { entityRanking: searchOptions.entityRanking },
6764
- ...searchOptions.temporalRanking && { temporalRanking: searchOptions.temporalRanking }
6940
+ ...searchOptions.temporalRanking && { temporalRanking: searchOptions.temporalRanking },
6941
+ rerankStats,
6942
+ v2HeadStats
6943
+ });
6944
+ return stampTimestamps({
6945
+ results: composite,
6946
+ vaultSize: loaded.length,
6947
+ reranked: rerankStats.applied,
6948
+ hadV2Head: v2HeadStats.hadResults
6765
6949
  });
6766
- return stampTimestamps({ results: composite, vaultSize: loaded.length });
6767
6950
  }
6768
6951
  }
6769
6952
  if (useFusion && searchOptions?.rerank) {
6953
+ const v2HeadStats = { hadResults: false };
6770
6954
  const results2 = await rankFusedVaultMemoriesAsync(query, queryEmbedding, embeddedItems, {
6771
6955
  limit,
6772
6956
  minSimilarity,
@@ -6778,9 +6962,16 @@ async function searchVaultMemoriesWithSize(query, vaultCtx, embeddingOptions, ca
6778
6962
  ...searchOptions.mmr !== void 0 && { mmr: searchOptions.mmr },
6779
6963
  ...tuning,
6780
6964
  ...searchOptions.entityRanking && { entityRanking: searchOptions.entityRanking },
6781
- ...searchOptions.temporalRanking && { temporalRanking: searchOptions.temporalRanking }
6965
+ ...searchOptions.temporalRanking && { temporalRanking: searchOptions.temporalRanking },
6966
+ rerankStats,
6967
+ v2HeadStats
6968
+ });
6969
+ return stampTimestamps({
6970
+ results: results2,
6971
+ vaultSize: loaded.length,
6972
+ reranked: rerankStats.applied,
6973
+ hadV2Head: v2HeadStats.hadResults
6782
6974
  });
6783
- return stampTimestamps({ results: results2, vaultSize: loaded.length });
6784
6975
  }
6785
6976
  if (useFusion) {
6786
6977
  const results2 = rankFusedVaultMemories(query, queryEmbedding, embeddedItems, {
@@ -6790,7 +6981,7 @@ async function searchVaultMemoriesWithSize(query, vaultCtx, embeddingOptions, ca
6790
6981
  ...searchOptions?.entityRanking && { entityRanking: searchOptions.entityRanking },
6791
6982
  ...searchOptions?.temporalRanking && { temporalRanking: searchOptions.temporalRanking }
6792
6983
  });
6793
- return stampTimestamps({ results: results2, vaultSize: loaded.length });
6984
+ return stampTimestamps({ results: results2, vaultSize: loaded.length, hadV2Head: results2.length > 0 });
6794
6985
  }
6795
6986
  const results = rankVaultMemories(query, queryEmbedding, embeddedItems, {
6796
6987
  limit,
@@ -6802,7 +6993,7 @@ async function searchVaultMemoriesWithSize(query, vaultCtx, embeddingOptions, ca
6802
6993
  supersessionWindow: searchOptions.supersessionWindow
6803
6994
  }
6804
6995
  });
6805
- return stampTimestamps({ results, vaultSize: loaded.length });
6996
+ return stampTimestamps({ results, vaultSize: loaded.length, hadV2Head: results.length > 0 });
6806
6997
  }
6807
6998
  async function searchVaultMemories(query, vaultCtx, embeddingOptions, cache, searchOptions) {
6808
6999
  const { results } = await searchVaultMemoriesWithSize(
@@ -7118,6 +7309,7 @@ __export(index_exports, {
7118
7309
  ConversationMemoryModel: () => ConversationMemory,
7119
7310
  DEFAULT_BACKUP_FOLDER: () => DEFAULT_BACKUP_FOLDER,
7120
7311
  DEFAULT_CACHE_EXPIRATION_MS: () => DEFAULT_CACHE_EXPIRATION_MS,
7312
+ DEFAULT_CHUNK_CACHE_SIZE: () => DEFAULT_CHUNK_CACHE_SIZE,
7121
7313
  DEFAULT_CHUNK_OVERLAP: () => DEFAULT_CHUNK_OVERLAP,
7122
7314
  DEFAULT_CHUNK_SIZE: () => DEFAULT_CHUNK_SIZE,
7123
7315
  DEFAULT_DRIVE_CONVERSATIONS_FOLDER: () => DEFAULT_CONVERSATIONS_FOLDER,
@@ -7149,6 +7341,7 @@ __export(index_exports, {
7149
7341
  R2_DEFAULT_TTL_MS: () => R2_DEFAULT_TTL_MS,
7150
7342
  RECALL_MAX_LIMIT: () => RECALL_MAX_LIMIT,
7151
7343
  RECALL_TOOL_NAME: () => RECALL_TOOL_NAME,
7344
+ RerankerUnavailableError: () => RerankerUnavailableError,
7152
7345
  SDK_SCHEMA_VERSION: () => SDK_SCHEMA_VERSION,
7153
7346
  SERVER_TOOL_DEPENDENCY_SETS: () => SERVER_TOOL_DEPENDENCY_SETS,
7154
7347
  SLIDER_CONFIG: () => SLIDER_CONFIG,
@@ -7207,6 +7400,7 @@ __export(index_exports, {
7207
7400
  conversationMemoryToStored: () => conversationMemoryToStored,
7208
7401
  cosineInt8: () => cosineInt8,
7209
7402
  createAutoExtractor: () => createAutoExtractor,
7403
+ createChunkVectorCache: () => createChunkVectorCache,
7210
7404
  createCryptoPricePreProcessor: () => createCryptoPricePreProcessor,
7211
7405
  createGitHubTools: () => createGitHubTools,
7212
7406
  createMediaBatchOp: () => createMediaBatchOp,
@@ -7214,6 +7408,7 @@ __export(index_exports, {
7214
7408
  createMemoryEngineTool: () => createMemoryEngineTool,
7215
7409
  createMemoryVaultSearchTool: () => createMemoryVaultSearchTool,
7216
7410
  createMemoryVaultTool: () => createMemoryVaultTool,
7411
+ createPlatformCursorStore: () => createPlatformCursorStore,
7217
7412
  createProjectOp: () => createProjectOp,
7218
7413
  createRecallTool: () => createRecallTool,
7219
7414
  createSavedToolOp: () => createSavedToolOp,
@@ -7361,6 +7556,7 @@ __export(index_exports, {
7361
7556
  isNotionCallback: () => isNotionCallback,
7362
7557
  isOPFSSupported: () => isOPFSSupported,
7363
7558
  isR2UrlExpired: () => isR2UrlExpired,
7559
+ isRerankerAvailable: () => isRerankerAvailable,
7364
7560
  isSupportedFile: () => isSupportedFile,
7365
7561
  isSupportedMediaType: () => isSupportedMediaType,
7366
7562
  linkMemoryEntitiesOp: () => linkMemoryEntitiesOp,
@@ -14596,6 +14792,23 @@ function parseLocalCalendarDay2(year, month, day) {
14596
14792
  // src/lib/memory/autoExtractWorker.ts
14597
14793
  init_logger();
14598
14794
  init_redactor();
14795
+ function createPlatformCursorStore(storage, keyPrefix = "anuma:mem:xcursor:") {
14796
+ return {
14797
+ get(conversationId) {
14798
+ try {
14799
+ return storage.getItem(keyPrefix + conversationId) ?? void 0;
14800
+ } catch {
14801
+ return void 0;
14802
+ }
14803
+ },
14804
+ set(conversationId, messageId) {
14805
+ try {
14806
+ storage.setItem(keyPrefix + conversationId, messageId);
14807
+ } catch {
14808
+ }
14809
+ }
14810
+ };
14811
+ }
14599
14812
  var DEFAULT_WINDOW_SIZE = 6;
14600
14813
  var DEFAULT_MAX_WINDOW_SIZE = 20;
14601
14814
  var CONTEXT_OVERLAP = 2;
@@ -14615,12 +14828,24 @@ function createAutoExtractor(options) {
14615
14828
  let inflight = 0;
14616
14829
  let disposed = false;
14617
14830
  const conversations = /* @__PURE__ */ new Map();
14831
+ const cursorStore = options.cursorStore;
14832
+ const hydrate = (state, conversationId) => {
14833
+ if (state.hydrated) return;
14834
+ state.hydrated = true;
14835
+ if (!cursorStore || conversationId === void 0 || state.watermark !== void 0) return;
14836
+ try {
14837
+ const persisted = cursorStore.get(conversationId);
14838
+ if (persisted) state.watermark = persisted;
14839
+ } catch {
14840
+ }
14841
+ };
14618
14842
  const stateFor = (conversationId) => {
14619
14843
  let state = conversations.get(conversationId);
14620
14844
  if (!state) {
14621
14845
  if (conversations.size >= maxTrackedConversations) evictOldestIdle(conversationId);
14622
14846
  state = {};
14623
14847
  conversations.set(conversationId, state);
14848
+ hydrate(state, conversationId);
14624
14849
  }
14625
14850
  return state;
14626
14851
  };
@@ -14633,10 +14858,12 @@ function createAutoExtractor(options) {
14633
14858
  }
14634
14859
  };
14635
14860
  function computeWindow(messages, conversationId) {
14636
- const lastId = conversations.get(conversationId)?.watermark;
14861
+ const lastId = stateFor(conversationId).watermark;
14637
14862
  const idx = lastId === void 0 ? -1 : messages.findIndex((m) => m.id === lastId);
14638
- if (idx === -1) return messages.slice(-windowSize);
14639
- if (idx >= messages.length - 1) return [];
14863
+ if (idx === -1) {
14864
+ return { window: messages.slice(-windowSize), contiguous: messages.length <= windowSize };
14865
+ }
14866
+ if (idx >= messages.length - 1) return { window: [], contiguous: true };
14640
14867
  const start = Math.max(0, idx + 1 - CONTEXT_OVERLAP);
14641
14868
  let window2 = messages.slice(start);
14642
14869
  if (window2.length > maxWindowSize) {
@@ -14645,8 +14872,21 @@ function createAutoExtractor(options) {
14645
14872
  );
14646
14873
  window2 = window2.slice(0, maxWindowSize);
14647
14874
  }
14648
- return window2;
14875
+ return { window: window2, contiguous: true };
14649
14876
  }
14877
+ const persistCursor = (conversationId, messages, advancedToId, contiguous) => {
14878
+ if (!cursorStore || conversationId === void 0 || !contiguous) return;
14879
+ try {
14880
+ const stored = cursorStore.get(conversationId);
14881
+ if (stored && stored !== advancedToId) {
14882
+ const storedIdx = messages.findIndex((m) => m.id === stored);
14883
+ const newIdx = messages.findIndex((m) => m.id === advancedToId);
14884
+ if (storedIdx !== -1 && newIdx !== -1 && storedIdx >= newIdx) return;
14885
+ }
14886
+ cursorStore.set(conversationId, advancedToId);
14887
+ } catch {
14888
+ }
14889
+ };
14650
14890
  const consolidateBaseUrl = options.consolidate?.baseUrl ?? options.extract.baseUrl;
14651
14891
  const consolidateOptions = options.consolidate ? {
14652
14892
  ...options.extract.apiKey !== void 0 && { apiKey: options.extract.apiKey },
@@ -14692,7 +14932,7 @@ function createAutoExtractor(options) {
14692
14932
  return dispatch(messages, conversationId);
14693
14933
  }
14694
14934
  function dispatch(messages, conversationId) {
14695
- const window2 = computeWindow(messages, conversationId);
14935
+ const { window: window2, contiguous } = computeWindow(messages, conversationId);
14696
14936
  if (window2.length === 0) {
14697
14937
  options.onSkipped?.({ reason: "no-new-content", conversationId });
14698
14938
  drainPending();
@@ -14717,7 +14957,9 @@ function createAutoExtractor(options) {
14717
14957
  }
14718
14958
  }
14719
14959
  );
14720
- stateFor(conversationId).watermark = window2[window2.length - 1].id;
14960
+ const advancedTo = window2[window2.length - 1].id;
14961
+ stateFor(conversationId).watermark = advancedTo;
14962
+ persistCursor(conversationId, messages, advancedTo, contiguous);
14721
14963
  for (let i = 0; i < results.length; i++) {
14722
14964
  if (results[i].action === "suppressed") continue;
14723
14965
  options.onMemoryExtracted?.({
@@ -14764,6 +15006,13 @@ function createAutoExtractor(options) {
14764
15006
  };
14765
15007
  }
14766
15008
 
15009
+ // src/lib/memory/chunkVectorCache.ts
15010
+ init_lruCache();
15011
+ var DEFAULT_CHUNK_CACHE_SIZE = 5e3;
15012
+ function createChunkVectorCache(maxSize = DEFAULT_CHUNK_CACHE_SIZE) {
15013
+ return new LRUCache(maxSize);
15014
+ }
15015
+
14767
15016
  // src/lib/memory/index.ts
14768
15017
  init_recall();
14769
15018
 
@@ -15028,6 +15277,9 @@ function parseAnswer(body, base, parseSchema) {
15028
15277
  };
15029
15278
  }
15030
15279
 
15280
+ // src/lib/memory/index.ts
15281
+ init_reranker();
15282
+
15031
15283
  // src/lib/memory/topicExtract.ts
15032
15284
  init_operations2();
15033
15285
  init_operations3();
@@ -18168,6 +18420,7 @@ function useChatStorage(options) {
18168
18420
  [vaultCtx]
18169
18421
  );
18170
18422
  const vaultEmbeddingCacheRef = (0, import_react5.useRef)(createVaultEmbeddingCache());
18423
+ const chunkVectorCacheRef = (0, import_react5.useRef)(createChunkVectorCache());
18171
18424
  const clientToolEmbeddingsCacheRef = (0, import_react5.useRef)(/* @__PURE__ */ new Map());
18172
18425
  (0, import_react5.useEffect)(() => {
18173
18426
  if (!getToken) return;
@@ -18183,7 +18436,10 @@ function useChatStorage(options) {
18183
18436
  })();
18184
18437
  }, [vaultCtx, getToken, vaultEmbeddingOptions]);
18185
18438
  (0, import_react5.useEffect)(() => {
18186
- return onClearAllEncryptionState(() => vaultEmbeddingCacheRef.current.clear());
18439
+ return onClearAllEncryptionState(() => {
18440
+ vaultEmbeddingCacheRef.current.clear();
18441
+ chunkVectorCacheRef.current.clear();
18442
+ });
18187
18443
  }, []);
18188
18444
  const createMemoryVaultSearchTool2 = (0, import_react5.useCallback)(
18189
18445
  (searchOptions) => {
@@ -18211,6 +18467,7 @@ function useChatStorage(options) {
18211
18467
  storageCtx,
18212
18468
  embeddingOptions: vaultEmbeddingOptions,
18213
18469
  vaultCache: vaultEmbeddingCacheRef.current,
18470
+ chunkCache: chunkVectorCacheRef.current,
18214
18471
  // Graph lane fires when entityCtx is present and the query
18215
18472
  // contains extractable entities. Empty memory_entity (e.g.
18216
18473
  // before any auto-extraction linked entities) is a graceful
@@ -18258,6 +18515,7 @@ function useChatStorage(options) {
18258
18515
  // would embed the raw query (often containing the user's PII).
18259
18516
  embeddingOptions: vaultEmbeddingOptions,
18260
18517
  vaultCache: vaultEmbeddingCacheRef.current,
18518
+ chunkCache: chunkVectorCacheRef.current,
18261
18519
  // Graph lane fires only when entityCtx is present and the query
18262
18520
  // has extractable entities; empty memory_entity is a graceful
18263
18521
  // no-op (falls through to fact + chunk lanes). Mirrors createRecallTool.
@@ -29927,6 +30185,7 @@ function renderAnumaJsx(jsx3) {
29927
30185
  ConversationMemoryModel,
29928
30186
  DEFAULT_BACKUP_FOLDER,
29929
30187
  DEFAULT_CACHE_EXPIRATION_MS,
30188
+ DEFAULT_CHUNK_CACHE_SIZE,
29930
30189
  DEFAULT_CHUNK_OVERLAP,
29931
30190
  DEFAULT_CHUNK_SIZE,
29932
30191
  DEFAULT_DRIVE_CONVERSATIONS_FOLDER,
@@ -29958,6 +30217,7 @@ function renderAnumaJsx(jsx3) {
29958
30217
  R2_DEFAULT_TTL_MS,
29959
30218
  RECALL_MAX_LIMIT,
29960
30219
  RECALL_TOOL_NAME,
30220
+ RerankerUnavailableError,
29961
30221
  SDK_SCHEMA_VERSION,
29962
30222
  SERVER_TOOL_DEPENDENCY_SETS,
29963
30223
  SLIDER_CONFIG,
@@ -30016,6 +30276,7 @@ function renderAnumaJsx(jsx3) {
30016
30276
  conversationMemoryToStored,
30017
30277
  cosineInt8,
30018
30278
  createAutoExtractor,
30279
+ createChunkVectorCache,
30019
30280
  createCryptoPricePreProcessor,
30020
30281
  createGitHubTools,
30021
30282
  createMediaBatchOp,
@@ -30023,6 +30284,7 @@ function renderAnumaJsx(jsx3) {
30023
30284
  createMemoryEngineTool,
30024
30285
  createMemoryVaultSearchTool,
30025
30286
  createMemoryVaultTool,
30287
+ createPlatformCursorStore,
30026
30288
  createProjectOp,
30027
30289
  createRecallTool,
30028
30290
  createSavedToolOp,
@@ -30170,6 +30432,7 @@ function renderAnumaJsx(jsx3) {
30170
30432
  isNotionCallback,
30171
30433
  isOPFSSupported,
30172
30434
  isR2UrlExpired,
30435
+ isRerankerAvailable,
30173
30436
  isSupportedFile,
30174
30437
  isSupportedMediaType,
30175
30438
  linkMemoryEntitiesOp,