hermes - 💡(How to fix) Fix [Bug]: Telegram native partial quotes are expanded to the full replied-to message [4 pull requests]

Official PRs (…)
ON THIS PAGE

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…

Root Cause

As a result, the model-visible context expands the user's selected Telegram quote into the full previous message. This can cause the agent to act on the wrong item because the injected reply context contains unrelated actionable-looking text.

Fix Action

Fixed

Code Example

[Replying to: "<entire previous Hermes message>"]

<user's actual message>

---

# Extract reply context if this message is a reply
reply_to_id = None
reply_to_text = None
if message.reply_to_message:
    reply_to_id = str(message.reply_to_message.message_id)
    reply_to_text = message.reply_to_message.text or message.reply_to_message.caption or None

---

[Replying to: "<only the selected quoted text>"]

mark this one as done

---

reply_to_text = message.reply_to_message.text or message.reply_to_message.caption or None

---

if message.reply_to_message:
    reply_to_id = str(message.reply_to_message.message_id)
    reply_to_text = message.reply_to_message.text or message.reply_to_message.caption or None

---

reply_to_id = None
reply_to_text = None
if message.reply_to_message:
    reply_to_id = str(message.reply_to_message.message_id)
    quote = getattr(message, "quote", None)
    if quote and getattr(quote, "text", None):
        reply_to_text = quote.text
        # optionally preserve quote.position/entities/is_manual in metadata
    else:
        reply_to_text = message.reply_to_message.text or message.reply_to_message.caption or None
RAW_BUFFERClick to expand / collapse

Bug Description

When a Telegram user replies using Telegram's native quote feature to select only part of a previous message, Hermes' Telegram adapter appears to ignore the selected quote and inject the entire replied-to message as reply_to_text.

This can materially change the user's intent. In a real Telegram DM, the user selected/quoted one specific part of a prior Hermes message, but the agent received a model-visible prefix containing the entire previous message:

[Replying to: "<entire previous Hermes message>"]

<user's actual message>

The root issue appears to be in the Telegram adapter's reply-context extraction. The current implementation uses message.reply_to_message.text / .caption, which represents the full replied-to message, even though python-telegram-bot exposes message.quote / TextQuote for the selected quoted portion.

Relevant code observed in gateway/platforms/telegram.py:

# Extract reply context if this message is a reply
reply_to_id = None
reply_to_text = None
if message.reply_to_message:
    reply_to_id = str(message.reply_to_message.message_id)
    reply_to_text = message.reply_to_message.text or message.reply_to_message.caption or None

python-telegram-bot 22.7 documents Message.quote / TextQuote as: "For replies that quote part of the original message, the quoted part of the message."

Steps to Reproduce

  1. Configure Hermes with the Telegram gateway.
  2. Have Hermes send a multi-section / multi-item message in Telegram. For example, a briefing with multiple actionable-looking items.
  3. In Telegram, select a specific substring/section of that message and use Telegram's native quote/reply feature.
  4. Send an ambiguous follow-up such as "mark this one as done".
  5. Inspect the resulting Hermes session content / agent input.

Expected Behavior

When Telegram provides a native partial quote, Hermes should use the selected quoted text as the reply context, likely from message.quote.text, while still preserving the replied-to message id.

For example:

[Replying to: "<only the selected quoted text>"]

mark this one as done

Possible expected implementation behavior:

  • If message.quote exists, use message.quote.text as reply_to_text.
  • Preserve message.reply_to_message.message_id as reply_to_message_id.
  • Optionally preserve quote metadata such as position, entities, and is_manual in event metadata for future use.
  • Fall back to message.reply_to_message.text / .caption only when there is no native quote object.

Actual Behavior

Hermes uses the entire replied-to message as reply_to_text:

reply_to_text = message.reply_to_message.text or message.reply_to_message.caption or None

As a result, the model-visible context expands the user's selected Telegram quote into the full previous message. This can cause the agent to act on the wrong item because the injected reply context contains unrelated actionable-looking text.

Affected Component

  • Gateway
  • Telegram platform adapter
  • Reply/quote context handling

Platform

  • Platform: Telegram DM
  • Hermes version observed locally: 0.13.0
  • Local checkout commit observed: 99f27123a
  • python-telegram-bot: 22.7

Related Context

This is related to the reply-context behavior added in #1594, but the issue here is more specific: the Telegram adapter should distinguish native partial quote text (message.quote) from the full replied-to message (message.reply_to_message).

Proposed Root Cause

The adapter treats any Telegram reply as a reply to the full original message and does not check message.quote:

if message.reply_to_message:
    reply_to_id = str(message.reply_to_message.message_id)
    reply_to_text = message.reply_to_message.text or message.reply_to_message.caption or None

Because Telegram's native quote feature is represented separately as message.quote, this discards the user's actual quote selection.

Suggested Fix

Prefer native quote text when present:

reply_to_id = None
reply_to_text = None
if message.reply_to_message:
    reply_to_id = str(message.reply_to_message.message_id)
    quote = getattr(message, "quote", None)
    if quote and getattr(quote, "text", None):
        reply_to_text = quote.text
        # optionally preserve quote.position/entities/is_manual in metadata
    else:
        reply_to_text = message.reply_to_message.text or message.reply_to_message.caption or None

This should be covered by a regression test that builds a Telegram Message with both reply_to_message.text and quote.text, then verifies the resulting MessageEvent.reply_to_text uses the quote text, not the full replied-to message.

Screenshots / Debug Report

I can provide a screenshot of the Telegram native quote UI if helpful, but the issue appears reproducible from the Telegram update shape and adapter code above.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING

hermes - 💡(How to fix) Fix [Bug]: Telegram native partial quotes are expanded to the full replied-to message [4 pull requests]