@sparkleideas/neural 3.5.2-patch.1

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 (122) hide show
  1. package/README.md +260 -0
  2. package/__tests__/README.md +235 -0
  3. package/__tests__/algorithms.test.ts +582 -0
  4. package/__tests__/patterns.test.ts +549 -0
  5. package/__tests__/sona.test.ts +445 -0
  6. package/docs/SONA_INTEGRATION.md +460 -0
  7. package/docs/SONA_QUICKSTART.md +168 -0
  8. package/examples/sona-usage.ts +318 -0
  9. package/package.json +23 -0
  10. package/src/algorithms/a2c.d.ts +86 -0
  11. package/src/algorithms/a2c.d.ts.map +1 -0
  12. package/src/algorithms/a2c.js +361 -0
  13. package/src/algorithms/a2c.js.map +1 -0
  14. package/src/algorithms/a2c.ts +478 -0
  15. package/src/algorithms/curiosity.d.ts +82 -0
  16. package/src/algorithms/curiosity.d.ts.map +1 -0
  17. package/src/algorithms/curiosity.js +392 -0
  18. package/src/algorithms/curiosity.js.map +1 -0
  19. package/src/algorithms/curiosity.ts +509 -0
  20. package/src/algorithms/decision-transformer.d.ts +82 -0
  21. package/src/algorithms/decision-transformer.d.ts.map +1 -0
  22. package/src/algorithms/decision-transformer.js +415 -0
  23. package/src/algorithms/decision-transformer.js.map +1 -0
  24. package/src/algorithms/decision-transformer.ts +521 -0
  25. package/src/algorithms/dqn.d.ts +72 -0
  26. package/src/algorithms/dqn.d.ts.map +1 -0
  27. package/src/algorithms/dqn.js +303 -0
  28. package/src/algorithms/dqn.js.map +1 -0
  29. package/src/algorithms/dqn.ts +382 -0
  30. package/src/algorithms/index.d.ts +32 -0
  31. package/src/algorithms/index.d.ts.map +1 -0
  32. package/src/algorithms/index.js +74 -0
  33. package/src/algorithms/index.js.map +1 -0
  34. package/src/algorithms/index.ts +122 -0
  35. package/src/algorithms/ppo.d.ts +72 -0
  36. package/src/algorithms/ppo.d.ts.map +1 -0
  37. package/src/algorithms/ppo.js +331 -0
  38. package/src/algorithms/ppo.js.map +1 -0
  39. package/src/algorithms/ppo.ts +429 -0
  40. package/src/algorithms/q-learning.d.ts +77 -0
  41. package/src/algorithms/q-learning.d.ts.map +1 -0
  42. package/src/algorithms/q-learning.js +259 -0
  43. package/src/algorithms/q-learning.js.map +1 -0
  44. package/src/algorithms/q-learning.ts +333 -0
  45. package/src/algorithms/sarsa.d.ts +82 -0
  46. package/src/algorithms/sarsa.d.ts.map +1 -0
  47. package/src/algorithms/sarsa.js +297 -0
  48. package/src/algorithms/sarsa.js.map +1 -0
  49. package/src/algorithms/sarsa.ts +383 -0
  50. package/src/algorithms/tmp.json +0 -0
  51. package/src/application/index.ts +11 -0
  52. package/src/application/services/neural-application-service.ts +217 -0
  53. package/src/domain/entities/pattern.ts +169 -0
  54. package/src/domain/index.ts +18 -0
  55. package/src/domain/services/learning-service.ts +256 -0
  56. package/src/index.d.ts +118 -0
  57. package/src/index.d.ts.map +1 -0
  58. package/src/index.js +201 -0
  59. package/src/index.js.map +1 -0
  60. package/src/index.ts +363 -0
  61. package/src/modes/balanced.d.ts +60 -0
  62. package/src/modes/balanced.d.ts.map +1 -0
  63. package/src/modes/balanced.js +234 -0
  64. package/src/modes/balanced.js.map +1 -0
  65. package/src/modes/balanced.ts +299 -0
  66. package/src/modes/base.ts +163 -0
  67. package/src/modes/batch.d.ts +82 -0
  68. package/src/modes/batch.d.ts.map +1 -0
  69. package/src/modes/batch.js +316 -0
  70. package/src/modes/batch.js.map +1 -0
  71. package/src/modes/batch.ts +434 -0
  72. package/src/modes/edge.d.ts +85 -0
  73. package/src/modes/edge.d.ts.map +1 -0
  74. package/src/modes/edge.js +310 -0
  75. package/src/modes/edge.js.map +1 -0
  76. package/src/modes/edge.ts +409 -0
  77. package/src/modes/index.d.ts +55 -0
  78. package/src/modes/index.d.ts.map +1 -0
  79. package/src/modes/index.js +83 -0
  80. package/src/modes/index.js.map +1 -0
  81. package/src/modes/index.ts +16 -0
  82. package/src/modes/real-time.d.ts +58 -0
  83. package/src/modes/real-time.d.ts.map +1 -0
  84. package/src/modes/real-time.js +196 -0
  85. package/src/modes/real-time.js.map +1 -0
  86. package/src/modes/real-time.ts +257 -0
  87. package/src/modes/research.d.ts +79 -0
  88. package/src/modes/research.d.ts.map +1 -0
  89. package/src/modes/research.js +389 -0
  90. package/src/modes/research.js.map +1 -0
  91. package/src/modes/research.ts +486 -0
  92. package/src/modes/tmp.json +0 -0
  93. package/src/pattern-learner.d.ts +117 -0
  94. package/src/pattern-learner.d.ts.map +1 -0
  95. package/src/pattern-learner.js +603 -0
  96. package/src/pattern-learner.js.map +1 -0
  97. package/src/pattern-learner.ts +757 -0
  98. package/src/reasoning-bank.d.ts +259 -0
  99. package/src/reasoning-bank.d.ts.map +1 -0
  100. package/src/reasoning-bank.js +993 -0
  101. package/src/reasoning-bank.js.map +1 -0
  102. package/src/reasoning-bank.ts +1279 -0
  103. package/src/reasoningbank-adapter.ts +697 -0
  104. package/src/sona-integration.d.ts +168 -0
  105. package/src/sona-integration.d.ts.map +1 -0
  106. package/src/sona-integration.js +316 -0
  107. package/src/sona-integration.js.map +1 -0
  108. package/src/sona-integration.ts +432 -0
  109. package/src/sona-manager.d.ts +147 -0
  110. package/src/sona-manager.d.ts.map +1 -0
  111. package/src/sona-manager.js +695 -0
  112. package/src/sona-manager.js.map +1 -0
  113. package/src/sona-manager.ts +835 -0
  114. package/src/tmp.json +0 -0
  115. package/src/types.d.ts +431 -0
  116. package/src/types.d.ts.map +1 -0
  117. package/src/types.js +11 -0
  118. package/src/types.js.map +1 -0
  119. package/src/types.ts +590 -0
  120. package/tmp.json +0 -0
  121. package/tsconfig.json +9 -0
  122. package/vitest.config.ts +19 -0
