Skip to content

fix: 修复手动停止后立即继续导致双 session 并发运行的竞态 bug#196

Merged
ErlichLiu merged 2 commits intoErlichLiu:mainfrom
SheldonLiu0412:fix/stop-resume-dual-session-race
Apr 7, 2026
Merged

fix: 修复手动停止后立即继续导致双 session 并发运行的竞态 bug#196
ErlichLiu merged 2 commits intoErlichLiu:mainfrom
SheldonLiu0412:fix/stop-resume-dual-session-race

Conversation

@SheldonLiu0412
Copy link
Copy Markdown
Contributor

@SheldonLiu0412 SheldonLiu0412 commented Apr 6, 2026

问题

手动停止正在运行的任务后,立即发送"继续"消息,会导致两个 session 同时在后端运行,表现为每次发送消息都同时触发两个会话。

日志审查发现

通过分析会话 50c759b0 的 JSONL 日志,确认了双流并发的实际证据:

  • 两组消息交错写入同一个 JSONL 文件,tool_use_id 完全独立,无重叠,证实是两个独立的 SDK 流在并行运行
  • 时间线重建:流B 运行期间(00:08:14 - 00:09:12),流C 也在并行运行(00:08:58 - 00:10:46),两个流各自独立执行工具调用和代码编辑

根因

两个竞态条件叠加导致:

  1. 前端竞态handleStop 立即将 running 设为 false,用户发"继续"触发新流(running=true)。旧流 abort 后的 STREAM_COMPLETE 延迟到达前端,无条件将 running 重置为 false —— 把新流的状态打掉了。前端误认为"已完成",用户再次操作即可触发第三个流。

  2. 后端竞态:旧流的 finally 块执行 activeSessions.delete(sessionId),误删了新流刚注册的条目,导致后端并发守卫(activeSessions.has(sessionId))失效。

修复

核心思路:给每次 sendMessage 调用分配一个 generation 时间戳,前后端都用它区分新旧流。

后端(agent-orchestrator.ts)

  • activeSessionsSet<string> 改为 Map<string, number>,value 为 generation 时间戳
  • finally 块只在 generation 匹配时才清理,防止旧流误删新流的注册
  • 所有 onComplete 回调传递 startedAt: runGeneration

IPC 层(agent-service.ts)

  • runAgentrunAgentHeadlessonComplete 都转发 startedAt 到前端

前端(useGlobalAgentListeners.ts)

  • STREAM_COMPLETE handler 通过比较 startedAt 区分新旧流
  • 如果当前流的 startedAt 比 complete 事件的更新,说明是旧流的 complete,忽略不处理

类型(@proma/shared agent.ts)

  • AgentStreamCompletePayload 增�� startedAt?: number 字段

附加修复:SubAgent 首次工具调用路径错误

问题

Agent/SubAgent 启动后,首次使用 Glob/Grep 等工具搜索附加工作目录中的文件时,经常因使用相对路径而失败(cwd 是会话目录而非项目源码目录),然后 fallback 到 Bash 命令用绝对路径重试,浪费一轮工具调用。

修复

agent-prompt-builder.ts 的工具使用指南中增加一条路径规则提醒,明确告知 Agent:cwd 是会话目录,操作附加工作目时必须使用绝对路径。


代码审查

  • 所有 activeSessions 用法与 Map 兼容(has/delete/size/clear 签名一致)
  • 6 处 callbacks.onComplete 调用均已传递 startedAt
  • startedAt 防护条件做了兜底:即使 data.startedAt 缺失(未来遗漏路径),只要当前流有 startedAt 也能防护
  • TypeScript 编译通过,无类型错误

影响范围

5 个文件,27 行新增 / 14 行删除。竞态保护逻辑不影响正常的发送、停止、流式推送等功能;提示词改动仅影响 Agent 工具调用行为。

SheldonLiu0412 and others added 2 commits April 6, 2026 21:05
通过 generation 时间戳区分同一会话的不同运行轮次,解决旧流的
STREAM_COMPLETE 和 finally 清理误伤新流状态的竞态问题。
Agent/SubAgent 的 cwd 是会话目录而非项目源码目录,导致使用 Glob/Grep 等工具时
若用相对路径会在第一次调用失败,然后 fallback 到 Bash 命令浪费一轮调用。
在工具使用指南中增加路径规则提醒,要求操作附加工作目录时必须使用绝对路径。

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@ErlichLiu ErlichLiu merged commit d1696fd into ErlichLiu:main Apr 7, 2026
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