0.167.11

Fixed

  • memory_context молча выбрасывался всеми провайдерами, но расходовал токен-бюджет (P0). Роль memory_context — единственный механизм, компенсирующий отсутствие tool-контекста между ходами (ADR 2026-07-02): трасса tool-работы хода (persistToolTrace) и результаты memory_search. Оба провайдерных конвертера имели switch m.Role без default и без case "memory_context" — сообщение не матчило ни одну ветку и тихо исчезало перед отправкой к LLM, при этом грузилось из БД, считалось в historyTokens и запускало enforceMemoryContextBudget, который при «переполнении» выбрасывал другие сообщения. Фикс (1) — доставка: internal/providers/openai_compat.gocase "memory_context" переписывает сообщение в system на своей позиции в массиве (семантическая смена: инструкция, а не реплика; статично — не хойстится в dynamic-system prefix, зарезервированный за промпт-инжинирингом, #278); internal/providers/anthropic.go — в user-сообщение (system у Anthropic вынесен в отдельное поле API). Оба конвертера получили default-ветку: неизвестная роль деградирует (system/user) с slog.Warn (msg_index, content_len, dropped_fields) — тихая потеря сообщений исключена в принципе. Помощники degradedSystemMessage/degradedUserMessage применяют fallback " " для пустого/whitespace-only контента (пустой text block — 400 от Anthropic; часть openai-совместимых бэкендов отвергает пустой content) с WARN-логом; degradedRoleFieldNames перечисляет структурные поля (tool_calls, tool_call_id, tool_name, is_error, media, reasoning_blocks), не переживающие текстовую деградацию — потеря наблюдаема в логах; WARN о потере зеркален и в case "memory_context" (продюсеры ставят ToolName="tool_trace"). Merge-hazard задокументирован и запинен тестом: реальная топология БД [..., assistant, mc, user(след.ход)] после user-переписывания даёт смежные user-ходы, которые mergeConsecutiveAnthropic сливает в одно multi-block сообщение — текст retrieved-контекста доезжает до модели (блоки: контекст → реплика пользователя), теряется только per-message identity. Фикс (2) — бюджет: enforceMemoryContextBudget (internal/agent/actor.go) сравнивал с contextMaxTokens/4 токены ВСЕЙ истории — на модели 128K выброс memory_context начинался, как только диалог сам превышал 32K токенов, независимо от объёма retrieved-контекста. Теперь бюджет меряется по самим memory_context-сообщениям (estimateMessageTokens, без slice-оверхеда EstimateTokens): старейшие вне tool-пар удаляются, пока остаток не уложится. Unit-тесты: internal/providers/memory_context_delivery_test.go — memory_context доставляется (system для OpenAI-compat, user для Anthropic) с сохранением позиции; merge с следующим user-ходом; неизвестная роль не теряется (оба провайдера); internal/agent/audit_medium_agent_test.go — огромный диалог не трогает маленький mc (регрессия старой семантики); переполнение выбрасывает старейшие mc до укладки в бюджет. Пункт 3 issue (прекратить запись tool-трассы) осознанно отложен до реализации ADR live-tool-messages (#280): сейчас трасса — единственный механизм осведомлённости о tool-работе. Проверки: go test ./... — ok; golangci-lint ./internal/... — 0 issues; ocr review — цикл до стабилизации (9 прогонов, актуальные замечания устранены, остальные — обоснованно неактуальные/LOW); make build-cross — 6/6.