Mac 睡眠和唤醒后如何管理代理权限
为 Mac 睡眠后的代理权限制定清晰规则,覆盖锁屏、合盖、唤醒、保管库访问、会话批准和夜间运行。

已批准的代理进程不应仅仅因为 Mac 唤醒后内存中仍保留着相同的进程,就自动继承原有权限。睡眠、显示器变黑、屏幕锁定、合上盖子和唤醒是不同的操作系统事件,但它们都带来同一个安全问题:批准这次运行的人,是否仍在场并且能够介入?
把这个问题当作授权决策,而不是电源管理细节。如果允许编程代理持有 API 或 SSH 权限,一次过期的批准就可能把普通的喝咖啡、通勤或夜间暂停,变成无人看管的凭据使用。解决方法不是堆出一座规则迷宫,而是建立一个小型状态模型、清楚的过期策略,以及能够覆盖那些人们通常会跳过的尴尬转换的测试。
睡眠不是一个单一事件
macOS 区分系统睡眠和显示器睡眠,这一点很重要,因为黑屏并不能证明代理已经停止。Apple 为 willSleep、didWake、screensDidSleep 和 screensDidWake 提供了单独的 NSWorkspace 通知;睡眠和唤醒通知不会携带用户数据来说明转换发生的原因。
笔记本电脑可能只是调暗或关闭显示器,而构建任务、网络传输或本地进程仍在继续。台式 Mac 也可以在没有活动显示器的情况下运行。连接电源和外设的笔记本电脑,行为可能与使用电池时不同。你不能根据一个像素变黑,就推断出权限决定。
设计时应分别使用以下四种事实:
- 显示器状态 表示屏幕是进入睡眠还是恢复唤醒。
- 电源状态 表示机器是否准备睡眠,或是否刚从睡眠中唤醒。
- 用户在场状态 表示会话是活动、锁定、已注销,还是已经切换到其他用户。
- 保管库状态 表示是否允许使用密钥。
团队经常把前三项压缩成一个名为 isAwake 的布尔值。这种捷径会泄露权限。屏幕睡眠时,代理可能仍在运行。机器也可能在锁屏界面唤醒。用户可以锁定屏幕,而不让系统进入睡眠。每种情况都需要单独定义预期结果。
正确的默认规则严格但容易解释:受保护操作需要已解锁的保管库,以及属于当前权限周期的当前批准。唤醒、锁屏、用户会话变化、手动撤销或保管库锁定,都可以推进这个周期。一旦周期推进,来自旧周期的调用就必须失败。
屏幕锁定应结束交互式权限
屏幕锁定是最明确的信号,说明交互式批准应该停止。Mac 可能继续运行,长期存在的终端进程可能仍保留套接字和内存,但批准操作的用户已经离开了交互会话。继续使用之前的一次点击,发送 API 请求或 SSH 命令,很难为其辩护。
这并不意味着每个代理都必须在锁屏时退出。是否终止本地计算是另一个决定。如果不需要经过需要凭据的网关,模型仍可以读取代码库、编译代码或准备补丁。边界在于外部操作。保留工作,移除权限。
这个区分很有用,因为它避免了全有或全无的选择。你不必在冻结代理和完全开放代理之间二选一。让代理继续执行本地工作区内安全的任务,然后让下一次受保护调用返回明确的拒绝:
authorization_denied
reason: authority_epoch_changed
required: unlock_vault_and_approve_session
有用的拒绝信息应说明发生了什么,但不能泄露秘密,也不能假装请求是因为网络问题失败。代理可以暂停、记录阻塞原因并等待用户,而不是反复尝试同一个可能造成破坏的命令。
不要因为锁屏来自空闲计时器,就为它设置例外。这种情况往往正是用户忘记代理仍在运行的时候。手动锁屏和自动锁屏表达了不同的人类意图,但都不能证明用户仍然能够批准生产环境变更。
只有一种狭窄情况可以考虑在锁屏后保留权限:用户明确授予了一个范围严格受限、专门用于无人看管任务的能力。这应该是独立的运行类型,而不是交互式批准中的隐藏例外。如果你的夜间自动化和白天由对话驱动的代理行为完全一样,就说明风险还没有被分开。
唤醒事件应开启新的权限周期
即使已批准的进程继续存在,唤醒也应使交互式批准失效。进程可能在睡眠前暂停,之后用相同的 PID、相同的环境变量和相同的打开文件描述符恢复。以上任何一点都不能证明旧的人类决定仍然适用。
权限周期是一个单调递增的值,用来标记授权可能有效的连续时间段。当系统跨过你关心的边界时,应在处理下一次操作前推进周期。进程不需要协商这一变化,而是在下一次请求时发现它。
最小授权记录可以是这样:
{
"session_nonce": "6a018d62-2e94-4d4a-9e79-1f4e4b5ca501",
"process_id": 84172,
"process_start_marker": "2026-07-22T14:03:18Z",
"signing_authority": "approved-agent-binary",
"authority_epoch": 27,
"approved_at": "2026-07-22T14:04:01Z",
"per_call_approval": false
}
进程 ID 只是其中一个字段。macOS 可能在进程退出后重新使用 PID,因此如果网关忘记清理旧记录,之后再次看到同一个数字,单独依靠 PID 就会有风险。应将批准绑定到新的 nonce 和已观察到的进程实例。使用代码签名权限作为可执行文件的身份信号,同时仍要求每个新进程运行都进行新的会话批准。
事件处理器看到 willSleep 时,应记录撤销意图,并立即停止接受新的受保护调用。Apple 表示,观察者最多可以延迟睡眠处理 30 秒,但不要利用这段时间完成待处理操作队列。应拒绝这些操作,或将其标记为已中断。用户没有授权在合盖期间进行最后一轮操作。
机器报告唤醒后,如果需要就再次推进周期,并保持保管库闸门关闭,直到用户完成解锁要求。这样可以应对不完美的事件顺序。电源事件在边缘情况下很复杂,比起让一个带有秘密的调用在转换期间漏过去,额外要求一次批准更安全。
合盖需要单独测试
对人来说,合上笔记本盖子就像发出睡眠命令,但软件不应假定这个物理动作会清晰地对应某一个操作系统事件。电源来源、外接显示器、扩展坞和系统设置都可能改变机器行为。唯一诚实的做法,是测试团队实际使用的硬件和配置。
安全策略仍然可以很简单:一旦观察到可靠的相关边界,合盖就结束交互式权限。在普通便携设备配置中,willSleep 会给你一个提前阻止新操作的机会。如果某种配置在合盖后仍保持唤醒,就使用用户会话或显示器边界作为保守的后备措施。不要等待一个名为 lidClosed 的完美语义标签。你真正需要阻止的是无人看管的凭据使用。
让代理持有一个无害的凭据能力来进行测试,例如向测试 API 写入标记,或对一次性 SSH 主机运行无害命令:
- 启动新的代理进程并批准其会话。
- 确认一次受保护调用成功,然后让代理准备执行下一次调用。
- 合上盖子,保持足够长时间以触发预期的电源行为,然后重新打开。
- 不解锁,也不重新批准,让代理重试受保护调用。
- 确认网关拒绝这次重试,并且在拒绝前记录了权限转换。
如果团队会使用这些模式,请在连接扩展坞、使用电池和连接外接显示器时重复测试。测试端点必须无害。目的在于观察权限行为,而不是发现生产部署能否在执行到一半时被中断。
这里的失败通常看起来非常正常:操作日志显示睡眠期间没有调用,然后唤醒后的第一次调用成功。如果用户面对的是锁屏界面,却从未批准恢复后的运行,这仍然是失败。时间间隔正是测试的重点。
单独依靠显示器睡眠并不适合作为撤销触发器
仅在显示器睡眠时撤销权限是安全的,但对于显示器会在日常工作中进入睡眠的台式 Mac 来说,可能过于打扰。单独让权限跨过显示器睡眠虽然方便,但当显示器超时同时也意味着用户无人看管时,很容易出错。
选择一条规则,并明确说明取舍。对于高影响凭据,应在屏幕锁定和系统睡眠时撤销,而不是只在显示器睡眠时撤销。如果某台机器的显示器睡眠总是可靠地发生在锁屏前,你也可以选择在显示器睡眠时额外撤销,但要接受更多批准提示。关键是不要把任何一种选择称为「显而易见」。它取决于部署环境和凭据能够造成的影响。
Apple 将屏幕通知和系统通知分开提供,这正好提醒我们不要假定两者含义相同。监控两者,分别记录两者,并测试你附加到每一种事件上的策略。
一个实用的矩阵可以避免规则变成口口相传的经验:
| 转换 | 本地代理工作 | 现有会话批准 | 依赖保管库的操作 |
|---|---|---|---|
| 显示器进入睡眠 | 可以继续 | 由你声明的策略决定 | 通常暂停,或只允许低风险使用 |
| 屏幕锁定 | 可以继续 | 结束 | 拒绝,直到重新批准 |
| 系统开始睡眠 | 自然暂停 | 立即结束 | 拒绝新的调用 |
| 系统在锁屏界面唤醒 | 可以在本地恢复 | 保持结束状态 | 拒绝,直到解锁并批准 |
| 用户解锁 | 可以继续 | 仍然结束 | 要求新的会话批准 |
用户解锁 Mac 是为了重新进入桌面。这一动作不应悄悄恢复代理之前的权限。解锁电脑和批准外部操作有关联,但回答的是两个不同的问题。
夜间运行需要独立的约定
夜间运行的代理不应继承下午交互式会话的权限。人们在查看差异、终端或请求卡片时批准交互式工作。夜间运行则是明确决定让某件事在没有即时监督的情况下继续。
把任务约定限定到可以用一句话解释的程度。「运行测试并准备一个拉取请求」是清楚的。「完成任务所需的事情都做」不是约定,而是一张空白支票。
对于无人看管的工作,应把本地操作和外部操作分开。如果工作被限制在本地,可以允许仓库分析、分支上的编辑、运行测试和生成构件。生产 API 变更、部署、发布软件包、写入共享数据库或通过 SSH 访问重要机器等敏感外部影响,都应要求新的人类批准。
有些夜间任务确实必须调用外部服务。此时应使用专用凭据,或使用影响范围与任务相匹配的测试环境。不要因为管理员凭据已经存在于保管库中,就重复使用它。常见的理由是用户当晚早些时候已经批准了代理。但那次批准针对的是可见的交互式运行,不包括用户入睡后剩下的所有工作。
如果任务确实需要重复执行外部操作,可以设置有边界的操作预算。边界应按目标、方法和影响来定义,而不是依赖模糊的置信度分数。例如,无人看管的测试任务可以向一个固定的预发布端点发送固定请求,但不能切换主机、改变 HTTP 方法或使用 SSH。如果任务需要更多权限,就暂停等待。
Sallyport 的逐会话授权会为每个新的代理进程建立独立的批准边界。让夜间工作在新启动的进程中进行,并让它的受保护操作与其他无人看管的运行一样接受审查。
日志必须证明拒绝,而不只是记录活动
一条写着「Mac 已唤醒」的记录,不能证明权限已经结束。你需要边界两侧的证据:触发策略转换的操作系统事件,以及网关拒绝的下一次受保护操作。
macOS 为电源侧调查提供了一个有用的起点:
pmset -g log | grep -E 'Sleep|Wake|DarkWake|Display'
具体行会因硬件和 macOS 版本而异,但输出应包含带时间戳的记录,以及 Sleep、Wake、DarkWake 或显示器转换等事件名称。每次测试都保存相应时间窗口。不要把它作为授权的真实来源,因为它不了解你的保管库或代理身份。
你自己的日志应回答另一组问题:
14:20:16.402 session_approved process=84172 epoch=27 authority=approved-agent-binary
14:22:04.118 power_will_sleep epoch=27
14:22:04.119 authority_revoked old_epoch=27 new_epoch=28 reason=system_sleep
14:25:38.771 power_did_wake epoch=28
14:25:44.025 action_denied process=84172 request=ssh.exec reason=authority_epoch_changed
顺序很重要。如果操作出现在撤销记录之前,就说明发现了竞态。如果没有拒绝记录,只是因为测试代理悄悄退出了,那么你无法证明持久进程的行为。应安排测试,让同一个长期运行的进程在每次转换后尝试受保护调用。
防篡改审计轨迹还有第二个好处:它让你可以事后确认事件和操作顺序没有被改写成更好看的故事。Sallyport 会从写入盲化、加密并采用哈希链的审计日志生成 Sessions 和 Activity 日志,而 sp audit verify 可以在离线状态下对密文验证哈希链。这对测试很有用,因为验证成功说明记录顺序没有被悄悄改写,但它不能弥补薄弱的撤销策略。
竞态条件发生在边界处
最危险的错误通常出现在 Mac 开始睡眠或用户锁屏时,请求已经在执行中。只在接受连接时检查批准的网关,可能让排队的工作在权限转换后继续执行。只在注入凭据后检查的网关,则可能在发现撤销前,已经把凭据泄露给辅助程序。
应在特权操作开始前立即检查权限。也就是说,在注入凭据前、打开带有可用密钥的 SSH 辅助程序前,以及发送 HTTP 请求前都要检查。如果一个操作有多个特权阶段,就应在系统可能跨过权限边界的每个阶段再次检查。
基本流程可以是这样:
receive request
identify process and session nonce
read current authority epoch
compare request grant epoch to current epoch
check vault gate
check per-call approval when required
inject credential and execute action
append result to audit log
让权限周期的读取和提交特权执行尽可能靠近。分布式 HTTP 操作一旦字节离开 Mac,就无法做到完全可逆,但你可以阻止旧授权启动它。
不要试图通过延迟睡眠来解决所有边界情况。Apple 的 willSleep 通知允许短暂延迟处理,但为了完成代理操作而阻止笔记本睡眠,反而颠倒了优先级。机器正在离开交互状态。撤销访问、记录中断,让用户决定恢复什么。
对于可能造成高额成本或不可逆影响的密钥,逐次调用批准是更清楚的办法。因为每次使用都会在使用时询问用户,睡眠和唤醒就不那么复杂了。这不是跳过会话撤销的理由,而是针对较小范围凭据的第二道防线。
测试人们真正会执行的转换
好的测试计划不会从通知处理器的单元测试开始。单元测试有帮助,但故障往往存在于真实的电源转换、真实的锁屏界面,以及能够超出终端窗口生命周期的代理进程中。
构建一个能够等待、接收信号,然后请求无害受保护操作的测试代理。每次测试都使用新的运行标识符。运行前写下预期结果,否则意外结果事后很容易被解释成「应该没问题」。
在每种受支持的 Mac 配置上,至少测试以下情况:
- 代理空闲时锁定屏幕,然后在解锁前后分别重试已批准的操作。
- 从 Apple 菜单让系统进入睡眠,唤醒到锁屏界面,再使用原进程重试。
- 在系统保持唤醒时让显示器进入睡眠,确认行为符合你选择的显示器策略。
- 合上并重新打开笔记本盖子,分别测试电池模式和团队实际使用的桌面配置。
- 让一个明确设置为长时间运行的代理过夜,然后检查电源日志、操作日志和恢复后的第一次受保护调用。
为每次测试填写一张简短结果表:预期权限周期、观察到的电源事件、原进程是否存活、保管库是否锁定,以及第一次受保护调用是否被拒绝。这可以发现一个常见错误:团队测试了应用是否收到事件,却从未测试操作层是否在之后拒绝调用。
还要测试糟糕的时序。在受保护操作等待网络响应时锁定屏幕,然后检查重试或后续操作是否使用旧授权运行。在睡眠前极短时间内启动操作。断开并重新连接扩展坞。唤醒后重启代理进程,确认它无法借用旧进程的批准记录。
要通过这些测试,你不需要庞大的策略引擎。你需要严格的保管库闸门、逐进程批准记录、可撤销的权限周期,以及在使用凭据前立即检查这些条件的操作路径。
先写清结果,再强制执行策略
好的权限策略可以写在一页纸上,因为它描述的是可观察结果,而不是一堆猜测出来的操作系统标签。明确锁屏、睡眠、唤醒、合盖、注销和手动撤销后,受保护操作会发生什么。明确本地工作能否继续,以及用户必须做什么才能恢复。
对于大多数交互式 AI 编程代理,策略可以这样写:
受保护操作需要已解锁的保管库,以及当前进程在当前权限周期内获得的批准。锁屏、睡眠、用户会话丢失、保管库锁定和手动撤销都会结束该批准。唤醒和解锁不会恢复它。代理必须在下一次受保护操作前请求新的批准。
这条规则的代价是,人们回到 Mac 后需要再次批准。接受这个代价。另一种做法,是要求用户记住合上盖子前仍在运行的每个代理进程,然后相信它们能在数小时后机器唤醒时表现得合乎预期。
在把代理设置称为适合无人看管工作之前,先运行这些测试。如果锁屏或唤醒后请求无需用户做出新决定就成功了,你就找到了一个存活时间超过授权时刻的权限。
常见问题
Mac 锁定后,AI 代理还应该保留访问权限吗?
除非有经过充分测试的特殊理由,否则应把锁屏视为权限边界。桌面锁定后,进程、网络连接和已批准的代理可能仍然存在,但授予批准的人已经不在交互会话中。对于任何需要凭据的操作,解锁后重新批准通常是更安全的规则。
合上 MacBook 盖子是否总会结束代理会话?
合上笔记本盖子是一种意图信号,不只是显示屏事件。它通常会触发睡眠,但电源条件、外接显示器和系统设置可能改变实际行为。请在团队支持的设备上测试真实的合盖场景,并在观察到的第一个可靠边界处撤销权限。
显示器睡眠和系统睡眠对代理安全有什么区别?
不是。显示器睡眠只说明屏幕变暗,Mac 仍可能保持唤醒,进程也可能继续运行。系统睡眠意味着机器进入更深的电源转换,但你仍需决定唤醒后是否恢复旧权限,还是开启新的权限周期。
Mac 唤醒后,AI 编程代理可以继续运行吗?
代理可以在唤醒后继续运行,但操作层必须先为它发放新的授权。不要因为同一个进程仍然存在,就让旧批准悄悄延续。你可以让本地计算工作继续,但应在用户解锁并重新批准前暂停所有需要凭据的调用。
如何让 AI 代理在 Mac 上运行一整夜?
只有在允许执行的工作范围明显窄于交互式权限时,夜间运行才比较安全。如果符合你的风险承受能力,可以让代理修改本地分支、运行测试或准备报告。部署、生产 API、SSH 访问和其他凭据使用,都应等你回来后重新批准。
如何测试 Mac 的睡眠和唤醒事件?
每次测试后使用 pmset -g log,保存相关日志,并与自己的操作日志对照。它有助于区分显示器事件、睡眠、唤醒和电源转换,但不能证明代理已经失去权限。你的操作网关必须记录撤销动作,并拒绝下一次受保护调用。
代理批准是否应该在固定时间后过期?
不要把计时器作为主要的过期规则。超时可能在长时间构建期间造成中断,却又让短暂离开时的权限保留太久。应将撤销绑定到可观察的安全边界,再把时间限制作为异常长会话的后备措施。
进程 ID 足以识别已批准的代理吗?
仅凭进程 ID 不够安全,因为进程退出后,系统可能重新使用相同的 ID。应将授权绑定到新观察到的进程实例、代码签名权限、启动环境和由网关持有的随机会话 nonce。进程退出,或任何权限边界使授权失效时,都要撤销该记录。
代理授权记录应该包含什么?
每次批准和每次受保护操作都应保存权限周期。发生唤醒、锁屏、注销、保管库锁定或手动撤销时,应先推进权限周期,再接受新的调用。即使进程仍在运行,携带旧周期的请求也必须失败。
如何为 AI 代理制定睡眠和唤醒测试计划?
使用一个真实的长期运行代理进程、一个已批准的低风险端点,以及一个会产生明显但无害结果的受保护端点。锁屏、让 Mac 睡眠、唤醒、合上盖子,并让它运行一整夜。对每次转换,都要验证事件记录和下一次受保护调用。只有日志而没有拒绝测试是不够的。