Description
Inbound non-image, non-audio, non-text-extension files (PDFs, .docx, .xlsx, .pptx, video, archives, etc.) are silently dropped by the Slack and Discord adapters. The agent receives the user's text prompt with no indication that an attachment was even sent.
Current adapter routing on main (after #291):
image/* → resize + base64 → ContentBlock::Image ✅
audio/* → STT transcript → ContentBlock::Text ✅
- text-extension files → inline content →
ContentBlock::Text ✅
- everything else → silently dropped ❌
This was previously raised in #254 ("support non-image file attachments (download to disk)"), where maintainers laid out a hybrid design and a security checklist. That issue was closed with Close in favor of #652, but #652 is the outbound images/files docs PR (docs: add sendimages.md and sendfiles.md), which doesn't address inbound binary handling. The inbound gap remains in main as of v0.8.3-beta.2.
Requesting we revive the design from #254 and land a download_to_disk fallback for binary attachments.
Use Case
Production deployment running openab-claude as a Slack bot for an internal team. Users routinely drop .xlsx reports, .docx drafts, .pdf documents, and .mp4 screen recordings into bot threads.
The bot has Anthropic's official document skills (docx, pdf, pptx, xlsx) plus a video-analysis skill mounted, with their runtime dependencies (LibreOffice, pandoc, Tesseract, ffmpeg) installed in the container. The skills can process every one of these formats — but openab never tells the agent the file exists, so the skills never trigger.
Concrete user-facing failure: user uploads report.xlsx and asks @bot summarize this. Bot responds with something like "I don't see any attachment in this thread." This is misleading and forces users to manually paste content as text or share via out-of-band channels.
This isn't a niche workflow, drag-and-drop file upload is the default mental model for Slack/Discord users. Every non-image attachment hitting an openab-driven bot today silently goes nowhere.
Proposed Solution
Implement the design from #254: a download_to_disk fallback in src/media.rs, used when a file isn't audio / inline-text / image. Returns a ContentBlock::Text with a structured "[Attachment received]" header pointing to the on-disk path, so agents with file-reading tools can pick it up.
Reference implementation (POC)
A working POC has been running in production for several days. Branch:
https://github.com/ShinyChang/openab/tree/feat/download-to-disk-attachments
Diff: main...ShinyChang:openab:feat/download-to-disk-attachments
Touches three files:
src/media.rs — adds download_to_disk() + sanitize_path_component()
src/slack.rs — replaces image-only fallback with image-OR-disk (bucket = ts)
src/discord.rs — same shape (bucket = msg.id)
Files land at ${OPENAB_ATTACHMENTS_DIR:-/tmp/openab-attachments}/<bucket_id>/<filename>. Tested in production with .xlsx / .pdf / .mp4 attachments via Slack — agent receives the path block, triggers the matching skill, processes the file end-to-end.
Known gaps vs the #254 security checklist
The POC ships fast but does not yet meet the bar maintainers proposed in #254:
Hybrid (small-text inline vs disk-fallback) is already covered by the existing is_text_file branch landing first in the routing chain, so no new logic needed there.
Description
Inbound non-image, non-audio, non-text-extension files (PDFs, .docx, .xlsx, .pptx, video, archives, etc.) are silently dropped by the Slack and Discord adapters. The agent receives the user's text prompt with no indication that an attachment was even sent.
Current adapter routing on
main(after #291):image/*→ resize + base64 →ContentBlock::Image✅audio/*→ STT transcript →ContentBlock::Text✅ContentBlock::Text✅This was previously raised in #254 ("support non-image file attachments (download to disk)"), where maintainers laid out a hybrid design and a security checklist. That issue was closed with
Close in favor of #652, but #652 is the outbound images/files docs PR (docs: add sendimages.md and sendfiles.md), which doesn't address inbound binary handling. The inbound gap remains inmainas of v0.8.3-beta.2.Requesting we revive the design from #254 and land a
download_to_diskfallback for binary attachments.Use Case
Production deployment running
openab-claudeas a Slack bot for an internal team. Users routinely drop.xlsxreports,.docxdrafts,.pdfdocuments, and.mp4screen recordings into bot threads.The bot has Anthropic's official document skills (
docx,pdf,pptx,xlsx) plus a video-analysis skill mounted, with their runtime dependencies (LibreOffice, pandoc, Tesseract, ffmpeg) installed in the container. The skills can process every one of these formats — but openab never tells the agent the file exists, so the skills never trigger.Concrete user-facing failure: user uploads
report.xlsxand asks@bot summarize this. Bot responds with something like "I don't see any attachment in this thread." This is misleading and forces users to manually paste content as text or share via out-of-band channels.This isn't a niche workflow, drag-and-drop file upload is the default mental model for Slack/Discord users. Every non-image attachment hitting an openab-driven bot today silently goes nowhere.
Proposed Solution
Implement the design from #254: a
download_to_diskfallback insrc/media.rs, used when a file isn't audio / inline-text / image. Returns aContentBlock::Textwith a structured "[Attachment received]" header pointing to the on-disk path, so agents with file-reading tools can pick it up.Reference implementation (POC)
A working POC has been running in production for several days. Branch:
https://github.com/ShinyChang/openab/tree/feat/download-to-disk-attachments
Diff: main...ShinyChang:openab:feat/download-to-disk-attachments
Touches three files:
src/media.rs— addsdownload_to_disk()+sanitize_path_component()src/slack.rs— replaces image-only fallback with image-OR-disk (bucket =ts)src/discord.rs— same shape (bucket =msg.id)Files land at
${OPENAB_ATTACHMENTS_DIR:-/tmp/openab-attachments}/<bucket_id>/<filename>. Tested in production with .xlsx / .pdf / .mp4 attachments via Slack — agent receives the path block, triggers the matching skill, processes the file end-to-end.Known gaps vs the #254 security checklist
The POC ships fast but does not yet meet the bar maintainers proposed in #254:
..escape)stream_promptreturns (collectPathBufs,remove_dir_all)<<<EXTERNAL_UNTRUSTED_CONTENT>>>markers around file path block[<adapter>.attachments]TOML section vs current env-var only)Hybrid (small-text inline vs disk-fallback) is already covered by the existing
is_text_filebranch landing first in the routing chain, so no new logic needed there.