0.167.19

Fixed

  • /stop-divider не влиял ни на что: findLastTopicBreak искал роли, отфильтрованные SQL ( — P1). /stop честно писал role="interrupted"-маркер, actor честно искал его через findLastTopicBreak для обрезки истории следующего хода — но GetRecentDialogMessages (SQL dialogRoleFilter в SQLite/PostgreSQL) эти роли не пропускал, поэтому findLastTopicBreak всегда возвращал -1, обе ветки обрезки были мёртвым кодом, и новый ход наследовал контекст отменённого (агент «доделывал» прерванную работу без просьбы). Фикс (вариант А из issue): topic_break/interrupted добавлены в dialogRoleFilter/pgDialogRoleFilter и канонический store.dialogRoles (пин TestDialogRoleFilterSync синхронен) — маркеры проходят в окно как граница обрезки, но не доезжают до LLM: buildHistoryFromDB их пропускает (defence-in-depth для будущих вызовов без обрезки), archiveToConversation (компакция) уже скипал их явно. Новый dropTopicMarkers (single-pass, без аллокаций на чистом окне) применяется после каждой обрезки — делает соответствие history[i] ↔ dbMessages[i] структурным; вычисление границы summary lastOldMsgID переписано проходом с пропуском маркеров + degenerate-фолбэк прежнего guard (ocr review: молчаливый mis-hit кэша summary). Дополнительно: GetRecentDialogMessages теперь проверяет rows.Err() (SQLite+PG) — ошибка итерации больше не выглядит успехом, усечённое окно не может молча потерять маркер обрезки. Комментарий handleStop (loop.go) приведён к реальности (устаревшие ссылки на строки). Тесты: пин TestIsDialogRole (маркеры — dialog roles), store-тест возврата обоих маркеров в окне, actor-тесты обрезки (маркер в середине / несколько маркеров — последний побеждает / без маркеров / маркер последним → пустая история при текущем user-сообщении, идущим отдельным аргументом), skip маркеров в buildHistoryFromDB, dropTopicMarkers.