@timmeck/brain-core 2.34.1 → 2.35.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.
@@ -55,6 +55,8 @@ export class ResearchOrchestrator {
55
55
  selfScanner = null;
56
56
  selfModificationEngine = null;
57
57
  bootstrapService = null;
58
+ conceptAbstraction = null;
59
+ db;
58
60
  brainName;
59
61
  feedbackTimer = null;
60
62
  cycleCount = 0;
@@ -69,6 +71,7 @@ export class ResearchOrchestrator {
69
71
  /** Hash of last written suggestions to prevent duplicate file writes. */
70
72
  lastSuggestionsHash = '';
71
73
  constructor(db, config, causalGraph) {
74
+ this.db = db;
72
75
  this.brainName = config.brainName;
73
76
  this.distillEvery = config.distillEvery ?? 5;
74
77
  this.agendaEvery = config.agendaEvery ?? 3;
@@ -86,7 +89,7 @@ export class ResearchOrchestrator {
86
89
  this.autoResponder = new AutoResponder(db, { brainName: config.brainName });
87
90
  this.autoResponder.setAdaptiveStrategy(this.adaptiveStrategy);
88
91
  this.autoResponder.setJournal(this.journal);
89
- this.hypothesisEngine = new HypothesisEngine(db, { minEvidence: 5, confirmThreshold: 0.05, rejectThreshold: 0.5 });
92
+ this.hypothesisEngine = new HypothesisEngine(db, { minEvidence: 3, confirmThreshold: 0.05, rejectThreshold: 0.5 });
90
93
  }
91
94
  /** Set the DataMiner instance for DB-driven engine feeding. */
92
95
  setDataMiner(miner) {
@@ -124,6 +127,8 @@ export class ResearchOrchestrator {
124
127
  /** Set the TransferEngine — cross-domain knowledge transfer. */
125
128
  setTransferEngine(engine) {
126
129
  this.transferEngine = engine;
130
+ // Register own distiller as peer for self-analysis and transfer proposals
131
+ engine.registerPeerDistiller(this.brainName + '_self', this.knowledgeDistiller);
127
132
  }
128
133
  /** Set the NarrativeEngine — brain explains itself in natural language. */
129
134
  setNarrativeEngine(engine) {
@@ -175,6 +180,8 @@ export class ResearchOrchestrator {
175
180
  setSelfModificationEngine(engine) { this.selfModificationEngine = engine; }
176
181
  /** Set the BootstrapService — seeds initial data on first cycle. */
177
182
  setBootstrapService(service) { this.bootstrapService = service; }
183
+ /** Set the ConceptAbstraction — clusters knowledge into abstract concepts. */
184
+ setConceptAbstraction(engine) { this.conceptAbstraction = engine; }
178
185
  /** Set the PredictionEngine — wires journal into it. */
179
186
  setPredictionEngine(engine) {
180
187
  this.predictionEngine = engine;
@@ -350,6 +357,73 @@ export class ResearchOrchestrator {
350
357
  this.hypothesisEngine.observe({ source: this.brainName, type: `anomaly:${a.metric}`, value: a.deviation, timestamp: now, metadata: { severity: a.severity } });
351
358
  }
352
359
  }
360
+ // 2e. Accumulate evidence for hypotheses based on cycle observations
361
+ {
362
+ const pendingHypotheses = this.hypothesisEngine.list('proposed', 50)
363
+ .concat(this.hypothesisEngine.list('testing', 50));
364
+ for (const hyp of pendingHypotheses) {
365
+ if (!hyp.id)
366
+ continue;
367
+ const vars = hyp.variables ?? [];
368
+ const statement = hyp.statement.toLowerCase();
369
+ let evidenceType = null;
370
+ let reason = '';
371
+ // Check if cycle metrics match hypothesis variables
372
+ if (vars.includes('journal_entries') || statement.includes('journal')) {
373
+ const journalStats = this.journal.getSummary();
374
+ const entries = journalStats.total_entries ?? 0;
375
+ if (entries > this.cycleCount) {
376
+ evidenceType = 'for';
377
+ reason = `journal_entries=${entries} growing (cycle ${this.cycleCount})`;
378
+ }
379
+ else if (this.cycleCount > 5 && entries < 3) {
380
+ evidenceType = 'against';
381
+ reason = `journal_entries=${entries} stagnant after ${this.cycleCount} cycles`;
382
+ }
383
+ }
384
+ else if (vars.includes('anomaly_count') || statement.includes('anomal')) {
385
+ if (anomalies.length > 0) {
386
+ evidenceType = 'for';
387
+ reason = `${anomalies.length} anomalies detected this cycle`;
388
+ }
389
+ }
390
+ else if (vars.includes('insight_count') || vars.includes('observation_type_count') || statement.includes('observation')) {
391
+ if (insights.length > 0) {
392
+ evidenceType = 'for';
393
+ reason = `${insights.length} insights from observations this cycle`;
394
+ }
395
+ }
396
+ else if (statement.includes('dream') || statement.includes('memor')) {
397
+ // Dream-related: check if dream engine produced output
398
+ if (this.dreamEngine) {
399
+ const dStatus = this.dreamEngine.getStatus();
400
+ const totals = dStatus.totals;
401
+ if ((totals?.memoriesConsolidated ?? 0) > 0) {
402
+ evidenceType = 'for';
403
+ reason = `dream consolidated ${totals?.memoriesConsolidated} memories`;
404
+ }
405
+ }
406
+ }
407
+ else if (statement.includes('attention') || statement.includes('focus')) {
408
+ if (this.attentionEngine) {
409
+ const topTopics = this.attentionEngine.getTopTopics(1);
410
+ if (topTopics.length > 0) {
411
+ evidenceType = 'for';
412
+ reason = `attention active: top topic "${topTopics[0].topic}" (score=${topTopics[0].score.toFixed(1)})`;
413
+ }
414
+ }
415
+ }
416
+ if (evidenceType && hyp.id) {
417
+ try {
418
+ const col = evidenceType === 'for' ? 'evidence_for' : 'evidence_against';
419
+ this.db.prepare(`UPDATE hypotheses SET ${col} = ${col} + 1 WHERE id = ?`).run(hyp.id);
420
+ }
421
+ catch {
422
+ // Hypothesis table might not have these columns yet — skip
423
+ }
424
+ }
425
+ }
426
+ }
353
427
  // 2d. Attention Engine: decay scores, compute engine weights, persist focus
354
428
  if (this.attentionEngine) {
355
429
  ts?.emit('attention', 'focusing', 'Updating attention scores and engine weights...');
@@ -497,18 +571,45 @@ export class ResearchOrchestrator {
497
571
  this.journal.reflect();
498
572
  ts?.emit('journal', 'reflecting', 'Reflection complete', 'notable');
499
573
  }
500
- // 8b. Cycle-end journal entry — guarantees journal grows every cycle
574
+ // 8b. Cycle-end journal entry — guarantees journal grows every cycle (enriched with engine state)
501
575
  {
502
576
  const cycleDuration = Date.now() - start;
503
577
  const journalStats = this.journal.getSummary();
578
+ const hypSummary = this.hypothesisEngine.getSummary();
579
+ const predAccuracy = (() => {
580
+ try {
581
+ const predSummary = this.predictionEngine?.getSummary();
582
+ const domains = (predSummary?.by_domain ?? []);
583
+ if (domains.length > 0)
584
+ return Math.round((domains[0]?.accuracy_rate ?? 0) * 100);
585
+ }
586
+ catch { /* */ }
587
+ return 0;
588
+ })();
589
+ const attTopics = (() => {
590
+ try {
591
+ return this.attentionEngine?.getTopTopics?.(3) ?? [];
592
+ }
593
+ catch {
594
+ return [];
595
+ }
596
+ })();
597
+ const focusStr = attTopics.length > 0
598
+ ? attTopics.map((t) => t.topic).join(', ')
599
+ : 'none';
504
600
  this.journal.write({
505
601
  type: 'insight',
506
602
  title: `Cycle #${this.cycleCount} summary`,
507
- content: `Completed feedback cycle #${this.cycleCount} in ${cycleDuration}ms. ${insights.length} insights, ${anomalies.length} anomalies detected. Journal now has ${journalStats.total_entries ?? 0} entries.`,
603
+ content: `Completed feedback cycle #${this.cycleCount} in ${cycleDuration}ms. ${insights.length} insights, ${anomalies.length} anomalies detected. Hypotheses: ${hypSummary.proposed} pending, ${hypSummary.confirmed} confirmed. Predictions: accuracy ${predAccuracy}%. Focus: ${focusStr}. Journal: ${journalStats.total_entries ?? 0} entries.`,
508
604
  tags: [this.brainName, 'cycle-summary'],
509
605
  references: [],
510
606
  significance: 'routine',
511
- data: { cycle: this.cycleCount, duration_ms: cycleDuration, insights: insights.length, anomalies: anomalies.length },
607
+ data: {
608
+ cycle: this.cycleCount, duration_ms: cycleDuration,
609
+ insights: insights.length, anomalies: anomalies.length,
610
+ hypotheses_confirmed: hypSummary.confirmed, hypotheses_total: hypSummary.total,
611
+ prediction_accuracy: predAccuracy, focus: focusStr,
612
+ },
512
613
  });
513
614
  }
514
615
  // 9. Prediction Engine: resolve pending + auto-predict
@@ -551,6 +652,15 @@ export class ResearchOrchestrator {
551
652
  if (this.autoExperimentEngine && this.cycleCount > 3 && this.cycleCount % 5 === 0) {
552
653
  ts?.emit('auto_experiment', 'experimenting', 'Processing auto-experiments...');
553
654
  try {
655
+ // Ensure MetaCognition has fresh report cards before AutoExperiment discovers candidates
656
+ if (this.metaCognitionLayer) {
657
+ try {
658
+ this.metaCognitionLayer.evaluate();
659
+ }
660
+ catch (mcErr) {
661
+ this.log.warn(`[orchestrator] MetaCog pre-eval for AutoExp: ${mcErr.message}`);
662
+ }
663
+ }
554
664
  // Feed measurements
555
665
  this.autoExperimentEngine.feedMeasurement('insight_count', insights.length);
556
666
  this.autoExperimentEngine.feedMeasurement('anomaly_count', anomalies.length);
@@ -584,6 +694,28 @@ export class ResearchOrchestrator {
584
694
  }
585
695
  // 13. Periodic Dream Consolidation: don't wait for idle, consolidate every 10 cycles
586
696
  if (this.dreamEngine && this.cycleCount % 10 === 0) {
697
+ ts?.emit('dream', 'dreaming', 'Feeding knowledge into memories for consolidation...');
698
+ try {
699
+ // Feed current knowledge into memories table so consolidation has material
700
+ const principles = this.knowledgeDistiller.getPrinciples(undefined, 50);
701
+ const journalEntries = this.journal.search('', 30);
702
+ const insertMemory = this.db.prepare(`
703
+ INSERT OR IGNORE INTO memories (category, key, content, importance, source, tags, active)
704
+ VALUES (?, ?, ?, ?, 'orchestrator_feed', ?, 1)
705
+ `);
706
+ for (const p of principles) {
707
+ insertMemory.run('principle', `principle:${p.id ?? p.statement.substring(0, 50)}`, p.statement, Math.min(1, (p.confidence ?? 0.5) + 0.3), JSON.stringify(['principle', this.brainName]));
708
+ }
709
+ for (const j of journalEntries) {
710
+ if (j.significance === 'breakthrough' || j.significance === 'notable') {
711
+ insertMemory.run('journal', `journal:${j.id ?? j.title.substring(0, 50)}`, `${j.title}: ${j.content?.substring(0, 200) ?? ''}`, j.significance === 'breakthrough' ? 0.9 : 0.6, JSON.stringify(['journal', j.type, this.brainName]));
712
+ }
713
+ }
714
+ ts?.emit('dream', 'dreaming', `Fed ${principles.length} principles + ${journalEntries.filter(j => j.significance === 'breakthrough' || j.significance === 'notable').length} journal entries into memories`);
715
+ }
716
+ catch (feedErr) {
717
+ this.log.warn(`[orchestrator] Dream memory feed error: ${feedErr.message}`);
718
+ }
587
719
  ts?.emit('dream', 'dreaming', 'Scheduled consolidation starting (every 10 cycles)...');
588
720
  try {
589
721
  this.dreamEngine.consolidate('auto');
@@ -867,6 +999,29 @@ export class ResearchOrchestrator {
867
999
  this.log.error(`[orchestrator] MetaCognition error: ${err.message}`);
868
1000
  }
869
1001
  }
1002
+ // 21b. Adaptive Strategy: adjust parameters for poorly performing engines
1003
+ if (this.metaCognitionLayer && this.cycleCount % this.reflectEvery === 0) {
1004
+ try {
1005
+ const cards = this.metaCognitionLayer.getLatestReportCards();
1006
+ const poorCards = cards.filter(c => c.grade === 'D' || c.grade === 'F');
1007
+ for (const card of poorCards.slice(0, 3)) {
1008
+ // Try adapting the engine's research strategy parameters
1009
+ const strategyDomains = ['research', 'learning'];
1010
+ for (const domain of strategyDomains) {
1011
+ const currentVal = this.adaptiveStrategy.getParam(domain, card.engine);
1012
+ if (currentVal !== null) {
1013
+ const direction = card.grade === 'F' ? 0.2 : 0.1;
1014
+ this.adaptiveStrategy.adapt(domain, card.engine, currentVal * (1 + direction), `MetaCog grade ${card.grade} — boosting ${card.engine}`, { reportCard: card });
1015
+ ts?.emit('adaptive_strategy', 'discovering', `Adapted ${domain}.${card.engine}: grade ${card.grade} → boost ${(direction * 100).toFixed(0)}%`, 'routine');
1016
+ break; // Only adapt one domain per engine
1017
+ }
1018
+ }
1019
+ }
1020
+ }
1021
+ catch (err) {
1022
+ this.log.warn(`[orchestrator] Step 21b AdaptiveStrategy error: ${err.message}`);
1023
+ }
1024
+ }
870
1025
  // 22. Parameter Registry: refresh orchestrator params from registry
871
1026
  if (this.parameterRegistry) {
872
1027
  const distill = this.parameterRegistry.get('orchestrator', 'distillEvery');
@@ -1328,6 +1483,48 @@ export class ResearchOrchestrator {
1328
1483
  this.log.warn(`[orchestrator] Step 40 error: ${err.message}`);
1329
1484
  }
1330
1485
  }
1486
+ // Step 41: ConceptAbstraction — cluster knowledge into abstract concepts (every 10 cycles)
1487
+ if (this.conceptAbstraction && this.cycleCount % 10 === 0) {
1488
+ try {
1489
+ ts?.emit('concept_abstraction', 'analyzing', 'Step 41: Forming abstract concepts...', 'routine');
1490
+ const result = this.conceptAbstraction.formConcepts();
1491
+ if (result.totalConcepts > 0) {
1492
+ this.journal.write({
1493
+ title: `Concept Formation: ${result.totalConcepts} concepts across ${Object.keys(result.levels).length} levels`,
1494
+ type: 'discovery',
1495
+ content: `Formed ${result.totalConcepts} concepts (L0: ${result.levels[0] ?? 0}, L1: ${result.levels[1] ?? 0}, L2: ${result.levels[2] ?? 0})`,
1496
+ tags: [this.brainName, 'concept-abstraction', 'knowledge-organization'],
1497
+ references: [],
1498
+ significance: result.newConcepts > 0 ? 'notable' : 'routine',
1499
+ data: result,
1500
+ });
1501
+ }
1502
+ // Register concepts in MemoryPalace
1503
+ if (this.memoryPalace && result.totalConcepts > 0) {
1504
+ try {
1505
+ this.conceptAbstraction.registerInPalace(this.memoryPalace);
1506
+ }
1507
+ catch (palaceErr) {
1508
+ this.log.warn(`[orchestrator] Step 41 palace registration error: ${palaceErr.message}`);
1509
+ }
1510
+ }
1511
+ // Feed transferable concepts into TransferEngine
1512
+ if (this.transferEngine && result.totalConcepts > 0) {
1513
+ try {
1514
+ const transferable = this.conceptAbstraction.getTransferableConcepts(0.3);
1515
+ if (transferable.length > 0) {
1516
+ ts?.emit('concept_abstraction', 'discovering', `${transferable.length} cross-domain concepts available for transfer`, 'notable');
1517
+ }
1518
+ }
1519
+ catch { /* not critical */ }
1520
+ }
1521
+ if (this.metaCognitionLayer)
1522
+ this.metaCognitionLayer.recordStep('concept_abstraction', this.cycleCount, { insights: result.newConcepts });
1523
+ }
1524
+ catch (err) {
1525
+ this.log.warn(`[orchestrator] Step 41 error: ${err.message}`);
1526
+ }
1527
+ }
1331
1528
  const duration = Date.now() - start;
1332
1529
  ts?.emit('orchestrator', 'reflecting', `Feedback Cycle #${this.cycleCount} complete (${duration}ms)`);
1333
1530
  this.log.info(`[orchestrator] ─── Feedback Cycle #${this.cycleCount} complete (${duration}ms) ───`);