@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.
@@ -2,7 +2,7 @@ import {
2
2
  callPortalJsonCompletion,
3
3
  notifyConsolidationFallback,
4
4
  resolvePiiRedactor
5
- } from "./chunk-4RGGAOAJ.mjs";
5
+ } from "./chunk-WCG5BFI6.mjs";
6
6
  import "./chunk-3RPGXXPZ.mjs";
7
7
 
8
8
  // src/lib/memory/consolidate.ts
@@ -14378,6 +14378,18 @@ var init_clientConfig = __esm({
14378
14378
  }
14379
14379
  });
14380
14380
 
14381
+ // src/lib/taskType.ts
14382
+ function taskTypeHeader(taskType) {
14383
+ return taskType ? { [TASK_TYPE_HEADER]: taskType } : {};
14384
+ }
14385
+ var TASK_TYPE_HEADER;
14386
+ var init_taskType = __esm({
14387
+ "src/lib/taskType.ts"() {
14388
+ "use strict";
14389
+ TASK_TYPE_HEADER = "X-Anuma-Task-Type";
14390
+ }
14391
+ });
14392
+
14381
14393
  // src/lib/pii/patterns.ts
14382
14394
  function luhnCheck(digits) {
14383
14395
  let sum = 0;
@@ -15022,18 +15034,6 @@ var init_internalFlowMarker = __esm({
15022
15034
  }
15023
15035
  });
15024
15036
 
15025
- // src/lib/taskType.ts
15026
- function taskTypeHeader(taskType) {
15027
- return taskType ? { [TASK_TYPE_HEADER]: taskType } : {};
15028
- }
15029
- var TASK_TYPE_HEADER;
15030
- var init_taskType = __esm({
15031
- "src/lib/taskType.ts"() {
15032
- "use strict";
15033
- TASK_TYPE_HEADER = "X-Anuma-Task-Type";
15034
- }
15035
- });
15036
-
15037
15037
  // node_modules/.pnpm/@nozbe+watermelondb@0.28.0/node_modules/@nozbe/watermelondb/decorators/action/index.js
