@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.
@@ -2543,7 +2543,13 @@ async function readJsonField(model, column, walletAddress) {
2543
2543
  }
2544
2544
  }
2545
2545
  async function searchChunksOp(ctx, queryVector, options) {
2546
- const { limit = 10, minSimilarity = 0.5, conversationId, embeddingModel } = options || {};
2546
+ const {
2547
+ limit = 10,
2548
+ minSimilarity = 0.5,
2549
+ conversationId,
2550
+ embeddingModel,
2551
+ chunkCache
2552
+ } = options || {};
2547
2553
  const activeConversations = await ctx.conversationsCollection.query(import_watermelondb.Q.where("is_deleted", false)).fetch();
2548
2554
  const activeConversationIds = new Set(
2549
2555
  // Use _getRaw for reliable raw column access
@@ -2563,23 +2569,54 @@ async function searchChunksOp(ctx, queryVector, options) {
2563
2569
  continue;
2564
2570
  }
2565
2571
  }
2572
+ const version = Number(message._getRaw("updated_at")) || 0;
2573
+ const cached = chunkCache?.get(message.id);
2574
+ if (cached && cached.version === version) {
2575
+ if (cached.chunks.length > 0) {
2576
+ for (let ci = 0; ci < cached.chunks.length; ci++) {
2577
+ const vec = cached.chunks[ci];
2578
+ if (vec.length === 0) continue;
2579
+ const similarity = cosineSimilarity(queryVector, vec);
2580
+ if (similarity >= minSimilarity) {
2581
+ candidates.push({
2582
+ message,
2583
+ similarity,
2584
+ chunkTextSource: { kind: "chunk-cached", chunkIndex: ci, version }
2585
+ });
2586
+ }
2587
+ }
2588
+ } else if (cached.fallback) {
2589
+ const similarity = cosineSimilarity(queryVector, cached.fallback);
2590
+ if (similarity >= minSimilarity) {
2591
+ candidates.push({ message, similarity, chunkTextSource: { kind: "message" } });
2592
+ }
2593
+ }
2594
+ continue;
2595
+ }
2566
2596
  const chunks = await readJsonField(message, "chunks", ctx.walletAddress);
2567
2597
  if (chunks && chunks.length > 0) {
2568
- for (const chunk of chunks) {
2569
- if (!chunk.vector || chunk.vector.length === 0) continue;
2570
- const similarity = cosineSimilarity(queryVector, chunk.vector);
2598
+ const vectors = chunks.map(
2599
+ (chunk) => chunk.vector && chunk.vector.length > 0 ? Float32Array.from(chunk.vector) : new Float32Array(0)
2600
+ );
2601
+ chunkCache?.set(message.id, { version, chunks: vectors });
2602
+ for (let ci = 0; ci < chunks.length; ci++) {
2603
+ const vec = vectors[ci];
2604
+ if (vec.length === 0) continue;
2605
+ const similarity = cosineSimilarity(queryVector, vec);
2571
2606
  if (similarity >= minSimilarity) {
2572
2607
  candidates.push({
2573
2608
  message,
2574
2609
  similarity,
2575
- chunkTextSource: { kind: "chunk", text: chunk.text }
2610
+ chunkTextSource: { kind: "chunk", text: chunks[ci].text }
2576
2611
  });
2577
2612
  }
2578
2613
  }
2579
2614
  } else {
2580
2615
  const messageVector = await readJsonField(message, "vector", ctx.walletAddress);
2581
2616
  if (!messageVector || messageVector.length === 0) continue;
2582
- const similarity = cosineSimilarity(queryVector, messageVector);
2617
+ const fallback = Float32Array.from(messageVector);
2618
+ chunkCache?.set(message.id, { version, chunks: [], fallback });
2619
+ const similarity = cosineSimilarity(queryVector, fallback);
2583
2620
  if (similarity >= minSimilarity) {
2584
2621
  candidates.push({ message, similarity, chunkTextSource: { kind: "message" } });
2585
2622
  }
@@ -2606,9 +2643,31 @@ async function searchChunksOp(ctx, queryVector, options) {
2606
2643
  })
2607
2644
  )
2608
2645
  );
2646
+ const chunkStateByMsgId = /* @__PURE__ */ new Map();
2647
+ const needsChunkText = /* @__PURE__ */ new Set();
2648
+ for (const c of topK) {
2649
+ if (c.chunkTextSource.kind === "chunk-cached") needsChunkText.add(c.message.id);
2650
+ }
2651
+ await Promise.all(
2652
+ Array.from(needsChunkText, async (id) => {
2653
+ const m = uniqueMessages.get(id);
2654
+ const parsed = await readJsonField(m, "chunks", ctx.walletAddress);
2655
+ const version = Number(m._getRaw("updated_at")) || 0;
2656
+ if (parsed) chunkStateByMsgId.set(id, { version, chunks: parsed });
2657
+ })
2658
+ );
2609
2659
  return topK.map(({ message, similarity, chunkTextSource }) => {
2610
2660
  const stored = { ...storedById.get(message.id) };
2611
- const chunkText2 = chunkTextSource.kind === "chunk" ? chunkTextSource.text : stored.content;
2661
+ let chunkText2;
2662
+ if (chunkTextSource.kind === "chunk") {
2663
+ chunkText2 = chunkTextSource.text;
2664
+ } else if (chunkTextSource.kind === "chunk-cached") {
2665
+ const state = chunkStateByMsgId.get(message.id);
2666
+ const chunk = state && state.version === chunkTextSource.version ? state.chunks[chunkTextSource.chunkIndex] : void 0;
2667
+ chunkText2 = chunk?.text ?? stored.content;
2668
+ } else {
2669
+ chunkText2 = stored.content;
2670
+ }
2612
2671
  return { chunkText: chunkText2, message: stored, similarity };
2613
2672
  });
