@@ -353,28 +353,54 @@ class NodeRuntimeDriver implements RuntimeDriver {
353353 } ;
354354 } ) ;
355355
356- // Stdin buffering — writeStdin collects data, closeStdin resolves the promise
356+ // Stdin plumbing — two modes:
357+ // 1. Batched (non-PTY): collect chunks, closeStdin concatenates and resolves promise
358+ // 2. Streaming (PTY): deliver each writeStdin chunk via _stdinRead bridge handler
359+ const isPty = ctx . stdinIsTTY ?? false ;
357360 const stdinChunks : Uint8Array [ ] = [ ] ;
358361 let stdinResolve : ( ( data : string | undefined ) => void ) | null = null ;
362+ // Callbacks set by _stdinRead bridge handler via onStdinReady
363+ let stdinDeliverFn : ( ( data : string ) => void ) | null = null ;
364+ let stdinEndFn : ( ( ) => void ) | null = null ;
359365 const stdinPromise = new Promise < string | undefined > ( ( resolve ) => {
360366 stdinResolve = resolve ;
361- // Auto-resolve on next microtask if nobody calls writeStdin
362- queueMicrotask ( ( ) => {
363- if ( stdinChunks . length === 0 && stdinResolve ) {
364- stdinResolve = null ;
365- resolve ( undefined ) ;
366- }
367- } ) ;
367+ if ( isPty ) {
368+ // PTY mode: resolve immediately with no initial stdin data
369+ stdinResolve = null ;
370+ resolve ( undefined ) ;
371+ } else {
372+ // Non-PTY: auto-resolve on next microtask if nobody calls writeStdin
373+ queueMicrotask ( ( ) => {
374+ if ( stdinChunks . length === 0 && stdinResolve ) {
375+ stdinResolve = null ;
376+ resolve ( undefined ) ;
377+ }
378+ } ) ;
379+ }
368380 } ) ;
369381
370382 const proc : DriverProcess = {
371383 onStdout : null ,
372384 onStderr : null ,
373385 onExit : null ,
374386 writeStdin : ( data : Uint8Array ) => {
375- stdinChunks . push ( data ) ;
387+ if ( isPty && stdinDeliverFn ) {
388+ // Streaming mode: deliver data to sandbox via _stdinRead bridge handler
389+ const text = new TextDecoder ( ) . decode ( data ) ;
390+ stdinDeliverFn ( text ) ;
391+ } else if ( isPty ) {
392+ // Bridge handler not ready yet — buffer for flush when handler connects
393+ stdinChunks . push ( data ) ;
394+ } else {
395+ // Non-PTY batched mode
396+ stdinChunks . push ( data ) ;
397+ }
376398 } ,
377399 closeStdin : ( ) => {
400+ if ( isPty && stdinEndFn ) {
401+ stdinEndFn ( ) ;
402+ return ;
403+ }
378404 if ( stdinResolve ) {
379405 if ( stdinChunks . length === 0 ) {
380406 // No data written — pass undefined (no stdin), not empty string
@@ -400,8 +426,20 @@ class NodeRuntimeDriver implements RuntimeDriver {
400426 wait : ( ) => exitPromise ,
401427 } ;
402428
429+ // Callback to wire up streaming stdin once _stdinRead bridge handler is ready
430+ const setStdinBridge = ( deliver : ( data : string ) => void , end : ( ) => void ) => {
431+ stdinDeliverFn = deliver ;
432+ stdinEndFn = end ;
433+ // Flush any data that arrived before the bridge handler was ready
434+ for ( const chunk of stdinChunks ) {
435+ const text = new TextDecoder ( ) . decode ( chunk ) ;
436+ deliver ( text ) ;
437+ }
438+ stdinChunks . length = 0 ;
439+ } ;
440+
403441 // Launch async — spawn() returns synchronously per RuntimeDriver contract
404- this . _executeAsync ( command , args , ctx , proc , resolveExit , stdinPromise ) ;
442+ this . _executeAsync ( command , args , ctx , proc , resolveExit , stdinPromise , isPty ? setStdinBridge : undefined ) ;
405443
406444 return proc ;
407445 }
@@ -425,6 +463,7 @@ class NodeRuntimeDriver implements RuntimeDriver {
425463 proc : DriverProcess ,
426464 resolveExit : ( code : number ) => void ,
427465 stdinPromise : Promise < string | undefined > ,
466+ setStdinBridge ?: ( deliver : ( data : string ) => void , end : ( ) => void ) => void ,
428467 ) : Promise < void > {
429468 const kernel = this . _kernel ! ;
430469
@@ -483,6 +522,12 @@ class NodeRuntimeDriver implements RuntimeDriver {
483522 bindings : this . _bindings ,
484523 onPtySetRawMode,
485524 } ) ;
525+
526+ // Wire streaming stdin for PTY processes via _stdinRead bridge handler
527+ if ( setStdinBridge ) {
528+ executionDriver . onStdinReady = setStdinBridge ;
529+ }
530+
486531 this . _activeDrivers . set ( ctx . pid , executionDriver ) ;
487532
488533 // Detect ESM files and use V8 native module system
0 commit comments