Skip to content

Remove file attachment, image/vision, and PDF input subsystem#181

Merged
chloeilabs merged 2 commits into
mainfrom
claude/relaxed-franklin-ap1wbm
Jun 15, 2026
Merged

Remove file attachment, image/vision, and PDF input subsystem#181
chloeilabs merged 2 commits into
mainfrom
claude/relaxed-franklin-ap1wbm

Conversation

@chloeilabs

@chloeilabs chloeilabs commented Jun 15, 2026

Copy link
Copy Markdown
Owner

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/uploads upload + download routes, private-blob-storage, agent-attachment-blobs (blob hydration), image-vision-preprocessor (+ utils), pdf-attachment-preprocessor, pdf-text-extraction
  • Client prompt-form/attachments.ts, the IndexedDB agent-attachment-store, and shared agent/attachments.ts

Surgically edited:

  • agent-runtime — dropped the blob-hydrate → PDF-prep → vision-describe pipeline; now calls toModelMessages directly
  • agent-runtime-messages — collapsed to text-only content
  • agent-route — removed the attachment Zod schema, validateAgentRequestAttachments, and the Buffer import
  • prompt-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 rendering
  • models.ts (capability helpers + preprocessor constants), messages.ts (MessageMetadata.attachments), thread-payload.ts (attachment schema/sanitizer), system-instructions.ts, index.ts

Cleanup:

  • Dropped deps @vercel/blob, pdf-parse, @napi-rs/canvas (+ lockfile + serverExternalPackages)
  • Removed blob: from the CSP img-src/media-src/worker-src directives
  • Removed BLOB_READ_WRITE_TOKEN from .env.example, README, CLAUDE.md, and the docs/ runbooks

Backward compatibility

Existing threads that have stored attachment metadata still load — sanitizeMessageMetadata rebuilds metadata from known keys only, so the now-unknown attachments key is dropped before the strict Zod parse. No DB migration needed (attachments lived inside the thread.messages JSONB, not a column).

Verification

  • pnpm lint (zero warnings), pnpm typecheck, pnpm format
  • pnpm test193/193 unit tests pass ✓
  • pnpm build ✓ (route table confirms /api/uploads is gone) · pnpm bundle:budget ✓ (~12.4 MiB total, 780 KB largest chunk)
  • pnpm test:smoke:mock7/7 Playwright flows pass against a fresh prod build ✓
  • Repo-wide grep sweep for every attachment identifier: no matches

https://claude.ai/code/session_01Q363HWzRisA9WbXjkJBvsW


Generated by Claude Code

Summary by CodeRabbit

Release Notes

  • Refactor

    • Removed file attachment upload and download functionality from the agent
    • Removed Vercel Blob storage integration and private blob handling
    • Removed PDF text extraction and image preprocessing pipelines
    • Removed client-side attachment storage and metadata persistence
    • Simplified message content to exclude attachment data
  • Chores

    • Removed dependencies: @vercel/blob, @napi-rs/canvas, pdf-parse
    • Updated Content Security Policy to disallow blob: URLs
    • Removed attachment-related test coverage and documentation

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
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@vercel

vercel Bot commented Jun 15, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
chloei Ready Ready Preview, Comment Jun 15, 2026 3:59am

@coderabbitai

coderabbitai Bot commented Jun 15, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@chloeilabs, we couldn't start this review because you've reached your PR review rate limit.

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 @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

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 configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: a432e728-999e-445a-a22e-fbac7219b99c

📥 Commits

Reviewing files that changed from the base of the PR and between f897257 and 37a0155.

📒 Files selected for processing (2)
  • docs/vercel-production-launch-readiness.md
  • src/components/agent/prompt-form/prompt-form.tsx
📝 Walkthrough

Walkthrough

Removes the entire agent file attachment system end-to-end: shared attachment types and constants, Vercel Blob-backed private storage, POST/GET upload API routes, PDF text extraction, image vision and PDF preprocessing pipeline, IndexedDB client persistence, all attachment state in the session hook, prompt form UI, user message attachment display, and all corresponding tests and configuration entries.

Changes

