mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-10-02 13:39:41 +02:00
- Add path traversal protection to GET /api/cases/:name and fix-plan - Use safePathSchema for LinkCaseSchema.path - Fix QR auth test timer leak (afterAll → afterEach) and env var try/finally - Remove dead terminal size check after Zod validation in resize route - Remove no-op sampleCount guard in adaptive timing - Replace hardcoded values with constants in notification-manager and subagent-windows - Add Zod validation to POST /api/auth/revoke - Use _apiPut instead of raw fetch in subagent-windows - Add SwipeHandler.cleanup() for consistency with other mobile handlers - Move NiceConfig/ProcessStats from types/plan.ts to types/common.ts Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
130 lines
3.9 KiB
TypeScript
130 lines
3.9 KiB
TypeScript
/**
|
|
* @fileoverview Adaptive timing controller for respawn idle detection.
|
|
*
|
|
* Extracted from respawn-controller.ts for modularity. Tracks historical timing
|
|
* data and adjusts the completion confirm timeout dynamically based on the 75th
|
|
* percentile of recent idle detection durations.
|
|
*
|
|
* @module respawn-adaptive-timing
|
|
*/
|
|
|
|
import type { TimingHistory } from './types.js';
|
|
|
|
/**
|
|
* Configuration for adaptive timing bounds.
|
|
*/
|
|
export interface AdaptiveTimingConfig {
|
|
/** Minimum adaptive completion confirm timeout (ms) */
|
|
adaptiveMinConfirmMs: number;
|
|
/** Maximum adaptive completion confirm timeout (ms) */
|
|
adaptiveMaxConfirmMs: number;
|
|
}
|
|
|
|
/**
|
|
* Manages adaptive timing for respawn idle detection.
|
|
*
|
|
* Uses historical idle detection durations to calculate an optimal completion
|
|
* confirm timeout. The timeout is based on the 75th percentile of recent
|
|
* durations with a 20% safety buffer, clamped to configured bounds.
|
|
*/
|
|
export class RespawnAdaptiveTiming {
|
|
private timingHistory: TimingHistory;
|
|
|
|
constructor(private config: AdaptiveTimingConfig) {
|
|
this.timingHistory = {
|
|
recentIdleDetectionMs: [],
|
|
recentCycleDurationMs: [],
|
|
adaptiveCompletionConfirmMs: 10000, // Start with default
|
|
sampleCount: 0,
|
|
maxSamples: 20, // Keep last 20 samples for rolling average
|
|
lastUpdatedAt: Date.now(),
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Record timing data from a completed cycle for adaptive adjustments.
|
|
*
|
|
* @param idleDetectionMs - Time spent detecting idle
|
|
* @param cycleDurationMs - Total cycle duration
|
|
*/
|
|
recordTimingData(idleDetectionMs: number, cycleDurationMs: number): void {
|
|
const history = this.timingHistory;
|
|
|
|
// Add to rolling windows
|
|
history.recentIdleDetectionMs.push(idleDetectionMs);
|
|
history.recentCycleDurationMs.push(cycleDurationMs);
|
|
|
|
// Trim to max samples
|
|
if (history.recentIdleDetectionMs.length > history.maxSamples) {
|
|
history.recentIdleDetectionMs.shift();
|
|
}
|
|
if (history.recentCycleDurationMs.length > history.maxSamples) {
|
|
history.recentCycleDurationMs.shift();
|
|
}
|
|
|
|
history.sampleCount = history.recentIdleDetectionMs.length;
|
|
history.lastUpdatedAt = Date.now();
|
|
|
|
// Recalculate adaptive timing
|
|
this.updateAdaptiveTiming();
|
|
}
|
|
|
|
/**
|
|
* Get the current adaptive completion confirm timeout.
|
|
* Returns the calculated value, or the default if not enough samples.
|
|
*
|
|
* @returns Completion confirm timeout in milliseconds
|
|
*/
|
|
getAdaptiveCompletionConfirmMs(): number {
|
|
return this.timingHistory.adaptiveCompletionConfirmMs;
|
|
}
|
|
|
|
/**
|
|
* Get the current timing history for monitoring.
|
|
* @returns Copy of timing history
|
|
*/
|
|
getTimingHistory(): TimingHistory {
|
|
return { ...this.timingHistory };
|
|
}
|
|
|
|
/**
|
|
* Reset all timing history.
|
|
*/
|
|
reset(): void {
|
|
this.timingHistory = {
|
|
recentIdleDetectionMs: [],
|
|
recentCycleDurationMs: [],
|
|
adaptiveCompletionConfirmMs: 10000,
|
|
sampleCount: 0,
|
|
maxSamples: 20,
|
|
lastUpdatedAt: Date.now(),
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Recalculate the adaptive completion confirm timeout based on historical data.
|
|
* Uses the 75th percentile of recent idle detection times as the new timeout,
|
|
* with a 20% buffer for safety.
|
|
*/
|
|
private updateAdaptiveTiming(): void {
|
|
const history = this.timingHistory;
|
|
const minMs = this.config.adaptiveMinConfirmMs;
|
|
const maxMs = this.config.adaptiveMaxConfirmMs;
|
|
|
|
if (history.recentIdleDetectionMs.length < 5) return;
|
|
|
|
// Sort for percentile calculation
|
|
const sorted = [...history.recentIdleDetectionMs].sort((a, b) => a - b);
|
|
|
|
// Use 75th percentile with 20% buffer
|
|
const p75Index = Math.floor(sorted.length * 0.75);
|
|
const p75Value = sorted[p75Index];
|
|
const withBuffer = Math.round(p75Value * 1.2);
|
|
|
|
// Clamp to configured bounds
|
|
const clamped = Math.max(minMs, Math.min(maxMs, withBuffer));
|
|
|
|
history.adaptiveCompletionConfirmMs = clamped;
|
|
}
|
|
}
|