fix(email): route multi-mailbox message actions by recorded provenance (#1707)#1735
Conversation
list_inbox/search_messages scan all connected mailboxes, tag each result with its source provider, and record provenance; summarize_message accepts an optional mailbox and routes via _backend_for_message. Fixes multi-mailbox summarize/archive failing without an explicit mailbox=.
Test evidence — multi-mailbox provenanceHow it was tested — per-worktree venv pinned to this branch's code (verified
Proof of implementation — driving the real branch code with two spy backends (OAuth boundary faked, routing logic real): Before this PR, acting on an id surfaced by search/list raised |
SummaryThis closes a real multi-mailbox gap: with Gmail + Outlook both connected, ids returned by One note on verification: I couldn't run the suite or formatters in the review sandbox (pytest/black/isort unavailable, read-only FS), so the findings are from static review against the author's reported Issues Found🟢 Budget under-fills when mailboxes are uneven ( 🟢 🟢 A weak test assertion can pass without verifying anything ( 🟢 Loop logic is now duplicated a fourth time ( Strengths
VerdictApprove with suggestions. No blocking issues — correctness and the fail-loud contract are sound, and coverage is genuinely good. The four 🟢 items (budget redistribution, dropped page token, the weak conditional assert, and the loop duplication) are all safe to address in this PR or a quick follow-up. |
Live real-world evidence — real Gmail + Outlook (connected Mac, Lemonade Gemma-4-E4B)Agent built from the real connectors (no injected backends); ids hashed and subjects/senders redacted (not posting mailbox content to a public PR): Real OAuth + real Gmail/Graph APIs + real inference + real branch code. Pre-fix, the cross-mailbox id raised |
kovtcharov-amd
left a comment
There was a problem hiding this comment.
Clean multi-mailbox provenance fix. list/search now tag each result with its source mailbox and record provenance, and summarize_message routes via _backend_for_message with an optional explicit mailbox — unknown id + multiple mailboxes still fails loud (no guessing). Confirmed removing the gmail = self._gmail capture in summarize_tools is safe (the remaining gmail refs are in the module-level summarize_message_impl, and there's only one registered tool in that closure). 13 new provenance tests.
Note (non-blocking): the one red check — "Test GAIA CLI on Linux (Full Integration)" — is an infra timeout in the "Install system dependencies" step (exceeded 15m before any test ran), unrelated to this change. All email-specific suites are green. LGTM.
Closes #1707
Before: with Gmail + Outlook both connected, acting on a message/thread id returned by search/list failed —
Cannot determine which mailbox message '<id>' belongs to … multiple mailboxes are connected— because the id arrived without its source mailbox. Full-thread summary, batch archive, and summarize-message all broke multi-mailbox. After:list_inbox/search_messagesscan every connected mailbox, tag each result with its source provider, and record provenance;summarize_messageaccepts an optionalmailboxand routes via the recorded origin. Actions route deterministically with nomailbox=.Proof it resolves #1707
Driving the real branch code with two mailbox backends:
Full captured output + the unknown-id fail-loud case are in the evidence comment below.
Test plan
pytest tests/unit/agents/email/ tests/unit/email/→ 453 passed (per-worktree venv pinned to this branch's code)test_multimailbox_provenance.py(13 tests): list/search tagging + provenance; summarize / summarize_thread / archive_message_batch route with nomailbox=; unknown id + 2 mailboxes fails loudgaia emailtwo-mailbox pass — deterministic code paths already proven above