@anuma/sdk 6.21.1 → 6.22.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.
@@ -1199,6 +1199,18 @@ var init_encryption_utils = __esm({
1199
1199
  }
1200
1200
  });
1201
1201
 
1202
+ // src/lib/db/extractionJobs/models.ts
1203
+ var import_watermelondb, ExtractionJob;
1204
+ var init_models = __esm({
1205
+ "src/lib/db/extractionJobs/models.ts"() {
1206
+ "use strict";
1207
+ import_watermelondb = require("@nozbe/watermelondb");
1208
+ ExtractionJob = class extends import_watermelondb.Model {
1209
+ };
1210
+ ExtractionJob.table = "memory_extraction_jobs";
1211
+ }
1212
+ });
1213
+
1202
1214
  // src/lib/db/chat/conversationEncryption.ts
1203
1215
  async function encryptConversationFields(conversation, address, signMessage, embeddedWalletSigner) {
1204
1216
  if (!address || !signMessage) {
@@ -1786,7 +1798,7 @@ async function createConversationOp(ctx, opts, defaultTitle = "New Conversation"
1786
1798
  );
1787
1799
  }
1788
1800
  async function getConversationOp(ctx, id) {
1789
- const results = await ctx.conversationsCollection.query(import_watermelondb.Q.where("conversation_id", id), import_watermelondb.Q.where("is_deleted", false)).fetch();
1801
+ const results = await ctx.conversationsCollection.query(import_watermelondb2.Q.where("conversation_id", id), import_watermelondb2.Q.where("is_deleted", false)).fetch();
1790
1802
  return results.length > 0 ? await conversationToStored(
1791
1803
  results[0],
1792
1804
  ctx.walletAddress,
@@ -1795,7 +1807,7 @@ async function getConversationOp(ctx, id) {
1795
1807
  ) : null;
1796
1808
  }
1797
1809
  async function getConversationsOp(ctx) {
1798
- const results = await ctx.conversationsCollection.query(import_watermelondb.Q.where("is_deleted", false), import_watermelondb.Q.sortBy("created_at", import_watermelondb.Q.desc)).unsafeFetchRaw();
1810
+ const results = await ctx.conversationsCollection.query(import_watermelondb2.Q.where("is_deleted", false), import_watermelondb2.Q.sortBy("created_at", import_watermelondb2.Q.desc)).unsafeFetchRaw();
1799
1811
  return Promise.all(
1800
1812
  results.map(
1801
1813
  (raw) => conversationRawToStored(raw, ctx.walletAddress, ctx.signMessage, ctx.embeddedWalletSigner)
@@ -1816,33 +1828,33 @@ function conversationRawToLazyStored(raw) {
1816
1828
  };
1817
1829
  }
1818
1830
  async function getConversationsLazyOp(ctx) {
1819
- const results = await ctx.conversationsCollection.query(import_watermelondb.Q.where("is_deleted", false), import_watermelondb.Q.sortBy("created_at", import_watermelondb.Q.desc)).unsafeFetchRaw();
1831
+ const results = await ctx.conversationsCollection.query(import_watermelondb2.Q.where("is_deleted", false), import_watermelondb2.Q.sortBy("created_at", import_watermelondb2.Q.desc)).unsafeFetchRaw();
1820
1832
  return results.map(conversationRawToLazyStored);
1821
1833
  }
1822
1834
  async function getConversationsPageOp(ctx, options) {
1823
1835
  const limit = Math.floor(options?.limit ?? 200);
1824
1836
  if (!Number.isFinite(limit) || limit < 1) return [];
1825
1837
  const exclude = new Set(options?.boundaryExcludeUniqueIds ?? []);
1826
- const clauses = [import_watermelondb.Q.where("is_deleted", false)];
1838
+ const clauses = [import_watermelondb2.Q.where("is_deleted", false)];
1827
1839
  if (options?.before !== void 0) {
1828
1840
  clauses.push(
1829
- exclude.size > 0 ? import_watermelondb.Q.where("created_at", import_watermelondb.Q.lte(options.before)) : import_watermelondb.Q.where("created_at", import_watermelondb.Q.lt(options.before))
1841
+ exclude.size > 0 ? import_watermelondb2.Q.where("created_at", import_watermelondb2.Q.lte(options.before)) : import_watermelondb2.Q.where("created_at", import_watermelondb2.Q.lt(options.before))
1830
1842
  );
1831
1843
  }
1832
- const fetched = await ctx.conversationsCollection.query(...clauses, import_watermelondb.Q.sortBy("created_at", import_watermelondb.Q.desc), import_watermelondb.Q.take(limit + exclude.size)).unsafeFetchRaw();
1844
+ const fetched = await ctx.conversationsCollection.query(...clauses, import_watermelondb2.Q.sortBy("created_at", import_watermelondb2.Q.desc), import_watermelondb2.Q.take(limit + exclude.size)).unsafeFetchRaw();
1833
1845
  const results = exclude.size > 0 ? fetched.filter((raw) => !exclude.has(raw.id)) : fetched;
1834
1846
  return results.slice(0, limit).map(conversationRawToLazyStored);
1835
1847
  }
1836
1848
  async function getConversationsByProjectLazyOp(ctx, projectId) {
1837
1849
  const results = await ctx.conversationsCollection.query(
1838
- import_watermelondb.Q.where("project_id", projectId === null ? "" : projectId),
1839
- import_watermelondb.Q.where("is_deleted", false),
1840
- import_watermelondb.Q.sortBy("created_at", import_watermelondb.Q.desc)
1850
+ import_watermelondb2.Q.where("project_id", projectId === null ? "" : projectId),
1851
+ import_watermelondb2.Q.where("is_deleted", false),
1852
+ import_watermelondb2.Q.sortBy("created_at", import_watermelondb2.Q.desc)
1841
1853
  ).unsafeFetchRaw();
1842
1854
  return results.map(conversationRawToLazyStored);
1843
1855
  }
1844
1856
  async function updateConversationTitleOp(ctx, id, title) {
1845
- const results = await ctx.conversationsCollection.query(import_watermelondb.Q.where("conversation_id", id), import_watermelondb.Q.where("is_deleted", false)).fetch();
1857
+ const results = await ctx.conversationsCollection.query(import_watermelondb2.Q.where("conversation_id", id), import_watermelondb2.Q.where("is_deleted", false)).fetch();
1846
1858
  if (results.length > 0) {
1847
1859
  const encryptedOpts = ctx.walletAddress && ctx.signMessage ? await encryptConversationFields(
1848
1860
  { title },
@@ -1861,19 +1873,21 @@ async function updateConversationTitleOp(ctx, id, title) {
1861
1873
  return false;
1862
1874
  }
1863
1875
  async function deleteConversationOp(ctx, id) {
1864
- const results = await ctx.conversationsCollection.query(import_watermelondb.Q.where("conversation_id", id), import_watermelondb.Q.where("is_deleted", false)).fetch();
1876
+ const results = await ctx.conversationsCollection.query(import_watermelondb2.Q.where("conversation_id", id), import_watermelondb2.Q.where("is_deleted", false)).fetch();
1865
1877
  if (results.length > 0) {
1866
1878
  await ctx.database.write(async () => {
1867
1879
  await results[0].update((conv) => {
1868
1880
  conv._setRaw("is_deleted", true);
1869
1881
  });
1882
+ const jobs = ctx.database.schema?.tables[ExtractionJob.table] ? await ctx.database.get(ExtractionJob.table).query(import_watermelondb2.Q.where("conversation_id", id)).fetch() : [];
1883
+ for (const job of jobs) await job.destroyPermanently();
1870
1884
  });
1871
1885
  return true;
1872
1886
  }
1873
1887
  return false;
1874
1888
  }
1875
1889
  async function updateConversationPinnedOp(ctx, id, pinned) {
1876
- const results = await ctx.conversationsCollection.query(import_watermelondb.Q.where("conversation_id", id), import_watermelondb.Q.where("is_deleted", false)).fetch();
1890
+ const results = await ctx.conversationsCollection.query(import_watermelondb2.Q.where("conversation_id", id), import_watermelondb2.Q.where("is_deleted", false)).fetch();
1877
1891
  if (results.length > 0) {
1878
1892
  await ctx.database.write(async () => {
1879
1893
  await results[0].update((conv) => {
@@ -1885,7 +1899,7 @@ async function updateConversationPinnedOp(ctx, id, pinned) {
1885
1899
  return false;
1886
1900
  }
1887
1901
  async function getMessagesOp(ctx, convId) {
1888
- const results = await ctx.messagesCollection.query(import_watermelondb.Q.where("conversation_id", convId), import_watermelondb.Q.sortBy("message_id", import_watermelondb.Q.asc)).unsafeFetchRaw();
1902
+ const results = await ctx.messagesCollection.query(import_watermelondb2.Q.where("conversation_id", convId), import_watermelondb2.Q.sortBy("message_id", import_watermelondb2.Q.asc)).unsafeFetchRaw();
1889
1903
  return Promise.all(
1890
1904
  results.map(
1891
1905
  (raw) => messageRawToStored(raw, ctx.walletAddress, ctx.signMessage, ctx.embeddedWalletSigner)
@@ -1893,19 +1907,19 @@ async function getMessagesOp(ctx, convId) {
1893
1907
  );
1894
1908
  }
1895
1909
  async function getMessageCountOp(ctx, convId) {
1896
- return await ctx.messagesCollection.query(import_watermelondb.Q.where("conversation_id", convId)).fetchCount();
1910
+ return await ctx.messagesCollection.query(import_watermelondb2.Q.where("conversation_id", convId)).fetchCount();
1897
1911
  }
1898
1912
  async function getMessagesPageOp(ctx, convId, opts) {
1899
1913
  const limit = Math.floor(opts.limit);
1900
1914
  if (!Number.isFinite(limit) || limit < 1) return [];
1901
1915
  const exclude = new Set(opts.boundaryExcludeUniqueIds ?? []);
1902
- const clauses = [import_watermelondb.Q.where("conversation_id", convId)];
1916
+ const clauses = [import_watermelondb2.Q.where("conversation_id", convId)];
1903
1917
  if (opts.beforeMessageId !== void 0) {
1904
1918
  clauses.push(
1905
- exclude.size > 0 ? import_watermelondb.Q.where("message_id", import_watermelondb.Q.lte(opts.beforeMessageId)) : import_watermelondb.Q.where("message_id", import_watermelondb.Q.lt(opts.beforeMessageId))
1919
+ exclude.size > 0 ? import_watermelondb2.Q.where("message_id", import_watermelondb2.Q.lte(opts.beforeMessageId)) : import_watermelondb2.Q.where("message_id", import_watermelondb2.Q.lt(opts.beforeMessageId))
1906
1920
  );
1907
1921
  }
1908
- const fetched = await ctx.messagesCollection.query(...clauses, import_watermelondb.Q.sortBy("message_id", import_watermelondb.Q.desc), import_watermelondb.Q.take(limit + exclude.size)).unsafeFetchRaw();
1922
+ const fetched = await ctx.messagesCollection.query(...clauses, import_watermelondb2.Q.sortBy("message_id", import_watermelondb2.Q.desc), import_watermelondb2.Q.take(limit + exclude.size)).unsafeFetchRaw();
1909
1923
  const results = (exclude.size > 0 ? fetched.filter((raw) => !exclude.has(raw.id)) : fetched).slice(0, limit);
1910
1924
  results.reverse();
1911
1925
  return Promise.all(
@@ -1917,7 +1931,7 @@ async function getMessagesPageOp(ctx, convId, opts) {
1917
1931
  );
1918
1932
  }
1919
1933
  async function getMessageSkeletonsOp(ctx, convId) {
1920
- const results = await ctx.messagesCollection.query(import_watermelondb.Q.where("conversation_id", convId), import_watermelondb.Q.sortBy("message_id", import_watermelondb.Q.asc)).unsafeFetchRaw();
1934
+ const results = await ctx.messagesCollection.query(import_watermelondb2.Q.where("conversation_id", convId), import_watermelondb2.Q.sortBy("message_id", import_watermelondb2.Q.asc)).unsafeFetchRaw();
1921
1935
  const roleById = /* @__PURE__ */ new Map();
1922
1936
  for (const raw of results) {
1923
1937
  roleById.set(raw.id, raw.role);
@@ -1964,7 +1978,7 @@ async function getMessageSkeletonsOp(ctx, convId) {
1964
1978
  return skeletons;
1965
1979
  }
1966
1980
  async function getToolCallEventIdsOp(ctx, convId) {
1967
- const results = await ctx.messagesCollection.query(import_watermelondb.Q.where("conversation_id", convId)).unsafeFetchRaw();
1981
+ const results = await ctx.messagesCollection.query(import_watermelondb2.Q.where("conversation_id", convId)).unsafeFetchRaw();
1968
1982
  const ids = /* @__PURE__ */ new Set();
1969
1983
  for (const raw of results) {
1970
1984
  const value = raw.tool_call_events;
@@ -1987,8 +2001,16 @@ async function getToolCallEventIdsOp(ctx, convId) {
1987
2001
  return ids;
1988
2002
  }
1989
2003
  async function clearMessagesOp(ctx, convId) {
1990
- const messages = await ctx.messagesCollection.query(import_watermelondb.Q.where("conversation_id", convId)).fetch();
1991
2004
  await ctx.database.write(async () => {
2005
+ const messages = await ctx.messagesCollection.query(import_watermelondb2.Q.where("conversation_id", convId)).fetch();
2006
+ const jobs = ctx.database.schema?.tables[ExtractionJob.table] ? await ctx.database.get(ExtractionJob.table).query(import_watermelondb2.Q.where("conversation_id", convId)).fetch() : [];
2007
+ for (const job of jobs) {
2008
+ await job.update((row) => {
2009
+ row._setRaw("message_ids", "[]");
2010
+ row._setRaw("watermark", null);
2011
+ row._setRaw("watermark_seq", null);
2012
+ });
2013
+ }
1992
2014
  for (const message of messages) {
1993
2015
  await message.update((msg) => {
1994
2016
  msg._setRaw("file_ids", null);
@@ -1998,6 +2020,30 @@ async function clearMessagesOp(ctx, convId) {
1998
2020
  }
1999
2021
  });
2000
2022
  }
2023
+ async function deleteMessageOp(ctx, uniqueId) {
2024
+ let message;
2025
+ try {
2026
+ message = await ctx.messagesCollection.find(uniqueId);
2027
+ } catch {
2028
+ return null;
2029
+ }
2030
+ await ctx.database.write(async () => {
2031
+ const jobs = ctx.database.schema?.tables[ExtractionJob.table] ? await ctx.database.get(ExtractionJob.table).query(import_watermelondb2.Q.where("conversation_id", message.conversationId)).fetch() : [];
2032
+ for (const job of jobs) {
2033
+ const ids = JSON.parse(String(job._getRaw("message_ids")));
2034
+ await job.update((row) => {
2035
+ row._setRaw("message_ids", JSON.stringify(ids.filter((id) => id !== uniqueId)));
2036
+ if (row._getRaw("watermark") === uniqueId) row._setRaw("watermark", null);
2037
+ });
2038
+ }
2039
+ await message.update((msg) => {
2040
+ msg._setRaw("file_ids", null);
2041
+ msg._setRaw("files", null);
2042
+ });
2043
+ await message.destroyPermanently();
2044
+ });
2045
+ return uniqueId;
2046
+ }
2001
2047
  async function encryptMessageOptsIfNeeded(ctx, opts) {
2002
2048
  return ctx.walletAddress && ctx.signMessage ? await encryptMessageFields(opts, ctx.walletAddress, ctx.signMessage, ctx.embeddedWalletSigner) : opts;
2003
2049
  }
@@ -2055,9 +2101,9 @@ async function createMessageOp(ctx, opts) {
2055
2101
  }
2056
2102
  }
2057
2103
  const newest = await ctx.messagesCollection.query(
2058
- import_watermelondb.Q.where("conversation_id", opts.conversationId),
2059
- import_watermelondb.Q.sortBy("message_id", import_watermelondb.Q.desc),
2060
- import_watermelondb.Q.take(1)
2104
+ import_watermelondb2.Q.where("conversation_id", opts.conversationId),
2105
+ import_watermelondb2.Q.sortBy("message_id", import_watermelondb2.Q.desc),
2106
+ import_watermelondb2.Q.take(1)
2061
2107
  ).fetch();
2062
2108
  const messageId = (newest[0]?.messageId ?? 0) + 1;
2063
2109
  const encryptedOpts = await encryptMessageOptsIfNeeded(ctx, opts);
@@ -2071,7 +2117,7 @@ async function createMessageOp(ctx, opts) {
2071
2117
  return messageToStored(created, ctx.walletAddress, ctx.signMessage, ctx.embeddedWalletSigner);
2072
2118
  }
2073
2119
  async function upsertMessageOp(ctx, opts) {
2074
- const results = await ctx.messagesCollection.query(import_watermelondb.Q.where("id", opts.uniqueId)).fetch();
2120
+ const results = await ctx.messagesCollection.query(import_watermelondb2.Q.where("id", opts.uniqueId)).fetch();
2075
2121
  const existing = results.length > 0 ? results[0] : null;
2076
2122
  if (!existing) {
2077
2123
  return createMessageOp(ctx, opts);
@@ -2244,12 +2290,12 @@ async function searchChunksOp(ctx, queryVector, options) {
2244
2290
  embeddingModel,
2245
2291
  chunkCache
2246
2292
  } = options || {};
2247
- const activeConversations = await ctx.conversationsCollection.query(import_watermelondb.Q.where("is_deleted", false)).fetch();
2293
+ const activeConversations = await ctx.conversationsCollection.query(import_watermelondb2.Q.where("is_deleted", false)).fetch();
2248
2294
  const activeConversationIds = new Set(
2249
2295
  // Use _getRaw for reliable raw column access
2250
2296
  activeConversations.map((c) => String(c._getRaw("conversation_id")))
2251
2297
  );
2252
- const queryConditions = conversationId ? [import_watermelondb.Q.where("conversation_id", conversationId)] : [];
2298
+ const queryConditions = conversationId ? [import_watermelondb2.Q.where("conversation_id", conversationId)] : [];
2253
2299
  const messages = await ctx.messagesCollection.query(...queryConditions).unsafeFetchRaw();
2254
2300
  const candidates = [];
2255
2301
  let staleSkipped = 0;
@@ -2413,17 +2459,18 @@ function makeSyntheticStoredConversation(opts, defaultTitle) {
2413
2459
  isDeleted: false
2414
2460
  };
2415
2461
  }
2416
- var import_watermelondb, import_uuid2;
2462
+ var import_watermelondb2, import_uuid2;
2417
2463
  var init_operations = __esm({
2418
2464
  "src/lib/db/chat/operations.ts"() {
2419
2465
  "use strict";
2420
- import_watermelondb = require("@nozbe/watermelondb");
2466
+ import_watermelondb2 = require("@nozbe/watermelondb");
2421
2467
  import_uuid2 = require("uuid");
2422
2468
  init_useEncryption();
2423
2469
  init_logger();
2424
2470
  init_vector();
2425
2471
  init_vectorEncoding();
2426
2472
  init_encryption_utils();
2473
+ init_models();
2427
2474
  init_conversationEncryption();
2428
2475
  init_encryption();
2429
2476
  init_types2();
@@ -4695,7 +4742,7 @@ async function upsertEntitiesInWrite(ctx, entities) {
4695
4742
  const unique = Array.from(kindByName.keys());
4696
4743
  const out = /* @__PURE__ */ new Map();
4697
4744
  if (unique.length === 0) return { entities: out, operations: [], kindBackfills: [] };
4698
- const existing = await ctx.entityCollection.query(import_watermelondb8.Q.where("canonical_name", import_watermelondb8.Q.oneOf(unique))).fetch();
4745
+ const existing = await ctx.entityCollection.query(import_watermelondb9.Q.where("canonical_name", import_watermelondb9.Q.oneOf(unique))).fetch();
4699
4746
  const existingNames = new Set(existing.map((e) => e.canonicalName));
4700
4747
  const kindBackfills = existing.filter((e) => {
4701
4748
  const incoming = kindByName.get(e.canonicalName);
@@ -4725,7 +4772,7 @@ function prepareKindBackfills(backfills) {
4725
4772
  }
4726
4773
  async function findVaultRowInWrite(ctx, memoryId) {
4727
4774
  try {
4728
- const rows = await ctx.database.get("memory_vault").query(import_watermelondb8.Q.where("id", memoryId)).fetch();
4775
+ const rows = await ctx.database.get("memory_vault").query(import_watermelondb9.Q.where("id", memoryId)).fetch();
4729
4776
  return rows[0] ?? null;
4730
4777
  } catch {
4731
4778
  return null;
@@ -4800,7 +4847,7 @@ async function linkMemoryEntitiesOp(ctx, memoryId, entityInputs, options) {
4800
4847
  return;
4801
4848
  }
4802
4849
  if (entities.length === 0) return;
4803
- const existingLinks = await ctx.memoryEntityCollection.query(import_watermelondb8.Q.where("memory_id", memoryId)).fetch();
4850
+ const existingLinks = await ctx.memoryEntityCollection.query(import_watermelondb9.Q.where("memory_id", memoryId)).fetch();
4804
4851
  const existingEntityIds = new Set(existingLinks.map((l) => String(l.entityId)));
4805
4852
  const toCreate = entities.filter((e) => !existingEntityIds.has(e.uniqueId));
4806
4853
  const keptIds = entities.map((e) => e.uniqueId);
@@ -4835,7 +4882,7 @@ async function linkMemoryEntitiesOp(ctx, memoryId, entityInputs, options) {
4835
4882
  }
4836
4883
  async function resolveEntitiesByIds(ctx, entityIds) {
4837
4884
  if (entityIds.length === 0) return [];
4838
- const rows = await ctx.entityCollection.query(import_watermelondb8.Q.where("id", import_watermelondb8.Q.oneOf(Array.from(entityIds)))).fetch();
4885
+ const rows = await ctx.entityCollection.query(import_watermelondb9.Q.where("id", import_watermelondb9.Q.oneOf(Array.from(entityIds)))).fetch();
4839
4886
  return rows.map(entityToStored);
4840
4887
  }
4841
4888
  function autoLinkBlocked(row) {
@@ -4870,7 +4917,7 @@ async function replaceMemoryEntities(ctx, memoryId, entityInputs, options) {
4870
4917
  kindBackfills
4871
4918
  } = await upsertEntitiesInWrite(ctx, normalized);
4872
4919
  entities = Array.from(byName.values());
4873
- const existingLinks = await ctx.memoryEntityCollection.query(import_watermelondb8.Q.where("memory_id", memoryId)).fetch();
4920
+ const existingLinks = await ctx.memoryEntityCollection.query(import_watermelondb9.Q.where("memory_id", memoryId)).fetch();
4874
4921
  const keep = new Set(entities.map((e) => e.uniqueId));
4875
4922
  const existingEntityIds = new Set(existingLinks.map((l) => String(l.entityId)));
4876
4923
  const toCreate = entities.filter((e) => !existingEntityIds.has(e.uniqueId));
@@ -4905,17 +4952,17 @@ async function replaceMemoryEntities(ctx, memoryId, entityInputs, options) {
4905
4952
  async function findOrphanedEntities(ctx, memoryId, toDestroy) {
4906
4953
  const candidateIds = [...new Set(toDestroy.map((l) => String(l.entityId)))];
4907
4954
  if (candidateIds.length === 0) return [];
4908
- const links = await ctx.memoryEntityCollection.query(import_watermelondb8.Q.where("entity_id", import_watermelondb8.Q.oneOf(candidateIds))).fetch();
4955
+ const links = await ctx.memoryEntityCollection.query(import_watermelondb9.Q.where("entity_id", import_watermelondb9.Q.oneOf(candidateIds))).fetch();
4909
4956
  const stillLinked = new Set(
4910
4957
  links.filter((l) => String(l.memoryId) !== memoryId).map((l) => String(l.entityId))
4911
4958
  );
4912
4959
  const orphanIds = candidateIds.filter((id) => !stillLinked.has(id));
4913
4960
  if (orphanIds.length === 0) return [];
4914
- return await ctx.entityCollection.query(import_watermelondb8.Q.where("id", import_watermelondb8.Q.oneOf(orphanIds))).fetch();
4961
+ return await ctx.entityCollection.query(import_watermelondb9.Q.where("id", import_watermelondb9.Q.oneOf(orphanIds))).fetch();
4915
4962
  }
4916
4963
  async function unlinkMemoryEntitiesOp(ctx, memoryIds) {
4917
4964
  if (memoryIds.length === 0) return;
4918
- const links = await ctx.memoryEntityCollection.query(import_watermelondb8.Q.where("memory_id", import_watermelondb8.Q.oneOf(Array.from(memoryIds)))).fetch();
4965
+ const links = await ctx.memoryEntityCollection.query(import_watermelondb9.Q.where("memory_id", import_watermelondb9.Q.oneOf(Array.from(memoryIds)))).fetch();
4919
4966
  if (links.length === 0) return;
4920
4967
  await ctx.database.write(async () => {
4921
4968
  await ctx.database.batch(...links.map((l) => l.prepareDestroyPermanently()));
@@ -4923,7 +4970,7 @@ async function unlinkMemoryEntitiesOp(ctx, memoryIds) {
4923
4970
  }
4924
4971
  async function unlinkAllMemoryEntitiesForUserOp(ctx, userId) {
4925
4972
  if (!userId) return;
4926
- const links = await ctx.memoryEntityCollection.query(import_watermelondb8.Q.where("user_id", userId)).fetch();
4973
+ const links = await ctx.memoryEntityCollection.query(import_watermelondb9.Q.where("user_id", userId)).fetch();
4927
4974
  if (links.length === 0) return;
4928
4975
  await ctx.database.write(async () => {
4929
4976
  await ctx.database.batch(...links.map((l) => l.prepareDestroyPermanently()));
@@ -4932,15 +4979,15 @@ async function unlinkAllMemoryEntitiesForUserOp(ctx, userId) {
4932
4979
  async function getMemoriesByEntityNamesOp(ctx, entityNames) {
4933
4980
  const unique = Array.from(new Set(entityNames.map(normalizeEntityName).filter((n) => n.length > 0)));
4934
4981
  if (unique.length === 0) return /* @__PURE__ */ new Map();
4935
- const entityRows = await ctx.entityCollection.query(import_watermelondb8.Q.where("canonical_name", import_watermelondb8.Q.oneOf(unique))).fetch();
4982
+ const entityRows = await ctx.entityCollection.query(import_watermelondb9.Q.where("canonical_name", import_watermelondb9.Q.oneOf(unique))).fetch();
4936
4983
  if (entityRows.length === 0) return /* @__PURE__ */ new Map();
4937
4984
  const entityIdToName = new Map(entityRows.map((e) => [e.id, e.canonicalName]));
4938
- const linkConditions = [import_watermelondb8.Q.where("entity_id", import_watermelondb8.Q.oneOf(entityRows.map((e) => e.id)))];
4985
+ const linkConditions = [import_watermelondb9.Q.where("entity_id", import_watermelondb9.Q.oneOf(entityRows.map((e) => e.id)))];
4939
4986
  if (ctx.userId !== void 0) {
4940
4987
  if (ctx.allowUnscopedRows) {
4941
- linkConditions.push(import_watermelondb8.Q.or(import_watermelondb8.Q.where("user_id", ctx.userId), import_watermelondb8.Q.where("user_id", null)));
4988
+ linkConditions.push(import_watermelondb9.Q.or(import_watermelondb9.Q.where("user_id", ctx.userId), import_watermelondb9.Q.where("user_id", null)));
4942
4989
  } else {
4943
- linkConditions.push(import_watermelondb8.Q.where("user_id", ctx.userId));
4990
+ linkConditions.push(import_watermelondb9.Q.where("user_id", ctx.userId));
4944
4991
  }
4945
4992
  }
4946
4993
  const links = await ctx.memoryEntityCollection.query(...linkConditions).fetch();
@@ -4961,18 +5008,18 @@ async function getMemoriesByEntityNamesOp(ctx, entityNames) {
4961
5008
  async function getEntitiesByMemoryIdsOp(ctx, memoryIds) {
4962
5009
  const unique = Array.from(new Set(memoryIds.filter((id) => id.length > 0)));
4963
5010
  if (unique.length === 0) return /* @__PURE__ */ new Map();
4964
- const linkConditions = [import_watermelondb8.Q.where("memory_id", import_watermelondb8.Q.oneOf(unique))];
5011
+ const linkConditions = [import_watermelondb9.Q.where("memory_id", import_watermelondb9.Q.oneOf(unique))];
4965
5012
  if (ctx.userId !== void 0) {
4966
5013
  if (ctx.allowUnscopedRows) {
4967
- linkConditions.push(import_watermelondb8.Q.or(import_watermelondb8.Q.where("user_id", ctx.userId), import_watermelondb8.Q.where("user_id", null)));
5014
+ linkConditions.push(import_watermelondb9.Q.or(import_watermelondb9.Q.where("user_id", ctx.userId), import_watermelondb9.Q.where("user_id", null)));
4968
5015
  } else {
4969
- linkConditions.push(import_watermelondb8.Q.where("user_id", ctx.userId));
5016
+ linkConditions.push(import_watermelondb9.Q.where("user_id", ctx.userId));
4970
5017
  }
4971
5018
  }
4972
5019
  const links = await ctx.memoryEntityCollection.query(...linkConditions).fetch();
4973
5020
  if (links.length === 0) return /* @__PURE__ */ new Map();
4974
5021
  const entityIds = Array.from(new Set(links.map((l) => String(l.entityId))));
4975
- const entityRows = await ctx.entityCollection.query(import_watermelondb8.Q.where("id", import_watermelondb8.Q.oneOf(entityIds))).fetch();
5022
+ const entityRows = await ctx.entityCollection.query(import_watermelondb9.Q.where("id", import_watermelondb9.Q.oneOf(entityIds))).fetch();
4976
5023
  const entityIdToName = new Map(entityRows.map((e) => [e.id, e.canonicalName]));
4977
5024
  const out = /* @__PURE__ */ new Map();
4978
5025
  for (const link of links) {
@@ -4988,11 +5035,11 @@ async function getEntitiesByMemoryIdsOp(ctx, memoryIds) {
4988
5035
  }
4989
5036
  return out;
4990
5037
  }
4991
- var import_watermelondb8;
5038
+ var import_watermelondb9;
4992
5039
  var init_operations2 = __esm({
4993
5040
  "src/lib/db/entities/operations.ts"() {
4994
5041
  "use strict";
4995
- import_watermelondb8 = require("@nozbe/watermelondb");
5042
+ import_watermelondb9 = require("@nozbe/watermelondb");
4996
5043
  init_types3();
4997
5044
  }
4998
5045
  });
@@ -5059,23 +5106,24 @@ function visibilityOrPrivate(value) {
5059
5106
  function visibilityConditions(requested) {
5060
5107
  if (!requested?.length) return [];
5061
5108
  if (!requested.includes("private")) {
5062
- return [import_watermelondb9.Q.where("visibility", import_watermelondb9.Q.oneOf([...requested]))];
5109
+ return [import_watermelondb10.Q.where("visibility", import_watermelondb10.Q.oneOf([...requested]))];
5063
5110
  }
5064
5111
  const excluded = NON_PRIVATE_VISIBILITIES.filter((v) => !requested.includes(v));
5065
5112
  if (excluded.length === 0) return [];
5066
- return [import_watermelondb9.Q.or(import_watermelondb9.Q.where("visibility", null), import_watermelondb9.Q.where("visibility", import_watermelondb9.Q.notIn(excluded)))];
5113
+ return [import_watermelondb10.Q.or(import_watermelondb10.Q.where("visibility", null), import_watermelondb10.Q.where("visibility", import_watermelondb10.Q.notIn(excluded)))];
5067
5114
  }
5068
5115
  function isOwnedByCtxUser(ctx, record) {
5069
5116
  return ctx.userId === void 0 || record.userId === ctx.userId;
5070
5117
  }
5071
5118
  function baseVaultConditions(ctx, options) {
5072
5119
  return [
5073
- ...options?.includeDeleted ? [] : [import_watermelondb9.Q.where("is_deleted", false)],
5074
- ...options?.includeArchived ? [] : [import_watermelondb9.Q.where("archived_at", import_watermelondb9.Q.eq(null))],
5075
- ...options?.includeQuarantined ? [] : [import_watermelondb9.Q.where("trust_tier", import_watermelondb9.Q.notEq("quarantined"))],
5076
- ...options?.includeSuperseded ? [] : [import_watermelondb9.Q.where("superseded_by", null)],
5077
- ...ctx.userId !== void 0 ? [import_watermelondb9.Q.where("user_id", ctx.userId)] : [],
5078
- ...options?.since ? [import_watermelondb9.Q.where("updated_at", import_watermelondb9.Q.gt(options.since.getTime()))] : []
5120
+ ...options?.memoryIds !== void 0 ? [import_watermelondb10.Q.where("id", import_watermelondb10.Q.oneOf(options.memoryIds))] : [],
5121
+ ...options?.includeDeleted ? [] : [import_watermelondb10.Q.where("is_deleted", false)],
5122
+ ...options?.includeArchived ? [] : [import_watermelondb10.Q.where("archived_at", import_watermelondb10.Q.eq(null))],
5123
+ ...options?.includeQuarantined ? [] : [import_watermelondb10.Q.where("trust_tier", import_watermelondb10.Q.notEq("quarantined"))],
5124
+ ...options?.includeSuperseded ? [] : [import_watermelondb10.Q.where("superseded_by", null)],
5125
+ ...ctx.userId !== void 0 ? [import_watermelondb10.Q.where("user_id", ctx.userId)] : [],
5126
+ ...options?.since ? [import_watermelondb10.Q.where("updated_at", import_watermelondb10.Q.gt(options.since.getTime()))] : []
5079
5127
  ];
5080
5128
  }
5081
5129
  function normalizeTrustTier(value) {
@@ -5153,6 +5201,8 @@ async function createVaultMemoryOp(ctx, opts) {
5153
5201
  ctx.embeddedWalletSigner
5154
5202
  ) : opts.content;
5155
5203
  const created = await ctx.database.write(async () => {
5204
+ if (ctx.canWrite && !await ctx.canWrite())
5205
+ throw new Error("Memory source is no longer eligible");
5156
5206
  return ctx.vaultMemoryCollection.create((record) => {
5157
5207
  record._setRaw("content", encryptedContent);
5158
5208
  record._setRaw("scope", scope);
@@ -5202,6 +5252,8 @@ async function createSupersedingMemoryOp(ctx, opts, targetId) {
5202
5252
  ) : opts.content;
5203
5253
  let createdRecord = null;
5204
5254
  await ctx.database.write(async () => {
5255
+ if (ctx.canWrite && !await ctx.canWrite())
5256
+ throw new Error("Memory source is no longer eligible");
5205
5257
  let target;
5206
5258
  try {
5207
5259
  target = await ctx.vaultMemoryCollection.find(targetId);
@@ -5247,11 +5299,11 @@ async function createSupersedingMemoryOp(ctx, opts, targetId) {
5247
5299
  async function getMemoriesByEventTimeOp(ctx, windowStart, windowEnd) {
5248
5300
  const records = await ctx.vaultMemoryCollection.query(
5249
5301
  ...baseVaultConditions(ctx),
5250
- import_watermelondb9.Q.where("event_time_start", import_watermelondb9.Q.notEq(null)),
5251
- import_watermelondb9.Q.where("event_time_start", import_watermelondb9.Q.lte(windowEnd)),
5252
- import_watermelondb9.Q.or(
5253
- import_watermelondb9.Q.where("event_time_start", import_watermelondb9.Q.gte(windowStart)),
5254
- import_watermelondb9.Q.where("event_time_kind", import_watermelondb9.Q.oneOf(["range", "ongoing"]))
5302
+ import_watermelondb10.Q.where("event_time_start", import_watermelondb10.Q.notEq(null)),
5303
+ import_watermelondb10.Q.where("event_time_start", import_watermelondb10.Q.lte(windowEnd)),
5304
+ import_watermelondb10.Q.or(
5305
+ import_watermelondb10.Q.where("event_time_start", import_watermelondb10.Q.gte(windowStart)),
5306
+ import_watermelondb10.Q.where("event_time_kind", import_watermelondb10.Q.oneOf(["range", "ongoing"]))
5255
5307
  )
5256
5308
  ).fetch();
5257
5309
  const out = [];
@@ -5306,6 +5358,8 @@ async function createVaultMemoriesBatchOp(ctx, optionsArray) {
5306
5358
  })
5307
5359
  );
5308
5360
  const created = await ctx.database.write(async () => {
5361
+ if (ctx.canWrite && !await ctx.canWrite())
5362
+ throw new Error("Memory source is no longer eligible");
5309
5363
  const prepared = optionsArray.map(
5310
5364
  (opts, i) => ctx.vaultMemoryCollection.prepareCreate((record) => {
5311
5365
  record._setRaw("content", encryptedContents[i]);
@@ -5426,12 +5480,12 @@ async function vaultMemoryRawToStored(raw, walletAddress, signMessage, embeddedW
5426
5480
  async function getAllVaultMemoriesOp(ctx, options) {
5427
5481
  const conditions = [
5428
5482
  ...baseVaultConditions(ctx, options),
5429
- ...options?.scopes?.length ? [import_watermelondb9.Q.where("scope", import_watermelondb9.Q.oneOf(options.scopes))] : [],
5483
+ ...options?.scopes?.length ? [import_watermelondb10.Q.where("scope", import_watermelondb10.Q.oneOf(options.scopes))] : [],
5430
5484
  ...visibilityConditions(options?.visibility),
5431
- ...options?.folderId !== void 0 ? [import_watermelondb9.Q.where("folder_id", options.folderId)] : [],
5432
- ...options?.factTypes?.length ? [import_watermelondb9.Q.where("fact_type", import_watermelondb9.Q.oneOf(options.factTypes))] : [],
5433
- import_watermelondb9.Q.sortBy(options?.since ? "updated_at" : "created_at", import_watermelondb9.Q.desc),
5434
- ...options?.limit !== null && options?.limit !== void 0 && options.limit > 0 ? [import_watermelondb9.Q.take(options.limit)] : []
5485
+ ...options?.folderId !== void 0 ? [import_watermelondb10.Q.where("folder_id", options.folderId)] : [],
5486
+ ...options?.factTypes?.length ? [import_watermelondb10.Q.where("fact_type", import_watermelondb10.Q.oneOf(options.factTypes))] : [],
5487
+ import_watermelondb10.Q.sortBy(options?.since ? "updated_at" : "created_at", import_watermelondb10.Q.desc),
5488
+ ...options?.limit !== null && options?.limit !== void 0 && options.limit > 0 ? [import_watermelondb10.Q.take(options.limit)] : []
5435
5489
  ];
5436
5490
  const results = await ctx.vaultMemoryCollection.query(...conditions).unsafeFetchRaw();
5437
5491
  return mapInBatches(
@@ -5454,10 +5508,10 @@ function vaultMemoryRawToRankable(raw) {
5454
5508
  async function getVaultRankingProjectionsOp(ctx, options) {
5455
5509
  const conditions = [
5456
5510
  ...baseVaultConditions(ctx, options),
5457
- ...options?.scopes?.length ? [import_watermelondb9.Q.where("scope", import_watermelondb9.Q.oneOf(options.scopes))] : [],
5458
- ...options?.folderId !== void 0 ? [import_watermelondb9.Q.where("folder_id", options.folderId)] : [],
5459
- import_watermelondb9.Q.sortBy(options?.since ? "updated_at" : "created_at", import_watermelondb9.Q.desc),
5460
- ...options?.limit !== null && options?.limit !== void 0 && options.limit > 0 ? [import_watermelondb9.Q.take(options.limit)] : []
5511
+ ...options?.scopes?.length ? [import_watermelondb10.Q.where("scope", import_watermelondb10.Q.oneOf(options.scopes))] : [],
5512
+ ...options?.folderId !== void 0 ? [import_watermelondb10.Q.where("folder_id", options.folderId)] : [],
5513
+ import_watermelondb10.Q.sortBy(options?.since ? "updated_at" : "created_at", import_watermelondb10.Q.desc),
5514
+ ...options?.limit !== null && options?.limit !== void 0 && options.limit > 0 ? [import_watermelondb10.Q.take(options.limit)] : []
5461
5515
  ];
5462
5516
  const results = await ctx.vaultMemoryCollection.query(...conditions).unsafeFetchRaw();
5463
5517
  return results.map(vaultMemoryRawToRankable);
@@ -5492,6 +5546,11 @@ async function getVaultCandidateKeysOp(ctx, options) {
5492
5546
  });
5493
5547
  const clauses = [base.sql];
5494
5548
  const args = [...base.args];
5549
+ if (options?.memoryIds !== void 0) {
5550
+ if (options.memoryIds.length === 0) return [];
5551
+ clauses.push(`"id" in (${options.memoryIds.map(() => "?").join(",")})`);
5552
+ args.push(...options.memoryIds);
5553
+ }
5495
5554
  if (options?.scopes?.length) {
5496
5555
  clauses.push(`"scope" in (${options.scopes.map(() => "?").join(",")})`);
5497
5556
  args.push(...options.scopes);
@@ -5505,7 +5564,7 @@ async function getVaultCandidateKeysOp(ctx, options) {
5505
5564
  args.push(...options.factTypes);
5506
5565
  }
5507
5566
  const sql = `select "id", "scope", "folder_id", "embedding_model", "updated_at" from "memory_vault" where ${clauses.join(" and ")}`;
5508
- const rows = await ctx.vaultMemoryCollection.query(import_watermelondb9.Q.unsafeSqlQuery(sql, args)).unsafeFetchRaw();
5567
+ const rows = await ctx.vaultMemoryCollection.query(import_watermelondb10.Q.unsafeSqlQuery(sql, args)).unsafeFetchRaw();
5509
5568
  return rows.map(mapRaw);
5510
5569
  } catch (err) {
5511
5570
  getLogger().debug(
@@ -5513,11 +5572,12 @@ async function getVaultCandidateKeysOp(ctx, options) {
5513
5572
  );
5514
5573
  const conditions = [
5515
5574
  ...baseVaultConditions(ctx, {
5575
+ memoryIds: options?.memoryIds,
5516
5576
  ...options?.includeArchived !== void 0 && { includeArchived: options.includeArchived }
5517
5577
  }),
5518
- ...options?.scopes?.length ? [import_watermelondb9.Q.where("scope", import_watermelondb9.Q.oneOf(options.scopes))] : [],
5519
- ...options?.folderId !== void 0 ? [import_watermelondb9.Q.where("folder_id", options.folderId)] : [],
5520
- ...options?.factTypes?.length ? [import_watermelondb9.Q.where("fact_type", import_watermelondb9.Q.oneOf(options.factTypes))] : []
5578
+ ...options?.scopes?.length ? [import_watermelondb10.Q.where("scope", import_watermelondb10.Q.oneOf(options.scopes))] : [],
5579
+ ...options?.folderId !== void 0 ? [import_watermelondb10.Q.where("folder_id", options.folderId)] : [],
5580
+ ...options?.factTypes?.length ? [import_watermelondb10.Q.where("fact_type", import_watermelondb10.Q.oneOf(options.factTypes))] : []
5521
5581
  ];
5522
5582
  const rows = await ctx.vaultMemoryCollection.query(...conditions).unsafeFetchRaw();
5523
5583
  return rows.map(mapRaw);
@@ -5535,7 +5595,7 @@ async function getVaultEmbeddingsByIdsOp(ctx, ids, options) {
5535
5595
  ...options?.includeArchived !== void 0 && { includeArchived: options.includeArchived }
5536
5596
  });
5537
5597
  const sql = `select "id", "embedding", "embedding_model" from "memory_vault" where ${base.sql} and "id" in (${ids.map(() => "?").join(",")})`;
5538
- const rows = await ctx.vaultMemoryCollection.query(import_watermelondb9.Q.unsafeSqlQuery(sql, [...base.args, ...ids])).unsafeFetchRaw();
5598
+ const rows = await ctx.vaultMemoryCollection.query(import_watermelondb10.Q.unsafeSqlQuery(sql, [...base.args, ...ids])).unsafeFetchRaw();
5539
5599
  return rows.map(mapRaw);
5540
5600
  } catch (err) {
5541
5601
  getLogger().debug(
@@ -5547,7 +5607,7 @@ async function getVaultEmbeddingsByIdsOp(ctx, ids, options) {
5547
5607
  includeArchived: options.includeArchived
5548
5608
  }
5549
5609
  }),
5550
- import_watermelondb9.Q.where("id", import_watermelondb9.Q.oneOf(ids))
5610
+ import_watermelondb10.Q.where("id", import_watermelondb10.Q.oneOf(ids))
5551
5611
  ).unsafeFetchRaw();
5552
5612
  return rows.map(mapRaw);
5553
5613
  }
@@ -5558,7 +5618,7 @@ async function getVaultMemoriesByIdsOp(ctx, ids, options) {
5558
5618
  ...baseVaultConditions(ctx, {
5559
5619
  ...options?.includeArchived !== void 0 && { includeArchived: options.includeArchived }
5560
5620
  }),
5561
- import_watermelondb9.Q.where("id", import_watermelondb9.Q.oneOf(ids))
5621
+ import_watermelondb10.Q.where("id", import_watermelondb10.Q.oneOf(ids))
5562
5622
  ];
5563
5623
  const results = await ctx.vaultMemoryCollection.query(...conditions).unsafeFetchRaw();
5564
5624
  return mapInBatches(
@@ -5583,7 +5643,7 @@ async function countActiveVaultMemoriesOp(ctx) {
5583
5643
  }
5584
5644
  async function getActiveVaultMemoryIdsOp(ctx, ids) {
5585
5645
  if (ids.length === 0) return /* @__PURE__ */ new Set();
5586
- const rows = await ctx.vaultMemoryCollection.query(...baseVaultConditions(ctx), import_watermelondb9.Q.where("id", import_watermelondb9.Q.oneOf(ids))).unsafeFetchRaw();
5646
+ const rows = await ctx.vaultMemoryCollection.query(...baseVaultConditions(ctx), import_watermelondb10.Q.where("id", import_watermelondb10.Q.oneOf(ids))).unsafeFetchRaw();
5587
5647
  return new Set(rows.map((r) => r.id));
5588
5648
  }
5589
5649
  async function updateVaultMemoryOp(ctx, id, opts) {
@@ -5600,10 +5660,23 @@ async function updateVaultMemoryOp(ctx, id, opts) {
5600
5660
  const record = probe;
5601
5661
  const originalUpdatedAt = record.updatedAt.getTime();
5602
5662
  await ctx.database.write(async () => {
5663
+ if (ctx.canWrite && !await ctx.canWrite())
5664
+ throw new Error("Memory source is no longer eligible");
5603
5665
  if (record.isDeleted || record.supersededBy || !isOwnedByCtxUser(ctx, record)) {
5604
5666
  stale = true;
5605
5667
  return;
5606
5668
  }
5669
+ let observedSources = [];
5670
+ let replayedObservation = false;
5671
+ if (opts.observationSourceIds?.length) {
5672
+ try {
5673
+ const parsed = JSON.parse(record.sourceChunkIds ?? "[]");
5674
+ if (Array.isArray(parsed))
5675
+ observedSources = parsed.filter((id2) => typeof id2 === "string");
5676
+ } catch {
5677
+ }
5678
+ replayedObservation = opts.observationSourceIds.every((id2) => observedSources.includes(id2));
5679
+ }
5607
5680
  await record.update((r) => {
5608
5681
  r._setRaw("content", encryptedContent);
5609
5682
  if (opts.scope !== void 0) {
@@ -5617,9 +5690,15 @@ async function updateVaultMemoryOp(ctx, id, opts) {
5617
5690
  r._setRaw("embedding_model", opts.embeddingModel ?? null);
5618
5691
  }
5619
5692
  if (opts.sourceChunkIds !== void 0) {
5620
- r._setRaw("source_chunk_ids", JSON.stringify(opts.sourceChunkIds));
5693
+ r._setRaw(
5694
+ "source_chunk_ids",
5695
+ JSON.stringify(
5696
+ opts.observationSourceIds?.length ? [.../* @__PURE__ */ new Set([...observedSources, ...opts.sourceChunkIds])] : opts.sourceChunkIds
5697
+ )
5698
+ );
5621
5699
  }
5622
- if (opts.proofCountIncrement !== void 0) {
5700
+ if (replayedObservation) {
5701
+ } else if (opts.proofCountIncrement !== void 0) {
5623
5702
  const current = r.proofCount ?? 1;
5624
5703
  r._setRaw("proof_count", current + opts.proofCountIncrement);
5625
5704
  } else if (opts.proofCount !== void 0) {
@@ -5636,7 +5715,7 @@ async function updateVaultMemoryOp(ctx, id, opts) {
5636
5715
  if (opts.topicsUserManaged !== void 0) {
5637
5716
  r._setRaw("topics_user_managed", opts.topicsUserManaged);
5638
5717
  }
5639
- if (opts.lastObservedAt !== void 0) {
5718
+ if (opts.lastObservedAt !== void 0 && !replayedObservation) {
5640
5719
  r._setRaw("last_observed_at", opts.lastObservedAt);
5641
5720
  }
5642
5721
  if (opts.factType !== void 0) {
@@ -5762,6 +5841,8 @@ async function supersedeVaultMemoryOp(ctx, id, supersededById) {
5762
5841
  }
5763
5842
  let stale = false;
5764
5843
  await ctx.database.write(async () => {
5844
+ if (ctx.canWrite && !await ctx.canWrite())
5845
+ throw new Error("Memory source is no longer eligible");
5765
5846
  if (record.isDeleted || record.supersededBy || !isOwnedByCtxUser(ctx, record)) {
5766
5847
  stale = true;
5767
5848
  return;
@@ -5782,9 +5863,9 @@ async function supersedeVaultMemoryOp(ctx, id, supersededById) {
5782
5863
  }
5783
5864
  async function getUnfiledVaultMemoriesOp(ctx) {
5784
5865
  const conditions = [
5785
- import_watermelondb9.Q.where("folder_id", null),
5866
+ import_watermelondb10.Q.where("folder_id", null),
5786
5867
  ...baseVaultConditions(ctx),
5787
- import_watermelondb9.Q.sortBy("created_at", import_watermelondb9.Q.desc)
5868
+ import_watermelondb10.Q.sortBy("created_at", import_watermelondb10.Q.desc)
5788
5869
  ];
5789
5870
  const results = await ctx.vaultMemoryCollection.query(...conditions).fetch();
5790
5871
  return mapInBatches(
@@ -5797,7 +5878,7 @@ async function linkedEntityNamesByMemory(entityCtx, memoryIds) {
5797
5878
  const entityIdByMemory = /* @__PURE__ */ new Map();
5798
5879
  const allEntityIds = /* @__PURE__ */ new Set();
5799
5880
  for (let i = 0; i < memoryIds.length; i += CHUNK) {
5800
- const links = await entityCtx.memoryEntityCollection.query(import_watermelondb9.Q.where("memory_id", import_watermelondb9.Q.oneOf(memoryIds.slice(i, i + CHUNK)))).unsafeFetchRaw();
5881
+ const links = await entityCtx.memoryEntityCollection.query(import_watermelondb10.Q.where("memory_id", import_watermelondb10.Q.oneOf(memoryIds.slice(i, i + CHUNK)))).unsafeFetchRaw();
5801
5882
  for (const link of links) {
5802
5883
  const memoryId = String(link.memory_id);
5803
5884
  const entityId = String(link.entity_id);
@@ -5813,7 +5894,7 @@ async function linkedEntityNamesByMemory(entityCtx, memoryIds) {
5813
5894
  const entityIds = Array.from(allEntityIds);
5814
5895
  const nameById = /* @__PURE__ */ new Map();
5815
5896
  for (let i = 0; i < entityIds.length; i += CHUNK) {
5816
- const entities = await entityCtx.entityCollection.query(import_watermelondb9.Q.where("id", import_watermelondb9.Q.oneOf(entityIds.slice(i, i + CHUNK)))).unsafeFetchRaw();
5897
+ const entities = await entityCtx.entityCollection.query(import_watermelondb10.Q.where("id", import_watermelondb10.Q.oneOf(entityIds.slice(i, i + CHUNK)))).unsafeFetchRaw();
5817
5898
  for (const e of entities) {
5818
5899
  if (typeof e.canonical_name === "string") nameById.set(String(e.id), e.canonical_name);
5819
5900
  }
@@ -5842,7 +5923,7 @@ async function getMemoriesNeedingTopicExtractionOp(ctx, options) {
5842
5923
  if (!entityCtx) {
5843
5924
  throw new Error("getMemoriesNeedingTopicExtractionOp requires ctx.entityCtx");
5844
5925
  }
5845
- const conditions = [...baseVaultConditions(ctx), import_watermelondb9.Q.sortBy("created_at", import_watermelondb9.Q.desc)];
5926
+ const conditions = [...baseVaultConditions(ctx), import_watermelondb10.Q.sortBy("created_at", import_watermelondb10.Q.desc)];
5846
5927
  const rows = await ctx.vaultMemoryCollection.query(...conditions).unsafeFetchRaw();
5847
5928
  const linkedNames = await linkedEntityNamesByMemory(
5848
5929
  entityCtx,
@@ -5993,10 +6074,10 @@ async function backfillMemoryTopicsOp(ctx, memoryIds) {
5993
6074
  }
5994
6075
  if (record.isDeleted || !isOwnedByCtxUser(ctx, record)) continue;
5995
6076
  if (parseTopics(record.topics) !== null) continue;
5996
- const links = await entityCtx.memoryEntityCollection.query(import_watermelondb9.Q.where("memory_id", id)).unsafeFetchRaw();
6077
+ const links = await entityCtx.memoryEntityCollection.query(import_watermelondb10.Q.where("memory_id", id)).unsafeFetchRaw();
5997
6078
  const entityIds = links.map((l) => String(l.entity_id));
5998
6079
  if (entityIds.length === 0) continue;
5999
- const entities = await entityCtx.entityCollection.query(import_watermelondb9.Q.where("id", import_watermelondb9.Q.oneOf(entityIds))).fetch();
6080
+ const entities = await entityCtx.entityCollection.query(import_watermelondb10.Q.where("id", import_watermelondb10.Q.oneOf(entityIds))).fetch();
6000
6081
  if (entities.length === 0) continue;
6001
6082
  const source = record.topicsUserManaged ? "user" : "auto";
6002
6083
  const write = await resolveMemoryTopicsWrite(entityCtx, id, entities, [], source);
@@ -6012,7 +6093,7 @@ async function backfillMemoryTopicsOp(ctx, memoryIds) {
6012
6093
  }
6013
6094
  async function deleteAllVaultMemoriesForUserOp(ctx, userId) {
6014
6095
  if (ctx.userId !== void 0 && ctx.userId !== userId) return 0;
6015
- const records = await ctx.vaultMemoryCollection.query(import_watermelondb9.Q.where("user_id", userId), import_watermelondb9.Q.where("is_deleted", false)).fetch();
6096
+ const records = await ctx.vaultMemoryCollection.query(import_watermelondb10.Q.where("user_id", userId), import_watermelondb10.Q.where("is_deleted", false)).fetch();
6016
6097
  if (records.length === 0) return 0;
6017
6098
  await ctx.database.write(async () => {
6018
6099
  const prepared = records.map(
@@ -6053,6 +6134,7 @@ async function getDecayCandidatesRawOp(ctx) {
6053
6134
  eventTimeEnd: raw.event_time_end ?? null,
6054
6135
  eventTimeKind: raw.event_time_kind ?? null,
6055
6136
  updatedAt: raw.updated_at,
6137
+ lastObservedAt: raw.last_observed_at ?? null,
6056
6138
  archivedAt: raw.archived_at ?? null,
6057
6139
  source: raw.source ?? null,
6058
6140
  trustTier: raw.trust_tier ?? null
@@ -6069,7 +6151,7 @@ async function archiveVaultMemoryOp(ctx, id, opts) {
6069
6151
  stale = true;
6070
6152
  return;
6071
6153
  }
6072
- if (opts?.expectedUpdatedAt !== void 0 && record.updatedAt.getTime() !== opts.expectedUpdatedAt) {
6154
+ if (opts?.expectedUpdatedAt !== void 0 && record.updatedAt.getTime() !== opts.expectedUpdatedAt || opts?.expectedLastObservedAt !== void 0 && (record.lastObservedAt ?? null) !== opts.expectedLastObservedAt) {
6073
6155
  stale = true;
6074
6156
  return;
6075
6157
  }
@@ -6144,11 +6226,11 @@ async function updateVaultMemoryEmbeddingOp(ctx, id, embedding, embeddingModel)
6144
6226
  return false;
6145
6227
  }
6146
6228
  }
6147
- var import_watermelondb9, NON_PRIVATE_VISIBILITIES, KNOWN_TRUST_TIERS, TOPICS_EXTRACTION_VERSION;
6229
+ var import_watermelondb10, NON_PRIVATE_VISIBILITIES, KNOWN_TRUST_TIERS, TOPICS_EXTRACTION_VERSION;
6148
6230
  var init_operations3 = __esm({
6149
6231
  "src/lib/db/memoryVault/operations.ts"() {
6150
6232
  "use strict";
6151
- import_watermelondb9 = require("@nozbe/watermelondb");
6233
+ import_watermelondb10 = require("@nozbe/watermelondb");
6152
6234
  init_logger();
6153
6235
  init_operations2();
6154
6236
  init_types3();
@@ -7654,7 +7736,9 @@ function dedupeBy(items, ...keys) {
7654
7736
  return out;
7655
7737
  }
7656
7738
  async function recall(query, ctx, options = {}) {
7657
- const types = options.types ?? ["fact"];
7739
+ const requestedTypes = options.types ?? ["fact"];
7740
+ const chunksScopeRestricted = options.memoryIds !== void 0 && requestedTypes.includes("chunk");
7741
+ const types = options.memoryIds !== void 0 ? requestedTypes.filter((kind) => kind === "fact") : requestedTypes;
7658
7742
  const limit = options.limit ?? DEFAULT_LIMIT;
7659
7743
  const usedBudget = options.budget ?? DEFAULT_BUDGET;
7660
7744
  const flags = flagsForBudget(usedBudget);
@@ -7706,7 +7790,10 @@ async function recall(query, ctx, options = {}) {
7706
7790
  if (embeddingsUnavailable) degraded.push("embeddings-unavailable");
7707
7791
  if (graphLaneFailed) degraded.push("graph-lane-failed");
7708
7792
  if (temporalLaneFailed) degraded.push("temporal-lane-failed");
7709
- const laneRan = types.includes("fact") && !!ctx.vaultCtx && !!ctx.vaultCache || types.includes("chunk") && !!ctx.storageCtx;
7793
+ if (chunksScopeRestricted) degraded.push("chunks-scope-restricted");
7794
+ const laneRan = types.includes("fact") && !!ctx.vaultCtx && !!ctx.vaultCache || types.includes("chunk") && !!ctx.storageCtx || // The chunk lane was wired and deliberately dropped for the scope, which
7795
+ // is not the "requested kinds have no store" wiring bug `no-lanes` means.
7796
+ chunksScopeRestricted && !!ctx.storageCtx;
7710
7797
  const scores = admitted.map((m) => m.score);
7711
7798
  const minScoreApplied = factResults.length > 0 ? factFloor : chunkResults.length > 0 ? chunkFloor : factFloor >= 0 ? factFloor : chunkFloor;
7712
7799
  const diagnostics = {
@@ -7851,6 +7938,7 @@ async function recall(query, ctx, options = {}) {
7851
7938
  ...options.scopes && { scopes: options.scopes },
7852
7939
  ...options.folderId !== void 0 && { folderId: options.folderId },
7853
7940
  ...options.factTypes?.length && { factTypes: options.factTypes },
7941
+ ...options.memoryIds !== void 0 && { memoryIds: options.memoryIds },
7854
7942
  ...options.factTypeWeights && { factTypeWeights: options.factTypeWeights },
7855
7943
  ...entityRanking.length > 0 && { entityRanking },
7856
7944
  ...temporalRanking.length > 0 && { temporalRanking }
@@ -8854,6 +8942,7 @@ async function prepareVaultCandidates(query, vaultCtx, embeddingOptions, cache,
8854
8942
  const scopes = searchOptions?.scopes;
8855
8943
  const folderId = searchOptions?.folderId;
8856
8944
  const queryOpts = {};
8945
+ if (searchOptions?.memoryIds !== void 0) queryOpts.memoryIds = searchOptions.memoryIds;
8857
8946
  if (scopes?.length) queryOpts.scopes = scopes;
8858
8947
  if (folderId !== void 0) queryOpts.folderId = folderId;
8859
8948
  if (searchOptions?.factTypes?.length) queryOpts.factTypes = searchOptions.factTypes;
@@ -9715,6 +9804,7 @@ __export(index_exports, {
9715
9804
  WalletPoller: () => WalletPoller,
9716
9805
  addConversationMemoriesOp: () => addConversationMemoriesOp,
9717
9806
  archiveVaultMemoryOp: () => archiveVaultMemoryOp,
9807
+ assembleMemoryContext: () => assembleMemoryContext,
9718
9808
  backfillMemoryTopicsOp: () => backfillMemoryTopicsOp,
9719
9809
  buildNotionAuthUrl: () => buildNotionAuthUrl,
9720
9810
  capHopsForDensity: () => capHopsForDensity,
@@ -9747,6 +9837,7 @@ __export(index_exports, {
9747
9837
  createChunkVectorCache: () => createChunkVectorCache,
9748
9838
  createCryptoPricePreProcessor: () => createCryptoPricePreProcessor,
9749
9839
  createDecaySweeper: () => createDecaySweeper,
9840
+ createDurableAutoExtractor: () => createDurableAutoExtractor,
9750
9841
  createLlmDecayClassifier: () => createLlmDecayClassifier,
9751
9842
  createLlmNeighborRefiner: () => createLlmNeighborRefiner,
9752
9843
  createMemoryEngineTool: () => createMemoryEngineTool,
@@ -9767,6 +9858,7 @@ __export(index_exports, {
9767
9858
  decodeChunkVector: () => decodeChunkVector,
9768
9859
  decryptConversationTitle: () => decryptConversationTitle,
9769
9860
  deleteAllVaultMemoriesForUserOp: () => deleteAllVaultMemoriesForUserOp,
9861
+ deleteMessageOp: () => deleteMessageOp,
9770
9862
  deleteVaultFolderOp: () => deleteVaultFolderOp,
9771
9863
  deleteVaultMemoryOp: () => deleteVaultMemoryOp,
9772
9864
  dequantizeEmbedding: () => dequantizeEmbedding,
@@ -9839,6 +9931,7 @@ __export(index_exports, {
9839
9931
  setLogger: () => setLogger,
9840
9932
  setMemoryVisibilityOp: () => setMemoryVisibilityOp,
9841
9933
  shouldChunkMessage: () => shouldChunkMessage,
9934
+ shouldRecallMemory: () => shouldRecallMemory,
9842
9935
  stampTopicsExtractedAtOp: () => stampTopicsExtractedAtOp,
9843
9936
  streamCancelPath: () => streamCancelPath,
9844
9937
  streamReplayPath: () => streamReplayPath,
@@ -14313,7 +14406,7 @@ function extractSourcesFromToolCallEvents(toolCallEvents) {
14313
14406
  init_clientConfig();
14314
14407
 
14315
14408
  // src/lib/db/chat/summaryOperations.ts
14316
- var import_watermelondb2 = require("@nozbe/watermelondb");
14409
+ var import_watermelondb3 = require("@nozbe/watermelondb");
14317
14410
  function createSummaryContext(database) {
14318
14411
  return {
14319
14412
  database,
@@ -14332,14 +14425,14 @@ function modelToStored(record) {
14332
14425
  };
14333
14426
  }
14334
14427
  async function getConversationSummaryOp(ctx, conversationId) {
14335
- const results = await ctx.summariesCollection.query(import_watermelondb2.Q.where("conversation_id", conversationId)).fetch();
14428
+ const results = await ctx.summariesCollection.query(import_watermelondb3.Q.where("conversation_id", conversationId)).fetch();
14336
14429
  if (results.length === 0) return null;
14337
14430
  return modelToStored(results[0]);
14338
14431
  }
14339
14432
  async function upsertConversationSummaryOp(ctx, conversationId, summary, summarizedUpTo, tokenCount) {
14340
14433
  let record;
14341
14434
  await ctx.database.write(async () => {
14342
- const existing = await ctx.summariesCollection.query(import_watermelondb2.Q.where("conversation_id", conversationId)).fetch();
14435
+ const existing = await ctx.summariesCollection.query(import_watermelondb3.Q.where("conversation_id", conversationId)).fetch();
14343
14436
  if (existing.length > 0) {
14344
14437
  record = await existing[0].update((rec) => {
14345
14438
  rec.summary = summary;
@@ -14359,7 +14452,7 @@ async function upsertConversationSummaryOp(ctx, conversationId, summary, summari
14359
14452
  }
14360
14453
  async function deleteConversationSummaryOp(ctx, conversationId) {
14361
14454
  await ctx.database.write(async () => {
14362
- const existing = await ctx.summariesCollection.query(import_watermelondb2.Q.where("conversation_id", conversationId)).fetch();
14455
+ const existing = await ctx.summariesCollection.query(import_watermelondb3.Q.where("conversation_id", conversationId)).fetch();
14363
14456
  for (const record of existing) {
14364
14457
  await record.destroyPermanently();
14365
14458
  }
@@ -14851,9 +14944,9 @@ async function decryptConversationTitle(encryptedTitle, address) {
14851
14944
  }
14852
14945
 
14853
14946
  // src/lib/db/chat/models.ts
14854
- var import_watermelondb3 = require("@nozbe/watermelondb");
14947
+ var import_watermelondb4 = require("@nozbe/watermelondb");
14855
14948
  var import_decorators = require("@nozbe/watermelondb/decorators");
14856
- var Message = class extends import_watermelondb3.Model {
14949
+ var Message = class extends import_watermelondb4.Model {
14857
14950
  };
14858
14951
  Message.table = "history";
14859
14952
  Message.associations = {
@@ -14932,7 +15025,7 @@ __decorateClass([
14932
15025
  __decorateClass([
14933
15026
  (0, import_decorators.text)("origin")
14934
15027
  ], Message.prototype, "origin", 2);
14935
- var Conversation = class extends import_watermelondb3.Model {
15028
+ var Conversation = class extends import_watermelondb4.Model {
14936
15029
  };
14937
15030
  Conversation.table = "conversations";
14938
15031
  Conversation.associations = {
@@ -14960,7 +15053,7 @@ __decorateClass([
14960
15053
  __decorateClass([
14961
15054
  (0, import_decorators.date)("pinned_at")
14962
15055
  ], Conversation.prototype, "pinnedAt", 2);
14963
- var ConversationSummary = class extends import_watermelondb3.Model {
15056
+ var ConversationSummary = class extends import_watermelondb4.Model {
14964
15057
  };
14965
15058
  ConversationSummary.table = "conversation_summaries";
14966
15059
  ConversationSummary.associations = {
@@ -14989,12 +15082,12 @@ __decorateClass([
14989
15082
  init_operations();
14990
15083
 
14991
15084
  // src/lib/db/chat/schema.ts
14992
- var import_watermelondb4 = require("@nozbe/watermelondb");
15085
+ var import_watermelondb5 = require("@nozbe/watermelondb");
14993
15086
  var import_migrations = require("@nozbe/watermelondb/Schema/migrations");
14994
- var chatStorageSchema = (0, import_watermelondb4.appSchema)({
15087
+ var chatStorageSchema = (0, import_watermelondb5.appSchema)({
14995
15088
  version: 7,
14996
15089
  tables: [
14997
- (0, import_watermelondb4.tableSchema)({
15090
+ (0, import_watermelondb5.tableSchema)({
14998
15091
  name: "history",
14999
15092
  columns: [
15000
15093
  { name: "message_id", type: "number" },
@@ -15021,7 +15114,7 @@ var chatStorageSchema = (0, import_watermelondb4.appSchema)({
15021
15114
  // Reasoning/thinking content
15022
15115
  ]
15023
15116
  }),
15024
- (0, import_watermelondb4.tableSchema)({
15117
+ (0, import_watermelondb5.tableSchema)({
15025
15118
  name: "conversations",
15026
15119
  columns: [
15027
15120
  { name: "conversation_id", type: "string", isIndexed: true },
@@ -15096,9 +15189,9 @@ var chatStorageMigrations = (0, import_migrations.schemaMigrations)({
15096
15189
  init_types2();
15097
15190
 
15098
15191
  // src/lib/db/media/models.ts
15099
- var import_watermelondb5 = require("@nozbe/watermelondb");
15192
+ var import_watermelondb6 = require("@nozbe/watermelondb");
15100
15193
  var import_decorators2 = require("@nozbe/watermelondb/decorators");
15101
- var Media = class extends import_watermelondb5.Model {
15194
+ var Media = class extends import_watermelondb6.Model {
15102
15195
  };
15103
15196
  Media.table = "media";
15104
15197
  Media.associations = {
@@ -15165,7 +15258,7 @@ function generateMediaId() {
15165
15258
  }
15166
15259
 
15167
15260
  // src/lib/db/media/operations.ts
15168
- var import_watermelondb6 = require("@nozbe/watermelondb");
15261
+ var import_watermelondb7 = require("@nozbe/watermelondb");
15169
15262
 
15170
15263
  // src/lib/storage/mcpImages.ts
15171
15264
  var IMAGE_TOOL_NAMES = /* @__PURE__ */ new Set([
@@ -15367,7 +15460,7 @@ async function createMediaBatchOp(ctx, optionsArray) {
15367
15460
  })
15368
15461
  );
15369
15462
  });
15370
- const results = await mediaCollection.query(import_watermelondb6.Q.where("media_id", import_watermelondb6.Q.oneOf(mediaIds))).fetch();
15463
+ const results = await mediaCollection.query(import_watermelondb7.Q.where("media_id", import_watermelondb7.Q.oneOf(mediaIds))).fetch();
15371
15464
  return Promise.all(
15372
15465
  results.map(
15373
15466
  (m) => mediaToStored(m, ctx.walletAddress, ctx.signMessage, ctx.embeddedWalletSigner)
@@ -15377,7 +15470,7 @@ async function createMediaBatchOp(ctx, optionsArray) {
15377
15470
  var IMAGE_VIDEO_MIMES = VIDEO_EXTENSIONS.map((ext) => `image/${ext}`);
15378
15471
  async function deleteMediaByConversationOp(ctx, conversationId) {
15379
15472
  const mediaCollection = ctx.database.get("media");
15380
- const results = await mediaCollection.query(import_watermelondb6.Q.where("conversation_id", conversationId), import_watermelondb6.Q.where("is_deleted", false)).fetch();
15473
+ const results = await mediaCollection.query(import_watermelondb7.Q.where("conversation_id", conversationId), import_watermelondb7.Q.where("is_deleted", false)).fetch();
15381
15474
  if (results.length === 0) {
15382
15475
  return 0;
15383
15476
  }
@@ -15398,9 +15491,9 @@ async function deleteMediaByConversationOp(ctx, conversationId) {
15398
15491
  }
15399
15492
 
15400
15493
  // src/lib/db/memoryVault/models.ts
15401
- var import_watermelondb7 = require("@nozbe/watermelondb");
15494
+ var import_watermelondb8 = require("@nozbe/watermelondb");
15402
15495
  var import_decorators3 = require("@nozbe/watermelondb/decorators");
15403
- var VaultMemory = class extends import_watermelondb7.Model {
15496
+ var VaultMemory = class extends import_watermelondb8.Model {
15404
15497
  };
15405
15498
  VaultMemory.table = "memory_vault";
15406
15499
  __decorateClass([
@@ -15563,7 +15656,8 @@ function classifyDecay(m, now, policy) {
15563
15656
  if (m.eventTimeEnd < now - resolved.pastEventGraceMs) return "archive";
15564
15657
  if (m.eventTimeEnd >= now) return "keep";
15565
15658
  }
15566
- if (now - m.updatedAt > ttlForType(m.factType, resolved)) return "archive";
15659
+ const observedAt = Number.isFinite(m.lastObservedAt) ? m.lastObservedAt : m.updatedAt;
15660
+ if (now - Math.max(m.updatedAt, observedAt) > ttlForType(m.factType, resolved)) return "archive";
15567
15661
  return "keep";
15568
15662
  }
15569
15663
 
@@ -15977,7 +16071,7 @@ var WalletPoller = class {
15977
16071
  init_logger();
15978
16072
 
15979
16073
  // src/lib/memory/autoExtract.ts
15980
- var import_watermelondb10 = require("@nozbe/watermelondb");
16074
+ var import_watermelondb11 = require("@nozbe/watermelondb");
15981
16075
  init_operations2();
15982
16076
  init_types3();
15983
16077
  init_logger();
@@ -16493,6 +16587,7 @@ async function retain(content, ctx, options = {}) {
16493
16587
  const updated = await updateVaultMemoryOp(ctx.vaultCtx, targetId, {
16494
16588
  content: existing.content,
16495
16589
  proofCountIncrement: 1,
16590
+ observationSourceIds: options.sourceChunkIds,
16496
16591
  sourceChunkIds: mergedSourceIds,
16497
16592
  // resurrect encodes the decay gate: ACTIVE target → { preserveUpdatedAt:
16498
16593
  // true } (main's normal re-observation path — bump proof_count without
@@ -16736,6 +16831,7 @@ async function tryConsolidate(trimmed, ctx, options, prepared) {
16736
16831
  const updated = await updateVaultMemoryOp(ctx.vaultCtx, decision.targetId, {
16737
16832
  content: existing.content,
16738
16833
  proofCountIncrement: 1,
16834
+ observationSourceIds: options.sourceChunkIds,
16739
16835
  sourceChunkIds: mergedSourceIds,
16740
16836
  // ACTIVE target → { preserveUpdatedAt: true } (main's normal re-observation
16741
16837
  // path); ARCHIVED (non-superseded, non-deleted) → { restore: true } and no
@@ -16785,6 +16881,7 @@ async function tryConsolidate(trimmed, ctx, options, prepared) {
16785
16881
  const updated = await updateVaultMemoryOp(ctx.vaultCtx, decision.targetId, {
16786
16882
  content: decision.content,
16787
16883
  proofCountIncrement: 1,
16884
+ observationSourceIds: options.sourceChunkIds,
16788
16885
  sourceChunkIds: mergedSourceIds,
16789
16886
  embedding: JSON.stringify(newEmbedding),
16790
16887
  embeddingModel: consolidatedModel,
@@ -16836,7 +16933,7 @@ async function assertMergeTargetGoneOrThrow(ctx, targetId) {
16836
16933
  // src/lib/memory/autoExtract.ts
16837
16934
  async function isMemoryTopicsUserManaged(ctx, memoryId) {
16838
16935
  try {
16839
- const rows = await ctx.database.get(VaultMemory.table).query(import_watermelondb10.Q.where("id", memoryId)).fetch();
16936
+ const rows = await ctx.database.get(VaultMemory.table).query(import_watermelondb11.Q.where("id", memoryId)).fetch();
16840
16937
  return rows[0]?.topicsUserManaged === true;
16841
16938
  } catch {
16842
16939
  return true;
@@ -17423,7 +17520,7 @@ function createAutoExtractor(options) {
17423
17520
  onQuarantined: (info) => options.onMemoryQuarantined?.({ ...info, conversationId })
17424
17521
  }
17425
17522
  });
17426
- if (outcome !== "empty-after-retry") {
17523
+ if (outcome !== "empty-after-retry" && failedCount === 0) {
17427
17524
  const advancedTo = window2[window2.length - 1].id;
17428
17525
  stateFor(conversationId).watermark = advancedTo;
17429
17526
  persistCursor(conversationId, messages, advancedTo, contiguous);
@@ -17584,6 +17681,7 @@ function toDecayInput(c) {
17584
17681
  eventTimeEnd: c.eventTimeEnd,
17585
17682
  eventTimeKind: c.eventTimeKind,
17586
17683
  updatedAt: c.updatedAt,
17684
+ lastObservedAt: c.lastObservedAt,
17587
17685
  archivedAt: c.archivedAt,
17588
17686
  source: c.source,
17589
17687
  trustTier: c.trustTier
@@ -17665,7 +17763,8 @@ function createDecaySweeper(options) {
17665
17763
  for (const c of toArchive) {
17666
17764
  const ok = await archiveVaultMemoryOp(vaultCtx, c.uniqueId, {
17667
17765
  now,
17668
- expectedUpdatedAt: c.updatedAt
17766
+ expectedUpdatedAt: c.updatedAt,
17767
+ expectedLastObservedAt: c.lastObservedAt ?? null
17669
17768
  });
17670
17769
  if (ok) archived++;
17671
17770
  }
@@ -17921,6 +18020,7 @@ function createRecallTool(ctx, toolOptions, callbacks) {
17921
18020
  budget: defaultBudget,
17922
18021
  ...toolOptions?.minScore !== void 0 && { minScore: toolOptions.minScore },
17923
18022
  ...toolOptions?.scopes && { scopes: toolOptions.scopes },
18023
+ ...toolOptions?.memoryIds !== void 0 && { memoryIds: toolOptions.memoryIds },
17924
18024
  ...toolOptions?.folderId !== void 0 && { folderId: toolOptions.folderId },
17925
18025
  ...toolOptions?.excludeConversationId && {
17926
18026
  excludeConversationId: toolOptions.excludeConversationId
@@ -19136,6 +19236,465 @@ function createMessageSourceResolver(ctx) {
19136
19236
  };
19137
19237
  }
19138
19238
 
19239
+ // src/lib/memory/context.ts
19240
+ function shouldRecallMemory(query) {
19241
+ const normalized = query.trim().toLowerCase().replace(/[.!?。!?¡¿]+$/u, "").replace(/^[¡¿]+/u, "");
19242
+ return normalized.length > 0 && !/^(hi|hi there|hello|hey|yes|no|yep|nope|sure|k|ok|okay|thanks|thank you|thank you very much|thanks a lot|cool|great|got it|sounds good|hola|buenos días|buenas|sí|si|gracias|muchas gracias|vale|bonjour|salut|oui|non|merci|merci beaucoup|d'accord|hallo|guten tag|ja|nein|danke|danke schön|alles klar|olá|obrigado|obrigada|ciao|grazie|sì|你好|谢谢|好的|こんにちは|ありがとう|안녕하세요|감사합니다)$/u.test(
19243
+ normalized
19244
+ );
19245
+ }
19246
+ async function assembleMemoryContext(options) {
19247
+ const { query, recall: recall2, loadFacts, loadSessionRefs } = options;
19248
+ const memoryIds = options.memoryIds ?? options.recallOptions?.memoryIds;
19249
+ const ranked = shouldRecallMemory(query);
19250
+ const degraded = [];
19251
+ const guard = async (lane, run, fallback) => {
19252
+ try {
19253
+ return await run();
19254
+ } catch (error) {
19255
+ degraded.push(lane);
19256
+ try {
19257
+ options.onDegraded?.(lane, error);
19258
+ } catch {
19259
+ }
19260
+ return fallback;
19261
+ }
19262
+ };
19263
+ const scoped = memoryIds !== void 0 || options.recallOptions?.scopes !== void 0 || options.recallOptions?.folderId !== void 0;
19264
+ const allowed = memoryIds !== void 0 ? new Set(memoryIds) : void 0;
19265
+ const filters = {
19266
+ ...options.recallOptions?.scopes && { scopes: options.recallOptions.scopes },
19267
+ ...options.recallOptions?.folderId !== void 0 && {
19268
+ folderId: options.recallOptions.folderId
19269
+ }
19270
+ };
19271
+ const active = (m) => !m.isDeleted && !m.archivedAt && !m.supersededBy && m.trustTier !== "quarantined" && (!allowed || allowed.has(m.uniqueId));
19272
+ const asItem = (m, lane, score = 1) => ({
19273
+ id: m.uniqueId,
19274
+ content: m.content,
19275
+ score,
19276
+ lane,
19277
+ sourceIds: m.sourceChunkIds ?? []
19278
+ });
19279
+ const empty = {
19280
+ memories: [],
19281
+ usedBudget: options.recallOptions?.budget ?? "low",
19282
+ reranked: false,
19283
+ candidateCount: 0
19284
+ };
19285
+ const [facts, episodes, profile, session] = await Promise.all([
19286
+ ranked ? guard(
19287
+ "fact",
19288
+ () => recall2(query, { ...options.recallOptions, types: ["fact"], limit: 20, memoryIds }),
19289
+ empty
19290
+ ) : empty,
19291
+ ranked && !scoped && options.includeEpisodes !== false ? guard(
19292
+ "episode",
19293
+ () => recall2(query, {
19294
+ ...options.recallOptions,
19295
+ types: ["chunk"],
19296
+ limit: 4,
19297
+ minScore: 0.5,
19298
+ onDiagnostics: void 0
19299
+ }),
19300
+ empty
19301
+ ) : empty,
19302
+ loadFacts ? guard(
19303
+ "profile",
19304
+ async () => {
19305
+ const stable = await loadFacts({
19306
+ ...filters,
19307
+ memoryIds,
19308
+ factTypes: ["identity", "preference", "constraint", "relationship"],
19309
+ limit: 8
19310
+ });
19311
+ const dynamic = await loadFacts({
19312
+ ...filters,
19313
+ memoryIds,
19314
+ factTypes: ["ongoing_context", "plan"],
19315
+ limit: 4
19316
+ });
19317
+ return [...stable, ...dynamic].filter(active).map((m) => asItem(m, "profile"));
19318
+ },
19319
+ []
19320
+ ) : [],
19321
+ loadFacts && loadSessionRefs ? guard(
19322
+ "session",
19323
+ async () => {
19324
+ const refs = (await loadSessionRefs()).filter((r) => !allowed || allowed.has(r.memoryId)).sort((a, b) => b.score - a.score).slice(0, 10);
19325
+ if (!refs.length) return [];
19326
+ const rows = await loadFacts({ ...filters, memoryIds: refs.map((r) => r.memoryId) });
19327
+ const byId = new Map(rows.filter(active).map((m) => [m.uniqueId, m]));
19328
+ return refs.flatMap((ref) => {
19329
+ const m = byId.get(ref.memoryId);
19330
+ return m ? [asItem(m, "session", ref.score)] : [];
19331
+ });
19332
+ },
19333
+ []
19334
+ ) : []
19335
+ ]);
19336
+ const toItems = (result, lane) => result.memories.filter((m) => !allowed || allowed.has(m.id)).map((m) => ({
19337
+ id: m.id,
19338
+ content: m.content,
19339
+ score: m.score,
19340
+ lane,
19341
+ ...lane === "fact" && { recalled: true },
19342
+ sourceIds: m.sourceChunkIds ?? (m.messageId ? [m.messageId] : [])
19343
+ }));
19344
+ const recalledFacts = toItems(facts, "fact");
19345
+ const recalledScores = /* @__PURE__ */ new Map();
19346
+ const recalledContentScores = /* @__PURE__ */ new Map();
19347
+ for (const item of recalledFacts) {
19348
+ recalledScores.set(item.id, Math.max(recalledScores.get(item.id) ?? -Infinity, item.score));
19349
+ const content = item.content.trim();
19350
+ recalledContentScores.set(
19351
+ content,
19352
+ Math.max(recalledContentScores.get(content) ?? -Infinity, item.score)
19353
+ );
19354
+ }
19355
+ const rankedProfile = profile.map((item) => {
19356
+ const score = Math.max(
19357
+ recalledScores.get(item.id) ?? -Infinity,
19358
+ recalledContentScores.get(item.content.trim()) ?? -Infinity
19359
+ );
19360
+ return score === -Infinity ? item : { ...item, score, recalled: true };
19361
+ });
19362
+ const lanes = [
19363
+ [rankedProfile, 2e3],
19364
+ [recalledFacts, 6e3],
19365
+ [toItems(episodes, "episode"), 3e3],
19366
+ [session, 1e3]
19367
+ ];
19368
+ const items = [];
19369
+ const seenIds = /* @__PURE__ */ new Set();
19370
+ const seenContent = /* @__PURE__ */ new Set();
19371
+ let remaining = Math.max(0, options.maxChars ?? 12e3);
19372
+ let truncated = false;
19373
+ for (const [candidates, laneBudget] of lanes) {
19374
+ let laneRemaining = Math.min(laneBudget, remaining);
19375
+ for (const item of candidates) {
19376
+ const content = item.content?.trim();
19377
+ const key = `${item.lane === "episode" ? "episode" : "fact"}:${item.id}`;
19378
+ if (!content || seenIds.has(key) || seenContent.has(content)) continue;
19379
+ if (content.length > laneRemaining) {
19380
+ truncated = true;
19381
+ continue;
19382
+ }
19383
+ items.push({ ...item, content });
19384
+ seenIds.add(key);
19385
+ seenContent.add(content);
19386
+ remaining -= content.length;
19387
+ laneRemaining -= content.length;
19388
+ }
19389
+ }
19390
+ return { items, ranked, rankedCount: recalledFacts.length, degraded, truncated };
19391
+ }
19392
+
19393
+ // src/lib/memory/durableExtraction.ts
19394
+ var import_watermelondb12 = require("@nozbe/watermelondb");
19395
+ init_encryption();
19396
+ init_operations();
19397
+ init_models();
19398
+ var MAX_JOBS_PER_PASS = 3;
19399
+ var MAX_ATTEMPTS = 3;
19400
+ function idsOf(job) {
19401
+ return JSON.parse(String(job._getRaw("message_ids")));
19402
+ }
19403
+ function seqOf(job) {
19404
+ const raw = job._getRaw("watermark_seq");
19405
+ return typeof raw === "number" && raw > 0 ? raw : void 0;
19406
+ }
19407
+ function createDurableAutoExtractor(options) {
19408
+ const vault = options.retainCtx.vaultCtx;
19409
+ const database = vault.database;
19410
+ const owner = vault.userId ?? vault.walletAddress;
19411
+ if (!owner) throw new Error("Durable extraction requires a user or wallet owner");
19412
+ const jobs = database.get(ExtractionJob.table);
19413
+ const storage = {
19414
+ database,
19415
+ messagesCollection: database.get("history"),
19416
+ conversationsCollection: database.get("conversations"),
19417
+ walletAddress: vault.walletAddress,
19418
+ signMessage: vault.signMessage,
19419
+ embeddedWalletSigner: vault.embeddedWalletSigner
19420
+ };
19421
+ const folderId = options.folderId ?? null;
19422
+ let disposed = false;
19423
+ let running = false;
19424
+ let wakeRequested = false;
19425
+ let timer;
19426
+ let writes = Promise.resolve();
19427
+ let storeFailures = 0;
19428
+ const attempts = /* @__PURE__ */ new Map();
19429
+ const reportError = (error, conversationId) => {
19430
+ try {
19431
+ options.onError?.(error instanceof Error ? error : new Error(String(error)), conversationId);
19432
+ } catch {
19433
+ }
19434
+ };
19435
+ const currentScope = () => {
19436
+ try {
19437
+ const value = typeof options.scope === "function" ? options.scope() : options.scope;
19438
+ return value ?? "private";
19439
+ } catch (error) {
19440
+ reportError(error);
19441
+ return "private";
19442
+ }
19443
+ };
19444
+ function schedule(delay) {
19445
+ if (disposed) return;
19446
+ if (running || timer !== void 0) {
19447
+ if (running) wakeRequested = true;
19448
+ return;
19449
+ }
19450
+ timer = setTimeout(() => {
19451
+ timer = void 0;
19452
+ void drain();
19453
+ }, delay);
19454
+ }
19455
+ async function extractBatch(messages, conversationId, ids, jobScope, jobFolderId) {
19456
+ let watchdog;
19457
+ try {
19458
+ return await Promise.race([
19459
+ new Promise((resolve, reject) => {
19460
+ const worker = createAutoExtractor({
19461
+ ...options,
19462
+ retainCtx: {
19463
+ ...options.retainCtx,
19464
+ vaultCtx: {
19465
+ ...vault,
19466
+ canWrite: async () => !disposed && await storage.conversationsCollection.query(import_watermelondb12.Q.where("conversation_id", conversationId), import_watermelondb12.Q.where("is_deleted", false)).fetchCount() > 0 && await storage.messagesCollection.query(import_watermelondb12.Q.where("conversation_id", conversationId), import_watermelondb12.Q.where("id", import_watermelondb12.Q.oneOf(ids))).fetchCount() === ids.length && (!vault.canWrite || await vault.canWrite()) && !disposed
19467
+ }
19468
+ },
19469
+ // Never publish a private queued observation after a mode flip.
19470
+ scope: jobScope,
19471
+ folderId: jobFolderId,
19472
+ cursorStore: void 0,
19473
+ windowSize: 20,
19474
+ maxWindowSize: 20,
19475
+ onTurnComplete: resolve,
19476
+ onError: reject
19477
+ });
19478
+ worker.processTurn(messages, conversationId);
19479
+ worker.dispose();
19480
+ }),
19481
+ new Promise((_resolve, reject) => {
19482
+ watchdog = setTimeout(
19483
+ () => reject(new Error("Extraction did not settle in time; retained for retry")),
19484
+ options.batchTimeoutMs ?? 18e4
19485
+ );
19486
+ })
19487
+ ]);
19488
+ } finally {
19489
+ if (watchdog !== void 0) clearTimeout(watchdog);
19490
+ }
19491
+ }
19492
+ async function drain() {
19493
+ if (disposed || running) return;
19494
+ running = true;
19495
+ wakeRequested = false;
19496
+ let retry = false;
19497
+ let scheduleAfterSuccess = false;
19498
+ try {
19499
+ await writes;
19500
+ const pending = await jobs.query(import_watermelondb12.Q.where("owner_key", owner), import_watermelondb12.Q.where("message_ids", import_watermelondb12.Q.notEq("[]"))).fetch();
19501
+ storeFailures = 0;
19502
+ let processed = 0;
19503
+ for (const job of pending) {
19504
+ if (disposed) break;
19505
+ const attempted = attempts.get(job.id) ?? 0;
19506
+ if (attempted >= MAX_ATTEMPTS) continue;
19507
+ if (processed >= MAX_JOBS_PER_PASS) {
19508
+ scheduleAfterSuccess = true;
19509
+ break;
19510
+ }
19511
+ processed++;
19512
+ const conversationId = String(job._getRaw("conversation_id"));
19513
+ try {
19514
+ const conversations = await storage.conversationsCollection.query(import_watermelondb12.Q.where("conversation_id", conversationId), import_watermelondb12.Q.where("is_deleted", false)).fetchCount();
19515
+ if (!conversations) {
19516
+ await database.write(() => job.destroyPermanently());
19517
+ continue;
19518
+ }
19519
+ const ids = idsOf(job).slice(0, 20);
19520
+ const loaded = await Promise.all(ids.map((id) => getMessageOp(storage, id)));
19521
+ const missing = ids.filter((_id, index) => loaded[index] === null);
19522
+ if (missing.length) {
19523
+ if (attempted < MAX_ATTEMPTS - 1)
19524
+ throw new Error("Source messages not yet available; retained for retry");
19525
+ const dropped = new Set(missing);
19526
+ await database.write(
19527
+ () => job.update(
19528
+ (r) => r._setRaw(
19529
+ "message_ids",
19530
+ JSON.stringify(idsOf(job).filter((id) => !dropped.has(id)))
19531
+ )
19532
+ )
19533
+ );
19534
+ attempts.delete(job.id);
19535
+ scheduleAfterSuccess = true;
19536
+ reportError(
19537
+ new Error(
19538
+ `Dropped ${missing.length} unresolvable source id(s) to unblock extraction`
19539
+ ),
19540
+ conversationId
19541
+ );
19542
+ continue;
19543
+ }
19544
+ if (loaded.some(
19545
+ (message) => message && (message.decryptionStatus || isEncrypted(message.content))
19546
+ )) {
19547
+ throw new Error("Source messages are locked; retained for retry");
19548
+ }
19549
+ const messages = loaded.flatMap(
19550
+ (m) => m && m.conversationId === conversationId && (m.role === "user" || m.role === "assistant") ? [{ id: m.uniqueId, role: m.role, content: m.content }] : []
19551
+ );
19552
+ let success = messages.length === 0;
19553
+ if (messages.length) {
19554
+ const result = await extractBatch(
19555
+ messages,
19556
+ conversationId,
19557
+ ids,
19558
+ currentScope() === "private" ? "private" : String(job._getRaw("scope")),
19559
+ job._getRaw("folder_id") ?? null
19560
+ );
19561
+ success = result.failedCount === 0 && result.outcome !== "empty-after-retry";
19562
+ options.onTurnComplete?.(result);
19563
+ }
19564
+ if (!success) throw new Error("Extraction batch incomplete; retained for retry");
19565
+ attempts.delete(job.id);
19566
+ const sequences = /* @__PURE__ */ new Map();
19567
+ for (const message of loaded)
19568
+ if (message && message.messageId > 0)
19569
+ sequences.set(message.uniqueId, message.messageId);
19570
+ await database.write(async () => {
19571
+ const current = idsOf(job);
19572
+ const acknowledged = ids.filter((id) => current.includes(id));
19573
+ if (!acknowledged.length) return;
19574
+ const done = new Set(acknowledged);
19575
+ const remaining = current.filter((id) => !done.has(id));
19576
+ if (remaining.length) {
19577
+ await job.update(
19578
+ (r) => r._setRaw("message_ids", JSON.stringify([...acknowledged.slice(-2), ...remaining]))
19579
+ );
19580
+ } else await job.update((r) => r._setRaw("message_ids", "[]"));
19581
+ const newest = acknowledged[acknowledged.length - 1];
19582
+ await job.update((r) => {
19583
+ r._setRaw("watermark", newest);
19584
+ const sequence = sequences.get(newest);
19585
+ if (sequence !== void 0) {
19586
+ const prior = seqOf(job);
19587
+ r._setRaw(
19588
+ "watermark_seq",
19589
+ prior === void 0 ? sequence : Math.max(prior, sequence)
19590
+ );
19591
+ }
19592
+ });
19593
+ });
19594
+ scheduleAfterSuccess = true;
19595
+ } catch (error) {
19596
+ attempts.set(job.id, attempted + 1);
19597
+ retry = true;
19598
+ reportError(error, conversationId);
19599
+ }
19600
+ }
19601
+ } catch (error) {
19602
+ retry = ++storeFailures < 3;
19603
+ reportError(error);
19604
+ } finally {
19605
+ running = false;
19606
+ const woken = wakeRequested;
19607
+ wakeRequested = false;
19608
+ if (retry || scheduleAfterSuccess || woken)
19609
+ schedule(retry ? options.retryDelayMs ?? 3e4 : 0);
19610
+ }
19611
+ }
19612
+ schedule(0);
19613
+ return {
19614
+ processTurn(messages, conversationId) {
19615
+ if (disposed || !conversationId || !messages.length) return false;
19616
+ const ids = messages.map((m) => m.id);
19617
+ const scope = currentScope();
19618
+ writes = writes.then(
19619
+ () => database.write(async () => {
19620
+ const existing = await jobs.query(import_watermelondb12.Q.where("owner_key", owner), import_watermelondb12.Q.where("conversation_id", conversationId)).fetch();
19621
+ const job = existing.find(
19622
+ (candidate) => candidate._getRaw("scope") === scope && (candidate._getRaw("folder_id") ?? null) === folderId
19623
+ );
19624
+ const positions = new Map(ids.map((id, index) => [id, index]));
19625
+ const rows = await storage.messagesCollection.query(import_watermelondb12.Q.where("id", import_watermelondb12.Q.oneOf(ids))).fetch();
19626
+ const sequences = /* @__PURE__ */ new Map();
19627
+ for (const row of rows) {
19628
+ const sequence = Number(row._getRaw("message_id"));
19629
+ if (Number.isFinite(sequence) && sequence > 0) sequences.set(row.id, sequence);
19630
+ }
19631
+ let boundary = -1;
19632
+ let observedSeq = 0;
19633
+ const foreignSources = /* @__PURE__ */ new Set();
19634
+ for (const prior of existing) {
19635
+ const sources = [...idsOf(prior), String(prior._getRaw("watermark") ?? "")];
19636
+ for (const id of sources) {
19637
+ boundary = Math.max(boundary, positions.get(id) ?? -1);
19638
+ if (prior !== job) foreignSources.add(id);
19639
+ }
19640
+ observedSeq = Math.max(observedSeq, seqOf(prior) ?? 0);
19641
+ for (const id of idsOf(prior))
19642
+ observedSeq = Math.max(observedSeq, sequences.get(id) ?? 0);
19643
+ }
19644
+ if (observedSeq > 0)
19645
+ ids.forEach((id, index) => {
19646
+ const sequence = sequences.get(id);
19647
+ if (sequence !== void 0 && sequence <= observedSeq)
19648
+ boundary = Math.max(boundary, index);
19649
+ });
19650
+ if (!existing.length && options.cursorStore) {
19651
+ try {
19652
+ const cursor = options.cursorStore.get(conversationId);
19653
+ if (cursor) boundary = Math.max(boundary, positions.get(cursor) ?? -1);
19654
+ } catch (error) {
19655
+ reportError(error, conversationId);
19656
+ }
19657
+ }
19658
+ if (boundary < 0 && existing.some((prior) => idsOf(prior).length > 0 || seqOf(prior) !== void 0)) {
19659
+ reportError(
19660
+ new Error("Extraction boundary unresolved; skipped rather than re-observing"),
19661
+ conversationId
19662
+ );
19663
+ return;
19664
+ }
19665
+ const start = boundary >= 0 ? boundary + 1 : Math.max(0, ids.length - Math.max(1, options.windowSize ?? 6));
19666
+ if (start >= ids.length) return;
19667
+ const ownWatermark = job?._getRaw("watermark");
19668
+ const atOwnWatermark = !!job && boundary >= 0 && (ids[boundary] === ownWatermark || sequences.get(ids[boundary]) !== void 0 && sequences.get(ids[boundary]) === seqOf(job));
19669
+ const overlapStart = atOwnWatermark ? Math.max(0, boundary - 1) : start;
19670
+ const fresh = ids.slice(overlapStart).filter((id) => !foreignSources.has(id));
19671
+ if (!fresh.length) return;
19672
+ if (job) {
19673
+ await job.update(
19674
+ (r) => r._setRaw("message_ids", JSON.stringify([.../* @__PURE__ */ new Set([...idsOf(job), ...fresh])]))
19675
+ );
19676
+ attempts.delete(job.id);
19677
+ } else {
19678
+ await jobs.create((r) => {
19679
+ r._setRaw("owner_key", owner);
19680
+ r._setRaw("conversation_id", conversationId);
19681
+ r._setRaw("scope", scope);
19682
+ r._setRaw("folder_id", folderId);
19683
+ r._setRaw("message_ids", JSON.stringify([...new Set(fresh)]));
19684
+ });
19685
+ }
19686
+ })
19687
+ ).then(() => schedule(options.debounceMs ?? 2e4)).catch((error) => reportError(error, conversationId));
19688
+ return true;
19689
+ },
19690
+ isProcessing: () => running,
19691
+ dispose() {
19692
+ disposed = true;
19693
+ if (timer !== void 0) clearTimeout(timer);
19694
+ }
19695
+ };
19696
+ }
19697
+
19139
19698
  // src/lib/memoryEngine/index.ts
19140
19699
  init_chunking();
19141
19700
  init_embeddings();
@@ -22349,13 +22908,13 @@ init_redactor();
22349
22908
  init_useEncryption();
22350
22909
 
22351
22910
  // src/lib/db/schema.ts
22352
- var import_watermelondb19 = require("@nozbe/watermelondb");
22911
+ var import_watermelondb21 = require("@nozbe/watermelondb");
22353
22912
  var import_migrations2 = require("@nozbe/watermelondb/Schema/migrations");
22354
22913
 
22355
22914
  // src/lib/db/appFiles/models.ts
22356
- var import_watermelondb11 = require("@nozbe/watermelondb");
22915
+ var import_watermelondb13 = require("@nozbe/watermelondb");
22357
22916
  var import_decorators4 = require("@nozbe/watermelondb/decorators");
22358
- var AppFile = class extends import_watermelondb11.Model {
22917
+ var AppFile = class extends import_watermelondb13.Model {
22359
22918
  };
22360
22919
  AppFile.table = "app_files";
22361
22920
  AppFile.associations = {};
@@ -22377,9 +22936,9 @@ __decorateClass([
22377
22936
  ], AppFile.prototype, "updatedAt", 2);
22378
22937
 
22379
22938
  // src/lib/db/conversationMemory/models.ts
22380
- var import_watermelondb12 = require("@nozbe/watermelondb");
22939
+ var import_watermelondb14 = require("@nozbe/watermelondb");
22381
22940
  var import_decorators5 = require("@nozbe/watermelondb/decorators");
22382
- var ConversationMemory = class extends import_watermelondb12.Model {
22941
+ var ConversationMemory = class extends import_watermelondb14.Model {
22383
22942
  };
22384
22943
  ConversationMemory.table = "conversation_memory";
22385
22944
  ConversationMemory.associations = {};
@@ -22398,9 +22957,9 @@ __decorateClass([
22398
22957
  ], ConversationMemory.prototype, "createdAt", 2);
22399
22958
 
22400
22959
  // src/lib/db/entities/models.ts
22401
- var import_watermelondb13 = require("@nozbe/watermelondb");
22960
+ var import_watermelondb15 = require("@nozbe/watermelondb");
22402
22961
  var import_decorators6 = require("@nozbe/watermelondb/decorators");
22403
- var Entity = class extends import_watermelondb13.Model {
22962
+ var Entity = class extends import_watermelondb15.Model {
22404
22963
  };
22405
22964
  Entity.table = "entity";
22406
22965
  __decorateClass([
@@ -22416,7 +22975,7 @@ __decorateClass([
22416
22975
  __decorateClass([
22417
22976
  (0, import_decorators6.date)("updated_at")
22418
22977
  ], Entity.prototype, "updatedAt", 2);
22419
- var MemoryEntity = class extends import_watermelondb13.Model {
22978
+ var MemoryEntity = class extends import_watermelondb15.Model {
22420
22979
  };
22421
22980
  MemoryEntity.table = "memory_entity";
22422
22981
  __decorateClass([
@@ -22433,10 +22992,13 @@ __decorateClass([
22433
22992
  (0, import_decorators6.date)("created_at")
22434
22993
  ], MemoryEntity.prototype, "createdAt", 2);
22435
22994
 
22995
+ // src/lib/db/schema.ts
22996
+ init_models();
22997
+
22436
22998
  // src/lib/db/project/models.ts
22437
- var import_watermelondb14 = require("@nozbe/watermelondb");
22999
+ var import_watermelondb16 = require("@nozbe/watermelondb");
22438
23000
  var import_decorators7 = require("@nozbe/watermelondb/decorators");
22439
- var Project = class extends import_watermelondb14.Model {
23001
+ var Project = class extends import_watermelondb16.Model {
22440
23002
  };
22441
23003
  Project.table = "projects";
22442
23004
  Project.associations = {
@@ -22459,10 +23021,10 @@ __decorateClass([
22459
23021
  ], Project.prototype, "isDeleted", 2);
22460
23022
 
22461
23023
  // src/lib/db/savedTools/models.ts
22462
- var import_watermelondb15 = require("@nozbe/watermelondb");
23024
+ var import_watermelondb17 = require("@nozbe/watermelondb");
22463
23025
  var import_decorators8 = require("@nozbe/watermelondb/decorators");
22464
23026
  var sanitizeParameters = (raw) => raw ?? {};
22465
- var SavedTool = class extends import_watermelondb15.Model {
23027
+ var SavedTool = class extends import_watermelondb17.Model {
22466
23028
  };
22467
23029
  SavedTool.table = "saved_tools";
22468
23030
  SavedTool.associations = {};
@@ -22496,9 +23058,9 @@ __decorateClass([
22496
23058
  ], SavedTool.prototype, "isDeleted", 2);
22497
23059
 
22498
23060
  // src/lib/db/settings/models.ts
22499
- var import_watermelondb16 = require("@nozbe/watermelondb");
23061
+ var import_watermelondb18 = require("@nozbe/watermelondb");
22500
23062
  var import_decorators9 = require("@nozbe/watermelondb/decorators");
22501
- var ModelPreference = class extends import_watermelondb16.Model {
23063
+ var ModelPreference = class extends import_watermelondb18.Model {
22502
23064
  };
22503
23065
  ModelPreference.table = "modelPreferences";
22504
23066
  __decorateClass([
@@ -22509,9 +23071,9 @@ __decorateClass([
22509
23071
  ], ModelPreference.prototype, "models", 2);
22510
23072
 
22511
23073
  // src/lib/db/userPreferences/models.ts
22512
- var import_watermelondb17 = require("@nozbe/watermelondb");
23074
+ var import_watermelondb19 = require("@nozbe/watermelondb");
22513
23075
  var import_decorators10 = require("@nozbe/watermelondb/decorators");
22514
- var UserPreference = class extends import_watermelondb17.Model {
23076
+ var UserPreference = class extends import_watermelondb19.Model {
22515
23077
  };
22516
23078
  UserPreference.table = "userPreferences";
22517
23079
  __decorateClass([
@@ -22540,9 +23102,9 @@ __decorateClass([
22540
23102
  ], UserPreference.prototype, "updatedAt", 2);
22541
23103
 
22542
23104
  // src/lib/db/vaultFolders/models.ts
22543
- var import_watermelondb18 = require("@nozbe/watermelondb");
23105
+ var import_watermelondb20 = require("@nozbe/watermelondb");
22544
23106
  var import_decorators11 = require("@nozbe/watermelondb/decorators");
22545
- var VaultFolder = class extends import_watermelondb18.Model {
23107
+ var VaultFolder = class extends import_watermelondb20.Model {
22546
23108
  };
22547
23109
  VaultFolder.table = "vault_folders";
22548
23110
  __decorateClass([
@@ -22569,12 +23131,27 @@ __decorateClass([
22569
23131
  ], VaultFolder.prototype, "context", 2);
22570
23132
 
22571
23133
  // src/lib/db/schema.ts
22572
- var SDK_SCHEMA_VERSION = 45;
22573
- var sdkSchema = (0, import_watermelondb19.appSchema)({
23134
+ var SDK_SCHEMA_VERSION = 46;
23135
+ var sdkSchema = (0, import_watermelondb21.appSchema)({
22574
23136
  version: SDK_SCHEMA_VERSION,
22575
23137
  tables: [
23138
+ (0, import_watermelondb21.tableSchema)({
23139
+ name: "memory_extraction_jobs",
23140
+ columns: [
23141
+ { name: "owner_key", type: "string", isIndexed: true },
23142
+ { name: "conversation_id", type: "string", isIndexed: true },
23143
+ { name: "scope", type: "string" },
23144
+ { name: "message_ids", type: "string" },
23145
+ { name: "watermark", type: "string", isOptional: true },
23146
+ // Deletion-proof anchor: history.message_id of the newest acknowledged
23147
+ // source. `watermark` alone is an id, and deleting that message erased
23148
+ // the anchor — see the column note in extractionJobs/models.ts.
23149
+ { name: "watermark_seq", type: "number", isOptional: true },
23150
+ { name: "folder_id", type: "string", isOptional: true }
23151
+ ]
23152
+ }),
22576
23153
  // Chat storage tables
22577
- (0, import_watermelondb19.tableSchema)({
23154
+ (0, import_watermelondb21.tableSchema)({
22578
23155
  name: "history",
22579
23156
  columns: [
22580
23157
  { name: "message_id", type: "number" },
@@ -22613,7 +23190,7 @@ var sdkSchema = (0, import_watermelondb19.appSchema)({
22613
23190
  // MessageOrigin — provenance, NOT encrypted (see v44)
22614
23191
  ]
22615
23192
  }),
22616
- (0, import_watermelondb19.tableSchema)({
23193
+ (0, import_watermelondb21.tableSchema)({
22617
23194
  name: "conversations",
22618
23195
  columns: [
22619
23196
  { name: "conversation_id", type: "string", isIndexed: true },
@@ -22635,7 +23212,7 @@ var sdkSchema = (0, import_watermelondb19.appSchema)({
22635
23212
  ]
22636
23213
  }),
22637
23214
  // Project storage table
22638
- (0, import_watermelondb19.tableSchema)({
23215
+ (0, import_watermelondb21.tableSchema)({
22639
23216
  name: "projects",
22640
23217
  columns: [
22641
23218
  { name: "project_id", type: "string", isIndexed: true },
@@ -22646,7 +23223,7 @@ var sdkSchema = (0, import_watermelondb19.appSchema)({
22646
23223
  ]
22647
23224
  }),
22648
23225
  // Settings storage tables (deprecated - use userPreferences)
22649
- (0, import_watermelondb19.tableSchema)({
23226
+ (0, import_watermelondb21.tableSchema)({
22650
23227
  name: "modelPreferences",
22651
23228
  columns: [
22652
23229
  { name: "wallet_address", type: "string", isIndexed: true },
@@ -22654,7 +23231,7 @@ var sdkSchema = (0, import_watermelondb19.appSchema)({
22654
23231
  ]
22655
23232
  }),
22656
23233
  // Unified user preferences storage
22657
- (0, import_watermelondb19.tableSchema)({
23234
+ (0, import_watermelondb21.tableSchema)({
22658
23235
  name: "userPreferences",
22659
23236
  columns: [
22660
23237
  // Identity
@@ -22673,7 +23250,7 @@ var sdkSchema = (0, import_watermelondb19.appSchema)({
22673
23250
  ]
22674
23251
  }),
22675
23252
  // Memory vault storage
22676
- (0, import_watermelondb19.tableSchema)({
23253
+ (0, import_watermelondb21.tableSchema)({
22677
23254
  name: "memory_vault",
22678
23255
  columns: [
22679
23256
  { name: "content", type: "string" },
@@ -22801,7 +23378,7 @@ var sdkSchema = (0, import_watermelondb19.appSchema)({
22801
23378
  ]
22802
23379
  }),
22803
23380
  // Entity table — canonical names extracted from auto-extraction (W5).
22804
- (0, import_watermelondb19.tableSchema)({
23381
+ (0, import_watermelondb21.tableSchema)({
22805
23382
  name: "entity",
22806
23383
  columns: [
22807
23384
  { name: "canonical_name", type: "string", isIndexed: true },
@@ -22811,7 +23388,7 @@ var sdkSchema = (0, import_watermelondb19.appSchema)({
22811
23388
  ]
22812
23389
  }),
22813
23390
  // Many-to-many join: which memories reference which entities.
22814
- (0, import_watermelondb19.tableSchema)({
23391
+ (0, import_watermelondb21.tableSchema)({
22815
23392
  name: "memory_entity",
22816
23393
  columns: [
22817
23394
  { name: "memory_id", type: "string", isIndexed: true },
@@ -22821,7 +23398,7 @@ var sdkSchema = (0, import_watermelondb19.appSchema)({
22821
23398
  ]
22822
23399
  }),
22823
23400
  // Vault folder organization
22824
- (0, import_watermelondb19.tableSchema)({
23401
+ (0, import_watermelondb21.tableSchema)({
22825
23402
  name: "vault_folders",
22826
23403
  columns: [
22827
23404
  { name: "name", type: "string" },
@@ -22834,7 +23411,7 @@ var sdkSchema = (0, import_watermelondb19.appSchema)({
22834
23411
  ]
22835
23412
  }),
22836
23413
  // Conversation summary cache for progressive history summarization
22837
- (0, import_watermelondb19.tableSchema)({
23414
+ (0, import_watermelondb21.tableSchema)({
22838
23415
  name: "conversation_summaries",
22839
23416
  columns: [
22840
23417
  { name: "conversation_id", type: "string", isIndexed: true },
@@ -22847,7 +23424,7 @@ var sdkSchema = (0, import_watermelondb19.appSchema)({
22847
23424
  ]
22848
23425
  }),
22849
23426
  // Media library storage (images, videos, audio, documents)
22850
- (0, import_watermelondb19.tableSchema)({
23427
+ (0, import_watermelondb21.tableSchema)({
22851
23428
  name: "media",
22852
23429
  columns: [
22853
23430
  // Identity
@@ -22883,7 +23460,7 @@ var sdkSchema = (0, import_watermelondb19.appSchema)({
22883
23460
  ]
22884
23461
  }),
22885
23462
  // ── App files ─────────────────────────────────────────────────────────
22886
- (0, import_watermelondb19.tableSchema)({
23463
+ (0, import_watermelondb21.tableSchema)({
22887
23464
  name: "app_files",
22888
23465
  columns: [
22889
23466
  { name: "conversation_id", type: "string", isIndexed: true },
@@ -22894,7 +23471,7 @@ var sdkSchema = (0, import_watermelondb19.appSchema)({
22894
23471
  ]
22895
23472
  }),
22896
23473
  // ── Saved tools ──────────────────────────────────────────────────────
22897
- (0, import_watermelondb19.tableSchema)({
23474
+ (0, import_watermelondb21.tableSchema)({
22898
23475
  name: "saved_tools",
22899
23476
  columns: [
22900
23477
  { name: "name", type: "string" },
@@ -22910,7 +23487,7 @@ var sdkSchema = (0, import_watermelondb19.appSchema)({
22910
23487
  ]
22911
23488
  }),
22912
23489
  // ── Conversation memories (panel persistence) ────────────────────────
22913
- (0, import_watermelondb19.tableSchema)({
23490
+ (0, import_watermelondb21.tableSchema)({
22914
23491
  name: "conversation_memory",
22915
23492
  columns: [
22916
23493
  { name: "conversation_id", type: "string", isIndexed: true },
@@ -23577,10 +24154,28 @@ var sdkMigrations = (0, import_migrations2.schemaMigrations)({
23577
24154
  columns: [{ name: "media", type: "string", isOptional: true }]
23578
24155
  })
23579
24156
  ]
24157
+ },
24158
+ {
24159
+ toVersion: 46,
24160
+ steps: [
24161
+ (0, import_migrations2.createTable)({
24162
+ name: "memory_extraction_jobs",
24163
+ columns: [
24164
+ { name: "owner_key", type: "string", isIndexed: true },
24165
+ { name: "conversation_id", type: "string", isIndexed: true },
24166
+ { name: "scope", type: "string" },
24167
+ { name: "message_ids", type: "string" },
24168
+ { name: "watermark", type: "string", isOptional: true },
24169
+ { name: "watermark_seq", type: "number", isOptional: true },
24170
+ { name: "folder_id", type: "string", isOptional: true }
24171
+ ]
24172
+ })
24173
+ ]
23580
24174
  }
23581
24175
  ]
23582
24176
  });
23583
24177
  var sdkModelClasses = [
24178
+ ExtractionJob,
23584
24179
  Message,
23585
24180
  Conversation,
23586
24181
  ConversationSummary,
@@ -23598,7 +24193,7 @@ var sdkModelClasses = [
23598
24193
  ];
23599
24194
 
23600
24195
  // src/lib/db/manager.ts
23601
- var import_watermelondb20 = require("@nozbe/watermelondb");
24196
+ var import_watermelondb22 = require("@nozbe/watermelondb");
23602
24197
  var DESTRUCTIVE_MIGRATION_VERSION = 8;
23603
24198
  var webPlatformStorage = {
23604
24199
  getItem: (key) => localStorage.getItem(key),
@@ -23679,7 +24274,7 @@ var DatabaseManager = class {
23679
24274
  walletAddress
23680
24275
  });
23681
24276
  const adapter = this.createAdapter(dbName, sdkSchema, sdkMigrations);
23682
- const database = new import_watermelondb20.Database({
24277
+ const database = new import_watermelondb22.Database({
23683
24278
  adapter,
23684
24279
  modelClasses: sdkModelClasses
23685
24280
  });
@@ -23758,7 +24353,7 @@ var DatabaseManager = class {
23758
24353
  };
23759
24354
 
23760
24355
  // src/lib/db/conversationMemory/operations.ts
23761
- var import_watermelondb21 = require("@nozbe/watermelondb");
24356
+ var import_watermelondb23 = require("@nozbe/watermelondb");
23762
24357
  var MAX_PER_CONVERSATION = 200;
23763
24358
  function conversationMemoryToStored(row) {
23764
24359
  return {
@@ -23773,7 +24368,7 @@ async function addConversationMemoriesOp(ctx, conversationId, items) {
23773
24368
  if (!conversationId || items.length === 0) return;
23774
24369
  const now = Date.now();
23775
24370
  await ctx.database.write(async () => {
23776
- const existing = await ctx.conversationMemoryCollection.query(import_watermelondb21.Q.where("conversation_id", conversationId), import_watermelondb21.Q.sortBy("created_at", import_watermelondb21.Q.asc)).fetch();
24371
+ const existing = await ctx.conversationMemoryCollection.query(import_watermelondb23.Q.where("conversation_id", conversationId), import_watermelondb23.Q.sortBy("created_at", import_watermelondb23.Q.asc)).fetch();
23777
24372
  const seen = new Set(existing.map((r) => r.memoryId));
23778
24373
  const batchSeen = /* @__PURE__ */ new Set();
23779
24374
  const deduped = [];
@@ -23808,20 +24403,20 @@ async function addConversationMemoriesOp(ctx, conversationId, items) {
23808
24403
  }
23809
24404
  async function getConversationMemoriesOp(ctx, conversationId) {
23810
24405
  if (!conversationId) return [];
23811
- const rows = await ctx.conversationMemoryCollection.query(import_watermelondb21.Q.where("conversation_id", conversationId), import_watermelondb21.Q.sortBy("created_at", import_watermelondb21.Q.asc)).fetch();
24406
+ const rows = await ctx.conversationMemoryCollection.query(import_watermelondb23.Q.where("conversation_id", conversationId), import_watermelondb23.Q.sortBy("created_at", import_watermelondb23.Q.asc)).fetch();
23812
24407
  return rows.map(conversationMemoryToStored);
23813
24408
  }
23814
24409
  async function clearConversationMemoriesOp(ctx, conversationId) {
23815
24410
  if (!conversationId) return;
23816
24411
  await ctx.database.write(async () => {
23817
- const rows = await ctx.conversationMemoryCollection.query(import_watermelondb21.Q.where("conversation_id", conversationId)).fetch();
24412
+ const rows = await ctx.conversationMemoryCollection.query(import_watermelondb23.Q.where("conversation_id", conversationId)).fetch();
23818
24413
  if (rows.length === 0) return;
23819
24414
  await ctx.database.batch(...rows.map((r) => r.prepareDestroyPermanently()));
23820
24415
  });
23821
24416
  }
23822
24417
 
23823
24418
  // src/lib/db/vaultFolders/operations.ts
23824
- var import_watermelondb22 = require("@nozbe/watermelondb");
24419
+ var import_watermelondb24 = require("@nozbe/watermelondb");
23825
24420
  function folderToStored(folder) {
23826
24421
  return {
23827
24422
  uniqueId: folder.id,
@@ -23846,7 +24441,7 @@ async function createVaultFolderOp(ctx, opts) {
23846
24441
  return folderToStored(created);
23847
24442
  }
23848
24443
  async function getAllVaultFoldersOp(ctx) {
23849
- const results = await ctx.vaultFolderCollection.query(import_watermelondb22.Q.where("is_deleted", false), import_watermelondb22.Q.sortBy("created_at", import_watermelondb22.Q.desc)).fetch();
24444
+ const results = await ctx.vaultFolderCollection.query(import_watermelondb24.Q.where("is_deleted", false), import_watermelondb24.Q.sortBy("created_at", import_watermelondb24.Q.desc)).fetch();
23850
24445
  return results.map(folderToStored);
23851
24446
  }
23852
24447
  async function updateVaultFolderOp(ctx, id, opts) {
@@ -23855,7 +24450,7 @@ async function updateVaultFolderOp(ctx, id, opts) {
23855
24450
  if (record.isDeleted) return null;
23856
24451
  const scopeChanged = opts.scope !== void 0 && opts.scope !== record.scope;
23857
24452
  const updated = await ctx.database.write(async () => {
23858
- const memories = scopeChanged ? await ctx.vaultMemoryCollection.query(import_watermelondb22.Q.where("folder_id", id), import_watermelondb22.Q.where("is_deleted", false)).fetch() : [];
24453
+ const memories = scopeChanged ? await ctx.vaultMemoryCollection.query(import_watermelondb24.Q.where("folder_id", id), import_watermelondb24.Q.where("is_deleted", false)).fetch() : [];
23859
24454
  const updates = [];
23860
24455
  updates.push(
23861
24456
  record.prepareUpdate((r) => {
@@ -23883,7 +24478,7 @@ async function deleteVaultFolderOp(ctx, id) {
23883
24478
  const record = await ctx.vaultFolderCollection.find(id);
23884
24479
  if (record.isDeleted) return false;
23885
24480
  await ctx.database.write(async () => {
23886
- const memories = await ctx.vaultMemoryCollection.query(import_watermelondb22.Q.where("folder_id", id), import_watermelondb22.Q.where("is_deleted", false)).fetch();
24481
+ const memories = await ctx.vaultMemoryCollection.query(import_watermelondb24.Q.where("folder_id", id), import_watermelondb24.Q.where("is_deleted", false)).fetch();
23887
24482
  const preparedMemories = memories.map(
23888
24483
  (memory) => memory.prepareUpdate((r) => {
23889
24484
  r._setRaw("folder_id", null);
@@ -23952,7 +24547,7 @@ async function updateVaultFolderContextOp(ctx, id, context) {
23952
24547
  }
23953
24548
  }
23954
24549
  async function getVaultFolderMemoryCountOp(ctx, folderId) {
23955
- return await ctx.vaultMemoryCollection.query(import_watermelondb22.Q.where("folder_id", folderId), import_watermelondb22.Q.where("is_deleted", false)).fetchCount();
24550
+ return await ctx.vaultMemoryCollection.query(import_watermelondb24.Q.where("folder_id", folderId), import_watermelondb24.Q.where("is_deleted", false)).fetchCount();
23956
24551
  }
23957
24552
 
23958
24553
  // src/lib/db/vaultFolders/defaults.ts
@@ -24966,6 +25561,7 @@ function createNotionTools(getAccessToken, requestNotionAccess) {
24966
25561
  WalletPoller,
24967
25562
  addConversationMemoriesOp,
24968
25563
  archiveVaultMemoryOp,
25564
+ assembleMemoryContext,
24969
25565
  backfillMemoryTopicsOp,
24970
25566
  buildNotionAuthUrl,
24971
25567
  capHopsForDensity,
@@ -24998,6 +25594,7 @@ function createNotionTools(getAccessToken, requestNotionAccess) {
24998
25594
  createChunkVectorCache,
24999
25595
  createCryptoPricePreProcessor,
25000
25596
  createDecaySweeper,
25597
+ createDurableAutoExtractor,
25001
25598
  createLlmDecayClassifier,
25002
25599
  createLlmNeighborRefiner,
25003
25600
  createMemoryEngineTool,
@@ -25018,6 +25615,7 @@ function createNotionTools(getAccessToken, requestNotionAccess) {
25018
25615
  decodeChunkVector,
25019
25616
  decryptConversationTitle,
25020
25617
  deleteAllVaultMemoriesForUserOp,
25618
+ deleteMessageOp,
25021
25619
  deleteVaultFolderOp,
25022
25620
  deleteVaultMemoryOp,
25023
25621
  dequantizeEmbedding,
@@ -25090,6 +25688,7 @@ function createNotionTools(getAccessToken, requestNotionAccess) {
25090
25688
  setLogger,
25091
25689
  setMemoryVisibilityOp,
25092
25690
  shouldChunkMessage,
25691
+ shouldRecallMemory,
25093
25692
  stampTopicsExtractedAtOp,
25094
25693
  streamCancelPath,
25095
25694
  streamReplayPath,