@cmdoss/memwal-sdk 0.6.1 → 0.7.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 (148) hide show
  1. package/dist/ai-sdk/PDWVectorStore.d.ts.map +1 -1
  2. package/dist/ai-sdk/PDWVectorStore.js +4 -1
  3. package/dist/ai-sdk/PDWVectorStore.js.map +1 -1
  4. package/dist/ai-sdk/tools.d.ts +2 -2
  5. package/dist/ai-sdk/tools.js +2 -2
  6. package/dist/browser.d.ts +5 -6
  7. package/dist/browser.d.ts.map +1 -1
  8. package/dist/browser.js +7 -6
  9. package/dist/browser.js.map +1 -1
  10. package/dist/client/ClientMemoryManager.d.ts +1 -0
  11. package/dist/client/ClientMemoryManager.d.ts.map +1 -1
  12. package/dist/client/ClientMemoryManager.js +5 -1
  13. package/dist/client/ClientMemoryManager.js.map +1 -1
  14. package/dist/client/SimplePDWClient.d.ts +24 -1
  15. package/dist/client/SimplePDWClient.d.ts.map +1 -1
  16. package/dist/client/SimplePDWClient.js +31 -9
  17. package/dist/client/SimplePDWClient.js.map +1 -1
  18. package/dist/client/namespaces/EmbeddingsNamespace.d.ts +1 -1
  19. package/dist/client/namespaces/EmbeddingsNamespace.js +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/MemoryNamespace.d.ts +27 -0
  25. package/dist/client/namespaces/MemoryNamespace.d.ts.map +1 -1
  26. package/dist/client/namespaces/MemoryNamespace.js +104 -0
  27. package/dist/client/namespaces/MemoryNamespace.js.map +1 -1
  28. package/dist/client/namespaces/SearchNamespace.d.ts.map +1 -1
  29. package/dist/client/namespaces/SearchNamespace.js +25 -14
  30. package/dist/client/namespaces/SearchNamespace.js.map +1 -1
  31. package/dist/client/namespaces/consolidated/AINamespace.d.ts +2 -2
  32. package/dist/client/namespaces/consolidated/AINamespace.js +2 -2
  33. package/dist/client/namespaces/consolidated/BlockchainNamespace.d.ts.map +1 -1
  34. package/dist/client/namespaces/consolidated/BlockchainNamespace.js +69 -1
  35. package/dist/client/namespaces/consolidated/BlockchainNamespace.js.map +1 -1
  36. package/dist/client/namespaces/consolidated/StorageNamespace.d.ts +46 -0
  37. package/dist/client/namespaces/consolidated/StorageNamespace.d.ts.map +1 -1
  38. package/dist/client/namespaces/consolidated/StorageNamespace.js +34 -0
  39. package/dist/client/namespaces/consolidated/StorageNamespace.js.map +1 -1
  40. package/dist/graph/GraphService.js +2 -2
  41. package/dist/graph/GraphService.js.map +1 -1
  42. package/dist/index.d.ts +3 -1
  43. package/dist/index.d.ts.map +1 -1
  44. package/dist/index.js +3 -1
  45. package/dist/index.js.map +1 -1
  46. package/dist/permissions/ConsentRepository.browser.d.ts +56 -0
  47. package/dist/permissions/ConsentRepository.browser.d.ts.map +1 -0
  48. package/dist/permissions/ConsentRepository.browser.js +198 -0
  49. package/dist/permissions/ConsentRepository.browser.js.map +1 -0
  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/EmbeddingService.d.ts +28 -1
  55. package/dist/services/EmbeddingService.d.ts.map +1 -1
  56. package/dist/services/EmbeddingService.js +54 -0
  57. package/dist/services/EmbeddingService.js.map +1 -1
  58. package/dist/services/GeminiAIService.d.ts.map +1 -1
  59. package/dist/services/GeminiAIService.js +283 -27
  60. package/dist/services/GeminiAIService.js.map +1 -1
  61. package/dist/services/IndexManager.d.ts +5 -1
  62. package/dist/services/IndexManager.d.ts.map +1 -1
  63. package/dist/services/IndexManager.js +17 -40
  64. package/dist/services/IndexManager.js.map +1 -1
  65. package/dist/services/MemoryIndexService.d.ts +31 -2
  66. package/dist/services/MemoryIndexService.d.ts.map +1 -1
  67. package/dist/services/MemoryIndexService.js +75 -3
  68. package/dist/services/MemoryIndexService.js.map +1 -1
  69. package/dist/services/QueryService.js +1 -1
  70. package/dist/services/QueryService.js.map +1 -1
  71. package/dist/services/StorageService.d.ts +10 -0
  72. package/dist/services/StorageService.d.ts.map +1 -1
  73. package/dist/services/StorageService.js +13 -0
  74. package/dist/services/StorageService.js.map +1 -1
  75. package/dist/services/storage/QuiltBatchManager.d.ts +111 -4
  76. package/dist/services/storage/QuiltBatchManager.d.ts.map +1 -1
  77. package/dist/services/storage/QuiltBatchManager.js +450 -38
  78. package/dist/services/storage/QuiltBatchManager.js.map +1 -1
  79. package/dist/services/storage/index.d.ts +1 -1
  80. package/dist/services/storage/index.d.ts.map +1 -1
  81. package/dist/services/storage/index.js.map +1 -1
  82. package/dist/utils/LRUCache.d.ts +106 -0
  83. package/dist/utils/LRUCache.d.ts.map +1 -0
  84. package/dist/utils/LRUCache.js +281 -0
  85. package/dist/utils/LRUCache.js.map +1 -0
  86. package/dist/utils/index.d.ts +1 -0
  87. package/dist/utils/index.d.ts.map +1 -1
  88. package/dist/utils/index.js +2 -0
  89. package/dist/utils/index.js.map +1 -1
  90. package/dist/utils/memoryIndexOnChain.d.ts +212 -0
  91. package/dist/utils/memoryIndexOnChain.d.ts.map +1 -0
  92. package/dist/utils/memoryIndexOnChain.js +312 -0
  93. package/dist/utils/memoryIndexOnChain.js.map +1 -0
  94. package/dist/utils/rebuildIndexNode.d.ts +29 -0
  95. package/dist/utils/rebuildIndexNode.d.ts.map +1 -1
  96. package/dist/utils/rebuildIndexNode.js +387 -45
  97. package/dist/utils/rebuildIndexNode.js.map +1 -1
  98. package/dist/vector/HnswWasmService.d.ts +20 -5
  99. package/dist/vector/HnswWasmService.d.ts.map +1 -1
  100. package/dist/vector/HnswWasmService.js +73 -40
  101. package/dist/vector/HnswWasmService.js.map +1 -1
  102. package/dist/vector/IHnswService.d.ts +10 -1
  103. package/dist/vector/IHnswService.d.ts.map +1 -1
  104. package/dist/vector/IHnswService.js.map +1 -1
  105. package/dist/vector/NodeHnswService.d.ts +16 -0
  106. package/dist/vector/NodeHnswService.d.ts.map +1 -1
  107. package/dist/vector/NodeHnswService.js +108 -10
  108. package/dist/vector/NodeHnswService.js.map +1 -1
  109. package/dist/vector/createHnswService.d.ts +1 -1
  110. package/dist/vector/createHnswService.js +1 -1
  111. package/dist/vector/index.d.ts +1 -1
  112. package/dist/vector/index.js +1 -1
  113. package/package.json +157 -157
  114. package/src/ai-sdk/PDWVectorStore.ts +4 -1
  115. package/src/ai-sdk/tools.ts +2 -2
  116. package/src/browser.ts +15 -10
  117. package/src/client/ClientMemoryManager.ts +6 -1
  118. package/src/client/SimplePDWClient.ts +63 -10
  119. package/src/client/namespaces/EmbeddingsNamespace.ts +1 -1
  120. package/src/client/namespaces/IndexNamespace.ts +89 -11
  121. package/src/client/namespaces/MemoryNamespace.ts +137 -0
  122. package/src/client/namespaces/SearchNamespace.ts +27 -14
  123. package/src/client/namespaces/consolidated/AINamespace.ts +2 -2
  124. package/src/client/namespaces/consolidated/BlockchainNamespace.ts +73 -1
  125. package/src/client/namespaces/consolidated/StorageNamespace.ts +57 -0
  126. package/src/core/types/index.ts +1 -1
  127. package/src/generated/pdw/capability.ts +319 -319
  128. package/src/graph/GraphService.ts +2 -2
  129. package/src/index.ts +25 -1
  130. package/src/permissions/ConsentRepository.browser.ts +249 -0
  131. package/src/retrieval/MemoryRetrievalService.ts +78 -4
  132. package/src/services/EmbeddingService.ts +66 -1
  133. package/src/services/GeminiAIService.ts +283 -27
  134. package/src/services/IndexManager.ts +18 -45
  135. package/src/services/MemoryIndexService.ts +85 -3
  136. package/src/services/QueryService.ts +1 -1
  137. package/src/services/StorageService.ts +15 -0
  138. package/src/services/storage/QuiltBatchManager.ts +538 -42
  139. package/src/services/storage/index.ts +6 -1
  140. package/src/utils/LRUCache.ts +378 -0
  141. package/src/utils/index.ts +8 -0
  142. package/src/utils/memoryIndexOnChain.ts +507 -0
  143. package/src/utils/rebuildIndexNode.ts +482 -52
  144. package/src/vector/HnswWasmService.ts +95 -43
  145. package/src/vector/IHnswService.ts +10 -1
  146. package/src/vector/NodeHnswService.ts +130 -10
  147. package/src/vector/createHnswService.ts +1 -1
  148. package/src/vector/index.ts +1 -1
