qwen-code/docs
Dragon 2c95d9383f
Some checks are pending
E2E Tests / E2E Test (Linux) - sandbox:docker (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:none (push) Waiting to run
E2E Tests / E2E Test - macOS (push) Waiting to run
feat(core,cli): unified reasoning effort with /effort command (#6072)
* feat(core,cli): unified reasoning effort with /effort command

Expose a provider-agnostic reasoning-effort ladder (low/medium/high/xhigh/max) through a new /effort slash command and a global model.reasoningEffort setting. Bare /effort opens an interactive tier picker (and lists tiers in non-interactive/ACP mode); /effort <tier> sets one directly. A single canonical reasoning.effort config is translated and clamped per provider so one control works everywhere:

- OpenAI: reasoning_effort (max clamps to xhigh)
- DeepSeek: flat reasoning_effort (low/medium→high, xhigh→max)
- GLM / z.ai: new adapter flattens nested reasoning.effort to a verbatim reasoning_effort
- Anthropic: output_config.effort, gated per model — Opus 4.7/4.8 and 5.x families pass xhigh/max through; Opus 4.6/Sonnet 4.6 take max only; older clamp to high
- Gemini: thinking_level (adds medium; xhigh/max cap at HIGH)
- Qwen / DashScope: a set effort turns on enable_thinking (no per-tier field yet — the single column to extend when qwen ships one)

Config.setReasoningEffort updates the live config so the change takes effect on the next turn without an auth refresh, and is re-applied across model switches and auth refreshes (including the initial one at startup) so the effort survives /model switches and CLI restarts — even for provider-backed models, whose preset sync would otherwise overwrite reasoning and drop the persisted effort. The model-with-reasoning status line now tracks the effort and updates immediately when /effort changes it.

A new core/reasoning-effort.ts holds the canonical tier list, rank-based clamp, and input normalizer shared across providers and the command.

* fix(core): reject date suffix as Anthropic minor version for effort gating

The version regex parsed the 8-digit date suffix of dated model ids
(e.g. claude-opus-4-20250514 = Opus 4.0) as the minor version, so
atLeast(4, 6)/atLeast(4, 7) returned true and Opus 4.0 was wrongly
granted the xhigh/max effort tiers it does not support, producing an
HTTP 400 on /effort max with no clamp warning.

Cap the minor-version group at one or two digits with a trailing (?!\d)
so a date suffix is not mis-parsed (a bare {1,2} cap is insufficient: it
would still greedily capture '20' from '20250514'). Dated ids that do
carry a minor, like claude-opus-4-7-20251101, still resolve to minor 7.

Adds a regression test asserting effort 'max' clamps to 'high' on
claude-opus-4-20250514.

* fix(i18n): translate /effort command description for all locales

The mustTranslateKeys strict-parity test failed because the new /effort
command description fell back to English in zh and zh-TW. Add the
description string to every locale file so the command is translated like
its siblings and strict-parity locales no longer fall back.

* fix(core): correct DeepSeek effort mapping and adaptive-thinking model parse

Address two review findings on the reasoning-effort path in
anthropicContentGenerator:

- DeepSeek anthropic-compatible output_config.effort accepts only high/max,
  but resolveEffectiveEffort passed low/medium through verbatim (only
  remapping xhigh -> max), which the endpoint would 400 on. Mirror the
  DeepSeek OpenAI adapter (deepseek.ts): low/medium lift to high, xhigh/max
  group to max.

- modelSupportsAdaptiveThinking used /claude-(?:opus|sonnet|haiku)-(\d+)-(\d+)/,
  so a dated id like claude-opus-4-20250514 (Opus 4.0) parsed the 8-digit date
  suffix as the minor version (>= 6), wrongly selecting adaptive thinking and
  tripping a server 400. Apply the same date-suffix-guarded regex the sibling
  anthropicSupportedEffortTiers already uses, also covering the fable/mythos
  families and a bare major (claude-opus-5).

Add regression tests for both.

* fix(core,cli): address /effort review feedback

- anthropic: family-guard the `max` tier on the 4.x branch so haiku 4.x no
  longer gains `max` (a server 400); 5.x families still get it via major>=5.
- effort command: when `setReasoningEffort` no-ops because thinking is disabled
  (reasoning: false), report that the tier won't take effect yet instead of a
  misleading success message (the tier is still persisted for future sessions).
- dashscope: drop the pipeline-injected nested `reasoning` object when
  `enable_thinking` is set, so qwen never receives two competing thinking knobs
  (mirrors deepseek.ts / zai.ts).

Adds regression tests for each.

* fix(core): collapse empty reasoning to undefined and drop redundant effort re-apply

setReasoningEffort(undefined) now resets reasoning to undefined instead of
leaving a truthy empty object, which the pipeline would emit as wire noise
and which would make 'if (cfg.reasoning)' a false positive.

handleModelChange no longer re-applies the reasoning effort after the
full-refresh path: refreshAuth already captures and restores it, so the
second call was redundant. The qwen-oauth hot-update path keeps its own
re-apply because it never routes through refreshAuth.

* fix(core): log once when Gemini clamps xhigh/max effort to HIGH

Gemini has no tier above HIGH, so /effort xhigh|max silently ran at HIGH
with no trace. Mirror the Anthropic generator's one-time clamp warning via
a per-generator latch so the downgrade is discoverable in debug logs.

* fix(core): warn when GLM model on non-Z.ai host skips reasoning_effort flatten

isZaiProvider routes any glm-* model through this provider, but the
reasoning_effort reshape stays hostname-gated. A self-hosted GLM on a
non-Z.ai hostname therefore kept its nested reasoning.effort unflattened
silently. Keep the reshape hostname-gated (so we never push GLM's flat
field at an arbitrary OpenAI-compatible backend) but log the skip once so
the gap is discoverable.

* docs(core,cli): correct false OpenAI 'max'->'xhigh' clamp claim

The reasoning-effort docs claimed OpenAI adapters clamp 'max' to 'xhigh',
but the default OpenAI-compatible pipeline forwards the tier verbatim and
no such clamp exists. Reword the doc comment and the reasoningEffort
setting description (and the regenerated VS Code schema) to match.

* fix(cli): clarify effort confirmation and improve the effort dialog UX

- The /effort confirmation now states the tier is the requested one and the
  effective tier depends on the active provider/model, instead of implying
  the shown tier is what reaches the wire (providers clamp per model).
- EffortDialog no longer pre-selects 'high' when no effort is configured;
  the cursor starts at the top and a footer notes the provider default is
  in effect, so the highlight is not mistaken for the current setting.
- The dialog path now mirrors the slash-command read-back: when thinking is
  disabled (setReasoningEffort is a no-op), it surfaces an info message that
  the tier is persisted but won't take effect until thinking is re-enabled.

* refactor(core): extract shared parseClaudeModelVersion for Claude effort gating

anthropicSupportedEffortTiers and modelSupportsAdaptiveThinking each parsed
Claude model ids with their own near-identical regex (family list +
date-suffix guard), kept in sync only by a comment. Extract a single
parseClaudeModelVersion helper both consume so adding a new family is a
one-line change with no drift risk between effort tiers and the thinking
shape.

* fix(anthropic): drop manual budget_tokens on models that reject it

Opus 4.7+ and every 5.x family (Sonnet 5, Fable 5, Mythos 5) dropped
manual extended thinking: a request with
thinking:{type:'enabled', budget_tokens:N} returns a 400 on those models,
which require adaptive thinking with output_config.effort instead
(https://platform.claude.com/docs/en/build-with-claude/effort).

buildThinkingConfig honored an explicit reasoning.budget_tokens on every
model, so a preset carrying budget_tokens (with or without /effort) shipped
the manual shape to an adaptive-only model and 400'd. Gate the escape hatch
behind a new modelRejectsManualThinking() predicate so the budget is only
emitted for models that still accept it (Opus 4.5/4.6, Sonnet 4.6, older
4.x, and unknown/unversioned ids); adaptive-only models fall through to
{type:'adaptive'} and let output_config.effort govern thinking depth.

* fix(cli,core): warn on invalid model.reasoningEffort; guard hot-update field set

- modelConfigUtils: surface a startup warning when settings.model.reasoningEffort
  holds an unrecognized value (normalizes to undefined and was silently skipped),
  so a typo in settings.json doesn't leave /effort with no visible effect.
- config: document that the qwen-oauth hot-update field set deliberately excludes
  `reasoning`, which is captured and re-applied via setReasoningEffort() — adding
  it to the copied set would mask the effort restore across model switches.

* fix(core,cli): address /effort review feedback

- config: re-apply reasoning effort on the full-refresh model-switch path.
  switchModel() wipes modelsConfig.reasoning via applyResolvedModelDefaults
  before handleModelChange fires, so refreshAuth's own capture reads undefined;
  restore the tier from the live config. Add a regression test.
- gemini: make buildThinkingConfig's effort switch exhaustive (explicit
  'undefined' case + never-guarded default) so a new tier is a compile error
  rather than a silent map to UNSPECIFIED.
- anthropic: emit a one-time warning when budget_tokens is dropped on models
  that reject manual thinking (Opus 4.7+/5.x); reword the version-regex comment
  to correctly credit the (?!\\d) lookahead. Add reseller-prefixed and 5.x
  effort-gating tests.
- cli/effort: confirm the requested tier in-chat on dialog success (mirror the
  slash-command message); add hook tests.
- tests: add dashscope vision-branch effort test and determineProvider z.ai
  routing tests.

* fix(core): harden reasoning-effort normalize and correct stale clamp comment

- normalizeReasoningEffort: reject non-string input (e.g. a hand-edited
  settings.json with a boolean/number reasoningEffort) instead of calling
  .trim() on it and crashing at startup; add regression tests.
- contentGenerator: update the reasoning-effort ladder comment to reflect
  the per-model Anthropic tier gating (Opus 4.7+/5.x accept xhigh/max,
  Opus/Sonnet 4.6 accept max, older models cap at high) instead of the
  outdated blanket 'xhigh/max -> high' description.

Addresses PR review feedback.

* fix: remove stray OpenClaw-Query-Submit gitlink

This branch carries a submodule gitlink (mode 160000, commit be66572)
with no .gitmodules entry. It entered via a merge from an older main
state; current main no longer contains it, so this branch would
re-introduce it on merge, breaking clone --recursive and any submodule
init. Remove it from the index.

* test(cli): add EffortDialog render tests

Adds render/behavior tests for EffortDialog mirroring
ApprovalModeDialog.test.tsx: renders the title and all five tiers,
shows/hides the 'no effort configured' hint based on currentEffort,
and verifies the active Escape handler cancels with undefined while
non-Escape keys do not.

* refactor(anthropic): single-source Claude family list; warn on DeepSeek effort remap

Derive the ClaudeModelFamily union and the model-id regex from one
CLAUDE_MODEL_FAMILIES const so the type and the parser can't drift apart
behind the 'as ClaudeModelFamily' cast.

Emit a one-time debugLogger.warn when the DeepSeek anthropic-compatible
branch remaps a requested effort tier (low/medium -> high, xhigh -> max),
mirroring the real-Anthropic clamp path so a silently upgraded effort is
visible in debug logs.

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: DragonnZhang <dragonzhang1024@gmail.com>
2026-07-01 20:20:28 +00:00
..
design feat(daemon,sdk): resumable /acp session stream (Last-Event-ID) + opt-in SDK transports export (#5852) 2026-06-30 02:07:48 +00:00
developers feat(cli): Harden daemon-managed channel worker (#6098) 2026-07-01 13:26:38 +00:00
e2e-tests feat(worktree): Phase D — startup --worktree flag + symlinkDirectories + PR refs (#4381) 2026-05-27 17:04:51 +08:00
plans feat(core)!: redesign auto-compaction thresholds with three-tier ladder (#4345) 2026-05-25 21:11:08 +08:00
superpowers feat(channel): add channel loop support (#6073) 2026-07-01 05:07:21 +00:00
users feat(core,cli): unified reasoning effort with /effort command (#6072) 2026-07-01 20:20:28 +00:00
verification/abort-controller-refactor fix(core): stop AbortSignal listener leak in long sessions (MaxListenersExceededWarning) (#4366) 2026-05-26 14:21:49 +08:00
_meta.ts feat: refactor docs 2025-12-05 10:51:57 +08:00
declarative-agents-port.md feat(core): port declarative-agent mcpServers + hooks (CC 2.1.168 parity follow-up) (#4996) 2026-06-12 14:15:51 +08:00
index.md fix: lint issues 2025-12-19 15:52:11 +08:00
yaml-parser-replacement.md feat(core): port declarative-agent mcpServers + hooks (CC 2.1.168 parity follow-up) (#4996) 2026-06-12 14:15:51 +08:00