@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.
@@ -0,0 +1,550 @@
1
+ # Poller, Daemon & Trigger Setup Guide
2
+
3
+ How the agent's autonomous event loop works: the lightweight poller, the reactive daemon, session management, launchd trigger scheduling, and the memory watchdog. This is the nervous system that makes the agent perpetually operational.
4
+
5
+ **Prerequisites**: Complete the [Mac Mini Bootstrap](../runbooks/mac-mini-bootstrap.md) and configure at least one communication channel (Slack, Gmail, etc.).
6
+
7
+ ---
8
+
9
+ ## Architecture Overview
10
+
11
+ The agent has three concurrent execution modes, powered by different subsystems:
12
+
13
+ ```
14
+ ┌─────────────────────────────────────────────────────────────────────┐
15
+ │ MODE 1: REACTIVE — Respond to Events │
16
+ │ │
17
+ │ ┌──────────┐ ┌─────────────┐ ┌────────────────────────────────┐ │
18
+ │ │ launchd │─▶│ Poller │─▶│ state/inbox/{slack,gmail, │ │
19
+ │ │ (60s) │ │ index.mjs │ │ calendar,sms,whatsapp}/*.yaml│ │
20
+ │ └──────────┘ └──────┬──────┘ └─────────────┬──────────────────┘ │
21
+ │ │ │ │
22
+ │ priority item? ┌────▼────────────┐ │
23
+ │ │ │ Inbox Processor │ │
24
+ │ ▼ │ (scheduled │ │
25
+ │ triggerSophie() │ trigger, 5 min) │ │
26
+ │ (immediate session) └─────────────────┘ │
27
+ ├─────────────────────────────────────────────────────────────────────┤
28
+ │ MODE 1b: REACTIVE DAEMON (Alternative to Poller) │
29
+ │ │
30
+ │ ┌────────────────────────────────────────────────────────────┐ │
31
+ │ │ sophie-daemon.mjs (persistent Node.js process) │ │
32
+ │ │ │ │
33
+ │ │ Poll loop (60s): │ │
34
+ │ │ Slack + Gmail + Calendar → classify (Haiku) → dispatch │ │
35
+ │ │ │ │
36
+ │ │ Backlog sweep (2min): │ │
37
+ │ │ Scan queues → pick actionable items → dispatch │ │
38
+ │ │ │ │
39
+ │ │ Health check (1min): │ │
40
+ │ │ Write health dashboard, log metrics │ │
41
+ │ │ │ │
42
+ │ │ Dispatcher: │ │
43
+ │ │ Up to 10 parallel claude --print sessions │ │
44
+ │ └────────────────────────────────────────────────────────────┘ │
45
+ ├─────────────────────────────────────────────────────────────────────┤
46
+ │ MODE 2: SCHEDULED — Run Cadence Workflows │
47
+ │ │
48
+ │ ┌──────────┐ ┌──────────────┐ ┌──────────────────────────┐ │
49
+ │ │ launchd │─▶│ run-trigger │─▶│ claude --print │ │
50
+ │ │ (schedule)│ │ .sh │ │ (non-interactive session)│ │
51
+ │ └──────────┘ └──────────────┘ └──────────────────────────┘ │
52
+ │ │
53
+ │ Triggers: morning-brief, midday-sweep, evening-wrap, │
54
+ │ backlog-executor, inbox-processor, meeting-prep, │
55
+ │ meeting-action-capture, weekly-*, quarterly-* │
56
+ ├─────────────────────────────────────────────────────────────────────┤
57
+ │ MODE 3: PROACTIVE — Execute the Backlog │
58
+ │ │
59
+ │ backlog-executor trigger (every 10 min): │
60
+ │ Read all queues → select top 3-5 items → spawn parallel agents │
61
+ │ → review results → update queues → pick next batch │
62
+ ├─────────────────────────────────────────────────────────────────────┤
63
+ │ SAFETY │
64
+ │ │
65
+ │ memory-watchdog.sh (every 30s): │
66
+ │ Monitor RAM + process count → kill runaways → emergency stop │
67
+ │ │
68
+ │ emergency-stop.sh: │
69
+ │ Creates .emergency-stop file → all triggers abort on check │
70
+ │ │
71
+ │ resume-operations.sh: │
72
+ │ Removes .emergency-stop → triggers resume │
73
+ └─────────────────────────────────────────────────────────────────────┘
74
+ ```
75
+
76
+ ---
77
+
78
+ ## 1. The Poller (`scripts/poller/`)
79
+
80
+ The poller is a lightweight Node.js script that runs every 60 seconds via launchd. It checks all inbound channels and writes new items to the inbox.
81
+
82
+ ### 1.1 Entry Point: `index.mjs`
83
+
84
+ Runs four service pollers in sequence:
85
+
86
+ | Service | Module | What it Polls | Credentials |
87
+ |---|---|---|---|
88
+ | Slack | `slack-poller.mjs` | DMs, channels, mentions | `SLACK_USER_TOKEN` |
89
+ | Gmail | `gmail-poller.mjs` | Agent's inbox (UNSEEN) | `GMAIL_APP_PASSWORD` |
90
+ | CEO Gmail | `mehran-gmail-poller.mjs` | Principal's inbox | `SECONDARY_GMAIL_APP_PASSWORD` |
91
+ | Calendar | `calendar-poller.mjs` | Upcoming events, changes | Google Calendar API |
92
+
93
+ ### 1.2 IMAP Client (`imap-client.mjs`)
94
+
95
+ Shared IMAP connection wrapper used by both Gmail pollers:
96
+ - Manages connection pooling and reconnection
97
+ - Handles IMAP search queries (UNSEEN, date ranges)
98
+ - Parses email headers for threading information
99
+
100
+ ### 1.3 Priority Detection (`utils.mjs`)
101
+
102
+ The `isPriorityItem()` function detects items that need immediate processing:
103
+ - CEO DMs (principal's Slack ID)
104
+ - Messages mentioning the agent by name
105
+ - Urgent keywords (urgent, ASAP, emergency)
106
+ - Messages tagged with priority emoji
107
+
108
+ When a priority item is detected, `triggerSophie()` spawns an immediate Claude session rather than waiting for the inbox processor cycle.
109
+
110
+ ### 1.4 Trigger Module (`trigger.mjs`)
111
+
112
+ Spawns a Claude Code session for priority items:
113
+ - Runs `claude --print --dangerously-skip-permissions` with a targeted prompt
114
+ - The prompt includes the priority item's content and relevant context
115
+ - Session output is logged to `logs/polling/`
116
+
117
+ ### 1.5 Intra-Session Check (`intra-session-check.mjs`)
118
+
119
+ Allows a running Claude session to check for new priority events mid-execution. Called by long-running sessions (like backlog executor) to detect urgent interrupts.
120
+
121
+ ### 1.6 Poller Logs
122
+
123
+ - `logs/polling/YYYY-MM-DD-poller.jsonl` — Per-run summary (items found, errors, duration)
124
+ - Each run logs: timestamp, items per service, errors, whether a priority trigger fired
125
+
126
+ ---
127
+
128
+ ## 2. The Reactive Daemon (`scripts/daemon/`)
129
+
130
+ The daemon is an alternative to the poller that provides faster response times. Instead of running every 60 seconds as a separate process, it's a persistent Node.js process with built-in polling, classification, and dispatching.
131
+
132
+ ### 2.1 Entry Point: `maestro-daemon.mjs`
133
+
134
+ - Agent-name-agnostic wrapper that sets `AGENT_DIR` environment variable
135
+ - Acquires a singleton lock (via `~/maestro/lib/singleton.js`) to prevent duplicate daemons
136
+ - Imports and runs `sophie-daemon.mjs`
137
+
138
+ ### 2.2 Core Daemon: `sophie-daemon.mjs`
139
+
140
+ Three concurrent loops:
141
+
142
+ | Loop | Interval | Purpose |
143
+ |---|---|---|
144
+ | Poll | 60s | Check Slack, Gmail, Calendar for new items |
145
+ | Backlog | 2 min | Sweep queues for actionable items |
146
+ | Health | 1 min | Write health dashboard, log metrics |
147
+
148
+ **Poll loop flow:**
149
+ 1. Run all four service pollers (same as standalone poller)
150
+ 2. For each new item, classify it via `classifier.mjs`
151
+ 3. Check if it's directed at the agent via `isDirectedAtSophie()`
152
+ 4. For items needing response: build prompt → dispatch session
153
+ 5. For quick replies: use `responder.mjs` for immediate response
154
+
155
+ ### 2.3 Classifier (`classifier.mjs`)
156
+
157
+ Classifies incoming items using Claude Haiku (fast, cheap):
158
+
159
+ - **Priority**: critical / high / medium / low
160
+ - **Type**: question, request, notification, FYI, greeting, spam
161
+ - **Directed at agent**: yes / no / unclear
162
+ - **Quick reply**: yes (can respond immediately) / no (needs full session)
163
+
164
+ Classification takes ~0.5-1 second via Haiku API.
165
+
166
+ ### 2.4 Dispatcher (`dispatcher.mjs`)
167
+
168
+ Manages parallel Claude Code sessions:
169
+
170
+ - Up to 10 concurrent sessions (`claude --print`)
171
+ - Tracks active sessions with PIDs and start times
172
+ - `availableSlots()` returns how many sessions can be spawned
173
+ - `canDispatchBacklog()` checks if there's capacity for proactive work
174
+ - `resetActiveSessions()` cleans up orphaned session records
175
+ - Each session gets a targeted prompt from `prompt-builder.mjs`
176
+
177
+ ### 2.5 Prompt Builder (`prompt-builder.mjs`)
178
+
179
+ Constructs context-rich prompts for dispatched sessions:
180
+
181
+ - Includes the classified item (message content, sender, channel)
182
+ - Loads relevant user profile from `memory/profiles/`
183
+ - Includes standing instructions from the user profile
184
+ - Includes relevant queue context if the item references tracked work
185
+ - Adds the session protocol (what to do at session start/end)
186
+
187
+ ### 2.6 Responder (`responder.mjs`)
188
+
189
+ Handles quick replies without spawning a full Claude session:
190
+
191
+ - `isQuickReply()` detects items that can be answered immediately (greetings, acknowledgments)
192
+ - `sendQuickResponse()` sends a fast reply via the appropriate channel
193
+ - `sendHoldingMessage()` sends "Got it, working on this" for complex requests
194
+
195
+ ### 2.7 Session Lock (`session-lock.mjs`)
196
+
197
+ File-based locking to prevent duplicate processing:
198
+
199
+ - `acquireLock(itemId)` — claim exclusive processing of an item
200
+ - `acquireThreadLock(channel, thread)` — claim a thread for response
201
+ - `claimRequest(requestId)` — claim a specific request
202
+ - `updateLock(lockId, status)` — update lock with processing status
203
+ - `scanStaleLocks()` — find and clean locks older than TTL
204
+ - Lock directory: `state/locks/daemon/`
205
+
206
+ ### 2.8 Health Monitor (`health.mjs`)
207
+
208
+ Tracks daemon performance:
209
+
210
+ - `recordPoll(results)` — log poll cycle results
211
+ - `recordClassification(item, classification)` — log classification outcomes
212
+ - `recordSession(sessionInfo)` — log dispatched session metrics
213
+ - `writeHealthDashboard()` — write `state/dashboards/daemon-health.yaml`
214
+
215
+ ### 2.9 Context Compiler (`context-compiler.mjs`)
216
+
217
+ Pre-compiles session context to reduce prompt size:
218
+
219
+ - Reads recent interactions, queue state, and active items
220
+ - Compresses context to fit within token limits
221
+ - Enabled via `DAEMON_CONTEXT_COMPILER=1` in `.env`
222
+
223
+ ---
224
+
225
+ ## 3. Session Management
226
+
227
+ ### 3.1 Spawning Sessions (`spawn-session.sh`)
228
+
229
+ Creates a new Claude Code session:
230
+
231
+ ```bash
232
+ ./scripts/spawn-session.sh "Process this incoming request" --session-id "req-12345"
233
+ ```
234
+
235
+ - Sets `SOPHIE_SESSION_ID` for dedup tracking
236
+ - Checks `.emergency-stop` before proceeding
237
+ - Logs session start to `logs/sessions/`
238
+
239
+ ### 3.2 Session Start (`session-start.sh`)
240
+
241
+ Initialises session state:
242
+
243
+ ```bash
244
+ ./scripts/session-start.sh
245
+ ```
246
+
247
+ Called at the beginning of each Claude session to:
248
+ - Load the executive summary dashboard
249
+ - Check for unprocessed inbox items
250
+ - Load pending decisions from the decision queue
251
+ - Set up the session environment
252
+
253
+ ### 3.3 Emergency Stop (`emergency-stop.sh`)
254
+
255
+ ```bash
256
+ ./scripts/emergency-stop.sh
257
+ ```
258
+
259
+ Creates a `.emergency-stop` file in the repo root. All triggers check for this file before executing and abort if present. Use this to immediately halt all autonomous operations.
260
+
261
+ ### 3.4 Resume Operations (`resume-operations.sh`)
262
+
263
+ ```bash
264
+ ./scripts/resume-operations.sh
265
+ ```
266
+
267
+ Removes the `.emergency-stop` file, allowing triggers to resume.
268
+
269
+ ---
270
+
271
+ ## 4. LaunchD Triggers (`scripts/local-triggers/`)
272
+
273
+ ### 4.1 How Triggers Work
274
+
275
+ Triggers are Markdown prompts in `schedules/triggers/` that launchd runs on a schedule. Each trigger invokes a Claude Code session with the prompt as input.
276
+
277
+ **Execution chain**: launchd → `run-trigger.sh <trigger-name>` → reads `schedules/triggers/<trigger-name>.md` → runs `claude --print --dangerously-skip-permissions <prompt>`
278
+
279
+ ### 4.2 Run a Trigger Manually
280
+
281
+ ```bash
282
+ ./scripts/local-triggers/run-trigger.sh morning-brief
283
+ ```
284
+
285
+ ### 4.3 Generate Plist Files (`generate-plists.sh`)
286
+
287
+ Generates all launchd plist files from the agent's configuration:
288
+
289
+ ```bash
290
+ ./scripts/local-triggers/generate-plists.sh
291
+ ```
292
+
293
+ This script:
294
+ 1. Reads `config/agent.ts` to extract the agent's name
295
+ 2. Generates plists for: daemon, poller, all scheduled triggers, watchdog
296
+ 3. Uses the agent name in plist labels (e.g., `ai.adaptic.sophie.morning-brief`)
297
+ 4. Sets correct working directory, Node.js path, and log paths
298
+ 5. Saves plists to `scripts/local-triggers/plists/`
299
+
300
+ ### 4.4 Install All Triggers (`install-all.sh`)
301
+
302
+ ```bash
303
+ ./scripts/local-triggers/install-all.sh
304
+ ```
305
+
306
+ Copies all generated plists to `~/Library/LaunchAgents/` and loads them.
307
+
308
+ ### 4.5 Standard Trigger Schedule
309
+
310
+ | Trigger | Cadence | Time | Purpose |
311
+ |---|---|---|---|
312
+ | `morning-brief` | Daily | 06:00 | CEO morning brief |
313
+ | `midday-sweep` | Daily | 12:00 | SLA check, loop closure |
314
+ | `evening-wrap` | Daily | 18:00 | End-of-day summary, queue cleanup |
315
+ | `backlog-executor` | Every 10 min | Continuous | Execute top queue items |
316
+ | `inbox-processor` | Every 5 min | Continuous | Classify and route inbox items |
317
+ | `meeting-prep` | Every 15 min | Continuous | Pre-meeting brief generation |
318
+ | `meeting-action-capture` | Every 30 min | Continuous | Extract actions from meetings |
319
+ | `weekly-priorities` | Weekly (Mon) | 09:00 | Priority review |
320
+ | `weekly-execution` | Weekly (Wed) | 09:00 | Execution review |
321
+ | `weekly-strategic-memo` | Weekly (Fri) | 15:00 | Strategic memo to principal |
322
+ | `weekly-hiring` | Weekly (Mon) | 11:00 | Hiring pipeline review |
323
+ | `weekly-engineering-health` | Weekly (Wed) | 08:00 | Engineering health check |
324
+ | `quarterly-self-assessment` | Quarterly | First Mon | Agent self-assessment |
325
+
326
+ Times are in the agent's configured timezone from `config/agent.ts`.
327
+
328
+ ---
329
+
330
+ ## 5. Memory Watchdog (`scripts/watchdog/`)
331
+
332
+ ### 5.1 What It Does
333
+
334
+ The watchdog monitors system resources and prevents runaway Claude processes from consuming all RAM:
335
+
336
+ | Threshold | Level | Action |
337
+ |---|---|---|
338
+ | >60% RAM (Claude processes) | Warning | Log warning |
339
+ | >75% RAM (Claude processes) | Critical | Kill oldest subagent processes |
340
+ | >85% total system memory pressure | Emergency | Emergency stop all operations |
341
+ | >8 concurrent claude processes | Process limit | Kill excess processes |
342
+
343
+ ### 5.2 Configuration
344
+
345
+ Environment variables (with defaults):
346
+
347
+ ```bash
348
+ WATCHDOG_WARN_PERCENT=60 # Warning threshold
349
+ WATCHDOG_CRITICAL_PERCENT=75 # Critical threshold (kill runaways)
350
+ WATCHDOG_EMERGENCY_PERCENT=85 # Emergency threshold (full stop)
351
+ WATCHDOG_MAX_CLAUDE_PROCS=8 # Max concurrent claude processes
352
+ WATCHDOG_MIN_AGE=60 # Minimum age (seconds) before killing a process
353
+ ```
354
+
355
+ ### 5.3 Running the Watchdog
356
+
357
+ ```bash
358
+ # Normal run (takes action if thresholds exceeded)
359
+ ./scripts/watchdog/memory-watchdog.sh
360
+
361
+ # Check status only (no actions)
362
+ ./scripts/watchdog/memory-watchdog.sh --check
363
+
364
+ # Dry run (show what would be killed)
365
+ ./scripts/watchdog/memory-watchdog.sh --dry-run
366
+ ```
367
+
368
+ ### 5.4 Force Reboot
369
+
370
+ For severe cases where the watchdog can't recover:
371
+
372
+ ```bash
373
+ ./scripts/watchdog/force-reboot.sh
374
+ ```
375
+
376
+ Kills all claude processes and restarts the daemon.
377
+
378
+ ### 5.5 Watchdog Plist
379
+
380
+ The watchdog runs every 30 seconds via `ai.maestro.memory-watchdog.plist`. Generated automatically by `generate-plists.sh`.
381
+
382
+ ---
383
+
384
+ ## 6. Health Monitoring
385
+
386
+ ### 6.1 Healthcheck (`healthcheck.sh`)
387
+
388
+ Quick system health verification:
389
+
390
+ ```bash
391
+ ./scripts/healthcheck.sh
392
+ ```
393
+
394
+ Checks:
395
+ - launchd agents are loaded and running
396
+ - Disk space available
397
+ - Memory usage
398
+ - Log directory sizes
399
+ - Last successful trigger runs
400
+ - Daemon process status
401
+
402
+ ### 6.2 System Verify (`system-verify.sh`)
403
+
404
+ Deep configuration verification:
405
+
406
+ ```bash
407
+ ./scripts/system-verify.sh
408
+ ```
409
+
410
+ Validates all config files, environment variables, directory structure, and permissions.
411
+
412
+ ### 6.3 Continuous Monitor (`continuous-monitor.sh`)
413
+
414
+ Long-running monitoring process:
415
+
416
+ ```bash
417
+ ./scripts/continuous-monitor.sh
418
+ ```
419
+
420
+ Watches system metrics over time and logs trends.
421
+
422
+ ### 6.4 Communications Monitor (`comms-monitor.sh`)
423
+
424
+ Monitors communication channel health:
425
+
426
+ ```bash
427
+ ./scripts/comms-monitor.sh
428
+ ```
429
+
430
+ Verifies Slack tokens, Gmail connectivity, Twilio status, and tunnel health.
431
+
432
+ ---
433
+
434
+ ## 7. Choosing: Poller vs Daemon
435
+
436
+ | Aspect | Standalone Poller | Reactive Daemon |
437
+ |---|---|---|
438
+ | Response latency | 60s + inbox processor cycle (~5 min total) | ~2 min (poll + classify + dispatch) |
439
+ | Resource usage | Low (runs briefly every 60s) | Moderate (persistent Node.js process) |
440
+ | Complexity | Simple (one-shot script) | Complex (8 interconnected modules) |
441
+ | Parallel sessions | None (relies on triggers) | Up to 10 concurrent sessions |
442
+ | Built-in classification | No (inbox processor does it) | Yes (Haiku classifier) |
443
+ | Backlog execution | Separate trigger (every 10 min) | Built-in sweep (every 2 min) |
444
+ | Recommended for | Low-volume agents, simple roles | High-volume agents, executive roles |
445
+
446
+ **Default**: Use the daemon for production agents that need fast response times. Use the standalone poller for development/testing or low-volume agents.
447
+
448
+ ---
449
+
450
+ ## 8. Testing
451
+
452
+ | # | Test | How to Verify |
453
+ |---|---|---|
454
+ | 1 | Poller runs | `node scripts/poller/index.mjs` — should poll all services |
455
+ | 2 | Daemon starts | `node scripts/daemon/maestro-daemon.mjs` — should show poll loop starting |
456
+ | 3 | Singleton guard | Start daemon twice — second should exit with "Already running" |
457
+ | 4 | Priority detection | Send CEO DM during poller run — should trigger immediate session |
458
+ | 5 | Trigger execution | `./scripts/local-triggers/run-trigger.sh morning-brief` |
459
+ | 6 | Emergency stop | `./scripts/emergency-stop.sh` → try running a trigger → should abort |
460
+ | 7 | Resume | `./scripts/resume-operations.sh` → trigger should work again |
461
+ | 8 | Watchdog check | `./scripts/watchdog/memory-watchdog.sh --check` |
462
+ | 9 | Plist generation | `./scripts/local-triggers/generate-plists.sh` → check `plists/` directory |
463
+ | 10 | Healthcheck | `./scripts/healthcheck.sh` |
464
+
465
+ ---
466
+
467
+ ## 9. Troubleshooting
468
+
469
+ ### Daemon won't start: "Already running"
470
+
471
+ 1. Check for stale singleton lock: `ls /tmp/maestro-daemon.lock`
472
+ 2. If the previous daemon crashed without releasing the lock, remove it: `rm /tmp/maestro-daemon.lock`
473
+ 3. Check for orphaned node processes: `pgrep -f maestro-daemon`
474
+
475
+ ### Triggers not firing
476
+
477
+ 1. Verify plists are loaded: `launchctl list | grep adaptic`
478
+ 2. Check plist logs: `cat ~/Library/LaunchAgents/com.adaptic.AGENT.*.plist`
479
+ 3. Verify working directory in plist points to the right repo
480
+ 4. Check `.emergency-stop` doesn't exist
481
+ 5. Check trigger prompt file exists: `ls schedules/triggers/morning-brief.md`
482
+
483
+ ### High memory usage
484
+
485
+ 1. Run watchdog: `./scripts/watchdog/memory-watchdog.sh --check`
486
+ 2. Count claude processes: `pgrep -f claude | wc -l` (should be ≤8)
487
+ 3. Kill orphaned processes: `./scripts/watchdog/memory-watchdog.sh` (auto-kills above threshold)
488
+ 4. Reduce `MAX_PARALLEL_SESSIONS` in daemon config if persistent issue
489
+
490
+ ### Poller errors for a specific service
491
+
492
+ 1. Check service credentials in `.env` (e.g., `SLACK_USER_TOKEN` for Slack)
493
+ 2. Check poller logs: `tail logs/polling/$(date +%Y-%m-%d)-poller.jsonl`
494
+ 3. Test the specific poller: import and run `pollSlack()` directly
495
+ 4. Check for rate limiting (especially Slack — 60s interval helps)
496
+
497
+ ### Sessions dispatched but no response sent
498
+
499
+ 1. Check daemon logs: `tail logs/daemon/$(date +%Y-%m-%d)-sessions.jsonl`
500
+ 2. Check session locks: `ls state/locks/daemon/`
501
+ 3. Verify Claude CLI is accessible: `claude --version`
502
+ 4. Check session output in logs for errors
503
+
504
+ ---
505
+
506
+ ## Key Files
507
+
508
+ | File | Purpose |
509
+ |---|---|
510
+ | **Poller** | |
511
+ | `scripts/poller/index.mjs` | Main poller entry point |
512
+ | `scripts/poller/slack-poller.mjs` | Slack event polling |
513
+ | `scripts/poller/gmail-poller.mjs` | Gmail inbox polling |
514
+ | `scripts/poller/calendar-poller.mjs` | Calendar event polling |
515
+ | `scripts/poller/mehran-gmail-poller.mjs` | CEO inbox polling |
516
+ | `scripts/poller/imap-client.mjs` | IMAP connection wrapper |
517
+ | `scripts/poller/trigger.mjs` | Priority item session spawner |
518
+ | `scripts/poller/utils.mjs` | Shared utilities (priority detection) |
519
+ | `scripts/poller/intra-session-check.mjs` | Mid-session event check |
520
+ | **Daemon** | |
521
+ | `scripts/daemon/maestro-daemon.mjs` | Generic daemon entry point |
522
+ | `scripts/daemon/sophie-daemon.mjs` | Core daemon logic (poll + backlog + health) |
523
+ | `scripts/daemon/classifier.mjs` | Haiku-based message classifier |
524
+ | `scripts/daemon/dispatcher.mjs` | Parallel session manager |
525
+ | `scripts/daemon/prompt-builder.mjs` | Context-rich prompt construction |
526
+ | `scripts/daemon/responder.mjs` | Quick reply and holding messages |
527
+ | `scripts/daemon/session-lock.mjs` | File-based dedup locking |
528
+ | `scripts/daemon/health.mjs` | Health monitoring and dashboard |
529
+ | `scripts/daemon/context-compiler.mjs` | Session context pre-compilation |
530
+ | **Triggers** | |
531
+ | `scripts/local-triggers/run-trigger.sh` | Execute a scheduled trigger |
532
+ | `scripts/local-triggers/generate-plists.sh` | Generate launchd plist files |
533
+ | `scripts/local-triggers/install-all.sh` | Install all plists to launchd |
534
+ | **Session** | |
535
+ | `scripts/spawn-session.sh` | Spawn a new Claude Code session |
536
+ | `scripts/session-start.sh` | Session initialisation |
537
+ | `scripts/emergency-stop.sh` | Emergency halt all operations |
538
+ | `scripts/resume-operations.sh` | Resume after emergency stop |
539
+ | **Watchdog** | |
540
+ | `scripts/watchdog/memory-watchdog.sh` | Resource monitoring and process management |
541
+ | `scripts/watchdog/force-reboot.sh` | Kill all claude processes and restart |
542
+
543
+ ---
544
+
545
+ ## Related Documents
546
+
547
+ - [Mac Mini Bootstrap](../runbooks/mac-mini-bootstrap.md) — Initial machine and launchd setup
548
+ - [Perpetual Operations](../runbooks/perpetual-operations.md) — How the system runs 24/7
549
+ - [Recovery and Failover](../runbooks/recovery-and-failover.md) — What to do when things break
550
+ - [Agent Persona Setup](agent-persona-setup.md) — Trigger schedule configuration