@@ -149,4 +149,62 @@ describe("persist close_agent leftover dispose", () => {
149149 ) ,
150150 ) ;
151151 } ) ;
152+
153+ test ( "onAgentReady close surfaces leftover posix dispose when agent.close hangs" , async ( ) => {
154+ const cwd = await mkdtemp ( join ( tmpdir ( ) , "corbits-persist-close-leftover-hang-" ) ) ;
155+ let closeStarted = false ;
156+
157+ await withMockedModuleDuring (
158+ import . meta. resolve ( "@intx/tools-posix" ) ,
159+ ( real : typeof import ( "@intx/tools-posix" ) ) => ( {
160+ ...real ,
161+ createPosixTools : ( opts : Parameters < typeof real . createPosixTools > [ 0 ] ) =>
162+ Object . assign ( real . createPosixTools ( opts ) , {
163+ dispose : async ( ) => {
164+ throw new Error ( "1 shell child process still live after 2000ms reap" ) ;
165+ } ,
166+ } ) ,
167+ } ) ,
168+ async ( ) =>
169+ withMockedModuleDuring (
170+ import . meta. resolve ( "../agent/live-tool-dispatch.js" ) ,
171+ ( real : typeof import ( "../agent/live-tool-dispatch.js" ) ) => ( {
172+ ...real ,
173+ createAgentWithLiveToolDispatch : async ( ) =>
174+ ( {
175+ ...stubAgent ( ) ,
176+ close : ( ) => {
177+ closeStarted = true ;
178+ return new Promise < void > ( ( ) => { } ) ;
179+ } ,
180+ } ) as unknown as Awaited < ReturnType < typeof real . createAgentWithLiveToolDispatch > > ,
181+ } ) ,
182+ async ( ) => {
183+ const { runSubAgent } = await import ( "./run.js" ) ;
184+ let handles :
185+ | {
186+ close : ( deadlineMs ?: number ) => Promise < void > ;
187+ }
188+ | undefined ;
189+ const params : RunSubAgentParams = {
190+ cwd,
191+ workdirBase : join ( cwd , ".ctx" ) ,
192+ permissionGate,
193+ provider : { providerName : "test" , baseURL : "http://localhost" , model : "test-model" } ,
194+ description : "persist close leftover hung close probe" ,
195+ prompt : "finish the first turn" ,
196+ persist : true ,
197+ onAgentReady : ( h ) => {
198+ handles = h ;
199+ } ,
200+ } ;
201+ const result = await runSubAgent ( params ) ;
202+ expect ( result . agentRetained ) . toBe ( true ) ;
203+ if ( handles === undefined ) throw new Error ( "onAgentReady never fired" ) ;
204+ await expect ( handles . close ( 200 ) ) . rejects . toThrow ( / s t i l l l i v e a f t e r 2 0 0 0 m s r e a p / ) ;
205+ expect ( closeStarted ) . toBe ( true ) ;
206+ } ,
207+ ) ,
208+ ) ;
209+ } ) ;
152210} ) ;
0 commit comments