Skip to content
Agentic Control Plane

The Qwen Code control model, explained

Qwen Code is Alibaba’s open-source terminal coding agent, a Gemini CLI fork tuned for Qwen models and usable with any OpenAI-compatible endpoint. It is larger than its profile suggests: about 27,600 GitHub stars and roughly 85,000 npm downloads a week as of early September 2026, and the heaviest wrapper we have measured: 99 KB before your prompt, 27 tools whose names mirror Claude Code’s in snake_case. Its control surface mirrors Claude Code too, which is the useful part.

This page covers Qwen Code’s own model. For adding ACP to Qwen Code, see the install guide; for the cross-harness picture, the controls comparison.

Layer 1: approval modes

tools.approvalMode in settings.json, or --approval-mode / --yolo on the command line:

Mode What runs without asking
plan Nothing that modifies files or runs commands
default Nothing. Every edit and shell command prompts
auto-edit File edits. Commands still prompt
auto (default) Whatever an LLM classifier judges safe. Risky actions are blocked
yolo Everything

The default deserves a second look. Out of the box, the thing deciding whether a shell command is safe is a model call, not a rule you wrote. That is the same design as fx’s reviewer and Gemini CLI’s, and it has the same property: it is a judgment, not a policy, and it is not reproducible. Teams that want a deterministic boundary set default or auto-edit and express the rest as rules.

Layer 2: permission rules

The permissions block is Claude Code’s rule grammar:

{
  "permissions": {
    "allow": ["Bash(git status)", "Read(./src/**)"],
    "ask":   ["Bash(git push *)"],
    "deny":  ["Read(./secrets/**)", "Bash(rm -rf *)", "mcp__untrusted"]
  }
}

Priority is deny over ask over allow. Rules are managed with /permissions in the session. The older tools.allowed and tools.exclude keys are deprecated in favor of this block but still honoured at startup, so an audit of an existing install should read both. tools.core restricts the built-in registry to an allowlist and fails closed for built-ins, but MCP, skill and synthetic tools bypass it.

Settings resolve in a fixed order: defaults, then the system defaults file, user ~/.qwen/settings.json, project .qwen/settings.json, then the system settings file at /etc/qwen-code/settings.json, then environment, then flags. System settings override user and project, which is the enterprise pin: an operator can fix the approval mode and deny list for every user on a machine.

Layer 3: sandbox

tools.sandbox accepts a boolean or a path. Docker and Podman are the container backends, with tools.sandboxImage or --sandbox-image selecting the image; on macOS a seatbelt profile can be supplied per project as .qwen/sandbox-macos-custom.sb. Off by default. As with every harness, the sandbox bounds where the agent can act; it does not decide which actions run.

Layer 4: hooks, and why they matter here

Qwen Code’s hook system is Claude Code’s contract, almost field for field:

  Qwen Code Claude Code
Events PreToolUse, PostToolUse, SessionStart, Stop, UserPromptSubmit, PermissionRequest, PermissionDenied, plus subagent, compaction and todo events Same core set
stdin session_id, transcript_path, cwd, hook_event_name, tool_name, tool_input, tool_use_id, permission_mode; tool_response on PostToolUse Same
Decision hookSpecificOutput.permissionDecision: allow / ask / deny, with permissionDecisionReason; exit code 2 blocks with stderr as the reason Same
Matcher Regex on the runtime tool id; * or empty matches all Regex; .*
Timeout milliseconds, default 60,000 seconds
Location hooks in ~/.qwen/settings.json or .qwen/settings.json ~/.claude/settings.json
Off switch disableAllHooks: true none

Two consequences. First, a hook written for Claude Code runs on Qwen Code with a client-name change and a timeout in the right unit, which is what the ACP install does. Second, hooks are the one layer that sees every tool with its arguments before it runs, across all five approval modes, including yolo. The permission rules gate the built-in prompts; the hook gates the call.

Project-level hooks require the folder to be trusted (security.folderTrust). User-level hooks in ~/.qwen/settings.json do not, which is why the installer writes there.

The empty chair

Qwen Code states the unattended case in its own docs: a hook’s ask degrades to deny in headless runs and background subagents. That is the correct posture and it is rare to see it written down. Combined with tools.approvalMode, a qwen --prompt run in CI has a known answer for every call: rules and hooks decide, and anything that would have needed a human is refused rather than waved through.

The gap is the same as everywhere else. Qwen Code records nothing durable about what was allowed, refused or attempted; the decision happens and is gone. A hook that reports each decision to an independent record closes it.

Where Qwen Code sits

Against the comparison table: a full native permission model (five modes plus rules), an optional sandbox, a Claude-contract hook surface with deny, ask and output rewrite, a documented headless posture, and an enterprise pin via system settings. Its escape hatch is --yolo, which the hook still sees. The thing to know before trusting the default is that auto mode’s safety judgment is a model’s.

Run agents? ACP lets you see, control, and price every tool call they make — allow/ask/deny policy, per-session cost, and a full audit log for Claude Code, Cursor, Codex, and OpenClaw, in one command:

curl -sf https://agenticcontrolplane.com/install.sh | bash

Getting started →  ·  see your first governed call →  ·  free up to 5 agents  ·  prefer fully on-device? add --local