@anuma/sdk 6.43.0 → 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.
@@ -19,7 +19,7 @@ import {
19
19
  taskTypeHeader,
20
20
  validateEndpointOverride,
21
21
  withInternalFlowMarker
22
- } from "./chunk-CERRBFF5.mjs";
22
+ } from "./chunk-TGLAOFVA.mjs";
23
23
  import {
24
24
  BASE_URL,
25
25
  MCP_R2_DOMAIN,
@@ -5387,7 +5387,10 @@ async function embedOne(text14, options, outer) {
5387
5387
  const cached = cache.get(text14);
5388
5388
  if (cached) return Array.from(cached);
5389
5389
  }
5390
- const headers = await resolveAuthHeaders(options, outer);
5390
+ const headers = {
5391
+ ...await resolveAuthHeaders(options, outer),
5392
+ ...taskTypeHeader(options.taskType)
5393
+ };
5391
5394
  const response = await withEmbeddingRetry(
5392
5395
  () => withDeadline(
5393
5396
  (signal) => postApiV1Embeddings({
@@ -5482,7 +5485,10 @@ async function embedMany(texts, options, outer) {
5482
5485
  if (uncachedTexts.length === 0) {
5483
5486
  return results;
5484
5487
  }
5485
- const headers = await resolveAuthHeaders(options, outer);
5488
+ const headers = {
5489
+ ...await resolveAuthHeaders(options, outer),
5490
+ ...taskTypeHeader(options.taskType)
5491
+ };
5486
5492
  if (outer?.aborted) throw outer.reason;
5487
5493
  const embeddingModel = model ?? DEFAULT_API_EMBEDDING_MODEL;
5488
5494
  let newEmbeddings;
@@ -16643,6 +16649,50 @@ function applyMMR(candidates, k, lambda = 0.5) {
16643
16649
  return selected;
16644
16650
  }
16645
16651
 
16652
+ // src/lib/memory/recallTelemetry.ts
16653
+ function notifyMemoryCallback(callback, value) {
16654
+ try {
16655
+ callback?.(value);
16656
+ } catch {
16657
+ return;
16658
+ }
16659
+ }
16660
+ function factRecallDiagnostics(scores, elapsedMs, options = {}) {
16661
+ return {
16662
+ usedBudget: "low",
16663
+ reranked: false,
16664
+ candidateCount: options.candidateCount ?? scores.length,
16665
+ ...options.vaultSize !== void 0 && { vaultSize: options.vaultSize },
16666
+ ...options.decryptLast !== void 0 && { decryptLast: options.decryptLast },
16667
+ ...options.vaultRowsDecrypted !== void 0 && {
16668
+ vaultRowsDecrypted: options.vaultRowsDecrypted
16669
+ },
16670
+ ...options.vaultRowsEmbedded !== void 0 && {
16671
+ vaultRowsEmbedded: options.vaultRowsEmbedded
16672
+ },
16673
+ factCount: scores.length,
16674
+ chunkCount: 0,
16675
+ admittedCount: scores.length,
16676
+ topScore: scores.length ? Math.max(...scores) : -1,
16677
+ lowestAdmittedScore: scores.length ? Math.min(...scores) : -1,
16678
+ minScoreApplied: options.minScore ?? -1,
16679
+ truncated: options.truncated ?? false,
16680
+ graphLaneCount: 0,
16681
+ temporalLaneCount: 0,
16682
+ emptyReason: scores.length ? "" : options.vaultSize === 0 ? "vault-empty" : "no-candidates",
16683
+ timings: {
16684
+ total: elapsedMs,
16685
+ prep: 0,
16686
+ factLane: elapsedMs,
16687
+ rerank: 0,
16688
+ queryEmbed: options.queryEmbedMs ?? 0,
16689
+ chunkLane: 0,
16690
+ fuse: 0
16691
+ },
16692
+ degraded: options.embeddingsUnavailable ? ["embeddings-unavailable"] : []
16693
+ };
16694
+ }
16695
+
16646
16696
  // src/lib/memory/recency.ts
16647
16697
  var DAY_MS2 = 1e3 * 60 * 60 * 24;
16648
16698
  var DEFAULT_PER_YEAR_DECAY = 0.2;
@@ -17332,6 +17382,7 @@ function rankVaultMemories(_query, queryEmbedding, items, options) {
17332
17382
  }
17333
17383
  filtered.sort((a, b) => b.similarity - a.similarity);
17334
17384
  }
17385
+ notifyMemoryCallback(options?.onCandidateCount, filtered.length);
17335
17386
  const itemById = new Map(items.map((i) => [i.id, i]));
17336
17387
  return filtered.slice(0, limit).map((r) => resultFromItem(itemById.get(r.uniqueId), r.similarity));
17337
17388
  }
@@ -18314,6 +18365,7 @@ async function rankPreparedVaultCandidates(query, prepared, embeddingOptions, se
18314
18365
  const results = rankVaultMemories(query, queryEmbedding, embeddedItems, {
18315
18366
  limit,
18316
18367
  minSimilarity,
18368
+ onCandidateCount: searchOptions?.onCandidateCount,
18317
18369
  ...searchOptions?.supersessionBoost !== void 0 && {
18318
18370
  supersessionBoost: searchOptions.supersessionBoost
18319
18371
  },
@@ -18414,16 +18466,20 @@ var DEFAULT_SCOPE = "private";
18414
18466
  var DEFAULT_CONSOLIDATE_THRESHOLD = 0.55;
18415
18467
  var DEFAULT_CONSOLIDATE_TOP_K = 20;
18416
18468
  async function retain(content, ctx, options = {}) {
18469
+ const embeddingOptions = {
18470
+ ...ctx.embeddingOptions,
18471
+ taskType: "memory_retain_embed"
18472
+ };
18417
18473
  return retainWithPersistence(
18418
18474
  content,
18419
18475
  {
18420
- embeddingOptions: ctx.embeddingOptions,
18476
+ embeddingOptions,
18421
18477
  vaultCache: ctx.vaultCache,
18422
18478
  persistence: {
18423
18479
  prepare: (query, searchOptions) => prepareVaultCandidates(
18424
18480
  query,
18425
18481
  ctx.vaultCtx,
18426
- ctx.embeddingOptions,
18482
+ embeddingOptions,
18427
18483
  ctx.vaultCache,
18428
18484
  searchOptions
18429
18485
  ),
@@ -18707,7 +18763,7 @@ async function tryConsolidate(trimmed, ctx, options, prepared) {
18707
18763
  content: m.content,
18708
18764
  similarity: m.similarity
18709
18765
  }));
18710
- const { consolidateMemory: doConsolidate } = await import("./consolidate-IW7EKXHN.mjs");
18766
+ const { consolidateMemory: doConsolidate } = await import("./consolidate-ZEINV2LX.mjs");
18711
18767
  const decision = await doConsolidate(trimmed, candidates, consolidateOptions);
18712
18768
  if (decision.action === "create") return decision.fallbackReason ? null : { create: true };
18713
18769
  if (decision.action === "supersede" && decision.content) {
@@ -19571,7 +19627,17 @@ function parseVerdict(parsed) {
19571
19627
  }
19572
19628
 
19573
19629
  // src/lib/memory/decayWorker.ts
19574
- var EMPTY_RESULT = { archived: 0, deleted: 0, scanned: 0 };
19630
+ function emptyResult() {
19631
+ return {
19632
+ archived: 0,
19633
+ deleted: 0,
19634
+ scanned: 0,
19635
+ ruleDriven: { archived: 0, deleted: 0 },
19636
+ classifierDriven: { archived: 0, deleted: 0 },
19637
+ classifierCalls: 0,
19638
+ classifierCapHits: 0
19639
+ };
19640
+ }
19575
19641
  var DEFAULT_MAX_CLASSIFIER_CALLS_PER_SWEEP = 20;
19576
19642
  function resolveNow(now) {
19577
19643
  if (typeof now === "function") return now();
@@ -19610,42 +19676,45 @@ function createDecaySweeper(options) {
19610
19676
  let disposed = false;
19611
19677
  async function verdictFor(input, now, sweep) {
19612
19678
  const ruleVerdict = classifyDecay(input, now, policy);
19613
- if (ruleVerdict !== "keep") return ruleVerdict;
19614
- if (!classifier || !isBorderline(input)) return ruleVerdict;
19679
+ const rule = { verdict: ruleVerdict, source: "ruleDriven" };
19680
+ if (ruleVerdict !== "keep") return rule;
19681
+ if (!classifier || !isBorderline(input)) return rule;
19615
19682
  if (input.id) {
19616
19683
  const cached = classifierCache.get(input.id);
19617
- if (cached && cached.activityAt === lastActivityAt(input)) return cached.verdict;
19684
+ if (cached && cached.activityAt === lastActivityAt(input))
19685
+ return { verdict: cached.verdict, source: "classifierDriven" };
19618
19686
  }
19619
19687
  if (sweep.classifierCalls >= maxClassifierCalls) {
19688
+ sweep.classifierCapHits++;
19620
19689
  if (!sweep.ceilingLogged) {
19621
19690
  sweep.ceilingLogged = true;
19622
19691
  getLogger().warn(
19623
19692
  `[memory/decay] classifier per-sweep ceiling (${maxClassifierCalls}) reached; remaining borderline rows use the rule verdict this sweep`
19624
19693
  );
19625
19694
  }
19626
- return ruleVerdict;
19695
+ return rule;
19627
19696
  }
19628
19697
  sweep.classifierCalls++;
19629
19698
  try {
19630
19699
  const verdict = await classifier.classify(input, ruleVerdict, now);
19631
19700
  if (input.id) classifierCache.set(input.id, { activityAt: lastActivityAt(input), verdict });
19632
- return verdict;
19701
+ return { verdict, source: "classifierDriven" };
19633
19702
  } catch (err) {
19634
19703
  getLogger().warn(
19635
19704
  `[memory/decay] classifier failed; falling back to rule verdict: ${err instanceof Error ? err.message : String(err)}`
19636
19705
  );
19637
- return ruleVerdict;
19706
+ return rule;
19638
19707
  }
19639
19708
  }
19640
19709
  async function runSweep() {
19641
- if (disposed) return { ...EMPTY_RESULT };
19710
+ if (disposed) return emptyResult();
19642
19711
  const now = resolveNow(options.now);
19643
19712
  let candidates;
19644
19713
  try {
19645
19714
  candidates = await getDecayCandidatesRawOp(vaultCtx);
19646
19715
  } catch (err) {
19647
19716
  onError?.(err instanceof Error ? err : new Error(String(err)));
19648
- return { ...EMPTY_RESULT };
19717
+ return emptyResult();
19649
19718
  }
19650
19719
  if (classifierCache.size > 0) {
19651
19720
  const liveIds = new Set(candidates.map((c) => c.uniqueId));
@@ -19653,33 +19722,40 @@ function createDecaySweeper(options) {
19653
19722
  if (!liveIds.has(id)) classifierCache.delete(id);
19654
19723
  }
19655
19724
  }
19656
- const sweep = { classifierCalls: 0, ceilingLogged: false };
19725
+ const sweep = { classifierCalls: 0, classifierCapHits: 0, ceilingLogged: false };
19657
19726
  const toArchive = [];
19658
19727
  const toDelete = [];
19659
19728
  for (const c of candidates) {
19660
- const verdict = await verdictFor(toDecayInput(c), now, sweep);
19661
- if (verdict === "archive") toArchive.push(c);
19662
- else if (verdict === "delete") toDelete.push(c);
19663
- }
19664
- let archived = 0;
19665
- let deleted = 0;
19729
+ const { verdict, source } = await verdictFor(toDecayInput(c), now, sweep);
19730
+ if (verdict === "archive") toArchive.push({ candidate: c, source });
19731
+ else if (verdict === "delete") toDelete.push({ candidate: c, source });
19732
+ }
19733
+ const result = emptyResult();
19734
+ result.scanned = candidates.length;
19735
+ result.classifierCalls = sweep.classifierCalls;
19736
+ result.classifierCapHits = sweep.classifierCapHits;
19666
19737
  try {
19667
- for (const c of toArchive) {
19738
+ for (const { candidate: c, source } of toArchive) {
19668
19739
  const ok = await archiveVaultMemoryOp(vaultCtx, c.uniqueId, {
19669
19740
  now,
19670
19741
  expectedUpdatedAt: c.updatedAt,
19671
19742
  expectedLastObservedAt: c.lastObservedAt ?? null
19672
19743
  });
19673
- if (ok) archived++;
19744
+ if (ok) {
19745
+ result.archived++;
19746
+ result[source].archived++;
19747
+ }
19674
19748
  }
19675
- for (const c of toDelete) {
19749
+ for (const { candidate: c, source } of toDelete) {
19676
19750
  const ok = await hardDeleteDecayedOp(vaultCtx, c.uniqueId, { hardDeleteWindowMs, now });
19677
- if (ok) deleted++;
19751
+ if (ok) {
19752
+ result.deleted++;
19753
+ result[source].deleted++;
19754
+ }
19678
19755
  }
19679
19756
  } catch (err) {
19680
19757
  onError?.(err instanceof Error ? err : new Error(String(err)));
19681
19758
  }
19682
- const result = { archived, deleted, scanned: candidates.length };
19683
19759
  onSwept?.(result);
19684
19760
  return result;
19685
19761
  }
@@ -20277,6 +20353,10 @@ async function recall(query, ctx, options = {}) {
20277
20353
  const flags = flagsForBudget(usedBudget);
20278
20354
  const normalizedFacets = normalizeSubQueries(options.subQueries);
20279
20355
  const subQueries = normalizedFacets.length >= 2 ? normalizedFacets : void 0;
20356
+ const embeddingOptions = {
20357
+ ...ctx.embeddingOptions,
20358
+ taskType: "memory_recall_embed"
20359
+ };
20280
20360
  const t0 = nowMs2();
20281
20361
  let prepMs = 0;
20282
20362
  let factLaneMs = 0;
@@ -20372,7 +20452,7 @@ async function recall(query, ctx, options = {}) {
20372
20452
  let sharedEmbedMs = 0;
20373
20453
  const [queryEmbedding, entityRanking, temporalRanking] = await Promise.all([
20374
20454
  needsChunkEmbedding ? generateEmbedding(query, {
20375
- ...ctx.embeddingOptions,
20455
+ ...embeddingOptions,
20376
20456
  totalTimeoutMs: queryEmbedTotalTimeoutMs
20377
20457
  }).finally(() => {
20378
20458
  sharedEmbedMs = nowMs2() - prepStart;
@@ -20430,7 +20510,7 @@ async function recall(query, ctx, options = {}) {
20430
20510
  } = await (ctx.factSource?.search.bind(ctx.factSource) ?? ((searchQuery, searchOptions) => searchVaultMemoriesWithSize(
20431
20511
  searchQuery,
20432
20512
  ctx.vaultCtx,
20433
- ctx.embeddingOptions,
20513
+ embeddingOptions,
20434
20514
  ctx.vaultCache,
20435
20515
  searchOptions
20436
20516
  )))(query, {
@@ -20796,7 +20876,7 @@ function isDumpQuery(query) {
20796
20876
  async function listRecentFacts(ctx, limit, toolOptions) {
20797
20877
  const store = toolOptions?.memoryStore;
20798
20878
  const vaultCtx = ctx.vaultCtx;
20799
- if (!vaultCtx && !store) return [];
20879
+ if (!vaultCtx && !store) return { memories: [], truncated: false };
20800
20880
  const read = (ids) => store ? store.list({ memoryIds: ids }) : getVaultMemoriesByIdsOp(vaultCtx, ids);
20801
20881
  const filters = {
20802
20882
  ...toolOptions?.scopes && { scopes: toolOptions.scopes },
@@ -20807,34 +20887,47 @@ async function listRecentFacts(ctx, limit, toolOptions) {
20807
20887
  ...toolOptions?.folderId !== void 0 && { folderId: toolOptions.folderId }
20808
20888
  });
20809
20889
  const readable = [];
20890
+ const readLimit = limit + 1;
20891
+ let batchLimit = limit;
20810
20892
  let start = 0;
20811
- for (let size = limit; readable.length < limit; size *= 2) {
20893
+ let projected = 0;
20894
+ let exhausted = false;
20895
+ for (let size = readLimit; readable.length < readLimit; size *= 2) {
20812
20896
  const ordered = await project(size);
20813
- for (let i = start; i < ordered.length && readable.length < limit; i += limit) {
20814
- const batch = ordered.slice(i, i + limit).map((p) => p.uniqueId);
20897
+ projected = ordered.length;
20898
+ exhausted = !store || ordered.length < size;
20899
+ for (let i = start; i < ordered.length && readable.length < readLimit; ) {
20900
+ const batch = ordered.slice(i, i + batchLimit).map((p) => p.uniqueId);
20815
20901
  const byId = new Map((await read(batch)).map((m) => [m.uniqueId, m]));
20902
+ const previousCount = readable.length;
20816
20903
  for (const id of batch) {
20817
20904
  const m = byId.get(id);
20818
20905
  if (m && !isEncrypted(m.content)) readable.push(m);
20819
- if (readable.length >= limit) break;
20906
+ if (readable.length >= readLimit) break;
20820
20907
  }
20908
+ batchLimit = readable.length > previousCount ? Math.min(limit, readLimit - readable.length) : limit;
20909
+ i += batch.length;
20821
20910
  }
20822
- if (!store || ordered.length < size) break;
20911
+ if (exhausted) break;
20823
20912
  start = ordered.length;
20824
20913
  }
20825
- return readable.map((m) => ({
20826
- id: m.uniqueId,
20827
- kind: "fact",
20828
- content: m.content,
20829
- score: 0,
20830
- folderId: m.folderId,
20831
- eventTimeStart: m.eventTimeStart,
20832
- eventTimeEnd: m.eventTimeEnd,
20833
- eventTimeKind: m.eventTimeKind,
20834
- factType: m.factType,
20835
- createdAt: m.createdAt,
20836
- updatedAt: m.updatedAt
20837
- }));
20914
+ return {
20915
+ memories: readable.slice(0, limit).map((m) => ({
20916
+ id: m.uniqueId,
20917
+ kind: "fact",
20918
+ content: m.content,
20919
+ score: 0,
20920
+ folderId: m.folderId,
20921
+ eventTimeStart: m.eventTimeStart,
20922
+ eventTimeEnd: m.eventTimeEnd,
20923
+ eventTimeKind: m.eventTimeKind,
20924
+ factType: m.factType,
20925
+ createdAt: m.createdAt,
20926
+ updatedAt: m.updatedAt
20927
+ })),
20928
+ ...exhausted && { vaultSize: projected },
20929
+ truncated: readable.length > limit
20930
+ };
20838
20931
  }
20839
20932
  function createRecallTool(ctx, toolOptions, callbacks) {
20840
20933
  if (toolOptions?.memoryStore && toolOptions.folderId !== void 0)
@@ -20873,7 +20966,11 @@ function createRecallTool(ctx, toolOptions, callbacks) {
20873
20966
  },
20874
20967
  executor: async (args) => {
20875
20968
  if (typeof args.query !== "string" || args.query.length === 0) {
20876
- throw new Error("recall_memory: `query` is required and must be a non-empty string.");
20969
+ const error = new Error(
20970
+ "recall_memory: `query` is required and must be a non-empty string."
20971
+ );
20972
+ notifyMemoryCallback(callbacks?.onError, error);
20973
+ throw error;
20877
20974
  }
20878
20975
  const query = args.query;
20879
20976
  const now = Date.now();
@@ -20901,10 +20998,17 @@ function createRecallTool(ctx, toolOptions, callbacks) {
20901
20998
  RECALL_MAX_MEMORIES_PER_CONVERSATION - conversationMemories
20902
20999
  );
20903
21000
  const effectiveLimit = Math.min(requestLimit, remainingTurnBudget, remainingConvBudget);
21001
+ const truncatedByBudget = effectiveLimit < requestLimit;
21002
+ if (truncatedByBudget) {
21003
+ notifyMemoryCallback(callbacks?.onTruncated, {
21004
+ requestedLimit: requestLimit,
21005
+ effectiveLimit,
21006
+ truncatedByBudget
21007
+ });
21008
+ }
20904
21009
  if (effectiveLimit <= 0) {
20905
21010
  return VOLUME_LIMIT_NOTICE;
20906
21011
  }
20907
- const truncatedByBudget = effectiveLimit < requestLimit;
20908
21012
  turnMemories += effectiveLimit;
20909
21013
  conversationMemories += effectiveLimit;
20910
21014
  let surfaced = 0;
@@ -20913,7 +21017,27 @@ function createRecallTool(ctx, toolOptions, callbacks) {
20913
21017
  const wantsRecent = args.sort === "recent" && defaultTypes.includes("fact") && (ctx.vaultCtx !== void 0 || toolOptions?.memoryStore !== void 0);
20914
21018
  let result;
20915
21019
  if (wantsRecent) {
20916
- result = { memories: await listRecentFacts(ctx, effectiveLimit, toolOptions) };
21020
+ const start = Date.now();
21021
+ const recent = await listRecentFacts(ctx, effectiveLimit, toolOptions);
21022
+ result = recent;
21023
+ notifyMemoryCallback(callbacks?.onDiagnostics, {
21024
+ ...factRecallDiagnostics(
21025
+ result.memories.map((m) => m.score),
21026
+ Date.now() - start,
21027
+ {
21028
+ truncated: recent.truncated,
21029
+ ...recent.vaultSize !== void 0 && { vaultSize: recent.vaultSize }
21030
+ }
21031
+ ),
21032
+ truncatedByBudget
21033
+ });
21034
+ if (recent.truncated && !truncatedByBudget) {
21035
+ notifyMemoryCallback(callbacks?.onTruncated, {
21036
+ requestedLimit: requestLimit,
21037
+ effectiveLimit,
21038
+ truncatedByBudget
21039
+ });
21040
+ }
20917
21041
  } else {
20918
21042
  let subQueries;
20919
21043
  if (defaultBudget === "high" && toolOptions?.decomposeOptions) {
@@ -20925,6 +21049,14 @@ function createRecallTool(ctx, toolOptions, callbacks) {
20925
21049
  const recallOpts = {
20926
21050
  onDiagnostics: (d) => {
20927
21051
  recallDegraded = d.degraded;
21052
+ notifyMemoryCallback(callbacks?.onDiagnostics, { ...d, truncatedByBudget });
21053
+ if (d.truncated && !truncatedByBudget) {
21054
+ notifyMemoryCallback(callbacks?.onTruncated, {
21055
+ requestedLimit: requestLimit,
21056
+ effectiveLimit,
21057
+ truncatedByBudget
21058
+ });
21059
+ }
20928
21060
  },
20929
21061
  types: defaultTypes,
20930
21062
  limit: effectiveLimit,
@@ -20968,6 +21100,7 @@ ${TRUNCATION_NOTICE}` : formatted;
20968
21100
  const message = error instanceof Error ? error.message : "Unknown error";
20969
21101
  const wrapped = new Error(`recall_memory: search failed \u2014 ${message}`);
20970
21102
  wrapped.cause = error;
21103
+ notifyMemoryCallback(callbacks?.onError, wrapped);
20971
21104
  throw wrapped;
20972
21105
  } finally {
20973
21106
  const unused = effectiveLimit - surfaced;
@@ -21201,6 +21334,18 @@ When a memory refers to one of these, reuse the EXACT existing name (same spelli
21201
21334
  ` : "";
21202
21335
  for (let i = 0; i < memories.length; i += TOPIC_EXTRACTION_BATCH_SIZE) {
21203
21336
  const batch = memories.slice(i, i + TOPIC_EXTRACTION_BATCH_SIZE);
21337
+ const start = Date.now();
21338
+ let attempts = 0;
21339
+ let failure;
21340
+ const reportBatch = (unansweredCount) => {
21341
+ notifyMemoryCallback(options.onBatch, {
21342
+ memoryIds: batch.map((m) => m.id),
21343
+ attempts,
21344
+ durationMs: Date.now() - start,
21345
+ unansweredCount,
21346
+ ...failure && { failure }
21347
+ });
21348
+ };
21204
21349
  const rows = [];
21205
21350
  for (const m of batch) {
21206
21351
  const content = m.content.slice(0, MAX_CHARS_PER_MEMORY).replace(/\s+/g, " ").trim();
@@ -21222,6 +21367,12 @@ ${listing}
21222
21367
 
21223
21368
  List each memory's named entities.`,
21224
21369
  tag: "memory/topics",
21370
+ onAttempt: (attempt) => {
21371
+ attempts = attempt.attempt;
21372
+ },
21373
+ onFailure: (value) => {
21374
+ failure = value;
21375
+ },
21225
21376
  extra: { max_completion_tokens: MAX_COMPLETION_TOKENS },
21226
21377
  ...options.fetchFn && { fetchFn: options.fetchFn },
21227
21378
  ...options.maxAttempts !== void 0 && { maxAttempts: options.maxAttempts },
@@ -21230,6 +21381,7 @@ List each memory's named entities.`,
21230
21381
  ...options.backoffMs && { backoffMs: options.backoffMs }
21231
21382
  });
21232
21383
  if (parsed === null) {
21384
+ reportBatch(batch.length);
21233
21385
  getLogger().warn(
21234
21386
  `[memory/topics] batch of ${batch.length} failed after retries \u2014 will retry next sweep`
21235
21387
  );
@@ -21238,6 +21390,7 @@ List each memory's named entities.`,
21238
21390
  const validIds = new Set(batch.map((m) => m.id));
21239
21391
  const byId = parseTopicResponse(parsed, validIds);
21240
21392
  if (byId === null) {
21393
+ reportBatch(batch.length);
21241
21394
  getLogger().warn(
21242
21395
  `[memory/topics] batch of ${batch.length} returned an unrecognized shape \u2014 will retry next sweep`
21243
21396
  );
@@ -21250,6 +21403,7 @@ List each memory's named entities.`,
21250
21403
  }
21251
21404
  out.set(m.id, redactor ? restoreEntities(entities, redactor) : entities);
21252
21405
  }
21406
+ reportBatch(batch.filter((m) => !byId.has(m.id)).length);
21253
21407
  }
21254
21408
  return out;
21255
21409
  }
@@ -21288,6 +21442,7 @@ function isDegradedTopicSkip(reason) {
21288
21442
  return DEGRADED_TOPIC_SKIP[reason] === true;
21289
21443
  }
21290
21444
  async function extractAndLinkEntitiesForMemoriesOp(ctx, memoryIds, options) {
21445
+ const start = Date.now();
21291
21446
  const entityCtx = ctx.entityCtx;
21292
21447
  if (!entityCtx) {
21293
21448
  throw new Error("extractAndLinkEntitiesForMemoriesOp requires ctx.entityCtx");
@@ -21328,7 +21483,18 @@ async function extractAndLinkEntitiesForMemoriesOp(ctx, memoryIds, options) {
21328
21483
  skip(id, "unreadable");
21329
21484
  }
21330
21485
  }
21331
- const entitiesByMemory = await extractEntitiesForMemories(inputs, options);
21486
+ const batches = [];
21487
+ const llmFailures = /* @__PURE__ */ new Map();
21488
+ const entitiesByMemory = await extractEntitiesForMemories(inputs, {
21489
+ ...options,
21490
+ onBatch: (batch) => {
21491
+ batches.push(batch);
21492
+ if (batch.failure) {
21493
+ for (const id of batch.memoryIds) llmFailures.set(id, batch.failure);
21494
+ }
21495
+ notifyMemoryCallback(options.onBatch, batch);
21496
+ }
21497
+ });
21332
21498
  const toStamp = [];
21333
21499
  for (const input of inputs) {
21334
21500
  const entities = entitiesByMemory.get(input.id);
@@ -21359,7 +21525,16 @@ async function extractAndLinkEntitiesForMemoriesOp(ctx, memoryIds, options) {
21359
21525
  entitiesByMemory.delete(id);
21360
21526
  }
21361
21527
  }
21362
- return { entitiesByMemory, stampedIds, skippedIds, skippedReasons };
21528
+ return {
21529
+ entitiesByMemory,
21530
+ stampedIds,
21531
+ skippedIds,
21532
+ skippedReasons,
21533
+ batchCount: batches.length,
21534
+ durationMs: Date.now() - start,
21535
+ batches,
21536
+ llmFailures
21537
+ };
21363
21538
  }
21364
21539
 
21365
21540
  // src/lib/memory/store/local.ts
@@ -21514,7 +21689,7 @@ function createLocalMemoryStore(options) {
21514
21689
  search: (query, searchOptions) => searchVaultMemoriesWithSize(
21515
21690
  query,
21516
21691
  vaultCtx,
21517
- options.embeddingOptions,
21692
+ { ...options.embeddingOptions, taskType: "memory_recall_embed" },
21518
21693
  vaultCache,
21519
21694
  searchOptions
21520
21695
  ),
@@ -22176,6 +22351,7 @@ function createRemoteMemoryPipeline(options) {
22176
22351
  const budgetMs = search.queryEmbedTotalTimeoutMs ?? 8e3;
22177
22352
  const embed = (texts) => generateEmbeddings(texts, {
22178
22353
  ...embeddingOptions,
22354
+ taskType: mode === "retain" ? "memory_retain_embed" : "memory_recall_embed",
22179
22355
  totalTimeoutMs: budgetMs
22180
22356
  });
22181
22357
  const embedFacets = async () => {
@@ -22424,7 +22600,7 @@ function createRemoteMemoryPipeline(options) {
22424
22600
  const ranked = await rankPreparedVaultCandidates(
22425
22601
  text14,
22426
22602
  prepared,
22427
- embeddingOptions,
22603
+ { ...embeddingOptions, taskType: "memory_recall_embed" },
22428
22604
  searchOptions
22429
22605
  );
22430
22606
  return {
@@ -22446,7 +22622,7 @@ function createRemoteMemoryPipeline(options) {
22446
22622
  content,
22447
22623
  {
22448
22624
  persistence: port,
22449
- embeddingOptions,
22625
+ embeddingOptions: { ...embeddingOptions, taskType: "memory_retain_embed" },
22450
22626
  vaultCache: /* @__PURE__ */ new Map(),
22451
22627
  normalizeEmbedding: storedVector
22452
22628
  },
@@ -23174,7 +23350,8 @@ function createRemoteMemoryStore(options) {
23174
23350
  };
23175
23351
  },
23176
23352
  extractTopics: async (memoryIds, extractOptions) => {
23177
- const extractedAt = extractOptions.now ?? Date.now();
23353
+ const start = Date.now();
23354
+ const extractedAt = extractOptions.now ?? start;
23178
23355
  const skippedIds = [];
23179
23356
  const skippedReasons = /* @__PURE__ */ new Map();
23180
23357
  const skip = (id, reason) => {
@@ -23192,7 +23369,18 @@ function createRemoteMemoryStore(options) {
23192
23369
  else if (memory.is_deleted || memory.topics_user_managed) skip(id, "excluded");
23193
23370
  else inputs.push({ id, content: memory.content });
23194
23371
  }
23195
- const entitiesByMemory = await extractEntitiesForMemories(inputs, extractOptions);
23372
+ const batches = [];
23373
+ const llmFailures = /* @__PURE__ */ new Map();
23374
+ const entitiesByMemory = await extractEntitiesForMemories(inputs, {
23375
+ ...extractOptions,
23376
+ onBatch: (batch) => {
23377
+ batches.push(batch);
23378
+ if (batch.failure) {
23379
+ for (const id of batch.memoryIds) llmFailures.set(id, batch.failure);
23380
+ }
23381
+ notifyMemoryCallback(extractOptions.onBatch, batch);
23382
+ }
23383
+ });
23196
23384
  const answered = /* @__PURE__ */ new Map();
23197
23385
  for (const input of inputs) {
23198
23386
  const entities = entitiesByMemory.get(input.id);
@@ -23241,7 +23429,16 @@ function createRemoteMemoryStore(options) {
23241
23429
  getLogger().warn("[memory/remote-store] Failed to write extracted topics:", error);
23242
23430
  for (const id of answered.keys()) fail(id, "link-failed");
23243
23431
  }
23244
- return { entitiesByMemory, stampedIds, skippedIds, skippedReasons };
23432
+ return {
23433
+ entitiesByMemory,
23434
+ stampedIds,
23435
+ skippedIds,
23436
+ skippedReasons,
23437
+ batchCount: batches.length,
23438
+ durationMs: Date.now() - start,
23439
+ batches,
23440
+ llmFailures
23441
+ };
23245
23442
  },
23246
23443
  stampTopicsExtracted: async (memoryIds, extractedAt, version = TOPICS_EXTRACTION_VERSION) => {
23247
23444
  const ids = [...new Set(memoryIds)];
@@ -25252,9 +25449,13 @@ function formatVaultHits(hits) {
25252
25449
  return hits.map((h, i) => `[${i + 1}] (id: ${h.id}, similarity: ${h.score.toFixed(2)})
25253
25450
  ${h.content}`).join("\n\n");
25254
25451
  }
25255
- function createMemoryVaultSearchTool(vaultCtx, embeddingOptions, cache, searchOptions) {
25452
+ function createMemoryVaultSearchTool(vaultCtx, embeddingOptions, cache, searchOptions, callbacks) {
25256
25453
  const limit = searchOptions?.limit ?? 5;
25257
25454
  const minSimilarity = searchOptions?.minSimilarity ?? 0.1;
25455
+ const recallEmbeddingOptions = {
25456
+ ...embeddingOptions,
25457
+ taskType: "memory_recall_embed"
25458
+ };
25258
25459
  const tuningForward = {
25259
25460
  ...searchOptions?.rerankTopN !== void 0 && { rerankTopN: searchOptions.rerankTopN },
25260
25461
  ...searchOptions?.ceWeight !== void 0 && { ceWeight: searchOptions.ceWeight },
@@ -25308,6 +25509,7 @@ function createMemoryVaultSearchTool(vaultCtx, embeddingOptions, cache, searchOp
25308
25509
  const requestLimit = Number.isFinite(rawLimit) ? Math.min(Math.max(Math.floor(rawLimit), 1), RECALL_MAX_LIMIT) : limit;
25309
25510
  const argFolderId = args.folder_id;
25310
25511
  if (!query || typeof query !== "string") {
25512
+ notifyMemoryCallback(callbacks?.onError, new Error("A search query is required."));
25311
25513
  return "Error: A search query is required.";
25312
25514
  }
25313
25515
  try {
@@ -25315,7 +25517,11 @@ function createMemoryVaultSearchTool(vaultCtx, embeddingOptions, cache, searchOp
25315
25517
  const budget = wantsDecompose ? "high" : searchOptions?.rerank ? "mid" : "low";
25316
25518
  const folderId = searchOptions?.folderId ?? argFolderId;
25317
25519
  if (searchOptions?.useFusion === false) {
25520
+ let candidateCount;
25318
25521
  const legacyOptions = {
25522
+ onCandidateCount: (count2) => {
25523
+ candidateCount = count2;
25524
+ },
25319
25525
  limit: requestLimit,
25320
25526
  minSimilarity,
25321
25527
  useFusion: false,
@@ -25323,13 +25529,38 @@ function createMemoryVaultSearchTool(vaultCtx, embeddingOptions, cache, searchOp
25323
25529
  ...folderId !== void 0 && { folderId },
25324
25530
  ...searchOptions?.scopes && { scopes: searchOptions.scopes }
25325
25531
  };
25326
- const { results: legacy, embeddingsUnavailable } = "retain" in vaultCtx ? await vaultCtx.factSource.search(query, legacyOptions) : await searchVaultMemoriesWithSize(
25532
+ const start = Date.now();
25533
+ const outcome2 = "retain" in vaultCtx ? await vaultCtx.factSource.search(query, legacyOptions) : await searchVaultMemoriesWithSize(
25327
25534
  query,
25328
25535
  vaultCtx,
25329
- embeddingOptions,
25536
+ recallEmbeddingOptions,
25330
25537
  cache,
25331
25538
  legacyOptions
25332
25539
  );
25540
+ const { results: legacy, embeddingsUnavailable, vaultSize } = outcome2;
25541
+ const diagnostics = factRecallDiagnostics(
25542
+ legacy.map((m) => m.similarity),
25543
+ Date.now() - start,
25544
+ {
25545
+ vaultSize,
25546
+ minScore: minSimilarity,
25547
+ embeddingsUnavailable,
25548
+ candidateCount,
25549
+ truncated: candidateCount !== void 0 && candidateCount > requestLimit,
25550
+ decryptLast: outcome2.decryptLast,
25551
+ vaultRowsDecrypted: outcome2.rowsDecrypted,
25552
+ vaultRowsEmbedded: outcome2.rowsEmbedded,
25553
+ queryEmbedMs: outcome2.queryEmbedMs
25554
+ }
25555
+ );
25556
+ notifyMemoryCallback(callbacks?.onDiagnostics, diagnostics);
25557
+ if (diagnostics.truncated) {
25558
+ notifyMemoryCallback(callbacks?.onTruncated, {
25559
+ requestedLimit: requestLimit,
25560
+ effectiveLimit: requestLimit,
25561
+ truncatedByBudget: false
25562
+ });
25563
+ }
25333
25564
  if (legacy.length === 0) {
25334
25565
  return embeddingsUnavailable ? EMBEDDINGS_DEGRADED_EMPTY_NO_LEXICAL : "No relevant memories found in the vault.";
25335
25566
  }
@@ -25351,6 +25582,14 @@ function createMemoryVaultSearchTool(vaultCtx, embeddingOptions, cache, searchOp
25351
25582
  {
25352
25583
  onDiagnostics: (d) => {
25353
25584
  recallDegraded = d.degraded;
25585
+ notifyMemoryCallback(callbacks?.onDiagnostics, d);
25586
+ if (d.truncated) {
25587
+ notifyMemoryCallback(callbacks?.onTruncated, {
25588
+ requestedLimit: requestLimit,
25589
+ effectiveLimit: requestLimit,
25590
+ truncatedByBudget: false
25591
+ });
25592
+ }
25354
25593
  },
25355
25594
  types: ["fact"],
25356
25595
  limit: requestLimit,
@@ -25383,6 +25622,10 @@ function createMemoryVaultSearchTool(vaultCtx, embeddingOptions, cache, searchOp
25383
25622
 
25384
25623
  ${formatted}`;
25385
25624
  } catch (error) {
25625
+ notifyMemoryCallback(
25626
+ callbacks?.onError,
25627
+ error instanceof Error ? error : new Error(String(error))
25628
+ );
25386
25629
  const message = error instanceof Error ? error.message : "Unknown error";
25387
25630
  return `Error searching vault: ${message}`;
25388
25631
  }