diff --git a/gateway/platforms/discord.py b/gateway/platforms/discord.py index 141e1110b2..5d7397114f 100644 --- a/gateway/platforms/discord.py +++ b/gateway/platforms/discord.py @@ -206,7 +206,29 @@ class DiscordAdapter(BasePlatformAdapter): except Exception as e: return SendResult(success=False, error=str(e)) - + + async def edit_message( + self, + chat_id: str, + message_id: str, + content: str, + ) -> SendResult: + """Edit a previously sent Discord message.""" + if not self._client: + return SendResult(success=False, error="Not connected") + try: + channel = self._client.get_channel(int(chat_id)) + if not channel: + channel = await self._client.fetch_channel(int(chat_id)) + msg = await channel.fetch_message(int(message_id)) + formatted = self.format_message(content) + if len(formatted) > self.MAX_MESSAGE_LENGTH: + formatted = formatted[:self.MAX_MESSAGE_LENGTH - 3] + "..." + await msg.edit(content=formatted) + return SendResult(success=True, message_id=message_id) + except Exception as e: + return SendResult(success=False, error=str(e)) + async def send_voice( self, chat_id: str, diff --git a/gateway/platforms/slack.py b/gateway/platforms/slack.py index be5f5045bf..85562cbb6b 100644 --- a/gateway/platforms/slack.py +++ b/gateway/platforms/slack.py @@ -156,6 +156,25 @@ class SlackAdapter(BasePlatformAdapter): print(f"[Slack] Send error: {e}") return SendResult(success=False, error=str(e)) + async def edit_message( + self, + chat_id: str, + message_id: str, + content: str, + ) -> SendResult: + """Edit a previously sent Slack message.""" + if not self._app: + return SendResult(success=False, error="Not connected") + try: + await self._app.client.chat_update( + channel=chat_id, + ts=message_id, + text=content, + ) + return SendResult(success=True, message_id=message_id) + except Exception as e: + return SendResult(success=False, error=str(e)) + async def send_typing(self, chat_id: str) -> None: """Slack doesn't have a direct typing indicator API for bots.""" pass diff --git a/gateway/platforms/telegram.py b/gateway/platforms/telegram.py index 6aed90389d..757d9d38ec 100644 --- a/gateway/platforms/telegram.py +++ b/gateway/platforms/telegram.py @@ -218,7 +218,36 @@ class TelegramAdapter(BasePlatformAdapter): except Exception as e: return SendResult(success=False, error=str(e)) - + + async def edit_message( + self, + chat_id: str, + message_id: str, + content: str, + ) -> SendResult: + """Edit a previously sent Telegram message.""" + if not self._bot: + return SendResult(success=False, error="Not connected") + try: + formatted = self.format_message(content) + try: + await self._bot.edit_message_text( + chat_id=int(chat_id), + message_id=int(message_id), + text=formatted, + parse_mode=ParseMode.MARKDOWN_V2, + ) + except Exception: + # Fallback: retry without markdown formatting + await self._bot.edit_message_text( + chat_id=int(chat_id), + message_id=int(message_id), + text=content, + ) + return SendResult(success=True, message_id=message_id) + except Exception as e: + return SendResult(success=False, error=str(e)) + async def send_voice( self, chat_id: str, diff --git a/gateway/run.py b/gateway/run.py index 38d831f413..2ed9ed8c2e 100644 --- a/gateway/run.py +++ b/gateway/run.py @@ -1950,30 +1950,36 @@ class GatewayRunner: progress_lines = [] # Accumulated tool lines progress_msg_id = None # ID of the progress message to edit + can_edit = True # False once an edit fails (platform doesn't support it) while True: try: msg = progress_queue.get_nowait() progress_lines.append(msg) - full_text = "\n".join(progress_lines) - if progress_msg_id is None: - # First tool: send as new message - result = await adapter.send(chat_id=source.chat_id, content=full_text) - if result.success and result.message_id: - progress_msg_id = result.message_id - else: - # Subsequent tools: try to edit, fall back to new message + if can_edit and progress_msg_id is not None: + # Try to edit the existing progress message + full_text = "\n".join(progress_lines) result = await adapter.edit_message( chat_id=source.chat_id, message_id=progress_msg_id, content=full_text, ) if not result.success: - # Edit failed — send as new message and track it + # Platform doesn't support editing — stop trying, + # send just this new line as a separate message + can_edit = False + await adapter.send(chat_id=source.chat_id, content=msg) + else: + if can_edit: + # First tool: send all accumulated text as new message + full_text = "\n".join(progress_lines) result = await adapter.send(chat_id=source.chat_id, content=full_text) - if result.success and result.message_id: - progress_msg_id = result.message_id + else: + # Editing unsupported: send just this line + result = await adapter.send(chat_id=source.chat_id, content=msg) + if result.success and result.message_id: + progress_msg_id = result.message_id # Restore typing indicator await asyncio.sleep(0.3) @@ -1989,8 +1995,8 @@ class GatewayRunner: progress_lines.append(msg) except Exception: break - # Final edit with all remaining tools - if progress_lines and progress_msg_id: + # Final edit with all remaining tools (only if editing works) + if can_edit and progress_lines and progress_msg_id: full_text = "\n".join(progress_lines) try: await adapter.edit_message(