Overview
SmtpChannel keeps a conversation as one email thread. Each post is emailed to the people in to, who can read and reply from their own mail client. A copy also goes to from_address, and messages() reads that mailbox over IMAP. A person’s reply joins the conversation under their email address, with the quoted history below it removed.
It works with any provider that accepts a password over SMTP and IMAP. For a hosted inbox with an API instead, see AgentMailChannel.
Import
Environment variables
Constructor
str
required
The mailbox the agents send as and read from.
str | List[str]
required
Who receives each post and can reply.
str
required
The SMTP server, such as
smtp.gmail.com.str
required
The IMAP server, such as
imap.gmail.com.str
default:"None"
The login for both servers. Defaults to
SMTP_USERNAME, then from_address.str
default:"None"
The password for both servers. Defaults to
SMTP_PASSWORD.int
default:"587"
465 uses implicit TLS. Any other port uses STARTTLS. Plain, unencrypted SMTP is not supported.int
default:"993"
IMAP over TLS.
str
default:"INBOX"
The IMAP folder that holds the thread.
str
default:"Swarms conversation"
The thread’s subject line.
str
default:"None"
The
Message-ID of a thread’s first message, to join an existing thread. Must look like <id@domain>. When omitted, the first post starts a new thread.float
default:"30.0"
Seconds to wait on each server.
ValueError if no password is passed or set, if the mailbox, username, password or thread id contains a NUL, CR or LF character, or if thread_id does not look like <id@domain>.
Attributes
How messages are sent and read
Eachpost() sends one plain-text email:
- From
from_address, To every address into, with a BCC-style copy delivered tofrom_addressso the mailbox holds the whole thread. - Subject
subject, withIn-Reply-ToandReferencesheaders that keep it in one thread in every mail client. - An
X-Swarms-Agentheader names the speaker. - The body is the exact text, base64-encoded as UTF-8, so it reads back byte for byte.
messages() logs into IMAP, finds every message in mailbox whose Message-ID or References names the thread, and returns them oldest first by their Date header:
Methods
post()
Email one message to the thread. The first post starts the thread and setsthread_id.
messages()
Read the thread over IMAP, oldest first. Returns[] before the first post when no thread_id was given.
clear()
Delete the thread’s messages frommailbox, and start a new thread on the next post.
imaplib.IMAP4.error if the server lacks the UIDPLUS extension, which lets it expunge only the thread’s messages. Emails already delivered to the people in to stay in their mailboxes.
close()
Does nothing. Each read and each post opens and closes its own connection.Usage Examples
Wait for a person’s reply, then continue
Rejoin a thread from another process
Provider notes
Notes
- Other instances see a post after delivery. Another
SmtpChannelon the same thread, in this process or another, sees a post only once the mailbox has received its copy. Until then it can miss an answer, and an agent there can post its task again. - Every read is an IMAP login. An agent reads the channel on every run, so a busy loop logs in often. Keep agent runs on an email channel coarse.
- Mail can be read by anyone on the thread. Do not post secrets, and treat replies as untrusted input.