From 29ac668668c93679a5eeb03464ce79dba3075ac4 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 11 Jan 2026 16:48:59 +0000 Subject: [PATCH 1/5] Initial plan From 70ea2500989f7dfe0e095fd4bd3970009166e727 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 11 Jan 2026 16:52:06 +0000 Subject: [PATCH 2/5] Fix /newchat command reply to wrong thread and add logging improvements Co-authored-by: BukeLy <19304666+BukeLy@users.noreply.github.com> --- agent-sdk-client/consumer.py | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/agent-sdk-client/consumer.py b/agent-sdk-client/consumer.py index 5b7e6c8..0192c89 100644 --- a/agent-sdk-client/consumer.py +++ b/agent-sdk-client/consumer.py @@ -43,6 +43,7 @@ async def process_message(message_data: dict) -> None: """Process single message from SQS queue.""" import logging logger = logging.getLogger() + logger.setLevel(logging.INFO) config = Config.from_env() bot = Bot(config.telegram_token) @@ -162,23 +163,40 @@ async def process_message(message_data: dict) -> None: text = text[:4000] + "\n\n... (truncated)" # Send response to Telegram + # Only reply to original message if we're in the same thread + reply_to_id = ( + message.message_id + if thread_id == message.message_thread_id + else None + ) + try: await bot.send_message( chat_id=message.chat_id, text=text, parse_mode=ParseMode.MARKDOWN_V2, message_thread_id=thread_id, - reply_to_message_id=message.message_id, + reply_to_message_id=reply_to_id, + ) + logger.info( + f"Message sent successfully to chat_id={message.chat_id}, " + f"thread_id={thread_id}, reply_to={reply_to_id}" ) except BadRequest as e: if "parse entities" in str(e).lower(): + logger.warning(f"Markdown parse error, retrying with escaped text: {e}") safe_text = escape_markdown(text, version=2) await bot.send_message( chat_id=message.chat_id, text=safe_text, parse_mode=ParseMode.MARKDOWN_V2, message_thread_id=thread_id, - reply_to_message_id=message.message_id, + reply_to_message_id=reply_to_id, + ) + logger.info( + f"Message sent successfully (escaped) to chat_id={message.chat_id}, " + f"thread_id={thread_id}, reply_to={reply_to_id}" ) else: + logger.error(f"Failed to send message: {e}") raise From 3f42aa2d3e20b1927a7c043e3f6bb66c7af95000 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 11 Jan 2026 16:54:09 +0000 Subject: [PATCH 3/5] Apply reply_to_message_id fix to local command handlers as well Co-authored-by: BukeLy <19304666+BukeLy@users.noreply.github.com> --- agent-sdk-client/consumer.py | 30 +++++++++++++++++++++--------- 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/agent-sdk-client/consumer.py b/agent-sdk-client/consumer.py index 0192c89..6817536 100644 --- a/agent-sdk-client/consumer.py +++ b/agent-sdk-client/consumer.py @@ -56,6 +56,10 @@ async def process_message(message_data: dict) -> None: logger.warning("Received update with no message or edited_message") return + # Use message_data fields for SQS message (allows handler to override text/thread_id) + user_message = message_data.get('text') or message.text + thread_id = message_data.get('thread_id') or message.message_thread_id + cmd = config.get_command(message.text) if cmd: if config.is_local_command(cmd): @@ -63,12 +67,18 @@ async def process_message(message_data: dict) -> None: "Handling local command in consumer (fallback path)", extra={'chat_id': message.chat_id, 'message_id': message.message_id}, ) + # Only reply to original message if we're in the same thread + reply_to_id = ( + message.message_id + if thread_id == message.message_thread_id + else None + ) try: await bot.send_message( chat_id=message.chat_id, text=config.local_response(cmd), - message_thread_id=message.message_thread_id, - reply_to_message_id=message.message_id, + message_thread_id=thread_id, + reply_to_message_id=reply_to_id, ) except Exception: logger.warning("Failed to send local command response", exc_info=True) @@ -83,12 +93,18 @@ async def process_message(message_data: dict) -> None: 'message_id': message.message_id, }, ) + # Only reply to original message if we're in the same thread + reply_to_id = ( + message.message_id + if thread_id == message.message_thread_id + else None + ) try: await bot.send_message( chat_id=message.chat_id, text=config.unknown_command_message(), - message_thread_id=message.message_thread_id, - reply_to_message_id=message.message_id, + message_thread_id=thread_id, + reply_to_message_id=reply_to_id, ) except Exception: logger.warning("Failed to send local command response", exc_info=True) @@ -98,7 +114,7 @@ async def process_message(message_data: dict) -> None: await bot.send_chat_action( chat_id=message.chat_id, action=ChatAction.TYPING, - message_thread_id=message.message_thread_id, + message_thread_id=thread_id, ) # Initialize result with default error response @@ -109,10 +125,6 @@ async def process_message(message_data: dict) -> None: 'error_message': 'Failed to get response from Agent Server' } - # Use message_data fields for SQS message (allows handler to override text/thread_id) - user_message = message_data.get('text') or message.text - thread_id = message_data.get('thread_id') or message.message_thread_id - # Call Agent Server try: async with httpx.AsyncClient(timeout=600.0) as client: From b41ae6c4366551cef8c4f7bcfb1babc620435505 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 11 Jan 2026 16:55:47 +0000 Subject: [PATCH 4/5] Refactor: Extract reply_to_id logic into helper function and improve comments Co-authored-by: BukeLy <19304666+BukeLy@users.noreply.github.com> --- agent-sdk-client/consumer.py | 42 ++++++++++++++++++++---------------- 1 file changed, 23 insertions(+), 19 deletions(-) diff --git a/agent-sdk-client/consumer.py b/agent-sdk-client/consumer.py index 6817536..f250da4 100644 --- a/agent-sdk-client/consumer.py +++ b/agent-sdk-client/consumer.py @@ -15,6 +15,23 @@ from config import Config +def _get_reply_to_id(message_id: int, thread_id: int | None, message_thread_id: int | None) -> int | None: + """Determine if we should reply to the original message. + + Only reply to the original message if we're in the same thread. + This prevents Telegram API errors when sending to a different thread (e.g., /newchat). + + Args: + message_id: The original message ID + thread_id: The target thread ID (may be overridden by handler) + message_thread_id: The original message's thread ID + + Returns: + message_id if in same thread, None otherwise + """ + return message_id if thread_id == message_thread_id else None + + def lambda_handler(event: dict, context: Any) -> dict: """SQS Consumer Lambda entry point.""" for record in event['Records']: @@ -43,6 +60,7 @@ async def process_message(message_data: dict) -> None: """Process single message from SQS queue.""" import logging logger = logging.getLogger() + # Enable INFO logging as suggested in issue for better debugging logger.setLevel(logging.INFO) config = Config.from_env() @@ -56,7 +74,8 @@ async def process_message(message_data: dict) -> None: logger.warning("Received update with no message or edited_message") return - # Use message_data fields for SQS message (allows handler to override text/thread_id) + # Extract thread_id and user_message early - needed for all message processing + # (allows handler to override text/thread_id via SQS message_data) user_message = message_data.get('text') or message.text thread_id = message_data.get('thread_id') or message.message_thread_id @@ -67,12 +86,7 @@ async def process_message(message_data: dict) -> None: "Handling local command in consumer (fallback path)", extra={'chat_id': message.chat_id, 'message_id': message.message_id}, ) - # Only reply to original message if we're in the same thread - reply_to_id = ( - message.message_id - if thread_id == message.message_thread_id - else None - ) + reply_to_id = _get_reply_to_id(message.message_id, thread_id, message.message_thread_id) try: await bot.send_message( chat_id=message.chat_id, @@ -93,12 +107,7 @@ async def process_message(message_data: dict) -> None: 'message_id': message.message_id, }, ) - # Only reply to original message if we're in the same thread - reply_to_id = ( - message.message_id - if thread_id == message.message_thread_id - else None - ) + reply_to_id = _get_reply_to_id(message.message_id, thread_id, message.message_thread_id) try: await bot.send_message( chat_id=message.chat_id, @@ -175,12 +184,7 @@ async def process_message(message_data: dict) -> None: text = text[:4000] + "\n\n... (truncated)" # Send response to Telegram - # Only reply to original message if we're in the same thread - reply_to_id = ( - message.message_id - if thread_id == message.message_thread_id - else None - ) + reply_to_id = _get_reply_to_id(message.message_id, thread_id, message.message_thread_id) try: await bot.send_message( From 5f2ed4f9d518773afbbd85c3308a2da28ae04814 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 11 Jan 2026 16:56:56 +0000 Subject: [PATCH 5/5] Apply reply_to_id fix to error and timeout messages for consistency Co-authored-by: BukeLy <19304666+BukeLy@users.noreply.github.com> --- agent-sdk-client/consumer.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/agent-sdk-client/consumer.py b/agent-sdk-client/consumer.py index f250da4..34c57b8 100644 --- a/agent-sdk-client/consumer.py +++ b/agent-sdk-client/consumer.py @@ -154,21 +154,25 @@ async def process_message(message_data: dict) -> None: except httpx.TimeoutException: logger.warning(f"Agent Server timeout for chat_id={message.chat_id}") + reply_to_id = _get_reply_to_id(message.message_id, thread_id, message.message_thread_id) await bot.send_message( chat_id=message.chat_id, text="Request timed out.", message_thread_id=thread_id, + reply_to_message_id=reply_to_id, ) raise # Re-raise to trigger SQS retry for transient errors except Exception as e: logger.exception(f"Agent Server error for chat_id={message.chat_id}") error_text = f"Error: {str(e)[:200]}" + reply_to_id = _get_reply_to_id(message.message_id, thread_id, message.message_thread_id) try: await bot.send_message( chat_id=message.chat_id, text=error_text, message_thread_id=thread_id, + reply_to_message_id=reply_to_id, ) except Exception as send_error: logger.error(f"Failed to send error message to Telegram: {send_error}")