Skip to content

feat(gateway): add HTML detection and multipart/alternative support to email adapter - #25441

Open
brotherko wants to merge 1 commit into
NousResearch:mainfrom
brotherko:feat/email-html-support
Open

feat(gateway): add HTML detection and multipart/alternative support to email adapter#25441
brotherko wants to merge 1 commit into
NousResearch:mainfrom
brotherko:feat/email-html-support

Conversation

@brotherko

@brotherko brotherko commented May 14, 2026

Copy link
Copy Markdown

What does this PR do?

Adds automatic HTML detection to the email gateway adapter's outbound path. When the response body contains HTML content, the adapter now sends a multipart/alternative email with both a plain-text fallback and the original HTML — ensuring proper rendering across all email clients while maintaining backward compatibility for text-only responses.

Related Issue

Fixes #11941

Type of Change

  • 🐛 Bug fix (non-breaking change that fixes an issue)
  • ✨ New feature (non-breaking change that adds functionality)
  • 🔒 Security fix
  • 📝 Documentation update
  • ✅ Tests (adding or improving test coverage)
  • ♻️ Refactor (no behavior change)
  • 🎯 New skill (bundled or hub)

Changes Made

  • gateway/platforms/email.py: Added _is_html(text) — detects HTML by checking for <!DOCTYPE/<html> prefixes and common block-level tags (<p>, <div>, <br>, <h1><h6>, <ul>, <ol>, <table>)
  • gateway/platforms/email.py: Added _build_body_part(body) — returns multipart/alternative (plain + HTML) when HTML detected, otherwise plain MIMEText as before
  • gateway/platforms/email.py: Updated _send_email, _send_email_with_attachment, and _send_email_with_attachments to use _build_body_part() instead of hardcoded MIMEText(body, "plain", ...)

How to Test

  1. Configure the email gateway with valid IMAP/SMTP credentials
  2. Send a message that triggers the agent to respond with HTML content (e.g., a skill that generates an HTML report)
  3. Verify the received email renders HTML properly in Gmail/Outlook/Apple Mail
  4. Send a message that triggers a plain-text response — verify it still arrives as plain text (no behavior change)
  5. Verify that text-only email clients (e.g., mustrstripe) show the plain-text fallback from the multipart/alternative

Checklist

Code

  • I've read the Contributing Guide
  • My commit messages follow Conventional Commits (fix(scope):, feat(scope):, etc.)
  • I searched for existing PRs to make sure this isn't a duplicate
  • My PR contains only changes related to this fix/feature (no unrelated commits)
  • I've run pytest tests/ -q and all tests pass
  • I've added tests for my changes (required for bug fixes, strongly encouraged for features)
  • I've tested on my platform: macOS 24.3.0

Documentation & Housekeeping

  • I've updated relevant documentation (README, docs/, docstrings) — or N/A
  • I've updated cli-config.yaml.example if I added/changed config keys — or N/A
  • I've updated CONTRIBUTING.md or AGENTS.md if I changed architecture or workflows — or N/A
  • I've considered cross-platform impact (Windows, macOS) per the compatibility guide — or N/A
  • I've updated tool descriptions/schemas if I changed tool behavior — or N/A

Made with Cursor

@brotherko
brotherko marked this pull request as draft May 14, 2026 05:19
…o email adapter

The email adapter previously always sent responses as text/plain, even
when the body contained HTML content. This adds automatic HTML detection
and sends a multipart/alternative email with both plain-text and HTML
parts when HTML is detected, ensuring proper rendering across all email
clients while maintaining a fallback for text-only clients.

Co-authored-by: Cursor <cursoragent@cursor.com>
@brotherko
brotherko force-pushed the feat/email-html-support branch from 8c57952 to 5652b21 Compare May 14, 2026 05:21
@brotherko
brotherko marked this pull request as ready for review May 14, 2026 05:27
@alt-glitch alt-glitch added type/feature New feature or request P3 Low — cosmetic, nice to have comp/gateway Gateway runner, session dispatch, delivery platform/email Email (IMAP/SMTP) adapter labels May 14, 2026

@teknium1 teknium1 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Thanks for the focused multipart/alternative implementation. The feature premise still holds on current main, but this branch cannot be applied as-is after the email-plugin migration.

Problems

  • The PR changes gateway/platforms/email.py, while commit 560010547 moved the live adapter to plugins/platforms/email/adapter.py. Current outbound paths still attach text/plain at plugins/platforms/email/adapter.py:949, :1063, and :1143.
  • The current standalone sender also emits text/plain at plugins/platforms/email/adapter.py:1219; it needs a deliberate decision if this feature covers all outbound email paths.
  • The detector at gateway/platforms/email.py:160 excludes valid inline-only HTML such as <a>, <span>, and <b> (also asserted by the added tests), so those bodies remain plain text.

Suggested changes

  • Salvage the implementation into plugins/platforms/email/adapter.py, update test imports, and cover the standalone route or document its exclusion.
  • Define the detection contract and test valid inline-only HTML before keeping the narrow tag list.

Automated hermes-sweeper review.

if stripped.startswith(("<!DOCTYPE", "<html", "<HTML", "<!doctype")):
return True
return bool(re.search(r"<(p|div|br|h[1-6]|ul|ol|table|html)\b", stripped, re.IGNORECASE))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This detector deliberately excludes valid inline-only HTML (<a>, <span>, <b>), which the added tests also assert. Those bodies will still be sent as text/plain; please either broaden and test the detection contract or narrow the feature claim.

@teknium1 teknium1 added sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform labels Jul 13, 2026
@swissly

swissly commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

Related: #73294 ports the full Markdown-to-HTML rendering (from #46619) to plugins/platforms/email/adapter.py with all 4 send paths. This PR detects HTML but doesn't render Markdown.

@swissly

swissly commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

Overlap note (2026-08-03): this PR addresses the same topic (HTML email delivery). PR #73294 (swissly) consolidates Markdown-to-HTML rendering for ALL 4 send paths + allowlist sanitizer + 20 tests, and was just rebased onto current main (commit 9529ee30). Please review #73294 for consolidation rather than duplicating send-path fixes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/gateway Gateway runner, session dispatch, delivery P3 Low — cosmetic, nice to have platform/email Email (IMAP/SMTP) adapter sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades type/feature New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature]: HTML email support for email gateway (multipart/alternative + Markdown rendering)

4 participants