diff --git a/src/hooks/anthropic-context-window-limit-recovery/aggressive-truncation-strategy.ts b/src/hooks/anthropic-context-window-limit-recovery/aggressive-truncation-strategy.ts index 21f761ab3..5fdcbe87c 100644 --- a/src/hooks/anthropic-context-window-limit-recovery/aggressive-truncation-strategy.ts +++ b/src/hooks/anthropic-context-window-limit-recovery/aggressive-truncation-strategy.ts @@ -17,7 +17,7 @@ import { findNearestMessageWithFields, findNearestMessageWithFieldsFromSDK, } from "../../features/hook-message-injector" -import { isSessionActive } from "../shared/session-idle-settle" +import { promptAsyncAfterSessionIdle } from "../shared/prompt-async-gate" export async function runAggressiveTruncationStrategy(params: { sessionID: string @@ -74,13 +74,6 @@ export async function runAggressiveTruncationStrategy(params: { clearSessionState(params.autoCompactState, params.sessionID) setTimeout(async () => { try { - if (await isSessionActive(params.client, params.sessionID)) { - log("[auto-compact] skipped delayed auto prompt because session became active", { - sessionID: params.sessionID, - }) - return - } - const sdkMessage = await findNearestMessageWithFieldsFromSDK(params.client, params.sessionID) const previousMessage = sdkMessage ?? (() => { const messageDir = getMessageDir(params.sessionID) @@ -95,17 +88,29 @@ export async function runAggressiveTruncationStrategy(params: { const launchVariant = previousMessage?.model?.variant const inheritedTools = resolveInheritedPromptTools(params.sessionID, previousMessage?.tools) - await params.client.session.promptAsync({ - path: { id: params.sessionID }, - body: { - auto: true, - ...(launchAgent ? { agent: launchAgent } : {}), - ...(launchModel ? { model: launchModel } : {}), - ...(launchVariant ? { variant: launchVariant } : {}), - ...(inheritedTools ? { tools: inheritedTools } : {}), + const promptResult = await promptAsyncAfterSessionIdle({ + client: params.client, + sessionID: params.sessionID, + source: "auto-compact", + settleMs: 0, + input: { + path: { id: params.sessionID }, + body: { + auto: true, + ...(launchAgent ? { agent: launchAgent } : {}), + ...(launchModel ? { model: launchModel } : {}), + ...(launchVariant ? { variant: launchVariant } : {}), + ...(inheritedTools ? { tools: inheritedTools } : {}), + } as never, + query: { directory: params.directory }, } as never, - query: { directory: params.directory }, }) + if (promptResult.status !== "dispatched") { + log("[auto-compact] delayed auto prompt skipped by promptAsync gate", { + sessionID: params.sessionID, + status: promptResult.status, + }) + } } catch (error) { log("[auto-compact] delayed auto prompt failed", { sessionID: params.sessionID, diff --git a/src/hooks/compaction-context-injector/recovery.ts b/src/hooks/compaction-context-injector/recovery.ts index ed69bf0e0..7abc8e71c 100644 --- a/src/hooks/compaction-context-injector/recovery.ts +++ b/src/hooks/compaction-context-injector/recovery.ts @@ -21,6 +21,7 @@ import { import { AGENT_RECOVERY_PROMPT, NO_TEXT_TAIL_THRESHOLD, RECOVERY_COOLDOWN_MS, RECENT_COMPACTION_WINDOW_MS } from "./constants" import type { CompactionContextClient } from "./types" import type { TailMonitorState } from "./tail-monitor" +import { promptAsyncAfterSessionIdle } from "../shared/prompt-async-gate" export function createRecoveryLogic( ctx: CompactionContextClient | undefined, @@ -81,17 +82,30 @@ export function createRecoveryLogic( } try { - await ctx.client.session.promptAsync({ - path: { id: sessionID }, - body: { - noReply: true, - agent: launchAgent ?? expectedPromptConfig.agent, - ...(model ? { model } : {}), - ...(tools ? { tools } : {}), - parts: [createInternalAgentContinuationTextPart(AGENT_RECOVERY_PROMPT)], + const promptResult = await promptAsyncAfterSessionIdle({ + client: ctx.client, + sessionID, + source: "compaction-context-injector", + input: { + path: { id: sessionID }, + body: { + noReply: true, + agent: launchAgent ?? expectedPromptConfig.agent, + ...(model ? { model } : {}), + ...(tools ? { tools } : {}), + parts: [createInternalAgentContinuationTextPart(AGENT_RECOVERY_PROMPT)], + }, + query: { directory: ctx.directory }, }, - query: { directory: ctx.directory }, }) + if (promptResult.status !== "dispatched") { + log(`[compaction-context-injector] Recovery skipped by promptAsync gate`, { + sessionID, + reason, + status: promptResult.status, + }) + return false + } const recoveredPromptConfig = await resolveLatestSessionPromptConfig(ctx, sessionID) if (!isPromptConfigRecovered(recoveredPromptConfig, expectedPromptConfig)) { diff --git a/src/hooks/compaction-context-injector/types.ts b/src/hooks/compaction-context-injector/types.ts index b560e21b4..1772550d2 100644 --- a/src/hooks/compaction-context-injector/types.ts +++ b/src/hooks/compaction-context-injector/types.ts @@ -20,6 +20,7 @@ export type CompactionContextClient = { } query?: { directory: string } }) => Promise + status?: () => Promise } } directory: string