Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
6ab42db
feat: add hybrid code mode
DragonnZhang Sep 14, 2026
fda09c0
fix(core): address hybrid code mode review feedback
DragonnZhang Sep 15, 2026
9a0cd57
chore: merge main into code mode branch
DragonnZhang Sep 15, 2026
30b2a6c
fix(code-mode): address review feedback
DragonnZhang Sep 18, 2026
6c919b7
fix(web-shell): map tool mode setting alias
DragonnZhang Sep 18, 2026
02afce8
fix(core): align code mode bindings and reported tool surfaces
DragonnZhang Sep 20, 2026
2ecf53e
merge: sync main and preserve CodeMode context tests
DragonnZhang Sep 20, 2026
4a38632
merge: preserve hybrid execution with stable tool bridge
DragonnZhang Sep 21, 2026
3239c68
fix: repair web-shell alias tests and address code-mode review feedback
qwen-code-ci-bot Sep 22, 2026
f35beb1
Merge remote-tracking branch 'origin/main' into dragon/add-codemode
qwen-code-ci-bot Sep 22, 2026
28df8ee
fix: align hybrid tool guidance with available invocation surfaces
DragonnZhang Sep 22, 2026
a7315eb
fix: address code-mode review feedback on exec binding narrowing and …
qwen-code-ci-bot Sep 22, 2026
3c69762
Merge branch 'main' into dragon/add-codemode
qwen-code-dev-bot Sep 23, 2026
34711cf
fix(core): avoid unverified agent zoom hints and clarify tool exposure
DragonnZhang Sep 23, 2026
ce7e57d
Merge remote-tracking branch 'origin/main' into dragon/add-codemode
qwen-code-ci-bot Sep 23, 2026
fa457bd
fix(core): narrow MCP nested bindings by the agent tools allowlist
qwen-code-ci-bot Sep 23, 2026
ae35809
fix(core): keep CodeModeOnly exec expansion from bypassing MCP narrowing
qwen-code-ci-bot Sep 24, 2026
4c53574
Merge branch 'main' into dragon/add-codemode
qwen-code-dev-bot Sep 24, 2026
22a9a40
fix: preserve code-mode fork boundaries and update mode guidance
DragonnZhang Sep 26, 2026
779d689
merge: refresh main OAuth discovery before publishing code-mode fixes
DragonnZhang Sep 26, 2026
b1561b7
fix: preserve nested-only fork access and legacy mode resets
DragonnZhang Sep 27, 2026
e6fbdcd
fix(core): preserve empty tool policies when syncing main
DragonnZhang Sep 28, 2026
6edd151
fix: merge main and translate current tool mode settings
DragonnZhang Sep 28, 2026
f746b88
fix(core): merge main and preserve hybrid skill lifecycle
DragonnZhang Oct 1, 2026
ed2e58c
fix(core): merge main and preserve hybrid agent permissions
DragonnZhang Oct 3, 2026
4a72ef8
fix(core): align child skill routes and merge current main
DragonnZhang Oct 4, 2026
cad9407
fix(core): retain code-mode behavior with current main
DragonnZhang Oct 5, 2026
1df2386
fix(cli): align tool mode readouts and merge main
DragonnZhang Oct 6, 2026
71ab2c9
fix(core): preserve code mode policy when merging MCP rule fixes
DragonnZhang Oct 7, 2026
68ceea7
Merge branch 'main' into dragon/add-codemode
qwen-code-dev-bot Oct 7, 2026
f669bd7
Merge branch 'main' into dragon/add-codemode
qwen-code-dev-bot Oct 7, 2026
f183ce0
fix(core): align hybrid tool visibility and prompt budgets
DragonnZhang Oct 8, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
feat: add hybrid code mode
  • Loading branch information
DragonnZhang committed Sep 14, 2026
commit 6ab42db9b41170014c3a3d7ac6168af41894dd8b
26 changes: 14 additions & 12 deletions docs/design/code-mode-only.md
Original file line number Diff line number Diff line change
@@ -1,20 +1,22 @@
# CodeModeOnly MVP

[English](code-mode-only.md) | [简体中文](code-mode-only.zh-CN.md)

## Status

