Skip to main content

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.
Raises: 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

Each post() sends one plain-text email:
  • From from_address, To every address in to, with a BCC-style copy delivered to from_address so the mailbox holds the whole thread.
  • Subject subject, with In-Reply-To and References headers that keep it in one thread in every mail client.
  • An X-Swarms-Agent header 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 sets thread_id.
A recipient the server refuses is logged as a warning. Other SMTP errors raise.

messages()

Read the thread over IMAP, oldest first. Returns [] before the first post when no thread_id was given.
Posts made through this instance are included at once, before the mailbox receives them.

clear()

Delete the thread’s messages from mailbox, and start a new thread on the next post.
Raises: 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

More examples are in the SmtpChannel quickstart.

Provider notes

Notes

  • Other instances see a post after delivery. Another SmtpChannel on 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.

Source Code

View the source on GitHub.