2c03fefa9d
Третий класс петли (инцидент 2026-07-10): ран упёрся в 20-шаговый кап, спалив все шаги на чтение; на 20-м шаге final-step lockdown отнял инструменты (toolChoice:'none') посреди незаконченной работы → модель выродилась в текст-повтор («loadTools.» ×20416, 255КБ). Пакет защит по дизайну владельца: - MAX_AGENT_STEPS 20→50; спеки выводятся из константы (нет захардкоженных 19/20). - Final-step lockdown под env-тогглом AI_CHAT_FINAL_STEP_LOCKDOWN (дефолт OFF, по образцу AI_CHAT_DEFERRED_TOOLS): OFF — инструменты доступны на всех шагах + мягкий финальный нудж; ON — легаси toolChoice:'none'+FINAL_STEP_INSTRUCTION. Спеки параметризованы по тогглу. .env.example задокументирован. - Пустой ход (все шаги без текста, шаги исчерпаны) получает синтетический маркер-текст — виден в UI и реплее. - Детектор токен-деградации в onChunk (единственная защита от болтовни, БЕЗ maxOutputTokens — tool-аргументы это выходные токены): чистые правила (≥25 одинаковых строк ИЛИ периодический хвост), при срабатывании abort через внутренний AbortController ∪ effectiveSignal (AbortSignal.any), финализация в onAbort: усечение хвоста, ai_chat_runs.error=Output degeneration detected, лизы MCP/снапшоты освобождаются (существующий lifecycle). - Предупреждение о бюджете шагов на MAX-6…MAX-2 с убывающим N. - loadTools-описание и преамбула каталога явно говорят, что CORE-тулы всегда активны (список из CORE_TOOL_KEYS динамически). Внутренний цикл: 2 прохода. Ревью нашло data-loss-риск: правило периодичности детектора ложно срабатывало на markdown-разделителях/setext-подчёркиваниях/ хвостовых пробелах (монохар-хвост p-периодичен при ЛЮБОМ p → ложный abort с пометкой error и усечением). Починка: отдельная монохар-проверка (порог 60, выше любого реального разделителя) + требование ≥2 различных символов в периодическом блоке при p≥2. Реальный loadTools-цикл (период ~10) ловится. Мутационно: 59 одинаковых — не флаг, 60 — флаг; loadTools×20416 — флаг. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>