|
65 | 65 | - V8 dynamic import callback (`dynamic_import_callback`) uses MODULE_RESOLVE_STATE thread-local — must be initialized before execution for both CJS and ESM modes |
66 | 66 | - `enable_dynamic_import()` must be called after every isolate create/restore (not captured in snapshots), alongside `disable_wasm()` |
67 | 67 | - `execute_script()` now takes optional `BridgeCallContext` as 2nd arg for dynamic import support in CJS mode |
| 68 | +- Streaming stdin uses _stdinRead bridge handler (async bridge call/response), NOT V8 stream events — stream events have V8 serialization cross-version issues |
| 69 | +- buildPtyBridgeHandlers must be called ONCE per executeInternal — the result is shared between dispatch handlers and main bridge handlers to avoid split closure state |
| 70 | +- process.exit() inside setTimeout callbacks is silently caught by timer error handler — use process.exit() only in synchronous or stdinDispatch contexts |
| 71 | +- PTY stdin flow: shell.write() → PTY master → input buffer → stdin pump (kernel openShell) → proc.writeStdin → onStdinData → resolves _stdinRead promise → readLoop dispatches to stdinDispatch → process.stdin 'data' events |
68 | 72 |
|
69 | 73 | # Ralph Progress Log |
70 | 74 | Started: Sat Mar 21 02:49:43 AM PDT 2026 |
@@ -818,3 +822,27 @@ Started: Sat Mar 21 02:49:43 AM PDT 2026 |
818 | 822 | - The __dynamicImport global is still installed in V8 snapshots but never called — removing it would require a snapshot rebuild |
819 | 823 | - convertEsmToCjs is still needed in loadFileSync for require() of ESM-only packages in CJS exec mode |
820 | 824 | --- |
| 825 | + |
| 826 | +## 2026-03-21 19:13 - US-026 |
| 827 | +- Fixed streaming stdin delivery from PTY to sandbox process |
| 828 | +- Three-layer fix: |
| 829 | + 1. **Kernel stdin pump** (kernel.ts openShell): Reads from PTY slave input buffer and forwards to driverProcess.writeStdin() — bridges shell.write() to the runtime driver |
| 830 | + 2. **Bridge handler** (bridge-handlers.ts buildPtyBridgeHandlers): Added `_stdinRead` async bridge handler that returns a Promise resolving with the next stdin chunk. Host resolves when writeStdin delivers data. |
| 831 | + 3. **Bridge read loop** (bridge/process.ts resume()): When process.stdin.resume() is called on TTY, starts async loop calling _stdinRead repeatedly. Each call creates a pending bridge promise keeping the V8 event loop alive. Data dispatched to stdinDispatch which emits process.stdin 'data' events. |
| 832 | +- Also updated stream.rs to route "stdin" → _stdinDispatch (and other missing event types) |
| 833 | +- Files changed: |
| 834 | + - packages/core/src/kernel/kernel.ts (stdin pump in openShell) |
| 835 | + - packages/nodejs/src/bridge-contract.ts (stdinRead key) |
| 836 | + - packages/nodejs/src/bridge-handlers.ts (PtyBridgeDeps, _stdinRead handler) |
| 837 | + - packages/nodejs/src/bridge/process.ts (_stdinRead loop, stdinDispatch) |
| 838 | + - packages/nodejs/src/execution-driver.ts (onStdinReady, deduplicate ptyDeps) |
| 839 | + - packages/nodejs/src/kernel-runtime.ts (stdinDeliverFn/stdinEndFn via bridge) |
| 840 | + - native/v8-runtime/src/stream.rs (new event type routing) |
| 841 | + - packages/secure-exec/tests/kernel/bridge-gap-behavior.test.ts (4 streaming stdin tests) |
| 842 | +- **Learnings for future iterations:** |
| 843 | + - V8 stream events (sendStreamEvent/dispatch_stream_event) use V8 serialization which may have cross-version issues between host Node.js V8 and Rust V8 crate — prefer bridge call/response for reliable data delivery |
| 844 | + - Bridge handlers must be built ONCE and shared — duplicate buildPtyBridgeHandlers calls create separate closures with separate state, causing data to go to the wrong instance |
| 845 | + - process.exit() inside setTimeout callbacks is silently caught by the timer error handler — avoid process.exit() in async timer callbacks |
| 846 | + - The _stdinRead bridge call pattern keeps the V8 event loop alive via pending promises — each call creates a pending promise until the host resolves it |
| 847 | + - buildPtyBridgeHandlers exists in BOTH dispatch handlers (inside buildModuleLoadingBridgeHandlers) and main handlers — must use the same ptyDeps/ptyHandlers instance |
| 848 | +--- |
0 commit comments