跳到主要内容
知仓学习社ZHICANG

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 So…

读凭据联网读文件写文件严重 15 · 高危 0nanocoai/nanoclaw

它会碰到什么

扫了多少4 个文本文件,34 KB
它会碰到什么读凭据联网读文件写文件
命中总数22 处
命中统计严重 15 · 高 0 · 中 6 · 低 1
逐条看命中(15 条严重或高危)
  • 严重 scripts/open-a2a-room.ts:6cred-paths
    * appends it to SLACK_A2A_ROOMS in .env so the slack-a2a-rooms bridge filter
  • 严重 scripts/open-a2a-room.ts:103cred-paths
    if (!token) fail(`missing ${envKey} in .env (slack-multi-instance token convention)`);
  • 严重 scripts/open-a2a-room.ts:116cred-paths
    /** Append the room to SLACK_A2A_ROOMS in .env (create the key if absent,
  • 严重 scripts/open-a2a-room.ts:119cred-paths
    const envPath = path.join(process.cwd(), '.env');
  • 严重 scripts/open-a2a-room.ts:124cred-paths
    // no .env — create one with just this key
  • 严重 scripts/open-a2a-room.ts:189cred-paths
    ? 'SLACK_A2A_ROOMS already lists this room — .env unchanged.'
  • 严重 scripts/open-a2a-room.ts:190cred-paths
    : `SLACK_A2A_ROOMS ${envResult === 'created' ? 'created' : 'updated'} in .env — the bridge filter picks it up within ~30s (no restart needed).`,
  • 严重 SKILL.md:120cred-paths
    `.env` keys (both re-read with a ~30s cache — no restart needed after edits):
  • 严重 SKILL.md:180cred-paths
    3. Remove `SLACK_A2A_ROOMS` and `SLACK_A2A_MAX_HOPS` from `.env`.
  • 严重 SKILL.md:214cred-paths
    `.env` manually.
  • 严重 src/channels/slack-a2a.test.ts:25cred-paths
    * Config is read from .env (SLACK_A2A_ROOMS / SLACK_A2A_MAX_HOPS) at first
  • 严重 src/channels/slack-a2a.test.ts:26cred-paths
    * decision, so the test chdirs into a temp dir carrying a crafted .env BEFORE
  • 严重 src/channels/slack-a2a.test.ts:44cred-paths
    join(dir, '.env'),
  • 严重 src/channels/slack-a2a.ts:32cred-paths
    * Short enough that a room appended to .env by scripts/open-a2a-room.ts is
  • 严重 src/channels/slack-a2a.ts:89cred-paths
    * `getConfig` is injectable for tests; production reads .env with a short TTL

这一栏是扫描器报的事实,不是结论。命中多不等于有毒(安全工具、规则库、示例脚本本来就会包含危险写法),命中少也不等于干净。它和你手上的凭据、文件、网络有什么关系,需要你自己看。

技能内容

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:<bot_id>, 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>; defaultSLACK_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:<bot_id> 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:<bot_id> 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 exec vitest run src/channels/slack-a2a.test.ts

Configuration

.env keys (both re-read with a ~30s cache — no restart needed after edits):

  • SLACK_A2A_ROOMS — comma-separated raw Slack channel ids (the MPIM ids the

opener script prints, e.g. G0AAAAAAA — note MPIM ids may start with G

or C depending on workspace vintage). Only these rooms admit bot-authored

inbound.

  • SLACK_A2A_MAX_HOPS — consecutive bot-authored inbound messages allowed in

an A2A room without a human message before further bot messages are dropped.

Default 6.

Opening a room

pnpm exec tsx scripts/open-a2a-room.ts --instances dana,eli --user U0AAAAAAA

The first listed instance opens the conversation (via conversations.open

with the human + the other bots' user ids, resolved through auth.test per

token) and posts an intro message. The script prints the channel id and

appends it to SLACK_A2A_ROOMS in .env. Without --user you get a

bots-only room, which needs at least three instances (Slack collapses a

two-party open into a 1:1 IM).

Letting bot senders through the access gate

The room allowlist gets bot messages to the router; the permissions module

still gates them like any sender. Bot senders arrive as user id

slack:bot:<bot_id>, which starts unknown. After the first human mention in

the room auto-creates its messaging group, either set the room public:

pnpm exec tsx scripts/q.ts data/v2.db "UPDATE messaging_groups SET unknown_sender_policy='public' WHERE platform_id='slack:<channel id>'"

or keep request_approval and approve each slack:bot:<bot_id> sender once

(or add them as members of the agent group). A private A2A room with known

humans is a reasonable place for public.

Engagement: A2A conversation is mention-driven

An MPIM is a group context in NanoClaw's channel-defaults model (Slack DMs

are only D… channels), so the Slack group defaults apply:

engage_mode: mention-sticky, per-thread stickiness. That means a bot

replies when it is @-mentioned (then stays engaged in that thread) — it does

not answer every room message. Bot-to-bot conversation is therefore

mention-driven by design: bot A's reply reaches bot B's agent when it

@-mentions bot B, and the chain continues only as long as each reply

mentions the next speaker. Slack does not emit app_mention for

bot-authored messages, but mention detection still works: the Chat SDK's

text-level detector matches the bot's own <@U…> token, which the adapter

deliberately leaves unresolved in the text. This is the intended loop

governor alongside the hop limit — prompt the agents (group CLAUDE.md /

personality) to @-mention the sibling they want an answer from, and to stop

mentioning anyone when the exchange has converged.

Remove

  1. Delete the three copied files: src/channels/slack-a2a.ts,

src/channels/slack-a2a.test.ts, scripts/open-a2a-room.ts.

  1. Delete the import './slack-a2a.js'; line from src/channels/index.ts.
  2. Remove SLACK_A2A_ROOMS and SLACK_A2A_MAX_HOPS from .env.
  3. Optionally re-tighten any messaging groups you set to

unknown_sender_policy='public', and archive/leave the MPIMs from Slack

(the rooms themselves are ordinary Slack conversations; NanoClaw holds no

other state for them beyond the usual messaging-group/session rows).

  1. Rebuild (pnpm run build).

With the skill removed, the channel guard's default applies everywhere again:

bot-authored inbound is dropped in every room.

Notes

  • Bot senders outside A2A rooms: the Slack channel's bot-inbound guard

drops bot-authored messages in rooms not listed in SLACK_A2A_ROOMS at the

bridge — before the router and before any sender-approval flow — with or

without this skill applied. If an install relies on a bot sender (another

workspace app posting into a channel the agent watches), add that room to

SLACK_A2A_ROOMS. Human messages are never affected.

  • Access-gate story is manual. The recipe deliberately leaves letting

slack:bot:<bot_id> senders through the gate as an operator step (public

policy or per-bot approval). Should open-a2a-room.ts instead pre-create

the messaging group + wirings + members via ncl so a room works with zero

extra steps? That needs the host running and a choice of agent group per

bot — deferred.

  • Hop-counter scope. The counter counts bot-authored inbound per bot

identity (bridge instance). A bot's own outbound is invisible to it (isMe

dropped upstream), so with two bots the effective conversation length is

roughly 2×maxHops messages; with k bots each message increments k−1

counters. If per-room total (not per-identity) semantics are wanted, the

counter needs to live host-side (router/session), not in the channel layer.

  • Cross-host rooms. For sibling bots on different NanoClaw hosts, each

host needs this skill (its own allowlist entry). The opener script only

handles co-hosted tokens; opening a cross-host room means running it where

the first bot's token lives and adding the room id to the other host's

.env manually.

  • message_changed / edited bot messages are not admitted (the adapter

only forwards unfurl data for edits) — fine for v1.

  • Attribution name. The users row for slack:bot:<bot_id> takes

whatever display name the bridge serialized (often unknown for bot

events, since event.username is frequently absent and event.user is

unset). A nicety would be resolving the bot's profile name via

bots.info — deferred.

  • MPIM id prefix. Older workspaces mint MPIM ids starting with G,

newer ones with C. The allowlist matches exact ids so both work, but the

adapter's getChannelVisibility calls C… ids "workspace"-visible —

cosmetic only, nothing here branches on it.

想直接用这个技能?

本站把开放许可(MIT / Apache 等)的技能按仓库打包整理到网盘,点一下转存到你自己的网盘,不用一个个从 GitHub 拉。许可未声明的技能只给原始仓库链接,不打包。

它属于哪个仓库

星标★ 30,781
本站分层T1
该仓技能数61
原文件路径.claude/skills/slack-a2a-rooms/SKILL.md

同一个仓库里的其他技能

看这个仓库的全部 61 个技能