From a0649616bf4f7b854c13e10b669f36846fed0c92 Mon Sep 17 00:00:00 2001 From: YeonGyu-Kim Date: Tue, 24 Mar 2026 20:36:22 +0900 Subject: [PATCH] fix(todo-continuation-enforcer): acknowledge compaction epochs during idle Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus --- .../todo-continuation-enforcer/idle-event.ts | 28 +++++++++++-------- 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/src/hooks/todo-continuation-enforcer/idle-event.ts b/src/hooks/todo-continuation-enforcer/idle-event.ts index a4d7ea83a..ed0301549 100644 --- a/src/hooks/todo-continuation-enforcer/idle-event.ts +++ b/src/hooks/todo-continuation-enforcer/idle-event.ts @@ -5,21 +5,14 @@ import { normalizeSDKResponse } from "../../shared" import { log } from "../../shared/logger" import { getAgentConfigKey } from "../../shared/agent-display-names" -import { - ABORT_WINDOW_MS, - CONTINUATION_COOLDOWN_MS, - DEFAULT_SKIP_AGENTS, - FAILURE_RESET_WINDOW_MS, - HOOK_NAME, - MAX_CONSECUTIVE_FAILURES, -} from "./constants" +import { ABORT_WINDOW_MS, CONTINUATION_COOLDOWN_MS, DEFAULT_SKIP_AGENTS, FAILURE_RESET_WINDOW_MS, HOOK_NAME, MAX_CONSECUTIVE_FAILURES } from "./constants" import { isLastAssistantMessageAborted } from "./abort-detection" import { hasUnansweredQuestion } from "./pending-question-detection" import { shouldStopForStagnation } from "./stagnation-detection" import { getIncompleteCount } from "./todo" import type { MessageInfo, ResolvedMessageInfo, Todo } from "./types" import { resolveLatestMessageInfo } from "./resolve-message-info" -import { isCompactionGuardActive } from "./compaction-guard" +import { acknowledgeCompactionGuard, isCompactionGuardActive } from "./compaction-guard" import type { SessionStateStore } from "./session-state" import { startCountdown } from "./countdown" @@ -43,6 +36,7 @@ export async function handleSessionIdle(args: { log(`[${HOOK_NAME}] session.idle`, { sessionID }) const state = sessionStateStore.getState(sessionID) + const observedCompactionEpoch = state.recentCompactionEpoch if (state.isRecovering) { log(`[${HOOK_NAME}] Skipped: in recovery`, { sessionID }) return @@ -150,9 +144,18 @@ export async function handleSessionIdle(args: { resolvedInfo = { ...resolvedInfo, agent: sessionAgent } } + const acknowledgedCompaction = resolvedInfo?.agent ? acknowledgeCompactionGuard(state, observedCompactionEpoch) : false const compactionGuardActive = isCompactionGuardActive(state, Date.now()) - log(`[${HOOK_NAME}] Agent check`, { sessionID, agentName: resolvedInfo?.agent, skipAgents, compactionGuardActive }) + log(`[${HOOK_NAME}] Agent check`, { + sessionID, + agentName: resolvedInfo?.agent, + skipAgents, + compactionGuardActive, + observedCompactionEpoch, + currentCompactionEpoch: state.recentCompactionEpoch, + acknowledgedCompaction, + }) const resolvedAgentName = resolvedInfo?.agent if (resolvedAgentName && skipAgents.some(s => getAgentConfigKey(s) === getAgentConfigKey(resolvedAgentName))) { @@ -163,8 +166,9 @@ export async function handleSessionIdle(args: { log(`[${HOOK_NAME}] Skipped: compaction occurred but no agent info resolved`, { sessionID }) return } - if (state.recentCompactionAt && resolvedInfo?.agent) { - state.recentCompactionAt = undefined + if (compactionGuardActive) { + log(`[${HOOK_NAME}] Skipped: compaction guard still armed for current epoch`, { sessionID, observedCompactionEpoch, currentCompactionEpoch: state.recentCompactionEpoch }) + return } if (isContinuationStopped?.(sessionID)) {