diff --git a/gateway/platforms/base.py b/gateway/platforms/base.py index 21a806c1c8..16e7daf3b2 100644 --- a/gateway/platforms/base.py +++ b/gateway/platforms/base.py @@ -196,7 +196,7 @@ class BasePlatformAdapter(ABC): if not self._message_handler: return - # Start continuous typing indicator (refreshes every 4 seconds) + # Start continuous typing indicator (refreshes every 2 seconds) typing_task = asyncio.create_task(self._keep_typing(event.source.chat_id)) try: @@ -205,13 +205,27 @@ class BasePlatformAdapter(ABC): # Send response if any if response: - await self.send( + result = await self.send( chat_id=event.source.chat_id, content=response, reply_to=event.message_id ) + + # Log send failures (don't raise - user already saw tool progress) + if not result.success: + print(f"[{self.name}] Failed to send response: {result.error}") + # Try sending without markdown as fallback + fallback_result = await self.send( + chat_id=event.source.chat_id, + content=f"(Response formatting failed, plain text:)\n\n{response[:3500]}", + reply_to=event.message_id + ) + if not fallback_result.success: + print(f"[{self.name}] Fallback send also failed: {fallback_result.error}") except Exception as e: print(f"[{self.name}] Error handling message: {e}") + import traceback + traceback.print_exc() finally: # Stop typing indicator typing_task.cancel() diff --git a/gateway/platforms/telegram.py b/gateway/platforms/telegram.py index ef46351c6a..10c67c96b9 100644 --- a/gateway/platforms/telegram.py +++ b/gateway/platforms/telegram.py @@ -142,13 +142,27 @@ class TelegramAdapter(BasePlatformAdapter): thread_id = metadata.get("thread_id") if metadata else None for i, chunk in enumerate(chunks): - msg = await self._bot.send_message( - chat_id=int(chat_id), - text=chunk, - parse_mode=ParseMode.MARKDOWN, - reply_to_message_id=int(reply_to) if reply_to and i == 0 else None, - message_thread_id=int(thread_id) if thread_id else None, - ) + # Try Markdown first, fall back to plain text if it fails + try: + msg = await self._bot.send_message( + chat_id=int(chat_id), + text=chunk, + parse_mode=ParseMode.MARKDOWN, + reply_to_message_id=int(reply_to) if reply_to and i == 0 else None, + message_thread_id=int(thread_id) if thread_id else None, + ) + except Exception as md_error: + # Markdown parsing failed, try plain text + if "parse" in str(md_error).lower() or "markdown" in str(md_error).lower(): + msg = await self._bot.send_message( + chat_id=int(chat_id), + text=chunk, + parse_mode=None, # Plain text + reply_to_message_id=int(reply_to) if reply_to and i == 0 else None, + message_thread_id=int(thread_id) if thread_id else None, + ) + else: + raise # Re-raise if not a parse error message_ids.append(str(msg.message_id)) return SendResult( diff --git a/gateway/run.py b/gateway/run.py index b2159e28e1..b10f4cf54e 100644 --- a/gateway/run.py +++ b/gateway/run.py @@ -446,11 +446,16 @@ class GatewayRunner: tool_progress_callback=progress_callback if tool_progress_enabled else None, ) - # If we have history, we need to restore it - # For now, we pass the message directly - # TODO: Implement proper history restoration + # Convert transcript history to agent format + # Transcript has timestamps; agent expects {"role": ..., "content": ...} + agent_history = [] + for msg in history: + role = msg.get("role") + content = msg.get("content") + if role and content: + agent_history.append({"role": role, "content": content}) - result = agent.run_conversation(message) + result = agent.run_conversation(message, conversation_history=agent_history) # Return final response, or a message if something went wrong final_response = result.get("final_response") diff --git a/run_agent.py b/run_agent.py index 502e6f60f7..72541f6bdb 100644 --- a/run_agent.py +++ b/run_agent.py @@ -1272,9 +1272,16 @@ class AIAgent: return try: - # Convert to trajectory format (reuse existing method) - # Use empty string as user_query since it's embedded in messages - trajectory = self._convert_to_trajectory_format(messages, "", True) + # Extract the first user message for the trajectory format + # The first message should be the user's initial query + first_user_query = "" + for msg in messages: + if msg.get("role") == "user": + first_user_query = msg.get("content", "") + break + + # Convert to trajectory format + trajectory = self._convert_to_trajectory_format(messages, first_user_query, True) # Build the session log entry entry = {