@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,466 +1,466 @@
1
- /**
2
- * ClassifierService - Content Classification and Filtering
3
- *
4
- * Determines if content should be saved as memory using pattern matching
5
- * and AI classification. Provides category classification for content organization.
6
- */
7
-
8
- import { EmbeddingService } from './EmbeddingService';
9
- import type { ClientWithCoreApi, PDWConfig } from '../types';
10
-
11
- export interface ClassificationResult {
12
- shouldSave: boolean;
13
- confidence: number;
14
- category: string;
15
- reasoning: string;
16
- }
17
-
18
- export interface ClassificationOptions {
19
- useAI?: boolean;
20
- confidenceThreshold?: number;
21
- categories?: string[];
22
- patterns?: RegExp[];
23
- /**
24
- * If true, only explicit memory commands trigger save (e.g., "remember that", "store in memory")
25
- * Default: true - to give users control over what gets saved
26
- */
27
- explicitOnly?: boolean;
28
- }
29
-
30
- export interface PatternAnalysisResult {
31
- patterns: string[];
32
- categories: string[];
33
- matches: Array<{
34
- pattern: string;
35
- match: string;
36
- category: string;
37
- confidence: number;
38
- }>;
39
- }
40
-
41
- /**
42
- * Content classification service for determining memory worthiness
43
- */
44
- export class ClassifierService {
45
- private embeddingService?: EmbeddingService;
46
- private aiApiKey?: string;
47
-
48
- // Explicit memory command patterns - only these trigger by default
49
- private readonly explicitPatterns: RegExp[] = [
50
- // Explicit memory requests
51
- /remember that ([^.!?]+)/i,
52
- /remember:?\s*([^.!?]+)/i,
53
- /don't forget that ([^.!?]+)/i,
54
- /don't forget:?\s*([^.!?]+)/i,
55
- /please remember ([^.!?]+)/i,
56
- /store (?:this )?(?:in|to) memory:?\s*([^.!?]*)/i,
57
- /save (?:this )?(?:in|to) memory:?\s*([^.!?]*)/i,
58
- /add (?:this )?to (?:my )?memory:?\s*([^.!?]*)/i,
59
- /keep in mind:?\s*([^.!?]+)/i,
60
- /note that ([^.!?]+)/i,
61
- ];
62
-
63
- // Full regex patterns for detecting factual statements (used when explicitOnly=false)
64
- private readonly factPatterns: RegExp[] = [
65
- // Include explicit patterns first
66
- ...this.explicitPatterns,
67
-
68
- // Personal information
69
- /my name is ([a-zA-Z\s]+)/i,
70
- /my email is ([a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,})/i,
71
- /i live in ([a-zA-Z\s,]+)/i,
72
- /i work at ([a-zA-Z\s,&]+)/i,
73
- /i am from ([a-zA-Z\s,]+)/i,
74
- /i was born in ([a-zA-Z0-9\s,]+)/i,
75
- /my birthday is ([a-zA-Z0-9\s,]+)/i,
76
- /my phone (?:number|#) is ([0-9+\-\s()]+)/i,
77
- /my address is ([a-zA-Z0-9\s,]+)/i,
78
-
79
- // Preferences and likes/dislikes
80
- /i love ([^.!?]+)/i,
81
- /i like ([^.!?]+)/i,
82
- /i enjoy ([^.!?]+)/i,
83
- /i prefer ([^.!?]+)/i,
84
- /i hate ([^.!?]+)/i,
85
- /i dislike ([^.!?]+)/i,
86
- /i don't like ([^.!?]+)/i,
87
- /my favorite ([^.!?]+) is ([^.!?]+)/i,
88
- /my favourite ([^.!?]+) is ([^.!?]+)/i,
89
-
90
- // Personal facts
91
- /i am ([^.!?]+)/i,
92
- /i have ([^.!?]+)/i,
93
- /i own ([^.!?]+)/i,
94
- /i studied ([^.!?]+)/i,
95
- /i graduated from ([^.!?]+)/i,
96
- ];
97
-
98
- // Map of regex patterns to categories (adapted from backend)
99
- private readonly categoryMap: Record<string, string> = {
100
- // Personal information
101
- 'my name is': 'personal_info',
102
- 'my email is': 'contact',
103
- 'i live in': 'location',
104
- 'i work at': 'career',
105
- 'i am from': 'background',
106
- 'i was born': 'background',
107
- 'my birthday': 'personal_info',
108
- 'my phone': 'contact',
109
- 'my address': 'contact',
110
-
111
- // Preferences
112
- 'i love': 'preference',
113
- 'i like': 'preference',
114
- 'i enjoy': 'preference',
115
- 'i prefer': 'preference',
116
- 'i hate': 'preference',
117
- 'i dislike': 'preference',
118
- "i don't like": 'preference',
119
- 'my favorite': 'preference',
120
- 'my favourite': 'preference',
121
-
122
- // Explicit memory requests
123
- 'remember that': 'custom',
124
- 'remember': 'custom',
125
- "don't forget": 'custom',
126
- 'please remember': 'custom',
127
- 'store': 'custom',
128
- 'save': 'custom',
129
- 'add': 'custom',
130
- 'keep in mind': 'custom',
131
- 'note that': 'custom',
132
-
133
- // Personal facts
134
- 'i am': 'personal_info',
135
- 'i have': 'personal_info',
136
- 'i own': 'personal_info',
137
- 'i studied': 'education',
138
- 'i graduated': 'education',
139
- };
140
-
141
- constructor(
142
- private client?: ClientWithCoreApi,
143
- private config?: PDWConfig,
144
- embeddingService?: EmbeddingService,
145
- aiApiKey?: string
146
- ) {
147
- this.embeddingService = embeddingService;
148
- this.aiApiKey = aiApiKey;
149
- }
150
-
151
- /**
152
- * Determine if a message contains information worth saving
153
- * @param message User message to classify
154
- * @param options Classification options
155
- * @returns Classification result
156
- */
157
- async shouldSaveMemory(message: string, options: ClassificationOptions = {}): Promise<ClassificationResult> {
158
- try {
159
- // Input validation
160
- if (!message || typeof message !== 'string' || message.trim().length === 0) {
161
- return {
162
- shouldSave: false,
163
- confidence: 0,
164
- category: 'general',
165
- reasoning: 'Empty or invalid message'
166
- };
167
- }
168
-
169
- const {
170
- useAI = true,
171
- confidenceThreshold = 0.7,
172
- explicitOnly = true, // Default to explicit-only mode for user control
173
- patterns = explicitOnly ? this.explicitPatterns : this.factPatterns
174
- } = options;
175
-
176
- // Step 1: Check for obvious patterns using regex
177
- const patternResult = this.matchPatterns(message, patterns);
178
- if (patternResult.matched && patternResult.confidence >= confidenceThreshold) {
179
- return {
180
- shouldSave: true,
181
- confidence: patternResult.confidence,
182
- category: patternResult.category,
183
- reasoning: `Matched pattern: ${patternResult.pattern}`
184
- };
185
- }
186
-
187
- // Step 2: Use AI for more complex classification (if enabled and available)
188
- if (useAI && (this.embeddingService || this.aiApiKey)) {
189
- try {
190
- const aiResult = await this.classifyWithAI(message, options);
191
- if (aiResult.confidence >= confidenceThreshold) {
192
- return aiResult;
193
- }
194
- } catch (aiError) {
195
- if (process.env.NODE_ENV === 'development') {
196
- const errorMessage = aiError instanceof Error ? aiError.message : String(aiError);
197
- console.warn('AI classification failed, falling back to pattern-only result:', errorMessage);
198
- }
199
- }
200
- }
201
-
202
- // Step 3: Return pattern result even if below threshold
203
- if (patternResult.matched) {
204
- return {
205
- shouldSave: patternResult.confidence >= confidenceThreshold,
206
- confidence: patternResult.confidence,
207
- category: patternResult.category,
208
- reasoning: `Pattern match (confidence: ${patternResult.confidence})`
209
- };
210
- }
211
-
212
- // Step 4: Default to not saving
213
- return {
214
- shouldSave: false,
215
- confidence: 0.1,
216
- category: 'general',
217
- reasoning: 'No significant patterns detected and AI classification unavailable or inconclusive'
218
- };
219
-
220
- } catch (error) {
221
- if (process.env.NODE_ENV === 'development') {
222
- console.error('Error in classification:', error);
223
- }
224
- const errorMessage = error instanceof Error ? error.message : String(error);
225
- return {
226
- shouldSave: false,
227
- confidence: 0,
228
- category: 'error',
229
- reasoning: `Classification error: ${errorMessage}`
230
- };
231
- }
232
- }
233
-
234
- /**
235
- * Classify content into categories
236
- * @param content Content to classify
237
- * @param options Classification options
238
- * @returns Category string
239
- */
240
- async classifyContent(content: string, options: ClassificationOptions = {}): Promise<string> {
241
- const result = await this.shouldSaveMemory(content, options);
242
- return result.category;
243
- }
244
-
245
- /**
246
- * Analyze patterns in text
247
- * @param text Text to analyze
248
- * @param patterns Optional custom patterns
249
- * @returns Pattern analysis result
250
- */
251
- async analyzePatterns(text: string, patterns: RegExp[] = this.factPatterns): Promise<PatternAnalysisResult> {
252
- const matches: Array<{
253
- pattern: string;
254
- match: string;
255
- category: string;
256
- confidence: number;
257
- }> = [];
258
- const foundPatterns: string[] = [];
259
- const foundCategories: string[] = [];
260
-
261
- for (const pattern of patterns) {
262
- const match = text.match(pattern);
263
- if (match) {
264
- const patternStr = pattern.toString();
265
- const category = this.getCategoryForPattern(patternStr);
266
- const confidence = this.calculatePatternConfidence(pattern, match[0]);
267
-
268
- matches.push({
269
- pattern: patternStr,
270
- match: match[0],
271
- category,
272
- confidence
273
- });
274
-
275
- if (!foundPatterns.includes(patternStr)) {
276
- foundPatterns.push(patternStr);
277
- }
278
-
279
- if (!foundCategories.includes(category)) {
280
- foundCategories.push(category);
281
- }
282
- }
283
- }
284
-
285
- return {
286
- patterns: foundPatterns,
287
- categories: foundCategories,
288
- matches
289
- };
290
- }
291
-
292
- /**
293
- * Match patterns against text
294
- * @param text Text to match
295
- * @param patterns Patterns to use
296
- * @returns Match result
297
- */
298
- private matchPatterns(text: string, patterns: RegExp[]): {
299
- matched: boolean;
300
- pattern?: string;
301
- category: string;
302
- confidence: number;
303
- } {
304
- // Handle null/undefined text input
305
- if (!text || typeof text !== 'string') {
306
- return {
307
- matched: false,
308
- category: 'general',
309
- confidence: 0.1
310
- };
311
- }
312
-
313
- for (const pattern of patterns) {
314
- try {
315
- const match = text.match(pattern);
316
- if (match) {
317
- const patternStr = pattern.toString();
318
- const category = this.getCategoryForPattern(patternStr);
319
- const confidence = this.calculatePatternConfidence(pattern, match[0]);
320
-
321
- return {
322
- matched: true,
323
- pattern: patternStr,
324
- category,
325
- confidence
326
- };
327
- }
328
- } catch (error) {
329
- // Skip invalid patterns
330
- if (process.env.NODE_ENV === 'development') {
331
- console.warn(`Pattern matching error for pattern ${pattern}:`, error);
332
- }
333
- }
334
- }
335
-
336
- return {
337
- matched: false,
338
- category: 'general',
339
- confidence: 0.1
340
- };
341
- }
342
-
343
- /**
344
- * Get category for a regex pattern
345
- * @param patternString String representation of the regex
346
- * @returns Category string
347
- */
348
- private getCategoryForPattern(patternString: string): string {
349
- // Extract the key part of the pattern for lookup
350
- const patternCore = patternString.toLowerCase()
351
- .replace(/^\/|\/[a-z]*$/g, '') // Remove regex delimiters
352
- .replace(/\([^)]*\)/g, '') // Remove capture groups
353
- .replace(/\[[^\]]*\]/g, '') // Remove character classes
354
- .replace(/[+*?{}|\\^$]/g, '') // Remove regex metacharacters
355
- .trim();
356
-
357
- // Find the best matching category
358
- for (const [key, category] of Object.entries(this.categoryMap)) {
359
- if (patternCore.includes(key.toLowerCase()) || key.toLowerCase().includes(patternCore)) {
360
- return category;
361
- }
362
- }
363
-
364
- return 'custom';
365
- }
366
-
367
- /**
368
- * Calculate confidence score for pattern match
369
- * @param pattern The regex pattern
370
- * @param match The matched text
371
- * @returns Confidence score (0-1)
372
- */
373
- private calculatePatternConfidence(pattern: RegExp, match: string): number {
374
- // Base confidence for any pattern match
375
- let confidence = 0.85;
376
-
377
- // Highest confidence for explicit memory commands
378
- if (pattern.source.includes('remember') ||
379
- pattern.source.includes('forget') ||
380
- pattern.source.includes('store') ||
381
- pattern.source.includes('save') ||
382
- pattern.source.includes('note that') ||
383
- pattern.source.includes('keep in mind')) {
384
- confidence = 0.98;
385
- }
386
- // High confidence for specific personal info
387
- else if (pattern.source.includes('my name is')) {
388
- confidence = 0.95;
389
- }
390
- // Higher confidence for specific patterns (email, phone, etc.)
391
- else if (pattern.source.includes('@') || pattern.source.includes('[0-9]')) {
392
- confidence = 0.92;
393
- }
394
-
395
- // Adjust based on match length (longer matches tend to be more specific)
396
- if (match.length > 30) {
397
- confidence = Math.min(confidence + 0.02, 1.0);
398
- }
399
-
400
- return confidence;
401
- }
402
-
403
- /**
404
- * Use AI for classification (fallback/enhancement)
405
- * @param message Message to classify
406
- * @param options Classification options
407
- * @returns Classification result
408
- */
409
- private async classifyWithAI(message: string, options: ClassificationOptions): Promise<ClassificationResult> {
410
- // For now, this is a placeholder that could integrate with:
411
- // 1. EmbeddingService for semantic similarity
412
- // 2. External AI APIs (OpenAI, Gemini, etc.)
413
- // 3. Local AI models
414
-
415
- if (this.embeddingService) {
416
- // Use embedding-based classification
417
- return await this.classifyWithEmbeddings(message, options);
418
- }
419
-
420
- // If no AI service is available, return low-confidence result
421
- return {
422
- shouldSave: false,
423
- confidence: 0.3,
424
- category: 'general',
425
- reasoning: 'AI classification not available'
426
- };
427
- }
428
-
429
- /**
430
- * Classify using embeddings (semantic similarity approach)
431
- * @param message Message to classify
432
- * @param options Classification options
433
- * @returns Classification result
434
- */
435
- private async classifyWithEmbeddings(message: string, options: ClassificationOptions): Promise<ClassificationResult> {
436
- if (!this.embeddingService) {
437
- throw new Error('EmbeddingService not available');
438
- }
439
-
440
- try {
441
- // Generate embedding for the message
442
- const result = await this.embeddingService.embedText({ text: message, type: 'content' });
443
-
444
- // For now, use simple heuristics based on common memory-worthy patterns
445
- // In a full implementation, this would compare against a database of
446
- // known memory-worthy content embeddings
447
-
448
- const messageWords = message.toLowerCase().split(/\s+/);
449
- const memoryKeywords = ['i', 'my', 'me', 'like', 'love', 'hate', 'prefer', 'enjoy', 'am', 'work', 'live'];
450
- const memoryWordCount = messageWords.filter(word => memoryKeywords.includes(word)).length;
451
-
452
- const confidence = Math.min(memoryWordCount * 0.15 + 0.1, 0.85);
453
- const shouldSave = confidence > 0.6;
454
-
455
- return {
456
- shouldSave,
457
- confidence,
458
- category: shouldSave ? 'personal_info' : 'general',
459
- reasoning: `Embedding-based classification (${memoryWordCount} memory keywords found)`
460
- };
461
- } catch (error) {
462
- const errorMessage = error instanceof Error ? error.message : String(error);
463
- throw new Error(`Embedding classification failed: ${errorMessage}`);
464
- }
465
- }
1
+ /**
2
+ * ClassifierService - Content Classification and Filtering
3
+ *
4
+ * Determines if content should be saved as memory using pattern matching
5
+ * and AI classification. Provides category classification for content organization.
6
+ */
7
+
8
+ import { EmbeddingService } from './EmbeddingService';
9
+ import type { ClientWithCoreApi, PDWConfig } from '../types';
10
+
11
+ export interface ClassificationResult {
12
+ shouldSave: boolean;
13
+ confidence: number;
14
+ category: string;
15
+ reasoning: string;
16
+ }
17
+
18
+ export interface ClassificationOptions {
19
+ useAI?: boolean;
20
+ confidenceThreshold?: number;
21
+ categories?: string[];
22
+ patterns?: RegExp[];
23
+ /**
24
+ * If true, only explicit memory commands trigger save (e.g., "remember that", "store in memory")
25
+ * Default: true - to give users control over what gets saved
26
+ */
27
+ explicitOnly?: boolean;
28
+ }
29
+
30
+ export interface PatternAnalysisResult {
31
+ patterns: string[];
32
+ categories: string[];
33
+ matches: Array<{
34
+ pattern: string;
35
+ match: string;
36
+ category: string;
37
+ confidence: number;
38
+ }>;
39
+ }
40
+
41
+ /**
42
+ * Content classification service for determining memory worthiness
43
+ */
44
+ export class ClassifierService {
45
+ private embeddingService?: EmbeddingService;
46
+ private aiApiKey?: string;
47
+
48
+ // Explicit memory command patterns - only these trigger by default
49
+ private readonly explicitPatterns: RegExp[] = [
50
+ // Explicit memory requests
51
+ /remember that ([^.!?]+)/i,
52
+ /remember:?\s*([^.!?]+)/i,
53
+ /don't forget that ([^.!?]+)/i,
54
+ /don't forget:?\s*([^.!?]+)/i,
55
+ /please remember ([^.!?]+)/i,
56
+ /store (?:this )?(?:in|to) memory:?\s*([^.!?]*)/i,
57
+ /save (?:this )?(?:in|to) memory:?\s*([^.!?]*)/i,
58
+ /add (?:this )?to (?:my )?memory:?\s*([^.!?]*)/i,
59
+ /keep in mind:?\s*([^.!?]+)/i,
60
+ /note that ([^.!?]+)/i,
61
+ ];
62
+
63
+ // Full regex patterns for detecting factual statements (used when explicitOnly=false)
64
+ private readonly factPatterns: RegExp[] = [
65
+ // Include explicit patterns first
66
+ ...this.explicitPatterns,
67
+
68
+ // Personal information
69
+ /my name is ([a-zA-Z\s]+)/i,
70
+ /my email is ([a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,})/i,
71
+ /i live in ([a-zA-Z\s,]+)/i,
72
+ /i work at ([a-zA-Z\s,&]+)/i,
73
+ /i am from ([a-zA-Z\s,]+)/i,
74
+ /i was born in ([a-zA-Z0-9\s,]+)/i,
75
+ /my birthday is ([a-zA-Z0-9\s,]+)/i,
76
+ /my phone (?:number|#) is ([0-9+\-\s()]+)/i,
77
+ /my address is ([a-zA-Z0-9\s,]+)/i,
78
+
79
+ // Preferences and likes/dislikes
80
+ /i love ([^.!?]+)/i,
81
+ /i like ([^.!?]+)/i,
82
+ /i enjoy ([^.!?]+)/i,
83
+ /i prefer ([^.!?]+)/i,
84
+ /i hate ([^.!?]+)/i,
85
+ /i dislike ([^.!?]+)/i,
86
+ /i don't like ([^.!?]+)/i,
87
+ /my favorite ([^.!?]+) is ([^.!?]+)/i,
88
+ /my favourite ([^.!?]+) is ([^.!?]+)/i,
89
+
90
+ // Personal facts
91
+ /i am ([^.!?]+)/i,
92
+ /i have ([^.!?]+)/i,
93
+ /i own ([^.!?]+)/i,
94
+ /i studied ([^.!?]+)/i,
95
+ /i graduated from ([^.!?]+)/i,
96
+ ];
97
+
98
+ // Map of regex patterns to categories (adapted from backend)
99
+ private readonly categoryMap: Record<string, string> = {
100
+ // Personal information
101
+ 'my name is': 'personal_info',
102
+ 'my email is': 'contact',
103
+ 'i live in': 'location',
104
+ 'i work at': 'career',
105
+ 'i am from': 'background',
106
+ 'i was born': 'background',
107
+ 'my birthday': 'personal_info',
108
+ 'my phone': 'contact',
109
+ 'my address': 'contact',
110
+
111
+ // Preferences
112
+ 'i love': 'preference',
113
+ 'i like': 'preference',
114
+ 'i enjoy': 'preference',
115
+ 'i prefer': 'preference',
116
+ 'i hate': 'preference',
117
+ 'i dislike': 'preference',
118
+ "i don't like": 'preference',
119
+ 'my favorite': 'preference',
120
+ 'my favourite': 'preference',
121
+
122
+ // Explicit memory requests
123
+ 'remember that': 'custom',
124
+ 'remember': 'custom',
125
+ "don't forget": 'custom',
126
+ 'please remember': 'custom',
127
+ 'store': 'custom',
128
+ 'save': 'custom',
129
+ 'add': 'custom',
130
+ 'keep in mind': 'custom',
131
+ 'note that': 'custom',
132
+
133
+ // Personal facts
134
+ 'i am': 'personal_info',
135
+ 'i have': 'personal_info',
136
+ 'i own': 'personal_info',
137
+ 'i studied': 'education',
138
+ 'i graduated': 'education',
139
+ };
140
+
141
+ constructor(
142
+ private client?: ClientWithCoreApi,
143
+ private config?: PDWConfig,
144
+ embeddingService?: EmbeddingService,
145
+ aiApiKey?: string
146
+ ) {
147
+ this.embeddingService = embeddingService;
148
+ this.aiApiKey = aiApiKey;
149
+ }
150
+
151
+ /**
152
+ * Determine if a message contains information worth saving
153
+ * @param message User message to classify
154
+ * @param options Classification options
155
+ * @returns Classification result
156
+ */
157
+ async shouldSaveMemory(message: string, options: ClassificationOptions = {}): Promise<ClassificationResult> {
158
+ try {
159
+ // Input validation
160
+ if (!message || typeof message !== 'string' || message.trim().length === 0) {
161
+ return {
162
+ shouldSave: false,
163
+ confidence: 0,
164
+ category: 'general',
165
+ reasoning: 'Empty or invalid message'
166
+ };
167
+ }
168
+
169
+ const {
170
+ useAI = true,
171
+ confidenceThreshold = 0.7,
172
+ explicitOnly = true, // Default to explicit-only mode for user control
173
+ patterns = explicitOnly ? this.explicitPatterns : this.factPatterns
174
+ } = options;
175
+
176
+ // Step 1: Check for obvious patterns using regex
177
+ const patternResult = this.matchPatterns(message, patterns);
178
+ if (patternResult.matched && patternResult.confidence >= confidenceThreshold) {
179
+ return {
180
+ shouldSave: true,
181
+ confidence: patternResult.confidence,
182
+ category: patternResult.category,
183
+ reasoning: `Matched pattern: ${patternResult.pattern}`
184
+ };
185
+ }
186
+
187
+ // Step 2: Use AI for more complex classification (if enabled and available)
188
+ if (useAI && (this.embeddingService || this.aiApiKey)) {
189
+ try {
190
+ const aiResult = await this.classifyWithAI(message, options);
191
+ if (aiResult.confidence >= confidenceThreshold) {
192
+ return aiResult;
193
+ }
194
+ } catch (aiError) {
195
+ if (process.env.NODE_ENV === 'development') {
196
+ const errorMessage = aiError instanceof Error ? aiError.message : String(aiError);
197
+ console.warn('AI classification failed, falling back to pattern-only result:', errorMessage);
198
+ }
199
+ }
200
+ }
201
+
202
+ // Step 3: Return pattern result even if below threshold
203
+ if (patternResult.matched) {
204
+ return {
205
+ shouldSave: patternResult.confidence >= confidenceThreshold,
206
+ confidence: patternResult.confidence,
207
+ category: patternResult.category,
208
+ reasoning: `Pattern match (confidence: ${patternResult.confidence})`
209
+ };
210
+ }
211
+
212
+ // Step 4: Default to not saving
213
+ return {
214
+ shouldSave: false,
215
+ confidence: 0.1,
216
+ category: 'general',
217
+ reasoning: 'No significant patterns detected and AI classification unavailable or inconclusive'
218
+ };
219
+
220
+ } catch (error) {
221
+ if (process.env.NODE_ENV === 'development') {
222
+ console.error('Error in classification:', error);
223
+ }
224
+ const errorMessage = error instanceof Error ? error.message : String(error);
225
+ return {
226
+ shouldSave: false,
227
+ confidence: 0,
228
+ category: 'error',
229
+ reasoning: `Classification error: ${errorMessage}`
230
+ };
231
+ }
232
+ }
233
+
234
+ /**
235
+ * Classify content into categories
236
+ * @param content Content to classify
237
+ * @param options Classification options
238
+ * @returns Category string
239
+ */
240
+ async classifyContent(content: string, options: ClassificationOptions = {}): Promise<string> {
241
+ const result = await this.shouldSaveMemory(content, options);
242
+ return result.category;
243
+ }
244
+
245
+ /**
246
+ * Analyze patterns in text
247
+ * @param text Text to analyze
248
+ * @param patterns Optional custom patterns
249
+ * @returns Pattern analysis result
250
+ */
251
+ async analyzePatterns(text: string, patterns: RegExp[] = this.factPatterns): Promise<PatternAnalysisResult> {
252
+ const matches: Array<{
253
+ pattern: string;
254
+ match: string;
255
+ category: string;
256
+ confidence: number;
257
+ }> = [];
258
+ const foundPatterns: string[] = [];
259
+ const foundCategories: string[] = [];
260
+
261
+ for (const pattern of patterns) {
262
+ const match = text.match(pattern);
263
+ if (match) {
264
+ const patternStr = pattern.toString();
265
+ const category = this.getCategoryForPattern(patternStr);
266
+ const confidence = this.calculatePatternConfidence(pattern, match[0]);
267
+
268
+ matches.push({
269
+ pattern: patternStr,
270
+ match: match[0],
271
+ category,
272
+ confidence
273
+ });
274
+
275
+ if (!foundPatterns.includes(patternStr)) {
276
+ foundPatterns.push(patternStr);
277
+ }
278
+
279
+ if (!foundCategories.includes(category)) {
280
+ foundCategories.push(category);
281
+ }
282
+ }
283
+ }
284
+
285
+ return {
286
+ patterns: foundPatterns,
287
+ categories: foundCategories,
288
+ matches
289
+ };
290
+ }
291
+
292
+ /**
293
+ * Match patterns against text
294
+ * @param text Text to match
295
+ * @param patterns Patterns to use
296
+ * @returns Match result
297
+ */
298
+ private matchPatterns(text: string, patterns: RegExp[]): {
299
+ matched: boolean;
300
+ pattern?: string;
301
+ category: string;
302
+ confidence: number;
303
+ } {
304
+ // Handle null/undefined text input
305
+ if (!text || typeof text !== 'string') {
306
+ return {
307
+ matched: false,
308
+ category: 'general',
309
+ confidence: 0.1
310
+ };
311
+ }
312
+
313
+ for (const pattern of patterns) {
314
+ try {
315
+ const match = text.match(pattern);
316
+ if (match) {
317
+ const patternStr = pattern.toString();
318
+ const category = this.getCategoryForPattern(patternStr);
319
+ const confidence = this.calculatePatternConfidence(pattern, match[0]);
320
+
321
+ return {
322
+ matched: true,
323
+ pattern: patternStr,
324
+ category,
325
+ confidence
326
+ };
327
+ }
328
+ } catch (error) {
329
+ // Skip invalid patterns
330
+ if (process.env.NODE_ENV === 'development') {
331
+ console.warn(`Pattern matching error for pattern ${pattern}:`, error);
332
+ }
333
+ }
334
+ }
335
+
336
+ return {
337
+ matched: false,
338
+ category: 'general',
339
+ confidence: 0.1
340
+ };
341
+ }
342
+
343
+ /**
344
+ * Get category for a regex pattern
345
+ * @param patternString String representation of the regex
346
+ * @returns Category string
347
+ */
348
+ private getCategoryForPattern(patternString: string): string {
349
+ // Extract the key part of the pattern for lookup
350
+ const patternCore = patternString.toLowerCase()
351
+ .replace(/^\/|\/[a-z]*$/g, '') // Remove regex delimiters
352
+ .replace(/\([^)]*\)/g, '') // Remove capture groups
353
+ .replace(/\[[^\]]*\]/g, '') // Remove character classes
354
+ .replace(/[+*?{}|\\^$]/g, '') // Remove regex metacharacters
355
+ .trim();
356
+
357
+ // Find the best matching category
358
+ for (const [key, category] of Object.entries(this.categoryMap)) {
359
+ if (patternCore.includes(key.toLowerCase()) || key.toLowerCase().includes(patternCore)) {
360
+ return category;
361
+ }
362
+ }
363
+
364
+ return 'custom';
365
+ }
366
+
367
+ /**
368
+ * Calculate confidence score for pattern match
369
+ * @param pattern The regex pattern
370
+ * @param match The matched text
371
+ * @returns Confidence score (0-1)
372
+ */
373
+ private calculatePatternConfidence(pattern: RegExp, match: string): number {
374
+ // Base confidence for any pattern match
375
+ let confidence = 0.85;
376
+
377
+ // Highest confidence for explicit memory commands
378
+ if (pattern.source.includes('remember') ||
379
+ pattern.source.includes('forget') ||
380
+ pattern.source.includes('store') ||
381
+ pattern.source.includes('save') ||
382
+ pattern.source.includes('note that') ||
383
+ pattern.source.includes('keep in mind')) {
384
+ confidence = 0.98;
385
+ }
386
+ // High confidence for specific personal info
387
+ else if (pattern.source.includes('my name is')) {
388
+ confidence = 0.95;
389
+ }
390
+ // Higher confidence for specific patterns (email, phone, etc.)
391
+ else if (pattern.source.includes('@') || pattern.source.includes('[0-9]')) {
392
+ confidence = 0.92;
393
+ }
394
+
395
+ // Adjust based on match length (longer matches tend to be more specific)
396
+ if (match.length > 30) {
397
+ confidence = Math.min(confidence + 0.02, 1.0);
398
+ }
399
+
400
+ return confidence;
401
+ }
402
+
403
+ /**
404
+ * Use AI for classification (fallback/enhancement)
405
+ * @param message Message to classify
406
+ * @param options Classification options
407
+ * @returns Classification result
408
+ */
409
+ private async classifyWithAI(message: string, options: ClassificationOptions): Promise<ClassificationResult> {
410
+ // For now, this is a placeholder that could integrate with:
411
+ // 1. EmbeddingService for semantic similarity
412
+ // 2. External AI APIs (OpenAI, Gemini, etc.)
413
+ // 3. Local AI models
414
+
415
+ if (this.embeddingService) {
416
+ // Use embedding-based classification
417
+ return await this.classifyWithEmbeddings(message, options);
418
+ }
419
+
420
+ // If no AI service is available, return low-confidence result
421
+ return {
422
+ shouldSave: false,
423
+ confidence: 0.3,
424
+ category: 'general',
425
+ reasoning: 'AI classification not available'
426
+ };
427
+ }
428
+
429
+ /**
430
+ * Classify using embeddings (semantic similarity approach)
431
+ * @param message Message to classify
432
+ * @param options Classification options
433
+ * @returns Classification result
434
+ */
435
+ private async classifyWithEmbeddings(message: string, options: ClassificationOptions): Promise<ClassificationResult> {
436
+ if (!this.embeddingService) {
437
+ throw new Error('EmbeddingService not available');
438
+ }
439
+
440
+ try {
441
+ // Generate embedding for the message
442
+ const result = await this.embeddingService.embedText({ text: message, type: 'content' });
443
+
444
+ // For now, use simple heuristics based on common memory-worthy patterns
445
+ // In a full implementation, this would compare against a database of
446
+ // known memory-worthy content embeddings
447
+
448
+ const messageWords = message.toLowerCase().split(/\s+/);
449
+ const memoryKeywords = ['i', 'my', 'me', 'like', 'love', 'hate', 'prefer', 'enjoy', 'am', 'work', 'live'];
450
+ const memoryWordCount = messageWords.filter(word => memoryKeywords.includes(word)).length;
451
+
452
+ const confidence = Math.min(memoryWordCount * 0.15 + 0.1, 0.85);
453
+ const shouldSave = confidence > 0.6;
454
+
455
+ return {
456
+ shouldSave,
457
+ confidence,
458
+ category: shouldSave ? 'personal_info' : 'general',
459
+ reasoning: `Embedding-based classification (${memoryWordCount} memory keywords found)`
460
+ };
461
+ } catch (error) {
462
+ const errorMessage = error instanceof Error ? error.message : String(error);
463
+ throw new Error(`Embedding classification failed: ${errorMessage}`);
464
+ }
465
+ }
466
466
  }