fix: prevent email loop by detecting bounces and capping conversation length
- email_channel.py: add detect_automated_message() that inspects RFC 3834 Auto-Submitted, mailer-daemon/postmaster sender patterns, X-Loop, multipart/report Content-Type, Precedence, and subject heuristics. fetch_unread_messages() now includes is_automated / automated_reason in every message dict. - main.py: if is_automated is set, call agent.handle_automated_message() instead of process_message() — no reply is ever sent to a bounce source. - agent/core.py: add MAX_USER_MESSAGES = 20 cap; process_message() returns "" without replying once a conversation exceeds the limit and calls notifier.notify_loop_escalation() on first breach. New public method handle_automated_message() records the event and triggers the same one-shot admin alert. - models/conversation.py: add loop_escalated: bool field (persisted) so the admin alert fires at most once per conversation. - notifications/notifier.py: add notify_loop_escalation() which sends a plain-text warning to the admin CC list (Markus Graf / spielgruppen@). https://claude.ai/code/session_01KwvR5hDPjSuJg4kvw5b5e5
This commit is contained in:
@@ -98,6 +98,27 @@ def run_poll_loop(agent: EmailAgent, channel: EmailChannel, poll_interval: int)
|
||||
for msg in messages:
|
||||
logger.info("Processing message from %s", msg["from"])
|
||||
try:
|
||||
# ----------------------------------------------------------
|
||||
# Bounce / automated-sender guard
|
||||
# If the channel layer flagged this as an automated message
|
||||
# (bounce, out-of-office, delivery failure, …) we must NOT
|
||||
# reply — that would create or worsen an email loop.
|
||||
# Instead, alert the admin once and drop the message.
|
||||
# ----------------------------------------------------------
|
||||
if msg.get("is_automated"):
|
||||
logger.warning(
|
||||
"Automated/bounce message from %s — reason: %s — not replying",
|
||||
msg["from"],
|
||||
msg.get("automated_reason", "unknown"),
|
||||
)
|
||||
agent.handle_automated_message(
|
||||
sender_email=msg["from"],
|
||||
subject=msg["subject"],
|
||||
reason=msg.get("automated_reason", "automated sender detected"),
|
||||
inbound_message_id=msg["message_id"],
|
||||
)
|
||||
continue
|
||||
|
||||
# Prepend email headers so the LLM can extract the
|
||||
# sender's address and subject (e.g. to fill in
|
||||
# parentGuardian.email automatically).
|
||||
|
||||
Reference in New Issue
Block a user