Skip to content

feat(core): add dynamic swarm worker tool#3433

Merged
wenshao merged 2 commits into
QwenLM:mainfrom
reidliu41:feat/agent-swarm-workers
Apr 19, 2026
Merged

feat(core): add dynamic swarm worker tool#3433
wenshao merged 2 commits into
QwenLM:mainfrom
reidliu41:feat/agent-swarm-workers

Conversation

@reidliu41

Copy link
Copy Markdown
Contributor

TLDR

Adds a new swarm core tool for ad-hoc parallel worker execution.

The main agent can now dynamically spawn ephemeral workers from a single tool call, pass each worker its own task prompt, run workers with bounded concurrency, and receive aggregated results. The implementation supports wait_all and first_success modes, isolates individual worker failures, and prevents nested workers from recursively spawning more swarm or agent sessions.

Screenshots / Video Demo

/usr/bin/env -u HTTP_PROXY -u HTTPS_PROXY -u ALL_PROXY -u http_proxy -u https_proxy -u all_proxy \
    NO_PROXY=localhost,127.0.0.1,::1 \
    QWEN_RUNTIME_DIR=/tmp/qwen-swarm-runtime \
    QWEN_CODE_MAX_SWARM_CONCURRENCY=2 \
    timeout 180s node dist/cli.js \
    -p "Use the swarm tool exactly once. Do not call any other tool. Build the swarm arguments exactly as follows: description is
  'deterministic swarm smoke test'; mode is 'wait_all'; max_concurrency is 2; max_turns is 1; timeout_seconds is 30; worker_system_prompt is
  'Return exactly the requested string. Do not use tools.'; tasks is an array of exactly three objects. Task 1: id 'alpha', description 'return
  alpha', prompt 'Return exactly: alpha'. Task 2: id 'beta', description 'return beta', prompt 'Return exactly: beta'. Task 3: id 'gamma',
  description 'return gamma', prompt 'Return exactly: gamma'. After the swarm returns, report the completed and failed counts." \
    --core-tools swarm \
    --output-format stream-json \
    --include-partial-messages

  This command intentionally uses --core-tools swarm so the model can only call the new swarm tool, making the output easier to audit.

  Expected CLI initialization proof:

  {
    "type": "system",
    "subtype": "init",
    "tools": [
      "agent",
      "swarm",
      "skill",
      "ask_user_question",
      "exit_plan_mode"
    ],
    "model": "gpt-oss:120b"
  }

  This proves the built CLI registered swarm as an available core tool.

  Expected tool call proof:

  {
    "type": "tool_use",
    "id": "call_g65wdtqz",
    "name": "swarm",
    "input": {
      "description": "deterministic swarm smoke test",
      "mode": "wait_all",
      "max_concurrency": 2,
      "max_turns": 1,
      "timeout_seconds": 30,
      "worker_system_prompt": "Return exactly the requested string. Do not use tools.",
      "tasks": [
        {
          "description": "return alpha",
          "id": "alpha",
          "prompt": "Return exactly: alpha"
        },
        {
          "description": "return beta",
          "id": "beta",
          "prompt": "Return exactly: beta"
        },
        {
          "description": "return gamma",
          "id": "gamma",
          "prompt": "Return exactly: gamma"
        }
      ]
    }
  }

  This proves the parent agent dynamically created three worker tasks at runtime, without any predefined subagent configuration.

  Expected aggregated tool result proof:

  ### Swarm Complete

  **Task**: deterministic swarm smoke test
  **Mode**: wait_all
  **Max concurrency**: 2

  | Status | Count |
  | --- | ---: |
  | Success | 3 |
  | Failed | 0 |
  | Cancelled | 0 |
  | Not started | 0 |

  | Task | Status | Result |
  | --- | --- | --- |
  | alpha | success | alpha |
  | beta | success | beta |
  | gamma | success | gamma |

  This proves the workers executed successfully and the parent received one aggregated result containing every worker outcome.

  Expected final CLI result proof:

  {
    "type": "result",
    "subtype": "success",
    "is_error": false,
    "num_turns": 2,
    "result": "Completed: 3 / 3 failed: 0",
    "permission_denials": []
  }

  This proves the end-to-end non-interactive CLI command completed successfully after the swarm tool returned.

Dive Deeper

This PR introduces SwarmTool, a lightweight map-style worker orchestration tool.

Key behavior:

  • Dynamically accepts tasks[] at runtime.
  • Does not require predefined subagent roles or configuration files.
  • Creates one ephemeral headless worker per task.
  • Runs workers concurrently with max_concurrency.
  • Aggregates all worker results into one parent tool result.
  • Supports wait_all for full aggregation.
  • Supports first_success for first-result-wins workflows.
  • Captures individual worker failures without failing unrelated workers.
  • Filters swarm and agent out of nested worker contexts to avoid recursive fan-out.

