fix(todo-continuation-enforcer): tighten post-compaction guard with session-agent fallback
Refine continuation agent resolution to prefer session-state agent fallback while keeping compaction-specific protection. Replace sticky boolean compaction flag with a short-lived timestamp guard so unresolved agents are blocked only during the immediate post-compaction window, avoiding long-lived suppression and preserving existing continuation behavior.
This commit is contained in:
@@ -1,7 +1,6 @@
|
||||
import type { PluginInput } from "@opencode-ai/plugin"
|
||||
|
||||
import type { BackgroundManager } from "../../features/background-agent"
|
||||
import type { ToolPermission } from "../../features/hook-message-injector"
|
||||
import { getSessionAgent } from "../../features/claude-code-session-state"
|
||||
import { normalizeSDKResponse } from "../../shared"
|
||||
import { log } from "../../shared/logger"
|
||||
import { getAgentConfigKey } from "../../shared/agent-display-names"
|
||||
@@ -19,6 +18,8 @@ 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 type { SessionStateStore } from "./session-state"
|
||||
import { startCountdown } from "./countdown"
|
||||
|
||||
@@ -119,10 +120,7 @@ export async function handleSessionIdle(args: {
|
||||
&& Date.now() - state.lastInjectedAt >= FAILURE_RESET_WINDOW_MS
|
||||
) {
|
||||
state.consecutiveFailures = 0
|
||||
log(`[${HOOK_NAME}] Reset consecutive failures after recovery window`, {
|
||||
sessionID,
|
||||
failureResetWindowMs: FAILURE_RESET_WINDOW_MS,
|
||||
})
|
||||
log(`[${HOOK_NAME}] Reset consecutive failures after recovery window`, { sessionID, failureResetWindowMs: FAILURE_RESET_WINDOW_MS })
|
||||
}
|
||||
|
||||
if (state.consecutiveFailures >= MAX_CONSECUTIVE_FAILURES) {
|
||||
@@ -147,39 +145,32 @@ export async function handleSessionIdle(args: {
|
||||
|
||||
let resolvedInfo: ResolvedMessageInfo | undefined
|
||||
try {
|
||||
const messagesResp = await ctx.client.session.messages({
|
||||
path: { id: sessionID },
|
||||
})
|
||||
const messages = normalizeSDKResponse(messagesResp, [] as Array<{ info?: MessageInfo }>)
|
||||
for (let i = messages.length - 1; i >= 0; i--) {
|
||||
const info = messages[i].info
|
||||
if (info?.agent === "compaction") {
|
||||
continue
|
||||
}
|
||||
if (info?.agent || info?.model || (info?.modelID && info?.providerID)) {
|
||||
resolvedInfo = {
|
||||
agent: info.agent,
|
||||
model: info.model ?? (info.providerID && info.modelID ? { providerID: info.providerID, modelID: info.modelID } : undefined),
|
||||
tools: info.tools as Record<string, ToolPermission> | undefined,
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
resolvedInfo = await resolveLatestMessageInfo(ctx, sessionID)
|
||||
} catch (error) {
|
||||
log(`[${HOOK_NAME}] Failed to fetch messages for agent check`, { sessionID, error: String(error) })
|
||||
}
|
||||
|
||||
log(`[${HOOK_NAME}] Agent check`, { sessionID, agentName: resolvedInfo?.agent, skipAgents, hasRecentCompaction: state.hasRecentCompaction })
|
||||
const sessionAgent = getSessionAgent(sessionID)
|
||||
if (!resolvedInfo?.agent && sessionAgent) {
|
||||
resolvedInfo = { ...resolvedInfo, agent: sessionAgent }
|
||||
}
|
||||
|
||||
const compactionGuardActive = isCompactionGuardActive(state, Date.now())
|
||||
|
||||
log(`[${HOOK_NAME}] Agent check`, { sessionID, agentName: resolvedInfo?.agent, skipAgents, compactionGuardActive })
|
||||
|
||||
const resolvedAgentName = resolvedInfo?.agent
|
||||
if (resolvedAgentName && skipAgents.some(s => getAgentConfigKey(s) === getAgentConfigKey(resolvedAgentName))) {
|
||||
log(`[${HOOK_NAME}] Skipped: agent in skipAgents list`, { sessionID, agent: resolvedAgentName })
|
||||
return
|
||||
}
|
||||
if (state.hasRecentCompaction && !resolvedInfo?.agent) {
|
||||
if (compactionGuardActive && !resolvedInfo?.agent) {
|
||||
log(`[${HOOK_NAME}] Skipped: compaction occurred but no agent info resolved`, { sessionID })
|
||||
return
|
||||
}
|
||||
if (state.recentCompactionAt && resolvedInfo?.agent) {
|
||||
state.recentCompactionAt = undefined
|
||||
}
|
||||
|
||||
if (isContinuationStopped?.(sessionID)) {
|
||||
log(`[${HOOK_NAME}] Skipped: continuation stopped for session`, { sessionID })
|
||||
@@ -195,7 +186,6 @@ export async function handleSessionIdle(args: {
|
||||
if (shouldStopForStagnation({ sessionID, incompleteCount, progressUpdate })) {
|
||||
return
|
||||
}
|
||||
|
||||
startCountdown({
|
||||
ctx,
|
||||
sessionID,
|
||||
|
||||
Reference in New Issue
Block a user