@reverbia/sdk 1.0.0-next.20260110155148 → 1.0.0-next.20260110210906

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.
@@ -42,6 +42,7 @@ __export(index_exports, {
42
42
  BACKUP_DRIVE_ROOT_FOLDER: () => DEFAULT_ROOT_FOLDER,
43
43
  BACKUP_ICLOUD_FOLDER: () => DEFAULT_BACKUP_FOLDER2,
44
44
  BackupAuthProvider: () => BackupAuthProvider,
45
+ BlobUrlManager: () => BlobUrlManager,
45
46
  ChatConversation: () => Conversation,
46
47
  ChatMessage: () => Message,
47
48
  DEFAULT_BACKUP_FOLDER: () => DEFAULT_BACKUP_FOLDER,
@@ -71,9 +72,11 @@ __export(index_exports, {
71
72
  createMemoryContextSystemMessage: () => createMemoryContextSystemMessage,
72
73
  decryptData: () => decryptData,
73
74
  decryptDataBytes: () => decryptDataBytes,
75
+ deleteEncryptedFile: () => deleteEncryptedFile,
74
76
  encryptData: () => encryptData,
75
77
  exportPublicKey: () => exportPublicKey,
76
78
  extractConversationContext: () => extractConversationContext,
79
+ fileExists: () => fileExists,
77
80
  findFileIdBySourceUrl: () => findFileIdBySourceUrl,
78
81
  formatMemoriesForChat: () => formatMemoriesForChat,
79
82
  generateCompositeKey: () => generateCompositeKey,
@@ -85,6 +88,7 @@ __export(index_exports, {
85
88
  getAndClearDriveReturnUrl: () => getAndClearDriveReturnUrl,
86
89
  getCalendarAccessToken: () => getCalendarAccessToken,
87
90
  getDriveAccessToken: () => getDriveAccessToken,
91
+ getEncryptionKey: () => getEncryptionKey,
88
92
  getGoogleDriveStoredToken: () => getGoogleDriveStoredToken,
89
93
  getValidCalendarToken: () => getValidCalendarToken,
90
94
  getValidDriveToken: () => getValidDriveToken,
@@ -99,7 +103,9 @@ __export(index_exports, {
99
103
  hasKeyPair: () => hasKeyPair,
100
104
  isCalendarCallback: () => isCalendarCallback,
101
105
  isDriveCallback: () => isDriveCallback,
106
+ isOPFSSupported: () => isOPFSSupported,
102
107
  memoryStorageSchema: () => memoryStorageSchema,
108
+ readEncryptedFile: () => readEncryptedFile,
103
109
  refreshCalendarToken: () => refreshCalendarToken,
104
110
  refreshDriveToken: () => refreshDriveToken,
105
111
  replaceUrlWithMCPPlaceholder: () => replaceUrlWithMCPPlaceholder,
@@ -137,7 +143,8 @@ __export(index_exports, {
137
143
  usePdf: () => usePdf,
138
144
  useSearch: () => useSearch,
139
145
  useSettings: () => useSettings,
140
- userPreferencesStorageSchema: () => userPreferencesStorageSchema
146
+ userPreferencesStorageSchema: () => userPreferencesStorageSchema,
147
+ writeEncryptedFile: () => writeEncryptedFile
141
148
  });
142
149
  module.exports = __toCommonJS(index_exports);
143
150
 
@@ -2829,6 +2836,178 @@ async function searchMessagesOp(ctx, queryVector, options) {
2829
2836
  return resultsWithSimilarity.sort((a, b) => b.similarity - a.similarity).slice(0, limit);
2830
2837
  }
2831
2838
 
2839
+ // src/lib/storage/opfs.ts
2840
+ var FILE_PLACEHOLDER_PREFIX = "__SDKFILE__";
2841
+ var FILE_PLACEHOLDER_SUFFIX = "__";
2842
+ var FILE_PLACEHOLDER_REGEX = /__SDKFILE__([a-f0-9-]+)__/g;
2843
+ function createFilePlaceholder(fileId) {
2844
+ return `${FILE_PLACEHOLDER_PREFIX}${fileId}${FILE_PLACEHOLDER_SUFFIX}`;
2845
+ }
2846
+ function extractFileIds(content) {
2847
+ const matches = content.matchAll(FILE_PLACEHOLDER_REGEX);
2848
+ return Array.from(matches, (m) => m[1]);
2849
+ }
2850
+ function isOPFSSupported() {
2851
+ return typeof navigator !== "undefined" && "storage" in navigator && "getDirectory" in navigator.storage;
2852
+ }
2853
+ async function getSDKDirectory() {
2854
+ const root = await navigator.storage.getDirectory();
2855
+ return root.getDirectoryHandle("reverbia-sdk-files", { create: true });
2856
+ }
2857
+ function bytesToHex2(bytes) {
2858
+ return Array.from(bytes).map((b) => b.toString(16).padStart(2, "0")).join("");
2859
+ }
2860
+ function hexToBytes2(hex) {
2861
+ const bytes = new Uint8Array(hex.length / 2);
2862
+ for (let i = 0; i < hex.length; i += 2) {
2863
+ bytes[i / 2] = parseInt(hex.slice(i, i + 2), 16);
2864
+ }
2865
+ return bytes;
2866
+ }
2867
+ async function encryptBlob(blob, encryptionKey) {
2868
+ const arrayBuffer = await blob.arrayBuffer();
2869
+ const plaintext = new Uint8Array(arrayBuffer);
2870
+ const iv = crypto.getRandomValues(new Uint8Array(12));
2871
+ const ciphertext = await crypto.subtle.encrypt(
2872
+ { name: "AES-GCM", iv },
2873
+ encryptionKey,
2874
+ plaintext
2875
+ );
2876
+ const combined = new Uint8Array(iv.length + ciphertext.byteLength);
2877
+ combined.set(iv, 0);
2878
+ combined.set(new Uint8Array(ciphertext), iv.length);
2879
+ return bytesToHex2(combined);
2880
+ }
2881
+ async function decryptToBytes(encryptedHex, encryptionKey) {
2882
+ const combined = hexToBytes2(encryptedHex);
2883
+ const iv = combined.slice(0, 12);
2884
+ const ciphertext = combined.slice(12);
2885
+ const decrypted = await crypto.subtle.decrypt(
2886
+ { name: "AES-GCM", iv },
2887
+ encryptionKey,
2888
+ ciphertext
2889
+ );
2890
+ return new Uint8Array(decrypted);
2891
+ }
2892
+ async function writeEncryptedFile(fileId, blob, encryptionKey, metadata) {
2893
+ if (!isOPFSSupported()) {
2894
+ throw new Error("OPFS is not supported in this browser");
2895
+ }
2896
+ const dir = await getSDKDirectory();
2897
+ const encryptedHex = await encryptBlob(blob, encryptionKey);
2898
+ const contentHandle = await dir.getFileHandle(`${fileId}.enc`, {
2899
+ create: true
2900
+ });
2901
+ const contentWritable = await contentHandle.createWritable();
2902
+ await contentWritable.write(encryptedHex);
2903
+ await contentWritable.close();
2904
+ const fileMetadata = {
2905
+ id: fileId,
2906
+ name: metadata?.name || `file-${fileId}`,
2907
+ type: blob.type || "application/octet-stream",
2908
+ size: blob.size,
2909
+ sourceUrl: metadata?.sourceUrl,
2910
+ createdAt: Date.now()
2911
+ };
2912
+ const metaHandle = await dir.getFileHandle(`${fileId}.meta.json`, {
2913
+ create: true
2914
+ });
2915
+ const metaWritable = await metaHandle.createWritable();
2916
+ await metaWritable.write(JSON.stringify(fileMetadata));
2917
+ await metaWritable.close();
2918
+ }
2919
+ async function readEncryptedFile(fileId, encryptionKey) {
2920
+ if (!isOPFSSupported()) {
2921
+ throw new Error("OPFS is not supported in this browser");
2922
+ }
2923
+ const dir = await getSDKDirectory();
2924
+ try {
2925
+ const contentHandle = await dir.getFileHandle(`${fileId}.enc`);
2926
+ const contentFile = await contentHandle.getFile();
2927
+ const encryptedHex = await contentFile.text();
2928
+ const metaHandle = await dir.getFileHandle(`${fileId}.meta.json`);
2929
+ const metaFile = await metaHandle.getFile();
2930
+ const metadata = JSON.parse(await metaFile.text());
2931
+ const decryptedBytes = await decryptToBytes(encryptedHex, encryptionKey);
2932
+ const blob = new Blob([decryptedBytes.buffer], { type: metadata.type });
2933
+ return { blob, metadata };
2934
+ } catch (error) {
2935
+ if (error instanceof DOMException && error.name === "NotFoundError") {
2936
+ return null;
2937
+ }
2938
+ throw error;
2939
+ }
2940
+ }
2941
+ async function deleteEncryptedFile(fileId) {
2942
+ if (!isOPFSSupported()) {
2943
+ throw new Error("OPFS is not supported in this browser");
2944
+ }
2945
+ const dir = await getSDKDirectory();
2946
+ try {
2947
+ await dir.removeEntry(`${fileId}.enc`);
2948
+ await dir.removeEntry(`${fileId}.meta.json`);
2949
+ } catch {
2950
+ }
2951
+ }
2952
+ async function fileExists(fileId) {
2953
+ if (!isOPFSSupported()) {
2954
+ return false;
2955
+ }
2956
+ const dir = await getSDKDirectory();
2957
+ try {
2958
+ await dir.getFileHandle(`${fileId}.enc`);
2959
+ return true;
2960
+ } catch {
2961
+ return false;
2962
+ }
2963
+ }
2964
+ var BlobUrlManager = class {
2965
+ constructor() {
2966
+ this.activeUrls = /* @__PURE__ */ new Map();
2967
+ }
2968
+ // fileId -> blobUrl
2969
+ /**
2970
+ * Creates a blob URL for a file and tracks it.
2971
+ */
2972
+ createUrl(fileId, blob) {
2973
+ this.revokeUrl(fileId);
2974
+ const url = URL.createObjectURL(blob);
2975
+ this.activeUrls.set(fileId, url);
2976
+ return url;
2977
+ }
2978
+ /**
2979
+ * Gets the active blob URL for a file, if any.
2980
+ */
2981
+ getUrl(fileId) {
2982
+ return this.activeUrls.get(fileId);
2983
+ }
2984
+ /**
2985
+ * Revokes a blob URL and removes it from tracking.
2986
+ */
2987
+ revokeUrl(fileId) {
2988
+ const url = this.activeUrls.get(fileId);
2989
+ if (url) {
2990
+ URL.revokeObjectURL(url);
2991
+ this.activeUrls.delete(fileId);
2992
+ }
2993
+ }
2994
+ /**
2995
+ * Revokes all tracked blob URLs.
2996
+ */
2997
+ revokeAll() {
2998
+ for (const url of this.activeUrls.values()) {
2999
+ URL.revokeObjectURL(url);
3000
+ }
3001
+ this.activeUrls.clear();
3002
+ }
3003
+ /**
3004
+ * Gets the count of active blob URLs.
3005
+ */
3006
+ get size() {
3007
+ return this.activeUrls.size;
3008
+ }
3009
+ };
3010
+
2832
3011
  // src/react/useChatStorage.ts
2833
3012
  function replaceUrlWithMCPPlaceholder(content, url, fileId) {
2834
3013
  const escapedUrl = url.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
@@ -2850,20 +3029,69 @@ function replaceUrlWithMCPPlaceholder(content, url, fileId) {
2850
3029
  function findFileIdBySourceUrl(files, sourceUrl) {
2851
3030
  return files?.find((f) => f.sourceUrl === sourceUrl)?.id;
2852
3031
  }
2853
- function storedToLlmapiMessage(stored) {
2854
- const content = [
2855
- { type: "text", text: stored.content }
2856
- ];
3032
+ async function isUrlValid(url, timeoutMs = 5e3) {
3033
+ try {
3034
+ const controller = new AbortController();
3035
+ const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
3036
+ const response = await fetch(url, {
3037
+ method: "GET",
3038
+ headers: { Range: "bytes=0-0" },
3039
+ signal: controller.signal
3040
+ });
3041
+ clearTimeout(timeoutId);
3042
+ return response.ok || response.status === 206;
3043
+ } catch {
3044
+ return false;
3045
+ }
3046
+ }
3047
+ async function storedToLlmapiMessage(stored) {
3048
+ let textContent = stored.content;
3049
+ const fileUrlMap = /* @__PURE__ */ new Map();
3050
+ const imageParts = [];
2857
3051
  if (stored.files?.length) {
2858
3052
  for (const file of stored.files) {
2859
3053
  if (file.url) {
2860
- content.push({
3054
+ imageParts.push({
2861
3055
  type: "image_url",
2862
3056
  image_url: { url: file.url }
2863
3057
  });
3058
+ } else if (file.sourceUrl) {
3059
+ const isValid = await isUrlValid(file.sourceUrl);
3060
+ if (isValid) {
3061
+ imageParts.push({
3062
+ type: "image_url",
3063
+ image_url: { url: file.sourceUrl }
3064
+ });
3065
+ fileUrlMap.set(file.id, file.sourceUrl);
3066
+ }
2864
3067
  }
2865
3068
  }
2866
3069
  }
3070
+ textContent = textContent.replace(
3071
+ /__SDKFILE__([a-f0-9-]+)__/g,
3072
+ (match, fileId) => {
3073
+ const sourceUrl = fileUrlMap.get(fileId);
3074
+ if (sourceUrl) {
3075
+ return `![image](${sourceUrl})`;
3076
+ }
3077
+ return "";
3078
+ }
3079
+ );
3080
+ textContent = textContent.replace(
3081
+ /!\[MCP_IMAGE:([a-f0-9-]+)\]/g,
3082
+ (match, fileId) => {
3083
+ const sourceUrl = fileUrlMap.get(fileId);
3084
+ if (sourceUrl) {
3085
+ return `![image](${sourceUrl})`;
3086
+ }
3087
+ return "";
3088
+ }
3089
+ );
3090
+ textContent = textContent.replace(/\n{3,}/g, "\n\n").trim();
3091
+ const content = [
3092
+ { type: "text", text: textContent },
3093
+ ...imageParts
3094
+ ];
2867
3095
  return {
2868
3096
  role: stored.role,
2869
3097
  content
@@ -2880,9 +3108,17 @@ function useChatStorage(options) {
2880
3108
  onData,
2881
3109
  onFinish,
2882
3110
  onError,
2883
- apiType
3111
+ apiType,
3112
+ walletAddress
2884
3113
  } = options;
2885
3114
  const [currentConversationId, setCurrentConversationId] = (0, import_react2.useState)(initialConversationId || null);
3115
+ const blobManagerRef = (0, import_react2.useRef)(new BlobUrlManager());
3116
+ (0, import_react2.useEffect)(() => {
3117
+ const manager = blobManagerRef.current;
3118
+ return () => {
3119
+ manager.revokeAll();
3120
+ };
3121
+ }, []);
2886
3122
  const messagesCollection = (0, import_react2.useMemo)(
2887
3123
  () => database.get("history"),
2888
3124
  [database]
@@ -2950,9 +3186,49 @@ function useChatStorage(options) {
2950
3186
  );
2951
3187
  const getMessages = (0, import_react2.useCallback)(
2952
3188
  async (convId) => {
2953
- return getMessagesOp(storageCtx, convId);
3189
+ const messages = await getMessagesOp(storageCtx, convId);
3190
+ if (walletAddress && hasEncryptionKey(walletAddress) && isOPFSSupported()) {
3191
+ try {
3192
+ const encryptionKey = await getEncryptionKey(walletAddress);
3193
+ const blobManager = blobManagerRef.current;
3194
+ const resolvedMessages = await Promise.all(
3195
+ messages.map(async (msg) => {
3196
+ const fileIds = extractFileIds(msg.content);
3197
+ if (fileIds.length === 0) {
3198
+ return msg;
3199
+ }
3200
+ let resolvedContent = msg.content;
3201
+ for (const fileId of fileIds) {
3202
+ let url = blobManager.getUrl(fileId);
3203
+ if (!url) {
3204
+ const result = await readEncryptedFile(fileId, encryptionKey);
3205
+ if (result) {
3206
+ url = blobManager.createUrl(fileId, result.blob);
3207
+ }
3208
+ }
3209
+ if (url) {
3210
+ const placeholder = createFilePlaceholder(fileId);
3211
+ resolvedContent = resolvedContent.replace(
3212
+ new RegExp(
3213
+ placeholder.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"),
3214
+ "g"
3215
+ ),
3216
+ `![image](${url})`
3217
+ );
3218
+ }
3219
+ }
3220
+ return { ...msg, content: resolvedContent };
3221
+ })
3222
+ );
3223
+ return resolvedMessages;
3224
+ } catch (error) {
3225
+ console.error("[useChatStorage] Failed to resolve file placeholders:", error);
3226
+ return messages;
3227
+ }
3228
+ }
3229
+ return messages;
2954
3230
  },
2955
- [storageCtx]
3231
+ [storageCtx, walletAddress]
2956
3232
  );
2957
3233
  const getMessageCount = (0, import_react2.useCallback)(
2958
3234
  async (convId) => {
@@ -3176,6 +3452,89 @@ function useChatStorage(options) {
3176
3452
  },
3177
3453
  []
3178
3454
  );
3455
+ const extractAndStoreEncryptedMCPImages = (0, import_react2.useCallback)(
3456
+ async (content, address) => {
3457
+ try {
3458
+ if (!isOPFSSupported()) {
3459
+ console.warn("[extractAndStoreEncryptedMCPImages] OPFS not supported");
3460
+ return { processedFiles: [], cleanedContent: content };
3461
+ }
3462
+ if (!hasEncryptionKey(address)) {
3463
+ console.warn("[extractAndStoreEncryptedMCPImages] Encryption key not available");
3464
+ return { processedFiles: [], cleanedContent: content };
3465
+ }
3466
+ const MCP_IMAGE_URL_PATTERN = new RegExp(
3467
+ `https://${MCP_R2_DOMAIN.replace(/\./g, "\\.")}[^\\s)]*`,
3468
+ "g"
3469
+ );
3470
+ const urlMatches = content.match(MCP_IMAGE_URL_PATTERN);
3471
+ if (!urlMatches || urlMatches.length === 0) {
3472
+ return { processedFiles: [], cleanedContent: content };
3473
+ }
3474
+ const uniqueUrls = [...new Set(urlMatches)];
3475
+ const encryptionKey = await getEncryptionKey(address);
3476
+ const processedFiles = [];
3477
+ let cleanedContent = content;
3478
+ const results = await Promise.allSettled(
3479
+ uniqueUrls.map(async (imageUrl) => {
3480
+ const controller = new AbortController();
3481
+ const timeoutId = setTimeout(() => controller.abort(), 3e4);
3482
+ try {
3483
+ const response = await fetch(imageUrl, {
3484
+ signal: controller.signal,
3485
+ cache: "no-store"
3486
+ });
3487
+ if (!response.ok) {
3488
+ throw new Error(`Failed to fetch image: ${response.status}`);
3489
+ }
3490
+ const blob = await response.blob();
3491
+ const fileId = crypto.randomUUID();
3492
+ const urlPath = imageUrl.split("?")[0] ?? imageUrl;
3493
+ const extension = urlPath.match(/\.([a-zA-Z0-9]+)$/)?.[1] || "png";
3494
+ const mimeType = blob.type || `image/${extension}`;
3495
+ const fileName = `mcp-image-${Date.now()}-${fileId.slice(0, 8)}.${extension}`;
3496
+ await writeEncryptedFile(fileId, blob, encryptionKey, {
3497
+ name: fileName,
3498
+ sourceUrl: imageUrl
3499
+ });
3500
+ return { fileId, fileName, mimeType, size: blob.size, imageUrl };
3501
+ } finally {
3502
+ clearTimeout(timeoutId);
3503
+ }
3504
+ })
3505
+ );
3506
+ results.forEach((result, i) => {
3507
+ const imageUrl = uniqueUrls[i];
3508
+ if (result.status === "fulfilled") {
3509
+ const { fileId, fileName, mimeType, size } = result.value;
3510
+ processedFiles.push({
3511
+ id: fileId,
3512
+ name: fileName,
3513
+ type: mimeType,
3514
+ size,
3515
+ sourceUrl: imageUrl
3516
+ });
3517
+ const placeholder = createFilePlaceholder(fileId);
3518
+ const escapedUrl = imageUrl.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
3519
+ const markdownImagePattern = new RegExp(
3520
+ `!\\[[^\\]]*\\]\\([\\s]*${escapedUrl}[\\s]*\\)`,
3521
+ "g"
3522
+ );
3523
+ cleanedContent = cleanedContent.replace(markdownImagePattern, placeholder);
3524
+ cleanedContent = cleanedContent.replace(new RegExp(escapedUrl, "g"), placeholder);
3525
+ } else {
3526
+ console.error("[extractAndStoreEncryptedMCPImages] Failed:", result.reason);
3527
+ }
3528
+ });
3529
+ cleanedContent = cleanedContent.replace(/\n{3,}/g, "\n\n").trim();
3530
+ return { processedFiles, cleanedContent };
3531
+ } catch (err) {
3532
+ console.error("[extractAndStoreEncryptedMCPImages] Unexpected error:", err);
3533
+ return { processedFiles: [], cleanedContent: content };
3534
+ }
3535
+ },
3536
+ []
3537
+ );
3179
3538
  const sendMessage = (0, import_react2.useCallback)(
3180
3539
  async (args) => {
3181
3540
  const {
@@ -3227,10 +3586,10 @@ function useChatStorage(options) {
3227
3586
  const storedMessages = await getMessages(convId);
3228
3587
  const validMessages = storedMessages.filter((msg) => !msg.error);
3229
3588
  const limitedMessages = validMessages.slice(-maxHistoryMessages);
3230
- messagesToSend = [
3231
- ...limitedMessages.map(storedToLlmapiMessage),
3232
- ...messages
3233
- ];
3589
+ const historyMessages = await Promise.all(
3590
+ limitedMessages.map(storedToLlmapiMessage)
3591
+ );
3592
+ messagesToSend = [...historyMessages, ...messages];
3234
3593
  } else {
3235
3594
  messagesToSend = [...messages];
3236
3595
  }
@@ -3368,7 +3727,14 @@ function useChatStorage(options) {
3368
3727
  let cleanedContent = assistantContent.replace(jsonSourcesBlockRegex, "").trim();
3369
3728
  cleanedContent = cleanedContent.replace(/\n{3,}/g, "\n\n");
3370
3729
  let processedFiles = [];
3371
- if (writeFile) {
3730
+ if (walletAddress) {
3731
+ const result2 = await extractAndStoreEncryptedMCPImages(
3732
+ cleanedContent,
3733
+ walletAddress
3734
+ );
3735
+ processedFiles = result2.processedFiles;
3736
+ cleanedContent = result2.cleanedContent;
3737
+ } else if (writeFile) {
3372
3738
  const result2 = await extractAndStoreMCPImages(
3373
3739
  cleanedContent,
3374
3740
  writeFile
@@ -3404,7 +3770,7 @@ function useChatStorage(options) {
3404
3770
  assistantMessage: storedAssistantMessage
3405
3771
  };
3406
3772
  },
3407
- [ensureConversation, getMessages, storageCtx, baseSendMessage]
3773
+ [ensureConversation, getMessages, storageCtx, baseSendMessage, walletAddress, extractAndStoreEncryptedMCPImages]
3408
3774
  );
3409
3775
  const searchMessages = (0, import_react2.useCallback)(
3410
3776
  async (queryVector, options2) => {
@@ -9179,6 +9545,7 @@ function hasDriveCredentials() {
9179
9545
  BACKUP_DRIVE_ROOT_FOLDER,
9180
9546
  BACKUP_ICLOUD_FOLDER,
9181
9547
  BackupAuthProvider,
9548
+ BlobUrlManager,
9182
9549
  ChatConversation,
9183
9550
  ChatMessage,
9184
9551
  DEFAULT_BACKUP_FOLDER,
@@ -9208,9 +9575,11 @@ function hasDriveCredentials() {
9208
9575
  createMemoryContextSystemMessage,
9209
9576
  decryptData,
9210
9577
  decryptDataBytes,
9578
+ deleteEncryptedFile,
9211
9579
  encryptData,
9212
9580
  exportPublicKey,
9213
9581
  extractConversationContext,
9582
+ fileExists,
9214
9583
  findFileIdBySourceUrl,
9215
9584
  formatMemoriesForChat,
9216
9585
  generateCompositeKey,
@@ -9222,6 +9591,7 @@ function hasDriveCredentials() {
9222
9591
  getAndClearDriveReturnUrl,
9223
9592
  getCalendarAccessToken,
9224
9593
  getDriveAccessToken,
9594
+ getEncryptionKey,
9225
9595
  getGoogleDriveStoredToken,
9226
9596
  getValidCalendarToken,
9227
9597
  getValidDriveToken,
@@ -9236,7 +9606,9 @@ function hasDriveCredentials() {
9236
9606
  hasKeyPair,
9237
9607
  isCalendarCallback,
9238
9608
  isDriveCallback,
9609
+ isOPFSSupported,
9239
9610
  memoryStorageSchema,
9611
+ readEncryptedFile,
9240
9612
  refreshCalendarToken,
9241
9613
  refreshDriveToken,
9242
9614
  replaceUrlWithMCPPlaceholder,
@@ -9274,5 +9646,6 @@ function hasDriveCredentials() {
9274
9646
  usePdf,
9275
9647
  useSearch,
9276
9648
  useSettings,
9277
- userPreferencesStorageSchema
9649
+ userPreferencesStorageSchema,
9650
+ writeEncryptedFile
9278
9651
  });
@@ -1055,6 +1055,15 @@ declare function clearEncryptionKey(address: string): void;
1055
1055
  * Clears all encryption keys from memory
1056
1056
  */
1057
1057
  declare function clearAllEncryptionKeys(): void;
1058
+ /**
1059
+ * Gets the encryption key from in-memory storage and imports it as a CryptoKey.
1060
+ * The key must have been previously requested via requestEncryptionKey.
1061
+ *
1062
+ * @param address - The wallet address
1063
+ * @returns The CryptoKey for AES-GCM encryption/decryption
1064
+ * @throws Error if the key hasn't been requested yet
1065
+ */
1066
+ declare function getEncryptionKey(address: string): Promise<CryptoKey>;
1058
1067
  /**
1059
1068
  * Encrypts data using AES-GCM with the stored encryption key.
1060
1069
  *
@@ -1816,6 +1825,18 @@ interface UseChatStorageOptions extends BaseUseChatStorageOptions {
1816
1825
  * - "completions": OpenAI Chat Completions API (wider model compatibility)
1817
1826
  */
1818
1827
  apiType?: ApiType;
1828
+ /**
1829
+ * Wallet address for encrypted file storage.
1830
+ * When provided, MCP-generated images are automatically encrypted and stored
1831
+ * in OPFS using wallet-derived keys. Messages are returned with working blob URLs.
1832
+ *
1833
+ * Requires:
1834
+ * - OPFS browser support
1835
+ * - Encryption key to be requested via `requestEncryptionKey` first
1836
+ *
1837
+ * When not provided, falls back to the `writeFile` callback in sendMessage args.
1838
+ */
1839
+ walletAddress?: string;
1819
1840
  }
1820
1841
  /**
1821
1842
  * Arguments for sendMessage with storage (React version)
@@ -1982,6 +2003,84 @@ interface UseChatStorageResult extends BaseUseChatStorageResult {
1982
2003
  */
1983
2004
  declare function useChatStorage(options: UseChatStorageOptions): UseChatStorageResult;
1984
2005
 
2006
+ /**
2007
+ * Checks if the browser supports OPFS.
2008
+ */
2009
+ declare function isOPFSSupported(): boolean;
2010
+ /**
2011
+ * File metadata stored alongside encrypted content.
2012
+ */
2013
+ interface StoredFileMetadata {
2014
+ id: string;
2015
+ name: string;
2016
+ type: string;
2017
+ size: number;
2018
+ sourceUrl?: string;
2019
+ createdAt: number;
2020
+ }
2021
+ /**
2022
+ * Writes an encrypted file to OPFS.
2023
+ *
2024
+ * @param fileId - Unique identifier for the file
2025
+ * @param blob - The file content
2026
+ * @param encryptionKey - CryptoKey for encryption
2027
+ * @param metadata - Optional metadata (name, type, sourceUrl)
2028
+ */
2029
+ declare function writeEncryptedFile(fileId: string, blob: Blob, encryptionKey: CryptoKey, metadata?: {
2030
+ name?: string;
2031
+ sourceUrl?: string;
2032
+ }): Promise<void>;
2033
+ /**
2034
+ * Reads and decrypts a file from OPFS.
2035
+ *
2036
+ * @param fileId - The file identifier
2037
+ * @param encryptionKey - CryptoKey for decryption
2038
+ * @returns The decrypted blob, or null if not found
2039
+ */
2040
+ declare function readEncryptedFile(fileId: string, encryptionKey: CryptoKey): Promise<{
2041
+ blob: Blob;
2042
+ metadata: StoredFileMetadata;
2043
+ } | null>;
2044
+ /**
2045
+ * Deletes a file from OPFS.
2046
+ *
2047
+ * @param fileId - The file identifier
2048
+ */
2049
+ declare function deleteEncryptedFile(fileId: string): Promise<void>;
2050
+ /**
2051
+ * Checks if a file exists in OPFS.
2052
+ *
2053
+ * @param fileId - The file identifier
2054
+ */
2055
+ declare function fileExists(fileId: string): Promise<boolean>;
2056
+ /**
2057
+ * Manager for blob URLs to prevent memory leaks.
2058
+ * Tracks active blob URLs and provides cleanup functionality.
2059
+ */
2060
+ declare class BlobUrlManager {
2061
+ private activeUrls;
2062
+ /**
2063
+ * Creates a blob URL for a file and tracks it.
2064
+ */
2065
+ createUrl(fileId: string, blob: Blob): string;
2066
+ /**
2067
+ * Gets the active blob URL for a file, if any.
2068
+ */
2069
+ getUrl(fileId: string): string | undefined;
2070
+ /**
2071
+ * Revokes a blob URL and removes it from tracking.
2072
+ */
2073
+ revokeUrl(fileId: string): void;
2074
+ /**
2075
+ * Revokes all tracked blob URLs.
2076
+ */
2077
+ revokeAll(): void;
2078
+ /**
2079
+ * Gets the count of active blob URLs.
2080
+ */
2081
+ get size(): number;
2082
+ }
2083
+
1985
2084
  /**
1986
2085
  * Combined WatermelonDB schema for all SDK storage modules.
1987
2086
  *
@@ -3801,4 +3900,4 @@ declare function storeDriveToken(accessToken: string, expiresIn?: number, refres
3801
3900
  */
3802
3901
  declare function hasDriveCredentials(): boolean;
3803
3902
 
3804
- export { DEFAULT_CONVERSATIONS_FOLDER as BACKUP_DRIVE_CONVERSATIONS_FOLDER, DEFAULT_ROOT_FOLDER as BACKUP_DRIVE_ROOT_FOLDER, DEFAULT_BACKUP_FOLDER as BACKUP_ICLOUD_FOLDER, type BackupAuthContextValue, BackupAuthProvider, type BackupAuthProviderProps, type BackupOperationOptions, Conversation as ChatConversation, Message as ChatMessage, type ChatRole, type CreateConversationOptions, type CreateMemoryOptions, type CreateMessageOptions, type CreateModelPreferenceOptions, type CreateUserPreferenceOptions, DEFAULT_BACKUP_FOLDER$1 as DEFAULT_BACKUP_FOLDER, DEFAULT_CONVERSATIONS_FOLDER as DEFAULT_DRIVE_CONVERSATIONS_FOLDER, DEFAULT_ROOT_FOLDER as DEFAULT_DRIVE_ROOT_FOLDER, DEFAULT_BACKUP_FOLDER$1 as DEFAULT_DROPBOX_FOLDER, DEFAULT_BACKUP_FOLDER as DEFAULT_ICLOUD_BACKUP_FOLDER, DEFAULT_PERSONALITY_SETTINGS, type DropboxAuthContextValue, DropboxAuthProvider, type DropboxAuthProviderProps, type DropboxExportResult, type DropboxImportResult, type EmbeddedWalletSignerFn, type FileMetadata, type GoogleDriveAuthContextValue, GoogleDriveAuthProvider, type GoogleDriveAuthProviderProps, type GoogleDriveExportResult, type GoogleDriveImportResult, type ICloudAuthContextValue, ICloudAuthProvider, type ICloudAuthProviderProps, type ICloudExportResult, type ICloudImportResult, type MemoryItem, type MemoryType, type OCRFile, type PdfFile, type PersonalitySettings, type PersonalitySliders, type PersonalityStyle, type ProfileUpdate, type ProgressCallback, type ProviderAuthState, type ProviderBackupState, SLIDER_CONFIG, type SearchMessagesOptions, type SearchSource, type SendMessageWithStorageArgs, type SendMessageWithStorageResult, type SignMessageFn, type ChatCompletionUsage as StoredChatCompletionUsage, type StoredConversation, type StoredMemory, Memory as StoredMemoryModel, type StoredMemoryWithSimilarity, type StoredMessage, type StoredMessageWithSimilarity, type StoredModelPreference, ModelPreference as StoredModelPreferenceModel, type StoredUserPreference, UserPreference as StoredUserPreferenceModel, type UpdateMemoryOptions, type UpdateModelPreferenceOptions, type UpdateUserPreferenceOptions, type UseBackupOptions, type UseBackupResult, type UseChatStorageOptions, type UseChatStorageResult, type UseDropboxBackupOptions, type UseDropboxBackupResult, type UseEncryptionResult, type UseGoogleDriveBackupOptions, type UseGoogleDriveBackupResult, type UseICloudBackupOptions, type UseICloudBackupResult, type UseImageGenerationResult, type UseMemoryStorageOptions, type UseMemoryStorageResult, type UseModelsResult, type UseOCRResult, type UsePdfResult, type UseSearchResult, type UseSettingsOptions, type UseSettingsResult, chatStorageMigrations, chatStorageSchema, clearAllEncryptionKeys, clearAllKeyPairs, clearCalendarToken, clearDriveToken, clearToken as clearDropboxToken, clearEncryptionKey, clearGoogleDriveToken, clearICloudAuth, clearKeyPair, createMemoryContextSystemMessage, decryptData, decryptDataBytes, encryptData, exportPublicKey, extractConversationContext, findFileIdBySourceUrl, formatMemoriesForChat, generateCompositeKey, generateConversationId, generateUniqueKey, getAndClearCalendarPendingMessage, getAndClearCalendarReturnUrl, getAndClearDrivePendingMessage, getAndClearDriveReturnUrl, getCalendarAccessToken, getDriveAccessToken, getGoogleDriveStoredToken, getValidCalendarToken, getValidDriveToken, handleCalendarCallback, handleDriveCallback, hasCalendarCredentials, hasDriveCredentials, hasDropboxCredentials, hasEncryptionKey, hasGoogleDriveCredentials, hasICloudCredentials, hasKeyPair, isCalendarCallback, isDriveCallback, memoryStorageSchema, refreshCalendarToken, refreshDriveToken, replaceUrlWithMCPPlaceholder, requestEncryptionKey, requestKeyPair, revokeCalendarToken, revokeDriveToken, sdkMigrations, sdkModelClasses, sdkSchema, settingsStorageSchema, startCalendarAuth, startDriveAuth, storeCalendarPendingMessage, storeCalendarReturnUrl, storeCalendarToken, storeDrivePendingMessage, storeDriveReturnUrl, storeDriveToken, useBackup, useBackupAuth, useChat, useChatStorage, useDropboxAuth, useDropboxBackup, useEncryption, useGoogleDriveAuth, useGoogleDriveBackup, useICloudAuth, useICloudBackup, useImageGeneration, useMemoryStorage, useModels, useOCR, usePdf, useSearch, useSettings, userPreferencesStorageSchema };
3903
+ export { DEFAULT_CONVERSATIONS_FOLDER as BACKUP_DRIVE_CONVERSATIONS_FOLDER, DEFAULT_ROOT_FOLDER as BACKUP_DRIVE_ROOT_FOLDER, DEFAULT_BACKUP_FOLDER as BACKUP_ICLOUD_FOLDER, type BackupAuthContextValue, BackupAuthProvider, type BackupAuthProviderProps, type BackupOperationOptions, BlobUrlManager, Conversation as ChatConversation, Message as ChatMessage, type ChatRole, type CreateConversationOptions, type CreateMemoryOptions, type CreateMessageOptions, type CreateModelPreferenceOptions, type CreateUserPreferenceOptions, DEFAULT_BACKUP_FOLDER$1 as DEFAULT_BACKUP_FOLDER, DEFAULT_CONVERSATIONS_FOLDER as DEFAULT_DRIVE_CONVERSATIONS_FOLDER, DEFAULT_ROOT_FOLDER as DEFAULT_DRIVE_ROOT_FOLDER, DEFAULT_BACKUP_FOLDER$1 as DEFAULT_DROPBOX_FOLDER, DEFAULT_BACKUP_FOLDER as DEFAULT_ICLOUD_BACKUP_FOLDER, DEFAULT_PERSONALITY_SETTINGS, type DropboxAuthContextValue, DropboxAuthProvider, type DropboxAuthProviderProps, type DropboxExportResult, type DropboxImportResult, type EmbeddedWalletSignerFn, type FileMetadata, type GoogleDriveAuthContextValue, GoogleDriveAuthProvider, type GoogleDriveAuthProviderProps, type GoogleDriveExportResult, type GoogleDriveImportResult, type ICloudAuthContextValue, ICloudAuthProvider, type ICloudAuthProviderProps, type ICloudExportResult, type ICloudImportResult, type MemoryItem, type MemoryType, type OCRFile, type PdfFile, type PersonalitySettings, type PersonalitySliders, type PersonalityStyle, type ProfileUpdate, type ProgressCallback, type ProviderAuthState, type ProviderBackupState, SLIDER_CONFIG, type SearchMessagesOptions, type SearchSource, type SendMessageWithStorageArgs, type SendMessageWithStorageResult, type SignMessageFn, type ChatCompletionUsage as StoredChatCompletionUsage, type StoredConversation, type StoredMemory, Memory as StoredMemoryModel, type StoredMemoryWithSimilarity, type StoredMessage, type StoredMessageWithSimilarity, type StoredModelPreference, ModelPreference as StoredModelPreferenceModel, type StoredUserPreference, UserPreference as StoredUserPreferenceModel, type UpdateMemoryOptions, type UpdateModelPreferenceOptions, type UpdateUserPreferenceOptions, type UseBackupOptions, type UseBackupResult, type UseChatStorageOptions, type UseChatStorageResult, type UseDropboxBackupOptions, type UseDropboxBackupResult, type UseEncryptionResult, type UseGoogleDriveBackupOptions, type UseGoogleDriveBackupResult, type UseICloudBackupOptions, type UseICloudBackupResult, type UseImageGenerationResult, type UseMemoryStorageOptions, type UseMemoryStorageResult, type UseModelsResult, type UseOCRResult, type UsePdfResult, type UseSearchResult, type UseSettingsOptions, type UseSettingsResult, chatStorageMigrations, chatStorageSchema, clearAllEncryptionKeys, clearAllKeyPairs, clearCalendarToken, clearDriveToken, clearToken as clearDropboxToken, clearEncryptionKey, clearGoogleDriveToken, clearICloudAuth, clearKeyPair, createMemoryContextSystemMessage, decryptData, decryptDataBytes, deleteEncryptedFile, encryptData, exportPublicKey, extractConversationContext, fileExists, findFileIdBySourceUrl, formatMemoriesForChat, generateCompositeKey, generateConversationId, generateUniqueKey, getAndClearCalendarPendingMessage, getAndClearCalendarReturnUrl, getAndClearDrivePendingMessage, getAndClearDriveReturnUrl, getCalendarAccessToken, getDriveAccessToken, getEncryptionKey, getGoogleDriveStoredToken, getValidCalendarToken, getValidDriveToken, handleCalendarCallback, handleDriveCallback, hasCalendarCredentials, hasDriveCredentials, hasDropboxCredentials, hasEncryptionKey, hasGoogleDriveCredentials, hasICloudCredentials, hasKeyPair, isCalendarCallback, isDriveCallback, isOPFSSupported, memoryStorageSchema, readEncryptedFile, refreshCalendarToken, refreshDriveToken, replaceUrlWithMCPPlaceholder, requestEncryptionKey, requestKeyPair, revokeCalendarToken, revokeDriveToken, sdkMigrations, sdkModelClasses, sdkSchema, settingsStorageSchema, startCalendarAuth, startDriveAuth, storeCalendarPendingMessage, storeCalendarReturnUrl, storeCalendarToken, storeDrivePendingMessage, storeDriveReturnUrl, storeDriveToken, useBackup, useBackupAuth, useChat, useChatStorage, useDropboxAuth, useDropboxBackup, useEncryption, useGoogleDriveAuth, useGoogleDriveBackup, useICloudAuth, useICloudBackup, useImageGeneration, useMemoryStorage, useModels, useOCR, usePdf, useSearch, useSettings, userPreferencesStorageSchema, writeEncryptedFile };