@anuma/sdk 7.0.7 → 7.1.0

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.
@@ -11131,6 +11131,11 @@ import { Q as Q5 } from "@nozbe/watermelondb";
11131
11131
 
11132
11132
  // src/lib/db/entities/operations.ts
11133
11133
  import { Q as Q4 } from "@nozbe/watermelondb";
11134
+ async function assertCanWrite(ctx) {
11135
+ if (ctx.canWrite && !await ctx.canWrite()) {
11136
+ throw new Error("Memory source is no longer eligible");
11137
+ }
11138
+ }
11134
11139
  function entityToStored(e) {
11135
11140
  return {
11136
11141
  uniqueId: e.id,
@@ -11271,6 +11276,7 @@ async function linkMemoryEntitiesOp(ctx, memoryId, entityInputs, options) {
11271
11276
  let skipped = false;
11272
11277
  let entities = [];
11273
11278
  await ctx.database.write(async () => {
11279
+ await assertCanWrite(ctx);
11274
11280
  const {
11275
11281
  entities: byName,
11276
11282
  operations: entityOps,
@@ -11346,6 +11352,7 @@ async function replaceMemoryEntities(ctx, memoryId, entityInputs, options) {
11346
11352
  let skipped = false;
11347
11353
  let entities = [];
11348
11354
  await ctx.database.write(async () => {
11355
+ await assertCanWrite(ctx);
11349
11356
  const vaultRow = await findVaultRowInWrite(ctx, memoryId);
11350
11357
  if (options.guarded && autoLinkBlocked(vaultRow)) {
11351
11358
  skipped = true;
@@ -11406,6 +11413,7 @@ async function unlinkMemoryEntitiesOp(ctx, memoryIds) {
11406
11413
  const links = await ctx.memoryEntityCollection.query(Q4.where("memory_id", Q4.oneOf(Array.from(memoryIds)))).fetch();
11407
11414
  if (links.length === 0) return;
11408
11415
  await ctx.database.write(async () => {
11416
+ await assertCanWrite(ctx);
11409
11417
  await ctx.database.batch(...links.map((l) => l.prepareDestroyPermanently()));
11410
11418
  });
11411
11419
  }
@@ -11414,6 +11422,7 @@ async function unlinkAllMemoryEntitiesForUserOp(ctx, userId) {
11414
11422
  const links = await ctx.memoryEntityCollection.query(Q4.where("user_id", userId)).fetch();
11415
11423
  if (links.length === 0) return;
11416
11424
  await ctx.database.write(async () => {
11425
+ await assertCanWrite(ctx);
11417
11426
  await ctx.database.batch(...links.map((l) => l.prepareDestroyPermanently()));
11418
11427
  });
11419
11428
  }
@@ -11588,6 +11597,11 @@ function visibilityConditions(requested) {
11588
11597
  function isKinded(record2) {
11589
11598
  return record2.kind !== null && record2.kind !== void 0;
11590
11599
  }
11600
+ async function assertCanWrite2(ctx) {
11601
+ if (ctx.canWrite && !await ctx.canWrite()) {
11602
+ throw new Error("Memory source is no longer eligible");
11603
+ }
11604
+ }
11591
11605
  function assertValidKindLevel(kind, level) {
11592
11606
  const hasKind = kind !== null && kind !== void 0;
11593
11607
  if (hasKind && !MEMORY_KINDS.includes(kind)) {
@@ -11768,8 +11782,7 @@ async function createVaultMemoryOp(ctx, opts) {
11768
11782
  ctx.embeddedWalletSigner
11769
11783
  ) : opts.content;
11770
11784
  const created = await ctx.database.write(async () => {
11771
- if (ctx.canWrite && !await ctx.canWrite())
11772
- throw new Error("Memory source is no longer eligible");
11785
+ await assertCanWrite2(ctx);
11773
11786
  return ctx.vaultMemoryCollection.create(
11774
11787
  (record2) => populateNewVaultMemory(record2, ctx, opts, encryptedContent, encryptedKindValue)
11775
11788
  );
@@ -11788,8 +11801,7 @@ async function createSupersedingMemoryOp(ctx, opts, targetId) {
11788
11801
  ) : opts.content;
11789
11802
  let createdRecord = null;
11790
11803
  await ctx.database.write(async () => {
11791
- if (ctx.canWrite && !await ctx.canWrite())
11792
- throw new Error("Memory source is no longer eligible");
11804
+ await assertCanWrite2(ctx);
11793
11805
  let target;
11794
11806
  try {
11795
11807
  target = await ctx.vaultMemoryCollection.find(targetId);
@@ -11881,8 +11893,7 @@ async function createVaultMemoriesBatchOp(ctx, optionsArray) {
11881
11893
  })
11882
11894
  );
11883
11895
  const created = await ctx.database.write(async () => {
11884
- if (ctx.canWrite && !await ctx.canWrite())
11885
- throw new Error("Memory source is no longer eligible");
11896
+ await assertCanWrite2(ctx);
11886
11897
  const prepared = optionsArray.map(
11887
11898
  (opts, i) => ctx.vaultMemoryCollection.prepareCreate(
11888
11899
  (record2) => populateNewVaultMemory(record2, ctx, opts, encryptedContents[i], encryptedKindValues[i])
@@ -12187,8 +12198,7 @@ async function updateVaultMemoryOp(ctx, id, opts) {
12187
12198
  const record2 = probe;
12188
12199
  const originalUpdatedAt = record2.updatedAt.getTime();
12189
12200
  await ctx.database.write(async () => {
12190
- if (ctx.canWrite && !await ctx.canWrite())
12191
- throw new Error("Memory source is no longer eligible");
12201
+ await assertCanWrite2(ctx);
12192
12202
  if (record2.isDeleted || record2.supersededBy || !isOwnedByCtxUser(ctx, record2) || opts.freeFormOnly && isKinded(record2)) {
12193
12203
  stale = true;
12194
12204
  return;
@@ -12299,6 +12309,7 @@ async function setMemoryEntitiesOp(ctx, memoryId, entities) {
12299
12309
  let stale = false;
12300
12310
  const originalUpdatedAt = record2.updatedAt.getTime();
12301
12311
  await ctx.database.write(async (writer) => {
12312
+ await assertCanWrite2(ctx);
12302
12313
  if (record2.isDeleted || !isOwnedByCtxUser(ctx, record2)) {
12303
12314
  stale = true;
12304
12315
  return;
@@ -12340,6 +12351,7 @@ async function clearMemoryTopicsOverrideOp(ctx, memoryId, options) {
12340
12351
  const originalUpdatedAt = record2.updatedAt.getTime();
12341
12352
  let cleared = false;
12342
12353
  await ctx.database.write(async () => {
12354
+ await assertCanWrite2(ctx);
12343
12355
  if (options?.unlessTopicsRecorded && parseTopics(record2.topics) !== null) return;
12344
12356
  cleared = true;
12345
12357
  await record2.update((r) => {
@@ -12363,6 +12375,7 @@ async function setMemoryVisibilityOp(ctx, id, opts) {
12363
12375
  if (record2.isDeleted || !isOwnedByCtxUser(ctx, record2)) return null;
12364
12376
  let stale = false;
12365
12377
  await ctx.database.write(async () => {
12378
+ await assertCanWrite2(ctx);
12366
12379
  if (record2.isDeleted || !isOwnedByCtxUser(ctx, record2)) {
12367
12380
  stale = true;
12368
12381
  return;
@@ -12391,6 +12404,7 @@ async function deleteVaultMemoryOp(ctx, id) {
12391
12404
  if (record2.isDeleted || !isOwnedByCtxUser(ctx, record2)) return false;
12392
12405
  let stale = false;
12393
12406
  await ctx.database.write(async () => {
12407
+ await assertCanWrite2(ctx);
12394
12408
  if (record2.isDeleted || !isOwnedByCtxUser(ctx, record2)) {
12395
12409
  stale = true;
12396
12410
  return;
@@ -12428,8 +12442,7 @@ async function supersedeVaultMemoryOp(ctx, id, supersededById) {
12428
12442
  }
12429
12443
  let stale = false;
12430
12444
  await ctx.database.write(async () => {
12431
- if (ctx.canWrite && !await ctx.canWrite())
12432
- throw new Error("Memory source is no longer eligible");
12445
+ await assertCanWrite2(ctx);
12433
12446
  if (record2.isDeleted || record2.supersededBy || !isOwnedByCtxUser(ctx, record2)) {
12434
12447
  stale = true;
12435
12448
  return;
@@ -12591,6 +12604,7 @@ async function stampTopicsExtractedAtOp(ctx, memoryIds, extractedAt, version = T
12591
12604
  for (let i = 0; i < uniqueIds.length; i += CHUNK) {
12592
12605
  const chunkIds = uniqueIds.slice(i, i + CHUNK);
12593
12606
  await ctx.database.write(async () => {
12607
+ await assertCanWrite2(ctx);
12594
12608
  const records = [];
12595
12609
  for (const id of chunkIds) {
12596
12610
  try {
@@ -12624,6 +12638,7 @@ async function relinkMemoryTopicsOp(ctx, memoryIds) {
12624
12638
  for (const id of Array.from(new Set(memoryIds))) {
12625
12639
  try {
12626
12640
  await ctx.database.write(async (writer) => {
12641
+ await assertCanWrite2(ctx);
12627
12642
  let record2;
12628
12643
  try {
12629
12644
  record2 = await ctx.vaultMemoryCollection.find(id);
@@ -12654,6 +12669,7 @@ async function backfillMemoryTopicsOp(ctx, memoryIds) {
12654
12669
  for (let i = 0; i < uniqueIds.length; i += CHUNK) {
12655
12670
  const chunkIds = uniqueIds.slice(i, i + CHUNK);
12656
12671
  await ctx.database.write(async () => {
12672
+ await assertCanWrite2(ctx);
12657
12673
  const writes = [];
12658
12674
  for (const id of chunkIds) {
12659
12675
  let record2;
@@ -12686,6 +12702,7 @@ async function deleteAllVaultMemoriesForUserOp(ctx, userId) {
12686
12702
  const records = await ctx.vaultMemoryCollection.query(Q5.where("user_id", userId), Q5.where("is_deleted", false)).fetch();
12687
12703
  if (records.length === 0) return 0;
12688
12704
  await ctx.database.write(async () => {
12705
+ await assertCanWrite2(ctx);
12689
12706
  const prepared = records.map(
12690
12707
  (record2) => record2.prepareUpdate((r) => {
12691
12708
  r._setRaw("is_deleted", true);
@@ -12740,6 +12757,7 @@ async function archiveVaultMemoryOp(ctx, id, opts) {
12740
12757
  let stale = false;
12741
12758
  const archivedAtValue = opts?.now ?? Date.now();
12742
12759
  await ctx.database.write(async () => {
12760
+ await assertCanWrite2(ctx);
12743
12761
  if (record2.isDeleted || !isOwnedByCtxUser(ctx, record2) || record2.archivedAt !== null) {
12744
12762
  stale = true;
12745
12763
  return;
@@ -12763,6 +12781,7 @@ async function restoreVaultMemoryOp(ctx, id) {
12763
12781
  if (record2.isDeleted || !isOwnedByCtxUser(ctx, record2)) return false;
12764
12782
  let stale = false;
12765
12783
  await ctx.database.write(async () => {
12784
+ await assertCanWrite2(ctx);
12766
12785
  if (record2.isDeleted || !isOwnedByCtxUser(ctx, record2)) {
12767
12786
  stale = true;
12768
12787
  return;
@@ -12783,6 +12802,7 @@ async function hardDeleteDecayedOp(ctx, id, opts) {
12783
12802
  const now = opts.now ?? Date.now();
12784
12803
  let stale = false;
12785
12804
  await ctx.database.write(async () => {
12805
+ await assertCanWrite2(ctx);
12786
12806
  const archivedAt = record2.archivedAt;
12787
12807
  if (record2.isDeleted || !isOwnedByCtxUser(ctx, record2) || archivedAt === null || now - archivedAt <= opts.hardDeleteWindowMs) {
12788
12808
  stale = true;
@@ -12822,6 +12842,7 @@ async function updateVaultMemoryEmbeddingOp(ctx, id, embedding2, embeddingModel,
12822
12842
  }
12823
12843
  let written = false;
12824
12844
  await ctx.database.write(async () => {
12845
+ await assertCanWrite2(ctx);
12825
12846
  if (record2.isDeleted || !isOwnedByCtxUser(ctx, record2)) return;
12826
12847
  const originalUpdatedAt = record2.updatedAt.getTime();
12827
12848
  if (originalUpdatedAt !== readUpdatedAt || record2._getRaw("content") !== readStoredContent)
@@ -16334,6 +16355,13 @@ function restoreCandidates(candidates, redactor, ownNames) {
16334
16355
  ).map((c) => c.candidate);
16335
16356
  }
16336
16357
  async function extractAndRetain(messages, retainCtx, options) {
16358
+ const { memoryStore, signal } = options;
16359
+ if (memoryStore === null) throw new Error("Memory store is not ready");
16360
+ if (memoryStore && options.folderId !== void 0 && options.folderId !== null)
16361
+ throw new Error("Remote memory extraction does not support folders");
16362
+ if (memoryStore && options.extract.piiRedaction && !memoryStore.masksEmbeddingInput)
16363
+ throw new Error("PII-redacted extraction requires a memory store that masks embedding inputs");
16364
+ signal?.throwIfAborted();
16337
16365
  const minConfidence = options.minConfidence ?? DEFAULT_MIN_CONFIDENCE;
16338
16366
  const resolvedConsolidatePii = options.consolidateOptions?.piiRedaction ?? options.extract.piiRedaction;
16339
16367
  const consolidateOptions = options.consolidateOptions !== void 0 ? {
@@ -16367,6 +16395,7 @@ async function extractAndRetain(messages, retainCtx, options) {
16367
16395
  }
16368
16396
  });
16369
16397
  const extractMs = Date.now() - tExtract;
16398
+ signal?.throwIfAborted();
16370
16399
  const filtered = candidates.filter((c) => c.confidence >= minConfidence);
16371
16400
  const log = getLogger();
16372
16401
  const screened = screenCandidatesForInjection(filtered);
@@ -16414,16 +16443,33 @@ async function extractAndRetain(messages, retainCtx, options) {
16414
16443
  const results = [];
16415
16444
  const quarantinedInfo = [];
16416
16445
  let failedWrites = 0;
16446
+ let auditRows;
16417
16447
  const tRetain = Date.now();
16418
16448
  for (const { candidate, isQuarantined, reason, signature } of toRetain) {
16419
16449
  try {
16450
+ signal?.throwIfAborted();
16420
16451
  if (isQuarantined) {
16421
- const existingId = await findQuarantinedDuplicateOp(
16422
- retainCtx.vaultCtx,
16423
- candidate.content,
16424
- candidate.sourceMessageIds,
16425
- { scope: options.scope ?? "private", folderId: options.folderId ?? null }
16426
- ).catch(() => null);
16452
+ let existingId;
16453
+ if (memoryStore) {
16454
+ auditRows ?? (auditRows = memoryStore.list({
16455
+ scopes: [options.scope ?? "private"],
16456
+ includeArchived: true,
16457
+ includeQuarantined: true,
16458
+ includeSuperseded: true
16459
+ }));
16460
+ const sourceKey = [...new Set(candidate.sourceMessageIds)].sort().join("\0");
16461
+ const normalize = (value) => value.normalize("NFKC").trim().replace(/\s+/g, " ").toLowerCase();
16462
+ existingId = (await auditRows).find(
16463
+ (row) => row.trustTier === "quarantined" && row.scope === (options.scope ?? "private") && [...new Set(row.sourceChunkIds ?? [])].sort().join("\0") === sourceKey && normalize(row.content) === normalize(candidate.content)
16464
+ )?.uniqueId ?? null;
16465
+ } else {
16466
+ existingId = await findQuarantinedDuplicateOp(
16467
+ retainCtx.vaultCtx,
16468
+ candidate.content,
16469
+ candidate.sourceMessageIds,
16470
+ { scope: options.scope ?? "private", folderId: options.folderId ?? null }
16471
+ ).catch(() => null);
16472
+ }
16427
16473
  if (existingId) {
16428
16474
  quarantinedInfo.push({
16429
16475
  candidate,
@@ -16434,18 +16480,26 @@ async function extractAndRetain(messages, retainCtx, options) {
16434
16480
  continue;
16435
16481
  }
16436
16482
  }
16437
- const result = await retain(candidate.content, retainCtx, {
16483
+ const retainOptions = {
16438
16484
  source: "auto-extracted",
16439
16485
  sourceChunkIds: candidate.sourceMessageIds,
16440
16486
  respectTombstones: true,
16441
16487
  ...options.scope !== void 0 && { scope: options.scope },
16442
- ...options.folderId !== void 0 && { folderId: options.folderId },
16443
16488
  ...consolidateOptions !== void 0 && { consolidateOptions },
16444
16489
  ...candidate.eventTime !== null && { eventTime: candidate.eventTime },
16445
16490
  factType: candidate.type,
16446
16491
  ...isQuarantined && { trustTier: "quarantined", enableAutoMerge: false }
16492
+ };
16493
+ const result = memoryStore ? await memoryStore.retain(candidate.content, {
16494
+ ...retainOptions,
16495
+ canWrite: retainCtx.vaultCtx.canWrite,
16496
+ signal
16497
+ }) : await retain(candidate.content, retainCtx, {
16498
+ ...retainOptions,
16499
+ ...options.folderId !== void 0 && { folderId: options.folderId }
16447
16500
  });
16448
16501
  if (isQuarantined) {
16502
+ auditRows = void 0;
16449
16503
  const info = {
16450
16504
  candidate,
16451
16505
  memoryId: result.memoryId,
@@ -16464,9 +16518,16 @@ async function extractAndRetain(messages, retainCtx, options) {
16464
16518
  }
16465
16519
  succeededCandidates.push(candidate);
16466
16520
  results.push(result);
16467
- if (result.action !== "suppressed" && options.entityCtx && candidate.entities.length > 0) {
16521
+ if (result.action !== "suppressed" && candidate.entities.length > 0) {
16468
16522
  try {
16469
- if (!await isMemoryTopicsUserManaged(options.entityCtx, result.memoryId)) {
16523
+ signal?.throwIfAborted();
16524
+ if (memoryStore) {
16525
+ await memoryStore.addTopics(result.memoryId, candidate.entities, {
16526
+ unlessUserManaged: true,
16527
+ canWrite: retainCtx.vaultCtx.canWrite,
16528
+ signal
16529
+ });
16530
+ } else if (options.entityCtx && !await isMemoryTopicsUserManaged(options.entityCtx, result.memoryId)) {
16470
16531
  await linkMemoryEntitiesOp(options.entityCtx, result.memoryId, candidate.entities, {
16471
16532
  unlessTopicsUserManaged: true
16472
16533
  });
@@ -16714,7 +16775,7 @@ function createAutoExtractor(options) {
16714
16775
  };
16715
16776
  const consolidateOptions = options.consolidate ? { ...options.consolidate, ...subPassAuth(options.consolidate.baseUrl) } : void 0;
16716
16777
  const injectionClassifierOptions = options.injectionClassifier ? { ...options.injectionClassifier, ...subPassAuth(options.injectionClassifier.baseUrl) } : void 0;
16717
- if (options.retainCtx.vaultCtx.entityCtx && !options.entityCtx) {
16778
+ if (!options.memoryStore && options.retainCtx.vaultCtx.entityCtx && !options.entityCtx) {
16718
16779
  getLogger().warn(
16719
16780
  "[memory/extract] retainCtx.vaultCtx.entityCtx is set but extractor was created without `entityCtx` \u2014 W5 graph lane will receive no writes"
16720
16781
  );
@@ -16759,6 +16820,8 @@ function createAutoExtractor(options) {
16759
16820
  try {
16760
16821
  const { candidates, results, failedCount, outcome: outcome2, failure, funnel, timings, model } = await extractAndRetain(window2, retainCtx, {
16761
16822
  extract,
16823
+ memoryStore: options.memoryStore,
16824
+ signal: options.signal,
16762
16825
  ...options.minConfidence !== void 0 && { minConfidence: options.minConfidence },
16763
16826
  ...options.entityCtx !== void 0 && { entityCtx: options.entityCtx },
16764
16827
  ...options.scope !== void 0 && { scope: options.scope },
@@ -18845,6 +18908,7 @@ function createLocalMemoryStore(options) {
18845
18908
  database,
18846
18909
  entityCollection: database.get("entity"),
18847
18910
  memoryEntityCollection: database.get("memory_entity"),
18911
+ canWrite: options.canWrite,
18848
18912
  ...userId !== void 0 && { userId },
18849
18913
  ...options.allowUnscopedRows !== void 0 && {
18850
18914
  allowUnscopedRows: options.allowUnscopedRows
@@ -18856,10 +18920,21 @@ function createLocalMemoryStore(options) {
18856
18920
  walletAddress: options.walletAddress,
18857
18921
  signMessage: options.signMessage,
18858
18922
  embeddedWalletSigner: options.embeddedWalletSigner,
18923
+ canWrite: options.canWrite,
18859
18924
  ...userId !== void 0 && { userId },
18860
18925
  ...options.singleTenant !== void 0 && { singleTenant: options.singleTenant },
18861
18926
  entityCtx
18862
18927
  };
18928
+ const assertWrite = async (writeOptions) => {
18929
+ writeOptions?.signal?.throwIfAborted();
18930
+ if (writeOptions?.canWrite && !await writeOptions.canWrite()) {
18931
+ throw new Error("Memory source is no longer eligible");
18932
+ }
18933
+ if (options.canWrite && !await options.canWrite()) {
18934
+ throw new Error("Memory source is no longer eligible");
18935
+ }
18936
+ writeOptions?.signal?.throwIfAborted();
18937
+ };
18863
18938
  const vaultCache = options.vaultCache ?? createVaultEmbeddingCache();
18864
18939
  const ownedBy = userId !== void 0 ? [Q8.where("user_id", userId)] : [];
18865
18940
  const linksOwnedBy = userId === void 0 ? [] : options.allowUnscopedRows ? [Q8.or(Q8.where("user_id", userId), Q8.where("user_id", null))] : [Q8.where("user_id", userId)];
@@ -18971,9 +19046,25 @@ function createLocalMemoryStore(options) {
18971
19046
  archive: (id) => archiveVaultMemoryOp(vaultCtx, id),
18972
19047
  restore: (id) => restoreVaultMemoryOp(vaultCtx, id),
18973
19048
  setTopics: (memoryId, topics) => setMemoryEntitiesOp(vaultCtx, memoryId, topics),
18974
- addTopics: (memoryId, topics) => database.write(async (writer) => {
19049
+ addTopics: (memoryId, topics, writeOptions) => database.write(async (writer) => {
19050
+ await assertWrite(writeOptions);
18975
19051
  const owned = await vaultCtx.vaultMemoryCollection.query(Q8.where("id", memoryId), Q8.where("is_deleted", false), ...ownedBy).fetchCount();
18976
- return owned > 0 ? writer.callWriter(() => linkMemoryEntitiesOp(entityCtx, memoryId, topics)) : [];
19052
+ return owned > 0 ? writer.callWriter(
19053
+ () => linkMemoryEntitiesOp(
19054
+ {
19055
+ ...entityCtx,
19056
+ canWrite: async () => {
19057
+ await assertWrite(writeOptions);
19058
+ return true;
19059
+ }
19060
+ },
19061
+ memoryId,
19062
+ topics,
19063
+ {
19064
+ unlessTopicsUserManaged: writeOptions?.unlessUserManaged
19065
+ }
19066
+ )
19067
+ ) : [];
18977
19068
  }),
18978
19069
  setVisibility: (id, visibility, visibilityOptions) => setMemoryVisibilityOp(vaultCtx, id, { visibility, ...visibilityOptions }),
18979
19070
  recall: (query, recallOptions) => recall(
@@ -18983,7 +19074,17 @@ function createLocalMemoryStore(options) {
18983
19074
  ),
18984
19075
  retain: (content, retainOptions) => retain(
18985
19076
  content,
18986
- { vaultCtx, embeddingOptions: options.embeddingOptions, vaultCache },
19077
+ {
19078
+ vaultCtx: {
19079
+ ...vaultCtx,
19080
+ canWrite: async () => {
19081
+ await assertWrite(retainOptions);
19082
+ return true;
19083
+ }
19084
+ },
19085
+ embeddingOptions: options.embeddingOptions,
19086
+ vaultCache
19087
+ },
18987
19088
  retainOptions
18988
19089
  ),
18989
19090
  factSource: {
@@ -19633,7 +19734,14 @@ function stored(row) {
19633
19734
  function createRemoteMemoryPipeline(options) {
19634
19735
  const { persistence, embeddingOptions } = options;
19635
19736
  const model = embeddingOptions.model ?? DEFAULT_API_EMBEDDING_MODEL;
19636
- const operation = (mode, queryOptions = embeddingOptions) => {
19737
+ const operation = (mode, queryOptions = embeddingOptions, writeOptions) => {
19738
+ const signal = writeOptions?.signal;
19739
+ const assertWrite = async () => {
19740
+ signal?.throwIfAborted();
19741
+ if (writeOptions?.canWrite && !await writeOptions.canWrite())
19742
+ throw new Error("Memory source is no longer eligible");
19743
+ signal?.throwIfAborted();
19744
+ };
19637
19745
  const rejectUndecryptable = (failed) => {
19638
19746
  if (!failed.length) return;
19639
19747
  if (mode === "retain")
@@ -19651,7 +19759,7 @@ function createRemoteMemoryPipeline(options) {
19651
19759
  return items.map((item) => stored(item.memory));
19652
19760
  };
19653
19761
  const get = async (id) => {
19654
- const snapshot = snapshots.get(id) ?? await persistence.get(id);
19762
+ const snapshot = snapshots.get(id) ?? await (signal ? persistence.get(id, signal) : persistence.get(id));
19655
19763
  if (!snapshot || snapshot.memory.is_deleted || snapshot.memory.superseded_by) return null;
19656
19764
  snapshots.set(id, snapshot);
19657
19765
  return stored(snapshot.memory);
@@ -19701,7 +19809,7 @@ function createRemoteMemoryPipeline(options) {
19701
19809
  if (forced.size < 100 && temporal[i] !== void 0) forced.add(temporal[i]);
19702
19810
  }
19703
19811
  const forceIds = [...forced];
19704
- const windowOptions = (i, signal) => ({
19812
+ const windowOptions = (i, signal2) => ({
19705
19813
  limit: Math.min(100, Math.max((search.limit ?? 8) * 3, 30)),
19706
19814
  force_ids: i === 0 ? forceIds : [],
19707
19815
  ...search.scopes && { scopes: search.scopes },
@@ -19709,13 +19817,15 @@ function createRemoteMemoryPipeline(options) {
19709
19817
  ...search.memoryIds !== void 0 && { memory_ids: search.memoryIds },
19710
19818
  include_archived: search.includeArchived,
19711
19819
  embedding_model: model,
19712
- ...signal && { signal }
19820
+ ...signal2 && { signal: signal2 }
19713
19821
  });
19714
19822
  const vectors = [queryEmbedding, ...facetEmbeddings];
19715
19823
  let windows;
19716
19824
  if (mode === "retain") {
19717
19825
  windows = await Promise.all(
19718
- vectors.map((embedding2, i) => persistence.candidateSet(embedding2, windowOptions(i)))
19826
+ vectors.map(
19827
+ (embedding2, i) => persistence.candidateSet(embedding2, windowOptions(i, signal))
19828
+ )
19719
19829
  );
19720
19830
  } else {
19721
19831
  const deadline = new AbortController();
@@ -19803,12 +19913,15 @@ function createRemoteMemoryPipeline(options) {
19803
19913
  twin_opt_in: false
19804
19914
  };
19805
19915
  };
19806
- const write = async (memory, snapshot) => remember([await persistence.put(memory, snapshot)])[0];
19916
+ const write = async (memory, snapshot) => {
19917
+ await assertWrite();
19918
+ return remember([await persistence.put(memory, snapshot, signal)])[0];
19919
+ };
19807
19920
  const port = {
19808
19921
  prepare,
19809
19922
  get,
19810
19923
  getFresh: async (id) => {
19811
- const current = await persistence.get(id);
19924
+ const current = await (signal ? persistence.get(id, signal) : persistence.get(id));
19812
19925
  return current && !current.memory.is_deleted && !current.memory.superseded_by ? stored(current.memory) : null;
19813
19926
  },
19814
19927
  tombstones: async (embedding2, embeddingModel, scope, folderId) => {
@@ -19819,7 +19932,8 @@ function createRemoteMemoryPipeline(options) {
19819
19932
  embedding_model: embeddingModel,
19820
19933
  strict_model: true,
19821
19934
  include_deleted: true,
19822
- deleted_only: true
19935
+ deleted_only: true,
19936
+ signal
19823
19937
  });
19824
19938
  rejectUndecryptable(window2.failed);
19825
19939
  return remember(window2.items);
@@ -19875,14 +19989,18 @@ function createRemoteMemoryPipeline(options) {
19875
19989
  if (!target || !newer || id === successor) return false;
19876
19990
  const old = snapshots.get(id);
19877
19991
  const next = snapshots.get(successor);
19992
+ await assertWrite();
19878
19993
  remember(
19879
- await persistence.putMany([
19880
- {
19881
- memory: { ...old.memory, superseded_by: successor, superseded_at: Date.now() },
19882
- expectedVersion: old
19883
- },
19884
- { memory: next.memory, expectedVersion: next }
19885
- ])
19994
+ await persistence.putMany(
19995
+ [
19996
+ {
19997
+ memory: { ...old.memory, superseded_by: successor, superseded_at: Date.now() },
19998
+ expectedVersion: old
19999
+ },
20000
+ { memory: next.memory, expectedVersion: next }
20001
+ ],
20002
+ signal
20003
+ )
19886
20004
  );
19887
20005
  return true;
19888
20006
  },
@@ -19891,18 +20009,22 @@ function createRemoteMemoryPipeline(options) {
19891
20009
  if (!target) return { created: null, retired: false };
19892
20010
  const snapshot = snapshots.get(targetId);
19893
20011
  const successor = createRow(input);
20012
+ await assertWrite();
19894
20013
  const rows = remember(
19895
- await persistence.putMany([
19896
- { memory: successor, expectedVersion: 0 },
19897
- {
19898
- memory: {
19899
- ...snapshot.memory,
19900
- superseded_by: successor.memory_id,
19901
- superseded_at: Date.now()
19902
- },
19903
- expectedVersion: snapshot
19904
- }
19905
- ])
20014
+ await persistence.putMany(
20015
+ [
20016
+ { memory: successor, expectedVersion: 0 },
20017
+ {
20018
+ memory: {
20019
+ ...snapshot.memory,
20020
+ superseded_by: successor.memory_id,
20021
+ superseded_at: Date.now()
20022
+ },
20023
+ expectedVersion: snapshot
20024
+ }
20025
+ ],
20026
+ signal
20027
+ )
19906
20028
  );
19907
20029
  return { created: rows[0], retired: true };
19908
20030
  }
@@ -19937,7 +20059,7 @@ function createRemoteMemoryPipeline(options) {
19937
20059
  factSource,
19938
20060
  recall: async (query, recallOptions = {}) => recall(query, { embeddingOptions, factSource }, { ...recallOptions, types: ["fact"] }),
19939
20061
  retain: async (content, retainOptions) => {
19940
- const { port } = operation("retain");
20062
+ const { port } = operation("retain", embeddingOptions, retainOptions);
19941
20063
  return retainWithPersistence(
19942
20064
  content,
19943
20065
  {
@@ -20598,16 +20720,21 @@ function createRemoteMemoryStore(options) {
20598
20720
  const record2 = await persistence.get(id);
20599
20721
  return record2 && !record2.memory.is_deleted ? record2 : null;
20600
20722
  };
20601
- const mutateLocked = async (id, change, edit) => {
20602
- const record2 = await live(id);
20723
+ const mutateLocked = async (id, change, edit, writeOptions) => {
20724
+ writeOptions?.signal?.throwIfAborted();
20725
+ const record2 = writeOptions?.signal ? await persistence.get(id, writeOptions.signal) : await live(id);
20603
20726
  if (!record2) return null;
20727
+ if (record2.memory.is_deleted) return null;
20728
+ if (writeOptions?.canWrite && !await writeOptions.canWrite())
20729
+ throw new Error("Memory source is no longer eligible");
20730
+ writeOptions?.signal?.throwIfAborted();
20604
20731
  const next = edit(structuredClone(record2.memory));
20605
20732
  if (!next) return null;
20606
- const saved = await persistence.put(next, record2);
20733
+ const saved = writeOptions?.signal ? await persistence.put(next, record2, writeOptions.signal) : await persistence.put(next, record2);
20607
20734
  notify(typeof change === "function" ? change(record2.memory) : change, [saved]);
20608
20735
  return saved;
20609
20736
  };
20610
- const mutate = (id, change, edit) => serialized(id, () => mutateLocked(id, change, edit));
20737
+ const mutate = (id, change, edit, writeOptions) => serialized(id, () => mutateLocked(id, change, edit, writeOptions));
20611
20738
  const serializedAll = (ids, task) => [...new Set(ids)].sort().reduceRight((inner, id) => () => serialized(id, inner), task)();
20612
20739
  const mutateMany = async (ids, change, edit) => {
20613
20740
  const saved = /* @__PURE__ */ new Map();
@@ -21120,13 +21247,21 @@ function createRemoteMemoryStore(options) {
21120
21247
  }))
21121
21248
  );
21122
21249
  },
21123
- addTopics: async (memoryId, topics) => {
21250
+ addTopics: async (memoryId, topics, writeOptions) => {
21124
21251
  const added = await withKnownKinds(topicInputs(topics, "auto"));
21125
- const saved = await mutate(memoryId, "edit", (memory) => ({
21126
- ...memory,
21127
- topics: serializeTopics(mergeTopics(parseTopics(memory.topics) ?? [], added)),
21128
- topics_updated_at: Date.now()
21129
- }));
21252
+ const saved = await mutate(
21253
+ memoryId,
21254
+ "edit",
21255
+ (memory) => {
21256
+ if (writeOptions?.unlessUserManaged && memory.topics_user_managed) return null;
21257
+ return {
21258
+ ...memory,
21259
+ topics: serializeTopics(mergeTopics(parseTopics(memory.topics) ?? [], added)),
21260
+ topics_updated_at: Date.now()
21261
+ };
21262
+ },
21263
+ writeOptions
21264
+ );
21130
21265
  if (!saved) return [];
21131
21266
  const now = /* @__PURE__ */ new Date();
21132
21267
  return mergeTopics([], added).map(
@@ -21977,6 +22112,7 @@ function createDurableAutoExtractor(options) {
21977
22112
  };
21978
22113
  const folderId = options.folderId ?? null;
21979
22114
  let disposed = false;
22115
+ let activeBatch = null;
21980
22116
  let running = false;
21981
22117
  let wakeRequested = false;
21982
22118
  let timer;
@@ -22013,14 +22149,18 @@ function createDurableAutoExtractor(options) {
22013
22149
  }, delay);
22014
22150
  }
22015
22151
  async function extractBatch(messages, conversationId, ids, jobScope, jobFolderId) {
22152
+ if (disposed) throw new Error("Extraction session was disposed");
22016
22153
  let watchdog;
22017
22154
  let cancelled = false;
22155
+ const controller = new AbortController();
22156
+ activeBatch = controller;
22018
22157
  const model = options.modelForScope?.(jobScope) ?? options.extract.model;
22019
22158
  try {
22020
22159
  return await Promise.race([
22021
22160
  new Promise((resolve, reject) => {
22022
22161
  const worker = createAutoExtractor({
22023
22162
  ...options,
22163
+ signal: controller.signal,
22024
22164
  retainCtx: {
22025
22165
  ...options.retainCtx,
22026
22166
  vaultCtx: {
@@ -22049,6 +22189,8 @@ function createDurableAutoExtractor(options) {
22049
22189
  ]);
22050
22190
  } finally {
22051
22191
  cancelled = true;
22192
+ controller.abort();
22193
+ if (activeBatch === controller) activeBatch = null;
22052
22194
  if (watchdog !== void 0) clearTimeout(watchdog);
22053
22195
  }
22054
22196
  }
@@ -22060,6 +22202,7 @@ function createDurableAutoExtractor(options) {
22060
22202
  for (const message of loaded)
22061
22203
  if (message && message.messageId > 0) sequences.set(message.uniqueId, message.messageId);
22062
22204
  await database.write(async () => {
22205
+ if (disposed) return;
22063
22206
  const current = idsOf(job);
22064
22207
  const acknowledged = ids.filter((id) => current.includes(id));
22065
22208
  if (!acknowledged.length) return;
@@ -22232,6 +22375,7 @@ function createDurableAutoExtractor(options) {
22232
22375
  jobScope,
22233
22376
  jobFolderId
22234
22377
  );
22378
+ if (disposed) throw new Error("Extraction session was disposed");
22235
22379
  options.onTurnComplete?.(result);
22236
22380
  return result;
22237
22381
  };
@@ -22268,6 +22412,7 @@ function createDurableAutoExtractor(options) {
22268
22412
  );
22269
22413
  scheduleAfterSuccess = true;
22270
22414
  } catch (error) {
22415
+ if (disposed) break;
22271
22416
  attempts.set(job.id, attempted + 1);
22272
22417
  retry = true;
22273
22418
  reportError(error, conversationId);
@@ -22290,6 +22435,14 @@ function createDurableAutoExtractor(options) {
22290
22435
  schedule(retry ? options.retryDelayMs ?? 3e4 : 0);
22291
22436
  }
22292
22437
  }
22438
+ function dispose() {
22439
+ disposed = true;
22440
+ activeBatch?.abort(options.signal?.reason);
22441
+ if (timer !== void 0) clearTimeout(timer);
22442
+ options.signal?.removeEventListener("abort", dispose);
22443
+ }
22444
+ if (options.signal?.aborted) dispose();
22445
+ else options.signal?.addEventListener("abort", dispose, { once: true });
22293
22446
  schedule(0);
22294
22447
  return {
22295
22448
  processTurn(messages, conversationId) {
@@ -22369,10 +22522,7 @@ function createDurableAutoExtractor(options) {
22369
22522
  return true;
22370
22523
  },
22371
22524
  isProcessing: () => running,
22372
- dispose() {
22373
- disposed = true;
22374
- if (timer !== void 0) clearTimeout(timer);
22375
- }
22525
+ dispose
22376
22526
  };
22377
22527
  }
22378
22528
 
@@ -25621,6 +25771,7 @@ var CONVERSATION_REDACTOR_LIMIT = 50;
25621
25771
  var NO_CONVERSATION_KEY = "__no_conversation__";
25622
25772
  var NO_STORE_FOLDERS = "Folders are not supported with a memoryStore";
25623
25773
  var NO_STORE_MASK = "piiRedaction needs a memoryStore built with embeddingOptions.maskInput";
25774
+ var MEMORY_STORE_NOT_READY = "Memory store is not ready";
25624
25775
  var conversationRedactors = /* @__PURE__ */ new Map();
25625
25776
  var clientToolFilterCache = /* @__PURE__ */ new Map();
25626
25777
  function getConversationRedactor(conversationId, nerDetector) {
@@ -25765,7 +25916,7 @@ function useChatStorage(options) {
25765
25916
  [resolvedPiiRedaction]
25766
25917
  );
25767
25918
  const maskEmbeddingInput = isPiiRedactor(resolvedPiiRedaction) ? maskForEmbedding : void 0;
25768
- const storeMaskMissing = memoryStore !== void 0 && maskEmbeddingInput !== void 0 && !memoryStore.masksEmbeddingInput;
25919
+ const storeMaskMissing = memoryStore !== void 0 && memoryStore !== null && maskEmbeddingInput !== void 0 && !memoryStore.masksEmbeddingInput;
25769
25920
  const messagesCollection = useMemo(() => database.get("history"), [database]);
25770
25921
  const conversationsCollection = useMemo(
25771
25922
  () => database.get("conversations"),
@@ -26014,6 +26165,7 @@ function useChatStorage(options) {
26014
26165
  }, [vaultEmbeddingCache]);
26015
26166
  const retainVaultMemory = useCallback4(
26016
26167
  async (input) => {
26168
+ if (memoryStore === null) throw new Error(MEMORY_STORE_NOT_READY);
26017
26169
  if (memoryStore) {
26018
26170
  if (storeMaskMissing) throw new Error(NO_STORE_MASK);
26019
26171
  if (input.folderId !== void 0) throw new Error(NO_STORE_FOLDERS);
@@ -26059,6 +26211,7 @@ function useChatStorage(options) {
26059
26211
  );
26060
26212
  const createMemoryVaultTool2 = useCallback4(
26061
26213
  (options2) => {
26214
+ if (memoryStore === null) throw new Error(MEMORY_STORE_NOT_READY);
26062
26215
  if (storeMaskMissing) throw new Error(NO_STORE_MASK);
26063
26216
  return createMemoryVaultTool(
26064
26217
  memoryStore ?? vaultCtx,
@@ -26069,6 +26222,7 @@ function useChatStorage(options) {
26069
26222
  );
26070
26223
  const createRecallTool2 = useCallback4(
26071
26224
  (toolOptions, callbacks) => {
26225
+ if (memoryStore === null) throw new Error(MEMORY_STORE_NOT_READY);
26072
26226
  if (!getToken && !memoryStore) {
26073
26227
  throw new Error("getToken is required for recall tool");
26074
26228
  }
@@ -26114,7 +26268,7 @@ function useChatStorage(options) {
26114
26268
  );
26115
26269
  const recallFn = useCallback4(
26116
26270
  async (query, options2) => {
26117
- if (!getToken && !memoryStore) {
26271
+ if (memoryStore === null || !getToken && !memoryStore) {
26118
26272
  return {
26119
26273
  memories: [],
26120
26274
  usedBudget: options2?.budget ?? "low",
@@ -26163,6 +26317,7 @@ function useChatStorage(options) {
26163
26317
  );
26164
26318
  const getVaultMemories = useCallback4(
26165
26319
  (options2) => {
26320
+ if (memoryStore === null) return Promise.resolve([]);
26166
26321
  if (!memoryStore) return getAllVaultMemoriesOp(vaultCtx, options2);
26167
26322
  const { folderId, levels, kinds, ...listOptions } = options2 ?? {};
26168
26323
  if (folderId !== void 0) throw new Error(NO_STORE_FOLDERS);
@@ -26173,7 +26328,8 @@ function useChatStorage(options) {
26173
26328
  [vaultCtx, memoryStore]
26174
26329
  );
26175
26330
  const deleteVaultMemory = useCallback4(
26176
- (id) => {
26331
+ async (id) => {
26332
+ if (memoryStore === null) throw new Error(MEMORY_STORE_NOT_READY);
26177
26333
  return memoryStore ? memoryStore.delete(id) : deleteVaultMemoryOp(vaultCtx, id);
26178
26334
  },
26179
26335
  [vaultCtx, memoryStore]