@anuma/sdk 3.12.0 → 3.12.2

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.
@@ -5254,8 +5254,26 @@ interface RecallDiagnostics {
5254
5254
  }
5255
5255
  type RetainAction = "create" | "merge" | "update" | "skip" | "suppressed" | "supersede";
5256
5256
  type RetainSource = "manual" | "auto-extracted" | "capsule";
5257
- /** Why the consolidator fell back to "create" instead of a real decision. */
5258
- type ConsolidationFallbackReason = "llm_error" | "invalid_response";
5257
+ /**
5258
+ * Why a retain fell back to "create" instead of applying a consolidation
5259
+ * decision. Each value names a DIFFERENT thing to go fix, which is the point of
5260
+ * keeping them apart:
5261
+ *
5262
+ * - `llm_error` — the consolidation call never produced a response (network,
5263
+ * timeout, 5xx, 429, empty completion, or missing credentials). Look at the
5264
+ * portal and the auth config.
5265
+ * - `invalid_response` — the model answered, but with something unusable: an
5266
+ * unknown action, a targetId that was not in the candidate set, an `update` or
5267
+ * `supersede` with empty content, a `noop` with no target. Look at the prompt
5268
+ * and the model.
5269
+ * - `target_vanished` — the model returned a decision that was valid when it was
5270
+ * made, and the row it named was deleted or superseded by a concurrent writer
5271
+ * before the write landed. Nothing is broken in the consolidator; a sustained
5272
+ * rate points at write contention (for example an auto-extraction worker and a
5273
+ * manual write racing over the same vault), which quietly costs you the
5274
+ * dedup that decision would have performed.
5275
+ */
5276
+ type ConsolidationFallbackReason = "llm_error" | "invalid_response" | "target_vanished";
5259
5277
  interface RetainOptions {
5260
5278
  source?: RetainSource;
5261
5279
  sourceChunkIds?: string[];
@@ -7250,7 +7268,11 @@ interface AutoExtractor {
7250
7268
  processTurn(messages: AutoExtractMessage[], conversationId?: string): boolean;
7251
7269
  /** True while a turn's extraction is in flight. */
7252
7270
  isProcessing(): boolean;
7253
- /** Stop accepting new turns. In-flight work continues to completion. */
7271
+ /**
7272
+ * Stop accepting new turns. In-flight work continues to completion, and any
7273
+ * turn already queued (coalesced while an extraction was in flight) is still
7274
+ * flushed — never dropped — so a turn sent right before unmount isn't lost.
7275
+ */
7254
7276
  dispose(): void;
7255
7277
  }
7256
7278
  /**
@@ -134,8 +134,9 @@ import {
134
134
  } from "./chunk-FXGG7JA5.mjs";
135
135
  import {
136
136
  createStreamingDeAnonymizer,
137
+ notifyConsolidationFallback,
137
138
  resolvePiiRedactor
138
- } from "./chunk-PXN5Q3MD.mjs";
139
+ } from "./chunk-RT75PNSJ.mjs";
139
140
  import {
140
141
  callPortalJsonCompletion,
141
142
  extractJsonCandidate,
@@ -5311,6 +5312,11 @@ async function retain(content, ctx, options = {}) {
5311
5312
  proofCount: 1
5312
5313
  };
5313
5314
  }
5315
+ notifyConsolidationFallback(
5316
+ "target_vanished",
5317
+ options.consolidateOptions?.onFallback,
5318
+ `supersede: primary target ${primaryTargetId} retired or deleted inside the atomic write`
5319
+ );
5314
5320
  }
5315
5321
  const created = await createVaultMemoryOp(ctx.vaultCtx, createOpts);
5316
5322
  ctx.vaultCache.set(created.uniqueId, Float32Array.from(embedding));
@@ -5362,6 +5368,10 @@ function pickFactTypeUpdate(existing, incoming) {
5362
5368
  function resurrectFields(existing) {
5363
5369
  return typeof existing.archivedAt === "number" ? { restore: true } : { preserveUpdatedAt: true };
5364
5370
  }
5371
+ function abandonToRace(options, detail) {
5372
+ notifyConsolidationFallback("target_vanished", options.consolidateOptions?.onFallback, detail);
5373
+ return null;
5374
+ }
5365
5375
  async function tryConsolidate(trimmed, ctx, options, prepared) {
5366
5376
  const consolidateOptions = options.consolidateOptions;
5367
5377
  if (!consolidateOptions) return null;
@@ -5383,7 +5393,7 @@ async function tryConsolidate(trimmed, ctx, options, prepared) {
5383
5393
  content: m.content,
5384
5394
  similarity: m.similarity
5385
5395
  }));
5386
- const { consolidateMemory: doConsolidate } = await import("./consolidate-JMGL3MXG.mjs");
5396
+ const { consolidateMemory: doConsolidate } = await import("./consolidate-4VEAS3YN.mjs");
5387
5397
  const decision = await doConsolidate(trimmed, candidates, consolidateOptions);
5388
5398
  if (decision.action === "create") return null;
5389
5399
  if (decision.action === "supersede" && decision.content) {
@@ -5394,12 +5404,22 @@ async function tryConsolidate(trimmed, ctx, options, prepared) {
5394
5404
  const existing = await getVaultMemoryOp(ctx.vaultCtx, id);
5395
5405
  if (existing && !existing.supersededBy) valid.push(id);
5396
5406
  }
5397
- if (valid.length === 0) return null;
5407
+ if (valid.length === 0) {
5408
+ return abandonToRace(
5409
+ options,
5410
+ `supersede: all ${requestedIds.length} target(s) deleted or already superseded`
5411
+ );
5412
+ }
5398
5413
  return { supersede: valid, content: decision.content };
5399
5414
  }
5400
5415
  if (decision.action === "noop" && decision.targetId) {
5401
5416
  const existing = await getVaultMemoryOp(ctx.vaultCtx, decision.targetId);
5402
- if (!existing || existing.supersededBy) return null;
5417
+ if (!existing || existing.supersededBy) {
5418
+ return abandonToRace(
5419
+ options,
5420
+ `noop: target ${decision.targetId} ${existing ? "superseded" : "deleted"} before the write`
5421
+ );
5422
+ }
5403
5423
  const mergedSourceIds = unionStrings(
5404
5424
  existing.sourceChunkIds ?? [],
5405
5425
  options.sourceChunkIds ?? []
@@ -5424,7 +5444,10 @@ async function tryConsolidate(trimmed, ctx, options, prepared) {
5424
5444
  });
5425
5445
  if (!updated) {
5426
5446
  await assertMergeTargetGoneOrThrow(ctx, decision.targetId);
5427
- return null;
5447
+ return abandonToRace(
5448
+ options,
5449
+ `noop: target ${decision.targetId} deleted mid-write (proof-count bump lost)`
5450
+ );
5428
5451
  }
5429
5452
  return {
5430
5453
  done: {
@@ -5437,7 +5460,12 @@ async function tryConsolidate(trimmed, ctx, options, prepared) {
5437
5460
  }
5438
5461
  if (decision.action === "update" && decision.targetId && decision.content) {
5439
5462
  const existing = await getVaultMemoryOp(ctx.vaultCtx, decision.targetId);
5440
- if (!existing || existing.supersededBy) return null;
5463
+ if (!existing || existing.supersededBy) {
5464
+ return abandonToRace(
5465
+ options,
5466
+ `update: target ${decision.targetId} ${existing ? "superseded" : "deleted"} before the write`
5467
+ );
5468
+ }
5441
5469
  const mergedSourceIds = unionStrings(
5442
5470
  existing.sourceChunkIds ?? [],
5443
5471
  options.sourceChunkIds ?? []
@@ -5468,7 +5496,10 @@ async function tryConsolidate(trimmed, ctx, options, prepared) {
5468
5496
  });
5469
5497
  if (!updated) {
5470
5498
  await assertMergeTargetGoneOrThrow(ctx, decision.targetId);
5471
- return null;
5499
+ return abandonToRace(
5500
+ options,
5501
+ `update: target ${decision.targetId} deleted mid-write (consolidated rewrite lost)`
5502
+ );
5472
5503
  }
5473
5504
  ctx.vaultCache.set(decision.targetId, Float32Array.from(newEmbedding));
5474
5505
  return {
@@ -5480,6 +5511,11 @@ async function tryConsolidate(trimmed, ctx, options, prepared) {
5480
5511
  }
5481
5512
  };
5482
5513
  }
5514
+ notifyConsolidationFallback(
5515
+ "invalid_response",
5516
+ options.consolidateOptions?.onFallback,
5517
+ `unapplicable ${decision.action} decision reached the applier \u2014 validate() should have rejected it`
5518
+ );
5483
5519
  return null;
5484
5520
  }
5485
5521
  async function assertMergeTargetGoneOrThrow(ctx, targetId) {
@@ -6060,7 +6096,6 @@ function createAutoExtractor(options) {
6060
6096
  return true;
6061
6097
  }
6062
6098
  function drainPending() {
6063
- if (disposed) return;
6064
6099
  for (const [conversationId, state] of conversations) {
6065
6100
  if (state.pending) {
6066
6101
  const messages = state.pending;
@@ -6075,7 +6110,7 @@ function createAutoExtractor(options) {
6075
6110
  isProcessing: () => inflight > 0,
6076
6111
  dispose: () => {
6077
6112
  disposed = true;
6078
- for (const state of conversations.values()) state.pending = void 0;
6113
+ if (inflight === 0) drainPending();
6079
6114
  }
6080
6115
  };
6081
6116
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@anuma/sdk",
3
- "version": "3.12.0",
3
+ "version": "3.12.2",
4
4
  "description": "",
5
5
  "main": "./dist/index.cjs",
6
6
  "module": "./dist/index.mjs",
@@ -123,6 +123,7 @@
123
123
  "e2e:tools": "vitest run --config vitest.e2e.config.mts",
124
124
  "e2e:compare": "tsx scripts/compare-app-gen-runs.ts",
125
125
  "perf:slides": "vitest run --config vitest.perf.config.mts",
126
+ "perf:memory": "vitest run --config vitest.perf-memory.config.mts",
126
127
  "eval:engine": "tsx test/memory/longmemeval.ts --strategy engine",
127
128
  "eval:vault": "tsx test/memory/longmemeval.ts --strategy vault",
128
129
  "eval:longmemeval": "tsx test/memory/longmemeval.ts",