Skip to content

Add Qwen Code as a supported agent - #787

Open
QuanCheng-QC wants to merge 3 commits into
developfrom
feature/workspace-support-qwen-code
Open

QuanCheng-QC wants to merge 3 commits into
developfrom
feature/workspace-support-qwen-code

Conversation

@QuanCheng-QC

Copy link
Copy Markdown
Collaborator

Why

Request, translated from the original:

Integrate Qwen Code (Alibaba Tongyi's coding CLI) into openagents as a new agent. Node side only (packages/agent-connector), no Python SDK. End-to-end with a real key: send a message, reuse the same session across turns, call an MCP tool, Stop.

Qwen Code (@qwen-code/qwen-code, binary qwen) is the first agent in the plan to add Chinese coding agents.

Before and after

On develop, Qwen Code is not in the catalog, the launcher cannot install it, and a workspace cannot run it: createAdapter('qwen-code') throws "Unknown agent type".

Before After
Catalog and launcher Not listed Listed and installable. Includes an icon, en/zh descriptions, and a model picker that lists from Model Studio
Workspace Cannot be added Replies to messages, keeps one CLI session per channel, uses workspace tools over MCP, and stops on Stop
Detection of a CLI the user installed Not detected Detected for npm global, nvm, and both official standalone installers (~/.local/bin, %LOCALAPPDATA%\qwen-code\bin, and QWEN_INSTALL_ROOT / QWEN_INSTALL_BIN_DIR)

How it works

One headless run per message:

qwen --output-format stream-json --approval-mode yolo \
  --exclude-tools cron_create cron_list cron_delete loop_wakeup \
  --auth-type openai --model <model> --mcp-config <file> \
  [--resume <session>] --append-system-prompt <workspace briefing>
  • Input and secrets. The prompt is piped over stdin and the key goes in the environment, so neither appears in argv.
  • Output. The frames have the same shape as Claude Code's. The reply is read from the result frame; assistant frames only drive the progress ticker.
  • Sessions. Each channel's session is resumed with --resume. Qwen files sessions by working directory, so a session is only resumed from the directory it was created in. If the CLI cannot find the saved session, the turn is retried once from scratch. A stopped session stays resumable.
  • Workspace tools. They come from the same stdio MCP server the Claude adapter uses. The config file is written 0600 and deleted when the run ends.
  • Stop. On POSIX, Stop sends SIGINT to the process group, and the CLI kills its own shell tools. On Windows the npm entry script relaunches the CLI as a child process, so Stop uses taskkill /T.
  • Binary. The npm package's cli-entry.js runs under the launcher's Node (the CLI needs Node 22 or newer), and a standalone install runs on the Node it bundles. Either way, the system prompt never passes through cmd.exe and its command-line length limit.
  • Decision log and glossary. Both are pinned into the system prompt on every turn, including resumed turns.

CLI behaviour this relies on

Verified against Qwen Code 0.25.1. The minimum supported version is 0.16.0, the first release that accepts --mcp-config.

  • Auto-memory delays every answer. With the default memory.enableManagedAutoMemory, a memory-extractor subagent runs after each turn and the result frame waits for it. On a resumed session the answer was ready after 7s but the frame arrived after 194s, and the extractor used 100k+ extra tokens. A system-defaults settings layer (QWEN_CODE_SYSTEM_DEFAULTS_PATH) turns auto-memory and self-update off. The user's own settings.json still takes precedence, and a machine-wide system-defaults file is merged in, not replaced. The CLI writes a $version stamp back into the file; the adapter ignores it, so the file is written only once.
  • Credentials. The CLI only infers its OpenAI provider when both a key and a model are set, so --auth-type openai is always passed with a key. Key, base URL and model are taken as one set, either all from the agent's LLM_* fields or all from OPENAI_*, never mixed.
    • A blank base URL means Model Studio only for the agent's own key.
    • A key from the environment with no base URL goes explicitly to api.openai.com, so it is never sent to another provider.
  • Saved providers override CLI arguments. A modelProviders entry in ~/.qwen/settings.json with the same model id redirects the request to its own endpoint, and can even swap in its own key. When the agent supplies a key, a per-run system-settings file clears modelProviders. Machine-wide system settings are carried over, and the temporary file is deleted after the run.
  • API errors. Errors are streamed as [API Error: …] assistant text, followed by an is_error result frame. Release 0.16.0 instead wraps them in a successful result and exits 0. Both forms are reported as errors and never posted as an answer.
  • Text encoding. stdout is decoded as a UTF-8 stream, so multi-byte characters split across chunks are not garbled.
  • Scheduling tools are excluded. They schedule inside a one-shot process that exits when the turn ends, so a reminder set that way would never fire.

Configuration

LLM_API_KEY, LLM_BASE_URL and LLM_MODEL map to OPENAI_*. The base URL is required and prefilled with Model Studio China. The model is required for two reasons:

  • Left blank, the CLI runs qwen3.7-max, its most expensive tier, which a Coding Plan account may not offer.
  • The launcher's "Test connection" probes whatever the form holds, so a blank field tested a different model or endpoint than the agent would use.

The workspace's own agent form can still create the agent with only a key. The adapter then falls back to Model Studio and the CLI's default model.

Compatibility

  • This only adds a new agent. Other adapters are unchanged, and paths.js only gains detection directories.
  • No package or launcher version is changed. The launcher offers Qwen Code once a core release containing this adapter is published and the launcher's core dependency is updated.
  • The branch merges cleanly with current develop (v1.1). The Qwen tests and the full agent-connector suite were re-run on the merged tree.
  • Follow-up, not in this PR: v1.1 gave the Claude adapter's MCP env OPENAGENTS_REQUESTER_EMAIL for escalation tools. The Qwen adapter does not pass it yet, and neither does CodeBuddy's.

Tests

  • qwen-code-stream.test.js and qwen-code.test.js: 77 tests, using a mock CLI that replays real transcripts (success, MCP call, missing key, 401, a 0.16.0-style error, a UTF-8 character split across chunks, stale resume, Stop, settings handling).
  • qwen-code-cli.test.js: an opt-in real-CLI regression test. Set QWEN_CODE_TEST_CLI to a qwen binary; it uses a local HTTP endpoint and no paid key. It passes on 0.25.1 and 0.16.0 and fails on the code before the saved-provider fix.
  • stop-control.test.js, agent-detection-matrix.test.js and the registry sync check now cover qwen-code.
  • End to end with a real Model Studio key against a local workspace backend:
    • A message gets a reply.
    • Three turns run on one CLI session (--resume).
    • workspace_put_todos called over MCP writes todos to the workspace.
    • Stop during a long shell command ends the CLI and its tool process within about 1–2s.
    • The conversation continues on the same session after Stop.
    • The settings file is not rewritten after the first turn.
    • On the final commit, one check missed only because the model answered "PONG" when asked for "PONG-1", and the adapter relayed that verbatim.
  • Tested manually on Windows.
  • Known failures that also occur on develop:
    • wsl.test.js "an agent that only exists inside the distro" fails on the test machine because it reads the real ~/.openagents/runtimes.
    • The first test in launcher index.test.tsx is flaky.
    • Launcher tsc passes.

Docs: docs/agents/qwen-code.md.

Qwen Code (npm @qwen-code/qwen-code, binary qwen) is Alibaba's open-source
terminal coding agent. It runs here the way CodeBuddy does, one headless
`qwen --output-format stream-json` run per message with the prompt piped
over stdin, workspace tools over --mcp-config, and the channel's session
resumed with --resume. The stream is Claude Code's frame format, so the
reply is read from the result frame and assistant frames only drive the
progress ticker.

Behaviour verified against the real CLI (0.25.1) that shaped the adapter

  - Auto-memory is turned off. Left on, every turn ends with a memory
    extractor subagent and the result frame waits for it. A resumed turn
    had its answer after 7s and its result after 194s, with 100k+ extra
    tokens. The adapter loads a system-defaults settings layer
    (QWEN_CODE_SYSTEM_DEFAULTS_PATH) that disables it, plus self-update
    and usage statistics. The user's own settings.json still wins, and a
    machine-wide system-defaults file is merged, not replaced.
  - --auth-type openai is passed whenever the agent has a key. The CLI
    only infers its OpenAI provider when a key AND a model are both set,
    otherwise it fails with "No auth type is selected". Key, base URL and
    model are taken as one set, from the agent's LLM_* fields or from
    OPENAI_*, never mixed. A blank base URL means Model Studio only for
    the agent's own key, so an OPENAI_API_KEY exported for another tool
    is never sent to DashScope.
  - Sessions are filed under the working directory, so a channel's
    session is only resumed from the directory it was created in, and a
    "No saved session found" resume is retried once from scratch. A
    stopped session stays resumable, so Stop keeps the conversation.
  - A model error is streamed as "[API Error ...]" assistant text before
    the is_error result frame. It is reported as an error, never posted
    as the answer.
  - On Windows the npm entry script relaunches the CLI as a child, and
    killing only the parent leaves the turn running, so Stop uses
    taskkill /T. On POSIX, SIGINT to the group ends the run in ~100ms.
  - The npm package runs as cli-entry.js under the launcher's Node (the
    CLI needs Node 22), and a standalone install on its bundled Node, so
    the system prompt never goes through cmd.exe and its length limit.
  - cron_create, cron_list, cron_delete and loop_wakeup are excluded, as
    scheduling inside a one-shot process never fires.

