feat: reply-to-agent detection in Gateway → SOWA Priority 3

When a user replies to an agent's message in Telegram groups,
it is now treated as a direct mention (SOWA FULL response).

Implementation:
- Detect reply_to_message.from.is_bot in Gateway webhook handler
- Verify bot_id matches this agent's token (multi-agent safe)
- Pass is_reply_to_agent=True to detect_explicit_request() and
  analyze_message() (SOWA v2.2)
- Add is_reply_to_agent to Router metadata for analytics

SOWA already had Priority 3 logic for reply_to_agent → FULL,
it was just never wired up (had TODO placeholders with False).

Edge cases handled:
- Only triggers when reply is to THIS agent's bot (not other bots)
- Reply to forwarded messages: won't trigger (from.is_bot would be
  the original sender, not the bot)
- Works alongside existing DM, mention, and training group rules

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Apple
2026-02-09 09:16:02 -08:00
parent aee2a55a26
commit 1f4472ec18

View File

@@ -1347,6 +1347,23 @@ async def handle_telegram_webhook(
if not telegram_token:
raise HTTPException(status_code=500, detail=f"Telegram token not configured for {agent_config.name}")
# === REPLY-TO-AGENT DETECTION ===
# If user replies to a bot message → treat as direct mention (SOWA Priority 3)
is_reply_to_agent = False
reply_to_message = update.message.get("reply_to_message")
if reply_to_message:
reply_from = reply_to_message.get("from", {})
if reply_from.get("is_bot"):
# Verify it's THIS agent's bot (bot_id = first part of token)
bot_id = telegram_token.split(":")[0] if telegram_token else None
reply_from_id = str(reply_from.get("id", ""))
if bot_id and reply_from_id == bot_id:
is_reply_to_agent = True
logger.info(
f"↩️ {agent_config.name}: Reply-to-agent detected "
f"(user {username} replied to bot msg {reply_to_message.get('message_id', '?')})"
)
text = update.message.get("text", "")
# Simple brand commands (Ukrainian)
@@ -2014,7 +2031,7 @@ async def handle_telegram_webhook(
has_explicit_request = detect_explicit_request(
text=text,
is_dm=is_private_chat,
is_reply_to_agent=False, # TODO: detect from Telegram reply_to_message
is_reply_to_agent=is_reply_to_agent,
mentioned_agents=mentioned_agents,
thread_has_agent_participation=False, # REQUIRED, fail-closed default
)
@@ -2033,7 +2050,7 @@ async def handle_telegram_webhook(
is_private_chat=is_private_chat,
payload_explicit_request=has_explicit_request,
payload_has_link=has_link,
is_reply_to_agent=False, # TODO: detect from Telegram reply_to_message
is_reply_to_agent=is_reply_to_agent,
thread_has_agent_participation=False, # TODO: track per thread
)
respond_decision = sowa_decision.should_respond
@@ -2161,6 +2178,7 @@ async def handle_telegram_webhook(
"sender_is_bot": is_sender_bot,
"mentioned_bots": mentioned_bots,
"requires_complex_reasoning": needs_complex_reasoning,
"is_reply_to_agent": is_reply_to_agent,
},
"context": {
"agent_name": agent_config.name,