2614
2673
  }
@@ -4840,9 +4899,9 @@ function recencyMultiplier(updatedAt, opts = {}) {
4840
4899
  const updatedMs = updatedAt.getTime();
4841
4900
  if (!Number.isFinite(updatedMs)) return noDate;
4842
4901
  const now = opts.now ?? /* @__PURE__ */ new Date();
4843
- const nowMs = now.getTime();
4844
- if (!Number.isFinite(nowMs)) return noDate;
4845
- const ageDays = Math.max(0, (nowMs - updatedMs) / DAY_MS);
4902
+ const nowMs2 = now.getTime();
4903
+ if (!Number.isFinite(nowMs2)) return noDate;
4904
+ const ageDays = Math.max(0, (nowMs2 - updatedMs) / DAY_MS);
4846
4905
  const perYearDecay = opts.perYearDecay ?? DEFAULT_PER_YEAR_DECAY;
4847
4906
  const floor = opts.floor ?? DEFAULT_FLOOR;
4848
4907
  const decay = perYearDecay * (ageDays / 365);
@@ -4860,14 +4919,42 @@ var init_recency = __esm({
4860
4919
  });
4861
4920
 
4862
4921
  // src/lib/memory/reranker.ts
4922
+ function isRerankerAvailable() {
4923
+ return available;
4924
+ }
4925
+ function isModuleMissing(err) {
4926
+ let e = err;
4927
+ for (let depth = 0; depth < 5 && e !== null && e !== void 0; depth++) {
4928
+ const code = e.code;
4929
+ if (code === "ERR_MODULE_NOT_FOUND" || code === "MODULE_NOT_FOUND") return true;
4930
+ if (e instanceof Error && /cannot find (module|package)|failed to resolve|unable to resolve module|requiring unknown module/i.test(
4931
+ e.message
4932
+ )) {
4933
+ return true;
4934
+ }
4935
+ e = e.cause;
4936
+ }
4937
+ return false;
4938
+ }
4863
4939
  async function getModel() {
4940
+ if (available === false) throw new RerankerUnavailableError(void 0);
4864
4941
  if (!modelPromise) {
4865
4942
  const load = (async () => {
4866
- const transformers = await import("@huggingface/transformers");
4943
+ let transformers;
4944
+ try {
4945
+ transformers = await import("@huggingface/transformers");
4946
+ } catch (err) {
4947
+ if (isModuleMissing(err)) {
4948
+ available = false;
4949
+ throw new RerankerUnavailableError(err);
4950
+ }
4951
+ throw err;
4952
+ }
4867
4953
  const [tokenizer, model] = await Promise.all([
4868
4954
  transformers.AutoTokenizer.from_pretrained(MODEL_ID),
4869
4955
  transformers.AutoModelForSequenceClassification.from_pretrained(MODEL_ID)
4870
4956
  ]);
4957
+ available = true;
4871
4958
  return { tokenizer, model };
4872
4959
  })();
4873
4960
  modelPromise = load;
@@ -4909,11 +4996,19 @@ async function rerankPairs(query, items) {
4909
4996
  }
4910
4997
  return items.map((item, i) => ({ id: item.id, content: item.content, score: scores[i] })).sort((a, b) => b.score - a.score);
4911
4998
  }
4912
- var MODEL_ID, modelPromise;
4999
+ var MODEL_ID, RerankerUnavailableError, available, modelPromise;
4913
5000
  var init_reranker = __esm({
4914
5001
  "src/lib/memory/reranker.ts"() {
4915
5002
  "use strict";
4916
5003
  MODEL_ID = "Xenova/ms-marco-MiniLM-L-6-v2";
5004
+ RerankerUnavailableError = class extends Error {
5005
+ constructor(reason) {
5006
+ super("cross-encoder reranker unavailable (@huggingface/transformers not installed)");
5007
+ this.name = "RerankerUnavailableError";
5008
+ this.reason = reason;
5009
+ }
5010
+ };
5011
+ available = void 0;
4917
5012
  modelPromise = null;
4918
5013
  }
4919
5014
  });
@@ -5582,22 +5677,67 @@ async function recall(query, ctx, options = {}) {
5582
5677
  const flags = flagsForBudget(requestedBudget);
5583
5678
  const decomposeAvailable = flags.decompose && !!options.decomposeOptions;
5584
5679
  const usedBudget = requestedBudget === "high" && !decomposeAvailable ? "mid" : requestedBudget;
5680
+ const t0 = nowMs();
5681
+ let prepMs = 0;
5682
+ let factLaneMs = 0;
5683
+ let chunkLaneMs = 0;
5684
+ let fuseMs = 0;
5685
+ const factResults = [];
5686
+ const chunkResults = [];
5687
+ let vaultSize;
5688
+ let didRerank = false;
5689
+ let hadV2Head = false;
5690
+ const emitDiagnostics = (candidateCount) => {
5691
+ const cb = options.onDiagnostics;
5692
+ if (!cb) return;
5693
+ const degraded = [];
5694
+ if (flags.rerank && factResults.length > 0 && hadV2Head && !didRerank) {
5695
+ degraded.push("rerank-unavailable");
5696
+ }
5697
+ if (flags.decompose && !decomposeAvailable) degraded.push("decompose-unavailable");
5698
+ const diagnostics = {
5699
+ usedBudget,
5700
+ reranked: didRerank,
5701
+ candidateCount,
5702
+ ...vaultSize !== void 0 && { vaultSize },
5703
+ factCount: factResults.length,
5704
+ chunkCount: chunkResults.length,
5705
+ timings: {
5706
+ total: nowMs() - t0,
5707
+ prep: prepMs,
5708
+ factLane: factLaneMs,
5709
+ chunkLane: chunkLaneMs,
5710
+ fuse: fuseMs
5711
+ },
5712
+ degraded
5713
+ };
5714
+ try {
5715
+ cb(diagnostics);
5716
+ } catch {
5717
+ }
5718
+ };
5585
5719
  if (!query || typeof query !== "string" || query.trim().length === 0) {
5720
+ emitDiagnostics(0);
5586
5721
  return { memories: [], usedBudget, reranked: false, candidateCount: 0 };
5587
5722
  }
5588
5723
  const needsChunkEmbedding = types.includes("chunk") && ctx.storageCtx;
5589
5724
  const wantsTemporal = types.includes("fact") && ctx.vaultCtx;
5725
+ const prepStart = nowMs();
5590
5726
  const [queryEmbedding, entityRanking, temporalRanking] = await Promise.all([
5591
5727
  needsChunkEmbedding ? generateEmbedding(query, ctx.embeddingOptions) : Promise.resolve(void 0),
5592
5728
  buildGraphLaneRanking(query, ctx),
5593
5729
  wantsTemporal ? buildTemporalLaneRanking(query, ctx.vaultCtx, options.now) : Promise.resolve([])
5594
5730
  ]);
5595
- const factResults = [];
5596
- const chunkResults = [];
5597
- let vaultSize;
5731
+ prepMs = nowMs() - prepStart;
5598
5732
  if (types.includes("fact") && ctx.vaultCtx && ctx.vaultCache) {
5733
+ const factStart = nowMs();
5599
5734
  const vaultMinScore = options.minScore ?? DEFAULT_FACT_MIN_SCORE;
5600
- const { results, vaultSize: size } = await searchVaultMemoriesWithSize(
5735
+ const {
5736
+ results,
5737
+ vaultSize: size,
5738
+ reranked,
5739
+ hadV2Head: v2Head
5740
+ } = await searchVaultMemoriesWithSize(
5601
5741
  query,
5602
5742
  ctx.vaultCtx,
5603
5743
  ctx.embeddingOptions,
@@ -5648,14 +5788,19 @@ async function recall(query, ctx, options = {}) {
5648
5788
  )
5649
5789
  );
5650
5790
  vaultSize = size;
5791
+ didRerank = reranked;
5792
+ hadV2Head = v2Head;
5793
+ factLaneMs = nowMs() - factStart;
5651
5794
  }
5652
5795
  if (types.includes("chunk") && ctx.storageCtx && queryEmbedding) {
5796
+ const chunkStart = nowMs();
5653
5797
  const chunkMinScore = options.minScore ?? DEFAULT_CHUNK_MIN_SCORE;
5654
5798
  const results = await searchChunksOp(ctx.storageCtx, queryEmbedding, {
5655
5799
  limit: types.includes("fact") ? Math.max(limit * 2, 16) : limit,
5656
5800
  minSimilarity: chunkMinScore,
5657
5801
  embeddingModel: ctx.embeddingOptions.model ?? DEFAULT_API_EMBEDDING_MODEL,
5658
- ...options.conversationId && { conversationId: options.conversationId }
5802
+ ...options.conversationId && { conversationId: options.conversationId },
5803
+ ...ctx.chunkCache && { chunkCache: ctx.chunkCache }
5659
5804
  });
5660
5805
  chunkResults.push(
5661
5806
  ...dedupeBy(
@@ -5665,18 +5810,23 @@ async function recall(query, ctx, options = {}) {
5665
5810
  (r) => r.chunkText.trim()
5666
5811
  )
5667
5812
  );
5813
+ chunkLaneMs = nowMs() - chunkStart;
5668
5814
  }
5815
+ const fuseStart = nowMs();
5669
5816
  if (types.length === 1 || factResults.length === 0 || chunkResults.length === 0) {
5670
5817
  const memories2 = [
5671
5818
  ...factResults.map(toFactMemory),
5672
5819
  ...chunkResults.map(toChunkMemory)
5673
5820
  ];
5674
5821
  memories2.sort((a, b) => b.score - a.score);
5822
+ const candidateCount = factResults.length + chunkResults.length;
5823
+ fuseMs = nowMs() - fuseStart;
5824
+ emitDiagnostics(candidateCount);
5675
5825
  return {
5676
5826
  memories: memories2.slice(0, limit),
5677
5827
  usedBudget,
5678
- reranked: flags.rerank,
5679
- candidateCount: factResults.length + chunkResults.length,
5828
+ reranked: didRerank,
5829
+ candidateCount,
5680
5830
  ...vaultSize !== void 0 && { vaultSize }
5681
5831
  };
5682
5832
  }
@@ -5725,10 +5875,12 @@ async function recall(query, ctx, options = {}) {
5725
5875
  suppressed = next;
5726
5876
  memories = selectWith(suppressed);
5727
5877
  }
5878
+ fuseMs = nowMs() - fuseStart;
5879
+ emitDiagnostics(byId.size);
5728
5880
  return {
5729
5881
  memories,
5730
5882
  usedBudget,
5731
- reranked: flags.rerank,
5883
+ reranked: didRerank,
5732
5884
  candidateCount: byId.size,
5733
5885
  ...vaultSize !== void 0 && { vaultSize }
5734
5886
  };
@@ -5792,7 +5944,7 @@ function toChunkMemory(r) {
5792
5944
  updatedAt: r.message.createdAt
5793
5945
  };
5794
5946
  }
5795
- var DEFAULT_LIMIT, DEFAULT_BUDGET, DEFAULT_FACT_MIN_SCORE, DEFAULT_CHUNK_MIN_SCORE;
5947
+ var DEFAULT_LIMIT, DEFAULT_BUDGET, DEFAULT_FACT_MIN_SCORE, nowMs, DEFAULT_CHUNK_MIN_SCORE;
5796
5948
  var init_recall = __esm({
5797
5949
  "src/lib/memory/recall.ts"() {
5798
5950
  "use strict";
@@ -5808,6 +5960,7 @@ var init_recall = __esm({
5808
5960
  DEFAULT_LIMIT = 8;
5809
5961
  DEFAULT_BUDGET = "low";
5810
5962
  DEFAULT_FACT_MIN_SCORE = 0.1;
5963
+ nowMs = () => typeof performance !== "undefined" && typeof performance.now === "function" ? performance.now() : Date.now();
5811
5964
  DEFAULT_CHUNK_MIN_SCORE = 0.5;
5812
5965
  }
5813
5966
  });
@@ -6007,6 +6160,9 @@ async function rankFusedVaultMemoriesAsync(query, queryEmbedding, items, options
6007
6160
  bm25AdmissionDivisor: options?.bm25AdmissionDivisor,
6008
6161
  rrfK: options?.rrfK
6009
6162
  });
6163
+ if (options?.v2HeadStats) {
6164
+ options.v2HeadStats.hadResults = v2Ranked.length > 0;
6165
+ }
6010
6166
  let combined;
6011
6167
  let tailSlice = [];
6012
6168
  if (options?.rerank) {
@@ -6027,8 +6183,13 @@ async function rankFusedVaultMemoriesAsync(query, queryEmbedding, items, options
6027
6183
  return { ...r, similarity: v2 * (1 + ceWeight * ce) };
6028
6184
  });
6029
6185
  combined.sort((a, b) => b.similarity - a.similarity);
6186
+ if (headSlice.length > 0 && options.rerankStats) options.rerankStats.applied = true;
6030
6187
  } catch (err) {
6031
- getLogger().warn("[memory/search] cross-encoder rerank failed; using V2 ranking", err);
6188
+ if (err instanceof RerankerUnavailableError) {
6189
+ getLogger().debug("[memory/search] cross-encoder unavailable; using V2 ranking");
6190
+ } else {
6191
+ getLogger().warn("[memory/search] cross-encoder rerank failed; using V2 ranking", err);
6192
+ }
6032
6193
  combined = v2Ranked;
6033
6194
  }
6034
6195
  } else {
@@ -6162,6 +6323,9 @@ async function rankComposite(originalQuery, originalQueryEmbedding, subQueries,
6162
6323
  (r) => r !== null
6163
6324
  );
6164
6325
  combined.sort((a, b) => b.similarity - a.similarity);
6326
+ if (options?.v2HeadStats) {
6327
+ options.v2HeadStats.hadResults = combined.length > 0;
6328
+ }
6165
6329
  if (options?.includeUnrankedTail) {
6166
6330
  const fusedIds = new Set(combined.map((r) => r.uniqueId));
6167
6331
  for (const item of items) {
@@ -6197,11 +6361,16 @@ async function rankComposite(originalQuery, originalQueryEmbedding, subQueries,
6197
6361
  }));
6198
6362
  head.sort((a, b) => b.similarity - a.similarity);
6199
6363
  combined = [...head, ...tailSlice];
6364
+ if (headSlice.length > 0 && options.rerankStats) options.rerankStats.applied = true;
6200
6365
  } catch (err) {
6201
- getLogger().warn(
6202
- "[memory/search] composite cross-encoder rerank failed; using fused ranking",
6203
- err
6204
- );
6366
+ if (err instanceof RerankerUnavailableError) {
6367
+ getLogger().debug("[memory/search] cross-encoder unavailable; using fused ranking");
6368
+ } else {
6369
+ getLogger().warn(
6370
+ "[memory/search] composite cross-encoder rerank failed; using fused ranking",
6371
+ err
6372
+ );
6373
+ }
6205
6374
  }
6206
6375
  }
6207
6376
  if (options?.mmr) {
@@ -6293,7 +6462,7 @@ async function searchVaultMemoriesWithSize(query, vaultCtx, embeddingOptions, ca
6293
6462
  const minSimilarity = searchOptions?.minSimilarity ?? 0.1;
6294
6463
  const scopes = searchOptions?.scopes;
6295
6464
  if (!query || typeof query !== "string") {
6296
- return { results: [], vaultSize: 0 };
6465
+ return { results: [], vaultSize: 0, reranked: false, hadV2Head: false };
6297
6466
  }
6298
6467
  const folderId = searchOptions?.folderId;
6299
6468
  const queryOpts = {};
@@ -6310,7 +6479,7 @@ async function searchVaultMemoriesWithSize(query, vaultCtx, embeddingOptions, ca
6310
6479
  );
6311
6480
  }
6312
6481
  if (memories.length === 0) {
6313
- return { results: [], vaultSize: loaded.length };
6482
+ return { results: [], vaultSize: loaded.length, reranked: false, hadV2Head: false };
6314
6483
  }
6315
6484
  const queryEmbedding = await generateEmbedding(query, embeddingOptions);
6316
6485
  const currentModel = embeddingOptions.model ?? DEFAULT_API_EMBEDDING_MODEL;
@@ -6386,12 +6555,18 @@ async function searchVaultMemoriesWithSize(query, vaultCtx, embeddingOptions, ca
6386
6555
  memories.map((m) => [m.uniqueId, { createdAt: m.createdAt, updatedAt: m.updatedAt }])
6387
6556
  );
6388
6557
  const stampTimestamps = (out) => {
6389
- out.results = out.results.map((r) => {
6558
+ const results2 = out.results.map((r) => {
6390
6559
  const ts = timestampById.get(r.uniqueId);
6391
6560
  return ts ? { ...r, createdAt: ts.createdAt, updatedAt: ts.updatedAt } : r;
6392
6561
  });
6393
- return out;
6562
+ return {
6563
+ results: results2,
6564
+ vaultSize: out.vaultSize,
6565
+ reranked: out.reranked ?? false,
6566
+ hadV2Head: out.hadV2Head ?? false
6567
+ };
6394
6568
  };
6569
+ const rerankStats = { applied: false };
6395
6570
  const useFusion = searchOptions?.useFusion ?? true;
6396
6571
  const tuning = {
6397
6572
  ...searchOptions?.recencyAlpha !== void 0 && { recencyAlpha: searchOptions.recencyAlpha },
@@ -6418,6 +6593,7 @@ async function searchVaultMemoriesWithSize(query, vaultCtx, embeddingOptions, ca
6418
6593
  query: sq,
6419
6594
  embedding: subEmbeddings[i]
6420
6595
  }));
6596
+ const v2HeadStats = { hadResults: false };
6421
6597
  const composite = await rankComposite(query, queryEmbedding, subQueries, embeddedItems, {
6422
6598
  limit,
6423
6599
  minSimilarity,
@@ -6429,12 +6605,20 @@ async function searchVaultMemoriesWithSize(query, vaultCtx, embeddingOptions, ca
6429
6605
  ...searchOptions.mmr !== void 0 && { mmr: searchOptions.mmr },
6430
6606
  ...tuning,
6431
6607
  ...searchOptions.entityRanking && { entityRanking: searchOptions.entityRanking },
6432
- ...searchOptions.temporalRanking && { temporalRanking: searchOptions.temporalRanking }
6608
+ ...searchOptions.temporalRanking && { temporalRanking: searchOptions.temporalRanking },
6609
+ rerankStats,
6610
+ v2HeadStats
6611
+ });
6612
+ return stampTimestamps({
6613
+ results: composite,
6614
+ vaultSize: loaded.length,
6615
+ reranked: rerankStats.applied,
6616
+ hadV2Head: v2HeadStats.hadResults
6433
6617
  });
6434
- return stampTimestamps({ results: composite, vaultSize: loaded.length });
6435
6618
  }
6436
6619
  }
6437
6620
  if (useFusion && searchOptions?.rerank) {
6621
+ const v2HeadStats = { hadResults: false };
6438
6622
  const results2 = await rankFusedVaultMemoriesAsync(query, queryEmbedding, embeddedItems, {
6439
6623
  limit,
6440
6624
  minSimilarity,
@@ -6446,9 +6630,16 @@ async function searchVaultMemoriesWithSize(query, vaultCtx, embeddingOptions, ca
6446
6630
  ...searchOptions.mmr !== void 0 && { mmr: searchOptions.mmr },
6447
6631
  ...tuning,
6448
6632
  ...searchOptions.entityRanking && { entityRanking: searchOptions.entityRanking },
6449
- ...searchOptions.temporalRanking && { temporalRanking: searchOptions.temporalRanking }
6633
+ ...searchOptions.temporalRanking && { temporalRanking: searchOptions.temporalRanking },
6634
+ rerankStats,
6635
+ v2HeadStats
6636
+ });
6637
+ return stampTimestamps({
6638
+ results: results2,
6639
+ vaultSize: loaded.length,
6640
+ reranked: rerankStats.applied,
6641
+ hadV2Head: v2HeadStats.hadResults
6450
6642
  });
6451
- return stampTimestamps({ results: results2, vaultSize: loaded.length });
6452
6643
  }
6453
6644
  if (useFusion) {
6454
6645
  const results2 = rankFusedVaultMemories(query, queryEmbedding, embeddedItems, {
@@ -6458,7 +6649,7 @@ async function searchVaultMemoriesWithSize(query, vaultCtx, embeddingOptions, ca
6458
6649
  ...searchOptions?.entityRanking && { entityRanking: searchOptions.entityRanking },
6459
6650
  ...searchOptions?.temporalRanking && { temporalRanking: searchOptions.temporalRanking }
6460
6651
  });
6461
- return stampTimestamps({ results: results2, vaultSize: loaded.length });
6652
+ return stampTimestamps({ results: results2, vaultSize: loaded.length, hadV2Head: results2.length > 0 });
6462
6653
  }
6463
6654
  const results = rankVaultMemories(query, queryEmbedding, embeddedItems, {
6464
6655
  limit,
@@ -6470,7 +6661,7 @@ async function searchVaultMemoriesWithSize(query, vaultCtx, embeddingOptions, ca
6470
6661
  supersessionWindow: searchOptions.supersessionWindow
6471
6662
  }
6472
6663
  });
6473
- return stampTimestamps({ results, vaultSize: loaded.length });
6664
+ return stampTimestamps({ results, vaultSize: loaded.length, hadV2Head: results.length > 0 });
6474
6665
  }
6475
6666
  async function searchVaultMemories(query, vaultCtx, embeddingOptions, cache, searchOptions) {
6476
6667
  const { results } = await searchVaultMemoriesWithSize(
@@ -6767,6 +6958,7 @@ __export(index_exports, {
6767
6958
  ChatMessage: () => Message,
6768
6959
  ConversationMemoryModel: () => ConversationMemory,
6769
6960
  DEFAULT_CACHE_EXPIRATION_MS: () => DEFAULT_CACHE_EXPIRATION_MS,
6961
+ DEFAULT_CHUNK_CACHE_SIZE: () => DEFAULT_CHUNK_CACHE_SIZE,
6770
6962
  DEFAULT_CHUNK_OVERLAP: () => DEFAULT_CHUNK_OVERLAP,
6771
6963
  DEFAULT_CHUNK_SIZE: () => DEFAULT_CHUNK_SIZE,
6772
6964
  DEFAULT_MIN_CHUNK_SIZE: () => DEFAULT_MIN_CHUNK_SIZE,
@@ -6780,6 +6972,7 @@ __export(index_exports, {
6780
6972
  QueueManager: () => QueueManager,
6781
6973
  RECALL_MAX_LIMIT: () => RECALL_MAX_LIMIT,
6782
6974
  RECALL_TOOL_NAME: () => RECALL_TOOL_NAME,
6975
+ RerankerUnavailableError: () => RerankerUnavailableError,
6783
6976
  SDK_SCHEMA_VERSION: () => SDK_SCHEMA_VERSION,
6784
6977
  STREAM_RESUMABLE_HEADER: () => STREAM_RESUMABLE_HEADER,
6785
6978
  StoredVaultFolderModel: () => VaultFolder,
@@ -6812,11 +7005,13 @@ __export(index_exports, {
6812
7005
  conversationMemoryToStored: () => conversationMemoryToStored,
6813
7006
  cosineInt8: () => cosineInt8,
6814
7007
  createAutoExtractor: () => createAutoExtractor,
7008
+ createChunkVectorCache: () => createChunkVectorCache,
6815
7009
  createCryptoPricePreProcessor: () => createCryptoPricePreProcessor,
6816
7010
  createMemoryEngineTool: () => createMemoryEngineTool,
6817
7011
  createMemoryVaultSearchTool: () => createMemoryVaultSearchTool,
6818
7012
  createMemoryVaultTool: () => createMemoryVaultTool,
6819
7013
  createNotionTools: () => createNotionTools,
7014
+ createPlatformCursorStore: () => createPlatformCursorStore,
6820
7015
  createRecallTool: () => createRecallTool,
6821
7016
  createStockPricePreProcessor: () => createStockPricePreProcessor,
6822
7017
  createStreamingDeAnonymizer: () => createStreamingDeAnonymizer,
@@ -6860,6 +7055,7 @@ __export(index_exports, {
6860
7055
  getVaultMemoryOp: () => getVaultMemoryOp,
6861
7056
  hasEncryptionKey: () => hasEncryptionKey,
6862
7057
  isPiiRedactor: () => isPiiRedactor,
7058
+ isRerankerAvailable: () => isRerankerAvailable,
6863
7059
  moveMemoriesToFolderOp: () => moveMemoriesToFolderOp,
6864
7060
  noopLogger: () => noopLogger,
6865
7061
  onKeyAvailable: () => onKeyAvailable,
@@ -13245,6 +13441,23 @@ function parseLocalCalendarDay2(year, month, day) {
13245
13441
  // src/lib/memory/autoExtractWorker.ts
13246
13442
  init_logger();
13247
13443
  init_redactor();
13444
+ function createPlatformCursorStore(storage, keyPrefix = "anuma:mem:xcursor:") {
13445
+ return {
13446
+ get(conversationId) {
13447
+ try {
13448
+ return storage.getItem(keyPrefix + conversationId) ?? void 0;
13449
+ } catch {
13450
+ return void 0;
13451
+ }
13452
+ },
13453
+ set(conversationId, messageId) {
13454
+ try {
13455
+ storage.setItem(keyPrefix + conversationId, messageId);
13456
+ } catch {
13457
+ }
13458
+ }
13459
+ };
13460
+ }
13248
13461
  var DEFAULT_WINDOW_SIZE = 6;
13249
13462
  var DEFAULT_MAX_WINDOW_SIZE = 20;
13250
13463
  var CONTEXT_OVERLAP = 2;
@@ -13264,12 +13477,24 @@ function createAutoExtractor(options) {
13264
13477
  let inflight = 0;
13265
13478
  let disposed = false;
13266
13479
  const conversations = /* @__PURE__ */ new Map();
13480
+ const cursorStore = options.cursorStore;
13481
+ const hydrate = (state, conversationId) => {
13482
+ if (state.hydrated) return;
13483
+ state.hydrated = true;
13484
+ if (!cursorStore || conversationId === void 0 || state.watermark !== void 0) return;
13485
+ try {
13486
+ const persisted = cursorStore.get(conversationId);
13487
+ if (persisted) state.watermark = persisted;
13488
+ } catch {
13489
+ }
13490
+ };
13267
13491
  const stateFor = (conversationId) => {
13268
13492
  let state = conversations.get(conversationId);
13269
13493
  if (!state) {
13270
13494
  if (conversations.size >= maxTrackedConversations) evictOldestIdle(conversationId);
13271
13495
  state = {};
13272
13496
  conversations.set(conversationId, state);
13497
+ hydrate(state, conversationId);
13273
13498
  }
13274
13499
  return state;
13275
13500
  };
@@ -13282,10 +13507,12 @@ function createAutoExtractor(options) {
13282
13507
  }
13283
13508
  };
13284
13509
  function computeWindow(messages, conversationId) {
13285
- const lastId = conversations.get(conversationId)?.watermark;
13510
+ const lastId = stateFor(conversationId).watermark;
13286
13511
  const idx = lastId === void 0 ? -1 : messages.findIndex((m) => m.id === lastId);
13287
- if (idx === -1) return messages.slice(-windowSize);
13288
- if (idx >= messages.length - 1) return [];
13512
+ if (idx === -1) {
13513
+ return { window: messages.slice(-windowSize), contiguous: messages.length <= windowSize };
13514
+ }
13515
+ if (idx >= messages.length - 1) return { window: [], contiguous: true };
13289
13516
  const start = Math.max(0, idx + 1 - CONTEXT_OVERLAP);
13290
13517
  let window2 = messages.slice(start);
13291
13518
  if (window2.length > maxWindowSize) {
@@ -13294,8 +13521,21 @@ function createAutoExtractor(options) {
13294
13521
  );
13295
13522
  window2 = window2.slice(0, maxWindowSize);
13296
13523
  }
13297
- return window2;
13524
+ return { window: window2, contiguous: true };
13298
13525
  }
13526
+ const persistCursor = (conversationId, messages, advancedToId, contiguous) => {
13527
+ if (!cursorStore || conversationId === void 0 || !contiguous) return;
13528
+ try {
13529
+ const stored = cursorStore.get(conversationId);
13530
+ if (stored && stored !== advancedToId) {
13531
+ const storedIdx = messages.findIndex((m) => m.id === stored);
13532
+ const newIdx = messages.findIndex((m) => m.id === advancedToId);
13533
+ if (storedIdx !== -1 && newIdx !== -1 && storedIdx >= newIdx) return;
13534
+ }
13535
+ cursorStore.set(conversationId, advancedToId);
13536
+ } catch {
13537
+ }
13538
+ };
13299
13539
  const consolidateBaseUrl = options.consolidate?.baseUrl ?? options.extract.baseUrl;
13300
13540
  const consolidateOptions = options.consolidate ? {
13301
13541
  ...options.extract.apiKey !== void 0 && { apiKey: options.extract.apiKey },
@@ -13341,7 +13581,7 @@ function createAutoExtractor(options) {
13341
13581
  return dispatch(messages, conversationId);
13342
13582
  }
13343
13583
  function dispatch(messages, conversationId) {
13344
- const window2 = computeWindow(messages, conversationId);
13584
+ const { window: window2, contiguous } = computeWindow(messages, conversationId);
13345
13585
  if (window2.length === 0) {
13346
13586
  options.onSkipped?.({ reason: "no-new-content", conversationId });
13347
13587
  drainPending();
@@ -13366,7 +13606,9 @@ function createAutoExtractor(options) {
13366
13606
  }
13367
13607
  }
13368
13608
  );
13369
- stateFor(conversationId).watermark = window2[window2.length - 1].id;
13609
+ const advancedTo = window2[window2.length - 1].id;
13610
+ stateFor(conversationId).watermark = advancedTo;
13611
+ persistCursor(conversationId, messages, advancedTo, contiguous);
13370
13612
  for (let i = 0; i < results.length; i++) {
13371
13613
  if (results[i].action === "suppressed") continue;
13372
13614
  options.onMemoryExtracted?.({
@@ -13413,6 +13655,13 @@ function createAutoExtractor(options) {
13413
13655
  };
13414
13656
  }
13415
13657
 
13658
+ // src/lib/memory/chunkVectorCache.ts
13659
+ init_lruCache();
13660
+ var DEFAULT_CHUNK_CACHE_SIZE = 5e3;
13661
+ function createChunkVectorCache(maxSize = DEFAULT_CHUNK_CACHE_SIZE) {
13662
+ return new LRUCache(maxSize);
13663
+ }
13664
+
13416
13665
  // src/lib/memory/index.ts
13417
13666
  init_recall();
13418
13667
 
@@ -13677,6 +13926,9 @@ function parseAnswer(body, base, parseSchema) {
13677
13926
  };
13678
13927
  }
13679
13928
 
13929
+ // src/lib/memory/index.ts
13930
+ init_reranker();
13931
+
13680
13932
  // src/lib/memory/topicExtract.ts
13681
13933
  init_operations2();
13682
13934
  init_operations3();
@@ -15058,6 +15310,13 @@ function useChatStorage(options) {
15058
15310
  [vaultCtx]
15059
15311
  );
15060
15312
  const vaultEmbeddingCacheRef = (0, import_react4.useRef)(createVaultEmbeddingCache());
15313
+ const chunkVectorCacheRef = (0, import_react4.useRef)(createChunkVectorCache());
15314
+ (0, import_react4.useEffect)(() => {
15315
+ return onClearAllEncryptionState(() => {
15316
+ vaultEmbeddingCacheRef.current.clear();
15317
+ chunkVectorCacheRef.current.clear();
15318
+ });
15319
+ }, []);
15061
15320
  const createRecallTool2 = (0, import_react4.useCallback)(
15062
15321
  (toolOptions, callbacks) => {
15063
15322
  if (!getToken) {
@@ -15074,7 +15333,8 @@ function useChatStorage(options) {
15074
15333
  model: embeddingModel,
15075
15334
  maskInput: maskEmbeddingInput
15076
15335
  },
15077
- vaultCache: vaultEmbeddingCacheRef.current
15336
+ vaultCache: vaultEmbeddingCacheRef.current,
15337
+ chunkCache: chunkVectorCacheRef.current
15078
15338
  // entityCtx is intentionally omitted on Expo for now — the
15079
15339
  // W5 graph lane is a no-op without it (recall falls through
15080
15340
  // to fact + chunk lanes). Wire it up when the Expo client
@@ -15116,7 +15376,8 @@ function useChatStorage(options) {
15116
15376
  model: embeddingModel,
15117
15377
  maskInput: maskEmbeddingInput
15118
15378
  },
15119
- vaultCache: vaultEmbeddingCacheRef.current
15379
+ vaultCache: vaultEmbeddingCacheRef.current,
15380
+ chunkCache: chunkVectorCacheRef.current
15120
15381
  },
15121
15382
  resolvedOptions
15122
15383
  );
@@ -18443,6 +18704,7 @@ function createNotionTools(getAccessToken, requestNotionAccess) {
18443
18704
  ChatMessage,
18444
18705
  ConversationMemoryModel,
18445
18706
  DEFAULT_CACHE_EXPIRATION_MS,
18707
+ DEFAULT_CHUNK_CACHE_SIZE,
18446
18708
  DEFAULT_CHUNK_OVERLAP,
18447
18709
  DEFAULT_CHUNK_SIZE,
18448
18710
  DEFAULT_MIN_CHUNK_SIZE,
@@ -18456,6 +18718,7 @@ function createNotionTools(getAccessToken, requestNotionAccess) {
18456
18718
  QueueManager,
18457
18719
  RECALL_MAX_LIMIT,
18458
18720
  RECALL_TOOL_NAME,
18721
+ RerankerUnavailableError,
18459
18722
  SDK_SCHEMA_VERSION,
18460
18723
  STREAM_RESUMABLE_HEADER,
18461
18724
  StoredVaultFolderModel,
@@ -18488,11 +18751,13 @@ function createNotionTools(getAccessToken, requestNotionAccess) {
18488
18751
  conversationMemoryToStored,
18489
18752
  cosineInt8,
18490
18753
  createAutoExtractor,
18754
+ createChunkVectorCache,
18491
18755
  createCryptoPricePreProcessor,
18492
18756
  createMemoryEngineTool,
18493
18757
  createMemoryVaultSearchTool,
18494
18758
  createMemoryVaultTool,
18495
18759
  createNotionTools,
18760
+ createPlatformCursorStore,
18496
18761
  createRecallTool,
18497
18762
  createStockPricePreProcessor,
18498
18763
  createStreamingDeAnonymizer,
@@ -18536,6 +18801,7 @@ function createNotionTools(getAccessToken, requestNotionAccess) {
18536
18801
  getVaultMemoryOp,
18537
18802
  hasEncryptionKey,
18538
18803
  isPiiRedactor,
18804
+ isRerankerAvailable,
18539
18805
  moveMemoriesToFolderOp,
18540
18806
  noopLogger,
18541
18807
  onKeyAvailable,