From 344d3f6f8a0f29cb229ddde58bfbf6838603d854 Mon Sep 17 00:00:00 2001 From: Pa0 <1414756+palol@users.noreply.github.com> Date: Wed, 16 Sep 2026 19:58:05 -0700 Subject: [PATCH] fix(slack): keep status line on legacy assistant.threads.setStatus MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit agents.sessions.setStatus (Agent Sessions API, slack-sdk 3.44+) takes a closed enum (active|processing|suspended|closed) with no free-text field, so Hermes' free-text status verbs ("is thinking...", "still working… (2m03s)") and the empty-string clear all fail with invalid_arguments and the status line silently never renders. Route only the title through agents.sessions.rename; keep the status line on assistant.threads.setStatus, which accepts free text and is supported until the Feb 2027 assistant-view deprecation. --- plugins/platforms/slack/adapter.py | 16 +++++++++----- tests/gateway/test_slack.py | 25 +++++++++++++--------- website/docs/user-guide/messaging/slack.md | 4 ++-- 3 files changed, 28 insertions(+), 17 deletions(-) diff --git a/plugins/platforms/slack/adapter.py b/plugins/platforms/slack/adapter.py index e98309bfd27c..e85ec469c150 100644 --- a/plugins/platforms/slack/adapter.py +++ b/plugins/platforms/slack/adapter.py @@ -281,11 +281,17 @@ def _sdk_supports_agent_sessions() -> bool: def _session_status_method(client: Any): - """Return the status setter: Agent Sessions API when available, else legacy.""" - if _sdk_supports_agent_sessions(): - method = getattr(client, "agents_sessions_setStatus", None) - if method is not None: - return method + """Return the status setter: always the legacy ``assistant.threads.setStatus``. + + The Agent Sessions API's ``agents.sessions.setStatus`` takes a CLOSED enum + (``active|processing|suspended|closed``) with no free-text field, so it cannot carry + the live verb phrases Hermes sends (``is thinking...``, ``still working… (2m03s)``) + nor the empty string used to clear the status — every such call returns + ``invalid_arguments`` and the status line silently never appears. The legacy + ``assistant.threads.setStatus`` accepts free text and remains supported until the + Feb 2027 assistant-view deprecation, so the status line stays on it; only the title + routes through ``agents.sessions.rename`` (see ``_session_title_method``). + """ return client.assistant_threads_setStatus diff --git a/tests/gateway/test_slack.py b/tests/gateway/test_slack.py index 31f09038981c..127ccf92f568 100644 --- a/tests/gateway/test_slack.py +++ b/tests/gateway/test_slack.py @@ -5968,9 +5968,12 @@ def test_block_kit_dump_still_describes_bot_ui_blocks(self): class TestAgentSessionsApiRouting: """slack-sdk 3.44.0 Agent Sessions API (assistant_view deprecation Feb 2027). - When the installed slack-sdk ships agents.sessions.* typed methods, status - and title calls route through them; older SDKs keep using the legacy - assistant.threads.* methods (compat bridge on Slack's side). + When the installed slack-sdk ships agents.sessions.* typed methods, the TITLE + routes through agents.sessions.rename. The STATUS line stays on the legacy + assistant.threads.setStatus even then: agents.sessions.setStatus takes a closed + enum (active|processing|suspended|closed) with no free-text field, so it cannot + carry the live verb phrases ("is thinking...", "still working… (2m03s)") nor the + empty string that clears the status. Older SDKs keep the legacy title method too. """ def _adapter(self): @@ -5981,18 +5984,20 @@ def _adapter(self): return a @pytest.mark.asyncio - async def test_typing_uses_agent_sessions_when_supported(self): + async def test_typing_stays_on_legacy_status_when_agent_sessions_supported(self): + # agents.sessions.setStatus is enum-only, so the free-text status line must + # stay on the legacy assistant.threads.setStatus even when the SDK ships it. _slack_mod._AGENT_SESSIONS_SUPPORTED = True a = self._adapter() a._app.client.agents_sessions_setStatus = AsyncMock() a._app.client.assistant_threads_setStatus = AsyncMock() await a.send_typing("C123", metadata={"thread_id": "parent_ts"}) - a._app.client.agents_sessions_setStatus.assert_called_once_with( + a._app.client.assistant_threads_setStatus.assert_called_once_with( channel_id="C123", thread_ts="parent_ts", status="is thinking...", ) - a._app.client.assistant_threads_setStatus.assert_not_called() + a._app.client.agents_sessions_setStatus.assert_not_called() @pytest.mark.asyncio async def test_typing_falls_back_to_legacy_without_sdk_support(self): @@ -6007,20 +6012,20 @@ async def test_typing_falls_back_to_legacy_without_sdk_support(self): ) @pytest.mark.asyncio - async def test_stop_typing_clears_via_agent_sessions(self): + async def test_stop_typing_clears_via_legacy_status_when_agent_sessions_supported(self): _slack_mod._AGENT_SESSIONS_SUPPORTED = True a = self._adapter() a._app.client.agents_sessions_setStatus = AsyncMock() a._app.client.assistant_threads_setStatus = AsyncMock() await a.send_typing("C123", metadata={"thread_id": "parent_ts"}) - a._app.client.agents_sessions_setStatus.reset_mock() + a._app.client.assistant_threads_setStatus.reset_mock() await a.stop_typing("C123", metadata={"thread_id": "parent_ts"}) - a._app.client.agents_sessions_setStatus.assert_called_once_with( + a._app.client.assistant_threads_setStatus.assert_called_once_with( channel_id="C123", thread_ts="parent_ts", status="", ) - a._app.client.assistant_threads_setStatus.assert_not_called() + a._app.client.agents_sessions_setStatus.assert_not_called() @pytest.mark.asyncio async def test_thread_title_uses_agents_sessions_rename(self): diff --git a/website/docs/user-guide/messaging/slack.md b/website/docs/user-guide/messaging/slack.md index d07e4ad58e61..6eb0120b81c5 100644 --- a/website/docs/user-guide/messaging/slack.md +++ b/website/docs/user-guide/messaging/slack.md @@ -107,7 +107,7 @@ These are the most commonly missed scopes. | Scope | Purpose | |-------|---------| | `groups:read` | List and get info about private channels | -| `assistant:write` | Render the working-state status line ("is thinking…") next to the bot name while it processes a message. Without this scope the status call (`agents.sessions.setStatus` on slack-sdk 3.44+, `assistant.threads.setStatus` on older SDKs) fails silently and Slack shows its own rotating generic placeholders instead ("Finding answers…", "Reviewing findings…", …) — Hermes never controls the text. Required for `typing_status_text` to have any visible effect. | +| `assistant:write` | Render the working-state status line ("is thinking…") next to the bot name while it processes a message. Without this scope the status call (`assistant.threads.setStatus`) fails silently and Slack shows its own rotating generic placeholders instead ("Finding answers…", "Reviewing findings…", …) — Hermes never controls the text. Required for `typing_status_text` to have any visible effect. | --- @@ -504,7 +504,7 @@ platforms: | `platforms.slack.typing_status_text` | `"is thinking..."` | Text of the working-state status line shown while the agent processes a message. Requires the `assistant:write` scope — without it the status call fails silently and Slack renders its own generic placeholder, whatever this is set to. Set `typing_indicator: false` to disable the status line entirely. | :::note Where the status renders -The custom status appears in the **footer beneath the reply composer** ("*BotName* is thinking…"), not inline in the message list. The inline "Generating response…" / "Finding answers…" lines Slack shows in the message area while an AI app works are **Slack's own rotating indicators** — the status API (`agents.sessions.setStatus` / `assistant.threads.setStatus`) does not control those, and both can appear at the same time. +The custom status appears in the **footer beneath the reply composer** ("*BotName* is thinking…"), not inline in the message list. The inline "Generating response…" / "Finding answers…" lines Slack shows in the message area while an AI app works are **Slack's own rotating indicators** — the status API (`assistant.threads.setStatus`) does not control those, and both can appear at the same time. ::: The same key customizes Google Chat's visible working-state marker message