Remove file attachment, image/vision, and PDF input subsystem#181
Conversation
Delete the entire attachment / vision / PDF input capability and clean up every vestige. The agent is now text-only (web search + code execution tools remain unchanged). Removed: - Upload API routes (/api/uploads), private Vercel Blob storage, blob-backed attachment hydration, the image-vision and PDF preprocessors, and PDF text extraction. - Client attachment UI (file picker, chips, drag-and-drop, Cmd/; shortcut), the IndexedDB attachment store, and the prompt-form/message-render plumbing. - Shared attachment types/constants, the MessageMetadata.attachments field, the thread-payload attachment schema/sanitizer, and the model image/file input capability helpers + vision/PDF preprocessor model constants. - Dependencies @vercel/blob, pdf-parse, @napi-rs/canvas (and the matching serverExternalPackages entry); dropped blob: from the CSP directives and BLOB_READ_WRITE_TOKEN from env/docs. Existing threads with stored attachment metadata still load — the payload sanitizer rebuilds metadata from known keys, so the unknown attachments key is dropped before the strict Zod parse. Updated/trimmed the affected unit, contract, and smoke tests, plus README/CLAUDE/docs. https://claude.ai/code/session_01Q363HWzRisA9WbXjkJBvsW
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Warning Review limit reached
More reviews will be available in 45 minutes and 48 seconds. Learn how PR review limits work. Your organization has used up its prepaid credits, and credit purchases are no longer available. Enable the review add-on in the billing tab to keep reviews running — you're only billed for reviews past your plan's rate limits ($0.25/file). ⌛ How to resolve this issue?After more reviews become available, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available. Please see our Fair Usage Limits Policy for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (2)
📝 WalkthroughWalkthroughRemoves the entire agent file attachment system end-to-end: shared attachment types and constants, Vercel Blob-backed private storage, ChangesRemove file attachment system
Estimated code review effort🎯 4 (Complex) | ⏱️ ~50 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
src/components/agent/prompt-form/prompt-form.tsx (1)
397-412:⚠️ Potential issue | 🟠 Major | ⚡ Quick winAdd an accessible name to the icon-only submit/stop button.
The button’s visible state is conveyed only by SVG icons, so screen reader users cannot reliably identify “send” vs “stop”.
♿ Proposed fix
<Button type="submit" size="iconSm" disabled={isSubmitButtonDisabled} + aria-label={ + isFormPending + ? "Sending message" + : isStreaming && !trimmedMessage + ? "Stop response" + : "Send message" + } className="shrink-0 ring-offset-background" >🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/components/agent/prompt-form/prompt-form.tsx` around lines 397 - 412, The submit/stop Button component displays only SVG icons without any accessible name for screen reader users. Add an aria-label attribute to the Button that dynamically describes its function based on the current state: when isFormPending is true, use a label indicating the form is processing; when isStreaming and trimmedMessage is empty, use a label indicating to stop the stream; otherwise, use a label indicating to send the message. This ensures screen reader users can understand the button's purpose regardless of which icon is displayed.src/components/agent/messages/user-message.tsx (1)
82-83:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winAvoid rendering legacy attachment-only turns as blank messages.
After attachment metadata is stripped, older attachment-only user turns can have empty
content; this renders an empty clickable bubble and loses conversation context.🧩 Proposed fallback
const { copyToClipboard, isCopied } = useCopyToClipboard() const hasCopyableContent = message.content.trim().length > 0 + const visibleContent = hasCopyableContent + ? message.content + : "No text content" @@ )} <div ref={messageContentRef} className="whitespace-pre-wrap"> - {message.content} + {visibleContent} </div>Also applies to: 311-313
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/components/agent/messages/user-message.tsx` around lines 82 - 83, The hasCopyableContent check at lines 82-83 only evaluates whether the message content is non-empty, which causes legacy attachment-only user turns to render as blank after metadata stripping. Update the hasCopyableContent condition to also check if attachments are present, not just content length. This same fix needs to be applied at the sibling location around lines 311-313. The condition should return true if either the content is non-empty OR if there are attachments available on the message object.
🧹 Nitpick comments (1)
src/components/agent/messages/user-message.tsx (1)
1-1: Add"use client"directive.This component uses React hooks but lacks the required
"use client"directive. Per project conventions, all client components insrc/components/must include this directive at the top of the file before imports.Proposed fix
+"use client" + import { Check, Copy, CornerRightUp, Loader2, X } from "lucide-react"🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/components/agent/messages/user-message.tsx` at line 1, The user-message.tsx component is a client component that uses React hooks but is missing the required "use client" directive at the top of the file. Add the "use client" directive as the very first line of the file, before all import statements including the lucide-react imports, following project conventions for client components in the src/components/ directory.Source: Coding guidelines
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@docs/vercel-production-launch-readiness.md`:
- Line 100: Remove or replace the stale recommendation on Line 165 that suggests
using Vercel Blob for large files, as this conflicts with the blob/attachment
removal in this PR. Delete the sentence referencing Vercel Blob storage for
large files and replace it with guidance on the current storage path approach
that aligns with the updated architecture, or simply remove the sentence if it
is no longer relevant to the production launch readiness documentation.
---
Outside diff comments:
In `@src/components/agent/messages/user-message.tsx`:
- Around line 82-83: The hasCopyableContent check at lines 82-83 only evaluates
whether the message content is non-empty, which causes legacy attachment-only
user turns to render as blank after metadata stripping. Update the
hasCopyableContent condition to also check if attachments are present, not just
content length. This same fix needs to be applied at the sibling location around
lines 311-313. The condition should return true if either the content is
non-empty OR if there are attachments available on the message object.
In `@src/components/agent/prompt-form/prompt-form.tsx`:
- Around line 397-412: The submit/stop Button component displays only SVG icons
without any accessible name for screen reader users. Add an aria-label attribute
to the Button that dynamically describes its function based on the current
state: when isFormPending is true, use a label indicating the form is
processing; when isStreaming and trimmedMessage is empty, use a label indicating
to stop the stream; otherwise, use a label indicating to send the message. This
ensures screen reader users can understand the button's purpose regardless of
which icon is displayed.
---
Nitpick comments:
In `@src/components/agent/messages/user-message.tsx`:
- Line 1: The user-message.tsx component is a client component that uses React
hooks but is missing the required "use client" directive at the top of the file.
Add the "use client" directive as the very first line of the file, before all
import statements including the lucide-react imports, following project
conventions for client components in the src/components/ directory.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: b3a1edbf-0512-4bf6-b755-9eb8fb941961
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (46)
.env.exampleCLAUDE.mdREADME.mddocs/managed-integrations-rollout.mddocs/vercel-production-launch-readiness.mdnext.config.mjspackage.jsonsrc/app/api/uploads/[...pathname]/route.tssrc/app/api/uploads/route.tssrc/components/agent/home/agent-attachment-store.tssrc/components/agent/home/agent-session-state.tssrc/components/agent/home/home-agent-utils.tssrc/components/agent/home/home-content.tsxsrc/components/agent/home/use-agent-session.tssrc/components/agent/home/use-thread-store.tssrc/components/agent/messages/user-message.tsxsrc/components/agent/prompt-form/attachments.tssrc/components/agent/prompt-form/prompt-form.tsxsrc/lib/server/agent-attachment-blobs.tssrc/lib/server/agent-route.tssrc/lib/server/llm/agent-runtime-messages.tssrc/lib/server/llm/agent-runtime.tssrc/lib/server/llm/image-vision-preprocessor-utils.tssrc/lib/server/llm/image-vision-preprocessor.tssrc/lib/server/llm/pdf-attachment-preprocessor.tssrc/lib/server/pdf-text-extraction.tssrc/lib/server/private-blob-storage.tssrc/lib/server/thread-payload.tssrc/lib/shared/agent/attachments.tssrc/lib/shared/agent/messages.tssrc/lib/shared/index.tssrc/lib/shared/llm/models.tssrc/lib/shared/llm/system-instructions.tstests/agent-attachments-contract.test.mjstests/agent-helper-behavior.test.mjstests/agent-runtime-attachments.test.mjstests/agent-session-state.test.mjstests/agent-system-prompt.test.mjstests/home-agent-utils.test.mjstests/home-content-contract.test.mjstests/managed-integrations.test.mjstests/model-registry.test.mjstests/private-blob-storage.test.mjstests/prompt-form-contract.test.mjstests/smoke/mock-authenticated-chat.spec.mjstests/thread-payload-contract.test.mjs
💤 Files with no reviewable changes (31)
- src/lib/server/llm/image-vision-preprocessor-utils.ts
- src/app/api/uploads/[...pathname]/route.ts
- src/components/agent/prompt-form/attachments.ts
- .env.example
- src/lib/server/llm/image-vision-preprocessor.ts
- src/components/agent/home/agent-attachment-store.ts
- src/app/api/uploads/route.ts
- src/lib/shared/index.ts
- src/lib/server/agent-attachment-blobs.ts
- tests/agent-runtime-attachments.test.mjs
- src/lib/server/private-blob-storage.ts
- src/lib/server/pdf-text-extraction.ts
- tests/agent-attachments-contract.test.mjs
- tests/private-blob-storage.test.mjs
- tests/model-registry.test.mjs
- src/lib/shared/agent/attachments.ts
- tests/agent-session-state.test.mjs
- tests/home-content-contract.test.mjs
- tests/thread-payload-contract.test.mjs
- src/lib/server/llm/pdf-attachment-preprocessor.ts
- src/lib/shared/agent/messages.ts
- src/lib/server/thread-payload.ts
- src/lib/shared/llm/models.ts
- tests/smoke/mock-authenticated-chat.spec.mjs
- package.json
- tests/home-agent-utils.test.mjs
- src/components/agent/home/use-thread-store.ts
- tests/managed-integrations.test.mjs
- src/lib/server/agent-route.ts
- src/components/agent/home/agent-session-state.ts
- tests/agent-helper-behavior.test.mjs
- prompt-form: add a dynamic aria-label to the icon-only submit/stop button so screen readers announce Send / Stop / Sending state. - docs/vercel-production-launch-readiness: drop the leftover "use Vercel Blob for large media" recommendation (blob storage was removed in this PR). https://claude.ai/code/session_01Q363HWzRisA9WbXjkJBvsW
Summary
Removes the entire file-attachment / image-vision / PDF-input capability and cleans up every vestige. The agent is now text-only — web search and code execution tools are unchanged.
Net diff: 47 files, −4,888 / +82 lines.
What was removed
Deleted whole files (11 source + 3 test):
/api/uploadsupload + download routes,private-blob-storage,agent-attachment-blobs(blob hydration),image-vision-preprocessor(+ utils),pdf-attachment-preprocessor,pdf-text-extractionprompt-form/attachments.ts, the IndexedDBagent-attachment-store, and sharedagent/attachments.tsSurgically edited:
agent-runtime— dropped the blob-hydrate → PDF-prep → vision-describe pipeline; now callstoModelMessagesdirectlyagent-runtime-messages— collapsed to text-only contentagent-route— removed the attachment Zod schema,validateAgentRequestAttachments, and theBufferimportprompt-form— removed the file picker, attachment chips, drag-and-drop, and the⌘;shortcut (textarea / model selector / Research toggle / submit kept)use-agent-session,agent-session-state,home-agent-utils,home-content,user-message,use-thread-store— removed attachment state, params, persistence, and renderingmodels.ts(capability helpers + preprocessor constants),messages.ts(MessageMetadata.attachments),thread-payload.ts(attachment schema/sanitizer),system-instructions.ts,index.tsCleanup:
@vercel/blob,pdf-parse,@napi-rs/canvas(+ lockfile +serverExternalPackages)blob:from the CSPimg-src/media-src/worker-srcdirectivesBLOB_READ_WRITE_TOKENfrom.env.example, README, CLAUDE.md, and thedocs/runbooksBackward compatibility
Existing threads that have stored attachment metadata still load —
sanitizeMessageMetadatarebuilds metadata from known keys only, so the now-unknownattachmentskey is dropped before the strict Zod parse. No DB migration needed (attachments lived inside thethread.messagesJSONB, not a column).Verification
pnpm lint(zero warnings),pnpm typecheck,pnpm format✓pnpm test— 193/193 unit tests pass ✓pnpm build✓ (route table confirms/api/uploadsis gone) ·pnpm bundle:budget✓ (~12.4 MiB total, 780 KB largest chunk)pnpm test:smoke:mock— 7/7 Playwright flows pass against a fresh prod build ✓https://claude.ai/code/session_01Q363HWzRisA9WbXjkJBvsW
Generated by Claude Code
Summary by CodeRabbit
Release Notes
Refactor
Chores
@vercel/blob,@napi-rs/canvas,pdf-parseblob:URLs