block / block/buzz

buzz-acp: --permission-mode is set but never reaches the agent; agents are silently muted while still billing full turns

Open
#5,303 4 comments 0 reactions 0 assignees View on GitHub
Dominant language
Rust
Stars
32.7k
Forks
4.3k
Avg merge
1d 13h
Merged PRs (30d)
253

Description

## Summary

`buzz-acp` resolves and logs `permission_mode=bypassPermissions`, but that value is never applied to the `claude-agent-acp` session. The adapter derives its permission mode solely from the user's `~/.claude/settings.json` → `permissions.defaultMode`. On a machine where that is `"auto"`, every managed agent runs headless in a mode that denies any non-allowlisted tool and cannot prompt — so `buzz messages send` is refused.

The failure is silent and expensive. Agents wake normally, load their full session, compose a reply, get denied on every send path, and end the turn. The channel stays empty, tokens are billed in full, and nothing is logged as an error at the Buzz layer.

## Environment

- Buzz Desktop 0.5.7 (also reproduced on 0.5.6)
- `@agentclientprotocol/claude-agent-acp` 0.64.0
- Bundled Claude Code CLI 2.1.220
- Windows 11
- 12 managed agents (7 `claude-agent-acp`, 4 `codex-acp`, 1 native)

## Evidence

**1. buzz-acp resolves the mode correctly.** Every agent's startup line reports it:

```
buzz-acp starting: ... idle_timeout=900s max_turn=7200s agents=10 subscribe=Mentions
context_limit=12 max_turns_per_session=0 memory=true permission_mode=bypassPermissions
```

**2. The adapter ignores it.** `claude-agent-acp` `dist/acp-agent.js`:

```js
const permissionMode = resolvePermissionMode(
settingsManager.getSettings().permissions?.defaultMode, this.logger);
```

The session's mode comes from `settings.json`. The ACP-side `permission_mode` is not consulted. `PERMISSION_MODE_ALIASES` maps `auto → "auto"`, so a user with `"defaultMode": "auto"` gets `auto` regardless of what Buzz asked for.

**3. Result at runtime.** Three agents mentioned in one channel message, all denied on every path:

```
07:13:15 Bash: buzz messages get --channel f5c91c95-...
→ "Permission to use Bash has been denied because Claude Code is
running in don't ask mode."
07:13:20 PowerShell: buzz messages get ... → denied
07:13:34 PowerShell: (the composed reply) → denied
07:13:42 agent: "I'm blocked from replying ... the `buzz` CLI is the only way
I can send channel messages."
```

A second agent independently tried Bash → PowerShell → `buzz messages send` directly, and was denied all three times.

**4. Cost of the silent failure.** One `halo @AgentA @AgentB @AgentC` in a channel:

| Agent | Requests | Input tokens | Output | Delivered? |
|---|---|---|---|---|
| A | 4 | 210,565 | 1,645 | no |
| B | 4 | 188,688 | 2,284 | no |
| C (codex) | 5 | 116,721 | 1,367 | no |
| **Total** | **13** | **515,974** | **5,296** | **none** |

A single DM to one agent cost 601,653 input tokens and also failed to deliver.

**5. Timeline.** Successful `buzz messages send` invocations per day, from the agents' own transcripts:

```
Aug 4: 229 Aug 5: 254 Aug 6: 7 Aug 7: 2 Aug 8: 4 (all denied)
```

No permission settings changed on this machine across that window (verified against a byte-identical backup: 536 allow rules, `defaultMode: auto`, zero deny). What changed underneath was the bundled CLI. The adapter carries this comment, which suggests mode semantics moved around then:

```js
// Claude Code 2.1.200 renamed the "default" mode to "Manual" and accepts
// `"defaultMode": "manual"` in settings.json; honor the same alias here.
```

## Why this is worse than a normal permission error

1. **It is invisible in Buzz.** The turn completes cleanly. There is no error surfaced in the channel, no delivery failure, and nothing in the agent log to distinguish "had nothing to say" from "was forbidden to speak."
2. **It bills in full.** Half a million tokens for one greeting, with zero output delivered. On a fixed subscription this consumes the plan allowance directly.
3. **It scales with the fleet.** Every mentioned agent pays independently.
4. **The user's own config is a trap they cannot reasonably discover.** `permissions.defaultMode: "auto"` is a sensible interactive setting. Nothing indicates it silently governs headless managed agents, or that `buzz` — the agent's only outbound channel — must be present in `permissions.allow` for agents to speak at all.

## Suggested fixes

1. **Pass the resolved `permission_mode` through to the adapter.** `claude-agent-acp` already reads session options from `params._meta` — sending it there would make `--permission-mode` / `BUZZ_ACP_PERMISSION_MODE` actually effective, and would restore Buzz's control over its own agents rather than deferring to a user setting written for interactive use.
2. **Surface send failures.** A `buzz messages send` that is refused at the permission layer should produce a visible error in the channel or agent status, not a clean exit. This is closely related to #2698 ("tokens are billed, chat stays empty").
3. **Fail loudly at startup.** If the resolved agent permission mode would deny the `buzz` CLI, log a warning at `buzz-acp starting:` time rather than discovering it mid-turn.

## Workaround for anyone hitting this

Add to `~/.claude/settings.json` → `permissions.allow`:

```json
"Bash(buzz:*)",
"PowerShell(buzz:*)"
```

Narrow, and it restores agent messaging without weakening permissions elsewhere. Setting `defaultMode` to `bypassPermissions` also works but applies to the user's own interactive Claude Code sessions on the same machine, which is a much larger blast radius.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.