@adaptic/maestro 1.1.7 → 1.4.1

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.
Files changed (60) hide show
  1. package/.claude/commands/init-maestro.md +502 -260
  2. package/README.md +47 -2
  3. package/bin/maestro.mjs +1 -1
  4. package/docs/guides/agents-observe-setup.md +64 -0
  5. package/docs/guides/ccxray-diagnostics.md +65 -0
  6. package/docs/guides/claude-mem-setup.md +79 -0
  7. package/docs/guides/claude-pace-setup.md +56 -0
  8. package/docs/guides/claudraband-sessions.md +98 -0
  9. package/docs/guides/clawteam-swarm.md +116 -0
  10. package/docs/guides/code-review-graph-setup.md +86 -0
  11. package/docs/guides/email-setup.md +399 -0
  12. package/docs/guides/media-generation-setup.md +349 -0
  13. package/docs/guides/outbound-governance-setup.md +438 -0
  14. package/docs/guides/pdf-generation-setup.md +315 -0
  15. package/docs/guides/poller-daemon-setup.md +550 -0
  16. package/docs/guides/rag-context-setup.md +459 -0
  17. package/docs/guides/self-optimization-pattern.md +82 -0
  18. package/docs/guides/slack-setup.md +350 -0
  19. package/docs/guides/twilio-subaccounts-setup.md +223 -0
  20. package/docs/guides/voice-sms-setup.md +698 -0
  21. package/docs/guides/webhook-relay-setup.md +349 -0
  22. package/docs/guides/whatsapp-setup.md +282 -0
  23. package/docs/runbooks/mac-mini-bootstrap.md +21 -0
  24. package/package.json +2 -1
  25. package/plugins/maestro-skills/plugin.json +16 -0
  26. package/plugins/maestro-skills/skills/agents-observe.md +110 -0
  27. package/plugins/maestro-skills/skills/ccxray-diagnostics.md +91 -0
  28. package/plugins/maestro-skills/skills/claude-pace.md +61 -0
  29. package/plugins/maestro-skills/skills/code-review-graph.md +99 -0
  30. package/scaffold/CLAUDE.md +64 -0
  31. package/scaffold/config/agent.ts.example +2 -1
  32. package/scaffold/config/caller-id-map.yaml +46 -0
  33. package/scaffold/config/known-agents.json +35 -0
  34. package/scripts/daemon/classifier.mjs +264 -50
  35. package/scripts/daemon/dispatcher.mjs +109 -5
  36. package/scripts/daemon/launchd-wrapper-generic.sh +96 -0
  37. package/scripts/daemon/launchd-wrapper-slack-events.sh +37 -0
  38. package/scripts/daemon/launchd-wrapper.sh +91 -0
  39. package/scripts/daemon/lib/session-router.mjs +274 -0
  40. package/scripts/daemon/lib/session-router.test.mjs +295 -0
  41. package/scripts/daemon/prompt-builder.mjs +51 -11
  42. package/scripts/daemon/responder.mjs +234 -19
  43. package/scripts/daemon/session-lock.mjs +194 -0
  44. package/scripts/daemon/sophie-daemon.mjs +16 -2
  45. package/scripts/email-signature.html +20 -4
  46. package/scripts/local-triggers/generate-plists.sh +62 -10
  47. package/scripts/media-generation/README.md +2 -0
  48. package/scripts/pdf-generation/README.md +2 -0
  49. package/scripts/poller/imap-client.mjs +4 -2
  50. package/scripts/poller/slack-poller.mjs +126 -59
  51. package/scripts/poller/trigger.mjs +12 -1
  52. package/scripts/setup/init-agent.sh +91 -1
  53. package/scripts/setup/install-dev-tools.sh +150 -0
  54. package/scripts/spawn-session.sh +21 -6
  55. package/workflows/continuous/backlog-executor.yaml +141 -0
  56. package/workflows/daily/evening-wrap.yaml +41 -1
  57. package/workflows/daily/morning-brief.yaml +17 -0
  58. package/workflows/event-driven/agent-failure-investigation.yaml +137 -0
  59. package/workflows/event-driven/pr-review.yaml +104 -0
  60. package/workflows/weekly/engineering-health.yaml +154 -0
@@ -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