feat(email): add postActionExpunge option to gate broad IMAP expunge

This commit is contained in:
Flávio Veloso Soares 2026-06-08 16:04:39 -07:00 committed by Xubin Ren
parent 1d683f0f18
commit 6de8d7f52e
2 changed files with 5 additions and 1 deletions

View File

@ -581,6 +581,7 @@ Give nanobot its own email account. It polls **IMAP** for incoming mail and repl
> This runs only after an accepted email is successfully delivered to the AI pipeline. > This runs only after an accepted email is successfully delivered to the AI pipeline.
> - `postActionMoveMailbox`: Destination mailbox used when `postAction` is `"move"` (for example `"Processed"` or `"[Gmail]/Trash"`). > - `postActionMoveMailbox`: Destination mailbox used when `postAction` is `"move"` (for example `"Processed"` or `"[Gmail]/Trash"`).
> - `postActionIgnoreSkipped`: If `true` (default), skipped emails are ignored for post-action and not moved/deleted. > - `postActionIgnoreSkipped`: If `true` (default), skipped emails are ignored for post-action and not moved/deleted.
> - `postActionExpunge`: When `true`, the channel performs a full mailbox cleanup after processing emails (default `false`). Enable only on very old IMAP servers that lack modern UIDPLUS support. Note that this will expunge **all** messages marked as deleted in the mailbox, including ones not handled by the agent. Leaving this off is safe for all modern IMAP servers.
> - `allowedAttachmentTypes`: Save inbound attachments matching these MIME types — `["*"]` for all, e.g. `["application/pdf", "image/*"]` (default `[]` = disabled). > - `allowedAttachmentTypes`: Save inbound attachments matching these MIME types — `["*"]` for all, e.g. `["application/pdf", "image/*"]` (default `[]` = disabled).
> - `maxAttachmentSize`: Max size per attachment in bytes (default `2000000` / 2MB). > - `maxAttachmentSize`: Max size per attachment in bytes (default `2000000` / 2MB).
> - `maxAttachmentsPerEmail`: Max attachments to save per email (default `5`). > - `maxAttachmentsPerEmail`: Max attachments to save per email (default `5`).
@ -604,6 +605,7 @@ Give nanobot its own email account. It polls **IMAP** for incoming mail and repl
"postAction": "move", "postAction": "move",
"postActionMoveMailbox": "[Gmail]/Trash", "postActionMoveMailbox": "[Gmail]/Trash",
"postActionIgnoreSkipped": true, "postActionIgnoreSkipped": true,
"postActionExpunge": false,
"allowedAttachmentTypes": ["application/pdf", "image/*"] "allowedAttachmentTypes": ["application/pdf", "image/*"]
} }
} }

View File

@ -56,6 +56,7 @@ class EmailConfig(Base):
mark_seen: bool = True mark_seen: bool = True
post_action: Literal["delete", "move"] | None = None post_action: Literal["delete", "move"] | None = None
post_action_move_mailbox: str | None = None post_action_move_mailbox: str | None = None
post_action_expunge: bool = False
post_action_ignore_skipped: bool = True post_action_ignore_skipped: bool = True
max_body_chars: int = 12000 max_body_chars: int = 12000
subject_prefix: str = "Re: " subject_prefix: str = "Re: "
@ -739,7 +740,8 @@ class EmailChannel(BaseChannel):
if status == "OK": if status == "OK":
return return
self.logger.warning("UID EXPUNGE failed for UID {}, falling back to EXPUNGE", uid) self.logger.warning("UID EXPUNGE failed for UID {}, falling back to EXPUNGE", uid)
client.expunge() if self.config.post_action_expunge:
client.expunge()
@classmethod @classmethod
def _is_stale_imap_error(cls, exc: Exception) -> bool: def _is_stale_imap_error(cls, exc: Exception) -> bool: