@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.
@@ -1483,7 +1483,13 @@ async function readJsonField(model, column, walletAddress) {
1483
1483
  }
1484
1484
  }
1485
1485
  async function searchChunksOp(ctx, queryVector, options) {
1486
- const { limit = 10, minSimilarity = 0.5, conversationId, embeddingModel } = options || {};
1486
+ const {
1487
+ limit = 10,
1488
+ minSimilarity = 0.5,
1489
+ conversationId,
1490
+ embeddingModel,
1491
+ chunkCache
1492
+ } = options || {};
1487
1493
  const activeConversations = await ctx.conversationsCollection.query(Q.where("is_deleted", false)).fetch();
1488
1494
  const activeConversationIds = new Set(
1489
1495
  // Use _getRaw for reliable raw column access
@@ -1503,23 +1509,54 @@ async function searchChunksOp(ctx, queryVector, options) {
1503
1509
  continue;
1504
1510
  }
1505
1511
  }
1512
+ const version = Number(message._getRaw("updated_at")) || 0;
1513
+ const cached = chunkCache?.get(message.id);
1514
+ if (cached && cached.version === version) {
1515
+ if (cached.chunks.length > 0) {
1516
+ for (let ci = 0; ci < cached.chunks.length; ci++) {
1517
+ const vec = cached.chunks[ci];
1518
+ if (vec.length === 0) continue;
1519
+ const similarity = cosineSimilarity(queryVector, vec);
1520
+ if (similarity >= minSimilarity) {
1521
+ candidates.push({
1522
+ message,
1523
+ similarity,
1524
+ chunkTextSource: { kind: "chunk-cached", chunkIndex: ci, version }
1525
+ });
1526
+ }
1527
+ }
1528
+ } else if (cached.fallback) {
1529
+ const similarity = cosineSimilarity(queryVector, cached.fallback);
1530
+ if (similarity >= minSimilarity) {
1531
+ candidates.push({ message, similarity, chunkTextSource: { kind: "message" } });
1532
+ }
1533
+ }
1534
+ continue;
1535
+ }
1506
1536
  const chunks = await readJsonField(message, "chunks", ctx.walletAddress);
1507
1537
  if (chunks && chunks.length > 0) {
1508
- for (const chunk of chunks) {
1509
- if (!chunk.vector || chunk.vector.length === 0) continue;
1510
- const similarity = cosineSimilarity(queryVector, chunk.vector);
1538
+ const vectors = chunks.map(
1539
+ (chunk) => chunk.vector && chunk.vector.length > 0 ? Float32Array.from(chunk.vector) : new Float32Array(0)
1540
+ );
1541
+ chunkCache?.set(message.id, { version, chunks: vectors });
1542
+ for (let ci = 0; ci < chunks.length; ci++) {
1543
+ const vec = vectors[ci];
1544
+ if (vec.length === 0) continue;
1545
+ const similarity = cosineSimilarity(queryVector, vec);
1511
1546
  if (similarity >= minSimilarity) {
1512
1547
  candidates.push({
1513
1548
  message,
1514
1549
  similarity,
1515
- chunkTextSource: { kind: "chunk", text: chunk.text }
1550
+ chunkTextSource: { kind: "chunk", text: chunks[ci].text }
1516
1551
  });
1517
1552
  }
1518
1553
  }
1519
1554
  } else {
1520
1555
  const messageVector = await readJsonField(message, "vector", ctx.walletAddress);
1521
1556
  if (!messageVector || messageVector.length === 0) continue;
1522
- const similarity = cosineSimilarity(queryVector, messageVector);
1557
+ const fallback = Float32Array.from(messageVector);
1558
+ chunkCache?.set(message.id, { version, chunks: [], fallback });
1559
+ const similarity = cosineSimilarity(queryVector, fallback);
1523
1560
  if (similarity >= minSimilarity) {
1524
1561
  candidates.push({ message, similarity, chunkTextSource: { kind: "message" } });
1525
1562
  }
@@ -1546,9 +1583,31 @@ async function searchChunksOp(ctx, queryVector, options) {
1546
1583
  })
1547
1584
  )
1548
1585
  );
1586
+ const chunkStateByMsgId = /* @__PURE__ */ new Map();
1587
+ const needsChunkText = /* @__PURE__ */ new Set();
1588
+ for (const c of topK) {
1589
+ if (c.chunkTextSource.kind === "chunk-cached") needsChunkText.add(c.message.id);
1590
+ }
1591
+ await Promise.all(
1592
+ Array.from(needsChunkText, async (id) => {
1593
+ const m = uniqueMessages.get(id);
1594
+ const parsed = await readJsonField(m, "chunks", ctx.walletAddress);
1595
+ const version = Number(m._getRaw("updated_at")) || 0;
1596
+ if (parsed) chunkStateByMsgId.set(id, { version, chunks: parsed });
1597
+ })
1598
+ );
1549
1599
  return topK.map(({ message, similarity, chunkTextSource }) => {
1550
1600
  const stored = { ...storedById.get(message.id) };
1551
- const chunkText2 = chunkTextSource.kind === "chunk" ? chunkTextSource.text : stored.content;
1601
+ let chunkText2;
1602
+ if (chunkTextSource.kind === "chunk") {
1603
+ chunkText2 = chunkTextSource.text;
1604
+ } else if (chunkTextSource.kind === "chunk-cached") {
1605
+ const state = chunkStateByMsgId.get(message.id);
1606
+ const chunk = state && state.version === chunkTextSource.version ? state.chunks[chunkTextSource.chunkIndex] : void 0;
1607
+ chunkText2 = chunk?.text ?? stored.content;
1608
+ } else {
1609
+ chunkText2 = stored.content;
1610
+ }
1552
1611
  return { chunkText: chunkText2, message: stored, similarity };
1553
1612
  });
1554
1613
  }
@@ -3685,9 +3744,9 @@ function recencyMultiplier(updatedAt, opts = {}) {
3685
3744
  const updatedMs = updatedAt.getTime();
3686
3745
  if (!Number.isFinite(updatedMs)) return noDate;
3687
3746
  const now = opts.now ?? /* @__PURE__ */ new Date();
3688
- const nowMs = now.getTime();
3689
- if (!Number.isFinite(nowMs)) return noDate;
3690
- const ageDays = Math.max(0, (nowMs - updatedMs) / DAY_MS);
3747
+ const nowMs2 = now.getTime();
3748
+ if (!Number.isFinite(nowMs2)) return noDate;
3749
+ const ageDays = Math.max(0, (nowMs2 - updatedMs) / DAY_MS);
3691
3750
  const perYearDecay = opts.perYearDecay ?? DEFAULT_PER_YEAR_DECAY;
3692
3751
  const floor = opts.floor ?? DEFAULT_FLOOR;
3693
3752
  const decay = perYearDecay * (ageDays / 365);
@@ -3696,15 +3755,51 @@ function recencyMultiplier(updatedAt, opts = {}) {
3696
3755
 
3697
3756
  // src/lib/memory/reranker.ts
3698
3757
  var MODEL_ID = "Xenova/ms-marco-MiniLM-L-6-v2";
3758
+ var RerankerUnavailableError = class extends Error {
3759
+ constructor(reason) {
3760
+ super("cross-encoder reranker unavailable (@huggingface/transformers not installed)");
3761
+ this.name = "RerankerUnavailableError";
3762
+ this.reason = reason;
3763
+ }
3764
+ };
3765
+ var available = void 0;
3699
3766
  var modelPromise = null;
3767
+ function isRerankerAvailable() {
3768
+ return available;
3769
+ }
3770
+ function isModuleMissing(err) {
3771
+ let e = err;
3772
+ for (let depth = 0; depth < 5 && e !== null && e !== void 0; depth++) {
3773
+ const code = e.code;
3774
+ if (code === "ERR_MODULE_NOT_FOUND" || code === "MODULE_NOT_FOUND") return true;
3775
+ if (e instanceof Error && /cannot find (module|package)|failed to resolve|unable to resolve module|requiring unknown module/i.test(
3776
+ e.message
3777
+ )) {
3778
+ return true;
3779
+ }
3780
+ e = e.cause;
3781
+ }
3782
+ return false;
3783
+ }
3700
3784
  async function getModel() {
3785
+ if (available === false) throw new RerankerUnavailableError(void 0);
3701
3786
  if (!modelPromise) {
3702
3787
  const load = (async () => {
3703
- const transformers = await import("@huggingface/transformers");
3788
+ let transformers;
3789
+ try {
3790
+ transformers = await import("@huggingface/transformers");
3791
+ } catch (err) {
3792
+ if (isModuleMissing(err)) {
3793
+ available = false;
3794
+ throw new RerankerUnavailableError(err);
3795
+ }
3796
+ throw err;
3797
+ }
3704
3798
  const [tokenizer, model] = await Promise.all([
3705
3799
  transformers.AutoTokenizer.from_pretrained(MODEL_ID),
3706
3800
  transformers.AutoModelForSequenceClassification.from_pretrained(MODEL_ID)
3707
3801
  ]);
3802
+ available = true;
3708
3803
  return { tokenizer, model };
3709
3804
  })();
3710
3805
  modelPromise = load;
@@ -4265,6 +4360,9 @@ async function rankFusedVaultMemoriesAsync(query, queryEmbedding, items, options
4265
4360
  bm25AdmissionDivisor: options?.bm25AdmissionDivisor,
4266
4361
  rrfK: options?.rrfK
4267
4362
  });
4363
+ if (options?.v2HeadStats) {
4364
+ options.v2HeadStats.hadResults = v2Ranked.length > 0;
4365
+ }
4268
4366
  let combined;
4269
4367
  let tailSlice = [];
4270
4368
  if (options?.rerank) {
@@ -4285,8 +4383,13 @@ async function rankFusedVaultMemoriesAsync(query, queryEmbedding, items, options
4285
4383
  return { ...r, similarity: v2 * (1 + ceWeight * ce) };
4286
4384
  });
4287
4385
  combined.sort((a, b) => b.similarity - a.similarity);
4386
+ if (headSlice.length > 0 && options.rerankStats) options.rerankStats.applied = true;
4288
4387
  } catch (err) {
4289
- getLogger().warn("[memory/search] cross-encoder rerank failed; using V2 ranking", err);
4388
+ if (err instanceof RerankerUnavailableError) {
4389
+ getLogger().debug("[memory/search] cross-encoder unavailable; using V2 ranking");
4390
+ } else {
4391
+ getLogger().warn("[memory/search] cross-encoder rerank failed; using V2 ranking", err);
4392
+ }
4290
4393
  combined = v2Ranked;
4291
4394
  }
4292
4395
  } else {
@@ -4420,6 +4523,9 @@ async function rankComposite(originalQuery, originalQueryEmbedding, subQueries,
4420
4523
  (r) => r !== null
4421
4524
  );
4422
4525
  combined.sort((a, b) => b.similarity - a.similarity);
4526
+ if (options?.v2HeadStats) {
4527
+ options.v2HeadStats.hadResults = combined.length > 0;
4528
+ }
4423
4529
  if (options?.includeUnrankedTail) {
4424
4530
  const fusedIds = new Set(combined.map((r) => r.uniqueId));
4425
4531
  for (const item of items) {
@@ -4455,11 +4561,16 @@ async function rankComposite(originalQuery, originalQueryEmbedding, subQueries,
4455
4561
  }));
4456
4562
  head.sort((a, b) => b.similarity - a.similarity);
4457
4563
  combined = [...head, ...tailSlice];
4564
+ if (headSlice.length > 0 && options.rerankStats) options.rerankStats.applied = true;
4458
4565
  } catch (err) {
4459
- getLogger().warn(
4460
- "[memory/search] composite cross-encoder rerank failed; using fused ranking",
4461
- err
4462
- );
4566
+ if (err instanceof RerankerUnavailableError) {
4567
+ getLogger().debug("[memory/search] cross-encoder unavailable; using fused ranking");
4568
+ } else {
4569
+ getLogger().warn(
4570
+ "[memory/search] composite cross-encoder rerank failed; using fused ranking",
4571
+ err
4572
+ );
4573
+ }
4463
4574
  }
4464
4575
  }
