@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.
@@ -133,7 +133,7 @@ import {
133
133
  upsertMessageOp,
134
134
  useEncryption,
135
135
  vaultMemoryToStored
136
- } from "./chunk-7HRJOHW4.mjs";
136
+ } from "./chunk-CIMZZ7GQ.mjs";
137
137
  import {
138
138
  PII_PATTERNS,
139
139
  PiiRedactor,
@@ -6646,6 +6646,7 @@ async function retain(content, ctx, options = {}) {
6646
6646
  const threshold = options.autoMergeThreshold ?? DEFAULT_AUTO_MERGE_THRESHOLD;
6647
6647
  const resolvedScope = options.scope ?? DEFAULT_SCOPE;
6648
6648
  let supersedeTargetIds = [];
6649
+ let consolidationDecidedCreate = false;
6649
6650
  let supersedeContent;
6650
6651
  let prepared;
6651
6652
  if (enableAutoMerge) {
@@ -6673,8 +6674,12 @@ async function retain(content, ctx, options = {}) {
6673
6674
  const outcome = await tryConsolidate(trimmed, ctx, options, prepared);
6674
6675
  if (outcome) {
6675
6676
  if ("done" in outcome) return outcome.done;
6676
- supersedeTargetIds = outcome.supersede;
6677
- supersedeContent = outcome.content;
6677
+ if ("create" in outcome) {
6678
+ consolidationDecidedCreate = true;
6679
+ } else {
6680
+ supersedeTargetIds = outcome.supersede;
6681
+ supersedeContent = outcome.content;
6682
+ }
6678
6683
  }
6679
6684
  }
6680
6685
  if (supersedeTargetIds.length === 0) {
@@ -6724,7 +6729,14 @@ async function retain(content, ctx, options = {}) {
6724
6729
  action: "merge",
6725
6730
  memoryId: targetId,
6726
6731
  targetId,
6727
- proofCount: updated.proofCount ?? (existing.proofCount ?? 1) + 1
6732
+ proofCount: updated.proofCount ?? (existing.proofCount ?? 1) + 1,
6733
+ similarity: matches[0].similarity,
6734
+ // Stage 1 may have run and explicitly said `create`, and Stage 2
6735
+ // then found a strict-cosine match anyway. Reporting the decision
6736
+ // here is what makes that DISAGREEMENT visible — without it the
6737
+ // model's create silently vanished from the distribution whenever
6738
+ // the cosine stage won.
6739
+ ...consolidationDecidedCreate && { consolidation: "create" }
6728
6740
  };
6729
6741
  }
6730
6742
  await assertMergeTargetGoneOrThrow(ctx, targetId);
@@ -6737,12 +6749,18 @@ async function retain(content, ctx, options = {}) {
6737
6749
  const embedding = reusableQueryEmbedding ?? await generateEmbedding(contentToWrite, ctx.embeddingOptions);
6738
6750
  const embeddingModel = ctx.embeddingOptions.model ?? DEFAULT_API_EMBEDDING_MODEL;
6739
6751
  if (options.respectTombstones) {
6740
- const tombstoneId = await findTombstoneMatch(embedding, embeddingModel, resolvedScope, ctx, {
6752
+ const tombstone = await findTombstoneMatch(embedding, embeddingModel, resolvedScope, ctx, {
6741
6753
  threshold,
6742
6754
  folderId: options.folderId
6743
6755
  });
6744
- if (tombstoneId) {
6745
- return { action: "suppressed", memoryId: tombstoneId, tombstoneId, proofCount: 0 };
6756
+ if (tombstone) {
6757
+ return {
6758
+ action: "suppressed",
6759
+ memoryId: tombstone.id,
6760
+ tombstoneId: tombstone.id,
6761
+ proofCount: 0,
6762
+ similarity: tombstone.similarity
6763
+ };
6746
6764
  }
6747
6765
  }
6748
6766
  const createOpts = {
@@ -6809,7 +6827,8 @@ async function retain(content, ctx, options = {}) {
6809
6827
  action: "supersede",
6810
6828
  memoryId: created2.uniqueId,
6811
6829
  targetId: primaryTargetId,
6812
- proofCount: 1
6830
+ proofCount: 1,
6831
+ consolidation: "supersede"
6813
6832
  };
6814
6833
  }
6815
6834
  notifyConsolidationFallback(
@@ -6823,7 +6842,8 @@ async function retain(content, ctx, options = {}) {
6823
6842
  return {
6824
6843
  action: "create",
6825
6844
  memoryId: created.uniqueId,
6826
- proofCount: 1
6845
+ proofCount: 1,
6846
+ ...consolidationDecidedCreate && { consolidation: "create" }
6827
6847
  };
6828
6848
  }
6829
6849
  function unionStrings(a, b) {
@@ -6853,7 +6873,7 @@ async function findTombstoneMatch(embedding, embeddingModel, scope, ctx, opts) {
6853
6873
  bestId = row.uniqueId;
6854
6874
  }
6855
6875
  }
6856
- return bestId;
6876
+ return bestId === null ? null : { id: bestId, similarity: bestSim };
6857
6877
  }
6858
6878
  function pickEventTimeUpdate(existing, incoming) {
6859
6879
  if (incoming === void 0 || incoming === null) return void 0;
@@ -6895,7 +6915,7 @@ async function tryConsolidate(trimmed, ctx, options, prepared) {
6895
6915
  }));
6896
6916
  const { consolidateMemory: doConsolidate } = await import("./consolidate-XBYKAFKT.mjs");
6897
6917
  const decision = await doConsolidate(trimmed, candidates, consolidateOptions);
6898
- if (decision.action === "create") return null;
6918
+ if (decision.action === "create") return decision.fallbackReason ? null : { create: true };
6899
6919
  if (decision.action === "supersede" && decision.content) {
6900
6920
  const requestedIds = decision.targetIds?.length ? decision.targetIds : decision.targetId ? [decision.targetId] : [];
6901
6921
  if (requestedIds.length === 0) return null;
@@ -6954,7 +6974,8 @@ async function tryConsolidate(trimmed, ctx, options, prepared) {
6954
6974
  action: "merge",
6955
6975
  memoryId: decision.targetId,
6956
6976
  targetId: decision.targetId,
6957
- proofCount: updated.proofCount ?? (existing.proofCount ?? 1) + 1
6977
+ proofCount: updated.proofCount ?? (existing.proofCount ?? 1) + 1,
6978
+ consolidation: "noop"
6958
6979
  }
6959
6980
  };
6960
6981
  }
@@ -7007,7 +7028,8 @@ async function tryConsolidate(trimmed, ctx, options, prepared) {
7007
7028
  action: "update",
7008
7029
  memoryId: decision.targetId,
7009
7030
  targetId: decision.targetId,
7010
- proofCount: updated.proofCount ?? (existing.proofCount ?? 1) + 1
7031
+ proofCount: updated.proofCount ?? (existing.proofCount ?? 1) + 1,
7032
+ consolidation: "update"
7011
7033
  }
7012
7034
  };
7013
7035
  }
@@ -7149,6 +7171,11 @@ Extract durable user facts.`,
7149
7171
  fallbackSourceId,
7150
7172
  options.userIdentity ?? []
7151
7173
  );
7174
+ const rawCandidates = parsed.candidates;
7175
+ options.onCandidatesParsed?.({
7176
+ rawCount: Array.isArray(rawCandidates) ? rawCandidates.length : 0,
7177
+ validCount: candidates.length
7178
+ });
7152
7179
  if (!redactor) return candidates;
7153
7180
  const restored = restoreCandidates(candidates, redactor, options.userIdentity ?? []);
7154
7181
  if (candidates.length > 0 && restored.length === 0) {
@@ -7185,8 +7212,12 @@ async function extractAndRetain(messages, retainCtx, options) {
7185
7212
  let exhaustedEmpty = false;
7186
7213
  let exhaustedFailure;
7187
7214
  let droppedAfterRedaction = false;
7215
+ let rawCandidateCount = 0;
7216
+ let validCandidateCount = 0;
7188
7217
  const callerOnExhaustedEmpty = options.extract.onExhaustedEmpty;
7189
7218
  const callerOnCandidatesDropped = options.extract.onCandidatesDropped;
7219
+ const callerOnCandidatesParsed = options.extract.onCandidatesParsed;
7220
+ const tExtract = Date.now();
7190
7221
  const candidates = await extractFacts(messages, {
7191
7222
  ...options.extract,
7192
7223
  onExhaustedEmpty: (failure) => {
@@ -7197,8 +7228,14 @@ async function extractAndRetain(messages, retainCtx, options) {
7197
7228
  onCandidatesDropped: () => {
7198
7229
  droppedAfterRedaction = true;
7199
7230
  callerOnCandidatesDropped?.();
7231
+ },
7232
+ onCandidatesParsed: (stats) => {
7233
+ rawCandidateCount = stats.rawCount;
7234
+ validCandidateCount = stats.validCount;
7235
+ callerOnCandidatesParsed?.(stats);
7200
7236
  }
7201
7237
  });
7238
+ const extractMs = Date.now() - tExtract;
7202
7239
  const filtered = candidates.filter((c) => c.confidence >= minConfidence);
7203
7240
  const log = getLogger();
7204
7241
  const screened = screenCandidatesForInjection(filtered);
@@ -7246,6 +7283,7 @@ async function extractAndRetain(messages, retainCtx, options) {
7246
7283
  const results = [];
7247
7284
  const quarantinedInfo = [];
7248
7285
  let failedWrites = 0;
7286
+ const tRetain = Date.now();
7249
7287
  for (const { candidate, isQuarantined, reason, signature } of toRetain) {
7250
7288
  try {
7251
7289
  const result = await retain(candidate.content, retainCtx, {
@@ -7312,6 +7350,21 @@ async function extractAndRetain(messages, retainCtx, options) {
7312
7350
  failedCount: failedWrites,
7313
7351
  outcome,
7314
7352
  quarantined: quarantinedInfo,
7353
+ funnel: {
7354
+ rawCandidateCount,
7355
+ validCandidateCount,
7356
+ afterRedactionCount: candidates.length,
7357
+ aboveConfidenceCount: filtered.length,
7358
+ // The PERSISTED quarantines (`quarantinedInfo`), not the screened list: a
7359
+ // screened candidate whose retain() throws is counted in `failedCount`,
7360
+ // and counting it here too would double it and break the identity below.
7361
+ // This also keeps the count equal to `quarantined.length` on the result.
7362
+ quarantinedCount: quarantinedInfo.length,
7363
+ retainedCount: results.length,
7364
+ failedCount: failedWrites
7365
+ },
7366
+ timings: { extractMs, retainMs: Date.now() - tRetain },
7367
+ model: options.extract.model ?? DEFAULT_EXTRACTION_MODEL,
7315
7368
  // Only meaningful alongside `outcome: "empty-after-retry"`. Returned as well
7316
7369
  // as pushed through the callback so a consumer that only inspects the result
7317
7370
  // (rather than wiring a hook) can still report WHY.
@@ -7565,27 +7618,23 @@ function createAutoExtractor(options) {
7565
7618
  const t0 = Date.now();
7566
7619
  void (async () => {
7567
7620
  try {
7568
- const { candidates, results, failedCount, outcome, failure } = await extractAndRetain(
7569
- window,
7570
- retainCtx,
7571
- {
7572
- extract,
7573
- ...options.minConfidence !== void 0 && { minConfidence: options.minConfidence },
7574
- ...options.entityCtx !== void 0 && { entityCtx: options.entityCtx },
7575
- ...options.scope !== void 0 && { scope: options.scope },
7576
- ...options.folderId !== void 0 && { folderId: options.folderId },
7577
- ...consolidateOptions !== void 0 && { consolidateOptions },
7578
- ...injectionClassifierOptions !== void 0 && {
7579
- injectionClassifier: injectionClassifierOptions
7580
- },
7581
- ...options.onCandidateFailed && {
7582
- onCandidateFailed: (candidate, error) => options.onCandidateFailed?.({ candidate, error, conversationId })
7583
- },
7584
- ...options.onMemoryQuarantined && {
7585
- onQuarantined: (info) => options.onMemoryQuarantined?.({ ...info, conversationId })
7586
- }
7621
+ const { candidates, results, failedCount, outcome, failure, funnel, timings, model } = await extractAndRetain(window, retainCtx, {
7622
+ extract,
7623
+ ...options.minConfidence !== void 0 && { minConfidence: options.minConfidence },
7624
+ ...options.entityCtx !== void 0 && { entityCtx: options.entityCtx },
7625
+ ...options.scope !== void 0 && { scope: options.scope },
7626
+ ...options.folderId !== void 0 && { folderId: options.folderId },
7627
+ ...consolidateOptions !== void 0 && { consolidateOptions },
7628
+ ...injectionClassifierOptions !== void 0 && {
7629
+ injectionClassifier: injectionClassifierOptions
7630
+ },
7631
+ ...options.onCandidateFailed && {
7632
+ onCandidateFailed: (candidate, error) => options.onCandidateFailed?.({ candidate, error, conversationId })
7633
+ },
7634
+ ...options.onMemoryQuarantined && {
7635
+ onQuarantined: (info) => options.onMemoryQuarantined?.({ ...info, conversationId })
7587
7636
  }
7588
- );
7637
+ });
7589
7638
  if (outcome !== "empty-after-retry") {
7590
7639
  const advancedTo = window[window.length - 1].id;
7591
7640
  stateFor(conversationId).watermark = advancedTo;
@@ -7606,6 +7655,9 @@ function createAutoExtractor(options) {
7606
7655
  durationMs: Date.now() - t0,
7607
7656
  conversationId,
7608
7657
  outcome,
7658
+ funnel,
7659
+ timings,
7660
+ model,
7609
7661
  // Only set alongside `outcome: "empty-after-retry"`. Spread so the key
7610
7662
  // is absent rather than explicitly undefined on a healthy turn —
7611
7663
  // analytics backends store an explicit undefined as a real value.
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  recall
3
- } from "./chunk-7HRJOHW4.mjs";
3
+ } from "./chunk-CIMZZ7GQ.mjs";
4
4
  import "./chunk-WUG6GTHO.mjs";
5
5
  import "./chunk-GYKJOWEO.mjs";
6
6
  export {
@@ -7196,7 +7196,7 @@ function createMemoryVaultSearchTool(vaultCtx, embeddingOptions, cache, searchOp
7196
7196
  subQueries = decomp.subQueries;
7197
7197
  }
7198
7198
  }
7199
- const { recall: recall2 } = await import("./recall-B3M4BWQ2.mjs");
7199
+ const { recall: recall2 } = await import("./recall-7Q3V337H.mjs");
7200
7200
  let recallDegraded = [];
7201
7201
  const result = await recall2(
7202
7202
  query,
@@ -7787,7 +7787,22 @@ async function recall(query, ctx, options = {}) {
7787
7787
  let factLaneRankedOnCosine = false;
7788
7788
  let decryptLastRan;
7789
7789
  let vaultRowsDecrypted;
7790
- const emitDiagnostics = (candidateCount) => {
7790
+ let graphLaneCount = 0;
7791
+ let temporalLaneCount = 0;
7792
+ let graphLaneFailed = false;
7793
+ let temporalLaneFailed = false;
7794
+ let hitLimit = false;
7795
+ let factFloor = -1;
7796
+ let chunkFloor = -1;
7797
+ const emptyReasonFor = (admitted, laneRan) => {
7798
+ if (admitted > 0) return "";
7799
+ if (!query || typeof query !== "string" || query.trim().length === 0) return "empty-query";
7800
+ if (!laneRan) return "no-lanes";
7801
+ const chunkLaneCouldAnswer = types.includes("chunk") && !!ctx.storageCtx;
7802
+ if (vaultSize === 0 && !chunkLaneCouldAnswer) return "vault-empty";
7803
+ return "no-candidates";
7804
+ };
7805
+ const emitDiagnostics = (candidateCount, admitted) => {
7791
7806
  const cb = options.onDiagnostics;
7792
7807
  if (!cb) return;
7793
7808
  const degraded = [];
@@ -7798,6 +7813,11 @@ async function recall(query, ctx, options = {}) {
7798
7813
  degraded.push("decompose-moved");
7799
7814
  }
7800
7815
  if (embeddingsUnavailable) degraded.push("embeddings-unavailable");
7816
+ if (graphLaneFailed) degraded.push("graph-lane-failed");
7817
+ if (temporalLaneFailed) degraded.push("temporal-lane-failed");
7818
+ const laneRan = types.includes("fact") && !!ctx.vaultCtx && !!ctx.vaultCache || types.includes("chunk") && !!ctx.storageCtx;
7819
+ const scores = admitted.map((m) => m.score);
7820
+ const minScoreApplied = factResults.length > 0 ? factFloor : chunkResults.length > 0 ? chunkFloor : factFloor >= 0 ? factFloor : chunkFloor;
7801
7821
  const diagnostics = {
7802
7822
  usedBudget,
7803
7823
  reranked: didRerank,
@@ -7808,6 +7828,14 @@ async function recall(query, ctx, options = {}) {
7808
7828
  ...vaultRowsEmbedded !== void 0 && { vaultRowsEmbedded },
7809
7829
  factCount: factResults.length,
7810
7830
  chunkCount: chunkResults.length,
7831
+ admittedCount: admitted.length,
7832
+ topScore: scores.length > 0 ? Math.max(...scores) : -1,
7833
+ lowestAdmittedScore: scores.length > 0 ? Math.min(...scores) : -1,
7834
+ minScoreApplied,
7835
+ truncated: hitLimit,
7836
+ graphLaneCount,
7837
+ temporalLaneCount,
7838
+ emptyReason: emptyReasonFor(admitted.length, laneRan),
7811
7839
  timings: {
7812
7840
  total: nowMs2() - t0,
7813
7841
  prep: prepMs,
@@ -7825,7 +7853,7 @@ async function recall(query, ctx, options = {}) {
7825
7853
  }
7826
7854
  };
7827
7855
  if (!query || typeof query !== "string" || query.trim().length === 0) {
7828
- emitDiagnostics(0);
7856
+ emitDiagnostics(0, []);
7829
7857
  return { memories: [], usedBudget, reranked: false, candidateCount: 0 };
7830
7858
  }
7831
7859
  const needsChunkEmbedding = types.includes("chunk") && ctx.storageCtx;
@@ -7859,6 +7887,7 @@ async function recall(query, ctx, options = {}) {
7859
7887
  // empty ranking instead (mirrors safeCountVault's fail-soft posture).
7860
7888
  safeLane(
7861
7889
  "graph",
7890
+ () => graphLaneFailed = true,
7862
7891
  () => buildGraphLaneRanking(query, ctx, flags.traverse, {
7863
7892
  ...options.maxHops !== void 0 && { maxHops: options.maxHops },
7864
7893
  ...options.entityFanout !== void 0 && { entityFanout: options.entityFanout },
@@ -7867,12 +7896,19 @@ async function recall(query, ctx, options = {}) {
7867
7896
  ...graphRefiner && { refineNeighbors: graphRefiner }
7868
7897
  })
7869
7898
  ),
7870
- wantsTemporal ? safeLane("temporal", () => buildTemporalLaneRanking(query, ctx.vaultCtx, options.now)) : Promise.resolve([])
7899
+ wantsTemporal ? safeLane(
7900
+ "temporal",
7901
+ () => temporalLaneFailed = true,
7902
+ () => buildTemporalLaneRanking(query, ctx.vaultCtx, options.now)
7903
+ ) : Promise.resolve([])
7871
7904
  ]);
7872
7905
  prepMs = nowMs2() - prepStart;
7906
+ graphLaneCount = entityRanking.length;
7907
+ temporalLaneCount = temporalRanking.length;
7873
7908
  if (types.includes("fact") && ctx.vaultCtx && ctx.vaultCache) {
7874
7909
  const factStart = nowMs2();
7875
7910
  const vaultMinScore = options.minScore ?? DEFAULT_FACT_MIN_SCORE;
7911
+ factFloor = vaultMinScore;
7876
7912
  const {
7877
7913
  results,
7878
7914
  vaultSize: size,
@@ -7952,6 +7988,7 @@ async function recall(query, ctx, options = {}) {
7952
7988
  if (types.includes("chunk") && ctx.storageCtx && queryEmbedding) {
7953
7989
  const chunkStart = nowMs2();
7954
7990
  const chunkMinScore = options.minScore ?? DEFAULT_CHUNK_MIN_SCORE;
7991
+ chunkFloor = chunkMinScore;
7955
7992
  const results = await searchChunksOp(ctx.storageCtx, queryEmbedding, {
7956
7993
  limit: types.includes("fact") ? Math.max(limit * 2, 16) : limit,
7957
7994
  minSimilarity: chunkMinScore,
@@ -7978,9 +8015,11 @@ async function recall(query, ctx, options = {}) {
7978
8015
  memories2.sort((a, b) => b.score - a.score);
7979
8016
  const candidateCount = factResults.length + chunkResults.length;
7980
8017
  fuseMs = nowMs2() - fuseStart;
7981
- emitDiagnostics(candidateCount);
8018
+ const admitted = memories2.slice(0, limit);
8019
+ hitLimit = memories2.length > limit;
8020
+ emitDiagnostics(candidateCount, admitted);
7982
8021
  return {
7983
- memories: memories2.slice(0, limit),
8022
+ memories: admitted,
7984
8023
  usedBudget,
7985
8024
  reranked: didRerank,
7986
8025
  candidateCount,
@@ -8010,12 +8049,16 @@ async function recall(query, ctx, options = {}) {
8010
8049
  const ordered = [...byId.values()].sort((a, b) => b.score - a.score);
8011
8050
  const selectWith = (suppressed2) => {
8012
8051
  const out = [];
8052
+ let cut = false;
8013
8053
  for (const m of ordered) {
8014
- if (out.length >= limit) break;
8015
8054
  if (m.kind === "chunk" && m.messageId && suppressed2.has(m.messageId)) continue;
8055
+ if (out.length >= limit) {
8056
+ cut = true;
8057
+ break;
8058
+ }
8016
8059
  out.push(m);
8017
8060
  }
8018
- return out;
8061
+ return { out, cut };
8019
8062
  };
8020
8063
  const provenanceOf = (selected) => {
8021
8064
  const s = /* @__PURE__ */ new Set();
@@ -8025,15 +8068,18 @@ async function recall(query, ctx, options = {}) {
8025
8068
  return s;
8026
8069
  };
8027
8070
  let suppressed = /* @__PURE__ */ new Set();
8028
- let memories = selectWith(suppressed);
8071
+ let selection = selectWith(suppressed);
8072
+ let memories = selection.out;
8029
8073
  for (let i = 0; i < ordered.length; i++) {
8030
8074
  const next = provenanceOf(memories);
8031
8075
  if (next.size === suppressed.size && [...next].every((id) => suppressed.has(id))) break;
8032
8076
  suppressed = next;
8033
- memories = selectWith(suppressed);
8077
+ selection = selectWith(suppressed);
8078
+ memories = selection.out;
8034
8079
  }
8080
+ hitLimit = selection.cut;
8035
8081
  fuseMs = nowMs2() - fuseStart;
8036
- emitDiagnostics(byId.size);
8082
+ emitDiagnostics(byId.size, memories);
8037
8083
  return {
8038
8084
  memories,
8039
8085
  usedBudget,
@@ -8117,10 +8163,11 @@ async function safeCountVault(ctx) {
8117
8163
  return void 0;
8118
8164
  }
8119
8165
  }
8120
- async function safeLane(label, run) {
8166
+ async function safeLane(label, onFailure, run) {
8121
8167
  try {
8122
8168
  return await run();
8123
8169
  } catch (err) {
8170
+ onFailure();
8124
8171
  getLogger().warn(
8125
8172
  `[memory/recall] ${label} lane failed; continuing without it: ${err instanceof Error ? err.message : String(err)}`
8126
8173
  );