Skip to content

feat(slack): outbound file sharing — extract code blocks and upload to Slack (#282) - #283

Merged
vybe merged 1 commit into
mainfrom
feature/282-slack-outbound-file-sharing
Apr 8, 2026
Merged

vybe merged 1 commit into
mainfrom
feature/282-slack-outbound-file-sharing

Conversation

@pavshulin

Copy link
Copy Markdown
Contributor

Summary

  • Extract fenced code blocks (CSV, JSON, HTML, YAML, SQL, Python, JS, TS) from agent text responses and upload them as native Slack file attachments via the V2 Upload API
  • Channel-agnostic extraction in message_router.py, Slack-specific upload in slack_adapter.py / slack_service.py
  • files:write OAuth scope added — requires Slack app reinstall to workspace
  • 27 unit tests covering extraction logic, language mapping, limits, and edge cases

How it works

Agent responds with:     "Here's the data: ```csv\nname,revenue\nAcme,150000\n...```"
Router extracts:         response_1.csv (the CSV content)
Slack receives:          File attachment + clean text "(see attached: response_1.csv)"

Security

No change to agent tool restrictions (WebSearch, WebFetch only for Slack users). Extraction operates on the agent's text response — no file system access, no Write/Bash tools granted.

Limits

Limit Value
Min block size 100 chars
Max files per response 5
Max per-file size 500 KB
Max total size 2 MB

Deployment note

After deploying, reinstall the Slack app to the workspace to pick up the files:write scope (same process as files:read for #222).

Test plan

  • 27 unit tests pass (tests/unit/test_slack_outbound_files.py)
  • Integration: Slack user asks agent for CSV data → receives downloadable file
  • Verify CSV opens correctly in Excel/Numbers
  • Verify small code blocks (<100 chars) stay inline
  • Verify non-data languages (bash, rust) stay inline
  • Verify text message delivered even if upload fails

Closes #282

🤖 Generated with Claude Code

…o Slack (#282)

When an agent responds with fenced code blocks (CSV, JSON, HTML, etc.)
in a Slack conversation, the message router now extracts qualifying
blocks, uploads them as native Slack file attachments via the V2 Upload
API, and sends a clean text message with file references.

Architecture:
- Channel-agnostic extraction in message_router._extract_outbound_files()
- OutboundFile model + files field on ChannelResponse (base.py)
- Slack V2 Upload: getUploadURLExternal → POST → completeUploadExternal
- files:write OAuth scope added (requires Slack app reinstall)

Security: No change to agent tool restrictions. Extraction operates on
the agent's text response only — no file system access, no Write/Bash.

Limits: 100 char min block size, 5 files max, 500KB per block, 2MB total.

Tests: 27 unit tests covering extraction, language mapping, limits, and
edge cases (unicode, nested blocks, unclosed fences, size limits).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

@vybe vybe left a comment

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.

LGTM — clean channel-agnostic design, solid V2 Upload API implementation, good test coverage (27 tests). Ship it.

@vybe
vybe merged commit ff934b1 into main Apr 8, 2026
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.

feat: Outbound file sharing — extract code blocks from agent responses and upload to Slack

2 participants