Skip to content

fix(server): 修复热会话复用时旧终态导致 SSE 提前结束 - #1

Merged
anyJohn merged 1 commit into
anyJohn:mainfrom
sxy1998123:codex/fix-warm-session-event-replay
Sep 18, 2026
Merged

anyJohn merged 1 commit into
anyJohn:mainfrom
sxy1998123:codex/fix-warm-session-event-replay

Conversation

@sxy1998123

Copy link
Copy Markdown
Contributor

同一会话在热缓存有效期内连续提交任务时,第二轮 /run 会重放上一轮的 Done / Error / Stopped。Web 客户端遇到终态便停止消费 SSE,因此界面提前结束等待,后台实际仍在执行新任务;重新挂载 /stream 也会遇到旧终态。

takeWarm() 成功命中、下一轮 register() 初始化序号之前清空临时事件历史,使新一轮事件从 seq=0 开始。保留同一个 Agent、会话消息和缓存连接;冷启动初始化事件仍正常重放。

回归测试覆盖连续两轮 /run、运行中 /stream 重挂、真实终态前持续等待、冷启动事件保留、会话消息保留,以及三种缓存终态的清理。使用真实 Hono 路由和 EventStream,模型与持久化使用测试替身,无外部模型调用。

验证(Windows,Node 24.19.0):

  • 修复前:新增路由回归 2 项及缓存断言失败;修复后相关 4 个测试文件共 26 项通过。
  • Web 全量测试:129 项通过。
  • domain / server / web TypeScript 检查通过。
  • domain / server / web / tui 构建及 desktop build:main 通过,与 CI 的构建范围一致。
  • server 全量测试:48 项通过,3 项 Windows 平台失败。已恢复原始 origin/main 的生产实现复查,同样 3 项失败:/tmp 路径假设,以及两项只设置 HOME、但 Windows os.homedir() 使用 USERPROFILE 的配置测试。测试进程的 HOME/USERPROFILE 均指向临时目录。

@anyJohn
anyJohn merged commit 3828c7a into anyJohn:main Sep 18, 2026

@anyJohn anyJohn 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.

代码审查结论:修法正确且最小,已验证,可以合并(实际上已在 3828c7a merge,本 review 为事后复核 + 与本地修复的对照记录)。

修法评审

PR 在 takeWarm()eventHistory$.next([])——位置选得对

  • 清理动作收敛在缓存出口(唯一复用入口),而不是散在 /run 路由里;/stream?since=-1 重挂也天然受益(route 层无需感知);
  • BehaviorSubject.next([])destroy()eventStream.clear() 语义一致;
  • 清空后 registerseq 基线回到 0,与 fresh create 对齐——本轮发现的 seq 漂移与旧终态重放两个症状一起消掉。

与本地修复(77f2041,已被本 PR 取代)的对照

本地曾走 domain 侧 AnyAgent.resetForReuse()(busy 守卫 + abortController/activeRun 归位 + eventStream.clear())。对比后确认 PR 的最小修法足够

  1. busy 守卫:takeWarm 只从 warm 池取出(warmUp 保证闲态),server 单线程下 register 前无并发窗口——守卫属于防御性冗余;
  2. abortController/activeRun 残留:executeTask 正常尾部已置 null;stop 路径残留的 controller 在下个 executeTask 会重建——均无害;
  3. 冷启动初始化事件只随第一轮 /run 重放一次(清空后第二轮不再发)——语义上正确,这些事件本来就该只发一次。

测试质量

warmReplay.test.ts 用真实 Hono 路由 + 真实 EventStream(只 mock 模型与持久化),覆盖了本轮事故的完整链路:

  • 连续两轮 /run:第二轮 seq 从 0 起、客户端等到真实终态而非上一轮残留的 Done;
  • 运行中 /stream 重挂:只重放当前 run 的事件;
  • 会话消息跨 run 保留(缓存复用的收益本体);三种终态参数化 + 缓存清理。
    observe() 模拟 Web 客户端“遇终态即停消费”的行为,直击 bug 的用户可见面。

已知边界(非阻塞,备忘)

  1. PR 测试未覆盖影子事件(ToolStart 等)与重放过滤(replayable)的交互——两者正交,当前无叠加风险,但未来改重放过滤时建议补一条用例;
  2. eventHistory$.next([]) 直接从 Manager 触达 agent 内部流——下一个可维护性改进是把重置收敛为 domain 公开方法(如 resetForReuse),避免 Manager 与 domain 内部结构耦合;当前规模下可接受。
  3. Windows 平台 3 项测试失败与本 PR 无关(PR 作者已定位:/tmp 路径假设 + HOME/USERPROFILE 差异,origin/main 同样失败)。

本地回归(merge 后)

server 51(+4)/ web 129 / domain 349 全绿,domain/server tsc 干净。

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