Skip to content

fix(telegram): prevent preview duplication in partial and block streaming modes#88634

Merged
obviyus merged 7 commits into
openclaw:mainfrom
jmao0001:main
Jun 3, 2026
Merged

fix(telegram): prevent preview duplication in partial and block streaming modes#88634
obviyus merged 7 commits into
openclaw:mainfrom
jmao0001:main

Conversation

@jmao0001

@jmao0001 jmao0001 commented May 31, 2026

Copy link
Copy Markdown
Contributor
Screenshot_20260531_070007_org_telegram_messenger_LaunchActivity

Summary

What problem does this PR solve?
In Telegram, channels.telegram.streaming.mode: "partial" and "block" modes duplicated the entire stream preview content on final emit if the reply size exceeded the ~4096 characters per-message cap. When the message was block-rotated into a new message, the draft stream controller unknowingly replayed chunks that were already delivered in preceding block/partial transmissions because it received the full cumulative text for final emit processing.

Why does this matter now?
This created broken and frustrating user experiences where long responses on Telegram were sent twice, interrupting reading flow and visually inflating output length.

What is the intended outcome?
The final stream text delivery respects the current activeChunkIndex tracked across stream rotations. The system now accurately targets the current active preview chunk for inline finalization and accurately distributes the remainder of the payload downstream sequentially. Additionally, partial streaming skips redundant text-only block deliveries that were needlessly stepping over incremental partial streaming.

What is intentionally out of scope?
Refactoring the larger core outbound block delivery protocol is out of scope. This addresses the Telegram-specific lane-delivery module interface handling.

What does success look like?
Long Telegram responses (>4096 characters) seamlessly transition to multiple paginated messages without repeating the initial content.

What should reviewers focus on?

  • Verification that lane.activeChunkIndex is scoped securely to onSupersededPreview so it only increments for retained overflow chunks.
  • Re-checking bounds clamp logic so active chunks perfectly index valid active streams.
  • Reviewing isDeliveredPrefix and suffix handling changes, ensuring they use activeFullText rather than text.

Linked context

Which issue does this close?

Closes #87624

Which issues, PRs, or discussions are related?

Related #84885

Was this requested by a maintainer or owner?

Yes.

Real behavior proof (required for external PRs)

  • Behavior or issue addressed: Final emit duplicating messages for Telegram single-turn replies exceeding the per-message chunk limit.
  • Real environment tested: Live Telegram Bot API via GitHub Codespaces running pnpm gateway:dev (using OpenRouter/GLM for generation).
  • Exact steps or command run after this patch:
    1. Set Telegram config to streaming.mode: "partial", chunkMode: "length", and artificially lowered textChunkLimit: 15 to force the chunking behavior on a short prompt.
    2. Sent the bot the prompt: "What is the capital of France?"
  • Evidence after fix: See the screenshot at the top of this PR.
  • Observed result after fix: The reply cleanly split into 3 small chunks (due to the 15-char limit) and successfully finalized in place without duplicating the first chunks at the end of the generation.
  • What was not tested: Exhaustive combinations of all available bot presentation controls (buttons/media) crossing the chunk boundary.
  • Proof limitations or environment constraints: Tested using an artificially low chunk limit (15) to efficiently simulate the 4096-character overflow behavior on a live connection.
  • Before evidence: Without the fix, this exact 15-char limit configuration would result in the first chunks printing again at the very bottom of the chat.

Tests and validation

Which commands did you run?
pnpm test:extensions & pnpm test:unit

What regression coverage was added or updated?
Coverage checks updated inside lane-delivery.test.ts and bot-message-dispatch.test.ts to properly account for the internal track increment value activeChunkIndex. Additional regressions created targeting long finals post-rotation.

What failed before this fix, if known?
The final delivery loops assumed message sequences weren't rotated locally by previous block triggers inside deliverLaneText. Text-only blocks would also get silently dropped if streams weren't properly enabled.

If no test was added, why not?
Tests were successfully added.

Risk checklist

Did user-visible behavior change? (Yes/No)
Yes (duplicate spam was removed)

Did config, environment, or migration behavior change? (Yes/No)
No

