@anuma/sdk 6.19.2 → 6.21.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.
@@ -179,7 +179,7 @@ import {
179
179
  updateVaultMemoryOp,
180
180
  useEncryption,
181
181
  vaultMemoryToStored
182
- } from "./chunk-M53NXLZS.mjs";
182
+ } from "./chunk-JRVAUZHM.mjs";
183
183
  import {
184
184
  PiiRedactor,
185
185
  createStreamingDeAnonymizer,
@@ -7464,6 +7464,7 @@ async function retain(content, ctx, options = {}) {
7464
7464
  const threshold = options.autoMergeThreshold ?? DEFAULT_AUTO_MERGE_THRESHOLD;
7465
7465
  const resolvedScope = options.scope ?? DEFAULT_SCOPE;
7466
7466
  let supersedeTargetIds = [];
7467
+ let consolidationDecidedCreate = false;
7467
7468
  let supersedeContent;
7468
7469
  let prepared;
7469
7470
  if (enableAutoMerge) {
@@ -7491,8 +7492,12 @@ async function retain(content, ctx, options = {}) {
7491
7492
  const outcome = await tryConsolidate(trimmed, ctx, options, prepared);
7492
7493
  if (outcome) {
7493
7494
  if ("done" in outcome) return outcome.done;
7494
- supersedeTargetIds = outcome.supersede;
7495
- supersedeContent = outcome.content;
7495
+ if ("create" in outcome) {
7496
+ consolidationDecidedCreate = true;
7497
+ } else {
7498
+ supersedeTargetIds = outcome.supersede;
7499
+ supersedeContent = outcome.content;
7500
+ }
7496
7501
  }
7497
7502
  }
7498
7503
  if (supersedeTargetIds.length === 0) {
@@ -7542,7 +7547,14 @@ async function retain(content, ctx, options = {}) {
7542
7547
  action: "merge",
7543
7548
  memoryId: targetId,
7544
7549
  targetId,
7545
- proofCount: updated.proofCount ?? (existing.proofCount ?? 1) + 1
7550
+ proofCount: updated.proofCount ?? (existing.proofCount ?? 1) + 1,
7551
+ similarity: matches[0].similarity,
7552
+ // Stage 1 may have run and explicitly said `create`, and Stage 2
7553
+ // then found a strict-cosine match anyway. Reporting the decision
7554
+ // here is what makes that DISAGREEMENT visible — without it the
7555
+ // model's create silently vanished from the distribution whenever
7556
+ // the cosine stage won.
7557
+ ...consolidationDecidedCreate && { consolidation: "create" }
7546
7558
  };
7547
7559
  }
7548
7560
  await assertMergeTargetGoneOrThrow(ctx, targetId);
@@ -7555,12 +7567,18 @@ async function retain(content, ctx, options = {}) {
7555
7567
  const embedding = reusableQueryEmbedding ?? await generateEmbedding(contentToWrite, ctx.embeddingOptions);
7556
7568
  const embeddingModel = ctx.embeddingOptions.model ?? DEFAULT_API_EMBEDDING_MODEL;
7557
7569
  if (options.respectTombstones) {
7558
- const tombstoneId = await findTombstoneMatch(embedding, embeddingModel, resolvedScope, ctx, {
7570
+ const tombstone = await findTombstoneMatch(embedding, embeddingModel, resolvedScope, ctx, {
7559
7571
  threshold,
7560
7572
  folderId: options.folderId
7561
7573
  });
7562
- if (tombstoneId) {
7563
- return { action: "suppressed", memoryId: tombstoneId, tombstoneId, proofCount: 0 };
7574
+ if (tombstone) {
7575
+ return {
7576
+ action: "suppressed",
7577
+ memoryId: tombstone.id,
7578
+ tombstoneId: tombstone.id,
7579
+ proofCount: 0,
7580
+ similarity: tombstone.similarity
7581
+ };
7564
7582
  }
7565
7583
  }
7566
7584
  const createOpts = {
@@ -7627,7 +7645,8 @@ async function retain(content, ctx, options = {}) {
7627
7645
  action: "supersede",
7628
7646
  memoryId: created2.uniqueId,
7629
7647
  targetId: primaryTargetId,
7630
- proofCount: 1
7648
+ proofCount: 1,
7649
+ consolidation: "supersede"
7631
7650
  };
7632
7651
  }
7633
7652
  notifyConsolidationFallback(
@@ -7641,7 +7660,8 @@ async function retain(content, ctx, options = {}) {
7641
7660
  return {
7642
7661
  action: "create",
7643
7662
  memoryId: created.uniqueId,
7644
- proofCount: 1
7663
+ proofCount: 1,
7664
+ ...consolidationDecidedCreate && { consolidation: "create" }
7645
7665
  };
7646
7666
  }
7647
7667
  function unionStrings(a, b) {
@@ -7671,7 +7691,7 @@ async function findTombstoneMatch(embedding, embeddingModel, scope, ctx, opts) {
7671
7691
  bestId = row.uniqueId;
7672
7692
  }
7673
7693
  }
7674
- return bestId;
7694
+ return bestId === null ? null : { id: bestId, similarity: bestSim };
7675
7695
  }
7676
7696
  function pickEventTimeUpdate(existing, incoming) {
7677
7697
  if (incoming === void 0 || incoming === null) return void 0;
@@ -7713,7 +7733,7 @@ async function tryConsolidate(trimmed, ctx, options, prepared) {
7713
7733
  }));
7714
7734
  const { consolidateMemory: doConsolidate } = await import("./consolidate-URHGJT7I.mjs");
7715
7735
  const decision = await doConsolidate(trimmed, candidates, consolidateOptions);
7716
- if (decision.action === "create") return null;
7736
+ if (decision.action === "create") return decision.fallbackReason ? null : { create: true };
7717
7737
  if (decision.action === "supersede" && decision.content) {
7718
7738
  const requestedIds = decision.targetIds?.length ? decision.targetIds : decision.targetId ? [decision.targetId] : [];
7719
7739
  if (requestedIds.length === 0) return null;
@@ -7772,7 +7792,8 @@ async function tryConsolidate(trimmed, ctx, options, prepared) {
7772
7792
  action: "merge",
7773
7793
  memoryId: decision.targetId,
7774
7794
  targetId: decision.targetId,
7775
- proofCount: updated.proofCount ?? (existing.proofCount ?? 1) + 1
7795
+ proofCount: updated.proofCount ?? (existing.proofCount ?? 1) + 1,
7796
+ consolidation: "noop"
7776
7797
  }
7777
7798
  };
7778
7799
  }
@@ -7825,7 +7846,8 @@ async function tryConsolidate(trimmed, ctx, options, prepared) {
7825
7846
  action: "update",
7826
7847
  memoryId: decision.targetId,
7827
7848
  targetId: decision.targetId,
7828
- proofCount: updated.proofCount ?? (existing.proofCount ?? 1) + 1
7849
+ proofCount: updated.proofCount ?? (existing.proofCount ?? 1) + 1,
7850
+ consolidation: "update"
7829
7851
  }
7830
7852
  };
7831
7853
  }
@@ -7967,6 +7989,11 @@ Extract durable user facts.`,
7967
7989
  fallbackSourceId,
7968
7990
  options.userIdentity ?? []
7969
7991
  );
7992
+ const rawCandidates = parsed.candidates;
7993
+ options.onCandidatesParsed?.({
7994
+ rawCount: Array.isArray(rawCandidates) ? rawCandidates.length : 0,
7995
+ validCount: candidates.length
7996
+ });
7970
7997
  if (!redactor) return candidates;
7971
7998
  const restored = restoreCandidates(candidates, redactor, options.userIdentity ?? []);
7972
7999
  if (candidates.length > 0 && restored.length === 0) {
@@ -8003,8 +8030,12 @@ async function extractAndRetain(messages, retainCtx, options) {
8003
8030
  let exhaustedEmpty = false;
8004
8031
  let exhaustedFailure;
8005
8032
  let droppedAfterRedaction = false;
8033
+ let rawCandidateCount = 0;
8034
+ let validCandidateCount = 0;
8006
8035
  const callerOnExhaustedEmpty = options.extract.onExhaustedEmpty;
8007
8036
  const callerOnCandidatesDropped = options.extract.onCandidatesDropped;
8037
+ const callerOnCandidatesParsed = options.extract.onCandidatesParsed;
8038
+ const tExtract = Date.now();
8008
8039
  const candidates = await extractFacts(messages, {
8009
8040
  ...options.extract,
8010
8041
  onExhaustedEmpty: (failure) => {
@@ -8015,8 +8046,14 @@ async function extractAndRetain(messages, retainCtx, options) {
8015
8046
  onCandidatesDropped: () => {
8016
8047
  droppedAfterRedaction = true;
8017
8048
  callerOnCandidatesDropped?.();
8049
+ },
8050
+ onCandidatesParsed: (stats) => {
8051
+ rawCandidateCount = stats.rawCount;
8052
+ validCandidateCount = stats.validCount;
8053
+ callerOnCandidatesParsed?.(stats);
8018
8054
  }
8019
8055
  });
8056
+ const extractMs = Date.now() - tExtract;
8020
8057
  const filtered = candidates.filter((c) => c.confidence >= minConfidence);
8021
8058
  const log = getLogger();
8022
8059
  const screened = screenCandidatesForInjection(filtered);
@@ -8064,6 +8101,7 @@ async function extractAndRetain(messages, retainCtx, options) {
8064
8101
  const results = [];
8065
8102
  const quarantinedInfo = [];
8066
8103
  let failedWrites = 0;
8104
+ const tRetain = Date.now();
8067
8105
  for (const { candidate, isQuarantined, reason, signature } of toRetain) {
8068
8106
  try {
8069
8107
  const result = await retain(candidate.content, retainCtx, {
@@ -8130,6 +8168,21 @@ async function extractAndRetain(messages, retainCtx, options) {
8130
8168
  failedCount: failedWrites,
8131
8169
  outcome,
8132
8170
  quarantined: quarantinedInfo,
8171
+ funnel: {
8172
+ rawCandidateCount,
8173
+ validCandidateCount,
8174
+ afterRedactionCount: candidates.length,
8175
+ aboveConfidenceCount: filtered.length,
8176
+ // The PERSISTED quarantines (`quarantinedInfo`), not the screened list: a
8177
+ // screened candidate whose retain() throws is counted in `failedCount`,
8178
+ // and counting it here too would double it and break the identity below.
8179
+ // This also keeps the count equal to `quarantined.length` on the result.
8180
+ quarantinedCount: quarantinedInfo.length,
8181
+ retainedCount: results.length,
8182
+ failedCount: failedWrites
8183
+ },
8184
+ timings: { extractMs, retainMs: Date.now() - tRetain },
8185
+ model: options.extract.model ?? DEFAULT_EXTRACTION_MODEL,
8133
8186
  // Only meaningful alongside `outcome: "empty-after-retry"`. Returned as well
8134
8187
  // as pushed through the callback so a consumer that only inspects the result
8135
8188
  // (rather than wiring a hook) can still report WHY.
@@ -8383,27 +8436,23 @@ function createAutoExtractor(options) {
8383
8436
  const t0 = Date.now();
8384
8437
  void (async () => {
8385
8438
  try {
8386
- const { candidates, results, failedCount, outcome, failure } = await extractAndRetain(
8387
- window2,
8388
- retainCtx,
8389
- {
8390
- extract,
8391
- ...options.minConfidence !== void 0 && { minConfidence: options.minConfidence },
8392
- ...options.entityCtx !== void 0 && { entityCtx: options.entityCtx },
8393
- ...options.scope !== void 0 && { scope: options.scope },
8394
- ...options.folderId !== void 0 && { folderId: options.folderId },
8395
- ...consolidateOptions !== void 0 && { consolidateOptions },
8396
- ...injectionClassifierOptions !== void 0 && {
8397
- injectionClassifier: injectionClassifierOptions
8398
- },
8399
- ...options.onCandidateFailed && {
8400
- onCandidateFailed: (candidate, error) => options.onCandidateFailed?.({ candidate, error, conversationId })
8401
- },
8402
- ...options.onMemoryQuarantined && {
8403
- onQuarantined: (info) => options.onMemoryQuarantined?.({ ...info, conversationId })
8404
- }
8439
+ const { candidates, results, failedCount, outcome, failure, funnel, timings, model } = await extractAndRetain(window2, retainCtx, {
8440
+ extract,
8441
+ ...options.minConfidence !== void 0 && { minConfidence: options.minConfidence },
8442
+ ...options.entityCtx !== void 0 && { entityCtx: options.entityCtx },
8443
+ ...options.scope !== void 0 && { scope: options.scope },
8444
+ ...options.folderId !== void 0 && { folderId: options.folderId },
8445
+ ...consolidateOptions !== void 0 && { consolidateOptions },
8446
+ ...injectionClassifierOptions !== void 0 && {
8447
+ injectionClassifier: injectionClassifierOptions
8448
+ },
8449
+ ...options.onCandidateFailed && {
8450
+ onCandidateFailed: (candidate, error) => options.onCandidateFailed?.({ candidate, error, conversationId })
8451
+ },
8452
+ ...options.onMemoryQuarantined && {
8453
+ onQuarantined: (info) => options.onMemoryQuarantined?.({ ...info, conversationId })
8405
8454
  }
8406
- );
8455
+ });
8407
8456
  if (outcome !== "empty-after-retry") {
8408
8457
  const advancedTo = window2[window2.length - 1].id;
8409
8458
  stateFor(conversationId).watermark = advancedTo;
@@ -8424,6 +8473,9 @@ function createAutoExtractor(options) {
8424
8473
  durationMs: Date.now() - t0,
8425
8474
  conversationId,
8426
8475
  outcome,
8476
+ funnel,
8477
+ timings,
8478
+ model,
8427
8479
  // Only set alongside `outcome: "empty-after-retry"`. Spread so the key
8428
8480
  // is absent rather than explicitly undefined on a healthy turn —
8429
8481
  // analytics backends store an explicit undefined as a real value.
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  recall
3
- } from "./chunk-M53NXLZS.mjs";
3
+ } from "./chunk-JRVAUZHM.mjs";
4
4
  import "./chunk-WUG6GTHO.mjs";
5
5
  import "./chunk-GYKJOWEO.mjs";
6
6
  export {
@@ -20927,7 +20927,7 @@ function createMemoryVaultSearchTool(vaultCtx, embeddingOptions, cache, searchOp
20927
20927
  subQueries = decomp.subQueries;
20928
20928
  }
20929
20929
  }
20930
- const { recall: recall2 } = await import("./recall-M3KBKU74.mjs");
20930
+ const { recall: recall2 } = await import("./recall-HA4UX4BB.mjs");
20931
20931
  let recallDegraded = [];
20932
20932
  const result = await recall2(
20933
20933
  query,
@@ -21518,7 +21518,22 @@ async function recall(query, ctx, options = {}) {
21518
21518
  let factLaneRankedOnCosine = false;
21519
21519
  let decryptLastRan;
21520
21520
  let vaultRowsDecrypted;
21521
- const emitDiagnostics = (candidateCount) => {
21521
+ let graphLaneCount = 0;
21522
+ let temporalLaneCount = 0;
21523
+ let graphLaneFailed = false;
21524
+ let temporalLaneFailed = false;
21525
+ let hitLimit = false;
21526
+ let factFloor = -1;
21527
+ let chunkFloor = -1;
21528
+ const emptyReasonFor = (admitted, laneRan) => {
21529
+ if (admitted > 0) return "";
21530
+ if (!query || typeof query !== "string" || query.trim().length === 0) return "empty-query";
21531
+ if (!laneRan) return "no-lanes";
21532
+ const chunkLaneCouldAnswer = types.includes("chunk") && !!ctx.storageCtx;
21533
+ if (vaultSize === 0 && !chunkLaneCouldAnswer) return "vault-empty";
21534
+ return "no-candidates";
21535
+ };
21536
+ const emitDiagnostics = (candidateCount, admitted) => {
21522
21537
  const cb = options.onDiagnostics;
21523
21538
  if (!cb) return;
21524
21539
  const degraded = [];
@@ -21529,6 +21544,11 @@ async function recall(query, ctx, options = {}) {
21529
21544
  degraded.push("decompose-moved");
21530
21545
  }
21531
21546
  if (embeddingsUnavailable) degraded.push("embeddings-unavailable");
21547
+ if (graphLaneFailed) degraded.push("graph-lane-failed");
21548
+ if (temporalLaneFailed) degraded.push("temporal-lane-failed");
21549
+ const laneRan = types.includes("fact") && !!ctx.vaultCtx && !!ctx.vaultCache || types.includes("chunk") && !!ctx.storageCtx;
21550
+ const scores = admitted.map((m) => m.score);
21551
+ const minScoreApplied = factResults.length > 0 ? factFloor : chunkResults.length > 0 ? chunkFloor : factFloor >= 0 ? factFloor : chunkFloor;
21532
21552
  const diagnostics = {
21533
21553
  usedBudget,
21534
21554
  reranked: didRerank,
@@ -21539,6 +21559,14 @@ async function recall(query, ctx, options = {}) {
21539
21559
  ...vaultRowsEmbedded !== void 0 && { vaultRowsEmbedded },
21540
21560
  factCount: factResults.length,
21541
21561
  chunkCount: chunkResults.length,
21562
+ admittedCount: admitted.length,
21563
+ topScore: scores.length > 0 ? Math.max(...scores) : -1,
21564
+ lowestAdmittedScore: scores.length > 0 ? Math.min(...scores) : -1,
21565
+ minScoreApplied,
21566
+ truncated: hitLimit,
21567
+ graphLaneCount,
21568
+ temporalLaneCount,
21569
+ emptyReason: emptyReasonFor(admitted.length, laneRan),
21542
21570
  timings: {
21543
21571
  total: nowMs2() - t0,
21544
21572
  prep: prepMs,
@@ -21556,7 +21584,7 @@ async function recall(query, ctx, options = {}) {
21556
21584
  }
21557
21585
  };
21558
21586
  if (!query || typeof query !== "string" || query.trim().length === 0) {
21559
- emitDiagnostics(0);
21587
+ emitDiagnostics(0, []);
21560
21588
  return { memories: [], usedBudget, reranked: false, candidateCount: 0 };
21561
21589
  }
21562
21590
  const needsChunkEmbedding = types.includes("chunk") && ctx.storageCtx;
@@ -21590,6 +21618,7 @@ async function recall(query, ctx, options = {}) {
21590
21618
  // empty ranking instead (mirrors safeCountVault's fail-soft posture).
21591
21619
  safeLane(
21592
21620
  "graph",
21621
+ () => graphLaneFailed = true,
21593
21622
  () => buildGraphLaneRanking(query, ctx, flags.traverse, {
21594
21623
  ...options.maxHops !== void 0 && { maxHops: options.maxHops },
21595
21624
  ...options.entityFanout !== void 0 && { entityFanout: options.entityFanout },
@@ -21598,12 +21627,19 @@ async function recall(query, ctx, options = {}) {
21598
21627
  ...graphRefiner && { refineNeighbors: graphRefiner }
21599
21628
  })
21600
21629
  ),
21601
- wantsTemporal ? safeLane("temporal", () => buildTemporalLaneRanking(query, ctx.vaultCtx, options.now)) : Promise.resolve([])
21630
+ wantsTemporal ? safeLane(
21631
+ "temporal",
21632
+ () => temporalLaneFailed = true,
21633
+ () => buildTemporalLaneRanking(query, ctx.vaultCtx, options.now)
21634
+ ) : Promise.resolve([])
21602
21635
  ]);
21603
21636
  prepMs = nowMs2() - prepStart;
21637
+ graphLaneCount = entityRanking.length;
21638
+ temporalLaneCount = temporalRanking.length;
21604
21639
  if (types.includes("fact") && ctx.vaultCtx && ctx.vaultCache) {
21605
21640
  const factStart = nowMs2();
21606
21641
  const vaultMinScore = options.minScore ?? DEFAULT_FACT_MIN_SCORE;
21642
+ factFloor = vaultMinScore;
21607
21643
  const {
21608
21644
  results,
21609
21645
  vaultSize: size,
@@ -21683,6 +21719,7 @@ async function recall(query, ctx, options = {}) {
21683
21719
  if (types.includes("chunk") && ctx.storageCtx && queryEmbedding) {
21684
21720
  const chunkStart = nowMs2();
21685
21721
  const chunkMinScore = options.minScore ?? DEFAULT_CHUNK_MIN_SCORE;
21722
+ chunkFloor = chunkMinScore;
21686
21723
  const results = await searchChunksOp(ctx.storageCtx, queryEmbedding, {
21687
21724
  limit: types.includes("fact") ? Math.max(limit * 2, 16) : limit,
21688
21725
  minSimilarity: chunkMinScore,
@@ -21709,9 +21746,11 @@ async function recall(query, ctx, options = {}) {
21709
21746
  memories2.sort((a, b) => b.score - a.score);
21710
21747
  const candidateCount = factResults.length + chunkResults.length;
21711
21748
  fuseMs = nowMs2() - fuseStart;
21712
- emitDiagnostics(candidateCount);
21749
+ const admitted = memories2.slice(0, limit);
21750
+ hitLimit = memories2.length > limit;
21751
+ emitDiagnostics(candidateCount, admitted);
21713
21752
  return {
21714
- memories: memories2.slice(0, limit),
21753
+ memories: admitted,
21715
21754
  usedBudget,
21716
21755
  reranked: didRerank,
21717
21756
  candidateCount,
@@ -21741,12 +21780,16 @@ async function recall(query, ctx, options = {}) {
21741
21780
  const ordered = [...byId.values()].sort((a, b) => b.score - a.score);
21742
21781
  const selectWith = (suppressed2) => {
21743
21782
  const out = [];
21783
+ let cut = false;
21744
21784
  for (const m of ordered) {
21745
- if (out.length >= limit) break;
21746
21785
  if (m.kind === "chunk" && m.messageId && suppressed2.has(m.messageId)) continue;
21786
+ if (out.length >= limit) {
21787
+ cut = true;
21788
+ break;
21789
+ }
21747
21790
  out.push(m);
21748
21791
  }
21749
- return out;
21792
+ return { out, cut };
21750
21793
  };
21751
21794
  const provenanceOf = (selected) => {
21752
21795
  const s = /* @__PURE__ */ new Set();
@@ -21756,15 +21799,18 @@ async function recall(query, ctx, options = {}) {
21756
21799
  return s;
21757
21800
  };
21758
21801
  let suppressed = /* @__PURE__ */ new Set();
21759
- let memories = selectWith(suppressed);
21802
+ let selection = selectWith(suppressed);
21803
+ let memories = selection.out;
21760
21804
  for (let i = 0; i < ordered.length; i++) {
21761
21805
  const next = provenanceOf(memories);
21762
21806
  if (next.size === suppressed.size && [...next].every((id) => suppressed.has(id))) break;
21763
21807
  suppressed = next;
21764
- memories = selectWith(suppressed);
21808
+ selection = selectWith(suppressed);
21809
+ memories = selection.out;
21765
21810
  }
21811
+ hitLimit = selection.cut;
21766
21812
  fuseMs = nowMs2() - fuseStart;
21767
- emitDiagnostics(byId.size);
21813
+ emitDiagnostics(byId.size, memories);
21768
21814
  return {
21769
21815
  memories,
21770
21816
  usedBudget,
@@ -21848,10 +21894,11 @@ async function safeCountVault(ctx) {
21848
21894
  return void 0;
21849
21895
  }
21850
21896
  }
21851
- async function safeLane(label, run) {
21897
+ async function safeLane(label, onFailure, run) {
21852
21898
  try {
21853
21899
  return await run();
21854
21900
  } catch (err) {
21901
+ onFailure();
21855
21902
  getLogger().warn(
21856
21903
  `[memory/recall] ${label} lane failed; continuing without it: ${err instanceof Error ? err.message : String(err)}`
21857
21904
  );