1.7.7

Fixed (#405)

  • Image-части больше не уходят на wire text-only модели: litellm-прокси отвечал 400 messages.content.type is invalid, allowed values: ['text'] на каждый ход диалога после первого сообщения с картинкой (прод: agent 1, glm-5.3 → fallback-цепочка glm-5.2/5.1, llm-логи ~/.local/share/taigaclaw/llm-logs/1/*.jsonl — user-сообщение с parts:["image"] персистится в messages.media и уезжало в каждом следующем запросе). Гейт способностей модели применялся только к вложениям из workspace; inline-медиа из WebSocket-канала, ре-гидрация истории из БД и сериализация обоих провайдеров шли без проверки. Новый ChatRequest.ImageInputAllowed *bool: агент резолвит вердикт полным каскадом (override models.supports_vision → реестр capabilities — реестр в провайдере напрямую НЕ читается, чтобы не ломать легитимный override на кастомных litellm-группах) при сборке хода (основной/resume/subagent) и кладёт в запрос; openai-compat и anthropic деградируют image-части user-сообщений и tool-результатов в один общий текстовый placeholder [Image attachment omitted: this model does not support image input] (паттерн E8.3 #331). nil = прежнее поведение (aux-вызовы без медиа не затронуты). Обходной путь для существующих сломанных сессий: новая сессия чата (картинка в окне истории продолжала бы 400-ить и после фикса — теперь вместо 400 модель увидит placeholder).

Fixed (#406)

  • Ошибка провайдера в чате — человекочитаемый текст вместо сырой строки SDK с килограммом JSON. providers.HumanizeError(err) (userText, detail): словарь по статусам/типу (400/401/403/404/429/5xx/таймаут/DNS/подключение — обобщение словаря test-connectivity), detail — полный текст ошибки с капом 4000 байт, rune-safe обрезкой, нормализацией невалидного UTF-8 и редъакцией секретов (Bearer/api_key/sk-токены с якорями против ложных срабатываний — claude-sk-… и пути не трогаются); редъакция до капа в обеих ветках (userText-фолбэки и detail). Терминальные ветки актора (run_error, resume-final-error; TTFB-текст #400 сохранён приоритетнее словаря, для resume теперь тоже) шлют в чат короткий текст + _error_detail в метаданных; WS-событие error несёт Detail; WebUI рендерит error-бабл (danger-акцент, ⚠️, role="status") со сворачиваемыми «Техническими деталями» (клиентский страховочный кап UTF-16, суррогатные пары не рвутся); флаги turn_failed/error_detail переживают merge с серверной строкой (переносятся на строку-замену); email получает короткий текст. В БД персистится короткий текст (полный — в llm-логе).

Review (#405, #406)

  • ocr review — 8 раундов; все high/medium исправлены: гейт tool-результатов в anthropic (image-блоки из enhanced-тузов шли мимо гейта), редъакция секретов (порядок редъакция→кап, прокси-сепараторы Bearer=/Bearer:, якоря sk-токенов), байтовый инвариант капа («…» больше не превышает лимит), TTFB-паритет resume-ветки, дедуп error-баблов (#348-семантика сохранена), перенос клиентских флагов через mergeServerMessages, a11y (role="status"), имя turn_failed вместо коллизионного is_error, rune-safe обрезка кириллицы. Осознанно оставлено: дублирование логики деградации image-части между провайдерами (3 маленьких сайта с разыми SDK-типами блоков), отсутствие юнит-теста клиентского страховочного капа (защитная сеть, серверный кап протестирован). Проверки: go test ./... -count=1 (0 падений), golangci-lint (0 новых; 20 легаси-SA5011 в нетронутых тестах), go vet, npm run check (0), npm test (891), make build-cross (6 платформ) — зелёные. Диагноз подтверждён llm-логами прода (100.64.0.5) до начала фикса.