@pinet/slack-bridge
v0.2.6
Published
Pi package for Pinet Slack assistant integration — multi-agent broker, thread routing, and inbox tools
Readme
slack-bridge (Pinet)
Connect pi coding agents to Slack. Pinet provides multi-agent coordination, thread routing, and inbox tools through Socket Mode.
Install Pinet
Install the latest version:
pi install npm:@pinet/slack-bridgePin a specific version:
pi install npm:@pinet/[email protected]For direct npm installation:
npm install @pinet/slack-bridgeWhat you need
- a Slack workspace where you can install apps
- Node.js 22 or later
- pi installed on your system
Set up your Slack app
Create the app
- Go to api.slack.com/apps
- Select 'Create New App'
- Choose 'From a manifest'
- Select your workspace
- Paste the contents of
manifest.yaml - If you want a different Slack command, change
features.slash_commands[0].commandbefore creating and setslackCommandNameorslackCommandNamesinsettings.json - Select 'Create'
The manifest configures Socket Mode, the assistant view, bot scopes, event subscriptions, and slash commands automatically.
Get your tokens
Generate two tokens:
| Token | Where to find it | Format |
| --------------- | ------------------------------------------------------------------------------- | ------------ |
| App-Level Token | Basic Information → App-Level Tokens → Generate (add connections:write scope) | xapp-1-... |
| Bot Token | OAuth & Permissions → Install to Workspace → Bot User OAuth Token | xoxb-... |
Required bot scopes
The manifest includes these scopes:
app_mentions:read assistant:write bookmarks:read
bookmarks:write canvases:read canvases:write
channels:history channels:read chat:write
commands files:read files:write
groups:history groups:read im:history
im:read im:write pins:read
pins:write reactions:read reactions:write
users:readThe commands scope enables slash commands. The files:read scope is needed because Slack uses files.info for canvas comment pagination.
Configure Pinet
Add tokens, runtime mode, and access rules to ~/.pi/agent/settings.json:
{
"slack-bridge": {
"botToken": "xoxb-your-bot-token",
"appToken": "xapp-your-app-token",
"runtimeMode": "single",
"allowedUsers": ["U_YOUR_USER_ID"]
}
}Pinet stays off unless you set runtimeMode, autoConnect, or autoFollow. Start pi after you configure access.
Use environment variables instead
export SLACK_BOT_TOKEN="xoxb-..."
export SLACK_APP_TOKEN="xapp-..."Settings in settings.json override environment variables.
Control who can use Pinet
Slack access is default-deny. Configure one of these:
allowedUsers: list specific Slack user IDsallowAllWorkspaceUsers: true: allow everyone in the workspace
Example with specific users:
{
"slack-bridge": {
"botToken": "xoxb-...",
"appToken": "xapp-...",
"allowedUsers": ["U_USER_ID_1", "U_USER_ID_2"]
}
}Find user IDs by selecting a user's profile in Slack and choosing 'Copy member ID'.
Optional settings
Mesh authentication
Shared-secret authentication is optional. Configure it with settings or environment variables:
Settings:
{
"slack-bridge": {
"meshSecret": "your-shared-secret"
}
}Or use a file:
{
"slack-bridge": {
"meshSecretPath": "/path/to/secret.txt"
}
}Environment variables:
export PINET_MESH_SECRET="your-shared-secret"
# or
export PINET_MESH_SECRET_PATH="/path/to/secret.txt"How it works:
- settings override environment variables
- inline secrets override file paths
- if nothing is set, mesh auth is disabled
- brokers create the secret file if it does not exist
- followers need an existing file or will show an error
Require mentions in channels
Make Pinet respond only when mentioned in specific channels:
{
"slack-bridge": {
"ingressGuard": {
"requireMention": {
"channels": ["C_CHANNEL_ID"],
"mixedParticipantThreads": {
"enabled": true,
"trustedUsers": ["U_TRUSTED_USER"]
}
}
}
}
}This is separate from allowedUsers. Authorization decides who can use Pinet. The guard decides when a mention is needed.
All configuration options
{
"slack-bridge": {
"botToken": "xoxb-...",
"appToken": "xapp-...",
"runtimeMode": "single",
"allowedUsers": ["U_USER_ID"],
"allowAllWorkspaceUsers": false,
"ingressGuard": {
"requireMention": {
"channels": ["C_CHANNEL_ID"],
"mixedParticipantThreads": {
"enabled": true,
"trustedUsers": ["U_USER_ID"]
}
}
},
"defaultChannel": "C_CHANNEL_ID",
"logChannel": "#pinet-logs",
"logLevel": "actions",
"autoConnect": false,
"autoFollow": false,
"ralphLoopIntervalMs": 300000,
"ralphSnoozeAfterEmptyCycles": 0,
"ralphSnoozeDurationMs": 1800000,
"meshSecretPath": "/path/to/secret",
"suggestedPrompts": [
{
"title": "Status",
"message": "What are you working on?"
}
],
"security": {
"readOnly": false,
"requireConfirmation": ["slack:create_channel"],
"blockedTools": []
}
}
}| Setting | Description | Default |
| ------------------------ | ------------------------------------------------------ | -------------------- |
| botToken | Bot User OAuth Token (required) | none |
| appToken | App-Level Token for Socket Mode (required) | none |
| runtimeMode | How Pinet runs (off, single, broker, follower) | off |
| allowedUsers | Slack user IDs who can use Pinet | none |
| allowAllWorkspaceUsers | Allow all workspace members | false |
| defaultChannel | Where to post updates | none |
| logChannel | Where to post logs | none |
| logLevel | What to log (errors, actions, verbose) | actions |
| autoConnect | Start as a single instance when runtimeMode is unset | false |
| autoFollow | Start as follower if broker exists | false |
| ralphLoopIntervalMs | How often to check for stalls (milliseconds) | 300000 (5 minutes) |
| meshSecret | Shared secret for mesh auth | none |
| meshSecretPath | File containing shared secret | none |
Using Pinet
In Slack
Talk to Pinet:
- Direct message: open a DM with Pinet
- In channels: mention
@pinet(or your app name) - Slack slash command: type
/pinet agents listor/pinet agents list all
Pi commands
Run these inside pi.
Main commands:
/pinet- show available commands/pinet status- show current Pinet status/pinet logs- show recent broker activity logs/pinet rename [name]- rename this agent/pinet free- mark this agent idle
Coordinator commands:
/pinet startor/pinet broker- become the broker/pinet start replace- take over a stale or stranded broker (graceful shutdown first, then a fenced SIGTERM fallback)/pinet follow- become a follower/pinet unfollow- disconnect from broker/pinet reload <agent>- ask another agent to reload/pinet exit <agent>- ask another agent to exit/pinet snooze [duration|off|status]- quiet empty RALPH cycles/pinet subtree [start|status|spawn|stop]- manage subtree broker mode
From pi
Use the Pinet dispatcher for agent coordination:
{
"action": "send",
"args": {
"to": "@worker",
"message": "Please review PR #123"
}
}Common actions:
send- send a message to an agent or broker-only channelread- read this agent's inboxschedule- schedule a future wake-upfree- mark this agent idlehelp- discover actions and schemas
Use slack_send for hot-path Slack replies. Use the slack dispatcher for uploads, canvases, pins, bookmarks, and other Slack actions.
Architecture
Broker and followers
Pinet can run as:
- single - one instance handles everything
- broker - coordinates and routes messages
- follower - receives work from the broker
The broker:
- watches Slack for messages
- assigns work to agents
- tracks who owns what
- syncs state across followers
Followers:
- connect to the broker
- receive assigned work
- stay in sync automatically
RALPH maintenance loop
RALPH keeps broker state healthy. It:
- runs every 5 minutes by default
- checks worker presence
- releases stale claims held by unavailable workers
- observes pending backlog while broker maintenance handles assignment
- triggers wake-ups
Configure RALPH:
{
"slack-bridge": {
"ralphLoopIntervalMs": 120000,
"ralphSnoozeAfterEmptyCycles": 3,
"ralphSnoozeDurationMs": 1800000
}
}Inbox and threading
Pinet maintains an inbox for each agent. Messages are:
- routed based on thread ownership
- queued when agents are busy
- marked read when processed
- preserved across restarts
Thread ownership ensures continuity. Once an agent owns a thread, it keeps receiving those messages.
Troubleshooting
Socket Mode connection issues
If you see 'WebSocket error' or connection failures:
- Check your app token is valid
- Verify Socket Mode is enabled in your Slack app
- Check network connectivity
- Look for rate limiting (Slack allows 10 connections per app)
Permission errors
If Pinet cannot perform actions:
- Check the bot is in the channel (invite with
/invite @pinet) - Verify bot scopes match the manifest
- Reinstall the app to update permissions
- Check
allowedUsersincludes the right user IDs
Messages not received
If Pinet does not respond:
- Check Socket Mode shows 'Connected' in Slack app settings
- Verify event subscriptions are enabled
- Check
allowedUsersorallowAllWorkspaceUsers - Look in the log channel for errors
- Try
/pinet statusto check if Pinet is running
Stranded broker (lock held, controlling session lost)
If /pinet start reports that another broker is already running but you no
longer have that broker's Pi session (crash, laptop restart, stalled process):
- Run
/pinet statusfrom any session — it reports machine-wide broker lock ownership and socket health even while disconnected. - If the broker is healthy and you just want to participate, run
/pinet follow. - If the broker is stranded (or you need the broker in this session), run
/pinet start replace. It asks the current broker to shut down gracefully over the socket, falls back to a verified SIGTERM against the recorded lock owner, and never escalates to SIGKILL.
Two cases intentionally refuse automatic termination:
- Legacy locks (written by older builds, PID-only): there is no recorded
process start identity, so a SIGTERM could hit an unrelated process that
reused the PID. Inspect the process manually (
ps -p <pid>), terminate it yourself if it is truly the stranded broker, then run/pinet start. - Rejected shutdown: a broker that responds but rejects the shutdown request (usually a mesh secret mismatch) is alive, not stranded. Fix the mesh secret configuration or stop that broker from its own session.
Stalled agents
If work gets stuck:
- Check
/pinet statusfor current state. - Check
/pinet logsfor repeated failures. - Wait for RALPH to run automatically.
- Reduce
ralphLoopIntervalMsfor faster recovery if needed.
Package information
Publishing metadata
The package declares pi metadata in package.json:
keywordsincludespi-packagefor gallery discoverypi.extensionspoints to./dist/index.jspi.skillspoints to bundled skills- No preview assets yet
Check the package contents:
cd slack-bridge
npm pack --dry-runDevelopment
Build the package:
cd slack-bridge
pnpm buildRun tests:
pnpm testDeploy the Slack manifest:
pnpm deploy:slackSecurity
Default-deny access
Pinet requires explicit configuration to allow users. Without allowedUsers or allowAllWorkspaceUsers, nobody can use it.
Token safety
- Never commit tokens to git
- Use environment variables in production
- Rotate tokens regularly
- Use separate apps for development and production
Confirmation for dangerous actions
Configure confirmation for sensitive operations:
{
"slack-bridge": {
"security": {
"requireConfirmation": ["slack:create_channel", "slack:upload", "slack:delete"]
}
}
}Read-only mode
Prevent all modifications:
{
"slack-bridge": {
"security": {
"readOnly": true
}
}
}Support
- GitHub repository
- Architecture documentation
- Check the log channel in Slack for runtime issues
