Prevent duplicate check-in replies in IMAP - #2002
Conversation
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: b4e5919bb4
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| // Mail answers carry their sending address in From (AskRequest.As). | ||
| // They belong to the mail transport even before delivery is indexed; | ||
| // projecting them here exposes a second .conversation sender to IMAP. | ||
| if m.Role == thread.RoleAgent && m.Workflow != "" && strings.HasSuffix(strings.ToLower(m.From), "@"+strings.ToLower(domain)) { |
There was a problem hiding this comment.
Suppress preflight mail replies before native delivery
When agent.Ask exits through safety.NeverAllowed or the insufficient-balance check, it records the reply with the reserved AnswerRef and local From, but with an empty Workflow. Because native delivery happens only after Ask returns, an IMAP fetch in that interval misses the lookup at lines 110–118 and also fails this new condition, assigning a UID to a synthetic .conversation message; after delivery, that message disappears and a different native-mail ID/UID appears, recreating the duplicate/expunge race this change is intended to eliminate. Identify reserved mail answers without requiring a workflow.
AGENTS.md reference: AGENTS.md:L306-L310
Useful? React with 👍 / 👎.
| _, err := mail.SendReplyAll(m.Owner, name, from, to, cc, subject, | ||
| plain, app.RenderString(plain), m.MessageID, m.References, answerRef) |
There was a problem hiding this comment.
Preserve the fallback reply identity after model errors
When QueryWithOpts returns an error after producing partial output, Ask deliberately clears AnswerRef before recording that partial agent turn, but this call sends the replacement failure notice under the reserved answerRef. The Inbox arrival therefore cannot deduplicate against the recorded agent turn: the stale partial answer remains and the delivered notice is added as a separate RolePerson message from the agent address (and may open a separate projected thread). The removed agent.Sent path previously attached the actual sent ID to that partial turn, so the fallback needs to be recorded as the agent answer with the reserved identity before delivery.
AGENTS.md reference: AGENTS.md:L291-L300
Useful? React with 👍 / 👎.
A check-in continued as a web conversation can expose the agent's answer twice in IMAP: as a synthetic .conversation sender and as the native agent@ email. Mail itself records only one delivery. The reply used to acquire its mail reference after sending, by inspecting the latest thread message; the independent arrival consumer or another turn could win that race.
Reserve the reply Message-ID before Ask records the answer and carry it unchanged through local delivery and the outbound MIME queue. The Inbox arrival then deduplicates against that same reference. Do not bridge mail-origin agent runs while their native delivery is pending, or older answers left without a reference by the race. Keep genuine web replies and imported check-in history visible. Remove the obsolete latest-message reference updater.
Validation: agent, agent/mail, mail and inbox tests pass; full Go build passes with -buildvcs=false. Regression tests cover pre/post-delivery bridging, reference deduplication, older unlinked mail answers, genuine web replies, and reserved IDs in local and outbound MIME delivery. No live mailbox data was read or altered; this fixes a reproduced code path matching the reported sender identities.