Minimum supported version is 0.16.0, the first release that accepts
--mcp-config.

Wiring
  - registry/qwen-code.json with LLM_API_KEY, LLM_BASE_URL and LLM_MODEL
    mapped to OPENAI_*, synced to the bundled and backend copies, icon in
    every icon dir
  - paths.js detects the standalone installer's Windows dir and its
    QWEN_INSTALL_ROOT and QWEN_INSTALL_BIN_DIR overrides, and the
    detection matrix covers npm and both standalone installers
  - launcher CORE_AGENTS, model picker listing from Model Studio when the
    base URL is blank, icon slug, en and zh descriptions
  - workspace connect view brand colour, docs/agents/qwen-code.md

Tests
  - qwen-code-stream.test.js and qwen-code.test.js (64), with a mock CLI
    that replays the real CLI's success, MCP, missing key, 401, stale
    resume and stop transcripts
  - stop-control.test.js now covers qwen-code
  - end to end with a real Model Studio key against a local workspace
    backend, a reply, three turns on one session, a workspace MCP tool
    writing todos, Stop mid tool clearing the CLI and its tool process in
    about a second, and the conversation continuing after Stop

No package or launcher version is changed.
…tings a run needs

The model and base URL are now required fields, with the base URL prefilled
with Model Studio China.

  - Left blank, the CLI runs qwen3.7-max, its most expensive tier, which a
    Coding Plan account may not offer at all.
  - The launcher's connection test probes what the form holds. A blank
    model made it try the endpoint's first listed model, which this key was
    not entitled to (a 403 that reads as a broken key), and a blank base
    URL made it probe api.openai.com while the agent itself would have used
    Model Studio. With both fields required, the test and the agent use the
    same endpoint and model.

The workspace's own agent form can still create the agent with only a key,
so the adapter keeps its fallbacks (Model Studio for a blank base URL, the
CLI's default model).

The settings layer no longer turns off usage statistics. It changes only
what a headless run depends on, auto-memory and self-update, and leaves the
rest to the user.

The CLI writes a "$version" stamp back into the settings file it loads. The
adapter rewrote the file whenever its text differed, so every turn replaced
the file and every run migrated and wrote it again, while another channel's
run could be reading it. The comparison now ignores that stamp, and the file
is written once.

Verified again end to end with a real Model Studio key against a local
workspace backend, all checks passing, including the settings file staying
untouched after the first turn.
Fix saved-provider credential routing, UTF-8 stream decoding, legacy API error results, and pinned channel context. Preserve system settings and clean legacy migration backups, with mocked and real CLI regression coverage.
@vercel

vercel Bot commented Oct 15, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
openagents-workspace Ready Ready Preview Oct 15, 2026 9:56am UTC

Request Review

This branch was successfully deployed

1 active deployment
Preview — 032d6b3e Deployed Oct 14, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant