""" Thread domain models for Mail Intake. This module defines the **canonical, provider-agnostic representation** of an email thread as used internally by the Mail Intake ingestion pipeline. Threads group related messages and serve as the primary unit of reasoning for higher-level correspondence workflows. """ from dataclasses import dataclass, field from datetime import datetime from typing import List, Set from mail_intake.models.message import MailIntakeMessage @dataclass class MailIntakeThread: """ Canonical internal representation of an email thread. A thread groups multiple related messages under a single subject and participant set. It is designed to support reasoning over conversational context such as job applications, interviews, follow-ups, and ongoing discussions. This model is provider-agnostic and safe to persist. """ thread_id: str """Provider-specific thread identifier.""" normalized_subject: str """Normalized subject line used to group related messages.""" participants: Set[str] = field(default_factory=set) """Set of unique participant email addresses observed in the thread.""" messages: List[MailIntakeMessage] = field(default_factory=list) """Ordered list of messages belonging to this thread.""" last_activity_at: datetime | None = None """Timestamp of the most recent message in the thread.""" def add_message(self, message: MailIntakeMessage) -> None: """ Add a message to the thread and update derived fields. This method: - Appends the message to the thread - Tracks unique participants - Updates the last activity timestamp Args: message: Parsed mail message to add to the thread. """ self.messages.append(message) if message.from_email: self.participants.add(message.from_email) if self.last_activity_at is None or message.timestamp > self.last_activity_at: self.last_activity_at = message.timestamp