Skip to content

fix(telegram): preserve DM topic routing via reply fallback#22053

Closed
leehack wants to merge 1 commit into
NousResearch:mainfrom
leehack:fix/telegram-dm-topic-reply-fallback
Closed

fix(telegram): preserve DM topic routing via reply fallback#22053
leehack wants to merge 1 commit into
NousResearch:mainfrom
leehack:fix/telegram-dm-topic-reply-fallback

Conversation

@leehack

@leehack leehack commented May 8, 2026

Copy link
Copy Markdown
Contributor

Summary

Fix Telegram replies for Hermes-created private DM topic lanes.

Telegram updates for these private lanes can arrive with message_thread_id plus a reply anchor, but without direct_messages_topic_id. Sending back with that message_thread_id fails with Message thread not found, and using direct_messages_topic_id targets a different Bot API feature unless explicitly available. This PR preserves the inbound reply anchor in metadata and uses it as the routing fallback for Hermes-created Telegram DM topics.

Fixes #22022.

Changes

  • Add shared helpers for platform-aware thread metadata and reply-anchor selection.
  • Mark Telegram private DM topic lanes as telegram_dm_topic_reply_fallback only when a real reply anchor is available.
  • Carry telegram_reply_to_message_id through metadata-only send paths such as progress/status/streaming and follow-up turns.
  • Keep normal Telegram forum/supergroup topics on message_thread_id.
  • Keep true Bot API Direct Messages topics available through explicit direct_messages_topic_id metadata.
  • Preserve routing metadata through base media fallback methods and Telegram media sends.
  • Avoid routing slash-confirm callback follow-ups with invalid forum General-topic message_thread_id=1 behavior.
  • Add regression coverage for reply fallback, metadata-only sends, multi-chunk sends, inline keyboard sends, missing-anchor safety, and base media fallback metadata.

How to test

Reproduce on Telegram with a Hermes-created private DM topic lane:

  1. Send a message in the topic lane so the incoming update carries message_thread_id and a reply anchor.
  2. Trigger a normal Hermes response, a streaming/progress response, and a follow-up queued while the agent is running.
  3. Verify replies stay in the same topic lane instead of failing with Message thread not found or falling back to the root chat.
  4. For true Bot API direct-message topics, send with explicit direct_messages_topic_id metadata and verify it still routes via that field.

Validation

  • Local platform tested: macOS development checkout.
  • Manual live Telegram test: not run in this PR; behavior is covered by targeted gateway regression tests because it requires specific bot/topic state.
  • venv/bin/python -m pytest tests/gateway/test_telegram_thread_fallback.py tests/gateway/test_run_progress_topics.py -q
    • 45 passed
  • venv/bin/python -m py_compile gateway/platforms/base.py gateway/platforms/telegram.py gateway/run.py tests/gateway/test_telegram_thread_fallback.py
  • git diff --check
  • Codex GPT-5.5 high/xhigh read-only reviews after cleanup:
    • final result: No blocking issues found.

Related issues / PRs

Notes

This is related to Telegram topic routing issues where private-chat topic lanes are not represented as Bot API direct_messages_topic_id on incoming updates. The fix intentionally separates three cases:

  1. forum/supergroup topics: message_thread_id
  2. true Bot API direct-message topics: explicit direct_messages_topic_id
  3. Hermes-created private DM topic lanes: reply-anchor fallback

@GodsBoy

GodsBoy commented May 8, 2026

Copy link
Copy Markdown
Contributor

Confirming this is the right fix shape from a code review of both this PR and #21941.

The three-case framing in the body matches what the Bot API actually exposes:

  1. Forum/supergroup topics: message_thread_id
  2. True Bot API Direct Messages topics: explicit direct_messages_topic_id metadata
  3. Hermes-created private DM topic lanes: reply-anchor fallback

The third case is the one #21941 mis-routed; sending Hermes-created lanes with direct_messages_topic_id does not work because the Bot API does not classify them as Direct Messages topics on incoming updates. The reply-anchor approach this PR uses is the actual mechanism Telegram supports for keeping replies in those lanes.

Coverage matches what I flagged on #21941 as the gap (text streaming send was the only site that PR fixed; this PR covers all the other outgoing sites I listed). The propagation through gateway/platforms/base.py (_thread_metadata_for_source, _reply_anchor_for_event) is cleaner than per-platform plumbing because Feishu was already getting an ad-hoc reply-anchor branch in the existing code; this PR generalizes that pattern.

Two small observations, neither blocking:

  • send_typing is not visibly updated in this diff (the existing _message_thread_id_for_typing path stays). For Hermes-created DM topic lanes, the typing indicator will still be sent with message_thread_id, which Telegram now rejects. The existing comment at L2534 says the failure is swallowed, so this is not a user-visible leak, but the typing indicator will silently no-op for affected lanes. Worth a short follow-up note in the PR body or a parallel _typing_kwargs_for_send if the intent is full parity.
  • gateway/run.py adds two near-identical inline conditionals around _handle_active_session_busy_message to compute the right reply anchor for Telegram DM topics. Worth extracting to a helper so the logic only lives in one place; minor cleanup suggestion.

