fix(gateway): route background-process notifications into Telegram DM topics

Background-process completion notifications (notify_on_complete) and
watch-pattern notifications were always delivered to the Telegram main
chat instead of the originating private-chat topic.

Hermes-created Telegram DM topic lanes only render a send when it carries
both message_thread_id and a reply anchor. The synthetic MessageEvent
injected on process completion had no message_id, so _reply_anchor_for_event
returned None and _thread_kwargs_for_send dropped message_thread_id
entirely — routing the notification to the main chat.

Capture the triggering message id at spawn time and thread it through to
the synthetic event so it can be reply-anchored back into the topic:

- session_context: add HERMES_SESSION_MESSAGE_ID context var
- telegram adapter: populate SessionSource.message_id on inbound messages
- terminal tool: persist watcher_message_id on the process session
- process registry: carry/persist message_id on watcher dicts + checkpoint
- gateway: set MessageEvent.message_id on injected notifications

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Fábio Siqueira
2026-05-16 22:31:15 -03:00
committed by Teknium
parent 17f3254ede
commit fbabd560ff
6 changed files with 130 additions and 0 deletions

View File

@@ -13754,6 +13754,7 @@ class GatewayRunner:
user_id=str(context.source.user_id) if context.source.user_id else "",
user_name=str(context.source.user_name) if context.source.user_name else "",
session_key=context.session_key,
message_id=str(context.source.message_id) if context.source.message_id else "",
)
def _clear_session_env(self, tokens: list) -> None:
@@ -14042,6 +14043,7 @@ class GatewayRunner:
message_type=MessageType.TEXT,
source=source,
internal=True,
message_id=str(evt.get("message_id") or "").strip() or None,
)
logger.info(
"Watch pattern notification — injecting for %s chat=%s thread=%s",
@@ -14076,6 +14078,7 @@ class GatewayRunner:
thread_id = watcher.get("thread_id", "")
user_id = watcher.get("user_id", "")
user_name = watcher.get("user_name", "")
message_id = str(watcher.get("message_id") or "").strip() or None
agent_notify = watcher.get("notify_on_complete", False)
notify_mode = self._load_background_notifications_mode()
@@ -14158,6 +14161,7 @@ class GatewayRunner:
message_type=MessageType.TEXT,
source=source,
internal=True,
message_id=message_id,
)
logger.info(
"Process %s finished — injecting agent notification for session %s chat=%s thread=%s",