mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-05-05 10:17:17 +08:00
Compare commits
1 Commits
fix/plugin
...
hermes/her
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
856d5bd69f |
@@ -485,6 +485,9 @@ class BasePlatformAdapter(ABC):
|
|||||||
self._background_tasks: set[asyncio.Task] = set()
|
self._background_tasks: set[asyncio.Task] = set()
|
||||||
# Chats where auto-TTS on voice input is disabled (set by /voice off)
|
# Chats where auto-TTS on voice input is disabled (set by /voice off)
|
||||||
self._auto_tts_disabled_chats: set = set()
|
self._auto_tts_disabled_chats: set = set()
|
||||||
|
# Chats where typing indicator is paused (e.g. during approval waits).
|
||||||
|
# _keep_typing skips send_typing when the chat_id is in this set.
|
||||||
|
self._typing_paused: set = set()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def has_fatal_error(self) -> bool:
|
def has_fatal_error(self) -> bool:
|
||||||
@@ -944,10 +947,16 @@ class BasePlatformAdapter(ABC):
|
|||||||
|
|
||||||
Telegram/Discord typing status expires after ~5 seconds, so we refresh every 2
|
Telegram/Discord typing status expires after ~5 seconds, so we refresh every 2
|
||||||
to recover quickly after progress messages interrupt it.
|
to recover quickly after progress messages interrupt it.
|
||||||
|
|
||||||
|
Skips send_typing when the chat is in ``_typing_paused`` (e.g. while
|
||||||
|
the agent is waiting for dangerous-command approval). This is critical
|
||||||
|
for Slack's Assistant API where ``assistant_threads_setStatus`` disables
|
||||||
|
the compose box — pausing lets the user type ``/approve`` or ``/deny``.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
while True:
|
while True:
|
||||||
await self.send_typing(chat_id, metadata=metadata)
|
if chat_id not in self._typing_paused:
|
||||||
|
await self.send_typing(chat_id, metadata=metadata)
|
||||||
await asyncio.sleep(interval)
|
await asyncio.sleep(interval)
|
||||||
except asyncio.CancelledError:
|
except asyncio.CancelledError:
|
||||||
pass # Normal cancellation when handler completes
|
pass # Normal cancellation when handler completes
|
||||||
@@ -961,6 +970,19 @@ class BasePlatformAdapter(ABC):
|
|||||||
await self.stop_typing(chat_id)
|
await self.stop_typing(chat_id)
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
|
self._typing_paused.discard(chat_id)
|
||||||
|
|
||||||
|
def pause_typing_for_chat(self, chat_id: str) -> None:
|
||||||
|
"""Pause typing indicator for a chat (e.g. during approval waits).
|
||||||
|
|
||||||
|
Thread-safe (CPython GIL) — can be called from the sync agent thread
|
||||||
|
while ``_keep_typing`` runs on the async event loop.
|
||||||
|
"""
|
||||||
|
self._typing_paused.add(chat_id)
|
||||||
|
|
||||||
|
def resume_typing_for_chat(self, chat_id: str) -> None:
|
||||||
|
"""Resume typing indicator for a chat after approval resolves."""
|
||||||
|
self._typing_paused.discard(chat_id)
|
||||||
|
|
||||||
# ── Processing lifecycle hooks ──────────────────────────────────────────
|
# ── Processing lifecycle hooks ──────────────────────────────────────────
|
||||||
# Subclasses override these to react to message processing events
|
# Subclasses override these to react to message processing events
|
||||||
|
|||||||
@@ -5431,6 +5431,11 @@ class GatewayRunner:
|
|||||||
if not count:
|
if not count:
|
||||||
return "No pending command to approve."
|
return "No pending command to approve."
|
||||||
|
|
||||||
|
# Resume typing indicator — agent is about to continue processing.
|
||||||
|
_adapter = self.adapters.get(source.platform)
|
||||||
|
if _adapter:
|
||||||
|
_adapter.resume_typing_for_chat(source.chat_id)
|
||||||
|
|
||||||
count_msg = f" ({count} commands)" if count > 1 else ""
|
count_msg = f" ({count} commands)" if count > 1 else ""
|
||||||
logger.info("User approved %d dangerous command(s) via /approve%s", count, scope_msg)
|
logger.info("User approved %d dangerous command(s) via /approve%s", count, scope_msg)
|
||||||
return f"✅ Command{'s' if count > 1 else ''} approved{scope_msg}{count_msg}. The agent is resuming..."
|
return f"✅ Command{'s' if count > 1 else ''} approved{scope_msg}{count_msg}. The agent is resuming..."
|
||||||
@@ -5463,6 +5468,11 @@ class GatewayRunner:
|
|||||||
if not count:
|
if not count:
|
||||||
return "No pending command to deny."
|
return "No pending command to deny."
|
||||||
|
|
||||||
|
# Resume typing indicator — agent continues (with BLOCKED result).
|
||||||
|
_adapter = self.adapters.get(source.platform)
|
||||||
|
if _adapter:
|
||||||
|
_adapter.resume_typing_for_chat(source.chat_id)
|
||||||
|
|
||||||
count_msg = f" ({count} commands)" if count > 1 else ""
|
count_msg = f" ({count} commands)" if count > 1 else ""
|
||||||
logger.info("User denied %d dangerous command(s) via /deny", count)
|
logger.info("User denied %d dangerous command(s) via /deny", count)
|
||||||
return f"❌ Command{'s' if count > 1 else ''} denied{count_msg}."
|
return f"❌ Command{'s' if count > 1 else ''} denied{count_msg}."
|
||||||
@@ -6713,6 +6723,15 @@ class GatewayRunner:
|
|||||||
UX. Otherwise fall back to a plain text message with
|
UX. Otherwise fall back to a plain text message with
|
||||||
``/approve`` instructions.
|
``/approve`` instructions.
|
||||||
"""
|
"""
|
||||||
|
# Pause the typing indicator while the agent waits for
|
||||||
|
# user approval. Critical for Slack's Assistant API where
|
||||||
|
# assistant_threads_setStatus disables the compose box — the
|
||||||
|
# user literally cannot type /approve while "is thinking..."
|
||||||
|
# is active. The approval message send auto-clears the Slack
|
||||||
|
# status; pausing prevents _keep_typing from re-setting it.
|
||||||
|
# Typing resumes in _handle_approve_command/_handle_deny_command.
|
||||||
|
_status_adapter.pause_typing_for_chat(_status_chat_id)
|
||||||
|
|
||||||
cmd = approval_data.get("command", "")
|
cmd = approval_data.get("command", "")
|
||||||
desc = approval_data.get("description", "dangerous command")
|
desc = approval_data.get("description", "dangerous command")
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user