Skip to content

fix(telegram): format final replies in bound topics - #43463

Closed
GodsBoy wants to merge 5 commits into
NousResearch:mainfrom
GodsBoy:fix/telegram-topic-final-markdown
Closed

fix(telegram): format final replies in bound topics#43463
GodsBoy wants to merge 5 commits into
NousResearch:mainfrom
GodsBoy:fix/telegram-topic-final-markdown

Conversation

@GodsBoy

@GodsBoy GodsBoy commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

What does this PR do?

Final assistant replies in Telegram bound/forum topics could arrive showing raw Markdown markers literally (**bold**, backticks, triple backtick fences as plain text), while the exact same content pasted manually into the same topic rendered fine. The same turns sometimes co-occurred with the overflow clipping tracked in #42765.

Two delivery paths caused it, and both are fixed:

  1. _edit_overflow_split chunked the raw text at the full 4096 UTF-16 limit and ran format_message per chunk afterwards. MarkdownV2 escaping inflates real-world text by roughly 4 to 8 percent, so every formatted chunk exceeded the limit, Telegram rejected each attempt with MESSAGE_TOO_LONG, and the fallbacks delivered the raw chunk with no parse mode. send() formats first and then chunks, so normal sends never hit this. The finalize split now mirrors send(): format the whole reply once, chunk the formatted text, re-escape the chunk indicators, and degrade per chunk to clean stripped text (never the raw chunk). edit_message also pre-flights finalize edits against the formatted length, so content in the inflation window (raw under 4096, formatted over it) splits properly instead of losing its formatting to the in-place plain fallback.
  2. The gateway cancels the stream consumer about 5 seconds after the stream finishes, and the cancellation handler re-delivered the accumulated reply with finalize=False, which is plain by design on Telegram. The whole final reply stayed a raw streaming preview while the success flags suppressed the gateway's formatted re-send. The best-effort delivery now uses finalize=True, is_turn_final=False (the latter keeps the fresh-final path from claiming the flags, per the Discord: tool-using responses (api_calls≥2) silently dropped — no Sending response log after response ready #29346 semantics).

