@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.
@@ -3902,6 +3902,13 @@ interface RecencyOptions {
3902
3902
  noDateMultiplier?: number;
3903
3903
  }
3904
3904
 
3905
+ type VaultCorpusCache = WeakMap<VaultMemoryOperationsContext, Map<string, Promise<StoredVaultMemory[]>>>;
3906
+ type RowRequests = Map<EmbeddingOptions["maskInput"], Map<string, Promise<number[]>>>;
3907
+ interface VaultPreparation {
3908
+ corpora: VaultCorpusCache;
3909
+ rows: WeakMap<VaultMemoryOperationsContext, RowRequests>;
3910
+ }
3911
+
3905
3912
  declare const DEFAULT_VAULT_CACHE_SIZE = 5000;
3906
3913
  /**
3907
3914
  * Create a VaultEmbeddingCache backed by an LRU with a default cap of
@@ -3919,6 +3926,7 @@ type VaultEmbeddingCache = Map<string, Float32Array>;
3919
3926
  * Options for the vault search tool.
3920
3927
  */
3921
3928
  interface MemoryVaultSearchOptions {
3929
+ /** @internal */ vaultPreparation?: VaultPreparation;
3922
3930
  onCandidateCount?: (count: number) => void;
3923
3931
  /** Maximum number of results to return (default: 5) */
3924
3932
  limit?: number;
@@ -4441,6 +4449,7 @@ interface RankedMemory {
4441
4449
  updatedAt: Date;
4442
4450
  }
4443
4451
  interface RecallOptions {
4452
+ /** @internal */ vaultPreparation?: VaultPreparation;
4444
4453
  /** Embedding cache shared with the rest of the turn, so the query is embedded once across lanes and the send. */
4445
4454
  embeddingCache?: Map<string, Float32Array>;
4446
4455
  /** Which kinds to search. Default: ['fact']. */
@@ -5660,7 +5669,8 @@ interface CreateVaultMemoryOptions {
5660
5669
  geohash?: string;
5661
5670
  }
5662
5671
  interface UpdateVaultMemoryOptions {
5663
- content: string;
5672
+ content?: string;
5673
+ expectedContent?: string;
5664
5674
  /** When true, the write is skipped (returns null) if the row carries a profile kind. */
5665
5675
  freeFormOnly?: boolean;
5666
5676
  /**
@@ -6264,9 +6274,10 @@ declare function ttlForType(factType: string | null, policy?: Partial<DecayPolic
6264
6274
  * null `eventTimeEnd` is still ongoing, so this rule does not fire for it.
6265
6275
  * 4. Age fallback — `now - updatedAt > TTL(factType)` → archive. Durable types
6266
6276
  * have Infinity TTL, so this never fires for them; null/unknown use medium.
6267
- * EXCEPTION: a `plan`/`ongoing_context` with a still-upcoming event
6268
- * (`eventTimeEnd >= now`) is kept — it must not age-archive before the event
6269
- * happens; only rule 3 (after it passes) may archive it.
6277
+ * EXCEPTION: a `plan` / `ongoing_context` with an upcoming event or an event
6278
+ * within the grace period (`eventTimeEnd >= now - pastEventGraceMs`) stays
6279
+ * active, even after its activity TTL expires. Rule 3 archives it after the
6280
+ * grace period ends.
6270
6281
  * 5. Otherwise → keep.
6271
6282
  *
6272
6283
  * @param m Plaintext decay inputs for one row.
@@ -6597,7 +6608,9 @@ type MemoryCreate = Pick<CreateVaultMemoryOptions, "content" | "scope" | "factTy
6597
6608
  * `embedding` (with `embeddingModel`) only when you already have the new one.
6598
6609
  * @public
6599
6610
  */
6600
- type MemoryUpdate = Pick<UpdateVaultMemoryOptions, "content" | "scope" | "factType" | "eventTime" | "embedding" | "embeddingModel" | "kind" | "kindValue" | "level">;
6611
+ type MemoryUpdate = Pick<UpdateVaultMemoryOptions, "scope" | "factType" | "eventTime" | "embedding" | "embeddingModel" | "kind" | "kindValue" | "level"> & {
6612
+ content: string;
6613
+ };
6601
6614
  /**
6602
6615
  * Fact-only recall. Conversation chunks are message storage, not memory, so
6603
6616
  * the chunk-lane knobs are not part of this surface; nor is `folderId` (see
@@ -3902,6 +3902,13 @@ interface RecencyOptions {
3902
3902
  noDateMultiplier?: number;
3903
3903
  }
3904
3904
 
3905
+ type VaultCorpusCache = WeakMap<VaultMemoryOperationsContext, Map<string, Promise<StoredVaultMemory[]>>>;
3906
+ type RowRequests = Map<EmbeddingOptions["maskInput"], Map<string, Promise<number[]>>>;
3907
+ interface VaultPreparation {
3908
+ corpora: VaultCorpusCache;
3909
+ rows: WeakMap<VaultMemoryOperationsContext, RowRequests>;
3910
+ }
3911
+
3905
3912
  declare const DEFAULT_VAULT_CACHE_SIZE = 5000;
3906
3913
  /**
3907
3914
  * Create a VaultEmbeddingCache backed by an LRU with a default cap of
@@ -3919,6 +3926,7 @@ type VaultEmbeddingCache = Map<string, Float32Array>;
3919
3926
  * Options for the vault search tool.
3920
3927
  */
3921
3928
  interface MemoryVaultSearchOptions {
3929
+ /** @internal */ vaultPreparation?: VaultPreparation;
3922
3930
  onCandidateCount?: (count: number) => void;
3923
3931
  /** Maximum number of results to return (default: 5) */
3924
3932
  limit?: number;
@@ -4441,6 +4449,7 @@ interface RankedMemory {
4441
4449
  updatedAt: Date;
4442
4450
  }
4443
4451
  interface RecallOptions {
4452
+ /** @internal */ vaultPreparation?: VaultPreparation;
4444
4453
  /** Embedding cache shared with the rest of the turn, so the query is embedded once across lanes and the send. */
4445
4454
  embeddingCache?: Map<string, Float32Array>;
4446
4455
  /** Which kinds to search. Default: ['fact']. */
@@ -5660,7 +5669,8 @@ interface CreateVaultMemoryOptions {
5660
5669
  geohash?: string;
5661
5670
  }
5662
5671
  interface UpdateVaultMemoryOptions {
5663
- content: string;
5672
+ content?: string;
5673
+ expectedContent?: string;
5664
5674
  /** When true, the write is skipped (returns null) if the row carries a profile kind. */
5665
5675
  freeFormOnly?: boolean;
5666
5676
  /**
@@ -6264,9 +6274,10 @@ declare function ttlForType(factType: string | null, policy?: Partial<DecayPolic
6264
6274
  * null `eventTimeEnd` is still ongoing, so this rule does not fire for it.
6265
6275
  * 4. Age fallback — `now - updatedAt > TTL(factType)` → archive. Durable types
6266
6276
  * have Infinity TTL, so this never fires for them; null/unknown use medium.
6267
- * EXCEPTION: a `plan`/`ongoing_context` with a still-upcoming event
6268
- * (`eventTimeEnd >= now`) is kept — it must not age-archive before the event
6269
- * happens; only rule 3 (after it passes) may archive it.
6277
+ * EXCEPTION: a `plan` / `ongoing_context` with an upcoming event or an event
6278
+ * within the grace period (`eventTimeEnd >= now - pastEventGraceMs`) stays
6279
+ * active, even after its activity TTL expires. Rule 3 archives it after the
6280
+ * grace period ends.
6270
6281
  * 5. Otherwise → keep.
6271
6282
  *
6272
6283
  * @param m Plaintext decay inputs for one row.
@@ -6597,7 +6608,9 @@ type MemoryCreate = Pick<CreateVaultMemoryOptions, "content" | "scope" | "factTy
6597
6608
  * `embedding` (with `embeddingModel`) only when you already have the new one.
6598
6609
  * @public
6599
6610
  */
6600
- type MemoryUpdate = Pick<UpdateVaultMemoryOptions, "content" | "scope" | "factType" | "eventTime" | "embedding" | "embeddingModel" | "kind" | "kindValue" | "level">;
6611
+ type MemoryUpdate = Pick<UpdateVaultMemoryOptions, "scope" | "factType" | "eventTime" | "embedding" | "embeddingModel" | "kind" | "kindValue" | "level"> & {
6612
+ content: string;
6613
+ };
6601
6614
  /**
6602
6615
  * Fact-only recall. Conversation chunks are message storage, not memory, so
6603
6616
  * the chunk-lane knobs are not part of this surface; nor is `folderId` (see
@@ -20,7 +20,7 @@ import {
20
20
  taskTypeHeader,
21
21
  validateEndpointOverride,
22
22
  withInternalFlowMarker
23
- } from "./chunk-XW3LYSDA.mjs";
23
+ } from "./chunk-WRZTH4OS.mjs";
24
24
  import {
25
25
  BASE_URL,
26
26
  MCP_R2_DOMAIN,
@@ -923,15 +923,16 @@ async function refreshEncryptionKeyIfMatches(walletAddress, probeCiphertext, sig
923
923
  if (!parsed) {
924
924
  return false;
925
925
  }
926
+ const startEpoch = sessionEpoch;
926
927
  const cached = refreshCandidatesByAddress.get(walletAddress);
927
928
  if (cached) {
928
- return tryCommitCandidatesIfProbeMatches(walletAddress, parsed, cached);
929
+ return tryCommitCandidatesIfProbeMatches(walletAddress, parsed, cached, startEpoch);
929
930
  }
930
931
  let derivePromise = pendingCandidateDerives.get(walletAddress);
931
932
  if (!derivePromise) {
932
933
  derivePromise = (async () => {
933
934
  const candidates2 = await deriveRefreshCandidates(signMessage, embeddedWalletSigner);
934
- if (candidates2) {
935
+ if (candidates2 && sessionEpoch === startEpoch) {
935
936
  refreshCandidatesByAddress.set(walletAddress, candidates2);
936
937
  }
937
938
  return candidates2;
@@ -943,10 +944,10 @@ async function refreshEncryptionKeyIfMatches(walletAddress, probeCiphertext, sig
943
944
  pendingCandidateDerives.set(walletAddress, derivePromise);
944
945
  }
945
946
  const candidates = await derivePromise;
946
- if (!candidates) {
947
+ if (!candidates || sessionEpoch !== startEpoch) {
947
948
  return false;
948
949
  }
949
- return tryCommitCandidatesIfProbeMatches(walletAddress, parsed, candidates);
950
+ return tryCommitCandidatesIfProbeMatches(walletAddress, parsed, candidates, startEpoch);
950
951
  }
951
952
  function parsePrefixedCiphertext(probeCiphertext) {
952
953
  const version = probeCiphertext.startsWith("enc:v3:") ? "v3" : probeCiphertext.startsWith("enc:v2:") ? "v2" : null;
@@ -989,7 +990,7 @@ async function deriveRefreshCandidates(signMessage, embeddedWalletSigner) {
989
990
  }
990
991
  return { legacy: legacyKey, current: currentKey };
991
992
  }
992
- async function tryCommitCandidatesIfProbeMatches(walletAddress, parsed, candidates) {
993
+ async function tryCommitCandidatesIfProbeMatches(walletAddress, parsed, candidates, startEpoch) {
993
994
  const candidateHex = parsed.version === "v2" ? candidates.legacy : candidates.current;
994
995
  const keyBytes = hexToBytes(candidateHex);
995
996
  const candidateCryptoKey = await crypto.subtle.importKey(
@@ -1007,6 +1008,9 @@ async function tryCommitCandidatesIfProbeMatches(walletAddress, parsed, candidat
1007
1008
  );
1008
1009
  return false;
1009
1010
  }
1011
+ if (sessionEpoch !== startEpoch) {
1012
+ return false;
1013
+ }
1010
1014
  setStoredKey(walletAddress, { legacy: candidates.legacy, current: candidates.current });
1011
1015
  cryptoKeyCache.delete(`${walletAddress}:v2`);
1012
1016
  cryptoKeyCache.delete(`${walletAddress}:v3`);
@@ -13341,6 +13345,16 @@ async function updateVaultMemoryOp(ctx, id2, opts) {
13341
13345
  const probe = await ctx.vaultMemoryCollection.find(id2);
13342
13346
  if (probe.isDeleted || probe.supersededBy || !isOwnedByCtxUser(ctx, probe)) return null;
13343
13347
  if (opts.freeFormOnly && isKinded(probe)) return null;
13348
+ const readStoredContent = probe._getRaw("content");
13349
+ if (opts.expectedContent !== void 0) {
13350
+ const current = await vaultMemoryToStored(
13351
+ probe,
13352
+ ctx.walletAddress,
13353
+ ctx.signMessage,
13354
+ ctx.embeddedWalletSigner
13355
+ );
13356
+ if (current.content !== opts.expectedContent) return null;
13357
+ }
13344
13358
  const levelAfter = (current) => {
13345
13359
  if (opts.level !== void 0) return opts.level;
13346
13360
  if (opts.scope === void 0) return resolveMemoryLevel(current.level, current.scope);
@@ -13348,7 +13362,7 @@ async function updateVaultMemoryOp(ctx, id2, opts) {
13348
13362
  };
13349
13363
  assertValidKindLevel(opts.kind !== void 0 ? opts.kind : probe.kind, levelAfter(probe));
13350
13364
  const encryptedKindValue = await encryptKindValue(ctx, opts.kindValue);
13351
- const encryptedContent = ctx.walletAddress && ctx.signMessage ? await encryptVaultMemoryContent(
13365
+ const encryptedContent = opts.content !== void 0 && ctx.walletAddress && ctx.signMessage ? await encryptVaultMemoryContent(
13352
13366
  opts.content,
13353
13367
  ctx.walletAddress,
13354
13368
  ctx.signMessage,
@@ -13356,13 +13370,13 @@ async function updateVaultMemoryOp(ctx, id2, opts) {
13356
13370
  ) : opts.content;
13357
13371
  let stale = false;
13358
13372
  const record2 = probe;
13359
- const originalUpdatedAt = record2.updatedAt.getTime();
13360
13373
  await ctx.database.write(async () => {
13361
13374
  await assertCanWrite2(ctx);
13362
- if (record2.isDeleted || record2.supersededBy || !isOwnedByCtxUser(ctx, record2) || opts.freeFormOnly && isKinded(record2)) {
13375
+ if (record2.isDeleted || record2.supersededBy || !isOwnedByCtxUser(ctx, record2) || opts.freeFormOnly && isKinded(record2) || opts.expectedContent !== void 0 && record2._getRaw("content") !== readStoredContent) {
13363
13376
  stale = true;
13364
13377
  return;
13365
13378
  }
13379
+ const originalUpdatedAt = record2.updatedAt.getTime();
13366
13380
  const nextKind = opts.kind !== void 0 ? opts.kind : record2.kind;
13367
13381
  const nextLevel = levelAfter(record2);
13368
13382
  assertValidKindLevel(nextKind, nextLevel);
@@ -13378,7 +13392,7 @@ async function updateVaultMemoryOp(ctx, id2, opts) {
13378
13392
  replayedObservation = opts.observationSourceIds.every((id3) => observedSources.includes(id3));
13379
13393
  }
13380
13394
  await record2.update((r) => {
13381
- r._setRaw("content", encryptedContent);
13395
+ if (encryptedContent !== void 0) r._setRaw("content", encryptedContent);
13382
13396
  if (opts.level !== void 0) {
13383
13397
  r._setRaw("level", nextLevel);
13384
13398
  r._setRaw("scope", scopeForLevel(nextLevel));
@@ -14145,7 +14159,7 @@ function classifyDecay(m, now, policy) {
14145
14159
  if (m.source === "manual" || m.source === SOURCE_PHOTO) return "keep";
14146
14160
  if (isTimeSensitiveType(m.factType) && m.eventTimeEnd !== null) {
14147
14161
  if (m.eventTimeEnd < now - resolved.pastEventGraceMs) return "archive";
14148
- if (m.eventTimeEnd >= now) return "keep";
14162
+ return "keep";
14149
14163
  }
14150
14164
  if (now - lastActivityAt(m) > ttlForType(m.factType, resolved)) return "archive";
14151
14165
  return "keep";
@@ -15635,6 +15649,104 @@ function validate(parsed, originalQuery) {
15635
15649
  return { mode, subQueries };
15636
15650
  }
15637
15651
 
15652
+ // src/lib/memoryVault/vaultPreparation.ts
15653
+ function createVaultPreparation() {
15654
+ return { corpora: /* @__PURE__ */ new WeakMap(), rows: /* @__PURE__ */ new WeakMap() };
15655
+ }
15656
+ var pendingRows = /* @__PURE__ */ new WeakMap();
15657
+ function loadVaultCorpus(ctx, options, preparation) {
15658
+ if (!preparation) return getAllVaultMemoriesOp(ctx, options);
15659
+ const corpusCache = preparation.corpora;
15660
+ let pending = corpusCache.get(ctx);
15661
+ if (!pending) {
15662
+ pending = /* @__PURE__ */ new Map();
15663
+ corpusCache.set(ctx, pending);
15664
+ }
15665
+ const key = JSON.stringify(options ?? {});
15666
+ let request = pending.get(key);
15667
+ if (!request) {
15668
+ const entries = pending;
15669
+ request = getAllVaultMemoriesOp(ctx, options).catch((error) => {
15670
+ entries.delete(key);
15671
+ throw error;
15672
+ });
15673
+ pending.set(key, request);
15674
+ }
15675
+ return request;
15676
+ }
15677
+ function rowRequestsFor(registry2, ctx, maskInput) {
15678
+ let byMask = registry2.get(ctx);
15679
+ if (!byMask) {
15680
+ byMask = /* @__PURE__ */ new Map();
15681
+ registry2.set(ctx, byMask);
15682
+ }
15683
+ let requests = byMask.get(maskInput);
15684
+ if (!requests) {
15685
+ requests = /* @__PURE__ */ new Map();
15686
+ byMask.set(maskInput, requests);
15687
+ }
15688
+ return requests;
15689
+ }
15690
+ function embedVaultRows(ctx, options, rows, preparation) {
15691
+ const pending = rowRequestsFor(pendingRows, ctx, options.maskInput);
15692
+ const prepared = preparation && rowRequestsFor(preparation.rows, ctx, options.maskInput);
15693
+ const model = options.model ?? DEFAULT_API_EMBEDDING_MODEL;
15694
+ const keys = rows.map(
15695
+ (row) => JSON.stringify([
15696
+ row.uniqueId,
15697
+ row.updatedAt?.getTime(),
15698
+ row.content,
15699
+ model,
15700
+ options.baseUrl,
15701
+ options.timeoutMs,
15702
+ options.tokenTimeoutMs,
15703
+ options.totalTimeoutMs
15704
+ ])
15705
+ );
15706
+ const missing = /* @__PURE__ */ new Map();
15707
+ rows.forEach((row, index) => {
15708
+ if (!pending.has(keys[index]) && !prepared?.has(keys[index])) missing.set(keys[index], row);
15709
+ });
15710
+ if (missing.size > 0) {
15711
+ const entries = pending;
15712
+ const batch = Promise.resolve().then(
15713
+ () => generateEmbeddings(
15714
+ [...missing.values()].map((row) => row.content),
15715
+ options
15716
+ )
15717
+ );
15718
+ [...missing].forEach(([key, row], index) => {
15719
+ const request = batch.then((vectors) => {
15720
+ const vector = vectors[index];
15721
+ updateVaultMemoryEmbeddingOp(ctx, row.uniqueId, JSON.stringify(vector), model, {
15722
+ content: row.content,
15723
+ updatedAt: row.updatedAt?.getTime()
15724
+ }).catch(() => {
15725
+ });
15726
+ return vector;
15727
+ }).finally(() => {
15728
+ entries.delete(key);
15729
+ if (entries.size === 0) pendingRows.get(ctx)?.delete(options.maskInput);
15730
+ });
15731
+ pending.set(key, request);
15732
+ });
15733
+ }
15734
+ return Promise.all(
15735
+ keys.map((key) => {
15736
+ const cached = prepared?.get(key);
15737
+ if (cached) return cached;
15738
+ const request = pending.get(key);
15739
+ if (!prepared) return request;
15740
+ const retained = request.catch((error) => {
15741
+ prepared.delete(key);
15742
+ throw error;
15743
+ });
15744
+ prepared.set(key, retained);
15745
+ return retained;
15746
+ })
15747
+ );
15748
+ }
15749
+
15638
15750
  // src/lib/memoryVault/vectorVersion.ts
15639
15751
  var vectorRowVersion = /* @__PURE__ */ new WeakMap();
15640
15752
  function contentFingerprint(content) {
@@ -16197,11 +16309,11 @@ async function rankComposite(originalQuery, originalQueryEmbedding, subQueries,
16197
16309
  return combined.slice(0, limit);
16198
16310
  }
16199
16311
  async function preEmbedVaultMemories(vaultCtx, embeddingOptions, cache, options) {
16200
- const memories = await getAllVaultMemoriesOp(vaultCtx, options);
16312
+ const memories = await loadVaultCorpus(vaultCtx, options);
16201
16313
  const currentModel = embeddingOptions.model ?? DEFAULT_API_EMBEDDING_MODEL;
16202
16314
  const uncachedTexts = [];
16203
16315
  const uncachedKeys = [];
16204
- const uncachedIds = [];
16316
+ const uncachedRows = [];
16205
16317
  const uncachedVersions = [];
16206
16318
  for (const m of memories) {
16207
16319
  const content = m.content;
@@ -16220,12 +16332,12 @@ async function preEmbedVaultMemories(vaultCtx, embeddingOptions, cache, options)
16220
16332
  }
16221
16333
  uncachedTexts.push(m.content);
16222
16334
  uncachedKeys.push(m.uniqueId);
16223
- uncachedIds.push(m.uniqueId);
16335
+ uncachedRows.push(m);
16224
16336
  uncachedVersions.push(m.updatedAt);
16225
16337
  }
16226
16338
  }
16227
16339
  if (uncachedTexts.length > 0) {
16228
- const embeddings = await generateEmbeddings(uncachedTexts, embeddingOptions);
16340
+ const embeddings = await embedVaultRows(vaultCtx, embeddingOptions, uncachedRows);
16229
16341
  for (let i = 0; i < uncachedKeys.length; i++) {
16230
16342
  cacheRowVector(
16231
16343
  cache,
@@ -16234,13 +16346,6 @@ async function preEmbedVaultMemories(vaultCtx, embeddingOptions, cache, options)
16234
16346
  uncachedVersions[i],
16235
16347
  uncachedTexts[i]
16236
16348
  );
16237
- updateVaultMemoryEmbeddingOp(
16238
- vaultCtx,
16239
- uncachedIds[i],
16240
- JSON.stringify(embeddings[i]),
16241
- currentModel
16242
- ).catch(() => {
16243
- });
16244
16349
  }
16245
16350
  }
16246
16351
  }
@@ -16382,10 +16487,7 @@ async function buildProjectedCorpus(query, vaultCtx, embeddingOptions, cache, qu
16382
16487
  let laneVecs;
16383
16488
  if (opts.embedStats) opts.embedStats.rowsEmbedded += laneRows.length;
16384
16489
  try {
16385
- laneVecs = await generateEmbeddings(
16386
- laneRows.map((m) => m.content),
16387
- embeddingOptions
16388
- );
16490
+ laneVecs = await embedVaultRows(vaultCtx, embeddingOptions, laneRows);
16389
16491
  } catch (err) {
16390
16492
  laneEmbedFailed = true;
16391
16493
  getLogger().warn(
@@ -16399,13 +16501,6 @@ async function buildProjectedCorpus(query, vaultCtx, embeddingOptions, cache, qu
16399
16501
  cacheRowVector(cache, m.uniqueId, vec, m.updatedAt, m.content);
16400
16502
  resolvedThisCall.add(m.uniqueId);
16401
16503
  vectored.push({ uniqueId: m.uniqueId, embedding: vec, updatedAt: m.updatedAt });
16402
- updateVaultMemoryEmbeddingOp(
16403
- vaultCtx,
16404
- m.uniqueId,
16405
- JSON.stringify(vecs[i]),
16406
- currentModel
16407
- ).catch(() => {
16408
- });
16409
16504
  });
16410
16505
  }
16411
16506
  }
@@ -16597,9 +16692,10 @@ async function prepareVaultCandidates(query, vaultCtx, embeddingOptions, cache,
16597
16692
  };
16598
16693
  }
16599
16694
  } else {
16600
- const loaded = await getAllVaultMemoriesOp(
16695
+ const loaded = await loadVaultCorpus(
16601
16696
  vaultCtx,
16602
- Object.keys(queryOpts).length > 0 ? queryOpts : void 0
16697
+ Object.keys(queryOpts).length > 0 ? queryOpts : void 0,
16698
+ searchOptions?.vaultPreparation
16603
16699
  );
16604
16700
  vaultSize = loaded.length;
16605
16701
  rowsDecrypted = loaded.length;
@@ -16672,7 +16768,12 @@ async function prepareVaultCandidates(query, vaultCtx, embeddingOptions, cache,
16672
16768
  let newEmbeddings;
16673
16769
  embedStats.rowsEmbedded += uncachedTexts.length;
16674
16770
  try {
16675
- newEmbeddings = await generateEmbeddings(uncachedTexts, embeddingOptions);
16771
+ newEmbeddings = await embedVaultRows(
16772
+ vaultCtx,
16773
+ embeddingOptions,
16774
+ uncachedIndices.map((index) => memories[index]),
16775
+ searchOptions?.vaultPreparation
16776
+ );
16676
16777
  } catch (err) {
16677
16778
  embeddingFailure = true;
16678
16779
  getLogger().warn(
@@ -16688,13 +16789,6 @@ async function prepareVaultCandidates(query, vaultCtx, embeddingOptions, cache,
16688
16789
  memories[uncachedIndices[j]].updatedAt,
16689
16790
  memories[uncachedIndices[j]].content
16690
16791
  );
16691
- updateVaultMemoryEmbeddingOp(
16692
- vaultCtx,
16693
- memories[uncachedIndices[j]].uniqueId,
16694
- JSON.stringify(newEmbeddings[j]),
16695
- currentModel
16696
- ).catch(() => {
16697
- });
16698
16792
  }
16699
16793
  }
16700
16794
  }
@@ -17098,7 +17192,6 @@ async function retainWithPersistence(content, ctx, options = {}) {
17098
17192
  const factTypeUpdate = pickFactTypeUpdate(existing, options.factType);
17099
17193
  const resurrect = resurrectFields(existing);
17100
17194
  const updated = await ctx.persistence.update(targetId, {
17101
- content: existing.content,
17102
17195
  freeFormOnly: true,
17103
17196
  proofCountIncrement: 1,
17104
17197
  observationSourceIds: options.sourceChunkIds,
@@ -17302,7 +17395,7 @@ async function tryConsolidate(trimmed, ctx, options, prepared) {
17302
17395
  content: m.content,
17303
17396
  similarity: m.similarity
17304
17397
  }));
17305
- const { consolidateMemory: doConsolidate } = await import("./consolidate-4CQJYKRK.mjs");
17398
+ const { consolidateMemory: doConsolidate } = await import("./consolidate-HKL5EGZT.mjs");
17306
17399
  const decision = await doConsolidate(trimmed, candidates, consolidateOptions);
17307
17400
  if (decision.action === "create") return decision.fallbackReason ? null : { create: true };
17308
17401
  if (decision.action === "supersede" && decision.content) {
@@ -17337,7 +17430,6 @@ async function tryConsolidate(trimmed, ctx, options, prepared) {
17337
17430
  const factTypeUpdate = pickFactTypeUpdate(existing, options.factType);
17338
17431
  const resurrect = resurrectFields(existing);
17339
17432
  const updated = await ctx.persistence.update(decision.targetId, {
17340
- content: existing.content,
17341
17433
  freeFormOnly: true,
17342
17434
  proofCountIncrement: 1,
17343
17435
  observationSourceIds: options.sourceChunkIds,
@@ -17365,6 +17457,10 @@ async function tryConsolidate(trimmed, ctx, options, prepared) {
17365
17457
  };
17366
17458
  }
17367
17459
  if (decision.action === "update" && decision.targetId && decision.content) {
17460
+ const expectedContent = candidates.find(
17461
+ (candidate) => candidate.id === decision.targetId
17462
+ )?.content;
17463
+ if (expectedContent === void 0) return null;
17368
17464
  const existing = await ctx.persistence.get(decision.targetId);
17369
17465
  if (!existing || existing.supersededBy) {
17370
17466
  return abandonToRace(
@@ -17372,6 +17468,9 @@ async function tryConsolidate(trimmed, ctx, options, prepared) {
17372
17468
  `update: target ${decision.targetId} ${existing ? "superseded" : "deleted"} before the write`
17373
17469
  );
17374
17470
  }
17471
+ if (existing.content !== expectedContent) {
17472
+ return abandonToRace(options, `update: target ${decision.targetId} edited before the write`);
17473
+ }
17375
17474
  const mergedSourceIds = unionStrings(
17376
17475
  existing.sourceChunkIds ?? [],
17377
17476
  options.sourceChunkIds ?? []
@@ -17384,6 +17483,7 @@ async function tryConsolidate(trimmed, ctx, options, prepared) {
17384
17483
  const resurrect = resurrectFields(existing);
17385
17484
  const updated = await ctx.persistence.update(decision.targetId, {
17386
17485
  content: decision.content,
17486
+ expectedContent,
17387
17487
  freeFormOnly: true,
17388
17488
  proofCountIncrement: 1,
17389
17489
  observationSourceIds: options.sourceChunkIds,
@@ -17396,10 +17496,10 @@ async function tryConsolidate(trimmed, ctx, options, prepared) {
17396
17496
  ...factTypeUpdate !== void 0 && { factType: factTypeUpdate }
17397
17497
  });
17398
17498
  if (!updated) {
17399
- await assertMergeTargetGoneOrThrow(ctx, decision.targetId);
17499
+ await assertMergeTargetGoneOrThrow(ctx, decision.targetId, expectedContent);
17400
17500
  return abandonToRace(
17401
17501
  options,
17402
- `update: target ${decision.targetId} deleted mid-write (consolidated rewrite lost)`
17502
+ `update: target ${decision.targetId} changed mid-write (consolidated rewrite lost)`
17403
17503
  );
17404
17504
  }
17405
17505
  cacheRowVector(
@@ -17426,9 +17526,9 @@ async function tryConsolidate(trimmed, ctx, options, prepared) {
17426
17526
  );
17427
17527
  return null;
17428
17528
  }
17429
- async function assertMergeTargetGoneOrThrow(ctx, targetId) {
17529
+ async function assertMergeTargetGoneOrThrow(ctx, targetId, expectedContent) {
17430
17530
  const stillExists = await (ctx.persistence.getFresh ? ctx.persistence.getFresh(targetId) : ctx.persistence.get(targetId));
17431
- if (stillExists && (stillExists.kind === null || stillExists.kind === void 0)) {
17531
+ if (stillExists && (stillExists.kind === null || stillExists.kind === void 0) && (expectedContent === void 0 || stillExists.content === expectedContent)) {
17432
17532
  throw new RetainError(
17433
17533
  "merge-not-persisted",
17434
17534
  `retain: merge into memory ${targetId} failed to persist`
@@ -19235,6 +19335,7 @@ async function recall(query, ctx, options = {}) {
19235
19335
  },
19236
19336
  ...options.rrfK !== void 0 && { rrfK: options.rrfK },
19237
19337
  ...options.decryptLast !== void 0 && { decryptLast: options.decryptLast },
19338
+ ...options.vaultPreparation && { vaultPreparation: options.vaultPreparation },
19238
19339
  ...subQueries && { subQueries },
19239
19340
  ...options.scopes && { scopes: options.scopes },
19240
19341
  ...options.folderId !== void 0 && { folderId: options.folderId },
@@ -20814,16 +20915,16 @@ function createRemoteMemoryPipeline(options) {
20814
20915
  create: (input) => write(createRow(input), 0),
20815
20916
  update: async (id2, patch) => {
20816
20917
  const existing = await get(id2);
20817
- if (!existing || patch.freeFormOnly && existing.kind !== null && existing.kind !== void 0)
20918
+ if (!existing || patch.expectedContent !== void 0 && existing.content !== patch.expectedContent || patch.freeFormOnly && existing.kind !== null && existing.kind !== void 0)
20818
20919
  return null;
20819
20920
  const snapshot = snapshots.get(id2);
20820
20921
  const memory = {
20821
20922
  ...snapshot.memory,
20822
- content: patch.content,
20923
+ content: patch.content ?? snapshot.memory.content,
20823
20924
  updated_at: patch.preserveUpdatedAt ? snapshot.memory.updated_at : Math.max(Date.now(), (snapshot.memory.topics_extracted_at ?? 0) + 1)
20824
20925
  };
20825
20926
  if (patch.scope !== void 0) memory.scope = patch.scope;
20826
- if (patch.embedding === void 0 && patch.content !== snapshot.memory.content) {
20927
+ if (patch.embedding === void 0 && patch.content !== void 0 && patch.content !== snapshot.memory.content) {
20827
20928
  delete memory.embedding;
20828
20929
  delete memory.embedding_model;
20829
20930
  }
@@ -22825,13 +22926,20 @@ async function synthesizeProfile(ctx, options = {}) {
22825
22926
  }
22826
22927
  }
22827
22928
  }
22929
+ const vaultPreparation = createVaultPreparation();
22828
22930
  const settled = await Promise.allSettled(
22829
22931
  facets.map(async (facet) => {
22830
22932
  const prior = previous?.sections.find((s) => s.key === facet.key);
22831
22933
  if (prior && !staleKeys.has(facet.key)) {
22832
22934
  return prior;
22833
22935
  }
22834
- return synthesizeFacet(facet, recallCtx, options, fallbackPriors.get(facet.key));
22936
+ return synthesizeFacet(
22937
+ facet,
22938
+ recallCtx,
22939
+ options,
22940
+ fallbackPriors.get(facet.key),
22941
+ vaultPreparation
22942
+ );
22835
22943
  })
22836
22944
  );
22837
22945
  const sections = settled.map((r, i) => {
@@ -22985,7 +23093,7 @@ async function attributeFacts(ctx, candidates, facets) {
22985
23093
  }
22986
23094
  return keys;
22987
23095
  }
22988
- async function synthesizeFacet(facet, ctx, options, prior) {
23096
+ async function synthesizeFacet(facet, ctx, options, prior, vaultPreparation) {
22989
23097
  const scopes = options.scopes ?? DEFAULT_SCOPES;
22990
23098
  const limit = options.limit ?? DEFAULT_FACET_RECALL_LIMIT;
22991
23099
  const factTypeWeights = { ...DEFAULT_PROFILE_FACT_TYPE_WEIGHTS, ...options.factTypeWeights };
@@ -23002,6 +23110,7 @@ async function synthesizeFacet(facet, ctx, options, prior) {
23002
23110
  };
23003
23111
  }
23004
23112
  const recalled = await recall(facet.query, ctx, {
23113
+ vaultPreparation,
23005
23114
  scopes,
23006
23115
  limit: hasReviewGate ? RECALL_MAX_LIMIT : limit,
23007
23116
  types: ["fact"],