@cmdoss/memwal-sdk 0.6.2 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (247) hide show
  1. package/ARCHITECTURE.md +547 -547
  2. package/BENCHMARKS.md +238 -238
  3. package/README.md +310 -181
  4. package/dist/ai-sdk/tools.d.ts +2 -2
  5. package/dist/ai-sdk/tools.js +2 -2
  6. package/dist/client/ClientMemoryManager.js +2 -2
  7. package/dist/client/ClientMemoryManager.js.map +1 -1
  8. package/dist/client/PersonalDataWallet.d.ts.map +1 -1
  9. package/dist/client/SimplePDWClient.d.ts +29 -1
  10. package/dist/client/SimplePDWClient.d.ts.map +1 -1
  11. package/dist/client/SimplePDWClient.js +45 -13
  12. package/dist/client/SimplePDWClient.js.map +1 -1
  13. package/dist/client/namespaces/EmbeddingsNamespace.d.ts +1 -1
  14. package/dist/client/namespaces/EmbeddingsNamespace.js +1 -1
  15. package/dist/client/namespaces/MemoryNamespace.d.ts +31 -0
  16. package/dist/client/namespaces/MemoryNamespace.d.ts.map +1 -1
  17. package/dist/client/namespaces/MemoryNamespace.js +272 -39
  18. package/dist/client/namespaces/MemoryNamespace.js.map +1 -1
  19. package/dist/client/namespaces/consolidated/AINamespace.d.ts +2 -2
  20. package/dist/client/namespaces/consolidated/AINamespace.js +2 -2
  21. package/dist/client/namespaces/consolidated/BlockchainNamespace.d.ts +12 -2
  22. package/dist/client/namespaces/consolidated/BlockchainNamespace.d.ts.map +1 -1
  23. package/dist/client/namespaces/consolidated/BlockchainNamespace.js +62 -4
  24. package/dist/client/namespaces/consolidated/BlockchainNamespace.js.map +1 -1
  25. package/dist/client/namespaces/consolidated/StorageNamespace.d.ts +67 -2
  26. package/dist/client/namespaces/consolidated/StorageNamespace.d.ts.map +1 -1
  27. package/dist/client/namespaces/consolidated/StorageNamespace.js +549 -16
  28. package/dist/client/namespaces/consolidated/StorageNamespace.js.map +1 -1
  29. package/dist/config/ConfigurationHelper.js +61 -61
  30. package/dist/config/defaults.js +2 -2
  31. package/dist/config/defaults.js.map +1 -1
  32. package/dist/graph/GraphService.js +21 -21
  33. package/dist/graph/GraphService.js.map +1 -1
  34. package/dist/index.d.ts +3 -1
  35. package/dist/index.d.ts.map +1 -1
  36. package/dist/index.js +3 -1
  37. package/dist/index.js.map +1 -1
  38. package/dist/infrastructure/seal/EncryptionService.d.ts +9 -5
  39. package/dist/infrastructure/seal/EncryptionService.d.ts.map +1 -1
  40. package/dist/infrastructure/seal/EncryptionService.js +37 -15
  41. package/dist/infrastructure/seal/EncryptionService.js.map +1 -1
  42. package/dist/infrastructure/seal/SealService.d.ts +13 -5
  43. package/dist/infrastructure/seal/SealService.d.ts.map +1 -1
  44. package/dist/infrastructure/seal/SealService.js +36 -34
  45. package/dist/infrastructure/seal/SealService.js.map +1 -1
  46. package/dist/langchain/createPDWRAG.js +30 -30
  47. package/dist/retrieval/MemoryDecryptionPipeline.d.ts.map +1 -1
  48. package/dist/retrieval/MemoryDecryptionPipeline.js +2 -1
  49. package/dist/retrieval/MemoryDecryptionPipeline.js.map +1 -1
  50. package/dist/retrieval/MemoryRetrievalService.d.ts +31 -0
  51. package/dist/retrieval/MemoryRetrievalService.d.ts.map +1 -1
  52. package/dist/retrieval/MemoryRetrievalService.js +44 -4
  53. package/dist/retrieval/MemoryRetrievalService.js.map +1 -1
  54. package/dist/services/CapabilityService.d.ts.map +1 -1
  55. package/dist/services/CapabilityService.js +30 -14
  56. package/dist/services/CapabilityService.js.map +1 -1
  57. package/dist/services/CrossContextPermissionService.d.ts.map +1 -1
  58. package/dist/services/CrossContextPermissionService.js +9 -7
  59. package/dist/services/CrossContextPermissionService.js.map +1 -1
  60. package/dist/services/EmbeddingService.d.ts +28 -1
  61. package/dist/services/EmbeddingService.d.ts.map +1 -1
  62. package/dist/services/EmbeddingService.js +54 -0
  63. package/dist/services/EmbeddingService.js.map +1 -1
  64. package/dist/services/EncryptionService.d.ts.map +1 -1
  65. package/dist/services/EncryptionService.js +6 -5
  66. package/dist/services/EncryptionService.js.map +1 -1
  67. package/dist/services/GeminiAIService.js +309 -309
  68. package/dist/services/IndexManager.d.ts +5 -1
  69. package/dist/services/IndexManager.d.ts.map +1 -1
  70. package/dist/services/IndexManager.js +17 -40
  71. package/dist/services/IndexManager.js.map +1 -1
  72. package/dist/services/QueryService.js +1 -1
  73. package/dist/services/QueryService.js.map +1 -1
  74. package/dist/services/StorageService.d.ts +11 -0
  75. package/dist/services/StorageService.d.ts.map +1 -1
  76. package/dist/services/StorageService.js +73 -10
  77. package/dist/services/StorageService.js.map +1 -1
  78. package/dist/services/TransactionService.d.ts +20 -0
  79. package/dist/services/TransactionService.d.ts.map +1 -1
  80. package/dist/services/TransactionService.js +43 -0
  81. package/dist/services/TransactionService.js.map +1 -1
  82. package/dist/services/ViewService.js +2 -2
  83. package/dist/services/ViewService.js.map +1 -1
  84. package/dist/services/storage/QuiltBatchManager.d.ts +101 -1
  85. package/dist/services/storage/QuiltBatchManager.d.ts.map +1 -1
  86. package/dist/services/storage/QuiltBatchManager.js +410 -20
  87. package/dist/services/storage/QuiltBatchManager.js.map +1 -1
  88. package/dist/services/storage/index.d.ts +1 -1
  89. package/dist/services/storage/index.d.ts.map +1 -1
  90. package/dist/services/storage/index.js.map +1 -1
  91. package/dist/utils/LRUCache.d.ts +106 -0
  92. package/dist/utils/LRUCache.d.ts.map +1 -0
  93. package/dist/utils/LRUCache.js +281 -0
  94. package/dist/utils/LRUCache.js.map +1 -0
  95. package/dist/utils/index.d.ts +1 -0
  96. package/dist/utils/index.d.ts.map +1 -1
  97. package/dist/utils/index.js +2 -0
  98. package/dist/utils/index.js.map +1 -1
  99. package/dist/utils/memoryIndexOnChain.d.ts +212 -0
  100. package/dist/utils/memoryIndexOnChain.d.ts.map +1 -0
  101. package/dist/utils/memoryIndexOnChain.js +312 -0
  102. package/dist/utils/memoryIndexOnChain.js.map +1 -0
  103. package/dist/utils/rebuildIndexNode.d.ts +29 -0
  104. package/dist/utils/rebuildIndexNode.d.ts.map +1 -1
  105. package/dist/utils/rebuildIndexNode.js +366 -98
  106. package/dist/utils/rebuildIndexNode.js.map +1 -1
  107. package/dist/vector/HnswWasmService.d.ts +20 -5
  108. package/dist/vector/HnswWasmService.d.ts.map +1 -1
  109. package/dist/vector/HnswWasmService.js +73 -40
  110. package/dist/vector/HnswWasmService.js.map +1 -1
  111. package/dist/vector/IHnswService.d.ts +10 -1
  112. package/dist/vector/IHnswService.d.ts.map +1 -1
  113. package/dist/vector/IHnswService.js.map +1 -1
  114. package/dist/vector/NodeHnswService.d.ts +16 -0
  115. package/dist/vector/NodeHnswService.d.ts.map +1 -1
  116. package/dist/vector/NodeHnswService.js +84 -5
  117. package/dist/vector/NodeHnswService.js.map +1 -1
  118. package/dist/vector/createHnswService.d.ts +1 -1
  119. package/dist/vector/createHnswService.js +1 -1
  120. package/dist/vector/index.d.ts +1 -1
  121. package/dist/vector/index.js +1 -1
  122. package/package.json +157 -157
  123. package/src/access/PermissionService.ts +635 -635
  124. package/src/aggregation/AggregationService.ts +389 -389
  125. package/src/ai-sdk/PDWVectorStore.ts +715 -715
  126. package/src/ai-sdk/index.ts +65 -65
  127. package/src/ai-sdk/tools.ts +460 -460
  128. package/src/ai-sdk/types.ts +404 -404
  129. package/src/batch/BatchManager.ts +597 -597
  130. package/src/batch/BatchingService.ts +429 -429
  131. package/src/batch/MemoryProcessingCache.ts +492 -492
  132. package/src/batch/index.ts +30 -30
  133. package/src/browser.ts +200 -200
  134. package/src/client/ClientMemoryManager.ts +987 -987
  135. package/src/client/PersonalDataWallet.ts +345 -345
  136. package/src/client/SimplePDWClient.ts +1289 -1222
  137. package/src/client/factory.ts +154 -154
  138. package/src/client/namespaces/AnalyticsNamespace.ts +377 -377
  139. package/src/client/namespaces/BatchNamespace.ts +356 -356
  140. package/src/client/namespaces/CacheNamespace.ts +123 -123
  141. package/src/client/namespaces/CapabilityNamespace.ts +217 -217
  142. package/src/client/namespaces/ClassifyNamespace.ts +169 -169
  143. package/src/client/namespaces/ContextNamespace.ts +297 -297
  144. package/src/client/namespaces/EmbeddingsNamespace.ts +99 -99
  145. package/src/client/namespaces/EncryptionNamespace.ts +221 -221
  146. package/src/client/namespaces/GraphNamespace.ts +468 -468
  147. package/src/client/namespaces/IndexNamespace.ts +361 -361
  148. package/src/client/namespaces/MemoryNamespace.ts +1422 -1135
  149. package/src/client/namespaces/PermissionsNamespace.ts +254 -254
  150. package/src/client/namespaces/PipelineNamespace.ts +220 -220
  151. package/src/client/namespaces/SearchNamespace.ts +1049 -1049
  152. package/src/client/namespaces/StorageNamespace.ts +458 -458
  153. package/src/client/namespaces/TxNamespace.ts +260 -260
  154. package/src/client/namespaces/WalletNamespace.ts +243 -243
  155. package/src/client/namespaces/consolidated/AINamespace.ts +449 -449
  156. package/src/client/namespaces/consolidated/BlockchainNamespace.ts +607 -546
  157. package/src/client/namespaces/consolidated/SecurityNamespace.ts +648 -648
  158. package/src/client/namespaces/consolidated/StorageNamespace.ts +1141 -497
  159. package/src/client/namespaces/consolidated/index.ts +39 -39
  160. package/src/client/signers/KeypairSigner.ts +108 -108
  161. package/src/client/signers/UnifiedSigner.ts +110 -110
  162. package/src/client/signers/WalletAdapterSigner.ts +159 -159
  163. package/src/client/signers/index.ts +26 -26
  164. package/src/config/ConfigurationHelper.ts +412 -412
  165. package/src/config/defaults.ts +51 -51
  166. package/src/config/index.ts +8 -8
  167. package/src/config/validation.ts +70 -70
  168. package/src/core/index.ts +14 -14
  169. package/src/core/interfaces/IService.ts +307 -307
  170. package/src/core/interfaces/index.ts +8 -8
  171. package/src/core/types/capability.ts +297 -297
  172. package/src/core/types/index.ts +870 -870
  173. package/src/core/types/wallet.ts +270 -270
  174. package/src/core/types.ts +9 -9
  175. package/src/core/wallet.ts +222 -222
  176. package/src/embedding/index.ts +19 -19
  177. package/src/embedding/types.ts +357 -357
  178. package/src/errors/index.ts +602 -602
  179. package/src/errors/recovery.ts +461 -461
  180. package/src/errors/validation.ts +567 -567
  181. package/src/generated/pdw/capability.ts +319 -319
  182. package/src/graph/GraphService.ts +887 -887
  183. package/src/graph/KnowledgeGraphManager.ts +728 -728
  184. package/src/graph/index.ts +25 -25
  185. package/src/index.ts +498 -474
  186. package/src/infrastructure/index.ts +22 -22
  187. package/src/infrastructure/seal/EncryptionService.ts +628 -603
  188. package/src/infrastructure/seal/SealService.ts +613 -615
  189. package/src/infrastructure/seal/index.ts +9 -9
  190. package/src/infrastructure/sui/BlockchainManager.ts +627 -627
  191. package/src/infrastructure/sui/SuiService.ts +888 -888
  192. package/src/infrastructure/sui/index.ts +9 -9
  193. package/src/infrastructure/walrus/StorageManager.ts +604 -604
  194. package/src/infrastructure/walrus/WalrusStorageService.ts +612 -612
  195. package/src/infrastructure/walrus/index.ts +9 -9
  196. package/src/langchain/PDWEmbeddings.ts +145 -145
  197. package/src/langchain/PDWVectorStore.ts +456 -456
  198. package/src/langchain/createPDWRAG.ts +303 -303
  199. package/src/langchain/index.ts +47 -47
  200. package/src/permissions/ConsentRepository.browser.ts +249 -249
  201. package/src/permissions/ConsentRepository.ts +364 -364
  202. package/src/pipeline/MemoryPipeline.ts +862 -862
  203. package/src/pipeline/PipelineManager.ts +683 -683
  204. package/src/pipeline/index.ts +26 -26
  205. package/src/retrieval/AdvancedSearchService.ts +629 -629
  206. package/src/retrieval/MemoryAnalyticsService.ts +711 -711
  207. package/src/retrieval/MemoryDecryptionPipeline.ts +825 -824
  208. package/src/retrieval/MemoryRetrievalService.ts +904 -830
  209. package/src/retrieval/index.ts +42 -42
  210. package/src/services/BatchService.ts +352 -352
  211. package/src/services/CapabilityService.ts +464 -448
  212. package/src/services/ClassifierService.ts +465 -465
  213. package/src/services/CrossContextPermissionService.ts +486 -484
  214. package/src/services/EmbeddingService.ts +771 -706
  215. package/src/services/EncryptionService.ts +712 -711
  216. package/src/services/GeminiAIService.ts +753 -753
  217. package/src/services/IndexManager.ts +977 -1004
  218. package/src/services/MemoryIndexService.ts +1003 -1003
  219. package/src/services/MemoryService.ts +369 -369
  220. package/src/services/QueryService.ts +890 -890
  221. package/src/services/StorageService.ts +1182 -1111
  222. package/src/services/TransactionService.ts +838 -790
  223. package/src/services/VectorService.ts +462 -462
  224. package/src/services/ViewService.ts +484 -484
  225. package/src/services/index.ts +25 -25
  226. package/src/services/storage/BlobAttributesManager.ts +333 -333
  227. package/src/services/storage/KnowledgeGraphManager.ts +425 -425
  228. package/src/services/storage/MemorySearchManager.ts +387 -387
  229. package/src/services/storage/QuiltBatchManager.ts +1130 -660
  230. package/src/services/storage/WalrusMetadataManager.ts +268 -268
  231. package/src/services/storage/WalrusStorageManager.ts +287 -287
  232. package/src/services/storage/index.ts +57 -52
  233. package/src/types/index.ts +13 -13
  234. package/src/utils/LRUCache.ts +378 -0
  235. package/src/utils/index.ts +76 -68
  236. package/src/utils/memoryIndexOnChain.ts +507 -0
  237. package/src/utils/rebuildIndex.ts +290 -290
  238. package/src/utils/rebuildIndexNode.ts +771 -424
  239. package/src/vector/BrowserHnswIndexService.ts +758 -758
  240. package/src/vector/HnswWasmService.ts +731 -679
  241. package/src/vector/IHnswService.ts +233 -224
  242. package/src/vector/NodeHnswService.ts +833 -735
  243. package/src/vector/VectorManager.ts +478 -478
  244. package/src/vector/createHnswService.ts +135 -135
  245. package/src/vector/index.ts +56 -56
  246. package/src/wallet/ContextWalletService.ts +656 -656
  247. package/src/wallet/MainWalletService.ts +317 -317
