UNPKG

oneie

Version:

🤝 ONE Personal Collaborative Intelligence - Creates personalized AI workspace from your me.md profile. Simple: npx oneie → edit me.md → generate personalized agents, workflows & missions. From students to enterprises, ONE adapts to your context.

609 lines (530 loc) 19.3 kB
#!/usr/bin/env node /** * Background Agent Loader System * Loads and executes agents from .claude/agents/ directory */ import { promises as fs } from 'fs'; import path from 'path'; import yaml from 'js-yaml'; import chalk from 'chalk'; // License validation removed for simplified deployment import { AdvancedCache, MemoryMonitor, PerformanceProfiler, LazyLoader } from './performance-optimizer.js'; /** * BackgroundAgentLoader class for loading and executing agents */ export class BackgroundAgentLoader { constructor(options = {}) { this.agentsPath = options.agentsPath || '.claude/agents'; // Advanced caching system this.cache = new AdvancedCache({ maxSize: options.cacheMaxSize || 200, ttl: options.cacheTTL || 600000, // 10 minutes }); // Memory monitoring this.memoryMonitor = new MemoryMonitor({ maxHeapSize: options.maxHeapSize || 500 * 1024 * 1024, // 500MB warningThreshold: options.memoryWarningThreshold || 0.8 }); // Performance profiling this.profiler = new PerformanceProfiler(); // Lazy loading system this.lazyLoader = new LazyLoader({ maxConcurrentLoads: options.maxConcurrentLoads || 5, loadTimeout: options.loadTimeout || 5000 }); // Legacy performance metrics for backward compatibility this.performanceMetrics = { loadTimes: [], memoryUsage: [], cacheHits: 0, cacheMisses: 0 }; // Initialize license validator for premium agent gating // License validation removed for simplified deployment // Coordination will be handled externally to avoid circular dependencies this.coordinator = null; this.enableCoordination = options.enableCoordination !== false; // Default true // Performance optimization settings this.enableProfiling = options.enableProfiling !== false; this.enablePreloading = options.enablePreloading !== false; this.preloadCommonAgents = options.preloadCommonAgents !== false; // Preload common agents if enabled if (this.preloadCommonAgents) { this.initializeCommonAgents(); } } /** * Initialize common agents for preloading */ async initializeCommonAgents() { const commonAgents = [ 'engineering-director', 'engineering-architect', 'engineering-developer', 'content-team-manager', 'research-foundation-analyst' ]; try { console.log(chalk.blue('🚀 Preloading common agents...')); const results = await this.cache.preload(commonAgents, async (agentName) => { try { const agentPath = path.join(this.agentsPath, `${agentName}.md`); return await this.parseAgentFile(agentPath); } catch (error) { console.log(chalk.yellow(`⚠️ Failed to preload ${agentName}: ${error.message}`)); return null; } }); const successful = results.filter(r => r.success && !r.cached).length; console.log(chalk.green(`✅ Preloaded ${successful} common agents`)); } catch (error) { console.log(chalk.yellow(`⚠️ Preloading failed: ${error.message}`)); } } /** * Load an agent from the .claude/agents/ directory * @param {string} agentName - Name of the agent (without .md extension) * @returns {Promise<BackgroundAgent>} Loaded agent instance */ async loadAgent(agentName) { const startTime = performance.now(); const profileId = this.enableProfiling ? this.profiler.start(`loadAgent-${agentName}`, { operation: 'loadAgent', agentName }) : null; try { // No premium gating - all agents are accessible const accessValidation = { hasAccess: true, tier: 'free', reason: 'No restrictions' }; if (profileId) this.profiler.mark(profileId, 'license-validated'); // Check advanced cache first const cachedAgent = this.cache.get(agentName); if (cachedAgent) { this.performanceMetrics.cacheHits++; if (profileId) { this.profiler.mark(profileId, 'cache-hit'); this.profiler.end(profileId); } // Add license info to cached agent return new BackgroundAgent({ ...cachedAgent, licenseInfo: accessValidation }); } this.performanceMetrics.cacheMisses++; if (profileId) this.profiler.mark(profileId, 'cache-miss'); // Use lazy loading for optimal performance const agentConfig = await this.lazyLoader.load(agentName, async (name) => { // Construct agent file path const agentPath = path.join(this.agentsPath, `${name}.md`); if (profileId) this.profiler.mark(profileId, 'file-loading'); // Parse agent file with optimized parsing const config = await this.parseAgentFile(agentPath); if (profileId) this.profiler.mark(profileId, 'file-parsed'); return config; }); // Add license information to agent config agentConfig.licenseInfo = accessValidation; // Create agent instance const agent = new BackgroundAgent(agentConfig); // Cache for future use with advanced cache this.cache.set(agentName, agentConfig); // Record performance metrics (legacy support) const loadTime = performance.now() - startTime; this.performanceMetrics.loadTimes.push(loadTime); if (profileId) { this.profiler.mark(profileId, 'agent-created'); this.profiler.end(profileId); } return agent; } catch (error) { // If it's already an AgentLoadError, re-throw as-is if (error instanceof AgentLoadError) { throw error; } throw new AgentLoadError(`Failed to load agent '${agentName}': ${error.message}`); } } /** * Parse agent file and extract configuration * @param {string} agentPath - Path to agent markdown file * @returns {Promise<Object>} Agent configuration object */ async parseAgentFile(agentPath) { try { // Check if file exists const exists = await fs.access(agentPath).then(() => true).catch(() => false); if (!exists) { throw new Error(`Agent file not found: ${agentPath}`); } // Read agent file const content = await fs.readFile(agentPath, 'utf8'); // Extract YAML frontmatter if present let config = {}; let yamlMatch = content.match(/^---\n([\s\S]*?)\n---/); if (yamlMatch) { try { config = yaml.load(yamlMatch[1]) || {}; } catch (yamlError) { console.warn(`Warning: Invalid YAML in ${agentPath}: ${yamlError.message}`); } } // Extract basic agent info from markdown const lines = content.split('\n'); let title = ''; let description = ''; for (const line of lines) { if (line.startsWith('# ') && !title) { title = line.substring(2).trim(); } else if (line.trim() && !line.startsWith('#') && !line.startsWith('---') && !description) { description = line.trim(); break; } } // Return comprehensive config return { name: config.name || title || path.basename(agentPath, '.md'), title: config.title || title, description: config.description || description, content: content, config: config, filePath: agentPath, lastModified: (await fs.stat(agentPath)).mtime }; } catch (error) { throw new Error(`Failed to parse agent file ${agentPath}: ${error.message}`); } } /** * Execute an agent with given context and arguments * @param {string} agentName - Name of the agent to execute * @param {Object} context - Execution context * @param {Array} args - Arguments for the agent * @param {Object} options - Execution options * @returns {Promise<Object>} Execution result */ async executeAgent(agentName, context = {}, args = [], options = {}) { const startTime = performance.now(); try { // Load the agent const agent = await this.loadAgent(agentName); // Check if coordination should be enabled for this execution let coordinationResult = null; if (this.coordinator && this.enableCoordination && options.enableCoordination !== false) { try { // Spawn related agents and coordinate if context is complex enough const contextString = typeof context === 'string' ? context : JSON.stringify(context); if (contextString.length > 50) { // Only coordinate for complex contexts coordinationResult = await this.coordinator.spawnRelatedAgents( agentName, contextString, { maxSpawn: options.maxSpawn || 2, executeImmediately: options.executeSpawnedAgents || false } ); } } catch (coordError) { console.log(`⚠️ Agent coordination failed: ${coordError.message}`); // Continue with normal execution even if coordination fails } } // Execute the primary agent with context and arguments const result = await agent.execute(context, args); // Record execution time const executionTime = performance.now() - startTime; return { success: true, result: result, agent: agentName, executionTime: executionTime, timestamp: new Date().toISOString(), coordination: coordinationResult }; } catch (error) { return { success: false, error: error.message, agent: agentName, timestamp: new Date().toISOString() }; } } /** * List all available agents in the .claude/agents/ directory * @returns {Promise<Array<string>>} Array of agent names */ async listAvailableAgents() { try { const files = await fs.readdir(this.agentsPath); return files .filter(file => file.endsWith('.md')) .map(file => path.basename(file, '.md')) .sort(); } catch (error) { throw new Error(`Failed to list agents: ${error.message}`); } } /** * Get license status and agent access summary * @returns {Promise<Object>} License status information */ async getLicenseStatus() { return { status: 'free', licenseType: 'open-source' }; // All agents are free } /** * Check if an agent requires premium access * @param {string} agentName - Agent name * @returns {boolean} True if premium license required */ isPremiumAgent(agentName) { return false; // All agents are free - no premium restriction } /** * Generate upgrade prompt for premium agents * @param {string} agentName - Agent name * @returns {Promise<string>} Formatted upgrade prompt */ async generateUpgradePrompt(agentName) { return ''; // No upgrade prompts needed - all agents are free } /** * List available agents with premium status indicators * @returns {Promise<Array<Object>>} Array of agent info with premium status */ async listAgentsWithStatus() { const agents = await this.listAvailableAgents(); const agentList = []; for (const agentName of agents) { const isPremium = this.isPremiumAgent(agentName); const accessValidation = { hasAccess: true, reason: 'All agents are free' }; // No access restrictions agentList.push({ name: agentName, isPremium: isPremium, hasAccess: accessValidation.hasAccess, tier: accessValidation.tier, reason: accessValidation.reason }); } return agentList; } /** * Create a trial license for testing premium features * @param {number} durationDays - Trial duration in days * @returns {Promise<Object>} Trial license information */ async createTrialLicense(durationDays = 7) { return { status: 'free', message: 'No trial needed - all agents are free' }; // No trials needed } /** * Get comprehensive performance metrics * @returns {Object} Performance metrics */ getPerformanceMetrics() { const loadTimes = this.performanceMetrics.loadTimes; const legacyMetrics = { totalLoads: loadTimes.length, averageLoadTime: loadTimes.length > 0 ? loadTimes.reduce((a, b) => a + b, 0) / loadTimes.length : 0, cacheHitRate: this.performanceMetrics.cacheHits / (this.performanceMetrics.cacheHits + this.performanceMetrics.cacheMisses) || 0, memoryUsage: process.memoryUsage(), cacheSize: this.cache instanceof Map ? this.cache.size : this.cache.getStats().size }; // Enhanced metrics from advanced systems const cacheStats = this.cache.getStats ? this.cache.getStats() : {}; const memoryStats = this.memoryMonitor.getStats(); const lazyStats = this.lazyLoader.getStats(); return { ...legacyMetrics, advanced: { cache: { ...cacheStats, efficiency: cacheStats.hitRate > 0.8 ? 'excellent' : cacheStats.hitRate > 0.6 ? 'good' : cacheStats.hitRate > 0.4 ? 'fair' : 'poor' }, memory: memoryStats ? { current: memoryStats.current, average: memoryStats.average, peak: memoryStats.peak, status: memoryStats.percentages?.heapTotal < 0.8 ? 'healthy' : memoryStats.percentages?.heapTotal < 0.9 ? 'warning' : 'critical' } : null, lazyLoading: lazyStats, profiling: { enabled: this.enableProfiling, profiles: this.profiler.getProfiles().length } } }; } /** * Get detailed profiling report for specific operation * @param {string} operationType - Type of operation to analyze * @returns {Object} Profiling summary */ getProfilingReport(operationType = 'loadAgent') { if (!this.enableProfiling) { return { enabled: false, message: 'Profiling is disabled' }; } return this.profiler.getSummary(operationType); } /** * Optimize cache based on usage patterns */ optimizeCache() { if (this.cache.cleanup) { this.cache.cleanup(); } console.log(chalk.blue('🔧 Cache optimization completed')); } /** * Preload specific agents * @param {Array<string>} agentNames - Names of agents to preload * @returns {Promise<Array>} Preload results */ async preloadAgents(agentNames) { if (!this.cache.preload) { console.log(chalk.yellow('⚠️ Advanced caching not available for preloading')); return []; } const results = await this.cache.preload(agentNames, async (agentName) => { const agentPath = path.join(this.agentsPath, `${agentName}.md`); return await this.parseAgentFile(agentPath); }); const successful = results.filter(r => r.success).length; console.log(chalk.green(`✅ Preloaded ${successful}/${agentNames.length} agents`)); return results; } /** * Check if performance targets are met * @returns {Object} Performance target status */ checkPerformanceTargets() { const metrics = this.getPerformanceMetrics(); const memoryPerAgent = metrics.advanced?.memory?.current?.heapUsed / (metrics.cacheSize || 1) / 1024 / 1024; // MB per agent return { loadTimeTarget: { target: 2000, // 2 seconds current: metrics.averageLoadTime, met: metrics.averageLoadTime < 2000, status: metrics.averageLoadTime < 500 ? 'excellent' : metrics.averageLoadTime < 1000 ? 'good' : metrics.averageLoadTime < 2000 ? 'acceptable' : 'needs_improvement' }, memoryTarget: { target: 100, // 100MB per agent current: memoryPerAgent || 0, met: (memoryPerAgent || 0) < 100, status: memoryPerAgent < 10 ? 'excellent' : memoryPerAgent < 25 ? 'good' : memoryPerAgent < 50 ? 'acceptable' : 'needs_improvement' }, cacheTarget: { target: 0.8, // 80% hit rate current: metrics.cacheHitRate, met: metrics.cacheHitRate > 0.8, status: metrics.cacheHitRate > 0.9 ? 'excellent' : metrics.cacheHitRate > 0.8 ? 'good' : metrics.cacheHitRate > 0.6 ? 'acceptable' : 'needs_improvement' } }; } /** * Set the agent coordinator (to avoid circular dependencies) * @param {AgentCoordinator} coordinator - Agent coordinator instance */ setCoordinator(coordinator) { this.coordinator = coordinator; } /** * Get agent coordination statistics * @returns {Object} Coordination statistics */ getCoordinationStats() { if (!this.coordinator) { return { enabled: false, message: 'Agent coordination is disabled' }; } return { enabled: true, ...this.coordinator.getCoordinationStats() }; } /** * Get active coordination sessions * @returns {Array} Active coordination sessions */ getActiveCoordinationSessions() { if (!this.coordinator) return []; return this.coordinator.getActiveSessions(); } /** * Clear agent cache */ clearCache() { this.cache.clear(); this.performanceMetrics.cacheHits = 0; this.performanceMetrics.cacheMisses = 0; } } /** * BackgroundAgent class representing a loaded agent */ export class BackgroundAgent { constructor(config) { this.name = config.name; this.title = config.title; this.description = config.description; this.content = config.content; this.config = config.config || {}; this.filePath = config.filePath; this.lastModified = config.lastModified; this.state = {}; } /** * Execute the agent with given context and arguments * @param {Object} context - Execution context * @param {Array} args - Arguments for execution * @returns {Promise<Object>} Execution result */ async execute(context = {}, args = []) { // For now, return agent info and basic execution // In a full implementation, this would process the agent's instructions return { agent: this.name, title: this.title, description: this.description, context: context, arguments: args, state: this.state, executed: true, timestamp: new Date().toISOString() }; } /** * Get agent metadata * @returns {Object} Agent metadata */ getMetadata() { return { name: this.name, title: this.title, description: this.description, filePath: this.filePath, lastModified: this.lastModified, configKeys: Object.keys(this.config) }; } } /** * Custom error class for agent loading errors */ export class AgentLoadError extends Error { constructor(message, code = 'AGENT_LOAD_FAILED', details = null) { super(message); this.name = 'AgentLoadError'; this.code = code; this.details = details; } } // Export default instance export default new BackgroundAgentLoader();