@anuma/sdk 7.0.3 → 7.0.4-next.20261015012621

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.
@@ -16574,6 +16574,10 @@ __export(index_exports, {
16574
16574
  });
16575
16575
  module.exports = __toCommonJS(index_exports);
16576
16576
 
16577
+ // src/lib/chat/toolResultMessage.ts
16578
+ var FOOTER = "\n\nBased on these results, continue with the task.";
16579
+ var TOOL_RESULT_FOOTER_LINE = FOOTER.trim();
16580
+
16577
16581
  // src/lib/tools/toolCatalog.ts
16578
16582
  var TOOL_CATALOG = {
16579
16583
  gmail_search_messages: { label: "Search emails", provider: "gmail", connector: "Gmail" },
@@ -16750,7 +16754,32 @@ ${neutralised}
16750
16754
  }
16751
16755
 
16752
16756
  // src/lib/chat/toolResults.ts
16757
+ var TOOL_RESULTS_PREFIX = "[Tool Execution Results]";
16758
+ var TOOL_LINE = /^Tool "([^"]+)" returned: (.*)$/;
16753
16759
  var TOOL_RESULT_ORIGIN = "tool_result";
16760
+ function isToolResultsRow(row) {
16761
+ if (row.origin === TOOL_RESULT_ORIGIN) return true;
16762
+ if (typeof row.origin === "string") return false;
16763
+ if (row.role !== "user" || !row.content.startsWith(TOOL_RESULTS_PREFIX)) return false;
16764
+ return parseToolResultSegments(row.content).length > 0;
16765
+ }
16766
+ function parseToolResultSegments(content) {
16767
+ const segments = [];
16768
+ for (const raw of content.split("\n")) {
16769
+ const line = raw.trim();
16770
+ const match = TOOL_LINE.exec(line);
16771
+ if (match) {
16772
+ segments.push({ name: match[1], line });
16773
+ continue;
16774
+ }
16775
+ const current = segments[segments.length - 1];
16776
+ if (!current || line === "" || line === TOOL_RESULT_FOOTER_LINE) continue;
16777
+ if (line === TOOL_RESULTS_PREFIX) continue;
16778
+ current.line = `${current.line}
16779
+ ${line}`;
16780
+ }
16781
+ return segments;
16782
+ }
16754
16783
 
16755
16784
  // src/lib/db/chat/operations.ts
16756
16785
  var import_watermelondb2 = __toESM(require_watermelondb());
@@ -18373,8 +18402,9 @@ async function getConversationsByProjectOp(ctx, projectId) {
18373
18402
  )
18374
18403
  );
18375
18404
  }
18376
- async function getMessagesOp(ctx, convId) {
18377
- const results = await ctx.messagesCollection.query(import_watermelondb2.Q.where("conversation_id", convId), import_watermelondb2.Q.sortBy("message_id", import_watermelondb2.Q.asc)).unsafeFetchRaw();
18405
+ async function getMessagesOp(ctx, convId, opts) {
18406
+ const fetched = await ctx.messagesCollection.query(import_watermelondb2.Q.where("conversation_id", convId), import_watermelondb2.Q.sortBy("message_id", import_watermelondb2.Q.asc)).unsafeFetchRaw();
18407
+ const results = opts?.rawFilter ? fetched.filter(opts.rawFilter) : fetched;
18378
18408
  return Promise.all(
18379
18409
  results.map(
18380
18410
  (raw) => messageRawToStored(raw, ctx.walletAddress, ctx.signMessage, ctx.embeddedWalletSigner)
@@ -20108,6 +20138,24 @@ var postApiV1Embeddings = (options) => (options.client ?? client).post({
20108
20138
  // src/lib/memoryEngine/generate.ts
20109
20139
  init_clientConfig();
20110
20140
  init_taskType();
20141
+
20142
+ // src/lib/memoryEngine/embeddingCacheState.ts
20143
+ var EMBEDDING_CACHE_STATE_KEY = /* @__PURE__ */ Symbol.for("@anuma/sdk/embedding-cache/v1");
20144
+ var cacheGlobal = globalThis;
20145
+ var fallbackState = {
20146
+ scopedViews: /* @__PURE__ */ new WeakMap(),
20147
+ inFlightByCache: /* @__PURE__ */ new WeakMap(),
20148
+ cachedEmbeddingModels: /* @__PURE__ */ new WeakMap()
20149
+ };
20150
+ function getEmbeddingCacheState() {
20151
+ try {
20152
+ return cacheGlobal[EMBEDDING_CACHE_STATE_KEY] ?? (cacheGlobal[EMBEDDING_CACHE_STATE_KEY] = fallbackState);
20153
+ } catch {
20154
+ return fallbackState;
20155
+ }
20156
+ }
20157
+
20158
+ // src/lib/memoryEngine/generate.ts
20111
20159
  var EMBED_MAX_ATTEMPTS = 4;
20112
20160
  var DEFAULT_EMBEDDING_REQUEST_TIMEOUT_MS = 15e3;
20113
20161
  var DEFAULT_EMBEDDING_TOKEN_TIMEOUT_MS = 1e4;
@@ -20198,15 +20246,18 @@ async function generateEmbedding(text12, options) {
20198
20246
  if (total === void 0) return embedOne(text12, options, void 0);
20199
20247
  return withDeadline((signal) => embedOne(text12, options, signal), total, "embedding");
20200
20248
  }
20201
- var inFlightByCache = /* @__PURE__ */ new WeakMap();
20249
+ var { inFlightByCache, cachedEmbeddingModels } = getEmbeddingCacheState();
20202
20250
  async function embedOne(text12, options, outer) {
20203
20251
  const { cache } = options;
20204
20252
  if (!cache) return Array.from(await requestEmbedding(text12, options, outer));
20253
+ const model = options.model ?? DEFAULT_API_EMBEDDING_MODEL;
20205
20254
  const cached = cache.get(text12);
20206
- if (cached) return Array.from(cached);
20255
+ const cachedModel = cached && cachedEmbeddingModels.get(cached);
20256
+ if (cached && (cachedModel === void 0 || cachedModel === model)) return Array.from(cached);
20207
20257
  const inFlight = inFlightByCache.get(cache) ?? /* @__PURE__ */ new Map();
20208
20258
  inFlightByCache.set(cache, inFlight);
20209
- const pending = inFlight.get(text12);
20259
+ const requestKey = JSON.stringify([model, text12]);
20260
+ const pending = inFlight.get(requestKey);
20210
20261
  if (pending) {
20211
20262
  try {
20212
20263
  return Array.from(await pending);
@@ -20215,13 +20266,14 @@ async function embedOne(text12, options, outer) {
20215
20266
  }
20216
20267
  }
20217
20268
  const request = requestEmbedding(text12, options, outer);
20218
- inFlight.set(text12, request);
20269
+ inFlight.set(requestKey, request);
20219
20270
  try {
20220
20271
  const embedding = await request;
20272
+ cachedEmbeddingModels.set(embedding, model);
20221
20273
  cache.set(text12, embedding);
20222
20274
  return Array.from(embedding);
20223
20275
  } finally {
20224
- if (inFlight.get(text12) === request) inFlight.delete(text12);
20276
+ if (inFlight.get(requestKey) === request) inFlight.delete(requestKey);
20225
20277
  }
20226
20278
  }
20227
20279
  async function requestEmbedding(text12, options, outer) {
@@ -20304,13 +20356,15 @@ async function embedMany(texts, options, outer) {
20304
20356
  const { baseUrl = BASE_URL, model, batchSize, cache } = options;
20305
20357
  const timeoutMs = options.timeoutMs ?? DEFAULT_EMBEDDING_REQUEST_TIMEOUT_MS;
20306
20358
  const chunkSize = batchSize ?? DEFAULT_EMBEDDING_BATCH_SIZE;
20359
+ const embeddingModel = model ?? DEFAULT_API_EMBEDDING_MODEL;
20307
20360
  const results = new Array(texts.length).fill(null);
20308
20361
  const uncachedIndices = [];
20309
20362
  const uncachedTexts = [];
20310
20363
  for (let i = 0; i < texts.length; i++) {
20311
20364
  if (cache) {
20312
20365
  const cached = cache.get(texts[i]);
20313
- if (cached) {
20366
+ const cachedModel = cached && cachedEmbeddingModels.get(cached);
20367
+ if (cached && (cachedModel === void 0 || cachedModel === embeddingModel)) {
20314
20368
  results[i] = Array.from(cached);
20315
20369
  continue;
20316
20370
  }
@@ -20326,7 +20380,6 @@ async function embedMany(texts, options, outer) {
20326
20380
  ...taskTypeHeader(options.taskType)
20327
20381
  };
20328
20382
  if (outer?.aborted) throw outer.reason;
20329
- const embeddingModel = model ?? DEFAULT_API_EMBEDDING_MODEL;
20330
20383
  let newEmbeddings;
20331
20384
  if (uncachedTexts.length <= chunkSize) {
20332
20385
  newEmbeddings = await generateEmbeddingsBatch(
@@ -20374,6 +20427,7 @@ async function embedMany(texts, options, outer) {
20374
20427
  const f32Embedding = Float32Array.from(newEmbeddings[i]);
20375
20428
  results[uncachedIndices[i]] = Array.from(f32Embedding);
20376
20429
  if (cache) {
20430
+ cachedEmbeddingModels.set(f32Embedding, embeddingModel);
20377
20431
  cache.set(uncachedTexts[i], f32Embedding);
20378
20432
  }
20379
20433
  }
@@ -20498,6 +20552,37 @@ async function chunkAndEmbedMessage(ctx, messageId, options) {
20498
20552
  }));
20499
20553
  return updateMessageChunksOp(ctx, messageId, messageChunks, embeddingModel);
20500
20554
  }
20555
+ var MIN_SEALED_EMBEDDING_HEX = 256;
20556
+ function rawHasEmbedding(value) {
20557
+ if (Array.isArray(value)) return value.length > 0;
20558
+ if (typeof value !== "string" || value.length === 0) return false;
20559
+ if (isEncrypted(value)) {
20560
+ return value.length - value.indexOf(":", 4) - 1 >= MIN_SEALED_EMBEDDING_HEX;
20561
+ }
20562
+ try {
20563
+ const parsed = JSON.parse(value);
20564
+ return Array.isArray(parsed) && parsed.length > 0;
20565
+ } catch {
20566
+ return false;
20567
+ }
20568
+ }
20569
+ function sealedVersion(value) {
20570
+ if (typeof value !== "string" || !isEncrypted(value)) return null;
20571
+ return value.startsWith("enc:v2:") ? "v2" : "v3";
20572
+ }
20573
+ function mayNeedChunkEmbedding(raw, embeddingModel, filter) {
20574
+ const role = raw.role;
20575
+ if (role === "system") return false;
20576
+ if (filter?.roles && !filter.roles.includes(role ?? "")) return false;
20577
+ if (isNonEmbeddableOrigin(raw.origin, filter?.reembedDiscarded)) {
20578
+ return false;
20579
+ }
20580
+ const model = raw.embedding_model;
20581
+ if (typeof model === "string" && model !== embeddingModel) return true;
20582
+ if (rawHasEmbedding(raw.chunks)) return false;
20583
+ if (rawHasEmbedding(raw.vector) && !filter?.rechunkExisting) return false;
20584
+ return true;
20585
+ }
20501
20586
  async function chunkAndEmbedAllMessages(ctx, options, filter) {
20502
20587
  const embeddingModel = options.model ?? DEFAULT_API_EMBEDDING_MODEL;
20503
20588
  const { chunkSize = DEFAULT_CHUNK_SIZE } = options;
@@ -20510,11 +20595,31 @@ async function chunkAndEmbedAllMessages(ctx, options, filter) {
20510
20595
  let considered = 0;
20511
20596
  let sealedRowsSeen = 0;
20512
20597
  let rowsSeen = 0;
20598
+ const keptSealed = /* @__PURE__ */ new Map();
20599
+ const candidatesStillSealed = /* @__PURE__ */ new Map();
20600
+ const droppedSealed = /* @__PURE__ */ new Map();
20601
+ const rawFilter = (raw) => {
20602
+ rowsSeen++;
20603
+ const keep = mayNeedChunkEmbedding(raw, embeddingModel, filter);
20604
+ const version = sealedVersion(raw.content);
20605
+ if (!version) return keep;
20606
+ if (keep) {
20607
+ keptSealed.set(version, (keptSealed.get(version) ?? 0) + 1);
20608
+ } else {
20609
+ const dropped = droppedSealed.get(version);
20610
+ if (dropped) dropped.count++;
20611
+ else droppedSealed.set(version, { count: 1, probeId: raw.id });
20612
+ }
20613
+ return keep;
20614
+ };
20513
20615
  for (const conv of targetConversations) {
20514
- const messages = await getMessagesOp(ctx, conv.conversationId);
20616
+ const messages = await getMessagesOp(ctx, conv.conversationId, { rawFilter });
20515
20617
  for (const message of messages) {
20516
- rowsSeen++;
20517
- if (isEncrypted(message.content)) sealedRowsSeen++;
20618
+ const stillSealed = sealedVersion(message.content);
20619
+ if (stillSealed) {
20620
+ sealedRowsSeen++;
20621
+ candidatesStillSealed.set(stillSealed, (candidatesStillSealed.get(stillSealed) ?? 0) + 1);
20622
+ }
20518
20623
  const isStale = message.embeddingModel !== void 0 && message.embeddingModel !== null && message.embeddingModel !== embeddingModel;
20519
20624
  if (message.chunks && message.chunks.length > 0 && !isStale) continue;
20520
20625
  const hasVector = message.vector && message.vector.length > 0;
@@ -20527,6 +20632,7 @@ async function chunkAndEmbedAllMessages(ctx, options, filter) {
20527
20632
  stillEncrypted++;
20528
20633
  continue;
20529
20634
  }
20635
+ if (isToolResultsRow(message)) continue;
20530
20636
  if (message.content.length < minLength) continue;
20531
20637
  if (shouldChunkMessage(message.content, chunkSize)) {
20532
20638
  longMessages.push({
@@ -20538,6 +20644,12 @@ async function chunkAndEmbedAllMessages(ctx, options, filter) {
20538
20644
  }
20539
20645
  }
20540
20646
  }
20647
+ for (const [version, dropped] of droppedSealed) {
20648
+ const keyOpenedACandidate = (keptSealed.get(version) ?? 0) > (candidatesStillSealed.get(version) ?? 0);
20649
+ if (keyOpenedACandidate) continue;
20650
+ const probe = await getMessageOp(ctx, dropped.probeId);
20651
+ if (!probe || isEncrypted(probe.content)) sealedRowsSeen += dropped.count;
20652
+ }
20541
20653
  if (stillEncrypted > 0 || sealedRowsSeen > 0) {
20542
20654
  getLogger().error(
20543
20655
  "memoryEngine: messages still encrypted (key unavailable?) \u2014 excluded from embedding",
@@ -27712,6 +27824,11 @@ function parseTopics(value) {
27712
27824
  }
27713
27825
 
27714
27826
  // src/lib/db/entities/operations.ts
27827
+ async function assertCanWrite(ctx) {
27828
+ if (ctx.canWrite && !await ctx.canWrite()) {
27829
+ throw new Error("Memory source is no longer eligible");
27830
+ }
27831
+ }
27715
27832
  function entityToStored(e) {
27716
27833
  return {
27717
27834
  uniqueId: e.id,
@@ -27852,6 +27969,7 @@ async function linkMemoryEntitiesOp(ctx, memoryId, entityInputs, options) {
27852
27969
  let skipped = false;
27853
27970
  let entities = [];
27854
27971
  await ctx.database.write(async () => {
27972
+ await assertCanWrite(ctx);
27855
27973
  const {
27856
27974
  entities: byName,
27857
27975
  operations: entityOps,
@@ -27927,6 +28045,7 @@ async function replaceMemoryEntities(ctx, memoryId, entityInputs, options) {
27927
28045
  let skipped = false;
27928
28046
  let entities = [];
27929
28047
  await ctx.database.write(async () => {
28048
+ await assertCanWrite(ctx);
27930
28049
  const vaultRow = await findVaultRowInWrite(ctx, memoryId);
27931
28050
  if (options.guarded && autoLinkBlocked(vaultRow)) {
27932
28051
  skipped = true;
@@ -27987,6 +28106,7 @@ async function unlinkMemoryEntitiesOp(ctx, memoryIds) {
27987
28106
  const links = await ctx.memoryEntityCollection.query(import_watermelondb18.Q.where("memory_id", import_watermelondb18.Q.oneOf(Array.from(memoryIds)))).fetch();
27988
28107
  if (links.length === 0) return;
27989
28108
  await ctx.database.write(async () => {
28109
+ await assertCanWrite(ctx);
27990
28110
  await ctx.database.batch(...links.map((l) => l.prepareDestroyPermanently()));
27991
28111
  });
27992
28112
  }
@@ -27995,6 +28115,7 @@ async function unlinkAllMemoryEntitiesForUserOp(ctx, userId) {
27995
28115
  const links = await ctx.memoryEntityCollection.query(import_watermelondb18.Q.where("user_id", userId)).fetch();
27996
28116
  if (links.length === 0) return;
27997
28117
  await ctx.database.write(async () => {
28118
+ await assertCanWrite(ctx);
27998
28119
  await ctx.database.batch(...links.map((l) => l.prepareDestroyPermanently()));
27999
28120
  });
28000
28121
  }
@@ -28159,6 +28280,11 @@ function visibilityConditions(requested) {
28159
28280
  function isKinded(record) {
28160
28281
  return record.kind !== null && record.kind !== void 0;
28161
28282
  }
28283
+ async function assertCanWrite2(ctx) {
28284
+ if (ctx.canWrite && !await ctx.canWrite()) {
28285
+ throw new Error("Memory source is no longer eligible");
28286
+ }
28287
+ }
28162
28288
  function assertValidKindLevel(kind, level) {
28163
28289
  const hasKind = kind !== null && kind !== void 0;
28164
28290
  if (hasKind && !MEMORY_KINDS.includes(kind)) {
@@ -28339,8 +28465,7 @@ async function createVaultMemoryOp(ctx, opts) {
28339
28465
  ctx.embeddedWalletSigner
28340
28466
  ) : opts.content;
28341
28467
  const created = await ctx.database.write(async () => {
28342
- if (ctx.canWrite && !await ctx.canWrite())
28343
- throw new Error("Memory source is no longer eligible");
28468
+ await assertCanWrite2(ctx);
28344
28469
  return ctx.vaultMemoryCollection.create(
28345
28470
  (record) => populateNewVaultMemory(record, ctx, opts, encryptedContent, encryptedKindValue)
28346
28471
  );
@@ -28359,8 +28484,7 @@ async function createSupersedingMemoryOp(ctx, opts, targetId) {
28359
28484
  ) : opts.content;
28360
28485
  let createdRecord = null;
28361
28486
  await ctx.database.write(async () => {
28362
- if (ctx.canWrite && !await ctx.canWrite())
28363
- throw new Error("Memory source is no longer eligible");
28487
+ await assertCanWrite2(ctx);
28364
28488
  let target;
28365
28489
  try {
28366
28490
  target = await ctx.vaultMemoryCollection.find(targetId);
@@ -28452,8 +28576,7 @@ async function createVaultMemoriesBatchOp(ctx, optionsArray) {
28452
28576
  })
28453
28577
  );
28454
28578
  const created = await ctx.database.write(async () => {
28455
- if (ctx.canWrite && !await ctx.canWrite())
28456
- throw new Error("Memory source is no longer eligible");
28579
+ await assertCanWrite2(ctx);
28457
28580
  const prepared = optionsArray.map(
28458
28581
  (opts, i) => ctx.vaultMemoryCollection.prepareCreate(
28459
28582
  (record) => populateNewVaultMemory(record, ctx, opts, encryptedContents[i], encryptedKindValues[i])
@@ -28744,8 +28867,7 @@ async function updateVaultMemoryOp(ctx, id, opts) {
28744
28867
  const record = probe;
28745
28868
  const originalUpdatedAt = record.updatedAt.getTime();
28746
28869
  await ctx.database.write(async () => {
28747
- if (ctx.canWrite && !await ctx.canWrite())
28748
- throw new Error("Memory source is no longer eligible");
28870
+ await assertCanWrite2(ctx);
28749
28871
  if (record.isDeleted || record.supersededBy || !isOwnedByCtxUser(ctx, record) || opts.freeFormOnly && isKinded(record)) {
28750
28872
  stale = true;
28751
28873
  return;
@@ -28856,6 +28978,7 @@ async function setMemoryEntitiesOp(ctx, memoryId, entities) {
28856
28978
  let stale = false;
28857
28979
  const originalUpdatedAt = record.updatedAt.getTime();
28858
28980
  await ctx.database.write(async (writer) => {
28981
+ await assertCanWrite2(ctx);
28859
28982
  if (record.isDeleted || !isOwnedByCtxUser(ctx, record)) {
28860
28983
  stale = true;
28861
28984
  return;
@@ -28897,6 +29020,7 @@ async function clearMemoryTopicsOverrideOp(ctx, memoryId, options) {
28897
29020
  const originalUpdatedAt = record.updatedAt.getTime();
28898
29021
  let cleared = false;
28899
29022
  await ctx.database.write(async () => {
29023
+ await assertCanWrite2(ctx);
28900
29024
  if (options?.unlessTopicsRecorded && parseTopics(record.topics) !== null) return;
28901
29025
  cleared = true;
28902
29026
  await record.update((r) => {
@@ -28920,6 +29044,7 @@ async function setMemoryVisibilityOp(ctx, id, opts) {
28920
29044
  if (record.isDeleted || !isOwnedByCtxUser(ctx, record)) return null;
28921
29045
  let stale = false;
28922
29046
  await ctx.database.write(async () => {
29047
+ await assertCanWrite2(ctx);
28923
29048
  if (record.isDeleted || !isOwnedByCtxUser(ctx, record)) {
28924
29049
  stale = true;
28925
29050
  return;
@@ -28948,6 +29073,7 @@ async function deleteVaultMemoryOp(ctx, id) {
28948
29073
  if (record.isDeleted || !isOwnedByCtxUser(ctx, record)) return false;
28949
29074
  let stale = false;
28950
29075
  await ctx.database.write(async () => {
29076
+ await assertCanWrite2(ctx);
28951
29077
  if (record.isDeleted || !isOwnedByCtxUser(ctx, record)) {
28952
29078
  stale = true;
28953
29079
  return;
@@ -28985,8 +29111,7 @@ async function supersedeVaultMemoryOp(ctx, id, supersededById) {
28985
29111
  }
28986
29112
  let stale = false;
28987
29113
  await ctx.database.write(async () => {
28988
- if (ctx.canWrite && !await ctx.canWrite())
28989
- throw new Error("Memory source is no longer eligible");
29114
+ await assertCanWrite2(ctx);
28990
29115
  if (record.isDeleted || record.supersededBy || !isOwnedByCtxUser(ctx, record)) {
28991
29116
  stale = true;
28992
29117
  return;
@@ -29148,6 +29273,7 @@ async function stampTopicsExtractedAtOp(ctx, memoryIds, extractedAt, version = T
29148
29273
  for (let i = 0; i < uniqueIds.length; i += CHUNK) {
29149
29274
  const chunkIds = uniqueIds.slice(i, i + CHUNK);
29150
29275
  await ctx.database.write(async () => {
29276
+ await assertCanWrite2(ctx);
29151
29277
  const records = [];
29152
29278
  for (const id of chunkIds) {
29153
29279
  try {
@@ -29181,6 +29307,7 @@ async function relinkMemoryTopicsOp(ctx, memoryIds) {
29181
29307
  for (const id of Array.from(new Set(memoryIds))) {
29182
29308
  try {
29183
29309
  await ctx.database.write(async (writer) => {
29310
+ await assertCanWrite2(ctx);
29184
29311
  let record;
29185
29312
  try {
29186
29313
  record = await ctx.vaultMemoryCollection.find(id);
@@ -29211,6 +29338,7 @@ async function backfillMemoryTopicsOp(ctx, memoryIds) {
29211
29338
  for (let i = 0; i < uniqueIds.length; i += CHUNK) {
29212
29339
  const chunkIds = uniqueIds.slice(i, i + CHUNK);
29213
29340
  await ctx.database.write(async () => {
29341
+ await assertCanWrite2(ctx);
29214
29342
  const writes = [];
29215
29343
  for (const id of chunkIds) {
29216
29344
  let record;
@@ -29243,6 +29371,7 @@ async function deleteAllVaultMemoriesForUserOp(ctx, userId) {
29243
29371
  const records = await ctx.vaultMemoryCollection.query(import_watermelondb19.Q.where("user_id", userId), import_watermelondb19.Q.where("is_deleted", false)).fetch();
29244
29372
  if (records.length === 0) return 0;
29245
29373
  await ctx.database.write(async () => {
29374
+ await assertCanWrite2(ctx);
29246
29375
  const prepared = records.map(
29247
29376
  (record) => record.prepareUpdate((r) => {
29248
29377
  r._setRaw("is_deleted", true);
@@ -29297,6 +29426,7 @@ async function archiveVaultMemoryOp(ctx, id, opts) {
29297
29426
  let stale = false;
29298
29427
  const archivedAtValue = opts?.now ?? Date.now();
29299
29428
  await ctx.database.write(async () => {
29429
+ await assertCanWrite2(ctx);
29300
29430
  if (record.isDeleted || !isOwnedByCtxUser(ctx, record) || record.archivedAt !== null) {
29301
29431
  stale = true;
29302
29432
  return;
@@ -29320,6 +29450,7 @@ async function restoreVaultMemoryOp(ctx, id) {
29320
29450
  if (record.isDeleted || !isOwnedByCtxUser(ctx, record)) return false;
29321
29451
  let stale = false;
29322
29452
  await ctx.database.write(async () => {
29453
+ await assertCanWrite2(ctx);
29323
29454
  if (record.isDeleted || !isOwnedByCtxUser(ctx, record)) {
29324
29455
  stale = true;
29325
29456
  return;
@@ -29340,6 +29471,7 @@ async function hardDeleteDecayedOp(ctx, id, opts) {
29340
29471
  const now = opts.now ?? Date.now();
29341
29472
  let stale = false;
29342
29473
  await ctx.database.write(async () => {
29474
+ await assertCanWrite2(ctx);
29343
29475
  const archivedAt = record.archivedAt;
29344
29476
  if (record.isDeleted || !isOwnedByCtxUser(ctx, record) || archivedAt === null || now - archivedAt <= opts.hardDeleteWindowMs) {
29345
29477
  stale = true;
@@ -29379,6 +29511,7 @@ async function updateVaultMemoryEmbeddingOp(ctx, id, embedding, embeddingModel,
29379
29511
  }
29380
29512
  let written = false;
29381
29513
  await ctx.database.write(async () => {
29514
+ await assertCanWrite2(ctx);
29382
29515
  if (record.isDeleted || !isOwnedByCtxUser(ctx, record)) return;
29383
29516
  const originalUpdatedAt = record.updatedAt.getTime();
29384
29517
  if (originalUpdatedAt !== readUpdatedAt || record._getRaw("content") !== readStoredContent)
@@ -33071,6 +33204,13 @@ function restoreCandidates(candidates, redactor, ownNames) {
33071
33204
  ).map((c) => c.candidate);
33072
33205
  }
33073
33206
  async function extractAndRetain(messages, retainCtx, options) {
33207
+ const { memoryStore, signal } = options;
33208
+ if (memoryStore === null) throw new Error("Memory store is not ready");
33209
+ if (memoryStore && options.folderId !== void 0 && options.folderId !== null)
33210
+ throw new Error("Remote memory extraction does not support folders");
33211
+ if (memoryStore && options.extract.piiRedaction && !memoryStore.masksEmbeddingInput)
33212
+ throw new Error("PII-redacted extraction requires a memory store that masks embedding inputs");
33213
+ signal?.throwIfAborted();
33074
33214
  const minConfidence = options.minConfidence ?? DEFAULT_MIN_CONFIDENCE;
33075
33215
  const resolvedConsolidatePii = options.consolidateOptions?.piiRedaction ?? options.extract.piiRedaction;
33076
33216
  const consolidateOptions = options.consolidateOptions !== void 0 ? {
@@ -33104,6 +33244,7 @@ async function extractAndRetain(messages, retainCtx, options) {
33104
33244
  }
33105
33245
  });
33106
33246
  const extractMs = Date.now() - tExtract;
33247
+ signal?.throwIfAborted();
33107
33248
  const filtered = candidates.filter((c) => c.confidence >= minConfidence);
33108
33249
  const log = getLogger();
33109
33250
  const screened = screenCandidatesForInjection(filtered);
@@ -33151,16 +33292,33 @@ async function extractAndRetain(messages, retainCtx, options) {
33151
33292
  const results = [];
33152
33293
  const quarantinedInfo = [];
33153
33294
  let failedWrites = 0;
33295
+ let auditRows;
33154
33296
  const tRetain = Date.now();
33155
33297
  for (const { candidate, isQuarantined, reason, signature } of toRetain) {
33156
33298
  try {
33299
+ signal?.throwIfAborted();
33157
33300
  if (isQuarantined) {
33158
- const existingId = await findQuarantinedDuplicateOp(
33159
- retainCtx.vaultCtx,
33160
- candidate.content,
33161
- candidate.sourceMessageIds,
33162
- { scope: options.scope ?? "private", folderId: options.folderId ?? null }
33163
- ).catch(() => null);
33301
+ let existingId;
33302
+ if (memoryStore) {
33303
+ auditRows ?? (auditRows = memoryStore.list({
33304
+ scopes: [options.scope ?? "private"],
33305
+ includeArchived: true,
33306
+ includeQuarantined: true,
33307
+ includeSuperseded: true
33308
+ }));
33309
+ const sourceKey = [...new Set(candidate.sourceMessageIds)].sort().join("\0");
33310
+ const normalize = (value) => value.normalize("NFKC").trim().replace(/\s+/g, " ").toLowerCase();
33311
+ existingId = (await auditRows).find(
33312
+ (row) => row.trustTier === "quarantined" && row.scope === (options.scope ?? "private") && [...new Set(row.sourceChunkIds ?? [])].sort().join("\0") === sourceKey && normalize(row.content) === normalize(candidate.content)
33313
+ )?.uniqueId ?? null;
33314
+ } else {
33315
+ existingId = await findQuarantinedDuplicateOp(
33316
+ retainCtx.vaultCtx,
33317
+ candidate.content,
33318
+ candidate.sourceMessageIds,
33319
+ { scope: options.scope ?? "private", folderId: options.folderId ?? null }
33320
+ ).catch(() => null);
33321
+ }
33164
33322
  if (existingId) {
33165
33323
  quarantinedInfo.push({
33166
33324
  candidate,
@@ -33171,18 +33329,26 @@ async function extractAndRetain(messages, retainCtx, options) {
33171
33329
  continue;
33172
33330
  }
33173
33331
  }
33174
- const result = await retain(candidate.content, retainCtx, {
33332
+ const retainOptions = {
33175
33333
  source: "auto-extracted",
33176
33334
  sourceChunkIds: candidate.sourceMessageIds,
33177
33335
  respectTombstones: true,
33178
33336
  ...options.scope !== void 0 && { scope: options.scope },
33179
- ...options.folderId !== void 0 && { folderId: options.folderId },
33180
33337
  ...consolidateOptions !== void 0 && { consolidateOptions },
33181
33338
  ...candidate.eventTime !== null && { eventTime: candidate.eventTime },
33182
33339
  factType: candidate.type,
33183
33340
  ...isQuarantined && { trustTier: "quarantined", enableAutoMerge: false }
33341
+ };
33342
+ const result = memoryStore ? await memoryStore.retain(candidate.content, {
33343
+ ...retainOptions,
33344
+ canWrite: retainCtx.vaultCtx.canWrite,
33345
+ signal
33346
+ }) : await retain(candidate.content, retainCtx, {
33347
+ ...retainOptions,
33348
+ ...options.folderId !== void 0 && { folderId: options.folderId }
33184
33349
  });
33185
33350
  if (isQuarantined) {
33351
+ auditRows = void 0;
33186
33352
  const info = {
33187
33353
  candidate,
33188
33354
  memoryId: result.memoryId,
@@ -33201,9 +33367,16 @@ async function extractAndRetain(messages, retainCtx, options) {
33201
33367
  }
33202
33368
  succeededCandidates.push(candidate);
33203
33369
  results.push(result);
33204
- if (result.action !== "suppressed" && options.entityCtx && candidate.entities.length > 0) {
33370
+ if (result.action !== "suppressed" && candidate.entities.length > 0) {
33205
33371
  try {
33206
- if (!await isMemoryTopicsUserManaged(options.entityCtx, result.memoryId)) {
33372
+ signal?.throwIfAborted();
33373
+ if (memoryStore) {
33374
+ await memoryStore.addTopics(result.memoryId, candidate.entities, {
33375
+ unlessUserManaged: true,
33376
+ canWrite: retainCtx.vaultCtx.canWrite,
33377
+ signal
33378
+ });
33379
+ } else if (options.entityCtx && !await isMemoryTopicsUserManaged(options.entityCtx, result.memoryId)) {
33207
33380
  await linkMemoryEntitiesOp(options.entityCtx, result.memoryId, candidate.entities, {
33208
33381
  unlessTopicsUserManaged: true
33209
33382
  });
@@ -33453,7 +33626,7 @@ function createAutoExtractor(options) {
33453
33626
  };
33454
33627
  const consolidateOptions = options.consolidate ? { ...options.consolidate, ...subPassAuth(options.consolidate.baseUrl) } : void 0;
33455
33628
  const injectionClassifierOptions = options.injectionClassifier ? { ...options.injectionClassifier, ...subPassAuth(options.injectionClassifier.baseUrl) } : void 0;
33456
- if (options.retainCtx.vaultCtx.entityCtx && !options.entityCtx) {
33629
+ if (!options.memoryStore && options.retainCtx.vaultCtx.entityCtx && !options.entityCtx) {
33457
33630
  getLogger().warn(
33458
33631
  "[memory/extract] retainCtx.vaultCtx.entityCtx is set but extractor was created without `entityCtx` \u2014 W5 graph lane will receive no writes"
33459
33632
  );
@@ -33498,6 +33671,8 @@ function createAutoExtractor(options) {
33498
33671
  try {
33499
33672
  const { candidates, results, failedCount, outcome, failure, funnel, timings, model } = await extractAndRetain(window2, retainCtx, {
33500
33673
  extract,
33674
+ memoryStore: options.memoryStore,
33675
+ signal: options.signal,
33501
33676
  ...options.minConfidence !== void 0 && { minConfidence: options.minConfidence },
33502
33677
  ...options.entityCtx !== void 0 && { entityCtx: options.entityCtx },
33503
33678
  ...options.scope !== void 0 && { scope: options.scope },
@@ -34158,7 +34333,7 @@ var MaskScopedEmbeddingCache = class extends Map {
34158
34333
  return this;
34159
34334
  }
34160
34335
  };
34161
- var scopedViews = /* @__PURE__ */ new WeakMap();
34336
+ var { scopedViews } = getEmbeddingCacheState();
34162
34337
  function maskScopedEmbeddingCache(cache, masked) {
34163
34338
  const views = scopedViews.get(cache) ?? {};
34164
34339
  scopedViews.set(cache, views);
@@ -34583,44 +34758,48 @@ async function recall(query, ctx, options = {}) {
34583
34758
  embeddingOptions,
34584
34759
  ctx.vaultCache,
34585
34760
  searchOptions
34586
- )))(query, {
34587
- limit: types.includes("chunk") ? Math.max(limit * 2, 16) : limit,
34588
- minSimilarity: vaultMinScore,
34589
- useFusion: true,
34590
- rerank: flags.rerank,
34591
- ...options.rerankTopN !== void 0 && { rerankTopN: options.rerankTopN },
34592
- ...options.ceWeight !== void 0 && { ceWeight: options.ceWeight },
34593
- ...options.rerankLoadTimeoutMs !== void 0 && {
34594
- rerankLoadTimeoutMs: options.rerankLoadTimeoutMs
34595
- },
34596
- ...options.recencyAlpha !== void 0 && { recencyAlpha: options.recencyAlpha },
34597
- ...options.recency && { recency: options.recency },
34598
- ...options.mmr !== void 0 && { mmr: options.mmr },
34599
- ...options.supersessionBoost !== void 0 && {
34600
- supersessionBoost: options.supersessionBoost
34601
- },
34602
- ...options.supersessionWindow !== void 0 && {
34603
- supersessionWindow: options.supersessionWindow
34604
- },
34605
- ...options.proofCountAlpha !== void 0 && {
34606
- proofCountAlpha: options.proofCountAlpha
34607
- },
34608
- ...options.bm25AdmissionDivisor !== void 0 && {
34609
- bm25AdmissionDivisor: options.bm25AdmissionDivisor
34761
+ )))(
34762
+ query,
34763
+ {
34764
+ limit: types.includes("chunk") ? Math.max(limit * 2, 16) : limit,
34765
+ minSimilarity: vaultMinScore,
34766
+ useFusion: true,
34767
+ rerank: flags.rerank,
34768
+ ...options.rerankTopN !== void 0 && { rerankTopN: options.rerankTopN },
34769
+ ...options.ceWeight !== void 0 && { ceWeight: options.ceWeight },
34770
+ ...options.rerankLoadTimeoutMs !== void 0 && {
34771
+ rerankLoadTimeoutMs: options.rerankLoadTimeoutMs
34772
+ },
34773
+ ...options.recencyAlpha !== void 0 && { recencyAlpha: options.recencyAlpha },
34774
+ ...options.recency && { recency: options.recency },
34775
+ ...options.mmr !== void 0 && { mmr: options.mmr },
34776
+ ...options.supersessionBoost !== void 0 && {
34777
+ supersessionBoost: options.supersessionBoost
34778
+ },
34779
+ ...options.supersessionWindow !== void 0 && {
34780
+ supersessionWindow: options.supersessionWindow
34781
+ },
34782
+ ...options.proofCountAlpha !== void 0 && {
34783
+ proofCountAlpha: options.proofCountAlpha
34784
+ },
34785
+ ...options.bm25AdmissionDivisor !== void 0 && {
34786
+ bm25AdmissionDivisor: options.bm25AdmissionDivisor
34787
+ },
34788
+ ...options.rrfK !== void 0 && { rrfK: options.rrfK },
34789
+ ...options.decryptLast !== void 0 && { decryptLast: options.decryptLast },
34790
+ ...subQueries && { subQueries },
34791
+ ...options.scopes && { scopes: options.scopes },
34792
+ ...options.folderId !== void 0 && { folderId: options.folderId },
34793
+ ...options.factTypes?.length && { factTypes: options.factTypes },
34794
+ ...options.memoryIds !== void 0 && { memoryIds: options.memoryIds },
34795
+ ...options.factTypeWeights && { factTypeWeights: options.factTypeWeights },
34796
+ ...entityRanking.length > 0 && { entityRanking },
34797
+ ...temporalRanking.length > 0 && { temporalRanking },
34798
+ ...needsChunkEmbedding && { queryEmbedding: queryEmbedding ?? [] },
34799
+ queryEmbedTotalTimeoutMs
34610
34800
  },
34611
- ...options.rrfK !== void 0 && { rrfK: options.rrfK },
34612
- ...options.decryptLast !== void 0 && { decryptLast: options.decryptLast },
34613
- ...subQueries && { subQueries },
34614
- ...options.scopes && { scopes: options.scopes },
34615
- ...options.folderId !== void 0 && { folderId: options.folderId },
34616
- ...options.factTypes?.length && { factTypes: options.factTypes },
34617
- ...options.memoryIds !== void 0 && { memoryIds: options.memoryIds },
34618
- ...options.factTypeWeights && { factTypeWeights: options.factTypeWeights },
34619
- ...entityRanking.length > 0 && { entityRanking },
34620
- ...temporalRanking.length > 0 && { temporalRanking },
34621
- ...needsChunkEmbedding && { queryEmbedding: queryEmbedding ?? [] },
34622
- queryEmbedTotalTimeoutMs
34623
- });
34801
+ options.embeddingCache
34802
+ );
34624
34803
  factResults.push(
34625
34804
  ...dedupeBy(
34626
34805
  results,
@@ -35625,6 +35804,7 @@ function createLocalMemoryStore(options) {
35625
35804
  database,
35626
35805
  entityCollection: database.get("entity"),
35627
35806
  memoryEntityCollection: database.get("memory_entity"),
35807
+ canWrite: options.canWrite,
35628
35808
  ...userId !== void 0 && { userId },
35629
35809
  ...options.allowUnscopedRows !== void 0 && {
35630
35810
  allowUnscopedRows: options.allowUnscopedRows
@@ -35636,10 +35816,21 @@ function createLocalMemoryStore(options) {
35636
35816
  walletAddress: options.walletAddress,
35637
35817
  signMessage: options.signMessage,
35638
35818
  embeddedWalletSigner: options.embeddedWalletSigner,
35819
+ canWrite: options.canWrite,
35639
35820
  ...userId !== void 0 && { userId },
35640
35821
  ...options.singleTenant !== void 0 && { singleTenant: options.singleTenant },
35641
35822
  entityCtx
35642
35823
  };
35824
+ const assertWrite = async (writeOptions) => {
35825
+ writeOptions?.signal?.throwIfAborted();
35826
+ if (writeOptions?.canWrite && !await writeOptions.canWrite()) {
35827
+ throw new Error("Memory source is no longer eligible");
35828
+ }
35829
+ if (options.canWrite && !await options.canWrite()) {
35830
+ throw new Error("Memory source is no longer eligible");
35831
+ }
35832
+ writeOptions?.signal?.throwIfAborted();
35833
+ };
35643
35834
  const vaultCache = options.vaultCache ?? createVaultEmbeddingCache();
35644
35835
  const ownedBy = userId !== void 0 ? [import_watermelondb24.Q.where("user_id", userId)] : [];
35645
35836
  const linksOwnedBy = userId === void 0 ? [] : options.allowUnscopedRows ? [import_watermelondb24.Q.or(import_watermelondb24.Q.where("user_id", userId), import_watermelondb24.Q.where("user_id", null))] : [import_watermelondb24.Q.where("user_id", userId)];
@@ -35751,9 +35942,25 @@ function createLocalMemoryStore(options) {
35751
35942
  archive: (id) => archiveVaultMemoryOp(vaultCtx, id),
35752
35943
  restore: (id) => restoreVaultMemoryOp(vaultCtx, id),
35753
35944
  setTopics: (memoryId, topics) => setMemoryEntitiesOp(vaultCtx, memoryId, topics),
35754
- addTopics: (memoryId, topics) => database.write(async (writer) => {
35945
+ addTopics: (memoryId, topics, writeOptions) => database.write(async (writer) => {
35946
+ await assertWrite(writeOptions);
35755
35947
  const owned = await vaultCtx.vaultMemoryCollection.query(import_watermelondb24.Q.where("id", memoryId), import_watermelondb24.Q.where("is_deleted", false), ...ownedBy).fetchCount();
35756
- return owned > 0 ? writer.callWriter(() => linkMemoryEntitiesOp(entityCtx, memoryId, topics)) : [];
35948
+ return owned > 0 ? writer.callWriter(
35949
+ () => linkMemoryEntitiesOp(
35950
+ {
35951
+ ...entityCtx,
35952
+ canWrite: async () => {
35953
+ await assertWrite(writeOptions);
35954
+ return true;
35955
+ }
35956
+ },
35957
+ memoryId,
35958
+ topics,
35959
+ {
35960
+ unlessTopicsUserManaged: writeOptions?.unlessUserManaged
35961
+ }
35962
+ )
35963
+ ) : [];
35757
35964
  }),
35758
35965
  setVisibility: (id, visibility, visibilityOptions) => setMemoryVisibilityOp(vaultCtx, id, { visibility, ...visibilityOptions }),
35759
35966
  recall: (query, recallOptions) => recall(
@@ -35763,14 +35970,33 @@ function createLocalMemoryStore(options) {
35763
35970
  ),
35764
35971
  retain: (content, retainOptions) => retain(
35765
35972
  content,
35766
- { vaultCtx, embeddingOptions: options.embeddingOptions, vaultCache },
35973
+ {
35974
+ vaultCtx: {
35975
+ ...vaultCtx,
35976
+ canWrite: async () => {
35977
+ await assertWrite(retainOptions);
35978
+ return true;
35979
+ }
35980
+ },
35981
+ embeddingOptions: options.embeddingOptions,
35982
+ vaultCache
35983
+ },
35767
35984
  retainOptions
35768
35985
  ),
35769
35986
  factSource: {
35770
- search: (query, searchOptions) => searchVaultMemoriesWithSize(
35987
+ search: (query, searchOptions, embeddingCache) => searchVaultMemoriesWithSize(
35771
35988
  query,
35772
35989
  vaultCtx,
35773
- { ...options.embeddingOptions, taskType: "memory_recall_embed" },
35990
+ {
35991
+ ...options.embeddingOptions,
35992
+ taskType: "memory_recall_embed",
35993
+ ...embeddingCache && !options.embeddingOptions.cache && {
35994
+ cache: maskScopedEmbeddingCache(
35995
+ embeddingCache,
35996
+ Boolean(options.embeddingOptions.maskInput)
35997
+ )
35998
+ }
35999
+ },
35774
36000
  vaultCache,
35775
36001
  searchOptions
35776
36002
  ),
@@ -36606,6 +36832,7 @@ function createDurableAutoExtractor(options) {
36606
36832
  };
36607
36833
  const folderId = options.folderId ?? null;
36608
36834
  let disposed = false;
36835
+ let activeBatch = null;
36609
36836
  let running = false;
36610
36837
  let wakeRequested = false;
36611
36838
  let timer;
@@ -36642,14 +36869,18 @@ function createDurableAutoExtractor(options) {
36642
36869
  }, delay);
36643
36870
  }
36644
36871
  async function extractBatch(messages, conversationId, ids, jobScope, jobFolderId) {
36872
+ if (disposed) throw new Error("Extraction session was disposed");
36645
36873
  let watchdog;
36646
36874
  let cancelled = false;
36875
+ const controller = new AbortController();
36876
+ activeBatch = controller;
36647
36877
  const model = options.modelForScope?.(jobScope) ?? options.extract.model;
36648
36878
  try {
36649
36879
  return await Promise.race([
36650
36880
  new Promise((resolve, reject) => {
36651
36881
  const worker = createAutoExtractor({
36652
36882
  ...options,
36883
+ signal: controller.signal,
36653
36884
  retainCtx: {
36654
36885
  ...options.retainCtx,
36655
36886
  vaultCtx: {
@@ -36678,6 +36909,8 @@ function createDurableAutoExtractor(options) {
36678
36909
  ]);
36679
36910
  } finally {
36680
36911
  cancelled = true;
36912
+ controller.abort();
36913
+ if (activeBatch === controller) activeBatch = null;
36681
36914
  if (watchdog !== void 0) clearTimeout(watchdog);
36682
36915
  }
36683
36916
  }
@@ -36689,6 +36922,7 @@ function createDurableAutoExtractor(options) {
36689
36922
  for (const message of loaded)
36690
36923
  if (message && message.messageId > 0) sequences.set(message.uniqueId, message.messageId);
36691
36924
  await database.write(async () => {
36925
+ if (disposed) return;
36692
36926
  const current = idsOf(job);
36693
36927
  const acknowledged = ids.filter((id) => current.includes(id));
36694
36928
  if (!acknowledged.length) return;
@@ -36861,6 +37095,7 @@ function createDurableAutoExtractor(options) {
36861
37095
  jobScope,
36862
37096
  jobFolderId
36863
37097
  );
37098
+ if (disposed) throw new Error("Extraction session was disposed");
36864
37099
  options.onTurnComplete?.(result);
36865
37100
  return result;
36866
37101
  };
@@ -36897,6 +37132,7 @@ function createDurableAutoExtractor(options) {
36897
37132
  );
36898
37133
  scheduleAfterSuccess = true;
36899
37134
  } catch (error) {
37135
+ if (disposed) break;
36900
37136
  attempts.set(job.id, attempted + 1);
36901
37137
  retry = true;
36902
37138
  reportError(error, conversationId);
@@ -36919,6 +37155,14 @@ function createDurableAutoExtractor(options) {
36919
37155
  schedule(retry ? options.retryDelayMs ?? 3e4 : 0);
36920
37156
  }
36921
37157
  }
37158
+ function dispose() {
37159
+ disposed = true;
37160
+ activeBatch?.abort(options.signal?.reason);
37161
+ if (timer !== void 0) clearTimeout(timer);
37162
+ options.signal?.removeEventListener("abort", dispose);
37163
+ }
37164
+ if (options.signal?.aborted) dispose();
37165
+ else options.signal?.addEventListener("abort", dispose, { once: true });
36922
37166
  schedule(0);
36923
37167
  return {
36924
37168
  processTurn(messages, conversationId) {
@@ -36998,10 +37242,7 @@ function createDurableAutoExtractor(options) {
36998
37242
  return true;
36999
37243
  },
37000
37244
  isProcessing: () => running,
37001
- dispose() {
37002
- disposed = true;
37003
- if (timer !== void 0) clearTimeout(timer);
37004
- }
37245
+ dispose
37005
37246
  };
37006
37247
  }
37007
37248