Approach is sound. Recommend merging this and closing #21941 as superseded.

@alt-glitch alt-glitch added type/bug Something isn't working platform/telegram Telegram bot adapter comp/gateway Gateway runner, session dispatch, delivery P2 Medium — degraded but workaround exists labels May 9, 2026
@leehack leehack force-pushed the fix/telegram-dm-topic-reply-fallback branch from 0a5c80a to ed85e3d Compare May 9, 2026 02:20
kshitijk4poor added a commit that referenced this pull request May 9, 2026
Adds jhin.lee@unity3d.com → leehack so contributor_audit.py strict
mode passes when the salvage of #22053 (telegram DM topic reply
fallback) lands on main.
kshitijk4poor added a commit that referenced this pull request May 9, 2026
The send path uses Hermes' reply-anchor fallback for DM topic lanes
(message_thread_id + reply_to_message_id), but send_chat_action only
accepts message_thread_id — Telegram's Bot API 10.0 rejects it for
these lanes. Without this short-circuit, every typing tick (~every 2s
during agent runs) makes a doomed API call that gets logged as a
'thread not found' debug warning. Skip the call entirely when the
metadata indicates a DM topic reply-fallback lane; the user-visible
behavior is unchanged (no typing indicator either way for these
lanes), but the logs stay clean.

Identified during salvage review of #22053.
@kshitijk4poor

Copy link
Copy Markdown
Collaborator

Thanks for this — your fix landed in #22410 (commit b323957 with your authorship preserved). Cherry-picked onto current main with one tiny follow-up commit on top: send_typing now short-circuits for the DM-topic reply-fallback lanes (the same lanes your fix addresses) since send_chat_action only accepts message_thread_id and would emit "thread not found" debug warnings on every typing tick.

Your three-case framing (forum / true Bot API DM topic / Hermes-created DM topic lane) is the correct mental model — the propagation through the shared _thread_metadata_for_source and _reply_anchor_for_event helpers in gateway/platforms/base.py is also kept intact.

Closing as merged via #22410. Thanks again!

JZKK720 pushed a commit to JZKK720/hermes-agent that referenced this pull request May 11, 2026
…alvage (NousResearch#22409)

Adds jhin.lee@unity3d.com → leehack so contributor_audit.py strict
mode passes when the salvage of NousResearch#22053 (telegram DM topic reply
fallback) lands on main.
JZKK720 pushed a commit to JZKK720/hermes-agent that referenced this pull request May 11, 2026
The send path uses Hermes' reply-anchor fallback for DM topic lanes
(message_thread_id + reply_to_message_id), but send_chat_action only
accepts message_thread_id — Telegram's Bot API 10.0 rejects it for
these lanes. Without this short-circuit, every typing tick (~every 2s
during agent runs) makes a doomed API call that gets logged as a
'thread not found' debug warning. Skip the call entirely when the
metadata indicates a DM topic reply-fallback lane; the user-visible
behavior is unchanged (no typing indicator either way for these
lanes), but the logs stay clean.

Identified during salvage review of NousResearch#22053.
rmulligan pushed a commit to rmulligan/hermes-agent that referenced this pull request May 11, 2026
…alvage (NousResearch#22409)

Adds jhin.lee@unity3d.com → leehack so contributor_audit.py strict
mode passes when the salvage of NousResearch#22053 (telegram DM topic reply
fallback) lands on main.
rmulligan pushed a commit to rmulligan/hermes-agent that referenced this pull request May 11, 2026
The send path uses Hermes' reply-anchor fallback for DM topic lanes
(message_thread_id + reply_to_message_id), but send_chat_action only
accepts message_thread_id — Telegram's Bot API 10.0 rejects it for
these lanes. Without this short-circuit, every typing tick (~every 2s
during agent runs) makes a doomed API call that gets logged as a
'thread not found' debug warning. Skip the call entirely when the
metadata indicates a DM topic reply-fallback lane; the user-visible
behavior is unchanged (no typing indicator either way for these
lanes), but the logs stay clean.

Identified during salvage review of NousResearch#22053.
JinyuID pushed a commit to JinyuID/hermes-agent that referenced this pull request May 11, 2026
…alvage (NousResearch#22409)

Adds jhin.lee@unity3d.com → leehack so contributor_audit.py strict
mode passes when the salvage of NousResearch#22053 (telegram DM topic reply
fallback) lands on main.
JinyuID pushed a commit to JinyuID/hermes-agent that referenced this pull request May 11, 2026
The send path uses Hermes' reply-anchor fallback for DM topic lanes
(message_thread_id + reply_to_message_id), but send_chat_action only
accepts message_thread_id — Telegram's Bot API 10.0 rejects it for
these lanes. Without this short-circuit, every typing tick (~every 2s
during agent runs) makes a doomed API call that gets logged as a
'thread not found' debug warning. Skip the call entirely when the
metadata indicates a DM topic reply-fallback lane; the user-visible
behavior is unchanged (no typing indicator either way for these
lanes), but the logs stay clean.

