diff --git a/gateway/platforms/telegram.py b/gateway/platforms/telegram.py index d5578961c..2f4ec9329 100644 --- a/gateway/platforms/telegram.py +++ b/gateway/platforms/telegram.py @@ -11,6 +11,7 @@ import asyncio import json import logging import os +import html as _html import re from typing import Dict, List, Optional, Any @@ -1129,13 +1130,10 @@ class TelegramAdapter(BasePlatformAdapter): try: cmd_preview = command[:3800] + "..." if len(command) > 3800 else command - # Escape backticks that would break Markdown v1 inline code parsing - safe_cmd = cmd_preview.replace("`", "'") - safe_desc = description.replace("`", "'").replace("*", "∗") text = ( - f"⚠️ *Command Approval Required*\n\n" - f"`{safe_cmd}`\n\n" - f"Reason: {safe_desc}" + f"⚠️ Command Approval Required\n\n" + f"
{_html.escape(cmd_preview)}
\n\n" + f"Reason: {_html.escape(description)}" ) # Resolve thread context for thread replies @@ -1163,7 +1161,7 @@ class TelegramAdapter(BasePlatformAdapter): kwargs: Dict[str, Any] = { "chat_id": int(chat_id), "text": text, - "parse_mode": ParseMode.MARKDOWN, + "parse_mode": ParseMode.HTML, "reply_markup": keyboard, **self._link_preview_kwargs(), } diff --git a/gateway/run.py b/gateway/run.py index 7517fdabd..7e4cc26a7 100644 --- a/gateway/run.py +++ b/gateway/run.py @@ -8816,7 +8816,7 @@ class GatewayRunner: # false positives from MagicMock auto-attribute creation in tests. if getattr(type(_status_adapter), "send_exec_approval", None) is not None: try: - asyncio.run_coroutine_threadsafe( + _approval_result = asyncio.run_coroutine_threadsafe( _status_adapter.send_exec_approval( chat_id=_status_chat_id, command=cmd, @@ -8826,7 +8826,12 @@ class GatewayRunner: ), _loop_for_step, ).result(timeout=15) - return + if _approval_result.success: + return + logger.warning( + "Button-based approval failed (send returned error), falling back to text: %s", + _approval_result.error, + ) except Exception as _e: logger.warning( "Button-based approval failed, falling back to text: %s", _e