#22781: fix(webchat): persist chat attachments after guards and expose media paths
app: web-ui
gateway
size: M
Cluster:
Attachment Processing Enhancements
## Summary
- Problem: `chat.send` webchat attachments were previously exposed to the agent as inline base64 blocks only, and persistence happened before request guards.
- Why it matters: this breaks file-path-based media workflows and can create unnecessary temp files for requests that are later blocked/deduped.
- What changed:
- Persist attachments only after policy/idempotency/request guards pass.
- Return `MediaPath`/`MediaPaths` and `MediaType`/`MediaTypes` in context while still passing inline images for model vision input.
- Add/expand gateway tests for persisted media-path behavior.
- Scope boundary: only gateway webchat attachment flow (`chat.send`) and related tests.
## Change Type
- [x] Bug fix
- [ ] Feature
- [ ] Refactor
- [ ] Docs
- [ ] Security hardening
- [ ] Chore/infra
## Scope
- [ ] Gateway / orchestration
- [x] Skills / tool execution
- [ ] Auth / tokens
- [ ] Memory / storage
- [ ] Integrations
- [x] API / contracts
- [ ] UI / DX
- [ ] CI/CD / infra
## Linked Issue/PR
- Related #22314 (split into focused PRs for faster review)
## Repro + Verification
- `pnpm vitest run src/gateway/chat-attachments.test.ts`
- `pnpm check`
## Compatibility / Migration
- Backward compatible: Yes
- Config/env changes: No
- Migration needed: No
## Security Impact
- New permissions/capabilities: No
- Secrets/tokens handling changed: No
- New/changed network calls: No
- Command/tool execution surface changed: Yes (context includes persisted media paths)
- Data access scope changed: No
<!-- greptile_comment -->
<h3>Greptile Summary</h3>
This PR moves webchat attachment parsing and persistence to occur **after** policy, idempotency, and request guards, preventing unnecessary file I/O for blocked requests. The change adds `MediaPath`/`MediaPaths` and `MediaType`/`MediaTypes` to the agent context, enabling file-path-based media workflows while maintaining backward compatibility by still passing inline base64 images for vision models.
- Moved `parseMessageWithAttachments` call in `chat.ts` from before guards (line 751) to after all guards pass (line 809-824)
- Added `persistImagesToDisk: true` flag when calling `parseMessageWithAttachments` to enable file persistence
- Persisted files use secure permissions (0o600) and are written to a safe tmp directory
- Agent context now includes both inline images (for model vision) and persisted file paths (for file-based tools)
- Added comprehensive test coverage for the persistence behavior in both unit and e2e tests
<h3>Confidence Score: 5/5</h3>
- This PR is safe to merge with minimal risk
- The changes are well-architected with proper security considerations (file permissions, path sanitization), comprehensive test coverage (both unit and e2e tests), and clear separation of concerns. The move of attachment parsing after guards is a logical improvement that prevents wasted resources. No breaking changes or security vulnerabilities introduced.
- No files require special attention
<sub>Last reviewed commit: 368be6b</sub>
<!-- greptile_other_comments_section -->
<!-- /greptile_comment -->
Most Similar PRs
#22113: feat: support non-image file attachments in webchat chat.send
by Kt-L · 2026-02-20
85.4%
#10441: webchat: switch file uploads to HTTP /uploads
by supernewbilityxht1 · 2026-02-06
80.2%
#4327: Gateway Web Chat: add image/file upload (attach, drag-drop, paste)
by RogerHsu7 · 2026-01-30
78.2%
#13104: fix: persist user command message in chat transcript
by mcaxtr · 2026-02-10
77.6%
#8284: Fix: Webchat images now persist after sending
by vishaltandale00 · 2026-02-03
76.7%
#14966: fix(webchat): preserve user message visibility after chat.send
by BenediktSchackenberg · 2026-02-12
76.7%
#6805: fix: increase WebSocket MAX_PAYLOAD_BYTES to 6MB for attachments
by cortexuvula · 2026-02-02
76.4%
#16949: fix(gateway): deliver chat:final even when sessionKey is unresolved (…
by ekleziast · 2026-02-15
76.4%
#23627: fix(telegram,feishu): pass mediaLocalRoots through channel action a...
by rockkoca · 2026-02-22
76.2%
#22178: test(web): allow fixture roots in media local file tests
by Kansodata · 2026-02-20
75.9%