0.167.39
Added
- Семантические дайджесты tool-выводов вместо слепой обрезки head+tail ( — P2). Прежде
FastTrimToolResultsрезал старые tool-результаты вслепую по байтам (~2/3 голова + ~1/3 хвост): серединаgrep-списка терялась без счётчиков,git diffрвался посередине ханка, упавшие кейсы тестов выпадали из середины exec-вывода — модель получала обрубок и перезапускала инструмент целиком (ровно тот цикл, который #265 пытался разорвать). ВведёнDigestToolResult(internal/agent/tool_result_digest.go): реестр дайджестеров по имени инструмента, подключённый вFastTrimToolResults(вMessageуже естьToolName). Дайджестеры: grep — сводка «N matches in M files» со счётчиками по файлам (top-15, отсортированы по числу совпадений) + первые 20 матчей; все три режима (content/files/count) парсятся устойчиво к двоеточиям в путях (content — lazy-regex, files/count — LastIndex, корректно для Windows-путей). exec — exit code + stderr (head/tail) + stdout (head/tail + все строки с error/fatal/panic/traceback из СЕРЕДИНЫ, без дублирования с head/tail; слово-границы + негативный фильтр «no errors»/«0 errors»); парсер секций терпим к пустому stdout-блоку и «stdout:»/«stderr:» в теле. list_dir/glob — entries/matches из атрибута (с fallback на пересчёт по телу, чтобы overflow-note не пропадал) + статистика dirs/files + первые записи. read_file — путь, диапазон строк (футер «showing X-Y of Z» или «file read in full»), head/tail нумерованных строк с кольцевым буфером O(1)/строку (без копирования всего файла в память). Общий каркас: строкаFull output saved to: <path>(изtruncateAndSaveResult) резервируется из бюджета ДО ужимания тела и гарантированно остаётся последней строкой дайджеста; при микробюджете путь приоритетнее сводки. Инструменты без дайджестера (включая MCPmcp__*) — прежний head+tail фолбэк. Тесты (24 новых): счётчики/файлы grep во всех режимах, error-строки из середины exec + негативы + дедуп с head/tail, бюджет во всех дайджестах, UTF-8-валидность на невалидном входе, lazy-regex на двоеточиях в тексте, saved-path (обычный/tiny/micro-бюджет, завершаемость), bounded read_file, интеграция с FastTrimToolResults.
Fixed
TruncateToolResultнарушал собственный бюджет на 1 токен (найдено тестами ). Запас+0.5у хвостового бюджета + round-half-up вtokens.Countдавали 501 токен при бюджете 500; при CJK-весах (1.0/руна) и микробюджетах расхождение достигало размера головы. Переписан пост-условиемtokens.Count(result) <= maxTokensпри любомmaxTokens >= 1: tight-fit-цикл дозажимает хвост (1-2 итерации, ранний выход если голова+маркер уже не влезают — иначе O(n²)), при невлезающем маркере —prefixByTokens-hard-cap ценой потери маркера. Регрессионные тесты: бюджеты {1,2,5,6,7,25,100,500,4000} на ASCII и CJK-контенте.