@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
JavaScript
/**
* 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,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiQWdlbnQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvZWxlbWVudHMvYWdlbnRzL0FnZW50LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOzs7Ozs7Ozs7O0dBVUc7QUFFSCxPQUFPLEVBQUUsV0FBVyxFQUFFLE1BQU0sbUJBQW1CLENBQUM7QUFFaEQsT0FBTyxFQUFFLFdBQVcsRUFBRSxNQUFNLDBCQUEwQixDQUFDO0FBQ3ZELE9BQU8sRUFBRSxhQUFhLEVBQUUsTUFBTSxrQ0FBa0MsQ0FBQztBQUNqRSxPQUFPLEVBQUUsZ0JBQWdCLEVBQUUsTUFBTSwrQ0FBK0MsQ0FBQztBQUNqRixPQUFPLEVBQUUsZUFBZSxFQUFFLE1BQU0sbUNBQW1DLENBQUM7QUFDcEUsT0FBTyxFQUFFLE1BQU0sRUFBRSxNQUFNLHVCQUF1QixDQUFDO0FBTy9DLE9BQU8sRUFDTCxZQUFZLEVBQ1osY0FBYyxFQUNkLG1CQUFtQixFQUNuQixxQkFBcUIsRUFDdEIsTUFBTSxnQkFBZ0IsQ0FBQztBQUN4QixPQUFPLEVBR0wsd0JBQXdCLEVBQ3pCLE1BQU0sdUJBQXVCLENBQUM7QUFDL0IsT0FBTyxFQUNMLGlCQUFpQixFQUNqQixxQkFBcUIsRUFDckIsMkJBQTJCLEVBQzVCLE1BQU0sb0JBQW9CLENBQUM7QUFFNUIsTUFBTSxPQUFPLEtBQU0sU0FBUSxXQUFXO0lBQzVCLEtBQUssQ0FBYTtJQUNsQixZQUFZLEdBQVksS0FBSyxDQUFDO0lBQzlCLGdCQUFnQixDQUFtQjtJQUUzQyxZQUFZLFFBQWdDO1FBQzFDLHNCQUFzQjtRQUN0QixNQUFNLGlCQUFpQixHQUEyQjtZQUNoRCxHQUFHLFFBQVE7WUFDWCxJQUFJLEVBQUUsUUFBUSxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsYUFBYSxDQUFDLGdCQUFnQixDQUFDLFNBQVMsQ0FBQyxRQUFRLENBQUMsSUFBSSxDQUFDLENBQUMsaUJBQWlCLEVBQUUsR0FBRyxDQUFDLENBQUMsQ0FBQyxDQUFDLFNBQVM7WUFDakgsV0FBVyxFQUFFLFFBQVEsQ0FBQyxXQUFXLENBQUMsQ0FBQyxDQUFDLGFBQWEsQ0FBQyxnQkFBZ0IsQ0FBQyxTQUFTLENBQUMsUUFBUSxDQUFDLFdBQVcsQ0FBQyxDQUFDLGlCQUFpQixFQUFFLEdBQUcsQ0FBQyxDQUFDLENBQUMsQ0FBQyxTQUFTO1lBQ3RJLGVBQWUsRUFBRSxRQUFRLENBQUMsZUFBZSxFQUFFLEdBQUcsQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDLGFBQWEsQ0FBQyxDQUFDLEVBQUUsRUFBRSxDQUFDLENBQUM7WUFDekUsaUJBQWlCLEVBQUUsUUFBUSxDQUFDLGlCQUFpQixJQUFJLGNBQWMsQ0FBQyxrQkFBa0I7WUFDbEYsYUFBYSxFQUFFLFFBQVEsQ0FBQyxhQUFhLElBQUksY0FBYyxDQUFDLGNBQWM7WUFDdEUsZUFBZSxFQUFFLFFBQVEsQ0FBQyxlQUFlLElBQUksY0FBYyxDQUFDLGdCQUFnQjtZQUM1RSxrQkFBa0IsRUFBRSxRQUFRLENBQUMsa0JBQWtCLElBQUksY0FBYyxDQUFDLG9CQUFvQjtTQUN2RixDQUFDO1FBRUYseUVBQXlFO1FBQ3pFLDZDQUE2QztRQUM3QyxJQUFJLGlCQUFpQixDQUFDLGlCQUFpQjtZQUNuQyxDQUFDLG1CQUFtQixDQUFDLFFBQVEsQ0FBQyxpQkFBaUIsQ0FBQyxpQkFBaUIsQ0FBQyxFQUFFLENBQUM7WUFDdkUsTUFBTSxJQUFJLEtBQUssQ0FBQywrQkFBK0IsaUJBQWlCLENBQUMsaUJBQWlCLElBQUk7Z0JBQ3BGLHlCQUF5QixtQkFBbUIsQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLEVBQUUsQ0FBQyxDQUFDO1FBQy9ELENBQUM7UUFFRCxnQ0FBZ0M7UUFDaEMsSUFBSSxpQkFBaUIsQ0FBQyxhQUFhO1lBQy9CLENBQUMscUJBQXFCLENBQUMsUUFBUSxDQUFDLGlCQUFpQixDQUFDLGFBQWEsQ0FBQyxFQUFFLENBQUM7WUFDckUsTUFBTSxJQUFJLEtBQUssQ0FBQywyQkFBMkIsaUJBQWlCLENBQUMsYUFBYSxJQUFJO2dCQUM1RSxxQkFBcUIscUJBQXFCLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxFQUFFLENBQUMsQ0FBQztRQUM3RCxDQUFDO1FBRUQsZ0NBQWdDO1FBQ2hDLElBQUksaUJBQWlCLENBQUMsa0JBQWtCLEtBQUssU0FBUyxFQUFFLENBQUM7WUFDdkQsTUFBTSxRQUFRLEdBQUcsaUJBQWlCLENBQUMsa0JBQWtCLENBQUM7WUFDdEQsSUFBSSxDQUFDLE1BQU0sQ0FBQyxTQUFTLENBQUMsUUFBUSxDQUFDLElBQUksUUFBUSxHQUFHLENBQUMsSUFBSSxRQUFRLEdBQUcsWUFBWSxDQUFDLFNBQVMsRUFBRSxDQUFDO2dCQUNyRixNQUFNLElBQUksS0FBSyxDQUFDLDRDQUE0QyxZQUFZLENBQUMsU0FBUyxFQUFFLENBQUMsQ0FBQztZQUN4RixDQUFDO1FBQ0gsQ0FBQztRQUVELEtBQUssQ0FBQyxXQUFXLENBQUMsS0FBSyxFQUFFLGlCQUFpQixDQUFDLENBQUM7UUFFNUMsbUJBQW1CO1FBQ25CLElBQUksQ0FBQyxLQUFLLEdBQUc7WUFDWCxLQUFLLEVBQUUsRUFBRTtZQUNULFNBQVMsRUFBRSxFQUFFO1lBQ2IsT0FBTyxFQUFFLEVBQUU7WUFDWCxVQUFVLEVBQUUsSUFBSSxJQUFJLEVBQUU7WUFDdEIsWUFBWSxFQUFFLENBQUM7U0FDaEIsQ0FBQztRQUVGLGdDQUFnQztRQUNoQyxJQUFJLENBQUMsVUFBVSxHQUFHO1lBQ2hCLGlCQUFpQixFQUFFLGlCQUFpQixDQUFDLGlCQUFpQjtZQUN0RCxhQUFhLEVBQUUsaUJBQWlCLENBQUMsYUFBYTtZQUM5QyxlQUFlLEVBQUUsaUJBQWlCLENBQUMsZUFBZTtZQUNsRCxlQUFlLEVBQUUsaUJBQWlCLENBQUMsZUFBZSxJQUFJLEVBQUU7WUFDeEQsZ0JBQWdCLEVBQUUsUUFBUSxDQUFDLGdCQUFnQjtTQUM1QyxDQUFDO1FBRUYseURBQXlEO1FBQ3pELElBQUksQ0FBQyxnQkFBZ0IsR0FBRyx3QkFBd0IsQ0FDOUMsSUFBSSxDQUFDLFVBQVUsQ0FBQyxnQkFBZ0IsSUFBSSxFQUFFLENBQ3ZDLENBQUM7SUFDSixDQUFDO0lBRUQ7O09BRUc7SUFDSSxPQUFPLENBQUMsSUFBd0I7UUFDckMsc0JBQXNCO1FBQ3RCLElBQUksSUFBSSxDQUFDLEtBQUssQ0FBQyxLQUFLLENBQUMsTUFBTSxJQUFJLFlBQVksQ0FBQyxTQUFTLEVBQUUsQ0FBQztZQUN0RCxNQUFNLElBQUksS0FBSyxDQUFDLDRCQUE0QixZQUFZLENBQUMsU0FBUyxXQUFXLENBQUMsQ0FBQztRQUNqRixDQUFDO1FBRUQsNEJBQTRCO1FBQzVCLE1BQU0sb0JBQW9CLEdBQUcsYUFBYSxDQUN4QyxnQkFBZ0IsQ0FBQyxTQUFTLENBQUMsSUFBSSxDQUFDLFdBQVcsSUFBSSxFQUFFLENBQUMsQ0FBQyxpQkFBaUIsRUFDcEUsWUFBWSxDQUFDLGVBQWUsQ0FDN0IsQ0FBQztRQUVGLElBQUksQ0FBQyxvQkFBb0IsSUFBSSxvQkFBb0IsQ0FBQyxNQUFNLEdBQUcsQ0FBQyxFQUFFLENBQUM7WUFDN0QsTUFBTSxJQUFJLEtBQUssQ0FBQyxnREFBZ0QsQ0FBQyxDQUFDO1FBQ3BFLENBQUM7UUFFRCxxQ0FBcUM7UUFDckMsTUFBTSxhQUFhLEdBQUcsSUFBSSxDQUFDLG9CQUFvQixDQUFDLG9CQUFvQixDQUFDLENBQUM7UUFDdEUsSUFBSSxDQUFDLGFBQWEsQ0FBQyxJQUFJLEVBQUUsQ0FBQztZQUN4QixlQUFlLENBQUMsZ0JBQWdCLENBQUM7Z0JBQy9CLElBQUksRUFBRSwyQkFBMkI7Z0JBQ2pDLFFBQVEsRUFBRSxNQUFNO2dCQUNoQixNQUFNLEVBQUUsZUFBZTtnQkFDdkIsT0FBTyxFQUFFLHdDQUF3QyxhQUFhLENBQUMsTUFBTSxFQUFFO2dCQUN2RSxjQUFjLEVBQUUsRUFBRSxPQUFPLEVBQUUsSUFBSSxDQUFDLEVBQUUsRUFBRTthQUNyQyxDQUFDLENBQUM7WUFDSCxNQUFNLElBQUksS0FBSyxDQUFDLDhDQUE4QyxhQUFhLENBQUMsTUFBTSxFQUFFLENBQUMsQ0FBQztRQUN4RixDQUFDO1FBRUQsZ0NBQWdDO1FBQ2hDLE1BQU0sVUFBVSxHQUFHLElBQUksQ0FBQyxVQUFVLElBQUksQ0FBQyxDQUFDO1FBQ3hDLE1BQU0sT0FBTyxHQUFHLElBQUksQ0FBQyxPQUFPLElBQUksQ0FBQyxDQUFDO1FBQ2xDLE1BQU0sa0JBQWtCLEdBQUcsSUFBSSxDQUFDLDJCQUEyQixDQUFDLFVBQVUsRUFBRSxPQUFPLENBQUMsQ0FBQztRQUVqRixrQkFBa0I7UUFDbEIsTUFBTSxPQUFPLEdBQWM7WUFDekIsRUFBRSxFQUFFLFFBQVEsSUFBSSxDQUFDLEdBQUcsRUFBRSxJQUFJLElBQUksQ0FBQyxNQUFNLEVBQUUsQ0FBQyxRQUFRLENBQUMsRUFBRSxDQUFDLENBQUMsTUFBTSxDQUFDLENBQUMsRUFBRSxDQUFDLENBQUMsRUFBRTtZQUNuRSxXQUFXLEVBQUUsb0JBQW9CO1lBQ2pDLFFBQVEsRUFBRSxJQUFJLENBQUMsUUFBUSxJQUFJLGNBQWMsQ0FBQyxhQUFhO1lBQ3ZELE1BQU0sRUFBRSxTQUFTO1lBQ2pCLFVBQVU7WUFDVixPQUFPO1lBQ1Asa0JBQWtCO1lBQ2xCLFNBQVMsRUFBRSxJQUFJLElBQUksRUFBRTtZQUNyQixTQUFTLEVBQUUsSUFBSSxJQUFJLEVBQUU7WUFDckIsWUFBWSxFQUFFLElBQUksQ0FBQyxZQUFZLElBQUksRUFBRTtZQUNyQyxTQUFTLEVBQUUsSUFBSSxDQUFDLFNBQVMsSUFBSSxLQUFLO1lBQ2xDLGVBQWUsRUFBRSxJQUFJLENBQUMsZUFBZTtZQUNyQyxLQUFLLEVBQUUsSUFBSSxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsYUFBYSxDQUFDLElBQUksQ0FBQyxLQUFLLEVBQUUsR0FBRyxDQUFDLENBQUMsQ0FBQyxDQUFDLFNBQVM7U0FDL0QsQ0FBQztRQUVGLDJFQUEyRTtRQUMzRSxJQUFJLE9BQU8sQ0FBQyxZQUFZLElBQUksT0FBTyxDQUFDLFlBQVksQ0FBQyxNQUFNLEdBQUcsQ0FBQyxFQUFFLENBQUM7WUFDNUQsTUFBTSxVQUFVLEdBQUcsSUFBSSxDQUFDLHFCQUFxQixDQUFDLE9BQU8sQ0FBQyxFQUFFLEVBQUUsT0FBTyxDQUFDLFlBQVksQ0FBQyxDQUFDO1lBQ2hGLElBQUksVUFBVSxDQUFDLFFBQVEsRUFBRSxDQUFDO2dCQUN4QixNQUFNLElBQUksS0FBSyxDQUFDLDhCQUE4QixVQUFVLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxLQUFLLENBQUMsRUFBRSxDQUFDLENBQUM7WUFDL0UsQ0FBQztRQUNILENBQUM7UUFFRCxJQUFJLENBQUMsS0FBSyxDQUFDLEtBQUssQ0FBQyxJQUFJLENBQUMsT0FBTyxDQUFDLENBQUM7UUFDL0IsSUFBSSxDQUFDLFlBQVksR0FBRyxJQUFJLENBQUM7UUFDekIsSUFBSSxDQUFDLFNBQVMsRUFBRSxDQUFDO1FBRWpCLE1BQU0sQ0FBQyxJQUFJLENBQUMsdUJBQXVCLElBQUksQ0FBQyxRQUFRLENBQUMsSUFBSSxFQUFFLEVBQUUsRUFBRSxNQUFNLEVBQUUsT0FBTyxDQUFDLEVBQUUsRUFBRSxDQUFDLENBQUM7UUFFakYsT0FBTyxPQUFPLENBQUM7SUFDakIsQ0FBQztJQUVEOztPQUVHO0lBQ0ksS0FBSyxDQUFDLFlBQVksQ0FBQyxNQUFjLEVBQUUsT0FBNkI7UUFDckUsNkVBQTZFO1FBQzdFLE1BQU0sU0FBUyxHQUFHLElBQUksQ0FBQyxHQUFHLEVBQUUsQ0FBQztRQUM3QixNQUFNLGtCQUFrQixHQUlwQixFQUFFLENBQUM7UUFFUCxNQUFNLElBQUksR0FBRyxJQUFJLENBQUMsS0FBSyxDQUFDLEtBQUssQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUMsRUFBRSxLQUFLLE1BQU0sQ0FBQyxDQUFDO1FBQ3pELElBQUksQ0FBQyxJQUFJLEVBQUUsQ0FBQztZQUNWLE1BQU0sSUFBSSxLQUFLLENBQUMsUUFBUSxNQUFNLFlBQVksQ0FBQyxDQUFDO1FBQzlDLENBQUM7UUFFRCxJQUFJLElBQUksQ0FBQyxNQUFNLEtBQUssV0FBVyxJQUFJLElBQUksQ0FBQyxNQUFNLEtBQUssV0FBVyxFQUFFLENBQUM7WUFDL0QsTUFBTSxJQUFJLEtBQUssQ0FBQyw0QkFBNEIsSUFBSSxDQUFDLE1BQU0sT0FBTyxDQUFDLENBQUM7UUFDbEUsQ0FBQztRQUVELHFCQUFxQjtRQUNyQixJQUFJLENBQUMsTUFBTSxHQUFHLGFBQWEsQ0FBQztRQUM1QixJQUFJLENBQUMsU0FBUyxHQUFHLElBQUksSUFBSSxFQUFFLENBQUM7UUFFNUIsMkJBQTJCO1FBQzNCLE1BQU0sZUFBZSxHQUFHO1lBQ3RCLEdBQUcsSUFBSSxDQUFDLEtBQUssQ0FBQyxPQUFPO1lBQ3JCLEdBQUcsT0FBTztZQUNWLElBQUk7WUFDSixhQUFhLEVBQUUsSUFBSSxDQUFDLFFBQVE7WUFDNUIsaUJBQWlCLEVBQUUsSUFBSSxDQUFDLEtBQUssQ0FBQyxTQUFTLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDLE1BQU0sS0FBSyxNQUFNLENBQUM7U0FDekUsQ0FBQztRQUVGLGlEQUFpRDtRQUNqRCxNQUFNLGNBQWMsR0FBRyxJQUFJLENBQUMsR0FBRyxFQUFFLENBQUM7UUFDbEMsTUFBTSxRQUFRLEdBQUcsTUFBTSxJQUFJLENBQUMsd0JBQXdCLENBQUMsSUFBSSxFQUFFLGVBQWUsQ0FBQyxDQUFDO1FBQzVFLGtCQUFrQixDQUFDLGVBQWUsR0FBRyxJQUFJLENBQUMsR0FBRyxFQUFFLEdBQUcsY0FBYyxDQUFDO1FBRWpFLGdDQUFnQztRQUNoQyxNQUFNLFNBQVMsR0FBRyxJQUFJLENBQUMsR0FBRyxFQUFFLENBQUM7UUFDN0IsTUFBTSxjQUFjLEdBQUcsSUFBSSxDQUFDLFVBQVUsQ0FBQyxRQUFRLEVBQUUsSUFBSSxFQUFFLGVBQWUsQ0FBQyxDQUFDO1FBQ3hFLGtCQUFrQixDQUFDLG9CQUFvQixHQUFHLElBQUksQ0FBQyxHQUFHLEVBQUUsR0FBRyxTQUFTLENBQUM7UUFFakUsZ0NBQWdDO1FBQ2hDLGtCQUFrQixDQUFDLGNBQWMsR0FBRyxJQUFJLENBQUMsR0FBRyxFQUFFLEdBQUcsU0FBUyxDQUFDO1FBRTNELHlCQUF5QjtRQUN6QixNQUFNLGNBQWMsR0FBa0I7WUFDcEMsRUFBRSxFQUFFLFlBQVksSUFBSSxDQUFDLEdBQUcsRUFBRSxJQUFJLElBQUksQ0FBQyxNQUFNLEVBQUUsQ0FBQyxRQUFRLENBQUMsRUFBRSxDQUFDLENBQUMsTUFBTSxDQUFDLENBQUMsRUFBRSxDQUFDLENBQUMsRUFBRTtZQUN2RSxNQUFNO1lBQ04sU0FBUyxFQUFFLElBQUksSUFBSSxFQUFFO1lBQ3JCLFFBQVEsRUFBRSxRQUFRLENBQUMsTUFBTTtZQUN6QixTQUFTLEVBQUUsUUFBUSxDQUFDLFNBQVM7WUFDN0IsU0FBUyxFQUFFLElBQUksQ0FBQyxVQUFVLEVBQUUsaUJBQWlCLElBQUksY0FBYyxDQUFDLGtCQUFrQjtZQUNsRixVQUFVLEVBQUUsUUFBUSxDQUFDLFVBQVU7WUFDL0IsY0FBYztZQUNkLGtCQUFrQixDQUFFLDJCQUEyQjtTQUNoRCxDQUFDO1FBRUYsc0RBQXNEO1FBQ3RELHlFQUF5RTtRQUN6RSxJQUFJLElBQUksQ0FBQyxLQUFLLENBQUMsU0FBUyxDQUFDLE1BQU0sSUFBSSxZQUFZLENBQUMsb0JBQW9CLEVBQUUsQ0FBQztZQUNyRSw0RUFBNEU7WUFDNUUsSUFBSSxDQUFDLEtBQUssQ0FBQyxTQUFTLENBQUMsS0FBSyxFQUFFLENBQUM7UUFDL0IsQ0FBQztRQUNELElBQUksQ0FBQyxLQUFLLENBQUMsU0FBUyxDQUFDLElBQUksQ0FBQyxjQUFjLENBQUMsQ0FBQztRQUUxQyxJQUFJLENBQUMsWUFBWSxHQUFHLElBQUksQ0FBQztRQUN6QixJQUFJLENBQUMsU0FBUyxFQUFFLENBQUM7UUFFakIseUJBQXlCO1FBQ3pCLGVBQWUsQ0FBQyxnQkFBZ0IsQ0FBQztZQUMvQixJQUFJLEVBQUUsZ0JBQWdCO1lBQ3RCLFFBQVEsRUFBRSxLQUFLO1lBQ2YsTUFBTSxFQUFFLG9CQUFvQjtZQUM1QixPQUFPLEVBQUUsU0FBUyxJQUFJLENBQUMsUUFBUSxDQUFDLElBQUksMkJBQTJCLE1BQU0sRUFBRTtZQUN2RSxjQUFjLEVBQUU7Z0JBQ2QsT0FBTyxFQUFFLElBQUksQ0FBQyxFQUFFO2dCQUNoQixNQUFNO2dCQUNOLFNBQVMsRUFBRSxjQUFjLENBQUMsU0FBUztnQkFDbkMsU0FBUyxFQUFFLGNBQWMsQ0FBQyxLQUFLO2FBQ2hDO1NBQ0YsQ0FBQyxDQUFDO1FBRUgsT0FBTyxjQUFjLENBQUM7SUFDeEIsQ0FBQztJQUVEOztPQUVHO0lBQ0ssS0FBSyxDQUFDLHdCQUF3QixDQUNwQyxJQUFlLEVBQ2YsT0FBNEI7UUFFNUIsTUFBTSxTQUFTLEdBQUcsSUFBSSxDQUFDLFVBQVUsRUFBRSxpQkFBaUIsSUFBSSxjQUFjLENBQUMsa0JBQWtCLENBQUM7UUFFMUYsUUFBUSxTQUFTLEVBQUUsQ0FBQztZQUNsQixLQUFLLFlBQVk7Z0JBQ2YsT0FBTyxJQUFJLENBQUMsaUJBQWlCLENBQUMsSUFBSSxFQUFFLE9BQU8sQ0FBQyxDQUFDO1lBRS9DLEtBQUssVUFBVTtnQkFDYixxQ0FBcUM7Z0JBQ3JDLE1BQU0sQ0FBQyxJQUFJLENBQUMsb0VBQW9FLENBQUMsQ0FBQztnQkFDbEYsT0FBTyxJQUFJLENBQUMsaUJBQWlCLENBQUMsSUFBSSxFQUFFLE9BQU8sQ0FBQyxDQUFDO1lBRS9DLEtBQUssY0FBYztnQkFDakIsT0FBTyxJQUFJLENBQUMsb0JBQW9CLENBQUMsSUFBSSxFQUFFLE9BQU8sQ0FBQyxDQUFDO1lBRWxELEtBQUssUUFBUTtnQkFDWCw4QkFBOEI7Z0JBQzlCLE1BQU0sWUFBWSxHQUFHLE1BQU0sSUFBSSxDQUFDLGlCQUFpQixDQUFDLElBQUksRUFBRSxPQUFPLENBQUMsQ0FBQztnQkFDakUsTUFBTSxZQUFZLEdBQUcsTUFBTSxJQUFJLENBQUMsb0JBQW9CLENBQUMsSUFBSSxFQUFFLE9BQU8sQ0FBQyxDQUFDO2dCQUVwRSwyQ0FBMkM7Z0JBQzNDLE9BQU87b0JBQ0wsTUFBTSxFQUFFLFlBQVksQ0FBQyxVQUFVLEdBQUcsWUFBWSxDQUFDLFVBQVUsQ0FBQyxDQUFDLENBQUMsWUFBWSxDQUFDLE1BQU0sQ0FBQyxDQUFDLENBQUMsWUFBWSxDQUFDLE1BQU07b0JBQ3JHLFNBQVMsRUFBRSxlQUFlLFlBQVksQ0FBQyxTQUFTLG1CQUFtQixZQUFZLENBQUMsU0FBUyxFQUFFO29CQUMzRixVQUFVLEVBQUUsQ0FBQyxZQUFZLENBQUMsVUFBVSxHQUFHLFlBQVksQ0FBQyxVQUFVLENBQUMsR0FBRyxDQUFDO2lCQUNwRSxDQUFDO1lBRUo7Z0JBQ0UsTUFBTSxJQUFJLEtBQUssQ0FBQywrQkFBK0IsU0FBUyxFQUFFLENBQUMsQ0FBQztRQUNoRSxDQUFDO0lBQ0gsQ0FBQztJQUVEOztPQUVHO0lBQ0ssS0FBSyxDQUFDLGlCQUFpQixDQUM3QixJQUFlLEVBQ2YsT0FBNEI7UUFFNUIsTUFBTSxLQUFLLEdBS047WUFDSCxrREFBa0Q7WUFDbEQ7Z0JBQ0UsU0FBUyxFQUFFLENBQUMsQ0FBQyxFQUFFLEVBQUUsQ0FBQyxDQUFDLENBQUMsUUFBUSxLQUFLLElBQUksQ0FBQyxnQkFBZ0IsQ0FBQyxTQUFTLENBQUMsUUFBUSxDQUFDLFFBQVE7b0JBQy9ELENBQUMsQ0FBQyxPQUFPLEdBQUcsSUFBSSxDQUFDLGdCQUFnQixDQUFDLFNBQVMsQ0FBQyxpQkFBaUIsQ0FBQyxTQUFTO2dCQUMxRixNQUFNLEVBQUUsSUFBSSxDQUFDLGdCQUFnQixDQUFDLE9BQU8sQ0FBQyxrQkFBa0I7Z0JBQ3hELFNBQVMsRUFBRSwrREFBK0Q7Z0JBQzFFLFVBQVUsRUFBRSxJQUFJLENBQUMsZ0JBQWdCLENBQUMsU0FBUyxDQUFDLFVBQVUsQ0FBQyxRQUFRO2FBQ2hFO1lBQ0QsMEJBQTBCO1lBQzFCO2dCQUNFLFNBQVMsRUFBRSxDQUFDLENBQUMsRUFBRSxHQUFHLEVBQUUsRUFBRTtvQkFDcEIsSUFBSSxDQUFDLENBQUMsQ0FBQyxZQUFZLElBQUksQ0FBQyxDQUFDLFlBQVksQ0FBQyxNQUFNLEtBQUssQ0FBQzt3QkFBRSxPQUFPLEtBQUssQ0FBQztvQkFDakUsTUFBTSxXQUFXLEdBQUcsQ0FBQyxDQUFDLFlBQVksQ0FBQyxNQUFNLENBQUMsS0FBSyxDQUFDLEVBQUU7d0JBQ2hELE1BQU0sR0FBRyxHQUFHLElBQUksQ0FBQyxLQUFLLENBQUMsS0FBSyxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsRUFBRSxDQUFDLElBQUksQ0FBQyxFQUFFLEtBQUssS0FBSyxDQUFDLENBQUM7d0JBQzdELE9BQU8sR0FBRyxJQUFJLEdBQUcsQ0FBQyxNQUFNLEtBQUssV0FBVyxDQUFDO29CQUMzQyxDQUFDLENBQUMsQ0FBQztvQkFDSCxPQUFPLFdBQVcsQ0FBQyxNQUFNLEdBQUcsQ0FBQyxDQUFDO2dCQUNoQyxDQUFDO2dCQUNELE1BQU0sRUFBRSxJQUFJLENBQUMsZ0JBQWdCLENBQUMsT0FBTyxDQUFDLG1CQUFtQjtnQkFDekQsU0FBUyxFQUFFLGtDQUFrQztnQkFDN0MsVUFBVSxFQUFFLElBQUksQ0FBQyxnQkFBZ0IsQ0FBQyxTQUFTLENBQUMsVUFBVSxDQUFDLE9BQU87YUFDL0Q7WUFDRCxrQkFBa0I7WUFDbEI7Z0JBQ0UsU0FBUyxFQUFFLENBQUMsQ0FBQyxFQUFFLEVBQUUsQ0FBQyxDQUFDLENBQUMsU0FBUyxLQUFLLE1BQU0sSUFBSSxJQUFJLENBQUMsVUFBVSxFQUFFLGFBQWEsS0FBSyxjQUFjO2dCQUM3RixNQUFNLEVBQUUsSUFBSSxDQUFDLGdCQUFnQixDQUFDLE9BQU8sQ0FBQyxlQUFlO2dCQUNyRCxTQUFTLEVBQUUsdURBQXVEO2dCQUNsRSxVQUFVLEVBQUUsSUFBSSxDQUFDLGdCQUFnQixDQUFDLFNBQVMsQ0FBQyxVQUFVLENBQUMsWUFBWTthQUNwRTtZQUNELHdCQUF3QjtZQUN4QjtnQkFDRSxTQUFTLEVBQUUsQ0FBQyxDQUFDLEVBQUUsR0FBRyxFQUFFLEVBQUU7b0JBQ3BCLE1BQU0sV0FBVyxHQUFHLElBQUksQ0FBQyxLQUFLLENBQUMsS0FBSyxDQUFDLE1BQU0sQ0FBQyxJQUFJLENBQUMsRUFBRSxDQUFDLElBQUksQ0FBQyxNQUFNLEtBQUssYUFBYSxDQUFDLENBQUMsTUFBTSxDQUFDO29CQUMxRixNQUFNLGFBQWEsR0FBSSxJQUFJLENBQUMsUUFBMEIsQ0FBQyxrQkFBa0IsSUFBSSxjQUFjLENBQUMsb0JBQW9CLENBQUM7b0JBQ2pILE9BQU8sV0FBVyxJQUFJLGFBQWEsQ0FBQztnQkFDdEMsQ0FBQztnQkFDRCxNQUFNLEVBQUUsSUFBSSxDQUFDLGdCQUFnQixDQUFDLE9BQU8sQ0FBQyxhQUFhO2dCQUNuRCxTQUFTLEVBQUUsa0NBQWtDO2dCQUM3QyxVQUFVLEVBQUUsSUFBSSxDQUFDLGdCQUFnQixDQUFDLFNBQVMsQ0FBQyxVQUFVLENBQUMsYUFBYTthQUNyRTtZQUNELGlCQUFpQjtZQUNqQjtnQkFDRSxTQUFTLEVBQUUsR0FBRyxFQUFFLENBQUMsSUFBSTtnQkFDckIsTUFBTSxFQUFFLElBQUksQ0FBQyxnQkFBZ0IsQ0FBQyxPQUFPLENBQUMsZUFBZTtnQkFDckQsU0FBUyxFQUFFLDhCQUE4QjtnQkFDekMsVUFBVSxFQUFFLElBQUksQ0FBQyxnQkFBZ0IsQ0FBQyxTQUFTLENBQUMsVUFBVSxDQUFDLE9BQU87YUFDL0Q7U0FDRixDQUFDO1FBRUYsMEJBQTBCO1FBQzFCLEtBQUssTUFBTSxJQUFJLElBQUksS0FBSyxFQUFFLENBQUM7WUFDekIsSUFBSSxJQUFJLENBQUMsU0FBUyxDQUFDLElBQUksRUFBRSxPQUFPLENBQUMsRUFBRSxDQUFDO2dCQUNsQyxPQUFPO29CQUNMLE1BQU0sRUFBRSxJQUFJLENBQUMsTUFBTTtvQkFDbkIsU0FBUyxFQUFFLElBQUksQ0FBQyxTQUFTO29CQUN6QixVQUFVLEVBQUUsSUFBSSxDQUFDLFVBQVU7aUJBQzVCLENBQUM7WUFDSixDQU