@timmeck/brain-core 2.23.0 → 2.25.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/debate/debate-engine.d.ts +137 -0
- package/dist/debate/debate-engine.js +540 -0
- package/dist/debate/debate-engine.js.map +1 -0
- package/dist/debate/index.d.ts +2 -0
- package/dist/debate/index.js +2 -0
- package/dist/debate/index.js.map +1 -0
- package/dist/emergence/emergence-engine.d.ts +169 -0
- package/dist/emergence/emergence-engine.js +687 -0
- package/dist/emergence/emergence-engine.js.map +1 -0
- package/dist/emergence/index.d.ts +2 -0
- package/dist/emergence/index.js +2 -0
- package/dist/emergence/index.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/research/research-orchestrator.d.ts +9 -0
- package/dist/research/research-orchestrator.js +91 -0
- package/dist/research/research-orchestrator.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,687 @@
|
|
|
1
|
+
import { getLogger } from '../utils/logger.js';
|
|
2
|
+
// ── Migration ───────────────────────────────────────────
|
|
3
|
+
export function runEmergenceMigration(db) {
|
|
4
|
+
db.exec(`
|
|
5
|
+
CREATE TABLE IF NOT EXISTS emergence_events (
|
|
6
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
7
|
+
timestamp TEXT DEFAULT (datetime('now')),
|
|
8
|
+
type TEXT NOT NULL,
|
|
9
|
+
title TEXT NOT NULL,
|
|
10
|
+
description TEXT NOT NULL,
|
|
11
|
+
surprise_score REAL NOT NULL DEFAULT 0,
|
|
12
|
+
evidence TEXT NOT NULL DEFAULT '[]',
|
|
13
|
+
source_engine TEXT NOT NULL DEFAULT '',
|
|
14
|
+
was_predicted INTEGER NOT NULL DEFAULT 0,
|
|
15
|
+
related_principles TEXT NOT NULL DEFAULT '[]'
|
|
16
|
+
);
|
|
17
|
+
CREATE INDEX IF NOT EXISTS idx_emergence_events_type ON emergence_events(type);
|
|
18
|
+
CREATE INDEX IF NOT EXISTS idx_emergence_events_surprise ON emergence_events(surprise_score DESC);
|
|
19
|
+
|
|
20
|
+
CREATE TABLE IF NOT EXISTS emergence_metrics (
|
|
21
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
22
|
+
timestamp TEXT DEFAULT (datetime('now')),
|
|
23
|
+
compression_complexity REAL NOT NULL DEFAULT 0,
|
|
24
|
+
knowledge_entropy REAL NOT NULL DEFAULT 0,
|
|
25
|
+
network_density REAL NOT NULL DEFAULT 0,
|
|
26
|
+
synapse_count INTEGER NOT NULL DEFAULT 0,
|
|
27
|
+
node_count INTEGER NOT NULL DEFAULT 0,
|
|
28
|
+
avg_weight REAL NOT NULL DEFAULT 0,
|
|
29
|
+
knowledge_diversity REAL NOT NULL DEFAULT 0,
|
|
30
|
+
integration_phi REAL NOT NULL DEFAULT 0,
|
|
31
|
+
cycle INTEGER NOT NULL DEFAULT 0
|
|
32
|
+
);
|
|
33
|
+
CREATE INDEX IF NOT EXISTS idx_emergence_metrics_cycle ON emergence_metrics(cycle);
|
|
34
|
+
`);
|
|
35
|
+
}
|
|
36
|
+
// ── Engine ──────────────────────────────────────────────
|
|
37
|
+
export class EmergenceEngine {
|
|
38
|
+
db;
|
|
39
|
+
config;
|
|
40
|
+
log = getLogger();
|
|
41
|
+
ts = null;
|
|
42
|
+
sources = {};
|
|
43
|
+
startTime = Date.now();
|
|
44
|
+
cycleCount = 0;
|
|
45
|
+
// Prepared statements
|
|
46
|
+
stmtInsertEvent;
|
|
47
|
+
stmtListEvents;
|
|
48
|
+
stmtEventsByType;
|
|
49
|
+
stmtTotalEvents;
|
|
50
|
+
stmtUnpredictedCount;
|
|
51
|
+
stmtAvgSurprise;
|
|
52
|
+
stmtInsertMetrics;
|
|
53
|
+
stmtLatestMetrics;
|
|
54
|
+
stmtMetricsTrend;
|
|
55
|
+
constructor(db, config) {
|
|
56
|
+
this.db = db;
|
|
57
|
+
this.config = {
|
|
58
|
+
brainName: config.brainName,
|
|
59
|
+
surpriseThreshold: config.surpriseThreshold ?? 0.5,
|
|
60
|
+
metricsEvery: config.metricsEvery ?? 5,
|
|
61
|
+
};
|
|
62
|
+
runEmergenceMigration(db);
|
|
63
|
+
this.stmtInsertEvent = db.prepare(`
|
|
64
|
+
INSERT INTO emergence_events (type, title, description, surprise_score, evidence, source_engine, was_predicted, related_principles)
|
|
65
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
66
|
+
`);
|
|
67
|
+
this.stmtListEvents = db.prepare('SELECT * FROM emergence_events ORDER BY surprise_score DESC LIMIT ?');
|
|
68
|
+
this.stmtEventsByType = db.prepare('SELECT type, COUNT(*) as cnt FROM emergence_events GROUP BY type');
|
|
69
|
+
this.stmtTotalEvents = db.prepare('SELECT COUNT(*) as cnt FROM emergence_events');
|
|
70
|
+
this.stmtUnpredictedCount = db.prepare('SELECT COUNT(*) as cnt FROM emergence_events WHERE was_predicted = 0');
|
|
71
|
+
this.stmtAvgSurprise = db.prepare('SELECT AVG(surprise_score) as avg FROM emergence_events');
|
|
72
|
+
this.stmtInsertMetrics = db.prepare(`
|
|
73
|
+
INSERT INTO emergence_metrics (compression_complexity, knowledge_entropy, network_density,
|
|
74
|
+
synapse_count, node_count, avg_weight, knowledge_diversity, integration_phi, cycle)
|
|
75
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
76
|
+
`);
|
|
77
|
+
this.stmtLatestMetrics = db.prepare('SELECT * FROM emergence_metrics ORDER BY cycle DESC LIMIT 1');
|
|
78
|
+
this.stmtMetricsTrend = db.prepare('SELECT cycle, compression_complexity, integration_phi, knowledge_entropy FROM emergence_metrics ORDER BY cycle DESC LIMIT ?');
|
|
79
|
+
this.log.debug(`[EmergenceEngine] Initialized for ${this.config.brainName}`);
|
|
80
|
+
}
|
|
81
|
+
// ── Setters ──────────────────────────────────────────
|
|
82
|
+
setThoughtStream(stream) { this.ts = stream; }
|
|
83
|
+
setDataSources(sources) {
|
|
84
|
+
this.sources = sources;
|
|
85
|
+
}
|
|
86
|
+
// ── Core: Emergence Detection ─────────────────────────
|
|
87
|
+
/**
|
|
88
|
+
* Scan for emergent patterns — things the system discovered
|
|
89
|
+
* that weren't explicitly programmed or predicted.
|
|
90
|
+
*/
|
|
91
|
+
detect() {
|
|
92
|
+
this.cycleCount++;
|
|
93
|
+
this.ts?.emit('emergence', 'exploring', 'Scanning for emergent patterns...', 'routine');
|
|
94
|
+
const events = [];
|
|
95
|
+
// 1. Unpredicted confirmed hypotheses
|
|
96
|
+
events.push(...this.detectUnpredictedHypotheses());
|
|
97
|
+
// 2. Anomaly patterns that repeat without rules
|
|
98
|
+
events.push(...this.detectRecurringAnomalies());
|
|
99
|
+
// 3. Cross-domain bridges (unexpected connections)
|
|
100
|
+
events.push(...this.detectCrossDomainBridges());
|
|
101
|
+
// 4. Phase transitions (sudden metric shifts)
|
|
102
|
+
events.push(...this.detectPhaseTransitions());
|
|
103
|
+
// 5. Novel experiment results
|
|
104
|
+
events.push(...this.detectNovelExperiments());
|
|
105
|
+
// Filter by surprise threshold and deduplicate
|
|
106
|
+
const significant = events.filter(e => e.surpriseScore >= this.config.surpriseThreshold);
|
|
107
|
+
const deduped = this.deduplicateEvents(significant);
|
|
108
|
+
// Persist new events
|
|
109
|
+
for (const e of deduped) {
|
|
110
|
+
const info = this.stmtInsertEvent.run(e.type, e.title, e.description, e.surpriseScore, JSON.stringify(e.evidence), e.sourceEngine, e.wasPredicted ? 1 : 0, JSON.stringify(e.relatedPrinciples));
|
|
111
|
+
e.id = Number(info.lastInsertRowid);
|
|
112
|
+
}
|
|
113
|
+
if (deduped.length > 0) {
|
|
114
|
+
const best = deduped[0];
|
|
115
|
+
this.ts?.emit('emergence', 'discovering', `${deduped.length} emergent event(s)! Top: "${best.title}" (surprise=${(best.surpriseScore * 100).toFixed(0)}%)`, best.surpriseScore > 0.8 ? 'breakthrough' : 'notable');
|
|
116
|
+
}
|
|
117
|
+
// Record complexity metrics periodically
|
|
118
|
+
if (this.cycleCount % this.config.metricsEvery === 0) {
|
|
119
|
+
this.recordMetrics();
|
|
120
|
+
}
|
|
121
|
+
return deduped;
|
|
122
|
+
}
|
|
123
|
+
// ── Core: Complexity Metrics ─────────────────────────
|
|
124
|
+
/**
|
|
125
|
+
* Compute and store complexity metrics for the current state.
|
|
126
|
+
* Approximates Kolmogorov complexity, Shannon entropy, and Phi (integration).
|
|
127
|
+
*/
|
|
128
|
+
recordMetrics() {
|
|
129
|
+
this.ts?.emit('emergence', 'analyzing', 'Computing complexity metrics...', 'routine');
|
|
130
|
+
const compressionComplexity = this.computeCompressionComplexity();
|
|
131
|
+
const knowledgeEntropy = this.computeKnowledgeEntropy();
|
|
132
|
+
const { density, synapseCount, nodeCount, avgWeight } = this.computeNetworkMetrics();
|
|
133
|
+
const knowledgeDiversity = this.computeKnowledgeDiversity();
|
|
134
|
+
const integrationPhi = this.computeIntegrationPhi();
|
|
135
|
+
const metrics = {
|
|
136
|
+
timestamp: new Date().toISOString(),
|
|
137
|
+
compressionComplexity,
|
|
138
|
+
knowledgeEntropy,
|
|
139
|
+
networkDensity: density,
|
|
140
|
+
synapseCount,
|
|
141
|
+
nodeCount,
|
|
142
|
+
avgWeight,
|
|
143
|
+
knowledgeDiversity,
|
|
144
|
+
integrationPhi,
|
|
145
|
+
cycle: this.cycleCount,
|
|
146
|
+
};
|
|
147
|
+
this.stmtInsertMetrics.run(compressionComplexity, knowledgeEntropy, density, synapseCount, nodeCount, avgWeight, knowledgeDiversity, integrationPhi, this.cycleCount);
|
|
148
|
+
this.ts?.emit('emergence', 'analyzing', `Complexity: K=${compressionComplexity.toFixed(3)}, H=${knowledgeEntropy.toFixed(2)}bit, Φ=${integrationPhi.toFixed(3)}, density=${density.toFixed(4)}`, 'routine');
|
|
149
|
+
return metrics;
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Compute surprise score for an observation.
|
|
153
|
+
* Uses Shannon information content: -log2(P(observation)).
|
|
154
|
+
* Normalized to 0-1.
|
|
155
|
+
*/
|
|
156
|
+
computeSurpriseScore(observation, context = {}) {
|
|
157
|
+
// Base surprise from rarity of related terms in journal
|
|
158
|
+
let baseSurprise = 0.5;
|
|
159
|
+
if (this.sources.journal) {
|
|
160
|
+
try {
|
|
161
|
+
const summary = this.sources.journal.getSummary();
|
|
162
|
+
const totalEntries = summary.total_entries || 1;
|
|
163
|
+
// Search for similar entries — fewer matches = more surprising
|
|
164
|
+
const entries = this.sources.journal.search(observation, 10);
|
|
165
|
+
const matchRate = entries.length / Math.max(totalEntries, 1);
|
|
166
|
+
baseSurprise = 1 - matchRate; // Rare = surprising
|
|
167
|
+
}
|
|
168
|
+
catch { /* not wired */ }
|
|
169
|
+
}
|
|
170
|
+
// Boost if contradicts known principles
|
|
171
|
+
let contradictionBoost = 0;
|
|
172
|
+
if (this.sources.knowledgeDistiller) {
|
|
173
|
+
try {
|
|
174
|
+
const pkg = this.sources.knowledgeDistiller.getPackage(this.config.brainName);
|
|
175
|
+
const contradicts = pkg.anti_patterns.some(ap => observation.toLowerCase().includes(ap.statement.toLowerCase().substring(0, 30)));
|
|
176
|
+
if (contradicts)
|
|
177
|
+
contradictionBoost = 0.2;
|
|
178
|
+
}
|
|
179
|
+
catch { /* not wired */ }
|
|
180
|
+
}
|
|
181
|
+
// Boost from context severity/deviation
|
|
182
|
+
const deviation = context.deviation ?? 0;
|
|
183
|
+
const deviationBoost = Math.min(0.3, deviation * 0.1);
|
|
184
|
+
return Math.min(1, baseSurprise + contradictionBoost + deviationBoost);
|
|
185
|
+
}
|
|
186
|
+
// ── Query Methods ────────────────────────────────────
|
|
187
|
+
getEvents(limit = 20) {
|
|
188
|
+
const rows = this.stmtListEvents.all(limit);
|
|
189
|
+
return rows.map(r => this.toEvent(r));
|
|
190
|
+
}
|
|
191
|
+
getEventsByType(type) {
|
|
192
|
+
if (type) {
|
|
193
|
+
const rows = this.db.prepare('SELECT * FROM emergence_events WHERE type = ? ORDER BY surprise_score DESC LIMIT 50').all(type);
|
|
194
|
+
return rows.map(r => this.toEvent(r));
|
|
195
|
+
}
|
|
196
|
+
return this.getEvents(50);
|
|
197
|
+
}
|
|
198
|
+
getLatestMetrics() {
|
|
199
|
+
const row = this.stmtLatestMetrics.get();
|
|
200
|
+
return row ? this.toMetrics(row) : null;
|
|
201
|
+
}
|
|
202
|
+
getMetricsTrend(limit = 20) {
|
|
203
|
+
const rows = this.stmtMetricsTrend.all(limit);
|
|
204
|
+
return rows.map(r => ({
|
|
205
|
+
cycle: r.cycle,
|
|
206
|
+
compressionComplexity: r.compression_complexity,
|
|
207
|
+
integrationPhi: r.integration_phi,
|
|
208
|
+
knowledgeEntropy: r.knowledge_entropy,
|
|
209
|
+
})).reverse(); // Chronological order
|
|
210
|
+
}
|
|
211
|
+
getStatus() {
|
|
212
|
+
const total = this.stmtTotalEvents.get().cnt;
|
|
213
|
+
const unpredicted = this.stmtUnpredictedCount.get().cnt;
|
|
214
|
+
const avgSurprise = this.stmtAvgSurprise.get().avg ?? 0;
|
|
215
|
+
const byType = {};
|
|
216
|
+
const typeRows = this.stmtEventsByType.all();
|
|
217
|
+
for (const r of typeRows)
|
|
218
|
+
byType[r.type] = r.cnt;
|
|
219
|
+
return {
|
|
220
|
+
totalEvents: total,
|
|
221
|
+
eventsByType: byType,
|
|
222
|
+
unpredictedCount: unpredicted,
|
|
223
|
+
avgSurpriseScore: avgSurprise,
|
|
224
|
+
latestMetrics: this.getLatestMetrics(),
|
|
225
|
+
metricsTrend: this.getMetricsTrend(20),
|
|
226
|
+
topEvents: this.getEvents(5),
|
|
227
|
+
uptime: Date.now() - this.startTime,
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
// ── Private: Detection Methods ────────────────────────
|
|
231
|
+
/** Find hypotheses that were confirmed but weren't predicted by any principle. */
|
|
232
|
+
detectUnpredictedHypotheses() {
|
|
233
|
+
const events = [];
|
|
234
|
+
if (!this.sources.hypothesisEngine)
|
|
235
|
+
return events;
|
|
236
|
+
try {
|
|
237
|
+
const confirmed = this.sources.hypothesisEngine.list('confirmed', 20);
|
|
238
|
+
const principles = this.sources.knowledgeDistiller
|
|
239
|
+
? this.sources.knowledgeDistiller.getPackage(this.config.brainName).principles
|
|
240
|
+
: [];
|
|
241
|
+
for (const h of confirmed) {
|
|
242
|
+
// Check if this hypothesis is covered by any principle
|
|
243
|
+
const coveredByPrinciple = principles.some(p => this.textOverlap(p.statement, h.statement) > 0.3);
|
|
244
|
+
if (!coveredByPrinciple) {
|
|
245
|
+
// This was discovered autonomously — emergent!
|
|
246
|
+
const surprise = this.computeSurpriseScore(h.statement, {
|
|
247
|
+
confidence: h.confidence,
|
|
248
|
+
evidence_for: h.evidence_for,
|
|
249
|
+
});
|
|
250
|
+
events.push({
|
|
251
|
+
type: 'unpredicted_pattern',
|
|
252
|
+
title: `Autonomous discovery: ${h.statement.substring(0, 80)}`,
|
|
253
|
+
description: `Hypothesis confirmed (p=${h.p_value.toFixed(4)}, evidence: ${h.evidence_for}/${h.evidence_for + h.evidence_against}) without matching any known principle. This was discovered by the system itself.`,
|
|
254
|
+
surpriseScore: surprise,
|
|
255
|
+
evidence: [`hypothesis:${h.id}`, `confidence:${h.confidence.toFixed(2)}`],
|
|
256
|
+
sourceEngine: 'HypothesisEngine',
|
|
257
|
+
wasPredicted: false,
|
|
258
|
+
relatedPrinciples: [],
|
|
259
|
+
timestamp: h.tested_at || new Date().toISOString(),
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
catch { /* not wired */ }
|
|
265
|
+
return events;
|
|
266
|
+
}
|
|
267
|
+
/** Find anomalies that recur in patterns not covered by any rule. */
|
|
268
|
+
detectRecurringAnomalies() {
|
|
269
|
+
const events = [];
|
|
270
|
+
if (!this.sources.anomalyDetective)
|
|
271
|
+
return events;
|
|
272
|
+
try {
|
|
273
|
+
const anomalies = this.sources.anomalyDetective.getAnomalies(undefined, 50);
|
|
274
|
+
// Group by metric to find recurring patterns
|
|
275
|
+
const byMetric = new Map();
|
|
276
|
+
for (const a of anomalies) {
|
|
277
|
+
const key = `${a.type}:${a.metric}`;
|
|
278
|
+
if (!byMetric.has(key))
|
|
279
|
+
byMetric.set(key, []);
|
|
280
|
+
byMetric.get(key).push({ deviation: a.deviation, timestamp: a.timestamp });
|
|
281
|
+
}
|
|
282
|
+
for (const [key, occurrences] of byMetric) {
|
|
283
|
+
if (occurrences.length >= 3) {
|
|
284
|
+
// Recurring anomaly — is this covered by any principle?
|
|
285
|
+
const principles = this.sources.knowledgeDistiller
|
|
286
|
+
? this.sources.knowledgeDistiller.getPackage(this.config.brainName).principles
|
|
287
|
+
: [];
|
|
288
|
+
const metricName = key.split(':')[1] || key;
|
|
289
|
+
const covered = principles.some(p => p.statement.toLowerCase().includes(metricName.toLowerCase()));
|
|
290
|
+
if (!covered) {
|
|
291
|
+
const avgDeviation = occurrences.reduce((s, o) => s + Math.abs(o.deviation), 0) / occurrences.length;
|
|
292
|
+
events.push({
|
|
293
|
+
type: 'self_organization',
|
|
294
|
+
title: `Recurring anomaly pattern: ${metricName} (${occurrences.length}x)`,
|
|
295
|
+
description: `The metric "${metricName}" shows recurring anomalies (${occurrences.length} occurrences, avg deviation: ${avgDeviation.toFixed(2)}) without any principle explaining this pattern. The system is detecting a regularity it wasn't programmed to find.`,
|
|
296
|
+
surpriseScore: Math.min(1, 0.4 + avgDeviation * 0.1 + occurrences.length * 0.05),
|
|
297
|
+
evidence: occurrences.map((_, i) => `anomaly:${key}:${i}`),
|
|
298
|
+
sourceEngine: 'AnomalyDetective',
|
|
299
|
+
wasPredicted: false,
|
|
300
|
+
relatedPrinciples: [],
|
|
301
|
+
timestamp: new Date().toISOString(),
|
|
302
|
+
});
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
catch { /* not wired */ }
|
|
308
|
+
return events;
|
|
309
|
+
}
|
|
310
|
+
/** Find unexpected cross-domain connections. */
|
|
311
|
+
detectCrossDomainBridges() {
|
|
312
|
+
const events = [];
|
|
313
|
+
if (!this.sources.journal)
|
|
314
|
+
return events;
|
|
315
|
+
try {
|
|
316
|
+
// Look for journal entries tagged with multiple domains
|
|
317
|
+
const recent = this.sources.journal.search('cross-domain', 20);
|
|
318
|
+
for (const entry of recent) {
|
|
319
|
+
if (entry.significance === 'notable' || entry.significance === 'breakthrough') {
|
|
320
|
+
const domains = (entry.tags || []).filter(t => ['brain', 'trading', 'marketing', 'cross-domain', 'cross_domain'].includes(t.toLowerCase()));
|
|
321
|
+
if (domains.length >= 2) {
|
|
322
|
+
events.push({
|
|
323
|
+
type: 'cross_domain_bridge',
|
|
324
|
+
title: `Cross-domain link: ${entry.title}`,
|
|
325
|
+
description: entry.content.substring(0, 300),
|
|
326
|
+
surpriseScore: entry.significance === 'breakthrough' ? 0.9 : 0.6,
|
|
327
|
+
evidence: [`journal:${entry.id}`],
|
|
328
|
+
sourceEngine: 'ResearchJournal',
|
|
329
|
+
wasPredicted: false,
|
|
330
|
+
relatedPrinciples: [],
|
|
331
|
+
timestamp: entry.created_at || new Date().toISOString(),
|
|
332
|
+
});
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
catch { /* not wired */ }
|
|
338
|
+
return events;
|
|
339
|
+
}
|
|
340
|
+
/** Detect phase transitions: sudden jumps in metrics. */
|
|
341
|
+
detectPhaseTransitions() {
|
|
342
|
+
const events = [];
|
|
343
|
+
const trend = this.getMetricsTrend(10);
|
|
344
|
+
if (trend.length < 3)
|
|
345
|
+
return events;
|
|
346
|
+
// Check for sudden jumps in complexity or entropy
|
|
347
|
+
for (let i = 2; i < trend.length; i++) {
|
|
348
|
+
const prev = trend[i - 1];
|
|
349
|
+
const curr = trend[i];
|
|
350
|
+
// Phi jump: > 50% increase
|
|
351
|
+
if (prev.integrationPhi > 0 && curr.integrationPhi / prev.integrationPhi > 1.5) {
|
|
352
|
+
events.push({
|
|
353
|
+
type: 'phase_transition',
|
|
354
|
+
title: `Integration spike: Φ jumped ${((curr.integrationPhi / prev.integrationPhi - 1) * 100).toFixed(0)}%`,
|
|
355
|
+
description: `Knowledge integration (Phi) jumped from ${prev.integrationPhi.toFixed(3)} to ${curr.integrationPhi.toFixed(3)} between cycles ${prev.cycle} and ${curr.cycle}. This suggests a qualitative shift in how knowledge is interconnected.`,
|
|
356
|
+
surpriseScore: Math.min(1, 0.5 + (curr.integrationPhi / prev.integrationPhi - 1) * 0.3),
|
|
357
|
+
evidence: [`metrics:cycle:${prev.cycle}`, `metrics:cycle:${curr.cycle}`],
|
|
358
|
+
sourceEngine: 'EmergenceEngine',
|
|
359
|
+
wasPredicted: false,
|
|
360
|
+
relatedPrinciples: [],
|
|
361
|
+
timestamp: curr.compressionComplexity ? new Date().toISOString() : new Date().toISOString(),
|
|
362
|
+
});
|
|
363
|
+
}
|
|
364
|
+
// Entropy jump: > 30% increase
|
|
365
|
+
if (prev.knowledgeEntropy > 0 && curr.knowledgeEntropy / prev.knowledgeEntropy > 1.3) {
|
|
366
|
+
events.push({
|
|
367
|
+
type: 'phase_transition',
|
|
368
|
+
title: `Knowledge entropy spike: H jumped ${((curr.knowledgeEntropy / prev.knowledgeEntropy - 1) * 100).toFixed(0)}%`,
|
|
369
|
+
description: `Knowledge entropy rose from ${prev.knowledgeEntropy.toFixed(2)} to ${curr.knowledgeEntropy.toFixed(2)} bits. New categories of knowledge are being created rapidly.`,
|
|
370
|
+
surpriseScore: Math.min(1, 0.4 + (curr.knowledgeEntropy / prev.knowledgeEntropy - 1) * 0.5),
|
|
371
|
+
evidence: [`metrics:cycle:${prev.cycle}`, `metrics:cycle:${curr.cycle}`],
|
|
372
|
+
sourceEngine: 'EmergenceEngine',
|
|
373
|
+
wasPredicted: false,
|
|
374
|
+
relatedPrinciples: [],
|
|
375
|
+
timestamp: new Date().toISOString(),
|
|
376
|
+
});
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
return events;
|
|
380
|
+
}
|
|
381
|
+
/** Find experiments with unexpectedly large effects. */
|
|
382
|
+
detectNovelExperiments() {
|
|
383
|
+
const events = [];
|
|
384
|
+
if (!this.sources.experimentEngine)
|
|
385
|
+
return events;
|
|
386
|
+
try {
|
|
387
|
+
const completed = this.sources.experimentEngine.list('complete', 20);
|
|
388
|
+
for (const exp of completed) {
|
|
389
|
+
if (exp.conclusion?.significant && exp.conclusion.effect_size) {
|
|
390
|
+
const effectSize = Math.abs(exp.conclusion.effect_size);
|
|
391
|
+
// Large effect (Cohen's d > 0.8) = novel finding
|
|
392
|
+
if (effectSize > 0.8) {
|
|
393
|
+
events.push({
|
|
394
|
+
type: 'novel_behavior',
|
|
395
|
+
title: `Large experiment effect: ${exp.name}`,
|
|
396
|
+
description: `Experiment "${exp.name}" showed effect d=${exp.conclusion.effect_size.toFixed(2)} (p=${exp.conclusion.p_value?.toFixed(4)}). Hypothesis: "${exp.hypothesis}". This is a large, statistically significant effect discovered through autonomous experimentation.`,
|
|
397
|
+
surpriseScore: Math.min(1, 0.5 + effectSize * 0.2),
|
|
398
|
+
evidence: [`experiment:${exp.id}`, `effect:${effectSize.toFixed(2)}`],
|
|
399
|
+
sourceEngine: 'ExperimentEngine',
|
|
400
|
+
wasPredicted: false,
|
|
401
|
+
relatedPrinciples: [],
|
|
402
|
+
timestamp: new Date().toISOString(),
|
|
403
|
+
});
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
catch { /* not wired */ }
|
|
409
|
+
return events;
|
|
410
|
+
}
|
|
411
|
+
// ── Private: Complexity Computations ──────────────────
|
|
412
|
+
/**
|
|
413
|
+
* Approximate Kolmogorov complexity via compression ratio.
|
|
414
|
+
* Serialize knowledge → measure compressibility.
|
|
415
|
+
* Less compressible = more complex (more unique information).
|
|
416
|
+
*/
|
|
417
|
+
computeCompressionComplexity() {
|
|
418
|
+
let serialized = '';
|
|
419
|
+
if (this.sources.knowledgeDistiller) {
|
|
420
|
+
try {
|
|
421
|
+
const pkg = this.sources.knowledgeDistiller.getPackage(this.config.brainName);
|
|
422
|
+
for (const p of pkg.principles)
|
|
423
|
+
serialized += p.statement + '|';
|
|
424
|
+
for (const ap of pkg.anti_patterns)
|
|
425
|
+
serialized += ap.statement + '|';
|
|
426
|
+
for (const s of pkg.strategies)
|
|
427
|
+
serialized += s.id + ':' + s.description + '|';
|
|
428
|
+
}
|
|
429
|
+
catch { /* not wired */ }
|
|
430
|
+
}
|
|
431
|
+
if (this.sources.hypothesisEngine) {
|
|
432
|
+
try {
|
|
433
|
+
const all = this.sources.hypothesisEngine.list(undefined, 50);
|
|
434
|
+
for (const h of all)
|
|
435
|
+
serialized += h.statement + ':' + h.status + '|';
|
|
436
|
+
}
|
|
437
|
+
catch { /* not wired */ }
|
|
438
|
+
}
|
|
439
|
+
if (serialized.length < 10)
|
|
440
|
+
return 0;
|
|
441
|
+
// Simple compression ratio: unique bigrams / total bigrams
|
|
442
|
+
const bigrams = new Set();
|
|
443
|
+
let totalBigrams = 0;
|
|
444
|
+
for (let i = 0; i < serialized.length - 1; i++) {
|
|
445
|
+
bigrams.add(serialized.substring(i, i + 2));
|
|
446
|
+
totalBigrams++;
|
|
447
|
+
}
|
|
448
|
+
// Ratio: 0 = fully repetitive, 1 = all unique (high complexity)
|
|
449
|
+
return totalBigrams > 0 ? bigrams.size / totalBigrams : 0;
|
|
450
|
+
}
|
|
451
|
+
/**
|
|
452
|
+
* Shannon entropy over knowledge categories.
|
|
453
|
+
* Higher entropy = more diverse knowledge.
|
|
454
|
+
*/
|
|
455
|
+
computeKnowledgeEntropy() {
|
|
456
|
+
const counts = {};
|
|
457
|
+
let total = 0;
|
|
458
|
+
if (this.sources.knowledgeDistiller) {
|
|
459
|
+
try {
|
|
460
|
+
const pkg = this.sources.knowledgeDistiller.getPackage(this.config.brainName);
|
|
461
|
+
counts['principles'] = pkg.principles.length;
|
|
462
|
+
counts['anti_patterns'] = pkg.anti_patterns.length;
|
|
463
|
+
counts['strategies'] = pkg.strategies.length;
|
|
464
|
+
total += pkg.principles.length + pkg.anti_patterns.length + pkg.strategies.length;
|
|
465
|
+
}
|
|
466
|
+
catch { /* not wired */ }
|
|
467
|
+
}
|
|
468
|
+
if (this.sources.hypothesisEngine) {
|
|
469
|
+
try {
|
|
470
|
+
const all = this.sources.hypothesisEngine.list(undefined, 100);
|
|
471
|
+
for (const h of all) {
|
|
472
|
+
const key = `hypothesis:${h.status}`;
|
|
473
|
+
counts[key] = (counts[key] || 0) + 1;
|
|
474
|
+
total++;
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
catch { /* not wired */ }
|
|
478
|
+
}
|
|
479
|
+
if (this.sources.journal) {
|
|
480
|
+
try {
|
|
481
|
+
const summary = this.sources.journal.getSummary();
|
|
482
|
+
for (const [type, count] of Object.entries(summary.by_type || {})) {
|
|
483
|
+
counts[`journal:${type}`] = count;
|
|
484
|
+
total += count;
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
catch { /* not wired */ }
|
|
488
|
+
}
|
|
489
|
+
if (total === 0)
|
|
490
|
+
return 0;
|
|
491
|
+
// Shannon entropy: H = -Σ p(x) * log2(p(x))
|
|
492
|
+
let entropy = 0;
|
|
493
|
+
for (const count of Object.values(counts)) {
|
|
494
|
+
if (count > 0) {
|
|
495
|
+
const p = count / total;
|
|
496
|
+
entropy -= p * Math.log2(p);
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
return entropy;
|
|
500
|
+
}
|
|
501
|
+
/**
|
|
502
|
+
* Network metrics from synapse data.
|
|
503
|
+
*/
|
|
504
|
+
computeNetworkMetrics() {
|
|
505
|
+
if (this.sources.getNetworkStats) {
|
|
506
|
+
try {
|
|
507
|
+
const stats = this.sources.getNetworkStats();
|
|
508
|
+
const n = stats.totalNodes;
|
|
509
|
+
const density = n > 1 ? stats.totalSynapses / (n * (n - 1)) : 0;
|
|
510
|
+
return {
|
|
511
|
+
density: Math.min(1, density),
|
|
512
|
+
synapseCount: stats.totalSynapses,
|
|
513
|
+
nodeCount: n,
|
|
514
|
+
avgWeight: stats.avgWeight,
|
|
515
|
+
};
|
|
516
|
+
}
|
|
517
|
+
catch { /* not wired */ }
|
|
518
|
+
}
|
|
519
|
+
return { density: 0, synapseCount: 0, nodeCount: 0, avgWeight: 0 };
|
|
520
|
+
}
|
|
521
|
+
/**
|
|
522
|
+
* Knowledge diversity: unique knowledge types / total types possible.
|
|
523
|
+
*/
|
|
524
|
+
computeKnowledgeDiversity() {
|
|
525
|
+
const categories = new Set();
|
|
526
|
+
let maxCategories = 0;
|
|
527
|
+
if (this.sources.knowledgeDistiller) {
|
|
528
|
+
try {
|
|
529
|
+
const pkg = this.sources.knowledgeDistiller.getPackage(this.config.brainName);
|
|
530
|
+
if (pkg.principles.length > 0)
|
|
531
|
+
categories.add('principles');
|
|
532
|
+
if (pkg.anti_patterns.length > 0)
|
|
533
|
+
categories.add('anti_patterns');
|
|
534
|
+
if (pkg.strategies.length > 0)
|
|
535
|
+
categories.add('strategies');
|
|
536
|
+
maxCategories += 3;
|
|
537
|
+
}
|
|
538
|
+
catch { /* not wired */ }
|
|
539
|
+
}
|
|
540
|
+
if (this.sources.hypothesisEngine) {
|
|
541
|
+
try {
|
|
542
|
+
const statuses = ['proposed', 'testing', 'confirmed', 'rejected', 'inconclusive'];
|
|
543
|
+
for (const s of statuses) {
|
|
544
|
+
const list = this.sources.hypothesisEngine.list(s, 1);
|
|
545
|
+
if (list.length > 0)
|
|
546
|
+
categories.add(`hypothesis:${s}`);
|
|
547
|
+
}
|
|
548
|
+
maxCategories += 5;
|
|
549
|
+
}
|
|
550
|
+
catch { /* not wired */ }
|
|
551
|
+
}
|
|
552
|
+
if (this.sources.experimentEngine) {
|
|
553
|
+
try {
|
|
554
|
+
const exps = this.sources.experimentEngine.list(undefined, 1);
|
|
555
|
+
if (exps.length > 0)
|
|
556
|
+
categories.add('experiments');
|
|
557
|
+
maxCategories += 1;
|
|
558
|
+
}
|
|
559
|
+
catch { /* not wired */ }
|
|
560
|
+
}
|
|
561
|
+
if (this.sources.curiosityEngine) {
|
|
562
|
+
try {
|
|
563
|
+
const status = this.sources.curiosityEngine.getStatus();
|
|
564
|
+
if (status.totalGaps > 0)
|
|
565
|
+
categories.add('knowledge_gaps');
|
|
566
|
+
if (status.totalExplorations > 0)
|
|
567
|
+
categories.add('explorations');
|
|
568
|
+
maxCategories += 2;
|
|
569
|
+
}
|
|
570
|
+
catch { /* not wired */ }
|
|
571
|
+
}
|
|
572
|
+
return maxCategories > 0 ? categories.size / maxCategories : 0;
|
|
573
|
+
}
|
|
574
|
+
/**
|
|
575
|
+
* Integration proxy (Phi): measures how interconnected the knowledge is.
|
|
576
|
+
* Approximated by: cross-references between knowledge categories.
|
|
577
|
+
* Higher Phi = knowledge is more integrated (parts inform each other).
|
|
578
|
+
*/
|
|
579
|
+
computeIntegrationPhi() {
|
|
580
|
+
let crossRefs = 0;
|
|
581
|
+
let totalItems = 0;
|
|
582
|
+
if (this.sources.journal) {
|
|
583
|
+
try {
|
|
584
|
+
const summary = this.sources.journal.getSummary();
|
|
585
|
+
totalItems += summary.total_entries || 0;
|
|
586
|
+
// Count entries that reference other entries
|
|
587
|
+
const recent = this.sources.journal.search('', 50);
|
|
588
|
+
for (const entry of recent) {
|
|
589
|
+
if (entry.references && entry.references.length > 0) {
|
|
590
|
+
crossRefs += entry.references.length;
|
|
591
|
+
}
|
|
592
|
+
// Multi-tag entries indicate cross-cutting knowledge
|
|
593
|
+
if (entry.tags && entry.tags.length > 2) {
|
|
594
|
+
crossRefs += entry.tags.length - 2;
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
catch { /* not wired */ }
|
|
599
|
+
}
|
|
600
|
+
if (this.sources.hypothesisEngine) {
|
|
601
|
+
try {
|
|
602
|
+
const all = this.sources.hypothesisEngine.list(undefined, 50);
|
|
603
|
+
totalItems += all.length;
|
|
604
|
+
// Hypotheses with multiple variables indicate integration
|
|
605
|
+
for (const h of all) {
|
|
606
|
+
if (h.variables && h.variables.length > 1) {
|
|
607
|
+
crossRefs += h.variables.length - 1;
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
catch { /* not wired */ }
|
|
612
|
+
}
|
|
613
|
+
// Phi ≈ crossRefs / totalItems (normalized)
|
|
614
|
+
if (totalItems === 0)
|
|
615
|
+
return 0;
|
|
616
|
+
return Math.min(1, crossRefs / (totalItems * 2)); // Normalize: 2 refs per item = fully integrated
|
|
617
|
+
}
|
|
618
|
+
// ── Private: Helpers ─────────────────────────────────
|
|
619
|
+
/** Simple word overlap ratio between two texts. */
|
|
620
|
+
textOverlap(a, b) {
|
|
621
|
+
const wordsA = new Set(a.toLowerCase().split(/\s+/).filter(w => w.length > 3));
|
|
622
|
+
const wordsB = new Set(b.toLowerCase().split(/\s+/).filter(w => w.length > 3));
|
|
623
|
+
if (wordsA.size === 0 || wordsB.size === 0)
|
|
624
|
+
return 0;
|
|
625
|
+
let overlap = 0;
|
|
626
|
+
for (const w of wordsA)
|
|
627
|
+
if (wordsB.has(w))
|
|
628
|
+
overlap++;
|
|
629
|
+
return overlap / Math.max(wordsA.size, wordsB.size);
|
|
630
|
+
}
|
|
631
|
+
/** Deduplicate events by title similarity. */
|
|
632
|
+
deduplicateEvents(events) {
|
|
633
|
+
const unique = [];
|
|
634
|
+
const seenTitles = new Set();
|
|
635
|
+
// Also check against already-persisted events
|
|
636
|
+
const existing = this.getEvents(50);
|
|
637
|
+
for (const e of existing)
|
|
638
|
+
seenTitles.add(e.title.toLowerCase().substring(0, 60));
|
|
639
|
+
for (const e of events) {
|
|
640
|
+
const key = e.title.toLowerCase().substring(0, 60);
|
|
641
|
+
if (!seenTitles.has(key)) {
|
|
642
|
+
seenTitles.add(key);
|
|
643
|
+
unique.push(e);
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
return unique.sort((a, b) => b.surpriseScore - a.surpriseScore);
|
|
647
|
+
}
|
|
648
|
+
toEvent(row) {
|
|
649
|
+
let evidence = [];
|
|
650
|
+
let relatedPrinciples = [];
|
|
651
|
+
try {
|
|
652
|
+
evidence = JSON.parse(row.evidence || '[]');
|
|
653
|
+
}
|
|
654
|
+
catch { /* ignore */ }
|
|
655
|
+
try {
|
|
656
|
+
relatedPrinciples = JSON.parse(row.related_principles || '[]');
|
|
657
|
+
}
|
|
658
|
+
catch { /* ignore */ }
|
|
659
|
+
return {
|
|
660
|
+
id: row.id,
|
|
661
|
+
timestamp: row.timestamp,
|
|
662
|
+
type: row.type,
|
|
663
|
+
title: row.title,
|
|
664
|
+
description: row.description,
|
|
665
|
+
surpriseScore: row.surprise_score,
|
|
666
|
+
evidence,
|
|
667
|
+
sourceEngine: row.source_engine,
|
|
668
|
+
wasPredicted: row.was_predicted === 1,
|
|
669
|
+
relatedPrinciples,
|
|
670
|
+
};
|
|
671
|
+
}
|
|
672
|
+
toMetrics(row) {
|
|
673
|
+
return {
|
|
674
|
+
timestamp: row.timestamp,
|
|
675
|
+
compressionComplexity: row.compression_complexity,
|
|
676
|
+
knowledgeEntropy: row.knowledge_entropy,
|
|
677
|
+
networkDensity: row.network_density,
|
|
678
|
+
synapseCount: row.synapse_count,
|
|
679
|
+
nodeCount: row.node_count,
|
|
680
|
+
avgWeight: row.avg_weight,
|
|
681
|
+
knowledgeDiversity: row.knowledge_diversity,
|
|
682
|
+
integrationPhi: row.integration_phi,
|
|
683
|
+
cycle: row.cycle,
|
|
684
|
+
};
|
|
685
|
+
}
|
|
686
|
+
}
|
|
687
|
+
//# sourceMappingURL=emergence-engine.js.map
|