* feat(core): accept cross-session messages behind an inbound gate Step two of QwenLM/qwen-code#8724, rebuilt on current main now that the registry from step one has landed. A session can be reached by another session on the same machine, and every message that arrives is gated before the model can act on it. Off by default behind `agents.crossSessionMessaging`. Transport is one UNIX domain socket per session, NDJSON over the wire, one frame per line. The socket directory is 0700 and the socket 0600, and that is the whole access-control story: Node cannot read SO_PEERCRED without a native addon, so a frame's claimed `from` is not authenticated. Everything downstream assumes that. The gate is why the transport and the policy land together. With an explicit `agents.crossSessionInbound` the user decides; unset, the policy follows approval-mode parity — a message auto-delivers only when acting on it cannot do more than the sender could already have done itself. Anything unreadable holds. Held messages are settled rather than stranded: the buffer is bounded, shutdown expires the rest, and every terminal outcome goes back to the sender as a control frame. Content reaches the model inside a <cross_session_message> envelope with the delimiter defanged in the body, so a peer cannot close the envelope early and forge one attributed to the user. The envelope carries a fixed notice that a peer holds none of the user's authority, and the auto-mode classifier gains the matching rule. `/peers` lists and releases held messages; without it, holding would be indistinguishable from dropping. Landed only after four independent reviews, whose non-obvious findings are worth naming because they were all live defects: - A receiver in AUTO_EDIT auto-accepted peer messages and nothing reviewed what they caused. The old rationale — "every consequential action still faces its own gate" — is true of AUTO but false of AUTO_EDIT, where edit confirmations are approved outright and the classifier does not run. A peer could get a file written with no prompt, no classifier and no user. Receiver policy now turns on whether the mode reviews actions at all, not on whether it is YOLO. - `server.unref()` does not cover accepted connections, so any peer that connected and lingered pinned the session open forever. - `fs.mkdir(recursive)` and `fs.chmod` both follow a symlink, so another user could pre-create the world-writable `/tmp` fallback directory and redirect our 0700 chmod onto a directory of ours. - A full listen backlog surfaces as EAGAIN on Linux, not EBUSY; the busy case was being read as dead on the primary platform. - The envelope was escapable without markup: `escapeAttribute` handled `&<>"` but not newlines, so a crafted `fromName` could emit free-standing lines inside the opening tag. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(i18n): translate the /peers description in strict-parity locales `Test (ubuntu-latest, Node 22.x)` has failed every run on this branch with FAIL src/i18n/mustTranslateKeys.test.ts > does not fall back to English for any built-in command description in strict-parity locale { code: 'zh-TW' | 'zh' } AssertionError: expected [ 'peers' ] to deeply equal [] `peersCommand` set `description` to a literal English string rather than routing it through `t()`, so both strict-parity Chinese locales fell back to English and the parity test — which exists to catch exactly this — reported the `peers` path. Four merges of current main were attempted against this check. None could fix it: the untranslated description is introduced by this branch, so no state of main contains the missing keys. Use the `get description() { return t(...) }` form every other built-in command uses, and add the key to `en.js`, `zh.js` and `zh-TW.js`. All three locale files remain in sync at 1817 keys. Verified both directions in a clean worktree install: with the change, `mustTranslateKeys.test.ts` is 20/20; stashing it reproduces the CI failure exactly, `expected [ 'peers' ] to deeply equal []`, twice. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VgTjRF91xANQh6SY9YGyCf * fix(ipc): close Critical review findings in the cross-session inbox - Dedup held msgIds case-insensitively, matching /peers resolution - Print unique dash-stripped handles and resolve them in /peers - Bound outbound sends: in-flight cap plus an absolute deadline (socket.setTimeout is an idle timer a dribbling peer resets) - Defang whitespace-split and quote-glued envelope delimiter tokens - Seed onHeldChange subscribers with the current held snapshot - Skip POSIX-only socket-path tests on win32 like the sibling suites * fix(cli): gate held-message announcements on the held set growing Once the hold buffer is full, every further peer frame evicts the oldest entry while arriving under a fresh id; announcing each of those appends a history item and re-renders per frame — reintroducing one layer up the unbounded growth the hold buffer's ceiling exists to prevent. * fix(ipc): close round-6 Critical findings in the cross-session inbox * fix(ipc): close round-7 Critical findings in the cross-session inbox - defang envelope delimiters split by render-invisible separators the \s class misses, sharing flattenPeerLabel's strip class so the two cannot drift (R5-1) - restore admission-failed queue submissions deferred until idle, the same recovery the /btw path uses, so a restored peer envelope cannot leak raw into the racing turn's mid-turn steer drain (R7-1) - cap the accepted-message backlog symmetric to the held cap: the pre-wiring buffer and the post-wiring queue refuse once full, with an honest 'expired' receipt instead of unbounded socket-speed growth (R7-11) * fix(ipc): close round-8 Critical findings in the cross-session inbox - Refuse any msgId canonicalizing to `all` at the wire boundary and fold the /peers bulk keyword case, so a peer-chosen id can never alias the bulk action or trap a per-message decision (R8-1, R1-11, R1-27). - Re-hold approved messages whose delivery fails instead of silently dropping them: decide() returns 'failed' and parks the entry back at its position, reevaluate() re-holds failed releases, and /peers reports the failure honestly (R8-2, R1-18). - Drain accepted peer envelopes on a preprocessing-free path: the queue marks them peer, the drain submits them with the Teammate send type (which returns before slash/shell/@ handling), renders the one-line projection, and restores failed admissions as peer entries (R8-3). - Close the envelope-defang class structurally: escape every `<` in peer content so no forged delimiter survives regardless of wedged invisibles or homoglyph spellings; the open-enumeration regex is deleted (R5-1). Every guard is witnessed by new focused tests and verified with mutation probes (each probe fails its witnesses when removed, passes restored). * fix(ipc): close peer messaging lifecycle races * chore(cli): regenerate the settings schema for the reworded inbound policy CI regenerates `settings.schema.json` and fails the Ubuntu job when the result differs from what is committed. Rewording the `crossSessionInbound` description in `settingsSchema.ts` (7ca3be72b2, for the AUTO change) moved the generated file without it being regenerated, so the job never reached lint, typecheck or tests. Generated output only — one description string, no behaviour and no hand-editing. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(cli): close peer-inbox admission and decision review gaps (#9576) * fix(cli): settle all peer-inbox receipts at teardown (#9576) * fix(cli): restore peer message when its in-flight turn fails delivery (#9576) * fix(cli): retry restored peer envelope once the failed turn settles (#9576) --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> Co-authored-by: qwen-code-dev-bot <qwen-code-dev@service.alibaba.com> Co-authored-by: yiliang114 <effortyiliang@gmail.com> Co-authored-by: qqqys <266654365+qqqys@users.noreply.github.com> |
||
|---|---|---|
| .github | ||
| .husky | ||
| .qwen | ||
| .vscode | ||
| docs | ||
| docs-site | ||
| eslint-rules | ||
| integration-tests | ||
| integrations/external-context | ||
| packages | ||
| patches | ||
| scripts | ||
| .dockerignore | ||
| .editorconfig | ||
| .gitattributes | ||
| .gitignore | ||
| .npmrc | ||
| .nvmrc | ||
| .prettierignore | ||
| .prettierrc.json | ||
| .yamllint.yml | ||
| AGENTS.md | ||
| CHANGELOG.md | ||
| CLAUDE.md | ||
| CONTRIBUTING.md | ||
| Dockerfile | ||
| esbuild.config.js | ||
| eslint.config.js | ||
| eslint.legacy-filenames.mjs | ||
| LICENSE | ||
| Makefile | ||
| package-lock.json | ||
| package.json | ||
| README.md | ||
| SECURITY.md | ||
| tsconfig.json | ||
| vitest.config.ts | ||
The open-source AI coding agent that lives in your terminal.
中文 | Deutsch | français | 日本語 | Русский | Português (Brasil) | 한국어
Why Qwen Code?
- Agentic out of the box — Auto-Memory, Auto-Skills, SubAgents, Agent Teams, and MCP. Dynamic workflows, zero setup.
- Open-source, inside and out — The framework and the Qwen models are open-source. They evolve together. No vendor lock-in.
- Multi-protocol — Supports OpenAI, Anthropic, Gemini, and Qwen APIs. Any third-party provider or local model (Ollama / vLLM). Switch at runtime.
- Beyond the terminal — IDE plugins, Desktop app, daemon mode, SDKs, and IM bots (Telegram / DingTalk / WeChat / Feishu).
Tip
Qwen Code is actively iterating on itself — using its own agent and models to file issues, submit PRs, review code, and run tests. Powered by the community, driven by AI.
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
Restart your terminal after installation to ensure environment variables take effect.
NPM / Homebrew
NPM (requires Node.js 22+):
npm install -g @qwen-code/qwen-code@latest
Homebrew (macOS / Linux):
brew install qwen-code
Quick Start
qwen # Launch interactive terminal UI
# Inside the session:
/auth # Configure your provider and API key
See the Authentication Guide and Settings Reference for detailed setup.
How to Use Qwen Code
| Mode | Command | Use Case |
|---|---|---|
| Interactive | qwen |
Terminal UI with rich rendering, @file references, slash commands |
| Headless | qwen -p "..." |
Scripts, CI/CD, batch processing — no UI |
| IDE | — | VS Code, Zed, JetBrains |
| Desktop | — | Qwen Code Desktop — GUI for macOS, Windows, Linux |
| Daemon | qwen serve |
Shared agent session over HTTP+SSE (ACP). Multiple clients, one agent. (experimental) Docs |
| SDK | — | TypeScript, Python, Java |
| IM Bot | qwen channel |
Connect to Telegram, DingTalk, WeChat, or Feishu |
SDK example (Python)
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())
Capabilities
If you know Claude Code, you already know Qwen Code — and then some. We've put significant effort into bringing Qwen Code to feature parity with Claude Code, improving both breadth and reliability across the board.
| Feature | Qwen Code | Claude Code |
|---|---|---|
| SubAgents, Agent Teams, Dynamic Workflows | ✓ | ✓ |
| Auto-Memory, Auto-Skills, Hooks | ✓ | ✓ |
| Built-in Skills (/review, /batch, /loop, /bugfix…) | ✓ | ✓ |
| MCP, Plan Mode, LSP Integration | ✓ | ✓ |
| Auto Mode, Sandbox, Git Worktrees | ✓ | ✓ |
| Computer Use (desktop automation) | ✓ | ✓ |
| IDE Plugins (VS Code / JetBrains / Zed) | ✓ | ✓ |
| SDK | ✓ | ✓ |
| Headless Mode, Session Management | ✓ | ✓ |
| Open-source — model and framework | ✓ | — |
| Multi-protocol (OpenAI / Anthropic / Gemini / Qwen + any provider) | ✓ | — |
| Agent Arena (multi-model head-to-head on same task) | ✓ | — |
Daemon Mode — qwen serve (multi-client shared agent) |
✓ | — |
| IM Channels (Telegram / DingTalk / WeChat / Feishu) | ✓ | — |
Ecosystem
-
Qwen Code Desktop — Official desktop app for macOS, Windows, and Linux
-
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
-
🦞 Qwen Code Claw — Let other agents (Claude, Codex, etc.) delegate coding tasks to Qwen Code via ACP. Paste this prompt 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.
- Aliyun Model Studio CLI — Official CLI for Aliyun's AI platform (
bailian-cli). Extends Qwen Code with image/video generation, knowledge retrieval, app orchestration, and model deployment
Contributing
Contributions are welcome! See CONTRIBUTING.md for guidelines.
Acknowledgments
This project was originally based on Google Gemini CLI v0.8.2. We gratefully acknowledge the Gemini CLI team's excellent work. Starting from Qwen Code v0.1, we stopped syncing with upstream and began independent development as a multi-protocol, multi-platform agent framework with deep integrations for Qwen models and beyond.
