Find a file
顾盼 7757d87850
Some checks are pending
Qwen Code CI / Classify PR (push) Waiting to run
Qwen Code CI / Lint (push) Blocked by required conditions
Qwen Code CI / Test (macos-latest, Node 22.x) (push) Blocked by required conditions
Qwen Code CI / Test (ubuntu-latest, Node 22.x) (push) Blocked by required conditions
Qwen Code CI / Test (windows-latest, Node 22.x) (push) Blocked by required conditions
Qwen Code CI / Post Coverage Comment (push) Blocked by required conditions
Qwen Code CI / CodeQL (push) Blocked by required conditions
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): Workflow tool P1 — minimal node:vm sandbox + sequential agent() (#4721) (#4732)
* feat(core): register Workflow tool name (P1)

* feat(core): isWorkflowsEnabled config gate with env-var override (P1)

* feat(core): stripExportMeta helper for workflow sandbox (P1)

* feat(core): createWorkflowSandbox with determinism stubs (P1)

* test(core): cover workflow sandbox phase/log/agent primitives (P1)

* feat(core): WorkflowOrchestrator with injectable dispatch (P1)

* feat(core): WorkflowOrchestrator production dispatch via AgentHeadless (P1)

* feat(core): WorkflowTool wraps WorkflowOrchestrator (P1)

* feat(core): register WorkflowTool behind isWorkflowsEnabled gate (P1)

* feat(core): export WorkflowTool from package index (P1)

* fix(core): harden workflow sandbox + tighten agent() opts surface (P1)

SEC-C1: deep-null-proto + hardenClosure blocks args/closure realm-escape PoC.
SEC-C2: vm 30s timeout kills sync infinite loops.
UP-C1: agent() throws on unsupported opts (schema/isolation/model/agentType).
UP-I1: keep verbatim subagent system prompt comment.
ARCH-C1: thread AbortSignal into buildProductionDispatch → subagent.execute().
UP-C2: llmContent carries script result verbatim; metadata moves to returnDisplay.
SEC-I1: add WORKFLOW to EXCLUDED_TOOLS_FOR_SUBAGENTS to prevent recursive fan-out.
SEC-I2: cap logs[] at 10 000 lines with a truncation marker.
REUSE-I1: use ToolErrorType.EXECUTION_FAILED in workflow error returns.
TST: add security PoC tests, unique-runId, dispatch-rejection, llmContent-unwrap.
TST-I1: remove setter/getter tautology test from config.workflows.test.ts.

* refactor(core): decouple WorkflowOrchestrator from Config (P1)

- Extract WORKFLOW_SUBAGENT_SYSTEM_PROMPT into workflow-prompts.ts
- Lift buildProductionDispatch() into exported createProductionDispatch(config, signal?)
- WorkflowOrchestrator constructor now takes dispatch directly: (dispatch: WorkflowAgentDispatch)
- Remove WorkflowOrchestratorOptions interface
- WorkflowToolOptions.orchestratorOverrides replaced by WorkflowToolOptions.dispatch
- WorkflowToolInvocation.execute() calls createProductionDispatch() when no override is set
- Tests updated: orchestrator tests inject dispatch directly; production-dispatch tests moved to createProductionDispatch describe block

* fix(core): Math proxy hardening, subagent prompt verbatim, Date.now throw, phases cap, test fidelity (P1)

* fix(core): construct Math+Date in vm realm, sever proto chains on injected closures (P1)

* fix(core): sever Array.prototype on args, cap deep-null-proto recursion, consolidate WorkflowAgentResult (P1)

* fix(core): stub parallel/pipeline/workflow/budget globals with P1-unsupported errors

* fix(core): harden budget inner functions, regression-test anti-recursion + args threading

* fix(core): P2/P5 forward-compat injection seams + document error.stack limitation (P1)

* fix(core): vm-realm wrap async sandbox globals + stripExportMeta hardening (PR #4732 R1)

Closes T1/T8/T14: thrown Errors and async-function Promises used to leak the
host Function constructor through their prototype chains. PoC:
  agent('x').constructor.constructor('return process')()
  try { throw } catch(e) { e.constructor.constructor('return process')() }
Build every async/sync global (agent, parallel, pipeline, workflow, budget,
console, phase, log, args) inside the vm-realm via the existing init
script. Host only exposes a primitive bridge that the init script reads
once and deletes from globalThis. Both rejection and resolution paths
cross the boundary as vm-realm values.

Closes T2: deepNullProto used to setPrototypeOf(null) on array args,
breaking for-of / .map / .filter / spread / destructuring. Replaced with
vm-realm JSON.parse of an args string — arrays retain vm-realm
Array.prototype methods.

Closes T13: runtime allowlist on agent() opts catches typos like 'scema'.

Closes T9/T16/T17: stripExportMeta now recognises //, /* */, and regex
literals; throws on unbalanced braces instead of silently returning ''.

Closes T6: validateArgs rejects functions, BigInt, circular refs, and
over-deep nesting (previously functions silently disappeared).

Closes T5: regression test for console.log/warn/error → getLogs routing.

* fix(core): change WorkflowTool export to type-only (PR #4732 R1 T3)

Production callers use Config.createToolRegistry's registerLazy path which
dynamic-imports './tools/workflow/workflow.js'. The barrel export at index.ts
previously forced eager evaluation of the workflow.js → workflow-orchestrator
→ workflow-sandbox → node:vm module chain for every consumer of
@qwen-code/core, even when workflows are disabled.

Sibling tool exports (AgentTool, SkillTool) are type-only; align WorkflowTool
with the same pattern. SDK consumers can still annotate types; instantiation
happens through the registry, not the barrel.

* fix(core): subagent terminateMode + bounded runConfig + disallowedTools + failure context + defensive serialization (PR #4732 R1)

Closes T10: runReasoningLoop returns terminateMode = CANCELLED|MAX_TURNS|
TIMEOUT|ERROR rather than throwing. Without checking it, await agent(...)
resolved to '' on user cancel and the workflow kept looping. Now check
getTerminateMode() after execute() and throw on non-GOAL — mirrors
AgentTool's existing handling.

Closes T11: workflow subagents previously ran with runConfig: {} (no
max_turns / max_time_minutes guard) and tools: ['*'] without
disallowedTools. A single agent() could loop the model indefinitely. Bound
to 50 turns / 10 minutes; add disallowedTools: [SEND_MESSAGE, EXIT_PLAN_MODE]
to mirror upstream Tg8 — defense in depth with the §XmO system prompt.

Closes T19: phases / logs accumulated before a script failure used to be
discarded with the sandbox instance. WorkflowExecutionError carries them
through the rejection so the user-visible display can show what ran.

Closes T12 / T18: defensive serialization. A successful workflow returning
a BigInt or circular value used to be reported as 'Workflow failed:
Converting circular structure to JSON' because JSON.stringify was inside
the try block. safeStringifyResult / safeStringifyDisplayPayload degrade
to a clear placeholder so a serialization issue doesn't masquerade as a
run failure.

Closes T4: regression test for ToolErrorType.EXECUTION_FAILED assertion.

Closes T7: vi as vitest alias removed (now matches every other test file).

* chore(core): add missing @license headers + remove stale config-session-env reference (PR #4732 R1)

Closes T20: 6 of 9 new workflow files were missing the standard @license
Apache-2.0 header. Add Qwen-style header (matches sibling tools/agent/agent.ts
and others) to: workflow-sandbox.ts, workflow-sandbox.test.ts,
workflow-orchestrator.ts, workflow-orchestrator.test.ts,
workflow-prompts.ts, workflow.test.ts.

Closes T21: config.workflows.test.ts and config.workflow-registration.test.ts
both contained 'mirrors config-session-env.test.ts' in a setup comment, but
that file does not exist in the repo. Drop the dangling reference.

* chore(core): clean up stray rebase conflict marker (PR #4732)

* fix(core): sever sandboxGlobals proto + add async wall-clock timeout (PR #4732 R2)

Closes T22: sandboxGlobals was a plain host-realm Object literal. Its
prototype chain reached host Object → host Function → host process,
bypassing every per-global hardening measure. PoC confirmed leak via
`globalThis.constructor.constructor('return process')()` returning
host process before fix. Fix: Object.setPrototypeOf(null) on both
sandboxGlobals and the bridge object before vm.createContext.
Regression tests cover both globalThis.constructor and implicit-this
escape paths.

Closes T23: vm.runInContext timeout only covers synchronous execution.
Once the async IIFE yields its first await, the watchdog disarms and
`return new Promise(() => {})` hangs forever. Fix: wrap in
Promise.race with a wall-clock timeout (default 30 minutes, configurable
via SandboxOptions.maxWallClockMs or QWEN_CODE_MAX_WORKFLOW_SECONDS env
var). This is a permanent defense-in-depth — not P1-only: P2/P3/P5
all add resource caps measured in agent-calls or tokens, but a
0-token / 0-agent hang requires a wall-clock cap.

Documented limitation: an in-script async microtask loop continues
consuming microtasks after the outer wall-clock rejects (node:vm
provides no way to halt async execution). In production the workflow
surface returns the timeout error and the vm context becomes
unreferenced; the leaked microtask loop is a host-process concern
that requires worker_threads-level isolation (out of P1 scope).

* fix(core): pre-sanitize non-serializable result before display payload (PR #4732 R3)

Sibling drift of the R1 T12/T18 fix. safeStringifyResult already degrades
per-field when the script's `result` is a BigInt / circular value, so
llmContent survives. But the success-path display payload wraps
{runId, phases, logs, result} in a single JSON.stringify — one bad
`result` collapsed the whole display to the generic
"(display payload not JSON-serializable)" string and the user lost the
runId (needed for log correlation), the accumulated phases, AND the
logs. Pre-sanitize `result` only; runId / phases / logs are always
serializable.

Add regression test that scripts a circular `result` with a `phase()`
in front: assertions check runId, the phase, and the non-JSON-serializable
placeholder all appear in returnDisplay, and that the atomic-failure
fallback string does NOT appear.

RED at 10:56:23 → fix → GREEN at 10:56:48. 14/14 workflow.test, 109/109
across the workflow test suite, typecheck silent.

* refactor(core): push display-payload per-field fallback into the helper (PR #4732)

Post-R3 /simplify pass. The R3 fix special-cased `result` at the call
site by pre-probing JSON.stringify and substituting a placeholder. Four
review angles (reuse / simplification / efficiency / altitude) all
converged on the same root cause: per-field degradation belongs in
`safeStringifyDisplayPayload`, not duplicated at every caller.

- Altitude: the bug ("all-or-nothing stringify is too coarse") names a
  property of the helper; the fix now lives in the helper. Any new
  payload field that becomes non-serializable in a future round
  (`metrics: bigint`, etc.) is handled without a fresh call-site patch.
- Reuse: the third try/JSON.stringify probe in this file is gone;
  the helper owns the probe.
- Simplification: call site reverts to the pre-R3 clean shape.
- Efficiency: success path is back to one stringify per payload.

Helper behavior:
  happy path  → 1 stringify (unchanged)
  one field fails → walk top-level keys, probe each, replace failing
                    value with `(non-JSON-serializable value of type X)`,
                    re-stringify; total 2 stringifies of payload + N
                    field probes. Fall through to the original generic
                    fallback if the sanitized re-stringify also fails.

R3 regression test (`execute() preserves runId/phases/logs in
returnDisplay when result is non-JSON-serializable`) passes unchanged
— it tests observable behavior, not the implementation site. 109/109
across the workflow suite, typecheck silent.

* fix(core): honest description + meta-strip anchor + wall-clock cancellation + stray gitignore (PR #4732 R4)

Four fixes from R4 review:

T32 (workflow.ts tool description) — P1 description claimed "sequential
only" but `Promise.all([agent(), agent()])` bypasses the claim because
the vm cannot intercept JS built-ins. Rewrite the description to be
honest: P1 ships sequential primitives only (no parallel/pipeline);
Promise.all spawns concurrent subagents that share Config and may race
on file edits. Matches upstream Claude Code behavior — they also expose
Promise.all without enforcement.

T33 (workflow-sandbox.ts stripExportMeta) — drop the `/m` flag on the
anchor regex. With `/m`, a template literal containing
`\nexport const meta = {\n` triggered a false match, and the brace
walker ripped content out of the string body, silently corrupting the
script. Per design intent ("required first statement of every script")
meta must be file-start; anchoring there closes the corruption surface.
Adds two RED-confirmed regression tests (template literal + leading
code) and a sanity test for leading whitespace.

T35 (packages/core/.gitignore) — removed. The `.qwen/computer-use/`
entry was stray scope pollution committed accidentally in R1's license-
header cleanup (`d118c55f8`) and unrelated to the Workflow P1 surface.

T40 (sandbox.ts + orchestrator.ts + workflow.ts) — completes the R2
wall-clock defense. When the timer fires the sandbox now `abort()`s a
caller-supplied AbortController BEFORE rejecting; the controller's
signal is also threaded into `createProductionDispatch`, so in-flight
subagent.execute() calls see the cancellation and stop burning tokens.
Without this, R2's "30 min wall-clock" still let subagents run for up
to their internal `max_time_minutes: 10` after the user-side timed out.

WorkflowTool.execute now derives `dispatchController`, forwards caller
signal abort to it, passes its signal to dispatch and the controller
itself to `orchestrator.run({abortOnTimeout})`. A `finally` block
aborts the controller on natural completion (cancel any straggler
subagent) and detaches the caller-signal listener to avoid leaks.

Adds two sandbox unit tests (RED-confirmed): controller IS aborted on
timeout, controller is NOT aborted on normal completion.

114/114 workflow suite tests pass, typecheck silent.

* refactor(core): use createChildAbortController for T40 dispatch-signal bridge (PR #4732)

Post-R4 /simplify pass. Four review angles converged on one finding:
the T40 manual AbortController-bridging at the call site re-implements
`createChildAbortController` from `packages/core/src/utils/abortController.ts:61`,
which is already the project-idiomatic helper for this exact pattern
(used at agent-headless.ts:231, agent-interactive.ts:157, agent-core.ts:603 & 952).

The replacement collapses 5 lines of imperative listener wiring at
`workflow.ts:execute()` head + 1 line of finally cleanup into a single
`createChildAbortController(signal)` call, plus inherits the helper's
hardenings:

- WeakRef on the parent (so a long-lived caller signal doesn't pin the
  child controller)
- Auto-removal of the parent listener when the child fires (covers
  both the wall-clock-fire path and the natural-completion path)
- Default 50-listener cap via `setMaxListeners`

No behavior change at the API boundary — the wall-clock `abortOnTimeout`
contract and the test assertions for T40's two cases (controller IS
aborted on wall-clock; controller is NOT aborted on normal completion)
all still hold. The /simplify "altitude" finding (push the bridging
into the orchestrator) is deferred — that would change WorkflowOrchestrator's
constructor/run signature and is outside this PR's scope.

Also trims a redundant inline comment at workflow-orchestrator.ts:172
(the `abortOnTimeout: req.abortOnTimeout` line; the field's type
comment at line 71-81 already explains the contract).

114/114 workflow suite tests pass, typecheck silent.

* chore(core): compress over-weight T40 comments after createChildAbortController refactor (PR #4732)

The previous commit moved the bridging logic into the helper, so the
inline comments restating the helper's contract (parent forwarding,
already-aborted fast path, WeakRef, auto-removal) became redundant —
that's the helper's job to document.

Compress to the load-bearing semantics: the child controller sees
both caller-driven and wall-clock-driven aborts, and `finally` cancels
stragglers on normal completion. No code change.

* chore(core): align copyright header to Qwen on 2 PR-new test files (PR #4732 R7 F4)

Both files were derived from a template (the stale `config-session-env.test.ts`
reference cleaned up in R1 T21) and retained the upstream `Copyright 2025
Google LLC` header. The other six new workflow source/test files in this
PR carry `Copyright 2025 Qwen`. Align for same-PR consistency.

Per DragonnZhang R7 F4. No behavior change; header text only.

---------

Co-authored-by: tanzhenxin <tanzhenxing1987@gmail.com>
2026-06-10 11:32:46 +08:00
.github fix(ci): acknowledge queued qwen review requests (#4847) 2026-06-09 21:38:13 +08:00
.husky Sync upstream Gemini-CLI v0.8.2 (#838) 2025-10-23 09:27:04 +08:00
.qwen feat(acp): support desktop qwen integration (#4728) 2026-06-09 19:09:44 +08:00
.vscode Merge branch 'main' into feat/sandbox-config-improvements 2026-03-06 14:38:39 +08:00
docs refactor(core): remove GitService, migrate /restore to FileHistoryService (#4871) 2026-06-09 18:34:31 +08:00
docs-site Hide internal docs from docs site (#4357) 2026-06-01 15:55:14 +08:00
eslint-rules pre-release commit 2025-07-22 23:26:01 +08:00
integration-tests test(integration): drop tight 30s timeout in sleep-interception e2e (#4878) 2026-06-09 11:22:56 +08:00
packages feat(core): Workflow tool P1 — minimal node:vm sandbox + sequential agent() (#4721) (#4732) 2026-06-10 11:32:46 +08:00
patches fix(cli): statusline not re-rendering when switching from preset to command type (#4706) 2026-06-03 14:17:16 +08:00
scripts fix(installer): correct 'for more info' URL to GitHub repo instead of docs site (#4916) 2026-06-10 10:46:58 +08:00
.dockerignore fix(cli): skip stdin read for ACP mode 2026-03-27 11:47:01 +00:00
.editorconfig pre-release commit 2025-07-22 23:26:01 +08:00
.gitattributes feat(installer): add standalone hosted install and uninstall flow (#3828) 2026-05-21 11:57:10 +08:00
.gitignore feat(skills): enforce auto-skill- directory prefix for auto-generated skills (#4839) 2026-06-08 17:07:00 +08:00
.npmrc chore: remove google registry 2025-08-08 20:45:54 +08:00
.nvmrc chore(deps): upgrade ink 6.2.3 → 7.0.2 + bump Node engine to 22 (#3860) 2026-05-11 17:29:50 +08:00
.prettierignore Merge branch 'main' into feat/add-vscode-settings-json-schema 2026-03-03 11:21:57 +08:00
.prettierrc.json pre-release commit 2025-07-22 23:26:01 +08:00
.yamllint.yml Sync upstream Gemini-CLI v0.8.2 (#838) 2025-10-23 09:27:04 +08:00
AGENTS.md docs(agents,pr-template): add Working Principles and restructure PR template (#4496) 2026-05-25 19:15:35 +08:00
CONTRIBUTING.md feat(installer): verify release assets + switch public docs to standalone entrypoint (#3855) 2026-06-04 17:23:04 +08:00
Dockerfile chore(deps): upgrade ink 6.2.3 → 7.0.2 + bump Node engine to 22 (#3860) 2026-05-11 17:29:50 +08:00
esbuild.config.js fix(build): tree-shake React reconciler dev build to prevent PerformanceMeasure leak (#4462) 2026-05-23 21:00:32 +08:00
eslint.config.js fix(core): stop AbortSignal listener leak in long sessions (MaxListenersExceededWarning) (#4366) 2026-05-26 14:21:49 +08:00
LICENSE Sync upstream Gemini-CLI v0.8.2 (#838) 2025-10-23 09:27:04 +08:00
Makefile feat: update docs 2025-12-22 21:11:33 +08:00
package-lock.json feat(acp): support desktop qwen integration (#4728) 2026-06-09 19:09:44 +08:00
package.json feat(acp): support desktop qwen integration (#4728) 2026-06-09 19:09:44 +08:00
README.md feat(installer): verify release assets + switch public docs to standalone entrypoint (#3855) 2026-06-04 17:23:04 +08:00
SECURITY.md fix: update security vulnerability reporting channel 2026-02-24 14:22:47 +08:00
tsconfig.json # 🚀 Sync Gemini CLI v0.2.1 - Major Feature Update (#483) 2025-09-01 14:48:55 +08:00
vitest.config.ts test(channels): add comprehensive test suites for channel adapters 2026-03-27 15:26:39 +00:00

npm version License Node.js Version Downloads

QwenLM%2Fqwen-code | Trendshift

An open-source AI agent that lives in your terminal.

中文 | Deutsch | français | 日本語 | Русский | Português (Brasil)

🎉 News

  • 2026-04-15: Qwen OAuth free tier has been discontinued. To continue using Qwen Code, switch to Alibaba Cloud Coding Plan, OpenRouter, Fireworks AI, or bring your own API key. Run qwen auth to configure.

  • 2026-04-13: Qwen OAuth free tier policy update: daily quota adjusted to 100 requests/day (from 1,000).

  • 2026-04-02: Qwen3.6-Plus is now live! Get an API key from Alibaba Cloud ModelStudio to access it through the OpenAI-compatible API.

  • 2026-02-16: Qwen3.5-Plus is now live!

Why Qwen Code?

Qwen Code is an open-source AI agent for the terminal, optimized for Qwen series models. It helps you understand large codebases, automate tedious work, and ship faster.

  • Multi-protocol, flexible providers: use OpenAI / Anthropic / Gemini-compatible APIs, Alibaba Cloud Coding Plan, OpenRouter, Fireworks AI, or bring your own API key.
  • Open-source, co-evolving: both the framework and the Qwen3-Coder model are open-source—and they ship and evolve together.
  • Agentic workflow, feature-rich: rich built-in tools (Skills, SubAgents) for a full agentic workflow and a Claude Code-like experience.
  • Terminal-first, IDE-friendly: built for developers who live in the command line, with optional integration for VS Code, Zed, and JetBrains IDEs.

Installation

Linux / macOS

curl -fsSL https://qwen-code-assets.oss-cn-hangzhou.aliyuncs.com/installation/install-qwen-standalone.sh | bash

Windows

irm https://qwen-code-assets.oss-cn-hangzhou.aliyuncs.com/installation/install-qwen-standalone.ps1 | iex

Note

: It's recommended to restart your terminal after installation to ensure environment variables take effect.

Manual Installation

Prerequisites

Make sure you have Node.js 22 or later installed. Download it from nodejs.org.

NPM

npm install -g @qwen-code/qwen-code@latest

Homebrew (macOS, Linux)

brew install qwen-code

Quick Start

# Start Qwen Code (interactive)
qwen

# Then, in the session:
/help
/auth

On first use, you'll be prompted to sign in. You can run /auth anytime to switch authentication methods.

Example prompts:

What does this project do?
Explain the codebase structure.
Help me refactor this function.
Generate unit tests for this module.
Click to watch a demo video

🦞 Use Qwen Code for Coding Tasks in Claw

Copy the prompt below and paste it into your agent:

Use Qwen Code as a coding agent. First, fetch and read this skill reference:
https://raw.githubusercontent.com/QwenLM/qwen-code/refs/heads/main/.qwen/skills/qwen-code-claw/SKILL.md
Then follow the instructions inside to install, authenticate, and use Qwen Code via acpx for all coding tasks.

Authentication

Qwen Code supports the following authentication methods:

  • API Key (recommended): use an API key from Alibaba Cloud Model Studio (Beijing / intl) or any supported provider (OpenAI, Anthropic, Google GenAI, and other compatible endpoints).
  • Coding Plan: subscribe to the Alibaba Cloud Coding Plan (Beijing / intl) for a fixed monthly fee with higher quotas.

⚠️ Qwen OAuth was discontinued on April 15, 2026. If you were previously using Qwen OAuth, please switch to one of the methods above. Run qwen and then /auth to reconfigure.

Use an API key to connect to Alibaba Cloud Model Studio or any supported provider. Supports multiple protocols:

  • OpenAI-compatible: Alibaba Cloud ModelStudio, ModelScope, OpenAI, OpenRouter, and other OpenAI-compatible providers
  • Anthropic: Claude models
  • Google GenAI: Gemini models

The recommended way to configure models and providers is by editing ~/.qwen/settings.json (create it if it doesn't exist). This file lets you define all available models, API keys, and default settings in one place.

Quick Setup in 3 Steps

Step 1: Create or edit ~/.qwen/settings.json

Here is a complete example:

{
  "modelProviders": {
    "openai": [
      {
        "id": "qwen3.6-plus",
        "name": "qwen3.6-plus",
        "baseUrl": "https://dashscope.aliyuncs.com/compatible-mode/v1",
        "description": "Qwen3-Coder via Dashscope",
        "envKey": "DASHSCOPE_API_KEY"
      }
    ]
  },
  "env": {
    "DASHSCOPE_API_KEY": "sk-xxxxxxxxxxxxx"
  },
  "security": {
    "auth": {
      "selectedType": "openai"
    }
  },
  "model": {
    "name": "qwen3.6-plus"
  }
}

Step 2: Understand each field

Field What it does
modelProviders Declares which models are available and how to connect to them. Keys like openai, anthropic, gemini represent the API protocol.
modelProviders[].id The model ID sent to the API (e.g. qwen3.6-plus, gpt-4o).
modelProviders[].envKey The name of the environment variable that holds your API key.
modelProviders[].baseUrl The API endpoint URL (required for non-default endpoints).
env A fallback place to store API keys (lowest priority; prefer .env files or export for sensitive keys).
security.auth.selectedType The protocol to use on startup (openai, anthropic, gemini, vertex-ai).
model.name The default model to use when Qwen Code starts.

Step 3: Start Qwen Code — your configuration takes effect automatically:

qwen

Use the /model command at any time to switch between all configured models.

More Examples
Coding Plan (Alibaba Cloud ModelStudio) — fixed monthly fee, higher quotas
{
  "modelProviders": {
    "openai": [
      {
        "id": "qwen3.6-plus",
        "name": "qwen3.6-plus (Coding Plan)",
        "baseUrl": "https://coding.dashscope.aliyuncs.com/v1",
        "description": "qwen3.6-plus from ModelStudio Coding Plan",
        "envKey": "BAILIAN_CODING_PLAN_API_KEY"
      },
      {
        "id": "qwen3.5-plus",
        "name": "qwen3.5-plus (Coding Plan)",
        "baseUrl": "https://coding.dashscope.aliyuncs.com/v1",
        "description": "qwen3.5-plus with thinking enabled from ModelStudio Coding Plan",
        "envKey": "BAILIAN_CODING_PLAN_API_KEY",
        "generationConfig": {
          "extra_body": {
            "enable_thinking": true
          }
        }
      },
      {
        "id": "glm-4.7",
        "name": "glm-4.7 (Coding Plan)",
        "baseUrl": "https://coding.dashscope.aliyuncs.com/v1",
        "description": "glm-4.7 with thinking enabled from ModelStudio Coding Plan",
        "envKey": "BAILIAN_CODING_PLAN_API_KEY",
        "generationConfig": {
          "extra_body": {
            "enable_thinking": true
          }
        }
      },
      {
        "id": "kimi-k2.5",
        "name": "kimi-k2.5 (Coding Plan)",
        "baseUrl": "https://coding.dashscope.aliyuncs.com/v1",
        "description": "kimi-k2.5 with thinking enabled from ModelStudio Coding Plan",
        "envKey": "BAILIAN_CODING_PLAN_API_KEY",
        "generationConfig": {
          "extra_body": {
            "enable_thinking": true
          }
        }
      }
    ]
  },
  "env": {
    "BAILIAN_CODING_PLAN_API_KEY": "sk-xxxxxxxxxxxxx"
  },
  "security": {
    "auth": {
      "selectedType": "openai"
    }
  },
  "model": {
    "name": "qwen3.6-plus"
  }
}

Subscribe to the Coding Plan and get your API key at Alibaba Cloud ModelStudio(Beijing) or Alibaba Cloud ModelStudio(intl).

Multiple providers (OpenAI + Anthropic + Gemini)
{
  "modelProviders": {
    "openai": [
      {
        "id": "gpt-4o",
        "name": "GPT-4o",
        "envKey": "OPENAI_API_KEY",
        "baseUrl": "https://api.openai.com/v1"
      }
    ],
    "anthropic": [
      {
        "id": "claude-sonnet-4-20250514",
        "name": "Claude Sonnet 4",
        "envKey": "ANTHROPIC_API_KEY"
      }
    ],
    "gemini": [
      {
        "id": "gemini-2.5-pro",
        "name": "Gemini 2.5 Pro",
        "envKey": "GEMINI_API_KEY"
      }
    ]
  },
  "env": {
    "OPENAI_API_KEY": "sk-xxxxxxxxxxxxx",
    "ANTHROPIC_API_KEY": "sk-ant-xxxxxxxxxxxxx",
    "GEMINI_API_KEY": "AIzaxxxxxxxxxxxxx"
  },
  "security": {
    "auth": {
      "selectedType": "openai"
    }
  },
  "model": {
    "name": "gpt-4o"
  }
}
Enable thinking mode (for supported models like qwen3.5-plus)
{
  "modelProviders": {
    "openai": [
      {
        "id": "qwen3.5-plus",
        "name": "qwen3.5-plus (thinking)",
        "envKey": "DASHSCOPE_API_KEY",
        "baseUrl": "https://dashscope.aliyuncs.com/compatible-mode/v1",
        "generationConfig": {
          "extra_body": {
            "enable_thinking": true
          }
        }
      }
    ]
  },
  "env": {
    "DASHSCOPE_API_KEY": "sk-xxxxxxxxxxxxx"
  },
  "security": {
    "auth": {
      "selectedType": "openai"
    }
  },
  "model": {
    "name": "qwen3.5-plus"
  }
}

Tip: You can also set API keys via export in your shell or .env files, which take higher priority than settings.jsonenv. See the authentication guide for full details.

Security note: Never commit API keys to version control. The ~/.qwen/settings.json file is in your home directory and should stay private.

Local Model Setup (Ollama / vLLM)

You can also run models locally — no API key or cloud account needed. This is not an authentication method; instead, configure your local model endpoint in ~/.qwen/settings.json using the modelProviders field.

Set generationConfig.contextWindowSize inside the matching provider entry and adjust it to the context length configured on your local server.

Ollama setup
  1. Install Ollama from ollama.com
  2. Pull a model: ollama pull qwen3:32b
  3. Configure ~/.qwen/settings.json:
{
  "modelProviders": {
    "openai": [
      {
        "id": "qwen3:32b",
        "name": "Qwen3 32B (Ollama)",
        "baseUrl": "http://localhost:11434/v1",
        "description": "Qwen3 32B running locally via Ollama",
        "generationConfig": {
          "contextWindowSize": 131072
        }
      }
    ]
  },
  "security": {
    "auth": {
      "selectedType": "openai"
    }
  },
  "model": {
    "name": "qwen3:32b"
  }
}
vLLM setup
  1. Install vLLM: pip install vllm
  2. Start the server: vllm serve Qwen/Qwen3-32B
  3. Configure ~/.qwen/settings.json:
{
  "modelProviders": {
    "openai": [
      {
        "id": "Qwen/Qwen3-32B",
        "name": "Qwen3 32B (vLLM)",
        "baseUrl": "http://localhost:8000/v1",
        "description": "Qwen3 32B running locally via vLLM",
        "generationConfig": {
          "contextWindowSize": 131072
        }
      }
    ]
  },
  "security": {
    "auth": {
      "selectedType": "openai"
    }
  },
  "model": {
    "name": "Qwen/Qwen3-32B"
  }
}

Usage

As an open-source terminal agent, you can use Qwen Code in five primary ways:

  1. Interactive mode (terminal UI)
  2. Headless mode (scripts, CI)
  3. IDE integration (VS Code, Zed)
  4. SDKs (TypeScript, Python, Java)
  5. Daemon mode — qwen serve exposes ACP over HTTP+SSE so multiple clients share one agent (experimental)

Interactive mode

cd your-project/
qwen

Run qwen in your project folder to launch the interactive terminal UI. Use @ to reference local files (for example @src/main.ts).

Headless mode

cd your-project/
qwen -p "your question"

Use -p to run Qwen Code without the interactive UI—ideal for scripts, automation, and CI/CD. Learn more: Headless mode.

IDE integration

Use Qwen Code inside your editor (VS Code, Zed, and JetBrains IDEs):

Daemon mode (qwen serve, experimental)

cd your-project/
qwen serve
# → qwen serve listening on http://127.0.0.1:4170 (mode=http-bridge)

Run Qwen Code as a local HTTP daemon so IDE plugins, web UIs, CI scripts and custom CLIs all share one agent session over HTTP+SSE — instead of each spawning their own subprocess. Loopback bind has no auth by default (set QWEN_SERVER_TOKEN to enable bearer auth even on loopback); remote binds (--hostname 0.0.0.0) require a token — boot refuses without one. See:

SDKs

Build on top of Qwen Code with the available SDKs:

Python SDK example:

import asyncio

from qwen_code_sdk import is_sdk_result_message, query


async def main() -> None:
    result = query(
        "Summarize the repository layout.",
        {
            "cwd": "/path/to/project",
            "path_to_qwen_executable": "qwen",
        },
    )

    async for message in result:
        if is_sdk_result_message(message):
            print(message["result"])


asyncio.run(main())

Commands & Shortcuts

Session Commands

  • /help - Display available commands
  • /clear - Clear conversation history
  • /compress - Compress history to save tokens
  • /stats - Show current session information
  • /bug - Submit a bug report
  • /exit or /quit - Exit Qwen Code

Keyboard Shortcuts

  • Ctrl+C - Cancel current operation
  • Ctrl+D - Exit (on empty line)
  • Up/Down - Navigate command history

Learn more about Commands

Tip: In YOLO mode (--yolo), vision switching happens automatically without prompts when images are detected. Learn more about Approval Mode

Configuration

Qwen Code can be configured via settings.json, environment variables, and CLI flags.

File Scope Description
~/.qwen/settings.json User (global) Applies to all your Qwen Code sessions. Recommended for modelProviders and env.
.qwen/settings.json Project Applies only when running Qwen Code in this project. Overrides user settings.

The most commonly used top-level fields in settings.json:

Field Description
modelProviders Define available models per protocol (openai, anthropic, gemini, vertex-ai).
env Fallback environment variables (e.g. API keys). Lower priority than shell export and .env files.
security.auth.selectedType The protocol to use on startup (e.g. openai).
model.name The default model to use when Qwen Code starts.

See the Authentication section above for complete settings.json examples, and the settings reference for all available options.

Benchmark Results

Terminal-Bench Performance

Agent Model Accuracy
Qwen Code Qwen3-Coder-480A35 37.5%
Qwen Code Qwen3-Coder-30BA3B 31.3%

Ecosystem

Looking for a graphical interface?

  • AionUi A modern GUI for command-line AI tools including Qwen Code
  • Gemini CLI Desktop A cross-platform desktop/web/mobile UI for Qwen Code

Troubleshooting

If you encounter issues, check the troubleshooting guide.

Common issues:

  • Qwen OAuth free tier was discontinued on 2026-04-15: Qwen OAuth is no longer available. Run qwen/auth and switch to API Key or Coding Plan. See the Authentication section above for setup instructions.

To report a bug from within the CLI, run /bug and include a short title and repro steps.

Connect with Us

Acknowledgments

This project is based on Google Gemini CLI. We acknowledge and appreciate the excellent work of the Gemini CLI team. Our main contribution focuses on parser-level adaptations to better support Qwen-Coder models.