@anuma/sdk 6.19.1 → 6.20.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.
@@ -141,7 +141,7 @@ import {
141
141
  updateVaultMemoryOp,
142
142
  upsertMessageOp,
143
143
  vaultMemoryToStored
144
- } from "./chunk-SPHKT76E.mjs";
144
+ } from "./chunk-X5EZQFGR.mjs";
145
145
  import {
146
146
  createStreamingDeAnonymizer,
147
147
  notifyConsolidationFallback,
@@ -5496,6 +5496,7 @@ async function retain(content, ctx, options = {}) {
5496
5496
  const threshold = options.autoMergeThreshold ?? DEFAULT_AUTO_MERGE_THRESHOLD;
5497
5497
  const resolvedScope = options.scope ?? DEFAULT_SCOPE;
5498
5498
  let supersedeTargetIds = [];
5499
+ let consolidationDecidedCreate = false;
5499
5500
  let supersedeContent;
5500
5501
  let prepared;
5501
5502
  if (enableAutoMerge) {
@@ -5523,8 +5524,12 @@ async function retain(content, ctx, options = {}) {
5523
5524
  const outcome = await tryConsolidate(trimmed, ctx, options, prepared);
5524
5525
  if (outcome) {
5525
5526
  if ("done" in outcome) return outcome.done;
5526
- supersedeTargetIds = outcome.supersede;
5527
- supersedeContent = outcome.content;
5527
+ if ("create" in outcome) {
5528
+ consolidationDecidedCreate = true;
5529
+ } else {
5530
+ supersedeTargetIds = outcome.supersede;
5531
+ supersedeContent = outcome.content;
5532
+ }
5528
5533
  }
5529
5534
  }
5530
5535
  if (supersedeTargetIds.length === 0) {
@@ -5574,7 +5579,14 @@ async function retain(content, ctx, options = {}) {
5574
5579
  action: "merge",
5575
5580
  memoryId: targetId,
5576
5581
  targetId,
5577
- proofCount: updated.proofCount ?? (existing.proofCount ?? 1) + 1
5582
+ proofCount: updated.proofCount ?? (existing.proofCount ?? 1) + 1,
5583
+ similarity: matches[0].similarity,
5584
+ // Stage 1 may have run and explicitly said `create`, and Stage 2
5585
+ // then found a strict-cosine match anyway. Reporting the decision
5586
+ // here is what makes that DISAGREEMENT visible — without it the
5587
+ // model's create silently vanished from the distribution whenever
5588
+ // the cosine stage won.
5589
+ ...consolidationDecidedCreate && { consolidation: "create" }
5578
5590
  };
5579
5591
  }
5580
5592
  await assertMergeTargetGoneOrThrow(ctx, targetId);
@@ -5587,12 +5599,18 @@ async function retain(content, ctx, options = {}) {
5587
5599
  const embedding = reusableQueryEmbedding ?? await generateEmbedding(contentToWrite, ctx.embeddingOptions);
5588
5600
  const embeddingModel = ctx.embeddingOptions.model ?? DEFAULT_API_EMBEDDING_MODEL;
5589
5601
  if (options.respectTombstones) {
5590
- const tombstoneId = await findTombstoneMatch(embedding, embeddingModel, resolvedScope, ctx, {
5602
+ const tombstone = await findTombstoneMatch(embedding, embeddingModel, resolvedScope, ctx, {
5591
5603
  threshold,
5592
5604
  folderId: options.folderId
5593
5605
  });
5594
- if (tombstoneId) {
5595
- return { action: "suppressed", memoryId: tombstoneId, tombstoneId, proofCount: 0 };
5606
+ if (tombstone) {
5607
+ return {
5608
+ action: "suppressed",
5609
+ memoryId: tombstone.id,
5610
+ tombstoneId: tombstone.id,
5611
+ proofCount: 0,
5612
+ similarity: tombstone.similarity
5613
+ };
5596
5614
  }
5597
5615
  }
5598
5616
  const createOpts = {
@@ -5659,7 +5677,8 @@ async function retain(content, ctx, options = {}) {
5659
5677
  action: "supersede",
5660
5678
  memoryId: created2.uniqueId,
5661
5679
  targetId: primaryTargetId,
5662
- proofCount: 1
5680
+ proofCount: 1,
5681
+ consolidation: "supersede"
5663
5682
  };
5664
5683
  }
5665
5684
  notifyConsolidationFallback(
@@ -5673,7 +5692,8 @@ async function retain(content, ctx, options = {}) {
5673
5692
  return {
5674
5693
  action: "create",
5675
5694
  memoryId: created.uniqueId,
5676
- proofCount: 1
5695
+ proofCount: 1,
5696
+ ...consolidationDecidedCreate && { consolidation: "create" }
5677
5697
  };
5678
5698
  }
5679
5699
  function unionStrings(a, b) {
@@ -5703,7 +5723,7 @@ async function findTombstoneMatch(embedding, embeddingModel, scope, ctx, opts) {
5703
5723
  bestId = row.uniqueId;
5704
5724
  }
5705
5725
  }
5706
- return bestId;
5726
+ return bestId === null ? null : { id: bestId, similarity: bestSim };
5707
5727
  }
5708
5728
  function pickEventTimeUpdate(existing, incoming) {
5709
5729
  if (incoming === void 0 || incoming === null) return void 0;
@@ -5745,7 +5765,7 @@ async function tryConsolidate(trimmed, ctx, options, prepared) {
5745
5765
  }));
5746
5766
  const { consolidateMemory: doConsolidate } = await import("./consolidate-54P6QZKU.mjs");
5747
5767
  const decision = await doConsolidate(trimmed, candidates, consolidateOptions);
5748
- if (decision.action === "create") return null;
5768
+ if (decision.action === "create") return decision.fallbackReason ? null : { create: true };
5749
5769
  if (decision.action === "supersede" && decision.content) {
5750
5770
  const requestedIds = decision.targetIds?.length ? decision.targetIds : decision.targetId ? [decision.targetId] : [];
5751
5771
  if (requestedIds.length === 0) return null;
@@ -5804,7 +5824,8 @@ async function tryConsolidate(trimmed, ctx, options, prepared) {
5804
5824
  action: "merge",
5805
5825
  memoryId: decision.targetId,
5806
5826
  targetId: decision.targetId,
5807
- proofCount: updated.proofCount ?? (existing.proofCount ?? 1) + 1
5827
+ proofCount: updated.proofCount ?? (existing.proofCount ?? 1) + 1,
5828
+ consolidation: "noop"
5808
5829
  }
5809
5830
  };
5810
5831
  }
@@ -5857,7 +5878,8 @@ async function tryConsolidate(trimmed, ctx, options, prepared) {
5857
5878
  action: "update",
5858
5879
  memoryId: decision.targetId,
5859
5880
  targetId: decision.targetId,
5860
- proofCount: updated.proofCount ?? (existing.proofCount ?? 1) + 1
5881
+ proofCount: updated.proofCount ?? (existing.proofCount ?? 1) + 1,
5882
+ consolidation: "update"
5861
5883
  }
5862
5884
  };
5863
5885
  }
@@ -5999,6 +6021,11 @@ Extract durable user facts.`,
5999
6021
  fallbackSourceId,
6000
6022
  options.userIdentity ?? []
6001
6023
  );
6024
+ const rawCandidates = parsed.candidates;
6025
+ options.onCandidatesParsed?.({
6026
+ rawCount: Array.isArray(rawCandidates) ? rawCandidates.length : 0,
6027
+ validCount: candidates.length
6028
+ });
6002
6029
  if (!redactor) return candidates;
6003
6030
  const restored = restoreCandidates(candidates, redactor, options.userIdentity ?? []);
6004
6031
  if (candidates.length > 0 && restored.length === 0) {
@@ -6035,8 +6062,12 @@ async function extractAndRetain(messages, retainCtx, options) {
6035
6062
  let exhaustedEmpty = false;
6036
6063
  let exhaustedFailure;
6037
6064
  let droppedAfterRedaction = false;
6065
+ let rawCandidateCount = 0;
6066
+ let validCandidateCount = 0;
6038
6067
  const callerOnExhaustedEmpty = options.extract.onExhaustedEmpty;
6039
6068
  const callerOnCandidatesDropped = options.extract.onCandidatesDropped;
6069
+ const callerOnCandidatesParsed = options.extract.onCandidatesParsed;
6070
+ const tExtract = Date.now();
6040
6071
  const candidates = await extractFacts(messages, {
6041
6072
  ...options.extract,
6042
6073
  onExhaustedEmpty: (failure) => {
@@ -6047,8 +6078,14 @@ async function extractAndRetain(messages, retainCtx, options) {
6047
6078
  onCandidatesDropped: () => {
6048
6079
  droppedAfterRedaction = true;
6049
6080
  callerOnCandidatesDropped?.();
6081
+ },
6082
+ onCandidatesParsed: (stats) => {
6083
+ rawCandidateCount = stats.rawCount;
6084
+ validCandidateCount = stats.validCount;
6085
+ callerOnCandidatesParsed?.(stats);
6050
6086
  }
6051
6087
  });
6088
+ const extractMs = Date.now() - tExtract;
6052
6089
  const filtered = candidates.filter((c) => c.confidence >= minConfidence);
6053
6090
  const log = getLogger();
6054
6091
  const screened = screenCandidatesForInjection(filtered);
@@ -6096,6 +6133,7 @@ async function extractAndRetain(messages, retainCtx, options) {
6096
6133
  const results = [];
6097
6134
  const quarantinedInfo = [];
6098
6135
  let failedWrites = 0;
6136
+ const tRetain = Date.now();
6099
6137
  for (const { candidate, isQuarantined, reason, signature } of toRetain) {
6100
6138
  try {
6101
6139
  const result = await retain(candidate.content, retainCtx, {
@@ -6162,6 +6200,21 @@ async function extractAndRetain(messages, retainCtx, options) {
6162
6200
  failedCount: failedWrites,
6163
6201
  outcome,
6164
6202
  quarantined: quarantinedInfo,
6203
+ funnel: {
6204
+ rawCandidateCount,
6205
+ validCandidateCount,
6206
+ afterRedactionCount: candidates.length,
6207
+ aboveConfidenceCount: filtered.length,
6208
+ // The PERSISTED quarantines (`quarantinedInfo`), not the screened list: a
6209
+ // screened candidate whose retain() throws is counted in `failedCount`,
6210
+ // and counting it here too would double it and break the identity below.
6211
+ // This also keeps the count equal to `quarantined.length` on the result.
6212
+ quarantinedCount: quarantinedInfo.length,
6213
+ retainedCount: results.length,
6214
+ failedCount: failedWrites
6215
+ },
6216
+ timings: { extractMs, retainMs: Date.now() - tRetain },
6217
+ model: options.extract.model ?? DEFAULT_EXTRACTION_MODEL,
6165
6218
  // Only meaningful alongside `outcome: "empty-after-retry"`. Returned as well
6166
6219
  // as pushed through the callback so a consumer that only inspects the result
6167
6220
  // (rather than wiring a hook) can still report WHY.
@@ -6415,27 +6468,23 @@ function createAutoExtractor(options) {
6415
6468
  const t0 = Date.now();
6416
6469
  void (async () => {
6417
6470
  try {
6418
- const { candidates, results, failedCount, outcome, failure } = await extractAndRetain(
6419
- window2,
6420
- retainCtx,
6421
- {
6422
- extract,
6423
- ...options.minConfidence !== void 0 && { minConfidence: options.minConfidence },
6424
- ...options.entityCtx !== void 0 && { entityCtx: options.entityCtx },
6425
- ...options.scope !== void 0 && { scope: options.scope },
6426
- ...options.folderId !== void 0 && { folderId: options.folderId },
6427
- ...consolidateOptions !== void 0 && { consolidateOptions },
6428
- ...injectionClassifierOptions !== void 0 && {
6429
- injectionClassifier: injectionClassifierOptions
6430
- },
6431
- ...options.onCandidateFailed && {
6432
- onCandidateFailed: (candidate, error) => options.onCandidateFailed?.({ candidate, error, conversationId })
6433
- },
6434
- ...options.onMemoryQuarantined && {
6435
- onQuarantined: (info) => options.onMemoryQuarantined?.({ ...info, conversationId })
6436
- }
6471
+ const { candidates, results, failedCount, outcome, failure, funnel, timings, model } = await extractAndRetain(window2, retainCtx, {
6472
+ extract,
6473
+ ...options.minConfidence !== void 0 && { minConfidence: options.minConfidence },
6474
+ ...options.entityCtx !== void 0 && { entityCtx: options.entityCtx },
6475
+ ...options.scope !== void 0 && { scope: options.scope },
6476
+ ...options.folderId !== void 0 && { folderId: options.folderId },
6477
+ ...consolidateOptions !== void 0 && { consolidateOptions },
6478
+ ...injectionClassifierOptions !== void 0 && {
6479
+ injectionClassifier: injectionClassifierOptions
6480
+ },
6481
+ ...options.onCandidateFailed && {
6482
+ onCandidateFailed: (candidate, error) => options.onCandidateFailed?.({ candidate, error, conversationId })
6483
+ },
6484
+ ...options.onMemoryQuarantined && {
6485
+ onQuarantined: (info) => options.onMemoryQuarantined?.({ ...info, conversationId })
6437
6486
  }
6438
- );
6487
+ });
6439
6488
  if (outcome !== "empty-after-retry") {
6440
6489
  const advancedTo = window2[window2.length - 1].id;
6441
6490
  stateFor(conversationId).watermark = advancedTo;
@@ -6456,6 +6505,9 @@ function createAutoExtractor(options) {
6456
6505
  durationMs: Date.now() - t0,
6457
6506
  conversationId,
6458
6507
  outcome,
6508
+ funnel,
6509
+ timings,
6510
+ model,
6459
6511
  // Only set alongside `outcome: "empty-after-retry"`. Spread so the key
6460
6512
  // is absent rather than explicitly undefined on a healthy turn —
6461
6513
  // analytics backends store an explicit undefined as a real value.
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  recall
3
- } from "./chunk-SPHKT76E.mjs";
3
+ } from "./chunk-X5EZQFGR.mjs";
4
4
  import "./chunk-YXL5VKCO.mjs";
5
5
  import "./chunk-3RPGXXPZ.mjs";
6
6
  export {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@anuma/sdk",
3
- "version": "6.19.1",
3
+ "version": "6.20.0",
4
4
  "description": "",
5
5
  "main": "./dist/index.cjs",
6
6
  "module": "./dist/index.mjs",
@@ -166,7 +166,7 @@
166
166
  "homepage": "https://github.com/anuma-ai/sdk#readme",
167
167
  "packageManager": "pnpm@10.23.0",
168
168
  "dependencies": {
169
- "@anuma/portal": "1.0.0-next.20260815023541",
169
+ "@anuma/portal": "1.0.0-next.20260901170209",
170
170
  "@babel/parser": "^7.29.2",
171
171
  "@babel/types": "^7.29.0",
172
172
  "ai": "6.0.202",