codecrucible-synth
Version:
Production-Ready AI Development Platform with Multi-Voice Synthesis, Smithery MCP Integration, Enterprise Security, and Zero-Timeout Reliability
479 lines (467 loc) ⢠18.6 kB
JavaScript
/**
* Sequential Dual-Agent Review System
* Implements automatic sequential code generation and auditing
* Agent 1 (Writer) generates code ā Agent 2 (Auditor) automatically reviews
* Created: August 21, 2025
*/
import { EventEmitter } from 'events';
import { Logger } from '../logger.js';
import { OllamaProvider } from '../../providers/ollama.js';
import { LMStudioProvider } from '../../providers/lm-studio.js';
import { modelCoordinator } from '../model-selection-coordinator.js';
import chalk from 'chalk';
export class SequentialDualAgentSystem extends EventEmitter {
logger;
config;
writerProvider = null;
auditorProvider = null;
isInitialized = false;
executionHistory = [];
constructor(config) {
super();
this.logger = new Logger('SequentialDualAgent');
// Default configuration with intelligent defaults
this.config = {
writer: {
provider: 'lm-studio', // Fast for initial generation
temperature: 0.7,
maxTokens: 4096,
...config?.writer,
},
auditor: {
provider: 'ollama', // Thorough for review
temperature: 0.2, // Lower temp for critical analysis
maxTokens: 2048,
...config?.auditor,
},
workflow: {
autoAudit: true, // Always audit automatically
applyFixes: false, // Manual approval for fixes by default
maxIterations: 3,
confidenceThreshold: 0.8,
...config?.workflow,
},
};
}
/**
* Initialize the sequential dual-agent system
*/
async initialize() {
this.logger.info('Initializing Sequential Dual-Agent System...');
try {
// Initialize writer provider
await this.initializeWriter();
// Initialize auditor provider
await this.initializeAuditor();
// Verify both agents are ready
if (!this.writerProvider || !this.auditorProvider) {
throw new Error('Failed to initialize one or both agents');
}
this.isInitialized = true;
this.logger.info('ā
Sequential Dual-Agent System ready');
console.log(chalk.green('ā
Sequential review system initialized'));
console.log(chalk.cyan(` Writer: ${this.config.writer.provider}`));
console.log(chalk.cyan(` Auditor: ${this.config.auditor.provider}`));
this.emit('system:ready');
}
catch (error) {
this.logger.error('Failed to initialize system:', error);
throw error;
}
}
/**
* Initialize the writer agent
*/
async initializeWriter() {
this.logger.info(`Initializing writer (${this.config.writer.provider})...`);
if (this.config.writer.provider === 'ollama') {
this.writerProvider = new OllamaProvider({
endpoint: 'http://localhost:11434',
timeout: 30000,
});
}
else {
this.writerProvider = new LMStudioProvider({
endpoint: 'http://localhost:1234',
timeout: 60000, // Increased timeout for code generation
});
}
// Select optimal model for writing
const selection = await modelCoordinator.selectModel(this.config.writer.provider, 'code_generation', []);
this.config.writer.model = selection.model;
this.logger.info(`Writer model selected: ${selection.model}`);
}
/**
* Initialize the auditor agent
*/
async initializeAuditor() {
this.logger.info(`Initializing auditor (${this.config.auditor.provider})...`);
if (this.config.auditor.provider === 'ollama') {
this.auditorProvider = new OllamaProvider({
endpoint: 'http://localhost:11434',
timeout: 45000, // Longer timeout for thorough review
});
}
else {
this.auditorProvider = new LMStudioProvider({
endpoint: 'http://localhost:1234',
timeout: 45000, // Longer timeout for thorough review
});
}
// Select optimal model for auditing
const selection = await modelCoordinator.selectModel(this.config.auditor.provider, 'code_review', []);
this.config.auditor.model = selection.model;
this.logger.info(`Auditor model selected: ${selection.model}`);
}
/**
* Execute the sequential dual-agent workflow
* This is the main entry point for automatic sequential review
*/
async executeSequentialReview(prompt, context) {
if (!this.isInitialized) {
await this.initialize();
}
const startTime = Date.now();
this.logger.info('Starting sequential dual-agent review...');
console.log(chalk.blue('\nš Starting Sequential Review Process'));
const result = {
originalPrompt: prompt,
writerOutput: {
code: '',
model: this.config.writer.model || 'unknown',
provider: this.config.writer.provider,
duration: 0,
timestamp: new Date(),
},
auditorOutput: {
review: this.getEmptyReview(),
model: this.config.auditor.model || 'unknown',
provider: this.config.auditor.provider,
duration: 0,
timestamp: new Date(),
},
totalDuration: 0,
accepted: false,
};
try {
// PHASE 1: Writer generates code
console.log(chalk.yellow('\nš Phase 1: Writer Agent Generating Code...'));
this.emit('phase:writer_start', { prompt });
const writerStart = Date.now();
result.writerOutput.code = await this.generateCode(prompt, context);
result.writerOutput.duration = Date.now() - writerStart;
result.writerOutput.timestamp = new Date();
this.emit('phase:writer_complete', {
code: result.writerOutput.code,
duration: result.writerOutput.duration,
});
console.log(chalk.green(`ā
Writer completed in ${(result.writerOutput.duration / 1000).toFixed(2)}s`));
// PHASE 2: Auditor automatically reviews code
if (this.config.workflow.autoAudit) {
console.log(chalk.yellow('\nš Phase 2: Auditor Agent Reviewing Code...'));
this.emit('phase:auditor_start', { code: result.writerOutput.code });
const auditorStart = Date.now();
result.auditorOutput.review = await this.auditCode(result.writerOutput.code, prompt, context);
result.auditorOutput.duration = Date.now() - auditorStart;
result.auditorOutput.timestamp = new Date();
this.emit('phase:auditor_complete', {
review: result.auditorOutput.review,
duration: result.auditorOutput.duration,
});
console.log(chalk.green(`ā
Auditor completed in ${(result.auditorOutput.duration / 1000).toFixed(2)}s`));
this.displayAuditResults(result.auditorOutput.review);
// PHASE 3: Optional refinement based on audit
if (this.config.workflow.applyFixes &&
result.auditorOutput.review.recommendation === 'refine') {
console.log(chalk.yellow('\nš§ Phase 3: Applying Refinements...'));
result.refinedOutput = await this.refineCode(result.writerOutput.code, result.auditorOutput.review, prompt);
console.log(chalk.green('ā
Refinements applied'));
}
// Determine acceptance
result.accepted =
result.auditorOutput.review.overallScore >=
this.config.workflow.confidenceThreshold * 100;
}
result.totalDuration = Date.now() - startTime;
// Store in history
this.executionHistory.push(result);
// Final summary
this.displayFinalSummary(result);
this.emit('workflow:complete', result);
return result;
}
catch (error) {
this.logger.error('Sequential review failed:', error);
this.emit('workflow:error', error);
throw error;
}
}
/**
* Generate code using the writer agent
*/
async generateCode(prompt, context) {
if (!this.writerProvider) {
throw new Error('Writer provider not initialized');
}
const enhancedPrompt = this.buildWriterPrompt(prompt, context);
const response = await this.writerProvider.processRequest({
prompt: enhancedPrompt,
temperature: this.config.writer.temperature,
maxTokens: this.config.writer.maxTokens,
});
return this.extractCode(response.content || response);
}
/**
* Audit code using the auditor agent
*/
async auditCode(code, originalPrompt, context) {
if (!this.auditorProvider) {
throw new Error('Auditor provider not initialized');
}
const auditPrompt = this.buildAuditorPrompt(code, originalPrompt, context);
const response = await this.auditorProvider.processRequest({
prompt: auditPrompt,
temperature: this.config.auditor.temperature,
maxTokens: this.config.auditor.maxTokens,
});
return this.parseAuditResponse(response.content || response);
}
/**
* Refine code based on audit feedback
*/
async refineCode(code, review, originalPrompt) {
const criticalIssues = review.issues.filter(i => i.severity === 'error' || i.severity === 'critical');
if (criticalIssues.length === 0) {
return { code, iterations: 0, finalScore: review.overallScore };
}
// Use writer to apply fixes
const fixPrompt = this.buildRefinementPrompt(code, review, originalPrompt);
const refinedCode = await this.generateCode(fixPrompt);
// Re-audit if within iteration limit
let iterations = 1;
let finalReview = review;
if (iterations < this.config.workflow.maxIterations) {
finalReview = await this.auditCode(refinedCode, originalPrompt);
iterations++;
}
return {
code: refinedCode,
iterations,
finalScore: finalReview.overallScore,
};
}
/**
* Build enhanced prompt for writer
*/
buildWriterPrompt(prompt, context) {
return `You are an expert programmer. Generate clean, efficient, production-ready code.
Request: ${prompt}
Requirements:
- Write complete, working code
- Include proper error handling
- Add clear comments for complex logic
- Follow best practices and design patterns
- Ensure code is secure and efficient
${context ? `Context: ${JSON.stringify(context)}` : ''}
Generate the code:`;
}
/**
* Build comprehensive audit prompt
*/
buildAuditorPrompt(code, originalPrompt, context) {
return `You are a senior code auditor. Review the following code critically and thoroughly.
Original Request: ${originalPrompt}
Code to Review:
\`\`\`
${code}
\`\`\`
Perform a comprehensive audit covering:
1. **Correctness**: Does it fulfill the requirements?
2. **Security**: Any vulnerabilities or risks?
3. **Performance**: Efficiency and optimization issues?
4. **Quality**: Code style, readability, maintainability?
5. **Best Practices**: Design patterns, error handling?
Provide your review in this JSON format:
{
"overallScore": 0-100,
"passed": true/false,
"issues": [
{
"severity": "critical/error/warning/info",
"type": "security/performance/logic/style",
"description": "...",
"suggestion": "...",
"autoFixable": true/false
}
],
"improvements": [
{
"category": "...",
"description": "...",
"impact": "high/medium/low"
}
],
"security": {
"score": 0-100,
"vulnerabilities": [],
"recommendations": []
},
"quality": {
"readability": 0-100,
"maintainability": 0-100,
"efficiency": 0-100,
"documentation": 0-100,
"testability": 0-100
},
"recommendation": "accept/refine/reject"
}`;
}
/**
* Build refinement prompt
*/
buildRefinementPrompt(code, review, originalPrompt) {
const issues = review.issues
.filter(i => i.severity === 'error' || i.severity === 'critical')
.map(i => `- ${i.description}: ${i.suggestion || 'Fix required'}`);
return `Refine the following code to address critical issues identified in review.
Original Request: ${originalPrompt}
Issues to Fix:
${issues.join('\n')}
Current Code:
\`\`\`
${code}
\`\`\`
Generate the improved code with all issues resolved:`;
}
/**
* Extract code from response
*/
extractCode(response) {
// Handle object responses from providers
const responseText = typeof response === 'string'
? response
: response?.content || response?.text || String(response);
if (!responseText || typeof responseText !== 'string') {
throw new Error('Invalid response format: expected string content');
}
const codeMatch = responseText.match(/```[\w]*\n?([\s\S]*?)```/);
return codeMatch ? codeMatch[1].trim() : responseText.trim();
}
/**
* Parse audit response
*/
parseAuditResponse(response) {
// Handle object responses from providers
const responseText = typeof response === 'string'
? response
: response?.content || response?.text || String(response);
try {
// Try to parse JSON from response
const jsonMatch = responseText.match(/\{[\s\S]*\}/);
if (jsonMatch) {
return JSON.parse(jsonMatch[0]);
}
}
catch (error) {
this.logger.warn('Failed to parse audit as JSON, using fallback');
}
// Fallback parsing
return this.getEmptyReview();
}
/**
* Get empty review structure
*/
getEmptyReview() {
return {
overallScore: 0,
passed: false,
issues: [],
improvements: [],
security: { score: 0, vulnerabilities: [], recommendations: [] },
quality: {
readability: 0,
maintainability: 0,
efficiency: 0,
documentation: 0,
testability: 0,
},
recommendation: 'reject',
};
}
/**
* Display audit results in console
*/
displayAuditResults(review) {
console.log(chalk.cyan('\nš Audit Results:'));
console.log(chalk.white(` Overall Score: ${review.overallScore}/100`));
console.log(chalk.white(` Status: ${review.passed ? chalk.green('PASSED') : chalk.red('FAILED')}`));
console.log(chalk.white(` Recommendation: ${review.recommendation.toUpperCase()}`));
if (review.issues.length > 0) {
console.log(chalk.yellow('\n Issues Found:'));
review.issues.forEach(issue => {
const color = issue.severity === 'critical'
? chalk.red
: issue.severity === 'error'
? chalk.magenta
: issue.severity === 'warning'
? chalk.yellow
: chalk.gray;
console.log(color(` - [${issue.severity}] ${issue.description}`));
});
}
if (review.security.vulnerabilities.length > 0) {
console.log(chalk.red('\n ā ļø Security Vulnerabilities:'));
review.security.vulnerabilities.forEach(v => {
console.log(chalk.red(` - ${v}`));
});
}
}
/**
* Display final summary
*/
displayFinalSummary(result) {
console.log(chalk.blue('\n' + '='.repeat(60)));
console.log(chalk.blue('š Sequential Review Summary'));
console.log(chalk.blue('='.repeat(60)));
console.log(chalk.white(`\nā±ļø Total Duration: ${(result.totalDuration / 1000).toFixed(2)}s`));
console.log(chalk.white(` - Writer: ${(result.writerOutput.duration / 1000).toFixed(2)}s`));
console.log(chalk.white(` - Auditor: ${(result.auditorOutput.duration / 1000).toFixed(2)}s`));
const statusColor = result.accepted ? chalk.green : chalk.yellow;
console.log(statusColor(`\nā
Final Status: ${result.accepted ? 'ACCEPTED' : 'NEEDS REVIEW'}`));
if (result.refinedOutput) {
console.log(chalk.cyan(`\nš§ Refinements Applied: ${result.refinedOutput.iterations} iteration(s)`));
console.log(chalk.cyan(` Final Score: ${result.refinedOutput.finalScore}/100`));
}
}
/**
* Get system metrics
*/
getMetrics() {
const recentResults = this.executionHistory.slice(-10);
const avgWriterTime = recentResults.reduce((sum, r) => sum + r.writerOutput.duration, 0) /
(recentResults.length || 1);
const avgAuditorTime = recentResults.reduce((sum, r) => sum + r.auditorOutput.duration, 0) /
(recentResults.length || 1);
const acceptanceRate = recentResults.filter(r => r.accepted).length / (recentResults.length || 1);
return {
totalExecutions: this.executionHistory.length,
averageWriterTime: avgWriterTime,
averageAuditorTime: avgAuditorTime,
acceptanceRate: acceptanceRate * 100,
configuration: this.config,
isInitialized: this.isInitialized,
};
}
/**
* Shutdown the system
*/
async shutdown() {
this.logger.info('Shutting down Sequential Dual-Agent System');
this.removeAllListeners();
this.executionHistory = [];
this.isInitialized = false;
}
}
// Export singleton instance for easy use
export const sequentialReviewSystem = new SequentialDualAgentSystem();
//# sourceMappingURL=sequential-dual-agent-system.js.map