93 lines
3.2 KiB
Markdown
93 lines
3.2 KiB
Markdown
|
|
# Exec Aide
|
||
|
|
|
||
|
|
Personal AI agent that monitors Adam Moussa's Gmail inbox, classifies emails by urgency using Claude Haiku (Bedrock), and delivers Slack alerts and daily digests.
|
||
|
|
|
||
|
|
## Architecture
|
||
|
|
|
||
|
|
```
|
||
|
|
EventBridge (15 min) → fetch-classify Lambda → Gmail API → Bedrock Haiku → DynamoDB + Slack DM
|
||
|
|
EventBridge Scheduler (5 PM ET M-F) → daily-digest Lambda → DynamoDB + Gmail API → Slack DM
|
||
|
|
```
|
||
|
|
|
||
|
|
- **exec-aide-fetch-classify**: Polls Gmail via History API, classifies each new message, sends immediate Slack DM for HIGH priority items, tracks thread state for unanswered detection.
|
||
|
|
- **exec-aide-daily-digest**: Summarizes the day's high-priority items, bypassed work orders (emails not CC'ing work-orders@), and unanswered threads older than 24h.
|
||
|
|
- **DynamoDB table `exec-aide`**: Single-table design with message records, thread tracking, and sync metadata. 90-day TTL.
|
||
|
|
|
||
|
|
## Classification Rules
|
||
|
|
|
||
|
|
| Category | Trigger |
|
||
|
|
|---|---|
|
||
|
|
| **HIGH** | VIP sender, urgency keywords, escalation language, financial/legal matters |
|
||
|
|
| **NORMAL** | Standard business correspondence |
|
||
|
|
| **LOW** | Newsletters, automated notifications, marketing |
|
||
|
|
| **Bypassed WO** | Email to Adam without work-orders@ in TO/CC |
|
||
|
|
| **Unanswered** | Thread where Adam hasn't replied in 24h+ |
|
||
|
|
|
||
|
|
## Prerequisites
|
||
|
|
|
||
|
|
- AWS CLI + SAM CLI
|
||
|
|
- Python 3.12
|
||
|
|
- Gmail API enabled in Google Cloud Console
|
||
|
|
- Slack app with `chat:write` + `im:write` scopes
|
||
|
|
|
||
|
|
## One-Time Setup
|
||
|
|
|
||
|
|
### 1. Gmail OAuth
|
||
|
|
|
||
|
|
```bash
|
||
|
|
# Install the setup dependency (not needed in Lambda)
|
||
|
|
pip install google-auth-oauthlib
|
||
|
|
|
||
|
|
# Download OAuth client JSON from Google Cloud Console, then:
|
||
|
|
python scripts/get_gmail_token.py --client-secrets-file path/to/client_secret.json
|
||
|
|
# Authorize as adam@seahavenind.com in the browser
|
||
|
|
# Copy the output into Secrets Manager
|
||
|
|
```
|
||
|
|
|
||
|
|
### 2. Secrets Manager
|
||
|
|
|
||
|
|
```bash
|
||
|
|
# Gmail OAuth (output from step 1)
|
||
|
|
aws secretsmanager create-secret --name exec-aide/gmail-oauth --secret-string '...'
|
||
|
|
|
||
|
|
# Slack bot token
|
||
|
|
aws secretsmanager create-secret --name exec-aide/slack-bot-token --secret-string 'xoxb-...'
|
||
|
|
```
|
||
|
|
|
||
|
|
### 3. SSM Parameters
|
||
|
|
|
||
|
|
```bash
|
||
|
|
aws ssm put-parameter --name /exec-aide/adam-email --value adam@seahavenind.com --type String
|
||
|
|
aws ssm put-parameter --name /exec-aide/adam-slack-user-id --value U01XXXXXXXX --type String
|
||
|
|
aws ssm put-parameter --name /exec-aide/vip-senders --value '["ceo@example.com"]' --type String
|
||
|
|
aws ssm put-parameter --name /exec-aide/vip-domains --value '["example.com"]' --type String
|
||
|
|
aws ssm put-parameter --name /exec-aide/work-order-addresses --value '["work-orders@seahavenind.com","work-orders@seahaven.com"]' --type String
|
||
|
|
aws ssm put-parameter --name /exec-aide/unanswered-threshold-hours --value 24 --type String
|
||
|
|
```
|
||
|
|
|
||
|
|
## Deploy
|
||
|
|
|
||
|
|
```bash
|
||
|
|
cp samconfig.toml.example samconfig.toml # edit if needed
|
||
|
|
sam build
|
||
|
|
sam deploy
|
||
|
|
```
|
||
|
|
|
||
|
|
## Manual Testing
|
||
|
|
|
||
|
|
```bash
|
||
|
|
# Trigger a poll
|
||
|
|
aws lambda invoke --function-name exec-aide-fetch-classify /dev/stdout
|
||
|
|
|
||
|
|
# Trigger the daily digest
|
||
|
|
aws lambda invoke --function-name exec-aide-daily-digest /dev/stdout
|
||
|
|
```
|
||
|
|
|
||
|
|
## Updating VIP List or Keywords
|
||
|
|
|
||
|
|
Edit the SSM parameters directly — changes take effect on the next Lambda cold start (or within ~15 minutes):
|
||
|
|
|
||
|
|
```bash
|
||
|
|
aws ssm put-parameter --name /exec-aide/vip-senders --value '["new@example.com"]' --type String --overwrite
|
||
|
|
```
|