Skip to content

fix(gmail): send emails as multipart/alternative so they render full-width#4611

Merged
waleedlatif1 merged 3 commits into
stagingfrom
waleedlatif1/gmail-newline-fix
May 15, 2026
Merged

fix(gmail): send emails as multipart/alternative so they render full-width#4611
waleedlatif1 merged 3 commits into
stagingfrom
waleedlatif1/gmail-newline-fix

Conversation

@waleedlatif1
Copy link
Copy Markdown
Collaborator

Summary

  • Gmail send/draft tools were emitting bare text/plain, which Gmail renders in a narrow hard-wrapped column — LLM-generated bodies looked cropped width-wise compared to hand-composed emails.
  • Switched to multipart/alternative (text/plain first, text/html last, per RFC 2046) so HTML clients render flowed paragraphs while plain-text-only clients keep the original body as the fallback. Attachments now nest as multipart/mixed → multipart/alternative + parts, the canonical Gmail API structure.
  • Backwards compatible: same tool params, same response shape, same threading/RFC 2047 subject encoding; only the wire MIME structure changes. Verified by decoding the actual base64url payload.

Type of Change

  • Bug fix

Testing

Added 11 unit tests for the new helpers and both build functions (15/15 pass). Decoded real wire output to confirm MIME structure. bun run check:api-validation:strict passes.

Checklist

  • Code follows project style guidelines
  • Self-reviewed my changes
  • Tests added/updated and passing
  • No new warnings introduced
  • I confirm that I have read and agree to the terms outlined in the Contributor License Agreement (CLA)

@vercel
Copy link
Copy Markdown

vercel Bot commented May 15, 2026

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

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
docs Skipped Skipped May 15, 2026 2:16am

Request Review

@cursor
Copy link
Copy Markdown

cursor Bot commented May 15, 2026

PR Summary

Medium Risk
Changes the wire-format MIME structure and content-transfer encoding for all outgoing Gmail messages, which could affect rendering or client compatibility if boundaries/encodings are wrong. Covered by new unit tests, but still impacts a core integration path.

Overview
Switches Gmail message generation from single-part text/plain/text/html to multipart/alternative (text/plain + text/html) so messages render full-width in Gmail while preserving a plain-text fallback.

Adds helpers to safely convert between plain text and HTML (escapeHtml, plainTextToHtml, htmlToPlainText, buildBodyAlternatives) and changes body transfer encoding to base64 with RFC2045 line wrapping for UTF-8 safety.

Updates attachment handling to use multipart/mixed → multipart/alternative + attachments and expands unit coverage to validate MIME structure, threading headers, and UTF-8 round-trips.

Reviewed by Cursor Bugbot for commit 01ca2dd. Configure here.

Comment thread apps/sim/tools/gmail/utils.ts Outdated
@waleedlatif1
Copy link
Copy Markdown
Collaborator Author

@greptile

@waleedlatif1
Copy link
Copy Markdown
Collaborator Author

@cursor review

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented May 15, 2026

Greptile Summary

This PR fixes the narrow hard-wrapped rendering issue in Gmail by switching both the send and draft tools from bare text/plain to multipart/alternative, emitting a proper text/plain + text/html pair for every message. When attachments are present, the alternative block is nested inside a multipart/mixed envelope following the canonical Gmail API structure.

  • MIME structure change: buildSimpleEmailMessage now emits multipart/alternative directly; buildMimeMessage wraps it in multipart/mixed when attachments exist, using two independent boundaries.
  • Encoding fix: Both body parts now use Content-Transfer-Encoding: base64 (76-char line-wrapped) instead of 7bit, allowing safe transport of emoji, accented characters, and other multi-byte UTF-8 content.
  • New helpers: escapeHtml, plainTextToHtml, htmlToPlainText, and buildBodyAlternatives cover the text↔HTML conversion path; all are exported and covered by tests that decode real base64 wire output.

Confidence Score: 5/5

Safe to merge — the change is backwards-compatible (same tool params, same response shape), the MIME structure follows RFC 2046, and the new encoding path is well-covered by tests that decode real wire output.

The MIME restructuring is straightforward and localized to two builder functions. Both the attachment and non-attachment paths produce valid RFC 2046 MIME. Base64 encoding correctly handles arbitrary UTF-8. The duplicate MIME-Version header that existed in the old else branch of buildMimeMessage is silently cleaned up as a side effect. No pre-existing callers are broken since the public API surface is unchanged.

No files require special attention.

Important Files Changed

Filename Overview
apps/sim/tools/gmail/utils.ts Adds multipart/alternative MIME structure for both send and draft paths; introduces escapeHtml, plainTextToHtml, htmlToPlainText, buildBodyAlternatives, and renderAlternativeParts helpers; switches body encoding from 7bit to base64 to handle arbitrary UTF-8
apps/sim/tools/gmail/utils.test.ts Adds 11 unit tests covering the new helper functions and both message-build paths; decodePart helper validates MIME structure by decoding actual base64 payloads

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[buildSimpleEmailMessage / buildMimeMessage] --> B{Has attachments?}
    B -- No --> C[multipart/alternative]
    B -- Yes --> D[multipart/mixed]
    D --> E[multipart/alternative nested part]
    D --> F[attachment parts base64 encoded]
    C --> G[text/plain base64 + UTF-8]
    C --> H[text/html base64 + UTF-8]
    E --> G
    E --> H
    I[contentType = text or undefined] --> J[plainTextToHtml blank lines to p newlines to br]
    I --> K[plain = body as-is]
    L[contentType = html] --> M[htmlToPlainText strip tags decode entities]
    L --> N[html = body as-is]
    J --> H
    K --> G
    M --> G
    N --> H
Loading

Reviews (2): Last reviewed commit: "fix(gmail): encode body parts as base64 ..." | Re-trigger Greptile

Comment thread apps/sim/tools/gmail/utils.ts Outdated
Comment thread apps/sim/tools/gmail/utils.ts
@waleedlatif1
Copy link
Copy Markdown
Collaborator Author

@greptile

@waleedlatif1
Copy link
Copy Markdown
Collaborator Author

@cursor review

Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

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

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit 02b88f9. Configure here.

Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

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

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit 01ca2dd. Configure here.

@waleedlatif1 waleedlatif1 merged commit e23c20e into staging May 15, 2026
14 checks passed
@waleedlatif1 waleedlatif1 deleted the waleedlatif1/gmail-newline-fix branch May 15, 2026 02:26
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.

1 participant