@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.
@@ -9923,118 +9923,19 @@ var xhrTransport = (options) => {
9923
9923
  return { stream: createStream() };
9924
9924
  };
9925
9925
 
9926
- // src/lib/db/chat/summaryOperations.ts
9927
- import { Q as Q3 } from "@nozbe/watermelondb";
9928
- function createSummaryContext(database, encryption = {}) {
9929
- return {
9930
- ...encryption,
9931
- database,
9932
- summariesCollection: database.get("conversation_summaries")
9933
- };
9934
- }
9935
- function modelToStored(record2, summary) {
9936
- return {
9937
- uniqueId: record2.id,
9938
- conversationId: record2.conversationId,
9939
- summary,
9940
- summarizedUpTo: record2.summarizedUpTo,
9941
- tokenCount: record2.tokenCount,
9942
- createdAt: record2.createdAt,
9943
- updatedAt: record2.updatedAt
9944
- };
9945
- }
9946
- async function getConversationSummaryOp(ctx, conversationId) {
9947
- const results = await ctx.summariesCollection.query(Q3.where("conversation_id", conversationId)).fetch();
9948
- if (results.length === 0) return null;
9949
- const record2 = results[0];
9950
- const stored2 = modelToStored(record2, record2.summary);
9951
- if (ctx.walletAddress && isEncrypted(stored2.summary)) {
9952
- if (ctx.signMessage) {
9953
- try {
9954
- await requestEncryptionKey(ctx.walletAddress, ctx.signMessage, ctx.embeddedWalletSigner);
9955
- } catch (error) {
9956
- getLogger().warn("Failed to request the encryption key for the summary:", error);
9957
- }
9958
- }
9959
- stored2.summary = await decryptField(stored2.summary, ctx.walletAddress);
9960
- } else if (ctx.walletAddress && ctx.signMessage && stored2.summary) {
9961
- try {
9962
- const encryptedSummary = await encryptField(
9963
- stored2.summary,
9964
- ctx.walletAddress,
9965
- ctx.signMessage,
9966
- ctx.embeddedWalletSigner
9967
- );
9968
- await ctx.database.write(async () => {
9969
- const current = await ctx.summariesCollection.query(Q3.where("id", record2.id)).fetch();
9970
- if (current[0]?.summary === stored2.summary) {
9971
- await current[0].update((rec) => {
9972
- rec.summary = encryptedSummary;
9973
- });
9974
- }
9975
- });
9976
- } catch (error) {
9977
- getLogger().warn("Failed to encrypt the legacy summary:", error);
9978
- }
9926
+ // src/lib/chatStore/types.ts
9927
+ var ChatStoreError = class extends Error {
9928
+ constructor(message, status, code, retryAfterMs) {
9929
+ super(message);
9930
+ this.status = status;
9931
+ this.code = code;
9932
+ this.retryAfterMs = retryAfterMs;
9933
+ this.name = "ChatStoreError";
9979
9934
  }
9980
- return stored2;
9981
- }
9982
- async function upsertConversationSummaryOp(ctx, conversationId, summary, summarizedUpTo, tokenCount) {
9983
- const revision2 = getConversationRevision(ctx.database, conversationId);
9984
- let record2;
9985
- const encryptedSummary = ctx.walletAddress ? await encryptField(summary, ctx.walletAddress, ctx.signMessage, ctx.embeddedWalletSigner) : summary;
9986
- await ctx.database.write(async () => {
9987
- assertConversationRevision(ctx.database, conversationId, revision2);
9988
- const existing = await ctx.summariesCollection.query(Q3.where("conversation_id", conversationId)).fetch();
9989
- if (existing.length > 0) {
9990
- record2 = await existing[0].update((rec) => {
9991
- rec.summary = encryptedSummary;
9992
- rec.summarizedUpTo = summarizedUpTo;
9993
- rec.tokenCount = tokenCount;
9994
- });
9995
- } else {
9996
- record2 = await ctx.summariesCollection.create((rec) => {
9997
- rec.conversationId = conversationId;
9998
- rec.summary = encryptedSummary;
9999
- rec.summarizedUpTo = summarizedUpTo;
10000
- rec.tokenCount = tokenCount;
10001
- });
10002
- }
10003
- });
10004
- return modelToStored(record2, summary);
10005
- }
10006
- async function deleteConversationSummaryOp(ctx, conversationId) {
10007
- await ctx.database.write(async () => {
10008
- const existing = await ctx.summariesCollection.query(Q3.where("conversation_id", conversationId)).fetch();
10009
- for (const record2 of existing) {
10010
- await record2.destroyPermanently();
10011
- }
10012
- });
10013
- }
10014
-
10015
- // src/lib/db/conversationMemory/models.ts
10016
- import { Model as Model3 } from "@nozbe/watermelondb";
10017
- import { date as date2, field as field2, readonly, text as text3 } from "@nozbe/watermelondb/decorators";
10018
- var ConversationMemory = class extends Model3 {
10019
9935
  };
10020
- ConversationMemory.table = "conversation_memory";
10021
- ConversationMemory.associations = {};
10022
- __decorateClass([
10023
- text3("conversation_id")
10024
- ], ConversationMemory.prototype, "conversationId", 2);
10025
- __decorateClass([
10026
- text3("memory_id")
10027
- ], ConversationMemory.prototype, "memoryId", 2);
10028
- __decorateClass([
10029
- field2("score")
10030
- ], ConversationMemory.prototype, "score", 2);
10031
- __decorateClass([
10032
- readonly,
10033
- date2("created_at")
10034
- ], ConversationMemory.prototype, "createdAt", 2);
10035
9936
 
10036
9937
  // src/lib/db/conversationMemory/operations.ts
10037
- import { Q as Q4 } from "@nozbe/watermelondb";
9938
+ import { Q as Q3 } from "@nozbe/watermelondb";
10038
9939
  var MAX_PER_CONVERSATION = 200;
10039
9940
  function conversationMemoryToStored(row) {
10040
9941
  return {
@@ -10049,7 +9950,7 @@ async function addConversationMemoriesOp(ctx, conversationId, items) {
10049
9950
  if (!conversationId || items.length === 0) return;
10050
9951
  const now = Date.now();
10051
9952
  await ctx.database.write(async () => {
10052
- const existing = await ctx.conversationMemoryCollection.query(Q4.where("conversation_id", conversationId), Q4.sortBy("created_at", Q4.asc)).fetch();
9953
+ const existing = await ctx.conversationMemoryCollection.query(Q3.where("conversation_id", conversationId), Q3.sortBy("created_at", Q3.asc)).fetch();
10053
9954
  const seen = new Set(existing.map((r) => r.memoryId));
10054
9955
  const batchSeen = /* @__PURE__ */ new Set();
10055
9956
  const deduped = [];
@@ -10086,7 +9987,7 @@ async function recordConversationMemoriesOp(ctx, conversationId, items, now = Da
10086
9987
  if (!conversationId || turn.size === 0) return;
10087
9988
  const kept = new Map([...turn].slice(-MAX_PER_CONVERSATION));
10088
9989
  await ctx.database.write(async () => {
10089
- const existing = await ctx.conversationMemoryCollection.query(Q4.where("conversation_id", conversationId), Q4.sortBy("created_at", Q4.asc)).fetch();
9990
+ const existing = await ctx.conversationMemoryCollection.query(Q3.where("conversation_id", conversationId), Q3.sortBy("created_at", Q3.asc)).fetch();
10090
9991
  const earlier = existing.filter((r) => !turn.has(r.memoryId));
10091
9992
  const linked = new Set(existing.map((r) => r.memoryId));
10092
9993
  await ctx.database.batch(
@@ -10111,13 +10012,13 @@ async function recordConversationMemoriesOp(ctx, conversationId, items, now = Da
10111
10012
  }
10112
10013
  async function getConversationMemoriesOp(ctx, conversationId) {
10113
10014
  if (!conversationId) return [];
10114
- const rows = await ctx.conversationMemoryCollection.query(Q4.where("conversation_id", conversationId), Q4.sortBy("created_at", Q4.asc)).fetch();
10015
+ const rows = await ctx.conversationMemoryCollection.query(Q3.where("conversation_id", conversationId), Q3.sortBy("created_at", Q3.asc)).fetch();
10115
10016
  return rows.map(conversationMemoryToStored);
10116
10017
  }
10117
10018
  async function clearConversationMemoriesOp(ctx, conversationId) {
10118
10019
  if (!conversationId) return;
10119
10020
  await ctx.database.write(async () => {
10120
- const rows = await ctx.conversationMemoryCollection.query(Q4.where("conversation_id", conversationId)).fetch();
10021
+ const rows = await ctx.conversationMemoryCollection.query(Q3.where("conversation_id", conversationId)).fetch();
10121
10022
  if (rows.length === 0) return;
10122
10023
  await ctx.database.batch(...rows.map((r) => r.prepareDestroyPermanently()));
10123
10024
  });
@@ -10132,46 +10033,93 @@ var maxChunks = 16;
10132
10033
  var droppable = ["thoughtProcess", "thinking", "sources", "toolCallEvents"];
10133
10034
  var messageMetadata = /* @__PURE__ */ new Set(["conversationId", "uniqueId", "vector", "chunks"]);
10134
10035
  var alwaysStored = /* @__PURE__ */ new Set(["role", "content", "responseDuration"]);
10036
+ var maxBubbles = 3;
10037
+ var maxTurnIdBytes = 128;
10038
+ var maxBubbleBytes = 16384;
10039
+ var anumaOrigins = /* @__PURE__ */ new Set(["intro", "reply"]);
10135
10040
  function plain(value) {
10136
10041
  return JSON.parse(JSON.stringify(value ?? {}));
10137
10042
  }
10138
- function text4(value) {
10043
+ function text3(value) {
10139
10044
  return typeof value === "string" ? value : void 0;
10140
10045
  }
10141
10046
  function number(value) {
10142
10047
  return typeof value === "number" ? value : void 0;
10143
10048
  }
10049
+ function readAnumaTurn(value) {
10050
+ if (!isObject(value)) return void 0;
10051
+ const { turnId, bubbles, origin } = value;
10052
+ if (typeof turnId !== "string" || turnId.length === 0 || new TextEncoder().encode(turnId).length > maxTurnIdBytes)
10053
+ return void 0;
10054
+ 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))
10055
+ return void 0;
10056
+ if (origin !== void 0 && (typeof origin !== "string" || !anumaOrigins.has(origin)))
10057
+ return void 0;
10058
+ return {
10059
+ turnId,
10060
+ ...bubbles !== void 0 ? { bubbles: [...bubbles] } : {},
10061
+ ...origin !== void 0 ? { origin } : {}
10062
+ };
10063
+ }
10064
+ function isObject(value) {
10065
+ return value !== null && typeof value === "object" && !Array.isArray(value);
10066
+ }
10144
10067
  function conversationSortKey(data, createdAt) {
10145
10068
  const pinnedAt = number(data.pinnedAt);
10146
10069
  if (pinnedAt) return PINNED_SORT_BASE + pinnedAt;
10147
10070
  return number(data.lastActivityAt) ?? createdAt;
10148
10071
  }
10072
+ var CONTEXT_FIELD_PREFIX = "context.";
10073
+ function contextField(key3) {
10074
+ return `${CONTEXT_FIELD_PREFIX}${key3}`;
10075
+ }
10076
+ function contextFields(context) {
10077
+ return plain(
10078
+ Object.fromEntries(Object.entries(context ?? {}).map(([k, value]) => [contextField(k), value]))
10079
+ );
10080
+ }
10081
+ function conversationContext(data) {
10082
+ const entries = Object.entries(data).filter(([field10]) => field10.startsWith(CONTEXT_FIELD_PREFIX)).map(([field10, value]) => [field10.slice(CONTEXT_FIELD_PREFIX.length), value]);
10083
+ return entries.length > 0 ? plain(Object.fromEntries(entries)) : void 0;
10084
+ }
10149
10085
  function toConversation(record2) {
10150
10086
  const pinnedAt = number(record2.data.pinnedAt);
10151
- const projectId = text4(record2.data.projectId);
10087
+ const projectId = text3(record2.data.projectId);
10088
+ const context = conversationContext(record2.data);
10152
10089
  return {
10153
10090
  uniqueId: record2.id,
10154
10091
  conversationId: record2.id,
10155
- title: text4(record2.data.title) ?? "",
10092
+ title: text3(record2.data.title) ?? "",
10156
10093
  ...projectId ? { projectId } : {},
10157
10094
  createdAt: new Date(record2.createdAt),
10158
10095
  updatedAt: new Date(record2.updatedAt),
10159
10096
  isDeleted: record2.isDeleted,
10160
- pinnedAt: pinnedAt ? new Date(pinnedAt) : null
10097
+ pinnedAt: pinnedAt ? new Date(pinnedAt) : null,
10098
+ ...context ? { context } : {}
10161
10099
  };
10162
10100
  }
10101
+ function anumaTurnData(value) {
10102
+ if (value === void 0) return void 0;
10103
+ const turn = readAnumaTurn(value);
10104
+ if (!turn) throw new ChatStoreError("Invalid Anuma turn", 0, "invalid_record");
10105
+ return turn;
10106
+ }
10163
10107
  function messageData(options) {
10164
- return plain(
10165
- Object.fromEntries(
10108
+ const anuma = anumaTurnData(options.anuma);
10109
+ return plain({
10110
+ ...Object.fromEntries(
10166
10111
  Object.entries(options).filter(
10167
- ([key3, value]) => !messageMetadata.has(key3) && (!!value || alwaysStored.has(key3))
10112
+ ([key3, value]) => key3 !== "anuma" && !messageMetadata.has(key3) && (!!value || alwaysStored.has(key3))
10168
10113
  )
10169
- )
10170
- );
10114
+ ),
10115
+ ...anuma ? { anuma } : {}
10116
+ });
10171
10117
  }
10172
10118
  function toMessage(record2) {
10173
- const { chunkOffsets: _offsets, truncated: _truncated, ...fields } = record2.data;
10119
+ const { chunkOffsets: _offsets, truncated: _truncated, anuma: rawAnuma, ...fields } = record2.data;
10120
+ const anuma = readAnumaTurn(rawAnuma);
10174
10121
  return {
10122
+ ...anuma ? { anuma } : {},
10175
10123
  ...fields,
10176
10124
  uniqueId: record2.id,
10177
10125
  messageId: record2.sortKey,
@@ -10185,20 +10133,20 @@ function chunkOffsets(record2) {
10185
10133
  return Array.isArray(offsets) ? offsets : void 0;
10186
10134
  }
10187
10135
  function toSkeletons(records) {
10188
- const roleById = new Map(records.map((r) => [r.id, text4(r.data.role)]));
10136
+ const roleById = new Map(records.map((r) => [r.id, text3(r.data.role)]));
10189
10137
  return records.map((r) => {
10190
- const parentMessageId = text4(r.data.parentMessageId);
10191
- const model = text4(r.data.model);
10138
+ const parentMessageId = text3(r.data.parentMessageId);
10139
+ const model = text3(r.data.model);
10192
10140
  const artifact = r.data.role === "user" && !!parentMessageId && roleById.get(parentMessageId) === "user";
10193
10141
  return {
10194
10142
  uniqueId: r.id,
10195
10143
  messageId: r.sortKey,
10196
10144
  conversationId: r.conversationId,
10197
- role: text4(r.data.role) ?? "",
10145
+ role: text3(r.data.role) ?? "",
10198
10146
  createdAt: new Date(r.createdAt),
10199
10147
  ...parentMessageId ? { parentMessageId } : {},
10200
10148
  ...model ? { model } : {},
10201
- ...artifact ? { content: text4(r.data.content) ?? "" } : {}
10149
+ ...artifact ? { content: text3(r.data.content) ?? "" } : {}
10202
10150
  };
10203
10151
  });
10204
10152
  }
@@ -10206,8 +10154,8 @@ function toSummary(record2) {
10206
10154
  return {
10207
10155
  uniqueId: record2.id,
10208
10156
  conversationId: record2.id,
10209
- summary: text4(record2.data.summary) ?? "",
10210
- summarizedUpTo: text4(record2.data.summarizedUpTo) ?? "",
10157
+ summary: text3(record2.data.summary) ?? "",
10158
+ summarizedUpTo: text3(record2.data.summarizedUpTo) ?? "",
10211
10159
  tokenCount: number(record2.data.tokenCount) ?? 0,
10212
10160
  createdAt: new Date(record2.createdAt),
10213
10161
  updatedAt: new Date(record2.updatedAt)
@@ -10258,7 +10206,7 @@ function fitRecord(record2) {
10258
10206
  delete data[field10];
10259
10207
  truncated.push(field10);
10260
10208
  }
10261
- const content = text4(data.content) ?? "";
10209
+ const content = text3(data.content) ?? "";
10262
10210
  const over = bytes({ ...record2, data }) - recordBudgetBytes;
10263
10211
  if (over > 0) {
10264
10212
  const encoded = new TextEncoder().encode(content);
@@ -10302,6 +10250,215 @@ function fitChunks(chunks3) {
10302
10250
  return out;
10303
10251
  }
10304
10252
 
10253
+ // src/lib/chatStore/anuma.ts
10254
+ var ANUMA_CONVERSATION_ID = "anuma-1on1";
10255
+ var maxAttempts = 3;
10256
+ function anumaTurnContent(bubbles) {
10257
+ return bubbles.join("\n\n");
10258
+ }
10259
+ function anumaLastReadSortKey(conversation) {
10260
+ const record2 = conversation && "record" in conversation ? conversation.record : conversation;
10261
+ const state = record2?.data.anuma;
10262
+ if (!isObject(state)) return 0;
10263
+ const sortKey = state.lastReadSortKey;
10264
+ return Number.isSafeInteger(sortKey) && sortKey > 0 ? sortKey : 0;
10265
+ }
10266
+ function conflict(error) {
10267
+ return error instanceof ChatStoreError && error.code === "version_conflict";
10268
+ }
10269
+ function live(snapshot) {
10270
+ if (snapshot?.record.isDeleted)
10271
+ throw new ChatStoreError("Anuma conversation was deleted", 409, "conversation_deleted");
10272
+ return snapshot;
10273
+ }
10274
+ async function ensureAnumaConversation(store, options = {}) {
10275
+ const existing = live(await store.get("conversation", ANUMA_CONVERSATION_ID, options.signal));
10276
+ if (existing) return existing;
10277
+ const at = (options.now ?? Date.now)();
10278
+ const data = plain({ title: "Anuma", lastActivityAt: at, anuma: { lastReadSortKey: 0 } });
10279
+ try {
10280
+ const [created] = await store.putMany(
10281
+ [
10282
+ {
10283
+ expectedVersion: 0,
10284
+ record: {
10285
+ kind: "conversation",
10286
+ id: ANUMA_CONVERSATION_ID,
10287
+ conversationId: "",
10288
+ sortKey: conversationSortKey(data, at),
10289
+ createdAt: at,
10290
+ updatedAt: at,
10291
+ isDeleted: false,
10292
+ data
10293
+ }
10294
+ }
10295
+ ],
10296
+ options.signal
10297
+ );
10298
+ return created;
10299
+ } catch (error) {
10300
+ if (!conflict(error)) throw error;
10301
+ const winner = live(await store.get("conversation", ANUMA_CONVERSATION_ID, options.signal));
10302
+ if (!winner) throw error;
10303
+ return winner;
10304
+ }
10305
+ }
10306
+ async function markAnumaRead(store, sortKey, options = {}) {
10307
+ if (!Number.isSafeInteger(sortKey) || sortKey < 0)
10308
+ throw new ChatStoreError("Invalid Anuma read marker", 0, "invalid_record");
10309
+ const now = options.now ?? Date.now;
10310
+ for (let attempt = 1; ; attempt++) {
10311
+ const current = await ensureAnumaConversation(store, options);
10312
+ const read = anumaLastReadSortKey(current);
10313
+ if (sortKey <= read) return current;
10314
+ const record2 = current.record;
10315
+ const state = isObject(record2.data.anuma) ? record2.data.anuma : {};
10316
+ const data = plain({ ...record2.data, anuma: { ...state, lastReadSortKey: sortKey } });
10317
+ try {
10318
+ const [written] = await store.putMany(
10319
+ [
10320
+ {
10321
+ expectedVersion: current.version,
10322
+ record: {
10323
+ ...record2,
10324
+ data,
10325
+ sortKey: conversationSortKey(data, record2.createdAt),
10326
+ updatedAt: Math.max(record2.updatedAt, now())
10327
+ }
10328
+ }
10329
+ ],
10330
+ options.signal
10331
+ );
10332
+ return written;
10333
+ } catch (error) {
10334
+ if (!conflict(error) || attempt >= maxAttempts) throw error;
10335
+ }
10336
+ }
10337
+ }
10338
+ function countUnreadAnumaTurns(messages, lastReadSortKey) {
10339
+ const answered = Math.max(
10340
+ 0,
10341
+ ...messages.filter((m) => m.role === "user").map((m) => m.messageId)
10342
+ );
10343
+ const floor = Math.max(lastReadSortKey, answered);
10344
+ const turns = /* @__PURE__ */ new Set();
10345
+ for (const m of messages) {
10346
+ if (m.role !== "assistant" || m.messageId <= floor) continue;
10347
+ turns.add(m.anuma?.turnId ? `turn:${m.anuma.turnId}` : `message:${m.uniqueId}`);
10348
+ }
10349
+ return turns.size;
10350
+ }
10351
+
10352
+ // src/lib/db/chat/summaryOperations.ts
10353
+ import { Q as Q4 } from "@nozbe/watermelondb";
10354
+ function createSummaryContext(database, encryption = {}) {
10355
+ return {
10356
+ ...encryption,
10357
+ database,
10358
+ summariesCollection: database.get("conversation_summaries")
10359
+ };
10360
+ }
10361
+ function modelToStored(record2, summary) {
10362
+ return {
10363
+ uniqueId: record2.id,
10364
+ conversationId: record2.conversationId,
10365
+ summary,
10366
+ summarizedUpTo: record2.summarizedUpTo,
10367
+ tokenCount: record2.tokenCount,
10368
+ createdAt: record2.createdAt,
10369
+ updatedAt: record2.updatedAt
10370
+ };
10371
+ }
10372
+ async function getConversationSummaryOp(ctx, conversationId) {
10373
+ const results = await ctx.summariesCollection.query(Q4.where("conversation_id", conversationId)).fetch();
10374
+ if (results.length === 0) return null;
10375
+ const record2 = results[0];
10376
+ const stored2 = modelToStored(record2, record2.summary);
10377
+ if (ctx.walletAddress && isEncrypted(stored2.summary)) {
10378
+ if (ctx.signMessage) {
10379
+ try {
10380
+ await requestEncryptionKey(ctx.walletAddress, ctx.signMessage, ctx.embeddedWalletSigner);
10381
+ } catch (error) {
10382
+ getLogger().warn("Failed to request the encryption key for the summary:", error);
10383
+ }
10384
+ }
10385
+ stored2.summary = await decryptField(stored2.summary, ctx.walletAddress);
10386
+ } else if (ctx.walletAddress && ctx.signMessage && stored2.summary) {
10387
+ try {
10388
+ const encryptedSummary = await encryptField(
10389
+ stored2.summary,
10390
+ ctx.walletAddress,
10391
+ ctx.signMessage,
10392
+ ctx.embeddedWalletSigner
10393
+ );
10394
+ await ctx.database.write(async () => {
10395
+ const current = await ctx.summariesCollection.query(Q4.where("id", record2.id)).fetch();
10396
+ if (current[0]?.summary === stored2.summary) {
10397
+ await current[0].update((rec) => {
10398
+ rec.summary = encryptedSummary;
10399
+ });
10400
+ }
10401
+ });
10402
+ } catch (error) {
10403
+ getLogger().warn("Failed to encrypt the legacy summary:", error);
10404
+ }
10405
+ }
10406
+ return stored2;
10407
+ }
10408
+ async function upsertConversationSummaryOp(ctx, conversationId, summary, summarizedUpTo, tokenCount) {
10409
+ const revision2 = getConversationRevision(ctx.database, conversationId);
10410
+ let record2;
10411
+ const encryptedSummary = ctx.walletAddress ? await encryptField(summary, ctx.walletAddress, ctx.signMessage, ctx.embeddedWalletSigner) : summary;
10412
+ await ctx.database.write(async () => {
10413
+ assertConversationRevision(ctx.database, conversationId, revision2);
10414
+ const existing = await ctx.summariesCollection.query(Q4.where("conversation_id", conversationId)).fetch();
10415
+ if (existing.length > 0) {
10416
+ record2 = await existing[0].update((rec) => {
10417
+ rec.summary = encryptedSummary;
10418
+ rec.summarizedUpTo = summarizedUpTo;
10419
+ rec.tokenCount = tokenCount;
10420
+ });
10421
+ } else {
10422
+ record2 = await ctx.summariesCollection.create((rec) => {
10423
+ rec.conversationId = conversationId;
10424
+ rec.summary = encryptedSummary;
10425
+ rec.summarizedUpTo = summarizedUpTo;
10426
+ rec.tokenCount = tokenCount;
10427
+ });
10428
+ }
10429
+ });
10430
+ return modelToStored(record2, summary);
10431
+ }
10432
+ async function deleteConversationSummaryOp(ctx, conversationId) {
10433
+ await ctx.database.write(async () => {
10434
+ const existing = await ctx.summariesCollection.query(Q4.where("conversation_id", conversationId)).fetch();
10435
+ for (const record2 of existing) {
10436
+ await record2.destroyPermanently();
10437
+ }
10438
+ });
10439
+ }
10440
+
10441
+ // src/lib/db/conversationMemory/models.ts
10442
+ import { Model as Model3 } from "@nozbe/watermelondb";
10443
+ import { date as date2, field as field2, readonly, text as text4 } from "@nozbe/watermelondb/decorators";
10444
+ var ConversationMemory = class extends Model3 {
10445
+ };
10446
+ ConversationMemory.table = "conversation_memory";
10447
+ ConversationMemory.associations = {};
10448
+ __decorateClass([
10449
+ text4("conversation_id")
10450
+ ], ConversationMemory.prototype, "conversationId", 2);
10451
+ __decorateClass([
10452
+ text4("memory_id")
10453
+ ], ConversationMemory.prototype, "memoryId", 2);
10454
+ __decorateClass([
10455
+ field2("score")
10456
+ ], ConversationMemory.prototype, "score", 2);
10457
+ __decorateClass([
10458
+ readonly,
10459
+ date2("created_at")
10460
+ ], ConversationMemory.prototype, "createdAt", 2);
10461
+
10305
10462
  // src/lib/chatStore/chatImports.ts
10306
10463
  var importBatch = 100;
10307
10464
  var importChunks = 200;
@@ -10316,6 +10473,7 @@ function bytes2(value) {
10316
10473
  function conversationRecord(conversation, messages) {
10317
10474
  const createdAt = ms(conversation.createdAt);
10318
10475
  const data = plain({
10476
+ ...contextFields(conversation.context),
10319
10477
  title: conversation.title,
10320
10478
  projectId: conversation.projectId || void 0,
10321
10479
  pinnedAt: conversation.pinnedAt ? ms(conversation.pinnedAt) : void 0,
@@ -10881,17 +11039,17 @@ async function createRemoteMemoryPersistence(options) {
10881
11039
  expectedVersion = typeof expectedVersion === "number" ? expectedVersion : snapshot.version;
10882
11040
  if (!Number.isSafeInteger(expectedVersion) || expectedVersion < 0)
10883
11041
  throw new Error("expectedVersion must be a nonnegative integer");
10884
- const conflict = () => new RemoteMemoryError("The memory changed since it was read", 409, "version_conflict");
11042
+ const conflict2 = () => new RemoteMemoryError("The memory changed since it was read", 409, "version_conflict");
10885
11043
  let existing;
10886
11044
  if (expectedVersion !== 0 && !snapshot) {
10887
11045
  try {
10888
11046
  existing = memoryRecord(await request(path, {}, signal));
10889
11047
  } catch (error) {
10890
- if (error instanceof RemoteMemoryError && error.status === 404) throw conflict();
11048
+ if (error instanceof RemoteMemoryError && error.status === 404) throw conflict2();
10891
11049
  throw error;
10892
11050
  }
10893
11051
  }
10894
- if (existing && existing.version !== expectedVersion) throw conflict();
11052
+ if (existing && existing.version !== expectedVersion) throw conflict2();
10895
11053
  const decoded = existing ? await decode(existing).catch(() => void 0) : void 0;
10896
11054
  const reusableContent = snapshot?.encryptedContent ?? existing?.memory.content;
10897
11055
  const content = reusableContent && (snapshot?.content ?? decoded?.memory.content) === memory.content ? reusableContent : await encryptField2(memory.content);
@@ -11116,17 +11274,6 @@ async function createRemoteMemoryPersistence(options) {
11116
11274
  };
11117
11275
  }
11118
11276
 
11119
- // src/lib/chatStore/types.ts
11120
- var ChatStoreError = class extends Error {
11121
- constructor(message, status, code, retryAfterMs) {
11122
- super(message);
11123
- this.status = status;
11124
- this.code = code;
11125
- this.retryAfterMs = retryAfterMs;
11126
- this.name = "ChatStoreError";
11127
- }
11128
- };
11129
-
11130
11277
  // src/lib/chatStore/remote.ts
11131
11278
  var kinds = /* @__PURE__ */ new Set(["conversation", "message", "summary", "project", "memory_links", "setting"]);
11132
11279
  var cipherPattern = /^enc:v\d+:(?:[0-9a-f]{2}){28,}$/i;
@@ -11851,10 +11998,10 @@ async function deletedSinceUpload(store, journal, local, signal) {
11851
11998
  const present = /* @__PURE__ */ new Set([...local.records.map((r) => key(r.record)), ...local.skipped.keys()]);
11852
11999
  const gone = [...journal.keys].filter((k) => !present.has(k));
11853
12000
  if (gone.length === 0) return [];
11854
- const live = (await store.getMany(gone.map(ref2), signal)).filter((s) => !s.record.isDeleted);
11855
- const tombstoning = new Set(live.map((s) => key(s.record)));
12001
+ const live2 = (await store.getMany(gone.map(ref2), signal)).filter((s) => !s.record.isDeleted);
12002
+ const tombstoning = new Set(live2.map((s) => key(s.record)));
11856
12003
  await journal.drop(gone.filter((k) => !tombstoning.has(k)));
11857
- return live.map(({ record: record2, version }) => {
12004
+ return live2.map(({ record: record2, version }) => {
11858
12005
  const empty = emptied[record2.kind];
11859
12006
  return {
11860
12007
  record: {
@@ -12514,12 +12661,13 @@ var resumableFields = [
12514
12661
  "error",
12515
12662
  "thoughtProcess",
12516
12663
  "thinking",
12517
- "toolCallEvents"
12664
+ "toolCallEvents",
12665
+ "anuma"
12518
12666
  ];
12519
12667
  function resumedFields(options) {
12520
12668
  return plain(
12521
12669
  Object.fromEntries(
12522
- resumableFields.filter((f) => options[f] !== void 0).map((f) => [f, options[f]])
12670
+ resumableFields.filter((f) => options[f] !== void 0).map((f) => [f, f === "anuma" ? anumaTurnData(options.anuma) : options[f]])
12523
12671
  )
12524
12672
  );
12525
12673
  }
@@ -13173,7 +13321,7 @@ function createVaultChatBackend(options) {
13173
13321
  },
13174
13322
  async getConversations(query) {
13175
13323
  await available2();
13176
- return all("conversation").map(toConversation).filter(
13324
+ return all("conversation").filter((record2) => record2.id !== ANUMA_CONVERSATION_ID).map(toConversation).filter(
13177
13325
  (c) => query?.projectId === void 0 || (query.projectId === null ? !c.projectId : c.projectId === query.projectId)
13178
13326
  ).sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime());
13179
13327
  },
@@ -13209,7 +13357,14 @@ function createVaultChatBackend(options) {
13209
13357
  async updateConversationPinned(conversationId, pinned) {
13210
13358
  return updateConversation(conversationId, { pinnedAt: pinned ? now() : null });
13211
13359
  },
13360
+ async updateConversationContext(conversationId, context) {
13361
+ const set = contextFields(context);
13362
+ if (Object.keys(set).length > 0) return updateConversation(conversationId, set);
13363
+ const record2 = await conversation(conversationId);
13364
+ return !!record2 && !record2.isDeleted;
13365
+ },
13212
13366
  async deleteConversation(conversationId) {
13367
+ if (conversationId === ANUMA_CONVERSATION_ID) return false;
13213
13368
  const record2 = await conversation(conversationId);
13214
13369
  if (!record2 || record2.isDeleted) return false;
13215
13370
  for (const [k, patch] of pending)
@@ -16216,7 +16371,7 @@ async function countActiveVaultMemoriesOp(ctx) {
16216
16371
  return ctx.vaultMemoryCollection.query(...baseVaultConditions(ctx)).fetchCount();
16217
16372
  }
16218
16373
  async function getVaultMemoryCountsOp(ctx) {
16219
- const [live, archived, quarantined] = await Promise.all([
16374
+ const [live2, archived, quarantined] = await Promise.all([
16220
16375
  countActiveVaultMemoriesOp(ctx),
16221
16376
  ctx.vaultMemoryCollection.query(
16222
16377
  ...baseVaultConditions(ctx, { includeArchived: true }),
@@ -16227,7 +16382,7 @@ async function getVaultMemoryCountsOp(ctx) {
16227
16382
  Q6.where("trust_tier", "quarantined")
16228
16383
  ).fetchCount()
16229
16384
  ]);
16230
- return { live, archived, quarantined };
16385
+ return { live: live2, archived, quarantined };
16231
16386
  }
16232
16387
  async function getActiveVaultMemoryIdsOp(ctx, ids) {
16233
16388
  if (ids.length === 0) return /* @__PURE__ */ new Set();
@@ -17512,12 +17667,12 @@ var WalletPoller = class {
17512
17667
  * @param maxAttempts - Maximum polling attempts before giving up (default: 60)
17513
17668
  * @returns Stop function to cancel polling
17514
17669
  */
17515
- startPolling(checkWallet, onWalletReady, intervalMs = DEFAULT_INTERVAL_MS, maxAttempts = DEFAULT_MAX_ATTEMPTS) {
17670
+ startPolling(checkWallet, onWalletReady, intervalMs = DEFAULT_INTERVAL_MS, maxAttempts2 = DEFAULT_MAX_ATTEMPTS) {
17516
17671
  this.stop();
17517
17672
  this.attempts = 0;
17518
17673
  const poll = async () => {
17519
17674
  this.attempts++;
17520
- if (this.attempts > maxAttempts) {
17675
+ if (this.attempts > maxAttempts2) {
17521
17676
  this.stop();
17522
17677
  return;
17523
17678
  }
@@ -24878,13 +25033,13 @@ function createRemoteMemoryStore(options) {
24878
25033
  });
24879
25034
  return run;
24880
25035
  };
24881
- const live = async (id2) => {
25036
+ const live2 = async (id2) => {
24882
25037
  const record2 = await persistence.get(id2);
24883
25038
  return record2 && !record2.memory.is_deleted ? record2 : null;
24884
25039
  };
24885
25040
  const mutateLocked = async (id2, change, edit, writeOptions) => {
24886
25041
  writeOptions?.signal?.throwIfAborted();
24887
- const record2 = writeOptions?.signal ? await persistence.get(id2, writeOptions.signal) : await live(id2);
25042
+ const record2 = writeOptions?.signal ? await persistence.get(id2, writeOptions.signal) : await live2(id2);
24888
25043
  if (!record2) return null;
24889
25044
  if (record2.memory.is_deleted) return null;
24890
25045
  if (writeOptions?.canWrite && !await writeOptions.canWrite())
@@ -25682,7 +25837,7 @@ function createRemoteMemoryStore(options) {
25682
25837
  }, clear);
25683
25838
  return snapshot.then((topics) => structuredClone(topics));
25684
25839
  },
25685
- get: async (id2) => asStored(await live(id2)),
25840
+ get: async (id2) => asStored(await live2(id2)),
25686
25841
  listArchived: async () => (await enumerate({ archived_only: true, order: "archived", include_embeddings: true })).map(
25687
25842
  (record2) => stored(record2.memory)
25688
25843
  ),
@@ -25767,7 +25922,7 @@ function createRemoteMemoryStore(options) {
25767
25922
  return serialized(
25768
25923
  first,
25769
25924
  () => serialized(second, async () => {
25770
- const [old, next] = await Promise.all([live(id2), live(supersededById)]);
25925
+ const [old, next] = await Promise.all([live2(id2), live2(supersededById)]);
25771
25926
  if (!old || !next || old.memory.superseded_by || next.memory.superseded_by) return false;
25772
25927
  if (old.memory.kind !== void 0 && old.memory.kind !== null) return false;
25773
25928
  const saved = await persistence.putMany([
@@ -25788,7 +25943,7 @@ function createRemoteMemoryStore(options) {
25788
25943
  (memory) => memory.archived_at === void 0 || memory.archived_at === null ? { ...memory, archived_at: Date.now() } : null
25789
25944
  ),
25790
25945
  restore: (id2) => serialized(id2, async () => {
25791
- const record2 = await live(id2);
25946
+ const record2 = await live2(id2);
25792
25947
  if (!record2) return false;
25793
25948
  if (record2.memory.archived_at === void 0 || record2.memory.archived_at === null)
25794
25949
  return true;
@@ -32934,6 +33089,7 @@ function useChatStorage(options) {
32934
33089
  };
32935
33090
  }
32936
33091
  export {
33092
+ ANUMA_CONVERSATION_ID,
32937
33093
  CHUNKS_DISCARDED_ORIGIN,
32938
33094
  Conversation as ChatConversation,
32939
33095
  Message as ChatMessage,
@@ -32993,6 +33149,8 @@ export {
32993
33149
  WALLET_PAYMENT_TOOL_NAME,
32994
33150
  WalletPoller,
32995
33151
  addConversationMemoriesOp,
33152
+ anumaLastReadSortKey,
33153
+ anumaTurnContent,
32996
33154
  archiveVaultMemoryOp,
32997
33155
  assembleMemoryContext,
32998
33156
  attachFileContextToLastUserMessage,
@@ -33026,6 +33184,7 @@ export {
33026
33184
  consoleLogger,
33027
33185
  conversationMemoryToStored,
33028
33186
  cosineInt8,
33187
+ countUnreadAnumaTurns,
33029
33188
  createAutoExtractor,
33030
33189
  createChunkVectorCache,
33031
33190
  createCryptoPricePreProcessor,
@@ -33074,6 +33233,7 @@ export {
33074
33233
  embedMessage,
33075
33234
  encodeChunkVector,
33076
33235
  encryptToolCallEventsOp,
33236
+ ensureAnumaConversation,
33077
33237
  ensureDefaultFoldersOp,
33078
33238
  exchangeNotionCode,
33079
33239
  extractAndLinkEntitiesForMemoriesOp,
@@ -33114,6 +33274,7 @@ export {
33114
33274
  isRerankerAvailable,
33115
33275
  loadSettings,
33116
33276
  localChatBackend,
33277
+ markAnumaRead,
33117
33278
  maskScopedEmbeddingCache,
33118
33279
  maxVaultAttachmentBytes,
33119
33280
  memoryCipher,
@@ -33129,6 +33290,7 @@ export {
33129
33290
  quantizeEmbedding,
33130
33291
  queueManager,
33131
33292
  rankProfileCandidates,
33293
+ readAnumaTurn,
33132
33294
  recall,
33133
33295
  reencryptMemoryField,
33134
33296
  reflect,