feat(telegram): Secretary Mode — business_message support + bot-echo classification + send-as-owner - #60809
feat(telegram): Secretary Mode — business_message support + bot-echo classification + send-as-owner#60809bgodlin wants to merge 14 commits into
Conversation
7fd0411 to
c3660f9
Compare
Competing Telegram Business / Secretary Mode cluster (all OPEN, overlapping ground, different mechanisms/locations): #46728 (gateway/platforms/telegram.py implementation), #30055 (observe-with-approval draft replies), and issue #42400 (the bot-echo classification this PR addresses). Marking |
teknium1
left a comment
There was a problem hiding this comment.
Thanks for assembling the ingress, metadata, send, docs, and bot-echo pieces. The feature remains relevant on current main, but the current patch has several blockers.
Problems
tools/send_message_tool.py:804referencesbusiness_connection_id, but_send_to_platform()has no such argument or local._handle_send()defines it at line 302 but does not forward it at lines 437-445; standalone Telegram sends will raiseNameError.plugins/platforms/telegram/adapter.py:8398-8404suppresses onlysender_business_botechoes. #42400 requires manual owner outgoing messages to be retained as context but not enqueued; messages with nosender_business_botstill take the normal agent path.gateway/session.py:242-298does not serialize the newbusiness_connection_id, so restored sources lose send-as-owner routing.- Current main's media handler still reads
update.messagedirectly atplugins/platforms/telegram/adapter.py:7752; business media needs the effective-message path and coverage.
Suggested changes
- Thread and test the standalone CLI argument end-to-end.
- Add explicit owner/bot/customer classification plus source persistence tests.
- Adapt all inbound handler paths, including media, to current adapter conventions.
Automated hermes-sweeper review.
| @@ -800,6 +801,7 @@ async def _send_to_platform(platform, pconfig, chat_id, message, thread_id=None, | |||
| thread_id=thread_id, | |||
| disable_link_previews=disable_link_previews, | |||
| force_document=force_document, | |||
| business_connection_id=business_connection_id, | |||
There was a problem hiding this comment.
business_connection_id is not in _send_to_platform's signature and is never forwarded from _handle_send, so this Telegram branch raises NameError. Thread the value through both call boundaries and add a standalone send_message regression test.
639a9eb to
a0695cb
Compare
Foundation for Secretary Mode support. Carries the connection ID through the session lifecycle so the send path can reply as the business owner's personal account.
The adapter now reads business_connection_id from the incoming message and passes it through build_source to SessionSource. This is the receive side of Secretary Mode — the ID is available to the session for later use in the send path.
Business messages carry a server-set business_connection_id that proves the owner connected the bot via Chat Automation. The external client's user ID will never be in the allowlist, so without this bypass every business message is rejected as unauthorized. The ID cannot be spoofed by the client — it's assigned by Telegram server-side.
When replying to a Secretary Mode message, the send() method now passes business_connection_id to bot.send_message. This makes the reply appear as the business owner's personal account rather than the bot. Only applied when the ID is present in metadata.
_thread_metadata_for_source now includes business_connection_id when the source carries it. This is the bridge between the receive path (SessionSource) and the send path (adapter.send), ensuring replies to Secretary Mode messages are routed as the business owner.
Adds _handle_business_connection handler that tracks active Secretary
Mode connections. Stores {can_reply, user_chat_id} per connection ID
on connect, removes on disconnect. Registered as a TypeHandler in
group -1 so it runs before message handlers.
_build_message_event returns None when sender_business_bot is present, so the gateway skips its own relayed sends instead of re-processing them as customer input. Guards added at all 6 call sites. Customer inbound and manual owner outgoing fall through to the normal flow — owner-outgoing classification needs the connection owner_id, which arrives via the BusinessConnection lifecycle handler. Docs: add references to the Telegram blog (AI Bot Revolution, May 2026) and the connected-business-bots API docs.
hermes send --business-connection-id <ID> sends as the business owner's personal account via Telegram's Chat Automation, not as the bot. Threads through send_message_tool → _send_telegram → Bot API sendMessage. Enables the standalone CLI to reply in Secretary Mode chats without a running gateway, complementing the gateway adapter's send path.
Adds a section pointing to the telegram-secretary-mode guide so users discovering business/Chat Automation features find it from the main doc.
The Secretary Mode patch referenced business_connection_id inside _send_to_platform without adding it to the signature, and _handle_send never forwarded the value it extracted — a NameError on every standalone Telegram send, business or not, and a dead --business-connection-id flag. - Add business_connection_id as an optional _send_to_platform parameter and forward it from _handle_send (cron callers are unaffected). - Reject the flag for non-Telegram targets instead of silently dropping the send-as-owner intent. - Carry the connection ID on _send_telegram media sends too, so attachments are delivered as the owner like the text they accompany. - Regression tests: plain send NameError, tool-arg threading end to end, CLI flag delivery, media kwargs, non-Telegram rejection.
The field was declared but never emitted by to_dict or restored by from_dict, so any source rehydrated from persistence lost send-as-owner routing and replies went out as the bot. Round-trip tests live in tests/gateway/test_telegram_business_message.py.
…sResearch#42400) Business updates were only guarded against sender_business_bot echoes; the owner's own manual replies still entered the agent path as prompts, and business media never arrived at all because _handle_media_message read update.message (business media comes in as update.business_message). Intake: - _classify_business_message: bot_echo / owner_outgoing / customer. Owner detection prefers tracked BusinessConnection state and falls back to the from_user-vs-chat-peer structural signal, which survives gateway restarts (Telegram does not replay connection updates). - Owner manual replies are appended to the customer chat's session as observed context (_observe_business_owner_message) — the agent sees the owner already answered — but are never enqueued as prompts. - Customer slash commands are routed to the agent as plain text: the business connection pre-authorizes the conversation, not operator authority, so the auth bypass must not reach the command executor. - _handle_media_message resolves messages via effective_message, so business media takes the normal download/caching path; owner media is observed with its caption, echoes are dropped. - _business_connection_id_of accepts only non-empty strings, so non-business traffic (and test doubles' auto-attributes) can never be misclassified; test_telegram_documents' update mock now mirrors real PTB semantics (effective_message == message). Send: - _business_kwargs applied to photo/document/video/voice/audio/album/ animation sends: attachments ride the same business connection as the text instead of being delivered as the bot. - supports_draft_streaming returns False for business sends — the draft API has no business_connection_id — so replies take the plain send path end to end.
a0695cb to
7b8b215
Compare
|
@teknium1 the PR is ready for review again |
Summary
Adds Telegram Secretary Mode (Chat Automation / Connected Business Bots) support to the gateway and CLI. A connected bot can now receive `business_message` updates sent to a user's personal account and reply on their behalf.
Closes #42400.
What's included
Ingress (gateway adapter):
Send path (gateway + CLI):
Docs:
Tests: 10 tests covering SessionSource field, auth bypass, send-path passthrough, lifecycle handler, and bot-echo classification.
References
Constraints