Skip to content

feat(cli): extend non-blocking slash commands to more builtins - #9495

Merged
DragonnZhang merged 3 commits into
QwenLM:mainfrom
DragonnZhang:feat/extend-nonblocking-slash-commands
Aug 21, 2026
Merged

feat(cli): extend non-blocking slash commands to more builtins#9495
DragonnZhang merged 3 commits into
QwenLM:mainfrom
DragonnZhang:feat/extend-nonblocking-slash-commands

Conversation

@DragonnZhang

Copy link
Copy Markdown
Collaborator

What this PR does

This PR extends the streaming-safe ("non-blocking") slash command capability introduced in #8130 to eleven more built-in commands. While a model response is streaming, these commands now execute immediately instead of waiting in the message queue for the active turn to finish: /theme, /editor, /vim, /voice, and /terminal-setup only manage UI preferences (their saved changes apply through the existing settings hooks), while /tools, /lsp, /tasks, /hooks, /docs, and /bug are read-only — they render local status information or open a browser, and never read state the active turn is writing. Each command declares the capability with a unit test pinning it, and the design doc records the extended set alongside the criteria used.

Why it's needed

Since #8130, only /about, /help, and /settings bypass the streaming queue. Local UI controls like the theme picker or the tools listing neither submit a model turn nor touch conversation state owned by the active turn, so making them wait for a long response to finish only adds friction with no safety benefit. This narrows serialization to the commands that genuinely need it (model-turn submission, history mutation, runtime configuration that affects the in-flight turn), keeping the queue semantics exactly as the design doc defines them.

Reviewer Test Plan

How to verify

  • Start the CLI (e.g. npm run dev) and submit a prompt that streams for a while, such as asking for a long essay.
  • While the response is still streaming, run /theme: the theme picker should open immediately. Press Escape and confirm the original response keeps streaming and completes normally.
  • Repeat with a read-only command such as /tools or /hooks: the output renders immediately while the stream continues.
  • Confirm commands that must stay serialized still queue during streaming, e.g. /context or /model — they should only run after the turn completes.
  • Unit coverage: cd packages/cli && npx vitest run src/ui/AppContainer.test.tsx src/ui/commands/themeCommand.test.ts src/ui/commands/toolsCommand.test.ts src/ui/commands/vimCommand.test.ts — the new assertions pin canRunDuringStreaming for every opted-in command, and the AppContainer tests verify opted-in commands bypass the queue during a response while unmarked ones remain queued.

Evidence (Before & After)

