环境
- macOS 桌面版 codeg(两台 Mac)
- codeg 版本:0.24.0
- 场景:Mac A 的桌面版通过 remote desktop transport 连接远程 Mac B 上运行的桌面版 codeg
复现步骤
- Mac A 通过 remote desktop 功能连接 Mac B 上的 codeg
- 仅将 Mac A(客户端)合上盖子,使其进入系统睡眠;Mac B(服务器)始终运行,从未合盖
- 等待一段时间(约 10 秒以上)后打开 Mac A 的盖子唤醒
现象
- Mac A 睡眠唤醒后连接已断开,且没有任何提示(无弹窗、无错误提示),仅表现为连接不可用/界面无响应
- 唤醒后不会自动重连
- 必须重启 codeg(或手动重新连接)才能恢复与远端机器的连接
根因分析## 根因分析
已证实的事实(双端日志取证,2026-08-12)
- Mac A(客户端)08-11 23:44:47 合盖进入
Clamshell Sleep(本机 pmset 记录,持续约 88 分钟),08-12 08:15:55 开盖唤醒(Wake from Deep Idle, due to lid)。
- 整个睡眠期间服务端从未断开连接:Mac B 日志显示连接
b91c1723 一直挂载,无 disconnect、无 idle sweep 记录;Mac B 从未睡眠(pmset 无 Sleep/Wake 记录)。
- Mac A 唤醒后没有自动重连成功:Mac B 侧直到 08:17:39 才收到新连接,而在此之前 08:16:34 用户已手动重启 Mac A 的 codeg。
- 重启后连接与会话均顺利恢复(会话
6fba7d87 恢复为 Connected → Prompting),说明 agent 会话从未被回收。
结论:断连/不恢复完全发生在客户端侧;服务端连接始终健在,问题既不是服务端主动断开,也不是会话被 idle sweep 回收。
可疑机制(源码 + 历史日志佐证,待复现实锤)
src-tauri/src/commands/remote_proxy.rs 中 WS_RECONNECT_FAIL_THRESHOLD = 3,退避节奏 1s → 2s → 4s,即约 7 秒内连续 3 次连接失败后彻底放弃重试。客户端历史日志(07-30、08-06、08-08、08-09)多次出现相同模式:[RemoteProxy] WS read error → 间隔 1s/2s 的两次 WS connect failed(Connection refused 等网络层错误)→ 停止,再无后续重试记录。"有限重试、失败即停"是实际行为,且网络层错误同样触发停止,并非只有 401。
- 该文件把"连续连接失败(网络不可达)"与"401 认证失败"合并成同一个
__unauthorized__ 信号(注释原文:"Rust gave up after WS_RECONNECT_FAIL_THRESHOLD failures, OR the remote rejected the handshake. Either way, surface as expired"),导致网络问题可能被误报为 token 过期。
- 前端
src/lib/transport/remote-desktop-transport.ts 收到 __unauthorized__ 后立即触发 onUnauthorized 弹出"连接过期",不再重试,用户只能手动重连或重启。
- 与浏览器路径
src/lib/transport/web-transport.ts 形成对比:后者是永不放弃重试(指数退避,上限 32s,注释明确覆盖 laptop sleep 场景)。两条路径行为不一致,疑似设计缺陷。
待实锤的环节
Mac A 唤醒后(08:15:55)到用户重启(08:16:34)之间约 39 秒内,客户端是否触发了重连、是否 3 次失败后停止——事发当天的客户端日志文件缺失,无法直接确认。下次复现时需保留当天日志,搜索 [RemoteProxy]、Reconnecting、__unauthorized__ 关键词。
另外,用户确认唤醒后没有任何提示——如果重连真的触发了 3 次失败并发射 __unauthorized__,前端应弹出"连接过期"提示(见上文可疑机制第 3 点)。无提示更符合另一种可能:客户端根本没有检测到连接失效(半开连接未被发现,codeg 无心跳、无 TCP keepalive),连接静默挂死,直到用户重启。这两种可能需要在下次复现时区分。
期望行为
- 唤醒后客户端应像
web-transport.ts 一样持续退避重试,直到远端恢复;
- 网络不可达不应被误判为 token 过期;
- 若确实需要放弃,等待时间应远大于一次睡眠的常见时长,而非约 7 秒。
临时缓解(供其他遇到同样问题的人参考)
- 客户端 Mac A 用
caffeinate -dimsu 防睡眠,或调整系统设置使合盖不进入睡眠(本次取证中会话并未被回收,调大 CODEG_ACP_IDLE_TIMEOUT_SECS 对这个问题无效);
- 如需协助复现取证:复现前备份
~/Library/Application Support/app.codeg/logs/ 整个目录,唤醒后第一时间检查 [RemoteProxy] 相关日志。
环境
复现步骤
现象
根因分析## 根因分析
已证实的事实(双端日志取证,2026-08-12)
Clamshell Sleep(本机 pmset 记录,持续约 88 分钟),08-12 08:15:55 开盖唤醒(Wake from Deep Idle, due to lid)。b91c1723一直挂载,无 disconnect、无 idle sweep 记录;Mac B 从未睡眠(pmset 无 Sleep/Wake 记录)。6fba7d87恢复为 Connected → Prompting),说明 agent 会话从未被回收。结论:断连/不恢复完全发生在客户端侧;服务端连接始终健在,问题既不是服务端主动断开,也不是会话被 idle sweep 回收。
可疑机制(源码 + 历史日志佐证,待复现实锤)
src-tauri/src/commands/remote_proxy.rs中WS_RECONNECT_FAIL_THRESHOLD = 3,退避节奏 1s → 2s → 4s,即约 7 秒内连续 3 次连接失败后彻底放弃重试。客户端历史日志(07-30、08-06、08-08、08-09)多次出现相同模式:[RemoteProxy] WS read error→ 间隔 1s/2s 的两次WS connect failed(Connection refused 等网络层错误)→ 停止,再无后续重试记录。"有限重试、失败即停"是实际行为,且网络层错误同样触发停止,并非只有 401。__unauthorized__信号(注释原文:"Rust gave up after WS_RECONNECT_FAIL_THRESHOLD failures, OR the remote rejected the handshake. Either way, surface as expired"),导致网络问题可能被误报为 token 过期。src/lib/transport/remote-desktop-transport.ts收到__unauthorized__后立即触发onUnauthorized弹出"连接过期",不再重试,用户只能手动重连或重启。src/lib/transport/web-transport.ts形成对比:后者是永不放弃重试(指数退避,上限 32s,注释明确覆盖laptop sleep场景)。两条路径行为不一致,疑似设计缺陷。待实锤的环节
Mac A 唤醒后(08:15:55)到用户重启(08:16:34)之间约 39 秒内,客户端是否触发了重连、是否 3 次失败后停止——事发当天的客户端日志文件缺失,无法直接确认。下次复现时需保留当天日志,搜索
[RemoteProxy]、Reconnecting、__unauthorized__关键词。另外,用户确认唤醒后没有任何提示——如果重连真的触发了 3 次失败并发射
__unauthorized__,前端应弹出"连接过期"提示(见上文可疑机制第 3 点)。无提示更符合另一种可能:客户端根本没有检测到连接失效(半开连接未被发现,codeg 无心跳、无 TCP keepalive),连接静默挂死,直到用户重启。这两种可能需要在下次复现时区分。期望行为
web-transport.ts一样持续退避重试,直到远端恢复;临时缓解(供其他遇到同样问题的人参考)
caffeinate -dimsu防睡眠,或调整系统设置使合盖不进入睡眠(本次取证中会话并未被回收,调大CODEG_ACP_IDLE_TIMEOUT_SECS对这个问题无效);~/Library/Application Support/app.codeg/logs/整个目录,唤醒后第一时间检查[RemoteProxy]相关日志。