diff --git a/crates/execution/src/wasm.rs b/crates/execution/src/wasm.rs index 31198d2d1..0d76a16a2 100644 --- a/crates/execution/src/wasm.rs +++ b/crates/execution/src/wasm.rs @@ -446,6 +446,18 @@ impl WasmExecution { self.inner.write_stdin(chunk).map_err(map_javascript_error) } + /// Feed stdin WITHOUT emitting a `stdin` stream event to the V8 session. + /// Sidecar-managed wasm always reads stdin through the kernel + /// (`__kernel_stdin_read`); the stream event is never consumed there, and + /// while the guest thread is blocked in a sync bridge call every + /// unconsumed event lands in the session's bounded deferred-message queue + /// — one dead event per keystroke until the queue limit kills the session. + pub fn write_stdin_kernel_only(&mut self, chunk: &[u8]) -> Result<(), WasmExecutionError> { + self.inner + .write_kernel_stdin_only(chunk) + .map_err(map_javascript_error) + } + pub fn close_stdin(&mut self) -> Result<(), WasmExecutionError> { self.inner.close_stdin().map_err(map_javascript_error) } diff --git a/crates/sidecar/src/execution.rs b/crates/sidecar/src/execution.rs index 080ef213f..8e07fe698 100644 --- a/crates/sidecar/src/execution.rs +++ b/crates/sidecar/src/execution.rs @@ -3184,8 +3184,13 @@ impl ActiveExecution { Self::Python(execution) => execution .write_stdin(chunk) .map_err(|error| SidecarError::Execution(error.to_string())), + // Sidecar wasm always runs with kernel-managed stdio + // (AGENTOS_WASI_STDIO_SYNC_RPC=1): the guest reads fd 0 via + // `__kernel_stdin_read`, so skip the V8 `stdin` stream event — + // it is never consumed and would flood the session's deferred + // message queue while the guest blocks in a sync read. Self::Wasm(execution) => execution - .write_stdin(chunk) + .write_stdin_kernel_only(chunk) .map_err(|error| SidecarError::Execution(error.to_string())), Self::Tool(_) => Ok(()), } @@ -9901,20 +9906,36 @@ fn sync_host_directory_tree_to_kernel_inner( continue; } } - let bytes = read_host_shadow_file(&host_path, desired_mode).map_err(|error| { - SidecarError::Io(format!( - "failed to read host shadow file {}: {error}", - host_path.display() - )) - })?; - vm.kernel.write_file(&guest_path, bytes).map_err(|error| { - SidecarError::InvalidState(format!( - "failed to sync host shadow file {} to guest {}: {}", - host_path.display(), - guest_path, - kernel_error(error) - )) - })?; + let bytes = match read_host_shadow_file(&host_path, desired_mode) { + Ok(bytes) => bytes, + // The host entry vanished between the walk and the read + // (short-lived files churn constantly — editor swap files, + // temp files). Skipping matches native semantics; failing + // here would poison EVERY subsequent fs op on the VM. + Err(error) if error.kind() == std::io::ErrorKind::NotFound => continue, + Err(error) => { + return Err(SidecarError::Io(format!( + "failed to read host shadow file {}: {error}", + host_path.display() + ))); + } + }; + match vm.kernel.write_file(&guest_path, bytes) { + Ok(()) => {} + // ENOENT here means the guest-side path cannot currently + // receive the write (e.g. it is a symlink whose target was + // just unlinked by the guest — vim's swap-file dance). The + // entry is mid-churn; skip it rather than failing the VM. + Err(error) if error.code() == "ENOENT" => continue, + Err(error) => { + return Err(SidecarError::InvalidState(format!( + "failed to sync host shadow file {} to guest {}: {}", + host_path.display(), + guest_path, + kernel_error(error) + ))); + } + } vm.kernel .chmod(&guest_path, desired_mode) .map_err(|error| { diff --git a/node_modules b/node_modules deleted file mode 120000 index a04c3d9cf..000000000 --- a/node_modules +++ /dev/null @@ -1 +0,0 @@ -/home/nathan/.herdr/workspaces/agent-os/secure-exec-provides/node_modules \ No newline at end of file diff --git a/packages/build-tools/node_modules b/packages/build-tools/node_modules deleted file mode 120000 index 1cdc2ffdc..000000000 --- a/packages/build-tools/node_modules +++ /dev/null @@ -1 +0,0 @@ -/home/nathan/.herdr/workspaces/agent-os/secure-exec-provides/packages/build-tools/node_modules \ No newline at end of file