qwen-code/docs/users/configuration
Shaojin Wen a9d338684d
fix(core): make the per-turn tool-call cap adaptive (#7052)
* fix(core): make the per-turn tool-call cap adaptive

The per-turn tool-call cap (model.maxToolCallsPerTurn, default 100) was a
blunt circuit breaker: it halted any turn on the 101st tool call regardless
of whether the model was stuck or doing productive work. Large multi-package
implementation turns legitimately exceed 100 calls, so the cap killed
productive turns — a false positive.

Make the cap adaptive. The configured value is now a soft cap: once a turn
exceeds it, the cap halts only when a stuck-repetition signal is present (the
same (tool, args) call repeated 6+ times); a productive turn (diverse calls,
no repetition) continues up to a hard backstop of 3x the soft cap, which
always halts to bound an argument-varying runaway.

Validated against a real session whose 100-call turn was halted mid-build
with no repetition (max key repeat 2): that turn now continues, while genuine
stuck loops still halt at the soft cap. The always-on cap keeps its own
per-(tool,args) repeat tracker so it stays independent of skipLoopDetection.

The ACP/daemon path has a separate blunt cap that is not aligned here; noted
as a follow-up in the design doc.

* chore: regenerate settings schema for adaptive cap

* fix(core): clarify adaptive cap is interactive-only; strengthen cap tests

Address review feedback:
- The setting description now notes the daemon/ACP path still halts at the
  configured value regardless of repetition, so it no longer overclaims the
  adaptive behavior for non-interactive paths.
- Rename the misleading "fires at the built-in default soft cap value" test
  (it only asserts diverse calls are allowed past the cap).
- Add a retry test that builds a stuck-repetition signal before the retry and
  verifies it is cleared, so removing the capMaxKeyRepeat reset would fail.

* test(core): cover productive-then-stuck cap; clarify cap halt hint

Address review round 2:
- Add a test for a turn that crosses the soft cap with diverse calls and then
  becomes stuck mid-range, so the stuck check is verified across the whole
  (softCap, hardCap] range, not just at the boundary.
- Make the headless cap-halt message accurate for both triggers: it no longer
  only suggests raising maxToolCallsPerTurn (correct for the hard backstop but
  misleading for a stuck repeat) and now also points at the repetition.

* refactor(core): hash tool-call key once in always-on cap path; tighten cap tests

Address review round 3:
- Compute the (tool,args) key once in checkAlwaysOnSafeties and share it
  between the consecutive-identical guard and the cap stuck tracker (was
  hashed twice per call; args can be large). Skip hashing entirely when loop
  detection is disabled for the session (no consumer).
- Add a test that the stuck signal accumulates across Finished round-trip
  boundaries within a turn.
- Tighten the Stop-hook continuation budget test (cap 4 -> 1) so it still
  guards the reset under the adaptive cap, where diverse calls no longer trip
  the soft cap.
- Document the monotone stuck-signal non-goal and the telemetry-differentiation
  follow-up in the design doc.

* fix(core): canonicalize tool-call key fields; clarify adaptive cap scope

Address Codex review (2 Critical):
- getToolCallKey now canonicalizes object keys recursively (preserving array
  order) before hashing, so a stuck model cannot evade the repeat guards by
  reordering argument fields. Adds a reordered-arguments regression test.
- Correct the maxToolCallsPerTurn description: the adaptive behavior applies to
  both the interactive TUI and non-interactive (-p / JSON / stream-JSON)
  core-client runs; only the daemon/ACP path is strict. Updated in
  settingsSchema.ts, settings.md, and the regenerated settings.schema.json.

* test(core): cover nested key reordering and the consecutive guard's canonicalization

Address review:
- Extend the reordered-args stuck-signal test to include nested objects, so a
  regression that breaks canonicalizeForHash's recursion is caught.
- Add a reordered-args test for the consecutive-identical guard, pinning the
  canonicalization contract for that always-on detector (not just the cap).

* fix(core): treat explicit maxToolCallsPerTurn as a hard cap; keep default adaptive

Address yiliang114's Critical: v0.19.10 shipped maxToolCallsPerTurn as a hard
cap, but the adaptive change multiplied every configured value by 3, turning an
explicit N into a 3N budget — a breaking change for users who set it to bound
unattended cost.

Behavior now depends on whether the value was explicitly configured
(Config.isMaxToolCallsPerTurnExplicit):
- Explicit N -> hard cap (halt at N+1), preserving the released contract.
- Default (unset) -> adaptive: soft cap 100, halt only on a stuck-repetition
  signal, with the hard backstop raised to 1000 (10x) so modern models making
  hundreds of legitimate calls per task are not false-positived.

Adds the explicit-hard-cap regression (cap of 2 halts call 3) and a contrast
test proving the explicit flag drives the behavior. Updates the setting
description, headless hint/label, and design doc accordingly.
2026-07-17 16:42:41 +00:00
..
_meta.ts docs: fix config/command/auth drift and surface the model-providers page (#5735) 2026-06-24 06:06:01 +08:00
auth.md docs: fix model-provider config shape and refresh feature/setting drift (#6552) 2026-07-08 23:04:47 +00:00
model-providers.md Fix long session timeline scrolling (#6526) 2026-07-09 11:43:21 +00:00
qwen-ignore.md feat(core): respect configurable agent ignore files (#4653) 2026-06-23 10:44:37 +08:00
settings.md fix(core): make the per-turn tool-call cap adaptive (#7052) 2026-07-17 16:42:41 +00:00
themes.md fix(core): Align MCP OAuth guidance and docs (#5589) 2026-06-24 07:09:53 +08:00
trusted-folders.md docs: updated all links, click and open in vscode, new showcase video in overview 2025-12-17 11:10:31 +08:00