Telegram: route a reply to the session that sent the replied-to message - #415
Open
serxa wants to merge 4 commits into
Open
Telegram: route a reply to the session that sent the replied-to message#415serxa wants to merge 4 commits into
serxa wants to merge 4 commits into
Conversation
Replying (Telegram reply) to a message the bot sent now routes the reply to the session that produced that message — and makes it the active session — instead of the chat's active session. This makes it easy to answer messages from cron/background sessions and to talk to several sessions without an explicit /session switch. The channel records message_id -> (chat_id, session_id) for every outbound message (interactive send() and notification delivery) in a bounded LRU, and resolves it on inbound replies. It falls back to the active session when the mapping is unknown or the target session is gone/archived, and requires the chat id to match so a reply can never cross into a session bound to a different chat. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…uting A reply we can't tie to a live session — the replied-to message was never recorded / was evicted from the LRU, or its session is gone/archived — is now rejected with a static, no-LLM message asking the user to pick a session with /sessions and resend, rather than silently delivered to the chat's active session (which would be a mis-route to the wrong session). _resolve_reply() is now three-way (active / route / reject); the inbound handlers send the nudge and drop a rejected reply without starting a turn. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
serxa
marked this pull request as ready for review
August 28, 2026 05:50
Record each inbound message against the session it is delivered to (the resolved reply target, else the active session), not just outbound bot messages. A user can then reply to their OWN earlier message to route the reply to that message's session — the same first-class targeting as replying to a bot message, and a clean way to re-target without /session. Adds _delivery_session(); both inbound handlers record the incoming message id -> target session before dispatch, and dispatch with that session explicitly. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…/telegram-reply-routing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Replying (a Telegram reply) to a message routes your reply to the session tied to that message, instead of the chat's active session — and makes that session active. This makes it easy to:
/sessionswitch.Plain (non-reply) messages are unchanged: they still go to the active session.
How
message_id -> (chat_id, session_id), populated forTelegramChannel.send) and notification /ask_userdelivery (NotificationService._deliver_telegram, the cron case), and_delivery_session— the resolved reply target, else the active session). This is what lets a reply to your own message route to that message's session._resolve_reply()classifies a message three ways: not a reply → active session (default, unchanged); reply to a mapped, live session → route there + make active; reply that can't be resolved → reject (below).Safety: an unresolvable reply is rejected, never mis-routed
An explicit reply we can't tie to a live session — the target message isn't in the map (sent before the daemon started, since the map is in-memory; or evicted from the LRU), its session is archived/gone, or the chat id doesn't match — is not delivered to the active session (that would be a silent mis-route). Instead the bot sends a static, no-LLM nudge ("couldn't find the session… use
/sessionsand resend") and drops the message without starting a turn. Resending is cheap; a mis-route is not.Behavior choice
Routing is sticky — a reply also makes the target session active (like
/session <id>), so a following plain message continues there.Testing
tests/test_telegram_reply_routing.py(17 tests): record/lookup, chat-mismatch isolation, empty-session no-op, LRU eviction + recency, three-way_resolve_reply(active / route / reject across live / missing / archived / unmapped / chat-mismatch),_delivery_session(routed vs active fallback), reply-to-own-recorded-message routing, andsend()recording.test_config_env,test_lockdown) reproduce on cleanmainin this environment and are unrelated to this change.