Several backends that are registered and tested were absent from the
user-facing lists: six from the README (pascal, qml, rego, systemverilog,
terraform, vue), six from the Language Support docs page (matlab,
powershell, rego, systemverilog, terraform, toml), and two from the
commented language-server list in the project template
(python_basedpyright, qml).
The drift is long-standing and runs in both directions: matlab,
powershell and toml reached the README but never the docs page; pascal,
vue and qml reached the docs page but never the README; terraform, rego
and systemverilog reached neither, the oldest of them since 2025-07-06.
The two template omissions are the most recent, from #1705 and #1635.
The guide's Documentation step does ask for the README and the docs page,
so most of this was missed rather than unspecified; it does not mention
the project template at all.
The setup notes on the new docs-page entries name the versions Serena
actually pins, taken from the corresponding language-server modules:
MATLAB extension 1.3.9, PowerShell Editor Services 4.4.0 and
PSScriptAnalyzer 1.25.0, Verible v0.0-4051-g9fdb4057, terraform-ls
0.36.5, and Taplo 0.10.0. The PowerShell entry mentions both automatic
installations, since enabling that backend also runs Save-Module against
the user's configured PowerShell repository.
The new Regal link points at open-policy-agent/regal, the current
canonical repository; StyraInc/regal still redirects there, and the
adapter and the CI workflow have not been changed.
The template block is refreshed from scripts/print_language_list.py,
which is why its column widths change: python_basedpyright is longer than
any identifier the previous list contained. The pointer directly below it
still named the Language enum, renamed to LanguageServerId in d2cf18dd.
The previous fix for #1490 depended on four fragments being combined
correctly by hand at the _start() call site: convert_shell_cmd's exec
prefix, the preexec_fn registration, routing through
popen_preserving_pdeathsig, and the Linux/own-session condition.
Nothing enforced their co-occurrence.
Reify "launch a subprocess whose lifecycle is tied to the parent
process (on Linux)" as LanguageServerSubprocessLauncher, a lazily
constructed singleton with a single public method:
launch(process_launch_info: ProcessLaunchInfo, start_new_session: bool) -> Popen[bytes]
Internalizes the libc/prctl preexec_fn, the exec prefixing, the
platform/own-session gate, and the dedicated long-lived spawner
thread (created lazily, only for own-session launches on Linux). The
singleton is required because the spawner thread it owns must be the
one thread in the process performing pdeathsig-registering fork()
calls; launch() takes start_new_session per call so the instance
itself carries no configuration state.
convert_shell_cmd loses the use_exec flag it gained for this fix: its
other callers (solidlsp.language_servers.common, serena.agent) never
used it, and it is invalid on Windows. The exec prefix now lives
entirely inside the launcher, applied to convert_shell_cmd's plain
output, so it can never be requested without the machinery it depends
on.
StdioLanguageServer._start() now calls
LanguageServerSubprocessLauncher.instance().launch(...) instead of
assembling the four fragments itself.
Adapted test/solidlsp/test_pdeathsig.py: the two existing end-to-end
regression tests (short-lived calling thread, SIGKILLed driver) are
unchanged in behavior since they exercise _start()/start() end-to-end
and now transparently go through the new launcher. Added three
targeted tests: convert_shell_cmd's reverted signature, the
launcher's singleton identity, and that its spawner thread is not
created for a non-own-session launch.
tasks are now cancelled on timeout and the executor recovers
Previously, Tool.apply_ex issued its task without a timeout, so a hung tool
would block the TaskExecutor's dispatcher thread forever: the caller received
a TimeoutError after tool_timeout, but no further tasks were ever processed.
Changes:
- Tool.apply_ex now passes the configured tool timeout to issue_task, bounding
the dispatcher's wait as well as the caller's.
- TaskExecutor.Task.result gained cancel_on_timeout (default True): on timeout,
the task is cancelled, preventing execution of tasks still waiting in the
queue and discarding the result of tasks already running.
- Task.wait_until_done no longer takes a timeout parameter; it uses the task's
own timeout and returns whether the task completed. The dispatcher logs a
warning and continues with the next task if the timeout was reached.
- TaskExecutor.execute_task now propagates its timeout to result() instead of
waiting indefinitely, thus also benefiting from cancel-on-timeout.
- Corrected the docstring of Task.result to match the actual semantics.
Tool calls and language server startup dispatch Popen() onto a short-lived
thread (e.g. LanguageServerManager's per-server StartLSThread), which
terminates moments after Popen() returns. PR_SET_PDEATHSIG ties its
registration to that specific calling thread, not the process, so the
language server received SIGTERM and died within milliseconds of a normal
startup on Linux CI, independent of any real SIGKILL scenario.
Route Popen() calls that set preexec_fn=set_pdeathsig_on_parent_exit through
one dedicated, permanently-running daemon thread instead, so the registration
stays tied to a thread that only ever dies alongside the whole process. Added
a regression test that starts a language server on a short-lived thread and
checks it survives past that thread's exit.
find_referencing_symbols/request_references could return incomplete results on
large TypeScript projects. _wait_for_indexing_start_or_completion waits only
_INDEXING_START_GRACE_S (hardcoded 2.0s) for tsserver to *start* emitting
$/progress after opening files; if nothing has appeared by then, it assumes no
indexing is needed and proceeds immediately. On a large project, tsserver can
take longer than that just to resolve the project graph before it even creates
the first progress token, so the first cross-file reference query can race a
project that is still loading.
Every caller of _wait_for_indexing_start_or_completion (the base TypeScript
path and the Svelte companion) always calls expect_indexing() first, so the
"no progress observed" branch is reached specifically when indexing is already
expected; it cannot distinguish "nothing to do" from "not started yet",
because the two look identical from the client for as long as tsserver stays
quiet. That ambiguity cannot be resolved from client-observable signals alone,
so this makes the grace an ls_specific_settings knob (`indexing_start_grace`),
the same way indexing_timeout and server_ready_timeout already are, and raises
the default from 2.0s to 5.0s.
Fixes#1586
On Linux, a SIGKILLed Serena (or an OOM kill, or a crash) leaves its language server process
running: it is launched in its own session so our graceful-shutdown code can clean up the whole
tree, but that code never runs if Serena itself dies uncleanly, and no signal from our process
group reaches a detached session either.
Fix via prctl(PR_SET_PDEATHSIG) at spawn time. Since the process runs under shell=True, the
registration alone only protects the intermediate shell; prefixing the command with `exec` makes
the shell replace itself with the language server so the same PID (and PDEATHSIG registration)
carries through.
Fixes#1490
Bare `*.log` no longer crosses `/` after #1732, so the exclude glob in
the search_files parametrization is anchored to data/ to keep excluding
data/b.log. Matches the repo convention that `**` is required to cross
directory boundaries (see test_search_files_glob_patterns).
Adds first-class support for xAI's Grok Build CLI, following the
existing client-integration pattern (Claude Code, CodeBuddy, Codex):
- grok context: single-project, shaped like claude-code.yml, with the
file-tool exclusions appropriate for a CLI agent that has its own
read/edit/shell tools. structured_tool_output is left at the auto
default (as in the codex and codebuddy contexts); claude-code's
explicit false is a Claude-Code-specific bug workaround that is not
carried over.
- serena setup grok: registers Serena as a user-scoped MCP server via
"grok mcp add" after detecting a compatible grok CLI.
- serena-hooks --client=grok: Grok-native PreToolUse output
({decision, reason}) instead of the Claude-style hookSpecificOutput
envelope; grep/read detection extended to Grok's tool names and
shell payloads (target_file/targetFile path keys, non-string command
values handled defensively). The payload-parsing refinements live in
the remind hook's client-agnostic parsing and apply to all hook
clients: a non-string command value previously raised, and
target_file/targetFile paths were not recognized.
- Docs (030_clients.md #grok), unit tests for setup/hooks/context, and
scripts/live_test_grok.py - a zero-inference, state-preserving live
smoke test against a real grok installation (documented in
CONTRIBUTING.md). Its M5 check verifies the structured-output wire
shape: the grok context (auto default) serves an output schema and
structuredContent for string-returning tools, while the claude-code
context (explicit false workaround) serves plain text only.
Review on #1728 found two blocking issues in the previous commit:
1. pyrefly_server.py wraps send_request with its own 5x/0.2s retry for
RequestCancelled/ContentModified. The base-class retry nested inside
it, so a persistent ContentModified produced 5x5=25 round-trips.
2. The base-class retry applied to every request on every server, but
general.staleRequestSupport.retryOnContentModified in InitializeParams
is the client's contract with the server about exactly which requests
it will reissue. rust-analyzer declares only the three semanticTokens
methods and haskell-language-server declares none; blanket-retrying
e.g. textDocument/hover (or workspace/executeCommand, which is not
idempotent) contradicts that declared capability.
Fix: send_request now only retries a method if it was registered via
the new LanguageServerInterface.set_content_modified_retry_methods,
which SolidLanguageServer._create_initialize_params populates from
each server's own general.staleRequestSupport.retryOnContentModified
list right after building it. The mechanism is opt-in per method, per
server, and mirrors what we actually told the server -- so it's a
no-op wherever nothing is declared.
rust-analyzer now declares textDocument/hover in that list (the method
issue #1724's repro -- test_find_symbol[rust_add_function] with
include_info=True -- actually needs), in addition to the pre-existing
semanticTokens methods.
This also resolves the pyrefly double-retry: pyrefly never declares
general.staleRequestSupport, so the base-class retry is a no-op for
every pyrefly request and pyrefly's own wrapper remains the sole
handler for both RequestCancelled and ContentModified there, exactly
as before this PR. pyrefly_server.py is intentionally left untouched;
narrowing its retry tuple, as literally suggested, would have removed
ContentModified handling for it entirely once the base layer stopped
covering it too.
Also: reduced _CONTENT_MODIFIED_MAX_ATTEMPTS from 5 to 3 (now the max
per allowlisted method only, so worst-case added latency for hover is
2 * request_timeout + 0.4s instead of 4 * request_timeout + 0.8s), and
restructured send_request around a _send_request_once helper so the
final failed attempt raises directly instead of falling through to a
trailing `raise AssertionError("unreachable")`. Retry tests now
monkeypatch time.sleep and gained coverage for the allowlist gate
(undeclared method, executeCommand, default-empty registration).
Fixes#1724
Per the LSP spec, ContentModified means the server discarded a stale,
in-flight computation because the workspace changed underneath it, not
that the request itself is invalid, so a well-behaved client is
expected to retry. LanguageServerInterface.send_request previously
converted any such response straight into a hard SolidLSPException,
with no retry, for every language server.
This was flaking test_find_symbol[rust_add_function] on windows-latest:
rust-analyzer returns ContentModified for hover requests cancelled by
concurrent indexing (its internal retry set does not cover hover), and
nothing on the client side retried it.
send_request now retries ContentModified responses a bounded number of
times with a short delay before giving up. This is generic to the base
request path, so it applies to every language server, not just
rust-analyzer. RequestCancelled (-32800) is left alone since the LSP
spec defines it as client-initiated.
Fixes#1724
causing some symbolic operations (such as `find_referencing_symbols`) to report stale information.
An explicit file system synchronisation mechanism is now used to detect changes prior to the
affected tool executions.
Note that tools like `find_symbol` and `get_symbols_overview` do not need the synchronisation,
because they explicitly open all the relevant files themselves.
Resolves#1718
Co-authored-by: Niccolò Franzin <niccolo@cognitivext.com>
- PHP/Intelephense: expose `file_filter` via `ls_specific_settings["php"]`, so that additional
extensions containing PHP sources (e.g. Drupal's `.module` / `.install` / `.inc` / `.theme`)
become visible to the symbol tools and are indexed by the language server
- PHP: treat `.phtml` files as PHP sources by default (all PHP language servers)