Skip to content

fix(core): guide agent to pivot to read-only tools when plan mode blocks - #6764

Merged
wenshao merged 1 commit into
QwenLM:mainfrom
Alex-ai-future:fix/plan-mode
Jul 12, 2026
Merged

fix(core): guide agent to pivot to read-only tools when plan mode blocks#6764
wenshao merged 1 commit into
QwenLM:mainfrom
Alex-ai-future:fix/plan-mode

Conversation

@Alex-ai-future

Copy link
Copy Markdown
Contributor

What this PR does

Changes the plan mode blocked error message and system prompt to guide the agent to pivot to read-only alternatives instead of immediately exiting plan mode. When a non-read-only tool is blocked, the agent is now told "Do NOT retry this tool" and instructed to gather equivalent context via read-only tools first, then call exit_plan_mode with a complete plan.

Why it's needed

After the plan mode response format fix, the error message still implied the agent should immediately call exit_plan_mode to unblock. This creates a negative feedback loop: the agent receives a blocked tool error, reads "Call exit_plan_mode to exit plan mode and execute this tool," and exits prematurely without gathering enough context to form a useful plan. The system prompt also lacked any guidance on what to do when a tool is blocked.

Reviewer Test Plan

How to verify

  1. Enter plan mode with the CLI
  2. Ask the agent to do something that requires a non-read-only tool (e.g., npm install, write_file)
  3. Verify the agent receives the error containing "Do NOT retry" and "Pivot to read-only"
  4. Verify the agent attempts to gather equivalent information via read-only tools instead of immediately exiting or retrying
  5. Run the unit tests:
    • cd packages/core && npx vitest run src/core/coreToolScheduler.test.ts -t "plan mode"
    • cd packages/core && npx vitest run src/core/prompts.test.ts -t "getPlanModeSystemReminder"

Evidence (Before & After)

N/A

Tested on

OS Status
🍏 macOS
🪟 Windows N/A
🐧 Linux N/A

Risk & Scope

  • Main risk or tradeoff: Message text change only — no logic changes to blocking behavior
  • Not validated / out of scope: Actual LLM behavior with the new message (requires end-to-end testing with a real model)
  • Breaking changes / migration notes: None

Linked Issues

Closes #6763

中文说明

这个 PR 做了什么

修改了 plan mode 阻塞时的错误消息和系统提示,引导 agent 先转向只读工具收集信息,而不是立即退出 plan mode。当非只读工具被阻塞时,agent 现在会收到 "Do NOT retry this tool" 的提示,并被指示先通过只读工具收集等效上下文,然后用完整计划调用 exit_plan_mode

为什么需要

在 plan mode 响应格式修复之后,错误消息仍然暗示 agent 应该立即调用 exit_plan_mode 来解除阻塞。这导致负反馈循环:agent 收到阻塞错误后,看到 "Call exit_plan_mode to exit plan mode and execute this tool",于是在收集到足够上下文之前就过早退出。系统提示也缺少关于工具被阻塞时该怎么做的指导。

Reviewer 测试计划

