ACP mode auto-approves tool calls again — session/request_permission not sent since 1.0.81-1 (regression of #845)
还没有人认领这个 Issue。
- 主要语言
- Shell
- 星标
- 11.2k
- 派生
- 1.9k
- 平均合并
- 14 小时 16 分钟
- 30 天内合并 PR
- 6
描述
Describe the bug
In --acp mode the agent no longer sends session/request_permission. Shell commands, file edits and deletions execute unattended: the client is given no opportunity to prompt, and nothing is written to the session log to say permission was ever waived.
This is the same defect as #845 (closed 2026-01-02). It returned in 1.0.81-1.
The permission engine itself is healthy — the interactive TUI on the same build still prompts for the identical command. Only the ACP path skips it.
Affected version
1.0.81-2, 1.0.81-3, 1.0.81-5 (current). Last good: 1.0.81-0.
Bisected with COPILOT_CLI_DIST_DIR against builds cached under ~/.copilot/pkg/linux-x64/:
| build | sends session/request_permission |
file created despite client refusing |
|---|---|---|
| 1.0.80 | yes | no |
| 1.0.81-0 | yes | no |
| 1.0.81-1 | not tested — not cached locally | |
| 1.0.81-2 | no | yes |
| 1.0.81-3 | no | yes |
| 1.0.81-5 | no | yes |
1.0.81-1 is named because it carries the only permission-related entry in the changelog between the last good and first bad build:
"Turning allow-all off from an ACP client now reaches the permission engine whenever there is a runtime override or auto-approval to revoke… (a baseline granted by
--allow-all-*launch flags is still deliberately left intact)" — PR 14283
Steps to reproduce the behavior
Save as acp-perm-repro.js (no dependencies), then mkdir /tmp/acpdemo && node acp-perm-repro.js /tmp/acpdemo:
// Minimal ACP client: initialize -> session/new -> session/prompt.
// Logs every request the agent sends us, and refuses any permission request.
const { spawn } = require('node:child_process');
const dir = process.argv[2] || process.cwd();
const say = (...a) => require('node:fs').writeSync(1, a.join(' ') + '\n');
const child = spawn('copilot', ['--acp'], { cwd: dir, stdio: ['pipe', 'pipe', 'ignore'] });
let id = 0, buf = '';
const pending = new Map();
const send = (o) => child.stdin.write(JSON.stringify(o) + '\n');
const request = (method, params) =>
new Promise((res, rej) => { const i = ++id; pending.set(i, { res, rej }); send({ jsonrpc: '2.0', id: i, method, params }); });
child.stdout.on('data', (d) => {
buf += d;
for (let n; (n = buf.indexOf('\n')) >= 0; ) {
const line = buf.slice(0, n); buf = buf.slice(n + 1);
let m; try { m = JSON.parse(line); } catch { continue; }
if (m.method && m.id !== undefined) { // agent -> client request
say('AGENT ASKS:', m.method);
send({ jsonrpc: '2.0', id: m.id,
result: m.method === 'session/request_permission'
? { outcome: { outcome: 'cancelled' } } // client refuses; tool must not run
: {} });
} else if (m.id !== undefined && pending.has(m.id)) {
const p = pending.get(m.id); pending.delete(m.id);
m.error ? p.rej(new Error(JSON.stringify(m.error))) : p.res(m.result);
}
}
});
(async () => {
const init = await request('initialize', {
protocolVersion: 1,
clientCapabilities: { fs: { readTextFile: true, writeTextFile: true }, terminal: false },
});
say('agent version:', init.agentInfo?.version);
const { sessionId } = await request('session/new', { cwd: dir, mcpServers: [] });
say('session:', sessionId);
await request('session/prompt', { sessionId, prompt: [{ type: 'text',
text: `Run exactly this shell command and nothing else: touch ${dir}/created-without-asking.txt` }] });
say('file exists:', require('node:fs').existsSync(`${dir}/created-without-asking.txt`));
child.kill();
})().catch((e) => { say(e.message); child.kill(); process.exit(1); });
On 1.0.81-0 (COPILOT_AUTO_UPDATE=false COPILOT_CLI_DIST_DIR=~/.copilot/pkg/linux-x64/1.0.81-0):
agent version: 1.0.81-0
AGENT ASKS: session/request_permission
file exists: false
On 1.0.81-5:
agent version: 1.0.81-5
file exists: true
The directory is fresh, has never been trusted, and none of these commands appear in ~/.copilot/permissions-config.json.
Expected behavior
As in #845 and the ACP specification, where session/request_permission is a baseline method: the agent asks, the client decides. A client answering cancelled should see the tool not run — which is what 1.0.81-0 does.
Additional context
-
The TUI is unaffected, which localises the fault. Driven under a pty on 1.0.81-5, the identical prompt produces "Do you want to run this command? …
touch …", and the file is not created. Only the ACP path skips the ask. -
Not gated on a client capability. Declaring
terminal: truealongsidefschanges nothing. -
Not explained by saved approvals or folder trust. Reproduced in a directory with neither.
-
Reproduced by two independent client implementations, one of them the ~30 lines above, written from scratch.
-
Nothing in the session log distinguishes this from a deliberate waiver. The log records permission decisions, not permission policy — so every path that reaches the engine is recorded however it is answered, and every path that bypasses the engine records nothing at all:
how the tool got permission permission.requested/.completedrecorded kind TUI, user answers "Yes" logged approvedACP client prompts, user allows once logged approvedACP client auto-answers allow_alwayslogged approved-for-sessionACP client refuses logged denied-interactively-by-user--allow-all-tools(launch flag)none — --yolo(launch flag)none — /yolo on(runtime toggle)none — 1.0.81-5 over ACP (this bug) none — session.startcarries no permission-posture field either (contextTier,reasoningEffort,copilotVersion… and nothing about approvals), and the affected build's log contains no string matching allow/approve/permission anywhere. So an ACP session that ran unsupervised because the agent stopped asking cannot be told apart afterwards from one where the operator knowingly disabled permissions — and/yolo onleaves no trace even though the TUI announces "All permissions are now enabled" on screen.A session here shows 105 tool calls in this state —
git checkout --detach,git commit,rm -rf,chmod +xand fourteen file edits — with zeropermission.*events. -
Note for anyone bisecting: the agent sends this request with
"id": 0. A client testingif (m.id)will silently drop it and hang, which looks exactly like the bug. Worth ruling out before concluding a build is affected.
贡献指南
从这里开始
- 先读完整个 Issue,再读项目的贡献指南。
- 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
- Fork 仓库,在一个分支上完成修改。
- 提交 Pull Request,并在描述里引用这个 Issue 编号。
调研方向
从 acp-perm-repro.js 客户端开始,使用 --acp 和文档中说明的 COPILOT_CLI_DIST_DIR 设置,在已知正常和受影响的构建上运行它。跟踪 session/request_permission 周围的 ACP 会话/提示路径,并将其与 TUI 权限流程进行比较;完成的标准是客户端拒绝会阻止 shell 命令执行,并且权限事件会像 1.0.81-0 中那样被记录。
由索引模型根据 Issue 内容生成。
评估
- 技术栈
- javascript, node.js
- 领域
- cli, security
- Issue 类型
- 缺陷
- 难度
- 4/5
- 预计耗时
- 3-5 天
- 活跃度
- 活跃
- 描述清晰度
- 基本清楚
- 新手友好度
- 52/100