fix(slack): clear uniquely scoped assistant status

This commit is contained in:
kshitijk4poor 2026-07-13 18:31:29 +05:30 committed by Teknium
parent 38cfae9b54
commit fc8f8ad33f
2 changed files with 59 additions and 10 deletions

View file

@ -1608,15 +1608,26 @@ class SlackAdapter(BasePlatformAdapter):
metadata.get("thread_id") or metadata.get("thread_ts") or ""
)
requested_team_id = self._metadata_team_id(metadata)
if not requested_team_id:
requested_team_id = self._channel_team.get(chat_id, "")
active = None
if requested_thread_ts:
active_key = self._workspace_thread_key(
requested_team_id, chat_id, requested_thread_ts
)
if active_key:
active = self._active_status_threads.pop(active_key, None)
if requested_team_id:
active_key = self._workspace_thread_key(
requested_team_id, chat_id, requested_thread_ts
)
if active_key:
active = self._active_status_threads.pop(active_key, None)
else:
# Do not trust the mutable channel-only workspace fallback for
# a thread-specific cleanup: Slack Connect workspaces can share
# a channel ID. Clear the uniquely matching tracked status and
# let its stored team choose the correct client.
matching_keys = [
key
for key in self._active_status_threads
if key[1] == str(chat_id) and key[2] == requested_thread_ts
]
if len(matching_keys) == 1:
active = self._active_status_threads.pop(matching_keys[0], None)
else:
# Metadata-free cleanup is safe only if exactly one status exists
# for this channel; otherwise it may clear another Slack Connect
@ -1624,9 +1635,7 @@ class SlackAdapter(BasePlatformAdapter):
matching_keys = [
key
for key in self._active_status_threads
if isinstance(key, tuple)
and len(key) == 3
and key[1] == str(chat_id)
if key[1] == str(chat_id)
]
if len(matching_keys) == 1:
active = self._active_status_threads.pop(matching_keys[0], None)

View file

@ -2292,6 +2292,46 @@ class TestSendTyping:
)
assert ("T_TWO", "D_SHARED", "171.000") in adapter._active_status_threads
@pytest.mark.asyncio
async def test_stop_typing_without_team_uses_unique_thread_status(self, adapter):
"""A stale channel fallback must not strand a uniquely tracked status."""
team_one, team_two = AsyncMock(), AsyncMock()
adapter._team_clients.update({"T_ONE": team_one, "T_TWO": team_two})
await adapter.send_typing(
"D_SHARED",
metadata={"thread_id": "171.000", "slack_team_id": "T_ONE"},
)
# Another workspace can overwrite this channel-only fallback map.
adapter._channel_team["D_SHARED"] = "T_TWO"
await adapter.stop_typing("D_SHARED", metadata={"thread_id": "171.000"})
assert team_one.assistant_threads_setStatus.call_args_list[-1] == call(
channel_id="D_SHARED", thread_ts="171.000", status=""
)
assert ("T_ONE", "D_SHARED", "171.000") not in adapter._active_status_threads
team_two.assistant_threads_setStatus.assert_not_called()
@pytest.mark.asyncio
async def test_stop_typing_without_team_preserves_ambiguous_thread_statuses(
self, adapter
):
"""Without team metadata, matching workspace statuses must not be guessed."""
team_one, team_two = AsyncMock(), AsyncMock()
adapter._team_clients.update({"T_ONE": team_one, "T_TWO": team_two})
for team_id in ("T_ONE", "T_TWO"):
await adapter.send_typing(
"D_SHARED",
metadata={"thread_id": "171.000", "slack_team_id": team_id},
)
await adapter.stop_typing("D_SHARED", metadata={"thread_id": "171.000"})
assert ("T_ONE", "D_SHARED", "171.000") in adapter._active_status_threads
assert ("T_TWO", "D_SHARED", "171.000") in adapter._active_status_threads
assert team_one.assistant_threads_setStatus.call_count == 1
assert team_two.assistant_threads_setStatus.call_count == 1
@pytest.mark.asyncio
async def test_streaming_final_edit_uses_workspace_client_from_metadata(
self, adapter