6.7 KiB
Phase 2 — Approval Reactions
Баг 1: Сообщения дропаются при аппрувл-прерывании (подтверждён)
Дата: 2026-06-18
Статус: Исправлен — добавлен streaming через display.platforms.webhook.streaming: true
Баг 2: ▉ курсор в стриминге для webhook/Zulip (активный)
Дата: 2026-06-18 Статус: Диагностируется
Симптом
При стриминге через webhook (Zulip) — каждое partial сообщение содержит ▉ курсор в конце. Zulip не поддерживает редактирование сообщений, курсор добавляется и никогда не убирается.
Почему
GatewayStreamConsumer добавляет курсор ▉ к каждому partial сообщению (on_delta → _send_or_edit). Когда adapter_supports_edit == False — курсор остаётся навсегда, потому что:
- Partial шлётся с курсором (первый send)
- Edit не поддерживается → нельзя перезаписать/убрать курсор
_send_fallback_finalприgot_done— должен убрать курсор, но если edit не поддерживается — не может
Что сделано
Конфиг (~/.hermes/hermes-whale/config.yaml):
- Добавлено
display.platforms.webhook.streaming: true— стриминг для webhook - Добавлено
platforms.webhook.extra.supports_message_editing: false— указание что webhook не поддерживает edit
Код (gateway/run.py):
- Добавлено чтение
platforms.{platform}.extra.supports_message_editingиз конфига (строка 16712-16718) - Если в конфиге
supports_message_editing: false—_adapter_supports_edit = False→_effective_cursor = ""→ курсор не добавляется
Текущий статус: supports_message_editing: false в конфиге есть, но логи всё ещё показывают adapter_edit=True. Причина выясняется.
Логи (актуальные)
[whale-cursor] add cursor: ... adapter_edit=True
[whale-send] send_or_edit allowed: ... adapter_edit=True
Все логи показывают adapter_edit=True — настройка не применяется.
Версии
- PID 17593 (Кит, порт 8645)
- Gateway запущен
python -m hermes_cli.main gateway run --replace - Логи:
~/.hermes/hermes-whale/logs/gateway.log - Конфиг:
~/.hermes/hermes-whale/config.yaml
Симптом
Когда LLM пишет текст (через стриминг), потом делает tool call с опасной командой — аппрув-промпт прерывает стрим. Весь текст, который LLM уже сказала ДО опасной команды, не доходит до юзера. Юзер видит только аппрув-промпт и финальный ответ (если он есть).
Почему
Причина в GatewayStreamConsumer (gateway/stream_consumer.py).
При streaming.enabled: true для webhook/Zulip:
- LLM стримит текст →
on_delta()кладёт в очередь - Стрим прерывается аппрувом (interrupt) →
got_done = False, циклrun()выходит раньше времени _send_fallback_final()не вызывается — потому что он только приgot_done(строка 580)- Накопленный
_accumulatedтекст теряется
Дополнительно: edit_supported=False для Zulip/Webhook — стример не может редактировать, fallback mode активируется, но не успевает отработать.
Логи из теста
[KIT-stream2] path=webhook streaming_enabled=True want_deltas=N/A _scfg.enabled=True transport=auto
[consumer] on_delta: 1 chars, queue=1
[send_or_edit] enter: ... edit_supported=False fallback_final=True ... (10+ раз, зациклен)
— got_done NOT FOUND в логах —
Что меняли для диагностики
gateway/stream_consumer.py:
logger.debug→logger.warningв_send_or_edit(enter, cursor-only)logger.debug→logger.warningв_send_fallback_final(enter)- Добавлен
warnings.warnна уровне импорта (для проверки загрузки модуля)
gateway/run.py:
[KIT] GatewayRunner.__init__ called— проверка что run.py подхватывается[KIT-stream] entering streaming setup— первый path (не для webhook)[KIT-stream2] path=webhook streaming_enabled=...— второй path (для webhook)
/Users/admin/.hermes/hermes-whale/config.yaml:
- Добавлено
display.platforms.webhook.streaming: true— чтобы стриминг работал для вебхука (был_TIER_MINIMALdefault)
gateway/__pycache__/stream_consumer.cpython-311.pyc:
- Был stale от June 4 — не влияет на проблему (редактируемый install работает)
Как чинить
Нужно чтобы при interrupt (аппрув-прерывание) стример отправлял накопленный текст перед выходом. Варианты:
- В
run.pyпосле прерывания стрима — вызвать_send_fallback_final(_accumulated)если есть что отправлять - В
stream_consumer.pyна interrupt path — форсироватьgot_doneили прямой вызов_send_fallback_final - Не прерывать стрим при аппруве — пусть стриминг продолжается до естественного завершения (
got_done)
История расследования
- Сначала думали что проблема в
stream_consumerс streaming enabled — но баг был ДО включения streaming - Выяснили что streaming был выключен для webhook из-за
_TIER_MINIMALвdisplay_config.py→streaming: False - После включения
display.platforms.webhook.streaming: true— стриминг пошёл - Тест с
rm -rfподтвердил: текст до аппрува не появляется у юзера - Логи подтвердили:
got_doneне вызывается →_send_fallback_finalне срабатывает