@anuma/sdk 7.4.0 → 7.6.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.
@@ -20671,6 +20671,8 @@ var MAX_QUERY_TOPICS = 100;
20671
20671
  var MAX_QUERY_FILTER = 20;
20672
20672
  var GRAPH_LANE_ROWS = 256;
20673
20673
  var TEMPORAL_LANE_ROWS = 256;
20674
+ var REPAIR_ROWS = 50;
20675
+ var REPAIR_SCAN = 2e3;
20674
20676
  var DECAY_TRANSITIONS_PER_SWEEP = 200;
20675
20677
  var RemoteMemoryPartialCreateError = class extends Error {
20676
20678
  constructor(created, cause) {
@@ -20680,6 +20682,9 @@ var RemoteMemoryPartialCreateError = class extends Error {
20680
20682
  this.name = "RemoteMemoryPartialCreateError";
20681
20683
  }
20682
20684
  };
20685
+ function rejectsInput(error) {
20686
+ return error instanceof EmbeddingHttpError && error.status !== void 0 && error.status >= 400 && error.status < 500 && error.status !== 429 && !isFatalEmbeddingError(error);
20687
+ }
20683
20688
  function warnFailed(failed) {
20684
20689
  if (failed.length)
20685
20690
  getLogger().warn(
@@ -21296,9 +21301,104 @@ function createRemoteMemoryStore(options) {
21296
21301
  polling = false;
21297
21302
  });
21298
21303
  };
21304
+ const unrepairable = /* @__PURE__ */ new Set();
21305
+ let repairCursor;
21306
+ const repairEmbeddings = async ({
21307
+ maxRows = REPAIR_ROWS,
21308
+ maxScan = REPAIR_SCAN,
21309
+ cursor: start = repairCursor
21310
+ } = {}) => {
21311
+ const scannedRows = [];
21312
+ let cursor2 = start;
21313
+ do {
21314
+ const page = await metadata2({
21315
+ include_deleted: true,
21316
+ include_archived: true,
21317
+ include_quarantined: true,
21318
+ include_superseded: true,
21319
+ limit: Math.min(200, Math.max(1, maxScan - scannedRows.length)),
21320
+ ...cursor2 && { cursor: cursor2 }
21321
+ });
21322
+ scannedRows.push(...page.items);
21323
+ cursor2 = page.next_cursor;
21324
+ } while (cursor2 && scannedRows.length < maxScan);
21325
+ const allStale = scannedRows.filter(
21326
+ ({ memory }) => memory.embedding_model !== model && !unrepairable.has(memory.memory_id)
21327
+ );
21328
+ const repaired = /* @__PURE__ */ new Set();
21329
+ const result = {
21330
+ scanned: scannedRows.length,
21331
+ reembedded: 0,
21332
+ failed: 0,
21333
+ skippedConflict: 0
21334
+ };
21335
+ const finish = () => {
21336
+ const settled = allStale.every(
21337
+ ({ memory }) => repaired.has(memory.memory_id) || unrepairable.has(memory.memory_id)
21338
+ );
21339
+ repairCursor = settled ? cursor2 : start;
21340
+ return repairCursor ? { ...result, cursor: repairCursor } : result;
21341
+ };
21342
+ const stale = allStale.slice(0, maxRows).map(({ memory }) => memory.memory_id);
21343
+ if (stale.length === 0) return finish();
21344
+ const { records, unreadable } = await readEveryState(stale);
21345
+ result.failed += unreadable.size;
21346
+ for (const id of stale)
21347
+ if (unreadable.has(id) || !records.get(id)?.memory.content.trim()) unrepairable.add(id);
21348
+ const targets = stale.filter((id) => !unrepairable.has(id)).map((id) => records.get(id));
21349
+ if (targets.length === 0) return finish();
21350
+ const vectors = await embedForRepair(targets);
21351
+ if (!vectors) {
21352
+ result.failed += targets.length;
21353
+ return finish();
21354
+ }
21355
+ for (const [i, record2] of targets.entries()) {
21356
+ const vector = vectors[i];
21357
+ if (!vector) {
21358
+ result.failed++;
21359
+ continue;
21360
+ }
21361
+ await serialized(record2.memory.memory_id, async () => {
21362
+ try {
21363
+ const saved = await persistence.put(
21364
+ { ...record2.memory, embedding: storedVector(vector), embedding_model: model },
21365
+ record2
21366
+ );
21367
+ notify("embedding", [saved]);
21368
+ repaired.add(record2.memory.memory_id);
21369
+ result.reembedded++;
21370
+ } catch (error) {
21371
+ if (error instanceof RemoteMemoryError && error.code === "version_conflict")
21372
+ result.skippedConflict++;
21373
+ else result.failed++;
21374
+ }
21375
+ });
21376
+ }
21377
+ return finish();
21378
+ };
21379
+ const embedForRepair = async (targets) => {
21380
+ const texts = targets.map(({ memory }) => memory.content);
21381
+ try {
21382
+ return await generateEmbeddings(texts, embeddingOptions);
21383
+ } catch (error) {
21384
+ getLogger().warn("[memory/remote-store] Failed to embed memories for repair:", error);
21385
+ if (!rejectsInput(error)) return null;
21386
+ }
21387
+ const vectors = [];
21388
+ for (const [i, text14] of texts.entries()) {
21389
+ try {
21390
+ [vectors[i]] = await generateEmbeddings([text14], embeddingOptions);
21391
+ } catch (error) {
21392
+ if (!rejectsInput(error)) return null;
21393
+ unrepairable.add(targets[i].memory.memory_id);
21394
+ }
21395
+ }
21396
+ return vectors;
21397
+ };
21299
21398
  return {
21300
21399
  masksEmbeddingInput: embeddingOptions.maskInput !== void 0,
21301
21400
  topicMaintenance,
21401
+ repairEmbeddings,
21302
21402
  list: async (listOptions = {}) => (await listRows(decrypted, listOptions, listFilters(listOptions), maxOf(listOptions))).map(
21303
21403
  (r) => stored(r.memory)
21304
21404
  ),
@@ -22361,6 +22461,43 @@ function createDurableAutoExtractor(options) {
22361
22461
  return "private";
22362
22462
  }
22363
22463
  };
22464
+ async function fromSource(call) {
22465
+ let timeout;
22466
+ try {
22467
+ return await Promise.race([
22468
+ call,
22469
+ new Promise((_resolve, reject) => {
22470
+ timeout = setTimeout(
22471
+ () => reject(new Error("Message source did not answer in time")),
22472
+ options.sourceTimeoutMs ?? 3e4
22473
+ );
22474
+ })
22475
+ ]);
22476
+ } finally {
22477
+ if (timeout !== void 0) clearTimeout(timeout);
22478
+ }
22479
+ }
22480
+ async function loadFromSource(source, conversationId, ids) {
22481
+ const byId = new Map(
22482
+ (await fromSource(source.load(conversationId, ids))).map((m) => [m.id, m])
22483
+ );
22484
+ return ids.map((id) => {
22485
+ const message = byId.get(id);
22486
+ return message ? {
22487
+ uniqueId: message.id,
22488
+ messageId: message.seq,
22489
+ conversationId,
22490
+ role: message.role,
22491
+ content: message.content
22492
+ } : null;
22493
+ });
22494
+ }
22495
+ async function sourcesEligible(conversationId, ids) {
22496
+ const source = options.messageSource?.(conversationId);
22497
+ if (source)
22498
+ return await fromSource(source.status(conversationId)) === "ready" && (await loadFromSource(source, conversationId, ids)).every(Boolean);
22499
+ return await storage.conversationsCollection.query(Q9.where("conversation_id", conversationId), Q9.where("is_deleted", false)).fetchCount() > 0 && await storage.messagesCollection.query(Q9.where("conversation_id", conversationId), Q9.where("id", Q9.oneOf(ids))).fetchCount() === ids.length;
22500
+ }
22364
22501
  function schedule(delay) {
22365
22502
  if (disposed) return;
22366
22503
  if (running || timer !== void 0) {
@@ -22389,7 +22526,7 @@ function createDurableAutoExtractor(options) {
22389
22526
  ...options.retainCtx,
22390
22527
  vaultCtx: {
22391
22528
  ...vault,
22392
- canWrite: async () => !disposed && !cancelled && await storage.conversationsCollection.query(Q9.where("conversation_id", conversationId), Q9.where("is_deleted", false)).fetchCount() > 0 && await storage.messagesCollection.query(Q9.where("conversation_id", conversationId), Q9.where("id", Q9.oneOf(ids))).fetchCount() === ids.length && (!vault.canWrite || await vault.canWrite()) && !disposed && !cancelled
22529
+ canWrite: async () => !disposed && !cancelled && await sourcesEligible(conversationId, ids) && (!vault.canWrite || await vault.canWrite()) && !disposed && !cancelled
22393
22530
  }
22394
22531
  },
22395
22532
  extract: { ...options.extract, ...model !== void 0 && { model } },
@@ -22530,6 +22667,7 @@ function createDurableAutoExtractor(options) {
22530
22667
  wakeRequested = false;
22531
22668
  let retry = false;
22532
22669
  let scheduleAfterSuccess = false;
22670
+ let sourceWaiting = false;
22533
22671
  try {
22534
22672
  await writes;
22535
22673
  const pending = await jobs.query(Q9.where("owner_key", owner), Q9.where("message_ids", Q9.notEq("[]"))).fetch();
@@ -22540,23 +22678,37 @@ function createDurableAutoExtractor(options) {
22540
22678
  const attempted = attempts.get(job.id) ?? 0;
22541
22679
  if (attempted >= MAX_ATTEMPTS || skippedThisSession.has(job.id) || awaitingKey.has(job.id))
22542
22680
  continue;
22681
+ const conversationId = String(job._getRaw("conversation_id"));
22682
+ const source = options.messageSource?.(conversationId);
22683
+ let sourceStatus;
22684
+ if (source) {
22685
+ try {
22686
+ sourceStatus = await fromSource(source.status(conversationId));
22687
+ } catch (error) {
22688
+ reportError(error, conversationId);
22689
+ sourceStatus = "later";
22690
+ }
22691
+ if (sourceStatus === "later") {
22692
+ sourceWaiting = true;
22693
+ continue;
22694
+ }
22695
+ }
22543
22696
  if (processed >= MAX_JOBS_PER_PASS) {
22544
22697
  scheduleAfterSuccess = true;
22545
22698
  break;
22546
22699
  }
22547
22700
  processed++;
22548
- const conversationId = String(job._getRaw("conversation_id"));
22549
22701
  const epochAtRead = keyEpoch;
22550
22702
  let ids = [];
22551
22703
  let loaded = [];
22552
22704
  try {
22553
- const conversations = await storage.conversationsCollection.query(Q9.where("conversation_id", conversationId), Q9.where("is_deleted", false)).fetchCount();
22554
- if (!conversations) {
22705
+ const present = source ? sourceStatus === "ready" : await storage.conversationsCollection.query(Q9.where("conversation_id", conversationId), Q9.where("is_deleted", false)).fetchCount() > 0;
22706
+ if (!present) {
22555
22707
  await database.write(() => job.destroyPermanently());
22556
22708
  continue;
22557
22709
  }
22558
22710
  ids = idsOf(job).slice(0, 20);
22559
- loaded = await Promise.all(ids.map((id) => getMessageOp(storage, id)));
22711
+ loaded = source ? await loadFromSource(source, conversationId, ids) : await Promise.all(ids.map((id) => getMessageOp(storage, id)));
22560
22712
  const missing = ids.filter((_id, index) => loaded[index] === null);
22561
22713
  if (missing.length) {
22562
22714
  if (attempted < MAX_ATTEMPTS - 1)
@@ -22600,7 +22752,7 @@ function createDurableAutoExtractor(options) {
22600
22752
  );
22601
22753
  continue;
22602
22754
  }
22603
- const locked = loaded.filter(
22755
+ const locked = source ? [] : loaded.filter(
22604
22756
  (message) => !!message && isEncrypted(message.content)
22605
22757
  );
22606
22758
  if (locked.length) {
@@ -22688,6 +22840,7 @@ function createDurableAutoExtractor(options) {
22688
22840
  wakeRequested = false;
22689
22841
  if (retry || scheduleAfterSuccess || woken)
22690
22842
  schedule(retry ? options.retryDelayMs ?? 3e4 : 0);
22843
+ else if (sourceWaiting) schedule(options.retryDelayMs ?? 3e4);
22691
22844
  }
22692
22845
  }
22693
22846
  function dispose() {
@@ -22722,7 +22875,8 @@ function createDurableAutoExtractor(options) {
22722
22875
  (candidate) => candidate._getRaw("scope") === scope && (candidate._getRaw("folder_id") ?? null) === folderId
22723
22876
  );
22724
22877
  const positions = new Map(ids.map((id, index) => [id, index]));
22725
- const watermarks = existing.flatMap((prior) => {
22878
+ const fromSourceConversation = options.messageSource?.(conversationId) !== void 0;
22879
+ const watermarks = fromSourceConversation ? [] : existing.flatMap((prior) => {
22726
22880
  const watermark = prior._getRaw("watermark");
22727
22881
  return typeof watermark === "string" && watermark ? [watermark] : [];
22728
22882
  });
@@ -22740,11 +22894,16 @@ function createDurableAutoExtractor(options) {
22740
22894
  r._setRaw("watermark_seq", null);
22741
22895
  });
22742
22896
  }
22743
- const rows = await storage.messagesCollection.query(Q9.where("id", Q9.oneOf(ids))).fetch();
22897
+ const rows = fromSourceConversation ? [] : await storage.messagesCollection.query(Q9.where("id", Q9.oneOf(ids))).fetch();
22744
22898
  const foreignRows = new Set(
22745
22899
  rows.filter((row) => row._getRaw("conversation_id") !== conversationId).map((row) => row.id)
22746
22900
  );
22747
22901
  const sequences = /* @__PURE__ */ new Map();
22902
+ if (fromSourceConversation) {
22903
+ for (const message of messages)
22904
+ if (message.seq !== void 0 && message.seq > 0)
22905
+ sequences.set(message.id, message.seq);
22906
+ }
22748
22907
  for (const row of rows) {
22749
22908
  if (foreignRows.has(row.id)) continue;
22750
22909
  const sequence = Number(row._getRaw("message_id"));
@@ -22777,14 +22936,17 @@ function createDurableAutoExtractor(options) {
22777
22936
  reportError(error, conversationId);
22778
22937
  }
22779
22938
  }
22780
- if (boundary < 0 && existing.some((prior) => idsOf(prior).length > 0 || seqOf(prior) !== void 0)) {
22939
+ const ordered = fromSourceConversation && ids.every((id) => sequences.has(id));
22940
+ if (boundary < 0 && !ordered && existing.some(
22941
+ (prior) => idsOf(prior).length > 0 || seqOf(prior) !== void 0 || fromSourceConversation && !!prior._getRaw("watermark")
22942
+ )) {
22781
22943
  reportError(
22782
22944
  new Error("Extraction boundary unresolved; skipped rather than re-observing"),
22783
22945
  conversationId
22784
22946
  );
22785
22947
  return;
22786
22948
  }
22787
- const start = boundary >= 0 ? boundary + 1 : Math.max(0, ids.length - Math.max(1, options.windowSize ?? 6));
22949
+ const start = boundary >= 0 ? boundary + 1 : fromSourceConversation ? 0 : Math.max(0, ids.length - Math.max(1, options.windowSize ?? 6));
22788
22950
  if (start >= ids.length) return;
22789
22951
  const ownWatermark = job?._getRaw("watermark");
22790
22952
  const atOwnWatermark = !!job && boundary >= 0 && failedSessionsOf(job, String(ownWatermark)) < MAX_FAILED_SESSIONS && (ids[boundary] === ownWatermark || sequences.get(ids[boundary]) !== void 0 && sequences.get(ids[boundary]) === seqOf(job));
@@ -24639,6 +24639,8 @@ var MAX_QUERY_TOPICS = 100;
24639
24639
  var MAX_QUERY_FILTER = 20;
24640
24640
  var GRAPH_LANE_ROWS = 256;
24641
24641
  var TEMPORAL_LANE_ROWS = 256;
24642
+ var REPAIR_ROWS = 50;
24643
+ var REPAIR_SCAN = 2e3;
24642
24644
  var DECAY_TRANSITIONS_PER_SWEEP = 200;
24643
24645
  var RemoteMemoryPartialCreateError = class extends Error {
24644
24646
  constructor(created, cause) {
@@ -24648,6 +24650,9 @@ var RemoteMemoryPartialCreateError = class extends Error {
24648
24650
  this.name = "RemoteMemoryPartialCreateError";
24649
24651
  }
24650
24652
  };
24653
+ function rejectsInput(error) {
24654
+ return error instanceof EmbeddingHttpError && error.status !== void 0 && error.status >= 400 && error.status < 500 && error.status !== 429 && !isFatalEmbeddingError(error);
24655
+ }
24651
24656
  function warnFailed(failed) {
24652
24657
  if (failed.length)
24653
24658
  getLogger().warn(
@@ -25264,9 +25269,104 @@ function createRemoteMemoryStore(options) {
25264
25269
  polling = false;
25265
25270
  });
25266
25271
  };
25272
+ const unrepairable = /* @__PURE__ */ new Set();
25273
+ let repairCursor;
25274
+ const repairEmbeddings = async ({
25275
+ maxRows = REPAIR_ROWS,
25276
+ maxScan = REPAIR_SCAN,
25277
+ cursor: start = repairCursor
25278
+ } = {}) => {
25279
+ const scannedRows = [];
25280
+ let cursor2 = start;
25281
+ do {
25282
+ const page = await metadata2({
25283
+ include_deleted: true,
25284
+ include_archived: true,
25285
+ include_quarantined: true,
25286
+ include_superseded: true,
25287
+ limit: Math.min(200, Math.max(1, maxScan - scannedRows.length)),
25288
+ ...cursor2 && { cursor: cursor2 }
25289
+ });
25290
+ scannedRows.push(...page.items);
25291
+ cursor2 = page.next_cursor;
25292
+ } while (cursor2 && scannedRows.length < maxScan);
25293
+ const allStale = scannedRows.filter(
25294
+ ({ memory }) => memory.embedding_model !== model && !unrepairable.has(memory.memory_id)
25295
+ );
25296
+ const repaired = /* @__PURE__ */ new Set();
25297
+ const result = {
25298
+ scanned: scannedRows.length,
25299
+ reembedded: 0,
25300
+ failed: 0,
25301
+ skippedConflict: 0
25302
+ };
25303
+ const finish = () => {
25304
+ const settled = allStale.every(
25305
+ ({ memory }) => repaired.has(memory.memory_id) || unrepairable.has(memory.memory_id)
25306
+ );
25307
+ repairCursor = settled ? cursor2 : start;
25308
+ return repairCursor ? { ...result, cursor: repairCursor } : result;
25309
+ };
25310
+ const stale = allStale.slice(0, maxRows).map(({ memory }) => memory.memory_id);
25311
+ if (stale.length === 0) return finish();
25312
+ const { records, unreadable } = await readEveryState(stale);
25313
+ result.failed += unreadable.size;
25314
+ for (const id of stale)
25315
+ if (unreadable.has(id) || !records.get(id)?.memory.content.trim()) unrepairable.add(id);
25316
+ const targets = stale.filter((id) => !unrepairable.has(id)).map((id) => records.get(id));
25317
+ if (targets.length === 0) return finish();
25318
+ const vectors = await embedForRepair(targets);
25319
+ if (!vectors) {
25320
+ result.failed += targets.length;
25321
+ return finish();
25322
+ }
25323
+ for (const [i, record2] of targets.entries()) {
25324
+ const vector = vectors[i];
25325
+ if (!vector) {
25326
+ result.failed++;
25327
+ continue;
25328
+ }
25329
+ await serialized(record2.memory.memory_id, async () => {
25330
+ try {
25331
+ const saved = await persistence.put(
25332
+ { ...record2.memory, embedding: storedVector(vector), embedding_model: model },
25333
+ record2
25334
+ );
25335
+ notify("embedding", [saved]);
25336
+ repaired.add(record2.memory.memory_id);
25337
+ result.reembedded++;
25338
+ } catch (error) {
25339
+ if (error instanceof RemoteMemoryError && error.code === "version_conflict")
25340
+ result.skippedConflict++;
25341
+ else result.failed++;
25342
+ }
25343
+ });
25344
+ }
25345
+ return finish();
25346
+ };
25347
+ const embedForRepair = async (targets) => {
25348
+ const texts = targets.map(({ memory }) => memory.content);
25349
+ try {
25350
+ return await generateEmbeddings(texts, embeddingOptions);
25351
+ } catch (error) {
25352
+ getLogger().warn("[memory/remote-store] Failed to embed memories for repair:", error);
25353
+ if (!rejectsInput(error)) return null;
25354
+ }
25355
+ const vectors = [];
25356
+ for (const [i, text14] of texts.entries()) {
25357
+ try {
25358
+ [vectors[i]] = await generateEmbeddings([text14], embeddingOptions);
25359
+ } catch (error) {
25360
+ if (!rejectsInput(error)) return null;
25361
+ unrepairable.add(targets[i].memory.memory_id);
25362
+ }
25363
+ }
25364
+ return vectors;
25365
+ };
25267
25366
  return {
25268
25367
  masksEmbeddingInput: embeddingOptions.maskInput !== void 0,
25269
25368
  topicMaintenance,
25369
+ repairEmbeddings,
25270
25370
  list: async (listOptions = {}) => (await listRows(decrypted, listOptions, listFilters(listOptions), maxOf(listOptions))).map(
25271
25371
  (r) => stored(r.memory)
25272
25372
  ),
@@ -26331,6 +26431,43 @@ function createDurableAutoExtractor(options) {
26331
26431
  return "private";
26332
26432
  }
26333
26433
  };
26434
+ async function fromSource(call) {
26435
+ let timeout;
26436
+ try {
26437
+ return await Promise.race([
26438
+ call,
26439
+ new Promise((_resolve, reject) => {
26440
+ timeout = setTimeout(
26441
+ () => reject(new Error("Message source did not answer in time")),
26442
+ options.sourceTimeoutMs ?? 3e4
26443
+ );
26444
+ })
26445
+ ]);
26446
+ } finally {
26447
+ if (timeout !== void 0) clearTimeout(timeout);
26448
+ }
26449
+ }
26450
+ async function loadFromSource(source, conversationId, ids) {
26451
+ const byId = new Map(
26452
+ (await fromSource(source.load(conversationId, ids))).map((m) => [m.id, m])
26453
+ );
26454
+ return ids.map((id) => {
26455
+ const message = byId.get(id);
26456
+ return message ? {
26457
+ uniqueId: message.id,
26458
+ messageId: message.seq,
26459
+ conversationId,
26460
+ role: message.role,
26461
+ content: message.content
26462
+ } : null;
26463
+ });
26464
+ }
26465
+ async function sourcesEligible(conversationId, ids) {
26466
+ const source = options.messageSource?.(conversationId);
26467
+ if (source)
26468
+ return await fromSource(source.status(conversationId)) === "ready" && (await loadFromSource(source, conversationId, ids)).every(Boolean);
26469
+ return await storage.conversationsCollection.query(import_watermelondb33.Q.where("conversation_id", conversationId), import_watermelondb33.Q.where("is_deleted", false)).fetchCount() > 0 && await storage.messagesCollection.query(import_watermelondb33.Q.where("conversation_id", conversationId), import_watermelondb33.Q.where("id", import_watermelondb33.Q.oneOf(ids))).fetchCount() === ids.length;
26470
+ }
26334
26471
  function schedule(delay) {
26335
26472
  if (disposed) return;
26336
26473
  if (running || timer !== void 0) {
@@ -26359,7 +26496,7 @@ function createDurableAutoExtractor(options) {
26359
26496
  ...options.retainCtx,
26360
26497
  vaultCtx: {
26361
26498
  ...vault,
26362
- canWrite: async () => !disposed && !cancelled && await storage.conversationsCollection.query(import_watermelondb33.Q.where("conversation_id", conversationId), import_watermelondb33.Q.where("is_deleted", false)).fetchCount() > 0 && await storage.messagesCollection.query(import_watermelondb33.Q.where("conversation_id", conversationId), import_watermelondb33.Q.where("id", import_watermelondb33.Q.oneOf(ids))).fetchCount() === ids.length && (!vault.canWrite || await vault.canWrite()) && !disposed && !cancelled
26499
+ canWrite: async () => !disposed && !cancelled && await sourcesEligible(conversationId, ids) && (!vault.canWrite || await vault.canWrite()) && !disposed && !cancelled
26363
26500
  }
26364
26501
  },
26365
26502
  extract: { ...options.extract, ...model !== void 0 && { model } },
@@ -26500,6 +26637,7 @@ function createDurableAutoExtractor(options) {
26500
26637
  wakeRequested = false;
26501
26638
  let retry = false;
26502
26639
  let scheduleAfterSuccess = false;
26640
+ let sourceWaiting = false;
26503
26641
  try {
26504
26642
  await writes;
26505
26643
  const pending = await jobs.query(import_watermelondb33.Q.where("owner_key", owner), import_watermelondb33.Q.where("message_ids", import_watermelondb33.Q.notEq("[]"))).fetch();
@@ -26510,23 +26648,37 @@ function createDurableAutoExtractor(options) {
26510
26648
  const attempted = attempts.get(job.id) ?? 0;
26511
26649
  if (attempted >= MAX_ATTEMPTS || skippedThisSession.has(job.id) || awaitingKey.has(job.id))
26512
26650
  continue;
26651
+ const conversationId = String(job._getRaw("conversation_id"));
26652
+ const source = options.messageSource?.(conversationId);
26653
+ let sourceStatus;
26654
+ if (source) {
26655
+ try {
26656
+ sourceStatus = await fromSource(source.status(conversationId));
26657
+ } catch (error) {
26658
+ reportError(error, conversationId);
26659
+ sourceStatus = "later";
26660
+ }
26661
+ if (sourceStatus === "later") {
26662
+ sourceWaiting = true;
26663
+ continue;
26664
+ }
26665
+ }
26513
26666
  if (processed >= MAX_JOBS_PER_PASS) {
26514
26667
  scheduleAfterSuccess = true;
26515
26668
  break;
26516
26669
  }
26517
26670
  processed++;
26518
- const conversationId = String(job._getRaw("conversation_id"));
26519
26671
  const epochAtRead = keyEpoch;
26520
26672
  let ids = [];
26521
26673
  let loaded = [];
26522
26674
  try {
26523
- const conversations = await storage.conversationsCollection.query(import_watermelondb33.Q.where("conversation_id", conversationId), import_watermelondb33.Q.where("is_deleted", false)).fetchCount();
26524
- if (!conversations) {
26675
+ const present = source ? sourceStatus === "ready" : await storage.conversationsCollection.query(import_watermelondb33.Q.where("conversation_id", conversationId), import_watermelondb33.Q.where("is_deleted", false)).fetchCount() > 0;
26676
+ if (!present) {
26525
26677
  await database.write(() => job.destroyPermanently());
26526
26678
  continue;
26527
26679
  }
26528
26680
  ids = idsOf(job).slice(0, 20);
26529
- loaded = await Promise.all(ids.map((id) => getMessageOp(storage, id)));
26681
+ loaded = source ? await loadFromSource(source, conversationId, ids) : await Promise.all(ids.map((id) => getMessageOp(storage, id)));
26530
26682
  const missing = ids.filter((_id, index) => loaded[index] === null);
26531
26683
  if (missing.length) {
26532
26684
  if (attempted < MAX_ATTEMPTS - 1)
@@ -26570,7 +26722,7 @@ function createDurableAutoExtractor(options) {
26570
26722
  );
26571
26723
  continue;
26572
26724
  }
26573
- const locked = loaded.filter(
26725
+ const locked = source ? [] : loaded.filter(
26574
26726
  (message) => !!message && isEncrypted(message.content)
26575
26727
  );
26576
26728
  if (locked.length) {
@@ -26658,6 +26810,7 @@ function createDurableAutoExtractor(options) {
26658
26810
  wakeRequested = false;
26659
26811
  if (retry || scheduleAfterSuccess || woken)
26660
26812
  schedule(retry ? options.retryDelayMs ?? 3e4 : 0);
26813
+ else if (sourceWaiting) schedule(options.retryDelayMs ?? 3e4);
26661
26814
  }
26662
26815
  }
26663
26816
  function dispose() {
@@ -26692,7 +26845,8 @@ function createDurableAutoExtractor(options) {
26692
26845
  (candidate) => candidate._getRaw("scope") === scope && (candidate._getRaw("folder_id") ?? null) === folderId
26693
26846
  );
26694
26847
  const positions = new Map(ids.map((id, index) => [id, index]));
26695
- const watermarks = existing.flatMap((prior) => {
26848
+ const fromSourceConversation = options.messageSource?.(conversationId) !== void 0;
26849
+ const watermarks = fromSourceConversation ? [] : existing.flatMap((prior) => {
26696
26850
  const watermark = prior._getRaw("watermark");
26697
26851
  return typeof watermark === "string" && watermark ? [watermark] : [];
26698
26852
  });
@@ -26710,11 +26864,16 @@ function createDurableAutoExtractor(options) {
26710
26864
  r._setRaw("watermark_seq", null);
26711
26865
  });
26712
26866
  }
26713
- const rows = await storage.messagesCollection.query(import_watermelondb33.Q.where("id", import_watermelondb33.Q.oneOf(ids))).fetch();
26867
+ const rows = fromSourceConversation ? [] : await storage.messagesCollection.query(import_watermelondb33.Q.where("id", import_watermelondb33.Q.oneOf(ids))).fetch();
26714
26868
  const foreignRows = new Set(
26715
26869
  rows.filter((row) => row._getRaw("conversation_id") !== conversationId).map((row) => row.id)
26716
26870
  );
26717
26871
  const sequences = /* @__PURE__ */ new Map();
26872
+ if (fromSourceConversation) {
26873
+ for (const message of messages)
26874
+ if (message.seq !== void 0 && message.seq > 0)
26875
+ sequences.set(message.id, message.seq);
26876
+ }
26718
26877
  for (const row of rows) {
26719
26878
  if (foreignRows.has(row.id)) continue;
26720
26879
  const sequence = Number(row._getRaw("message_id"));
@@ -26747,14 +26906,17 @@ function createDurableAutoExtractor(options) {
26747
26906
  reportError(error, conversationId);
26748
26907
  }
26749
26908
  }
26750
- if (boundary < 0 && existing.some((prior) => idsOf(prior).length > 0 || seqOf(prior) !== void 0)) {
26909
+ const ordered = fromSourceConversation && ids.every((id) => sequences.has(id));
26910
+ if (boundary < 0 && !ordered && existing.some(
26911
+ (prior) => idsOf(prior).length > 0 || seqOf(prior) !== void 0 || fromSourceConversation && !!prior._getRaw("watermark")
26912
+ )) {
26751
26913
  reportError(
26752
26914
  new Error("Extraction boundary unresolved; skipped rather than re-observing"),
26753
26915
  conversationId
26754
26916
  );
26755
26917
  return;
26756
26918
  }
26757
- const start = boundary >= 0 ? boundary + 1 : Math.max(0, ids.length - Math.max(1, options.windowSize ?? 6));
26919
+ const start = boundary >= 0 ? boundary + 1 : fromSourceConversation ? 0 : Math.max(0, ids.length - Math.max(1, options.windowSize ?? 6));
26758
26920
  if (start >= ids.length) return;
26759
26921
  const ownWatermark = job?._getRaw("watermark");
26760
26922
  const atOwnWatermark = !!job && boundary >= 0 && failedSessionsOf(job, String(ownWatermark)) < MAX_FAILED_SESSIONS && (ids[boundary] === ownWatermark || sequences.get(ids[boundary]) !== void 0 && sequences.get(ids[boundary]) === seqOf(job));