fix(cli): Stop after cancelled ask_user_question#5218
Conversation
Stop ACP tool execution after ask_user_question is cancelled, including nested Agent cancellation paths. Co-authored-by: Qwen-Coder <[email protected]>
Resolve the Session.ts conflict after upstream moved the mid-turn queue drain method into acp-bridge shared types. Co-authored-by: Qwen-Coder <[email protected]>
DragonnZhang
left a comment
There was a problem hiding this comment.
No review findings. Downgraded from Approve to Comment: CI still running.
Well-structured fix: the RunToolResult wrapper cleanly propagates cancellation through all 4 tool-call sites (main, retry, background, subagent loops). The recordSkippedToolCall + appendSkippedAfter pattern correctly records skipped tool calls so the model sees them on resume. The activeToolAbortSignal fix (vs parent abortSignal) is the right scoping. Comprehensive test coverage. ✅ — qwen3-coder via Qwen Code /review
|
Qwen Code review did not complete successfully: Qwen review timed out after 85 minutes. See workflow logs. |
Address PR QwenLM#5218 review feedback by sending a failed ACP tool update for tool calls skipped after ask_user_question cancellation. Co-authored-by: Qwen-Coder <[email protected]>
Integrate the remote PR branch updates before pushing the review feedback fix. Co-authored-by: Qwen-Coder <[email protected]>
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
Additional finding (unchanged code, not in diff): firePreToolUseHook (line 3602) and firePostToolUseHook (line 3722, success path) pass the original abortSignal instead of activeToolAbortSignal. For Agent tools where a nested ask_user_question was cancelled, activeToolAbortSignal is aborted while abortSignal may not be. The PostToolUseFailure hook at line 3860 correctly uses activeToolAbortSignal, but the success-path hooks don't. Low practical impact today since the success hook is guarded by !aborted, but worth aligning for consistency.
— qwen3.7-max via Qwen Code /review
doudouOUC
left a comment
There was a problem hiding this comment.
— qwen3.7-max via Qwen Code /review
Co-authored-by: Qwen-Coder <[email protected]>
Co-authored-by: Qwen-Coder <[email protected]>
Co-authored-by: Qwen-Coder <[email protected]>
|
@qwen-code /triage |
|
Thanks for the PR @doudouOUC! Template looks good ✓ On direction: this fixes a real user-facing bug — after the 5-minute On approach: the scope feels right for what it's solving. The cancellation signal needs to propagate through four distinct layers (top-level prompt loop → sequential tool batches → concurrent Agent siblings → nested sub-agent questions), and the diff addresses each with a dedicated mechanism (abort controllers, One question worth thinking about: the Moving on to code review. 🔍 中文说明感谢 @doudouOUC 的贡献! 模板完整 ✓ 方向:这修复了一个真实的用户问题—— 方案:范围与解决的问题匹配。取消信号需要穿透四个不同层级(顶层 prompt 循环 → 顺序工具批次 → 并发 Agent 兄弟 → 嵌套子代理问题),diff 里每一层都有对应机制(abort controller、 一个值得思考的问题: 进入代码审查 🔍 — Qwen Code · qwen3.7-max |
ytahdn
left a comment
There was a problem hiding this comment.
Reviewed the cancellation propagation logic across Session.ts and SubAgentTracker.ts. The abort signal plumbing (parent → batch controller → per-tool controller → nested agent) is correct with proper cleanup in finally blocks. The RunToolResult wrapper and skipped-tool recording preserve replay history correctly.
No new issues beyond what existing inline comments already cover. Build passes, CI green across all platforms.
— qwen3.7-plus via Qwen Code /review
Code ReviewReviewed the diff against my independent proposal. The problem requires cancelling the turn at four distinct concurrency boundaries (top-level prompt loop, sequential batches, concurrent Agent batches, nested sub-agent questions), and the PR handles each with a dedicated mechanism. The No critical blockers found. Specific observations:
One minor note: the TestsUnit tests: 173/173 passed (Session.test.ts: 145, SubAgentTracker.test.ts: 28). The new tests cover all cancellation propagation paths: direct cancel, sequential skip, concurrent batch abort, nested agent cancel, sibling abort, unstarted skip, hook suppression, interrupt labeling, and rewrite-wait-before-end. Build: ✅ exit 0 (lint warnings are pre-existing in vscode-ide-companion, unrelated to this PR) Typecheck: ✅ exit 0 across all packages Real-Scenario TestingBasic smoke test — Before (installed build — main branch)After (this PR)Limitation: the specific bug (ask_user_question 5-minute timeout → model continues executing) requires an ACP client to cancel the permission request, which isn't reproducible in a simple tmux prompt. The 173 unit tests thoroughly cover this cancellation path at every concurrency boundary. 中文说明代码审查对照我的独立方案审查了 diff。问题需要在四个不同的并发边界(顶层 prompt 循环、顺序批次、并发 Agent 批次、嵌套子代理问题)取消 turn,PR 在每一层都有对应机制。 没有发现关键阻塞项。具体观察:
测试单元测试: 173/173 通过。新测试覆盖所有取消传播路径。 构建: ✅ exit 0(lint 警告是 vscode-ide-companion 的已有问题,与本 PR 无关) 类型检查: ✅ exit 0 真实场景测试基本冒烟测试——前后对比 限制: 特定 bug(ask_user_question 5 分钟超时 → 模型继续执行)需要 ACP 客户端取消权限请求,无法在简单 tmux 提示中复现。173 个单元测试已全面覆盖此取消路径。 — Qwen Code · qwen3.7-max |
ReflectionThis PR fixes a real, user-reported bug: after the 5-minute Going back to my independent proposal: I would have used a shared The test suite (173 tests, ~1000 lines of additions) is large, but each test isolates a distinct propagation path: direct cancel, sequential skip, concurrent batch abort, nested agent, sibling abort, unstarted skip, hook suppression, interrupt labeling, rewrite-wait, cron stop. For a fix that touches this many concurrency boundaries, the coverage is insurance, not bloat. Build, typecheck, and smoke test all pass. No critical blockers in code review. The one reservation from Stage 1 stands — the callback threading through three constructor/parameter levels is slightly more surface area than a pure abort-controller approach would need. But it's a stylistic choice, not a correctness issue, and the author clearly thought about it (the callback name is descriptive, the parameter is optional at every level). Verdict: this is a solid, well-tested fix for a real bug. Ready to ship. ✅ 中文说明反思这个 PR 修复了一个真实的、用户反馈的 bug: 回到我的独立方案:我会用一个共享的 测试套件(173 个测试,约 1000 行新增)量大,但每个测试隔离一条传播路径。对于涉及这么多并发边界的修复,这些覆盖是保险而非膨胀。 构建、类型检查、冒烟测试全部通过。代码审查无关键阻塞项。 Stage 1 的保留意见依然成立——回调穿过三层构造/参数,比纯 abort-controller 方案多了一些参数传递。但这是风格选择,不是正确性问题,而且作者显然考虑过了(回调名有描述性,每层参数都是可选的)。 结论:这是一个扎实的、经过充分测试的真实 bug 修复。可以合入。✅ — Qwen Code · qwen3.7-max |
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
LGTM, looks ready to ship. ✅
|
Qwen Code review did not complete successfully: Qwen review timed out after 85 minutes. See workflow logs. |
What this PR does
This PR makes ACP tool execution stop immediately after
ask_user_questionis cancelled, including nested Agent cases. It records skipped follow-up tool responses for calls that must not run, preserves the pending tool-response history needed for replay, waits for pending message rewrites before ending the turn, and propagates nested cancellation through the current Agent and sibling Agent calls in the same batch. It also keeps cancellation telemetry and hooks consistent by using the active Agent abort signal for cancelled Agent executions.Why it's needed
Users reported that after the 5-minute
ask_user_questiontimeout, the model continued executing. The root cause was that a cancelled question was represented as a tool error but the ACP turn loop continued feeding tool responses back to the model, and nested Agent cancellations did not consistently abort or suppress later Agent activity. This PR prevents the model from continuing without required user input and keeps daemon/ACP state, hooks, and replay records aligned with that stop.Reviewer Test Plan
How to verify
Run
cd packages/cli && npx vitest run src/acp-integration/session/Session.test.ts && npx vitest run src/acp-integration/session/SubAgentTracker.test.tsand expect both files to pass. Runnpm run build && npm run typecheckfrom the repo root and expect exit 0. Reviewers can also simulate cancelling an ACPask_user_questionand confirm no later model follow-up or sibling Agent success hook runs after cancellation.Evidence (Before & After)
N/A for UI evidence. Before this change, cancellation could be returned as a tool error and the turn could continue; after this change, cancellation ends the turn and records skipped tool responses for unexecuted calls.
Tested on
Environment (optional)
Local macOS worktree using the repository npm workspace. Verification commands:
cd packages/cli && npx vitest run src/acp-integration/session/Session.test.ts && npx vitest run src/acp-integration/session/SubAgentTracker.test.ts;npm run build && npm run typecheck.Risk & Scope
Linked Issues
N/A
中文说明
What this PR does
这个 PR 让 ACP 工具执行在
ask_user_question被取消后立即停止,包括嵌套 Agent 场景。它会为不应继续运行的后续工具记录 skipped tool response,保留 replay 需要的待发送工具响应 history,在结束 turn 前等待 pending message rewrite,并把嵌套取消传播到当前 Agent 以及同一批次里的 sibling Agent。它还通过使用当前 Agent 的 active abort signal,让取消场景下的 telemetry 和 hook 语义保持一致。Why it's needed
用户反馈 5 分钟
ask_user_question超时后模型继续执行。根因是取消的问题被表示成工具错误后,ACP turn loop 仍然把工具响应继续交给模型,而且嵌套 Agent 取消没有稳定地中断或抑制后续 Agent 活动。这个 PR 防止缺少必要用户输入时模型继续执行,并让 daemon/ACP 状态、hooks 和 replay 记录与停止语义保持一致。Reviewer Test Plan
How to verify
运行
cd packages/cli && npx vitest run src/acp-integration/session/Session.test.ts && npx vitest run src/acp-integration/session/SubAgentTracker.test.ts,预期两个测试文件都通过。从仓库根目录运行npm run build && npm run typecheck,预期 exit 0。评审也可以模拟取消 ACPask_user_question,确认取消后不会继续模型 follow-up,也不会触发 sibling Agent 的成功 hook。Evidence (Before & After)
非 UI 改动,UI 证据为 N/A。变更前,取消可能作为工具错误返回并让 turn 继续;变更后,取消会结束当前 turn,并为未执行的调用记录 skipped tool response。
Tested on
Environment (optional)
本地 macOS worktree,使用仓库 npm workspace。验证命令:
cd packages/cli && npx vitest run src/acp-integration/session/Session.test.ts && npx vitest run src/acp-integration/session/SubAgentTracker.test.ts;npm run build && npm run typecheck。Risk & Scope
Linked Issues
N/A