@reverbia/sdk 1.0.0-next.20260110032702 → 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.
@@ -2255,7 +2255,7 @@ function useEncryption(signMessage, embeddedWalletSigner) {
2255
2255
  }
2256
2256
 
2257
2257
  // src/react/useChatStorage.ts
2258
- import { useCallback as useCallback2, useState as useState2, useMemo } from "react";
2258
+ import { useCallback as useCallback2, useState as useState2, useMemo, useRef as useRef2, useEffect as useEffect2 } from "react";
2259
2259
 
2260
2260
  // src/lib/db/chat/schema.ts
2261
2261
  import { appSchema, tableSchema } from "@nozbe/watermelondb";
@@ -2700,21 +2700,262 @@ async function searchMessagesOp(ctx, queryVector, options) {
2700
2700
  return resultsWithSimilarity.sort((a, b) => b.similarity - a.similarity).slice(0, limit);
2701
2701
  }
2702
2702
 
2703
+ // src/lib/storage/opfs.ts
2704
+ var FILE_PLACEHOLDER_PREFIX = "__SDKFILE__";
2705
+ var FILE_PLACEHOLDER_SUFFIX = "__";
2706
+ var FILE_PLACEHOLDER_REGEX = /__SDKFILE__([a-f0-9-]+)__/g;
2707
+ function createFilePlaceholder(fileId) {
2708
+ return `${FILE_PLACEHOLDER_PREFIX}${fileId}${FILE_PLACEHOLDER_SUFFIX}`;
2709
+ }
2710
+ function extractFileIds(content) {
2711
+ const matches = content.matchAll(FILE_PLACEHOLDER_REGEX);
2712
+ return Array.from(matches, (m) => m[1]);
2713
+ }
2714
+ function isOPFSSupported() {
2715
+ return typeof navigator !== "undefined" && "storage" in navigator && "getDirectory" in navigator.storage;
2716
+ }
2717
+ async function getSDKDirectory() {
2718
+ const root = await navigator.storage.getDirectory();
2719
+ return root.getDirectoryHandle("reverbia-sdk-files", { create: true });
2720
+ }
2721
+ function bytesToHex2(bytes) {
2722
+ return Array.from(bytes).map((b) => b.toString(16).padStart(2, "0")).join("");
2723
+ }
2724
+ function hexToBytes2(hex) {
2725
+ const bytes = new Uint8Array(hex.length / 2);
2726
+ for (let i = 0; i < hex.length; i += 2) {
2727
+ bytes[i / 2] = parseInt(hex.slice(i, i + 2), 16);
2728
+ }
2729
+ return bytes;
2730
+ }
2731
+ async function encryptBlob(blob, encryptionKey) {
2732
+ const arrayBuffer = await blob.arrayBuffer();
2733
+ const plaintext = new Uint8Array(arrayBuffer);
2734
+ const iv = crypto.getRandomValues(new Uint8Array(12));
2735
+ const ciphertext = await crypto.subtle.encrypt(
2736
+ { name: "AES-GCM", iv },
2737
+ encryptionKey,
2738
+ plaintext
2739
+ );
2740
+ const combined = new Uint8Array(iv.length + ciphertext.byteLength);
2741
+ combined.set(iv, 0);
2742
+ combined.set(new Uint8Array(ciphertext), iv.length);
2743
+ return bytesToHex2(combined);
2744
+ }
2745
+ async function decryptToBytes(encryptedHex, encryptionKey) {
2746
+ const combined = hexToBytes2(encryptedHex);
2747
+ const iv = combined.slice(0, 12);
2748
+ const ciphertext = combined.slice(12);
2749
+ const decrypted = await crypto.subtle.decrypt(
2750
+ { name: "AES-GCM", iv },
2751
+ encryptionKey,
2752
+ ciphertext
2753
+ );
2754
+ return new Uint8Array(decrypted);
2755
+ }
2756
+ async function writeEncryptedFile(fileId, blob, encryptionKey, metadata) {
2757
+ if (!isOPFSSupported()) {
2758
+ throw new Error("OPFS is not supported in this browser");
2759
+ }
2760
+ const dir = await getSDKDirectory();
2761
+ const encryptedHex = await encryptBlob(blob, encryptionKey);
2762
+ const contentHandle = await dir.getFileHandle(`${fileId}.enc`, {
2763
+ create: true
2764
+ });
2765
+ const contentWritable = await contentHandle.createWritable();
2766
+ await contentWritable.write(encryptedHex);
2767
+ await contentWritable.close();
2768
+ const fileMetadata = {
2769
+ id: fileId,
2770
+ name: metadata?.name || `file-${fileId}`,
2771
+ type: blob.type || "application/octet-stream",
2772
+ size: blob.size,
2773
+ sourceUrl: metadata?.sourceUrl,
2774
+ createdAt: Date.now()
2775
+ };
2776
+ const metaHandle = await dir.getFileHandle(`${fileId}.meta.json`, {
2777
+ create: true
2778
+ });
2779
+ const metaWritable = await metaHandle.createWritable();
2780
+ await metaWritable.write(JSON.stringify(fileMetadata));
2781
+ await metaWritable.close();
2782
+ }
2783
+ async function readEncryptedFile(fileId, encryptionKey) {
2784
+ if (!isOPFSSupported()) {
2785
+ throw new Error("OPFS is not supported in this browser");
2786
+ }
2787
+ const dir = await getSDKDirectory();
2788
+ try {
2789
+ const contentHandle = await dir.getFileHandle(`${fileId}.enc`);
2790
+ const contentFile = await contentHandle.getFile();
2791
+ const encryptedHex = await contentFile.text();
2792
+ const metaHandle = await dir.getFileHandle(`${fileId}.meta.json`);
2793
+ const metaFile = await metaHandle.getFile();
2794
+ const metadata = JSON.parse(await metaFile.text());
2795
+ const decryptedBytes = await decryptToBytes(encryptedHex, encryptionKey);
2796
+ const blob = new Blob([decryptedBytes.buffer], { type: metadata.type });
2797
+ return { blob, metadata };
2798
+ } catch (error) {
2799
+ if (error instanceof DOMException && error.name === "NotFoundError") {
2800
+ return null;
2801
+ }
2802
+ throw error;
2803
+ }
2804
+ }
2805
+ async function deleteEncryptedFile(fileId) {
2806
+ if (!isOPFSSupported()) {
2807
+ throw new Error("OPFS is not supported in this browser");
2808
+ }
2809
+ const dir = await getSDKDirectory();
2810
+ try {
2811
+ await dir.removeEntry(`${fileId}.enc`);
2812
+ await dir.removeEntry(`${fileId}.meta.json`);
2813
+ } catch {
2814
+ }
2815
+ }
2816
+ async function fileExists(fileId) {
2817
+ if (!isOPFSSupported()) {
2818
+ return false;
2819
+ }
2820
+ const dir = await getSDKDirectory();
2821
+ try {
2822
+ await dir.getFileHandle(`${fileId}.enc`);
2823
+ return true;
2824
+ } catch {
2825
+ return false;
2826
+ }
2827
+ }
2828
+ var BlobUrlManager = class {
2829
+ constructor() {
2830
+ this.activeUrls = /* @__PURE__ */ new Map();
2831
+ }
2832
+ // fileId -> blobUrl
2833
+ /**
2834
+ * Creates a blob URL for a file and tracks it.
2835
+ */
2836
+ createUrl(fileId, blob) {
2837
+ this.revokeUrl(fileId);
2838
+ const url = URL.createObjectURL(blob);
2839
+ this.activeUrls.set(fileId, url);
2840
+ return url;
2841
+ }
2842
+ /**
2843
+ * Gets the active blob URL for a file, if any.
2844
+ */
2845
+ getUrl(fileId) {
2846
+ return this.activeUrls.get(fileId);
2847
+ }
2848
+ /**
2849
+ * Revokes a blob URL and removes it from tracking.
2850
+ */
2851
+ revokeUrl(fileId) {
2852
+ const url = this.activeUrls.get(fileId);
2853
+ if (url) {
2854
+ URL.revokeObjectURL(url);
2855
+ this.activeUrls.delete(fileId);
2856
+ }
2857
+ }
2858
+ /**
2859
+ * Revokes all tracked blob URLs.
2860
+ */
2861
+ revokeAll() {
2862
+ for (const url of this.activeUrls.values()) {
2863
+ URL.revokeObjectURL(url);
2864
+ }
2865
+ this.activeUrls.clear();
2866
+ }
2867
+ /**
2868
+ * Gets the count of active blob URLs.
2869
+ */
2870
+ get size() {
2871
+ return this.activeUrls.size;
2872
+ }
2873
+ };
2874
+
2703
2875
  // src/react/useChatStorage.ts
