6.1 KiB
Executable File
План: tool-progress бабблы в тред (Mattermost)
Задача Alex 2026-09-29: «надо починить MM интеграцию: tool calls сыпятся в основной канал а не в тред». Статус: ПЛАН, ждёт подтверждения.
Симптом
Alex пишет @whale в треде → финальный ответ приходит в тред правильно,
а бабблы tool-progress (🔍/⚙️/emoji + имя тула) падают в корень канала.
Доказательство из ~/.hermes/hermes-whale/logs/gateway.log (2026-09-29):
13:34:11 inbound message: platform=mattermost user=admin chat=a6nyh77eyjbn5c1owh1i8eebee msg='Почему'
13:35:09 response ready: platform=mattermost chat=a6nyh77eyjbn5c1owh1i8eebee ... response=805 chars
Session key: agent:main:mattermost:channel:a6nyh77eyjbn5c1owh1i8eebee:pcoiddzrqty3ipqtozgcs9ttte
→ тред-часть в session_key есть (pcoiddzrqty3ipqtozgcs9ttte), значит финал
адресуется верно. Проблема только в progress-канале.
Причина (найдена в коде)
gateway/run.py, строки 17153-17170 — расчёт анкора для progress-бабблов:
if source.platform in (Platform.SLACK, Platform.MATTERMOST):
_progress_thread_id = source.thread_id or event_message_id
else:
_progress_thread_id = source.thread_id
_progress_metadata = (...) if _progress_thread_id else None
_progress_reply_to = (
event_message_id
if source.platform in (Platform.FEISHU, Platform.MATTERMOST)
and source.thread_id and event_message_id
else None
)
🔴 Ключевая строка — _progress_reply_to: требуется source.thread_id.
Но source.thread_id заполняется только когда триггер-сообщение само внутри треда.
Когда @whale написан в корне канала (root_id пустой) — source.thread_id = None
→ _progress_reply_to = None → adapter.send(..., reply_to=None) → пост уходит
новым корневым постом канала.
При этом _progress_metadata считается по другой ветке (thread_id or event_message_id)
и тоже может получать якорь — то есть два поля рассинхронизированы:
metadata иногда есть, reply_to нет.
_send_progress_text() (строка 17273) передаёт оба:
result = await adapter.send(
chat_id=source.chat_id,
content=text,
reply_to=_progress_reply_to, # ← здесь None
metadata=_progress_metadata,
)
MM-адаптер в send() (строка 290) уважает анкор только так:
if reply_to and self._reply_mode == "thread":
resolved_root = await self._resolve_root_id(reply_to)
payload["root_id"] = resolved_root
→ при reply_to=None ветка не срабатывает вообще. MATTERMOST_REPLY_MODE=thread
уже стоит (.env), т.е. адаптер готов — до него не доезжает анкор.
Сравнение с остальными платформами
| Платформа | Правило анкора progress | Ок? |
|---|---|---|
| Slack | thread_id or event_message_id для metadata |
так же как MM |
| Feishu | reply_to только при thread_id |
тот же дефект |
| Mattermost | metadata ← thread_id or event_message_id, reply_to ← только thread_id |
🔴 рассинхрон |
Предлагаемый фикс (1 строка + симметрия)
Выровнять _progress_reply_to под ту же логику, что metadata:
опираться на уже вычисленный _progress_thread_id, а не на сырой source.thread_id.
_progress_reply_to = (
event_message_id
if source.platform in (Platform.FEISHU, Platform.MATTERMOST)
and _progress_thread_id and event_message_id
else None
)
Что меняется: при посте в корне канала _progress_thread_id = event_message_id
→ _progress_reply_to = event_message_id → адаптер резолвит root и кладёт
progress-баббл в тред, созданный от этого поста. При посте внутри треда
поведение не меняется (source.thread_id уже непустой).
⚠️ Не трогаю: reply_mode, REQUIRE_MENTION, логику меншн-гейта (патч 9b46ba791a),
edit_message-путь (там metadata передаётся отдельно, строка 17243).
Шаги
- Бэкап
gateway/run.py→backups/<ts>-mm-thread-progress/run.py(+ md5). - Правка строки 17166-17170 в
run.py(скриптом в файле, не sed/inline). pytest tests/gateway/ -k mattermost+tests/gateway/test_stream_events.pyесли есть.- Прогон шире:
pytest tests/gateway/ -q -o 'addopts='. - Коммит
fix(mattermost): anchor tool-progress bubbles to thread. - Alex рестартует gateway (
gateway run --replaceиз моей сессии убьёт её же).
Приёмка (Alex)
Пишу @whale в корне канала hermes-maintenance → и финальный ответ,
и бабблы tool-progress видны в треде под моим постом, в корне — ничего лишнего.
Риски
| Риск | Оценка |
|---|---|
| Прогресс уедет в тред там, где Alex ждал в корне | низкий — при reply_mode=thread это и есть желаемое |
| Поломка Slack/Feishu | низкий — правка в общей ветке, для них _progress_thread_id считается иначе; проверить тестами |
| Регресс в approval-промптах | нет — у них свой путь (send_exec_approval) |