Implemented for [#10377](https://github.com/QwenLM/qwen-code/issues/10377).
The feature is opt-in and defaults off.

## Goal

Add a `tools.codeModeOnly` setting that replaces the ordinary model-facing
tool surface with one `exec` JavaScript tool plus the small set of tools that
must remain direct control-plane calls. `exec` code can call ordinary tools
Add a `tools.mode: "code_mode_only"` setting that replaces the ordinary
model-facing tool surface with one `exec` JavaScript tool plus the small set of
tools that must remain direct control-plane calls. `exec` code can call ordinary tools
through `tools.<name>(args)` without bypassing Qwen Code's validation,
permissions, approvals, hooks, telemetry, cancellation, concurrency, or output
budgets.

Direct mode is a compatibility boundary: when the setting is false, tool
Direct mode is a compatibility boundary: when `tools.mode` is `direct`, tool
registration, deferred-tool behavior, provider requests, and execution remain
unchanged.

Expand All @@ -32,15 +34,15 @@ unchanged.
```json
{
"tools": {
"codeModeOnly": true
"mode": "code_mode_only"
}
}
```

The setting resolves once to the effective `ToolMode` value `direct` or
`code_mode_only`. `ToolRegistry` and the execution surfaces consume that mode.
`exec` is only registered when the setting is enabled, so disabling the setting
also removes it from diagnostics and registry listings.
The setting resolves once to the effective `ToolMode` value. `ToolRegistry`
and the execution surfaces consume that mode. `exec` is only registered when a
code mode is enabled, so selecting `direct` also removes it from diagnostics
Comment thread
DragonnZhang marked this conversation as resolved.
and registry listings.

## Exposure policy

Expand Down Expand Up @@ -171,9 +173,9 @@ closed before scheduling. Invalid arguments continue to fail in the normal
execution chain. A sandbox startup, protocol, timeout, memory, or teardown
failure becomes an `exec` tool error.

Rollback is setting `tools.codeModeOnly` to false. No session migration or
registry cleanup is required because code mode has no persistent state and the
ordinary registry was never replaced.
Rollback is setting `tools.mode` to `direct`. No session migration or registry
cleanup is required because code mode has no persistent state and the ordinary
registry was never replaced.

## Verification

Expand Down
168 changes: 168 additions & 0 deletions docs/design/code-mode-only.zh-CN.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
# CodeModeOnly MVP

[English](code-mode-only.md) | [简体中文](code-mode-only.zh-CN.md)

## 状态

已为 [#10377](https://github.com/QwenLM/qwen-code/issues/10377) 实现。
该功能为可选功能,默认关闭。
Comment on lines +9 to +10

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.

[Suggestion] R1-3: The new ZH version's 状态 section omits the English Status paragraph at docs/design/code-mode-only.md:12-16 ("Partly superseded by Lazy Code Mode: tool_search is now a top-level direct control, and exec omits deferred tool signatures while search is available. The exposure table and the deferred-schema paragraph below describe this MVP; tool_call stays hidden."), and the ZH Sandbox paragraph at line 103 likewise drops EN's clause "whose own scheduler/ACP timeouts remain authoritative" (docs/design/code-mode-only.md:119-121).

The ZH reader never learns that tool_call remains hidden under current behavior, nor that the 暴露策略 table and the deferred-schema paragraph below it describe a superseded MVP rather than shipped behavior — the top blockquote only supersedes "隐藏 tool_search / 始终完整 schema", not the table as a whole. Concretely the ZH table row "隐藏 bridge(tool_search、tool_call)| 严格模式之外沿用既有行为" plus ZH line 76 "CodeModeOnly 会隐藏 tool_search" read as current policy, which the lazy-loading design replaced. At ZH:103 the dropped clause leaves the reader with no statement of which timeout governs while the guest CPU budget and watchdog are paused on a host tool. This violates the repo's own rule in docs/design/README.md: "neither version omits decisions, limitations, acceptance criteria, or follow-up work" and "Do not leave one version with an earlier requirement … that the other version has already answered."

Witness:

`grep -n "Partly superseded|stays hidden|scheduler/ACP timeouts remain authoritative" code-mode-only.md code-mode-only.zh-CN.md` → **3 hits, all in the EN file** (`code-mode-only.md:12`, `:15`, `:120`), zero in the ZH file; and `sed -n '7,11p' code-mode-only.zh-CN.md` → ``` ## 状态 已为 [#10377](…) 实现。 该功能为可选功能,默认关闭。 ``` So the ZH 状态 carries neither the "Partly superseded by Lazy Code Mode … The exposure table and the deferred-schema paragraph below describe this MVP; `tool_call` stays hidden" scope note (EN 12-15) nor, at ZH:103-104, EN's "whose own scheduler/ACP timeouts remain authoritative" (E

Suggested fix: Translate the EN Status paragraph into 状态 (adding the tool_call 仍隐藏 statement and the scope note that the exposure table and deferred-schema paragraph describe the MVP), and add "已注册 host 工具自身的 scheduler/ACP timeout 仍然生效" to the ZH watchdog sentence at line 103.

The fix must not violate this existing fact: docs/design/code-mode-only.md:12-16 is the source text the translation must carry, and docs/design/README.md ("Keep section order and heading levels aligned") requires it land in the existing 状态 section rather than as a new heading.

Acceptance criterion: N/A (documentation text; no guard, branch, or behavior to pin). Please prove it by removing the fix and confirming that test goes red.

中文说明

新增中文版的「状态」小节漏掉了英文 Status 段落(docs/design/code-mode-only.md:12-16)中"Partly superseded by Lazy Code Mode"这一段,导致中英两版结构不同步,违反 AGENTS.md 对双语文档"完整且同步"的要求。

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


## 目标

新增 `tools.mode: "code_mode_only"` 设置,用一个 `exec` JavaScript 工具和少量
必须保留为直接调用的控制面工具,取代面向模型的普通工具面。`exec` 中的代码可通过
`tools.<name>(args)` 调用普通工具,同时不会绕过 Qwen Code 的校验、权限、审批、
hook、遥测、取消、并发或输出预算。

直接模式是兼容性边界:当 `tools.mode` 为 `direct` 时,工具注册、延迟工具行为、
provider 请求和执行均保持不变。

## 非目标

- 混合暴露直接调用和代码调用。
- 在多次 `exec` 调用间持久化 cell、全局变量或值。
- 后台任务、`wait`、`yield`、`store` 或 `load`。
- 原始/freeform provider 调用。
- 在 code mode 中提供 `tool_search` 或 `tool_call` bridge。
- 提供兼容 Node.js 的 sandbox。

## 配置

```json
{
"tools": {
"mode": "code_mode_only"
}
}
```

该设置会解析一次,得到有效的 `ToolMode` 值;`ToolRegistry` 和各执行面使用这一
模式。只有启用某个 code mode 时才注册 `exec`,因此选择 `direct` 也会将它从
诊断信息和 registry 列表中移除。

## 暴露策略

registry 仍是事实来源。暴露只是注册工具之上的视图,而不是第二套 registry。

| 类别 | 模型顶层调用 | `exec` 内的 `tools.*` |
| ----------------------------------------- | --------------- | --------------------- |
| `exec` | 仅 CodeModeOnly | 否 |
| 直接控制工具 | 是 | 否 |
| 已注册的普通工具 | 否 | 是 |
| 隐藏 bridge(`tool_search`、`tool_call`) | 否 | 否 |

直接控制 allowlist 集中维护且刻意保持精简。它覆盖用户交互
(`ask_user_question`)、委派(`agent`)、终止输出约定、
plan/goal/task/team/worktree/session 控制,以及生命周期无法安全隐藏在解释执行程序
后的 ACP host 控制。新工具默认可在 code mode 中调用;增加仅直接调用或隐藏工具时,
必须显式修改策略。

延迟工具保持注册状态和延迟 registry 状态,并可从 `exec` 调用。生成的 `exec`
描述仍会携带这些工具的完整 schema,以及它们在 `ALL_TOOLS` 中的名称和描述:
CodeModeOnly 会隐藏 `tool_search`,嵌套调用也不会以 `functionCall` 出现在历史记录
中,因此后续 reveal 无法补充描述里遗漏的 schema。CodeModeOnly 会跳过延迟预加载
和 ToolSearch 提醒,因为二者都不属于它面向模型的协议。

## 确定性的 JavaScript 接口

每次向 provider 同步工具前,都会从当前 registry 生成 `exec` 描述。工具按规范
名称排序。名称会通过替换无效标识符字符来规范化为 JavaScript 属性;如果名称以
数字开头,还会添加前缀。如果两个规范名称映射到同一个属性,则字典序靠前的名称
胜出,并用一条 warning 指出被省略的冲突项。

描述会定义:

- 全新的异步 JavaScript 执行环境;
- 用于嵌套调用的 `tools.<normalizedName>(args)`;
- 包含规范名称和 JavaScript 名称的 `ALL_TOOLS`;
- `text(value)`、`image(value)`、`audio(value)` 和 `exit()`;
- 从 JSON Schema 确定性生成的类 TypeScript 签名;
- 不提供 Node.js、import、网络 API、timer 和持久状态。
Comment thread
DragonnZhang marked this conversation as resolved.
Outdated

嵌套调用返回一个 JSON-safe 对象,其中包含真实 call id、工具名、状态、输出和
structured content。调用失败或取消时,guest promise 会使用 scheduler/ACP 错误
reject。

## Sandbox 与传输

`exec` 在独立子进程中运行编译为 WebAssembly 的 QuickJS。每次调用都会创建全新
的 QuickJS runtime 和 context。子进程通过 stdio 接收精简的 framed JSON 协议;
其中不包含工具实现或 Qwen 配置。父进程把 JavaScript 名称映射回规范 registry
名称,并分派每次调用。

guest 不提供 Node 全局变量、`require`、`process`、文件系统、socket、模块加载器、
`console`、timer、`Atomics`、`SharedArrayBuffer` 或 `WebAssembly`。由于没有安装
模块加载器,动态和静态 import 都会失败。runtime 的内存和 stack 限制固定。
QuickJS 的 interrupt hook 会限制 guest CPU 预算。当 guest 挂起等待已注册的 host
工具时,该预算和父进程的兜底 watchdog 会暂停;在 guest job 再次运行前恢复。
因此,长时间 build 可以继续使用工具声明的 timeout,同时 guest CPU 死循环无法
逃逸固定预算。源码、协议 frame、helper 输出和最终结果都有上限。

取消操作会中止所有嵌套调用并终止子进程。顶层 promise settled 后,也会在 teardown
前取消未 await 的嵌套调用。子进程、timer、promise handle 或 guest 全局变量都不
会在调用结束后存活。

子进程只接收最小化且经过清理的环境,guest 无法检查该环境。独立进程为解释器故障
提供纵深防御;QuickJS/WASM 是 guest 的 capability 边界。

## 重入式分派

`CoreToolScheduler.schedule()` 无法递归调用:子调用会排在仍在运行的父调用之后,
从而形成死锁。因此,scheduler 会在 invocation 边界绑定 async-local
`ToolCallRuntime` context,`exec` 只与该 context 通信。

scheduler runtime 会将同一 event-loop turn 中收到的嵌套调用合并为一批,并通过
使用相同 `Config` 和 observer 配置的 sibling scheduler 执行。这样无需直接调用
`tool.execute()`,仍能沿用现有的构建/校验、权限、确认、hook、执行、截断、遥测和
并发链路。guest 的连续 await 会生成连续 batch;`Promise.all` 调用会进入同一个
batch,由现有的只读并发分类器处理。嵌套 request id 包含父 id,并携带
Comment on lines +123 to +124

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.

[Suggestion] R1-2: The new Chinese translation states the opposite of the English original (docs/design/code-mode-only.md:144-148) on Code Mode shell concurrency: EN says "Code Mode Bash calls bypass the read-only command classifier, with the model responsible for keeping dependent calls sequential. Other tools retain their existing concurrency classification"; ZH says the batch is "由现有的只读并发分类器处理" and drops the Bash carve-out entirely.

A reader working from the ZH design concludes that shell calls submitted together inside one exec (await Promise.all([...])) are serialized unless the read-only command checker classifies them as read-only. The shipped behavior is the reverse: packages/core/src/core/coreToolScheduler.ts:1557-1563 returns true for source === 'code_mode' + ToolNames.SHELL + Kind.Execute before isShellCommandReadOnly is ever consulted, so two dependent mutating Bash calls in one batch run in parallel. Someone reasoning about ordering safety, or reviewing a nested-call bug from the ZH doc, starts from a false premise; the ZH doc also contradicts docs/design/code-mode-concurrency.zh-CN.md:20-21 ("Code Mode 中的 Bash 调用跳过只读命令判定"), so the two Chinese designs disagree with each other.

Witness:

driving the real `isToolCallConcurrencySafe` (coreToolScheduler.ts:1546-1564) with the same args, varying only `source`: ``` mutating shell (npm install && git push origin main), source=code_mode -> true mutating shell (npm install && git push origin main), source=model -> false read-only shell (git log --oneline -5), source=code_mode -> true read-only shell (git log --oneline -5), source=model -> true ``` i.e. code-mode Bash is classified concurrency-safe *before* `isShellCommandReadOnly` is consulted — the EN sentence is right, the new ZH sentence is the inverse. ---

Suggested fix: Translate the two missing EN sentences into the ZH paragraph, e.g. replace "…Promise.all 调用会进入同一个 batch,由现有的只读并发分类器处理。" with "…Promise.all 调用会进入同一个 batch。Code Mode 中的 Bash 调用跳过只读命令判定,由模型负责让存在依赖的调用保持串行;其他工具沿用现有的并发分类。"

The fix must not violate this existing fact: packages/core/src/core/coreToolScheduler.ts:1557 — // Code Mode lets the model batch independent shell calls explicitly. guarding if (source === 'code_mode' && canonicalName === ToolNames.SHELL && kind === Kind.Execute) { return true; }; the corrected wording must not reassert read-only classification for code-mode Bash, which docs/design/code-mode-concurrency.zh-CN.md:20 also forbids.

Acceptance criterion: N/A (documentation text; no guard, branch, or behavior to pin). Please prove it by removing the fix and confirming that test goes red.

中文说明

新增中文翻译在 Code Mode shell 并发这一点上与英文原文相反:英文说 "Code Mode Bash calls bypass the read-only command classifier",中文写成"由现有的只读并发分类器处理",恰好把豁免说成了适用。同仓库的 code-mode-concurrency.zh-CN.md:21 与英文一致,可证这是翻译错误。实测 isToolCallConcurrencySafe 对同样的 mutating shell 命令在 source=code_mode 时返回 true、source=model 时返回 false。

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

`source: code_mode` 和 `parentCallId`。

嵌套 scheduler 更新会合并到所属 scheduler 的可见调用中,嵌套 id 的确认响应也会
委派给它。外层模型仍只接收完整的 `exec` 响应。

ACP 沿用其独立审计过的执行链。它的 invocation 边界会绑定相同的 runtime 接口,
嵌套分派通过 `Session.runTool` 重入。因此,ACP 顶层和嵌套调用使用相同的 ACP 权限、
审批、hook、遥测、持久化和取消机制,而不是借用 CLI scheduler 状态或直接执行工具。
ACP 会串行执行普通嵌套调用,与现有直接工具顺序一致;Core scheduler 则保留现有的
安全只读并行 batch。

## Provider 行为

所有 provider 继续使用 `ToolRegistry` 提供的 `FunctionDeclaration[]`。在 Direct
模式下,该数组与现有视图逐字节一致。在 CodeModeOnly 中,它是暴露策略生成的视图,
因此 Gemini/Qwen、OpenAI-compatible 和 Anthropic adapter 都会收到结构化的
`exec` declaration,不需要 provider 专用 prompt。

经过过滤的子智能体 declaration 使用相同策略。对于只读 teammate 或带执行
allowlist 的 fork,`exec` 是经过审计的 gateway,而允许的确切嵌套名称会随 invocation
context 传递。同一集合既用于生成描述,也会在 Core 分派前再次校验,因此显式
allowlist 可以收窄 code-mode-callable 嵌套工具,而不会变成仅 prompt 策略、暴露
隐藏 bridge 或使 `exec` 能够递归。对于 cache-compatible fork,继承的 `exec`
declaration 代表其普通 binding:省略 `fork_tools` 时继承这些 binding,显式列表则
用请求的子集替换它们。

## 失败与回滚

未知、冲突、隐藏、仅直接调用和递归请求的工具都会在调度前 fail closed。无效参数
继续在正常执行链中失败。sandbox 启动、协议、timeout、内存或 teardown 失败会转为
`exec` 工具错误。

回滚方式是将 `tools.mode` 设为 `direct`。无需迁移 session 或清理 registry,
因为 code mode 没有持久状态,且普通 registry 从未被替换。

## 验证

单元和集成测试必须覆盖:

- Direct 和 CodeModeOnly 暴露、延迟保留、冲突处理、确定性描述和显式子智能体过滤。
- 有效/无效 JavaScript、异步顺序、`Promise.all`、helper 输出、throw error、CPU
死循环、内存限制、import、不可用全局变量、隔离、输出限制、取消、未 await 工作和
递归调用。
- 嵌套权限、确认、Pre/Post hook、失败、输出预算、真实名称遥测/UI 更新、MCP 工具
和 scheduler 死锁回归。
- Gemini/Qwen、OpenAI-compatible、Anthropic、headless、interactive、subagent
和 ACP 调用面。

只有通过相关 package 测试、build、typecheck、E2E probe 和两轮干净的完整 diff
自审,才算实现完成。
85 changes: 85 additions & 0 deletions docs/design/code-mode.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
# Code Mode

[English](code-mode.md) | [简体中文](code-mode.zh-CN.md)

## Status

Implemented. The feature is experimental and opt-in.

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.

[Suggestion] R1-6: The new hybrid design doc claims full implementation but records nowhere that the wait half of the referenced issue's hybrid mode is absent — wait does not exist in the codebase, and this doc (unlike its sibling) has no Non-goals section to say so.

Issue #10377 defines hybrid as | CodeMode | exec、wait 与直接工具并存 | and its acceptance line reads "Hybrid CodeMode 的 model-visible spec 与 direct + exec/wait 并存 golden tests 在 Phase 6 交付". I grepped the head: there is no ToolNames.WAIT, no name: 'wait' tool, and no yield_control/wait helper anywhere in packages/core/src/code-mode/ — the control plane the issue pairs with exec was never delivered (code-mode-only.md:34 lists it as a non-goal: "Background jobs, wait, yield, store, or load."). A maintainer verifying #10377's Phase 6 acceptance traces to docs/design/code-mode.md, reads "Implemented." plus an acceptance list that omits wait entirely, and ticks the hybrid checkbox on a partial delivery; the missing half is recorded in no document that a Phase-6 auditor would open.

Witness:

`gh issue view 10377 --repo QwenLM/qwen-code` returns the mode table `| CodeMode | `exec`、`wait` 与直接工具并存 | 灰度、调试和模型适配 |`, Phase 6 "增加 direct tools 与 `exec`/`wait` 并存的 Hybrid CodeMode", and the acceptance checkbox "Hybrid CodeMode 的 model-visible spec 与 direct + `exec`/`wait` 并存 golden tests 在 Phase 6 交付" — all exactly as the finding quotes. Against that: `grep "WAIT|EXEC|SKILL" packages/core/src/tools/tool-names.ts` → `EXEC: 'exec'`, `SKILL: 'skill'`, `THREAD_WAIT: 'thread_wait'` — **no `WAIT`/`wait` control tool** (the `\bwait\b` hits under `packages/core/src/code-mode/` are all `code-mode.te

Suggested fix: Add a Non-goals (or Scope) section to docs/design/code-mode.md mirroring the sibling doc — e.g. "- Background jobs, wait, yield, store, or load; hybrid ships exec alongside direct tools only." — and qualify the Status line ("Implemented for the exec half of #10377 Phase 6; wait is not delivered").

The fix must not violate this existing fact: AGENTS.md, General workflow §1: "Provide both an English <name>.md and a Chinese <name>.zh-CN.md version in the same directory … keep both versions complete and synchronized in the same change" — so docs/design/code-mode.zh-CN.md (added by this same diff, ## 状态 → 已实现。该功能为实验性功能,默认关闭。) must carry the identical Non-goals/scope text.

Acceptance criterion: N/A (docs-only; no guard, branch or behaviour to pin). Please prove it by removing the fix and confirming that test goes red.

中文说明

新增的 hybrid 设计文档声称"Implemented",却没有任何地方记录所关联 issue 中 hybrid 模式的 wait 一半并未交付——代码库中不存在 wait 工具,而其姊妹文档在 Non-goals 中明确列出了它。验收 #10377 Phase 6 的维护者会据本文档勾选一个只完成了一半的交付项。

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

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.

[Suggestion] R1-6: The new hybrid design doc claims full implementation but records nowhere that the wait half of the referenced issue's hybrid mode is absent — wait does not exist in the codebase, and this doc (unlike its sibling) has no Non-goals section to say so.

Issue #10377 defines hybrid as | CodeMode | exec、wait 与直接工具并存 | and its acceptance line reads "Hybrid CodeMode 的 model-visible spec 与 direct + exec/wait 并存 golden tests 在 Phase 6 交付". I grepped the head: there is no ToolNames.WAIT, no name: 'wait' tool, and no yield_control/wait helper anywhere in packages/core/src/code-mode/ — the control plane the issue pairs with exec was never delivered (code-mode-only.md:34 lists it as a non-goal: "Background jobs, wait, yield, store, or load."). A maintainer verifying #10377's Phase 6 acceptance traces to docs/design/code-mode.md, reads "Implemented." plus an acceptance list that omits wait entirely, and ticks the hybrid checkbox on a partial delivery; the missing half is recorded in no document that a Phase-6 auditor would open.

Witness:

`gh issue view 10377 --repo QwenLM/qwen-code` returns the mode table `| CodeMode | `exec`、`wait` 与直接工具并存 | 灰度、调试和模型适配 |`, Phase 6 "增加 direct tools 与 `exec`/`wait` 并存的 Hybrid CodeMode", and the acceptance checkbox "Hybrid CodeMode 的 model-visible spec 与 direct + `exec`/`wait` 并存 golden tests 在 Phase 6 交付" — all exactly as the finding quotes. Against that: `grep "WAIT|EXEC|SKILL" packages/core/src/tools/tool-names.ts` → `EXEC: 'exec'`, `SKILL: 'skill'`, `THREAD_WAIT: 'thread_wait'` — **no `WAIT`/`wait` control tool** (the `\bwait\b` hits under `packages/core/src/code-mode/` are all `code-mode.te

Suggested fix: Add a Non-goals (or Scope) section to docs/design/code-mode.md mirroring the sibling doc — e.g. "- Background jobs, wait, yield, store, or load; hybrid ships exec alongside direct tools only." — and qualify the Status line ("Implemented for the exec half of #10377 Phase 6; wait is not delivered").

The fix must not violate this existing fact: AGENTS.md, General workflow §1: "Provide both an English <name>.md and a Chinese <name>.zh-CN.md version in the same directory … keep both versions complete and synchronized in the same change" — so docs/design/code-mode.zh-CN.md (added by this same diff, ## 状态 → 已实现。该功能为实验性功能,默认关闭。) must carry the identical Non-goals/scope text.

Acceptance criterion: N/A (docs-only; no guard, branch or behaviour to pin). Please prove it by removing the fix and confirming that test goes red.

中文说明

新增的 hybrid 设计文档声称"Implemented",却没有任何地方记录所关联 issue 中 hybrid 模式的 wait 一半并未交付——代码库中不存在 wait 工具,而其姊妹文档在 Non-goals 中明确列出了它。验收 #10377 Phase 6 的维护者会据本文档勾选一个只完成了一半的交付项。

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


## Problem

Qwen Code currently supports direct tool calls and `CodeModeOnly`. The latter
replaces ordinary top-level tools with `exec`, which is useful for orchestration
but prevents a model from choosing direct calls when they are clearer or more
efficient. Codex models this as three tool modes: direct, hybrid Code Mode, and
CodeModeOnly.

## Goal

Add a hybrid `CodeMode` option without changing the existing default or
`CodeModeOnly` behavior.

## Configuration

```json
{
"tools": {
"mode": "code_mode"
}
}
```

The effective modes are:

| `tools.mode` value | Effective mode |
| ------------------ | ---------------- |
| omitted / `direct` | `direct` |
| `code_mode` | `code_mode` |
| `code_mode_only` | `code_mode_only` |

This follows Codex's `ToolMode` serialized values. Safe mode and bare mode
force `direct`.

## Exposure policy

| Surface | `direct` | `code_mode` | `code_mode_only` |
| -------------------- | ------------------------ | ------------------------------- | ---------------- |
| Ordinary eager tools | Direct | Direct and nested | Nested only |
| Deferred tools | `tool_search` | `tool_search` and nested | Nested only |
| Direct-control tools | Direct | Direct only | Direct only |
| `exec` | Hidden | Direct | Direct |
| Hidden bridge tools | Existing direct behavior | Direct where already applicable | Hidden |

In `code_mode`, ordinary visible tool descriptions gain an `exec` declaration
for that tool. The `exec` description keeps the complete `ALL_TOOLS` metadata
but does not duplicate every schema. Deferred tools gain their declaration when
their normal top-level declaration is revealed. In `code_mode_only`, the
existing behavior remains: all nested declarations live in the `exec`
description because no later top-level reveal is possible.

Filtered subagent declarations preserve the same mode and narrow both direct
Comment thread
DragonnZhang marked this conversation as resolved.
Outdated
and nested calls to the subagent's allowed tool set.

## Constraints and risks

- Normal direct declarations must remain byte-for-byte unchanged in `direct`.
- Nested calls continue through the existing scheduler or ACP execution path;
the mode must not bypass validation, permissions, hooks, cancellation, or
telemetry.
- Duplicating every schema in hybrid mode would increase prompt size, so only
the per-tool nested declaration is appended there.

## Validation

- Verify mode resolution and safe/bare-mode fallback.
- Verify direct, hybrid, and CodeModeOnly declaration surfaces.
- Verify filtered subagent declarations and nested allowlists.
- Run focused Core and CLI tests, then build and typecheck.

## Acceptance criteria

- `tools.mode: "code_mode"` registers `exec` while retaining ordinary direct
tools and deferred discovery.
- Ordinary visible tools advertise their nested JavaScript signature.
- `tools.mode: "code_mode_only"` selects the stricter behavior.
- The default, safe-mode, and bare-mode surfaces remain direct-only.
79 changes: 79 additions & 0 deletions docs/design/code-mode.zh-CN.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
# Code Mode

[English](code-mode.md) | [简体中文](code-mode.zh-CN.md)

## 状态

已实现。该功能为实验性功能,默认关闭。

## 问题

Qwen Code 目前支持直接工具调用和 `CodeModeOnly`。后者会用 `exec` 替换普通
顶层工具,适合编排调用,但模型无法在直接调用更清晰或更高效时选择直接调用。
Codex 将这套能力建模为三种工具模式:直接模式、混合 Code Mode 和
CodeModeOnly。

## 目标

新增混合 `CodeMode`,同时保持现有默认模式和 `CodeModeOnly` 行为不变。

## 配置

```json
{
"tools": {
"mode": "code_mode"
}
}
```

有效模式如下:

| `tools.mode` 值 | 有效模式 |
| ---------------- | ---------------- |
| 省略 / `direct` | `direct` |
| `code_mode` | `code_mode` |
| `code_mode_only` | `code_mode_only` |

这些值与 Codex 的 `ToolMode` 序列化值一致。安全模式和 bare 模式会强制使用
`direct`。

## 暴露策略

| 调用面 | `direct` | `code_mode` | `code_mode_only` |
| ------------ | -------------------- | ------------------------ | ---------------- |
| 普通即时工具 | 直接调用 | 直接调用和嵌套调用 | 仅嵌套调用 |
| 延迟工具 | `tool_search` | `tool_search` 和嵌套调用 | 仅嵌套调用 |
| 直接控制工具 | 直接调用 | 仅直接调用 | 仅直接调用 |
| `exec` | 隐藏 | 直接调用 | 直接调用 |
| 隐藏桥接工具 | 保持现有直接模式行为 | 在原本适用时直接调用 | 隐藏 |

在 `code_mode` 中,普通可见工具的描述会附加该工具的 `exec` 调用声明。
`exec` 描述保留完整的 `ALL_TOOLS` 元数据,但不重复所有 schema。延迟工具在
按正常流程暴露顶层声明时获得对应声明。在 `code_mode_only` 中保持现有行为:
由于后续无法暴露顶层声明,所有嵌套声明都集中在 `exec` 描述中。

经过过滤的子智能体声明沿用相同模式,并将直接调用和嵌套调用都限制在该子智能体
允许的工具集合内。

## 约束与风险

- `direct` 模式下的普通工具声明必须保持完全不变。
- 嵌套调用继续经过现有 scheduler 或 ACP 执行链,不能绕过校验、权限、hook、
取消和遥测。
- 混合模式若重复全部 schema 会增大提示词,因此只在各顶层工具描述中附加对应的
嵌套声明。

## 验证

- 验证模式解析以及安全模式/bare 模式回退。
- 验证直接、混合和 CodeModeOnly 三种声明面。
- 验证子智能体过滤后的声明和嵌套 allowlist。
- 运行 Core 和 CLI 的相关测试,然后执行构建和类型检查。

## 验收标准

- `tools.mode: "code_mode"` 注册 `exec`,同时保留普通直接工具和延迟发现。
- 普通可见工具会声明其嵌套 JavaScript 调用签名。
- `tools.mode: "code_mode_only"` 选择严格模式。
- 默认模式、安全模式和 bare 模式仍然只使用直接调用。
Loading
Loading