From f0bf3be56d348cabbbe7113eccafdeb6b945d715 Mon Sep 17 00:00:00 2001 From: David Szarzynski Date: Thu, 19 Feb 2026 08:30:26 -0600 Subject: [PATCH] fix(slack): pass recipient_team_id and recipient_user_id to streaming API calls The Slack Agents & AI Apps streaming API (chat.startStream / chat.stopStream) requires recipient_team_id and recipient_user_id parameters. Without them, stopStream fails with 'missing_recipient_team_id' (all contexts) or 'missing_recipient_user_id' (DM contexts), causing streamed messages to disappear after generation completes. This passes: - team_id (from auth.test at provider startup, stored in monitor context) - user_id (from the incoming message sender, for DM recipient identification) through to the ChatStreamer via recipient_team_id and recipient_user_id options. Fixes #19839, #20847, #20299, #19791, #20337 AI-assisted: Written with Claude (Opus 4.6) via OpenClaw. Lightly tested (unit tests pass, live workspace verification in progress). --- src/slack/monitor/message-handler/dispatch.ts | 2 ++ src/slack/streaming.ts | 20 +++++++++++++++++-- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/src/slack/monitor/message-handler/dispatch.ts b/src/slack/monitor/message-handler/dispatch.ts index b9c88e3448..9147f6a7a1 100644 --- a/src/slack/monitor/message-handler/dispatch.ts +++ b/src/slack/monitor/message-handler/dispatch.ts @@ -199,6 +199,8 @@ export async function dispatchPreparedSlackMessage(prepared: PreparedSlackMessag channel: message.channel, threadTs: streamThreadTs, text, + teamId: ctx.teamId, + userId: message.user, }); replyPlan.markSent(); return; diff --git a/src/slack/streaming.ts b/src/slack/streaming.ts index f9e1ab6979..936fba79fe 100644 --- a/src/slack/streaming.ts +++ b/src/slack/streaming.ts @@ -36,6 +36,18 @@ export type StartSlackStreamParams = { threadTs: string; /** Optional initial markdown text to include in the stream start. */ text?: string; + /** + * The team ID of the workspace this stream belongs to. + * Required by the Slack API for `chat.startStream` / `chat.stopStream`. + * Obtain from `auth.test` response (`team_id`). + */ + teamId?: string; + /** + * The user ID of the message recipient (required for DM streaming). + * Without this, `chat.stopStream` fails with `missing_recipient_user_id` + * in direct message conversations. + */ + userId?: string; }; export type AppendSlackStreamParams = { @@ -64,13 +76,17 @@ export type StopSlackStreamParams = { export async function startSlackStream( params: StartSlackStreamParams, ): Promise { - const { client, channel, threadTs, text } = params; + const { client, channel, threadTs, text, teamId, userId } = params; - logVerbose(`slack-stream: starting stream in ${channel} thread=${threadTs}`); + logVerbose( + `slack-stream: starting stream in ${channel} thread=${threadTs}${teamId ? ` team=${teamId}` : ""}${userId ? ` user=${userId}` : ""}`, + ); const streamer = client.chatStream({ channel, thread_ts: threadTs, + ...(teamId ? { recipient_team_id: teamId } : {}), + ...(userId ? { recipient_user_id: userId } : {}), }); const session: SlackStreamSession = {