Files
Sergey BessonovandShaojin Wen 0e92f135c9 feat(cli): enable LSP through a user-scoped setting and env pair (#14613)
* feat(cli): enable LSP through a user-scoped setting and env pair

The LSP service ships already working inside the daemon, but the flag was the only entrance, and the clients that need code intelligence most are exactly the ones that cannot pass it: the Desktop client pins the daemon's argument list, as does any host embedding the Web Shell. #14507 asked for an opt-in channel rather than general availability, and the triage named the model to follow, so the setting arrives with both halves of that model's safety pairing: a user/system-scoped key, and an environment pair in which the disable switch wins over the enable variable and over the flag.

Workspace scope is deliberately excluded and a workspace value is stripped before the merge with a warning, because enabling LSP lets a session start language servers with the user's own permissions; a project environment file can neither opt in nor clear the kill switch. Enablement is read at startup only, matching the design decision that rules out toggling the service mid-session.

The daemon now resolves the same question per workspace runtime, from the settings and environment it actually gives that runtime, rather than only from the arguments it forwards. Forwarding was reachable from one of the daemon's two start-up paths and not the other, so a settings-enabled daemon could certify a session as compatible with a paired managed engine host while that session really did spawn language servers under the user's permissions.

An embedding host can also refuse the service for a configuration it owns and never uses it for. Passing the flag as off stopped expressing a refusal as soon as a second entrance existed, so the refusal is now a runtime-only policy: three internal configurations that exist only to enumerate MCP servers, run one hosted model turn, or run one hook operation no longer each start - and never stop - a full set of language servers.

Refs #14507

* fix(cli): guard every host-owned config against LSP

Close the gaps the first review round found in the settings entrance: the pre-sandbox auth config and the hosted workflow turn now refuse LSP like their siblings, safe mode refuses the setting entrance, and the daemon recomputes its per-session answer on settings reload instead of freezing it at boot.

Also publish the Web Shell alias and zh-CN strings the panel requires, group the row under Experimental while keeping the tools.lspEnabled key, and pin each guard against removal in tests.

* docs: record the LSP enablement contract

The precedence, the host-refusal policy and the list of configs that owe lspDisabled lived only in code comments and a PR thread. Add the bilingual design record, correct the user docs that still called the flag the only switch, and align the managed-engine spec tables with the non-argv trigger.

* fix(cli): resolve LSP compatibility from the evaluated snapshot

The cached per-runtime boolean went stale on every path that did not reload: a settings write (the panel toggle this PR exists for) reached no recompute, a model-provider reload replaced the environment without one, and caught read failures narrowed the inputs it was derived from.

Apply the resolver inside evaluateManagedCompatibility instead, to the settings snapshot and effective environment it already reads, with the forwarded flag as the argv entrance. Nothing is cached, so a write, an env swap or a failed read cannot certify a session that starts language servers.

* fix(core): block shell and npm loader vectors in LSP env

npm maps npm_config_node_options onto the child's NODE_OPTIONS, so blocking NODE_OPTIONS alone left the same preload reachable through a workspace .lsp.json env entry; BASH_ENV and ENV are the shell-startup equivalents. Add all three to the LSP denylist and pin them in the existing buildProcessEnv case.

* docs: correct LSP enablement wording after review

Name both trust gates (folder trust and an IDE Restricted Mode report), the execution-sandbox refusal with its escape hatches, the provenance limit on the env pair, and safe mode in the LSP and safe-mode lists. Fix the Experimental-group note so it no longer claims a false uniqueness, and align the design validation section with what the tests actually witness.

* docs(cli): trim the LSP setting description

The panel row had grown into a paragraph. Keep what a user decides on - what the setting does, the trust gate, the execution-sandbox conflict and the env precedence - and leave the re-evaluated IDE gate and the debug hint to the LSP page, where troubleshooting already lives.

* docs: point the /lsp hints at the setting and add C# setup

The not-enabled hint and the LSP page's debug/FAQ examples still told users to pass --experimental-lsp, which the hosts this change exists for cannot do. Name the setting first and keep the flag as the alternative, and add a Roslyn language-server row plus a .lsp.json example to the prerequisites.

* docs: document the Windows cmd shim for the Roslyn server

Qwen Code starts LSP servers without a shell, so the global dotnet tool's .cmd shim under C:\Users\heatw\.dotnet\tools cannot be spawned directly: a bare name is found by where but fails with command not found. Show the cmd /c form and the absolute-executable alternative next to the POSIX example.

* test(cli): pin the bare-mode LSP refusal

* fix(cli): refuse LSP in two more host-owned Configs

* fix(cli): read the LSP env pair as a spawned process sees it

* docs: record the new guarded Configs and drop the fixed teardown follow-up

* docs: describe explicit folder trust for workspace LSP servers

---------

Co-authored-by: Shaojin Wen <[email protected]>
2026-11-08 02:48:42 +00:00
..