@anuma/sdk 9.1.4 → 9.1.6

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.
@@ -4402,6 +4402,13 @@ interface RecencyOptions {
4402
4402
  noDateMultiplier?: number;
4403
4403
  }
4404
4404
 
4405
+ type VaultCorpusCache = WeakMap<VaultMemoryOperationsContext, Map<string, Promise<StoredVaultMemory[]>>>;
4406
+ type RowRequests = Map<EmbeddingOptions["maskInput"], Map<string, Promise<number[]>>>;
4407
+ interface VaultPreparation {
4408
+ corpora: VaultCorpusCache;
4409
+ rows: WeakMap<VaultMemoryOperationsContext, RowRequests>;
4410
+ }
4411
+
4405
4412
  declare const DEFAULT_VAULT_CACHE_SIZE = 5000;
4406
4413
  /**
4407
4414
  * Create a VaultEmbeddingCache backed by an LRU with a default cap of
@@ -4419,6 +4426,7 @@ type VaultEmbeddingCache = Map<string, Float32Array>;
4419
4426
  * Options for the vault search tool.
4420
4427
  */
4421
4428
  interface MemoryVaultSearchOptions {
4429
+ /** @internal */ vaultPreparation?: VaultPreparation;
4422
4430
  onCandidateCount?: (count: number) => void;
4423
4431
  /** Maximum number of results to return (default: 5) */
4424
4432
  limit?: number;
@@ -4957,6 +4965,7 @@ interface RankedMemory {
4957
4965
  updatedAt: Date;
4958
4966
  }
4959
4967
  interface RecallOptions {
4968
+ /** @internal */ vaultPreparation?: VaultPreparation;
4960
4969
  /** Embedding cache shared with the rest of the turn, so the query is embedded once across lanes and the send. */
4961
4970
  embeddingCache?: Map<string, Float32Array>;
4962
4971
  /** Which kinds to search. Default: ['fact']. */
@@ -6176,7 +6185,8 @@ interface CreateVaultMemoryOptions {
6176
6185
  geohash?: string;
6177
6186
  }
6178
6187
  interface UpdateVaultMemoryOptions {
6179
- content: string;
6188
+ content?: string;
6189
+ expectedContent?: string;
6180
6190
  /** When true, the write is skipped (returns null) if the row carries a profile kind. */
6181
6191
  freeFormOnly?: boolean;
6182
6192
  /**
@@ -6869,9 +6879,10 @@ declare function ttlForType(factType: string | null, policy?: Partial<DecayPolic
6869
6879
  * null `eventTimeEnd` is still ongoing, so this rule does not fire for it.
6870
6880
  * 4. Age fallback — `now - updatedAt > TTL(factType)` → archive. Durable types
6871
6881
  * have Infinity TTL, so this never fires for them; null/unknown use medium.
6872
- * EXCEPTION: a `plan`/`ongoing_context` with a still-upcoming event
6873
- * (`eventTimeEnd >= now`) is kept — it must not age-archive before the event
6874
- * happens; only rule 3 (after it passes) may archive it.
6882
+ * EXCEPTION: a `plan` / `ongoing_context` with an upcoming event or an event
6883
+ * within the grace period (`eventTimeEnd >= now - pastEventGraceMs`) stays
6884
+ * active, even after its activity TTL expires. Rule 3 archives it after the
6885
+ * grace period ends.
6875
6886
  * 5. Otherwise → keep.
6876
6887
  *
6877
6888
  * @param m Plaintext decay inputs for one row.
@@ -7202,7 +7213,9 @@ type MemoryCreate = Pick<CreateVaultMemoryOptions, "content" | "scope" | "factTy
7202
7213
  * `embedding` (with `embeddingModel`) only when you already have the new one.
7203
7214
  * @public
7204
7215
  */
7205
- type MemoryUpdate = Pick<UpdateVaultMemoryOptions, "content" | "scope" | "factType" | "eventTime" | "embedding" | "embeddingModel" | "kind" | "kindValue" | "level">;
7216
+ type MemoryUpdate = Pick<UpdateVaultMemoryOptions, "scope" | "factType" | "eventTime" | "embedding" | "embeddingModel" | "kind" | "kindValue" | "level"> & {
7217
+ content: string;
7218
+ };
7206
7219
  /**
7207
7220
  * Fact-only recall. Conversation chunks are message storage, not memory, so
7208
7221
  * the chunk-lane knobs are not part of this surface; nor is `folderId` (see
@@ -19,7 +19,7 @@ import {
19
19
  taskTypeHeader,
20
20
  validateEndpointOverride,
21
21
  withInternalFlowMarker
22
- } from "./chunk-7QX4J7KV.mjs";
22
+ } from "./chunk-RSA3S6OA.mjs";
23
23
  import {
24
24
  BASE_URL,
25
25
  MCP_R2_DOMAIN,
@@ -540,15 +540,16 @@ async function refreshEncryptionKeyIfMatches(walletAddress, probeCiphertext, sig
540
540
  if (!parsed) {
541
541
  return false;
542
542
  }
543
+ const startEpoch = sessionEpoch;
543
544
  const cached = refreshCandidatesByAddress.get(walletAddress);
544
545
  if (cached) {
545
- return tryCommitCandidatesIfProbeMatches(walletAddress, parsed, cached);
546
+ return tryCommitCandidatesIfProbeMatches(walletAddress, parsed, cached, startEpoch);
546
547
  }
547
548
  let derivePromise = pendingCandidateDerives.get(walletAddress);
548
549
  if (!derivePromise) {
549
550
  derivePromise = (async () => {
550
551
  const candidates2 = await deriveRefreshCandidates(signMessage, embeddedWalletSigner);
551
- if (candidates2) {
552
+ if (candidates2 && sessionEpoch === startEpoch) {
552
553
  refreshCandidatesByAddress.set(walletAddress, candidates2);
553
554
  }
554
555
  return candidates2;
@@ -560,10 +561,10 @@ async function refreshEncryptionKeyIfMatches(walletAddress, probeCiphertext, sig
560
561
  pendingCandidateDerives.set(walletAddress, derivePromise);
561
562
  }
562
563
  const candidates = await derivePromise;
563
- if (!candidates) {
564
+ if (!candidates || sessionEpoch !== startEpoch) {
564
565
  return false;
565
566
  }
566
- return tryCommitCandidatesIfProbeMatches(walletAddress, parsed, candidates);
567
+ return tryCommitCandidatesIfProbeMatches(walletAddress, parsed, candidates, startEpoch);
567
568
  }
568
569
  function parsePrefixedCiphertext(probeCiphertext) {
569
570
  const version = probeCiphertext.startsWith("enc:v3:") ? "v3" : probeCiphertext.startsWith("enc:v2:") ? "v2" : null;
@@ -606,7 +607,7 @@ async function deriveRefreshCandidates(signMessage, embeddedWalletSigner) {
606
607
  }
607
608
  return { legacy: legacyKey, current: currentKey };
608
609
  }
609
- async function tryCommitCandidatesIfProbeMatches(walletAddress, parsed, candidates) {
610
+ async function tryCommitCandidatesIfProbeMatches(walletAddress, parsed, candidates, startEpoch) {
610
611
  const candidateHex = parsed.version === "v2" ? candidates.legacy : candidates.current;
611
612
  const keyBytes = hexToBytes(candidateHex);
612
613
  const candidateCryptoKey = await crypto.subtle.importKey(
@@ -624,6 +625,9 @@ async function tryCommitCandidatesIfProbeMatches(walletAddress, parsed, candidat
624
625
  );
625
626
  return false;
626
627
  }
628
+ if (sessionEpoch !== startEpoch) {
629
+ return false;
630
+ }
627
631
  setStoredKey(walletAddress, { legacy: candidates.legacy, current: candidates.current });
628
632
  cryptoKeyCache.delete(`${walletAddress}:v2`);
629
633
  cryptoKeyCache.delete(`${walletAddress}:v3`);
@@ -14953,6 +14957,16 @@ async function updateVaultMemoryOp(ctx, id2, opts) {
14953
14957
  const probe = await ctx.vaultMemoryCollection.find(id2);
14954
14958
  if (probe.isDeleted || probe.supersededBy || !isOwnedByCtxUser(ctx, probe)) return null;
14955
14959
  if (opts.freeFormOnly && isKinded(probe)) return null;
14960
+ const readStoredContent = probe._getRaw("content");
14961
+ if (opts.expectedContent !== void 0) {
14962
+ const current = await vaultMemoryToStored(
14963
+ probe,
14964
+ ctx.walletAddress,
14965
+ ctx.signMessage,
14966
+ ctx.embeddedWalletSigner
14967
+ );
14968
+ if (current.content !== opts.expectedContent) return null;
14969
+ }
14956
14970
  const levelAfter = (current) => {
14957
14971
  if (opts.level !== void 0) return opts.level;
14958
14972
  if (opts.scope === void 0) return resolveMemoryLevel(current.level, current.scope);
@@ -14960,7 +14974,7 @@ async function updateVaultMemoryOp(ctx, id2, opts) {
14960
14974
  };
14961
14975
  assertValidKindLevel(opts.kind !== void 0 ? opts.kind : probe.kind, levelAfter(probe));
14962
14976
  const encryptedKindValue = await encryptKindValue(ctx, opts.kindValue);
14963
- const encryptedContent = ctx.walletAddress && ctx.signMessage ? await encryptVaultMemoryContent(
14977
+ const encryptedContent = opts.content !== void 0 && ctx.walletAddress && ctx.signMessage ? await encryptVaultMemoryContent(
14964
14978
  opts.content,
14965
14979
  ctx.walletAddress,
14966
14980
  ctx.signMessage,
@@ -14968,13 +14982,13 @@ async function updateVaultMemoryOp(ctx, id2, opts) {
14968
14982
  ) : opts.content;
14969
14983
  let stale = false;
14970
14984
  const record2 = probe;
14971
- const originalUpdatedAt = record2.updatedAt.getTime();
14972
14985
  await ctx.database.write(async () => {
14973
14986
  await assertCanWrite2(ctx);
14974
- if (record2.isDeleted || record2.supersededBy || !isOwnedByCtxUser(ctx, record2) || opts.freeFormOnly && isKinded(record2)) {
14987
+ if (record2.isDeleted || record2.supersededBy || !isOwnedByCtxUser(ctx, record2) || opts.freeFormOnly && isKinded(record2) || opts.expectedContent !== void 0 && record2._getRaw("content") !== readStoredContent) {
14975
14988
  stale = true;
14976
14989
  return;
14977
14990
  }
14991
+ const originalUpdatedAt = record2.updatedAt.getTime();
14978
14992
  const nextKind = opts.kind !== void 0 ? opts.kind : record2.kind;
14979
14993
  const nextLevel = levelAfter(record2);
14980
14994
  assertValidKindLevel(nextKind, nextLevel);
@@ -14990,7 +15004,7 @@ async function updateVaultMemoryOp(ctx, id2, opts) {
14990
15004
  replayedObservation = opts.observationSourceIds.every((id3) => observedSources.includes(id3));
14991
15005
  }
14992
15006
  await record2.update((r) => {
14993
- r._setRaw("content", encryptedContent);
15007
+ if (encryptedContent !== void 0) r._setRaw("content", encryptedContent);
14994
15008
  if (opts.level !== void 0) {
14995
15009
  r._setRaw("level", nextLevel);
14996
15010
  r._setRaw("scope", scopeForLevel(nextLevel));
@@ -15757,7 +15771,7 @@ function classifyDecay(m, now, policy) {
15757
15771
  if (m.source === "manual" || m.source === SOURCE_PHOTO) return "keep";
15758
15772
  if (isTimeSensitiveType(m.factType) && m.eventTimeEnd !== null) {
15759
15773
  if (m.eventTimeEnd < now - resolved.pastEventGraceMs) return "archive";
15760
- if (m.eventTimeEnd >= now) return "keep";
15774
+ return "keep";
15761
15775
  }
15762
15776
  if (now - lastActivityAt(m) > ttlForType(m.factType, resolved)) return "archive";
15763
15777
  return "keep";
@@ -17714,6 +17728,104 @@ function validate(parsed, originalQuery) {
17714
17728
  return { mode, subQueries };
17715
17729
  }
17716
17730
 
17731
+ // src/lib/memoryVault/vaultPreparation.ts
17732
+ function createVaultPreparation() {
17733
+ return { corpora: /* @__PURE__ */ new WeakMap(), rows: /* @__PURE__ */ new WeakMap() };
17734
+ }
17735
+ var pendingRows = /* @__PURE__ */ new WeakMap();
17736
+ function loadVaultCorpus(ctx, options, preparation) {
17737
+ if (!preparation) return getAllVaultMemoriesOp(ctx, options);
17738
+ const corpusCache = preparation.corpora;
17739
+ let pending = corpusCache.get(ctx);
17740
+ if (!pending) {
17741
+ pending = /* @__PURE__ */ new Map();
17742
+ corpusCache.set(ctx, pending);
17743
+ }
17744
+ const key = JSON.stringify(options ?? {});
17745
+ let request = pending.get(key);
17746
+ if (!request) {
17747
+ const entries = pending;
17748
+ request = getAllVaultMemoriesOp(ctx, options).catch((error) => {
17749
+ entries.delete(key);
17750
+ throw error;
17751
+ });
17752
+ pending.set(key, request);
17753
+ }
17754
+ return request;
17755
+ }
17756
+ function rowRequestsFor(registry2, ctx, maskInput) {
17757
+ let byMask = registry2.get(ctx);
17758
+ if (!byMask) {
17759
+ byMask = /* @__PURE__ */ new Map();
17760
+ registry2.set(ctx, byMask);
17761
+ }
17762
+ let requests = byMask.get(maskInput);
17763
+ if (!requests) {
17764
+ requests = /* @__PURE__ */ new Map();
17765
+ byMask.set(maskInput, requests);
17766
+ }
17767
+ return requests;
17768
+ }
17769
+ function embedVaultRows(ctx, options, rows, preparation) {
17770
+ const pending = rowRequestsFor(pendingRows, ctx, options.maskInput);
17771
+ const prepared = preparation && rowRequestsFor(preparation.rows, ctx, options.maskInput);
17772
+ const model = options.model ?? DEFAULT_API_EMBEDDING_MODEL;
17773
+ const keys = rows.map(
17774
+ (row) => JSON.stringify([
17775
+ row.uniqueId,
17776
+ row.updatedAt?.getTime(),
17777
+ row.content,
17778
+ model,
17779
+ options.baseUrl,
17780
+ options.timeoutMs,
17781
+ options.tokenTimeoutMs,
17782
+ options.totalTimeoutMs
17783
+ ])
17784
+ );
17785
+ const missing = /* @__PURE__ */ new Map();
17786
+ rows.forEach((row, index) => {
17787
+ if (!pending.has(keys[index]) && !prepared?.has(keys[index])) missing.set(keys[index], row);
17788
+ });
17789
+ if (missing.size > 0) {
17790
+ const entries = pending;
17791
+ const batch = Promise.resolve().then(
17792
+ () => generateEmbeddings(
17793
+ [...missing.values()].map((row) => row.content),
17794
+ options
17795
+ )
17796
+ );
17797
+ [...missing].forEach(([key, row], index) => {
17798
+ const request = batch.then((vectors) => {
17799
+ const vector = vectors[index];
17800
+ updateVaultMemoryEmbeddingOp(ctx, row.uniqueId, JSON.stringify(vector), model, {
17801
+ content: row.content,
17802
+ updatedAt: row.updatedAt?.getTime()
17803
+ }).catch(() => {
17804
+ });
17805
+ return vector;
17806
+ }).finally(() => {
17807
+ entries.delete(key);
17808
+ if (entries.size === 0) pendingRows.get(ctx)?.delete(options.maskInput);
17809
+ });
17810
+ pending.set(key, request);
17811
+ });
17812
+ }
17813
+ return Promise.all(
17814
+ keys.map((key) => {
17815
+ const cached = prepared?.get(key);
17816
+ if (cached) return cached;
17817
+ const request = pending.get(key);
17818
+ if (!prepared) return request;
17819
+ const retained = request.catch((error) => {
17820
+ prepared.delete(key);
17821
+ throw error;
17822
+ });
17823
+ prepared.set(key, retained);
17824
+ return retained;
17825
+ })
17826
+ );
17827
+ }
17828
+
17717
17829
  // src/lib/memoryVault/vectorVersion.ts
17718
17830
  var vectorRowVersion = /* @__PURE__ */ new WeakMap();
17719
17831
  function contentFingerprint(content) {
@@ -18276,11 +18388,11 @@ async function rankComposite(originalQuery, originalQueryEmbedding, subQueries,
18276
18388
  return combined.slice(0, limit);
18277
18389
  }
18278
18390
  async function preEmbedVaultMemories(vaultCtx, embeddingOptions, cache, options) {
18279
- const memories = await getAllVaultMemoriesOp(vaultCtx, options);
18391
+ const memories = await loadVaultCorpus(vaultCtx, options);
18280
18392
  const currentModel = embeddingOptions.model ?? DEFAULT_API_EMBEDDING_MODEL;
18281
18393
  const uncachedTexts = [];
18282
18394
  const uncachedKeys = [];
18283
- const uncachedIds = [];
18395
+ const uncachedRows = [];
18284
18396
  const uncachedVersions = [];
18285
18397
  for (const m of memories) {
18286
18398
  const content = m.content;
@@ -18299,12 +18411,12 @@ async function preEmbedVaultMemories(vaultCtx, embeddingOptions, cache, options)
18299
18411
  }
18300
18412
  uncachedTexts.push(m.content);
18301
18413
  uncachedKeys.push(m.uniqueId);
18302
- uncachedIds.push(m.uniqueId);
18414
+ uncachedRows.push(m);
18303
18415
  uncachedVersions.push(m.updatedAt);
18304
18416
  }
18305
18417
  }
18306
18418
  if (uncachedTexts.length > 0) {
18307
- const embeddings = await generateEmbeddings(uncachedTexts, embeddingOptions);
18419
+ const embeddings = await embedVaultRows(vaultCtx, embeddingOptions, uncachedRows);
18308
18420
  for (let i = 0; i < uncachedKeys.length; i++) {
18309
18421
  cacheRowVector(
18310
18422
  cache,
@@ -18313,13 +18425,6 @@ async function preEmbedVaultMemories(vaultCtx, embeddingOptions, cache, options)
18313
18425
  uncachedVersions[i],
18314
18426
  uncachedTexts[i]
18315
18427
  );
18316
- updateVaultMemoryEmbeddingOp(
18317
- vaultCtx,
18318
- uncachedIds[i],
18319
- JSON.stringify(embeddings[i]),
18320
- currentModel
18321
- ).catch(() => {
18322
- });
18323
18428
  }
18324
18429
  }
18325
18430
  }
