feat(cli): Enable multi-workspace session routing - #6511
Conversation
Implement the Phase 2a sessions closed loop for qwen serve multi-workspace mode. Multiple explicit workspaces now create registered runtimes while legacy workspace surfaces remain primary-only, and live session routes dispatch by owning runtime. Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
Code Coverage Summary
CLI Package - Full Text ReportCore Package - Full Text ReportFor detailed HTML reports, please see the 'coverage-reports-22.x-ubuntu-latest' artifact from the main CI run. |
There was a problem hiding this comment.
Pull request overview
Enables Phase 2a “multi-workspace sessions” for qwen serve by allowing multiple explicit --workspace inputs, registering each as a runtime, and routing the live session closed-loop to the owning workspace runtime (while keeping legacy/non-session surfaces primary-only). It also updates the daemon capability/status schema (additive fields) plus SDK types and documentation to match.
Changes:
- Accept repeated
--workspaceinputs inqwen serve, boot a primary runtime plus sessions-only secondary runtimes, and derive an effectivemaxTotalSessionsdefault in multi-workspace mode. - Route live session endpoints (prompt/cancel/heartbeat/detach/events/status/pending-prompts/permission votes, plus live-only non-primary session listing) by resolving the live owner runtime before calling a bridge.
- Advertise
multi_workspace_sessions,workspaces[], and additive session limits only when multiple runtimes are registered; update SDK types/tests and daemon docs accordingly.
Reviewed changes
Copilot reviewed 35 out of 35 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| packages/sdk-typescript/test/unit/DaemonClient.test.ts | Adds coverage ensuring capability envelopes preserve multi-workspace metadata. |
| packages/sdk-typescript/src/index.ts | Re-exports the new workspace capability type. |
| packages/sdk-typescript/src/daemon/types.ts | Extends capability/status types with workspaces[] and additive limits fields. |
| packages/sdk-typescript/src/daemon/index.ts | Re-exports the new workspace capability type from the daemon barrel. |
| packages/sdk-typescript/src/daemon/DaemonClient.ts | Updates SDK request docs/comments to describe registered workspaces vs a single bound workspace. |
| packages/sdk-typescript/src/daemon-mcp/serve-bridge/tools/session.ts | Updates MCP tool schema description to “primary workspace” wording. |
| packages/cli/src/serve/workspace-inputs.ts | Introduces multi-workspace parsing helper while keeping single-workspace callers gated. |
| packages/cli/src/serve/workspace-inputs.test.ts | Adds tests for both single-workspace rejection and multi-workspace parsing behavior. |
| packages/cli/src/serve/types.ts | Updates serve option/capabilities typing to describe primary vs registered workspaces + additive limits. |
| packages/cli/src/serve/server/session-list.ts | Adds a live-only workspace session listing helper for non-primary runtimes. |
| packages/cli/src/serve/server/serve-features.ts | Adds multi_workspace_sessions toggle wiring and maxSessions advertising helper. |
| packages/cli/src/serve/server.ts | Threads workspaceRegistry through route registration and feature toggles. |
| packages/cli/src/serve/server.test.ts | Updates serve feature advertisement tests and adjusts workspace-related session tests. |
| packages/cli/src/serve/run-qwen-serve.ts | Boots multiple runtime bridges/services, derives total session cap, aggregates gauges, and improves multi-bridge shutdown. |
| packages/cli/src/serve/run-qwen-serve.test.ts | Adds tests for multi-workspace capability advertisement, injected-bridge rejection, and multi-bridge cleanup. |
| packages/cli/src/serve/routes/sse-events.ts | Resolves live session owner runtime for SSE event streaming. |
| packages/cli/src/serve/routes/session.ts | Routes live session endpoints by owner runtime and makes non-primary workspace session listing live-only. |
| packages/cli/src/serve/routes/permission.ts | Resolves live session owner runtime for session-scoped permission voting. |
| packages/cli/src/serve/routes/daemon-status.ts | Passes workspaceRegistry into status response building. |
| packages/cli/src/serve/routes/capabilities.ts | Conditionally advertises workspaces[] and additive session limits when multi-workspace is active. |
| packages/cli/src/serve/multi-workspace-sessions.test.ts | New end-to-end-ish route dispatch tests validating owner routing and failure modes. |
| packages/cli/src/serve/daemon-status.ts | Aggregates status/session counts across runtime bridges and conditionally exposes workspace metadata. |
| packages/cli/src/serve/capabilities.ts | Registers multi_workspace_sessions as a capability tag and adds conditional toggle plumbing. |
| packages/cli/src/commands/serve.ts | Updates CLI flag help text for repeatable --workspace and multi-workspace HTTP-bridge semantics. |
| docs/developers/qwen-serve-protocol.md | Updates protocol docs for primary workspace semantics, workspaces list, and multi-workspace limits behavior. |
| docs/developers/examples/daemon-client-quickstart.md | Updates SDK quickstart to describe primary vs multi-workspace session creation. |
| docs/developers/daemon/20-quickstart-operations.md | Updates ops flag table for repeatable --workspace (and related wording). |
| docs/developers/daemon/17-configuration.md | Documents repeatable --workspace configuration. |
| docs/developers/daemon/16-vscode-ide-adapter.md | Updates IDE adapter docs to allow registered workspace runtime selection. |
| docs/developers/daemon/15-channel-adapters.md | Clarifies channel-worker remains primary-only in Phase 2a. |
| docs/developers/daemon/11-capabilities-versioning.md | Updates capabilities contract wording and adds workspaces[] shape. |
| docs/developers/daemon/02-serve-runtime.md | Updates serve runtime responsibilities for primary + additional workspace canonicalization. |
| docs/developers/daemon/01-architecture.md | Updates high-level architecture description for multi-workspace session runtimes. |
| docs/developers/daemon/00-index.md | Updates glossary definition for workspace vs primary + workspaces list. |
| docs/design/daemon-multi-workspace-phase2a-sessions.md | Updates Phase 2a design doc to reflect the ungated sessions closed loop and additive schemas. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
Thanks for the PR! Template looks good ✓ — all required headings present, including 中文说明, Reviewer Test Plan with How to verify, Evidence, and Tested on. Problem: This is a planned feature addition — Phase 2a of the multi-workspace daemon roadmap (#6378). Phase 1 laid the foundation (WorkspaceRegistry, Direction: Aligned. Multi-workspace session dispatch is a natural next step after the WorkspaceRegistry foundation landed in Phase 1. The Size: 1,634 production lines (additions + deletions, excluding test/generated/schema files), 714 test lines, 258 docs lines. This exceeds both the 500-line maintainer-awareness threshold and the 1,000-line large-PR advisory. The production changes span Approach: The scope feels right for what Phase 2a sessions requires. The Moving on to code review. 🔍 中文说明感谢贡献! 模板完整 ✓ — 所有必需的章节齐全,包括中文说明、Reviewer Test Plan 的 How to verify、Evidence 和 Tested on。 问题:这是一个计划中的功能添加 — 多工作区 daemon 路线图的 Phase 2a(#6378)。Phase 1 奠定了基础(WorkspaceRegistry、 方向:对齐。多工作区会话分发是 Phase 1 WorkspaceRegistry 基础落地后的自然下一步。 规模:1,634 行生产代码(增删合计,排除测试/生成/schema 文件),714 行测试代码,258 行文档。超过了 500 行的维护者关注阈值和 1,000 行的大 PR 建议。生产代码变更涵盖 方案:范围对于 Phase 2a sessions 的需求来说是合理的。 进入代码审查 🔍 — Qwen Code · qwen3.7-max |
Code ReviewIndependent proposal: For multi-workspace session dispatch, I would have either (a) maintained a session-id → workspace-id mapping updated at creation time, or (b) scanned runtime bridges on each operation. Option (a) is faster but requires state synchronization on session cleanup/archive. Option (b) is simpler and correct-by-construction but O(n) per operation. For the expected small workspace count (2–5), scanning is the right tradeoff. Diff comparison: The PR chose option (b) — Reuse check: The Findings: No correctness bugs, security holes, or regressions found. The trust check for non-primary workspaces ( E2E TestingStarted daemon with two workspaces, verified capabilities, daemon status, and workspace mismatch rejection. Daemon startup: 1. GET /capabilities — confirms {
"features": [ ..., "multi_workspace_sessions", "voice_transcribe" ],
"limits": {
"maxPendingPromptsPerSession": 5,
"maxSessionsPerWorkspace": 20,
"maxTotalSessions": 40
},
"workspaces": [
{ "id": "8437b50f81a3f5bc", "cwd": "/tmp/triage-ws1-3080916", "primary": true, "trusted": true },
{ "id": "438afc9ac2e597a6", "cwd": "/tmp/triage-ws2-3080916", "primary": false, "trusted": true }
]
}2. GET /daemon/status — confirms aggregated workspace metadata: {
"status": "ok",
"limits": { "maxSessions": 20, "maxTotalSessions": 40 },
"workspaces": [
{ "id": "8437b50f81a3f5bc", "cwd": "/tmp/triage-ws1-3080916", "primary": true, "trusted": true },
{ "id": "438afc9ac2e597a6", "cwd": "/tmp/triage-ws2-3080916", "primary": false, "trusted": true }
],
"runtime": {
"sessions": { "active": 0, "admissionInFlight": 0 },
"permissions": { "pending": 0 },
"channel": { "live": true }
}
}3. POST /session with unknown cwd — confirms 400 workspace_mismatch rejection: Unit TestsAll 1,099 tests pass (276 CLI + 212 SDK + 611 server). No whitespace errors. 中文说明代码审查独立方案: 对于多工作区会话分发,我会选择 (a) 在创建时维护 session-id → workspace-id 映射并在每次操作时查找,或 (b) 在每次操作时扫描 runtime bridge。方案 (a) 更快但需要在会话清理/归档时同步状态。方案 (b) 更简单且构造即正确,但每次操作 O(n)。对于预期的小工作区数量(2-5),扫描是正确的取舍。 Diff 比较: PR 选择了方案 (b) — 复用检查: Phase 1 的 发现: 未发现正确性 bug、安全漏洞或回归。非主工作区的信任检查( E2E 测试用两个工作区启动 daemon,验证了 capabilities、daemon status 和 workspace mismatch 拒绝。所有结果符合预期。 单元测试全部 1,099 个测试通过(276 CLI + 212 SDK + 611 server)。无空白字符错误。 — Qwen Code · qwen3.7-max |
|
This PR does what it says — closes the multi-workspace session dispatch loop that Phase 1 started. The implementation is straightforward: scan runtime bridges to find the session owner, fast-path for single-workspace, proper error codes for not-found and ambiguous cases. The E2E testing confirms the three key behaviors: capabilities publishes the new Unit tests (1,099 total) all pass. Code review found no correctness bugs, security issues, or convention violations. The one flag: 1,634 production lines across Escalating to maintainer for final review. 👀 中文说明这个 PR 完成了它承诺的事情 — 闭合了 Phase 1 开始的多工作区会话分发闭环。实现直截了当:扫描 runtime bridge 找到会话所有者,单工作区走快速路径,not-found 和 ambiguous 情况有正确的错误码。 E2E 测试确认了三个关键行为:capabilities 发布了新的 单元测试(共 1,099 个)全部通过。代码审查未发现正确性 bug、安全问题或规范违反。 唯一的标记:1,634 行生产代码横跨 转交维护者进行最终审查。👀 — Qwen Code · qwen3.7-max |
Suggestions — commit
|
| File | Issue | Suggested fix |
|---|---|---|
routes/session.ts:~210 |
resolveRuntimeFromWorkspaceParam (used by GET /workspace/:id/sessions) does NOT check runtime.trusted, unlike resolveRuntimeForSessionCreation and requireSessionRuntime. Untrusted workspace session listing returns 200 instead of 403. |
Add trust check mirroring resolveRuntimeForSessionCreation after resolving the runtime. |
workspace-inputs.ts:71-73 |
rejectDuplicateOrNestedWorkspaceInputs catches canonicalizeWorkspace errors and silently returns, skipping ALL duplicate/nested checks for ALL workspaces. |
Re-throw the canonicalization error, or restructure to validate and canonicalize together. |
routes/session.ts:148-155 |
Single-workspace session creation leaks canonicalization error details (EACCES, ENOENT) to client via sendBridgeError instead of clean workspace_mismatch. |
Send sendWorkspaceMismatch for single-workspace mode too when 'cwd' in body. |
— glm-5.2 via Qwen Code /review
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
wenshao
left a comment
There was a problem hiding this comment.
No blockers found in this changeset. Downgraded from Approve to Comment: CI failing (review-pr, Test (ubuntu-latest, Node 22.x)). Suggestion-level recommendations are in the Suggestion summary comment below.
— qwen3.7-max via Qwen Code /review
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
wenshao
left a comment
There was a problem hiding this comment.
No review findings. Downgraded from Approve to Comment: CI still running.
— GPT-5 via Qwen Code /review
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
No high-confidence issues found. The Phase 2a session routing implementation is well-structured with comprehensive test coverage. A few low-confidence observations were flagged for human review (trust reload behavior, bridge creation duplication, workspace input validation contract) — none are blockers.
— qwen3.7-max via Qwen Code /review
What this PR does
This PR implements the Phase 2a sessions closed loop for
qwen servemulti-workspace mode. Multiple distinct explicit workspaces now boot as registered runtimes with the first workspace kept as primary, while legacy workspace-less APIs and workspace management surfaces remain primary-only.Live session creation and live session routes now resolve the owning workspace runtime before touching a bridge. The dispatched set covers session creation, events, prompt, cancel, session-scoped permission votes, heartbeat, detach, pending prompts, close, basic status, and live-only non-primary workspace session listing.
The daemon advertises
multi_workspace_sessions,workspaces[], and additive session limits only when multiple runtimes are registered. SDK capability types and daemon documentation are updated for the additive schema without adding a workspace-specific SDK client.Why it's needed
Issue #6378 needs a safe multi-workspace daemon path that does not accidentally route non-primary traffic through the primary runtime. This PR opens only the live session loop after the env isolation and total admission guardrails, while keeping persisted storage, file/MCP/settings/voice/channel-worker, ACP, and other legacy surfaces primary-only for later phases.
Reviewer Test Plan
How to verify
Start
qwen servewith two distinct trusted--workspacevalues and confirm/capabilitiesincludesmulti_workspace_sessions, keepsworkspaceCwdas the primary cwd, and lists both runtimes inworkspaces[]. Create a session with an explicit non-primarycwd, then verify events, prompt, cancel, heartbeat, detach, pending prompts, close, andGET /session/:id/statusoperate on that non-primary session. Also verify unknown cwd returnsworkspace_mismatch, untrusted non-primary cwd returnsuntrusted_workspace, and non-primaryGET /workspace/:id/sessionsremains live-only.Local verification run after rebasing onto latest
origin/main:cd packages/cli && npx vitest run src/commands/serve.test.ts src/serve/fast-path.test.ts src/serve/workspace-inputs.test.ts src/serve/run-qwen-serve.test.ts src/serve/multi-workspace-sessions.test.ts src/serve/daemon-status.test.ts src/serve/process-env-guard.test.ts --poolOptions.threads.minThreads=1 --poolOptions.threads.maxThreads=1passed with 276 tests.cd packages/sdk-typescript && npx vitest run test/unit/DaemonClient.test.tspassed with 212 tests.git diff --check HEAD~1..HEADpassed.npm run typecheckwas also attempted after the rebase, but the workspace currently fails on pre-existing/mainline channel and usage-stats type drift outside this PR diff, including missing@qwen-code/channel-wecom, channel-base permission/memory exports, andloadUsageHistoryWithLivefrom core.Evidence (Before & After)
N/A. This is daemon/API/session routing, docs, and SDK type work with no TUI or visual surface change.
Tested on
Environment (optional)
macOS local worktree, Node.js v26.0.0, npm 11.12.1.
Risk & Scope
Linked Issues
Related to #6378.
中文说明
What this PR does
本 PR 实现
qwen servemulti-workspace 模式的 Phase 2a sessions closed loop。多个互不嵌套的显式 workspace 现在会作为已注册 runtime 启动,第一个 workspace 仍保持为 primary,同时 legacy 的无 workspace API 和 workspace 管理面继续保持 primary-only。Live session 创建和 live session 路由现在会先解析所属 workspace runtime,再触碰对应 bridge。分发范围包括 session 创建、events、prompt、cancel、session-scoped permission vote、heartbeat、detach、pending prompts、close、basic status,以及非 primary workspace 的 live-only session listing。
Daemon 只在注册了多个 runtime 时发布
multi_workspace_sessions、workspaces[]和 additive session limits。SDK capability types 和 daemon 文档也更新为 additive schema,但不新增 workspace-specific SDK client。Why it's needed
Issue #6378 需要一个安全的 multi-workspace daemon 路径,避免 non-primary 流量被错误路由到 primary runtime。本 PR 在 env isolation 和 total admission guardrails 之后只打开 live session loop,同时将 persisted storage、file/MCP/settings/voice/channel-worker、ACP 和其他 legacy surfaces 保持 primary-only,留给后续阶段。
Reviewer Test Plan
How to verify
使用两个不同且 trusted 的
--workspace启动qwen serve,确认/capabilities包含multi_workspace_sessions,workspaceCwd仍是 primary cwd,并且workspaces[]列出两个 runtime。使用显式 non-primarycwd创建 session,然后验证 events、prompt、cancel、heartbeat、detach、pending prompts、close 和GET /session/:id/status都作用于该 non-primary session。还需要验证未知 cwd 返回workspace_mismatch,未信任的 non-primary cwd 返回untrusted_workspace,以及 non-primaryGET /workspace/:id/sessions仍然是 live-only。在 rebase 到最新
origin/main后,本地验证:cd packages/cli && npx vitest run src/commands/serve.test.ts src/serve/fast-path.test.ts src/serve/workspace-inputs.test.ts src/serve/run-qwen-serve.test.ts src/serve/multi-workspace-sessions.test.ts src/serve/daemon-status.test.ts src/serve/process-env-guard.test.ts --poolOptions.threads.minThreads=1 --poolOptions.threads.maxThreads=1通过,276 个测试通过。cd packages/sdk-typescript && npx vitest run test/unit/DaemonClient.test.ts通过,212 个测试通过。git diff --check HEAD~1..HEAD通过。rebase 后也尝试了
npm run typecheck,但当前 workspace 会在本 PR diff 之外的既有/mainline channel 和 usage-stats 类型漂移上失败,包括缺失@qwen-code/channel-wecom、channel-base permission/memory exports,以及 core 中的loadUsageHistoryWithLive。Evidence (Before & After)
N/A。本 PR 是 daemon/API/session routing、文档和 SDK 类型变更,没有 TUI 或视觉界面变化。
Tested on
Environment (optional)
macOS 本地 worktree,Node.js v26.0.0,npm 11.12.1。
Risk & Scope
Linked Issues
Related to #6378.