ShellSession 退出后超时
ShellSession 顶层 exit 后,EOF 没结束收集,仍等待完成标记而超时。单任务按条件使用子 shell,保留退出码、会话状态与原有安全限制。
Agent 的持久 ShellSession 执行顶层 exit 7 后,shell 已退出,却一直等到 command_timeout,最后报“Command timed out … restarting shell session”。原本的退出码与输出可能没有按正常结果返回。
这一条件有 langchain 1.4.4、Linux Bash 的反馈,已核对的正式 1.4.5 仍保留相同完成标记与 EOF 处理。不是命令字符串里出现 exit 字样就一定触发;关键是命令真正终止了承载会话的 shell。
会话来不及写完成标记
ShellSession 把命令写入持久进程后,再写一条带唯一标记的 printf,通过它收集上一条命令的 $?。顶层 exit 会结束这个进程,后面的标记便没有机会执行。
若写入阶段已经发现 BrokenPipe,会走退出后的收集分支;若写入先成功、shell 随后才退出,普通收集分支拿到 reader 的 EOF 标记后却继续等待,直到 deadline。相同退出操作可能因时序不同表现不同,不能只用一次未超时来排除这条路径。
因此加长 command_timeout 不会让已结束的 shell 重新发出标记,只会延后错误判定。也不能把这类超时都当成实际命令仍在运行;需要对齐进程状态、EOF、标记和退出码。
不让单次任务结束父会话
业务只是想结束当前任务、返回一个状态码,而不是销毁持久会话时,可以在 POSIX shell 中把完整任务放到括号子 shell。最小形式是:
( exit 7 )
这里展示的是命令形状。exit 结束子环境,父会话仍可继续执行完成标记,分组的状态沿原 $? 路径返回。花括号 { ...; } 在当前环境执行,不能代替这层隔离。
这要求父会话没有因非零状态又主动退出的配置,例如相关 set -e 或退出 trap。已有错误处理、管道、后台进程和信号规则也要保留;不是给任意命令外面加一层括号就自动得到相同行为。
子 shell 中的 cd、变量赋值等环境改变不会留到后续会话命令。原流程依赖这些持久状态时,应分别安排父会话初始化与单次任务执行,不能直接整体套括号后继续假定工作目录已改变。文件写入等外部副作用仍会发生,子 shell 也不是安全沙箱。
若业务确实要关闭整个会话,应走应用明确的会话生命周期管理。后续再创建或重启时,核对 startup 初始化与原来环境,不把一次顶层 exit 当成普通工具完成后继续复用已死亡的进程。
保留真实失败状态
获准的无副作用验收应覆盖正常完成、子任务非零退出、真正的慢任务超时与会话关闭。非零退出应保留实际 exit_code 并进入原来的失败契约,不能改成零来证明“不超时”;真正超时也应维持原终止与资源回收要求。
采用后续版本时,应对照普通收集、BrokenPipe、EOF 与退出码等待几个分支,并核对输出截断和脱敏流程。
发生过这类超时的任务,还要先确认已经产生的文件或外部结果。重启 shell 不会撤销副作用,不能让 Agent 因为没拿到完成标记就自动再执行一遍。