@@ -18461,10 +18566,7 @@ async function buildProjectedCorpus(query, vaultCtx, embeddingOptions, cache, qu
18461
18566
  let laneVecs;
18462
18567
  if (opts.embedStats) opts.embedStats.rowsEmbedded += laneRows.length;
18463
18568
  try {
18464
- laneVecs = await generateEmbeddings(
18465
- laneRows.map((m) => m.content),
18466
- embeddingOptions
18467
- );
18569
+ laneVecs = await embedVaultRows(vaultCtx, embeddingOptions, laneRows);
18468
18570
  } catch (err) {
18469
18571
  laneEmbedFailed = true;
18470
18572
  getLogger().warn(
@@ -18478,13 +18580,6 @@ async function buildProjectedCorpus(query, vaultCtx, embeddingOptions, cache, qu
18478
18580
  cacheRowVector(cache, m.uniqueId, vec, m.updatedAt, m.content);
18479
18581
  resolvedThisCall.add(m.uniqueId);
18480
18582
  vectored.push({ uniqueId: m.uniqueId, embedding: vec, updatedAt: m.updatedAt });
18481
- updateVaultMemoryEmbeddingOp(
18482
- vaultCtx,
18483
- m.uniqueId,
18484
- JSON.stringify(vecs[i]),
18485
- currentModel
18486
- ).catch(() => {
18487
- });
18488
18583
  });
18489
18584
  }
18490
18585
  }
