feat(server): accept PDF, ZIP, and other file uploads up to 50MB - #8235
Conversation
|
Important Review skippedAuto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
Comment |
Thread transfer impact✅ Thread transfer remains within every enforced ceiling.
Baseline: Scenario and decoded snapshot size10 historical turns, 5 command tools per turn, 878.9 KiB retained MCP result per historical turn, and a 1.05 MiB retained result in the measured turn.
Updated in place by a trusted workflow. PR artifacts are strictly validated and never executed. |
ApprovabilityVerdict: Not approved Macroscope's review found this PR not approvable — This PR introduces generic file uploads and downloads up to 50 MiB, including streaming filesystem I/O, signed download metadata, provider-specific attachment handling, and persisted schema changes. The broad runtime and compatibility impact, plus security-sensitive MIME and browser-disposition behavior, warrants human review. You can add or adjust custom eligibility rules. Learn more. |
Uploads of any file type now stream through the attachment route with a byte cap instead of buffering in memory. The chat attachment schema gains a "file" member plus a catch-all for attachment types from newer builds, so older readers keep decoding messages instead of failing. Downloads serve the real filename and Content-Type from the signed asset claims. Each provider adapter decides what its provider ingests natively: OpenCode sends generic files as file parts, the others send images only and rely on the file path added to the turn text. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ract The attachment contract now carries file and unknown members. Web renders images only for now and filters the rest through an isImageAttachment guard, so unknown attachment types degrade to nothing instead of a decode failure. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The tolerant attachment member no longer accepts the image and file discriminators, so a malformed known attachment fails its own schema instead of bypassing size and mime checks. Files named *.part store as .bin so the stale-upload sweep cannot delete them. Download dispositions survive unpaired surrogates in filenames. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
A ZIP sent as a native file part makes OpenCode's Anthropic path throw AI_UnsupportedFunctionalityError before the turn starts, so the file path in the prompt never helps. Images, text, and PDFs stay native; everything else rides as the path line like the other providers. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Model APIs reject BMP, AVIF, and SVG image parts, and OpenCode's direct-attachment path caps out at 20 MB. Native file parts are now PNG/JPEG/GIF/WebP, text, and PDF at or under that size; the rest ride as the prompt's file path line. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Remove partial upload files when request streams fail or get interrupted. Keep chunked requests open until the server sends an oversize response. Encode extended filenames correctly, skip files in the mobile image renderer, and document old-client and old-server replay limits. Built with GPT-5.6 Sol in the Codex harness.
c669b79 to
826e1e3
Compare
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 826e1e3. Configure here.
Narrow XML download detection so Office Open XML files keep their official content types while actual XML and SVG content stays octet-stream. Built with GPT-5.6 Sol in the Codex harness.
…gdotgg#8235) Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
## What's Changed * fix(codex): show sub-agent models by @t3dotgg in pingdotgg/t3code#8502 * feat(analytics): report connected client platforms by @t3dotgg in pingdotgg/t3code#8481 * feat(server): accept PDF, ZIP, and other file uploads up to 50MB by @t3dotgg in pingdotgg/t3code#8235 * feat(web): toggle a thread's pin from the keyboard by @ipanasenko in pingdotgg/t3code#8440 **Full Changelog**: pingdotgg/t3code@v0.0.36-nightly.20260828.1209...v0.0.36-nightly.20260828.1210 Upstream release: https://github.com/pingdotgg/t3code/releases/tag/v0.0.36-nightly.20260828.1210
Round-11 rebase onto main (25 commits). Reverse-merged main's new work into the v2 cutover files: #8395 muted ordinary-tool-failure treatment (v2-adapted workEntrySignalsSevereFailure keyed on error items), #5931 sidebar project-filter combobox + #4c51 keyboard pin/settle with their ChatView support graph, the auto-settle-mode migration through threadSettled/threadListV2, #8235 file/unknown attachment schemas moved into chatAttachment.ts with nullable attachment paths, #8481 client analytics through the v2 ws layer, #8480 OpenCode server owner wired into the driver beside the v2 orchestration adapter, and the mobile semantic-theme migration applied to branch-only components. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Round-11 rebase onto main (25 commits). Reverse-merged main's new work into the v2 cutover files: #8395 muted ordinary-tool-failure treatment (v2-adapted workEntrySignalsSevereFailure keyed on error items), #5931 sidebar project-filter combobox + #4c51 keyboard pin/settle with their ChatView support graph, the auto-settle-mode migration through threadSettled/threadListV2, #8235 file/unknown attachment schemas moved into chatAttachment.ts with nullable attachment paths, #8481 client analytics through the v2 ws layer, #8480 OpenCode server owner wired into the driver beside the v2 orchestration adapter, and the mobile semantic-theme migration applied to branch-only components. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Round-11 rebase onto main (25 commits). Reverse-merged main's new work into the v2 cutover files: #8395 muted ordinary-tool-failure treatment (v2-adapted workEntrySignalsSevereFailure keyed on error items), #5931 sidebar project-filter combobox + #4c51 keyboard pin/settle with their ChatView support graph, the auto-settle-mode migration through threadSettled/threadListV2, #8235 file/unknown attachment schemas moved into chatAttachment.ts with nullable attachment paths, #8481 client analytics through the v2 ws layer, #8480 OpenCode server owner wired into the driver beside the v2 orchestration adapter, and the mobile semantic-theme migration applied to branch-only components. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
## What's Changed * fix(grok): improve skills, plans, usage, and turn reliability by @t3dotgg in pingdotgg/t3code#8358 * fix(server): recover stale Codex approval callbacks by @luckyPipewrench in pingdotgg/t3code#5195 * test(server): remove duplicate missing worktree test by @t3-code[bot] in pingdotgg/t3code#8252 * fix(server): replay all un-applied events during projection bootstrap by @krutftw in pingdotgg/t3code#7538 * test: remove low-signal test files by @t3-code[bot] in pingdotgg/t3code#8397 * test: prune trivial error and layout tests by @t3-code[bot] in pingdotgg/t3code#8400 * Fix Android adaptive launcher icon by @colonelpanic8 in pingdotgg/t3code#4332 * feat(web): split provider settings into list and editor by @t3dotgg in pingdotgg/t3code#8380 * fix(codex): accept Codex 0.150 account plans by @gsimone in pingdotgg/t3code#8447 * fix(tooling): allow ignored-only staged changes by @juliusmarminge in pingdotgg/t3code#8468 * fix(mobile): keep iOS home header stable by @juliusmarminge in pingdotgg/t3code#8467 * fix(web): stop showing red x summaries for ordinary tool failures by @t3dotgg in pingdotgg/t3code#8395 * fix(mobile): refine Git action toast glass styling by @juliusmarminge in pingdotgg/t3code#8399 * fix(desktop): allow preview automation in agent-created threads by @t3dotgg in pingdotgg/t3code#8483 * test(web): remove redundant cache key test by @t3-code[bot] in pingdotgg/t3code#8484 * fix(release): move nightly schedule to minute 38 by @t3dotgg in pingdotgg/t3code#8509 * fix(web): stabilize the provider settings editor by @t3dotgg in pingdotgg/t3code#8472 * fix(web): open GitHub pull requests in browser when loading fails by @t3dotgg in pingdotgg/t3code#8507 * fix(codex): show sub-agent models by @t3dotgg in pingdotgg/t3code#8502 * feat(analytics): report connected client platforms by @t3dotgg in pingdotgg/t3code#8481 * feat(server): accept PDF, ZIP, and other file uploads up to 50MB by @t3dotgg in pingdotgg/t3code#8235 * feat(web): toggle a thread's pin from the keyboard by @ipanasenko in pingdotgg/t3code#8440 * fix(web): add back button to project settings by @StiensWout in pingdotgg/t3code#8168 * refactor(mobile): compile semantic themes for Uniwind by @juliusmarminge in pingdotgg/t3code#7327 * fix(desktop): Cache Runtime locally on WSL Filesystem, dramatically improving launch times by @ikifar2012 in pingdotgg/t3code#5769 * fix(mobile): show OpenCode model sources in picker by @juliusmarminge in pingdotgg/t3code#8573 * fix(clients): honor project default models in new threads by @anirudhsama in pingdotgg/t3code#6011 * fix(mobile): show file actions on Android by @none23 in pingdotgg/t3code#8215 * fix(connect): explain DPoP connection failures by @extoci in pingdotgg/t3code#8351 * feat(web): make the sidebar project filter a searchable combobox by @SunkenInTime in pingdotgg/t3code#5931 * fix(server): a draft can retry its first send after a failed bootstrap by @shivamhwp in pingdotgg/t3code#8226 * fix(desktop): stop hidden previews draining battery by @Bil0000 in pingdotgg/t3code#8567 * fix(desktop): oauth popups open from the browser preview by @walid-baharwal in pingdotgg/t3code#8435 * fix(web): keep long task drawers usable on small screens by @shivamhwp in pingdotgg/t3code#8313 * fix(opencode): handle child approvals, stops, and model catalogs by @t3dotgg in pingdotgg/t3code#8480 * fix: make thread auto-settling opt-in by @shivamhwp in pingdotgg/t3code#8321 * fix(web): stop session activity timing test from blocking releases by @t3dotgg in pingdotgg/t3code#8585 * fix(mobile): show composer menus when starting a task by @juliusmarminge in pingdotgg/t3code#8587 * fix(web): show the configured stash shortcut by @UtkarshUsername in pingdotgg/t3code#8437 * feat(web): add toggleable confirmation before unpinning a thread by @UtkarshUsername in pingdotgg/t3code#7313 * fix: restore automatic thread settling defaults by @t3dotgg in pingdotgg/t3code#8596 * fix(mobile): restore composer glass and rounded shadows by @juliusmarminge in pingdotgg/t3code#8597 ## New Contributors * @luckyPipewrench made their first contribution in pingdotgg/t3code#5195 * @krutftw made their first contribution in pingdotgg/t3code#7538 * @colonelpanic8 made their first contribution in pingdotgg/t3code#4332 * @ikifar2012 made their first contribution in pingdotgg/t3code#5769 * @walid-baharwal made their first contribution in pingdotgg/t3code#8435 **Full Changelog**: pingdotgg/t3code@v0.0.35...v0.0.36 Upstream release: https://github.com/pingdotgg/t3code/releases/tag/v0.0.36
Round-11 rebase onto main (25 commits). Reverse-merged main's new work into the v2 cutover files: #8395 muted ordinary-tool-failure treatment (v2-adapted workEntrySignalsSevereFailure keyed on error items), #5931 sidebar project-filter combobox + #4c51 keyboard pin/settle with their ChatView support graph, the auto-settle-mode migration through threadSettled/threadListV2, #8235 file/unknown attachment schemas moved into chatAttachment.ts with nullable attachment paths, #8481 client analytics through the v2 ws layer, #8480 OpenCode server owner wired into the driver beside the v2 orchestration adapter, and the mobile semantic-theme migration applied to branch-only components. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Adds the server contract, streaming upload, storage, and download support for generic files up to 50 MiB. Previously only images were accepted. Every attachment's absolute path goes into the turn text, and every attachment now reaches the provider adapter, which decides what its provider ingests natively. OpenCode sends images, text files, and PDFs up to 20 MB as native file parts; the other adapters send images only and rely on the path line. Adopted from T3 Code 8f49132214a40c85cf46bf5e3d8ea11c04a9610a (pingdotgg/t3code#8235). Adaptation: PrimeAgentAdapter needed the image-only guard the other five adapters received. Upstream has no Prime adapter, and Pylon's pushed every attachment into an ACP image content block unconditionally - so once ProviderService started forwarding generic files, a PDF would have been base64'd and sent as an image with application/pdf as its mime type. Prime runs models on Anthropic and OpenAI Codex, so images-only matches those providers' own adapters. Unlike upstream, that guard ships with a test. The mock ACP agent's existing request log is used to assert the prompt Prime actually sends. The cherry-pick also dropped ProviderService.test.ts's EnvironmentId import, which upstream no longer needs but Pylon's browser-credential test still uses. docs/internals/providers.md keeps Pylon's Subscription capacity section and gains upstream's Attachment access section, with Prime Agent listed among the image-only providers.
Round-11 rebase onto main (25 commits). Reverse-merged main's new work into the v2 cutover files: #8395 muted ordinary-tool-failure treatment (v2-adapted workEntrySignalsSevereFailure keyed on error items), #5931 sidebar project-filter combobox + #4c51 keyboard pin/settle with their ChatView support graph, the auto-settle-mode migration through threadSettled/threadListV2, #8235 file/unknown attachment schemas moved into chatAttachment.ts with nullable attachment paths, #8481 client analytics through the v2 ws layer, #8480 OpenCode server owner wired into the driver beside the v2 orchestration adapter, and the mobile semantic-theme migration applied to branch-only components. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Merges 58 upstream commits (`badae6a5c` → `6a9d9f988`, upstream v0.0.34 → v0.0.36) through the `fork-upstream-merge` skill. `merge-stats.mjs` reconciles exactly: **431 files landed** against **431 in the upstream range**, no gap to explain. Fork delta is 611 files. ## Conflicts Three, plus one git resolved silently and wrongly. - **`Sidebar.tsx`** — upstream replaced the project picker's `Menu`/`MenuRadioGroup` with a searchable `Combobox` (pingdotgg#5931). Took upstream's structure and re-applied only the `FEATURES.projectManagement` gate on "New project", now carrying the `// Fork:` marker it had been missing. Upstream has since grown the mobile touch-target span itself, so the Mobile Touch Delta has nothing left to re-apply here — one convergence, unprompted. - **`ProviderSettingsPanel.tsx`** — upstream split provider settings into list and editor (pingdotgg#8380, pingdotgg#8472), moving `ProviderLastChecked` and the refresh button out of `headerAction` into the list footer. Took upstream's and re-applied the gate as `!readOnly && FEATURES.serverAdministration`. - **`pnpm-lock.yaml`** — took upstream's and re-ran `vp i`, which restored the fork's `@t3tools/moatless-api` workspace edge. - **`packages/contracts/src/orchestration.test.ts`** — auto-merged, no marker, broken. Both sides appended `OrchestrationMessage` to the same import list and the same `decodeOrchestrationMessage` const at different offsets, so git took both. Surfaced as a parse error in lint, typecheck and test at once. This is the case `merge-stats.mjs`'s conflict-candidate list exists to catch. `HostedBrowserFrame.tsx` (fork-only) also needed the `renderingActive` prop pingdotgg#8567 made required. Upstream suspends a parked webview unless background audio, PiP or a recording still needs it painted; a frame has none of those to read and is the app's only copy of the preview page, so it passes `true` and keeps today's behavior. ## Sweep Five keyword hits, all false positives: - `apps/web/src/connection/clientMetadata.ts` + test — reports the client's OS/browser/device on connect (pingdotgg#8481). Auth-adjacent, but it rides `ClientPresentation` on the relay and remote-bearer bootstraps; the fork's primary environment sends none of it. - `packages/client-runtime/src/relay/errorPresentation.ts` + test, `connection/errors.test.ts` — explain DPoP failures (pingdotgg#8351). Relay only, and T3 Connect is decided out. ## Feature classification **Usable as-is** - Searchable project-filter combobox in the sidebar (pingdotgg#5931). - Long task drawers stay usable on small screens (pingdotgg#8313) — directly relevant, the fork's phone story is `apps/web` in mobile Safari/Chrome. - Toggleable confirmation before unpinning a thread (pingdotgg#7313); toggle thread pin from the keyboard. - Back button in project settings (pingdotgg#8168); the configured stash shortcut is shown (pingdotgg#8437). - No more red-x summaries for ordinary tool failures (pingdotgg#8395); PRs open in the browser when loading fails (pingdotgg#8507). - Project default models are honored in new threads (pingdotgg#6011). - Provider settings split into list and editor (pingdotgg#8380, pingdotgg#8472) — landed, though `/settings/providers` is itself gated behind `serverAdministration`. **Unsupported in Moatless / needs implementation** - **Non-image file attachments** (pingdotgg#8235) — a turn may now carry any file up to 50MB, advertised as `capabilities.fileAttachments.maxUploadBytes` and sized by `PROVIDER_SEND_TURN_MAX_FILE_BYTES`. Moatless advertises neither this nor `attachmentUploads`, so the composer's attach affordance stays off. Costs nothing today (upstream's own web composer offers images only; `ChatAttachment` widened just far enough to typecheck), but it is the second capability key to report when uploads land. Extends the existing **Attachment uploads** entry in `docs/fork/gaps.md`. - **Connected-client platform analytics** (pingdotgg#8481) and **DPoP failure explanations** (pingdotgg#8351) — relay and T3 Connect surfaces, already decided out of the fork. No new WS methods entered the contract in this range, so no new `UnsupportedMethodError` union entries. `unsupported-methods.mjs` reports `ADD 0`, `DROP 1` (`scripts.run`), `KEEP 2`. The `scripts.run` DROP is **not** actioned — it is the documented exception in the gaps register: that union entry answers for `apps/server`, which still stubs the method (verified surviving in `ws.ts`), not for Moatless. **Backend behavior to consider reproducing in Moatless** - **Replay all un-applied events during projection bootstrap** (pingdotgg#7538) — a one-line pipeline fix upstream; Moatless runs its own projection and the same class of bug applies. - **A draft can retry its first send after a failed bootstrap** (pingdotgg#8226) — Moatless owns turn start, and a draft wedged by a failed bootstrap is the same dead end here. - **Recover stale Codex approval callbacks** (pingdotgg#5195) — Moatless owns the agent runtime in the sandbox. - **Stop querying Claude context usage after turns** (pingdotgg#8610) — a cheap correctness/cost fix in the same place Moatless polls. - **Server-side accept and size-limit non-image uploads** (pingdotgg#8235), the other half of the gap above. - Auto-settling churn (pingdotgg#8321 made it opt-in, pingdotgg#8596 reverted) nets to no change — noted so the next merge does not re-derive it. ## Verification `verify.mjs`: `tripwires`, `fmt:check`, `lint`, `typecheck` pass. `test` reports `@t3tools/web` **flaky, passed alone** — in the full run `MessagesTimeline.test.tsx` skipped all 34 tests on a 30s module-import timeout under `ChatMarkdown.tsx`; alone the package is 297 files / 3117 tests green. Load, not the merge. `inventory-check.mjs` clean. Model: Claude Opus 5, via Claude Code in a Moatless sandbox. --- Moatless task: https://moatless.soaplabstest.com/tasks/a0c041bb-1426-4591-9296-6a4b0cfa2eff
Round-11 rebase onto main (25 commits). Reverse-merged main's new work into the v2 cutover files: #8395 muted ordinary-tool-failure treatment (v2-adapted workEntrySignalsSevereFailure keyed on error items), #5931 sidebar project-filter combobox + #4c51 keyboard pin/settle with their ChatView support graph, the auto-settle-mode migration through threadSettled/threadListV2, #8235 file/unknown attachment schemas moved into chatAttachment.ts with nullable attachment paths, #8481 client analytics through the v2 ws layer, #8480 OpenCode server owner wired into the driver beside the v2 orchestration adapter, and the mobile semantic-theme migration applied to branch-only components. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

T3 Code currently accepts only image attachments. This PR adds the server contract, streaming
upload, storage, and download support for generic files up to 50 MiB. The web and mobile file
pickers remain separate in #8236 and
#8237.
Each provider adapter chooses which attachments it sends as native inputs. The prompt includes the
real path for every attachment, so unsupported native inputs still reach the agent as paths.
The takeover fixes interrupted upload cleanup, returns HTTP 400 for unfinished oversize uploads
instead of resetting the socket, encodes extended download filenames correctly, keeps mobile from
rendering files as images before its file UI ships, and corrects the rollback documentation.
Compatibility warning: the tolerant schema only helps updated clients. Older clients cannot decode
messages with file attachments. Older servers can also fail startup for the whole environment when
event replay reaches a file-bearing message. Do not treat rollback as a per-thread failure.
Theo Browne wrote the original implementation with Claude Fable 5 in Claude Code. The original
commit authorship and co-author trailers are preserved.
Built with GPT-5.6 Sol in the Codex harness.
Note
Medium Risk
Touches attachment auth tokens, streaming upload I/O, and download response headers (XSS via mime/disposition), plus persisted event schemas—version skew can break whole-environment replay on rollback.
Overview
Adds generic file attachments (PDF, ZIP, etc.) alongside images: contracts expose
ChatFileAttachment,type: "file"upload URLs (50 MiB cap), and environment capabilityfileAttachments.maxUploadBytes. Upload and storage stream bodies to.partfiles with size enforcement and cleanup on failure or interrupt; attachment IDs can carry a sanitized extension suffix so paths resolve without directory scans. Downloads bakedownload,fileName, andmimeTypeinto signed asset tokens and serve attachment disposition, sandbox CSP, and vettedContent-Type(HTML/XML/SVG forced to octet-stream).Provider turns append an on-disk path line for every attachment; image-only adapters skip non-images for native input while OpenCode sends supported PDFs/text/images under a size cap. Projection and cleanup handle file attachments; mobile thread feed shows images only until file UI ships; web gains
isImageAttachmentso file members typecheck without image-only UI.Compatibility: tolerant
ChatUnknownAttachmentdecoding on messages; legacy upload tokens default toimage; older clients/servers and file-bearing persisted events remain a documented skew risk.Reviewed by Cursor Bugbot for commit 88b541d. Bugbot is set up for automated code reviews on this repo. Configure here.
Note
Add support for generic file uploads up to 50MB with streaming
ChatFileAttachmentandFileAttachmentCreateUploadUrlInputin orchestration.ts and assets.ts to validate generic files up to 50 MiB (PROVIDER_SEND_TURN_MAX_FILE_BYTES)storeAttachmentUploadin AttachmentUpload.ts to stream uploads to a.partfile and enforce size limits, rejecting oversized or mismatched uploads with a 400 status-pdf) to allow direct path resolution without scanning directoriesContent-Disposition, sandbox CSP) and safe mime type validation in http.ts for serving generic filesattachmentRelativePathnow returnsnullfor unknown attachment types;ProviderService.sendTurnno longer setsinput.attachmentsto an empty array when absent, so downstream adapters may receiveundefinedMacroscope summarized 88b541d.