@anuma/sdk 11.3.4 → 11.4.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.
@@ -6262,9 +6262,9 @@ function createNearbyRequest(options, apiPath = "/api/private-memories") {
6262
6262
  };
6263
6263
  const request = async (path, init = {}, signal) => {
6264
6264
  const controller = new AbortController();
6265
- let timedOut = false;
6265
+ let timedOut2 = false;
6266
6266
  const timer = setTimeout(() => {
6267
- timedOut = true;
6267
+ timedOut2 = true;
6268
6268
  controller.abort();
6269
6269
  }, timeoutMs);
6270
6270
  const forwardAbort = () => controller.abort(signal?.reason);
@@ -6330,7 +6330,7 @@ function createNearbyRequest(options, apiPath = "/api/private-memories") {
6330
6330
  if (response.status === 204) return null;
6331
6331
  return await response.json();
6332
6332
  } catch (error) {
6333
- if (timedOut) throw new RemoteMemoryError("Nearby request timed out", 408, "timeout");
6333
+ if (timedOut2) throw new RemoteMemoryError("Nearby request timed out", 408, "timeout");
6334
6334
  throw error;
6335
6335
  } finally {
6336
6336
  clearTimeout(timer);
@@ -12363,6 +12363,168 @@ function countUnreadAnumaTurns(messages, lastReadSortKey) {
12363
12363
  return turns.size;
12364
12364
  }
12365
12365
 
12366
+ // src/utils/paths.ts
12367
+ function normalizePath(p) {
12368
+ return p.replace(/^\/+/, "").replace(/\/\/+/g, "/").split("/").reduce((acc, seg) => {
12369
+ if (seg === "..") acc.pop();
12370
+ else if (seg !== ".") acc.push(seg);
12371
+ return acc;
12372
+ }, []).join("/");
12373
+ }
12374
+
12375
+ // src/lib/chatStore/appFiles.ts
12376
+ var maxSaveAttempts = 3;
12377
+ async function appFileId(store, conversationId, path) {
12378
+ if (!store.sealedId) throw new Error("App file ids need a chat store that seals ids");
12379
+ return store.sealedId(`app_file\0${conversationId}\0${normalizePath(path)}`);
12380
+ }
12381
+ async function putLatest(store, target, next, signal) {
12382
+ for (let attempt = 1; ; attempt++) {
12383
+ const current = await store.get(target.kind, target.id, signal);
12384
+ if (next.isDeleted && (!current || current.record.isDeleted)) return current;
12385
+ const now = Date.now();
12386
+ try {
12387
+ const [saved] = await store.putMany(
12388
+ [
12389
+ {
12390
+ record: {
12391
+ ...target,
12392
+ sortKey: 0,
12393
+ createdAt: current?.record.createdAt ?? now,
12394
+ updatedAt: Math.max(now, (current?.record.updatedAt ?? 0) + 1),
12395
+ isDeleted: next.isDeleted,
12396
+ data: next.isDeleted ? {} : next.data
12397
+ },
12398
+ expectedVersion: current?.version ?? 0
12399
+ }
12400
+ ],
12401
+ signal
12402
+ );
12403
+ return saved;
12404
+ } catch (error) {
12405
+ if (!(error instanceof ChatStoreError) || error.code !== "version_conflict" || attempt >= maxSaveAttempts)
12406
+ throw error;
12407
+ }
12408
+ }
12409
+ }
12410
+ async function listAll(store, kind, conversationId, signal) {
12411
+ const out = [];
12412
+ let cursor;
12413
+ do {
12414
+ const page = await store.list({ kind, conversationId, limit: 100, cursor, signal });
12415
+ out.push(...page.items.filter((item) => !item.record.isDeleted));
12416
+ cursor = page.nextCursor;
12417
+ } while (cursor);
12418
+ return out;
12419
+ }
12420
+ function toAppFile(snapshot) {
12421
+ const { path, content } = snapshot.record.data;
12422
+ if (typeof path !== "string" || typeof content !== "string") return null;
12423
+ return {
12424
+ conversationId: snapshot.record.conversationId,
12425
+ path,
12426
+ content,
12427
+ version: snapshot.version,
12428
+ createdAt: snapshot.record.createdAt,
12429
+ updatedAt: snapshot.record.updatedAt
12430
+ };
12431
+ }
12432
+ function toSavedTool(snapshot) {
12433
+ const d = snapshot.record.data;
12434
+ if (typeof d.name !== "string" || typeof d.displayName !== "string" || typeof d.description !== "string" || typeof d.parameters !== "string" || typeof d.html !== "string" || d.conversationId !== null && typeof d.conversationId !== "string")
12435
+ return null;
12436
+ return {
12437
+ id: snapshot.record.id,
12438
+ name: d.name,
12439
+ displayName: d.displayName,
12440
+ description: d.description,
12441
+ parameters: d.parameters,
12442
+ html: d.html,
12443
+ conversationId: d.conversationId,
12444
+ version: snapshot.version,
12445
+ createdAt: snapshot.record.createdAt,
12446
+ updatedAt: snapshot.record.updatedAt
12447
+ };
12448
+ }
12449
+ async function listAppFiles(store, conversationId, signal) {
12450
+ const items = await listAll(store, "app_file", conversationId, signal);
12451
+ return items.map(toAppFile).filter((file) => file !== null);
12452
+ }
12453
+ function createAppFileIndex(store) {
12454
+ const latest = /* @__PURE__ */ new Map();
12455
+ let cursor;
12456
+ let queue = Promise.resolve();
12457
+ const catchUp = async (signal) => {
12458
+ for (; ; ) {
12459
+ const page = await store.changes(cursor, { kinds: ["app_file"], limit: 200, signal });
12460
+ for (const item of page.items) latest.set(item.record.id, item);
12461
+ cursor = page.nextCursor ?? page.changesCursor;
12462
+ if (!page.nextCursor) break;
12463
+ }
12464
+ return [...latest.values()].filter((item) => !item.record.isDeleted).map(toAppFile).filter((file) => file !== null);
12465
+ };
12466
+ return {
12467
+ list(signal) {
12468
+ const next = queue.then(() => catchUp(signal));
12469
+ queue = next.catch(() => void 0);
12470
+ return next;
12471
+ }
12472
+ };
12473
+ }
12474
+ function listEveryAppFile(store, signal) {
12475
+ return createAppFileIndex(store).list(signal);
12476
+ }
12477
+ async function getAppFile(store, conversationId, path, signal) {
12478
+ const snapshot = await store.get(
12479
+ "app_file",
12480
+ await appFileId(store, conversationId, path),
12481
+ signal
12482
+ );
12483
+ return snapshot && !snapshot.record.isDeleted ? toAppFile(snapshot) : null;
12484
+ }
12485
+ async function saveAppFile(store, file, signal) {
12486
+ const saved = await putLatest(
12487
+ store,
12488
+ {
12489
+ kind: "app_file",
12490
+ id: await appFileId(store, file.conversationId, file.path),
12491
+ conversationId: file.conversationId
12492
+ },
12493
+ { data: { path: normalizePath(file.path), content: file.content }, isDeleted: false },
12494
+ signal
12495
+ );
12496
+ return toAppFile(saved);
12497
+ }
12498
+ async function deleteAppFile(store, conversationId, path, signal) {
12499
+ await putLatest(
12500
+ store,
12501
+ { kind: "app_file", id: await appFileId(store, conversationId, path), conversationId },
12502
+ { data: {}, isDeleted: true },
12503
+ signal
12504
+ );
12505
+ }
12506
+ async function loadSavedTools(store, signal) {
12507
+ const items = await listAll(store, "saved_tool", void 0, signal);
12508
+ return items.map(toSavedTool).filter((tool) => tool !== null);
12509
+ }
12510
+ async function saveSavedTool(store, id2, tool, signal) {
12511
+ const saved = await putLatest(
12512
+ store,
12513
+ { kind: "saved_tool", id: id2, conversationId: "" },
12514
+ { data: { ...tool }, isDeleted: false },
12515
+ signal
12516
+ );
12517
+ return toSavedTool(saved);
12518
+ }
12519
+ async function deleteSavedTool(store, id2, signal) {
12520
+ await putLatest(
12521
+ store,
12522
+ { kind: "saved_tool", id: id2, conversationId: "" },
12523
+ { data: {}, isDeleted: true },
12524
+ signal
12525
+ );
12526
+ }
12527
+
12366
12528
  // src/lib/db/chat/summaryOperations.ts
12367
12529
  import { Q as Q4 } from "@nozbe/watermelondb";
12368
12530
  function createSummaryContext(database, encryption = {}) {
@@ -12565,7 +12727,16 @@ function batches(records) {
12565
12727
  }
12566
12728
 
12567
12729
  // src/lib/chatStore/remote.ts
12568
- var kinds = /* @__PURE__ */ new Set(["conversation", "message", "summary", "project", "memory_links", "setting"]);
12730
+ var kinds = /* @__PURE__ */ new Set([
12731
+ "conversation",
12732
+ "message",
12733
+ "summary",
12734
+ "project",
12735
+ "memory_links",
12736
+ "setting",
12737
+ "app_file",
12738
+ "saved_tool"
12739
+ ]);
12569
12740
  var cipherPattern = /^enc:v\d+:(?:[0-9a-f]{2}){28,}$/i;
12570
12741
  var maxPayload = 2 * 1024 * 1024;
12571
12742
  var storedDims2 = 1536;
@@ -12625,7 +12796,7 @@ function validRecord(value) {
12625
12796
  return root ? value.conversationId === "" : id(value.conversationId);
12626
12797
  }
12627
12798
  function rootKind(kind) {
12628
- return kind === "conversation" || kind === "project" || kind === "setting";
12799
+ return kind === "conversation" || kind === "project" || kind === "setting" || kind === "saved_tool";
12629
12800
  }
12630
12801
  function embeddings(write) {
12631
12802
  if (write.embeddings === void 0) return true;
@@ -12706,6 +12877,7 @@ async function createRemoteChatStore(options) {
12706
12877
  const keyId = options.keyId;
12707
12878
  const encrypt = options.encrypt;
12708
12879
  const decrypt = options.decrypt;
12880
+ const hashTopic = options.hashTopic;
12709
12881
  const nearby = createNearbyRequest({ ...options }, "/api/private-chats");
12710
12882
  const request = async (...args) => {
12711
12883
  try {
@@ -12756,7 +12928,9 @@ async function createRemoteChatStore(options) {
12756
12928
  JSON.stringify({ schema: 1, account_id: accountId, key_id: keyId, record: record3 })
12757
12929
  );
12758
12930
  signal?.throwIfAborted();
12759
- if (!cipherPattern.test(payload) || payload.length > maxPayload) invalid();
12931
+ if (!cipherPattern.test(payload)) invalid();
12932
+ if (payload.length > maxPayload)
12933
+ throw new ChatStoreError("Encrypted chat record is too large", 413, "record_too_large");
12760
12934
  return payload;
12761
12935
  };
12762
12936
  const capture = (items, max, signal) => {
@@ -13037,6 +13211,7 @@ async function createRemoteChatStore(options) {
13037
13211
  if (!object(value) || !integer(value.deleted)) invalid();
13038
13212
  return value.deleted;
13039
13213
  },
13214
+ ...hashTopic ? { sealedId: (value) => hashTopic(`record-id\0${value}`) } : {},
13040
13215
  attachments: {
13041
13216
  async create(sizeBytes, signal) {
13042
13217
  if (!integer(sizeBytes, 1) || sizeBytes > maxAttachmentBytes) invalid();
@@ -13114,6 +13289,7 @@ var emptyMessage = {
13114
13289
  };
13115
13290
 
13116
13291
  // src/lib/chatStore/migrateChats.ts
13292
+ var timedOut = (error) => error instanceof ChatStoreError && error.status === 408;
13117
13293
  var maxPasses = 5;
13118
13294
  var verifyBatch = 1e3;
13119
13295
  var writeBatch = 25;
@@ -13382,19 +13558,107 @@ async function migrateChatsToRemote(options) {
13382
13558
  await journal.add(upload.map((r) => key(r.record)));
13383
13559
  let sent = 0;
13384
13560
  options.onProgress?.({ imported: sent, toImport: upload.length });
13385
- for (const batch of batches(upload)) {
13386
- await store.importRecords(batch, signal);
13561
+ const send = async (batch) => {
13562
+ try {
13563
+ await store.importRecords(batch, signal);
13564
+ } catch (error) {
13565
+ if (batch.length < 2 || !timedOut(error)) throw error;
13566
+ const half = Math.ceil(batch.length / 2);
13567
+ await send(batch.slice(0, half));
13568
+ await send(batch.slice(half));
13569
+ return;
13570
+ }
13387
13571
  sent += batch.length;
13388
13572
  options.onProgress?.({ imported: sent, toImport: upload.length });
13389
- }
13573
+ };
13574
+ for (const batch of batches(upload)) await send(batch);
13390
13575
  imported += sent;
13391
13576
  }
13392
13577
  };
13393
13578
  return passes(false);
13394
13579
  }
13395
13580
 
13581
+ // src/lib/chatStore/seedAppFiles.ts
13582
+ var maxSeedAttempts = 3;
13583
+ async function rawRows(database, table) {
13584
+ const rows = await database.get(table).query().fetch();
13585
+ return rows.map((row) => row._raw);
13586
+ }
13587
+ async function seedRecord(store, target, device, signal) {
13588
+ for (let attempt = 1; ; attempt++) {
13589
+ const current = await store.get(target.kind, target.id, signal);
13590
+ if (current && (current.record.isDeleted || current.record.updatedAt >= device.updatedAt))
13591
+ return false;
13592
+ try {
13593
+ await store.putMany(
13594
+ [
13595
+ {
13596
+ record: {
13597
+ ...target,
13598
+ sortKey: 0,
13599
+ createdAt: current?.record.createdAt ?? device.createdAt,
13600
+ updatedAt: device.updatedAt,
13601
+ isDeleted: false,
13602
+ data: device.data
13603
+ },
13604
+ expectedVersion: current?.version ?? 0
13605
+ }
13606
+ ],
13607
+ signal
13608
+ );
13609
+ return true;
13610
+ } catch (error) {
13611
+ if (!(error instanceof ChatStoreError) || error.code !== "version_conflict" || attempt >= maxSeedAttempts)
13612
+ throw error;
13613
+ }
13614
+ }
13615
+ }
13616
+ function leftOnDevice(error, result) {
13617
+ if (!(error instanceof ChatStoreError)) return false;
13618
+ if (error.code === "conversation_not_found") result.orphaned++;
13619
+ else if (error.code === "record_too_large") result.tooLarge++;
13620
+ else return false;
13621
+ return true;
13622
+ }
13623
+ async function seedAppFilesToVault(database, store, signal) {
13624
+ const result = { appFiles: 0, savedTools: 0, orphaned: 0, tooLarge: 0 };
13625
+ for (const file of await rawRows(database, "app_files")) {
13626
+ try {
13627
+ const target = {
13628
+ kind: "app_file",
13629
+ id: await appFileId(store, file.conversation_id, file.path),
13630
+ conversationId: file.conversation_id
13631
+ };
13632
+ const data = { path: normalizePath(file.path), content: file.content };
13633
+ const device = { data, createdAt: file.created_at, updatedAt: file.updated_at };
13634
+ if (await seedRecord(store, target, device, signal)) result.appFiles++;
13635
+ } catch (error) {
13636
+ if (!leftOnDevice(error, result)) throw error;
13637
+ }
13638
+ }
13639
+ for (const tool of await rawRows(database, "saved_tools")) {
13640
+ if (tool.is_deleted) continue;
13641
+ try {
13642
+ const data = {
13643
+ name: tool.name,
13644
+ displayName: tool.display_name,
13645
+ description: tool.description,
13646
+ parameters: tool.parameters,
13647
+ html: tool.html,
13648
+ conversationId: tool.conversation_id || null
13649
+ };
13650
+ const target = { kind: "saved_tool", id: tool.id, conversationId: "" };
13651
+ const device = { data, createdAt: tool.created_at, updatedAt: tool.updated_at };
13652
+ if (await seedRecord(store, target, device, signal)) result.savedTools++;
13653
+ } catch (error) {
13654
+ if (!leftOnDevice(error, result)) throw error;
13655
+ }
13656
+ }
13657
+ return result;
13658
+ }
13659
+
13396
13660
  // src/lib/chatStore/settings.ts
13397
- var maxSaveAttempts = 3;
13661
+ var maxSaveAttempts2 = 3;
13398
13662
  function toSetting(snapshot) {
13399
13663
  if (snapshot.record.isDeleted) return null;
13400
13664
  return {
@@ -13443,7 +13707,7 @@ async function saveSetting(store, section, data, signal) {
13443
13707
  );
13444
13708
  return toSetting(saved);
13445
13709
  } catch (error) {
13446
- if (!(error instanceof ChatStoreError) || error.code !== "version_conflict" || attempt >= maxSaveAttempts)
13710
+ if (!(error instanceof ChatStoreError) || error.code !== "version_conflict" || attempt >= maxSaveAttempts2)
13447
13711
  throw error;
13448
13712
  }
13449
13713
  }
@@ -14269,12 +14533,15 @@ function createVaultChatBackend(options) {
14269
14533
  emit({ type: "vector_quota" });
14270
14534
  return "retry";
14271
14535
  }
14272
- if (error.code === "private_chat_storage_limit_reached" || error.code === "invalid_record" || error.status === 400) {
14536
+ if (error.code === "private_chat_storage_limit_reached" || error.code === "invalid_record" || error.code === "record_too_large" || error.status === 400) {
14273
14537
  if (batch.length > 1) {
14274
14538
  isolate = batch.length;
14275
14539
  return "retry";
14276
14540
  }
14277
- drop(batch[0], error.status === 413 ? "storage_quota" : "invalid");
14541
+ drop(
14542
+ batch[0],
14543
+ error.code === "private_chat_storage_limit_reached" ? "storage_quota" : "invalid"
14544
+ );
14278
14545
  return "retry";
14279
14546
  }
14280
14547
  if (error.status === 429) return backoff(error.retryAfterMs, true);
@@ -15045,17 +15312,6 @@ __decorateClass([
15045
15312
 
15046
15313
  // src/lib/db/appFiles/operations.ts
15047
15314
  import { Q as Q5 } from "@nozbe/watermelondb";
15048
-
15049
- // src/utils/paths.ts
15050
- function normalizePath(p) {
15051
- return p.replace(/^\/+/, "").replace(/\/\/+/g, "/").split("/").reduce((acc, seg) => {
15052
- if (seg === "..") acc.pop();
15053
- else if (seg !== ".") acc.push(seg);
15054
- return acc;
15055
- }, []).join("/");
15056
- }
15057
-
15058
- // src/lib/db/appFiles/operations.ts
15059
15315
  function appFileToStored(file) {
15060
15316
  return {
15061
15317
  uniqueId: file.id,
@@ -27434,6 +27690,12 @@ async function importBatch2(request, keyId, rows, signal) {
27434
27690
  );
27435
27691
  return;
27436
27692
  } catch (error) {
27693
+ if (error instanceof RemoteMemoryError && error.status === 408 && rows.length > 1) {
27694
+ const half = Math.ceil(rows.length / 2);
27695
+ await importBatch2(request, keyId, rows.slice(0, half), signal);
27696
+ await importBatch2(request, keyId, rows.slice(half), signal);
27697
+ return;
27698
+ }
27437
27699
  if (!(error instanceof RemoteMemoryError) || error.status !== 429 || attempt >= MAX_RATE_LIMIT_WAITS)
27438
27700
  throw error;
27439
27701
  await wait(error.retryAfterMs ?? DEFAULT_RATE_LIMIT_WAIT_MS, signal);
@@ -32632,12 +32894,12 @@ async function preprocessFiles(files, options = {}) {
32632
32894
  }
32633
32895
  } catch (error) {
32634
32896
  errorCount++;
32635
- const timedOut = error instanceof ProcessingTimeoutError;
32897
+ const timedOut2 = error instanceof ProcessingTimeoutError;
32636
32898
  fileStatuses.push({
32637
32899
  fileId: file.id,
32638
32900
  fileName: file.name,
32639
32901
  status: "failed",
32640
- reason: timedOut ? "timeout" : "error"
32902
+ reason: timedOut2 ? "timeout" : "error"
32641
32903
  });
32642
32904
  logger.error(
32643
32905
  `[preprocessFiles] Error processing file ${label} with ${processor.name}:`,
@@ -42479,6 +42741,7 @@ export {
42479
42741
  addConversationMemoriesOp,
42480
42742
  anumaLastReadSortKey,
42481
42743
  anumaTurnContent,
42744
+ appFileId,
42482
42745
  appFileToStored,
42483
42746
  archiveVaultMemoryOp,
42484
42747
  assembleMemoryContext,
@@ -42519,6 +42782,7 @@ export {
42519
42782
  conversationMemoryToStored,
42520
42783
  cosineInt8,
42521
42784
  countUnreadAnumaTurns,
42785
+ createAppFileIndex,
42522
42786
  createAutoExtractor,
42523
42787
  createChunkVectorCache,
42524
42788
  createCryptoPricePreProcessor,
@@ -42566,6 +42830,7 @@ export {
42566
42830
  defaultServerToolsFilter,
42567
42831
  deleteAllAppFilesOp,
42568
42832
  deleteAllVaultMemoriesForUserOp,
42833
+ deleteAppFile,
42569
42834
  deleteAppFileOp,
42570
42835
  deleteEncryptedFile,
42571
42836
  deleteMediaByConversationOp,
@@ -42573,6 +42838,7 @@ export {
42573
42838
  deleteMediaOp,
42574
42839
  deleteMessageOp,
42575
42840
  deleteProjectOp,
42841
+ deleteSavedTool,
42576
42842
  deleteSavedToolOp,
42577
42843
  deleteVaultFolderOp,
42578
42844
  deleteVaultMemoryOp,
@@ -42619,6 +42885,7 @@ export {
42619
42885
  getAllVaultMemoryContentsOp,
42620
42886
  getAndClearNotionPendingMessage,
42621
42887
  getAndClearNotionReturnUrl,
42888
+ getAppFile,
42622
42889
  getAppFileMapOp,
42623
42890
  getAppFileOp,
42624
42891
  getAppFilesOp,
@@ -42697,6 +42964,9 @@ export {
42697
42964
  isSupportedMediaType,
42698
42965
  linkMemoryEntitiesOp,
42699
42966
  listActiveStreams,
42967
+ listAppFiles,
42968
+ listEveryAppFile,
42969
+ loadSavedTools,
42700
42970
  loadSettings,
42701
42971
  localChatBackend,
42702
42972
  markAnumaRead,
@@ -42748,6 +43018,8 @@ export {
42748
43018
  resumeStream,
42749
43019
  retain,
42750
43020
  revokeNotionAccess,
43021
+ saveAppFile,
43022
+ saveSavedTool,
42751
43023
  saveSetting,
42752
43024
  savedToolToStored,
42753
43025
  scoreProfileSalience,
@@ -42760,6 +43032,7 @@ export {
42760
43032
  searchMediaOp,
42761
43033
  searchMessagesOp,
42762
43034
  searchVaultMemories,
43035
+ seedAppFilesToVault,
42763
43036
  seedEncryptionKeys,
42764
43037
  selectServerToolsForPrompt,
42765
43038
  serializeJsx,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@anuma/sdk",
3
- "version": "11.3.4",
3
+ "version": "11.4.0",
4
4
  "description": "",
5
5
  "main": "./dist/index.cjs",
6
6
  "module": "./dist/index.mjs",