Skip to content

feat(bot): add image attachment support for Slack bot#2749

Merged
RSO merged 3 commits intomainfrom
feat/slack-bot-image-support
Apr 28, 2026
Merged

feat(bot): add image attachment support for Slack bot#2749
RSO merged 3 commits intomainfrom
feat/slack-bot-image-support

Conversation

@kilo-code-bot
Copy link
Copy Markdown
Contributor

@kilo-code-bot kilo-code-bot Bot commented Apr 23, 2026

Summary

  • When users attach images (PNG, JPEG, WebP, GIF) to their Slack messages mentioning the bot, the images are now automatically extracted, downloaded via the chat SDK's authenticated fetchData(), uploaded to R2 in the Cloud Agent attachments bucket, and forwarded to the Cloud Agent session via PrepareSessionInput.images.
  • The bot agent LLM is informed when images are present so it can reference them in the prompt it constructs for the Cloud Agent (e.g. "implement the design shown in the attached screenshot").
  • Image processing is non-fatal — if extraction or upload fails for any image, the bot logs the error to Sentry and continues without it. Respects existing limits (max 5 images, max 5MB each).

Visual Changes

CleanShot 2026-04-24 at 11 58 54@2x CleanShot 2026-04-24 at 11 58 35@2x

Reviewer Notes

  • The new extractAndUploadImages utility (apps/web/src/lib/bot/images.ts) uses the chat SDK's Attachment.fetchData() which handles Slack's url_private authentication automatically via the bot token — no manual token handling needed.
  • Images are uploaded directly to R2 server-side using PutObjectCommand (unlike the browser flow which uses presigned URLs). This is analogous to how cli-sessions.ts does direct R2 uploads.
  • The R2 key structure follows the existing convention: {userId}/cloud-agent/{messageUuid}/{imageId}.{ext}.
  • No changes to the Cloud Agent worker are needed — it already supports the images field in prepareSession.

Built for Mark IJbema by Kilo for Slack

When users attach images to their Slack messages, the bot now extracts
them via the chat SDK's fetchData() method (which handles Slack auth),
uploads them to R2 in the Cloud Agent attachments bucket, and forwards
the image references to the Cloud Agent session via PrepareSessionInput.

This bridges Slack's file attachment format to the existing Cloud Agent
image pipeline without requiring any changes to the Cloud Agent worker.
Comment thread apps/web/src/lib/bot/images.ts Outdated
@kilo-code-bot
Copy link
Copy Markdown
Contributor Author

kilo-code-bot Bot commented Apr 23, 2026

Code Review Summary

Status: No Issues Found | Recommendation: Merge

Files Reviewed (4 files)
  • apps/web/src/lib/bot/agent-runner.ts
  • apps/web/src/lib/bot/images.ts
  • apps/web/src/lib/bot/run.ts
  • apps/web/src/lib/bot/tools/spawn-cloud-agent-session.ts

Reviewed by gpt-5.5-2026-04-23 · 432,169 tokens

@RSO RSO requested a review from eshurakov April 24, 2026 09:59
@RSO RSO enabled auto-merge (squash) April 24, 2026 13:04
Comment on lines +48 to +94
const toProcess = imageAttachments.slice(0, CLOUD_AGENT_IMAGE_MAX_COUNT);

const messageUuid = randomUUID();
const filenames: string[] = [];

for (const attachment of toProcess) {
try {
const imageId = randomUUID();
const ext = CLOUD_AGENT_IMAGE_MIME_TO_EXTENSION[attachment.mimeType];
const filename = `${imageId}.${ext}`;
const r2Key = `${userId}/cloud-agent/${messageUuid}/${filename}`;

if (
typeof attachment.size === 'number' &&
attachment.size > CLOUD_AGENT_IMAGE_MAX_SIZE_BYTES
) {
throw new Error(
`Image ${attachment.name ?? filename} exceeds ${CLOUD_AGENT_IMAGE_MAX_SIZE_BYTES / (1024 * 1024)}MB limit (${(attachment.size / (1024 * 1024)).toFixed(1)}MB)`
);
}

const data = await attachment.fetchData();

if (data.byteLength > CLOUD_AGENT_IMAGE_MAX_SIZE_BYTES) {
throw new Error(
`Image ${attachment.name ?? filename} exceeds ${CLOUD_AGENT_IMAGE_MAX_SIZE_BYTES / (1024 * 1024)}MB limit (${(data.byteLength / (1024 * 1024)).toFixed(1)}MB)`
);
}

await r2Client.send(
new PutObjectCommand({
Bucket: r2CloudAgentAttachmentsBucketName,
Key: r2Key,
Body: data,
ContentType: attachment.mimeType,
ContentLength: data.byteLength,
Metadata: { userId, messageUuid, imageId },
})
);

filenames.push(filename);
} catch (error) {
console.error('[KiloBot] Failed to upload image attachment:', error);
captureException(error, {
tags: { component: 'kilo-bot', op: 'upload-slack-image' },
});
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This piece is probably very similar to what cloud agent is doing and can be extracted into a common method

@RSO RSO merged commit 8777905 into main Apr 28, 2026
16 checks passed
@RSO RSO deleted the feat/slack-bot-image-support branch April 28, 2026 07:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants