fix(gateway): classify email document attachments as DOCUMENT

Email cached document attachments and placed them in media_urls, but
msg_type only flipped on image attachments — documents stayed TEXT and
run.py's document-context injection (gated on MessageType.DOCUMENT)
silently dropped them. Same bug class as Signal #12845. DOCUMENT wins
over PHOTO for mixed attachments since image handling keys off per-path
mime types while document injection gates strictly on message_type.
This commit is contained in:
Teknium 2026-06-11 22:58:34 -07:00
parent 1e29ab38c7
commit f03f161b39
2 changed files with 70 additions and 1 deletions

View file

@ -470,8 +470,15 @@ class EmailAdapter(BasePlatformAdapter):
for att in attachments:
media_urls.append(att["path"])
media_types.append(att["media_type"])
if att["type"] == "image":
if att["type"] == "image" and msg_type == MessageType.TEXT:
msg_type = MessageType.PHOTO
elif att["type"] == "document":
# Document wins over PHOTO for mixed attachments: run.py's
# image handling keys off the per-path image/* mime type
# regardless of message_type, but document-context injection
# gates strictly on MessageType.DOCUMENT — so DOCUMENT is the
# only classification that surfaces both.
msg_type = MessageType.DOCUMENT
# Store thread context for reply threading
self._thread_context[sender_addr] = {

View file

@ -393,6 +393,68 @@ class TestDispatchMessage(unittest.TestCase):
self.assertEqual(captured_events[0].message_type, MessageType.PHOTO)
self.assertEqual(captured_events[0].media_urls, ["/tmp/img.jpg"])
def test_document_attachment_sets_document_type(self):
"""Email with a document attachment must set DOCUMENT so run.py injects file context."""
import asyncio
from gateway.platforms.base import MessageType
adapter = self._make_adapter()
captured_events = []
async def capture_handle(event):
captured_events.append(event)
adapter.handle_message = capture_handle
msg_data = {
"uid": b"6",
"sender_addr": "user@test.com",
"sender_name": "User",
"subject": "Re: report",
"message_id": "<msg6@test.com>",
"in_reply_to": "",
"body": "See attached",
"attachments": [{"path": "/tmp/report.pdf", "filename": "report.pdf", "type": "document", "media_type": "application/pdf"}],
"date": "",
}
asyncio.run(adapter._dispatch_message(msg_data))
self.assertEqual(len(captured_events), 1)
self.assertEqual(captured_events[0].message_type, MessageType.DOCUMENT)
self.assertEqual(captured_events[0].media_urls, ["/tmp/report.pdf"])
def test_mixed_image_and_document_prefers_document(self):
"""DOCUMENT wins for mixed attachments — image handling keys off per-path
mime types, but document injection gates strictly on MessageType.DOCUMENT."""
import asyncio
from gateway.platforms.base import MessageType
adapter = self._make_adapter()
captured_events = []
async def capture_handle(event):
captured_events.append(event)
adapter.handle_message = capture_handle
msg_data = {
"uid": b"7",
"sender_addr": "user@test.com",
"sender_name": "User",
"subject": "Re: both",
"message_id": "<msg7@test.com>",
"in_reply_to": "",
"body": "Photo and PDF",
"attachments": [
{"path": "/tmp/img.jpg", "filename": "img.jpg", "type": "image", "media_type": "image/jpeg"},
{"path": "/tmp/report.pdf", "filename": "report.pdf", "type": "document", "media_type": "application/pdf"},
],
"date": "",
}
asyncio.run(adapter._dispatch_message(msg_data))
self.assertEqual(len(captured_events), 1)
self.assertEqual(captured_events[0].message_type, MessageType.DOCUMENT)
self.assertEqual(len(captured_events[0].media_urls), 2)
def test_source_built_correctly(self):
"""Session source should have correct chat_id and user info."""
import asyncio