fix(integration-tests): ack the daemon tool guard in the mock ACP child (#9160) - #9162
Conversation
E2E Report — issue #9160 (Main CI failed: E2E Tests — live journal recovery)Root causeThe The three failing tests are the only CI tests that spawn the daemon with the mock ACP child fixture ( Reproduced locally at the exact failing commit before the fix (all 3 tests fail with the error above), and verified after the fix (all 3 pass, twice in a row). FixThe mock ACP child now mirrors the production child handshake: it consumes and deletes the private Note: while smoke-running the gated loadtest that shares this fixture, I observed pre-existing failures unrelated to this issue — VerificationCommands actually run (at the failing commit
中文说明E2E 报告 — issue #9160(主分支 CI 失败:E2E Tests — live journal recovery)根因
这三个失败的测试是 CI 中唯一使用 mock ACP 子进程夹具( 修复前已在完全相同的失败 commit 上本地复现(3 个测试全部以上述错误失败),修复后验证通过(3 个测试全部通过,并连续重复运行两次确认稳定)。 修复方式mock ACP 子进程现在镜像真实子进程的握手行为:它读取并从环境变量中删除私有激活标记 说明:在对共享该夹具的门控 loadtest 做冒烟运行时,发现了与本 issue 无关的既有失败—— 验证实际执行的命令(在失败 commit
🧠 Handled by Qwen Code · model/模型 |
|
✅ Qwen Triage finished — CI landed green on ✅ Qwen Triage 已完成 —— |
|
Thanks for the PR! Template looks good ✓ Problem: observed, not theoretical. Direction: aligned. Main CI is red and this is the repair: teach the test fixture the handshake the daemon now requires. No product-surface change. Size: not applicable — one test fixture, +21/-0, no core paths. Approach: scope feels right. The fixture mirrors the real child — consume and delete the private marker, then acknowledge with the shared protocol constants imported from the bridge package rather than hardcoded strings, so the mock cannot drift from the values the daemon checks. Without the marker the response is unchanged. Nothing to cut, no unrelated edits. Risk: no elevated risk signals — no high-risk paths matched. Moving on to code review. 🔍 中文说明感谢贡献! 模板完整 ✓ 问题:已观测到的真实故障,不是理论问题。 方向:对齐。main CI 红了,这个 PR 就是修复:让测试夹具学会 daemon 现在要求的握手。不涉及产品行为变化。 规模:不适用——单个测试夹具文件,+21/-0,未触及核心路径。 方案:范围合理。夹具镜像真实子进程的行为——消费并删除私有标记,然后使用从 bridge 包导入的共享协议常量(而非硬编码字符串)来确认守卫,mock 不会与 daemon 校验的值脱节。没有该标记时响应保持不变。没有可砍的部分,也没有无关改动。 风险:无升级风险信号——未匹配高风险路径。 进入代码审查 🔍 — Qwen Code · qwen3.8-max Reviewed at |
Code reviewRead against the diff and the guard plumbing on
No blockers, no convention issues. Test evidence (this PR's own CI, via API — no PR code was executed locally)The E2E claim behind this PR cannot be settled by PR CI, and it's worth saying why: the Sandboxed verification would settle this: Final CI results for
One row per check name (latest run); skipped checks omitted; failures sort first. / 每个检查名一行(取最新一次运行),省略 skipped,失败项排在最前。 Real-scenario testing: N/A on this path — unattended CI run; live behavior is exercised only by the isolated 中文说明代码审查对照 diff 和 main 上的守卫实现逐条核对,改动正确且镜像了真实子进程:
无阻塞问题,无规范问题。 测试证据(来自本 PR 自己的 CI,经 API 获取——未在本地执行任何 PR 代码)本 PR 的 E2E 结论无法由 PR CI 证实,原因值得说明: 沙箱验证可以坐实这一点: (CI 状态表格见上方标记区域,finalize 工作流会在 CI 结束后原地更新。) 真实场景测试:本路径不适用——无人值守 CI 运行;真实行为只由隔离的 — Qwen Code · qwen3.8-max Reviewed at |
|
Confidence: 4/5 — clean, minimal, and provably correct against the handshake code; the one reservation is that no check in this PR's CI actually runs the three tests being fixed. Stepping back: this is exactly the repair The reservation, plainly stated: the repo's CI has no pre-merge lane that runs these E2E tests on a PR ( Approving once CI lands green on the reviewed commit. 中文说明置信度:4/5 —— 改动干净、最小,对照握手代码可证明正确;唯一的保留意见是本 PR 的 CI 中没有任何检查真正运行被修复的那三个测试。 退一步看:这正是 保留意见直说:仓库 CI 没有任何在 PR 上运行这些 E2E 测试的 pre-merge 通道( 待 CI 在受审提交上全绿后批准。 — Qwen Code · qwen3.8-max Reviewed at |
Code Coverage Summary
CLI Package - Full Text ReportCore Package - Full Text ReportFor detailed HTML reports, please see the 'coverage-reports-22.x-ubuntu-latest' artifact from the main CI run. |
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
LGTM, looks ready to ship — CI landed green after the review. ✅
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
Partially reviewed — gaps disclosed. Suggestions are inline.
Not reviewed: build-and-test — Integration Tests (CLI, No Sandbox) was skipped in CI and its suite did not run locally.
Not explored to full depth (tool budget reached): "This PR (QwenLM/qwen-code #9162) teaches the mock ACP…": could not run the integration tests / load fixture (worktree has no node_modules or dist; requires npm install + npm run build + npm run bundle).; "This PR (QwenLM/qwen-code #9162) teaches the mock ACP…": could not run ESLint/Prettier against the fixture.; "This PR (QwenLM/qwen-code #9162) teaches the mock ACP…": running the consuming integration tests / loading the fixture live — the worktree has no node_modules or dist (verified), which would require npm install …; "This PR (QwenLM/qwen-code #9162) teaches the mock ACP…": running ESLint/Prettier on the fixture (same missing-install reason); the import grouping matches the file's existing ordering, and no any /style issues are vi…; "This PR (QwenLM/qwen-code #9162) teaches the mock ACP…": none — all checks above completed within the tool budget..
Test Plan (not a blocker): cli/qwen-serve-live-journal-recovery.test.ts — no such file or directory; cli/qwen-serve-webui-live-journal-recovery.test.ts — no such file or directory.
中文说明
仅完成部分审查,审查缺口已披露。 建议见行内评论。
未审查:build-and-test — Integration Tests (CLI, No Sandbox) was skipped in CI and its suite did not run locally。
未探索到全部深度(达到工具调用预算):"This PR (QwenLM/qwen-code #9162) teaches the mock ACP…":could not run the integration tests / load fixture (worktree has no node_modules or dist; requires npm install + npm run build + npm run bundle).;"This PR (QwenLM/qwen-code #9162) teaches the mock ACP…":could not run ESLint/Prettier against the fixture.;"This PR (QwenLM/qwen-code #9162) teaches the mock ACP…":running the consuming integration tests / loading the fixture live — the worktree has no node_modules or dist (verified), which would require npm install …;"This PR (QwenLM/qwen-code #9162) teaches the mock ACP…":running ESLint/Prettier on the fixture (same missing-install reason); the import grouping matches the file's existing ordering, and no any /style issues are vi…;"This PR (QwenLM/qwen-code #9162) teaches the mock ACP…":none — all checks above completed within the tool budget.。
Test Plan(非阻断):cli/qwen-serve-live-journal-recovery.test.ts — no such file or directory; cli/qwen-serve-webui-live-journal-recovery.test.ts — no such file or directory。
— qwen3.8-max via Qwen Code /review (v0.21.11)
| import { | ||
| EXTERNAL_TOOL_GUARD_READY_META_KEY, | ||
| EXTERNAL_TOOL_GUARD_REQUIRED_VALUE, | ||
| PRIVATE_EXTERNAL_TOOL_GUARD_ENV, | ||
| } from '@qwen-code/acp-bridge/externalToolGuard'; |
There was a problem hiding this comment.
[Suggestion] R1-1: A racing duplicate PR (9161) edits these same fixture lines for sibling issue 9159 — both autofix PRs opened about 4 minutes apart, and issue 9160's triage instructed that the repair PR reference both issues. PR 9161 makes the identical handshake change off the same base blob, plus one extra delete process.env[PRIVATE_EXTERNAL_TOOL_GUARD_PROVIDER_ENV] (matching the real child in gemini.tsx/acpAgent.ts). — Failure scenario: whichever of PR 9161 / PR 9162 merges second hits a guaranteed text conflict on agent.mjs; if this PR merges alone, issue 9160 closes but issue 9159 stays open until PR 9161 merges separately. Suggested fix: add Fixes #9159 to this PR's description and close PR 9161 (optionally adopting its extra provider-env delete so the retained variant fully mirrors the real child), or defer to PR 9161 and close this PR. No code change is needed for the issue 9160 fix itself.
中文说明
[建议] 竞争中的重复 PR(9161)正在为姊妹 issue 9159 修改本夹具的同一段代码——两个 autofix PR 相隔约 4 分钟提交,且 issue 9160 的分诊评论要求修复 PR 同时引用两个 issue。PR 9161 基于相同的基础版本对同一文件做了完全相同的握手修改,只是额外多了一行 delete process.env[PRIVATE_EXTERNAL_TOOL_GUARD_PROVIDER_ENV](与真实子进程 gemini.tsx/acpAgent.ts 的行为一致)。— 失败场景:PR 9161 / PR 9162 中后合并的一方必然在 agent.mjs 上产生文本冲突;若仅本 PR 合并,issue 9160 会关闭,但 issue 9159 要等 PR 9161 另行合并后才会关闭。建议处理:在本 PR 描述中添加 Fixes #9159 并关闭 PR 9161(可选地采纳其多出的 provider 环境变量删除行,使保留的变体完全镜像真实子进程),或者改用 PR 9161 并关闭本 PR。就 issue 9160 的修复本身而言无需任何代码改动。
— qwen3.8-max via Qwen Code /review (v0.21.11)
There was a problem hiding this comment.
Code part adopted; PR-management part needs a maintainer.
This round adopted PR 9161's extra line: the mock child now also does delete process.env[PRIVATE_EXTERNAL_TOOL_GUARD_PROVIDER_ENV] (commit ac2fa92abc). Verified against the real children first — acpAgent.ts and gemini.tsx both consume and delete both guard markers, and qwen serve passes exactly these two envs via childEnvOverrides (the token/capability envs are scrubbed daemon-side and never reach the child, so the fixture does not delete them). This PR's fixture change is now a strict superset of PR 9161's.
Not actionable from this flow (no GitHub credentials): adding Fixes #9159 to the PR description and closing PR 9161, or the inverse. Recommendation: keep this PR, add Fixes #9159 to its description, and close PR 9161, since it now carries the complete fixture change. Leaving this thread open for the maintainer's call.
Verification this round: npm run build, npm run bundle, npm run typecheck, npm run lint all pass; both journal-recovery integration suites (the fixture's consumers) pass 3/3 against the bundled CLI with QWEN_SANDBOX=false.
中文说明
代码部分已采纳;PR 管理部分需要维护者处理。
本轮已采纳 PR 9161 多出的那一行:mock 子进程现在同样执行 delete process.env[PRIVATE_EXTERNAL_TOOL_GUARD_PROVIDER_ENV](提交 ac2fa92abc)。实现前已对照真实子进程核实——acpAgent.ts 与 gemini.tsx 都会消费并删除这两个守卫标记,而 qwen serve 通过 childEnvOverrides 传递给子进程的恰好就是这两个环境变量(token/capability 环境变量在守护进程一侧即被清除、不会到达子进程,因此夹具不删除它们)。本 PR 的夹具改动现在是 PR 9161 的严格超集。
本流程无法执行(没有 GitHub 凭据):在 PR 描述中添加 Fixes #9159 并关闭 PR 9161,或反向操作。建议:保留本 PR、在其描述中添加 Fixes #9159、关闭 PR 9161,因为本 PR 已包含完整的夹具改动。此线程保持打开,等待维护者决定。
本轮验证:npm run build、npm run bundle、npm run typecheck、npm run lint 全部通过;两个 journal-recovery 集成测试套件(该夹具的消费方)在 QWEN_SANDBOX=false 下针对打包后的 CLI 运行,3/3 全部通过。
| const externalToolGuardMarker = process.env[PRIVATE_EXTERNAL_TOOL_GUARD_ENV]; | ||
| delete process.env[PRIVATE_EXTERNAL_TOOL_GUARD_ENV]; |
There was a problem hiding this comment.
[Suggestion] R1-2: No PR-stage CI gate exercises this fixture change. The PR-stage integration gate (test:integration:no-ak:sandbox:none) is a hardcoded file list containing none of the fixture's three consumers; test:ci covers no workspace this file belongs to; full coverage lives only in the merge-queue job (integration_cli) and post-merge/nightly e2e.yml. This PR's own CI run skipped Integration Tests (CLI, No Sandbox), so CI verified nothing about this change, and the review's scoped build/test collected zero suites for the file. — Failure scenario: if this fixture edit were wrong (ack _meta key/value mismatch, or the new @qwen-code/acp-bridge/externalToolGuard import failing to resolve at child-spawn time), nothing running on pull_request would catch it — the PR would merge and only then fail the merge queue or post-merge E2E, i.e. the very CI failures this PR fixes would persist or be reintroduced, discovered late. Suggested fix: run the consuming integration tests before merge (the Test Plan reports this was already done for two of the three files):
npm run build && npm run bundle
cd integration-tests && QWEN_SANDBOX=false npx vitest run \
cli/qwen-serve-live-journal-recovery.test.ts \
cli/qwen-serve-webui-live-journal-recovery.test.ts中文说明
[建议] 本夹具改动没有任何 PR 阶段的 CI 门禁覆盖。PR 阶段的集成门禁(test:integration:no-ak:sandbox:none)是一个硬编码的文件列表,其中不包含该夹具的三个使用者测试;test:ci 也不覆盖该文件所属的任何工作区;完整覆盖只存在于合并队列任务(integration_cli)和合并后/每夜的 e2e.yml。本 PR 自己的 CI 运行中 Integration Tests (CLI, No Sandbox) 被跳过,因此 CI 未对本改动做任何验证,且本次评审中按范围运行的构建/测试对该文件也未收集到任何测试套件。— 失败场景:如果该夹具修改有误(确认用的 _meta 键/值不匹配,或新增的 @qwen-code/acp-bridge/externalToolGuard 导入在子进程启动时解析失败),pull_request 阶段运行的任何检查都无法发现——PR 会先合并,随后才在合并队列或合并后 E2E 中失败,也就是说本 PR 要修复的 CI 失败会持续存在或被重新引入,且发现时机滞后。建议处理:在合并前运行消费该夹具的集成测试(测试计划显示作者已对其中两个文件执行过):
npm run build && npm run bundle
cd integration-tests && QWEN_SANDBOX=false npx vitest run \
cli/qwen-serve-live-journal-recovery.test.ts \
cli/qwen-serve-webui-live-journal-recovery.test.ts— qwen3.8-max via Qwen Code /review (v0.21.11)
|
🤖 Addressed the latest review feedback (round 1/10). What changed, and what I pushed back on: · 已处理最新评审反馈(第 1/10 轮)。改动内容与我反驳保留之处如下: Review feedback addressed — PR #9162 (issue #9160), review round 1Two Findings and dispositions1. R1-1 — racing duplicate PR 9161 edits the same fixture lines for sibling issue 9159 (
2. R1-2 — no PR-stage CI gate exercises this fixture change (
Changes
Conflict notes
Verification
中文说明本轮处理的评审反馈 — PR #9162(issue #9160),评审第 1 轮自动评审提出两条 各发现的处理结论1. R1-1 — 竞争中的重复 PR 9161 为姊妹 issue 9159 修改了同一夹具文件(
2. R1-2 — PR 阶段没有任何 CI 门禁覆盖该夹具改动(
改动内容
冲突说明
验证
Base-conflict check · 基分支冲突检查: no conflict with main. · 与 main 无冲突。 Re-review when you have a moment. After round 10 this bot stops and leaves the PR for a human. · 有空请复审;第 10 轮后本 bot 停止并将 PR 交给人工。 🧠 Handled by Qwen Code · model/模型 |
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
Reviewed. Suggestions are inline.
Not explored to full depth (tool budget reached): "This PR teaches the mock ACP child fixture…": running the three live-journal-recovery E2E tests — the review worktree has no node_modules , no built dist/ , and no bundle, so an integration run would requ…; "You are review agent reverse-audit — Reverse audit agent…": none — all planned checks completed within budget..
Test Plan (not a blocker): cli/qwen-serve-live-journal-recovery.test.ts — no such file or directory; cli/qwen-serve-webui-live-journal-recovery.test.ts — no such file or directory.
中文说明
已审查。 建议见行内评论。
未探索到全部深度(达到工具调用预算):"This PR teaches the mock ACP child fixture…":running the three live-journal-recovery E2E tests — the review worktree has no node_modules , no built dist/ , and no bundle, so an integration run would requ…;"You are review agent reverse-audit — Reverse audit agent…":none — all planned checks completed within budget.。
Test Plan(非阻断):cli/qwen-serve-live-journal-recovery.test.ts — no such file or directory; cli/qwen-serve-webui-live-journal-recovery.test.ts — no such file or directory。
— qwen3.8-max via Qwen Code /review (v0.21.11)
| ...(externalToolGuardRequired | ||
| ? { | ||
| _meta: { | ||
| [EXTERNAL_TOOL_GUARD_READY_META_KEY]: | ||
| EXTERNAL_TOOL_GUARD_REQUIRED_VALUE, | ||
| }, | ||
| } | ||
| : {}), |
There was a problem hiding this comment.
[Suggestion] R2-1: The guard ack is attached by spreading a conditional _meta property directly into the initialize() response object, so a second _meta source added to the same literal would clobber it (or be clobbered by it) instead of merging. The real child this fixture mirrors (acpAgent.ts:4873-4894) builds ONE responseMeta record from all conditional sources and attaches _meta exactly once. Probe-confirmed: adding a second _meta before this spread silently drops the new meta (no error); adding one after drops the guard ack. — Failure scenario: a future maintainer extends this mock with another initialize-response meta feature (e.g. the active-work heartbeat meta the bridge parses from every child at bridge.ts:3794-3815); duplicate-key semantics keep only the last _meta, and if the guard ack is the one dropped, every daemon-spawned test using this fixture fails with ACP child did not acknowledge the required external tool guard (received: undefined) — pointing at the daemon/handshake, not at this fixture. Suggested fix — mirror the real child's shape: build one meta record and attach _meta once:
const responseMeta = {
...(externalToolGuardRequired
? {
[EXTERNAL_TOOL_GUARD_READY_META_KEY]:
EXTERNAL_TOOL_GUARD_REQUIRED_VALUE,
}
: {}),
};
return {
protocolVersion: PROTOCOL_VERSION,
agentInfo: { name: 'mock-acp', version: '0.0.1' },
authMethods: [],
agentCapabilities: {},
...(Object.keys(responseMeta).length > 0
? { _meta: responseMeta }
: {}),
};中文说明
[建议] R2-1:守卫确认(guard ack)是通过把一个条件 _meta 属性直接展开到 initialize() 响应对象中来附加的,因此若向同一对象字面量添加第二个 _meta 来源,两者会互相覆盖而不是合并。本夹具所镜像的真实子进程(acpAgent.ts:4873-4894)会用所有条件来源构建一个 responseMeta 记录,并且只附加一次 _meta。已经探针验证:在此展开之前添加第二个 _meta 会静默丢弃新的 meta(无任何报错);在其之后添加则会丢弃守卫确认。— 失败场景:未来维护者为本 mock 增加另一个 initialize 响应 meta 特性(例如 bridge 在 bridge.ts:3794-3815 处从每个子进程解析的 active-work heartbeat meta);重复键语义只保留最后一个 _meta,若丢弃的是守卫确认,所有通过该夹具启动 daemon 的测试都会以 ACP child did not acknowledge the required external tool guard (received: undefined) 失败——错误指向 daemon/握手,而非本夹具。建议修复——镜像真实子进程的结构:构建一个 meta 记录,只附加一次 _meta(代码同上方英文部分)。
— qwen3.8-max via Qwen Code /review (v0.21.11)
|
🤖 Addressed the latest review feedback (round 2/10). What changed, and what I pushed back on: · 已处理最新评审反馈(第 2/10 轮)。改动内容与我反驳保留之处如下: Address review summary — PR #9162 (issue #9160), round 2Feedback points and dispositions1. [Suggestion] R2-1 — guard ack attached via a conditional
|
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
No issues found. LGTM! ✅
Test Plan (not a blocker): cli/qwen-serve-live-journal-recovery.test.ts — no such file or directory; cli/qwen-serve-webui-live-journal-recovery.test.ts — no such file or directory.
中文说明
未发现问题。LGTM!✅
Test Plan(非阻断):cli/qwen-serve-live-journal-recovery.test.ts — no such file or directory; cli/qwen-serve-webui-live-journal-recovery.test.ts — no such file or directory。
— qwen3.8-max via Qwen Code /review (v0.21.11)
|
Released in v0.21.12. |
What this PR does
Teaches the mock ACP child fixture used by the daemon E2E tests to complete the tool-guard handshake that
qwen servenow requires from every child. The fixture consumes and deletes the private guard activation marker from its environment, and when the marker carries the required value it acknowledges the guard in itsinitializeresponse metadata, reusing the shared protocol constants so the mock cannot drift from the values the daemon checks. Without the marker the response is unchanged.Why it's needed
The main-branch
E2E Testsrun failed deterministically on three live-journal-recovery tests. The guard work in #8687 made the daemon's managed tool guard unconditional: the bridge now rejects any ACP child that does not acknowledge the guard during initialize. The realqwen --acpchild acknowledges it, but the mock child used by these tests never did, so every test failed immediately at session creation withACP child did not acknowledge the required external tool guard (received: undefined). These three tests are the only CI tests that spawn the daemon with the mock child (the loadtest sharing it is gated behindQWEN_LOADTEST_ENABLED=1and does not run in CI), which is exactly the failure set the issue reports.Reviewer Test Plan
How to verify
Build and bundle, then run the two affected E2E files against the bundled CLI:
Expected: all 3 tests pass. On the parent commit they fail 3/3 at session creation with the guard-ack error quoted above.
Evidence (Before & After)
N/A (test-infrastructure fix, no user-visible UI change). Test output before the fix:
Tests 3 failed (3)withDaemonHttpError: POST /session: ACP child did not acknowledge the required external tool guard (received: undefined).After the fix:Test Files 2 passed (2) / Tests 3 passed (3), stable across two consecutive runs.Tested on
Environment (optional)
Node 22,
QWEN_SANDBOX=false, bundled CLI (dist/cli.js) resolved by the integration harness.Risk & Scope
qwen-daemon-loadtest.test.tsstill has pre-existing failures unrelated to this fix (qwen/control/session/closemethod not implemented by the mock, protocol drift since feat(daemon): merge daemon-mode feature batch into main #4490); it does not run in CI and repairing it is worth a separate issue. macOS/Windows E2E not run locally — CI covers those.Linked Issues
Fixes #9160
中文说明
本 PR 做了什么
让 daemon E2E 测试使用的 mock ACP 子进程夹具完成
qwen serve现在对每个子进程都要求的工具守卫(tool guard)握手。夹具会读取并从环境变量中删除私有的守卫激活标记;当该标记为要求值时,在initialize响应的元数据中确认守卫,并直接复用共享的协议常量,避免 mock 与 daemon 校验的值脱节。没有该标记时响应保持不变。为什么需要
main 分支的
E2E Tests在三个 live-journal-recovery 测试上确定性失败。#8687 的守卫改造把 daemon 的托管工具守卫改为无条件启用:bridge 现在会拒绝任何在 initialize 阶段未确认守卫的 ACP 子进程。真实的qwen --acp子进程会确认,但这些测试使用的 mock 子进程从未确认,于是每个测试都在创建会话时立即失败,报ACP child did not acknowledge the required external tool guard (received: undefined)。这三个测试是 CI 中唯一用该 mock 子进程启动 daemon 的测试(共享该夹具的 loadtest 被QWEN_LOADTEST_ENABLED=1门控,不在 CI 中运行),与 issue 报告的失败集合完全一致。评审者测试计划
如何验证
构建并打包后,用打包后的 CLI 运行受影响的两个 E2E 文件:
预期:3 个测试全部通过。在父提交上则为 3/3 失败,失败于上述守卫确认错误的会话创建阶段。
前后对比证据
N/A(测试基础设施修复,无用户可见的 UI 变化)。修复前测试输出:
Tests 3 failed (3),错误为DaemonHttpError: POST /session: ACP child did not acknowledge the required external tool guard (received: undefined).;修复后:Test Files 2 passed (2) / Tests 3 passed (3),连续两次运行均稳定通过。测试平台
环境(可选)
Node 22,
QWEN_SANDBOX=false,集成测试框架解析打包后的 CLI(dist/cli.js)。风险与范围
qwen-daemon-loadtest.test.ts仍存在与本修复无关的既有失败(mock 未实现qwen/control/session/close,自 feat(daemon): merge daemon-mode feature batch into main #4490 起的协议漂移);它不在 CI 中运行,修复它建议另开 issue。本地未运行 macOS/Windows E2E——由 CI 覆盖。关联 Issue
Fixes #9160