@anuma/sdk 9.1.3 → 9.1.5

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']. */
@@ -11665,6 +11674,10 @@ interface MemoryMigrationOptions extends Pick<RemoteMemoryPersistenceOptions, "b
11665
11674
  name: string;
11666
11675
  kind?: string;
11667
11676
  }[];
11677
+ /** Drain all local memory writes, block new writes until the task returns, and retain the block if the remote handoff fails. */
11678
+ withWriteBarrier: (task: () => Promise<MemoryMigrationResult>) => Promise<MemoryMigrationResult>;
11679
+ /** Route all subsequent memory writes to the remote store before this callback returns. */
11680
+ onActivated: () => void | Promise<void>;
11668
11681
  signal?: AbortSignal;
11669
11682
  onProgress?: (progress: MemoryMigrationProgress) => void;
11670
11683
  }
@@ -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']. */
@@ -11665,6 +11674,10 @@ interface MemoryMigrationOptions extends Pick<RemoteMemoryPersistenceOptions, "b
11665
11674
  name: string;
11666
11675
  kind?: string;
11667
11676
  }[];
11677
+ /** Drain all local memory writes, block new writes until the task returns, and retain the block if the remote handoff fails. */
11678
+ withWriteBarrier: (task: () => Promise<MemoryMigrationResult>) => Promise<MemoryMigrationResult>;
11679
+ /** Route all subsequent memory writes to the remote store before this callback returns. */
11680
+ onActivated: () => void | Promise<void>;
11668
11681
  signal?: AbortSignal;
11669
11682
  onProgress?: (progress: MemoryMigrationProgress) => void;
11670
11683
  }
@@ -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`);
@@ -17714,6 +17718,104 @@ function validate(parsed, originalQuery) {
17714
17718
  return { mode, subQueries };
17715
17719
  }
17716
17720
 
17721
+ // src/lib/memoryVault/vaultPreparation.ts
17722
+ function createVaultPreparation() {
17723
+ return { corpora: /* @__PURE__ */ new WeakMap(), rows: /* @__PURE__ */ new WeakMap() };
17724
+ }
17725
+ var pendingRows = /* @__PURE__ */ new WeakMap();
17726
+ function loadVaultCorpus(ctx, options, preparation) {
17727
+ if (!preparation) return getAllVaultMemoriesOp(ctx, options);
17728
+ const corpusCache = preparation.corpora;
17729
+ let pending = corpusCache.get(ctx);
17730
+ if (!pending) {
17731
+ pending = /* @__PURE__ */ new Map();
17732
+ corpusCache.set(ctx, pending);
17733
+ }
17734
+ const key = JSON.stringify(options ?? {});
17735
+ let request = pending.get(key);
17736
+ if (!request) {
17737
+ const entries = pending;
17738
+ request = getAllVaultMemoriesOp(ctx, options).catch((error) => {
17739
+ entries.delete(key);
17740
+ throw error;
17741
+ });
17742
+ pending.set(key, request);
17743
+ }
17744
+ return request;
17745
+ }
17746
+ function rowRequestsFor(registry2, ctx, maskInput) {
17747
+ let byMask = registry2.get(ctx);
17748
+ if (!byMask) {
17749
+ byMask = /* @__PURE__ */ new Map();
17750
+ registry2.set(ctx, byMask);
17751
+ }
17752
+ let requests = byMask.get(maskInput);
17753
+ if (!requests) {
17754
+ requests = /* @__PURE__ */ new Map();
17755
+ byMask.set(maskInput, requests);
17756
+ }
17757
+ return requests;
17758
+ }
17759
+ function embedVaultRows(ctx, options, rows, preparation) {
17760
+ const pending = rowRequestsFor(pendingRows, ctx, options.maskInput);
17761
+ const prepared = preparation && rowRequestsFor(preparation.rows, ctx, options.maskInput);
17762
+ const model = options.model ?? DEFAULT_API_EMBEDDING_MODEL;
17763
+ const keys = rows.map(
17764
+ (row) => JSON.stringify([
17765
+ row.uniqueId,
17766
+ row.updatedAt?.getTime(),
17767
+ row.content,
17768
+ model,
17769
+ options.baseUrl,
17770
+ options.timeoutMs,
17771
+ options.tokenTimeoutMs,
17772
+ options.totalTimeoutMs
17773
+ ])
17774
+ );
17775
+ const missing = /* @__PURE__ */ new Map();
17776
+ rows.forEach((row, index) => {
17777
+ if (!pending.has(keys[index]) && !prepared?.has(keys[index])) missing.set(keys[index], row);
17778
+ });
17779
+ if (missing.size > 0) {
17780
+ const entries = pending;
17781
+ const batch = Promise.resolve().then(
17782
+ () => generateEmbeddings(
17783
+ [...missing.values()].map((row) => row.content),
17784
+ options
17785
+ )
17786
+ );
17787
+ [...missing].forEach(([key, row], index) => {
17788
+ const request = batch.then((vectors) => {
17789
+ const vector = vectors[index];
17790
+ updateVaultMemoryEmbeddingOp(ctx, row.uniqueId, JSON.stringify(vector), model, {
17791
+ content: row.content,
17792
+ updatedAt: row.updatedAt?.getTime()
17793
+ }).catch(() => {
17794
+ });
17795
+ return vector;
17796
+ }).finally(() => {
17797
+ entries.delete(key);
17798
+ if (entries.size === 0) pendingRows.get(ctx)?.delete(options.maskInput);
17799
+ });
17800
+ pending.set(key, request);
17801
+ });
17802
+ }
17803
+ return Promise.all(
17804
+ keys.map((key) => {
17805
+ const cached = prepared?.get(key);
17806
+ if (cached) return cached;
17807
+ const request = pending.get(key);
17808
+ if (!prepared) return request;
17809
+ const retained = request.catch((error) => {
17810
+ prepared.delete(key);
17811
+ throw error;
17812
+ });
17813
+ prepared.set(key, retained);
17814
+ return retained;
17815
+ })
17816
+ );
17817
+ }
17818
+
17717
17819
  // src/lib/memoryVault/vectorVersion.ts
17718
17820
  var vectorRowVersion = /* @__PURE__ */ new WeakMap();
17719
17821
  function contentFingerprint(content) {
@@ -18276,11 +18378,11 @@ async function rankComposite(originalQuery, originalQueryEmbedding, subQueries,
18276
18378
  return combined.slice(0, limit);
18277
18379
  }
18278
18380
  async function preEmbedVaultMemories(vaultCtx, embeddingOptions, cache, options) {
18279
- const memories = await getAllVaultMemoriesOp(vaultCtx, options);
18381
+ const memories = await loadVaultCorpus(vaultCtx, options);
18280
18382
  const currentModel = embeddingOptions.model ?? DEFAULT_API_EMBEDDING_MODEL;
18281
18383
  const uncachedTexts = [];
18282
18384
  const uncachedKeys = [];
18283
- const uncachedIds = [];
18385
+ const uncachedRows = [];
18284
18386
  const uncachedVersions = [];
18285
18387
  for (const m of memories) {
18286
18388
  const content = m.content;
@@ -18299,12 +18401,12 @@ async function preEmbedVaultMemories(vaultCtx, embeddingOptions, cache, options)
18299
18401
  }
18300
18402
  uncachedTexts.push(m.content);
18301
18403
  uncachedKeys.push(m.uniqueId);
18302
- uncachedIds.push(m.uniqueId);
18404
+ uncachedRows.push(m);
18303
18405
  uncachedVersions.push(m.updatedAt);
18304
18406
  }
18305
18407
  }
18306
18408
  if (uncachedTexts.length > 0) {
18307
- const embeddings = await generateEmbeddings(uncachedTexts, embeddingOptions);
18409
+ const embeddings = await embedVaultRows(vaultCtx, embeddingOptions, uncachedRows);
18308
18410
  for (let i = 0; i < uncachedKeys.length; i++) {
18309
18411
  cacheRowVector(
18310
18412
  cache,
@@ -18313,13 +18415,6 @@ async function preEmbedVaultMemories(vaultCtx, embeddingOptions, cache, options)
18313
18415
  uncachedVersions[i],
18314
18416
  uncachedTexts[i]
18315
18417
  );
18316
- updateVaultMemoryEmbeddingOp(
18317
- vaultCtx,
18318
- uncachedIds[i],
18319
- JSON.stringify(embeddings[i]),
18320
- currentModel
18321
- ).catch(() => {
18322
- });
18323
18418
  }
18324
18419
  }
18325
18420
  }
@@ -18461,10 +18556,7 @@ async function buildProjectedCorpus(query, vaultCtx, embeddingOptions, cache, qu
18461
18556
  let laneVecs;
18462
18557
  if (opts.embedStats) opts.embedStats.rowsEmbedded += laneRows.length;
18463
18558
  try {
18464
- laneVecs = await generateEmbeddings(
18465
- laneRows.map((m) => m.content),
18466
- embeddingOptions
18467
- );
18559
+ laneVecs = await embedVaultRows(vaultCtx, embeddingOptions, laneRows);
18468
18560
  } catch (err) {
18469
18561
  laneEmbedFailed = true;
18470
18562
  getLogger().warn(
@@ -18478,13 +18570,6 @@ async function buildProjectedCorpus(query, vaultCtx, embeddingOptions, cache, qu
18478
18570
  cacheRowVector(cache, m.uniqueId, vec, m.updatedAt, m.content);
18479
18571
  resolvedThisCall.add(m.uniqueId);
18480
18572
  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
18573
  });
18489
18574
  }
18490
18575
  }
@@ -18676,9 +18761,10 @@ async function prepareVaultCandidates(query, vaultCtx, embeddingOptions, cache,
18676
18761
  };
18677
18762
  }
18678
18763
  } else {
18679
- const loaded = await getAllVaultMemoriesOp(
18764
+ const loaded = await loadVaultCorpus(
18680
18765
  vaultCtx,
18681
- Object.keys(queryOpts).length > 0 ? queryOpts : void 0
18766
+ Object.keys(queryOpts).length > 0 ? queryOpts : void 0,
18767
+ searchOptions?.vaultPreparation
18682
18768
  );
18683
18769
  vaultSize = loaded.length;
18684
18770
  rowsDecrypted = loaded.length;
@@ -18751,7 +18837,12 @@ async function prepareVaultCandidates(query, vaultCtx, embeddingOptions, cache,
18751
18837
  let newEmbeddings;
18752
18838
  embedStats.rowsEmbedded += uncachedTexts.length;
18753
18839
  try {
18754
- newEmbeddings = await generateEmbeddings(uncachedTexts, embeddingOptions);
18840
+ newEmbeddings = await embedVaultRows(
18841
+ vaultCtx,
18842
+ embeddingOptions,
18843
+ uncachedIndices.map((index) => memories[index]),
18844
+ searchOptions?.vaultPreparation
18845
+ );
18755
18846
  } catch (err) {
18756
18847
  embeddingFailure = true;
18757
18848
  getLogger().warn(
@@ -18767,13 +18858,6 @@ async function prepareVaultCandidates(query, vaultCtx, embeddingOptions, cache,
18767
18858
  memories[uncachedIndices[j]].updatedAt,
18768
18859
  memories[uncachedIndices[j]].content
18769
18860
  );
18770
- updateVaultMemoryEmbeddingOp(
18771
- vaultCtx,
18772
- memories[uncachedIndices[j]].uniqueId,
18773
- JSON.stringify(newEmbeddings[j]),
18774
- currentModel
18775
- ).catch(() => {
18776
- });
18777
18861
  }
18778
18862
  }
18779
18863
  }
@@ -21340,6 +21424,7 @@ async function recall(query, ctx, options = {}) {
21340
21424
  },
21341
21425
  ...options.rrfK !== void 0 && { rrfK: options.rrfK },
21342
21426
  ...options.decryptLast !== void 0 && { decryptLast: options.decryptLast },
21427
+ ...options.vaultPreparation && { vaultPreparation: options.vaultPreparation },
21343
21428
  ...subQueries && { subQueries },
21344
21429
  ...options.scopes && { scopes: options.scopes },
21345
21430
  ...options.folderId !== void 0 && { folderId: options.folderId },
@@ -23357,6 +23442,8 @@ async function backfillTopics(ctx) {
23357
23442
  if (topicless.length > 0) await backfillMemoryTopicsOp(ctx, topicless);
23358
23443
  }
23359
23444
  async function migrateMemoriesToRemote(options) {
23445
+ if (typeof options.withWriteBarrier !== "function" || typeof options.onActivated !== "function")
23446
+ throw new Error("A write barrier and a remote store handoff are required");
23360
23447
  const { canonical, signal } = options;
23361
23448
  const rings = [canonical, ...options.fallbacks ?? []];
23362
23449
  const model = options.embeddingOptions?.model ?? DEFAULT_API_EMBEDDING_MODEL;
@@ -23384,93 +23471,98 @@ async function migrateMemoriesToRemote(options) {
23384
23471
  const placeholders = /* @__PURE__ */ new Set();
23385
23472
  const truncated = /* @__PURE__ */ new Set();
23386
23473
  let imported = 0;
23387
- for (let pass = 0; ; pass++) {
23388
- const rows = await getAllVaultMemoryRowsOp(options.vaultCtx);
23389
- for (const raw of rows) if (invalid2(raw)) skipped.set(String(raw.id), "invalid");
23390
- const candidates = rows.filter((raw) => !skipped.has(String(raw.id)));
23391
- const missing = await missingIds(request, candidates.map(revision), signal);
23392
- const pending = /* @__PURE__ */ new Map();
23393
- for (const raw of candidates)
23394
- if (missing.has(raw.id)) pending.set(raw.id, raw.updated_at);
23395
- if (pending.size === 0 && !active) {
23396
- const remote = await remoteMetadata(
23397
- request,
23398
- candidates.map((raw) => raw.id),
23399
- signal
23400
- );
23401
- const needsVector = (raw, server) => !!options.embeddingOptions && !embedding(raw, model) && server.embedding_model !== model;
23402
- for (const raw of candidates) {
23403
- const server = remote.get(raw.id);
23404
- if (server && server.updated_at === raw.updated_at && (!sameMetadata2(raw, server, model) || needsVector(raw, server)))
23405
- pending.set(raw.id, raw.updated_at + 1);
23406
- }
23407
- }
23408
- if (pending.size === 0) {
23409
- const result = (state) => ({
23410
- state,
23411
- total: rows.length,
23412
- imported,
23413
- skipped: [...skipped].map(([memoryId, reason]) => ({ memoryId, reason })),
23414
- reembedded: reembedded.size,
23415
- unembedded: unembedded.size,
23416
- placeholderTombstones: placeholders.size,
23417
- truncatedSourceChunkIds: truncated.size
23418
- });
23419
- if (skipped.size > 0 && !options.allowSkipped) return result("migrating");
23420
- if (!claimed) await importBatch(request, canonical.keyId, [], signal);
23421
- if (!active)
23422
- await request(
23423
- "/account",
23424
- { method: "PUT", body: JSON.stringify({ state: "active" }) },
23474
+ const uploadPasses = async (finalizing = false) => {
23475
+ for (let pass = 0; ; pass++) {
23476
+ const rows = await getAllVaultMemoryRowsOp(options.vaultCtx);
23477
+ for (const raw of rows) if (invalid2(raw)) skipped.set(String(raw.id), "invalid");
23478
+ const candidates = rows.filter((raw) => !skipped.has(String(raw.id)));
23479
+ const missing = await missingIds(request, candidates.map(revision), signal);
23480
+ const pending = /* @__PURE__ */ new Map();
23481
+ for (const raw of candidates)
23482
+ if (missing.has(raw.id)) pending.set(raw.id, raw.updated_at);
23483
+ if (pending.size === 0 && !active) {
23484
+ const remote = await remoteMetadata(
23485
+ request,
23486
+ candidates.map((raw) => raw.id),
23425
23487
  signal
23426
23488
  );
23427
- return result("active");
23428
- }
23429
- if (pass >= MAX_UPLOAD_PASSES)
23430
- throw new RemoteMemoryError(
23431
- "Nearby still reports memories missing after importing them",
23432
- 409,
23433
- "migration_incomplete"
23434
- );
23435
- const prepared = [];
23436
- for (const raw of candidates) {
23437
- const updatedAt = pending.get(raw.id);
23438
- if (updatedAt === void 0) continue;
23439
- try {
23440
- prepared.push(await prepareRow(raw, canonical, rings, model, updatedAt));
23441
- } catch (error) {
23442
- if (!(error instanceof MemoryKeyError)) throw error;
23443
- skipped.set(raw.id, "unreadable");
23444
- }
23445
- }
23446
- const usable = new Set(prepared.filter((p) => p.row.embedding).map((p) => p.row.memory_id));
23447
- if (options.embeddingOptions)
23448
- await embedMissing(prepared, options.embeddingOptions, model, signal);
23449
- const local = new Map(candidates.map((raw) => [raw.id, raw]));
23450
- const upload = [];
23451
- for (const { row, plaintext } of prepared) {
23452
- const id2 = row.memory_id;
23453
- if (!fitsNearby(row)) {
23454
- skipped.set(id2, "invalid");
23455
- continue;
23489
+ const needsVector = (raw, server) => !!options.embeddingOptions && !embedding(raw, model) && server.embedding_model !== model;
23490
+ for (const raw of candidates) {
23491
+ const server = remote.get(raw.id);
23492
+ if (server && server.updated_at === raw.updated_at && (!sameMetadata2(raw, server, model) || needsVector(raw, server)))
23493
+ pending.set(raw.id, raw.updated_at + 1);
23494
+ }
23495
+ }
23496
+ if (pending.size === 0) {
23497
+ const result = (state) => ({
23498
+ state,
23499
+ total: rows.length,
23500
+ imported,
23501
+ skipped: [...skipped].map(([memoryId, reason]) => ({ memoryId, reason })),
23502
+ reembedded: reembedded.size,
23503
+ unembedded: unembedded.size,
23504
+ placeholderTombstones: placeholders.size,
23505
+ truncatedSourceChunkIds: truncated.size
23506
+ });
23507
+ if (skipped.size > 0 && !options.allowSkipped) return result("migrating");
23508
+ if (!finalizing) return options.withWriteBarrier(() => uploadPasses(true));
23509
+ if (!claimed) await importBatch(request, canonical.keyId, [], signal);
23510
+ if (!active)
23511
+ await request(
23512
+ "/account",
23513
+ { method: "PUT", body: JSON.stringify({ state: "active" }) },
23514
+ signal
23515
+ );
23516
+ await options.onActivated();
23517
+ return result("active");
23518
+ }
23519
+ if (pass >= MAX_UPLOAD_PASSES)
23520
+ throw new RemoteMemoryError(
23521
+ "Nearby still reports memories missing after importing them",
23522
+ 409,
23523
+ "migration_incomplete"
23524
+ );
23525
+ const prepared = [];
23526
+ for (const raw of candidates) {
23527
+ const updatedAt = pending.get(raw.id);
23528
+ if (updatedAt === void 0) continue;
23529
+ try {
23530
+ prepared.push(await prepareRow(raw, canonical, rings, model, updatedAt));
23531
+ } catch (error) {
23532
+ if (!(error instanceof MemoryKeyError)) throw error;
23533
+ skipped.set(raw.id, "unreadable");
23534
+ }
23535
+ }
23536
+ const usable = new Set(prepared.filter((p) => p.row.embedding).map((p) => p.row.memory_id));
23537
+ if (options.embeddingOptions)
23538
+ await embedMissing(prepared, options.embeddingOptions, model, signal);
23539
+ const local = new Map(candidates.map((raw) => [raw.id, raw]));
23540
+ const upload = [];
23541
+ for (const { row, plaintext } of prepared) {
23542
+ const id2 = row.memory_id;
23543
+ if (!fitsNearby(row)) {
23544
+ skipped.set(id2, "invalid");
23545
+ continue;
23546
+ }
23547
+ upload.push(row);
23548
+ const track = (set, on) => on ? set.add(id2) : set.delete(id2);
23549
+ track(reembedded, !!row.embedding && !usable.has(id2));
23550
+ track(unembedded, !row.embedding);
23551
+ track(placeholders, plaintext === void 0);
23552
+ track(truncated, row.source_chunk_ids !== text13(local.get(id2).source_chunk_ids));
23456
23553
  }
23457
- upload.push(row);
23458
- const track = (set, on) => on ? set.add(id2) : set.delete(id2);
23459
- track(reembedded, !!row.embedding && !usable.has(id2));
23460
- track(unembedded, !row.embedding);
23461
- track(placeholders, plaintext === void 0);
23462
- track(truncated, row.source_chunk_ids !== text13(local.get(id2).source_chunk_ids));
23463
- }
23464
- let sent = 0;
23465
- options.onProgress?.({ imported: sent, toImport: upload.length });
23466
- for (const batch of chunks(upload, IMPORT_BATCH)) {
23467
- await importBatch(request, canonical.keyId, batch, signal);
23468
- claimed = true;
23469
- sent += batch.length;
23554
+ let sent = 0;
23470
23555
  options.onProgress?.({ imported: sent, toImport: upload.length });
23556
+ for (const batch of chunks(upload, IMPORT_BATCH)) {
23557
+ await importBatch(request, canonical.keyId, batch, signal);
23558
+ claimed = true;
23559
+ sent += batch.length;
23560
+ options.onProgress?.({ imported: sent, toImport: upload.length });
23561
+ }
23562
+ imported += sent;
23471
23563
  }
23472
- imported += sent;
23473
- }
23564
+ };
23565
+ return uploadPasses();
23474
23566
  }
23475
23567
 
23476
23568
  // src/lib/memory/store/remoteStore.ts
@@ -24923,13 +25015,20 @@ async function synthesizeProfile(ctx, options = {}) {
24923
25015
  }
24924
25016
  }
24925
25017
  }
25018
+ const vaultPreparation = createVaultPreparation();
24926
25019
  const settled = await Promise.allSettled(
24927
25020
  facets.map(async (facet) => {
24928
25021
  const prior = previous?.sections.find((s) => s.key === facet.key);
24929
25022
  if (prior && !staleKeys.has(facet.key)) {
24930
25023
  return prior;
24931
25024
  }
24932
- return synthesizeFacet(facet, recallCtx, options, fallbackPriors.get(facet.key));
25025
+ return synthesizeFacet(
25026
+ facet,
25027
+ recallCtx,
25028
+ options,
25029
+ fallbackPriors.get(facet.key),
25030
+ vaultPreparation
25031
+ );
24933
25032
  })
24934
25033
  );
24935
25034
  const sections = settled.map((r, i) => {
@@ -25083,7 +25182,7 @@ async function attributeFacts(ctx, candidates, facets) {
25083
25182
  }
25084
25183
  return keys;
25085
25184
  }
25086
- async function synthesizeFacet(facet, ctx, options, prior) {
25185
+ async function synthesizeFacet(facet, ctx, options, prior, vaultPreparation) {
25087
25186
  const scopes = options.scopes ?? DEFAULT_SCOPES;
25088
25187
  const limit = options.limit ?? DEFAULT_FACET_RECALL_LIMIT;
25089
25188
  const factTypeWeights = { ...DEFAULT_PROFILE_FACT_TYPE_WEIGHTS, ...options.factTypeWeights };
@@ -25100,6 +25199,7 @@ async function synthesizeFacet(facet, ctx, options, prior) {
25100
25199
  };
25101
25200
  }
25102
25201
  const recalled = await recall(facet.query, ctx, {
25202
+ vaultPreparation,
25103
25203
  scopes,
25104
25204
  limit: hasReviewGate ? RECALL_MAX_LIMIT : limit,
25105
25205
  types: ["fact"],