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:
lorentz 2026-04-01 16:02:24 -04:00
commit 3bfda9e585
39 changed files with 3656 additions and 0 deletions

60
.env.example Normal file
View file

@ -0,0 +1,60 @@
# =============================================================
# Howl - M365 Email Management Daemon
# Copy this file to .env and fill in your values.
# =============================================================
# --- Microsoft Azure / Graph ---
AZURE_TENANT_ID=your-tenant-id
AZURE_CLIENT_ID=your-app-client-id
AZURE_CLIENT_SECRET=your-app-client-secret # Required for client_credentials mode only
# Auth mode: client_credentials (shared/app-only) or delegated (personal mailbox)
GRAPH_AUTH_MODE=client_credentials
# Path to MSAL token cache (delegated mode only — persists refresh token across restarts)
MSAL_TOKEN_CACHE_PATH=.msal_cache.bin
# Mailbox to monitor (UPN or shared mailbox address)
GRAPH_MAILBOX=shared@yourcompany.com
# Poll interval in seconds (default: 5 minutes)
GRAPH_POLL_INTERVAL_SECONDS=300
# Number of messages to fetch per poll cycle
GRAPH_BATCH_SIZE=50
# Max characters of email body sent to LLM (truncated beyond this)
GRAPH_MAX_BODY_CHARS=4000
# --- Anthropic ---
ANTHROPIC_API_KEY=sk-ant-your-key-here
ANTHROPIC_MODEL=claude-sonnet-4-6
ANTHROPIC_MAX_TOKENS=1024
# Confidence threshold: below this value → inbox_keep + requires_human_review
LLM_CONFIDENCE_THRESHOLD=0.60
# --- PostgreSQL ---
DATABASE_URL=postgresql+asyncpg://user:password@localhost:5432/howl
DATABASE_POOL_SIZE=5
DATABASE_POOL_MAX_OVERFLOW=10
# --- Processing ---
# Set to true to analyze emails but take no actions (safe for testing)
DRY_RUN=false
MAX_RETRY_COUNT=3
# Optional: Teams or Slack incoming webhook URL for escalation notifications
NOTIFICATION_WEBHOOK_URL=
# --- Mail Folder Names (Graph display names) ---
FOLDER_CUSTOMERS=Customers
FOLDER_VENDORS=Vendors
FOLDER_REVIEW=Needs Review
FOLDER_SPAM=Junk Email
FOLDER_ESCALATE=Escalate
# --- Logging ---
LOG_LEVEL=INFO
# json (structured, for production) or text (human-readable, for development)
LOG_FORMAT=json