@cmdoss/memwal-sdk 0.6.1 → 0.6.2

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 (82) hide show
  1. package/ARCHITECTURE.md +547 -547
  2. package/BENCHMARKS.md +238 -238
  3. package/README.md +181 -181
  4. package/dist/ai-sdk/PDWVectorStore.d.ts.map +1 -1
  5. package/dist/ai-sdk/PDWVectorStore.js +4 -1
  6. package/dist/ai-sdk/PDWVectorStore.js.map +1 -1
  7. package/dist/browser.d.ts +5 -6
  8. package/dist/browser.d.ts.map +1 -1
  9. package/dist/browser.js +7 -6
  10. package/dist/browser.js.map +1 -1
  11. package/dist/client/ClientMemoryManager.d.ts +1 -0
  12. package/dist/client/ClientMemoryManager.d.ts.map +1 -1
  13. package/dist/client/ClientMemoryManager.js +5 -1
  14. package/dist/client/ClientMemoryManager.js.map +1 -1
  15. package/dist/client/PersonalDataWallet.d.ts.map +1 -1
  16. package/dist/client/SimplePDWClient.d.ts +23 -0
  17. package/dist/client/SimplePDWClient.d.ts.map +1 -1
  18. package/dist/client/SimplePDWClient.js +15 -2
  19. package/dist/client/SimplePDWClient.js.map +1 -1
  20. package/dist/client/namespaces/IndexNamespace.d.ts +38 -9
  21. package/dist/client/namespaces/IndexNamespace.d.ts.map +1 -1
  22. package/dist/client/namespaces/IndexNamespace.js +77 -10
  23. package/dist/client/namespaces/IndexNamespace.js.map +1 -1
  24. package/dist/client/namespaces/SearchNamespace.d.ts.map +1 -1
  25. package/dist/client/namespaces/SearchNamespace.js +25 -14
  26. package/dist/client/namespaces/SearchNamespace.js.map +1 -1
  27. package/dist/client/namespaces/consolidated/BlockchainNamespace.d.ts.map +1 -1
  28. package/dist/client/namespaces/consolidated/BlockchainNamespace.js +49 -1
  29. package/dist/client/namespaces/consolidated/BlockchainNamespace.js.map +1 -1
  30. package/dist/client/namespaces/consolidated/StorageNamespace.d.ts +46 -0
  31. package/dist/client/namespaces/consolidated/StorageNamespace.d.ts.map +1 -1
  32. package/dist/client/namespaces/consolidated/StorageNamespace.js +34 -0
  33. package/dist/client/namespaces/consolidated/StorageNamespace.js.map +1 -1
  34. package/dist/graph/GraphService.js +1 -1
  35. package/dist/permissions/ConsentRepository.browser.d.ts +56 -0
  36. package/dist/permissions/ConsentRepository.browser.d.ts.map +1 -0
  37. package/dist/permissions/ConsentRepository.browser.js +198 -0
  38. package/dist/permissions/ConsentRepository.browser.js.map +1 -0
  39. package/dist/services/GeminiAIService.d.ts.map +1 -1
  40. package/dist/services/GeminiAIService.js +283 -27
  41. package/dist/services/GeminiAIService.js.map +1 -1
  42. package/dist/services/MemoryIndexService.d.ts +31 -2
  43. package/dist/services/MemoryIndexService.d.ts.map +1 -1
  44. package/dist/services/MemoryIndexService.js +75 -3
  45. package/dist/services/MemoryIndexService.js.map +1 -1
  46. package/dist/services/storage/QuiltBatchManager.d.ts +10 -3
  47. package/dist/services/storage/QuiltBatchManager.d.ts.map +1 -1
  48. package/dist/services/storage/QuiltBatchManager.js +49 -27
  49. package/dist/services/storage/QuiltBatchManager.js.map +1 -1
  50. package/dist/utils/rebuildIndexNode.d.ts.map +1 -1
  51. package/dist/utils/rebuildIndexNode.js +109 -35
  52. package/dist/utils/rebuildIndexNode.js.map +1 -1
  53. package/dist/vector/NodeHnswService.d.ts.map +1 -1
  54. package/dist/vector/NodeHnswService.js +26 -7
  55. package/dist/vector/NodeHnswService.js.map +1 -1
  56. package/package.json +1 -1
  57. package/src/access/index.ts +8 -8
  58. package/src/aggregation/index.ts +8 -8
  59. package/src/ai-sdk/PDWVectorStore.ts +4 -1
  60. package/src/browser.ts +15 -10
  61. package/src/client/ClientMemoryManager.ts +6 -1
  62. package/src/client/SimplePDWClient.ts +40 -2
  63. package/src/client/namespaces/IndexNamespace.ts +89 -11
  64. package/src/client/namespaces/SearchNamespace.ts +27 -14
  65. package/src/client/namespaces/consolidated/BlockchainNamespace.ts +55 -1
  66. package/src/client/namespaces/consolidated/StorageNamespace.ts +57 -0
  67. package/src/client/signers/DappKitSigner.ts +207 -207
  68. package/src/generated/pdw/capability.ts +319 -319
  69. package/src/generated/pdw/deps/sui/object.ts +12 -12
  70. package/src/generated/pdw/deps/sui/vec_map.ts +32 -32
  71. package/src/generated/pdw/memory.ts +1087 -1087
  72. package/src/generated/pdw/wallet.ts +123 -123
  73. package/src/generated/utils/index.ts +159 -159
  74. package/src/graph/GraphService.ts +1 -1
  75. package/src/permissions/ConsentRepository.browser.ts +249 -0
  76. package/src/permissions/index.ts +9 -9
  77. package/src/services/GeminiAIService.ts +283 -27
  78. package/src/services/MemoryIndexService.ts +85 -3
  79. package/src/services/storage/QuiltBatchManager.ts +55 -29
  80. package/src/utils/rebuildIndexNode.ts +126 -43
  81. package/src/vector/NodeHnswService.ts +29 -7
  82. package/src/wallet/index.ts +17 -17
