@timmeck/brain-core 2.2.0 → 2.4.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.
package/dist/index.js CHANGED
@@ -45,4 +45,16 @@ export { CrossBrainSubscriptionManager } from './cross-brain/subscription.js';
45
45
  export { CrossBrainCorrelator } from './cross-brain/correlator.js';
46
46
  // ── Ecosystem ──────────────────────────────────────────────
47
47
  export { EcosystemService } from './ecosystem/service.js';
48
+ // ── Webhooks ──────────────────────────────────────────────
49
+ export { WebhookService, runWebhookMigration } from './webhooks/service.js';
50
+ // ── Export ────────────────────────────────────────────────
51
+ export { ExportService } from './export/service.js';
52
+ // ── Backup ────────────────────────────────────────────────
53
+ export { BackupService } from './backup/service.js';
54
+ // ── Meta-Learning ────────────────────────────────────────
55
+ export { MetaLearningEngine, runMetaLearningMigration } from './meta-learning/engine.js';
56
+ // ── Causal Inference ─────────────────────────────────────
57
+ export { CausalGraph, runCausalMigration } from './causal/engine.js';
58
+ // ── Hypothesis Engine ────────────────────────────────────
59
+ export { HypothesisEngine, runHypothesisMigration } from './hypothesis/engine.js';
48
60
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA,8DAA8D;AAC9D,OAAO,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AACzC,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAEzE,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC1E,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAElD,8DAA8D;AAC9D,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAEtD,8DAA8D;AAC9D,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAClE,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAE5C,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAErE,OAAO,EAAE,QAAQ,EAAE,eAAe,EAAE,aAAa,EAAE,YAAY,EAAE,uBAAuB,EAAE,MAAM,iBAAiB,CAAC;AAElH,8DAA8D;AAC9D,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAEjD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAGrD,8DAA8D;AAC9D,OAAO,EAAE,CAAC,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,WAAW,EAAE,WAAW,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAEtH,8DAA8D;AAC9D,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAEhD,OAAO,EAAE,WAAW,EAAE,iBAAiB,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAG3F,8DAA8D;AAC9D,OAAO,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AACrD,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAEvD,8DAA8D;AAC9D,OAAO,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAQ/D,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAC;AAC3D,OAAO,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAC/C,OAAO,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAC/D,OAAO,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AACpD,OAAO,EAAE,kBAAkB,EAAE,MAAM,+BAA+B,CAAC;AAEnE,6DAA6D;AAC7D,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAE/D,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAG/D,2DAA2D;AAC3D,OAAO,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAC;AAU7D,OAAO,EAAE,gBAAgB,EAAE,MAAM,gCAAgC,CAAC;AAElE,4DAA4D;AAC5D,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAExD,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAG/D,8DAA8D;AAC9D,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAE3D,OAAO,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAC;AAEpE,OAAO,EAAE,6BAA6B,EAAE,MAAM,+BAA+B,CAAC;AAE9E,OAAO,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAC;AAGnE,8DAA8D;AAC9D,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA,8DAA8D;AAC9D,OAAO,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AACzC,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAEzE,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC1E,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAElD,8DAA8D;AAC9D,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAEtD,8DAA8D;AAC9D,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAClE,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAE5C,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAErE,OAAO,EAAE,QAAQ,EAAE,eAAe,EAAE,aAAa,EAAE,YAAY,EAAE,uBAAuB,EAAE,MAAM,iBAAiB,CAAC;AAElH,8DAA8D;AAC9D,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAEjD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAGrD,8DAA8D;AAC9D,OAAO,EAAE,CAAC,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,WAAW,EAAE,WAAW,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAEtH,8DAA8D;AAC9D,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAEhD,OAAO,EAAE,WAAW,EAAE,iBAAiB,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAG3F,8DAA8D;AAC9D,OAAO,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AACrD,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAEvD,8DAA8D;AAC9D,OAAO,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAQ/D,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAC;AAC3D,OAAO,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAC/C,OAAO,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAC/D,OAAO,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AACpD,OAAO,EAAE,kBAAkB,EAAE,MAAM,+BAA+B,CAAC;AAEnE,6DAA6D;AAC7D,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAE/D,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAG/D,2DAA2D;AAC3D,OAAO,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAC;AAU7D,OAAO,EAAE,gBAAgB,EAAE,MAAM,gCAAgC,CAAC;AAElE,4DAA4D;AAC5D,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAExD,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAG/D,8DAA8D;AAC9D,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAE3D,OAAO,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAC;AAEpE,OAAO,EAAE,6BAA6B,EAAE,MAAM,+BAA+B,CAAC;AAE9E,OAAO,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAC;AAGnE,8DAA8D;AAC9D,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAG1D,6DAA6D;AAC7D,OAAO,EAAE,cAAc,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAG5E,6DAA6D;AAC7D,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAGpD,6DAA6D;AAC7D,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAGpD,4DAA4D;AAC5D,OAAO,EAAE,kBAAkB,EAAE,wBAAwB,EAAE,MAAM,2BAA2B,CAAC;AAGzF,4DAA4D;AAC5D,OAAO,EAAE,WAAW,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAGrE,4DAA4D;AAC5D,OAAO,EAAE,gBAAgB,EAAE,sBAAsB,EAAE,MAAM,wBAAwB,CAAC"}
@@ -0,0 +1,108 @@
1
+ import type Database from 'better-sqlite3';
2
+ export interface HyperParameter {
3
+ name: string;
4
+ value: number;
5
+ min: number;
6
+ max: number;
7
+ step: number;
8
+ }
9
+ export interface LearningSnapshot {
10
+ id?: number;
11
+ cycle: number;
12
+ params: Record<string, number>;
13
+ metrics: Record<string, number>;
14
+ score: number;
15
+ created_at?: string;
16
+ }
17
+ export interface ParameterRecommendation {
18
+ name: string;
19
+ currentValue: number;
20
+ recommendedValue: number;
21
+ expectedImprovement: number;
22
+ confidence: number;
23
+ evidence: number;
24
+ }
25
+ export interface MetaLearningStatus {
26
+ totalSnapshots: number;
27
+ totalOptimizations: number;
28
+ bestScore: number;
29
+ worstScore: number;
30
+ currentScore: number;
31
+ trend: 'improving' | 'stable' | 'declining';
32
+ recommendations: ParameterRecommendation[];
33
+ }
34
+ export declare function runMetaLearningMigration(db: Database.Database): void;
35
+ /**
36
+ * Meta-Learning Engine: observes learning cycle outcomes over time
37
+ * and auto-tunes hyperparameters using gradient-free optimization.
38
+ *
39
+ * Research approach: Bayesian-inspired parameter search.
40
+ * - After each learning cycle, record a snapshot (params + metrics + effectiveness score)
41
+ * - Periodically analyze: which parameter configurations produced the best scores?
42
+ * - Use perturbation: try small changes to parameters, measure improvement, keep what works
43
+ * - Implements "explore vs exploit": 80% exploit best known, 20% explore new configurations
44
+ */
45
+ export declare class MetaLearningEngine {
46
+ private db;
47
+ private params;
48
+ private logger;
49
+ private cycleCount;
50
+ private analyzeInterval;
51
+ private explorationRate;
52
+ constructor(db: Database.Database, params: HyperParameter[], config?: {
53
+ analyzeInterval?: number;
54
+ explorationRate?: number;
55
+ });
56
+ /**
57
+ * Record a learning cycle's outcome. Called after each learning cycle.
58
+ * @param metrics - the raw metrics from the cycle (e.g. newPatterns, updatedRules, prunedRules)
59
+ * @param score - a composite effectiveness score (higher = better)
60
+ */
61
+ recordSnapshot(metrics: Record<string, number>, score: number): LearningSnapshot;
62
+ /**
63
+ * Analyze snapshots and recommend parameter changes.
64
+ * Uses a simplified approach inspired by Bayesian optimization:
65
+ * - Group snapshots by parameter ranges
66
+ * - Find which ranges produced the best average scores
67
+ * - Recommend moving towards those ranges
68
+ */
69
+ analyze(): ParameterRecommendation[];
70
+ /**
71
+ * Apply recommendations: perturb parameters towards better configurations.
72
+ * Returns the parameters that were changed.
73
+ */
74
+ optimize(): ParameterRecommendation[];
75
+ /**
76
+ * Run analysis + optimization if it's time. Call this after every learning cycle.
77
+ */
78
+ step(metrics: Record<string, number>, score: number): {
79
+ snapshot: LearningSnapshot;
80
+ optimized: ParameterRecommendation[];
81
+ };
82
+ /** Get current parameter values. */
83
+ getParams(): Record<string, number>;
84
+ /** Update a parameter value externally (e.g. user override). */
85
+ setParam(name: string, value: number): boolean;
86
+ /** Get learning effectiveness status. */
87
+ getStatus(): MetaLearningStatus;
88
+ /** Get optimization history. */
89
+ getHistory(limit?: number): Array<{
90
+ param_name: string;
91
+ old_value: number;
92
+ new_value: number;
93
+ reason: string;
94
+ improvement: number | null;
95
+ created_at: string;
96
+ }>;
97
+ private getSnapshots;
98
+ /**
99
+ * Analyze a single parameter's effect on learning effectiveness.
100
+ *
101
+ * Algorithm:
102
+ * 1. Divide the parameter's range into bins
103
+ * 2. For each bin, calculate the average score of snapshots in that bin
104
+ * 3. Find the bin with the highest average score
105
+ * 4. Recommend moving towards the center of that bin
106
+ */
107
+ private analyzeParameter;
108
+ }
@@ -0,0 +1,275 @@
1
+ import { getLogger } from '../utils/logger.js';
2
+ // ── Migration ───────────────────────────────────────────
3
+ export function runMetaLearningMigration(db) {
4
+ db.exec(`
5
+ CREATE TABLE IF NOT EXISTS meta_learning_snapshots (
6
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
7
+ cycle INTEGER NOT NULL,
8
+ params TEXT NOT NULL,
9
+ metrics TEXT NOT NULL,
10
+ score REAL NOT NULL,
11
+ created_at TEXT NOT NULL DEFAULT (datetime('now'))
12
+ );
13
+
14
+ CREATE TABLE IF NOT EXISTS meta_learning_optimizations (
15
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
16
+ param_name TEXT NOT NULL,
17
+ old_value REAL NOT NULL,
18
+ new_value REAL NOT NULL,
19
+ reason TEXT NOT NULL,
20
+ improvement REAL,
21
+ created_at TEXT NOT NULL DEFAULT (datetime('now'))
22
+ );
23
+
24
+ CREATE INDEX IF NOT EXISTS idx_meta_snapshots_score ON meta_learning_snapshots(score);
25
+ CREATE INDEX IF NOT EXISTS idx_meta_snapshots_cycle ON meta_learning_snapshots(cycle);
26
+ `);
27
+ }
28
+ // ── Engine ───────────────────────────────────────────────
29
+ /**
30
+ * Meta-Learning Engine: observes learning cycle outcomes over time
31
+ * and auto-tunes hyperparameters using gradient-free optimization.
32
+ *
33
+ * Research approach: Bayesian-inspired parameter search.
34
+ * - After each learning cycle, record a snapshot (params + metrics + effectiveness score)
35
+ * - Periodically analyze: which parameter configurations produced the best scores?
36
+ * - Use perturbation: try small changes to parameters, measure improvement, keep what works
37
+ * - Implements "explore vs exploit": 80% exploit best known, 20% explore new configurations
38
+ */
39
+ export class MetaLearningEngine {
40
+ db;
41
+ params;
42
+ logger = getLogger();
43
+ cycleCount = 0;
44
+ analyzeInterval; // analyze every N cycles
45
+ explorationRate; // % of cycles spent exploring (0-1)
46
+ constructor(db, params, config) {
47
+ this.db = db;
48
+ this.params = params;
49
+ runMetaLearningMigration(db);
50
+ this.analyzeInterval = config?.analyzeInterval ?? 5;
51
+ this.explorationRate = config?.explorationRate ?? 0.2;
52
+ }
53
+ /**
54
+ * Record a learning cycle's outcome. Called after each learning cycle.
55
+ * @param metrics - the raw metrics from the cycle (e.g. newPatterns, updatedRules, prunedRules)
56
+ * @param score - a composite effectiveness score (higher = better)
57
+ */
58
+ recordSnapshot(metrics, score) {
59
+ this.cycleCount++;
60
+ const currentParams = {};
61
+ for (const p of this.params) {
62
+ currentParams[p.name] = p.value;
63
+ }
64
+ this.db.prepare(`
65
+ INSERT INTO meta_learning_snapshots (cycle, params, metrics, score)
66
+ VALUES (?, ?, ?, ?)
67
+ `).run(this.cycleCount, JSON.stringify(currentParams), JSON.stringify(metrics), score);
68
+ const snapshot = {
69
+ cycle: this.cycleCount,
70
+ params: currentParams,
71
+ metrics,
72
+ score,
73
+ };
74
+ this.logger.debug(`Meta-learning snapshot #${this.cycleCount}: score=${score.toFixed(3)}`);
75
+ return snapshot;
76
+ }
77
+ /**
78
+ * Analyze snapshots and recommend parameter changes.
79
+ * Uses a simplified approach inspired by Bayesian optimization:
80
+ * - Group snapshots by parameter ranges
81
+ * - Find which ranges produced the best average scores
82
+ * - Recommend moving towards those ranges
83
+ */
84
+ analyze() {
85
+ const snapshots = this.getSnapshots(50); // last 50 cycles
86
+ if (snapshots.length < 5)
87
+ return []; // need minimum data
88
+ const recommendations = [];
89
+ for (const param of this.params) {
90
+ const rec = this.analyzeParameter(param, snapshots);
91
+ if (rec)
92
+ recommendations.push(rec);
93
+ }
94
+ return recommendations;
95
+ }
96
+ /**
97
+ * Apply recommendations: perturb parameters towards better configurations.
98
+ * Returns the parameters that were changed.
99
+ */
100
+ optimize() {
101
+ const recommendations = this.analyze();
102
+ const applied = [];
103
+ for (const rec of recommendations) {
104
+ if (rec.confidence < 0.3)
105
+ continue; // skip low-confidence recommendations
106
+ if (Math.abs(rec.expectedImprovement) < 0.01)
107
+ continue; // skip negligible improvements
108
+ const param = this.params.find(p => p.name === rec.name);
109
+ if (!param)
110
+ continue;
111
+ // Explore vs exploit: sometimes try random perturbations
112
+ const exploring = Math.random() < this.explorationRate;
113
+ let newValue;
114
+ if (exploring) {
115
+ // Random perturbation within bounds
116
+ const range = param.max - param.min;
117
+ newValue = param.value + (Math.random() - 0.5) * range * 0.2;
118
+ }
119
+ else {
120
+ // Move towards recommended value
121
+ newValue = rec.recommendedValue;
122
+ }
123
+ // Clamp to bounds
124
+ newValue = Math.max(param.min, Math.min(param.max, newValue));
125
+ // Record optimization
126
+ this.db.prepare(`
127
+ INSERT INTO meta_learning_optimizations (param_name, old_value, new_value, reason, improvement)
128
+ VALUES (?, ?, ?, ?, ?)
129
+ `).run(param.name, param.value, newValue, exploring ? 'exploration' : 'exploitation', rec.expectedImprovement);
130
+ this.logger.info(`Meta-learning: ${param.name} ${param.value.toFixed(4)} → ${newValue.toFixed(4)} (${exploring ? 'explore' : 'exploit'}, expected +${(rec.expectedImprovement * 100).toFixed(1)}%)`);
131
+ param.value = newValue;
132
+ rec.recommendedValue = newValue;
133
+ applied.push(rec);
134
+ }
135
+ return applied;
136
+ }
137
+ /**
138
+ * Run analysis + optimization if it's time. Call this after every learning cycle.
139
+ */
140
+ step(metrics, score) {
141
+ const snapshot = this.recordSnapshot(metrics, score);
142
+ let optimized = [];
143
+ if (this.cycleCount > 0 && this.cycleCount % this.analyzeInterval === 0) {
144
+ optimized = this.optimize();
145
+ }
146
+ return { snapshot, optimized };
147
+ }
148
+ /** Get current parameter values. */
149
+ getParams() {
150
+ const result = {};
151
+ for (const p of this.params) {
152
+ result[p.name] = p.value;
153
+ }
154
+ return result;
155
+ }
156
+ /** Update a parameter value externally (e.g. user override). */
157
+ setParam(name, value) {
158
+ const param = this.params.find(p => p.name === name);
159
+ if (!param)
160
+ return false;
161
+ param.value = Math.max(param.min, Math.min(param.max, value));
162
+ return true;
163
+ }
164
+ /** Get learning effectiveness status. */
165
+ getStatus() {
166
+ const snapshots = this.getSnapshots(100);
167
+ const optimizations = this.db.prepare('SELECT COUNT(*) as count FROM meta_learning_optimizations').get();
168
+ const scores = snapshots.map(s => s.score);
169
+ const currentScore = scores.length > 0 ? scores[0] : 0;
170
+ const bestScore = scores.length > 0 ? Math.max(...scores) : 0;
171
+ const worstScore = scores.length > 0 ? Math.min(...scores) : 0;
172
+ // Trend detection: compare average of last 5 vs previous 5
173
+ let trend = 'stable';
174
+ if (scores.length >= 10) {
175
+ const recent = scores.slice(0, 5).reduce((a, b) => a + b, 0) / 5;
176
+ const previous = scores.slice(5, 10).reduce((a, b) => a + b, 0) / 5;
177
+ const delta = recent - previous;
178
+ if (delta > 0.05)
179
+ trend = 'improving';
180
+ else if (delta < -0.05)
181
+ trend = 'declining';
182
+ }
183
+ return {
184
+ totalSnapshots: snapshots.length,
185
+ totalOptimizations: optimizations.count,
186
+ bestScore,
187
+ worstScore,
188
+ currentScore,
189
+ trend,
190
+ recommendations: this.analyze(),
191
+ };
192
+ }
193
+ /** Get optimization history. */
194
+ getHistory(limit = 20) {
195
+ return this.db.prepare('SELECT * FROM meta_learning_optimizations ORDER BY created_at DESC LIMIT ?').all(limit);
196
+ }
197
+ // ── Private ─────────────────────────────────────────
198
+ getSnapshots(limit) {
199
+ const rows = this.db.prepare('SELECT * FROM meta_learning_snapshots ORDER BY cycle DESC LIMIT ?').all(limit);
200
+ return rows.map(r => ({
201
+ id: r.id,
202
+ cycle: r.cycle,
203
+ params: JSON.parse(r.params),
204
+ metrics: JSON.parse(r.metrics),
205
+ score: r.score,
206
+ created_at: r.created_at,
207
+ }));
208
+ }
209
+ /**
210
+ * Analyze a single parameter's effect on learning effectiveness.
211
+ *
212
+ * Algorithm:
213
+ * 1. Divide the parameter's range into bins
214
+ * 2. For each bin, calculate the average score of snapshots in that bin
215
+ * 3. Find the bin with the highest average score
216
+ * 4. Recommend moving towards the center of that bin
217
+ */
218
+ analyzeParameter(param, snapshots) {
219
+ const numBins = 5;
220
+ const range = param.max - param.min;
221
+ if (range <= 0)
222
+ return null;
223
+ const binSize = range / numBins;
224
+ // Accumulate scores per bin
225
+ const bins = Array.from({ length: numBins }, () => ({ sum: 0, count: 0 }));
226
+ for (const snap of snapshots) {
227
+ const val = snap.params[param.name];
228
+ if (val === undefined)
229
+ continue;
230
+ const binIdx = Math.min(numBins - 1, Math.floor((val - param.min) / binSize));
231
+ bins[binIdx].sum += snap.score;
232
+ bins[binIdx].count++;
233
+ }
234
+ // Find best bin (minimum 2 samples)
235
+ let bestBin = -1;
236
+ let bestAvg = -Infinity;
237
+ let totalSamples = 0;
238
+ for (let i = 0; i < numBins; i++) {
239
+ if (bins[i].count < 2)
240
+ continue;
241
+ const avg = bins[i].sum / bins[i].count;
242
+ totalSamples += bins[i].count;
243
+ if (avg > bestAvg) {
244
+ bestAvg = avg;
245
+ bestBin = i;
246
+ }
247
+ }
248
+ if (bestBin === -1)
249
+ return null; // not enough data
250
+ // Calculate recommended value (center of best bin)
251
+ const recommended = param.min + (bestBin + 0.5) * binSize;
252
+ // Skip if current value is already in or near the best bin
253
+ const currentBin = Math.min(numBins - 1, Math.floor((param.value - param.min) / binSize));
254
+ if (currentBin === bestBin)
255
+ return null;
256
+ // Estimate improvement
257
+ const currentBinAvg = bins[currentBin].count > 0
258
+ ? bins[currentBin].sum / bins[currentBin].count
259
+ : 0;
260
+ const expectedImprovement = currentBinAvg > 0
261
+ ? (bestAvg - currentBinAvg) / currentBinAvg
262
+ : 0;
263
+ // Confidence: based on sample count in best bin
264
+ const confidence = Math.min(1, bins[bestBin].count / 10);
265
+ return {
266
+ name: param.name,
267
+ currentValue: param.value,
268
+ recommendedValue: recommended,
269
+ expectedImprovement,
270
+ confidence,
271
+ evidence: totalSamples,
272
+ };
273
+ }
274
+ }
275
+ //# sourceMappingURL=engine.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"engine.js","sourceRoot":"","sources":["../../src/meta-learning/engine.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,65 @@
1
+ import type Database from 'better-sqlite3';
2
+ export interface WebhookConfig {
3
+ id?: number;
4
+ url: string;
5
+ events: string[];
6
+ secret?: string;
7
+ active?: boolean;
8
+ name?: string;
9
+ }
10
+ export interface WebhookRecord {
11
+ id: number;
12
+ url: string;
13
+ events: string;
14
+ secret: string | null;
15
+ active: number;
16
+ name: string | null;
17
+ created_at: string;
18
+ }
19
+ export interface DeliveryRecord {
20
+ id: number;
21
+ webhook_id: number;
22
+ event: string;
23
+ payload: string;
24
+ status: number;
25
+ response: string | null;
26
+ attempts: number;
27
+ created_at: string;
28
+ }
29
+ export interface WebhookDeliveryResult {
30
+ webhookId: number;
31
+ url: string;
32
+ status: number;
33
+ success: boolean;
34
+ attempts: number;
35
+ }
36
+ export declare function runWebhookMigration(db: Database.Database): void;
37
+ export declare class WebhookService {
38
+ private db;
39
+ private logger;
40
+ private retryDelays;
41
+ constructor(db: Database.Database);
42
+ /** Register a new webhook endpoint. */
43
+ add(config: WebhookConfig): WebhookRecord;
44
+ /** Remove a webhook by ID. */
45
+ remove(id: number): boolean;
46
+ /** Get a single webhook by ID. */
47
+ get(id: number): WebhookRecord | null;
48
+ /** List all webhooks. */
49
+ list(): WebhookRecord[];
50
+ /** Toggle a webhook active/inactive. */
51
+ toggle(id: number, active: boolean): boolean;
52
+ /** Get delivery history for a webhook (most recent first). */
53
+ history(webhookId?: number, limit?: number): DeliveryRecord[];
54
+ /**
55
+ * Fire an event to all matching webhooks.
56
+ * Returns delivery results (non-blocking — fire and forget with retry).
57
+ */
58
+ fire(event: string, data: unknown): Promise<WebhookDeliveryResult[]>;
59
+ /** Deliver a payload to a single webhook with retries. */
60
+ private deliver;
61
+ private recordDelivery;
62
+ private delay;
63
+ /** Cleanup old delivery records. */
64
+ cleanup(olderThanDays?: number): number;
65
+ }
@@ -0,0 +1,157 @@
1
+ import crypto from 'node:crypto';
2
+ import { getLogger } from '../utils/logger.js';
3
+ // ── Migration ───────────────────────────────────────────
4
+ export function runWebhookMigration(db) {
5
+ db.exec(`
6
+ CREATE TABLE IF NOT EXISTS webhooks (
7
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
8
+ url TEXT NOT NULL,
9
+ events TEXT NOT NULL DEFAULT '[]',
10
+ secret TEXT,
11
+ active INTEGER NOT NULL DEFAULT 1,
12
+ name TEXT,
13
+ created_at TEXT NOT NULL DEFAULT (datetime('now'))
14
+ );
15
+
16
+ CREATE TABLE IF NOT EXISTS webhook_deliveries (
17
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
18
+ webhook_id INTEGER NOT NULL REFERENCES webhooks(id) ON DELETE CASCADE,
19
+ event TEXT NOT NULL,
20
+ payload TEXT NOT NULL,
21
+ status INTEGER NOT NULL DEFAULT 0,
22
+ response TEXT,
23
+ attempts INTEGER NOT NULL DEFAULT 0,
24
+ created_at TEXT NOT NULL DEFAULT (datetime('now'))
25
+ );
26
+
27
+ CREATE INDEX IF NOT EXISTS idx_webhook_deliveries_webhook_id ON webhook_deliveries(webhook_id);
28
+ CREATE INDEX IF NOT EXISTS idx_webhook_deliveries_created_at ON webhook_deliveries(created_at);
29
+ `);
30
+ }
31
+ // ── Service ─────────────────────────────────────────────
32
+ export class WebhookService {
33
+ db;
34
+ logger = getLogger();
35
+ retryDelays = [1000, 3000, 10000]; // 1s, 3s, 10s
36
+ constructor(db) {
37
+ this.db = db;
38
+ runWebhookMigration(db);
39
+ }
40
+ /** Register a new webhook endpoint. */
41
+ add(config) {
42
+ const stmt = this.db.prepare(`
43
+ INSERT INTO webhooks (url, events, secret, active, name)
44
+ VALUES (?, ?, ?, ?, ?)
45
+ `);
46
+ const info = stmt.run(config.url, JSON.stringify(config.events), config.secret ?? null, config.active !== false ? 1 : 0, config.name ?? null);
47
+ this.logger.info(`Webhook #${info.lastInsertRowid} registered: ${config.url}`);
48
+ return this.get(Number(info.lastInsertRowid));
49
+ }
50
+ /** Remove a webhook by ID. */
51
+ remove(id) {
52
+ const info = this.db.prepare('DELETE FROM webhooks WHERE id = ?').run(id);
53
+ return info.changes > 0;
54
+ }
55
+ /** Get a single webhook by ID. */
56
+ get(id) {
57
+ return this.db.prepare('SELECT * FROM webhooks WHERE id = ?').get(id);
58
+ }
59
+ /** List all webhooks. */
60
+ list() {
61
+ return this.db.prepare('SELECT * FROM webhooks ORDER BY created_at DESC').all();
62
+ }
63
+ /** Toggle a webhook active/inactive. */
64
+ toggle(id, active) {
65
+ const info = this.db.prepare('UPDATE webhooks SET active = ? WHERE id = ?').run(active ? 1 : 0, id);
66
+ return info.changes > 0;
67
+ }
68
+ /** Get delivery history for a webhook (most recent first). */
69
+ history(webhookId, limit = 50) {
70
+ if (webhookId) {
71
+ return this.db.prepare('SELECT * FROM webhook_deliveries WHERE webhook_id = ? ORDER BY created_at DESC LIMIT ?').all(webhookId, limit);
72
+ }
73
+ return this.db.prepare('SELECT * FROM webhook_deliveries ORDER BY created_at DESC LIMIT ?').all(limit);
74
+ }
75
+ /**
76
+ * Fire an event to all matching webhooks.
77
+ * Returns delivery results (non-blocking — fire and forget with retry).
78
+ */
79
+ async fire(event, data) {
80
+ const webhooks = this.db.prepare('SELECT * FROM webhooks WHERE active = 1').all();
81
+ const matching = webhooks.filter(wh => {
82
+ const events = JSON.parse(wh.events);
83
+ return events.includes('*') || events.includes(event);
84
+ });
85
+ if (matching.length === 0)
86
+ return [];
87
+ const payload = JSON.stringify({ event, data, timestamp: new Date().toISOString() });
88
+ const results = [];
89
+ await Promise.all(matching.map(async (wh) => {
90
+ const result = await this.deliver(wh, event, payload);
91
+ results.push(result);
92
+ }));
93
+ return results;
94
+ }
95
+ /** Deliver a payload to a single webhook with retries. */
96
+ async deliver(wh, event, payload) {
97
+ let lastStatus = 0;
98
+ let lastResponse = null;
99
+ let attempts = 0;
100
+ for (let i = 0; i <= this.retryDelays.length; i++) {
101
+ attempts++;
102
+ try {
103
+ const headers = {
104
+ 'Content-Type': 'application/json',
105
+ 'X-Webhook-Event': event,
106
+ };
107
+ // HMAC signing
108
+ if (wh.secret) {
109
+ const signature = crypto
110
+ .createHmac('sha256', wh.secret)
111
+ .update(payload)
112
+ .digest('hex');
113
+ headers['X-Webhook-Signature'] = `sha256=${signature}`;
114
+ }
115
+ const response = await fetch(wh.url, {
116
+ method: 'POST',
117
+ headers,
118
+ body: payload,
119
+ signal: AbortSignal.timeout(10_000),
120
+ });
121
+ lastStatus = response.status;
122
+ lastResponse = await response.text().catch(() => null);
123
+ if (response.ok) {
124
+ this.recordDelivery(wh.id, event, payload, lastStatus, lastResponse, attempts);
125
+ return { webhookId: wh.id, url: wh.url, status: lastStatus, success: true, attempts };
126
+ }
127
+ }
128
+ catch (err) {
129
+ lastStatus = 0;
130
+ lastResponse = err instanceof Error ? err.message : String(err);
131
+ }
132
+ // Retry delay (don't delay after last attempt)
133
+ if (i < this.retryDelays.length) {
134
+ await this.delay(this.retryDelays[i]);
135
+ }
136
+ }
137
+ // All retries exhausted
138
+ this.recordDelivery(wh.id, event, payload, lastStatus, lastResponse, attempts);
139
+ this.logger.warn(`Webhook #${wh.id} delivery failed after ${attempts} attempts: ${wh.url}`);
140
+ return { webhookId: wh.id, url: wh.url, status: lastStatus, success: false, attempts };
141
+ }
142
+ recordDelivery(webhookId, event, payload, status, response, attempts) {
143
+ this.db.prepare(`
144
+ INSERT INTO webhook_deliveries (webhook_id, event, payload, status, response, attempts)
145
+ VALUES (?, ?, ?, ?, ?, ?)
146
+ `).run(webhookId, event, payload, status, response, attempts);
147
+ }
148
+ delay(ms) {
149
+ return new Promise(resolve => setTimeout(resolve, ms));
150
+ }
151
+ /** Cleanup old delivery records. */
152
+ cleanup(olderThanDays = 30) {
153
+ const info = this.db.prepare(`DELETE FROM webhook_deliveries WHERE created_at < datetime('now', '-' || ? || ' days')`).run(olderThanDays);
154
+ return info.changes;
155
+ }
156
+ }
157
+ //# sourceMappingURL=service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"service.js","sourceRoot":"","sources":["../../src/webhooks/service.ts"],"names":[],"mappings":"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"}