improve(email): format agent markdown responses as styled HTML emails

linuztx committed Mar 23, 2026 at 09:54 UTC 85a9e988c28220552c3b5e3eb07a7ab2c491c662
1 file changed +30 -2
plugins/_email_integration/helpers/smtp_client.py
+30 -2
@@ -47,10 +47,34 @@ async def send_reply(
47 loop = asyncio.get_event_loop()
48
49 def _sync_send():
50 - msg = MIMEMultipart() if attachments else MIMEText(body, "plain", "utf-8")
50 + import markdown
51 +
52 + html_body = markdown.markdown(body, extensions=['extra', 'nl2br'])
53 + html_content = f"""
54 + <html>
55 + <head>
56 + <style>
57 + body {{ font-family: sans-serif; line-height: 1.5; }}
58 + blockquote {{ border-left: 4px solid #ccc; margin: 0; padding-left: 1em; color: #666; }}
59 + pre {{ background-color: #f6f8fa; padding: 1em; border-radius: 4px; overflow-x: auto; }}
60 + code {{ font-family: monospace; background-color: #f6f8fa; padding: 0.2em 0.4em; border-radius: 3px; }}
61 + pre code {{ padding: 0; background-color: transparent; }}
62 + </style>
63 + </head>
64 + <body>
65 + {html_body}
66 + </body>
67 + </html>
68 + """
69
70 if attachments:
53 - msg.attach(MIMEText(body, "plain", "utf-8"))
71 + msg = MIMEMultipart("mixed")
72 +
73 + alt_part = MIMEMultipart("alternative")
74 + alt_part.attach(MIMEText(body, "plain", "utf-8"))
75 + alt_part.attach(MIMEText(html_content, "html", "utf-8"))
76 + msg.attach(alt_part)
77 +
78 for filename, content in attachments:
79 part = MIMEBase("application", "octet-stream")
80 part.set_payload(content)
@@ -60,6 +84,10 @@ async def send_reply(
84 f"attachment; filename={filename}",
85 )
86 msg.attach(part)
87 + else:
88 + msg = MIMEMultipart("alternative")
89 + msg.attach(MIMEText(body, "plain", "utf-8"))
90 + msg.attach(MIMEText(html_content, "html", "utf-8"))
91
92 msg["From"] = config.username
93 msg["To"] = to