Initial commit: Howl M365 email management daemon
Full implementation of the Howl email triage system: - Microsoft Graph API integration with MSAL auth (client-credentials and delegated modes) - Claude LLM classification via tool use for structured output - PostgreSQL database with customers, vendors, whitelist, and email_log tables - Alembic migration for full schema - APScheduler daemon with graceful shutdown - Typer CLI (run, dry-run, status commands) - Business rule classifier with overrides (whitelist protection, low-confidence fallback) - Action executor (move to folders, flag, escalate with webhook) - 35 passing unit tests - README, SETUP, and NEXT_STEPS documentation Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
commit
3bfda9e585
39 changed files with 3656 additions and 0 deletions
125
README.md
Normal file
125
README.md
Normal file
|
|
@ -0,0 +1,125 @@
|
|||
# Howl
|
||||
|
||||
An M365 email management daemon that uses the Microsoft Graph API and Claude AI to automatically classify and route incoming email based on your customer, vendor, and whitelist database.
|
||||
|
||||
## How it works
|
||||
|
||||
```
|
||||
Inbox (M365)
|
||||
│
|
||||
▼
|
||||
Graph API polls for unread mail
|
||||
│
|
||||
▼
|
||||
PostgreSQL lookup — is the sender a customer, vendor, or trusted contact?
|
||||
│
|
||||
▼
|
||||
Claude (LLM) classifies the email using sender context + body content
|
||||
│
|
||||
▼
|
||||
Business rules applied (whitelist always wins, low-confidence → hold for review)
|
||||
│
|
||||
▼
|
||||
Action taken: move to folder / flag / escalate with notification
|
||||
│
|
||||
▼
|
||||
email_log table records every decision for audit and retry
|
||||
```
|
||||
|
||||
## Features
|
||||
|
||||
- Supports both **shared mailboxes** (app-only, no user login) and **personal mailboxes** (delegated device-code auth)
|
||||
- **No emails are ever deleted** — only moved, flagged, or escalated
|
||||
- **Dry-run mode** — analyze and log without touching any email
|
||||
- **Idempotent** — safe to restart; already-processed messages are skipped
|
||||
- **Automatic retry** — failed messages are retried on the next poll cycle
|
||||
- **Full audit log** — every classification, confidence score, and action is stored in PostgreSQL
|
||||
|
||||
## Folder routing
|
||||
|
||||
| Sender type | LLM classification | Action |
|
||||
|---|---|---|
|
||||
| Customer | Inquiry / correspondence | Move to `Customers` folder |
|
||||
| Vendor | Invoice / notification | Move to `Vendors` folder |
|
||||
| Whitelist | Trusted contact | Mark read, stay in Inbox |
|
||||
| Any | Urgent / escalation | Move to `Escalate` + webhook notification |
|
||||
| Any | Uncertain (low confidence) | Move to `Needs Review` |
|
||||
| Unknown | Spam / newsletter | Move to `Junk Email` |
|
||||
|
||||
## Quick start
|
||||
|
||||
```bash
|
||||
# 1. Create and activate a virtual environment
|
||||
python3 -m venv .venv && source .venv/bin/activate
|
||||
|
||||
# 2. Install dependencies
|
||||
pip install -e ".[dev]"
|
||||
|
||||
# 3. Configure environment
|
||||
cp .env.example .env
|
||||
# Edit .env with your Azure, Anthropic, and PostgreSQL credentials
|
||||
|
||||
# 4. Run database migrations
|
||||
alembic upgrade head
|
||||
|
||||
# 5. Test with dry-run (no email mutations)
|
||||
howl dry-run --limit 10
|
||||
|
||||
# 6. Start the daemon
|
||||
howl run
|
||||
```
|
||||
|
||||
## CLI commands
|
||||
|
||||
| Command | Description |
|
||||
|---|---|
|
||||
| `howl run` | Start the daemon (blocking, polls on schedule) |
|
||||
| `howl dry-run [--limit N]` | Classify up to N emails, log results, take no action |
|
||||
| `howl status [--limit N]` | Display recent email_log entries in a table |
|
||||
|
||||
## Project structure
|
||||
|
||||
```
|
||||
howl/
|
||||
├── howl/
|
||||
│ ├── config.py # All configuration (pydantic-settings + .env)
|
||||
│ ├── main.py # CLI entry point (Typer)
|
||||
│ ├── logging_config.py # structlog setup (JSON or text)
|
||||
│ ├── db/
|
||||
│ │ ├── engine.py # Async SQLAlchemy engine
|
||||
│ │ ├── models.py # ORM models
|
||||
│ │ └── queries.py # lookup_sender(), email log helpers
|
||||
│ ├── graph/
|
||||
│ │ ├── auth.py # MSAL token provider (client-credentials or delegated)
|
||||
│ │ └── client.py # Graph API: list, read, move, flag messages
|
||||
│ ├── llm/
|
||||
│ │ ├── client.py # Anthropic SDK wrapper with retry
|
||||
│ │ ├── prompts.py # Prompt builder (system prompt + per-email user turn)
|
||||
│ │ └── schemas.py # EmailClassification model + tool definition
|
||||
│ ├── pipeline/
|
||||
│ │ ├── processor.py # EmailProcessor — orchestrates the full pipeline
|
||||
│ │ ├── classifier.py # Business rule overrides on LLM output
|
||||
│ │ └── actions.py # ActionExecutor — Graph mutations + webhook
|
||||
│ └── daemon/
|
||||
│ └── scheduler.py # APScheduler poll loop + graceful shutdown
|
||||
├── migrations/ # Alembic database migrations
|
||||
├── tests/ # Unit and integration tests
|
||||
├── .env.example # Configuration template
|
||||
├── SETUP.md # Detailed setup and configuration guide
|
||||
└── NEXT_STEPS.md # Roadmap and future improvements
|
||||
```
|
||||
|
||||
## Running tests
|
||||
|
||||
```bash
|
||||
pytest tests/ -q # All tests (no external services needed)
|
||||
pytest tests/ -q -m "not integration" # Unit tests only (default)
|
||||
pytest tests/ -q -m integration # Requires real M365 + Anthropic credentials
|
||||
```
|
||||
|
||||
## Requirements
|
||||
|
||||
- Python 3.11+
|
||||
- PostgreSQL 14+
|
||||
- Microsoft Azure app registration with `Mail.Read` + `Mail.ReadWrite` permissions
|
||||
- Anthropic API key
|
||||
Loading…
Add table
Add a link
Reference in a new issue