@sparkleideas/integration 3.0.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.
@@ -0,0 +1,824 @@
1
+ /**
2
+ * SONA (Self-Optimizing Neural Architecture) Adapter
3
+ *
4
+ * Provides integration with @sparkleideas/agentic-flow's SONA learning system,
5
+ * enabling real-time adaptation, pattern recognition, and
6
+ * continuous learning capabilities.
7
+ *
8
+ * Performance Targets:
9
+ * - Real-time mode: ~0.05ms adaptation
10
+ * - Balanced mode: General purpose learning
11
+ * - Research mode: Deep exploration with higher accuracy
12
+ *
13
+ * @module v3/integration/sona-adapter
14
+ * @version 3.0.0-alpha.1
15
+ */
16
+
17
+ import { EventEmitter } from 'events';
18
+ import type {
19
+ SONAConfiguration,
20
+ SONALearningMode,
21
+ SONATrajectory,
22
+ SONATrajectoryStep,
23
+ SONAPattern,
24
+ SONALearningStats,
25
+ DEFAULT_SONA_CONFIG,
26
+ } from './types.js';
27
+
28
+ /**
29
+ * Interface for @sparkleideas/agentic-flow SONA reference (for delegation)
30
+ * This allows the adapter to delegate to @sparkleideas/agentic-flow when available
31
+ */
32
+ interface AgenticFlowSONAReference {
33
+ setMode(mode: string): Promise<void>;
34
+ storePattern(params: {
35
+ pattern: string;
36
+ solution: string;
37
+ category: string;
38
+ confidence: number;
39
+ metadata?: Record<string, unknown>;
40
+ }): Promise<string>;
41
+ findPatterns(query: string, options?: {
42
+ category?: string;
43
+ topK?: number;
44
+ threshold?: number;
45
+ }): Promise<Array<{
46
+ id: string;
47
+ pattern: string;
48
+ solution: string;
49
+ category: string;
50
+ confidence: number;
51
+ usageCount: number;
52
+ createdAt: number;
53
+ lastUsedAt: number;
54
+ metadata: Record<string, unknown>;
55
+ }>>;
56
+ getStats(): Promise<unknown>;
57
+ beginTrajectory?(params: unknown): Promise<string>;
58
+ recordStep?(params: unknown): Promise<void>;
59
+ endTrajectory?(params: unknown): Promise<unknown>;
60
+ }
61
+
62
+ /**
63
+ * Mode-specific configurations for SONA learning
64
+ */
65
+ const MODE_CONFIGS: Record<SONALearningMode, Partial<SONAConfiguration>> = {
66
+ 'real-time': {
67
+ learningRate: 0.01,
68
+ similarityThreshold: 0.8,
69
+ maxPatterns: 5000,
70
+ consolidationInterval: 1800000, // 30 minutes
71
+ },
72
+ 'balanced': {
73
+ learningRate: 0.001,
74
+ similarityThreshold: 0.7,
75
+ maxPatterns: 10000,
76
+ consolidationInterval: 3600000, // 1 hour
77
+ },
78
+ 'research': {
79
+ learningRate: 0.0001,
80
+ similarityThreshold: 0.6,
81
+ maxPatterns: 50000,
82
+ consolidationInterval: 7200000, // 2 hours
83
+ },
84
+ 'edge': {
85
+ learningRate: 0.005,
86
+ similarityThreshold: 0.85,
87
+ maxPatterns: 1000,
88
+ consolidationInterval: 900000, // 15 minutes
89
+ },
90
+ 'batch': {
91
+ learningRate: 0.0005,
92
+ similarityThreshold: 0.65,
93
+ maxPatterns: 100000,
94
+ consolidationInterval: 14400000, // 4 hours
95
+ },
96
+ };
97
+
98
+ /**
99
+ * SONAAdapter - SONA Learning System Integration
100
+ *
101
+ * This adapter provides a clean interface to @sparkleideas/agentic-flow's SONA
102
+ * learning capabilities, including:
103
+ * - Learning mode selection and auto-switching
104
+ * - Trajectory tracking for experience replay
105
+ * - Pattern storage and retrieval
106
+ * - Memory distillation and consolidation
107
+ */
108
+ export class SONAAdapter extends EventEmitter {
109
+ private config: SONAConfiguration;
110
+ private initialized: boolean = false;
111
+ private activeTrajectories: Map<string, SONATrajectory> = new Map();
112
+ private patterns: Map<string, SONAPattern> = new Map();
113
+ private stats: SONALearningStats;
114
+ private consolidationTimer: NodeJS.Timeout | null = null;
115
+ private learningCycleCount: number = 0;
116
+
117
+ /**
118
+ * Reference to @sparkleideas/agentic-flow SONA for delegation (ADR-001)
119
+ * When set, methods delegate to @sparkleideas/agentic-flow instead of local implementation
120
+ */
121
+ private agenticFlowSona: AgenticFlowSONAReference | null = null;
122
+
123
+ /**
124
+ * Indicates if delegation to @sparkleideas/agentic-flow is active
125
+ */
126
+ private delegationEnabled: boolean = false;
127
+
128
+ constructor(config: Partial<SONAConfiguration> = {}) {
129
+ super();
130
+ this.config = this.mergeConfig(config);
131
+ this.stats = this.initializeStats();
132
+ }
133
+
134
+ /**
135
+ * Set reference to @sparkleideas/agentic-flow SONA for delegation
136
+ *
137
+ * This implements ADR-001: Adopt @sparkleideas/agentic-flow as Core Foundation
138
+ * When a reference is provided, pattern storage and retrieval
139
+ * delegate to @sparkleideas/agentic-flow's optimized implementations.
140
+ *
141
+ * @param sonaRef - The @sparkleideas/agentic-flow SONA interface reference
142
+ */
143
+ setAgenticFlowReference(sonaRef: AgenticFlowSONAReference): void {
144
+ this.agenticFlowSona = sonaRef;
145
+ this.delegationEnabled = true;
146
+ this.emit('delegation-enabled', { target: '@sparkleideas/agentic-flow' });
147
+ }
148
+
149
+ /**
150
+ * Check if delegation to @sparkleideas/agentic-flow is enabled
151
+ */
152
+ isDelegationEnabled(): boolean {
153
+ return this.delegationEnabled && this.agenticFlowSona !== null;
154
+ }
155
+
156
+ /**
157
+ * Initialize the SONA adapter
158
+ */
159
+ async initialize(): Promise<void> {
160
+ if (this.initialized) {
161
+ return;
162
+ }
163
+
164
+ this.emit('initializing');
165
+
166
+ try {
167
+ // Apply mode-specific configuration
168
+ this.applyModeConfig(this.config.mode);
169
+
170
+ // Start consolidation timer if enabled
171
+ if (this.config.consolidationInterval > 0) {
172
+ this.startConsolidationTimer();
173
+ }
174
+
175
+ this.initialized = true;
176
+ this.emit('initialized', { mode: this.config.mode });
177
+ } catch (error) {
178
+ this.emit('initialization-failed', { error });
179
+ throw error;
180
+ }
181
+ }
182
+
183
+ /**
184
+ * Reconfigure the adapter
185
+ */
186
+ async reconfigure(config: Partial<SONAConfiguration>): Promise<void> {
187
+ this.config = this.mergeConfig(config);
188
+
189
+ if (config.mode) {
190
+ this.applyModeConfig(config.mode);
191
+ }
192
+
193
+ // Restart consolidation timer with new interval
194
+ if (config.consolidationInterval !== undefined) {
195
+ this.stopConsolidationTimer();
196
+ if (config.consolidationInterval > 0) {
197
+ this.startConsolidationTimer();
198
+ }
199
+ }
200
+
201
+ this.emit('reconfigured', { config: this.config });
202
+ }
203
+
204
+ /**
205
+ * Get current learning mode
206
+ */
207
+ getMode(): SONALearningMode {
208
+ return this.config.mode;
209
+ }
210
+
211
+ /**
212
+ * Set learning mode
213
+ */
214
+ async setMode(mode: SONALearningMode): Promise<void> {
215
+ const previousMode = this.config.mode;
216
+ this.config.mode = mode;
217
+ this.applyModeConfig(mode);
218
+
219
+ this.emit('mode-changed', {
220
+ previousMode,
221
+ newMode: mode,
222
+ config: MODE_CONFIGS[mode]
223
+ });
224
+ }
225
+
226
+ /**
227
+ * Begin a new trajectory for task tracking
228
+ */
229
+ async beginTrajectory(params: {
230
+ taskId: string;
231
+ description?: string;
232
+ category?: string;
233
+ metadata?: Record<string, unknown>;
234
+ }): Promise<string> {
235
+ this.ensureInitialized();
236
+
237
+ const trajectoryId = this.generateId('traj');
238
+ const trajectory: SONATrajectory = {
239
+ id: trajectoryId,
240
+ taskId: params.taskId,
241
+ steps: [],
242
+ startTime: Date.now(),
243
+ totalReward: 0,
244
+ metadata: {
245
+ description: params.description,
246
+ category: params.category,
247
+ ...params.metadata,
248
+ },
249
+ };
250
+
251
+ this.activeTrajectories.set(trajectoryId, trajectory);
252
+ this.stats.activeTrajectories++;
253
+
254
+ this.emit('trajectory-started', { trajectoryId, taskId: params.taskId });
255
+ return trajectoryId;
256
+ }
257
+
258
+ /**
259
+ * Record a step in an active trajectory
260
+ */
261
+ async recordTrajectoryStep(params: {
262
+ trajectoryId: string;
263
+ stepId?: string;
264
+ action: string;
265
+ observation: string;
266
+ reward: number;
267
+ embedding?: number[];
268
+ }): Promise<void> {
269
+ this.ensureInitialized();
270
+
271
+ const trajectory = this.activeTrajectories.get(params.trajectoryId);
272
+ if (!trajectory) {
273
+ throw new Error(`Trajectory ${params.trajectoryId} not found`);
274
+ }
275
+
276
+ const step: SONATrajectoryStep = {
277
+ stepId: params.stepId || this.generateId('step'),
278
+ action: params.action,
279
+ observation: params.observation,
280
+ reward: params.reward,
281
+ timestamp: Date.now(),
282
+ embedding: params.embedding,
283
+ };
284
+
285
+ trajectory.steps.push(step);
286
+ trajectory.totalReward += params.reward;
287
+
288
+ this.emit('trajectory-step-recorded', {
289
+ trajectoryId: params.trajectoryId,
290
+ step
291
+ });
292
+ }
293
+
294
+ /**
295
+ * End a trajectory with final verdict
296
+ */
297
+ async endTrajectory(params: {
298
+ trajectoryId: string;
299
+ success: boolean;
300
+ verdict?: 'positive' | 'negative' | 'neutral';
301
+ reward?: number;
302
+ }): Promise<SONATrajectory> {
303
+ this.ensureInitialized();
304
+
305
+ const trajectory = this.activeTrajectories.get(params.trajectoryId);
306
+ if (!trajectory) {
307
+ throw new Error(`Trajectory ${params.trajectoryId} not found`);
308
+ }
309
+
310
+ // Finalize trajectory
311
+ trajectory.endTime = Date.now();
312
+ trajectory.verdict = params.verdict || (params.success ? 'positive' : 'negative');
313
+
314
+ if (params.reward !== undefined) {
315
+ trajectory.totalReward += params.reward;
316
+ }
317
+
318
+ // Remove from active and update stats
319
+ this.activeTrajectories.delete(params.trajectoryId);
320
+ this.stats.activeTrajectories--;
321
+ this.stats.completedTrajectories++;
322
+
323
+ // Learn from successful trajectories
324
+ if (params.success && trajectory.verdict === 'positive') {
325
+ await this.learnFromTrajectory(trajectory);
326
+ }
327
+
328
+ this.emit('trajectory-completed', {
329
+ trajectoryId: params.trajectoryId,
330
+ trajectory
331
+ });
332
+
333
+ return trajectory;
334
+ }
335
+
336
+ /**
337
+ * Store a learned pattern
338
+ *
339
+ * ADR-001: When @sparkleideas/agentic-flow is available, delegates to its optimized
340
+ * pattern storage which uses AgentDB with HNSW indexing for
341
+ * 150x-12,500x faster similarity search.
342
+ */
343
+ async storePattern(params: {
344
+ pattern: string;
345
+ solution: string;
346
+ category: string;
347
+ confidence: number;
348
+ metadata?: Record<string, unknown>;
349
+ }): Promise<string> {
350
+ this.ensureInitialized();
351
+
352
+ // ADR-001: Delegate to @sparkleideas/agentic-flow when available
353
+ if (this.isDelegationEnabled() && this.agenticFlowSona) {
354
+ try {
355
+ const patternId = await this.agenticFlowSona.storePattern({
356
+ pattern: params.pattern,
357
+ solution: params.solution,
358
+ category: params.category,
359
+ confidence: Math.max(0, Math.min(1, params.confidence)),
360
+ metadata: params.metadata,
361
+ });
362
+
363
+ this.stats.totalPatterns++;
364
+ this.emit('pattern-stored', {
365
+ patternId,
366
+ delegated: true,
367
+ target: '@sparkleideas/agentic-flow',
368
+ });
369
+
370
+ return patternId;
371
+ } catch (error) {
372
+ // Log delegation failure and fall back to local implementation
373
+ this.emit('delegation-failed', {
374
+ method: 'storePattern',
375
+ error: (error as Error).message,
376
+ fallback: 'local',
377
+ });
378
+ // Continue with local implementation below
379
+ }
380
+ }
381
+
382
+ // Local implementation (fallback or when @sparkleideas/agentic-flow not available)
383
+ const patternId = this.generateId('pat');
384
+ const storedPattern: SONAPattern = {
385
+ id: patternId,
386
+ pattern: params.pattern,
387
+ solution: params.solution,
388
+ category: params.category,
389
+ confidence: Math.max(0, Math.min(1, params.confidence)),
390
+ usageCount: 0,
391
+ createdAt: Date.now(),
392
+ lastUsedAt: Date.now(),
393
+ metadata: params.metadata || {},
394
+ };
395
+
396
+ // Check if we need to prune patterns
397
+ if (this.patterns.size >= this.config.maxPatterns) {
398
+ await this.prunePatterns();
399
+ }
400
+
401
+ this.patterns.set(patternId, storedPattern);
402
+ this.stats.totalPatterns++;
403
+ this.updateAverageConfidence();
404
+
405
+ this.emit('pattern-stored', { patternId, pattern: storedPattern });
406
+ return patternId;
407
+ }
408
+
409
+ /**
410
+ * Find similar patterns to a query
411
+ *
412
+ * ADR-001: When @sparkleideas/agentic-flow is available, delegates to its optimized
413
+ * HNSW-indexed search for 150x-12,500x faster retrieval.
414
+ */
415
+ async findSimilarPatterns(params: {
416
+ query: string;
417
+ category?: string;
418
+ topK?: number;
419
+ threshold?: number;
420
+ }): Promise<SONAPattern[]> {
421
+ this.ensureInitialized();
422
+
423
+ const topK = params.topK || 5;
424
+ const threshold = params.threshold ?? this.config.similarityThreshold;
425
+
426
+ // ADR-001: Delegate to @sparkleideas/agentic-flow when available for optimized search
427
+ if (this.isDelegationEnabled() && this.agenticFlowSona) {
428
+ try {
429
+ const results = await this.agenticFlowSona.findPatterns(params.query, {
430
+ category: params.category,
431
+ topK,
432
+ threshold,
433
+ });
434
+
435
+ // Map results to SONAPattern format
436
+ const patterns: SONAPattern[] = results.map(r => ({
437
+ id: r.id,
438
+ pattern: r.pattern,
439
+ solution: r.solution,
440
+ category: r.category,
441
+ confidence: r.confidence,
442
+ usageCount: r.usageCount,
443
+ createdAt: r.createdAt,
444
+ lastUsedAt: r.lastUsedAt,
445
+ metadata: r.metadata,
446
+ }));
447
+
448
+ this.emit('patterns-retrieved', {
449
+ query: params.query,
450
+ count: patterns.length,
451
+ delegated: true,
452
+ target: '@sparkleideas/agentic-flow',
453
+ });
454
+
455
+ return patterns;
456
+ } catch (error) {
457
+ // Log delegation failure and fall back to local implementation
458
+ this.emit('delegation-failed', {
459
+ method: 'findSimilarPatterns',
460
+ error: (error as Error).message,
461
+ fallback: 'local',
462
+ });
463
+ // Continue with local implementation below
464
+ }
465
+ }
466
+
467
+ // Local implementation (fallback or when @sparkleideas/agentic-flow not available)
468
+ const results: Array<{ pattern: SONAPattern; score: number }> = [];
469
+
470
+ for (const pattern of this.patterns.values()) {
471
+ // Filter by category if specified
472
+ if (params.category && pattern.category !== params.category) {
473
+ continue;
474
+ }
475
+
476
+ // Calculate text similarity (for vector embeddings, use HNSW index)
477
+ const score = this.calculateSimilarity(params.query, pattern.pattern);
478
+
479
+ if (score >= threshold) {
480
+ results.push({ pattern, score });
481
+ }
482
+ }
483
+
484
+ // Sort by score and return top K
485
+ results.sort((a, b) => b.score - a.score);
486
+ const topResults = results.slice(0, topK).map(r => {
487
+ // Update usage stats
488
+ r.pattern.usageCount++;
489
+ r.pattern.lastUsedAt = Date.now();
490
+ return r.pattern;
491
+ });
492
+
493
+ this.emit('patterns-retrieved', {
494
+ query: params.query,
495
+ count: topResults.length
496
+ });
497
+
498
+ return topResults;
499
+ }
500
+
501
+ /**
502
+ * Get a pattern by ID
503
+ */
504
+ async getPattern(patternId: string): Promise<SONAPattern | null> {
505
+ this.ensureInitialized();
506
+ return this.patterns.get(patternId) || null;
507
+ }
508
+
509
+ /**
510
+ * Delete a pattern
511
+ */
512
+ async deletePattern(patternId: string): Promise<boolean> {
513
+ this.ensureInitialized();
514
+
515
+ const deleted = this.patterns.delete(patternId);
516
+ if (deleted) {
517
+ this.stats.totalPatterns--;
518
+ this.updateAverageConfidence();
519
+ this.emit('pattern-deleted', { patternId });
520
+ }
521
+
522
+ return deleted;
523
+ }
524
+
525
+ /**
526
+ * Force a learning cycle
527
+ */
528
+ async forceLearningCycle(): Promise<void> {
529
+ this.ensureInitialized();
530
+
531
+ this.emit('learning-cycle-starting');
532
+
533
+ try {
534
+ // Consolidate patterns
535
+ await this.consolidatePatterns();
536
+
537
+ // Prune low-confidence patterns
538
+ await this.prunePatterns();
539
+
540
+ // Update statistics
541
+ this.learningCycleCount++;
542
+ this.stats.learningCycles = this.learningCycleCount;
543
+
544
+ this.emit('learning-cycle-completed', {
545
+ cycleCount: this.learningCycleCount
546
+ });
547
+ } catch (error) {
548
+ this.emit('learning-cycle-failed', { error });
549
+ throw error;
550
+ }
551
+ }
552
+
553
+ /**
554
+ * Get learning statistics
555
+ */
556
+ async getStats(): Promise<SONALearningStats> {
557
+ this.ensureInitialized();
558
+
559
+ return {
560
+ ...this.stats,
561
+ totalPatterns: this.patterns.size,
562
+ activeTrajectories: this.activeTrajectories.size,
563
+ currentMode: this.config.mode,
564
+ memoryUsage: this.estimateMemoryUsage(),
565
+ };
566
+ }
567
+
568
+ /**
569
+ * Export patterns for persistence
570
+ */
571
+ async exportPatterns(): Promise<SONAPattern[]> {
572
+ this.ensureInitialized();
573
+ return Array.from(this.patterns.values());
574
+ }
575
+
576
+ /**
577
+ * Import patterns from storage
578
+ */
579
+ async importPatterns(patterns: SONAPattern[]): Promise<number> {
580
+ this.ensureInitialized();
581
+
582
+ let imported = 0;
583
+ for (const pattern of patterns) {
584
+ if (!this.patterns.has(pattern.id)) {
585
+ this.patterns.set(pattern.id, pattern);
586
+ imported++;
587
+ }
588
+ }
589
+
590
+ this.stats.totalPatterns = this.patterns.size;
591
+ this.updateAverageConfidence();
592
+
593
+ this.emit('patterns-imported', { count: imported });
594
+ return imported;
595
+ }
596
+
597
+ /**
598
+ * Shutdown the adapter
599
+ */
600
+ async shutdown(): Promise<void> {
601
+ this.stopConsolidationTimer();
602
+
603
+ // Complete any active trajectories
604
+ for (const [id, trajectory] of this.activeTrajectories) {
605
+ trajectory.endTime = Date.now();
606
+ trajectory.verdict = 'neutral';
607
+ }
608
+ this.activeTrajectories.clear();
609
+
610
+ this.initialized = false;
611
+ this.emit('shutdown');
612
+ }
613
+
614
+ // ===== Private Methods =====
615
+
616
+ private mergeConfig(config: Partial<SONAConfiguration>): SONAConfiguration {
617
+ return {
618
+ mode: config.mode || 'balanced',
619
+ learningRate: config.learningRate ?? 0.001,
620
+ similarityThreshold: config.similarityThreshold ?? 0.7,
621
+ maxPatterns: config.maxPatterns ?? 10000,
622
+ enableTrajectoryTracking: config.enableTrajectoryTracking ?? true,
623
+ consolidationInterval: config.consolidationInterval ?? 3600000,
624
+ autoModeSelection: config.autoModeSelection ?? true,
625
+ };
626
+ }
627
+
628
+ private initializeStats(): SONALearningStats {
629
+ return {
630
+ totalPatterns: 0,
631
+ activeTrajectories: 0,
632
+ completedTrajectories: 0,
633
+ averageConfidence: 0,
634
+ learningCycles: 0,
635
+ lastConsolidation: Date.now(),
636
+ memoryUsage: 0,
637
+ currentMode: this.config.mode,
638
+ };
639
+ }
640
+
641
+ private applyModeConfig(mode: SONALearningMode): void {
642
+ const modeConfig = MODE_CONFIGS[mode];
643
+ if (modeConfig) {
644
+ Object.assign(this.config, modeConfig);
645
+ }
646
+ }
647
+
648
+ private startConsolidationTimer(): void {
649
+ this.consolidationTimer = setInterval(
650
+ () => this.consolidatePatterns(),
651
+ this.config.consolidationInterval
652
+ );
653
+ }
654
+
655
+ private stopConsolidationTimer(): void {
656
+ if (this.consolidationTimer) {
657
+ clearInterval(this.consolidationTimer);
658
+ this.consolidationTimer = null;
659
+ }
660
+ }
661
+
662
+ private async consolidatePatterns(): Promise<void> {
663
+ // Merge similar patterns
664
+ const patternsArray = Array.from(this.patterns.values());
665
+ const toRemove: Set<string> = new Set();
666
+
667
+ for (let i = 0; i < patternsArray.length; i++) {
668
+ if (toRemove.has(patternsArray[i].id)) continue;
669
+
670
+ for (let j = i + 1; j < patternsArray.length; j++) {
671
+ if (toRemove.has(patternsArray[j].id)) continue;
672
+
673
+ const similarity = this.calculateSimilarity(
674
+ patternsArray[i].pattern,
675
+ patternsArray[j].pattern
676
+ );
677
+
678
+ if (similarity > 0.95) {
679
+ // Merge into pattern with higher confidence
680
+ if (patternsArray[i].confidence >= patternsArray[j].confidence) {
681
+ patternsArray[i].usageCount += patternsArray[j].usageCount;
682
+ toRemove.add(patternsArray[j].id);
683
+ } else {
684
+ patternsArray[j].usageCount += patternsArray[i].usageCount;
685
+ toRemove.add(patternsArray[i].id);
686
+ }
687
+ }
688
+ }
689
+ }
690
+
691
+ // Remove merged patterns
692
+ for (const id of toRemove) {
693
+ this.patterns.delete(id);
694
+ }
695
+
696
+ this.stats.lastConsolidation = Date.now();
697
+ this.emit('consolidation-completed', {
698
+ removed: toRemove.size,
699
+ remaining: this.patterns.size
700
+ });
701
+ }
702
+
703
+ private async prunePatterns(): Promise<void> {
704
+ const maxPatterns = this.config.maxPatterns;
705
+ if (this.patterns.size <= maxPatterns) {
706
+ return;
707
+ }
708
+
709
+ // Sort patterns by score (combination of confidence, recency, and usage)
710
+ const scored = Array.from(this.patterns.entries()).map(([id, pattern]) => ({
711
+ id,
712
+ pattern,
713
+ score: this.calculatePatternScore(pattern),
714
+ }));
715
+
716
+ scored.sort((a, b) => b.score - a.score);
717
+
718
+ // Keep only top patterns
719
+ const toKeep = new Set(scored.slice(0, maxPatterns).map(s => s.id));
720
+
721
+ for (const id of this.patterns.keys()) {
722
+ if (!toKeep.has(id)) {
723
+ this.patterns.delete(id);
724
+ }
725
+ }
726
+
727
+ this.emit('patterns-pruned', {
728
+ removed: scored.length - maxPatterns,
729
+ remaining: this.patterns.size
730
+ });
731
+ }
732
+
733
+ private async learnFromTrajectory(trajectory: SONATrajectory): Promise<void> {
734
+ // Extract patterns from successful trajectory
735
+ if (trajectory.steps.length === 0) return;
736
+
737
+ const pattern = trajectory.steps.map(s => s.action).join(' -> ');
738
+ const solution = trajectory.steps[trajectory.steps.length - 1].observation;
739
+
740
+ await this.storePattern({
741
+ pattern,
742
+ solution,
743
+ category: (trajectory.metadata.category as string) || 'general',
744
+ confidence: Math.min(1, trajectory.totalReward / trajectory.steps.length),
745
+ metadata: {
746
+ trajectoryId: trajectory.id,
747
+ taskId: trajectory.taskId,
748
+ stepCount: trajectory.steps.length,
749
+ },
750
+ });
751
+ }
752
+
753
+ private calculateSimilarity(a: string, b: string): number {
754
+ // Jaccard similarity on words (simplified)
755
+ const wordsA = new Set(a.toLowerCase().split(/\s+/));
756
+ const wordsB = new Set(b.toLowerCase().split(/\s+/));
757
+
758
+ const intersection = new Set([...wordsA].filter(x => wordsB.has(x)));
759
+ const union = new Set([...wordsA, ...wordsB]);
760
+
761
+ return intersection.size / union.size;
762
+ }
763
+
764
+ private calculatePatternScore(pattern: SONAPattern): number {
765
+ const now = Date.now();
766
+ const ageMs = now - pattern.createdAt;
767
+ const recencyMs = now - pattern.lastUsedAt;
768
+
769
+ // Normalize age and recency (decay over 30 days)
770
+ const ageFactor = Math.exp(-ageMs / (30 * 24 * 60 * 60 * 1000));
771
+ const recencyFactor = Math.exp(-recencyMs / (7 * 24 * 60 * 60 * 1000));
772
+ const usageFactor = Math.min(1, pattern.usageCount / 100);
773
+
774
+ return (
775
+ pattern.confidence * 0.4 +
776
+ recencyFactor * 0.3 +
777
+ usageFactor * 0.2 +
778
+ ageFactor * 0.1
779
+ );
780
+ }
781
+
782
+ private updateAverageConfidence(): void {
783
+ if (this.patterns.size === 0) {
784
+ this.stats.averageConfidence = 0;
785
+ return;
786
+ }
787
+
788
+ let total = 0;
789
+ for (const pattern of this.patterns.values()) {
790
+ total += pattern.confidence;
791
+ }
792
+ this.stats.averageConfidence = total / this.patterns.size;
793
+ }
794
+
795
+ private estimateMemoryUsage(): number {
796
+ // Rough estimate: 500 bytes per pattern, 1KB per trajectory step
797
+ const patternBytes = this.patterns.size * 500;
798
+ const trajectoryBytes = Array.from(this.activeTrajectories.values())
799
+ .reduce((sum, t) => sum + t.steps.length * 1024, 0);
800
+
801
+ return patternBytes + trajectoryBytes;
802
+ }
803
+
804
+ private generateId(prefix: string): string {
805
+ return `${prefix}_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
806
+ }
807
+
808
+ private ensureInitialized(): void {
809
+ if (!this.initialized) {
810
+ throw new Error('SONAAdapter not initialized. Call initialize() first.');
811
+ }
812
+ }
813
+ }
814
+
815
+ /**
816
+ * Create and initialize a SONA adapter
817
+ */
818
+ export async function createSONAAdapter(
819
+ config?: Partial<SONAConfiguration>
820
+ ): Promise<SONAAdapter> {
821
+ const adapter = new SONAAdapter(config);
822
+ await adapter.initialize();
823
+ return adapter;
824
+ }