@adaptic/maestro 1.1.7 → 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/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
- - [Agent Persona Setup Guide](docs/guides/agent-persona-setup.md) -- Detailed guide for configuring identity, operating charter, and behavioural policies
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