Did security, auth, secrets, network, or tool execution behavior change? (Yes/No)
No

What is the highest-risk area?
Draft lane text rotation bounds targeting potentially non-existent sequence indexes.

How is that risk mitigated?
Indices are firmly clamped against 0 and chunks.length - 1 with logical fallbacks handling edge-case length disparities securely.

Current review state

What is the next action?
Maintainer review and functional verification.

What is still waiting on author, maintainer, CI, or external proof?
CI success.

Which bot or reviewer comments were addressed?
Codex recommendation to provide concrete source-backed bounds correction targeting the final delivery overflow chunks. Re-scoped activeChunkIndex increments to onSupersededPreview specifically, ensured it isn't incremented in generic resets, and modified text-block delivery skips so they only suppress when an active partial draft exists.

AI Assistance Transparency

  • AI-assisted: This PR was generated with the assistance of an LLM.
  • Code Understanding: I have reviewed the logic regarding activeChunkIndex and how it correctly targets the active stream without duplicating already-delivered final chunks. I confirm I understand how this code works.
  • Prompts used: I provided the exact GitHub issue text and the codebase context to the LLM.
  • Real behavior proof: Attached in the section above. I have personally compiled and run this against a local Telegram bot to verify the duplicate messages are gone.
  • Codex Review: N/A (I do not have access to the local codex CLI tool, but I addressed the Codex bot's comments from the issue ticket).

Maintainer update (2026-06-01)

Maintainer repair pushed to this PR branch at 5d9f143b5f7811a1199315d0f84a0fcc1647b1f3.

Additional fixes on top of the contributor patch:

  • Recomputes retained long-final chunks after stopping the draft lane so already-visible overflow chunks are not resent.
  • Limits partial-mode text-only block suppression to true duplicate answer blocks, preserving tool-progress, button-bearing, and differing block deliveries.
  • Finalizes duplicate streamed blocks when no final payload follows, including pending draft streams that materialize their message id only after stop().
  • Keeps inline buttons attached to the post-stop active retained chunk instead of editing a stale first chunk.

Maintainer verification:

  • node scripts/run-vitest.mjs extensions/telegram/src/lane-delivery.test.ts extensions/telegram/src/bot-message-dispatch.test.ts extensions/telegram/src/draft-stream.test.ts (170 tests)
  • node scripts/run-oxlint.mjs extensions/telegram/src/lane-delivery-text-deliverer.ts extensions/telegram/src/bot-message-dispatch.ts extensions/telegram/src/lane-delivery.test.ts extensions/telegram/src/bot-message-dispatch.test.ts
  • git diff --check origin/main...HEAD
  • .agents/skills/autoreview/scripts/autoreview --mode branch --base origin/main clean on the repaired branch before the final unrelated main rebase
  • L2 tmux proof on pushed head 5d9f143b5f7: L2_TELEGRAM_EXIT=0 vitest=0 lint=0 diff=0
  • Testbox-through-Crabbox changed gate: tbx_01kt0wfts5fck0xvqp05zg9erx, lanes extensions, extensionTests, exit 0

@openclaw-barnacle openclaw-barnacle Bot added channel: telegram Channel integration: telegram size: S proof: supplied External PR includes structured after-fix real behavior proof. labels May 31, 2026
@clawsweeper

clawsweeper Bot commented May 31, 2026

Copy link
Copy Markdown
Contributor

Codex review: needs maintainer review before merge. Reviewed June 3, 2026, 8:57 AM ET / 12:57 UTC.

Summary
The PR updates Telegram draft-lane finalization to track retained preview chunks, suppress true duplicate partial-mode text-only blocks, finalize skipped streamed blocks, and add regression tests.

PR surface: Source +101, Tests +227. Total +328 across 4 files.

Reproducibility: yes. Current main is source-reproducible: retained overflow previews are possible in draft-stream, while the main finalizer still starts from the first final chunk and lacks retained active-chunk accounting.

Review metrics: none identified.

Merge readiness
Overall: 🦞 diamond lobster
Proof: 🦞 diamond lobster ✨ media proof bonus
Patch quality: 🦞 diamond lobster
Result: ready for maintainer review.

Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch.

Mantis proof suggestion
A native Telegram recording would materially help confirm the no-duplicate chunk behavior across the transport this PR changes. A maintainer can ask Mantis to capture proof by posting a new PR comment that starts with the OpenClaw Mantis account mention, followed by:

telegram desktop proof: verify a long Telegram partial/block streamed reply splits into final chunks without duplicate preview replay.

Risk before merge

  • [P1] Merging changes visible Telegram streamed-message finalization; if retained active-chunk accounting misses an edge case, users could still see dropped, duplicated, or misordered Telegram chunks.

Maintainer options:

  1. Land With Delivery-Risk Acceptance (recommended)
    Maintainership can accept the remaining Telegram edge-case risk because the current head has focused repairs, regression tests, live screenshot proof, and no concrete blocking defect found in review.
  2. Request Native Recording First
    Before merge, a maintainer can ask for a Telegram Desktop proof run showing a long partial or block streamed reply finalizing without duplicate preview replay.

Next step before merge

  • No automated repair remains; this is an implementation PR with maintainer-repaired code, sufficient proof, and visible delivery-risk acceptance pending.

Security
Cleared: The diff changes Telegram message finalization logic and tests only; it does not alter CI, dependencies, package execution, credentials, auth, or secret handling.

Review details

Best possible solution:

Land the Telegram-layer retained-preview accounting after maintainer acceptance of the visible delivery risk and required checks, then close the linked bug through the merge.

Do we have a high-confidence way to reproduce the issue?

Yes. Current main is source-reproducible: retained overflow previews are possible in draft-stream, while the main finalizer still starts from the first final chunk and lacks retained active-chunk accounting.

Is this the best way to solve the issue?

Yes. The PR keeps the repair in the Telegram streaming owner boundary and addresses retained preview accounting directly; a broader core outbound refactor would be larger than needed for this bug.

AGENTS.md: found and applied where relevant.

Codex review notes: model gpt-5.5, reasoning high; reviewed against a61c94b1f179.

Label changes

Label changes:

  • add proof: sufficient: Contributor real behavior proof is sufficient. The PR supplies live Telegram Bot API proof and an inspected screenshot showing the after-fix answer split into chunks without duplicated preview replay.

Label justifications:

  • P1: This PR fixes a linked P1 Telegram regression where long streamed replies can duplicate visible chunks for real users.
  • merge-risk: 🚨 message-delivery: The diff changes Telegram streamed-message finalization and duplicate-block suppression, so a mistake could drop, duplicate, or misorder visible Telegram messages.
  • rating: 🦞 diamond lobster: Overall readiness is 🦞 diamond lobster; proof is 🦞 diamond lobster and patch quality is 🦞 diamond lobster.
  • status: 👀 ready for maintainer look: ClawSweeper has no concrete contributor-facing blocker left for this PR. Sufficient (screenshot): The PR supplies live Telegram Bot API proof and an inspected screenshot showing the after-fix answer split into chunks without duplicated preview replay.
  • proof: sufficient: Contributor real behavior proof is sufficient. The PR supplies live Telegram Bot API proof and an inspected screenshot showing the after-fix answer split into chunks without duplicated preview replay.
  • proof: 📸 screenshot: Contributor real behavior proof includes screenshot evidence. The PR supplies live Telegram Bot API proof and an inspected screenshot showing the after-fix answer split into chunks without duplicated preview replay.
  • mantis: telegram-visible-proof: Mantis should capture Telegram visible proof. This PR changes visible Telegram streamed chat output, which can be demonstrated in a short native Telegram proof recording.
Evidence reviewed

PR surface:

Source +101, Tests +227. Total +328 across 4 files.

View PR surface stats
Area Files Added Removed Net
Source 2 121 20 +101
Tests 2 227 0 +227
Docs 0 0 0 0
Config 0 0 0 0
Generated 0 0 0 0
Other 0 0 0 0
Total 4 348 20 +328

What I checked:

Likely related people:

  • steipete: Peter Steinberger appears repeatedly in the file history for Telegram lane delivery, draft-stream extraction, and the default partial streaming path that this PR repairs. (role: recent area contributor; confidence: high; commits: 13541864e599, 269cc22b61d5, fc1787fd4bea; files: extensions/telegram/src/lane-delivery-text-deliverer.ts, extensions/telegram/src/draft-stream.ts, extensions/telegram/src/bot-message-dispatch.ts)
  • obviyus: Ayaan Zaidi authored the latest PR-head refactor and has multiple merged-history commits in Telegram draft-stream and duplicate-message handling paths. (role: recent repair author and feature-history contributor; confidence: high; commits: 2a39e6ae4158, daf8afc95494, a69e82765f20; files: extensions/telegram/src/lane-delivery-text-deliverer.ts, extensions/telegram/src/draft-stream.ts, extensions/telegram/src/bot-message-dispatch.test.ts)
  • vincentkoc: Vincent Koc pushed maintainer repair commits to this PR branch and has adjacent merged history around duplicate-message/runtime behavior. (role: recent repair author and adjacent contributor; confidence: high; commits: efaa4166f4ef, a3e89075e0e5, 927d4920f791; files: extensions/telegram/src/bot-message-dispatch.ts, extensions/telegram/src/lane-delivery-text-deliverer.ts, extensions/telegram/src/bot-message-dispatch.test.ts)
What the crustacean ranks mean
  • 🦀 challenger crab: rare, exceptional readiness with strong proof, clean implementation, and convincing validation.
  • 🦞 diamond lobster: very strong readiness with only minor maintainer review expected.
  • 🐚 platinum hermit: good normal PR, likely mergeable with ordinary maintainer review.
  • 🦐 gold shrimp: useful signal, but proof or patch confidence is still limited.
  • 🦪 silver shellfish: thin signal; proof, validation, or implementation needs work.
  • 🧂 unranked krab: not merge-ready because proof is missing/unusable or there are serious correctness or safety concerns.
  • 🌊 off-meta tidepool: rating does not apply to this item.

Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics.

How this review workflow works
  • ClawSweeper keeps one durable marker-backed review comment per issue or PR.
  • Re-runs edit this comment so the latest verdict, findings, and automation markers stay together instead of adding duplicate bot comments.
  • A fresh review can be triggered by eligible @clawsweeper re-review comments, exact-item GitHub events, scheduled/background review runs, or manual workflow dispatch.
  • PR/issue authors and users with repository write access can comment @clawsweeper re-review or @clawsweeper re-run on an open PR or issue to request a fresh review only.
  • Maintainers can also comment @clawsweeper review to request a fresh review only.
  • Fresh-review commands do not start repair, autofix, rebase, CI repair, or automerge.
  • Maintainer-only repair and merge flows require explicit commands such as @clawsweeper autofix, @clawsweeper automerge, @clawsweeper fix ci, or @clawsweeper address review.
  • Maintainers can comment @clawsweeper explain to ask for more context, or @clawsweeper stop to stop active automation.

@clawsweeper clawsweeper Bot added proof: sufficient ClawSweeper judged the real behavior proof convincing. proof: 📸 screenshot Contributor real behavior proof includes screenshot evidence. rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. status: ⏳ waiting on author ClawSweeper has contributor-facing work open and is waiting for author action. mantis: telegram-visible-proof Mantis should capture Telegram visible proof. labels May 31, 2026
@openclaw-barnacle openclaw-barnacle Bot removed the proof: sufficient ClawSweeper judged the real behavior proof convincing. label Jun 1, 2026
@clawsweeper clawsweeper Bot added proof: sufficient ClawSweeper judged the real behavior proof convincing. P1 High-priority user-facing bug, regression, or broken workflow. merge-risk: 🚨 message-delivery 🚨 May drop, duplicate, misroute, suppress, or wrongly target messages. labels Jun 1, 2026
@vincentkoc vincentkoc added status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR. rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. and removed rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. status: ⏳ waiting on author ClawSweeper has contributor-facing work open and is waiting for author action. labels Jun 1, 2026
@vincentkoc vincentkoc self-assigned this Jun 1, 2026
@openclaw-barnacle openclaw-barnacle Bot removed the proof: sufficient ClawSweeper judged the real behavior proof convincing. label Jun 1, 2026
@clawsweeper clawsweeper Bot added proof: sufficient ClawSweeper judged the real behavior proof convincing. rating: 🦞 diamond lobster Very strong PR readiness with only minor maintainer review expected. and removed rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. labels Jun 1, 2026
@openclaw-barnacle openclaw-barnacle Bot removed the proof: sufficient ClawSweeper judged the real behavior proof convincing. label Jun 1, 2026
@clawsweeper clawsweeper Bot added the proof: sufficient ClawSweeper judged the real behavior proof convincing. label Jun 1, 2026
@openclaw-barnacle openclaw-barnacle Bot removed the proof: sufficient ClawSweeper judged the real behavior proof convincing. label Jun 3, 2026
@clawsweeper clawsweeper Bot added the proof: sufficient ClawSweeper judged the real behavior proof convincing. label Jun 3, 2026
@openclaw-barnacle openclaw-barnacle Bot removed the proof: sufficient ClawSweeper judged the real behavior proof convincing. label Jun 3, 2026
@obviyus obviyus merged commit e4993ec into openclaw:main Jun 3, 2026
157 checks passed
@obviyus

obviyus commented Jun 3, 2026

Copy link
Copy Markdown
Contributor

Landed via squash in e4993ec from head a83bfd2.

Verification:

  • node scripts/run-vitest.mjs extensions/telegram/src/draft-stream.test.ts extensions/telegram/src/lane-delivery.test.ts extensions/telegram/src/bot-message-dispatch.test.ts -> 3 files, 175 tests passed.
  • node scripts/run-oxlint.mjs extensions/telegram/src/lane-delivery-text-deliverer.ts extensions/telegram/src/bot-message-dispatch.ts extensions/telegram/src/lane-delivery.test.ts extensions/telegram/src/bot-message-dispatch.test.ts -> passed.
  • pnpm exec oxfmt --check --threads=1 extensions/telegram/src/lane-delivery-text-deliverer.ts extensions/telegram/src/bot-message-dispatch.test.ts -> passed.
  • git diff --check origin/main...HEAD -> passed after final rebase.
  • .agents/skills/autoreview/scripts/autoreview --mode branch --base origin/main -> clean, no accepted/actionable findings.
  • GitHub CI on a83bfd272de670ef8047e48cb5c8b4c4bafcde41 -> CLEAN, no pending/failing checks.

Behavior addressed: Telegram streamed replies in partial and block modes finalize preview chunks once instead of replaying duplicate preview bodies.
Real environment tested: real Telegram bot-to-bot group -1003825137325 with driver bot foremanclawbot and SUT bot cameronclawbot.
Exact steps or command run after this patch: node ~/.codex/skills/custom/telegram-e2e-bot-to-bot/scripts/run-mock-sut-e2e.mjs --text '@{sut} Please answer with OPENCLAW_E2E_OK only.' --expect OPENCLAW_E2E_OK, plus a manual long partial-stream run through the SUT gateway using streaming.mode=partial, chunkMode=length, and textChunkLimit=280.
Evidence after fix: fast turn produced driver message 26534 and SUT reply 26535 with OPENCLAW_E2E_OK; long partial run produced driver message 26541, SUT messages 26542 through 26553, final marker LONG_E2E_DONE_mpy2nfc7, duplicateBodies: 0, and mockRequests: 1.
Observed result after fix: one model request, sequential Telegram chunks, no duplicate preview body replay.
What was not tested: production provider streaming; proof used mocked model streaming over real Telegram transport.

Changelog: not edited; CHANGELOG.md is release-owned in this repo. Release-note context is in the squash commit and PR history.

Thanks @jmao0001.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

channel: telegram Channel integration: telegram mantis: telegram-visible-proof Mantis should capture Telegram visible proof. merge-risk: 🚨 message-delivery 🚨 May drop, duplicate, misroute, suppress, or wrongly target messages. P1 High-priority user-facing bug, regression, or broken workflow. proof: 📸 screenshot Contributor real behavior proof includes screenshot evidence. proof: supplied External PR includes structured after-fix real behavior proof. rating: 🦞 diamond lobster Very strong PR readiness with only minor maintainer review expected. size: M status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Telegram streaming.mode: "partial" and "block" duplicate the full preview when reply >4096 chars

3 participants