15038
15038
  var require_action = __commonJS({
15039
15039
  "node_modules/.pnpm/@nozbe+watermelondb@0.28.0/node_modules/@nozbe/watermelondb/decorators/action/index.js"(exports2) {
@@ -19836,6 +19836,7 @@ var postApiV1Embeddings = (options) => (options.client ?? client).post({
19836
19836
 
19837
19837
  // src/lib/memoryEngine/generate.ts
19838
19838
  init_clientConfig();
19839
+ init_taskType();
19839
19840
  var EMBED_MAX_ATTEMPTS = 4;
19840
19841
  var DEFAULT_EMBEDDING_REQUEST_TIMEOUT_MS = 15e3;
19841
19842
  var DEFAULT_EMBEDDING_TOKEN_TIMEOUT_MS = 1e4;
@@ -19933,7 +19934,10 @@ async function embedOne(text12, options, outer) {
19933
19934
  const cached = cache.get(text12);
19934
19935
  if (cached) return Array.from(cached);
19935
19936
  }
19936
- const headers = await resolveAuthHeaders(options, outer);
19937
+ const headers = {
19938
+ ...await resolveAuthHeaders(options, outer),
19939
+ ...taskTypeHeader(options.taskType)
19940
+ };
19937
19941
  const response = await withEmbeddingRetry(
19938
19942
  () => withDeadline(
19939
19943
  (signal) => postApiV1Embeddings({
@@ -20028,7 +20032,10 @@ async function embedMany(texts, options, outer) {
20028
20032
  if (uncachedTexts.length === 0) {
20029
20033
  return results;
20030
20034
  }
20031
- const headers = await resolveAuthHeaders(options, outer);
20035
+ const headers = {
20036
+ ...await resolveAuthHeaders(options, outer),
20037
+ ...taskTypeHeader(options.taskType)
20038
+ };
20032
20039
  if (outer?.aborted) throw outer.reason;
20033
20040
  const embeddingModel = model ?? DEFAULT_API_EMBEDDING_MODEL;
20034
20041
  let newEmbeddings;
@@ -30374,6 +30381,50 @@ function applyMMR(candidates, k, lambda = 0.5) {
30374
30381
  return selected;
30375
30382
  }
30376
30383
 
30384
+ // src/lib/memory/recallTelemetry.ts
30385
+ function notifyMemoryCallback(callback, value) {
30386
+ try {
30387
+ callback?.(value);
30388
+ } catch {
30389
+ return;
30390
+ }
30391
+ }
30392
+ function factRecallDiagnostics(scores, elapsedMs, options = {}) {
30393
+ return {
30394
+ usedBudget: "low",
30395
+ reranked: false,
30396
+ candidateCount: options.candidateCount ?? scores.length,
30397
+ ...options.vaultSize !== void 0 && { vaultSize: options.vaultSize },
30398
+ ...options.decryptLast !== void 0 && { decryptLast: options.decryptLast },
30399
+ ...options.vaultRowsDecrypted !== void 0 && {
30400
+ vaultRowsDecrypted: options.vaultRowsDecrypted
30401
+ },
30402
+ ...options.vaultRowsEmbedded !== void 0 && {
30403
+ vaultRowsEmbedded: options.vaultRowsEmbedded
30404
+ },
30405
+ factCount: scores.length,
30406
+ chunkCount: 0,
30407
+ admittedCount: scores.length,
30408
+ topScore: scores.length ? Math.max(...scores) : -1,
30409
+ lowestAdmittedScore: scores.length ? Math.min(...scores) : -1,
30410
+ minScoreApplied: options.minScore ?? -1,
30411
+ truncated: options.truncated ?? false,
30412
+ graphLaneCount: 0,
30413
+ temporalLaneCount: 0,
30414
+ emptyReason: scores.length ? "" : options.vaultSize === 0 ? "vault-empty" : "no-candidates",
30415
+ timings: {
30416
+ total: elapsedMs,
30417
+ prep: 0,
30418
+ factLane: elapsedMs,
30419
+ rerank: 0,
30420
+ queryEmbed: options.queryEmbedMs ?? 0,
30421
+ chunkLane: 0,
30422
+ fuse: 0
30423
+ },
30424
+ degraded: options.embeddingsUnavailable ? ["embeddings-unavailable"] : []
30425
+ };
30426
+ }
30427
+
30377
30428
  // src/lib/memory/recency.ts
30378
30429
  var DAY_MS2 = 1e3 * 60 * 60 * 24;
30379
30430
  var DEFAULT_PER_YEAR_DECAY = 0.2;
@@ -31066,6 +31117,7 @@ function rankVaultMemories(_query, queryEmbedding, items, options) {
31066
31117
  }
31067
31118
  filtered.sort((a, b) => b.similarity - a.similarity);
31068
31119
  }
31120
+ notifyMemoryCallback(options?.onCandidateCount, filtered.length);
31069
31121
  const itemById = new Map(items.map((i) => [i.id, i]));
31070
31122
  return filtered.slice(0, limit).map((r) => resultFromItem(itemById.get(r.uniqueId), r.similarity));
31071
31123
  }
@@ -32048,6 +32100,7 @@ async function rankPreparedVaultCandidates(query, prepared, embeddingOptions, se
32048
32100
  const results = rankVaultMemories(query, queryEmbedding, embeddedItems, {
32049
32101
  limit,
32050
32102
  minSimilarity,
32103
+ onCandidateCount: searchOptions?.onCandidateCount,
32051
32104
  ...searchOptions?.supersessionBoost !== void 0 && {
32052
32105
  supersessionBoost: searchOptions.supersessionBoost
32053
32106
  },
@@ -32149,16 +32202,20 @@ var DEFAULT_SCOPE = "private";
32149
32202
  var DEFAULT_CONSOLIDATE_THRESHOLD = 0.55;
32150
32203
  var DEFAULT_CONSOLIDATE_TOP_K = 20;
32151
32204
  async function retain(content, ctx, options = {}) {
32205
+ const embeddingOptions = {
32206
+ ...ctx.embeddingOptions,
32207
+ taskType: "memory_retain_embed"
32208
+ };
32152
32209
  return retainWithPersistence(
32153
32210
  content,
32154
32211
  {
32155
- embeddingOptions: ctx.embeddingOptions,
32212
+ embeddingOptions,
32156
32213
  vaultCache: ctx.vaultCache,
32157
32214
  persistence: {
32158
32215
  prepare: (query, searchOptions) => prepareVaultCandidates(
32159
32216
  query,
32160
32217
  ctx.vaultCtx,
32161
- ctx.embeddingOptions,
32218
+ embeddingOptions,
32162
32219
  ctx.vaultCache,
32163
32220
  searchOptions
32164
32221
  ),
@@ -33312,7 +33369,17 @@ function parseVerdict(parsed) {
33312
33369
 
33313
33370
  // src/lib/memory/decayWorker.ts
33314
33371
  init_logger();
33315
- var EMPTY_RESULT = { archived: 0, deleted: 0, scanned: 0 };
33372
+ function emptyResult() {
33373
+ return {
33374
+ archived: 0,
33375
+ deleted: 0,
33376
+ scanned: 0,
33377
+ ruleDriven: { archived: 0, deleted: 0 },
33378
+ classifierDriven: { archived: 0, deleted: 0 },
33379
+ classifierCalls: 0,
33380
+ classifierCapHits: 0
33381
+ };
33382
+ }
33316
33383
  var DEFAULT_MAX_CLASSIFIER_CALLS_PER_SWEEP = 20;
33317
33384
  function resolveNow(now) {
33318
33385
  if (typeof now === "function") return now();
@@ -33351,42 +33418,45 @@ function createDecaySweeper(options) {
33351
33418
  let disposed = false;
33352
33419
  async function verdictFor(input, now, sweep) {
33353
33420
  const ruleVerdict = classifyDecay(input, now, policy);
33354
- if (ruleVerdict !== "keep") return ruleVerdict;
33355
- if (!classifier || !isBorderline(input)) return ruleVerdict;
33421
+ const rule = { verdict: ruleVerdict, source: "ruleDriven" };
33422
+ if (ruleVerdict !== "keep") return rule;
33423
+ if (!classifier || !isBorderline(input)) return rule;
33356
33424
  if (input.id) {
33357
33425
  const cached = classifierCache.get(input.id);
33358
- if (cached && cached.activityAt === lastActivityAt(input)) return cached.verdict;
33426
+ if (cached && cached.activityAt === lastActivityAt(input))
33427
+ return { verdict: cached.verdict, source: "classifierDriven" };
33359
33428
  }
33360
33429
  if (sweep.classifierCalls >= maxClassifierCalls) {
33430
+ sweep.classifierCapHits++;
33361
33431
  if (!sweep.ceilingLogged) {
33362
33432
  sweep.ceilingLogged = true;
33363
33433
  getLogger().warn(
33364
33434
  `[memory/decay] classifier per-sweep ceiling (${maxClassifierCalls}) reached; remaining borderline rows use the rule verdict this sweep`
33365
33435
  );
33366
33436
  }
33367
- return ruleVerdict;
33437
+ return rule;
33368
33438
  }
33369
33439
  sweep.classifierCalls++;
33370
33440
  try {
33371
33441
  const verdict = await classifier.classify(input, ruleVerdict, now);
33372
33442
  if (input.id) classifierCache.set(input.id, { activityAt: lastActivityAt(input), verdict });
33373
- return verdict;
33443
+ return { verdict, source: "classifierDriven" };
33374
33444
  } catch (err) {
33375
33445
  getLogger().warn(
33376
33446
  `[memory/decay] classifier failed; falling back to rule verdict: ${err instanceof Error ? err.message : String(err)}`
33377
33447
  );
33378
- return ruleVerdict;
33448
+ return rule;
33379
33449
  }
33380
33450
  }
33381
33451
  async function runSweep() {
33382
- if (disposed) return { ...EMPTY_RESULT };
33452
+ if (disposed) return emptyResult();
33383
33453
  const now = resolveNow(options.now);
33384
33454
  let candidates;
33385
33455
  try {
33386
33456
  candidates = await getDecayCandidatesRawOp(vaultCtx);
33387
33457
  } catch (err) {
33388
33458
  onError?.(err instanceof Error ? err : new Error(String(err)));
33389
- return { ...EMPTY_RESULT };
33459
+ return emptyResult();
33390
33460
  }
33391
33461
  if (classifierCache.size > 0) {
33392
33462
  const liveIds = new Set(candidates.map((c) => c.uniqueId));
@@ -33394,33 +33464,40 @@ function createDecaySweeper(options) {
33394
33464
  if (!liveIds.has(id)) classifierCache.delete(id);
33395
33465
  }
33396
33466
  }
33397
- const sweep = { classifierCalls: 0, ceilingLogged: false };
33467
+ const sweep = { classifierCalls: 0, classifierCapHits: 0, ceilingLogged: false };
33398
33468
  const toArchive = [];
33399
33469
  const toDelete = [];
33400
33470
  for (const c of candidates) {
33401
- const verdict = await verdictFor(toDecayInput(c), now, sweep);
33402
- if (verdict === "archive") toArchive.push(c);
33403
- else if (verdict === "delete") toDelete.push(c);
33404
- }
33405
- let archived = 0;
33406
- let deleted = 0;
33471
+ const { verdict, source } = await verdictFor(toDecayInput(c), now, sweep);
33472
+ if (verdict === "archive") toArchive.push({ candidate: c, source });
33473
+ else if (verdict === "delete") toDelete.push({ candidate: c, source });
33474
+ }
33475
+ const result = emptyResult();
33476
+ result.scanned = candidates.length;
33477
+ result.classifierCalls = sweep.classifierCalls;
33478
+ result.classifierCapHits = sweep.classifierCapHits;
33407
33479
  try {
33408
- for (const c of toArchive) {
33480
+ for (const { candidate: c, source } of toArchive) {
33409
33481
  const ok = await archiveVaultMemoryOp(vaultCtx, c.uniqueId, {
33410
33482
  now,
33411
33483
  expectedUpdatedAt: c.updatedAt,
33412
33484
  expectedLastObservedAt: c.lastObservedAt ?? null
33413
33485
  });
33414
- if (ok) archived++;
33486
+ if (ok) {
33487
+ result.archived++;
33488
+ result[source].archived++;
33489
+ }
33415
33490
  }
33416
- for (const c of toDelete) {
33491
+ for (const { candidate: c, source } of toDelete) {
33417
33492
  const ok = await hardDeleteDecayedOp(vaultCtx, c.uniqueId, { hardDeleteWindowMs, now });
33418
- if (ok) deleted++;
33493
+ if (ok) {
33494
+ result.deleted++;
33495
+ result[source].deleted++;
33496
+ }
33419
33497
  }
33420
33498
  } catch (err) {
33421
33499
  onError?.(err instanceof Error ? err : new Error(String(err)));
33422
33500
  }
33423
- const result = { archived, deleted, scanned: candidates.length };
33424
33501
  onSwept?.(result);
33425
33502
  return result;
33426
33503
  }
@@ -34025,6 +34102,10 @@ async function recall(query, ctx, options = {}) {
34025
34102
  const flags = flagsForBudget(usedBudget);
34026
34103
  const normalizedFacets = normalizeSubQueries(options.subQueries);
34027
34104
  const subQueries = normalizedFacets.length >= 2 ? normalizedFacets : void 0;
34105
+ const embeddingOptions = {
34106
+ ...ctx.embeddingOptions,
34107
+ taskType: "memory_recall_embed"
34108
+ };
34028
34109
  const t0 = nowMs2();
34029
34110
  let prepMs = 0;
34030
34111
  let factLaneMs = 0;
@@ -34120,7 +34201,7 @@ async function recall(query, ctx, options = {}) {
34120
34201
  let sharedEmbedMs = 0;
34121
34202
  const [queryEmbedding, entityRanking, temporalRanking] = await Promise.all([
34122
34203
  needsChunkEmbedding ? generateEmbedding(query, {
34123
- ...ctx.embeddingOptions,
34204
+ ...embeddingOptions,
34124
34205
  totalTimeoutMs: queryEmbedTotalTimeoutMs
34125
34206
  }).finally(() => {
34126
34207
  sharedEmbedMs = nowMs2() - prepStart;
@@ -34178,7 +34259,7 @@ async function recall(query, ctx, options = {}) {
34178
34259
  } = await (ctx.factSource?.search.bind(ctx.factSource) ?? ((searchQuery, searchOptions) => searchVaultMemoriesWithSize(
34179
34260
  searchQuery,
34180
34261
  ctx.vaultCtx,
34181
- ctx.embeddingOptions,
34262
+ embeddingOptions,
34182
34263
  ctx.vaultCache,
34183
34264
  searchOptions
34184
34265
  )))(query, {
@@ -34544,7 +34625,7 @@ function isDumpQuery(query) {
34544
34625
  async function listRecentFacts(ctx, limit, toolOptions) {
34545
34626
  const store = toolOptions?.memoryStore;
34546
34627
  const vaultCtx = ctx.vaultCtx;
34547
- if (!vaultCtx && !store) return [];
34628
+ if (!vaultCtx && !store) return { memories: [], truncated: false };
34548
34629
  const read = (ids) => store ? store.list({ memoryIds: ids }) : getVaultMemoriesByIdsOp(vaultCtx, ids);
34549
34630
  const filters = {
34550
34631
  ...toolOptions?.scopes && { scopes: toolOptions.scopes },
@@ -34555,34 +34636,47 @@ async function listRecentFacts(ctx, limit, toolOptions) {
34555
34636
  ...toolOptions?.folderId !== void 0 && { folderId: toolOptions.folderId }
34556
34637
  });
34557
34638
  const readable = [];
34639
+ const readLimit = limit + 1;
34640
+ let batchLimit = limit;
34558
34641
  let start = 0;
34559
- for (let size = limit; readable.length < limit; size *= 2) {
34642
+ let projected = 0;
34643
+ let exhausted = false;
34644
+ for (let size = readLimit; readable.length < readLimit; size *= 2) {
34560
34645
  const ordered = await project(size);
34561
- for (let i = start; i < ordered.length && readable.length < limit; i += limit) {
34562
- const batch = ordered.slice(i, i + limit).map((p) => p.uniqueId);
34646
+ projected = ordered.length;
34647
+ exhausted = !store || ordered.length < size;
34648
+ for (let i = start; i < ordered.length && readable.length < readLimit; ) {
34649
+ const batch = ordered.slice(i, i + batchLimit).map((p) => p.uniqueId);
34563
34650
  const byId = new Map((await read(batch)).map((m) => [m.uniqueId, m]));
34651
+ const previousCount = readable.length;
34564
34652
  for (const id of batch) {
34565
34653
  const m = byId.get(id);
34566
34654
  if (m && !isEncrypted(m.content)) readable.push(m);
34567
- if (readable.length >= limit) break;
34655
+ if (readable.length >= readLimit) break;
34568
34656
  }
34657
+ batchLimit = readable.length > previousCount ? Math.min(limit, readLimit - readable.length) : limit;
34658
+ i += batch.length;
34569
34659
  }
34570
- if (!store || ordered.length < size) break;
34660
+ if (exhausted) break;
34571
34661
  start = ordered.length;
34572
34662
  }
34573
- return readable.map((m) => ({
34574
- id: m.uniqueId,
34575
- kind: "fact",
34576
- content: m.content,
34577
- score: 0,
34578
- folderId: m.folderId,
34579
- eventTimeStart: m.eventTimeStart,
34580
- eventTimeEnd: m.eventTimeEnd,
34581
- eventTimeKind: m.eventTimeKind,
34582
- factType: m.factType,
34583
- createdAt: m.createdAt,
34584
- updatedAt: m.updatedAt
34585
- }));
34663
+ return {
34664
+ memories: readable.slice(0, limit).map((m) => ({
34665
+ id: m.uniqueId,
34666
+ kind: "fact",
34667
+ content: m.content,
34668
+ score: 0,
34669
+ folderId: m.folderId,
34670
+ eventTimeStart: m.eventTimeStart,
34671
+ eventTimeEnd: m.eventTimeEnd,
34672
+ eventTimeKind: m.eventTimeKind,
34673
+ factType: m.factType,
34674
+ createdAt: m.createdAt,
34675
+ updatedAt: m.updatedAt
34676
+ })),
34677
+ ...exhausted && { vaultSize: projected },
34678
+ truncated: readable.length > limit
34679
+ };
34586
34680
  }
34587
34681
  function createRecallTool(ctx, toolOptions, callbacks) {
34588
34682
  if (toolOptions?.memoryStore && toolOptions.folderId !== void 0)
@@ -34621,7 +34715,11 @@ function createRecallTool(ctx, toolOptions, callbacks) {
34621
34715
  },
34622
34716
  executor: async (args) => {
34623
34717
  if (typeof args.query !== "string" || args.query.length === 0) {
34624
- throw new Error("recall_memory: `query` is required and must be a non-empty string.");
34718
+ const error = new Error(
34719
+ "recall_memory: `query` is required and must be a non-empty string."
34720
+ );
34721
+ notifyMemoryCallback(callbacks?.onError, error);
34722
+ throw error;
34625
34723
  }
34626
34724
  const query = args.query;
34627
34725
  const now = Date.now();
@@ -34649,10 +34747,17 @@ function createRecallTool(ctx, toolOptions, callbacks) {
34649
34747
  RECALL_MAX_MEMORIES_PER_CONVERSATION - conversationMemories
34650
34748
  );
34651
34749
  const effectiveLimit = Math.min(requestLimit, remainingTurnBudget, remainingConvBudget);
34750
+ const truncatedByBudget = effectiveLimit < requestLimit;
34751
+ if (truncatedByBudget) {
34752
+ notifyMemoryCallback(callbacks?.onTruncated, {
34753
+ requestedLimit: requestLimit,
34754
+ effectiveLimit,
34755
+ truncatedByBudget
34756
+ });
34757
+ }
34652
34758
  if (effectiveLimit <= 0) {
34653
34759
  return VOLUME_LIMIT_NOTICE;
34654
34760
  }
34655
- const truncatedByBudget = effectiveLimit < requestLimit;
34656
34761
  turnMemories += effectiveLimit;
34657
34762
  conversationMemories += effectiveLimit;
34658
34763
  let surfaced = 0;
@@ -34661,7 +34766,27 @@ function createRecallTool(ctx, toolOptions, callbacks) {
34661
34766
  const wantsRecent = args.sort === "recent" && defaultTypes.includes("fact") && (ctx.vaultCtx !== void 0 || toolOptions?.memoryStore !== void 0);
34662
34767
  let result;
34663
34768
  if (wantsRecent) {
34664
- result = { memories: await listRecentFacts(ctx, effectiveLimit, toolOptions) };
34769
+ const start = Date.now();
34770
+ const recent = await listRecentFacts(ctx, effectiveLimit, toolOptions);
34771
+ result = recent;
34772
+ notifyMemoryCallback(callbacks?.onDiagnostics, {
34773
+ ...factRecallDiagnostics(
34774
+ result.memories.map((m) => m.score),
34775
+ Date.now() - start,
34776
+ {
34777
+ truncated: recent.truncated,
34778
+ ...recent.vaultSize !== void 0 && { vaultSize: recent.vaultSize }
34779
+ }
34780
+ ),
34781
+ truncatedByBudget
34782
+ });
34783
+ if (recent.truncated && !truncatedByBudget) {
34784
+ notifyMemoryCallback(callbacks?.onTruncated, {
34785
+ requestedLimit: requestLimit,
34786
+ effectiveLimit,
34787
+ truncatedByBudget
34788
+ });
34789
+ }
34665
34790
  } else {
34666
34791
  let subQueries;
34667
34792
  if (defaultBudget === "high" && toolOptions?.decomposeOptions) {
@@ -34673,6 +34798,14 @@ function createRecallTool(ctx, toolOptions, callbacks) {
34673
34798
  const recallOpts = {
34674
34799
  onDiagnostics: (d) => {
34675
34800
  recallDegraded = d.degraded;
34801
+ notifyMemoryCallback(callbacks?.onDiagnostics, { ...d, truncatedByBudget });
34802
+ if (d.truncated && !truncatedByBudget) {
34803
+ notifyMemoryCallback(callbacks?.onTruncated, {
34804
+ requestedLimit: requestLimit,
34805
+ effectiveLimit,
34806
+ truncatedByBudget
34807
+ });
34808
+ }
34676
34809
  },
34677
34810
  types: defaultTypes,
34678
34811
  limit: effectiveLimit,
@@ -34716,6 +34849,7 @@ ${TRUNCATION_NOTICE}` : formatted;
34716
34849
  const message = error instanceof Error ? error.message : "Unknown error";
34717
34850
  const wrapped = new Error(`recall_memory: search failed \u2014 ${message}`);
34718
34851
  wrapped.cause = error;
34852
+ notifyMemoryCallback(callbacks?.onError, wrapped);
34719
34853
  throw wrapped;
34720
34854
  } finally {
34721
34855
  const unused = effectiveLimit - surfaced;
@@ -34957,6 +35091,18 @@ When a memory refers to one of these, reuse the EXACT existing name (same spelli
34957
35091
  ` : "";
34958
35092
  for (let i = 0; i < memories.length; i += TOPIC_EXTRACTION_BATCH_SIZE) {
34959
35093
  const batch = memories.slice(i, i + TOPIC_EXTRACTION_BATCH_SIZE);
35094
+ const start = Date.now();
35095
+ let attempts = 0;
35096
+ let failure;
35097
+ const reportBatch = (unansweredCount) => {
35098
+ notifyMemoryCallback(options.onBatch, {
35099
+ memoryIds: batch.map((m) => m.id),
35100
+ attempts,
35101
+ durationMs: Date.now() - start,
35102
+ unansweredCount,
35103
+ ...failure && { failure }
35104
+ });
35105
+ };
34960
35106
  const rows = [];
34961
35107
  for (const m of batch) {
34962
35108
  const content = m.content.slice(0, MAX_CHARS_PER_MEMORY).replace(/\s+/g, " ").trim();
@@ -34978,6 +35124,12 @@ ${listing}
34978
35124
 
34979
35125
  List each memory's named entities.`,
34980
35126
  tag: "memory/topics",
35127
+ onAttempt: (attempt) => {
35128
+ attempts = attempt.attempt;
35129
+ },
35130
+ onFailure: (value) => {
35131
+ failure = value;
35132
+ },
34981
35133
  extra: { max_completion_tokens: MAX_COMPLETION_TOKENS },
34982
35134
  ...options.fetchFn && { fetchFn: options.fetchFn },
34983
35135
  ...options.maxAttempts !== void 0 && { maxAttempts: options.maxAttempts },
@@ -34986,6 +35138,7 @@ List each memory's named entities.`,
34986
35138
  ...options.backoffMs && { backoffMs: options.backoffMs }
34987
35139
  });
34988
35140
  if (parsed === null) {
35141
+ reportBatch(batch.length);
34989
35142
  getLogger().warn(
34990
35143
  `[memory/topics] batch of ${batch.length} failed after retries \u2014 will retry next sweep`
34991
35144
  );
@@ -34994,6 +35147,7 @@ List each memory's named entities.`,
34994
35147
  const validIds = new Set(batch.map((m) => m.id));
34995
35148
  const byId = parseTopicResponse(parsed, validIds);
34996
35149
  if (byId === null) {
35150
+ reportBatch(batch.length);
34997
35151
  getLogger().warn(
34998
35152
  `[memory/topics] batch of ${batch.length} returned an unrecognized shape \u2014 will retry next sweep`
34999
35153
  );
@@ -35006,6 +35160,7 @@ List each memory's named entities.`,
35006
35160
  }
35007
35161
  out.set(m.id, redactor ? restoreEntities(entities, redactor) : entities);
35008
35162
  }
35163
+ reportBatch(batch.filter((m) => !byId.has(m.id)).length);
35009
35164
  }
35010
35165
  return out;
35011
35166
  }
@@ -35044,6 +35199,7 @@ function isDegradedTopicSkip(reason) {
35044
35199
  return DEGRADED_TOPIC_SKIP[reason] === true;
35045
35200
  }
35046
35201
  async function extractAndLinkEntitiesForMemoriesOp(ctx, memoryIds, options) {
35202
+ const start = Date.now();
35047
35203
  const entityCtx = ctx.entityCtx;
35048
35204
  if (!entityCtx) {
35049
35205
  throw new Error("extractAndLinkEntitiesForMemoriesOp requires ctx.entityCtx");
@@ -35084,7 +35240,18 @@ async function extractAndLinkEntitiesForMemoriesOp(ctx, memoryIds, options) {
35084
35240
  skip(id, "unreadable");
35085
35241
  }
35086
35242
  }
35087
- const entitiesByMemory = await extractEntitiesForMemories(inputs, options);
35243
+ const batches = [];
35244
+ const llmFailures = /* @__PURE__ */ new Map();
35245
+ const entitiesByMemory = await extractEntitiesForMemories(inputs, {
35246
+ ...options,
35247
+ onBatch: (batch) => {
35248
+ batches.push(batch);
35249
+ if (batch.failure) {
35250
+ for (const id of batch.memoryIds) llmFailures.set(id, batch.failure);
35251
+ }
35252
+ notifyMemoryCallback(options.onBatch, batch);
35253
+ }
35254
+ });
35088
35255
  const toStamp = [];
35089
35256
  for (const input of inputs) {
35090
35257
  const entities = entitiesByMemory.get(input.id);
@@ -35115,7 +35282,16 @@ async function extractAndLinkEntitiesForMemoriesOp(ctx, memoryIds, options) {
35115
35282
  entitiesByMemory.delete(id);
35116
35283
  }
35117
35284
  }
35118
- return { entitiesByMemory, stampedIds, skippedIds, skippedReasons };
35285
+ return {
35286
+ entitiesByMemory,
35287
+ stampedIds,
35288
+ skippedIds,
35289
+ skippedReasons,
35290
+ batchCount: batches.length,
35291
+ durationMs: Date.now() - start,
35292
+ batches,
35293
+ llmFailures
35294
+ };
35119
35295
  }
35120
35296
 
35121
35297
  // src/lib/memory/store/local.ts
@@ -35270,7 +35446,7 @@ function createLocalMemoryStore(options) {
35270
35446
  search: (query, searchOptions) => searchVaultMemoriesWithSize(
35271
35447
  query,
35272
35448
  vaultCtx,
35273
- options.embeddingOptions,
35449
+ { ...options.embeddingOptions, taskType: "memory_recall_embed" },
35274
35450
  vaultCache,
35275
35451
  searchOptions
35276
35452
  ),
@@ -36929,9 +37105,13 @@ function formatVaultHits(hits) {
36929
37105
  return hits.map((h, i) => `[${i + 1}] (id: ${h.id}, similarity: ${h.score.toFixed(2)})
36930
37106
  ${h.content}`).join("\n\n");
36931
37107
  }
36932
- function createMemoryVaultSearchTool(vaultCtx, embeddingOptions, cache, searchOptions) {
37108
+ function createMemoryVaultSearchTool(vaultCtx, embeddingOptions, cache, searchOptions, callbacks) {
36933
37109
  const limit = searchOptions?.limit ?? 5;
36934
37110
  const minSimilarity = searchOptions?.minSimilarity ?? 0.1;
37111
+ const recallEmbeddingOptions = {
37112
+ ...embeddingOptions,
37113
+ taskType: "memory_recall_embed"
37114
+ };
36935
37115
  const tuningForward = {
36936
37116
  ...searchOptions?.rerankTopN !== void 0 && { rerankTopN: searchOptions.rerankTopN },
36937
37117
  ...searchOptions?.ceWeight !== void 0 && { ceWeight: searchOptions.ceWeight },
@@ -36985,6 +37165,7 @@ function createMemoryVaultSearchTool(vaultCtx, embeddingOptions, cache, searchOp
36985
37165
  const requestLimit = Number.isFinite(rawLimit) ? Math.min(Math.max(Math.floor(rawLimit), 1), RECALL_MAX_LIMIT) : limit;
36986
37166
  const argFolderId = args.folder_id;
36987
37167
  if (!query || typeof query !== "string") {
37168
+ notifyMemoryCallback(callbacks?.onError, new Error("A search query is required."));
36988
37169
  return "Error: A search query is required.";
36989
37170
  }
36990
37171
  try {
@@ -36992,7 +37173,11 @@ function createMemoryVaultSearchTool(vaultCtx, embeddingOptions, cache, searchOp
36992
37173
  const budget = wantsDecompose ? "high" : searchOptions?.rerank ? "mid" : "low";
36993
37174
  const folderId = searchOptions?.folderId ?? argFolderId;
36994
37175
  if (searchOptions?.useFusion === false) {
37176
+ let candidateCount;
36995
37177
  const legacyOptions = {
37178
+ onCandidateCount: (count) => {
37179
+ candidateCount = count;
37180
+ },
36996
37181
  limit: requestLimit,
36997
37182
  minSimilarity,
36998
37183
  useFusion: false,
@@ -37000,13 +37185,38 @@ function createMemoryVaultSearchTool(vaultCtx, embeddingOptions, cache, searchOp
37000
37185
  ...folderId !== void 0 && { folderId },
37001
37186
  ...searchOptions?.scopes && { scopes: searchOptions.scopes }
37002
37187
  };
37003
- const { results: legacy, embeddingsUnavailable } = "retain" in vaultCtx ? await vaultCtx.factSource.search(query, legacyOptions) : await searchVaultMemoriesWithSize(
37188
+ const start = Date.now();
37189
+ const outcome = "retain" in vaultCtx ? await vaultCtx.factSource.search(query, legacyOptions) : await searchVaultMemoriesWithSize(
37004
37190
  query,
37005
37191
  vaultCtx,
37006
- embeddingOptions,
37192
+ recallEmbeddingOptions,
37007
37193
  cache,
37008
37194
  legacyOptions
37009
37195
  );
37196
+ const { results: legacy, embeddingsUnavailable, vaultSize } = outcome;
37197
+ const diagnostics = factRecallDiagnostics(
37198
+ legacy.map((m) => m.similarity),
37199
+ Date.now() - start,
37200
+ {
37201
+ vaultSize,
37202
+ minScore: minSimilarity,
37203
+ embeddingsUnavailable,
37204
+ candidateCount,
37205
+ truncated: candidateCount !== void 0 && candidateCount > requestLimit,
37206
+ decryptLast: outcome.decryptLast,
37207
+ vaultRowsDecrypted: outcome.rowsDecrypted,
37208
+ vaultRowsEmbedded: outcome.rowsEmbedded,
37209
+ queryEmbedMs: outcome.queryEmbedMs
37210
+ }
37211
+ );
37212
+ notifyMemoryCallback(callbacks?.onDiagnostics, diagnostics);
37213
+ if (diagnostics.truncated) {
37214
+ notifyMemoryCallback(callbacks?.onTruncated, {
37215
+ requestedLimit: requestLimit,
37216
+ effectiveLimit: requestLimit,
37217
+ truncatedByBudget: false
37218
+ });
37219
+ }
37010
37220
  if (legacy.length === 0) {
37011
37221
  return embeddingsUnavailable ? EMBEDDINGS_DEGRADED_EMPTY_NO_LEXICAL : "No relevant memories found in the vault.";
37012
37222
  }
@@ -37028,6 +37238,14 @@ function createMemoryVaultSearchTool(vaultCtx, embeddingOptions, cache, searchOp
37028
37238
  {
37029
37239
  onDiagnostics: (d) => {
37030
37240
  recallDegraded = d.degraded;
37241
+ notifyMemoryCallback(callbacks?.onDiagnostics, d);
37242
+ if (d.truncated) {
37243
+ notifyMemoryCallback(callbacks?.onTruncated, {
37244
+ requestedLimit: requestLimit,
37245
+ effectiveLimit: requestLimit,
37246
+ truncatedByBudget: false
37247
+ });
37248
+ }
37031
37249
  },
37032
37250
  types: ["fact"],
37033
37251
  limit: requestLimit,
@@ -37060,6 +37278,10 @@ function createMemoryVaultSearchTool(vaultCtx, embeddingOptions, cache, searchOp
37060
37278
 
37061
37279
  ${formatted}`;
37062
37280
  } catch (error) {
37281
+ notifyMemoryCallback(
37282
+ callbacks?.onError,
37283
+ error instanceof Error ? error : new Error(String(error))
37284
+ );
37063
37285
  const message = error instanceof Error ? error.message : "Unknown error";
37064
37286
  return `Error searching vault: ${message}`;
37065
37287
  }