@anuma/sdk 10.1.0 → 10.2.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.
@@ -10348,17 +10348,18 @@ function createWebSearchPreProcessor(options = {}) {
10348
10348
 
10349
10349
  // src/lib/db/chat/summaryOperations.ts
10350
10350
  import { Q as Q3 } from "@nozbe/watermelondb";
10351
- function createSummaryContext(database) {
10351
+ function createSummaryContext(database, encryption = {}) {
10352
10352
  return {
10353
+ ...encryption,
10353
10354
  database,
10354
10355
  summariesCollection: database.get("conversation_summaries")
10355
10356
  };
10356
10357
  }
10357
- function modelToStored(record2) {
10358
+ function modelToStored(record2, summary) {
10358
10359
  return {
10359
10360
  uniqueId: record2.id,
10360
10361
  conversationId: record2.conversationId,
10361
- summary: record2.summary,
10362
+ summary,
10362
10363
  summarizedUpTo: record2.summarizedUpTo,
10363
10364
  tokenCount: record2.tokenCount,
10364
10365
  createdAt: record2.createdAt,
@@ -10368,28 +10369,62 @@ function modelToStored(record2) {
10368
10369
  async function getConversationSummaryOp(ctx, conversationId) {
10369
10370
  const results = await ctx.summariesCollection.query(Q3.where("conversation_id", conversationId)).fetch();
10370
10371
  if (results.length === 0) return null;
10371
- return modelToStored(results[0]);
10372
+ const record2 = results[0];
10373
+ const stored2 = modelToStored(record2, record2.summary);
10374
+ if (ctx.walletAddress && isEncrypted(stored2.summary)) {
10375
+ if (ctx.signMessage) {
10376
+ try {
10377
+ await requestEncryptionKey(ctx.walletAddress, ctx.signMessage, ctx.embeddedWalletSigner);
10378
+ } catch (error) {
10379
+ getLogger().warn("Failed to request the encryption key for the summary:", error);
10380
+ }
10381
+ }
10382
+ stored2.summary = await decryptField(stored2.summary, ctx.walletAddress);
10383
+ } else if (ctx.walletAddress && ctx.signMessage && stored2.summary) {
10384
+ try {
10385
+ const encryptedSummary = await encryptField(
10386
+ stored2.summary,
10387
+ ctx.walletAddress,
10388
+ ctx.signMessage,
10389
+ ctx.embeddedWalletSigner
10390
+ );
10391
+ await ctx.database.write(async () => {
10392
+ const current = await ctx.summariesCollection.query(Q3.where("id", record2.id)).fetch();
10393
+ if (current[0]?.summary === stored2.summary) {
10394
+ await current[0].update((rec) => {
10395
+ rec.summary = encryptedSummary;
10396
+ });
10397
+ }
10398
+ });
10399
+ } catch (error) {
10400
+ getLogger().warn("Failed to encrypt the legacy summary:", error);
10401
+ }
10402
+ }
10403
+ return stored2;
10372
10404
  }
10373
10405
  async function upsertConversationSummaryOp(ctx, conversationId, summary, summarizedUpTo, tokenCount) {
10406
+ const revision2 = getConversationRevision(ctx.database, conversationId);
10374
10407
  let record2;
10408
+ const encryptedSummary = ctx.walletAddress ? await encryptField(summary, ctx.walletAddress, ctx.signMessage, ctx.embeddedWalletSigner) : summary;
10375
10409
  await ctx.database.write(async () => {
10410
+ assertConversationRevision(ctx.database, conversationId, revision2);
10376
10411
  const existing = await ctx.summariesCollection.query(Q3.where("conversation_id", conversationId)).fetch();
10377
10412
  if (existing.length > 0) {
10378
10413
  record2 = await existing[0].update((rec) => {
10379
- rec.summary = summary;
10414
+ rec.summary = encryptedSummary;
10380
10415
  rec.summarizedUpTo = summarizedUpTo;
10381
10416
  rec.tokenCount = tokenCount;
10382
10417
  });
10383
10418
  } else {
10384
10419
  record2 = await ctx.summariesCollection.create((rec) => {
10385
10420
  rec.conversationId = conversationId;
10386
- rec.summary = summary;
10421
+ rec.summary = encryptedSummary;
10387
10422
  rec.summarizedUpTo = summarizedUpTo;
10388
10423
  rec.tokenCount = tokenCount;
10389
10424
  });
10390
10425
  }
10391
10426
  });
10392
- return modelToStored(record2);
10427
+ return modelToStored(record2, summary);
10393
10428
  }
10394
10429
  async function deleteConversationSummaryOp(ctx, conversationId) {
10395
10430
  await ctx.database.write(async () => {
@@ -10511,95 +10546,364 @@ async function clearConversationMemoriesOp(ctx, conversationId) {
10511
10546
  });
10512
10547
  }
10513
10548
 
10514
- // src/lib/db/entities/types.ts
10515
- var ENTITY_KINDS = [
10516
- "person",
10517
- "organization",
10518
- "place",
10519
- "event",
10520
- "product",
10521
- "thing",
10522
- "concept",
10523
- "other"
10524
- ];
10525
- function normalizeEntityName(name) {
10526
- return name.trim().toLowerCase();
10549
+ // src/lib/chatStore/vaultRecords.ts
10550
+ var PINNED_SORT_BASE = 2 ** 51;
10551
+ var recordBudgetBytes = 104e4;
10552
+ var storedDims = 1536;
10553
+ var acceptedDims = /* @__PURE__ */ new Set([1536, 4096]);
10554
+ var maxChunks = 16;
10555
+ var droppable = ["thoughtProcess", "thinking", "sources", "toolCallEvents"];
10556
+ var messageMetadata = /* @__PURE__ */ new Set(["conversationId", "uniqueId", "vector", "chunks"]);
10557
+ var alwaysStored = /* @__PURE__ */ new Set(["role", "content", "responseDuration"]);
10558
+ function plain(value) {
10559
+ return JSON.parse(JSON.stringify(value ?? {}));
10527
10560
  }
10528
- var TOPIC_SALIENCE_WEIGHTS = { primary: 1, secondary: 0.6, mention: 0.3 };
10529
- function topicWeight(topic2) {
10530
- return topic2.source === "user" ? 1 : topic2.weight ?? 1;
10561
+ function text4(value) {
10562
+ return typeof value === "string" ? value : void 0;
10531
10563
  }
10532
- function primaryTopic(topics) {
10533
- let best = null;
10534
- for (const topic2 of topics ?? []) {
10535
- if (best === null || topicWeight(topic2) > topicWeight(best)) best = topic2;
10564
+ function number(value) {
10565
+ return typeof value === "number" ? value : void 0;
10566
+ }
10567
+ function conversationSortKey(data, createdAt) {
10568
+ const pinnedAt = number(data.pinnedAt);
10569
+ if (pinnedAt) return PINNED_SORT_BASE + pinnedAt;
10570
+ return number(data.lastActivityAt) ?? createdAt;
10571
+ }
10572
+ function toConversation(record2) {
10573
+ const pinnedAt = number(record2.data.pinnedAt);
10574
+ const projectId = text4(record2.data.projectId);
10575
+ return {
10576
+ uniqueId: record2.id,
10577
+ conversationId: record2.id,
10578
+ title: text4(record2.data.title) ?? "",
10579
+ ...projectId ? { projectId } : {},
10580
+ createdAt: new Date(record2.createdAt),
10581
+ updatedAt: new Date(record2.updatedAt),
10582
+ isDeleted: record2.isDeleted,
10583
+ pinnedAt: pinnedAt ? new Date(pinnedAt) : null
10584
+ };
10585
+ }
10586
+ function messageData(options) {
10587
+ return plain(
10588
+ Object.fromEntries(
10589
+ Object.entries(options).filter(
10590
+ ([key3, value]) => !messageMetadata.has(key3) && (!!value || alwaysStored.has(key3))
10591
+ )
10592
+ )
10593
+ );
10594
+ }
10595
+ function toMessage(record2) {
10596
+ const { chunkOffsets: _offsets, truncated: _truncated, ...fields } = record2.data;
10597
+ return {
10598
+ ...fields,
10599
+ uniqueId: record2.id,
10600
+ messageId: record2.sortKey,
10601
+ conversationId: record2.conversationId,
10602
+ createdAt: new Date(record2.createdAt),
10603
+ updatedAt: new Date(record2.updatedAt)
10604
+ };
10605
+ }
10606
+ function chunkOffsets(record2) {
10607
+ const offsets = record2.data.chunkOffsets;
10608
+ return Array.isArray(offsets) ? offsets : void 0;
10609
+ }
10610
+ function toSkeletons(records) {
10611
+ const roleById = new Map(records.map((r) => [r.id, text4(r.data.role)]));
10612
+ return records.map((r) => {
10613
+ const parentMessageId = text4(r.data.parentMessageId);
10614
+ const model = text4(r.data.model);
10615
+ const artifact = r.data.role === "user" && !!parentMessageId && roleById.get(parentMessageId) === "user";
10616
+ return {
10617
+ uniqueId: r.id,
10618
+ messageId: r.sortKey,
10619
+ conversationId: r.conversationId,
10620
+ role: text4(r.data.role) ?? "",
10621
+ createdAt: new Date(r.createdAt),
10622
+ ...parentMessageId ? { parentMessageId } : {},
10623
+ ...model ? { model } : {},
10624
+ ...artifact ? { content: text4(r.data.content) ?? "" } : {}
10625
+ };
10626
+ });
10627
+ }
10628
+ function toSummary(record2) {
10629
+ return {
10630
+ uniqueId: record2.id,
10631
+ conversationId: record2.id,
10632
+ summary: text4(record2.data.summary) ?? "",
10633
+ summarizedUpTo: text4(record2.data.summarizedUpTo) ?? "",
10634
+ tokenCount: number(record2.data.tokenCount) ?? 0,
10635
+ createdAt: new Date(record2.createdAt),
10636
+ updatedAt: new Date(record2.updatedAt)
10637
+ };
10638
+ }
10639
+ function links(data) {
10640
+ return Array.isArray(data.links) ? data.links : [];
10641
+ }
10642
+ function applyLinkOps(data, ops) {
10643
+ let current = links(data);
10644
+ for (const op of ops) {
10645
+ if ("clear" in op) {
10646
+ current = [];
10647
+ continue;
10648
+ }
10649
+ const byId = new Map(current.map((l) => [l.memoryId, l]));
10650
+ const turn = /* @__PURE__ */ new Set();
10651
+ op.add.forEach((item, i) => {
10652
+ if (!item.memoryId || turn.has(item.memoryId)) return;
10653
+ turn.add(item.memoryId);
10654
+ const known = byId.get(item.memoryId);
10655
+ if (known && known.createdAt >= op.at + i) return;
10656
+ byId.set(item.memoryId, { memoryId: item.memoryId, score: item.score, createdAt: op.at + i });
10657
+ });
10658
+ current = [...byId.values()].sort((a, b) => a.createdAt - b.createdAt).slice(-MAX_PER_CONVERSATION);
10536
10659
  }
10537
- return best;
10660
+ return plain({ links: current });
10538
10661
  }
10539
- function parseTopicWeight(value) {
10540
- return typeof value === "number" && value > 0 && value <= 1 ? value : void 0;
10662
+ function toLinks(record2) {
10663
+ return links(record2.data).map((l) => ({
10664
+ uniqueId: `${record2.id}:${l.memoryId}`,
10665
+ conversationId: record2.id,
10666
+ memoryId: l.memoryId,
10667
+ score: l.score,
10668
+ createdAt: new Date(l.createdAt)
10669
+ }));
10541
10670
  }
10542
- function serializeTopics(topics) {
10543
- return JSON.stringify(topics);
10671
+ function bytes(value) {
10672
+ return new TextEncoder().encode(JSON.stringify(value)).length;
10544
10673
  }
10545
- function parseTopics(value) {
10546
- if (typeof value !== "string" || value.length === 0) return null;
10547
- let parsed;
10548
- try {
10549
- parsed = JSON.parse(value);
10550
- } catch {
10551
- return null;
10674
+ function fitRecord(record2) {
10675
+ if (record2.kind !== "message" || bytes(record2) <= recordBudgetBytes) return record2;
10676
+ const data = { ...record2.data };
10677
+ const truncated = [];
10678
+ for (const field10 of droppable) {
10679
+ if (bytes({ ...record2, data }) <= recordBudgetBytes) break;
10680
+ if (data[field10] === void 0) continue;
10681
+ delete data[field10];
10682
+ truncated.push(field10);
10552
10683
  }
10553
- if (!Array.isArray(parsed)) return null;
10684
+ const content = text4(data.content) ?? "";
10685
+ const over = bytes({ ...record2, data }) - recordBudgetBytes;
10686
+ if (over > 0) {
10687
+ const encoded = new TextEncoder().encode(content);
10688
+ data.content = new TextDecoder().decode(encoded.slice(0, Math.max(0, encoded.length - over - 64))).replace(/�$/, "");
10689
+ truncated.push("content");
10690
+ }
10691
+ return { ...record2, data: { ...data, truncated } };
10692
+ }
10693
+ function acceptedVector(vector2) {
10694
+ if (!vector2 || !acceptedDims.has(vector2.length)) return void 0;
10695
+ if (!vector2.every(Number.isFinite) || !vector2.slice(0, storedDims).some((x) => x !== 0))
10696
+ return void 0;
10697
+ return [...vector2];
10698
+ }
10699
+ function normalized(vector2) {
10700
+ const norm = Math.hypot(...vector2) || 1;
10701
+ return vector2.map((x) => x / norm);
10702
+ }
10703
+ function fitChunks(chunks3) {
10704
+ const usable = chunks3.map((c) => ({ ...c, vector: acceptedVector(c.vector) }));
10705
+ if (usable.length === 0 || usable.some((c) => !c.vector)) return void 0;
10706
+ if (usable.length <= maxChunks) return usable;
10554
10707
  const out = [];
10555
- for (const raw of parsed) {
10556
- if (typeof raw !== "object" || raw === null) continue;
10557
- const { name, kind, source, weight } = raw;
10558
- if (typeof name !== "string" || name.trim().length === 0) continue;
10559
- const parsedWeight = parseTopicWeight(weight);
10708
+ for (let g = 0; g < maxChunks; g++) {
10709
+ const group = usable.slice(
10710
+ Math.floor(g * usable.length / maxChunks),
10711
+ Math.floor((g + 1) * usable.length / maxChunks)
10712
+ );
10713
+ const dims = group[0].vector.length;
10714
+ const mean = new Array(dims).fill(0);
10715
+ for (const c of group) {
10716
+ const unit = normalized(c.vector);
10717
+ for (let d = 0; d < dims; d++) mean[d] += unit[d];
10718
+ }
10560
10719
  out.push({
10561
- name,
10562
- ...typeof kind === "string" && kind.length > 0 ? { kind } : {},
10563
- source: source === "user" ? "user" : "auto",
10564
- ...parsedWeight !== void 0 ? { weight: parsedWeight } : {}
10720
+ vector: normalized(mean),
10721
+ startOffset: group[0].startOffset,
10722
+ endOffset: group[group.length - 1].endOffset
10565
10723
  });
10566
10724
  }
10567
10725
  return out;
10568
10726
  }
10569
10727
 
10570
- // src/lib/memory/store/remoteTopics.ts
10571
- function topic(value) {
10572
- const t = value;
10573
- if (!t || typeof t.name !== "string" || !t.name || !Number.isSafeInteger(t.version) || t.version < 1 || typeof t.is_deleted !== "boolean" || typeof t.server_updated_at !== "string" || t.kind !== void 0 && typeof t.kind !== "string")
10574
- throw new Error("Invalid nearby topic");
10575
- return t;
10728
+ // src/lib/chatStore/chatImports.ts
10729
+ var importBatch = 100;
10730
+ var importChunks = 200;
10731
+ var importPlaintextBytes = 39e5;
10732
+ function ms(date10, fallback = 1) {
10733
+ const value = date10?.getTime();
10734
+ return value && Number.isFinite(value) ? Math.max(1, Math.floor(value)) : fallback;
10576
10735
  }
10577
- function createRemoteTopicPersistence(request) {
10736
+ function bytes2(value) {
10737
+ return new TextEncoder().encode(JSON.stringify(value)).length;
10738
+ }
10739
+ function conversationRecord(conversation, messages) {
10740
+ const createdAt = ms(conversation.createdAt);
10741
+ const data = plain({
10742
+ title: conversation.title,
10743
+ projectId: conversation.projectId || void 0,
10744
+ pinnedAt: conversation.pinnedAt ? ms(conversation.pinnedAt) : void 0,
10745
+ lastActivityAt: Math.max(
10746
+ ms(conversation.updatedAt, createdAt),
10747
+ ...messages.map((m) => ms(m.createdAt))
10748
+ )
10749
+ });
10578
10750
  return {
10579
- topics: async () => {
10580
- const out = [];
10581
- let cursor;
10582
- do {
10583
- const page = await request(
10584
- "/topics?limit=200" + (cursor ? `&cursor=${encodeURIComponent(cursor)}` : ""),
10585
- {}
10586
- );
10587
- if (!Array.isArray(page.items) || page.next_cursor !== void 0 && typeof page.next_cursor !== "string")
10588
- throw new Error("Invalid nearby topic page");
10589
- out.push(...page.items.map(topic));
10590
- cursor = page.next_cursor;
10591
- } while (cursor);
10592
- return out;
10593
- },
10594
- putTopic: async (value, expectedVersion) => {
10595
- if (!Number.isSafeInteger(expectedVersion) || expectedVersion < 0)
10596
- throw new Error("Expected topic version must be a nonnegative integer");
10597
- return topic(
10598
- await request("/topics", {
10599
- method: "PUT",
10600
- body: JSON.stringify({ topic: value, expected_version: expectedVersion })
10601
- })
10602
- );
10751
+ record: {
10752
+ kind: "conversation",
10753
+ id: conversation.conversationId,
10754
+ conversationId: "",
10755
+ sortKey: conversationSortKey(data, createdAt),
10756
+ createdAt,
10757
+ updatedAt: ms(conversation.updatedAt, createdAt),
10758
+ isDeleted: false,
10759
+ data
10760
+ }
10761
+ };
10762
+ }
10763
+ function messageRecord(message) {
10764
+ const {
10765
+ uniqueId: _uniqueId,
10766
+ messageId: _messageId,
10767
+ createdAt: _createdAt,
10768
+ updatedAt: _updatedAt,
10769
+ decryptionStatus: _status,
10770
+ chunks: chunks3,
10771
+ vector: vector2,
10772
+ ...fields
10773
+ } = message;
10774
+ const fitted = chunks3?.length ? fitChunks(chunks3) : void 0;
10775
+ const single = fitted ? void 0 : acceptedVector(vector2);
10776
+ const embeddings2 = fitted?.map((c) => c.vector) ?? (single ? [single] : void 0);
10777
+ const record2 = fitRecord({
10778
+ kind: "message",
10779
+ id: message.uniqueId,
10780
+ conversationId: message.conversationId,
10781
+ sortKey: message.messageId,
10782
+ createdAt: ms(message.createdAt),
10783
+ updatedAt: ms(message.updatedAt, ms(message.createdAt)),
10784
+ isDeleted: false,
10785
+ data: {
10786
+ ...messageData(fields),
10787
+ ...fitted ? { chunkOffsets: fitted.map((c) => [c.startOffset, c.endOffset]) } : {}
10788
+ }
10789
+ });
10790
+ return {
10791
+ record: record2,
10792
+ ...embeddings2 ? { embeddings: embeddings2 } : {},
10793
+ ...embeddings2 && message.embeddingModel ? { embeddingModel: message.embeddingModel } : {}
10794
+ };
10795
+ }
10796
+ function batches(records) {
10797
+ const out = [];
10798
+ let batch = [];
10799
+ let chunks3 = 0;
10800
+ let size = 0;
10801
+ for (const item of records) {
10802
+ const vectors = item.embeddings?.length ?? 0;
10803
+ const itemSize = bytes2(item.record);
10804
+ if (batch.length > 0 && (batch.length >= importBatch || chunks3 + vectors > importChunks || size + itemSize > importPlaintextBytes)) {
10805
+ out.push(batch);
10806
+ batch = [];
10807
+ chunks3 = 0;
10808
+ size = 0;
10809
+ }
10810
+ batch.push(item);
10811
+ chunks3 += vectors;
10812
+ size += itemSize;
10813
+ }
10814
+ if (batch.length) out.push(batch);
10815
+ return out;
10816
+ }
10817
+
10818
+ // src/lib/db/entities/types.ts
10819
+ var ENTITY_KINDS = [
10820
+ "person",
10821
+ "organization",
10822
+ "place",
10823
+ "event",
10824
+ "product",
10825
+ "thing",
10826
+ "concept",
10827
+ "other"
10828
+ ];
10829
+ function normalizeEntityName(name) {
10830
+ return name.trim().toLowerCase();
10831
+ }
10832
+ var TOPIC_SALIENCE_WEIGHTS = { primary: 1, secondary: 0.6, mention: 0.3 };
10833
+ function topicWeight(topic2) {
10834
+ return topic2.source === "user" ? 1 : topic2.weight ?? 1;
10835
+ }
10836
+ function primaryTopic(topics) {
10837
+ let best = null;
10838
+ for (const topic2 of topics ?? []) {
10839
+ if (best === null || topicWeight(topic2) > topicWeight(best)) best = topic2;
10840
+ }
10841
+ return best;
10842
+ }
10843
+ function parseTopicWeight(value) {
10844
+ return typeof value === "number" && value > 0 && value <= 1 ? value : void 0;
10845
+ }
10846
+ function serializeTopics(topics) {
10847
+ return JSON.stringify(topics);
10848
+ }
10849
+ function parseTopics(value) {
10850
+ if (typeof value !== "string" || value.length === 0) return null;
10851
+ let parsed;
10852
+ try {
10853
+ parsed = JSON.parse(value);
10854
+ } catch {
10855
+ return null;
10856
+ }
10857
+ if (!Array.isArray(parsed)) return null;
10858
+ const out = [];
10859
+ for (const raw of parsed) {
10860
+ if (typeof raw !== "object" || raw === null) continue;
10861
+ const { name, kind, source, weight } = raw;
10862
+ if (typeof name !== "string" || name.trim().length === 0) continue;
10863
+ const parsedWeight = parseTopicWeight(weight);
10864
+ out.push({
10865
+ name,
10866
+ ...typeof kind === "string" && kind.length > 0 ? { kind } : {},
10867
+ source: source === "user" ? "user" : "auto",
10868
+ ...parsedWeight !== void 0 ? { weight: parsedWeight } : {}
10869
+ });
10870
+ }
10871
+ return out;
10872
+ }
10873
+
10874
+ // src/lib/memory/store/remoteTopics.ts
10875
+ function topic(value) {
10876
+ const t = value;
10877
+ if (!t || typeof t.name !== "string" || !t.name || !Number.isSafeInteger(t.version) || t.version < 1 || typeof t.is_deleted !== "boolean" || typeof t.server_updated_at !== "string" || t.kind !== void 0 && typeof t.kind !== "string")
10878
+ throw new Error("Invalid nearby topic");
10879
+ return t;
10880
+ }
10881
+ function createRemoteTopicPersistence(request) {
10882
+ return {
10883
+ topics: async () => {
10884
+ const out = [];
10885
+ let cursor;
10886
+ do {
10887
+ const page = await request(
10888
+ "/topics?limit=200" + (cursor ? `&cursor=${encodeURIComponent(cursor)}` : ""),
10889
+ {}
10890
+ );
10891
+ if (!Array.isArray(page.items) || page.next_cursor !== void 0 && typeof page.next_cursor !== "string")
10892
+ throw new Error("Invalid nearby topic page");
10893
+ out.push(...page.items.map(topic));
10894
+ cursor = page.next_cursor;
10895
+ } while (cursor);
10896
+ return out;
10897
+ },
10898
+ putTopic: async (value, expectedVersion) => {
10899
+ if (!Number.isSafeInteger(expectedVersion) || expectedVersion < 0)
10900
+ throw new Error("Expected topic version must be a nonnegative integer");
10901
+ return topic(
10902
+ await request("/topics", {
10903
+ method: "PUT",
10904
+ body: JSON.stringify({ topic: value, expected_version: expectedVersion })
10905
+ })
10906
+ );
10603
10907
  },
10604
10908
  importTopics: async (topics, signal) => {
10605
10909
  for (let i = 0; i < topics.length; i += 50) {
@@ -11249,7 +11553,7 @@ var ChatStoreError = class extends Error {
11249
11553
  var kinds = /* @__PURE__ */ new Set(["conversation", "message", "summary", "project", "memory_links"]);
11250
11554
  var cipherPattern = /^enc:v\d+:(?:[0-9a-f]{2}){28,}$/i;
11251
11555
  var maxPayload = 2 * 1024 * 1024;
11252
- var storedDims = 1536;
11556
+ var storedDims2 = 1536;
11253
11557
  var maxRequestCiphertext = 8 * 1024 * 1024;
11254
11558
  var maxGetRefs = 50;
11255
11559
  var maxImportRecords = 100;
@@ -11317,24 +11621,24 @@ function vector(v) {
11317
11621
  for (let i = 0; i < v.length; i++) {
11318
11622
  const x = v[i];
11319
11623
  if (typeof x !== "number" || !Number.isFinite(x)) return false;
11320
- if (i < storedDims && x !== 0) nonZero = true;
11624
+ if (i < storedDims2 && x !== 0) nonZero = true;
11321
11625
  }
11322
11626
  return nonZero;
11323
11627
  }
11324
11628
  function vectorFields(write) {
11325
11629
  return {
11326
- ...write.embeddings !== void 0 ? { embeddings: write.embeddings.map((v) => v.slice(0, storedDims)) } : {},
11630
+ ...write.embeddings !== void 0 ? { embeddings: write.embeddings.map((v) => v.slice(0, storedDims2)) } : {},
11327
11631
  ...write.embeddingModel !== void 0 ? { embedding_model: write.embeddingModel } : {}
11328
11632
  };
11329
11633
  }
11330
- function text4(value) {
11634
+ function text5(value) {
11331
11635
  return typeof value === "string";
11332
11636
  }
11333
11637
  function timestamp(value) {
11334
11638
  return typeof value === "string" && Number.isFinite(Date.parse(value));
11335
11639
  }
11336
11640
  function presigned(value) {
11337
- if (!object(value) || !text4(value.url) || !/^https:\/\//.test(value.url) || !text4(value.method) || !object(value.headers) || !Object.values(value.headers).every(text4) || !timestamp(value.expires_at))
11641
+ if (!object(value) || !text5(value.url) || !/^https:\/\//.test(value.url) || !text5(value.method) || !object(value.headers) || !Object.values(value.headers).every(text5) || !timestamp(value.expires_at))
11338
11642
  invalid();
11339
11643
  return {
11340
11644
  url: value.url,
@@ -11344,7 +11648,7 @@ function presigned(value) {
11344
11648
  };
11345
11649
  }
11346
11650
  function attachment(value) {
11347
- if (!object(value) || !text4(value.attachment_id) || !attachmentIdPattern.test(value.attachment_id) || !integer(value.size_bytes, 1) || value.state !== "pending" && value.state !== "committed" || value.owner_kind !== void 0 && value.owner_kind !== "message" && value.owner_kind !== "summary" || value.owner_id !== void 0 && !id(value.owner_id) || !timestamp(value.created_at) || value.committed_at !== void 0 && !timestamp(value.committed_at))
11651
+ if (!object(value) || !text5(value.attachment_id) || !attachmentIdPattern.test(value.attachment_id) || !integer(value.size_bytes, 1) || value.state !== "pending" && value.state !== "committed" || value.owner_kind !== void 0 && value.owner_kind !== "message" && value.owner_kind !== "summary" || value.owner_id !== void 0 && !id(value.owner_id) || !timestamp(value.created_at) || value.committed_at !== void 0 && !timestamp(value.committed_at))
11348
11652
  invalid();
11349
11653
  return {
11350
11654
  id: value.attachment_id,
@@ -11357,7 +11661,7 @@ function attachment(value) {
11357
11661
  };
11358
11662
  }
11359
11663
  function ref(value) {
11360
- if (!object(value) || !text4(value.kind) || !kinds.has(value.kind) || !id(value.id)) invalid();
11664
+ if (!object(value) || !text5(value.kind) || !kinds.has(value.kind) || !id(value.id)) invalid();
11361
11665
  return { kind: value.kind, id: value.id };
11362
11666
  }
11363
11667
  function sameMetadata(a, b) {
@@ -11580,7 +11884,7 @@ async function createRemoteChatStore(options) {
11580
11884
  },
11581
11885
  async account(signal) {
11582
11886
  const value = await request("/account", {}, signal);
11583
- if (!object(value) || value.account_id !== accountId || !text4(value.key_id) || value.state !== "none" && value.state !== "migrating" && value.state !== "active" || !integer(value.record_count) || !integer(value.storage_bytes) || !integer(value.vector_count) || value.changes_cursor !== void 0 && (!text4(value.changes_cursor) || value.changes_cursor.length > 2048))
11887
+ if (!object(value) || value.account_id !== accountId || !text5(value.key_id) || value.state !== "none" && value.state !== "migrating" && value.state !== "active" || !integer(value.record_count) || !integer(value.storage_bytes) || !integer(value.vector_count) || value.changes_cursor !== void 0 && (!text5(value.changes_cursor) || value.changes_cursor.length > 2048))
11584
11888
  invalid();
11585
11889
  if (value.key_id && value.key_id !== keyId)
11586
11890
  throw new ChatStoreError("Canonical chat key does not match", 409, "key_mismatch");
@@ -11625,363 +11929,177 @@ async function createRemoteChatStore(options) {
11625
11929
  return {
11626
11930
  kind: checked.kind,
11627
11931
  id: checked.id,
11628
- updated_at: r.updatedAt,
11629
- is_deleted: r.isDeleted
11630
- };
11631
- });
11632
- if (wireRevisions.length === 0) return [];
11633
- const value = await request(
11634
- "/verify",
11635
- {
11636
- method: "POST",
11637
- body: JSON.stringify({ account_id: accountId, revisions: wireRevisions })
11638
- },
11639
- signal
11640
- );
11641
- if (!object(value) || !Array.isArray(value.missing)) invalid();
11642
- const missing2 = value.missing.map(ref);
11643
- if (missing2.some((r) => !asked.has(`${r.kind}/${r.id}`))) invalid();
11644
- return missing2;
11645
- },
11646
- async activate(signal) {
11647
- const value = await request(
11648
- "/account",
11649
- { method: "PUT", body: JSON.stringify({ account_id: accountId, state: "active" }) },
11650
- signal
11651
- );
11652
- if (!object(value) || value.state !== "active" || typeof value.transitioned !== "boolean")
11653
- invalid();
11654
- return value.transitioned;
11655
- },
11656
- async changes(cursor, options2) {
11657
- const limit = options2?.limit ?? 100;
11658
- if (!integer(limit, 1) || limit > 200 || cursor !== void 0 && (!text4(cursor) || cursor.length > 2048))
11659
- invalid();
11660
- const params = new URLSearchParams({ account_id: String(accountId), limit: String(limit) });
11661
- if (cursor) params.set("cursor", cursor);
11662
- const value = await request(`/changes?${params}`, {}, options2?.signal);
11663
- if (!object(value) || !Array.isArray(value.items) || value.items.length > limit || value.next_cursor !== void 0 && !text4(value.next_cursor) || value.changes_cursor !== void 0 && !text4(value.changes_cursor))
11664
- invalid();
11665
- const items = [];
11666
- for (const item of value.items) items.push(await decode(item));
11667
- options2?.signal?.throwIfAborted();
11668
- return {
11669
- items,
11670
- ...value.next_cursor ? { nextCursor: value.next_cursor } : {},
11671
- changesCursor: value.changes_cursor ?? cursor ?? ""
11672
- };
11673
- },
11674
- async search(embedding2, options2) {
11675
- const limit = options2?.limit ?? 20;
11676
- const kindsFilter = options2?.kinds;
11677
- if (!Array.isArray(embedding2) || !embeddings({
11678
- record: { ...emptyMessage, isDeleted: false },
11679
- embeddings: [embedding2]
11680
- }) || !integer(limit, 1) || limit > 50 || kindsFilter !== void 0 && (!Array.isArray(kindsFilter) || kindsFilter.length === 0 || kindsFilter.some((k) => k !== "message" && k !== "summary")) || options2?.conversationId !== void 0 && !id(options2.conversationId) || options2?.excludeConversationId !== void 0 && !id(options2.excludeConversationId) || options2?.embeddingModel !== void 0 && (!text4(options2.embeddingModel) || options2.embeddingModel.length > 256))
11681
- invalid();
11682
- const value = await request(
11683
- "/search",
11684
- {
11685
- method: "POST",
11686
- body: JSON.stringify({
11687
- account_id: accountId,
11688
- embedding: embedding2.slice(0, storedDims),
11689
- limit,
11690
- ...kindsFilter ? { kinds: kindsFilter } : {},
11691
- ...options2?.conversationId ? { conversation_id: options2.conversationId } : {},
11692
- ...options2?.excludeConversationId ? { exclude_conversation_id: options2.excludeConversationId } : {},
11693
- ...options2?.embeddingModel ? { embedding_model: options2.embeddingModel } : {}
11694
- })
11695
- },
11696
- options2?.signal
11697
- );
11698
- if (!object(value) || !Array.isArray(value.items) || value.items.length > limit) invalid();
11699
- const hits = [];
11700
- for (const item of value.items) {
11701
- if (!object(item) || !integer(item.chunk) || typeof item.score !== "number" || !Number.isFinite(item.score))
11702
- invalid();
11703
- const snapshot = await decode(item);
11704
- if (snapshot.record.isDeleted || kindsFilter !== void 0 && !kindsFilter.includes(snapshot.record.kind) || snapshot.record.kind !== "message" && snapshot.record.kind !== "summary" || options2?.conversationId !== void 0 && snapshot.record.conversationId !== options2.conversationId || snapshot.record.conversationId === options2?.excludeConversationId)
11705
- invalid();
11706
- hits.push({ ...snapshot, chunk: item.chunk, score: item.score });
11707
- }
11708
- options2?.signal?.throwIfAborted();
11709
- return hits;
11710
- },
11711
- async deleteAll(signal) {
11712
- const value = await request(`/records?account_id=${accountId}`, { method: "DELETE" }, signal);
11713
- if (!object(value) || !integer(value.deleted)) invalid();
11714
- return value.deleted;
11715
- },
11716
- attachments: {
11717
- async create(sizeBytes, signal) {
11718
- if (!integer(sizeBytes, 1) || sizeBytes > maxAttachmentBytes) invalid();
11719
- const value = await request(
11720
- "/attachments",
11721
- {
11722
- method: "POST",
11723
- body: JSON.stringify({ account_id: accountId, size_bytes: sizeBytes })
11724
- },
11725
- signal
11726
- );
11727
- if (!object(value)) invalid();
11728
- const created = attachment(value.attachment);
11729
- if (created.sizeBytes !== sizeBytes || created.state !== "pending") invalid();
11730
- return { attachment: created, upload: presigned(value.upload) };
11731
- },
11732
- async commit(attachmentId, owner, signal) {
11733
- if (!attachmentIdPattern.test(attachmentId) || !object(owner) || owner.kind !== "message" && owner.kind !== "summary" || !id(owner.id))
11734
- invalid();
11735
- const value = await request(
11736
- `/attachments/${attachmentId}/commit`,
11737
- {
11738
- method: "POST",
11739
- body: JSON.stringify({
11740
- account_id: accountId,
11741
- owner_kind: owner.kind,
11742
- owner_id: owner.id
11743
- })
11744
- },
11745
- signal
11746
- );
11747
- if (!object(value)) invalid();
11748
- const committed = attachment(value.attachment);
11749
- if (committed.id !== attachmentId || committed.state !== "committed" || committed.ownerKind !== owner.kind || committed.ownerId !== owner.id)
11750
- invalid();
11751
- return committed;
11752
- },
11753
- async get(attachmentId, signal) {
11754
- if (!attachmentIdPattern.test(attachmentId)) invalid();
11755
- let value;
11756
- try {
11757
- value = await request(`/attachments/${attachmentId}?account_id=${accountId}`, {}, signal);
11758
- } catch (error) {
11759
- if (error instanceof ChatStoreError && error.status === 404) return null;
11760
- throw error;
11761
- }
11762
- if (!object(value)) invalid();
11763
- const found = attachment(value.attachment);
11764
- if (found.id !== attachmentId) invalid();
11765
- return {
11766
- attachment: found,
11767
- ...value.download !== void 0 ? { download: presigned(value.download) } : {}
11768
- };
11769
- },
11770
- async delete(attachmentId, signal) {
11771
- if (!attachmentIdPattern.test(attachmentId)) invalid();
11772
- await request(
11773
- `/attachments/${attachmentId}?account_id=${accountId}`,
11774
- { method: "DELETE" },
11775
- signal
11776
- );
11777
- }
11778
- }
11779
- };
11780
- }
11781
- var emptyMessage = {
11782
- kind: "message",
11783
- id: "query",
11784
- conversationId: "query",
11785
- sortKey: 0,
11786
- createdAt: 1,
11787
- updatedAt: 1,
11788
- isDeleted: false,
11789
- data: {}
11790
- };
11791
-
11792
- // src/lib/chatStore/vaultRecords.ts
11793
- var PINNED_SORT_BASE = 2 ** 51;
11794
- var recordBudgetBytes = 104e4;
11795
- var storedDims2 = 1536;
11796
- var acceptedDims = /* @__PURE__ */ new Set([1536, 4096]);
11797
- var maxChunks = 16;
11798
- var droppable = ["thoughtProcess", "thinking", "sources", "toolCallEvents"];
11799
- var messageMetadata = /* @__PURE__ */ new Set(["conversationId", "uniqueId", "vector", "chunks"]);
11800
- var alwaysStored = /* @__PURE__ */ new Set(["role", "content", "responseDuration"]);
11801
- function plain(value) {
11802
- return JSON.parse(JSON.stringify(value ?? {}));
11803
- }
11804
- function text5(value) {
11805
- return typeof value === "string" ? value : void 0;
11806
- }
11807
- function number(value) {
11808
- return typeof value === "number" ? value : void 0;
11809
- }
11810
- function conversationSortKey(data, createdAt) {
11811
- const pinnedAt = number(data.pinnedAt);
11812
- if (pinnedAt) return PINNED_SORT_BASE + pinnedAt;
11813
- return number(data.lastActivityAt) ?? createdAt;
11814
- }
11815
- function toConversation(record2) {
11816
- const pinnedAt = number(record2.data.pinnedAt);
11817
- const projectId = text5(record2.data.projectId);
11818
- return {
11819
- uniqueId: record2.id,
11820
- conversationId: record2.id,
11821
- title: text5(record2.data.title) ?? "",
11822
- ...projectId ? { projectId } : {},
11823
- createdAt: new Date(record2.createdAt),
11824
- updatedAt: new Date(record2.updatedAt),
11825
- isDeleted: record2.isDeleted,
11826
- pinnedAt: pinnedAt ? new Date(pinnedAt) : null
11827
- };
11828
- }
11829
- function messageData(options) {
11830
- return plain(
11831
- Object.fromEntries(
11832
- Object.entries(options).filter(
11833
- ([key3, value]) => !messageMetadata.has(key3) && (!!value || alwaysStored.has(key3))
11834
- )
11835
- )
11836
- );
11837
- }
11838
- function toMessage(record2) {
11839
- const { chunkOffsets: _offsets, truncated: _truncated, ...fields } = record2.data;
11840
- return {
11841
- ...fields,
11842
- uniqueId: record2.id,
11843
- messageId: record2.sortKey,
11844
- conversationId: record2.conversationId,
11845
- createdAt: new Date(record2.createdAt),
11846
- updatedAt: new Date(record2.updatedAt)
11847
- };
11848
- }
11849
- function chunkOffsets(record2) {
11850
- const offsets = record2.data.chunkOffsets;
11851
- return Array.isArray(offsets) ? offsets : void 0;
11852
- }
11853
- function toSkeletons(records) {
11854
- const roleById = new Map(records.map((r) => [r.id, text5(r.data.role)]));
11855
- return records.map((r) => {
11856
- const parentMessageId = text5(r.data.parentMessageId);
11857
- const model = text5(r.data.model);
11858
- const artifact = r.data.role === "user" && !!parentMessageId && roleById.get(parentMessageId) === "user";
11859
- return {
11860
- uniqueId: r.id,
11861
- messageId: r.sortKey,
11862
- conversationId: r.conversationId,
11863
- role: text5(r.data.role) ?? "",
11864
- createdAt: new Date(r.createdAt),
11865
- ...parentMessageId ? { parentMessageId } : {},
11866
- ...model ? { model } : {},
11867
- ...artifact ? { content: text5(r.data.content) ?? "" } : {}
11868
- };
11869
- });
11870
- }
11871
- function toSummary(record2) {
11872
- return {
11873
- uniqueId: record2.id,
11874
- conversationId: record2.id,
11875
- summary: text5(record2.data.summary) ?? "",
11876
- summarizedUpTo: text5(record2.data.summarizedUpTo) ?? "",
11877
- tokenCount: number(record2.data.tokenCount) ?? 0,
11878
- createdAt: new Date(record2.createdAt),
11879
- updatedAt: new Date(record2.updatedAt)
11880
- };
11881
- }
11882
- function links(data) {
11883
- return Array.isArray(data.links) ? data.links : [];
11884
- }
11885
- function applyLinkOps(data, ops) {
11886
- let current = links(data);
11887
- for (const op of ops) {
11888
- if ("clear" in op) {
11889
- current = [];
11890
- continue;
11891
- }
11892
- const byId = new Map(current.map((l) => [l.memoryId, l]));
11893
- const turn = /* @__PURE__ */ new Set();
11894
- op.add.forEach((item, i) => {
11895
- if (!item.memoryId || turn.has(item.memoryId)) return;
11896
- turn.add(item.memoryId);
11897
- const known = byId.get(item.memoryId);
11898
- if (known && known.createdAt >= op.at + i) return;
11899
- byId.set(item.memoryId, { memoryId: item.memoryId, score: item.score, createdAt: op.at + i });
11900
- });
11901
- current = [...byId.values()].sort((a, b) => a.createdAt - b.createdAt).slice(-MAX_PER_CONVERSATION);
11902
- }
11903
- return plain({ links: current });
11904
- }
11905
- function toLinks(record2) {
11906
- return links(record2.data).map((l) => ({
11907
- uniqueId: `${record2.id}:${l.memoryId}`,
11908
- conversationId: record2.id,
11909
- memoryId: l.memoryId,
11910
- score: l.score,
11911
- createdAt: new Date(l.createdAt)
11912
- }));
11913
- }
11914
- function bytes(value) {
11915
- return new TextEncoder().encode(JSON.stringify(value)).length;
11916
- }
11917
- function fitRecord(record2) {
11918
- if (record2.kind !== "message" || bytes(record2) <= recordBudgetBytes) return record2;
11919
- const data = { ...record2.data };
11920
- const truncated = [];
11921
- for (const field10 of droppable) {
11922
- if (bytes({ ...record2, data }) <= recordBudgetBytes) break;
11923
- if (data[field10] === void 0) continue;
11924
- delete data[field10];
11925
- truncated.push(field10);
11926
- }
11927
- const content = text5(data.content) ?? "";
11928
- const over = bytes({ ...record2, data }) - recordBudgetBytes;
11929
- if (over > 0) {
11930
- const encoded = new TextEncoder().encode(content);
11931
- data.content = new TextDecoder().decode(encoded.slice(0, Math.max(0, encoded.length - over - 64))).replace(/�$/, "");
11932
- truncated.push("content");
11933
- }
11934
- return { ...record2, data: { ...data, truncated } };
11935
- }
11936
- function acceptedVector(vector2) {
11937
- if (!vector2 || !acceptedDims.has(vector2.length)) return void 0;
11938
- if (!vector2.every(Number.isFinite) || !vector2.slice(0, storedDims2).some((x) => x !== 0))
11939
- return void 0;
11940
- return [...vector2];
11941
- }
11942
- function normalized(vector2) {
11943
- const norm = Math.hypot(...vector2) || 1;
11944
- return vector2.map((x) => x / norm);
11945
- }
11946
- function fitChunks(chunks3) {
11947
- const usable = chunks3.map((c) => ({ ...c, vector: acceptedVector(c.vector) }));
11948
- if (usable.length === 0 || usable.some((c) => !c.vector)) return void 0;
11949
- if (usable.length <= maxChunks) return usable;
11950
- const out = [];
11951
- for (let g = 0; g < maxChunks; g++) {
11952
- const group = usable.slice(
11953
- Math.floor(g * usable.length / maxChunks),
11954
- Math.floor((g + 1) * usable.length / maxChunks)
11955
- );
11956
- const dims = group[0].vector.length;
11957
- const mean = new Array(dims).fill(0);
11958
- for (const c of group) {
11959
- const unit = normalized(c.vector);
11960
- for (let d = 0; d < dims; d++) mean[d] += unit[d];
11932
+ updated_at: r.updatedAt,
11933
+ is_deleted: r.isDeleted
11934
+ };
11935
+ });
11936
+ if (wireRevisions.length === 0) return [];
11937
+ const value = await request(
11938
+ "/verify",
11939
+ {
11940
+ method: "POST",
11941
+ body: JSON.stringify({ account_id: accountId, revisions: wireRevisions })
11942
+ },
11943
+ signal
11944
+ );
11945
+ if (!object(value) || !Array.isArray(value.missing)) invalid();
11946
+ const missing2 = value.missing.map(ref);
11947
+ if (missing2.some((r) => !asked.has(`${r.kind}/${r.id}`))) invalid();
11948
+ return missing2;
11949
+ },
11950
+ async activate(signal) {
11951
+ const value = await request(
11952
+ "/account",
11953
+ { method: "PUT", body: JSON.stringify({ account_id: accountId, state: "active" }) },
11954
+ signal
11955
+ );
11956
+ if (!object(value) || value.state !== "active" || typeof value.transitioned !== "boolean")
11957
+ invalid();
11958
+ return value.transitioned;
11959
+ },
11960
+ async changes(cursor, options2) {
11961
+ const limit = options2?.limit ?? 100;
11962
+ if (!integer(limit, 1) || limit > 200 || cursor !== void 0 && (!text5(cursor) || cursor.length > 2048))
11963
+ invalid();
11964
+ const params = new URLSearchParams({ account_id: String(accountId), limit: String(limit) });
11965
+ if (cursor) params.set("cursor", cursor);
11966
+ const value = await request(`/changes?${params}`, {}, options2?.signal);
11967
+ if (!object(value) || !Array.isArray(value.items) || value.items.length > limit || value.next_cursor !== void 0 && !text5(value.next_cursor) || value.changes_cursor !== void 0 && !text5(value.changes_cursor))
11968
+ invalid();
11969
+ const items = [];
11970
+ for (const item of value.items) items.push(await decode(item));
11971
+ options2?.signal?.throwIfAborted();
11972
+ return {
11973
+ items,
11974
+ ...value.next_cursor ? { nextCursor: value.next_cursor } : {},
11975
+ changesCursor: value.changes_cursor ?? cursor ?? ""
11976
+ };
11977
+ },
11978
+ async search(embedding2, options2) {
11979
+ const limit = options2?.limit ?? 20;
11980
+ const kindsFilter = options2?.kinds;
11981
+ if (!Array.isArray(embedding2) || !embeddings({
11982
+ record: { ...emptyMessage, isDeleted: false },
11983
+ embeddings: [embedding2]
11984
+ }) || !integer(limit, 1) || limit > 50 || kindsFilter !== void 0 && (!Array.isArray(kindsFilter) || kindsFilter.length === 0 || kindsFilter.some((k) => k !== "message" && k !== "summary")) || options2?.conversationId !== void 0 && !id(options2.conversationId) || options2?.excludeConversationId !== void 0 && !id(options2.excludeConversationId) || options2?.embeddingModel !== void 0 && (!text5(options2.embeddingModel) || options2.embeddingModel.length > 256))
11985
+ invalid();
11986
+ const value = await request(
11987
+ "/search",
11988
+ {
11989
+ method: "POST",
11990
+ body: JSON.stringify({
11991
+ account_id: accountId,
11992
+ embedding: embedding2.slice(0, storedDims2),
11993
+ limit,
11994
+ ...kindsFilter ? { kinds: kindsFilter } : {},
11995
+ ...options2?.conversationId ? { conversation_id: options2.conversationId } : {},
11996
+ ...options2?.excludeConversationId ? { exclude_conversation_id: options2.excludeConversationId } : {},
11997
+ ...options2?.embeddingModel ? { embedding_model: options2.embeddingModel } : {}
11998
+ })
11999
+ },
12000
+ options2?.signal
12001
+ );
12002
+ if (!object(value) || !Array.isArray(value.items) || value.items.length > limit) invalid();
12003
+ const hits = [];
12004
+ for (const item of value.items) {
12005
+ if (!object(item) || !integer(item.chunk) || typeof item.score !== "number" || !Number.isFinite(item.score))
12006
+ invalid();
12007
+ const snapshot = await decode(item);
12008
+ if (snapshot.record.isDeleted || kindsFilter !== void 0 && !kindsFilter.includes(snapshot.record.kind) || snapshot.record.kind !== "message" && snapshot.record.kind !== "summary" || options2?.conversationId !== void 0 && snapshot.record.conversationId !== options2.conversationId || snapshot.record.conversationId === options2?.excludeConversationId)
12009
+ invalid();
12010
+ hits.push({ ...snapshot, chunk: item.chunk, score: item.score });
12011
+ }
12012
+ options2?.signal?.throwIfAborted();
12013
+ return hits;
12014
+ },
12015
+ async deleteAll(signal) {
12016
+ const value = await request(`/records?account_id=${accountId}`, { method: "DELETE" }, signal);
12017
+ if (!object(value) || !integer(value.deleted)) invalid();
12018
+ return value.deleted;
12019
+ },
12020
+ attachments: {
12021
+ async create(sizeBytes, signal) {
12022
+ if (!integer(sizeBytes, 1) || sizeBytes > maxAttachmentBytes) invalid();
12023
+ const value = await request(
12024
+ "/attachments",
12025
+ {
12026
+ method: "POST",
12027
+ body: JSON.stringify({ account_id: accountId, size_bytes: sizeBytes })
12028
+ },
12029
+ signal
12030
+ );
12031
+ if (!object(value)) invalid();
12032
+ const created = attachment(value.attachment);
12033
+ if (created.sizeBytes !== sizeBytes || created.state !== "pending") invalid();
12034
+ return { attachment: created, upload: presigned(value.upload) };
12035
+ },
12036
+ async commit(attachmentId, owner, signal) {
12037
+ if (!attachmentIdPattern.test(attachmentId) || !object(owner) || owner.kind !== "message" && owner.kind !== "summary" || !id(owner.id))
12038
+ invalid();
12039
+ const value = await request(
12040
+ `/attachments/${attachmentId}/commit`,
12041
+ {
12042
+ method: "POST",
12043
+ body: JSON.stringify({
12044
+ account_id: accountId,
12045
+ owner_kind: owner.kind,
12046
+ owner_id: owner.id
12047
+ })
12048
+ },
12049
+ signal
12050
+ );
12051
+ if (!object(value)) invalid();
12052
+ const committed = attachment(value.attachment);
12053
+ if (committed.id !== attachmentId || committed.state !== "committed" || committed.ownerKind !== owner.kind || committed.ownerId !== owner.id)
12054
+ invalid();
12055
+ return committed;
12056
+ },
12057
+ async get(attachmentId, signal) {
12058
+ if (!attachmentIdPattern.test(attachmentId)) invalid();
12059
+ let value;
12060
+ try {
12061
+ value = await request(`/attachments/${attachmentId}?account_id=${accountId}`, {}, signal);
12062
+ } catch (error) {
12063
+ if (error instanceof ChatStoreError && error.status === 404) return null;
12064
+ throw error;
12065
+ }
12066
+ if (!object(value)) invalid();
12067
+ const found = attachment(value.attachment);
12068
+ if (found.id !== attachmentId) invalid();
12069
+ return {
12070
+ attachment: found,
12071
+ ...value.download !== void 0 ? { download: presigned(value.download) } : {}
12072
+ };
12073
+ },
12074
+ async delete(attachmentId, signal) {
12075
+ if (!attachmentIdPattern.test(attachmentId)) invalid();
12076
+ await request(
12077
+ `/attachments/${attachmentId}?account_id=${accountId}`,
12078
+ { method: "DELETE" },
12079
+ signal
12080
+ );
12081
+ }
11961
12082
  }
11962
- out.push({
11963
- vector: normalized(mean),
11964
- startOffset: group[0].startOffset,
11965
- endOffset: group[group.length - 1].endOffset
11966
- });
11967
- }
11968
- return out;
12083
+ };
11969
12084
  }
12085
+ var emptyMessage = {
12086
+ kind: "message",
12087
+ id: "query",
12088
+ conversationId: "query",
12089
+ sortKey: 0,
12090
+ createdAt: 1,
12091
+ updatedAt: 1,
12092
+ isDeleted: false,
12093
+ data: {}
12094
+ };
11970
12095
 
11971
12096
  // src/lib/chatStore/migrateChats.ts
11972
12097
  var maxPasses = 5;
11973
12098
  var verifyBatch = 1e3;
11974
- var importBatch = 100;
11975
- var importChunks = 200;
11976
- var importPlaintextBytes = 39e5;
11977
12099
  var writeBatch = 25;
11978
12100
  function key(record2) {
11979
12101
  return `${record2.kind}/${record2.id}`;
11980
12102
  }
11981
- function ms(date10, fallback = 1) {
11982
- const value = date10?.getTime();
11983
- return value && Number.isFinite(value) ? Math.max(1, Math.floor(value)) : fallback;
11984
- }
11985
12103
  function sealed(value) {
11986
12104
  if (typeof value === "string") return isEncrypted(value);
11987
12105
  if (Array.isArray(value)) return typeof value[0] !== "number" && value.some(sealed);
@@ -11989,42 +12107,6 @@ function sealed(value) {
11989
12107
  return Object.values(value).some(sealed);
11990
12108
  return false;
11991
12109
  }
11992
- function bytes2(value) {
11993
- return new TextEncoder().encode(JSON.stringify(value)).length;
11994
- }
11995
- function messageRecord(message) {
11996
- const {
11997
- uniqueId: _uniqueId,
11998
- messageId: _messageId,
11999
- createdAt: _createdAt,
12000
- updatedAt: _updatedAt,
12001
- decryptionStatus: _status,
12002
- chunks: chunks3,
12003
- vector: vector2,
12004
- ...fields
12005
- } = message;
12006
- const fitted = chunks3?.length ? fitChunks(chunks3) : void 0;
12007
- const single = fitted ? void 0 : acceptedVector(vector2);
12008
- const embeddings2 = fitted?.map((c) => c.vector) ?? (single ? [single] : void 0);
12009
- const record2 = fitRecord({
12010
- kind: "message",
12011
- id: message.uniqueId,
12012
- conversationId: message.conversationId,
12013
- sortKey: message.messageId,
12014
- createdAt: ms(message.createdAt),
12015
- updatedAt: ms(message.updatedAt, ms(message.createdAt)),
12016
- isDeleted: false,
12017
- data: {
12018
- ...messageData(fields),
12019
- ...fitted ? { chunkOffsets: fitted.map((c) => [c.startOffset, c.endOffset]) } : {}
12020
- }
12021
- });
12022
- return {
12023
- record: record2,
12024
- ...embeddings2 ? { embeddings: embeddings2 } : {},
12025
- ...embeddings2 && message.embeddingModel ? { embeddingModel: message.embeddingModel } : {}
12026
- };
12027
- }
12028
12110
  async function readLocal(options) {
12029
12111
  const { database } = options;
12030
12112
  const ctx = {
@@ -12035,7 +12117,7 @@ async function readLocal(options) {
12035
12117
  signMessage: options.signMessage,
12036
12118
  embeddedWalletSigner: options.embeddedWalletSigner
12037
12119
  };
12038
- const summaries = createSummaryContext(database);
12120
+ const summaries = createSummaryContext(database, ctx);
12039
12121
  const memory = {
12040
12122
  database,
12041
12123
  conversationMemoryCollection: database.get(ConversationMemory.table)
@@ -12078,29 +12160,10 @@ async function readLocal(options) {
12078
12160
  const messages = await getMessagesOp(ctx, conversationId);
12079
12161
  const unreadableTitle = isEncrypted(conversation.title);
12080
12162
  if (unreadableTitle) out.untitled++;
12081
- const createdAt = ms(conversation.createdAt);
12082
- const data = plain({
12083
- title: unreadableTitle ? "" : conversation.title,
12084
- projectId: conversation.projectId || void 0,
12085
- pinnedAt: conversation.pinnedAt ? ms(conversation.pinnedAt) : void 0,
12086
- lastActivityAt: Math.max(
12087
- ms(conversation.updatedAt, createdAt),
12088
- ...messages.map((m) => ms(m.createdAt))
12089
- )
12090
- });
12091
12163
  out.conversations++;
12092
- out.records.push({
12093
- record: {
12094
- kind: "conversation",
12095
- id: conversationId,
12096
- conversationId: "",
12097
- sortKey: conversationSortKey(data, createdAt),
12098
- createdAt,
12099
- updatedAt: ms(conversation.updatedAt, createdAt),
12100
- isDeleted: false,
12101
- data
12102
- }
12103
- });
12164
+ out.records.push(
12165
+ conversationRecord(unreadableTitle ? { ...conversation, title: "" } : conversation, messages)
12166
+ );
12104
12167
  for (const message of messages) {
12105
12168
  if (message.decryptionStatus || sealed(message)) {
12106
12169
  skip("message", message.uniqueId, "unreadable");
@@ -12116,7 +12179,9 @@ async function readLocal(options) {
12116
12179
  out.records.push(prepared);
12117
12180
  }
12118
12181
  const summary = await getConversationSummaryOp(summaries, conversationId);
12119
- if (summary)
12182
+ if (summary && isEncrypted(summary.summary)) {
12183
+ skip("summary", conversationId, "unreadable");
12184
+ } else if (summary)
12120
12185
  out.records.push({
12121
12186
  record: {
12122
12187
  kind: "summary",
@@ -12219,27 +12284,6 @@ async function deletedSinceUpload(store, journal, local, signal) {
12219
12284
  };
12220
12285
  });
12221
12286
  }
12222
- function batches(records) {
12223
- const out = [];
12224
- let batch = [];
12225
- let chunks3 = 0;
12226
- let size = 0;
12227
- for (const item of records) {
12228
- const vectors = item.embeddings?.length ?? 0;
12229
- const itemSize = bytes2(item.record);
12230
- if (batch.length > 0 && (batch.length >= importBatch || chunks3 + vectors > importChunks || size + itemSize > importPlaintextBytes)) {
12231
- out.push(batch);
12232
- batch = [];
12233
- chunks3 = 0;
12234
- size = 0;
12235
- }
12236
- batch.push(item);
12237
- chunks3 += vectors;
12238
- size += itemSize;
12239
- }
12240
- if (batch.length) out.push(batch);
12241
- return out;
12242
- }
12243
12287
  function fitVectors(records, available2, unembedded) {
12244
12288
  let total = records.reduce((sum, r) => sum + (r.embeddings?.length ?? 0), 0);
12245
12289
  if (total <= available2) return records;
@@ -13320,6 +13364,45 @@ function createVaultChatBackend(options) {
13320
13364
  },
13321
13365
  flush,
13322
13366
  poll,
13367
+ async importChats(chats) {
13368
+ await ready();
13369
+ const seen = /* @__PURE__ */ new Set();
13370
+ const unique = (items) => items.filter(({ record: record2 }) => {
13371
+ const k = key2(record2.kind, record2.id);
13372
+ if (seen.has(k)) return false;
13373
+ seen.add(k);
13374
+ return true;
13375
+ });
13376
+ const valid = chats.filter((c) => validChatId(c.conversation.conversationId));
13377
+ const conversations = unique(
13378
+ valid.map((c) => conversationRecord(c.conversation, c.messages))
13379
+ );
13380
+ const messages = unique(
13381
+ valid.flatMap(
13382
+ (c) => c.messages.filter(
13383
+ (m) => m.conversationId === c.conversation.conversationId && validChatId(m.uniqueId) && Number.isSafeInteger(m.messageId)
13384
+ ).map((m) => messageRecord({ ...m, vector: void 0, chunks: void 0 }))
13385
+ )
13386
+ );
13387
+ let conversationCount = 0;
13388
+ let messageCount = 0;
13389
+ try {
13390
+ for (const batch of batches(conversations))
13391
+ conversationCount += await store.importRecords(batch);
13392
+ for (const batch of batches(messages)) messageCount += await store.importRecords(batch);
13393
+ } finally {
13394
+ for (const { record: record2 } of messages) checked.delete(key2("message", record2.id));
13395
+ await refresh(conversations.map(({ record: record2 }) => key2("conversation", record2.id))).catch(
13396
+ () => void 0
13397
+ );
13398
+ for (const { record: record2 } of conversations) threads.delete(record2.id);
13399
+ channel?.postMessage("changed");
13400
+ emit({ type: "conversations", origin: "local" });
13401
+ for (const { record: record2 } of conversations)
13402
+ emit({ type: "messages", conversationId: record2.id, origin: "local" });
13403
+ }
13404
+ return { conversations: conversationCount, messages: messageCount };
13405
+ },
13323
13406
  status: () => ({ pending: pending.size + inflight.size, ...stopped ? { stopped } : {} }),
13324
13407
  close() {
13325
13408
  closed = true;
@@ -15797,11 +15880,14 @@ var WatermelonChatStorageAdapter = class {
15797
15880
  return searchChunksOp(this.ctx, queryVector, options);
15798
15881
  }
15799
15882
  getSummary(conversationId) {
15800
- return getConversationSummaryOp(createSummaryContext(this.ctx.database), conversationId);
15883
+ return getConversationSummaryOp(
15884
+ createSummaryContext(this.ctx.database, this.ctx),
15885
+ conversationId
15886
+ );
15801
15887
  }
15802
15888
  upsertSummary(conversationId, summary, summarizedUpTo, tokenCount) {
15803
15889
  return upsertConversationSummaryOp(
15804
- createSummaryContext(this.ctx.database),
15890
+ createSummaryContext(this.ctx.database, this.ctx),
15805
15891
  conversationId,
15806
15892
  summary,
15807
15893
  summarizedUpTo,
@@ -34098,7 +34184,7 @@ async function summarizeHistoryAtRevision(options, revision2) {
34098
34184
  const cachedSummary = await summaryStore(options).getSummary(conversationId);
34099
34185
  return {
34100
34186
  messagesToConvert: messages,
34101
- summarySystemMessage: cachedSummary?.summary ? summaryToSystemMessage(cachedSummary.summary) : null
34187
+ summarySystemMessage: cachedSummary?.summary && !isEncrypted(cachedSummary.summary) ? summaryToSystemMessage(cachedSummary.summary) : null
34102
34188
  };
34103
34189
  } catch {
34104
34190
  return { messagesToConvert: messages, summarySystemMessage: null };
@@ -34153,14 +34239,15 @@ async function doSummarizeHistory(options, revision2) {
34153
34239
  } = options;
34154
34240
  try {
34155
34241
  const summaries = summaryStore(options);
34156
- const persistSummary = (summary, summarizedUpTo, tokenCount) => database.write(async (writer) => {
34242
+ const persistSummary = (summary, summarizedUpTo, tokenCount) => {
34157
34243
  assertConversationRevision(database, conversationId, revision2);
34158
- await writer.callWriter(
34159
- () => summaries.upsertSummary(conversationId, summary, summarizedUpTo, tokenCount)
34160
- );
34161
- });
34244
+ return summaries.upsertSummary(conversationId, summary, summarizedUpTo, tokenCount);
34245
+ };
34162
34246
  let cachedSummary = await summaries.getSummary(conversationId);
34163
34247
  assertConversationRevision(database, conversationId, revision2);
34248
+ if (cachedSummary && isEncrypted(cachedSummary.summary)) {
34249
+ throw new Error("The cached summary could not be decrypted");
34250
+ }
34164
34251
  const lastCompacted = lastCompactionTime.get(conversationId) ?? 0;
34165
34252
  const compactionCooledDown = Date.now() - lastCompacted > COMPACTION_COOLDOWN_MS;
34166
34253
  if (cachedSummary && cachedSummary.tokenCount > summaryTokenThreshold * MAX_SUMMARY_TOKEN_RATIO && compactionCooledDown) {