Skip to content

fix(telegram): preserve debounce for partial draft fragments#80045

Merged
obviyus merged 4 commits into
openclaw:mainfrom
TurboTheTurtle:fix/telegram-partial-preserve-debounce
May 10, 2026
Merged

fix(telegram): preserve debounce for partial draft fragments#80045
obviyus merged 4 commits into
openclaw:mainfrom
TurboTheTurtle:fix/telegram-partial-preserve-debounce

Conversation

@TurboTheTurtle

@TurboTheTurtle TurboTheTurtle commented May 10, 2026

Copy link
Copy Markdown
Contributor

Fixes #79605.

Summary

Telegram partial draft streaming now coalesces delta-shaped partial fragments into the cumulative answer preview before handing them to the draft stream. This keeps the existing 30-character first-preview debounce intact, but prevents providers that deliver token-sized fragments from staying permanently below that threshold until the final answer arrives.

This is the debounce-preserving replacement for the closed debounce-removal attempts, including #80002.

Root Cause

The Telegram draft lane treated every onPartialReply text payload as a complete cumulative preview. That works when the provider sends full accumulated text, but providers can also surface token/delta-shaped fragments. In that case each fragment may be shorter than the 30-character first-preview debounce, so no preview message is created before final delivery.

Real behavior proof

  • Behavior or issue addressed: Telegram streaming.mode: "partial" can build a cumulative preview from delta-shaped text fragments while still passing minInitialChars: 30 to the draft stream, then edit that preview when the final answer extends it.

  • Real environment tested: Local OpenClaw checkout on macOS, branch fix/telegram-partial-preserve-debounce, running the Telegram dispatcher and draft stream runtime with a redacted local Telegram API adapter.

  • Exact steps or command run after this patch: node --import tsx /tmp/telegram-partial-smoke.mjs

  • Evidence after fix: Console output from the local OpenClaw Telegram partial runtime smoke:

    OpenClaw Telegram partial runtime smoke
    [telegram] sendMessage preview chat=123 chars=57 text="Streaming previews are useful because they show progress."
    [telegram] editMessageText preview chat=123 message=2001 chars=87 text="Streaming previews are useful because they show progress before the final answer lands."
    [telegram] internal sent hook message=2001 content="Streaming previews are useful because they show progress before the final answer lands."
    
  • Observed result after fix: The first two fragments, Streaming and previews , remain under the 30-character debounce until the third fragment coalesces the cumulative preview to 57 characters. The runtime then creates the Telegram preview with sendMessage, edits the same message to the 87-character final answer with editMessageText, and records the internal sent hook for message 2001.

  • What was not tested: A live Telegram DM screenshot or production bot-token run was not captured in this environment; the local runtime smoke uses a redacted in-process Telegram API adapter.

Validation

  • pnpm test extensions/telegram/src/bot-message-dispatch.test.ts extensions/telegram/src/draft-stream.test.ts src/plugin-sdk/channel-streaming.test.ts
  • pnpm check:changed --base upstream/main
  • pnpm exec oxfmt --check --threads=1 extensions/telegram/src/bot-message-dispatch.ts extensions/telegram/src/bot-message-dispatch.test.ts
  • git diff --check upstream/main
pnpm test extensions/telegram/src/bot-message-dispatch.test.ts extensions/telegram/src/draft-stream.test.ts src/plugin-sdk/channel-streaming.test.ts
Test Files  1 passed (1)
     Tests  17 passed (17)
Test Files  2 passed (2)
     Tests  71 passed (71)
[test] passed 2 Vitest shards

pnpm check:changed --base upstream/main
[check:changed] lanes=extensions, extensionTests
Found 0 warnings and 0 errors.
runtime-sidecar-loaders: local runtime sidecar loaders look OK.
Import cycle check: 0 runtime value cycle(s).

pnpm exec oxfmt --check --threads=1 extensions/telegram/src/bot-message-dispatch.ts extensions/telegram/src/bot-message-dispatch.test.ts
All matched files use the correct format.

git diff --check upstream/main
passed with no output