@@ -190,6 +190,23 @@ export interface SimplePDWConfig {
190
190
  /** Progress callback for index operations */
191
191
  onProgress?: IndexProgressCallback;
192
192
  };
193
+
194
+ /**
195
+ * Optional: Index backup to Walrus cloud storage
196
+ * Enables syncing local HNSW index to Walrus for cross-device restoration
197
+ */
198
+ indexBackup?: {
199
+ /** Enable Walrus backup for local index */
200
+ enabled: boolean;
201
+ /** Walrus aggregator URL for downloading */
202
+ aggregatorUrl?: string;
203
+ /** Walrus publisher URL for uploading */
204
+ publisherUrl?: string;
205
+ /** Auto-sync index to Walrus on every save (default: false) */
206
+ autoSync?: boolean;
207
+ /** Storage duration in epochs (default: 3) */
208
+ epochs?: number;
209
+ };
193
210
  }
194
211
 
195
212
  /**
@@ -239,6 +256,13 @@ interface ResolvedConfig {
239
256
  enableAutoSave?: boolean;
240
257
  onProgress?: IndexProgressCallback;
241
258
  };
259
+ indexBackup?: {
260
+ enabled: boolean;
261
+ aggregatorUrl?: string;
262
+ publisherUrl?: string;
263
+ autoSync?: boolean;
264
+ epochs?: number;
265
+ };
242
266
  }
243
267
 
244
268
  /**
@@ -360,7 +384,8 @@ export class SimplePDWClient {
360
384
  enableLocalIndexing: config.features?.enableLocalIndexing ?? true,
361
385
  enableKnowledgeGraph: config.features?.enableKnowledgeGraph ?? true
362
386
  },
363
- indexManager: config.indexManager
387
+ indexManager: config.indexManager,
388
+ indexBackup: config.indexBackup
364
389
  };
365
390
  }
366
391
 
@@ -473,6 +498,15 @@ export class SimplePDWClient {
473
498
  // Note: This starts async initialization - services will wait for it when needed
474
499
  let sharedHnswService: IHnswService | undefined;
475
500
  if (config.features.enableLocalIndexing) {
501
+ // Prepare Walrus backup config if enabled
502
+ const walrusBackupConfig = config.indexBackup?.enabled ? {
503
+ enabled: true,
504
+ aggregatorUrl: config.indexBackup.aggregatorUrl || config.walrus.aggregator,
505
+ publisherUrl: config.indexBackup.publisherUrl || config.walrus.publisher,
506
+ autoSync: config.indexBackup.autoSync ?? false,
507
+ epochs: config.indexBackup.epochs ?? 3
508
+ } : undefined;
509
+
476
510
  // Dynamic import to avoid bundling Node.js dependencies (hnswlib-node) in browser builds
477
511
  // When enableLocalIndexing is false, this code never runs and webpack won't bundle it
478
512
  this.sharedHnswServicePromise = import('../vector/createHnswService').then(
@@ -486,10 +520,14 @@ export class SimplePDWClient {
486
520
  batchConfig: {
487
521
  maxBatchSize: 100,
488
522
  batchDelayMs: 5000
489
- }
523
+ },
524
+ walrusBackup: walrusBackupConfig
490
525
  })
491
526
  );
492
527
  console.log('✅ Shared HNSW service initialization started (singleton for all vector services)');
528
+ if (walrusBackupConfig?.enabled) {
529
+ console.log(' ☁️ Walrus backup enabled for local index');
530
+ }
493
531
  }
494
532
 
495
533
  // 9a. Vector Service (if local indexing enabled)
@@ -189,13 +189,11 @@ export class IndexNamespace {
189
189
  }
190
190
 
191
191
  /**
192
- * Save index to Walrus storage
192
+ * Save index to local storage
193
193
  *
194
- * Persists the HNSW index binary to Walrus for durability.
195
- * Uses HnswWasmService.saveIndex() which properly serializes the index.
194
+ * Persists the HNSW index binary to local filesystem.
196
195
  *
197
196
  * @param spaceId - Index space identifier (userAddress)
198
- * @returns Blob ID of saved index on Walrus, or null if no index exists
199
197
  */
