@@ -47,7 +47,10 @@ class MockChildProcess extends EventEmitter {
4747 readonly stderr = new PassThrough ( ) ;
4848 readonly stdin : Writable ;
4949
50- constructor ( private readonly initializeResponsePrefix = "" ) {
50+ constructor (
51+ private readonly initializeResponsePrefix = "" ,
52+ private readonly respondMethods ?: ReadonlySet < string > ,
53+ ) {
5154 super ( ) ;
5255 this . stdin = new Writable ( {
5356 write : ( chunk , _encoding , callback ) => {
@@ -70,6 +73,9 @@ class MockChildProcess extends EventEmitter {
7073 if ( ! body || typeof body . id !== "number" || typeof body . method !== "string" ) {
7174 return ;
7275 }
76+ if ( this . respondMethods && ! this . respondMethods . has ( body . method ) ) {
77+ return ;
78+ }
7379 const result = body . method === "initialize" ? { capabilities : { hoverProvider : true } } : null ;
7480 queueMicrotask ( ( ) => {
7581 this . stdout . write (
@@ -138,6 +144,38 @@ describe("bundle LSP runtime", () => {
138144 expect ( killProcessTreeMock ) . toHaveBeenCalledWith ( 4321 , { graceMs : 1000 } ) ;
139145 } ) ;
140146
147+ it ( "rejects pending LSP requests immediately when the child process exits" , async ( ) => {
148+ configureSingleLspServer ( ) ;
149+ const child = new MockChildProcess ( "" , new Set ( [ "initialize" ] ) ) ;
150+ spawnMock . mockReturnValue ( child ) ;
151+ const { createBundleLspToolRuntime } = await import ( "./agent-bundle-lsp-runtime.js" ) ;
152+
153+ const runtime = await createBundleLspToolRuntime ( { workspaceDir : "/tmp/workspace" } ) ;
154+ const hoverTool = runtime . tools . find ( ( tool ) => tool . name === "lsp_hover_typescript" ) ;
155+ if ( ! hoverTool ) {
156+ throw new Error ( "expected hover tool" ) ;
157+ }
158+
159+ const request = hoverTool . execute ( "call-1" , {
160+ uri : "file:///tmp/workspace/index.ts" ,
161+ line : 0 ,
162+ character : 0 ,
163+ } ) ;
164+ child . exitCode = 1 ;
165+ child . emit ( "exit" , 1 , null ) ;
166+
167+ await expect ( request ) . rejects . toThrow ( 'LSP server "typescript" exited (1)' ) ;
168+ await expect (
169+ hoverTool . execute ( "call-2" , {
170+ uri : "file:///tmp/workspace/index.ts" ,
171+ line : 0 ,
172+ character : 0 ,
173+ } ) ,
174+ ) . rejects . toThrow ( 'LSP server "typescript" exited (1)' ) ;
175+
176+ await runtime . dispose ( ) ;
177+ } ) ;
178+
141179 it ( "keeps LSP framing aligned after multibyte messages in the same chunk" , async ( ) => {
142180 configureSingleLspServer ( ) ;
143181 const prefix = encodeLspMessage ( {
0 commit comments