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
- Accepted client/registerCapability and client/unregisterCapability server requests so Expert can continue startup before semantic requests.\n- Allowed JSON-RPC string ids on LSP request/response handling.\n- Added a regression test for dynamic registration gating hover.\n\nFixes #3029
- Switch all UI components and tests from sharp box corners (`boxSharp`) to rounded ones (`boxRound`).
- Update `Theme` to re-export sharp junction symbols (tees and cross) under `boxRound` to ensure consistent divider rendering in rounded boxes.
- Remove outdated architectural notes regarding forced tool-choice queues in documentation.
- Updated log and error truncation messages to use a consistent `[...Nch elided...]` format.
- Standardized diagnostics and prompt text output to improve consistency in reporting truncated information.
- Standardized elision markers across all tool outputs and filters to use cohesive `[...N [type] elided...]`, `[...Nln elided...]`, and `[...xB elided...]` syntax.
- Updated documentation, prompts, and test expectations to reflect the unified elision format.
- Improved transcript viewer robustness by preventing content aliasing through path-inclusive signature hashing.
- Added logic to clear stale transcript content when associated session files are deleted, accompanied by verifying test cases.
- Migrated all wire protocol, schema definitions, and tools validation from Zod to ArkType across multiple packages.
- Updated extension runtimes, custom tools loader, and TypeBox compatibility shim to expose and use ArkType instances.
- Added a comprehensive ArkType migration guide, validation parity tests, and helper utilities.
- Removed redundant PDF asset routing and parsing implementations from the read tool.
- Added optional `useless` flags to tool result types and payload builders.
- Added `pruneUseless` and `dropUeless` options to control uneventful result pruning.
- Changed compaction and shake passes to prune or ignore non-error useless tool results.
- Changed conversation serialization to omit useless toolCall/toolResult pairs from output.
- Added coverage for useless tagging, pruning, and serialization behavior.
The format-on-write path sent a hardcoded {tabSize:3, insertSpaces:true}
on every textDocument/formatting request from two duplicated DEFAULT_FORMAT_OPTIONS
constants. Servers that honour tabSize for re-indent (yaml-language-server, the
common YAML/Kustomize/Flux case) reserialized 2-space files at a 3-space stride
on every write — exactly the corruption reported on Kubernetes/Flux YAML repos.
Replace both constants with a single resolveFormatOptions(filePath, content)
helper that layers, in order:
- .editorconfig (indent_style, indent_size, tab_width) via the new
getEditorConfigFormatting() helper in pi-utils — strict, no fallback.
- Indent sniffed from the in-memory content the agent is about to write
(first indented line decides spaces vs tabs; GCD of space-indent widths
fixes the unit).
- Hardcoded 2-space fallback. The previous 3-space stride was an unusual
default that actively damaged every file with a 2/4-space convention.
Tests cover the editorconfig and content-sniffing paths plus a direct
regression check that 2-space YAML stays 2-space (the issue's repro).
Fixes#2329
Adds an `expert` server entry (binary `expert`, invoked `expert --stdio`)
to the built-in LSP defaults, auto-detected for Mix projects
(`mix.exs`/`mix.lock`). Placed after `elixirls` so elixir-ls remains the
primary navigation server when both binaries are installed; this keeps the
default getServerForFile() behavior unchanged. Absent-binary setups are
unaffected by loadConfig's PATH/root-marker gating.
Includes a docs row, changelog entry, and a regression test pinning that
`.ex` files match `expert` and that `elixirls` still sorts ahead of it.
- Added "available" status so recognized servers show under lazy mode without warmup.
- Rendered full welcome box as pre-TUI splash with fixed slot heights to avoid layout shift.
- Reported lazy servers as available in /status instead of omitting the section.
clients publish only after initialize; dead readers tear down for respawn instead of permanent 30s timeouts; framing resyncs past junk headers; numeric code-action selectors pick strictly by index; file URIs percent-encode and raw fragment/query chars route to the lax parser; equal-position inserts keep spec order; workspace edits validate before writing; shutdown covers mid-init clients; reload sends notification; writethrough init deadline-bounded with negative caching; DAP pause/breakpoint races fixed, mutations serialized and abort-aware, output buffering O(n) with correct tail retention.
- Added status.done and tool.* symbols to theme mappings and presets.
- Replaced generic success glyphs with contextual +/-, tool icons, and warnings.
- Mapped tool/task/job completions to status.done or status.enabled with icon overrides.
- Triggered runtime provider refresh after extension registration and warned on failure.
- Routed background-job completions and late LSP diagnostics through the new non-interrupting aside channel so the model sees them mid-run between requests.
- Removed inline custom rendering from the LSP tool now that diagnostics surface through the shared transcript renderer.
- Updated `waitForDiagnostics` to accept exact document-version matches immediately and otherwise wait for a quiescence window before using the latest publish.
- Removed the old unversioned-acceptance option and applied the settle-based wait logic through inline and deferred diagnostics fetch paths.
- Added an LSP writethrough regression test ensuring stale unversioned diagnostics are ignored in favor of later fresh publishes.
- Added a configurable diagnostics timeout to `getDiagnosticsForFile` and wired callers to pass custom budgets.
- Refactored writethrough diagnostics retrieval to `fetchDiagnosticsWithDeferral`, waiting inline briefly and handing off slow results to the deferred callback.
- Extended deferred fetches to use a longer timeout so late diagnostics are still delivered for slow servers.
- Adjusted `LspTool` to retry only zero/decl-only references with two 250ms waits for project-aware servers.
- Removed raw-mode GitHub repo README API fallback in `read`, so `:raw` now renders the page directly.
- Replaced regex heuristics in `isImagePlaceholderAnswer` with a fixed normalized placeholder set.