@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.
@@ -11434,17 +11434,19 @@ var xhrTransport = (options) => {
11434
11434
 
11435
11435
  // src/lib/db/chat/summaryOperations.ts
11436
11436
  var import_watermelondb5 = require("@nozbe/watermelondb");
11437
- function createSummaryContext(database) {
11437
+ init_logger();
11438
+ function createSummaryContext(database, encryption = {}) {
11438
11439
  return {
11440
+ ...encryption,
11439
11441
  database,
11440
11442
  summariesCollection: database.get("conversation_summaries")
11441
11443
  };
11442
11444
  }
11443
- function modelToStored(record2) {
11445
+ function modelToStored(record2, summary) {
11444
11446
  return {
11445
11447
  uniqueId: record2.id,
11446
11448
  conversationId: record2.conversationId,
11447
- summary: record2.summary,
11449
+ summary,
11448
11450
  summarizedUpTo: record2.summarizedUpTo,
11449
11451
  tokenCount: record2.tokenCount,
11450
11452
  createdAt: record2.createdAt,
@@ -11454,28 +11456,62 @@ function modelToStored(record2) {
11454
11456
  async function getConversationSummaryOp(ctx, conversationId) {
11455
11457
  const results = await ctx.summariesCollection.query(import_watermelondb5.Q.where("conversation_id", conversationId)).fetch();
11456
11458
  if (results.length === 0) return null;
11457
- return modelToStored(results[0]);
11459
+ const record2 = results[0];
11460
+ const stored2 = modelToStored(record2, record2.summary);
11461
+ if (ctx.walletAddress && isEncrypted(stored2.summary)) {
11462
+ if (ctx.signMessage) {
11463
+ try {
11464
+ await requestEncryptionKey(ctx.walletAddress, ctx.signMessage, ctx.embeddedWalletSigner);
11465
+ } catch (error) {
11466
+ getLogger().warn("Failed to request the encryption key for the summary:", error);
11467
+ }
11468
+ }
11469
+ stored2.summary = await decryptField(stored2.summary, ctx.walletAddress);
11470
+ } else if (ctx.walletAddress && ctx.signMessage && stored2.summary) {
11471
+ try {
11472
+ const encryptedSummary = await encryptField(
11473
+ stored2.summary,
11474
+ ctx.walletAddress,
11475
+ ctx.signMessage,
11476
+ ctx.embeddedWalletSigner
11477
+ );
11478
+ await ctx.database.write(async () => {
11479
+ const current = await ctx.summariesCollection.query(import_watermelondb5.Q.where("id", record2.id)).fetch();
11480
+ if (current[0]?.summary === stored2.summary) {
11481
+ await current[0].update((rec) => {
11482
+ rec.summary = encryptedSummary;
11483
+ });
11484
+ }
11485
+ });
11486
+ } catch (error) {
11487
+ getLogger().warn("Failed to encrypt the legacy summary:", error);
11488
+ }
11489
+ }
11490
+ return stored2;
11458
11491
  }
11459
11492
  async function upsertConversationSummaryOp(ctx, conversationId, summary, summarizedUpTo, tokenCount) {
11493
+ const revision2 = getConversationRevision(ctx.database, conversationId);
11460
11494
  let record2;
11495
+ const encryptedSummary = ctx.walletAddress ? await encryptField(summary, ctx.walletAddress, ctx.signMessage, ctx.embeddedWalletSigner) : summary;
11461
11496
  await ctx.database.write(async () => {
11497
+ assertConversationRevision(ctx.database, conversationId, revision2);
11462
11498
  const existing = await ctx.summariesCollection.query(import_watermelondb5.Q.where("conversation_id", conversationId)).fetch();
11463
11499
  if (existing.length > 0) {
11464
11500
  record2 = await existing[0].update((rec) => {
11465
- rec.summary = summary;
11501
+ rec.summary = encryptedSummary;
11466
11502
  rec.summarizedUpTo = summarizedUpTo;
11467
11503
  rec.tokenCount = tokenCount;
11468
11504
  });
11469
11505
  } else {
11470
11506
  record2 = await ctx.summariesCollection.create((rec) => {
11471
11507
  rec.conversationId = conversationId;
11472
- rec.summary = summary;
11508
+ rec.summary = encryptedSummary;
11473
11509
  rec.summarizedUpTo = summarizedUpTo;
11474
11510
  rec.tokenCount = tokenCount;
11475
11511
  });
11476
11512
  }
11477
11513
  });
11478
- return modelToStored(record2);
11514
+ return modelToStored(record2, summary);
11479
11515
  }
11480
11516
  async function deleteConversationSummaryOp(ctx, conversationId) {
11481
11517
  await ctx.database.write(async () => {
@@ -11597,94 +11633,363 @@ async function clearConversationMemoriesOp(ctx, conversationId) {
11597
11633
  });
11598
11634
  }
11599
11635
 
11600
- // src/lib/memory/store/remotePersistence.ts
11601
- init_portalLlm();
11602
-
11603
- // src/lib/db/entities/types.ts
11604
- var ENTITY_KINDS = [
11605
- "person",
11606
- "organization",
11607
- "place",
11608
- "event",
11609
- "product",
11610
- "thing",
11611
- "concept",
11612
- "other"
11613
- ];
11614
- function normalizeEntityName(name) {
11615
- return name.trim().toLowerCase();
11636
+ // src/lib/chatStore/vaultRecords.ts
11637
+ var PINNED_SORT_BASE = 2 ** 51;
11638
+ var recordBudgetBytes = 104e4;
11639
+ var storedDims = 1536;
11640
+ var acceptedDims = /* @__PURE__ */ new Set([1536, 4096]);
11641
+ var maxChunks = 16;
11642
+ var droppable = ["thoughtProcess", "thinking", "sources", "toolCallEvents"];
11643
+ var messageMetadata = /* @__PURE__ */ new Set(["conversationId", "uniqueId", "vector", "chunks"]);
11644
+ var alwaysStored = /* @__PURE__ */ new Set(["role", "content", "responseDuration"]);
11645
+ function plain(value) {
11646
+ return JSON.parse(JSON.stringify(value ?? {}));
11616
11647
  }
11617
- var TOPIC_SALIENCE_WEIGHTS = { primary: 1, secondary: 0.6, mention: 0.3 };
11618
- function topicWeight(topic2) {
11619
- return topic2.source === "user" ? 1 : topic2.weight ?? 1;
11648
+ function text4(value) {
11649
+ return typeof value === "string" ? value : void 0;
11620
11650
  }
11621
- function primaryTopic(topics) {
11622
- let best = null;
11623
- for (const topic2 of topics ?? []) {
11624
- if (best === null || topicWeight(topic2) > topicWeight(best)) best = topic2;
11651
+ function number(value) {
11652
+ return typeof value === "number" ? value : void 0;
11653
+ }
11654
+ function conversationSortKey(data, createdAt) {
11655
+ const pinnedAt = number(data.pinnedAt);
11656
+ if (pinnedAt) return PINNED_SORT_BASE + pinnedAt;
11657
+ return number(data.lastActivityAt) ?? createdAt;
11658
+ }
11659
+ function toConversation(record2) {
11660
+ const pinnedAt = number(record2.data.pinnedAt);
11661
+ const projectId = text4(record2.data.projectId);
11662
+ return {
11663
+ uniqueId: record2.id,
11664
+ conversationId: record2.id,
11665
+ title: text4(record2.data.title) ?? "",
11666
+ ...projectId ? { projectId } : {},
11667
+ createdAt: new Date(record2.createdAt),
11668
+ updatedAt: new Date(record2.updatedAt),
11669
+ isDeleted: record2.isDeleted,
11670
+ pinnedAt: pinnedAt ? new Date(pinnedAt) : null
11671
+ };
11672
+ }
11673
+ function messageData(options) {
11674
+ return plain(
11675
+ Object.fromEntries(
11676
+ Object.entries(options).filter(
11677
+ ([key3, value]) => !messageMetadata.has(key3) && (!!value || alwaysStored.has(key3))
11678
+ )
11679
+ )
11680
+ );
11681
+ }
11682
+ function toMessage(record2) {
11683
+ const { chunkOffsets: _offsets, truncated: _truncated, ...fields } = record2.data;
11684
+ return {
11685
+ ...fields,
11686
+ uniqueId: record2.id,
11687
+ messageId: record2.sortKey,
11688
+ conversationId: record2.conversationId,
11689
+ createdAt: new Date(record2.createdAt),
11690
+ updatedAt: new Date(record2.updatedAt)
11691
+ };
11692
+ }
11693
+ function chunkOffsets(record2) {
11694
+ const offsets = record2.data.chunkOffsets;
11695
+ return Array.isArray(offsets) ? offsets : void 0;
11696
+ }
11697
+ function toSkeletons(records) {
11698
+ const roleById = new Map(records.map((r) => [r.id, text4(r.data.role)]));
11699
+ return records.map((r) => {
11700
+ const parentMessageId = text4(r.data.parentMessageId);
11701
+ const model = text4(r.data.model);
11702
+ const artifact = r.data.role === "user" && !!parentMessageId && roleById.get(parentMessageId) === "user";
11703
+ return {
11704
+ uniqueId: r.id,
11705
+ messageId: r.sortKey,
11706
+ conversationId: r.conversationId,
11707
+ role: text4(r.data.role) ?? "",
11708
+ createdAt: new Date(r.createdAt),
11709
+ ...parentMessageId ? { parentMessageId } : {},
11710
+ ...model ? { model } : {},
11711
+ ...artifact ? { content: text4(r.data.content) ?? "" } : {}
11712
+ };
11713
+ });
11714
+ }
11715
+ function toSummary(record2) {
11716
+ return {
11717
+ uniqueId: record2.id,
11718
+ conversationId: record2.id,
11719
+ summary: text4(record2.data.summary) ?? "",
11720
+ summarizedUpTo: text4(record2.data.summarizedUpTo) ?? "",
11721
+ tokenCount: number(record2.data.tokenCount) ?? 0,
11722
+ createdAt: new Date(record2.createdAt),
11723
+ updatedAt: new Date(record2.updatedAt)
11724
+ };
11725
+ }
11726
+ function links(data) {
11727
+ return Array.isArray(data.links) ? data.links : [];
11728
+ }
11729
+ function applyLinkOps(data, ops) {
11730
+ let current = links(data);
11731
+ for (const op of ops) {
11732
+ if ("clear" in op) {
11733
+ current = [];
11734
+ continue;
11735
+ }
11736
+ const byId = new Map(current.map((l) => [l.memoryId, l]));
11737
+ const turn = /* @__PURE__ */ new Set();
11738
+ op.add.forEach((item, i) => {
11739
+ if (!item.memoryId || turn.has(item.memoryId)) return;
11740
+ turn.add(item.memoryId);
11741
+ const known = byId.get(item.memoryId);
11742
+ if (known && known.createdAt >= op.at + i) return;
11743
+ byId.set(item.memoryId, { memoryId: item.memoryId, score: item.score, createdAt: op.at + i });
11744
+ });
11745
+ current = [...byId.values()].sort((a, b) => a.createdAt - b.createdAt).slice(-MAX_PER_CONVERSATION);
11625
11746
  }
11626
- return best;
11747
+ return plain({ links: current });
11627
11748
  }
11628
- function parseTopicWeight(value) {
11629
- return typeof value === "number" && value > 0 && value <= 1 ? value : void 0;
11749
+ function toLinks(record2) {
11750
+ return links(record2.data).map((l) => ({
11751
+ uniqueId: `${record2.id}:${l.memoryId}`,
11752
+ conversationId: record2.id,
11753
+ memoryId: l.memoryId,
11754
+ score: l.score,
11755
+ createdAt: new Date(l.createdAt)
11756
+ }));
11630
11757
  }
11631
- function serializeTopics(topics) {
11632
- return JSON.stringify(topics);
11758
+ function bytes(value) {
11759
+ return new TextEncoder().encode(JSON.stringify(value)).length;
11633
11760
  }
11634
- function parseTopics(value) {
11635
- if (typeof value !== "string" || value.length === 0) return null;
11636
- let parsed;
11637
- try {
11638
- parsed = JSON.parse(value);
11639
- } catch {
11640
- return null;
11761
+ function fitRecord(record2) {
11762
+ if (record2.kind !== "message" || bytes(record2) <= recordBudgetBytes) return record2;
11763
+ const data = { ...record2.data };
11764
+ const truncated = [];
11765
+ for (const field10 of droppable) {
11766
+ if (bytes({ ...record2, data }) <= recordBudgetBytes) break;
11767
+ if (data[field10] === void 0) continue;
11768
+ delete data[field10];
11769
+ truncated.push(field10);
11641
11770
  }
11642
- if (!Array.isArray(parsed)) return null;
11771
+ const content = text4(data.content) ?? "";
11772
+ const over = bytes({ ...record2, data }) - recordBudgetBytes;
11773
+ if (over > 0) {
11774
+ const encoded = new TextEncoder().encode(content);
11775
+ data.content = new TextDecoder().decode(encoded.slice(0, Math.max(0, encoded.length - over - 64))).replace(/�$/, "");
11776
+ truncated.push("content");
11777
+ }
11778
+ return { ...record2, data: { ...data, truncated } };
11779
+ }
11780
+ function acceptedVector(vector2) {
11781
+ if (!vector2 || !acceptedDims.has(vector2.length)) return void 0;
11782
+ if (!vector2.every(Number.isFinite) || !vector2.slice(0, storedDims).some((x) => x !== 0))
11783
+ return void 0;
11784
+ return [...vector2];
11785
+ }
11786
+ function normalized(vector2) {
11787
+ const norm = Math.hypot(...vector2) || 1;
11788
+ return vector2.map((x) => x / norm);
11789
+ }
11790
+ function fitChunks(chunks3) {
11791
+ const usable = chunks3.map((c) => ({ ...c, vector: acceptedVector(c.vector) }));
11792
+ if (usable.length === 0 || usable.some((c) => !c.vector)) return void 0;
11793
+ if (usable.length <= maxChunks) return usable;
11643
11794
  const out = [];
11644
- for (const raw of parsed) {
11645
- if (typeof raw !== "object" || raw === null) continue;
11646
- const { name, kind, source, weight } = raw;
11647
- if (typeof name !== "string" || name.trim().length === 0) continue;
11648
- const parsedWeight = parseTopicWeight(weight);
11795
+ for (let g = 0; g < maxChunks; g++) {
11796
+ const group = usable.slice(
11797
+ Math.floor(g * usable.length / maxChunks),
11798
+ Math.floor((g + 1) * usable.length / maxChunks)
11799
+ );
11800
+ const dims = group[0].vector.length;
11801
+ const mean = new Array(dims).fill(0);
11802
+ for (const c of group) {
11803
+ const unit = normalized(c.vector);
11804
+ for (let d = 0; d < dims; d++) mean[d] += unit[d];
11805
+ }
11649
11806
  out.push({
11650
- name,
11651
- ...typeof kind === "string" && kind.length > 0 ? { kind } : {},
11652
- source: source === "user" ? "user" : "auto",
11653
- ...parsedWeight !== void 0 ? { weight: parsedWeight } : {}
11807
+ vector: normalized(mean),
11808
+ startOffset: group[0].startOffset,
11809
+ endOffset: group[group.length - 1].endOffset
11654
11810
  });
11655
11811
  }
11656
11812
  return out;
11657
11813
  }
11658
11814
 
11659
- // src/lib/memory/store/remoteTopics.ts
11660
- function topic(value) {
11661
- const t = value;
11662
- 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")
11663
- throw new Error("Invalid nearby topic");
11664
- return t;
11815
+ // src/lib/chatStore/chatImports.ts
11816
+ var importBatch = 100;
11817
+ var importChunks = 200;
11818
+ var importPlaintextBytes = 39e5;
11819
+ function ms(date10, fallback = 1) {
11820
+ const value = date10?.getTime();
11821
+ return value && Number.isFinite(value) ? Math.max(1, Math.floor(value)) : fallback;
11665
11822
  }
11666
- function createRemoteTopicPersistence(request) {
11823
+ function bytes2(value) {
11824
+ return new TextEncoder().encode(JSON.stringify(value)).length;
11825
+ }
11826
+ function conversationRecord(conversation, messages) {
11827
+ const createdAt = ms(conversation.createdAt);
11828
+ const data = plain({
11829
+ title: conversation.title,
11830
+ projectId: conversation.projectId || void 0,
11831
+ pinnedAt: conversation.pinnedAt ? ms(conversation.pinnedAt) : void 0,
11832
+ lastActivityAt: Math.max(
11833
+ ms(conversation.updatedAt, createdAt),
11834
+ ...messages.map((m) => ms(m.createdAt))
11835
+ )
11836
+ });
11667
11837
  return {
11668
- topics: async () => {
11669
- const out = [];
11670
- let cursor;
11671
- do {
11672
- const page = await request(
11673
- "/topics?limit=200" + (cursor ? `&cursor=${encodeURIComponent(cursor)}` : ""),
11674
- {}
11675
- );
11676
- if (!Array.isArray(page.items) || page.next_cursor !== void 0 && typeof page.next_cursor !== "string")
11677
- throw new Error("Invalid nearby topic page");
11678
- out.push(...page.items.map(topic));
11679
- cursor = page.next_cursor;
11680
- } while (cursor);
11681
- return out;
11682
- },
11683
- putTopic: async (value, expectedVersion) => {
11684
- if (!Number.isSafeInteger(expectedVersion) || expectedVersion < 0)
11685
- throw new Error("Expected topic version must be a nonnegative integer");
11686
- return topic(
11687
- await request("/topics", {
11838
+ record: {
11839
+ kind: "conversation",
11840
+ id: conversation.conversationId,
11841
+ conversationId: "",
11842
+ sortKey: conversationSortKey(data, createdAt),
11843
+ createdAt,
11844
+ updatedAt: ms(conversation.updatedAt, createdAt),
11845
+ isDeleted: false,
11846
+ data
11847
+ }
11848
+ };
11849
+ }
11850
+ function messageRecord(message) {
11851
+ const {
11852
+ uniqueId: _uniqueId,
11853
+ messageId: _messageId,
11854
+ createdAt: _createdAt,
11855
+ updatedAt: _updatedAt,
11856
+ decryptionStatus: _status,
11857
+ chunks: chunks3,
11858
+ vector: vector2,
11859
+ ...fields
11860
+ } = message;
11861
+ const fitted = chunks3?.length ? fitChunks(chunks3) : void 0;
11862
+ const single = fitted ? void 0 : acceptedVector(vector2);
11863
+ const embeddings2 = fitted?.map((c) => c.vector) ?? (single ? [single] : void 0);
11864
+ const record2 = fitRecord({
11865
+ kind: "message",
11866
+ id: message.uniqueId,
11867
+ conversationId: message.conversationId,
11868
+ sortKey: message.messageId,
11869
+ createdAt: ms(message.createdAt),
11870
+ updatedAt: ms(message.updatedAt, ms(message.createdAt)),
11871
+ isDeleted: false,
11872
+ data: {
11873
+ ...messageData(fields),
11874
+ ...fitted ? { chunkOffsets: fitted.map((c) => [c.startOffset, c.endOffset]) } : {}
11875
+ }
11876
+ });
11877
+ return {
11878
+ record: record2,
11879
+ ...embeddings2 ? { embeddings: embeddings2 } : {},
11880
+ ...embeddings2 && message.embeddingModel ? { embeddingModel: message.embeddingModel } : {}
11881
+ };
11882
+ }
11883
+ function batches(records) {
11884
+ const out = [];
11885
+ let batch = [];
11886
+ let chunks3 = 0;
11887
+ let size = 0;
11888
+ for (const item of records) {
11889
+ const vectors = item.embeddings?.length ?? 0;
11890
+ const itemSize = bytes2(item.record);
11891
+ if (batch.length > 0 && (batch.length >= importBatch || chunks3 + vectors > importChunks || size + itemSize > importPlaintextBytes)) {
11892
+ out.push(batch);
11893
+ batch = [];
11894
+ chunks3 = 0;
11895
+ size = 0;
11896
+ }
11897
+ batch.push(item);
11898
+ chunks3 += vectors;
11899
+ size += itemSize;
11900
+ }
11901
+ if (batch.length) out.push(batch);
11902
+ return out;
11903
+ }
11904
+
11905
+ // src/lib/memory/store/remotePersistence.ts
11906
+ init_portalLlm();
11907
+
11908
+ // src/lib/db/entities/types.ts
11909
+ var ENTITY_KINDS = [
11910
+ "person",
11911
+ "organization",
11912
+ "place",
11913
+ "event",
11914
+ "product",
11915
+ "thing",
11916
+ "concept",
11917
+ "other"
11918
+ ];
11919
+ function normalizeEntityName(name) {
11920
+ return name.trim().toLowerCase();
11921
+ }
11922
+ var TOPIC_SALIENCE_WEIGHTS = { primary: 1, secondary: 0.6, mention: 0.3 };
11923
+ function topicWeight(topic2) {
11924
+ return topic2.source === "user" ? 1 : topic2.weight ?? 1;
11925
+ }
11926
+ function primaryTopic(topics) {
11927
+ let best = null;
11928
+ for (const topic2 of topics ?? []) {
11929
+ if (best === null || topicWeight(topic2) > topicWeight(best)) best = topic2;
11930
+ }
11931
+ return best;
11932
+ }
11933
+ function parseTopicWeight(value) {
11934
+ return typeof value === "number" && value > 0 && value <= 1 ? value : void 0;
11935
+ }
11936
+ function serializeTopics(topics) {
11937
+ return JSON.stringify(topics);
11938
+ }
11939
+ function parseTopics(value) {
11940
+ if (typeof value !== "string" || value.length === 0) return null;
11941
+ let parsed;
11942
+ try {
11943
+ parsed = JSON.parse(value);
11944
+ } catch {
11945
+ return null;
11946
+ }
11947
+ if (!Array.isArray(parsed)) return null;
11948
+ const out = [];
11949
+ for (const raw of parsed) {
11950
+ if (typeof raw !== "object" || raw === null) continue;
11951
+ const { name, kind, source, weight } = raw;
11952
+ if (typeof name !== "string" || name.trim().length === 0) continue;
11953
+ const parsedWeight = parseTopicWeight(weight);
11954
+ out.push({
11955
+ name,
11956
+ ...typeof kind === "string" && kind.length > 0 ? { kind } : {},
11957
+ source: source === "user" ? "user" : "auto",
11958
+ ...parsedWeight !== void 0 ? { weight: parsedWeight } : {}
11959
+ });
11960
+ }
11961
+ return out;
11962
+ }
11963
+
11964
+ // src/lib/memory/store/remoteTopics.ts
11965
+ function topic(value) {
11966
+ const t = value;
11967
+ 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")
11968
+ throw new Error("Invalid nearby topic");
11969
+ return t;
11970
+ }
11971
+ function createRemoteTopicPersistence(request) {
11972
+ return {
11973
+ topics: async () => {
11974
+ const out = [];
11975
+ let cursor;
11976
+ do {
11977
+ const page = await request(
11978
+ "/topics?limit=200" + (cursor ? `&cursor=${encodeURIComponent(cursor)}` : ""),
11979
+ {}
11980
+ );
11981
+ if (!Array.isArray(page.items) || page.next_cursor !== void 0 && typeof page.next_cursor !== "string")
11982
+ throw new Error("Invalid nearby topic page");
11983
+ out.push(...page.items.map(topic));
11984
+ cursor = page.next_cursor;
11985
+ } while (cursor);
11986
+ return out;
11987
+ },
11988
+ putTopic: async (value, expectedVersion) => {
11989
+ if (!Number.isSafeInteger(expectedVersion) || expectedVersion < 0)
11990
+ throw new Error("Expected topic version must be a nonnegative integer");
11991
+ return topic(
11992
+ await request("/topics", {
11688
11993
  method: "PUT",
11689
11994
  body: JSON.stringify({ topic: value, expected_version: expectedVersion })
11690
11995
  })
@@ -12339,7 +12644,7 @@ var ChatStoreError = class extends Error {
12339
12644
  var kinds = /* @__PURE__ */ new Set(["conversation", "message", "summary", "project", "memory_links"]);
12340
12645
  var cipherPattern = /^enc:v\d+:(?:[0-9a-f]{2}){28,}$/i;
12341
12646
  var maxPayload = 2 * 1024 * 1024;
12342
- var storedDims = 1536;
12647
+ var storedDims2 = 1536;
12343
12648
  var maxRequestCiphertext = 8 * 1024 * 1024;
12344
12649
  var maxGetRefs = 50;
12345
12650
  var maxImportRecords = 100;
@@ -12407,24 +12712,24 @@ function vector(v) {
12407
12712
  for (let i = 0; i < v.length; i++) {
12408
12713
  const x = v[i];
12409
12714
  if (typeof x !== "number" || !Number.isFinite(x)) return false;
12410
- if (i < storedDims && x !== 0) nonZero = true;
12715
+ if (i < storedDims2 && x !== 0) nonZero = true;
12411
12716
  }
12412
12717
  return nonZero;
12413
12718
  }
12414
12719
  function vectorFields(write) {
12415
12720
  return {
12416
- ...write.embeddings !== void 0 ? { embeddings: write.embeddings.map((v) => v.slice(0, storedDims)) } : {},
12721
+ ...write.embeddings !== void 0 ? { embeddings: write.embeddings.map((v) => v.slice(0, storedDims2)) } : {},
12417
12722
  ...write.embeddingModel !== void 0 ? { embedding_model: write.embeddingModel } : {}
12418
12723
  };
12419
12724
  }
12420
- function text4(value) {
12725
+ function text5(value) {
12421
12726
  return typeof value === "string";
12422
12727
  }
12423
12728
  function timestamp(value) {
12424
12729
  return typeof value === "string" && Number.isFinite(Date.parse(value));
12425
12730
  }
12426
12731
  function presigned(value) {
12427
- 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))
12732
+ 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))
12428
12733
  invalid();
12429
12734
  return {
12430
12735
  url: value.url,
@@ -12434,7 +12739,7 @@ function presigned(value) {
12434
12739
  };
12435
12740
  }
12436
12741
  function attachment(value) {
12437
- 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))
12742
+ 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))
12438
12743
  invalid();
12439
12744
  return {
12440
12745
  id: value.attachment_id,
@@ -12447,7 +12752,7 @@ function attachment(value) {
12447
12752
  };
12448
12753
  }
12449
12754
  function ref(value) {
12450
- if (!object(value) || !text4(value.kind) || !kinds.has(value.kind) || !id(value.id)) invalid();
12755
+ if (!object(value) || !text5(value.kind) || !kinds.has(value.kind) || !id(value.id)) invalid();
12451
12756
  return { kind: value.kind, id: value.id };
12452
12757
  }
12453
12758
  function sameMetadata(a, b) {
@@ -12670,7 +12975,7 @@ async function createRemoteChatStore(options) {
12670
12975
  },
12671
12976
  async account(signal) {
12672
12977
  const value = await request("/account", {}, signal);
12673
- 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))
12978
+ 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))
12674
12979
  invalid();
12675
12980
  if (value.key_id && value.key_id !== keyId)
12676
12981
  throw new ChatStoreError("Canonical chat key does not match", 409, "key_mismatch");
@@ -12714,364 +13019,178 @@ async function createRemoteChatStore(options) {
12714
13019
  asked.add(`${checked.kind}/${checked.id}`);
12715
13020
  return {
12716
13021
  kind: checked.kind,
12717
- id: checked.id,
12718
- updated_at: r.updatedAt,
12719
- is_deleted: r.isDeleted
12720
- };
12721
- });
12722
- if (wireRevisions.length === 0) return [];
12723
- const value = await request(
12724
- "/verify",
12725
- {
12726
- method: "POST",
12727
- body: JSON.stringify({ account_id: accountId, revisions: wireRevisions })
12728
- },
12729
- signal
12730
- );
12731
- if (!object(value) || !Array.isArray(value.missing)) invalid();
12732
- const missing2 = value.missing.map(ref);
12733
- if (missing2.some((r) => !asked.has(`${r.kind}/${r.id}`))) invalid();
12734
- return missing2;
12735
- },
12736
- async activate(signal) {
12737
- const value = await request(
12738
- "/account",
12739
- { method: "PUT", body: JSON.stringify({ account_id: accountId, state: "active" }) },
12740
- signal
12741
- );
12742
- if (!object(value) || value.state !== "active" || typeof value.transitioned !== "boolean")
12743
- invalid();
12744
- return value.transitioned;
12745
- },
12746
- async changes(cursor, options2) {
12747
- const limit = options2?.limit ?? 100;
12748
- if (!integer(limit, 1) || limit > 200 || cursor !== void 0 && (!text4(cursor) || cursor.length > 2048))
12749
- invalid();
12750
- const params = new URLSearchParams({ account_id: String(accountId), limit: String(limit) });
12751
- if (cursor) params.set("cursor", cursor);
12752
- const value = await request(`/changes?${params}`, {}, options2?.signal);
12753
- 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))
12754
- invalid();
12755
- const items = [];
12756
- for (const item of value.items) items.push(await decode(item));
12757
- options2?.signal?.throwIfAborted();
12758
- return {
12759
- items,
12760
- ...value.next_cursor ? { nextCursor: value.next_cursor } : {},
12761
- changesCursor: value.changes_cursor ?? cursor ?? ""
12762
- };
12763
- },
12764
- async search(embedding2, options2) {
12765
- const limit = options2?.limit ?? 20;
12766
- const kindsFilter = options2?.kinds;
12767
- if (!Array.isArray(embedding2) || !embeddings({
12768
- record: { ...emptyMessage, isDeleted: false },
12769
- embeddings: [embedding2]
12770
- }) || !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))
12771
- invalid();
12772
- const value = await request(
12773
- "/search",
12774
- {
12775
- method: "POST",
12776
- body: JSON.stringify({
12777
- account_id: accountId,
12778
- embedding: embedding2.slice(0, storedDims),
12779
- limit,
12780
- ...kindsFilter ? { kinds: kindsFilter } : {},
12781
- ...options2?.conversationId ? { conversation_id: options2.conversationId } : {},
12782
- ...options2?.excludeConversationId ? { exclude_conversation_id: options2.excludeConversationId } : {},
12783
- ...options2?.embeddingModel ? { embedding_model: options2.embeddingModel } : {}
12784
- })
12785
- },
12786
- options2?.signal
12787
- );
12788
- if (!object(value) || !Array.isArray(value.items) || value.items.length > limit) invalid();
12789
- const hits = [];
12790
- for (const item of value.items) {
12791
- if (!object(item) || !integer(item.chunk) || typeof item.score !== "number" || !Number.isFinite(item.score))
12792
- invalid();
12793
- const snapshot = await decode(item);
12794
- 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)
12795
- invalid();
12796
- hits.push({ ...snapshot, chunk: item.chunk, score: item.score });
12797
- }
12798
- options2?.signal?.throwIfAborted();
12799
- return hits;
12800
- },
12801
- async deleteAll(signal) {
12802
- const value = await request(`/records?account_id=${accountId}`, { method: "DELETE" }, signal);
12803
- if (!object(value) || !integer(value.deleted)) invalid();
12804
- return value.deleted;
12805
- },
12806
- attachments: {
12807
- async create(sizeBytes, signal) {
12808
- if (!integer(sizeBytes, 1) || sizeBytes > maxAttachmentBytes) invalid();
12809
- const value = await request(
12810
- "/attachments",
12811
- {
12812
- method: "POST",
12813
- body: JSON.stringify({ account_id: accountId, size_bytes: sizeBytes })
12814
- },
12815
- signal
12816
- );
12817
- if (!object(value)) invalid();
12818
- const created = attachment(value.attachment);
12819
- if (created.sizeBytes !== sizeBytes || created.state !== "pending") invalid();
12820
- return { attachment: created, upload: presigned(value.upload) };
12821
- },
12822
- async commit(attachmentId, owner, signal) {
12823
- if (!attachmentIdPattern.test(attachmentId) || !object(owner) || owner.kind !== "message" && owner.kind !== "summary" || !id(owner.id))
12824
- invalid();
12825
- const value = await request(
12826
- `/attachments/${attachmentId}/commit`,
12827
- {
12828
- method: "POST",
12829
- body: JSON.stringify({
12830
- account_id: accountId,
12831
- owner_kind: owner.kind,
12832
- owner_id: owner.id
12833
- })
12834
- },
12835
- signal
12836
- );
12837
- if (!object(value)) invalid();
12838
- const committed = attachment(value.attachment);
12839
- if (committed.id !== attachmentId || committed.state !== "committed" || committed.ownerKind !== owner.kind || committed.ownerId !== owner.id)
12840
- invalid();
12841
- return committed;
12842
- },
12843
- async get(attachmentId, signal) {
12844
- if (!attachmentIdPattern.test(attachmentId)) invalid();
12845
- let value;
12846
- try {
12847
- value = await request(`/attachments/${attachmentId}?account_id=${accountId}`, {}, signal);
12848
- } catch (error) {
12849
- if (error instanceof ChatStoreError && error.status === 404) return null;
12850
- throw error;
12851
- }
12852
- if (!object(value)) invalid();
12853
- const found = attachment(value.attachment);
12854
- if (found.id !== attachmentId) invalid();
12855
- return {
12856
- attachment: found,
12857
- ...value.download !== void 0 ? { download: presigned(value.download) } : {}
12858
- };
12859
- },
12860
- async delete(attachmentId, signal) {
12861
- if (!attachmentIdPattern.test(attachmentId)) invalid();
12862
- await request(
12863
- `/attachments/${attachmentId}?account_id=${accountId}`,
12864
- { method: "DELETE" },
12865
- signal
12866
- );
12867
- }
12868
- }
12869
- };
12870
- }
12871
- var emptyMessage = {
12872
- kind: "message",
12873
- id: "query",
12874
- conversationId: "query",
12875
- sortKey: 0,
12876
- createdAt: 1,
12877
- updatedAt: 1,
12878
- isDeleted: false,
12879
- data: {}
12880
- };
12881
-
12882
- // src/lib/chatStore/vaultRecords.ts
12883
- var PINNED_SORT_BASE = 2 ** 51;
12884
- var recordBudgetBytes = 104e4;
12885
- var storedDims2 = 1536;
12886
- var acceptedDims = /* @__PURE__ */ new Set([1536, 4096]);
12887
- var maxChunks = 16;
12888
- var droppable = ["thoughtProcess", "thinking", "sources", "toolCallEvents"];
12889
- var messageMetadata = /* @__PURE__ */ new Set(["conversationId", "uniqueId", "vector", "chunks"]);
12890
- var alwaysStored = /* @__PURE__ */ new Set(["role", "content", "responseDuration"]);
12891
- function plain(value) {
12892
- return JSON.parse(JSON.stringify(value ?? {}));
12893
- }
12894
- function text5(value) {
12895
- return typeof value === "string" ? value : void 0;
12896
- }
12897
- function number(value) {
12898
- return typeof value === "number" ? value : void 0;
12899
- }
12900
- function conversationSortKey(data, createdAt) {
12901
- const pinnedAt = number(data.pinnedAt);
12902
- if (pinnedAt) return PINNED_SORT_BASE + pinnedAt;
12903
- return number(data.lastActivityAt) ?? createdAt;
12904
- }
12905
- function toConversation(record2) {
12906
- const pinnedAt = number(record2.data.pinnedAt);
12907
- const projectId = text5(record2.data.projectId);
12908
- return {
12909
- uniqueId: record2.id,
12910
- conversationId: record2.id,
12911
- title: text5(record2.data.title) ?? "",
12912
- ...projectId ? { projectId } : {},
12913
- createdAt: new Date(record2.createdAt),
12914
- updatedAt: new Date(record2.updatedAt),
12915
- isDeleted: record2.isDeleted,
12916
- pinnedAt: pinnedAt ? new Date(pinnedAt) : null
12917
- };
12918
- }
12919
- function messageData(options) {
12920
- return plain(
12921
- Object.fromEntries(
12922
- Object.entries(options).filter(
12923
- ([key3, value]) => !messageMetadata.has(key3) && (!!value || alwaysStored.has(key3))
12924
- )
12925
- )
12926
- );
12927
- }
12928
- function toMessage(record2) {
12929
- const { chunkOffsets: _offsets, truncated: _truncated, ...fields } = record2.data;
12930
- return {
12931
- ...fields,
12932
- uniqueId: record2.id,
12933
- messageId: record2.sortKey,
12934
- conversationId: record2.conversationId,
12935
- createdAt: new Date(record2.createdAt),
12936
- updatedAt: new Date(record2.updatedAt)
12937
- };
12938
- }
12939
- function chunkOffsets(record2) {
12940
- const offsets = record2.data.chunkOffsets;
12941
- return Array.isArray(offsets) ? offsets : void 0;
12942
- }
12943
- function toSkeletons(records) {
12944
- const roleById = new Map(records.map((r) => [r.id, text5(r.data.role)]));
12945
- return records.map((r) => {
12946
- const parentMessageId = text5(r.data.parentMessageId);
12947
- const model = text5(r.data.model);
12948
- const artifact = r.data.role === "user" && !!parentMessageId && roleById.get(parentMessageId) === "user";
12949
- return {
12950
- uniqueId: r.id,
12951
- messageId: r.sortKey,
12952
- conversationId: r.conversationId,
12953
- role: text5(r.data.role) ?? "",
12954
- createdAt: new Date(r.createdAt),
12955
- ...parentMessageId ? { parentMessageId } : {},
12956
- ...model ? { model } : {},
12957
- ...artifact ? { content: text5(r.data.content) ?? "" } : {}
12958
- };
12959
- });
12960
- }
12961
- function toSummary(record2) {
12962
- return {
12963
- uniqueId: record2.id,
12964
- conversationId: record2.id,
12965
- summary: text5(record2.data.summary) ?? "",
12966
- summarizedUpTo: text5(record2.data.summarizedUpTo) ?? "",
12967
- tokenCount: number(record2.data.tokenCount) ?? 0,
12968
- createdAt: new Date(record2.createdAt),
12969
- updatedAt: new Date(record2.updatedAt)
12970
- };
12971
- }
12972
- function links(data) {
12973
- return Array.isArray(data.links) ? data.links : [];
12974
- }
12975
- function applyLinkOps(data, ops) {
12976
- let current = links(data);
12977
- for (const op of ops) {
12978
- if ("clear" in op) {
12979
- current = [];
12980
- continue;
12981
- }
12982
- const byId = new Map(current.map((l) => [l.memoryId, l]));
12983
- const turn = /* @__PURE__ */ new Set();
12984
- op.add.forEach((item, i) => {
12985
- if (!item.memoryId || turn.has(item.memoryId)) return;
12986
- turn.add(item.memoryId);
12987
- const known = byId.get(item.memoryId);
12988
- if (known && known.createdAt >= op.at + i) return;
12989
- byId.set(item.memoryId, { memoryId: item.memoryId, score: item.score, createdAt: op.at + i });
12990
- });
12991
- current = [...byId.values()].sort((a, b) => a.createdAt - b.createdAt).slice(-MAX_PER_CONVERSATION);
12992
- }
12993
- return plain({ links: current });
12994
- }
12995
- function toLinks(record2) {
12996
- return links(record2.data).map((l) => ({
12997
- uniqueId: `${record2.id}:${l.memoryId}`,
12998
- conversationId: record2.id,
12999
- memoryId: l.memoryId,
13000
- score: l.score,
13001
- createdAt: new Date(l.createdAt)
13002
- }));
13003
- }
13004
- function bytes(value) {
13005
- return new TextEncoder().encode(JSON.stringify(value)).length;
13006
- }
13007
- function fitRecord(record2) {
13008
- if (record2.kind !== "message" || bytes(record2) <= recordBudgetBytes) return record2;
13009
- const data = { ...record2.data };
13010
- const truncated = [];
13011
- for (const field10 of droppable) {
13012
- if (bytes({ ...record2, data }) <= recordBudgetBytes) break;
13013
- if (data[field10] === void 0) continue;
13014
- delete data[field10];
13015
- truncated.push(field10);
13016
- }
13017
- const content = text5(data.content) ?? "";
13018
- const over = bytes({ ...record2, data }) - recordBudgetBytes;
13019
- if (over > 0) {
13020
- const encoded = new TextEncoder().encode(content);
13021
- data.content = new TextDecoder().decode(encoded.slice(0, Math.max(0, encoded.length - over - 64))).replace(/�$/, "");
13022
- truncated.push("content");
13023
- }
13024
- return { ...record2, data: { ...data, truncated } };
13025
- }
13026
- function acceptedVector(vector2) {
13027
- if (!vector2 || !acceptedDims.has(vector2.length)) return void 0;
13028
- if (!vector2.every(Number.isFinite) || !vector2.slice(0, storedDims2).some((x) => x !== 0))
13029
- return void 0;
13030
- return [...vector2];
13031
- }
13032
- function normalized(vector2) {
13033
- const norm = Math.hypot(...vector2) || 1;
13034
- return vector2.map((x) => x / norm);
13035
- }
13036
- function fitChunks(chunks3) {
13037
- const usable = chunks3.map((c) => ({ ...c, vector: acceptedVector(c.vector) }));
13038
- if (usable.length === 0 || usable.some((c) => !c.vector)) return void 0;
13039
- if (usable.length <= maxChunks) return usable;
13040
- const out = [];
13041
- for (let g = 0; g < maxChunks; g++) {
13042
- const group = usable.slice(
13043
- Math.floor(g * usable.length / maxChunks),
13044
- Math.floor((g + 1) * usable.length / maxChunks)
13045
- );
13046
- const dims = group[0].vector.length;
13047
- const mean = new Array(dims).fill(0);
13048
- for (const c of group) {
13049
- const unit = normalized(c.vector);
13050
- for (let d = 0; d < dims; d++) mean[d] += unit[d];
13022
+ id: checked.id,
13023
+ updated_at: r.updatedAt,
13024
+ is_deleted: r.isDeleted
13025
+ };
13026
+ });
13027
+ if (wireRevisions.length === 0) return [];
13028
+ const value = await request(
13029
+ "/verify",
13030
+ {
13031
+ method: "POST",
13032
+ body: JSON.stringify({ account_id: accountId, revisions: wireRevisions })
13033
+ },
13034
+ signal
13035
+ );
13036
+ if (!object(value) || !Array.isArray(value.missing)) invalid();
13037
+ const missing2 = value.missing.map(ref);
13038
+ if (missing2.some((r) => !asked.has(`${r.kind}/${r.id}`))) invalid();
13039
+ return missing2;
13040
+ },
13041
+ async activate(signal) {
13042
+ const value = await request(
13043
+ "/account",
13044
+ { method: "PUT", body: JSON.stringify({ account_id: accountId, state: "active" }) },
13045
+ signal
13046
+ );
13047
+ if (!object(value) || value.state !== "active" || typeof value.transitioned !== "boolean")
13048
+ invalid();
13049
+ return value.transitioned;
13050
+ },
13051
+ async changes(cursor, options2) {
13052
+ const limit = options2?.limit ?? 100;
13053
+ if (!integer(limit, 1) || limit > 200 || cursor !== void 0 && (!text5(cursor) || cursor.length > 2048))
13054
+ invalid();
13055
+ const params = new URLSearchParams({ account_id: String(accountId), limit: String(limit) });
13056
+ if (cursor) params.set("cursor", cursor);
13057
+ const value = await request(`/changes?${params}`, {}, options2?.signal);
13058
+ 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))
13059
+ invalid();
13060
+ const items = [];
13061
+ for (const item of value.items) items.push(await decode(item));
13062
+ options2?.signal?.throwIfAborted();
13063
+ return {
13064
+ items,
13065
+ ...value.next_cursor ? { nextCursor: value.next_cursor } : {},
13066
+ changesCursor: value.changes_cursor ?? cursor ?? ""
13067
+ };
13068
+ },
13069
+ async search(embedding2, options2) {
13070
+ const limit = options2?.limit ?? 20;
13071
+ const kindsFilter = options2?.kinds;
13072
+ if (!Array.isArray(embedding2) || !embeddings({
13073
+ record: { ...emptyMessage, isDeleted: false },
13074
+ embeddings: [embedding2]
13075
+ }) || !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))
13076
+ invalid();
13077
+ const value = await request(
13078
+ "/search",
13079
+ {
13080
+ method: "POST",
13081
+ body: JSON.stringify({
13082
+ account_id: accountId,
13083
+ embedding: embedding2.slice(0, storedDims2),
13084
+ limit,
13085
+ ...kindsFilter ? { kinds: kindsFilter } : {},
13086
+ ...options2?.conversationId ? { conversation_id: options2.conversationId } : {},
13087
+ ...options2?.excludeConversationId ? { exclude_conversation_id: options2.excludeConversationId } : {},
13088
+ ...options2?.embeddingModel ? { embedding_model: options2.embeddingModel } : {}
13089
+ })
13090
+ },
13091
+ options2?.signal
13092
+ );
13093
+ if (!object(value) || !Array.isArray(value.items) || value.items.length > limit) invalid();
13094
+ const hits = [];
13095
+ for (const item of value.items) {
13096
+ if (!object(item) || !integer(item.chunk) || typeof item.score !== "number" || !Number.isFinite(item.score))
13097
+ invalid();
13098
+ const snapshot = await decode(item);
13099
+ 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)
13100
+ invalid();
13101
+ hits.push({ ...snapshot, chunk: item.chunk, score: item.score });
13102
+ }
13103
+ options2?.signal?.throwIfAborted();
13104
+ return hits;
13105
+ },
13106
+ async deleteAll(signal) {
13107
+ const value = await request(`/records?account_id=${accountId}`, { method: "DELETE" }, signal);
13108
+ if (!object(value) || !integer(value.deleted)) invalid();
13109
+ return value.deleted;
13110
+ },
13111
+ attachments: {
13112
+ async create(sizeBytes, signal) {
13113
+ if (!integer(sizeBytes, 1) || sizeBytes > maxAttachmentBytes) invalid();
13114
+ const value = await request(
13115
+ "/attachments",
13116
+ {
13117
+ method: "POST",
13118
+ body: JSON.stringify({ account_id: accountId, size_bytes: sizeBytes })
13119
+ },
13120
+ signal
13121
+ );
13122
+ if (!object(value)) invalid();
13123
+ const created = attachment(value.attachment);
13124
+ if (created.sizeBytes !== sizeBytes || created.state !== "pending") invalid();
13125
+ return { attachment: created, upload: presigned(value.upload) };
13126
+ },
13127
+ async commit(attachmentId, owner, signal) {
13128
+ if (!attachmentIdPattern.test(attachmentId) || !object(owner) || owner.kind !== "message" && owner.kind !== "summary" || !id(owner.id))
13129
+ invalid();
13130
+ const value = await request(
13131
+ `/attachments/${attachmentId}/commit`,
13132
+ {
13133
+ method: "POST",
13134
+ body: JSON.stringify({
13135
+ account_id: accountId,
13136
+ owner_kind: owner.kind,
13137
+ owner_id: owner.id
13138
+ })
13139
+ },
13140
+ signal
13141
+ );
13142
+ if (!object(value)) invalid();
13143
+ const committed = attachment(value.attachment);
13144
+ if (committed.id !== attachmentId || committed.state !== "committed" || committed.ownerKind !== owner.kind || committed.ownerId !== owner.id)
13145
+ invalid();
13146
+ return committed;
13147
+ },
13148
+ async get(attachmentId, signal) {
13149
+ if (!attachmentIdPattern.test(attachmentId)) invalid();
13150
+ let value;
13151
+ try {
13152
+ value = await request(`/attachments/${attachmentId}?account_id=${accountId}`, {}, signal);
13153
+ } catch (error) {
13154
+ if (error instanceof ChatStoreError && error.status === 404) return null;
13155
+ throw error;
13156
+ }
13157
+ if (!object(value)) invalid();
13158
+ const found = attachment(value.attachment);
13159
+ if (found.id !== attachmentId) invalid();
13160
+ return {
13161
+ attachment: found,
13162
+ ...value.download !== void 0 ? { download: presigned(value.download) } : {}
13163
+ };
13164
+ },
13165
+ async delete(attachmentId, signal) {
13166
+ if (!attachmentIdPattern.test(attachmentId)) invalid();
13167
+ await request(
13168
+ `/attachments/${attachmentId}?account_id=${accountId}`,
13169
+ { method: "DELETE" },
13170
+ signal
13171
+ );
13172
+ }
13051
13173
  }
13052
- out.push({
13053
- vector: normalized(mean),
13054
- startOffset: group[0].startOffset,
13055
- endOffset: group[group.length - 1].endOffset
13056
- });
13057
- }
13058
- return out;
13174
+ };
13059
13175
  }
13176
+ var emptyMessage = {
13177
+ kind: "message",
13178
+ id: "query",
13179
+ conversationId: "query",
13180
+ sortKey: 0,
13181
+ createdAt: 1,
13182
+ updatedAt: 1,
13183
+ isDeleted: false,
13184
+ data: {}
13185
+ };
13060
13186
 
13061
13187
  // src/lib/chatStore/migrateChats.ts
13062
13188
  var maxPasses = 5;
13063
13189
  var verifyBatch = 1e3;
13064
- var importBatch = 100;
13065
- var importChunks = 200;
13066
- var importPlaintextBytes = 39e5;
13067
13190
  var writeBatch = 25;
13068
13191
  function key(record2) {
13069
13192
  return `${record2.kind}/${record2.id}`;
13070
13193
  }
13071
- function ms(date10, fallback = 1) {
13072
- const value = date10?.getTime();
13073
- return value && Number.isFinite(value) ? Math.max(1, Math.floor(value)) : fallback;
13074
- }
13075
13194
  function sealed(value) {
13076
13195
  if (typeof value === "string") return isEncrypted(value);
13077
13196
  if (Array.isArray(value)) return typeof value[0] !== "number" && value.some(sealed);
@@ -13079,42 +13198,6 @@ function sealed(value) {
13079
13198
  return Object.values(value).some(sealed);
13080
13199
  return false;
13081
13200
  }
13082
- function bytes2(value) {
13083
- return new TextEncoder().encode(JSON.stringify(value)).length;
13084
- }
13085
- function messageRecord(message) {
13086
- const {
13087
- uniqueId: _uniqueId,
13088
- messageId: _messageId,
13089
- createdAt: _createdAt,
13090
- updatedAt: _updatedAt,
13091
- decryptionStatus: _status,
13092
- chunks: chunks3,
13093
- vector: vector2,
13094
- ...fields
13095
- } = message;
13096
- const fitted = chunks3?.length ? fitChunks(chunks3) : void 0;
13097
- const single = fitted ? void 0 : acceptedVector(vector2);
13098
- const embeddings2 = fitted?.map((c) => c.vector) ?? (single ? [single] : void 0);
13099
- const record2 = fitRecord({
13100
- kind: "message",
13101
- id: message.uniqueId,
13102
- conversationId: message.conversationId,
13103
- sortKey: message.messageId,
13104
- createdAt: ms(message.createdAt),
13105
- updatedAt: ms(message.updatedAt, ms(message.createdAt)),
13106
- isDeleted: false,
13107
- data: {
13108
- ...messageData(fields),
13109
- ...fitted ? { chunkOffsets: fitted.map((c) => [c.startOffset, c.endOffset]) } : {}
13110
- }
13111
- });
13112
- return {
13113
- record: record2,
13114
- ...embeddings2 ? { embeddings: embeddings2 } : {},
13115
- ...embeddings2 && message.embeddingModel ? { embeddingModel: message.embeddingModel } : {}
13116
- };
13117
- }
13118
13201
  async function readLocal(options) {
13119
13202
  const { database } = options;
13120
13203
  const ctx = {
@@ -13125,7 +13208,7 @@ async function readLocal(options) {
13125
13208
  signMessage: options.signMessage,
13126
13209
  embeddedWalletSigner: options.embeddedWalletSigner
13127
13210
  };
13128
- const summaries = createSummaryContext(database);
13211
+ const summaries = createSummaryContext(database, ctx);
13129
13212
  const memory = {
13130
13213
  database,
13131
13214
  conversationMemoryCollection: database.get(ConversationMemory.table)
@@ -13168,29 +13251,10 @@ async function readLocal(options) {
13168
13251
  const messages = await getMessagesOp(ctx, conversationId);
13169
13252
  const unreadableTitle = isEncrypted(conversation.title);
13170
13253
  if (unreadableTitle) out.untitled++;
13171
- const createdAt = ms(conversation.createdAt);
13172
- const data = plain({
13173
- title: unreadableTitle ? "" : conversation.title,
13174
- projectId: conversation.projectId || void 0,
13175
- pinnedAt: conversation.pinnedAt ? ms(conversation.pinnedAt) : void 0,
13176
- lastActivityAt: Math.max(
13177
- ms(conversation.updatedAt, createdAt),
13178
- ...messages.map((m) => ms(m.createdAt))
13179
- )
13180
- });
13181
13254
  out.conversations++;
13182
- out.records.push({
13183
- record: {
13184
- kind: "conversation",
13185
- id: conversationId,
13186
- conversationId: "",
13187
- sortKey: conversationSortKey(data, createdAt),
13188
- createdAt,
13189
- updatedAt: ms(conversation.updatedAt, createdAt),
13190
- isDeleted: false,
13191
- data
13192
- }
13193
- });
13255
+ out.records.push(
13256
+ conversationRecord(unreadableTitle ? { ...conversation, title: "" } : conversation, messages)
13257
+ );
13194
13258
  for (const message of messages) {
13195
13259
  if (message.decryptionStatus || sealed(message)) {
13196
13260
  skip("message", message.uniqueId, "unreadable");
@@ -13206,7 +13270,9 @@ async function readLocal(options) {
13206
13270
  out.records.push(prepared);
13207
13271
  }
13208
13272
  const summary = await getConversationSummaryOp(summaries, conversationId);
13209
- if (summary)
13273
+ if (summary && isEncrypted(summary.summary)) {
13274
+ skip("summary", conversationId, "unreadable");
13275
+ } else if (summary)
13210
13276
  out.records.push({
13211
13277
  record: {
13212
13278
  kind: "summary",
@@ -13309,27 +13375,6 @@ async function deletedSinceUpload(store, journal, local, signal) {
13309
13375
  };
13310
13376
  });
13311
13377
  }
13312
- function batches(records) {
13313
- const out = [];
13314
- let batch = [];
13315
- let chunks3 = 0;
13316
- let size = 0;
13317
- for (const item of records) {
13318
- const vectors = item.embeddings?.length ?? 0;
13319
- const itemSize = bytes2(item.record);
13320
- if (batch.length > 0 && (batch.length >= importBatch || chunks3 + vectors > importChunks || size + itemSize > importPlaintextBytes)) {
13321
- out.push(batch);
13322
- batch = [];
13323
- chunks3 = 0;
13324
- size = 0;
13325
- }
13326
- batch.push(item);
13327
- chunks3 += vectors;
13328
- size += itemSize;
13329
- }
13330
- if (batch.length) out.push(batch);
13331
- return out;
13332
- }
13333
13378
  function fitVectors(records, available2, unembedded) {
13334
13379
  let total = records.reduce((sum, r) => sum + (r.embeddings?.length ?? 0), 0);
13335
13380
  if (total <= available2) return records;
@@ -14410,6 +14455,45 @@ function createVaultChatBackend(options) {
14410
14455
  },
14411
14456
  flush,
14412
14457
  poll,
14458
+ async importChats(chats) {
14459
+ await ready();
14460
+ const seen = /* @__PURE__ */ new Set();
14461
+ const unique = (items) => items.filter(({ record: record2 }) => {
14462
+ const k = key2(record2.kind, record2.id);
14463
+ if (seen.has(k)) return false;
14464
+ seen.add(k);
14465
+ return true;
14466
+ });
14467
+ const valid = chats.filter((c) => validChatId(c.conversation.conversationId));
14468
+ const conversations = unique(
14469
+ valid.map((c) => conversationRecord(c.conversation, c.messages))
14470
+ );
14471
+ const messages = unique(
14472
+ valid.flatMap(
14473
+ (c) => c.messages.filter(
14474
+ (m) => m.conversationId === c.conversation.conversationId && validChatId(m.uniqueId) && Number.isSafeInteger(m.messageId)
14475
+ ).map((m) => messageRecord({ ...m, vector: void 0, chunks: void 0 }))
14476
+ )
14477
+ );
14478
+ let conversationCount = 0;
14479
+ let messageCount = 0;
14480
+ try {
14481
+ for (const batch of batches(conversations))
14482
+ conversationCount += await store.importRecords(batch);
14483
+ for (const batch of batches(messages)) messageCount += await store.importRecords(batch);
14484
+ } finally {
14485
+ for (const { record: record2 } of messages) checked.delete(key2("message", record2.id));
14486
+ await refresh(conversations.map(({ record: record2 }) => key2("conversation", record2.id))).catch(
14487
+ () => void 0
14488
+ );
14489
+ for (const { record: record2 } of conversations) threads.delete(record2.id);
14490
+ channel?.postMessage("changed");
14491
+ emit({ type: "conversations", origin: "local" });
14492
+ for (const { record: record2 } of conversations)
14493
+ emit({ type: "messages", conversationId: record2.id, origin: "local" });
14494
+ }
14495
+ return { conversations: conversationCount, messages: messageCount };
14496
+ },
14413
14497
  status: () => ({ pending: pending.size + inflight.size, ...stopped ? { stopped } : {} }),
14414
14498
  close() {
14415
14499
  closed = true;
@@ -28988,11 +29072,14 @@ var WatermelonChatStorageAdapter = class {
28988
29072
  return searchChunksOp(this.ctx, queryVector, options);
28989
29073
  }
28990
29074
  getSummary(conversationId) {
28991
- return getConversationSummaryOp(createSummaryContext(this.ctx.database), conversationId);
29075
+ return getConversationSummaryOp(
29076
+ createSummaryContext(this.ctx.database, this.ctx),
29077
+ conversationId
29078
+ );
28992
29079
  }
28993
29080
  upsertSummary(conversationId, summary, summarizedUpTo, tokenCount) {
28994
29081
  return upsertConversationSummaryOp(
28995
- createSummaryContext(this.ctx.database),
29082
+ createSummaryContext(this.ctx.database, this.ctx),
28996
29083
  conversationId,
28997
29084
  summary,
28998
29085
  summarizedUpTo,
@@ -31138,7 +31225,7 @@ async function summarizeHistoryAtRevision(options, revision2) {
31138
31225
  const cachedSummary = await summaryStore(options).getSummary(conversationId);
31139
31226
  return {
31140
31227
  messagesToConvert: messages,
31141
- summarySystemMessage: cachedSummary?.summary ? summaryToSystemMessage(cachedSummary.summary) : null
31228
+ summarySystemMessage: cachedSummary?.summary && !isEncrypted(cachedSummary.summary) ? summaryToSystemMessage(cachedSummary.summary) : null
31142
31229
  };
31143
31230
  } catch {
31144
31231
  return { messagesToConvert: messages, summarySystemMessage: null };
@@ -31193,14 +31280,15 @@ async function doSummarizeHistory(options, revision2) {
31193
31280
  } = options;
31194
31281
  try {
31195
31282
  const summaries = summaryStore(options);
31196
- const persistSummary = (summary, summarizedUpTo, tokenCount) => database.write(async (writer) => {
31283
+ const persistSummary = (summary, summarizedUpTo, tokenCount) => {
31197
31284
  assertConversationRevision(database, conversationId, revision2);
31198
- await writer.callWriter(
31199
- () => summaries.upsertSummary(conversationId, summary, summarizedUpTo, tokenCount)
31200
- );
31201
- });
31285
+ return summaries.upsertSummary(conversationId, summary, summarizedUpTo, tokenCount);
31286
+ };
31202
31287
  let cachedSummary = await summaries.getSummary(conversationId);
31203
31288
  assertConversationRevision(database, conversationId, revision2);
31289
+ if (cachedSummary && isEncrypted(cachedSummary.summary)) {
31290
+ throw new Error("The cached summary could not be decrypted");
31291
+ }
31204
31292
  const lastCompacted = lastCompactionTime.get(conversationId) ?? 0;
31205
31293
  const compactionCooledDown = Date.now() - lastCompacted > COMPACTION_COOLDOWN_MS;
31206
31294
  if (cachedSummary && cachedSummary.tokenCount > summaryTokenThreshold * MAX_SUMMARY_TOKEN_RATIO && compactionCooledDown) {