* fix(telegram): prevent polling watchdog from aborting in-flight message delivery
The polling-stall watchdog only tracked getUpdates timestamps to detect
network stalls. When the agent takes >90s to process a message (common
with local/large models), getUpdates naturally pauses, and the watchdog
misidentifies this as a stall. It then calls fetchAbortController.abort(),
which cancels all in-flight Telegram API requests — including the
sendMessage call delivering the agent's reply. The message is silently
lost with no retry.
Track a separate lastApiActivityAt timestamp that is updated whenever
any Telegram API call (sendMessage, sendChatAction, etc.) completes
successfully. The watchdog now only triggers when both getUpdates AND
all other API activity have been silent beyond the threshold, proving
the network is genuinely stalled rather than just busy processing.
Update existing stall test to account for the new timestamp, and add a
regression test verifying that recent sendMessage activity suppresses
the watchdog.
Fixes#56065
Related: #53374, #54708
* fix(telegram): guard watchdog against in-flight API calls
* fix(telegram): bound watchdog API liveness
* fix: track newest watchdog API activity (#56343) (thanks @openperf)
* fix: note Telegram watchdog delivery fix (#56343) (thanks @openperf)
---------
Co-authored-by: Ayaan Zaidi <hi@obviy.us>
When approvals.exec.targets routes to a Telegram DM, the recipient
receives inline approval buttons but may not have explicit
channels.telegram.execApprovals configured. This adds a fallback
isTelegramExecApprovalTargetRecipient check so those DM recipients
can act on the buttons they were sent.
Includes accountId scoping for multi-bot deployments and 9 new tests.
Build a topic-qualified routing target (telegram:<chatId>:topic:<threadId>)
for native commands in forum groups so /new and /reset stay scoped to
the active topic instead of falling back to General.
General topic (threadId=1) correctly falls through to the base chat
target since Telegram rejects message_thread_id=1 on sends.
Add regression tests for topic routing and General topic edge case.
Fixes#35963
Filter whitespace-only text chunks at the bot delivery fan-in before
they reach sendTelegramText(). Covers normal text replies, follow-up
text, and voice fallback text paths.
Media-only replies are unaffected. message_sent hook still fires with
success: false for suppressed empty replies.
Fixes#37278
Replace proportional text estimate with binary search for the largest
text prefix whose rendered Telegram HTML fits the character limit, then
split at the last whitespace boundary within that verified prefix.
Single words longer than the limit still hard-split (unavoidable).
Markdown formatting stays balanced across split points.
Fixes#36644
Add shared normalizeTelegramReplyToMessageId() that rejects non-numeric,
NaN, and mixed-content strings before they reach the Telegram Bot API.
Apply at all four API sinks: direct send, bot delivery, draft stream,
and bot helpers.
Prevents GrammyError 400 when non-numeric values from session metadata
slip through typed boundaries.
Fixes#37222
* fix: display model name instead of ID in Telegram model selector (#56165)
* fix(telegram): scope model display names by provider
Signed-off-by: sallyom <somalley@redhat.com>
---------
Signed-off-by: sallyom <somalley@redhat.com>
Co-authored-by: sallyom <somalley@redhat.com>