Remove file attachment system

Layer / File(s) Summary
Shared attachment types and model capability constants removed
src/lib/shared/agent/attachments.ts, src/lib/shared/agent/messages.ts, src/lib/shared/index.ts, src/lib/shared/llm/models.ts
Deletes the entire attachments.ts shared module, removes attachments from MessageMetadata, drops the module re-export from the shared index, and removes modelSupportsImageInput, modelSupportsFileInput, VISION_PREPROCESSOR_MODEL, and PDF_PREPROCESSOR_MODEL from the model registry.
Server blob storage, upload routes, and PDF extraction removed
src/lib/server/private-blob-storage.ts, src/app/api/uploads/route.ts, src/app/api/uploads/[...pathname]/route.ts, src/lib/server/pdf-text-extraction.ts, package.json, next.config.mjs
Deletes all private blob pathname/upload/read helpers, both upload API routes, and the pdf-parse+@napi-rs/canvas PDF extraction pipeline. Removes the three associated npm dependencies and drops blob: from CSP img-src/worker-src and @napi-rs/canvas from serverExternalPackages.
Server LLM preprocessing pipeline simplified
src/lib/server/llm/image-vision-preprocessor.ts, src/lib/server/llm/image-vision-preprocessor-utils.ts, src/lib/server/llm/pdf-attachment-preprocessor.ts, src/lib/server/agent-attachment-blobs.ts, src/lib/server/llm/agent-runtime-messages.ts, src/lib/server/llm/agent-runtime.ts, src/lib/server/agent-route.ts
Removes the image vision and PDF attachment preprocessors, blob hydration helper, and all attachment validation from agentStreamRequestSchema. AgentInputMessage loses its attachments field; toModelMessages writes plain string content; startAgentRuntimeStream calls toModelMessages directly instead of the three-step preprocess chain.
Thread payload sanitization strips attachment metadata
src/lib/server/thread-payload.ts
Removes attachment-related schemas, sanitizeAttachmentMetadata, and the attachments spread from sanitizeMessageMetadata; messageMetadataSchema now only validates optional parts.
Client attachment state management and session handling removed
src/components/agent/home/agent-attachment-store.ts, src/components/agent/home/agent-session-state.ts, src/components/agent/home/use-agent-session.ts, src/components/agent/home/use-thread-store.ts, src/components/agent/home/home-agent-utils.ts
Deletes the IndexedDB attachment store. Removes AttachmentPayloadsByThread and payload helpers from session state, all attachment ref/effect/pruning/persistence from the session hook, deleteThreadAttachments from thread deletion, and attachments parameters from toRequestMessages and appendUserMessage.
Prompt form and user message UI simplified
src/components/agent/prompt-form/attachments.ts, src/components/agent/prompt-form/prompt-form.tsx, src/components/agent/messages/user-message.tsx, src/components/agent/home/home-content.tsx
Deletes the prompt-form attachments helper. Removes file input, drag/drop, attachment chip, keyboard shortcut, toast, and pending-attachment UI from PromptForm; updates onSubmit and QueuedPromptSubmission to drop attachments. Removes attachment chip rendering from user messages and attachment forwarding from both submit paths in HomePageContent.
System prompt, config, and docs updated
src/lib/shared/llm/system-instructions.ts, .env.example, README.md, CLAUDE.md, docs/managed-integrations-rollout.md, docs/vercel-production-launch-readiness.md
Removes "attachments" from DEFAULT_OPERATING_INSTRUCTION data-trust rules, removes BLOB_READ_WRITE_TOKEN from all env var documentation, and updates the rollout runbook to replace blob smoke-test steps with a finance-analysis workflow validation step.
Attachment-related tests removed and updated
tests/agent-attachments-contract.test.mjs, tests/agent-runtime-attachments.test.mjs, tests/private-blob-storage.test.mjs, tests/agent-helper-behavior.test.mjs, tests/agent-session-state.test.mjs, tests/home-agent-utils.test.mjs, tests/home-content-contract.test.mjs, tests/thread-payload-contract.test.mjs, tests/prompt-form-contract.test.mjs, tests/managed-integrations.test.mjs, tests/model-registry.test.mjs, tests/smoke/mock-authenticated-chat.spec.mjs, tests/agent-system-prompt.test.mjs
Deletes three entire attachment test suites and removes attachment test blocks from ten other test files. Updates the system-prompt contract test regex and the PromptForm contract test boundary.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related PRs

  • chloeilabs/chloei#32: This PR removes the entire attachment pipeline that PR #32 originally introduced, including schema validation, runtime conversion, and prompt UI state.
  • chloeilabs/chloei#64: This PR deletes src/app/api/uploads/* and src/lib/server/private-blob-storage.ts that PR #64 added as the Vercel Blob-backed upload/download infrastructure.
  • chloeilabs/chloei#121: Both PRs modify next.config.mjs's buildContentSecurityPolicy() by adjusting blob: allowances in img-src and worker-src directives.

