diff --git a/.env.example b/.env.example index f50cf87..fa514a9 100644 --- a/.env.example +++ b/.env.example @@ -30,6 +30,11 @@ EMAIL_AGENT_IMAP_PORT=993 EMAIL_AGENT_IMAP_LOOKBACK_DAYS=14 EMAIL_AGENT_IMAP_MAX_MESSAGES=100 +# --- Gmail SMTP (Phase 5 replies) ------------------------------------------- +# Submission uses STARTTLS on 587 and forces IPv4, like IMAP. +EMAIL_AGENT_SMTP_HOST=smtp.gmail.com +EMAIL_AGENT_SMTP_PORT=587 + # --- Integrations (used from later phases) --------------------------------- # JEV is the TypeSafe System One HTTP API. JEV_BASE_URL defaults to the # documented host https://api.typesafe.ai when unset. @@ -39,6 +44,9 @@ JEV_MODEL=jev-latest # Classifier bounds (FR-3): in-flight JEV requests, and messages per run. EMAIL_AGENT_CLASSIFY_CONCURRENCY=4 EMAIL_AGENT_CLASSIFY_MAX_MESSAGES=200 +# OmniRoute is the house AI gateway; all non-JEV models go through it. The +# default base URL is the gateway on .13 (http://192.168.20.13:20129/v1). OMNIROUTE_BASE_URL= OMNIROUTE_API_KEY= +OMNIROUTE_MODEL=auto/best-chat HUB_TOKEN= diff --git a/README.md b/README.md index 3da7f7d..6ac3fab 100644 --- a/README.md +++ b/README.md @@ -9,16 +9,19 @@ pre-planning material and is not authoritative. ## Status -**Phase 4 — Triage actions.** The Go skeleton, session login, and HTMX -dashboard (Phase 1), real mail over **IMAP with an app password, forced to IPv4** -(Phase 2), **JEV classification** (Phase 3), and now **bulk triage actions** -(Phase 4). The inbox has a select-all toolbar for delete, move, and mark (FR-6), -and a folder view for the recoverable Delete/Saved/Replied-to Gmail labels (FR-9). -A delete moves the message to a Gmail label (never a hard delete) and a row in -the Delete folder can be **restored** to the inbox. Bulk delete needs a UI -confirmation and a message classified Spam needs a second confirmation — spam is -never auto-deleted (§10). Every action is appended to `actions_log` (FR-13). See -[`docs/phase4-triage.md`](docs/phase4-triage.md) for the phase evidence. +**Phase 5 — Reply drafts.** The Go skeleton, session login, and HTMX dashboard +(Phase 1), real mail over **IMAP with an app password, forced to IPv4** (Phase +2), **JEV classification** (Phase 3), **bulk triage actions** (Phase 4), and now +**reply drafts** (Phase 5). Open a message to read it, draft a reply with the LLM +through **OmniRoute** from a template, edit it, and send it over Gmail SMTP +(IPv4 + STARTTLS). A reply is sent **as the address the mail arrived at** +(`X-Pm-Original-To`, FR-15) and returns to the original sender; the reply sets +`In-Reply-To`/`References` so Gmail keeps the thread, and the original is filed +under its `Replied-to/
` label. Sending is a deliberate UI action with a +confirmation — there is no auto-send (§10). Every action, including drafts and +sends, is appended to `actions_log` (FR-13). See +[`docs/phase5-replies.md`](docs/phase5-replies.md) for the phase evidence and +[`docs/phase4-triage.md`](docs/phase4-triage.md) for Phase 4. ## Layout @@ -27,7 +30,8 @@ cmd/email-agent/ service entry point (`-fetch`, `-classify`, `-classify-a internal/classify/ JEV (TypeSafe System One) client, rule engine, orchestration internal/config/ environment-driven configuration internal/mailfetch/ stdlib-only IMAP client, MIME parsing, FR-2 source address -internal/mailops/ Gmail label moves and flag changes for triage actions +internal/mailops/ Gmail label moves and flag changes, and SMTP reply sending +internal/reply/ LLM reply drafts through OmniRoute, reply templates internal/session/ server-side session store + signed cookie values internal/store/ SQLite open + schema (internal/store/schema.sql) internal/server/ HTTP routes, auth middleware, HTMX handlers @@ -58,6 +62,8 @@ Key variables: | `EMAIL_AGENT_PASSWORD` | _(empty)_ | Login password for Sam. If unset, `EMAIL_AGENT_SESSION_SECRET` doubles as the password | | `GMAIL_USER` | _(empty)_ | Gmail account that receives the forwarded mail | | `GMAIL_APP_PASSWORD` | _(empty)_ | Gmail app password for IMAP/SMTP (no OAuth) | +| `EMAIL_AGENT_SMTP_HOST` | `smtp.gmail.com` | SMTP submission host | +| `EMAIL_AGENT_SMTP_PORT` | `587` | SMTP submission port (STARTTLS, IPv4) | | `EMAIL_AGENT_IMAP_HOST` | `imap.gmail.com` | IMAP host | | `EMAIL_AGENT_IMAP_PORT` | `993` | IMAP port (implicit TLS) | | `EMAIL_AGENT_IMAP_LOOKBACK_DAYS` | `14` | First-scan window | @@ -67,6 +73,9 @@ Key variables: | `JEV_MODEL` | `jev-latest` | System One model alias | | `EMAIL_AGENT_CLASSIFY_CONCURRENCY` | `4` | In-flight JEV requests per run | | `EMAIL_AGENT_CLASSIFY_MAX_MESSAGES` | `200` | Messages per classify run | +| `OMNIROUTE_BASE_URL` | `http://192.168.20.13:20129/v1` | OmniRoute gateway (all non-JEV models) | +| `OMNIROUTE_API_KEY` | _(empty)_ | OmniRoute API key (required to draft replies) | +| `OMNIROUTE_MODEL` | `auto/best-chat` | Model alias for reply drafts | ## Mail sync (Phase 2) @@ -144,6 +153,42 @@ or Replied-to folder has a **Restore** button that moves it back to `INBOX`. Every action appends to `actions_log`: one `move` or `mark` row per message, plus one `bulk_