Listener posts "Thinking..." placeholder and async-invokes a new exec-aide-conversation Lambda that runs a Bedrock Sonnet tool-use loop over 7 email tools, then updates the Slack message with the response.
111 lines
4.6 KiB
Markdown
111 lines
4.6 KiB
Markdown
# Exec Aide
|
|
|
|
Personal AI executive assistant for Adam Moussa. Monitors Gmail inbox, classifies emails by urgency using Claude Haiku (Bedrock), delivers Slack alerts and daily digests, and supports two-way conversation via Slack DM.
|
|
|
|
## 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
|
|
Fargate (Socket Mode) → Slack listener → conversation Lambda → Bedrock Sonnet + tools → Slack reply
|
|
```
|
|
|
|
**Stack:** CDK (TypeScript), stack name `exec-aide`, region us-east-1
|
|
|
|
### Services
|
|
|
|
- **exec-aide-fetch-classify** (Lambda): Polls Gmail via History API every 15 minutes, classifies each new message via Bedrock Haiku, filters out marketing, sends immediate Slack DM for HIGH priority, tracks thread state for unanswered detection.
|
|
- **exec-aide-daily-digest** (Lambda): Runs weekdays at 5 PM ET (DST-aware). Summarizes high-priority items, bypassed work orders, and unanswered threads older than 24h.
|
|
- **exec-aide-conversation** (Lambda): Bedrock Sonnet-powered conversational assistant. Handles multi-turn conversations with tool use for inbox queries, email search, thread details, and on-demand digest triggers. Invoked asynchronously by the listener.
|
|
- **exec-aide-listener** (Fargate): Socket Mode Slack bot for two-way conversation. Receives DMs, posts "Thinking..." placeholder, invokes conversation Lambda async, which updates the placeholder with the response. Runs in a dedicated VPC (10.30.0.0/16) on ARM64.
|
|
- **DynamoDB `exec-aide`**: Single-table design (MSG#, THD#, META#, CONV# prefixes). GSI `by-date` for daily digest queries. 90-day TTL.
|
|
|
|
### Conversation Tools
|
|
|
|
The conversation Lambda has access to these tools:
|
|
|
|
| Tool | Description |
|
|
|---|---|
|
|
| `get_inbox_summary` | Today's email stats + recent high-priority items |
|
|
| `get_high_priority_emails` | HIGH-classified emails for a given date |
|
|
| `get_unanswered_threads` | Threads awaiting reply for 24h+ |
|
|
| `get_bypassed_work_orders` | Emails sent without CC'ing work-orders@ |
|
|
| `search_emails_by_sender` | Find emails from a specific sender |
|
|
| `get_email_thread_detail` | Full thread context from Gmail |
|
|
| `trigger_daily_digest` | Send a digest on demand |
|
|
|
|
### CDK Constructs
|
|
|
|
- `lib/constructs/email-pipeline.ts` — DynamoDB table, all three Lambda functions, EventBridge schedules
|
|
- `lib/constructs/socket-mode.ts` — VPC, ECS cluster, Fargate service, ECR repo (image built automatically via `ContainerImage.fromAsset()`)
|
|
|
|
## Classification Rules
|
|
|
|
| Category | Trigger |
|
|
|---|---|
|
|
| **HIGH** | VIP sender, urgency keywords, escalation language, financial/legal matters |
|
|
| **NORMAL** | Standard business correspondence |
|
|
| **LOW** | FYI-only, calendar confirmations, informational notifications |
|
|
| **MARKETING** | Newsletters, promotions, bulk mailings, cold outreach (filtered out) |
|
|
| **Bypassed WO** | Email to Adam without work-orders@ in TO/CC |
|
|
| **Unanswered** | Thread where Adam hasn't replied in 24h+ |
|
|
|
|
## Prerequisites
|
|
|
|
- AWS CDK CLI (`npm install -g aws-cdk`)
|
|
- Node.js 18+, Python 3.12
|
|
- Docker (for Lambda bundling and Fargate image builds)
|
|
- Gmail API enabled in Google Cloud Console (project: `exec-aide`)
|
|
- Slack app "Exec Aide" with Socket Mode enabled
|
|
|
|
## One-Time Setup
|
|
|
|
### 1. Gmail OAuth
|
|
|
|
```bash
|
|
pip install google-auth-oauthlib
|
|
python scripts/get_gmail_token.py --client-secrets-file path/to/client_secret.json
|
|
# Authorize as adam@seahavenind.com, copy output to Secrets Manager
|
|
```
|
|
|
|
### 2. Secrets Manager
|
|
|
|
| Secret | Contents |
|
|
|---|---|
|
|
| `exec-aide/gmail-oauth` | `{ client_id, client_secret, refresh_token, access_token, token_expiry }` |
|
|
| `exec-aide/slack-credentials` | `{ botToken, signingSecret, appToken }` |
|
|
|
|
### 3. SSM Parameters
|
|
|
|
| Parameter | Value |
|
|
|---|---|
|
|
| `/exec-aide/adam-email` | `adam@seahavenind.com` |
|
|
| `/exec-aide/adam-slack-user-id` | Slack user ID |
|
|
| `/exec-aide/vip-senders` | JSON array of email addresses |
|
|
| `/exec-aide/vip-domains` | JSON array of domains |
|
|
| `/exec-aide/work-order-addresses` | `["work-orders@seahavenind.com","work-orders@seahaven.com"]` |
|
|
| `/exec-aide/unanswered-threshold-hours` | `24` |
|
|
|
|
## Deploy
|
|
|
|
```bash
|
|
npm install
|
|
cdk deploy
|
|
```
|
|
|
|
## Manual Testing
|
|
|
|
```bash
|
|
aws lambda invoke --function-name exec-aide-fetch-classify /dev/stdout
|
|
aws lambda invoke --function-name exec-aide-daily-digest /dev/stdout
|
|
```
|
|
|
|
Or DM the Exec Aide bot in Slack to test the conversational assistant.
|
|
|
|
## Updating VIP List
|
|
|
|
Edit the SSM parameters — changes take effect on the next Lambda cold start:
|
|
|
|
```bash
|
|
aws ssm put-parameter --name /exec-aide/vip-senders --value '["new@example.com"]' --type String --overwrite
|
|
```
|