@anuma/sdk 10.2.6 → 10.3.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.
@@ -11858,6 +11858,25 @@ async function migrateChatsToRemote(options) {
11858
11858
  // src/lib/chatStore/vaultBackend.ts
11859
11859
  import { v7 as uuidv73 } from "uuid";
11860
11860
 
11861
+ // src/lib/chatStore/vaultCache.ts
11862
+ async function saveChatCache(storage, encrypt, snapshot) {
11863
+ await storage.set(snapshot.account, await encrypt(JSON.stringify({ schema: 1, ...snapshot })));
11864
+ }
11865
+ async function loadChatCache(storage, account, decrypt) {
11866
+ const sealed2 = await storage.get(account);
11867
+ if (!sealed2) return void 0;
11868
+ const parsed = JSON.parse(await decrypt(sealed2));
11869
+ if (parsed.schema !== 1 || parsed.account !== account || typeof parsed.cursor !== "string" || !Array.isArray(parsed.records) || !Array.isArray(parsed.threads) || !Array.isArray(parsed.absent))
11870
+ return void 0;
11871
+ return {
11872
+ account,
11873
+ cursor: parsed.cursor,
11874
+ records: parsed.records,
11875
+ threads: parsed.threads,
11876
+ absent: parsed.absent
11877
+ };
11878
+ }
11879
+
11861
11880
  // src/lib/chatStore/vaultOutbox.ts
11862
11881
  function patchKey(patch) {
11863
11882
  return `${patch.kind}/${patch.id}`;
@@ -11962,6 +11981,10 @@ async function loadOutbox(storage, slot, decrypt) {
11962
11981
 
11963
11982
  // src/lib/chatStore/vaultBackend.ts
11964
11983
  var terminalCodes = /* @__PURE__ */ new Set(["key_mismatch", "account_changed", "account_not_found"]);
11984
+ var cacheSaveDelayMs = 500;
11985
+ function cursorRejected(error) {
11986
+ return error instanceof ChatStoreError && (error.status === 400 || error.status === 410);
11987
+ }
11965
11988
  function key2(kind, id2) {
11966
11989
  return `${kind}/${id2}`;
11967
11990
  }
@@ -12006,7 +12029,7 @@ function defaultActivity() {
12006
12029
  };
12007
12030
  }
12008
12031
  function createVaultChatBackend(options) {
12009
- const { store } = options;
12032
+ const { cache } = options;
12010
12033
  const now = options.now ?? Date.now;
12011
12034
  const threadLimit = options.threadCacheSize ?? 30;
12012
12035
  const pollIntervalMs = options.pollIntervalMs ?? 6e4;
@@ -12033,6 +12056,13 @@ function createVaultChatBackend(options) {
12033
12056
  let persisting = Promise.resolve();
12034
12057
  let readyPromise = null;
12035
12058
  let adopting = null;
12059
+ let connected = typeof options.store === "function" ? void 0 : options.store;
12060
+ const complete = /* @__PURE__ */ new Set();
12061
+ let fromCache = false;
12062
+ let caching = false;
12063
+ let cacheOff = false;
12064
+ let cacheTimer;
12065
+ let saving = Promise.resolve();
12036
12066
  const slot = uuidv73();
12037
12067
  const locks = options.outbox?.locks ?? globalThis.navigator?.locks;
12038
12068
  let releaseSlot;
@@ -12069,13 +12099,55 @@ function createVaultChatBackend(options) {
12069
12099
  }
12070
12100
  return out;
12071
12101
  };
12102
+ const cacheSnapshot = () => {
12103
+ const kept = [...threads.keys()].filter((id2) => complete.has(id2) && confirmedLive(id2));
12104
+ const keep = new Set(kept);
12105
+ return {
12106
+ account: cache.account,
12107
+ cursor: cursor ?? "",
12108
+ records: [...confirmed.values()].filter(
12109
+ ({ record: record2 }) => record2.kind === "conversation" || record2.kind !== "project" && keep.has(record2.conversationId)
12110
+ ),
12111
+ threads: kept,
12112
+ absent: [...checked].filter((k) => {
12113
+ const [kind, ...rest] = k.split("/");
12114
+ return (kind === "summary" || kind === "memory_links") && !confirmed.has(k) && keep.has(rest.join("/"));
12115
+ })
12116
+ };
12117
+ };
12118
+ const saveCache = () => {
12119
+ if (!cache || !caching || cacheOff || closed || cacheTimer || cursor === void 0) return;
12120
+ cacheTimer = setTimeout(() => {
12121
+ cacheTimer = void 0;
12122
+ saving = saving.then(
12123
+ () => cacheOff || closed ? void 0 : saveChatCache(cache.storage, cache.seal.encrypt, cacheSnapshot())
12124
+ ).catch(() => void 0);
12125
+ }, cacheSaveDelayMs);
12126
+ };
12127
+ const clearCache = async () => {
12128
+ fromCache = false;
12129
+ cacheOff = true;
12130
+ clearTimeout(cacheTimer);
12131
+ cacheTimer = void 0;
12132
+ await saving;
12133
+ if (cache) await cache.storage.set(cache.account, null);
12134
+ };
12135
+ const wipeIfTerminal = (error) => {
12136
+ if (error instanceof ChatStoreError && terminalCodes.has(error.code ?? ""))
12137
+ void clearCache().catch(() => void 0);
12138
+ };
12072
12139
  const merge = (snapshot) => {
12073
12140
  const k = key2(snapshot.record.kind, snapshot.record.id);
12074
12141
  const known = confirmed.get(k);
12075
12142
  if (known && known.version >= snapshot.version) return false;
12076
12143
  confirmed.set(k, snapshot);
12144
+ saveCache();
12077
12145
  return true;
12078
12146
  };
12147
+ const remote = async () => {
12148
+ await ready();
12149
+ return connected;
12150
+ };
12079
12151
  const persist = () => {
12080
12152
  const storage = options.outbox;
12081
12153
  if (!storage) return Promise.resolve();
@@ -12130,7 +12202,7 @@ function createVaultChatBackend(options) {
12130
12202
  return { kind, id: rest.join("/") };
12131
12203
  });
12132
12204
  if (refs.length === 0) return;
12133
- const found = await store.getMany(refs);
12205
+ const found = await (await remote()).getMany(refs);
12134
12206
  const seen = new Set(found.map((s) => key2(s.record.kind, s.record.id)));
12135
12207
  for (const snapshot of found) merge(snapshot);
12136
12208
  for (const ref3 of refs) {
@@ -12138,6 +12210,7 @@ function createVaultChatBackend(options) {
12138
12210
  if (!seen.has(k)) confirmed.delete(k);
12139
12211
  checked.add(k);
12140
12212
  }
12213
+ saveCache();
12141
12214
  };
12142
12215
  const drop = (patch, reason) => {
12143
12216
  inflight.delete(patchKey(patch));
@@ -12192,7 +12265,7 @@ function createVaultChatBackend(options) {
12192
12265
  sent.push(patch);
12193
12266
  }
12194
12267
  if (writes.length === 0) return;
12195
- const results = await store.putMany(writes);
12268
+ const results = await (await remote()).putMany(writes);
12196
12269
  results.forEach((snapshot, i) => {
12197
12270
  merge(snapshot);
12198
12271
  checked.add(patchKey(sent[i]));
@@ -12226,6 +12299,7 @@ function createVaultChatBackend(options) {
12226
12299
  }
12227
12300
  if (terminalCodes.has(error.code ?? "")) {
12228
12301
  stopped = error.code;
12302
+ wipeIfTerminal(error);
12229
12303
  reportOutbox();
12230
12304
  return "stop";
12231
12305
  }
@@ -12292,6 +12366,7 @@ function createVaultChatBackend(options) {
12292
12366
  const forget = (k) => {
12293
12367
  confirmed.delete(k);
12294
12368
  checked.delete(k);
12369
+ saveCache();
12295
12370
  };
12296
12371
  const forgetConversation = (conversationId) => {
12297
12372
  threads.delete(conversationId);
@@ -12318,9 +12393,10 @@ function createVaultChatBackend(options) {
12318
12393
  }
12319
12394
  const loading = (async () => {
12320
12395
  if (!confirmedLive(conversationId)) return;
12396
+ const source = await remote();
12321
12397
  let page;
12322
12398
  do {
12323
- const result = await store.list({
12399
+ const result = await source.list({
12324
12400
  kind: "message",
12325
12401
  conversationId,
12326
12402
  limit: 200,
@@ -12330,7 +12406,15 @@ function createVaultChatBackend(options) {
12330
12406
  page = result.nextCursor;
12331
12407
  } while (page);
12332
12408
  })();
12333
- loading.catch(() => threads.delete(conversationId));
12409
+ void loading.then(
12410
+ () => {
12411
+ if (threads.get(conversationId) !== loading) return;
12412
+ complete.add(conversationId);
12413
+ saveCache();
12414
+ },
12415
+ () => threads.delete(conversationId)
12416
+ );
12417
+ complete.delete(conversationId);
12334
12418
  threads.set(conversationId, loading);
12335
12419
  evict();
12336
12420
  return loading;
@@ -12348,20 +12432,58 @@ function createVaultChatBackend(options) {
12348
12432
  const relevant2 = threads.has(record2.conversationId) || confirmed.has(k) || checked.has(k) && record2.kind !== "message";
12349
12433
  if (relevant2 && merge(snapshot)) announce([record2]);
12350
12434
  };
12435
+ const listIndex = async (source) => {
12436
+ const index = [];
12437
+ let page;
12438
+ do {
12439
+ const result = await source.list({
12440
+ kind: "conversation",
12441
+ limit: 200,
12442
+ ...page ? { cursor: page } : {}
12443
+ });
12444
+ index.push(...result.items);
12445
+ page = result.nextCursor;
12446
+ } while (page);
12447
+ return index;
12448
+ };
12449
+ const catchUp = async (source) => {
12450
+ let next = cursor ?? "";
12451
+ for (; ; ) {
12452
+ const page = await source.changes(next || void 0, { limit: 200 });
12453
+ page.items.forEach(applyRemote);
12454
+ next = page.nextCursor ?? page.changesCursor;
12455
+ if (!page.nextCursor) break;
12456
+ }
12457
+ cursor = next;
12458
+ };
12459
+ const refetch = async (source) => {
12460
+ const account = await source.account();
12461
+ const index = await listIndex(source);
12462
+ const reopened = [...threads.keys()];
12463
+ confirmed.clear();
12464
+ checked.clear();
12465
+ threads.clear();
12466
+ complete.clear();
12467
+ cursor = account.changesCursor ?? "";
12468
+ index.forEach(merge);
12469
+ emit({ type: "conversations", origin: "remote" });
12470
+ for (const conversationId of reopened)
12471
+ emit({ type: "messages", conversationId, origin: "remote" });
12472
+ };
12351
12473
  const poll = () => {
12352
12474
  polling ?? (polling = (async () => {
12353
- await ready();
12354
- let next = cursor ?? "";
12355
- for (; ; ) {
12356
- const page = await store.changes(next || void 0, { limit: 200 });
12357
- page.items.forEach(applyRemote);
12358
- next = page.nextCursor ?? page.changesCursor;
12359
- if (!page.nextCursor) break;
12360
- }
12361
- cursor = next;
12475
+ const source = await remote();
12476
+ try {
12477
+ await catchUp(source);
12478
+ } catch (error) {
12479
+ if (!cursorRejected(error)) throw error;
12480
+ await refetch(source);
12481
+ }
12362
12482
  pollFailures = 0;
12363
- })().catch(() => {
12483
+ saveCache();
12484
+ })().catch((error) => {
12364
12485
  pollFailures++;
12486
+ wipeIfTerminal(error);
12365
12487
  }).finally(() => {
12366
12488
  polling = null;
12367
12489
  schedulePoll();
@@ -12377,26 +12499,27 @@ function createVaultChatBackend(options) {
12377
12499
  };
12378
12500
  const ready = () => {
12379
12501
  readyPromise ?? (readyPromise = (async () => {
12380
- const account = await store.account();
12381
- cursor = account.changesCursor ?? "";
12382
- let page;
12383
- do {
12384
- const result = await store.list({
12385
- kind: "conversation",
12386
- limit: 200,
12387
- ...page ? { cursor: page } : {}
12388
- });
12389
- result.items.forEach(merge);
12390
- page = result.nextCursor;
12391
- } while (page);
12502
+ await seeding;
12503
+ if (!connected && typeof options.store === "function") connected = await options.store();
12504
+ const source = connected;
12505
+ const account = await source.account();
12506
+ if (!fromCache) {
12507
+ cursor = account.changesCursor ?? "";
12508
+ (await listIndex(source)).forEach(merge);
12509
+ }
12392
12510
  await adopt();
12393
12511
  schedulePoll();
12394
12512
  })());
12395
- readyPromise.catch(() => {
12513
+ readyPromise.catch((error) => {
12396
12514
  readyPromise = null;
12515
+ wipeIfTerminal(error);
12397
12516
  });
12398
12517
  return readyPromise;
12399
12518
  };
12519
+ const available2 = async () => {
12520
+ await seeding;
12521
+ if (!fromCache) await ready();
12522
+ };
12400
12523
  const adoptSlot = async (outbox, other) => {
12401
12524
  const patches = await loadOutbox(outbox.storage, other, outbox.seal.decrypt);
12402
12525
  for (const patch of patches) {
@@ -12441,20 +12564,20 @@ function createVaultChatBackend(options) {
12441
12564
  });
12442
12565
  if (channel) channel.onmessage = () => void poll();
12443
12566
  const conversation = async (conversationId) => {
12444
- await ready();
12567
+ await available2();
12445
12568
  const k = key2("conversation", conversationId);
12446
12569
  if (!view(k) && !checked.has(k)) {
12447
- const found = await store.get("conversation", conversationId);
12570
+ const found = await (await remote()).get("conversation", conversationId);
12448
12571
  checked.add(k);
12449
12572
  if (found) merge(found);
12450
12573
  }
12451
12574
  return view(k);
12452
12575
  };
12453
12576
  const message = async (uniqueId) => {
12454
- await ready();
12577
+ await available2();
12455
12578
  const k = key2("message", uniqueId);
12456
12579
  if (!view(k) && !checked.has(k)) {
12457
- const found = await store.get("message", uniqueId);
12580
+ const found = await (await remote()).get("message", uniqueId);
12458
12581
  checked.add(k);
12459
12582
  if (found) merge(found);
12460
12583
  }
@@ -12462,17 +12585,18 @@ function createVaultChatBackend(options) {
12462
12585
  return record2 && !record2.isDeleted && conversationLive(record2.conversationId) ? record2 : void 0;
12463
12586
  };
12464
12587
  const child = async (kind, conversationId) => {
12465
- await ready();
12588
+ await available2();
12466
12589
  const k = key2(kind, conversationId);
12467
12590
  if (!view(k) && !checked.has(k) && conversationLive(conversationId)) {
12468
- const found = await store.get(kind, conversationId);
12591
+ const found = await (await remote()).get(kind, conversationId);
12469
12592
  checked.add(k);
12470
12593
  if (found) merge(found);
12594
+ saveCache();
12471
12595
  }
12472
12596
  return view(k);
12473
12597
  };
12474
12598
  const thread = async (conversationId) => {
12475
- await ready();
12599
+ await available2();
12476
12600
  if (!conversationLive(conversationId)) return [];
12477
12601
  await loadThread(conversationId);
12478
12602
  return all("message", conversationId).sort(
@@ -12536,7 +12660,7 @@ function createVaultChatBackend(options) {
12536
12660
  return record2 && !record2.isDeleted ? toConversation(record2) : null;
12537
12661
  },
12538
12662
  async getConversations(query) {
12539
- await ready();
12663
+ await available2();
12540
12664
  return all("conversation").map(toConversation).filter(
12541
12665
  (c) => query?.projectId === void 0 || (query.projectId === null ? !c.projectId : c.projectId === query.projectId)
12542
12666
  ).sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime());
@@ -12606,7 +12730,7 @@ function createVaultChatBackend(options) {
12606
12730
  return toSkeletons(await thread(conversationId));
12607
12731
  },
12608
12732
  async getMessagesByIds(uniqueIds) {
12609
- await ready();
12733
+ await available2();
12610
12734
  const ids = [...new Set(uniqueIds)];
12611
12735
  const unknown = ids.map((id2) => key2("message", id2)).filter((k) => !view(k) && !checked.has(k));
12612
12736
  await refresh(unknown);
@@ -12738,7 +12862,7 @@ function createVaultChatBackend(options) {
12738
12862
  );
12739
12863
  },
12740
12864
  async getAllFiles(query) {
12741
- await ready();
12865
+ await available2();
12742
12866
  if (query?.conversationId) await loadThread(query.conversationId);
12743
12867
  const files = [];
12744
12868
  for (const record2 of all("message", query?.conversationId)) {
@@ -12763,7 +12887,7 @@ function createVaultChatBackend(options) {
12763
12887
  const vector2 = acceptedVector(queryVector);
12764
12888
  if (!vector2) return [];
12765
12889
  await flush();
12766
- const hits = await store.search(vector2, {
12890
+ const hits = await (await remote()).search(vector2, {
12767
12891
  limit: Math.min(50, limit),
12768
12892
  kinds: ["message"],
12769
12893
  ...conversationId ? { conversationId } : {},
@@ -12870,8 +12994,9 @@ function createVaultChatBackend(options) {
12870
12994
  let messageCount = 0;
12871
12995
  try {
12872
12996
  for (const batch of batches(conversations))
12873
- conversationCount += await store.importRecords(batch);
12874
- for (const batch of batches(messages)) messageCount += await store.importRecords(batch);
12997
+ conversationCount += await connected.importRecords(batch);
12998
+ for (const batch of batches(messages))
12999
+ messageCount += await connected.importRecords(batch);
12875
13000
  } finally {
12876
13001
  for (const { record: record2 } of messages) checked.delete(key2("message", record2.id));
12877
13002
  await refresh(conversations.map(({ record: record2 }) => key2("conversation", record2.id))).catch(
@@ -12886,8 +13011,10 @@ function createVaultChatBackend(options) {
12886
13011
  return { conversations: conversationCount, messages: messageCount };
12887
13012
  },
12888
13013
  status: () => ({ pending: pending.size + inflight.size, ...stopped ? { stopped } : {} }),
13014
+ clearCache,
12889
13015
  close() {
12890
13016
  closed = true;
13017
+ clearTimeout(cacheTimer);
12891
13018
  clearTimeout(flushTimer);
12892
13019
  clearTimeout(retryTimer);
12893
13020
  clearTimeout(pollTimer);
@@ -12904,7 +13031,31 @@ function createVaultChatBackend(options) {
12904
13031
  emit({ type: "conversations", origin: "local" });
12905
13032
  return true;
12906
13033
  };
13034
+ const seed = async () => {
13035
+ if (!cache) return;
13036
+ try {
13037
+ const snapshot = await loadChatCache(cache.storage, cache.account, cache.seal.decrypt).catch(
13038
+ () => void 0
13039
+ );
13040
+ if (!snapshot || cacheOff) return;
13041
+ snapshot.records.forEach(merge);
13042
+ for (const conversationId of snapshot.threads) {
13043
+ if (!confirmedLive(conversationId)) continue;
13044
+ threads.set(conversationId, Promise.resolve());
13045
+ complete.add(conversationId);
13046
+ }
13047
+ evict();
13048
+ for (const k of snapshot.absent) checked.add(k);
13049
+ cursor = snapshot.cursor;
13050
+ fromCache = true;
13051
+ await adopt().catch(() => void 0);
13052
+ } finally {
13053
+ caching = true;
13054
+ }
13055
+ };
13056
+ const seeding = seed();
12907
13057
  void ready().catch(() => void 0);
13058
+ if (cache) void seeding.then(() => fromCache ? poll() : void 0);
12908
13059
  return backend;
12909
13060
  }
12910
13061
  function vaultChatBackend(backend) {