diff --git a/src/slack/monitor/media.ts b/src/slack/monitor/media.ts index 4a51e9abd..143d6b36f 100644 --- a/src/slack/monitor/media.ts +++ b/src/slack/monitor/media.ts @@ -53,6 +53,7 @@ export type SlackThreadStarter = { text: string; userId?: string; ts?: string; + files?: SlackFile[]; }; const THREAD_STARTER_CACHE = new Map(); @@ -71,7 +72,7 @@ export async function resolveSlackThreadStarter(params: { ts: params.threadTs, limit: 1, inclusive: true, - })) as { messages?: Array<{ text?: string; user?: string; ts?: string }> }; + })) as { messages?: Array<{ text?: string; user?: string; ts?: string; files?: SlackFile[] }> }; const message = response?.messages?.[0]; const text = (message?.text ?? "").trim(); if (!message || !text) return null; @@ -79,6 +80,7 @@ export async function resolveSlackThreadStarter(params: { text, userId: message.user, ts: message.ts, + files: message.files, }; THREAD_STARTER_CACHE.set(cacheKey, starter); return starter; diff --git a/src/slack/monitor/message-handler/prepare.ts b/src/slack/monitor/message-handler/prepare.ts index c76ae4285..767ecea89 100644 --- a/src/slack/monitor/message-handler/prepare.ts +++ b/src/slack/monitor/message-handler/prepare.ts @@ -434,6 +434,7 @@ export async function prepareSlackMessage(params: { let threadStarterBody: string | undefined; let threadLabel: string | undefined; + let threadStarterMedia: Awaited> = null; if (isThreadReply && threadTs) { const starter = await resolveSlackThreadStarter({ channelId: message.channel, @@ -453,11 +454,27 @@ export async function prepareSlackMessage(params: { }); const snippet = starter.text.replace(/\s+/g, " ").slice(0, 80); threadLabel = `Slack thread ${roomLabel}${snippet ? `: ${snippet}` : ""}`; + // If current message has no files but thread starter does, fetch starter's files + if (!media && starter.files && starter.files.length > 0) { + threadStarterMedia = await resolveSlackMedia({ + files: starter.files, + token: ctx.botToken, + maxBytes: ctx.mediaMaxBytes, + }); + if (threadStarterMedia) { + logVerbose( + `slack: hydrated thread starter file ${threadStarterMedia.placeholder} from root message`, + ); + } + } } else { threadLabel = `Slack thread ${roomLabel}`; } } + // Use thread starter media if current message has none + const effectiveMedia = media ?? threadStarterMedia; + const ctxPayload = finalizeInboundContext({ Body: combinedBody, RawBody: rawBody, @@ -483,9 +500,9 @@ export async function prepareSlackMessage(params: { ThreadLabel: threadLabel, Timestamp: message.ts ? Math.round(Number(message.ts) * 1000) : undefined, WasMentioned: isRoomish ? effectiveWasMentioned : undefined, - MediaPath: media?.path, - MediaType: media?.contentType, - MediaUrl: media?.path, + MediaPath: effectiveMedia?.path, + MediaType: effectiveMedia?.contentType, + MediaUrl: effectiveMedia?.path, CommandAuthorized: commandAuthorized, OriginatingChannel: "slack" as const, OriginatingTo: slackTo,