Qwen Code 取消恢复机制流式输出被 CtrlC 中断后如何保留部分响应并还原可编辑 Prompt【免费下载链接】qwen-codeAn open-source AI coding agent that lives in your terminal.项目地址: https://gitcode.com/GitHub_Trending/qw/qwen-code导读本篇基于 Qwen Code 仓库中的交互回归测试场景2026-07-18-cancelled-prompt-restore.md展开聚焦终端交互模式下按CtrlC取消正在进行的流式响应时的三项关键行为部分输出是否安全地保留在对话记录中、已提交的 Prompt 是否被还原回输入框并允许继续编辑、以及各类边界场景响应期间打字、排队中的追问、工具调用中、无任何输出时是否不会互相干扰。读完本文你将掌握 Qwen Code 取消恢复的完整行为契约、背后的 prompt-stash 与流缓冲快照机制以及对应的自动化验证方法。场景流式输出期间的 CtrlC 取消在 Qwen Code 的交互式REPL模式下取消恢复的核心体验可以用下面这组步骤完整复现对应原文档的 Scenario 章节启动 Qwen Code进入交互模式提交一条需要较长生成时间的 Prompt例如Explain how a hash table handles collisions in detail.等待模型开始输出、响应文本已经在界面上可见时按下CtrlC确认已经生成的那部分响应仍然保留在对话记录transcript中没有被一并抹掉确认刚刚提交的那条 Prompt 被恢复restore回输入框且可以直接编辑后再次提交。这是一个与 Claude Code 交互习惯对齐的体验取消请求不等于销毁这条对话。用户中断的只是继续生成而已经产生的输出和这条 Prompt 本身都应当被保留以便用户微调后重试或者从已有内容继续追问。四条回归检查取消恢复的行为边界原文档的 Regression checks 章节定义了四条必须长期成立的行为边界它们决定了取消恢复功能不会被一个修复引入的副作用破坏回归检查期望行为源码侧对应验证响应运行期间输入框内已有草稿恢复 Prompt 时不得覆盖用户当前正在编辑的草稿文本prompt-stash.ts 中restorePromptStash仅在currentText.length 0时才执行onRestore队列中已有排队的后续追问输入框应保持显示排队中的追问而不得被旧 Prompt 顶替AppContainer的取消处理器对排队消息队列与 auto-restore 分支做了互斥处理工具调用tool call期间取消保持既有的工具执行行为不因取消恢复逻辑而改变工具调用生命周期取消只中断 LLM 流工具执行状态机维持原语义尚未产生任何响应时取消与以前一致回退rewind空回合即撤销刚提交的 user 条目与尾部 INFO 条目并把 Prompt 拉回输入框AppContainer.test.tsx 中 auto-restores the just-submitted prompt when cancelling before any meaningful output 用例其中无输出即回退空回合这一条尤其关键当模型尚未产出任何有意义的文本时这次提交等同于一次误操作因此完整撤销truncate 回退到提交前、清掉Request cancelled.提示条目是更符合直觉的行为而一旦产生了有意义的内容则采用只还原 Prompt、不回退输出的策略见下文两条恢复路径。两条恢复路径源码层面的判定逻辑AppContainer的取消处理器根据是否产生了有意义内容来选择两种完全不同的恢复策略这在测试中体现为两条对称的用例产生了有意义内容调用mockSetText(what time is it?)还原 Prompt但不调用truncateToItem即保留已产出的模型输出与对话记录AppContainer.test.tsx没有任何输出走回退路径调用truncateToItem连同Request cancelled.提示一起回滚同时把 Prompt 文本写回输入框AppContainer.test.tsx。而这次取消是否产生了内容这个判断依赖一个同步快照info.pendingItem。测试中专门有一条用例指出流式 chunk 到达 →cancelOngoingRequest通过addItem提交 → 在 React 重渲染之前触发onCancelSubmit此时外部消费方读到的pendingLlmHistoryItemsprop 仍然是[]陈旧状态。因此不能依赖 React state而必须使用取消回调中同步传递的info.pendingItem快照覆盖陈旧的 React 状态副本对应 AppContainer.test.tsx 中 does not auto-restore when the sync pendingItem snapshot has meaningful content 用例。此外还有一系列防御性守卫被测试覆盖例如lastTurnUserItem.text与候选 user 条目文本不一致时不执行 auto-restore防止误截断更早的 PromptlastTurnUserItem.id与候选条目不匹配时不执行 auto-restore防止addItem去重生成新 id 造成误判Cron / 斜杠命令submit_prompt等未新增 user 条目的回合不触发 auto-restore。流冲刷竞态取消前必须先 flush 缓冲事件取消恢复里最容易出问题的不是恢复本身而是取消瞬间流事件仍被缓冲在内存中。useLlmStream对内容事件存在节流throttle缓冲事件先进入bufferedEvents再按节流窗口批量刷入状态因此取消时pendingHistoryItemRef仍为 null是完全可能出现的合法状态。针对这一竞态use-llm-stream.test.tsx中有一条专门的回归用例use-llm-stream.test.tsx其验证策略极具代表性构造一个异步生成器先yield一条Content事件partial response然后await一个被挂起的 Promise让流保持打开提交查询后只让微任务排空、不推进节流定时器从而确保pendingHistoryItems保持为空、内容仍困在bufferedEvents中此时调用cancelOngoingRequest()断言取消回调收到的info.pendingItem中已经包含文本partial response。这个用例的注释点明了根因如果先对pendingHistoryItemRef.current做快照、再 flush内容事件就会滞留在bufferedEvents中而对快照不可见info.pendingItem传回AppContainer时会是 null进而导致 auto-restore 误把刚刚提交的有意义内容截断掉。因此取消路径必须先 flush 缓冲、再快照The cancel path flushed FIRST, then snapshotted。同文件还有一条配套用例 flushes buffered content before cancellationuse-llm-stream.test.tsx覆盖相同场景下的内容可见性。与之并列的另一条防御性用例验证onCancelSubmit抛出异常时try/finally仍保证setIsResponding(false)与setShellInputFocused(false)执行避免流被永久卡在 Responding 状态导致 ESC 后续失效use-llm-stream.test.tsx。Prompt Stash跨会话的 Prompt 持久化还原除了取消恢复之外Qwen Code 还提供了一套独立的Prompt 暂存机制用于进程重启等场景下还原输入框内容。其实现位于 packages/cli/src/services/prompt-stash.ts包括四个函数savePromptStash(targetDir, text)把当前输入框文本原子写入项目目录下的prompt-stash.jsonversion: 1结构文件权限0o600、目录0o700并启用atomicWriteFileSync与noFollow防止符号链接攻击loadPromptStash(targetDir)读取并校验暂存文件缺失或格式非法时静默返回null注释明确A missing or malformed stash must never prevent CLI startuprestorePromptStash(targetDir, currentText, onRestore)只有暂存存在且当前输入框为空时才执行onRestore——这与取消恢复不覆盖草稿的边界完全一致clearPromptStash(targetDir)删除暂存文件ENOENT视为成功。对应的AppContainer测试覆盖了暂存恢复的若干细节恢复的 Prompt 被视为provenance unavailableAppContainer.test.tsx手动清空输入框后恢复的撤销历史undo history被清空AppContainer.test.tsx以及编辑后的恢复与同文本历史重提交相互区分AppContainer.test.tsx。自动化验证如何运行回归测试原文档给出的自动化验证命令直接作用于 CLI 包的组件层与 Hook 层均可本地复现cd packages/cli npx vitest run src/ui/AppContainer.test.tsx npx vitest run src/ui/hooks/useGeminiStream.test.tsx -t flushes buffered stream events before snapshotting注意文档中的useGeminiStream是历史命名当前仓库中该 Hook 已演进为useLlmStream对应的测试文件为src/ui/hooks/use-llm-stream.test.tsx上述-t过滤的用例名在现仓库中以 flushes buffered stream events before snapshotting pendingItem so cancelling mid-throttle does not lose content 的形式存在可直接用新文件名运行cd packages/cli npx vitest run src/ui/AppContainer.test.tsx npx vitest run src/ui/hooks/use-llm-stream.test.tsx -t flushes buffered stream events before snapshotting原文档记录的运行结果为119 个 AppContainer 测试全部通过流冲刷竞态回归用例通过。这一数字可作为本地运行后的对照基准。手动验证状态与局限说明原文档在 Manual status 章节如实记录了局限性当前环境因缺少一个可用于 before/after 对比的已发布全局qwen可执行文件手动端到端验证未在此环境中执行。也就是说该功能的验证目前完全依赖上述自动化测试覆盖手动确认步骤 4 观察 transcript 中保留部分响应、步骤 5 观察输入框恢复 Prompt仍有待在有可用发布包的终端环境中补做。这一说明本身也是一条重要的工程实践提示交互式 UI 的取消/恢复行为难以在无头环境完整复现因此在设计回归测试时把行为契约拆解为可单测的判定分支是否产生内容、是否匹配lastTurnUserItem、快照是否同步并用组件级与 Hook 级测试钉死是比依赖手工验证更可靠的策略。总结Qwen Code 的取消恢复机制由三层构成行为契约保留部分输出、还原可编辑 Prompt、四条边界检查、实现机制先 flush 缓冲再同步快照的pendingItem、按内容有无分叉的回退/还原路径、prompt-stash 跨会话暂存以及验证体系AppContainer与useLlmStream的 119 个用例。对于想要为类似终端 AI 交互工具实现取消不丢内容体验的开发者本仓库的这套设计与测试可以作为直接参考的实现蓝本。【免费下载链接】qwen-codeAn open-source AI coding agent that lives in your terminal.项目地址: https://gitcode.com/GitHub_Trending/qw/qwen-code创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考