@@ -31,7 +31,7 @@ import type { Keypair } from '@mysten/sui/cryptography';
31
31
  import type { UnifiedSigner, WalletAdapter } from './signers';
32
32
  import { KeypairSigner, WalletAdapterSigner } from './signers';
33
33
  import { StorageService } from '../services/StorageService';
34
- import { EmbeddingService } from '../services/EmbeddingService';
34
+ import { EmbeddingService, getSharedEmbeddingService } from '../services/EmbeddingService';
35
35
  import { MemoryService } from '../services/MemoryService';
36
36
  import { QueryService } from '../services/QueryService';
37
37
  import { ClassifierService } from '../services/ClassifierService';
@@ -130,7 +130,7 @@ export interface SimplePDWConfig {
130
130
  modelName?: string;
131
131
  /**
132
132
  * Embedding dimensions
133
- * @default 768 (for google/openrouter), 1536 (for openai)
133
+ * @default 3072 (for google/openrouter), 1536 (for openai)
134
134
  */
135
135
  dimensions?: number;
136
136
  };
@@ -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
 
@@ -433,17 +458,19 @@ export class SimplePDWClient {
433
458
  useUploadRelay: true
434
459
  });
435
460
 
436
- // 2. Embedding Service (if API key provided)
461
+ // 2. Embedding Service (if API key provided) - uses singleton pattern
437
462
  let embedding: EmbeddingService | undefined;