Identified during salvage review of NousResearch#22053.
jsboige pushed a commit to jsboige/hermes-agent that referenced this pull request May 14, 2026
…alvage (NousResearch#22409)

Adds jhin.lee@unity3d.com → leehack so contributor_audit.py strict
mode passes when the salvage of NousResearch#22053 (telegram DM topic reply
fallback) lands on main.
jsboige pushed a commit to jsboige/hermes-agent that referenced this pull request May 14, 2026
The send path uses Hermes' reply-anchor fallback for DM topic lanes
(message_thread_id + reply_to_message_id), but send_chat_action only
accepts message_thread_id — Telegram's Bot API 10.0 rejects it for
these lanes. Without this short-circuit, every typing tick (~every 2s
during agent runs) makes a doomed API call that gets logged as a
'thread not found' debug warning. Skip the call entirely when the
metadata indicates a DM topic reply-fallback lane; the user-visible
behavior is unchanged (no typing indicator either way for these
lanes), but the logs stay clean.

Identified during salvage review of NousResearch#22053.
Egavasyug pushed a commit to Egavasyug/hermes-agent that referenced this pull request May 25, 2026
…alvage (NousResearch#22409)

Adds jhin.lee@unity3d.com → leehack so contributor_audit.py strict
mode passes when the salvage of NousResearch#22053 (telegram DM topic reply
fallback) lands on main.
Egavasyug pushed a commit to Egavasyug/hermes-agent that referenced this pull request May 25, 2026
The send path uses Hermes' reply-anchor fallback for DM topic lanes
(message_thread_id + reply_to_message_id), but send_chat_action only
accepts message_thread_id — Telegram's Bot API 10.0 rejects it for
these lanes. Without this short-circuit, every typing tick (~every 2s
during agent runs) makes a doomed API call that gets logged as a
'thread not found' debug warning. Skip the call entirely when the
metadata indicates a DM topic reply-fallback lane; the user-visible
behavior is unchanged (no typing indicator either way for these
lanes), but the logs stay clean.

Identified during salvage review of NousResearch#22053.
gweeteve pushed a commit to gweeteve/hermes-agent that referenced this pull request Jun 2, 2026
…alvage (NousResearch#22409)

Adds jhin.lee@unity3d.com → leehack so contributor_audit.py strict
mode passes when the salvage of NousResearch#22053 (telegram DM topic reply
fallback) lands on main.
gweeteve pushed a commit to gweeteve/hermes-agent that referenced this pull request Jun 2, 2026
The send path uses Hermes' reply-anchor fallback for DM topic lanes
(message_thread_id + reply_to_message_id), but send_chat_action only
accepts message_thread_id — Telegram's Bot API 10.0 rejects it for
these lanes. Without this short-circuit, every typing tick (~every 2s
during agent runs) makes a doomed API call that gets logged as a
'thread not found' debug warning. Skip the call entirely when the
metadata indicates a DM topic reply-fallback lane; the user-visible
behavior is unchanged (no typing indicator either way for these
lanes), but the logs stay clean.

Identified during salvage review of NousResearch#22053.
Egavasyug pushed a commit to Egavasyug/hermes-agent that referenced this pull request Jun 10, 2026
…alvage (NousResearch#22409)

Adds jhin.lee@unity3d.com → leehack so contributor_audit.py strict
mode passes when the salvage of NousResearch#22053 (telegram DM topic reply
fallback) lands on main.
Egavasyug pushed a commit to Egavasyug/hermes-agent that referenced this pull request Jun 10, 2026
The send path uses Hermes' reply-anchor fallback for DM topic lanes
(message_thread_id + reply_to_message_id), but send_chat_action only
accepts message_thread_id — Telegram's Bot API 10.0 rejects it for
these lanes. Without this short-circuit, every typing tick (~every 2s
during agent runs) makes a doomed API call that gets logged as a
'thread not found' debug warning. Skip the call entirely when the
metadata indicates a DM topic reply-fallback lane; the user-visible
behavior is unchanged (no typing indicator either way for these
lanes), but the logs stay clean.

Identified during salvage review of NousResearch#22053.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/gateway Gateway runner, session dispatch, delivery P2 Medium — degraded but workaround exists platform/telegram Telegram bot adapter type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Telegram Bot API 10 breaks private chat topic replies; Hermes falls back to All Messages

4 participants