远程Agent卡在本地权限弹窗时不会真正失败,而是永远停止——比可观察的失败更难排查。解决方案是将权限请求建模为持久化状态,而非UI状态。
远程编码 Agent 会在本地权限对话框上死锁
最恶劣的远程编码 Agent 失败模式不是糟糕的补丁。
而是一个谁也看不到的权限提示。
你在工作站上启动了一个长时间运行的作业,离开工位,之后用手机查看结果。Agent 执行到一条需要审批的命令。如果这个请求只存在于桌面 UI 的模态框里,作业从技术上讲并没有失败。它只是永远停在那里了。
这更糟。失败的作业是可以被观察到的。隐藏的等待在外人看来一切正常,直到有人发现没有任何工作往前推进。
权限提示是协议状态
修复从一个小小的观念转变开始:如何对审批进行建模。
权限请求不是 UI 状态。它是由执行工作的作业拥有的持久状态。
生命周期应该长这样:
asked → persisted → surfaced → answered → applied → resolved
桌面对话框、手机屏幕、CLI 或 Web 控制器只是该状态的一种视图。关闭窗口不应将其抹去。重新连接不应产生第二个请求。两个控制器不应因为屏幕上碰巧有个过期的按钮就解决掉不同的请求。
这也改变了远程控制协议需要具备的能力。控制器应该能够获取带有待审批的作业状态、针对某个请求 ID 提交答案、观察产生的事件。它不需要为了点击"允许"而成为文件系统或运行时的代理。
断线后需要保留什么
至少,待处理的请求需要一个稳定的请求 ID、其所属的作业/会话、请求的操作和资源,以及足够的排序信息来确定性渲染并发请求。
答案同样需要一个身份。
如果请求 abc 处于待处理状态,针对 xyz 的答案必须失败。重放 abc 的同一个答案是无害的。重放同一个 ID 下不同的答案不应悄无声息地覆盖第一个决定。
这听起来很挑剔——直到手机在网络不稳定时重新连接并重试了上一条命令。这时,幂等性和"Agent 执行了两次"之间就是天壤之别。
邮箱也需要有上限。一个损坏的或恶意的工具不应能够在一个无人值守的主机上填入无限数量的序列化审批请求。
恢复 Worker 是单独的一步
持久化一个"已批准"标志是不够的。
正在运行的 Worker 必须消费这个答案、将其应用到具体那个待处理的请求上、记录回复来自何处,然后才能将请求从邮箱中移除。如果 Worker 在这些步骤之间崩溃,恢复机制应该能够判断答案是排队中、已应用、还是已完全解决。
回复来源也很重要。用户批准、自动策略、系统拒绝不是同一种审计事件——即使它们都解锁了同一个未来。
当权限事件流消失时,安全的行为不是假定已批准。待处理的工作应该失败关闭,或以明确的理由取消。否则传输故障会悄无声息地转化为更广泛的授权。
UI 是最简单的一部分
一旦协议存在,手机 UI 真的可以只有两个按钮:批准和拒绝。
但这两个按钮只占百分之五。困难的部分是让请求变得持久、路由到正确的会话、重放安全、可审计、有界限、且失败关闭。
还有一个边界值得明确保持:应用级别的工具审批不是操作系统安全授权。远程控制器可以批准 Agent 执行应用已有能力执行的操作。它不能正当地制造操作系统尚未授予的辅助功能、屏幕录制或类似的主机权限。
我正在开发 BitFun,这就是我们处理分离作业审批的方式:目标拥有一个持久化的权限邮箱;控制器按请求 ID 回答;Worker 应用回复时记录其来源并标记为已解决。同样的权限事件可以随后到达桌面和远程控制界面,而无需让控制器成为运行时。