@openclaw-barnacle openclaw-barnacle Bot added channel: telegram Channel integration: telegram size: S labels May 10, 2026
@openclaw-barnacle openclaw-barnacle Bot added the triage: mock-only-proof Candidate: PR proof only shows tests, mocks, snapshots, lint, typecheck, or CI. label May 10, 2026
@clawsweeper

clawsweeper Bot commented May 10, 2026

Copy link
Copy Markdown
Contributor

Codex review: needs changes before merge.

Summary
The PR adds partial-reply delta/replace typing, updates Telegram draft streaming to coalesce partial deltas before draft updates, and adds Telegram dispatcher regression tests.

Reproducibility: yes. source-level reproduction is high confidence: current main keeps the 30-character first-preview debounce and the draft stream suppresses first previews below that threshold. A token-shaped partial forwarded as text-only can therefore remain invisible until final delivery; I did not run a live Telegram bot in this read-only review.

Real behavior proof
Sufficient (terminal): The PR body includes after-fix terminal output from a local Telegram dispatcher/draft-stream runtime smoke showing preview send, preview edit, and the internal sent hook; this is separate from the upstream-forwarding correctness finding.

Next step before merge
A narrow repair can propagate or recompute the new partial metadata through the agent runner and add focused regression coverage.

Security
Cleared: The diff changes runtime text payload handling and focused tests only, with no dependency, workflow, secret, permission, install, or package-resolution surface changed.

Review findings

  • [P2] Forward partial deltas through the runner — src/auto-reply/get-reply-options.types.ts:80
Review details

Best possible solution:

Keep the Telegram-side accumulator, but complete the contract by preserving or recomputing partial delta/replace metadata through agent-runner-execution and add focused coverage for the full callback path before merge.

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

Yes, source-level reproduction is high confidence: current main keeps the 30-character first-preview debounce and the draft stream suppresses first previews below that threshold. A token-shaped partial forwarded as text-only can therefore remain invisible until final delivery; I did not run a live Telegram bot in this read-only review.

Is this the best way to solve the issue?

No. The Telegram accumulator is a maintainable shape, but the PR must also carry or recompute delta/replace through the normal agent-runner callback wrappers so production Telegram sees the same payload shape as the new tests.

Full review comments:

  • [P2] Forward partial deltas through the runner — src/auto-reply/get-reply-options.types.ts:80
    The new Telegram accumulator only works when onPartialReply receives delta/replace, but the normal runner wrappers still call params.opts.onPartialReply({ text }) or { text, mediaUrls } after typing normalization, dropping those fields in agent-runner-execution.ts. Token-sized fragments can therefore still reach Telegram as text-only snapshots and stay below the first-preview debounce in the production path.
    Confidence: 0.87

Overall correctness: patch is incorrect
Overall confidence: 0.86

Acceptance criteria:

  • pnpm test src/auto-reply/reply/agent-runner-execution.test.ts extensions/telegram/src/bot-message-dispatch.test.ts extensions/telegram/src/draft-stream.test.ts src/plugin-sdk/channel-streaming.test.ts
  • pnpm exec oxfmt --check --threads=1 src/auto-reply/reply/agent-runner-execution.ts src/auto-reply/reply/agent-runner-execution.test.ts extensions/telegram/src/bot-message-dispatch.ts extensions/telegram/src/bot-message-dispatch.test.ts
  • git diff --check upstream/main

