@timmeck/brain-core 2.36.68 → 2.36.70

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.
@@ -114,6 +114,13 @@ export class ResearchOrchestrator {
114
114
  lastSuggestionsCycle = -1;
115
115
  /** Recent debate topic keys to prevent repeating the same debate. */
116
116
  recentDebateTopics = [];
117
+ // ── Desire Feedback Tracking ──────────────────────────────
118
+ /** Tracks outcomes per desire key: successes, failures, last result. */
119
+ desireOutcomes = new Map();
120
+ /** Category-level success rates for adaptive confidence. */
121
+ desireCategoryRates = new Map();
122
+ /** Desire keys currently being actuated by other brains (received via cross-brain). */
123
+ crossBrainActiveDesires = new Map();
117
124
  constructor(db, config, causalGraph) {
118
125
  this.db = db;
119
126
  this.brainName = config.brainName;
@@ -385,6 +392,44 @@ export class ResearchOrchestrator {
385
392
  onCrossBrainEvent(sourceBrain, eventType, data = {}) {
386
393
  this.crossDomain.recordEvent(sourceBrain, eventType, data);
387
394
  this.anomalyDetective.recordMetric(`cross:${sourceBrain}:${eventType}`, 1);
395
+ // Handle cross-brain desire coordination signals
396
+ if (eventType === 'desire_active' && data.desireKey) {
397
+ this.onCrossBrainDesireSignal(sourceBrain, data.desireKey, data.priority ?? 0);
398
+ }
399
+ // Handle cross-brain debate perspective requests
400
+ if (eventType === 'debate_perspective_request' && data.question && this.debateEngine) {
401
+ try {
402
+ const perspective = this.debateEngine.generatePerspective(data.question);
403
+ // Send perspective back via signal
404
+ if (this.signalRouter) {
405
+ this.signalRouter.emit({
406
+ targetBrain: sourceBrain,
407
+ signalType: 'debate_perspective_response',
408
+ payload: {
409
+ debateId: data.debateId,
410
+ perspective: { brainName: perspective.brainName, position: perspective.position, confidence: perspective.confidence, relevance: perspective.relevance, arguments: perspective.arguments },
411
+ },
412
+ confidence: perspective.confidence,
413
+ }).catch(() => { });
414
+ }
415
+ }
416
+ catch { /* debate engine not ready */ }
417
+ }
418
+ // Handle incoming debate perspective response — add to our debate
419
+ if (eventType === 'debate_perspective_response' && data.debateId && data.perspective && this.debateEngine) {
420
+ try {
421
+ const p = data.perspective;
422
+ this.debateEngine.addPerspective(data.debateId, {
423
+ brainName: p.brainName ?? sourceBrain,
424
+ position: p.position ?? '',
425
+ confidence: p.confidence ?? 0,
426
+ relevance: p.relevance ?? 0,
427
+ arguments: p.arguments ?? [],
428
+ });
429
+ this.log.info(`[orchestrator] Added cross-brain perspective from ${sourceBrain} to debate #${data.debateId}`);
430
+ }
431
+ catch { /* debate may not exist */ }
432
+ }
388
433
  }
389
434
  /**
390
435
  * Hook into AutonomousResearchScheduler cycle completion.
@@ -1414,25 +1459,57 @@ export class ResearchOrchestrator {
1414
1459
  this.log.error(`[orchestrator] Emergence step error: ${err.message}`);
1415
1460
  }
1416
1461
  }
1417
- // 20. Internal Debate: periodically debate key findings to synthesize cross-engine wisdom
1462
+ // 20. Internal + Cross-Brain Debate: debate key findings, solicit cross-brain perspectives
1418
1463
  if (this.debateEngine && this.cycleCount % this.reflectEvery === 0) {
1419
- ts?.emit('reflecting', 'reflecting', 'Initiating internal debate on recent findings...');
1464
+ ts?.emit('reflecting', 'reflecting', 'Initiating debate on recent findings...');
1420
1465
  try {
1421
- // Pick a debate topic from recent attention or agenda
1422
1466
  const topic = this.pickDebateTopic();
1423
1467
  if (topic) {
1424
1468
  const debate = this.debateEngine.startDebate(topic);
1469
+ // Solicit cross-brain perspectives via SignalRouter
1470
+ if (this.signalRouter) {
1471
+ const peers = ['brain', 'trading-brain', 'marketing-brain'].filter(b => b !== this.brainName);
1472
+ for (const peer of peers) {
1473
+ try {
1474
+ this.signalRouter.emit({
1475
+ targetBrain: peer,
1476
+ signalType: 'debate_perspective_request',
1477
+ payload: { debateId: debate.id, question: topic },
1478
+ confidence: 0.7,
1479
+ }).catch(() => { });
1480
+ }
1481
+ catch { /* peer offline */ }
1482
+ }
1483
+ }
1425
1484
  const synthesis = this.debateEngine.synthesize(debate.id);
