Repository navigation
Conversation
RTF treats a backslash followed by a carriage return or line feed as \par, and macOS TextEdit ends every paragraph that way. The document text extractor skipped the pair, so an attached TextEdit file reached the agent as one run-on paragraph. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ApprovabilityVerdict: Approved at Macroscope's review found this PR approvable — This preserves paragraph breaks when reading RTF documents whose paragraphs end with a backslash and line break. The change is confined to text extraction and retains existing parser safeguards. You can add or adjust custom eligibility rules. Learn more. |
|
No actionable comments were generated in the recent review. Walkthrough
Merge Risk:
|
|
Note Written by Verified in a real dev environmentverified unchanged head used the repo's playwright with
actual provider command transcript and output before, main with the same five attachments: after, exact pr head with the same five attachments: Not verifiednative ios/android and electron clients, other providers, and macos/windows execution were not exercised on this linux vm. the rtf documents were hand-authored with the documented textedit paragraph encoding; no file was saved by textedit itself. the separate web file-preview parser was outside this server-extraction verification. uploaded video playback and seeking were checked in t3's browser. |


Problem
When a user attaches an
.rtffile, the server extracts its text so the agent can read it. A document saved by macOS TextEdit reaches the agent as a single run-on paragraph.The RTF spec treats a backslash followed by a carriage return or line feed as
\par, and TextEdit ends every paragraph that way:The
rtf()reader indocumentTextWorker.tsskipped the pair as an unknown control symbol, so the agent receivedFirstSecondThird. An explicit\parin the same position already yields a newline. The agent loses the paragraph, list and line structure of the document it was asked to read.Change
In the reader's control-symbol branch, a backslash before
\nor\rnow yields the same"\n"that\paryields, and respects hidden destinations in the same way. For\<CR><LF>, the\ris consumed with the backslash, and the loop already skips the bare\nthat follows, so CRLF files give one newline, not two.Scope and approval
This is a very small, focused fix for an obvious bug, so it uses CONTRIBUTING's exception for those. It is a one-line parsing correction for a form the RTF spec defines and macOS TextEdit writes. Documents that use
\parare unchanged.It is the agent-facing counterpart of #30812, which fixes the same rule in the web file preview's separate RTF parser. The two readers share no code and either fix stands alone, so they are separate PRs as CONTRIBUTING asks.
I found no other existing issue or PR. I searched open and closed PRs and issues for
rtf,rtf paragraph,textedit, anddocument text rtf. The reader came from #21458, and #29796 / #29827 changed other formats. #29760 doesn't touch this file.Verification
Windows 11, Node 26.12.1, base
8b44f42b14.line break paragraphsrow, covering LF and CRLF, to the existingextracts RTF symbols and Unicode fallbackstable inuserInputAttachments.test.ts. With main's reader it fails, receivingFirstSecondThird. With this change it passes, along with every other RTF row. The table also checks the cached.txtthe agent is pointed at.vp run check:changed origin/mainpassed format, lint, both Knip checks, GraphQL, and typechecks for 3 affected packages. Its test step ran 32 tests: 29 passed and 3 failed. The 3 failures are other cases inuserInputAttachments.test.ts(the notebook, PDF/binary, and "readable server path" cases) that assert POSIX-style paths. They fail identically on clean main on this Windows machine and don't involve RTF. Linux CI should run them normally.Not checked: I'm on Windows, so I didn't attach a file produced by TextEdit itself. The test input reproduces TextEdit's body structure. I also didn't start a provider turn that reads the attachment. The change is confined to the extractor the attachment path calls.
Implemented and verified with Claude Opus 5.5 (
claude-opus-5-5) in the Claude Code harness.🤖 Generated with Claude Code