@anuma/sdk 6.43.0 → 6.44.0

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
  },
@@ -22822,6 +22998,7 @@ var MAX_BATCH = 50;
22822
22998
  var MAX_QUERY_IDS = 1e3;
22823
22999
  var MAX_QUERY_TOPICS = 100;
22824
23000
  var MAX_QUERY_FILTER = 20;
23001
+ var GRAPH_LANE_ROWS = 256;
22825
23002
  var RemoteMemoryPartialCreateError = class extends Error {
22826
23003
  constructor(created, cause) {
22827
23004
  super(`createMany stored ${created.length} memories before a batch failed`);
@@ -22917,7 +23094,11 @@ function createRemoteMemoryStore(options) {
22917
23094
  const { persistence, embeddingOptions } = options;
22918
23095
  const model = embeddingOptions.model ?? DEFAULT_API_EMBEDDING_MODEL;
22919
23096
  const pollIntervalMs = options.pollIntervalMs ?? 6e4;
22920
- const pipeline = createRemoteMemoryPipeline(options);
23097
+ const pipeline = createRemoteMemoryPipeline({
23098
+ ...options,
23099
+ graphRanking: options.graphRanking ?? ((query, _traverse, recallOptions) => topicGraphRanking(query, recallOptions)),
23100
+ temporalRanking: options.temporalRanking ?? (() => Promise.resolve([]))
23101
+ });
22921
23102
  const subscribers = /* @__PURE__ */ new Set();
22922
23103
  const seen = /* @__PURE__ */ new Map();
22923
23104
  let timer;
@@ -22949,6 +23130,36 @@ function createRemoteMemoryStore(options) {
22949
23130
  const decrypted = (options2) => persistence.query(options2);
22950
23131
  const metadata2 = (options2) => persistence.queryMetadata(options2);
22951
23132
  const enumerate = (filters, max = Infinity) => pages(decrypted, filters, max);
23133
+ const memoriesByTopics = async (names) => {
23134
+ const wanted = new Set(names.map(normalizeEntityName).filter(Boolean));
23135
+ const out = /* @__PURE__ */ new Map();
23136
+ const pages2 = await Promise.all(
23137
+ chunks2([...wanted], MAX_QUERY_TOPICS).map((topics) => enumerate({ topics }))
23138
+ );
23139
+ for (const { memory } of pages2.flat()) {
23140
+ const matched = [...canonicalTopics(memory)].filter((name) => wanted.has(name));
23141
+ if (matched.length) out.set(memory.memory_id, new Set(matched));
23142
+ }
23143
+ return out;
23144
+ };
23145
+ const topicGraphRanking = async (query, recallOptions = {}) => {
23146
+ const wanted = new Set(extractQueryEntities(query).map(normalizeEntityName).filter(Boolean));
23147
+ if (wanted.size === 0 || recallOptions.memoryIds?.length === 0) return [];
23148
+ const rows = await pages(
23149
+ metadata2,
23150
+ {
23151
+ topics: [...wanted].slice(0, MAX_QUERY_TOPICS),
23152
+ ...recallOptions.scopes && { scopes: recallOptions.scopes },
23153
+ ...recallOptions.factTypes && { fact_types: recallOptions.factTypes },
23154
+ ...recallOptions.memoryIds && { memory_ids: recallOptions.memoryIds }
23155
+ },
23156
+ GRAPH_LANE_ROWS
23157
+ );
23158
+ return rows.map(({ memory }) => ({
23159
+ memoryId: memory.memory_id,
23160
+ matched: [...canonicalTopics(memory)].filter((name) => wanted.has(name)).length
23161
+ })).filter((row) => row.matched > 0).sort((a, b) => b.matched - a.matched).slice(0, NODE_BUDGET).map((row) => row.memoryId);
23162
+ };
22952
23163
  const enumerateIds = async (read, ids, filters) => (await Promise.all(
22953
23164
  chunks2([...new Set(ids)], MAX_QUERY_IDS).map(
22954
23165
  (memory_ids) => pages(read, { ...filters, memory_ids })
@@ -23174,7 +23385,8 @@ function createRemoteMemoryStore(options) {
23174
23385
  };
23175
23386
  },
23176
23387
  extractTopics: async (memoryIds, extractOptions) => {
23177
- const extractedAt = extractOptions.now ?? Date.now();
23388
+ const start = Date.now();
23389
+ const extractedAt = extractOptions.now ?? start;
23178
23390
  const skippedIds = [];
23179
23391
  const skippedReasons = /* @__PURE__ */ new Map();
23180
23392
  const skip = (id, reason) => {
@@ -23192,7 +23404,18 @@ function createRemoteMemoryStore(options) {
23192
23404
  else if (memory.is_deleted || memory.topics_user_managed) skip(id, "excluded");
23193
23405
  else inputs.push({ id, content: memory.content });
23194
23406
  }
23195
- const entitiesByMemory = await extractEntitiesForMemories(inputs, extractOptions);
23407
+ const batches = [];
23408
+ const llmFailures = /* @__PURE__ */ new Map();
23409
+ const entitiesByMemory = await extractEntitiesForMemories(inputs, {
23410
+ ...extractOptions,
23411
+ onBatch: (batch) => {
23412
+ batches.push(batch);
23413
+ if (batch.failure) {
23414
+ for (const id of batch.memoryIds) llmFailures.set(id, batch.failure);
23415
+ }
23416
+ notifyMemoryCallback(extractOptions.onBatch, batch);
23417
+ }
23418
+ });
23196
23419
  const answered = /* @__PURE__ */ new Map();
23197
23420
  for (const input of inputs) {
23198
23421
  const entities = entitiesByMemory.get(input.id);
@@ -23241,7 +23464,16 @@ function createRemoteMemoryStore(options) {
23241
23464
  getLogger().warn("[memory/remote-store] Failed to write extracted topics:", error);
23242
23465
  for (const id of answered.keys()) fail(id, "link-failed");
23243
23466
  }
23244
- return { entitiesByMemory, stampedIds, skippedIds, skippedReasons };
23467
+ return {
23468
+ entitiesByMemory,
23469
+ stampedIds,
23470
+ skippedIds,
23471
+ skippedReasons,
23472
+ batchCount: batches.length,
23473
+ durationMs: Date.now() - start,
23474
+ batches,
23475
+ llmFailures
23476
+ };
23245
23477
  },
23246
23478
  stampTopicsExtracted: async (memoryIds, extractedAt, version = TOPICS_EXTRACTION_VERSION) => {
23247
23479
  const ids = [...new Set(memoryIds)];
@@ -23368,18 +23600,7 @@ function createRemoteMemoryStore(options) {
23368
23600
  listArchived: async () => (await enumerate({ archived_only: true, order: "archived", include_embeddings: true })).map(
23369
23601
  (record2) => stored(record2.memory)
23370
23602
  ),
23371
- memoriesByTopics: async (names) => {
23372
- const wanted = new Set(names.map(normalizeEntityName).filter(Boolean));
23373
- const out = /* @__PURE__ */ new Map();
23374
- const pages2 = await Promise.all(
23375
- chunks2([...wanted], MAX_QUERY_TOPICS).map((topics) => enumerate({ topics }))
23376
- );
23377
- for (const { memory } of pages2.flat()) {
23378
- const matched = [...canonicalTopics(memory)].filter((name) => wanted.has(name));
23379
- if (matched.length) out.set(memory.memory_id, new Set(matched));
23380
- }
23381
- return out;
23382
- },
23603
+ memoriesByTopics,
23383
23604
  topicsByMemories: async (memoryIds) => {
23384
23605
  const out = /* @__PURE__ */ new Map();
23385
23606
  for (const { memory } of await enumerateIds(decrypted, memoryIds, {
@@ -25252,9 +25473,13 @@ function formatVaultHits(hits) {
25252
25473
  return hits.map((h, i) => `[${i + 1}] (id: ${h.id}, similarity: ${h.score.toFixed(2)})
25253
25474
  ${h.content}`).join("\n\n");
25254
25475
  }
25255
- function createMemoryVaultSearchTool(vaultCtx, embeddingOptions, cache, searchOptions) {
25476
+ function createMemoryVaultSearchTool(vaultCtx, embeddingOptions, cache, searchOptions, callbacks) {
25256
25477
  const limit = searchOptions?.limit ?? 5;
25257
25478
  const minSimilarity = searchOptions?.minSimilarity ?? 0.1;
25479
+ const recallEmbeddingOptions = {
25480
+ ...embeddingOptions,
25481
+ taskType: "memory_recall_embed"
25482
+ };
25258
25483
  const tuningForward = {
25259
25484
  ...searchOptions?.rerankTopN !== void 0 && { rerankTopN: searchOptions.rerankTopN },
25260
25485
  ...searchOptions?.ceWeight !== void 0 && { ceWeight: searchOptions.ceWeight },
@@ -25308,6 +25533,7 @@ function createMemoryVaultSearchTool(vaultCtx, embeddingOptions, cache, searchOp
25308
25533
  const requestLimit = Number.isFinite(rawLimit) ? Math.min(Math.max(Math.floor(rawLimit), 1), RECALL_MAX_LIMIT) : limit;
25309
25534
  const argFolderId = args.folder_id;
25310
25535
  if (!query || typeof query !== "string") {
25536
+ notifyMemoryCallback(callbacks?.onError, new Error("A search query is required."));
25311
25537
  return "Error: A search query is required.";
25312
25538
  }
25313
25539
  try {
@@ -25315,7 +25541,11 @@ function createMemoryVaultSearchTool(vaultCtx, embeddingOptions, cache, searchOp
25315
25541
  const budget = wantsDecompose ? "high" : searchOptions?.rerank ? "mid" : "low";
25316
25542
  const folderId = searchOptions?.folderId ?? argFolderId;
25317
25543
  if (searchOptions?.useFusion === false) {
25544
+ let candidateCount;
25318
25545
  const legacyOptions = {
25546
+ onCandidateCount: (count2) => {
25547
+ candidateCount = count2;
25548
+ },
25319
25549
  limit: requestLimit,
25320
25550
  minSimilarity,
25321
25551
  useFusion: false,
@@ -25323,13 +25553,38 @@ function createMemoryVaultSearchTool(vaultCtx, embeddingOptions, cache, searchOp
25323
25553
  ...folderId !== void 0 && { folderId },
25324
25554
  ...searchOptions?.scopes && { scopes: searchOptions.scopes }
25325
25555
  };
25326
- const { results: legacy, embeddingsUnavailable } = "retain" in vaultCtx ? await vaultCtx.factSource.search(query, legacyOptions) : await searchVaultMemoriesWithSize(
25556
+ const start = Date.now();
25557
+ const outcome2 = "retain" in vaultCtx ? await vaultCtx.factSource.search(query, legacyOptions) : await searchVaultMemoriesWithSize(
25327
25558
  query,
25328
25559
  vaultCtx,
25329
- embeddingOptions,
25560
+ recallEmbeddingOptions,
25330
25561
  cache,
25331
25562
  legacyOptions
25332
25563
  );
25564
+ const { results: legacy, embeddingsUnavailable, vaultSize } = outcome2;
25565
+ const diagnostics = factRecallDiagnostics(
25566
+ legacy.map((m) => m.similarity),
25567
+ Date.now() - start,
25568
+ {
25569
+ vaultSize,
25570
+ minScore: minSimilarity,
25571
+ embeddingsUnavailable,
25572
+ candidateCount,
25573
+ truncated: candidateCount !== void 0 && candidateCount > requestLimit,
25574
+ decryptLast: outcome2.decryptLast,
25575
+ vaultRowsDecrypted: outcome2.rowsDecrypted,
25576
+ vaultRowsEmbedded: outcome2.rowsEmbedded,
25577
+ queryEmbedMs: outcome2.queryEmbedMs
25578
+ }
25579
+ );
25580
+ notifyMemoryCallback(callbacks?.onDiagnostics, diagnostics);
25581
+ if (diagnostics.truncated) {
25582
+ notifyMemoryCallback(callbacks?.onTruncated, {
25583
+ requestedLimit: requestLimit,
25584
+ effectiveLimit: requestLimit,
25585
+ truncatedByBudget: false
25586
+ });
25587
+ }
25333
25588
  if (legacy.length === 0) {
25334
25589
  return embeddingsUnavailable ? EMBEDDINGS_DEGRADED_EMPTY_NO_LEXICAL : "No relevant memories found in the vault.";
25335
25590
  }
@@ -25351,6 +25606,14 @@ function createMemoryVaultSearchTool(vaultCtx, embeddingOptions, cache, searchOp
25351
25606
  {
25352
25607
  onDiagnostics: (d) => {
25353
25608
  recallDegraded = d.degraded;
25609
+ notifyMemoryCallback(callbacks?.onDiagnostics, d);
25610
+ if (d.truncated) {
25611
+ notifyMemoryCallback(callbacks?.onTruncated, {
25612
+ requestedLimit: requestLimit,
25613
+ effectiveLimit: requestLimit,
25614
+ truncatedByBudget: false
25615
+ });
25616
+ }
25354
25617
  },
25355
25618
  types: ["fact"],
25356
25619
  limit: requestLimit,
@@ -25383,6 +25646,10 @@ function createMemoryVaultSearchTool(vaultCtx, embeddingOptions, cache, searchOp
25383
25646
 
25384
25647
  ${formatted}`;
25385
25648
  } catch (error) {
25649
+ notifyMemoryCallback(
25650
+ callbacks?.onError,
25651
+ error instanceof Error ? error : new Error(String(error))
25652
+ );
25386
25653
  const message = error instanceof Error ? error.message : "Unknown error";
25387
25654
  return `Error searching vault: ${message}`;
25388
25655
  }