Skip to content

Prevent duplicate check-in replies in IMAP - #2002

Merged
asim merged 1 commit into
mainfrom
fix/checkin-imap-duplicate-replies
Oct 2, 2026
Merged

asim merged 1 commit into
mainfrom
fix/checkin-imap-duplicate-replies

Conversation

@asim

@asim asim commented Oct 2, 2026

Copy link
Copy Markdown
Member

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.

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 2, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-02T09:40:48.030955Z b4e5919 PR opened
ℹ️ 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" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@asim
asim merged commit 3ce7a64 into main Oct 2, 2026
1 check passed

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 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".

Comment thread inbox/imapbridge.go
// 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)) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge 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 👍 / 👎.

Comment thread agent/mail/mail.go
Comment on lines +288 to +289
_, err := mail.SendReplyAll(m.Owner, name, from, to, cc, subject,
plain, app.RenderString(plain), m.MessageID, m.References, answerRef)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge 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 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant