@anuma/sdk 6.42.1 → 6.43.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.
@@ -13,7 +13,7 @@ import {
13
13
  taskTypeHeader,
14
14
  validateEndpointOverride,
15
15
  withInternalFlowMarker
16
- } from "./chunk-4RGGAOAJ.mjs";
16
+ } from "./chunk-WCG5BFI6.mjs";
17
17
  import {
18
18
  BASE_URL,
19
19
  __commonJS,
@@ -18276,7 +18276,10 @@ async function embedOne(text12, options, outer) {
18276
18276
  const cached = cache.get(text12);
18277
18277
  if (cached) return Array.from(cached);
18278
18278
  }
18279
- const headers = await resolveAuthHeaders(options, outer);
18279
+ const headers = {
18280
+ ...await resolveAuthHeaders(options, outer),
18281
+ ...taskTypeHeader(options.taskType)
18282
+ };
18280
18283
  const response = await withEmbeddingRetry(
18281
18284
  () => withDeadline(
18282
18285
  (signal) => postApiV1Embeddings({
@@ -18371,7 +18374,10 @@ async function embedMany(texts, options, outer) {
18371
18374
  if (uncachedTexts.length === 0) {
18372
18375
  return results;
18373
18376
  }
18374
- const headers = await resolveAuthHeaders(options, outer);
18377
+ const headers = {
18378
+ ...await resolveAuthHeaders(options, outer),
18379
+ ...taskTypeHeader(options.taskType)
18380
+ };
18375
18381
  if (outer?.aborted) throw outer.reason;
18376
18382
  const embeddingModel = model ?? DEFAULT_API_EMBEDDING_MODEL;
18377
18383
  let newEmbeddings;
@@ -28676,6 +28682,50 @@ function applyMMR(candidates, k, lambda = 0.5) {
28676
28682
  return selected;
28677
28683
  }
28678
28684
 
28685
+ // src/lib/memory/recallTelemetry.ts
28686
+ function notifyMemoryCallback(callback, value) {
28687
+ try {
28688
+ callback?.(value);
28689
+ } catch {
28690
+ return;
28691
+ }
28692
+ }
28693
+ function factRecallDiagnostics(scores, elapsedMs, options = {}) {
28694
+ return {
28695
+ usedBudget: "low",
28696
+ reranked: false,
28697
+ candidateCount: options.candidateCount ?? scores.length,
28698
+ ...options.vaultSize !== void 0 && { vaultSize: options.vaultSize },
28699
+ ...options.decryptLast !== void 0 && { decryptLast: options.decryptLast },
28700
+ ...options.vaultRowsDecrypted !== void 0 && {
28701
+ vaultRowsDecrypted: options.vaultRowsDecrypted
28702
+ },
28703
+ ...options.vaultRowsEmbedded !== void 0 && {
28704
+ vaultRowsEmbedded: options.vaultRowsEmbedded
28705
+ },
28706
+ factCount: scores.length,
28707
+ chunkCount: 0,
28708
+ admittedCount: scores.length,
28709
+ topScore: scores.length ? Math.max(...scores) : -1,
28710
+ lowestAdmittedScore: scores.length ? Math.min(...scores) : -1,
28711
+ minScoreApplied: options.minScore ?? -1,
28712
+ truncated: options.truncated ?? false,
28713
+ graphLaneCount: 0,
28714
+ temporalLaneCount: 0,
28715
+ emptyReason: scores.length ? "" : options.vaultSize === 0 ? "vault-empty" : "no-candidates",
28716
+ timings: {
28717
+ total: elapsedMs,
28718
+ prep: 0,
28719
+ factLane: elapsedMs,
28720
+ rerank: 0,
28721
+ queryEmbed: options.queryEmbedMs ?? 0,
28722
+ chunkLane: 0,
28723
+ fuse: 0
28724
+ },
28725
+ degraded: options.embeddingsUnavailable ? ["embeddings-unavailable"] : []
28726
+ };
28727
+ }
28728
+
28679
28729
  // src/lib/memory/recency.ts
28680
28730
  var DAY_MS2 = 1e3 * 60 * 60 * 24;
28681
28731
  var DEFAULT_PER_YEAR_DECAY = 0.2;
@@ -29365,6 +29415,7 @@ function rankVaultMemories(_query, queryEmbedding, items, options) {
29365
29415
  }
29366
29416
  filtered.sort((a, b) => b.similarity - a.similarity);
29367
29417
  }
29418
+ notifyMemoryCallback(options?.onCandidateCount, filtered.length);
29368
29419
  const itemById = new Map(items.map((i) => [i.id, i]));
29369
29420
  return filtered.slice(0, limit).map((r) => resultFromItem(itemById.get(r.uniqueId), r.similarity));
29370
29421
  }
@@ -30347,6 +30398,7 @@ async function rankPreparedVaultCandidates(query, prepared, embeddingOptions, se
30347
30398
  const results = rankVaultMemories(query, queryEmbedding, embeddedItems, {
30348
30399
  limit,
30349
30400
  minSimilarity,
30401
+ onCandidateCount: searchOptions?.onCandidateCount,
30350
30402
  ...searchOptions?.supersessionBoost !== void 0 && {
30351
30403
  supersessionBoost: searchOptions.supersessionBoost
30352
30404
  },
@@ -30447,16 +30499,20 @@ var DEFAULT_SCOPE = "private";
30447
30499
  var DEFAULT_CONSOLIDATE_THRESHOLD = 0.55;
30448
30500
  var DEFAULT_CONSOLIDATE_TOP_K = 20;
30449
30501
  async function retain(content, ctx, options = {}) {
30502
+ const embeddingOptions = {
30503
+ ...ctx.embeddingOptions,
30504
+ taskType: "memory_retain_embed"
30505
+ };
30450
30506
  return retainWithPersistence(
30451
30507
  content,
30452
30508
  {
30453
- embeddingOptions: ctx.embeddingOptions,
30509
+ embeddingOptions,
30454
30510
  vaultCache: ctx.vaultCache,
30455
30511
  persistence: {
30456
30512
  prepare: (query, searchOptions) => prepareVaultCandidates(
30457
30513
  query,
30458
30514
  ctx.vaultCtx,
30459
- ctx.embeddingOptions,
30515
+ embeddingOptions,
30460
30516
  ctx.vaultCache,
30461
30517
  searchOptions
30462
30518
  ),
@@ -30740,7 +30796,7 @@ async function tryConsolidate(trimmed, ctx, options, prepared) {
30740
30796
  content: m.content,
30741
30797
  similarity: m.similarity
30742
30798
  }));
30743
- const { consolidateMemory: doConsolidate } = await import("./consolidate-YEGACWLR.mjs");
30799
+ const { consolidateMemory: doConsolidate } = await import("./consolidate-WY6BHODK.mjs");
30744
30800
  const decision = await doConsolidate(trimmed, candidates, consolidateOptions);
30745
30801
  if (decision.action === "create") return decision.fallbackReason ? null : { create: true };
30746
30802
  if (decision.action === "supersede" && decision.content) {
@@ -31604,7 +31660,17 @@ function parseVerdict(parsed) {
31604
31660
  }
31605
31661
 
31606
31662
  // src/lib/memory/decayWorker.ts
31607
- var EMPTY_RESULT = { archived: 0, deleted: 0, scanned: 0 };
31663
+ function emptyResult() {
31664
+ return {
31665
+ archived: 0,
31666
+ deleted: 0,
31667
+ scanned: 0,
31668
+ ruleDriven: { archived: 0, deleted: 0 },
31669
+ classifierDriven: { archived: 0, deleted: 0 },
31670
+ classifierCalls: 0,
31671
+ classifierCapHits: 0
31672
+ };
31673
+ }
31608
31674
  var DEFAULT_MAX_CLASSIFIER_CALLS_PER_SWEEP = 20;
31609
31675
  function resolveNow(now) {
31610
31676
  if (typeof now === "function") return now();
@@ -31643,42 +31709,45 @@ function createDecaySweeper(options) {
31643
31709
  let disposed = false;
31644
31710
  async function verdictFor(input, now, sweep) {
31645
31711
  const ruleVerdict = classifyDecay(input, now, policy);
31646
- if (ruleVerdict !== "keep") return ruleVerdict;
31647
- if (!classifier || !isBorderline(input)) return ruleVerdict;
31712
+ const rule = { verdict: ruleVerdict, source: "ruleDriven" };
31713
+ if (ruleVerdict !== "keep") return rule;
31714
+ if (!classifier || !isBorderline(input)) return rule;
31648
31715
  if (input.id) {
31649
31716
  const cached = classifierCache.get(input.id);
31650
- if (cached && cached.activityAt === lastActivityAt(input)) return cached.verdict;
31717
+ if (cached && cached.activityAt === lastActivityAt(input))
31718
+ return { verdict: cached.verdict, source: "classifierDriven" };
31651
31719
  }
31652
31720
  if (sweep.classifierCalls >= maxClassifierCalls) {
31721
+ sweep.classifierCapHits++;
31653
31722
  if (!sweep.ceilingLogged) {
31654
31723
  sweep.ceilingLogged = true;
31655
31724
  getLogger().warn(
31656
31725
  `[memory/decay] classifier per-sweep ceiling (${maxClassifierCalls}) reached; remaining borderline rows use the rule verdict this sweep`
31657
31726
  );
31658
31727
  }
31659
- return ruleVerdict;
31728
+ return rule;
31660
31729
  }
31661
31730
  sweep.classifierCalls++;
31662
31731
  try {
31663
31732
  const verdict = await classifier.classify(input, ruleVerdict, now);
31664
31733
  if (input.id) classifierCache.set(input.id, { activityAt: lastActivityAt(input), verdict });
31665
- return verdict;
31734
+ return { verdict, source: "classifierDriven" };
31666
31735
  } catch (err) {
31667
31736
  getLogger().warn(
31668
31737
  `[memory/decay] classifier failed; falling back to rule verdict: ${err instanceof Error ? err.message : String(err)}`
31669
31738
  );
31670
- return ruleVerdict;
31739
+ return rule;
31671
31740
  }
31672
31741
  }
31673
31742
  async function runSweep() {
31674
- if (disposed) return { ...EMPTY_RESULT };
31743
+ if (disposed) return emptyResult();
31675
31744
  const now = resolveNow(options.now);
31676
31745
  let candidates;
31677
31746
  try {
31678
31747
  candidates = await getDecayCandidatesRawOp(vaultCtx);
31679
31748
  } catch (err) {
31680
31749
  onError?.(err instanceof Error ? err : new Error(String(err)));
31681
- return { ...EMPTY_RESULT };
31750
+ return emptyResult();
31682
31751
  }
31683
31752
  if (classifierCache.size > 0) {
31684
31753
  const liveIds = new Set(candidates.map((c) => c.uniqueId));
@@ -31686,33 +31755,40 @@ function createDecaySweeper(options) {
31686
31755
  if (!liveIds.has(id)) classifierCache.delete(id);
31687
31756
  }
31688
31757
  }
31689
- const sweep = { classifierCalls: 0, ceilingLogged: false };
31758
+ const sweep = { classifierCalls: 0, classifierCapHits: 0, ceilingLogged: false };
31690
31759
  const toArchive = [];
31691
31760
  const toDelete = [];
31692
31761
  for (const c of candidates) {
31693
- const verdict = await verdictFor(toDecayInput(c), now, sweep);
31694
- if (verdict === "archive") toArchive.push(c);
31695
- else if (verdict === "delete") toDelete.push(c);
31696
- }
31697
- let archived = 0;
31698
- let deleted = 0;
31762
+ const { verdict, source } = await verdictFor(toDecayInput(c), now, sweep);
31763
+ if (verdict === "archive") toArchive.push({ candidate: c, source });
31764
+ else if (verdict === "delete") toDelete.push({ candidate: c, source });
31765
+ }
31766
+ const result = emptyResult();
31767
+ result.scanned = candidates.length;
31768
+ result.classifierCalls = sweep.classifierCalls;
31769
+ result.classifierCapHits = sweep.classifierCapHits;
31699
31770
  try {
31700
- for (const c of toArchive) {
31771
+ for (const { candidate: c, source } of toArchive) {
31701
31772
  const ok = await archiveVaultMemoryOp(vaultCtx, c.uniqueId, {
31702
31773
  now,
31703
31774
  expectedUpdatedAt: c.updatedAt,
31704
31775
  expectedLastObservedAt: c.lastObservedAt ?? null
31705
31776
  });
31706
- if (ok) archived++;
31777
+ if (ok) {
31778
+ result.archived++;
31779
+ result[source].archived++;
31780
+ }
31707
31781
  }
31708
- for (const c of toDelete) {
31782
+ for (const { candidate: c, source } of toDelete) {
31709
31783
  const ok = await hardDeleteDecayedOp(vaultCtx, c.uniqueId, { hardDeleteWindowMs, now });
31710
- if (ok) deleted++;
31784
+ if (ok) {
31785
+ result.deleted++;
31786
+ result[source].deleted++;
31787
+ }
31711
31788
  }
31712
31789
  } catch (err) {
31713
31790
  onError?.(err instanceof Error ? err : new Error(String(err)));
31714
31791
  }
31715
- const result = { archived, deleted, scanned: candidates.length };
31716
31792
  onSwept?.(result);
31717
31793
  return result;
31718
31794
  }
@@ -32310,6 +32386,10 @@ async function recall(query, ctx, options = {}) {
32310
32386
  const flags = flagsForBudget(usedBudget);
32311
32387
  const normalizedFacets = normalizeSubQueries(options.subQueries);
32312
32388
  const subQueries = normalizedFacets.length >= 2 ? normalizedFacets : void 0;
32389
+ const embeddingOptions = {
32390
+ ...ctx.embeddingOptions,
32391
+ taskType: "memory_recall_embed"
32392
+ };
32313
32393
  const t0 = nowMs2();
32314
32394
  let prepMs = 0;
32315
32395
  let factLaneMs = 0;
@@ -32405,7 +32485,7 @@ async function recall(query, ctx, options = {}) {
32405
32485
  let sharedEmbedMs = 0;
32406
32486
  const [queryEmbedding, entityRanking, temporalRanking] = await Promise.all([
32407
32487
  needsChunkEmbedding ? generateEmbedding(query, {
32408
- ...ctx.embeddingOptions,
32488
+ ...embeddingOptions,
32409
32489
  totalTimeoutMs: queryEmbedTotalTimeoutMs
32410
32490
  }).finally(() => {
32411
32491
  sharedEmbedMs = nowMs2() - prepStart;
@@ -32463,7 +32543,7 @@ async function recall(query, ctx, options = {}) {
32463
32543
  } = await (ctx.factSource?.search.bind(ctx.factSource) ?? ((searchQuery, searchOptions) => searchVaultMemoriesWithSize(
32464
32544
  searchQuery,
32465
32545
  ctx.vaultCtx,
32466
- ctx.embeddingOptions,
32546
+ embeddingOptions,
32467
32547
  ctx.vaultCache,
32468
32548
  searchOptions
32469
32549
  )))(query, {
@@ -32829,7 +32909,7 @@ function isDumpQuery(query) {
32829
32909
  async function listRecentFacts(ctx, limit, toolOptions) {
32830
32910
  const store = toolOptions?.memoryStore;
32831
32911
  const vaultCtx = ctx.vaultCtx;
32832
- if (!vaultCtx && !store) return [];
32912
+ if (!vaultCtx && !store) return { memories: [], truncated: false };
32833
32913
  const read = (ids) => store ? store.list({ memoryIds: ids }) : getVaultMemoriesByIdsOp(vaultCtx, ids);
32834
32914
  const filters = {
32835
32915
  ...toolOptions?.scopes && { scopes: toolOptions.scopes },
@@ -32840,34 +32920,47 @@ async function listRecentFacts(ctx, limit, toolOptions) {
32840
32920
  ...toolOptions?.folderId !== void 0 && { folderId: toolOptions.folderId }
32841
32921
  });
32842
32922
  const readable = [];
32923
+ const readLimit = limit + 1;
32924
+ let batchLimit = limit;
32843
32925
  let start = 0;
32844
- for (let size = limit; readable.length < limit; size *= 2) {
32926
+ let projected = 0;
32927
+ let exhausted = false;
32928
+ for (let size = readLimit; readable.length < readLimit; size *= 2) {
32845
32929
  const ordered = await project(size);
32846
- for (let i = start; i < ordered.length && readable.length < limit; i += limit) {
32847
- const batch = ordered.slice(i, i + limit).map((p) => p.uniqueId);
32930
+ projected = ordered.length;
32931
+ exhausted = !store || ordered.length < size;
32932
+ for (let i = start; i < ordered.length && readable.length < readLimit; ) {
32933
+ const batch = ordered.slice(i, i + batchLimit).map((p) => p.uniqueId);
32848
32934
  const byId = new Map((await read(batch)).map((m) => [m.uniqueId, m]));
32935
+ const previousCount = readable.length;
32849
32936
  for (const id of batch) {
32850
32937
  const m = byId.get(id);
32851
32938
  if (m && !isEncrypted(m.content)) readable.push(m);
32852
- if (readable.length >= limit) break;
32939
+ if (readable.length >= readLimit) break;
32853
32940
  }
32941
+ batchLimit = readable.length > previousCount ? Math.min(limit, readLimit - readable.length) : limit;
32942
+ i += batch.length;
32854
32943
  }
32855
- if (!store || ordered.length < size) break;
32944
+ if (exhausted) break;
32856
32945
  start = ordered.length;
32857
32946
  }
32858
- return readable.map((m) => ({
32859
- id: m.uniqueId,
32860
- kind: "fact",
32861
- content: m.content,
32862
- score: 0,
32863
- folderId: m.folderId,
32864
- eventTimeStart: m.eventTimeStart,
32865
- eventTimeEnd: m.eventTimeEnd,
32866
- eventTimeKind: m.eventTimeKind,
32867
- factType: m.factType,
32868
- createdAt: m.createdAt,
32869
- updatedAt: m.updatedAt
32870
- }));
32947
+ return {
32948
+ memories: readable.slice(0, limit).map((m) => ({
32949
+ id: m.uniqueId,
32950
+ kind: "fact",
32951
+ content: m.content,
32952
+ score: 0,
32953
+ folderId: m.folderId,
32954
+ eventTimeStart: m.eventTimeStart,
32955
+ eventTimeEnd: m.eventTimeEnd,
32956
+ eventTimeKind: m.eventTimeKind,
32957
+ factType: m.factType,
32958
+ createdAt: m.createdAt,
32959
+ updatedAt: m.updatedAt
32960
+ })),
32961
+ ...exhausted && { vaultSize: projected },
32962
+ truncated: readable.length > limit
32963
+ };
32871
32964
  }
32872
32965
  function createRecallTool(ctx, toolOptions, callbacks) {
32873
32966
  if (toolOptions?.memoryStore && toolOptions.folderId !== void 0)
@@ -32906,7 +32999,11 @@ function createRecallTool(ctx, toolOptions, callbacks) {
32906
32999
  },
32907
33000
  executor: async (args) => {
32908
33001
  if (typeof args.query !== "string" || args.query.length === 0) {
32909
- throw new Error("recall_memory: `query` is required and must be a non-empty string.");
33002
+ const error = new Error(
33003
+ "recall_memory: `query` is required and must be a non-empty string."
33004
+ );
33005
+ notifyMemoryCallback(callbacks?.onError, error);
33006
+ throw error;
32910
33007
  }
32911
33008
  const query = args.query;
32912
33009
  const now = Date.now();
@@ -32934,10 +33031,17 @@ function createRecallTool(ctx, toolOptions, callbacks) {
32934
33031
  RECALL_MAX_MEMORIES_PER_CONVERSATION - conversationMemories
32935
33032
  );
32936
33033
  const effectiveLimit = Math.min(requestLimit, remainingTurnBudget, remainingConvBudget);
33034
+ const truncatedByBudget = effectiveLimit < requestLimit;
33035
+ if (truncatedByBudget) {
33036
+ notifyMemoryCallback(callbacks?.onTruncated, {
33037
+ requestedLimit: requestLimit,
33038
+ effectiveLimit,
33039
+ truncatedByBudget
33040
+ });
33041
+ }
32937
33042
  if (effectiveLimit <= 0) {
32938
33043
  return VOLUME_LIMIT_NOTICE;
32939
33044
  }
32940
- const truncatedByBudget = effectiveLimit < requestLimit;
32941
33045
  turnMemories += effectiveLimit;
32942
33046
  conversationMemories += effectiveLimit;
32943
33047
  let surfaced = 0;
@@ -32946,7 +33050,27 @@ function createRecallTool(ctx, toolOptions, callbacks) {
32946
33050
  const wantsRecent = args.sort === "recent" && defaultTypes.includes("fact") && (ctx.vaultCtx !== void 0 || toolOptions?.memoryStore !== void 0);
32947
33051
  let result;
32948
33052
  if (wantsRecent) {
32949
- result = { memories: await listRecentFacts(ctx, effectiveLimit, toolOptions) };
33053
+ const start = Date.now();
33054
+ const recent = await listRecentFacts(ctx, effectiveLimit, toolOptions);
33055
+ result = recent;
33056
+ notifyMemoryCallback(callbacks?.onDiagnostics, {
33057
+ ...factRecallDiagnostics(
33058
+ result.memories.map((m) => m.score),
33059
+ Date.now() - start,
33060
+ {
33061
+ truncated: recent.truncated,
33062
+ ...recent.vaultSize !== void 0 && { vaultSize: recent.vaultSize }
33063
+ }
33064
+ ),
33065
+ truncatedByBudget
33066
+ });
33067
+ if (recent.truncated && !truncatedByBudget) {
33068
+ notifyMemoryCallback(callbacks?.onTruncated, {
33069
+ requestedLimit: requestLimit,
33070
+ effectiveLimit,
33071
+ truncatedByBudget
33072
+ });
33073
+ }
32950
33074
  } else {
32951
33075
  let subQueries;
32952
33076
  if (defaultBudget === "high" && toolOptions?.decomposeOptions) {
@@ -32958,6 +33082,14 @@ function createRecallTool(ctx, toolOptions, callbacks) {
32958
33082
  const recallOpts = {
32959
33083
  onDiagnostics: (d) => {
32960
33084
  recallDegraded = d.degraded;
33085
+ notifyMemoryCallback(callbacks?.onDiagnostics, { ...d, truncatedByBudget });
33086
+ if (d.truncated && !truncatedByBudget) {
33087
+ notifyMemoryCallback(callbacks?.onTruncated, {
33088
+ requestedLimit: requestLimit,
33089
+ effectiveLimit,
33090
+ truncatedByBudget
33091
+ });
33092
+ }
32961
33093
  },
32962
33094
  types: defaultTypes,
32963
33095
  limit: effectiveLimit,
@@ -33001,6 +33133,7 @@ ${TRUNCATION_NOTICE}` : formatted;
33001
33133
  const message = error instanceof Error ? error.message : "Unknown error";
33002
33134
  const wrapped = new Error(`recall_memory: search failed \u2014 ${message}`);
33003
33135
  wrapped.cause = error;
33136
+ notifyMemoryCallback(callbacks?.onError, wrapped);
33004
33137
  throw wrapped;
33005
33138
  } finally {
33006
33139
  const unused = effectiveLimit - surfaced;
@@ -33234,6 +33367,18 @@ When a memory refers to one of these, reuse the EXACT existing name (same spelli
33234
33367
  ` : "";
33235
33368
  for (let i = 0; i < memories.length; i += TOPIC_EXTRACTION_BATCH_SIZE) {
33236
33369
  const batch = memories.slice(i, i + TOPIC_EXTRACTION_BATCH_SIZE);
33370
+ const start = Date.now();
33371
+ let attempts = 0;
33372
+ let failure;
33373
+ const reportBatch = (unansweredCount) => {
33374
+ notifyMemoryCallback(options.onBatch, {
33375
+ memoryIds: batch.map((m) => m.id),
33376
+ attempts,
33377
+ durationMs: Date.now() - start,
33378
+ unansweredCount,
33379
+ ...failure && { failure }
33380
+ });
33381
+ };
33237
33382
  const rows = [];
33238
33383
  for (const m of batch) {
33239
33384
  const content = m.content.slice(0, MAX_CHARS_PER_MEMORY).replace(/\s+/g, " ").trim();
@@ -33255,6 +33400,12 @@ ${listing}
33255
33400
 
33256
33401
  List each memory's named entities.`,
33257
33402
  tag: "memory/topics",
33403
+ onAttempt: (attempt) => {
33404
+ attempts = attempt.attempt;
33405
+ },
33406
+ onFailure: (value) => {
33407
+ failure = value;
33408
+ },
33258
33409
  extra: { max_completion_tokens: MAX_COMPLETION_TOKENS },
33259
33410
  ...options.fetchFn && { fetchFn: options.fetchFn },
33260
33411
  ...options.maxAttempts !== void 0 && { maxAttempts: options.maxAttempts },
@@ -33263,6 +33414,7 @@ List each memory's named entities.`,
33263
33414
  ...options.backoffMs && { backoffMs: options.backoffMs }
33264
33415
  });
33265
33416
  if (parsed === null) {
33417
+ reportBatch(batch.length);
33266
33418
  getLogger().warn(
33267
33419
  `[memory/topics] batch of ${batch.length} failed after retries \u2014 will retry next sweep`
33268
33420
  );
@@ -33271,6 +33423,7 @@ List each memory's named entities.`,
33271
33423
  const validIds = new Set(batch.map((m) => m.id));
33272
33424
  const byId = parseTopicResponse(parsed, validIds);
33273
33425
  if (byId === null) {
33426
+ reportBatch(batch.length);
33274
33427
  getLogger().warn(
33275
33428
  `[memory/topics] batch of ${batch.length} returned an unrecognized shape \u2014 will retry next sweep`
33276
33429
  );
@@ -33283,6 +33436,7 @@ List each memory's named entities.`,
33283
33436
  }
33284
33437
  out.set(m.id, redactor ? restoreEntities(entities, redactor) : entities);
33285
33438
  }
33439
+ reportBatch(batch.filter((m) => !byId.has(m.id)).length);
33286
33440
  }
33287
33441
  return out;
33288
33442
  }
@@ -33321,6 +33475,7 @@ function isDegradedTopicSkip(reason) {
33321
33475
  return DEGRADED_TOPIC_SKIP[reason] === true;
33322
33476
  }
33323
33477
  async function extractAndLinkEntitiesForMemoriesOp(ctx, memoryIds, options) {
33478
+ const start = Date.now();
33324
33479
  const entityCtx = ctx.entityCtx;
33325
33480
  if (!entityCtx) {
33326
33481
  throw new Error("extractAndLinkEntitiesForMemoriesOp requires ctx.entityCtx");
@@ -33361,7 +33516,18 @@ async function extractAndLinkEntitiesForMemoriesOp(ctx, memoryIds, options) {
33361
33516
  skip(id, "unreadable");
33362
33517
  }
33363
33518
  }
33364
- const entitiesByMemory = await extractEntitiesForMemories(inputs, options);
33519
+ const batches = [];
33520
+ const llmFailures = /* @__PURE__ */ new Map();
33521
+ const entitiesByMemory = await extractEntitiesForMemories(inputs, {
33522
+ ...options,
33523
+ onBatch: (batch) => {
33524
+ batches.push(batch);
33525
+ if (batch.failure) {
33526
+ for (const id of batch.memoryIds) llmFailures.set(id, batch.failure);
33527
+ }
33528
+ notifyMemoryCallback(options.onBatch, batch);
33529
+ }
33530
+ });
33365
33531
  const toStamp = [];
33366
33532
  for (const input of inputs) {
33367
33533
  const entities = entitiesByMemory.get(input.id);
@@ -33392,7 +33558,16 @@ async function extractAndLinkEntitiesForMemoriesOp(ctx, memoryIds, options) {
33392
33558
  entitiesByMemory.delete(id);
33393
33559
  }
33394
33560
  }
33395
- return { entitiesByMemory, stampedIds, skippedIds, skippedReasons };
33561
+ return {
33562
+ entitiesByMemory,
33563
+ stampedIds,
33564
+ skippedIds,
33565
+ skippedReasons,
33566
+ batchCount: batches.length,
33567
+ durationMs: Date.now() - start,
33568
+ batches,
33569
+ llmFailures
33570
+ };
33396
33571
  }
33397
33572
 
33398
33573
  // src/lib/memory/store/local.ts
@@ -33547,7 +33722,7 @@ function createLocalMemoryStore(options) {
33547
33722
  search: (query, searchOptions) => searchVaultMemoriesWithSize(
33548
33723
  query,
33549
33724
  vaultCtx,
33550
- options.embeddingOptions,
33725
+ { ...options.embeddingOptions, taskType: "memory_recall_embed" },
33551
33726
  vaultCache,
33552
33727
  searchOptions
33553
33728
  ),
@@ -35201,9 +35376,13 @@ function formatVaultHits(hits) {
35201
35376
  return hits.map((h, i) => `[${i + 1}] (id: ${h.id}, similarity: ${h.score.toFixed(2)})
35202
35377
  ${h.content}`).join("\n\n");
35203
35378
  }
35204
- function createMemoryVaultSearchTool(vaultCtx, embeddingOptions, cache, searchOptions) {
35379
+ function createMemoryVaultSearchTool(vaultCtx, embeddingOptions, cache, searchOptions, callbacks) {
35205
35380
  const limit = searchOptions?.limit ?? 5;
35206
35381
  const minSimilarity = searchOptions?.minSimilarity ?? 0.1;
35382
+ const recallEmbeddingOptions = {
35383
+ ...embeddingOptions,
35384
+ taskType: "memory_recall_embed"
35385
+ };
35207
35386
  const tuningForward = {
35208
35387
  ...searchOptions?.rerankTopN !== void 0 && { rerankTopN: searchOptions.rerankTopN },
35209
35388
  ...searchOptions?.ceWeight !== void 0 && { ceWeight: searchOptions.ceWeight },
@@ -35257,6 +35436,7 @@ function createMemoryVaultSearchTool(vaultCtx, embeddingOptions, cache, searchOp
35257
35436
  const requestLimit = Number.isFinite(rawLimit) ? Math.min(Math.max(Math.floor(rawLimit), 1), RECALL_MAX_LIMIT) : limit;
35258
35437
  const argFolderId = args.folder_id;
35259
35438
  if (!query || typeof query !== "string") {
35439
+ notifyMemoryCallback(callbacks?.onError, new Error("A search query is required."));
35260
35440
  return "Error: A search query is required.";
35261
35441
  }
35262
35442
  try {
@@ -35264,7 +35444,11 @@ function createMemoryVaultSearchTool(vaultCtx, embeddingOptions, cache, searchOp
35264
35444
  const budget = wantsDecompose ? "high" : searchOptions?.rerank ? "mid" : "low";
35265
35445
  const folderId = searchOptions?.folderId ?? argFolderId;
35266
35446
  if (searchOptions?.useFusion === false) {
35447
+ let candidateCount;
35267
35448
  const legacyOptions = {
35449
+ onCandidateCount: (count) => {
35450
+ candidateCount = count;
35451
+ },
35268
35452
  limit: requestLimit,
35269
35453
  minSimilarity,
35270
35454
  useFusion: false,
@@ -35272,13 +35456,38 @@ function createMemoryVaultSearchTool(vaultCtx, embeddingOptions, cache, searchOp
35272
35456
  ...folderId !== void 0 && { folderId },
35273
35457
  ...searchOptions?.scopes && { scopes: searchOptions.scopes }
35274
35458
  };
35275
- const { results: legacy, embeddingsUnavailable } = "retain" in vaultCtx ? await vaultCtx.factSource.search(query, legacyOptions) : await searchVaultMemoriesWithSize(
35459
+ const start = Date.now();
35460
+ const outcome = "retain" in vaultCtx ? await vaultCtx.factSource.search(query, legacyOptions) : await searchVaultMemoriesWithSize(
35276
35461
  query,
35277
35462
  vaultCtx,
35278
- embeddingOptions,
35463
+ recallEmbeddingOptions,
35279
35464
  cache,
35280
35465
  legacyOptions
35281
35466
  );
35467
+ const { results: legacy, embeddingsUnavailable, vaultSize } = outcome;
35468
+ const diagnostics = factRecallDiagnostics(
35469
+ legacy.map((m) => m.similarity),
35470
+ Date.now() - start,
35471
+ {
35472
+ vaultSize,
35473
+ minScore: minSimilarity,
35474
+ embeddingsUnavailable,
35475
+ candidateCount,
35476
+ truncated: candidateCount !== void 0 && candidateCount > requestLimit,
35477
+ decryptLast: outcome.decryptLast,
35478
+ vaultRowsDecrypted: outcome.rowsDecrypted,
35479
+ vaultRowsEmbedded: outcome.rowsEmbedded,
35480
+ queryEmbedMs: outcome.queryEmbedMs
35481
+ }
35482
+ );
35483
+ notifyMemoryCallback(callbacks?.onDiagnostics, diagnostics);
35484
+ if (diagnostics.truncated) {
35485
+ notifyMemoryCallback(callbacks?.onTruncated, {
35486
+ requestedLimit: requestLimit,
35487
+ effectiveLimit: requestLimit,
35488
+ truncatedByBudget: false
35489
+ });
35490
+ }
35282
35491
  if (legacy.length === 0) {
35283
35492
  return embeddingsUnavailable ? EMBEDDINGS_DEGRADED_EMPTY_NO_LEXICAL : "No relevant memories found in the vault.";
35284
35493
  }
@@ -35300,6 +35509,14 @@ function createMemoryVaultSearchTool(vaultCtx, embeddingOptions, cache, searchOp
35300
35509
  {
35301
35510
  onDiagnostics: (d) => {
35302
35511
  recallDegraded = d.degraded;
35512
+ notifyMemoryCallback(callbacks?.onDiagnostics, d);
35513
+ if (d.truncated) {
35514
+ notifyMemoryCallback(callbacks?.onTruncated, {
35515
+ requestedLimit: requestLimit,
35516
+ effectiveLimit: requestLimit,
35517
+ truncatedByBudget: false
35518
+ });
35519
+ }
35303
35520
  },
35304
35521
  types: ["fact"],
35305
35522
  limit: requestLimit,
@@ -35332,6 +35549,10 @@ function createMemoryVaultSearchTool(vaultCtx, embeddingOptions, cache, searchOp
35332
35549
 
35333
35550
  ${formatted}`;
35334
35551
  } catch (error) {
35552
+ notifyMemoryCallback(
35553
+ callbacks?.onError,
35554
+ error instanceof Error ? error : new Error(String(error))
35555
+ );
35335
35556
  const message = error instanceof Error ? error.message : "Unknown error";
35336
35557
  return `Error searching vault: ${message}`;
35337
35558
  }
@@ -374,6 +374,7 @@ interface RecallDiagnostics {
374
374
  * happened.
375
375
  */
376
376
  truncated: boolean;
377
+ truncatedByBudget?: boolean;
377
378
  /** Memory ids the W5 graph (entity) side lane contributed to the fusion. */
378
379
  graphLaneCount: number;
379
380
  /** Memory ids the W6 temporal side lane contributed to the fusion. */
@@ -374,6 +374,7 @@ interface RecallDiagnostics {
374
374
  * happened.
375
375
  */
376
376
  truncated: boolean;
377
+ truncatedByBudget?: boolean;
377
378
  /** Memory ids the W5 graph (entity) side lane contributed to the fusion. */
378
379
  graphLaneCount: number;
379
380
  /** Memory ids the W6 temporal side lane contributed to the fusion. */