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 = {