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.

620 lines 87.6 kB
/** * Autonomy Evaluator Service * * Determines whether an agent should continue autonomously or pause * for human input after each step. This is the brain that enables * automatic continue/pause decisions in the agentic loop. * * Decision factors: * 1. Step count vs maxAutonomousSteps * 2. Next action vs requiresApproval patterns * 3. Safety tier (ALLOW/VERIFY/DENY) * 4. Risk tolerance configuration * * ## Danger Zone Verification Flow (Issue #142) * * When an agent action triggers the DANGER_ZONE or VERIFY safety tier, * a human-in-the-loop verification is required before the agent can proceed. * * End-to-end sequence: * * 1. Agent proposes an action (nextActionHint) during agentic loop * 2. evaluateAutonomy() calls checkSafetyTier() → returns DANGER_ZONE or VERIFY * 3. createVerificationChallenge() generates a challenge with: * - challengeId (UUID v4 via crypto.randomUUID) * - displayCode (human-readable code, e.g. "ABC123") * - expiresAt (configurable, default 5 minutes) * 4. storeAndDisplayChallenge(): * a. Stores {code, expiresAt, reason} in VerificationStore (server-side, one-time use) * b. Shows displayCode to human via OS-native dialog (AppleScript/zenity/PowerShell) * c. displayCode is NEVER included in the LLM-facing directive * 5. For DANGER_ZONE: DangerZoneEnforcer.block() programmatically blocks the agent * with verificationId=challengeId (file-backed, survives restarts) * 6. LLM receives directive with verification.verificationId but NO displayCode * plus actionable guidance: "Use verify_challenge { challenge_id, code }" * 7. Human reads code from OS dialog, tells LLM * 8. LLM calls verify_challenge MCP-AQL operation * 9. MCPAQLHandler verify handler: * a. Validates UUID v4 format (rejects invalid IDs before store lookup) * b. Checks rate limit (max 10 failures per 60s sliding window) * c. Distinguishes expired (VERIFICATION_EXPIRED) from wrong code (VERIFICATION_FAILED) * d. On success: finds blocked agent by challengeId → DangerZoneEnforcer.unblock() * e. Logs granular security events at each stage * 10. Agent retries the operation → passes (no longer blocked) * * Security invariants: * - displayCode never reaches the LLM (stripped before directive is built) * - Codes are one-time use (VerificationStore deletes after any verify attempt) * - Expired challenges are rejected with distinct VERIFICATION_EXPIRED events * - Challenge IDs are validated as UUID v4 before store lookup (anti-enumeration) * - Failed attempts are rate-limited globally (anti-brute-force) * - All verification events are logged with granular types for monitoring * * Part of the Agentic Loop Completion (Epic #380). * * @since v2.0.0 */ import { logger } from '../../utils/logger.js'; import { SecurityMonitor } from '../../security/securityMonitor.js'; import { matchesPattern } from '../../utils/patternMatcher.js'; import { determineSafetyTier, createVerificationChallenge, DEFAULT_SAFETY_CONFIG, showVerificationDialog, } from './safetyTierService.js'; import { getAutonomyRiskThresholds, getAutonomyMaxStepsDefault, } from '../../config/autonomy-config.js'; /** * Default autonomy configuration * * Used when an agent doesn't specify autonomy settings. * Conservative defaults prioritize safety over speed. * * Note: maxAutonomousSteps uses a getter to support env-var overrides * (Issue #390). The static value here serves as the baseline; callers * should prefer mergeWithDefaults() which reads the live config. */ export const DEFAULT_AUTONOMY_CONFIG = { riskTolerance: 'moderate', maxAutonomousSteps: 10, requiresApproval: [], autoApprove: [], verificationTimeoutMinutes: 5, }; /** * Lightweight in-memory tracker for autonomy evaluation patterns. * Issue #391: Provides observability into agent continue/pause decisions. */ class AutonomyMetricsTracker { _totalEvaluations = 0; _continueCount = 0; _pauseCount = 0; _pauseReasons = {}; _dangerZoneTriggered = 0; _verificationRequired = 0; _pauseStepCounts = []; static MAX_STEP_COUNTS = 1000; static LOG_INTERVAL = 50; recordContinue() { this._totalEvaluations++; this._continueCount++; this.maybeLogSnapshot(); } recordPause(reason, stepCount) { this._totalEvaluations++; this._pauseCount++; this._pauseReasons[reason] = (this._pauseReasons[reason] || 0) + 1; this._pauseStepCounts.push(stepCount); if (this._pauseStepCounts.length > AutonomyMetricsTracker.MAX_STEP_COUNTS) { this._pauseStepCounts.shift(); } this.maybeLogSnapshot(); } recordDangerZone() { this._dangerZoneTriggered++; } recordVerification() { this._verificationRequired++; } getSnapshot() { const avgStepCount = this._pauseStepCounts.length > 0 ? Math.round(this._pauseStepCounts.reduce((a, b) => a + b, 0) / this._pauseStepCounts.length) : 0; return { totalEvaluations: this._totalEvaluations, continueCount: this._continueCount, pauseCount: this._pauseCount, pauseReasons: { ...this._pauseReasons }, dangerZoneTriggered: this._dangerZoneTriggered, verificationRequired: this._verificationRequired, averageStepCountAtPause: avgStepCount, }; } /** Reset (for testing). */ reset() { this._totalEvaluations = 0; this._continueCount = 0; this._pauseCount = 0; this._pauseReasons = {}; this._dangerZoneTriggered = 0; this._verificationRequired = 0; this._pauseStepCounts = []; } maybeLogSnapshot() { if (this._totalEvaluations > 0 && this._totalEvaluations % AutonomyMetricsTracker.LOG_INTERVAL === 0) { logger.info('Autonomy evaluation metrics snapshot', { ...this.getSnapshot(), }); } } } /** Module-level metrics instance (Issue #391) */ const autonomyMetrics = new AutonomyMetricsTracker(); /** * Get the current autonomy evaluation metrics snapshot. * Issue #391: Programmatic access for monitoring/diagnostics. */ export function getAutonomyMetrics() { return autonomyMetrics.getSnapshot(); } /** * Reset autonomy metrics (for testing only). * @internal */ export function resetAutonomyMetrics() { autonomyMetrics.reset(); } /** * Sanitize a risk score to a valid 0-100 number. * * Handles NaN, non-number types, Infinity, and out-of-range values with * appropriate warning logs. Always returns a concrete number in [0, 100] * suitable for threshold comparison and safety tier evaluation. * * @param score - The raw risk score to sanitize (may be any numeric edge case) * @param agentName - Agent name for contextual log messages * @returns A clamped, finite number in the range [0, 100] */ function sanitizeRiskScore(score, agentName) { if (typeof score !== 'number' || Number.isNaN(score)) { logger.warn('Invalid risk score (NaN or non-number), treating as 0', { originalScore: score, agentName, }); return 0; } if (!Number.isFinite(score)) { logger.warn('Infinite risk score, clamping to range boundary', { originalScore: score, agentName, }); return score > 0 ? 100 : 0; } if (score < 0 || score > 100) { logger.warn('Risk score out of range, clamping to 0-100', { originalScore: score, agentName, }); return Math.max(0, Math.min(100, score)); } return score; } /** * Store a verification code server-side and show it to the human via OS dialog. * The display code is intentionally NOT returned to the LLM. * * Issue #142: Danger zone verification flow */ function storeAndDisplayChallenge(challenge, context) { if (!challenge.displayCode) return; // Store server-side for later verification if (context.verificationStore) { context.verificationStore.set(challenge.challengeId, { code: challenge.displayCode, expiresAt: new Date(challenge.expiresAt).getTime(), reason: challenge.reason, }); } // Show to human via OS-native dialog (fire-and-forget, non-blocking for evaluation) try { showVerificationDialog(challenge.displayCode, challenge.reason, { title: 'DollhouseMCP - Verification Required', icon: 'warning' }); } catch (error) { logger.warn('Failed to show verification dialog', { error: error instanceof Error ? error.message : String(error), challengeId: challenge.challengeId, }); } } /** * Evaluate whether an agent should continue autonomously or pause * * This is the main entry point for autonomy decisions. Call this after * each step to determine if the LLM can proceed or must wait for * human approval. * * @param context - The autonomy evaluation context * @returns AutonomyDirective indicating continue or pause */ export function evaluateAutonomy(context) { const config = mergeWithDefaults(context.autonomyConfig); const factors = []; logger.debug('Evaluating autonomy', { agentName: context.agentName, stepCount: context.stepCount, outcome: context.currentStepOutcome, nextActionHint: context.nextActionHint, }); // Early validation: sanitize risk score before any checks consume it if (context.riskScore !== undefined) { context.riskScore = sanitizeRiskScore(context.riskScore, context.agentName); } // Check 1: Step count limit const stepLimitResult = checkStepLimit(context.stepCount, config.maxAutonomousSteps); if (stepLimitResult) { factors.push(stepLimitResult.factor); if (!stepLimitResult.continue) { autonomyMetrics.recordPause(stepLimitResult.reason || 'step_limit', context.stepCount); return buildDirective(false, stepLimitResult.reason, factors, { stepsRemaining: 0, }); } } // Check 2: Current step outcome if (context.currentStepOutcome === 'failure') { factors.push('Previous step failed'); autonomyMetrics.recordPause('Previous step failed - human review recommended', context.stepCount); return buildDirective(false, 'Previous step failed - human review recommended', factors); } // Check 3: Pattern matching for next action if (context.nextActionHint) { const patternResult = checkActionPatterns(context.nextActionHint, config.requiresApproval, config.autoApprove); factors.push(...patternResult.factors); if (!patternResult.continue) { autonomyMetrics.recordPause(patternResult.reason || 'pattern_match', context.stepCount); return buildDirective(false, patternResult.reason, factors); } } // Check 4: Safety tier evaluation if (context.nextActionHint || context.riskScore !== undefined) { const safetyResult = checkSafetyTier(context.nextActionHint || context.currentStepDescription, context.riskScore || 0, config.riskTolerance, config.verificationTimeoutMinutes); factors.push(...safetyResult.factors); if (safetyResult.stopped) { // Issue #142: Create verification challenge for danger zone unblocking const dzChallenge = createVerificationChallenge(`Danger zone verification: ${(context.nextActionHint || context.currentStepDescription).substring(0, 100)}`, 'display_code', config.verificationTimeoutMinutes); // Store the code server-side and show via OS dialog (Issue #142) storeAndDisplayChallenge(dzChallenge, context); // Programmatic enforcement: block the agent from further execution // This prevents a compromised LLM from ignoring the danger zone warning // Issue #402: Use DI-injected enforcer instead of singleton context.dangerZoneEnforcer?.block(context.agentName, safetyResult.reason || 'Danger zone operation detected', safetyResult.factors, dzChallenge.challengeId, { stepNumber: context.stepCount, currentStepDescription: context.currentStepDescription, currentStepOutcome: context.currentStepOutcome, nextActionHint: context.nextActionHint, riskScore: context.riskScore, goalDescription: context.goalDescription, goalId: context.goalId, safetyFactors: factors, }); SecurityMonitor.logSecurityEvent({ type: 'DANGER_ZONE_TRIGGERED', severity: 'HIGH', source: 'AutonomyEvaluator.evaluateAutonomy', details: `Agent '${context.agentName}' blocked: danger zone verification required — ${safetyResult.reason ?? 'danger zone operation detected'}`, additionalData: { agentName: context.agentName, factors, nextActionHint: context.nextActionHint, stepCount: context.stepCount, riskScore: context.riskScore, currentStepDescription: context.currentStepDescription, goalDescription: context.goalDescription?.substring(0, 200), challengeId: dzChallenge.challengeId, }, }); autonomyMetrics.recordPause(safetyResult.reason || 'danger_zone', context.stepCount); autonomyMetrics.recordDangerZone(); autonomyMetrics.recordVerification(); return buildDirective(false, safetyResult.reason, factors, { stopped: true, nextStepRisk: 'danger_zone', // Issue #142: Include verification info (without displayCode) so LLM knows how to unblock verification: { verificationId: dzChallenge.challengeId, prompt: dzChallenge.prompt, // displayCode intentionally omitted — shown via OS dialog only challengeType: dzChallenge.challengeType, expiresAt: dzChallenge.expiresAt, }, }); } if (safetyResult.verification) { // Issue #142: Store code server-side, show via OS dialog, strip displayCode storeAndDisplayChallenge({ challengeId: safetyResult.verification.verificationId, displayCode: safetyResult.verification.displayCode, reason: `Verify action: ${(context.nextActionHint || context.currentStepDescription).substring(0, 100)}`, expiresAt: safetyResult.verification.expiresAt, }, context); autonomyMetrics.recordPause(safetyResult.reason || 'verification_required', context.stepCount); autonomyMetrics.recordVerification(); return buildDirective(false, safetyResult.reason, factors, { verification: { verificationId: safetyResult.verification.verificationId, prompt: safetyResult.verification.prompt, // displayCode intentionally omitted — shown via OS dialog only (Issue #142) challengeType: safetyResult.verification.challengeType, expiresAt: safetyResult.verification.expiresAt, }, nextStepRisk: safetyResult.tier, }); } if (!safetyResult.continue) { autonomyMetrics.recordPause(safetyResult.reason || 'safety_tier', context.stepCount); return buildDirective(false, safetyResult.reason, factors, { nextStepRisk: safetyResult.tier, }); } } // Check 5: Risk score vs tolerance threshold if (context.riskScore !== undefined) { const thresholdResult = checkRiskThreshold(context.riskScore, config.riskTolerance); factors.push(thresholdResult.factor); if (!thresholdResult.continue) { autonomyMetrics.recordPause(thresholdResult.reason || 'risk_threshold', context.stepCount); return buildDirective(false, thresholdResult.reason, factors); } } // All checks passed - continue autonomously const stepsRemaining = config.maxAutonomousSteps > 0 ? config.maxAutonomousSteps - context.stepCount - 1 : undefined; factors.push('All autonomy checks passed'); logger.debug('Autonomy evaluation complete: continue', { agentName: context.agentName, stepsRemaining, factors, }); // Issue #391: Record metrics for continue outcome autonomyMetrics.recordContinue(); return buildDirective(true, undefined, factors, { stepsRemaining }); } /** * Check if step count has reached the maximum allowed autonomous steps. * * @param stepCount - Current zero-based step index * @param maxSteps - Maximum autonomous steps allowed (0 = unlimited) * @returns StepLimitResult with continue/pause decision and descriptive factor, * or null if maxSteps is 0 (unlimited) — though in practice always returns a result */ function checkStepLimit(stepCount, maxSteps) { if (maxSteps === 0) { return { continue: true, factor: 'Unlimited autonomous steps configured' }; } if (stepCount >= maxSteps) { return { continue: false, reason: `Maximum autonomous steps reached (${maxSteps})`, factor: `Step ${stepCount + 1} exceeds maxAutonomousSteps=${maxSteps}`, }; } return { continue: true, factor: `Step ${stepCount + 1} of ${maxSteps} autonomous steps`, }; } /** * Check an action string against requiresApproval and autoApprove glob patterns. * * requiresApproval patterns take precedence over autoApprove patterns. * If no pattern matches, the action is allowed by default. * * @param action - The proposed next action string (e.g. "delete user data") * @param requiresApproval - Glob patterns that force a pause (e.g. ["*delete*"]) * @param autoApprove - Glob patterns that explicitly allow continuation (e.g. ["read*"]) * @returns ActionPatternResult with continue/pause decision and matching factor strings */ function checkActionPatterns(action, requiresApproval, autoApprove) { const factors = []; const actionLower = action.toLowerCase(); // requiresApproval takes precedence for (const pattern of requiresApproval) { if (matchesPattern(actionLower, pattern)) { factors.push(`Action matches requiresApproval pattern: ${pattern}`); return { continue: false, reason: `Action requires approval: matches pattern "${pattern}"`, factors, }; } } // Check autoApprove patterns for (const pattern of autoApprove) { if (matchesPattern(actionLower, pattern)) { factors.push(`Action matches autoApprove pattern: ${pattern}`); return { continue: true, factors }; } } factors.push('No pattern match - using default behavior'); return { continue: true, factors }; } // matchesPattern is imported from '../../utils/patternMatcher.js' /** * Evaluate the safety tier for a proposed action and determine if it can proceed. * * Calls `determineSafetyTier()` from the safety package and maps the result * to a continue/pause decision. Tiers escalate: advisory → confirm → verify → danger_zone. * * Issue #389: Wraps determineSafetyTier() in try-catch. If the safety package throws, * logs the error and returns a conservative "pause" result (fail-safe: if we can't * evaluate safety, don't continue). * * @param action - Description of the proposed next action * @param riskScore - Numeric risk score (0-100, already sanitized) * @param riskTolerance - Agent's risk tolerance level ("conservative" | "moderate" | "aggressive") * @param verificationTimeoutMinutes - Expiry time for verification challenges (default: 5) * @returns SafetyTierResult with continue/pause decision, tier info, and optional verification challenge */ function checkSafetyTier(action, riskScore, riskTolerance, verificationTimeoutMinutes = 5) { const factors = []; // Determine safety tier (Issue #389: fail-safe on errors) let tierResult; try { tierResult = determineSafetyTier(riskScore, [], // No security warnings for step evaluation action, DEFAULT_SAFETY_CONFIG); } catch (error) { const errorMsg = error instanceof Error ? error.message : String(error); logger.error('Safety tier evaluation failed — returning conservative pause', { error: errorMsg, action: action.substring(0, 200), riskScore, riskTolerance, }); SecurityMonitor.logSecurityEvent({ type: 'SAFETY_EVALUATION_FAILURE', severity: 'HIGH', source: 'AutonomyEvaluator.checkSafetyTier', details: `determineSafetyTier() threw: ${errorMsg}`, additionalData: { action: action.substring(0, 200), riskScore, riskTolerance }, }); factors.push(`Safety tier evaluation failed: ${errorMsg}`); return { continue: false, reason: 'Safety evaluation failed — pausing for human review', factors, }; } factors.push(`Safety tier: ${tierResult.tier}`); factors.push(...tierResult.factors.map((f) => ` - ${f}`)); switch (tierResult.tier) { case 'advisory': return { continue: true, factors, tier: 'advisory' }; case 'confirm': // For moderate+ tolerance, auto-approve confirm tier if (riskTolerance === 'aggressive') { factors.push('Aggressive risk tolerance: auto-approving CONFIRM tier'); return { continue: true, factors, tier: 'confirm' }; } return { continue: false, reason: 'Action requires confirmation before proceeding', factors, tier: 'confirm', }; case 'verify': { // Create verification challenge const challenge = createVerificationChallenge(`Verify action: ${action.substring(0, 100)}`, 'display_code', verificationTimeoutMinutes); return { continue: false, reason: 'Action requires verification before proceeding', factors, tier: 'verify', verification: { verificationId: challenge.challengeId, prompt: challenge.prompt, displayCode: challenge.displayCode, challengeType: challenge.challengeType, expiresAt: challenge.expiresAt, }, }; } case 'danger_zone': return { continue: false, reason: 'Action blocked: danger zone operation detected', factors, tier: 'danger_zone', stopped: true, }; default: return { continue: true, factors, tier: tierResult.tier }; } } /** * Check whether a risk score exceeds the threshold for the given tolerance level. * * Uses strict greater-than comparison: a score equal to the threshold is allowed. * Issue #390: Thresholds are now configurable via environment variables. * * @param riskScore - Numeric risk score (0-100, already sanitized) * @param riskTolerance - Agent's risk tolerance level ("conservative" | "moderate" | "aggressive") * @returns RiskThresholdResult with continue/pause decision and descriptive factor */ function checkRiskThreshold(riskScore, riskTolerance) { const thresholds = getAutonomyRiskThresholds(); const threshold = thresholds[riskTolerance] ?? thresholds.moderate; if (riskScore > threshold) { return { continue: false, reason: `Risk score (${riskScore}) exceeds ${riskTolerance} threshold (${threshold})`, factor: `Risk ${riskScore} > threshold ${threshold} for ${riskTolerance} tolerance`, }; } return { continue: true, factor: `Risk ${riskScore} within ${riskTolerance} threshold (${threshold})`, }; } /** * Merge a partial user-provided autonomy config with defaults. * * Missing fields fall back to DEFAULT_AUTONOMY_CONFIG values, except * maxAutonomousSteps which reads the env-configurable default (Issue #390). * * @param config - Optional partial autonomy config from the agent definition * @returns Fully populated config with all fields guaranteed present */ function mergeWithDefaults(config) { return { riskTolerance: config?.riskTolerance ?? DEFAULT_AUTONOMY_CONFIG.riskTolerance, maxAutonomousSteps: config?.maxAutonomousSteps ?? getAutonomyMaxStepsDefault(), requiresApproval: config?.requiresApproval ?? DEFAULT_AUTONOMY_CONFIG.requiresApproval, autoApprove: config?.autoApprove ?? DEFAULT_AUTONOMY_CONFIG.autoApprove, verificationTimeoutMinutes: config?.verificationTimeoutMinutes ?? DEFAULT_AUTONOMY_CONFIG.verificationTimeoutMinutes, }; } /** * Build a structured AutonomyDirective from components. * * @param shouldContinue - Whether the agent may continue autonomously * @param reason - Human-readable explanation when paused (undefined when continuing) * @param factors - Array of descriptive strings explaining each check's contribution * @param extras - Optional additional fields (stepsRemaining, stopped, verification, nextStepRisk) * @returns Complete AutonomyDirective ready for the LLM handoff */ function buildDirective(shouldContinue, reason, factors, extras) { return { continue: shouldContinue, reason, factors, ...extras, }; } /** * Quick check if an action would be auto-approved given an autonomy config. * * Utility function for pre-checking actions before execution. * Does NOT evaluate safety tiers or risk scores — only pattern matching. * * @param action - The proposed action string to check * @param config - Optional autonomy config (uses defaults if not provided) * @returns true if the action matches an autoApprove pattern or the agent * uses aggressive tolerance; false if it matches requiresApproval or no match */ export function wouldAutoApprove(action, config) { const mergedConfig = mergeWithDefaults(config); const actionLower = action.toLowerCase(); // Check requiresApproval first (takes precedence) for (const pattern of mergedConfig.requiresApproval) { if (matchesPattern(actionLower, pattern)) { return false; } } // Check autoApprove for (const pattern of mergedConfig.autoApprove) { if (matchesPattern(actionLower, pattern)) { return true; } } // Default behavior depends on risk tolerance return mergedConfig.riskTolerance === 'aggressive'; } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYXV0b25vbXlFdmFsdWF0b3IuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvZWxlbWVudHMvYWdlbnRzL2F1dG9ub215RXZhbHVhdG9yLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0dBdURHO0FBRUgsT0FBTyxFQUFFLE1BQU0sRUFBRSxNQUFNLHVCQUF1QixDQUFDO0FBQy9DLE9BQU8sRUFBRSxlQUFlLEVBQUUsTUFBTSxtQ0FBbUMsQ0FBQztBQUNwRSxPQUFPLEVBQUUsY0FBYyxFQUFFLE1BQU0sK0JBQStCLENBQUM7QUFDL0QsT0FBTyxFQUNMLG1CQUFtQixFQUNuQiwyQkFBMkIsRUFDM0IscUJBQXFCLEVBQ3JCLHNCQUFzQixHQUN2QixNQUFNLHdCQUF3QixDQUFDO0FBQ2hDLE9BQU8sRUFDTCx5QkFBeUIsRUFDekIsMEJBQTBCLEdBQzNCLE1BQU0saUNBQWlDLENBQUM7QUFRekM7Ozs7Ozs7OztHQVNHO0FBQ0gsTUFBTSxDQUFDLE1BQU0sdUJBQXVCLEdBQWtDO0lBQ3BFLGFBQWEsRUFBRSxVQUFVO0lBQ3pCLGtCQUFrQixFQUFFLEVBQUU7SUFDdEIsZ0JBQWdCLEVBQUUsRUFBRTtJQUNwQixXQUFXLEVBQUUsRUFBRTtJQUNmLDBCQUEwQixFQUFFLENBQUM7Q0FDOUIsQ0FBQztBQTZFRjs7O0dBR0c7QUFDSCxNQUFNLHNCQUFzQjtJQUNsQixpQkFBaUIsR0FBRyxDQUFDLENBQUM7SUFDdEIsY0FBYyxHQUFHLENBQUMsQ0FBQztJQUNuQixXQUFXLEdBQUcsQ0FBQyxDQUFDO0lBQ2hCLGFBQWEsR0FBMkIsRUFBRSxDQUFDO0lBQzNDLG9CQUFvQixHQUFHLENBQUMsQ0FBQztJQUN6QixxQkFBcUIsR0FBRyxDQUFDLENBQUM7SUFDMUIsZ0JBQWdCLEdBQWEsRUFBRSxDQUFDO0lBQ2hDLE1BQU0sQ0FBVSxlQUFlLEdB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QUFXLEVBQUUsQ0FBQztRQUMvQyxJQUFJLGNBQWMsQ0FBQyxXQUFXLEVBQUUsT0FBTyxDQUFDLEVBQUUsQ0FBQztZQUN6QyxPQUFPLElBQUksQ0FBQztRQUNkLENBQUM7SUFDSCxDQUFDO0lBRUQsNkNBQTZDO0lBQzdDLE9BQU8sWUFBWSxDQUFDLGFBQWEsS0FBSyxZQUFZLENBQUM7QUFDckQsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogQXV0b25vbXkgRXZhbHVhdG9yIFNlcnZpY2VcbiAqXG4gKiBEZXRlcm1pbmVzIHdoZXRoZXIgYW4gYWdlbnQgc2hvdWxkIGNvbnRpbnVlIGF1dG9ub21vdXNseSBvciBwYXVzZVxuICogZm9yIGh1bWFuIGlucHV0IGFmdGVyIGVhY2ggc3RlcC4gVGhpcyBpcyB0aGUgYnJhaW4gdGhhdCBlbmFibGVzXG4gKiBhdXRvbWF0aWMgY29udGludWUvcGF1c2UgZGVjaXNpb25zIGluIHRoZSBhZ2VudGljIGxvb3AuXG4gKlxuICogRGVjaXNpb24gZmFjdG9yczpcbiAqIDEuIFN0ZXAgY291bnQgdnMgbWF4QXV0b25vbW91c1N0ZXBzXG4gKiAyLiBOZXh0IGFjdGlvbiB2cyByZXF1aXJlc0FwcHJvdmFsIHBhdHRlcm5zXG4gKiAzLiBTYWZldHkgdGllciAoQUxMT1cvVkVSSUZZL0RFTlkpXG4gKiA0LiBSaXNrIHRvbGVyYW5jZSBjb25maWd1cmF0aW9uXG4gKlxuICogIyMgRGFuZ2VyIFpvbmUgVmVyaWZpY2F0aW9uIEZsb3cgKElzc3VlICMxNDIpXG4gKlxuICogV2hlbiBhbiBhZ2VudCBhY3Rpb24gdHJpZ2dlcnMgdGhlIERBTkdFUl9aT05FIG9yIFZFUklGWSBzYWZldHkgdGllcixcbiAqIGEgaHVtYW4taW4tdGhlLWxvb3AgdmVyaWZpY2F0aW9uIGlzIHJlcXVpcmVkIGJlZm9yZSB0aGUgYWdlbnQgY2FuIHByb2NlZWQuXG4gKlxuICogRW5kLXRvLWVuZCBzZXF1ZW5jZTpcbiAqXG4gKiAgMS4gQWdlbnQgcHJvcG9zZXMgYW4gYWN0aW9uIChuZXh0QWN0aW9uSGludCkgZHVyaW5nIGFnZW50aWMgbG9vcFxuICogIDIuIGV2YWx1YXRlQXV0b25vbXkoKSBjYWxscyBjaGVja1NhZmV0eVRpZXIoKSDihpIgcmV0dXJucyBEQU5HRVJfWk9ORSBvciBWRVJJRllcbiAqICAzLiBjcmVhdGVWZXJpZmljYXRpb25DaGFsbGVuZ2UoKSBnZW5lcmF0ZXMgYSBjaGFsbGVuZ2Ugd2l0aDpcbiAqICAgICAtIGNoYWxsZW5nZUlkIChVVUlEIHY0IHZpYSBjcnlwdG8ucmFuZG9tVVVJRClcbiAqICAgICAtIGRpc3BsYXlDb2RlIChodW1hbi1yZWFkYWJsZSBjb2RlLCBlLmcuIFwiQUJDMTIzXCIpXG4gKiAgICAgLSBleHBpcmVzQXQgKGNvbmZpZ3VyYWJsZSwgZGVmYXVsdCA1IG1pbnV0ZXMpXG4gKiAgNC4gc3RvcmVBbmREaXNwbGF5Q2hhbGxlbmdlKCk6XG4gKiAgICAgYS4gU3RvcmVzIHtjb2RlLCBleHBpcmVzQXQsIHJlYXNvbn0gaW4gVmVyaWZpY2F0aW9uU3RvcmUgKHNlcnZlci1zaWRlLCBvbmUtdGltZSB1c2UpXG4gKiAgICAgYi4gU2hvd3MgZGlzcGxheUNvZGUgdG8gaHVtYW4gdmlhIE9TLW5hdGl2ZSBkaWFsb2cgKEFwcGxlU2NyaXB0L3plbml0eS9Qb3dlclNoZWxsKVxuICogICAgIGMuIGRpc3BsYXlDb2RlIGlzIE5FVkVSIGluY2x1ZGVkIGluIHRoZSBMTE0tZmFjaW5nIGRpcmVjdGl2ZVxuICogIDUuIEZvciBEQU5HRVJfWk9ORTogRGFuZ2VyWm9uZUVuZm9yY2VyLmJsb2NrKCkgcHJvZ3JhbW1hdGljYWxseSBibG9ja3MgdGhlIGFnZW50XG4gKiAgICAgd2l0aCB2ZXJpZmljYXRpb25JZD1jaGFsbGVuZ2VJZCAoZmlsZS1iYWNrZWQsIHN1cnZpdmVzIHJlc3RhcnRzKVxuICogIDYuIExMTSByZWNlaXZlcyBkaXJlY3RpdmUgd2l0aCB2ZXJpZmljYXRpb24udmVyaWZpY2F0aW9uSWQgYnV0IE5PIGRpc3BsYXlDb2RlXG4gKiAgICAgcGx1cyBhY3Rpb25hYmxlIGd1aWRhbmNlOiBcIlVzZSB2ZXJpZnlfY2hhbGxlbmdlIHsgY2hhbGxlbmdlX2lkLCBjb2RlIH1cIlxuICogIDcuIEh1bWFuIHJlYWRzIGNvZGUgZnJvbSBPUyBkaWFsb2csIHRlbGxzIExMTVxuICogIDguIExMTSBjYWxscyB2ZXJpZnlfY2hhbGxlbmdlIE1DUC1BUUwgb3BlcmF0aW9uXG4gKiAgOS4gTUNQQVFMSGFuZGxlciB2ZXJpZnkgaGFuZGxlcjpcbiAqICAgICBhLiBWYWxpZGF0ZXMgVVVJRCB2NCBmb3JtYXQgKHJlamVjdHMgaW52YWxpZCBJRHMgYmVmb3JlIHN0b3JlIGxvb2t1cClcbiAqICAgICBiLiBDaGVja3MgcmF0ZSBsaW1pdCAobWF4IDEwIGZhaWx1cmVzIHBlciA2MHMgc2xpZGluZyB3aW5kb3cpXG4gKiAgICAgYy4gRGlzdGluZ3Vpc2hlcyBleHBpcmVkIChWRVJJRklDQVRJT05fRVhQSVJFRCkgZnJvbSB3cm9uZyBjb2RlIChWRVJJRklDQVRJT05fRkFJTEVEKVxuICogICAgIGQuIE9uIHN1Y2Nlc3M6IGZpbmRzIGJsb2NrZWQgYWdlbnQgYnkgY2hhbGxlbmdlSWQg4oaSIERhbmdlclpvbmVFbmZvcmNlci51bmJsb2NrKClcbiAqICAgICBlLiBMb2dzIGdyYW51bGFyIHNlY3VyaXR5IGV2ZW50cyBhdCBlYWNoIHN0YWdlXG4gKiAxMC4gQWdlbnQgcmV0cmllcyB0aGUgb3BlcmF0aW9uIOKGkiBwYXNzZXMgKG5vIGxvbmdlciBibG9ja2VkKVxuICpcbiAqIFNlY3VyaXR5IGludmFyaWFudHM6XG4gKiAtIGRpc3BsYXlDb2RlIG5ldmVyIHJlYWNoZXMgdGhlIExMTSAoc3RyaXBwZWQgYmVmb3JlIGRpcmVjdGl2ZSBpcyBidWlsdClcbiAqIC0gQ29kZXMgYXJlIG9uZS10aW1lIHVzZSAoVmVyaWZpY2F0aW9uU3RvcmUgZGVs