1426
- if (synthesis && synthesis.conflicts.length > 0) {
1427
- this.journal.write({
1428
- type: 'discovery',
1429
- title: `Debate: ${topic.substring(0, 80)}`,
1430
- content: `Internal debate with ${synthesis.participantCount} perspective(s). ${synthesis.conflicts.length} conflict(s). Resolution: ${synthesis.resolution}`,
1431
- tags: [this.brainName, 'debate', 'synthesis'],
1432
- references: [],
1433
- significance: synthesis.conflicts.length > 2 ? 'notable' : 'routine',
1434
- data: { debate: { question: topic, synthesis } },
1435
- });
1485
+ if (synthesis) {
1486
+ // Journal the debate
1487
+ if (synthesis.conflicts.length > 0) {
1488
+ this.journal.write({
1489
+ type: 'discovery',
1490
+ title: `Debate: ${topic.substring(0, 80)}`,
1491
+ content: `Debate with ${synthesis.participantCount} perspective(s). ${synthesis.conflicts.length} conflict(s). Resolution: ${synthesis.resolution}`,
1492
+ tags: [this.brainName, 'debate', 'synthesis'],
1493
+ references: [],
1494
+ significance: synthesis.conflicts.length > 2 ? 'notable' : 'routine',
1495
+ data: { debate: { question: topic, synthesis } },
1496
+ });
1497
+ }
1498
+ // Convert recommendations to ActionBridge proposals
1499
+ if (this.actionBridge && synthesis.recommendations.length > 0) {
1500
+ for (const rec of synthesis.recommendations.slice(0, 2)) {
1501
+ this.actionBridge.propose({
1502
+ source: 'research',
1503
+ type: 'create_goal',
1504
+ title: `Debate recommendation: ${rec.substring(0, 70)}`,
1505
+ description: `From debate "${topic.substring(0, 80)}": ${rec}`,
1506
+ confidence: synthesis.confidence,
1507
+ payload: { debateId: debate.id, recommendation: rec },
1508
+ });
1509
+ }
1510
+ }
1511
+ // Auto-close the debate after synthesis
1512
+ this.debateEngine.closeDebate(debate.id);
1436
1513
  }
1437
1514
  ts?.emit('reflecting', 'reflecting', `Debate on "${topic.substring(0, 40)}...": ${synthesis?.conflicts.length ?? 0} conflicts, confidence=${((synthesis?.confidence ?? 0) * 100).toFixed(0)}%`, synthesis && synthesis.conflicts.length > 0 ? 'notable' : 'routine');
1438
1515
  }
@@ -1586,14 +1663,16 @@ export class ResearchOrchestrator {
1586
1663
  this.log.warn(`[orchestrator] Step 24 error: ${err.message}`);
1587
1664
  }
1588
1665
  }
