Issue #22872 reports the write tool hanging indefinitely after a file
is written. Two underlying causes, both in the post-write LSP
enrichment path:
1. LSPClient.create wraps the `initialize` request in a 45s
withTimeout. If the spawned LSP process is wedged (happens with
pyright under certain conditions), every write that matches that
LSP blocks the tool for up to 45s even though the file is on disk.
2. Server.spawn for npm-distributed LSPs (pyright, tsserver,
biome, ...) calls Npm.which, which internally uses arborist.reify
with no timeout. In sandboxed containers with no network access
this promise never resolves — the write tool hangs forever.
Fix applied at three layers of defense:
- write.ts / edit.ts / apply_patch.ts: wrap the touchFile +
diagnostics tail in a 5s Effect.timeout with catch-to-empty.
Diagnostics are a best-effort enrichment; they must not block the
tool's return after the file is already written.
- lsp.ts schedule(): bound server.spawn with a 10s Promise.race
timeout. On timeout the server is added to s.broken so subsequent
touches short-circuit instantly instead of re-racing.
- client.ts: lower the `initialize` withTimeout from 45_000 to
10_000. If a server hasn't responded to initialize in 10s it's
wedged; 45s was punishing for no benefit.
Reproducer tests (added in earlier commits on this branch) now pass:
- write-lsp-hang.test.ts (branch A, 45s initialize timeout)
- write-lsp-spawn-hang.test.ts (branch B, forever Npm.which)
Both complete in ~5s.
Full opencode test suite: 1934 pass, 0 fail.