From ad66a1ed5261b71fd46c6dc227d57bf3c533d738 Mon Sep 17 00:00:00 2001 From: byjk Date: Thu, 9 Apr 2026 16:30:22 +0200 Subject: [PATCH 1/3] feat(gateway): silent tool progress + auto-delete progress messages - Telegram: send tool progress with disable_notification=True (no sound/vibration) - Telegram: add delete_message() via bot.delete_message() - Base: add delete_message() stub - Gateway: auto-delete progress messages after final response - Config: add display.delete_progress_messages option --- gateway/platforms/base.py | 11 +++++++++++ gateway/platforms/telegram.py | 18 ++++++++++++++++++ gateway/run.py | 21 +++++++++++++++++++++ hermes_cli/config.py | 1 + 4 files changed, 51 insertions(+) diff --git a/gateway/platforms/base.py b/gateway/platforms/base.py index 1561cd526f0e..0dc3e5e75cd4 100644 --- a/gateway/platforms/base.py +++ b/gateway/platforms/base.py @@ -988,6 +988,17 @@ async def edit_message( """ return SendResult(success=False, error="Not supported") + async def delete_message( + self, + chat_id: str, + message_id: str, + ) -> bool: + """ + Delete a previously sent message. Optional — platforms that don't + support deletion return False. + """ + return False + async def send_typing(self, chat_id: str, metadata=None) -> None: """ Send a typing indicator. diff --git a/gateway/platforms/telegram.py b/gateway/platforms/telegram.py index 112b232d0a49..2f168566c25c 100644 --- a/gateway/platforms/telegram.py +++ b/gateway/platforms/telegram.py @@ -1021,6 +1021,24 @@ async def edit_message( ) return SendResult(success=False, error=str(e)) + async def delete_message( + self, + chat_id: str, + message_id: str, + ) -> bool: + """Delete a previously sent Telegram message.""" + if not self._bot: + return False + try: + await self._bot.delete_message( + chat_id=int(chat_id), + message_id=int(message_id), + ) + return True + except Exception as e: + logger.debug("[%s] Failed to delete message %s: %s", self.name, message_id, e) + return False + async def send_update_prompt( self, chat_id: str, prompt: str, default: str = "", session_key: str = "", diff --git a/gateway/run.py b/gateway/run.py index 2eb745f92bd1..d73511504e3d 100644 --- a/gateway/run.py +++ b/gateway/run.py @@ -8047,6 +8047,7 @@ async def _run_agent( last_tool = [None] # Mutable container for tracking in closure last_progress_msg = [None] # Track last message for dedup repeat_count = [0] # How many times the same message repeated + progress_msg_id_holder = [None] # Track progress message ID for deletion def progress_callback(event_type: str, tool_name: str = None, preview: str = None, args: dict = None, **kwargs): """Callback invoked by agent on tool lifecycle events.""" @@ -8210,6 +8211,7 @@ async def send_progress_messages(): result = await adapter.send(chat_id=source.chat_id, content=msg, metadata=_progress_metadata) if result.success and result.message_id: progress_msg_id = result.message_id + progress_msg_id_holder[0] = progress_msg_id _last_edit_ts = time.monotonic() @@ -9281,6 +9283,25 @@ async def _notify_long_running(): # Stop progress sender, interrupt monitor, and notification task if progress_task: progress_task.cancel() + + # Auto-delete progress message after agent finishes (if enabled) + _delete_progress = False + try: + _display_cfg = user_config.get("display", {}) + if isinstance(_display_cfg, dict): + _delete_progress = _display_cfg.get("delete_progress_messages", False) + except Exception: + pass + if _delete_progress and progress_msg_id_holder[0]: + try: + adapter = self.adapters.get(source.platform) + if adapter: + await adapter.delete_message( + chat_id=source.chat_id, + message_id=progress_msg_id_holder[0], + ) + except Exception: + pass interrupt_monitor.cancel() _notify_task.cancel() diff --git a/hermes_cli/config.py b/hermes_cli/config.py index d06338aa14e4..9ed0158edb31 100644 --- a/hermes_cli/config.py +++ b/hermes_cli/config.py @@ -508,6 +508,7 @@ def _ensure_hermes_home_managed(home: Path): "tool_progress_overrides": {}, # DEPRECATED — use display.platforms instead "tool_preview_length": 0, # Max chars for tool call previews (0 = no limit, show full paths/commands) "platforms": {}, # Per-platform display overrides: {"telegram": {"tool_progress": "all"}, "slack": {"tool_progress": "off"}} + "delete_progress_messages": False, # Auto-delete progress messages after final response }, # Privacy settings From 5ae237d596b56436f9161e62a3066cc81f6c56ad Mon Sep 17 00:00:00 2001 From: byjk Date: Sun, 12 Apr 2026 19:01:27 +0200 Subject: [PATCH 2/3] fix(gateway): restore disable_notification=True for tool progress messages MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Silent mode was lost during merge. This patch restores: - telegram.py: read metadata["silent"] → disable_notification in send_message - run.py: set silent=True in _progress_metadata for all progress messages --- gateway/platforms/telegram.py | 3 +++ gateway/run.py | 6 +++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/gateway/platforms/telegram.py b/gateway/platforms/telegram.py index 2f168566c25c..cbadf1308f7a 100644 --- a/gateway/platforms/telegram.py +++ b/gateway/platforms/telegram.py @@ -815,6 +815,7 @@ async def send( message_ids = [] thread_id = metadata.get("thread_id") if metadata else None + silent = metadata.get("silent", False) if metadata else False try: from telegram.error import NetworkError as _NetErr @@ -847,6 +848,7 @@ async def send( parse_mode=ParseMode.MARKDOWN_V2, reply_to_message_id=reply_to_id, message_thread_id=effective_thread_id, + disable_notification=silent, ) except Exception as md_error: # Markdown parsing failed, try plain text @@ -859,6 +861,7 @@ async def send( parse_mode=None, reply_to_message_id=reply_to_id, message_thread_id=effective_thread_id, + disable_notification=silent, ) else: raise diff --git a/gateway/run.py b/gateway/run.py index d73511504e3d..40be93c4a2f2 100644 --- a/gateway/run.py +++ b/gateway/run.py @@ -8126,7 +8126,11 @@ def progress_callback(event_type: str, tool_name: str = None, preview: str = Non _progress_thread_id = source.thread_id or event_message_id else: _progress_thread_id = source.thread_id - _progress_metadata = {"thread_id": _progress_thread_id} if _progress_thread_id else None + # Silent mode: tool progress messages are sent without notification + # so the user isn't spammed with sounds/vibrations for every tool call. + _progress_metadata = {"silent": True} + if _progress_thread_id: + _progress_metadata["thread_id"] = _progress_thread_id async def send_progress_messages(): if not progress_queue: From e6f2b0b5b8178f394d629194828eec8952aead95 Mon Sep 17 00:00:00 2001 From: byjk Date: Wed, 15 Apr 2026 12:40:04 +0200 Subject: [PATCH 3/3] feat(telegram): disable link preview for tool progress messages Adds no_preview metadata flag to suppress web page previews in progress messages, keeping them compact and distraction-free. --- gateway/platforms/telegram.py | 3 +++ gateway/run.py | 3 ++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/gateway/platforms/telegram.py b/gateway/platforms/telegram.py index cbadf1308f7a..4929b10dd475 100644 --- a/gateway/platforms/telegram.py +++ b/gateway/platforms/telegram.py @@ -816,6 +816,7 @@ async def send( message_ids = [] thread_id = metadata.get("thread_id") if metadata else None silent = metadata.get("silent", False) if metadata else False + no_preview = metadata.get("no_preview", False) if metadata else False try: from telegram.error import NetworkError as _NetErr @@ -849,6 +850,7 @@ async def send( reply_to_message_id=reply_to_id, message_thread_id=effective_thread_id, disable_notification=silent, + disable_web_page_preview=no_preview, ) except Exception as md_error: # Markdown parsing failed, try plain text @@ -862,6 +864,7 @@ async def send( reply_to_message_id=reply_to_id, message_thread_id=effective_thread_id, disable_notification=silent, + disable_web_page_preview=no_preview, ) else: raise diff --git a/gateway/run.py b/gateway/run.py index 40be93c4a2f2..c844378cb05f 100644 --- a/gateway/run.py +++ b/gateway/run.py @@ -8128,7 +8128,8 @@ def progress_callback(event_type: str, tool_name: str = None, preview: str = Non _progress_thread_id = source.thread_id # Silent mode: tool progress messages are sent without notification # so the user isn't spammed with sounds/vibrations for every tool call. - _progress_metadata = {"silent": True} + # Link preview disabled to keep progress messages compact. + _progress_metadata = {"silent": True, "no_preview": True} if _progress_thread_id: _progress_metadata["thread_id"] = _progress_thread_id