Find a file
qqqys f9470f570a
feat(core): accept cross-session messages behind an inbound gate (#9576)
* 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>
2026-08-26 11:51:31 +00:00
.github fix(review): give cancelled runs an accurate fallback body instead of the failure comment (#10114) 2026-08-26 10:49:08 +00:00
.husky Sync upstream Gemini-CLI v0.8.2 (#838) 2025-10-23 09:27:04 +08:00
.qwen fix(live): restore Live Host after desktop removal (#9994) 2026-08-25 12:32:31 +00:00
.vscode Merge branch 'main' into feat/sandbox-config-improvements 2026-03-06 14:38:39 +08:00
docs feat(acp): enable managed auto-memory lifecycle (#9992) 2026-08-26 09:30:42 +00:00
docs-site Hide internal docs from docs site (#4357) 2026-06-01 15:55:14 +08:00
eslint-rules refactor(core): make utils/ a leaf layer (#9778) 2026-08-24 07:43:01 +00:00
integration-tests feat(acp): enable managed auto-memory lifecycle (#9992) 2026-08-26 09:30:42 +00:00
integrations/external-context chore(release): v0.22.0 (#9736) 2026-08-22 15:23:02 +00:00
packages feat(core): accept cross-session messages behind an inbound gate (#9576) 2026-08-26 11:51:31 +00:00
patches feat(cli): add TUI image display tool (#8217) 2026-08-01 12:39:52 +00:00
scripts fix(review): give cancelled runs an accurate fallback body instead of the failure comment (#10114) 2026-08-26 10:49:08 +00: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 chore(ci): Add security hygiene: CODEOWNERS for release workflows, least-privilege permissions, security checks and Scorecard (#9008) 2026-08-14 01:22:53 +00: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 feat(desktop): remove packages/desktop after OpenWork fork; keep the Tauri upgrade bridge (#9085) 2026-08-25 05:26:11 +00:00
.prettierrc.json pre-release commit 2025-07-22 23:26:01 +08:00
.yamllint.yml feat(desktop): remove packages/desktop after OpenWork fork; keep the Tauri upgrade bridge (#9085) 2026-08-25 05:26:11 +00:00
AGENTS.md fix(devx): fail with actionable message when unit-test build prerequisites are missing (#9149) (#9171) 2026-08-18 13:19:09 +00:00
CHANGELOG.md chore(release): v0.22.0 (#9736) 2026-08-22 15:23:02 +00:00
CLAUDE.md docs: rewrite CLAUDE.md to point to AGENTS.md as authoritative source (#5138) 2026-06-15 15:23:26 +08:00
CONTRIBUTING.md revert: remove local PR verification gate (#7031) 2026-07-16 11:24:38 +00:00
Dockerfile perf(ci): cut the E2E suite from ~40min to ~24min (#7798) 2026-07-28 12:56:34 +00:00
esbuild.config.js refactor(core): make utils/ a leaf layer (#9778) 2026-08-24 07:43:01 +00:00
eslint.config.js fix(live): restore Live Host after desktop removal (#9994) 2026-08-25 12:32:31 +00:00
eslint.legacy-filenames.mjs refactor(cli): remove unused useInputHistoryStore hook (#10041) 2026-08-26 07:38:30 +00: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 fix(live): restore Live Host after desktop removal (#9994) 2026-08-25 12:32:31 +00:00
package.json fix(live): restore Live Host after desktop removal (#9994) 2026-08-25 12:32:31 +00:00
README.md docs(readme): add Korean to the documentation language bar (#8836) 2026-08-10 07:34:11 +00: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 feat(channels): add DingTalk Workspace channel (#9394) 2026-08-25 06:40:47 +00:00

npm version License Node.js Version Downloads

QwenLM%2Fqwen-code | Trendshift

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.

Qwen Code

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.