@cyqlelabs/mcp-dual-cycle-reasoner
Version:
MCP server implementing dual-cycle metacognitive reasoning framework for autonomous agents
282 lines (245 loc) ⢠9.59 kB
text/typescript
import { Sentinel } from './sentinel.js';
import { Adjudicator } from './adjudicator.js';
import {
CognitiveTrace,
LoopDetectionResult,
DiagnosisResult,
RecoveryPlan,
BeliefRevisionResult,
Case,
SentinelConfig
} from './types.js';
import { v4 as uuidv4 } from 'uuid';
import chalk from 'chalk';
/**
* The Dual-Cycle Engine implements the metacognitive framework described in the DUAL-CYCLE document.
* It consists of two interconnected cycles:
* - Cognitive Cycle (The "Doer"): Direct interaction with the environment
* - Metacognitive Cycle (The "Thinker"): Monitors and controls the cognitive cycle
*/
export class DualCycleEngine {
private sentinel: Sentinel;
private adjudicator: Adjudicator;
private currentTrace: CognitiveTrace;
private isMonitoring: boolean = false;
private interventionCount: number = 0;
constructor(config?: Partial<SentinelConfig>) {
this.sentinel = new Sentinel(config);
this.adjudicator = new Adjudicator();
this.currentTrace = this.initializeTrace();
}
/**
* Initialize a new cognitive trace for monitoring
*/
private initializeTrace(): CognitiveTrace {
return {
recent_actions: [],
current_context: undefined,
goal: '',
step_count: 0
};
}
/**
* Start metacognitive monitoring of an agent's cognitive trace
*/
startMonitoring(initialGoal: string, initialBeliefs: string[] = []): void {
this.isMonitoring = true;
this.currentTrace = this.initializeTrace();
this.currentTrace.goal = initialGoal;
this.interventionCount = 0;
console.log(chalk.blue('š§ Dual-Cycle Engine: Metacognitive monitoring started'));
console.log(chalk.gray(`Goal: ${initialGoal}`));
console.log(chalk.gray(`Initial beliefs: ${initialBeliefs.length}`));
}
/**
* Stop metacognitive monitoring
*/
stopMonitoring(): void {
this.isMonitoring = false;
console.log(chalk.blue('š§ Dual-Cycle Engine: Monitoring stopped'));
console.log(chalk.gray(`Total interventions: ${this.interventionCount}`));
}
/**
* Process a new cognitive trace update (called by the cognitive cycle)
*/
async processTraceUpdate(trace: CognitiveTrace): Promise<{
intervention_required: boolean;
loop_detected?: LoopDetectionResult;
diagnosis?: DiagnosisResult;
recovery_plan?: RecoveryPlan;
revised_beliefs?: BeliefRevisionResult;
explanation?: string;
}> {
if (!this.isMonitoring) {
return { intervention_required: false };
}
// Update current trace
this.currentTrace = { ...this.currentTrace, ...trace };
console.log(chalk.gray(`\nš Processing trace update: ${trace.recent_actions.length} actions, step ${trace.step_count}`));
// METACOGNITIVE CYCLE - Phase 1: MONITOR
const loopDetection = this.monitorForLoops(trace);
if (!loopDetection.detected) {
console.log(chalk.green('ā
No loops detected - cognitive cycle proceeding normally'));
return {
intervention_required: false,
loop_detected: loopDetection
};
}
console.log(chalk.yellow(`ā ļø Loop detected: ${loopDetection.type} (confidence: ${(loopDetection.confidence * 100).toFixed(1)}%)`));
console.log(chalk.yellow(` Details: ${loopDetection.details}`));
// METACOGNITIVE CYCLE - Phase 2: INTERPRET/DETECT
const diagnosis = await this.interpretFailure(loopDetection, trace);
console.log(chalk.red(`š Diagnosis: ${diagnosis.primary_hypothesis} (confidence: ${(diagnosis.confidence * 100).toFixed(1)}%)`));
console.log(chalk.red(` Evidence: ${diagnosis.evidence.join('; ')}`));
// METACOGNITIVE CYCLE - Phase 3: PLAN (Meta-Level)
const recoveryPlan = this.planRecovery(diagnosis, trace);
console.log(chalk.blue(`š ļø Recovery plan: ${recoveryPlan.pattern}`));
console.log(chalk.blue(` Rationale: ${recoveryPlan.rationale}`));
// METACOGNITIVE CYCLE - Phase 4: CONTROL (Meta-Level)
const beliefRevision = await this.controlCognition(loopDetection, diagnosis, trace);
console.log(chalk.magenta(`š§ Beliefs revised: ${beliefRevision.revised_beliefs.length} beliefs, ${beliefRevision.removed_beliefs.length} removed`));
// Store this experience for future learning
this.storeExperience(loopDetection, diagnosis, recoveryPlan, trace);
this.interventionCount++;
const explanation = this.generateInterventionExplanation(
loopDetection,
diagnosis,
recoveryPlan,
beliefRevision
);
console.log(chalk.cyan(`\nš” Intervention #${this.interventionCount}: ${explanation}`));
return {
intervention_required: true,
loop_detected: loopDetection,
diagnosis,
recovery_plan: recoveryPlan,
revised_beliefs: beliefRevision,
explanation
};
}
/**
* METACOGNITIVE CYCLE - Phase 1: MONITOR
* Uses the Sentinel to detect problematic patterns
*/
private monitorForLoops(trace: CognitiveTrace): LoopDetectionResult {
return this.sentinel.detectLoop(trace, 'hybrid');
}
/**
* METACOGNITIVE CYCLE - Phase 2: INTERPRET/DETECT
* Uses the Adjudicator to diagnose the failure
*/
private async interpretFailure(loopResult: LoopDetectionResult, trace: CognitiveTrace): Promise<DiagnosisResult> {
return await this.adjudicator.diagnoseFailure(loopResult, trace);
}
/**
* METACOGNITIVE CYCLE - Phase 3: PLAN (Meta-Level)
* Uses the Adjudicator to generate a recovery plan
*/
private planRecovery(diagnosis: DiagnosisResult, trace: CognitiveTrace): RecoveryPlan {
return this.adjudicator.generateRecoveryPlan(diagnosis, trace);
}
/**
* METACOGNITIVE CYCLE - Phase 4: CONTROL (Meta-Level)
* Revises beliefs and prepares cognitive control signals
*/
private async controlCognition(
loopResult: LoopDetectionResult,
diagnosis: DiagnosisResult,
trace: CognitiveTrace
): Promise<BeliefRevisionResult> {
const contradictingEvidence = `Loop detected: ${loopResult.type}. Diagnosis: ${diagnosis.primary_hypothesis}. Current strategy is ineffective.`;
// For simplified traces, we'll use empty beliefs array as default
const currentBeliefs: string[] = [];
return await this.adjudicator.reviseBeliefs(
currentBeliefs,
contradictingEvidence,
trace
);
}
/**
* Store the experience for case-based reasoning
*/
private storeExperience(
loopResult: LoopDetectionResult,
diagnosis: DiagnosisResult,
recoveryPlan: RecoveryPlan,
trace: CognitiveTrace
): void {
const experience: Case = {
id: uuidv4(),
problem_description: `${loopResult.type} loop detected: ${diagnosis.primary_hypothesis} in context: ${this.extractContextSummary(trace)}`,
solution: `Apply ${recoveryPlan.pattern} strategy: ${recoveryPlan.rationale}`,
outcome: false, // Will be updated when outcome is known
timestamp: Date.now()
};
this.adjudicator.storeExperience(experience);
}
/**
* Generate a human-readable explanation of the intervention
*/
private generateInterventionExplanation(
loopResult: LoopDetectionResult,
diagnosis: DiagnosisResult,
recoveryPlan: RecoveryPlan,
beliefRevision: BeliefRevisionResult
): string {
const loopType = loopResult.type?.replace('_', ' ') || 'unknown';
const hypothesis = diagnosis.primary_hypothesis.replace('_', ' ');
const pattern = recoveryPlan.pattern.replace('_', ' ');
return `Detected ${loopType} loop (${(loopResult.confidence * 100).toFixed(0)}% confidence). ` +
`Diagnosed as ${hypothesis} issue. ` +
`Applying ${pattern} recovery strategy. ` +
`Revised ${beliefRevision.revised_beliefs.length} beliefs to maintain consistency.`;
}
/**
* Extract a summary of the current context for case storage
*/
private extractContextSummary(trace: CognitiveTrace): string {
const recentActions = trace.recent_actions.slice(-3).join(' -> ');
const currentContext = trace.current_context || 'unknown';
return `Goal: ${trace.goal.substring(0, 50)}..., ` +
`Recent actions: ${recentActions}, ` +
`Context: ${currentContext}, ` +
`Step: ${trace.step_count}`;
}
/**
* Get current monitoring status and statistics
*/
getMonitoringStatus(): {
is_monitoring: boolean;
intervention_count: number;
current_goal: string;
trace_length: number;
} {
return {
is_monitoring: this.isMonitoring,
intervention_count: this.interventionCount,
current_goal: this.currentTrace.goal,
trace_length: this.currentTrace.recent_actions.length
};
}
/**
* Update the outcome of a previously generated recovery plan
*/
updateRecoveryOutcome(successful: boolean, explanation: string): void {
// In a full implementation, this would update the most recent case
// For now, we'll just log it
console.log(chalk.cyan(`š Recovery outcome updated: ${successful ? 'SUCCESS' : 'FAILURE'} - ${explanation}`));
}
/**
* Reset the engine state (useful for testing or new sessions)
*/
reset(): void {
this.sentinel.reset();
this.currentTrace = this.initializeTrace();
this.isMonitoring = false;
this.interventionCount = 0;
console.log(chalk.blue('š Dual-Cycle Engine reset'));
}
/**
* Get similar cases for analysis
*/
getSimilarCases(problemDescription: string, maxResults: number = 5): Case[] {
return this.adjudicator.retrieveSimilarCases(problemDescription, maxResults);
}
}