Updated areas:

  • Core tool implementation and registration.
  • Tool scheduler concurrency safety.
  • Nested agent/worker tool filtering.
  • Public core exports.
  • Developer and user documentation.

Reviewer Test Plan

Run the focused swarm tests:

cd packages/core
npx vitest run src/tools/swarm.test.ts

Expected output:

✓ src/tools/swarm.test.ts (4 tests)

Test Files 1 passed (1)
Tests 4 passed (4)

This directly validates:

  • parameter validation,
  • mixed success/failure aggregation,
  • max_concurrency,
  • first_success.

Run the full core suite:

cd packages/core
npm run test

Expected output includes the new test file and full core pass:

✓ src/tools/swarm.test.ts (4 tests)

Test Files 238 passed (238)
Tests 5794 passed | 2 skipped (5796)

Run core static checks:

cd packages/core
npm run typecheck
npm run lint

Expected output:

@qwen-code/qwen-code-core@0.14.5 typecheck
tsc --noEmit

@qwen-code/qwen-code-core@0.14.5 lint
eslint . --ext .ts,.tsx

Run root build and bundle from the repository root:

npm run build
npm run bundle

Expected bundle output:

Copied sandbox profiles to dist/
Copied vendor directory to dist/
Copied bundled skills to dist/bundled/
Copied docs/users/ to dist/bundled/qc-helper/docs/

✅ All bundle assets copied to dist/

Run root checks:

npm run typecheck
npm run lint

Expected output includes:

@qwen-code/qwen-code@0.14.5 typecheck
npm run typecheck --workspaces --if-present

@qwen-code/qwen-code@0.14.5 lint
eslint . --ext .ts,.tsx && eslint integration-tests

Run root tests:

npm run test

Expected output:

packages/cli:
Test Files 268 passed (268)
Tests 4183 passed | 7 skipped (4190)

packages/core:
Test Files 238 passed (238)
Tests 5794 passed | 2 skipped (5796)

packages/sdk-typescript:
Test Files 6 passed (6)
Tests 201 passed (201)

packages/vscode-ide-companion:
Test Files 30 passed (30)
Tests 192 passed | 1 skipped (193)

Testing Matrix

🍏 🪟 🐧
npm run
npx
Docker
Podman - -
Seatbelt - -

Linked issues / bugs

Resolves #1816

  Add a swarm tool for ad-hoc parallel worker execution with bounded concurrency, wait-all and first-success modes, per-worker failure
  isolation, and aggregated results.

  Register the tool in core, prevent nested worker recursion, and document the new workflow.
Comment thread packages/core/src/tools/swarm.ts Fixed
Comment thread packages/core/src/core/coreToolScheduler.ts Outdated
Comment thread packages/core/src/tools/swarm.ts
  Prevent swarm calls from bypassing the outer scheduler concurrency budget.

  Disallow interactive question prompts in swarm workers by default, and avoid incomplete Markdown table escaping by using an HTML entity for
  pipe characters. Add focused tests for the scheduler behavior, worker tool restrictions, and result formatting.

@wenshao wenshao left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No issues found. LGTM! ✅ — gpt-5.4 via Qwen Code /review

@wenshao

wenshao commented Apr 19, 2026

Copy link
Copy Markdown
Collaborator

Verified end-to-end on the current head (6b8275818fix(core): harden swarm worker execution). Ran three scenarios against glm-5.1 with --core-tools swarm --output-format stream-json:

1. wait_all smoke (per PR description, 3 tasks × "Return exactly: X", max_concurrency=2)

  • CLI init event: tools: ["agent","swarm","skill","ask_user_question","exit_plan_mode"] → swarm registered.
  • tool_use input carried the full tasks array built at runtime (no predefined subagent config).
  • Aggregated tool_result: Success: 3 / Failed: 0, num_turns: 2, ~18s total.

2. first_success early termination (max_concurrency=1, 3 tasks)

| one   | success     | one |
| two   | not_started |     |
| three | not_started |     |

Confirms shouldStopLaunching in runFirstSuccess blocks queued workers from launching after the first success, and that the aggregator correctly distinguishes not_started from cancelled.

3. per-worker timeout (timeout_seconds=1, prompts that force >1s output)

| slow-a | cancelled | Worker timed out after 1 seconds. |
| slow-b | cancelled | Worker timed out after 1 seconds. |

Confirms the setTimeout-driven abort in executeTask routes to status: 'cancelled' with the documented error message.

