@anuma/sdk 7.0.7 → 7.1.1

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.
@@ -10495,6 +10495,11 @@ function parseTopics(value) {
10495
10495
  }
10496
10496
 
10497
10497
  // src/lib/db/entities/operations.ts
10498
+ async function assertCanWrite(ctx) {
10499
+ if (ctx.canWrite && !await ctx.canWrite()) {
10500
+ throw new Error("Memory source is no longer eligible");
10501
+ }
10502
+ }
10498
10503
  function entityToStored(e) {
10499
10504
  return {
10500
10505
  uniqueId: e.id,
@@ -10635,6 +10640,7 @@ async function linkMemoryEntitiesOp(ctx, memoryId, entityInputs, options) {
10635
10640
  let skipped = false;
10636
10641
  let entities = [];
10637
10642
  await ctx.database.write(async () => {
10643
+ await assertCanWrite(ctx);
10638
10644
  const {
10639
10645
  entities: byName,
10640
10646
  operations: entityOps,
@@ -10710,6 +10716,7 @@ async function replaceMemoryEntities(ctx, memoryId, entityInputs, options) {
10710
10716
  let skipped = false;
10711
10717
  let entities = [];
10712
10718
  await ctx.database.write(async () => {
10719
+ await assertCanWrite(ctx);
10713
10720
  const vaultRow = await findVaultRowInWrite(ctx, memoryId);
10714
10721
  if (options.guarded && autoLinkBlocked(vaultRow)) {
10715
10722
  skipped = true;
@@ -10770,6 +10777,7 @@ async function unlinkMemoryEntitiesOp(ctx, memoryIds) {
10770
10777
  const links = await ctx.memoryEntityCollection.query(Q5.where("memory_id", Q5.oneOf(Array.from(memoryIds)))).fetch();
10771
10778
  if (links.length === 0) return;
10772
10779
  await ctx.database.write(async () => {
10780
+ await assertCanWrite(ctx);
10773
10781
  await ctx.database.batch(...links.map((l) => l.prepareDestroyPermanently()));
10774
10782
  });
10775
10783
  }
@@ -10778,6 +10786,7 @@ async function unlinkAllMemoryEntitiesForUserOp(ctx, userId) {
10778
10786
  const links = await ctx.memoryEntityCollection.query(Q5.where("user_id", userId)).fetch();
10779
10787
  if (links.length === 0) return;
10780
10788
  await ctx.database.write(async () => {
10789
+ await assertCanWrite(ctx);
10781
10790
  await ctx.database.batch(...links.map((l) => l.prepareDestroyPermanently()));
10782
10791
  });
10783
10792
  }
@@ -10797,6 +10806,7 @@ async function backfillMemoryEntityUserIdsOp(ctx, vaultMemoryCollection) {
10797
10806
  }
10798
10807
  if (toUpdate.length === 0) return 0;
10799
10808
  await ctx.database.write(async () => {
10809
+ await assertCanWrite(ctx);
10800
10810
  await ctx.database.batch(
10801
10811
  ...toUpdate.map(({ link, userId }) => link.prepareUpdate((r) => r._setRaw("user_id", userId)))
10802
10812
  );
@@ -13201,6 +13211,11 @@ function visibilityConditions(requested) {
13201
13211
  function isKinded(record2) {
13202
13212
  return record2.kind !== null && record2.kind !== void 0;
13203
13213
  }
13214
+ async function assertCanWrite2(ctx) {
13215
+ if (ctx.canWrite && !await ctx.canWrite()) {
13216
+ throw new Error("Memory source is no longer eligible");
13217
+ }
13218
+ }
13204
13219
  function assertValidKindLevel(kind, level) {
13205
13220
  const hasKind = kind !== null && kind !== void 0;
13206
13221
  if (hasKind && !MEMORY_KINDS.includes(kind)) {
@@ -13381,8 +13396,7 @@ async function createVaultMemoryOp(ctx, opts) {
13381
13396
  ctx.embeddedWalletSigner
13382
13397
  ) : opts.content;
13383
13398
  const created = await ctx.database.write(async () => {
13384
- if (ctx.canWrite && !await ctx.canWrite())
13385
- throw new Error("Memory source is no longer eligible");
13399
+ await assertCanWrite2(ctx);
13386
13400
  return ctx.vaultMemoryCollection.create(
13387
13401
  (record2) => populateNewVaultMemory(record2, ctx, opts, encryptedContent, encryptedKindValue)
13388
13402
  );
@@ -13401,8 +13415,7 @@ async function createSupersedingMemoryOp(ctx, opts, targetId) {
13401
13415
  ) : opts.content;
13402
13416
  let createdRecord = null;
13403
13417
  await ctx.database.write(async () => {
13404
- if (ctx.canWrite && !await ctx.canWrite())
13405
- throw new Error("Memory source is no longer eligible");
13418
+ await assertCanWrite2(ctx);
13406
13419
  let target;
13407
13420
  try {
13408
13421
  target = await ctx.vaultMemoryCollection.find(targetId);
@@ -13494,8 +13507,7 @@ async function createVaultMemoriesBatchOp(ctx, optionsArray) {
13494
13507
  })
13495
13508
  );
13496
13509
  const created = await ctx.database.write(async () => {
13497
- if (ctx.canWrite && !await ctx.canWrite())
13498
- throw new Error("Memory source is no longer eligible");
13510
+ await assertCanWrite2(ctx);
13499
13511
  const prepared = optionsArray.map(
13500
13512
  (opts, i) => ctx.vaultMemoryCollection.prepareCreate(
13501
13513
  (record2) => populateNewVaultMemory(record2, ctx, opts, encryptedContents[i], encryptedKindValues[i])
@@ -13800,8 +13812,7 @@ async function updateVaultMemoryOp(ctx, id, opts) {
13800
13812
  const record2 = probe;
13801
13813
  const originalUpdatedAt = record2.updatedAt.getTime();
13802
13814
  await ctx.database.write(async () => {
13803
- if (ctx.canWrite && !await ctx.canWrite())
13804
- throw new Error("Memory source is no longer eligible");
13815
+ await assertCanWrite2(ctx);
13805
13816
  if (record2.isDeleted || record2.supersededBy || !isOwnedByCtxUser(ctx, record2) || opts.freeFormOnly && isKinded(record2)) {
13806
13817
  stale = true;
13807
13818
  return;
@@ -13912,6 +13923,7 @@ async function setMemoryEntitiesOp(ctx, memoryId, entities) {
13912
13923
  let stale = false;
13913
13924
  const originalUpdatedAt = record2.updatedAt.getTime();
13914
13925
  await ctx.database.write(async (writer) => {
13926
+ await assertCanWrite2(ctx);
13915
13927
  if (record2.isDeleted || !isOwnedByCtxUser(ctx, record2)) {
13916
13928
  stale = true;
13917
13929
  return;
@@ -13953,6 +13965,7 @@ async function clearMemoryTopicsOverrideOp(ctx, memoryId, options) {
13953
13965
  const originalUpdatedAt = record2.updatedAt.getTime();
13954
13966
  let cleared = false;
13955
13967
  await ctx.database.write(async () => {
13968
+ await assertCanWrite2(ctx);
13956
13969
  if (options?.unlessTopicsRecorded && parseTopics(record2.topics) !== null) return;
13957
13970
  cleared = true;
13958
13971
  await record2.update((r) => {
@@ -13976,6 +13989,7 @@ async function setMemoryVisibilityOp(ctx, id, opts) {
13976
13989
  if (record2.isDeleted || !isOwnedByCtxUser(ctx, record2)) return null;
13977
13990
  let stale = false;
13978
13991
  await ctx.database.write(async () => {
13992
+ await assertCanWrite2(ctx);
13979
13993
  if (record2.isDeleted || !isOwnedByCtxUser(ctx, record2)) {
13980
13994
  stale = true;
13981
13995
  return;
@@ -14004,6 +14018,7 @@ async function deleteVaultMemoryOp(ctx, id) {
14004
14018
  if (record2.isDeleted || !isOwnedByCtxUser(ctx, record2)) return false;
14005
14019
  let stale = false;
14006
14020
  await ctx.database.write(async () => {
14021
+ await assertCanWrite2(ctx);
14007
14022
  if (record2.isDeleted || !isOwnedByCtxUser(ctx, record2)) {
14008
14023
  stale = true;
14009
14024
  return;
@@ -14041,8 +14056,7 @@ async function supersedeVaultMemoryOp(ctx, id, supersededById) {
14041
14056
  }
14042
14057
  let stale = false;
14043
14058
  await ctx.database.write(async () => {
14044
- if (ctx.canWrite && !await ctx.canWrite())
14045
- throw new Error("Memory source is no longer eligible");
14059
+ await assertCanWrite2(ctx);
14046
14060
  if (record2.isDeleted || record2.supersededBy || !isOwnedByCtxUser(ctx, record2)) {
14047
14061
  stale = true;
14048
14062
  return;
@@ -14204,6 +14218,7 @@ async function stampTopicsExtractedAtOp(ctx, memoryIds, extractedAt, version = T
14204
14218
  for (let i = 0; i < uniqueIds.length; i += CHUNK) {
14205
14219
  const chunkIds = uniqueIds.slice(i, i + CHUNK);
14206
14220
  await ctx.database.write(async () => {
14221
+ await assertCanWrite2(ctx);
14207
14222
  const records = [];
14208
14223
  for (const id of chunkIds) {
14209
14224
  try {
@@ -14237,6 +14252,7 @@ async function relinkMemoryTopicsOp(ctx, memoryIds) {
14237
14252
  for (const id of Array.from(new Set(memoryIds))) {
14238
14253
  try {
14239
14254
  await ctx.database.write(async (writer) => {
14255
+ await assertCanWrite2(ctx);
14240
14256
  let record2;
14241
14257
  try {
14242
14258
  record2 = await ctx.vaultMemoryCollection.find(id);
@@ -14267,6 +14283,7 @@ async function backfillMemoryTopicsOp(ctx, memoryIds) {
14267
14283
  for (let i = 0; i < uniqueIds.length; i += CHUNK) {
14268
14284
  const chunkIds = uniqueIds.slice(i, i + CHUNK);
14269
14285
  await ctx.database.write(async () => {
14286
+ await assertCanWrite2(ctx);
14270
14287
  const writes = [];
14271
14288
  for (const id of chunkIds) {
14272
14289
  let record2;
@@ -14299,6 +14316,7 @@ async function deleteAllVaultMemoriesForUserOp(ctx, userId) {
14299
14316
  const records = await ctx.vaultMemoryCollection.query(Q8.where("user_id", userId), Q8.where("is_deleted", false)).fetch();
14300
14317
  if (records.length === 0) return 0;
14301
14318
  await ctx.database.write(async () => {
14319
+ await assertCanWrite2(ctx);
14302
14320
  const prepared = records.map(
14303
14321
  (record2) => record2.prepareUpdate((r) => {
14304
14322
  r._setRaw("is_deleted", true);
@@ -14353,6 +14371,7 @@ async function archiveVaultMemoryOp(ctx, id, opts) {
14353
14371
  let stale = false;
14354
14372
  const archivedAtValue = opts?.now ?? Date.now();
14355
14373
  await ctx.database.write(async () => {
14374
+ await assertCanWrite2(ctx);
14356
14375
  if (record2.isDeleted || !isOwnedByCtxUser(ctx, record2) || record2.archivedAt !== null) {
14357
14376
  stale = true;
14358
14377
  return;
@@ -14376,6 +14395,7 @@ async function restoreVaultMemoryOp(ctx, id) {
14376
14395
  if (record2.isDeleted || !isOwnedByCtxUser(ctx, record2)) return false;
14377
14396
  let stale = false;
14378
14397
  await ctx.database.write(async () => {
14398
+ await assertCanWrite2(ctx);
14379
14399
  if (record2.isDeleted || !isOwnedByCtxUser(ctx, record2)) {
14380
14400
  stale = true;
14381
14401
  return;
@@ -14396,6 +14416,7 @@ async function hardDeleteDecayedOp(ctx, id, opts) {
14396
14416
  const now = opts.now ?? Date.now();
14397
14417
  let stale = false;
14398
14418
  await ctx.database.write(async () => {
14419
+ await assertCanWrite2(ctx);
14399
14420
  const archivedAt = record2.archivedAt;
14400
14421
  if (record2.isDeleted || !isOwnedByCtxUser(ctx, record2) || archivedAt === null || now - archivedAt <= opts.hardDeleteWindowMs) {
14401
14422
  stale = true;
@@ -14435,6 +14456,7 @@ async function updateVaultMemoryEmbeddingOp(ctx, id, embedding2, embeddingModel,
14435
14456
  }
14436
14457
  let written = false;
14437
14458
  await ctx.database.write(async () => {
14459
+ await assertCanWrite2(ctx);
14438
14460
  if (record2.isDeleted || !isOwnedByCtxUser(ctx, record2)) return;
14439
14461
  const originalUpdatedAt = record2.updatedAt.getTime();
14440
14462
  if (originalUpdatedAt !== readUpdatedAt || record2._getRaw("content") !== readStoredContent)
@@ -18404,6 +18426,13 @@ function restoreCandidates(candidates, redactor, ownNames) {
18404
18426
  ).map((c) => c.candidate);
18405
18427
  }
18406
18428
  async function extractAndRetain(messages, retainCtx, options) {
18429
+ const { memoryStore, signal } = options;
18430
+ if (memoryStore === null) throw new Error("Memory store is not ready");
18431
+ if (memoryStore && options.folderId !== void 0 && options.folderId !== null)
18432
+ throw new Error("Remote memory extraction does not support folders");
18433
+ if (memoryStore && options.extract.piiRedaction && !memoryStore.masksEmbeddingInput)
18434
+ throw new Error("PII-redacted extraction requires a memory store that masks embedding inputs");
18435
+ signal?.throwIfAborted();
18407
18436
  const minConfidence = options.minConfidence ?? DEFAULT_MIN_CONFIDENCE;
18408
18437
  const resolvedConsolidatePii = options.consolidateOptions?.piiRedaction ?? options.extract.piiRedaction;
18409
18438
  const consolidateOptions = options.consolidateOptions !== void 0 ? {
@@ -18437,6 +18466,7 @@ async function extractAndRetain(messages, retainCtx, options) {
18437
18466
  }
18438
18467
  });
18439
18468
  const extractMs = Date.now() - tExtract;
18469
+ signal?.throwIfAborted();
18440
18470
  const filtered = candidates.filter((c) => c.confidence >= minConfidence);
18441
18471
  const log = getLogger();
18442
18472
  const screened = screenCandidatesForInjection(filtered);
@@ -18484,16 +18514,33 @@ async function extractAndRetain(messages, retainCtx, options) {
18484
18514
  const results = [];
18485
18515
  const quarantinedInfo = [];
18486
18516
  let failedWrites = 0;
18517
+ let auditRows;
18487
18518
  const tRetain = Date.now();
18488
18519
  for (const { candidate, isQuarantined, reason, signature } of toRetain) {
18489
18520
  try {
18521
+ signal?.throwIfAborted();
18490
18522
  if (isQuarantined) {
18491
- const existingId = await findQuarantinedDuplicateOp(
18492
- retainCtx.vaultCtx,
18493
- candidate.content,
18494
- candidate.sourceMessageIds,
18495
- { scope: options.scope ?? "private", folderId: options.folderId ?? null }
18496
- ).catch(() => null);
18523
+ let existingId;
18524
+ if (memoryStore) {
18525
+ auditRows ?? (auditRows = memoryStore.list({
18526
+ scopes: [options.scope ?? "private"],
18527
+ includeArchived: true,
18528
+ includeQuarantined: true,
18529
+ includeSuperseded: true
18530
+ }));
18531
+ const sourceKey = [...new Set(candidate.sourceMessageIds)].sort().join("\0");
18532
+ const normalize = (value) => value.normalize("NFKC").trim().replace(/\s+/g, " ").toLowerCase();
18533
+ existingId = (await auditRows).find(
18534
+ (row) => row.trustTier === "quarantined" && row.scope === (options.scope ?? "private") && [...new Set(row.sourceChunkIds ?? [])].sort().join("\0") === sourceKey && normalize(row.content) === normalize(candidate.content)
18535
+ )?.uniqueId ?? null;
18536
+ } else {
18537
+ existingId = await findQuarantinedDuplicateOp(
18538
+ retainCtx.vaultCtx,
18539
+ candidate.content,
18540
+ candidate.sourceMessageIds,
18541
+ { scope: options.scope ?? "private", folderId: options.folderId ?? null }
18542
+ ).catch(() => null);
18543
+ }
18497
18544
  if (existingId) {
18498
18545
  quarantinedInfo.push({
18499
18546
  candidate,
@@ -18504,18 +18551,26 @@ async function extractAndRetain(messages, retainCtx, options) {
18504
18551
  continue;
18505
18552
  }
18506
18553
  }
18507
- const result = await retain(candidate.content, retainCtx, {
18554
+ const retainOptions = {
18508
18555
  source: "auto-extracted",
18509
18556
  sourceChunkIds: candidate.sourceMessageIds,
18510
18557
  respectTombstones: true,
18511
18558
  ...options.scope !== void 0 && { scope: options.scope },
18512
- ...options.folderId !== void 0 && { folderId: options.folderId },
18513
18559
  ...consolidateOptions !== void 0 && { consolidateOptions },
18514
18560
  ...candidate.eventTime !== null && { eventTime: candidate.eventTime },
18515
18561
  factType: candidate.type,
18516
18562
  ...isQuarantined && { trustTier: "quarantined", enableAutoMerge: false }
18563
+ };
18564
+ const result = memoryStore ? await memoryStore.retain(candidate.content, {
18565
+ ...retainOptions,
18566
+ canWrite: retainCtx.vaultCtx.canWrite,
18567
+ signal
18568
+ }) : await retain(candidate.content, retainCtx, {
18569
+ ...retainOptions,
18570
+ ...options.folderId !== void 0 && { folderId: options.folderId }
18517
18571
  });
18518
18572
  if (isQuarantined) {
18573
+ auditRows = void 0;
18519
18574
  const info = {
18520
18575
  candidate,
18521
18576
  memoryId: result.memoryId,
@@ -18534,9 +18589,16 @@ async function extractAndRetain(messages, retainCtx, options) {
18534
18589
  }
18535
18590
  succeededCandidates.push(candidate);
18536
18591
  results.push(result);
18537
- if (result.action !== "suppressed" && options.entityCtx && candidate.entities.length > 0) {
18592
+ if (result.action !== "suppressed" && candidate.entities.length > 0) {
18538
18593
  try {
18539
- if (!await isMemoryTopicsUserManaged(options.entityCtx, result.memoryId)) {
18594
+ signal?.throwIfAborted();
18595
+ if (memoryStore) {
18596
+ await memoryStore.addTopics(result.memoryId, candidate.entities, {
18597
+ unlessUserManaged: true,
18598
+ canWrite: retainCtx.vaultCtx.canWrite,
18599
+ signal
18600
+ });
18601
+ } else if (options.entityCtx && !await isMemoryTopicsUserManaged(options.entityCtx, result.memoryId)) {
18540
18602
  await linkMemoryEntitiesOp(options.entityCtx, result.memoryId, candidate.entities, {
18541
18603
  unlessTopicsUserManaged: true
18542
18604
  });
@@ -18784,7 +18846,7 @@ function createAutoExtractor(options) {
18784
18846
  };
18785
18847
  const consolidateOptions = options.consolidate ? { ...options.consolidate, ...subPassAuth(options.consolidate.baseUrl) } : void 0;
18786
18848
  const injectionClassifierOptions = options.injectionClassifier ? { ...options.injectionClassifier, ...subPassAuth(options.injectionClassifier.baseUrl) } : void 0;
18787
- if (options.retainCtx.vaultCtx.entityCtx && !options.entityCtx) {
18849
+ if (!options.memoryStore && options.retainCtx.vaultCtx.entityCtx && !options.entityCtx) {
18788
18850
  getLogger().warn(
18789
18851
  "[memory/extract] retainCtx.vaultCtx.entityCtx is set but extractor was created without `entityCtx` \u2014 W5 graph lane will receive no writes"
18790
18852
  );
@@ -18829,6 +18891,8 @@ function createAutoExtractor(options) {
18829
18891
  try {
18830
18892
  const { candidates, results, failedCount, outcome: outcome2, failure, funnel, timings, model } = await extractAndRetain(window2, retainCtx, {
18831
18893
  extract,
18894
+ memoryStore: options.memoryStore,
18895
+ signal: options.signal,
18832
18896
  ...options.minConfidence !== void 0 && { minConfidence: options.minConfidence },
18833
18897
  ...options.entityCtx !== void 0 && { entityCtx: options.entityCtx },
18834
18898
  ...options.scope !== void 0 && { scope: options.scope },
@@ -20941,6 +21005,7 @@ function createLocalMemoryStore(options) {
20941
21005
  database,
20942
21006
  entityCollection: database.get("entity"),
20943
21007
  memoryEntityCollection: database.get("memory_entity"),
21008
+ canWrite: options.canWrite,
20944
21009
  ...userId !== void 0 && { userId },
20945
21010
  ...options.allowUnscopedRows !== void 0 && {
20946
21011
  allowUnscopedRows: options.allowUnscopedRows
@@ -20952,10 +21017,21 @@ function createLocalMemoryStore(options) {
20952
21017
  walletAddress: options.walletAddress,
20953
21018
  signMessage: options.signMessage,
20954
21019
  embeddedWalletSigner: options.embeddedWalletSigner,
21020
+ canWrite: options.canWrite,
20955
21021
  ...userId !== void 0 && { userId },
20956
21022
  ...options.singleTenant !== void 0 && { singleTenant: options.singleTenant },
20957
21023
  entityCtx
20958
21024
  };
21025
+ const assertWrite = async (writeOptions) => {
21026
+ writeOptions?.signal?.throwIfAborted();
21027
+ if (writeOptions?.canWrite && !await writeOptions.canWrite()) {
21028
+ throw new Error("Memory source is no longer eligible");
21029
+ }
21030
+ if (options.canWrite && !await options.canWrite()) {
21031
+ throw new Error("Memory source is no longer eligible");
21032
+ }
21033
+ writeOptions?.signal?.throwIfAborted();
21034
+ };
20959
21035
  const vaultCache = options.vaultCache ?? createVaultEmbeddingCache();
20960
21036
  const ownedBy = userId !== void 0 ? [Q15.where("user_id", userId)] : [];
20961
21037
  const linksOwnedBy = userId === void 0 ? [] : options.allowUnscopedRows ? [Q15.or(Q15.where("user_id", userId), Q15.where("user_id", null))] : [Q15.where("user_id", userId)];
@@ -21067,9 +21143,25 @@ function createLocalMemoryStore(options) {
21067
21143
  archive: (id) => archiveVaultMemoryOp(vaultCtx, id),
21068
21144
  restore: (id) => restoreVaultMemoryOp(vaultCtx, id),
21069
21145
  setTopics: (memoryId, topics) => setMemoryEntitiesOp(vaultCtx, memoryId, topics),
21070
- addTopics: (memoryId, topics) => database.write(async (writer) => {
21146
+ addTopics: (memoryId, topics, writeOptions) => database.write(async (writer) => {
21147
+ await assertWrite(writeOptions);
21071
21148
  const owned = await vaultCtx.vaultMemoryCollection.query(Q15.where("id", memoryId), Q15.where("is_deleted", false), ...ownedBy).fetchCount();
21072
- return owned > 0 ? writer.callWriter(() => linkMemoryEntitiesOp(entityCtx, memoryId, topics)) : [];
21149
+ return owned > 0 ? writer.callWriter(
21150
+ () => linkMemoryEntitiesOp(
21151
+ {
21152
+ ...entityCtx,
21153
+ canWrite: async () => {
21154
+ await assertWrite(writeOptions);
21155
+ return true;
21156
+ }
21157
+ },
21158
+ memoryId,
21159
+ topics,
21160
+ {
21161
+ unlessTopicsUserManaged: writeOptions?.unlessUserManaged
21162
+ }
21163
+ )
21164
+ ) : [];
21073
21165
  }),
21074
21166
  setVisibility: (id, visibility, visibilityOptions) => setMemoryVisibilityOp(vaultCtx, id, { visibility, ...visibilityOptions }),
21075
21167
  recall: (query, recallOptions) => recall(
@@ -21079,7 +21171,17 @@ function createLocalMemoryStore(options) {
21079
21171
  ),
21080
21172
  retain: (content, retainOptions) => retain(
21081
21173
  content,
21082
- { vaultCtx, embeddingOptions: options.embeddingOptions, vaultCache },
21174
+ {
21175
+ vaultCtx: {
21176
+ ...vaultCtx,
21177
+ canWrite: async () => {
21178
+ await assertWrite(retainOptions);
21179
+ return true;
21180
+ }
21181
+ },
21182
+ embeddingOptions: options.embeddingOptions,
21183
+ vaultCache
21184
+ },
21083
21185
  retainOptions
21084
21186
  ),
21085
21187
  factSource: {
@@ -21729,7 +21831,14 @@ function stored(row) {
21729
21831
  function createRemoteMemoryPipeline(options) {
21730
21832
  const { persistence, embeddingOptions } = options;
21731
21833
  const model = embeddingOptions.model ?? DEFAULT_API_EMBEDDING_MODEL;
21732
- const operation = (mode, queryOptions = embeddingOptions) => {
21834
+ const operation = (mode, queryOptions = embeddingOptions, writeOptions) => {
21835
+ const signal = writeOptions?.signal;
21836
+ const assertWrite = async () => {
21837
+ signal?.throwIfAborted();
21838
+ if (writeOptions?.canWrite && !await writeOptions.canWrite())
21839
+ throw new Error("Memory source is no longer eligible");
21840
+ signal?.throwIfAborted();
21841
+ };
21733
21842
  const rejectUndecryptable = (failed) => {
21734
21843
  if (!failed.length) return;
21735
21844
  if (mode === "retain")
@@ -21747,7 +21856,7 @@ function createRemoteMemoryPipeline(options) {
21747
21856
  return items.map((item) => stored(item.memory));
21748
21857
  };
21749
21858
  const get = async (id) => {
21750
- const snapshot = snapshots.get(id) ?? await persistence.get(id);
21859
+ const snapshot = snapshots.get(id) ?? await (signal ? persistence.get(id, signal) : persistence.get(id));
21751
21860
  if (!snapshot || snapshot.memory.is_deleted || snapshot.memory.superseded_by) return null;
21752
21861
  snapshots.set(id, snapshot);
21753
21862
  return stored(snapshot.memory);
@@ -21797,7 +21906,7 @@ function createRemoteMemoryPipeline(options) {
21797
21906
  if (forced.size < 100 && temporal[i] !== void 0) forced.add(temporal[i]);
21798
21907
  }
21799
21908
  const forceIds = [...forced];
21800
- const windowOptions = (i, signal) => ({
21909
+ const windowOptions = (i, signal2) => ({
21801
21910
  limit: Math.min(100, Math.max((search.limit ?? 8) * 3, 30)),
21802
21911
  force_ids: i === 0 ? forceIds : [],
21803
21912
  ...search.scopes && { scopes: search.scopes },
@@ -21805,13 +21914,15 @@ function createRemoteMemoryPipeline(options) {
21805
21914
  ...search.memoryIds !== void 0 && { memory_ids: search.memoryIds },
21806
21915
  include_archived: search.includeArchived,
21807
21916
  embedding_model: model,
21808
- ...signal && { signal }
21917
+ ...signal2 && { signal: signal2 }
21809
21918
  });
21810
21919
  const vectors = [queryEmbedding, ...facetEmbeddings];
21811
21920
  let windows;
21812
21921
  if (mode === "retain") {
21813
21922
  windows = await Promise.all(
21814
- vectors.map((embedding2, i) => persistence.candidateSet(embedding2, windowOptions(i)))
21923
+ vectors.map(
21924
+ (embedding2, i) => persistence.candidateSet(embedding2, windowOptions(i, signal))
21925
+ )
21815
21926
  );
21816
21927
  } else {
21817
21928
  const deadline = new AbortController();
@@ -21899,12 +22010,15 @@ function createRemoteMemoryPipeline(options) {
21899
22010
  twin_opt_in: false
21900
22011
  };
21901
22012
  };
21902
- const write = async (memory, snapshot) => remember([await persistence.put(memory, snapshot)])[0];
22013
+ const write = async (memory, snapshot) => {
22014
+ await assertWrite();
22015
+ return remember([await persistence.put(memory, snapshot, signal)])[0];
22016
+ };
21903
22017
  const port = {
21904
22018
  prepare,
21905
22019
  get,
21906
22020
  getFresh: async (id) => {
21907
- const current = await persistence.get(id);
22021
+ const current = await (signal ? persistence.get(id, signal) : persistence.get(id));
21908
22022
  return current && !current.memory.is_deleted && !current.memory.superseded_by ? stored(current.memory) : null;
21909
22023
  },
21910
22024
  tombstones: async (embedding2, embeddingModel, scope, folderId) => {
@@ -21915,7 +22029,8 @@ function createRemoteMemoryPipeline(options) {
21915
22029
  embedding_model: embeddingModel,
21916
22030
  strict_model: true,
21917
22031
  include_deleted: true,
21918
- deleted_only: true
22032
+ deleted_only: true,
22033
+ signal
21919
22034
  });
21920
22035
  rejectUndecryptable(window2.failed);
21921
22036
  return remember(window2.items);
@@ -21971,14 +22086,18 @@ function createRemoteMemoryPipeline(options) {
21971
22086
  if (!target || !newer || id === successor) return false;
21972
22087
  const old = snapshots.get(id);
21973
22088
  const next = snapshots.get(successor);
22089
+ await assertWrite();
21974
22090
  remember(
21975
- await persistence.putMany([
21976
- {
21977
- memory: { ...old.memory, superseded_by: successor, superseded_at: Date.now() },
21978
- expectedVersion: old
21979
- },
21980
- { memory: next.memory, expectedVersion: next }
21981
- ])
22091
+ await persistence.putMany(
22092
+ [
22093
+ {
22094
+ memory: { ...old.memory, superseded_by: successor, superseded_at: Date.now() },
22095
+ expectedVersion: old
22096
+ },
22097
+ { memory: next.memory, expectedVersion: next }
22098
+ ],
22099
+ signal
22100
+ )
21982
22101
  );
21983
22102
  return true;
21984
22103
  },
@@ -21987,18 +22106,22 @@ function createRemoteMemoryPipeline(options) {
21987
22106
  if (!target) return { created: null, retired: false };
21988
22107
  const snapshot = snapshots.get(targetId);
21989
22108
  const successor = createRow(input);
22109
+ await assertWrite();
21990
22110
  const rows = remember(
21991
- await persistence.putMany([
21992
- { memory: successor, expectedVersion: 0 },
21993
- {
21994
- memory: {
21995
- ...snapshot.memory,
21996
- superseded_by: successor.memory_id,
21997
- superseded_at: Date.now()
21998
- },
21999
- expectedVersion: snapshot
22000
- }
22001
- ])
22111
+ await persistence.putMany(
22112
+ [
22113
+ { memory: successor, expectedVersion: 0 },
22114
+ {
22115
+ memory: {
22116
+ ...snapshot.memory,
22117
+ superseded_by: successor.memory_id,
22118
+ superseded_at: Date.now()
22119
+ },
22120
+ expectedVersion: snapshot
22121
+ }
22122
+ ],
22123
+ signal
22124
+ )
22002
22125
  );
22003
22126
  return { created: rows[0], retired: true };
22004
22127
  }
@@ -22033,7 +22156,7 @@ function createRemoteMemoryPipeline(options) {
22033
22156
  factSource,
22034
22157
  recall: async (query, recallOptions = {}) => recall(query, { embeddingOptions, factSource }, { ...recallOptions, types: ["fact"] }),
22035
22158
  retain: async (content, retainOptions) => {
22036
- const { port } = operation("retain");
22159
+ const { port } = operation("retain", embeddingOptions, retainOptions);
22037
22160
  return retainWithPersistence(
22038
22161
  content,
22039
22162
  {
@@ -22694,16 +22817,21 @@ function createRemoteMemoryStore(options) {
22694
22817
  const record2 = await persistence.get(id);
22695
22818
  return record2 && !record2.memory.is_deleted ? record2 : null;
22696
22819
  };
22697
- const mutateLocked = async (id, change, edit) => {
22698
- const record2 = await live(id);
22820
+ const mutateLocked = async (id, change, edit, writeOptions) => {
22821
+ writeOptions?.signal?.throwIfAborted();
22822
+ const record2 = writeOptions?.signal ? await persistence.get(id, writeOptions.signal) : await live(id);
22699
22823
  if (!record2) return null;
22824
+ if (record2.memory.is_deleted) return null;
22825
+ if (writeOptions?.canWrite && !await writeOptions.canWrite())
22826
+ throw new Error("Memory source is no longer eligible");
22827
+ writeOptions?.signal?.throwIfAborted();
22700
22828
  const next = edit(structuredClone(record2.memory));
22701
22829
  if (!next) return null;
22702
- const saved = await persistence.put(next, record2);
22830
+ const saved = writeOptions?.signal ? await persistence.put(next, record2, writeOptions.signal) : await persistence.put(next, record2);
22703
22831
  notify(typeof change === "function" ? change(record2.memory) : change, [saved]);
22704
22832
  return saved;
22705
22833
  };
22706
- const mutate = (id, change, edit) => serialized(id, () => mutateLocked(id, change, edit));
22834
+ const mutate = (id, change, edit, writeOptions) => serialized(id, () => mutateLocked(id, change, edit, writeOptions));
22707
22835
  const serializedAll = (ids, task) => [...new Set(ids)].sort().reduceRight((inner, id) => () => serialized(id, inner), task)();
22708
22836
  const mutateMany = async (ids, change, edit) => {
22709
22837
  const saved = /* @__PURE__ */ new Map();
@@ -23216,13 +23344,21 @@ function createRemoteMemoryStore(options) {
23216
23344
  }))
23217
23345
  );
23218
23346
  },
23219
- addTopics: async (memoryId, topics) => {
23347
+ addTopics: async (memoryId, topics, writeOptions) => {
23220
23348
  const added = await withKnownKinds(topicInputs(topics, "auto"));
23221
- const saved = await mutate(memoryId, "edit", (memory) => ({
23222
- ...memory,
23223
- topics: serializeTopics(mergeTopics(parseTopics(memory.topics) ?? [], added)),
23224
- topics_updated_at: Date.now()
23225
- }));
23349
+ const saved = await mutate(
23350
+ memoryId,
23351
+ "edit",
23352
+ (memory) => {
23353
+ if (writeOptions?.unlessUserManaged && memory.topics_user_managed) return null;
23354
+ return {
23355
+ ...memory,
23356
+ topics: serializeTopics(mergeTopics(parseTopics(memory.topics) ?? [], added)),
23357
+ topics_updated_at: Date.now()
23358
+ };
23359
+ },
23360
+ writeOptions
23361
+ );
23226
23362
  if (!saved) return [];
23227
23363
  const now = /* @__PURE__ */ new Date();
23228
23364
  return mergeTopics([], added).map(
@@ -24073,6 +24209,7 @@ function createDurableAutoExtractor(options) {
24073
24209
  };
24074
24210
  const folderId = options.folderId ?? null;
24075
24211
  let disposed = false;
24212
+ let activeBatch = null;
24076
24213
  let running = false;
24077
24214
  let wakeRequested = false;
24078
24215
  let timer;
@@ -24109,14 +24246,18 @@ function createDurableAutoExtractor(options) {
24109
24246
  }, delay);
24110
24247
  }
24111
24248
  async function extractBatch(messages, conversationId, ids, jobScope, jobFolderId) {
24249
+ if (disposed) throw new Error("Extraction session was disposed");
24112
24250
  let watchdog;
24113
24251
  let cancelled = false;
24252
+ const controller = new AbortController();
24253
+ activeBatch = controller;
24114
24254
  const model = options.modelForScope?.(jobScope) ?? options.extract.model;
24115
24255
  try {
24116
24256
  return await Promise.race([
24117
24257
  new Promise((resolve, reject) => {
24118
24258
  const worker = createAutoExtractor({
24119
24259
  ...options,
24260
+ signal: controller.signal,
24120
24261
  retainCtx: {
24121
24262
  ...options.retainCtx,
24122
24263
  vaultCtx: {
@@ -24145,6 +24286,8 @@ function createDurableAutoExtractor(options) {
24145
24286
  ]);
24146
24287
  } finally {
24147
24288
  cancelled = true;
24289
+ controller.abort();
24290
+ if (activeBatch === controller) activeBatch = null;
24148
24291
  if (watchdog !== void 0) clearTimeout(watchdog);
24149
24292
  }
24150
24293
  }
@@ -24156,6 +24299,7 @@ function createDurableAutoExtractor(options) {
24156
24299
  for (const message of loaded)
24157
24300
  if (message && message.messageId > 0) sequences.set(message.uniqueId, message.messageId);
24158
24301
  await database.write(async () => {
24302
+ if (disposed) return;
24159
24303
  const current = idsOf(job);
24160
24304
  const acknowledged = ids.filter((id) => current.includes(id));
24161
24305
  if (!acknowledged.length) return;
@@ -24328,6 +24472,7 @@ function createDurableAutoExtractor(options) {
24328
24472
  jobScope,
24329
24473
  jobFolderId
24330
24474
  );
24475
+ if (disposed) throw new Error("Extraction session was disposed");
24331
24476
  options.onTurnComplete?.(result);
24332
24477
  return result;
24333
24478
  };
@@ -24364,6 +24509,7 @@ function createDurableAutoExtractor(options) {
24364
24509
  );
24365
24510
  scheduleAfterSuccess = true;
24366
24511
  } catch (error) {
24512
+ if (disposed) break;
24367
24513
  attempts.set(job.id, attempted + 1);
24368
24514
  retry = true;
24369
24515
  reportError(error, conversationId);
@@ -24386,6 +24532,14 @@ function createDurableAutoExtractor(options) {
24386
24532
  schedule(retry ? options.retryDelayMs ?? 3e4 : 0);
24387
24533
  }
24388
24534
  }
24535
+ function dispose() {
24536
+ disposed = true;
24537
+ activeBatch?.abort(options.signal?.reason);
24538
+ if (timer !== void 0) clearTimeout(timer);
24539
+ options.signal?.removeEventListener("abort", dispose);
24540
+ }
24541
+ if (options.signal?.aborted) dispose();
24542
+ else options.signal?.addEventListener("abort", dispose, { once: true });
24389
24543
  schedule(0);
24390
24544
  return {
24391
24545
  processTurn(messages, conversationId) {
@@ -24465,10 +24619,7 @@ function createDurableAutoExtractor(options) {
24465
24619
  return true;
24466
24620
  },
24467
24621
  isProcessing: () => running,
24468
- dispose() {
24469
- disposed = true;
24470
- if (timer !== void 0) clearTimeout(timer);
24471
- }
24622
+ dispose
24472
24623
  };
24473
24624
  }
24474
24625
 
@@ -28961,7 +29112,7 @@ async function requestICloudSignIn() {
28961
29112
  }
28962
29113
  return container.whenUserSignsIn();
28963
29114
  }
28964
- async function uploadFileToICloud(filename, content) {
29115
+ async function uploadFileToICloud(filename, content, backupRevision) {
28965
29116
  const container = await getContainer();
28966
29117
  const database = container.privateCloudDatabase;
28967
29118
  const recordName = `backup_${filename.replace(/[^a-zA-Z0-9]/g, "_")}`;
@@ -28974,7 +29125,8 @@ async function uploadFileToICloud(filename, content) {
28974
29125
  filename: { value: filename },
28975
29126
  data: { value: base64Data },
28976
29127
  size: { value: content.size },
28977
- contentType: { value: content.type || "application/json" }
29128
+ contentType: { value: content.type || "application/json" },
29129
+ ...backupRevision ? { backupRevision: { value: backupRevision } } : {}
28978
29130
  }
28979
29131
  };
28980
29132
  const response = await database.saveRecords(record2);
@@ -28986,7 +29138,8 @@ async function uploadFileToICloud(filename, content) {
28986
29138
  recordName: savedRecord.recordName,
28987
29139
  filename,
28988
29140
  modifiedAt: new Date(savedRecord.modified?.timestamp ?? Date.now()),
28989
- size: content.size
29141
+ size: content.size,
29142
+ backupRevision: savedRecord.fields.backupRevision?.value
28990
29143
  };
28991
29144
  }
28992
29145
  async function listICloudFiles() {
@@ -29007,6 +29160,7 @@ async function listICloudFiles() {
29007
29160
  recordName: record2.recordName,
29008
29161
  filename: record2.fields.filename?.value ?? "",
29009
29162
  modifiedAt: new Date(record2.modified?.timestamp ?? Date.now()),
29163
+ backupRevision: record2.fields.backupRevision?.value,
29010
29164
  size: typeof record2.fields.data?.value === "object" && record2.fields.data?.value !== null ? record2.fields.data.value.size : 0
29011
29165
  }));
29012
29166
  return files.sort((a, b) => b.modifiedAt.getTime() - a.modifiedAt.getTime());
@@ -29060,35 +29214,53 @@ async function findICloudFile(filename) {
29060
29214
  recordName: record2.recordName,
29061
29215
  filename: record2.fields.filename?.value ?? "",
29062
29216
  modifiedAt: new Date(record2.modified?.timestamp ?? Date.now()),
29217
+ backupRevision: record2.fields.backupRevision?.value,
29063
29218
  size: typeof record2.fields.data?.value === "object" && record2.fields.data?.value !== null ? record2.fields.data.value.size : 0
29064
29219
  };
29065
29220
  }
29066
29221
 
29067
29222
  // src/lib/backup/icloud/backup.ts
29068
29223
  var isAuthError = (err) => err instanceof Error && (err.message.includes("AUTHENTICATION") || err.message.includes("NOT_AUTHENTICATED") || err.message.includes("sign in"));
29224
+ var UNREVISIONED_COLUMNS = /* @__PURE__ */ new Set([
29225
+ "updated_at",
29226
+ "vector",
29227
+ "embedding_model",
29228
+ "chunks",
29229
+ "origin"
29230
+ ]);
29231
+ async function getBackupRevision(database, conversationId) {
29232
+ const serialized = await database.read(async () => {
29233
+ const { Q: Q20 } = await import("@nozbe/watermelondb");
29234
+ const tables = await Promise.all(
29235
+ ["conversations", "history"].map(
29236
+ (table) => database.get(table).query(Q20.where("conversation_id", conversationId), Q20.sortBy("id", Q20.asc)).unsafeFetchRaw()
29237
+ )
29238
+ );
29239
+ return JSON.stringify(
29240
+ tables.map(
29241
+ (rows) => rows.map(
29242
+ (row) => Object.entries(row).filter(([key]) => !key.startsWith("_") && !UNREVISIONED_COLUMNS.has(key)).sort(([left], [right]) => left < right ? -1 : left > right ? 1 : 0)
29243
+ )
29244
+ )
29245
+ );
29246
+ });
29247
+ const digest = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(serialized));
29248
+ return "v1:" + Array.from(new Uint8Array(digest), (byte) => byte.toString(16).padStart(2, "0")).join("");
29249
+ }
29069
29250
  async function pushConversationToICloud(database, conversationId, userAddress, deps, _retried = false) {
29070
29251
  try {
29071
29252
  await deps.requestEncryptionKey(userAddress);
29072
29253
  const filename = `${conversationId}.json`;
29073
29254
  const existingFile = await findICloudFile(filename);
29074
- if (existingFile) {
29075
- const { Q: Q20 } = await import("@nozbe/watermelondb");
29076
- const conversationsCollection = database.get("conversations");
29077
- const records = await conversationsCollection.query(Q20.where("conversation_id", conversationId)).fetch();
29078
- if (records.length > 0) {
29079
- const conversation = conversationToStoredRaw(records[0]);
29080
- const localUpdated = conversation.updatedAt.getTime();
29081
- const remoteModified = existingFile.modifiedAt.getTime();
29082
- if (localUpdated <= remoteModified) {
29083
- return "skipped";
29084
- }
29085
- }
29255
+ const backupRevision = await getBackupRevision(database, conversationId);
29256
+ if (existingFile?.backupRevision === backupRevision) {
29257
+ return "skipped";
29086
29258
  }
29087
29259
  const exportResult = await deps.exportConversation(conversationId, userAddress);
29088
29260
  if (!exportResult.success || !exportResult.blob) {
29089
29261
  return "failed";
29090
29262
  }
29091
- await uploadFileToICloud(filename, exportResult.blob);
29263
+ await uploadFileToICloud(filename, exportResult.blob, backupRevision);
29092
29264
  return "uploaded";
29093
29265
  } catch (err) {
29094
29266
  if (isAuthError(err) && !_retried) {
@@ -30730,6 +30902,7 @@ var CONVERSATION_REDACTOR_LIMIT = 50;
30730
30902
  var NO_CONVERSATION_KEY = "__no_conversation__";
30731
30903
  var NO_STORE_FOLDERS = "Folders are not supported with a memoryStore";
30732
30904
  var NO_STORE_MASK = "piiRedaction needs a memoryStore built with embeddingOptions.maskInput";
30905
+ var MEMORY_STORE_NOT_READY = "Memory store is not ready";
30733
30906
  var conversationRedactors = /* @__PURE__ */ new Map();
30734
30907
  function getConversationRedactor(conversationId, nerDetector) {
30735
30908
  const key = conversationId ?? NO_CONVERSATION_KEY;
@@ -30820,7 +30993,7 @@ function useChatStorage(options) {
30820
30993
  () => ({ getToken, baseUrl, model: embeddingModel, maskInput: maskEmbeddingInput }),
30821
30994
  [getToken, baseUrl, embeddingModel, maskEmbeddingInput]
30822
30995
  );
30823
- const storeMaskMissing = memoryStore !== void 0 && maskEmbeddingInput !== void 0 && !memoryStore.masksEmbeddingInput;
30996
+ const storeMaskMissing = memoryStore !== void 0 && memoryStore !== null && maskEmbeddingInput !== void 0 && !memoryStore.masksEmbeddingInput;
30824
30997
  const blobManagerRef = useRef4(new BlobUrlManager());
30825
30998
  useEffect4(() => {
30826
30999
  const manager = blobManagerRef.current;
@@ -31097,6 +31270,7 @@ function useChatStorage(options) {
31097
31270
  );
31098
31271
  const retainVaultMemory = useCallback5(
31099
31272
  async (input) => {
31273
+ if (memoryStore === null) throw new Error(MEMORY_STORE_NOT_READY);
31100
31274
  if (memoryStore) {
31101
31275
  if (storeMaskMissing) throw new Error(NO_STORE_MASK);
31102
31276
  if (input.folderId !== void 0) throw new Error(NO_STORE_FOLDERS);
@@ -31128,6 +31302,7 @@ function useChatStorage(options) {
31128
31302
  );
31129
31303
  const createMemoryVaultTool2 = useCallback5(
31130
31304
  (options2) => {
31305
+ if (memoryStore === null) throw new Error(MEMORY_STORE_NOT_READY);
31131
31306
  if (storeMaskMissing) throw new Error(NO_STORE_MASK);
31132
31307
  const embOpts = getToken ? vaultEmbeddingOptions : void 0;
31133
31308
  return createMemoryVaultTool(
@@ -31149,6 +31324,7 @@ function useChatStorage(options) {
31149
31324
  );
31150
31325
  const getVaultMemories = useCallback5(
31151
31326
  (options2) => {
31327
+ if (memoryStore === null) return Promise.resolve([]);
31152
31328
  if (!memoryStore) return getAllVaultMemoriesOp(vaultCtx, options2);
31153
31329
  const { folderId, levels, kinds, ...listOptions } = options2 ?? {};
31154
31330
  if (folderId !== void 0) throw new Error(NO_STORE_FOLDERS);
@@ -31160,6 +31336,7 @@ function useChatStorage(options) {
31160
31336
  );
31161
31337
  const createVaultMemory = useCallback5(
31162
31338
  async (content, scope) => {
31339
+ if (memoryStore === null) throw new Error(MEMORY_STORE_NOT_READY);
31163
31340
  if (storeMaskMissing) throw new Error(NO_STORE_MASK);
31164
31341
  if (memoryStore) return memoryStore.create({ content, scope });
31165
31342
  const result = await createVaultMemoryOp(vaultCtx, { content, scope });
@@ -31181,6 +31358,7 @@ function useChatStorage(options) {
31181
31358
  );
31182
31359
  const updateVaultMemory = useCallback5(
31183
31360
  async (id, content, scope) => {
31361
+ if (memoryStore === null) throw new Error(MEMORY_STORE_NOT_READY);
31184
31362
  if (storeMaskMissing) throw new Error(NO_STORE_MASK);
31185
31363
  if (memoryStore) return memoryStore.update(id, { content, scope });
31186
31364
  const existing = await getVaultMemoryOp(vaultCtx, id);
@@ -31206,6 +31384,7 @@ function useChatStorage(options) {
31206
31384
  );
31207
31385
  const deleteVaultMemory = useCallback5(
31208
31386
  async (id) => {
31387
+ if (memoryStore === null) throw new Error(MEMORY_STORE_NOT_READY);
31209
31388
  if (memoryStore) return memoryStore.delete(id);
31210
31389
  const existing = await getVaultMemoryOp(vaultCtx, id);
31211
31390
  const result = await deleteVaultMemoryOp(vaultCtx, id);
@@ -31219,7 +31398,7 @@ function useChatStorage(options) {
31219
31398
  const chunkVectorCacheRef = useRef4(createChunkVectorCache());
31220
31399
  const clientToolEmbeddingsCacheRef = useRef4(/* @__PURE__ */ new Map());
31221
31400
  useEffect4(() => {
31222
- if (!getToken || memoryStore) return;
31401
+ if (!getToken || memoryStore !== void 0) return;
31223
31402
  void (async () => {
31224
31403
  try {
31225
31404
  await preEmbedVaultMemories(vaultCtx, vaultEmbeddingOptions, vaultEmbeddingCache);
@@ -31235,6 +31414,7 @@ function useChatStorage(options) {
31235
31414
  }, [vaultEmbeddingCache]);
31236
31415
  const createMemoryVaultSearchTool2 = useCallback5(
31237
31416
  (searchOptions) => {
31417
+ if (memoryStore === null) throw new Error(MEMORY_STORE_NOT_READY);
31238
31418
  if (!getToken && !memoryStore) {
31239
31419
  throw new Error("getToken is required for memory vault search tool");
31240
31420
  }
@@ -31250,6 +31430,7 @@ function useChatStorage(options) {
31250
31430
  );
31251
31431
  const createRecallTool2 = useCallback5(
31252
31432
  (toolOptions, callbacks) => {
31433
+ if (memoryStore === null) throw new Error(MEMORY_STORE_NOT_READY);
31253
31434
  if (!getToken && !memoryStore) {
31254
31435
  throw new Error("getToken is required for recall tool");
31255
31436
  }
@@ -31289,6 +31470,7 @@ function useChatStorage(options) {
31289
31470
  );
31290
31471
  const searchVaultMemoriesFn = useCallback5(
31291
31472
  async (query, searchOptions) => {
31473
+ if (memoryStore === null) return [];
31292
31474
  if (storeMaskMissing) throw new Error(NO_STORE_MASK);
31293
31475
  if (memoryStore) return (await memoryStore.factSource.search(query, searchOptions)).results;
31294
31476
  if (!getToken) return [];
@@ -31304,7 +31486,7 @@ function useChatStorage(options) {
31304
31486
  );
31305
31487
  const recallFn = useCallback5(
31306
31488
  async (query, options2) => {
31307
- if (!getToken && !memoryStore) {
31489
+ if (memoryStore === null || !getToken && !memoryStore) {
31308
31490
  return {
31309
31491
  memories: [],
31310
31492
  usedBudget: options2?.budget ?? "low",