feat(FN-882): add loop detection recovery with compact-and-resume

- Add ContextLimitDetector to detect agent loops via repeated tool call patterns
- Implement compact-and-resume strategy: summarize conversation and restart agent from current step
- Add loop recovery to StuckTaskDetector with configurable attempt tracking and retry limits
- Extend executor with automatic loop recovery on context limit detection
- Add loop recovery support to pi executor with same compact-and-resume pattern
- Add comprehensive tests for context-limit-detector, stuck-task-detector loop detection, executor, and pi recovery
- Add changeset for patch bump to @gsxdsm/fusion
- Update README with loop detection and recovery documentation
This commit is contained in:
gsxdsm
2026-04-04 19:47:48 -07:00
parent c0b1c2ae30
commit 4c2be10d73
12 changed files with 846 additions and 6 deletions

View File

@@ -59,6 +59,56 @@ export function describeModel(session: AgentSession): string {
return `${model.provider}/${model.id}`;
}
/**
* Default instructions used when calling `session.compact()` for loop recovery.
* These guide the compaction summary to preserve essential context while
* freeing up the context window for continued work.
*/
export const COMPACTION_FALLBACK_INSTRUCTIONS = [
"Summarize all completed steps concisely.",
"Preserve the current step number and any in-progress work details.",
"Keep references to key files, decisions, and error states.",
"Discard verbose tool output, repeated attempts, and exploration history.",
].join(" ");
/**
* Compact an agent session's context to free up the context window.
*
* Uses the SDK's native `session.compact()` method when available (the
* preferred path — it produces structured, LLM-generated summaries).
*
* @param session — The agent session to compact
* @param customInstructions — Optional instructions for the compaction summary.
* When not provided, uses COMPACTION_FALLBACK_INSTRUCTIONS.
* @returns The compaction result with summary and token metrics, or null if
* compaction was not available or failed.
*/
export async function compactSessionContext(
session: AgentSession,
customInstructions?: string,
): Promise<{ summary: string; tokensBefore: number } | null> {
const instructions = customInstructions ?? COMPACTION_FALLBACK_INSTRUCTIONS;
// Check if session.compact is available (runtime capability detection)
if (typeof (session as any).compact !== "function") {
return null;
}
try {
const result = await (session as any).compact(instructions);
if (result && typeof result === "object") {
return {
summary: result.summary ?? "",
tokensBefore: result.tokensBefore ?? 0,
};
}
return null;
} catch {
// Compaction failed — return null so caller can fall through to kill/requeue
return null;
}
}
export interface AgentOptions {
cwd: string;
systemPrompt: string;