Skip to content

fix(slack): map thread-status text to Agent Sessions API enum - #111820

Open
ching-kaching wants to merge 3 commits into
NousResearch:mainfrom
ching-kaching:fix/slack-agent-sessions-status-enum
Open

ching-kaching wants to merge 3 commits into
NousResearch:mainfrom
ching-kaching:fix/slack-agent-sessions-status-enum

Conversation

@ching-kaching

@ching-kaching ching-kaching commented Sep 15, 2026 •

Copy link
Copy Markdown

Bug

Slack's new Agent Sessions API (agents.sessions.setStatus, used automatically once slack-sdk >= 3.44 is installed — see the earlier migration in this repo) requires status to be a lifecycle enum: active / processing / suspended / closed. The legacy assistant.threads.setStatus it replaces took an arbitrary display string ("is thinking...", a custom typing_status_text, live per-tool text).

_set_thread_status in plugins/platforms/slack/adapter.py still passes the display string straight through to whichever method _session_status_method resolves. On any install where the SDK auto-selects the new Agent Sessions API, the call now fails with invalid_arguments every time — caught and only debug-logged, so nothing surfaces the failure. Net effect: the working-state status line ("is thinking...") silently disappears on every agent-session-capable install, with no error visible to the operator.

Repro (live, against the real API)

curl -X POST -H "Authorization: Bearer $SLACK_BOT_TOKEN" \
  -d '{"channel_id":"...","thread_ts":"...","status":"is thinking..."}' \
  https://slack.com/api/agents.sessions.setStatus
# {"ok":false,"error":"invalid_arguments",..."must be a valid enum value [json-pointer:/status]"}

Fix

Map the display string to the nearest lifecycle enum only when the Agent Sessions API is in play (checked via the existing _sdk_supports_agent_sessions()): processing while a turn is working, active when the status is cleared. The legacy path (older slack-sdk) is untouched and keeps sending free text as before.

Custom typing_status_text / live per-tool status text has no enum equivalent under the new API — Slack renders its own generic placeholder in that case, same as an app with no assistant:write scope. That's a Slack platform limitation (confirmed against docs.slack.dev/ai/agent-sessions), not something this adapter can route around.

Verification

Called both enum values live against a real workspace/thread before and after the fix:

  • Before: status: "is thinking..." → invalid_arguments
  • After: status: "processing" → ok:true; status: "active" → ok:true

Slack's Agent Sessions API (agents.sessions.setStatus, used automatically
on slack-sdk >= 3.44) replaced the legacy assistant.threads.setStatus.
The legacy call took an arbitrary display string ("is thinking...", a
custom typing_status_text, live per-tool text); the new one requires a
lifecycle enum (active/processing/suspended/closed) and rejects free text
with invalid_arguments.

_set_thread_status passed the display string straight through, so on any
install where slack-sdk auto-selects the new API the status call silently
no-ops (caught + debug-logged) and the working-state status line
disappears entirely, with no error surfaced anywhere.

Map to the enum when the new API is in play: "processing" while working,
"active" when clearing. Verified live against the real Slack API (both
enum values return ok:true; the previous free-text call returned
invalid_arguments).
The existing TestAgentSessionsApiRouting tests asserted the pre-fix
(buggy) behavior: free-text status strings passed straight through to
agents_sessions_setStatus. Update them to expect the enum mapping
("processing"/"active") and add a regression test proving a custom
typing_status_text still collapses to the enum instead of leaking as
free text, which Slack's real API rejects with invalid_arguments.
@alt-glitch alt-glitch added type/bug Something isn't working P3 Low — cosmetic, nice to have comp/plugins Plugin system and bundled plugins platform/slack Slack app adapter sweeper:risk-message-delivery Sweeper risk: may drop, duplicate, misroute, or suppress messages duplicate This issue or pull request already exists labels Sep 15, 2026
@alt-glitch

Copy link
Copy Markdown

This was generated by AI during triage.

Duplicate of #109914 — same fix mechanism (map the display string to the processing/active lifecycle enum inside _set_thread_status when the Agent Sessions API is selected). #110389 takes the same approach; #110391 (by the #110374 reporter) instead pins the legacy assistant.threads.setStatus endpoint to keep free-text status. Leaving all open for the maintainer to pick an approach.

ching-kaching added a commit to ching-kaching/hermes-agent that referenced this pull request Sep 15, 2026
Claude Code review of NousResearch#111820 found a real gap: _sdk_supports_agent_sessions()
is a class-level probe, but the client INSTANCE can still lack
agents_sessions_setStatus (older cached client, a test double), in which
case _session_status_method already fell back to the legacy call. The
enum rewrite in _set_thread_status checked only the class-level probe, so
that fallback path would send "processing"/"active" as free text to the
legacy API - which only clears on an empty string, so stop_typing would
never clear the status there.

