docs(agent): clarify parameter preconditions - #9580
Conversation
|
Thanks for the PR! Template looks good ✓ Problem: observed, not theoretical — the linked issue #9514 documents two planning exercises in which models reading the Agent tool schema could not determine rules the runtime enforces ( Direction: aligned. The schema text is what models plan tool calls from; stating preconditions and failure modes the same way neighbouring descriptions already do is squarely in scope. Size: core paths are touched but the change is tiny — 9 production-logic lines (description string literals only), 15 test lines, 2 docs lines. No escalation triggers. Approach: matches the issue's asks one-for-one and carries nothing extra — the two main instances plus the mutual-exclusion note, with schema-description regression assertions in the existing test style, and a one-line alignment of the user-facing worktree guide. Nothing I'd cut. Risk: no elevated risk signals (no high-risk paths touched). Moving on to code review. 🔍 中文说明感谢贡献! 模板完整 ✓ 问题:已观测而非理论——关联 issue #9514 记录了两次规划练习,模型仅凭 Agent 工具 schema 无法确定运行时实际强制的规则(无名 subagent 上的 方向:对齐。schema 文本是模型规划工具调用的依据;像相邻描述已有的写法那样写明前置条件和失败方式,完全在项目范围内。 规模:触及核心路径但改动很小——9 行生产逻辑(仅描述字符串字面量)、15 行测试、2 行文档。无升级触发条件。 方案:与 issue 的诉求一一对应,没有多余内容——两个主要实例加上互斥说明,附既有测试风格的 schema 描述回归断言,以及用户侧 worktree 指南的一行对齐。没有需要砍掉的部分。 风险:无升级风险信号(未触及高风险路径)。 进入代码审查 🔍 — Qwen Code · qwen3.8-max Reviewed at |
Code reviewRead against the runtime at the reviewed commit, every new sentence is accurate:
No runtime behavior changes — the diff touches only description string literals, test assertions, and docs prose. One non-blocking observation: the issue's "Anything else" section listed two smaller instances; this PR covers the mutual-exclusion one but not the note that TestingCI on this commit: this is a fork PR, and both
中文说明代码审查对照被审 commit 的运行时代码逐条核对,新增的每一句描述都是准确的:
无运行时行为变化——diff 只触及描述字符串字面量、测试断言和文档文本。 一个非阻塞观察:issue 的"其他"小节列了两个较小实例;本 PR 覆盖了互斥那一条,但没有处理 " 测试本 commit 的 CI:这是 fork PR,两个
— Qwen Code · qwen3.8-max Reviewed at |
|
Confidence: 4/5 — verified word-by-word against the runtime, minimal, and exactly what the issue asked for; the only gap is that the fork's CI hasn't run yet. Stepping back: this is the kind of PR that is easy to vouch for. The problem is real and observed — models reading the schema could not learn rules the runtime enforces — and the fix does precisely what #9514 requested, copying the sibling wording instead of inventing new phrasing. My independent sketch of the fix (update the three descriptions, pin them with schema assertions in the existing test style, align the user guide) is exactly what landed; I found no simpler path and nothing unnecessary in the diff. The additions to The one reservation is procedural, not substantive: the fork's CI is still awaiting workflow approval, so "tests pass on this commit" is the author's claim rather than a green run — static review can vouch for the wording, not for the suite. The two 中文说明这个 PR 很容易让人认可。问题真实且已观测——模型仅凭 schema 无法得知运行时实际强制的规则——修复恰好做了 #9514 要求的事,沿用相邻参数的既有措辞而不是自造新说法。我独立的修复设想(更新三处描述、用既有测试风格的 schema 断言固定下来、对齐用户指南)与落地的完全一致;没找到更简路径,diff 中也没有多余内容。 唯一的保留是流程性的,而非实质性的:fork 的 CI 仍在等待工作流批准,因此"该 commit 测试通过"目前是作者自述而非绿色运行结果——静态审查可以为措辞背书,无法为测试套件背书。两个 — Qwen Code · qwen3.8-max Reviewed at |
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
Partially reviewed — gaps disclosed. Suggestions are inline.
Not explored to full depth (tool budget reached): "agent 3b": none — no check was cut short.; "agent 6b": executing the updated agent.test.ts — the worktree's node_modules is partially broken ( npm ci fails with ENOTEMPTY under packages/mobile-mcp/node_modul…; "agent 1a": run packages/core agent.test.ts to execute the new assertions (worktree has no node_modules ; install+build exceeds the review budget). Substring/mocking v….
Not reviewed: reverse audit — stopped before round 4 by the review time budget.
Test Plan (not a blocker): 2 passed — this review observed 20711, 1638, 22570, 1616, 494, 3860, 556 passed; 261 passed — this review observed 20711, 1638, 22570, 1616, 494, 3860, 556 passed.
中文说明
仅完成部分审查,审查缺口已披露。 建议见行内评论。
未探索到全部深度(达到工具调用预算):"agent 3b":none — no check was cut short.;"agent 6b":executing the updated agent.test.ts — the worktree's node_modules is partially broken ( npm ci fails with ENOTEMPTY under packages/mobile-mcp/node_modul…;"agent 1a":run packages/core agent.test.ts to execute the new assertions (worktree has no node_modules ; install+build exceeds the review budget). Substring/mocking v…。
未审查:反向审计——评审时间预算不足,未能开始第 4 轮。
Test Plan(非阻断):2 passed — this review observed 20711, 1638, 22570, 1616, 494, 3860, 556 passed; 261 passed — this review observed 20711, 1638, 22570, 1616, 494, 3860, 556 passed。
— qwen3.8-max via Qwen Code /review (v0.21.14)
| expect(parameters.properties.read_only?.description).toContain( | ||
| 'named teammate in an active team', | ||
| ); |
There was a problem hiding this comment.
[Suggestion] This diff pins description text promising three read_only preconditions, but the runtime validation branches that enforce them — requires a name, requires an active team, mutual exclusion with plan_mode_required (agent.ts:1199-1216) — have zero tests, while the analogous plan_mode_required branches are tested. A probe deletion confirmed the gap: removing the mutual-exclusion guard (or all three branches) ships green at 262/262, so the enforcement the new schema text promises could be deleted with CI staying green — and the description assertion this diff adds would stay green too, since it only reads text. Mirror the existing plan_mode_required tests, e.g.:
it('rejects read_only without a named teammate', () => {
expect(
agentTool.validateToolParams({ ...validParams, read_only: true }),
).toMatch(/named teammate/i);
});
it('rejects read_only when no team is active', () => {
vi.mocked(config.getTeamManager).mockReturnValue(null);
expect(
agentTool.validateToolParams({
...validParams,
name: 'reader',
read_only: true,
}),
).toMatch(/active team/i);
});
it('rejects combining read_only with plan_mode_required', () => {
vi.mocked(config.getTeamManager).mockReturnValue({
spawnTeammate: vi.fn(),
} as never);
expect(
agentTool.validateToolParams({
...validParams,
name: 'reader',
read_only: true,
plan_mode_required: true,
}),
).toMatch(/cannot be used together/i);
});Probe evidence: deleting only the mutual-exclusion guard → 262/262 pass; deleting all three read_only branches → 262/262 pass; a probe test asserting the rejection fails with the guard removed and passes with it restored.
中文说明
此 diff 固定了承诺 read_only 三项前置条件的描述文本,但执行这些前置条件的运行时校验分支(需要 name、需要 active team、与 plan_mode_required 互斥,agent.ts:1199-1216)没有任何测试,而类似的 plan_mode_required 分支却有测试。探针删除证实了该缺口:删除互斥守卫(或全部三个分支)后 262/262 仍全部通过,即新 schema 文本所承诺的校验可以在 CI 全绿的情况下被删除——本 diff 新增的描述断言也会保持绿色,因为它只读取文本。建议仿照现有 plan_mode_required 测试补充上述测试。
探针证据:仅删除互斥守卫 → 262/262 通过;删除全部三个 read_only 分支 → 262/262 通过;断言该拒绝的探针测试在守卫删除时失败、恢复后通过。
— qwen3.8-max via Qwen Code /review (v0.21.14)
There was a problem hiding this comment.
Fixed in 4c2cc6f. Added direct validateToolParams regression coverage for all three advertised read_only preconditions: named teammate, active team, and mutual exclusion with plan_mode_required. I also mutation-tested the coverage by temporarily removing the three guards; all three new tests failed, then passed again after restoring the runtime validation.
| default: true, | ||
| description: | ||
| 'Defaults to true for top-level regular subagents. Set to false to run a regular agent in the foreground and return its result inline. Set to true for an interactive fork to receive its completion notification; headless forks always run in the background. Nested agents run in the foreground unless run_in_background is explicitly true, which is rejected because they cannot receive background completion notifications. Unnamed caller-owned working_dir launches default to foreground. Named teammates are always concurrent and report through team messaging: omit run_in_background when spawning one — an explicit false is rejected; for an inline blocking result, omit "name" and run a regular agent with run_in_background: false. A teammate pinned to a caller-owned worktree must be shut down before that worktree is removed.', | ||
| 'Defaults to true for top-level regular subagents. Set to false to run a regular agent in the foreground and return its result inline. Set to true for an interactive fork to receive its completion notification; headless forks always run in the background. Nested agents run in the foreground unless run_in_background is explicitly true, which is rejected because they cannot receive background completion notifications. Unnamed caller-owned working_dir launches run in the foreground; explicit run_in_background: true is rejected because the caller owns the worktree lifecycle. Named teammates are always concurrent and report through team messaging: omit run_in_background when spawning one — an explicit false is rejected; for an inline blocking result, omit "name" and run a regular agent with run_in_background: false. A teammate pinned to a caller-owned worktree must be shut down before that worktree is removed.', |
There was a problem hiding this comment.
[Suggestion] The sentence this PR rewrites documents only the explicit-true rejection, but the worktree.md sentence written in this same PR says "reject explicit or configured background execution" — and the configured route is real: a subagent definition with background: true frontmatter, launched unnamed with working_dir while omitting run_in_background, passes validateToolParams (agent.ts:1143 only checks explicit true), then at execute time the omitted flag resolves to subagentConfig.background === true and the spawn guard (agent.ts:2655-2667) rejects the launch — the existing test 'rejects working_dir when the resolved subagent config runs in the background' proves that route. So the schema promises a foreground run where the runtime hard-rejects the launch: the same description-vs-rejection mismatch class issue #9514 is about, in the very sentence this PR rewrote. Consider mirroring the worktree.md wording:
| 'Defaults to true for top-level regular subagents. Set to false to run a regular agent in the foreground and return its result inline. Set to true for an interactive fork to receive its completion notification; headless forks always run in the background. Nested agents run in the foreground unless run_in_background is explicitly true, which is rejected because they cannot receive background completion notifications. Unnamed caller-owned working_dir launches run in the foreground; explicit run_in_background: true is rejected because the caller owns the worktree lifecycle. Named teammates are always concurrent and report through team messaging: omit run_in_background when spawning one — an explicit false is rejected; for an inline blocking result, omit "name" and run a regular agent with run_in_background: false. A teammate pinned to a caller-owned worktree must be shut down before that worktree is removed.', | |
| 'Defaults to true for top-level regular subagents. Set to false to run a regular agent in the foreground and return its result inline. Set to true for an interactive fork to receive its completion notification; headless forks always run in the background. Nested agents run in the foreground unless run_in_background is explicitly true, which is rejected because they cannot receive background completion notifications. Unnamed caller-owned working_dir launches run in the foreground; explicit run_in_background: true and configured background defaults (a subagent definition with background: true) are rejected because the caller owns the worktree lifecycle. Named teammates are always concurrent and report through team messaging: omit run_in_background when spawning one — an explicit false is rejected; for an inline blocking result, omit "name" and run a regular agent with run_in_background: false. A teammate pinned to a caller-owned worktree must be shut down before that worktree is removed.', |
中文说明
本 PR 重写的这句话只写明了显式 run_in_background: true 会被拒绝,但同一 PR 中 worktree.md 的句子写的是"拒绝显式或配置出的后台执行"——配置路径确实存在:带有 background: true frontmatter 的 subagent 定义,在未指定 name 时携带 working_dir 启动且省略 run_in_background,能通过 validateToolParams(agent.ts:1143 只检查显式 true),但在执行期省略的标志会解析为 subagentConfig.background === true,spawn 守卫(agent.ts:2655-2667)会拒绝该启动——现有测试 'rejects working_dir when the resolved subagent config runs in the background' 证明了该路径。即 schema 承诺前台运行,而运行时却硬拒绝该启动:正是 issue #9514 所指的"描述与拒绝不一致"的同类问题,且恰好出现在本 PR 重写的这句里。建议对齐 worktree.md 的措辞(见上方 suggestion)。
— qwen3.8-max via Qwen Code /review (v0.21.14)
There was a problem hiding this comment.
Fixed in 4c2cc6f. The schema now documents both rejection routes: explicit run_in_background: true and configured background defaults from a subagent definition with background: true. The schema assertion was updated to lock in the configured-default wording.
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): "agent 1a": executing the new assertions via vitest in packages/core — the worktree (and parent checkout) has no node_modules , and a full npm ci + monorepo build exce…; "agent 3b": running packages/core agent.test.ts to confirm the new tests pass — the review worktree has no installed dependencies ( vitest unresolvable), so verificati….
Test Plan (not a blocker): 4 passed — this review observed 20636, 1638, 22570, 1616, 494, 3860, 556 passed; 264 passed — this review observed 20636, 1638, 22570, 1616, 494, 3860, 556 passed.
Deferred under the convergence posture (round 2, not a blocker) — recorded, not requested in this round:
packages/core/src/tools/agent/agent.ts:296 — [probe] read_only description omits the team-leader precondition enforced in execute()
中文说明
已审查。 建议见行内评论。
未探索到全部深度(达到工具调用预算):"agent 1a":executing the new assertions via vitest in packages/core — the worktree (and parent checkout) has no node_modules , and a full npm ci + monorepo build exce…;"agent 3b":running packages/core agent.test.ts to confirm the new tests pass — the review worktree has no installed dependencies ( vitest unresolvable), so verificati…。
Test Plan(非阻断):4 passed — this review observed 20636, 1638, 22570, 1616, 494, 3860, 556 passed; 264 passed — this review observed 20636, 1638, 22570, 1616, 494, 3860, 556 passed。
收敛姿态下延后(第 2 轮,非阻断)——已记录,本轮不要求修改:共 1 条(原文未翻译,列表见上方英文部分)。
— qwen3.8-max via Qwen Code /review (v0.21.14)
| default: true, | ||
| description: | ||
| 'Defaults to true for top-level regular subagents. Set to false to run a regular agent in the foreground and return its result inline. Set to true for an interactive fork to receive its completion notification; headless forks always run in the background. Nested agents run in the foreground unless run_in_background is explicitly true, which is rejected because they cannot receive background completion notifications. Unnamed caller-owned working_dir launches default to foreground. Named teammates are always concurrent and report through team messaging: omit run_in_background when spawning one — an explicit false is rejected; for an inline blocking result, omit "name" and run a regular agent with run_in_background: false. A teammate pinned to a caller-owned worktree must be shut down before that worktree is removed.', | ||
| 'Defaults to true for top-level regular subagents. Set to false to run a regular agent in the foreground and return its result inline. Set to true for an interactive fork to receive its completion notification; headless forks always run in the background. Nested agents run in the foreground unless run_in_background is explicitly true, which is rejected because they cannot receive background completion notifications. Unnamed caller-owned working_dir launches run in the foreground; explicit run_in_background: true and configured background defaults (a subagent definition with background: true) are rejected because the caller owns the worktree lifecycle. Named teammates are always concurrent and report through team messaging: omit run_in_background when spawning one — an explicit false is rejected; for an inline blocking result, omit "name" and run a regular agent with run_in_background: false. A teammate pinned to a caller-owned worktree must be shut down before that worktree is removed.', |
There was a problem hiding this comment.
[Suggestion] The new sentence documents two rejection routes for unnamed caller-owned working_dir launches, but the configured-background route is only rejected at the top level. The spawn guard is conditioned on shouldRunInBackground = backgroundRequested && isTopLevelSession() (agent.ts:2654-2655), so a nested launch with a background: true subagent definition never reaches it — the launch is silently downgraded to a foreground run with only a debug log. A nested sub-agent spawning an unnamed working_dir agent whose definition sets background: true, omitting run_in_background, gets a silent foreground launch where the documented rejection is expected. The behavior itself is safe and deliberate (the guard's comment documents the downgrade to avoid over-rejecting a nested call), so this is a wording fix only: qualify the sentence, e.g. "explicit run_in_background: true is rejected, and a configured background default (a subagent definition with background: true) is rejected at the top level (downgraded to a foreground run for nested launches), because the caller owns the worktree lifecycle". The worktree.md sentence carries the same claim and gets a parallel comment. Confirmed by running the two existing tests that pin each arm at this commit — the top-level rejection test and the nested-downgrade test, both green.
中文说明
新句子为匿名 caller-owned working_dir 启动记录了两条拒绝路径,但配置出的后台路径只在顶层会话被拒绝。spawn 守卫以 shouldRunInBackground = backgroundRequested && isTopLevelSession()(agent.ts:2654-2655)为条件,因此带有 background: true subagent 定义的嵌套启动永远到不了该守卫——启动会静默降级为前台运行,仅输出一条 debug 日志。当嵌套 sub-agent 启动一个定义中设置了 background: true 且省略 run_in_background 的匿名 working_dir agent 时,调用方期待文档承诺的拒绝,实际得到的却是静默的前台启动。行为本身安全且是有意为之(守卫注释说明了为避免过度拒绝嵌套调用而降级),因此这只是措辞修正:给句子加限定,例如"显式 run_in_background: true 会被拒绝;配置出的后台默认(background: true 的 subagent 定义)在顶层被拒绝,嵌套启动则降级为前台运行,因为 caller 拥有 worktree 生命周期"。worktree.md 中的句子有相同表述,会收到一条并行评论。已在本提交上运行钉住两种分支的两个既有测试确认——顶层拒绝测试与嵌套降级测试均为绿色。
— qwen3.8-max via Qwen Code /review (v0.21.14)
There was a problem hiding this comment.
Thanks for catching the top-level versus nested distinction. Addressed in c6be15f: the schema now states that explicit run_in_background: true is rejected, while a configured background default is rejected at the top level and downgraded to the foreground for nested launches. The focused rejection and nested-downgrade tests pass.
|
|
||
| - `isolation: "worktree"` requires a non-fork `subagent_type` — forked sub-agents (`subagent_type: "fork"`) reuse the parent's full conversation context, so isolating them would split intent from working tree. | ||
| - Agents using `isolation: "worktree"` follow the default background behavior; the cleanup runs when the agent reports completion. Set `run_in_background: false` for an inline result. Caller-owned `working_dir` launches remain foreground by default because their lifecycle is managed externally. | ||
| - Agents using `isolation: "worktree"` follow the default background behavior; the cleanup runs when the agent reports completion. Set `run_in_background: false` for an inline result. Unnamed caller-owned `working_dir` launches run in the foreground and reject explicit or configured background execution because their lifecycle is managed externally. |
There was a problem hiding this comment.
[Suggestion] Same wording defect as the parallel comment on agent.ts: this rewritten sentence states that unnamed caller-owned working_dir launches "reject explicit or configured background execution", but the configured-background route is only rejected at the top level — the spawn guard is conditioned on shouldRunInBackground, which requires a top-level session (agent.ts:2654-2655), and a nested launch with a background: true subagent definition silently downgrades to a foreground run instead of being rejected (verified by running the existing nested-downgrade test at this commit). Qualify the sentence the same way, e.g. "run in the foreground; explicit or configured background execution is rejected at the top level and downgrades to a foreground run when nested, because their lifecycle is managed externally."
中文说明
与 agent.ts 上的并行评论相同的措辞缺陷:本句重写后声称匿名 caller-owned working_dir 启动"拒绝显式或配置出的后台执行",但配置出的后台路径只在顶层被拒绝——spawn 守卫以 shouldRunInBackground 为条件,要求顶层会话(agent.ts:2654-2655);带有 background: true subagent 定义的嵌套启动会静默降级为前台运行,而非被拒绝(已在本提交上运行既有的嵌套降级测试确认)。请同样为句子加限定,例如"以前台运行;显式或配置出的后台执行在顶层被拒绝,嵌套时降级为前台运行,因为其生命周期由外部管理"。
— qwen3.8-max via Qwen Code /review (v0.21.14)
There was a problem hiding this comment.
Addressed in c6be15f. The worktree guide now matches the runtime semantics: explicit background execution is rejected; configured background execution is rejected at the top level and downgraded to a foreground run when nested.
| expect(properties.properties.run_in_background.description).toContain( | ||
| 'explicit run_in_background: true and configured background defaults', | ||
| ); |
There was a problem hiding this comment.
[Suggestion] The new regression guard pins the subject of the new claim but stops before its verb: if the description's "are rejected" were rewritten to "are supported", this test would still pass, and the #9514 guard would stay green while the model-facing description becomes false. Verified by mutation at this commit — with the description mutated to "are supported" the test stays green, and adding the suggested assertion turns it red. The sibling assertion in the same test ('an explicit false is rejected') already pins its verb the same way.
| expect(properties.properties.run_in_background.description).toContain( | |
| 'explicit run_in_background: true and configured background defaults', | |
| ); | |
| expect(properties.properties.run_in_background.description).toContain( | |
| 'explicit run_in_background: true and configured background defaults', | |
| ); | |
| expect(properties.properties.run_in_background.description).toContain( | |
| 'are rejected because the caller owns the worktree lifecycle', | |
| ); |
中文说明
新的回归守卫钉住了新声明的主语,却停在动词之前:若把描述中的 "are rejected" 改写成 "are supported",该测试仍会通过,#9514 的回归守卫保持绿色,而面向模型的描述已变为错误。已在本提交上做变异验证——将描述变异为 "are supported" 后该测试仍为绿色;加上建议的断言后变红。同一测试中的姊妹断言('an explicit false is rejected')已经以同样方式钉住了动词。
— qwen3.8-max via Qwen Code /review (v0.21.14)
There was a problem hiding this comment.
Addressed in c6be15f. The regression assertions now pin both verbs explicitly: explicit run_in_background: true is rejected, and configured background defaults are rejected at the top level and downgraded for nested launches. The focused schema test is green.
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
Reviewed — no blockers. Suggestions are inline.
Test Plan (not a blocker): 4 passed — this review observed 20714, 1638, 22567, 1616, 494, 3860, 556 passed; 264 passed — this review observed 20714, 1638, 22567, 1616, 494, 3860, 556 passed.
Deferred under the convergence posture (round 3, not a blocker) — recorded, not requested in this round:
packages/core/src/tools/agent/agent.test.ts:697 — [probe] missing absence-pin for read_only in the team-disabled schema half
中文说明
已审查——无阻断问题。 建议见行内评论。
Test Plan(非阻断):4 passed — this review observed 20714, 1638, 22567, 1616, 494, 3860, 556 passed; 264 passed — this review observed 20714, 1638, 22567, 1616, 494, 3860, 556 passed。
收敛姿态下延后(第 3 轮,非阻断)——已记录,本轮不要求修改:共 1 条(原文未翻译,列表见上方英文部分)。
— qwen3.8-max via Qwen Code /review (v0.21.15)
|
|
||
| - `isolation: "worktree"` requires a non-fork `subagent_type` — forked sub-agents (`subagent_type: "fork"`) reuse the parent's full conversation context, so isolating them would split intent from working tree. | ||
| - Agents using `isolation: "worktree"` follow the default background behavior; the cleanup runs when the agent reports completion. Set `run_in_background: false` for an inline result. Caller-owned `working_dir` launches remain foreground by default because their lifecycle is managed externally. | ||
| - Agents using `isolation: "worktree"` follow the default background behavior; the cleanup runs when the agent reports completion. Set `run_in_background: false` for an inline result. Unnamed caller-owned `working_dir` launches run in the foreground; explicit background execution is rejected, while configured background execution is rejected at the top level and downgraded to a foreground run when nested because their lifecycle is managed externally. |
There was a problem hiding this comment.
[Suggestion] This clarified wording updates two of the surfaces that state this rule, but a third copy is left stale: docs/developers/tools/task.md:19 still reads "Caller-owned working_dir launches run in the foreground and reject explicit or configured background execution." The blanket reject for configured background execution is false for nested launches — in agent.ts execute() (~2643–2672), a subagent definition with background: true resolves backgroundRequested = true, the spawn guard only blocks when isTopLevelSession() is true, and a nested launch logs "Background request downgraded to a foreground run" and proceeds in the foreground. A developer consulting task.md expects the nested working_dir + background: true combination to be rejected; the runtime accepts and downgrades it, so after this PR two doc surfaces disagree on exactly the reject-vs-downgrade distinction the PR exists to clarify. Update the run_in_background bullet in task.md to match, e.g. "Unnamed caller-owned working_dir launches run in the foreground: an explicit run_in_background: true is rejected, and a configured background default is rejected at the top level but downgraded to a foreground run when nested."
Witness: the repo's own pinned tests run at the reviewed commit — 'allows working_dir for a background:true subagent that downgrades to foreground when nested' → 1 passed; 'rejects working_dir when the resolved subagent config runs in the background' → 1 passed.
中文说明
这句澄清后的措辞更新了表述该规则的其中两处,但第三份副本仍然过时:docs/developers/tools/task.md:19 仍写着 "Caller-owned working_dir launches run in the foreground and reject explicit or configured background execution."。其中对配置出的后台执行一律 拒绝 的说法在嵌套启动中不成立——在 agent.ts 的 execute()(约 2643–2672 行)中,带有 background: true 的 subagent 定义会解析出 backgroundRequested = true,spawn 守卫只在 isTopLevelSession() 为真时拦截,嵌套启动会记录 "Background request downgraded to a foreground run" 并以前台方式继续执行。阅读 task.md 的开发者会以为嵌套的 working_dir + background: true 组合会被拒绝,而运行时实际接受并降级为前台,因此本 PR 合入后,两份文档恰好在本 PR 要澄清的"拒绝还是降级"这一点上互相矛盾。请将 task.md 中 run_in_background 条目更新为一致的措辞,例如:"Unnamed caller-owned working_dir launches run in the foreground: an explicit run_in_background: true is rejected, and a configured background default is rejected at the top level but downgraded to a foreground run when nested."
证据:在受审提交上运行仓库自带的固定测试——'allows working_dir for a background:true subagent that downgrades to foreground when nested' → 1 passed;'rejects working_dir when the resolved subagent config runs in the background' → 1 passed。
— qwen3.8-max via Qwen Code /review (v0.21.15)
There was a problem hiding this comment.
Fixed in fa42510. The task.md run_in_background entry now scopes the rule to unnamed caller-owned working_dir launches and distinguishes explicit rejection from configured top-level rejection and nested foreground downgrade.
| default: true, | ||
| description: | ||
| 'Defaults to true for top-level regular subagents. Set to false to run a regular agent in the foreground and return its result inline. Set to true for an interactive fork to receive its completion notification; headless forks always run in the background. Nested agents run in the foreground unless run_in_background is explicitly true, which is rejected because they cannot receive background completion notifications. Unnamed caller-owned working_dir launches default to foreground. Named teammates are always concurrent and report through team messaging: omit run_in_background when spawning one — an explicit false is rejected; for an inline blocking result, omit "name" and run a regular agent with run_in_background: false. A teammate pinned to a caller-owned worktree must be shut down before that worktree is removed.', | ||
| 'Defaults to true for top-level regular subagents. Set to false to run a regular agent in the foreground and return its result inline. Set to true for an interactive fork to receive its completion notification; headless forks always run in the background. Nested agents run in the foreground unless run_in_background is explicitly true, which is rejected because they cannot receive background completion notifications. Unnamed caller-owned working_dir launches run in the foreground; explicit run_in_background: true is rejected, while a configured background default is rejected at the top level and downgraded to the foreground for nested launches because the caller owns the worktree lifecycle. A configured default comes from a subagent definition with background: true. Named teammates are always concurrent and report through team messaging: omit run_in_background when spawning one — an explicit false is rejected; for an inline blocking result, omit "name" and run a regular agent with run_in_background: false. A teammate pinned to a caller-owned worktree must be shut down before that worktree is removed.', |
There was a problem hiding this comment.
[Suggestion] The same rule is also stated in this file's usage-guidance bullet at agent.ts:866 ("Unnamed caller-owned working_dir launches default to foreground and cannot run in the background"), and this update does not reach that copy. The bullet is not currently wrong — such a launch never actually runs in the background on any route — but the regression assertions this PR adds pin only the schema wording above. The next edit to this rule will be forced to touch the pinned copy while the unpinned bullet in the same file stays behind, leaving two model-facing texts stating the same rule at different depths indefinitely. Aligning the bullet with this wording in this PR makes all three surfaces move together.
中文说明
同一规则还出现在本文件的使用指引条目 agent.ts:866 中("Unnamed caller-owned working_dir launches default to foreground and cannot run in the background"),本次更新没有覆盖到那份副本。该条目目前并不算错——无论走哪条路径,这类启动最终都不会真正在后台运行——但本 PR 新增的回归断言只固定了上方 schema 措辞。下次修改这条规则时,被固定措辞的副本会被迫同步更新,而同文件中这条未被固定措辞的条目会被落下,两份面向模型的文本将无限期地以不同深度表述同一规则。在本 PR 中将该条目对齐为此处措辞,可让三处表述同步变更。
— qwen3.8-max via Qwen Code /review (v0.21.15)
There was a problem hiding this comment.
Fixed in fa42510. The model-facing usage guidance now carries the same explicit/configured distinction as the schema, and the initialization test pins both the explicit rejection and configured top-level rejection/nested downgrade clauses.
|
|
||
| - `isolation: "worktree"` requires a non-fork `subagent_type` — forked sub-agents (`subagent_type: "fork"`) reuse the parent's full conversation context, so isolating them would split intent from working tree. | ||
| - Agents using `isolation: "worktree"` follow the default background behavior; the cleanup runs when the agent reports completion. Set `run_in_background: false` for an inline result. Caller-owned `working_dir` launches remain foreground by default because their lifecycle is managed externally. | ||
| - Agents using `isolation: "worktree"` follow the default background behavior; the cleanup runs when the agent reports completion. Set `run_in_background: false` for an inline result. Unnamed caller-owned `working_dir` launches run in the foreground; explicit background execution is rejected, while configured background execution is rejected at the top level and downgraded to a foreground run when nested because their lifecycle is managed externally. |
There was a problem hiding this comment.
[Suggestion] A fourth surface still states the old blanket rule: docs/users/features/sub-agents.md:138 says a working_dir launch "cannot be combined with subagent_type: \"fork\" or background execution." The background half of that sentence is false for the nested + configured case — background: true is a real subagent-definition key, and a nested launch with working_dir is accepted and downgraded to a foreground run (agent.ts ~2668–2672; the pinned test 'allows working_dir for a background:true subagent that downgrades to foreground when nested' passes at this commit). The fork half stays correct (fork + working_dir is always rejected) and should be kept. Rewrite the background half to match this sentence, e.g. "an explicit background request is rejected, and a configured background: true default is rejected at the top level and downgraded to a foreground run when nested."
中文说明
还有第四处表述仍写着旧的一刀切规则:docs/users/features/sub-agents.md:138 称 working_dir 启动 "cannot be combined with subagent_type: \"fork\" or background execution."。这句话中关于 background 的半句在嵌套 + 配置路径下不成立——background: true 是真实存在的 subagent 定义字段,嵌套启动携带 working_dir 时会被接受并降级为前台运行(agent.ts 约 2668–2672 行;固定测试 'allows working_dir for a background:true subagent that downgrades to foreground when nested' 在本提交上通过)。关于 fork 的半句仍然正确(fork + working_dir 始终被拒绝),应保留。请将关于 background 的半句改写为与本句一致,例如 "an explicit background request is rejected, and a configured background: true default is rejected at the top level and downgraded to a foreground run when nested."
— qwen3.8-max via Qwen Code /review (v0.21.15)
There was a problem hiding this comment.
Fixed in fa42510. The sub-agents guide preserves the unconditional fork incompatibility and now documents explicit background rejection plus configured top-level rejection/nested foreground downgrade.
|
|
||
| - `isolation: "worktree"` requires a non-fork `subagent_type` — forked sub-agents (`subagent_type: "fork"`) reuse the parent's full conversation context, so isolating them would split intent from working tree. | ||
| - Agents using `isolation: "worktree"` follow the default background behavior; the cleanup runs when the agent reports completion. Set `run_in_background: false` for an inline result. Caller-owned `working_dir` launches remain foreground by default because their lifecycle is managed externally. | ||
| - Agents using `isolation: "worktree"` follow the default background behavior; the cleanup runs when the agent reports completion. Set `run_in_background: false` for an inline result. Unnamed caller-owned `working_dir` launches run in the foreground; explicit background execution is rejected, while configured background execution is rejected at the top level and downgraded to a foreground run when nested because their lifecycle is managed externally. |
There was a problem hiding this comment.
[Suggestion] This sentence introduces "configured background execution" without defining it — it is the only occurrence of "background" in this file, and no page under docs/users/ documents the background: true subagent-definition key it refers to. The schema-description counterpart updated in this same PR carries an explicit gloss ("A configured default comes from a subagent definition with background: true."), which shows the term is not self-explanatory; this sentence omits it. A user whose subagent definition has background: true — and who hits either the top-level rejection or an unexplained foreground run — cannot tell from this page which outcome applies to their setup, because "configured background execution" reads as a near-synonym of the "explicit background execution" earlier in the same sentence. Add the gloss inline, e.g. "...while configured background execution (a subagent definition with background: true) is rejected at the top level and downgraded to a foreground run when nested...".
中文说明
这句话引入了 "configured background execution" 一词却没有定义——它是本文件中唯一出现的 "background",而 docs/users/ 下没有任何页面说明它所指的 background: true subagent 定义字段。同一 PR 中更新的 schema 描述带有明确的解释("A configured default comes from a subagent definition with background: true."),说明该术语并非不言自明;本句却省略了。一位在 subagent 定义中写了 background: true 的用户——无论是遇到顶层拒绝还是不明原因的前台运行——都无法从本页判断自己的配置对应哪种结果,因为 "configured background execution" 读起来与同句前面的 "explicit background execution" 近乎同义。请在句中内联补充解释,例如 "...while configured background execution (a subagent definition with background: true) is rejected at the top level and downgraded to a foreground run when nested..."。
— qwen3.8-max via Qwen Code /review (v0.21.15)
There was a problem hiding this comment.
Fixed in fa42510. The worktree guide now defines configured background execution inline as background: true in a subagent definition.
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
Not explored to full depth (tool budget reached): "agent 6b": executing packages/core/src/tools/agent/agent.test.ts — the review worktree has no node_modules (verified), so test execution was replaced by static verific….
Test Plan (not a blocker): 4 passed — this review observed 20714, 1638, 22567, 1616, 494, 3860, 556 passed; 264 passed — this review observed 20714, 1638, 22567, 1616, 494, 3860, 556 passed.
Deferred under the convergence posture (round 4, not a blocker) — recorded, not requested in this round:
packages/core/src/tools/agent/agent.test.ts:545 — [probe] schema-description assertions leave the leading foreground clause unpinned
中文说明
未探索到全部深度(达到工具调用预算):"agent 6b":executing packages/core/src/tools/agent/agent.test.ts — the review worktree has no node_modules (verified), so test execution was replaced by static verific…。
Test Plan(非阻断):4 passed — this review observed 20714, 1638, 22567, 1616, 494, 3860, 556 passed; 264 passed — this review observed 20714, 1638, 22567, 1616, 494, 3860, 556 passed。
收敛姿态下延后(第 4 轮,非阻断)——已记录,本轮不要求修改:共 1 条(原文未翻译,列表见上方英文部分)。
— qwen3.8-max via Qwen Code /review (v0.21.15)
| - `fork_tools` (array of strings, optional): Only valid with `subagent_type="fork"`. Restricts execution to exact canonical tool names or MCP server patterns while keeping the fork's current model-visible tool declarations unchanged for prompt-cache sharing. Entries cannot have surrounding whitespace; wildcards are limited to `mcp__*` or a trailing MCP tool-prefix pattern such as `mcp__github__read_*`. Forks never execute `ask_user_question`; omit `fork_tools` to allow every other inherited tool, or use an empty array to reject every tool call. | ||
| - `fork_profile` (string, optional): Only valid with `subagent_type="fork"`. Loads a frontmatter-only regular `.qwen/fork-profiles/<name>.md` of at most 64 KiB from the active project root and applies its required `tools` array plus an optional `promptHint` of at most 200 characters. The file cannot resolve outside the project profile directory. `fork_profile` cannot be combined with `fork_tools` or a named teammate, and it is unavailable in safe mode or bare mode. | ||
| - `run_in_background` (boolean, optional): Defaults to `true` for top-level regular agents. Set to `false` to wait for a regular agent's result inline. Headless forks always run in the background. Nested agents run in the foreground unless `run_in_background` is explicitly `true`, which is rejected because nested agents cannot receive background completion notifications. Caller-owned `working_dir` launches run in the foreground and reject explicit or configured background execution. | ||
| - `run_in_background` (boolean, optional): Defaults to `true` for top-level regular agents. Set to `false` to wait for a regular agent's result inline. Headless forks always run in the background. Nested agents run in the foreground unless `run_in_background` is explicitly `true`, which is rejected because nested agents cannot receive background completion notifications. Unnamed caller-owned `working_dir` launches run in the foreground: an explicit `run_in_background: true` request is rejected, while a configured background default (`background: true` in a subagent definition) is rejected at the top level and downgraded to the foreground when nested. |
There was a problem hiding this comment.
[Critical] The bullet this diff adds scopes the foreground rule to unnamed caller-owned working_dir launches, but the unchanged working_dir bullet two lines below (line 21) still states categorically "The caller owns the worktree lifecycle, so this mode runs in the foreground" — a guarantee wider than the code's. Named teammates pinned to a working_dir are always concurrent: team routing returns via executeTeammate before any background classification (agent.ts ~2278), the teammate worktree pin passes the worktree as the teammate's cwd and returns immediately (agent.ts ~4267), and an explicit run_in_background: false for a named teammate is rejected both in validateToolParams (~1172) and at runtime (~2293) — while the tool's own guidance recommends pinning a writer teammate to a leader-owned worktree (agent.ts ~832). The diff re-scoped the equivalent sentence in sub-agents.md, worktree.md, the schema description and the usage notes to "Unnamed" while missing this one, so the page now contradicts itself: pre-diff it was uniformly blanket, post-diff line 19's scoping falsifies line 21. An integrator scripting a team workflow reads the working_dir bullet, expects an inline blocking result when pinning a named teammate to a leader-owned worktree, and instead receives a concurrent spawn confirmation, then an error when trying to force the documented foreground semantics.
Witness (probe at fa42510):
ROW_A (name=writer + working_dir): "Teammate \"writer\" is now running concurrently. … Worktree: /test/project/.qwen/tmp/writer …"
ROW_B (same + run_in_background: false): "Parameter \"run_in_background\" cannot be false for a named teammate: teammates always run concurrently …"
Update the working_dir bullet (line 21) in the same change — e.g.:
- `working_dir` (string, optional): Pin an explicitly named, non-fork agent to an existing registered git worktree inside the current repository. Unnamed launches run in the foreground because the caller owns the worktree lifecycle (see `run_in_background`); a named teammate pinned to one runs concurrently and must be shut down before the worktree is removed. If both `working_dir` and `isolation` are provided, `working_dir` takes precedence.(No one-click suggestion block: this comment is anchored on line 19, the run_in_background bullet, because line 21 is outside the diff hunks — the fix targets line 21.)
中文说明
本 diff 新增的条目将前台规则限定为匿名 caller-owned working_dir 启动,但下方两行的 working_dir 条目(第 21 行,未被修改)仍然绝对地写着 "The caller owns the worktree lifecycle, so this mode runs in the foreground"——承诺范围超出代码实际行为。固定到 working_dir 的具名 teammate 始终并发运行:team 路由在任何后台分类之前就通过 executeTeammate 返回(agent.ts ~2278),teammate worktree 固定把 worktree 作为 teammate 的 cwd 并立即返回(agent.ts ~4267),而对具名 teammate 显式传 run_in_background: false 会在 validateToolParams(~1172)和运行时(~2293)两层被拒绝——与此同时工具自身的指引却推荐把 writer teammate 固定到 leader 拥有的 worktree(agent.ts ~832)。本 diff 已把 sub-agents.md、worktree.md、schema 描述和使用说明中的等价句子都改为 "Unnamed" 限定,唯独漏了这一处,导致该页面自相矛盾:改动前整页是笼统一致的表述,改动后第 19 行的限定使第 21 行失真。集成者按 working_dir 条目编写团队工作流脚本,把具名 teammate 固定到 leader 的 worktree 并期望内联阻塞结果,实际收到的是并发启动确认,再试图强制前台语义时得到错误。
见证(在 fa42510 上的探针执行结果)见英文部分 ROW_A / ROW_B。请在同一修改中更新 working_dir 条目(第 21 行),示例见英文部分。(注:因第 21 行不在 diff hunk 内,本评论锚定在第 19 行即 run_in_background 条目上,修改目标是第 21 行,故不提供一键 suggestion 块。)
— qwen3.8-max via Qwen Code /review (v0.21.15)
There was a problem hiding this comment.
Fixed in f854a19. The working_dir bullet now distinguishes unnamed launches, which run in the foreground, from named teammates, which run concurrently and must be shut down before the caller-owned worktree is removed.
| expect(tool.description).toContain( | ||
| 'an explicit `run_in_background: true` request is rejected', | ||
| ); |
There was a problem hiding this comment.
[Suggestion] This assertion is satisfied by the pre-existing nested-agent sentence, not by the working_dir sentence it was added to pin — the substring occurs twice in tool.description (the usage notes at agent.ts:866 carry both the nested-agent sentence and the new working_dir sentence). Mutate only the working_dir sentence this PR added — flip "is rejected" to "is downgraded to the foreground" in that sentence, inverting exactly the rejected-vs-downgraded distinction the PR clarifies in the model-facing prompt — and the whole agent.test.ts stays green (probe: 265 passed with the mutant in place), so the regression ships with the #9514 guard green. The schema-property test is unaffected (the phrase is unique there); only the usage-notes half is unpinned. Anchoring the assertion to the working_dir sentence fixes it — verified: the longer assertion fails the mutant and passes the real code.
| expect(tool.description).toContain( | |
| 'an explicit `run_in_background: true` request is rejected', | |
| ); | |
| expect(tool.description).toContain( | |
| 'Unnamed caller-owned `working_dir` launches run in the foreground: an explicit `run_in_background: true` request is rejected', | |
| ); |
中文说明
该断言命中的是既有的 nested-agent 句子,而非它要保护的 working_dir 句子——这个子串在 tool.description 中出现两次(agent.ts:866 的使用说明同时包含 nested-agent 句子和新增的 working_dir 句子)。只变异本 PR 新增的 working_dir 句子——把该句中的 "is rejected" 翻转为 "is downgraded to the foreground",恰好颠倒本 PR 在模型可见提示中澄清的"拒绝/降级"区分——整个 agent.test.ts 仍然全绿(探针:变异体下 265 通过),该回归会在 #9514 守卫保持绿色的情况下溜走。schema 属性测试不受影响(该短语在那里唯一);只有使用说明这一半未被固定。将断言锚定到 working_dir 句子即可修复——已验证:加长后的断言能使变异体失败、在真实代码上通过。
— qwen3.8-max via Qwen Code /review (v0.21.15)
There was a problem hiding this comment.
Fixed in f854a19. The assertion is now anchored to the complete working_dir clause (Unnamed caller-owned working_dir launches...), so it no longer passes by matching the earlier nested-agent sentence. Mutation verification confirmed that changing only the working_dir rejection to a downgrade makes the focused test fail; the restored code passes all four focused tests.
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
No blocking issues. LGTM! ✅
Test Plan (not a blocker): 4 passed — this review observed 20714, 1638, 22570, 1616, 494, 3860, 556 passed; 264 passed — this review observed 20714, 1638, 22570, 1616, 494, 3860, 556 passed.
Deferred under the convergence posture (round 5, not a blocker) — recorded, not requested in this round:
packages/core/src/tools/agent/agent.test.ts:714 — [probe] schema test missing read_only absence-pin for the teams-disabled half
中文说明
无阻断问题。LGTM!✅
Test Plan(非阻断):4 passed — this review observed 20714, 1638, 22570, 1616, 494, 3860, 556 passed; 264 passed — this review observed 20714, 1638, 22570, 1616, 494, 3860, 556 passed。
收敛姿态下延后(第 5 轮,非阻断)——已记录,本轮不要求修改:共 1 条(原文未翻译,列表见上方英文部分)。
— qwen3.8-max via Qwen Code /review (v0.21.15)
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
No blocking issues. LGTM! ✅
Not explored to full depth (tool budget reached): "agent 6c": execute the new agent.test.ts assertions (no node_modules/dist in the shared review worktree; npm ci + root build prerequisite exceeded the review budget) — s….
Test Plan (not a blocker): 4 passed — this review observed 20747, 1661, 22839, 1638, 495, 4026, 595 passed; 264 passed — this review observed 20747, 1661, 22839, 1638, 495, 4026, 595 passed.
Deferred under the convergence posture (round 6, not a blocker) — recorded, not requested in this round:
packages/core/src/tools/agent/agent.test.ts:717 — [probe] read_only schema removal has no absence-pin for the teams-disabled refresh path
中文说明
无阻断问题。LGTM!✅
未探索到全部深度(达到工具调用预算):"agent 6c":execute the new agent.test.ts assertions (no node_modules/dist in the shared review worktree; npm ci + root build prerequisite exceeded the review budget) — s…。
Test Plan(非阻断):4 passed — this review observed 20747, 1661, 22839, 1638, 495, 4026, 595 passed; 264 passed — this review observed 20747, 1661, 22839, 1638, 495, 4026, 595 passed。
收敛姿态下延后(第 6 轮,非阻断)——已记录,本轮不要求修改:共 1 条(原文未翻译,列表见上方英文部分)。
— qwen3.8-max via Qwen Code /review (v0.21.15)
Maintainer local verification —
|
arm (full agent.test.ts) |
result |
|---|---|
| head (control) | 265/265 green |
agent.ts reverted to base + head tests |
exactly the 3 schema-description tests red, 262 green — and the 3 new validateToolParams tests stay green ⇒ they pin pre-existing runtime |
| mutual-exclusion guard deleted | exactly rejects combining read_only with plan_mode_required red (positive control; closes the zero-coverage gap the first CI review round flagged) |
usage-note verb flipped (is rejected → is downgraded) |
exactly the pinned usage-notes test red (the "satisfied by the nested-agent sentence" finding from round 1 stays fixed) |
Suite gates: head 265/265, base 262/262 (Δ = +3 = exactly the new tests; the Windows-only path failure in the PR description does not reproduce on macOS), core tsc --noEmit clean. Trial merge into main@2855149d (which had already touched agent.ts/agent.test.ts in disjoint regions, and a diverging lockfile — merged tree got its own npm ci): 265/265 green, wire schema still carries all five sentences, rejections identical, docs pages updated — 14/14 merge-arm assertions.
Not covered
- The configured-background (
background: truedefinition) top-level rejection / nested downgrade was verified at suite level (the two pre-existing pinned tests, green on both arms) plus code reading ofagent.ts:2643–2672— not driven end-to-end through the wire harness (needs a background subagent definition + full execute). - No real model call — the loopback server is a wire-shape oracle; issue Agent tool parameters document effects but not preconditions or failure modes #9514's planning exercises were not re-run.
- Windows-only path assertion not reproduced (macOS arm64 round); ESLint/Prettier and the CVE audit left to CI (lockfile byte-identical base↔head, so the audit result cannot differ).
中文摘要
结论:merge-ready —— 52/52 条脚本化断言全部通过。(本验证进行期间 PR 已由 wenshao squash 合并为 3a9d2d37,本报告作为对已合入内容的独立确认。)
- A/B 双向证明:用真实 CLI 二进制 + 本地回环 OpenAI 服务器捕获完整请求体。head 的 Agent 工具 schema 携带全部五条新契约句(
read_only需具名 teammate 且与plan_mode_required互斥;working_dir显式后台被拒绝、配置默认后台顶层拒绝/嵌套降级为前台),base 全部缺失;屏蔽这 4 处描述后两臂 schema 逐字节相同。5 个拒绝场景(含team_create建队后的互斥拒绝多轮流程)报错文本 base↔head 逐字节一致 —— 运行时行为未变,与 PR 声明一致。 - 变异矩阵:还原描述后恰好 3 个 schema 测试变红(文本断言 load-bearing)、3 个新运行时测试仍绿(钉的是既有行为);删除互斥 guard 恰好对应测试变红(阳性对照,封住 CI 首轮指出的零覆盖缺口);翻转 usage-notes 动词恰好对应测试变红。
- 套件门禁:head 265/265、base 262/262(差值 +3 恰为新增测试;Windows 路径失败在 macOS 不复现);core typecheck 干净;对 main@2855149d 的 trial merge 无冲突且全绿(14/14)。
- 未覆盖:
background: true配置默认的顶层拒绝/嵌套降级仅在套件层 + 代码阅读验证,未走 wire 端到端;未调用真实模型;Windows 断言未复现;CVE audit 交由 CI(lockfile 两臂逐字节相同,结果不可能不同)。
|
Released in v0.22.2. |



What this PR does
Clarifies the Agent tool's advertised parameter contracts without changing runtime behavior:
read_onlyrequires a named teammate in an active team;read_onlyandplan_mode_required;working_dirrejects explicit background execution, while configured background execution is rejected at the top level and downgraded to the foreground when nested;Why it's needed
The runtime already rejects these invalid combinations, but the tool schema described effects or defaults without stating the corresponding preconditions and failure modes. Because the schema guides model tool calls, the mismatch can cause avoidable invalid Agent invocations.
Reviewer Test Plan
How to verify
read_onlysays it requires a named teammate in an active team and cannot be combined withplan_mode_required.plan_mode_requireddocuments the reciprocal incompatibility.run_in_backgroundstates that an unnamed caller-ownedworking_dirrejects explicit background execution, rejects configured background execution at the top level, and downgrades configured background execution to the foreground when nested.Evidence (Before & After)
N/A — tool-schema and documentation-only change.
Tested on
Environment (optional)
Windows 11, Node.js 24.15.0, npm 11.12.1.
Verification:
npm run test --workspace=packages/core -- --run src/tools/agent/agent.test.ts -t "declares the background default and foreground opt-out|rejects read_only without a named teammate|rejects read_only when no team is active|rejects combining read_only with plan_mode_required"— 4 passed.npm run typecheck --workspace=packages/core— passed.npm run build -- --cli-only— passed.git diff --check— passed.agent.test.ts: 264 passed, 1 existing Windows-only path assertion failed because the test expects/test/project/...while Windows returnsD:\\test\\project\\...; the schema tests changed here passed.Risk & Scope
Linked Issues
Fixes #9514
中文说明
此 PR 的改动
在不改变运行时行为的前提下,明确 Agent 工具公开参数的契约:
read_only只能用于 active team 中的具名 teammate;read_only与plan_mode_required互斥;working_dir会拒绝显式后台执行;配置出的后台执行在顶层被拒绝,在嵌套调用中降级为前台;为什么需要
运行时已经会拒绝这些非法组合,但工具 schema 只描述了效果或默认值,没有写清前置条件和失败方式。由于 schema 会指导模型调用工具,这种不一致可能导致本可避免的无效 Agent 调用。
Reviewer Test Plan
验证方式
read_only写明需要 active team 中的具名 teammate,且不能与plan_mode_required组合。plan_mode_required也写明对应的互斥关系。run_in_background写明匿名 caller-ownedworking_dir会拒绝显式后台执行、在顶层拒绝配置出的后台执行,并在嵌套调用中将配置出的后台执行降级为前台。修改前后证据
N/A——仅修改工具 schema 描述和文档。
测试平台
环境
Windows 11、Node.js 24.15.0、npm 11.12.1。
验证结果:
npm run build -- --cli-only通过;git diff --check通过;agent.test.ts为 264 通过、1 失败;唯一失败是既有 Windows 路径断言,测试期望/test/project/...,Windows 实际返回D:\\test\\project\\...,本 PR 修改的 schema 测试均通过。风险与范围
关联 Issue
Fixes #9514