What I checked:

  • Current-main Telegram debounce path: Current main still passes minInitialChars: 30 for Telegram partial/block draft streams, so a first preview is intentionally withheld until the rendered preview reaches the threshold. (extensions/telegram/src/bot-message-dispatch.ts:527, cb60a06c9e58)
  • Current-main draft suppression: The draft stream returns without sending while no preview message exists and rendered text is shorter than minInitialChars, matching the reported no-preview symptom for token-sized fragments. (extensions/telegram/src/draft-stream.ts:284, cb60a06c9e58)
  • Delta fields are produced upstream: The embedded subscription layer builds assistant stream data with text, delta, and replace, then calls onPartialReply(data), so the missing contract is downstream forwarding rather than upstream derivation. (src/agents/pi-embedded-subscribe.handlers.messages.ts:605, cb60a06c9e58)
  • Delta fields are dropped before channel callbacks: The normal agent-runner wrappers call params.opts.onPartialReply with only { text } or { text, mediaUrls }, dropping delta and replace before Telegram can use the PR's accumulator. (src/auto-reply/reply/agent-runner-execution.ts:1474, cb60a06c9e58)
  • PR-head Telegram accumulator: PR head 0cab701b2afb adds DraftPartialTextUpdate, resolves updates using delta/replace, and passes the whole partial payload into Telegram ingestion, but this only works when those fields survive to the channel callback. (extensions/telegram/src/bot-message-dispatch.ts:113, 0cab701b2afb)
  • PR discussion and proof: The PR body and comments include terminal proof from a local Telegram dispatcher/draft-stream smoke and contributor validation, and the related issue remains the active bug target while earlier debounce-removal attempts were closed. (0cab701b2afb)

Likely related people:

  • obviyus: Recent commits on Telegram draft streaming and draft-stream behavior include 10bbed8a6d30, 30e079dd89b4, and 7a2cc4b8d65c; this PR is also assigned to this handle and its latest commit is by this handle. (role: recent Telegram streaming contributor; confidence: high; commits: 10bbed8a6d30, 30e079dd89b4, 7a2cc4b8d65c; files: extensions/telegram/src/bot-message-dispatch.ts, extensions/telegram/src/draft-stream.ts, extensions/telegram/src/bot-message-dispatch.test.ts)
  • steipete: Recent agent-runner-execution.ts history includes CLI preview bridge serialization and related bridge work, which is the path currently dropping the new partial metadata before channel callbacks. (role: recent agent preview bridge contributor; confidence: high; commits: 560c7440fb88, 2afffccee3af, 434b3d387608; files: src/auto-reply/reply/agent-runner-execution.ts, src/auto-reply/reply/agent-runner-execution.test.ts)
  • medns: Recent agent streaming/refactor history touches the embedded subscription and stream-data area that produces text/delta payloads consumed by this PR's intended contract. (role: recent adjacent agent streaming contributor; confidence: medium; commits: e7277b4e3a4b; files: src/agents/pi-embedded-subscribe.handlers.messages.ts, extensions/telegram/src/bot-message-dispatch.ts)

Remaining risk / open question:

  • The supplied terminal smoke proves the Telegram dispatcher/draft-stream behavior when delta metadata reaches it, but it does not cover the full agent-runner-to-Telegram callback path where the metadata is currently lost.

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

@openclaw-barnacle openclaw-barnacle Bot added proof: supplied External PR includes structured after-fix real behavior proof. and removed triage: mock-only-proof Candidate: PR proof only shows tests, mocks, snapshots, lint, typecheck, or CI. labels May 10, 2026
@TurboTheTurtle

TurboTheTurtle commented May 10, 2026

Copy link
Copy Markdown
Contributor Author

Follow-up after opening: the Real behavior proof gate is green after adding the local Telegram runtime smoke output to the PR body.

The current red CI jobs look unrelated to this two-file Telegram diff:

  • build-artifacts failed with Node heap OOM during tsdown; build-smoke failed only because build-artifacts failed.
  • check-additional-runtime-topology-architecture reported a config-type Madge cycle in src/config/types.*; this branch does not touch that path, and pnpm check:architecture passes locally on this branch.

I tried to rerun failed CI jobs, but GitHub rejected the rerun because this account does not have repository admin rights.

Additional local validation after the PR opened:

  • pnpm check:architecture -> passed
  • node --import tsx /tmp/telegram-partial-smoke.mjs -> preview sendMessage, preview editMessageText, internal sent hook

@clawsweeper clawsweeper Bot added the proof: sufficient ClawSweeper judged the real behavior proof convincing. label May 10, 2026
@TurboTheTurtle

Copy link
Copy Markdown
Contributor Author

Pushed 8151d4d to address the ClawSweeper P2. The answer-fragment accumulator is now separate from the visible answer lane draft state, so tool-progress status text is not used as the previous answer partial when coalescing delta-shaped answer fragments.

