0.167.46

Added

  • Reasoning-поля end-to-end + thinking-переключатели запроса ( — P3, аудит 2026-08-context). Цепочка рассуждений reasoning-моделей больше не теряется: разбор → живой стриминг → персист → UI. Провайдеры: parseOpenAIResponse/ChatStream OpenAI-compat извлекают reasoning_content (DeepSeek/Qwen/MiniMax/vLLM, приоритет) и reasoning (OpenRouter) из SDK ExtraFields (типизированных полей в openai-go v1.12 нет; unknown-ключи SDK хранит со статусом invalid — присутствие детектится через Raw(), не Valid()); стриминг эмитит StreamEvent{Type:"reasoning_delta"} (ctx-guarded send — потребитель, переставший читать, не подвешивает горутину) и собирает текст в LLMResponse.ReasoningContent. Anthropic: thinking_delta дополнительно к echo-back-буферам (#277) эмитит reasoning_delta для живого отображения. Fallback-цепочка форвардит reasoning_delta с пометкой committed (видимый вывод неотменим при failover). Реестр: ProviderSpec.ThinkingStylethinking_type (DeepSeek thinking:{type:enabled|disabled}) и enable_thinking (Qwen/DashScope, гибридные модели; wire-форматы сверены с доками провайдеров июль 2026); ChatRequest.ThinkingLevel прокидывается из AgentRunSpec (applyThinkingLevel) и транслируется в wire-параметр; level="" ничего не отправляет (ручной extra_body остаётся рабочим), а явный extra_body-override того же ключа выигрывает у реестрового переключателя (обратная совместимость: thinking_level агента по умолчанию “standard”, и занятый ключ молча выкинул бы pre-#306 DeepSeek-конфиги). Транспорт: AgentHook.OnReasoningStream (+Nop/Composite/все реализации); runner прокидывает reasoning-дельты в hook и пишет reasoning tool-итераций в live-персист; actor публикует _reasoning_delta (отдельный от _stream_delta флаг — коалесцер менеджера каналов не смешивает мышление с телом ответа), финальный персист messages.reasoning (колонка существует с миграции 003, всегда NULL; reasoning-only финал при пустом Content тоже персистится); WS: event reasoning_delta, ReasoningText в конверте, reasoning-flavour терминатора (_reasoning_end) — payload reasoning-терминатора не может утечь в answer-слот Text; resume-снапшот agent_status.reasoning_text (единый снимок состояния под одной блокировкой — без torn snapshot). Коалесцер: два независимых потока дельт, foreign-сообщения в очереди pendingMsgs. Компакция: reasoning НЕ входит в сводки (инвариант — тесты messagesToText/archiveToConversation); echo-back reasoning в OpenAI-запросы не выполняется (DeepSeek принимает reasoning_content только в Chat Prefix Completion beta). WebUI: streamingReasoning в ChatStore (санитизация bidi/zero-width, кап 256 KiB на дельтах и снапшотах, watchdog-очистка), свёрнутый блок «Размышления» (ReasoningBlock: plain-text в live-фазе → markdown после стрима; ручное сворачивание уважается до конца цикла стрима, сброс latch на новом цикле; focus-visible a11y), resume блока при реконнекте, reasoning прикрепляется к сообщению на stream_end. llmlog: reasoning_content ответа логируется для всех OpenAI-compat провайдеров. Тесты (+35): parse-приоритеты/null/non-string, стриминг (reasoning-дельты, отсутствие regressions), thinkingExtras-матрица и buildParams-переключатели (включая extra_body-override), fallback forward/commit, anthropic-эмит, runner hook/FinalReasoning/live-персист, streamHook-публикация, coalesce-изоляция потоков + сохранность foreign, SendDelta-маршрутизация (комбинированные флаги), компакция-инварианты, store-тесты фронтенда и компонентные тесты ReasoningBlock (5 сценариев).