Poem

🐇 Hop, hop, snippety-snip!
The blob URLs have taken their trip.
No more PDFs, no image queue—
Just plain text messages, shiny and new!
The IndexedDB stores are swept clean away,
A lighter bundle hops into the day. ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately and specifically describes the main change: removing file attachment, image/vision, and PDF input subsystem. It is concise, clear, and directly summarizes the primary objective of the changeset.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/relaxed-franklin-ap1wbm

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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 win

Add 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 win

Avoid 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 in src/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

📥 Commits

Reviewing files that changed from the base of the PR and between f970da6 and f897257.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (46)
  • .env.example
  • CLAUDE.md
  • README.md
  • docs/managed-integrations-rollout.md
  • docs/vercel-production-launch-readiness.md
  • next.config.mjs
  • package.json
  • src/app/api/uploads/[...pathname]/route.ts
  • src/app/api/uploads/route.ts
  • src/components/agent/home/agent-attachment-store.ts
  • src/components/agent/home/agent-session-state.ts
  • src/components/agent/home/home-agent-utils.ts
  • src/components/agent/home/home-content.tsx
  • src/components/agent/home/use-agent-session.ts
  • src/components/agent/home/use-thread-store.ts
  • src/components/agent/messages/user-message.tsx
  • src/components/agent/prompt-form/attachments.ts
  • src/components/agent/prompt-form/prompt-form.tsx
  • src/lib/server/agent-attachment-blobs.ts
  • src/lib/server/agent-route.ts
  • src/lib/server/llm/agent-runtime-messages.ts
  • src/lib/server/llm/agent-runtime.ts
  • src/lib/server/llm/image-vision-preprocessor-utils.ts
  • src/lib/server/llm/image-vision-preprocessor.ts
  • src/lib/server/llm/pdf-attachment-preprocessor.ts
  • src/lib/server/pdf-text-extraction.ts
  • src/lib/server/private-blob-storage.ts
  • src/lib/server/thread-payload.ts
  • src/lib/shared/agent/attachments.ts
  • src/lib/shared/agent/messages.ts
  • src/lib/shared/index.ts
  • src/lib/shared/llm/models.ts
  • src/lib/shared/llm/system-instructions.ts
  • tests/agent-attachments-contract.test.mjs
  • tests/agent-helper-behavior.test.mjs
  • tests/agent-runtime-attachments.test.mjs
  • tests/agent-session-state.test.mjs
  • tests/agent-system-prompt.test.mjs
  • tests/home-agent-utils.test.mjs
  • tests/home-content-contract.test.mjs
  • tests/managed-integrations.test.mjs
  • tests/model-registry.test.mjs
  • tests/private-blob-storage.test.mjs
  • tests/prompt-form-contract.test.mjs
  • tests/smoke/mock-authenticated-chat.spec.mjs
  • tests/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

Comment thread docs/vercel-production-launch-readiness.md
- 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
@chloeilabs chloeilabs merged commit d7970ed into main Jun 15, 2026
7 checks passed
@chloeilabs chloeilabs deleted the claude/relaxed-franklin-ap1wbm branch June 15, 2026 04:04
@chloeilabs chloeilabs mentioned this pull request Jun 15, 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.

2 participants