Plugin SDK: raw message bytes, per-message move, mailbox in the envelope

Three additive surfaces the antispam plugin (M7, Appendix C) needs and the
frozen §11 API did not have. None of them changes a core mail path.

`actions.move_to_special` moves ONE message into an account's folder by
special-use, wrapping the same optimistic-update + move_job path the core
endpoint uses. The existing `trash` moves a whole thread, which is right for
Delete and wrong for a spam verdict: one spam reply must not drag a legitimate
conversation into Junk. It returns False rather than raising when the account
has no such folder — an account whose server lacks a Junk folder must still
receive its mail.

`messages.raw` returns the stored RFC822 bytes. Spam scoring needs headers and
body; the SDK could only hand out one header at a time.

`envelope` now also carries account_id and mailbox_special_use. Appendix C must
classify only inbox arrivals, and neither the message.new payload nor the
envelope previously said where a message landed.

docs/open-questions.md gains OQ-6 (why these are additive rather than a spec
violation of "zero core diffs") and OQ-7 (why the engine is reached with plain
httpx instead of the SSRF-guarded sdk.http).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018SqJYt7jD7oUEkoY68sBZo
This commit is contained in:
2026-09-16 22:14:24 +02:00
co-authored by Claude Opus 5
parent 348daf8ad7
commit 428ec58c71
3 changed files with 135 additions and 9 deletions
+4 -2
View File
@@ -143,12 +143,14 @@ class PluginSdk:
@property
def messages(self) -> PluginMessages:
"""Read a user's message envelope (subject/sender/flags) for matching."""
"""Read a user's message: envelope (subject/sender/flags), one header, or
the whole RFC822 blob for engines that need it."""
return PluginMessages()
@property
def actions(self) -> PluginActions:
"""User-scoped message actions (``set_flags``) — e.g. for filter rules."""
"""User-scoped message actions: ``set_flags``, ``trash`` (thread), and
``move_to_special`` (one message into e.g. Junk)."""
return PluginActions()
@property