Spaces:
Sleeping
Sleeping
| 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; | |