Before (main): typing /theme while a response streams queues it; the picker only opens after the turn completes (the default queue behavior established in #8130).

After (this PR): captured via tmux during a live streaming response — the theme dialog opens on top of the still-streaming text:

    二、起源:春秋战国的诸侯边墙
    …这些互不相连
  ╭──────────────────────────────────────────────╮
  │ > 选择主题                       预览        │
  │    1. 自动  Auto                             │
  │    2. Qwen Light Light                       │
  │    3. Qwen Dark Dark                         │
  │    …                                         │
  ╰──────────────────────────────────────────────╯

And /tools renders its listing while the footer shows the stream still in flight:

    - WriteFile
    - ZoomImage
    我们今天所见的长城,绝大多数修筑于明代。…
  .  正在拥抱变化,迭代核心价值... (30s · ↓ 572 tokens · esc to cancel)

Tested on

OS Status
🍏 macOS ✅ tested
🪟 Windows ⚠️ not tested
🐧 Linux ⚠️ not tested

Environment

npm run dev against ModelStudio (qwen3.8-max) for the tmux evidence; unit tests run with vitest.

Risk & Scope

  • Main risk or tradeoff: these commands now run while a turn is in flight; the UI-preference ones persist settings mid-turn, but that is the same path /settings has used since feat(cli): run safe slash commands during streaming #8130, and /terminal-setup only writes external IDE keybinding files.
  • Not validated / out of scope: interactive E2E for each of the eleven commands (verified /theme and /tools as representatives of both groups); commands that read state the active turn mutates (/context, /stats, /copy, /diff, /export) intentionally remain serialized per the design doc.
  • Breaking changes / migration notes: none.

Linked Issues

Follow-up to #8130.

中文说明

这个 PR 做了什么

本 PR 将 #8130 引入的流式安全("非阻塞")slash command 能力扩展到另外 11 个内置命令。在模型流式响应期间,这些命令现在会立即执行,而不再在消息队列中等待活跃轮次结束:/theme/editor/vim/voice/terminal-setup 只管理 UI 偏好(保存的变更通过既有的 settings hooks 生效),而 /tools/lsp/tasks/hooks/docs/bug 是纯只读的——它们只渲染本地状态信息或打开浏览器,从不读取活跃轮次正在写入的状态。每个命令都声明了该能力并用单元测试锁定,设计文档也记录了扩展后的集合及所用标准。

为什么需要

#8130 以来,只有 /about/help/settings 可以绕过流式队列。像主题选择器、工具列表这类本地 UI 控件,既不提交模型轮次,也不触碰活跃轮次拥有的会话状态,让它们等待一个长响应结束只会徒增阻塞而没有任何安全收益。本次改动把串行化收窄到真正需要它的命令(提交模型轮次、变更历史、变更影响进行中轮次的运行时配置),使队列语义与设计文档的定义完全一致。

评审者测试计划

如何验证

  • 启动 CLI(如 npm run dev),提交一个会流式较久的 prompt(例如要求写一篇长文)。
  • 在响应仍在流式输出时运行 /theme:主题选择对话框应立即打开;按 Escape 关闭后确认原响应继续流式并正常完成。
  • /tools/hooks 等只读命令重复验证:输出立即渲染,流式继续。
  • 确认必须保持串行的命令在流式期间仍然排队,例如 /context/model——它们应等轮次结束后才执行。
  • 单元测试覆盖:cd packages/cli && npx vitest run src/ui/AppContainer.test.tsx src/ui/commands/themeCommand.test.ts src/ui/commands/toolsCommand.test.ts src/ui/commands/vimCommand.test.ts——新增断言为每个 opt-in 命令锁定 canRunDuringStreaming,AppContainer 测试验证响应期间 opt-in 命令绕过队列、未标记命令仍被排队。

证据(Before & After)

Before(main):流式期间输入 /theme 会被排队,对话框要等轮次结束才打开(#8130 确立的默认排队行为)。After(本 PR):tmux 实时抓屏显示主题对话框在仍在流式输出的文本之上打开,以及 /tools 输出渲染时底部仍显示流式进行中状态(详见英文正文的代码块)。

测试环境

macOS ✅ 已测试;Windows ⚠️ 未测试;Linux ⚠️ 未测试

环境

tmux 证据使用 npm run dev + ModelStudio(qwen3.8-max);单元测试使用 vitest。

风险与范围

  • 主要风险或权衡:这些命令现在会在轮次进行中执行;UI 偏好类命令会在轮次中途持久化设置,但这与 /settingsfeat(cli): run safe slash commands during streaming #8130 以来走的路径一致,且 /terminal-setup 只写外部 IDE 按键绑定文件。
  • 未验证 / 超出范围:11 个命令各自的交互式 E2E(验证了 /theme/tools 作为两组的代表);读取活跃轮次正在变更的状态的命令(/context/stats/copy/diff/export)按设计文档有意保持串行。
  • 破坏性变更 / 迁移说明:无。

关联 Issue

#8130 的后续。

QwenLM#8130 opted /about, /help, and /settings in to run immediately while
a response streams. Apply the same criteria to eleven more builtins so
local UI controls no longer wait for the active turn:

- UI-preference commands whose saved changes apply through the
  existing settings hooks: /theme, /editor, /vim, /voice, and
  /terminal-setup.
- Read-only status commands: /tools, /lsp, /tasks, /hooks, /docs,
  and /bug.

Commands that submit model turns, mutate conversation state, or read
state the active turn is writing remain serialized, as documented in
the non-blocking slash commands design doc. Each opt-in is pinned by a
unit test.
@DragonnZhang

Copy link
Copy Markdown
Collaborator Author

E2E test report

Environment: npm run dev (tsx), ModelStudio qwen3.8-max, tmux 220×50, macOS.

Steps:

  1. Submitted a prompt that streams for a long time (a 1500-word essay request).
  2. While the response was still streaming, submitted /theme.
  3. Closed the dialog with Escape, then submitted /tools while still streaming.
  4. Confirmed the original response kept streaming and the footer showed the in-flight turn.

Results:

  • /theme (UI-preference group): the theme picker opened immediately on top of the still-streaming text — no queueing:
    二、起源:春秋战国的诸侯边墙
    …这些互不相连
  ╭──────────────────────────────────────────────╮
  │ > 选择主题                       预览        │
  │    1. 自动  Auto                             │
  │    2. Qwen Light Light                       │
  │    3. Qwen Dark Dark                         │
  ╰──────────────────────────────────────────────╯
  • /tools (read-only group): the tools listing rendered immediately, and the footer confirmed the stream was still in flight afterwards:
    - WriteFile
    - ZoomImage
    我们今天所见的长城,绝大多数修筑于明代。…
  .  正在拥抱变化,迭代核心价值... (30s · ↓ 572 tokens · esc to cancel)

Unit coverage: all 11 command test files (70 tests) plus AppContainer.test.tsx (156 tests) pass — the AppContainer suite pins the gate behavior (opted-in commands bypass the queue during a response; unmarked ones stay queued).

@qwen-code-ci-bot

qwen-code-ci-bot commented Aug 19, 2026

Copy link
Copy Markdown
Collaborator

Qwen Triage finished — CI landed green on 1cd08ae and the deferred approval was posted. finalize run

Qwen Triage 已完成 —— 1cd08ae 的 CI 全绿,延迟审批已提交。查看 finalize 运行

@qwen-code-ci-bot

Copy link
Copy Markdown
Collaborator

Thanks for the PR!

Template looks good ✓

Problem: observed friction, not theory — since #8130 only /about, /help, and /settings bypass the streaming queue, everything else waits for the turn to finish. The PR body carries before/after tmux capture (author-reported). The gap is the default queue behavior established in #8130, so the problem clearly exists.

Direction: aligned — this is a direct follow-up to merged #8130, extending the capability to more builtins under the same criteria the design doc already defines. No direct CHANGELOG entry, but the reference agent does run slash commands mid-response (claude-code changelog fixes "slash commands run while Claude is responding"), so the UX pattern is established.

Size: not applicable — no core paths touched. 11 production lines (one flag per command), ~74 test lines, 13 doc lines.

Approach: the scope feels right. Each command opts in with a single flag declaration pinned by a unit test, and the design doc records the extended set and criteria. A skim of each command against the flag contract (no model-turn submission, no mutation of active-turn state) matches the PR's grouping: the UI-preference commands persist through the same settings hooks /settings already uses, the rest are read-only listings or browser launches, and the state-reading commands (/context, /stats, /copy, /diff, /export) correctly stay serialized. Nothing to cut.

Risk: no elevated risk signals — Stage 1e path check came back clean.

Moving on to code review. 🔍

中文说明

感谢贡献!

模板完整 ✓

问题:是实际可观察到的使用摩擦,而非理论问题——自 #8130 以来只有 /about/help/settings 能绕过流式队列,其余命令都要等当前轮次结束。PR 正文附有 before/after tmux 截图(作者自述)。这个差距正是 #8130 确立的默认排队行为,问题真实存在。

方向:对齐——这是对已合并 #8130 的直接跟进,按设计文档已定义的标准把该能力扩展到更多内置命令。CHANGELOG 无直接条目,但参考产品确实支持响应期间执行 slash command(claude-code changelog 有 "slash commands run while Claude is responding" 的修复记录),该 UX 模式已有先例。

规模:不适用——未触及核心路径。生产代码 11 行(每个命令一个标志位)、测试约 74 行、文档 13 行。

方案:范围合理。每个命令以单个标志位声明加入,并用单元测试锁定;设计文档记录了扩展集合与判定标准。按标志位契约(不提交模型轮次、不变更活跃轮次拥有的状态)快速核对每个命令,与 PR 的分组一致:UI 偏好类命令经由 /settings 已在使用的 settings hooks 持久化,其余为只读列表或打开浏览器,而读取活跃轮次状态的命令(/context/stats/copy/diff/export)正确保持串行。没有可砍的部分。

风险:无升级风险信号——Stage 1e 高风险路径检查未命中。

进入代码审查 🔍

Qwen Code · qwen3.8-max

Reviewed at 1cd08ae1550dda675e00129e4f7bc891e89fcb31 · re-run with @qwen-code /triage

@qwen-code-ci-bot

qwen-code-ci-bot commented Aug 19, 2026

Copy link
Copy Markdown
Collaborator

Code review

No blockers found. My independent take before reading the diff was exactly what this PR does: audit each builtin against the streaming-safe contract from #8130, add the flag declaration per command, pin it with a test, and record the set in the design doc. The queue mechanism in AppContainer itself is untouched, so the change is purely additive (+101/−0).

What I verified per command against the contract (no model-turn submission, no mutation of state the active turn owns):

  • /hooks opens HooksManagementDialog, which I checked is navigation-only — it reads user/workspace/extension/session hooks and renders them, with no write path. The non-interactive branch is a plain text listing.
  • /terminal-setup writes only external IDE keybinding files (VS Code / Cursor / Windsurf / Trae keybindings.json), never app state.
  • /vim and /voice persist through settings.setValue — the same hooks path /settings has used since feat(cli): run safe slash commands during streaming #8130.
  • /theme and /editor open settings dialogs (theme keeps its NO_COLOR early-reject).
  • /tools, /lsp, /tasks are read-only listings (the tasks dump even sanitizes registry strings before printing).
  • /docs and /bug append one history item and open a browser via openBrowserSecurely.

One subtlety worth noting: the AppContainer bypass reads parseSlashCommand(...).commandToExecute?.canRunDuringStreaming, and commandToExecute resolves to the deepest matched command — none of these eleven register subCommands, so the check also holds for argument forms like /voice status or /tools desc. The pre-existing AppContainer tests already cover the bypass-vs-queue behavior generically.

Style follows house conventions; the new vimCommand.test.ts fills a pre-existing gap (vimCommand had no test file at all). No sequence diagram or per-file table — the change is uniform one-line declarations, they would add no signal.

Test evidence (PR's own CI, read via API — no PR code executed)

Everything that has completed is green; the main Linux unit suite is still running. The macOS and Windows test jobs are skipped at this stage (they gate later), so cross-platform coverage for this commit comes from CI's Linux leg only. No failed checks, so no log excerpts to quote. The finalize job will refresh the table below once CI settles.

Final CI results for 1cd08ae (auto-updated by the triage finalize job after CI completed):

Check Conclusion
Classify PR ✅ success
Dependency CVE audit ✅ success
Desktop Shell (ubuntu-22.04) ✅ success
Desktop Shell (windows-2022) ✅ success
Secret scan (TruffleHog) ✅ success
Test (ubuntu-latest, Node 22.x) ✅ success
web-shell E2E Smoke (ubuntu-latest, Node 22.x) ✅ success

One row per check name (latest run); skipped checks omitted; failures sort first. / 每个检查名一行(取最新一次运行),省略 skipped,失败项排在最前。

The unit tests pin the flag declaration on every command, and the AppContainer suite covers the queue-bypass mechanism — but that proves the declarations are wired, not the end-to-end behavior. The mid-stream claim ("these commands actually execute during a live stream, and the stream survives it") currently rests on the author's tmux capture on macOS only. Sandboxed verification would settle this: @qwen-code /tmux — drive the TUI as a real user, start a streaming response, and confirm the opted-in commands execute mid-stream while an unmarked one (e.g. /context) still queues.

中文说明

代码审查

未发现阻塞问题。我在看 diff 之前的独立思路与本 PR 完全一致:按 #8130 的流式安全契约逐个审查内置命令、为每个命令添加标志位声明、用测试锁定、并在设计文档中记录集合。AppContainer 的排队机制本身未改动,变更为纯增量(+101/−0)。

按契约(不提交模型轮次、不变更活跃轮次拥有的状态)逐一核实:/hooks 打开的 HooksManagementDialog 经核实为纯浏览(只读渲染各类 hooks,无写入路径);/terminal-setup 只写外部 IDE 的 keybindings.json;/vim/voice 经由与 /settings 相同的 settings.setValue 路径持久化;/theme/editor 打开设置对话框;/tools/lsp/tasks 为只读列表;/docs/bug 仅追加一条记录并安全打开浏览器。另核实了 AppContainer 旁路读取的 commandToExecute 对这 11 个命令(均无子命令)在带参数形式(如 /voice status)下也能正确解析。新增的 vimCommand.test.ts 补上了原本缺失的测试。变更为统一的一行声明,故不附时序图或文件清单表。

测试证据(通过 API 读取 PR 自身 CI——未执行任何 PR 代码)

已完成的检查全部通过;Linux 主单测套件仍在运行。macOS/Windows 测试作业在此阶段被跳过,本提交的跨平台覆盖仅来自 CI 的 Linux 环节。无失败检查,故无日志摘录。finalize 作业会在 CI 结束后更新上表。

单元测试锁定了每个命令的标志位,AppContainer 套件覆盖了排队旁路机制——但这证明的是声明已接线,而非端到端行为。"流式期间命令确实立即执行且流式响应不受影响"这一主张目前仅有作者在 macOS 上的 tmux 截图支撑。可用沙箱验证收尾:@qwen-code /tmux——以真实用户方式驱动 TUI,在流式响应期间确认 opt-in 命令立即执行、未标记命令(如 /context)仍然排队。

Qwen Code · qwen3.8-max

Reviewed at 1cd08ae1550dda675e00129e4f7bc891e89fcb31 · re-run with @qwen-code /triage

@qwen-code-ci-bot

Copy link
Copy Markdown
Collaborator

Confidence: 4/5 — clean, minimal change that does exactly what it says; the only reservation is evidentiary, not about the code.

This is what a good follow-up PR looks like. The mechanism shipped in #8130; this PR applies it to eleven more builtins with one declarative line each, pins every one with a test, and updates the design doc so the criteria and the set live in one place. The diff matches my independent proposal exactly — I couldn't find a simpler path, and there's nothing in it that isn't needed.

The thing to get right here was the safety judgment, so I audited all eleven against the flag contract rather than trusting the grouping: the dialogs are read-only or settings-scoped, the listings don't touch active-turn state, and the state-reading commands that could race a streaming turn were deliberately left serialized. The queue mechanism itself is untouched, which is why this feels low-risk despite changing behavior on eleven commands.

Two honest reservations, neither blocking: the mid-stream end-to-end behavior rests on the author's tmux capture from macOS — the unit tests prove the wiring, not the live behavior (the /tmux lane named above settles that if a maintainer wants it); and the Linux unit suite is still running on this commit. Approval is deferred until CI lands green on this commit.

中文说明

这是一个规范的跟进型 PR:机制在 #8130 中已落地,本 PR 用每个命令一行声明将其扩展到另外 11 个内置命令,每个都有测试锁定,设计文档也同步记录了标准与集合。diff 与我的独立方案完全一致——找不到更简单的路径,也没有任何多余的改动。

这里的关键是安全判断,因此我逐一按标志位契约核实了全部 11 个命令,而不是直接采信其分组:对话框要么只读要么仅涉及设置,列表类命令不触碰活跃轮次状态,而可能与流式轮次竞争的状态读取类命令被有意保留为串行。排队机制本身未改动,这正是改动覆盖 11 个命令但风险仍低的原因。

两点坦率的保留意见(均不阻塞):流式期间的端到端行为目前仅有作者在 macOS 上的 tmux 截图支撑——单元测试证明的是接线而非实际运行效果(如需收尾可用上面提到的 /tmux 通道);该提交的 Linux 单测套件仍在运行。批准将推迟到 CI 在该提交上全绿之后。

Qwen Code · qwen3.8-max

Reviewed at 1cd08ae1550dda675e00129e4f7bc891e89fcb31 · re-run with @qwen-code /triage

@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 — CI landed green after the review. ✅

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

Reviewed — no blockers. Suggestions are inline.

中文说明

已审查——无阻断问题。 建议见行内评论。

— qwen3.8-max via Qwen Code /review (v0.21.14)

Comment thread docs/design/nonblocking-slash-commands.md Outdated

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

No issues found. LGTM! ✅

中文说明

未发现问题。LGTM!✅

— qwen3.8-max via Qwen Code /review (v0.21.14)

@yiliang114 yiliang114 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 — review found no blockers and CI is green on the latest head.

@DragonnZhang
DragonnZhang added this pull request to the merge queue Aug 21, 2026
Merged via the queue into QwenLM:main with commit d0d68c0 Aug 21, 2026
57 checks passed
@qwen-code-ci-bot

Copy link
Copy Markdown
Collaborator

Released in v0.22.0.

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.

3 participants