fix(email): isolate sessions by normalized subject when opted in - #104148
santiagomalter wants to merge 1 commit into
Conversation
SummaryAdds opt-in per-subject email session isolation ( Findings (all Non-blocking)
VerdictCareful, backward-compatible (default off), thoroughly tested. No blocking issues. |
|
Thanks for the pass. On the unbounded The other notes match the intended contract: extra CJK/locale prefixes fail closed into a new session; outbound |
|
On the Without isolation, two mails from the same allowlisted sender with different subjects share one Hermes session and one
Happy to retitle or split if maintainers want this filed strictly as |
|
Running this exact shape in production — normalized-subject slug as thread_id plus sender+thread reply-context routing, plugin-layer and config-gated as here. It fixed parallel-topic interference for a single allowlisted sender on generic IMAP/SMTP; without it the far-side Gmail client piled replies onto the wrong conversation and all topics shared one agent session. Happy to validate this port against current main once merged. The other half of #27804 (per-tool-call status-email volume) is still outstanding for us — watching for a follow-up there. |
|
Thanks — that matches what this port is for (generic IMAP/SMTP, one allowlisted sender, parallel subjects, Gmail on the far side). Status-email volume (#27804 part 2) is intentionally out of this PR so isolation can land without mixing display/ |
|
Duplicate-check addendum: #99814 ( It is Gmail-only / always-on ( |
51ce142 to
29593d7
Compare
Default stays one session per sender. With EMAIL_SESSION_BY_SUBJECT or platforms.email.extra.session_by_subject, chat_id remains the sender address and source.thread_id is the normalized subject so parallel topics no longer share context or clobber In-Reply-To headers. Fixes NousResearch#26277
29593d7 to
0b3c2ee
Compare
What does this PR do?
The Email adapter currently omits
thread_idand keys_thread_contextby sender address alone. Every inbound mail from one person shares a Hermes session, and outboundIn-Reply-To/Subjectfollow that sender's last message. Parallel topics interrupt each other and Gmail (or any client that threads on those headers) piles replies onto the wrong conversation.This ports closed #26307 onto the live plugin adapter. Default behavior is unchanged. When opted in,
chat_idstays the sender address (SMTPTo:remains valid) and isolation uses the existing DMthread_idslot inbuild_session_key(). Reply context is keyed by sender + normalized subject, andmetadata.thread_idis plumbed through text, image, multi-image, and document sends.I have been running this shape in production on generic IMAP/SMTP (not Gmail IMAP): one allowlisted sender, several parallel topics.
Fixes #26277
Related: #27804 (isolation half only — status-mail volume is out of scope)
Duplicate check
plugins/platforms/email/adapter.py. No Discord / provider-parity files; nogateway/config.py.thread_id. Right native slot, different key; still on the retired path; outbound context still sender-wide. Complementary follow-up, not a duplicate.chat_id. Sweeper: SMTPTo:becomes a subject string; two senders with the same subject share a session. This PR keepschat_idas the address.X-GM-THRID. Not portable to generic IMAP._thread_contextwithout settingsource.thread_id, so sessions still collide.Type of Change
Changes Made
plugins/platforms/email/adapter.py— opt-inEMAIL_SESSION_BY_SUBJECT/platforms.email.extra.session_by_subject; normalizeRe:/Fw:/Fwd:/CJK prefixes;build_source(..., thread_id=...);_new_replylooks up context by sender+thread; all send paths forwardmetadata.thread_idplugins/platforms/email/plugin.yaml— optional envtests/gateway/test_email.py— prefix stripping, default-off lock, extra-without-env, empty subject, two-subjectIn-Reply-To, document metadata, restart subject rebuildtests/gateway/test_send_multiple_images.py— multi-image forwards the context keycli-config.yaml.exampleextraHow to Test
EMAIL_SESSION_BY_SUBJECT=trueorplatforms.email.extra.session_by_subject: true: those two mails → two sessions; replies keep the matchingRe:/In-Reply-To.source.chat_idon the event is stilluser@example.com(never a subject string).metadata.thread_id.Checklist
Code
fix(email): …)pytest tests/gateway/test_email.py tests/gateway/test_send_multiple_images.py::TestEmailMultiImage -q(47 + 2 passed)Documentation & Housekeeping
cli-config.yaml.example