Repository navigation
feat(uploads): prompt uploads take documents - #621
Conversation
The Attach button, the Compose and paste-dialog pickers, terminal drop and the clipboard now take documents beside images and videos: the office formats pdf/docx/pptx/xlsx/zip and the text family txt/md/csv/tsv/json/log/yaml/yml/xml. A document is admitted by its name's extension (the MIME a phone attaches to a .md is anyone's guess), buffered under the same 50MB image cap, and judged on its bytes by the same fail-closed sniffer: a magic-number signature for the binary formats, UTF-8 with no NUL byte for text. It is saved under a sanitized stem of its name, so Q3 report.pdf is findable as paste-<ts>-<hex>-Q3-report.pdf. sh/py/js/html are refused by name with a clear 400. That is a scope choice, not a security property: file-raw already serves non-media text inert, so this keeps the server from storing what it would serve or an agent could run, and html and svg are the ones deliberately kept out. One client classifier (_promptAttachKind) now returns image, video or document, and the server's dispatch mirrors it: an image MIME wins whatever the name says, a document extension takes the document branch, and a foreign extension with no image MIME is refused only after the image sniff fails, so nothing the image path accepted by its bytes is lost.
The wiki said .codeman-uploads/ is the one path "a local agent, a container and a remote host all see the same way", two sentences after saying uploads are refused for remote (SSH) sessions. Only a local agent and a Docker case (workspace bind-mounted at the same absolute path) share it, which is why the route refuses remote sessions; say that. The same paragraph said every image goes in after a re-encode on the device, but _normalizeImageForUpload passes a GIF through untouched to keep its frames. CLAUDE.md and a test comment described the image fallback as behaving "as before 1.36", a release that never existed (1.35.0 was followed by 1.40.0). It means the image path as it was before this change. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
feat(uploads): prompt uploads take documents Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
fix(uploads): refuse an unsupported video before uploading it Conflict resolved against #621 in src/web/public/image-input.js (the _promptAttachKind doc comment only): #621's comment is kept with #622's clause that a video MIME counts only for the containers the server admits; #622's video MIME check and #621's document branch both stay. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
|
Merged and shipped in 1.42.0. Thanks @JDProfresh! The fail-closed byte checks (a signature for office files, UTF-8 with no NUL byte for text) and the log line that never prints a text file's contents are exactly right for this route. Thanks also for working through every point of the #553 decision, down to the api-reference entry and the zip/tsv link patterns. I tested it live: a PDF, a CSV, a markdown file with no MIME and a docx went through, and a renamed binary pretending to be a PDF was refused. Three doc statements were corrected at merge time: only a local agent and a Docker case share Follow-ups we'd welcome, none blocking: a clearer "not UTF-8 text" refusal (or UTF-16 with a BOM) for Excel and Notepad CSV/TXT exports, which today read like a corrupt file, and a parity test pinning the five copies of the document list against |
Master's #621 added tsv and zip to both file-link extension groups in constants.js while this branch added diff|patch to the same lines. The resolution keeps both, in master's order with diff|patch after sql where the PR put it; the rooted and relative groups stay identical, and every upload document extension plus .diff/.patch still links in both patterns. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
A user can hand the agent an image or a video but not a document: a PDF to read, a spreadsheet to summarise, a log to scan. This widens the same
paste-imageroute to take them, the last of the three parts from #553, now that the Attach and video part is in (#606).What it does
The Attach button, the Compose and paste-dialog pickers, terminal drop and the clipboard now take documents beside images and videos: the office formats pdf, docx, pptx, xlsx and zip, and the text family txt, md, csv, tsv, json, log, yaml, yml and xml. The Compose and paste dialogs' picker button reads
📎 Attachinstead of🖼 Image, since it now takes more than images.A document is admitted by its name's extension, not its Content-Type (the MIME a phone attaches to a
.mdor.csvis anyone's guess). It is buffered under the same 50 MB image cap and judged on its bytes by the same fail-closed sniffer: a magic-number signature for the binary formats (%PDF-,PK\x03\x04for the OOXML trio and zip), and valid UTF-8 with no NUL byte for text. It is saved under a sanitized stem of its name, soQ3 report.pdfis findable aspaste-<ts>-<hex>-Q3-report.pdf.From your #553 notes
file-rawalready serves non-media text inert, so this only keeps the server from storing what it would serve or an agent could run. html and svg are the ones that really stay out. A named type that is neither an image nor a document is answered 400Unsupported file type: .sh, which the agent and the user can read, where master answers a.shwith a 415 about.png(an unknown extension falls back to.pngand then fails the PNG sniff).paste-imageis described indocs/api-reference.md, extending the section feat(uploads): write prompt uploads to a hidden, self-ignoring .codeman-uploads/ folder #574 and feat(mobile): phone Attach button and camera-roll video uploads #606 added, with the document types, the size cap, the by-name admission and the scope refusal.Design notes
_promptAttachKind, which now returnsimage,videoordocument: animage/*MIME takes the image path whatever the name says (a GIF namedanimation.txtstays a GIF), a document extension takes the document branch, and a foreign extension with no image MIME is refused only after the image sniff fails, so nothing the image path ever accepted by its bytes (a HEIC under a generic name) is lost.nul-byteornot-utf8) and never prints the bytes, since the head of a text file is user content.ALLOWED_DOCUMENT_EXTS, neverSUPPORTED_ATTACHMENT_EXTENSIONSorEDITABLE_EXTENSIONS(those carry sh/py/js/html): a prompt upload is a thing to read, not a thing to run or serve.Unsupported file typekey (the tile grid's), the oldOnly image or video files are supportedkey goes with its last use, and the one new key is📎 Attachfor the dialog button. I checked it against the existingAttachkey after your feat(mobile): phone Attach button and camera-roll video uploads #606 fix; it is a different string and overrides nothing.Tests
Guard tests, each shown to fail with its guard removed: the document bytes-vs-extension check (force it to accept and the mismatch test fails), and the by-name
Unsupported file type400 (remove it and the script/HTML refusal test fails). Plus: a PDF, an OOXML document and a text file accepted with the name kept; the sanitized-stem rule; the sh/py/html refusal; a mismatch per type (PNG bytes under a PDF name, a NUL byte in text, non-UTF-8 in a.csv); the log naming the reason without the bytes; and the classifier and pickers intoolbar-attach.test.tsandmobile-prompt-composer.test.ts.Verified
Observed on this branch, rebased onto today's master (1.41.1,
a8a8bb71):npm testgate passes: 11,266 passed, 12 skipped, 0 failed. Static checks and prettier on the touched tests pass.Q3 report.pdf, a docx, a zip, a.md, a.csvand a.tsvwere accepted into<ws>/.codeman-uploads/with their names kept;run.shandpage.htmlanswered 400Unsupported file type; a.txtwith a NUL byte, a Latin-1.csvand PNG bytes named.pdfanswered 415 and wrote nothing. A PNG still lands as before, and an mkv on?kind=videois still refused at once. A headless iPhone-profile run through the real Attach picker put the PDF and CSV paths on the prompt as unsent text, and pickingrun.shshowed theUnsupported file typetoast without uploading anything.On my iPhone, a PDF picked from the Files app through Attach landed on the prompt and Claude read it. Earlier builds of this have been running on my own installs since 2026-10-07.
Link pattern
You flagged on #553 that some upload extensions were not in
FILE_PATH_LINK_PATTERN. m4v and ogv came in with #606; this adds the last two the uploads produce, zip and tsv, to both the rooted and the relative pattern (the parity test #604 added pins that they match), with a positive case inlink-provider-regex.test.ts.The README's upload line (both languages) now says documents too. No changeset is included, matching #574.