/slack-a2a-rooms
Agent-to-agent Slack rooms — a group DM (MPIM) holding a human plus two or more NanoClaw sibling bots, where each bot hears the room over its own Socket Mode connection. Registers the admission policy on the Slack channel's bot-inbound guard so bot-authored inbound is admitted
Claude Code
$ npx -y skills add nanocoai/nanoclaw --skill slack-a2a-rooms --agent claude-code
Codex
$ npx skills add nanocoai/nanoclaw --skill slack-a2a-rooms -a codex -y
Cursor
$ npx skills add nanocoai/nanoclaw --skill slack-a2a-rooms -a cursor -y
Gemini CLI
$ npx skills add nanocoai/nanoclaw --skill slack-a2a-rooms -a gemini-cli -y
GitHub Copilot
$ npx skills add nanocoai/nanoclaw --skill slack-a2a-rooms -a github-copilot -y
OpenCode
$ npx skills add nanocoai/nanoclaw --skill slack-a2a-rooms -a opencode -y
Windsurf
$ npx skills add nanocoai/nanoclaw --skill slack-a2a-rooms -a windsurf -y
SKILL.md
slack-a2a-rooms.SKILL.mdname: slack-a2a-rooms
description: Agent-to-agent Slack rooms — a group DM (MPIM) holding a human plus two or more NanoClaw sibling bots, where each bot hears the room over its own Socket Mode connection. Registers the admission policy on the Slack channel's bot-inbound guard so bot-authored inbound is admitted only for rooms allowlisted in SLACK_A2A_ROOMS (re-attributed as slack:bot:<botId>, hop-limited via SLACK_A2A_MAX_HOPS), plus scripts/open-a2a-room.ts to open a room and register it.
Slack agent-to-agent rooms (SLACK_A2A_ROOMS)
Lets two or more NanoClaw Slack bots talk to each other — and to a human — in a shared group DM (MPIM). Empirically established against live Slack: `conversations.open` with `users=[<human>, <other bot user id>…]` works from a bot token holding `mpim:write` and returns an `is_mpim` channel, and bots receive each other's messages over their own Socket Mode connections as plain `message` events with `channel_type: "mpim"`, `bot_id` set, and `subtype` null.
**Canonical home.** This directory on `main` is the skill's canonical source — the setup wizard and any direct apply read it from the checkout. The copy on the `channels` branch is a compatibility mirror for older checkouts whose setup fetches companions from there; edits land here, never there.
**Where sibling-bot messages die today.** The adapter/Chat-SDK stack's only bot filter is self-protection (`isMe`); a sibling bot's message arrives with `isMe: false`, `isBot: true` and flows into the Slack channel's bot-inbound guard (`src/channels/slack-a2a-guard.ts`, installed with `/add-slack`), which drops all bot-authored inbound at the bridge boundary by default — before the router, before any sender-approval flow. The guard exposes a single admission seam, `setBotInboundPolicy`; this skill registers the policy that opens it up selectively:
- Rooms listed in `SLACK_A2A_ROOMS`: bot-authored inbound passes, attributed
to the user id `slack:bot:<botId>`, under a consecutive-hop limit.
- Everywhere else: bot-authored inbound stays dropped at the bridge, exactly
as the guard's default already does.
**Loop safety.** After N consecutive bot-authored inbound messages in an A2A room without a human message (N = `SLACK_A2A_MAX_HOPS`, default 6), further bot messages are dropped with a log line until a human speaks. The counter is per bot identity and per room; any human message resets it.
**Requires:**
- The Slack channel installed (`/add-slack`), current enough to ship the
bot-inbound guard (`src/channels/slack-a2a-guard.ts` with the `setBotInboundPolicy` seam). The default drop arrives with that payload; this skill only adds the allowlisted-room admission on top.
- At least two Slack bot identities on this host (or sibling bots on other
hosts sharing the workspace). Named identities are registered natively by the adapter from `SLACK_INSTANCES` — see the `slack-multi-instance` skill for the env-key format. `scripts/open-a2a-room.ts` reads tokens by that convention (`SLACK_BOT_TOKEN_<NAME>`; `default` → `SLACK_BOT_TOKEN`).
- Every participating app's manifest must carry the MPIM scopes and event:
`mpim:write` (open the room), `mpim:history` + `mpim:read` (see it), and the `message.mpim` bot event (hear it). Apps provisioned through the managed flow (`apps.manifest.create` + `apps.managedInstall`) already include all of these.
Apply
1. Verify the installed Slack channel ships the bot-inbound guard
The policy module copied next imports `setBotInboundPolicy` from the installed `src/channels/slack-a2a-guard.ts`. On a Slack payload that predates the guard, that import takes down the channel barrel — and with it **every** adapter — so verify the seam first. If the check fails, **stop**: re-run `/add-slack` from a channels branch that ships the guard, then re-apply this skill.
grep -sq 'export function setBotInboundPolicy' src/channels/slack-a2a-guard.ts || { echo 'slack-a2a-rooms: src/channels/slack-a2a-guard.ts is missing or does not export setBotInboundPolicy. Installing anyway would break the channel barrel and take down every channel adapter. Update the installed Slack channel first (re-run /add-slack from a channels branch that ships the bot-inbound guard), then re-apply this skill.' >&2; exit 1; }2. Copy the policy module, its guard test, and the room-opener script
This skill ships three files alongside this document; copy them into the tree at the same relative paths (overwrite; the skill's copies are canonical):
src/channels/slack-a2a.ts
src/channels/slack-a2a.test.ts
scripts/open-a2a-room.ts
- `slack-a2a.ts` — the admission policy: `SLACK_A2A_ROOMS` /
`SLACK_A2A_MAX_HOPS` parsing (re-read with a ~30s cache so a freshly opened room needs no restart), the per-room, per-identity consecutive-hop counter with human reset, and the `slack:bot:<botId>` re-attribution. The module registers itself onto the guard's admission seam on import.
- `slack-a2a.test.ts` — the guard: drives the real channel barrel and the
real bot-inbound guard end-to-end (see step 4 for what it pins).
- `open-a2a-room.ts` — operator CLI to open a room (see Configuration).
3. Register the policy module
Append the self-registration import to the channel barrel (skipped if the line is already present). Appending at the end keeps it after the Slack channel's own imports:
import './slack-a2a.js';
4. Build and validate
Build first — it guards the typed `setBotInboundPolicy` call against guard drift. The test imports the real channel barrel (a deleted or broken barrel line goes red) and asserts the policy end-to-end: allowlisted-room admission with `slack:bot:<botId>` re-attribution, non-listed-room drop, the hop limit with human reset, per-room/per-identity budgets, and that a downstream throw consumes no hop budget:
pnpm run build
pnpm exe
Read more
name: slack-a2a-rooms description: Agent-to-agent Slack rooms — a group DM (MPIM) holding a human plus two or more NanoClaw sibling bots, where each bot hears the room over its own Socket Mode connection. Registers the admission policy on the Slack channel's bot-inbound guard so bot-authored inbound is admitted only for rooms allowlisted in SLACK_A2A_ROOMS (re-attributed as slack:bot:<botId>, hop-limited via SLACK_A2A_MAX_HOPS), plus scripts/open-a2a-room.ts to open a room and register it.
Slack agent-to-agent rooms (SLACK_A2A_ROOMS)
Lets two or more NanoClaw Slack bots talk to each other — and to a human — in a shared group DM (MPIM). Empirically established against live Slack: `conversations.open` with `users=[<human>, <other bot user id>…]` works from a bot token holding `mpim:write` and returns an `is_mpim` channel, and bots receive each other's messages over their own Socket Mode connections as plain `message` events with `channel_type: "mpim"`, `bot_id` set, and `subtype` null.
**Canonical home.** This directory on `main` is the skill's canonical source — the setup wizard and any direct apply read it from the checkout. The copy on the `channels` branch is a compatibility mirror for older checkouts whose setup fetches companions from there; edits land here, never there.
**Where sibling-bot messages die today.** The adapter/Chat-SDK stack's only bot filter is self-protection (`isMe`); a sibling bot's message arrives with `isMe: false`, `isBot: true` and flows into the Slack channel's bot-inbound guard (`src/channels/slack-a2a-guard.ts`, installed with `/add-slack`), which drops all bot-authored inbound at the bridge boundary by default — before the router, before any sender-approval flow. The guard exposes a single admission seam, `setBotInboundPolicy`; this skill registers the policy that opens it up selectively:
- Rooms listed in `SLACK_A2A_ROOMS`: bot-authored inbound passes, attributed
to the user id `slack:bot:<botId>`, under a consecutive-hop limit.
- Everywhere else: bot-authored inbound stays dropped at the bridge, exactly
as the guard's default already does.
**Loop safety.** After N consecutive bot-authored inbound messages in an A2A room without a human message (N = `SLACK_A2A_MAX_HOPS`, default 6), further bot messages are dropped with a log line until a human speaks. The counter is per bot identity and per room; any human message resets it.
**Requires:**
- The Slack channel installed (`/add-slack`), current enough to ship the
bot-inbound guard (`src/channels/slack-a2a-guard.ts` with the `setBotInboundPolicy` seam). The default drop arrives with that payload; this skill only adds the allowlisted-room admission on top.
- At least two Slack bot identities on this host (or sibling bots on other
hosts sharing the workspace). Named identities are registered natively by the adapter from `SLACK_INSTANCES` — see the `slack-multi-instance` skill for the env-key format. `scripts/open-a2a-room.ts` reads tokens by that convention (`SLACK_BOT_TOKEN_<NAME>`; `default` → `SLACK_BOT_TOKEN`).
- Every participating app's manifest must carry the MPIM scopes and event:
`mpim:write` (open the room), `mpim:history` + `mpim:read` (see it), and the `message.mpim` bot event (hear it). Apps provisioned through the managed flow (`apps.manifest.create` + `apps.managedInstall`) already include all of these.
Apply
1. Verify the installed Slack channel ships the bot-inbound guard
The policy module copied next imports `setBotInboundPolicy` from the installed `src/channels/slack-a2a-guard.ts`. On a Slack payload that predates the guard, that import takes down the channel barrel — and with it **every** adapter — so verify the seam first. If the check fails, **stop**: re-run `/add-slack` from a channels branch that ships the guard, then re-apply this skill.
grep -sq 'export function setBotInboundPolicy' src/channels/slack-a2a-guard.ts || { echo 'slack-a2a-rooms: src/channels/slack-a2a-guard.ts is missing or does not export setBotInboundPolicy. Installing anyway would break the channel barrel and take down every channel adapter. Update the installed Slack channel first (re-run /add-slack from a channels branch that ships the bot-inbound guard), then re-apply this skill.' >&2; exit 1; }2. Copy the policy module, its guard test, and the room-opener script
This skill ships three files alongside this document; copy them into the tree at the same relative paths (overwrite; the skill's copies are canonical):
src/channels/slack-a2a.ts src/channels/slack-a2a.test.ts scripts/open-a2a-room.ts
- `slack-a2a.ts` — the admission policy: `SLACK_A2A_ROOMS` /
`SLACK_A2A_MAX_HOPS` parsing (re-read with a ~30s cache so a freshly opened room needs no restart), the per-room, per-identity consecutive-hop counter with human reset, and the `slack:bot:<botId>` re-attribution. The module registers itself onto the guard's admission seam on import.
- `slack-a2a.test.ts` — the guard: drives the real channel barrel and the
real bot-inbound guard end-to-end (see step 4 for what it pins).
- `open-a2a-room.ts` — operator CLI to open a room (see Configuration).
3. Register the policy module
Append the self-registration import to the channel barrel (skipped if the line is already present). Appending at the end keeps it after the Slack channel's own imports:
import './slack-a2a.js';
4. Build and validate
Build first — it guards the typed `setBotInboundPolicy` call against guard drift. The test imports the real channel barrel (a deleted or broken barrel line goes red) and asserts the policy end-to-end: allowlisted-room admission with `slack:bot:<botId>` re-attribution, non-listed-room drop, the hop limit with human reset, per-room/per-identity budgets, and that a downstream throw consumes no hop budget:
pnpm run build
pnpm exe
A lightweight alternative to OpenClaw that runs in containers for security. Connects to WhatsApp, Telegram, Slack, Discord, Gmail and other messaging apps,, has memory, scheduled jobs, and runs directly on Anthropic's Agents SDK
Other skills on nanoclaw.
More skills from other plugins.
browser-use
Direct browser control via CDP for web interaction: automation, scraping, testing, screenshots, and site/app work. Also covers…
agent-browser
Browser automation CLI for AI agents. Use when the user needs to interact with websites, including navigating pages, filling…
opencli-adapter-author
Build or improve an OpenCLI JS adapter using observed site behavior, existing adapters and browser API evidence. For ad-hoc…
Questions about slack-a2a-rooms.
- Which coding agents can use slack-a2a-rooms?
- slack-a2a-rooms installs in Claude Code, Codex, Cursor, Gemini CLI, GitHub Copilot, OpenCode and Windsurf with the skills CLI, using each agent's flag shown on this page. Flowy's auto-invocation, which fires a skill when your prompt matches, runs in Claude Code only.

