@anuma/sdk 10.8.0 → 10.9.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.
@@ -880,17 +880,17 @@ async function callPortalJsonCompletion(req) {
880
880
  if (autoUpgraded) endpoint = endpointForTransport(endpoint, transport);
881
881
  assertTransportMatchesEndpoint(transport, endpoint);
882
882
  }
883
- const maxAttempts = Math.max(1, req.maxAttempts ?? 3);
883
+ const maxAttempts2 = Math.max(1, req.maxAttempts ?? 3);
884
884
  const startedAt = Date.now();
885
885
  const deadlineAt = req.totalTimeoutMs === void 0 ? void 0 : startedAt + req.totalTimeoutMs;
886
886
  const overBudget = () => deadlineAt !== void 0 && Date.now() >= deadlineAt;
887
887
  let lastFailure;
888
888
  let reinforce = false;
889
889
  const giveUp = () => {
890
- req.onFailure?.(lastFailure ?? { reason: "network", attempts: maxAttempts });
890
+ req.onFailure?.(lastFailure ?? { reason: "network", attempts: maxAttempts2 });
891
891
  return null;
892
892
  };
893
- for (let attempt = 1; attempt <= maxAttempts; attempt++) {
893
+ for (let attempt = 1; attempt <= maxAttempts2; attempt++) {
894
894
  if (attempt > 1 && overBudget()) {
895
895
  log.warn(`[${req.tag}] over time budget before attempt ${attempt}, giving up`);
896
896
  lastFailure = { reason: "time-budget-exhausted", attempts: attempt - 1 };
@@ -922,20 +922,20 @@ async function callPortalJsonCompletion(req) {
922
922
  log.warn(`[${req.tag}] ${outcome2.reason}`);
923
923
  return giveUp();
924
924
  }
925
- if (attempt >= maxAttempts) {
926
- log.warn(`[${req.tag}] ${outcome2.reason} \u2014 attempt ${attempt}/${maxAttempts}, giving up`);
925
+ if (attempt >= maxAttempts2) {
926
+ log.warn(`[${req.tag}] ${outcome2.reason} \u2014 attempt ${attempt}/${maxAttempts2}, giving up`);
927
927
  break;
928
928
  }
929
929
  const backoff = (req.backoffMs ?? defaultBackoffMs)(attempt);
930
930
  const delay = Math.max(backoff, outcome2.retryAfterMs ?? 0);
931
931
  if (overBudget() || req.totalTimeoutMs !== void 0 && Date.now() - startedAt + delay >= req.totalTimeoutMs) {
932
932
  log.warn(
933
- `[${req.tag}] ${outcome2.reason} \u2014 attempt ${attempt}/${maxAttempts}, over time budget, giving up`
933
+ `[${req.tag}] ${outcome2.reason} \u2014 attempt ${attempt}/${maxAttempts2}, over time budget, giving up`
934
934
  );
935
935
  lastFailure = { reason: "time-budget-exhausted", attempts: attempt };
936
936
  break;
937
937
  }
938
- log.warn(`[${req.tag}] ${outcome2.reason} \u2014 attempt ${attempt}/${maxAttempts}, retrying`);
938
+ log.warn(`[${req.tag}] ${outcome2.reason} \u2014 attempt ${attempt}/${maxAttempts2}, retrying`);
939
939
  if (delay > 0) await sleep2(delay);
940
940
  }
941
941
  return giveUp();
@@ -1419,6 +1419,7 @@ CARRY FORWARD UNCHANGED DETAILS (every "update" and "supersede"): a memory can h
1419
1419
  // src/expo/index.ts
1420
1420
  var index_exports = {};
1421
1421
  __export(index_exports, {
1422
+ ANUMA_CONVERSATION_ID: () => ANUMA_CONVERSATION_ID,
1422
1423
  CHUNKS_DISCARDED_ORIGIN: () => CHUNKS_DISCARDED_ORIGIN,
1423
1424
  ChatConversation: () => Conversation,
1424
1425
  ChatMessage: () => Message,
@@ -1478,6 +1479,8 @@ __export(index_exports, {
1478
1479
  WALLET_PAYMENT_TOOL_NAME: () => WALLET_PAYMENT_TOOL_NAME,
1479
1480
  WalletPoller: () => WalletPoller,
1480
1481
  addConversationMemoriesOp: () => addConversationMemoriesOp,
1482
+ anumaLastReadSortKey: () => anumaLastReadSortKey,
1483
+ anumaTurnContent: () => anumaTurnContent,
1481
1484
  archiveVaultMemoryOp: () => archiveVaultMemoryOp,
1482
1485
  assembleMemoryContext: () => assembleMemoryContext,
1483
1486
  attachFileContextToLastUserMessage: () => attachFileContextToLastUserMessage,
@@ -1511,6 +1514,7 @@ __export(index_exports, {
1511
1514
  consoleLogger: () => consoleLogger,
1512
1515
  conversationMemoryToStored: () => conversationMemoryToStored,
1513
1516
  cosineInt8: () => cosineInt8,
1517
+ countUnreadAnumaTurns: () => countUnreadAnumaTurns,
1514
1518
  createAutoExtractor: () => createAutoExtractor,
1515
1519
  createChunkVectorCache: () => createChunkVectorCache,
1516
1520
  createCryptoPricePreProcessor: () => createCryptoPricePreProcessor,
@@ -1559,6 +1563,7 @@ __export(index_exports, {
1559
1563
  embedMessage: () => embedMessage,
1560
1564
  encodeChunkVector: () => encodeChunkVector,
1561
1565
  encryptToolCallEventsOp: () => encryptToolCallEventsOp,
1566
+ ensureAnumaConversation: () => ensureAnumaConversation,
1562
1567
  ensureDefaultFoldersOp: () => ensureDefaultFoldersOp,
1563
1568
  exchangeNotionCode: () => exchangeNotionCode,
1564
1569
  extractAndLinkEntitiesForMemoriesOp: () => extractAndLinkEntitiesForMemoriesOp,
@@ -1599,6 +1604,7 @@ __export(index_exports, {
1599
1604
  isRerankerAvailable: () => isRerankerAvailable,
1600
1605
  loadSettings: () => loadSettings,
1601
1606
  localChatBackend: () => localChatBackend,
1607
+ markAnumaRead: () => markAnumaRead,
1602
1608
  maskScopedEmbeddingCache: () => maskScopedEmbeddingCache,
1603
1609
  maxVaultAttachmentBytes: () => maxVaultAttachmentBytes,
1604
1610
  memoryCipher: () => memoryCipher,
@@ -1614,6 +1620,7 @@ __export(index_exports, {
1614
1620
  quantizeEmbedding: () => quantizeEmbedding,
1615
1621
  queueManager: () => queueManager,
1616
1622
  rankProfileCandidates: () => rankProfileCandidates,
1623
+ readAnumaTurn: () => readAnumaTurn,
1617
1624
  recall: () => recall,
1618
1625
  reencryptMemoryField: () => reencryptMemoryField,
1619
1626
  reflect: () => reflect,
@@ -11591,119 +11598,19 @@ var xhrTransport = (options) => {
11591
11598
  return { stream: createStream() };
11592
11599
  };
11593
11600
 
11594
- // src/lib/db/chat/summaryOperations.ts
11595
- var import_watermelondb5 = require("@nozbe/watermelondb");
11596
- init_logger();
11597
- function createSummaryContext(database, encryption = {}) {
11598
- return {
11599
- ...encryption,
11600
- database,
11601
- summariesCollection: database.get("conversation_summaries")
11602
- };
11603
- }
11604
- function modelToStored(record2, summary) {
11605
- return {
11606
- uniqueId: record2.id,
11607
- conversationId: record2.conversationId,
11608
- summary,
11609
- summarizedUpTo: record2.summarizedUpTo,
11610
- tokenCount: record2.tokenCount,
11611
- createdAt: record2.createdAt,
11612
- updatedAt: record2.updatedAt
11613
- };
11614
- }
11615
- async function getConversationSummaryOp(ctx, conversationId) {
11616
- const results = await ctx.summariesCollection.query(import_watermelondb5.Q.where("conversation_id", conversationId)).fetch();
11617
- if (results.length === 0) return null;
11618
- const record2 = results[0];
11619
- const stored2 = modelToStored(record2, record2.summary);
11620
- if (ctx.walletAddress && isEncrypted(stored2.summary)) {
11621
- if (ctx.signMessage) {
11622
- try {
11623
- await requestEncryptionKey(ctx.walletAddress, ctx.signMessage, ctx.embeddedWalletSigner);
11624
- } catch (error) {
11625
- getLogger().warn("Failed to request the encryption key for the summary:", error);
11626
- }
11627
- }
11628
- stored2.summary = await decryptField(stored2.summary, ctx.walletAddress);
11629
- } else if (ctx.walletAddress && ctx.signMessage && stored2.summary) {
11630
- try {
11631
- const encryptedSummary = await encryptField(
11632
- stored2.summary,
11633
- ctx.walletAddress,
11634
- ctx.signMessage,
11635
- ctx.embeddedWalletSigner
11636
- );
11637
- await ctx.database.write(async () => {
11638
- const current = await ctx.summariesCollection.query(import_watermelondb5.Q.where("id", record2.id)).fetch();
11639
- if (current[0]?.summary === stored2.summary) {
11640
- await current[0].update((rec) => {
11641
- rec.summary = encryptedSummary;
11642
- });
11643
- }
11644
- });
11645
- } catch (error) {
11646
- getLogger().warn("Failed to encrypt the legacy summary:", error);
11647
- }
11601
+ // src/lib/chatStore/types.ts
11602
+ var ChatStoreError = class extends Error {
11603
+ constructor(message, status, code, retryAfterMs) {
11604
+ super(message);
11605
+ this.status = status;
11606
+ this.code = code;
11607
+ this.retryAfterMs = retryAfterMs;
11608
+ this.name = "ChatStoreError";
11648
11609
  }
11649
- return stored2;
11650
- }
11651
- async function upsertConversationSummaryOp(ctx, conversationId, summary, summarizedUpTo, tokenCount) {
11652
- const revision2 = getConversationRevision(ctx.database, conversationId);
11653
- let record2;
11654
- const encryptedSummary = ctx.walletAddress ? await encryptField(summary, ctx.walletAddress, ctx.signMessage, ctx.embeddedWalletSigner) : summary;
11655
- await ctx.database.write(async () => {
11656
- assertConversationRevision(ctx.database, conversationId, revision2);
11657
- const existing = await ctx.summariesCollection.query(import_watermelondb5.Q.where("conversation_id", conversationId)).fetch();
11658
- if (existing.length > 0) {
11659
- record2 = await existing[0].update((rec) => {
11660
- rec.summary = encryptedSummary;
11661
- rec.summarizedUpTo = summarizedUpTo;
11662
- rec.tokenCount = tokenCount;
11663
- });
11664
- } else {
11665
- record2 = await ctx.summariesCollection.create((rec) => {
11666
- rec.conversationId = conversationId;
11667
- rec.summary = encryptedSummary;
11668
- rec.summarizedUpTo = summarizedUpTo;
11669
- rec.tokenCount = tokenCount;
11670
- });
11671
- }
11672
- });
11673
- return modelToStored(record2, summary);
11674
- }
11675
- async function deleteConversationSummaryOp(ctx, conversationId) {
11676
- await ctx.database.write(async () => {
11677
- const existing = await ctx.summariesCollection.query(import_watermelondb5.Q.where("conversation_id", conversationId)).fetch();
11678
- for (const record2 of existing) {
11679
- await record2.destroyPermanently();
11680
- }
11681
- });
11682
- }
11683
-
11684
- // src/lib/db/conversationMemory/models.ts
11685
- var import_watermelondb6 = require("@nozbe/watermelondb");
11686
- var import_decorators2 = require("@nozbe/watermelondb/decorators");
11687
- var ConversationMemory = class extends import_watermelondb6.Model {
11688
11610
  };
11689
- ConversationMemory.table = "conversation_memory";
11690
- ConversationMemory.associations = {};
11691
- __decorateClass([
11692
- (0, import_decorators2.text)("conversation_id")
11693
- ], ConversationMemory.prototype, "conversationId", 2);
11694
- __decorateClass([
11695
- (0, import_decorators2.text)("memory_id")
11696
- ], ConversationMemory.prototype, "memoryId", 2);
11697
- __decorateClass([
11698
- (0, import_decorators2.field)("score")
11699
- ], ConversationMemory.prototype, "score", 2);
11700
- __decorateClass([
11701
- import_decorators2.readonly,
11702
- (0, import_decorators2.date)("created_at")
11703
- ], ConversationMemory.prototype, "createdAt", 2);
11704
11611
 
11705
11612
  // src/lib/db/conversationMemory/operations.ts
11706
- var import_watermelondb7 = require("@nozbe/watermelondb");
11613
+ var import_watermelondb5 = require("@nozbe/watermelondb");
11707
11614
  var MAX_PER_CONVERSATION = 200;
11708
11615
  function conversationMemoryToStored(row) {
11709
11616
  return {
@@ -11718,7 +11625,7 @@ async function addConversationMemoriesOp(ctx, conversationId, items) {
11718
11625
  if (!conversationId || items.length === 0) return;
11719
11626
  const now = Date.now();
11720
11627
  await ctx.database.write(async () => {
11721
- const existing = await ctx.conversationMemoryCollection.query(import_watermelondb7.Q.where("conversation_id", conversationId), import_watermelondb7.Q.sortBy("created_at", import_watermelondb7.Q.asc)).fetch();
11628
+ const existing = await ctx.conversationMemoryCollection.query(import_watermelondb5.Q.where("conversation_id", conversationId), import_watermelondb5.Q.sortBy("created_at", import_watermelondb5.Q.asc)).fetch();
11722
11629
  const seen = new Set(existing.map((r) => r.memoryId));
11723
11630
  const batchSeen = /* @__PURE__ */ new Set();
11724
11631
  const deduped = [];
@@ -11755,7 +11662,7 @@ async function recordConversationMemoriesOp(ctx, conversationId, items, now = Da
11755
11662
  if (!conversationId || turn.size === 0) return;
11756
11663
  const kept = new Map([...turn].slice(-MAX_PER_CONVERSATION));
11757
11664
  await ctx.database.write(async () => {
11758
- const existing = await ctx.conversationMemoryCollection.query(import_watermelondb7.Q.where("conversation_id", conversationId), import_watermelondb7.Q.sortBy("created_at", import_watermelondb7.Q.asc)).fetch();
11665
+ const existing = await ctx.conversationMemoryCollection.query(import_watermelondb5.Q.where("conversation_id", conversationId), import_watermelondb5.Q.sortBy("created_at", import_watermelondb5.Q.asc)).fetch();
11759
11666
  const earlier = existing.filter((r) => !turn.has(r.memoryId));
11760
11667
  const linked = new Set(existing.map((r) => r.memoryId));
11761
11668
  await ctx.database.batch(
@@ -11780,13 +11687,13 @@ async function recordConversationMemoriesOp(ctx, conversationId, items, now = Da
11780
11687
  }
11781
11688
  async function getConversationMemoriesOp(ctx, conversationId) {
11782
11689
  if (!conversationId) return [];
11783
- const rows = await ctx.conversationMemoryCollection.query(import_watermelondb7.Q.where("conversation_id", conversationId), import_watermelondb7.Q.sortBy("created_at", import_watermelondb7.Q.asc)).fetch();
11690
+ const rows = await ctx.conversationMemoryCollection.query(import_watermelondb5.Q.where("conversation_id", conversationId), import_watermelondb5.Q.sortBy("created_at", import_watermelondb5.Q.asc)).fetch();
11784
11691
  return rows.map(conversationMemoryToStored);
11785
11692
  }
11786
11693
  async function clearConversationMemoriesOp(ctx, conversationId) {
11787
11694
  if (!conversationId) return;
11788
11695
  await ctx.database.write(async () => {
11789
- const rows = await ctx.conversationMemoryCollection.query(import_watermelondb7.Q.where("conversation_id", conversationId)).fetch();
11696
+ const rows = await ctx.conversationMemoryCollection.query(import_watermelondb5.Q.where("conversation_id", conversationId)).fetch();
11790
11697
  if (rows.length === 0) return;
11791
11698
  await ctx.database.batch(...rows.map((r) => r.prepareDestroyPermanently()));
11792
11699
  });
@@ -11801,15 +11708,37 @@ var maxChunks = 16;
11801
11708
  var droppable = ["thoughtProcess", "thinking", "sources", "toolCallEvents"];
11802
11709
  var messageMetadata = /* @__PURE__ */ new Set(["conversationId", "uniqueId", "vector", "chunks"]);
11803
11710
  var alwaysStored = /* @__PURE__ */ new Set(["role", "content", "responseDuration"]);
11711
+ var maxBubbles = 3;
11712
+ var maxTurnIdBytes = 128;
11713
+ var maxBubbleBytes = 16384;
11714
+ var anumaOrigins = /* @__PURE__ */ new Set(["intro", "reply"]);
11804
11715
  function plain(value) {
11805
11716
  return JSON.parse(JSON.stringify(value ?? {}));
11806
11717
  }
11807
- function text4(value) {
11718
+ function text3(value) {
11808
11719
  return typeof value === "string" ? value : void 0;
11809
11720
  }
11810
11721
  function number(value) {
11811
11722
  return typeof value === "number" ? value : void 0;
11812
11723
  }
11724
+ function readAnumaTurn(value) {
11725
+ if (!isObject(value)) return void 0;
11726
+ const { turnId, bubbles, origin } = value;
11727
+ if (typeof turnId !== "string" || turnId.length === 0 || new TextEncoder().encode(turnId).length > maxTurnIdBytes)
11728
+ return void 0;
11729
+ if (bubbles !== void 0 && (!Array.isArray(bubbles) || bubbles.length < 1 || bubbles.length > maxBubbles || !bubbles.every((b) => typeof b === "string") || bubbles.reduce((sum, b) => sum + new TextEncoder().encode(b).length, 0) > maxBubbleBytes))
11730
+ return void 0;
11731
+ if (origin !== void 0 && (typeof origin !== "string" || !anumaOrigins.has(origin)))
11732
+ return void 0;
11733
+ return {
11734
+ turnId,
11735
+ ...bubbles !== void 0 ? { bubbles: [...bubbles] } : {},
11736
+ ...origin !== void 0 ? { origin } : {}
11737
+ };
11738
+ }
11739
+ function isObject(value) {
11740
+ return value !== null && typeof value === "object" && !Array.isArray(value);
11741
+ }
11813
11742
  function conversationSortKey(data, createdAt) {
11814
11743
  const pinnedAt = number(data.pinnedAt);
11815
11744
  if (pinnedAt) return PINNED_SORT_BASE + pinnedAt;
@@ -11830,12 +11759,12 @@ function conversationContext(data) {
11830
11759
  }
11831
11760
  function toConversation(record2) {
11832
11761
  const pinnedAt = number(record2.data.pinnedAt);
11833
- const projectId = text4(record2.data.projectId);
11762
+ const projectId = text3(record2.data.projectId);
11834
11763
  const context = conversationContext(record2.data);
11835
11764
  return {
11836
11765
  uniqueId: record2.id,
11837
11766
  conversationId: record2.id,
11838
- title: text4(record2.data.title) ?? "",
11767
+ title: text3(record2.data.title) ?? "",
11839
11768
  ...projectId ? { projectId } : {},
11840
11769
  createdAt: new Date(record2.createdAt),
11841
11770
  updatedAt: new Date(record2.updatedAt),
@@ -11844,18 +11773,28 @@ function toConversation(record2) {
11844
11773
  ...context ? { context } : {}
11845
11774
  };
11846
11775
  }
11776
+ function anumaTurnData(value) {
11777
+ if (value === void 0) return void 0;
11778
+ const turn = readAnumaTurn(value);
11779
+ if (!turn) throw new ChatStoreError("Invalid Anuma turn", 0, "invalid_record");
11780
+ return turn;
11781
+ }
11847
11782
  function messageData(options) {
11848
- return plain(
11849
- Object.fromEntries(
11783
+ const anuma = anumaTurnData(options.anuma);
11784
+ return plain({
11785
+ ...Object.fromEntries(
11850
11786
  Object.entries(options).filter(
11851
- ([key3, value]) => !messageMetadata.has(key3) && (!!value || alwaysStored.has(key3))
11787
+ ([key3, value]) => key3 !== "anuma" && !messageMetadata.has(key3) && (!!value || alwaysStored.has(key3))
11852
11788
  )
11853
- )
11854
- );
11789
+ ),
11790
+ ...anuma ? { anuma } : {}
11791
+ });
11855
11792
  }
11856
11793
  function toMessage(record2) {
11857
- const { chunkOffsets: _offsets, truncated: _truncated, ...fields } = record2.data;
11794
+ const { chunkOffsets: _offsets, truncated: _truncated, anuma: rawAnuma, ...fields } = record2.data;
11795
+ const anuma = readAnumaTurn(rawAnuma);
11858
11796
  return {
11797
+ ...anuma ? { anuma } : {},
11859
11798
  ...fields,
11860
11799
  uniqueId: record2.id,
11861
11800
  messageId: record2.sortKey,
@@ -11869,20 +11808,20 @@ function chunkOffsets(record2) {
11869
11808
  return Array.isArray(offsets) ? offsets : void 0;
11870
11809
  }
11871
11810
  function toSkeletons(records) {
11872
- const roleById = new Map(records.map((r) => [r.id, text4(r.data.role)]));
11811
+ const roleById = new Map(records.map((r) => [r.id, text3(r.data.role)]));
11873
11812
  return records.map((r) => {
11874
- const parentMessageId = text4(r.data.parentMessageId);
11875
- const model = text4(r.data.model);
11813
+ const parentMessageId = text3(r.data.parentMessageId);
11814
+ const model = text3(r.data.model);
11876
11815
  const artifact = r.data.role === "user" && !!parentMessageId && roleById.get(parentMessageId) === "user";
11877
11816
  return {
11878
11817
  uniqueId: r.id,
11879
11818
  messageId: r.sortKey,
11880
11819
  conversationId: r.conversationId,
11881
- role: text4(r.data.role) ?? "",
11820
+ role: text3(r.data.role) ?? "",
11882
11821
  createdAt: new Date(r.createdAt),
11883
11822
  ...parentMessageId ? { parentMessageId } : {},
11884
11823
  ...model ? { model } : {},
11885
- ...artifact ? { content: text4(r.data.content) ?? "" } : {}
11824
+ ...artifact ? { content: text3(r.data.content) ?? "" } : {}
11886
11825
  };
11887
11826
  });
11888
11827
  }
@@ -11890,8 +11829,8 @@ function toSummary(record2) {
11890
11829
  return {
11891
11830
  uniqueId: record2.id,
11892
11831
  conversationId: record2.id,
11893
- summary: text4(record2.data.summary) ?? "",
11894
- summarizedUpTo: text4(record2.data.summarizedUpTo) ?? "",
11832
+ summary: text3(record2.data.summary) ?? "",
11833
+ summarizedUpTo: text3(record2.data.summarizedUpTo) ?? "",
11895
11834
  tokenCount: number(record2.data.tokenCount) ?? 0,
11896
11835
  createdAt: new Date(record2.createdAt),
11897
11836
  updatedAt: new Date(record2.updatedAt)
@@ -11942,7 +11881,7 @@ function fitRecord(record2) {
11942
11881
  delete data[field10];
11943
11882
  truncated.push(field10);
11944
11883
  }
11945
- const content = text4(data.content) ?? "";
11884
+ const content = text3(data.content) ?? "";
11946
11885
  const over = bytes({ ...record2, data }) - recordBudgetBytes;
11947
11886
  if (over > 0) {
11948
11887
  const encoded = new TextEncoder().encode(content);
@@ -11986,6 +11925,216 @@ function fitChunks(chunks3) {
11986
11925
  return out;
11987
11926
  }
11988
11927
 
11928
+ // src/lib/chatStore/anuma.ts
11929
+ var ANUMA_CONVERSATION_ID = "anuma-1on1";
11930
+ var maxAttempts = 3;
11931
+ function anumaTurnContent(bubbles) {
11932
+ return bubbles.join("\n\n");
11933
+ }
11934
+ function anumaLastReadSortKey(conversation) {
11935
+ const record2 = conversation && "record" in conversation ? conversation.record : conversation;
11936
+ const state = record2?.data.anuma;
11937
+ if (!isObject(state)) return 0;
11938
+ const sortKey = state.lastReadSortKey;
11939
+ return Number.isSafeInteger(sortKey) && sortKey > 0 ? sortKey : 0;
11940
+ }
11941
+ function conflict(error) {
11942
+ return error instanceof ChatStoreError && error.code === "version_conflict";
11943
+ }
11944
+ function live(snapshot) {
11945
+ if (snapshot?.record.isDeleted)
11946
+ throw new ChatStoreError("Anuma conversation was deleted", 409, "conversation_deleted");
11947
+ return snapshot;
11948
+ }
11949
+ async function ensureAnumaConversation(store, options = {}) {
11950
+ const existing = live(await store.get("conversation", ANUMA_CONVERSATION_ID, options.signal));
11951
+ if (existing) return existing;
11952
+ const at = (options.now ?? Date.now)();
11953
+ const data = plain({ title: "Anuma", lastActivityAt: at, anuma: { lastReadSortKey: 0 } });
11954
+ try {
11955
+ const [created] = await store.putMany(
11956
+ [
11957
+ {
11958
+ expectedVersion: 0,
11959
+ record: {
11960
+ kind: "conversation",
11961
+ id: ANUMA_CONVERSATION_ID,
11962
+ conversationId: "",
11963
+ sortKey: conversationSortKey(data, at),
11964
+ createdAt: at,
11965
+ updatedAt: at,
11966
+ isDeleted: false,
11967
+ data
11968
+ }
11969
+ }
11970
+ ],
11971
+ options.signal
11972
+ );
11973
+ return created;
11974
+ } catch (error) {
11975
+ if (!conflict(error)) throw error;
11976
+ const winner = live(await store.get("conversation", ANUMA_CONVERSATION_ID, options.signal));
11977
+ if (!winner) throw error;
11978
+ return winner;
11979
+ }
11980
+ }
11981
+ async function markAnumaRead(store, sortKey, options = {}) {
11982
+ if (!Number.isSafeInteger(sortKey) || sortKey < 0)
11983
+ throw new ChatStoreError("Invalid Anuma read marker", 0, "invalid_record");
11984
+ const now = options.now ?? Date.now;
11985
+ for (let attempt = 1; ; attempt++) {
11986
+ const current = await ensureAnumaConversation(store, options);
11987
+ const read = anumaLastReadSortKey(current);
11988
+ if (sortKey <= read) return current;
11989
+ const record2 = current.record;
11990
+ const state = isObject(record2.data.anuma) ? record2.data.anuma : {};
11991
+ const data = plain({ ...record2.data, anuma: { ...state, lastReadSortKey: sortKey } });
11992
+ try {
11993
+ const [written] = await store.putMany(
11994
+ [
11995
+ {
11996
+ expectedVersion: current.version,
11997
+ record: {
11998
+ ...record2,
11999
+ data,
12000
+ sortKey: conversationSortKey(data, record2.createdAt),
12001
+ updatedAt: Math.max(record2.updatedAt, now())
12002
+ }
12003
+ }
12004
+ ],
12005
+ options.signal
12006
+ );
12007
+ return written;
12008
+ } catch (error) {
12009
+ if (!conflict(error) || attempt >= maxAttempts) throw error;
12010
+ }
12011
+ }
12012
+ }
12013
+ function countUnreadAnumaTurns(messages, lastReadSortKey) {
12014
+ const answered = Math.max(
12015
+ 0,
12016
+ ...messages.filter((m) => m.role === "user").map((m) => m.messageId)
12017
+ );
12018
+ const floor = Math.max(lastReadSortKey, answered);
12019
+ const turns = /* @__PURE__ */ new Set();
12020
+ for (const m of messages) {
12021
+ if (m.role !== "assistant" || m.messageId <= floor) continue;
12022
+ turns.add(m.anuma?.turnId ? `turn:${m.anuma.turnId}` : `message:${m.uniqueId}`);
12023
+ }
12024
+ return turns.size;
12025
+ }
12026
+
12027
+ // src/lib/db/chat/summaryOperations.ts
12028
+ var import_watermelondb6 = require("@nozbe/watermelondb");
12029
+ init_logger();
12030
+ function createSummaryContext(database, encryption = {}) {
12031
+ return {
12032
+ ...encryption,
12033
+ database,
12034
+ summariesCollection: database.get("conversation_summaries")
12035
+ };
12036
+ }
12037
+ function modelToStored(record2, summary) {
12038
+ return {
12039
+ uniqueId: record2.id,
12040
+ conversationId: record2.conversationId,
12041
+ summary,
12042
+ summarizedUpTo: record2.summarizedUpTo,
12043
+ tokenCount: record2.tokenCount,
12044
+ createdAt: record2.createdAt,
12045
+ updatedAt: record2.updatedAt
12046
+ };
12047
+ }
12048
+ async function getConversationSummaryOp(ctx, conversationId) {
12049
+ const results = await ctx.summariesCollection.query(import_watermelondb6.Q.where("conversation_id", conversationId)).fetch();
12050
+ if (results.length === 0) return null;
12051
+ const record2 = results[0];
12052
+ const stored2 = modelToStored(record2, record2.summary);
12053
+ if (ctx.walletAddress && isEncrypted(stored2.summary)) {
12054
+ if (ctx.signMessage) {
12055
+ try {
12056
+ await requestEncryptionKey(ctx.walletAddress, ctx.signMessage, ctx.embeddedWalletSigner);
12057
+ } catch (error) {
12058
+ getLogger().warn("Failed to request the encryption key for the summary:", error);
12059
+ }
12060
+ }
12061
+ stored2.summary = await decryptField(stored2.summary, ctx.walletAddress);
12062
+ } else if (ctx.walletAddress && ctx.signMessage && stored2.summary) {
12063
+ try {
12064
+ const encryptedSummary = await encryptField(
12065
+ stored2.summary,
12066
+ ctx.walletAddress,
12067
+ ctx.signMessage,
12068
+ ctx.embeddedWalletSigner
12069
+ );
12070
+ await ctx.database.write(async () => {
12071
+ const current = await ctx.summariesCollection.query(import_watermelondb6.Q.where("id", record2.id)).fetch();
12072
+ if (current[0]?.summary === stored2.summary) {
12073
+ await current[0].update((rec) => {
12074
+ rec.summary = encryptedSummary;
12075
+ });
12076
+ }
12077
+ });
12078
+ } catch (error) {
12079
+ getLogger().warn("Failed to encrypt the legacy summary:", error);
12080
+ }
12081
+ }
12082
+ return stored2;
12083
+ }
12084
+ async function upsertConversationSummaryOp(ctx, conversationId, summary, summarizedUpTo, tokenCount) {
12085
+ const revision2 = getConversationRevision(ctx.database, conversationId);
12086
+ let record2;
12087
+ const encryptedSummary = ctx.walletAddress ? await encryptField(summary, ctx.walletAddress, ctx.signMessage, ctx.embeddedWalletSigner) : summary;
12088
+ await ctx.database.write(async () => {
12089
+ assertConversationRevision(ctx.database, conversationId, revision2);
12090
+ const existing = await ctx.summariesCollection.query(import_watermelondb6.Q.where("conversation_id", conversationId)).fetch();
12091
+ if (existing.length > 0) {
12092
+ record2 = await existing[0].update((rec) => {
12093
+ rec.summary = encryptedSummary;
12094
+ rec.summarizedUpTo = summarizedUpTo;
12095
+ rec.tokenCount = tokenCount;
12096
+ });
12097
+ } else {
12098
+ record2 = await ctx.summariesCollection.create((rec) => {
12099
+ rec.conversationId = conversationId;
12100
+ rec.summary = encryptedSummary;
12101
+ rec.summarizedUpTo = summarizedUpTo;
12102
+ rec.tokenCount = tokenCount;
12103
+ });
12104
+ }
12105
+ });
12106
+ return modelToStored(record2, summary);
12107
+ }
12108
+ async function deleteConversationSummaryOp(ctx, conversationId) {
12109
+ await ctx.database.write(async () => {
12110
+ const existing = await ctx.summariesCollection.query(import_watermelondb6.Q.where("conversation_id", conversationId)).fetch();
12111
+ for (const record2 of existing) {
12112
+ await record2.destroyPermanently();
12113
+ }
12114
+ });
12115
+ }
12116
+
12117
+ // src/lib/db/conversationMemory/models.ts
12118
+ var import_watermelondb7 = require("@nozbe/watermelondb");
12119
+ var import_decorators2 = require("@nozbe/watermelondb/decorators");
12120
+ var ConversationMemory = class extends import_watermelondb7.Model {
12121
+ };
12122
+ ConversationMemory.table = "conversation_memory";
12123
+ ConversationMemory.associations = {};
12124
+ __decorateClass([
12125
+ (0, import_decorators2.text)("conversation_id")
12126
+ ], ConversationMemory.prototype, "conversationId", 2);
12127
+ __decorateClass([
12128
+ (0, import_decorators2.text)("memory_id")
12129
+ ], ConversationMemory.prototype, "memoryId", 2);
12130
+ __decorateClass([
12131
+ (0, import_decorators2.field)("score")
12132
+ ], ConversationMemory.prototype, "score", 2);
12133
+ __decorateClass([
12134
+ import_decorators2.readonly,
12135
+ (0, import_decorators2.date)("created_at")
12136
+ ], ConversationMemory.prototype, "createdAt", 2);
12137
+
11989
12138
  // src/lib/chatStore/chatImports.ts
11990
12139
  var importBatch = 100;
11991
12140
  var importChunks = 200;
@@ -12570,17 +12719,17 @@ async function createRemoteMemoryPersistence(options) {
12570
12719
  expectedVersion = typeof expectedVersion === "number" ? expectedVersion : snapshot.version;
12571
12720
  if (!Number.isSafeInteger(expectedVersion) || expectedVersion < 0)
12572
12721
  throw new Error("expectedVersion must be a nonnegative integer");
12573
- const conflict = () => new RemoteMemoryError("The memory changed since it was read", 409, "version_conflict");
12722
+ const conflict2 = () => new RemoteMemoryError("The memory changed since it was read", 409, "version_conflict");
12574
12723
  let existing;
12575
12724
  if (expectedVersion !== 0 && !snapshot) {
12576
12725
  try {
12577
12726
  existing = memoryRecord(await request(path, {}, signal));
12578
12727
  } catch (error) {
12579
- if (error instanceof RemoteMemoryError && error.status === 404) throw conflict();
12728
+ if (error instanceof RemoteMemoryError && error.status === 404) throw conflict2();
12580
12729
  throw error;
12581
12730
  }
12582
12731
  }
12583
- if (existing && existing.version !== expectedVersion) throw conflict();
12732
+ if (existing && existing.version !== expectedVersion) throw conflict2();
12584
12733
  const decoded = existing ? await decode(existing).catch(() => void 0) : void 0;
12585
12734
  const reusableContent = snapshot?.encryptedContent ?? existing?.memory.content;
12586
12735
  const content = reusableContent && (snapshot?.content ?? decoded?.memory.content) === memory.content ? reusableContent : await encryptField2(memory.content);
@@ -12805,17 +12954,6 @@ async function createRemoteMemoryPersistence(options) {
12805
12954
  };
12806
12955
  }
12807
12956
 
12808
- // src/lib/chatStore/types.ts
12809
- var ChatStoreError = class extends Error {
12810
- constructor(message, status, code, retryAfterMs) {
12811
- super(message);
12812
- this.status = status;
12813
- this.code = code;
12814
- this.retryAfterMs = retryAfterMs;
12815
- this.name = "ChatStoreError";
12816
- }
12817
- };
12818
-
12819
12957
  // src/lib/chatStore/remote.ts
12820
12958
  var kinds = /* @__PURE__ */ new Set(["conversation", "message", "summary", "project", "memory_links", "setting"]);
12821
12959
  var cipherPattern = /^enc:v\d+:(?:[0-9a-f]{2}){28,}$/i;
@@ -13540,10 +13678,10 @@ async function deletedSinceUpload(store, journal, local, signal) {
13540
13678
  const present = /* @__PURE__ */ new Set([...local.records.map((r) => key(r.record)), ...local.skipped.keys()]);
13541
13679
  const gone = [...journal.keys].filter((k) => !present.has(k));
13542
13680
  if (gone.length === 0) return [];
13543
- const live = (await store.getMany(gone.map(ref2), signal)).filter((s) => !s.record.isDeleted);
13544
- const tombstoning = new Set(live.map((s) => key(s.record)));
13681
+ const live2 = (await store.getMany(gone.map(ref2), signal)).filter((s) => !s.record.isDeleted);
13682
+ const tombstoning = new Set(live2.map((s) => key(s.record)));
13545
13683
  await journal.drop(gone.filter((k) => !tombstoning.has(k)));
13546
- return live.map(({ record: record2, version }) => {
13684
+ return live2.map(({ record: record2, version }) => {
13547
13685
  const empty = emptied[record2.kind];
13548
13686
  return {
13549
13687
  record: {
@@ -14203,12 +14341,13 @@ var resumableFields = [
14203
14341
  "error",
14204
14342
  "thoughtProcess",
14205
14343
  "thinking",
14206
- "toolCallEvents"
14344
+ "toolCallEvents",
14345
+ "anuma"
14207
14346
  ];
14208
14347
  function resumedFields(options) {
14209
14348
  return plain(
14210
14349
  Object.fromEntries(
14211
- resumableFields.filter((f) => options[f] !== void 0).map((f) => [f, options[f]])
14350
+ resumableFields.filter((f) => options[f] !== void 0).map((f) => [f, f === "anuma" ? anumaTurnData(options.anuma) : options[f]])
14212
14351
  )
14213
14352
  );
14214
14353
  }
@@ -14862,7 +15001,7 @@ function createVaultChatBackend(options) {
14862
15001
  },
14863
15002
  async getConversations(query) {
14864
15003
  await available2();
14865
- return all("conversation").map(toConversation).filter(
15004
+ return all("conversation").filter((record2) => record2.id !== ANUMA_CONVERSATION_ID).map(toConversation).filter(
14866
15005
  (c) => query?.projectId === void 0 || (query.projectId === null ? !c.projectId : c.projectId === query.projectId)
14867
15006
  ).sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime());
14868
15007
  },
@@ -14905,6 +15044,7 @@ function createVaultChatBackend(options) {
14905
15044
  return !!record2 && !record2.isDeleted;
14906
15045
  },
14907
15046
  async deleteConversation(conversationId) {
15047
+ if (conversationId === ANUMA_CONVERSATION_ID) return false;
14908
15048
  const record2 = await conversation(conversationId);
14909
15049
  if (!record2 || record2.isDeleted) return false;
14910
15050
  for (const [k, patch] of pending)
@@ -17908,7 +18048,7 @@ async function countActiveVaultMemoriesOp(ctx) {
17908
18048
  return ctx.vaultMemoryCollection.query(...baseVaultConditions(ctx)).fetchCount();
17909
18049
  }
17910
18050
  async function getVaultMemoryCountsOp(ctx) {
17911
- const [live, archived, quarantined] = await Promise.all([
18051
+ const [live2, archived, quarantined] = await Promise.all([
17912
18052
  countActiveVaultMemoriesOp(ctx),
17913
18053
  ctx.vaultMemoryCollection.query(
17914
18054
  ...baseVaultConditions(ctx, { includeArchived: true }),
@@ -17919,7 +18059,7 @@ async function getVaultMemoryCountsOp(ctx) {
17919
18059
  import_watermelondb21.Q.where("trust_tier", "quarantined")
17920
18060
  ).fetchCount()
17921
18061
  ]);
17922
- return { live, archived, quarantined };
18062
+ return { live: live2, archived, quarantined };
17923
18063
  }
17924
18064
  async function getActiveVaultMemoryIdsOp(ctx, ids) {
17925
18065
  if (ids.length === 0) return /* @__PURE__ */ new Set();
@@ -19206,12 +19346,12 @@ var WalletPoller = class {
19206
19346
  * @param maxAttempts - Maximum polling attempts before giving up (default: 60)
19207
19347
  * @returns Stop function to cancel polling
19208
19348
  */
19209
- startPolling(checkWallet, onWalletReady, intervalMs = DEFAULT_INTERVAL_MS, maxAttempts = DEFAULT_MAX_ATTEMPTS) {
19349
+ startPolling(checkWallet, onWalletReady, intervalMs = DEFAULT_INTERVAL_MS, maxAttempts2 = DEFAULT_MAX_ATTEMPTS) {
19210
19350
  this.stop();
19211
19351
  this.attempts = 0;
19212
19352
  const poll = async () => {
19213
19353
  this.attempts++;
19214
- if (this.attempts > maxAttempts) {
19354
+ if (this.attempts > maxAttempts2) {
19215
19355
  this.stop();
19216
19356
  return;
19217
19357
  }
@@ -26621,13 +26761,13 @@ function createRemoteMemoryStore(options) {
26621
26761
  });
26622
26762
  return run;
26623
26763
  };
26624
- const live = async (id2) => {
26764
+ const live2 = async (id2) => {
26625
26765
  const record2 = await persistence.get(id2);
26626
26766
  return record2 && !record2.memory.is_deleted ? record2 : null;
26627
26767
  };
26628
26768
  const mutateLocked = async (id2, change, edit, writeOptions) => {
26629
26769
  writeOptions?.signal?.throwIfAborted();
26630
- const record2 = writeOptions?.signal ? await persistence.get(id2, writeOptions.signal) : await live(id2);
26770
+ const record2 = writeOptions?.signal ? await persistence.get(id2, writeOptions.signal) : await live2(id2);
26631
26771
  if (!record2) return null;
26632
26772
  if (record2.memory.is_deleted) return null;
26633
26773
  if (writeOptions?.canWrite && !await writeOptions.canWrite())
@@ -27425,7 +27565,7 @@ function createRemoteMemoryStore(options) {
27425
27565
  }, clear);
27426
27566
  return snapshot.then((topics) => structuredClone(topics));
27427
27567
  },
27428
- get: async (id2) => asStored(await live(id2)),
27568
+ get: async (id2) => asStored(await live2(id2)),
27429
27569
  listArchived: async () => (await enumerate({ archived_only: true, order: "archived", include_embeddings: true })).map(
27430
27570
  (record2) => stored(record2.memory)
27431
27571
  ),
@@ -27510,7 +27650,7 @@ function createRemoteMemoryStore(options) {
27510
27650
  return serialized(
27511
27651
  first,
27512
27652
  () => serialized(second, async () => {
27513
- const [old, next] = await Promise.all([live(id2), live(supersededById)]);
27653
+ const [old, next] = await Promise.all([live2(id2), live2(supersededById)]);
27514
27654
  if (!old || !next || old.memory.superseded_by || next.memory.superseded_by) return false;
27515
27655
  if (old.memory.kind !== void 0 && old.memory.kind !== null) return false;
27516
27656
  const saved = await persistence.putMany([
@@ -27531,7 +27671,7 @@ function createRemoteMemoryStore(options) {
27531
27671
  (memory) => memory.archived_at === void 0 || memory.archived_at === null ? { ...memory, archived_at: Date.now() } : null
27532
27672
  ),
27533
27673
  restore: (id2) => serialized(id2, async () => {
27534
- const record2 = await live(id2);
27674
+ const record2 = await live2(id2);
27535
27675
  if (!record2) return false;
27536
27676
  if (record2.memory.archived_at === void 0 || record2.memory.archived_at === null)
27537
27677
  return true;
@@ -34704,6 +34844,7 @@ function useChatStorage(options) {
34704
34844
  }
34705
34845
  // Annotate the CommonJS export names for ESM import in node:
34706
34846
  0 && (module.exports = {
34847
+ ANUMA_CONVERSATION_ID,
34707
34848
  CHUNKS_DISCARDED_ORIGIN,
34708
34849
  ChatConversation,
34709
34850
  ChatMessage,
@@ -34763,6 +34904,8 @@ function useChatStorage(options) {
34763
34904
  WALLET_PAYMENT_TOOL_NAME,
34764
34905
  WalletPoller,
34765
34906
  addConversationMemoriesOp,
34907
+ anumaLastReadSortKey,
34908
+ anumaTurnContent,
34766
34909
  archiveVaultMemoryOp,
34767
34910
  assembleMemoryContext,
34768
34911
  attachFileContextToLastUserMessage,
@@ -34796,6 +34939,7 @@ function useChatStorage(options) {
34796
34939
  consoleLogger,
34797
34940
  conversationMemoryToStored,
34798
34941
  cosineInt8,
34942
+ countUnreadAnumaTurns,
34799
34943
  createAutoExtractor,
34800
34944
  createChunkVectorCache,
34801
34945
  createCryptoPricePreProcessor,
@@ -34844,6 +34988,7 @@ function useChatStorage(options) {
34844
34988
  embedMessage,
34845
34989
  encodeChunkVector,
34846
34990
  encryptToolCallEventsOp,
34991
+ ensureAnumaConversation,
34847
34992
  ensureDefaultFoldersOp,
34848
34993
  exchangeNotionCode,
34849
34994
  extractAndLinkEntitiesForMemoriesOp,
@@ -34884,6 +35029,7 @@ function useChatStorage(options) {
34884
35029
  isRerankerAvailable,
34885
35030
  loadSettings,
34886
35031
  localChatBackend,
35032
+ markAnumaRead,
34887
35033
  maskScopedEmbeddingCache,
34888
35034
  maxVaultAttachmentBytes,
34889
35035
  memoryCipher,
@@ -34899,6 +35045,7 @@ function useChatStorage(options) {
34899
35045
  quantizeEmbedding,
34900
35046
  queueManager,
34901
35047
  rankProfileCandidates,
35048
+ readAnumaTurn,
34902
35049
  recall,
34903
35050
  reencryptMemoryField,
34904
35051
  reflect,