a4phone 在 AI 助手请求执行敏感操作(运行命令、读写文件、修改代码等)时,把权限请求推送到手机:外出模式下手机端显示 Approve / Deny / Always Approve 三个决策按钮,点选后决策回传会话,AI 按你的选择继续执行或中止。本文章介绍权限请求的触发范围、消息格式化、决策按钮与回传机制。
1. 功能概述
| 维度 | 说明 |
|---|---|
| 触发事件 | Claude Code / Codex:PermissionRequest;DSH:approval/request |
| 拦截方式 | Claude Code / Codex:PermissionRequest Hook;DSH:approval/request 事件拦截 |
| 决策选项 | Approve(允许)/ Deny(拒绝)/ Always Approve(总是允许,按建议批量授权) |
| 生效模式 | 仅外出模式(a4p out) |
| 超时回退 | 手机超时未决策自动回退终端审批 |
1.1 事件流程
graph LR
A[AI 请求权限
PermissionRequest] --> B{当前模式} B -->|终端优先| C[终端原生审批] B -->|外出模式| D{工具类型} D -->|AskUserQuestion| E[直接放行
提问由提问功能处理] D -->|其他工具| F[格式化请求内容] F --> G[推送手机
Approve / Deny] G --> H{存在权限建议} H -->|是| I[附加 Always Approve
按钮] H -->|否| J[仅两个按钮] I --> K[手机点选决策] J --> K K --> L[决策回传会话] L --> M[AI 继续或中止] style A fill:#e8f4f8,stroke:#1a6b8a,stroke-width:2px style B fill:#ebdef0,stroke:#8e44ad,stroke-width:2px style C fill:#fdebd0,stroke:#b7950b,stroke-width:2px style D fill:#ebdef0,stroke:#8e44ad,stroke-width:2px style E fill:#d5f5e3,stroke:#27ae60,stroke-width:2px style F fill:#d6eaf8,stroke:#1a6b8a,stroke-width:2px style G fill:#fadbd8,stroke:#c0392b,stroke-width:2px style H fill:#ebdef0,stroke:#8e44ad,stroke-width:2px style I fill:#fdebd0,stroke:#b7950b,stroke-width:2px style J fill:#fdebd0,stroke:#b7950b,stroke-width:2px style K fill:#e8f4f8,stroke:#1a6b8a,stroke-width:2px style L fill:#d5f5e3,stroke:#27ae60,stroke-width:2px style M fill:#ffecd6,stroke:#e67e22,stroke-width:2px
PermissionRequest] --> B{当前模式} B -->|终端优先| C[终端原生审批] B -->|外出模式| D{工具类型} D -->|AskUserQuestion| E[直接放行
提问由提问功能处理] D -->|其他工具| F[格式化请求内容] F --> G[推送手机
Approve / Deny] G --> H{存在权限建议} H -->|是| I[附加 Always Approve
按钮] H -->|否| J[仅两个按钮] I --> K[手机点选决策] J --> K K --> L[决策回传会话] L --> M[AI 继续或中止] style A fill:#e8f4f8,stroke:#1a6b8a,stroke-width:2px style B fill:#ebdef0,stroke:#8e44ad,stroke-width:2px style C fill:#fdebd0,stroke:#b7950b,stroke-width:2px style D fill:#ebdef0,stroke:#8e44ad,stroke-width:2px style E fill:#d5f5e3,stroke:#27ae60,stroke-width:2px style F fill:#d6eaf8,stroke:#1a6b8a,stroke-width:2px style G fill:#fadbd8,stroke:#c0392b,stroke-width:2px style H fill:#ebdef0,stroke:#8e44ad,stroke-width:2px style I fill:#fdebd0,stroke:#b7950b,stroke-width:2px style J fill:#fdebd0,stroke:#b7950b,stroke-width:2px style K fill:#e8f4f8,stroke:#1a6b8a,stroke-width:2px style L fill:#d5f5e3,stroke:#27ae60,stroke-width:2px style M fill:#ffecd6,stroke:#e67e22,stroke-width:2px
2. 请求内容格式化
推送标题为 {Agent}: {工具名}(如 Claude Code: Bash),正文按工具类型格式化展示请求内容:
| 工具类型 | 展示内容 |
|---|---|
ExitPlanMode |
计划全文(截断到 1000 字符) |
Bash |
要执行的命令 |
Read / Write / Edit |
目标文件路径 |
| 其他工具 | 工具输入的 JSON 序列化 |
请求内容超过 1000 字符时截断并添加省略号,避免超出 ntfy 推送上限。
3. 决策按钮
| 按钮 | 行为 |
|---|---|
| Approve | 允许本次操作(decision.behavior = "allow") |
| Deny | 拒绝本次操作(decision.behavior = "deny") |
| Always Approve | 允许本次操作,并批量授权工具返回的所有权限建议(仅当请求携带 permission_suggestions 时出现) |
当工具的权限请求附带建议列表(如 Bash 工具建议的一批可执行命令)时,推送会插入 Always Approve 按钮,点击后 a4phone 把建议写入 updatedPermissions,AI 后续同类操作不再逐次询问。
4. 特殊处理
4.1 AskUserQuestion 直接放行
AskUserQuestion(Claude Code 的提问工具)触发的权限请求不推送手机——提问的完整交互由AI提问交互功能负责,a4phone 直接返回 allow 放行,避免推送冗余的原始 JSON。
4.2 计划审批超时更长
ExitPlanMode(计划确认)使用独立的 planTimeout(默认 300 秒),比普通权限请求的 timeout(默认 60 秒)更长,给离开电脑的用户更充足的决策时间。
5. 超时与失败回退
| 场景 | 处理方式 |
|---|---|
| 手机超时未决策 | 等待超时后回退终端原生审批 |
| 推送失败 | 回退终端原生审批 |
| 终端优先模式 | 不拦截,直接走终端审批 |
| DSH 手机决策 | Approve 映射为 allowed-once,Deny 映射为 rejected |
举手提问