Skip to content

Telegram: route a reply to the session that sent the replied-to message - #415

Open
serxa wants to merge 4 commits into
mainfrom
serxa/telegram-reply-routing
Open

Telegram: route a reply to the session that sent the replied-to message#415
serxa wants to merge 4 commits into
mainfrom
serxa/telegram-reply-routing

Conversation

@serxa

@serxa serxa commented Aug 27, 2026

Copy link
Copy Markdown
Member

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:

  • answer messages from cron / background sessions (previously hard to send into),
  • reply to your own earlier message to jump a reply back into whatever session that message went to, and
  • talk to several sessions by replying, without an explicit /session switch.

Plain (non-reply) messages are unchanged: they still go to the active session.

How

  • Recording (both directions): the Telegram channel keeps a bounded LRU message_id -> (chat_id, session_id), populated for
    • every message the bot sends — interactive turn responses (TelegramChannel.send) and notification / ask_user delivery (NotificationService._deliver_telegram, the cron case), and
    • every inbound message, against the session it lands in (_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.
  • Inbound: _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 /sessions and 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, and send() recording.
  • Full suite: 3375 passed; the only 2 failures (test_config_env, test_lockdown) reproduce on clean main in this environment and are unrelated to this change.

serxa and others added 2 commits August 27, 2026 16:57
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
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>
@alex-clickhouse alex-clickhouse self-assigned this Aug 28, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants