@pinet/slack-bridge 0.2.1 → 0.2.4
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 +302 -448
- package/dist/broker/adapters/slack.d.ts +11 -0
- package/dist/broker/adapters/slack.js +90 -2
- package/dist/broker/client.d.ts +3 -1
- package/dist/broker/client.js +11 -0
- package/dist/broker/socket-server.d.ts +2 -0
- package/dist/broker/socket-server.js +89 -2
- package/dist/helpers.d.ts +17 -0
- package/dist/helpers.js +12 -1
- package/dist/index.js +3 -1
- package/dist/pinet-mesh-ops.d.ts +6 -1
- package/dist/pinet-mesh-ops.js +20 -0
- package/dist/pinet-session-formatting.d.ts +13 -0
- package/dist/pinet-session-formatting.js +80 -0
- package/dist/pinet-tools.d.ts +4 -1
- package/dist/pinet-tools.js +148 -3
- package/dist/slack-markdown.d.ts +1 -0
- package/dist/slack-markdown.js +128 -0
- package/dist/slack-pinet-runtime-adapter.js +7 -0
- package/dist/slack-tools.d.ts +8 -0
- package/dist/slack-tools.js +26 -49
- package/dist/subtree-broker-runtime.d.ts +3 -0
- package/dist/subtree-broker-runtime.js +3 -0
- package/package.json +5 -5
package/README.md
CHANGED
|
@@ -1,157 +1,185 @@
|
|
|
1
1
|
# slack-bridge (Pinet)
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Connect pi coding agents to Slack. Pinet provides multi-agent coordination, thread routing, and inbox tools through Socket Mode.
|
|
4
4
|
|
|
5
|
-
## Install
|
|
5
|
+
## Install Pinet
|
|
6
6
|
|
|
7
|
-
Install the latest
|
|
7
|
+
Install the latest version:
|
|
8
8
|
|
|
9
9
|
```bash
|
|
10
10
|
pi install npm:@pinet/slack-bridge
|
|
11
11
|
```
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
Pin a specific version:
|
|
14
14
|
|
|
15
15
|
```bash
|
|
16
|
-
pi install npm:@pinet/slack-bridge@0.
|
|
16
|
+
pi install npm:@pinet/slack-bridge@0.2.2
|
|
17
17
|
```
|
|
18
18
|
|
|
19
|
-
For
|
|
19
|
+
For direct npm installation:
|
|
20
20
|
|
|
21
21
|
```bash
|
|
22
22
|
npm install @pinet/slack-bridge
|
|
23
23
|
```
|
|
24
24
|
|
|
25
|
-
##
|
|
25
|
+
## What you need
|
|
26
26
|
|
|
27
|
-
|
|
27
|
+
- a Slack workspace where you can install apps
|
|
28
|
+
- Node.js 22 or later
|
|
29
|
+
- pi installed on your system
|
|
28
30
|
|
|
29
|
-
|
|
30
|
-
- `pi.extensions` points at the built extension entrypoint, `./dist/index.js`.
|
|
31
|
-
- `pi.skills` points at the bundled skill directory, `./skills`.
|
|
32
|
-
- No `pi.image` or `pi.video` preview is declared yet because this package does
|
|
33
|
-
not currently include a reviewed gallery image/video asset.
|
|
31
|
+
## Set up your Slack app
|
|
34
32
|
|
|
35
|
-
|
|
36
|
-
Slack app manifest, built `dist/` files, bundled `skills/`, and LICENSE. Verify
|
|
37
|
-
that locally with:
|
|
33
|
+
### Create the app
|
|
38
34
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
workflow's default dry-run/readiness path for validation; do not publish, tag, or
|
|
47
|
-
bump versions without explicit maintainer release approval.
|
|
48
|
-
|
|
49
|
-
## Prerequisites
|
|
50
|
-
|
|
51
|
-
- A Slack workspace where you have permission to install apps
|
|
52
|
-
- Node.js 22+ (uses native `fetch` and `WebSocket`)
|
|
53
|
-
- [pi](https://github.com/badlogic/pi-mono) installed
|
|
54
|
-
|
|
55
|
-
## Slack App Setup
|
|
35
|
+
1. Go to [api.slack.com/apps](https://api.slack.com/apps)
|
|
36
|
+
2. Select 'Create New App'
|
|
37
|
+
3. Choose 'From a manifest'
|
|
38
|
+
4. Select your workspace
|
|
39
|
+
5. Paste the contents of [`manifest.yaml`](./manifest.yaml)
|
|
40
|
+
6. If you want a different Slack command, change `features.slash_commands[0].command` before creating and set `slackCommandName` or `slackCommandNames` in `settings.json`
|
|
41
|
+
7. Select 'Create'
|
|
56
42
|
|
|
57
|
-
|
|
43
|
+
The manifest configures Socket Mode, the assistant view, bot scopes, event subscriptions, and slash commands automatically.
|
|
58
44
|
|
|
59
|
-
|
|
60
|
-
2. Choose **From a manifest**
|
|
61
|
-
3. Select your workspace
|
|
62
|
-
4. Paste the contents of [`manifest.yaml`](./manifest.yaml) from this directory
|
|
63
|
-
5. If the Slack app is not named Pinet, change `features.slash_commands[0].command` before creating the app (for example, Oathgate uses `/oathgate` instead of the packaged `/pinet` default)
|
|
64
|
-
6. Click **Create**
|
|
45
|
+
### Get your tokens
|
|
65
46
|
|
|
66
|
-
|
|
47
|
+
Generate two tokens:
|
|
67
48
|
|
|
68
|
-
|
|
49
|
+
| Token | Where to find it | Format |
|
|
50
|
+
| --------------- | ------------------------------------------------------------------------------- | ------------ |
|
|
51
|
+
| App-Level Token | Basic Information → App-Level Tokens → Generate (add `connections:write` scope) | `xapp-1-...` |
|
|
52
|
+
| Bot Token | OAuth & Permissions → Install to Workspace → Bot User OAuth Token | `xoxb-...` |
|
|
69
53
|
|
|
70
|
-
|
|
54
|
+
### Required bot scopes
|
|
71
55
|
|
|
72
|
-
|
|
73
|
-
| ------------------- | -------------------------------------------------------------------------------- | ------------ |
|
|
74
|
-
| **App-Level Token** | Basic Information → App-Level Tokens → Generate (with `connections:write` scope) | `xapp-1-...` |
|
|
75
|
-
| **Bot Token** | OAuth & Permissions → Install to Workspace → Bot User OAuth Token | `xoxb-...` |
|
|
76
|
-
|
|
77
|
-
### 3. Required bot scopes
|
|
78
|
-
|
|
79
|
-
These are included in the manifest, but for reference:
|
|
56
|
+
The manifest includes these scopes:
|
|
80
57
|
|
|
81
58
|
```
|
|
82
59
|
app_mentions:read assistant:write bookmarks:read
|
|
83
60
|
bookmarks:write canvases:read canvases:write
|
|
84
61
|
channels:history channels:read chat:write
|
|
85
|
-
commands files:read files:write
|
|
86
|
-
groups:read im:history
|
|
87
|
-
im:write pins:read
|
|
88
|
-
reactions:read reactions:write
|
|
62
|
+
commands files:read files:write
|
|
63
|
+
groups:history groups:read im:history
|
|
64
|
+
im:read im:write pins:read
|
|
65
|
+
pins:write reactions:read reactions:write
|
|
66
|
+
users:read
|
|
89
67
|
```
|
|
90
68
|
|
|
91
|
-
`commands`
|
|
69
|
+
The `commands` scope enables slash commands. The `files:read` scope is needed because Slack uses `files.info` for canvas comment pagination.
|
|
92
70
|
|
|
93
|
-
|
|
71
|
+
## Configure Pinet
|
|
94
72
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
Add your tokens to `~/.pi/agent/settings.json`:
|
|
73
|
+
Add tokens, runtime mode, and access rules to `~/.pi/agent/settings.json`:
|
|
98
74
|
|
|
99
75
|
```json
|
|
100
76
|
{
|
|
101
77
|
"slack-bridge": {
|
|
102
78
|
"botToken": "xoxb-your-bot-token",
|
|
103
|
-
"appToken": "xapp-your-app-token"
|
|
79
|
+
"appToken": "xapp-your-app-token",
|
|
80
|
+
"runtimeMode": "single",
|
|
81
|
+
"allowedUsers": ["U_YOUR_USER_ID"]
|
|
104
82
|
}
|
|
105
83
|
}
|
|
106
84
|
```
|
|
107
85
|
|
|
108
|
-
|
|
86
|
+
Pinet stays off unless you set `runtimeMode`, `autoConnect`, or `autoFollow`. Start pi after you configure access.
|
|
109
87
|
|
|
110
|
-
###
|
|
88
|
+
### Use environment variables instead
|
|
111
89
|
|
|
112
90
|
```bash
|
|
113
91
|
export SLACK_BOT_TOKEN="xoxb-..."
|
|
114
92
|
export SLACK_APP_TOKEN="xapp-..."
|
|
115
93
|
```
|
|
116
94
|
|
|
117
|
-
Settings in `settings.json`
|
|
95
|
+
Settings in `settings.json` override environment variables.
|
|
118
96
|
|
|
119
|
-
|
|
97
|
+
## Control who can use Pinet
|
|
120
98
|
|
|
121
|
-
|
|
99
|
+
Slack access is default-deny. Configure one of these:
|
|
100
|
+
|
|
101
|
+
- `allowedUsers`: list specific Slack user IDs
|
|
102
|
+
- `allowAllWorkspaceUsers: true`: allow everyone in the workspace
|
|
103
|
+
|
|
104
|
+
Example with specific users:
|
|
122
105
|
|
|
123
106
|
```json
|
|
124
107
|
{
|
|
125
108
|
"slack-bridge": {
|
|
126
|
-
"
|
|
109
|
+
"botToken": "xoxb-...",
|
|
110
|
+
"appToken": "xapp-...",
|
|
111
|
+
"allowedUsers": ["U_USER_ID_1", "U_USER_ID_2"]
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
Find user IDs by selecting a user's profile in Slack and choosing 'Copy member ID'.
|
|
117
|
+
|
|
118
|
+
## Optional settings
|
|
119
|
+
|
|
120
|
+
### Mesh authentication
|
|
121
|
+
|
|
122
|
+
Shared-secret authentication is optional. Configure it with settings or environment variables:
|
|
123
|
+
|
|
124
|
+
Settings:
|
|
125
|
+
|
|
126
|
+
```json
|
|
127
|
+
{
|
|
128
|
+
"slack-bridge": {
|
|
129
|
+
"meshSecret": "your-shared-secret"
|
|
127
130
|
}
|
|
128
131
|
}
|
|
129
132
|
```
|
|
130
133
|
|
|
134
|
+
Or use a file:
|
|
135
|
+
|
|
131
136
|
```json
|
|
132
137
|
{
|
|
133
138
|
"slack-bridge": {
|
|
134
|
-
"meshSecretPath": "/
|
|
139
|
+
"meshSecretPath": "/path/to/secret.txt"
|
|
135
140
|
}
|
|
136
141
|
}
|
|
137
142
|
```
|
|
138
143
|
|
|
144
|
+
Environment variables:
|
|
145
|
+
|
|
139
146
|
```bash
|
|
140
|
-
export PINET_MESH_SECRET="shared-secret"
|
|
147
|
+
export PINET_MESH_SECRET="your-shared-secret"
|
|
141
148
|
# or
|
|
142
|
-
export PINET_MESH_SECRET_PATH="
|
|
149
|
+
export PINET_MESH_SECRET_PATH="/path/to/secret.txt"
|
|
143
150
|
```
|
|
144
151
|
|
|
145
|
-
|
|
152
|
+
How it works:
|
|
153
|
+
|
|
154
|
+
- settings override environment variables
|
|
155
|
+
- inline secrets override file paths
|
|
156
|
+
- if nothing is set, mesh auth is disabled
|
|
157
|
+
- brokers create the secret file if it does not exist
|
|
158
|
+
- followers need an existing file or will show an error
|
|
146
159
|
|
|
147
|
-
|
|
148
|
-
- Inline secrets win over secret paths. If `meshSecret` or `PINET_MESH_SECRET` is set, the corresponding `*Path` value is ignored.
|
|
149
|
-
- If all four values are unset, broker/follower mesh auth is disabled.
|
|
150
|
-
- A broker started with `meshSecretPath` creates the secret file if it does not exist yet.
|
|
151
|
-
- A follower started with `meshSecretPath` does **not** create the file. If the configured file is missing, follow fails with a clear error telling you to point at an existing file, provide `meshSecret` directly, or leave both unset to disable shared-secret auth.
|
|
152
|
-
- A follower configured for mesh auth will fail closed against an older/no-auth broker with a clear compatibility error. It will **not** silently retry as an unauthenticated follower.
|
|
160
|
+
### Require mentions in channels
|
|
153
161
|
|
|
154
|
-
|
|
162
|
+
Make Pinet respond only when mentioned in specific channels:
|
|
163
|
+
|
|
164
|
+
```json
|
|
165
|
+
{
|
|
166
|
+
"slack-bridge": {
|
|
167
|
+
"ingressGuard": {
|
|
168
|
+
"requireMention": {
|
|
169
|
+
"channels": ["C_CHANNEL_ID"],
|
|
170
|
+
"mixedParticipantThreads": {
|
|
171
|
+
"enabled": true,
|
|
172
|
+
"trustedUsers": ["U_TRUSTED_USER"]
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
This is separate from `allowedUsers`. Authorization decides who can use Pinet. The guard decides when a mention is needed.
|
|
181
|
+
|
|
182
|
+
### All configuration options
|
|
155
183
|
|
|
156
184
|
```json
|
|
157
185
|
{
|
|
@@ -159,16 +187,32 @@ Behavior and precedence:
|
|
|
159
187
|
"botToken": "xoxb-...",
|
|
160
188
|
"appToken": "xapp-...",
|
|
161
189
|
"runtimeMode": "single",
|
|
162
|
-
"allowedUsers": ["
|
|
163
|
-
"
|
|
190
|
+
"allowedUsers": ["U_USER_ID"],
|
|
191
|
+
"allowAllWorkspaceUsers": false,
|
|
192
|
+
"ingressGuard": {
|
|
193
|
+
"requireMention": {
|
|
194
|
+
"channels": ["C_CHANNEL_ID"],
|
|
195
|
+
"mixedParticipantThreads": {
|
|
196
|
+
"enabled": true,
|
|
197
|
+
"trustedUsers": ["U_USER_ID"]
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
},
|
|
201
|
+
"defaultChannel": "C_CHANNEL_ID",
|
|
164
202
|
"logChannel": "#pinet-logs",
|
|
165
203
|
"logLevel": "actions",
|
|
166
|
-
"
|
|
204
|
+
"autoConnect": false,
|
|
205
|
+
"autoFollow": false,
|
|
167
206
|
"ralphLoopIntervalMs": 300000,
|
|
168
207
|
"ralphSnoozeAfterEmptyCycles": 0,
|
|
169
208
|
"ralphSnoozeDurationMs": 1800000,
|
|
170
|
-
"meshSecretPath": "/
|
|
171
|
-
"suggestedPrompts": [
|
|
209
|
+
"meshSecretPath": "/path/to/secret",
|
|
210
|
+
"suggestedPrompts": [
|
|
211
|
+
{
|
|
212
|
+
"title": "Status",
|
|
213
|
+
"message": "What are you working on?"
|
|
214
|
+
}
|
|
215
|
+
],
|
|
172
216
|
"security": {
|
|
173
217
|
"readOnly": false,
|
|
174
218
|
"requireConfirmation": ["slack:create_channel"],
|
|
@@ -178,445 +222,255 @@ Behavior and precedence:
|
|
|
178
222
|
}
|
|
179
223
|
```
|
|
180
224
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
|
187
|
-
|
|
|
188
|
-
| `
|
|
189
|
-
| `
|
|
190
|
-
| `
|
|
191
|
-
| `
|
|
192
|
-
| `
|
|
193
|
-
| `
|
|
194
|
-
| `
|
|
195
|
-
| `
|
|
196
|
-
| `autoConnect` | no | Legacy compatibility alias for `runtimeMode: "single"` |
|
|
197
|
-
| `autoFollow` | no | Legacy compatibility alias for follower startup when a broker socket exists |
|
|
198
|
-
| `ralphLoopIntervalMs` | no | Broker RALPH maintenance cadence in milliseconds; defaults to `300000` (5 minutes), valid range `1000`-`2147483647` |
|
|
199
|
-
| `ralphSnoozeAfterEmptyCycles` | no | Broker RALPH auto-snooze trigger after N empty cycles; defaults to `0` (disabled), valid range `0`-`100` |
|
|
200
|
-
| `ralphSnoozeDurationMs` | no | Broker RALPH auto-snooze duration in milliseconds; defaults to `1800000` (30 minutes), valid range `60000`-`86400000` |
|
|
201
|
-
| `skinTheme` | no | Pinet presentation skin selected at broker startup/reload (`default`, `foundation`, `cosmere`, or free-form) |
|
|
202
|
-
| `slackCommandName` | no | Slack web app slash command name for `agents list`; defaults to `/pinet`, or `/oathgate` for Oathgate/Cosmere skins |
|
|
203
|
-
| `slackCommandNames` | no | Optional list of accepted/deployed Slack slash command aliases when one app needs multiple command names |
|
|
204
|
-
| `meshSecret` | no | Optional inline Pinet shared secret; overrides `meshSecretPath` and env fallbacks |
|
|
205
|
-
| `meshSecretPath` | no | Optional path to a shared-secret file; broker creates it if missing, followers require an existing file |
|
|
206
|
-
| `suggestedPrompts` | no | Prompts shown when a user opens a new conversation |
|
|
207
|
-
| `security.readOnly` | no | Runtime-block write-capable tools for Slack-triggered turns, including core tools like `bash`, `edit`, and `write` |
|
|
208
|
-
| `security.requireConfirmation` | no | Runtime-require Slack approval before matching tools execute; core tools need a specific Slack thread context |
|
|
209
|
-
| `security.blockedTools` | no | Runtime-block matching tools for Slack-triggered turns, including core tools |
|
|
210
|
-
|
|
211
|
-
## Scope carrier model (compatibility-first)
|
|
212
|
-
|
|
213
|
-
Slack/Pinet now threads a first-class runtime `scope` carrier through shared message contracts and runtime metadata.
|
|
214
|
-
|
|
215
|
-
For this first slice:
|
|
216
|
-
|
|
217
|
-
- **workspace/install scope** is carried as compatibility-first metadata for Slack
|
|
218
|
-
- **instance scope** is also carried as a first-class compatibility carrier
|
|
219
|
-
- today’s single-workspace deployments use one default compatibility scope
|
|
220
|
-
- a missing or empty Slack `teamId` stays **unknown** — the bridge does not invent a fake workspace ID
|
|
221
|
-
- these carriers are metadata only in this slice; enforcement and multi-install behavior land later in `#547` / `#550`
|
|
222
|
-
|
|
223
|
-
## Usage
|
|
224
|
-
|
|
225
|
-
Once configured, Pinet appears in Slack's sidebar. Users open it, type a message, and the pi agent responds.
|
|
225
|
+
| Setting | Description | Default |
|
|
226
|
+
| ------------------------ | ------------------------------------------------------ | -------------------- |
|
|
227
|
+
| `botToken` | Bot User OAuth Token (required) | none |
|
|
228
|
+
| `appToken` | App-Level Token for Socket Mode (required) | none |
|
|
229
|
+
| `runtimeMode` | How Pinet runs (`off`, `single`, `broker`, `follower`) | `off` |
|
|
230
|
+
| `allowedUsers` | Slack user IDs who can use Pinet | none |
|
|
231
|
+
| `allowAllWorkspaceUsers` | Allow all workspace members | `false` |
|
|
232
|
+
| `defaultChannel` | Where to post updates | none |
|
|
233
|
+
| `logChannel` | Where to post logs | none |
|
|
234
|
+
| `logLevel` | What to log (`errors`, `actions`, `verbose`) | `actions` |
|
|
235
|
+
| `autoConnect` | Start as a single instance when `runtimeMode` is unset | `false` |
|
|
236
|
+
| `autoFollow` | Start as follower if broker exists | `false` |
|
|
237
|
+
| `ralphLoopIntervalMs` | How often to check for stalls (milliseconds) | `300000` (5 minutes) |
|
|
238
|
+
| `meshSecret` | Shared secret for mesh auth | none |
|
|
239
|
+
| `meshSecretPath` | File containing shared secret | none |
|
|
226
240
|
|
|
227
|
-
|
|
228
|
-
User opens Pinet in Slack sidebar
|
|
229
|
-
└─► types a message
|
|
230
|
-
└─► 👀 reaction appears (thinking)
|
|
231
|
-
└─► message queued for pi agent
|
|
232
|
-
└─► agent responds via slack_send
|
|
233
|
-
└─► 👀 removed, reply appears in thread
|
|
234
|
-
```
|
|
241
|
+
## Using Pinet
|
|
235
242
|
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
### Reaction triggers
|
|
239
|
-
|
|
240
|
-
Slack emoji reactions are ignored by default: they do not enqueue Pinet work, trigger reviews, steer agents, interrupt owners, or cause broker/worker replies. To opt in deliberately, configure `reactionCommands` for the exact emoji aliases that should become structured Pinet requests from the reacted-to Slack message. Even configured reactions are accepted only inside an already authorized Pinet thread (for example a thread with a current Pinet owner, or persisted Slack assistant-thread context). Reaction authorization is deny-by-default: it requires an explicit broker-backed authorization gate, and a thread the adapter has merely seen or cached never qualifies on its own. Reactions in ordinary, uninvoked Slack channel threads remain no-op — even from authorized users — and they do not enqueue work, persist thread state, claim ownership, or receive a Slack ACK. Messages and interactive events from users outside the allowlist also never mint known-thread state that could later admit reactions or replies. Pinet adds ✅ only when it accepts an opt-in reaction-triggered request. If it cannot process an accepted opted-in reaction, it adds ❌; check broker logs for the underlying Slack/API error. When Slack cannot return the reacted message text, Pinet can still route configured reactions when the message timestamp itself identifies an already authorized thread; otherwise it ignores the reaction safely.
|
|
241
|
-
|
|
242
|
-
### Available tools
|
|
243
|
-
|
|
244
|
-
Slack-bridge uses progressive disclosure to keep the per-turn tool surface
|
|
245
|
-
small:
|
|
246
|
-
|
|
247
|
-
| Tool | Description |
|
|
248
|
-
| ------------- | --------------------------------------------------------------------------- |
|
|
249
|
-
| `slack_inbox` | Hot-path inbox drain for pending incoming Slack messages |
|
|
250
|
-
| `slack_send` | Hot-path reply tool for Slack assistant threads |
|
|
251
|
-
| `slack` | Dispatcher for all non-hot Slack actions; call `action: "help"` for schemas |
|
|
252
|
-
|
|
253
|
-
Cold Slack actions live behind the `slack` dispatcher:
|
|
254
|
-
|
|
255
|
-
| Dispatcher action | Description |
|
|
256
|
-
| ---------------------- | --------------------------------------------------------------------------------- |
|
|
257
|
-
| `react` | Add an emoji reaction to a message |
|
|
258
|
-
| `read` | Read messages from a thread |
|
|
259
|
-
| `upload` | Upload files, snippets, or diffs into Slack |
|
|
260
|
-
| `file` | Download Slack-hosted files to a controlled local temp cache by file ID |
|
|
261
|
-
| `schedule` | Schedule a message for later delivery |
|
|
262
|
-
| `post_channel` | Post to a channel (by name or ID) |
|
|
263
|
-
| `delete` | Delete a bot-posted message or an entire thread |
|
|
264
|
-
| `read_channel` | Read channel history or a thread in a channel |
|
|
265
|
-
| `create_channel` | Create a new Slack channel |
|
|
266
|
-
| `project_create` | Create a project channel + RFC canvas + bot invite in one call |
|
|
267
|
-
| `pin` | Pin or unpin a message |
|
|
268
|
-
| `bookmark` | Add, list, or remove channel bookmarks |
|
|
269
|
-
| `export` | Export a thread as markdown, plain text, or JSON |
|
|
270
|
-
| `presence` | Check if users are active, away, or in DND |
|
|
271
|
-
| `canvas_comments_read` | Read comments attached to a verified canvas by canvas ID or channel canvas lookup |
|
|
272
|
-
| `canvas_create` | Create a standalone or channel canvas |
|
|
273
|
-
| `canvas_update` | Append, prepend, or replace canvas content |
|
|
274
|
-
| `modal_open` | Open a modal from a trigger interaction |
|
|
275
|
-
| `modal_push` | Push a new step onto a modal stack |
|
|
276
|
-
| `modal_update` | Update an existing open modal |
|
|
277
|
-
| `confirm_action` | Request user confirmation before a dangerous action |
|
|
278
|
-
|
|
279
|
-
Use `slack` with `action: "help"` for the action catalogue, or
|
|
280
|
-
`action: "help", args: { "topic": "canvas_update" }` for a specific JSON
|
|
281
|
-
schema and example invocations. Dispatcher responses use a consistent
|
|
282
|
-
`{ "status", "data", "errors", "warnings" }` envelope. Guardrails match
|
|
283
|
-
cold Slack actions as `slack:<action>` (for example `slack:upload` or
|
|
284
|
-
`slack:canvas_update`); legacy `slack_<action>` patterns are accepted during
|
|
285
|
-
migration.
|
|
286
|
-
|
|
287
|
-
#### Tool and workflow usage notes
|
|
288
|
-
|
|
289
|
-
- **Reply where the work arrived.** Use `slack_send` for assistant-thread
|
|
290
|
-
replies. If a task was delivered in a Slack thread, acknowledge briefly,
|
|
291
|
-
do the work, report blockers immediately, and finish with the outcome. If
|
|
292
|
-
you know only a channel/thread pair, use dispatcher action `post_channel`
|
|
293
|
-
with `channel` and optional `thread_ts` instead.
|
|
294
|
-
- **Channel posting is explicit.** `post_channel` posts to a named channel or
|
|
295
|
-
channel ID. When `channel` is omitted, it first resolves a provided
|
|
296
|
-
`thread_ts` to a tracked thread channel, then falls back to `defaultChannel`
|
|
297
|
-
from settings. `slack_send` is intentionally narrower and resolves the
|
|
298
|
-
current tracked assistant thread/DM context.
|
|
299
|
-
- **Rich messages use Block Kit JSON.** Pass `blocks` directly to
|
|
300
|
-
`slack_send` or `post_channel`; keep `text` as the notification/fallback.
|
|
301
|
-
Block Kit builder tools are not registered by this package. Load the bundled
|
|
302
|
-
`slack-bridge` skill for copyable status-report, button, code, and diff
|
|
303
|
-
templates. The package also bundles `pinet-skin-creator` for safely drafting
|
|
304
|
-
or reviewing curated Pinet skin descriptors and character/status-vocabulary
|
|
305
|
-
pools before changing runtime skin wiring.
|
|
306
|
-
- **Modal helpers are patterns, not hot tools.** Use dispatcher actions
|
|
307
|
-
`modal_open`, `modal_push`, and `modal_update` with Slack view JSON. Open or
|
|
308
|
-
push immediately after receiving a fresh `trigger_id`; Slack trigger IDs
|
|
309
|
-
expire quickly. Include `thread_ts` when submissions should route back to an
|
|
310
|
-
original assistant thread.
|
|
311
|
-
- **Uploads are for bulky artifacts.** Use `upload` for logs, screenshots,
|
|
312
|
-
long diffs, and generated files instead of large inline messages. Inline
|
|
313
|
-
uploads require `filename`; path uploads are guarded and must stay within the
|
|
314
|
-
current working directory or system temp directory. `slack_send` also accepts
|
|
315
|
-
`files: [{ path, filename?, title?, filetype? }]` so one assistant-thread
|
|
316
|
-
reply can contain both text and local binary attachments in the same Slack
|
|
317
|
-
file upload message. Slack external file uploads cannot include Block Kit in
|
|
318
|
-
that same message, so omit `blocks` when sending files or send a separate
|
|
319
|
-
block-only reply.
|
|
320
|
-
- **Inbound Slack files are fetched explicitly.** Incoming file-share messages
|
|
321
|
-
preserve safe `slackFiles` metadata such as file ID, name, type, size, and
|
|
322
|
-
permalink, but private Slack download URLs are not exposed in normal tool
|
|
323
|
-
output. To inspect raw content, call dispatcher action `file` with
|
|
324
|
-
`op: "download"`, `file_id`, and optionally `thread_ts`, `message_ts`, and
|
|
325
|
-
`channel`. The bot fetches the file with Slack bot auth, stores it under the
|
|
326
|
-
system temp `pi-slack-files` cache with best-effort TTL cleanup, and returns a
|
|
327
|
-
descriptor containing the local path, filename, type, size, SHA-256, expiry,
|
|
328
|
-
and residual privacy risks.
|
|
329
|
-
- **Upload host egress note.** The second upload leg goes to Slack file upload
|
|
330
|
-
hosts (`files.slack.com`/`uploads.slack.com`) for the raw payload. In
|
|
331
|
-
environments with restricted egress this can fail with `403` (proxy
|
|
332
|
-
allowlist) or DNS errors after `files.getUploadURLExternal`; verify the proxy
|
|
333
|
-
allowlist first, or route through an environment that can reach those hosts.
|
|
334
|
-
- **Upload metadata note.** Slack snippet uploads attempt to use inferred
|
|
335
|
-
`snippet_type` values for inline content and retry with plain upload metadata
|
|
336
|
-
when Slack returns `invalid_arguments`, preserving syntax highlighting for
|
|
337
|
-
supported types while avoiding hard failures on unsupported snippet types.
|
|
338
|
-
- **Canvases are long-lived docs.** `canvas_create` creates standalone or
|
|
339
|
-
channel canvases. If Slack rejects channel tab creation with
|
|
340
|
-
`canvas_tab_creation_failed`, it falls back to a standalone canvas attached to
|
|
341
|
-
the channel, attempts to bookmark the canvas URL, and returns the fallback
|
|
342
|
-
`canvas_id` for future `canvas_update` calls. `canvas_update` can append,
|
|
343
|
-
prepend, replace the whole canvas, or replace a matched section;
|
|
344
|
-
`canvas_comments_read` is read-only and limited to verified canvas targets.
|
|
345
|
-
- **Scheduling, pins, and bookmarks are durable affordances.** Use `schedule`
|
|
346
|
-
for delayed reminders instead of waiting; use `pin` for important thread
|
|
347
|
-
messages; use `bookmark` for persistent channel-header links to repos,
|
|
348
|
-
dashboards, docs, or runbooks.
|
|
349
|
-
- **Presence helps choose timing.** Use `presence` before pinging humans when
|
|
350
|
-
active/away/DND status affects routing or whether to schedule a follow-up.
|
|
351
|
-
- **Destructive actions stay constrained.** `delete` can remove only messages
|
|
352
|
-
posted by the current bot and every delete call requires `confirm: true`.
|
|
353
|
-
Whole-thread deletion additionally requires `thread: true` and succeeds only
|
|
354
|
-
when every message in the target thread belongs to the current bot. Prefer
|
|
355
|
-
asking for explicit approval before destructive cleanup.
|
|
356
|
-
- **Confirm guarded actions in the same thread.** If guardrails require
|
|
357
|
-
confirmation, call `confirm_action` with the target `thread_ts`, exact tool
|
|
358
|
-
name, and the exact action string required by the guarded tool. The safest
|
|
359
|
-
flow is: attempt the guarded call, copy the `requires confirmation for action
|
|
360
|
-
...` string from the error, request confirmation, wait for the user's approval
|
|
361
|
-
via `slack_inbox`, then retry the guarded call unchanged. Batched
|
|
362
|
-
multi-thread Slack turns cannot satisfy a single-thread confirmation.
|
|
363
|
-
- **Plain emoji reactions are not tasks.** Slack emoji reactions are ignored
|
|
364
|
-
unless `reactionCommands` explicitly opts that emoji into structured
|
|
365
|
-
reaction-trigger handling and the reacted message belongs to an already
|
|
366
|
-
authorized Pinet thread. If an opt-in reaction-triggered request or a Block
|
|
367
|
-
Kit/modal interaction payload arrives through `slack_inbox` with metadata,
|
|
368
|
-
treat it as a user instruction tied to the referenced Slack thread or
|
|
369
|
-
message.
|
|
370
|
-
|
|
371
|
-
#### Common dispatcher examples
|
|
372
|
-
|
|
373
|
-
Reply in the current Slack assistant thread with Block Kit:
|
|
243
|
+
### In Slack
|
|
374
244
|
|
|
375
|
-
|
|
376
|
-
{
|
|
377
|
-
"text": "Deploy complete — branch main, checks passed.",
|
|
378
|
-
"blocks": [
|
|
379
|
-
{
|
|
380
|
-
"type": "section",
|
|
381
|
-
"fields": [
|
|
382
|
-
{ "type": "mrkdwn", "text": "*Branch*\n`main`" },
|
|
383
|
-
{ "type": "mrkdwn", "text": "*Checks*\n✅ lint/typecheck/test" }
|
|
384
|
-
]
|
|
385
|
-
}
|
|
386
|
-
]
|
|
387
|
-
}
|
|
388
|
-
```
|
|
245
|
+
Talk to Pinet:
|
|
389
246
|
|
|
390
|
-
|
|
247
|
+
- Direct message: open a DM with Pinet
|
|
248
|
+
- In channels: mention `@pinet` (or your app name)
|
|
249
|
+
- Slack slash command: type `/pinet agents list` or `/pinet agents list all`
|
|
391
250
|
|
|
392
|
-
|
|
393
|
-
{
|
|
394
|
-
"action": "post_channel",
|
|
395
|
-
"args": {
|
|
396
|
-
"channel": "#pinet-logs",
|
|
397
|
-
"thread_ts": "1712345678.000100",
|
|
398
|
-
"text": "PR #123 is ready for review."
|
|
399
|
-
}
|
|
400
|
-
}
|
|
401
|
-
```
|
|
251
|
+
### Pi commands
|
|
402
252
|
|
|
403
|
-
|
|
253
|
+
Run these inside pi.
|
|
404
254
|
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
}
|
|
415
|
-
}
|
|
416
|
-
```
|
|
255
|
+
Main commands:
|
|
256
|
+
|
|
257
|
+
- `/pinet` - show available commands
|
|
258
|
+
- `/pinet status` - show current Pinet status
|
|
259
|
+
- `/pinet logs` - show recent broker activity logs
|
|
260
|
+
- `/pinet rename [name]` - rename this agent
|
|
261
|
+
- `/pinet free` - mark this agent idle
|
|
262
|
+
|
|
263
|
+
Coordinator commands:
|
|
417
264
|
|
|
418
|
-
|
|
419
|
-
|
|
265
|
+
- `/pinet start` or `/pinet broker` - become the broker
|
|
266
|
+
- `/pinet follow` - become a follower
|
|
267
|
+
- `/pinet unfollow` - disconnect from broker
|
|
268
|
+
- `/pinet reload <agent>` - ask another agent to reload
|
|
269
|
+
- `/pinet exit <agent>` - ask another agent to exit
|
|
270
|
+
- `/pinet snooze [duration|off|status]` - quiet empty RALPH cycles
|
|
271
|
+
- `/pinet subtree [start|status|spawn|stop]` - manage subtree broker mode
|
|
272
|
+
|
|
273
|
+
### From pi
|
|
274
|
+
|
|
275
|
+
Use the Pinet dispatcher for agent coordination:
|
|
420
276
|
|
|
421
277
|
```json
|
|
422
278
|
{
|
|
423
|
-
"action": "
|
|
279
|
+
"action": "send",
|
|
424
280
|
"args": {
|
|
425
|
-
"
|
|
426
|
-
"
|
|
427
|
-
"action": "channel=#pinet-logs | thread_ts=1712345678.000100 | ts=1712345678.000200 | thread=false"
|
|
281
|
+
"to": "@worker",
|
|
282
|
+
"message": "Please review PR #123"
|
|
428
283
|
}
|
|
429
284
|
}
|
|
430
285
|
```
|
|
431
286
|
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
The `canvas_comments_read` dispatcher action is intentionally narrow:
|
|
435
|
-
|
|
436
|
-
- it validates the target with `canvases.sections.lookup` before reading comment pages via `files.info`
|
|
437
|
-
- it needs `files:read` because Slack exposes canvas comments through the file API surface
|
|
438
|
-
- it will **not** inspect generic Slack files, non-canvas file comments, or full canvas body/history
|
|
287
|
+
Common actions:
|
|
439
288
|
|
|
440
|
-
|
|
289
|
+
- `send` - send a message to an agent or broker-only channel
|
|
290
|
+
- `read` - read this agent's inbox
|
|
291
|
+
- `schedule` - schedule a future wake-up
|
|
292
|
+
- `free` - mark this agent idle
|
|
293
|
+
- `help` - discover actions and schemas
|
|
441
294
|
|
|
442
|
-
|
|
443
|
-
| -------------------------- | ---------------------------------------------------------- |
|
|
444
|
-
| `/pinet <action>` | Unified Pinet command surface; run `/pinet help` for usage |
|
|
445
|
-
| `/pinet status` | Show connection status, threads, and agent identity |
|
|
446
|
-
| `/pinet rename` | Change the agent's display name |
|
|
447
|
-
| `/pinet logs` | Show recent broker activity log entries |
|
|
448
|
-
| `/<app> agents list [all]` | Slack-native broker roster, workload, task, and lane view |
|
|
295
|
+
Use `slack_send` for hot-path Slack replies. Use the `slack` dispatcher for uploads, canvases, pins, bookmarks, and other Slack actions.
|
|
449
296
|
|
|
450
|
-
##
|
|
297
|
+
## Architecture
|
|
451
298
|
|
|
452
|
-
|
|
299
|
+
### Broker and followers
|
|
453
300
|
|
|
454
|
-
|
|
455
|
-
| ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
456
|
-
| `off` | Slack bridge is loaded, but **no Slack Socket Mode ingress** and no coordination runtime are started. |
|
|
457
|
-
| `single` | One local Pi session owns Slack ingress and local thread/inbox ownership only. No broker DB/socket/client, no RALPH/control plane, no mesh auth, no multi-agent surface. |
|
|
458
|
-
| `broker` | The session runs the broker coordination runtime. |
|
|
459
|
-
| `follower` | The session connects to an existing broker as a worker runtime. |
|
|
301
|
+
Pinet can run as:
|
|
460
302
|
|
|
461
|
-
|
|
303
|
+
- single - one instance handles everything
|
|
304
|
+
- broker - coordinates and routes messages
|
|
305
|
+
- follower - receives work from the broker
|
|
462
306
|
|
|
463
|
-
|
|
464
|
-
- `autoConnect` is a legacy compatibility alias for `runtimeMode: "single"`.
|
|
465
|
-
- `autoFollow` is a legacy compatibility alias for `runtimeMode: "follower"` when a broker socket is available.
|
|
466
|
-
- explicit `runtimeMode` wins over the legacy flags.
|
|
467
|
-
- `/pinet start` and `/pinet follow` still switch the live session into broker/follower runtimes explicitly.
|
|
307
|
+
The broker:
|
|
468
308
|
|
|
469
|
-
|
|
309
|
+
- watches Slack for messages
|
|
310
|
+
- assigns work to agents
|
|
311
|
+
- tracks who owns what
|
|
312
|
+
- syncs state across followers
|
|
470
313
|
|
|
471
|
-
|
|
314
|
+
Followers:
|
|
472
315
|
|
|
473
|
-
-
|
|
474
|
-
-
|
|
475
|
-
-
|
|
476
|
-
- if Slack omits `team_id`, the carrier keeps the workspace id **unknown** instead of inventing a fake one
|
|
477
|
-
- this slice is metadata/plumbing only: it does **not** change routing, enforcement, or multi-install orchestration yet
|
|
316
|
+
- connect to the broker
|
|
317
|
+
- receive assigned work
|
|
318
|
+
- stay in sync automatically
|
|
478
319
|
|
|
479
|
-
|
|
320
|
+
### RALPH maintenance loop
|
|
480
321
|
|
|
481
|
-
|
|
322
|
+
RALPH keeps broker state healthy. It:
|
|
482
323
|
|
|
483
|
-
|
|
324
|
+
- runs every 5 minutes by default
|
|
325
|
+
- checks worker presence
|
|
326
|
+
- releases stale claims held by unavailable workers
|
|
327
|
+
- observes pending backlog while broker maintenance handles assignment
|
|
328
|
+
- triggers wake-ups
|
|
484
329
|
|
|
485
|
-
|
|
330
|
+
Configure RALPH:
|
|
486
331
|
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
332
|
+
```json
|
|
333
|
+
{
|
|
334
|
+
"slack-bridge": {
|
|
335
|
+
"ralphLoopIntervalMs": 120000,
|
|
336
|
+
"ralphSnoozeAfterEmptyCycles": 3,
|
|
337
|
+
"ralphSnoozeDurationMs": 1800000
|
|
338
|
+
}
|
|
339
|
+
}
|
|
493
340
|
```
|
|
494
341
|
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
```
|
|
498
|
-
/pinet follow
|
|
499
|
-
```
|
|
342
|
+
### Inbox and threading
|
|
500
343
|
|
|
501
|
-
|
|
344
|
+
Pinet maintains an inbox for each agent. Messages are:
|
|
502
345
|
|
|
503
|
-
|
|
346
|
+
- routed based on thread ownership
|
|
347
|
+
- queued when agents are busy
|
|
348
|
+
- marked read when processed
|
|
349
|
+
- preserved across restarts
|
|
504
350
|
|
|
505
|
-
|
|
351
|
+
Thread ownership ensures continuity. Once an agent owns a thread, it keeps receiving those messages.
|
|
506
352
|
|
|
507
|
-
|
|
508
|
-
2. user-local override: `~/.pi/agent/slack-bridge/tmux.md`
|
|
509
|
-
3. packaged default: `dist/prompts/broker/tmux.md`
|
|
353
|
+
## Troubleshooting
|
|
510
354
|
|
|
511
|
-
|
|
355
|
+
### Socket Mode connection issues
|
|
512
356
|
|
|
513
|
-
|
|
357
|
+
If you see 'WebSocket error' or connection failures:
|
|
514
358
|
|
|
515
|
-
|
|
359
|
+
1. Check your app token is valid
|
|
360
|
+
2. Verify Socket Mode is enabled in your Slack app
|
|
361
|
+
3. Check network connectivity
|
|
362
|
+
4. Look for rate limiting (Slack allows 10 connections per app)
|
|
516
363
|
|
|
517
|
-
###
|
|
364
|
+
### Permission errors
|
|
518
365
|
|
|
519
|
-
|
|
520
|
-
| ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
521
|
-
| `pinet` | Pinet dispatcher with token-efficient `action`-based routing (`help`, `send`, `read`, `free`, `snooze`, `schedule`, `agents`, `lanes`, `ports`, `spawn`, `reload`, `exit`) |
|
|
366
|
+
If Pinet cannot perform actions:
|
|
522
367
|
|
|
523
|
-
|
|
368
|
+
1. Check the bot is in the channel (invite with `/invite @pinet`)
|
|
369
|
+
2. Verify bot scopes match the manifest
|
|
370
|
+
3. Reinstall the app to update permissions
|
|
371
|
+
4. Check `allowedUsers` includes the right user IDs
|
|
524
372
|
|
|
525
|
-
|
|
373
|
+
### Messages not received
|
|
526
374
|
|
|
527
|
-
|
|
375
|
+
If Pinet does not respond:
|
|
528
376
|
|
|
529
|
-
|
|
377
|
+
1. Check Socket Mode shows 'Connected' in Slack app settings
|
|
378
|
+
2. Verify event subscriptions are enabled
|
|
379
|
+
3. Check `allowedUsers` or `allowAllWorkspaceUsers`
|
|
380
|
+
4. Look in the log channel for errors
|
|
381
|
+
5. Try `/pinet status` to check if Pinet is running
|
|
530
382
|
|
|
531
|
-
|
|
383
|
+
### Stalled agents
|
|
532
384
|
|
|
533
|
-
|
|
385
|
+
If work gets stuck:
|
|
534
386
|
|
|
535
|
-
|
|
387
|
+
1. Check `/pinet status` for current state.
|
|
388
|
+
2. Check `/pinet logs` for repeated failures.
|
|
389
|
+
3. Wait for RALPH to run automatically.
|
|
390
|
+
4. Reduce `ralphLoopIntervalMs` for faster recovery if needed.
|
|
536
391
|
|
|
537
|
-
|
|
392
|
+
## Package information
|
|
538
393
|
|
|
539
|
-
|
|
394
|
+
### Publishing metadata
|
|
540
395
|
|
|
541
|
-
|
|
396
|
+
The package declares pi metadata in [`package.json`](./package.json):
|
|
542
397
|
|
|
543
|
-
|
|
398
|
+
- `keywords` includes `pi-package` for gallery discovery
|
|
399
|
+
- `pi.extensions` points to `./dist/index.js`
|
|
400
|
+
- `pi.skills` points to bundled skills
|
|
401
|
+
- No preview assets yet
|
|
544
402
|
|
|
545
|
-
|
|
403
|
+
Check the package contents:
|
|
546
404
|
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
| `/pinet unfollow` | Disconnect from the broker |
|
|
552
|
-
| `/pinet reload <agent>` | Ask another agent to reload |
|
|
553
|
-
| `/pinet exit <agent>` | Ask another agent to exit |
|
|
554
|
-
| `/pinet free` | Mark this agent as idle |
|
|
555
|
-
| `/pinet snooze [duration/off/status]` | Quiet empty RALPH cycles while preserving human-triggered wake/route behavior |
|
|
556
|
-
| `/pinet subtree [start/status/spawn/stop]` | Run this worker as a local subtree broker for child followers |
|
|
405
|
+
```bash
|
|
406
|
+
cd slack-bridge
|
|
407
|
+
npm pack --dry-run
|
|
408
|
+
```
|
|
557
409
|
|
|
558
|
-
###
|
|
410
|
+
### Development
|
|
559
411
|
|
|
560
|
-
|
|
412
|
+
Build the package:
|
|
561
413
|
|
|
562
|
-
|
|
414
|
+
```bash
|
|
415
|
+
cd slack-bridge
|
|
416
|
+
pnpm build
|
|
417
|
+
```
|
|
563
418
|
|
|
564
|
-
|
|
565
|
-
- `foundation` / `foundation/space` / `space` — JSON descriptor with curated institutional sci-fi characters, full-name aliases, and archive, relay, frontier, and crisis-room flavor.
|
|
566
|
-
- `cosmere` / `cosmere-inspired` / `oathgate` — JSON descriptor with curated/prebaked 1–3 word identities, static emoji, and whimsical Mistborn/Stormlight/Emberdark-inspired agents, spren, artifacts, places, and jokes while avoiding exact third-party character names.
|
|
419
|
+
Run tests:
|
|
567
420
|
|
|
568
|
-
|
|
421
|
+
```bash
|
|
422
|
+
pnpm test
|
|
423
|
+
```
|
|
569
424
|
|
|
570
|
-
|
|
425
|
+
Deploy the Slack manifest:
|
|
571
426
|
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
- Thread ownership is first-responder-wins — the first agent to reply claims the thread
|
|
427
|
+
```bash
|
|
428
|
+
pnpm deploy:slack
|
|
429
|
+
```
|
|
576
430
|
|
|
577
431
|
## Security
|
|
578
432
|
|
|
579
|
-
|
|
580
|
-
- **Tool guardrails**: `security.readOnly`, `security.requireConfirmation`, and `security.blockedTools` are runtime-enforced for Slack-triggered turns, including core tools such as `bash`, `edit`, and `write`
|
|
581
|
-
- **Guardrail posture**: If Slack/Pinet access is enabled for admitted users and `security.readOnly`, `security.blockedTools`, and `security.requireConfirmation` are all effectively empty (`readOnly !== true` and both arrays are absent or empty), the bridge emits a startup/runtime warning and `/pinet status` shows `Guardrails: empty (warn-first posture; behavior unchanged)`. This is visibility-only: it does **not** auto-enable `readOnly`, block startup, or require an acknowledgement flow.
|
|
582
|
-
- **Mesh authentication**: Optional. Configure `meshSecret` or `meshSecretPath` (or `PINET_MESH_SECRET` / `PINET_MESH_SECRET_PATH`) to require a shared secret; leave them unset to disable shared-secret auth. Configured followers fail closed on missing secret files or older/no-auth brokers rather than silently downgrading.
|
|
583
|
-
|
|
584
|
-
Find Slack user IDs: click a user's profile → **More** → **Copy member ID**.
|
|
433
|
+
### Default-deny access
|
|
585
434
|
|
|
586
|
-
|
|
435
|
+
Pinet requires explicit configuration to allow users. Without `allowedUsers` or `allowAllWorkspaceUsers`, nobody can use it.
|
|
587
436
|
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
### Build
|
|
591
|
-
|
|
592
|
-
```bash
|
|
593
|
-
pnpm run build
|
|
594
|
-
```
|
|
437
|
+
### Token safety
|
|
595
438
|
|
|
596
|
-
|
|
439
|
+
- Never commit tokens to git
|
|
440
|
+
- Use environment variables in production
|
|
441
|
+
- Rotate tokens regularly
|
|
442
|
+
- Use separate apps for development and production
|
|
597
443
|
|
|
598
|
-
|
|
599
|
-
pnpm lint
|
|
600
|
-
pnpm typecheck
|
|
601
|
-
pnpm test
|
|
602
|
-
```
|
|
444
|
+
### Confirmation for dangerous actions
|
|
603
445
|
|
|
604
|
-
|
|
446
|
+
Configure confirmation for sensitive operations:
|
|
605
447
|
|
|
606
|
-
```
|
|
607
|
-
|
|
448
|
+
```json
|
|
449
|
+
{
|
|
450
|
+
"slack-bridge": {
|
|
451
|
+
"security": {
|
|
452
|
+
"requireConfirmation": ["slack:create_channel", "slack:upload", "slack:delete"]
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
}
|
|
608
456
|
```
|
|
609
457
|
|
|
610
|
-
|
|
458
|
+
### Read-only mode
|
|
611
459
|
|
|
612
|
-
|
|
460
|
+
Prevent all modifications:
|
|
613
461
|
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
-
|
|
617
|
-
|
|
618
|
-
|
|
462
|
+
```json
|
|
463
|
+
{
|
|
464
|
+
"slack-bridge": {
|
|
465
|
+
"security": {
|
|
466
|
+
"readOnly": true
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
```
|
|
619
471
|
|
|
620
|
-
##
|
|
472
|
+
## Support
|
|
621
473
|
|
|
622
|
-
|
|
474
|
+
- [GitHub repository](https://github.com/gugu91/extensions)
|
|
475
|
+
- [Architecture documentation](../plans/)
|
|
476
|
+
- Check the log channel in Slack for runtime issues
|