Waited briefly for process exit publication after clean stdout EOF so the process handler preserves the real exit code and stderr, while genuine reader errors still tear down immediately.
Cleared only the matching initialization failure for explicit reloads and added regressions for quick exits, reader errors, ordinary backoff, and immediate reload retries.
Fixes#7041
(cherry picked from commit a76522b759f14421202d4cc437ec611b78be20d1)
Added an optional per-server languageId override and used it for both disk-backed and in-memory didOpen notifications.
Covered config loading and both document-open paths with a fake custom GDScript server.
Fixes#6800
Codex P2 on #6403: sendRequest rejected with only the server's error
message; a server answering rust-analyzer/reloadWorkspace with code
-32601 but nonstandard text (e.g. "Unknown request") would fail
isMethodNotFoundError and turn lsp reload into a hard error instead of
falling back to the generic reload. Include the code in the rejection
message so the existing -32601 substring check matches. Adds a
regression test with a -32601/"Unknown request" response.
reloadServer caught every error from both fallback mechanisms in bare
catch blocks, so a caller cancel or tool timeout was swallowed and fell
through to `proc.kill(); return "Restarted"` -- reporting a successful
restart while killing the server with no replacement.
- Propagate ToolAbortError/timeout from both the rust-analyzer request
and the didChangeConfiguration notification fallback.
- Gate the fallback on genuine method-not-found via isMethodNotFoundError
instead of any error.
- Replace the blind proc.kill with shutdownClientInstance: remove the
client from the registry by identity and await confirmed process exit,
surfacing a truthful teardown error when the process outlives the kill.
Fixes#6369
clampTimeout resolved the per-tool default (bash 300s) whenever the agent
omitted `timeout` and only enforced the tool's own min/max, so the
tools.maxTimeout global ceiling — applied solely in sdk.ts on explicitly
numeric args — was bypassed on the common default-fallback path.
Thread maxTimeout into clampTimeout so the resolved effective timeout,
including the default path, is capped before the per-tool floor/ceiling
apply. Explicit values below the cap still win; maxTimeout <= 0 stays
no-cap. Applied at every call site (bash, eval, browser, debug, lsp,
fetch, and the session-level bash executor), and the bash clamp notice
now names the global ceiling when it is the binding limit.
Fixes#6294
Raced document diagnostic pulls against the push polling interval so completed full reports return without waiting for the deadline.
Covered inline edit writethrough delivery for an immediate pull-only response.
Fixes#5825
Advertised document diagnostic support and tracked static and dynamic server capabilities.
Pulled full reports within the existing diagnostics wait window while preserving push precedence.
Fixes#5825
Answered unconfigured workspace/configuration sections with the spec-required null value and covered the Roslyn post-initialization pull path.
Fixes#5745
Sent workspace/didChangeConfiguration after initialized so servers waiting for client configuration finish startup before semantic requests.
Added a fake-LSP regression covering notification order and settings payload.
Fixes#5276
- Offloaded slow LSP diagnostics to a deferred channel in the `write` tool to prevent blocking agent execution for the full 3-second poll window.
- Abstracted deferred diagnostic logic into a reusable `DeferredDiagnostics` class to standardize tracking and deduplication across tools.
- Updated `WriteTool` to support `beginDeferredDiagnosticsForPath` callbacks, surfacing diagnostics as an aside instead of stalling the tool result.
- Added a regression test validating that `write` completes immediately while diagnostics arrive asynchronously.
- Kept missing language-specific adapters from falling through to native debuggers.
- Resolved nested launch roots before session-local binaries and PATH, including explicit adapters and go.work workspaces.
- Added actionable install/configuration errors and deterministic regression coverage.
Fixes#5037
Detected go.work before go.mod for workspace diagnostics and expanded go build package patterns from go.work use entries.
Added regression coverage for go.work-only roots and mixed go.work/go.mod workspaces.
Fixes#5038
- Implemented a 2-second timeout for `workspace/didChangeWatchedFiles` notifications to prevent agent stalls during server congestion.
- Updated `runLspWritethrough` to gracefully reset notification state when the internal watchdog triggers but the primary operation remains active.
- Added explicit abort signal checks in `acp-bridge` to prevent initiation of file writes after tool cancellation.
- Refactored `notifyWorkspaceWatchedFiles` to use `AbortSignal.any` for unified signaling across timeout and caller-provided triggers.
Watched-file notifications sent from the LSP writethrough now use the same bounded operation signal as sync/save notifications, so a stalled server cannot hang the write after disk commit.
Extended the freshness regression to assert a bounded signal reaches watched-file notification sends.
Refs #4459
Announced harness-authored create, change, and delete operations to active LSP clients with workspace/didChangeWatchedFiles before edit-time diagnostics are read.
Added regression coverage for non-LSP sibling files in a batched write so diagnostics see the workspace state the harness just produced.
Fixes#4459
Collapsed duplicate byte-identical non-empty LSP text edits before overlap validation so rename_file accepts idempotent server output while still rejecting real conflicts.
Fixes#4458
Filtered TypeScript project-resolution diagnostics for files with no root marker ancestor while preserving syntax diagnostics. Added regression coverage for Bun scratch files outside project roots.\n\nFixes #4401
The postmortem SIGTERM/SIGHUP/uncaughtException handlers only ran the registered
cleanup callback list before process.exit, and the only session-related callback
was session-manager-flush. So a real kernel signal (terminal close, process
manager killing omp, IDE stop) skipped saveDraft, session.dispose (session_shutdown
emit, owned async job disposal, kernel disposal, MCP disconnect, browser tab
release), and violated the SessionShutdownEvent docstring contract that promises
delivery on SIGINT/SIGTERM. The LSP client also owned its own SIGINT/SIGTERM
handlers that called shutdownAll then process.exit(0), which could race postmortem's
async runCleanup and short-circuit the session teardown.
- Extracted a promise-memoized createSessionTeardown helper (modes/session-teardown.ts)
that snapshots the editor draft, persists it via sessionManager.saveDraft, then
invokes session.dispose. A saveDraft failure is logged but never aborts disposal.
- Memoized AgentSession.dispose so the keypress path and the signal path share one
settled promise and cannot double-emit session_shutdown or double-drain the owned
AsyncJobManager.
- Registered the teardown on postmortem as "session-teardown" in InteractiveMode.init,
replacing the narrower session-manager-flush callback. InteractiveMode.shutdown
now delegates the draft+dispose steps to the same helper.
- Replaced the LSP client's SIGINT/SIGTERM handlers with a "lsp-shutdown" postmortem
callback so LSP cleanup runs alongside every other session teardown instead of
racing them via process.exit(0). beforeExit is unchanged.
- Added session-teardown.test.ts covering: draft-then-dispose ordering, disposal
after saveDraft rejects, empty-string clears stale sidecar, promise memoization
under concurrent invocation, and snapshot-at-first-call semantics.
Fixes#4080
Distinguish aborts that race an active sink.flush() from aborts that happen
before a queued write starts. Only the former leaves the sink flush pending
and requires killing/evicting the LSP client; pre-write aborts should reject
that caller without disrupting unrelated in-flight operations.
Add a regression with one notification blocked in flush and a second queued
notification whose signal aborts before its write starts, asserting the shared
client is not killed and only the first message is written.
Caller cancellations and tool timeout signals are transient initialize failures.
Do not put them in the three-minute init failure backoff, so a later normal
LSP call can retry the server/cwd instead of failing fast as recently failed.
Add a regression that aborts a wedged initialize and then retries the same
server/cwd with a short explicit timeout, asserting it does not hit the
negative-cache error.
sink.flush()'s type is number | Promise<number>, so calling .then/.catch
directly failed under tsgo. Wrap in Promise.resolve() and annotate the
rejection handler's err parameter to satisfy strict noImplicitAny.
Two client-level paths in the LSP tool bypassed the combined tool-timeout/
caller abort signal built in `LspTool.execute`, so a wedged server hung
past the advertised tool deadline and past user cancellation:
- `getOrCreateClient` took no `AbortSignal` and its `initialize`
`sendRequest` was invoked with `signal = undefined`. With no signal
and no explicit `timeoutMs`, `sendRequest` fell back to the hard-coded
`DEFAULT_REQUEST_TIMEOUT_MS = 30000` internal timer, so a first-use
`lsp` call against a server that wedged in `initialize` ignored the
20s tool default (and any user-supplied shorter `timeout`) until the
30s internal timer fired.
- `writeMessage`/`queueWriteMessage`/`sendNotification` had no timeout
and no signal, so a `textDocument/didOpen`/`didChange`/`didSave` sent
to a server that stopped draining stdin awaited `sink.flush()`
forever. Because writes serialize through `client.writeQueue`, every
later op on the client stalled behind the stuck flush too.
Thread the caller `AbortSignal` through `getOrCreateClient` (initialize
+ initialized notification) and through `sendNotification` /
`queueWriteMessage` / `writeMessage` so the sink flush is raced against
the signal. On abort, tear the client down: kill the process and evict
it from the active-clients map so the next `getOrCreateClient` call
spawns a fresh server instead of queueing behind the wedged sink.
Update the LSP tool callsites and internal helpers
(`captureDiagnosticVersions`, `captureOpenFileVersions`,
`syncFileContent`, `notifyFileSaved`, `formatContent`,
`getDiagnosticsForFile`, `reloadServer`, and the rename didClose /
didRenameFiles path) to forward their operation signal.
Warmup keeps its short explicit `initTimeoutMs` and passes no caller
signal; `sendRequest`'s existing `timeoutMs ?? (signal ? undefined : DEFAULT)`
policy still uses that fixed timer.
Fixes#3962
Added setup.cfg and pyrightconfig.json to PYTHON_ROOT_MARKERS so pyright, basedpyright, and pylsp project shapes also probe Windows .venv/Scripts before PATH fallback.
Fixes#3916
Added Windows virtualenv Scripts directories to local LSP command resolution so project-local Ruff launchers are discovered before PATH fallback.
Fixes#3916
- Centralized draft state and image management by migrating fields from context to the CustomEditor component.
- Standardized transcript row construction by introducing shared helpers for background jobs, IRC traffic, and file mentions.
- Refactored redundant UI logic and helper functions into reusable utility modules to streamline message submission and component rendering.
- Standardized event handler types by consolidating lifecycle definitions into a shared module while maintaining public API stability.
- Extracted incremental byte-stream framing logic into a shared `MessageFramer` class.
- Replaced redundant, per-client implementations in `dap/client.ts` and `lsp/client.ts`.
- Centralized buffer management to avoid O(n^2) allocation patterns during large message bursts.
The background message reader matched every incoming message against the
pending client-request map by id before checking for a `method`. Server
request ids live in the server's own id space and routinely collide with
the client's in-flight request ids, so a server-originated
`workspace/configuration` pull whose id matched a pending request (e.g. a
basedpyright pull landing while a `documentSymbol` request with the same
id was open) was swallowed as a bogus response: the client request
resolved with `undefined` and the pull was never answered, wedging
servers that gate analysis on configuration.
Route any message carrying a `method` as a server request (or
notification) before id-matching responses, so every config pull is
answered under lazy init -- parity with the warmup/reload path, which
escaped the bug only because it issues no concurrent semantic request
while the cold-start pulls drain. lsp.lazy default is unchanged.
Fixes#3001
The PR fixed the -32601 hang for the defined server->client refresh
requests but missed two real spec methods of the identical class:
workspace/inlineValue/refresh (LSP 3.17) and workspace/foldingRange/refresh.
A server emitting either still received Method not found and could stall.
Add both to the void-ack chain and extend the regression test.
handleServerRequest fell through to a JSON-RPC -32601 Method not found for several defined server -> client requests (window/showMessageRequest, window/showDocument, workspace/{semanticTokens,inlayHint,codeLens,codeAction,diagnostic}/refresh). Servers that block on a real reply -- the same failure class as the client/registerCapability hang fixed in #3029 -- could stall waiting for an acknowledgement that never came.
Reply with the spec no-op result instead: null for showMessageRequest / *Refresh, { success: false } for showDocument. Headless omp cannot honour the UI surface, but it still owes a defined response.
Fixes#3044