@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,348 @@
1
+ # Slack Setup Guide
2
+
3
+ How to enable Slack integration for a Maestro agent: app creation, token configuration, message sending (with typing indicators), file uploads, emoji reactions, event polling, real-time events server, and Slack CDP for huddle automation.
4
+
5
+ **Prerequisites**: Complete the [Mac Mini Bootstrap](../runbooks/mac-mini-bootstrap.md). The agent needs a Slack workspace account.
6
+
7
+ ---
8
+
9
+ ## Architecture Overview
10
+
11
+ ```
12
+ ┌──────────────────────────────────────────────────────────────────────┐
13
+ │ INBOUND │
14
+ │ │
15
+ │ Slack workspace ──▶ slack-poller.mjs ──▶ state/inbox/slack/*.yaml │
16
+ │ (via xoxp- token) (every 60s) (inbox processor routes) │
17
+ │ │
18
+ │ OR (real-time): │
19
+ │ Slack Events API ──▶ slack-events-server.mjs ──▶ inbox / daemon │
20
+ │ (webhook) (Socket Mode or HTTP) │
21
+ ├──────────────────────────────────────────────────────────────────────┤
22
+ │ OUTBOUND │
23
+ │ │
24
+ │ ┌───────────────┐ ┌─────────────────┐ ┌──────────────────┐ │
25
+ │ │ slack-send.sh │ │ slack-upload- │ │ slack-react.mjs │ │
26
+ │ │ (messages) │ │ v2.py (files) │ │ (emoji reactions)│ │
27
+ │ └──────┬────────┘ └────────┬────────┘ └──────────────────┘ │
28
+ │ │ │ │
29
+ │ ┌──────▼────────────────────▼──────────────────────────────────┐ │
30
+ │ │ Pre-send pipeline: │ │
31
+ │ │ 1. validate-outbound.py (factual checks) │ │
32
+ │ │ 2. slack-responded.sh (atomic response dedup) │ │
33
+ │ │ 3. pre_draft_lookup.py (context enrichment) │ │
34
+ │ │ 4. slack-typing.mjs (typing indicator) │ │
35
+ │ │ 5. markdown → mrkdwn (format conversion) │ │
36
+ │ └──────────────────────────────────────────────────────────────┘ │
37
+ │ │ │
38
+ │ ▼ │
39
+ │ Slack Web API (chat.postMessage) via xoxp- User Token │
40
+ └──────────────────────────────────────────────────────────────────────┘
41
+ ```
42
+
43
+ **Critical design decision**: All Slack messages are sent via the **User OAuth Token (xoxp-)**, not the Bot Token (xoxb-). Messages sent with the user token appear as the agent's Slack user account (e.g. "Sophie Nguyen"), not as a bot app. The `block-mcp-slack-send.sh` hook enforces this — MCP Slack sends are blocked because they add a "Sent using @Claude" label.
44
+
45
+ ---
46
+
47
+ ## 1. Slack App Creation
48
+
49
+ ### 1.1 Create a Slack App
50
+
51
+ 1. Go to https://api.slack.com/apps → **Create New App**
52
+ 2. Choose "From scratch"
53
+ 3. Name it (e.g. "Adaptic Agent") and select your workspace
54
+ 4. Note the **App ID** from Basic Information
55
+
56
+ ### 1.2 Configure OAuth Scopes
57
+
58
+ Under **OAuth & Permissions** → **Scopes**, add:
59
+
60
+ **Bot Token Scopes** (xoxb-):
61
+
62
+ | Scope | Purpose |
63
+ |---|---|
64
+ | `chat:write` | Send messages |
65
+ | `channels:read` | List channels |
66
+ | `groups:read` | List private channels |
67
+ | `im:read` | List DM conversations |
68
+ | `users:read` | Look up user info |
69
+ | `files:write` | Upload files |
70
+ | `reactions:write` | Add emoji reactions |
71
+
72
+ **User Token Scopes** (xoxp-):
73
+
74
+ | Scope | Purpose |
75
+ |---|---|
76
+ | `channels:history` | Read channel messages |
77
+ | `groups:history` | Read private channel messages |
78
+ | `im:history` | Read DM messages |
79
+ | `search:read` | Search messages |
80
+ | `chat:write` | Send as user (primary send method) |
81
+ | `files:write` | Upload as user |
82
+ | `reactions:write` | React as user |
83
+
84
+ ### 1.3 Install the App
85
+
86
+ 1. Click **Install to Workspace**
87
+ 2. Authorize the requested permissions
88
+ 3. Copy both tokens:
89
+ - **Bot User OAuth Token** (starts with `xoxb-`)
90
+ - **User OAuth Token** (starts with `xoxp-`)
91
+
92
+ ### 1.4 Configure Environment Variables
93
+
94
+ Add to `.env`:
95
+
96
+ ```bash
97
+ # User token (xoxp-) — used for sending messages as the agent's user account
98
+ SLACK_USER_TOKEN=xoxp-...
99
+
100
+ # Bot token (xoxb-) — used for API calls that don't need user identity
101
+ SLACK_BOT_TOKEN=xoxb-...
102
+
103
+ # Optional: signing secret for Events API webhook verification
104
+ SLACK_SIGNING_SECRET=...
105
+ ```
106
+
107
+ ---
108
+
109
+ ## 2. Sending Messages (`slack-send.sh`)
110
+
111
+ The primary script for all Slack message sending.
112
+
113
+ ### 2.1 Usage
114
+
115
+ ```bash
116
+ # Send to a channel
117
+ ./scripts/slack-send.sh "C1234567890" "Hello from the agent"
118
+
119
+ # Reply in a thread
120
+ ./scripts/slack-send.sh "C1234567890" "Thread reply" --thread_ts "1234567890.123456"
121
+
122
+ # With response dedup (prevents double-replying to the same message)
123
+ ./scripts/slack-send.sh "C1234567890" "Reply" --responding_to "1234567890.123456"
124
+
125
+ # Skip typing indicator
126
+ ./scripts/slack-send.sh "C1234567890" "Quick message" --no-typing
127
+ ```
128
+
129
+ ### 2.2 Pre-Send Pipeline
130
+
131
+ Every Slack send goes through this pipeline (in order):
132
+
133
+ 1. **Response dedup** (`slack-responded.sh`): If `--responding_to` is set, acquires an atomic mkdir-based lock to prevent concurrent sessions from replying to the same message
134
+ 2. **Typing indicator** (`slack-typing.mjs`): Shows "Agent is typing..." for a duration proportional to message length (1.5s–4s)
135
+ 3. **Pre-draft context lookup** (`pre_draft_lookup.py`): Looks up recipient in entity index (advisory, never blocks)
136
+ 4. **Factual validation** (`validate-outbound.py`): Checks for relationship claims, AI disclosure, scheduling errors — **blocks if critical issues found**
137
+ 5. **Markdown → mrkdwn conversion**: Converts standard Markdown (`**bold**`, `[link](url)`, `## heading`) to Slack's mrkdwn format (`*bold*`, `<url|link>`, `*heading*`)
138
+ 6. **Send via `chat.postMessage`**: Uses User OAuth Token
139
+
140
+ ### 2.3 Typing Indicators (`slack-typing.mjs`)
141
+
142
+ Creates a natural feel by showing typing dots before messages arrive:
143
+
144
+ - Duration scales with message length: <50 chars → 1.5s, 50-200 → 2.5s, 200-500 → 3s, >500 → 4s
145
+ - Connects via WebSocket RTM API using the user token
146
+ - Requires `rtm:stream` scope (degrades gracefully if missing — message sends without dots)
147
+ - Disable globally: `SLACK_TYPING_ENABLED=0` in `.env`
148
+
149
+ ### 2.4 Response Deduplication (`slack-responded.sh`)
150
+
151
+ Prevents the agent from sending multiple replies to the same message:
152
+
153
+ - Uses atomic `mkdir` for race-safe locking (POSIX-guaranteed atomic)
154
+ - Lock path: `state/locks/slack-responded/{channel}/{message_ts}/`
155
+ - After send succeeds, records a preview of the sent message for audit
156
+ - Lock TTL handled by `outbound-dedup-cleanup.sh`
157
+
158
+ ---
159
+
160
+ ## 3. File Uploads (`slack-upload-v2.py`)
161
+
162
+ Uploads files to Slack channels or threads:
163
+
164
+ ```bash
165
+ python3 scripts/slack-upload-v2.py --channel "C1234567890" --file /path/to/document.pdf --title "Q2 Board Pack"
166
+
167
+ # Upload to a thread
168
+ python3 scripts/slack-upload-v2.py --channel "C1234567890" --file report.pdf --thread_ts "1234567890.123456"
169
+ ```
170
+
171
+ Uses Slack's v2 file upload API (`files.uploadV2`) which supports larger files and better threading.
172
+
173
+ ---
174
+
175
+ ## 4. Emoji Reactions (`slack-react.mjs`)
176
+
177
+ Adds emoji reactions to messages:
178
+
179
+ ```bash
180
+ node scripts/slack-react.mjs --channel "C1234567890" --timestamp "1234567890.123456" --emoji "white_check_mark"
181
+ ```
182
+
183
+ Used by the agent to acknowledge messages (e.g. ✅ after processing a request).
184
+
185
+ ---
186
+
187
+ ## 5. Event Polling (`slack-poller.mjs`)
188
+
189
+ ### 5.1 How It Works
190
+
191
+ The Slack poller runs as part of the main poller loop (every 60 seconds):
192
+
193
+ 1. Reads recent messages from monitored channels and DMs using `conversations.history`
194
+ 2. Filters for new messages since last poll (tracks last-seen timestamps)
195
+ 3. Writes new messages to `state/inbox/slack/` as YAML files
196
+ 4. Inbox processor classifies and routes each message
197
+
198
+ ### 5.2 Monitored Channels
199
+
200
+ The poller monitors channels configured in the agent's operational setup. Typically:
201
+ - All DM conversations
202
+ - Channels where the agent is mentioned
203
+ - Priority channels (CEO DM, team channels)
204
+
205
+ ### 5.3 Priority Detection
206
+
207
+ CEO DMs and messages containing urgent keywords trigger immediate processing rather than waiting for the next inbox processor cycle.
208
+
209
+ ---
210
+
211
+ ## 6. Real-Time Events Server (`slack-events-server.mjs`)
212
+
213
+ ### 6.1 Overview
214
+
215
+ For faster event detection than polling, the events server receives Slack events in real-time:
216
+
217
+ ```bash
218
+ node scripts/slack-events-server.mjs
219
+ ```
220
+
221
+ ### 6.2 Control Script
222
+
223
+ ```bash
224
+ # Start the events server
225
+ ./scripts/slack-events-ctl.sh start
226
+
227
+ # Stop it
228
+ ./scripts/slack-events-ctl.sh stop
229
+
230
+ # Check status
231
+ ./scripts/slack-events-ctl.sh status
232
+ ```
233
+
234
+ ### 6.3 Configuration
235
+
236
+ Requires the Slack app to have **Event Subscriptions** enabled:
237
+
238
+ 1. Go to Slack App settings → Event Subscriptions → Enable Events
239
+ 2. Request URL: `https://your-tunnel-url/slack/events`
240
+ 3. Subscribe to events: `message.channels`, `message.groups`, `message.im`, `app_mention`
241
+ 4. The events server verifies requests using `SLACK_SIGNING_SECRET`
242
+
243
+ ---
244
+
245
+ ## 7. MCP Slack Send Block
246
+
247
+ The `block-mcp-slack-send.sh` hook prevents sending via MCP Slack:
248
+
249
+ ```
250
+ BLOCKED: Per CEO directive si-010 — do NOT use MCP Slack for sending.
251
+ Use scripts/slack-send.sh with SLACK_USER_TOKEN instead.
252
+ ```
253
+
254
+ **Why**: MCP Slack sends add a "Sent using @Claude" label to messages, breaking the agent's identity. All sends must go through `slack-send.sh` using the User OAuth Token so messages appear as the agent's Slack user account.
255
+
256
+ This hook is registered in `.claude/settings.json` as a `PreToolUse` hook matching MCP Slack send tools.
257
+
258
+ ---
259
+
260
+ ## 8. Slack CDP (Chrome DevTools Protocol)
261
+
262
+ For advanced Slack UI automation (huddle participation, keyboard shortcuts), the Slack desktop app is launched with CDP enabled:
263
+
264
+ ```bash
265
+ ./scripts/huddle/launch-slack.sh
266
+ # or manually:
267
+ /Applications/Slack.app/Contents/MacOS/Slack --remote-debugging-port=9222
268
+ ```
269
+
270
+ See [Voice & SMS Setup](voice-sms-setup.md) for the full huddle voice infrastructure that builds on Slack CDP.
271
+
272
+ ---
273
+
274
+ ## 9. Testing
275
+
276
+ | # | Test | How to Verify |
277
+ |---|---|---|
278
+ | 1 | Token validation | `curl -s -H "Authorization: Bearer $SLACK_USER_TOKEN" https://slack.com/api/auth.test \| jq .` |
279
+ | 2 | Simple send | `./scripts/slack-send.sh "C_TEST_CHANNEL" "Test message"` |
280
+ | 3 | Thread reply | Send with `--thread_ts` and verify it threads in Slack |
281
+ | 4 | Typing indicator | Watch Slack while sending — "Agent is typing..." should appear |
282
+ | 5 | Response dedup | Send same `--responding_to` twice; second should `DEDUP_SKIP` |
283
+ | 6 | File upload | `python3 scripts/slack-upload-v2.py --channel C_TEST --file test.txt` |
284
+ | 7 | Emoji reaction | `node scripts/slack-react.mjs --channel C_TEST --timestamp TS --emoji thumbsup` |
285
+ | 8 | MCP block | Attempt MCP Slack send — should be blocked by hook |
286
+ | 9 | Poller | Send a DM to the agent; check `state/inbox/slack/` |
287
+ | 10 | Markdown conversion | Send message with `**bold**` and `[link](url)` — verify Slack renders correctly |
288
+
289
+ ---
290
+
291
+ ## 10. Troubleshooting
292
+
293
+ ### "invalid_auth" or "token_revoked" errors
294
+
295
+ 1. Regenerate tokens: Slack App → OAuth & Permissions → Reinstall to Workspace
296
+ 2. Verify token type: user sends need `xoxp-` token, not `xoxb-`
297
+ 3. Update `.env` with new tokens
298
+
299
+ ### Messages appearing as bot (not user)
300
+
301
+ 1. Verify `SLACK_USER_TOKEN` starts with `xoxp-` (not `xoxb-`)
302
+ 2. Check `slack-send.sh` is using `SLACK_USER_TOKEN` (line 12)
303
+ 3. Verify `block-mcp-slack-send.sh` hook is active in `.claude/settings.json`
304
+
305
+ ### Typing indicator not showing
306
+
307
+ 1. Check `rtm:stream` scope on user token (not available on all Slack plans)
308
+ 2. Verify `SLACK_TYPING_ENABLED` is not set to `0`
309
+ 3. Check `slack-typing.mjs` can connect: `node scripts/slack-typing.mjs C_TEST 2000`
310
+ 4. Degrades gracefully — messages still send without typing dots
311
+
312
+ ### Poller missing messages
313
+
314
+ 1. Verify channel access: agent's Slack account must be a member of monitored channels
315
+ 2. Check `conversations.history` scope is granted
316
+ 3. Check last-poll timestamp in poller state isn't in the future (clock sync issue)
317
+
318
+ ### Response dedup false positives
319
+
320
+ 1. Check lock directory: `ls state/locks/slack-responded/CHANNEL/`
321
+ 2. Clean stale locks: `./scripts/outbound-dedup-cleanup.sh`
322
+ 3. Locks should auto-expire — check TTL in `outbound-dedup.sh`
323
+
324
+ ---
325
+
326
+ ## Key Files
327
+
328
+ | File | Purpose |
329
+ |---|---|
330
+ | `scripts/slack-send.sh` | Primary message sender (user token, typing, dedup, markdown) |
331
+ | `scripts/slack-typing.mjs` | WebSocket RTM typing indicator |
332
+ | `scripts/slack-react.mjs` | Add emoji reactions to messages |
333
+ | `scripts/slack-upload-v2.py` | File upload (v2 API) |
334
+ | `scripts/slack-responded.sh` | Atomic response deduplication |
335
+ | `scripts/slack-events-server.mjs` | Real-time Slack events receiver |
336
+ | `scripts/slack-events-ctl.sh` | Events server control (start/stop/status) |
337
+ | `scripts/poll-slack-events.sh` | Legacy event polling script |
338
+ | `scripts/hooks/block-mcp-slack-send.sh` | Blocks MCP Slack sends (enforces user token) |
339
+ | `scripts/poller/slack-poller.mjs` | Slack channel/DM polling |
340
+
341
+ ---
342
+
343
+ ## Related Documents
344
+
345
+ - [Voice & SMS Setup](voice-sms-setup.md) — Slack huddle voice via CDP
346
+ - [Outbound Governance Setup](outbound-governance-setup.md) — Dedup and validation pipeline
347
+ - [Poller & Daemon Setup](poller-daemon-setup.md) — How Slack polling integrates with the event loop
348
+ - [Communications Policy](../governance/communications-policy.md) — Voice modes and approval rules for Slack