You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Claude Code's own June 2026 release added Tool(param:value) permission-rule syntax so a rule can match a tool's input parameters with * wildcards — e.g. Agent(model:opus) to block Opus subagents (v2.1.178, 2026-06-15) — a first-party move toward finer-grained, parameterized restriction of what a spawned subagent may do, beyond a flat allow/deny per tool name. [Source: Claude Code changelog]
SWE-agent's Agent-Computer Interface (ACI) design principles argue a tool surface should be no larger than the task needs — the ACI should "help the agent recover from mistakes" and "remove unnecessary context from prior observations," i.e. a smaller, task-appropriate tool surface reduces mistake surface. [Source: arXiv:2405.15793, docs/background/aci.md]
The gap, confirmed against this repo (not just landscape-inspired)
src/config.js:49 defines WRITE_TOOLS = ['Edit', 'Write', 'MultiEdit', 'NotebookEdit'] as a single global constant. src/delegate-invocation.js:191 applies it uniformly to every do-mode spawn:
There is no way for an individual peer to receive a narrower write-tool set than the full four. Contrast with WEB_TOOLS: agentWantsWebTools (delegate-invocation.js:159-188) already threads a per-agent additive grant from the operator-owned mesh.json manifest (webTools: true, ask-only, "operator-owned... so a tampered third-folder card cannot expand egress"). There is no analogous subtractive per-peer scoping for do-mode's write tools.
Concretely: a peer whose entire job is prose/doc editing gets the identical NotebookEdit/MultiEdit/Write grant as a peer that legitimately creates new files — needless blast radius. Every ACI mistake-surface argument applies to the unused tools just as much as the used ones.
Proposed direction
Add an optional writeTools array under a peer's own agent.jsonx-agentmesh block (the same established namespace already used for modes/meshVersion — see test/structure-conformance.test.js), e.g. "x-agentmesh": { "writeTools": ["Edit"] }. At spawn time, the tool-list builder intersects the declared list with the global WRITE_TOOLS constant — a peer can only ever narrow its own write surface, never widen it (a tampered/compromised agent.json cannot grant itself Bash or anything outside the fixed global set). Omitting the field preserves today's behavior exactly (full WRITE_TOOLS) — fully backward compatible, opt-in hardening.
This is a distinct axis from two already-open ideas, so it doesn't overlap:
No dynamic/runtime tool negotiation — the allowlist is static per peer, read once (like description.js reads AGENT.md once at server start).
Invariants preserved
Single writable root / path-guard unaffected — this narrows which write tools exist, not where they may write.
No Bash in do: the intersection is always a subset of WRITE_TOOLS, so Bash can never enter regardless of what a peer declares.
Anti-spoof: no new field is read from tool-call args, only from the peer's own manifest at spawn setup.
Considered and rejected
MetaGPT's global publish-subscribe message pool (agents broadcast to a shared bus; others subscribe by role) was also surveyed as a landscape idea for cross-peer coordination, but it directly conflicts with this project's governing "no central registry, broker, or dispatcher by design" principle (PROJECT.md) and was not carried forward as a proposal.
Sources
Claude Code changelog — v2.1.178 (2026-06-15): Tool(param:value) permission-rule syntax
Draft spec location:docs/superpowers/specs/2026-07-24-per-peer-write-tool-allowlist-design.md (Goal/Non-goals/design above; invariants above; risk: a peer misconfiguring its own allowlist too narrowly just fails a do-mode task with a clear tool-not-allowed error, no security risk either direction). Spec-push to the repo is currently blocked pending #771 (DEV_MESH_PAT not configured); this issue carries the full draft in the body in the meantime, matching current practice for #818/#824/#831/etc.
Research brief
Source: Analyst landscape survey 2026-07-24 — agent_mesh roadmap vs. comparable OSS agent frameworks (OpenHands, SWE-agent, Aider, MetaGPT/ChatDev, Renovate, claude-code-action).
Claude Code's own June 2026 release added
Tool(param:value)permission-rule syntax so a rule can match a tool's input parameters with*wildcards — e.g.Agent(model:opus)to block Opus subagents (v2.1.178, 2026-06-15) — a first-party move toward finer-grained, parameterized restriction of what a spawned subagent may do, beyond a flat allow/deny per tool name. [Source: Claude Code changelog]SWE-agent's Agent-Computer Interface (ACI) design principles argue a tool surface should be no larger than the task needs — the ACI should "help the agent recover from mistakes" and "remove unnecessary context from prior observations," i.e. a smaller, task-appropriate tool surface reduces mistake surface. [Source: arXiv:2405.15793, docs/background/aci.md]
The gap, confirmed against this repo (not just landscape-inspired)
src/config.js:49definesWRITE_TOOLS = ['Edit', 'Write', 'MultiEdit', 'NotebookEdit']as a single global constant.src/delegate-invocation.js:191applies it uniformly to every do-mode spawn:There is no way for an individual peer to receive a narrower write-tool set than the full four. Contrast with
WEB_TOOLS:agentWantsWebTools(delegate-invocation.js:159-188) already threads a per-agent additive grant from the operator-ownedmesh.jsonmanifest (webTools: true, ask-only, "operator-owned... so a tampered third-folder card cannot expand egress"). There is no analogous subtractive per-peer scoping for do-mode's write tools.Concretely: a peer whose entire job is prose/doc editing gets the identical
NotebookEdit/MultiEdit/Writegrant as a peer that legitimately creates new files — needless blast radius. Every ACI mistake-surface argument applies to the unused tools just as much as the used ones.Proposed direction
Add an optional
writeToolsarray under a peer's ownagent.jsonx-agentmeshblock (the same established namespace already used formodes/meshVersion— seetest/structure-conformance.test.js), e.g."x-agentmesh": { "writeTools": ["Edit"] }. At spawn time, the tool-list builder intersects the declared list with the globalWRITE_TOOLSconstant — a peer can only ever narrow its own write surface, never widen it (a tampered/compromisedagent.jsoncannot grant itselfBashor anything outside the fixed global set). Omitting the field preserves today's behavior exactly (fullWRITE_TOOLS) — fully backward compatible, opt-in hardening.This is a distinct axis from two already-open ideas, so it doesn't overlap:
Non-goals
askmode (already read-tool-only).delegate_tasktool args — that would violate the anti-spoof invariant (model-facing surface stays exactly{ mode, task }). Scoping is declared once, out-of-band, in the peer's ownagent.json— same trust boundary as idea: post-do validation gate — declarative validators in agent.json run after do-mode tasks #509's proposed validators.description.jsreadsAGENT.mdonce at server start).Invariants preserved
Bashindo: the intersection is always a subset ofWRITE_TOOLS, soBashcan never enter regardless of what a peer declares.Considered and rejected
MetaGPT's global publish-subscribe message pool (agents broadcast to a shared bus; others subscribe by role) was also surveyed as a landscape idea for cross-peer coordination, but it directly conflicts with this project's governing "no central registry, broker, or dispatcher by design" principle (PROJECT.md) and was not carried forward as a proposal.
Sources
Tool(param:value)permission-rule syntaxsrc/config.js:49-56,src/delegate-invocation.js:159-198Draft spec location:
docs/superpowers/specs/2026-07-24-per-peer-write-tool-allowlist-design.md(Goal/Non-goals/design above; invariants above; risk: a peer misconfiguring its own allowlist too narrowly just fails a do-mode task with a clear tool-not-allowed error, no security risk either direction). Spec-push to the repo is currently blocked pending #771 (DEV_MESH_PATnot configured); this issue carries the full draft in the body in the meantime, matching current practice for #818/#824/#831/etc.