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.

111 lines 13.1 kB
/** * Resilience Metrics Tracker * * Provides observability into agent execution resilience actions: * auto-continuations past step limits, step retries, auto-restarts, * circuit breaker trips, and outcome tracking after resilience fires. * * Issue #526: Visibility into how often resilience actions fire and * whether they lead to successful completions. */ import { logger } from '../../utils/logger.js'; // ============================================================================ // Metrics tracker // ============================================================================ /** * Lightweight in-memory tracker for agent resilience patterns. * Issue #526: Provides observability into auto-continue, retry, and * circuit breaker behaviour during agent execution. */ export class ResilienceMetricsTracker { _autoContinuations = 0; _autoRestarts = 0; _stepRetries = 0; _resilienceLimitsReached = 0; _circuitBreakerTrips = 0; _successAfterResilience = 0; _failureAfterResilience = 0; static LOG_INTERVAL = 25; recordAutoContinuation() { this._autoContinuations++; this.maybeLogSnapshot(); } recordAutoRestart() { this._autoRestarts++; this.maybeLogSnapshot(); } recordStepRetry() { this._stepRetries++; this.maybeLogSnapshot(); } recordResilienceLimit() { this._resilienceLimitsReached++; this.maybeLogSnapshot(); } recordCircuitBreakerTrip() { this._circuitBreakerTrips++; this.maybeLogSnapshot(); } recordCompletionAfterResilience(success) { if (success) { this._successAfterResilience++; } else { this._failureAfterResilience++; } this.maybeLogSnapshot(); } getSnapshot() { return { autoContinuations: this._autoContinuations, autoRestarts: this._autoRestarts, stepRetries: this._stepRetries, resilienceLimitsReached: this._resilienceLimitsReached, circuitBreakerTrips: this._circuitBreakerTrips, successAfterResilience: this._successAfterResilience, failureAfterResilience: this._failureAfterResilience, }; } /** Reset all counters (for testing). */ reset() { this._autoContinuations = 0; this._autoRestarts = 0; this._stepRetries = 0; this._resilienceLimitsReached = 0; this._circuitBreakerTrips = 0; this._successAfterResilience = 0; this._failureAfterResilience = 0; } get _totalActions() { return (this._autoContinuations + this._autoRestarts + this._stepRetries + this._resilienceLimitsReached + this._circuitBreakerTrips); } maybeLogSnapshot() { if (this._totalActions > 0 && this._totalActions % ResilienceMetricsTracker.LOG_INTERVAL === 0) { logger.info('Resilience metrics snapshot', { ...this.getSnapshot(), }); } } } /** Module-level singleton instance (Issue #526) */ export const resilienceMetrics = new ResilienceMetricsTracker(); /** * Get the current resilience metrics snapshot. * Issue #526: Programmatic access for monitoring/diagnostics. */ export function getResilienceMetrics() { return resilienceMetrics.getSnapshot(); } /** * Reset resilience metrics (for testing only). * @internal */ export function resetResilienceMetrics() { resilienceMetrics.reset(); } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicmVzaWxpZW5jZU1ldHJpY3MuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvZWxlbWVudHMvYWdlbnRzL3Jlc2lsaWVuY2VNZXRyaWNzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOzs7Ozs7Ozs7R0FTRztBQUVILE9BQU8sRUFBRSxNQUFNLEVBQUUsTUFBTSx1QkFBdUIsQ0FBQztBQTJCL0MsK0VBQStFO0FBQy9FLGtCQUFrQjtBQUNsQiwrRUFBK0U7QUFFL0U7Ozs7R0FJRztBQUNILE1BQU0sT0FBTyx3QkFBd0I7SUFDM0Isa0JBQWtCLEdBQUcsQ0FBQyxDQUFDO0lBQ3ZCLGFBQWEsR0FBRyxDQUFDLENBQUM7SUFDbEIsWUFBWSxHQUFHLENBQUMsQ0FBQztJQUNqQix3QkFBd0IsR0FBRyxDQUFDLENBQUM7SUFDN0Isb0JBQW9CLEdBQUcsQ0FBQyxDQUFDO0lBQ3pCLHVCQUF1QixHQUFHLENBQUMsQ0FBQztJQUM1Qix1QkFBdUIsR0FBRyxDQUFDLENBQUM7SUFFNUIsTUFBTSxDQUFVLFlBQVksR0FBRyxFQUFFLENBQUM7SUFFMUMsc0JBQXNCO1FBQ3BCLElBQUksQ0FBQyxrQkFBa0IsRUFBRSxDQUFDO1FBQzFCLElBQUksQ0FBQyxnQkFBZ0IsRUFBRSxDQUFDO0lBQzFCLENBQUM7SUFFRCxpQkFBaUI7UUFDZixJQUFJLENBQUMsYUFBYSxFQUFFLENBQUM7UUFDckIsSUFBSSxDQUFDLGdCQUFnQixFQUFFLENBQUM7SUFDMUIsQ0FBQztJQUVELGVBQWU7UUFDYixJQUFJLENBQUMsWUFBWSxFQUFFLENBQUM7UUFDcEIsSUFBSSxDQUFDLGdCQUFnQixFQUFFLENBQUM7SUFDMUIsQ0FBQztJQUVELHFCQUFxQjtRQUNuQixJQUFJLENBQUMsd0JBQXdCLEVBQUUsQ0FBQztRQUNoQyxJQUFJLENBQUMsZ0JBQWdCLEVBQUUsQ0FBQztJQUMxQixDQUFDO0lBRUQsd0JBQXdCO1FBQ3RCLElBQUksQ0FBQyxvQkFBb0IsRUFBRSxDQUFDO1FBQzVCLElBQUksQ0FBQyxnQkFBZ0IsRUFBRSxDQUFDO0lBQzFCLENBQUM7SUFFRCwrQkFBK0IsQ0FBQyxPQUFnQjtRQUM5QyxJQUFJLE9BQU8sRUFBRSxDQUFDO1lBQ1osSUFBSSxDQUFDLHVCQUF1QixFQUFFLENBQUM7UUFDakMsQ0FBQzthQUFNLENBQUM7WUFDTixJQUFJLENBQUMsdUJBQXVCLEVBQUUsQ0FBQztRQUNqQyxDQUFDO1FBQ0QsSUFBSSxDQUFDLGdCQUFnQixFQUFFLENBQUM7SUFDMUIsQ0FBQztJQUVELFdBQVc7UUFDVCxPQUFPO1lBQ0wsaUJBQWlCLEVBQUUsSUFBSSxDQUFDLGtCQUFrQjtZQUMxQyxZQUFZLEVBQUUsSUFBSSxDQUFDLGFBQWE7WUFDaEMsV0FBVyxFQUFFLElBQUksQ0FBQyxZQUFZO1lBQzlCLHVCQUF1QixFQUFFLElBQUksQ0FBQyx3QkFBd0I7WUFDdEQsbUJBQW1CLEVBQUUsSUFBSSxDQUFDLG9CQUFvQjtZQUM5QyxzQkFBc0IsRUFBRSxJQUFJLENBQUMsdUJBQXVCO1lBQ3BELHNCQUFzQixFQUFFLElBQUksQ0FBQyx1QkFBdUI7U0FDckQsQ0FBQztJQUNKLENBQUM7SUFFRCx3Q0FBd0M7SUFDeEMsS0FBSztRQUNILElBQUksQ0FBQyxrQkFBa0IsR0FBRyxDQUFDLENBQUM7UUFDNUIsSUFBSSxDQUFDLGFBQWEsR0FBRyxDQUFDLENBQUM7UUFDdkIsSUFBSSxDQUFDLFlBQVksR0FBRyxDQUFDLENBQUM7UUFDdEIsSUFBSSxDQUFDLHdCQUF3QixHQUFHLENBQUMsQ0FBQztRQUNsQyxJQUFJLENBQUMsb0JBQW9CLEdBQUcsQ0FBQyxDQUFDO1FBQzlCLElBQUksQ0FBQyx1QkFBdUIsR0FBRyxDQUFDLENBQUM7UUFDakMsSUFBSSxDQUFDLHVCQUF1QixHQUFHLENBQUMsQ0FBQztJQUNuQyxDQUFDO0lBRUQsSUFBWSxhQUFhO1FBQ3ZCLE9BQU8sQ0FDTCxJQUFJLENBQUMsa0JBQWtCO1lBQ3ZCLElBQUksQ0FBQyxhQUFhO1lBQ2xCLElBQUksQ0FBQyxZQUFZO1lBQ2pCLElBQUksQ0FBQyx3QkFBd0I7WUFDN0IsSUFBSSxDQUFDLG9CQUFvQixDQUMxQixDQUFDO0lBQ0osQ0FBQztJQUVPLGdCQUFnQjtRQUN0QixJQUNFLElBQUksQ0FBQyxhQUFhLEdBQUcsQ0FBQztZQUN0QixJQUFJLENBQUMsYUFBYSxHQUFHLHdCQUF3QixDQUFDLFlBQVksS0FBSyxDQUFDLEVBQ2hFLENBQUM7WUFDRCxNQUFNLENBQUMsSUFBSSxDQUFDLDZCQUE2QixFQUFFO2dCQUN6QyxHQUFHLElBQUksQ0FBQyxXQUFXLEVBQUU7YUFDdEIsQ0FBQyxDQUFDO1FBQ0wsQ0FBQztJQUNILENBQUM7O0FBR0gsbURBQW1EO0FBQ25ELE1BQU0sQ0FBQyxNQUFNLGlCQUFpQixHQUFHLElBQUksd0JBQXdCLEVBQUUsQ0FBQztBQUVoRTs7O0dBR0c7QUFDSCxNQUFNLFVBQVUsb0JBQW9CO0lBQ2xDLE9BQU8saUJBQWlCLENBQUMsV0FBVyxFQUFFLENBQUM7QUFDekMsQ0FBQztBQUVEOzs7R0FHRztBQUNILE1BQU0sVUFBVSxzQkFBc0I7SUFDcEMsaUJBQWlCLENBQUMsS0FBSyxFQUFFLENBQUM7QUFDNUIsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogUmVzaWxpZW5jZSBNZXRyaWNzIFRyYWNrZXJcbiAqXG4gKiBQcm92aWRlcyBvYnNlcnZhYmlsaXR5IGludG8gYWdlbnQgZXhlY3V0aW9uIHJlc2lsaWVuY2UgYWN0aW9uczpcbiAqIGF1dG8tY29udGludWF0aW9ucyBwYXN0IHN0ZXAgbGltaXRzLCBzdGVwIHJldHJpZXMsIGF1dG8tcmVzdGFydHMsXG4gKiBjaXJjdWl0IGJyZWFrZXIgdHJpcHMsIGFuZCBvdXRjb21lIHRyYWNraW5nIGFmdGVyIHJlc2lsaWVuY2UgZmlyZXMuXG4gKlxuICogSXNzdWUgIzUyNjogVmlzaWJpbGl0eSBpbnRvIGhvdyBvZnRlbiByZXNpbGllbmNlIGFjdGlvbnMgZmlyZSBhbmRcbiAqIHdoZXRoZXIgdGhleSBsZWFkIHRvIHN1Y2Nlc3NmdWwgY29tcGxldGlvbnMuXG4gKi9cblxuaW1wb3J0IHsgbG9nZ2VyIH0gZnJvbSAnLi4vLi4vdXRpbHMvbG9nZ2VyLmpzJztcblxuLy8gPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuLy8gTWV0cmljcyBzbmFwc2hvdCBpbnRlcmZhY2Vcbi8vID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cblxuLyoqXG4gKiBTbmFwc2hvdCBvZiByZXNpbGllbmNlIG1ldHJpY3MgY291bnRlcnMuXG4gKiBOb24tcGVyc2lzdGVkLCBpbi1tZW1vcnkgY291bnRlcnMgcmVzZXQgb24gc2VydmVyIHJlc3RhcnQuXG4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgUmVzaWxpZW5jZU1ldHJpY3NTbmFwc2hvdCB7XG4gIC8qKiBUb3RhbCBhdXRvLWNvbnRpbnVhdGlvbnMgdHJpZ2dlcmVkIChhZ2VudCBjb250aW51ZWQgcGFzdCBzdGVwIGxpbWl0KSAqL1xuICBhdXRvQ29udGludWF0aW9uczogbnVtYmVyO1xuICAvKiogVG90YWwgYXV0by1yZXN0YXJ0cyB0cmlnZ2VyZWQgKGFnZW50IHJlc3RhcnRlZCBhZnRlciBmYWlsdXJlKSAqL1xuICBhdXRvUmVzdGFydHM6IG51bWJlcjtcbiAgLyoqIFRvdGFsIHN0ZXAgcmV0cmllcyB0cmlnZ2VyZWQgKGluZGl2aWR1YWwgc3RlcCByZXRyaWVkIGFmdGVyIGVycm9yKSAqL1xuICBzdGVwUmV0cmllczogbnVtYmVyO1xuICAvKiogVGltZXMgbWF4Q29udGludWF0aW9ucy9tYXhSZXRyaWVzIGV4aGF1c3RlZCwgZm9yY2luZyBhIHBhdXNlICovXG4gIHJlc2lsaWVuY2VMaW1pdHNSZWFjaGVkOiBudW1iZXI7XG4gIC8qKiBUaW1lcyBjaXJjdWl0IGJyZWFrZXIgZW5nYWdlZCAocmVwZWF0ZWQgZmFpbHVyZXMgdHJpcHBlZCBicmVha2VyKSAqL1xuICBjaXJjdWl0QnJlYWtlclRyaXBzOiBudW1iZXI7XG4gIC8qKiBFeGVjdXRpb25zIHRoYXQgY29tcGxldGVkIHN1Y2Nlc3NmdWxseSBhZnRlciBhdCBsZWFzdCBvbmUgcmVzaWxpZW5jZSBhY3Rpb24gKi9cbiAgc3VjY2Vzc0FmdGVyUmVzaWxpZW5jZTogbnVtYmVyO1xuICAvKiogRXhlY3V0aW9ucyB0aGF0IGZhaWxlZC9hYm9ydGVkIGFmdGVyIGF0IGxlYXN0IG9uZSByZXNpbGllbmNlIGFjdGlvbiAqL1xuICBmYWlsdXJlQWZ0ZXJSZXNpbGllbmNlOiBudW1iZXI7XG59XG5cbi8vID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cbi8vIE1ldHJpY3MgdHJhY2tlclxuLy8gPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuXG4vKipcbiAqIExpZ2h0d2VpZ2h0IGluLW1lbW9yeSB0cmFja2VyIGZvciBhZ2VudCByZXNpbGllbmNlIHBhdHRlcm5zLlxuICogSXNzdWUgIzUyNjogUHJvdmlkZXMgb2JzZXJ2YWJpbGl0eSBpbnRvIGF1dG8tY29udGludWUsIHJldHJ5LCBhbmRcbiAqIGNpcmN1aXQgYnJlYWtlciBiZWhhdmlvdXIgZHVyaW5nIGFnZW50IGV4ZWN1dGlvbi5cbiAqL1xuZXhwb3J0IGNsYXNzIFJlc2lsaWVuY2VNZXRyaWNzVHJhY2tlciB7XG4gIHByaXZhdGUgX2F1dG9Db250aW51YXRpb25zID0gMDtcbiAgcHJpdmF0ZSBfYXV0b1Jlc3RhcnRzID0gMDtcbiAgcHJpdmF0ZSBfc3RlcFJldHJpZXMgPSAwO1xuICBwcml2YXRlIF9yZXNpbGllbmNlTGltaXRzUmVhY2hlZCA9IDA7XG4gIHByaXZhdGUgX2NpcmN1aXRCcmVha2VyVHJpcHMgPSAwO1xuICBwcml2YXRlIF9zdWNjZXNzQWZ0ZXJSZXNpbGllbmNlID0gMDtcbiAgcHJpdmF0ZSBfZmFpbHVyZUFmdGVyUmVzaWxpZW5jZSA9IDA7XG5cbiAgcHJpdmF0ZSBzdGF0aWMgcmVhZG9ubHkgTE9HX0lOVEVSVkFMID0gMjU7XG5cbiAgcmVjb3JkQXV0b0NvbnRpbnVhdGlvbigpOiB2b2lkIHtcbiAgICB0aGlzLl9hdXRvQ29udGludWF0aW9ucysrO1xuICAgIHRoaXMubWF5YmVMb2dTbmFwc2hvdCgpO1xuICB9XG5cbiAgcmVjb3JkQXV0b1Jlc3RhcnQoKTogdm9pZCB7XG4gICAgdGhpcy5fYXV0b1Jlc3RhcnRzKys7XG4gICAgdGhpcy5tYXliZUxvZ1NuYXBzaG90KCk7XG4gIH1cblxuICByZWNvcmRTdGVwUmV0cnkoKTogdm9pZCB7XG4gICAgdGhpcy5fc3RlcFJldHJpZXMrKztcbiAgICB0aGlzLm1heWJlTG9nU25hcHNob3QoKTtcbiAgfVxuXG4gIHJlY29yZFJlc2lsaWVuY2VMaW1pdCgpOiB2b2lkIHtcbiAgICB0aGlzLl9yZXNpbGllbmNlTGltaXRzUmVhY2hlZCsrO1xuICAgIHRoaXMubWF5YmVMb2dTbmFwc2hvdCgpO1xuICB9XG5cbiAgcmVjb3JkQ2lyY3VpdEJyZWFrZXJUcmlwKCk6IHZvaWQge1xuICAgIHRoaXMuX2NpcmN1aXRCcmVha2VyVHJpcHMrKztcbiAgICB0aGlzLm1heWJlTG9nU25hcHNob3QoKTtcbiAgfVxuXG4gIHJlY29yZENvbXBsZXRpb25BZnRlclJlc2lsaWVuY2Uoc3VjY2VzczogYm9vbGVhbik6IHZvaWQge1xuICAgIGlmIChzdWNjZXNzKSB7XG4gICAgICB0aGlzLl9zdWNjZXNzQWZ0ZXJSZXNpbGllbmNlKys7XG4gICAgfSBlbHNlIHtcbiAgICAgIHRoaXMuX2ZhaWx1cmVBZnRlclJlc2lsaWVuY2UrKztcbiAgICB9XG4gICAgdGhpcy5tYXliZUxvZ1NuYXBzaG90KCk7XG4gIH1cblxuICBnZXRTbmFwc2hvdCgpOiBSZXNpbGllbmNlTWV0cmljc1NuYXBzaG90IHtcbiAgICByZXR1cm4ge1xuICAgICAgYXV0b0NvbnRpbnVhdGlvbnM6IHRoaXMuX2F1dG9Db250aW51YXRpb25zLFxuICAgICAgYXV0b1Jlc3RhcnRzOiB0aGlzLl9hdXRvUmVzdGFydHMsXG4gICAgICBzdGVwUmV0cmllczogdGhpcy5fc3RlcFJldHJpZXMsXG4gICAgICByZXNpbGllbmNlTGltaXRzUmVhY2hlZDogdGhpcy5fcmVzaWxpZW5jZUxpbWl0c1JlYWNoZWQsXG4gICAgICBjaXJjdWl0QnJlYWtlclRyaXBzOiB0aGlzLl9jaXJjdWl0QnJlYWtlclRyaXBzLFxuICAgICAgc3VjY2Vzc0FmdGVyUmVzaWxpZW5jZTogdGhpcy5fc3VjY2Vzc0FmdGVyUmVzaWxpZW5jZSxcbiAgICAgIGZhaWx1cmVBZnRlclJlc2lsaWVuY2U6IHRoaXMuX2ZhaWx1cmVBZnRlclJlc2lsaWVuY2UsXG4gICAgfTtcbiAgfVxuXG4gIC8qKiBSZXNldCBhbGwgY291bnRlcnMgKGZvciB0ZXN0aW5nKS4gKi9cbiAgcmVzZXQoKTogdm9pZCB7XG4gICAgdGhpcy5fYXV0b0NvbnRpbnVhdGlvbnMgPSAwO1xuICAgIHRoaXMuX2F1dG9SZXN0YXJ0cyA9IDA7XG4gICAgdGhpcy5fc3RlcFJldHJpZXMgPSAwO1xuICAgIHRoaXMuX3Jlc2lsaWVuY2VMaW1pdHNSZWFjaGVkID0gMDtcbiAgICB0aGlzLl9jaXJjdWl0QnJlYWtlclRyaXBzID0gMDtcbiAgICB0aGlzLl9zdWNjZXNzQWZ0ZXJSZXNpbGllbmNlID0gMDtcbiAgICB0aGlzLl9mYWlsdXJlQWZ0ZXJSZXNpbGllbmNlID0gMDtcbiAgfVxuXG4gIHByaXZhdGUgZ2V0IF90b3RhbEFjdGlvbnMoKTogbnVtYmVyIHtcbiAgICByZXR1cm4gKFxuICAgICAgdGhpcy5fYXV0b0NvbnRpbnVhdGlvbnMgK1xuICAgICAgdGhpcy5fYXV0b1Jlc3RhcnRzICtcbiAgICAgIHRoaXMuX3N0ZXBSZXRyaWVzICtcbiAgICAgIHRoaXMuX3Jlc2lsaWVuY2VMaW1pdHNSZWFjaGVkICtcbiAgICAgIHRoaXMuX2NpcmN1aXRCcmVha2VyVHJpcHNcbiAgICApO1xuICB9XG5cbiAgcHJpdmF0ZSBtYXliZUxvZ1NuYXBzaG90KCk6IHZvaWQge1xuICAgIGlmIChcbiAgICAgIHRoaXMuX3RvdGFsQWN0aW9ucyA+IDAgJiZcbiAgICAgIHRoaXMuX3RvdGFsQWN0aW9ucyAlIFJlc2lsaWVuY2VNZXRyaWNzVHJhY2tlci5MT0dfSU5URVJWQUwgPT09IDBcbiAgICApIHtcbiAgICAgIGxvZ2dlci5pbmZvKCdSZXNpbGllbmNlIG1ldHJpY3Mgc25hcHNob3QnLCB7XG4gICAgICAgIC4uLnRoaXMuZ2V0U25hcHNob3QoKSxcbiAgICAgIH0pO1xuICAgIH1cbiAgfVxufVxuXG4vKiogTW9kdWxlLWxldmVsIHNpbmdsZXRvbiBpbnN0YW5jZSAoSXNzdWUgIzUyNikgKi9cbmV4cG9ydCBjb25zdCByZXNpbGllbmNlTWV0cmljcyA9IG5ldyBSZXNpbGllbmNlTWV0cmljc1RyYWNrZXIoKTtcblxuLyoqXG4gKiBHZXQgdGhlIGN1cnJlbnQgcmVzaWxpZW5jZSBtZXRyaWNzIHNuYXBzaG90LlxuICogSXNzdWUgIzUyNjogUHJvZ3JhbW1hdGljIGFjY2VzcyBmb3IgbW9uaXRvcmluZy9kaWFnbm9zdGljcy5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGdldFJlc2lsaWVuY2VNZXRyaWNzKCk6IFJlc2lsaWVuY2VNZXRyaWNzU25hcHNob3Qge1xuICByZXR1cm4gcmVzaWxpZW5jZU1ldHJpY3MuZ2V0U25hcHNob3QoKTtcbn1cblxuLyoqXG4gKiBSZXNldCByZXNpbGllbmNlIG1ldHJpY3MgKGZvciB0ZXN0aW5nIG9ubHkpLlxuICogQGludGVybmFsXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiByZXNldFJlc2lsaWVuY2VNZXRyaWNzKCk6IHZvaWQge1xuICByZXNpbGllbmNlTWV0cmljcy5yZXNldCgpO1xufVxuIl19