From 0a80dd9c7ac98df85b93be1d18c40611fdfd0b6d Mon Sep 17 00:00:00 2001 From: Teknium <127238744+teknium1@users.noreply.github.com> Date: Sat, 28 Mar 2026 23:46:43 -0700 Subject: [PATCH] fix(discord): clean up deferred "thinking..." after slash commands complete (#3674) After a slash command is deferred (interaction.response.defer), the "thinking..." indicator persisted indefinitely because the code used followup.send() which creates a separate message instead of replacing or removing the deferred response. Fix: use edit_original_response() to replace "thinking..." with the confirmation text when provided, or delete_original_response() to remove it when there is no confirmation. Also consolidated /reasoning and /voice handlers to use _run_simple_slash instead of duplicating the defer+dispatch pattern. Fixes #3595. --- gateway/platforms/discord.py | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/gateway/platforms/discord.py b/gateway/platforms/discord.py index 7c735e624..2060f344f 100644 --- a/gateway/platforms/discord.py +++ b/gateway/platforms/discord.py @@ -1429,15 +1429,23 @@ class DiscordAdapter(BasePlatformAdapter): command_text: str, followup_msg: str | None = None, ) -> None: - """Common handler for simple slash commands that dispatch a command string.""" + """Common handler for simple slash commands that dispatch a command string. + + Defers the interaction (shows "thinking..."), dispatches the command, + then cleans up the deferred response. If *followup_msg* is provided + the "thinking..." indicator is replaced with that text; otherwise it + is deleted so the channel isn't cluttered. + """ await interaction.response.defer(ephemeral=True) event = self._build_slash_event(interaction, command_text) await self.handle_message(event) - if followup_msg: - try: - await interaction.followup.send(followup_msg, ephemeral=True) - except Exception as e: - logger.debug("Discord followup failed: %s", e) + try: + if followup_msg: + await interaction.edit_original_response(content=followup_msg) + else: + await interaction.delete_original_response() + except Exception as e: + logger.debug("Discord interaction cleanup failed: %s", e) def _register_slash_commands(self) -> None: """Register Discord slash commands on the command tree.""" @@ -1462,9 +1470,7 @@ class DiscordAdapter(BasePlatformAdapter): @tree.command(name="reasoning", description="Show or change reasoning effort") @discord.app_commands.describe(effort="Reasoning effort: xhigh, high, medium, low, minimal, or none.") async def slash_reasoning(interaction: discord.Interaction, effort: str = ""): - await interaction.response.defer(ephemeral=True) - event = self._build_slash_event(interaction, f"/reasoning {effort}".strip()) - await self.handle_message(event) + await self._run_simple_slash(interaction, f"/reasoning {effort}".strip()) @tree.command(name="personality", description="Set a personality") @discord.app_commands.describe(name="Personality name. Leave empty to list available.") @@ -1537,9 +1543,7 @@ class DiscordAdapter(BasePlatformAdapter): discord.app_commands.Choice(name="status — show current mode", value="status"), ]) async def slash_voice(interaction: discord.Interaction, mode: str = ""): - await interaction.response.defer(ephemeral=True) - event = self._build_slash_event(interaction, f"/voice {mode}".strip()) - await self.handle_message(event) + await self._run_simple_slash(interaction, f"/voice {mode}".strip()) @tree.command(name="update", description="Update Hermes Agent to the latest version") async def slash_update(interaction: discord.Interaction):