The two review comments from the earlier /review pass are addressed in 6b8275818:

  • Scheduler no longer marks swarm as concurrency-safe; new should execute multiple swarm tools sequentially test locks in the behavior.
  • Workers default-disallow ask_user_question via DEFAULT_WORKER_DISALLOWED_TOOLS, with coverage in swarm.test.ts.

The CodeQL "incomplete string escaping" alert is also resolved by switching escapeMarkdownTableCell to the | HTML entity (with a direct test assertion).

Unit tests pass locally: swarm.test.ts 6/6, coreToolScheduler.test.ts 66/66.

One non-blocking observation for follow-up: createWorkerConfig uses Object.create(this.config) to shadow a single method (swarm.ts:499-506). It works today but relies on prototype-chain impersonation — if Config ever adopts #private fields or this-sensitive caching, this path would need revisiting. A short comment documenting the invariant would help future readers.

LGTM.

@wenshao wenshao merged commit f7ebc37 into QwenLM:main Apr 19, 2026
13 checks passed
@tanzhenxin

Copy link
Copy Markdown
Collaborator

Opening a discussion on how we handle larger community contributions going forward — using this PR as a concrete example, not to critique it specifically.

My main concern is design scrutiny. This PR adds ~900 lines introducing a new top-level core tool that overlaps meaningfully with our existing agent tool, and it landed without any design discussion on the linked issue or elsewhere. Questions I'd have expected us to resolve first:

  • Should swarm be a separate tool, or a mode of agent? What's the long-term story for having both?
  • How does it interact with the scheduler, nested agents, and tool filtering as a coherent system — not just "does the new tool work in isolation"?
  • What's the contract we're committing to for users? New tool surfaces are hard to remove once shipped.

These are the kinds of questions a design doc or an issue thread surfaces naturally, and that an automated /review pass can't. I'd like us to adopt a convention: for community PRs that introduce a new tool or touch shared infrastructure, we align on the design first — on the issue, in a short doc, or in the PR description before implementation — and require at least one human review before merge.

Curious what others think. Not asking to revisit this PR; just want us to be deliberate about how new surfaces enter the codebase.

@reidliu41

Copy link
Copy Markdown
Contributor Author

Thanks for raising this. I agree with the concern.

I initially treated the linked issue as a detailed feature proposal because it included pain points, requirements, examples, and acceptance criteria. In hindsight, I understand that this was not the same as explicit design alignment from maintainers, especially for a new top- level tool touching shared execution infrastructure.

I apologize for the extra review and discussion burden this created. As an external contributor, I am still learning the project’s expectations around larger design changes, and I appreciate guidance on the right process.

I also agree that for future changes introducing new tools or touching shared infrastructure, it would be helpful to have explicit human design alignment before merge, in addition to automated review.

Since this PR has already been merged, I am happy to help with whichever follow-up path maintainers prefer: revert/remove the tool and discuss the design first, gate it as experimental, reshape it as an agent mode, or add a design rationale documenting the intended boundary and user contract.

@pomelo-nwu

Copy link
Copy Markdown
Collaborator

@wenshao 老师,原则上,我们不合并任何没有演示视频,没有产完整 E2E 测试的 PR ,同时也不能完全依赖 gpt-5.4 的review 能力,swarm agents 和 agent team 息息相关,是非常重要的能力,原则上也不接受社区 PR

@pomelo-nwu

Copy link
Copy Markdown
Collaborator

@wenshao @reidliu41 我建议 revert 该 PR

wenshao added a commit that referenced this pull request Apr 20, 2026
wenshao added a commit that referenced this pull request Apr 20, 2026
chiga0 pushed a commit that referenced this pull request Apr 24, 2026
xaelistic pushed a commit to xaelistic/qwen-code that referenced this pull request Jun 7, 2026
* feat(core): add dynamic swarm worker tool

  Add a swarm tool for ad-hoc parallel worker execution with bounded concurrency, wait-all and first-success modes, per-worker failure
  isolation, and aggregated results.

  Register the tool in core, prevent nested worker recursion, and document the new workflow.

* fix(core): harden swarm worker execution

  Prevent swarm calls from bypassing the outer scheduler concurrency budget.

  Disallow interactive question prompts in swarm workers by default, and avoid incomplete Markdown table escaping by using an HTML entity for
  pipe characters. Add focused tests for the scheduler behavior, worker tool restrictions, and result formatting.
xaelistic pushed a commit to xaelistic/qwen-code that referenced this pull request Jun 7, 2026
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.

Feature Request: Agent Swarm - Dynamic Parallel Worker Spawning

5 participants