Files
SuperCharged-Claude-Code-Up…/scripts/auto-fix.js
uroma 55aafbae9a Fix project isolation: Make loadChatHistory respect active project sessions
- Modified loadChatHistory() to check for active project before fetching all sessions
- When active project exists, use project.sessions instead of fetching from API
- Added detailed console logging to debug session filtering
- This prevents ALL sessions from appearing in every project's sidebar

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-22 14:43:05 +00:00

312 lines
9.4 KiB
JavaScript

/**
* Auto-Fix Agent for Agentic Chat
*
* Automatically diagnoses and fixes common issues with agentic chat.
* Triggered by ChatMonitor when failures are detected.
*/
const fs = require('fs');
const path = require('path');
const { spawn, exec } = require('child_process');
class AutoFixAgent {
constructor(failureFile) {
this.failureFile = failureFile;
this.failure = this.loadFailure();
this.logsDir = path.join(__dirname, '../logs/chat-monitor');
}
loadFailure() {
const content = fs.readFileSync(this.failureFile, 'utf-8');
return JSON.parse(content);
}
async run() {
console.log(`[AutoFix] 🔧 Starting auto-fix for session ${this.failure.sessionId}`);
console.log(`[AutoFix] Failure type: ${this.failure.failureType}`);
const diagnosis = await this.diagnose();
console.log(`[AutoFix] Diagnosis:`, diagnosis);
const fix = await this.applyFix(diagnosis);
console.log(`[AutoFix] Fix applied:`, fix);
const verification = await this.verify(fix);
console.log(`[AutoFix] Verification:`, verification);
// Write results
this.writeResults({ diagnosis, fix, verification });
}
async diagnose() {
const { sessionId, failureType, events } = this.failure;
const diagnosis = {
sessionId,
failureType,
rootCause: null,
fixes: []
};
// Check each potential issue
diagnosis.checks = {
sessionExists: await this.checkSessionExists(sessionId),
serverRunning: await this.checkServerRunning(),
claudeWorking: await this.checkClaudeCLI(),
sseWorking: await this.checkSSE(),
frontendConnected: await this.checkFrontendConnection(sessionId)
};
// Determine root cause
if (!diagnosis.checks.serverRunning) {
diagnosis.rootCause = 'server_not_running';
diagnosis.fixes.push('restart_server');
} else if (!diagnosis.checks.sessionExists) {
diagnosis.rootCause = 'session_not_found';
diagnosis.fixes.push('create_session');
} else if (!diagnosis.checks.claudeWorking) {
diagnosis.rootCause = 'claude_cli_failed';
diagnosis.fixes.push('check_claude_installation');
} else if (!diagnosis.checks.sseWorking) {
diagnosis.rootCause = 'sse_broken';
diagnosis.fixes.push('fix_sse_routes');
} else if (!diagnosis.checks.frontendConnected) {
diagnosis.rootCause = 'frontend_not_receiving';
diagnosis.fixes.push('check_frontend_sse_client');
}
// Event-based diagnosis
if (events) {
const spawnError = events.find(e => e.eventType === 'claude_spawn_error');
if (spawnError) {
diagnosis.rootCause = 'claude_spawn_failed';
diagnosis.fixes.push('check_claude_command');
}
const parseError = events.find(e => e.eventType === 'json_parse_error');
if (parseError) {
diagnosis.rootCause = 'json_parse_failed';
diagnosis.fixes.push('fix_json_parsing');
}
}
return diagnosis;
}
async checkSessionExists(sessionId) {
return new Promise((resolve) => {
exec(`curl -s http://localhost:3010/claude/api/session/${sessionId}/status`, (error, stdout) => {
if (error) {
resolve(false);
} else {
try {
const data = JSON.parse(stdout);
resolve(data.sessionId === sessionId);
} catch {
resolve(false);
}
}
});
});
}
async checkServerRunning() {
return new Promise((resolve) => {
exec('pgrep -f "node.*server.js"', (error) => {
resolve(!error);
});
});
}
async checkClaudeCLI() {
return new Promise((resolve) => {
exec('which claude', (error) => {
resolve(!error);
});
});
}
async checkSSE() {
return new Promise((resolve) => {
exec('curl -s http://localhost:3010/claude/api/session/test-session/events', (error) => {
// SSE endpoints return 200 even if session doesn't exist
resolve(!error || error.code !== 'ECONNREFUSED');
});
});
}
async checkFrontendConnection(sessionId) {
// Check if frontend has received SSE events
const logFile = path.join(this.logsDir, `${new Date().toISOString().split('T')[0]}-${sessionId}.log`);
if (!fs.existsSync(logFile)) {
return false;
}
const content = fs.readFileSync(logFile, 'utf-8');
return content.includes('ai_response');
}
async applyFix(diagnosis) {
const fixResults = [];
for (const fix of diagnosis.fixes) {
console.log(`[AutoFix] Applying fix: ${fix}`);
const result = await this.applySingleFix(fix, diagnosis);
fixResults.push({ fix, result });
}
return { fixes: fixResults };
}
async applySingleFix(fix, diagnosis) {
switch (fix) {
case 'restart_server':
return this.restartServer();
case 'create_session':
return this.createSession(diagnosis.sessionId);
case 'check_claude_installation':
return this.checkClaudeInstallation();
case 'fix_sse_routes':
return this.fixSSERoutes();
case 'check_frontend_sse_client':
return this.checkFrontendSSEClient();
case 'fix_json_parsing':
return this.fixJSONParsing();
default:
return { success: false, message: `Unknown fix: ${fix}` };
}
}
async restartServer() {
return new Promise((resolve) => {
exec('pkill -f "node.*server.js" && sleep 2 && cd /home/uroma/obsidian-web-interface && node server.js > /tmp/server.log 2>&1 &',
(error, stdout, stderr) => {
if (error) {
resolve({ success: false, error: error.message });
} else {
setTimeout(() => {
resolve({ success: true, message: 'Server restarted' });
}, 3000);
}
}
);
});
}
async createSession(sessionId) {
// Can't create a session with a specific ID, need to create new one
return { success: false, message: 'Cannot create session with specific ID. User should create new session.' };
}
async checkClaudeInstallation() {
return new Promise((resolve) => {
exec('claude --version', (error, stdout) => {
if (error) {
resolve({ success: false, error: 'Claude CLI not found', fix: 'Install Claude CLI' });
} else {
resolve({ success: true, version: stdout.trim() });
}
});
});
}
async fixSSERoutes() {
// Check if sessions-routes.js exists
const routesPath = path.join(__dirname, '../routes/sessions-routes.js');
if (!fs.existsSync(routesPath)) {
return { success: false, error: 'sessions-routes.js missing', needsManualFix: true };
}
return { success: true, message: 'SSE routes present' };
}
async checkFrontendSSEClient() {
// Check sse-client.js for the event routing fix
const sseClientPath = path.join(__dirname, '../public/claude-ide/sse-client.js');
if (!fs.existsSync(sseClientPath)) {
return { success: false, error: 'sse-client.js missing' };
}
const content = fs.readFileSync(sseClientPath, 'utf-8');
const hasFix = content.includes('_eventType') && content.includes('routeEvent');
if (!hasFix) {
return {
success: false,
error: 'SSE client missing event routing fix',
fix: 'Apply _eventType fix to sse-client.js line 95'
};
}
return { success: true, message: 'SSE client has routing fix' };
}
async fixJSONParsing() {
// Check if claude-service.js has JSON parsing fix
const servicePath = path.join(__dirname, '../services/claude-service.js');
if (!fs.existsSync(servicePath)) {
return { success: false, error: 'claude-service.js missing' };
}
const content = fs.readFileSync(servicePath, 'utf-8');
const hasJSONFix = content.includes('--output-format') && content.includes('jsonOutput');
if (!hasJSONFix) {
return {
success: false,
error: 'claude-service.js missing JSON output format fix',
fix: 'Add --output-format json flag and JSON parsing'
};
}
return { success: true, message: 'JSON parsing fix present' };
}
async verify(fix) {
// Wait a moment then check if server is responding
await new Promise(r => setTimeout(r, 2000));
const checks = {
serverRunning: await this.checkServerRunning(),
portOpen: await this.checkPortOpen()
};
return {
success: checks.serverRunning && checks.portOpen,
checks
};
}
async checkPortOpen() {
return new Promise((resolve) => {
exec('curl -s http://localhost:3010/health > /dev/null', (error) => {
resolve(!error);
});
});
}
writeResults(results) {
const resultsFile = path.join(this.logsDir, `fix-result-${Date.now()}.json`);
fs.writeFileSync(resultsFile, JSON.stringify(results, null, 2));
console.log(`[AutoFix] Results written to ${resultsFile}`);
}
}
// Main execution
if (require.main === module) {
const failureFile = process.argv[2];
if (!failureFile) {
console.error('Usage: node auto-fix.js <failure-file>');
process.exit(1);
}
const agent = new AutoFixAgent(failureFile);
agent.run().catch(console.error);
}
module.exports = AutoFixAgent;