@anuma/sdk 10.7.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.
@@ -6163,17 +6163,17 @@ async function createRemoteMemoryPersistence(options) {
6163
6163
  expectedVersion = typeof expectedVersion === "number" ? expectedVersion : snapshot.version;
6164
6164
  if (!Number.isSafeInteger(expectedVersion) || expectedVersion < 0)
6165
6165
  throw new Error("expectedVersion must be a nonnegative integer");
6166
- const conflict = () => new RemoteMemoryError("The memory changed since it was read", 409, "version_conflict");
6166
+ const conflict2 = () => new RemoteMemoryError("The memory changed since it was read", 409, "version_conflict");
6167
6167
  let existing;
6168
6168
  if (expectedVersion !== 0 && !snapshot) {
6169
6169
  try {
6170
6170
  existing = memoryRecord(await request(path, {}, signal));
6171
6171
  } catch (error) {
6172
- if (error instanceof RemoteMemoryError && error.status === 404) throw conflict();
6172
+ if (error instanceof RemoteMemoryError && error.status === 404) throw conflict2();
6173
6173
  throw error;
6174
6174
  }
6175
6175
  }
6176
- if (existing && existing.version !== expectedVersion) throw conflict();
6176
+ if (existing && existing.version !== expectedVersion) throw conflict2();
6177
6177
  const decoded = existing ? await decode(existing).catch(() => void 0) : void 0;
6178
6178
  const reusableContent = snapshot?.encryptedContent ?? existing?.memory.content;
6179
6179
  const content = reusableContent && (snapshot?.content ?? decoded?.memory.content) === memory.content ? reusableContent : await encryptField2(memory.content);
@@ -11331,118 +11331,19 @@ function createWebSearchPreProcessor(options = {}) {
11331
11331
  };
11332
11332
  }
11333
11333
 