@@ -18676,9 +18771,10 @@ async function prepareVaultCandidates(query, vaultCtx, embeddingOptions, cache,
18676
18771
  };
18677
18772
  }
18678
18773
  } else {
18679
- const loaded = await getAllVaultMemoriesOp(
18774
+ const loaded = await loadVaultCorpus(
18680
18775
  vaultCtx,
18681
- Object.keys(queryOpts).length > 0 ? queryOpts : void 0
18776
+ Object.keys(queryOpts).length > 0 ? queryOpts : void 0,
18777
+ searchOptions?.vaultPreparation
18682
18778
  );
18683
18779
  vaultSize = loaded.length;
18684
18780
  rowsDecrypted = loaded.length;
@@ -18751,7 +18847,12 @@ async function prepareVaultCandidates(query, vaultCtx, embeddingOptions, cache,
18751
18847
  let newEmbeddings;
18752
18848
  embedStats.rowsEmbedded += uncachedTexts.length;
18753
18849
  try {
18754
- newEmbeddings = await generateEmbeddings(uncachedTexts, embeddingOptions);
18850
+ newEmbeddings = await embedVaultRows(
18851
+ vaultCtx,
18852
+ embeddingOptions,
18853
+ uncachedIndices.map((index) => memories[index]),
18854
+ searchOptions?.vaultPreparation
18855
+ );
18755
18856
  } catch (err) {
18756
18857
  embeddingFailure = true;
18757
18858
  getLogger().warn(
@@ -18767,13 +18868,6 @@ async function prepareVaultCandidates(query, vaultCtx, embeddingOptions, cache,
18767
18868
  memories[uncachedIndices[j]].updatedAt,
18768
18869
  memories[uncachedIndices[j]].content
18769
18870
  );
18770
- updateVaultMemoryEmbeddingOp(
18771
- vaultCtx,
18772
- memories[uncachedIndices[j]].uniqueId,
18773
- JSON.stringify(newEmbeddings[j]),
18774
- currentModel
18775
- ).catch(() => {
18776
- });
18777
18871
  }
18778
18872
  }
18779
18873
  }
@@ -19177,7 +19271,6 @@ async function retainWithPersistence(content, ctx, options = {}) {
19177
19271
  const factTypeUpdate = pickFactTypeUpdate(existing, options.factType);
19178
19272
  const resurrect = resurrectFields(existing);
19179
19273
  const updated = await ctx.persistence.update(targetId, {
19180
- content: existing.content,
19181
19274
  freeFormOnly: true,
19182
19275
  proofCountIncrement: 1,
19183
19276
  observationSourceIds: options.sourceChunkIds,
@@ -19381,7 +19474,7 @@ async function tryConsolidate(trimmed, ctx, options, prepared) {
19381
19474
  content: m.content,
19382
19475
  similarity: m.similarity
19383
19476
  }));
19384
- const { consolidateMemory: doConsolidate } = await import("./consolidate-WLDYJF6J.mjs");
19477
+ const { consolidateMemory: doConsolidate } = await import("./consolidate-WFTPBHDV.mjs");
19385
19478
  const decision = await doConsolidate(trimmed, candidates, consolidateOptions);
19386
19479
  if (decision.action === "create") return decision.fallbackReason ? null : { create: true };
19387
19480
  if (decision.action === "supersede" && decision.content) {
@@ -19416,7 +19509,6 @@ async function tryConsolidate(trimmed, ctx, options, prepared) {
19416
19509
  const factTypeUpdate = pickFactTypeUpdate(existing, options.factType);
19417
19510
  const resurrect = resurrectFields(existing);
19418
19511
  const updated = await ctx.persistence.update(decision.targetId, {
19419
- content: existing.content,
19420
19512
  freeFormOnly: true,
19421
19513
  proofCountIncrement: 1,
19422
19514
  observationSourceIds: options.sourceChunkIds,
@@ -19444,6 +19536,10 @@ async function tryConsolidate(trimmed, ctx, options, prepared) {
19444
19536
  };
19445
19537
  }
19446
19538
  if (decision.action === "update" && decision.targetId && decision.content) {
19539
+ const expectedContent = candidates.find(
19540
+ (candidate) => candidate.id === decision.targetId
19541
+ )?.content;
19542
+ if (expectedContent === void 0) return null;
19447
19543
  const existing = await ctx.persistence.get(decision.targetId);
19448
19544
  if (!existing || existing.supersededBy) {
19449
19545
  return abandonToRace(
@@ -19451,6 +19547,9 @@ async function tryConsolidate(trimmed, ctx, options, prepared) {
19451
19547
  `update: target ${decision.targetId} ${existing ? "superseded" : "deleted"} before the write`
19452
19548
  );
19453
19549
  }
19550
+ if (existing.content !== expectedContent) {
19551
+ return abandonToRace(options, `update: target ${decision.targetId} edited before the write`);
19552
+ }
19454
19553
  const mergedSourceIds = unionStrings(
19455
19554
  existing.sourceChunkIds ?? [],
19456
19555
  options.sourceChunkIds ?? []
@@ -19463,6 +19562,7 @@ async function tryConsolidate(trimmed, ctx, options, prepared) {
19463
19562
  const resurrect = resurrectFields(existing);
19464
19563
  const updated = await ctx.persistence.update(decision.targetId, {
19465
19564
  content: decision.content,
19565
+ expectedContent,
19466
19566
  freeFormOnly: true,
19467
19567
  proofCountIncrement: 1,
19468
19568
  observationSourceIds: options.sourceChunkIds,
@@ -19475,10 +19575,10 @@ async function tryConsolidate(trimmed, ctx, options, prepared) {
19475
19575
  ...factTypeUpdate !== void 0 && { factType: factTypeUpdate }
19476
19576
  });
19477
19577
  if (!updated) {
19478
- await assertMergeTargetGoneOrThrow(ctx, decision.targetId);
19578
+ await assertMergeTargetGoneOrThrow(ctx, decision.targetId, expectedContent);
19479
19579
  return abandonToRace(
19480
19580
  options,
19481
- `update: target ${decision.targetId} deleted mid-write (consolidated rewrite lost)`
19581
+ `update: target ${decision.targetId} changed mid-write (consolidated rewrite lost)`
19482
19582
  );
19483
19583
  }
19484
19584
  cacheRowVector(
@@ -19505,9 +19605,9 @@ async function tryConsolidate(trimmed, ctx, options, prepared) {
19505
19605
  );
19506
19606
  return null;
19507
19607
  }
19508
- async function assertMergeTargetGoneOrThrow(ctx, targetId) {
19608
+ async function assertMergeTargetGoneOrThrow(ctx, targetId, expectedContent) {
19509
19609
  const stillExists = await (ctx.persistence.getFresh ? ctx.persistence.getFresh(targetId) : ctx.persistence.get(targetId));
19510
- if (stillExists && (stillExists.kind === null || stillExists.kind === void 0)) {
19610
+ if (stillExists && (stillExists.kind === null || stillExists.kind === void 0) && (expectedContent === void 0 || stillExists.content === expectedContent)) {
19511
19611
  throw new RetainError(
19512
19612
  "merge-not-persisted",
19513
19613
  `retain: merge into memory ${targetId} failed to persist`
@@ -21340,6 +21440,7 @@ async function recall(query, ctx, options = {}) {
21340
21440
  },
21341
21441
  ...options.rrfK !== void 0 && { rrfK: options.rrfK },
21342
21442
  ...options.decryptLast !== void 0 && { decryptLast: options.decryptLast },
21443
+ ...options.vaultPreparation && { vaultPreparation: options.vaultPreparation },
21343
21444
  ...subQueries && { subQueries },
21344
21445
  ...options.scopes && { scopes: options.scopes },
21345
21446
  ...options.folderId !== void 0 && { folderId: options.folderId },
@@ -22919,16 +23020,16 @@ function createRemoteMemoryPipeline(options) {
22919
23020
  create: (input) => write(createRow(input), 0),
22920
23021
  update: async (id2, patch) => {
22921
23022
  const existing = await get(id2);
22922
- if (!existing || patch.freeFormOnly && existing.kind !== null && existing.kind !== void 0)
23023
+ if (!existing || patch.expectedContent !== void 0 && existing.content !== patch.expectedContent || patch.freeFormOnly && existing.kind !== null && existing.kind !== void 0)
22923
23024
  return null;
22924
23025
  const snapshot = snapshots.get(id2);
22925
23026
  const memory = {
22926
23027
  ...snapshot.memory,
22927
- content: patch.content,
23028
+ content: patch.content ?? snapshot.memory.content,
22928
23029
  updated_at: patch.preserveUpdatedAt ? snapshot.memory.updated_at : Math.max(Date.now(), (snapshot.memory.topics_extracted_at ?? 0) + 1)
22929
23030
  };
22930
23031
  if (patch.scope !== void 0) memory.scope = patch.scope;
22931
- if (patch.embedding === void 0 && patch.content !== snapshot.memory.content) {
23032
+ if (patch.embedding === void 0 && patch.content !== void 0 && patch.content !== snapshot.memory.content) {
22932
23033
  delete memory.embedding;
22933
23034
  delete memory.embedding_model;
22934
23035
  }
@@ -24930,13 +25031,20 @@ async function synthesizeProfile(ctx, options = {}) {
24930
25031
  }
24931
25032
  }
24932
25033
  }
25034
+ const vaultPreparation = createVaultPreparation();
24933
25035
  const settled = await Promise.allSettled(
24934
25036
  facets.map(async (facet) => {
24935
25037
  const prior = previous?.sections.find((s) => s.key === facet.key);
24936
25038
  if (prior && !staleKeys.has(facet.key)) {
24937
25039
  return prior;
24938
25040
  }
24939
- return synthesizeFacet(facet, recallCtx, options, fallbackPriors.get(facet.key));
25041
+ return synthesizeFacet(
25042
+ facet,
25043
+ recallCtx,
25044
+ options,
25045
+ fallbackPriors.get(facet.key),
25046
+ vaultPreparation
25047
+ );
24940
25048
  })
24941
25049
  );
24942
25050
  const sections = settled.map((r, i) => {
@@ -25090,7 +25198,7 @@ async function attributeFacts(ctx, candidates, facets) {
25090
25198
  }
25091
25199
  return keys;
25092
25200
  }
25093
- async function synthesizeFacet(facet, ctx, options, prior) {
25201
+ async function synthesizeFacet(facet, ctx, options, prior, vaultPreparation) {
25094
25202
  const scopes = options.scopes ?? DEFAULT_SCOPES;
25095
25203
  const limit = options.limit ?? DEFAULT_FACET_RECALL_LIMIT;
25096
25204
  const factTypeWeights = { ...DEFAULT_PROFILE_FACT_TYPE_WEIGHTS, ...options.factTypeWeights };
@@ -25107,6 +25215,7 @@ async function synthesizeFacet(facet, ctx, options, prior) {
25107
25215
  };
25108
25216
  }
25109
25217
  const recalled = await recall(facet.query, ctx, {
25218
+ vaultPreparation,
25110
25219
  scopes,
25111
25220
  limit: hasReviewGate ? RECALL_MAX_LIMIT : limit,
25112
25221
  types: ["fact"],
@@ -36657,20 +36766,27 @@ function useSubscription(options = {}) {
36657
36766
  }
36658
36767
 
36659
36768
  // src/react/useTools.ts
36660
- import { useCallback as useCallback18, useEffect as useEffect12, useRef as useRef9, useState as useState16 } from "react";
36769
+ import { useCallback as useCallback18, useEffect as useEffect12, useMemo as useMemo10, useRef as useRef9, useState as useState16 } from "react";
36661
36770
  function useTools(options) {
36662
36771
  const { getToken, baseUrl = BASE_URL, includeTools, autoFetch = true } = options;
36663
- const [tools, setTools] = useState16([]);
36772
+ const [allTools, setAllTools] = useState16([]);
36664
36773
  const [checksum, setChecksum] = useState16(getToolsChecksum2);
36665
36774
  const [isLoading, setIsLoading] = useState16(false);
36666
36775
  const [error, setError] = useState16(null);
36776
+ const includeToolsRef = useRef9(includeTools);
36777
+ if (includeTools !== includeToolsRef.current && (includeTools === void 0 || includeToolsRef.current === void 0 || includeTools.length !== includeToolsRef.current.length || includeTools.some((name, i) => name !== includeToolsRef.current?.[i]))) {
36778
+ includeToolsRef.current = includeTools;
36779
+ }
36780
+ const stableIncludeTools = includeToolsRef.current;
36781
+ const tools = useMemo10(
36782
+ () => filterServerTools(allTools, stableIncludeTools),
36783
+ [allTools, stableIncludeTools]
36784
+ );
36667
36785
  const getTokenRef = useRef9(getToken);
36668
36786
  const baseUrlRef = useRef9(baseUrl);
36669
- const includeToolsRef = useRef9(includeTools);
36670
36787
  useEffect12(() => {
36671
36788
  getTokenRef.current = getToken;
36672
36789
  baseUrlRef.current = baseUrl;
36673
- includeToolsRef.current = includeTools;
36674
36790
  });
36675
36791
  const fetchTools = useCallback18(async (forceRefresh = false) => {
36676
36792
  setIsLoading(true);
@@ -36681,8 +36797,7 @@ function useTools(options) {
36681
36797
  baseUrl: baseUrlRef.current,
36682
36798
  forceRefresh
36683
36799
  });
36684
- const filteredTools = filterServerTools(fetchedTools, includeToolsRef.current);
36685
- setTools(filteredTools);
36800
+ setAllTools(fetchedTools);
36686
36801
  setChecksum(getToolsChecksum2());
36687
36802
  } catch (err) {
36688
36803
  setError(err instanceof Error ? err : new Error(String(err)));
@@ -36706,11 +36821,6 @@ function useTools(options) {
36706
36821
  },
36707
36822
  [refresh]
36708
36823
  );
36709
- useEffect12(() => {
36710
- if (tools.length > 0 && includeTools !== void 0) {
36711
- setTools((currentTools) => filterServerTools(currentTools, includeTools));
36712
- }
36713
- }, [includeTools, tools.length]);
36714
36824
  const hasFetchedRef = useRef9(false);
36715
36825
  useEffect12(() => {
36716
36826
  if (autoFetch && !hasFetchedRef.current) {
@@ -36737,7 +36847,7 @@ import {
36737
36847
  createElement as createElement4,
36738
36848
  useCallback as useCallback19,
36739
36849
  useContext as useContext5,
36740
- useMemo as useMemo10,
36850
+ useMemo as useMemo11,
36741
36851
  useRef as useRef10,
36742
36852
  useState as useState17
36743
36853
  } from "react";
@@ -36874,7 +36984,7 @@ function UIInteractionProvider({
36874
36984
  },
36875
36985
  [pendingInteractions]
36876
36986
  );
36877
- const contextValue = useMemo10(
36987
+ const contextValue = useMemo11(
36878
36988
  () => ({
36879
36989
  pendingInteractions,
36880
36990
  createInteraction,
@@ -36898,7 +37008,7 @@ function UIInteractionProvider({
36898
37008
  }
36899
37009
 
36900
37010
  // src/react/useVoice.ts
36901
- import { useCallback as useCallback20, useMemo as useMemo11, useRef as useRef11, useState as useState18 } from "react";
37011
+ import { useCallback as useCallback20, useMemo as useMemo12, useRef as useRef11, useState as useState18 } from "react";
36902
37012
  function getOnDeviceSpeechRecognition() {
36903
37013
  if (typeof window === "undefined") return null;
36904
37014
  if (!/iPhone|iPad|iPod/.test(navigator.userAgent)) return null;
@@ -37102,7 +37212,7 @@ function useVoice(options) {
37102
37212
  },
37103
37213
  [recording, loadPipeline, options?.language]
37104
37214
  );
37105
- const nativeSpeechAvailable = useMemo11(() => getOnDeviceSpeechRecognition() !== null, []);
37215
+ const nativeSpeechAvailable = useMemo12(() => getOnDeviceSpeechRecognition() !== null, []);
37106
37216
  const [isNativeListening, setIsNativeListening] = useState18(false);
37107
37217
  const nativeRecognitionRef = useRef11(null);
37108
37218
  const nativeTextRef = useRef11("");