Metadata-Version: 2.4
Name: waken-email
Version: 0.1.0
Summary: Email Source/Output for Waken — IMAP polling in, SMTP send out.
Project-URL: Homepage, https://github.com/WakenHQ/waken-email
Project-URL: Repository, https://github.com/WakenHQ/waken-email
Project-URL: Issues, https://github.com/WakenHQ/waken-email/issues
Project-URL: Documentation, https://github.com/WakenHQ/waken-email/blob/main/README.md
Author: Waken contributors
License: MIT
License-File: LICENSE
Keywords: agents,ai,email,imap,smtp,waken
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Software Development :: Libraries
Requires-Python: >=3.12
Requires-Dist: aiosmtplib>=3.0
Requires-Dist: waken>=0.1.1
Provides-Extra: dev
Requires-Dist: mypy>=1.11; extra == 'dev'
Requires-Dist: pytest-asyncio>=0.24; extra == 'dev'
Requires-Dist: pytest-cov>=5.0; extra == 'dev'
Requires-Dist: pytest>=8.3; extra == 'dev'
Requires-Dist: ruff>=0.6; extra == 'dev'
Description-Content-Type: text/markdown

# waken-email

[![CI](https://github.com/WakenHQ/waken-email/actions/workflows/ci.yml/badge.svg)](https://github.com/WakenHQ/waken-email/actions/workflows/ci.yml)
[![License: MIT](https://img.shields.io/badge/license-MIT-lightgrey)](https://github.com/WakenHQ/waken-email/blob/main/LICENSE)

Email `Source`/`Output` for [Waken](https://github.com/WakenHQ/waken) —
IMAP polling in, SMTP send out.

Like `waken-slack`/`waken-telegram`/`waken-voice`, this is a **channel**
adapter, not an AI backend: it implements `Source`/`Output`, not `Target`.
Unlike those siblings, there's no single company or SDK to defer to here —
SMTP and IMAP are generic protocols, not a vendor's product, so the
configuration and secret names below (`IMAP_*`/`SMTP_*`) are this
package's own invention, not inherited from anywhere.

## Install

```bash
pip install waken-email
```

## Usage

```python
from waken import Runtime
from waken_claude import ClaudeAdapter
from waken_email import EmailOutput, EmailSource

runtime = Runtime()
runtime.target("claude", ClaudeAdapter())

runtime.source("email", EmailSource(target="claude"))
runtime.output("email", EmailOutput())

runtime.run()
```

`EmailSource` polls an IMAP inbox for new mail and dispatches each new
message as an `Event`, keyed for replies by the sender's address (stashed
in `Event.metadata["to"]`). `EmailOutput` sends a `Target`'s `Response`
back to that address over SMTP. Registering both under the same name
(`"email"` above) means a reply to the original `Target`'s answer goes back
out over the same channel it came in on — see [`docs/api-spec.md`
§3](https://github.com/WakenHQ/waken/blob/main/docs/api-spec.md#registration) and
[§9](https://github.com/WakenHQ/waken/blob/main/docs/api-spec.md#9-error-handling)
in the main `waken` repo for how `Runtime.dispatch()` resolves delivery.

## Configuration

Both classes accept constructor keyword arguments, falling back to
environment variables if omitted:

| `EmailSource` | Env var | Default |
|---|---|---|
| `host` | `IMAP_HOST` | required |
| `username` | `IMAP_USERNAME` | required |
| `password` | `IMAP_PASSWORD` | required |
| `folder` | — | `"INBOX"` |
| `interval` | — | `30.0` seconds |
| `use_ssl` | — | `True` |

| `EmailOutput` | Env var | Default |
|---|---|---|
| `host` | `SMTP_HOST` | required |
| `username` | `SMTP_USERNAME` | required |
| `password` | `SMTP_PASSWORD` | required |
| `port` | — | `587` |
| `from_addr` | — | `username` |
| `use_tls` | — | `True` (STARTTLS) |

These names are invented for this package specifically — there's no
provider SDK whose own default env vars to reuse the way `waken-claude`
reuses `ANTHROPIC_API_KEY` or `waken-slack` reuses `SLACK_BOT_TOKEN`, since
SMTP/IMAP are generic protocols implemented by every mail provider, not one
company's SDK. See `docs/adapter-ci-setup.md` in the main `waken` repo for
the broader convention this deliberately doesn't fit.

## Design notes and scope boundaries

- **Sync stdlib for IMAP, async library for SMTP.** `imaplib` (stdlib) is
  wrapped in `loop.run_in_executor(...)` for `EmailSource`'s polling —
  the async IMAP libraries that exist (e.g. `aioimaplib`) are far less
  mature/widely used than stdlib `imaplib`, the same reasoning
  `waken-bedrock` applies to wrapping synchronous `boto3`. `EmailOutput`,
  by contrast, uses [`aiosmtplib`](https://github.com/cole/aiosmtplib)
  directly rather than wrapping stdlib `smtplib` — a mature, widely-used
  async SMTP client actually exists for this side of the protocol, so
  there's no reason to prefer the executor-wrapping workaround here.
- **IMAP UID tracking, not an in-memory "seen" set.** `EmailSource` tracks
  the highest IMAP UID processed so far rather than keeping an in-memory
  set of seen message ids — UIDs are monotonically increasing per mailbox
  (RFC 3501), so this is the IMAP-native equivalent of the built-in
  `FilesystemSource`'s `_seen` set or `waken-telegram`'s `offset`. A fresh
  IMAP connection is opened per poll and closed at the end of it, rather
  than holding one connection open across polls — simpler and more robust
  against idle-timeout disconnects. Whatever is already in the mailbox
  when `start()` runs is the baseline, not a new arrival, and never fires
  — the same rule `FilesystemSource` applies to pre-existing files.
- **Plain text only.** Message bodies are parsed with stdlib `email`
  (`get_body(preferencelist=("plain",))`) and only the first `text/plain`
  part is used. An HTML-only email with no plain-text alternative part
  comes through with an empty body. Parsing or stripping HTML, and sending
  HTML replies, are both out of scope for v1. `response.files` attachments
  are likewise not sent by `EmailOutput` yet.

## Development

```bash
git clone https://github.com/WakenHQ/waken-email
cd waken-email
pip install -e ".[dev]"
pytest
```

## License

[MIT](https://github.com/WakenHQ/waken-email/blob/main/LICENSE)