11334
- // src/lib/db/chat/summaryOperations.ts
11335
- import { Q as Q3 } from "@nozbe/watermelondb";
11336
- function createSummaryContext(database, encryption = {}) {
11337
- return {
11338
- ...encryption,
11339
- database,
11340
- summariesCollection: database.get("conversation_summaries")
11341
- };
11342
- }
11343
- function modelToStored(record2, summary) {
11344
- return {
11345
- uniqueId: record2.id,
11346
- conversationId: record2.conversationId,
11347
- summary,
11348
- summarizedUpTo: record2.summarizedUpTo,
11349
- tokenCount: record2.tokenCount,
11350
- createdAt: record2.createdAt,
11351
- updatedAt: record2.updatedAt
11352
- };
11353
- }
11354
- async function getConversationSummaryOp(ctx, conversationId) {
11355
- const results = await ctx.summariesCollection.query(Q3.where("conversation_id", conversationId)).fetch();
11356
- if (results.length === 0) return null;
11357
- const record2 = results[0];
11358
- const stored2 = modelToStored(record2, record2.summary);
11359
- if (ctx.walletAddress && isEncrypted(stored2.summary)) {
11360
- if (ctx.signMessage) {
11361
- try {
11362
- await requestEncryptionKey(ctx.walletAddress, ctx.signMessage, ctx.embeddedWalletSigner);
11363
- } catch (error) {
11364
- getLogger().warn("Failed to request the encryption key for the summary:", error);
11365
- }
11366
- }
11367
- stored2.summary = await decryptField(stored2.summary, ctx.walletAddress);
11368
- } else if (ctx.walletAddress && ctx.signMessage && stored2.summary) {
11369
- try {
11370
- const encryptedSummary = await encryptField(
11371
- stored2.summary,
11372
- ctx.walletAddress,
11373
- ctx.signMessage,
11374
- ctx.embeddedWalletSigner
11375
- );
11376
- await ctx.database.write(async () => {
11377
- const current = await ctx.summariesCollection.query(Q3.where("id", record2.id)).fetch();
11378
- if (current[0]?.summary === stored2.summary) {
11379
- await current[0].update((rec) => {
11380
- rec.summary = encryptedSummary;
11381
- });
11382
- }
11383
- });
11384
- } catch (error) {
11385
- getLogger().warn("Failed to encrypt the legacy summary:", error);
11386
- }
11334
+ // src/lib/chatStore/types.ts
11335
+ var ChatStoreError = class extends Error {
11336
+ constructor(message, status, code, retryAfterMs) {
11337
+ super(message);
11338
+ this.status = status;
11339
+ this.code = code;
11340
+ this.retryAfterMs = retryAfterMs;
11341
+ this.name = "ChatStoreError";
11387
11342
  }
11388
- return stored2;
11389
- }
11390
- async function upsertConversationSummaryOp(ctx, conversationId, summary, summarizedUpTo, tokenCount) {
11391
- const revision2 = getConversationRevision(ctx.database, conversationId);
11392
- let record2;
11393
- const encryptedSummary = ctx.walletAddress ? await encryptField(summary, ctx.walletAddress, ctx.signMessage, ctx.embeddedWalletSigner) : summary;
11394
- await ctx.database.write(async () => {
11395
- assertConversationRevision(ctx.database, conversationId, revision2);
11396
- const existing = await ctx.summariesCollection.query(Q3.where("conversation_id", conversationId)).fetch();
11397
- if (existing.length > 0) {
11398
- record2 = await existing[0].update((rec) => {
11399
- rec.summary = encryptedSummary;
11400
- rec.summarizedUpTo = summarizedUpTo;
11401
- rec.tokenCount = tokenCount;
11402
- });
11403
- } else {
11404
- record2 = await ctx.summariesCollection.create((rec) => {
11405
- rec.conversationId = conversationId;
11406
- rec.summary = encryptedSummary;
11407
- rec.summarizedUpTo = summarizedUpTo;
11408
- rec.tokenCount = tokenCount;
11409
- });
11410
- }
11411
- });
11412
- return modelToStored(record2, summary);
11413
- }
11414
- async function deleteConversationSummaryOp(ctx, conversationId) {
11415
- await ctx.database.write(async () => {
11416
- const existing = await ctx.summariesCollection.query(Q3.where("conversation_id", conversationId)).fetch();
11417
- for (const record2 of existing) {
11418
- await record2.destroyPermanently();
11419
- }
11420
- });
11421
- }
11422
-
11423
- // src/lib/db/conversationMemory/models.ts
11424
- import { Model as Model3 } from "@nozbe/watermelondb";
11425
- import { date as date2, field as field2, readonly, text as text3 } from "@nozbe/watermelondb/decorators";
11426
- var ConversationMemory = class extends Model3 {
11427
11343
  };
11428
- ConversationMemory.table = "conversation_memory";
11429
- ConversationMemory.associations = {};
11430
- __decorateClass([
11431
- text3("conversation_id")
11432
- ], ConversationMemory.prototype, "conversationId", 2);
11433
- __decorateClass([
11434
- text3("memory_id")
11435
- ], ConversationMemory.prototype, "memoryId", 2);
11436
- __decorateClass([
11437
- field2("score")
11438
- ], ConversationMemory.prototype, "score", 2);
11439
- __decorateClass([
11440
- readonly,
11441
- date2("created_at")
11442
- ], ConversationMemory.prototype, "createdAt", 2);
11443
11344
 
11444
11345
  // src/lib/db/conversationMemory/operations.ts
11445
- import { Q as Q4 } from "@nozbe/watermelondb";
11346
+ import { Q as Q3 } from "@nozbe/watermelondb";
11446
11347
  var MAX_PER_CONVERSATION = 200;
11447
11348
  function conversationMemoryToStored(row) {
11448
11349
  return {
@@ -11457,7 +11358,7 @@ async function addConversationMemoriesOp(ctx, conversationId, items) {
11457
11358
  if (!conversationId || items.length === 0) return;
11458
11359
  const now = Date.now();
11459
11360
  await ctx.database.write(async () => {
11460
- const existing = await ctx.conversationMemoryCollection.query(Q4.where("conversation_id", conversationId), Q4.sortBy("created_at", Q4.asc)).fetch();
11361
+ const existing = await ctx.conversationMemoryCollection.query(Q3.where("conversation_id", conversationId), Q3.sortBy("created_at", Q3.asc)).fetch();
11461
11362
  const seen = new Set(existing.map((r) => r.memoryId));
11462
11363
  const batchSeen = /* @__PURE__ */ new Set();
11463
11364
  const deduped = [];
@@ -11494,7 +11395,7 @@ async function recordConversationMemoriesOp(ctx, conversationId, items, now = Da
11494
11395
  if (!conversationId || turn.size === 0) return;
11495
11396
  const kept = new Map([...turn].slice(-MAX_PER_CONVERSATION));
11496
11397
  await ctx.database.write(async () => {
11497
- const existing = await ctx.conversationMemoryCollection.query(Q4.where("conversation_id", conversationId), Q4.sortBy("created_at", Q4.asc)).fetch();
11398
+ const existing = await ctx.conversationMemoryCollection.query(Q3.where("conversation_id", conversationId), Q3.sortBy("created_at", Q3.asc)).fetch();
11498
11399
  const earlier = existing.filter((r) => !turn.has(r.memoryId));
11499
11400
  const linked = new Set(existing.map((r) => r.memoryId));
11500
11401
  await ctx.database.batch(
@@ -11519,13 +11420,13 @@ async function recordConversationMemoriesOp(ctx, conversationId, items, now = Da
11519
11420
  }
11520
11421
  async function getConversationMemoriesOp(ctx, conversationId) {
11521
11422
  if (!conversationId) return [];
11522
- const rows = await ctx.conversationMemoryCollection.query(Q4.where("conversation_id", conversationId), Q4.sortBy("created_at", Q4.asc)).fetch();
11423
+ const rows = await ctx.conversationMemoryCollection.query(Q3.where("conversation_id", conversationId), Q3.sortBy("created_at", Q3.asc)).fetch();
11523
11424
  return rows.map(conversationMemoryToStored);
11524
11425
  }
11525
11426
  async function clearConversationMemoriesOp(ctx, conversationId) {
11526
11427
  if (!conversationId) return;
11527
11428
  await ctx.database.write(async () => {
11528
- const rows = await ctx.conversationMemoryCollection.query(Q4.where("conversation_id", conversationId)).fetch();
11429
+ const rows = await ctx.conversationMemoryCollection.query(Q3.where("conversation_id", conversationId)).fetch();
11529
11430
  if (rows.length === 0) return;
11530
11431
  await ctx.database.batch(...rows.map((r) => r.prepareDestroyPermanently()));
11531
11432
  });
@@ -11540,46 +11441,93 @@ var maxChunks = 16;
11540
11441
  var droppable = ["thoughtProcess", "thinking", "sources", "toolCallEvents"];
11541
11442
  var messageMetadata = /* @__PURE__ */ new Set(["conversationId", "uniqueId", "vector", "chunks"]);
11542
11443
  var alwaysStored = /* @__PURE__ */ new Set(["role", "content", "responseDuration"]);
11444
+ var maxBubbles = 3;
11445
+ var maxTurnIdBytes = 128;
11446
+ var maxBubbleBytes = 16384;
11447
+ var anumaOrigins = /* @__PURE__ */ new Set(["intro", "reply"]);
11543
11448
  function plain(value) {
11544
11449
  return JSON.parse(JSON.stringify(value ?? {}));
11545
11450
  }
11546
- function text4(value) {
11451
+ function text3(value) {
11547
11452
  return typeof value === "string" ? value : void 0;
11548
11453
  }
11549
11454
  function number(value) {
11550
11455
  return typeof value === "number" ? value : void 0;
11551
11456
  }
11457
+ function readAnumaTurn(value) {
11458
+ if (!isObject(value)) return void 0;
11459
+ const { turnId, bubbles, origin } = value;
11460
+ if (typeof turnId !== "string" || turnId.length === 0 || new TextEncoder().encode(turnId).length > maxTurnIdBytes)
11461
+ return void 0;
11462
+ 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))
11463
+ return void 0;
11464
+ if (origin !== void 0 && (typeof origin !== "string" || !anumaOrigins.has(origin)))
11465
+ return void 0;
11466
+ return {
11467
+ turnId,
11468
+ ...bubbles !== void 0 ? { bubbles: [...bubbles] } : {},
11469
+ ...origin !== void 0 ? { origin } : {}
11470
+ };
11471
+ }
11472
+ function isObject(value) {
11473
+ return value !== null && typeof value === "object" && !Array.isArray(value);
11474
+ }
11552
11475
  function conversationSortKey(data, createdAt) {
11553
11476
  const pinnedAt = number(data.pinnedAt);
11554
11477
  if (pinnedAt) return PINNED_SORT_BASE + pinnedAt;
11555
11478
  return number(data.lastActivityAt) ?? createdAt;
11556
11479
  }
11480
+ var CONTEXT_FIELD_PREFIX = "context.";
11481
+ function contextField(key3) {
11482
+ return `${CONTEXT_FIELD_PREFIX}${key3}`;
11483
+ }
11484
+ function contextFields(context) {
11485
+ return plain(
11486
+ Object.fromEntries(Object.entries(context ?? {}).map(([k, value]) => [contextField(k), value]))
11487
+ );
11488
+ }
11489
+ function conversationContext(data) {
11490
+ const entries = Object.entries(data).filter(([field10]) => field10.startsWith(CONTEXT_FIELD_PREFIX)).map(([field10, value]) => [field10.slice(CONTEXT_FIELD_PREFIX.length), value]);
11491
+ return entries.length > 0 ? plain(Object.fromEntries(entries)) : void 0;
11492
+ }
11557
11493
  function toConversation(record2) {
11558
11494
  const pinnedAt = number(record2.data.pinnedAt);
11559
- const projectId = text4(record2.data.projectId);
11495
+ const projectId = text3(record2.data.projectId);
11496
+ const context = conversationContext(record2.data);
11560
11497
  return {
11561
11498
  uniqueId: record2.id,
11562
11499
  conversationId: record2.id,
11563
- title: text4(record2.data.title) ?? "",
11500
+ title: text3(record2.data.title) ?? "",
11564
11501
  ...projectId ? { projectId } : {},
11565
11502
  createdAt: new Date(record2.createdAt),
11566
11503
  updatedAt: new Date(record2.updatedAt),
11567
11504
  isDeleted: record2.isDeleted,
11568
- pinnedAt: pinnedAt ? new Date(pinnedAt) : null
11505
+ pinnedAt: pinnedAt ? new Date(pinnedAt) : null,
11506
+ ...context ? { context } : {}
11569
11507
  };
11570
11508
  }
11509
+ function anumaTurnData(value) {
11510
+ if (value === void 0) return void 0;
11511
+ const turn = readAnumaTurn(value);
11512
+ if (!turn) throw new ChatStoreError("Invalid Anuma turn", 0, "invalid_record");
11513
+ return turn;
11514
+ }
11571
11515
  function messageData(options) {
11572
- return plain(
11573
- Object.fromEntries(
11516
+ const anuma = anumaTurnData(options.anuma);
11517
+ return plain({
11518
+ ...Object.fromEntries(
11574
11519
  Object.entries(options).filter(
11575
- ([key3, value]) => !messageMetadata.has(key3) && (!!value || alwaysStored.has(key3))
11520
+ ([key3, value]) => key3 !== "anuma" && !messageMetadata.has(key3) && (!!value || alwaysStored.has(key3))
11576
11521
  )
11577
- )
11578
- );
11522
+ ),
11523
+ ...anuma ? { anuma } : {}
11524
+ });
11579
11525
  }
11580
11526
  function toMessage(record2) {
11581
- const { chunkOffsets: _offsets, truncated: _truncated, ...fields } = record2.data;
11527
+ const { chunkOffsets: _offsets, truncated: _truncated, anuma: rawAnuma, ...fields } = record2.data;
11528
+ const anuma = readAnumaTurn(rawAnuma);
11582
11529
  return {
11530
+ ...anuma ? { anuma } : {},
11583
11531
  ...fields,
11584
11532
  uniqueId: record2.id,
11585
11533
  messageId: record2.sortKey,
@@ -11593,20 +11541,20 @@ function chunkOffsets(record2) {
11593
11541
  return Array.isArray(offsets) ? offsets : void 0;
11594
11542
  }
11595
11543
  function toSkeletons(records) {
11596
- const roleById = new Map(records.map((r) => [r.id, text4(r.data.role)]));
11544
+ const roleById = new Map(records.map((r) => [r.id, text3(r.data.role)]));
11597
11545
  return records.map((r) => {
11598
- const parentMessageId = text4(r.data.parentMessageId);
11599
- const model = text4(r.data.model);
11546
+ const parentMessageId = text3(r.data.parentMessageId);
11547
+ const model = text3(r.data.model);
11600
11548
  const artifact = r.data.role === "user" && !!parentMessageId && roleById.get(parentMessageId) === "user";
11601
11549
  return {
11602
11550
  uniqueId: r.id,
11603
11551
  messageId: r.sortKey,
11604
11552
  conversationId: r.conversationId,
11605
- role: text4(r.data.role) ?? "",
11553
+ role: text3(r.data.role) ?? "",
11606
11554
  createdAt: new Date(r.createdAt),
11607
11555
  ...parentMessageId ? { parentMessageId } : {},
11608
11556
  ...model ? { model } : {},
11609
- ...artifact ? { content: text4(r.data.content) ?? "" } : {}
11557
+ ...artifact ? { content: text3(r.data.content) ?? "" } : {}
11610
11558
  };
11611
11559
  });
11612
11560
  }
@@ -11614,8 +11562,8 @@ function toSummary(record2) {
11614
11562
  return {
11615
11563
  uniqueId: record2.id,
11616
11564
  conversationId: record2.id,
11617
- summary: text4(record2.data.summary) ?? "",
11618
- summarizedUpTo: text4(record2.data.summarizedUpTo) ?? "",
11565
+ summary: text3(record2.data.summary) ?? "",
11566
+ summarizedUpTo: text3(record2.data.summarizedUpTo) ?? "",
11619
11567
  tokenCount: number(record2.data.tokenCount) ?? 0,
11620
11568
  createdAt: new Date(record2.createdAt),
11621
11569
  updatedAt: new Date(record2.updatedAt)
@@ -11666,7 +11614,7 @@ function fitRecord(record2) {
11666
11614
  delete data[field10];
11667
11615
  truncated.push(field10);
11668
11616
  }
11669
- const content = text4(data.content) ?? "";
11617
+ const content = text3(data.content) ?? "";
11670
11618
  const over = bytes({ ...record2, data }) - recordBudgetBytes;
11671
11619
  if (over > 0) {
11672
11620
  const encoded = new TextEncoder().encode(content);
@@ -11710,6 +11658,215 @@ function fitChunks(chunks3) {
11710
11658
  return out;
11711
11659
  }
11712
11660
 
11661
+ // src/lib/chatStore/anuma.ts
11662
+ var ANUMA_CONVERSATION_ID = "anuma-1on1";
11663
+ var maxAttempts = 3;
11664
+ function anumaTurnContent(bubbles) {
11665
+ return bubbles.join("\n\n");
11666
+ }
11667
+ function anumaLastReadSortKey(conversation) {
11668
+ const record2 = conversation && "record" in conversation ? conversation.record : conversation;
11669
+ const state = record2?.data.anuma;
11670
+ if (!isObject(state)) return 0;
11671
+ const sortKey = state.lastReadSortKey;
11672
+ return Number.isSafeInteger(sortKey) && sortKey > 0 ? sortKey : 0;
11673
+ }
11674
+ function conflict(error) {
11675
+ return error instanceof ChatStoreError && error.code === "version_conflict";
11676
+ }
11677
+ function live(snapshot) {
11678
+ if (snapshot?.record.isDeleted)
11679
+ throw new ChatStoreError("Anuma conversation was deleted", 409, "conversation_deleted");
11680
+ return snapshot;
11681
+ }
11682
+ async function ensureAnumaConversation(store, options = {}) {
11683
+ const existing = live(await store.get("conversation", ANUMA_CONVERSATION_ID, options.signal));
11684
+ if (existing) return existing;
11685
+ const at = (options.now ?? Date.now)();
11686
+ const data = plain({ title: "Anuma", lastActivityAt: at, anuma: { lastReadSortKey: 0 } });
11687
+ try {
11688
+ const [created] = await store.putMany(
11689
+ [
11690
+ {
11691
+ expectedVersion: 0,
11692
+ record: {
11693
+ kind: "conversation",
11694
+ id: ANUMA_CONVERSATION_ID,
11695
+ conversationId: "",
11696
+ sortKey: conversationSortKey(data, at),
11697
+ createdAt: at,
11698
+ updatedAt: at,
11699
+ isDeleted: false,
11700
+ data
11701
+ }
11702
+ }
11703
+ ],
11704
+ options.signal
11705
+ );
11706
+ return created;
11707
+ } catch (error) {
11708
+ if (!conflict(error)) throw error;
11709
+ const winner = live(await store.get("conversation", ANUMA_CONVERSATION_ID, options.signal));
11710
+ if (!winner) throw error;
11711
+ return winner;
11712
+ }
11713
+ }
11714
+ async function markAnumaRead(store, sortKey, options = {}) {
11715
+ if (!Number.isSafeInteger(sortKey) || sortKey < 0)
11716
+ throw new ChatStoreError("Invalid Anuma read marker", 0, "invalid_record");
11717
+ const now = options.now ?? Date.now;
11718
+ for (let attempt = 1; ; attempt++) {
11719
+ const current = await ensureAnumaConversation(store, options);
11720
+ const read = anumaLastReadSortKey(current);
11721
+ if (sortKey <= read) return current;
11722
+ const record2 = current.record;
11723
+ const state = isObject(record2.data.anuma) ? record2.data.anuma : {};
11724
+ const data = plain({ ...record2.data, anuma: { ...state, lastReadSortKey: sortKey } });
11725
+ try {
11726
+ const [written] = await store.putMany(
11727
+ [
11728
+ {
11729
+ expectedVersion: current.version,
11730
+ record: {
11731
+ ...record2,
11732
+ data,
11733
+ sortKey: conversationSortKey(data, record2.createdAt),
11734
+ updatedAt: Math.max(record2.updatedAt, now())
11735
+ }
11736
+ }
11737
+ ],
11738
+ options.signal
11739
+ );
11740
+ return written;
11741
+ } catch (error) {
11742
+ if (!conflict(error) || attempt >= maxAttempts) throw error;
11743
+ }
11744
+ }
11745
+ }
11746
+ function countUnreadAnumaTurns(messages, lastReadSortKey) {
11747
+ const answered = Math.max(
11748
+ 0,
11749
+ ...messages.filter((m) => m.role === "user").map((m) => m.messageId)
11750
+ );
11751
+ const floor = Math.max(lastReadSortKey, answered);
11752
+ const turns = /* @__PURE__ */ new Set();
11753
+ for (const m of messages) {
11754
+ if (m.role !== "assistant" || m.messageId <= floor) continue;
11755
+ turns.add(m.anuma?.turnId ? `turn:${m.anuma.turnId}` : `message:${m.uniqueId}`);
11756
+ }
11757
+ return turns.size;
11758
+ }
11759
+
11760
+ // src/lib/db/chat/summaryOperations.ts
11761
+ import { Q as Q4 } from "@nozbe/watermelondb";
11762
+ function createSummaryContext(database, encryption = {}) {
11763
+ return {
11764
+ ...encryption,
11765
+ database,
11766
+ summariesCollection: database.get("conversation_summaries")
11767
+ };
11768
+ }
11769
+ function modelToStored(record2, summary) {
11770
+ return {
11771
+ uniqueId: record2.id,
11772
+ conversationId: record2.conversationId,
11773
+ summary,
11774
+ summarizedUpTo: record2.summarizedUpTo,
11775
+ tokenCount: record2.tokenCount,
11776
+ createdAt: record2.createdAt,
11777
+ updatedAt: record2.updatedAt
11778
+ };
11779
+ }
11780
+ async function getConversationSummaryOp(ctx, conversationId) {
11781
+ const results = await ctx.summariesCollection.query(Q4.where("conversation_id", conversationId)).fetch();
11782
+ if (results.length === 0) return null;
11783
+ const record2 = results[0];
11784
+ const stored2 = modelToStored(record2, record2.summary);
11785
+ if (ctx.walletAddress && isEncrypted(stored2.summary)) {
11786
+ if (ctx.signMessage) {
11787
+ try {
11788
+ await requestEncryptionKey(ctx.walletAddress, ctx.signMessage, ctx.embeddedWalletSigner);
11789
+ } catch (error) {
11790
+ getLogger().warn("Failed to request the encryption key for the summary:", error);
11791
+ }
11792
+ }
11793
+ stored2.summary = await decryptField(stored2.summary, ctx.walletAddress);
11794
+ } else if (ctx.walletAddress && ctx.signMessage && stored2.summary) {
11795
+ try {
11796
+ const encryptedSummary = await encryptField(
11797
+ stored2.summary,
11798
+ ctx.walletAddress,
11799
+ ctx.signMessage,
11800
+ ctx.embeddedWalletSigner
11801
+ );
11802
+ await ctx.database.write(async () => {
11803
+ const current = await ctx.summariesCollection.query(Q4.where("id", record2.id)).fetch();
11804
+ if (current[0]?.summary === stored2.summary) {
11805
+ await current[0].update((rec) => {
11806
+ rec.summary = encryptedSummary;
11807
+ });
11808
+ }
11809
+ });
11810
+ } catch (error) {
11811
+ getLogger().warn("Failed to encrypt the legacy summary:", error);
11812
+ }
11813
+ }
11814
+ return stored2;
11815
+ }
11816
+ async function upsertConversationSummaryOp(ctx, conversationId, summary, summarizedUpTo, tokenCount) {
11817
+ const revision2 = getConversationRevision(ctx.database, conversationId);
11818
+ let record2;
11819
+ const encryptedSummary = ctx.walletAddress ? await encryptField(summary, ctx.walletAddress, ctx.signMessage, ctx.embeddedWalletSigner) : summary;
11820
+ await ctx.database.write(async () => {
11821
+ assertConversationRevision(ctx.database, conversationId, revision2);
11822
+ const existing = await ctx.summariesCollection.query(Q4.where("conversation_id", conversationId)).fetch();
11823
+ if (existing.length > 0) {
11824
+ record2 = await existing[0].update((rec) => {
11825
+ rec.summary = encryptedSummary;
11826
+ rec.summarizedUpTo = summarizedUpTo;
11827
+ rec.tokenCount = tokenCount;
11828
+ });
11829
+ } else {
11830
+ record2 = await ctx.summariesCollection.create((rec) => {
11831
+ rec.conversationId = conversationId;
11832
+ rec.summary = encryptedSummary;
11833
+ rec.summarizedUpTo = summarizedUpTo;
11834
+ rec.tokenCount = tokenCount;
11835
+ });
11836
+ }
11837
+ });
11838
+ return modelToStored(record2, summary);
11839
+ }
11840
+ async function deleteConversationSummaryOp(ctx, conversationId) {
11841
+ await ctx.database.write(async () => {
11842
+ const existing = await ctx.summariesCollection.query(Q4.where("conversation_id", conversationId)).fetch();
11843
+ for (const record2 of existing) {
11844
+ await record2.destroyPermanently();
11845
+ }
11846
+ });
11847
+ }
11848
+
11849
+ // src/lib/db/conversationMemory/models.ts
11850
+ import { Model as Model3 } from "@nozbe/watermelondb";
11851
+ import { date as date2, field as field2, readonly, text as text4 } from "@nozbe/watermelondb/decorators";
11852
+ var ConversationMemory = class extends Model3 {
11853
+ };
11854
+ ConversationMemory.table = "conversation_memory";
11855
+ ConversationMemory.associations = {};
11856
+ __decorateClass([
11857
+ text4("conversation_id")
11858
+ ], ConversationMemory.prototype, "conversationId", 2);
11859
+ __decorateClass([
11860
+ text4("memory_id")
11861
+ ], ConversationMemory.prototype, "memoryId", 2);
11862
+ __decorateClass([
11863
+ field2("score")
11864
+ ], ConversationMemory.prototype, "score", 2);
11865
+ __decorateClass([
11866
+ readonly,
11867
+ date2("created_at")
11868
+ ], ConversationMemory.prototype, "createdAt", 2);
11869
+
11713
11870
  // src/lib/chatStore/chatImports.ts
11714
11871
  var importBatch = 100;
11715
11872
  var importChunks = 200;
@@ -11724,6 +11881,7 @@ function bytes2(value) {
11724
11881
  function conversationRecord(conversation, messages) {
11725
11882
  const createdAt = ms(conversation.createdAt);
11726
11883
  const data = plain({
11884
+ ...contextFields(conversation.context),
11727
11885
  title: conversation.title,
11728
11886
  projectId: conversation.projectId || void 0,
11729
11887
  pinnedAt: conversation.pinnedAt ? ms(conversation.pinnedAt) : void 0,
@@ -11800,17 +11958,6 @@ function batches(records) {
11800
11958
  return out;
11801
11959
  }
11802
11960
 
11803
- // src/lib/chatStore/types.ts
11804
- var ChatStoreError = class extends Error {
11805
- constructor(message, status, code, retryAfterMs) {
11806
- super(message);
11807
- this.status = status;
11808
- this.code = code;
11809
- this.retryAfterMs = retryAfterMs;
11810
- this.name = "ChatStoreError";
11811
- }
11812
- };
11813
-
11814
11961
  // src/lib/chatStore/remote.ts
11815
11962
  var kinds = /* @__PURE__ */ new Set(["conversation", "message", "summary", "project", "memory_links", "setting"]);
11816
11963
  var cipherPattern = /^enc:v\d+:(?:[0-9a-f]{2}){28,}$/i;
@@ -12535,10 +12682,10 @@ async function deletedSinceUpload(store, journal, local, signal) {
12535
12682
  const present = /* @__PURE__ */ new Set([...local.records.map((r) => key(r.record)), ...local.skipped.keys()]);
12536
12683
  const gone = [...journal.keys].filter((k) => !present.has(k));
12537
12684
  if (gone.length === 0) return [];
12538
- const live = (await store.getMany(gone.map(ref2), signal)).filter((s) => !s.record.isDeleted);
12539
- const tombstoning = new Set(live.map((s) => key(s.record)));
12685
+ const live2 = (await store.getMany(gone.map(ref2), signal)).filter((s) => !s.record.isDeleted);
12686
+ const tombstoning = new Set(live2.map((s) => key(s.record)));
12540
12687
  await journal.drop(gone.filter((k) => !tombstoning.has(k)));
12541
- return live.map(({ record: record2, version }) => {
12688
+ return live2.map(({ record: record2, version }) => {
12542
12689
  const empty = emptied[record2.kind];
12543
12690
  return {
12544
12691
  record: {
@@ -13198,12 +13345,13 @@ var resumableFields = [
13198
13345
  "error",
13199
13346
  "thoughtProcess",
13200
13347
  "thinking",
13201
- "toolCallEvents"
13348
+ "toolCallEvents",
13349
+ "anuma"
13202
13350
  ];
13203
13351
  function resumedFields(options) {
13204
13352
  return plain(
13205
13353
  Object.fromEntries(
13206
- resumableFields.filter((f) => options[f] !== void 0).map((f) => [f, options[f]])
13354
+ resumableFields.filter((f) => options[f] !== void 0).map((f) => [f, f === "anuma" ? anumaTurnData(options.anuma) : options[f]])
13207
13355
  )
13208
13356
  );
13209
13357
  }
@@ -13857,7 +14005,7 @@ function createVaultChatBackend(options) {
13857
14005
  },
13858
14006
  async getConversations(query) {
13859
14007
  await available2();
13860
- return all("conversation").map(toConversation).filter(
14008
+ return all("conversation").filter((record2) => record2.id !== ANUMA_CONVERSATION_ID).map(toConversation).filter(
13861
14009
  (c) => query?.projectId === void 0 || (query.projectId === null ? !c.projectId : c.projectId === query.projectId)
13862
14010
  ).sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime());
13863
14011
  },
@@ -13893,7 +14041,14 @@ function createVaultChatBackend(options) {
13893
14041
  async updateConversationPinned(conversationId, pinned) {
13894
14042
  return updateConversation(conversationId, { pinnedAt: pinned ? now() : null });
13895
14043
  },
14044
+ async updateConversationContext(conversationId, context) {
14045
+ const set = contextFields(context);
14046
+ if (Object.keys(set).length > 0) return updateConversation(conversationId, set);
14047
+ const record2 = await conversation(conversationId);
14048
+ return !!record2 && !record2.isDeleted;
14049
+ },
13896
14050
  async deleteConversation(conversationId) {
14051
+ if (conversationId === ANUMA_CONVERSATION_ID) return false;
13897
14052
  const record2 = await conversation(conversationId);
13898
14053
  if (!record2 || record2.isDeleted) return false;
13899
14054
  for (const [k, patch] of pending)
@@ -17964,7 +18119,7 @@ async function countActiveVaultMemoriesOp(ctx) {
17964
18119
  return ctx.vaultMemoryCollection.query(...baseVaultConditions(ctx)).fetchCount();
17965
18120
  }
17966
18121
  async function getVaultMemoryCountsOp(ctx) {
17967
- const [live, archived, quarantined] = await Promise.all([
18122
+ const [live2, archived, quarantined] = await Promise.all([
17968
18123
  countActiveVaultMemoriesOp(ctx),
17969
18124
  ctx.vaultMemoryCollection.query(
17970
18125
  ...baseVaultConditions(ctx, { includeArchived: true }),
@@ -17975,7 +18130,7 @@ async function getVaultMemoryCountsOp(ctx) {
17975
18130
  Q9.where("trust_tier", "quarantined")
17976
18131
  ).fetchCount()
17977
18132
  ]);
17978
- return { live, archived, quarantined };
18133
+ return { live: live2, archived, quarantined };
17979
18134
  }
17980
18135
  async function getActiveVaultMemoryIdsOp(ctx, ids) {
17981
18136
  if (ids.length === 0) return /* @__PURE__ */ new Set();
@@ -19347,12 +19502,12 @@ var WalletPoller = class {
19347
19502
  * @param maxAttempts - Maximum polling attempts before giving up (default: 60)
19348
19503
  * @returns Stop function to cancel polling
19349
19504
  */
19350
- startPolling(checkWallet, onWalletReady, intervalMs = DEFAULT_INTERVAL_MS, maxAttempts = DEFAULT_MAX_ATTEMPTS) {
19505
+ startPolling(checkWallet, onWalletReady, intervalMs = DEFAULT_INTERVAL_MS, maxAttempts2 = DEFAULT_MAX_ATTEMPTS) {
19351
19506
  this.stop();
19352
19507
  this.attempts = 0;
19353
19508
  const poll = async () => {
19354
19509
  this.attempts++;
19355
- if (this.attempts > maxAttempts) {
19510
+ if (this.attempts > maxAttempts2) {
19356
19511
  this.stop();
19357
19512
  return;
19358
19513
  }
@@ -27119,13 +27274,13 @@ function createRemoteMemoryStore(options) {
27119
27274
  });
27120
27275
  return run;
27121
27276
  };
27122
- const live = async (id2) => {
27277
+ const live2 = async (id2) => {
27123
27278
  const record2 = await persistence.get(id2);
27124
27279
  return record2 && !record2.memory.is_deleted ? record2 : null;
27125
27280
  };
27126
27281
  const mutateLocked = async (id2, change, edit, writeOptions) => {
27127
27282
  writeOptions?.signal?.throwIfAborted();
27128
- const record2 = writeOptions?.signal ? await persistence.get(id2, writeOptions.signal) : await live(id2);
27283
+ const record2 = writeOptions?.signal ? await persistence.get(id2, writeOptions.signal) : await live2(id2);
27129
27284
  if (!record2) return null;
27130
27285
  if (record2.memory.is_deleted) return null;
27131
27286
  if (writeOptions?.canWrite && !await writeOptions.canWrite())
@@ -27923,7 +28078,7 @@ function createRemoteMemoryStore(options) {
27923
28078
  }, clear);
27924
28079
  return snapshot.then((topics) => structuredClone(topics));
27925
28080
  },
27926
- get: async (id2) => asStored(await live(id2)),
28081
+ get: async (id2) => asStored(await live2(id2)),
27927
28082
  listArchived: async () => (await enumerate({ archived_only: true, order: "archived", include_embeddings: true })).map(
27928
28083
  (record2) => stored(record2.memory)
27929
28084
  ),
@@ -28008,7 +28163,7 @@ function createRemoteMemoryStore(options) {
28008
28163
  return serialized(
28009
28164
  first,
28010
28165
  () => serialized(second, async () => {
28011
- const [old, next] = await Promise.all([live(id2), live(supersededById)]);
28166
+ const [old, next] = await Promise.all([live2(id2), live2(supersededById)]);
28012
28167
  if (!old || !next || old.memory.superseded_by || next.memory.superseded_by) return false;
28013
28168
  if (old.memory.kind !== void 0 && old.memory.kind !== null) return false;
28014
28169
  const saved = await persistence.putMany([
@@ -28029,7 +28184,7 @@ function createRemoteMemoryStore(options) {
28029
28184
  (memory) => memory.archived_at === void 0 || memory.archived_at === null ? { ...memory, archived_at: Date.now() } : null
28030
28185
  ),
28031
28186
  restore: (id2) => serialized(id2, async () => {
28032
- const record2 = await live(id2);
28187
+ const record2 = await live2(id2);
28033
28188
  if (!record2) return false;
28034
28189
  if (record2.memory.archived_at === void 0 || record2.memory.archived_at === null)
28035
28190
  return true;
@@ -39639,11 +39794,11 @@ function usePhoneCalls(options = {}) {
39639
39794
  pollRunIdRef.current = runId;
39640
39795
  setIsPolling(true);
39641
39796
  const intervalMs = pollOptions.intervalMs ?? DEFAULT_POLL_INTERVAL_MS;
39642
- const maxAttempts = pollOptions.maxAttempts ?? DEFAULT_POLL_MAX_ATTEMPTS;
39797
+ const maxAttempts2 = pollOptions.maxAttempts ?? DEFAULT_POLL_MAX_ATTEMPTS;
39643
39798
  const stopWhenCompleted = pollOptions.stopWhenCompleted ?? true;
39644
39799
  let latestCall = null;
39645
39800
  try {
39646
- for (let attempt = 0; attempt < maxAttempts; attempt += 1) {
39801
+ for (let attempt = 0; attempt < maxAttempts2; attempt += 1) {
39647
39802
  if (pollRunIdRef.current !== runId) {
39648
39803
  break;
39649
39804
  }
@@ -39673,7 +39828,7 @@ function usePhoneCalls(options = {}) {
39673
39828
  break;
39674
39829
  }
39675
39830
  }
39676
- if (attempt < maxAttempts - 1) {
39831
+ if (attempt < maxAttempts2 - 1) {
39677
39832
  await wait2(intervalMs);
39678
39833
  }
39679
39834
  }
@@ -41191,6 +41346,7 @@ function useWalletBinding(options = {}) {
41191
41346
  };
41192
41347
  }
41193
41348
  export {
41349
+ ANUMA_CONVERSATION_ID,
41194
41350
  Anuma,
41195
41351
  AnumaJsxError,
41196
41352
  AnumaShadowIsolationProvider,
@@ -41295,6 +41451,8 @@ export {
41295
41451
  WordProcessor,
41296
41452
  ZipProcessor,
41297
41453
  addConversationMemoriesOp,
41454
+ anumaLastReadSortKey,
41455
+ anumaTurnContent,
41298
41456
  appFileToStored,
41299
41457
  archiveVaultMemoryOp,
41300
41458
  assembleMemoryContext,
@@ -41334,6 +41492,7 @@ export {
41334
41492
  consoleLogger,
41335
41493
  conversationMemoryToStored,
41336
41494
  cosineInt8,
41495
+ countUnreadAnumaTurns,
41337
41496
  createAutoExtractor,
41338
41497
  createChunkVectorCache,
41339
41498
  createCryptoPricePreProcessor,
@@ -41403,6 +41562,7 @@ export {
41403
41562
  encryptDataBytes,
41404
41563
  encryptDataWithKey,
41405
41564
  encryptToolCallEventsOp,
41565
+ ensureAnumaConversation,
41406
41566
  ensureDefaultFoldersOp,
41407
41567
  expandToolSetsAdditive,
41408
41568
  exportElementToPdf,
@@ -41513,6 +41673,7 @@ export {
41513
41673
  listActiveStreams,
41514
41674
  loadSettings,
41515
41675
  localChatBackend,
41676
+ markAnumaRead,
41516
41677
  maskScopedEmbeddingCache,
41517
41678
  maxVaultAttachmentBytes,
41518
41679
  mediaToStored,
@@ -41538,6 +41699,7 @@ export {
41538
41699
  quantizeEmbedding,
41539
41700
  queueManager,
41540
41701
  rankProfileCandidates,
41702
+ readAnumaTurn,
41541
41703
  readEncryptedFile,
41542
41704
  recall,
41543
41705
  reencryptMemoryField,