@reverbia/sdk 1.0.0-next.20251229084841 → 1.1.0-next.20251230221037

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.
@@ -5,6 +5,9 @@ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
5
  var __getOwnPropNames = Object.getOwnPropertyNames;
6
6
  var __getProtoOf = Object.getPrototypeOf;
7
7
  var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __esm = (fn, res) => function __init() {
9
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
10
+ };
8
11
  var __export = (target, all) => {
9
12
  for (var name in all)
10
13
  __defProp(target, name, { get: all[name], enumerable: true });
@@ -15,25 +18,729 @@ var __copyProps = (to, from, except, desc) => {
15
18
  if (!__hasOwnProp.call(to, key) && key !== except)
16
19
  __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
20
  }
18
- return to;
19
- };
20
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
- // If the importer is in node compatibility mode or this is not an ESM
22
- // file that has been converted to a CommonJS file using a Babel-
23
- // compatible transform (i.e. "__esModule" has not been set), then set
24
- // "default" to the CommonJS "module.exports" for node compatibility.
25
- isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
- mod
27
- ));
28
- var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
- var __decorateClass = (decorators, target, key, kind) => {
30
- var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
31
- for (var i = decorators.length - 1, decorator; i >= 0; i--)
32
- if (decorator = decorators[i])
33
- result = (kind ? decorator(target, key, result) : decorator(result)) || result;
34
- if (kind && result) __defProp(target, key, result);
35
- return result;
36
- };
21
+ return to;
22
+ };
23
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
24
+ // If the importer is in node compatibility mode or this is not an ESM
25
+ // file that has been converted to a CommonJS file using a Babel-
26
+ // compatible transform (i.e. "__esModule" has not been set), then set
27
+ // "default" to the CommonJS "module.exports" for node compatibility.
28
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
29
+ mod
30
+ ));
31
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
32
+ var __decorateClass = (decorators, target, key, kind) => {
33
+ var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
34
+ for (var i = decorators.length - 1, decorator; i >= 0; i--)
35
+ if (decorator = decorators[i])
36
+ result = (kind ? decorator(target, key, result) : decorator(result)) || result;
37
+ if (kind && result) __defProp(target, key, result);
38
+ return result;
39
+ };
40
+
41
+ // src/lib/validation.ts
42
+ function isValidWalletAddress(address) {
43
+ if (!address || typeof address !== "string") return false;
44
+ const ethereumAddressRegex = /^0x[a-fA-F0-9]{40}$/;
45
+ return ethereumAddressRegex.test(address);
46
+ }
47
+ var init_validation = __esm({
48
+ "src/lib/validation.ts"() {
49
+ "use strict";
50
+ }
51
+ });
52
+
53
+ // src/lib/backup/oauth/storage.ts
54
+ var storage_exports = {};
55
+ __export(storage_exports, {
56
+ clearTokenData: () => clearTokenData,
57
+ getRefreshToken: () => getRefreshToken,
58
+ getRefreshTokenSync: () => getRefreshTokenSync,
59
+ getStoredTokenData: () => getStoredTokenData,
60
+ getStoredTokenDataSync: () => getStoredTokenDataSync,
61
+ getValidAccessToken: () => getValidAccessToken,
62
+ getValidAccessTokenSync: () => getValidAccessTokenSync,
63
+ hasStoredCredentialsSync: () => hasStoredCredentialsSync,
64
+ isTokenExpired: () => isTokenExpired,
65
+ reEncryptUnencryptedTokens: () => reEncryptUnencryptedTokens,
66
+ storeTokenData: () => storeTokenData,
67
+ tokenResponseToStoredData: () => tokenResponseToStoredData
68
+ });
69
+ function getStorageKey(provider) {
70
+ return `${STORAGE_KEY_PREFIX}${provider}`;
71
+ }
72
+ function isEncrypted(value) {
73
+ return value.startsWith(ENCRYPTION_PREFIX);
74
+ }
75
+ async function getOrCreateSessionKey() {
76
+ if (typeof window === "undefined") {
77
+ throw new Error("Session key generation requires browser environment");
78
+ }
79
+ let sessionKey = sessionStorage.getItem(SESSION_KEY_STORAGE_KEY);
80
+ if (sessionKey) {
81
+ return sessionKey;
82
+ }
83
+ const keyBytes = crypto.getRandomValues(new Uint8Array(32));
84
+ sessionKey = Array.from(keyBytes).map((b) => b.toString(16).padStart(2, "0")).join("");
85
+ sessionStorage.setItem(SESSION_KEY_STORAGE_KEY, sessionKey);
86
+ return sessionKey;
87
+ }
88
+ async function encryptWithSessionKey(plaintext) {
89
+ const sessionKeyHex = await getOrCreateSessionKey();
90
+ const keyBytes = hexToBytes(sessionKeyHex);
91
+ const key = await crypto.subtle.importKey(
92
+ "raw",
93
+ keyBytes.buffer,
94
+ { name: "AES-GCM" },
95
+ false,
96
+ ["encrypt", "decrypt"]
97
+ );
98
+ const iv = crypto.getRandomValues(new Uint8Array(12));
99
+ const plaintextBytes = new TextEncoder().encode(plaintext);
100
+ const encryptedData = await crypto.subtle.encrypt(
101
+ { name: "AES-GCM", iv },
102
+ key,
103
+ plaintextBytes.buffer
104
+ );
105
+ const encryptedBytes = new Uint8Array(encryptedData);
106
+ const combined = new Uint8Array(iv.length + encryptedBytes.length);
107
+ combined.set(iv, 0);
108
+ combined.set(encryptedBytes, iv.length);
109
+ return Array.from(combined).map((b) => b.toString(16).padStart(2, "0")).join("");
110
+ }
111
+ async function decryptWithSessionKey(encryptedHex) {
112
+ const sessionKeyHex = sessionStorage.getItem(SESSION_KEY_STORAGE_KEY);
113
+ if (!sessionKeyHex) {
114
+ throw new Error("Session key not found");
115
+ }
116
+ const keyBytes = hexToBytes(sessionKeyHex);
117
+ const key = await crypto.subtle.importKey(
118
+ "raw",
119
+ keyBytes.buffer,
120
+ { name: "AES-GCM" },
121
+ false,
122
+ ["encrypt", "decrypt"]
123
+ );
124
+ const combined = hexToBytes(encryptedHex);
125
+ const iv = combined.slice(0, 12);
126
+ const encryptedData = combined.slice(12);
127
+ const decryptedData = await crypto.subtle.decrypt(
128
+ { name: "AES-GCM", iv },
129
+ key,
130
+ encryptedData
131
+ );
132
+ return new TextDecoder().decode(decryptedData);
133
+ }
134
+ function hexToBytes(hex) {
135
+ const cleanHex = hex.startsWith("0x") ? hex.slice(2) : hex;
136
+ const bytes = new Uint8Array(cleanHex.length / 2);
137
+ for (let i = 0; i < cleanHex.length; i += 2) {
138
+ bytes[i / 2] = parseInt(cleanHex.slice(i, i + 2), 16);
139
+ }
140
+ return bytes;
141
+ }
142
+ async function reEncryptUnencryptedTokens(walletAddress) {
143
+ if (typeof window === "undefined") return;
144
+ if (!hasEncryptionKey(walletAddress)) return;
145
+ const providers = ["google-drive", "dropbox"];
146
+ for (const provider of providers) {
147
+ try {
148
+ const stored = localStorage.getItem(getStorageKey(provider));
149
+ if (!stored) continue;
150
+ if (!isEncrypted(stored)) {
151
+ try {
152
+ const data = JSON.parse(stored);
153
+ if (data.accessToken) {
154
+ await storeTokenData(provider, data, walletAddress);
155
+ }
156
+ } catch {
157
+ continue;
158
+ }
159
+ } else {
160
+ const encryptedPayload = stored.slice(ENCRYPTION_PREFIX.length);
161
+ try {
162
+ const decrypted = await decryptWithSessionKey(encryptedPayload);
163
+ const data = JSON.parse(decrypted);
164
+ if (data.accessToken) {
165
+ await storeTokenData(provider, data, walletAddress);
166
+ }
167
+ } catch {
168
+ continue;
169
+ }
170
+ }
171
+ } catch {
172
+ continue;
173
+ }
174
+ }
175
+ }
176
+ async function getStoredTokenData(provider, walletAddress) {
177
+ if (typeof window === "undefined") return null;
178
+ try {
179
+ const stored = localStorage.getItem(getStorageKey(provider));
180
+ if (!stored) return null;
181
+ if (isEncrypted(stored)) {
182
+ const encryptedPayload = stored.slice(ENCRYPTION_PREFIX.length);
183
+ if (walletAddress && hasEncryptionKey(walletAddress)) {
184
+ try {
185
+ const decrypted = await decryptData(encryptedPayload, walletAddress);
186
+ const data = JSON.parse(decrypted);
187
+ if (!data.accessToken) return null;
188
+ return data;
189
+ } catch {
190
+ }
191
+ }
192
+ try {
193
+ const decrypted = await decryptWithSessionKey(encryptedPayload);
194
+ const data = JSON.parse(decrypted);
195
+ if (!data.accessToken) return null;
196
+ if (walletAddress && hasEncryptionKey(walletAddress)) {
197
+ try {
198
+ await storeTokenData(provider, data, walletAddress);
199
+ } catch {
200
+ console.error(`Failed to re-encrypt OAuth token for ${provider}`);
201
+ }
202
+ }
203
+ return data;
204
+ } catch {
205
+ return null;
206
+ }
207
+ } else {
208
+ try {
209
+ const data = JSON.parse(stored);
210
+ if (!data.accessToken) return null;
211
+ if (walletAddress && hasEncryptionKey(walletAddress)) {
212
+ try {
213
+ await storeTokenData(provider, data, walletAddress);
214
+ } catch {
215
+ console.error(`Failed to re-encrypt OAuth token for ${provider}`);
216
+ }
217
+ } else {
218
+ try {
219
+ await storeTokenData(provider, data);
220
+ } catch {
221
+ console.error(`Failed to encrypt OAuth token for ${provider}`);
222
+ }
223
+ }
224
+ return data;
225
+ } catch {
226
+ return null;
227
+ }
228
+ }
229
+ } catch {
230
+ return null;
231
+ }
232
+ }
233
+ function getStoredTokenDataSync(provider) {
234
+ if (typeof window === "undefined") return null;
235
+ try {
236
+ const stored = localStorage.getItem(getStorageKey(provider));
237
+ if (!stored) return null;
238
+ if (isEncrypted(stored)) {
239
+ return null;
240
+ }
241
+ const data = JSON.parse(stored);
242
+ if (!data.accessToken) return null;
243
+ return data;
244
+ } catch {
245
+ return null;
246
+ }
247
+ }
248
+ async function storeTokenData(provider, data, walletAddress) {
249
+ if (typeof window === "undefined") return;
250
+ const jsonData = JSON.stringify(data);
251
+ if (walletAddress && hasEncryptionKey(walletAddress)) {
252
+ try {
253
+ const encrypted = await encryptData(jsonData, walletAddress);
254
+ localStorage.setItem(getStorageKey(provider), `${ENCRYPTION_PREFIX}${encrypted}`);
255
+ return;
256
+ } catch (error) {
257
+ const message = error instanceof Error ? error.message : "Unknown encryption error";
258
+ throw new Error(`OAuth token encryption failed: ${message}`);
259
+ }
260
+ }
261
+ try {
262
+ const encrypted = await encryptWithSessionKey(jsonData);
263
+ localStorage.setItem(getStorageKey(provider), `${ENCRYPTION_PREFIX}${encrypted}`);
264
+ } catch (error) {
265
+ const sessionStorageAvailable = typeof window !== "undefined" && typeof window.sessionStorage !== "undefined" && typeof window.sessionStorage.getItem === "function" && typeof window.sessionStorage.setItem === "function";
266
+ if (!sessionStorageAvailable) {
267
+ localStorage.setItem(getStorageKey(provider), jsonData);
268
+ } else {
269
+ const message = error instanceof Error ? error.message : "Unknown encryption error";
270
+ throw new Error(`OAuth token encryption failed: ${message}`);
271
+ }
272
+ }
273
+ }
274
+ function clearTokenData(provider) {
275
+ if (typeof window === "undefined") return;
276
+ localStorage.removeItem(getStorageKey(provider));
277
+ }
278
+ function isTokenExpired(data, bufferSeconds = 60) {
279
+ if (!data) return true;
280
+ if (!data.expiresAt) return false;
281
+ const now = Date.now();
282
+ const bufferMs = bufferSeconds * 1e3;
283
+ return data.expiresAt - bufferMs <= now;
284
+ }
285
+ async function getValidAccessToken(provider, walletAddress) {
286
+ const data = await getStoredTokenData(provider, walletAddress);
287
+ if (!data) return null;
288
+ if (data.expiresAt && isTokenExpired(data)) {
289
+ return null;
290
+ }
291
+ return data.accessToken;
292
+ }
293
+ function getValidAccessTokenSync(provider) {
294
+ const data = getStoredTokenDataSync(provider);
295
+ if (!data) return null;
296
+ if (data.expiresAt && isTokenExpired(data)) {
297
+ return null;
298
+ }
299
+ return data.accessToken;
300
+ }
301
+ async function getRefreshToken(provider, walletAddress) {
302
+ const data = await getStoredTokenData(provider, walletAddress);
303
+ return data?.refreshToken ?? null;
304
+ }
305
+ function getRefreshTokenSync(provider) {
306
+ const data = getStoredTokenDataSync(provider);
307
+ return data?.refreshToken ?? null;
308
+ }
309
+ function hasStoredCredentialsSync(provider) {
310
+ if (typeof window === "undefined") return false;
311
+ const stored = localStorage.getItem(getStorageKey(provider));
312
+ if (!stored) return false;
313
+ if (isEncrypted(stored)) {
314
+ return true;
315
+ }
316
+ try {
317
+ const data = JSON.parse(stored);
318
+ return !!(data?.accessToken || data?.refreshToken);
319
+ } catch {
320
+ return false;
321
+ }
322
+ }
323
+ function tokenResponseToStoredData(accessToken, expiresIn, refreshToken, scope) {
324
+ const data = {
325
+ accessToken,
326
+ refreshToken,
327
+ scope
328
+ };
329
+ if (expiresIn) {
330
+ data.expiresAt = Date.now() + expiresIn * 1e3;
331
+ }
332
+ return data;
333
+ }
334
+ var STORAGE_KEY_PREFIX, ENCRYPTION_PREFIX, SESSION_KEY_STORAGE_KEY;
335
+ var init_storage = __esm({
336
+ "src/lib/backup/oauth/storage.ts"() {
337
+ "use strict";
338
+ init_useEncryption();
339
+ STORAGE_KEY_PREFIX = "oauth_token_";
340
+ ENCRYPTION_PREFIX = "enc:";
341
+ SESSION_KEY_STORAGE_KEY = "oauth_session_encryption_key";
342
+ }
343
+ });
344
+
345
+ // src/react/useEncryption.ts
346
+ var useEncryption_exports = {};
347
+ __export(useEncryption_exports, {
348
+ clearAllEncryptionKeys: () => clearAllEncryptionKeys,
349
+ clearAllKeyPairs: () => clearAllKeyPairs,
350
+ clearEncryptionKey: () => clearEncryptionKey,
351
+ clearKeyPair: () => clearKeyPair,
352
+ decryptData: () => decryptData,
353
+ decryptDataBytes: () => decryptDataBytes,
354
+ encryptData: () => encryptData,
355
+ exportPublicKey: () => exportPublicKey,
356
+ hasEncryptionKey: () => hasEncryptionKey,
357
+ hasKeyPair: () => hasKeyPair,
358
+ requestEncryptionKey: () => requestEncryptionKey,
359
+ requestKeyPair: () => requestKeyPair,
360
+ useEncryption: () => useEncryption
361
+ });
362
+ function getStoredKey(address) {
363
+ return encryptionKeyStore.get(address) ?? null;
364
+ }
365
+ function setStoredKey(address, keyHex) {
366
+ encryptionKeyStore.set(address, keyHex);
367
+ }
368
+ function clearEncryptionKey(address) {
369
+ encryptionKeyStore.delete(address);
370
+ }
371
+ function clearAllEncryptionKeys() {
372
+ encryptionKeyStore.clear();
373
+ }
374
+ function hexToBytes2(hex) {
375
+ const cleanHex = hex.startsWith("0x") ? hex.slice(2) : hex;
376
+ const bytes = new Uint8Array(cleanHex.length / 2);
377
+ for (let i = 0; i < cleanHex.length; i += 2) {
378
+ bytes[i / 2] = parseInt(cleanHex.slice(i, i + 2), 16);
379
+ }
380
+ return bytes;
381
+ }
382
+ function bytesToHex(bytes) {
383
+ return Array.from(bytes).map((b) => b.toString(16).padStart(2, "0")).join("");
384
+ }
385
+ async function deriveKeyFromSignature(signature) {
386
+ const sigBytes = hexToBytes2(signature);
387
+ const hashBuffer = await crypto.subtle.digest(
388
+ "SHA-256",
389
+ sigBytes.buffer
390
+ );
391
+ const hashBytes = new Uint8Array(hashBuffer);
392
+ return bytesToHex(hashBytes);
393
+ }
394
+ function getStoredKeyPair(address) {
395
+ return keyPairStore.get(address) ?? null;
396
+ }
397
+ function setStoredKeyPair(address, keyPair) {
398
+ keyPairStore.set(address, keyPair);
399
+ }
400
+ async function deriveKeyPairFromSignature(signature, walletAddress) {
401
+ const sigBytes = hexToBytes2(signature);
402
+ const seedBuffer = await crypto.subtle.digest(
403
+ "SHA-256",
404
+ sigBytes.buffer
405
+ );
406
+ const seed = new Uint8Array(seedBuffer);
407
+ const addressBytes = hexToBytes2(walletAddress);
408
+ const hkdfKey = await crypto.subtle.importKey(
409
+ "raw",
410
+ seed.buffer,
411
+ { name: "HKDF" },
412
+ false,
413
+ ["deriveBits"]
414
+ );
415
+ const saltBytes = new Uint8Array(addressBytes);
416
+ const derivedBits = await crypto.subtle.deriveBits(
417
+ {
418
+ name: "HKDF",
419
+ hash: "SHA-256",
420
+ salt: saltBytes,
421
+ // Use wallet address as salt for deterministic derivation
422
+ info: new TextEncoder().encode("ECDH-P256-KeyPair")
423
+ // Context info
424
+ },
425
+ hkdfKey,
426
+ 256
427
+ // 32 bytes = 256 bits
428
+ );
429
+ const privateKeyBytes = new Uint8Array(derivedBits);
430
+ if (privateKeyBytes.every((b) => b === 0)) {
431
+ privateKeyBytes[31] = 1;
432
+ }
433
+ const privateKey = await crypto.subtle.importKey(
434
+ "pkcs8",
435
+ await createPKCS8PrivateKey(privateKeyBytes),
436
+ {
437
+ name: "ECDH",
438
+ namedCurve: "P-256"
439
+ },
440
+ true,
441
+ // extractable so we can export to JWK
442
+ ["deriveBits", "deriveKey"]
443
+ );
444
+ const privateKeyJwk = await crypto.subtle.exportKey("jwk", privateKey);
445
+ if (!privateKeyJwk.x || !privateKeyJwk.y) {
446
+ throw new Error("Failed to derive public key from private key");
447
+ }
448
+ const publicKey = await crypto.subtle.importKey(
449
+ "jwk",
450
+ {
451
+ kty: "EC",
452
+ crv: "P-256",
453
+ x: privateKeyJwk.x,
454
+ y: privateKeyJwk.y
455
+ },
456
+ {
457
+ name: "ECDH",
458
+ namedCurve: "P-256"
459
+ },
460
+ true,
461
+ // extractable
462
+ []
463
+ // no key usage needed for public key
464
+ );
465
+ return {
466
+ privateKey,
467
+ publicKey
468
+ };
469
+ }
470
+ async function createPKCS8PrivateKey(privateKeyBytes) {
471
+ const ecPublicKeyOID = new Uint8Array([
472
+ 6,
473
+ 7,
474
+ 42,
475
+ 134,
476
+ 72,
477
+ 206,
478
+ 61,
479
+ 2,
480
+ 1
481
+ ]);
482
+ const prime256v1OID = new Uint8Array([
483
+ 6,
484
+ 8,
485
+ 42,
486
+ 134,
487
+ 72,
488
+ 206,
489
+ 61,
490
+ 3,
491
+ 1,
492
+ 7
493
+ ]);
494
+ const version2 = new Uint8Array([2, 1, 1]);
495
+ const privateKeyOctet = new Uint8Array([
496
+ 4,
497
+ 32,
498
+ // OCTET STRING, 32 bytes
499
+ ...privateKeyBytes
500
+ ]);
501
+ const ecPrivateKeyContent = new Uint8Array([
502
+ ...version2,
503
+ ...privateKeyOctet
504
+ ]);
505
+ const ecPrivateKeyLength = ecPrivateKeyContent.length;
506
+ const ecPrivateKeySeq = new Uint8Array([
507
+ 48,
508
+ // SEQUENCE
509
+ ecPrivateKeyLength,
510
+ // Length
511
+ ...ecPrivateKeyContent
512
+ ]);
513
+ const wrappedPrivateKey = new Uint8Array([
514
+ 4,
515
+ // OCTET STRING
516
+ ecPrivateKeySeq.length,
517
+ // Length
518
+ ...ecPrivateKeySeq
519
+ ]);
520
+ const algorithmIdContent = new Uint8Array([
521
+ ...ecPublicKeyOID,
522
+ ...prime256v1OID
523
+ ]);
524
+ const algorithmIdLength = algorithmIdContent.length;
525
+ const algorithmIdSeq = new Uint8Array([
526
+ 48,
527
+ // SEQUENCE
528
+ algorithmIdLength,
529
+ // Length
530
+ ...algorithmIdContent
531
+ ]);
532
+ const pkcs8Version = new Uint8Array([2, 1, 0]);
533
+ const pkcs8Content = new Uint8Array([
534
+ ...pkcs8Version,
535
+ ...algorithmIdSeq,
536
+ ...wrappedPrivateKey
537
+ ]);
538
+ const pkcs8ContentLength = pkcs8Content.length;
539
+ const pkcs8Seq = new Uint8Array([
540
+ 48,
541
+ // SEQUENCE
542
+ pkcs8ContentLength,
543
+ // Length
544
+ ...pkcs8Content
545
+ ]);
546
+ return pkcs8Seq.buffer;
547
+ }
548
+ async function getEncryptionKey(address) {
549
+ const keyHex = getStoredKey(address);
550
+ if (!keyHex) {
551
+ throw new Error(
552
+ "Encryption key not found. Please sign a message to generate your encryption key."
553
+ );
554
+ }
555
+ const keyBytes = hexToBytes2(keyHex);
556
+ return crypto.subtle.importKey(
557
+ "raw",
558
+ keyBytes.buffer,
559
+ { name: "AES-GCM" },
560
+ false,
561
+ ["encrypt", "decrypt"]
562
+ );
563
+ }
564
+ async function encryptData(plaintext, address) {
565
+ if (!isValidWalletAddress(address)) {
566
+ throw new Error(`Invalid wallet address: ${address}. Wallet address must be a valid Ethereum address (0x followed by 40 hex characters).`);
567
+ }
568
+ const key = await getEncryptionKey(address);
569
+ const plaintextBytes = typeof plaintext === "string" ? new TextEncoder().encode(plaintext) : plaintext;
570
+ const iv = crypto.getRandomValues(new Uint8Array(12));
571
+ const encryptedData = await crypto.subtle.encrypt(
572
+ {
573
+ name: "AES-GCM",
574
+ iv
575
+ },
576
+ key,
577
+ plaintextBytes.buffer
578
+ );
579
+ const encryptedBytes = new Uint8Array(encryptedData);
580
+ const combined = new Uint8Array(iv.length + encryptedBytes.length);
581
+ combined.set(iv, 0);
582
+ combined.set(encryptedBytes, iv.length);
583
+ return bytesToHex(combined);
584
+ }
585
+ async function decryptData(encryptedHex, address) {
586
+ if (!isValidWalletAddress(address)) {
587
+ throw new Error(`Invalid wallet address: ${address}. Wallet address must be a valid Ethereum address (0x followed by 40 hex characters).`);
588
+ }
589
+ const key = await getEncryptionKey(address);
590
+ const combined = hexToBytes2(encryptedHex);
591
+ const iv = combined.slice(0, 12);
592
+ const encryptedData = combined.slice(12);
593
+ const decryptedData = await crypto.subtle.decrypt(
594
+ {
595
+ name: "AES-GCM",
596
+ iv
597
+ },
598
+ key,
599
+ encryptedData
600
+ );
601
+ return new TextDecoder().decode(decryptedData);
602
+ }
603
+ async function decryptDataBytes(encryptedHex, address) {
604
+ const key = await getEncryptionKey(address);
605
+ const combined = hexToBytes2(encryptedHex);
606
+ const iv = combined.slice(0, 12);
607
+ const encryptedData = combined.slice(12);
608
+ const decryptedData = await crypto.subtle.decrypt(
609
+ {
610
+ name: "AES-GCM",
611
+ iv
612
+ },
613
+ key,
614
+ encryptedData
615
+ );
616
+ return new Uint8Array(decryptedData);
617
+ }
618
+ function hasEncryptionKey(address) {
619
+ return getStoredKey(address) !== null;
620
+ }
621
+ function isRateLimitError(error) {
622
+ if (!error) return false;
623
+ if (typeof error === "object") {
624
+ const err = error;
625
+ if (err.status === 429 || err.statusCode === 429) return true;
626
+ if (err.code === "429" || err.code === "RATE_LIMIT_EXCEEDED") return true;
627
+ if (err.message?.includes("429") || err.message?.toLowerCase().includes("rate limit")) return true;
628
+ }
629
+ if (typeof error === "string") {
630
+ return error.includes("429") || error.toLowerCase().includes("rate limit");
631
+ }
632
+ return false;
633
+ }
634
+ async function signMessageWithRetry(signMessage, message, maxRetries = 3, initialDelay = 1e3) {
635
+ let lastError;
636
+ for (let attempt = 0; attempt <= maxRetries; attempt++) {
637
+ try {
638
+ return await signMessage(message);
639
+ } catch (error) {
640
+ lastError = error;
641
+ if (!isRateLimitError(error)) {
642
+ throw error;
643
+ }
644
+ if (attempt === maxRetries) {
645
+ throw new Error(
646
+ `Rate limit exceeded: Unable to sign message after ${maxRetries + 1} attempts. Please wait a moment and try again. Privy allows 5 signatures per 60 seconds.`
647
+ );
648
+ }
649
+ const delay = Math.min(
650
+ initialDelay * Math.pow(2, attempt),
651
+ 6e4
652
+ // Max 60 seconds
653
+ );
654
+ if (process.env.NODE_ENV === "development") {
655
+ console.warn(
656
+ `[Encryption] Rate limit hit (attempt ${attempt + 1}/${maxRetries + 1}). Retrying in ${Math.ceil(delay / 1e3)}s...`
657
+ );
658
+ }
659
+ await new Promise((resolve) => setTimeout(resolve, delay));
660
+ }
661
+ }
662
+ throw lastError;
663
+ }
664
+ async function requestEncryptionKey(walletAddress, signMessage) {
665
+ if (!isValidWalletAddress(walletAddress)) {
666
+ throw new Error(`Invalid wallet address: ${walletAddress}. Wallet address must be a valid Ethereum address (0x followed by 40 hex characters).`);
667
+ }
668
+ const existingKey = getStoredKey(walletAddress);
669
+ if (existingKey) {
670
+ try {
671
+ const { reEncryptUnencryptedTokens: reEncryptUnencryptedTokens2 } = await Promise.resolve().then(() => (init_storage(), storage_exports));
672
+ await reEncryptUnencryptedTokens2(walletAddress);
673
+ } catch {
674
+ }
675
+ return;
676
+ }
677
+ const signature = await signMessageWithRetry(signMessage, SIGN_MESSAGE);
678
+ const encryptionKey = await deriveKeyFromSignature(signature);
679
+ setStoredKey(walletAddress, encryptionKey);
680
+ try {
681
+ const { reEncryptUnencryptedTokens: reEncryptUnencryptedTokens2 } = await Promise.resolve().then(() => (init_storage(), storage_exports));
682
+ await reEncryptUnencryptedTokens2(walletAddress);
683
+ } catch {
684
+ }
685
+ }
686
+ async function getKeyPair(address, signMessage) {
687
+ const existingKeyPair = getStoredKeyPair(address);
688
+ if (existingKeyPair) {
689
+ return existingKeyPair;
690
+ }
691
+ await requestKeyPair(address, signMessage);
692
+ const keyPair = getStoredKeyPair(address);
693
+ if (!keyPair) {
694
+ throw new Error(
695
+ "Key pair not found. Please sign a message to generate your key pair."
696
+ );
697
+ }
698
+ return keyPair;
699
+ }
700
+ async function requestKeyPair(walletAddress, signMessage) {
701
+ const existingKeyPair = getStoredKeyPair(walletAddress);
702
+ if (existingKeyPair) {
703
+ return;
704
+ }
705
+ const signature = await signMessageWithRetry(signMessage, SIGN_MESSAGE);
706
+ const keyPair = await deriveKeyPairFromSignature(signature, walletAddress);
707
+ setStoredKeyPair(walletAddress, keyPair);
708
+ }
709
+ async function exportPublicKey(address, signMessage) {
710
+ const keyPair = await getKeyPair(address, signMessage);
711
+ const spkiBuffer = await crypto.subtle.exportKey("spki", keyPair.publicKey);
712
+ const spkiBytes = new Uint8Array(spkiBuffer);
713
+ return btoa(String.fromCharCode(...spkiBytes));
714
+ }
715
+ function hasKeyPair(address) {
716
+ return getStoredKeyPair(address) !== null;
717
+ }
718
+ function clearKeyPair(address) {
719
+ keyPairStore.delete(address);
720
+ }
721
+ function clearAllKeyPairs() {
722
+ keyPairStore.clear();
723
+ }
724
+ function useEncryption(signMessage) {
725
+ return {
726
+ requestEncryptionKey: (walletAddress) => requestEncryptionKey(walletAddress, signMessage),
727
+ requestKeyPair: (walletAddress) => requestKeyPair(walletAddress, signMessage),
728
+ exportPublicKey: (walletAddress) => exportPublicKey(walletAddress, signMessage),
729
+ hasKeyPair: (walletAddress) => hasKeyPair(walletAddress),
730
+ clearKeyPair: (walletAddress) => clearKeyPair(walletAddress)
731
+ };
732
+ }
733
+ var SIGN_MESSAGE, encryptionKeyStore, keyPairStore;
734
+ var init_useEncryption = __esm({
735
+ "src/react/useEncryption.ts"() {
736
+ "use strict";
737
+ "use client";
738
+ init_validation();
739
+ SIGN_MESSAGE = "The app is asking you to sign this message to generate encryption keys, which will be used to encrypt data and for encryption with cloud services.";
740
+ encryptionKeyStore = /* @__PURE__ */ new Map();
741
+ keyPairStore = /* @__PURE__ */ new Map();
742
+ }
743
+ });
37
744
 
38
745
  // src/react/index.ts
39
746
  var index_exports = {};
@@ -44,6 +751,7 @@ __export(index_exports, {
44
751
  BackupAuthProvider: () => BackupAuthProvider,
45
752
  ChatConversation: () => Conversation,
46
753
  ChatMessage: () => Message,
754
+ DECRYPTION_FAILED_PLACEHOLDER: () => DECRYPTION_FAILED_PLACEHOLDER,
47
755
  DEFAULT_BACKUP_FOLDER: () => DEFAULT_BACKUP_FOLDER,
48
756
  DEFAULT_DRIVE_CONVERSATIONS_FOLDER: () => DEFAULT_CONVERSATIONS_FOLDER,
49
757
  DEFAULT_DRIVE_ROOT_FOLDER: () => DEFAULT_ROOT_FOLDER,
@@ -57,26 +765,41 @@ __export(index_exports, {
57
765
  chatStorageMigrations: () => chatStorageMigrations,
58
766
  chatStorageSchema: () => chatStorageSchema,
59
767
  clearAllEncryptionKeys: () => clearAllEncryptionKeys,
768
+ clearAllKeyPairs: () => clearAllKeyPairs,
60
769
  clearDropboxToken: () => clearToken,
61
770
  clearEncryptionKey: () => clearEncryptionKey,
62
771
  clearGoogleDriveToken: () => clearGoogleDriveToken,
63
772
  clearICloudAuth: () => clearICloudAuth,
773
+ clearKeyPair: () => clearKeyPair,
64
774
  createMemoryContextSystemMessage: () => createMemoryContextSystemMessage,
65
775
  decryptData: () => decryptData,
66
776
  decryptDataBytes: () => decryptDataBytes,
777
+ decryptField: () => decryptField,
778
+ decryptMemoriesBatch: () => decryptMemoriesBatch,
779
+ decryptMemoryFields: () => decryptMemoryFields,
67
780
  encryptData: () => encryptData,
781
+ encryptField: () => encryptField,
782
+ encryptMemoriesBatch: () => encryptMemoriesBatch,
783
+ encryptMemoriesBatchInPlace: () => encryptMemoriesBatchInPlace,
784
+ encryptMemoryFields: () => encryptMemoryFields,
785
+ exportPublicKey: () => exportPublicKey,
68
786
  extractConversationContext: () => extractConversationContext,
69
787
  formatMemoriesForChat: () => formatMemoriesForChat,
70
788
  generateCompositeKey: () => generateCompositeKey,
71
789
  generateConversationId: () => generateConversationId,
72
790
  generateUniqueKey: () => generateUniqueKey,
791
+ getEncryptionVersion: () => getEncryptionVersion2,
73
792
  getGoogleDriveStoredToken: () => getGoogleDriveStoredToken,
74
793
  hasDropboxCredentials: () => hasDropboxCredentials,
794
+ hasEncryptedFields: () => hasEncryptedFields,
75
795
  hasEncryptionKey: () => hasEncryptionKey,
76
796
  hasGoogleDriveCredentials: () => hasGoogleDriveCredentials,
77
797
  hasICloudCredentials: () => hasICloudCredentials,
798
+ hasKeyPair: () => hasKeyPair,
78
799
  memoryStorageSchema: () => memoryStorageSchema,
800
+ needsEncryption: () => needsEncryption,
79
801
  requestEncryptionKey: () => requestEncryptionKey,
802
+ requestKeyPair: () => requestKeyPair,
80
803
  sdkMigrations: () => sdkMigrations,
81
804
  sdkModelClasses: () => sdkModelClasses,
82
805
  sdkSchema: () => sdkSchema,
@@ -1322,122 +2045,8 @@ function useChat(options) {
1322
2045
  };
1323
2046
  }
1324
2047
 
1325
- // src/react/useEncryption.ts
1326
- var SIGN_MESSAGE = "The app is asking you to sign this message to generate a key, which will be used to encrypt data.";
1327
- var encryptionKeyStore = /* @__PURE__ */ new Map();
1328
- function getStoredKey(address) {
1329
- return encryptionKeyStore.get(address) ?? null;
1330
- }
1331
- function setStoredKey(address, keyHex) {
1332
- encryptionKeyStore.set(address, keyHex);
1333
- }
1334
- function clearEncryptionKey(address) {
1335
- encryptionKeyStore.delete(address);
1336
- }
1337
- function clearAllEncryptionKeys() {
1338
- encryptionKeyStore.clear();
1339
- }
1340
- function hexToBytes(hex) {
1341
- const cleanHex = hex.startsWith("0x") ? hex.slice(2) : hex;
1342
- const bytes = new Uint8Array(cleanHex.length / 2);
1343
- for (let i = 0; i < cleanHex.length; i += 2) {
1344
- bytes[i / 2] = parseInt(cleanHex.slice(i, i + 2), 16);
1345
- }
1346
- return bytes;
1347
- }
1348
- function bytesToHex(bytes) {
1349
- return Array.from(bytes).map((b) => b.toString(16).padStart(2, "0")).join("");
1350
- }
1351
- async function deriveKeyFromSignature(signature) {
1352
- const sigBytes = hexToBytes(signature);
1353
- const hashBuffer = await crypto.subtle.digest(
1354
- "SHA-256",
1355
- sigBytes.buffer
1356
- );
1357
- const hashBytes = new Uint8Array(hashBuffer);
1358
- return bytesToHex(hashBytes);
1359
- }
1360
- async function getEncryptionKey(address) {
1361
- const keyHex = getStoredKey(address);
1362
- if (!keyHex) {
1363
- throw new Error(
1364
- "Encryption key not found. Please sign a message to generate your encryption key."
1365
- );
1366
- }
1367
- const keyBytes = hexToBytes(keyHex);
1368
- return crypto.subtle.importKey(
1369
- "raw",
1370
- keyBytes.buffer,
1371
- { name: "AES-GCM" },
1372
- false,
1373
- ["encrypt", "decrypt"]
1374
- );
1375
- }
1376
- async function encryptData(plaintext, address) {
1377
- const key = await getEncryptionKey(address);
1378
- const plaintextBytes = typeof plaintext === "string" ? new TextEncoder().encode(plaintext) : plaintext;
1379
- const iv = crypto.getRandomValues(new Uint8Array(12));
1380
- const encryptedData = await crypto.subtle.encrypt(
1381
- {
1382
- name: "AES-GCM",
1383
- iv
1384
- },
1385
- key,
1386
- plaintextBytes.buffer
1387
- );
1388
- const encryptedBytes = new Uint8Array(encryptedData);
1389
- const combined = new Uint8Array(iv.length + encryptedBytes.length);
1390
- combined.set(iv, 0);
1391
- combined.set(encryptedBytes, iv.length);
1392
- return bytesToHex(combined);
1393
- }
1394
- async function decryptData(encryptedHex, address) {
1395
- const key = await getEncryptionKey(address);
1396
- const combined = hexToBytes(encryptedHex);
1397
- const iv = combined.slice(0, 12);
1398
- const encryptedData = combined.slice(12);
1399
- const decryptedData = await crypto.subtle.decrypt(
1400
- {
1401
- name: "AES-GCM",
1402
- iv
1403
- },
1404
- key,
1405
- encryptedData
1406
- );
1407
- return new TextDecoder().decode(decryptedData);
1408
- }
1409
- async function decryptDataBytes(encryptedHex, address) {
1410
- const key = await getEncryptionKey(address);
1411
- const combined = hexToBytes(encryptedHex);
1412
- const iv = combined.slice(0, 12);
1413
- const encryptedData = combined.slice(12);
1414
- const decryptedData = await crypto.subtle.decrypt(
1415
- {
1416
- name: "AES-GCM",
1417
- iv
1418
- },
1419
- key,
1420
- encryptedData
1421
- );
1422
- return new Uint8Array(decryptedData);
1423
- }
1424
- function hasEncryptionKey(address) {
1425
- return getStoredKey(address) !== null;
1426
- }
1427
- async function requestEncryptionKey(walletAddress, signMessage) {
1428
- const existingKey = getStoredKey(walletAddress);
1429
- if (existingKey) {
1430
- return;
1431
- }
1432
- const signature = await signMessage(SIGN_MESSAGE);
1433
- const encryptionKey = await deriveKeyFromSignature(signature);
1434
- setStoredKey(walletAddress, encryptionKey);
1435
- }
1436
- function useEncryption(signMessage) {
1437
- return {
1438
- requestEncryptionKey: (walletAddress) => requestEncryptionKey(walletAddress, signMessage)
1439
- };
1440
- }
2048
+ // src/react/index.ts
2049
+ init_useEncryption();
1441
2050
 
1442
2051
  // src/react/useChatStorage.ts
1443
2052
  var import_react2 = require("react");
@@ -2614,6 +3223,24 @@ async function getMemoriesByKeyOp(ctx, namespace, key) {
2614
3223
  return results.map(memoryToStored);
2615
3224
  }
2616
3225
  async function saveMemoryOp(ctx, opts) {
3226
+ if (!opts.namespace || typeof opts.namespace !== "string") {
3227
+ throw new Error("Namespace is required and must be a string");
3228
+ }
3229
+ if (!opts.key || typeof opts.key !== "string") {
3230
+ throw new Error("Key is required and must be a string");
3231
+ }
3232
+ if (!opts.value || typeof opts.value !== "string") {
3233
+ throw new Error("Value is required and must be a string");
3234
+ }
3235
+ if (!opts.type || typeof opts.type !== "string") {
3236
+ throw new Error("Type is required and must be a string");
3237
+ }
3238
+ if (typeof opts.confidence !== "number" || opts.confidence < 0 || opts.confidence > 1) {
3239
+ throw new Error("Confidence must be a number between 0 and 1");
3240
+ }
3241
+ if (typeof opts.pii !== "boolean") {
3242
+ throw new Error("PII must be a boolean");
3243
+ }
2617
3244
  const compositeKey = generateCompositeKey(opts.namespace, opts.key);
2618
3245
  const uniqueKey = generateUniqueKey(opts.namespace, opts.key, opts.value);
2619
3246
  const result = await ctx.database.write(async () => {
@@ -3027,28 +3654,302 @@ var generateEmbeddingForText = async (text4, options = {}) => {
3027
3654
  Authorization: `Bearer ${token}`
3028
3655
  }
3029
3656
  });
3030
- if (response.error) {
3031
- throw new Error(
3032
- typeof response.error === "object" && response.error && "error" in response.error ? response.error.error : "API embedding failed"
3033
- );
3657
+ if (response.error) {
3658
+ throw new Error(
3659
+ typeof response.error === "object" && response.error && "error" in response.error ? response.error.error : "API embedding failed"
3660
+ );
3661
+ }
3662
+ if (!response.data?.data?.[0]?.embedding) {
3663
+ throw new Error("No embedding returned from API");
3664
+ }
3665
+ return response.data.data[0].embedding;
3666
+ };
3667
+ var generateEmbeddingForMemory = async (memory, options = {}) => {
3668
+ const text4 = [
3669
+ memory.rawEvidence,
3670
+ memory.type,
3671
+ memory.namespace,
3672
+ memory.key,
3673
+ memory.value
3674
+ ].filter(Boolean).join(" ");
3675
+ return generateEmbeddingForText(text4, options);
3676
+ };
3677
+
3678
+ // src/lib/db/memory/encryption.ts
3679
+ init_useEncryption();
3680
+
3681
+ // src/lib/db/migration.ts
3682
+ init_useEncryption();
3683
+ var ENCRYPTION_PREFIX_V1 = "enc:v1:";
3684
+ var ENCRYPTION_PREFIX_V2 = "enc:v2:";
3685
+ var ENCRYPTION_PREFIX_LEGACY = "enc:";
3686
+ function getEncryptionVersion(value) {
3687
+ if (value.startsWith(ENCRYPTION_PREFIX_V2)) return "v2";
3688
+ if (value.startsWith(ENCRYPTION_PREFIX_V1)) return "v1";
3689
+ if (value.startsWith(ENCRYPTION_PREFIX_LEGACY)) return "v1";
3690
+ return null;
3691
+ }
3692
+ function isOldEncryption(value) {
3693
+ const version2 = getEncryptionVersion(value);
3694
+ return version2 === "v1";
3695
+ }
3696
+ async function encryptNewValue(plaintext, walletAddress) {
3697
+ if (!hasEncryptionKey(walletAddress)) {
3698
+ throw new Error("Encryption key not available for new scheme");
3699
+ }
3700
+ const encrypted = await encryptData(plaintext, walletAddress);
3701
+ return `enc:v2:${encrypted}`;
3702
+ }
3703
+ async function migrateEncryptedValue(encryptedValue, walletAddress, signMessage) {
3704
+ if (getEncryptionVersion(encryptedValue) === "v2") {
3705
+ return encryptedValue;
3706
+ }
3707
+ if (!getEncryptionVersion(encryptedValue)) {
3708
+ return encryptedValue;
3709
+ }
3710
+ if (!hasEncryptionKey(walletAddress)) {
3711
+ const { requestEncryptionKey: requestEncryptionKey2 } = await Promise.resolve().then(() => (init_useEncryption(), useEncryption_exports));
3712
+ await requestEncryptionKey2(walletAddress, signMessage);
3713
+ if (!hasEncryptionKey(walletAddress)) {
3714
+ throw new Error("Encryption key not available after signing");
3715
+ }
3716
+ }
3717
+ let encryptedPayload;
3718
+ if (encryptedValue.startsWith(ENCRYPTION_PREFIX_V1)) {
3719
+ encryptedPayload = encryptedValue.slice(ENCRYPTION_PREFIX_V1.length);
3720
+ } else if (encryptedValue.startsWith(ENCRYPTION_PREFIX_LEGACY)) {
3721
+ encryptedPayload = encryptedValue.slice(ENCRYPTION_PREFIX_LEGACY.length);
3722
+ } else {
3723
+ throw new Error("Invalid old encryption format");
3724
+ }
3725
+ const plaintext = await decryptData(encryptedPayload, walletAddress);
3726
+ return await encryptNewValue(plaintext, walletAddress);
3727
+ }
3728
+
3729
+ // src/lib/db/memory/encryption.ts
3730
+ var ENCRYPTED_FIELDS = ["value", "rawEvidence", "key", "namespace"];
3731
+ var ENCRYPTION_PREFIX_V12 = "enc:v1:";
3732
+ var ENCRYPTION_PREFIX_V22 = "enc:v2:";
3733
+ var ENCRYPTION_PREFIX_LEGACY2 = "enc:";
3734
+ var CURRENT_ENCRYPTION_VERSION = "v2";
3735
+ var ENCRYPTION_PREFIX2 = `enc:${CURRENT_ENCRYPTION_VERSION}:`;
3736
+ var MAX_FIELD_SIZE = 1024 * 1024;
3737
+ function isEncrypted2(value) {
3738
+ return value.startsWith(ENCRYPTION_PREFIX2) || value.startsWith(ENCRYPTION_PREFIX_V12) || value.startsWith(ENCRYPTION_PREFIX_LEGACY2);
3739
+ }
3740
+ function getEncryptionVersion2(value) {
3741
+ if (value.startsWith(ENCRYPTION_PREFIX_V22)) return "v2";
3742
+ if (value.startsWith(ENCRYPTION_PREFIX_V12)) return "v1";
3743
+ if (value.startsWith(ENCRYPTION_PREFIX_LEGACY2)) return "v1";
3744
+ return null;
3745
+ }
3746
+ async function encryptField(value, address) {
3747
+ if (!value || isEncrypted2(value)) {
3748
+ return value;
3749
+ }
3750
+ if (value.length > MAX_FIELD_SIZE) {
3751
+ throw new Error(
3752
+ `Field value too large: ${value.length} bytes (max: ${MAX_FIELD_SIZE} bytes)`
3753
+ );
3754
+ }
3755
+ try {
3756
+ const encrypted = await encryptData(value, address);
3757
+ return `${ENCRYPTION_PREFIX2}${encrypted}`;
3758
+ } catch (error) {
3759
+ const message = error instanceof Error ? error.message : "Unknown encryption error";
3760
+ throw new Error(`Encryption failed: ${message}`);
3761
+ }
3762
+ }
3763
+ var DECRYPTION_FAILED_PLACEHOLDER = "[Decryption Failed]";
3764
+ async function decryptField(value, address, signMessage, onMigrated) {
3765
+ if (!value || !isEncrypted2(value)) {
3766
+ return value;
3767
+ }
3768
+ try {
3769
+ if (isOldEncryption(value) && signMessage && onMigrated) {
3770
+ try {
3771
+ const migratedValue = await migrateEncryptedValue(value, address, signMessage);
3772
+ await onMigrated(migratedValue);
3773
+ const encryptedPayload2 = migratedValue.slice(ENCRYPTION_PREFIX_V22.length);
3774
+ return await decryptData(encryptedPayload2, address);
3775
+ } catch (migrationError) {
3776
+ const errorMessage = migrationError instanceof Error ? migrationError.message : "Unknown migration error";
3777
+ console.warn("Migration failed, attempting decryption with current key:", {
3778
+ error: errorMessage,
3779
+ address
3780
+ });
3781
+ }
3782
+ }
3783
+ let encryptedPayload;
3784
+ if (value.startsWith(ENCRYPTION_PREFIX_V22)) {
3785
+ encryptedPayload = value.slice(ENCRYPTION_PREFIX_V22.length);
3786
+ } else if (value.startsWith(ENCRYPTION_PREFIX_V12)) {
3787
+ encryptedPayload = value.slice(ENCRYPTION_PREFIX_V12.length);
3788
+ } else if (value.startsWith(ENCRYPTION_PREFIX_LEGACY2)) {
3789
+ encryptedPayload = value.slice(ENCRYPTION_PREFIX_LEGACY2.length);
3790
+ } else {
3791
+ return value;
3792
+ }
3793
+ return await decryptData(encryptedPayload, address);
3794
+ } catch (error) {
3795
+ const errorMessage = error instanceof Error ? error.message : "Unknown decryption error";
3796
+ console.error("Decryption failed for field:", {
3797
+ address,
3798
+ valueLength: value.length,
3799
+ error: errorMessage
3800
+ });
3801
+ throw new Error(`Decryption failed: ${errorMessage}`);
3802
+ }
3803
+ }
3804
+ async function encryptMemoryFields(memory, address) {
3805
+ const encrypted = { ...memory };
3806
+ const encryptionPromises = ENCRYPTED_FIELDS.map(async (field3) => {
3807
+ const value = memory[field3];
3808
+ if (typeof value === "string" && value.length > 0) {
3809
+ try {
3810
+ encrypted[field3] = await encryptField(value, address);
3811
+ } catch (error) {
3812
+ throw new Error(`Failed to encrypt field ${field3}: ${error instanceof Error ? error.message : "Unknown error"}`);
3813
+ }
3814
+ }
3815
+ });
3816
+ const results = await Promise.allSettled(encryptionPromises);
3817
+ const failures = results.filter((r) => r.status === "rejected");
3818
+ if (failures.length > 0) {
3819
+ const errorMessages = failures.map(
3820
+ (f) => f.status === "rejected" ? f.reason instanceof Error ? f.reason.message : String(f.reason) : ""
3821
+ ).join("; ");
3822
+ throw new Error(`Encryption failed for ${failures.length} field(s): ${errorMessages}`);
3823
+ }
3824
+ return encrypted;
3825
+ }
3826
+ async function decryptMemoryFields(memory, address, signMessage, updateMemory) {
3827
+ const decrypted = { ...memory };
3828
+ const decryptionPromises = ENCRYPTED_FIELDS.map(async (field3) => {
3829
+ const value = memory[field3];
3830
+ if (typeof value === "string" && value.length > 0) {
3831
+ if (!isEncrypted2(value)) {
3832
+ if (updateMemory && memory.uniqueId) {
3833
+ try {
3834
+ const encryptedValue = await encryptField(value, address);
3835
+ await updateMemory(memory.uniqueId, {
3836
+ [field3]: encryptedValue
3837
+ });
3838
+ decrypted[field3] = value;
3839
+ } catch (error) {
3840
+ if (process.env.NODE_ENV === "development") {
3841
+ console.warn(`Failed to encrypt field ${field3}:`, error);
3842
+ }
3843
+ decrypted[field3] = value;
3844
+ }
3845
+ } else {
3846
+ decrypted[field3] = value;
3847
+ }
3848
+ } else {
3849
+ const onMigrated = updateMemory && memory.uniqueId ? async (migratedValue) => {
3850
+ await updateMemory(memory.uniqueId, {
3851
+ [field3]: migratedValue
3852
+ });
3853
+ } : void 0;
3854
+ decrypted[field3] = await decryptField(value, address, signMessage, onMigrated);
3855
+ }
3856
+ }
3857
+ });
3858
+ await Promise.all(decryptionPromises);
3859
+ return decrypted;
3860
+ }
3861
+ async function encryptMemoriesBatch(memories, address) {
3862
+ return Promise.all(
3863
+ memories.map((memory) => encryptMemoryFields(memory, address))
3864
+ );
3865
+ }
3866
+ async function decryptMemoriesBatch(memories, address, signMessage, updateMemory) {
3867
+ return Promise.all(
3868
+ memories.map(
3869
+ (memory) => decryptMemoryFields(memory, address, signMessage, updateMemory)
3870
+ )
3871
+ );
3872
+ }
3873
+ function hasEncryptedFields(memory) {
3874
+ return ENCRYPTED_FIELDS.some((field3) => {
3875
+ const value = memory[field3];
3876
+ return typeof value === "string" && isEncrypted2(value);
3877
+ });
3878
+ }
3879
+ function needsEncryption(memory) {
3880
+ return ENCRYPTED_FIELDS.some((field3) => {
3881
+ const value = memory[field3];
3882
+ return typeof value === "string" && value.length > 0 && !isEncrypted2(value);
3883
+ });
3884
+ }
3885
+ function extractMemoryFieldsForEncryption(memory) {
3886
+ return {
3887
+ type: memory.type,
3888
+ namespace: memory.namespace,
3889
+ key: memory.key,
3890
+ value: memory.value,
3891
+ rawEvidence: memory.rawEvidence,
3892
+ confidence: memory.confidence,
3893
+ pii: memory.pii
3894
+ };
3895
+ }
3896
+ async function retryEncryption(operation, maxRetries = 3, initialDelay = 500) {
3897
+ let lastError;
3898
+ for (let attempt = 0; attempt <= maxRetries; attempt++) {
3899
+ try {
3900
+ return await operation();
3901
+ } catch (error) {
3902
+ lastError = error;
3903
+ if (attempt === maxRetries) {
3904
+ throw lastError;
3905
+ }
3906
+ const delay = initialDelay * Math.pow(2, attempt);
3907
+ await new Promise((resolve) => setTimeout(resolve, delay));
3908
+ }
3034
3909
  }
3035
- if (!response.data?.data?.[0]?.embedding) {
3036
- throw new Error("No embedding returned from API");
3910
+ throw lastError;
3911
+ }
3912
+ async function encryptMemoriesBatchInPlace(memories, address, updateFn, batchSize = 5) {
3913
+ const failed = [];
3914
+ const errors = [];
3915
+ let success = 0;
3916
+ for (let i = 0; i < memories.length; i += batchSize) {
3917
+ const batch = memories.slice(i, i + batchSize);
3918
+ const results = await Promise.allSettled(
3919
+ batch.map(async (memory) => {
3920
+ await retryEncryption(async () => {
3921
+ const fields = extractMemoryFieldsForEncryption(memory);
3922
+ const encryptedData = await encryptMemoryFields(fields, address);
3923
+ await updateFn(memory.uniqueId, encryptedData);
3924
+ });
3925
+ })
3926
+ );
3927
+ results.forEach((result, index) => {
3928
+ if (result.status === "fulfilled") {
3929
+ success++;
3930
+ } else {
3931
+ const memory = batch[index];
3932
+ if (memory) {
3933
+ failed.push(memory.uniqueId);
3934
+ const errorMessage = result.reason instanceof Error ? result.reason.message : String(result.reason);
3935
+ errors.push({
3936
+ id: memory.uniqueId,
3937
+ error: errorMessage
3938
+ });
3939
+ console.error(
3940
+ "Failed to encrypt memory:",
3941
+ memory.uniqueId,
3942
+ errorMessage
3943
+ );
3944
+ }
3945
+ }
3946
+ });
3037
3947
  }
3038
- return response.data.data[0].embedding;
3039
- };
3040
- var generateEmbeddingForMemory = async (memory, options = {}) => {
3041
- const text4 = [
3042
- memory.rawEvidence,
3043
- memory.type,
3044
- memory.namespace,
3045
- memory.key,
3046
- memory.value
3047
- ].filter(Boolean).join(" ");
3048
- return generateEmbeddingForText(text4, options);
3049
- };
3948
+ return { success, failed, errors };
3949
+ }
3050
3950
 
3051
3951
  // src/react/useMemoryStorage.ts
3952
+ init_useEncryption();
3052
3953
  function useMemoryStorage(options) {
3053
3954
  const {
3054
3955
  database,
@@ -3057,7 +3958,10 @@ function useMemoryStorage(options) {
3057
3958
  generateEmbeddings = true,
3058
3959
  onFactsExtracted,
3059
3960
  getToken,
3060
- baseUrl = BASE_URL
3961
+ baseUrl = BASE_URL,
3962
+ walletAddress,
3963
+ requestEncryptionKey: requestEncryptionKey2,
3964
+ signMessage
3061
3965
  } = options;
3062
3966
  const embeddingModel = userEmbeddingModel === void 0 ? DEFAULT_API_EMBEDDING_MODEL : userEmbeddingModel;
3063
3967
  const [memories, setMemories] = (0, import_react3.useState)([]);
@@ -3085,10 +3989,127 @@ function useMemoryStorage(options) {
3085
3989
  }),
3086
3990
  [effectiveEmbeddingModel, getToken, baseUrl]
3087
3991
  );
3992
+ const isEncryptionEnabled = (0, import_react3.useMemo)(
3993
+ () => !!walletAddress && !!requestEncryptionKey2,
3994
+ [walletAddress, requestEncryptionKey2]
3995
+ );
3996
+ const ensureEncryptionReady = (0, import_react3.useCallback)(async () => {
3997
+ if (!isEncryptionEnabled || !walletAddress) {
3998
+ return null;
3999
+ }
4000
+ if (hasEncryptionKey(walletAddress)) {
4001
+ return walletAddress;
4002
+ }
4003
+ if (requestEncryptionKey2) {
4004
+ try {
4005
+ await requestEncryptionKey2(walletAddress);
4006
+ return walletAddress;
4007
+ } catch {
4008
+ return null;
4009
+ }
4010
+ }
4011
+ return null;
4012
+ }, [isEncryptionEnabled, walletAddress, requestEncryptionKey2]);
4013
+ const getSignMessageForMigration = (0, import_react3.useCallback)(() => {
4014
+ return signMessage;
4015
+ }, [signMessage]);
4016
+ const encryptUnencryptedMemories = (0, import_react3.useCallback)(
4017
+ async (address) => {
4018
+ if (!isEncryptionEnabled) return;
4019
+ try {
4020
+ const allMemories = await getAllMemoriesOp(storageCtx);
4021
+ if (!allMemories || allMemories.length === 0) return;
4022
+ const memoriesToEncrypt = allMemories.filter(
4023
+ (m) => needsEncryption(m)
4024
+ );
4025
+ if (memoriesToEncrypt.length === 0) return;
4026
+ await encryptMemoriesBatchInPlace(
4027
+ memoriesToEncrypt,
4028
+ address,
4029
+ async (id, data) => {
4030
+ const result = await updateMemoryOp(storageCtx, id, data);
4031
+ if (!result.ok) {
4032
+ throw new Error(`Failed to update memory ${id}`);
4033
+ }
4034
+ }
4035
+ );
4036
+ } catch (error) {
4037
+ if (process.env.NODE_ENV === "development") {
4038
+ console.error("Failed to encrypt unencrypted memories:", error);
4039
+ }
4040
+ }
4041
+ },
4042
+ [isEncryptionEnabled, storageCtx]
4043
+ );
4044
+ const encryptionLockRef = (0, import_react3.useRef)(null);
4045
+ const autoEncryptionRunRef = (0, import_react3.useRef)(false);
4046
+ (0, import_react3.useEffect)(() => {
4047
+ if (!isEncryptionEnabled || !walletAddress) {
4048
+ autoEncryptionRunRef.current = false;
4049
+ encryptionLockRef.current = null;
4050
+ return;
4051
+ }
4052
+ if (encryptionLockRef.current) {
4053
+ encryptionLockRef.current.then(() => {
4054
+ if (isEncryptionEnabled && walletAddress && !autoEncryptionRunRef.current) {
4055
+ ensureEncryptionReady().then((address) => {
4056
+ if (address) {
4057
+ autoEncryptionRunRef.current = true;
4058
+ const encryptionPromise2 = encryptUnencryptedMemories(address).catch((err) => {
4059
+ if (process.env.NODE_ENV === "development") {
4060
+ console.error("Failed to automatically encrypt memories:", err);
4061
+ }
4062
+ }).finally(() => {
4063
+ encryptionLockRef.current = null;
4064
+ });
4065
+ encryptionLockRef.current = encryptionPromise2;
4066
+ }
4067
+ });
4068
+ }
4069
+ });
4070
+ return;
4071
+ }
4072
+ if (autoEncryptionRunRef.current) {
4073
+ return;
4074
+ }
4075
+ autoEncryptionRunRef.current = true;
4076
+ const encryptionPromise = ensureEncryptionReady().then((address) => {
4077
+ if (address) {
4078
+ return encryptUnencryptedMemories(address);
4079
+ }
4080
+ }).catch((err) => {
4081
+ if (process.env.NODE_ENV === "development") {
4082
+ console.error("Failed to automatically encrypt memories:", err);
4083
+ }
4084
+ }).finally(() => {
4085
+ encryptionLockRef.current = null;
4086
+ });
4087
+ encryptionLockRef.current = encryptionPromise;
4088
+ }, [isEncryptionEnabled, walletAddress, ensureEncryptionReady, encryptUnencryptedMemories]);
3088
4089
  const refreshMemories = (0, import_react3.useCallback)(async () => {
3089
4090
  const storedMemories = await getAllMemoriesOp(storageCtx);
4091
+ if (isEncryptionEnabled && walletAddress) {
4092
+ const address = await ensureEncryptionReady();
4093
+ if (address) {
4094
+ const signMsg = getSignMessageForMigration();
4095
+ const updateMemoryFn = async (id, data) => {
4096
+ const result = await updateMemoryOp(storageCtx, id, data);
4097
+ if (!result.ok) {
4098
+ throw new Error(`Failed to update memory ${id} during migration`);
4099
+ }
4100
+ };
4101
+ const decrypted = await decryptMemoriesBatch(
4102
+ storedMemories,
4103
+ address,
4104
+ signMsg,
4105
+ updateMemoryFn
4106
+ );
4107
+ setMemories(decrypted);
4108
+ return;
4109
+ }
4110
+ }
3090
4111
  setMemories(storedMemories);
3091
- }, [storageCtx]);
4112
+ }, [storageCtx, isEncryptionEnabled, walletAddress, ensureEncryptionReady, getSignMessageForMigration]);
3092
4113
  const extractMemoriesFromMessage = (0, import_react3.useCallback)(
3093
4114
  async (opts) => {
3094
4115
  const { messages, model } = opts;
@@ -3222,21 +4243,38 @@ function useMemoryStorage(options) {
3222
4243
  pii: item.pii
3223
4244
  })
3224
4245
  );
3225
- const savedMemories = await saveMemoriesOp(storageCtx, createOptions);
4246
+ let optionsToSave = createOptions;
4247
+ if (isEncryptionEnabled && walletAddress) {
4248
+ const address = await ensureEncryptionReady();
4249
+ if (address) {
4250
+ optionsToSave = await Promise.all(
4251
+ createOptions.map(async (opt) => {
4252
+ const encrypted = await encryptMemoryFields(
4253
+ opt,
4254
+ address
4255
+ );
4256
+ return encrypted;
4257
+ })
4258
+ );
4259
+ }
4260
+ }
4261
+ const savedMemories = await saveMemoriesOp(storageCtx, optionsToSave);
3226
4262
  console.log(
3227
4263
  `Saved ${savedMemories.length} memories to WatermelonDB`
3228
4264
  );
3229
4265
  if (generateEmbeddings && embeddingModel) {
3230
4266
  try {
3231
- for (const saved of savedMemories) {
4267
+ for (let i = 0; i < savedMemories.length; i++) {
4268
+ const memory = createOptions[i];
4269
+ const saved = savedMemories[i];
3232
4270
  const memoryItem = {
3233
- type: saved.type,
3234
- namespace: saved.namespace,
3235
- key: saved.key,
3236
- value: saved.value,
3237
- rawEvidence: saved.rawEvidence,
3238
- confidence: saved.confidence,
3239
- pii: saved.pii
4271
+ type: memory.type,
4272
+ namespace: memory.namespace,
4273
+ key: memory.key,
4274
+ value: memory.value,
4275
+ rawEvidence: memory.rawEvidence,
4276
+ confidence: memory.confidence,
4277
+ pii: memory.pii
3240
4278
  };
3241
4279
  const embedding = await generateEmbeddingForMemory(
3242
4280
  memoryItem,
@@ -3257,6 +4295,19 @@ function useMemoryStorage(options) {
3257
4295
  }
3258
4296
  }
3259
4297
  await refreshMemories();
4298
+ if (isEncryptionEnabled && walletAddress && !encryptionLockRef.current) {
4299
+ const address = await ensureEncryptionReady();
4300
+ if (address) {
4301
+ const encryptionPromise = encryptUnencryptedMemories(address).catch((err) => {
4302
+ if (process.env.NODE_ENV === "development") {
4303
+ console.error("Failed to encrypt memories:", err);
4304
+ }
4305
+ }).finally(() => {
4306
+ encryptionLockRef.current = null;
4307
+ });
4308
+ encryptionLockRef.current = encryptionPromise;
4309
+ }
4310
+ }
3260
4311
  } catch (error) {
3261
4312
  console.error("Failed to save memories to WatermelonDB:", error);
3262
4313
  }
@@ -3281,7 +4332,11 @@ function useMemoryStorage(options) {
3281
4332
  onFactsExtracted,
3282
4333
  baseUrl,
3283
4334
  storageCtx,
3284
- refreshMemories
4335
+ refreshMemories,
4336
+ isEncryptionEnabled,
4337
+ walletAddress,
4338
+ ensureEncryptionReady,
4339
+ encryptUnencryptedMemories
3285
4340
  ]
3286
4341
  );
3287
4342
  const searchMemories = (0, import_react3.useCallback)(
@@ -3301,6 +4356,25 @@ function useMemoryStorage(options) {
3301
4356
  limit,
3302
4357
  minSimilarity
3303
4358
  );
4359
+ if (isEncryptionEnabled && walletAddress && results.length > 0) {
4360
+ const address = await ensureEncryptionReady();
4361
+ if (address) {
4362
+ const signMsg = getSignMessageForMigration();
4363
+ const updateMemoryFn = async (id, data) => {
4364
+ const result = await updateMemoryOp(storageCtx, id, data);
4365
+ if (!result.ok) {
4366
+ throw new Error(`Failed to update memory ${id} during migration`);
4367
+ }
4368
+ };
4369
+ const decrypted = await decryptMemoriesBatch(
4370
+ results,
4371
+ address,
4372
+ signMsg,
4373
+ updateMemoryFn
4374
+ );
4375
+ return decrypted;
4376
+ }
4377
+ }
3304
4378
  if (results.length === 0) {
3305
4379
  console.warn(
3306
4380
  `[Memory Search] No memories found above similarity threshold ${minSimilarity}.`
@@ -3315,31 +4389,71 @@ function useMemoryStorage(options) {
3315
4389
  return [];
3316
4390
  }
3317
4391
  },
3318
- [embeddingModel, embeddingOptions, storageCtx]
4392
+ [embeddingModel, embeddingOptions, storageCtx, isEncryptionEnabled, walletAddress, ensureEncryptionReady, getSignMessageForMigration]
3319
4393
  );
3320
4394
  const fetchAllMemories = (0, import_react3.useCallback)(async () => {
3321
4395
  try {
3322
- return await getAllMemoriesOp(storageCtx);
4396
+ const memories2 = await getAllMemoriesOp(storageCtx);
4397
+ if (isEncryptionEnabled && walletAddress && memories2.length > 0) {
4398
+ const address = await ensureEncryptionReady();
4399
+ if (address) {
4400
+ const signMsg = getSignMessageForMigration();
4401
+ const updateMemoryFn = async (id, data) => {
4402
+ const result = await updateMemoryOp(storageCtx, id, data);
4403
+ if (!result.ok) {
4404
+ throw new Error(`Failed to update memory ${id} during migration`);
4405
+ }
4406
+ };
4407
+ const decrypted = await decryptMemoriesBatch(
4408
+ memories2,
4409
+ address,
4410
+ signMsg,
4411
+ updateMemoryFn
4412
+ );
4413
+ return decrypted;
4414
+ }
4415
+ }
4416
+ return memories2;
3323
4417
  } catch (error) {
3324
4418
  throw new Error(
3325
4419
  "Failed to fetch all memories: " + (error instanceof Error ? error.message : String(error))
3326
4420
  );
3327
4421
  }
3328
- }, [storageCtx]);
4422
+ }, [storageCtx, isEncryptionEnabled, walletAddress, ensureEncryptionReady, getSignMessageForMigration]);
3329
4423
  const fetchMemoriesByNamespace = (0, import_react3.useCallback)(
3330
4424
  async (namespace) => {
3331
4425
  if (!namespace) {
3332
4426
  throw new Error("Missing required field: namespace");
3333
4427
  }
3334
4428
  try {
3335
- return await getMemoriesByNamespaceOp(storageCtx, namespace);
4429
+ const memories2 = await getMemoriesByNamespaceOp(storageCtx, namespace);
4430
+ if (isEncryptionEnabled && walletAddress && memories2.length > 0) {
4431
+ const address = await ensureEncryptionReady();
4432
+ if (address) {
4433
+ const signMsg = getSignMessageForMigration();
4434
+ const updateMemoryFn = async (id, data) => {
4435
+ const result = await updateMemoryOp(storageCtx, id, data);
4436
+ if (!result.ok) {
4437
+ throw new Error(`Failed to update memory ${id} during migration`);
4438
+ }
4439
+ };
4440
+ const decrypted = await decryptMemoriesBatch(
4441
+ memories2,
4442
+ address,
4443
+ signMsg,
4444
+ updateMemoryFn
4445
+ );
4446
+ return decrypted;
4447
+ }
4448
+ }
4449
+ return memories2;
3336
4450
  } catch (error) {
3337
4451
  throw new Error(
3338
4452
  `Failed to fetch memories for namespace "${namespace}": ` + (error instanceof Error ? error.message : String(error))
3339
4453
  );
3340
4454
  }
3341
4455
  },
3342
- [storageCtx]
4456
+ [storageCtx, isEncryptionEnabled, walletAddress, ensureEncryptionReady, getSignMessageForMigration]
3343
4457
  );
3344
4458
  const fetchMemoriesByKey = (0, import_react3.useCallback)(
3345
4459
  async (namespace, key) => {
@@ -3347,31 +4461,82 @@ function useMemoryStorage(options) {
3347
4461
  throw new Error("Missing required fields: namespace, key");
3348
4462
  }
3349
4463
  try {
3350
- return await getMemoriesByKeyOp(storageCtx, namespace, key);
4464
+ const memories2 = await getMemoriesByKeyOp(storageCtx, namespace, key);
4465
+ if (isEncryptionEnabled && walletAddress && memories2.length > 0) {
4466
+ const address = await ensureEncryptionReady();
4467
+ if (address) {
4468
+ const signMsg = getSignMessageForMigration();
4469
+ const updateMemoryFn = async (id, data) => {
4470
+ const result = await updateMemoryOp(storageCtx, id, data);
4471
+ if (!result.ok) {
4472
+ throw new Error(`Failed to update memory ${id} during migration`);
4473
+ }
4474
+ };
4475
+ const decrypted = await decryptMemoriesBatch(
4476
+ memories2,
4477
+ address,
4478
+ signMsg,
4479
+ updateMemoryFn
4480
+ );
4481
+ return decrypted;
4482
+ }
4483
+ }
4484
+ return memories2;
3351
4485
  } catch (error) {
3352
4486
  throw new Error(
3353
4487
  `Failed to fetch memories for "${namespace}:${key}": ` + (error instanceof Error ? error.message : String(error))
3354
4488
  );
3355
4489
  }
3356
4490
  },
3357
- [storageCtx]
4491
+ [storageCtx, isEncryptionEnabled, walletAddress, ensureEncryptionReady, getSignMessageForMigration]
3358
4492
  );
3359
4493
  const getMemoryById = (0, import_react3.useCallback)(
3360
4494
  async (id) => {
3361
4495
  try {
3362
- return await getMemoryByIdOp(storageCtx, id);
4496
+ const memory = await getMemoryByIdOp(storageCtx, id);
4497
+ if (!memory) return null;
4498
+ if (isEncryptionEnabled && walletAddress) {
4499
+ const address = await ensureEncryptionReady();
4500
+ if (address) {
4501
+ const signMsg = getSignMessageForMigration();
4502
+ const updateMemoryFn = async (id2, data) => {
4503
+ const result = await updateMemoryOp(storageCtx, id2, data);
4504
+ if (!result.ok) {
4505
+ throw new Error(`Failed to update memory ${id2} during migration`);
4506
+ }
4507
+ };
4508
+ const decrypted = await decryptMemoryFields(
4509
+ memory,
4510
+ address,
4511
+ signMsg,
4512
+ updateMemoryFn
4513
+ );
4514
+ return decrypted;
4515
+ }
4516
+ }
4517
+ return memory;
3363
4518
  } catch (error) {
3364
4519
  throw new Error(
3365
4520
  `Failed to get memory ${id}: ` + (error instanceof Error ? error.message : String(error))
3366
4521
  );
3367
4522
  }
3368
4523
  },
3369
- [storageCtx]
4524
+ [storageCtx, isEncryptionEnabled, walletAddress, ensureEncryptionReady, getSignMessageForMigration]
3370
4525
  );
3371
4526
  const saveMemory = (0, import_react3.useCallback)(
3372
4527
  async (memory) => {
3373
4528
  try {
3374
- const saved = await saveMemoryOp(storageCtx, memory);
4529
+ let memoryToSave = memory;
4530
+ if (isEncryptionEnabled && walletAddress) {
4531
+ const address = await ensureEncryptionReady();
4532
+ if (address) {
4533
+ memoryToSave = await encryptMemoryFields(
4534
+ memory,
4535
+ address
4536
+ );
4537
+ }
4538
+ }
4539
+ const saved = await saveMemoryOp(storageCtx, memoryToSave);
3375
4540
  if (generateEmbeddings && embeddingModel && !memory.embedding) {
3376
4541
  try {
3377
4542
  const memoryItem = {
@@ -3397,26 +4562,60 @@ function useMemoryStorage(options) {
3397
4562
  console.error("Failed to generate embedding:", error);
3398
4563
  }
3399
4564
  }
4565
+ let savedForState = saved;
4566
+ if (isEncryptionEnabled && walletAddress) {
4567
+ const address = await ensureEncryptionReady();
4568
+ if (address) {
4569
+ const signMsg = getSignMessageForMigration();
4570
+ const updateMemoryFn = async (id, data) => {
4571
+ const result = await updateMemoryOp(storageCtx, id, data);
4572
+ if (!result.ok) {
4573
+ throw new Error(`Failed to update memory ${id} during migration`);
4574
+ }
4575
+ };
4576
+ savedForState = await decryptMemoryFields(
4577
+ saved,
4578
+ address,
4579
+ signMsg,
4580
+ updateMemoryFn
4581
+ );
4582
+ }
4583
+ }
3400
4584
  setMemories((prev) => {
3401
- const existing = prev.find((m) => m.uniqueId === saved.uniqueId);
4585
+ const existing = prev.find((m) => m.uniqueId === savedForState.uniqueId);
3402
4586
  if (existing) {
3403
- return prev.map((m) => m.uniqueId === saved.uniqueId ? saved : m);
4587
+ return prev.map((m) => m.uniqueId === savedForState.uniqueId ? savedForState : m);
3404
4588
  }
3405
- return [saved, ...prev];
4589
+ return [savedForState, ...prev];
3406
4590
  });
3407
- return saved;
4591
+ return savedForState;
3408
4592
  } catch (error) {
3409
4593
  throw new Error(
3410
4594
  "Failed to save memory: " + (error instanceof Error ? error.message : String(error))
3411
4595
  );
3412
4596
  }
3413
4597
  },
3414
- [storageCtx, generateEmbeddings, embeddingModel, embeddingOptions]
4598
+ [storageCtx, generateEmbeddings, embeddingModel, embeddingOptions, isEncryptionEnabled, walletAddress, ensureEncryptionReady, getSignMessageForMigration]
3415
4599
  );
3416
4600
  const saveMemories = (0, import_react3.useCallback)(
3417
4601
  async (memoriesToSave) => {
3418
4602
  try {
3419
- const saved = await saveMemoriesOp(storageCtx, memoriesToSave);
4603
+ let optionsToSave = memoriesToSave;
4604
+ if (isEncryptionEnabled && walletAddress) {
4605
+ const address = await ensureEncryptionReady();
4606
+ if (address) {
4607
+ optionsToSave = await Promise.all(
4608
+ memoriesToSave.map(async (opt) => {
4609
+ const encrypted = await encryptMemoryFields(
4610
+ opt,
4611
+ address
4612
+ );
4613
+ return encrypted;
4614
+ })
4615
+ );
4616
+ }
4617
+ }
4618
+ const saved = await saveMemoriesOp(storageCtx, optionsToSave);
3420
4619
  if (generateEmbeddings && embeddingModel) {
3421
4620
  for (let i = 0; i < saved.length; i++) {
3422
4621
  const memory = memoriesToSave[i];
@@ -3447,8 +4646,27 @@ function useMemoryStorage(options) {
3447
4646
  }
3448
4647
  }
3449
4648
  }
4649
+ let savedForReturn = saved;
4650
+ if (isEncryptionEnabled && walletAddress) {
4651
+ const address = await ensureEncryptionReady();
4652
+ if (address) {
4653
+ const signMsg = getSignMessageForMigration();
4654
+ const updateMemoryFn = async (id, data) => {
4655
+ const result = await updateMemoryOp(storageCtx, id, data);
4656
+ if (!result.ok) {
4657
+ throw new Error(`Failed to update memory ${id} during migration`);
4658
+ }
4659
+ };
4660
+ savedForReturn = await decryptMemoriesBatch(
4661
+ saved,
4662
+ address,
4663
+ signMsg,
4664
+ updateMemoryFn
4665
+ );
4666
+ }
4667
+ }
3450
4668
  await refreshMemories();
3451
- return saved;
4669
+ return savedForReturn;
3452
4670
  } catch (error) {
3453
4671
  throw new Error(
3454
4672
  "Failed to save memories: " + (error instanceof Error ? error.message : String(error))
@@ -3460,12 +4678,56 @@ function useMemoryStorage(options) {
3460
4678
  generateEmbeddings,
3461
4679
  embeddingModel,
3462
4680
  embeddingOptions,
3463
- refreshMemories
4681
+ refreshMemories,
4682
+ isEncryptionEnabled,
4683
+ walletAddress,
4684
+ ensureEncryptionReady,
4685
+ getSignMessageForMigration
3464
4686
  ]
3465
4687
  );
3466
4688
  const updateMemory = (0, import_react3.useCallback)(
3467
4689
  async (id, updates) => {
3468
- const result = await updateMemoryOp(storageCtx, id, updates);
4690
+ let updatesToSave = updates;
4691
+ if (isEncryptionEnabled && walletAddress) {
4692
+ const address = await ensureEncryptionReady();
4693
+ if (address) {
4694
+ try {
4695
+ const fieldsToEncrypt = {};
4696
+ if (updates.value !== void 0) {
4697
+ fieldsToEncrypt.value = updates.value;
4698
+ }
4699
+ if (updates.rawEvidence !== void 0) {
4700
+ fieldsToEncrypt.rawEvidence = updates.rawEvidence;
4701
+ }
4702
+ if (updates.key !== void 0) {
4703
+ fieldsToEncrypt.key = updates.key;
4704
+ }
4705
+ if (updates.namespace !== void 0) {
4706
+ fieldsToEncrypt.namespace = updates.namespace;
4707
+ }
4708
+ const encrypted = await encryptMemoryFields(fieldsToEncrypt, address);
4709
+ const encryptedUpdates = { ...updates };
4710
+ if (updates.value !== void 0) {
4711
+ encryptedUpdates.value = encrypted.value;
4712
+ }
4713
+ if (updates.rawEvidence !== void 0) {
4714
+ encryptedUpdates.rawEvidence = encrypted.rawEvidence;
4715
+ }
4716
+ if (updates.key !== void 0) {
4717
+ encryptedUpdates.key = encrypted.key;
4718
+ }
4719
+ if (updates.namespace !== void 0) {
4720
+ encryptedUpdates.namespace = encrypted.namespace;
4721
+ }
4722
+ updatesToSave = encryptedUpdates;
4723
+ } catch (error) {
4724
+ throw new Error(
4725
+ `Failed to encrypt memory fields: ${error instanceof Error ? error.message : "Unknown error"}`
4726
+ );
4727
+ }
4728
+ }
4729
+ }
4730
+ const result = await updateMemoryOp(storageCtx, id, updatesToSave);
3469
4731
  if (!result.ok) {
3470
4732
  if (result.reason === "not_found") {
3471
4733
  return null;
@@ -3479,7 +4741,25 @@ function useMemoryStorage(options) {
3479
4741
  `Failed to update memory ${id}: ${result.error.message}`
3480
4742
  );
3481
4743
  }
3482
- const updated = result.memory;
4744
+ let updated = result.memory;
4745
+ if (isEncryptionEnabled && walletAddress) {
4746
+ const address = await ensureEncryptionReady();
4747
+ if (address) {
4748
+ const signMsg = getSignMessageForMigration();
4749
+ const updateMemoryFn = async (id2, data) => {
4750
+ const result2 = await updateMemoryOp(storageCtx, id2, data);
4751
+ if (!result2.ok) {
4752
+ throw new Error(`Failed to update memory ${id2} during migration`);
4753
+ }
4754
+ };
4755
+ updated = await decryptMemoryFields(
4756
+ updated,
4757
+ address,
4758
+ signMsg,
4759
+ updateMemoryFn
4760
+ );
4761
+ }
4762
+ }
3483
4763
  const contentChanged = updates.value !== void 0 || updates.rawEvidence !== void 0 || updates.type !== void 0 || updates.namespace !== void 0 || updates.key !== void 0;
3484
4764
  if (contentChanged && generateEmbeddings && embeddingModel && !updates.embedding) {
3485
4765
  try {
@@ -3511,7 +4791,7 @@ function useMemoryStorage(options) {
3511
4791
  );
3512
4792
  return updated;
3513
4793
  },
3514
- [storageCtx, generateEmbeddings, embeddingModel, embeddingOptions]
4794
+ [storageCtx, generateEmbeddings, embeddingModel, embeddingOptions, isEncryptionEnabled, walletAddress, ensureEncryptionReady, getSignMessageForMigration]
3515
4795
  );
3516
4796
  const removeMemory = (0, import_react3.useCallback)(
3517
4797
  async (namespace, key, value) => {
@@ -4517,63 +5797,8 @@ async function performDropboxImport(userAddress, token, deps, onProgress, backup
4517
5797
  // src/react/useDropboxAuth.ts
4518
5798
  var import_react10 = require("react");
4519
5799
 
4520
- // src/lib/backup/oauth/storage.ts
4521
- var STORAGE_KEY_PREFIX = "oauth_token_";
4522
- function getStorageKey(provider) {
4523
- return `${STORAGE_KEY_PREFIX}${provider}`;
4524
- }
4525
- function getStoredTokenData(provider) {
4526
- if (typeof window === "undefined") return null;
4527
- try {
4528
- const stored = localStorage.getItem(getStorageKey(provider));
4529
- if (!stored) return null;
4530
- const data = JSON.parse(stored);
4531
- if (!data.accessToken) return null;
4532
- return data;
4533
- } catch {
4534
- return null;
4535
- }
4536
- }
4537
- function storeTokenData(provider, data) {
4538
- if (typeof window === "undefined") return;
4539
- localStorage.setItem(getStorageKey(provider), JSON.stringify(data));
4540
- }
4541
- function clearTokenData(provider) {
4542
- if (typeof window === "undefined") return;
4543
- localStorage.removeItem(getStorageKey(provider));
4544
- }
4545
- function isTokenExpired(data, bufferSeconds = 60) {
4546
- if (!data) return true;
4547
- if (!data.expiresAt) return false;
4548
- const now = Date.now();
4549
- const bufferMs = bufferSeconds * 1e3;
4550
- return data.expiresAt - bufferMs <= now;
4551
- }
4552
- function getValidAccessToken(provider) {
4553
- const data = getStoredTokenData(provider);
4554
- if (!data) return null;
4555
- if (data.expiresAt && isTokenExpired(data)) {
4556
- return null;
4557
- }
4558
- return data.accessToken;
4559
- }
4560
- function getRefreshToken(provider) {
4561
- const data = getStoredTokenData(provider);
4562
- return data?.refreshToken ?? null;
4563
- }
4564
- function tokenResponseToStoredData(accessToken, expiresIn, refreshToken, scope) {
4565
- const data = {
4566
- accessToken,
4567
- refreshToken,
4568
- scope
4569
- };
4570
- if (expiresIn) {
4571
- data.expiresAt = Date.now() + expiresIn * 1e3;
4572
- }
4573
- return data;
4574
- }
4575
-
4576
5800
  // src/lib/backup/dropbox/auth.ts
5801
+ init_storage();
4577
5802
  var PROVIDER = "dropbox";
4578
5803
  var STATE_STORAGE_KEY = "dropbox_oauth_state";
4579
5804
  var DROPBOX_AUTH_URL = "https://www.dropbox.com/oauth2/authorize";
@@ -4581,6 +5806,7 @@ function getRedirectUri(callbackPath) {
4581
5806
  if (typeof window === "undefined") return "";
4582
5807
  return `${window.location.origin}${callbackPath}`;
4583
5808
  }
5809
+ var MAX_STATE_AGE_MS = 10 * 60 * 1e3;
4584
5810
  function generateState() {
4585
5811
  const array = new Uint8Array(16);
4586
5812
  crypto.getRandomValues(array);
@@ -4590,21 +5816,48 @@ function generateState() {
4590
5816
  }
4591
5817
  function storeOAuthState(state) {
4592
5818
  if (typeof window === "undefined") return;
4593
- sessionStorage.setItem(STATE_STORAGE_KEY, state);
5819
+ const stateData = {
5820
+ state,
5821
+ timestamp: Date.now()
5822
+ };
5823
+ sessionStorage.setItem(STATE_STORAGE_KEY, JSON.stringify(stateData));
4594
5824
  }
4595
5825
  function getAndClearOAuthState() {
4596
5826
  if (typeof window === "undefined") return null;
4597
- const state = sessionStorage.getItem(STATE_STORAGE_KEY);
4598
- sessionStorage.removeItem(STATE_STORAGE_KEY);
4599
- return state;
5827
+ try {
5828
+ const stored = sessionStorage.getItem(STATE_STORAGE_KEY);
5829
+ if (!stored) return null;
5830
+ const stateData = JSON.parse(stored);
5831
+ const age = Date.now() - stateData.timestamp;
5832
+ if (age > MAX_STATE_AGE_MS) {
5833
+ sessionStorage.removeItem(STATE_STORAGE_KEY);
5834
+ return null;
5835
+ }
5836
+ sessionStorage.removeItem(STATE_STORAGE_KEY);
5837
+ return stateData.state;
5838
+ } catch {
5839
+ sessionStorage.removeItem(STATE_STORAGE_KEY);
5840
+ return null;
5841
+ }
4600
5842
  }
4601
5843
  function isDropboxCallback() {
4602
5844
  if (typeof window === "undefined") return false;
4603
5845
  const url = new URL(window.location.href);
4604
5846
  const code = url.searchParams.get("code");
4605
5847
  const state = url.searchParams.get("state");
4606
- const storedState = sessionStorage.getItem(STATE_STORAGE_KEY);
4607
- return !!code && !!state && state === storedState;
5848
+ if (!code || !state) return false;
5849
+ try {
5850
+ const stored = sessionStorage.getItem(STATE_STORAGE_KEY);
5851
+ if (!stored) return false;
5852
+ const stateData = JSON.parse(stored);
5853
+ const age = Date.now() - stateData.timestamp;
5854
+ if (age > MAX_STATE_AGE_MS) {
5855
+ return false;
5856
+ }
5857
+ return state === stateData.state;
5858
+ } catch {
5859
+ return false;
5860
+ }
4608
5861
  }
4609
5862
  async function handleDropboxCallback(callbackPath, apiClient) {
4610
5863
  if (typeof window === "undefined") return null;
@@ -4633,15 +5886,35 @@ async function handleDropboxCallback(callbackPath, apiClient) {
4633
5886
  response.data.refresh_token,
4634
5887
  response.data.scope
4635
5888
  );
4636
- storeTokenData(PROVIDER, tokenData);
5889
+ try {
5890
+ await storeTokenData(PROVIDER, tokenData);
5891
+ } catch (error) {
5892
+ const errorMessage = error instanceof Error ? error.message : "Unknown encryption error";
5893
+ console.error("OAuth token encryption failed in Dropbox callback:", errorMessage);
5894
+ console.warn("OAuth token encryption failed - tokens not stored securely");
5895
+ return null;
5896
+ }
4637
5897
  window.history.replaceState({}, "", window.location.pathname);
4638
5898
  return response.data.access_token;
4639
- } catch {
5899
+ } catch (error) {
5900
+ const errorMessage = error instanceof Error ? error.message : "Unknown error";
5901
+ const errorName = error instanceof Error ? error.name : "Error";
5902
+ console.error(`Dropbox OAuth callback error: ${errorName}: ${errorMessage}`);
5903
+ console.error("Error details:", {
5904
+ name: errorName,
5905
+ message: errorMessage,
5906
+ stack: error instanceof Error ? error.stack : void 0
5907
+ });
5908
+ console.warn(`Dropbox OAuth callback failed: ${errorMessage}`);
5909
+ if (errorMessage.includes("encryption failed") || errorMessage.includes("OAuth token encryption")) {
5910
+ console.warn("OAuth callback failed due to encryption error - this is a security issue");
5911
+ throw error;
5912
+ }
4640
5913
  return null;
4641
5914
  }
4642
5915
  }
4643
- async function refreshDropboxToken(apiClient) {
4644
- const refreshToken = getRefreshToken(PROVIDER);
5916
+ async function refreshDropboxToken(apiClient, walletAddress) {
5917
+ const refreshToken = walletAddress ? await getRefreshToken(PROVIDER, walletAddress) : getRefreshTokenSync(PROVIDER);
4645
5918
  if (!refreshToken) return null;
4646
5919
  try {
4647
5920
  const response = await postAuthOauthByProviderRefresh({
@@ -4652,22 +5925,22 @@ async function refreshDropboxToken(apiClient) {
4652
5925
  if (!response.data?.access_token) {
4653
5926
  throw new Error("No access token in refresh response");
4654
5927
  }
4655
- const currentData = getStoredTokenData(PROVIDER);
5928
+ const currentData = walletAddress ? await getStoredTokenData(PROVIDER, walletAddress) : getStoredTokenDataSync(PROVIDER);
4656
5929
  const tokenData = tokenResponseToStoredData(
4657
5930
  response.data.access_token,
4658
5931
  response.data.expires_in,
4659
5932
  response.data.refresh_token ?? currentData?.refreshToken,
4660
5933
  response.data.scope ?? currentData?.scope
4661
5934
  );
4662
- storeTokenData(PROVIDER, tokenData);
5935
+ await storeTokenData(PROVIDER, tokenData, walletAddress);
4663
5936
  return response.data.access_token;
4664
5937
  } catch {
4665
5938
  clearTokenData(PROVIDER);
4666
5939
  return null;
4667
5940
  }
4668
5941
  }
4669
- async function revokeDropboxToken(apiClient) {
4670
- const tokenData = getStoredTokenData(PROVIDER);
5942
+ async function revokeDropboxToken(apiClient, walletAddress) {
5943
+ const tokenData = walletAddress ? await getStoredTokenData(PROVIDER, walletAddress) : getStoredTokenDataSync(PROVIDER);
4671
5944
  if (!tokenData) return;
4672
5945
  try {
4673
5946
  const tokenToRevoke = tokenData.refreshToken ?? tokenData.accessToken;
@@ -4681,10 +5954,10 @@ async function revokeDropboxToken(apiClient) {
4681
5954
  clearTokenData(PROVIDER);
4682
5955
  }
4683
5956
  }
4684
- async function getDropboxAccessToken(apiClient) {
4685
- const validToken = getValidAccessToken(PROVIDER);
5957
+ async function getDropboxAccessToken(apiClient, walletAddress) {
5958
+ const validToken = walletAddress ? await getValidAccessToken(PROVIDER, walletAddress) : getValidAccessTokenSync(PROVIDER);
4686
5959
  if (validToken) return validToken;
4687
- return refreshDropboxToken(apiClient);
5960
+ return refreshDropboxToken(apiClient, walletAddress);
4688
5961
  }
4689
5962
  async function startDropboxAuth(appKey, callbackPath) {
4690
5963
  const state = generateState();
@@ -4705,8 +5978,7 @@ function clearToken() {
4705
5978
  clearTokenData(PROVIDER);
4706
5979
  }
4707
5980
  function hasDropboxCredentials() {
4708
- const data = getStoredTokenData(PROVIDER);
4709
- return !!(data?.accessToken || data?.refreshToken);
5981
+ return hasStoredCredentialsSync(PROVIDER);
4710
5982
  }
4711
5983
 
4712
5984
  // src/react/useDropboxAuth.ts
@@ -4885,6 +6157,7 @@ function useDropboxBackup(options) {
4885
6157
  var import_react12 = require("react");
4886
6158
 
4887
6159
  // src/lib/backup/google/auth.ts
6160
+ init_storage();
4888
6161
  var PROVIDER2 = "google-drive";
4889
6162
  var CODE_STORAGE_KEY = "google_oauth_state";
4890
6163
  var GOOGLE_AUTH_URL = "https://accounts.google.com/o/oauth2/v2/auth";
@@ -4896,6 +6169,7 @@ function getRedirectUri2(callbackPath) {
4896
6169
  if (typeof window === "undefined") return "";
4897
6170
  return `${window.location.origin}${callbackPath}`;
4898
6171
  }
6172
+ var MAX_STATE_AGE_MS2 = 10 * 60 * 1e3;
4899
6173
  function generateState2() {
4900
6174
  const array = new Uint8Array(16);
4901
6175
  crypto.getRandomValues(array);
@@ -4905,21 +6179,48 @@ function generateState2() {
4905
6179
  }
4906
6180
  function storeOAuthState2(state) {
4907
6181
  if (typeof window === "undefined") return;
4908
- sessionStorage.setItem(CODE_STORAGE_KEY, state);
6182
+ const stateData = {
6183
+ state,
6184
+ timestamp: Date.now()
6185
+ };
6186
+ sessionStorage.setItem(CODE_STORAGE_KEY, JSON.stringify(stateData));
4909
6187
  }
4910
6188
  function getAndClearOAuthState2() {
4911
6189
  if (typeof window === "undefined") return null;
4912
- const state = sessionStorage.getItem(CODE_STORAGE_KEY);
4913
- sessionStorage.removeItem(CODE_STORAGE_KEY);
4914
- return state;
6190
+ try {
6191
+ const stored = sessionStorage.getItem(CODE_STORAGE_KEY);
6192
+ if (!stored) return null;
6193
+ const stateData = JSON.parse(stored);
6194
+ const age = Date.now() - stateData.timestamp;
6195
+ if (age > MAX_STATE_AGE_MS2) {
6196
+ sessionStorage.removeItem(CODE_STORAGE_KEY);
6197
+ return null;
6198
+ }
6199
+ sessionStorage.removeItem(CODE_STORAGE_KEY);
6200
+ return stateData.state;
6201
+ } catch {
6202
+ sessionStorage.removeItem(CODE_STORAGE_KEY);
6203
+ return null;
6204
+ }
4915
6205
  }
4916
6206
  function isGoogleDriveCallback() {
4917
6207
  if (typeof window === "undefined") return false;
4918
6208
  const url = new URL(window.location.href);
4919
6209
  const code = url.searchParams.get("code");
4920
6210
  const state = url.searchParams.get("state");
4921
- const storedState = sessionStorage.getItem(CODE_STORAGE_KEY);
4922
- return !!code && !!state && state === storedState;
6211
+ if (!code || !state) return false;
6212
+ try {
6213
+ const stored = sessionStorage.getItem(CODE_STORAGE_KEY);
6214
+ if (!stored) return false;
6215
+ const stateData = JSON.parse(stored);
6216
+ const age = Date.now() - stateData.timestamp;
6217
+ if (age > MAX_STATE_AGE_MS2) {
6218
+ return false;
6219
+ }
6220
+ return state === stateData.state;
6221
+ } catch {
6222
+ return false;
6223
+ }
4923
6224
  }
4924
6225
  async function handleGoogleDriveCallback(callbackPath, apiClient) {
4925
6226
  if (typeof window === "undefined") return null;
@@ -4948,15 +6249,35 @@ async function handleGoogleDriveCallback(callbackPath, apiClient) {
4948
6249
  response.data.refresh_token,
4949
6250
  response.data.scope
4950
6251
  );
4951
- storeTokenData(PROVIDER2, tokenData);
6252
+ try {
6253
+ await storeTokenData(PROVIDER2, tokenData);
6254
+ } catch (error) {
6255
+ const errorMessage = error instanceof Error ? error.message : "Unknown encryption error";
6256
+ console.error("OAuth token encryption failed in Google Drive callback:", errorMessage);
6257
+ console.warn("OAuth token encryption failed - tokens not stored securely");
6258
+ return null;
6259
+ }
4952
6260
  window.history.replaceState({}, "", window.location.pathname);
4953
6261
  return response.data.access_token;
4954
- } catch {
6262
+ } catch (error) {
6263
+ const errorMessage = error instanceof Error ? error.message : "Unknown error";
6264
+ const errorName = error instanceof Error ? error.name : "Error";
6265
+ console.error(`Google Drive OAuth callback error: ${errorName}: ${errorMessage}`);
6266
+ console.error("Error details:", {
6267
+ name: errorName,
6268
+ message: errorMessage,
6269
+ stack: error instanceof Error ? error.stack : void 0
6270
+ });
6271
+ console.warn(`Google Drive OAuth callback failed: ${errorMessage}`);
6272
+ if (errorMessage.includes("encryption failed") || errorMessage.includes("OAuth token encryption")) {
6273
+ console.warn("OAuth callback failed due to encryption error - this is a security issue");
6274
+ throw error;
6275
+ }
4955
6276
  return null;
4956
6277
  }
4957
6278
  }
4958
- async function refreshGoogleDriveToken(apiClient) {
4959
- const refreshToken = getRefreshToken(PROVIDER2);
6279
+ async function refreshGoogleDriveToken(apiClient, walletAddress) {
6280
+ const refreshToken = walletAddress ? await getRefreshToken(PROVIDER2, walletAddress) : getRefreshTokenSync(PROVIDER2);
4960
6281
  if (!refreshToken) return null;
4961
6282
  try {
4962
6283
  const response = await postAuthOauthByProviderRefresh({
@@ -4967,22 +6288,22 @@ async function refreshGoogleDriveToken(apiClient) {
4967
6288
  if (!response.data?.access_token) {
4968
6289
  throw new Error("No access token in refresh response");
4969
6290
  }
4970
- const currentData = getStoredTokenData(PROVIDER2);
6291
+ const currentData = walletAddress ? await getStoredTokenData(PROVIDER2, walletAddress) : getStoredTokenDataSync(PROVIDER2);
4971
6292
  const tokenData = tokenResponseToStoredData(
4972
6293
  response.data.access_token,
4973
6294
  response.data.expires_in,
4974
6295
  response.data.refresh_token ?? currentData?.refreshToken,
4975
6296
  response.data.scope ?? currentData?.scope
4976
6297
  );
4977
- storeTokenData(PROVIDER2, tokenData);
6298
+ await storeTokenData(PROVIDER2, tokenData, walletAddress);
4978
6299
  return response.data.access_token;
4979
6300
  } catch {
4980
6301
  clearTokenData(PROVIDER2);
4981
6302
  return null;
4982
6303
  }
4983
6304
  }
4984
- async function revokeGoogleDriveToken(apiClient) {
4985
- const tokenData = getStoredTokenData(PROVIDER2);
6305
+ async function revokeGoogleDriveToken(apiClient, walletAddress) {
6306
+ const tokenData = walletAddress ? await getStoredTokenData(PROVIDER2, walletAddress) : getStoredTokenDataSync(PROVIDER2);
4986
6307
  if (!tokenData) return;
4987
6308
  try {
4988
6309
  const tokenToRevoke = tokenData.refreshToken ?? tokenData.accessToken;
@@ -4996,8 +6317,8 @@ async function revokeGoogleDriveToken(apiClient) {
4996
6317
  clearTokenData(PROVIDER2);
4997
6318
  }
4998
6319
  }
4999
- async function getGoogleDriveAccessToken(apiClient) {
5000
- const storedData = getStoredTokenData(PROVIDER2);
6320
+ async function getGoogleDriveAccessToken(apiClient, walletAddress) {
6321
+ const storedData = walletAddress ? await getStoredTokenData(PROVIDER2, walletAddress) : getStoredTokenDataSync(PROVIDER2);
5001
6322
  if (!storedData) {
5002
6323
  return null;
5003
6324
  }
@@ -5005,7 +6326,7 @@ async function getGoogleDriveAccessToken(apiClient) {
5005
6326
  return storedData.accessToken;
5006
6327
  }
5007
6328
  if (storedData.refreshToken) {
5008
- const refreshedToken = await refreshGoogleDriveToken(apiClient);
6329
+ const refreshedToken = await refreshGoogleDriveToken(apiClient, walletAddress);
5009
6330
  if (refreshedToken) {
5010
6331
  return refreshedToken;
5011
6332
  }
@@ -5034,14 +6355,13 @@ async function startGoogleDriveAuth(clientId, callbackPath) {
5034
6355
  });
5035
6356
  }
5036
6357
  function getGoogleDriveStoredToken() {
5037
- return getValidAccessToken(PROVIDER2);
6358
+ return getValidAccessTokenSync(PROVIDER2);
5038
6359
  }
5039
6360
  function clearGoogleDriveToken() {
5040
6361
  clearTokenData(PROVIDER2);
5041
6362
  }
5042
6363
  function hasGoogleDriveCredentials() {
5043
- const data = getStoredTokenData(PROVIDER2);
5044
- return !!(data?.accessToken || data?.refreshToken);
6364
+ return hasStoredCredentialsSync(PROVIDER2);
5045
6365
  }
5046
6366
 
5047
6367
  // src/react/useGoogleDriveAuth.ts
@@ -6588,6 +7908,7 @@ function useBackup(options) {
6588
7908
  BackupAuthProvider,
6589
7909
  ChatConversation,
6590
7910
  ChatMessage,
7911
+ DECRYPTION_FAILED_PLACEHOLDER,
6591
7912
  DEFAULT_BACKUP_FOLDER,
6592
7913
  DEFAULT_DRIVE_CONVERSATIONS_FOLDER,
6593
7914
  DEFAULT_DRIVE_ROOT_FOLDER,
@@ -6601,26 +7922,41 @@ function useBackup(options) {
6601
7922
  chatStorageMigrations,
6602
7923
  chatStorageSchema,
6603
7924
  clearAllEncryptionKeys,
7925
+ clearAllKeyPairs,
6604
7926
  clearDropboxToken,
6605
7927
  clearEncryptionKey,
6606
7928
  clearGoogleDriveToken,
6607
7929
  clearICloudAuth,
7930
+ clearKeyPair,
6608
7931
  createMemoryContextSystemMessage,
6609
7932
  decryptData,
6610
7933
  decryptDataBytes,
7934
+ decryptField,
7935
+ decryptMemoriesBatch,
7936
+ decryptMemoryFields,
6611
7937
  encryptData,
7938
+ encryptField,
7939
+ encryptMemoriesBatch,
7940
+ encryptMemoriesBatchInPlace,
7941
+ encryptMemoryFields,
7942
+ exportPublicKey,
6612
7943
  extractConversationContext,
6613
7944
  formatMemoriesForChat,
6614
7945
  generateCompositeKey,
6615
7946
  generateConversationId,
6616
7947
  generateUniqueKey,
7948
+ getEncryptionVersion,
6617
7949
  getGoogleDriveStoredToken,
6618
7950
  hasDropboxCredentials,
7951
+ hasEncryptedFields,
6619
7952
  hasEncryptionKey,
6620
7953
  hasGoogleDriveCredentials,
6621
7954
  hasICloudCredentials,
7955
+ hasKeyPair,
6622
7956
  memoryStorageSchema,
7957
+ needsEncryption,
6623
7958
  requestEncryptionKey,
7959
+ requestKeyPair,
6624
7960
  sdkMigrations,
6625
7961
  sdkModelClasses,
6626
7962
  sdkSchema,