2704
- function storedToLlmapiMessage(stored) {
2705
- const content = [
2706
- { type: "text", text: stored.content }
2707
- ];
2876
+ function replaceUrlWithMCPPlaceholder(content, url, fileId) {
2877
+ const escapedUrl = url.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
2878
+ const placeholder = `![MCP_IMAGE:${fileId}]`;
2879
+ let result = content;
2880
+ const markdownImagePattern = new RegExp(
2881
+ `!\\[[^\\]]*\\]\\([\\s]*${escapedUrl}[\\s]*\\)`,
2882
+ "g"
2883
+ );
2884
+ result = result.replace(markdownImagePattern, placeholder);
2885
+ result = result.replace(new RegExp(escapedUrl, "g"), placeholder);
2886
+ const orphanedMarkdownPattern = new RegExp(
2887
+ `!\\[[^\\]]*\\]\\([\\s]*\\!\\[MCP_IMAGE:${fileId}\\][\\s]*\\)`,
2888
+ "g"
2889
+ );
2890
+ result = result.replace(orphanedMarkdownPattern, placeholder);
2891
+ return result;
2892
+ }
2893
+ function findFileIdBySourceUrl(files, sourceUrl) {
2894
+ return files?.find((f) => f.sourceUrl === sourceUrl)?.id;
2895
+ }
2896
+ async function isUrlValid(url, timeoutMs = 5e3) {
2897
+ try {
2898
+ const controller = new AbortController();
2899
+ const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
2900
+ const response = await fetch(url, {
2901
+ method: "GET",
2902
+ headers: { Range: "bytes=0-0" },
2903
+ signal: controller.signal
2904
+ });
2905
+ clearTimeout(timeoutId);
2906
+ return response.ok || response.status === 206;
2907
+ } catch {
2908
+ return false;
2909
+ }
2910
+ }
2911
+ async function storedToLlmapiMessage(stored) {
2912
+ let textContent = stored.content;
2913
+ const fileUrlMap = /* @__PURE__ */ new Map();
2914
+ const imageParts = [];
2708
2915
  if (stored.files?.length) {
2709
2916
  for (const file of stored.files) {
2710
2917
  if (file.url) {
2711
- content.push({
2918
+ imageParts.push({
2712
2919
  type: "image_url",
2713
2920
  image_url: { url: file.url }
2714
2921
  });
2922
+ } else if (file.sourceUrl) {
2923
+ const isValid = await isUrlValid(file.sourceUrl);
2924
+ if (isValid) {
2925
+ imageParts.push({
2926
+ type: "image_url",
2927
+ image_url: { url: file.sourceUrl }
2928
+ });
2929
+ fileUrlMap.set(file.id, file.sourceUrl);
2930
+ }
2715
2931
  }
2716
2932
  }
2717
2933
  }
2934
+ textContent = textContent.replace(
2935
+ /__SDKFILE__([a-f0-9-]+)__/g,
2936
+ (match, fileId) => {
2937
+ const sourceUrl = fileUrlMap.get(fileId);
2938
+ if (sourceUrl) {
2939
+ return `![image](${sourceUrl})`;
2940
+ }
2941
+ return "";
2942
+ }
2943
+ );
2944
+ textContent = textContent.replace(
2945
+ /!\[MCP_IMAGE:([a-f0-9-]+)\]/g,
2946
+ (match, fileId) => {
2947
+ const sourceUrl = fileUrlMap.get(fileId);
2948
+ if (sourceUrl) {
2949
+ return `![image](${sourceUrl})`;
2950
+ }
2951
+ return "";
2952
+ }
2953
+ );
2954
+ textContent = textContent.replace(/\n{3,}/g, "\n\n").trim();
2955
+ const content = [
2956
+ { type: "text", text: textContent },
2957
+ ...imageParts
2958
+ ];
2718
2959
  return {
2719
2960
  role: stored.role,
2720
2961
  content
@@ -2731,9 +2972,17 @@ function useChatStorage(options) {
2731
2972
  onData,
2732
2973
  onFinish,
2733
2974
  onError,
2734
- apiType
2975
+ apiType,
2976
+ walletAddress
2735
2977
  } = options;
2736
2978
  const [currentConversationId, setCurrentConversationId] = useState2(initialConversationId || null);
2979
+ const blobManagerRef = useRef2(new BlobUrlManager());
2980
+ useEffect2(() => {
2981
+ const manager = blobManagerRef.current;
2982
+ return () => {
2983
+ manager.revokeAll();
2984
+ };
2985
+ }, []);
2737
2986
  const messagesCollection = useMemo(
2738
2987
  () => database.get("history"),
2739
2988
  [database]
@@ -2801,9 +3050,49 @@ function useChatStorage(options) {
2801
3050
  );
2802
3051
  const getMessages = useCallback2(
2803
3052
  async (convId) => {
2804
- return getMessagesOp(storageCtx, convId);
3053
+ const messages = await getMessagesOp(storageCtx, convId);
3054
+ if (walletAddress && hasEncryptionKey(walletAddress) && isOPFSSupported()) {
3055
+ try {
3056
+ const encryptionKey = await getEncryptionKey(walletAddress);
3057
+ const blobManager = blobManagerRef.current;
3058
+ const resolvedMessages = await Promise.all(
3059
+ messages.map(async (msg) => {
3060
+ const fileIds = extractFileIds(msg.content);
3061
+ if (fileIds.length === 0) {
3062
+ return msg;
3063
+ }
3064
+ let resolvedContent = msg.content;
3065
+ for (const fileId of fileIds) {
3066
+ let url = blobManager.getUrl(fileId);
3067
+ if (!url) {
3068
+ const result = await readEncryptedFile(fileId, encryptionKey);
3069
+ if (result) {
3070
+ url = blobManager.createUrl(fileId, result.blob);
3071
+ }
3072
+ }
3073
+ if (url) {
3074
+ const placeholder = createFilePlaceholder(fileId);
3075
+ resolvedContent = resolvedContent.replace(
3076
+ new RegExp(
3077
+ placeholder.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"),
3078
+ "g"
3079
+ ),
3080
+ `![image](${url})`
3081
+ );
3082
+ }
3083
+ }
3084
+ return { ...msg, content: resolvedContent };
3085
+ })
3086
+ );
3087
+ return resolvedMessages;
3088
+ } catch (error) {
3089
+ console.error("[useChatStorage] Failed to resolve file placeholders:", error);
3090
+ return messages;
3091
+ }
3092
+ }
3093
+ return messages;
2805
3094
  },
2806
- [storageCtx]
3095
+ [storageCtx, walletAddress]
2807
3096
  );
2808
3097
  const getMessageCount = useCallback2(
2809
3098
  async (convId) => {
@@ -2927,7 +3216,8 @@ function useChatStorage(options) {
2927
3216
  []
2928
3217
  );
2929
3218
  const extractAndStoreMCPImages = useCallback2(
2930
- async (content, writeFile) => {
3219
+ async (content, writeFile, options2) => {
3220
+ const { replaceUrls = true } = options2 ?? {};
2931
3221
  try {
2932
3222
  const MCP_IMAGE_URL_PATTERN = new RegExp(
2933
3223
  `https://${MCP_R2_DOMAIN.replace(/\./g, "\\.")}[^\\s)]*`,
@@ -3001,9 +3291,10 @@ function useChatStorage(options) {
3001
3291
  id: fileId,
3002
3292
  name: fileName,
3003
3293
  type: mimeType,
3004
- size
3294
+ size,
3295
+ sourceUrl: imageUrl
3005
3296
  });
3006
- if (imageUrl) {
3297
+ if (replaceUrls && imageUrl) {
3007
3298
  replaceUrlWithPlaceholder(imageUrl, fileId);
3008
3299
  }
3009
3300
  } else {
@@ -3025,6 +3316,89 @@ function useChatStorage(options) {
3025
3316
  },
3026
3317
  []
3027
3318
  );
3319
+ const extractAndStoreEncryptedMCPImages = useCallback2(
3320
+ async (content, address) => {
3321
+ try {
3322
+ if (!isOPFSSupported()) {
3323
+ console.warn("[extractAndStoreEncryptedMCPImages] OPFS not supported");
3324
+ return { processedFiles: [], cleanedContent: content };
3325
+ }
3326
+ if (!hasEncryptionKey(address)) {
3327
+ console.warn("[extractAndStoreEncryptedMCPImages] Encryption key not available");
3328
+ return { processedFiles: [], cleanedContent: content };
3329
+ }
3330
+ const MCP_IMAGE_URL_PATTERN = new RegExp(
3331
+ `https://${MCP_R2_DOMAIN.replace(/\./g, "\\.")}[^\\s)]*`,
3332
+ "g"
3333
+ );
3334
+ const urlMatches = content.match(MCP_IMAGE_URL_PATTERN);
3335
+ if (!urlMatches || urlMatches.length === 0) {
3336
+ return { processedFiles: [], cleanedContent: content };
3337
+ }
3338
+ const uniqueUrls = [...new Set(urlMatches)];
3339
+ const encryptionKey = await getEncryptionKey(address);
3340
+ const processedFiles = [];
3341
+ let cleanedContent = content;
3342
+ const results = await Promise.allSettled(
3343
+ uniqueUrls.map(async (imageUrl) => {
3344
+ const controller = new AbortController();
3345
+ const timeoutId = setTimeout(() => controller.abort(), 3e4);
3346
+ try {
3347
+ const response = await fetch(imageUrl, {
3348
+ signal: controller.signal,
3349
+ cache: "no-store"
3350
+ });
3351
+ if (!response.ok) {
3352
+ throw new Error(`Failed to fetch image: ${response.status}`);
3353
+ }
3354
+ const blob = await response.blob();
3355
+ const fileId = crypto.randomUUID();
3356
+ const urlPath = imageUrl.split("?")[0] ?? imageUrl;
3357
+ const extension = urlPath.match(/\.([a-zA-Z0-9]+)$/)?.[1] || "png";
3358
+ const mimeType = blob.type || `image/${extension}`;
3359
+ const fileName = `mcp-image-${Date.now()}-${fileId.slice(0, 8)}.${extension}`;
3360
+ await writeEncryptedFile(fileId, blob, encryptionKey, {
3361
+ name: fileName,
3362
+ sourceUrl: imageUrl
3363
+ });
3364
+ return { fileId, fileName, mimeType, size: blob.size, imageUrl };
3365
+ } finally {
3366
+ clearTimeout(timeoutId);
3367
+ }
3368
+ })
3369
+ );
3370
+ results.forEach((result, i) => {
3371
+ const imageUrl = uniqueUrls[i];
3372
+ if (result.status === "fulfilled") {
3373
+ const { fileId, fileName, mimeType, size } = result.value;
3374
+ processedFiles.push({
3375
+ id: fileId,
3376
+ name: fileName,
3377
+ type: mimeType,
3378
+ size,
3379
+ sourceUrl: imageUrl
3380
+ });
3381
+ const placeholder = createFilePlaceholder(fileId);
3382
+ const escapedUrl = imageUrl.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
3383
+ const markdownImagePattern = new RegExp(
3384
+ `!\\[[^\\]]*\\]\\([\\s]*${escapedUrl}[\\s]*\\)`,
3385
+ "g"
3386
+ );
3387
+ cleanedContent = cleanedContent.replace(markdownImagePattern, placeholder);
3388
+ cleanedContent = cleanedContent.replace(new RegExp(escapedUrl, "g"), placeholder);
3389
+ } else {
3390
+ console.error("[extractAndStoreEncryptedMCPImages] Failed:", result.reason);
3391
+ }
3392
+ });
3393
+ cleanedContent = cleanedContent.replace(/\n{3,}/g, "\n\n").trim();
3394
+ return { processedFiles, cleanedContent };
3395
+ } catch (err) {
3396
+ console.error("[extractAndStoreEncryptedMCPImages] Unexpected error:", err);
3397
+ return { processedFiles: [], cleanedContent: content };
3398
+ }
3399
+ },
3400
+ []
3401
+ );
3028
3402
  const sendMessage = useCallback2(
3029
3403
  async (args) => {
3030
3404
  const {
@@ -3076,10 +3450,10 @@ function useChatStorage(options) {
3076
3450
  const storedMessages = await getMessages(convId);
3077
3451
  const validMessages = storedMessages.filter((msg) => !msg.error);
3078
3452
  const limitedMessages = validMessages.slice(-maxHistoryMessages);
3079
- messagesToSend = [
3080
- ...limitedMessages.map(storedToLlmapiMessage),
3081
- ...messages
3082
- ];
3453
+ const historyMessages = await Promise.all(
3454
+ limitedMessages.map(storedToLlmapiMessage)
3455
+ );
3456
+ messagesToSend = [...historyMessages, ...messages];
3083
3457
  } else {
3084
3458
  messagesToSend = [...messages];
3085
3459
  }
@@ -3217,7 +3591,14 @@ function useChatStorage(options) {
3217
3591
  let cleanedContent = assistantContent.replace(jsonSourcesBlockRegex, "").trim();
3218
3592
  cleanedContent = cleanedContent.replace(/\n{3,}/g, "\n\n");
3219
3593
  let processedFiles = [];
3220
- if (writeFile) {
3594
+ if (walletAddress) {
3595
+ const result2 = await extractAndStoreEncryptedMCPImages(
3596
+ cleanedContent,
3597
+ walletAddress
3598
+ );
3599
+ processedFiles = result2.processedFiles;
3600
+ cleanedContent = result2.cleanedContent;
3601
+ } else if (writeFile) {
3221
3602
  const result2 = await extractAndStoreMCPImages(
3222
3603
  cleanedContent,
3223
3604
  writeFile
@@ -3253,7 +3634,7 @@ function useChatStorage(options) {
3253
3634
  assistantMessage: storedAssistantMessage
3254
3635
  };
3255
3636
  },
3256
- [ensureConversation, getMessages, storageCtx, baseSendMessage]
3637
+ [ensureConversation, getMessages, storageCtx, baseSendMessage, walletAddress, extractAndStoreEncryptedMCPImages]
3257
3638
  );
3258
3639
  const searchMessages = useCallback2(
3259
3640
  async (queryVector, options2) => {
@@ -3576,7 +3957,7 @@ var sdkModelClasses = [
3576
3957
  ];
3577
3958
 
3578
3959
  // src/react/useMemoryStorage.ts
3579
- import { useCallback as useCallback3, useState as useState3, useMemo as useMemo2, useRef as useRef2 } from "react";
3960
+ import { useCallback as useCallback3, useState as useState3, useMemo as useMemo2, useRef as useRef3 } from "react";
3580
3961
  import { postApiV1ChatCompletions } from "@reverbia/sdk";
3581
3962
 
3582
3963
  // src/lib/db/memory/schema.ts
@@ -4368,7 +4749,7 @@ function useMemoryStorage(options) {
4368
4749
  } = options;
4369
4750
  const embeddingModel = userEmbeddingModel === void 0 ? DEFAULT_API_EMBEDDING_MODEL : userEmbeddingModel;
4370
4751
  const [memories, setMemories] = useState3([]);
4371
- const extractionInProgressRef = useRef2(false);
4752
+ const extractionInProgressRef = useRef3(false);
4372
4753
  const memoriesCollection = useMemo2(
4373
4754
  () => database.get("memories"),
4374
4755
  [database]
@@ -4892,7 +5273,7 @@ function useMemoryStorage(options) {
4892
5273
 
4893
5274
  // src/react/useSettings.ts
4894
5275
  import { Q as Q5 } from "@nozbe/watermelondb";
4895
- import { useCallback as useCallback4, useState as useState4, useMemo as useMemo3, useEffect as useEffect2 } from "react";
5276
+ import { useCallback as useCallback4, useState as useState4, useMemo as useMemo3, useEffect as useEffect3 } from "react";
4896
5277
 
4897
5278
  // src/lib/db/settings/schema.ts
4898
5279
  import { appSchema as appSchema4, tableSchema as tableSchema4 } from "@nozbe/watermelondb";
@@ -5369,7 +5750,7 @@ function useSettings(options) {
5369
5750
  },
5370
5751
  [storageCtx, walletAddress]
5371
5752
  );
5372
- useEffect2(() => {
5753
+ useEffect3(() => {
5373
5754
  if (!walletAddress) {
5374
5755
  setModelPreferenceState(null);
5375
5756
  setUserPreferenceState(null);
@@ -5407,7 +5788,7 @@ function useSettings(options) {
5407
5788
  cancelled = true;
5408
5789
  };
5409
5790
  }, [walletAddress, storageCtx, legacyStorageCtx]);
5410
- useEffect2(() => {
5791
+ useEffect3(() => {
5411
5792
  if (!walletAddress) return;
5412
5793
  const subscription = userPreferencesCollection.query(Q5.where("wallet_address", walletAddress)).observeWithColumns([
5413
5794
  "nickname",
@@ -5648,22 +6029,22 @@ ${text5}`;
5648
6029
  }
5649
6030
 
5650
6031
  // src/react/useModels.ts
5651
- import { useCallback as useCallback7, useEffect as useEffect3, useRef as useRef3, useState as useState7 } from "react";
6032
+ import { useCallback as useCallback7, useEffect as useEffect4, useRef as useRef4, useState as useState7 } from "react";
5652
6033
  function useModels(options = {}) {
5653
6034
  const { getToken, baseUrl = BASE_URL, provider, autoFetch = true } = options;
5654
6035
  const [models, setModels] = useState7([]);
5655
6036
  const [isLoading, setIsLoading] = useState7(false);
5656
6037
  const [error, setError] = useState7(null);
5657
- const getTokenRef = useRef3(getToken);
5658
- const baseUrlRef = useRef3(baseUrl);
5659
- const providerRef = useRef3(provider);
5660
- const abortControllerRef = useRef3(null);
5661
- useEffect3(() => {
6038
+ const getTokenRef = useRef4(getToken);
6039
+ const baseUrlRef = useRef4(baseUrl);
6040
+ const providerRef = useRef4(provider);
6041
+ const abortControllerRef = useRef4(null);
6042
+ useEffect4(() => {
5662
6043
  getTokenRef.current = getToken;
5663
6044
  baseUrlRef.current = baseUrl;
5664
6045
  providerRef.current = provider;
5665
6046
  });
5666
- useEffect3(() => {
6047
+ useEffect4(() => {
5667
6048
  return () => {
5668
6049
  if (abortControllerRef.current) {
5669
6050
  abortControllerRef.current.abort();
@@ -5733,8 +6114,8 @@ function useModels(options = {}) {
5733
6114
  setModels([]);
5734
6115
  await fetchModels();
5735
6116
  }, [fetchModels]);
5736
- const hasFetchedRef = useRef3(false);
5737
- useEffect3(() => {
6117
+ const hasFetchedRef = useRef4(false);
6118
+ useEffect4(() => {
5738
6119
  if (autoFetch && !hasFetchedRef.current) {
5739
6120
  hasFetchedRef.current = true;
5740
6121
  fetchModels();
@@ -5752,15 +6133,15 @@ function useModels(options = {}) {
5752
6133
  }
5753
6134
 
5754
6135
  // src/react/useSearch.ts
5755
- import { useCallback as useCallback8, useEffect as useEffect4, useRef as useRef4, useState as useState8 } from "react";
6136
+ import { useCallback as useCallback8, useEffect as useEffect5, useRef as useRef5, useState as useState8 } from "react";
5756
6137
  function useSearch(options = {}) {
5757
6138
  const { getToken, baseUrl = BASE_URL, onError } = options;
5758
6139
  const [isLoading, setIsLoading] = useState8(false);
5759
6140
  const [results, setResults] = useState8(null);
5760
6141
  const [response, setResponse] = useState8(null);
5761
6142
  const [error, setError] = useState8(null);
5762
- const abortControllerRef = useRef4(null);
5763
- useEffect4(() => {
6143
+ const abortControllerRef = useRef5(null);
6144
+ useEffect5(() => {
5764
6145
  return () => {
5765
6146
  if (abortControllerRef.current) {
5766
6147
  abortControllerRef.current.abort();
@@ -5836,12 +6217,12 @@ function useSearch(options = {}) {
5836
6217
  }
5837
6218
 
5838
6219
  // src/react/useImageGeneration.ts
5839
- import { useCallback as useCallback9, useEffect as useEffect5, useRef as useRef5, useState as useState9 } from "react";
6220
+ import { useCallback as useCallback9, useEffect as useEffect6, useRef as useRef6, useState as useState9 } from "react";
5840
6221
  function useImageGeneration(options = {}) {
5841
6222
  const { getToken, baseUrl = BASE_URL, onFinish, onError } = options;
5842
6223
  const [isLoading, setIsLoading] = useState9(false);
5843
- const abortControllerRef = useRef5(null);
5844
- useEffect5(() => {
6224
+ const abortControllerRef = useRef6(null);
6225
+ useEffect6(() => {
5845
6226
  return () => {
5846
6227
  if (abortControllerRef.current) {
5847
6228
  abortControllerRef.current.abort();
@@ -6223,7 +6604,7 @@ import {
6223
6604
  createElement,
6224
6605
  useCallback as useCallback10,
6225
6606
  useContext,
6226
- useEffect as useEffect6,
6607
+ useEffect as useEffect7,
6227
6608
  useState as useState10
6228
6609
  } from "react";
6229
6610
 
@@ -6554,7 +6935,7 @@ function DropboxAuthProvider({
6554
6935
  }) {
6555
6936
  const [accessToken, setAccessToken] = useState10(null);
6556
6937
  const isConfigured = !!appKey;
6557
- useEffect6(() => {
6938
+ useEffect7(() => {
6558
6939
  const checkStoredToken = async () => {
6559
6940
  if (walletAddress) {
6560
6941
  await migrateUnencryptedTokens("dropbox", walletAddress);
@@ -6568,7 +6949,7 @@ function DropboxAuthProvider({
6568
6949
  };
6569
6950
  checkStoredToken();
6570
6951
  }, [apiClient, walletAddress]);
6571
- useEffect6(() => {
6952
+ useEffect7(() => {
6572
6953
  if (!isConfigured) return;
6573
6954
  const handleCallback = async () => {
6574
6955
  if (isDropboxCallback()) {
@@ -6740,7 +7121,7 @@ import {
6740
7121
  createElement as createElement2,
6741
7122
  useCallback as useCallback12,
6742
7123
  useContext as useContext2,
6743
- useEffect as useEffect7,
7124
+ useEffect as useEffect8,
6744
7125
  useState as useState11
6745
7126
  } from "react";
6746
7127
 
@@ -6974,7 +7355,7 @@ function GoogleDriveAuthProvider({
6974
7355
  }) {
6975
7356
  const [accessToken, setAccessToken] = useState11(null);
6976
7357
  const isConfigured = !!clientId;
6977
- useEffect7(() => {
7358
+ useEffect8(() => {
6978
7359
  const checkStoredToken = async () => {
6979
7360
  if (walletAddress) {
6980
7361
  await migrateUnencryptedTokens("google-drive", walletAddress);
@@ -6988,7 +7369,7 @@ function GoogleDriveAuthProvider({
6988
7369
  };
6989
7370
  checkStoredToken();
6990
7371
  }, [apiClient, walletAddress]);
6991
- useEffect7(() => {
7372
+ useEffect8(() => {
6992
7373
  if (!isConfigured) return;
6993
7374
  const handleCallback = async () => {
6994
7375
  if (isGoogleDriveCallback()) {
@@ -7471,7 +7852,7 @@ import {
7471
7852
  createElement as createElement3,
7472
7853
  useCallback as useCallback14,
7473
7854
  useContext as useContext3,
7474
- useEffect as useEffect8,
7855
+ useEffect as useEffect9,
7475
7856
  useState as useState12
7476
7857
  } from "react";
7477
7858
 
@@ -7721,7 +8102,7 @@ function ICloudAuthProvider({
7721
8102
  const [isAvailable, setIsAvailable] = useState12(false);
7722
8103
  const [isConfigured, setIsConfigured] = useState12(false);
7723
8104
  const [isLoading, setIsLoading] = useState12(false);
7724
- useEffect8(() => {
8105
+ useEffect9(() => {
7725
8106
  if (!apiToken || typeof window === "undefined") {
7726
8107
  return;
7727
8108
  }
@@ -8037,7 +8418,7 @@ import {
8037
8418
  createElement as createElement4,
8038
8419
  useCallback as useCallback16,
8039
8420
  useContext as useContext4,
8040
- useEffect as useEffect9,
8421
+ useEffect as useEffect10,
8041
8422
  useState as useState13
8042
8423
  } from "react";
8043
8424
  var BackupAuthContext = createContext4(null);
@@ -8061,7 +8442,7 @@ function BackupAuthProvider({
8061
8442
  const [icloudUserRecordName, setIcloudUserRecordName] = useState13(null);
8062
8443
  const [isIcloudAvailable, setIsIcloudAvailable] = useState13(false);
8063
8444
  const isIcloudConfigured = isIcloudAvailable && !!icloudApiToken;
8064
- useEffect9(() => {
8445
+ useEffect10(() => {
8065
8446
  const checkStoredTokens = async () => {
8066
8447
  if (walletAddress) {
8067
8448
  await Promise.all([
@@ -8084,7 +8465,7 @@ function BackupAuthProvider({
8084
8465
  };
8085
8466
  checkStoredTokens();
8086
8467
  }, [apiClient, walletAddress]);
8087
- useEffect9(() => {
8468
+ useEffect10(() => {
8088
8469
  if (!icloudApiToken || typeof window === "undefined") {
8089
8470
  return;
8090
8471
  }
@@ -8112,7 +8493,7 @@ function BackupAuthProvider({
8112
8493
  };
8113
8494
  initCloudKit();
8114
8495
  }, [icloudApiToken, icloudContainerIdentifier, icloudEnvironment]);
8115
- useEffect9(() => {
8496
+ useEffect10(() => {
8116
8497
  if (!isDropboxConfigured) return;
8117
8498
  const handleCallback = async () => {
8118
8499
  if (isDropboxCallback()) {
@@ -8132,7 +8513,7 @@ function BackupAuthProvider({
8132
8513
  };
8133
8514
  handleCallback();
8134
8515
  }, [dropboxCallbackPath, isDropboxConfigured, apiClient, walletAddress]);
8135
- useEffect9(() => {
8516
+ useEffect10(() => {
8136
8517
  if (!isGoogleConfigured) return;
8137
8518
  const handleCallback = async () => {
8138
8519
  if (isGoogleDriveCallback()) {
@@ -9060,6 +9441,7 @@ export {
9060
9441
  DEFAULT_ROOT_FOLDER as BACKUP_DRIVE_ROOT_FOLDER,
9061
9442
  DEFAULT_BACKUP_FOLDER2 as BACKUP_ICLOUD_FOLDER,
9062
9443
  BackupAuthProvider,
9444
+ BlobUrlManager,
9063
9445
  Conversation as ChatConversation,
9064
9446
  Message as ChatMessage,
9065
9447
  DEFAULT_BACKUP_FOLDER,
@@ -9089,9 +9471,12 @@ export {
9089
9471
  createMemoryContextSystemMessage,
9090
9472
  decryptData,
9091
9473
  decryptDataBytes,
9474
+ deleteEncryptedFile,
9092
9475
  encryptData,
9093
9476
  exportPublicKey,
9094
9477
  extractConversationContext,
9478
+ fileExists,
9479
+ findFileIdBySourceUrl,
9095
9480
  formatMemoriesForChat,
9096
9481
  generateCompositeKey,
9097
9482
  generateConversationId,
@@ -9102,6 +9487,7 @@ export {
9102
9487
  getAndClearDriveReturnUrl,
9103
9488
  getCalendarAccessToken,
9104
9489
  getDriveAccessToken,
9490
+ getEncryptionKey,
9105
9491
  getGoogleDriveStoredToken,
9106
9492
  getValidCalendarToken,
9107
9493
  getValidDriveToken,
@@ -9116,9 +9502,12 @@ export {
9116
9502
  hasKeyPair,
9117
9503
  isCalendarCallback,
9118
9504
  isDriveCallback,
9505
+ isOPFSSupported,
9119
9506
  memoryStorageSchema,
9507
+ readEncryptedFile,
9120
9508
  refreshCalendarToken,
9121
9509
  refreshDriveToken,
9510
+ replaceUrlWithMCPPlaceholder,
9122
9511
  requestEncryptionKey,
9123
9512
  requestKeyPair,
9124
9513
  revokeCalendarToken,
@@ -9153,5 +9542,6 @@ export {
9153
9542
  usePdf,
9154
9543
  useSearch,
9155
9544
  useSettings,
9156
- userPreferencesStorageSchema
9545
+ userPreferencesStorageSchema,
9546
+ writeEncryptedFile
9157
9547
  };