{"version":3,"file":"stall-detector.d.ts","sourceRoot":"","sources":["../../../../src/core/long-horizon/adaptive/stall-detector.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AAEzD,OAAO,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAEzD,MAAM,WAAW,WAAW;IAC3B,0DAA0D;IAC1D,2BAA2B,EAAE,MAAM,CAAC;IACpC,0DAA0D;IAC1D,2BAA2B,EAAE,MAAM,CAAC;IACpC,kCAAkC;IAClC,0BAA0B,EAAE,MAAM,CAAC;IACnC,0BAA0B,EAAE,MAAM,CAAC;IACnC,iCAAiC;IACjC,yBAAyB,EAAE,MAAM,CAAC;IAClC,yBAAyB,EAAE,MAAM,CAAC;IAClC,0BAA0B;IAC1B,2BAA2B,EAAE,MAAM,CAAC;IACpC,2BAA2B,EAAE,MAAM,CAAC;CACpC;AAED,eAAO,MAAM,oBAAoB,EAAE,WASlC,CAAC;AAEF,MAAM,WAAW,UAAU;IAC1B,QAAQ,EAAE,mBAAmB,CAAC;IAC9B,eAAe,EAAE,MAAM,CAAC;IACxB,mBAAmB,EAAE,MAAM,CAAC;IAC5B,0BAA0B,CAAC,EAAE,MAAM,CAAC;IACpC,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,MAAM,CAAC,EAAE,WAAW,CAAC;CACrB;AAED,MAAM,WAAW,eAAe;IAC/B,KAAK,EAAE,UAAU,CAAC;IAClB,aAAa,CAAC,EAAE,UAAU,CAAC;CAC3B;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,UAAU,GAAG,eAAe,CAoDhE;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAC3B,YAAY,EAAE,MAAM,GAAG,SAAS,EAChC,WAAW,EAAE,MAAM,GAAG,SAAS,EAC/B,YAAY,EAAE,OAAO,GACnB,OAAO,CAET;AAED,iDAAiD;AACjD,wBAAgB,eAAe,CAC9B,KAAK,EAAE,UAAU,GACf,UAAU,GAAG,aAAa,GAAG,QAAQ,GAAG,OAAO,GAAG,UAAU,GAAG,OAAO,CAaxE","sourcesContent":["/**\n * Bounded stall detector.\n *\n * Detects no-progress at strategy/phase level using structured progress\n * observations (and call-level no-progress evidence from the 1.4.0 Tool Storm\n * Breaker). Proceeds through bounded stages:\n *\n *   none → warning → strategy_review → pivot_required → blocked\n *\n * It never permits infinite self-reflection. Legitimate waiting for a changing\n * external condition is not counted as stall when a bounded polling policy\n * exists.\n */\n\nimport type { ProgressAccumulator } from \"./progress.js\";\nimport { accumulatorWeight } from \"./progress.js\";\nimport type { StallLevel, StallState } from \"./types.js\";\n\nexport interface StallConfig {\n\t/** Turns without structured progress before a warning. */\n\twarningAfterNoProgressTurns: number;\n\t/** Calls without structured progress before a warning. */\n\twarningAfterNoProgressCalls: number;\n\t/** ... before strategy_review. */\n\treviewAfterNoProgressTurns: number;\n\treviewAfterNoProgressCalls: number;\n\t/** ... before pivot_required. */\n\tpivotAfterNoProgressTurns: number;\n\tpivotAfterNoProgressCalls: number;\n\t/** ... before blocked. */\n\tblockedAfterNoProgressTurns: number;\n\tblockedAfterNoProgressCalls: number;\n}\n\nexport const DEFAULT_STALL_CONFIG: StallConfig = {\n\twarningAfterNoProgressTurns: 20,\n\twarningAfterNoProgressCalls: 40,\n\treviewAfterNoProgressTurns: 40,\n\treviewAfterNoProgressCalls: 80,\n\tpivotAfterNoProgressTurns: 60,\n\tpivotAfterNoProgressCalls: 120,\n\tblockedAfterNoProgressTurns: 90,\n\tblockedAfterNoProgressCalls: 180,\n};\n\nexport interface StallInput {\n\tprogress: ProgressAccumulator;\n\tnoProgressTurns: number;\n\tnoProgressToolCalls: number;\n\trepeatedFailureFingerprint?: string;\n\tevidenceIds?: string[];\n\toscillationCount?: number;\n\tconfig?: StallConfig;\n}\n\nexport interface StallEvaluation {\n\tstate: StallState;\n\tpreviousLevel?: StallLevel;\n}\n\n/**\n * Deterministically evaluate stall level from no-progress counts and the\n * structured progress accumulator. The accumulator is the authority on progress:\n * prose never resets stall.\n */\nexport function evaluateStall(input: StallInput): StallEvaluation {\n\tconst config = input.config ?? DEFAULT_STALL_CONFIG;\n\tconst progressWeight = accumulatorWeight(input.progress);\n\n\tconst effectiveNoProgressTurns =\n\t\tprogressWeight > 0 ? Math.max(0, input.noProgressTurns - Math.floor(progressWeight / 3)) : input.noProgressTurns;\n\n\tconst reasonCodes: string[] = [];\n\tlet level: StallLevel = \"none\";\n\n\tif (input.oscillationCount && input.oscillationCount >= 2) {\n\t\treasonCodes.push(\"STRATEGY_OSCILLATION\");\n\t}\n\tif (input.repeatedFailureFingerprint) {\n\t\treasonCodes.push(\"REPEATED_FAILURE_FINGERPRINT\");\n\t}\n\n\tif (\n\t\teffectiveNoProgressTurns >= config.blockedAfterNoProgressTurns ||\n\t\tinput.noProgressToolCalls >= config.blockedAfterNoProgressCalls\n\t) {\n\t\tlevel = \"blocked\";\n\t\treasonCodes.push(\"NO_PROGRESS_BLOCKED\");\n\t} else if (\n\t\teffectiveNoProgressTurns >= config.pivotAfterNoProgressTurns ||\n\t\tinput.noProgressToolCalls >= config.pivotAfterNoProgressCalls\n\t) {\n\t\tlevel = \"pivot_required\";\n\t\treasonCodes.push(\"NO_PROGRESS_PIVOT_REQUIRED\");\n\t} else if (\n\t\teffectiveNoProgressTurns >= config.reviewAfterNoProgressTurns ||\n\t\tinput.noProgressToolCalls >= config.reviewAfterNoProgressCalls\n\t) {\n\t\tlevel = \"strategy_review\";\n\t\treasonCodes.push(\"NO_PROGRESS_STRATEGY_REVIEW\");\n\t} else if (\n\t\teffectiveNoProgressTurns >= config.warningAfterNoProgressTurns ||\n\t\tinput.noProgressToolCalls >= config.warningAfterNoProgressCalls\n\t) {\n\t\tlevel = \"warning\";\n\t\treasonCodes.push(\"NO_PROGRESS_WARNING\");\n\t}\n\n\tconst state: StallState = {\n\t\tlevel,\n\t\treasonCodes: Object.freeze([...new Set(reasonCodes)]),\n\t\tnoProgressTurns: effectiveNoProgressTurns,\n\t\tnoProgressToolCalls: input.noProgressToolCalls,\n\t\trepeatedFailureFingerprint: input.repeatedFailureFingerprint,\n\t\tevidenceIds: Object.freeze([...(input.evidenceIds ?? [])]),\n\t};\n\treturn { state };\n}\n\n/**\n * Poll-gating helper: a bounded polling policy counts as legitimate waiting,\n * not stall, when the poll observes a state change.\n */\nexport function pollProgress(\n\tpreviousHash: string | undefined,\n\tcurrentHash: string | undefined,\n\thasStructure: boolean,\n): boolean {\n\treturn hasStructure && Boolean(previousHash) && Boolean(currentHash) && previousHash !== currentHash;\n}\n\n/** Stage the next action after a stall level. */\nexport function stallNextAction(\n\tlevel: StallLevel,\n): \"continue\" | \"self_review\" | \"review\" | \"pivot\" | \"escalate\" | \"block\" {\n\tswitch (level) {\n\t\tcase \"none\":\n\t\t\treturn \"continue\";\n\t\tcase \"warning\":\n\t\t\treturn \"self_review\";\n\t\tcase \"strategy_review\":\n\t\t\treturn \"review\";\n\t\tcase \"pivot_required\":\n\t\t\treturn \"pivot\";\n\t\tcase \"blocked\":\n\t\t\treturn \"block\";\n\t}\n}\n"]}