# 审批标识与跨连接投票

> 区分 ACP 连接内 JSON-RPC ID、bridge requestId 和会话绑定。

- 网址：https://funcoding.ai/agents/qwen-code/runtime/permission-routing/
- 核实日期：2026-10-08（命令、配置和价格以官方文档为准）
- 官方来源：[Qwen Code 官方文档：Multi-Client Permission Mediation](https://github.com/QwenLM/qwen-code/blob/main/docs/developers/daemon/04-permission-mediation.md)

---
审批可以通过当前 ACP transport 回答，也可以由另一个有权限的 HTTP 客户端使用 REST 回答。两条路径最终进入相同的 session-bound mediator 处理。

## 两套 request ID

| 标识 | 范围 | 用途 |
| --- | --- | --- |
| _qwen_perm_N | 当前 ACP 连接内递增 | JSON-RPC 请求与响应关联 |
| bridge requestId | bridge 审批标识 | 在 REST 与 mediator 中定位请求 |

ACP session/request_permission 的 params._meta.qwen.requestId 携带 bridge 标识。REST 投票使用 POST /session/:id/permission/:requestId；不要把连接内 _qwen_perm_N 填进该 URL。

旧 POST /permission/:requestId 通过内部查找确定 session，再委派给同一路径。新客户端优先使用明确 session 的接口，不把旧全局路由当作跨工作区寻址保证。

## 检查顺序

1. 地址中的 session 必须仍 live。
2. requestId 若属于另一 session，拒绝且不披露成员关系。
3. 未知 requestId 在 clientId 验证前返回未找到，避免通过不同错误探测会话成员。
4. 显式 clientId 必须已登记到该 session；未提供时交给策略判断。
5. 拒绝通过 selected optionId 注入内部取消 sentinel。
6. 按请求创建时的 policy 处理投票。

匿名普通投票仅可能被 first-responder 或 loopback 的 local-only 接受；designated、consensus 拒绝匿名选择。已注册 ID 仍是 v1 自报身份，不具有持有证明。

## 回答 ACP 请求

在收到的同一 JSON-RPC id 上选择实际公布的 optionId：

```json
{
  "jsonrpc": "2.0",
  "id": "_qwen_perm_3",
  "result": {
    "outcome": {
      "outcome": "selected",
      "optionId": "allow"
    }
  }
}
```

allow 仅代表该请求实际提供这一选项时的示例。取消使用 outcome.outcome 为 cancelled；JSON-RPC error 响应也被 dispatcher 映射为取消。

内部 CANCEL_VOTE_SENTINEL 值为 __cancelled__，供 bridge 将显式取消转为 mediator 输入。wire 的 selected optionId 使用该值会拒绝；agent 公布允许选项时包含该 sentinel 也会触发 CancelSentinelCollisionError。

## Loopback 按投票请求判断

REST 和 ACP 的 fromLoopback 都取自该次请求 socket remoteAddress，不使用 X-Forwarded-For、Forwarded 等 header。ACP initialize 来自本机，不代表后续从另一连接地址提交的投票也自动被认作本机。

这也意味着通过本地代理到达的请求与直接远端请求有不同 socket 来源；部署方不能仅用自报 clientId 或代理 header 推断操作者身份。

## 失败恢复

ACP dispatcher 回答审批时若 bridge 抛错，会尝试显式 cancelAbandonedPermission，避免留下无限等待。若原投票和取消都失败，保留 pending 映射供连接最终 teardown 再清理，而不是丢掉最后的恢复线索。

session close、eviction 和 bridge shutdown 都应调用 forgetSession，在 child 的关闭宽限期之前结算该 session 的待审批。不要把 HTTP 响应失败简单视为后台请求已经取消。