4465
4576
  if (options?.mmr) {
@@ -4551,7 +4662,7 @@ async function searchVaultMemoriesWithSize(query, vaultCtx, embeddingOptions, ca
4551
4662
  const minSimilarity = searchOptions?.minSimilarity ?? 0.1;
4552
4663
  const scopes = searchOptions?.scopes;
4553
4664
  if (!query || typeof query !== "string") {
4554
- return { results: [], vaultSize: 0 };
4665
+ return { results: [], vaultSize: 0, reranked: false, hadV2Head: false };
4555
4666
  }
4556
4667
  const folderId = searchOptions?.folderId;
4557
4668
  const queryOpts = {};
@@ -4568,7 +4679,7 @@ async function searchVaultMemoriesWithSize(query, vaultCtx, embeddingOptions, ca
4568
4679
  );
4569
4680
  }
4570
4681
  if (memories.length === 0) {
4571
- return { results: [], vaultSize: loaded.length };
4682
+ return { results: [], vaultSize: loaded.length, reranked: false, hadV2Head: false };
4572
4683
  }
4573
4684
  const queryEmbedding = await generateEmbedding(query, embeddingOptions);
4574
4685
  const currentModel = embeddingOptions.model ?? DEFAULT_API_EMBEDDING_MODEL;
@@ -4644,12 +4755,18 @@ async function searchVaultMemoriesWithSize(query, vaultCtx, embeddingOptions, ca
4644
4755
  memories.map((m) => [m.uniqueId, { createdAt: m.createdAt, updatedAt: m.updatedAt }])
4645
4756
  );
4646
4757
  const stampTimestamps = (out) => {
4647
- out.results = out.results.map((r) => {
4758
+ const results2 = out.results.map((r) => {
4648
4759
  const ts = timestampById.get(r.uniqueId);
4649
4760
  return ts ? { ...r, createdAt: ts.createdAt, updatedAt: ts.updatedAt } : r;
4650
4761
  });
4651
- return out;
4762
+ return {
4763
+ results: results2,
4764
+ vaultSize: out.vaultSize,
4765
+ reranked: out.reranked ?? false,
4766
+ hadV2Head: out.hadV2Head ?? false
4767
+ };
4652
4768
  };
4769
+ const rerankStats = { applied: false };
4653
4770
  const useFusion = searchOptions?.useFusion ?? true;
4654
4771
  const tuning = {
4655
4772
  ...searchOptions?.recencyAlpha !== void 0 && { recencyAlpha: searchOptions.recencyAlpha },
@@ -4676,6 +4793,7 @@ async function searchVaultMemoriesWithSize(query, vaultCtx, embeddingOptions, ca
4676
4793
  query: sq,
4677
4794
  embedding: subEmbeddings[i]
4678
4795
  }));
4796
+ const v2HeadStats = { hadResults: false };
4679
4797
  const composite = await rankComposite(query, queryEmbedding, subQueries, embeddedItems, {
4680
4798
  limit,
4681
4799
  minSimilarity,
@@ -4687,12 +4805,20 @@ async function searchVaultMemoriesWithSize(query, vaultCtx, embeddingOptions, ca
4687
4805
  ...searchOptions.mmr !== void 0 && { mmr: searchOptions.mmr },
4688
4806
  ...tuning,
4689
4807
  ...searchOptions.entityRanking && { entityRanking: searchOptions.entityRanking },
4690
- ...searchOptions.temporalRanking && { temporalRanking: searchOptions.temporalRanking }
4808
+ ...searchOptions.temporalRanking && { temporalRanking: searchOptions.temporalRanking },
4809
+ rerankStats,
4810
+ v2HeadStats
4811
+ });
4812
+ return stampTimestamps({
4813
+ results: composite,
4814
+ vaultSize: loaded.length,
4815
+ reranked: rerankStats.applied,
4816
+ hadV2Head: v2HeadStats.hadResults
4691
4817
  });
4692
- return stampTimestamps({ results: composite, vaultSize: loaded.length });
4693
4818
  }
4694
4819
  }
4695
4820
  if (useFusion && searchOptions?.rerank) {
4821
+ const v2HeadStats = { hadResults: false };
4696
4822
  const results2 = await rankFusedVaultMemoriesAsync(query, queryEmbedding, embeddedItems, {
4697
4823
  limit,
4698
4824
  minSimilarity,
@@ -4704,9 +4830,16 @@ async function searchVaultMemoriesWithSize(query, vaultCtx, embeddingOptions, ca
4704
4830
  ...searchOptions.mmr !== void 0 && { mmr: searchOptions.mmr },
4705
4831
  ...tuning,
4706
4832
  ...searchOptions.entityRanking && { entityRanking: searchOptions.entityRanking },
4707
- ...searchOptions.temporalRanking && { temporalRanking: searchOptions.temporalRanking }
4833
+ ...searchOptions.temporalRanking && { temporalRanking: searchOptions.temporalRanking },
4834
+ rerankStats,
4835
+ v2HeadStats
4836
+ });
4837
+ return stampTimestamps({
4838
+ results: results2,
4839
+ vaultSize: loaded.length,
4840
+ reranked: rerankStats.applied,
4841
+ hadV2Head: v2HeadStats.hadResults
4708
4842
  });
4709
- return stampTimestamps({ results: results2, vaultSize: loaded.length });
4710
4843
  }
4711
4844
  if (useFusion) {
4712
4845
  const results2 = rankFusedVaultMemories(query, queryEmbedding, embeddedItems, {
@@ -4716,7 +4849,7 @@ async function searchVaultMemoriesWithSize(query, vaultCtx, embeddingOptions, ca
4716
4849
  ...searchOptions?.entityRanking && { entityRanking: searchOptions.entityRanking },
4717
4850
  ...searchOptions?.temporalRanking && { temporalRanking: searchOptions.temporalRanking }
4718
4851
  });
4719
- return stampTimestamps({ results: results2, vaultSize: loaded.length });
4852
+ return stampTimestamps({ results: results2, vaultSize: loaded.length, hadV2Head: results2.length > 0 });
4720
4853
  }
4721
4854
  const results = rankVaultMemories(query, queryEmbedding, embeddedItems, {
4722
4855
  limit,
@@ -4728,7 +4861,7 @@ async function searchVaultMemoriesWithSize(query, vaultCtx, embeddingOptions, ca
4728
4861
  supersessionWindow: searchOptions.supersessionWindow
4729
4862
  }
4730
4863
  });
4731
- return stampTimestamps({ results, vaultSize: loaded.length });
4864
+ return stampTimestamps({ results, vaultSize: loaded.length, hadV2Head: results.length > 0 });
4732
4865
  }
4733
4866
  async function searchVaultMemories(query, vaultCtx, embeddingOptions, cache, searchOptions) {
4734
4867
  const { results } = await searchVaultMemoriesWithSize(
@@ -4817,7 +4950,7 @@ function createMemoryVaultSearchTool(vaultCtx, embeddingOptions, cache, searchOp
4817
4950
  legacy.map((r) => ({ id: r.uniqueId, content: r.content, score: r.similarity }))
4818
4951
  );
4819
4952
  }
4820
- const { recall: recall2 } = await import("./recall-PCKDVF3U.mjs");
4953
+ const { recall: recall2 } = await import("./recall-HI7VO3VM.mjs");
4821
4954
  const result = await recall2(
4822
4955
  query,
4823
4956
  { vaultCtx, embeddingOptions, vaultCache: cache },
@@ -5135,6 +5268,7 @@ function scoreEventTimeOverlap(memoryStart, memoryEnd, memoryKind, window2) {
5135
5268
  var DEFAULT_LIMIT = 8;
5136
5269
  var DEFAULT_BUDGET = "low";
5137
5270
  var DEFAULT_FACT_MIN_SCORE = 0.1;
5271
+ var nowMs = () => typeof performance !== "undefined" && typeof performance.now === "function" ? performance.now() : Date.now();
5138
5272
  var DEFAULT_CHUNK_MIN_SCORE = 0.5;
5139
5273
  function flagsForBudget(budget) {
5140
5274
  switch (budget) {
@@ -5167,22 +5301,67 @@ async function recall(query, ctx, options = {}) {
5167
5301
  const flags = flagsForBudget(requestedBudget);
5168
5302
  const decomposeAvailable = flags.decompose && !!options.decomposeOptions;
5169
5303
  const usedBudget = requestedBudget === "high" && !decomposeAvailable ? "mid" : requestedBudget;
5304
+ const t0 = nowMs();
5305
+ let prepMs = 0;
5306
+ let factLaneMs = 0;
5307
+ let chunkLaneMs = 0;
5308
+ let fuseMs = 0;
5309
+ const factResults = [];
5310
+ const chunkResults = [];
5311
+ let vaultSize;
5312
+ let didRerank = false;
5313
+ let hadV2Head = false;
5314
+ const emitDiagnostics = (candidateCount) => {
5315
+ const cb = options.onDiagnostics;
5316
+ if (!cb) return;
5317
+ const degraded = [];
5318
+ if (flags.rerank && factResults.length > 0 && hadV2Head && !didRerank) {
5319
+ degraded.push("rerank-unavailable");
5320
+ }
5321
+ if (flags.decompose && !decomposeAvailable) degraded.push("decompose-unavailable");
5322
+ const diagnostics = {
5323
+ usedBudget,
5324
+ reranked: didRerank,
5325
+ candidateCount,
5326
+ ...vaultSize !== void 0 && { vaultSize },
5327
+ factCount: factResults.length,
5328
+ chunkCount: chunkResults.length,
5329
+ timings: {
5330
+ total: nowMs() - t0,
5331
+ prep: prepMs,
5332
+ factLane: factLaneMs,
5333
+ chunkLane: chunkLaneMs,
5334
+ fuse: fuseMs
5335
+ },
5336
+ degraded
5337
+ };
5338
+ try {
5339
+ cb(diagnostics);
5340
+ } catch {
5341
+ }
5342
+ };
5170
5343
  if (!query || typeof query !== "string" || query.trim().length === 0) {
5344
+ emitDiagnostics(0);
5171
5345
  return { memories: [], usedBudget, reranked: false, candidateCount: 0 };
5172
5346
  }
5173
5347
  const needsChunkEmbedding = types.includes("chunk") && ctx.storageCtx;
5174
5348
  const wantsTemporal = types.includes("fact") && ctx.vaultCtx;
5349
+ const prepStart = nowMs();
5175
5350
  const [queryEmbedding, entityRanking, temporalRanking] = await Promise.all([
5176
5351
  needsChunkEmbedding ? generateEmbedding(query, ctx.embeddingOptions) : Promise.resolve(void 0),
5177
5352
  buildGraphLaneRanking(query, ctx),
5178
5353
  wantsTemporal ? buildTemporalLaneRanking(query, ctx.vaultCtx, options.now) : Promise.resolve([])
5179
5354
  ]);
5180
- const factResults = [];
5181
- const chunkResults = [];
5182
- let vaultSize;
5355
+ prepMs = nowMs() - prepStart;
5183
5356
  if (types.includes("fact") && ctx.vaultCtx && ctx.vaultCache) {
5357
+ const factStart = nowMs();
5184
5358
  const vaultMinScore = options.minScore ?? DEFAULT_FACT_MIN_SCORE;
5185
- const { results, vaultSize: size } = await searchVaultMemoriesWithSize(
5359
+ const {
5360
+ results,
5361
+ vaultSize: size,
5362
+ reranked,
5363
+ hadV2Head: v2Head
5364
+ } = await searchVaultMemoriesWithSize(
5186
5365
  query,
5187
5366
  ctx.vaultCtx,
5188
5367
  ctx.embeddingOptions,
@@ -5233,14 +5412,19 @@ async function recall(query, ctx, options = {}) {
5233
5412
  )
5234
5413
  );
5235
5414
  vaultSize = size;
5415
+ didRerank = reranked;
5416
+ hadV2Head = v2Head;
5417
+ factLaneMs = nowMs() - factStart;
5236
5418
  }
5237
5419
  if (types.includes("chunk") && ctx.storageCtx && queryEmbedding) {
5420
+ const chunkStart = nowMs();
5238
5421
  const chunkMinScore = options.minScore ?? DEFAULT_CHUNK_MIN_SCORE;
5239
5422
  const results = await searchChunksOp(ctx.storageCtx, queryEmbedding, {
5240
5423
  limit: types.includes("fact") ? Math.max(limit * 2, 16) : limit,
5241
5424
  minSimilarity: chunkMinScore,
5242
5425
  embeddingModel: ctx.embeddingOptions.model ?? DEFAULT_API_EMBEDDING_MODEL,
5243
- ...options.conversationId && { conversationId: options.conversationId }
5426
+ ...options.conversationId && { conversationId: options.conversationId },
5427
+ ...ctx.chunkCache && { chunkCache: ctx.chunkCache }
5244
5428
  });
5245
5429
  chunkResults.push(
5246
5430
  ...dedupeBy(
@@ -5250,18 +5434,23 @@ async function recall(query, ctx, options = {}) {
5250
5434
  (r) => r.chunkText.trim()
5251
5435
  )
5252
5436
  );
5437
+ chunkLaneMs = nowMs() - chunkStart;
5253
5438
  }
5439
+ const fuseStart = nowMs();
5254
5440
  if (types.length === 1 || factResults.length === 0 || chunkResults.length === 0) {
5255
5441
  const memories2 = [
5256
5442
  ...factResults.map(toFactMemory),
5257
5443
  ...chunkResults.map(toChunkMemory)
5258
5444
  ];
5259
5445
  memories2.sort((a, b) => b.score - a.score);
5446
+ const candidateCount = factResults.length + chunkResults.length;
5447
+ fuseMs = nowMs() - fuseStart;
5448
+ emitDiagnostics(candidateCount);
5260
5449
  return {
5261
5450
  memories: memories2.slice(0, limit),
5262
5451
  usedBudget,
5263
- reranked: flags.rerank,
5264
- candidateCount: factResults.length + chunkResults.length,
5452
+ reranked: didRerank,
5453
+ candidateCount,
5265
5454
  ...vaultSize !== void 0 && { vaultSize }
5266
5455
  };
5267
5456
  }
@@ -5310,10 +5499,12 @@ async function recall(query, ctx, options = {}) {
5310
5499
  suppressed = next;
5311
5500
  memories = selectWith(suppressed);
5312
5501
  }
5502
+ fuseMs = nowMs() - fuseStart;
5503
+ emitDiagnostics(byId.size);
5313
5504
  return {
5314
5505
  memories,
5315
5506
  usedBudget,
5316
- reranked: flags.rerank,
5507
+ reranked: didRerank,
5317
5508
  candidateCount: byId.size,
5318
5509
  ...vaultSize !== void 0 && { vaultSize }
5319
5510
  };
@@ -5456,7 +5647,10 @@ export {
5456
5647
  stampTopicsExtractedAtOp,
5457
5648
  deleteAllVaultMemoriesForUserOp,
5458
5649
  updateVaultMemoryEmbeddingOp,
5650
+ RerankerUnavailableError,
5651
+ isRerankerAvailable,
5459
5652
  DEFAULT_VAULT_CACHE_SIZE,
5653
+ LRUCache,
5460
5654
  createVaultEmbeddingCache,
5461
5655
  recall,
5462
5656
  preEmbedVaultMemories,