Added regression coverage for a partial-mode turn that emits tool progress before short answer fragments; the answer stream now updates from the progress draft to Done , then Done answer, then final Done answer. instead of appending to the progress text.

Validation:

  • pnpm test extensions/telegram/src/bot-message-dispatch.test.ts extensions/telegram/src/draft-stream.test.ts src/plugin-sdk/channel-streaming.test.ts — 3 files / 89 tests passed
  • pnpm check:changed --base upstream/main — passed
  • pnpm exec oxfmt --check --threads=1 extensions/telegram/src/bot-message-dispatch.ts extensions/telegram/src/bot-message-dispatch.test.ts — passed
  • git diff --check upstream/main — passed

@clawsweeper re-review

@openclaw-barnacle openclaw-barnacle Bot removed the proof: sufficient ClawSweeper judged the real behavior proof convincing. label May 10, 2026
@TurboTheTurtle

Copy link
Copy Markdown
Contributor Author

Post-push CI follow-up for 8151d4d: the real behavior proof gate is green, and the Telegram-adjacent/changed-file checks are passing. The remaining red jobs look unrelated to this patch:

  • build-artifacts OOMs in tsdown (Reached heap limit Allocation failed - JavaScript heap out of memory); build-smoke then fails as a dependent aggregate.
  • check-additional-runtime-topology-architecture reports a Madge cycle through src/config/types.*, with no Telegram files involved. I also reran pnpm check:architecture locally on this branch after the push and it passed (Madge import cycle check: 0 cycle(s)).

So I do not see another code change needed in this PR for the current red CI.

@clawsweeper clawsweeper Bot added the proof: sufficient ClawSweeper judged the real behavior proof convincing. label May 10, 2026
@obviyus obviyus self-assigned this May 10, 2026
@openclaw-barnacle openclaw-barnacle Bot added agents Agent runtime and tooling size: M and removed size: S labels May 10, 2026
@obviyus
obviyus force-pushed the fix/telegram-partial-preserve-debounce branch from 0cab701 to fdb7de5 Compare May 10, 2026 02:52
@openclaw-barnacle openclaw-barnacle Bot removed the proof: sufficient ClawSweeper judged the real behavior proof convincing. label May 10, 2026
@obviyus
obviyus merged commit 5eaed7d into openclaw:main May 10, 2026
100 of 102 checks passed
@obviyus

obviyus commented May 10, 2026

Copy link
Copy Markdown
Contributor

Landed via rebase onto main.

  • Scoped tests: pnpm test extensions/telegram/src/bot-message-dispatch.test.ts extensions/telegram/src/draft-stream.test.ts src/plugin-sdk/channel-streaming.test.ts; pnpm check:changed --base origin/main; pnpm exec oxfmt --check --threads=1 extensions/telegram/src/bot-message-dispatch.ts extensions/telegram/src/bot-message-dispatch.test.ts src/auto-reply/get-reply-options.types.ts src/auto-reply/types.ts src/agents/pi-embedded-runner/run/params.ts src/agents/pi-embedded-subscribe.types.ts; git diff --check origin/main; real Telegram bot-to-bot proof observed driver msg 6024 and SUT msg 6025 edited to OPENCLAW_E2E_PARTIAL_STREAM_OK.
  • Changelog: CHANGELOG.md updated.
  • Land commit: bc31b76
  • Merge commit: 5eaed7d

Thanks @TurboTheTurtle!

longstoryscott pushed a commit to longstoryscott/openclaw that referenced this pull request May 13, 2026
@TurboTheTurtle
TurboTheTurtle deleted the fix/telegram-partial-preserve-debounce branch May 18, 2026 22:23
github-actions Bot pushed a commit to Desicool/openclaw that referenced this pull request May 24, 2026
jameslcowan pushed a commit to jameslcowan/openclaw that referenced this pull request Jun 2, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agents Agent runtime and tooling channel: telegram Channel integration: telegram proof: supplied External PR includes structured after-fix real behavior proof. size: M

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Telegram streaming mode "partial" has no effect — only sendMessage ok in logs, no preview edits

2 participants