Skip to content

telegram: avoid silent fallback after message tool send (#78685)#78726

Merged
obviyus merged 2 commits intoopenclaw:mainfrom
neeravmakwana:fix/telegram-msg-tool-no-silent-fallback-78685
May 7, 2026
Merged

telegram: avoid silent fallback after message tool send (#78685)#78726
obviyus merged 2 commits intoopenclaw:mainfrom
neeravmakwana:fix/telegram-msg-tool-no-silent-fallback-78685

Conversation

@neeravmakwana
Copy link
Copy Markdown
Contributor

@neeravmakwana neeravmakwana commented May 7, 2026

Root cause

Telegram inbound dispatch tracks user-visible replies with deliveryState when the buffered reply pipeline calls deliverReplies / durable final delivery. The message tool path for Telegram is gateway-owned and sends through extensions/telegram/src/action-runtime.ts; the original patch marked the generic outbound delivery path instead, so a successful Telegram message.send still left the inbound turn looking undelivered and allowed the rewritten silent-reply fallback to show up.

Fixes #78685 .

Approach

Keep the correlation inside the Telegram plugin. During an inbound Telegram turn, correlate the trusted session key to the active chat/account, pass that trusted session key into the Telegram action runtime, and mark the inbound turn delivered only after the matching Telegram message.send succeeds for the same target/account.

Safety

Scoped to Telegram + matching session key/outbound recipient. The correlation is cleared when inbound dispatch exits, wrong targets do not mark the turn delivered, and no core/SDK Telegram special case remains.

Runtime / security

No change to Telegram auth/pairing, gateway auth models, sandboxing, or tool ACLs. Delivery correlation only affects whether the rewritten silent filler is skipped after proven outbound visibility.

Real behavior proof

  • Behavior or issue addressed: Telegram inbound DM turns that already produced a same-chat message.send should not also get the unwanted rewritten silent fallback.
  • Real environment tested: macOS isolated maintainer worktree at PR head c4f3116a4660374562554f2be9f37febb22aefcc, current origin/main base 92284bc46043900bccd5a3c612d49132638d5da4, Node/pnpm repo tooling, Telegram plugin dispatch/action runtime path.
  • Exact steps or command run after this patch: pnpm test extensions/telegram/src/inbound-turn-delivery.test.ts extensions/telegram/src/action-runtime.test.ts extensions/telegram/src/channel-actions.test.ts extensions/telegram/src/bot-message-dispatch.test.ts; pnpm exec oxfmt --check --threads=1 CHANGELOG.md extensions/telegram/src/action-runtime.ts extensions/telegram/src/action-runtime.test.ts extensions/telegram/src/bot-message-dispatch.ts extensions/telegram/src/channel-actions.ts extensions/telegram/src/channel-actions.test.ts extensions/telegram/src/inbound-turn-delivery.ts extensions/telegram/src/inbound-turn-delivery.test.ts; node scripts/run-oxlint.mjs --tsconfig config/tsconfig/oxlint.extensions.json extensions/telegram/src/action-runtime.ts extensions/telegram/src/action-runtime.test.ts extensions/telegram/src/bot-message-dispatch.ts extensions/telegram/src/channel-actions.ts extensions/telegram/src/channel-actions.test.ts extensions/telegram/src/inbound-turn-delivery.ts extensions/telegram/src/inbound-turn-delivery.test.ts; git diff --check origin/main...HEAD; GraphQL review-thread query for PR telegram: avoid silent fallback after message tool send (#78685) #78726.
  • Evidence after fix: terminal output from the rebased head showed Test Files 4 passed (4) / Tests 100 passed (100), All matched files use the correct format., Found 0 warnings and 0 errors., clean git diff --check, and GraphQL reviewThreads.nodes=[] at head c4f3116a4660374562554f2be9f37febb22aefcc.
  • Observed result after fix: the matching Telegram action-runtime send marks the active inbound turn delivered, wrong-target sends do not, and dispatch cleanup prevents stale/double delivery marks.
  • What was not tested: live Telegram Bot API duplicate-message repro was not rerun in this landing pass because no TELEGRAM_BOT_TOKEN / OPENCLAW_QA_TELEGRAM_* credentials were exposed locally; maintainer override is applied for the RBP gate with the scoped code-path proof above.

Tests run

pnpm test extensions/telegram/src/inbound-turn-delivery.test.ts extensions/telegram/src/action-runtime.test.ts extensions/telegram/src/channel-actions.test.ts extensions/telegram/src/bot-message-dispatch.test.ts
pnpm exec oxfmt --check --threads=1 CHANGELOG.md extensions/telegram/src/action-runtime.ts extensions/telegram/src/action-runtime.test.ts extensions/telegram/src/bot-message-dispatch.ts extensions/telegram/src/channel-actions.ts extensions/telegram/src/channel-actions.test.ts extensions/telegram/src/inbound-turn-delivery.ts extensions/telegram/src/inbound-turn-delivery.test.ts
node scripts/run-oxlint.mjs --tsconfig config/tsconfig/oxlint.extensions.json extensions/telegram/src/action-runtime.ts extensions/telegram/src/action-runtime.test.ts extensions/telegram/src/bot-message-dispatch.ts extensions/telegram/src/channel-actions.ts extensions/telegram/src/channel-actions.test.ts extensions/telegram/src/inbound-turn-delivery.ts extensions/telegram/src/inbound-turn-delivery.test.ts
git diff --check origin/main...HEAD

Out of scope

  • Broader refactoring of Telegram delivery trackers.
  • Matching other outbound channels unless the same symptom is confirmed there.

@clawsweeper
Copy link
Copy Markdown
Contributor

clawsweeper Bot commented May 7, 2026

Codex review: needs real behavior proof before merge.

Summary
The PR adds Telegram inbound-turn delivery correlation, forwards sessionKey into Telegram message actions, adds focused tests, and updates the changelog.

Reproducibility: yes. as a source-level path but not a live run in this review. Current main only marks Telegram delivery from the inbound reply pipeline, while message(action=send) can succeed through the gateway plugin action path and leave deliveryState false before the empty-final fallback guard.

Real behavior proof
Needs real behavior proof before merge: The PR body/comments only provide tests and informal manual statements; the contributor should attach redacted after-fix Telegram runtime proof and update the PR body for re-review.

Next step before merge
Contributor revision and real behavior proof are needed; automation should not take over while the external PR proof gate is still missing.

Security
Cleared: No concrete security or supply-chain regression was found in this scoped Telegram delivery-correlation diff.

Review findings

  • [P2] Compare against the resolved Telegram chat id — extensions/telegram/src/action-runtime.ts:399
Review details

Best possible solution:

Mark the active inbound turn delivered using the successful Telegram send result's resolved chatId with the session/account guard, cover the gateway-normalized message tool path in tests, and require redacted real Telegram proof before merge.

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

Yes, as a source-level path but not a live run in this review. Current main only marks Telegram delivery from the inbound reply pipeline, while message(action=send) can succeed through the gateway plugin action path and leave deliveryState false before the empty-final fallback guard.

Is this the best way to solve the issue?

No. Forwarding sessionKey into the gateway action is the right direction, but the correlation should compare against the resolved Telegram result.chatId, not the normalized/prefixed to value.

Full review comments:

  • [P2] Compare against the resolved Telegram chat id — extensions/telegram/src/action-runtime.ts:399
    runMessageAction normalizes Telegram message targets to provider-prefixed values like telegram:123 before the gateway action runs, but beginTelegramInboundTurnDeliveryCorrelation stores the inbound chat as String(chatId) such as 123. Passing to here means the same-chat send still misses the active turn and the silent fallback can still be emitted; use the successful send result's resolved chatId instead and cover the gateway-normalized path.
    Confidence: 0.91

Overall correctness: patch is incorrect
Overall confidence: 0.9

What I checked:

  • Current fallback guard: Current main creates a Telegram deliveryState, marks it only from inbound reply delivery, and emits the silent fallback when the snapshot is still undelivered. (extensions/telegram/src/bot-message-dispatch.ts:686, f66a2dc41d3b)
  • Gateway message action path: Current main normalizes message tool targets before dispatching gateway-owned Telegram actions, so target/to values such as a current Telegram chat become normalized provider-prefixed targets before handleTelegramAction. (src/infra/outbound/message-action-runner.ts:1041, f66a2dc41d3b)
  • Telegram target normalization: Telegram target normalization returns telegram:<chatId> for numeric/current targets, while inbound dispatch registers the active turn with outboundTo: String(chatId). (extensions/telegram/src/normalize.ts:38, f66a2dc41d3b)
  • Proposed mismatch: The PR's new notification passes to instead of the successful send result's resolved chatId, so telegram:123 will not match inbound 123. (extensions/telegram/src/action-runtime.ts:399, 90070cb7bc95)
  • Resolved chat id is available: sendMessageTelegram resolves the target, sends to the numeric Telegram chat id, and returns chatId, which is the safer value to correlate with the inbound turn. (extensions/telegram/src/send.ts:585, f66a2dc41d3b)
  • Real behavior proof missing: The PR body and comments list automated tests plus informal manual checks, but no attached after-fix live Telegram logs, terminal output, screenshot, recording, or artifact. (90070cb7bc95)

Likely related people:

  • Vincent Koc: Current-main blame points to this author across the Telegram delivery-state/fallback guard, Telegram action runtime, gateway message action dispatch, and message-action runner paths central to this behavior. (role: recent maintainer; confidence: high; commits: beee6449a1d2, 90f713453562; files: extensions/telegram/src/bot-message-dispatch.ts, extensions/telegram/src/channel-actions.ts, extensions/telegram/src/action-runtime.ts)
  • Ayaan Zaidi: Recent local history includes Telegram duplicate-final and DM reply-dedupe fixes adjacent to this fallback/delivery-tracking behavior. (role: adjacent owner; confidence: medium; commits: 4b1c37a15239, 26e76f9a6149; files: extensions/telegram/src/bot-message-dispatch.ts)
  • Peter Steinberger: Recent Telegram draft finalization ordering work is near the same fallback and delivery-state path. (role: recent adjacent maintainer; confidence: medium; commits: 69d446d1784c; files: extensions/telegram/src/bot-message-dispatch.ts)

Remaining risk / open question:

  • No attached live Telegram proof shows the after-fix behavior in a real setup.
  • The new tests call handleTelegramAction directly and do not exercise the runMessageAction/gateway target-normalization path that creates the prefixed-target mismatch.

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

@neeravmakwana
Copy link
Copy Markdown
Contributor Author

Code review follow-up ([P2] gateway message tool path)

Issue: notifyTelegramInboundTurnOutboundSuccess wired under deliverOutboundPayloads / emitMessageSent never ran for Telegram message(action=send) when execution mode is gateway, because the gateway handler dispatches straight into handleTelegramActionsendMessageTelegram, skipping the deliver/message-sent emitter. The active inbound turn could stay undelivered and the silent fallback could still appear after a successful tool send.

Fix (this PR):

  • extensions/telegram/src/channel-actions.ts — Forward sessionKey from ChannelMessageActionContext into handleTelegramAction (third-arg options alongside mediaLocalRoots). That propagates both for local plugin dispatch and for gateway RPC (send.ts already passes sessionKey into dispatchChannelMessageAction).

  • extensions/telegram/src/action-runtime.ts — After successful sendMessageTelegram and sendPollTelegram, call notifyTelegramInboundTurnOutboundSuccess with channelId: "telegram", to: result.chatId (resolved Telegram chat id — matches beginTelegramInboundTurnDeliveryCorrelation’s outboundTo: String(chatId) from inbound dispatch), accountId, and sessionKey from options.

  • deliver.ts hook is unchanged on purpose: it still covers sends that do go through createMessageSentEmitter. Duplicate markInboundTurnDelivered is harmless (idempotent).

Tests:

  • action-runtime.test.ts — Correlation fires when sessionKey + ChatId align; no mark when result.chatId mismatches outboundTo.
  • channel-actions.test.tssessionKey is forwarded into handleTelegramAction.

Pushed commit: telegram: correlate inbound turn on gateway sendMessage/sendPoll path.

@neeravmakwana
Copy link
Copy Markdown
Contributor Author

@clawsweeper re-review

@obviyus obviyus force-pushed the fix/telegram-msg-tool-no-silent-fallback-78685 branch from 3816525 to 90070cb Compare May 7, 2026 02:56
@obviyus obviyus self-assigned this May 7, 2026
neeravmakwana and others added 2 commits May 7, 2026 09:03
)

Register the active Telegram inbound SessionKey/outbound peer while dispatching,
and mark inbound lane delivery when deliverOutbound emits a matching telegram
message:sent success. Prevents rewritten silent-reply fillers after visible
tool-routed replies with an empty final.

Co-authored-by: Cursor <cursoragent@cursor.com>
@obviyus obviyus force-pushed the fix/telegram-msg-tool-no-silent-fallback-78685 branch from 90070cb to c4f3116 Compare May 7, 2026 03:35
@obviyus obviyus added proof: override Maintainer override for the external PR real behavior proof gate. and removed triage: needs-real-behavior-proof Candidate: external PR needs after-fix proof from a real setup. labels May 7, 2026
Copy link
Copy Markdown
Contributor

@obviyus obviyus left a comment

Choose a reason for hiding this comment

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

Verified the Telegram same-chat message.send path and confirmed the fix marks the matching inbound turn delivered after the action-runtime send succeeds, so the rewritten silent fallback is skipped.

Maintainer follow-up: moved correlation into the Telegram plugin, removed the core/SDK hook, rebased, and updated structured RBP proof with maintainer override.

Gate: pnpm test extensions/telegram/src/inbound-turn-delivery.test.ts extensions/telegram/src/action-runtime.test.ts extensions/telegram/src/channel-actions.test.ts extensions/telegram/src/bot-message-dispatch.test.ts; pnpm exec oxfmt --check --threads=1 ...; node scripts/run-oxlint.mjs --tsconfig config/tsconfig/oxlint.extensions.json ...; git diff --check origin/main...HEAD; CI/RBP green on c4f3116.

@obviyus obviyus merged commit 447182a into openclaw:main May 7, 2026
111 of 115 checks passed
@obviyus
Copy link
Copy Markdown
Contributor

obviyus commented May 7, 2026

Landed on main.

Thanks @neeravmakwana.

rogerdigital pushed a commit to rogerdigital/openclaw that referenced this pull request May 7, 2026
… (thanks @neeravmakwana)

* telegram: correlate message-tool sends with inbound turn (openclaw#78685)

Register the active Telegram inbound SessionKey/outbound peer while dispatching,
and mark inbound lane delivery when deliverOutbound emits a matching telegram
message:sent success. Prevents rewritten silent-reply fillers after visible
tool-routed replies with an empty final.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(telegram): track message action delivery

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Ayaan Zaidi <hi@obviy.us>
steipete pushed a commit that referenced this pull request May 7, 2026
…@neeravmakwana)

* telegram: correlate message-tool sends with inbound turn (#78685)

Register the active Telegram inbound SessionKey/outbound peer while dispatching,
and mark inbound lane delivery when deliverOutbound emits a matching telegram
message:sent success. Prevents rewritten silent-reply fillers after visible
tool-routed replies with an empty final.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(telegram): track message action delivery

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Ayaan Zaidi <hi@obviy.us>
(cherry picked from commit 447182a)
github-actions Bot pushed a commit to Desicool/openclaw that referenced this pull request May 9, 2026
… (thanks @neeravmakwana)

* telegram: correlate message-tool sends with inbound turn (openclaw#78685)

Register the active Telegram inbound SessionKey/outbound peer while dispatching,
and mark inbound lane delivery when deliverOutbound emits a matching telegram
message:sent success. Prevents rewritten silent-reply fillers after visible
tool-routed replies with an empty final.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(telegram): track message action delivery

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Ayaan Zaidi <hi@obviy.us>
rogerdigital pushed a commit to rogerdigital/openclaw that referenced this pull request May 9, 2026
… (thanks @neeravmakwana)

* telegram: correlate message-tool sends with inbound turn (openclaw#78685)

Register the active Telegram inbound SessionKey/outbound peer while dispatching,
and mark inbound lane delivery when deliverOutbound emits a matching telegram
message:sent success. Prevents rewritten silent-reply fillers after visible
tool-routed replies with an empty final.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(telegram): track message action delivery

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Ayaan Zaidi <hi@obviy.us>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

channel: telegram Channel integration: telegram proof: override Maintainer override for the external PR real behavior proof gate. size: S

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Telegram direct chat receives unwanted "Nothing further to report" fallback after message.send

2 participants