如何验证

  1. 在 CLI 中进入 plan mode
  2. 让 agent 执行需要非只读工具的操作(如 npm installwrite_file
  3. 确认 agent 收到的错误消息包含 "Do NOT retry" 和 "Pivot to read-only"
  4. 确认 agent 尝试通过只读工具收集等效信息,而不是立即退出或重试
  5. 运行单元测试:
    • cd packages/core && npx vitest run src/core/coreToolScheduler.test.ts -t "plan mode"
    • cd packages/core && npx vitest run src/core/prompts.test.ts -t "getPlanModeSystemReminder"

证据(Before & After)

N/A

测试平台

OS 状态
🍏 macOS
🪟 Windows N/A
🐧 Linux N/A

风险与范围

  • 主要风险/权衡:仅修改消息文本,不改变阻塞逻辑
  • 未验证/超出范围:新消息下 LLM 的实际行为(需要真实模型端到端测试)
  • 破坏性变更/迁移说明:无

关联 Issue

Closes #6763

When a non-read-only tool is blocked by plan mode, the error message
tells the agent to immediately call exit_plan_mode, causing premature
exit before gathering enough context. The system prompt also lacks
guidance on what to do when a tool is blocked.

Fix: Add 'Do NOT retry this tool' and 'Pivot to read-only alternatives'
to the error message, and add a 'When a Tool is Blocked by Plan Mode'
section to the system prompt. The agent should first gather equivalent
information via read-only tools, then call exit_plan_mode with a
complete plan.

Signed-off-by: Alex <alex.tech.lab@outlook.com>
@qwen-code-ci-bot

Copy link
Copy Markdown
Collaborator

Qwen precheck requires maintainer approval before automated triage/review.

Head SHA: 218e7d34c3f81702030934593e7725263b73660b

Reason:

  • prompt_injection:system_prompt

A maintainer with write access can inspect the PR and manually request a run with @qwen-code /triage or @qwen-code /review. A new push requires a fresh precheck.

@wenshao

wenshao commented Jul 12, 2026

Copy link
Copy Markdown
Collaborator

@qwen-code /triage

@qwen-code-ci-bot

Copy link
Copy Markdown
Collaborator

Thanks for the PR!

Template looks good ✓

Problem: Linked to issue #6763 which clearly describes the mechanism — the current error message "Call exit_plan_mode to exit plan mode and execute this tool" creates a negative feedback loop where the agent exits plan mode prematurely. This is a follow-up to #6667 which already fixed the response format. No formal before/after reproduction, but the mechanism is well-documented and the fix is text-only (error messages + system prompt).

Direction: Aligned — plan mode is a core feature, and misleading error messages degrade the experience. The fix targets exactly the two strings identified in the issue.

Size: 13 production lines (coreToolScheduler.ts: +3/-2, prompts.ts: +8/-0) plus 15 test lines. Minimal core change, no threshold concerns.

Approach: Focused and minimal — only the error message and system prompt text are changed. No logic changes, no new abstractions. The implementation follows the issue's proposal closely. One minor deviation from the issue's exact wording (non-teammate branch says "present your plan directly" instead of keeping the original casing) — this is correct since it now follows a period. Moving on to code review. 🔍

中文说明

感谢贡献!

模板完整 ✓

问题:关联 issue #6763 清楚描述了机制——当前错误消息 "Call exit_plan_mode to exit plan mode and execute this tool" 导致 agent 过早退出 plan mode 的负反馈循环。这是 #6667(修复响应格式)的后续。虽然没有正式的 before/after 复现,但机制描述清晰,且修改仅限于文本(错误消息 + 系统提示)。

方向:对齐——plan mode 是核心功能,误导性的错误消息降低了体验。修改精准针对 issue 中指出的两个字符串。

规模:13 行生产代码(coreToolScheduler.ts: +3/-2, prompts.ts: +8/-0)加 15 行测试。核心改动极小,无阈值问题。

方案:聚焦且最小化——仅修改错误消息和系统提示文本,无逻辑变更,无新抽象。实现紧跟 issue 的提案。与 issue 的措辞有一处细微偏差(非 teammate 分支说 "present your plan directly" 而非保持原大小写)——这是正确的,因为它现在跟在句号后面。进入代码审查 🔍

Qwen Code · qwen3.7-max

@qwen-code-ci-bot

Copy link
Copy Markdown
Collaborator

Code Review

Independent proposal: change the error message to discourage immediate exit_plan_mode and add a system prompt section guiding the agent to pivot to read-only tools. The PR matches this exactly — clean, minimal, no unnecessary changes.

No critical issues. No AGENTS.md violations. The implementation is a textbook text-only fix: the blocking logic is untouched, only the guidance strings change. Tests verify the new assertions for both branches (teammate and non-teammate paths).

Test Results

Unit tests (all pass):

  • coreToolScheduler.test.ts (plan mode): 14 passed
  • prompts.test.ts (getPlanModeSystemReminder): 4 passed
  • Full files: coreToolScheduler 258/258, prompts 59/59

TypeScript build: passes with no errors.

Before/After: Error Message

=== ERROR MESSAGE (main branch) ===
const planModeError = new Error(
  `Tool blocked by plan mode: "${reqInfo.name}" is not a read-only tool. ` +
    `Only read-only tools (read_file, grep_search, glob, list_directory, ` +
    `web_fetch, etc.) are allowed in plan mode.` +
    (isPlanRequiredTeammate
      ? ` Call exit_plan_mode to exit plan mode and execute this tool.`
      ...

=== ERROR MESSAGE (PR branch) ===
const planModeError = new Error(
  `Tool blocked by plan mode: "${reqInfo.name}" is not a read-only tool. ` +
    `Only read-only tools (read_file, grep_search, glob, list_directory, ` +
    `web_fetch, etc.) are allowed in plan mode.` +
    ` Do NOT retry this tool. ` +
    (isPlanRequiredTeammate
      ? `Pivot to read-only alternatives to gather the information you need, then call exit_plan_mode with a plan that covers this tool's purpose.`
      ...

The old message ("Call exit_plan_mode to exit plan mode and execute this tool") directly encourages premature exit. The new message ("Do NOT retry this tool. Pivot to read-only alternatives...") correctly guides the agent to gather context first.

Before/After: System Prompt

=== SYSTEM PROMPT (main branch) ===
### Planning Principles
...
- Include a verification section describing how to test the changes end-to-end.

### When to Converge
Your plan is ready when you have addressed all ambiguities...

=== SYSTEM PROMPT (PR branch) ===
### Planning Principles
...
- Include a verification section describing how to test the changes end-to-end.

### When a Tool is Blocked by Plan Mode

If a non-read-only tool is blocked:
- Do NOT retry the blocked tool or repeatedly attempt similar non-read-only tools
- Do NOT immediately call exit_plan_mode just to unblock it — continue gathering context with read-only tools first
- Pivot to read-only tools (read_file, grep_search, glob, list_directory, agents) to gather the information the blocked tool would have provided
- Once you have enough context to form a complete plan, call exit_plan_mode

### When to Converge
Your plan is ready when you have addressed all ambiguities...

The new section is well-placed — between "Planning Principles" and "When to Converge" — and provides clear, actionable guidance that closes the gap the issue describes.

Tmux Test Output

$ cd /home/github-runner/actions-runner-24/_work/qwen-code/qwen-code/packages/core && npx vitest run src/core/coreToolScheduler.test.ts -t 'plan mode' 2>&1 | tail -20

 RUN  v3.2.4 /home/github-runner/actions-runner-24/_work/qwen-code/qwen-code/packages/core
      Coverage enabled with v8

 ✓ src/core/coreToolScheduler.test.ts (258 tests | 244 skipped) 23ms

 Test Files  1 passed (1)
      Tests  14 passed | 244 skipped (258)
   Start at  20:07:36
   Duration  5.52s

$ npx vitest run src/core/prompts.test.ts -t 'getPlanModeSystemReminder' 2>&1 | tail -20

 RUN  v3.2.4 /home/github-runner/actions-runner-24/_work/qwen-code/qwen-code/packages/core
      Coverage enabled with v8

 ✓ src/core/prompts.test.ts (59 tests | 55 skipped) 3ms

 Test Files  1 passed (1)
      Tests  4 passed | 55 skipped (59)
   Start at  20:07:58
   Duration  3.68s

Note: This is a text-only change (error messages + system prompt). The error message is LLM-internal (sent as a tool result, not displayed in terminal), so tmux before/after of the CLI TUI is not meaningful here. The unit tests verify the exact string changes and are the appropriate test for this type of change. The PR explicitly notes: "Not validated / out of scope: Actual LLM behavior with the new message (requires end-to-end testing with a real model)."

Qwen Code · qwen3.7-max

@qwen-code-ci-bot

Copy link
Copy Markdown
Collaborator

This is a clean, well-scoped text-only fix. The problem is real (documented in #6763, follow-up to #6667), the solution matches what I'd propose independently, and every line in the diff serves the stated goal. No scope creep, no unnecessary abstractions.

The before/after comparison is convincing: the old message ("Call exit_plan_mode to exit plan mode and execute this tool") actively misleads the agent toward premature exit, and the new message ("Do NOT retry this tool. Pivot to read-only alternatives...") provides clear corrective guidance. The system prompt addition fills a gap that shouldn't have been there.

Unit tests verify the exact string changes across both code paths (teammate and non-teammate). Build passes. TypeScript type-checks clean.

13 production lines, 15 test lines. Exactly the right size for this fix. Approving. ✅

中文说明

这是一个干净、范围合理的纯文本修复。问题是真实存在的(#6763 中有文档记录,是 #6667 的后续),方案与我的独立提案一致,diff 中的每一行都服务于既定目标。没有范围蔓延,没有不必要的抽象。

Before/after 对比有说服力:旧消息("Call exit_plan_mode to exit plan mode and execute this tool")主动误导 agent 过早退出,新消息("Do NOT retry this tool. Pivot to read-only alternatives...")提供了清晰的纠正指引。系统提示的补充填补了一个本不该存在的空白。

单元测试验证了两条代码路径(teammate 和 non-teammate)的确切字符串变更。构建通过,TypeScript 类型检查无错误。

13 行生产代码,15 行测试。对于此修复来说恰到好处。批准。✅

Qwen Code · qwen3.7-max

@qwen-code-ci-bot qwen-code-ci-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, looks ready to ship. ✅

@wenshao

wenshao commented Jul 12, 2026

Copy link
Copy Markdown
Collaborator

✅ Local verification — PR #6764

I built and ran this PR locally as a maintainer merge-reference. All deterministic checks pass and the runtime strings render exactly as intended.

Environment: macOS 24.6.0 · node v22.23.1 · npm 10.9.8 · vitest v3.2.4 · isolated worktree @ 218e7d34c, which rebases cleanly on origin/main (MERGEABLE).

1 · Automated tests

Check Command Result
PR test plan — scheduler vitest run coreToolScheduler.test.ts -t "plan mode" 14 passed
PR test plan — prompts vitest run prompts.test.ts -t "getPlanModeSystemReminder" 4 passed
Full file (regression guard) vitest run prompts.test.ts 59 passed
Full file (regression guard) vitest run coreToolScheduler.test.ts 258 passed
Typecheck tsc --noEmit (core) clean
Lint eslint on the 4 changed files clean (exit 0)

tests

2 · Runtime evidence (the actual strings the model receives)

I instrumented the real CoreToolScheduler blocked-tool path to capture the emitted responseParts verbatim (then reverted the instrumentation — the validated worktree is byte-identical to the PR head), and rendered getPlanModeSystemReminder() from the built dist/. Both the new error text and the new system-prompt section are produced correctly in every context:

  • Interactive / plan-required main agent… Do NOT retry this tool. Pivot to read-only alternatives … then call exit_plan_mode with a plan that covers this tool's purpose.
  • Subagent / teammate-under-plan / SDK caller (all 3 verified identical) → … Do NOT retry this tool. Pivot to read-only alternatives … then present your plan directly to the caller.
  • New ### When a Tool is Blocked by Plan Mode section present in both planOnly=false and planOnly=true reminder variants.

runtime

Verdict

The change is text-only (no control-flow change), tests + typecheck + lint are green, and the new guidance renders correctly for every caller path. LGTM from a mechanical-verification standpoint. ✔️

Scope note (matches the PR's own): this verifies the content of the message and system prompt deterministically. The actual downstream LLM behavior (whether the agent now pivots to read-only tools instead of exiting prematurely) requires an end-to-end run against a live model and is out of scope here.

🀄 中文版本(点击展开)

✅ 本地验证 —— PR #6764

作为维护者,我在本地构建并运行了该 PR 作为合并参考。所有确定性检查均通过,运行时字符串也完全按预期渲染。

环境: macOS 24.6.0 · node v22.23.1 · npm 10.9.8 · vitest v3.2.4 · 独立 worktree @ 218e7d34c,可干净地 rebase 到 origin/mainMERGEABLE)。

1 · 自动化测试

检查项 命令 结果
PR 测试计划 — scheduler vitest run coreToolScheduler.test.ts -t "plan mode" 14 通过
PR 测试计划 — prompts vitest run prompts.test.ts -t "getPlanModeSystemReminder" 4 通过
完整文件(回归防护) vitest run prompts.test.ts 59 通过
完整文件(回归防护) vitest run coreToolScheduler.test.ts 258 通过
类型检查 tsc --noEmit(core) 无错误
Lint 对 4 个改动文件运行 eslint 无错误(exit 0)

(截图见上方英文部分)

2 · 运行时证据(模型实际收到的字符串)

我对真实的 CoreToolScheduler 阻塞工具路径做了埋点,逐字捕获了产出的 responseParts(随后撤销了埋点 —— 被验证的 worktree 与 PR HEAD 逐字节一致),并从构建产物 dist/ 中渲染了 getPlanModeSystemReminder()。新的错误文案与新的系统提示段落在所有场景下都被正确产出:

  • 交互式 / plan-required 主 agent… Do NOT retry this tool. Pivot to read-only alternatives … then call exit_plan_mode with a plan that covers this tool's purpose.
  • subagent / plan 下的 teammate / SDK 调用方(三者均验证一致)→ … Do NOT retry this tool. Pivot to read-only alternatives … then present your plan directly to the caller.
  • 新的 ### When a Tool is Blocked by Plan Mode 段落在 planOnly=falseplanOnly=true 两个 reminder 变体中都存在。

(截图见上方英文部分)

结论

该改动仅涉及文案(无控制流变更),测试 + 类型检查 + lint 全绿,且新的指导语在每条调用方路径下都正确渲染。从机械验证角度 LGTM。✔️

范围说明(与 PR 本身一致): 本次验证确定性地覆盖了消息与系统提示的内容。至于新文案对下游 LLM 行为的实际影响(agent 是否会真的转向只读工具而不是过早退出),需要针对真实模型做端到端运行,不在本次验证范围内。

🔧 Verified locally by the maintainer using a built CLI in an isolated worktree.

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.

Plan mode blocked tool error misleads LLM to immediately exit instead of pivoting to read-only alternatives

3 participants