Fix: _session_status_method_with_kind now returns which method it picked,
and _set_thread_status keys the enum rewrite on that, not the separate
class-level check. Added a test pinning the fallback behavior and a docs
caveat clarifying typing_status_text has no visible effect at all on
slack-sdk 3.44+, independent of the assistant:write scope.
Claude Code review of NousResearch#111820 found a real gap: _sdk_supports_agent_sessions()
is a class-level probe, but the client INSTANCE can still lack
agents_sessions_setStatus (older cached client, a test double), in which
case _session_status_method already fell back to the legacy call. The
enum rewrite in _set_thread_status checked only the class-level probe, so
that fallback path would send "processing"/"active" as free text to the
legacy API - which only clears on an empty string, so stop_typing would
never clear the status there.

Fix: _session_status_method_with_kind now returns which method it picked,
and _set_thread_status keys the enum rewrite on that, not the separate
class-level check. Added a test pinning the fallback behavior and a docs
caveat clarifying typing_status_text has no visible effect at all on
slack-sdk 3.44+, independent of the assistant:write scope.
@ching-kaching
ching-kaching force-pushed the fix/slack-agent-sessions-status-enum branch from 88cd48e to 0b9eadf Compare September 15, 2026 11:05
@ching-kaching

Copy link
Copy Markdown
Author

Ran a self-review with Claude Code against this diff. It found one real correctness gap and I fixed it in the latest push:

Gap: _sdk_supports_agent_sessions() is a class-level probe (checked once against the SDK class); a specific client instance can still lack agents_sessions_setStatus (older cached client). _session_status_method already fell back to the legacy call per-instance in that case, but my enum rewrite only checked the class-level probe — so on that fallback path it would send "processing"/"active" as free text to the legacy API, which only clears on an empty string. stop_typing would never actually clear the status there.

Fix: _session_status_method_with_kind now returns which method it selected; the enum rewrite keys off that instead of the separate class-level check. Added test_typing_uses_legacy_format_when_instance_lacks_agent_sessions_method to pin it.

Also added a docs caveat: typing_status_text has no visible effect at all on slack-sdk 3.44+ (Agent Sessions API), independent of the assistant:write scope — the enum has no slot for custom text, unlike the old "missing scope" failure mode the docs already described.

Two things flagged as reasonable but out of scope for this fix, noting here in case a maintainer wants them split out:

  • Every 2s typing refresh now sends an identical status=\"processing\" call under the new API (previously these calls were failing/cheap; now they succeed and count toward rate limits).
  • suspended (Slack's "waiting on user" enum) could replace the current approval-wait typing pause, but that's a separate behavior change.

@Paulo-Augusto-Malfara

Copy link
Copy Markdown

Independent reproduction + confirmation that this is the right fix.

We hit this in production yesterday (2026-09-14, ~17:00 UTC) on a self-hosted Hermes gateway running three Slack bots. The status line under the composer ("Bot is thinking…") simply stopped appearing — no error surfaced anywhere in normal operation, since _set_thread_status swallows the exception into a logger.debug. At debug level the actual cause shows up as invalid_arguments from agents.sessions.setStatus.

Environment:

  • Hermes 0.21.3
  • slack-sdk 3.44.1 → AsyncWebClient.agents_sessions_setStatus present, so _sdk_supports_agent_sessions() returns True and every status write routes to the new API
  • Python 3.12.14

Before finding this PR we arrived independently at the same diagnosis and patched our install with a functionally identical change — _session_status_method returning (method, is_agent_sessions) and the caller rewriting the display string to processing / active. That patch restored the indicator immediately on all three bots and has been running clean since. So this approach is confirmed working outside the author's setup.

Two details in this PR that I can confirm matter in practice, having gotten them wrong first:

  1. The empty-string clear is broken too, not just the free text. It's tempting to only guard the "set" path and let status="" through on the clear path; Slack rejects "" with invalid_arguments exactly like any other non-enum value, which leaves the session stuck in processing and the composer disabled after the reply lands. Mapping the clear to active is required, not cosmetic.
  2. The per-instance fallback is a real case, not a hypothetical. _sdk_supports_agent_sessions() is a class-level probe, so a bound client that predates the upgrade can still lack the attribute while the class advertises it. test_typing_uses_legacy_format_when_instance_lacks_agent_sessions_method covers precisely that, and the enum rewrite correctly follows the same per-instance branch rather than the class probe — worth keeping as-is if this gets refactored.

The doc change is a good call as well; typing_status_text being silently unreachable on 3.44+ is otherwise a confusing config key.

No changes requested — this looks complete to me. Flagging one process point: the linked issue is labelled P3, but the practical effect on any install that has picked up slack-sdk 3.44+ is that the working-state indicator is gone entirely and threads can be left with a disabled composer. That reads closer to P2. There is no CI reported on this branch yet, which may be worth a nudge.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/plugins Plugin system and bundled plugins duplicate This issue or pull request already exists P3 Low — cosmetic, nice to have platform/slack Slack app adapter sweeper:risk-message-delivery Sweeper risk: may drop, duplicate, misroute, or suppress messages type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants