diff --git a/plugins/platforms/slack/adapter.py b/plugins/platforms/slack/adapter.py index 0bc4b94eff..5cd278a3e6 100644 --- a/plugins/platforms/slack/adapter.py +++ b/plugins/platforms/slack/adapter.py @@ -1934,8 +1934,9 @@ class SlackAdapter(BasePlatformAdapter): chunks.extend(self._task_update_chunk(task) for task in tasks) append_payload: Dict[str, Any] = { "channel": chat_id, "ts": stream.stream_ts, "chunks": chunks} - if fallback_text: - append_payload["markdown_text"] = fallback_text + # chunks-only: Slack rejects markdown_text alongside chunks + # (cannot_provide_both_markdown_text_and_chunks, #87743); the gateway owns + # the editable-text fallback rail that fallback_text feeds when this call fails. await client.api_call("chat.appendStream", json=append_payload) return SendResult(success=True, message_id=stream.stream_ts) except Exception as exc: # pragma: no cover - defensive logging diff --git a/tests/gateway/test_slack.py b/tests/gateway/test_slack.py index 055b6ed35a..5b94c36395 100644 --- a/tests/gateway/test_slack.py +++ b/tests/gateway/test_slack.py @@ -5143,6 +5143,45 @@ class TestNativeTaskCardProgress: ] assert adapter._native_task_card_streams == {} + @pytest.mark.asyncio + async def test_append_payload_never_mixes_markdown_text_with_chunks( + self, adapter + ): + """#87743: chat.appendStream rejects a request carrying both + markdown_text and chunks (`cannot_provide_both_markdown_text_and_chunks`), + which made every native task-card update fail and silently downgraded + each turn to the plain-text fallback. The fallback_text must never be + attached to the chunks payload.""" + client = adapter._app.client + + async def api_call(method, *, json): + if method == "chat.startStream": + return {"ts": "stream-1"} + return {"ok": True} + + client.api_call.side_effect = api_call + + result = await adapter.send_native_task_card_progress( + "C1", + [{"id": "call-1", "title": "terminal", "status": "in_progress"}], + metadata={"thread_id": "thread-1"}, + fallback_text="fallback progress text", + ) + + assert result.success is True + append_calls = [ + call + for call in client.api_call.await_args_list + if call.args[0] == "chat.appendStream" + ] + assert append_calls, "expected an appendStream call" + payload = append_calls[0].kwargs["json"] + assert "chunks" in payload + assert "markdown_text" not in payload, ( + "appendStream must not mix markdown_text with chunks — Slack " + "rejects the pair and the whole native card fails (#87743)" + ) + # --------------------------------------------------------------------------- # TestSlackAuthoredTextDeduplication