0.164.0

Added

  • Синхронный /stop: UI получает реальный статус остановки хода (ADR 2026-07-04, ). Раньше /stop был асинхронным: loop.go звал cancel(), сразу публиковал _turn_end: true и UI сбрасывал «thinking»-индикатор. Если cancel не пробивал блокирующую операцию (зависший LLM/exec/MCP) — пользователь думал, что ход остановлен, но тот продолжался и расходовал токены. В проде зафиксировано 5 подряд /stop (04-07-2026, агент semen), каждый раз interrupted-divider записывался в БД, но ход не отменялся.

  • Новый протокол (internal/agent/loop.go:handleStop) — cancel()WaitForIdle (новый метод на SessionActor, ждёт выхода handleInbound через polling busy флага) → cleanup очередей → запись interrupted-divider → публикация _turn_end с реальным статусом stopped / still_running / already_idle. Порядок критичен: divider обязан оказаться в БД раньше, чем UI получит _turn_end.

  • UI (web/src/lib/stores/chat.svelte.ts) — больше НЕ сбрасывает waitingForAgent мгновенно при отправке /stop; ждёт _turn_end от сервера.

  • Setting stop_wait_timeout_seconds (1–30 сек, по умолчанию 5) — таймаут ожидания; по истечении UI видит «Не удалось остановить ход за 5s…».

  • ТестыTestWaitForIdle_* (3 сценария), TestHandleStop_WaitsAndWritesDivider, TestHandleStop_AlreadyIdleStatus.

  • ocr review 20 замечаний, валидные устранены (см. ниже).

  • Setting stop_wait_timeout_seconds (1–30 сек, по умолчанию 5) — таймаут ожидания для синхронного /stop.

Changed

  • Терминальность финального ответа: hasContinuationMarker и continuation-loop удалены (ADR 2026-07-04, ). Раньше при StopReason="stop" + эвристике «сделаю / let me / i’ll» в финальном ответе запускалось до 3 доп. итераций ([System] Продолжай выполнение задачи.). False positive на «I’ll think about it» продлевал ход после финала. Теперь финал строго терминален: если LLM обрезан, пользователь явно скажет «продолжай», либо LLM использует ask_user. Удалены continuationMarkers, loadMaxContinuation, setting agent_max_continuations (from ParamRegistry и docs/guide/configuration.md). ADR 2026-05-05 помечен как «частично отменён».

  • Subagent injection на финальном ответе: continuereturn (ADR 2026-07-04, ). В internal/agent/runner.go при StopReason="stop" InjectionCallback больше НЕ вызывается. Subagent-результат остаётся в pendingInjections actor’а и попадёт в контекст следующего хода. Это устраняет риск, что orphan-subagent (переживший cancel) продлит отменённый ход.

Fixed

  • persistTurnTimeout: recover + retry + логирование (ADR 2026-07-04, ). При расследовании эпизода 04-07-2026 обнаружено: persistTurnTimeout не сработал в 10:09 при agent_turn_timeout_minutes=60 — нет ни WARN-лога «agent turn timed out», ни assistant-сообщения, ни wip_turn в scratchpad. Фикс:

  • Тело обёрнуто в defer recover() — паника в defer больше не молча убивает функцию.

  • AddMessage ретраится 1 раз через 200мс на transient-ошибку БД (addMessageWithRetry).

  • Логирование ошибок записи divider’а и cleanup очереди в handleStop (раньше silently проглатывались).

  • Тесты — TestPersistTurnTimeout_RecoversFromPanicpanickingStore заглушкой).

  • ctx-аудит: операции, переживающие ctx.Done() хода (ADR 2026-07-04, ). Несколько мест в коде выполняли async-persist отвязанно от ctx хода и могли пережить interrupt:

  • internal/agent/runner.go:236AsyncAddMessage(context.Background(), …) для tool_call_history: теперь пропускается, если ctx.Err() != nil (с INFO-логом). Регрессионные тесты TestRun_CancelledCtxSkipsToolCallHistoryPersist + TestRun_NormalCtxPersistsToolCallHistory.

  • internal/agent/runner.go — добавлен early-return по ctx.Err() != nil после tool execution (защита от 5-мин блокировки на InjectionCallbackpendingCond.Wait).

  • internal/agent/actor.go:1184-1196 — горутины bgWG (autoCompact, pruneToolResults): WARN-лог только при errors.Is(ctx.Err(), context.Canceled) (раньше ловил и нормальный DeadlineExceeded, засоряя логи).

  • PG-миграция kg_extraction_queue. На проде 192.168.2.104 (PostgreSQL) постоянный WARN отношение "kg_extraction_queue" не существует (SQLSTATE 42P01) — миграция 045_kg_queue не была применена (инстанс инициализирован до её создания). Добавлена idempotent-миграция 104_kg_queue_ensure.up.sql (SQLite + PG, парные down) — CREATE TABLE IF NOT EXISTS + INSERT … ON CONFLICT DO NOTHING бэкфилл.

ADR

  • ADR 2026-07-04 «Терминальность прерывания и финального ответа агента» — формализация инварианта «прерывание и финальный ответ — терминальные состояния». Описывает 7 изменений (P0-1 … P2-8), из которых 6 реализованы в этом релизе, 2 (P1-6, P2-7) закрыты как already-covered существующими механизмами.

Issues

  • #252 [P0-1] Синхронный /stop — fixed
  • #253 [P0-2] persistTurnTimeout recover/retry — fixed
  • #254 [P0-3] ctx-аудит — fixed
  • #255 [P1-4] Удалить hasContinuationMarker — fixed
  • #256 [P1-5] runner continue→return — fixed
  • #257 [P1-6] cleanupSubagents — closed (duplicate, уже реализовано в b8a1e76)
  • #258 [P2-7] Skip abandoned tool_call_history — closed (covered by P0-3 + существующий фильтр)
  • #259 [P2-8] PG-миграция kg_queue ensure — fixed