diff --git a/src/hooks/prometheus-md-only/agent-resolution.ts b/src/hooks/prometheus-md-only/agent-resolution.ts index 22dc9cae0..f650ab722 100644 --- a/src/hooks/prometheus-md-only/agent-resolution.ts +++ b/src/hooks/prometheus-md-only/agent-resolution.ts @@ -12,21 +12,30 @@ import { isSqliteBackend } from "../../shared/opencode-storage-detection" type OpencodeClient = PluginInput["client"] +function isCompactionAgent(agent: string): boolean { + return agent.toLowerCase() === "compaction" +} + async function getAgentFromMessageFiles( sessionID: string, client?: OpencodeClient ): Promise { if (isSqliteBackend() && client) { const firstAgent = await findFirstMessageWithAgentFromSDK(client, sessionID) - if (firstAgent) return firstAgent + if (firstAgent && !isCompactionAgent(firstAgent)) return firstAgent const nearest = await findNearestMessageWithFieldsFromSDK(client, sessionID) - return nearest?.agent + if (nearest?.agent && !isCompactionAgent(nearest.agent)) return nearest.agent + return undefined } const messageDir = getMessageDir(sessionID) if (!messageDir) return undefined - return findFirstMessageWithAgent(messageDir) ?? findNearestMessageWithFields(messageDir)?.agent + const firstAgent = findFirstMessageWithAgent(messageDir) + if (firstAgent && !isCompactionAgent(firstAgent)) return firstAgent + const nearestAgent = findNearestMessageWithFields(messageDir)?.agent + if (nearestAgent && !isCompactionAgent(nearestAgent)) return nearestAgent + return undefined } /** diff --git a/src/hooks/todo-continuation-enforcer/continuation-injection.ts b/src/hooks/todo-continuation-enforcer/continuation-injection.ts index 5e4cb124c..6dcac9045 100644 --- a/src/hooks/todo-continuation-enforcer/continuation-injection.ts +++ b/src/hooks/todo-continuation-enforcer/continuation-injection.ts @@ -120,6 +120,14 @@ export async function injectContinuation(args: { return } + if (!agentName) { + const compactionState = sessionStateStore.getExistingState(sessionID) + if (compactionState?.hasRecentCompaction) { + log(`[${HOOK_NAME}] Skipped: agent unknown after compaction`, { sessionID }) + return + } + } + if (!hasWritePermission(tools)) { log(`[${HOOK_NAME}] Skipped: agent lacks write permission`, { sessionID, agent: agentName }) return diff --git a/src/hooks/todo-continuation-enforcer/handler.ts b/src/hooks/todo-continuation-enforcer/handler.ts index 4e4ef69d0..ee8168d64 100644 --- a/src/hooks/todo-continuation-enforcer/handler.ts +++ b/src/hooks/todo-continuation-enforcer/handler.ts @@ -63,6 +63,17 @@ export function createTodoContinuationHandler(args: { return } + if (event.type === "session.compacted") { + const sessionID = (props?.sessionID ?? (props?.info as { id?: string } | undefined)?.id) as string | undefined + if (sessionID) { + const state = sessionStateStore.getState(sessionID) + state.hasRecentCompaction = true + sessionStateStore.cancelCountdown(sessionID) + log(`[${HOOK_NAME}] Session compacted: marked hasRecentCompaction`, { sessionID }) + } + return + } + if (event.type === "session.deleted") { const sessionInfo = props?.info as { id?: string } | undefined if (sessionInfo?.id) { diff --git a/src/hooks/todo-continuation-enforcer/idle-event.ts b/src/hooks/todo-continuation-enforcer/idle-event.ts index c55bf4abc..834ed0054 100644 --- a/src/hooks/todo-continuation-enforcer/idle-event.ts +++ b/src/hooks/todo-continuation-enforcer/idle-event.ts @@ -146,7 +146,6 @@ export async function handleSessionIdle(args: { } let resolvedInfo: ResolvedMessageInfo | undefined - let hasCompactionMessage = false try { const messagesResp = await ctx.client.session.messages({ path: { id: sessionID }, @@ -155,7 +154,6 @@ export async function handleSessionIdle(args: { for (let i = messages.length - 1; i >= 0; i--) { const info = messages[i].info if (info?.agent === "compaction") { - hasCompactionMessage = true continue } if (info?.agent || info?.model || (info?.modelID && info?.providerID)) { @@ -171,14 +169,14 @@ export async function handleSessionIdle(args: { log(`[${HOOK_NAME}] Failed to fetch messages for agent check`, { sessionID, error: String(error) }) } - log(`[${HOOK_NAME}] Agent check`, { sessionID, agentName: resolvedInfo?.agent, skipAgents, hasCompactionMessage }) + log(`[${HOOK_NAME}] Agent check`, { sessionID, agentName: resolvedInfo?.agent, skipAgents, hasRecentCompaction: state.hasRecentCompaction }) 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 (hasCompactionMessage && !resolvedInfo?.agent) { + if (state.hasRecentCompaction && !resolvedInfo?.agent) { log(`[${HOOK_NAME}] Skipped: compaction occurred but no agent info resolved`, { sessionID }) return } diff --git a/src/hooks/todo-continuation-enforcer/types.ts b/src/hooks/todo-continuation-enforcer/types.ts index c801ab128..e8ea96af0 100644 --- a/src/hooks/todo-continuation-enforcer/types.ts +++ b/src/hooks/todo-continuation-enforcer/types.ts @@ -35,6 +35,7 @@ export interface SessionState { inFlight?: boolean stagnationCount: number consecutiveFailures: number + hasRecentCompaction?: boolean } export interface MessageInfo {