import loadConfig from './utils/config.js'; import logger from './utils/logger.js'; import HumanSchedule from './utils/humanSchedule.js'; import AIProvider from './services/aiProvider.js'; import GitHubService from './services/githubService.js'; import ContextMemory from './core/contextMemory.js'; import MultiRoleDeveloper from './core/multiRoleDeveloper.js'; import ConversationSimulator from './core/conversationSimulator.js'; import CodeReviewEngine from './core/codeReviewEngine.js'; import GitHubReactor from './core/githubReactor.js'; import IssueTracker from './core/issueTracker.js'; import PRWorkflow from './core/prWorkflow.js'; import TimeBehavior from './core/timeBehavior.js'; import SelfUpdater from './core/selfUpdater.js'; import HealthCheck from './core/healthCheck.js'; class HumanBehaviorEngine { constructor() { this.config = loadConfig(); this.memory = new ContextMemory({ persistFile: 'data/memory.json', persistInterval: this.config.memory.persistIntervalMinutes * 60 * 1000, maxHistorySize: this.config.memory.maxHistorySize, maxDecisionsSize: this.config.memory.maxDecisionsSize, }); this.schedule = new HumanSchedule(this.config); this.timeBehavior = new TimeBehavior(this.config); this.ai = new AIProvider(this.config); this.github = new GitHubService(this.config); this.developer = new MultiRoleDeveloper(this.config, this.memory); this.conversation = new ConversationSimulator(this.ai, this.memory, this.developer); this.reviewEngine = new CodeReviewEngine(this.ai, this.memory, this.developer); this.reactor = new GitHubReactor( this.github, this.ai, this.memory, this.developer, this.conversation, this.reviewEngine ); this.issueTracker = new IssueTracker( this.github, this.ai, this.memory, this.developer, this.conversation ); this.prWorkflow = new PRWorkflow( this.github, this.ai, this.memory, this.developer, this.conversation, this.reviewEngine ); this.selfUpdater = new SelfUpdater(this.config, this.memory); this.healthCheck = new HealthCheck(); this.running = false; this._timeoutId = null; this._cycleCount = 0; this._startTime = null; this._lastError = null; this._consecutiveErrors = 0; this._registerHealthChecks(); } async start() { logger.info('🚀 Starting Human Behavior Engine v3.0'); logger.info(`AI Provider: ${this.config.ai.provider}`); logger.info(`Timezone: ${this.config.schedule.timezone}`); logger.info(`Role: ${this.developer.currentRole}`); this._startTime = Date.now(); await this.memory.initialize(); await this.developer.initialize(); await this.selfUpdater.initialize(); logger.info(`Timezone: ${this.config.schedule.timezone} (no work hours restriction)`); this.running = true; await this._runCycle(); } async _runCycle() { if (!this.running) return; if (!this.timeBehavior.shouldAct()) { const delay = this.timeBehavior.getNextActionDelay(); logger.debug(`Skipping action (intensity: ${this.timeBehavior.getIntensity().toFixed(2)})`); this._timeoutId = setTimeout(() => this._runCycle(), delay); return; } this._cycleCount++; try { const mood = this.timeBehavior.getMood(); const period = this.timeBehavior.getCurrentPeriod(); logger.info(`🔄 Cycle #${this._cycleCount} started (mood: ${mood}, period: ${period})`); await this._loadProjectContext(); const actions = [ { name: 'reactor', fn: () => this.reactor.react() }, { name: 'issues', fn: () => this.issueTracker.processIssues() }, { name: 'prs', fn: () => this.prWorkflow.processPRs() }, ]; const maxActions = this.config.activity.maxActionsPerCycle; const selectedActions = actions.slice(0, maxActions); for (const action of selectedActions) { try { await action.fn(); } catch (error) { logger.error(`Action "${action.name}" failed: ${error.message}`); } } this._consecutiveErrors = 0; if (this._cycleCount % 10 === 0) { await this._runHealthCheck(); } if (this._cycleCount % 50 === 0) { await this.memory.forcePersist(); } if (this._cycleCount % 100 === 0) { await this.memory.cleanup(); } if (this._cycleCount >= this.config.daemon.maxCyclesBeforeRestart) { logger.info(`Reached max cycles (${this._cycleCount}), restarting cycle count`); this._cycleCount = 0; } const delay = this.timeBehavior.getNextActionDelay(); logger.info(`⏳ Next cycle in ${Math.round(delay / 60000)} minutes`); this._timeoutId = setTimeout(() => this._runCycle(), delay); } catch (error) { this._consecutiveErrors++; this._lastError = error; logger.error(`Cycle #${this._cycleCount} failed: ${error.message}`); if (this._consecutiveErrors >= 5) { logger.error('Too many consecutive errors, pausing for 1 hour'); this._timeoutId = setTimeout(() => { this._consecutiveErrors = 0; this._runCycle(); }, 60 * 60 * 1000); } else { const delay = Math.min( this.timeBehavior.getNextActionDelay(), 30 * 60 * 1000 ); this._timeoutId = setTimeout(() => this._runCycle(), delay); } } } async _loadProjectContext() { try { const commits = await this.github.getCommitHistory('main', 10); const files = await this.github.getRepositoryContent('src', 'main'); await this.developer.adaptRoleFromContext( commits.map(c => c.commit.message).join(' '), files || [] ); } catch (error) { logger.warn(`Could not load project context: ${error.message}`); } } async _runHealthCheck() { const result = await this.healthCheck.runAll(); if (!result.allHealthy) { logger.warn(`Health check failed: ${Object.entries(result.results) .filter(([, r]) => r.status === 'unhealthy') .map(([name]) => name) .join(', ')}`); } await this.selfUpdater.adaptBehavior({ errorRate: this._consecutiveErrors / Math.max(this._cycleCount, 1), successRate: 1 - (this._consecutiveErrors / Math.max(this._cycleCount, 1)), avgResponseTime: 0, }); } _registerHealthChecks() { this.healthCheck.registerCheck('memory', async () => { return this.memory.getContextSummary(); }); this.healthCheck.registerCheck('ai', async () => { return this.ai.getStats(); }); this.healthCheck.registerCheck('github', async () => { return this.github.getStats(); }); this.healthCheck.registerCheck('developer', async () => { return this.developer.getStats(); }); } _scheduleNextCycle() { const nextWork = this.timeBehavior.getNextWorkTime(); const delay = nextWork.getTime() - Date.now(); logger.info(`📅 Scheduling next work session in ${Math.round(delay / 60000)} minutes`); this._timeoutId = setTimeout(async () => { this.running = true; await this._runCycle(); }, Math.min(delay, 86400000)); } async stop() { logger.info('🛑 Stopping Human Behavior Engine'); this.running = false; if (this._timeoutId) { clearTimeout(this._timeoutId); this._timeoutId = null; } await this.memory.forcePersist(); } async getStatus() { const isWorkHours = this.timeBehavior.isWorkHours(); const mood = this.timeBehavior.getMood(); const period = this.timeBehavior.getCurrentPeriod(); const dayName = this.timeBehavior.getDayOfWeekName(); const roleContext = this.developer.getRoleContext(); const uptime = this._startTime ? Date.now() - this._startTime : 0; const uptimeHours = Math.floor(uptime / (1000 * 60 * 60)); const uptimeMinutes = Math.floor((uptime % (1000 * 60 * 60)) / (1000 * 60)); return { running: this.running, isWorkHours, mood, period, day: dayName, role: roleContext.role, workingOn: this.memory.recall('currentTask') || 'idle', nextWorkTime: isWorkHours ? 'Now' : this.timeBehavior.getNextWorkTime().toLocaleString(), aiProvider: this.config.ai.provider, timezone: this.config.schedule.timezone, memory: this.memory.getContextSummary(), uptime: `${uptimeHours}h ${uptimeMinutes}m`, cycles: this._cycleCount, consecutiveErrors: this._consecutiveErrors, health: this.healthCheck.getLastResult(), config: { timezone: this.config.schedule.timezone, noWorkHoursRestriction: this.config.schedule.noWorkHoursRestriction, }, }; } async runOnce() { logger.info('Running single human behavior cycle'); try { await this.memory.initialize(); } catch (e) { logger.warn(`Memory init warning: ${e.message}`); } try { await this.developer.initialize(); } catch (e) { logger.warn(`Developer init warning: ${e.message}`); } const actions = [ { name: 'context', fn: () => this._loadProjectContext() }, { name: 'reactor', fn: () => this.reactor.react() }, { name: 'issues', fn: () => this.issueTracker.processIssues() }, { name: 'prs', fn: () => this.prWorkflow.processPRs() }, ]; for (const action of actions) { try { const timeout = new Promise((_, reject) => setTimeout(() => reject(new Error(`${action.name} timed out after 30s`)), 30000) ); await Promise.race([action.fn(), timeout]); } catch (error) { logger.error(`Action "${action.name}" failed: ${error.message}`); } } } async exportMemory() { return this.memory.export(); } async importMemory(data) { this.memory.import(data); } } export default HumanBehaviorEngine;