feat(serve): Add remote LSP status route#5741
Conversation
Co-authored-by: Qwen-Coder <[email protected]>
Co-authored-by: Qwen-Coder <[email protected]>
|
Thanks for the PR, @doudouOUC! Template looks good ✓ — all required sections present, bilingual, N/A appropriately used for the evidence section on this non-UI change. On direction: This directly addresses one of the five tracked ACP gaps in issue #5677 ( On approach: The scope feels right. The change follows the established session-status pattern (REST route → ACP method → capability tag → SDK helper), mirrors existing routes like One thing worth noting: Moving on to code review. 🔍 中文说明感谢贡献,@doudouOUC! 模板完整 ✓ — 所有必填章节齐全,非 UI 变更合理使用 N/A。 方向: 直接对应 issue #5677 中跟踪的五个 ACP 缺口之一( 方案: 范围合理。变更遵循已有的 session-status 模式(REST route → ACP method → capability tag → SDK helper),与 值得注意的一点: 进入代码审查 🔍 — Qwen Code · qwen3.7-max |
Code ReviewThe implementation follows established patterns well — REST route, ACP method, capability tag, SDK helper, sanitization. The diff is clean and focused. One critical bug found: 🐛 Everything else looks correct: sanitization strips pid/args/stderr/rootUri/workspaceFolder, session ownership is enforced, disabled/unavailable states return HTTP 200 with proper payload shape. Real-Scenario TestingAttempt 1:
|
|
This is a well-structured PR that follows the project's established patterns for session-status endpoints. The REST route, ACP methods, capability tag, SDK helpers, and sanitization are all implemented consistently with existing routes like The one issue is real though: Requesting changes for that one fix. Everything else looks ready. 中文说明这是一个结构良好的 PR,遵循了项目中 session-status 端点的既有模式。REST route、ACP methods、capability tag、SDK helpers 和清洗逻辑都与 唯一的问题是 请求修改这一处。其他部分都已就绪。 — Qwen Code · qwen3.7-max |
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
Missing yargs builder registration for --experimental-lsp — the flag is rejected as unknown at runtime due to .strict() in the production CLI. The unit test doesn't catch this because buildParser() lacks .strict(). Fix: add .option('experimental-lsp', { type: 'boolean', default: false, description: '...' }) to the builder chain in serve.ts. Everything else looks good. 🙏
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
Summary
This PR adds a well-structured, read-only remote LSP status surface across REST, ACP HTTP/WS, and the TypeScript SDK. The sanitization boundary correctly strips debug internals via an explicit allowlist, tests cover the key paths (enabled, disabled, unavailable), and the implementation closely follows established patterns from existing status routes.
2 findings reported, 2 confirmed after verification (1 high-confidence, 1 low-confidence).
Findings
Suggestion
- File:
packages/cli/src/serve/acp-http/dispatch.ts:99 - Source: [review]
- Issue: The JSON-RPC method is named
_qwen/session/lsp_status, but the REST path is/session/:id/lspand the ext method isqwen/status/session/lsp. Every other session-status route uses matching suffixes across all three layers (e.g.,tasks/tasks/tasks,context/context/context). LSP is the only route where the JSON-RPC suffix (lsp_status) diverges from both REST (lsp) and ext (lsp). - Impact: Future developers adding a new session status route will see two contradictory precedents. SDK clients using the JSON-RPC transport directly must call
_qwen/session/lsp_statuswhile REST clients call/session/:id/lsp— the asymmetry is unique to this route. - Suggested fix: Rename
_qwen/session/lsp_status→_qwen/session/lspinALL_QWEN_VENDOR_METHODS, the dispatchcasearm,WS_READ_METHODS, andacpRouteTable.ts. Update corresponding test assertions.
Needs Human Review
Possibly:
- File:
packages/cli/src/serve/server.test.ts— The existing "maps missing sessions on read-only session routes to 404" test coverscontext,supported-commands, andtasksbut was not extended to include the new/session/:id/lsproute. Adding a 404 assertion for the LSP route would close the gap.
Verdict
Comment — One naming inconsistency worth aligning before merge; no blockers.
— qwen3.7-max via Qwen Code /review
Co-authored-by: Qwen-Coder <[email protected]>
wenshao
left a comment
There was a problem hiding this comment.
Independent review of the remote LSP status route. The implementation is clean and consistent with the sibling session-status routes: per-server sanitization strips debug internals (args, pid, stderrTail, rootUri, workspaceFolder) behind an explicit allowlist, the core LspStatusSnapshot ↔ bridge ↔ SDK types line up, and the REST / ACP HTTP+WS / SDK wiring plus tests are thorough.
No new issues surfaced beyond the threads already open on this PR. I did independently reproduce and confirm the existing --experimental-lsp registration blocker is real (the strict serve parser rejects the flag, and CI stays green only because the unit test's parser isn't strict), so that remains the one item to address before merge — deferring to the existing thread for the fix.
— claude-opus-4-8[1m] via Qwen Code /qreview
wenshao
left a comment
There was a problem hiding this comment.
I did not find any additional issues beyond the existing requested change. The --experimental-lsp blocker is still reproducible on the built CLI: node packages/cli/dist/index.js serve --experimental-lsp --no-web --port 0 exits with Unknown arguments: experimental-lsp, experimentalLsp, so the serve command still needs to register the flag before this can merge.
— GPT-5 Codex via Qwen Code /review
Co-authored-by: Qwen-Coder <[email protected]>
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
No review findings. Downgraded from Approve to Comment: CI failing: Test (windows-latest, Node 22.x).
Implementation is clean and well-structured. The sanitization allowlist correctly strips debug internals, all 1421 tests pass, and the new routes follow established patterns for session-status endpoints.
— qwen3.7-max via Qwen Code /review
|
@qwen-code /triage |
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
No review findings. The implementation is clean and well-structured — REST route, ACP HTTP/WS methods, capability tag, SDK helpers, sanitization, and documentation all follow established patterns consistently across 35 files. Downgraded from Approve to Comment: CI failing (Test (windows-latest, Node 22.x)).
— qwen3.7-max via Qwen Code /review
chiga0
left a comment
There was a problem hiding this comment.
Overview
Final Verdict: Approve — Clean, well-structured addition of a read-only LSP status surface across REST, ACP HTTP/WS, bridge, and SDK. Follows the established pattern of existing status routes (tasks, stats, context). Sanitization is correctly scoped to stable client-facing fields.
Cross-Validation
| # | Finding | Reviewer | My Assessment |
|---|---|---|---|
| C1 | Missing yargs builder registration for --experimental-lsp (strict parser rejects) |
qwen-code-ci-bot, wenshao | Fixed at HEAD — serve.ts now has .option('experimental-lsp', ...) in the builder chain |
| C2 | Naming inconsistency _qwen/session/lsp_status vs REST /lsp |
qwen-code-ci-bot | Fixed at HEAD — commit 1596d06 renamed to session_lsp / _qwen/session/lsp consistently |
| C3 | Missing error-state sanitization test | wenshao | Fixed at HEAD — commit abf9992 added 'includes error field for FAILED server while stripping debug internals' test |
No unique Critical or Major findings from my independent review.
Additional Audit Coverage
Areas I independently verified beyond existing findings:
- Sanitization completeness:
buildSessionLspStatusinacpAgent.tsmaps snapshot → response, only includingname,status,languages,transport,command,error. Debug internals (pids, args, workspace folders, root URIs, stderr tails) are correctly excluded. Conditional spreads (...(server.error ? { error } : {})) correctly omit fields when absent. - Route consistency: REST
GET /session/:id/lsp, ACP vendor_qwen/session/lsp, WS read method, status ext methodqwen/status/session/lsp— all consistently named and wired.requireOwnedcheck present in ACP dispatcher before processing. - Type parity:
DaemonSessionLspStatus(SDK) andServeSessionLspStatus(bridge) are structurally identical with matching field names and types. Separate definitions follow existing codebase convention. extraArgsgenerically threaded:spawnChannel.tsspreadsextraArgsafter--acp, correctly forwarding--experimental-lspto child processes. The mechanism is generic and reusable.- Capability versioning:
session_lsp: { since: 'v1' }correctly registered inSERVE_CAPABILITY_REGISTRY.
Minor Observations
- Type literal difference:
ServeSessionLspStatus.vusestypeof STATUS_SCHEMA_VERSION(literal type) whileDaemonSessionLspStatus.vuses1(numeric literal). Functionally equivalent but stylistically inconsistent. Non-blocking. - Trailing semicolon:
buildSessionLspStatusmethod has};(semicolon after method definition) — valid TS but inconsistent with other method definitions in the class. Non-blocking.
This review was generated by QoderWork AI
|
Thanks for the PR, @doudouOUC! Template looks good ✓ — all required sections present, bilingual, N/A correctly used for the evidence section since this is a non-UI API change. On direction: well-aligned. Issue #5677 tracks the ACP gap for remote status commands, and On approach: scope feels right. REST route, ACP HTTP/WS methods, capability tag, SDK helpers, sanitization boundary, docs — each file in the diff serves the stated goal. The Moving on to code review. 🔍 中文说明感谢贡献! 模板完整 ✓ — 所有必需部分齐全,双语,非 UI 变更的 Evidence 部分正确使用 N/A。 方向:对齐。Issue #5677 跟踪远端状态命令的 ACP 缺口, 方案:范围合理。REST route、ACP HTTP/WS methods、capability tag、SDK helpers、sanitization 边界、文档 — diff 中每个文件都服务于目标。 进入代码审查 🔍 — Qwen Code · qwen3.7-max |
Code ReviewThe implementation is clean and consistent across all surfaces. The The new types ( No correctness bugs, security holes, or structural violations found. The Test ResultsAll focused test suites pass:
Build: ✅ 0 errors (15 pre-existing warnings). Typecheck: ✅ clean. CI: ✅ all green (macOS, Linux, Windows, lint, integration tests). Real-Scenario Testing (tmux)Booted the daemon from PR code via Capabilities endpoint confirms LSP endpoint returns correct 404 for non-existent sessions: Daemon log confirms the route is handled: (Note: a full session-attached LSP status test requires an active ACP session with model configuration, which is not feasible in a sandboxed CI runner without API keys. The unit tests cover the enabled/disabled/unavailable/sanitization paths comprehensively.) — Qwen Code · qwen3.7-max |
|
This is a clean, well-tested PR that fills a real gap (issue #5677) — daemon/ACP clients can now get structured LSP status instead of relying on Markdown from the local The implementation follows the established session-status pattern exactly: REST route, ACP methods (HTTP + WS), capability tag, SDK types + helpers, sanitization boundary. The The scope is proportional to the goal — no drive-by refactors, no speculative features. The Approval guardrail check: this is a Approving. ✅ 中文说明这是一个干净、测试充分的 PR,填补了真实缺口(issue #5677)— daemon/ACP 客户端现在可以获取结构化 LSP 状态,而无需依赖本地 实现完全遵循已有的 session-status 模式:REST route、ACP methods(HTTP + WS)、capability tag、SDK types + helpers、sanitization 边界。 范围与目标相称 — 无附带重构,无投机功能。 审批护栏检查:这是 批准 ✅ — Qwen Code · qwen3.7-max |
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
One doc fix needed: capability tag is session_lsp in code but session_lsp_status in 5 doc places (08-session-lifecycle.md, 11-capabilities-versioning.md, qwen-serve-protocol.md). Quick find-and-replace, then this is ready. See review comments above for details. 🙏
Maintainer verification — local real-binary + tmux E2E ✅I built this PR from a clean worktree at the PR head ( Environment
1. Build / typecheck / tests — all green
*The single failure was
2. Real tmux E2EDaemon A — // GET /session/:id/lsp → HTTP 200
{"v":1,"enabled":true,"configuredServers":1,"readyServers":1,
"failedServers":0,"inProgressServers":0,"notStartedServers":0,
"servers":[{"name":"typescript-language-server","status":"READY",
"languages":["typescript"],"transport":"stdio",
"command":"typescript-language-server"}]}Sanitization proven against a live server: the actual process exists with a real PID and — yet the response leaks none of Daemon B — // GET /session/:id/lsp → HTTP 200 (NOT an error)
{"v":1,"enabled":false,"configuredServers":0,"readyServers":0,"servers":[]}Unknown session → HTTP End-to-end opt-in chain confirmed working: 3.
|
| 检查项 | 结果 |
|---|---|
npm run build(根) |
✅ exit 0(仅 vscode-ide-companion 既有 lint warning,0 error) |
npm run typecheck(根) |
✅ exit 0 |
acp-bridge:bridge.test.ts + spawnChannel.test.ts |
✅ 320 passed |
sdk-typescript:计划中的 4 个单测文件 |
✅ 273 passed |
cli:计划中的 5 个文件 |
✅ 828 passed,1 个 flake* |
*唯一失败的是 server.test.ts › bearer auth › is open by default,报 Error: socket hang up —— 这是 supertest 的瞬时 socket flake,与本 PR 无关(它不碰 auth 代码),单独重跑即通过。所有 LSP 相关用例均通过(按名字逐一核实):
acpAgent.test.ts:disabled / unavailable / READY-带脱敏 /FAILED server while stripping debug internals✅transport.test.ts:initialize advertises _qwen/session/lsp、_qwen/session/lsp returns status✅serve.test.ts/run-qwen-serve.test.ts:--experimental-lsp解析 + spawn opt-in ✅
2. 真实 tmux 端到端
Daemon A —— qwen serve --experimental-lsp + 真实 typescript-language-server:
// GET /session/:id/lsp → HTTP 200
{"v":1,"enabled":true,"configuredServers":1,"readyServers":1,
"failedServers":0,"inProgressServers":0,"notStartedServers":0,
"servers":[{"name":"typescript-language-server","status":"READY",
"languages":["typescript"],"transport":"stdio",
"command":"typescript-language-server"}]}脱敏对照活的 server 得到证明:真实进程确实存在,有真实 PID 和 --stdio 参数 ——
52826 node .../typescript-language-server --stdio
—— 但响应没有泄漏任何 pid / args / stderrTail / rootUri / workspaceFolder / exitCode / exitSignal / warmedUp / restartAttempts。server 条目的字段恰好是 name, status, languages, transport, command。buildSessionLspStatus 用的是白名单映射,所以脱敏是结构性的(未来 core 新增的 debug 字段也不会泄漏)。
Daemon B —— qwen serve(不带 --experimental-lsp):
// GET /session/:id/lsp → HTTP 200(不是 error)
{"v":1,"enabled":false,"configuredServers":0,"readyServers":0,"servers":[]}未知 session → HTTP 404 {"error":"No session with id ...","sessionId":"..."}。✅
端到端 opt-in 链路确认可用:serve --experimental-lsp → extraArgs:['--experimental-lsp'] → child 以 qwen --acp --experimental-lsp 启动 → 主 CLI config.ts(lspEnabled = !bareMode && argv.experimentalLsp) → getLspStatusSnapshot() 返回真实快照。getLspStatusSnapshot() 在 main 的 core 中已存在,所以本 PR 不动 packages/core 是正确的。
3. ⚠️ 发现项(纯文档):capability tag 名称不一致 —— session_lsp_status vs session_lsp
代码、测试、以及运行中的 daemon 用的都是 session_lsp(最后一个 commit f6453e4a9 特意把测试基线改成了 session_lsp)。但文档里仍有 5 处写成 session_lsp_status。来自 GET /capabilities 的 runtime 证据:
has session_lsp : true
has session_lsp_status : false
按文档去 pre-flight session_lsp_status 的客户端(协议文档明确要求:"pre-flight this tag before exposing remote LSP status")将永远发现不了这个能力,因为 daemon 广告的是 session_lsp。滞后位置:
docs/developers/daemon/08-session-lifecycle.md:227(标题)和:259(capability-tags 列表)docs/developers/daemon/11-capabilities-versioning.md:92("67 tags" 的 Sessions 列表)docs/developers/qwen-serve-protocol.md:132(features数组字面量)和:162(正文)
建议: 把这 5 处 session_lsp_status 改成 session_lsp,以符合注册表/路由命名惯例(/tasks→session_tasks、/stats→session_stats,故 /lsp→session_lsp)。纯文档、不影响 runtime,但它破坏了面向 SDK 使用者的能力发现契约,建议并入本 PR 一起修。
结论
功能正确、测试充分、脱敏在真实 language server 下成立。唯一待办是上面这 5 行文档 tag 名称修正。修掉即可合并。👍
|
[qwen] Fixed in 5cb3a9d: renamed all 5 |
|
@qwen-code /triage |
chiga0
left a comment
There was a problem hiding this comment.
Re-Review at HEAD 5cb3a9d
One new commit since last review — docs-only follow-up.
| Change | Assessment |
|---|---|
5cb3a9d — rename session_lsp_status → session_lsp in docs |
✓ Clean string replacement across 3 doc files, matching the code rename from 1596d06 |
No new issues. All previous findings remain resolved.
Verdict: LGTM
This review was generated by QoderWork AI
|
@qwen-code /triage |
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
LGTM, looks ready to ship. ✅
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
No review findings. Downgraded from Approve to Comment: CI still running.
Implementation is clean and well-structured — sanitization allowlist correctly strips debug internals, all 1,422 focused tests pass, and the new REST/ACP/WS/SDK routes follow established patterns consistently across 35 files.
— qwen3.7-max via Qwen Code /review
What this PR does
This PR adds a structured, read-only remote LSP status surface for daemon and ACP clients. Sessions can now expose sanitized per-session LSP details through REST, ACP HTTP/WS, the ACP child status extension, and the TypeScript SDK without changing the existing slash-command
/lspMarkdown output.The new status response reports whether LSP is enabled, aggregate server counts, unavailable/initialization states, and a sanitized per-server list containing only stable client-facing fields.
Why it's needed
Issue #5677 tracks ACP gaps for remote status commands.
/lspalready has local interactive and non-interactive behavior, but daemon/ACP clients only had coarse status from/statusor/about, and adding ACP support directly to/lspwould expose presentation-oriented Markdown rather than a stable schema.A structured route gives remote clients a typed capability, SDK helper, and versioned response shape while keeping LSP startup opt-in for spawned ACP children.
Reviewer Test Plan
Reviewers should confirm that remote clients can request LSP status for an owned session, that disabled or unavailable LSP is represented as a successful status payload rather than an HTTP error, and that the response does not expose debug-only LSP internals such as process ids, args, workspace folders, root URIs, or stderr tails.
How to verify
Run the focused bridge tests with
cd packages/acp-bridge && npx vitest run src/bridge.test.ts src/spawnChannel.test.tsand expect the LSP status request to travel over the existing ACP channel and optional child args to be appended after--acp.Run the focused CLI tests with
cd packages/cli && npx vitest run src/acp-integration/acpAgent.test.ts src/serve/server.test.ts src/serve/acp-http/transport.test.ts src/commands/serve.test.ts src/serve/run-qwen-serve.test.tsor the focused LSP test names and expect REST, ACP HTTP/WS, capability, disabled/unavailable, sanitization, and--experimental-lspopt-in behavior to pass.Run the focused SDK tests with
cd packages/sdk-typescript && npx vitest run test/unit/DaemonClient.test.ts test/unit/DaemonSessionClient.test.ts test/unit/acpRouteTable.test.ts test/unit/daemon-public-surface.test.tsand expect the route mapping, helper forwarding, public exports, and client-id forwarding to pass.Run
npm run buildandnpm run typecheckfrom the repository root and expect both to complete successfully.Evidence (Before & After)
N/A. This is a non-UI daemon/ACP/SDK API change.
Tested on
Environment (optional)
Node.js v22.22.3, npm 11.12.1, local macOS worktree.
Risk & Scope
/lspoutput, and end-to-end tests against real language servers are intentionally out of scope.Linked Issues
Refs #5677
中文说明
What this PR does
这个 PR 为 daemon 和 ACP 客户端新增结构化、只读的远端 LSP 状态面。Session 现在可以通过 REST、ACP HTTP/WS、ACP child status extension 和 TypeScript SDK 暴露经过清洗的 per-session LSP 详情,同时不改变现有 slash command
/lsp的 Markdown 输出。新的状态响应会报告 LSP 是否启用、server 聚合计数、unavailable/initialization 状态,以及只包含稳定客户端字段的 per-server 清单。
Why it's needed
Issue #5677 跟踪远端 ACP 状态命令的缺口。
/lsp已经支持本地 interactive 和 non-interactive 行为,但 daemon/ACP 客户端之前只能从/status或/about得到粗粒度信息;如果直接给/lsp增加 ACP 支持,会暴露面向展示的 Markdown,而不是稳定 schema。结构化 route 可以给远端客户端提供 typed capability、SDK helper 和带版本的响应形状,同时保持 spawned ACP child 的 LSP 启动仍然是 opt-in。
Reviewer Test Plan
Reviewer 应确认远端客户端能为已拥有的 session 请求 LSP 状态,disabled 或 unavailable LSP 会以成功状态 payload 表示而不是 HTTP error,并且响应不会暴露 process id、args、workspace folder、root URI、stderr tail 等仅用于调试的 LSP 内部字段。
How to verify
运行 focused bridge 测试:
cd packages/acp-bridge && npx vitest run src/bridge.test.ts src/spawnChannel.test.ts,预期 LSP status request 通过现有 ACP channel 传递,并且可选 child args 会追加在--acp后面。运行 focused CLI 测试:
cd packages/cli && npx vitest run src/acp-integration/acpAgent.test.ts src/serve/server.test.ts src/serve/acp-http/transport.test.ts src/commands/serve.test.ts src/serve/run-qwen-serve.test.ts,或运行对应的 focused LSP test names,预期 REST、ACP HTTP/WS、capability、disabled/unavailable、sanitization 和--experimental-lspopt-in 行为都通过。运行 focused SDK 测试:
cd packages/sdk-typescript && npx vitest run test/unit/DaemonClient.test.ts test/unit/DaemonSessionClient.test.ts test/unit/acpRouteTable.test.ts test/unit/daemon-public-surface.test.ts,预期 route mapping、helper forwarding、public exports 和 client-id forwarding 都通过。在仓库根目录运行
npm run build和npm run typecheck,预期两者都成功完成。Evidence (Before & After)
N/A。这是非 UI 的 daemon/ACP/SDK API 变更。
Tested on
Environment (optional)
Node.js v22.22.3,npm 11.12.1,本地 macOS worktree。
Risk & Scope
/lsp输出,以及针对真实 language server 的端到端测试都明确不在范围内。Linked Issues
Refs #5677