/** * AgentOS 2.0 - Base Agent Class * All department agents inherit from this class * Provides: memory, personality, voice, scoring, learning */ const fs = require('fs'); const path = require('path'); const apiCaller = require('../skills/api_caller'); const memory = require('../skills/memory'); const conversationMemory = require('../skills/conversation_memory'); const AGENTS_DB = path.join(__dirname, '../database/agents_db.json'); const METRICS_DB = path.join(__dirname, '../database/metrics_db.json'); class BaseAgent { constructor(config) { this.id = config.id; this.name = config.name; this.role = config.role; this.department = config.department; this.personality = config.personality; this.voice = config.voice; this.skills = config.skills || []; this.status = 'idle'; // idle, working, waiting, error this.lastActive = null; this.pulseInterval = config.pulseInterval || 15 * 60 * 1000; // 15 min default this.pulseTimer = null; // Load agent-specific memory this.agentMemory = this.loadAgentMemory(); // Metrics this.metrics = { tasksCompleted: 0, tasksFailed: 0, avgScore: 0, totalActions: 0, learningLevel: 1 }; this.loadMetrics(); } /** * Agent personality system - defines tone, style, behavior */ getSystemPrompt() { return `You are ${this.name}, the ${this.role} in AgentOS. PERSONALITY: ${this.personality.description} VOICE & STYLE: - Tone: ${this.personality.tone} - Speaking style: ${this.personality.style} - Emoji usage: ${this.personality.emojiUsage} - Greeting: ${this.personality.greeting} - Signoff: ${this.personality.signoff} RESPONSIBILITIES: ${this.personality.responsibilities.join('\n')} BEHAVIOR RULES: 1. Always be proactive - suggest next actions 2. Ask for feedback after completing tasks 3. Learn from user corrections 4. Score your own work (1-10) before submitting 5. If stuck, ask the CEO (APEX) for guidance 6. Maintain context across conversations 7. Document everything for legacy tracking CURRENT CONTEXT: - User: ${this.agentMemory.userProfile?.name || 'Unknown'} - Company: ${this.agentMemory.userProfile?.company || 'Unknown'} - Goals: ${JSON.stringify(this.agentMemory.userProfile?.goals || [])} - Preferences: ${JSON.stringify(this.agentMemory.userProfile?.preferences || {})}`; } /** * Load agent-specific memory from DB */ loadAgentMemory() { try { const db = JSON.parse(fs.readFileSync(AGENTS_DB, 'utf8')); return db.agents?.[this.id] || { userProfile: {}, conversationHistory: [], learnedPatterns: [], feedbackHistory: [], skillLevels: {} }; } catch (e) { return { userProfile: {}, conversationHistory: [], learnedPatterns: [], feedbackHistory: [], skillLevels: {} }; } } /** * Save agent memory */ saveAgentMemory() { try { const db = JSON.parse(fs.readFileSync(AGENTS_DB, 'utf8')) || { agents: {} }; db.agents[this.id] = this.agentMemory; fs.writeFileSync(AGENTS_DB, JSON.stringify(db, null, 2)); return true; } catch (e) { console.error(`[${this.name}] Failed to save memory:`, e.message); return false; } } /** * Load metrics from DB */ loadMetrics() { try { const db = JSON.parse(fs.readFileSync(METRICS_DB, 'utf8')); const agentMetrics = db.agents?.[this.id]; if (agentMetrics) { this.metrics = { ...this.metrics, ...agentMetrics }; } } catch (e) { // No metrics yet } } /** * Save metrics */ saveMetrics() { try { const db = JSON.parse(fs.readFileSync(METRICS_DB, 'utf8')) || { agents: {}, timestamp: new Date().toISOString() }; db.agents[this.id] = this.metrics; db.timestamp = new Date().toISOString(); fs.writeFileSync(METRICS_DB, JSON.stringify(db, null, 2)); return true; } catch (e) { console.error(`[${this.name}] Failed to save metrics:`, e.message); return false; } } /** * Score an action (1-10) */ scoreAction(action, score, feedback = '') { const record = { action, score, feedback, timestamp: new Date().toISOString(), agent: this.id }; // Update running average const totalActions = this.metrics.totalActions + 1; this.metrics.avgScore = ((this.metrics.avgScore * this.metrics.totalActions) + score) / totalActions; this.metrics.totalActions = totalActions; // Store in feedback history this.agentMemory.feedbackHistory.push(record); if (this.agentMemory.feedbackHistory.length > 100) { this.agentMemory.feedbackHistory = this.agentMemory.feedbackHistory.slice(-100); } this.saveMetrics(); this.saveAgentMemory(); console.log(`[${this.name}] Action scored: ${score}/10 - ${action}`); return record; } /** * Learn from feedback */ async learnFromFeedback(task, feedback, correction) { const learning = { task, feedback, correction, timestamp: new Date().toISOString(), lesson: await this.extractLesson(task, feedback, correction) }; this.agentMemory.learnedPatterns.push(learning); this.metrics.learningLevel = Math.min(10, Math.floor(this.agentMemory.learnedPatterns.length / 5) + 1); this.saveAgentMemory(); this.saveMetrics(); console.log(`[${this.name}] Learned: ${learning.lesson}`); return learning; } /** * Extract lesson from feedback using AI */ async extractLesson(task, feedback, correction) { const prompt = [ { role: 'system', content: 'Extract a concise lesson from this feedback. Max 1 sentence.' }, { role: 'user', content: `Task: ${task}\nFeedback: ${feedback}\nCorrection: ${correction}\n\nLesson:` } ]; const result = await apiCaller.callOpenRouter(prompt, 'meta-llama/llama-3.3-70b-instruct:free'); return result.success ? result.data : 'Learned from feedback'; } /** * Execute a task with scoring */ async execute(task, options = {}) { const startTime = Date.now(); this.status = 'working'; this.lastActive = new Date().toISOString(); try { // Pre-task: Check context const context = await this.gatherContext(task); // Execute task using appropriate skill const result = await this.performTask(task, context, options); // Self-score before user feedback const selfScore = await this.selfScore(result); // Post-task: Update metrics this.metrics.tasksCompleted++; const duration = ((Date.now() - startTime) / 1000).toFixed(2); this.status = 'idle'; return { success: true, agent: this.name, result, selfScore, duration, timestamp: new Date().toISOString() }; } catch (error) { this.metrics.tasksFailed++; this.status = 'error'; return { success: false, agent: this.name, error: error.message, timestamp: new Date().toISOString() }; } finally { this.saveMetrics(); } } /** * Gather context for task (RAG-style) */ async gatherContext(task) { const context = { userProfile: this.agentMemory.userProfile, relevantHistory: this.agentMemory.conversationHistory.slice(-10), learnedPatterns: this.agentMemory.learnedPatterns.slice(-5), similarTasks: await this.findSimilarTasks(task) }; return context; } /** * Find similar tasks from history */ async findSimilarTasks(task) { // Simple keyword matching - can be enhanced with vector search const history = this.agentMemory.conversationHistory; const taskKeywords = task.toLowerCase().split(' ').filter(w => w.length > 4); return history.filter(h => taskKeywords.some(k => h.content?.toLowerCase().includes(k)) ).slice(-3); } /** * Self-score result before user feedback */ async selfScore(result) { const prompt = [ { role: 'system', content: 'Score this task result from 1-10 based on completeness and quality.' }, { role: 'user', content: `Result: ${JSON.stringify(result).substring(0, 500)}\n\nScore (1-10):` } ]; const res = await apiCaller.callOpenRouter(prompt, 'meta-llama/llama-3.3-70b-instruct:free'); const score = parseInt(res.data.match(/\d+/)?.[0]) || 5; return Math.min(10, Math.max(1, score)); } /** * Abstract method - override in subclasses */ async performTask(task, context, options) { throw new Error('performTask must be implemented by subclass'); } /** * Start pulse/heartbeat */ startPulse() { if (this.pulseTimer) clearInterval(this.pulseTimer); this.pulseTimer = setInterval(() => { this.pulse(); }, this.pulseInterval); console.log(`[${this.name}] Pulse started (${this.pulseInterval/1000}s interval)`); } /** * Pulse heartbeat - check pending tasks */ async pulse() { console.log(`[${this.name}] Pulse check at ${new Date().toISOString()}`); // Override in subclasses for specific pulse behavior } /** * Stop pulse */ stopPulse() { if (this.pulseTimer) { clearInterval(this.pulseTimer); this.pulseTimer = null; console.log(`[${this.name}] Pulse stopped`); } } /** * Get agent status */ getStatus() { return { id: this.id, name: this.name, role: this.role, status: this.status, lastActive: this.lastActive, metrics: this.metrics, learningLevel: this.metrics.learningLevel }; } /** * Interview user to learn preferences */ async interviewUser(chatId, topic = 'general') { const questions = await this.generateInterviewQuestions(topic); return { agent: this.name, topic, questions, instructions: 'Answer these questions to help me understand your needs better.' }; } /** * Generate interview questions */ async generateInterviewQuestions(topic) { const prompt = [ { role: 'system', content: `You are ${this.name}. Generate 5 interview questions to understand user's ${topic} needs.` }, { role: 'user', content: `Generate 5 concise questions about: ${topic}` } ]; const result = await apiCaller.callOpenRouter(prompt, 'meta-llama/llama-3.3-70b-instruct:free'); return result.success ? result.data.split('\n').filter(q => q.trim()) : []; } } module.exports = BaseAgent;