@@ -0,0 +1,1279 @@
1
+ /**
2
+ * ReasoningBank Integration with AgentDB
3
+ *
4
+ * Implements the 4-step learning pipeline with real vector storage:
5
+ * 1. RETRIEVE - Top-k memory injection with MMR diversity (using AgentDB HNSW)
6
+ * 2. JUDGE - LLM-as-judge trajectory evaluation
7
+ * 3. DISTILL - Extract strategy memories from trajectories
8
+ * 4. CONSOLIDATE - Dedup, detect contradictions, prune old patterns
9
+ *
10
+ * Performance Targets:
11
+ * - Retrieval: <10ms with AgentDB HNSW (150x faster than brute-force)
12
+ * - Learning step: <10ms
13
+ * - Consolidation: <100ms
14
+ *
15
+ * @module reasoning-bank
16
+ */
17
+
18
+ import type {
19
+ Trajectory,
20
+ TrajectoryStep,
21
+ TrajectoryVerdict,
22
+ DistilledMemory,
23
+ Pattern,
24
+ PatternEvolution,
25
+ NeuralEvent,
26
+ NeuralEventListener,
27
+ } from './types.js';
28
+
29
+ // ============================================================================
30
+ // AgentDB Integration
31
+ // ============================================================================
32
+
33
+ let AgentDB: any;
34
+ let agentdbImportPromise: Promise<void> | undefined;
35
+
36
+ async function ensureAgentDBImport(): Promise<void> {
37
+ if (!agentdbImportPromise) {
38
+ agentdbImportPromise = (async () => {
39
+ try {
40
+ const agentdbModule = await import('@sparkleideas/agentdb');
41
+ AgentDB = agentdbModule.AgentDB || agentdbModule.default;
42
+ } catch {
43
+ // AgentDB not available - will use fallback
44
+ AgentDB = undefined;
45
+ }
46
+ })();
47
+ }
48
+ return agentdbImportPromise;
49
+ }
50
+
51
+ // ============================================================================
52
+ // Configuration
53
+ // ============================================================================
54
+
55
+ /**
56
+ * Configuration for ReasoningBank
57
+ */
58
+ export interface ReasoningBankConfig {
59
+ /** Maximum number of trajectories to store */
60
+ maxTrajectories: number;
61
+
62
+ /** Minimum quality threshold for distillation */
63
+ distillationThreshold: number;
64
+
65
+ /** Number of similar memories to retrieve */
66
+ retrievalK: number;
67
+
68
+ /** Diversity factor for MMR (0-1) */
69
+ mmrLambda: number;
70
+
71
+ /** Maximum age of patterns in days */
72
+ maxPatternAgeDays: number;
73
+
74
+ /** Similarity threshold for deduplication */
75
+ dedupThreshold: number;
76
+
77
+ /** Enable contradiction detection */
78
+ enableContradictionDetection: boolean;
79
+
80
+ /** Database path for persistent storage */
81
+ dbPath?: string;
82
+
83
+ /** Vector dimension for embeddings */
84
+ vectorDimension: number;
85
+
86
+ /** Namespace for AgentDB storage */
87
+ namespace: string;
88
+
89
+ /** Enable AgentDB vector storage */
90
+ enableAgentDB: boolean;
91
+ }
92
+
93
+ /**
94
+ * Default ReasoningBank configuration
95
+ */
96
+ const DEFAULT_CONFIG: ReasoningBankConfig = {
97
+ maxTrajectories: 5000,
98
+ distillationThreshold: 0.6,
99
+ retrievalK: 3,
100
+ mmrLambda: 0.7,
101
+ maxPatternAgeDays: 30,
102
+ dedupThreshold: 0.95,
103
+ enableContradictionDetection: true,
104
+ dbPath: undefined,
105
+ vectorDimension: 768,
106
+ namespace: 'reasoning-bank',
107
+ enableAgentDB: true,
108
+ };
109
+
110
+ // ============================================================================
111
+ // Memory Types
112
+ // ============================================================================
113
+
114
+ /**
115
+ * Memory entry with metadata
116
+ */
117
+ interface MemoryEntry {
118
+ memory: DistilledMemory;
119
+ trajectory: Trajectory;
120
+ verdict: TrajectoryVerdict;
121
+ consolidated: boolean;
122
+ }
123
+
124
+ /**
125
+ * Retrieval result with diversity scoring
126
+ */
127
+ export interface RetrievalResult {
128
+ memory: DistilledMemory;
129
+ relevanceScore: number;
130
+ diversityScore: number;
131
+ combinedScore: number;
132
+ }
133
+
134
+ /**
135
+ * Consolidation result
136
+ */
137
+ export interface ConsolidationResult {
138
+ removedDuplicates: number;
139
+ contradictionsDetected: number;
140
+ prunedPatterns: number;
141
+ mergedPatterns: number;
142
+ }
143
+
144
+ /**
145
+ * Step analysis result
146
+ */
147
+ interface StepAnalysis {
148
+ totalSteps: number;
149
+ avgReward: number;
150
+ positiveRatio: number;
151
+ trajectory: number;
152
+ }
153
+
154
+ // ============================================================================
155
+ // ReasoningBank Class
156
+ // ============================================================================
157
+
158
+ /**
159
+ * ReasoningBank - Trajectory storage and learning pipeline with AgentDB
160
+ *
161
+ * This class implements a complete learning pipeline for AI agents:
162
+ * - Store and retrieve trajectories using vector similarity
163
+ * - Judge trajectory quality using rule-based evaluation
164
+ * - Distill successful trajectories into reusable patterns
165
+ * - Consolidate patterns to remove duplicates and contradictions
166
+ */
167
+ export class ReasoningBank {
168
+ private config: ReasoningBankConfig;
169
+ private trajectories: Map<string, Trajectory> = new Map();
170
+ private memories: Map<string, MemoryEntry> = new Map();
171
+ private patterns: Map<string, Pattern> = new Map();
172
+ private eventListeners: Set<NeuralEventListener> = new Set();
173
+
174
+ // AgentDB instance for vector storage
175
+ private @sparkleideas/agentdb: any = null;
176
+ private agentdbAvailable: boolean = false;
177
+ private initialized: boolean = false;
178
+
179
+ // Performance tracking
180
+ private retrievalCount = 0;
181
+ private totalRetrievalTime = 0;
182
+ private distillationCount = 0;
183
+ private totalDistillationTime = 0;
184
+ private judgeCount = 0;
185
+ private totalJudgeTime = 0;
186
+ private consolidationCount = 0;
187
+ private totalConsolidationTime = 0;
188
+
189
+ constructor(config: Partial<ReasoningBankConfig> = {}) {
190
+ this.config = { ...DEFAULT_CONFIG, ...config };
191
+ }
192
+
193
+ // ==========================================================================
194
+ // Initialization
195
+ // ==========================================================================
196
+
197
+ /**
198
+ * Initialize ReasoningBank with AgentDB
199
+ */
200
+ async initialize(): Promise<void> {
201
+ if (this.initialized) return;
202
+
203
+ if (this.config.enableAgentDB) {
204
+ await ensureAgentDBImport();
205
+ this.agentdbAvailable = AgentDB !== undefined;
206
+
207
+ if (this.agentdbAvailable) {
208
+ try {
209
+ this.@sparkleideas/agentdb = new AgentDB({
210
+ dbPath: this.config.dbPath || ':memory:',
211
+ namespace: this.config.namespace,
212
+ vectorDimension: this.config.vectorDimension,
213
+ vectorBackend: 'auto',
214
+ });
215
+
216
+ await this.@sparkleideas/agentdb.initialize();
217
+ this.emitEvent({ type: 'memory_consolidated', memoriesCount: 0 });
218
+ } catch (error) {
219
+ console.warn('AgentDB initialization failed, using fallback:', error);
220
+ this.agentdbAvailable = false;
221
+ }
222
+ }
223
+ }
224
+
225
+ this.initialized = true;
226
+ }
227
+
228
+ /**
229
+ * Shutdown and cleanup resources
230
+ */
231
+ async shutdown(): Promise<void> {
232
+ if (this.@sparkleideas/agentdb) {
233
+ await this.@sparkleideas/agentdb.close?.();
234
+ }
235
+ this.initialized = false;
236
+ }
237
+
238
+ // ==========================================================================
239
+ // STEP 1: RETRIEVE - Top-k memory injection with MMR diversity
240
+ // ==========================================================================
241
+
242
+ /**
243
+ * Retrieve relevant memories using Maximal Marginal Relevance (MMR)
244
+ *
245
+ * Uses AgentDB HNSW index for 150x faster retrieval when available.
246
+ *
247
+ * @param queryEmbedding - Query vector for similarity search
248
+ * @param k - Number of results to return (default: config.retrievalK)
249
+ * @returns Retrieval results with relevance and diversity scores
250
+ */
251
+ async retrieve(queryEmbedding: Float32Array, k?: number): Promise<RetrievalResult[]> {
252
+ const startTime = performance.now();
253
+ const retrieveK = k ?? this.config.retrievalK;
254
+
255
+ if (this.memories.size === 0) {
256
+ return [];
257
+ }
258
+
259
+ let candidates: Array<{ entry: MemoryEntry; relevance: number }> = [];
260
+
261
+ // Try AgentDB HNSW search first
262
+ if (this.@sparkleideas/agentdb && this.agentdbAvailable) {
263
+ try {
264
+ const results = await this.searchWithAgentDB(queryEmbedding, retrieveK * 3);
265
+ candidates = results
266
+ .map(r => {
267
+ const entry = this.memories.get(r.id);
268
+ return entry ? { entry, relevance: r.similarity } : null;
269
+ })
270
+ .filter((c): c is { entry: MemoryEntry; relevance: number } => c !== null);
271
+ } catch {
272
+ // Fall through to brute-force
273
+ }
274
+ }
275
+
276
+ // Fallback: brute-force search
277
+ if (candidates.length === 0) {
278
+ for (const entry of this.memories.values()) {
279
+ const relevance = this.cosineSimilarity(queryEmbedding, entry.memory.embedding);
280
+ candidates.push({ entry, relevance });
281
+ }
282
+ candidates.sort((a, b) => b.relevance - a.relevance);
283
+ }
284
+
285
+ // Apply MMR for diversity
286
+ const results: RetrievalResult[] = [];
287
+ const selected: MemoryEntry[] = [];
288
+
289
+ while (results.length < retrieveK && candidates.length > 0) {
290
+ let bestIdx = 0;
291
+ let bestScore = -Infinity;
292
+
293
+ for (let i = 0; i < candidates.length; i++) {
294
+ const candidate = candidates[i];
295
+
296
+ // Compute MMR score: lambda * relevance - (1 - lambda) * max_similarity_to_selected
297
+ const relevance = candidate.relevance;
298
+ let maxSimilarity = 0;
299
+
300
+ for (const sel of selected) {
301
+ const sim = this.cosineSimilarity(
302
+ candidate.entry.memory.embedding,
303
+ sel.memory.embedding
304
+ );
305
+ maxSimilarity = Math.max(maxSimilarity, sim);
306
+ }
307
+
308
+ const diversityScore = 1 - maxSimilarity;
309
+ const mmrScore = this.config.mmrLambda * relevance +
310
+ (1 - this.config.mmrLambda) * diversityScore;
311
+
312
+ if (mmrScore > bestScore) {
313
+ bestScore = mmrScore;
314
+ bestIdx = i;
315
+ }
316
+ }
317
+
318
+ // Add best candidate
319
+ const best = candidates[bestIdx];
320
+ selected.push(best.entry);
321
+ results.push({
322
+ memory: best.entry.memory,
323
+ relevanceScore: best.relevance,
324
+ diversityScore: 1 - this.computeMaxSimilarity(best.entry, selected.slice(0, -1)),
325
+ combinedScore: bestScore,
326
+ });
327
+
328
+ // Remove from candidates
329
+ candidates.splice(bestIdx, 1);
330
+ }
331
+
332
+ // Update stats
333
+ this.retrievalCount++;
334
+ this.totalRetrievalTime += performance.now() - startTime;
335
+
336
+ return results;
337
+ }
338
+
339
+ /**
340
+ * Search for similar memories by content string
341
+ *
342
+ * @param content - Text content to search for
343
+ * @param k - Number of results
344
+ * @returns Retrieval results
345
+ */
346
+ async retrieveByContent(content: string, k?: number): Promise<RetrievalResult[]> {
347
+ // Simple content-based retrieval using memory strategies
348
+ const retrieveK = k ?? this.config.retrievalK;
349
+ const results: RetrievalResult[] = [];
350
+
351
+ const contentLower = content.toLowerCase();
352
+ const entries = Array.from(this.memories.values());
353
+
354
+ // Score by content similarity
355
+ const scored = entries.map(entry => ({
356
+ entry,
357
+ score: this.computeContentSimilarity(contentLower, entry.memory.strategy),
358
+ }));
359
+
360
+ scored.sort((a, b) => b.score - a.score);
361
+
362
+ for (let i = 0; i < Math.min(retrieveK, scored.length); i++) {
363
+ const { entry, score } = scored[i];
364
+ if (score > 0) {
365
+ results.push({
366
+ memory: entry.memory,
367
+ relevanceScore: score,
368
+ diversityScore: 1,
369
+ combinedScore: score,
370
+ });
371
+ }
372
+ }
373
+
374
+ return results;
375
+ }
376
+
377
+ // ==========================================================================
378
+ // STEP 2: JUDGE - LLM-as-judge trajectory evaluation
379
+ // ==========================================================================
380
+
381
+ /**
382
+ * Judge a trajectory and produce a verdict
383
+ *
384
+ * Uses rule-based evaluation to assess trajectory quality.
385
+ * In production, this could be enhanced with LLM-as-judge.
386
+ *
387
+ * @param trajectory - Completed trajectory to judge
388
+ * @returns Verdict with success status and analysis
389
+ */
390
+ async judge(trajectory: Trajectory): Promise<TrajectoryVerdict> {
391
+ const startTime = performance.now();
392
+
393
+ if (!trajectory.isComplete) {
394
+ throw new Error('Cannot judge incomplete trajectory');
395
+ }
396
+
397
+ // Analyze trajectory steps
398
+ const stepAnalysis = this.analyzeSteps(trajectory.steps);
399
+
400
+ // Compute success based on quality and step analysis
401
+ const success = trajectory.qualityScore >= this.config.distillationThreshold &&
402
+ stepAnalysis.positiveRatio > 0.6;
403
+
404
+ // Identify strengths and weaknesses
405
+ const strengths = this.identifyStrengths(trajectory, stepAnalysis);
406
+ const weaknesses = this.identifyWeaknesses(trajectory, stepAnalysis);
407
+
408
+ // Generate improvement suggestions
409
+ const improvements = this.generateImprovements(weaknesses);
410
+
411
+ // Compute relevance for similar future tasks
412
+ const relevanceScore = this.computeRelevanceScore(trajectory);
413
+
414
+ const verdict: TrajectoryVerdict = {
415
+ success,
416
+ confidence: this.computeConfidence(trajectory, stepAnalysis),
417
+ strengths,
418
+ weaknesses,
419
+ improvements,
420
+ relevanceScore,
421
+ };
422
+
423
+ // Store verdict with trajectory
424
+ trajectory.verdict = verdict;
425
+
426
+ // Update stats
427
+ this.judgeCount++;
428
+ this.totalJudgeTime += performance.now() - startTime;
429
+
430
+ return verdict;
431
+ }
432
+
433
+ // ==========================================================================
434
+ // STEP 3: DISTILL - Extract strategy memories from trajectories
435
+ // ==========================================================================
436
+
437
+ /**
438
+ * Distill a trajectory into a reusable memory
439
+ *
440
+ * @param trajectory - Trajectory to distill
441
+ * @returns Distilled memory or null if quality too low
442
+ */
443
+ async distill(trajectory: Trajectory): Promise<DistilledMemory | null> {
444
+ const startTime = performance.now();
445
+
446
+ // Must be judged first
447
+ if (!trajectory.verdict) {
448
+ await this.judge(trajectory);
449
+ }
450
+
451
+ // Only distill successful trajectories
452
+ if (!trajectory.verdict!.success ||
453
+ trajectory.qualityScore < this.config.distillationThreshold) {
454
+ return null;
455
+ }
456
+
457
+ // Extract strategy from trajectory
458
+ const strategy = this.extractStrategy(trajectory);
459
+
460
+ // Extract key learnings
461
+ const keyLearnings = this.extractKeyLearnings(trajectory);
462
+
463
+ // Compute aggregated embedding
464
+ const embedding = this.computeAggregateEmbedding(trajectory);
465
+
466
+ const memory: DistilledMemory = {
467
+ memoryId: `mem_${Date.now()}_${Math.random().toString(36).slice(2, 9)}`,
468
+ trajectoryId: trajectory.trajectoryId,
469
+ strategy,
470
+ keyLearnings,
471
+ embedding,
472
+ quality: trajectory.qualityScore,
473
+ usageCount: 0,
474
+ lastUsed: Date.now(),
475
+ };
476
+
477
+ // Store the memory
478
+ const entry: MemoryEntry = {
479
+ memory,
480
+ trajectory,
481
+ verdict: trajectory.verdict!,
482
+ consolidated: false,
483
+ };
484
+ this.memories.set(memory.memoryId, entry);
485
+
486
+ // Store in AgentDB for vector search
487
+ if (this.@sparkleideas/agentdb && this.agentdbAvailable) {
488
+ await this.storeInAgentDB(memory);
489
+ }
490
+
491
+ // Also store trajectory reference
492
+ trajectory.distilledMemory = memory;
493
+
494
+ // Update stats
495
+ this.distillationCount++;
496
+ this.totalDistillationTime += performance.now() - startTime;
497
+
498
+ this.emitEvent({
499
+ type: 'trajectory_completed',
500
+ trajectoryId: trajectory.trajectoryId,
501
+ qualityScore: trajectory.qualityScore,
502
+ });
503
+
504
+ return memory;
505
+ }
506
+
507
+ /**
508
+ * Batch distill multiple trajectories
509
+ *
510
+ * @param trajectories - Array of trajectories to distill
511
+ * @returns Array of distilled memories (excludes nulls)
512
+ */
513
+ async distillBatch(trajectories: Trajectory[]): Promise<DistilledMemory[]> {
514
+ const memories: DistilledMemory[] = [];
515
+
516
+ for (const trajectory of trajectories) {
517
+ const memory = await this.distill(trajectory);
518
+ if (memory) {
519
+ memories.push(memory);
520
+ }
521
+ }
522
+
523
+ return memories;
524
+ }
525
+
526
+ // ==========================================================================
527
+ // STEP 4: CONSOLIDATE - Dedup, detect contradictions, prune old patterns
528
+ // ==========================================================================
529
+
530
+ /**
531
+ * Consolidate memories: deduplicate, detect contradictions, prune old
532
+ *
533
+ * @returns Consolidation statistics
534
+ */
535
+ async consolidate(): Promise<ConsolidationResult> {
536
+ const startTime = performance.now();
537
+
538
+ const result: ConsolidationResult = {
539
+ removedDuplicates: 0,
540
+ contradictionsDetected: 0,
541
+ prunedPatterns: 0,
542
+ mergedPatterns: 0,
543
+ };
544
+
545
+ // 1. Deduplicate similar memories
546
+ result.removedDuplicates = await this.deduplicateMemories();
547
+
548
+ // 2. Detect contradictions
549
+ if (this.config.enableContradictionDetection) {
550
+ result.contradictionsDetected = await this.detectContradictions();
551
+ }
552
+
553
+ // 3. Prune old patterns
554
+ result.prunedPatterns = await this.pruneOldPatterns();
555
+
556
+ // 4. Merge similar patterns
557
+ result.mergedPatterns = await this.mergePatterns();
558
+
559
+ // Update stats
560
+ this.consolidationCount++;
561
+ this.totalConsolidationTime += performance.now() - startTime;
562
+
563
+ // Emit consolidation event
564
+ this.emitEvent({
565
+ type: 'memory_consolidated',
566
+ memoriesCount: this.memories.size,
567
+ });
568
+
569
+ return result;
570
+ }
571
+
572
+ // ==========================================================================
573
+ // Pattern Management
574
+ // ==========================================================================
575
+
576
+ /**
577
+ * Convert a distilled memory to a pattern
578
+ */
579
+ memoryToPattern(memory: DistilledMemory): Pattern {
580
+ const pattern: Pattern = {
581
+ patternId: `pat_${memory.memoryId}`,
582
+ name: this.generatePatternName(memory),
583
+ domain: this.inferDomain(memory),
584
+ embedding: memory.embedding,
585
+ strategy: memory.strategy,
586
+ successRate: memory.quality,
587
+ usageCount: memory.usageCount,
588
+ qualityHistory: [memory.quality],
589
+ evolutionHistory: [],
590
+ createdAt: Date.now(),
591
+ updatedAt: Date.now(),
592
+ };
593
+
594
+ this.patterns.set(pattern.patternId, pattern);
595
+ return pattern;
596
+ }
597
+
598
+ /**
599
+ * Evolve a pattern based on new experience
600
+ */
601
+ evolvePattern(patternId: string, newExperience: Trajectory): void {
602
+ const pattern = this.patterns.get(patternId);
603
+ if (!pattern) return;
604
+
605
+ const previousQuality = pattern.successRate;
606
+
607
+ // Update quality history
608
+ pattern.qualityHistory.push(newExperience.qualityScore);
609
+ if (pattern.qualityHistory.length > 100) {
610
+ pattern.qualityHistory = pattern.qualityHistory.slice(-100);
611
+ }
612
+
613
+ // Update success rate
614
+ pattern.successRate = pattern.qualityHistory.reduce((a, b) => a + b, 0) /
615
+ pattern.qualityHistory.length;
616
+
617
+ pattern.usageCount++;
618
+ pattern.updatedAt = Date.now();
619
+
620
+ // Record evolution
621
+ const evolutionType = this.determineEvolutionType(previousQuality, pattern.successRate);
622
+ pattern.evolutionHistory.push({
623
+ timestamp: Date.now(),
624
+ type: evolutionType,
625
+ previousQuality,
626
+ newQuality: pattern.successRate,
627
+ description: `Updated based on trajectory ${newExperience.trajectoryId}`,
628
+ });
629
+
630
+ // Emit event
631
+ this.emitEvent({
632
+ type: 'pattern_evolved',
633
+ patternId,
634
+ evolutionType,
635
+ });
636
+ }
637
+
638
+ /**
639
+ * Get all patterns
640
+ */
641
+ getPatterns(): Pattern[] {
642
+ return Array.from(this.patterns.values());
643
+ }
644
+
645
+ /**
646
+ * Find patterns matching a query
647
+ */
648
+ async findPatterns(queryEmbedding: Float32Array, k: number = 5): Promise<Pattern[]> {
649
+ const results: Array<{ pattern: Pattern; score: number }> = [];
650
+
651
+ for (const pattern of this.patterns.values()) {
652
+ const score = this.cosineSimilarity(queryEmbedding, pattern.embedding);
653
+ results.push({ pattern, score });
654
+ }
655
+
656
+ results.sort((a, b) => b.score - a.score);
657
+ return results.slice(0, k).map(r => r.pattern);
658
+ }
659
+
660
+ // ==========================================================================
661
+ // Trajectory Management
662
+ // ==========================================================================
663
+
664
+ /**
665
+ * Store a trajectory
666
+ */
667
+ storeTrajectory(trajectory: Trajectory): void {
668
+ this.trajectories.set(trajectory.trajectoryId, trajectory);
669
+
670
+ // Prune if over capacity
671
+ if (this.trajectories.size > this.config.maxTrajectories) {
672
+ this.pruneTrajectories();
673
+ }
674
+ }
675
+
676
+ /**
677
+ * Get trajectory by ID
678
+ */
679
+ getTrajectory(trajectoryId: string): Trajectory | undefined {
680
+ return this.trajectories.get(trajectoryId);
681
+ }
682
+
683
+ /**
684
+ * Get all trajectories
685
+ */
686
+ getTrajectories(): Trajectory[] {
687
+ return Array.from(this.trajectories.values());
688
+ }
689
+
690
+ /**
691
+ * Get successful trajectories
692
+ */
693
+ getSuccessfulTrajectories(): Trajectory[] {
694
+ return Array.from(this.trajectories.values())
695
+ .filter(t => t.verdict?.success);
696
+ }
697
+
698
+ /**
699
+ * Get failed trajectories
700
+ */
701
+ getFailedTrajectories(): Trajectory[] {
702
+ return Array.from(this.trajectories.values())
703
+ .filter(t => t.isComplete && !t.verdict?.success);
704
+ }
705
+
706
+ // ==========================================================================
707
+ // Statistics
708
+ // ==========================================================================
709
+
710
+ /**
711
+ * Get ReasoningBank statistics
712
+ */
713
+ getStats(): Record<string, number> {
714
+ return {
715
+ trajectoryCount: this.trajectories.size,
716
+ memoryCount: this.memories.size,
717
+ patternCount: this.patterns.size,
718
+ avgRetrievalTimeMs: this.retrievalCount > 0
719
+ ? this.totalRetrievalTime / this.retrievalCount
720
+ : 0,
721
+ avgDistillationTimeMs: this.distillationCount > 0
722
+ ? this.totalDistillationTime / this.distillationCount
723
+ : 0,
724
+ avgJudgeTimeMs: this.judgeCount > 0
725
+ ? this.totalJudgeTime / this.judgeCount
726
+ : 0,
727
+ avgConsolidationTimeMs: this.consolidationCount > 0
728
+ ? this.totalConsolidationTime / this.consolidationCount
729
+ : 0,
730
+ consolidatedMemories: Array.from(this.memories.values())
731
+ .filter(e => e.consolidated).length,
732
+ successfulTrajectories: this.getSuccessfulTrajectories().length,
733
+ failedTrajectories: this.getFailedTrajectories().length,
734
+ agentdbEnabled: this.agentdbAvailable ? 1 : 0,
735
+ retrievalCount: this.retrievalCount,
736
+ distillationCount: this.distillationCount,
737
+ judgeCount: this.judgeCount,
738
+ consolidationCount: this.consolidationCount,
739
+ };
740
+ }
741
+
742
+ /**
743
+ * Get detailed metrics for hooks
744
+ */
745
+ getDetailedMetrics(): {
746
+ routing: { totalRoutes: number; avgConfidence: number; topAgents: Array<{ agent: string; count: number; successRate: number }> };
747
+ edits: { totalEdits: number; successRate: number; commonPatterns: string[] };
748
+ commands: { totalCommands: number; successRate: number; avgExecutionTime: number; commonCommands: string[] };
749
+ } {
750
+ const successfulTrajectories = this.getSuccessfulTrajectories();
751
+ const allTrajectories = this.getTrajectories();
752
+ const successRate = allTrajectories.length > 0
753
+ ? successfulTrajectories.length / allTrajectories.length
754
+ : 0;
755
+
756
+ // Extract domain-based routing stats
757
+ const domainStats = new Map<string, { count: number; successes: number }>();
758
+ for (const t of allTrajectories) {
759
+ const domain = t.domain || 'general';
760
+ const stats = domainStats.get(domain) || { count: 0, successes: 0 };
761
+ stats.count++;
762
+ if (t.verdict?.success) stats.successes++;
763
+ domainStats.set(domain, stats);
764
+ }
765
+
766
+ const topAgents = Array.from(domainStats.entries())
767
+ .map(([agent, stats]) => ({
768
+ agent,
769
+ count: stats.count,
770
+ successRate: stats.count > 0 ? stats.successes / stats.count : 0,
771
+ }))
772
+ .sort((a, b) => b.count - a.count)
773
+ .slice(0, 5);
774
+
775
+ // Extract common patterns
776
+ const patternStrategies = Array.from(this.patterns.values())
777
+ .sort((a, b) => b.usageCount - a.usageCount)
778
+ .slice(0, 5)
779
+ .map(p => p.strategy);
780
+
781
+ return {
782
+ routing: {
783
+ totalRoutes: allTrajectories.length,
784
+ avgConfidence: successfulTrajectories.length > 0
785
+ ? successfulTrajectories.reduce((sum, t) => sum + (t.verdict?.confidence || 0), 0) / successfulTrajectories.length
786
+ : 0,
787
+ topAgents,
788
+ },
789
+ edits: {
790
+ totalEdits: this.memories.size,
791
+ successRate,
792
+ commonPatterns: patternStrategies.slice(0, 4),
793
+ },
794
+ commands: {
795
+ totalCommands: this.distillationCount,
796
+ successRate,
797
+ avgExecutionTime: this.totalDistillationTime / Math.max(this.distillationCount, 1),
798
+ commonCommands: patternStrategies.slice(0, 4),
799
+ },
800
+ };
801
+ }
802
+
803
+ // ==========================================================================
804
+ // Event System
805
+ // ==========================================================================
806
+
807
+ addEventListener(listener: NeuralEventListener): void {
808
+ this.eventListeners.add(listener);
809
+ }
810
+
811
+ removeEventListener(listener: NeuralEventListener): void {
812
+ this.eventListeners.delete(listener);
813
+ }
814
+
815
+ private emitEvent(event: NeuralEvent): void {
816
+ for (const listener of this.eventListeners) {
817
+ try {
818
+ listener(event);
819
+ } catch (error) {
820
+ console.error('Error in ReasoningBank event listener:', error);
821
+ }
822
+ }
823
+ }
824
+
825
+ // ==========================================================================
826
+ // AgentDB Integration Helpers
827
+ // ==========================================================================
828
+
829
+ /**
830
+ * Store memory in AgentDB for vector search
831
+ */
832
+ private async storeInAgentDB(memory: DistilledMemory): Promise<void> {
833
+ if (!this.@sparkleideas/agentdb) return;
834
+
835
+ try {
836
+ if (typeof this.@sparkleideas/agentdb.store === 'function') {
837
+ await this.@sparkleideas/agentdb.store(memory.memoryId, {
838
+ content: memory.strategy,
839
+ embedding: memory.embedding,
840
+ metadata: {
841
+ trajectoryId: memory.trajectoryId,
842
+ quality: memory.quality,
843
+ keyLearnings: memory.keyLearnings,
844
+ usageCount: memory.usageCount,
845
+ lastUsed: memory.lastUsed,
846
+ },
847
+ });
848
+ }
849
+ } catch (error) {
850
+ console.warn('Failed to store in AgentDB:', error);
851
+ }
852
+ }
853
+
854
+ /**
855
+ * Search using AgentDB HNSW index
856
+ */
857
+ private async searchWithAgentDB(
858
+ queryEmbedding: Float32Array,
859
+ k: number
860
+ ): Promise<Array<{ id: string; similarity: number }>> {
861
+ if (!this.@sparkleideas/agentdb) return [];
862
+
863
+ try {
864
+ if (typeof this.@sparkleideas/agentdb.search === 'function') {
865
+ return await this.@sparkleideas/agentdb.search(queryEmbedding, k);
866
+ }
867
+
868
+ // Try HNSW controller if available
869
+ const hnsw = this.@sparkleideas/agentdb.getController?.('hnsw');
870
+ if (hnsw) {
871
+ const results = await hnsw.search(queryEmbedding, k);
872
+ return results.map((r: any) => ({
873
+ id: String(r.id),
874
+ similarity: r.similarity || 1 - r.distance,
875
+ }));
876
+ }
877
+ } catch {
878
+ // Fall through to return empty
879
+ }
880
+
881
+ return [];
882
+ }
883
+
884
+ /**
885
+ * Delete from AgentDB
886
+ */
887
+ private async deleteFromAgentDB(memoryId: string): Promise<void> {
888
+ if (!this.@sparkleideas/agentdb) return;
889
+
890
+ try {
891
+ if (typeof this.@sparkleideas/agentdb.delete === 'function') {
892
+ await this.@sparkleideas/agentdb.delete(memoryId);
893
+ }
894
+ } catch {
895
+ // Ignore deletion errors
896
+ }
897
+ }
898
+
899
+ // ==========================================================================
900
+ // Private Helper Methods
901
+ // ==========================================================================
902
+
903
+ private cosineSimilarity(a: Float32Array, b: Float32Array): number {
904
+ if (a.length !== b.length) return 0;
905
+
906
+ let dot = 0, normA = 0, normB = 0;
907
+ for (let i = 0; i < a.length; i++) {
908
+ dot += a[i] * b[i];
909
+ normA += a[i] * a[i];
910
+ normB += b[i] * b[i];
911
+ }
912
+
913
+ const denom = Math.sqrt(normA) * Math.sqrt(normB);
914
+ // Clamp to [0, 1] to handle floating point precision issues
915
+ const similarity = denom > 0 ? dot / denom : 0;
916
+ return Math.max(0, Math.min(1, similarity));
917
+ }
918
+
919
+ private computeContentSimilarity(query: string, content: string): number {
920
+ const queryWords = new Set(query.toLowerCase().split(/\s+/));
921
+ const contentWords = content.toLowerCase().split(/\s+/);
922
+
923
+ let matches = 0;
924
+ for (const word of contentWords) {
925
+ if (queryWords.has(word)) matches++;
926
+ }
927
+
928
+ return contentWords.length > 0 ? matches / contentWords.length : 0;
929
+ }
930
+
931
+ private computeMaxSimilarity(entry: MemoryEntry, selected: MemoryEntry[]): number {
932
+ let maxSim = 0;
933
+ for (const sel of selected) {
934
+ const sim = this.cosineSimilarity(entry.memory.embedding, sel.memory.embedding);
935
+ maxSim = Math.max(maxSim, sim);
936
+ }
937
+ return maxSim;
938
+ }
939
+
940
+ private analyzeSteps(steps: TrajectoryStep[]): StepAnalysis {
941
+ const rewardSum = steps.reduce((s, step) => s + step.reward, 0);
942
+ const positiveSteps = steps.filter(s => s.reward > 0.5).length;
943
+
944
+ return {
945
+ totalSteps: steps.length,
946
+ avgReward: steps.length > 0 ? rewardSum / steps.length : 0,
947
+ positiveRatio: steps.length > 0 ? positiveSteps / steps.length : 0,
948
+ trajectory: steps.length > 1
949
+ ? (steps[steps.length - 1].reward - steps[0].reward)
950
+ : 0,
951
+ };
952
+ }
953
+
954
+ private identifyStrengths(trajectory: Trajectory, analysis: StepAnalysis): string[] {
955
+ const strengths: string[] = [];
956
+
957
+ if (analysis.avgReward > 0.7) {
958
+ strengths.push('High average reward across steps');
959
+ }
960
+ if (analysis.trajectory > 0.2) {
961
+ strengths.push('Positive reward trajectory');
962
+ }
963
+ if (trajectory.qualityScore > 0.8) {
964
+ strengths.push('High overall quality');
965
+ }
966
+ if (analysis.totalSteps < 5 && trajectory.qualityScore > 0.6) {
967
+ strengths.push('Efficient solution (few steps)');
968
+ }
969
+
970
+ return strengths;
971
+ }
972
+
973
+ private identifyWeaknesses(trajectory: Trajectory, analysis: StepAnalysis): string[] {
974
+ const weaknesses: string[] = [];
975
+
976
+ if (analysis.avgReward < 0.4) {
977
+ weaknesses.push('Low average reward');
978
+ }
979
+ if (analysis.trajectory < -0.1) {
980
+ weaknesses.push('Declining reward trajectory');
981
+ }
982
+ if (analysis.positiveRatio < 0.5) {
983
+ weaknesses.push('Many negative/neutral steps');
984
+ }
985
+ if (analysis.totalSteps > 10 && trajectory.qualityScore < 0.7) {
986
+ weaknesses.push('Long trajectory with mediocre outcome');
987
+ }
988
+
989
+ return weaknesses;
990
+ }
991
+
992
+ private generateImprovements(weaknesses: string[]): string[] {
993
+ const improvements: string[] = [];
994
+
995
+ for (const weakness of weaknesses) {
996
+ if (weakness.includes('Low average reward')) {
997
+ improvements.push('Consider alternative strategies for each step');
998
+ }
999
+ if (weakness.includes('Declining')) {
1000
+ improvements.push('Re-evaluate approach when reward decreases');
1001
+ }
1002
+ if (weakness.includes('negative/neutral')) {
1003
+ improvements.push('Focus on steps with clearer positive signals');
1004
+ }
1005
+ if (weakness.includes('Long trajectory')) {
1006
+ improvements.push('Look for shortcuts or more direct approaches');
1007
+ }
1008
+ }
1009
+
1010
+ return improvements;
1011
+ }
1012
+
1013
+ private computeRelevanceScore(trajectory: Trajectory): number {
1014
+ // Base relevance on quality and recency
1015
+ const qualityFactor = trajectory.qualityScore;
1016
+ const ageDays = (Date.now() - trajectory.startTime) / (1000 * 60 * 60 * 24);
1017
+ const recencyFactor = Math.exp(-ageDays / 30); // Decay over 30 days
1018
+
1019
+ return qualityFactor * 0.7 + recencyFactor * 0.3;
1020
+ }
1021
+
1022
+ private computeConfidence(trajectory: Trajectory, analysis: StepAnalysis): number {
1023
+ // More steps = more confidence
1024
+ const stepFactor = Math.min(analysis.totalSteps / 10, 1);
1025
+ // Consistent rewards = more confidence
1026
+ const consistencyFactor = analysis.positiveRatio;
1027
+ // Clear outcome = more confidence
1028
+ const outcomeFactor = Math.abs(trajectory.qualityScore - 0.5) * 2;
1029
+
1030
+ return (stepFactor * 0.3 + consistencyFactor * 0.4 + outcomeFactor * 0.3);
1031
+ }
1032
+
1033
+ private extractStrategy(trajectory: Trajectory): string {
1034
+ const actions = trajectory.steps.map(s => s.action);
1035
+ const uniqueActions = [...new Set(actions)];
1036
+
1037
+ if (uniqueActions.length <= 3) {
1038
+ return `Apply ${uniqueActions.join(' -> ')}`;
1039
+ }
1040
+
1041
+ return `Multi-step approach: ${uniqueActions.slice(0, 3).join(', ')}...`;
1042
+ }
1043
+
1044
+ private extractKeyLearnings(trajectory: Trajectory): string[] {
1045
+ const learnings: string[] = [];
1046
+ const verdict = trajectory.verdict!;
1047
+
1048
+ // Add key success factors
1049
+ if (verdict.success) {
1050
+ learnings.push(`Successful approach for ${trajectory.domain} domain`);
1051
+ for (const strength of verdict.strengths.slice(0, 2)) {
1052
+ learnings.push(`Strength: ${strength}`);
1053
+ }
1054
+ } else {
1055
+ learnings.push(`Approach needs refinement`);
1056
+ for (const improvement of verdict.improvements.slice(0, 2)) {
1057
+ learnings.push(`Improvement: ${improvement}`);
1058
+ }
1059
+ }
1060
+
1061
+ return learnings;
1062
+ }
1063
+
1064
+ private computeAggregateEmbedding(trajectory: Trajectory): Float32Array {
1065
+ if (trajectory.steps.length === 0) {
1066
+ return new Float32Array(this.config.vectorDimension);
1067
+ }
1068
+
1069
+ const dim = trajectory.steps[0].stateAfter.length;
1070
+ const aggregate = new Float32Array(dim);
1071
+
1072
+ // Weighted average of step embeddings (higher weight for later steps)
1073
+ let totalWeight = 0;
1074
+ for (let i = 0; i < trajectory.steps.length; i++) {
1075
+ const weight = (i + 1) / trajectory.steps.length;
1076
+ totalWeight += weight;
1077
+ const step = trajectory.steps[i];
1078
+ for (let j = 0; j < dim; j++) {
1079
+ aggregate[j] += step.stateAfter[j] * weight;
1080
+ }
1081
+ }
1082
+
1083
+ // Normalize
1084
+ for (let j = 0; j < dim; j++) {
1085
+ aggregate[j] /= totalWeight;
1086
+ }
1087
+
1088
+ return aggregate;
1089
+ }
1090
+
1091
+ private async deduplicateMemories(): Promise<number> {
1092
+ let removed = 0;
1093
+ const entries = Array.from(this.memories.entries());
1094
+
1095
+ for (let i = 0; i < entries.length; i++) {
1096
+ for (let j = i + 1; j < entries.length; j++) {
1097
+ const sim = this.cosineSimilarity(
1098
+ entries[i][1].memory.embedding,
1099
+ entries[j][1].memory.embedding
1100
+ );
1101
+
1102
+ if (sim > this.config.dedupThreshold) {
1103
+ // Keep the higher quality one
1104
+ if (entries[i][1].memory.quality >= entries[j][1].memory.quality) {
1105
+ this.memories.delete(entries[j][0]);
1106
+ await this.deleteFromAgentDB(entries[j][0]);
1107
+ } else {
1108
+ this.memories.delete(entries[i][0]);
1109
+ await this.deleteFromAgentDB(entries[i][0]);
1110
+ }
1111
+ removed++;
1112
+ }
1113
+ }
1114
+ }
1115
+
1116
+ return removed;
1117
+ }
1118
+
1119
+ private async detectContradictions(): Promise<number> {
1120
+ let contradictions = 0;
1121
+ const entries = Array.from(this.memories.values());
1122
+
1123
+ for (let i = 0; i < entries.length; i++) {
1124
+ for (let j = i + 1; j < entries.length; j++) {
1125
+ // Similar context but opposite outcomes
1126
+ const sim = this.cosineSimilarity(
1127
+ entries[i].memory.embedding,
1128
+ entries[j].memory.embedding
1129
+ );
1130
+
1131
+ if (sim > 0.8) {
1132
+ const qualityDiff = Math.abs(
1133
+ entries[i].memory.quality - entries[j].memory.quality
1134
+ );
1135
+
1136
+ if (qualityDiff > 0.4) {
1137
+ contradictions++;
1138
+ // Mark lower quality as consolidated (to exclude from retrieval)
1139
+ if (entries[i].memory.quality < entries[j].memory.quality) {
1140
+ entries[i].consolidated = true;
1141
+ } else {
1142
+ entries[j].consolidated = true;
1143
+ }
1144
+ }
1145
+ }
1146
+ }
1147
+ }
1148
+
1149
+ return contradictions;
1150
+ }
1151
+
1152
+ private async pruneOldPatterns(): Promise<number> {
1153
+ const now = Date.now();
1154
+ const maxAge = this.config.maxPatternAgeDays * 24 * 60 * 60 * 1000;
1155
+ let pruned = 0;
1156
+
1157
+ for (const [id, pattern] of this.patterns) {
1158
+ const age = now - pattern.updatedAt;
1159
+ if (age > maxAge && pattern.usageCount < 5) {
1160
+ this.patterns.delete(id);
1161
+ pruned++;
1162
+ }
1163
+ }
1164
+
1165
+ return pruned;
1166
+ }
1167
+
1168
+ private async mergePatterns(): Promise<number> {
1169
+ let merged = 0;
1170
+ const patterns = Array.from(this.patterns.entries());
1171
+
1172
+ for (let i = 0; i < patterns.length; i++) {
1173
+ for (let j = i + 1; j < patterns.length; j++) {
1174
+ const sim = this.cosineSimilarity(
1175
+ patterns[i][1].embedding,
1176
+ patterns[j][1].embedding
1177
+ );
1178
+
1179
+ if (sim > 0.9 && patterns[i][1].domain === patterns[j][1].domain) {
1180
+ // Merge into higher quality pattern
1181
+ const [keepId, keep] = patterns[i][1].successRate >= patterns[j][1].successRate
1182
+ ? patterns[i]
1183
+ : patterns[j];
1184
+ const [removeId, remove] = patterns[i][1].successRate < patterns[j][1].successRate
1185
+ ? patterns[i]
1186
+ : patterns[j];
1187
+
1188
+ // Combine statistics
1189
+ keep.usageCount += remove.usageCount;
1190
+ keep.qualityHistory.push(...remove.qualityHistory);
1191
+ keep.evolutionHistory.push({
1192
+ timestamp: Date.now(),
1193
+ type: 'merge',
1194
+ previousQuality: keep.successRate,
1195
+ newQuality: (keep.successRate + remove.successRate) / 2,
1196
+ description: `Merged with pattern ${removeId}`,
1197
+ });
1198
+
1199
+ this.patterns.delete(removeId);
1200
+ merged++;
1201
+ }
1202
+ }
1203
+ }
1204
+
1205
+ return merged;
1206
+ }
1207
+
1208
+ private pruneTrajectories(): void {
1209
+ const entries = Array.from(this.trajectories.entries())
1210
+ .sort((a, b) => a[1].qualityScore - b[1].qualityScore);
1211
+
1212
+ const toRemove = entries.length - Math.floor(this.config.maxTrajectories * 0.8);
1213
+ for (let i = 0; i < toRemove && i < entries.length; i++) {
1214
+ this.trajectories.delete(entries[i][0]);
1215
+ }
1216
+ }
1217
+
1218
+ private generatePatternName(memory: DistilledMemory): string {
1219
+ const words = memory.strategy.split(' ').slice(0, 4);
1220
+ return words.join('_').toLowerCase().replace(/[^a-z0-9_]/g, '');
1221
+ }
1222
+
1223
+ private inferDomain(memory: DistilledMemory): string {
1224
+ // First check if we have the trajectory directly in our store
1225
+ const trajectory = this.trajectories.get(memory.trajectoryId);
1226
+ if (trajectory?.domain) {
1227
+ return trajectory.domain;
1228
+ }
1229
+
1230
+ // Check if the memory entry has the trajectory with domain info
1231
+ const memoryEntry = this.memories.get(memory.memoryId);
1232
+ if (memoryEntry?.trajectory?.domain) {
1233
+ return memoryEntry.trajectory.domain;
1234
+ }
1235
+
1236
+ return 'general';
1237
+ }
1238
+
1239
+ private determineEvolutionType(
1240
+ prev: number,
1241
+ curr: number
1242
+ ): 'improvement' | 'merge' | 'split' | 'prune' {
1243
+ const delta = curr - prev;
1244
+ if (delta > 0.05) return 'improvement';
1245
+ if (delta < -0.1) return 'prune';
1246
+ return 'improvement';
1247
+ }
1248
+
1249
+ /**
1250
+ * Check if AgentDB is available and initialized
1251
+ */
1252
+ isAgentDBAvailable(): boolean {
1253
+ return this.agentdbAvailable;
1254
+ }
1255
+ }
1256
+
1257
+ // ============================================================================
1258
+ // Factory Function
1259
+ // ============================================================================
1260
+
1261
+ /**
1262
+ * Factory function for creating ReasoningBank
1263
+ */
1264
+ export function createReasoningBank(
1265
+ config?: Partial<ReasoningBankConfig>
1266
+ ): ReasoningBank {
1267
+ return new ReasoningBank(config);
1268
+ }
1269
+
1270
+ /**
1271
+ * Create and initialize a ReasoningBank instance
1272
+ */
1273
+ export async function createInitializedReasoningBank(
1274
+ config?: Partial<ReasoningBankConfig>
1275
+ ): Promise<ReasoningBank> {
1276
+ const bank = new ReasoningBank(config);
1277
+ await bank.initialize();
1278
+ return bank;
1279
+ }