438
463
  if (embeddingConfig.apiKey) {
439
- embedding = new EmbeddingService({
464
+ // Use singleton to share EmbeddingService across all clients
465
+ // Reduces memory usage and connection overhead for multi-user scenarios
466
+ embedding = getSharedEmbeddingService({
440
467
  provider: embeddingConfig.provider,
441
468
  apiKey: embeddingConfig.apiKey,
442
469
  modelName: embeddingConfig.modelName,
443
470
  dimensions: embeddingConfig.dimensions
444
471
  });
445
472
 
446
- console.log(`✅ Embedding Service initialized: ${embeddingConfig.provider}/${embeddingConfig.modelName} (${embeddingConfig.dimensions}d)`);
473
+ console.log(`✅ Embedding Service (singleton): ${embeddingConfig.provider}/${embeddingConfig.modelName} (${embeddingConfig.dimensions}d)`);
447
474
 
448
475
  // Connect to storage for search
449
476
  storage.initializeSearch(embedding);
@@ -473,6 +500,15 @@ export class SimplePDWClient {
473
500
  // Note: This starts async initialization - services will wait for it when needed
474
501
  let sharedHnswService: IHnswService | undefined;
475
502
  if (config.features.enableLocalIndexing) {
503
+ // Prepare Walrus backup config if enabled
504
+ const walrusBackupConfig = config.indexBackup?.enabled ? {
505
+ enabled: true,
506
+ aggregatorUrl: config.indexBackup.aggregatorUrl || config.walrus.aggregator,
507
+ publisherUrl: config.indexBackup.publisherUrl || config.walrus.publisher,
508
+ autoSync: config.indexBackup.autoSync ?? false,
509
+ epochs: config.indexBackup.epochs ?? 3
510
+ } : undefined;
511
+
476
512
  // Dynamic import to avoid bundling Node.js dependencies (hnswlib-node) in browser builds
477
513
  // When enableLocalIndexing is false, this code never runs and webpack won't bundle it
478
514
  this.sharedHnswServicePromise = import('../vector/createHnswService').then(
@@ -486,10 +522,14 @@ export class SimplePDWClient {
486
522
  batchConfig: {
487
523
  maxBatchSize: 100,
488
524
  batchDelayMs: 5000
489
- }
525
+ },
526
+ walrusBackup: walrusBackupConfig
490
527
  })
491
528
  );
492
529
  console.log('✅ Shared HNSW service initialization started (singleton for all vector services)');
530
+ if (walrusBackupConfig?.enabled) {
531
+ console.log(' ☁️ Walrus backup enabled for local index');
532
+ }
493
533
  }
494
534
 
495
535
  // 9a. Vector Service (if local indexing enabled)
@@ -722,11 +762,24 @@ export class SimplePDWClient {
722
762
  }
723
763
 
724
764
  // Initialize knowledge graph if enabled
725
- if (this.config.features.enableKnowledgeGraph && this.services.embedding) {
726
- await this.services.storage.initializeKnowledgeGraph({
765
+ // Use apiKey (OpenRouter) as primary, fallback to geminiApiKey for backward compatibility
766
+ const graphApiKey = this.config.ai.apiKey || this.config.ai.geminiApiKey;
767
+ if (this.config.features.enableKnowledgeGraph && this.services.embedding && graphApiKey) {
768
+ const graphService = await this.services.storage.initializeKnowledgeGraph({
727
769
  embeddingService: this.services.embedding,
728
- geminiApiKey: this.config.ai.geminiApiKey
770
+ geminiApiKey: graphApiKey
729
771
  });
772
+
773
+ // Update QueryService with the initialized GraphService
774
+ if (graphService && this.services.query && this.services.memoryIndex && this.services.embedding) {
775
+ this.services.query.initialize(
776
+ this.services.memoryIndex,
777
+ this.services.embedding,
778
+ this.services.storage,
779
+ graphService
780
+ );
781
+ console.log('✅ QueryService updated with Knowledge Graph');
782
+ }
730
783
  }
731
784
 
732
785
  this.initialized = true;
@@ -24,7 +24,7 @@ export class EmbeddingsNamespace {
24
24
  *
25
25
  * @param text - Text to embed
26
26
  * @param options - Embedding options
27
- * @returns Embedding vector (768 dimensions for Gemini)
27
+ * @returns Embedding vector (3072 dimensions for Gemini)
28
28
  */
29
29
  async generate(text: string, options?: { type?: 'query' | 'document' }): Promise<number[]> {
30
30
  if (!this.services.embedding) {
@@ -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
  *
@@ -1132,4 +1132,141 @@ export class MemoryNamespace {
1132
1132
  throw new Error(`Failed to export memories: ${error instanceof Error ? error.message : String(error)}`);
1133
1133
  }
1134
1134
  }
1135
+
1136
+ /**
1137
+ * Index memories from a Quilt into the local HNSW index
1138
+ *
1139
+ * This allows Quilt batch-uploaded memories to be searchable via vector search.
1140
+ * The method fetches memory packages from the Quilt and indexes each one.
1141
+ *
1142
+ * @param quiltId - The Quilt blob ID containing memory packages
1143
+ * @param options - Indexing options
1144
+ * @returns Result with number of memories indexed
1145
+ *
1146
+ * @example
1147
+ * ```typescript
1148
+ * // After uploading a batch via Quilt
1149
+ * const result = await pdw.memory.indexFromQuilt('GTNhNTdWecbUVg3cZuC_VKlRSzJE3iQfBkojWUuwoh0');
1150
+ * console.log(`Indexed ${result.indexed} memories from Quilt`);
1151
+ * ```
1152
+ */
1153
+ async indexFromQuilt(
1154
+ quiltId: string,
1155
+ options?: { forceReindex?: boolean }
1156
+ ): Promise<{
1157
+ success: boolean;
1158
+ total: number;
1159
+ indexed: number;
1160
+ skipped: number;
1161
+ failed: number;
1162
+ errors: string[];
1163
+ }> {
1164
+ const errors: string[] = [];
1165
+ let indexed = 0;
1166
+ let skipped = 0;
1167
+ let failed = 0;
1168
+
1169
+ try {
1170
+ console.log(`📦 Indexing memories from Quilt ${quiltId}...`);
1171
+
1172
+ // Check if index service is available
1173
+ if (!this.services.memoryIndex) {
1174
+ throw new Error('Memory index service not available');
1175
+ }
1176
+
1177
+ // Check if quilt manager is available
1178
+ if (!this.services.storage?.quiltBatchManager) {
1179
+ throw new Error('Quilt batch manager not available');
1180
+ }
1181
+
1182
+ // Fetch all memory packages from the Quilt
1183
+ const packages = await this.services.storage.quiltBatchManager.getAllMemoryPackages(quiltId);
1184
+ console.log(` Found ${packages.length} memory packages in Quilt`);
1185
+
1186
+ // Index each memory
1187
+ for (const pkg of packages) {
1188
+ try {
1189
+ const { identifier, memoryPackage, tags } = pkg;
1190
+
1191
+ // Skip if no embedding
1192
+ if (!memoryPackage.embedding || memoryPackage.embedding.length === 0) {
1193
+ console.warn(` ⚠️ Skipping ${identifier}: no embedding`);
1194
+ skipped++;
1195
+ continue;
1196
+ }
1197
+
1198
+ // Create a unique memory ID from identifier or use memoryId from metadata
1199
+ const memoryId = memoryPackage.metadata?.memoryId as string ||
1200
+ identifier.replace('.json', '').replace('memory-', '');
1201
+
1202
+ // Build metadata for indexing (satisfy MemoryMetadata interface)
1203
+ const metadata = {
1204
+ category: memoryPackage.metadata?.category || 'general',
1205
+ importance: memoryPackage.metadata?.importance || 3,
1206
+ topic: memoryPackage.metadata?.topic || '',
1207
+ contentType: 'text',
1208
+ contentSize: memoryPackage.content?.length || 0,
1209
+ contentHash: '', // Not available from Quilt package
1210
+ embeddingDimension: memoryPackage.embedding?.length || 0,
1211
+ createdTimestamp: memoryPackage.timestamp,
1212
+ customMetadata: {
1213
+ quiltId,
1214
+ identifier,
1215
+ ...Object.fromEntries(
1216
+ Object.entries(memoryPackage.metadata || {}).map(([k, v]) => [k, String(v)])
1217
+ )
1218
+ }
1219
+ };
1220
+
1221
+ // Content is empty if encrypted, use placeholder
1222
+ const content = memoryPackage.content || '[encrypted]';
1223
+ const isEncrypted = memoryPackage.encrypted === true;
1224
+
1225
+ // Index the memory
1226
+ await this.services.memoryIndex.indexMemory(
1227
+ this.services.config.userAddress,
1228
+ memoryId,
1229
+ quiltId, // Use quiltId as blobId for retrieval reference
1230
+ content,
1231
+ metadata,
1232
+ memoryPackage.embedding,
1233
+ { isEncrypted }
1234
+ );
1235
+
1236
+ indexed++;
1237
+ console.log(` ✅ Indexed ${identifier} (${indexed}/${packages.length})`);
1238
+
1239
+ } catch (pkgError) {
1240
+ const errMsg = pkgError instanceof Error ? pkgError.message : String(pkgError);
1241
+ errors.push(`${pkg.identifier}: ${errMsg}`);
1242
+ failed++;
1243
+ console.error(` ❌ Failed to index ${pkg.identifier}:`, errMsg);
1244
+ }
1245
+ }
1246
+
1247
+ console.log(`\n📊 Quilt indexing complete:`);
1248
+ console.log(` Total: ${packages.length}, Indexed: ${indexed}, Skipped: ${skipped}, Failed: ${failed}`);
1249
+
1250
+ return {
1251
+ success: failed === 0,
1252
+ total: packages.length,
1253
+ indexed,
1254
+ skipped,
1255
+ failed,
1256
+ errors
1257
+ };
1258
+
1259
+ } catch (error) {
1260
+ const errMsg = error instanceof Error ? error.message : String(error);
1261
+ console.error(`❌ Failed to index from Quilt: ${errMsg}`);
1262
+ return {
1263
+ success: false,
1264
+ total: 0,
1265
+ indexed,
1266
+ skipped,
1267
+ failed,
1268
+ errors: [errMsg, ...errors]
1269
+ };
1270
+ }
1271
+ }
1135
1272
  }
@@ -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
  });
@@ -76,12 +76,12 @@ export class AINamespace {
76
76
  *
77
77
  * @param text - Text to embed
78
78
  * @param options - Embedding options
79
- * @returns Embedding vector (768 dimensions for Gemini)
79
+ * @returns Embedding vector (3072 dimensions for Gemini)
80
80
  *
81
81
  * @example
82
82
  * ```typescript
83
83
  * const vector = await pdw.ai.embed('My favorite color is blue');
84
- * console.log(vector.length); // 768
84
+ * console.log(vector.length); // 3072
85
85
  * ```
86
86
  */
87
87
  async embed(text: string, options?: EmbedOptions): Promise<number[]> {
@@ -135,12 +135,84 @@ 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
+ // dapp-kit may not return full effects structure unless custom execute is configured
169
+ // We check multiple indicators:
170
+ // 1. If effects.status.status explicitly says 'failure', it failed
171
+ // 2. If effects.status.status says 'success', it succeeded
172
+ // 3. If we have a digest but no effects status, assume success (tx was submitted and confirmed)
173
+ let status: 'success' | 'failure';
174
+ const effectsStatus = result.effects?.status?.status;
175
+
176
+ if (effectsStatus === 'failure') {
177
+ status = 'failure';
178
+ } else if (effectsStatus === 'success') {
179
+ status = 'success';
180
+ } else if (result.digest) {
181
+ // Has digest but no explicit status - DappKitSigner waits for confirmation
182
+ // If we reach here without error, the transaction was successful
183
+ status = 'success';
184
+ } else {
185
+ status = 'failure';
186
+ }
187
+
188
+ return {
189
+ digest: result.digest,
190
+ status,
191
+ effects: result.effects,
192
+ createdObjects,
193
+ mutatedObjects,
194
+ gasUsed: result.effects?.gasUsed?.computationCost
195
+ ? Number(result.effects.gasUsed.computationCost)
196
+ : undefined,
197
+ error: status === 'failure' ? (result.effects?.status?.error || 'Transaction failed without digest') : undefined,
198
+ };
199
+ } catch (error) {
200
+ console.error('Transaction execution failed:', error);
201
+ return {
202
+ digest: '',
203
+ status: 'failure',
204
+ error: error instanceof Error ? error.message : String(error),
205
+ };
206
+ }
207
+ }
208
+
209
+ // Fallback: Use TransactionService with raw Signer (Node.js/backend)
138
210
  if (!this.services.tx) {
139
211
  throw new Error('Transaction service not configured.');
140
212
  }
141
213
  return await this.services.tx.executeTransaction(
142
214
  tx,
143
- this.services.config.signer.getSigner()
215
+ signer.getSigner()
144
216
  );
145
217
  }
146
218
 
@@ -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
  }
@@ -232,7 +232,7 @@ export interface VectorEmbedding {
232
232
 
233
233
  export interface EmbeddingOptions {
234
234
  model?: string;
235
- dimension?: number; // Default 768 for Gemini
235
+ dimension?: number; // Default 3072 for Gemini
236
236
  normalize?: boolean;
237
237
  batchSize?: number; // For batch processing
238
238
  }