docs(agent-team): slack app is now workspace-level A0BCC7TTU66 (org app deleted) (#27)
The org-owned app A0BC7AT8NUD (created via the org config token) connected its Socket Mode socket but received ZERO workspace Events API events, so the clarifier never heard answers. Root cause: Socket Mode event delivery only works for WORKSPACE-LEVEL apps. Recreated from the dashboard scoped to the Sea Haven Industries workspace -> A0BCC7TTU66 (bot @agentteam2); events now deliver and the live human gate works end to end. Old org app deleted. Updates the slack/ README: new app id/bot, a hard-lesson callout (must be workspace-level, never org-owned), the real-envelope answer-matching note, and a corrected dashboard setup/update flow.
This commit is contained in:
parent
e85a56148e
commit
b6a12af477
1 changed files with 45 additions and 34 deletions
|
|
@ -7,55 +7,66 @@ that the nightly secrev sweep uses, to isolate the two-way bot's trust surface.
|
|||
| Field | Value |
|
||||
|---|---|
|
||||
| App name | Sea Haven agent-team |
|
||||
| App ID | `A0BC7AT8NUD` |
|
||||
| Org / team | seahaven (`E0A524V806L`) |
|
||||
| App ID | `A0BCC7TTU66` |
|
||||
| Bot | `agent-team` (handle `@agentteam2`) · user id `U0BCFSJ7SUC` |
|
||||
| Scope | **Workspace-level** app, installed to **Sea Haven Industries** (`T0A46CP6QR3`) |
|
||||
| Manifest | [`agent-team-manifest.json`](./agent-team-manifest.json) (source of truth) |
|
||||
| Settings | https://api.slack.com/apps/A0BC7AT8NUD |
|
||||
| Settings | https://api.slack.com/apps/A0BCC7TTU66 |
|
||||
|
||||
> ## ⚠️ Must be a WORKSPACE-LEVEL app (hard lesson, 2026-06-22)
|
||||
> Socket Mode event delivery only works for **workspace-level** apps. An
|
||||
> **org-owned** app (one created via the Enterprise **org/config token** /
|
||||
> `apps.manifest.create`, even when workspace-granted with
|
||||
> `--org-workspace-grant`) **connects the socket but receives ZERO workspace
|
||||
> Events API events** — outbound `chat.postMessage` works, but inbound
|
||||
> `message`/`app_mention` are never delivered, so the clarifier never hears
|
||||
> answers. The first build hit exactly this with the now-deleted org app
|
||||
> `A0BC7AT8NUD`. **Create this app from the dashboard** (api.slack.com/apps →
|
||||
> Create New App → From manifest) and **pick the *workspace* "Sea Haven
|
||||
> Industries" as the dev workspace**, NOT the Enterprise org. Then a normal
|
||||
> *Install to Workspace* works (no org-grant dance). Do **not** recreate it via
|
||||
> the org config token.
|
||||
|
||||
## Why these scopes (verified against the code)
|
||||
|
||||
`agent_team/transport/slack_listener.py` subscribes over Socket Mode to
|
||||
`message`, `app_mention`, and Block Kit `block_actions`; `slack_live.py` posts
|
||||
questions via `chat.postMessage`.
|
||||
questions via `chat.postMessage`. Inbound message/app_mention arrive as the
|
||||
Events API envelope `{"type":"event_callback","event":{...}}` and a free-text
|
||||
thread reply is matched to its question by `thread_ts == channel_ref` (see
|
||||
`find_open_question_by_channel_ref`).
|
||||
|
||||
| Capability | Scope / setting | Why |
|
||||
|---|---|---|
|
||||
| Post clarifier questions | `chat:write` | `slack_live.py` `chat.postMessage` |
|
||||
| Hear thread replies in the channel | `channels:history` / `groups:history` + `message.channels`/`message.groups` events | `@app.event("message")` |
|
||||
| Hear DM replies | `im:history` + `message.im` event | DM answer path |
|
||||
| Hear @mentions | `app_mentions:read` + `app_mention` event | `@app.event("app_mention")` |
|
||||
| Block Kit button/select answers | `interactivity.is_enabled` | `@app.action({})` |
|
||||
| Inbound WebSocket | `socket_mode_enabled` + an app-level token w/ `connections:write` | VPN-only box, no public HTTPS endpoint |
|
||||
| Hear thread replies | `channels:history` / `groups:history` + `message.channels`/`message.groups` | `@app.event("message")` |
|
||||
| Hear DM replies | `im:history` + `message.im` | DM answer path |
|
||||
| Hear @mentions | `app_mentions:read` + `app_mention` | `@app.event("app_mention")` |
|
||||
| Block Kit answers | `interactivity.is_enabled` | `@app.action(...)` |
|
||||
| Inbound WebSocket | `socket_mode_enabled` + app-level token w/ `connections:write` | VPN-only box, no public HTTPS endpoint |
|
||||
|
||||
Inbound auth is NOT scope-based: AUTHZ-01 (`AGENT_TEAM_SLACK_OWNER_IDS`) gates
|
||||
the *sender* and fails closed. Socket membership alone is never authorization.
|
||||
|
||||
## Remaining manual token mints (operator, in browser)
|
||||
## Setup (operator, in browser — secrets never echoed)
|
||||
|
||||
Both produce secrets — paste them straight into `~/secrev.env` on the box
|
||||
(mode 600), never into shell history.
|
||||
|
||||
1. **Bot token (`xoxb-`)** — https://api.slack.com/apps/A0BC7AT8NUD/oauth →
|
||||
*Install to Workspace* → approve → copy the **Bot User OAuth Token** →
|
||||
1. **Create the app** — api.slack.com/apps → *Create New App* → *From an app
|
||||
manifest* → **pick workspace "Sea Haven Industries"** → paste
|
||||
`agent-team-manifest.json`.
|
||||
2. **Install to Workspace** → copy the **Bot User OAuth Token** (`xoxb-`) →
|
||||
`SLACK_BOT_TOKEN`.
|
||||
2. **App-level token (`xapp-`)** — https://api.slack.com/apps/A0BC7AT8NUD/general
|
||||
→ *App-Level Tokens* → *Generate Token and Scopes* → add scope
|
||||
`connections:write` → copy → `SLACK_APP_TOKEN`.
|
||||
3. **Channel** — create/choose the clarifier channel, `/invite @agent-team`,
|
||||
copy its `C0...` id → `SLACK_CHANNEL_ID`.
|
||||
4. **Owner allowlist** — `AGENT_TEAM_SLACK_OWNER_IDS` = Adam's Slack user id
|
||||
(`U0A3SC48T47`), comma-separated if more than one. Gate fails closed if empty.
|
||||
3. **Basic Information → App-Level Tokens → Generate** with scope
|
||||
`connections:write` → `SLACK_APP_TOKEN`.
|
||||
4. **Channel** — `/invite @agentteam2` into the clarifier channel; its `C0…` id
|
||||
→ `SLACK_CHANNEL_ID`.
|
||||
5. **Owner allowlist** — `AGENT_TEAM_SLACK_OWNER_IDS` = Adam's Slack user id
|
||||
(`U0A3SC48T47`), comma-separated if more than one. Fails closed if empty.
|
||||
|
||||
## Reproduce / update the app from the manifest
|
||||
All five land in `~/secrev.env` on the box (mode 600), never shell history.
|
||||
|
||||
```bash
|
||||
TOKEN=$(python3 -c "import json;print(json.load(open(os.path.expanduser('~/.slack/credentials.json')))['E0A524V806L']['token'])")
|
||||
# validate
|
||||
curl -s -X POST https://slack.com/api/apps.manifest.validate \
|
||||
-H "Authorization: Bearer $TOKEN" --data-urlencode "manifest=$(cat agent-team-manifest.json)"
|
||||
# update existing app
|
||||
curl -s -X POST https://slack.com/api/apps.manifest.update \
|
||||
-H "Authorization: Bearer $TOKEN" \
|
||||
--data-urlencode "app_id=A0BC7AT8NUD" \
|
||||
--data-urlencode "manifest=$(cat agent-team-manifest.json)"
|
||||
```
|
||||
## Updating the app from the manifest
|
||||
|
||||
Edit `agent-team-manifest.json`, then in the dashboard (App Manifest tab) paste
|
||||
the updated manifest, or use `apps.manifest.update` with an app **config token
|
||||
scoped to the workspace app** (the org config token can read/manage it as org
|
||||
owner, but keep the app workspace-level — do not re-create it org-owned).
|
||||
|
|
|
|||
Reference in a new issue