vinos-engine / agents /base_agent.js
VinOS Agent
feat: CEO Command Center — Apex standalone HF Space
e4224e9
/**
* 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;