fix(recovery): gate compaction prompts

This commit is contained in:
YeonGyu-Kim
2026-05-15 11:30:03 +09:00
parent c75f548863
commit db28a32cff
3 changed files with 46 additions and 26 deletions
@@ -17,7 +17,7 @@ import {
findNearestMessageWithFields, findNearestMessageWithFields,
findNearestMessageWithFieldsFromSDK, findNearestMessageWithFieldsFromSDK,
} from "../../features/hook-message-injector" } from "../../features/hook-message-injector"
import { isSessionActive } from "../shared/session-idle-settle" import { promptAsyncAfterSessionIdle } from "../shared/prompt-async-gate"
export async function runAggressiveTruncationStrategy(params: { export async function runAggressiveTruncationStrategy(params: {
sessionID: string sessionID: string
@@ -74,13 +74,6 @@ export async function runAggressiveTruncationStrategy(params: {
clearSessionState(params.autoCompactState, params.sessionID) clearSessionState(params.autoCompactState, params.sessionID)
setTimeout(async () => { setTimeout(async () => {
try { 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 sdkMessage = await findNearestMessageWithFieldsFromSDK(params.client, params.sessionID)
const previousMessage = sdkMessage ?? (() => { const previousMessage = sdkMessage ?? (() => {
const messageDir = getMessageDir(params.sessionID) const messageDir = getMessageDir(params.sessionID)
@@ -95,17 +88,29 @@ export async function runAggressiveTruncationStrategy(params: {
const launchVariant = previousMessage?.model?.variant const launchVariant = previousMessage?.model?.variant
const inheritedTools = resolveInheritedPromptTools(params.sessionID, previousMessage?.tools) const inheritedTools = resolveInheritedPromptTools(params.sessionID, previousMessage?.tools)
await params.client.session.promptAsync({ const promptResult = await promptAsyncAfterSessionIdle({
path: { id: params.sessionID }, client: params.client,
body: { sessionID: params.sessionID,
auto: true, source: "auto-compact",
...(launchAgent ? { agent: launchAgent } : {}), settleMs: 0,
...(launchModel ? { model: launchModel } : {}), input: {
...(launchVariant ? { variant: launchVariant } : {}), path: { id: params.sessionID },
...(inheritedTools ? { tools: inheritedTools } : {}), body: {
auto: true,
...(launchAgent ? { agent: launchAgent } : {}),
...(launchModel ? { model: launchModel } : {}),
...(launchVariant ? { variant: launchVariant } : {}),
...(inheritedTools ? { tools: inheritedTools } : {}),
} as never,
query: { directory: params.directory },
} as never, } 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) { } catch (error) {
log("[auto-compact] delayed auto prompt failed", { log("[auto-compact] delayed auto prompt failed", {
sessionID: params.sessionID, sessionID: params.sessionID,
@@ -21,6 +21,7 @@ import {
import { AGENT_RECOVERY_PROMPT, NO_TEXT_TAIL_THRESHOLD, RECOVERY_COOLDOWN_MS, RECENT_COMPACTION_WINDOW_MS } from "./constants" import { AGENT_RECOVERY_PROMPT, NO_TEXT_TAIL_THRESHOLD, RECOVERY_COOLDOWN_MS, RECENT_COMPACTION_WINDOW_MS } from "./constants"
import type { CompactionContextClient } from "./types" import type { CompactionContextClient } from "./types"
import type { TailMonitorState } from "./tail-monitor" import type { TailMonitorState } from "./tail-monitor"
import { promptAsyncAfterSessionIdle } from "../shared/prompt-async-gate"
export function createRecoveryLogic( export function createRecoveryLogic(
ctx: CompactionContextClient | undefined, ctx: CompactionContextClient | undefined,
@@ -81,17 +82,30 @@ export function createRecoveryLogic(
} }
try { try {
await ctx.client.session.promptAsync({ const promptResult = await promptAsyncAfterSessionIdle({
path: { id: sessionID }, client: ctx.client,
body: { sessionID,
noReply: true, source: "compaction-context-injector",
agent: launchAgent ?? expectedPromptConfig.agent, input: {
...(model ? { model } : {}), path: { id: sessionID },
...(tools ? { tools } : {}), body: {
parts: [createInternalAgentContinuationTextPart(AGENT_RECOVERY_PROMPT)], 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) const recoveredPromptConfig = await resolveLatestSessionPromptConfig(ctx, sessionID)
if (!isPromptConfigRecovered(recoveredPromptConfig, expectedPromptConfig)) { if (!isPromptConfigRecovered(recoveredPromptConfig, expectedPromptConfig)) {
@@ -20,6 +20,7 @@ export type CompactionContextClient = {
} }
query?: { directory: string } query?: { directory: string }
}) => Promise<unknown> }) => Promise<unknown>
status?: () => Promise<unknown>
} }
} }
directory: string directory: string