diff --git a/src/hooks/preemptive-compaction-degradation-monitor.ts b/src/hooks/preemptive-compaction-degradation-monitor.ts index 4eb3dfc37..64a29eb03 100644 --- a/src/hooks/preemptive-compaction-degradation-monitor.ts +++ b/src/hooks/preemptive-compaction-degradation-monitor.ts @@ -74,12 +74,16 @@ export function createPostCompactionDegradationMonitor(args: { const postCompactionNoTextStreak = new Map() const postCompactionRecoveryTriggered = new Set() const postCompactionEpoch = new Map() + const postCompactionRecoveryCount = new Map() + + const MAX_RECOVERY_ATTEMPTS = 3 const clear = (sessionID: string): void => { postCompactionRemaining.delete(sessionID) postCompactionNoTextStreak.delete(sessionID) postCompactionRecoveryTriggered.delete(sessionID) postCompactionEpoch.delete(sessionID) + postCompactionRecoveryCount.delete(sessionID) } const onSessionCompacted = (sessionID: string): void => { @@ -93,6 +97,16 @@ export function createPostCompactionDegradationMonitor(args: { const triggerRecovery = async (sessionID: string): Promise => { if (postCompactionRecoveryTriggered.has(sessionID) || compactionInProgress.has(sessionID)) return + const recoveryCount = postCompactionRecoveryCount.get(sessionID) ?? 0 + if (recoveryCount >= MAX_RECOVERY_ATTEMPTS) { + log("[preemptive-compaction] Max recovery attempts reached, giving up", { + sessionID, + recoveryCount, + }) + return + } + postCompactionRecoveryCount.set(sessionID, recoveryCount + 1) + const cached = tokenCache.get(sessionID) if (!cached?.modelID) { log("[preemptive-compaction] No-text tail detected but compaction model is unavailable", { sessionID })