@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.
822 lines • 119 kB
JavaScript
/**
* AgentElementValidator - Specialized validator for Agent elements
*
* Extends GenericElementValidator to add Agent-specific validation:
* - V2.0 fields: goal (template/parameters), activates, tools, systemPrompt, autonomy
* - V1.x legacy fields with deprecation warnings
* - Complex validations: goal template/parameter consistency, autonomy conflict detection
* - Migration suggestions for V1 agents
*
* @since v2.0.0 - Agentic Loop Redesign
*/
import { ElementType } from '../../portfolio/types.js';
import { GenericElementValidator } from './GenericElementValidator.js';
import { ValidatorHelpers } from './ElementValidator.js';
import { DECISION_FRAMEWORKS, RISK_TOLERANCE_LEVELS, STEP_LIMIT_ACTIONS, EXECUTION_FAILURE_ACTIONS, BACKOFF_STRATEGIES, AGENT_LIMITS, isOneOf } from '../../elements/agents/constants.js';
import { findPatternConflicts } from '../../utils/patternMatcher.js';
import { parseElementPolicy } from '../../handlers/mcp-aql/policies/ElementPolicies.js';
import { SECURITY_LIMITS } from '../../security/constants.js';
/**
* Valid parameter types for goal templates
*/
const VALID_PARAMETER_TYPES = ['string', 'number', 'boolean'];
/**
* Valid element types for activates configuration
*/
const VALID_ACTIVATES_TYPES = [
'personas',
'skills',
'memories',
'templates',
'ensembles'
];
/**
* System prompt length limits
*/
const SYSTEM_PROMPT_MIN_LENGTH = 1;
const SYSTEM_PROMPT_MAX_LENGTH = 10000;
/**
* Specialized validator for Agent elements
*/
export class AgentElementValidator extends GenericElementValidator {
constructor(validationService, triggerValidationService, metadataService) {
super(ElementType.AGENT, validationService, triggerValidationService, metadataService);
}
/**
* Override validateCreate to add agent-specific validation
*/
async validateCreate(data, options) {
// First run generic validation
const baseResult = await super.validateCreate(data, options);
const errors = [...baseResult.errors];
const warnings = [...baseResult.warnings];
const suggestions = [...(baseResult.suggestions || [])];
if (!data || typeof data !== 'object') {
return baseResult;
}
const record = data;
// V2.0 OPTIONAL FIELD: goal
// Note: V1 agents don't have goal - they use decisionFramework/specializations
// V2 agents should define goal, but we don't require it to maintain backward compatibility
if (record.goal !== undefined) {
const goalResult = this.validateGoal(record.goal);
if (!goalResult.isValid) {
errors.push(...goalResult.errors);
}
warnings.push(...goalResult.warnings);
}
// V2.0 OPTIONAL FIELDS
// Validate activates configuration
if (record.activates !== undefined) {
const activatesResult = this.validateActivates(record.activates);
if (!activatesResult.isValid) {
errors.push(...activatesResult.errors);
}
warnings.push(...activatesResult.warnings);
}
// Validate tools configuration
if (record.tools !== undefined) {
const toolsResult = this.validateTools(record.tools);
if (!toolsResult.isValid) {
errors.push(...toolsResult.errors);
}
warnings.push(...toolsResult.warnings);
}
// Validate system prompt
if (record.systemPrompt !== undefined) {
const promptResult = this.validateSystemPrompt(record.systemPrompt);
if (!promptResult.isValid) {
errors.push(...promptResult.errors);
}
warnings.push(...promptResult.warnings);
}
// Validate autonomy configuration
if (record.autonomy !== undefined) {
const autonomyResult = this.validateAutonomy(record.autonomy);
if (!autonomyResult.isValid) {
errors.push(...autonomyResult.errors);
}
warnings.push(...autonomyResult.warnings);
}
// Validate resilience configuration (Issue #526)
if (record.resilience !== undefined) {
const resilienceResult = this.validateResilience(record.resilience);
if (!resilienceResult.isValid) {
errors.push(...resilienceResult.errors);
}
warnings.push(...resilienceResult.warnings);
}
// Validate gatekeeper policy structure (Issue #449)
// Uses parseElementPolicy() which validates: object type, allow/confirm/deny
// are string arrays, and scopeRestrictions structure if present.
if (record.gatekeeper !== undefined) {
try {
const policy = parseElementPolicy({ gatekeeper: record.gatekeeper });
if (!policy) {
errors.push('Invalid gatekeeper policy: must be a non-null object with allow, confirm, or deny arrays');
}
}
catch (error) {
const message = error instanceof Error ? error.message : String(error);
errors.push(`Invalid gatekeeper policy: ${message}`);
}
}
// V1.X LEGACY FIELDS - Validate but emit warnings for deprecation
// decisionFramework (V1.x)
if (record.decisionFramework !== undefined) {
const frameworkResult = this.validateDecisionFramework(record.decisionFramework);
if (!frameworkResult.isValid) {
errors.push(...frameworkResult.errors);
}
warnings.push(...frameworkResult.warnings);
warnings.push("Field 'decisionFramework' is deprecated in V2.0. LLM judgment is now used instead.");
}
// specializations (V1.x)
if (record.specializations !== undefined) {
const specializationsResult = this.validateSpecializations(record.specializations);
if (!specializationsResult.isValid) {
warnings.push(...specializationsResult.errors);
}
}
// riskTolerance (V1.x)
if (record.riskTolerance !== undefined) {
const riskResult = this.validateRiskTolerance(record.riskTolerance);
if (!riskResult.isValid) {
errors.push(...riskResult.errors);
}
// Note: riskTolerance is also used in V2.0 autonomy config, so only warn if NOT in autonomy
if (!record.autonomy || !record.autonomy?.riskTolerance) {
warnings.push("Field 'riskTolerance' at root level is deprecated in V2.0. Use 'autonomy.riskTolerance' instead.");
}
}
// learningEnabled (V1.x)
if (record.learningEnabled !== undefined) {
if (typeof record.learningEnabled !== 'boolean') {
warnings.push("Field 'learningEnabled' must be a boolean");
}
warnings.push("Field 'learningEnabled' is deprecated in V2.0. LLM handles learning naturally.");
}
// maxConcurrentGoals (V1.x)
if (record.maxConcurrentGoals !== undefined) {
const concurrentResult = this.validateMaxConcurrentGoals(record.maxConcurrentGoals);
if (!concurrentResult.isValid) {
warnings.push(...concurrentResult.errors);
}
warnings.push("Field 'maxConcurrentGoals' is deprecated in V2.0.");
}
// ruleEngineConfig (V1.x)
if (record.ruleEngineConfig !== undefined) {
warnings.push("Field 'ruleEngineConfig' is deprecated in V2.0. Constraints are handled by evaluateConstraints().");
}
// SUGGESTIONS
// Suggest migration for V1 agents
if (this.isV1Agent(record)) {
suggestions.push('This appears to be a V1.x agent. Consider migrating to V2.0 format with goal templates.');
}
// Suggest optional fields if missing
if (!record.activates) {
suggestions.push("Consider adding 'activates' configuration to specify which elements should be activated.");
}
if (!record.tools) {
suggestions.push("Consider adding 'tools' configuration to guide LLM on appropriate tool usage.");
}
if (!record.systemPrompt) {
suggestions.push("Consider adding 'systemPrompt' to provide custom LLM context for this agent.");
}
if (!record.autonomy) {
suggestions.push("Consider adding 'autonomy' configuration to control continue/pause behavior.");
}
return {
isValid: errors.length === 0,
errors,
warnings,
suggestions: suggestions.length > 0 ? suggestions : undefined
};
}
/**
* Validate V2.0 goal configuration (REQUIRED)
*
* Must have:
* - template (string with {parameter} placeholders)
* - parameters (array of parameter definitions)
* - successCriteria (optional string[])
*
* Also validates template/parameter consistency.
*
* Architecture: Input normalization happens at GenericElementValidator boundary.
* This validator receives pre-normalized data and validates business rules.
*/
validateGoal(goal) {
if (!goal || typeof goal !== 'object' || Array.isArray(goal)) {
return ValidatorHelpers.fail(['Goal must be an object with template and parameters']);
}
const goalObj = goal;
const errors = [];
const warnings = [];
// Validate template
// Note: Input is already normalized by GenericElementValidator
if (goalObj.template === undefined || goalObj.template === null) {
errors.push("Goal is missing required 'template' field");
}
else if (typeof goalObj.template !== 'string') {
errors.push("Goal template must be a string");
}
else {
// Validate length and content patterns (data is already normalized)
const contentResult = this.validationService.validateContent(goalObj.template, {
maxLength: AGENT_LIMITS.MAX_GOAL_LENGTH
});
if (!contentResult.isValid) {
errors.push(...(contentResult.detectedPatterns || ['Goal template failed content validation']));
}
else if (goalObj.template.trim().length === 0) {
errors.push("Goal template cannot be empty");
}
}
// Validate parameters
if (!goalObj.parameters) {
errors.push("Goal is missing required 'parameters' field");
}
else if (!Array.isArray(goalObj.parameters)) {
errors.push("Goal parameters must be an array");
}
else {
// Validate each parameter
const template = String(goalObj.template || '');
const parametersResult = this.validateGoalParameters(goalObj.parameters, template);
if (!parametersResult.isValid) {
errors.push(...parametersResult.errors);
}
warnings.push(...parametersResult.warnings);
}
// Validate successCriteria (optional)
if (goalObj.successCriteria !== undefined) {
if (!Array.isArray(goalObj.successCriteria)) {
errors.push("Goal successCriteria must be an array of strings");
}
else {
for (let i = 0; i < goalObj.successCriteria.length; i++) {
const criterion = goalObj.successCriteria[i];
if (typeof criterion !== 'string') {
errors.push(`Success criterion at index ${i} must be a string`);
}
else {
// Validate length and patterns (data is already normalized)
const contentResult = this.validationService.validateContent(criterion, {
maxLength: AGENT_LIMITS.MAX_GOAL_LENGTH
});
if (!contentResult.isValid) {
errors.push(`Success criterion at index ${i} failed validation: ${contentResult.detectedPatterns?.join(', ') || 'invalid content'}`);
}
else if (criterion.trim().length === 0) {
warnings.push(`Success criterion at index ${i} is empty`);
}
}
}
}
}
return {
isValid: errors.length === 0,
errors,
warnings
};
}
/**
* Validate goal parameters and check consistency with template
*/
validateGoalParameters(parameters, template) {
const errors = [];
const warnings = [];
const parameterNames = new Set();
const templatePlaceholders = this.extractTemplatePlaceholders(template);
for (let i = 0; i < parameters.length; i++) {
const param = parameters[i];
if (!param || typeof param !== 'object') {
errors.push(`Parameter at index ${i} must be an object`);
continue;
}
const paramObj = param;
// Validate name
if (!paramObj.name) {
errors.push(`Parameter at index ${i} is missing required 'name' field`);
continue;
}
if (typeof paramObj.name !== 'string') {
errors.push(`Parameter name at index ${i} must be a string`);
continue;
}
// Validate parameter name (data is already normalized)
const nameResult = this.validationService.validateContent(paramObj.name, {
maxLength: AGENT_LIMITS.MAX_AGENT_NAME_LENGTH
});
if (!nameResult.isValid) {
errors.push(`Parameter name at index ${i} failed validation: ${nameResult.detectedPatterns?.join(', ') || 'invalid content'}`);
continue;
}
const paramName = paramObj.name;
// Check for duplicate parameter names
if (parameterNames.has(paramName)) {
errors.push(`Duplicate parameter name '${paramName}' at index ${i}`);
}
parameterNames.add(paramName);
// Validate type
if (!paramObj.type) {
errors.push(`Parameter '${paramName}' is missing required 'type' field`);
}
else if (typeof paramObj.type !== 'string') {
errors.push(`Parameter '${paramName}' type must be a string`);
}
else if (!VALID_PARAMETER_TYPES.includes(paramObj.type)) {
errors.push(`Parameter '${paramName}' has invalid type '${paramObj.type}'. Valid types: ${VALID_PARAMETER_TYPES.join(', ')}`);
}
// Validate required
if (!('required' in paramObj)) {
errors.push(`Parameter '${paramName}' is missing required 'required' field`);
}
else if (typeof paramObj.required !== 'boolean') {
errors.push(`Parameter '${paramName}' required field must be a boolean`);
}
// Validate description (optional) - data is already normalized
if (paramObj.description !== undefined) {
if (typeof paramObj.description !== 'string') {
warnings.push(`Parameter '${paramName}' description must be a string`);
}
else {
const descResult = this.validationService.validateContent(paramObj.description, {
maxLength: SECURITY_LIMITS.MAX_DOCUMENTATION_FIELD_LENGTH
});
if (!descResult.isValid) {
warnings.push(`Parameter '${paramName}' description failed validation: ${descResult.detectedPatterns?.join(', ') || 'invalid content'}`);
}
}
}
// Validate default (optional) - data is already normalized
if (paramObj.default !== undefined) {
const defaultType = typeof paramObj.default;
if (paramObj.type === 'string' && defaultType !== 'string') {
warnings.push(`Parameter '${paramName}' default value type mismatch (expected string)`);
}
else if (paramObj.type === 'string' && defaultType === 'string') {
// Validate string default values (already normalized)
const defaultResult = this.validationService.validateContent(paramObj.default, {
maxLength: AGENT_LIMITS.MAX_GOAL_LENGTH
});
if (!defaultResult.isValid) {
warnings.push(`Parameter '${paramName}' default value failed validation: ${defaultResult.detectedPatterns?.join(', ') || 'invalid content'}`);
}
}
else if (paramObj.type === 'number' && defaultType !== 'number') {
warnings.push(`Parameter '${paramName}' default value type mismatch (expected number)`);
}
else if (paramObj.type === 'boolean' && defaultType !== 'boolean') {
warnings.push(`Parameter '${paramName}' default value type mismatch (expected boolean)`);
}
}
}
// Check template/parameter consistency
// Warn if template has placeholders not defined in parameters
for (const placeholder of templatePlaceholders) {
if (!parameterNames.has(placeholder)) {
warnings.push(`Template references parameter '{${placeholder}}' which is not defined in parameters array`);
}
}
// Warn if parameters are defined but not used in template
for (const paramName of parameterNames) {
if (!templatePlaceholders.has(paramName)) {
warnings.push(`Parameter '${paramName}' is defined but not used in template`);
}
}
return {
isValid: errors.length === 0,
errors,
warnings
};
}
/**
* Extract parameter placeholders from template
* Finds all {parameterName} patterns
*/
extractTemplatePlaceholders(template) {
const placeholders = new Set();
const regex = /\{([a-zA-Z_][a-zA-Z0-9_]*)\}/g;
let match;
while ((match = regex.exec(template)) !== null) {
placeholders.add(match[1]);
}
return placeholders;
}
/**
* Validate V2.0 activates configuration (OPTIONAL)
*
* Must be an object where each property is a string array.
* Supports: personas, skills, memories, templates, ensembles
*/
validateActivates(activates) {
if (!activates || typeof activates !== 'object' || Array.isArray(activates)) {
return ValidatorHelpers.fail(['Activates must be an object']);
}
const activatesObj = activates;
const warnings = [];
for (const [key, value] of Object.entries(activatesObj)) {
// Check if key is a known element type (warning only)
if (!VALID_ACTIVATES_TYPES.includes(key)) {
warnings.push(`Unknown element type '${key}' in activates. Known types: ${VALID_ACTIVATES_TYPES.join(', ')}`);
}
// Validate value is string array
if (!Array.isArray(value)) {
return ValidatorHelpers.fail([`Activates.${key} must be an array of strings`]);
}
for (let i = 0; i < value.length; i++) {
if (typeof value[i] !== 'string') {
return ValidatorHelpers.fail([`Activates.${key}[${i}] must be a string`]);
}
// Validate length and patterns (data is already normalized)
const contentResult = this.validationService.validateContent(value[i], {
maxLength: AGENT_LIMITS.MAX_AGENT_NAME_LENGTH
});
if (!contentResult.isValid) {
return ValidatorHelpers.fail([`Activates.${key}[${i}] failed validation: ${contentResult.detectedPatterns?.join(', ') || 'invalid content'}`]);
}
if (value[i].trim().length === 0) {
warnings.push(`Activates.${key}[${i}] is empty`);
}
}
}
return {
isValid: true,
errors: [],
warnings
};
}
/**
* Validate V2.0 tools configuration (OPTIONAL)
*
* Must have:
* - allowed (string[], required if tools defined)
* - denied (string[], optional)
*/
validateTools(tools) {
if (!tools || typeof tools !== 'object' || Array.isArray(tools)) {
return ValidatorHelpers.fail(['Tools must be an object']);
}
const toolsObj = tools;
const warnings = [];
// Validate allowed (required)
if (!toolsObj.allowed) {
return ValidatorHelpers.fail(["Tools configuration must include 'allowed' array"]);
}
if (!Array.isArray(toolsObj.allowed)) {
return ValidatorHelpers.fail(["Tools.allowed must be an array of strings"]);
}
const allowedSet = new Set();
for (let i = 0; i < toolsObj.allowed.length; i++) {
const tool = toolsObj.allowed[i];
if (typeof tool !== 'string') {
return ValidatorHelpers.fail([`Tools.allowed[${i}] must be a string`]);
}
// Validate length and patterns (data is already normalized)
const contentResult = this.validationService.validateContent(tool, {
maxLength: AGENT_LIMITS.MAX_AGENT_NAME_LENGTH
});
if (!contentResult.isValid) {
return ValidatorHelpers.fail([`Tools.allowed[${i}] failed validation: ${contentResult.detectedPatterns?.join(', ') || 'invalid content'}`]);
}
if (tool.trim().length === 0) {
warnings.push(`Tools.allowed[${i}] is empty`);
}
allowedSet.add(tool);
}
// Validate denied (optional)
if (toolsObj.denied !== undefined) {
if (!Array.isArray(toolsObj.denied)) {
return ValidatorHelpers.fail(["Tools.denied must be an array of strings"]);
}
const deniedSet = new Set();
for (let i = 0; i < toolsObj.denied.length; i++) {
const tool = toolsObj.denied[i];
if (typeof tool !== 'string') {
return ValidatorHelpers.fail([`Tools.denied[${i}] must be a string`]);
}
// Validate length and patterns (data is already normalized)
const contentResult = this.validationService.validateContent(tool, {
maxLength: AGENT_LIMITS.MAX_AGENT_NAME_LENGTH
});
if (!contentResult.isValid) {
return ValidatorHelpers.fail([`Tools.denied[${i}] failed validation: ${contentResult.detectedPatterns?.join(', ') || 'invalid content'}`]);
}
if (tool.trim().length === 0) {
warnings.push(`Tools.denied[${i}] is empty`);
}
deniedSet.add(tool);
}
// Warn if same tool is in both allowed and denied
for (const tool of deniedSet) {
if (allowedSet.has(tool)) {
warnings.push(`Tool '${tool}' appears in both allowed and denied lists. Denied takes precedence.`);
}
}
}
return {
isValid: true,
errors: [],
warnings
};
}
/**
* Validate V2.0 system prompt (OPTIONAL)
*
* Must be a string, length 1-10000 characters.
* Uses validationService for content security.
*
* Architecture: Input is already normalized by GenericElementValidator.
* This validator checks business rules (length, patterns) on normalized data.
*/
validateSystemPrompt(systemPrompt) {
// Type check must happen first
if (typeof systemPrompt !== 'string') {
return ValidatorHelpers.fail(['System prompt must be a string']);
}
// Validate length and patterns (data is already normalized)
const contentResult = this.validationService.validateContent(systemPrompt, {
maxLength: SYSTEM_PROMPT_MAX_LENGTH
});
if (!contentResult.isValid) {
return ValidatorHelpers.fail(contentResult.detectedPatterns || ['System prompt failed content validation']);
}
if (systemPrompt.length < SYSTEM_PROMPT_MIN_LENGTH) {
return ValidatorHelpers.fail(['System prompt cannot be empty']);
}
const warnings = [];
if (systemPrompt.length > 5000) {
warnings.push('System prompt is very long - consider keeping it concise for better LLM performance');
}
return {
isValid: true,
errors: [],
warnings
};
}
/**
* Validate V2.0 autonomy configuration (OPTIONAL)
*
* Fields:
* - riskTolerance (enum)
* - maxAutonomousSteps (positive integer, 0 = unlimited)
* - requiresApproval (string[] glob patterns)
* - autoApprove (string[] glob patterns)
*
* Also detects conflicts between requiresApproval and autoApprove.
*/
validateAutonomy(autonomy) {
if (!autonomy || typeof autonomy !== 'object' || Array.isArray(autonomy)) {
return ValidatorHelpers.fail(['Autonomy must be an object']);
}
const autonomyObj = autonomy;
const errors = [];
const warnings = [];
// Validate riskTolerance (optional)
if (autonomyObj.riskTolerance !== undefined) {
const riskResult = this.validateRiskTolerance(autonomyObj.riskTolerance);
if (!riskResult.isValid) {
errors.push(...riskResult.errors);
}
}
// Validate maxAutonomousSteps (optional)
if (autonomyObj.maxAutonomousSteps !== undefined) {
if (typeof autonomyObj.maxAutonomousSteps !== 'number') {
errors.push('Autonomy.maxAutonomousSteps must be a number');
}
else if (autonomyObj.maxAutonomousSteps < 0) {
errors.push('Autonomy.maxAutonomousSteps must be non-negative (0 = unlimited)');
}
else if (!Number.isInteger(autonomyObj.maxAutonomousSteps)) {
errors.push('Autonomy.maxAutonomousSteps must be an integer');
}
}
// Validate requiresApproval (optional) - data is already normalized
const requiresApprovalSet = new Set();
if (autonomyObj.requiresApproval !== undefined) {
if (!Array.isArray(autonomyObj.requiresApproval)) {
errors.push('Autonomy.requiresApproval must be an array of strings');
}
else {
for (let i = 0; i < autonomyObj.requiresApproval.length; i++) {
const pattern = autonomyObj.requiresApproval[i];
if (typeof pattern !== 'string') {
errors.push(`Autonomy.requiresApproval[${i}] must be a string`);
}
else {
const contentResult = this.validationService.validateContent(pattern, {
maxLength: AGENT_LIMITS.MAX_GOAL_LENGTH
});
if (!contentResult.isValid) {
errors.push(`Autonomy.requiresApproval[${i}] failed validation: ${contentResult.detectedPatterns?.join(', ') || 'invalid content'}`);
}
else {
requiresApprovalSet.add(pattern);
}
}
}
}
}
// Validate autoApprove (optional) - data is already normalized
const autoApproveSet = new Set();
if (autonomyObj.autoApprove !== undefined) {
if (!Array.isArray(autonomyObj.autoApprove)) {
errors.push('Autonomy.autoApprove must be an array of strings');
}
else {
for (let i = 0; i < autonomyObj.autoApprove.length; i++) {
const pattern = autonomyObj.autoApprove[i];
if (typeof pattern !== 'string') {
errors.push(`Autonomy.autoApprove[${i}] must be a string`);
}
else {
const contentResult = this.validationService.validateContent(pattern, {
maxLength: AGENT_LIMITS.MAX_GOAL_LENGTH
});
if (!contentResult.isValid) {
errors.push(`Autonomy.autoApprove[${i}] failed validation: ${contentResult.detectedPatterns?.join(', ') || 'invalid content'}`);
}
else {
autoApproveSet.add(pattern);
}
}
}
}
}
// Detect conflicts between requiresApproval and autoApprove (including glob pattern conflicts)
if (requiresApprovalSet.size > 0 && autoApproveSet.size > 0) {
const conflictMessages = findPatternConflicts(Array.from(requiresApprovalSet), Array.from(autoApproveSet));
for (const conflict of conflictMessages) {
warnings.push(`${conflict}. requiresApproval takes precedence.`);
}
}
return {
isValid: errors.length === 0,
errors,
warnings
};
}
/**
* Validate V2.1 resilience configuration (OPTIONAL, nested under autonomy)
*
* Fields:
* - onStepLimitReached ('pause' | 'continue' | 'restart')
* - onExecutionFailure ('pause' | 'retry' | 'restart-fresh')
* - maxRetries (non-negative integer, default 3)
* - maxContinuations (non-negative integer, 0 = unlimited, default 10)
* - retryBackoff ('none' | 'linear' | 'exponential')
* - preserveState (boolean)
*
* @since v2.1.0 - Agent Execution Resilience (Issue #526)
*/
validateResilience(resilience) {
if (!resilience || typeof resilience !== 'object' || Array.isArray(resilience)) {
return ValidatorHelpers.fail(['Resilience must be an object']);
}
const obj = resilience;
const errors = [];
const warnings = [];
// Issue #730: Use shared constants from constants.ts (single source of truth)
if (obj.onStepLimitReached !== undefined) {
if (!isOneOf(obj.onStepLimitReached, STEP_LIMIT_ACTIONS)) {
errors.push(`Resilience.onStepLimitReached must be one of: ${STEP_LIMIT_ACTIONS.join(', ')}`);
}
}
if (obj.onExecutionFailure !== undefined) {
if (!isOneOf(obj.onExecutionFailure, EXECUTION_FAILURE_ACTIONS)) {
errors.push(`Resilience.onExecutionFailure must be one of: ${EXECUTION_FAILURE_ACTIONS.join(', ')}`);
}
}
if (obj.maxRetries !== undefined) {
if (typeof obj.maxRetries !== 'number' || !Number.isInteger(obj.maxRetries) || obj.maxRetries < 0) {
errors.push('Resilience.maxRetries must be a non-negative integer');
}
}
if (obj.maxContinuations !== undefined) {
if (typeof obj.maxContinuations !== 'number' || !Number.isInteger(obj.maxContinuations) || obj.maxContinuations < 0) {
errors.push('Resilience.maxContinuations must be a non-negative integer');
}
else if (obj.maxContinuations === 0) {
warnings.push('Resilience.maxContinuations=0 means unlimited auto-continuations. Consider setting a safety limit.');
}
}
if (obj.retryBackoff !== undefined) {
if (!isOneOf(obj.retryBackoff, BACKOFF_STRATEGIES)) {
errors.push(`Resilience.retryBackoff must be one of: ${BACKOFF_STRATEGIES.join(', ')}`);
}
}
if (obj.preserveState !== undefined) {
if (typeof obj.preserveState !== 'boolean') {
errors.push('Resilience.preserveState must be a boolean');
}
}
return {
isValid: errors.length === 0,
errors,
warnings
};
}
/**
* Validate V1.x decisionFramework (DEPRECATED)
* Data is already normalized by GenericElementValidator
*/
validateDecisionFramework(framework) {
if (typeof framework !== 'string') {
return ValidatorHelpers.fail(['Decision framework must be a string']);
}
// Validate length and patterns (data is already normalized)
const contentResult = this.validationService.validateContent(framework, {
maxLength: AGENT_LIMITS.MAX_SPECIALIZATION_LENGTH
});
if (!contentResult.isValid) {
return ValidatorHelpers.fail([
`Decision framework failed validation: ${contentResult.detectedPatterns?.join(', ') || 'invalid content'}`
]);
}
if (!DECISION_FRAMEWORKS.includes(framework)) {
return ValidatorHelpers.fail([
`Invalid decision framework '${framework}'. Valid options: ${DECISION_FRAMEWORKS.join(', ')}`
]);
}
return ValidatorHelpers.pass();
}
/**
* Validate V1.x specializations (DEPRECATED)
* Data is already normalized by GenericElementValidator
*/
validateSpecializations(specializations) {
if (!Array.isArray(specializations)) {
return ValidatorHelpers.fail(['Specializations must be an array']);
}
for (let i = 0; i < specializations.length; i++) {
if (typeof specializations[i] !== 'string') {
return ValidatorHelpers.fail([`Specialization at index ${i} must be a string`]);
}
const spec = specializations[i];
// Validate length and patterns (data is already normalized)
const contentResult = this.validationService.validateContent(spec, {
maxLength: AGENT_LIMITS.MAX_SPECIALIZATION_LENGTH
});
if (!contentResult.isValid) {
return ValidatorHelpers.fail([
`Specialization at index ${i} failed validation: ${contentResult.detectedPatterns?.join(', ') || 'invalid content'}`
]);
}
}
return ValidatorHelpers.pass();
}
/**
* Validate riskTolerance enum
* Used in both V1.x (deprecated) and V2.0 autonomy config
* Data is already normalized by GenericElementValidator
*/
validateRiskTolerance(riskTolerance) {
if (typeof riskTolerance !== 'string') {
return ValidatorHelpers.fail(['Risk tolerance must be a string']);
}
// Validate length and patterns (data is already normalized)
const contentResult = this.validationService.validateContent(riskTolerance, {
maxLength: AGENT_LIMITS.MAX_SPECIALIZATION_LENGTH
});
if (!contentResult.isValid) {
return ValidatorHelpers.fail([
`Risk tolerance failed validation: ${contentResult.detectedPatterns?.join(', ') || 'invalid content'}`
]);
}
if (!RISK_TOLERANCE_LEVELS.includes(riskTolerance)) {
return ValidatorHelpers.fail([
`Invalid risk tolerance '${riskTolerance}'. Valid options: ${RISK_TOLERANCE_LEVELS.join(', ')}`
]);
}
return ValidatorHelpers.pass();
}
/**
* Validate V1.x maxConcurrentGoals (DEPRECATED)
*/
validateMaxConcurrentGoals(maxConcurrentGoals) {
if (typeof maxConcurrentGoals !== 'number') {
return ValidatorHelpers.fail(['Max concurrent goals must be a number']);
}
if (!Number.isInteger(maxConcurrentGoals)) {
return ValidatorHelpers.fail(['Max concurrent goals must be an integer']);
}
if (maxConcurrentGoals < 1 || maxConcurrentGoals > 100) {
return ValidatorHelpers.fail(['Max concurrent goals must be between 1 and 100']);
}
return ValidatorHelpers.pass();
}
/**
* Check if an agent appears to be V1.x format
* V1 agents have decisionFramework or specializations but no goal config
*/
isV1Agent(record) {
const hasV1Fields = (record.decisionFramework !== undefined ||
record.specializations !== undefined ||
record.ruleEngineConfig !== undefined);
const hasV2Goal = record.goal !== undefined;
return hasV1Fields && !hasV2Goal;
}
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiQWdlbnRFbGVtZW50VmFsaWRhdG9yLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vc3JjL3NlcnZpY2VzL3ZhbGlkYXRpb24vQWdlbnRFbGVtZW50VmFsaWRhdG9yLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOzs7Ozs7Ozs7O0dBVUc7QUFFSCxPQUFPLEVBQUUsV0FBVyxFQUFFLE1BQU0sMEJBQTBCLENBQUM7QUFDdkQsT0FBTyxFQUFFLHVCQUF1QixFQUFFLE1BQU0sOEJBQThCLENBQUM7QUFDdkUsT0FBTyxFQUFvQixnQkFBZ0IsRUFBNEIsTUFBTSx1QkFBdUIsQ0FBQztBQUlyRyxPQUFPLEVBQ0wsbUJBQW1CLEVBQ25CLHFCQUFxQixFQUNyQixrQkFBa0IsRUFDbEIseUJBQXlCLEVBQ3pCLGtCQUFrQixFQUNsQixZQUFZLEVBQ1osT0FBTyxFQUdSLE1BQU0sb0NBQW9DLENBQUM7QUFDNUMsT0FBTyxFQUFFLG9CQUFvQixFQUFFLE1BQU0sK0JBQStCLENBQUM7QUFDckUsT0FBTyxFQUFFLGtCQUFrQixFQUFFLE1BQU0sb0RBQW9ELENBQUM7QUFDeEYsT0FBTyxFQUFFLGVBQWUsRUFBRSxNQUFNLDZCQUE2QixDQUFDO0FBRTlEOztHQUVHO0FBQ0gsTUFBTSxxQkFBcUIsR0FBRyxDQUFDLFFBQVEsRUFBRSxRQUFRLEVBQUUsU0FBUyxDQUFVLENBQUM7QUFHdkU7O0dBRUc7QUFDSCxNQUFNLHFCQUFxQixHQUFHO0lBQzVCLFVBQVU7SUFDVixRQUFRO0lBQ1IsVUFBVTtJQUNWLFdBQVc7SUFDWCxXQUFXO0NBQ0gsQ0FBQztBQUVYOztHQUVHO0FBQ0gsTUFBTSx3QkFBd0IsR0FBRyxDQUFDLENBQUM7QUFDbkMsTUFBTSx3QkFBd0IsR0FBRyxLQUFLLENBQUM7QUFFdkM7O0dBRUc7QUFDSCxNQUFNLE9BQU8scUJBQXNCLFNBQVEsdUJBQXVCO0lBQ2hFLFlBQ0UsaUJBQW9DLEVBQ3BDLHdCQUFrRCxFQUNsRCxlQUFnQztRQUVo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