@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.
@@ -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,6 +2700,178 @@ 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
2876
  function replaceUrlWithMCPPlaceholder(content, url, fileId) {
2705
2877
  const escapedUrl = url.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
@@ -2721,20 +2893,69 @@ function replaceUrlWithMCPPlaceholder(content, url, fileId) {
2721
2893
  function findFileIdBySourceUrl(files, sourceUrl) {
2722
2894
  return files?.find((f) => f.sourceUrl === sourceUrl)?.id;
2723
2895
  }
2724
- function storedToLlmapiMessage(stored) {
2725
- const content = [
2726
- { type: "text", text: stored.content }
2727
- ];
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 = [];
2728
2915
  if (stored.files?.length) {
2729
2916
  for (const file of stored.files) {
2730
2917
  if (file.url) {
2731
- content.push({
2918
+ imageParts.push({
2732
2919
  type: "image_url",
2733
2920
  image_url: { url: file.url }
2734
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
+ }
2735
2931
  }
2736
2932
  }
2737
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
+ ];
2738
2959
  return {
2739
2960
  role: stored.role,
2740
2961
  content
@@ -2751,9 +2972,17 @@ function useChatStorage(options) {
2751
2972
  onData,
2752
2973
  onFinish,
2753
2974
  onError,
2754
- apiType
2975
+ apiType,
2976
+ walletAddress
2755
2977
  } = options;
2756
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
+ }, []);
2757
2986
  const messagesCollection = useMemo(
2758
2987
  () => database.get("history"),
2759
2988
  [database]
@@ -2821,9 +3050,49 @@ function useChatStorage(options) {
2821
3050
  );
2822
3051
  const getMessages = useCallback2(
2823
3052
  async (convId) => {
2824
- 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;
2825
3094
  },
2826
- [storageCtx]
3095
+ [storageCtx, walletAddress]
2827
3096
  );
2828
3097
  const getMessageCount = useCallback2(
2829
3098
  async (convId) => {
@@ -3047,6 +3316,89 @@ function useChatStorage(options) {
3047
3316
  },
3048
3317
  []
3049
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
+ );
3050
3402
  const sendMessage = useCallback2(
3051
3403
  async (args) => {
3052
3404
  const {
@@ -3098,10 +3450,10 @@ function useChatStorage(options) {
3098
3450
  const storedMessages = await getMessages(convId);
3099
3451
  const validMessages = storedMessages.filter((msg) => !msg.error);
3100
3452
  const limitedMessages = validMessages.slice(-maxHistoryMessages);
3101
- messagesToSend = [
3102
- ...limitedMessages.map(storedToLlmapiMessage),
3103
- ...messages
3104
- ];
3453
+ const historyMessages = await Promise.all(
3454
+ limitedMessages.map(storedToLlmapiMessage)
3455
+ );
3456
+ messagesToSend = [...historyMessages, ...messages];
3105
3457
  } else {
3106
3458
  messagesToSend = [...messages];
3107
3459
  }
@@ -3239,7 +3591,14 @@ function useChatStorage(options) {
3239
3591
  let cleanedContent = assistantContent.replace(jsonSourcesBlockRegex, "").trim();
3240
3592
  cleanedContent = cleanedContent.replace(/\n{3,}/g, "\n\n");
3241
3593
  let processedFiles = [];
3242
- 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) {
3243
3602
  const result2 = await extractAndStoreMCPImages(
3244
3603
  cleanedContent,
3245
3604
  writeFile
@@ -3275,7 +3634,7 @@ function useChatStorage(options) {
3275
3634
  assistantMessage: storedAssistantMessage
3276
3635
  };
3277
3636
  },
3278
- [ensureConversation, getMessages, storageCtx, baseSendMessage]
3637
+ [ensureConversation, getMessages, storageCtx, baseSendMessage, walletAddress, extractAndStoreEncryptedMCPImages]
3279
3638
  );
3280
3639
  const searchMessages = useCallback2(
3281
3640
  async (queryVector, options2) => {
@@ -3598,7 +3957,7 @@ var sdkModelClasses = [
3598
3957
  ];
3599
3958
 
3600
3959
  // src/react/useMemoryStorage.ts
3601
- 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";
3602
3961
  import { postApiV1ChatCompletions } from "@reverbia/sdk";
3603
3962
 
3604
3963
  // src/lib/db/memory/schema.ts
@@ -4390,7 +4749,7 @@ function useMemoryStorage(options) {
4390
4749
  } = options;
4391
4750
  const embeddingModel = userEmbeddingModel === void 0 ? DEFAULT_API_EMBEDDING_MODEL : userEmbeddingModel;
4392
4751
  const [memories, setMemories] = useState3([]);
4393
- const extractionInProgressRef = useRef2(false);
4752
+ const extractionInProgressRef = useRef3(false);
4394
4753
  const memoriesCollection = useMemo2(
4395
4754
  () => database.get("memories"),
4396
4755
  [database]
@@ -4914,7 +5273,7 @@ function useMemoryStorage(options) {
4914
5273
 
4915
5274
  // src/react/useSettings.ts
4916
5275
  import { Q as Q5 } from "@nozbe/watermelondb";
4917
- 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";
4918
5277
 
4919
5278
  // src/lib/db/settings/schema.ts
4920
5279
  import { appSchema as appSchema4, tableSchema as tableSchema4 } from "@nozbe/watermelondb";
@@ -5391,7 +5750,7 @@ function useSettings(options) {
5391
5750
  },
5392
5751
  [storageCtx, walletAddress]
5393
5752
  );
5394
- useEffect2(() => {
5753
+ useEffect3(() => {
5395
5754
  if (!walletAddress) {
5396
5755
  setModelPreferenceState(null);
5397
5756
  setUserPreferenceState(null);
@@ -5429,7 +5788,7 @@ function useSettings(options) {
5429
5788
  cancelled = true;
5430
5789
  };
5431
5790
  }, [walletAddress, storageCtx, legacyStorageCtx]);
5432
- useEffect2(() => {
5791
+ useEffect3(() => {
5433
5792
  if (!walletAddress) return;
5434
5793
  const subscription = userPreferencesCollection.query(Q5.where("wallet_address", walletAddress)).observeWithColumns([
5435
5794
  "nickname",
@@ -5670,22 +6029,22 @@ ${text5}`;
5670
6029
  }
5671
6030
 
5672
6031
  // src/react/useModels.ts
5673
- 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";
5674
6033
  function useModels(options = {}) {
5675
6034
  const { getToken, baseUrl = BASE_URL, provider, autoFetch = true } = options;
5676
6035
  const [models, setModels] = useState7([]);
5677
6036
  const [isLoading, setIsLoading] = useState7(false);
5678
6037
  const [error, setError] = useState7(null);
5679
- const getTokenRef = useRef3(getToken);
5680
- const baseUrlRef = useRef3(baseUrl);
5681
- const providerRef = useRef3(provider);
5682
- const abortControllerRef = useRef3(null);
5683
- useEffect3(() => {
6038
+ const getTokenRef = useRef4(getToken);
6039
+ const baseUrlRef = useRef4(baseUrl);
6040
+ const providerRef = useRef4(provider);
6041
+ const abortControllerRef = useRef4(null);
6042
+ useEffect4(() => {
5684
6043
  getTokenRef.current = getToken;
5685
6044
  baseUrlRef.current = baseUrl;
5686
6045
  providerRef.current = provider;
5687
6046
  });
5688
- useEffect3(() => {
6047
+ useEffect4(() => {
5689
6048
  return () => {
5690
6049
  if (abortControllerRef.current) {
5691
6050
  abortControllerRef.current.abort();
@@ -5755,8 +6114,8 @@ function useModels(options = {}) {
5755
6114
  setModels([]);
5756
6115
  await fetchModels();
5757
6116
  }, [fetchModels]);
5758
- const hasFetchedRef = useRef3(false);
5759
- useEffect3(() => {
6117
+ const hasFetchedRef = useRef4(false);
6118
+ useEffect4(() => {
5760
6119
  if (autoFetch && !hasFetchedRef.current) {
5761
6120
  hasFetchedRef.current = true;
5762
6121
  fetchModels();
@@ -5774,15 +6133,15 @@ function useModels(options = {}) {
5774
6133
  }
5775
6134
 
5776
6135
  // src/react/useSearch.ts
5777
- 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";
5778
6137
  function useSearch(options = {}) {
5779
6138
  const { getToken, baseUrl = BASE_URL, onError } = options;
5780
6139
  const [isLoading, setIsLoading] = useState8(false);
5781
6140
  const [results, setResults] = useState8(null);
5782
6141
  const [response, setResponse] = useState8(null);
5783
6142
  const [error, setError] = useState8(null);
5784
- const abortControllerRef = useRef4(null);
5785
- useEffect4(() => {
6143
+ const abortControllerRef = useRef5(null);
6144
+ useEffect5(() => {
5786
6145
  return () => {
5787
6146
  if (abortControllerRef.current) {
5788
6147
  abortControllerRef.current.abort();
@@ -5858,12 +6217,12 @@ function useSearch(options = {}) {
5858
6217
  }
5859
6218
 
5860
6219
  // src/react/useImageGeneration.ts
5861
- 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";
5862
6221
  function useImageGeneration(options = {}) {
5863
6222
  const { getToken, baseUrl = BASE_URL, onFinish, onError } = options;
5864
6223
  const [isLoading, setIsLoading] = useState9(false);
5865
- const abortControllerRef = useRef5(null);
5866
- useEffect5(() => {
6224
+ const abortControllerRef = useRef6(null);
6225
+ useEffect6(() => {
5867
6226
  return () => {
5868
6227
  if (abortControllerRef.current) {
5869
6228
  abortControllerRef.current.abort();
@@ -6245,7 +6604,7 @@ import {
6245
6604
  createElement,
6246
6605
  useCallback as useCallback10,
6247
6606
  useContext,
6248
- useEffect as useEffect6,
6607
+ useEffect as useEffect7,
6249
6608
  useState as useState10
6250
6609
  } from "react";
6251
6610
 
@@ -6576,7 +6935,7 @@ function DropboxAuthProvider({
6576
6935
  }) {
6577
6936
  const [accessToken, setAccessToken] = useState10(null);
6578
6937
  const isConfigured = !!appKey;
6579
- useEffect6(() => {
6938
+ useEffect7(() => {
6580
6939
  const checkStoredToken = async () => {
6581
6940
  if (walletAddress) {
6582
6941
  await migrateUnencryptedTokens("dropbox", walletAddress);
@@ -6590,7 +6949,7 @@ function DropboxAuthProvider({
6590
6949
  };
6591
6950
  checkStoredToken();
6592
6951
  }, [apiClient, walletAddress]);
6593
- useEffect6(() => {
6952
+ useEffect7(() => {
6594
6953
  if (!isConfigured) return;
6595
6954
  const handleCallback = async () => {
6596
6955
  if (isDropboxCallback()) {
@@ -6762,7 +7121,7 @@ import {
6762
7121
  createElement as createElement2,
6763
7122
  useCallback as useCallback12,
6764
7123
  useContext as useContext2,
6765
- useEffect as useEffect7,
7124
+ useEffect as useEffect8,
6766
7125
  useState as useState11
6767
7126
  } from "react";
6768
7127
 
@@ -6996,7 +7355,7 @@ function GoogleDriveAuthProvider({
6996
7355
  }) {
6997
7356
  const [accessToken, setAccessToken] = useState11(null);
6998
7357
  const isConfigured = !!clientId;
6999
- useEffect7(() => {
7358
+ useEffect8(() => {
7000
7359
  const checkStoredToken = async () => {
7001
7360
  if (walletAddress) {
7002
7361
  await migrateUnencryptedTokens("google-drive", walletAddress);
@@ -7010,7 +7369,7 @@ function GoogleDriveAuthProvider({
7010
7369
  };
7011
7370
  checkStoredToken();
7012
7371
  }, [apiClient, walletAddress]);
7013
- useEffect7(() => {
7372
+ useEffect8(() => {
7014
7373
  if (!isConfigured) return;
7015
7374
  const handleCallback = async () => {
7016
7375
  if (isGoogleDriveCallback()) {
@@ -7493,7 +7852,7 @@ import {
7493
7852
  createElement as createElement3,
7494
7853
  useCallback as useCallback14,
7495
7854
  useContext as useContext3,
7496
- useEffect as useEffect8,
7855
+ useEffect as useEffect9,
7497
7856
  useState as useState12
7498
7857
  } from "react";
7499
7858
 
@@ -7743,7 +8102,7 @@ function ICloudAuthProvider({
7743
8102
  const [isAvailable, setIsAvailable] = useState12(false);
7744
8103
  const [isConfigured, setIsConfigured] = useState12(false);
7745
8104
  const [isLoading, setIsLoading] = useState12(false);
7746
- useEffect8(() => {
8105
+ useEffect9(() => {
7747
8106
  if (!apiToken || typeof window === "undefined") {
7748
8107
  return;
7749
8108
  }
@@ -8059,7 +8418,7 @@ import {
8059
8418
  createElement as createElement4,
8060
8419
  useCallback as useCallback16,
8061
8420
  useContext as useContext4,
8062
- useEffect as useEffect9,
8421
+ useEffect as useEffect10,
8063
8422
  useState as useState13
8064
8423
  } from "react";
8065
8424
  var BackupAuthContext = createContext4(null);
@@ -8083,7 +8442,7 @@ function BackupAuthProvider({
8083
8442
  const [icloudUserRecordName, setIcloudUserRecordName] = useState13(null);
8084
8443
  const [isIcloudAvailable, setIsIcloudAvailable] = useState13(false);
8085
8444
  const isIcloudConfigured = isIcloudAvailable && !!icloudApiToken;
8086
- useEffect9(() => {
8445
+ useEffect10(() => {
8087
8446
  const checkStoredTokens = async () => {
8088
8447
  if (walletAddress) {
8089
8448
  await Promise.all([
@@ -8106,7 +8465,7 @@ function BackupAuthProvider({
8106
8465
  };
8107
8466
  checkStoredTokens();
8108
8467
  }, [apiClient, walletAddress]);
8109
- useEffect9(() => {
8468
+ useEffect10(() => {
8110
8469
  if (!icloudApiToken || typeof window === "undefined") {
8111
8470
  return;
8112
8471
  }
@@ -8134,7 +8493,7 @@ function BackupAuthProvider({
8134
8493
  };
8135
8494
  initCloudKit();
8136
8495
  }, [icloudApiToken, icloudContainerIdentifier, icloudEnvironment]);
8137
- useEffect9(() => {
8496
+ useEffect10(() => {
8138
8497
  if (!isDropboxConfigured) return;
8139
8498
  const handleCallback = async () => {
8140
8499
  if (isDropboxCallback()) {
@@ -8154,7 +8513,7 @@ function BackupAuthProvider({
8154
8513
  };
8155
8514
  handleCallback();
8156
8515
  }, [dropboxCallbackPath, isDropboxConfigured, apiClient, walletAddress]);
8157
- useEffect9(() => {
8516
+ useEffect10(() => {
8158
8517
  if (!isGoogleConfigured) return;
8159
8518
  const handleCallback = async () => {
8160
8519
  if (isGoogleDriveCallback()) {
@@ -9082,6 +9441,7 @@ export {
9082
9441
  DEFAULT_ROOT_FOLDER as BACKUP_DRIVE_ROOT_FOLDER,
9083
9442
  DEFAULT_BACKUP_FOLDER2 as BACKUP_ICLOUD_FOLDER,
9084
9443
  BackupAuthProvider,
9444
+ BlobUrlManager,
9085
9445
  Conversation as ChatConversation,
9086
9446
  Message as ChatMessage,
9087
9447
  DEFAULT_BACKUP_FOLDER,
@@ -9111,9 +9471,11 @@ export {
9111
9471
  createMemoryContextSystemMessage,
9112
9472
  decryptData,
9113
9473
  decryptDataBytes,
9474
+ deleteEncryptedFile,
9114
9475
  encryptData,
9115
9476
  exportPublicKey,
9116
9477
  extractConversationContext,
9478
+ fileExists,
9117
9479
  findFileIdBySourceUrl,
9118
9480
  formatMemoriesForChat,
9119
9481
  generateCompositeKey,
@@ -9125,6 +9487,7 @@ export {
9125
9487
  getAndClearDriveReturnUrl,
9126
9488
  getCalendarAccessToken,
9127
9489
  getDriveAccessToken,
9490
+ getEncryptionKey,
9128
9491
  getGoogleDriveStoredToken,
9129
9492
  getValidCalendarToken,
9130
9493
  getValidDriveToken,
@@ -9139,7 +9502,9 @@ export {
9139
9502
  hasKeyPair,
9140
9503
  isCalendarCallback,
9141
9504
  isDriveCallback,
9505
+ isOPFSSupported,
9142
9506
  memoryStorageSchema,
9507
+ readEncryptedFile,
9143
9508
  refreshCalendarToken,
9144
9509
  refreshDriveToken,
9145
9510
  replaceUrlWithMCPPlaceholder,
@@ -9177,5 +9542,6 @@ export {
9177
9542
  usePdf,
9178
9543
  useSearch,
9179
9544
  useSettings,
9180
- userPreferencesStorageSchema
9545
+ userPreferencesStorageSchema,
9546
+ writeEncryptedFile
9181
9547
  };