hydrate files from thread root message on replies

When replying to a Slack thread, files attached to the root message were
  not being fetched. The existing `resolveSlackThreadStarter()` fetched the
  root message text via `conversations.replies` but ignored the `files[]`
  array in the response.

  Changes:
  - Add `files` to `SlackThreadStarter` type and extract from API response
  - Download thread starter files when the reply message has no attachments
  - Add verbose log for thread starter file hydration

  Fixes issue where asking about a PDF in a thread reply would fail because
  the model never received the file content from the root message.
This commit is contained in:
Travis Irby
2026-01-22 21:55:03 -05:00
committed by Peter Steinberger
parent 2accb47e4d
commit 578ac9f1a9
2 changed files with 23 additions and 4 deletions

View File

@@ -434,6 +434,7 @@ export async function prepareSlackMessage(params: {
let threadStarterBody: string | undefined;
let threadLabel: string | undefined;
let threadStarterMedia: Awaited<ReturnType<typeof resolveSlackMedia>> = 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,