@adaptic/maestro 1.1.6 → 1.1.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/commands/init-maestro.md +225 -279
- package/README.md +19 -2
- package/docs/guides/email-setup.md +399 -0
- package/docs/guides/media-generation-setup.md +349 -0
- package/docs/guides/outbound-governance-setup.md +438 -0
- package/docs/guides/pdf-generation-setup.md +315 -0
- package/docs/guides/poller-daemon-setup.md +550 -0
- package/docs/guides/rag-context-setup.md +459 -0
- package/docs/guides/slack-setup.md +348 -0
- package/docs/guides/voice-sms-setup.md +698 -0
- package/docs/guides/whatsapp-setup.md +282 -0
- package/docs/runbooks/mac-mini-bootstrap.md +21 -0
- package/package.json +1 -1
- package/scaffold/config/caller-id-map.yaml +46 -0
- package/scripts/media-generation/README.md +2 -0
- package/scripts/pdf-generation/README.md +2 -0
- package/scripts/poller/slack-poller.mjs +22 -7
- package/scripts/poller/trigger.mjs +12 -1
- package/scripts/setup/boot-claude-session.sh +4 -8
- package/scripts/setup/configure-macos.sh +8 -4
package/README.md
CHANGED
|
@@ -152,7 +152,7 @@ After scaffolding, run `claude "/init-maestro"` to configure your agent. This is
|
|
|
152
152
|
| **1. Identity Gathering** | Prompts for agent name, surname, title, archetype, email, phone, Mac mini hostname, principal (reporting line), responsibilities, communication style, operating principles |
|
|
153
153
|
| **2. Parallel Rewrite** | 8 sub-agents rewrite the repo in parallel: `config/agent.ts`, `CLAUDE.md`, config files, `package.json`, scripts, agent definitions, triggers/workflows, and agent-specific tools/skills |
|
|
154
154
|
| **3. Machine Config** | Generates launchd plists with the correct agent name, installs them, optionally configures macOS for headless 24/7 operation |
|
|
155
|
-
| **4. Service Setup** | Walks through Slack, Gmail, Twilio, Cloudflare, voice integration -- writing credentials to `.env
|
|
155
|
+
| **4. Service Setup** | Walks through Slack, Gmail, Twilio, Cloudflare, voice integration -- writing credentials to `.env`. See [Voice & SMS Setup Guide](docs/guides/voice-sms-setup.md) for detailed telephony instructions |
|
|
156
156
|
| **5. README** | Generates an agent-specific README.md |
|
|
157
157
|
| **6. GitHub Repo** | Optionally creates a GitHub repository and pushes the initial commit |
|
|
158
158
|
| **7. Verification** | Greps for stale references, validates `config/agent.ts`, runs healthcheck, prints summary |
|
|
@@ -361,11 +361,28 @@ Security policies are defined in `policies/` and `docs/governance/`.
|
|
|
361
361
|
|
|
362
362
|
## Key Documentation
|
|
363
363
|
|
|
364
|
-
|
|
364
|
+
### Setup Guides
|
|
365
|
+
|
|
366
|
+
- [Agent Persona Setup](docs/guides/agent-persona-setup.md) -- Identity, operating charter, and behavioural policies
|
|
367
|
+
- [Email Setup](docs/guides/email-setup.md) -- Gmail IMAP polling, SMTP sending, thread dedup, signatures
|
|
368
|
+
- [Slack Setup](docs/guides/slack-setup.md) -- Slack app, tokens, sending, typing indicators, events, CDP
|
|
369
|
+
- [WhatsApp Setup](docs/guides/whatsapp-setup.md) -- Twilio WhatsApp sandbox/production, webhook handler
|
|
370
|
+
- [Voice & SMS Setup](docs/guides/voice-sms-setup.md) -- Twilio SMS, Slack huddle voice, Deepgram STT, ElevenLabs TTS
|
|
371
|
+
- [Poller & Daemon Setup](docs/guides/poller-daemon-setup.md) -- Event polling, reactive daemon, triggers, watchdog
|
|
372
|
+
- [Outbound Governance Setup](docs/guides/outbound-governance-setup.md) -- Hooks, dedup, information barriers, disclosure
|
|
373
|
+
- [RAG & Context Setup](docs/guides/rag-context-setup.md) -- SQLite FTS5 search, pre-draft context, entity indexing
|
|
374
|
+
- [PDF Generation Setup](docs/guides/pdf-generation-setup.md) -- Pandoc + XeLaTeX branded document generation
|
|
375
|
+
- [Media Generation Setup](docs/guides/media-generation-setup.md) -- Gemini/Veo image and video generation
|
|
376
|
+
|
|
377
|
+
### Architecture & Governance
|
|
378
|
+
|
|
365
379
|
- [System Architecture](docs/architecture/system-architecture.md) -- Full system design and component overview
|
|
366
380
|
- [Agent Topology](docs/architecture/agent-topology.md) -- Sub-agent hierarchy and delegation model
|
|
367
381
|
- [Action Approval Model](docs/governance/action-approval-model.md) -- Communication governance and approval levels
|
|
368
382
|
- [Communications Policy](docs/governance/communications-policy.md) -- Voice modes, autonomy model, escalation rules
|
|
383
|
+
|
|
384
|
+
### Runbooks
|
|
385
|
+
|
|
369
386
|
- [Mac Mini Bootstrap](docs/runbooks/mac-mini-bootstrap.md) -- Hardware setup and initial configuration
|
|
370
387
|
- [Perpetual Operations](docs/runbooks/perpetual-operations.md) -- Keeping the agent running 24/7
|
|
371
388
|
- [Recovery and Failover](docs/runbooks/recovery-and-failover.md) -- Disaster recovery procedures
|
|
@@ -0,0 +1,399 @@
|
|
|
1
|
+
# Email Setup Guide
|
|
2
|
+
|
|
3
|
+
How to enable Gmail-based email for a Maestro agent: IMAP polling (reading inbound mail), SMTP sending (plain, threaded, with attachments, as principal), email thread deduplication, signature management, and email archival.
|
|
4
|
+
|
|
5
|
+
**Prerequisites**: Complete the [Mac Mini Bootstrap](../runbooks/mac-mini-bootstrap.md) and have the agent's `.env` file created.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Architecture Overview
|
|
10
|
+
|
|
11
|
+
```
|
|
12
|
+
┌──────────────────────────────────────────────────────────────────────┐
|
|
13
|
+
│ INBOUND │
|
|
14
|
+
│ Gmail IMAP ──▶ gmail-poller.mjs ──▶ state/inbox/gmail/*.yaml │
|
|
15
|
+
│ (every 60s) imap-client.mjs (inbox processor routes) │
|
|
16
|
+
│ │
|
|
17
|
+
│ Optional: secondary inbox (e.g. CEO inbox) │
|
|
18
|
+
│ Gmail IMAP ──▶ mehran-gmail-poller.mjs ──▶ state/inbox/gmail/*.yaml │
|
|
19
|
+
├──────────────────────────────────────────────────────────────────────┤
|
|
20
|
+
│ OUTBOUND │
|
|
21
|
+
│ │
|
|
22
|
+
│ ┌─────────────────┐ ┌───────────────────────┐ ┌───────────────┐ │
|
|
23
|
+
│ │ send-email.sh │ │ send-email-threaded.py │ │ send-email- │ │
|
|
24
|
+
│ │ (simple HTML) │ │ (thread-aware + dedup) │ │ with-attach.py│ │
|
|
25
|
+
│ └───────┬─────────┘ └──────────┬────────────┘ └──────┬────────┘ │
|
|
26
|
+
│ │ │ │ │
|
|
27
|
+
│ ▼ ▼ ▼ │
|
|
28
|
+
│ ┌────────────────────────────────────────────────────────────────┐ │
|
|
29
|
+
│ │ Pre-send pipeline: │ │
|
|
30
|
+
│ │ 1. validate_outbound.py (factual checks — blocks if issues) │ │
|
|
31
|
+
│ │ 2. llm_email_dedup.py (LLM asks: already addressed?) │ │
|
|
32
|
+
│ │ 3. outbound_dedup.py (content-hash dedup) │ │
|
|
33
|
+
│ │ 4. pre_draft_lookup.py (context enrichment — advisory) │ │
|
|
34
|
+
│ │ 5. email_quote_thread.py (include quoted original in reply) │ │
|
|
35
|
+
│ └────────────────────────────────────────────────────────────────┘ │
|
|
36
|
+
│ │ │
|
|
37
|
+
│ ▼ │
|
|
38
|
+
│ msmtp / Gmail SMTP ──▶ recipient │
|
|
39
|
+
│ (with branded HTML signature auto-appended) │
|
|
40
|
+
├──────────────────────────────────────────────────────────────────────┤
|
|
41
|
+
│ DEDUP & THREADING │
|
|
42
|
+
│ email_thread_dedup.py — Hash-based thread dedup │
|
|
43
|
+
│ llm_email_dedup.py — LLM semantic dedup (Claude Haiku) │
|
|
44
|
+
│ email_quote_thread.py — Fetch and format quoted reply chain │
|
|
45
|
+
├──────────────────────────────────────────────────────────────────────┤
|
|
46
|
+
│ SEARCH │
|
|
47
|
+
│ search-mehran-inbox.py — Search CEO inbox via IMAP │
|
|
48
|
+
└──────────────────────────────────────────────────────────────────────┘
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
## 1. Gmail Account & App Password
|
|
54
|
+
|
|
55
|
+
Maestro uses IMAP for reading email and SMTP for sending. Both use Gmail app passwords (not OAuth) for simplicity and reliability on headless Mac minis.
|
|
56
|
+
|
|
57
|
+
### 1.1 Enable 2-Factor Authentication
|
|
58
|
+
|
|
59
|
+
App passwords require 2FA:
|
|
60
|
+
|
|
61
|
+
1. Go to https://myaccount.google.com/security
|
|
62
|
+
2. Enable 2-Step Verification if not already active
|
|
63
|
+
|
|
64
|
+
### 1.2 Generate an App Password
|
|
65
|
+
|
|
66
|
+
1. Go to https://myaccount.google.com/apppasswords
|
|
67
|
+
2. Select app: "Mail", device: "Mac"
|
|
68
|
+
3. Click "Generate"
|
|
69
|
+
4. Copy the 16-character password (no spaces)
|
|
70
|
+
|
|
71
|
+
### 1.3 Configure Environment Variables
|
|
72
|
+
|
|
73
|
+
Add to `.env`:
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
# Agent's Gmail account
|
|
77
|
+
GMAIL_APP_PASSWORD=xxxxxxxxxxxxxxxx
|
|
78
|
+
|
|
79
|
+
# Optional: secondary inbox (e.g. CEO email) for monitoring
|
|
80
|
+
SECONDARY_GMAIL_APP_PASSWORD=xxxxxxxxxxxxxxxx
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### 1.4 Configure msmtp (SMTP Client)
|
|
84
|
+
|
|
85
|
+
The `send-email.sh` script uses `msmtp` to send email via Gmail SMTP:
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
# Install msmtp
|
|
89
|
+
brew install msmtp
|
|
90
|
+
|
|
91
|
+
# Create configuration
|
|
92
|
+
cat > ~/.msmtprc << 'EOF'
|
|
93
|
+
defaults
|
|
94
|
+
auth on
|
|
95
|
+
tls on
|
|
96
|
+
tls_trust_file /etc/ssl/cert.pem
|
|
97
|
+
logfile ~/maestro/logs/msmtp.log
|
|
98
|
+
|
|
99
|
+
account adaptic
|
|
100
|
+
host smtp.gmail.com
|
|
101
|
+
port 587
|
|
102
|
+
from agent@company.com
|
|
103
|
+
user agent@company.com
|
|
104
|
+
password YOUR_GMAIL_APP_PASSWORD
|
|
105
|
+
EOF
|
|
106
|
+
|
|
107
|
+
chmod 600 ~/.msmtprc
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
Replace `agent@company.com` and the password with your agent's credentials.
|
|
111
|
+
|
|
112
|
+
**Alternative**: The Python send scripts (`send-email-threaded.py`, `send-email-with-attachment.py`) use `smtplib` directly with Gmail SMTP, so they don't need msmtp. Only the shell `send-email.sh` requires msmtp.
|
|
113
|
+
|
|
114
|
+
---
|
|
115
|
+
|
|
116
|
+
## 2. Inbound Email — IMAP Polling
|
|
117
|
+
|
|
118
|
+
The poller checks Gmail via IMAP on every polling cycle (default: 60 seconds) and writes new messages to the inbox.
|
|
119
|
+
|
|
120
|
+
### 2.1 How It Works
|
|
121
|
+
|
|
122
|
+
1. `scripts/poller/gmail-poller.mjs` connects via `scripts/poller/imap-client.mjs`
|
|
123
|
+
2. Searches for UNSEEN messages in INBOX
|
|
124
|
+
3. Parses each message: sender, subject, body, attachments, threading headers
|
|
125
|
+
4. Writes a YAML file to `state/inbox/gmail/` for the inbox processor
|
|
126
|
+
5. Marks messages as SEEN to avoid reprocessing
|
|
127
|
+
|
|
128
|
+
### 2.2 IMAP Configuration
|
|
129
|
+
|
|
130
|
+
The poller reads credentials from `.env`:
|
|
131
|
+
|
|
132
|
+
| Variable | Purpose |
|
|
133
|
+
|---|---|
|
|
134
|
+
| `GMAIL_APP_PASSWORD` | Agent's own inbox |
|
|
135
|
+
| `SECONDARY_GMAIL_APP_PASSWORD` | CEO/secondary inbox |
|
|
136
|
+
|
|
137
|
+
IMAP server settings are hardcoded to Gmail defaults (`imap.gmail.com:993` with SSL).
|
|
138
|
+
|
|
139
|
+
### 2.3 Secondary Inbox Monitoring
|
|
140
|
+
|
|
141
|
+
If the agent monitors a second inbox (e.g. the CEO's email for triage):
|
|
142
|
+
|
|
143
|
+
- `scripts/poller/mehran-gmail-poller.mjs` handles the secondary inbox
|
|
144
|
+
- Uses `SECONDARY_GMAIL_APP_PASSWORD`
|
|
145
|
+
- Writes to the same `state/inbox/gmail/` directory with source annotation
|
|
146
|
+
|
|
147
|
+
### 2.4 Verify IMAP Access
|
|
148
|
+
|
|
149
|
+
```bash
|
|
150
|
+
# Test IMAP connectivity (uses Python imaplib)
|
|
151
|
+
python3 -c "
|
|
152
|
+
import imaplib
|
|
153
|
+
m = imaplib.IMAP4_SSL('imap.gmail.com')
|
|
154
|
+
m.login('agent@company.com', 'YOUR_APP_PASSWORD')
|
|
155
|
+
m.select('INBOX')
|
|
156
|
+
_, msgs = m.search(None, 'UNSEEN')
|
|
157
|
+
print(f'Connected OK. {len(msgs[0].split())} unread messages.')
|
|
158
|
+
m.logout()
|
|
159
|
+
"
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
---
|
|
163
|
+
|
|
164
|
+
## 3. Outbound Email — Sending
|
|
165
|
+
|
|
166
|
+
Three send scripts handle different use cases:
|
|
167
|
+
|
|
168
|
+
### 3.1 Simple HTML Email (`send-email.sh`)
|
|
169
|
+
|
|
170
|
+
Sends a one-shot HTML email with branded signature via `msmtp`.
|
|
171
|
+
|
|
172
|
+
```bash
|
|
173
|
+
./scripts/send-email.sh "recipient@example.com" "Subject line" "Body text here"
|
|
174
|
+
|
|
175
|
+
# With CC and threading headers:
|
|
176
|
+
./scripts/send-email.sh "to@example.com" "Re: Subject" "Reply body" "cc@example.com" "<message-id>" "<references>"
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
**Features**:
|
|
180
|
+
- HTML signature with name, title, logo, phone, address auto-appended
|
|
181
|
+
- Content-hash dedup via `outbound-dedup.sh`
|
|
182
|
+
- Markdown-to-HTML conversion for body
|
|
183
|
+
|
|
184
|
+
### 3.2 Threaded Email (`send-email-threaded.py`)
|
|
185
|
+
|
|
186
|
+
The primary send script for production use. Thread-aware with full dedup pipeline.
|
|
187
|
+
|
|
188
|
+
```bash
|
|
189
|
+
python3 scripts/send-email-threaded.py "to@example.com" "Subject" "Body" \
|
|
190
|
+
--reply-to-subject "Original thread subject" \
|
|
191
|
+
--attachment /path/to/file.pdf
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
**Features**:
|
|
195
|
+
- IMAP lookup for real Message-IDs (proper Gmail threading)
|
|
196
|
+
- 3-layer dedup: LLM semantic check → Gmail Sent folder check → content-hash
|
|
197
|
+
- Pre-draft context enrichment (advisory — looks up recipient before sending)
|
|
198
|
+
- Factual validation (blocks if critical issues found)
|
|
199
|
+
- Quoted reply chain inclusion (`email_quote_thread.py`)
|
|
200
|
+
- Branded HTML signature auto-appended
|
|
201
|
+
- `--force` flag to bypass dedup in exceptional cases
|
|
202
|
+
|
|
203
|
+
### 3.3 Email with Attachments (`send-email-with-attachment.py`)
|
|
204
|
+
|
|
205
|
+
```bash
|
|
206
|
+
python3 scripts/send-email-with-attachment.py "to@example.com" "Subject" "Body" \
|
|
207
|
+
--attachment /path/to/file.pdf \
|
|
208
|
+
--attachment /path/to/image.png \
|
|
209
|
+
--cc "cc@example.com" \
|
|
210
|
+
--reply-to-subject "Thread subject"
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
**Features**:
|
|
214
|
+
- MIME multipart with proper content-type detection
|
|
215
|
+
- Supports multiple attachments
|
|
216
|
+
- Full dedup pipeline (LLM + content-hash)
|
|
217
|
+
- Pre-draft context enrichment
|
|
218
|
+
- Same branded signature
|
|
219
|
+
|
|
220
|
+
### 3.4 Sending as Principal (`send-email-as-mehran.py`)
|
|
221
|
+
|
|
222
|
+
Sends email in the CEO/principal's voice from their email account:
|
|
223
|
+
|
|
224
|
+
```bash
|
|
225
|
+
python3 scripts/send-email-as-mehran.py "to@example.com" "Subject" "Body" \
|
|
226
|
+
--cc "cc@example.com"
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
This uses the secondary Gmail credentials and the principal's signature block.
|
|
230
|
+
|
|
231
|
+
---
|
|
232
|
+
|
|
233
|
+
## 4. Email Signatures
|
|
234
|
+
|
|
235
|
+
### 4.1 Signature Files
|
|
236
|
+
|
|
237
|
+
Two HTML signature templates live in `scripts/`:
|
|
238
|
+
|
|
239
|
+
| File | Used By | Identity |
|
|
240
|
+
|---|---|---|
|
|
241
|
+
| `email-signature.html` | Agent's own emails | Agent name, title, logo, phone, address |
|
|
242
|
+
| `email-signature-mehran.html` | Principal's emails | CEO name, title, logo, phone, address |
|
|
243
|
+
|
|
244
|
+
### 4.2 Signature Behaviour
|
|
245
|
+
|
|
246
|
+
- **All send scripts auto-append the HTML signature** — do not include a text sign-off in the email body
|
|
247
|
+
- The signature includes: name, title, Adaptic logo (hosted on Google), phone number, office address, confidentiality disclaimer
|
|
248
|
+
- Logo URL points to a Google-hosted image (no local file dependency)
|
|
249
|
+
|
|
250
|
+
### 4.3 Customising for a New Agent
|
|
251
|
+
|
|
252
|
+
Update `email-signature.html`:
|
|
253
|
+
- Replace the agent name, title, and phone number
|
|
254
|
+
- Keep the logo URL, address, and disclaimer structure
|
|
255
|
+
|
|
256
|
+
---
|
|
257
|
+
|
|
258
|
+
## 5. Email Thread Deduplication
|
|
259
|
+
|
|
260
|
+
Email dedup prevents the agent from sending duplicate replies to the same thread. Three layers work together:
|
|
261
|
+
|
|
262
|
+
### 5.1 Layer 1 — LLM Semantic Dedup (`llm_email_dedup.py`)
|
|
263
|
+
|
|
264
|
+
Uses Claude Haiku to check if a topic has already been addressed in recent conversation history with the recipient.
|
|
265
|
+
|
|
266
|
+
- Reads recent sent emails to the same recipient via IMAP
|
|
267
|
+
- Asks Claude: "Has this topic already been addressed?"
|
|
268
|
+
- Returns `DEDUP_SKIP` if the LLM determines it's a duplicate
|
|
269
|
+
- Replaced the old subject+recipient hash lock (removed per CEO directive — was causing false positives on follow-up emails with the same subject)
|
|
270
|
+
|
|
271
|
+
### 5.2 Layer 2 — Gmail Sent Folder Check
|
|
272
|
+
|
|
273
|
+
Checks the actual Gmail Sent folder via IMAP to see if a similar email was recently sent.
|
|
274
|
+
|
|
275
|
+
### 5.3 Layer 3 — Content-Hash Dedup (`outbound_dedup.py`)
|
|
276
|
+
|
|
277
|
+
Atomic mkdir-based locking using SHA-256 hash of `to + subject + first 100 chars of body`. Prevents concurrent sessions from sending identical emails.
|
|
278
|
+
|
|
279
|
+
- Lock TTL: 12 hours
|
|
280
|
+
- Fail-open: if the lock system errors, the email sends anyway
|
|
281
|
+
|
|
282
|
+
### 5.4 Email Quote Threading (`email_quote_thread.py`)
|
|
283
|
+
|
|
284
|
+
When replying to an existing thread:
|
|
285
|
+
1. Fetches the original email chain via IMAP
|
|
286
|
+
2. Formats quoted HTML with attribution headers ("On [date], [sender] wrote:")
|
|
287
|
+
3. Appends quoted chain below the new reply
|
|
288
|
+
|
|
289
|
+
---
|
|
290
|
+
|
|
291
|
+
## 6. Email Search
|
|
292
|
+
|
|
293
|
+
### 6.1 Inbox Search (`search-mehran-inbox.py`)
|
|
294
|
+
|
|
295
|
+
Searches the CEO's (or agent's) Gmail via IMAP for context retrieval:
|
|
296
|
+
|
|
297
|
+
```bash
|
|
298
|
+
python3 scripts/search-mehran-inbox.py --query "DFSA submission" --limit 5
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
Used by the pre-draft context system to find relevant email history before composing messages.
|
|
302
|
+
|
|
303
|
+
---
|
|
304
|
+
|
|
305
|
+
## 7. Email Archival
|
|
306
|
+
|
|
307
|
+
### 7.1 Archive Script (`archive-email.sh`)
|
|
308
|
+
|
|
309
|
+
Archives processed emails:
|
|
310
|
+
|
|
311
|
+
```bash
|
|
312
|
+
./scripts/archive-email.sh
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
Moves processed emails from active inbox directories to date-partitioned archive directories.
|
|
316
|
+
|
|
317
|
+
---
|
|
318
|
+
|
|
319
|
+
## 8. Testing
|
|
320
|
+
|
|
321
|
+
| # | Test | How to Verify |
|
|
322
|
+
|---|---|---|
|
|
323
|
+
| 1 | IMAP connectivity | Run the Python IMAP test from section 2.4 |
|
|
324
|
+
| 2 | msmtp configuration | `echo "test" \| msmtp -a adaptic your@email.com` |
|
|
325
|
+
| 3 | Simple send | `./scripts/send-email.sh "your@email.com" "Test" "Body"` |
|
|
326
|
+
| 4 | Threaded send | Send a reply with `--reply-to-subject` and verify Gmail threads it |
|
|
327
|
+
| 5 | Attachment send | Send with `--attachment` and verify file arrives |
|
|
328
|
+
| 6 | Dedup working | Send identical email twice; second should show `DEDUP_SKIP` |
|
|
329
|
+
| 7 | LLM dedup | Send two different emails about same topic; second should warn |
|
|
330
|
+
| 8 | Signature rendering | Check received email has branded HTML signature |
|
|
331
|
+
| 9 | Poller picking up mail | Send email to agent, check `state/inbox/gmail/` |
|
|
332
|
+
| 10 | Audit logging | Check `logs/audit/YYYY-MM-DD-actions.jsonl` for send entries |
|
|
333
|
+
|
|
334
|
+
---
|
|
335
|
+
|
|
336
|
+
## 9. Troubleshooting
|
|
337
|
+
|
|
338
|
+
### IMAP connection refused
|
|
339
|
+
|
|
340
|
+
1. Verify 2FA is enabled on the Google account
|
|
341
|
+
2. Verify the app password is correct (no spaces)
|
|
342
|
+
3. Check that IMAP is enabled: Gmail Settings → Forwarding and POP/IMAP → Enable IMAP
|
|
343
|
+
4. Check firewall allows outbound connections to `imap.gmail.com:993`
|
|
344
|
+
|
|
345
|
+
### msmtp authentication failure
|
|
346
|
+
|
|
347
|
+
1. Verify `~/.msmtprc` has correct username and app password
|
|
348
|
+
2. Verify file permissions: `chmod 600 ~/.msmtprc`
|
|
349
|
+
3. Test: `echo "test" | msmtp -a adaptic --debug your@email.com`
|
|
350
|
+
4. Check `logs/msmtp.log` for detailed error
|
|
351
|
+
|
|
352
|
+
### Emails not threading in Gmail
|
|
353
|
+
|
|
354
|
+
1. Verify `In-Reply-To` and `References` headers are set correctly
|
|
355
|
+
2. Use `send-email-threaded.py` (not `send-email.sh`) for replies — it does IMAP lookup for real Message-IDs
|
|
356
|
+
3. Gmail threads by `In-Reply-To` + matching `References` + same subject (with `Re:` prefix)
|
|
357
|
+
|
|
358
|
+
### LLM dedup false positives
|
|
359
|
+
|
|
360
|
+
1. If legitimate follow-ups are being blocked, use `--force` flag
|
|
361
|
+
2. Review the LLM dedup logic in `llm_email_dedup.py` — it uses Claude Haiku for semantic comparison
|
|
362
|
+
3. The LLM checks recent sent emails only (last 48 hours) — old conversations won't trigger
|
|
363
|
+
|
|
364
|
+
### Duplicate emails being sent
|
|
365
|
+
|
|
366
|
+
1. Verify `outbound-dedup.sh` is executable: `chmod +x scripts/outbound-dedup.sh`
|
|
367
|
+
2. Check lock directory exists: `ls state/locks/outbound/email/`
|
|
368
|
+
3. Verify lock TTL hasn't been set too low (default: 720 minutes / 12 hours)
|
|
369
|
+
4. Run cleanup: `./scripts/outbound-dedup-cleanup.sh`
|
|
370
|
+
|
|
371
|
+
---
|
|
372
|
+
|
|
373
|
+
## Key Files
|
|
374
|
+
|
|
375
|
+
| File | Purpose |
|
|
376
|
+
|---|---|
|
|
377
|
+
| `scripts/send-email.sh` | Simple HTML email via msmtp |
|
|
378
|
+
| `scripts/send-email-threaded.py` | Thread-aware email with full dedup pipeline |
|
|
379
|
+
| `scripts/send-email-with-attachment.py` | MIME email with file attachments |
|
|
380
|
+
| `scripts/send-email-as-mehran.py` | Send as principal (CEO voice) |
|
|
381
|
+
| `scripts/email_thread_dedup.py` | Hash-based email thread deduplication |
|
|
382
|
+
| `scripts/llm_email_dedup.py` | LLM semantic deduplication (Claude Haiku) |
|
|
383
|
+
| `scripts/email_quote_thread.py` | Fetch and format quoted reply chains |
|
|
384
|
+
| `scripts/archive-email.sh` | Email archival workflow |
|
|
385
|
+
| `scripts/email-signature.html` | Agent's branded HTML signature |
|
|
386
|
+
| `scripts/email-signature-mehran.html` | Principal's branded HTML signature |
|
|
387
|
+
| `scripts/search-mehran-inbox.py` | IMAP email search for context retrieval |
|
|
388
|
+
| `scripts/poller/gmail-poller.mjs` | Inbound email polling (IMAP) |
|
|
389
|
+
| `scripts/poller/imap-client.mjs` | IMAP client wrapper |
|
|
390
|
+
| `scripts/poller/mehran-gmail-poller.mjs` | Secondary inbox polling |
|
|
391
|
+
|
|
392
|
+
---
|
|
393
|
+
|
|
394
|
+
## Related Documents
|
|
395
|
+
|
|
396
|
+
- [Voice & SMS Setup](voice-sms-setup.md) — Phone and SMS capabilities
|
|
397
|
+
- [Outbound Governance Setup](outbound-governance-setup.md) — Dedup, validation, information barriers
|
|
398
|
+
- [Poller & Daemon Setup](poller-daemon-setup.md) — How email polling integrates with the event loop
|
|
399
|
+
- [Agent Persona Setup](agent-persona-setup.md) — Configuring agent identity for email signature
|