问题
RunManager.execute() 的结算段不在任何 try/catch 保护范围内。一旦结算期间任一存储写入抛错(例如 JSON 文件损坏触发 STORAGE_WRITE_FAILED——仓库近期刚修过组合文件损坏 #147,说明这是现实场景),execute 的 promise 会以 unhandled rejection 失败,this.activeRun 永远不会被清空。
后果是进程级死锁:
isRunning() 从此恒为 true,之后每一次 startRun 都被 RUN_IN_PROGRESS 拒绝,直到重启;
- 事件订阅者收不到任何终端事件(eval harness 等待方会一直挂到超时);
- run 记录永远停留在
running。
当前现状
代码审查基线:039c7388f855ab85f9882300c16b1898fe5955bb。
packages/shared/src/kernel/run-manager.ts:287:void this.execute(run, ...) fire-and-forget,无 .catch;
- 行 352–354 的
catch 只覆盖 ensureSession + 事件循环消费;
- 行 356–428 的结算代码(
runs.update、appendMessage、updateSession、clearWallClockTimer、this.activeRun = null、终端事件合成)全部在 try 之外,其中任何一个 await 抛错都会绕过所有清理逻辑。
期待的解决方向
- 结算持久化(run 记录、assistant message、session 状态)降级为尽力而为:单个写入失败不阻断其余清理;
- 无论持久化成败,
clearWallClockTimer、this.activeRun = null 与终端事件合成必须发生——订阅者总能观察到 run_completed/run_failed;
- 补充测试覆盖"结算写入失败"场景。
验证标准
问题
RunManager.execute()的结算段不在任何 try/catch 保护范围内。一旦结算期间任一存储写入抛错(例如 JSON 文件损坏触发STORAGE_WRITE_FAILED——仓库近期刚修过组合文件损坏 #147,说明这是现实场景),execute的 promise 会以 unhandled rejection 失败,this.activeRun永远不会被清空。后果是进程级死锁:
isRunning()从此恒为true,之后每一次startRun都被RUN_IN_PROGRESS拒绝,直到重启;running。当前现状
代码审查基线:
039c7388f855ab85f9882300c16b1898fe5955bb。packages/shared/src/kernel/run-manager.ts:287:void this.execute(run, ...)fire-and-forget,无.catch;catch只覆盖ensureSession+ 事件循环消费;runs.update、appendMessage、updateSession、clearWallClockTimer、this.activeRun = null、终端事件合成)全部在 try 之外,其中任何一个await抛错都会绕过所有清理逻辑。期待的解决方向
clearWallClockTimer、this.activeRun = null与终端事件合成必须发生——订阅者总能观察到run_completed/run_failed;验证标准
isRunning()恢复false,终端事件仍被广播;startRun不再被RUN_IN_PROGRESS拒绝;