Заменяет JivoBotController+JivoBotClient на ChatController (POST /api/chat/message)
и ProcessJivoMessageJob на ProcessChatMessageJob. Реплику клиента теперь пишет
контроллер и возвращает её номер (message_id); джоба получает этот номер и берёт
историю строго до него, не отправляя ответ во внешний Jivo — он ложится в bot_dialogs,
откуда его заберёт своё окошко (Задача 3). Мозг бота не менялся.
Спека: docs/superpowers/specs/2026-07-13-own-chat-widget-design.md §5
Прогон 46 диалогами (250 реплик подряд в одном чате) вскрыл то, чего не видел
прогон отдельными вопросами:
- Клиент оставил телефон → бот не отвечал ВООБЩЕ: письмо в поддержку слалось
синхронно внутри ответа, SMTP не отозвался → упала вся задача (tries=1).
Теперь номер в чате = заявка на звонок: принимаем всегда (даже без просьбы
бота), письмо уходит через очередь и не может уронить ответ.
- Бот «забывал» себя: сторож резал цену, взятую из истории чата, ответ пустел
→ эскалация. Числа из своих прошлых ответов теперь разрешены; память чата
3 → 5 пар реплик; в эскалационную ветку тоже передаётся история.
- AnswerGuard: + телефон поддержки (его нет), + юр-гарантии в новых оборотах
(«Да, законно», «Штраф не прилетит»), + враньё «на паузе списаний нет»,
+ выдумки про 18:00/ночь, + «цена зависит от ниши»; снимает висящее «Но…»
и голое «Правильно, не будут» после вырезанной лжи.
- Промпт: не повторять сказанное, не поддакивать клиенту, без нумерованных списков.
- pravilo-18-00.md: 18:00 — граница для ИЗМЕНЕНИЙ, а не конец приёма заявок.
Тесты 120/120. Бот НЕ на проде.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Правило владельца 12.07.2026: оператора в чате может не быть, поэтому бот НЕ
переводит диалог (INVITE_AGENT убран из авто-флоу). Не смог ответить (личный
вопрос, стоп-тема, незнание) → просит номер телефона → как только клиент его
оставил, контакт уходит письмом в поддержку, а клиенту — «Спасибо, передал».
- ContactCapture: распознаёт номер в любом формате (+7/8/скобки/дефисы) → 79161234567
- BotContactRequestMail + шаблон: телефон, вопрос-на-котором-спасовал, id чата,
переписка целиком, время → services.support.email
- ProcessJivoMessageJob: ждём контакт, если прошлый ответ бота просил номер;
номера нет (клиент передумал/спросил другое) — отвечаем как обычно
- тексты эскалации переписаны под сбор контакта
TDD: BotContactCaptureTest (4 новых) + обновлены 4 теста прежней механики. 46/46.
Живая проверка на стенде: вопрос → просьба → номер → письмо с полной перепиской.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>