import type { Message } from "@kenkaiiii/gg-ai"; /** * Semantic (LLM-judged) loop detection — the layer between the deterministic * checks that fire and silence. `evaluateLoopBreak` only sees syntactic * signals: identical calls, unchanged results, A/B cycles, repeated text. It * is blind to the common thrash where every retry differs slightly (new args, * swapped tools, edited-then-reverted files) while the run makes no real * progress. Gemini CLI ships exactly this hybrid: deterministic heuristics * plus a periodic LLM "unproductive state" check (loopDetectionService). * * Cost control: the judge never runs unless the deterministic evidence is * already suspicious (failures accumulating) AND the deterministic breaker has * stayed quiet (it handles its own case). It runs on the session's ACTIVE * model, in the background between turns, with a bounded per-run budget. */ export interface SemanticCallDigest { tool: string; /** Short arg summary — enough for the judge to see WHAT was attempted. */ args: string; ok: boolean; /** Short result tail — enough to see the failure shape. */ result: string; } export interface SemanticLoopVerdict { loop: boolean; reason: string; advice: string; } /** Consecutive failed tool results before the judge is worth its tokens. */ export declare const SEMANTIC_LOOP_MIN_CONSECUTIVE_FAILURES = 2; /** Judge cooldown in agent turns — one check per burst of failures, not per failure. */ export declare const SEMANTIC_LOOP_COOLDOWN_TURNS = 4; /** Total judge calls per run. Two verdicts is enough to steer any thrash. */ export declare const MAX_SEMANTIC_LOOP_CHECKS = 2; /** Recent tool calls handed to the judge — bounded context, not the transcript. */ export declare const MAX_SEMANTIC_LOOP_CALLS = 12; export interface SemanticLoopTriggerInput { consecutiveFailures: number; totalFailures: number; turns: number; lastCheckTurn: number; checksUsed: number; checkPending: boolean; /** The deterministic verdict for the same evidence — when it fires, it owns the correction. */ deterministicBreak: boolean; } export declare function shouldRunSemanticLoopCheck(input: SemanticLoopTriggerInput): boolean; export declare function buildSemanticLoopJudgePrompt(calls: readonly SemanticCallDigest[], originalRequest: string): string; /** * Parse the judge's reply. Fails CLOSED to "no loop": an unparseable or * malformed verdict must never stop a run that may be making progress. */ export declare function parseSemanticLoopVerdict(raw: string): SemanticLoopVerdict | null; /** Total judge-response deadline. The judge runs mid-turn in the background; * a hung call must not pin memory or budget forever. */ export declare const SEMANTIC_LOOP_JUDGE_TIMEOUT_MS = 20000; export declare function withJudgeTimeout(response: Promise, timeoutMs: number): Promise; export declare function buildSemanticLoopMessage(verdict: SemanticLoopVerdict): Message;