While covering the new pre-flight, review surfaced that a got_done finalize edit which split across continuations was followed by the redundant requires-finalize edit, which re-split the full text into the adopted continuation and duplicated chunks on screen. The redundant edit is now skipped only when the first one split-and-delivered, so the explicit finalize contract for unchanged text (#25010) is untouched.

Related Issue

Fixes #43441

Related: #42766 and #42765 (overflow clipping, a distinct problem in the same continuation loop; see note below), #42443 (MarkdownV2 escaping inside code blocks), #42421 (same bug class for progress message edits, merged).

Note for #42766: its consumer-side recovery matches delivered chunks against the raw accumulated text. With this change, finalize chunks are formatted before sizing, so whichever PR merges second needs to rework that prefix match (raw content offsets, or matching against formatted text). SendResult semantics are unchanged here.

Type of Change

  • 🐛 Bug fix (non-breaking change that fixes an issue)
  • ✨ New feature (non-breaking change that adds functionality)
  • 🔒 Security fix
  • 📝 Documentation update
  • ✅ Tests (adding or improving test coverage)
  • ♻️ Refactor (no behavior change)
  • 🎯 New skill (bundled or hub)

Changes Made

  • gateway/platforms/telegram.py: finalize overflow splits format first and chunk the formatted text; shared _escape_chunk_indicator helper used by send() and the split path; all split fallbacks (first chunk edit, continuations, reply-not-found retry) degrade via _strip_mdv2 instead of sending the raw chunk; edit_message finalize pre-flight sizes against the formatted length; non-finalize streaming previews are byte-for-byte unchanged.
  • gateway/stream_consumer.py: cancellation best-effort delivery uses finalize=True, is_turn_final=False; a finalize edit that adopted continuation messages is not re-finalized (prevents chunk duplication).
  • tests/gateway/test_telegram_format.py: 13 new tests (formatted split delivery, topic metadata preservation, escaped chunk indicators, stripped fallbacks, inflation window routing, unchanged plain previews, _escape_chunk_indicator unit tests).
  • tests/gateway/test_stream_consumer_fresh_final.py: 6 new tests (cancel path finalize semantics incl. fresh-final-enabled config and failure path, split-not-refinalized regression pair).
  • .github/pr-screenshots/telegram-bound-topic-markdown/: evidence screenshots.

How to Test

  1. scripts/run_tests.sh tests/gateway/test_telegram_format.py tests/gateway/test_stream_consumer_fresh_final.py tests/gateway/test_stream_consumer.py tests/gateway/test_stream_consumer_thread_routing.py tests/gateway/test_telegram_send_draft_format.py
    Output: 5 files, 242 tests passed, 0 failed.
  2. The new regression tests were written first and proven to fail on the unfixed code: the overflow formatting tests fail with raw markers and missing parse mode, the cancel path tests fail with finalize=False, and the re-finalize test fails with two finalize edits.
  3. Live repro: in a bound forum topic, ask the bot for a reply well over 4096 characters containing bold, inline code, fenced code blocks, and bullet lists. Before: the reply renders with literal markers. After: every chunk renders formatted and stays in the topic thread.
  4. Full tests/gateway/ run is green on this host except one pre-existing test_agent_cache mtime memoization failure, reproduced identically on a clean origin/main worktree (environment dependent, unrelated to this diff).

Checklist

Code

  • I've read the Contributing Guide
  • My commit messages follow Conventional Commits (fix(scope):, feat(scope):, etc.)
  • I searched for existing PRs to make sure this isn't a duplicate
  • My PR contains only changes related to this fix/feature (no unrelated commits)
  • I've run the test suite and all tests pass (via scripts/run_tests.sh, see How to Test for the one pre-existing host-specific failure also present on main)
  • I've added tests for my changes (required for bug fixes, strongly encouraged for features)
  • I've tested on my platform: Ubuntu Linux (server)

Documentation & Housekeeping

  • I've updated relevant documentation (docstrings cover the new sizing behavior), or N/A
  • I've updated cli-config.yaml.example if I added/changed config keys, or N/A (no config changes)
  • I've updated CONTRIBUTING.md or AGENTS.md if I changed architecture or workflows, or N/A (no architecture change)
  • I've considered cross-platform impact (Windows, macOS) per the compatibility guide, or N/A (pure Python gateway logic)
  • I've updated tool descriptions/schemas if I changed tool behavior, or N/A (no tool changes)

Screenshots / Logs

Bot-sent final reply in a bound topic before the fix, raw markers visible:

bot-sent raw markdown

The same content pasted manually by the user into the same topic, rendering correctly (what bot replies look like after the fix):

user-pasted markdown renders

Residual Review Findings

Known follow-ups noted during review, intentionally out of scope to keep this diff minimal and avoid colliding with #42766:

  • P2, gateway/stream_consumer.py:659: a second cancellation arriving during the best-effort finalize escapes the inner exception guard, so the success flags stay unset and the gateway fallback can duplicate a delivery that actually landed. Pre-existing handler structure, slightly widened by the finalize path doing more work; a fix needs a design decision (shielding with a small budget, or catching BaseException).
  • P2, gateway/platforms/telegram.py:2347: chunking already formatted text can split a MarkdownV2 entity (links especially) across a chunk boundary, degrading the affected chunks to clean stripped plain text. Same exposure exists in send() today; the proper fix is an entity balance guard in truncate_message next to its existing backtick parity guard.
  • P3, gateway/platforms/telegram.py:2455: the reply-not-found retry sends stripped plain text even though the failure was the reply anchor, not parsing; it could retry the formatted chunk first.
  • P3, gateway/stream_consumer.py:594: a got_done delivery that goes through a first send (no prior streaming message) in the inflation band can still double-split, because send() does not report continuation ids; needs an additive SendResult change.

Post-Deploy Monitoring & Validation

  • Log searches: Overflow split, MarkdownV2 edit failed, falling back to plain text, Overflow split: MarkdownV2 first-chunk edit failed, Overflow split: stopped at.
  • Healthy signal: long final replies in topics render formatted across all chunks; the fallback warnings above stay rare.
  • Failure signal: user reports of raw markers, duplicated chunks, or a spike in the fallback warnings; the rollback trigger is any reproducible duplication report.
  • Rollback: revert this PR's commits; behavior returns to the previous raw rendering with no data or state impact.
  • Validation window and owner: a few days of normal topic traffic, watched by the PR author.

@alt-glitch alt-glitch added type/bug Something isn't working P2 Medium — degraded but workaround exists comp/gateway Gateway runner, session dispatch, delivery platform/telegram Telegram bot adapter labels Jun 10, 2026
@alt-glitch

Copy link
Copy Markdown
Collaborator

Related: competing fix for #43441 alongside #43458 and #43470. This PR has the broadest scope — format-before-chunk in _edit_overflow_split plus an edit_message pre-flight against formatted length AND a stream-consumer cancellation fix (re-delivering with finalize=False). #43458/#43470 only fix the chunk-ordering. Maintainer should choose between the focused and the comprehensive fix.

@liuhao1024

Copy link
Copy Markdown
Contributor

Reviewed the full diff across telegram.py, stream_consumer.py, and both test files. This is a well-structured multi-part fix:

  1. Format-then-chunk for finalize edits_edit_overflow_split now pre-formats content to MarkdownV2 before chunking on the finalize path. Previously it chunked raw text at 4096 then formatted each chunk, inflating every chunk past the limit and forcing plain-text fallbacks.

  2. _escape_chunk_indicator extraction — the inline regex in send() is now a reusable function, applied consistently in both send() and _edit_overflow_split.

  3. _last_edit_overflowed guard — prevents the redundant post-split finalize edit from re-splitting into the adopted continuation and duplicating chunks on screen.

  4. Cancel-path finalize — the CancelledError handler now passes finalize=True so REQUIRES_EDIT_FINALIZE platforms (Telegram) apply final formatting instead of leaving raw streaming markers.

The test suite is comprehensive: 8 new tests covering cancel-path finalization, overflow split formatting, chunk indicator escaping, inflation window routing, and the non-refinalize guard. No issues found.

@GodsBoy

GodsBoy commented Jun 10, 2026

Copy link
Copy Markdown
Contributor Author

Consolidating the comparison since the triager flagged #43458 and #43470 as competing fixes. All three PRs agree on the core chunk-ordering change in _edit_overflow_split (format first, then chunk, matching send()); the convergence is good evidence that piece is right. The reason #43441 needs more than that piece:

  1. The cancellation path still delivers raw markers. The gateway cancels the stream consumer about 5 seconds after finish(), and the CancelledError handler in gateway/stream_consumer.py re-delivers the whole accumulated reply with finalize=False (plain by design on Telegram), then sets the success flags that suppress the gateway's formatted re-send. Any long finalize that is still in flight at the 5 second mark, common in busy topics where flood control slows edits, renders the entire reply raw even with the split fixed. This is the co-occurring path behind the truncation reports in [Bug]: Telegram topic streamed reply can stop after first overflow chunk #42765.
  2. The inflation window never reaches _edit_overflow_split. edit_message's pre-flight measures raw length, so content under 4096 raw whose formatted form exceeds it takes the in-place fallback and silently loses formatting (backticks and fences stay literal). Fixing only the split leaves that band broken; this PR sizes finalize pre-flights against the formatted text.
  3. Re-finalize duplication. After a split, the requires-finalize second edit re-submits the full text against the adopted continuation and splits again, duplicating chunk 1 on screen. Present on main; becomes more visible once splits start succeeding formatted. Guarded here with a regression pair that keeps the Telegram streaming can leave incomplete partial message while final send is suppressed #25010 contract intact.

On the narrow PRs themselves: #43458's tests cover the split sizing and we converge on mechanics. #43470 ships no tests, and its indicator re-escape hardcodes the total (r" \\(\1/2\\)"), so a 3-chunk reply renders (3/2) on every chunk.

This PR carries 19 regression tests (each demonstrated to fail before its fix), the before/after screenshots, and the documented interaction with #42766's recovery work. Happy to rebase or split if smaller increments are preferred.

@austinpickett austinpickett left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approve: fix(telegram): format final replies in bound topics

Canonical fix — subsumes #43458. This PR is the comprehensive solution and should merge; #43458 covers only a subset of this diff.

Core correctness ✅

The bug: _edit_overflow_split chunked raw text at the 4096 UTF-16 limit, then formatted each chunk individually. MarkdownV2 escaping inflates content by ~4–8%, so a chunk that was at 4096 raw bytes would exceed 4096 after formatting. Telegram rejected it; the fallback sent raw Markdown markers to the user.

The fix correctly mirrors send(): format the entire content once, then chunk the already-formatted text, so every chunk is guaranteed within the limit.

What's complete beyond #43458

  1. Inflation-window fix in edit_message — raw text under 4096 whose formatted version exceeds it now correctly routes to the split path instead of falling through to the in-place plain-text fallback (which would show raw Markdown).

  2. _escape_chunk_indicator — the inline regex that escapes the (1/2) suffix is extracted as a named helper (also used in send()), eliminating duplicate logic.

  3. stream_consumer._last_edit_overflowed — prevents the stream consumer from issuing a redundant second finalize after an overflow split that already carried finalize=True, which would overflow-split again into the adopted continuation and duplicate chunks on screen.

  4. Cancellation path_send_or_edit in the CancelledError handler now passes finalize=True, is_turn_final=False, so Telegram applies final formatting on early cancellation instead of leaving a raw streaming preview on screen.

  5. _strip_mdv2 on all plain fallbacks — all three use-markdown=False branches (first_chunk, continuation, no-anchor retry) fall back to clean stripped text rather than the raw pre-formatted chunk.

Edge cases reviewed ✅

  • if not chunks:#43463 changed the defensive fallback from len(chunks) <= 1 to not chunks. Correct: truncate_message always produces ≥ 1 chunk from non-empty input, so the old <= 1 guard was accidentally suppressing valid single-chunk splits (when the finalize=True format already produced exactly one chunk). The new guard is tighter.
  • Double-format on inflation-window route — the comment correctly documents that formatted precomputed in edit_message is NOT forwarded to _edit_overflow_split; the split function does its own pass. Acceptable: this rare path formats twice; the result is identical.
  • Non-finalize path — streaming previews correctly stay plain (no parse_mode), confirmed by test_non_finalize_overflow_keeps_plain_chunks.

Test coverage ✅

8 new tests across 4 files: format-before-chunk assertion, per-chunk size enforcement, topic/thread-id preservation, chunk-indicator escaping, MarkdownV2 fallback to stripped text (continuation, first-chunk edit, no-anchor retry), inflation-window routing, non-finalize independence.

Minor nit (non-blocking)

The log warning added on first-chunk MarkdownV2 failure uses self.name — fine, just confirm TelegramAdapter always has a non-None name by the time _edit_overflow_split is called (it does, as it's set in __init__). No issue.

Approved. ✅

teknium1 added a commit that referenced this pull request Jun 10, 2026
…ly delete guard

Follow-ups on top of the two salvaged GodsBoy commits, all live-validated
against the real Telegram Bot API:

- _edit_overflow_split finalize fallbacks degrade to _strip_mdv2() clean
  text instead of putting raw **markdown** markers on screen (salvaged
  from PR #43463 minus its format-first sizing — live probes show
  Telegram's 4096 limit counts PARSED text, so MarkdownV2 escape
  inflation cannot cause MESSAGE_TOO_LONG and sizing against formatted
  wire length only causes premature splits and fragment messages).
- Skip the redundant requires-finalize edit after a got_done edit that
  split-and-delivered (salvaged from PR #43463): re-finalizing re-splits
  the full text into the adopted continuation and duplicates chunks.
- _send_fallback_final only deletes the stale partial message when the
  fallback re-sent the COMPLETE final text. When the prefix dedup sent
  only the missing tail, the partial IS the head of the answer; deleting
  it left users with only the second half of long responses (live-
  reproduced: flood-control during a long stream -> head deleted,
  ratio 0.54 of content visible). This is the third bug behind the
  'Telegram cut messages' reports and was present on main and both PRs.
@teknium1

Copy link
Copy Markdown
Contributor

Merged in part via PR #43761 — your cancel-path commit was cherry-picked onto current main with your authorship preserved in git log (da81851), and your stripped-text fallbacks + re-finalize skip landed in the follow-up commit with credit.

What shipped from this PR:

  • Cancel-path best-effort delivery with finalize=True — live-reproduced on main (whole reply frozen as raw **markdown** when the gateway cancels the consumer) and live-confirmed fixed. Your test classes went in as-is.
  • _strip_mdv2() degradation in all _edit_overflow_split fallbacks — raw markers never hit the screen.
  • The re-finalize skip after a split edit (_last_edit_overflowed), preventing duplicated chunks.

What was dropped, with evidence: the format-first sizing (format_message before truncate_message in the finalize split, and the formatted-length pre-flight in edit_message). We probed the live Bot API directly: a MarkdownV2 payload with wire length 6120 but parsed length 4080 is accepted; parsed length 4120 is rejected. Telegram's 4096 limit counts the text after entities parsing, and parsing only removes characters — so raw text under 4096 can never produce MESSAGE_TOO_LONG, and the escape-inflation cascade described in the PR body can't occur. In live testing the formatted-length sizing over-estimated and caused premature splits with ~120-char fragment messages on content that fits cleanly without it. The raw-marker symptom in your screenshot is fully explained by the cancel path and parse-failure fallbacks, both now fixed.

Your residual-findings list (entity-boundary splits in truncate_message, the double-cancellation guard) is solid follow-up material — the entity balance guard next to the existing backtick parity check would be a welcome separate PR.

Thanks for the rigorous work — the TDD discipline and the honest residual-findings section made this salvage straightforward.

alt-glitch pushed a commit that referenced this pull request Jun 14, 2026
…ly delete guard

Follow-ups on top of the two salvaged GodsBoy commits, all live-validated
against the real Telegram Bot API:

- _edit_overflow_split finalize fallbacks degrade to _strip_mdv2() clean
  text instead of putting raw **markdown** markers on screen (salvaged
  from PR #43463 minus its format-first sizing — live probes show
  Telegram's 4096 limit counts PARSED text, so MarkdownV2 escape
  inflation cannot cause MESSAGE_TOO_LONG and sizing against formatted
  wire length only causes premature splits and fragment messages).
- Skip the redundant requires-finalize edit after a got_done edit that
  split-and-delivered (salvaged from PR #43463): re-finalizing re-splits
  the full text into the adopted continuation and duplicates chunks.
- _send_fallback_final only deletes the stale partial message when the
  fallback re-sent the COMPLETE final text. When the prefix dedup sent
  only the missing tail, the partial IS the head of the answer; deleting
  it left users with only the second half of long responses (live-
  reproduced: flood-control during a long stream -> head deleted,
  ratio 0.54 of content visible). This is the third bug behind the
  'Telegram cut messages' reports and was present on main and both PRs.
fatalacris pushed a commit to fatalacris/hermes-agent that referenced this pull request Jun 16, 2026
…ly delete guard

Follow-ups on top of the two salvaged GodsBoy commits, all live-validated
against the real Telegram Bot API:

- _edit_overflow_split finalize fallbacks degrade to _strip_mdv2() clean
  text instead of putting raw **markdown** markers on screen (salvaged
  from PR NousResearch#43463 minus its format-first sizing — live probes show
  Telegram's 4096 limit counts PARSED text, so MarkdownV2 escape
  inflation cannot cause MESSAGE_TOO_LONG and sizing against formatted
  wire length only causes premature splits and fragment messages).
- Skip the redundant requires-finalize edit after a got_done edit that
  split-and-delivered (salvaged from PR NousResearch#43463): re-finalizing re-splits
  the full text into the adopted continuation and duplicates chunks.
- _send_fallback_final only deletes the stale partial message when the
  fallback re-sent the COMPLETE final text. When the prefix dedup sent
  only the missing tail, the partial IS the head of the answer; deleting
  it left users with only the second half of long responses (live-
  reproduced: flood-control during a long stream -> head deleted,
  ratio 0.54 of content visible). This is the third bug behind the
  'Telegram cut messages' reports and was present on main and both PRs.

(cherry picked from commit 3b4c715)
davidgut1982 pushed a commit to davidgut1982/hermes-agent that referenced this pull request Jun 17, 2026
…ly delete guard

Follow-ups on top of the two salvaged GodsBoy commits, all live-validated
against the real Telegram Bot API:

- _edit_overflow_split finalize fallbacks degrade to _strip_mdv2() clean
  text instead of putting raw **markdown** markers on screen (salvaged
  from PR NousResearch#43463 minus its format-first sizing — live probes show
  Telegram's 4096 limit counts PARSED text, so MarkdownV2 escape
  inflation cannot cause MESSAGE_TOO_LONG and sizing against formatted
  wire length only causes premature splits and fragment messages).
- Skip the redundant requires-finalize edit after a got_done edit that
  split-and-delivered (salvaged from PR NousResearch#43463): re-finalizing re-splits
  the full text into the adopted continuation and duplicates chunks.
- _send_fallback_final only deletes the stale partial message when the
  fallback re-sent the COMPLETE final text. When the prefix dedup sent
  only the missing tail, the partial IS the head of the answer; deleting
  it left users with only the second half of long responses (live-
  reproduced: flood-control during a long stream -> head deleted,
  ratio 0.54 of content visible). This is the third bug behind the
  'Telegram cut messages' reports and was present on main and both PRs.
T02200059 pushed a commit to T02200059/hermes-agent that referenced this pull request Jun 18, 2026
…ly delete guard

Follow-ups on top of the two salvaged GodsBoy commits, all live-validated
against the real Telegram Bot API:

- _edit_overflow_split finalize fallbacks degrade to _strip_mdv2() clean
  text instead of putting raw **markdown** markers on screen (salvaged
  from PR NousResearch#43463 minus its format-first sizing — live probes show
  Telegram's 4096 limit counts PARSED text, so MarkdownV2 escape
  inflation cannot cause MESSAGE_TOO_LONG and sizing against formatted
  wire length only causes premature splits and fragment messages).
- Skip the redundant requires-finalize edit after a got_done edit that
  split-and-delivered (salvaged from PR NousResearch#43463): re-finalizing re-splits
  the full text into the adopted continuation and duplicates chunks.
- _send_fallback_final only deletes the stale partial message when the
  fallback re-sent the COMPLETE final text. When the prefix dedup sent
  only the missing tail, the partial IS the head of the answer; deleting
  it left users with only the second half of long responses (live-
  reproduced: flood-control during a long stream -> head deleted,
  ratio 0.54 of content visible). This is the third bug behind the
  'Telegram cut messages' reports and was present on main and both PRs.
waefrebeorn pushed a commit to waefrebeorn/slermes that referenced this pull request Jul 2, 2026
…ly delete guard

Follow-ups on top of the two salvaged GodsBoy commits, all live-validated
against the real Telegram Bot API:

- _edit_overflow_split finalize fallbacks degrade to _strip_mdv2() clean
  text instead of putting raw **markdown** markers on screen (salvaged
  from PR NousResearch#43463 minus its format-first sizing — live probes show
  Telegram's 4096 limit counts PARSED text, so MarkdownV2 escape
  inflation cannot cause MESSAGE_TOO_LONG and sizing against formatted
  wire length only causes premature splits and fragment messages).
- Skip the redundant requires-finalize edit after a got_done edit that
  split-and-delivered (salvaged from PR NousResearch#43463): re-finalizing re-splits
  the full text into the adopted continuation and duplicates chunks.
- _send_fallback_final only deletes the stale partial message when the
  fallback re-sent the COMPLETE final text. When the prefix dedup sent
  only the missing tail, the partial IS the head of the answer; deleting
  it left users with only the second half of long responses (live-
  reproduced: flood-control during a long stream -> head deleted,
  ratio 0.54 of content visible). This is the third bug behind the
  'Telegram cut messages' reports and was present on main and both PRs.
santhreal pushed a commit to santhreal/hermes-agent that referenced this pull request Jul 13, 2026
…ly delete guard

Follow-ups on top of the two salvaged GodsBoy commits, all live-validated
against the real Telegram Bot API:

- _edit_overflow_split finalize fallbacks degrade to _strip_mdv2() clean
  text instead of putting raw **markdown** markers on screen (salvaged
  from PR NousResearch#43463 minus its format-first sizing — live probes show
  Telegram's 4096 limit counts PARSED text, so MarkdownV2 escape
  inflation cannot cause MESSAGE_TOO_LONG and sizing against formatted
  wire length only causes premature splits and fragment messages).
- Skip the redundant requires-finalize edit after a got_done edit that
  split-and-delivered (salvaged from PR NousResearch#43463): re-finalizing re-splits
  the full text into the adopted continuation and duplicates chunks.
- _send_fallback_final only deletes the stale partial message when the
  fallback re-sent the COMPLETE final text. When the prefix dedup sent
  only the missing tail, the partial IS the head of the answer; deleting
  it left users with only the second half of long responses (live-
  reproduced: flood-control during a long stream -> head deleted,
  ratio 0.54 of content visible). This is the third bug behind the
  'Telegram cut messages' reports and was present on main and both PRs.
donbowman pushed a commit to donbowman/hermes-agent that referenced this pull request Jul 13, 2026
…ly delete guard

Follow-ups on top of the two salvaged GodsBoy commits, all live-validated
against the real Telegram Bot API:

- _edit_overflow_split finalize fallbacks degrade to _strip_mdv2() clean
  text instead of putting raw **markdown** markers on screen (salvaged
  from PR NousResearch#43463 minus its format-first sizing — live probes show
  Telegram's 4096 limit counts PARSED text, so MarkdownV2 escape
  inflation cannot cause MESSAGE_TOO_LONG and sizing against formatted
  wire length only causes premature splits and fragment messages).
- Skip the redundant requires-finalize edit after a got_done edit that
  split-and-delivered (salvaged from PR NousResearch#43463): re-finalizing re-splits
  the full text into the adopted continuation and duplicates chunks.
- _send_fallback_final only deletes the stale partial message when the
  fallback re-sent the COMPLETE final text. When the prefix dedup sent
  only the missing tail, the partial IS the head of the answer; deleting
  it left users with only the second half of long responses (live-
  reproduced: flood-control during a long stream -> head deleted,
  ratio 0.54 of content visible). This is the third bug behind the
  'Telegram cut messages' reports and was present on main and both PRs.
Gravezzz pushed a commit to Gravezzz/hermes-agent that referenced this pull request Jul 21, 2026
…ly delete guard

Follow-ups on top of the two salvaged GodsBoy commits, all live-validated
against the real Telegram Bot API:

- _edit_overflow_split finalize fallbacks degrade to _strip_mdv2() clean
  text instead of putting raw **markdown** markers on screen (salvaged
  from PR NousResearch#43463 minus its format-first sizing — live probes show
  Telegram's 4096 limit counts PARSED text, so MarkdownV2 escape
  inflation cannot cause MESSAGE_TOO_LONG and sizing against formatted
  wire length only causes premature splits and fragment messages).
- Skip the redundant requires-finalize edit after a got_done edit that
  split-and-delivered (salvaged from PR NousResearch#43463): re-finalizing re-splits
  the full text into the adopted continuation and duplicates chunks.
- _send_fallback_final only deletes the stale partial message when the
  fallback re-sent the COMPLETE final text. When the prefix dedup sent
  only the missing tail, the partial IS the head of the answer; deleting
  it left users with only the second half of long responses (live-
  reproduced: flood-control during a long stream -> head deleted,
  ratio 0.54 of content visible). This is the third bug behind the
  'Telegram cut messages' reports and was present on main and both PRs.
leewenjie pushed a commit to leewenjie/hermes-agent that referenced this pull request Aug 7, 2026
…ly delete guard

Follow-ups on top of the two salvaged GodsBoy commits, all live-validated
against the real Telegram Bot API:

- _edit_overflow_split finalize fallbacks degrade to _strip_mdv2() clean
  text instead of putting raw **markdown** markers on screen (salvaged
  from PR NousResearch#43463 minus its format-first sizing — live probes show
  Telegram's 4096 limit counts PARSED text, so MarkdownV2 escape
  inflation cannot cause MESSAGE_TOO_LONG and sizing against formatted
  wire length only causes premature splits and fragment messages).
- Skip the redundant requires-finalize edit after a got_done edit that
  split-and-delivered (salvaged from PR NousResearch#43463): re-finalizing re-splits
  the full text into the adopted continuation and duplicates chunks.
- _send_fallback_final only deletes the stale partial message when the
  fallback re-sent the COMPLETE final text. When the prefix dedup sent
  only the missing tail, the partial IS the head of the answer; deleting
  it left users with only the second half of long responses (live-
  reproduced: flood-control during a long stream -> head deleted,
  ratio 0.54 of content visible). This is the third bug behind the
  'Telegram cut messages' reports and was present on main and both PRs.
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 bound topic final replies render raw Markdown instead of formatted text

5 participants