200
198
  async save(spaceId: string): Promise<void> {
201
199
  const { type, service } = this.getService();
@@ -211,26 +209,106 @@ export class IndexNamespace {
211
209
  }
212
210
 
213
211
  /**
214
- * Load index from Walrus storage
212
+ * Load index from storage (local or Walrus)
215
213
  *
216
- * Loads a previously saved HNSW index from Walrus.
217
- * Uses HnswWasmService.loadIndex() which properly deserializes the binary.
214
+ * If blobId is provided, attempts to load from Walrus first.
215
+ * Falls back to local storage if Walrus load fails.
218
216
  *
219
217
  * @param spaceId - Index space identifier (userAddress)
220
- * @param blobId - Blob ID of the saved index on Walrus
218
+ * @param blobId - Optional Walrus blob ID to load from cloud
221
219
  */
222
- async load(spaceId: string, blobId: string): Promise<void> {
220
+ async load(spaceId: string, blobId?: string): Promise<void> {
223
221
  const { type, service } = this.getService();
224
222
 
225
223
  if (type === 'memoryIndex') {
226
- // MemoryIndexService.loadIndex(userAddress, indexBlobId?)
227
224
  await service.loadIndex(spaceId, blobId);
228
- console.log(`Index loaded from Walrus: ${blobId}`);
225
+ if (blobId) {
226
+ console.log(`Index loaded from Walrus: ${blobId}`);
227
+ } else {
228
+ console.log(`Index loaded from local storage: ${spaceId}`);
229
+ }
229
230
  } else {
230
231
  await service.loadIndex(spaceId, blobId);
231
232
  }
232
233
  }
233
234
 
235
+ /**
236
+ * Sync index to Walrus cloud storage
237
+ *
238
+ * Uploads the HNSW index binary + metadata to Walrus for durability.
239
+ * This enables cross-device index restoration.
240
+ *
241
+ * @param spaceId - Index space identifier (userAddress)
242
+ * @returns Walrus blob ID if successful, null if Walrus is disabled
243
+ */
244
+ async syncToWalrus(spaceId: string): Promise<string | null> {
245
+ const { type, service } = this.getService();
246
+
247
+ if (type === 'memoryIndex' && 'syncToWalrus' in service) {
248
+ const blobId = await service.syncToWalrus(spaceId);
249
+ if (blobId) {
250
+ console.log(`Index synced to Walrus: ${blobId}`);
251
+ }
252
+ return blobId;
253
+ }
254
+
255
+ console.warn('Walrus sync not available for this service type');
256
+ return null;
257
+ }
258
+
259
+ /**
260
+ * Load index from Walrus cloud storage
261
+ *
262
+ * Downloads and restores a previously synced index from Walrus.
263
+ *
264
+ * @param spaceId - Index space identifier (userAddress)
265
+ * @param blobId - Walrus blob ID of the saved index
266
+ * @returns true if successfully loaded
267
+ */
268
+ async loadFromWalrus(spaceId: string, blobId: string): Promise<boolean> {
269
+ const { type, service } = this.getService();
270
+
271
+ if (type === 'memoryIndex' && 'loadFromWalrus' in service) {
272
+ const loaded = await service.loadFromWalrus(spaceId, blobId);
273
+ if (loaded) {
274
+ console.log(`Index loaded from Walrus: ${blobId}`);
275
+ }
276
+ return loaded;
277
+ }
278
+
279
+ console.warn('Walrus load not available for this service type');
280
+ return false;
281
+ }
282
+
283
+ /**
284
+ * Get the Walrus blob ID for a user's index (if backed up)
285
+ *
286
+ * @param spaceId - Index space identifier (userAddress)
287
+ * @returns Blob ID or null if not backed up
288
+ */
289
+ getWalrusBlobId(spaceId: string): string | null {
290
+ const { type, service } = this.getService();
291
+
292
+ if (type === 'memoryIndex' && 'getWalrusBlobId' in service) {
293
+ return service.getWalrusBlobId(spaceId);
294
+ }
295
+
296
+ return null;
297
+ }
298
+
299
+ /**
300
+ * Check if Walrus backup is enabled
301
+ */
302
+ isWalrusEnabled(): boolean {
303
+ const { type, service } = this.getService();
304
+
305
+ if (type === 'memoryIndex' && 'isWalrusEnabled' in service) {
306
+ return service.isWalrusEnabled();
307
+ }
308
+
309
+ return false;
310
+ }
311
+
234
312
  /**
235
313
  * Clear index and remove all vectors
236
314
  *
@@ -154,18 +154,26 @@ export class SearchNamespace {
154
154
 
155
155
  // Convert to SearchResult format
156
156
  // Option A+: Content may be available from local index when encryption is OFF
157
- const searchResults: SearchResult[] = results.map((r: any) => ({
158
- id: r.memoryId || r.vectorId.toString(),
159
- content: r.metadata?.content || r.content || '', // Get content from index metadata if available
160
- score: r.similarity,
161
- similarity: r.similarity,
162
- category: r.metadata?.category,
163
- importance: r.metadata?.importance || 5,
164
- topic: r.metadata?.topic,
165
- blobId: r.metadata?.blobId || r.memoryId || r.vectorId.toString(),
166
- metadata: r.metadata || {},
167
- timestamp: r.metadata?.timestamp || Date.now()
168
- }));
157
+ const searchResults: SearchResult[] = results.map((r: any) => {
158
+ // blobId must be a valid Walrus blob ID, not a vectorId
159
+ // Only use metadata.blobId if it's a non-empty string that looks like a Walrus blobId
160
+ const rawBlobId = r.metadata?.blobId;
161
+ const isValidBlobId = rawBlobId && typeof rawBlobId === 'string' && rawBlobId.length > 10 && !/^\d+$/.test(rawBlobId);
162
+ const blobId = isValidBlobId ? rawBlobId : (r.metadata?.memoryObjectId || '');
163
+
164
+ return {
165
+ id: r.memoryId || r.vectorId.toString(),
166
+ content: r.metadata?.content || r.content || '', // ✅ Get content from index metadata if available
167
+ score: r.similarity,
168
+ similarity: r.similarity,
169
+ category: r.metadata?.category,
170
+ importance: r.metadata?.importance || 5,
171
+ topic: r.metadata?.topic,
172
+ blobId,
173
+ metadata: r.metadata || {},
174
+ timestamp: r.metadata?.timestamp || Date.now()
175
+ };
176
+ });
169
177
 
170
178
  // Optionally fetch content from Walrus
171
179
  if (fetchContent) {
@@ -329,7 +337,12 @@ export class SearchNamespace {
329
337
  const localResults = this.services.vector.getVectorsByCategory(spaceId, category);
330
338
 
331
339
  for (const { vectorId, metadata } of localResults) {
332
- const id = metadata?.blobId || metadata?.memoryId || vectorId?.toString();
340
+ // blobId must be a valid Walrus blob ID, not a vectorId
341
+ const rawBlobId = metadata?.blobId;
342
+ const isValidBlobId = rawBlobId && typeof rawBlobId === 'string' && rawBlobId.length > 10 && !/^\d+$/.test(rawBlobId);
343
+ const blobId = isValidBlobId ? rawBlobId : (metadata?.memoryObjectId || '');
344
+ const id = blobId || metadata?.memoryId || vectorId?.toString();
345
+
333
346
  if (id && !seenIds.has(id)) {
334
347
  seenIds.add(id);
335
348
  results.push({
@@ -340,7 +353,7 @@ export class SearchNamespace {
340
353
  category: metadata?.category,
341
354
  importance: metadata?.importance || 5,
342
355
  topic: metadata?.topic,
343
- blobId: id,
356
+ blobId,
344
357
  metadata: metadata || {},
345
358
  timestamp: metadata?.timestamp || Date.now()
346
359
  });
@@ -135,12 +135,66 @@ class TxSubNamespace {
135
135
  * ```
136
136
  */
137
137
  async execute(tx: Transaction): Promise<TransactionResult> {
138
+ const signer = this.services.config.signer;
139
+
140
+ // Check if signer supports signAndExecuteTransaction (browser wallets like DappKitSigner)
141
+ // Browser wallets cannot expose raw Signer for security reasons
142
+ if ('signAndExecuteTransaction' in signer && typeof signer.signAndExecuteTransaction === 'function') {
143
+ try {
144
+ // Use the signer's signAndExecuteTransaction directly
145
+ const result = await signer.signAndExecuteTransaction(tx);
146
+
147
+ // Get full transaction details to extract created objects
148
+ let createdObjects: Array<{ objectId: string; objectType: string }> | undefined;
149
+ let mutatedObjects: Array<{ objectId: string; objectType: string }> | undefined;
150
+
151
+ if (result.objectChanges && Array.isArray(result.objectChanges)) {
152
+ createdObjects = result.objectChanges
153
+ .filter((change: any) => change.type === 'created')
154
+ .map((change: any) => ({
155
+ objectId: change.objectId,
156
+ objectType: change.objectType || 'unknown',
157
+ }));
158
+
159
+ mutatedObjects = result.objectChanges
160
+ .filter((change: any) => change.type === 'mutated')
161
+ .map((change: any) => ({
162
+ objectId: change.objectId,
163
+ objectType: change.objectType || 'unknown',
164
+ }));
165
+ }
166
+
167
+ // Determine status from effects
168
+ const status = result.effects?.status?.status === 'success' ? 'success' : 'failure';
169
+
170
+ return {
171
+ digest: result.digest,
172
+ status,
173
+ effects: result.effects,
174
+ createdObjects,
175
+ mutatedObjects,
176
+ gasUsed: result.effects?.gasUsed?.computationCost
177
+ ? Number(result.effects.gasUsed.computationCost)
178
+ : undefined,
179
+ error: status === 'failure' ? result.effects?.status?.error : undefined,
180
+ };
181
+ } catch (error) {
182
+ console.error('Transaction execution failed:', error);
183
+ return {
184
+ digest: '',
185
+ status: 'failure',
186
+ error: error instanceof Error ? error.message : String(error),
187
+ };
188
+ }
189
+ }
190
+
191
+ // Fallback: Use TransactionService with raw Signer (Node.js/backend)
138
192
  if (!this.services.tx) {
139
193
  throw new Error('Transaction service not configured.');
140
194
  }
141
195
  return await this.services.tx.executeTransaction(
142
196
  tx,
143
- this.services.config.signer.getSigner()
197
+ signer.getSigner()
144
198
  );
145
199
  }
146
200
 
@@ -437,4 +437,61 @@ export class StorageNamespace {
437
437
  };
438
438
  }
439
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
+ }
440
497
  }