77 type CapabilitiesResponse ,
88 APIServerURLEnvironmentVariable ,
99} from "../agent/client" ;
10+ import { RWLock } from "../local/rw-lock" ;
1011
1112export interface AgentLog {
1213 readonly level : "log" | "error" ;
@@ -53,6 +54,8 @@ export default function useAgent(options: UseAgentOptions) {
5354 setAgent ( undefined ) ;
5455 setCapabilities ( undefined ) ;
5556
57+ let lock : RWLock | undefined ;
58+
5659 ( async ( ) => {
5760 const port = await getRandomPort ( ) ;
5861 const proc = spawn ( "node" , [ "--no-deprecation" , buildResult . entry ] , {
@@ -64,11 +67,27 @@ export default function useAgent(options: UseAgentOptions) {
6467 PORT : port . toString ( ) ,
6568 HOST : "127.0.0.1" ,
6669 } ,
70+ // keep the child process tied to the parent process
71+ detached : false ,
6772 } ) ;
68- controller . signal . addEventListener ( "abort" , ( ) => {
73+ const cleanup = ( ) => {
6974 try {
7075 proc . kill ( ) ;
7176 } catch { }
77+ } ;
78+
79+ // Clean up - when the parent process exits, kill the child process.
80+ process . once ( "exit" , cleanup ) ;
81+ process . once ( "SIGINT" , cleanup ) ;
82+ process . once ( "SIGTERM" , cleanup ) ;
83+ process . once ( "uncaughtException" , cleanup ) ;
84+
85+ controller . signal . addEventListener ( "abort" , ( ) => {
86+ process . off ( "exit" , cleanup ) ;
87+ process . off ( "SIGINT" , cleanup ) ;
88+ process . off ( "SIGTERM" , cleanup ) ;
89+ process . off ( "uncaughtException" , cleanup ) ;
90+ cleanup ( ) ;
7291 } ) ;
7392 let ready = false ;
7493 proc . stdout . on ( "data" , ( data ) => {
@@ -114,6 +133,7 @@ export default function useAgent(options: UseAgentOptions) {
114133 const client = new Client ( {
115134 baseUrl : `http://127.0.0.1:${ port } ` ,
116135 } ) ;
136+ lock = client . agentLock ;
117137 // Wait for the health endpoint to be alive.
118138 while ( ! controller . signal . aborted ) {
119139 try {
@@ -139,7 +159,12 @@ export default function useAgent(options: UseAgentOptions) {
139159 } ) ;
140160 return ( ) => {
141161 isCleanup = true ;
142- controller . abort ( ) ;
162+ ( async ( ) => {
163+ // Acquire write lock before cleaning up this agent instance
164+ // This waits for any active streams using this agent to complete
165+ using _writeLock = await lock ?. write ( ) ;
166+ controller . abort ( ) ;
167+ } ) ( ) ;
143168 } ;
144169 } , [ buildResult , env , apiServerUrl ] ) ;
145170
0 commit comments