export type RestartDecision = | { allow: true } | { allow: false; reason: 'cooldown_active'; retryAfterMs: number; }; type RestartGovernorOptions = { cooldownMs: number; }; const DEFAULT_OPTIONS: RestartGovernorOptions = { cooldownMs: 2500, }; export class GatewayRestartGovernor { private readonly options: RestartGovernorOptions; private lastRestartAt = 0; private suppressedTotal = 0; private executedTotal = 0; constructor(options?: Partial) { this.options = { ...DEFAULT_OPTIONS, ...options }; } onRunning(_now = Date.now()): void { // Kept for interface compatibility with ClawX lifecycle wiring. } decide(now = Date.now()): RestartDecision { if (this.lastRestartAt > 0) { const sinceLast = now - this.lastRestartAt; if (sinceLast < this.options.cooldownMs) { this.suppressedTotal = this.safeIncrement(this.suppressedTotal); return { allow: false, reason: 'cooldown_active', retryAfterMs: this.options.cooldownMs - sinceLast, }; } } return { allow: true }; } recordExecuted(now = Date.now()): void { this.executedTotal = this.safeIncrement(this.executedTotal); this.lastRestartAt = now; } getCounters(): { executedTotal: number; suppressedTotal: number } { return { executedTotal: this.executedTotal, suppressedTotal: this.suppressedTotal, }; } getObservability(): { suppressed_total: number; executed_total: number; circuit_open_until: number; } { return { suppressed_total: this.suppressedTotal, executed_total: this.executedTotal, circuit_open_until: 0, }; } private safeIncrement(current: number): number { if (current >= Number.MAX_SAFE_INTEGER) return 0; return current + 1; } }