fix(recovery): more aggressive truncation, remove revert fallback

- Change charsPerToken from 4 to 2 for more aggressive truncation calculation
- Remove revert fallback (PHASE 2.5)
- Always try Continue after truncation if anything was truncated

🤖 Generated with assistance of OhMyOpenCode (https://github.com/code-yeongyu/oh-my-opencode)
This commit is contained in:
YeonGyu-Kim
2026-01-01 16:46:48 +09:00
parent a5983f1678
commit b30c17ac77
2 changed files with 13 additions and 107 deletions

View File

@@ -158,27 +158,7 @@ export async function getLastAssistant(
} }
} }
async function getLastMessageId(
sessionID: string,
client: Client,
directory: string,
): Promise<string | null> {
try {
const resp = await client.session.messages({
path: { id: sessionID },
query: { directory },
});
const data = (resp as { data?: unknown[] }).data;
if (!Array.isArray(data) || data.length === 0) return null;
const lastMsg = data[data.length - 1] as Record<string, unknown>;
const info = lastMsg.info as Record<string, unknown> | undefined;
return (info?.id as string) ?? null;
} catch {
return null;
}
}
function clearSessionState( function clearSessionState(
autoCompactState: AutoCompactState, autoCompactState: AutoCompactState,
@@ -421,7 +401,6 @@ export async function executeCompact(
log("[auto-compact] aggressive truncation completed", aggressiveResult); log("[auto-compact] aggressive truncation completed", aggressiveResult);
if (aggressiveResult.sufficient) {
clearSessionState(autoCompactState, sessionID); clearSessionState(autoCompactState, sessionID);
setTimeout(async () => { setTimeout(async () => {
try { try {
@@ -434,82 +413,9 @@ export async function executeCompact(
}, 500); }, 500);
return; return;
} }
} else {
log("[auto-compact] no tool outputs found to truncate", { sessionID });
}
// PHASE 2.5: Revert fallback - if still over limit, remove last message
log("[auto-compact] PHASE 2.5: revert fallback - still over limit after truncation", {
sessionID,
currentTokens: errorData.currentTokens,
maxTokens: errorData.maxTokens,
});
const lastMessageId = await getLastMessageId(
sessionID,
client as Client,
directory,
);
if (lastMessageId) {
try {
await (client as Client).session.revert({
path: { id: sessionID },
body: { messageID: lastMessageId },
query: { directory },
});
await (client as Client).tui
.showToast({
body: {
title: "Message Reverted",
message: "Removed last message to reduce context. Retrying...",
variant: "warning",
duration: 3000,
},
})
.catch(() => {});
clearSessionState(autoCompactState, sessionID);
setTimeout(async () => {
try {
await (client as Client).session.prompt_async({
path: { sessionID },
body: { parts: [{ type: "text", text: "Continue" }] },
query: { directory },
});
} catch {}
}, 500);
return;
} catch (revertError) {
log("[auto-compact] revert failed", { error: String(revertError) });
}
}
}
// PHASE 3: Summarize - only when under limit (otherwise it will also fail)
if (isOverLimit) {
log("[auto-compact] skipping summarize - still over token limit", {
sessionID,
currentTokens: errorData?.currentTokens,
maxTokens: errorData?.maxTokens,
});
clearSessionState(autoCompactState, sessionID);
await (client as Client).tui
.showToast({
body: {
title: "Recovery Failed",
message: `Still over token limit (${errorData?.currentTokens}/${errorData?.maxTokens}). Please start a new session or manually compact.`,
variant: "error",
duration: 10000,
},
})
.catch(() => {});
return;
} }
// PHASE 3: Summarize - fallback when no tool outputs to truncate
const retryState = getOrCreateRetryState(autoCompactState, sessionID); const retryState = getOrCreateRetryState(autoCompactState, sessionID);
if (errorData?.errorType?.includes("non-empty content")) { if (errorData?.errorType?.includes("non-empty content")) {

View File

@@ -44,5 +44,5 @@ export const TRUNCATE_CONFIG = {
maxTruncateAttempts: 20, maxTruncateAttempts: 20,
minOutputSizeToTruncate: 500, minOutputSizeToTruncate: 500,
targetTokenRatio: 0.5, targetTokenRatio: 0.5,
charsPerToken: 4, charsPerToken: 2,
} as const } as const