Skip to content

fix(cli): ls 自动恢复休眠会话 - #912

Open
Alan-Paul wants to merge 2 commits into
deepcoldy:masterfrom
Alan-Paul:fix/list-wake-dormant
Open

fix(cli): ls 自动恢复休眠会话#912
Alan-Paul wants to merge 2 commits into
deepcoldy:masterfrom
Alan-Paul:fix/list-wake-dormant

Conversation

@Alan-Paul

@Alan-Paul Alan-Paul commented Aug 18, 2026

Copy link
Copy Markdown

改动内容

  • botmux ls/list 选中持久后端确认缺失的 Botmux 托管会话时,向所属 daemon 请求幂等唤醒,等待精确 tmux/ZMX target 恢复后再连接。
  • 新增内部 wake IPC;已有 live worker 时直接返回,不重启正在工作的会话,避免与飞书消息唤醒发生竞态。
  • wake HTTP、服务端 mutation acquisition 和 backend probe 共用一个 15 秒绝对 deadline;请求退出会 abort IPC fetch。
  • Ctrl-C/q/Esc 在 in-flight guard 前处理,恢复等待期间可立即退出 raw mode。
  • picker 明确显示“Enter 恢复并连接”;adopt、Riff、状态探测 unknown、transfer 和受保护 mutation 保持 fail closed。

问题原因

主机重启会清空 tmux/ZMX backing,但活跃会话记录和 transcript 仍然保留,可由 daemon 冷恢复。此前 list 会保留这些会话行,Enter 却只连接已经存在的 backing,因此本地入口不可用,而飞书新消息仍能触发恢复。

影响范围

  • 平台:共享 CLI/daemon IPC,macOS 和 Linux 使用同一逻辑。
  • CLI:不依赖具体 CLI adapter,Claude Code、Codex 等本地 CLI 共用。
  • 后端:仅 tmux/ZMX 可连接后端开放恢复;PTY、Riff、adopt 不开放。
  • 会话:仅 status=active、真实 Botmux 托管、backing 确认 missing 的会话;live worker 和受保护状态不被重启。
  • 可取消性:never-resolving HTTP 和被 admission 占用的 mutation gate 都有界;退出键主动中止客户端等待。

验证

  • pnpm build
  • pnpm exec vitest run --project unit test/session-list-wake.test.ts test/session-list-liveness.test.ts test/session-picker-responsive.test.ts test/dashboard-ipc.test.ts
  • 4 个测试文件,167 项测试全部通过。
  • never-resolving wake 下按 q 可在 1 秒内退出 raw mode;服务端 mutation 超时后不会延迟执行 forkWorker。
  • 本机真实验证:missing tmux 会话在 botmux ls 中按 Enter 后完成冷恢复,target online,成功 attach/detach,未发送新 prompt。

@Alan-Paul
Alan-Paul requested a review from deepcoldy as a code owner August 18, 2026 07:51

@deepcoldy deepcoldy left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

结论:请求修改,发现 1 个阻断问题。

[P2] 为 wake 请求和 backing 轮询使用同一个有界 deadline,并保留退出能力。

当前 wakeDormantBackendForAttach() 的 15 秒预算只覆盖 await options.wake() 返回后的 probe 循环;requestDormantSessionWake() 调用 fetchDaemonIpc() 时没有 AbortSignal/超时。服务端 /wake 又会在 bot 级 withBotTurnMutation() 中等待已有 admission 排空,因此 daemon 即使已接受连接,也可能长期不返回。与此同时 picker 在 raw mode 下先用 if (deleteInFlight || wakeInFlight) return 丢弃所有按键,Ctrl-C/q/Esc 都到不了 cleanup。结果不是“最多 15 秒无法取消”,而是一次恢复可能把当前终端无限卡死。

建议用一个覆盖 wake HTTP + probe 的绝对 deadline(并将 AbortSignal 传进 IPC fetch),同时让 Ctrl-C/q/Esc 在 in-flight guard 之前生效并中止等待;补一个 wake 永不 resolve/慢 gate 时仍能按 deadline 结束或取消的测试。

其余重点复核结论:

  • wake 与飞书 turn 分别经过 mutation/admission gate;任一方先到,另一方都会在状态稳定后重新检查,因此已有活 worker 时不会被 restart。
  • forkWorker() 从入口检查到 ds.worker = worker 没有 await,且 mutation gate 覆盖该调用;Node 单线程下这里不存在两个事件各自穿过 worker-null 检查后双 fork 的窗口。
  • 本地验证:pnpm build 通过;pnpm vitest run test/dashboard-ipc.test.ts test/session-list-wake.test.ts test/session-picker-responsive.test.ts test/session-list-liveness.test.ts 为 163 passed / 1 skipped。

@deepcoldy

Copy link
Copy Markdown
Owner

感谢这个 PR,把「主机重启后本地 botmux ls 入口无法唤醒休眠会话」这个不一致补齐了,思路和 fail-closed 的守卫(transfer/adopt/riff/protected mutation 各自 409、live worker 直接 already_running 不重启)都很扎实,命名两侧收敛、ZMX 仍走全 label+PID 校验的 attach 这些也都没问题。

自动评审发现 1 个建议在合入前修复的问题(liveness / 可取消性):

picker 在 wakeInFlight 期间可能让终端无限卡死,且无键盘逃生。

  • wakeDormantBackendForAttach 里的 timeoutMs(默认 15s)只 bound 了 await options.wake() 返回之后的 probe 轮询循环;wake 这一步的 HTTP(fetchDaemonIpc)本身没有 AbortSignal、也没有超时。
  • 服务端 /wake 包在 withBotTurnMutation 里,会 await waitUntilDrained:如果同一个 bot 有正在进行的飞书 turn(admission 会持有整轮)或另一个 mutation 在跑,wake handler 会阻塞到那一轮/那个 mutation 结束,可能是分钟级。
  • process.stdin.on('data') 的第一行 if (deleteInFlight || wakeInFlight) return 排在 Ctrl-C/q/Esc 处理之前,所以 in-flight 期间这些退出键都被丢弃 → raw-mode 终端可能长时间卡住、连 Ctrl-C 都退不出。

建议:

  1. 用同一个绝对 deadline 覆盖 wake HTTP + probe 两段,并把 AbortSignal 串到 IPC fetch;
  2. 把退出键(Ctrl-C/q/Esc)的处理移到 in-flight guard 之前,并在退出时中止在飞的等待;
  3. 补一个 slow / never-resolving wake 的取消或超时测试。

(这是自动评审的初步意见,最终以维护者审阅为准。)

@Alan-Paul
Alan-Paul force-pushed the fix/list-wake-dormant branch from e42305e to 98333cc Compare August 18, 2026 12:08
@Alan-Paul

Copy link
Copy Markdown
Author

已按建议修复并推送(98333cc):

  • 用同一个绝对 deadline 覆盖 wake HTTP、服务端 mutation acquisition 和 backend probe;AbortSignal 已传入 fetchDaemonIpc。
  • /wake 改用有界的 tryWithBotTurnMutation,并按客户端 deadline clamp;排队超时后不会稍后补跑 forkWorker。
  • Ctrl-C/q/Esc 已移到 in-flight guard 前;cleanup 会 abort 正在进行的 wake,避免 raw-mode 终端被锁住。
  • 新增 never-resolving HTTP 下按 q 退出的 PTY 回归,以及 never-resolving wake deadline、mutation gate timeout 的测试。

已在最新 master 上 rebase 后复验:pnpm build 通过,相关 4 个测试文件共 167 项全部通过。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants