{"repo":"NousResearch/hermes-agent","public_id":"3595","display_label":"Discord deferred \"thinking...\" indicator lingering after slash commands","summary":"The three members all concern the Discord deferred \"thinking...\" indicator remaining after a slash command's final message, with one bug report and two pull requests proposing fixes. Their titles do not show whether the problem is confirmed fixed or what the final outcome is.","subsystem":"gateway","counts":{"members":3,"open_issues":0,"open_issues_distinct":0,"open_prs":0,"closed_issues":1,"merged_prs":1,"attempt_prs":1},"landed_fix":{"status":"merged_to_main","basis":"structural","pr":3674,"merged_at":"2026-03-29T06:46:43+00:00"},"resolution":{"state":"resolved","by":"pr-3674"},"freshness":{"state":"stale","last_verified_at":"2026-03-29T06:46:43+00:00","is_stale":true},"grouping":{"backbone":"STRUCTURAL_SPANNING","link_density":0.6666666666666666},"fix_edges":[{"issue_number":3595,"pr_number":3674,"tier":"verified","basis":"closing_ref","confidence":0.95,"recoverable_from_timeline":true,"pr_merged_at":"2026-03-29T06:46:43+00:00"}],"members":[{"kind":"issue","number":3595,"title":"[Bug]: Discord gateway: \"thinking...\" deferred response indicator persists after final message is sent","url":null,"state":"closed"},{"kind":"pr","number":3615,"title":"fix(discord): resolve deferred \"thinking...\" indicator after slash commands","url":null,"state":"closed","close_reason":{"refs":[{"kind":"pr","value":"#3674","number":3674}],"category":"superseded_by_pr","implication":"superseded"},"merged":false},{"kind":"pr","number":3674,"title":"fix(discord): clean up deferred \"thinking...\" after slash commands complete","url":null,"state":"merged","close_reason":{"category":"merged"},"merged":true,"is_fix_pr":true,"fix_tier":"verified","files_changed":["gateway/platforms/discord.py"],"fix_hunks":[{"file":"gateway/platforms/discord.py","hunks":["@@ -1429,15 +1429,23 @@ async def _run_simple_slash(\n         command_text: str,\n         followup_msg: str | None = None,\n     ) -> None:\n-        \"\"\"Common handler for simple slash commands that dispatch a command string.\"\"\"\n+        \"\"\"Common handler for simple slash commands that dispatch a command string.\n+\n+        Defers the interaction (shows \"thinking...\"), dispatches the command,\n+        then cleans up the deferred response.  If *followup_msg* is provided\n+        the \"thinking...\" indicator is replaced with that text; otherwise it\n+        is deleted so the channel isn't cluttered.\n+        \"\"\"\n         await interaction.response.defer(ephemeral=True)\n         event = self._build_slash_event(interaction, command_text)\n         await self.handle_message(event)\n-        if followup_msg:\n-            try:\n-                await interaction.followup.send(followup_msg, ephemeral=True)\n-            except Exception as e:\n-                logger.debug(\"Discord followup failed: %s\", e)\n+        try:\n+            if followup_msg:\n+                await interaction.edit_original_response(content=followup_msg)\n+            else:\n+                await interaction.delete_original_response()\n+        except Exception as e:\n+            logger.debug(\"Discord interaction cleanup failed: %s\", e)\n \n     def _register_slash_commands(self) -> None:\n         \"\"\"Register Discord slash commands on the command tree.\"\"\"","@@ -1462,9 +1470,7 @@ async def slash_model(interaction: discord.Interaction, name: str = \"\"):\n         @tree.command(name=\"reasoning\", description=\"Show or change reasoning effort\")\n         @discord.app_commands.describe(effort=\"Reasoning effort: xhigh, high, medium, low, minimal, or none.\")\n         async def slash_reasoning(interaction: discord.Interaction, effort: str = \"\"):\n-            await interaction.response.defer(ephemeral=True)\n-            event = self._build_slash_event(interaction, f\"/reasoning {effort}\".strip())\n-            await self.handle_message(event)\n+            await self._run_simple_slash(interaction, f\"/reasoning {effort}\".strip())\n \n         @tree.command(name=\"personality\", description=\"Set a personality\")\n         @discord.app_commands.describe(name=\"Personality name. Leave empty to list available.\")","@@ -1537,9 +1543,7 @@ async def slash_reload_mcp(interaction: discord.Interaction):\n             discord.app_commands.Choice(name=\"status — show current mode\", value=\"status\"),\n         ])\n         async def slash_voice(interaction: discord.Interaction, mode: str = \"\"):\n-            await interaction.response.defer(ephemeral=True)\n-            event = self._build_slash_event(interaction, f\"/voice {mode}\".strip())\n-            await self.handle_message(event)\n+            await self._run_simple_slash(interaction, f\"/voice {mode}\".strip())\n \n         @tree.command(name=\"update\", description=\"Update Hermes Agent to the latest version\")\n         async def slash_update(interaction: discord.Interaction):\n"]}]}],"trust":{"default_tier":"verified","includes_suggested":false}}