UNPKG

@dollhousemcp/mcp-server

Version:

DollhouseMCP - A Model Context Protocol (MCP) server that enables dynamic AI persona management from markdown files, allowing Claude and other compatible AI assistants to activate and switch between different behavioral personas.

848 lines 121 kB
/** * Agent element implementation. * Autonomous goal-oriented actors with decision-making capabilities. * * SECURITY MEASURES IMPLEMENTED: * 1. Goal validation to prevent malicious objectives * 2. Decision framework sandboxing * 3. State size limits to prevent DoS * 4. Risk assessment for damage prevention * 5. Audit logging for all decisions and actions */ import { BaseElement } from '../BaseElement.js'; import { ElementType } from '../../portfolio/types.js'; import { sanitizeInput } from '../../security/InputValidator.js'; import { UnicodeValidator } from '../../security/validators/unicodeValidator.js'; import { SecurityMonitor } from '../../security/securityMonitor.js'; import { logger } from '../../utils/logger.js'; import { AGENT_LIMITS, AGENT_DEFAULTS, DECISION_FRAMEWORKS, RISK_TOLERANCE_LEVELS } from './constants.js'; import { validateRuleEngineConfig } from './ruleEngineConfig.js'; import { applyGoalTemplate, recommendGoalTemplate, validateGoalAgainstTemplate } from './goalTemplates.js'; export class Agent extends BaseElement { state; isDirtyState = false; ruleEngineConfig; constructor(metadata) { // Sanitize all inputs const sanitizedMetadata = { ...metadata, name: metadata.name ? sanitizeInput(UnicodeValidator.normalize(metadata.name).normalizedContent, 100) : undefined, description: metadata.description ? sanitizeInput(UnicodeValidator.normalize(metadata.description).normalizedContent, 500) : undefined, specializations: metadata.specializations?.map(s => sanitizeInput(s, 50)), decisionFramework: metadata.decisionFramework || AGENT_DEFAULTS.DECISION_FRAMEWORK, riskTolerance: metadata.riskTolerance || AGENT_DEFAULTS.RISK_TOLERANCE, learningEnabled: metadata.learningEnabled ?? AGENT_DEFAULTS.LEARNING_ENABLED, maxConcurrentGoals: metadata.maxConcurrentGoals ?? AGENT_DEFAULTS.MAX_CONCURRENT_GOALS }; // MEDIUM PRIORITY IMPROVEMENT: Validate decision framework configuration // Ensures only supported frameworks are used if (sanitizedMetadata.decisionFramework && !DECISION_FRAMEWORKS.includes(sanitizedMetadata.decisionFramework)) { throw new Error(`Invalid decision framework: ${sanitizedMetadata.decisionFramework}. ` + `Supported frameworks: ${DECISION_FRAMEWORKS.join(', ')}`); } // Validate risk tolerance level if (sanitizedMetadata.riskTolerance && !RISK_TOLERANCE_LEVELS.includes(sanitizedMetadata.riskTolerance)) { throw new Error(`Invalid risk tolerance: ${sanitizedMetadata.riskTolerance}. ` + `Supported levels: ${RISK_TOLERANCE_LEVELS.join(', ')}`); } // Validate max concurrent goals if (sanitizedMetadata.maxConcurrentGoals !== undefined) { const maxGoals = sanitizedMetadata.maxConcurrentGoals; if (!Number.isInteger(maxGoals) || maxGoals < 1 || maxGoals > AGENT_LIMITS.MAX_GOALS) { throw new Error(`maxConcurrentGoals must be between 1 and ${AGENT_LIMITS.MAX_GOALS}`); } } super(ElementType.AGENT, sanitizedMetadata); // Initialize state this.state = { goals: [], decisions: [], context: {}, lastActive: new Date(), sessionCount: 0 }; // Set agent-specific extensions this.extensions = { decisionFramework: sanitizedMetadata.decisionFramework, riskTolerance: sanitizedMetadata.riskTolerance, learningEnabled: sanitizedMetadata.learningEnabled, specializations: sanitizedMetadata.specializations || [], ruleEngineConfig: metadata.ruleEngineConfig }; // Initialize rule engine configuration (with validation) this.ruleEngineConfig = validateRuleEngineConfig(this.extensions.ruleEngineConfig || {}); } /** * Add a new goal with security validation */ addGoal(goal) { // Validate goal count if (this.state.goals.length >= AGENT_LIMITS.MAX_GOALS) { throw new Error(`Maximum number of goals (${AGENT_LIMITS.MAX_GOALS}) reached`); } // Sanitize goal description const sanitizedDescription = sanitizeInput(UnicodeValidator.normalize(goal.description || '').normalizedContent, AGENT_LIMITS.MAX_GOAL_LENGTH); if (!sanitizedDescription || sanitizedDescription.length < 3) { throw new Error('Goal description must be at least 3 characters'); } // Validate goal for security threats const securityCheck = this.validateGoalSecurity(sanitizedDescription); if (!securityCheck.safe) { SecurityMonitor.logSecurityEvent({ type: 'CONTENT_INJECTION_ATTEMPT', severity: 'HIGH', source: 'Agent.addGoal', details: `Potentially malicious goal rejected: ${securityCheck.reason}`, additionalData: { agentId: this.id } }); throw new Error(`Goal contains potentially harmful content: ${securityCheck.reason}`); } // Calculate Eisenhower quadrant const importance = goal.importance || 5; const urgency = goal.urgency || 5; const eisenhowerQuadrant = this.calculateEisenhowerQuadrant(importance, urgency); // Create new goal const newGoal = { id: `goal_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`, description: sanitizedDescription, priority: goal.priority || AGENT_DEFAULTS.GOAL_PRIORITY, status: 'pending', importance, urgency, eisenhowerQuadrant, createdAt: new Date(), updatedAt: new Date(), dependencies: goal.dependencies || [], riskLevel: goal.riskLevel || 'low', estimatedEffort: goal.estimatedEffort, notes: goal.notes ? sanitizeInput(goal.notes, 500) : undefined }; // MEDIUM PRIORITY IMPROVEMENT: Detect dependency cycles before adding goal if (newGoal.dependencies && newGoal.dependencies.length > 0) { const cycleCheck = this.detectDependencyCycle(newGoal.id, newGoal.dependencies); if (cycleCheck.hasCycle) { throw new Error(`Dependency cycle detected: ${cycleCheck.path.join(' → ')}`); } } this.state.goals.push(newGoal); this.isDirtyState = true; this.markDirty(); logger.info(`Goal added to agent ${this.metadata.name}`, { goalId: newGoal.id }); return newGoal; } /** * Make a decision for a goal */ async makeDecision(goalId, context) { // MEDIUM PRIORITY IMPROVEMENT: Track performance metrics for decision making const startTime = Date.now(); const performanceMetrics = {}; const goal = this.state.goals.find(g => g.id === goalId); if (!goal) { throw new Error(`Goal ${goalId} not found`); } if (goal.status === 'completed' || goal.status === 'cancelled') { throw new Error(`Cannot make decision for ${goal.status} goal`); } // Update goal status goal.status = 'in_progress'; goal.updatedAt = new Date(); // Prepare decision context const decisionContext = { ...this.state.context, ...context, goal, agentMetadata: this.metadata, previousDecisions: this.state.decisions.filter(d => d.goalId === goalId) }; // Make decision based on framework (with timing) const frameworkStart = Date.now(); const decision = await this.executeDecisionFramework(goal, decisionContext); performanceMetrics.frameworkTimeMs = Date.now() - frameworkStart; // Risk assessment (with timing) const riskStart = Date.now(); const riskAssessment = this.assessRisk(decision, goal, decisionContext); performanceMetrics.riskAssessmentTimeMs = Date.now() - riskStart; // Calculate total decision time performanceMetrics.decisionTimeMs = Date.now() - startTime; // Create decision record const decisionRecord = { id: `decision_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`, goalId, timestamp: new Date(), decision: decision.action, reasoning: decision.reasoning, framework: this.extensions?.decisionFramework || AGENT_DEFAULTS.DECISION_FRAMEWORK, confidence: decision.confidence, riskAssessment, performanceMetrics // Add performance tracking }; // OPTIMIZATION: Use efficient circular buffer pattern // Add to history with limit (avoid array slicing for better performance) if (this.state.decisions.length >= AGENT_LIMITS.MAX_DECISION_HISTORY) { // Remove oldest entry before adding new one (O(n) but happens infrequently) this.state.decisions.shift(); } this.state.decisions.push(decisionRecord); this.isDirtyState = true; this.markDirty(); // Log decision for audit SecurityMonitor.logSecurityEvent({ type: 'AGENT_DECISION', severity: 'LOW', source: 'Agent.makeDecision', details: `Agent ${this.metadata.name} made decision for goal ${goalId}`, additionalData: { agentId: this.id, goalId, framework: decisionRecord.framework, riskLevel: riskAssessment.level } }); return decisionRecord; } /** * Execute decision framework */ async executeDecisionFramework(goal, context) { const framework = this.extensions?.decisionFramework || AGENT_DEFAULTS.DECISION_FRAMEWORK; switch (framework) { case 'rule_based': return this.ruleBasedDecision(goal, context); case 'ml_based': // Placeholder for ML-based decisions logger.warn('ML-based decisions not yet implemented, falling back to rule-based'); return this.ruleBasedDecision(goal, context); case 'programmatic': return this.programmaticDecision(goal, context); case 'hybrid': // Combine multiple frameworks const ruleDecision = await this.ruleBasedDecision(goal, context); const progDecision = await this.programmaticDecision(goal, context); // Average confidence and combine reasoning return { action: ruleDecision.confidence > progDecision.confidence ? ruleDecision.action : progDecision.action, reasoning: `Rule-based: ${ruleDecision.reasoning}\nProgrammatic: ${progDecision.reasoning}`, confidence: (ruleDecision.confidence + progDecision.confidence) / 2 }; default: throw new Error(`Unknown decision framework: ${framework}`); } } /** * Rule-based decision making */ async ruleBasedDecision(goal, context) { const rules = [ // High priority + high urgency = immediate action { condition: (g) => g.priority === this.ruleEngineConfig.ruleBased.priority.critical && g.urgency > this.ruleEngineConfig.ruleBased.urgencyThresholds.immediate, action: this.ruleEngineConfig.actions.executeImmediately, reasoning: 'Critical priority with high urgency requires immediate action', confidence: this.ruleEngineConfig.ruleBased.confidence.critical }, // Blocked by dependencies { condition: (g, ctx) => { if (!g.dependencies || g.dependencies.length === 0) return false; const blockedDeps = g.dependencies.filter(depId => { const dep = this.state.goals.find(goal => goal.id === depId); return dep && dep.status !== 'completed'; }); return blockedDeps.length > 0; }, action: this.ruleEngineConfig.actions.waitForDependencies, reasoning: 'Goal has incomplete dependencies', confidence: this.ruleEngineConfig.ruleBased.confidence.blocked }, // Risk assessment { condition: (g) => g.riskLevel === 'high' && this.extensions?.riskTolerance === 'conservative', action: this.ruleEngineConfig.actions.requestApproval, reasoning: 'High risk goal requires approval in conservative mode', confidence: this.ruleEngineConfig.ruleBased.confidence.riskApproval }, // Resource availability { condition: (g, ctx) => { const activeGoals = this.state.goals.filter(goal => goal.status === 'in_progress').length; const maxConcurrent = this.metadata.maxConcurrentGoals || AGENT_DEFAULTS.MAX_CONCURRENT_GOALS; return activeGoals >= maxConcurrent; }, action: this.ruleEngineConfig.actions.queueForLater, reasoning: 'Maximum concurrent goals reached', confidence: this.ruleEngineConfig.ruleBased.confidence.resourceLimit }, // Default action { condition: () => true, action: this.ruleEngineConfig.actions.proceedWithGoal, reasoning: 'No blocking conditions found', confidence: this.ruleEngineConfig.ruleBased.confidence.default } ]; // Evaluate rules in order for (const rule of rules) { if (rule.condition(goal, context)) { return { action: rule.action, reasoning: rule.reasoning, confidence: rule.confidence }; } } // Fallback (should not reach here) return { action: this.ruleEngineConfig.actions.reviewManually, reasoning: 'No applicable rules found', confidence: 0.5 }; } /** * Programmatic decision making */ async programmaticDecision(goal, context) { // Calculate decision score based on multiple factors let score = 0; const factors = []; // Factor 1: Eisenhower matrix if (goal.eisenhowerQuadrant === 'do_first') { score += this.ruleEngineConfig.programmatic.scoreWeights.eisenhower.doFirst; factors.push('High importance and urgency (Do First quadrant)'); } else if (goal.eisenhowerQuadrant === 'schedule') { score += this.ruleEngineConfig.programmatic.scoreWeights.eisenhower.schedule; factors.push('High importance, low urgency (Schedule quadrant)'); } else if (goal.eisenhowerQuadrant === 'delegate') { score += this.ruleEngineConfig.programmatic.scoreWeights.eisenhower.delegate; factors.push('Low importance, high urgency (Delegate quadrant)'); } // Factor 2: Risk level if (goal.riskLevel === 'low') { score += this.ruleEngineConfig.programmatic.scoreWeights.risk.low; factors.push('Low risk'); } else if (goal.riskLevel === 'medium') { score += this.ruleEngineConfig.programmatic.scoreWeights.risk.medium; factors.push('Medium risk'); } else { score += this.ruleEngineConfig.programmatic.scoreWeights.risk.high; factors.push('High risk penalty'); } // Factor 3: Dependencies if (!goal.dependencies || goal.dependencies.length === 0) { score += this.ruleEngineConfig.programmatic.scoreWeights.noDependencies; factors.push('No dependencies'); } // Factor 4: Estimated effort if (goal.estimatedEffort && goal.estimatedEffort <= this.ruleEngineConfig.programmatic.quickWinHours) { score += this.ruleEngineConfig.programmatic.scoreWeights.quickWin; factors.push(`Quick win (≤${this.ruleEngineConfig.programmatic.quickWinHours} hours)`); } // Factor 5: Previous success rate const previousDecisions = this.state.decisions.filter(d => d.outcome === 'success'); const successRate = previousDecisions.length / Math.max(this.state.decisions.length, 1); if (successRate > this.ruleEngineConfig.programmatic.successRateThreshold) { score += this.ruleEngineConfig.programmatic.scoreWeights.successBonus; factors.push(`High success rate (${(successRate * 100).toFixed(0)}%)`); } // Determine action based on score let action; let confidence; if (score >= this.ruleEngineConfig.programmatic.actionThresholds.executeImmediately) { action = this.ruleEngineConfig.actions.executeImmediately; confidence = this.ruleEngineConfig.programmatic.confidenceLevels.executeImmediately; } else if (score >= this.ruleEngineConfig.programmatic.actionThresholds.proceed) { action = this.ruleEngineConfig.actions.proceedWithGoal; confidence = this.ruleEngineConfig.programmatic.confidenceLevels.proceed; } else if (score >= this.ruleEngineConfig.programmatic.actionThresholds.schedule) { action = this.ruleEngineConfig.actions.scheduleForLater; confidence = this.ruleEngineConfig.programmatic.confidenceLevels.schedule; } else { action = this.ruleEngineConfig.actions.reviewAndRevise; confidence = this.ruleEngineConfig.programmatic.confidenceLevels.review; } return { action, reasoning: `Score: ${score}. Factors: ${factors.join(', ')}`, confidence }; } /** * Assess risk for a decision */ assessRisk(decision, goal, context) { const factors = []; let riskScore = 0; // Check for immediate execution with high risk if (decision.action === 'execute_immediately' && goal.riskLevel === 'high') { factors.push('Immediate execution of high-risk goal'); riskScore += 30; } // Check for low confidence decisions if (decision.confidence < 0.6) { factors.push('Low decision confidence'); riskScore += 20; } // Check for complex dependencies if (goal.dependencies && goal.dependencies.length > 3) { factors.push('Complex dependency chain'); riskScore += 15; } // Check for aggressive risk tolerance with high-risk goal if (this.extensions?.riskTolerance === 'aggressive' && goal.riskLevel === 'high') { factors.push('Aggressive risk tolerance with high-risk goal'); riskScore += 25; } // Determine risk level let level; const mitigations = []; if (riskScore >= 50) { level = 'high'; mitigations.push('Request human approval'); mitigations.push('Create backup plan'); mitigations.push('Monitor closely'); } else if (riskScore >= 25) { level = 'medium'; mitigations.push('Add checkpoints'); mitigations.push('Review after completion'); } else { level = 'low'; mitigations.push('Standard monitoring'); } return { level, factors, mitigations: mitigations.length > 0 ? mitigations : undefined }; } /** * Calculate Eisenhower quadrant */ calculateEisenhowerQuadrant(importance, urgency) { if (importance >= 7 && urgency >= 7) { return 'do_first'; } else if (importance >= 7 && urgency < 7) { return 'schedule'; } else if (importance < 7 && urgency >= 7) { return 'delegate'; } else { return 'eliminate'; } } /** * Validate goal for security threats */ validateGoalSecurity(goal) { // Check for command injection patterns const dangerousPatterns = [ /system\s*\(/i, /exec\s*\(/i, /eval\s*\(/i, /require\s*\(/i, /import\s*\(/i, /\$\{.*\}/, /`.*`/, /process\.\w+/i, /child_process/i ]; for (const pattern of dangerousPatterns) { if (pattern.test(goal)) { return { safe: false, reason: 'Contains potentially dangerous code patterns' }; } } // Check for social engineering attempts const socialEngineeringPatterns = [ /password|credential|secret|token|key/i, /hack|exploit|breach|attack/i, /delete\s+all|destroy|wipe|erase\s+everything/i, /steal|theft|rob/i ]; for (const pattern of socialEngineeringPatterns) { if (pattern.test(goal)) { return { safe: false, reason: 'Contains potentially harmful intent' }; } } return { safe: true }; } /** * Get agent state */ getState() { return { ...this.state }; } /** * Update agent context */ updateContext(key, value) { const sanitizedKey = sanitizeInput(key, 50); // Validate context size const contextStr = JSON.stringify({ ...this.state.context, [sanitizedKey]: value }); if (contextStr.length > AGENT_LIMITS.MAX_CONTEXT_LENGTH) { throw new Error(`Context size exceeds maximum of ${AGENT_LIMITS.MAX_CONTEXT_LENGTH} characters`); } this.state.context[sanitizedKey] = value; this.isDirtyState = true; this.markDirty(); } /** * Complete a goal */ completeGoal(goalId, outcome = 'success') { const goal = this.state.goals.find(g => g.id === goalId); if (!goal) { throw new Error(`Goal ${goalId} not found`); } goal.status = outcome === 'success' ? 'completed' : 'failed'; goal.completedAt = new Date(); goal.updatedAt = new Date(); // Update actual effort if it was being tracked if (goal.estimatedEffort && goal.createdAt) { const hoursElapsed = (goal.completedAt.getTime() - goal.createdAt.getTime()) / (1000 * 60 * 60); goal.actualEffort = Math.round(hoursElapsed * 10) / 10; } // Update decision outcomes const decisions = this.state.decisions.filter(d => d.goalId === goalId); decisions.forEach(d => { if (!d.outcome) { d.outcome = outcome; } }); this.isDirtyState = true; this.markDirty(); logger.info(`Goal ${goalId} completed with outcome: ${outcome}`); } /** * Detect dependency cycles in goal dependencies * MEDIUM PRIORITY IMPROVEMENT: Prevents circular dependencies between goals */ detectDependencyCycle(newGoalId, dependencies) { // Build dependency graph including the new goal const visited = new Set(); const recursionStack = new Set(); const path = []; // Helper function to perform DFS const hasCycleDFS = (goalId) => { visited.add(goalId); recursionStack.add(goalId); path.push(goalId); // Get dependencies for current goal let deps = []; if (goalId === newGoalId) { // For the new goal being added, use provided dependencies deps = dependencies; } else { // For existing goals, get from state const goal = this.state.goals.find(g => g.id === goalId); deps = goal?.dependencies || []; } // Check each dependency for (const depId of deps) { if (!visited.has(depId)) { if (hasCycleDFS(depId)) { return true; } } else if (recursionStack.has(depId)) { // Found a cycle - add the repeated node to show the cycle path.push(depId); return true; } } recursionStack.delete(goalId); // Only pop from path if we're not returning a cycle if (!deps.some(depId => recursionStack.has(depId))) { path.pop(); } return false; }; // Check if adding this goal would create a cycle const hasCycle = hasCycleDFS(newGoalId); return { hasCycle, path: hasCycle ? path : [] }; } /** * Get goals by status */ getGoalsByStatus(status) { return this.state.goals.filter(g => g.status === status); } /** * Get goals by quadrant */ getGoalsByQuadrant(quadrant) { return this.state.goals.filter(g => g.eisenhowerQuadrant === quadrant); } /** * Calculate agent performance metrics * MEDIUM PRIORITY IMPROVEMENT: Enhanced to include decision timing metrics */ getPerformanceMetrics() { const completedGoals = this.state.goals.filter(g => g.status === 'completed'); const failedGoals = this.state.goals.filter(g => g.status === 'failed'); const inProgressGoals = this.state.goals.filter(g => g.status === 'in_progress'); const totalCompleted = completedGoals.length + failedGoals.length; const successRate = totalCompleted > 0 ? completedGoals.length / totalCompleted : 0; // Calculate average completion time let totalTime = 0; let timeCount = 0; completedGoals.forEach(goal => { if (goal.completedAt && goal.createdAt) { totalTime += goal.completedAt.getTime() - goal.createdAt.getTime(); timeCount++; } }); const averageCompletionTime = timeCount > 0 ? totalTime / timeCount / (1000 * 60 * 60) : 0; // in hours // Calculate decision accuracy const decisionsWithOutcome = this.state.decisions.filter(d => d.outcome); const successfulDecisions = decisionsWithOutcome.filter(d => d.outcome === 'success'); const decisionAccuracy = decisionsWithOutcome.length > 0 ? successfulDecisions.length / decisionsWithOutcome.length : 0; // Calculate average decision timing metrics const decisionsWithMetrics = this.state.decisions.filter(d => d.performanceMetrics); let avgDecisionTime = 0; let avgFrameworkTime = 0; let avgRiskTime = 0; if (decisionsWithMetrics.length > 0) { const totalDecisionTime = decisionsWithMetrics.reduce((sum, d) => sum + (d.performanceMetrics?.decisionTimeMs || 0), 0); const totalFrameworkTime = decisionsWithMetrics.reduce((sum, d) => sum + (d.performanceMetrics?.frameworkTimeMs || 0), 0); const totalRiskTime = decisionsWithMetrics.reduce((sum, d) => sum + (d.performanceMetrics?.riskAssessmentTimeMs || 0), 0); avgDecisionTime = totalDecisionTime / decisionsWithMetrics.length; avgFrameworkTime = totalFrameworkTime / decisionsWithMetrics.length; avgRiskTime = totalRiskTime / decisionsWithMetrics.length; } return { successRate, averageCompletionTime, goalsCompleted: completedGoals.length, goalsInProgress: inProgressGoals.length, decisionAccuracy, averageDecisionTimeMs: decisionsWithMetrics.length > 0 ? avgDecisionTime : undefined, averageFrameworkTimeMs: decisionsWithMetrics.length > 0 ? avgFrameworkTime : undefined, averageRiskAssessmentTimeMs: decisionsWithMetrics.length > 0 ? avgRiskTime : undefined }; } /** * Validate the agent */ validate() { const result = super.validate(); const errors = result.errors || []; const warnings = result.warnings || []; const suggestions = result.suggestions || []; // Validate decision framework if (this.extensions?.decisionFramework && !DECISION_FRAMEWORKS.includes(this.extensions.decisionFramework)) { errors.push({ field: 'extensions.decisionFramework', message: `Invalid decision framework. Must be one of: ${DECISION_FRAMEWORKS.join(', ')}` }); } // Validate risk tolerance if (this.extensions?.riskTolerance && !RISK_TOLERANCE_LEVELS.includes(this.extensions.riskTolerance)) { errors.push({ field: 'extensions.riskTolerance', message: `Invalid risk tolerance. Must be one of: ${RISK_TOLERANCE_LEVELS.join(', ')}` }); } // Validate state size const stateSize = JSON.stringify(this.state).length; if (stateSize > AGENT_LIMITS.MAX_STATE_SIZE) { errors.push({ field: 'state', message: `State size (${stateSize} bytes) exceeds maximum of ${AGENT_LIMITS.MAX_STATE_SIZE} bytes` }); } // Check for orphaned dependencies const allGoalIds = new Set(this.state.goals.map(g => g.id)); this.state.goals.forEach(goal => { if (goal.dependencies) { goal.dependencies.forEach(depId => { if (!allGoalIds.has(depId)) { warnings.push({ field: `goal.${goal.id}.dependencies`, message: `Dependency ${depId} not found`, severity: 'medium' }); } }); } }); // Suggestions if (this.state.goals.length === 0) { suggestions.push('Add some goals to make the agent functional'); } if (!this.extensions?.specializations || this.extensions.specializations.length === 0) { suggestions.push('Consider adding specializations to improve agent focus'); } const metrics = this.getPerformanceMetrics(); if (metrics.successRate < 0.5 && metrics.goalsCompleted > 5) { suggestions.push('Low success rate detected. Consider reviewing goal difficulty or decision framework'); } return { valid: errors.length === 0, errors: errors.length > 0 ? errors : undefined, warnings: warnings.length > 0 ? warnings : undefined, suggestions: suggestions.length > 0 ? suggestions : undefined }; } /** * Serialize agent including state */ serialize() { const data = { ...JSON.parse(super.serialize()), state: this.state }; return JSON.stringify(data, null, 2); } /** * Deserialize agent including state */ deserialize(data) { const validationResult = UnicodeValidator.normalize(data); const parsed = JSON.parse(validationResult.normalizedContent); // Deserialize base properties super.deserialize(JSON.stringify({ id: parsed.id, type: parsed.type, version: parsed.version, metadata: parsed.metadata, references: parsed.references, extensions: parsed.extensions, ratings: parsed.ratings })); // Deserialize state with validation if (parsed.state) { // Validate state size const stateStr = JSON.stringify(parsed.state); if (stateStr.length > AGENT_LIMITS.MAX_STATE_SIZE) { throw new Error(`State size exceeds maximum of ${AGENT_LIMITS.MAX_STATE_SIZE} bytes`); } // Restore dates if (parsed.state.goals) { parsed.state.goals.forEach((goal) => { goal.createdAt = new Date(goal.createdAt); goal.updatedAt = new Date(goal.updatedAt); if (goal.completedAt) { goal.completedAt = new Date(goal.completedAt); } }); } if (parsed.state.decisions) { parsed.state.decisions.forEach((decision) => { decision.timestamp = new Date(decision.timestamp); }); } if (parsed.state.lastActive) { parsed.state.lastActive = new Date(parsed.state.lastActive); } this.state = parsed.state; } this.isDirtyState = false; } /** * Agent activation */ async activate() { await super.activate(); // Update session tracking this.state.sessionCount++; this.state.lastActive = new Date(); this.isDirtyState = true; // Log activation logger.info(`Agent ${this.metadata.name} activated`, { sessionCount: this.state.sessionCount, activeGoals: this.getGoalsByStatus('in_progress').length }); } /** * Agent deactivation */ async deactivate() { // Save any pending state if (this.isDirtyState) { logger.debug(`Agent ${this.metadata.name} has unsaved state changes`); } await super.deactivate(); } /** * Check if agent needs state persistence */ needsStatePersistence() { return this.isDirtyState; } /** * Mark state as persisted */ markStatePersisted() { this.isDirtyState = false; } /** * Create a goal from a template * LOW PRIORITY IMPROVEMENT: Goal template system for common patterns */ addGoalFromTemplate(templateId, customFields) { // Apply template to get goal data const goalData = applyGoalTemplate(templateId, customFields); // Create goal using the template data return this.addGoal(goalData); } /** * Get template recommendations based on goal description */ getGoalTemplateRecommendations(description) { return recommendGoalTemplate(description); } /** * Validate a goal against its template */ validateGoalTemplate(goalId) { const goal = this.state.goals.find(g => g.id === goalId); if (!goal) { return { valid: false, errors: ['Goal not found'] }; } // If goal was created from template, validate against it const templateId = goal.templateId; return validateGoalAgainstTemplate(goal, templateId); } /** * Update rule engine configuration */ updateRuleEngineConfig(config) { this.ruleEngineConfig = validateRuleEngineConfig({ ...this.ruleEngineConfig, ...config }); // Update extensions this.extensions = { ...this.extensions, ruleEngineConfig: this.ruleEngineConfig }; this.markDirty(); } /** * Get current rule engine configuration */ getRuleEngineConfig() { return { ...this.ruleEngineConfig }; } } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiQWdlbnQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvZWxlbWVudHMvYWdlbnRzL0FnZW50LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOzs7Ozs7Ozs7O0dBVUc7QUFFSCxPQUFPLEVBQUUsV0FBVyxFQUFFLE1BQU0sbUJBQW1CLENBQUM7QUFFaEQsT0FBTyxFQUFFLFdBQVcsRUFBRSxNQUFNLDBCQUEwQixDQUFDO0FBQ3ZELE9BQU8sRUFBRSxhQUFhLEVBQUUsTUFBTSxrQ0FBa0MsQ0FBQztBQUNqRSxPQUFPLEVBQUUsZ0JBQWdCLEVBQUUsTUFBTSwrQ0FBK0MsQ0FBQztBQUNqRixPQUFPLEVBQUUsZUFBZSxFQUFFLE1BQU0sbUNBQW1DLENBQUM7QUFDcEUsT0FBTyxFQUFFLE1BQU0sRUFBRSxNQUFNLHVCQUF1QixDQUFDO0FBTy9DLE9BQU8sRUFDTCxZQUFZLEVBQ1osY0FBYyxFQUNkLG1CQUFtQixFQUNuQixxQkFBcUIsRUFDdEIsTUFBTSxnQkFBZ0IsQ0FBQztBQUN4QixPQUFPLEVBR0wsd0JBQXdCLEVBQ3pCLE1BQU0sdUJBQXVCLENBQUM7QUFDL0IsT0FBTyxFQUNMLGlCQUFpQixFQUNqQixxQkFBcUIsRUFDckIsMkJBQTJCLEVBQzVCLE1BQU0sb0JBQW9CLENBQUM7QUFFNUIsTUFBTSxPQUFPLEtBQU0sU0FBUSxXQUFXO0lBQzVCLEtBQUssQ0FBYTtJQUNsQixZQUFZLEdBQVksS0FBSyxDQUFDO0lBQzlCLGdCQUFnQixDQUFtQjtJQUUzQyxZQUFZLFFBQWdDO1FBQzFDLHNCQUFzQjtRQUN0QixNQUFNLGlCQUFpQixHQUEyQjtZQUNoRCxHQUFHLFFBQVE7WUFDWCxJQUFJLEVBQUUsUUFBUSxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsYUFBYSxDQUFDLGdCQUFnQixDQUFDLFNBQVMsQ0FBQyxRQUFRLENBQUMsSUFBSSxDQUFDLENBQUMsaUJBQWlCLEVBQUUsR0FBRyxDQUFDLENBQUMsQ0FBQyxDQUFDLFNB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