无人值守地跑 Agent,最难的不是让它会做,而是让它在关键时刻停下来等你点头。 这套系统把「要不要做」推到你 iPhone / Apple Watch 上,你在表上点一下,结论回传给 Agent。 推送通道是自建的 Home Assistant —— 不用第三方推送服务,不用自写 iOS App。
前两种都是偷懒的正确,第三种才是能长期跑下去的。
Agent 想干什么就干什么。它会替你 rm -rf、git push --force、npm publish。
方便,直到某一天不方便。
只读之外全部拒绝。安全,但 Agent 会卡在第一步,你得到的是一个不会干活的 Agent。
无害操作静默放行,高危操作推到你手腕上等你点一下。
既不放任,也不卡死。
Agent 宿主的 PreToolUse hook 在命令真正执行之前拦下来,向本地网关发一个请求;
网关把卡片送出去、等你选择,再把结论回填给 hook。链路两端都是本地 HTTP / WebSocket,
推送这一跳才交给 Home Assistant。
# Agent 宿主 → 网关(本地回环 127.0.0.1) POST /v1/approvals { "tier": "L3", "title": "高危操作确认", "body": "git push --force origin main", "ttl": 120, "wait": 90000, "session_id": "…" }
# 网关 → Agent 宿主(回填结论) 200 OK { "verdict": "allow", "decidedBy": "ha", "latencyMs": 8420 } # 其余时刻(超时 / 推送失败) "verdict": "deny" ← fail-closed,绝不放行
关键分工:Home Assistant 只负责送通知、收事件,不做任何判断。 令牌校验、重放拦截、超时兜底、审计留痕全部在网关里。
点下面的按钮,看一条 L3 命令怎么从 Agent 走到你的手腕、再走回来。 中途把「手机离线」打开,可以看它在推送失败时的兜底行为 —— 那是这套系统最见功力的地方。
分级只看命令文本的正则。设计目标不是「把危险的都抓出来」——而是 不把无害的判高:打扰预算一旦被噪音消耗完,人就会开始无脑点「允许」,整套方案等于白接。
复合命令取最危险的那一段,不让无害段稀释它:
for f in a; do sudo true; done 仍是 L3。反过来,rm 会看目标路径是否落在
node_modules / dist / /tmp 这类常规清理目录,以及是否递归 ——
目录内清理判 L1,非递归删单文件判 L2,递归删外部路径判 L3。
推送失败时最糟的处理,是「静默放行」。这里的每一层都朝着 fail-closed 长。
每次确认签一张绑定到「这一次动作」的令牌。手改过期时间会直接判定签名无效。
一次授权只放行一次。同一条命令再跑,还得重新问你。重放会被拦截。
L2 / L3 默认 120 秒无响应即拒绝。挂在你腕上的卡片过期后自己作废。
推送失败不降级成放行,而是拦住命令,再让你在桌面上一次性放行这一条。
每一次创建、结算、超时、撤销都写进 audit 日志,标明是谁决定的。
网关 ↔ Home Assistant 走 127.0.0.1,不受局域网 IP 漂移影响,也不出网。