@@ -1,497 +1,1141 @@
1
- /**
2
- * Storage Namespace - Consolidated Storage Operations
3
- *
4
- * Merges functionality from:
5
- * - StorageService: Walrus blob storage operations
6
- * - CacheNamespace: LRU in-memory caching
7
- *
8
- * Provides unified interface for data persistence (Walrus) and caching.
9
- *
10
- * @module client/namespaces/consolidated
11
- */
12
-
13
- import type { ServiceContainer } from '../../SimplePDWClient';
14
-
15
- // ============================================================================
16
- // Types
17
- // ============================================================================
18
-
19
- /**
20
- * Cache statistics
21
- */
22
- export interface CacheStats {
23
- size: number;
24
- totalAccess: number;
25
- hitRate: number;
26
- oldestItem?: Date;
27
- newestItem?: Date;
28
- }
29
-
30
- /**
31
- * Upload result from Walrus
32
- */
33
- export interface UploadResult {
34
- blobId: string;
35
- size: number;
36
- contentType?: string;
37
- }
38
-
39
- /**
40
- * Memory package structure
41
- */
42
- export interface MemoryPackage {
43
- content: string;
44
- contentType: string;
45
- metadata?: Record<string, any>;
46
- embedding?: number[];
47
- createdAt?: number;
48
- }
49
-
50
- /**
51
- * Upload options
52
- */
53
- export interface UploadOptions {
54
- contentType?: string;
55
- encrypt?: boolean;
56
- epochs?: number;
57
- }
58
-
59
- // ============================================================================
60
- // Sub-Namespaces
61
- // ============================================================================
62
-
63
- /**
64
- * Cache sub-namespace for LRU caching operations
65
- */
66
- class CacheSubNamespace {
67
- constructor(private services: ServiceContainer) {}
68
-
69
- /**
70
- * Get cached value
71
- *
72
- * @param key - Cache key
73
- * @returns Cached value or null if not found/expired
74
- *
75
- * @example
76
- * ```typescript
77
- * const cached = pdw.storage.cache.get<User>('user:123');
78
- * if (cached) {
79
- * console.log('Cache hit:', cached);
80
- * }
81
- * ```
82
- */
83
- get<T = any>(key: string): T | null {
84
- if (!this.services.batchService) {
85
- throw new Error('Batch service (cache) not configured.');
86
- }
87
- return this.services.batchService.getCache<T>(key);
88
- }
89
-
90
- /**
91
- * Set cache value
92
- *
93
- * @param key - Cache key
94
- * @param value - Value to cache
95
- * @param ttl - Time-to-live in milliseconds (optional)
96
- *
97
- * @example
98
- * ```typescript
99
- * pdw.storage.cache.set('user:123', userData, 60000); // 1 minute TTL
100
- * ```
101
- */
102
- set<T = any>(key: string, value: T, ttl?: number): void {
103
- if (!this.services.batchService) {
104
- throw new Error('Batch service (cache) not configured.');
105
- }
106
- this.services.batchService.setCache(key, value, ttl);
107
- }
108
-
109
- /**
110
- * Check if key exists in cache
111
- *
112
- * @param key - Cache key
113
- * @returns True if key exists and not expired
114
- */
115
- has(key: string): boolean {
116
- if (!this.services.batchService) {
117
- throw new Error('Batch service (cache) not configured.');
118
- }
119
- return this.services.batchService.hasCache(key);
120
- }
121
-
122
- /**
123
- * Delete cache entry
124
- *
125
- * @param key - Cache key
126
- * @returns True if deleted, false if not found
127
- */
128
- delete(key: string): boolean {
129
- if (!this.services.batchService) {
130
- throw new Error('Batch service (cache) not configured.');
131
- }
132
- return this.services.batchService.deleteCache(key);
133
- }
134
-
135
- /**
136
- * Clear all cache entries
137
- */
138
- clear(): void {
139
- if (!this.services.batchService) {
140
- throw new Error('Batch service (cache) not configured.');
141
- }
142
- this.services.batchService.clearCache();
143
- }
144
-
145
- /**
146
- * Get cache statistics
147
- *
148
- * @returns Cache statistics
149
- */
150
- stats(): CacheStats {
151
- if (!this.services.batchService) {
152
- throw new Error('Batch service (cache) not configured.');
153
- }
154
- return this.services.batchService.getCacheStats();
155
- }
156
- }
157
-
158
- // ============================================================================
159
- // Storage Namespace
160
- // ============================================================================
161
-
162
- /**
163
- * Storage Namespace - Unified Storage Operations
164
- *
165
- * Consolidates Walrus blob storage and in-memory caching.
166
- *
167
- * @example
168
- * ```typescript
169
- * // Upload to Walrus
170
- * const result = await pdw.storage.upload(data);
171
- * console.log('Blob ID:', result.blobId);
172
- *
173
- * // Download from Walrus
174
- * const data = await pdw.storage.download(blobId);
175
- *
176
- * // Use cache
177
- * pdw.storage.cache.set('key', value, 60000);
178
- * const cached = pdw.storage.cache.get('key');
179
- * ```
180
- */
181
- export class StorageNamespace {
182
- private _cache: CacheSubNamespace;
183
-
184
- constructor(private services: ServiceContainer) {
185
- this._cache = new CacheSubNamespace(services);
186
- }
187
-
188
- /**
189
- * Cache operations
190
- */
191
- get cache(): CacheSubNamespace {
192
- return this._cache;
193
- }
194
-
195
- // ==========================================================================
196
- // Walrus Storage Operations
197
- // ==========================================================================
198
-
199
- /**
200
- * Upload data to Walrus
201
- *
202
- * @param data - Data to upload (string, Uint8Array, or object)
203
- * @param options - Upload options
204
- * @returns Upload result with blob ID
205
- *
206
- * @example
207
- * ```typescript
208
- * // Upload raw bytes
209
- * const result = await pdw.storage.upload(new Uint8Array([1, 2, 3]));
210
- *
211
- * // Upload JSON
212
- * const result = await pdw.storage.upload({ name: 'test' });
213
- *
214
- * // Upload with options
215
- * const result = await pdw.storage.upload(data, { encrypt: true });
216
- * ```
217
- */
218
- async upload(data: string | Uint8Array | object, options: UploadOptions = {}): Promise<UploadResult> {
219
- if (!this.services.storage) {
220
- throw new Error('Storage service not configured.');
221
- }
222
-
223
- // Convert data to Uint8Array
224
- let bytes: Uint8Array;
225
- let contentType = options.contentType || 'application/octet-stream';
226
-
227
- if (typeof data === 'string') {
228
- bytes = new TextEncoder().encode(data);
229
- contentType = options.contentType || 'text/plain';
230
- } else if (data instanceof Uint8Array) {
231
- bytes = data;
232
- } else {
233
- bytes = new TextEncoder().encode(JSON.stringify(data));
234
- contentType = options.contentType || 'application/json';
235
- }
236
-
237
- // Encrypt if requested
238
- if (options.encrypt && this.services.encryption) {
239
- const encryptResult = await this.services.encryption.encrypt(
240
- bytes,
241
- this.services.config.userAddress,
242
- 2
243
- );
244
- bytes = encryptResult.encryptedObject;
245
- }
246
-
247
- // Upload to Walrus using uploadBlob method
248
- const result = await this.services.storage.uploadBlob(bytes, {
249
- signer: this.services.config.signer,
250
- epochs: options.epochs,
251
- deletable: true
252
- });
253
-
254
- return {
255
- blobId: result.blobId,
256
- size: bytes.length,
257
- contentType
258
- };
259
- }
260
-
261
- /**
262
- * Download data from Walrus
263
- *
264
- * @param blobId - Blob ID to download
265
- * @returns Raw data as Uint8Array
266
- *
267
- * @example
268
- * ```typescript
269
- * const data = await pdw.storage.download('blobId123');
270
- * const text = new TextDecoder().decode(data);
271
- * ```
272
- */
273
- async download(blobId: string): Promise<Uint8Array> {
274
- if (!this.services.storage) {
275
- throw new Error('Storage service not configured.');
276
- }
277
-
278
- // Use retrieveFromWalrusOnly which returns { content, metadata }
279
- const result = await this.services.storage.retrieveFromWalrusOnly(blobId);
280
- return result.content;
281
- }
282
-
283
- /**
284
- * Download and parse JSON from Walrus
285
- *
286
- * @param blobId - Blob ID to download
287
- * @returns Parsed JSON object
288
- *
289
- * @example
290
- * ```typescript
291
- * const data = await pdw.storage.downloadJson<MyType>('blobId123');
292
- * ```
293
- */
294
- async downloadJson<T = any>(blobId: string): Promise<T> {
295
- const bytes = await this.download(blobId);
296
- const text = new TextDecoder().decode(bytes);
297
- return JSON.parse(text);
298
- }
299
-
300
- /**
301
- * Download and decrypt data from Walrus
302
- *
303
- * @param blobId - Blob ID to download
304
- * @param options - Decryption options
305
- * @returns Decrypted data
306
- *
307
- * @example
308
- * ```typescript
309
- * const data = await pdw.storage.downloadDecrypted('blobId123');
310
- * ```
311
- */
312
- async downloadDecrypted(blobId: string, options?: {
313
- memoryCapId?: string;
314
- keyId?: Uint8Array;
315
- }): Promise<Uint8Array> {
316
- if (!this.services.storage) {
317
- throw new Error('Storage service not configured.');
318
- }
319
- if (!this.services.encryption) {
320
- throw new Error('Encryption service not configured.');
321
- }
322
-
323
- const encryptedData = await this.download(blobId);
324
-
325
- return await this.services.encryption.decrypt({
326
- encryptedContent: encryptedData,
327
- userAddress: this.services.config.userAddress,
328
- requestingWallet: this.services.config.userAddress,
329
- memoryCapId: options?.memoryCapId,
330
- keyId: options?.keyId
331
- });
332
- }
333
-
334
- /**
335
- * Store memory package to Walrus
336
- *
337
- * Higher-level method that stores a complete memory package.
338
- *
339
- * @param memoryPackage - Memory package to store
340
- * @returns Upload result
341
- */
342
- async storeMemoryPackage(memoryPackage: MemoryPackage): Promise<UploadResult> {
343
- if (!this.services.storage) {
344
- throw new Error('Storage service not configured.');
345
- }
346
-
347
- // Use uploadMemoryPackage method
348
- const result = await this.services.storage.uploadMemoryPackage(
349
- {
350
- content: memoryPackage.content,
351
- embedding: memoryPackage.embedding || [],
352
- metadata: memoryPackage.metadata || {},
353
- identity: this.services.config.userAddress
354
- },
355
- {
356
- signer: this.services.config.signer,
357
- epochs: 3,
358
- deletable: true
359
- }
360
- );
361
-
362
- return {
363
- blobId: result.blobId,
364
- size: 0, // Size not returned by uploadMemoryPackage
365
- contentType: 'application/json'
366
- };
367
- }
368
-
369
- /**
370
- * Retrieve memory package from Walrus
371
- *
372
- * @param blobId - Blob ID of the memory package
373
- * @returns Retrieved memory package with decrypted content
374
- */
375
- async retrieveMemoryPackage(blobId: string): Promise<{
376
- memoryPackage: MemoryPackage | null;
377
- decryptionStatus: 'success' | 'failed' | 'not_encrypted';
378
- }> {
379
- if (!this.services.storage) {
380
- throw new Error('Storage service not configured.');
381
- }
382
-
383
- const result = await this.services.storage.retrieveMemoryPackage(blobId);
384
-
385
- // Map to expected return type
386
- return {
387
- memoryPackage: result.memoryPackage ? {
388
- content: result.memoryPackage.content,
389
- contentType: result.memoryPackage.contentType || 'text/plain',
390
- metadata: result.memoryPackage.metadata,
391
- embedding: result.memoryPackage.embedding,
392
- createdAt: result.memoryPackage.timestamp
393
- } : null,
394
- decryptionStatus: result.isEncrypted
395
- ? (result.memoryPackage ? 'success' : 'failed')
396
- : 'not_encrypted'
397
- };
398
- }
399
-
400
- /**
401
- * Check if blob exists on Walrus
402
- *
403
- * @param blobId - Blob ID to check
404
- * @returns True if blob exists
405
- */
406
- async exists(blobId: string): Promise<boolean> {
407
- try {
408
- await this.download(blobId);
409
- return true;
410
- } catch {
411
- return false;
412
- }
413
- }
414
-
415
- /**
416
- * Get metadata for a blob
417
- *
418
- * @param blobId - Blob ID
419
- * @returns Blob metadata or null
420
- */
421
- async getMetadata(blobId: string): Promise<{
422
- blobId: string;
423
- size?: number;
424
- exists: boolean;
425
- }> {
426
- try {
427
- const data = await this.download(blobId);
428
- return {
429
- blobId,
430
- size: data.length,
431
- exists: true
432
- };
433
- } catch {
434
- return {
435
- blobId,
436
- exists: false
437
- };
438
- }
439
- }
440
-
441
- // ==========================================================================
442
- // Batch Operations (Quilt)
443
- // ==========================================================================
444
-
445
- /**
446
- * Upload multiple memories as a Quilt (batch upload)
447
- *
448
- * Uses Walrus Quilt for ~90% gas savings compared to individual uploads.
449
- * Requires 2 user signatures:
450
- * - Transaction 1: Register blob on-chain
451
- * - Transaction 2: Certify upload on-chain
452
- *
453
- * @param memories - Array of memories to upload
454
- * @param options - Upload options including signer
455
- * @returns Quilt result with file mappings
456
- *
457
- * @example
458
- * ```typescript
459
- * const result = await pdw.storage.uploadMemoryBatch(
460
- * memories,
461
- * {
462
- * signer: pdw.getConfig().signer,
463
- * epochs: 3,
464
- * userAddress: pdw.getConfig().userAddress
465
- * }
466
- * );
467
- * console.log(`Uploaded ${result.files.length} files`);
468
- * ```
469
- */
470
- async uploadMemoryBatch(
471
- memories: Array<{
472
- content: string;
473
- category: string;
474
- importance: number;
475
- topic: string;
476
- embedding: number[];
477
- encryptedContent: Uint8Array;
478
- summary?: string;
479
- id?: string;
480
- }>,
481
- options: {
482
- signer: any; // UnifiedSigner
483
- epochs?: number;
484
- userAddress: string;
485
- }
486
- ): Promise<{
487
- quiltId: string;
488
- files: Array<{ identifier: string; blobId: string }>;
489
- uploadTimeMs: number;
490
- }> {
491
- if (!this.services.storage) {
492
- throw new Error('Storage service not configured.');
493
- }
494
-
495
- return this.services.storage.uploadMemoryBatch(memories, options);
496
- }
497
- }
1
+ /**
2
+ * Storage Namespace - Consolidated Storage Operations
3
+ *
4
+ * Merges functionality from:
5
+ * - StorageService: Walrus blob storage operations
6
+ * - CacheNamespace: LRU in-memory caching
7
+ *
8
+ * Provides unified interface for data persistence (Walrus) and caching.
9
+ *
10
+ * @module client/namespaces/consolidated
11
+ */
12
+
13
+ import type { ServiceContainer } from '../../SimplePDWClient';
14
+
15
+ // ============================================================================
16
+ // Types
17
+ // ============================================================================
18
+
19
+ /**
20
+ * Cache statistics
21
+ */
22
+ export interface CacheStats {
23
+ size: number;
24
+ totalAccess: number;
25
+ hitRate: number;
26
+ oldestItem?: Date;
27
+ newestItem?: Date;
28
+ }
29
+
30
+ /**
31
+ * Upload result from Walrus
32
+ */
33
+ export interface UploadResult {
34
+ blobId: string;
35
+ size: number;
36
+ contentType?: string;
37
+ }
38
+
39
+ /**
40
+ * Memory package structure
41
+ */
42
+ export interface MemoryPackage {
43
+ content: string;
44
+ contentType: string;
45
+ metadata?: Record<string, any>;
46
+ embedding?: number[];
47
+ createdAt?: number;
48
+ }
49
+
50
+ /**
51
+ * Upload options
52
+ */
53
+ export interface UploadOptions {
54
+ contentType?: string;
55
+ encrypt?: boolean;
56
+ epochs?: number;
57
+ }
58
+
59
+ // ============================================================================
60
+ // Sub-Namespaces
61
+ // ============================================================================
62
+
63
+ /**
64
+ * Cache sub-namespace for LRU caching operations
65
+ */
66
+ class CacheSubNamespace {
67
+ constructor(private services: ServiceContainer) {}
68
+
69
+ /**
70
+ * Get cached value
71
+ *
72
+ * @param key - Cache key
73
+ * @returns Cached value or null if not found/expired
74
+ *
75
+ * @example
76
+ * ```typescript
77
+ * const cached = pdw.storage.cache.get<User>('user:123');
78
+ * if (cached) {
79
+ * console.log('Cache hit:', cached);
80
+ * }
81
+ * ```
82
+ */
83
+ get<T = any>(key: string): T | null {
84
+ if (!this.services.batchService) {
85
+ throw new Error('Batch service (cache) not configured.');
86
+ }
87
+ return this.services.batchService.getCache<T>(key);
88
+ }
89
+
90
+ /**
91
+ * Set cache value
92
+ *
93
+ * @param key - Cache key
94
+ * @param value - Value to cache
95
+ * @param ttl - Time-to-live in milliseconds (optional)
96
+ *
97
+ * @example
98
+ * ```typescript
99
+ * pdw.storage.cache.set('user:123', userData, 60000); // 1 minute TTL
100
+ * ```
101
+ */
102
+ set<T = any>(key: string, value: T, ttl?: number): void {
103
+ if (!this.services.batchService) {
104
+ throw new Error('Batch service (cache) not configured.');
105
+ }
106
+ this.services.batchService.setCache(key, value, ttl);
107
+ }
108
+
109
+ /**
110
+ * Check if key exists in cache
111
+ *
112
+ * @param key - Cache key
113
+ * @returns True if key exists and not expired
114
+ */
115
+ has(key: string): boolean {
116
+ if (!this.services.batchService) {
117
+ throw new Error('Batch service (cache) not configured.');
118
+ }
119
+ return this.services.batchService.hasCache(key);
120
+ }
121
+
122
+ /**
123
+ * Delete cache entry
124
+ *
125
+ * @param key - Cache key
126
+ * @returns True if deleted, false if not found
127
+ */
128
+ delete(key: string): boolean {
129
+ if (!this.services.batchService) {
130
+ throw new Error('Batch service (cache) not configured.');
131
+ }
132
+ return this.services.batchService.deleteCache(key);
133
+ }
134
+
135
+ /**
136
+ * Clear all cache entries
137
+ */
138
+ clear(): void {
139
+ if (!this.services.batchService) {
140
+ throw new Error('Batch service (cache) not configured.');
141
+ }
142
+ this.services.batchService.clearCache();
143
+ }
144
+
145
+ /**
146
+ * Get cache statistics
147
+ *
148
+ * @returns Cache statistics
149
+ */
150
+ stats(): CacheStats {
151
+ if (!this.services.batchService) {
152
+ throw new Error('Batch service (cache) not configured.');
153
+ }
154
+ return this.services.batchService.getCacheStats();
155
+ }
156
+ }
157
+
158
+ // ============================================================================
159
+ // Storage Namespace
160
+ // ============================================================================
161
+
162
+ /**
163
+ * Storage Namespace - Unified Storage Operations
164
+ *
165
+ * Consolidates Walrus blob storage and in-memory caching.
166
+ *
167
+ * @example
168
+ * ```typescript
169
+ * // Upload to Walrus
170
+ * const result = await pdw.storage.upload(data);
171
+ * console.log('Blob ID:', result.blobId);
172
+ *
173
+ * // Download from Walrus
174
+ * const data = await pdw.storage.download(blobId);
175
+ *
176
+ * // Use cache
177
+ * pdw.storage.cache.set('key', value, 60000);
178
+ * const cached = pdw.storage.cache.get('key');
179
+ * ```
180
+ */
181
+ export class StorageNamespace {
182
+ private _cache: CacheSubNamespace;
183
+
184
+ constructor(private services: ServiceContainer) {
185
+ this._cache = new CacheSubNamespace(services);
186
+ }
187
+
188
+ /**
189
+ * Cache operations
190
+ */
191
+ get cache(): CacheSubNamespace {
192
+ return this._cache;
193
+ }
194
+
195
+ // ==========================================================================
196
+ // Walrus Storage Operations
197
+ // ==========================================================================
198
+
199
+ /**
200
+ * Upload data to Walrus
201
+ *
202
+ * @param data - Data to upload (string, Uint8Array, or object)
203
+ * @param options - Upload options
204
+ * @returns Upload result with blob ID
205
+ *
206
+ * @example
207
+ * ```typescript
208
+ * // Upload raw bytes
209
+ * const result = await pdw.storage.upload(new Uint8Array([1, 2, 3]));
210
+ *
211
+ * // Upload JSON
212
+ * const result = await pdw.storage.upload({ name: 'test' });
213
+ *
214
+ * // Upload with options
215
+ * const result = await pdw.storage.upload(data, { encrypt: true });
216
+ * ```
217
+ */
218
+ async upload(data: string | Uint8Array | object, options: UploadOptions = {}): Promise<UploadResult> {
219
+ if (!this.services.storage) {
220
+ throw new Error('Storage service not configured.');
221
+ }
222
+
223
+ // Convert data to Uint8Array
224
+ let bytes: Uint8Array;
225
+ let contentType = options.contentType || 'application/octet-stream';
226
+
227
+ if (typeof data === 'string') {
228
+ bytes = new TextEncoder().encode(data);
229
+ contentType = options.contentType || 'text/plain';
230
+ } else if (data instanceof Uint8Array) {
231
+ bytes = data;
232
+ } else {
233
+ bytes = new TextEncoder().encode(JSON.stringify(data));
234
+ contentType = options.contentType || 'application/json';
235
+ }
236
+
237
+ // Encrypt if requested
238
+ if (options.encrypt && this.services.encryption) {
239
+ const encryptResult = await this.services.encryption.encrypt(
240
+ bytes,
241
+ this.services.config.userAddress,
242
+ 2
243
+ );
244
+ bytes = encryptResult.encryptedObject;
245
+ }
246
+
247
+ // Upload to Walrus using uploadBlob method
248
+ const result = await this.services.storage.uploadBlob(bytes, {
249
+ signer: this.services.config.signer,
250
+ epochs: options.epochs,
251
+ deletable: true
252
+ });
253
+
254
+ return {
255
+ blobId: result.blobId,
256
+ size: bytes.length,
257
+ contentType
258
+ };
259
+ }
260
+
261
+ /**
262
+ * Download data from Walrus
263
+ *
264
+ * @param blobId - Blob ID to download
265
+ * @returns Raw data as Uint8Array
266
+ *
267
+ * @example
268
+ * ```typescript
269
+ * const data = await pdw.storage.download('blobId123');
270
+ * const text = new TextDecoder().decode(data);
271
+ * ```
272
+ */
273
+ async download(blobId: string): Promise<Uint8Array> {
274
+ if (!this.services.storage) {
275
+ throw new Error('Storage service not configured.');
276
+ }
277
+
278
+ // Use retrieveFromWalrusOnly which returns { content, metadata }
279
+ const result = await this.services.storage.retrieveFromWalrusOnly(blobId);
280
+ return result.content;
281
+ }
282
+
283
+ /**
284
+ * Download and parse JSON from Walrus
285
+ *
286
+ * @param blobId - Blob ID to download
287
+ * @returns Parsed JSON object
288
+ *
289
+ * @example
290
+ * ```typescript
291
+ * const data = await pdw.storage.downloadJson<MyType>('blobId123');
292
+ * ```
293
+ */
294
+ async downloadJson<T = any>(blobId: string): Promise<T> {
295
+ const bytes = await this.download(blobId);
296
+ const text = new TextDecoder().decode(bytes);
297
+ return JSON.parse(text);
298
+ }
299
+
300
+ /**
301
+ * Download and decrypt data from Walrus
302
+ *
303
+ * @param blobId - Blob ID to download
304
+ * @param options - Decryption options
305
+ * @returns Decrypted data
306
+ *
307
+ * @example
308
+ * ```typescript
309
+ * const data = await pdw.storage.downloadDecrypted('blobId123');
310
+ * ```
311
+ */
312
+ async downloadDecrypted(blobId: string, options?: {
313
+ memoryCapId?: string;
314
+ keyId?: Uint8Array;
315
+ }): Promise<Uint8Array> {
316
+ if (!this.services.storage) {
317
+ throw new Error('Storage service not configured.');
318
+ }
319
+ if (!this.services.encryption) {
320
+ throw new Error('Encryption service not configured.');
321
+ }
322
+
323
+ const encryptedData = await this.download(blobId);
324
+
325
+ return await this.services.encryption.decrypt({
326
+ encryptedContent: encryptedData,
327
+ userAddress: this.services.config.userAddress,
328
+ requestingWallet: this.services.config.userAddress,
329
+ memoryCapId: options?.memoryCapId,
330
+ keyId: options?.keyId
331
+ });
332
+ }
333
+
334
+ /**
335
+ * Store memory package to Walrus
336
+ *
337
+ * Higher-level method that stores a complete memory package.
338
+ * Automatically encrypts content if encryption is enabled in config.
339
+ *
340
+ * @param memoryPackage - Memory package to store
341
+ * @returns Upload result
342
+ */
343
+ async storeMemoryPackage(memoryPackage: MemoryPackage): Promise<UploadResult> {
344
+ if (!this.services.storage) {
345
+ throw new Error('Storage service not configured.');
346
+ }
347
+
348
+ // Check if encryption is enabled
349
+ const encryptionEnabled = this.services.config.features?.enableEncryption ?? true;
350
+ let encryptedContent: Uint8Array | undefined;
351
+ let encryptedEmbedding: Uint8Array | undefined; // Option A v2: encrypted embedding
352
+ let encryptionType: string | undefined;
353
+ let memoryCapId: string | undefined;
354
+ let keyId: string | undefined;
355
+
356
+ console.log('🔍 Encryption check:', {
357
+ encryptionEnabled,
358
+ hasEncryptionService: !!this.services.encryption,
359
+ hasCapabilityService: !!this.services.capability,
360
+ configFeatures: this.services.config.features
361
+ });
362
+
363
+ if (encryptionEnabled && this.services.encryption && this.services.capability) {
364
+ console.log('🔒 Encrypting memory package with SEAL (capability-based)...');
365
+
366
+ try {
367
+ // Step 1: Get or create capability for this app context
368
+ const category = memoryPackage.metadata?.category || 'general';
369
+ console.log(`🔐 Getting/creating capability for category: ${category}`);
370
+
371
+ const cap = await this.services.capability.getOrCreate(
372
+ {
373
+ appId: category,
374
+ userAddress: this.services.config.userAddress
375
+ },
376
+ this.services.config.signer
377
+ );
378
+
379
+ memoryCapId = cap.id;
380
+ console.log(`✅ Capability ready: ${memoryCapId}`);
381
+
382
+ // Step 2: Compute key_id from capability (keccak256(owner || nonce))
383
+ keyId = this.services.capability.computeKeyId(cap);
384
+ console.log(`🔑 Key ID computed: ${keyId.substring(0, 20)}...`);
385
+
386
+ // Step 3: Encrypt the content using key_id as SEAL identity
387
+ const contentBytes = new TextEncoder().encode(memoryPackage.content);
388
+ const encryptResult = await this.services.encryption.encrypt(
389
+ contentBytes,
390
+ keyId, // Use key_id from capability as SEAL identity!
391
+ 2 // threshold: 2 of 2 key servers
392
+ );
393
+
394
+ encryptedContent = encryptResult.encryptedObject;
395
+ encryptionType = 'seal-capability';
396
+
397
+ console.log(`✅ Content encrypted: ${contentBytes.length} bytes → ${encryptedContent?.length || 0} bytes`);
398
+
399
+ // Step 4: Also encrypt the embedding for fast index rebuild (Option A v2)
400
+ const embeddingToEncrypt = memoryPackage.embedding && memoryPackage.embedding.length > 0
401
+ ? memoryPackage.embedding
402
+ : (memoryPackage.metadata?.embedding || []);
403
+
404
+ if (embeddingToEncrypt.length > 0) {
405
+ const embeddingBytes = new TextEncoder().encode(JSON.stringify(embeddingToEncrypt));
406
+ const encryptEmbeddingResult = await this.services.encryption.encrypt(
407
+ embeddingBytes,
408
+ keyId,
409
+ 2
410
+ );
411
+ encryptedEmbedding = encryptEmbeddingResult.encryptedObject;
412
+ console.log(`✅ Embedding encrypted: ${embeddingToEncrypt.length}D → ${encryptedEmbedding?.length || 0} bytes`);
413
+ }
414
+
415
+ console.log(` Using capability: ${memoryCapId}`);
416
+ console.log(` Key ID: ${keyId.substring(0, 20)}...`);
417
+ } catch (encryptError) {
418
+ console.error('❌ Encryption failed:', encryptError);
419
+ console.warn('⚠️ Falling back to plaintext storage');
420
+ // Fall back to plaintext if encryption fails
421
+ }
422
+ } else {
423
+ console.log('📝 Encryption disabled or services not available - storing plaintext');
424
+ }
425
+
426
+ // Include capability metadata for decryption
427
+ const metadata = {
428
+ ...memoryPackage.metadata,
429
+ // Add capability info for decryption (CRITICAL for Option A!)
430
+ ...(memoryCapId && keyId ? {
431
+ memoryCapId,
432
+ keyId,
433
+ encryptionVersion: 'v2-capability' // Mark as new capability-based encryption
434
+ } : {})
435
+ };
436
+
437
+ // Get embedding from root level (correct API) or metadata (legacy fallback)
438
+ const rootEmbedding = memoryPackage.embedding && memoryPackage.embedding.length > 0;
439
+ const metadataEmbedding = memoryPackage.metadata?.embedding && memoryPackage.metadata.embedding.length > 0;
440
+ const embedding = rootEmbedding
441
+ ? memoryPackage.embedding
442
+ : (memoryPackage.metadata?.embedding || []);
443
+
444
+ if (metadataEmbedding && !rootEmbedding) {
445
+ console.warn('⚠️ Embedding in metadata (legacy) - should be at root level');
446
+ }
447
+ console.log(`📊 Embedding: ${embedding.length}D vector`);
448
+
449
+ // Use uploadMemoryPackage method
450
+ const result = await this.services.storage.uploadMemoryPackage(
451
+ {
452
+ content: memoryPackage.content,
453
+ embedding,
454
+ metadata,
455
+ identity: this.services.config.userAddress,
456
+ encryptedContent, // Pass encrypted content if available
457
+ encryptedEmbedding, // Pass encrypted embedding for Option A v2
458
+ encryptionType
459
+ },
460
+ {
461
+ signer: this.services.config.signer,
462
+ epochs: 3,
463
+ deletable: true
464
+ }
465
+ );
466
+
467
+ return {
468
+ blobId: result.blobId,
469
+ size: 0, // Size not returned by uploadMemoryPackage
470
+ contentType: 'application/json',
471
+ memoryCapId, // Return capability ID for reference
472
+ keyId // Return key ID for reference
473
+ } as UploadResult;
474
+ }
475
+
476
+ /**
477
+ * Retrieve memory package from Walrus with optional decryption
478
+ *
479
+ * @param blobId - Blob ID of the memory package
480
+ * @param decryptionContext - Optional context for decrypting SEAL-encrypted content
481
+ * @returns Retrieved memory package with decrypted content
482
+ *
483
+ * @example
484
+ * ```typescript
485
+ * // Without decryption (returns encrypted data info)
486
+ * const result = await pdw.storage.retrieveMemoryPackage(blobId);
487
+ *
488
+ * // With decryption
489
+ * const result = await pdw.storage.retrieveMemoryPackage(blobId, {
490
+ * sessionKey,
491
+ * memoryCapId,
492
+ * keyId
493
+ * });
494
+ * ```
495
+ */
496
+ async retrieveMemoryPackage(
497
+ blobId: string,
498
+ decryptionContext?: {
499
+ sessionKey: any;
500
+ memoryCapId: string;
501
+ keyId: Uint8Array;
502
+ }
503
+ ): Promise<{
504
+ memoryPackage: MemoryPackage | null;
505
+ decryptionStatus: 'success' | 'failed' | 'not_encrypted';
506
+ error?: string;
507
+ }> {
508
+ if (!this.services.storage) {
509
+ throw new Error('Storage service not configured.');
510
+ }
511
+
512
+ const result = await this.services.storage.retrieveMemoryPackage(blobId);
513
+
514
+ // If not encrypted, return as-is
515
+ if (!result.isEncrypted && result.memoryPackage) {
516
+ return {
517
+ memoryPackage: {
518
+ content: result.memoryPackage.content,
519
+ contentType: result.memoryPackage.contentType || 'text/plain',
520
+ metadata: result.memoryPackage.metadata,
521
+ embedding: result.memoryPackage.embedding,
522
+ createdAt: result.memoryPackage.timestamp
523
+ },
524
+ decryptionStatus: 'not_encrypted'
525
+ };
526
+ }
527
+
528
+ // v2.2 JSON package with encrypted content + encrypted embedding
529
+ if (result.memoryPackage?.version === '2.2' && result.memoryPackage?.encryptedContent) {
530
+ console.log('🔐 Detected v2.2 JSON package (Full Encryption - Content + Embedding)');
531
+
532
+ const embeddingDimension = result.memoryPackage.metadata?.embeddingDimension || 0;
533
+ console.log(` embeddingDimension: ${embeddingDimension}D (encrypted on Walrus)`);
534
+
535
+ // Return base package (content encrypted, embedding encrypted)
536
+ const basePackage: MemoryPackage = {
537
+ content: '[ENCRYPTED - requires decryption]',
538
+ contentType: 'text/plain',
539
+ metadata: result.memoryPackage.metadata,
540
+ embedding: [], // Embedding is encrypted, needs decryption
541
+ createdAt: result.memoryPackage.timestamp
542
+ };
543
+
544
+ if (decryptionContext && this.services.encryption) {
545
+ try {
546
+ console.log('🔐 Decrypting v2.2 content...');
547
+
548
+ // Decrypt content
549
+ const encryptedContentBase64 = result.memoryPackage.encryptedContent;
550
+ const contentBinaryString = atob(encryptedContentBase64);
551
+ const encryptedContentBytes = new Uint8Array(contentBinaryString.length);
552
+ for (let i = 0; i < contentBinaryString.length; i++) {
553
+ encryptedContentBytes[i] = contentBinaryString.charCodeAt(i);
554
+ }
555
+
556
+ const decryptedContentData = await this.services.encryption.decrypt({
557
+ encryptedContent: encryptedContentBytes,
558
+ userAddress: this.services.config.userAddress,
559
+ sessionKey: decryptionContext.sessionKey,
560
+ memoryCapId: decryptionContext.memoryCapId,
561
+ keyId: decryptionContext.keyId
562
+ });
563
+
564
+ const decryptedContent = new TextDecoder().decode(decryptedContentData);
565
+ console.log(`✅ v2.2 content decrypted: "${decryptedContent.substring(0, 50)}..."`);
566
+
567
+ // Decrypt embedding if available
568
+ let decryptedEmbedding: number[] = [];
569
+ if (result.memoryPackage.encryptedEmbedding) {
570
+ console.log('🔐 Decrypting v2.2 embedding...');
571
+ const encryptedEmbeddingBase64 = result.memoryPackage.encryptedEmbedding;
572
+ const embeddingBinaryString = atob(encryptedEmbeddingBase64);
573
+ const encryptedEmbeddingBytes = new Uint8Array(embeddingBinaryString.length);
574
+ for (let i = 0; i < embeddingBinaryString.length; i++) {
575
+ encryptedEmbeddingBytes[i] = embeddingBinaryString.charCodeAt(i);
576
+ }
577
+
578
+ const decryptedEmbeddingData = await this.services.encryption.decrypt({
579
+ encryptedContent: encryptedEmbeddingBytes,
580
+ userAddress: this.services.config.userAddress,
581
+ sessionKey: decryptionContext.sessionKey,
582
+ memoryCapId: decryptionContext.memoryCapId,
583
+ keyId: decryptionContext.keyId
584
+ });
585
+
586
+ const embeddingJson = new TextDecoder().decode(decryptedEmbeddingData);
587
+ decryptedEmbedding = JSON.parse(embeddingJson);
588
+ console.log(`✅ v2.2 embedding decrypted: ${decryptedEmbedding.length}D vector`);
589
+ }
590
+
591
+ return {
592
+ memoryPackage: {
593
+ ...basePackage,
594
+ content: decryptedContent,
595
+ embedding: decryptedEmbedding
596
+ },
597
+ decryptionStatus: 'success'
598
+ };
599
+ } catch (decryptError: any) {
600
+ console.error('❌ v2.2 decryption failed:', decryptError.message);
601
+ return {
602
+ memoryPackage: basePackage,
603
+ decryptionStatus: 'failed',
604
+ error: decryptError.message
605
+ };
606
+ }
607
+ }
608
+
609
+ // No decryption context
610
+ return {
611
+ memoryPackage: basePackage,
612
+ decryptionStatus: 'failed',
613
+ error: 'No decryption context provided for v2.2 encrypted package'
614
+ };
615
+ }
616
+
617
+ // v2.1 JSON package with encrypted content only (no embedding on Walrus)
618
+ if (result.memoryPackage?.version === '2.1' && result.memoryPackage?.encryptedContent) {
619
+ console.log('🔐 Detected v2.1 JSON package (Full Encryption - Content only)');
620
+
621
+ const embeddingDimension = result.memoryPackage.metadata?.embeddingDimension || 0;
622
+ console.log(` embeddingDimension: ${embeddingDimension}D (stored locally, not on Walrus)`);
623
+
624
+ const basePackage: MemoryPackage = {
625
+ content: '[ENCRYPTED - requires decryption]',
626
+ contentType: 'text/plain',
627
+ metadata: result.memoryPackage.metadata,
628
+ embedding: [], // v2.1 has no embedding on Walrus
629
+ createdAt: result.memoryPackage.timestamp
630
+ };
631
+
632
+ if (decryptionContext && this.services.encryption) {
633
+ try {
634
+ console.log('🔐 Decrypting v2.1 content...');
635
+
636
+ const encryptedBase64 = result.memoryPackage.encryptedContent;
637
+ const binaryString = atob(encryptedBase64);
638
+ const encryptedBytes = new Uint8Array(binaryString.length);
639
+ for (let i = 0; i < binaryString.length; i++) {
640
+ encryptedBytes[i] = binaryString.charCodeAt(i);
641
+ }
642
+
643
+ const decryptedData = await this.services.encryption.decrypt({
644
+ encryptedContent: encryptedBytes,
645
+ userAddress: this.services.config.userAddress,
646
+ sessionKey: decryptionContext.sessionKey,
647
+ memoryCapId: decryptionContext.memoryCapId,
648
+ keyId: decryptionContext.keyId
649
+ });
650
+
651
+ const decryptedContent = new TextDecoder().decode(decryptedData);
652
+ console.log(`✅ v2.1 decryption successful: "${decryptedContent.substring(0, 50)}..."`);
653
+
654
+ return {
655
+ memoryPackage: {
656
+ ...basePackage,
657
+ content: decryptedContent
658
+ },
659
+ decryptionStatus: 'success'
660
+ };
661
+ } catch (decryptError: any) {
662
+ console.error('❌ v2.1 decryption failed:', decryptError.message);
663
+ return {
664
+ memoryPackage: basePackage,
665
+ decryptionStatus: 'failed',
666
+ error: decryptError.message
667
+ };
668
+ }
669
+ }
670
+
671
+ return {
672
+ memoryPackage: basePackage,
673
+ decryptionStatus: 'failed',
674
+ error: 'No decryption context provided for v2.1 encrypted package'
675
+ };
676
+ }
677
+
678
+ // v2.0 JSON package with encrypted content + plaintext embedding (legacy)
679
+ if (result.memoryPackage?.version === '2.0' && result.memoryPackage?.encryptedContent) {
680
+ console.log('📦 Detected v2.0 JSON package with encrypted content (legacy)');
681
+
682
+ // Get embedding from root level OR metadata.embedding (fallback for encryption service bug)
683
+ const embeddingArray = result.memoryPackage.embedding?.length > 0
684
+ ? result.memoryPackage.embedding
685
+ : result.memoryPackage.metadata?.embedding;
686
+
687
+ const embeddingSource = result.memoryPackage.embedding?.length > 0 ? 'root' : 'metadata';
688
+ console.log(` embedding: ${embeddingArray?.length || 0}D (from ${embeddingSource})`);
689
+
690
+ // Return embedding even without decryption (for index rebuilding)
691
+ const basePackage: MemoryPackage = {
692
+ content: '[ENCRYPTED - requires decryption]',
693
+ contentType: 'text/plain',
694
+ metadata: result.memoryPackage.metadata,
695
+ embedding: embeddingArray, // Plaintext embedding available!
696
+ createdAt: result.memoryPackage.timestamp
697
+ };
698
+
699
+ if (decryptionContext && this.services.encryption) {
700
+ try {
701
+ console.log('🔐 Decrypting v2.0 encryptedContent...');
702
+
703
+ // Convert base64 back to Uint8Array
704
+ const encryptedBase64 = result.memoryPackage.encryptedContent;
705
+ const binaryString = atob(encryptedBase64);
706
+ const encryptedBytes = new Uint8Array(binaryString.length);
707
+ for (let i = 0; i < binaryString.length; i++) {
708
+ encryptedBytes[i] = binaryString.charCodeAt(i);
709
+ }
710
+
711
+ const decryptedData = await this.services.encryption.decrypt({
712
+ encryptedContent: encryptedBytes,
713
+ userAddress: this.services.config.userAddress,
714
+ sessionKey: decryptionContext.sessionKey,
715
+ memoryCapId: decryptionContext.memoryCapId,
716
+ keyId: decryptionContext.keyId
717
+ });
718
+
719
+ const decryptedContent = new TextDecoder().decode(decryptedData);
720
+ console.log(`✅ v2.0 decryption successful, content: "${decryptedContent.substring(0, 50)}..."`);
721
+
722
+ return {
723
+ memoryPackage: {
724
+ ...basePackage,
725
+ content: decryptedContent
726
+ },
727
+ decryptionStatus: 'success'
728
+ };
729
+ } catch (decryptError: any) {
730
+ console.error('❌ v2.0 decryption failed:', decryptError.message);
731
+ return {
732
+ memoryPackage: basePackage, // Return with embedding but encrypted content
733
+ decryptionStatus: 'failed',
734
+ error: decryptError.message
735
+ };
736
+ }
737
+ }
738
+
739
+ // No decryption context - return with embedding only
740
+ return {
741
+ memoryPackage: basePackage,
742
+ decryptionStatus: 'failed',
743
+ error: 'No decryption context provided (embedding still available)'
744
+ };
745
+ }
746
+
747
+ // Legacy binary format (v0) - try to decrypt if context provided
748
+ if (decryptionContext && this.services.encryption) {
749
+ try {
750
+ console.log('🔐 Decrypting legacy binary format...');
751
+
752
+ const decryptedData = await this.services.encryption.decrypt({
753
+ encryptedContent: result.content,
754
+ userAddress: this.services.config.userAddress,
755
+ sessionKey: decryptionContext.sessionKey,
756
+ memoryCapId: decryptionContext.memoryCapId,
757
+ keyId: decryptionContext.keyId
758
+ });
759
+
760
+ const decryptedContent = new TextDecoder().decode(decryptedData);
761
+ console.log(`✅ Legacy decryption successful, content length: ${decryptedContent.length}`);
762
+
763
+ return {
764
+ memoryPackage: {
765
+ content: decryptedContent,
766
+ contentType: 'text/plain',
767
+ metadata: result.metadata,
768
+ createdAt: result.metadata.createdTimestamp
769
+ },
770
+ decryptionStatus: 'success'
771
+ };
772
+ } catch (decryptError: any) {
773
+ console.error('❌ Legacy decryption failed:', decryptError.message);
774
+ return {
775
+ memoryPackage: null,
776
+ decryptionStatus: 'failed',
777
+ error: decryptError.message
778
+ };
779
+ }
780
+ }
781
+
782
+ // Encrypted but no decryption context provided
783
+ return {
784
+ memoryPackage: null,
785
+ decryptionStatus: 'failed',
786
+ error: 'Content is encrypted but no decryption context provided'
787
+ };
788
+ }
789
+
790
+ /**
791
+ * Check if blob exists on Walrus
792
+ *
793
+ * @param blobId - Blob ID to check
794
+ * @returns True if blob exists
795
+ */
796
+ async exists(blobId: string): Promise<boolean> {
797
+ try {
798
+ await this.download(blobId);
799
+ return true;
800
+ } catch {
801
+ return false;
802
+ }
803
+ }
804
+
805
+ /**
806
+ * Get metadata for a blob
807
+ *
808
+ * @param blobId - Blob ID
809
+ * @returns Blob metadata or null
810
+ */
811
+ async getMetadata(blobId: string): Promise<{
812
+ blobId: string;
813
+ size?: number;
814
+ exists: boolean;
815
+ }> {
816
+ try {
817
+ const data = await this.download(blobId);
818
+ return {
819
+ blobId,
820
+ size: data.length,
821
+ exists: true
822
+ };
823
+ } catch {
824
+ return {
825
+ blobId,
826
+ exists: false
827
+ };
828
+ }
829
+ }
830
+
831
+ // ==========================================================================
832
+ // High-Level Decrypt API
833
+ // ==========================================================================
834
+
835
+ /**
836
+ * Retrieve and decrypt a memory package with minimal boilerplate.
837
+ * SDK handles all version detection, format conversion, and decryption internally.
838
+ *
839
+ * @param blobId - Blob ID on Walrus
840
+ * @param options - Decryption options
841
+ * @returns Decrypted content, embedding, and metadata
842
+ *
843
+ * @example
844
+ * ```typescript
845
+ * // With sign function (SDK creates session key)
846
+ * const result = await pdw.storage.retrieveAndDecrypt(blobId, {
847
+ * signFn: async (message) => {
848
+ * const sig = await signPersonalMessage({ message: new TextEncoder().encode(message) });
849
+ * return { signature: sig.signature };
850
+ * }
851
+ * });
852
+ *
853
+ * // With existing session key
854
+ * const result = await pdw.storage.retrieveAndDecrypt(blobId, { sessionKey });
855
+ *
856
+ * console.log(result.content); // "my name is Aaron"
857
+ * console.log(result.embedding); // [0.12, -0.34, ...] (3072D)
858
+ * console.log(result.version); // "2.2"
859
+ * ```
860
+ */
861
+ async retrieveAndDecrypt(
862
+ blobId: string,
863
+ options: {
864
+ /** Function to sign personal message (SDK will create session key) */
865
+ signFn?: (message: string) => Promise<{ signature: string }>;
866
+ /** Existing session key (skip signing if provided) */
867
+ sessionKey?: any;
868
+ /** Override memoryCapId (auto-detected from metadata if not provided) */
869
+ memoryCapId?: string;
870
+ /** Override keyId hex string (auto-detected from metadata if not provided) */
871
+ keyId?: string;
872
+ } = {}
873
+ ): Promise<{
874
+ content: string;
875
+ embedding: number[];
876
+ version: '2.2' | '2.1' | '2.0' | 'legacy' | 'plaintext';
877
+ isEncrypted: boolean;
878
+ metadata: Record<string, any>;
879
+ blobId: string;
880
+ }> {
881
+ if (!this.services.storage) {
882
+ throw new Error('Storage service not configured.');
883
+ }
884
+
885
+ console.log(`🔐 retrieveAndDecrypt: Downloading blob ${blobId}...`);
886
+
887
+ // Step 1: Download blob from Walrus
888
+ const blobData = await this.download(blobId);
889
+ console.log(` Downloaded ${blobData.length} bytes`);
890
+
891
+ // Step 2: Detect format and parse
892
+ let version: '2.2' | '2.1' | '2.0' | 'legacy' | 'plaintext' = 'legacy';
893
+ let isEncrypted = false;
894
+ let encryptedContentBase64: string | null = null;
895
+ let encryptedEmbeddingBase64: string | null = null;
896
+ let metadata: Record<string, any> = {};
897
+ let plainEmbedding: number[] = [];
898
+
899
+ try {
900
+ const blobText = new TextDecoder().decode(blobData);
901
+ const parsed = JSON.parse(blobText);
902
+
903
+ // v2.2: Full encryption (content + embedding both encrypted)
904
+ if (parsed.version === '2.2' && parsed.encryptedContent) {
905
+ version = '2.2';
906
+ isEncrypted = true;
907
+ encryptedContentBase64 = parsed.encryptedContent;
908
+ encryptedEmbeddingBase64 = parsed.encryptedEmbedding || null;
909
+ metadata = parsed.metadata || {};
910
+ console.log(` Detected v2.2: encrypted content + encrypted embedding`);
911
+ }
912
+ // v2.1: Encrypted content only (no embedding on Walrus)
913
+ else if (parsed.version === '2.1' && parsed.encryptedContent) {
914
+ version = '2.1';
915
+ isEncrypted = true;
916
+ encryptedContentBase64 = parsed.encryptedContent;
917
+ metadata = parsed.metadata || {};
918
+ console.log(` Detected v2.1: encrypted content only`);
919
+ }
920
+ // v2.0: Encrypted content + plaintext embedding
921
+ else if (parsed.version === '2.0' && parsed.encryptedContent) {
922
+ version = '2.0';
923
+ isEncrypted = true;
924
+ encryptedContentBase64 = parsed.encryptedContent;
925
+ plainEmbedding = parsed.embedding || [];
926
+ metadata = parsed.metadata || {};
927
+ console.log(` Detected v2.0: encrypted content + ${plainEmbedding.length}D plaintext embedding`);
928
+ }
929
+ // v1.0: Plaintext JSON package
930
+ else if (parsed.version && parsed.content) {
931
+ version = 'plaintext';
932
+ isEncrypted = false;
933
+ metadata = parsed.metadata || {};
934
+ plainEmbedding = parsed.embedding || [];
935
+ console.log(` Detected plaintext JSON package`);
936
+ return {
937
+ content: parsed.content,
938
+ embedding: plainEmbedding,
939
+ version,
940
+ isEncrypted,
941
+ metadata,
942
+ blobId
943
+ };
944
+ }
945
+ } catch {
946
+ // Not JSON - check if binary SEAL data
947
+ const isBinary = blobData.some(byte => byte < 32 && byte !== 9 && byte !== 10 && byte !== 13);
948
+ if (isBinary || blobData.some(byte => byte > 127)) {
949
+ version = 'legacy';
950
+ isEncrypted = true;
951
+ console.log(` Detected legacy binary format`);
952
+ } else {
953
+ // Plain text content
954
+ version = 'plaintext';
955
+ isEncrypted = false;
956
+ console.log(` Detected plaintext content`);
957
+ return {
958
+ content: new TextDecoder().decode(blobData),
959
+ embedding: [],
960
+ version,
961
+ isEncrypted,
962
+ metadata: {},
963
+ blobId
964
+ };
965
+ }
966
+ }
967
+
968
+ // Step 3: If not encrypted, return as-is
969
+ if (!isEncrypted) {
970
+ return {
971
+ content: new TextDecoder().decode(blobData),
972
+ embedding: plainEmbedding,
973
+ version,
974
+ isEncrypted,
975
+ metadata,
976
+ blobId
977
+ };
978
+ }
979
+
980
+ // Step 4: Get decryption parameters (from options or metadata)
981
+ const memoryCapId = options.memoryCapId || metadata.memoryCapId;
982
+ const keyIdHex = options.keyId || metadata.keyId;
983
+
984
+ if (!memoryCapId || !keyIdHex) {
985
+ throw new Error(
986
+ `Missing decryption parameters. memoryCapId=${!!memoryCapId}, keyId=${!!keyIdHex}. ` +
987
+ `Provide via options or ensure metadata contains these values.`
988
+ );
989
+ }
990
+
991
+ // Step 5: Convert keyId hex string to Uint8Array (SDK handles this!)
992
+ const keyIdBytes = new Uint8Array(
993
+ (keyIdHex.startsWith('0x') ? keyIdHex.slice(2) : keyIdHex)
994
+ .match(/.{1,2}/g)!
995
+ .map((byte: string) => parseInt(byte, 16))
996
+ );
997
+
998
+ // Step 6: Get or create session key
999
+ let sessionKey = options.sessionKey;
1000
+ if (!sessionKey) {
1001
+ if (!options.signFn) {
1002
+ throw new Error(
1003
+ 'Decryption requires either sessionKey or signFn. ' +
1004
+ 'Provide signFn to create session key automatically.'
1005
+ );
1006
+ }
1007
+ if (!this.services.encryption) {
1008
+ throw new Error('Encryption service not configured.');
1009
+ }
1010
+
1011
+ console.log(` Creating session key (will prompt for signature)...`);
1012
+ sessionKey = await this.services.encryption.createSessionKey(
1013
+ this.services.config.userAddress,
1014
+ {
1015
+ signPersonalMessageFn: async (message: string) => {
1016
+ return options.signFn!(message);
1017
+ }
1018
+ }
1019
+ );
1020
+ console.log(` Session key created`);
1021
+ }
1022
+
1023
+ // Step 7: Decrypt content
1024
+ console.log(` Decrypting content...`);
1025
+
1026
+ // Convert base64 to Uint8Array if needed
1027
+ let dataToDecrypt: Uint8Array;
1028
+ if (encryptedContentBase64) {
1029
+ const binaryString = atob(encryptedContentBase64);
1030
+ dataToDecrypt = new Uint8Array(binaryString.length);
1031
+ for (let i = 0; i < binaryString.length; i++) {
1032
+ dataToDecrypt[i] = binaryString.charCodeAt(i);
1033
+ }
1034
+ } else {
1035
+ dataToDecrypt = blobData;
1036
+ }
1037
+
1038
+ const decryptedContentData = await this.services.encryption!.decrypt({
1039
+ encryptedContent: dataToDecrypt,
1040
+ userAddress: this.services.config.userAddress,
1041
+ sessionKey,
1042
+ memoryCapId,
1043
+ keyId: keyIdBytes
1044
+ });
1045
+
1046
+ const content = new TextDecoder().decode(decryptedContentData);
1047
+ console.log(` Content decrypted: "${content.substring(0, 50)}${content.length > 50 ? '...' : ''}"`);
1048
+
1049
+ // Step 8: Decrypt embedding if v2.2
1050
+ let embedding: number[] = plainEmbedding;
1051
+
1052
+ if (version === '2.2' && encryptedEmbeddingBase64) {
1053
+ console.log(` Decrypting embedding...`);
1054
+ const embeddingBinaryString = atob(encryptedEmbeddingBase64);
1055
+ const encryptedEmbeddingBytes = new Uint8Array(embeddingBinaryString.length);
1056
+ for (let i = 0; i < embeddingBinaryString.length; i++) {
1057
+ encryptedEmbeddingBytes[i] = embeddingBinaryString.charCodeAt(i);
1058
+ }
1059
+
1060
+ const decryptedEmbeddingData = await this.services.encryption!.decrypt({
1061
+ encryptedContent: encryptedEmbeddingBytes,
1062
+ userAddress: this.services.config.userAddress,
1063
+ sessionKey,
1064
+ memoryCapId,
1065
+ keyId: keyIdBytes
1066
+ });
1067
+
1068
+ const embeddingJson = new TextDecoder().decode(decryptedEmbeddingData);
1069
+ embedding = JSON.parse(embeddingJson);
1070
+ console.log(` Embedding decrypted: ${embedding.length}D vector`);
1071
+ }
1072
+
1073
+ console.log(`✅ retrieveAndDecrypt complete: ${content.length} chars, ${embedding.length}D embedding`);
1074
+
1075
+ return {
1076
+ content,
1077
+ embedding,
1078
+ version,
1079
+ isEncrypted,
1080
+ metadata,
1081
+ blobId
1082
+ };
1083
+ }
1084
+
1085
+ // ==========================================================================
1086
+ // Batch Operations (Quilt)
1087
+ // ==========================================================================
1088
+
1089
+ /**
1090
+ * Upload multiple memories as a Quilt (batch upload)
1091
+ *
1092
+ * Uses Walrus Quilt for ~90% gas savings compared to individual uploads.
1093
+ * Requires 2 user signatures:
1094
+ * - Transaction 1: Register blob on-chain
1095
+ * - Transaction 2: Certify upload on-chain
1096
+ *
1097
+ * @param memories - Array of memories to upload
1098
+ * @param options - Upload options including signer
1099
+ * @returns Quilt result with file mappings
1100
+ *
1101
+ * @example
1102
+ * ```typescript
1103
+ * const result = await pdw.storage.uploadMemoryBatch(
1104
+ * memories,
1105
+ * {
1106
+ * signer: pdw.getConfig().signer,
1107
+ * epochs: 3,
1108
+ * userAddress: pdw.getConfig().userAddress
1109
+ * }
1110
+ * );
1111
+ * console.log(`Uploaded ${result.files.length} files`);
1112
+ * ```
1113
+ */
1114
+ async uploadMemoryBatch(
1115
+ memories: Array<{
1116
+ content: string;
1117
+ category: string;
1118
+ importance: number;
1119
+ topic: string;
1120
+ embedding: number[];
1121
+ encryptedContent: Uint8Array;
1122
+ summary?: string;
1123
+ id?: string;
1124
+ }>,
1125
+ options: {
1126
+ signer: any; // UnifiedSigner
1127
+ epochs?: number;
1128
+ userAddress: string;
1129
+ }
1130
+ ): Promise<{
1131
+ quiltId: string;
1132
+ files: Array<{ identifier: string; blobId: string }>;
1133
+ uploadTimeMs: number;
1134
+ }> {
1135
+ if (!this.services.storage) {
1136
+ throw new Error('Storage service not configured.');
1137
+ }
1138
+
1139
+ return this.services.storage.uploadMemoryBatch(memories, options);
1140
+ }
1141
+ }