import type { Message } from "@kenkaiiii/gg-ai"; /** * Loop-breaker hook — the mid-loop counterpart to the ideal review. * * Where the ideal review fires when the agent is about to STOP, the * loop-breaker fires only on high-confidence evidence that the agent is * STUCK: a failure streak, the same call returning the same result repeatedly, * or degenerate repeated output. * * Successful edits to one file are progress, not a loop. Repeated calls are * counted only while consecutive and only when their result is unchanged; * expected polling/wait calls are excluded. This avoids interrupting healthy * long-running commands and iterative edits. */ export interface LoopBreakStats { /** Failed tool calls in an unbroken streak (reset by any success). */ consecutiveFailures: number; /** Consecutive identical non-polling calls whose result did not change. */ repeatedNoProgressCalls: number; /** Whether streamed assistant text degenerated into repetition. */ textRepetitionDetected: boolean; /** Detected cyclic tool-call pattern (A/B/A/B…) with no new results. */ cyclicPattern?: CycleDetection; } export interface LoopBreakDecision { shouldBreak: boolean; reasons: string[]; } export declare const LOOP_BREAK_PROMPT: string; /** * Second-stage break — injected when the agent is STILL looping after the * first LOOP_BREAK_PROMPT nudge. No more retries: report and hand back. */ export declare const LOOP_BREAK_FINAL_PROMPT: string; /** Stable signature for a tool call: name + canonicalized args. */ export declare function toolCallSignature(name: string, args: unknown): string; /** * Tracks only a currently repeating, unchanged tool outcome. Different calls, * changed output, and explicit polling/wait tools all prove enough movement to * reset the streak. One instance is used per agent run. */ export declare class ToolCallProgressTracker { private lastSignature; private lastResult; private lastWasError; private repeatCount; record(name: string, args: unknown, result: string, isError: boolean): number; reset(): void; private resetRepetition; } export interface CycleDetection { /** Cycle length k (1–5): the repeating unit is the last k distinct calls. */ length: number; /** How many full consecutive repetitions of the unit were observed. */ repeats: number; } /** * Detects the agent alternating through a short cycle of tool calls * (A/B/A/B… up to length 5) that produce no new results — the multi-call * generalization of ToolCallProgressTracker's single-call repeat streak * (Gemini CLI's cycle detector, hardened with an unchanged-result guard: a * call whose result changed between visits is progress, not a loop). * * One instance per agent run; `reset()` after a loop-break injection so the * second stage measures fresh evidence. */ export declare class CycleDetector { /** Bounded FIFO of recent call signatures (most recent last). */ private history; /** Whether each historical call's result was unchanged from that * signature's previous occurrence. Parallel to `history`. */ private unchanged; /** Last observed result per signature. */ private lastResults; record(name: string, args: unknown, result: string, _isError: boolean): CycleDetection | null; reset(): void; /** True when the last k*THRESHOLD calls repeat the same k-cycle and every * one of them returned the same result as its previous occurrence. */ private matchesCycle; } /** * Detects verbatim repetition at the tail of streamed text — the model * looping on a phrase/block. Scans candidate block lengths and checks how * many times the trailing block repeats consecutively. Cheap: bounded to a * fixed tail window. */ export declare function detectTextRepetition(text: string): boolean; export declare function evaluateLoopBreak(stats: LoopBreakStats): LoopBreakDecision; export declare function buildLoopBreakMessage(reasons: readonly string[], final?: boolean): Message; //# sourceMappingURL=loop-breaker.d.ts.map