1589
- // Step 25: Advocatus Diaboli — challenge a random confirmed principle (every 10 cycles)
1666
+ // Step 25: Advocatus Diaboli — challenge weakest principle + adjust confidence (every 10 cycles)
1590
1667
  if (this.debateEngine && this.cycleCount % 10 === 0) {
1591
1668
  try {
1592
1669
  ts?.emit('orchestrator', 'reflecting', 'Step 25: Challenging a principle...', 'routine');
1593
1670
  const principles = this.knowledgeDistiller.getPrinciples(undefined, 20);
1594
1671
  if (principles.length > 0) {
1595
- const randomPrinciple = principles[Math.floor(Math.random() * principles.length)];
1596
- const challenge = this.debateEngine.challenge(randomPrinciple.statement);
1672
+ // Target weakest principles first (lowest confidence), not random
1673
+ const sorted = [...principles].sort((a, b) => a.confidence - b.confidence);
1674
+ const targetPrinciple = sorted[0];
1675
+ const challenge = this.debateEngine.challenge(targetPrinciple.statement);
1597
1676
  this.journal.write({
1598
1677
  type: 'reflection',
1599
1678
  title: `Principle challenged: resilience=${(challenge.resilienceScore * 100).toFixed(0)}% → ${challenge.outcome}`,
@@ -1603,6 +1682,24 @@ export class ResearchOrchestrator {
1603
1682
  significance: challenge.outcome === 'disproved' ? 'notable' : 'routine',
1604
1683
  data: { challenge },
1605
1684
  });
1685
+ // Actually adjust principle confidence based on challenge outcome
1686
+ if (challenge.principleId !== null) {
1687
+ const principleIdStr = String(challenge.principleId);
1688
+ if (challenge.outcome === 'disproved') {
1689
+ // Disproved → remove principle entirely
1690
+ this.knowledgeDistiller.removePrinciple(principleIdStr);
1691
+ this.log.info(`[orchestrator] Step 25: Principle "${targetPrinciple.statement.substring(0, 50)}" REMOVED (disproved)`);
1692
+ }
1693
+ else if (challenge.outcome === 'weakened') {
1694
+ // Weakened → reduce confidence by 30%
1695
+ this.knowledgeDistiller.adjustPrincipleConfidence(principleIdStr, 0.7);
1696
+ this.log.info(`[orchestrator] Step 25: Principle confidence reduced by 30% (weakened)`);
1697
+ }
1698
+ else if (challenge.outcome === 'survived') {
1699
+ // Survived → slight boost (+10%)
1700
+ this.knowledgeDistiller.adjustPrincipleConfidence(principleIdStr, 1.1);
1701
+ }
1702
+ }
1606
1703
  }
1607
1704
  if (this.metaCognitionLayer)
1608
1705
  this.metaCognitionLayer.recordStep('advocatus_diaboli', this.cycleCount, { insights: 1 });
@@ -2793,12 +2890,42 @@ export class ResearchOrchestrator {
2793
2890
  }
2794
2891
  }
2795
2892
  // Step 64: DesireActuator — convert top desire to action (every 15 cycles)
2893
+ // Fix 1: Feedback-loop — deprioritize failed desires, boost successful ones
2894
+ // Fix 2: Cross-brain coordination — skip desires already active in other brains
2895
+ // Fix 3: Adaptive confidence — use category success rate instead of static priority/10
2796
2896
  if (this.actionBridge && this.cycleCount - this.lastDesireActuationCycle >= 15) {
2797
2897
  try {
2798
2898
  const desires = this.getDesires();
2799
- const topDesire = desires.find(d => d.priority >= 5);
2899
+ // Apply feedback adjustments to priorities
2900
+ const adjusted = desires.map(d => {
2901
+ const outcome = this.desireOutcomes.get(d.key);
2902
+ let adjustedPriority = d.priority;
2903
+ if (outcome) {
2904
+ // Deprioritize desires that keep failing (−2 per failure, +1 per success, floor 0)
2905
+ adjustedPriority = Math.max(0, d.priority + outcome.successes - outcome.failures * 2);
2906
+ // If failed 3+ times consecutively, hard suppress below threshold
2907
+ if (outcome.failures >= 3 && outcome.lastResult === 'failure') {
2908
+ adjustedPriority = Math.min(adjustedPriority, 2);
2909
+ this.log.info(`[orchestrator] Step 64: Desire "${d.key}" suppressed (${outcome.failures} consecutive failures)`);
2910
+ }
2911
+ }
2912
+ return { ...d, adjustedPriority };
2913
+ });
2914
+ // Re-sort by adjusted priority
2915
+ adjusted.sort((a, b) => b.adjustedPriority - a.adjustedPriority);
2916
+ // Find top desire that passes threshold AND isn't already active in another brain
2917
+ const topDesire = adjusted.find(d => {
2918
+ if (d.adjustedPriority < 5)
2919
+ return false;
2920
+ const crossActive = this.crossBrainActiveDesires.get(d.key);
2921
+ if (crossActive && this.cycleCount - crossActive.cycle < 30) {
2922
+ this.log.info(`[orchestrator] Step 64: Skipping "${d.key}" — active in ${crossActive.brain}`);
2923
+ return false;
2924
+ }
2925
+ return true;
2926
+ });
2800
2927
  if (topDesire) {
2801
- ts?.emit('desire', 'analyzing', `Step 64: Actuating desire "${topDesire.key}" (P${topDesire.priority})...`, 'notable');
2928
+ ts?.emit('desire', 'analyzing', `Step 64: Actuating desire "${topDesire.key}" (P${topDesire.adjustedPriority})...`, 'notable');
2802
2929
  // Map desire key → action type
2803
2930
  let actionType = 'create_goal';
2804
2931
  if (topDesire.key.startsWith('contradiction_')) {
@@ -2807,25 +2934,50 @@ export class ResearchOrchestrator {
2807
2934
  else if (topDesire.key.startsWith('no_predictions') || topDesire.key.startsWith('low_accuracy')) {
2808
2935
  actionType = 'adjust_parameter';
2809
2936
  }
2937
+ // Adaptive confidence: use category success rate if available
2938
+ const category = this.desireKeyToCategory(topDesire.key);
2939
+ const categoryRate = this.desireCategoryRates.get(category);
2940
+ let confidence;
2941
+ if (categoryRate && categoryRate.total >= 3) {
2942
+ // Blend: 60% category success rate + 40% priority-based
2943
+ const rateComponent = categoryRate.successes / categoryRate.total;
2944
+ const priorityComponent = Math.min(topDesire.adjustedPriority / 10, 0.9);
2945
+ confidence = Math.min(rateComponent * 0.6 + priorityComponent * 0.4, 0.9);
2946
+ }
2947
+ else {
2948
+ confidence = Math.min(topDesire.adjustedPriority / 10, 0.9);
2949
+ }
2810
2950
  const actionId = this.actionBridge.propose({
2811
2951
  source: 'desire',
2812
2952
  type: actionType,
2813
2953
  title: `Desire: ${topDesire.suggestion.substring(0, 80)}`,
2814
2954
  description: topDesire.suggestion,
2815
- confidence: Math.min(topDesire.priority / 10, 0.9),
2816
- payload: { desireKey: topDesire.key, priority: topDesire.priority, alternatives: topDesire.alternatives },
2955
+ confidence,
2956
+ payload: { desireKey: topDesire.key, priority: topDesire.adjustedPriority, category, alternatives: topDesire.alternatives },
2817
2957
  });
2818
2958
  if (actionId > 0) {
2819
- this.log.info(`[orchestrator] Step 64: Desire "${topDesire.key}" → Action #${actionId} (${actionType})`);
2959
+ this.log.info(`[orchestrator] Step 64: Desire "${topDesire.key}" → Action #${actionId} (${actionType}, conf=${confidence.toFixed(2)})`);
2820
2960
  this.journal.write({
2821
2961
  title: `Desire Actuation: ${topDesire.key}`,
2822
- content: `Converted desire (P${topDesire.priority}) to ${actionType} action #${actionId}: ${topDesire.suggestion}`,
2962
+ content: `Converted desire (P${topDesire.adjustedPriority}, conf=${confidence.toFixed(2)}) to ${actionType} action #${actionId}: ${topDesire.suggestion}`,
2823
2963
  type: 'insight',
2824
2964
  significance: 'notable',
2825
2965
  tags: [this.brainName, 'desire', 'actuation'],
2826
2966
  references: [],
2827
- data: { desireKey: topDesire.key, actionId, actionType },
2967
+ data: { desireKey: topDesire.key, actionId, actionType, adjustedPriority: topDesire.adjustedPriority, confidence },
2828
2968
  });
2969
+ // Broadcast to other brains that we're working on this desire
2970
+ if (this.signalRouter) {
2971
+ const peers = ['brain', 'trading-brain', 'marketing-brain'].filter(b => b !== this.brainName);
2972
+ for (const peer of peers) {
2973
+ this.signalRouter.emit({
2974
+ targetBrain: peer,
2975
+ signalType: 'desire_active',
2976
+ payload: { desireKey: topDesire.key, priority: topDesire.adjustedPriority },
2977
+ confidence,
2978
+ }).catch(() => { });
2979
+ }
2980
+ }
2829
2981
  }
2830
2982
  this.lastDesireActuationCycle = this.cycleCount;
2831
2983
  }
@@ -2835,6 +2987,7 @@ export class ResearchOrchestrator {
2835
2987
  }
2836
2988
  }
2837
2989
  // Step 65: Action-Outcome Review (every 20 cycles)
2990
+ // Enhanced: Feed desire outcomes back into desireOutcomes + desireCategoryRates
2838
2991
  if (this.actionBridge && this.cycleCount % 20 === 0) {
2839
2992
  try {
2840
2993
  const history = this.actionBridge.getHistory(10);
@@ -2842,7 +2995,7 @@ export class ResearchOrchestrator {
2842
2995
  const recentFailed = history.filter(a => a.status === 'failed' || (a.outcome && !a.outcome.success));
2843
2996
  if (recentCompleted.length > 0 || recentFailed.length > 0) {
2844
2997
  ts?.emit('orchestrator', 'reflecting', `Step 65: Reviewing ${recentCompleted.length} successes, ${recentFailed.length} failures`, 'routine');
2845
- // Success → journal lesson learned
2998
+ // Success → journal lesson learned + desire feedback
2846
2999
  for (const action of recentCompleted.slice(0, 3)) {
2847
3000
  const lesson = action.outcome?.learnedLesson ?? `Action "${action.title}" succeeded`;
2848
3001
  this.journal.write({
@@ -2854,8 +3007,19 @@ export class ResearchOrchestrator {
2854
3007
  references: [],
2855
3008
  data: { actionId: action.id, type: action.type, source: action.source },
2856
3009
  });
3010
+ // Feed success back into desire tracking
3011
+ if (action.source === 'desire') {
3012
+ const desireKey = action.payload.desireKey;
3013
+ const category = action.payload.category;
3014
+ if (desireKey) {
3015
+ this.recordDesireOutcome(desireKey, 'success');
3016
+ }
3017
+ if (category) {
3018
+ this.recordDesireCategoryOutcome(category, true);
3019
+ }
3020
+ }
2857
3021
  }
2858
- // Failure → journal + hypothesis rejection
3022
+ // Failure → journal + desire feedback
2859
3023
  for (const action of recentFailed.slice(0, 3)) {
2860
3024
  this.journal.write({
2861
3025
  title: `Action Failed: ${action.title}`,
@@ -2866,6 +3030,17 @@ export class ResearchOrchestrator {
2866
3030
  references: [],
2867
3031
  data: { actionId: action.id, type: action.type, source: action.source },
2868
3032
  });
3033
+ // Feed failure back into desire tracking
3034
+ if (action.source === 'desire') {
3035
+ const desireKey = action.payload.desireKey;
3036
+ const category = action.payload.category;
3037
+ if (desireKey) {
3038
+ this.recordDesireOutcome(desireKey, 'failure');
3039
+ }
3040
+ if (category) {
3041
+ this.recordDesireCategoryOutcome(category, false);
3042
+ }
3043
+ }
2869
3044
  }
2870
3045
  if (this.metaCognitionLayer) {
2871
3046
  this.metaCognitionLayer.recordStep('action_outcome_review', this.cycleCount, {
@@ -3371,6 +3546,69 @@ export class ResearchOrchestrator {
3371
3546
  }
3372
3547
  return result;
3373
3548
  }
3549
+ // ── Desire Feedback Helpers ──────────────────────────────
3550
+ /** Record a desire outcome (success or failure) for feedback-loop. */
3551
+ recordDesireOutcome(desireKey, result) {
3552
+ const existing = this.desireOutcomes.get(desireKey) ?? { successes: 0, failures: 0, lastResult: result, lastCycle: 0 };
3553
+ if (result === 'success') {
3554
+ existing.successes++;
3555
+ }
3556
+ else {
3557
+ existing.failures++;
3558
+ }
3559
+ existing.lastResult = result;
3560
+ existing.lastCycle = this.cycleCount;
3561
+ this.desireOutcomes.set(desireKey, existing);
3562
+ this.log.info(`[orchestrator] Desire feedback: "${desireKey}" → ${result} (${existing.successes}S/${existing.failures}F)`);
3563
+ }
3564
+ /** Record category-level outcome for adaptive confidence. */
3565
+ recordDesireCategoryOutcome(category, success) {
3566
+ const existing = this.desireCategoryRates.get(category) ?? { successes: 0, total: 0 };
3567
+ existing.total++;
3568
+ if (success)
3569
+ existing.successes++;
3570
+ this.desireCategoryRates.set(category, existing);
3571
+ }
3572
+ /** Map a desire key to a broad category for confidence tracking. */
3573
+ desireKeyToCategory(key) {
3574
+ if (key.startsWith('no_predictions') || key.startsWith('low_accuracy'))
3575
+ return 'prediction';
3576
+ if (key.startsWith('contradiction_'))
3577
+ return 'contradiction';
3578
+ if (key.startsWith('curiosity_gap_'))
3579
+ return 'curiosity';
3580
+ if (key.startsWith('no_knowledge'))
3581
+ return 'knowledge';
3582
+ if (key.startsWith('pending_transfers') || key.startsWith('want_cross_brain'))
3583
+ return 'cross_brain';
3584
+ if (key.startsWith('deep_dive_'))
3585
+ return 'deep_dive';
3586
+ return 'general';
3587
+ }
3588
+ /** Handle incoming cross-brain desire signal. */
3589
+ onCrossBrainDesireSignal(brain, desireKey, priority) {
3590
+ this.crossBrainActiveDesires.set(desireKey, { brain, priority, cycle: this.cycleCount });
3591
+ this.log.info(`[orchestrator] Cross-brain desire received: "${desireKey}" from ${brain} (P${priority})`);
3592
+ // Clean up stale entries (older than 60 cycles)
3593
+ for (const [key, entry] of this.crossBrainActiveDesires) {
3594
+ if (this.cycleCount - entry.cycle > 60) {
3595
+ this.crossBrainActiveDesires.delete(key);
3596
+ }
3597
+ }
3598
+ }
3599
+ /** Get desire feedback stats for monitoring. */
3600
+ getDesireFeedbackStats() {
3601
+ const outcomes = [...this.desireOutcomes.entries()].map(([key, v]) => ({
3602
+ key, successes: v.successes, failures: v.failures, lastResult: v.lastResult,
3603
+ }));
3604
+ const categoryRates = [...this.desireCategoryRates.entries()].map(([category, v]) => ({
3605
+ category, successRate: v.total > 0 ? v.successes / v.total : 0, total: v.total,
3606
+ }));
3607
+ const crossBrainActive = [...this.crossBrainActiveDesires.entries()].map(([key, v]) => ({
3608
+ key, brain: v.brain, priority: v.priority,
3609
+ }));
3610
+ return { outcomes, categoryRates, crossBrainActive };
3611
+ }
3374
3612
  /** Get structured self-improvement desires with priority and alternatives. */
3375
3613
  getDesires() {
3376
3614
  const raw = [];