Files
navop/docs/design
胡飞 45a75c5be4 fix(terminal): 修复 Shell Integration 握手无兜底导致 SSH 键盘输入被永久暂存 (#206)
运行时 Shell Integration 的就绪握手期间,actor 会把用户键盘输入压入
deferred_actor_inputs 暂存。四个暂存态(WaitingForFirstOutput / Injecting /
AwaitingPrompt / PlainAwaitingOutput)都只能靠远端输出推进,其中三个没有任何
超时兜底:远端一旦不再产出可解除握手的输出(登录 expect 永不匹配导致
should_inject 不触发、prompt 结束标记缺失等),会话就永久停在暂存态——远端
输出照常刷新、提示符与光标都正常,但按键(含 Enter / Ctrl+C)被静默吞掉,
界面没有任何提示。本地终端走 pty_backend、没有这道门禁,因此表现为
「同窗口本地终端正常、SSH 标签输入全死」。

- RuntimeShellIntegration 新增 phase_name() 与 force_release_input():把上述四个
  暂存态强制推进到 Plain(丢弃注入回显缓冲,此后不再抑制输出、不再注入),
  已可输入的阶段返回 false,不干扰正常会话
- actor 新增 30s 看门狗 SHELL_INTEGRATION_HANDSHAKE_TIMEOUT,到期调用
  force_release_input() 并置 shell_ready = true,下一轮循环按序重放 deferred
  队列;输入被暂存时打 terminal.ssh.runtime 的 debug 日志(含 phase 与队列长度),
  看门狗触发时打 terminal.ssh.setup 的 warn 日志
- 抽出 arm_shell_integration_handshake_watchdog() 供测试直接断言武装条件:
  不能写成 accepts_terminal_input().then(..)——bool::then 是「为真才取」,那样只在
  已可输入的会话里武装,握手态反而是 None,看门狗永不触发(等于没接上),且到期
  那侧 force_release_input() 返回 false 属无害空转,既不报错也不失败。状态机推进
  单向,启动时判定一次即可
- 设计与状态表同步更新到 docs/design/ssh-runtime-shell-integration.md

验证(在本提交所覆盖的工作区执行):

- cargo test -p terminal --lib => 491 passed / 0 failed
- 新增 handshake_watchdog_arms_exactly_when_input_is_deferred:把武装条件改回反向
  写法实测 FAILED,修正后 Green
- cargo clippy -p terminal --all-targets -- -D warnings:terminal 自身干净,剩余报错在
  agent_runtime、crates/ssh/src/host_key.rs 等未触碰文件,属既存 lint

已知限制:兜底只放行输入,不补发 init_commands——其发送判定在输出处理分支内,
远端彻底沉默时仍需再来一段输出才会发出。
2026-09-16 16:16:59 +08:00
..