//! Process execution, networking, and runtime event handling extracted from service.rs.

use secure_exec_vm_config as vm_config;

use crate::filesystem::{
    handle_python_vfs_rpc_request as filesystem_handle_python_vfs_rpc_request,
    service_javascript_fs_sync_rpc, service_javascript_module_sync_rpc,
};
use crate::protocol::{
    BoundUdpSnapshotResponse, CloseStdinRequest, EventFrame, EventPayload, ExecuteRequest,
    FindBoundUdpRequest, FindListenerRequest, GetProcessSnapshotRequest, GetSignalStateRequest,
    GetZombieTimerCountRequest, GuestRuntimeKind, JavascriptChildProcessSpawnOptions,
    JavascriptChildProcessSpawnRequest, JavascriptDgramBindRequest,
    JavascriptDgramCreateSocketRequest, JavascriptDgramSendRequest, JavascriptDnsLookupRequest,
    JavascriptDnsResolveRequest, JavascriptNetConnectRequest, JavascriptNetListenRequest,
    JavascriptNetReserveTcpPortRequest, KillProcessRequest, ListenerSnapshotResponse,
    OwnershipScope, ProcessExitedEvent, ProcessKilledResponse, ProcessOutputEvent,
    ProcessSnapshotEntry, ProcessSnapshotResponse, ProcessSnapshotStatus, ProcessStartedResponse,
    RequestFrame, ResponseFrame, ResponsePayload, SidecarRequestPayload, SignalDispositionAction,
    SignalHandlerRegistration, SignalStateResponse, SocketStateEntry, StdinClosedResponse,
    StdinWrittenResponse, StreamChannel, VmFetchRequest, VmFetchResponse, WasmPermissionTier,
    WriteStdinRequest, ZombieTimerCountResponse,
};
use crate::service::{
    audit_fields, dirname, emit_security_audit_event, emit_structured_event, javascript_error,
    kernel_error, log_stale_process_event, normalize_host_path, normalize_path,
    parse_javascript_child_process_spawn_request, path_is_within_root,
    process_event_queue_overflow_error, python_error, wasm_error, MAX_PROCESS_EVENT_QUEUE,
};
use crate::state::{
    ActiveCipherSession, ActiveDhSession, ActiveDiffieHellmanSession, ActiveEcdhSession,
    ActiveExecution, ActiveExecutionEvent, ActiveHttp2Server, ActiveHttp2Session,
    ActiveHttp2Stream, ActiveHttpServer, ActiveMappedHostFd, ActiveProcess, ActiveSqliteDatabase,
    ActiveSqliteStatement, ActiveTcpListener, ActiveTcpSocket, ActiveTlsState, ActiveTlsStream,
    ActiveUdpSocket, ActiveUnixListener, ActiveUnixSocket, BridgeError, ExitedProcessSnapshot,
    Http2BridgeEvent, Http2RuntimeSnapshot, Http2SessionCommand, Http2SessionSnapshot,
    Http2SocketSnapshot, JavascriptHttpLoopbackTarget, JavascriptSocketFamily,
    JavascriptSocketPathContext, JavascriptTcpListenerEvent, JavascriptTcpSocketEvent,
    JavascriptTlsBridgeOptions, JavascriptTlsClientHello, JavascriptTlsDataValue,
    JavascriptTlsMaterial, JavascriptUdpFamily, JavascriptUdpSocketEvent,
    JavascriptUnixListenerEvent, NetworkResourceCounts, PendingTcpSocket, PendingUnixSocket,
    ProcNetEntry, ProcessEventEnvelope, ResolvedChildProcessExecution, ResolvedTcpConnectAddr,
    SharedBridge, SharedSidecarRequestClient, SidecarKernel, SocketQueryKind, ToolExecution,
    VmDnsConfig, VmListenPolicy, VmState, DEFAULT_JAVASCRIPT_NET_BACKLOG, EXECUTION_DRIVER_NAME,
    EXECUTION_SANDBOX_ROOT_ENV, JAVASCRIPT_COMMAND, LOOPBACK_EXEMPT_PORTS_ENV,
    MAPPED_HOST_FD_START, PYTHON_COMMAND, TOOL_DRIVER_NAME,
    VM_LISTEN_ALLOW_PRIVILEGED_METADATA_KEY, WASM_COMMAND, WASM_STDIO_SYNC_RPC_ENV,
};
use crate::tools::{
    format_tool_failure_output, is_tool_command, normalized_tool_command_name,
    resolve_tool_command, ToolCommandResolution,
};
use crate::wire::{ProtocolFrame as WireProtocolFrame, WireFrameCodec, DEFAULT_MAX_FRAME_BYTES};
use crate::{DispatchResult, NativeSidecar, NativeSidecarBridge, SidecarError};

use base64::Engine;
use bytes::Bytes;
use h2::{client, server, Reason};
use hickory_resolver::proto::rr::{RData, Record, RecordType};
use hmac::{Hmac, Mac};
use http::{HeaderMap, HeaderName, HeaderValue, Method, Request, Response, Uri};
use md5::Md5;
use nix::libc;
use nix::sys::signal::{kill as send_signal, Signal};
use nix::sys::wait::WaitStatus;
#[cfg(not(target_os = "macos"))]
use nix::sys::wait::{waitid as wait_on_child, Id as WaitId, WaitPidFlag};
#[cfg(target_os = "macos")]
use nix::sys::wait::{waitpid, WaitPidFlag};
use nix::unistd::Pid;
use openssl::bn::{BigNum, BigNumContext};
use openssl::derive::Deriver;
use openssl::dh::Dh;
use openssl::ec::{EcGroup, EcKey, EcPoint, PointConversionForm};
use openssl::hash::MessageDigest;
use openssl::nid::Nid;
use openssl::pkey::{Id as PKeyId, PKey, Params, Private, Public};
use openssl::rand::rand_bytes;
use openssl::rsa::{Padding, Rsa};
use openssl::sign::{Signer, Verifier};
use openssl::symm::{Cipher, Crypter, Mode};
use pbkdf2::pbkdf2_hmac;
use rusqlite::types::ValueRef as SqliteValueRef;
use rusqlite::{
    Connection as SqliteConnection, OpenFlags as SqliteOpenFlags, Statement as SqliteStatement,
};
use rustls::client::danger::{HandshakeSignatureValid, ServerCertVerified, ServerCertVerifier};
use rustls::crypto::aws_lc_rs;
use rustls::pki_types::{CertificateDer, PrivateKeyDer, ServerName};
use rustls::{
    ClientConfig, ClientConnection, DigitallySignedStruct, RootCertStore, ServerConfig,
    ServerConnection, SignatureScheme,
};
use scrypt::{scrypt, Params as ScryptParams};
use secure_exec_bridge::LifecycleState;
use secure_exec_execution::wasm::WasmExecutionError;
use secure_exec_execution::{
    javascript::handle_internal_bridge_call_from_host_context, v8_host::V8SessionHandle,
    v8_runtime, CreateJavascriptContextRequest, CreatePythonContextRequest,
    CreateWasmContextRequest, GuestRuntimeConfig, JavascriptExecutionEvent,
    JavascriptExecutionLimits, JavascriptSyncRpcRequest, ModuleFsReader,
    NodeSignalDispositionAction, NodeSignalHandlerRegistration, PythonExecutionEvent,
    PythonExecutionLimits, PythonVfsRpcMethod, PythonVfsRpcRequest, PythonVfsRpcResponsePayload,
    StartJavascriptExecutionRequest, StartPythonExecutionRequest, StartWasmExecutionRequest,
    WasmExecutionEvent, WasmExecutionLimits, WasmPermissionTier as ExecutionWasmPermissionTier,
};
use secure_exec_kernel::dns::{
    DnsLookupPolicy, DnsRecordResolution, DnsResolutionSource as KernelDnsResolutionSource,
};
use secure_exec_kernel::kernel::{KernelProcessHandle, SpawnOptions, VirtualProcessOptions};
use secure_exec_kernel::permissions::NetworkOperation;
use secure_exec_kernel::poll::{PollEvents, PollFd, PollTargetEntry, POLLERR, POLLHUP, POLLIN};
use secure_exec_kernel::process_table::{ProcessStatus, WaitPidFlags, SIGKILL, SIGTERM};
use secure_exec_kernel::pty::LineDisciplineConfig;
use secure_exec_kernel::resource_accounting::ResourceLimits;
use secure_exec_kernel::root_fs::RootFilesystemMode;
use secure_exec_kernel::socket_table::{
    InetSocketAddress, SocketDomain, SocketId, SocketShutdown as KernelSocketShutdown, SocketSpec,
    SocketState, SocketType,
};
use serde::{Deserialize, Serialize};
use serde_json::{json, Map, Value};
use sha1::Sha1;
use sha2::{digest::Digest, Sha256, Sha512};
use socket2::{SockRef, TcpKeepalive};
use std::collections::VecDeque;
use std::collections::{BTreeMap, BTreeSet};
use std::fmt;
use std::fs;
use std::io::{Cursor, Read, Write};
use std::net::{
    IpAddr, Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr, TcpListener, TcpStream, ToSocketAddrs,
    UdpSocket,
};
use std::os::unix::fs::{MetadataExt, PermissionsExt};
use std::os::unix::net::{SocketAddr as UnixSocketAddr, UnixListener, UnixStream};
use std::path::{Path, PathBuf};
use std::pin::Pin;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::mpsc::{self, RecvTimeoutError, Sender};
use std::sync::{Arc, Mutex, OnceLock, Weak};
use std::thread;
use std::time::{Duration, Instant};
use tokio::io::{AsyncRead, AsyncWrite};
use tokio::runtime::Builder as TokioRuntimeBuilder;
use tokio::sync::mpsc::{unbounded_channel, UnboundedReceiver};
use tokio_rustls::{TlsAcceptor, TlsConnector};
use url::Url;

const DEFAULT_KERNEL_STDIN_READ_MAX_BYTES: usize = 64 * 1024;
const DEFAULT_KERNEL_STDIN_READ_TIMEOUT_MS: u64 = 100;
const JAVASCRIPT_NET_TIMEOUT_SENTINEL: &str = "__secure_exec_net_timeout__";
const PYTHON_PYODIDE_GUEST_ROOT: &str = "/__agentos_pyodide";
const PYTHON_PYODIDE_CACHE_GUEST_ROOT: &str = "/__agentos_pyodide_cache";
const TCP_SOCKET_POLL_TIMEOUT: Duration = Duration::from_millis(100);
const TLS_HANDSHAKE_TIMEOUT: Duration = Duration::from_secs(5);
const HTTP_LOOPBACK_REQUEST_TIMEOUT: Duration = Duration::from_secs(30);
pub(crate) const MAX_PER_PROCESS_STATE_HANDLES: usize = 1024;
const VM_FETCH_BUFFER_LIMIT_BYTES: usize = DEFAULT_MAX_FRAME_BYTES;
const DEFAULT_SCRYPT_COST: u64 = 16_384;
const DEFAULT_SCRYPT_BLOCK_SIZE: u32 = 8;
const DEFAULT_SCRYPT_PARALLELIZATION: u32 = 1;
const SQLITE_JS_SAFE_INTEGER_MAX: i64 = 9_007_199_254_740_991;
const HTTP_LOOPBACK_REQUEST_TIMEOUT_MS_ENV: &str =
    "SECURE_EXEC_TEST_HTTP_LOOPBACK_REQUEST_TIMEOUT_MS";

trait Http2AsyncIo: AsyncRead + AsyncWrite + Unpin + Send {}

impl<T> Http2AsyncIo for T where T: AsyncRead + AsyncWrite + Unpin + Send {}

fn http_loopback_request_timeout() -> Duration {
    static TIMEOUT: OnceLock<Duration> = OnceLock::new();
    *TIMEOUT.get_or_init(|| {
        std::env::var(HTTP_LOOPBACK_REQUEST_TIMEOUT_MS_ENV)
            .ok()
            .and_then(|value| value.parse::<u64>().ok())
            .map(Duration::from_millis)
            .unwrap_or(HTTP_LOOPBACK_REQUEST_TIMEOUT)
    })
}

const DEFAULT_ALLOWED_NODE_BUILTINS: &[&str] = &[
    "assert",
    "buffer",
    "console",
    "child_process",
    "crypto",
    "dns",
    "events",
    "fs",
    "http",
    "http2",
    "https",
    "module",
    "os",
    "path",
    "perf_hooks",
    "querystring",
    "sqlite",
    "stream",
    "string_decoder",
    "timers",
    "tls",
    "tty",
    "url",
    "util",
    "zlib",
];

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum JavascriptCryptoDigestAlgorithm {
    Md5,
    Sha1,
    Sha256,
    Sha512,
}

#[derive(Debug, Default, Deserialize)]
#[serde(default, rename_all = "camelCase")]
struct JavascriptScryptOptions {
    #[serde(alias = "N")]
    cost: Option<u64>,
    #[serde(alias = "r")]
    block_size: Option<u32>,
    #[serde(alias = "p")]
    parallelization: Option<u32>,
}

#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
struct JavascriptHttpListenRequest {
    server_id: u64,
    #[serde(default)]
    port: Option<u16>,
    #[serde(default)]
    hostname: Option<String>,
}

#[derive(Debug, Default, Deserialize)]
#[serde(default, rename_all = "camelCase")]
struct JavascriptHttpRequestOptions {
    method: Option<String>,
    headers: BTreeMap<String, Value>,
    body: Option<String>,
    reject_unauthorized: Option<bool>,
}

#[derive(Debug, Default, Deserialize)]
#[serde(default, rename_all = "camelCase")]
struct JavascriptHttp2ServerListenRequest {
    server_id: u64,
    secure: bool,
    port: Option<u16>,
    host: Option<String>,
    backlog: Option<u32>,
    timeout: Option<u64>,
    settings: BTreeMap<String, Value>,
    tls: Option<JavascriptTlsBridgeOptions>,
}

#[derive(Debug, Default, Deserialize)]
#[serde(default, rename_all = "camelCase")]
struct JavascriptHttp2SessionConnectRequest {
    authority: Option<String>,
    protocol: Option<String>,
    host: Option<String>,
    port: Option<u16>,
    settings: BTreeMap<String, Value>,
    tls: Option<JavascriptTlsBridgeOptions>,
}

#[derive(Debug, Default, Deserialize)]
#[serde(default, rename_all = "camelCase")]
struct JavascriptHttp2RequestOptions {
    end_stream: bool,
}

#[derive(Debug, Default, Deserialize)]
#[serde(default, rename_all = "camelCase")]
struct JavascriptHttp2FileResponseOptions {
    offset: Option<u64>,
    length: Option<i64>,
}

#[derive(Debug, Clone)]
struct HttpHeaderCollection {
    normalized: BTreeMap<String, Vec<String>>,
    raw_pairs: Vec<(String, String)>,
}

#[derive(Debug)]
struct InsecureTlsVerifier {
    supported_schemes: Vec<SignatureScheme>,
}

impl ServerCertVerifier for InsecureTlsVerifier {
    fn verify_server_cert(
        &self,
        _end_entity: &CertificateDer<'_>,
        _intermediates: &[CertificateDer<'_>],
        _server_name: &ServerName<'_>,
        _ocsp_response: &[u8],
        _now: rustls::pki_types::UnixTime,
    ) -> Result<ServerCertVerified, rustls::Error> {
        Ok(ServerCertVerified::assertion())
    }

    fn verify_tls12_signature(
        &self,
        _message: &[u8],
        _cert: &CertificateDer<'_>,
        _dss: &DigitallySignedStruct,
    ) -> Result<HandshakeSignatureValid, rustls::Error> {
        Ok(HandshakeSignatureValid::assertion())
    }

    fn verify_tls13_signature(
        &self,
        _message: &[u8],
        _cert: &CertificateDer<'_>,
        _dss: &DigitallySignedStruct,
    ) -> Result<HandshakeSignatureValid, rustls::Error> {
        Ok(HandshakeSignatureValid::assertion())
    }

    fn supported_verify_schemes(&self) -> Vec<SignatureScheme> {
        self.supported_schemes.clone()
    }
}

impl ActiveProcess {
    pub(crate) fn new(
        kernel_pid: u32,
        kernel_handle: KernelProcessHandle,
        runtime: GuestRuntimeKind,
        execution: ActiveExecution,
    ) -> Self {
        Self {
            kernel_pid,
            kernel_handle,
            kernel_stdin_writer_fd: None,
            runtime,
            detached: false,
            execution,
            guest_cwd: String::from("/"),
            env: BTreeMap::new(),
            host_cwd: PathBuf::from("/"),
            mapped_host_fds: BTreeMap::new(),
            next_mapped_host_fd: MAPPED_HOST_FD_START,
            pending_execution_events: VecDeque::new(),
            pending_self_signal_exit: None,
            child_processes: BTreeMap::new(),
            next_child_process_id: 0,
            http_servers: BTreeMap::new(),
            pending_http_requests: BTreeMap::new(),
            http2: Default::default(),
            tcp_listeners: BTreeMap::new(),
            next_tcp_listener_id: 0,
            tcp_sockets: BTreeMap::new(),
            next_tcp_socket_id: 0,
            tcp_port_reservations: BTreeMap::new(),
            next_tcp_port_reservation_id: 0,
            unix_listeners: BTreeMap::new(),
            next_unix_listener_id: 0,
            unix_sockets: BTreeMap::new(),
            next_unix_socket_id: 0,
            udp_sockets: BTreeMap::new(),
            next_udp_socket_id: 0,
            cipher_sessions: BTreeMap::new(),
            next_cipher_session_id: 0,
            diffie_hellman_sessions: BTreeMap::new(),
            next_diffie_hellman_session_id: 0,
            sqlite_databases: BTreeMap::new(),
            next_sqlite_database_id: 0,
            sqlite_statements: BTreeMap::new(),
            next_sqlite_statement_id: 0,
            module_resolution_cache: secure_exec_execution::LocalModuleResolutionCache::default(),
        }
    }

    pub(crate) fn queue_pending_execution_event(
        &mut self,
        event: ActiveExecutionEvent,
    ) -> Result<(), SidecarError> {
        if self.pending_execution_events.len() >= MAX_PROCESS_EVENT_QUEUE {
            return Err(process_event_queue_overflow_error());
        }
        self.pending_execution_events.push_back(event);
        Ok(())
    }

    pub(crate) fn with_host_cwd(mut self, host_cwd: PathBuf) -> Self {
        self.host_cwd = host_cwd;
        self
    }

    pub(crate) fn with_guest_cwd(mut self, guest_cwd: String) -> Self {
        self.guest_cwd = guest_cwd;
        self
    }

    pub(crate) fn with_env(mut self, env: BTreeMap<String, String>) -> Self {
        self.env = env;
        self
    }

    pub(crate) fn with_kernel_stdin_writer_fd(mut self, fd: u32) -> Self {
        self.kernel_stdin_writer_fd = Some(fd);
        self
    }

    pub(crate) fn with_detached(mut self, detached: bool) -> Self {
        self.detached = detached;
        self
    }

    pub(crate) fn allocate_mapped_host_fd(&mut self, fd: ActiveMappedHostFd) -> u32 {
        let handle = self.next_mapped_host_fd;
        self.next_mapped_host_fd = self
            .next_mapped_host_fd
            .checked_add(1)
            .unwrap_or(MAPPED_HOST_FD_START);
        self.mapped_host_fds.insert(handle, fd);
        handle
    }

    pub(crate) fn mapped_host_fd(&self, fd: u32) -> Option<&ActiveMappedHostFd> {
        self.mapped_host_fds.get(&fd)
    }

    pub(crate) fn mapped_host_fd_mut(&mut self, fd: u32) -> Option<&mut ActiveMappedHostFd> {
        self.mapped_host_fds.get_mut(&fd)
    }

    pub(crate) fn close_mapped_host_fd(&mut self, fd: u32) -> bool {
        self.mapped_host_fds.remove(&fd).is_some()
    }

    pub(crate) fn allocate_child_process_id(&mut self) -> String {
        self.next_child_process_id += 1;
        format!("child-{}", self.next_child_process_id)
    }

    fn allocate_tcp_listener_id(&mut self) -> String {
        self.next_tcp_listener_id += 1;
        format!("listener-{}", self.next_tcp_listener_id)
    }

    fn allocate_tcp_socket_id(&mut self) -> String {
        self.next_tcp_socket_id += 1;
        format!("socket-{}", self.next_tcp_socket_id)
    }

    fn allocate_tcp_port_reservation_id(&mut self) -> String {
        self.next_tcp_port_reservation_id += 1;
        format!("tcp-port-reservation-{}", self.next_tcp_port_reservation_id)
    }

    fn allocate_unix_listener_id(&mut self) -> String {
        self.next_unix_listener_id += 1;
        format!("unix-listener-{}", self.next_unix_listener_id)
    }

    fn allocate_unix_socket_id(&mut self) -> String {
        self.next_unix_socket_id += 1;
        format!("unix-socket-{}", self.next_unix_socket_id)
    }

    fn allocate_udp_socket_id(&mut self) -> String {
        self.next_udp_socket_id += 1;
        format!("udp-socket-{}", self.next_udp_socket_id)
    }

    pub(crate) fn network_resource_counts(&self) -> NetworkResourceCounts {
        let mut counts = NetworkResourceCounts {
            sockets: self.http_servers.len()
                + self.tcp_listeners.len()
                + self.tcp_sockets.len()
                + self.unix_listeners.len()
                + self.unix_sockets.len()
                + self.udp_sockets.len(),
            connections: self.tcp_sockets.len() + self.unix_sockets.len(),
        };
        if let Ok(http2) = self.http2.shared.lock() {
            counts.sockets += http2.servers.len() + http2.sessions.len();
            counts.connections += http2.sessions.len();
        }

        for child in self.child_processes.values() {
            let child_counts = child.network_resource_counts();
            counts.sockets += child_counts.sockets;
            counts.connections += child_counts.connections;
        }

        counts
    }

    fn sidecar_only_network_resource_counts(&self) -> NetworkResourceCounts {
        let mut counts = NetworkResourceCounts {
            sockets: self.http_servers.len()
                + self
                    .tcp_listeners
                    .values()
                    .filter(|listener| listener.kernel_socket_id.is_none())
                    .count()
                + self
                    .tcp_sockets
                    .values()
                    .filter(|socket| socket.kernel_socket_id.is_none())
                    .count()
                + self.unix_listeners.len()
                + self.unix_sockets.len()
                + self
                    .udp_sockets
                    .values()
                    .filter(|socket| socket.kernel_socket_id.is_none())
                    .count(),
            connections: self
                .tcp_sockets
                .values()
                .filter(|socket| socket.kernel_socket_id.is_none())
                .count()
                + self.unix_sockets.len(),
        };
        if let Ok(http2) = self.http2.shared.lock() {
            counts.sockets += http2.servers.len() + http2.sessions.len();
            counts.connections += http2.sessions.len();
        }

        for child in self.child_processes.values() {
            let child_counts = child.sidecar_only_network_resource_counts();
            counts.sockets += child_counts.sockets;
            counts.connections += child_counts.connections;
        }

        counts
    }
}

fn poll_tool_process_event(
    execution: &ToolExecution,
) -> Result<Option<ActiveExecutionEvent>, SidecarError> {
    let event = execution
        .pending_events
        .lock()
        .unwrap_or_else(|poisoned| poisoned.into_inner())
        .pop_front();
    if event.is_some() {
        return Ok(event);
    }
    if execution.events_overflowed.load(Ordering::Relaxed) {
        return Err(process_event_queue_overflow_error());
    }
    Ok(None)
}

fn descendant_pending_execution_event_capacity(
    root: &ActiveProcess,
    child_path: &[&str],
) -> Option<usize> {
    let mut child = root;
    for child_process_id in child_path {
        child = child.child_processes.get(*child_process_id)?;
    }
    Some(MAX_PROCESS_EVENT_QUEUE.saturating_sub(child.pending_execution_events.len()))
}

fn poll_child_execution_after_exit(
    child: &mut ActiveProcess,
    wait: Duration,
) -> Result<Option<ActiveExecutionEvent>, SidecarError> {
    match child.execution.poll_event_blocking(wait) {
        Ok(event) => Ok(event),
        Err(SidecarError::Execution(message))
            if child.runtime == GuestRuntimeKind::WebAssembly
                && message == WasmExecutionError::EventChannelClosed.to_string() =>
        {
            Ok(None)
        }
        Err(error) => Err(error),
    }
}

fn closed_javascript_event_channel(message: &str) -> bool {
    message == "guest JavaScript event channel closed unexpectedly"
}

fn closed_python_event_channel(message: &str) -> bool {
    message == "guest Python event channel closed unexpectedly"
}

fn closed_wasm_event_channel(message: &str) -> bool {
    message == WasmExecutionError::EventChannelClosed.to_string()
}

fn missing_vm_error(vm_id: &str) -> SidecarError {
    SidecarError::InvalidState(format!("VM {vm_id} is no longer active"))
}

fn missing_process_error(vm_id: &str, process_id: &str) -> SidecarError {
    SidecarError::InvalidState(format!(
        "VM {vm_id} no longer has active process {process_id}"
    ))
}

fn is_broken_pipe_error(error: &SidecarError) -> bool {
    matches!(error, SidecarError::Execution(message) if message.contains("Broken pipe") || message.contains("os error 32") || message.contains("EPIPE"))
}

fn javascript_child_process_gone_error(process_id: &str, child_path: &[&str]) -> SidecarError {
    let child_label = if child_path.is_empty() {
        process_id.to_owned()
    } else {
        format!("{process_id}/{}", child_path.join("/"))
    };
    SidecarError::Execution(format!(
        "ECHILD: child_process {child_label} is no longer available"
    ))
}

fn is_javascript_child_process_gone_error(error: &SidecarError) -> bool {
    matches!(
        error,
        SidecarError::Execution(message) if guest_errno_code(message) == Some("ECHILD")
    )
}

fn loopback_tls_transport_registry(
) -> &'static Mutex<BTreeMap<String, Weak<crate::state::LoopbackTlsTransportPair>>> {
    static REGISTRY: OnceLock<
        Mutex<BTreeMap<String, Weak<crate::state::LoopbackTlsTransportPair>>>,
    > = OnceLock::new();
    REGISTRY.get_or_init(|| Mutex::new(BTreeMap::new()))
}

fn loopback_tls_transport_key(
    vm_id: &str,
    socket_id: SocketId,
    peer_socket_id: SocketId,
) -> String {
    let (lower, higher) = if socket_id <= peer_socket_id {
        (socket_id, peer_socket_id)
    } else {
        (peer_socket_id, socket_id)
    };
    format!("{vm_id}:{lower}:{higher}")
}

fn loopback_tls_endpoint(
    vm_id: &str,
    socket_id: SocketId,
    peer_socket_id: SocketId,
) -> Result<crate::state::LoopbackTlsEndpoint, SidecarError> {
    let key = loopback_tls_transport_key(vm_id, socket_id, peer_socket_id);
    let registry = loopback_tls_transport_registry();
    let mut transports = registry.lock().map_err(|_| {
        SidecarError::InvalidState(String::from(
            "loopback TLS transport registry lock poisoned",
        ))
    })?;
    transports.retain(|_, pair| pair.strong_count() > 0);
    let pair = transports
        .get(&key)
        .and_then(Weak::upgrade)
        .unwrap_or_else(|| {
            let pair = Arc::new(crate::state::LoopbackTlsTransportPair {
                state: Mutex::new(crate::state::LoopbackTlsTransportPairState::default()),
                ready: std::sync::Condvar::new(),
            });
            transports.insert(key, Arc::downgrade(&pair));
            pair
        });
    Ok(crate::state::LoopbackTlsEndpoint {
        pair,
        is_lower_socket: socket_id <= peer_socket_id,
    })
}

impl crate::state::LoopbackTlsEndpoint {
    fn shutdown_write(&self) -> Result<(), SidecarError> {
        let mut state = self.pair.state.lock().map_err(|_| {
            SidecarError::InvalidState(String::from("loopback TLS transport lock poisoned"))
        })?;
        if self.is_lower_socket {
            state.lower_write_closed = true;
        } else {
            state.higher_write_closed = true;
        }
        self.pair.ready.notify_all();
        Ok(())
    }

    fn close_endpoint(&self) -> Result<(), SidecarError> {
        let mut state = self.pair.state.lock().map_err(|_| {
            SidecarError::InvalidState(String::from("loopback TLS transport lock poisoned"))
        })?;
        if self.is_lower_socket {
            state.lower_write_closed = true;
            state.lower_closed = true;
        } else {
            state.higher_write_closed = true;
            state.higher_closed = true;
        }
        self.pair.ready.notify_all();
        Ok(())
    }
}

fn parse_tls_client_hello_from_bytes(
    buffer: &[u8],
) -> Result<Option<JavascriptTlsClientHello>, SidecarError> {
    if buffer.is_empty() {
        return Ok(None);
    }

    let mut acceptor = rustls::server::Acceptor::default();
    let mut cursor = Cursor::new(buffer);
    acceptor.read_tls(&mut cursor).map_err(sidecar_net_error)?;
    let Some(accepted) = acceptor.accept().map_err(|(error, _)| {
        SidecarError::Execution(format!("failed to parse TLS client hello: {error}"))
    })?
    else {
        return Ok(None);
    };
    let client_hello = accepted.client_hello();
    let alpn_protocols = client_hello.alpn().map(|protocols| {
        protocols
            .filter_map(|protocol| String::from_utf8(protocol.to_vec()).ok())
            .collect::<Vec<_>>()
    });
    Ok(Some(JavascriptTlsClientHello {
        servername: client_hello.server_name().map(str::to_owned),
        alpn_protocols,
    }))
}

fn peek_loopback_tls_client_hello(
    vm_id: &str,
    socket_id: SocketId,
    peer_socket_id: SocketId,
) -> Result<Option<JavascriptTlsClientHello>, SidecarError> {
    let key = loopback_tls_transport_key(vm_id, socket_id, peer_socket_id);
    let registry = loopback_tls_transport_registry();
    let pair = registry
        .lock()
        .map_err(|_| {
            SidecarError::InvalidState(String::from(
                "loopback TLS transport registry lock poisoned",
            ))
        })?
        .get(&key)
        .and_then(Weak::upgrade);
    let Some(pair) = pair else {
        return Ok(None);
    };
    let is_lower_socket = socket_id <= peer_socket_id;
    let state = pair.state.lock().map_err(|_| {
        SidecarError::InvalidState(String::from("loopback TLS transport lock poisoned"))
    })?;
    let buffered = if is_lower_socket {
        state.higher_to_lower.iter().copied().collect::<Vec<_>>()
    } else {
        state.lower_to_higher.iter().copied().collect::<Vec<_>>()
    };
    drop(state);
    parse_tls_client_hello_from_bytes(&buffered)
}

fn wait_for_loopback_peer_socket_id(
    kernel: &SidecarKernel,
    socket_id: SocketId,
) -> Option<SocketId> {
    for _ in 0..50 {
        if let Some(peer_socket_id) = kernel
            .socket_get(socket_id)
            .and_then(|record| record.peer_socket_id())
        {
            return Some(peer_socket_id);
        }
        std::thread::sleep(Duration::from_millis(10));
    }
    None
}

impl Drop for crate::state::LoopbackTlsEndpoint {
    fn drop(&mut self) {
        let _ = self.close_endpoint();
    }
}

impl Read for crate::state::LoopbackTlsEndpoint {
    fn read(&mut self, buffer: &mut [u8]) -> std::io::Result<usize> {
        let mut state = self
            .pair
            .state
            .lock()
            .map_err(|_| std::io::Error::other("loopback TLS transport lock poisoned"))?;

        loop {
            let (peer_write_closed, peer_closed) = if self.is_lower_socket {
                (state.higher_write_closed, state.higher_closed)
            } else {
                (state.lower_write_closed, state.lower_closed)
            };

            let incoming = if self.is_lower_socket {
                &mut state.higher_to_lower
            } else {
                &mut state.lower_to_higher
            };

            if !incoming.is_empty() {
                let mut count = 0;
                while count < buffer.len() {
                    let Some(byte) = incoming.pop_front() else {
                        break;
                    };
                    buffer[count] = byte;
                    count += 1;
                }
                return Ok(count);
            }

            if peer_write_closed || peer_closed {
                return Ok(0);
            }

            let (next_state, wait_result) = self
                .pair
                .ready
                .wait_timeout(state, TCP_SOCKET_POLL_TIMEOUT)
                .map_err(|_| std::io::Error::other("loopback TLS transport lock poisoned"))?;
            state = next_state;
            if wait_result.timed_out() {
                return Err(std::io::Error::new(
                    std::io::ErrorKind::WouldBlock,
                    "loopback TLS transport read timed out",
                ));
            }
        }
    }
}

impl Write for crate::state::LoopbackTlsEndpoint {
    fn write(&mut self, buffer: &[u8]) -> std::io::Result<usize> {
        let mut state = self
            .pair
            .state
            .lock()
            .map_err(|_| std::io::Error::other("loopback TLS transport lock poisoned"))?;

        let peer_closed = if self.is_lower_socket {
            state.higher_closed
        } else {
            state.lower_closed
        };
        let outgoing = if self.is_lower_socket {
            &mut state.lower_to_higher
        } else {
            &mut state.higher_to_lower
        };
        if peer_closed {
            return Err(std::io::Error::new(
                std::io::ErrorKind::BrokenPipe,
                "loopback TLS peer is closed",
            ));
        }

        outgoing.extend(buffer.iter().copied());
        self.pair.ready.notify_all();
        Ok(buffer.len())
    }

    fn flush(&mut self) -> std::io::Result<()> {
        Ok(())
    }
}

// TCP types moved to crate::state

struct ActiveTcpConnectRequest<'a, B> {
    bridge: &'a SharedBridge<B>,
    kernel: &'a mut SidecarKernel,
    kernel_pid: u32,
    vm_id: &'a str,
    dns: &'a VmDnsConfig,
    host: &'a str,
    port: u16,
    local_address: Option<&'a str>,
    local_port: Option<u16>,
    local_reservation: Option<(JavascriptSocketFamily, u16)>,
    context: &'a JavascriptSocketPathContext,
}

struct ActiveUdpSendToRequest<'a, B> {
    bridge: &'a SharedBridge<B>,
    kernel: &'a mut SidecarKernel,
    kernel_pid: u32,
    vm_id: &'a str,
    dns: &'a VmDnsConfig,
    host: &'a str,
    port: u16,
    context: &'a JavascriptSocketPathContext,
    contents: &'a [u8],
}

struct UdpRemoteAddrRequest<'a, B> {
    bridge: &'a SharedBridge<B>,
    kernel: &'a SidecarKernel,
    vm_id: &'a str,
    dns: &'a VmDnsConfig,
    host: &'a str,
    port: u16,
    family: JavascriptUdpFamily,
    context: &'a JavascriptSocketPathContext,
}

pub(crate) struct JavascriptSyncRpcServiceRequest<'a, B> {
    pub(crate) bridge: &'a SharedBridge<B>,
    pub(crate) vm_id: &'a str,
    pub(crate) dns: &'a VmDnsConfig,
    pub(crate) socket_paths: &'a JavascriptSocketPathContext,
    pub(crate) kernel: &'a mut SidecarKernel,
    pub(crate) process: &'a mut ActiveProcess,
    pub(crate) sync_request: &'a JavascriptSyncRpcRequest,
    pub(crate) resource_limits: &'a ResourceLimits,
    pub(crate) network_counts: NetworkResourceCounts,
}

pub(crate) struct JavascriptNetSyncRpcServiceRequest<'a, B> {
    pub(crate) bridge: &'a SharedBridge<B>,
    pub(crate) vm_id: &'a str,
    pub(crate) dns: &'a VmDnsConfig,
    pub(crate) socket_paths: &'a JavascriptSocketPathContext,
    pub(crate) kernel: &'a mut SidecarKernel,
    pub(crate) process: &'a mut ActiveProcess,
    pub(crate) sync_request: &'a JavascriptSyncRpcRequest,
    pub(crate) resource_limits: &'a ResourceLimits,
    pub(crate) network_counts: NetworkResourceCounts,
}

struct LoopbackHttpResponseWaitRequest<'a, B> {
    bridge: &'a SharedBridge<B>,
    vm_id: &'a str,
    dns: &'a VmDnsConfig,
    socket_paths: &'a JavascriptSocketPathContext,
    kernel: &'a mut SidecarKernel,
    process: &'a mut ActiveProcess,
    resource_limits: &'a ResourceLimits,
    request_key: (u64, u64),
}

pub(crate) struct LoopbackHttpDispatchRequest<'a, B> {
    pub(crate) bridge: &'a SharedBridge<B>,
    pub(crate) vm_id: &'a str,
    pub(crate) dns: &'a VmDnsConfig,
    pub(crate) socket_paths: &'a JavascriptSocketPathContext,
    pub(crate) kernel: &'a mut SidecarKernel,
    pub(crate) process: &'a mut ActiveProcess,
    pub(crate) resource_limits: &'a ResourceLimits,
    pub(crate) server_id: u64,
    pub(crate) request_json: &'a str,
}

struct JavascriptDgramSyncRpcServiceRequest<'a, B> {
    bridge: &'a SharedBridge<B>,
    kernel: &'a mut SidecarKernel,
    vm_id: &'a str,
    dns: &'a VmDnsConfig,
    socket_paths: &'a JavascriptSocketPathContext,
    process: &'a mut ActiveProcess,
    sync_request: &'a JavascriptSyncRpcRequest,
    resource_limits: &'a ResourceLimits,
    network_counts: NetworkResourceCounts,
}

struct JavascriptHttp2SyncRpcServiceRequest<'a, B> {
    bridge: &'a SharedBridge<B>,
    kernel: &'a mut SidecarKernel,
    vm_id: &'a str,
    dns: &'a VmDnsConfig,
    socket_paths: &'a JavascriptSocketPathContext,
    process: &'a mut ActiveProcess,
    sync_request: &'a JavascriptSyncRpcRequest,
    resource_limits: &'a ResourceLimits,
    network_counts: NetworkResourceCounts,
}

impl ActiveTcpSocket {
    fn connect<B>(request: ActiveTcpConnectRequest<'_, B>) -> Result<Self, SidecarError>
    where
        B: NativeSidecarBridge + Send + 'static,
        BridgeError<B>: fmt::Debug + Send + Sync + 'static,
    {
        let ActiveTcpConnectRequest {
            bridge,
            kernel,
            kernel_pid,
            vm_id,
            dns,
            host,
            port,
            local_address,
            local_port,
            local_reservation,
            context,
        } = request;
        let resolved = resolve_tcp_connect_addr(bridge, kernel, vm_id, dns, host, port, context)?;
        if resolved.use_kernel_loopback {
            let family = JavascriptSocketFamily::from_ip(resolved.guest_remote_addr.ip());
            let requested_local_port = local_port.unwrap_or(0);
            let local_port = if requested_local_port != 0
                && local_reservation == Some((family, requested_local_port))
            {
                requested_local_port
            } else {
                allocate_guest_listen_port(
                    requested_local_port,
                    family,
                    &context.used_tcp_guest_ports,
                    context.listen_policy,
                )?
            };
            let local_ip = match (family, local_address) {
                (JavascriptSocketFamily::Ipv4, Some("0.0.0.0")) => {
                    IpAddr::V4(Ipv4Addr::UNSPECIFIED)
                }
                (JavascriptSocketFamily::Ipv4, Some("127.0.0.1") | Some("localhost") | None) => {
                    IpAddr::V4(Ipv4Addr::LOCALHOST)
                }
                (JavascriptSocketFamily::Ipv6, Some("::")) => IpAddr::V6(Ipv6Addr::UNSPECIFIED),
                (JavascriptSocketFamily::Ipv6, Some("::1") | Some("localhost") | None) => {
                    IpAddr::V6(Ipv6Addr::LOCALHOST)
                }
                (JavascriptSocketFamily::Ipv4, Some(other)) => {
                    return Err(SidecarError::Execution(format!(
                        "EACCES: TCP sockets must bind to loopback or unspecified addresses, got {other}"
                    )));
                }
                (JavascriptSocketFamily::Ipv6, Some(other)) => {
                    return Err(SidecarError::Execution(format!(
                        "EACCES: TCP sockets must bind to loopback or unspecified addresses, got {other}"
                    )));
                }
            };
            let local_addr = SocketAddr::new(local_ip, local_port);
            let spec = match family {
                JavascriptSocketFamily::Ipv4 => SocketSpec::tcp(),
                JavascriptSocketFamily::Ipv6 => {
                    SocketSpec::new(SocketDomain::Inet6, SocketType::Stream)
                }
            };
            let socket_id = kernel
                .socket_create(EXECUTION_DRIVER_NAME, kernel_pid, spec)
                .map_err(kernel_error)?;
            kernel
                .socket_bind_inet(
                    EXECUTION_DRIVER_NAME,
                    kernel_pid,
                    socket_id,
                    InetSocketAddress::new(local_ip.to_string(), local_port),
                )
                .map_err(kernel_error)?;
            kernel
                .socket_connect_inet_loopback(
                    EXECUTION_DRIVER_NAME,
                    kernel_pid,
                    socket_id,
                    InetSocketAddress::new(
                        resolved.guest_remote_addr.ip().to_string(),
                        resolved.guest_remote_addr.port(),
                    ),
                )
                .map_err(kernel_error)?;
            return Ok(Self::from_kernel(
                socket_id,
                None,
                local_addr,
                resolved.guest_remote_addr,
            ));
        }

        let stream = TcpStream::connect_timeout(&resolved.actual_addr, Duration::from_secs(30))
            .map_err(sidecar_net_error)?;
        let guest_local_addr = stream.local_addr().map_err(sidecar_net_error)?;
        Self::from_stream(stream, None, guest_local_addr, resolved.guest_remote_addr)
    }

    fn from_stream(
        stream: TcpStream,
        listener_id: Option<String>,
        guest_local_addr: SocketAddr,
        guest_remote_addr: SocketAddr,
    ) -> Result<Self, SidecarError> {
        let read_stream = stream.try_clone().map_err(sidecar_net_error)?;
        read_stream
            .set_read_timeout(Some(TCP_SOCKET_POLL_TIMEOUT))
            .map_err(sidecar_net_error)?;
        let stream = Arc::new(Mutex::new(stream));
        let pending_read_stream = Arc::new(Mutex::new(Some(read_stream)));
        let (sender, events) = mpsc::channel();
        let tls_mode = Arc::new(AtomicBool::new(false));
        let tls_stream = Arc::new(Mutex::new(None));
        let tls_state = Arc::new(Mutex::new(None));
        let saw_local_shutdown = Arc::new(AtomicBool::new(false));
        let saw_remote_end = Arc::new(AtomicBool::new(false));
        let close_notified = Arc::new(AtomicBool::new(false));

        Ok(Self {
            stream: Some(stream),
            pending_read_stream: Some(pending_read_stream),
            events: Some(events),
            event_sender: Some(sender),
            kernel_socket_id: None,
            no_delay: false,
            keep_alive: false,
            keep_alive_initial_delay_secs: None,
            guest_local_addr,
            guest_remote_addr,
            listener_id,
            tls_mode,
            tls_stream,
            tls_state,
            saw_local_shutdown,
            saw_remote_end,
            close_notified,
        })
    }

    fn from_kernel(
        socket_id: SocketId,
        listener_id: Option<String>,
        guest_local_addr: SocketAddr,
        guest_remote_addr: SocketAddr,
    ) -> Self {
        let (sender, events) = mpsc::channel();
        Self {
            stream: None,
            pending_read_stream: None,
            events: Some(events),
            event_sender: Some(sender),
            kernel_socket_id: Some(socket_id),
            no_delay: false,
            keep_alive: false,
            keep_alive_initial_delay_secs: None,
            guest_local_addr,
            guest_remote_addr,
            listener_id,
            tls_mode: Arc::new(AtomicBool::new(false)),
            tls_stream: Arc::new(Mutex::new(None)),
            tls_state: Arc::new(Mutex::new(None)),
            saw_local_shutdown: Arc::new(AtomicBool::new(false)),
            saw_remote_end: Arc::new(AtomicBool::new(false)),
            close_notified: Arc::new(AtomicBool::new(false)),
        }
    }

    fn poll(
        &mut self,
        kernel: &mut SidecarKernel,
        kernel_pid: u32,
        wait: Duration,
    ) -> Result<Option<JavascriptTcpSocketEvent>, SidecarError> {
        if self.tls_mode.load(Ordering::SeqCst) {
            self.ensure_tcp_reader()?;
            return match self
                .events
                .as_ref()
                .ok_or_else(|| {
                    SidecarError::InvalidState(String::from("TCP socket event channel missing"))
                })?
                .recv_timeout(wait)
            {
                Ok(event) => Ok(Some(event)),
                Err(RecvTimeoutError::Timeout) => Ok(None),
                Err(RecvTimeoutError::Disconnected) => Ok(None),
            };
        }

        if let Some(socket_id) = self.kernel_socket_id {
            let result = kernel
                .poll_targets(
                    EXECUTION_DRIVER_NAME,
                    kernel_pid,
                    vec![PollTargetEntry::socket(
                        socket_id,
                        POLLIN | POLLHUP | POLLERR,
                    )],
                    i32::try_from(wait.as_millis()).unwrap_or(i32::MAX),
                )
                .map_err(kernel_error)?;
            let revents = result
                .targets
                .first()
                .map(|entry| entry.revents)
                .unwrap_or_else(PollEvents::empty);
            if revents.is_empty() {
                return Ok(None);
            }
            if revents.intersects(POLLIN) {
                return match kernel.socket_read(
                    EXECUTION_DRIVER_NAME,
                    kernel_pid,
                    socket_id,
                    64 * 1024,
                ) {
                    Ok(Some(bytes)) if !bytes.is_empty() => {
                        Ok(Some(JavascriptTcpSocketEvent::Data(bytes)))
                    }
                    Ok(Some(_)) => Ok(Some(JavascriptTcpSocketEvent::Data(Vec::new()))),
                    Ok(None) => Ok(Some(JavascriptTcpSocketEvent::End)),
                    Err(error) if error.code() == "EAGAIN" => Ok(None),
                    Err(error) => Ok(Some(JavascriptTcpSocketEvent::Error {
                        code: Some(error.code().to_string()),
                        message: error.to_string(),
                    })),
                };
            }
            if revents.intersects(POLLHUP) {
                return Ok(Some(JavascriptTcpSocketEvent::End));
            }
            if revents.intersects(POLLERR) {
                return Ok(Some(JavascriptTcpSocketEvent::Error {
                    code: Some(String::from("EPIPE")),
                    message: String::from("kernel TCP socket reported POLLERR"),
                }));
            }
            return Ok(None);
        }

        self.ensure_tcp_reader()?;
        match self
            .events
            .as_ref()
            .ok_or_else(|| {
                SidecarError::InvalidState(String::from("TCP socket event channel missing"))
            })?
            .recv_timeout(wait)
        {
            Ok(event) => Ok(Some(event)),
            Err(RecvTimeoutError::Timeout) => Ok(None),
            Err(RecvTimeoutError::Disconnected) => Ok(None),
        }
    }

    fn ensure_tcp_reader(&self) -> Result<(), SidecarError> {
        if self.kernel_socket_id.is_some() {
            return Ok(());
        }
        if self.tls_mode.load(Ordering::SeqCst) {
            return Ok(());
        }
        let read_stream = self
            .pending_read_stream
            .as_ref()
            .ok_or_else(|| {
                SidecarError::InvalidState(String::from("TCP socket reader handle missing"))
            })?
            .lock()
            .map_err(|_| {
                SidecarError::InvalidState(String::from("TCP socket reader lock poisoned"))
            })?
            .take();
        if let Some(read_stream) = read_stream {
            spawn_tcp_socket_reader(
                read_stream,
                self.event_sender
                    .as_ref()
                    .ok_or_else(|| {
                        SidecarError::InvalidState(String::from("TCP socket event sender missing"))
                    })?
                    .clone(),
                Arc::clone(&self.tls_mode),
                Arc::clone(&self.saw_local_shutdown),
                Arc::clone(&self.saw_remote_end),
                Arc::clone(&self.close_notified),
            );
        }
        Ok(())
    }

    fn socket_info(&self) -> Value {
        json!({
            "localAddress": self.guest_local_addr.ip().to_string(),
            "localPort": self.guest_local_addr.port(),
            "localFamily": socket_addr_family(&self.guest_local_addr),
            "remoteAddress": self.guest_remote_addr.ip().to_string(),
            "remotePort": self.guest_remote_addr.port(),
            "remoteFamily": socket_addr_family(&self.guest_remote_addr),
        })
    }

    fn set_no_delay(&mut self, enable: bool) -> Result<(), SidecarError> {
        self.no_delay = enable;
        if self.kernel_socket_id.is_some() {
            return Ok(());
        }
        let stream = self
            .stream
            .as_ref()
            .ok_or_else(|| SidecarError::InvalidState(String::from("TCP socket stream missing")))?
            .lock()
            .map_err(|_| SidecarError::InvalidState(String::from("TCP socket lock poisoned")))?;
        stream.set_nodelay(enable).map_err(sidecar_net_error)
    }

    fn set_keep_alive(
        &mut self,
        enable: bool,
        initial_delay_secs: Option<u64>,
    ) -> Result<(), SidecarError> {
        self.keep_alive = enable;
        self.keep_alive_initial_delay_secs = initial_delay_secs;
        if self.kernel_socket_id.is_some() {
            return Ok(());
        }
        let stream = self
            .stream
            .as_ref()
            .ok_or_else(|| SidecarError::InvalidState(String::from("TCP socket stream missing")))?
            .lock()
            .map_err(|_| SidecarError::InvalidState(String::from("TCP socket lock poisoned")))?;
        let socket = SockRef::from(&*stream);
        socket.set_keepalive(enable).map_err(sidecar_net_error)?;
        if enable {
            if let Some(delay_secs) = initial_delay_secs.filter(|delay_secs| *delay_secs > 0) {
                socket
                    .set_tcp_keepalive(
                        &TcpKeepalive::new().with_time(Duration::from_secs(delay_secs)),
                    )
                    .map_err(sidecar_net_error)?;
            }
        }
        Ok(())
    }

    fn upgrade_tls(
        &self,
        vm_id: &str,
        kernel: &SidecarKernel,
        options: JavascriptTlsBridgeOptions,
    ) -> Result<(), SidecarError> {
        if self.tls_mode.load(Ordering::SeqCst) {
            return Ok(());
        }

        let client_hello = if options.is_server {
            self.peek_tls_client_hello(vm_id, kernel)?
        } else {
            None
        };

        let tls_stream = if let Some(socket_id) = self.kernel_socket_id {
            let peer_socket_id = wait_for_loopback_peer_socket_id(kernel, socket_id)
                .ok_or_else(|| {
                    SidecarError::Execution(format!(
                        "ERR_NOT_IMPLEMENTED: kernel-backed loopback socket {socket_id} has no peer for TLS upgrade"
                    ))
                })?;
            let endpoint = loopback_tls_endpoint(vm_id, socket_id, peer_socket_id)?;
            if options.is_server {
                ActiveTlsStream::LoopbackServer(build_server_loopback_tls_stream(
                    endpoint, &options,
                )?)
            } else {
                ActiveTlsStream::LoopbackClient(build_client_loopback_tls_stream(
                    endpoint, &options,
                )?)
            }
        } else {
            self.pending_read_stream
                .as_ref()
                .ok_or_else(|| {
                    SidecarError::InvalidState(String::from("TCP socket reader handle missing"))
                })?
                .lock()
                .map_err(|_| {
                    SidecarError::InvalidState(String::from("TCP socket reader lock poisoned"))
                })?
                .take();
            let stream = self
                .stream
                .as_ref()
                .ok_or_else(|| {
                    SidecarError::InvalidState(String::from("TCP socket stream missing"))
                })?
                .lock()
                .map_err(|_| {
                    SidecarError::InvalidState(String::from("TCP socket lock poisoned"))
                })?;
            let cloned = stream.try_clone().map_err(sidecar_net_error)?;
            drop(stream);

            if options.is_server {
                ActiveTlsStream::Server(build_server_tls_stream(cloned, &options)?)
            } else {
                ActiveTlsStream::Client(build_client_tls_stream(cloned, &options)?)
            }
        };

        let tls_state = ActiveTlsState {
            client_hello,
            local_certificates: tls_local_certificates(&options)?,
            session_reused: false,
        };

        self.tls_mode.store(true, Ordering::SeqCst);
        {
            let mut state = self
                .tls_state
                .lock()
                .map_err(|_| SidecarError::InvalidState(String::from("TLS state lock poisoned")))?;
            *state = Some(tls_state);
        }
        {
            let mut stream = self.tls_stream.lock().map_err(|_| {
                SidecarError::InvalidState(String::from("TLS stream lock poisoned"))
            })?;
            *stream = Some(tls_stream);
        }

        spawn_tls_socket_reader(
            Arc::clone(&self.tls_stream),
            self.event_sender
                .as_ref()
                .ok_or_else(|| {
                    SidecarError::InvalidState(String::from("TCP socket event sender missing"))
                })?
                .clone(),
            Arc::clone(&self.saw_local_shutdown),
            Arc::clone(&self.saw_remote_end),
            Arc::clone(&self.close_notified),
        );
        Ok(())
    }

    fn peek_tls_client_hello(
        &self,
        vm_id: &str,
        kernel: &SidecarKernel,
    ) -> Result<Option<JavascriptTlsClientHello>, SidecarError> {
        if let Some(socket_id) = self.kernel_socket_id {
            let Some(peer_socket_id) = kernel
                .socket_get(socket_id)
                .and_then(|record| record.peer_socket_id())
            else {
                return Ok(None);
            };
            return peek_loopback_tls_client_hello(vm_id, socket_id, peer_socket_id);
        }

        let stream = self
            .stream
            .as_ref()
            .ok_or_else(|| SidecarError::InvalidState(String::from("TCP socket stream missing")))?
            .lock()
            .map_err(|_| SidecarError::InvalidState(String::from("TCP socket lock poisoned")))?;
        let mut buffer = vec![0_u8; 16 * 1024];
        let bytes = match stream.peek(&mut buffer) {
            Ok(0) => return Ok(None),
            Ok(bytes) => bytes,
            Err(error)
                if matches!(
                    error.kind(),
                    std::io::ErrorKind::WouldBlock | std::io::ErrorKind::TimedOut
                ) =>
            {
                return Ok(None);
            }
            Err(error) => return Err(sidecar_net_error(error)),
        };
        parse_tls_client_hello_from_bytes(&buffer[..bytes])
    }

    fn tls_client_hello_json(
        &self,
        vm_id: &str,
        kernel: &SidecarKernel,
    ) -> Result<Value, SidecarError> {
        if let Some(client_hello) = self
            .tls_state
            .lock()
            .map_err(|_| SidecarError::InvalidState(String::from("TLS state lock poisoned")))?
            .as_ref()
            .and_then(|state| state.client_hello.clone())
        {
            return javascript_net_json_string(
                serde_json::to_value(client_hello).map_err(|error| {
                    SidecarError::InvalidState(format!(
                        "failed to serialize TLS client hello: {error}"
                    ))
                })?,
                "net.socket_get_tls_client_hello",
            );
        }

        javascript_net_json_string(
            serde_json::to_value(
                self.peek_tls_client_hello(vm_id, kernel)?
                    .unwrap_or_default(),
            )
            .map_err(|error| {
                SidecarError::InvalidState(format!("failed to serialize TLS client hello: {error}"))
            })?,
            "net.socket_get_tls_client_hello",
        )
    }

    fn tls_query(&self, query: &str, detailed: bool) -> Result<Value, SidecarError> {
        let state = self
            .tls_state
            .lock()
            .map_err(|_| SidecarError::InvalidState(String::from("TLS state lock poisoned")))?
            .clone();
        let mut tls_stream = self
            .tls_stream
            .lock()
            .map_err(|_| SidecarError::InvalidState(String::from("TLS stream lock poisoned")))?;
        let Some(stream) = tls_stream.as_mut() else {
            return javascript_net_json_string(
                tls_bridge_undefined_value(),
                "net.socket_tls_query",
            );
        };

        let payload = match query {
            "getSession" => tls_bridge_undefined_value(),
            "isSessionReused" => Value::Bool(
                state
                    .as_ref()
                    .is_some_and(|tls_state| tls_state.session_reused),
            ),
            "getPeerCertificate" => {
                let certificate = stream
                    .peer_certificates()
                    .and_then(|certificates| certificates.first())
                    .map(|certificate| {
                        tls_certificate_bridge_value(certificate.as_ref(), detailed)
                    });
                certificate.unwrap_or_else(tls_bridge_undefined_value)
            }
            "getCertificate" => state
                .as_ref()
                .and_then(|tls_state| tls_state.local_certificates.first())
                .map(|certificate| tls_certificate_bridge_value(certificate, detailed))
                .unwrap_or_else(tls_bridge_undefined_value),
            "getProtocol" => stream
                .protocol_version()
                .map(tls_protocol_name)
                .map(Value::String)
                .unwrap_or(Value::Null),
            "getCipher" => stream
                .negotiated_cipher_suite()
                .map(tls_cipher_bridge_value)
                .unwrap_or_else(tls_bridge_undefined_value),
            other => {
                return Err(SidecarError::InvalidState(format!(
                    "unsupported TLS query {other}"
                )));
            }
        };
        javascript_net_json_string(payload, "net.socket_tls_query")
    }

    fn write_all(
        &self,
        kernel: &mut SidecarKernel,
        kernel_pid: u32,
        contents: &[u8],
    ) -> Result<usize, SidecarError> {
        if self.tls_mode.load(Ordering::SeqCst) {
            let mut tls_stream = self.tls_stream.lock().map_err(|_| {
                SidecarError::InvalidState(String::from("TLS stream lock poisoned"))
            })?;
            let stream = tls_stream.as_mut().ok_or_else(|| {
                SidecarError::InvalidState(String::from("TLS stream missing for upgraded socket"))
            })?;
            stream.write_all(contents)?;
            return Ok(contents.len());
        }
        if let Some(socket_id) = self.kernel_socket_id {
            return kernel
                .socket_write(EXECUTION_DRIVER_NAME, kernel_pid, socket_id, contents)
                .map_err(kernel_error);
        }

        let mut stream = self
            .stream
            .as_ref()
            .ok_or_else(|| SidecarError::InvalidState(String::from("TCP socket stream missing")))?
            .lock()
            .map_err(|_| SidecarError::InvalidState(String::from("TCP socket lock poisoned")))?;
        stream.write_all(contents).map_err(sidecar_net_error)?;
        Ok(contents.len())
    }

    fn shutdown_write(
        &self,
        kernel: &mut SidecarKernel,
        kernel_pid: u32,
    ) -> Result<(), SidecarError> {
        if self.tls_mode.load(Ordering::SeqCst) {
            if let Some(stream) = self
                .tls_stream
                .lock()
                .map_err(|_| SidecarError::InvalidState(String::from("TLS stream lock poisoned")))?
                .as_mut()
            {
                let _ = stream.send_close_notify();
                let _ = stream.shutdown_write();
            }
            if self.kernel_socket_id.is_some() {
                self.saw_local_shutdown.store(true, Ordering::SeqCst);
                return Ok(());
            }
        }
        if let Some(socket_id) = self.kernel_socket_id {
            return kernel
                .socket_shutdown(
                    EXECUTION_DRIVER_NAME,
                    kernel_pid,
                    socket_id,
                    KernelSocketShutdown::Write,
                )
                .map_err(kernel_error);
        }
        let stream = self
            .stream
            .as_ref()
            .ok_or_else(|| SidecarError::InvalidState(String::from("TCP socket stream missing")))?
            .lock()
            .map_err(|_| SidecarError::InvalidState(String::from("TCP socket lock poisoned")))?;
        self.saw_local_shutdown.store(true, Ordering::SeqCst);
        match stream.shutdown(Shutdown::Write) {
            Ok(()) => {}
            Err(error) if error.kind() == std::io::ErrorKind::NotConnected => {}
            Err(error) => return Err(sidecar_net_error(error)),
        }
        if self.saw_remote_end.load(Ordering::SeqCst)
            && !self.close_notified.swap(true, Ordering::SeqCst)
        {
            let _ = self
                .event_sender
                .as_ref()
                .ok_or_else(|| {
                    SidecarError::InvalidState(String::from("TCP socket event sender missing"))
                })?
                .send(JavascriptTcpSocketEvent::Close { had_error: false });
        }
        Ok(())
    }

    fn close(&self, kernel: &mut SidecarKernel, kernel_pid: u32) -> Result<(), SidecarError> {
        if self.tls_mode.load(Ordering::SeqCst) {
            if let Some(stream) = self
                .tls_stream
                .lock()
                .map_err(|_| SidecarError::InvalidState(String::from("TLS stream lock poisoned")))?
                .as_mut()
            {
                let _ = stream.send_close_notify();
                let _ = stream.close();
            }
            if self.kernel_socket_id.is_some() {
                return Ok(());
            }
        }
        if let Some(socket_id) = self.kernel_socket_id {
            return kernel
                .socket_close(EXECUTION_DRIVER_NAME, kernel_pid, socket_id)
                .map_err(kernel_error);
        }
        let stream = self
            .stream
            .as_ref()
            .ok_or_else(|| SidecarError::InvalidState(String::from("TCP socket stream missing")))?
            .lock()
            .map_err(|_| SidecarError::InvalidState(String::from("TCP socket lock poisoned")))?;
        stream.shutdown(Shutdown::Both).map_err(sidecar_net_error)
    }
}

impl ActiveTlsStream {
    fn write_all(&mut self, contents: &[u8]) -> Result<(), SidecarError> {
        match self {
            Self::Client(stream) => {
                stream.write_all(contents).map_err(sidecar_net_error)?;
                stream.flush().map_err(sidecar_net_error)
            }
            Self::Server(stream) => {
                stream.write_all(contents).map_err(sidecar_net_error)?;
                stream.flush().map_err(sidecar_net_error)
            }
            Self::LoopbackClient(stream) => {
                stream.write_all(contents).map_err(sidecar_net_error)?;
                stream.flush().map_err(sidecar_net_error)
            }
            Self::LoopbackServer(stream) => {
                stream.write_all(contents).map_err(sidecar_net_error)?;
                stream.flush().map_err(sidecar_net_error)
            }
        }
    }

    fn read(&mut self, buffer: &mut [u8]) -> std::io::Result<usize> {
        match self {
            Self::Client(stream) => stream.read(buffer),
            Self::Server(stream) => stream.read(buffer),
            Self::LoopbackClient(stream) => stream.read(buffer),
            Self::LoopbackServer(stream) => stream.read(buffer),
        }
    }

    fn send_close_notify(&mut self) -> Result<(), SidecarError> {
        match self {
            Self::Client(stream) => {
                stream.conn.send_close_notify();
                let _ = stream.conn.complete_io(&mut stream.sock);
            }
            Self::Server(stream) => {
                stream.conn.send_close_notify();
                let _ = stream.conn.complete_io(&mut stream.sock);
            }
            Self::LoopbackClient(stream) => {
                stream.conn.send_close_notify();
                let _ = stream.conn.complete_io(&mut stream.sock);
            }
            Self::LoopbackServer(stream) => {
                stream.conn.send_close_notify();
                let _ = stream.conn.complete_io(&mut stream.sock);
            }
        }
        Ok(())
    }

    fn shutdown_write(&mut self) -> Result<(), SidecarError> {
        match self {
            Self::Client(stream) => stream
                .sock
                .shutdown(Shutdown::Write)
                .map_err(sidecar_net_error),
            Self::Server(stream) => stream
                .sock
                .shutdown(Shutdown::Write)
                .map_err(sidecar_net_error),
            Self::LoopbackClient(stream) => stream.sock.shutdown_write(),
            Self::LoopbackServer(stream) => stream.sock.shutdown_write(),
        }
    }

    fn close(&mut self) -> Result<(), SidecarError> {
        match self {
            Self::Client(stream) => stream
                .sock
                .shutdown(Shutdown::Both)
                .map_err(sidecar_net_error),
            Self::Server(stream) => stream
                .sock
                .shutdown(Shutdown::Both)
                .map_err(sidecar_net_error),
            Self::LoopbackClient(stream) => stream.sock.close_endpoint(),
            Self::LoopbackServer(stream) => stream.sock.close_endpoint(),
        }
    }

    fn peer_certificates(&self) -> Option<&[CertificateDer<'static>]> {
        match self {
            Self::Client(stream) => stream.conn.peer_certificates(),
            Self::Server(stream) => stream.conn.peer_certificates(),
            Self::LoopbackClient(stream) => stream.conn.peer_certificates(),
            Self::LoopbackServer(stream) => stream.conn.peer_certificates(),
        }
    }

    fn negotiated_cipher_suite(&self) -> Option<rustls::SupportedCipherSuite> {
        match self {
            Self::Client(stream) => stream.conn.negotiated_cipher_suite(),
            Self::Server(stream) => stream.conn.negotiated_cipher_suite(),
            Self::LoopbackClient(stream) => stream.conn.negotiated_cipher_suite(),
            Self::LoopbackServer(stream) => stream.conn.negotiated_cipher_suite(),
        }
    }

    fn protocol_version(&self) -> Option<rustls::ProtocolVersion> {
        match self {
            Self::Client(stream) => stream.conn.protocol_version(),
            Self::Server(stream) => stream.conn.protocol_version(),
            Self::LoopbackClient(stream) => stream.conn.protocol_version(),
            Self::LoopbackServer(stream) => stream.conn.protocol_version(),
        }
    }
}

// ActiveTcpListener moved to crate::state

// Unix socket types moved to crate::state

impl ActiveUnixSocket {
    fn connect(host_path: &Path, guest_path: &str) -> Result<Self, SidecarError> {
        let stream = UnixStream::connect(host_path).map_err(sidecar_net_error)?;
        Self::from_stream(stream, None, None, Some(guest_path.to_owned()))
    }

    fn from_stream(
        stream: UnixStream,
        listener_id: Option<String>,
        local_path: Option<String>,
        remote_path: Option<String>,
    ) -> Result<Self, SidecarError> {
        let read_stream = stream.try_clone().map_err(sidecar_net_error)?;
        let stream = Arc::new(Mutex::new(stream));
        let (sender, events) = mpsc::channel();
        let saw_local_shutdown = Arc::new(AtomicBool::new(false));
        let saw_remote_end = Arc::new(AtomicBool::new(false));
        let close_notified = Arc::new(AtomicBool::new(false));
        spawn_unix_socket_reader(
            read_stream,
            sender.clone(),
            Arc::clone(&saw_local_shutdown),
            Arc::clone(&saw_remote_end),
            Arc::clone(&close_notified),
        );

        Ok(Self {
            stream,
            events,
            event_sender: sender,
            listener_id,
            local_path,
            remote_path,
            saw_local_shutdown,
            saw_remote_end,
            close_notified,
        })
    }

    fn poll(&mut self, wait: Duration) -> Result<Option<JavascriptTcpSocketEvent>, SidecarError> {
        match self.events.recv_timeout(wait) {
            Ok(event) => Ok(Some(event)),
            Err(RecvTimeoutError::Timeout) => Ok(None),
            Err(RecvTimeoutError::Disconnected) => Ok(None),
        }
    }

    fn socket_info(&self) -> Value {
        json!({
            "localPath": self.local_path.clone(),
            "remotePath": self.remote_path.clone(),
        })
    }

    fn write_all(&self, contents: &[u8]) -> Result<usize, SidecarError> {
        let mut stream = self
            .stream
            .lock()
            .map_err(|_| SidecarError::InvalidState(String::from("Unix socket lock poisoned")))?;
        stream.write_all(contents).map_err(sidecar_net_error)?;
        Ok(contents.len())
    }

    fn shutdown_write(&self) -> Result<(), SidecarError> {
        let stream = self
            .stream
            .lock()
            .map_err(|_| SidecarError::InvalidState(String::from("Unix socket lock poisoned")))?;
        self.saw_local_shutdown.store(true, Ordering::SeqCst);
        stream
            .shutdown(Shutdown::Write)
            .map_err(sidecar_net_error)?;
        if self.saw_remote_end.load(Ordering::SeqCst)
            && !self.close_notified.swap(true, Ordering::SeqCst)
        {
            let _ = self
                .event_sender
                .send(JavascriptTcpSocketEvent::Close { had_error: false });
        }
        Ok(())
    }

    fn close(&self) -> Result<(), SidecarError> {
        let stream = self
            .stream
            .lock()
            .map_err(|_| SidecarError::InvalidState(String::from("Unix socket lock poisoned")))?;
        stream.shutdown(Shutdown::Both).map_err(sidecar_net_error)
    }
}

// ActiveUnixListener moved to crate::state

impl ActiveUnixListener {
    fn bind(
        host_path: &Path,
        guest_path: &str,
        backlog: Option<u32>,
    ) -> Result<Self, SidecarError> {
        if let Some(parent) = host_path.parent() {
            fs::create_dir_all(parent).map_err(sidecar_net_error)?;
        }
        let listener = UnixListener::bind(host_path).map_err(sidecar_net_error)?;
        listener.set_nonblocking(true).map_err(sidecar_net_error)?;
        Ok(Self {
            listener,
            path: guest_path.to_owned(),
            backlog: usize::try_from(backlog.unwrap_or(DEFAULT_JAVASCRIPT_NET_BACKLOG))
                .expect("default backlog fits within usize"),
            active_connection_ids: BTreeSet::new(),
        })
    }

    fn path(&self) -> &str {
        &self.path
    }

    fn poll(
        &mut self,
        wait: Duration,
    ) -> Result<Option<JavascriptUnixListenerEvent>, SidecarError> {
        let deadline = Instant::now() + wait;
        loop {
            match self.listener.accept() {
                Ok((stream, remote_addr)) => {
                    if self.active_connection_ids.len() >= self.backlog {
                        let _ = stream.shutdown(Shutdown::Both);
                        if wait.is_zero() || Instant::now() >= deadline {
                            return Ok(None);
                        }
                        continue;
                    }

                    let local_path = Some(self.path.clone());
                    let remote_path = unix_socket_path(&remote_addr);
                    return Ok(Some(JavascriptUnixListenerEvent::Connection(
                        PendingUnixSocket {
                            stream,
                            local_path,
                            remote_path,
                        },
                    )));
                }
                Err(error) if error.kind() == std::io::ErrorKind::WouldBlock => {
                    if wait.is_zero() || Instant::now() >= deadline {
                        return Ok(None);
                    }
                    thread::sleep(Duration::from_millis(10));
                }
                Err(error) => {
                    return Ok(Some(JavascriptUnixListenerEvent::Error {
                        code: io_error_code(&error),
                        message: error.to_string(),
                    }));
                }
            }
        }
    }

    fn close(&self) -> Result<(), SidecarError> {
        Ok(())
    }

    fn active_connection_count(&self) -> usize {
        self.active_connection_ids.len()
    }

    fn register_connection(&mut self, socket_id: &str) {
        self.active_connection_ids.insert(socket_id.to_string());
    }

    fn release_connection(&mut self, socket_id: &str) {
        self.active_connection_ids.remove(socket_id);
    }
}

impl ActiveTcpListener {
    fn bind(
        bind_host: &str,
        guest_host: &str,
        guest_port: u16,
        backlog: Option<u32>,
    ) -> Result<Self, SidecarError> {
        let bind_addr = resolve_tcp_bind_addr(bind_host, 0)?;
        let guest_addr = resolve_tcp_bind_addr(guest_host, guest_port)?;
        let listener = TcpListener::bind(bind_addr).map_err(sidecar_net_error)?;
        listener.set_nonblocking(true).map_err(sidecar_net_error)?;
        let local_addr = listener.local_addr().map_err(sidecar_net_error)?;
        Ok(Self {
            listener: Some(listener),
            kernel_socket_id: None,
            local_addr: Some(local_addr),
            guest_local_addr: guest_addr,
            backlog: usize::try_from(backlog.unwrap_or(DEFAULT_JAVASCRIPT_NET_BACKLOG))
                .expect("default backlog fits within usize"),
            active_connection_ids: BTreeSet::new(),
        })
    }

    fn bind_kernel(
        kernel: &mut SidecarKernel,
        kernel_pid: u32,
        guest_host: &str,
        guest_port: u16,
        backlog: Option<u32>,
    ) -> Result<Self, SidecarError> {
        let guest_addr = resolve_tcp_bind_addr(guest_host, guest_port)?;
        let spec = match guest_addr {
            SocketAddr::V4(_) => SocketSpec::tcp(),
            SocketAddr::V6(_) => SocketSpec::new(SocketDomain::Inet6, SocketType::Stream),
        };
        let socket_id = kernel
            .socket_create(EXECUTION_DRIVER_NAME, kernel_pid, spec)
            .map_err(kernel_error)?;
        kernel
            .socket_bind_inet(
                EXECUTION_DRIVER_NAME,
                kernel_pid,
                socket_id,
                InetSocketAddress::new(guest_addr.ip().to_string(), guest_addr.port()),
            )
            .map_err(kernel_error)?;
        kernel
            .socket_listen(
                EXECUTION_DRIVER_NAME,
                kernel_pid,
                socket_id,
                usize::try_from(backlog.unwrap_or(DEFAULT_JAVASCRIPT_NET_BACKLOG))
                    .expect("default backlog fits within usize"),
            )
            .map_err(kernel_error)?;
        Ok(Self {
            listener: None,
            kernel_socket_id: Some(socket_id),
            local_addr: Some(guest_addr),
            guest_local_addr: guest_addr,
            backlog: usize::try_from(backlog.unwrap_or(DEFAULT_JAVASCRIPT_NET_BACKLOG))
                .expect("default backlog fits within usize"),
            active_connection_ids: BTreeSet::new(),
        })
    }

    pub(crate) fn local_addr(&self) -> SocketAddr {
        self.local_addr.unwrap_or(self.guest_local_addr)
    }

    fn guest_local_addr(&self) -> SocketAddr {
        self.guest_local_addr
    }

    fn poll(
        &mut self,
        kernel: &mut SidecarKernel,
        kernel_pid: u32,
        wait: Duration,
    ) -> Result<Option<JavascriptTcpListenerEvent>, SidecarError> {
        if let Some(socket_id) = self.kernel_socket_id {
            let result = kernel
                .poll_targets(
                    EXECUTION_DRIVER_NAME,
                    kernel_pid,
                    vec![PollTargetEntry::socket(socket_id, POLLIN)],
                    i32::try_from(wait.as_millis()).unwrap_or(i32::MAX),
                )
                .map_err(kernel_error)?;
            let revents = result
                .targets
                .first()
                .map(|entry| entry.revents)
                .unwrap_or_else(PollEvents::empty);
            if revents.is_empty() {
                return Ok(None);
            }
            let accepted_socket_id =
                match kernel.socket_accept(EXECUTION_DRIVER_NAME, kernel_pid, socket_id) {
                    Ok(accepted_socket_id) => accepted_socket_id,
                    Err(error) if error.code() == "EAGAIN" => return Ok(None),
                    Err(error) => {
                        return Ok(Some(JavascriptTcpListenerEvent::Error {
                            code: Some(error.code().to_string()),
                            message: error.to_string(),
                        }));
                    }
                };
            let accepted = kernel.socket_get(accepted_socket_id).ok_or_else(|| {
                SidecarError::InvalidState(format!(
                    "accepted kernel TCP socket {accepted_socket_id} is missing"
                ))
            })?;
            let local_addr = accepted.local_address().ok_or_else(|| {
                SidecarError::InvalidState(format!(
                    "accepted kernel TCP socket {accepted_socket_id} missing local address"
                ))
            })?;
            let remote_addr = accepted.peer_address().ok_or_else(|| {
                SidecarError::InvalidState(format!(
                    "accepted kernel TCP socket {accepted_socket_id} missing peer address"
                ))
            })?;
            return Ok(Some(JavascriptTcpListenerEvent::Connection(
                PendingTcpSocket {
                    stream: None,
                    kernel_socket_id: Some(accepted_socket_id),
                    preallocated: true,
                    guest_local_addr: resolve_tcp_bind_addr(local_addr.host(), local_addr.port())?,
                    guest_remote_addr: resolve_tcp_bind_addr(
                        remote_addr.host(),
                        remote_addr.port(),
                    )?,
                },
            )));
        }

        let deadline = Instant::now() + wait;
        loop {
            match self
                .listener
                .as_ref()
                .ok_or_else(|| {
                    SidecarError::InvalidState(String::from("TCP listener socket missing"))
                })?
                .accept()
            {
                Ok((stream, remote_addr)) => {
                    if self.active_connection_ids.len() >= self.backlog {
                        let _ = stream.shutdown(Shutdown::Both);
                        if wait.is_zero() || Instant::now() >= deadline {
                            return Ok(None);
                        }
                        continue;
                    }
                    return Ok(Some(JavascriptTcpListenerEvent::Connection(
                        PendingTcpSocket {
                            stream: Some(stream),
                            kernel_socket_id: None,
                            preallocated: false,
                            guest_local_addr: self.guest_local_addr,
                            guest_remote_addr: SocketAddr::new(
                                remote_addr.ip(),
                                remote_addr.port(),
                            ),
                        },
                    )));
                }
                Err(error) if error.kind() == std::io::ErrorKind::WouldBlock => {
                    if wait.is_zero() || Instant::now() >= deadline {
                        return Ok(None);
                    }
                    thread::sleep(Duration::from_millis(10));
                }
                Err(error) => {
                    return Ok(Some(JavascriptTcpListenerEvent::Error {
                        code: io_error_code(&error),
                        message: error.to_string(),
                    }));
                }
            }
        }
    }

    fn close(&self, kernel: &mut SidecarKernel, kernel_pid: u32) -> Result<(), SidecarError> {
        if let Some(socket_id) = self.kernel_socket_id {
            kernel
                .socket_close(EXECUTION_DRIVER_NAME, kernel_pid, socket_id)
                .map_err(kernel_error)?;
        }
        Ok(())
    }

    fn active_connection_count(&self) -> usize {
        self.active_connection_ids.len()
    }

    fn register_connection(&mut self, socket_id: &str) {
        self.active_connection_ids.insert(socket_id.to_string());
    }

    fn release_connection(&mut self, socket_id: &str) {
        self.active_connection_ids.remove(socket_id);
    }
}

// UDP types moved to crate::state

impl ActiveUdpSocket {
    fn new(
        kernel: &mut SidecarKernel,
        kernel_pid: u32,
        family: JavascriptUdpFamily,
    ) -> Result<Self, SidecarError> {
        let spec = match family {
            JavascriptUdpFamily::Ipv4 => SocketSpec::udp(),
            JavascriptUdpFamily::Ipv6 => SocketSpec::new(SocketDomain::Inet6, SocketType::Datagram),
        };
        let socket_id = kernel
            .socket_create(EXECUTION_DRIVER_NAME, kernel_pid, spec)
            .map_err(kernel_error)?;
        Ok(Self {
            family,
            socket: None,
            kernel_socket_id: Some(socket_id),
            guest_local_addr: None,
            recv_buffer_size: 0,
            send_buffer_size: 0,
        })
    }

    fn local_addr(&self) -> Option<SocketAddr> {
        self.guest_local_addr
    }

    fn socket(&self) -> Result<&UdpSocket, SidecarError> {
        self.socket
            .as_ref()
            .ok_or_else(|| SidecarError::Execution(String::from("EBADF: bad file descriptor")))
    }

    fn bind(
        &mut self,
        kernel: &mut SidecarKernel,
        kernel_pid: u32,
        host: Option<&str>,
        port: u16,
        context: &JavascriptSocketPathContext,
    ) -> Result<SocketAddr, SidecarError> {
        if self.socket.is_some() || self.guest_local_addr.is_some() {
            return Err(SidecarError::Execution(String::from(
                "EINVAL: secure-exec dgram socket is already bound",
            )));
        }

        let (bind_host, guest_host, guest_family) = normalize_udp_bind_host(host, self.family)?;
        let guest_port = allocate_guest_listen_port(
            port,
            guest_family,
            &context.used_udp_guest_ports,
            context.listen_policy,
        )?;
        let local_addr = resolve_udp_bind_addr(guest_host, guest_port, self.family)?;
        if let Some(socket_id) = self.kernel_socket_id {
            kernel
                .socket_bind_inet(
                    EXECUTION_DRIVER_NAME,
                    kernel_pid,
                    socket_id,
                    InetSocketAddress::new(local_addr.ip().to_string(), local_addr.port()),
                )
                .map_err(kernel_error)?;
        } else {
            let bind_addr = resolve_udp_bind_addr(bind_host, 0, self.family)?;
            let socket = UdpSocket::bind(bind_addr).map_err(sidecar_net_error)?;
            socket.set_nonblocking(true).map_err(sidecar_net_error)?;
            self.socket = Some(socket);
        }
        self.guest_local_addr = Some(local_addr);
        Ok(local_addr)
    }

    fn ensure_bound_for_send(
        &mut self,
        kernel: &mut SidecarKernel,
        kernel_pid: u32,
        context: &JavascriptSocketPathContext,
    ) -> Result<SocketAddr, SidecarError> {
        if let Some(local_addr) = self.local_addr() {
            return Ok(local_addr);
        }

        self.bind(kernel, kernel_pid, None, 0, context)
    }

    fn send_to<B>(
        &mut self,
        request: ActiveUdpSendToRequest<'_, B>,
    ) -> Result<(usize, SocketAddr), SidecarError>
    where
        B: NativeSidecarBridge + Send + 'static,
        BridgeError<B>: fmt::Debug + Send + Sync + 'static,
    {
        let ActiveUdpSendToRequest {
            bridge,
            kernel,
            kernel_pid,
            vm_id,
            dns,
            host,
            port,
            context,
            contents,
        } = request;
        let remote_addr = resolve_udp_addr(UdpRemoteAddrRequest {
            bridge,
            kernel,
            vm_id,
            dns,
            host,
            port,
            family: self.family,
            context,
        })?;
        let local_addr = self.ensure_bound_for_send(kernel, kernel_pid, context)?;
        let written = if let Some(socket_id) = self.kernel_socket_id {
            if is_loopback_ip(remote_addr.ip()) && remote_addr.port() == port {
                kernel
                    .socket_send_to_inet_loopback(
                        EXECUTION_DRIVER_NAME,
                        kernel_pid,
                        socket_id,
                        InetSocketAddress::new(remote_addr.ip().to_string(), remote_addr.port()),
                        contents,
                    )
                    .map_err(kernel_error)?
            } else {
                return Err(SidecarError::Execution(String::from(
                    "ERR_NOT_IMPLEMENTED: external UDP datagrams are not yet supported by the kernel-backed V8 bridge",
                )));
            }
        } else {
            let socket = self.socket.as_ref().ok_or_else(|| {
                SidecarError::InvalidState(String::from("UDP socket is not initialized"))
            })?;
            socket
                .send_to(contents, remote_addr)
                .map_err(sidecar_net_error)?
        };
        Ok((written, local_addr))
    }

    fn poll(
        &self,
        kernel: &mut SidecarKernel,
        kernel_pid: u32,
        wait: Duration,
    ) -> Result<Option<JavascriptUdpSocketEvent>, SidecarError> {
        if let Some(socket_id) = self.kernel_socket_id {
            let result = kernel
                .poll_targets(
                    EXECUTION_DRIVER_NAME,
                    kernel_pid,
                    vec![PollTargetEntry::socket(socket_id, POLLIN)],
                    i32::try_from(wait.as_millis()).unwrap_or(i32::MAX),
                )
                .map_err(kernel_error)?;
            let revents = result
                .targets
                .first()
                .map(|entry| entry.revents)
                .unwrap_or_else(PollEvents::empty);
            if revents.is_empty() {
                return Ok(None);
            }
            return match kernel.socket_recv_datagram(
                EXECUTION_DRIVER_NAME,
                kernel_pid,
                socket_id,
                64 * 1024,
            ) {
                Ok(Some(datagram)) => {
                    let (source_address, payload) = datagram.into_parts();
                    let remote_addr = source_address
                        .map(|source| {
                            resolve_udp_bind_addr(source.host(), source.port(), self.family)
                        })
                        .transpose()?
                        .unwrap_or_else(|| match self.family {
                            JavascriptUdpFamily::Ipv4 => {
                                SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 0)
                            }
                            JavascriptUdpFamily::Ipv6 => {
                                SocketAddr::new(IpAddr::V6(Ipv6Addr::LOCALHOST), 0)
                            }
                        });
                    Ok(Some(JavascriptUdpSocketEvent::Message {
                        data: payload,
                        remote_addr,
                    }))
                }
                Ok(None) => Ok(None),
                Err(error) if error.code() == "EAGAIN" => Ok(None),
                Err(error) => Ok(Some(JavascriptUdpSocketEvent::Error {
                    code: Some(error.code().to_string()),
                    message: error.to_string(),
                })),
            };
        }
        let socket = self.socket()?;
        let deadline = Instant::now() + wait;
        let mut buffer = vec![0_u8; 64 * 1024];

        loop {
            match socket.recv_from(&mut buffer) {
                Ok((bytes_read, remote_addr)) => {
                    return Ok(Some(JavascriptUdpSocketEvent::Message {
                        data: buffer[..bytes_read].to_vec(),
                        remote_addr,
                    }));
                }
                Err(error) if error.kind() == std::io::ErrorKind::WouldBlock => {
                    if wait.is_zero() || Instant::now() >= deadline {
                        return Ok(None);
                    }
                    thread::sleep(Duration::from_millis(10));
                }
                Err(error) => {
                    return Ok(Some(JavascriptUdpSocketEvent::Error {
                        code: io_error_code(&error),
                        message: error.to_string(),
                    }));
                }
            }
        }
    }

    fn close(&mut self, kernel: &mut SidecarKernel, kernel_pid: u32) {
        if let Some(socket_id) = self.kernel_socket_id {
            let _ = kernel.socket_close(EXECUTION_DRIVER_NAME, kernel_pid, socket_id);
        }
        self.socket.take();
        self.guest_local_addr = None;
    }

    fn set_buffer_size(&mut self, which: &str, size: usize) -> Result<(), SidecarError> {
        match which {
            "recv" => self.recv_buffer_size = size,
            "send" => self.send_buffer_size = size,
            other => {
                return Err(SidecarError::InvalidState(format!(
                    "unsupported UDP buffer size kind {other}"
                )));
            }
        }
        if self.kernel_socket_id.is_some() {
            return Ok(());
        }
        let socket = self.socket()?;
        let socket = SockRef::from(socket);
        match which {
            "recv" => socket.set_recv_buffer_size(size).map_err(sidecar_net_error),
            "send" => socket.set_send_buffer_size(size).map_err(sidecar_net_error),
            other => Err(SidecarError::InvalidState(format!(
                "unsupported UDP buffer size kind {other}"
            ))),
        }
    }

    fn get_buffer_size(&self, which: &str) -> Result<usize, SidecarError> {
        if self.kernel_socket_id.is_some() {
            return Ok(match which {
                "recv" => self.recv_buffer_size,
                "send" => self.send_buffer_size,
                other => {
                    return Err(SidecarError::InvalidState(format!(
                        "unsupported UDP buffer size kind {other}"
                    )));
                }
            });
        }
        let socket = self.socket()?;
        let socket = SockRef::from(socket);
        match which {
            "recv" => socket.recv_buffer_size().map_err(sidecar_net_error),
            "send" => socket.send_buffer_size().map_err(sidecar_net_error),
            other => Err(SidecarError::InvalidState(format!(
                "unsupported UDP buffer size kind {other}"
            ))),
        }
    }
}

// ActiveExecution, ActiveExecutionEvent, SocketQueryKind moved to crate::state

impl ActiveExecution {
    pub(crate) fn uses_shared_v8_runtime(&self) -> bool {
        match self {
            Self::Javascript(execution) => execution.uses_shared_v8_runtime(),
            Self::Python(execution) => execution.uses_shared_v8_runtime(),
            Self::Wasm(execution) => execution.uses_shared_v8_runtime(),
            Self::Tool(_) => false,
        }
    }

    pub(crate) fn child_pid(&self) -> u32 {
        match self {
            Self::Javascript(execution) => execution.child_pid(),
            Self::Python(execution) => execution.child_pid(),
            Self::Wasm(execution) => execution.child_pid(),
            Self::Tool(_) => 0,
        }
    }

    pub(crate) fn write_stdin(&mut self, chunk: &[u8]) -> Result<(), SidecarError> {
        match self {
            Self::Javascript(execution) => execution
                .write_stdin(chunk)
                .map_err(|error| SidecarError::Execution(error.to_string())),
            Self::Python(execution) => execution
                .write_stdin(chunk)
                .map_err(|error| SidecarError::Execution(error.to_string())),
            Self::Wasm(execution) => execution
                .write_stdin(chunk)
                .map_err(|error| SidecarError::Execution(error.to_string())),
            Self::Tool(_) => Ok(()),
        }
    }

    pub(crate) fn close_stdin(&mut self) -> Result<(), SidecarError> {
        match self {
            Self::Javascript(execution) => execution
                .close_stdin()
                .map_err(|error| SidecarError::Execution(error.to_string())),
            Self::Python(execution) => execution
                .close_stdin()
                .map_err(|error| SidecarError::Execution(error.to_string())),
            Self::Wasm(execution) => execution
                .close_stdin()
                .map_err(|error| SidecarError::Execution(error.to_string())),
            Self::Tool(_) => Ok(()),
        }
    }

    pub(crate) fn respond_python_vfs_rpc_success(
        &mut self,
        id: u64,
        payload: PythonVfsRpcResponsePayload,
    ) -> Result<(), SidecarError> {
        match self {
            Self::Python(execution) => execution
                .respond_vfs_rpc_success(id, payload)
                .map_err(|error| SidecarError::Execution(error.to_string())),
            _ => Err(SidecarError::InvalidState(String::from(
                "only Python executions can service Python VFS RPC responses",
            ))),
        }
    }

    pub(crate) fn respond_python_vfs_rpc_error(
        &mut self,
        id: u64,
        code: impl Into<String>,
        message: impl Into<String>,
    ) -> Result<(), SidecarError> {
        match self {
            Self::Python(execution) => execution
                .respond_vfs_rpc_error(id, code, message)
                .map_err(|error| SidecarError::Execution(error.to_string())),
            _ => Err(SidecarError::InvalidState(String::from(
                "only Python executions can service Python VFS RPC responses",
            ))),
        }
    }

    pub(crate) fn send_javascript_stream_event(
        &self,
        event_type: &str,
        payload: Value,
    ) -> Result<(), SidecarError> {
        match self {
            Self::Javascript(execution) => execution
                .send_stream_event(event_type, payload)
                .map_err(|error| SidecarError::Execution(error.to_string())),
            Self::Wasm(execution) => execution
                .send_stream_event(event_type, payload)
                .map_err(|error| SidecarError::Execution(error.to_string())),
            _ => Err(SidecarError::InvalidState(String::from(
                "only embedded V8 executions can receive JavaScript stream events",
            ))),
        }
    }

    pub(crate) fn javascript_v8_session_handle(&self) -> Option<V8SessionHandle> {
        match self {
            Self::Javascript(execution) => Some(execution.v8_session_handle()),
            Self::Wasm(execution) => Some(execution.v8_session_handle()),
            _ => None,
        }
    }

    pub(crate) fn terminate(&mut self) -> Result<(), SidecarError> {
        match self {
            Self::Javascript(execution) => execution
                .terminate()
                .map_err(|error| SidecarError::Execution(error.to_string())),
            Self::Python(execution) => execution
                .kill()
                .map_err(|error| SidecarError::Execution(error.to_string())),
            Self::Wasm(execution) => execution
                .terminate()
                .map_err(|error| SidecarError::Execution(error.to_string())),
            Self::Tool(_) => Ok(()),
        }
    }

    pub(crate) fn respond_javascript_sync_rpc_success(
        &mut self,
        id: u64,
        result: Value,
    ) -> Result<(), SidecarError> {
        match self {
            Self::Javascript(execution) => execution
                .respond_sync_rpc_success(id, result)
                .map_err(|error| SidecarError::Execution(error.to_string())),
            Self::Python(execution) => execution
                .respond_javascript_sync_rpc_success(id, result)
                .map_err(|error| SidecarError::Execution(error.to_string())),
            Self::Wasm(execution) => execution
                .respond_sync_rpc_success(id, result)
                .map_err(|error| SidecarError::Execution(error.to_string())),
            _ => Err(SidecarError::InvalidState(String::from(
                "only JavaScript, Python, and WebAssembly executions can service JavaScript sync RPC responses",
            ))),
        }
    }

    pub(crate) fn respond_javascript_sync_rpc_error(
        &mut self,
        id: u64,
        code: impl Into<String>,
        message: impl Into<String>,
    ) -> Result<(), SidecarError> {
        match self {
            Self::Javascript(execution) => execution
                .respond_sync_rpc_error(id, code, message)
                .map_err(|error| SidecarError::Execution(error.to_string())),
            Self::Python(execution) => execution
                .respond_javascript_sync_rpc_error(id, code, message)
                .map_err(|error| SidecarError::Execution(error.to_string())),
            Self::Wasm(execution) => execution
                .respond_sync_rpc_error(id, code, message)
                .map_err(|error| SidecarError::Execution(error.to_string())),
            _ => Err(SidecarError::InvalidState(String::from(
                "only JavaScript, Python, and WebAssembly executions can service JavaScript sync RPC responses",
            ))),
        }
    }

    pub(crate) async fn poll_event(
        &mut self,
        timeout: Duration,
    ) -> Result<Option<ActiveExecutionEvent>, SidecarError> {
        match self {
            Self::Javascript(execution) => execution
                .poll_event(timeout)
                .await
                .map(|event| {
                    event.map(|event| match event {
                        JavascriptExecutionEvent::Stdout(chunk) => {
                            ActiveExecutionEvent::Stdout(chunk)
                        }
                        JavascriptExecutionEvent::Stderr(chunk) => {
                            ActiveExecutionEvent::Stderr(chunk)
                        }
                        JavascriptExecutionEvent::SyncRpcRequest(request) => {
                            ActiveExecutionEvent::JavascriptSyncRpcRequest(request)
                        }
                        JavascriptExecutionEvent::SignalState {
                            signal,
                            registration,
                        } => ActiveExecutionEvent::SignalState {
                            signal,
                            registration: map_node_signal_registration(registration),
                        },
                        JavascriptExecutionEvent::Exited(code) => {
                            ActiveExecutionEvent::Exited(code)
                        }
                    })
                })
                .map_err(|error| SidecarError::Execution(error.to_string())),
            Self::Python(execution) => execution
                .poll_event(timeout)
                .await
                .map(|event| {
                    event.map(|event| match event {
                        PythonExecutionEvent::Stdout(chunk) => ActiveExecutionEvent::Stdout(chunk),
                        PythonExecutionEvent::Stderr(chunk) => ActiveExecutionEvent::Stderr(chunk),
                        PythonExecutionEvent::JavascriptSyncRpcRequest(request) => {
                            ActiveExecutionEvent::JavascriptSyncRpcRequest(request)
                        }
                        PythonExecutionEvent::VfsRpcRequest(request) => {
                            ActiveExecutionEvent::PythonVfsRpcRequest(request)
                        }
                        PythonExecutionEvent::Exited(code) => ActiveExecutionEvent::Exited(code),
                    })
                })
                .map_err(|error| SidecarError::Execution(error.to_string())),
            Self::Wasm(execution) => execution
                .poll_event(timeout)
                .await
                .map(|event| {
                    event.map(|event| match event {
                        WasmExecutionEvent::Stdout(chunk) => ActiveExecutionEvent::Stdout(chunk),
                        WasmExecutionEvent::Stderr(chunk) => ActiveExecutionEvent::Stderr(chunk),
                        WasmExecutionEvent::SyncRpcRequest(request) => {
                            ActiveExecutionEvent::JavascriptSyncRpcRequest(request)
                        }
                        WasmExecutionEvent::SignalState {
                            signal,
                            registration,
                        } => ActiveExecutionEvent::SignalState {
                            signal,
                            registration: map_wasm_signal_registration(registration),
                        },
                        WasmExecutionEvent::Exited(code) => ActiveExecutionEvent::Exited(code),
                    })
                })
                .map_err(|error| SidecarError::Execution(error.to_string())),
            Self::Tool(execution) => {
                let _ = timeout;
                poll_tool_process_event(execution)
            }
        }
    }

    pub(crate) fn poll_event_blocking(
        &mut self,
        timeout: Duration,
    ) -> Result<Option<ActiveExecutionEvent>, SidecarError> {
        match self {
            Self::Javascript(execution) => execution
                .poll_event_blocking(timeout)
                .map(|event| {
                    event.map(|event| match event {
                        JavascriptExecutionEvent::Stdout(chunk) => {
                            ActiveExecutionEvent::Stdout(chunk)
                        }
                        JavascriptExecutionEvent::Stderr(chunk) => {
                            ActiveExecutionEvent::Stderr(chunk)
                        }
                        JavascriptExecutionEvent::SyncRpcRequest(request) => {
                            ActiveExecutionEvent::JavascriptSyncRpcRequest(request)
                        }
                        JavascriptExecutionEvent::SignalState {
                            signal,
                            registration,
                        } => ActiveExecutionEvent::SignalState {
                            signal,
                            registration: map_node_signal_registration(registration),
                        },
                        JavascriptExecutionEvent::Exited(code) => {
                            ActiveExecutionEvent::Exited(code)
                        }
                    })
                })
                .map_err(|error| SidecarError::Execution(error.to_string())),
            Self::Python(execution) => execution
                .poll_event_blocking(timeout)
                .map(|event| {
                    event.map(|event| match event {
                        PythonExecutionEvent::Stdout(chunk) => ActiveExecutionEvent::Stdout(chunk),
                        PythonExecutionEvent::Stderr(chunk) => ActiveExecutionEvent::Stderr(chunk),
                        PythonExecutionEvent::JavascriptSyncRpcRequest(request) => {
                            ActiveExecutionEvent::JavascriptSyncRpcRequest(request)
                        }
                        PythonExecutionEvent::VfsRpcRequest(request) => {
                            ActiveExecutionEvent::PythonVfsRpcRequest(request)
                        }
                        PythonExecutionEvent::Exited(code) => ActiveExecutionEvent::Exited(code),
                    })
                })
                .map_err(|error| SidecarError::Execution(error.to_string())),
            Self::Wasm(execution) => execution
                .poll_event_blocking(timeout)
                .map(|event| {
                    event.map(|event| match event {
                        WasmExecutionEvent::Stdout(chunk) => ActiveExecutionEvent::Stdout(chunk),
                        WasmExecutionEvent::Stderr(chunk) => ActiveExecutionEvent::Stderr(chunk),
                        WasmExecutionEvent::SyncRpcRequest(request) => {
                            ActiveExecutionEvent::JavascriptSyncRpcRequest(request)
                        }
                        WasmExecutionEvent::SignalState {
                            signal,
                            registration,
                        } => ActiveExecutionEvent::SignalState {
                            signal,
                            registration: map_wasm_signal_registration(registration),
                        },
                        WasmExecutionEvent::Exited(code) => ActiveExecutionEvent::Exited(code),
                    })
                })
                .map_err(|error| SidecarError::Execution(error.to_string())),
            Self::Tool(execution) => {
                let _ = timeout;
                poll_tool_process_event(execution)
            }
        }
    }
}

struct ToolProcessEventRequest {
    sidecar_requests: SharedSidecarRequestClient,
    connection_id: String,
    session_id: String,
    vm_id: String,
    tool_resolution: ToolCommandResolution,
    cancelled: Arc<AtomicBool>,
    pending_events: Arc<Mutex<VecDeque<ActiveExecutionEvent>>>,
    events_overflowed: Arc<AtomicBool>,
}

pub(crate) fn send_tool_process_event(
    pending_events: &Arc<Mutex<VecDeque<ActiveExecutionEvent>>>,
    events_overflowed: &AtomicBool,
    event: ActiveExecutionEvent,
) -> bool {
    let mut pending_events = pending_events
        .lock()
        .unwrap_or_else(|poisoned| poisoned.into_inner());
    if pending_events.len() >= MAX_PROCESS_EVENT_QUEUE {
        events_overflowed.store(true, Ordering::Relaxed);
        return false;
    }
    pending_events.push_back(event);
    true
}

fn spawn_tool_process_events(request: ToolProcessEventRequest) {
    let ToolProcessEventRequest {
        sidecar_requests,
        connection_id,
        session_id,
        vm_id,
        tool_resolution,
        cancelled,
        pending_events,
        events_overflowed,
    } = request;
    std::thread::spawn(move || match tool_resolution {
        ToolCommandResolution::Failure(message) => {
            if !send_tool_process_event(
                &pending_events,
                &events_overflowed,
                ActiveExecutionEvent::Stderr(format_tool_failure_output(&message)),
            ) {
                return;
            }
            let _ = send_tool_process_event(
                &pending_events,
                &events_overflowed,
                ActiveExecutionEvent::Exited(1),
            );
        }
        ToolCommandResolution::Invoke { request, timeout } => {
            let response = sidecar_requests.invoke(
                OwnershipScope::vm(connection_id.clone(), session_id.clone(), vm_id.clone()),
                SidecarRequestPayload::HostCallback(request.clone()),
                timeout,
            );
            if cancelled.load(Ordering::Relaxed) {
                return;
            }

            match response {
                Ok(crate::protocol::SidecarResponsePayload::HostCallbackResult(result)) => {
                    if let Some(value) = result.result {
                        let value: serde_json::Value = serde_json::from_str(&value)
                            .unwrap_or(serde_json::Value::String(value));
                        let stdout = serde_json::to_vec(&json!({
                            "ok": true,
                            "result": value,
                        }))
                        .unwrap_or_else(|error| {
                            format_tool_failure_output(&format!(
                                "failed to serialize tool result: {error}"
                            ))
                        });
                        if !send_tool_process_event(
                            &pending_events,
                            &events_overflowed,
                            ActiveExecutionEvent::Stdout(stdout),
                        ) {
                            return;
                        }
                        let _ = send_tool_process_event(
                            &pending_events,
                            &events_overflowed,
                            ActiveExecutionEvent::Exited(0),
                        );
                    } else {
                        let message = result
                            .error
                            .unwrap_or_else(|| String::from("tool invocation returned no result"));
                        if !send_tool_process_event(
                            &pending_events,
                            &events_overflowed,
                            ActiveExecutionEvent::Stderr(format_tool_failure_output(&message)),
                        ) {
                            return;
                        }
                        let _ = send_tool_process_event(
                            &pending_events,
                            &events_overflowed,
                            ActiveExecutionEvent::Exited(1),
                        );
                    }
                }
                Ok(_) => {
                    if !send_tool_process_event(
                        &pending_events,
                        &events_overflowed,
                        ActiveExecutionEvent::Stderr(format_tool_failure_output(
                            "unexpected sidecar tool response",
                        )),
                    ) {
                        return;
                    }
                    let _ = send_tool_process_event(
                        &pending_events,
                        &events_overflowed,
                        ActiveExecutionEvent::Exited(1),
                    );
                }
                Err(error) => {
                    if !send_tool_process_event(
                        &pending_events,
                        &events_overflowed,
                        ActiveExecutionEvent::Stderr(format_tool_failure_output(
                            &error.to_string(),
                        )),
                    ) {
                        return;
                    }
                    let _ = send_tool_process_event(
                        &pending_events,
                        &events_overflowed,
                        ActiveExecutionEvent::Exited(1),
                    );
                }
            }
        }
    });
}

impl<B> NativeSidecar<B>
where
    B: NativeSidecarBridge + Send + 'static,
    BridgeError<B>: fmt::Debug + Send + Sync + 'static,
{
    pub(crate) async fn execute(
        &mut self,
        request: &RequestFrame,
        payload: ExecuteRequest,
    ) -> Result<DispatchResult, SidecarError> {
        let (connection_id, session_id, vm_id) = self.vm_scope_for(&request.ownership)?;
        self.require_owned_vm(&connection_id, &session_id, &vm_id)?;

        let vm = self
            .vms
            .get_mut(&vm_id)
            .ok_or_else(|| missing_vm_error(&vm_id))?;
        if vm.active_processes.contains_key(&payload.process_id) {
            return Err(SidecarError::InvalidState(format!(
                "VM {vm_id} already has an active process with id {}",
                payload.process_id
            )));
        }

        if let Some(command) = payload.command.as_deref() {
            if let Some(tool_resolution) =
                resolve_tool_command(vm, command, &payload.args, payload.cwd.as_deref())?
            {
                let guest_cwd = payload
                    .cwd
                    .as_deref()
                    .map(normalize_path)
                    .unwrap_or_else(|| vm.guest_cwd.clone());
                let kernel_handle = vm
                    .kernel
                    .create_virtual_process(
                        EXECUTION_DRIVER_NAME,
                        TOOL_DRIVER_NAME,
                        command,
                        std::iter::once(command.to_owned())
                            .chain(payload.args.iter().cloned())
                            .collect(),
                        VirtualProcessOptions {
                            env: vm.guest_env.clone(),
                            cwd: Some(guest_cwd.clone()),
                            ..VirtualProcessOptions::default()
                        },
                    )
                    .map_err(kernel_error)?;
                let kernel_pid = kernel_handle.pid();
                let tool_execution = ToolExecution::default();
                let cancelled = tool_execution.cancelled.clone();
                let pending_events = tool_execution.pending_events.clone();
                let events_overflowed = tool_execution.events_overflowed.clone();
                vm.active_processes.insert(
                    payload.process_id.clone(),
                    ActiveProcess::new(
                        kernel_pid,
                        kernel_handle,
                        GuestRuntimeKind::JavaScript,
                        ActiveExecution::Tool(tool_execution),
                    )
                    .with_guest_cwd(guest_cwd.clone())
                    .with_host_cwd(resolve_vm_guest_path_to_host(vm, &guest_cwd)),
                );
                self.bridge.emit_lifecycle(&vm_id, LifecycleState::Busy)?;
                spawn_tool_process_events(ToolProcessEventRequest {
                    sidecar_requests: self.sidecar_requests.clone(),
                    connection_id: connection_id.clone(),
                    session_id: session_id.clone(),
                    vm_id: vm_id.clone(),
                    tool_resolution,
                    cancelled,
                    pending_events,
                    events_overflowed,
                });

                return Ok(DispatchResult {
                    response: self.respond(
                        request,
                        ResponsePayload::ProcessStarted(ProcessStartedResponse {
                            process_id: payload.process_id,
                            pid: Some(kernel_pid),
                        }),
                    ),
                    events: Vec::new(),
                });
            }
        }

        let resolved = resolve_execute_request(vm, &payload)?;
        let mut env = resolved.env.clone();
        let sandbox_root = normalize_host_path(&vm.cwd);
        env.insert(
            String::from(EXECUTION_SANDBOX_ROOT_ENV),
            sandbox_root.to_string_lossy().into_owned(),
        );
        if resolved.runtime == GuestRuntimeKind::JavaScript {
            env.insert(
                String::from("SECURE_EXEC_KEEP_STDIN_OPEN"),
                String::from("1"),
            );
        } else if resolved.runtime == GuestRuntimeKind::WebAssembly {
            env.insert(String::from(WASM_STDIO_SYNC_RPC_ENV), String::from("1"));
        }
        let argv = std::iter::once(resolved.entrypoint.clone())
            .chain(resolved.execution_args.iter().cloned())
            .collect::<Vec<_>>();
        let kernel_handle = vm
            .kernel
            .spawn_process(
                &resolved.command,
                argv,
                SpawnOptions {
                    requester_driver: Some(String::from(EXECUTION_DRIVER_NAME)),
                    cwd: Some(resolved.guest_cwd.clone()),
                    ..SpawnOptions::default()
                },
            )
            .map_err(kernel_error)?;
        let kernel_pid = kernel_handle.pid();

        let (execution, process_env) = match resolved.runtime {
            GuestRuntimeKind::JavaScript => {
                let inline_code = load_javascript_entrypoint_source(
                    vm,
                    &resolved.host_cwd,
                    &resolved.entrypoint,
                    &env,
                );
                prepare_javascript_shadow(vm, &resolved)?;

                let context =
                    self.javascript_engine
                        .create_context(CreateJavascriptContextRequest {
                            vm_id: vm_id.clone(),
                            bootstrap_module: None,
                            compile_cache_root: Some(self.cache_root.join("node-compile-cache")),
                        });
                let module_reader = build_module_reader(vm, &resolved)
                    .map(|reader| Box::new(reader) as Box<dyn ModuleFsReader + Send>);
                let execution = self
                    .javascript_engine
                    .start_execution_with_module_reader(
                        StartJavascriptExecutionRequest {
                            guest_runtime: guest_runtime_identity(vm, None, None),
                            vm_id: vm_id.clone(),
                            context_id: context.context_id,
                            argv: std::iter::once(resolved.entrypoint.clone())
                                .chain(resolved.execution_args.iter().cloned())
                                .collect(),
                            env: env.clone(),
                            cwd: resolved.host_cwd.clone(),
                            limits: javascript_execution_limits(vm),
                            inline_code,
                        },
                        module_reader,
                    )
                    .map_err(javascript_error)?;
                (ActiveExecution::Javascript(execution), env.clone())
            }
            GuestRuntimeKind::Python => {
                let python_file_path = python_file_entrypoint(&resolved.entrypoint);
                let pyodide_dist_path = self
                    .python_engine
                    .bundled_pyodide_dist_path_for_vm(&vm_id)
                    .map_err(python_error)?;
                let pyodide_cache_path = pyodide_dist_path
                    .parent()
                    .and_then(Path::parent)
                    .unwrap_or(pyodide_dist_path.as_path())
                    .join("pyodide-package-cache");
                add_runtime_guest_path_mapping(
                    &mut env,
                    PYTHON_PYODIDE_GUEST_ROOT,
                    &pyodide_dist_path,
                );
                add_runtime_guest_path_mapping(
                    &mut env,
                    PYTHON_PYODIDE_CACHE_GUEST_ROOT,
                    &pyodide_cache_path,
                );
                add_runtime_host_access_path(
                    &mut env,
                    "AGENTOS_EXTRA_FS_READ_PATHS",
                    &pyodide_dist_path,
                    true,
                );
                add_runtime_host_access_path(
                    &mut env,
                    "AGENTOS_EXTRA_FS_READ_PATHS",
                    &pyodide_cache_path,
                    true,
                );
                add_runtime_host_access_path(
                    &mut env,
                    "AGENTOS_EXTRA_FS_WRITE_PATHS",
                    &pyodide_cache_path,
                    false,
                );
                let context = self
                    .python_engine
                    .create_context(CreatePythonContextRequest {
                        vm_id: vm_id.clone(),
                        pyodide_dist_path,
                    });
                let execution = self
                    .python_engine
                    .start_execution(StartPythonExecutionRequest {
                        vm_id: vm_id.clone(),
                        context_id: context.context_id,
                        code: resolved.entrypoint.clone(),
                        file_path: python_file_path,
                        env: env.clone(),
                        cwd: resolved.host_cwd.clone(),
                        limits: python_execution_limits(vm),
                        guest_runtime: guest_runtime_identity(vm, None, None),
                    })
                    .map_err(python_error)?;
                (ActiveExecution::Python(execution), env.clone())
            }
            GuestRuntimeKind::WebAssembly => {
                let wasm_limits = wasm_execution_limits(vm);
                let wasm_guest_runtime =
                    guest_runtime_identity(vm, Some(u64::from(kernel_pid)), Some(0));
                let wasm_permission_tier = resolved.wasm_permission_tier.unwrap_or_else(|| {
                    resolve_wasm_permission_tier(
                        vm,
                        Some(&resolved.command),
                        None,
                        &resolved.entrypoint,
                    )
                });
                let context = self.wasm_engine.create_context(CreateWasmContextRequest {
                    vm_id: vm_id.clone(),
                    module_path: Some(resolved.entrypoint.clone()),
                });
                let execution = self
                    .wasm_engine
                    .start_execution(StartWasmExecutionRequest {
                        vm_id: vm_id.clone(),
                        context_id: context.context_id,
                        argv: resolved.process_args.clone(),
                        env: env.clone(),
                        cwd: resolved.host_cwd.clone(),
                        permission_tier: execution_wasm_permission_tier(wasm_permission_tier),
                        limits: wasm_limits,
                        guest_runtime: wasm_guest_runtime,
                    })
                    .map_err(wasm_error)?;
                (ActiveExecution::Wasm(Box::new(execution)), env)
            }
        };
        let child_pid = execution.child_pid();
        let kernel_stdin_writer_fd = install_kernel_stdin_pipe(&mut vm.kernel, kernel_pid)?;
        vm.active_processes.insert(
            payload.process_id.clone(),
            ActiveProcess::new(kernel_pid, kernel_handle, resolved.runtime, execution)
                .with_kernel_stdin_writer_fd(kernel_stdin_writer_fd)
                .with_guest_cwd(resolved.guest_cwd.clone())
                .with_env(process_env)
                .with_host_cwd(resolved.host_cwd.clone()),
        );
        self.bridge.emit_lifecycle(&vm_id, LifecycleState::Busy)?;

        Ok(DispatchResult {
            response: self.respond(
                request,
                ResponsePayload::ProcessStarted(ProcessStartedResponse {
                    process_id: payload.process_id,
                    pid: Some(if child_pid == 0 {
                        kernel_pid
                    } else {
                        child_pid
                    }),
                }),
            ),
            events: Vec::new(),
        })
    }

    pub(crate) async fn write_stdin(
        &mut self,
        request: &RequestFrame,
        payload: WriteStdinRequest,
    ) -> Result<DispatchResult, SidecarError> {
        let (connection_id, session_id, vm_id) = self.vm_scope_for(&request.ownership)?;
        self.require_owned_vm(&connection_id, &session_id, &vm_id)?;

        let vm = self
            .vms
            .get_mut(&vm_id)
            .ok_or_else(|| missing_vm_error(&vm_id))?;
        let process = vm
            .active_processes
            .get_mut(&payload.process_id)
            .ok_or_else(|| {
                SidecarError::InvalidState(format!(
                    "VM {vm_id} has no active process {}",
                    payload.process_id
                ))
            })?;
        process.execution.write_stdin(&payload.chunk)?;
        write_kernel_process_stdin(&mut vm.kernel, process, &payload.chunk)?;

        Ok(DispatchResult {
            response: self.respond(
                request,
                ResponsePayload::StdinWritten(StdinWrittenResponse {
                    process_id: payload.process_id,
                    accepted_bytes: payload.chunk.len() as u64,
                }),
            ),
            events: Vec::new(),
        })
    }

    pub(crate) async fn close_stdin(
        &mut self,
        request: &RequestFrame,
        payload: CloseStdinRequest,
    ) -> Result<DispatchResult, SidecarError> {
        let (connection_id, session_id, vm_id) = self.vm_scope_for(&request.ownership)?;
        self.require_owned_vm(&connection_id, &session_id, &vm_id)?;

        let vm = self
            .vms
            .get_mut(&vm_id)
            .ok_or_else(|| missing_vm_error(&vm_id))?;
        let process = vm
            .active_processes
            .get_mut(&payload.process_id)
            .ok_or_else(|| {
                SidecarError::InvalidState(format!(
                    "VM {vm_id} has no active process {}",
                    payload.process_id
                ))
            })?;
        process.execution.close_stdin()?;
        close_kernel_process_stdin(&mut vm.kernel, process)?;

        Ok(DispatchResult {
            response: self.respond(
                request,
                ResponsePayload::StdinClosed(StdinClosedResponse {
                    process_id: payload.process_id,
                }),
            ),
            events: Vec::new(),
        })
    }

    pub(crate) async fn kill_process(
        &mut self,
        request: &RequestFrame,
        payload: KillProcessRequest,
    ) -> Result<DispatchResult, SidecarError> {
        let (connection_id, session_id, vm_id) = self.vm_scope_for(&request.ownership)?;
        self.require_owned_vm(&connection_id, &session_id, &vm_id)?;
        self.kill_process_internal(&vm_id, &payload.process_id, &payload.signal)?;

        Ok(DispatchResult {
            response: self.respond(
                request,
                ResponsePayload::ProcessKilled(ProcessKilledResponse {
                    process_id: payload.process_id,
                }),
            ),
            events: Vec::new(),
        })
    }

    pub(crate) async fn find_listener(
        &mut self,
        request: &RequestFrame,
        payload: FindListenerRequest,
    ) -> Result<DispatchResult, SidecarError> {
        let (connection_id, session_id, vm_id) = self.vm_scope_for(&request.ownership)?;
        self.require_owned_vm(&connection_id, &session_id, &vm_id)?;
        require_vm_inspection_permission(
            &self.bridge,
            &vm_id,
            "network.inspect",
            "network",
            &socket_query_resource(SocketQueryKind::TcpListener, &payload),
        )?;

        let listener =
            find_socket_state_entry(self.vms.get(&vm_id), SocketQueryKind::TcpListener, &payload)?;

        Ok(DispatchResult {
            response: self.respond(
                request,
                ResponsePayload::ListenerSnapshot(ListenerSnapshotResponse { listener }),
            ),
            events: Vec::new(),
        })
    }

    pub(crate) async fn get_process_snapshot(
        &mut self,
        request: &RequestFrame,
        _payload: GetProcessSnapshotRequest,
    ) -> Result<DispatchResult, SidecarError> {
        let (connection_id, session_id, vm_id) = self.vm_scope_for(&request.ownership)?;
        self.require_owned_vm(&connection_id, &session_id, &vm_id)?;
        require_vm_inspection_permission(
            &self.bridge,
            &vm_id,
            "process.inspect",
            "process",
            "process://snapshot",
        )?;

        let processes = self
            .vms
            .get_mut(&vm_id)
            .map(|vm| {
                prune_exited_process_snapshots(vm);
                snapshot_vm_processes(vm)
            })
            .unwrap_or_default();

        Ok(DispatchResult {
            response: self.respond(
                request,
                ResponsePayload::ProcessSnapshot(ProcessSnapshotResponse { processes }),
            ),
            events: Vec::new(),
        })
    }

    pub(crate) async fn find_bound_udp(
        &mut self,
        request: &RequestFrame,
        payload: FindBoundUdpRequest,
    ) -> Result<DispatchResult, SidecarError> {
        let (connection_id, session_id, vm_id) = self.vm_scope_for(&request.ownership)?;
        self.require_owned_vm(&connection_id, &session_id, &vm_id)?;

        let lookup_request = FindListenerRequest {
            host: payload.host,
            port: payload.port,
            path: None,
        };
        require_vm_inspection_permission(
            &self.bridge,
            &vm_id,
            "network.inspect",
            "network",
            &socket_query_resource(SocketQueryKind::UdpBound, &lookup_request),
        )?;
        let socket = find_socket_state_entry(
            self.vms.get(&vm_id),
            SocketQueryKind::UdpBound,
            &lookup_request,
        )?;

        Ok(DispatchResult {
            response: self.respond(
                request,
                ResponsePayload::BoundUdpSnapshot(BoundUdpSnapshotResponse { socket }),
            ),
            events: Vec::new(),
        })
    }

    pub(crate) async fn vm_fetch(
        &mut self,
        request: &RequestFrame,
        payload: VmFetchRequest,
    ) -> Result<DispatchResult, SidecarError> {
        let (connection_id, session_id, vm_id) = self.vm_scope_for(&request.ownership)?;
        self.require_owned_vm(&connection_id, &session_id, &vm_id)?;

        let vm = self
            .vms
            .get_mut(&vm_id)
            .ok_or_else(|| SidecarError::InvalidState(String::from("unknown sidecar VM")))?;
        let target_path = if payload.path.starts_with('/') {
            payload.path.clone()
        } else {
            format!("/{}", payload.path)
        };
        let request_url = Url::parse(&format!("http://127.0.0.1:{}{target_path}", payload.port))
            .map_err(|error| {
                SidecarError::InvalidState(format!(
                    "invalid vm.fetch target {target_path:?}: {error}"
                ))
            })?;
        let header_values: BTreeMap<String, Value> = serde_json::from_str(&payload.headers_json)
            .map_err(|error| {
                SidecarError::InvalidState(format!(
                    "vm.fetch headers_json must be valid JSON: {error}"
                ))
            })?;
        let options = JavascriptHttpRequestOptions {
            method: Some(payload.method),
            headers: header_values,
            body: payload.body,
            reject_unauthorized: None,
        };
        let headers = parse_http_header_collection(&options.headers, "vm.fetch headers")?;
        let target_process_id = find_kernel_http_listener_process(vm, payload.port);
        if let Some(target_process_id) = target_process_id {
            let max_fetch_response_bytes = vm.limits.http.max_fetch_response_bytes;
            let response_json = match dispatch_kernel_http_fetch(
                &self.bridge,
                &vm_id,
                vm,
                &target_process_id,
                payload.port,
                &target_path,
                &options,
                &headers,
                max_fetch_response_bytes,
            ) {
                Ok(response_json) => response_json,
                Err(error) => {
                    if let Some(exit_code) = kernel_http_fetch_target_exit_code(&error) {
                        let _ = vm;
                        self.finish_active_process_exit(&vm_id, &target_process_id, exit_code)?;
                    }
                    return Err(error);
                }
            };
            let response = self.respond(
                request,
                ResponsePayload::VmFetchResult(VmFetchResponse { response_json }),
            );
            ensure_vm_fetch_response_frame_within_limit(&response, self.config.max_frame_bytes)?;

            return Ok(DispatchResult {
                response,
                events: Vec::new(),
            });
        }

        let Some((target_process_id, server_id)) =
            vm.active_processes
                .iter()
                .find_map(|(process_id, process)| {
                    process
                        .http_servers
                        .iter()
                        .find(|(_, server)| server.guest_local_addr.port() == payload.port)
                        .map(|(server_id, _)| (process_id.clone(), *server_id))
                })
        else {
            return Err(SidecarError::Execution(format!(
                "vm.fetch could not find a guest HTTP listener on port {}",
                payload.port
            )));
        };
        let socket_paths = build_javascript_socket_path_context(vm)?;
        let resource_limits = vm.kernel.resource_limits().clone();
        let process = vm
            .active_processes
            .get_mut(&target_process_id)
            .ok_or_else(|| {
                SidecarError::InvalidState(format!(
                    "vm.fetch target process disappeared: {target_process_id}"
                ))
            })?;
        let request_json = serialize_http_loopback_request(&request_url, &options, &headers)?;
        let response_json = dispatch_loopback_http_request(LoopbackHttpDispatchRequest {
            bridge: &self.bridge,
            vm_id: &vm_id,
            dns: &vm.dns,
            socket_paths: &socket_paths,
            kernel: &mut vm.kernel,
            process,
            resource_limits: &resource_limits,
            server_id,
            request_json: &request_json,
        })?;

        let response = self.respond(
            request,
            ResponsePayload::VmFetchResult(VmFetchResponse { response_json }),
        );
        ensure_vm_fetch_response_frame_within_limit(&response, self.config.max_frame_bytes)?;

        Ok(DispatchResult {
            response,
            events: Vec::new(),
        })
    }

    pub(crate) async fn get_signal_state(
        &mut self,
        request: &RequestFrame,
        payload: GetSignalStateRequest,
    ) -> Result<DispatchResult, SidecarError> {
        let (connection_id, session_id, vm_id) = self.vm_scope_for(&request.ownership)?;
        self.require_owned_vm(&connection_id, &session_id, &vm_id)?;

        let handlers = self
            .vms
            .get(&vm_id)
            .and_then(|vm| vm.signal_states.get(&payload.process_id))
            .cloned()
            .unwrap_or_default();

        Ok(DispatchResult {
            response: self.respond(
                request,
                ResponsePayload::SignalState(SignalStateResponse {
                    process_id: payload.process_id,
                    handlers: handlers.into_iter().collect(),
                }),
            ),
            events: Vec::new(),
        })
    }

    pub(crate) async fn get_zombie_timer_count(
        &mut self,
        request: &RequestFrame,
        _payload: GetZombieTimerCountRequest,
    ) -> Result<DispatchResult, SidecarError> {
        let (connection_id, session_id, vm_id) = self.vm_scope_for(&request.ownership)?;
        self.require_owned_vm(&connection_id, &session_id, &vm_id)?;

        let count = self
            .vms
            .get(&vm_id)
            .map(|vm| vm.kernel.zombie_timer_count() as u64)
            .unwrap_or_default();

        Ok(DispatchResult {
            response: self.respond(
                request,
                ResponsePayload::ZombieTimerCount(ZombieTimerCountResponse { count }),
            ),
            events: Vec::new(),
        })
    }

    pub(crate) fn kill_process_internal(
        &mut self,
        vm_id: &str,
        process_id: &str,
        signal: &str,
    ) -> Result<(), SidecarError> {
        let signal_name = signal.to_owned();
        let signal = parse_signal(signal)?;
        let vm = self
            .vms
            .get_mut(vm_id)
            .ok_or_else(|| SidecarError::InvalidState(format!("unknown sidecar VM {vm_id}")))?;
        let process = vm.active_processes.get_mut(process_id).ok_or_else(|| {
            SidecarError::InvalidState(format!("VM {vm_id} has no active process {process_id}"))
        })?;
        let kernel_pid = process.kernel_pid;

        enum KillBehavior {
            Tool,
            SharedV8StateOnly,
            SharedV8Continue,
            SharedV8Terminate,
            SharedV8DispatchOrTerminate,
            Noop,
            HostPid(u32),
        }

        let behavior = match &process.execution {
            ActiveExecution::Tool(_) => KillBehavior::Tool,
            ActiveExecution::Javascript(execution)
                if execution.uses_shared_v8_runtime() && matches!(signal, 0 | libc::SIGSTOP) =>
            {
                KillBehavior::SharedV8StateOnly
            }
            ActiveExecution::Javascript(execution)
                if execution.uses_shared_v8_runtime() && signal == libc::SIGCONT =>
            {
                KillBehavior::SharedV8Continue
            }
            ActiveExecution::Wasm(execution)
                if execution.uses_shared_v8_runtime()
                    && matches!(signal, 0 | libc::SIGSTOP | libc::SIGCONT) =>
            {
                KillBehavior::SharedV8StateOnly
            }
            ActiveExecution::Python(execution)
                if execution.uses_shared_v8_runtime()
                    && matches!(signal, 0 | libc::SIGSTOP | libc::SIGCONT) =>
            {
                KillBehavior::SharedV8StateOnly
            }
            ActiveExecution::Javascript(execution)
                if execution.uses_shared_v8_runtime() && signal == SIGKILL =>
            {
                KillBehavior::SharedV8Terminate
            }
            ActiveExecution::Wasm(execution)
                if execution.uses_shared_v8_runtime() && signal == SIGKILL =>
            {
                KillBehavior::SharedV8Terminate
            }
            ActiveExecution::Javascript(execution) if execution.uses_shared_v8_runtime() => {
                KillBehavior::SharedV8DispatchOrTerminate
            }
            ActiveExecution::Wasm(execution) if execution.uses_shared_v8_runtime() => {
                KillBehavior::SharedV8Terminate
            }
            ActiveExecution::Python(execution) if execution.uses_shared_v8_runtime() => {
                KillBehavior::SharedV8Terminate
            }
            ActiveExecution::Javascript(execution) if execution.child_pid() == 0 => {
                KillBehavior::Noop
            }
            _ => KillBehavior::HostPid(process.execution.child_pid()),
        };

        match behavior {
            KillBehavior::Tool => {
                let ActiveExecution::Tool(execution) = &process.execution else {
                    unreachable!("kill behavior must match tool execution");
                };
                if signal != 0 {
                    execution.cancelled.store(true, Ordering::Relaxed);
                    process.queue_pending_execution_event(ActiveExecutionEvent::Exited(
                        128 + signal,
                    ))?;
                }
            }
            KillBehavior::SharedV8StateOnly => {
                if matches!(signal, libc::SIGSTOP | libc::SIGCONT) {
                    vm.kernel
                        .kill_process(EXECUTION_DRIVER_NAME, kernel_pid, signal)
                        .map_err(kernel_error)?;
                }
            }
            KillBehavior::SharedV8Continue => {
                vm.kernel
                    .kill_process(EXECUTION_DRIVER_NAME, kernel_pid, signal)
                    .map_err(kernel_error)?;
                if signal != 0 && !dispatch_v8_process_signal(process, signal)? {
                    process.execution.terminate()?;
                }
            }
            KillBehavior::SharedV8Terminate => {
                if signal != 0 && matches!(process.execution, ActiveExecution::Python(_)) {
                    close_kernel_process_stdin(&mut vm.kernel, process)?;
                }
                process.execution.terminate()?;
                let needs_synthetic_exit = matches!(process.execution, ActiveExecution::Wasm(_))
                    || (signal == SIGKILL
                        && matches!(process.execution, ActiveExecution::Javascript(_)));
                if signal != 0 && needs_synthetic_exit {
                    process.queue_pending_execution_event(ActiveExecutionEvent::Exited(
                        128 + signal,
                    ))?;
                }
            }
            KillBehavior::SharedV8DispatchOrTerminate => {
                if signal != 0 && !dispatch_v8_process_signal(process, signal)? {
                    process.execution.terminate()?;
                }
            }
            KillBehavior::Noop => {}
            KillBehavior::HostPid(pid) => {
                if signal != 0 && matches!(process.execution, ActiveExecution::Python(_)) {
                    close_kernel_process_stdin(&mut vm.kernel, process)?;
                }
                signal_runtime_process(pid, signal)?;
            }
        }
        emit_security_audit_event(
            &self.bridge,
            vm_id,
            "security.process.kill",
            audit_fields([
                (String::from("source"), String::from("control_plane")),
                (String::from("source_pid"), String::from("0")),
                (String::from("target_pid"), process.kernel_pid.to_string()),
                (String::from("process_id"), process_id.to_owned()),
                (String::from("signal"), signal_name),
                (
                    String::from("host_pid"),
                    process.execution.child_pid().to_string(),
                ),
            ]),
        );
        Ok(())
    }

    pub async fn pump_process_events(
        &mut self,
        ownership: &OwnershipScope,
    ) -> Result<bool, SidecarError> {
        let mut emitted_any = false;

        let mut queued_envelopes = Vec::new();
        {
            let pending_capacity = self.pending_process_event_capacity();
            let receiver = self.process_event_receiver.as_mut().ok_or_else(|| {
                SidecarError::InvalidState(String::from("process event receiver unavailable"))
            })?;
            loop {
                if queued_envelopes.len() >= pending_capacity {
                    if receiver.is_empty() {
                        break;
                    }
                    return Err(process_event_queue_overflow_error());
                }
                match receiver.try_recv() {
                    Ok(envelope) => {
                        queued_envelopes.push(envelope);
                        emitted_any = true;
                    }
                    Err(tokio::sync::mpsc::error::TryRecvError::Empty) => break,
                    Err(tokio::sync::mpsc::error::TryRecvError::Disconnected) => break,
                }
            }
        }
        for envelope in queued_envelopes {
            self.queue_pending_process_event(envelope)?;
        }

        let vm_ids = self.vm_ids_for_scope(ownership)?;
        for vm_id in vm_ids {
            while let Some(vm) = self.vms.get(&vm_id) {
                let connection_id = vm.connection_id.clone();
                let session_id = vm.session_id.clone();
                let process_ids = self
                    .vms
                    .get(&vm_id)
                    .map(|vm| vm.active_processes.keys().cloned().collect::<Vec<_>>())
                    .unwrap_or_default();
                let mut emitted_this_pass = false;

                for process_id in process_ids {
                    if self
                        .vms
                        .get(&vm_id)
                        .is_some_and(|vm| vm.detached_child_processes.contains(&process_id))
                    {
                        continue;
                    }
                    enum ProcessPollResult {
                        Event(Box<Option<ActiveExecutionEvent>>),
                        RecoverClosedChannel,
                    }
                    let poll_result = {
                        let Some(vm) = self.vms.get_mut(&vm_id) else {
                            continue;
                        };
                        let Some(process) = vm.active_processes.get_mut(&process_id) else {
                            continue;
                        };
                        if let Some(event) = process.pending_execution_events.pop_front() {
                            ProcessPollResult::Event(Box::new(Some(event)))
                        } else {
                            match process.execution.poll_event(Duration::ZERO).await {
                                Ok(event) => ProcessPollResult::Event(Box::new(event)),
                                Err(SidecarError::Execution(message))
                                    if (process.runtime == GuestRuntimeKind::JavaScript
                                        && closed_javascript_event_channel(&message))
                                        || (process.runtime == GuestRuntimeKind::Python
                                            && closed_python_event_channel(&message))
                                        || (process.runtime == GuestRuntimeKind::WebAssembly
                                            && closed_wasm_event_channel(&message)) =>
                                {
                                    ProcessPollResult::RecoverClosedChannel
                                }
                                Err(other) => return Err(other),
                            }
                        }
                    };
                    let event = match poll_result {
                        ProcessPollResult::Event(event) => *event,
                        ProcessPollResult::RecoverClosedChannel => {
                            self.recover_closed_root_runtime_process_event(&vm_id, &process_id)?
                        }
                    };

                    let Some(event) = event else {
                        continue;
                    };

                    if Self::internal_execution_event(&event) {
                        // These events are sidecar work items, not client-facing
                        // process events. Handle them immediately so a sibling
                        // process can service sync RPCs while another request
                        // waits on VM-local networking.
                        self.handle_execution_event(&vm_id, &process_id, event)?;
                    } else {
                        self.queue_pending_process_event(ProcessEventEnvelope {
                            connection_id: connection_id.clone(),
                            session_id: session_id.clone(),
                            vm_id: vm_id.clone(),
                            process_id: process_id.clone(),
                            event,
                        })?;
                    }
                    emitted_any = true;
                    emitted_this_pass = true;
                }

                if !emitted_this_pass {
                    break;
                }
            }

            if self.pump_detached_child_process_events(&vm_id)? {
                emitted_any = true;
            }
        }

        Ok(emitted_any)
    }

    fn internal_execution_event(event: &ActiveExecutionEvent) -> bool {
        matches!(
            event,
            ActiveExecutionEvent::JavascriptSyncRpcRequest(_)
                | ActiveExecutionEvent::PythonVfsRpcRequest(_)
                | ActiveExecutionEvent::SignalState { .. }
        )
    }

    fn recover_closed_root_runtime_process_event(
        &mut self,
        vm_id: &str,
        process_id: &str,
    ) -> Result<Option<ActiveExecutionEvent>, SidecarError> {
        let Some(vm) = self.vms.get_mut(vm_id) else {
            return Ok(None);
        };
        let Some(process) = vm.active_processes.get(process_id) else {
            return Ok(None);
        };
        if process.execution.uses_shared_v8_runtime() {
            return Ok(None);
        }
        if process.runtime != GuestRuntimeKind::JavaScript
            && process.runtime != GuestRuntimeKind::Python
            && process.runtime != GuestRuntimeKind::WebAssembly
        {
            return Ok(None);
        }
        let runtime_child_pid = process.execution.child_pid();
        if runtime_child_pid == 0 {
            return Ok(None);
        }
        if let Some(status) = runtime_child_exit_status(runtime_child_pid)? {
            return Ok(Some(ActiveExecutionEvent::Exited(status)));
        }
        if runtime_child_is_alive(runtime_child_pid)? {
            return Ok(None);
        }
        Ok(Some(ActiveExecutionEvent::Exited(0)))
    }

    fn active_process_by_path<'a>(
        process: &'a ActiveProcess,
        child_path: &[&str],
    ) -> Option<&'a ActiveProcess> {
        let mut current = process;
        for child_id in child_path {
            current = current.child_processes.get(*child_id)?;
        }
        Some(current)
    }

    fn active_process_by_path_mut<'a>(
        process: &'a mut ActiveProcess,
        child_path: &[&str],
    ) -> Option<&'a mut ActiveProcess> {
        let mut current = process;
        for child_id in child_path {
            current = current.child_processes.get_mut(*child_id)?;
        }
        Some(current)
    }

    fn active_process_by_owned_path_mut<'a>(
        process: &'a mut ActiveProcess,
        child_path: &[String],
    ) -> Option<&'a mut ActiveProcess> {
        let mut current = process;
        for child_id in child_path {
            current = current.child_processes.get_mut(child_id)?;
        }
        Some(current)
    }

    fn active_process_path_by_kernel_pid(
        process: &ActiveProcess,
        kernel_pid: u32,
    ) -> Option<Vec<String>> {
        if process.kernel_pid == kernel_pid {
            return Some(Vec::new());
        }

        for (child_id, child) in &process.child_processes {
            let Some(mut path) = Self::active_process_path_by_kernel_pid(child, kernel_pid) else {
                continue;
            };
            path.insert(0, child_id.clone());
            return Some(path);
        }

        None
    }

    fn descendant_parent_process<'a>(
        vm: &'a VmState,
        process_id: &str,
        child_path: &[&str],
    ) -> Option<&'a ActiveProcess> {
        let root = vm.active_processes.get(process_id)?;
        Self::active_process_by_path(root, child_path)
    }

    fn descendant_parent_process_mut<'a>(
        vm: &'a mut VmState,
        process_id: &str,
        child_path: &[&str],
    ) -> Option<&'a mut ActiveProcess> {
        let root = vm.active_processes.get_mut(process_id)?;
        Self::active_process_by_path_mut(root, child_path)
    }

    fn child_process_path_label(process_id: &str, child_path: &[&str]) -> String {
        if child_path.is_empty() {
            process_id.to_owned()
        } else {
            format!("{process_id}/{}", child_path.join("/"))
        }
    }

    fn adopt_detached_child_processes(
        current_process_id: &str,
        process: &mut ActiveProcess,
    ) -> Vec<(String, ActiveProcess)> {
        let mut adopted = Vec::new();
        let child_ids = process.child_processes.keys().cloned().collect::<Vec<_>>();
        for child_id in child_ids {
            let child_process_id = format!("{current_process_id}/{child_id}");
            let Some(mut child) = process.child_processes.remove(&child_id) else {
                continue;
            };
            if child.detached {
                adopted.push((child_process_id, child));
                continue;
            }

            adopted.extend(Self::adopt_detached_child_processes(
                &child_process_id,
                &mut child,
            ));
            process.child_processes.insert(child_id, child);
        }
        adopted
    }

    fn child_process_signal_key<'a>(process_id: &'a str, child_path: &[&'a str]) -> &'a str {
        child_path.last().copied().unwrap_or(process_id)
    }

    fn resolve_detached_child_process_path(
        vm: &VmState,
        detached_process_id: &str,
    ) -> Option<(String, Vec<String>)> {
        let root_process_id = vm
            .active_processes
            .keys()
            .filter(|candidate| {
                detached_process_id == candidate.as_str()
                    || detached_process_id
                        .strip_prefix(candidate.as_str())
                        .is_some_and(|remainder| remainder.starts_with('/'))
            })
            .max_by_key(|candidate| candidate.len())?
            .clone();

        let remainder = detached_process_id
            .strip_prefix(root_process_id.as_str())
            .unwrap_or_default();
        if remainder.is_empty() {
            return Some((root_process_id, Vec::new()));
        }

        Some((
            root_process_id,
            remainder
                .trim_start_matches('/')
                .split('/')
                .map(str::to_owned)
                .collect(),
        ))
    }

    fn pump_detached_child_process_events(&mut self, vm_id: &str) -> Result<bool, SidecarError> {
        let detached_process_ids = self
            .vms
            .get(vm_id)
            .map(|vm| {
                vm.detached_child_processes
                    .iter()
                    .cloned()
                    .collect::<Vec<_>>()
            })
            .unwrap_or_default();
        let mut emitted_any = false;
        for detached_process_id in detached_process_ids {
            let Some((root_process_id, child_path)) = self
                .vms
                .get(vm_id)
                .and_then(|vm| Self::resolve_detached_child_process_path(vm, &detached_process_id))
            else {
                if let Some(vm) = self.vms.get_mut(vm_id) {
                    vm.detached_child_processes.remove(&detached_process_id);
                }
                continue;
            };
            if child_path.is_empty() {
                loop {
                    enum ProcessPollResult {
                        Event(Box<Option<ActiveExecutionEvent>>),
                        RecoverClosedChannel,
                    }
                    let poll_result = {
                        let Some(vm) = self.vms.get_mut(vm_id) else {
                            break;
                        };
                        let Some(process) = vm.active_processes.get_mut(&root_process_id) else {
                            break;
                        };
                        if let Some(event) = process.pending_execution_events.pop_front() {
                            ProcessPollResult::Event(Box::new(Some(event)))
                        } else {
                            match process.execution.poll_event_blocking(Duration::ZERO) {
                                Ok(event) => ProcessPollResult::Event(Box::new(event)),
                                Err(SidecarError::Execution(message))
                                    if (process.runtime == GuestRuntimeKind::JavaScript
                                        && closed_javascript_event_channel(&message))
                                        || (process.runtime == GuestRuntimeKind::Python
                                            && closed_python_event_channel(&message))
                                        || (process.runtime == GuestRuntimeKind::WebAssembly
                                            && closed_wasm_event_channel(&message)) =>
                                {
                                    ProcessPollResult::RecoverClosedChannel
                                }
                                Err(error) => return Err(error),
                            }
                        }
                    };
                    let event = match poll_result {
                        ProcessPollResult::Event(event) => *event,
                        ProcessPollResult::RecoverClosedChannel => {
                            self.recover_closed_root_runtime_process_event(vm_id, &root_process_id)?
                        }
                    };
                    let Some(event) = event else {
                        break;
                    };
                    let Some((connection_id, session_id)) = self
                        .vms
                        .get(vm_id)
                        .map(|vm| (vm.connection_id.clone(), vm.session_id.clone()))
                    else {
                        break;
                    };
                    match event {
                        ActiveExecutionEvent::Stdout(chunk) => {
                            self.queue_pending_process_event(ProcessEventEnvelope {
                                connection_id,
                                session_id,
                                vm_id: vm_id.to_owned(),
                                process_id: detached_process_id.clone(),
                                event: ActiveExecutionEvent::Stdout(chunk),
                            })?;
                            emitted_any = true;
                        }
                        ActiveExecutionEvent::Stderr(chunk) => {
                            self.queue_pending_process_event(ProcessEventEnvelope {
                                connection_id,
                                session_id,
                                vm_id: vm_id.to_owned(),
                                process_id: detached_process_id.clone(),
                                event: ActiveExecutionEvent::Stderr(chunk),
                            })?;
                            emitted_any = true;
                        }
                        ActiveExecutionEvent::Exited(exit_code) => {
                            if let Some(vm) = self.vms.get_mut(vm_id) {
                                vm.detached_child_processes.remove(&detached_process_id);
                            }
                            self.queue_pending_process_event(ProcessEventEnvelope {
                                connection_id,
                                session_id,
                                vm_id: vm_id.to_owned(),
                                process_id: detached_process_id.clone(),
                                event: ActiveExecutionEvent::Exited(exit_code),
                            })?;
                            emitted_any = true;
                            break;
                        }
                        ActiveExecutionEvent::JavascriptSyncRpcRequest(request) => {
                            self.handle_javascript_sync_rpc_request(
                                vm_id,
                                &root_process_id,
                                request,
                            )?;
                        }
                        ActiveExecutionEvent::PythonVfsRpcRequest(request) => {
                            self.handle_python_vfs_rpc_request(vm_id, &root_process_id, *request)?;
                        }
                        ActiveExecutionEvent::SignalState {
                            signal,
                            registration,
                        } => {
                            if let Some(vm) = self.vms.get_mut(vm_id) {
                                vm.signal_states
                                    .entry(root_process_id.clone())
                                    .or_default()
                                    .insert(signal, registration);
                            }
                        }
                    }
                }
                continue;
            }

            let parent_path = child_path[..child_path.len() - 1]
                .iter()
                .map(String::as_str)
                .collect::<Vec<_>>();
            let child_process_id = child_path.last().expect("child path cannot be empty");

            loop {
                let event = match self.poll_descendant_javascript_child_process(
                    vm_id,
                    &root_process_id,
                    &parent_path,
                    child_process_id,
                    0,
                ) {
                    Ok(event) => event,
                    Err(SidecarError::InvalidState(message))
                        if message.contains("unknown child process")
                            || message.contains("unknown child process path") =>
                    {
                        if let Some(vm) = self.vms.get_mut(vm_id) {
                            vm.detached_child_processes.remove(&detached_process_id);
                        }
                        break;
                    }
                    Err(error) if is_javascript_child_process_gone_error(&error) => {
                        if let Some(vm) = self.vms.get_mut(vm_id) {
                            vm.detached_child_processes.remove(&detached_process_id);
                        }
                        break;
                    }
                    Err(error) => return Err(error),
                };

                let Some(event_type) = event.get("type").and_then(Value::as_str) else {
                    break;
                };
                let Some((connection_id, session_id)) = self
                    .vms
                    .get(vm_id)
                    .map(|vm| (vm.connection_id.clone(), vm.session_id.clone()))
                else {
                    break;
                };

                let envelope = match event_type {
                    "stdout" => Some(ProcessEventEnvelope {
                        connection_id: connection_id.clone(),
                        session_id: session_id.clone(),
                        vm_id: vm_id.to_owned(),
                        process_id: detached_process_id.clone(),
                        event: ActiveExecutionEvent::Stdout(javascript_sync_rpc_bytes_arg(
                            &[event.get("data").cloned().unwrap_or(Value::Null)],
                            0,
                            "detached child_process stdout",
                        )?),
                    }),
                    "stderr" => Some(ProcessEventEnvelope {
                        connection_id: connection_id.clone(),
                        session_id: session_id.clone(),
                        vm_id: vm_id.to_owned(),
                        process_id: detached_process_id.clone(),
                        event: ActiveExecutionEvent::Stderr(javascript_sync_rpc_bytes_arg(
                            &[event.get("data").cloned().unwrap_or(Value::Null)],
                            0,
                            "detached child_process stderr",
                        )?),
                    }),
                    "exit" => {
                        if let Some(vm) = self.vms.get_mut(vm_id) {
                            vm.detached_child_processes.remove(&detached_process_id);
                        }
                        Some(ProcessEventEnvelope {
                            connection_id,
                            session_id,
                            vm_id: vm_id.to_owned(),
                            process_id: detached_process_id.clone(),
                            event: ActiveExecutionEvent::Exited(
                                event
                                    .get("exitCode")
                                    .and_then(Value::as_i64)
                                    .map(|value| value as i32)
                                    .unwrap_or(1),
                            ),
                        })
                    }
                    _ => None,
                };

                let Some(envelope) = envelope else {
                    break;
                };
                self.queue_pending_process_event(envelope)?;
                emitted_any = true;

                if event_type == "exit" {
                    break;
                }
            }
        }

        Ok(emitted_any)
    }
    pub(crate) fn drain_queued_descendant_javascript_child_process_events(
        &mut self,
        vm_id: &str,
        process_id: &str,
        child_path: &[&str],
    ) -> Result<(), SidecarError> {
        if child_path.is_empty() {
            return Ok(());
        }
        let target_process_id = Self::child_process_path_label(process_id, child_path);
        let mut child_capacity = self
            .vms
            .get(vm_id)
            .and_then(|vm| vm.active_processes.get(process_id))
            .and_then(|root| descendant_pending_execution_event_capacity(root, child_path));

        let mut deferred = VecDeque::new();
        while let Some(envelope) = self.pending_process_events.pop_front() {
            if envelope.vm_id == vm_id && envelope.process_id == target_process_id {
                if matches!(child_capacity, Some(0)) {
                    self.pending_process_events.push_front(envelope);
                    while let Some(deferred_envelope) = deferred.pop_back() {
                        self.pending_process_events.push_front(deferred_envelope);
                    }
                    return Err(process_event_queue_overflow_error());
                }
                if let Some(vm) = self.vms.get_mut(vm_id) {
                    if let Some(root) = vm.active_processes.get_mut(process_id) {
                        if let Some(child) = Self::active_process_by_path_mut(root, child_path) {
                            child.queue_pending_execution_event(envelope.event)?;
                            child_capacity = child_capacity.map(|capacity| capacity - 1);
                            continue;
                        }
                    }
                }
            }
            deferred.push_back(envelope);
        }
        self.pending_process_events = deferred;

        let mut queued = Vec::new();
        {
            let transfer_capacity = self
                .pending_process_event_capacity()
                .min(child_capacity.unwrap_or(usize::MAX));
            let receiver = self.process_event_receiver.as_mut().ok_or_else(|| {
                SidecarError::InvalidState(String::from("process event receiver unavailable"))
            })?;
            loop {
                if queued.len() >= transfer_capacity {
                    if receiver.is_empty() {
                        break;
                    }
                    return Err(process_event_queue_overflow_error());
                }
                match receiver.try_recv() {
                    Ok(envelope) => queued.push(envelope),
                    Err(tokio::sync::mpsc::error::TryRecvError::Empty) => break,
                    Err(tokio::sync::mpsc::error::TryRecvError::Disconnected) => break,
                }
            }
        }
        for envelope in queued {
            if envelope.vm_id == vm_id && envelope.process_id == target_process_id {
                if let Some(vm) = self.vms.get_mut(vm_id) {
                    if let Some(root) = vm.active_processes.get_mut(process_id) {
                        if let Some(child) = Self::active_process_by_path_mut(root, child_path) {
                            child.queue_pending_execution_event(envelope.event)?;
                            continue;
                        }
                    }
                }
            }
            self.queue_pending_process_event(envelope)?;
        }

        Ok(())
    }

    pub(crate) fn handle_execution_event(
        &mut self,
        vm_id: &str,
        process_id: &str,
        event: ActiveExecutionEvent,
    ) -> Result<Option<EventFrame>, SidecarError> {
        let Some(vm) = self.vms.get(vm_id) else {
            log_stale_process_event(&self.bridge, vm_id, process_id, "execution event dispatch");
            return Ok(None);
        };
        if !vm.active_processes.contains_key(process_id) {
            log_stale_process_event(&self.bridge, vm_id, process_id, "execution event dispatch");
            return Ok(None);
        }
        let (connection_id, session_id) = { (vm.connection_id.clone(), vm.session_id.clone()) };
        let ownership = OwnershipScope::vm(&connection_id, &session_id, vm_id);

        if self.capture_extension_process_output_event(vm_id, process_id, &event) {
            return Ok(None);
        }

        match event {
            ActiveExecutionEvent::Stdout(chunk) => Ok(Some(EventFrame::new(
                ownership,
                EventPayload::ProcessOutput(ProcessOutputEvent {
                    process_id: process_id.to_owned(),
                    channel: StreamChannel::Stdout,
                    chunk,
                }),
            ))),
            ActiveExecutionEvent::Stderr(chunk) => Ok(Some(EventFrame::new(
                ownership,
                EventPayload::ProcessOutput(ProcessOutputEvent {
                    process_id: process_id.to_owned(),
                    channel: StreamChannel::Stderr,
                    chunk,
                }),
            ))),
            ActiveExecutionEvent::JavascriptSyncRpcRequest(request) => {
                self.handle_javascript_sync_rpc_request(vm_id, process_id, request)?;
                Ok(None)
            }
            ActiveExecutionEvent::PythonVfsRpcRequest(request) => {
                self.handle_python_vfs_rpc_request(vm_id, process_id, *request)?;
                Ok(None)
            }
            ActiveExecutionEvent::SignalState {
                signal,
                registration,
            } => {
                let Some(vm) = self.vms.get_mut(vm_id) else {
                    return Ok(None);
                };
                if !vm.active_processes.contains_key(process_id) {
                    return Ok(None);
                }
                vm.signal_states
                    .entry(process_id.to_owned())
                    .or_default()
                    .insert(signal, registration);
                Ok(None)
            }
            ActiveExecutionEvent::Exited(exit_code) => {
                let became_idle = self
                    .finish_active_process_exit(vm_id, process_id, exit_code)?
                    .unwrap_or(false);

                if became_idle {
                    self.bridge.emit_lifecycle(vm_id, LifecycleState::Ready)?;
                }

                Ok(Some(EventFrame::new(
                    ownership,
                    EventPayload::ProcessExited(ProcessExitedEvent {
                        process_id: process_id.to_owned(),
                        exit_code,
                    }),
                )))
            }
        }
    }

    pub(crate) fn finish_active_process_exit(
        &mut self,
        vm_id: &str,
        process_id: &str,
        exit_code: i32,
    ) -> Result<Option<bool>, SidecarError> {
        let Some(vm) = self.vms.get_mut(vm_id) else {
            log_stale_process_event(&self.bridge, vm_id, process_id, "process exit cleanup");
            return Ok(None);
        };
        if !vm.active_processes.contains_key(process_id) {
            log_stale_process_event(&self.bridge, vm_id, process_id, "process exit cleanup");
            return Ok(None);
        }

        prune_exited_process_snapshots(vm);
        let process_table = vm.kernel.list_processes();
        let Some(mut process) = vm.active_processes.remove(process_id) else {
            return Ok(None);
        };
        if let Some(info) = process_table.get(&process.kernel_pid) {
            vm.exited_process_snapshots
                .push_back(ExitedProcessSnapshot {
                    captured_at: Instant::now(),
                    process: build_process_snapshot_entry(
                        process_id,
                        &process,
                        info,
                        Some(exit_code),
                    ),
                });
        }
        let detached_children = Self::adopt_detached_child_processes(process_id, &mut process);
        sync_process_host_writes_to_kernel(vm, &process)?;
        terminate_child_process_tree(&mut vm.kernel, &mut process);
        process.kernel_handle.finish(exit_code);
        let _ = vm.kernel.wait_and_reap(process.kernel_pid);
        vm.signal_states.remove(process_id);
        for (detached_process_id, detached_child) in detached_children {
            vm.detached_child_processes
                .insert(detached_process_id.clone());
            vm.active_processes
                .insert(detached_process_id, detached_child);
        }
        let became_idle = vm.active_processes.is_empty();
        self.prune_extension_process_resource(process_id);

        Ok(Some(became_idle))
    }

    pub(crate) fn drain_process_events_blocking_with_limit(
        &mut self,
        vm_id: &str,
        process_id: &str,
        max_events: usize,
    ) -> Result<Vec<ActiveExecutionEvent>, SidecarError> {
        let mut events = Vec::new();
        if max_events == 0 {
            return Ok(events);
        }
        let mut deadline = Instant::now() + Duration::from_millis(150);

        loop {
            if events.len() >= max_events {
                break;
            }
            let event = {
                let Some(vm) = self.vms.get_mut(vm_id) else {
                    break;
                };
                let Some(process) = vm.active_processes.get_mut(process_id) else {
                    break;
                };
                if let Some(event) = process.pending_execution_events.pop_front() {
                    Some(event)
                } else {
                    match process.execution.poll_event_blocking(Duration::ZERO) {
                        Ok(event) => event,
                        Err(SidecarError::Execution(_)) => None,
                        Err(other) => return Err(other),
                    }
                }
            };

            let Some(event) = event else {
                if Instant::now() >= deadline {
                    break;
                }
                let blocking_wait = deadline.saturating_duration_since(Instant::now());
                if blocking_wait.is_zero() {
                    break;
                }
                if events.len() >= max_events {
                    break;
                }
                let delayed_event = {
                    let Some(vm) = self.vms.get_mut(vm_id) else {
                        break;
                    };
                    let Some(process) = vm.active_processes.get_mut(process_id) else {
                        break;
                    };
                    if let Some(event) = process.pending_execution_events.pop_front() {
                        Some(event)
                    } else {
                        match process.execution.poll_event_blocking(blocking_wait) {
                            Ok(event) => event,
                            Err(SidecarError::Execution(_)) => None,
                            Err(other) => return Err(other),
                        }
                    }
                };
                let Some(event) = delayed_event else {
                    break;
                };
                events.push(event);
                deadline = Instant::now() + Duration::from_millis(150);
                continue;
            };
            events.push(event);
            deadline = Instant::now() + Duration::from_millis(150);
        }

        Ok(events)
    }

    pub(crate) fn handle_python_vfs_rpc_request(
        &mut self,
        vm_id: &str,
        process_id: &str,
        request: PythonVfsRpcRequest,
    ) -> Result<(), SidecarError> {
        match request.method {
            PythonVfsRpcMethod::Read
            | PythonVfsRpcMethod::Write
            | PythonVfsRpcMethod::Stat
            | PythonVfsRpcMethod::ReadDir
            | PythonVfsRpcMethod::Mkdir => {
                filesystem_handle_python_vfs_rpc_request(self, vm_id, process_id, request)
            }
            PythonVfsRpcMethod::HttpRequest => {
                self.handle_python_http_rpc_request(vm_id, process_id, request)
            }
            PythonVfsRpcMethod::DnsLookup => {
                self.handle_python_dns_rpc_request(vm_id, process_id, request)
            }
            PythonVfsRpcMethod::SubprocessRun => {
                self.handle_python_subprocess_rpc_request(vm_id, process_id, request)
            }
        }
    }

    fn handle_python_http_rpc_request(
        &mut self,
        vm_id: &str,
        process_id: &str,
        request: PythonVfsRpcRequest,
    ) -> Result<(), SidecarError> {
        let Some(vm) = self.vms.get(vm_id) else {
            return Ok(());
        };
        if !vm.active_processes.contains_key(process_id) {
            return Ok(());
        }
        let response = (|| {
            let url_text = request.url.as_deref().ok_or_else(|| {
                SidecarError::InvalidState(String::from("python httpRequest requires a url"))
            })?;
            let url = Url::parse(url_text)
                .map_err(|error| SidecarError::Execution(format!("ERR_INVALID_URL: {error}")))?;
            let host = url.host_str().ok_or_else(|| {
                SidecarError::Execution(String::from("ERR_INVALID_URL: missing host"))
            })?;
            let port = url.port_or_known_default().ok_or_else(|| {
                SidecarError::Execution(String::from("ERR_INVALID_URL: missing port"))
            })?;
            self.bridge.require_network_access(
                vm_id,
                NetworkOperation::Http,
                format_tcp_resource(host, port),
            )?;
            // Pin the outbound connection to the IP addresses that pass the
            // egress range guard at resolution time. A literal IP is validated
            // directly; a hostname is resolved once here and the resulting
            // address set is pinned into the HTTP client's resolver below so a
            // rebinding DNS server cannot make the second (TLS/TCP) lookup land
            // on a private/link-local/metadata IP that this check rejected.
            let pinned_addresses = if let Ok(literal_ip) = host.parse::<IpAddr>() {
                filter_dns_safe_ip_addrs(vec![literal_ip], host)?
            } else {
                filter_dns_safe_ip_addrs(
                    resolve_dns_ip_addrs(
                        &self.bridge,
                        &vm.kernel,
                        vm_id,
                        &vm.dns,
                        host,
                        DnsLookupPolicy::SkipPermissions,
                    )?,
                    host,
                )?
            };
            let mut headers = BTreeMap::new();
            for (name, value) in &request.headers {
                headers.insert(name.clone(), Value::String(value.clone()));
            }
            let options = JavascriptHttpRequestOptions {
                method: Some(
                    request
                        .http_method
                        .clone()
                        .unwrap_or_else(|| String::from("GET")),
                ),
                headers,
                body: request.body_base64.as_deref().map(|body| {
                    String::from_utf8(
                        base64::engine::general_purpose::STANDARD
                            .decode(body)
                            .unwrap_or_default(),
                    )
                    .unwrap_or_default()
                }),
                reject_unauthorized: None,
            };
            let headers =
                parse_http_header_collection(&options.headers, "python httpRequest headers")?;
            let response =
                issue_outbound_http_request(&url, &options, &headers, &pinned_addresses)?;
            let payload_json = response.as_str().ok_or_else(|| {
                SidecarError::Execution(String::from(
                    "python httpRequest returned a non-string response payload",
                ))
            })?;
            let payload: Value = serde_json::from_str(payload_json).map_err(|error| {
                SidecarError::Execution(format!(
                    "python httpRequest response must be valid JSON: {error}"
                ))
            })?;
            let header_map = payload
                .get("headers")
                .and_then(Value::as_array)
                .map(|entries| {
                    let mut normalized = BTreeMap::<String, Vec<String>>::new();
                    for entry in entries {
                        let Some(pair) = entry.as_array() else {
                            continue;
                        };
                        let Some(name) = pair.first().and_then(Value::as_str) else {
                            continue;
                        };
                        let Some(value) = pair.get(1).and_then(Value::as_str) else {
                            continue;
                        };
                        normalized
                            .entry(name.to_owned())
                            .or_default()
                            .push(value.to_owned());
                    }
                    normalized
                })
                .unwrap_or_default();
            Ok(PythonVfsRpcResponsePayload::Http {
                status: payload
                    .get("status")
                    .and_then(Value::as_u64)
                    .map(|value| value as u16)
                    .unwrap_or_default(),
                reason: payload
                    .get("statusText")
                    .and_then(Value::as_str)
                    .unwrap_or_default()
                    .to_owned(),
                url: payload
                    .get("url")
                    .and_then(Value::as_str)
                    .unwrap_or(url_text)
                    .to_owned(),
                headers: header_map,
                body_base64: payload
                    .get("body")
                    .and_then(Value::as_str)
                    .unwrap_or_default()
                    .to_owned(),
            })
        })();

        self.respond_python_rpc(vm_id, process_id, request.id, response)
    }

    fn handle_python_dns_rpc_request(
        &mut self,
        vm_id: &str,
        process_id: &str,
        request: PythonVfsRpcRequest,
    ) -> Result<(), SidecarError> {
        let Some(vm) = self.vms.get(vm_id) else {
            return Ok(());
        };
        if !vm.active_processes.contains_key(process_id) {
            return Ok(());
        }
        let response = (|| {
            let hostname = request.hostname.as_deref().ok_or_else(|| {
                SidecarError::InvalidState(String::from("python dnsLookup requires a hostname"))
            })?;
            let mut addresses = filter_dns_safe_ip_addrs(
                resolve_dns_ip_addrs(
                    &self.bridge,
                    &vm.kernel,
                    vm_id,
                    &vm.dns,
                    hostname,
                    DnsLookupPolicy::CheckPermissions,
                )?,
                hostname,
            )?;
            if let Some(family) = request.family {
                addresses.retain(|address| {
                    matches!((family, address), (4, IpAddr::V4(_)) | (6, IpAddr::V6(_)))
                });
            }
            Ok(PythonVfsRpcResponsePayload::DnsLookup {
                addresses: addresses
                    .into_iter()
                    .map(|address| address.to_string())
                    .collect(),
            })
        })();

        self.respond_python_rpc(vm_id, process_id, request.id, response)
    }

    fn handle_python_subprocess_rpc_request(
        &mut self,
        vm_id: &str,
        process_id: &str,
        request: PythonVfsRpcRequest,
    ) -> Result<(), SidecarError> {
        let command = request.command.clone().ok_or_else(|| {
            SidecarError::InvalidState(String::from("python subprocessRun requires a command"))
        })?;
        let (internal_bootstrap_env, cwd) = {
            let Some(vm) = self.vms.get(vm_id) else {
                return Ok(());
            };
            let Some(process) = vm.active_processes.get(process_id) else {
                return Ok(());
            };
            let virtual_home = guest_virtual_home(vm);
            let cwd = request.cwd.clone().or_else(|| {
                guest_runtime_path_for_host_path(
                    &vm.guest_env,
                    &virtual_home,
                    &vm.host_cwd,
                    &process.host_cwd.to_string_lossy(),
                )
            });
            (
                sanitize_javascript_child_process_internal_bootstrap_env(&vm.guest_env),
                cwd,
            )
        };
        let response = self
            .spawn_javascript_child_process_sync(
                vm_id,
                process_id,
                JavascriptChildProcessSpawnRequest {
                    command,
                    args: request.args.clone(),
                    options: JavascriptChildProcessSpawnOptions {
                        cwd,
                        env: request.env.clone(),
                        input: None,
                        internal_bootstrap_env,
                        shell: request.shell,
                        detached: false,
                        stdio: vec![
                            String::from("pipe"),
                            String::from("pipe"),
                            String::from("pipe"),
                        ],
                        timeout: None,
                        kill_signal: None,
                    },
                },
                request.max_buffer,
            )
            .map(|payload| PythonVfsRpcResponsePayload::SubprocessRun {
                exit_code: payload
                    .get("code")
                    .and_then(Value::as_i64)
                    .map(|value| value as i32)
                    .unwrap_or(1),
                stdout: payload
                    .get("stdout")
                    .and_then(Value::as_str)
                    .unwrap_or_default()
                    .to_owned(),
                stderr: payload
                    .get("stderr")
                    .and_then(Value::as_str)
                    .unwrap_or_default()
                    .to_owned(),
                max_buffer_exceeded: payload
                    .get("maxBufferExceeded")
                    .and_then(Value::as_bool)
                    .unwrap_or(false),
            });

        self.respond_python_rpc(vm_id, process_id, request.id, response)
    }

    fn respond_python_rpc(
        &mut self,
        vm_id: &str,
        process_id: &str,
        request_id: u64,
        response: Result<PythonVfsRpcResponsePayload, SidecarError>,
    ) -> Result<(), SidecarError> {
        let Some(vm) = self.vms.get_mut(vm_id) else {
            return Ok(());
        };
        let Some(process) = vm.active_processes.get_mut(process_id) else {
            return Ok(());
        };
        let result = match response {
            Ok(payload) => process
                .execution
                .respond_python_vfs_rpc_success(request_id, payload),
            Err(error) => process.execution.respond_python_vfs_rpc_error(
                request_id,
                "ERR_AGENTOS_PYTHON_VFS_RPC",
                error.to_string(),
            ),
        };
        match result {
            Ok(()) => Ok(()),
            Err(error) if is_broken_pipe_error(&error) => Ok(()),
            Err(error) => Err(error),
        }
    }

    pub(crate) fn resolve_javascript_child_process_execution(
        &self,
        vm: &VmState,
        parent_env: &BTreeMap<String, String>,
        parent_guest_cwd: &str,
        parent_host_cwd: &Path,
        request: &JavascriptChildProcessSpawnRequest,
    ) -> Result<ResolvedChildProcessExecution, SidecarError> {
        let mut runtime_env = parent_env.clone();
        runtime_env.extend(request.options.internal_bootstrap_env.clone());
        let (guest_cwd, host_cwd_override) = request
            .options
            .cwd
            .as_deref()
            .map(|cwd| {
                let normalized_parent_host_cwd = normalize_host_path(parent_host_cwd);
                let requested_host_cwd = normalize_host_path(Path::new(cwd));
                if path_is_within_root(&requested_host_cwd, &normalized_parent_host_cwd) {
                    let relative = requested_host_cwd
                        .strip_prefix(&normalized_parent_host_cwd)
                        .unwrap_or_else(|_| Path::new(""));
                    let relative = relative.to_string_lossy().replace('\\', "/");
                    let guest_cwd = if relative.is_empty() {
                        parent_guest_cwd.to_owned()
                    } else {
                        normalize_path(&format!("{parent_guest_cwd}/{relative}"))
                    };
                    (guest_cwd, Some(requested_host_cwd))
                } else if Path::new(cwd).is_relative() {
                    (
                        normalize_path(&format!("{parent_guest_cwd}/{cwd}")),
                        Some(normalize_host_path(&parent_host_cwd.join(cwd))),
                    )
                } else {
                    (normalize_path(cwd), None)
                }
            })
            .unwrap_or_else(|| (parent_guest_cwd.to_owned(), None));
        let inherited_host_cwd = (host_cwd_override.is_none() && guest_cwd == parent_guest_cwd)
            .then(|| normalize_host_path(parent_host_cwd));
        let host_cwd = host_cwd_override
            .or(inherited_host_cwd)
            .or_else(|| {
                host_runtime_path_for_guest_path_with_env(
                    vm,
                    &runtime_env,
                    &guest_cwd,
                    parent_host_cwd,
                )
            })
            .unwrap_or_else(|| {
                let candidate = PathBuf::from(&guest_cwd);
                if guest_cwd == parent_guest_cwd {
                    normalize_host_path(parent_host_cwd)
                } else if candidate.is_absolute() {
                    shadow_path_for_guest(vm, &guest_cwd)
                } else {
                    vm.host_cwd.clone()
                }
            });
        let mut env = parent_env.clone();
        env.extend(request.options.env.clone());
        // Child JavaScript executions must resolve their own entrypoint/eval state.
        // Reusing the parent's values makes the sidecar load the wrong source file.
        env.remove("AGENTOS_GUEST_ENTRYPOINT");
        env.remove("AGENTOS_NODE_EVAL");

        let (command, process_args) = if request.options.shell {
            let tokens = tokenize_shell_free_command(&request.command);
            let requires_shell = command_requires_shell(&request.command)
                || tokens.first().is_some_and(|command| {
                    is_posix_shell_builtin(command) || shell_first_token_requires_shell(command)
                });
            if requires_shell {
                if !vm.command_guest_paths.contains_key("sh") {
                    return Err(SidecarError::InvalidState(format!(
                        "shell-mode child_process command requires /bin/sh, which is not \
                         installed in this VM (install a software package that provides sh, \
                         for example @secure-exec/coreutils): {}",
                        request.command
                    )));
                }
                (
                    String::from("sh"),
                    vec![String::from("-c"), request.command.clone()],
                )
            } else {
                let Some((command, args)) = tokens.split_first() else {
                    return Err(SidecarError::InvalidState(String::from(
                        "child_process shell command must not be empty",
                    )));
                };
                (command.clone(), args.to_vec())
            }
        } else {
            (request.command.clone(), request.args.clone())
        };
        let process_args = apply_shell_cwd_prefix(&command, process_args, &guest_cwd);
        if is_tool_command(vm, &command) {
            let command = normalized_tool_command_name(&command).unwrap_or(command);
            return Ok(ResolvedChildProcessExecution {
                command: command.clone(),
                process_args: std::iter::once(command.clone())
                    .chain(process_args.iter().cloned())
                    .collect(),
                runtime: GuestRuntimeKind::JavaScript,
                entrypoint: command,
                execution_args: process_args,
                env,
                guest_cwd,
                host_cwd,
                wasm_permission_tier: None,
                tool_command: true,
            });
        }

        if is_path_like_specifier(&command)
            && matches!(
                Path::new(&command).extension().and_then(|ext| ext.to_str()),
                Some("js" | "mjs" | "cjs" | "ts" | "mts" | "cts")
            )
        {
            let guest_entrypoint = if command.starts_with('/') {
                normalize_path(&command)
            } else if command.starts_with("file:") {
                normalize_path(command.trim_start_matches("file:"))
            } else {
                normalize_path(&format!("{guest_cwd}/{command}"))
            };
            let host_entrypoint = if command.starts_with("./") || command.starts_with("../") {
                normalize_host_path(&host_cwd.join(&command))
            } else {
                host_runtime_path_for_guest_path_with_env(
                    vm,
                    &runtime_env,
                    &guest_entrypoint,
                    parent_host_cwd,
                )
                .unwrap_or_else(|| {
                    let candidate = PathBuf::from(&guest_entrypoint);
                    if candidate.is_absolute() {
                        candidate
                    } else {
                        host_cwd.join(&guest_entrypoint)
                    }
                })
            };
            env.insert(String::from("AGENTOS_GUEST_ENTRYPOINT"), guest_entrypoint);
            let guest_entrypoint = env.get("AGENTOS_GUEST_ENTRYPOINT").cloned();
            prepare_guest_runtime_env(vm, &mut env, &guest_cwd, &host_cwd, guest_entrypoint)?;

            return Ok(ResolvedChildProcessExecution {
                command: command.clone(),
                process_args: std::iter::once(command)
                    .chain(process_args.iter().cloned())
                    .collect(),
                runtime: GuestRuntimeKind::JavaScript,
                entrypoint: host_entrypoint.to_string_lossy().into_owned(),
                execution_args: process_args,
                env,
                guest_cwd,
                host_cwd,
                wasm_permission_tier: None,
                tool_command: false,
            });
        }

        if is_node_runtime_command(&command) {
            if let Some(cli) = resolve_host_node_cli_entrypoint(&command) {
                env.insert(
                    String::from("AGENTOS_NODE_EVAL"),
                    build_host_node_cli_eval(&cli),
                );
                prepare_guest_runtime_env(vm, &mut env, &guest_cwd, &host_cwd, None)?;
                add_runtime_guest_path_mapping(&mut env, &cli.guest_root, &cli.package_root);
                add_runtime_host_access_path(
                    &mut env,
                    "AGENTOS_EXTRA_FS_READ_PATHS",
                    &cli.package_root,
                    true,
                );

                return Ok(ResolvedChildProcessExecution {
                    command: command.clone(),
                    process_args: std::iter::once(command.clone())
                        .chain(process_args.iter().cloned())
                        .collect(),
                    runtime: GuestRuntimeKind::JavaScript,
                    entrypoint: String::from("-e"),
                    execution_args: std::iter::once(cli.guest_entrypoint.clone())
                        .chain(process_args.iter().cloned())
                        .collect(),
                    env,
                    guest_cwd,
                    host_cwd,
                    wasm_permission_tier: None,
                    tool_command: false,
                });
            }

            if process_args.is_empty() {
                env.insert(String::from("AGENTOS_NODE_EVAL"), String::new());
                prepare_guest_runtime_env(vm, &mut env, &guest_cwd, &host_cwd, None)?;

                return Ok(ResolvedChildProcessExecution {
                    command: command.clone(),
                    process_args: vec![command.clone()],
                    runtime: GuestRuntimeKind::JavaScript,
                    entrypoint: String::from("-e"),
                    execution_args: Vec::new(),
                    env,
                    guest_cwd,
                    host_cwd,
                    wasm_permission_tier: None,
                    tool_command: false,
                });
            }

            if let Some((entrypoint, execution_args)) =
                resolve_special_node_cli_invocation(&process_args, &mut env)
            {
                prepare_guest_runtime_env(vm, &mut env, &guest_cwd, &host_cwd, None)?;

                return Ok(ResolvedChildProcessExecution {
                    command: command.clone(),
                    process_args: std::iter::once(command.clone())
                        .chain(process_args.iter().cloned())
                        .collect(),
                    runtime: GuestRuntimeKind::JavaScript,
                    entrypoint,
                    execution_args,
                    env,
                    guest_cwd,
                    host_cwd,
                    wasm_permission_tier: None,
                    tool_command: false,
                });
            }

            let Some(entrypoint_specifier) = process_args.first() else {
                return Err(SidecarError::InvalidState(format!(
                    "{command} child_process spawn requires an entrypoint"
                )));
            };

            let (entrypoint, execution_args) = if is_path_like_specifier(entrypoint_specifier) {
                let guest_entrypoint = if entrypoint_specifier.starts_with('/') {
                    normalize_path(entrypoint_specifier)
                } else if entrypoint_specifier.starts_with("file:") {
                    normalize_path(entrypoint_specifier.trim_start_matches("file:"))
                } else {
                    normalize_path(&format!("{guest_cwd}/{entrypoint_specifier}"))
                };
                let host_entrypoint = if entrypoint_specifier.starts_with("./")
                    || entrypoint_specifier.starts_with("../")
                {
                    normalize_host_path(&host_cwd.join(entrypoint_specifier))
                } else {
                    host_runtime_path_for_guest_path_with_env(
                        vm,
                        &runtime_env,
                        &guest_entrypoint,
                        parent_host_cwd,
                    )
                    .unwrap_or_else(|| {
                        let candidate = PathBuf::from(&guest_entrypoint);
                        if candidate.is_absolute() {
                            candidate
                        } else {
                            host_cwd.join(&guest_entrypoint)
                        }
                    })
                };
                env.insert(String::from("AGENTOS_GUEST_ENTRYPOINT"), guest_entrypoint);
                (
                    host_entrypoint.to_string_lossy().into_owned(),
                    process_args.iter().skip(1).cloned().collect(),
                )
            } else {
                (
                    entrypoint_specifier.clone(),
                    process_args.iter().skip(1).cloned().collect(),
                )
            };
            let guest_entrypoint = env.get("AGENTOS_GUEST_ENTRYPOINT").cloned();
            prepare_guest_runtime_env(vm, &mut env, &guest_cwd, &host_cwd, guest_entrypoint)?;

            return Ok(ResolvedChildProcessExecution {
                command: command.clone(),
                process_args: std::iter::once(command)
                    .chain(process_args.iter().cloned())
                    .collect(),
                runtime: GuestRuntimeKind::JavaScript,
                entrypoint,
                execution_args,
                env,
                guest_cwd,
                host_cwd,
                wasm_permission_tier: None,
                tool_command: false,
            });
        }

        if command == PYTHON_COMMAND {
            return Err(SidecarError::InvalidState(String::from(
                "nested python child_process execution is not supported yet",
            )));
        }

        let guest_entrypoint = resolve_guest_command_entrypoint(
            vm,
            &guest_cwd,
            &command,
            env.get("PATH").map(String::as_str),
        )
        .ok_or_else(|| SidecarError::InvalidState(format!("command not found: {command}")))?;
        let host_entrypoint = resolve_vm_guest_path_to_host(vm, &guest_entrypoint);
        let wasm_permission_tier = vm.command_permissions.get(&command).copied().or_else(|| {
            Path::new(&guest_entrypoint)
                .file_name()
                .and_then(|name| name.to_str())
                .and_then(|name| vm.command_permissions.get(name).copied())
        });
        if let Some((javascript_guest_entrypoint, javascript_host_entrypoint)) =
            resolve_javascript_command_entrypoint(vm, &guest_entrypoint, &host_entrypoint)
        {
            prepare_guest_runtime_env(
                vm,
                &mut env,
                &guest_cwd,
                &host_cwd,
                Some(javascript_guest_entrypoint),
            )?;

            return Ok(ResolvedChildProcessExecution {
                command: command.clone(),
                process_args: std::iter::once(command)
                    .chain(process_args.iter().cloned())
                    .collect(),
                runtime: GuestRuntimeKind::JavaScript,
                entrypoint: javascript_host_entrypoint.to_string_lossy().into_owned(),
                execution_args: process_args,
                env,
                guest_cwd,
                host_cwd,
                wasm_permission_tier: None,
                tool_command: false,
            });
        }
        prepare_guest_runtime_env(
            vm,
            &mut env,
            &guest_cwd,
            &host_cwd,
            Some(guest_entrypoint.clone()),
        )?;

        Ok(ResolvedChildProcessExecution {
            command: command.clone(),
            process_args: std::iter::once(command)
                .chain(process_args.iter().cloned())
                .collect(),
            runtime: GuestRuntimeKind::WebAssembly,
            entrypoint: host_entrypoint.to_string_lossy().into_owned(),
            execution_args: process_args,
            env,
            guest_cwd,
            host_cwd,
            wasm_permission_tier,
            tool_command: false,
        })
    }

    pub(crate) fn spawn_javascript_child_process(
        &mut self,
        vm_id: &str,
        process_id: &str,
        request: JavascriptChildProcessSpawnRequest,
    ) -> Result<Value, SidecarError> {
        let resolved = {
            let vm = self.vms.get(vm_id).ok_or_else(|| missing_vm_error(vm_id))?;
            let parent = vm
                .active_processes
                .get(process_id)
                .ok_or_else(|| missing_process_error(vm_id, process_id))?;
            self.resolve_javascript_child_process_execution(
                vm,
                &parent.env,
                &parent.guest_cwd,
                &parent.host_cwd,
                &request,
            )?
        };
        let (parent_kernel_pid, child_process_id) = {
            let vm = self
                .vms
                .get_mut(vm_id)
                .ok_or_else(|| missing_vm_error(vm_id))?;
            let process = vm
                .active_processes
                .get_mut(process_id)
                .ok_or_else(|| missing_process_error(vm_id, process_id))?;
            (process.kernel_pid, process.allocate_child_process_id())
        };
        let sidecar_requests = self.sidecar_requests.clone();
        let vm = self
            .vms
            .get_mut(vm_id)
            .ok_or_else(|| missing_vm_error(vm_id))?;
        let (kernel_pid, kernel_handle, execution, kernel_stdin_writer_fd) = if resolved
            .tool_command
        {
            let tool_resolution = resolve_tool_command(
                vm,
                &resolved.command,
                &resolved.execution_args,
                Some(&resolved.guest_cwd),
            )?
            .ok_or_else(|| {
                SidecarError::InvalidState(format!(
                    "tool command no longer resolves: {}",
                    resolved.command
                ))
            })?;
            let kernel_handle = vm
                .kernel
                .create_virtual_process(
                    EXECUTION_DRIVER_NAME,
                    TOOL_DRIVER_NAME,
                    &resolved.command,
                    resolved.process_args.clone(),
                    VirtualProcessOptions {
                        parent_pid: Some(parent_kernel_pid),
                        env: resolved.env.clone(),
                        cwd: Some(resolved.guest_cwd.clone()),
                    },
                )
                .map_err(kernel_error)?;
            let kernel_pid = kernel_handle.pid();
            let tool_execution = ToolExecution::default();
            let cancelled = tool_execution.cancelled.clone();
            let pending_events = tool_execution.pending_events.clone();
            let events_overflowed = tool_execution.events_overflowed.clone();
            spawn_tool_process_events(ToolProcessEventRequest {
                sidecar_requests: sidecar_requests.clone(),
                connection_id: vm.connection_id.clone(),
                session_id: vm.session_id.clone(),
                vm_id: vm_id.to_owned(),
                tool_resolution,
                cancelled,
                pending_events,
                events_overflowed,
            });
            (
                kernel_pid,
                kernel_handle,
                ActiveExecution::Tool(tool_execution),
                None,
            )
        } else {
            let kernel_command = match resolved.runtime {
                GuestRuntimeKind::JavaScript => JAVASCRIPT_COMMAND,
                GuestRuntimeKind::WebAssembly => WASM_COMMAND,
                GuestRuntimeKind::Python => {
                    unreachable!("python child_process execution is rejected")
                }
            };
            let kernel_handle = vm
                .kernel
                .spawn_process(
                    kernel_command,
                    resolved.process_args.clone(),
                    SpawnOptions {
                        requester_driver: Some(String::from(EXECUTION_DRIVER_NAME)),
                        parent_pid: Some(parent_kernel_pid),
                        env: resolved.env.clone(),
                        cwd: Some(resolved.guest_cwd.clone()),
                    },
                )
                .map_err(kernel_error)?;
            let kernel_pid = kernel_handle.pid();
            if request.options.detached {
                vm.kernel
                    .setsid(EXECUTION_DRIVER_NAME, kernel_pid)
                    .map_err(kernel_error)?;
            }
            let mut execution_env = resolved.env.clone();
            execution_env.insert(
                String::from(EXECUTION_SANDBOX_ROOT_ENV),
                normalize_host_path(&vm.cwd).to_string_lossy().into_owned(),
            );

            let execution = match resolved.runtime {
                GuestRuntimeKind::JavaScript => {
                    execution_env.extend(sanitize_javascript_child_process_internal_bootstrap_env(
                        &request.options.internal_bootstrap_env,
                    ));
                    execution_env.insert(
                        String::from("SECURE_EXEC_KEEP_STDIN_OPEN"),
                        String::from("1"),
                    );
                    let context =
                        self.javascript_engine
                            .create_context(CreateJavascriptContextRequest {
                                vm_id: vm_id.to_owned(),
                                bootstrap_module: None,
                                compile_cache_root: Some(
                                    self.cache_root.join("node-compile-cache"),
                                ),
                            });
                    let inline_code = load_javascript_entrypoint_source(
                        vm,
                        &resolved.host_cwd,
                        &resolved.entrypoint,
                        &execution_env,
                    );
                    prepare_javascript_shadow(vm, &resolved)?;

                    let module_reader = build_module_reader(vm, &resolved)
                        .map(|reader| Box::new(reader) as Box<dyn ModuleFsReader + Send>);
                    let execution = self
                        .javascript_engine
                        .start_execution_with_module_reader(
                            StartJavascriptExecutionRequest {
                                guest_runtime: guest_runtime_identity(
                                    vm,
                                    Some(u64::from(kernel_pid)),
                                    Some(u64::from(parent_kernel_pid)),
                                ),
                                vm_id: vm_id.to_owned(),
                                context_id: context.context_id,
                                argv: std::iter::once(resolved.entrypoint.clone())
                                    .chain(resolved.execution_args.clone())
                                    .collect(),
                                env: execution_env,
                                cwd: resolved.host_cwd.clone(),
                                limits: javascript_execution_limits(vm),
                                inline_code,
                            },
                            module_reader,
                        )
                        .map_err(javascript_error)?;
                    ActiveExecution::Javascript(execution)
                }
                GuestRuntimeKind::WebAssembly => {
                    execution_env.insert(String::from(WASM_STDIO_SYNC_RPC_ENV), String::from("1"));
                    let wasm_limits = wasm_execution_limits(vm);
                    let wasm_guest_runtime = guest_runtime_identity(
                        vm,
                        Some(u64::from(kernel_pid)),
                        Some(u64::from(parent_kernel_pid)),
                    );
                    let context = self.wasm_engine.create_context(CreateWasmContextRequest {
                        vm_id: vm_id.to_owned(),
                        module_path: Some(resolved.entrypoint.clone()),
                    });
                    let execution = self
                        .wasm_engine
                        .start_execution(StartWasmExecutionRequest {
                            vm_id: vm_id.to_owned(),
                            context_id: context.context_id,
                            argv: resolved.process_args.clone(),
                            env: execution_env,
                            cwd: resolved.host_cwd.clone(),
                            permission_tier: execution_wasm_permission_tier(
                                resolved
                                    .wasm_permission_tier
                                    .unwrap_or(WasmPermissionTier::Full),
                            ),
                            limits: wasm_limits,
                            guest_runtime: wasm_guest_runtime,
                        })
                        .map_err(wasm_error)?;
                    ActiveExecution::Wasm(Box::new(execution))
                }
                GuestRuntimeKind::Python => {
                    unreachable!("python child_process execution is rejected")
                }
            };
            let kernel_stdin_writer_fd = match javascript_child_process_stdin_mode(&request) {
                "pipe" => Some(install_kernel_stdin_pipe(&mut vm.kernel, kernel_pid)?),
                "ignore" => {
                    vm.kernel
                        .fd_close(EXECUTION_DRIVER_NAME, kernel_pid, 0)
                        .map_err(kernel_error)?;
                    None
                }
                "inherit" => None,
                _ => Some(install_kernel_stdin_pipe(&mut vm.kernel, kernel_pid)?),
            };
            (kernel_pid, kernel_handle, execution, kernel_stdin_writer_fd)
        };

        let process = vm
            .active_processes
            .get_mut(process_id)
            .ok_or_else(|| missing_process_error(vm_id, process_id))?;
        process.child_processes.insert(
            child_process_id.clone(),
            ActiveProcess::new(kernel_pid, kernel_handle, resolved.runtime, execution)
                .with_detached(request.options.detached)
                .with_guest_cwd(resolved.guest_cwd.clone())
                .with_env(resolved.env.clone())
                .with_host_cwd(resolved.host_cwd.clone()),
        );
        if let Some(kernel_stdin_writer_fd) = kernel_stdin_writer_fd {
            process
                .child_processes
                .get_mut(&child_process_id)
                .ok_or_else(|| {
                    SidecarError::InvalidState(format!(
                        "child process {child_process_id} disappeared during spawn"
                    ))
                })?
                .kernel_stdin_writer_fd = Some(kernel_stdin_writer_fd);
        }
        Ok(json!({
            "childId": child_process_id,
            "pid": kernel_pid,
            "command": resolved.command,
            "args": resolved.process_args,
        }))
    }

    pub(crate) fn spawn_javascript_child_process_sync(
        &mut self,
        vm_id: &str,
        process_id: &str,
        request: JavascriptChildProcessSpawnRequest,
        max_buffer: Option<usize>,
    ) -> Result<Value, SidecarError> {
        let sync_input = javascript_child_process_sync_input_bytes(request.options.input.as_ref())?;
        let timeout_deadline = request
            .options
            .timeout
            .map(|timeout_ms| Instant::now() + Duration::from_millis(timeout_ms));
        let timeout_signal = request
            .options
            .kill_signal
            .clone()
            .unwrap_or_else(|| String::from("SIGTERM"));
        let spawned = self.spawn_javascript_child_process(vm_id, process_id, request)?;
        let child_process_id = spawned
            .get("childId")
            .and_then(Value::as_str)
            .ok_or_else(|| {
                SidecarError::InvalidState(String::from(
                    "child_process.spawn_sync response is missing childId",
                ))
            })?
            .to_owned();

        if let Some(input) = sync_input.as_deref() {
            self.write_javascript_child_process_stdin(vm_id, process_id, &child_process_id, input)?;
        }
        self.close_javascript_child_process_stdin(vm_id, process_id, &child_process_id)?;

        let max_buffer = max_buffer.unwrap_or(1024 * 1024);
        let mut stdout = Vec::new();
        let mut stderr = Vec::new();
        let mut max_buffer_exceeded = false;
        let mut kill_sent = false;
        let mut timed_out = false;

        let exit_code = loop {
            let wait_ms = if let Some(deadline) = timeout_deadline {
                let now = Instant::now();
                if now >= deadline {
                    if !kill_sent {
                        timed_out = true;
                        self.kill_javascript_child_process(
                            vm_id,
                            process_id,
                            &child_process_id,
                            &timeout_signal,
                        )?;
                        kill_sent = true;
                    }
                    0
                } else {
                    u64::try_from(deadline.saturating_duration_since(now).as_millis().min(50))
                        .unwrap_or(50)
                }
            } else {
                50
            };
            let event =
                self.poll_javascript_child_process(vm_id, process_id, &child_process_id, wait_ms)?;
            if event.is_null() {
                continue;
            }

            match event.get("type").and_then(Value::as_str) {
                Some("stdout") => {
                    let chunk = javascript_sync_rpc_bytes_arg(
                        &[event.get("data").cloned().unwrap_or(Value::Null)],
                        0,
                        "child_process.spawn_sync stdout",
                    )?;
                    stdout.extend_from_slice(&chunk);
                    if stdout.len() > max_buffer && !kill_sent {
                        max_buffer_exceeded = true;
                        self.kill_javascript_child_process(
                            vm_id,
                            process_id,
                            &child_process_id,
                            "SIGTERM",
                        )?;
                        kill_sent = true;
                    }
                }
                Some("stderr") => {
                    let chunk = javascript_sync_rpc_bytes_arg(
                        &[event.get("data").cloned().unwrap_or(Value::Null)],
                        0,
                        "child_process.spawn_sync stderr",
                    )?;
                    stderr.extend_from_slice(&chunk);
                    if stderr.len() > max_buffer && !kill_sent {
                        max_buffer_exceeded = true;
                        self.kill_javascript_child_process(
                            vm_id,
                            process_id,
                            &child_process_id,
                            "SIGTERM",
                        )?;
                        kill_sent = true;
                    }
                }
                Some("exit") => {
                    break event
                        .get("exitCode")
                        .and_then(Value::as_i64)
                        .map(|value| value as i32)
                        .unwrap_or(1);
                }
                _ => {}
            }
        };

        Ok(json!({
            "stdout": String::from_utf8_lossy(&stdout),
            "stderr": String::from_utf8_lossy(&stderr),
            "code": exit_code,
            "signal": if timed_out { Value::String(timeout_signal) } else { Value::Null },
            "timedOut": timed_out,
            "maxBufferExceeded": max_buffer_exceeded,
        }))
    }

    fn spawn_descendant_javascript_child_process(
        &mut self,
        vm_id: &str,
        process_id: &str,
        current_process_path: &[&str],
        request: JavascriptChildProcessSpawnRequest,
    ) -> Result<Value, SidecarError> {
        let current_process_label =
            Self::child_process_path_label(process_id, current_process_path);
        let (resolved, parent_kernel_pid) = {
            let vm = self.vms.get(vm_id).ok_or_else(|| missing_vm_error(vm_id))?;
            let root = vm
                .active_processes
                .get(process_id)
                .ok_or_else(|| missing_process_error(vm_id, process_id))?;
            let parent =
                Self::active_process_by_path(root, current_process_path).ok_or_else(|| {
                    SidecarError::InvalidState(format!(
                        "unknown child process path {current_process_label} during nested spawn"
                    ))
                })?;
            (
                self.resolve_javascript_child_process_execution(
                    vm,
                    &parent.env,
                    &parent.guest_cwd,
                    &parent.host_cwd,
                    &request,
                )?,
                parent.kernel_pid,
            )
        };

        let sidecar_requests = self.sidecar_requests.clone();
        let vm = self
            .vms
            .get_mut(vm_id)
            .ok_or_else(|| missing_vm_error(vm_id))?;
        let child_process_id = {
            let root = vm
                .active_processes
                .get_mut(process_id)
                .ok_or_else(|| missing_process_error(vm_id, process_id))?;
            let parent =
                Self::active_process_by_path_mut(root, current_process_path).ok_or_else(|| {
                    SidecarError::InvalidState(format!(
                        "unknown child process path {current_process_label} during nested spawn"
                    ))
                })?;
            parent.allocate_child_process_id()
        };
        let mut child_path = current_process_path.to_vec();
        child_path.push(child_process_id.as_str());
        let (kernel_pid, kernel_handle, execution, kernel_stdin_writer_fd) = if resolved
            .tool_command
        {
            let tool_resolution = resolve_tool_command(
                vm,
                &resolved.command,
                &resolved.execution_args,
                Some(&resolved.guest_cwd),
            )?
            .ok_or_else(|| {
                SidecarError::InvalidState(format!(
                    "tool command no longer resolves: {}",
                    resolved.command
                ))
            })?;
            let kernel_handle = vm
                .kernel
                .create_virtual_process(
                    EXECUTION_DRIVER_NAME,
                    TOOL_DRIVER_NAME,
                    &resolved.command,
                    resolved.process_args.clone(),
                    VirtualProcessOptions {
                        parent_pid: Some(parent_kernel_pid),
                        env: resolved.env.clone(),
                        cwd: Some(resolved.guest_cwd.clone()),
                    },
                )
                .map_err(kernel_error)?;
            let kernel_pid = kernel_handle.pid();
            let tool_execution = ToolExecution::default();
            let cancelled = tool_execution.cancelled.clone();
            let pending_events = tool_execution.pending_events.clone();
            let events_overflowed = tool_execution.events_overflowed.clone();
            spawn_tool_process_events(ToolProcessEventRequest {
                sidecar_requests: sidecar_requests.clone(),
                connection_id: vm.connection_id.clone(),
                session_id: vm.session_id.clone(),
                vm_id: vm_id.to_owned(),
                tool_resolution,
                cancelled,
                pending_events,
                events_overflowed,
            });
            (
                kernel_pid,
                kernel_handle,
                ActiveExecution::Tool(tool_execution),
                None,
            )
        } else {
            let kernel_command = match resolved.runtime {
                GuestRuntimeKind::JavaScript => JAVASCRIPT_COMMAND,
                GuestRuntimeKind::WebAssembly => WASM_COMMAND,
                GuestRuntimeKind::Python => {
                    unreachable!("python child_process execution is rejected")
                }
            };
            let kernel_handle = vm
                .kernel
                .spawn_process(
                    kernel_command,
                    resolved.process_args.clone(),
                    SpawnOptions {
                        requester_driver: Some(String::from(EXECUTION_DRIVER_NAME)),
                        parent_pid: Some(parent_kernel_pid),
                        env: resolved.env.clone(),
                        cwd: Some(resolved.guest_cwd.clone()),
                    },
                )
                .map_err(kernel_error)?;
            let kernel_pid = kernel_handle.pid();
            if request.options.detached {
                vm.kernel
                    .setsid(EXECUTION_DRIVER_NAME, kernel_pid)
                    .map_err(kernel_error)?;
            }
            let mut execution_env = resolved.env.clone();
            execution_env.insert(
                String::from(EXECUTION_SANDBOX_ROOT_ENV),
                normalize_host_path(&vm.cwd).to_string_lossy().into_owned(),
            );
            let execution = match resolved.runtime {
                GuestRuntimeKind::JavaScript => {
                    execution_env.extend(sanitize_javascript_child_process_internal_bootstrap_env(
                        &request.options.internal_bootstrap_env,
                    ));
                    execution_env.insert(
                        String::from("SECURE_EXEC_KEEP_STDIN_OPEN"),
                        String::from("1"),
                    );
                    let context =
                        self.javascript_engine
                            .create_context(CreateJavascriptContextRequest {
                                vm_id: vm_id.to_owned(),
                                bootstrap_module: None,
                                compile_cache_root: Some(
                                    self.cache_root.join("node-compile-cache"),
                                ),
                            });
                    let inline_code = load_javascript_entrypoint_source(
                        vm,
                        &resolved.host_cwd,
                        &resolved.entrypoint,
                        &execution_env,
                    );
                    prepare_javascript_shadow(vm, &resolved)?;

                    let module_reader = build_module_reader(vm, &resolved)
                        .map(|reader| Box::new(reader) as Box<dyn ModuleFsReader + Send>);
                    let execution = self
                        .javascript_engine
                        .start_execution_with_module_reader(
                            StartJavascriptExecutionRequest {
                                guest_runtime: guest_runtime_identity(
                                    vm,
                                    Some(u64::from(kernel_pid)),
                                    Some(u64::from(parent_kernel_pid)),
                                ),
                                vm_id: vm_id.to_owned(),
                                context_id: context.context_id,
                                argv: std::iter::once(resolved.entrypoint.clone())
                                    .chain(resolved.execution_args.clone())
                                    .collect(),
                                env: execution_env,
                                cwd: resolved.host_cwd.clone(),
                                limits: javascript_execution_limits(vm),
                                inline_code,
                            },
                            module_reader,
                        )
                        .map_err(javascript_error)?;
                    ActiveExecution::Javascript(execution)
                }
                GuestRuntimeKind::WebAssembly => {
                    execution_env.insert(String::from(WASM_STDIO_SYNC_RPC_ENV), String::from("1"));
                    let wasm_limits = wasm_execution_limits(vm);
                    let wasm_guest_runtime = guest_runtime_identity(
                        vm,
                        Some(u64::from(kernel_pid)),
                        Some(u64::from(parent_kernel_pid)),
                    );
                    let context = self.wasm_engine.create_context(CreateWasmContextRequest {
                        vm_id: vm_id.to_owned(),
                        module_path: Some(resolved.entrypoint.clone()),
                    });
                    let execution = self
                        .wasm_engine
                        .start_execution(StartWasmExecutionRequest {
                            vm_id: vm_id.to_owned(),
                            context_id: context.context_id,
                            argv: resolved.process_args.clone(),
                            env: execution_env,
                            cwd: resolved.host_cwd.clone(),
                            permission_tier: execution_wasm_permission_tier(
                                resolved
                                    .wasm_permission_tier
                                    .unwrap_or(WasmPermissionTier::Full),
                            ),
                            limits: wasm_limits,
                            guest_runtime: wasm_guest_runtime,
                        })
                        .map_err(wasm_error)?;
                    ActiveExecution::Wasm(Box::new(execution))
                }
                GuestRuntimeKind::Python => {
                    unreachable!("python child_process execution is rejected")
                }
            };
            let kernel_stdin_writer_fd = match javascript_child_process_stdin_mode(&request) {
                "pipe" => Some(install_kernel_stdin_pipe(&mut vm.kernel, kernel_pid)?),
                "ignore" => {
                    vm.kernel
                        .fd_close(EXECUTION_DRIVER_NAME, kernel_pid, 0)
                        .map_err(kernel_error)?;
                    None
                }
                "inherit" => None,
                _ => Some(install_kernel_stdin_pipe(&mut vm.kernel, kernel_pid)?),
            };
            (kernel_pid, kernel_handle, execution, kernel_stdin_writer_fd)
        };

        let root = vm
            .active_processes
            .get_mut(process_id)
            .ok_or_else(|| missing_process_error(vm_id, process_id))?;
        let parent =
            Self::active_process_by_path_mut(root, current_process_path).ok_or_else(|| {
                SidecarError::InvalidState(format!(
                    "unknown child process path {current_process_label} during nested spawn"
                ))
            })?;
        parent.child_processes.insert(
            child_process_id.clone(),
            ActiveProcess::new(kernel_pid, kernel_handle, resolved.runtime, execution)
                .with_detached(request.options.detached)
                .with_guest_cwd(resolved.guest_cwd.clone())
                .with_env(resolved.env.clone())
                .with_host_cwd(resolved.host_cwd.clone()),
        );
        if let Some(kernel_stdin_writer_fd) = kernel_stdin_writer_fd {
            parent
                .child_processes
                .get_mut(&child_process_id)
                .ok_or_else(|| {
                    SidecarError::InvalidState(format!(
                        "child process {child_process_id} disappeared during nested spawn"
                    ))
                })?
                .kernel_stdin_writer_fd = Some(kernel_stdin_writer_fd);
        }
        Ok(json!({
            "childId": child_process_id,
            "pid": kernel_pid,
            "command": resolved.command,
            "args": resolved.process_args,
        }))
    }

    fn spawn_descendant_javascript_child_process_sync(
        &mut self,
        vm_id: &str,
        process_id: &str,
        current_process_path: &[&str],
        request: JavascriptChildProcessSpawnRequest,
        max_buffer: Option<usize>,
    ) -> Result<Value, SidecarError> {
        let sync_input = javascript_child_process_sync_input_bytes(request.options.input.as_ref())?;
        let timeout_deadline = request
            .options
            .timeout
            .map(|timeout_ms| Instant::now() + Duration::from_millis(timeout_ms));
        let timeout_signal = request
            .options
            .kill_signal
            .clone()
            .unwrap_or_else(|| String::from("SIGTERM"));
        let spawned = self.spawn_descendant_javascript_child_process(
            vm_id,
            process_id,
            current_process_path,
            request,
        )?;
        let child_process_id = spawned
            .get("childId")
            .and_then(Value::as_str)
            .ok_or_else(|| {
                SidecarError::InvalidState(String::from(
                    "child_process.spawn_sync response is missing childId",
                ))
            })?
            .to_owned();

        if let Some(input) = sync_input.as_deref() {
            self.write_descendant_javascript_child_process_stdin(
                vm_id,
                process_id,
                current_process_path,
                &child_process_id,
                input,
            )?;
        }
        self.close_descendant_javascript_child_process_stdin(
            vm_id,
            process_id,
            current_process_path,
            &child_process_id,
        )?;

        let max_buffer = max_buffer.unwrap_or(1024 * 1024);
        let mut stdout = Vec::new();
        let mut stderr = Vec::new();
        let mut max_buffer_exceeded = false;
        let mut kill_sent = false;
        let mut timed_out = false;

        let exit_code = loop {
            let wait_ms = if let Some(deadline) = timeout_deadline {
                let now = Instant::now();
                if now >= deadline {
                    if !kill_sent {
                        timed_out = true;
                        self.kill_descendant_javascript_child_process(
                            vm_id,
                            process_id,
                            current_process_path,
                            &child_process_id,
                            &timeout_signal,
                        )?;
                        kill_sent = true;
                    }
                    0
                } else {
                    u64::try_from(deadline.saturating_duration_since(now).as_millis().min(50))
                        .unwrap_or(50)
                }
            } else {
                50
            };
            let event = self.poll_descendant_javascript_child_process(
                vm_id,
                process_id,
                current_process_path,
                &child_process_id,
                wait_ms,
            )?;
            if event.is_null() {
                continue;
            }

            match event.get("type").and_then(Value::as_str) {
                Some("stdout") => {
                    let chunk = javascript_sync_rpc_bytes_arg(
                        &[event.get("data").cloned().unwrap_or(Value::Null)],
                        0,
                        "child_process.spawn_sync stdout",
                    )?;
                    stdout.extend_from_slice(&chunk);
                    if stdout.len() > max_buffer && !kill_sent {
                        max_buffer_exceeded = true;
                        self.kill_descendant_javascript_child_process(
                            vm_id,
                            process_id,
                            current_process_path,
                            &child_process_id,
                            "SIGTERM",
                        )?;
                        kill_sent = true;
                    }
                }
                Some("stderr") => {
                    let chunk = javascript_sync_rpc_bytes_arg(
                        &[event.get("data").cloned().unwrap_or(Value::Null)],
                        0,
                        "child_process.spawn_sync stderr",
                    )?;
                    stderr.extend_from_slice(&chunk);
                    if stderr.len() > max_buffer && !kill_sent {
                        max_buffer_exceeded = true;
                        self.kill_descendant_javascript_child_process(
                            vm_id,
                            process_id,
                            current_process_path,
                            &child_process_id,
                            "SIGTERM",
                        )?;
                        kill_sent = true;
                    }
                }
                Some("exit") => {
                    break event
                        .get("exitCode")
                        .and_then(Value::as_i64)
                        .map(|value| value as i32)
                        .unwrap_or(1);
                }
                _ => {}
            }
        };

        Ok(json!({
            "stdout": String::from_utf8_lossy(&stdout),
            "stderr": String::from_utf8_lossy(&stderr),
            "code": exit_code,
            "signal": if timed_out { Value::String(timeout_signal) } else { Value::Null },
            "timedOut": timed_out,
            "maxBufferExceeded": max_buffer_exceeded,
        }))
    }

    fn handle_descendant_javascript_child_process_rpc(
        &mut self,
        vm_id: &str,
        process_id: &str,
        current_process_path: &[&str],
        request: &JavascriptSyncRpcRequest,
    ) -> Result<Value, SidecarError> {
        match request.method.as_str() {
            "child_process.spawn" => {
                let Some(vm) = self.vms.get(vm_id) else {
                    return Ok(Value::Null);
                };
                let (payload, _) = parse_javascript_child_process_spawn_request(vm, &request.args)?;
                self.spawn_descendant_javascript_child_process(
                    vm_id,
                    process_id,
                    current_process_path,
                    payload,
                )
            }
            "child_process.spawn_sync" => {
                let Some(vm) = self.vms.get(vm_id) else {
                    return Ok(Value::Null);
                };
                let (payload, max_buffer) =
                    parse_javascript_child_process_spawn_request(vm, &request.args)?;
                self.spawn_descendant_javascript_child_process_sync(
                    vm_id,
                    process_id,
                    current_process_path,
                    payload,
                    max_buffer,
                )
            }
            "child_process.poll" => {
                let child_process_id =
                    javascript_sync_rpc_arg_str(&request.args, 0, "child_process.poll child id")?;
                let wait_ms = javascript_sync_rpc_arg_u64_optional(
                    &request.args,
                    1,
                    "child_process.poll wait ms",
                )?
                .unwrap_or_default();
                self.poll_descendant_javascript_child_process(
                    vm_id,
                    process_id,
                    current_process_path,
                    child_process_id,
                    wait_ms,
                )
            }
            "child_process.write_stdin" => {
                let child_process_id = javascript_sync_rpc_arg_str(
                    &request.args,
                    0,
                    "child_process.write_stdin child id",
                )?;
                let chunk = javascript_sync_rpc_bytes_arg(
                    &request.args,
                    1,
                    "child_process.write_stdin chunk",
                )?;
                self.write_descendant_javascript_child_process_stdin(
                    vm_id,
                    process_id,
                    current_process_path,
                    child_process_id,
                    &chunk,
                )?;
                Ok(Value::Null)
            }
            "child_process.close_stdin" => {
                let child_process_id = javascript_sync_rpc_arg_str(
                    &request.args,
                    0,
                    "child_process.close_stdin child id",
                )?;
                self.close_descendant_javascript_child_process_stdin(
                    vm_id,
                    process_id,
                    current_process_path,
                    child_process_id,
                )?;
                Ok(Value::Null)
            }
            "child_process.kill" => {
                let child_process_id =
                    javascript_sync_rpc_arg_str(&request.args, 0, "child_process.kill child id")?;
                let signal =
                    javascript_sync_rpc_arg_str(&request.args, 1, "child_process.kill signal")?;
                self.kill_descendant_javascript_child_process(
                    vm_id,
                    process_id,
                    current_process_path,
                    child_process_id,
                    signal,
                )?;
                Ok(Value::Null)
            }
            _ => Err(SidecarError::InvalidState(format!(
                "unsupported nested child process RPC method {}",
                request.method
            ))),
        }
    }

    fn poll_descendant_javascript_child_process(
        &mut self,
        vm_id: &str,
        process_id: &str,
        current_process_path: &[&str],
        child_process_id: &str,
        wait_ms: u64,
    ) -> Result<Value, SidecarError> {
        let mut child_path = current_process_path.to_vec();
        child_path.push(child_process_id);
        let child_gone_error = || javascript_child_process_gone_error(process_id, &child_path);
        let deadline = Instant::now() + Duration::from_millis(wait_ms);
        let mut polled_once = false;

        loop {
            self.drain_queued_descendant_javascript_child_process_events(
                vm_id,
                process_id,
                &child_path,
            )?;
            enum ChildPollResult {
                Event(Box<Option<ActiveExecutionEvent>>),
                RecoverRuntimeExit,
                Timeout,
            }
            let wait = if wait_ms == 0 {
                Duration::ZERO
            } else {
                deadline.saturating_duration_since(Instant::now())
            };
            let poll_result = {
                let Some(vm) = self.vms.get_mut(vm_id) else {
                    return Ok(Value::Null);
                };
                let Some(parent) =
                    Self::descendant_parent_process_mut(vm, process_id, current_process_path)
                else {
                    return Err(child_gone_error());
                };
                let Some(child) = parent.child_processes.get_mut(child_process_id) else {
                    return Err(child_gone_error());
                };
                if let Some(event) = child.pending_execution_events.pop_front() {
                    ChildPollResult::Event(Box::new(Some(event)))
                } else if polled_once && wait.is_zero() {
                    ChildPollResult::Timeout
                } else {
                    polled_once = true;
                    match child.execution.poll_event_blocking(wait) {
                        Ok(Some(event)) => ChildPollResult::Event(Box::new(Some(event))),
                        Ok(None) => ChildPollResult::RecoverRuntimeExit,
                        Err(SidecarError::Execution(message))
                            if (child.runtime == GuestRuntimeKind::JavaScript
                                && closed_javascript_event_channel(&message))
                                || (child.runtime == GuestRuntimeKind::Python
                                    && closed_python_event_channel(&message))
                                || (child.runtime == GuestRuntimeKind::WebAssembly
                                    && closed_wasm_event_channel(&message)) =>
                        {
                            ChildPollResult::RecoverRuntimeExit
                        }
                        Err(error) => return Err(error),
                    }
                }
            };
            let event = match poll_result {
                ChildPollResult::Event(event) => *event,
                ChildPollResult::Timeout => return Ok(Value::Null),
                ChildPollResult::RecoverRuntimeExit => self
                    .recover_descendant_runtime_child_process_event(
                        vm_id,
                        process_id,
                        current_process_path,
                        child_process_id,
                        wait.as_millis().try_into().unwrap_or(u64::MAX),
                    )?,
            };

            let Some(event) = event else {
                return Ok(Value::Null);
            };

            match event {
                ActiveExecutionEvent::Stdout(chunk) => {
                    return Ok(json!({
                        "type": "stdout",
                        "data": javascript_sync_rpc_bytes_value(&chunk),
                    }));
                }
                ActiveExecutionEvent::Stderr(chunk) => {
                    return Ok(json!({
                        "type": "stderr",
                        "data": javascript_sync_rpc_bytes_value(&chunk),
                    }));
                }
                ActiveExecutionEvent::Exited(exit_code) => {
                    let had_trailing_events = {
                        let Some(vm) = self.vms.get_mut(vm_id) else {
                            return Ok(Value::Null);
                        };
                        let Some(parent) = Self::descendant_parent_process_mut(
                            vm,
                            process_id,
                            current_process_path,
                        ) else {
                            return Ok(Value::Null);
                        };
                        let Some(child) = parent.child_processes.get_mut(child_process_id) else {
                            return Ok(Value::Null);
                        };
                        let deadline = Instant::now() + Duration::from_millis(150);
                        loop {
                            let wait = deadline.saturating_duration_since(Instant::now());
                            let next = poll_child_execution_after_exit(child, wait)?;
                            let Some(next) = next else {
                                break;
                            };
                            if matches!(next, ActiveExecutionEvent::Exited(_)) {
                                continue;
                            }
                            child.queue_pending_execution_event(next)?;
                            if Instant::now() >= deadline {
                                break;
                            }
                        }
                        if !child.pending_execution_events.is_empty() {
                            child.queue_pending_execution_event(ActiveExecutionEvent::Exited(
                                exit_code,
                            ))?;
                            true
                        } else {
                            false
                        }
                    };
                    if had_trailing_events {
                        continue;
                    }

                    let parent_signal_key =
                        Self::child_process_signal_key(process_id, current_process_path);
                    let Some(vm) = self.vms.get_mut(vm_id) else {
                        return Ok(Value::Null);
                    };
                    let signal_name = {
                        let Some(parent) = Self::descendant_parent_process_mut(
                            vm,
                            process_id,
                            current_process_path,
                        ) else {
                            return Ok(Value::Null);
                        };
                        let Some(child) = parent.child_processes.get_mut(child_process_id) else {
                            return Ok(Value::Null);
                        };
                        child.pending_self_signal_exit.take().and_then(|signal| {
                            if exit_code == 128 + signal {
                                canonical_signal_name(signal).map(str::to_owned)
                            } else {
                                None
                            }
                        })
                    };
                    let (parent_runtime_pid, parent_v8_signal_session, should_signal_parent) = {
                        let Some(parent) =
                            Self::descendant_parent_process(vm, process_id, current_process_path)
                        else {
                            return Ok(Value::Null);
                        };
                        (
                            parent.execution.child_pid(),
                            parent.execution.javascript_v8_session_handle().filter(|_| {
                                matches!(
                                    &parent.execution,
                                    ActiveExecution::Javascript(execution)
                                        if execution.uses_shared_v8_runtime()
                                )
                            }),
                            vm.signal_states
                                .get(parent_signal_key)
                                .and_then(|handlers| handlers.get(&(libc::SIGCHLD as u32)))
                                .is_some_and(|registration| {
                                    registration.action != SignalDispositionAction::Default
                                }),
                        )
                    };
                    let Some(parent) =
                        Self::descendant_parent_process_mut(vm, process_id, current_process_path)
                    else {
                        return Ok(Value::Null);
                    };
                    let Some(mut child) = parent.child_processes.remove(child_process_id) else {
                        return Ok(Value::Null);
                    };
                    let child_process_label =
                        Self::child_process_path_label(process_id, &child_path);
                    let detached_children =
                        Self::adopt_detached_child_processes(&child_process_label, &mut child);
                    sync_process_host_writes_to_kernel(vm, &child)?;
                    terminate_child_process_tree(&mut vm.kernel, &mut child);
                    child.kernel_handle.finish(exit_code);
                    let _ = vm.kernel.wait_and_reap(child.kernel_pid);
                    vm.signal_states.remove(child_process_id);
                    for (detached_process_id, detached_child) in detached_children {
                        vm.detached_child_processes
                            .insert(detached_process_id.clone());
                        vm.active_processes
                            .insert(detached_process_id, detached_child);
                    }
                    if should_signal_parent {
                        if let Some(session) = parent_v8_signal_session {
                            dispatch_v8_session_signal_async(session, libc::SIGCHLD);
                        } else {
                            signal_runtime_process(parent_runtime_pid, libc::SIGCHLD)?;
                        }
                    }
                    let mut payload = Map::new();
                    payload.insert(String::from("type"), Value::String(String::from("exit")));
                    payload.insert(String::from("exitCode"), Value::from(exit_code));
                    if let Some(signal_name) = signal_name {
                        payload.insert(String::from("signal"), Value::String(signal_name));
                    }
                    return Ok(Value::Object(payload));
                }
                ActiveExecutionEvent::JavascriptSyncRpcRequest(request) => {
                    let mut current_child_path = current_process_path.to_vec();
                    current_child_path.push(child_process_id);
                    let response = if request.method == "process.signal_state" {
                        let (signal, registration) =
                            parse_process_signal_state_request(&request.args)?;
                        let Some(vm) = self.vms.get_mut(vm_id) else {
                            return Ok(Value::Null);
                        };
                        let signal_key =
                            Self::child_process_signal_key(process_id, &current_child_path)
                                .to_owned();
                        apply_process_signal_state_update(
                            &mut vm.signal_states,
                            &signal_key,
                            signal,
                            registration,
                        );
                        Ok(Value::Null)
                    } else if request.method == "process.kill" {
                        self.handle_descendant_process_kill_rpc(
                            vm_id,
                            process_id,
                            current_process_path,
                            child_process_id,
                            &request,
                        )
                    } else if request.method.starts_with("child_process.") {
                        self.handle_descendant_javascript_child_process_rpc(
                            vm_id,
                            process_id,
                            &current_child_path,
                            &request,
                        )
                    } else {
                        let Some(vm) = self.vms.get_mut(vm_id) else {
                            return Ok(Value::Null);
                        };
                        let resource_limits = vm.kernel.resource_limits().clone();
                        let network_counts = vm_network_resource_counts(vm);
                        let socket_paths = build_javascript_socket_path_context(vm)?;
                        let Some(root) = vm.active_processes.get_mut(process_id) else {
                            return Ok(Value::Null);
                        };
                        let Some(parent) =
                            Self::active_process_by_path_mut(root, current_process_path)
                        else {
                            return Ok(Value::Null);
                        };
                        let Some(child) = parent.child_processes.get_mut(child_process_id) else {
                            return Ok(Value::Null);
                        };
                        service_javascript_sync_rpc(JavascriptSyncRpcServiceRequest {
                            bridge: &self.bridge,
                            vm_id,
                            dns: &vm.dns,
                            socket_paths: &socket_paths,
                            kernel: &mut vm.kernel,
                            process: child,
                            sync_request: &request,
                            resource_limits: &resource_limits,
                            network_counts,
                        })
                    };

                    let Some(vm) = self.vms.get_mut(vm_id) else {
                        return Ok(Value::Null);
                    };
                    let Some(parent) =
                        Self::descendant_parent_process_mut(vm, process_id, current_process_path)
                    else {
                        return Ok(Value::Null);
                    };
                    let Some(child) = parent.child_processes.get_mut(child_process_id) else {
                        return Ok(Value::Null);
                    };
                    let parent_signal_event = response.as_ref().ok().and_then(|result| {
                        let target_path_label =
                            Self::child_process_path_label(process_id, current_process_path);
                        if request.method != "process.kill"
                            || result.get("action").and_then(Value::as_str) != Some("user")
                            || result.get("targetProcessPath").and_then(Value::as_str)
                                != Some(target_path_label.as_str())
                        {
                            return None;
                        }
                        Some(json!({
                            "type": "signal",
                            "signal": result.get("signal").and_then(Value::as_str).unwrap_or_default(),
                            "number": result.get("number").and_then(Value::as_i64).unwrap_or_default(),
                        }))
                    });
                    match response {
                        Ok(result) => child
                            .execution
                            .respond_javascript_sync_rpc_success(request.id, result)
                            .or_else(ignore_stale_javascript_sync_rpc_response)?,
                        Err(error) => child
                            .execution
                            .respond_javascript_sync_rpc_error(
                                request.id,
                                javascript_sync_rpc_error_code(&error),
                                error.to_string(),
                            )
                            .or_else(ignore_stale_javascript_sync_rpc_response)?,
                    }
                    if let Some(event) = parent_signal_event {
                        return Ok(event);
                    }
                }
                ActiveExecutionEvent::PythonVfsRpcRequest(_) => {
                    return Err(SidecarError::InvalidState(String::from(
                        "nested Python child_process execution is not supported yet",
                    )));
                }
                ActiveExecutionEvent::SignalState {
                    signal,
                    registration,
                } => {
                    let Some(vm) = self.vms.get_mut(vm_id) else {
                        return Ok(Value::Null);
                    };
                    let signal_key =
                        Self::child_process_signal_key(process_id, &child_path).to_owned();
                    apply_process_signal_state_update(
                        &mut vm.signal_states,
                        &signal_key,
                        signal,
                        registration.clone(),
                    );
                    return Ok(json!({
                        "type": "signal_state",
                        "signal": signal,
                        "registration": registration,
                    }));
                }
            }
        }
    }

    fn recover_descendant_runtime_child_process_event(
        &mut self,
        vm_id: &str,
        process_id: &str,
        current_process_path: &[&str],
        child_process_id: &str,
        wait_ms: u64,
    ) -> Result<Option<ActiveExecutionEvent>, SidecarError> {
        let (
            parent_kernel_pid,
            child_kernel_pid,
            child_runtime_pid,
            child_runtime,
            child_shared_runtime,
        ) = {
            let mut child_path = current_process_path.to_vec();
            child_path.push(child_process_id);
            let Some(vm) = self.vms.get_mut(vm_id) else {
                return Ok(None);
            };
            let Some(parent) =
                Self::descendant_parent_process_mut(vm, process_id, current_process_path)
            else {
                return Err(javascript_child_process_gone_error(process_id, &child_path));
            };
            let Some(child) = parent.child_processes.get_mut(child_process_id) else {
                return Err(javascript_child_process_gone_error(process_id, &child_path));
            };
            (
                parent.kernel_pid,
                child.kernel_pid,
                child.execution.child_pid(),
                child.runtime.clone(),
                child.execution.uses_shared_v8_runtime(),
            )
        };
        if child_runtime != GuestRuntimeKind::JavaScript
            && child_runtime != GuestRuntimeKind::Python
            && child_runtime != GuestRuntimeKind::WebAssembly
        {
            return Ok(None);
        }
        let wait_deadline = Instant::now() + Duration::from_millis(wait_ms.min(25));
        loop {
            let Some(vm) = self.vms.get_mut(vm_id) else {
                return Ok(None);
            };
            if let Some(process_info) = vm.kernel.list_processes().get(&child_kernel_pid) {
                if process_info.status == ProcessStatus::Exited {
                    return Ok(Some(ActiveExecutionEvent::Exited(
                        process_info.exit_code.unwrap_or(0),
                    )));
                }
            }
            if let Some(wait_result) = vm
                .kernel
                .waitpid_with_options(
                    EXECUTION_DRIVER_NAME,
                    parent_kernel_pid,
                    child_kernel_pid as i32,
                    WaitPidFlags::WNOHANG,
                )
                .map_err(kernel_error)?
            {
                return Ok(Some(ActiveExecutionEvent::Exited(wait_result.status)));
            }

            if !child_shared_runtime && child_runtime_pid != 0 {
                if let Some(status) = runtime_child_exit_status(child_runtime_pid)? {
                    return Ok(Some(ActiveExecutionEvent::Exited(status)));
                }
                if !runtime_child_is_alive(child_runtime_pid)? {
                    return Ok(Some(ActiveExecutionEvent::Exited(0)));
                }
            }
            if Instant::now() >= wait_deadline {
                return Ok(None);
            }
            std::thread::sleep(Duration::from_millis(5));
        }
    }

    fn write_descendant_javascript_child_process_stdin(
        &mut self,
        vm_id: &str,
        process_id: &str,
        current_process_path: &[&str],
        child_process_id: &str,
        chunk: &[u8],
    ) -> Result<(), SidecarError> {
        let mut child_path = current_process_path.to_vec();
        child_path.push(child_process_id);
        let Some(vm) = self.vms.get_mut(vm_id) else {
            return Err(javascript_child_process_gone_error(process_id, &child_path));
        };
        let Some(root) = vm.active_processes.get_mut(process_id) else {
            return Err(javascript_child_process_gone_error(process_id, &child_path));
        };
        let Some(parent) = Self::active_process_by_path_mut(root, current_process_path) else {
            return Err(javascript_child_process_gone_error(process_id, &child_path));
        };
        let Some(child) = parent.child_processes.get_mut(child_process_id) else {
            return Err(javascript_child_process_gone_error(process_id, &child_path));
        };
        if let Err(error) = child.execution.write_stdin(chunk) {
            if is_broken_pipe_error(&error) {
                return Ok(());
            }
            return Err(error);
        }
        write_kernel_process_stdin(&mut vm.kernel, child, chunk)
    }

    fn close_descendant_javascript_child_process_stdin(
        &mut self,
        vm_id: &str,
        process_id: &str,
        current_process_path: &[&str],
        child_process_id: &str,
    ) -> Result<(), SidecarError> {
        let mut child_path = current_process_path.to_vec();
        child_path.push(child_process_id);
        let Some(vm) = self.vms.get_mut(vm_id) else {
            return Err(javascript_child_process_gone_error(process_id, &child_path));
        };
        let Some(root) = vm.active_processes.get_mut(process_id) else {
            return Err(javascript_child_process_gone_error(process_id, &child_path));
        };
        let Some(parent) = Self::active_process_by_path_mut(root, current_process_path) else {
            return Err(javascript_child_process_gone_error(process_id, &child_path));
        };
        let Some(child) = parent.child_processes.get_mut(child_process_id) else {
            return Err(javascript_child_process_gone_error(process_id, &child_path));
        };
        child.execution.close_stdin()?;
        close_kernel_process_stdin(&mut vm.kernel, child)
    }

    fn kill_descendant_javascript_child_process(
        &mut self,
        vm_id: &str,
        process_id: &str,
        current_process_path: &[&str],
        child_process_id: &str,
        signal: &str,
    ) -> Result<(), SidecarError> {
        let signal_name = signal.to_owned();
        let signal = parse_signal(signal)?;
        let Some(vm) = self.vms.get_mut(vm_id) else {
            return Ok(());
        };
        let Some(root) = vm.active_processes.get_mut(process_id) else {
            return Ok(());
        };
        let Some(parent) = Self::active_process_by_path_mut(root, current_process_path) else {
            return Ok(());
        };
        let source_pid = parent.kernel_pid;
        let Some(child) = parent.child_processes.get_mut(child_process_id) else {
            return Ok(());
        };
        terminate_tracked_child_process_for_signal(&mut vm.kernel, child, signal)?;
        let child_process_label = if current_process_path.is_empty() {
            child_process_id.to_owned()
        } else {
            format!("{}/{}", current_process_path.join("/"), child_process_id)
        };
        emit_security_audit_event(
            &self.bridge,
            vm_id,
            "security.process.kill",
            audit_fields([
                (String::from("source"), String::from("guest_child_process")),
                (String::from("source_pid"), source_pid.to_string()),
                (String::from("target_pid"), child.kernel_pid.to_string()),
                (String::from("process_id"), process_id.to_owned()),
                (String::from("child_process_id"), child_process_label),
                (String::from("signal"), signal_name),
            ]),
        );
        Ok(())
    }

    fn handle_descendant_process_kill_rpc(
        &mut self,
        vm_id: &str,
        process_id: &str,
        current_process_path: &[&str],
        child_process_id: &str,
        request: &JavascriptSyncRpcRequest,
    ) -> Result<Value, SidecarError> {
        let target_pid = javascript_sync_rpc_arg_i32(&request.args, 0, "process.kill target pid")?;
        let signal_name = javascript_sync_rpc_arg_str(&request.args, 1, "process.kill signal")?;
        let signal = parse_signal(signal_name)?;

        let mut source_path = current_process_path.to_vec();
        source_path.push(child_process_id);

        if signal != 0 && target_pid < 0 {
            let pgid = target_pid.unsigned_abs();
            let caller_kernel_pid = {
                let Some(vm) = self.vms.get(vm_id) else {
                    return Err(SidecarError::InvalidState(String::from(
                        "ESRCH: unknown VM during process.kill",
                    )));
                };
                let Some(root) = vm.active_processes.get(process_id) else {
                    return Err(SidecarError::InvalidState(format!(
                        "ESRCH: unknown process {process_id} during process.kill",
                    )));
                };
                let Some(source) = Self::active_process_by_path(root, &source_path) else {
                    return Err(SidecarError::InvalidState(format!(
                        "ESRCH: unknown child process {child_process_id} during process.kill",
                    )));
                };
                source.kernel_pid
            };
            let caller_is_member =
                self.signal_vm_process_group(vm_id, caller_kernel_pid, pgid, signal_name)?;
            if !caller_is_member {
                return Ok(Value::Null);
            }
            let Some(vm) = self.vms.get_mut(vm_id) else {
                return Ok(Value::Null);
            };
            let Some(root) = vm.active_processes.get_mut(process_id) else {
                return Ok(Value::Null);
            };
            let Some(source) = Self::active_process_by_path_mut(root, &source_path) else {
                return Ok(Value::Null);
            };
            source.pending_self_signal_exit = None;
            if !matches!(
                canonical_signal_name(signal),
                Some("SIGWINCH" | "SIGCHLD" | "SIGCONT" | "SIGURG")
            ) {
                source.pending_self_signal_exit = Some(signal);
            }
            return Ok(json!({
                "self": true,
                "action": "default",
            }));
        }

        let Some(vm) = self.vms.get_mut(vm_id) else {
            return Err(SidecarError::InvalidState(String::from(
                "ESRCH: unknown VM during process.kill",
            )));
        };

        if signal == 0 {
            vm.kernel
                .signal_process(EXECUTION_DRIVER_NAME, target_pid, signal)
                .map_err(kernel_error)?;
            return Ok(Value::Null);
        }

        let target_kernel_pid = u32::try_from(target_pid).map_err(|_| {
            SidecarError::InvalidState(format!("EINVAL: invalid process pid {target_pid}"))
        })?;
        let (source_pid, located_target_path) = {
            let Some(root) = vm.active_processes.get(process_id) else {
                return Err(SidecarError::InvalidState(format!(
                    "ESRCH: unknown process {process_id} during process.kill",
                )));
            };
            let Some(source) = Self::active_process_by_path(root, &source_path) else {
                return Err(SidecarError::InvalidState(format!(
                    "ESRCH: unknown child process {child_process_id} during process.kill",
                )));
            };
            vm.kernel
                .signal_process(EXECUTION_DRIVER_NAME, target_pid, 0)
                .map_err(kernel_error)?;
            (
                source.kernel_pid,
                Self::active_process_path_by_kernel_pid(root, target_kernel_pid),
            )
        };
        let Some(target_path) = located_target_path else {
            // The target is alive but not part of this root's process tree.
            // Resolve it VM-wide so cross-tree pids and untracked kernel
            // processes still receive the signal.
            self.signal_vm_kernel_pid(vm_id, target_kernel_pid, signal_name)?;
            return Ok(Value::Null);
        };
        let Some(vm) = self.vms.get_mut(vm_id) else {
            return Err(SidecarError::InvalidState(String::from(
                "ESRCH: unknown VM during process.kill",
            )));
        };

        if source_pid == target_kernel_pid {
            let Some(root) = vm.active_processes.get_mut(process_id) else {
                return Ok(Value::Null);
            };
            let Some(source) = Self::active_process_by_path_mut(root, &source_path) else {
                return Ok(Value::Null);
            };
            source.pending_self_signal_exit = None;
            if !matches!(
                canonical_signal_name(signal),
                Some("SIGWINCH" | "SIGCHLD" | "SIGCONT" | "SIGURG")
            ) {
                source.pending_self_signal_exit = Some(signal);
            }
            return Ok(json!({
                "self": true,
                "action": "default",
            }));
        }

        let signal_key = target_path.last().map(String::as_str).unwrap_or(process_id);
        let registration = vm
            .signal_states
            .get(signal_key)
            .and_then(|handlers| handlers.get(&(signal as u32)))
            .cloned();

        let action = match registration
            .as_ref()
            .map(|registration| &registration.action)
        {
            Some(SignalDispositionAction::Ignore) => "ignore",
            Some(SignalDispositionAction::User) => {
                let Some(root) = vm.active_processes.get_mut(process_id) else {
                    return Ok(Value::Null);
                };
                let Some(target) = Self::active_process_by_owned_path_mut(root, &target_path)
                else {
                    return Err(SidecarError::InvalidState(format!(
                        "ESRCH: unknown process pid {target_pid}"
                    )));
                };
                if let Some(session) = target.execution.javascript_v8_session_handle().filter(
                    |_| matches!(&target.execution, ActiveExecution::Javascript(execution) if execution.uses_shared_v8_runtime())
                        || matches!(&target.execution, ActiveExecution::Wasm(execution) if execution.uses_shared_v8_runtime()),
                ) {
                    dispatch_v8_session_signal_async(session, signal);
                } else if !dispatch_v8_process_signal(target, signal)? {
                    return Err(SidecarError::InvalidState(format!(
                        "unsupported guest signal delivery for pid {target_pid}"
                    )));
                }
                "user"
            }
            Some(SignalDispositionAction::Default) | None
                if matches!(
                    canonical_signal_name(signal),
                    Some("SIGWINCH" | "SIGCHLD" | "SIGURG")
                ) =>
            {
                "ignore"
            }
            Some(SignalDispositionAction::Default) | None => {
                let Some(root) = vm.active_processes.get_mut(process_id) else {
                    return Ok(Value::Null);
                };
                let Some(target) = Self::active_process_by_owned_path_mut(root, &target_path)
                else {
                    return Err(SidecarError::InvalidState(format!(
                        "ESRCH: unknown process pid {target_pid}"
                    )));
                };
                apply_active_process_default_signal(&mut vm.kernel, target, signal)?;
                "default"
            }
        };

        let target_path_label = Self::child_process_path_label(
            process_id,
            &target_path.iter().map(String::as_str).collect::<Vec<_>>(),
        );
        emit_security_audit_event(
            &self.bridge,
            vm_id,
            "security.process.kill",
            audit_fields([
                (String::from("source"), String::from("guest_process")),
                (String::from("source_pid"), source_pid.to_string()),
                (String::from("target_pid"), target_pid.to_string()),
                (String::from("process_id"), process_id.to_owned()),
                (
                    String::from("target_process_path"),
                    target_path_label.clone(),
                ),
                (String::from("signal"), signal_name.to_owned()),
            ]),
        );

        Ok(json!({
            "self": false,
            "action": action,
            "signal": signal_name,
            "number": signal,
            "targetProcessPath": target_path_label,
        }))
    }

    pub(crate) fn poll_javascript_child_process(
        &mut self,
        vm_id: &str,
        process_id: &str,
        child_process_id: &str,
        wait_ms: u64,
    ) -> Result<Value, SidecarError> {
        self.poll_descendant_javascript_child_process(
            vm_id,
            process_id,
            &[],
            child_process_id,
            wait_ms,
        )
    }

    pub(crate) fn write_javascript_child_process_stdin(
        &mut self,
        vm_id: &str,
        process_id: &str,
        child_process_id: &str,
        chunk: &[u8],
    ) -> Result<(), SidecarError> {
        let Some(vm) = self.vms.get_mut(vm_id) else {
            return Err(javascript_child_process_gone_error(
                process_id,
                &[child_process_id],
            ));
        };
        let Some(child) = vm
            .active_processes
            .get_mut(process_id)
            .ok_or_else(|| missing_process_error(vm_id, process_id))?
            .child_processes
            .get_mut(child_process_id)
        else {
            return Err(javascript_child_process_gone_error(
                process_id,
                &[child_process_id],
            ));
        };
        if let Err(error) = child.execution.write_stdin(chunk) {
            if is_broken_pipe_error(&error) {
                return Ok(());
            }
            return Err(error);
        }
        write_kernel_process_stdin(&mut vm.kernel, child, chunk)
    }

    pub(crate) fn close_javascript_child_process_stdin(
        &mut self,
        vm_id: &str,
        process_id: &str,
        child_process_id: &str,
    ) -> Result<(), SidecarError> {
        let Some(vm) = self.vms.get_mut(vm_id) else {
            return Err(javascript_child_process_gone_error(
                process_id,
                &[child_process_id],
            ));
        };
        let Some(child) = vm
            .active_processes
            .get_mut(process_id)
            .ok_or_else(|| missing_process_error(vm_id, process_id))?
            .child_processes
            .get_mut(child_process_id)
        else {
            return Err(javascript_child_process_gone_error(
                process_id,
                &[child_process_id],
            ));
        };
        child.execution.close_stdin()?;
        close_kernel_process_stdin(&mut vm.kernel, child)
    }

    pub(crate) fn kill_javascript_child_process(
        &mut self,
        vm_id: &str,
        process_id: &str,
        child_process_id: &str,
        signal: &str,
    ) -> Result<(), SidecarError> {
        let signal_name = signal.to_owned();
        let signal = parse_signal(signal)?;
        let Some(vm) = self.vms.get_mut(vm_id) else {
            return Ok(());
        };
        let process = vm
            .active_processes
            .get_mut(process_id)
            .ok_or_else(|| missing_process_error(vm_id, process_id))?;
        let source_pid = process.kernel_pid;
        let child = process
            .child_processes
            .get_mut(child_process_id)
            .ok_or_else(|| {
                SidecarError::InvalidState(format!(
                    "unknown child process {child_process_id} during kill"
                ))
            })?;
        terminate_tracked_child_process_for_signal(&mut vm.kernel, child, signal)?;
        emit_security_audit_event(
            &self.bridge,
            vm_id,
            "security.process.kill",
            audit_fields([
                (String::from("source"), String::from("guest_child_process")),
                (String::from("source_pid"), source_pid.to_string()),
                (String::from("target_pid"), child.kernel_pid.to_string()),
                (String::from("process_id"), process_id.to_owned()),
                (
                    String::from("child_process_id"),
                    child_process_id.to_owned(),
                ),
                (String::from("signal"), signal_name),
            ]),
        );
        Ok(())
    }

    /// Delivers a signal to one kernel pid inside a VM, resolving the target
    /// through the active-process tree first so tracked sidecar executions get
    /// the same termination handling as a direct `child_process.kill`.
    /// Untracked kernel processes (for example WASM subprocess trees) receive
    /// the signal through the kernel process table directly.
    pub(crate) fn signal_vm_kernel_pid(
        &mut self,
        vm_id: &str,
        target_kernel_pid: u32,
        signal_name: &str,
    ) -> Result<(), SidecarError> {
        let signal = parse_signal(signal_name)?;
        let located = {
            let Some(vm) = self.vms.get(vm_id) else {
                return Err(SidecarError::InvalidState(String::from(
                    "ESRCH: unknown VM during process.kill",
                )));
            };
            let alive = vm
                .kernel
                .list_processes()
                .get(&target_kernel_pid)
                .is_some_and(|info| info.status != ProcessStatus::Exited);
            if !alive {
                return Err(SidecarError::InvalidState(format!(
                    "ESRCH: no such process {target_kernel_pid}"
                )));
            }
            vm.active_processes.iter().find_map(|(process_id, root)| {
                Self::active_process_path_by_kernel_pid(root, target_kernel_pid)
                    .map(|path| (process_id.clone(), path))
            })
        };

        match located {
            Some((process_id, path)) if path.is_empty() => {
                self.kill_process_internal(vm_id, &process_id, signal_name)
            }
            Some((process_id, path)) => {
                let Some(vm) = self.vms.get_mut(vm_id) else {
                    return Ok(());
                };
                let Some(root) = vm.active_processes.get_mut(&process_id) else {
                    return Ok(());
                };
                let Some(target) = Self::active_process_by_owned_path_mut(root, &path) else {
                    return Err(SidecarError::InvalidState(format!(
                        "ESRCH: no such process {target_kernel_pid}"
                    )));
                };
                terminate_tracked_child_process_for_signal(&mut vm.kernel, target, signal)?;
                emit_security_audit_event(
                    &self.bridge,
                    vm_id,
                    "security.process.kill",
                    audit_fields([
                        (String::from("source"), String::from("guest_process")),
                        (String::from("target_pid"), target_kernel_pid.to_string()),
                        (String::from("process_id"), process_id),
                        (String::from("signal"), signal_name.to_owned()),
                    ]),
                );
                Ok(())
            }
            None => {
                let Some(vm) = self.vms.get_mut(vm_id) else {
                    return Ok(());
                };
                let target_pid = i32::try_from(target_kernel_pid).map_err(|_| {
                    SidecarError::InvalidState(format!(
                        "EINVAL: invalid process pid {target_kernel_pid}"
                    ))
                })?;
                vm.kernel
                    .signal_process(EXECUTION_DRIVER_NAME, target_pid, signal)
                    .map_err(kernel_error)?;
                emit_security_audit_event(
                    &self.bridge,
                    vm_id,
                    "security.process.kill",
                    audit_fields([
                        (String::from("source"), String::from("guest_process")),
                        (String::from("target_pid"), target_kernel_pid.to_string()),
                        (String::from("signal"), signal_name.to_owned()),
                    ]),
                );
                Ok(())
            }
        }
    }

    /// Delivers a signal to every live member of a VM process group, matching
    /// Linux `kill(-pgid, sig)` semantics. Returns whether the caller itself
    /// is a member of the group so entry points can apply self-signal
    /// delivery; the caller is intentionally skipped here.
    pub(crate) fn signal_vm_process_group(
        &mut self,
        vm_id: &str,
        caller_kernel_pid: u32,
        pgid: u32,
        signal_name: &str,
    ) -> Result<bool, SidecarError> {
        parse_signal(signal_name)?;
        let members = {
            let Some(vm) = self.vms.get(vm_id) else {
                return Err(SidecarError::InvalidState(String::from(
                    "ESRCH: unknown VM during process.kill",
                )));
            };
            vm.kernel
                .list_processes()
                .into_iter()
                .filter(|(_, info)| info.pgid == pgid && info.status != ProcessStatus::Exited)
                .map(|(pid, _)| pid)
                .collect::<Vec<_>>()
        };
        if members.is_empty() {
            return Err(SidecarError::InvalidState(format!(
                "ESRCH: no such process group {pgid}"
            )));
        }

        let mut caller_is_member = false;
        for member_pid in members {
            if member_pid == caller_kernel_pid {
                caller_is_member = true;
                continue;
            }
            match self.signal_vm_kernel_pid(vm_id, member_pid, signal_name) {
                Ok(()) => {}
                // Group members can exit while the group is being signaled. A
                // vanished member is not an error for the group kill overall.
                Err(error) if sidecar_error_is_esrch(&error) => {}
                Err(error) => return Err(error),
            }
        }
        Ok(caller_is_member)
    }
}

/// Applies a kill signal to a tracked child execution. Shared-runtime
/// executions for lethal signals are terminated directly with a synthetic
/// signal exit so child polls observe a prompt close; everything else routes
/// through the kernel process table.
fn terminate_tracked_child_process_for_signal(
    kernel: &mut SidecarKernel,
    child: &mut ActiveProcess,
    signal: i32,
) -> Result<(), SidecarError> {
    let should_terminate_shared_runtime = child.execution.uses_shared_v8_runtime()
        && signal != 0
        && !matches!(
            signal,
            libc::SIGHUP
                | libc::SIGINT
                | libc::SIGTERM
                | libc::SIGCHLD
                | libc::SIGWINCH
                | libc::SIGSTOP
                | libc::SIGCONT
        );
    if should_terminate_shared_runtime {
        child.execution.terminate()?;
        child.pending_self_signal_exit = Some(signal);
        child.queue_pending_execution_event(ActiveExecutionEvent::Exited(128 + signal))?;
    } else {
        kernel
            .kill_process(EXECUTION_DRIVER_NAME, child.kernel_pid, signal)
            .map_err(kernel_error)?;
    }
    Ok(())
}

fn sidecar_error_is_esrch(error: &SidecarError) -> bool {
    error.to_string().contains("ESRCH")
}

fn apply_active_process_default_signal(
    kernel: &mut SidecarKernel,
    process: &mut ActiveProcess,
    signal: i32,
) -> Result<(), SidecarError> {
    if matches!(signal, libc::SIGSTOP | libc::SIGCONT) {
        return kernel
            .kill_process(EXECUTION_DRIVER_NAME, process.kernel_pid, signal)
            .map_err(kernel_error);
    }

    if signal != 0 && matches!(process.execution, ActiveExecution::Python(_)) {
        close_kernel_process_stdin(kernel, process)?;
    }

    if process.execution.uses_shared_v8_runtime() {
        process.execution.terminate()?;
        if signal != 0 && matches!(process.execution, ActiveExecution::Wasm(_)) {
            process.queue_pending_execution_event(ActiveExecutionEvent::Exited(128 + signal))?;
        }
        return Ok(());
    }

    kernel
        .kill_process(EXECUTION_DRIVER_NAME, process.kernel_pid, signal)
        .map_err(kernel_error)
}

fn map_wasm_signal_registration(
    registration: secure_exec_execution::wasm::WasmSignalHandlerRegistration,
) -> SignalHandlerRegistration {
    SignalHandlerRegistration {
        action: match registration.action {
            secure_exec_execution::wasm::WasmSignalDispositionAction::Default => {
                crate::protocol::SignalDispositionAction::Default
            }
            secure_exec_execution::wasm::WasmSignalDispositionAction::Ignore => {
                crate::protocol::SignalDispositionAction::Ignore
            }
            secure_exec_execution::wasm::WasmSignalDispositionAction::User => {
                crate::protocol::SignalDispositionAction::User
            }
        },
        mask: registration.mask,
        flags: registration.flags,
    }
}

fn parse_process_signal_state_request(
    args: &[Value],
) -> Result<(u32, SignalHandlerRegistration), SidecarError> {
    let signal = javascript_sync_rpc_arg_u32(args, 0, "process.signal_state signal")?;
    let action = javascript_sync_rpc_arg_str(args, 1, "process.signal_state action")?;
    let mask_json = javascript_sync_rpc_arg_str(args, 2, "process.signal_state mask")?;
    let flags = javascript_sync_rpc_arg_u32(args, 3, "process.signal_state flags")?;
    let mask: Vec<u32> = serde_json::from_str(mask_json).map_err(|error| {
        SidecarError::InvalidState(format!(
            "process.signal_state mask must be valid JSON: {error}"
        ))
    })?;
    let action = match action.trim().to_ascii_lowercase().as_str() {
        "default" => SignalDispositionAction::Default,
        "ignore" => SignalDispositionAction::Ignore,
        "user" => SignalDispositionAction::User,
        other => {
            return Err(SidecarError::InvalidState(format!(
                "unsupported process.signal_state action {other}"
            )));
        }
    };

    Ok((
        signal,
        SignalHandlerRegistration {
            action,
            mask,
            flags,
        },
    ))
}

fn apply_process_signal_state_update(
    signal_states: &mut BTreeMap<String, BTreeMap<u32, SignalHandlerRegistration>>,
    process_id: &str,
    signal: u32,
    registration: SignalHandlerRegistration,
) {
    if registration.action == SignalDispositionAction::Default
        && registration.mask.is_empty()
        && registration.flags == 0
    {
        let remove_process_entry = signal_states
            .get_mut(process_id)
            .map(|handlers| {
                handlers.remove(&signal);
                handlers.is_empty()
            })
            .unwrap_or(false);
        if remove_process_entry {
            signal_states.remove(process_id);
        }
        return;
    }

    signal_states
        .entry(process_id.to_owned())
        .or_default()
        .insert(signal, registration);
}

fn map_node_signal_registration(
    registration: NodeSignalHandlerRegistration,
) -> SignalHandlerRegistration {
    SignalHandlerRegistration {
        action: match registration.action {
            NodeSignalDispositionAction::Default => SignalDispositionAction::Default,
            NodeSignalDispositionAction::Ignore => SignalDispositionAction::Ignore,
            NodeSignalDispositionAction::User => SignalDispositionAction::User,
        },
        mask: registration.mask,
        flags: registration.flags,
    }
}

fn javascript_child_process_sync_input_bytes(
    value: Option<&Value>,
) -> Result<Option<Vec<u8>>, SidecarError> {
    let Some(value) = value else {
        return Ok(None);
    };

    match value {
        Value::Null => Ok(None),
        Value::String(text) => Ok(Some(text.as_bytes().to_vec())),
        other => javascript_sync_rpc_bytes_arg(
            std::slice::from_ref(other),
            0,
            "child_process.spawn_sync input",
        )
        .map(Some),
    }
}

// bridge_permissions moved to crate::bridge

// reconcile_mounts, resolve_cwd moved to crate::vm

fn resolve_execute_request(
    vm: &VmState,
    payload: &ExecuteRequest,
) -> Result<ResolvedChildProcessExecution, SidecarError> {
    let payload_env: BTreeMap<String, String> = payload
        .env
        .iter()
        .map(|(k, v)| (k.clone(), v.clone()))
        .collect();
    if let Some(command) = payload.command.as_deref() {
        return resolve_command_execution(
            vm,
            command,
            &payload.args,
            &payload_env,
            payload.cwd.as_deref(),
            payload.wasm_permission_tier,
        );
    }

    let runtime = payload.runtime.clone().ok_or_else(|| {
        SidecarError::InvalidState(String::from("execute requires either command or runtime"))
    })?;
    let entrypoint = payload.entrypoint.clone().ok_or_else(|| {
        SidecarError::InvalidState(String::from(
            "execute requires either command or entrypoint",
        ))
    })?;
    let (guest_cwd, host_cwd, allow_host_path_overrides) =
        resolve_execution_cwds(vm, payload.cwd.as_deref());
    let mut env = vm.guest_env.clone();
    env.extend(payload_env.clone());

    let requested_host_entrypoint = resolve_host_entrypoint_within_vm_host_cwd(vm, &entrypoint);
    if requested_host_entrypoint.is_some() && !allow_host_path_overrides {
        let requested_cwd = payload.cwd.as_deref().unwrap_or(guest_cwd.as_str());
        return Err(SidecarError::InvalidState(format!(
            "execution cwd {requested_cwd} is outside sandbox root {}",
            vm.host_cwd.to_string_lossy()
        )));
    }
    let host_entrypoint_override = allow_host_path_overrides
        .then(|| resolve_host_entrypoint_within_vm_host_cwd(vm, &entrypoint))
        .flatten();

    let guest_entrypoint = host_entrypoint_override
        .as_ref()
        .map(|(guest_entrypoint, _)| guest_entrypoint.clone())
        .or_else(|| guest_entrypoint_for_specifier(&guest_cwd, &entrypoint));
    prepare_guest_runtime_env(vm, &mut env, &guest_cwd, &host_cwd, guest_entrypoint)?;

    Ok(ResolvedChildProcessExecution {
        command: match runtime {
            GuestRuntimeKind::JavaScript => String::from(JAVASCRIPT_COMMAND),
            GuestRuntimeKind::Python => String::from(PYTHON_COMMAND),
            GuestRuntimeKind::WebAssembly => String::from(WASM_COMMAND),
        },
        process_args: std::iter::once(entrypoint.clone())
            .chain(payload.args.iter().cloned())
            .collect(),
        runtime,
        entrypoint: host_entrypoint_override
            .map(|(_, host_entrypoint)| host_entrypoint)
            .unwrap_or(entrypoint),
        execution_args: payload.args.clone(),
        env,
        guest_cwd,
        host_cwd,
        wasm_permission_tier: payload.wasm_permission_tier,
        tool_command: false,
    })
}

fn resolve_command_execution(
    vm: &VmState,
    command: &str,
    args: &[String],
    extra_env: &BTreeMap<String, String>,
    cwd: Option<&str>,
    explicit_wasm_permission_tier: Option<WasmPermissionTier>,
) -> Result<ResolvedChildProcessExecution, SidecarError> {
    let (guest_cwd, host_cwd, allow_host_path_overrides) = resolve_execution_cwds(vm, cwd);
    let mut env = vm.guest_env.clone();
    env.extend(extra_env.clone());
    let args = apply_shell_cwd_prefix(command, args.to_vec(), &guest_cwd);

    if is_tool_command(vm, command) {
        let command = normalized_tool_command_name(command).unwrap_or_else(|| command.to_owned());
        return Ok(ResolvedChildProcessExecution {
            command: command.clone(),
            process_args: std::iter::once(command.clone())
                .chain(args.iter().cloned())
                .collect(),
            runtime: GuestRuntimeKind::JavaScript,
            entrypoint: command,
            execution_args: args,
            env,
            guest_cwd,
            host_cwd,
            wasm_permission_tier: None,
            tool_command: true,
        });
    }

    if is_node_runtime_command(command) {
        if let Some(cli) = resolve_host_node_cli_entrypoint(command) {
            env.insert(
                String::from("AGENTOS_NODE_EVAL"),
                build_host_node_cli_eval(&cli),
            );
            prepare_guest_runtime_env(vm, &mut env, &guest_cwd, &host_cwd, None)?;
            add_runtime_guest_path_mapping(&mut env, &cli.guest_root, &cli.package_root);
            add_runtime_host_access_path(
                &mut env,
                "AGENTOS_EXTRA_FS_READ_PATHS",
                &cli.package_root,
                true,
            );

            return Ok(ResolvedChildProcessExecution {
                command: String::from(JAVASCRIPT_COMMAND),
                process_args: std::iter::once(command.to_owned())
                    .chain(args.iter().cloned())
                    .collect(),
                runtime: GuestRuntimeKind::JavaScript,
                entrypoint: String::from("-e"),
                execution_args: std::iter::once(cli.guest_entrypoint.clone())
                    .chain(args.iter().cloned())
                    .collect(),
                env,
                guest_cwd,
                host_cwd,
                wasm_permission_tier: None,
                tool_command: false,
            });
        }

        if args.is_empty() {
            env.insert(String::from("AGENTOS_NODE_EVAL"), String::new());
            prepare_guest_runtime_env(vm, &mut env, &guest_cwd, &host_cwd, None)?;

            return Ok(ResolvedChildProcessExecution {
                command: String::from(JAVASCRIPT_COMMAND),
                process_args: vec![command.to_owned()],
                runtime: GuestRuntimeKind::JavaScript,
                entrypoint: String::from("-e"),
                execution_args: Vec::new(),
                env,
                guest_cwd,
                host_cwd,
                wasm_permission_tier: None,
                tool_command: false,
            });
        }

        if let Some((entrypoint, execution_args)) =
            resolve_special_node_cli_invocation(&args, &mut env)
        {
            prepare_guest_runtime_env(vm, &mut env, &guest_cwd, &host_cwd, None)?;

            return Ok(ResolvedChildProcessExecution {
                command: String::from(JAVASCRIPT_COMMAND),
                process_args: std::iter::once(command.to_owned())
                    .chain(args.iter().cloned())
                    .collect(),
                runtime: GuestRuntimeKind::JavaScript,
                entrypoint,
                execution_args,
                env,
                guest_cwd,
                host_cwd,
                wasm_permission_tier: None,
                tool_command: false,
            });
        }

        let Some(entrypoint_specifier) = args.first() else {
            return Err(SidecarError::InvalidState(format!(
                "{command} execution requires an entrypoint"
            )));
        };

        let (entrypoint, execution_args, guest_entrypoint) = {
            let requested_host_entrypoint =
                resolve_host_entrypoint_within_vm_host_cwd(vm, entrypoint_specifier);
            if requested_host_entrypoint.is_some() && !allow_host_path_overrides {
                let requested_cwd = cwd.unwrap_or(guest_cwd.as_str());
                return Err(SidecarError::InvalidState(format!(
                    "execution cwd {requested_cwd} is outside sandbox root {}",
                    vm.host_cwd.to_string_lossy()
                )));
            }
            let host_entrypoint_override = allow_host_path_overrides
                .then(|| resolve_host_entrypoint_within_vm_host_cwd(vm, entrypoint_specifier))
                .flatten();
            let guest_entrypoint = host_entrypoint_override
                .as_ref()
                .map(|(guest_entrypoint, _)| guest_entrypoint.clone())
                .or_else(|| guest_entrypoint_for_specifier(&guest_cwd, entrypoint_specifier));
            let entrypoint = host_entrypoint_override.map_or_else(
                || {
                    guest_entrypoint.as_ref().map_or_else(
                        || entrypoint_specifier.clone(),
                        |guest_entrypoint| {
                            resolve_vm_guest_path_to_host(vm, guest_entrypoint)
                                .to_string_lossy()
                                .into_owned()
                        },
                    )
                },
                |(_, host_entrypoint)| host_entrypoint,
            );
            (
                entrypoint,
                args.iter().skip(1).cloned().collect(),
                guest_entrypoint,
            )
        };

        prepare_guest_runtime_env(vm, &mut env, &guest_cwd, &host_cwd, guest_entrypoint)?;

        return Ok(ResolvedChildProcessExecution {
            command: String::from(JAVASCRIPT_COMMAND),
            process_args: std::iter::once(command.to_owned())
                .chain(args.iter().cloned())
                .collect(),
            runtime: GuestRuntimeKind::JavaScript,
            entrypoint,
            execution_args,
            env,
            guest_cwd,
            host_cwd,
            wasm_permission_tier: None,
            tool_command: false,
        });
    }

    if command.ends_with(".js") || command.ends_with(".mjs") || command.ends_with(".cjs") {
        let requested_host_entrypoint = resolve_host_entrypoint_within_vm_host_cwd(vm, command);
        if requested_host_entrypoint.is_some() && !allow_host_path_overrides {
            let requested_cwd = cwd.unwrap_or(guest_cwd.as_str());
            return Err(SidecarError::InvalidState(format!(
                "execution cwd {requested_cwd} is outside sandbox root {}",
                vm.host_cwd.to_string_lossy()
            )));
        }
        let host_entrypoint_override = allow_host_path_overrides
            .then(|| resolve_host_entrypoint_within_vm_host_cwd(vm, command))
            .flatten();
        let guest_entrypoint = host_entrypoint_override
            .as_ref()
            .map(|(guest_entrypoint, _)| guest_entrypoint.clone())
            .or_else(|| guest_entrypoint_for_specifier(&guest_cwd, command));
        let entrypoint = host_entrypoint_override.map_or_else(
            || {
                guest_entrypoint.as_ref().map_or_else(
                    || command.to_owned(),
                    |guest_entrypoint| {
                        resolve_vm_guest_path_to_host(vm, guest_entrypoint)
                            .to_string_lossy()
                            .into_owned()
                    },
                )
            },
            |(_, host_entrypoint)| host_entrypoint,
        );
        prepare_guest_runtime_env(vm, &mut env, &guest_cwd, &host_cwd, guest_entrypoint)?;

        return Ok(ResolvedChildProcessExecution {
            command: String::from(JAVASCRIPT_COMMAND),
            process_args: std::iter::once(command.to_owned())
                .chain(args.iter().cloned())
                .collect(),
            runtime: GuestRuntimeKind::JavaScript,
            entrypoint,
            execution_args: args.to_vec(),
            env,
            guest_cwd,
            host_cwd,
            wasm_permission_tier: None,
            tool_command: false,
        });
    }

    let guest_entrypoint = resolve_guest_command_entrypoint(
        vm,
        &guest_cwd,
        command,
        env.get("PATH").map(String::as_str),
    )
    .ok_or_else(|| {
        SidecarError::InvalidState(format!(
            "command not found on native sidecar path: {command}"
        ))
    })?;
    let wasm_permission_tier = explicit_wasm_permission_tier
        .or_else(|| vm.command_permissions.get(command).copied())
        .or_else(|| {
            Path::new(&guest_entrypoint)
                .file_name()
                .and_then(|name| name.to_str())
                .and_then(|name| vm.command_permissions.get(name).copied())
        });

    let host_entrypoint = resolve_vm_guest_path_to_host(vm, &guest_entrypoint);
    if let Some((javascript_guest_entrypoint, javascript_host_entrypoint)) =
        resolve_javascript_command_entrypoint(vm, &guest_entrypoint, &host_entrypoint)
    {
        prepare_guest_runtime_env(
            vm,
            &mut env,
            &guest_cwd,
            &host_cwd,
            Some(javascript_guest_entrypoint),
        )?;

        return Ok(ResolvedChildProcessExecution {
            command: command.to_owned(),
            process_args: std::iter::once(command.to_owned())
                .chain(args.iter().cloned())
                .collect(),
            runtime: GuestRuntimeKind::JavaScript,
            entrypoint: javascript_host_entrypoint.to_string_lossy().into_owned(),
            execution_args: args.to_vec(),
            env,
            guest_cwd,
            host_cwd,
            wasm_permission_tier: None,
            tool_command: false,
        });
    }
    prepare_guest_runtime_env(
        vm,
        &mut env,
        &guest_cwd,
        &host_cwd,
        Some(guest_entrypoint.clone()),
    )?;

    Ok(ResolvedChildProcessExecution {
        command: command.to_owned(),
        process_args: std::iter::once(command.to_owned())
            .chain(args.iter().cloned())
            .collect(),
        runtime: GuestRuntimeKind::WebAssembly,
        entrypoint: host_entrypoint.to_string_lossy().into_owned(),
        execution_args: args.to_vec(),
        env,
        guest_cwd,
        host_cwd,
        wasm_permission_tier,
        tool_command: false,
    })
}

const MAX_JAVASCRIPT_COMMAND_REDIRECT_DEPTH: usize = 4;

fn resolve_javascript_command_entrypoint(
    vm: &VmState,
    guest_entrypoint: &str,
    host_entrypoint: &Path,
) -> Option<(String, PathBuf)> {
    resolve_javascript_command_entrypoint_inner(
        vm,
        guest_entrypoint,
        host_entrypoint,
        MAX_JAVASCRIPT_COMMAND_REDIRECT_DEPTH,
    )
}

fn resolve_javascript_command_entrypoint_inner(
    vm: &VmState,
    guest_entrypoint: &str,
    host_entrypoint: &Path,
    redirects_remaining: usize,
) -> Option<(String, PathBuf)> {
    if redirects_remaining > 0 {
        let symlink_target = fs::symlink_metadata(host_entrypoint)
            .ok()
            .filter(|metadata| metadata.file_type().is_symlink())
            .and_then(|_| fs::read_link(host_entrypoint).ok());
        if let Some(symlink_target) = symlink_target {
            let guest_parent = Path::new(guest_entrypoint)
                .parent()
                .and_then(|path| path.to_str())
                .unwrap_or("/");
            let symlink_guest_entrypoint = if symlink_target.is_absolute() {
                normalize_path(&symlink_target.to_string_lossy())
            } else {
                normalize_path(&format!(
                    "{guest_parent}/{}",
                    symlink_target.to_string_lossy().replace('\\', "/")
                ))
            };
            let symlink_host_entrypoint =
                resolve_vm_guest_path_to_host(vm, &symlink_guest_entrypoint);
            return resolve_javascript_command_entrypoint_inner(
                vm,
                &symlink_guest_entrypoint,
                &symlink_host_entrypoint,
                redirects_remaining - 1,
            );
        }
    }

    let script = load_executable_script_preview(host_entrypoint)?;
    let interpreter = parse_script_interpreter_name(&script);

    if interpreter.is_none() && is_probable_javascript_entrypoint(host_entrypoint, &script) {
        return Some((guest_entrypoint.to_owned(), host_entrypoint.to_path_buf()));
    }

    let interpreter = interpreter?;
    if interpreter == "node" {
        return Some((guest_entrypoint.to_owned(), host_entrypoint.to_path_buf()));
    }

    if redirects_remaining == 0 || !matches!(interpreter.as_str(), "sh" | "bash" | "dash") {
        return None;
    }

    let shim_target = parse_node_shell_shim_target(&script)?;
    let guest_parent = Path::new(guest_entrypoint)
        .parent()
        .and_then(|path| path.to_str())
        .unwrap_or("/");
    let shim_guest_entrypoint = normalize_path(&format!("{guest_parent}/{shim_target}"));
    let shim_host_entrypoint = resolve_vm_guest_path_to_host(vm, &shim_guest_entrypoint);
    resolve_javascript_command_entrypoint_inner(
        vm,
        &shim_guest_entrypoint,
        &shim_host_entrypoint,
        redirects_remaining - 1,
    )
}

fn load_executable_script_preview(path: &Path) -> Option<String> {
    let bytes = fs::read(path).ok()?;
    let preview_len = bytes.len().min(16 * 1024);
    Some(String::from_utf8_lossy(&bytes[..preview_len]).into_owned())
}

fn parse_script_interpreter_name(script: &str) -> Option<String> {
    let shebang = script.lines().next()?.strip_prefix("#!")?.trim();
    let mut tokens = shebang.split_whitespace();
    let command = tokens.next()?;
    let command_name = Path::new(command).file_name()?.to_str()?;
    if command_name == "env" {
        for token in tokens {
            if token.starts_with('-') {
                continue;
            }
            return Path::new(token)
                .file_name()
                .and_then(|name| name.to_str())
                .map(ToOwned::to_owned);
        }
        return None;
    }

    Some(command_name.to_owned())
}

fn parse_node_shell_shim_target(script: &str) -> Option<String> {
    for line in script.lines() {
        let trimmed = line.trim();
        if !trimmed.starts_with("exec ") {
            continue;
        }

        let mut remaining = trimmed;
        while let Some(start) = remaining.find("\"$basedir/") {
            let after_prefix = &remaining[start + "\"$basedir/".len()..];
            let end = after_prefix.find('"')?;
            let candidate = &after_prefix[..end];
            remaining = &after_prefix[end + 1..];

            if candidate.is_empty() || candidate == "node" || candidate.ends_with("/node") {
                continue;
            }

            return Some(candidate.to_owned());
        }
    }

    None
}

fn is_probable_javascript_entrypoint(path: &Path, script: &str) -> bool {
    let extension = path
        .extension()
        .and_then(|value| value.to_str())
        .unwrap_or_default();
    if matches!(extension, "js" | "cjs" | "mjs") {
        return true;
    }

    if !path
        .components()
        .any(|component| component.as_os_str() == "node_modules")
    {
        return false;
    }

    let preview = script.trim_start_matches('\u{feff}').trim_start();
    !preview.is_empty()
        && !preview.starts_with("#!")
        && (preview.starts_with("\"use strict\"")
            || preview.starts_with("'use strict'")
            || preview.starts_with("import ")
            || preview.starts_with("export ")
            || preview.starts_with("const ")
            || preview.starts_with("let ")
            || preview.starts_with("var ")
            || preview.starts_with("Object.defineProperty(exports")
            || preview.starts_with("module.exports")
            || preview.starts_with("require("))
}

fn resolve_guest_execution_cwd(vm: &VmState, value: Option<&str>) -> String {
    value
        .map(normalize_path)
        .unwrap_or_else(|| vm.guest_cwd.clone())
}

fn resolve_execution_cwds(vm: &VmState, value: Option<&str>) -> (String, PathBuf, bool) {
    if let Some(raw_cwd) = value {
        let normalized_vm_host_cwd = normalize_host_path(&vm.host_cwd);
        let requested_host_cwd = normalize_host_path(Path::new(raw_cwd));
        if path_is_within_root(&requested_host_cwd, &normalized_vm_host_cwd) {
            let relative = requested_host_cwd
                .strip_prefix(&normalized_vm_host_cwd)
                .unwrap_or_else(|_| Path::new(""));
            let relative = relative.to_string_lossy().replace('\\', "/");
            let guest_cwd = if relative.is_empty() {
                String::from("/")
            } else {
                normalize_path(&format!("/{relative}"))
            };
            return (guest_cwd, requested_host_cwd, true);
        }
    }

    let guest_cwd = resolve_guest_execution_cwd(vm, value);
    let host_cwd = if value.is_none() {
        vm.host_cwd.clone()
    } else {
        resolve_vm_guest_path_to_host(vm, &guest_cwd)
    };
    (guest_cwd, host_cwd, value.is_none())
}

fn resolve_vm_guest_path_to_host(vm: &VmState, guest_path: &str) -> PathBuf {
    host_mount_path_for_guest_path(vm, guest_path)
        .unwrap_or_else(|| shadow_path_for_guest(vm, guest_path))
}

fn shadow_path_for_guest(vm: &VmState, guest_path: &str) -> PathBuf {
    let normalized = normalize_path(guest_path);
    let relative = normalized.trim_start_matches('/');
    if relative.is_empty() {
        return vm.cwd.clone();
    }
    vm.cwd.join(relative)
}

fn apply_shell_cwd_prefix(command: &str, mut args: Vec<String>, guest_cwd: &str) -> Vec<String> {
    if guest_cwd == "/" || !is_shell_command(command) {
        return args;
    }

    let Some(flag) = args.first() else {
        return args;
    };
    if !matches!(flag.as_str(), "-c" | "-lc") || args.len() < 2 {
        return args;
    }

    let command_text = args[1].clone();
    let quoted_cwd = shell_single_quote(guest_cwd);
    args[1] = format!("cd {quoted_cwd} && {command_text}");
    args
}

fn is_shell_command(command: &str) -> bool {
    Path::new(command)
        .file_name()
        .and_then(|name| name.to_str())
        .unwrap_or(command)
        .trim_end_matches(".exe")
        .eq("sh")
        || Path::new(command)
            .file_name()
            .and_then(|name| name.to_str())
            .unwrap_or(command)
            .trim_end_matches(".exe")
            .eq("bash")
}

fn shell_single_quote(value: &str) -> String {
    if value.is_empty() {
        return String::from("''");
    }
    format!("'{}'", value.replace('\'', "'\"'\"'"))
}

pub(crate) fn sync_active_process_host_writes_to_kernel(
    vm: &mut VmState,
) -> Result<(), SidecarError> {
    if vm.root_filesystem_mode != RootFilesystemMode::ReadOnly {
        let shadow_root = vm.cwd.clone();
        sync_host_directory_tree_to_kernel(vm, &shadow_root, "/")?;
    }

    let normalized_vm_root = normalize_host_path(&vm.cwd);
    let extra_roots = collect_active_process_host_sync_roots(vm, &normalized_vm_root);
    for (host_cwd, guest_cwd) in extra_roots {
        sync_host_directory_tree_to_kernel(vm, &host_cwd, &guest_cwd)?;
    }

    Ok(())
}

fn collect_active_process_host_sync_roots(
    vm: &VmState,
    normalized_vm_root: &Path,
) -> Vec<(PathBuf, String)> {
    let mut roots = Vec::new();
    let mut seen = BTreeSet::new();

    for process in vm.active_processes.values() {
        collect_process_host_sync_roots(process, normalized_vm_root, &mut seen, &mut roots);
    }

    roots
}

fn collect_process_host_sync_roots(
    process: &ActiveProcess,
    normalized_vm_root: &Path,
    seen: &mut BTreeSet<(PathBuf, String)>,
    roots: &mut Vec<(PathBuf, String)>,
) {
    let normalized_host_cwd = normalize_host_path(&process.host_cwd);
    if !path_is_within_root(&normalized_host_cwd, normalized_vm_root) {
        let guest_cwd = normalize_path(&process.guest_cwd);
        if seen.insert((normalized_host_cwd.clone(), guest_cwd.clone())) {
            roots.push((normalized_host_cwd, guest_cwd));
        }
    }

    for child in process.child_processes.values() {
        collect_process_host_sync_roots(child, normalized_vm_root, seen, roots);
    }
}

fn sync_process_host_writes_to_kernel(
    vm: &mut VmState,
    process: &ActiveProcess,
) -> Result<(), SidecarError> {
    if vm.root_filesystem_mode != RootFilesystemMode::ReadOnly {
        let shadow_root = vm.cwd.clone();
        sync_host_directory_tree_to_kernel(vm, &shadow_root, "/")?;
    }

    if !path_is_within_root(
        &normalize_host_path(&process.host_cwd),
        &normalize_host_path(&vm.cwd),
    ) {
        sync_host_directory_tree_to_kernel(vm, &process.host_cwd, &process.guest_cwd)?;
    }

    Ok(())
}

fn sync_host_directory_tree_to_kernel(
    vm: &mut VmState,
    host_root: &Path,
    guest_root: &str,
) -> Result<(), SidecarError> {
    let normalized_host_root = normalize_host_path(host_root);
    let normalized_guest_root = normalize_path(guest_root);
    let mut synced_file_times = BTreeMap::new();
    sync_host_directory_tree_to_kernel_inner(
        vm,
        &normalized_host_root,
        &normalized_host_root,
        &normalized_guest_root,
        &mut synced_file_times,
    )
}

fn sync_host_directory_tree_to_kernel_inner(
    vm: &mut VmState,
    host_root: &Path,
    current_host_dir: &Path,
    guest_root: &str,
    synced_file_times: &mut BTreeMap<(u64, u64), (u64, u64)>,
) -> Result<(), SidecarError> {
    let entries = match fs::read_dir(current_host_dir) {
        Ok(entries) => entries,
        Err(error) if error.kind() == std::io::ErrorKind::NotFound => return Ok(()),
        Err(error) => {
            return Err(SidecarError::Io(format!(
                "failed to read host shadow directory {}: {error}",
                current_host_dir.display()
            )));
        }
    };

    for entry in entries {
        let entry = entry.map_err(|error| {
            SidecarError::Io(format!(
                "failed to read host shadow entry in {}: {error}",
                current_host_dir.display()
            ))
        })?;
        let host_path = entry.path();
        let file_type = entry.file_type().map_err(|error| {
            SidecarError::Io(format!(
                "failed to stat host shadow entry {}: {error}",
                host_path.display()
            ))
        })?;
        let relative_path = host_path
            .strip_prefix(host_root)
            .map_err(|error| {
                SidecarError::InvalidState(format!(
                    "failed to relativize host shadow path {} against {}: {error}",
                    host_path.display(),
                    host_root.display()
                ))
            })?
            .to_string_lossy()
            .replace('\\', "/");
        let guest_path = if guest_root == "/" {
            normalize_path(&format!("/{relative_path}"))
        } else {
            normalize_path(&format!(
                "{}/{}",
                guest_root.trim_end_matches('/'),
                relative_path
            ))
        };

        if should_skip_shadow_sync_path(vm, &guest_path) {
            continue;
        }

        if file_type.is_dir() {
            let metadata = entry.metadata().map_err(|error| {
                SidecarError::Io(format!(
                    "failed to read host shadow metadata {}: {error}",
                    host_path.display()
                ))
            })?;
            if !is_shadow_bootstrap_dir(&guest_path)
                && !vm.kernel.exists(&guest_path).unwrap_or(false)
            {
                vm.kernel.mkdir(&guest_path, true).map_err(|error| {
                    SidecarError::InvalidState(format!(
                        "failed to sync host shadow directory {} to guest {}: {}",
                        host_path.display(),
                        guest_path,
                        kernel_error(error)
                    ))
                })?;
                vm.kernel
                    .chmod(&guest_path, host_shadow_mode(&metadata))
                    .map_err(|error| {
                        SidecarError::InvalidState(format!(
                            "failed to sync host shadow directory mode {} to guest {}: {}",
                            host_path.display(),
                            guest_path,
                            kernel_error(error)
                        ))
                    })?;
            }
            sync_host_directory_tree_to_kernel_inner(
                vm,
                host_root,
                &host_path,
                guest_root,
                synced_file_times,
            )?;
            continue;
        }

        if file_type.is_file() {
            let metadata = entry.metadata().map_err(|error| {
                SidecarError::Io(format!(
                    "failed to read host shadow metadata {}: {error}",
                    host_path.display()
                ))
            })?;
            let timestamp_key = (metadata.dev(), metadata.ino());
            let (atime_ms, mtime_ms) =
                *synced_file_times.entry(timestamp_key).or_insert_with(|| {
                    (
                        metadata_time_ms(metadata.atime(), metadata.atime_nsec()),
                        metadata_time_ms(metadata.mtime(), metadata.mtime_nsec()),
                    )
                });
            let desired_mode = host_shadow_mode(&metadata);
            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)
                ))
            })?;
            vm.kernel
                .chmod(&guest_path, desired_mode)
                .map_err(|error| {
                    SidecarError::InvalidState(format!(
                        "failed to sync host shadow file mode {} to guest {}: {}",
                        host_path.display(),
                        guest_path,
                        kernel_error(error)
                    ))
                })?;
            vm.kernel
                .utimes(&guest_path, atime_ms, mtime_ms)
                .map_err(|error| {
                    SidecarError::InvalidState(format!(
                        "failed to sync host shadow file times {} to guest {}: {}",
                        host_path.display(),
                        guest_path,
                        kernel_error(error)
                    ))
                })?;
            continue;
        }

        if file_type.is_symlink() {
            let target = match fs::read_link(&host_path) {
                Ok(target) => target,
                Err(error) if error.kind() == std::io::ErrorKind::NotFound => continue,
                Err(error) => {
                    return Err(SidecarError::Io(format!(
                        "failed to read host shadow symlink {}: {error}",
                        host_path.display()
                    )));
                }
            };
            replace_kernel_symlink(vm, &guest_path, &target.to_string_lossy())?;
        }
    }

    Ok(())
}

fn replace_kernel_symlink(
    vm: &mut VmState,
    guest_path: &str,
    target: &str,
) -> Result<(), SidecarError> {
    if vm.kernel.symlink(target, guest_path).is_ok() {
        return Ok(());
    }

    if let Ok(existing_target) = vm.kernel.read_link(guest_path) {
        if existing_target == target {
            return Ok(());
        }
    }

    let _ = vm.kernel.remove_file(guest_path);
    let _ = vm.kernel.remove_dir(guest_path);
    vm.kernel
        .symlink(target, guest_path)
        .map_err(kernel_error)?;
    Ok(())
}

fn host_shadow_mode(metadata: &fs::Metadata) -> u32 {
    metadata.permissions().mode() & 0o7777
}

/// Reads a shadow-root file back into the kernel even when guest-visible mode
/// bits make it unreadable for the host user. The sidecar is the kernel for
/// this tree, so guest permission bits (for example a 0o200 write-only file
/// produced by `chmod` plus a shell append redirect) must not break the
/// exit-time shadow sync. The original mode is restored after the read.
fn read_host_shadow_file(host_path: &Path, mode: u32) -> std::io::Result<Vec<u8>> {
    match fs::read(host_path) {
        Ok(bytes) => Ok(bytes),
        Err(error) if error.kind() == std::io::ErrorKind::PermissionDenied => {
            fs::set_permissions(host_path, fs::Permissions::from_mode(mode | 0o400))?;
            let result = fs::read(host_path);
            fs::set_permissions(host_path, fs::Permissions::from_mode(mode))?;
            result
        }
        Err(error) => Err(error),
    }
}

fn metadata_time_ms(seconds: i64, nanos: i64) -> u64 {
    let seconds = seconds.max(0) as u64;
    let nanos = nanos.max(0) as u64;
    seconds
        .saturating_mul(1_000)
        .saturating_add(nanos / 1_000_000)
}

fn is_shadow_bootstrap_dir(path: &str) -> bool {
    matches!(
        path,
        "/dev"
            | "/proc"
            | "/tmp"
            | "/bin"
            | "/lib"
            | "/sbin"
            | "/boot"
            | "/etc"
            | "/root"
            | "/run"
            | "/srv"
            | "/sys"
            | "/opt"
            | "/mnt"
            | "/media"
            | "/home"
            | "/home/agentos"
            | "/usr"
            | "/usr/bin"
            | "/usr/games"
            | "/usr/include"
            | "/usr/lib"
            | "/usr/libexec"
            | "/usr/man"
            | "/usr/local"
            | "/usr/local/bin"
            | "/usr/sbin"
            | "/usr/share"
            | "/usr/share/man"
            | "/var"
            | "/var/cache"
            | "/var/empty"
            | "/var/lib"
            | "/var/lock"
            | "/var/log"
            | "/var/run"
            | "/var/spool"
            | "/var/tmp"
            | "/etc/agentos"
            | "/workspace"
    )
}

#[cfg(test)]
mod shadow_sync_tests {
    use super::{is_protected_agentos_shadow_sync_path, is_shadow_bootstrap_dir};

    #[test]
    fn shadow_bootstrap_sync_skips_virtual_home_tree() {
        assert!(is_shadow_bootstrap_dir("/home"));
        assert!(is_shadow_bootstrap_dir("/home/agentos"));
    }

    #[test]
    fn protected_agentos_paths_are_not_shadow_synced() {
        assert!(is_protected_agentos_shadow_sync_path("/etc/agentos"));
        assert!(is_protected_agentos_shadow_sync_path(
            "/etc/agentos/instructions.md"
        ));
        assert!(!is_protected_agentos_shadow_sync_path("/etc/agentos-copy"));
        assert!(!is_protected_agentos_shadow_sync_path("/etc/agentos.md"));
    }
}

fn is_kernel_owned_shadow_sync_path(path: &str) -> bool {
    matches!(path, "/dev" | "/proc" | "/sys")
        || path.starts_with("/dev/")
        || path.starts_with("/proc/")
        || path.starts_with("/sys/")
}

pub(crate) fn is_protected_agentos_shadow_sync_path(path: &str) -> bool {
    path == "/etc/agentos" || path.starts_with("/etc/agentos/")
}

fn should_skip_shadow_sync_path(vm: &VmState, guest_path: &str) -> bool {
    is_kernel_owned_shadow_sync_path(guest_path)
        || is_protected_agentos_shadow_sync_path(guest_path)
        || host_mount_path_for_guest_path_from_mounts(&vm.configuration.mounts, guest_path)
            .is_some()
}

fn resolve_path_like_guest_specifier(cwd: &str, specifier: &str) -> String {
    if specifier.starts_with("file://") {
        normalize_path(specifier.trim_start_matches("file://"))
    } else if specifier.starts_with("file:") {
        normalize_path(specifier.trim_start_matches("file:"))
    } else if specifier.starts_with('/') {
        normalize_path(specifier)
    } else {
        normalize_path(&format!("{cwd}/{specifier}"))
    }
}

fn guest_entrypoint_for_specifier(cwd: &str, specifier: &str) -> Option<String> {
    is_path_like_specifier(specifier).then(|| resolve_path_like_guest_specifier(cwd, specifier))
}

fn is_node_runtime_command(command: &str) -> bool {
    matches!(command, "node" | "npm" | "npx")
        || Path::new(command)
            .file_name()
            .and_then(|name| name.to_str())
            .is_some_and(|name| matches!(name, "node" | "npm" | "npx"))
}

fn resolve_special_node_cli_invocation(
    args: &[String],
    env: &mut BTreeMap<String, String>,
) -> Option<(String, Vec<String>)> {
    let first = args.first()?;
    match first.as_str() {
        "-e" | "--eval" => {
            env.insert(
                String::from("AGENTOS_NODE_EVAL"),
                args.get(1).cloned().unwrap_or_default(),
            );
            Some((first.clone(), args.iter().skip(2).cloned().collect()))
        }
        "-v" | "--version" => {
            env.insert(
                String::from("AGENTOS_NODE_EVAL"),
                String::from("console.log(process.version);"),
            );
            Some((String::from("-e"), args.to_vec()))
        }
        _ => None,
    }
}

fn node_runtime_command_name(command: &str) -> Option<&str> {
    let name = Path::new(command)
        .file_name()
        .and_then(|name| name.to_str())?;
    matches!(name, "node" | "npm" | "npx").then_some(name)
}

struct ResolvedHostNodeCliEntrypoint {
    command_name: String,
    guest_root: String,
    guest_entrypoint: String,
    package_root: PathBuf,
}

fn resolve_host_node_cli_entrypoint(command: &str) -> Option<ResolvedHostNodeCliEntrypoint> {
    let command_name = node_runtime_command_name(command)?;
    if !matches!(command_name, "npm" | "npx") {
        return None;
    }

    let path = std::env::var_os("PATH")?;
    for root in std::env::split_paths(&path) {
        let candidate = root.join(command_name);
        if !candidate.is_file() {
            continue;
        }
        let entrypoint = candidate.canonicalize().ok().unwrap_or(candidate);
        let package_root = entrypoint.parent()?.parent()?.to_path_buf();
        let guest_root = format!("/__secure_exec/node-runtime/{command_name}");
        let relative_entrypoint = entrypoint.strip_prefix(&package_root).ok()?;
        let guest_entrypoint = normalize_path(&format!(
            "{guest_root}/{}",
            relative_entrypoint.to_string_lossy().replace('\\', "/")
        ));
        return Some(ResolvedHostNodeCliEntrypoint {
            command_name: command_name.to_owned(),
            guest_root,
            guest_entrypoint,
            package_root,
        });
    }

    None
}

fn build_host_node_cli_eval(cli: &ResolvedHostNodeCliEntrypoint) -> String {
    let guest_npm_main = normalize_path(&format!("{}/lib/npm.js", cli.guest_root));
    let guest_npm_cli = normalize_path(&format!("{}/bin/npm-cli.js", cli.guest_root));
    let guest_package_json = normalize_path(&format!("{}/package.json", cli.guest_root));
    let guest_display_module = normalize_path(&format!("{}/lib/utils/display.js", cli.guest_root));
    let guest_log_file_module =
        normalize_path(&format!("{}/lib/utils/log-file.js", cli.guest_root));
    let debug_preamble = "const __agentOSDebugNpmCli = !!process.env.CODEX_DEBUG_NPM_CLI; const __agentOSDebugLog = (...args) => { if (__agentOSDebugNpmCli) { console.error('[secure-exec npm debug]', ...args); } }; const __agentOSIsProcessExitError = (error) => !!(error && typeof error === 'object' && (error._isProcessExit === true || error.name === 'ProcessExitError')); const __agentOSResolveExitCode = (code) => Number.isFinite(code) ? code : (Number.isFinite(process.exitCode) ? process.exitCode : 0); const __agentOSFinish = (code) => { process.exitCode = __agentOSResolveExitCode(code); }; if (__agentOSDebugNpmCli) { const __agentOSWrapAsyncFsMethod = (__agentOSTarget, __agentOSMethod) => { const __agentOSOriginal = __agentOSTarget[__agentOSMethod]; if (typeof __agentOSOriginal !== 'function' || __agentOSOriginal.__agentOSDebugWrapped) { return; } const __agentOSWrapped = async (...args) => { const target = args.length > 0 ? args[0] : '<none>'; __agentOSDebugLog(`fs.${__agentOSMethod}:start`, String(target)); try { const result = await __agentOSOriginal.apply(__agentOSTarget, args); __agentOSDebugLog(`fs.${__agentOSMethod}:done`, String(target)); return result; } catch (error) { __agentOSDebugLog(`fs.${__agentOSMethod}:error`, String(target), error && error.stack ? error.stack : String(error)); throw error; } }; __agentOSWrapped.__agentOSDebugWrapped = true; __agentOSTarget[__agentOSMethod] = __agentOSWrapped; }; const __agentOSWrapSyncFsMethod = (__agentOSTarget, __agentOSMethod) => { const __agentOSOriginal = __agentOSTarget[__agentOSMethod]; if (typeof __agentOSOriginal !== 'function' || __agentOSOriginal.__agentOSDebugWrapped) { return; } const __agentOSWrapped = (...args) => { const target = args.length > 0 ? args[0] : '<none>'; __agentOSDebugLog(`fs.${__agentOSMethod}:start`, String(target)); try { const result = __agentOSOriginal.apply(__agentOSTarget, args); __agentOSDebugLog(`fs.${__agentOSMethod}:done`, String(target)); return result; } catch (error) { __agentOSDebugLog(`fs.${__agentOSMethod}:error`, String(target), error && error.stack ? error.stack : String(error)); throw error; } }; __agentOSWrapped.__agentOSDebugWrapped = true; __agentOSTarget[__agentOSMethod] = __agentOSWrapped; }; const __agentOSFsPromiseModules = [require('fs/promises'), require('node:fs/promises')]; for (const __agentOSFsPromises of __agentOSFsPromiseModules) { for (const __agentOSMethod of ['access', 'lstat', 'mkdir', 'open', 'readFile', 'readdir', 'readlink', 'realpath', 'rename', 'rm', 'rmdir', 'stat', 'symlink', 'unlink', 'writeFile']) { __agentOSWrapAsyncFsMethod(__agentOSFsPromises, __agentOSMethod); } } const __agentOSFsModules = [require('fs'), require('node:fs')]; for (const __agentOSFs of __agentOSFsModules) { for (const __agentOSMethod of ['accessSync', 'existsSync', 'lstatSync', 'mkdirSync', 'openSync', 'readFileSync', 'readdirSync', 'readlinkSync', 'realpathSync', 'renameSync', 'rmSync', 'rmdirSync', 'statSync', 'symlinkSync', 'unlinkSync', 'writeFileSync']) { __agentOSWrapSyncFsMethod(__agentOSFs, __agentOSMethod); } } }";
    let display_stub = format!(
        "const __agentOSDisplayModulePath = require.resolve({display_module}); const __agentOSLogFileModulePath = require.resolve({log_file_module}); const __agentOSColorPassthrough = new Proxy((value) => value, {{ get: () => __agentOSColorPassthrough, apply: (_target, _thisArg, args) => args[0] }}); class __AgentOSNpmDisplayStub {{ constructor() {{ this.chalk = {{ noColor: __agentOSColorPassthrough, stdout: __agentOSColorPassthrough, stderr: __agentOSColorPassthrough }}; this._logPaused = true; this._logBuffer = []; this._outputBuffer = []; this._write = (stream, values) => {{ if (!Array.isArray(values) || values.length === 0) {{ return; }} const text = values.map((value) => typeof value === 'string' ? value : String(value)).join(' '); if (text.length === 0) {{ return; }} const normalized = text.replace(/\\r\\n/g, '\\n'); if (/^\\n?> npx\\n> /u.test(normalized)) {{ return; }} stream.write(text.endsWith('\\n') ? text : `${{text}}\\n`); }}; this._inputHandler = (level, ...args) => {{ if (level !== 'read') {{ return; }} const [resolve, reject, callback] = args; Promise.resolve().then(() => callback()).then(resolve, reject); }}; this._logHandler = (level, ...args) => {{ if (level === 'resume') {{ this._logPaused = false; for (const entry of this._logBuffer.splice(0)) {{ this._write(process.stderr, entry); }} return; }} if (level === 'pause') {{ this._logPaused = true; return; }} if (this._logPaused) {{ this._logBuffer.push(args); return; }} this._write(process.stderr, args); }}; this._outputHandler = (level, ...args) => {{ if (level === 'buffer') {{ this._outputBuffer.push(['standard', args]); return; }} if (level === 'flush') {{ for (const [bufferLevel, bufferArgs] of this._outputBuffer.splice(0)) {{ this._write(bufferLevel === 'error' ? process.stderr : process.stdout, bufferArgs); }} return; }} this._write(level === 'error' ? process.stderr : process.stdout, args); }}; process.on('input', this._inputHandler); process.on('log', this._logHandler); process.on('output', this._outputHandler); }} async load() {{ process.emit('log', 'resume'); process.emit('output', 'flush'); }} off() {{ if (this._inputHandler) {{ process.off('input', this._inputHandler); }} if (this._logHandler) {{ process.off('log', this._logHandler); }} if (this._outputHandler) {{ process.off('output', this._outputHandler); }} this._logBuffer.length = 0; this._outputBuffer.length = 0; }} }} class __AgentOSNpmLogFileStub {{ constructor() {{ this.files = []; }} async load() {{ return []; }} off() {{}} }} globalThis._moduleCache[__agentOSDisplayModulePath] = {{ exports: __AgentOSNpmDisplayStub }}; globalThis._moduleCache[__agentOSLogFileModulePath] = {{ exports: __AgentOSNpmLogFileStub }};",
        display_module = serde_json::to_string(&guest_display_module)
            .unwrap_or_else(|_| format!("\"{guest_display_module}\"")),
        log_file_module = serde_json::to_string(&guest_log_file_module)
            .unwrap_or_else(|_| format!("\"{guest_log_file_module}\"")),
    );
    let registry_fetch_stub = "const { createRequire: __agentOSCreateRequire } = require('module'); const __agentOSNpmRequire = __agentOSCreateRequire(require.resolve(__AGENTOS_NPM_MAIN__)); try { const __agentOSMinipassFetchPath = __agentOSNpmRequire.resolve('minipass-fetch'); const __agentOSMinipassFetch = __agentOSNpmRequire(__agentOSMinipassFetchPath); const { FetchError: __agentOSFetchError, Headers: __agentOSFetchHeaders, Request: __agentOSFetchRequest, Response: __agentOSFetchResponse, AbortError: __agentOSAbortError } = __agentOSMinipassFetch; const { Minipass: __agentOSMinipass } = __agentOSNpmRequire('minipass'); const __agentOSCreateBinaryMinipass = () => new __agentOSMinipass({ objectMode: false, encoding: null }); const __agentOSCloneBuffer = (buffer) => Buffer.isBuffer(buffer) ? Buffer.from(buffer) : Buffer.from(buffer ?? []); const __agentOSBufferToArrayBuffer = (buffer) => { const bytes = __agentOSCloneBuffer(buffer); return bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength); }; const __agentOSAttachBufferedBodyMethods = (response, responseBuffer) => { const __agentOSReadBuffer = async () => __agentOSCloneBuffer(responseBuffer); response.__agentOSBufferedBody = __agentOSCloneBuffer(responseBuffer); response.buffer = __agentOSReadBuffer; response.text = async () => (await __agentOSReadBuffer()).toString('utf8'); response.json = async () => JSON.parse(await response.text()); response.arrayBuffer = async () => __agentOSBufferToArrayBuffer(await __agentOSReadBuffer()); response.clone = () => { const clonedBody = __agentOSCreateBinaryMinipass(); const clonedBuffer = __agentOSCloneBuffer(responseBuffer); clonedBody.end(clonedBuffer); const clonedResponse = new __agentOSFetchResponse(clonedBody, { url: response.url, status: response.status, statusText: response.statusText, headers: response.headers, size: response.size, timeout: response.timeout, counter: response.counter, trailer: response.trailer }); return __agentOSAttachBufferedBodyMethods(clonedResponse, clonedBuffer); }; return response; }; const __agentOSNormalizeHeaders = (__agentOSHeaders) => { const normalized = {}; __agentOSHeaders.forEach((value, key) => { if (normalized[key] === undefined) { normalized[key] = value; return; } if (Array.isArray(normalized[key])) { normalized[key].push(value); return; } normalized[key] = [normalized[key], value]; }); return normalized; }; const __agentOSPatchedMinipassFetch = async (input, opts = {}) => { const request = input instanceof __agentOSFetchRequest ? input : new __agentOSFetchRequest(input, opts); const __agentOSController = !request.signal && typeof AbortController === 'function' ? new AbortController() : null; const __agentOSSignal = request.signal ?? __agentOSController?.signal; let __agentOSTimer = null; if (__agentOSController && Number.isFinite(request.timeout) && request.timeout > 0) { __agentOSTimer = setTimeout(() => __agentOSController.abort(new Error(`network timeout at: ${request.url}`)), request.timeout); __agentOSTimer.unref?.(); } try { const requestHeaders = {}; request.headers.forEach((value, key) => { requestHeaders[key] = value; }); const response = await fetch(request.url, { method: request.method, headers: requestHeaders, body: request.body ?? undefined, redirect: request.redirect ?? opts.redirect ?? 'follow', signal: __agentOSSignal, ...(request.body ? { duplex: 'half' } : {}) }); const responseBody = __agentOSCreateBinaryMinipass(); const contentType = String(response.headers.get('content-type') || '').toLowerCase(); const responseBuffer = contentType.includes('json') ? Buffer.from(JSON.stringify(await response.json())) : contentType.startsWith('text/') ? Buffer.from(await response.text()) : Buffer.from(await response.arrayBuffer()); responseBody.end(responseBuffer); return __agentOSAttachBufferedBodyMethods(new __agentOSFetchResponse(responseBody, { url: response.url, status: response.status, statusText: response.statusText, headers: __agentOSNormalizeHeaders(response.headers), size: request.size, timeout: request.timeout, counter: request.counter ?? opts.counter ?? 0, trailer: Promise.resolve(new __agentOSFetchHeaders()) }), responseBuffer); } catch (error) { if (error instanceof Error) { throw error; } throw new __agentOSFetchError(String(error), 'system', error); } finally { if (__agentOSTimer) { clearTimeout(__agentOSTimer); } } }; globalThis.__agentOSPatchedMinipassFetch = __agentOSPatchedMinipassFetch; __agentOSPatchedMinipassFetch.isRedirect = typeof __agentOSMinipassFetch.isRedirect === 'function' ? __agentOSMinipassFetch.isRedirect.bind(__agentOSMinipassFetch) : (code) => code === 301 || code === 302 || code === 303 || code === 307 || code === 308; __agentOSPatchedMinipassFetch.FetchError = __agentOSFetchError; __agentOSPatchedMinipassFetch.Headers = __agentOSFetchHeaders; __agentOSPatchedMinipassFetch.Request = __agentOSFetchRequest; __agentOSPatchedMinipassFetch.Response = __agentOSFetchResponse; __agentOSPatchedMinipassFetch.AbortError = __agentOSAbortError; globalThis._moduleCache[__agentOSMinipassFetchPath] = { exports: __agentOSPatchedMinipassFetch }; __agentOSDebugLog('patched-minipass-fetch', __agentOSMinipassFetchPath); const __agentOSCheckResponsePath = __agentOSNpmRequire.resolve('npm-registry-fetch/lib/check-response.js'); const __agentOSCheckResponse = __agentOSNpmRequire(__agentOSCheckResponsePath); const __agentOSEnsureResponseBodyStream = (response) => { if (!response || (response.body && typeof response.body.on === 'function')) { return response; } const body = __agentOSCreateBinaryMinipass(); const finishWithError = (error) => body.emit('error', error instanceof Error ? error : new Error(String(error))); try { if (typeof response.buffer === 'function') { Promise.resolve(response.buffer()).then((buffer) => body.end(buffer), finishWithError); } else if (Buffer.isBuffer(response.body) || typeof response.body === 'string') { body.end(response.body); } else if (response.body && typeof response.body[Symbol.asyncIterator] === 'function') { (async () => { try { for await (const chunk of response.body) { body.write(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk)); } body.end(); } catch (error) { finishWithError(error); body.end(); } })(); } else { body.end(); } } catch (error) { finishWithError(error); body.end(); } return new __agentOSFetchResponse(body, response); }; globalThis._moduleCache[__agentOSCheckResponsePath] = { exports: (payload) => { const normalized = { ...payload, res: __agentOSEnsureResponseBodyStream(payload.res) }; __agentOSDebugLog('check-response-body', normalized.res && normalized.res.status, typeof (normalized.res && normalized.res.body), normalized.res && normalized.res.body && typeof normalized.res.body.on, normalized.res && normalized.res.body && normalized.res.body.constructor && normalized.res.body.constructor.name, !!(normalized.res && normalized.res.__agentOSBufferedBody), normalized.res && typeof normalized.res.json); return __agentOSCheckResponse(normalized); } }; __agentOSDebugLog('patched-check-response', __agentOSCheckResponsePath); } catch (error) { __agentOSDebugLog('patch-minipass-fetch-failed', error && error.stack ? error.stack : String(error)); } try { const __agentOSRegistryFetchPath = __agentOSNpmRequire.resolve('npm-registry-fetch'); const __agentOSRegistryFetch = __agentOSNpmRequire(__agentOSRegistryFetchPath); const __agentOSWrapRegistryFetch = (fn) => { const wrapResult = (promise) => Promise.resolve(promise).then((res) => { __agentOSDebugLog('registry-fetch-result', res && res.status, typeof (res && res.body), res && res.body && typeof res.body.on, res && res.body && res.body.constructor && res.body.constructor.name, !!(res && res.__agentOSBufferedBody), res && typeof res.json); return res; }); const wrapped = (uri, opts = {}) => wrapResult(globalThis.__agentOSPatchedMinipassFetch(uri, { method: opts.method, headers: opts.headers, body: opts.body, redirect: opts.redirect, signal: opts.signal, timeout: opts.timeout, size: opts.size, counter: opts.counter })); if (typeof fn.json === 'function') { wrapped.json = (uri, opts = {}) => wrapped(uri, opts).then((res) => res.json()); } if (fn.json && typeof fn.json.stream === 'function') { wrapped.json = wrapped.json || {}; wrapped.json.stream = (uri, path, opts = {}) => fn.json.stream(uri, path, { ...opts, agent: false }); } if (typeof fn.pickRegistry === 'function') { wrapped.pickRegistry = fn.pickRegistry.bind(fn); } if (typeof fn.getAuth === 'function') { wrapped.getAuth = fn.getAuth.bind(fn); } return wrapped; }; globalThis._moduleCache[__agentOSRegistryFetchPath] = { exports: __agentOSWrapRegistryFetch(__agentOSRegistryFetch) }; __agentOSDebugLog('patched-npm-registry-fetch', __agentOSRegistryFetchPath); } catch (error) { __agentOSDebugLog('patch-npm-registry-fetch-failed', error && error.stack ? error.stack : String(error)); }";
    match cli.command_name.as_str() {
        "npx" => format!(
            "{debug_preamble} {display_stub} {registry_fetch_stub} process.argv[1] = require.resolve({npm_cli}); process.argv.splice(2, 0, 'exec'); __agentOSDebugLog('argv', JSON.stringify(process.argv), 'cwd', process.cwd()); (async () => {{ const pkg = require({package_json}); if (process.argv.includes('--version') || process.argv.includes('-v')) {{ __agentOSDebugLog('version-shortcut'); console.log(pkg.version); __agentOSFinish(0); return; }} const Npm = require({npm_main}); const npm = new Npm(); __agentOSDebugLog('before-load'); const loaded = await npm.load(); __agentOSDebugLog('after-load', loaded && loaded.command, JSON.stringify(loaded && loaded.args)); if (!loaded.exec) {{ __agentOSDebugLog('no-exec'); __agentOSFinish(); return; }} if (!loaded.command) {{ __agentOSDebugLog('no-command'); const {{ output }} = require('proc-log'); output.standard(npm.usage); __agentOSFinish(1); return; }} __agentOSDebugLog('before-exec', loaded.command, JSON.stringify(loaded.args)); await npm.exec(loaded.command, loaded.args); __agentOSDebugLog('after-exec', __agentOSResolveExitCode()); __agentOSFinish(); }})().catch((error) => {{ if (__agentOSIsProcessExitError(error)) {{ __agentOSDebugLog('process-exit-error', __agentOSResolveExitCode(error.code)); __agentOSFinish(error.code); return; }} console.error(error && error.stack ? error.stack : String(error)); __agentOSFinish(error && typeof error === 'object' && Number.isFinite(error.exitCode) ? error.exitCode : 1); }});",
            debug_preamble = debug_preamble,
            display_stub = display_stub,
            registry_fetch_stub = registry_fetch_stub.replace(
                "__AGENTOS_NPM_MAIN__",
                &serde_json::to_string(&guest_npm_main)
                    .unwrap_or_else(|_| format!("\"{guest_npm_main}\"")),
            ),
            npm_main = serde_json::to_string(&guest_npm_main)
                .unwrap_or_else(|_| format!("\"{guest_npm_main}\"")),
            npm_cli = serde_json::to_string(&guest_npm_cli)
                .unwrap_or_else(|_| format!("\"{guest_npm_cli}\"")),
            package_json = serde_json::to_string(&guest_package_json)
                .unwrap_or_else(|_| format!("\"{guest_package_json}\"")),
        ),
        _ => format!(
            "{debug_preamble} {display_stub} {registry_fetch_stub} __agentOSDebugLog('argv', JSON.stringify(process.argv), 'cwd', process.cwd()); (async () => {{ const pkg = require({package_json}); if (process.argv.includes('--version') || process.argv.includes('-v')) {{ __agentOSDebugLog('version-shortcut'); console.log(pkg.version); __agentOSFinish(0); return; }} const Npm = require({npm_main}); const npm = new Npm(); __agentOSDebugLog('before-load'); const loaded = await npm.load(); __agentOSDebugLog('after-load', loaded && loaded.command, JSON.stringify(loaded && loaded.args)); if (!loaded.exec) {{ __agentOSDebugLog('no-exec'); __agentOSFinish(); return; }} if (!loaded.command) {{ __agentOSDebugLog('no-command'); const {{ output }} = require('proc-log'); output.standard(npm.usage); __agentOSFinish(1); return; }} __agentOSDebugLog('before-exec', loaded.command, JSON.stringify(loaded.args)); await npm.exec(loaded.command, loaded.args); __agentOSDebugLog('after-exec', __agentOSResolveExitCode()); __agentOSFinish(); }})().catch((error) => {{ if (__agentOSIsProcessExitError(error)) {{ __agentOSDebugLog('process-exit-error', __agentOSResolveExitCode(error.code)); __agentOSFinish(error.code); return; }} console.error(error && error.stack ? error.stack : String(error)); __agentOSFinish(error && typeof error === 'object' && Number.isFinite(error.exitCode) ? error.exitCode : 1); }});",
            debug_preamble = debug_preamble,
            display_stub = display_stub,
            registry_fetch_stub = registry_fetch_stub.replace(
                "__AGENTOS_NPM_MAIN__",
                &serde_json::to_string(&guest_npm_main)
                    .unwrap_or_else(|_| format!("\"{guest_npm_main}\"")),
            ),
            npm_main = serde_json::to_string(&guest_npm_main)
                .unwrap_or_else(|_| format!("\"{guest_npm_main}\"")),
            package_json = serde_json::to_string(&guest_package_json)
                .unwrap_or_else(|_| format!("\"{guest_package_json}\"")),
        ),
    }
}

fn resolve_guest_command_entrypoint(
    vm: &VmState,
    guest_cwd: &str,
    command: &str,
    path_env: Option<&str>,
) -> Option<String> {
    if !is_path_like_specifier(command) {
        if let Some(entrypoint) = vm.command_guest_paths.get(command) {
            return Some(entrypoint.clone());
        }

        for search_dir in guest_command_search_dirs(vm, guest_cwd, path_env) {
            let candidate = normalize_path(&format!("{search_dir}/{command}"));
            if let Some(entrypoint) = resolve_guest_command_path_candidate(vm, &candidate) {
                return Some(entrypoint);
            }
        }

        return None;
    }

    let normalized = resolve_path_like_guest_specifier(guest_cwd, command);
    resolve_guest_command_path_candidate(vm, &normalized).or_else(|| {
        // Some guest shells materialize PATH lookups into absolute candidate paths.
        // If that path points into a searched directory but does not exist, fall
        // back to the command basename so the sidecar can remap VM command packages.
        let parent_dir = Path::new(&normalized).parent()?.to_str()?;
        if !guest_command_search_dirs(vm, guest_cwd, path_env)
            .iter()
            .any(|search_dir| normalize_path(search_dir) == normalize_path(parent_dir))
        {
            return None;
        }

        let file_name = Path::new(&normalized).file_name()?.to_str()?;
        vm.command_guest_paths.get(file_name).cloned()
    })
}

fn guest_command_search_dirs(vm: &VmState, guest_cwd: &str, path_env: Option<&str>) -> Vec<String> {
    let mut search_dirs = Vec::new();
    let mut seen = BTreeSet::new();

    if let Some(path) = path_env.or_else(|| vm.guest_env.get("PATH").map(String::as_str)) {
        for segment in path.split(':') {
            let trimmed = segment.trim();
            if trimmed.is_empty() {
                continue;
            }
            let normalized = if trimmed.starts_with('/') {
                normalize_path(trimmed)
            } else {
                normalize_path(&format!("{guest_cwd}/{trimmed}"))
            };
            if seen.insert(normalized.clone()) {
                search_dirs.push(normalized);
            }
        }
    }

    for fallback in ["/bin", "/usr/bin", "/usr/local/bin"] {
        let normalized = String::from(fallback);
        if seen.insert(normalized.clone()) {
            search_dirs.push(normalized);
        }
    }

    search_dirs
}

fn resolve_guest_command_path_candidate(vm: &VmState, candidate: &str) -> Option<String> {
    if candidate.starts_with("/bin/")
        || candidate.starts_with("/usr/bin/")
        || candidate.starts_with("/usr/local/bin/")
        || candidate.starts_with("/__secure_exec/commands/")
    {
        if let Some(file_name) = Path::new(candidate)
            .file_name()
            .and_then(|name| name.to_str())
        {
            if let Some(guest_entrypoint) = vm.command_guest_paths.get(file_name) {
                return Some(guest_entrypoint.clone());
            }
        }
    }

    if vm
        .kernel
        .exists(candidate)
        .ok()
        .is_some_and(|exists| exists)
    {
        return Some(normalize_path(candidate));
    }

    resolve_vm_guest_path_to_host(vm, candidate)
        .is_file()
        .then(|| normalize_path(candidate))
}

fn resolve_host_entrypoint_within_vm_host_cwd(
    vm: &VmState,
    specifier: &str,
) -> Option<(String, String)> {
    let candidate = Path::new(specifier);
    if !candidate.is_absolute() {
        return None;
    }

    let normalized_entrypoint = normalize_host_path(candidate);
    let normalized_host_cwd = normalize_host_path(&vm.host_cwd);
    if !path_is_within_root(&normalized_entrypoint, &normalized_host_cwd) {
        return None;
    }

    let relative = normalized_entrypoint
        .strip_prefix(&normalized_host_cwd)
        .ok()?
        .to_string_lossy()
        .replace('\\', "/");
    let guest_entrypoint = if relative.is_empty() {
        String::from("/")
    } else {
        normalize_path(&format!("/{relative}"))
    };
    Some((
        guest_entrypoint,
        normalized_entrypoint.to_string_lossy().into_owned(),
    ))
}

fn prepare_guest_runtime_env(
    vm: &VmState,
    env: &mut BTreeMap<String, String>,
    guest_cwd: &str,
    host_cwd: &Path,
    guest_entrypoint: Option<String>,
) -> Result<(), SidecarError> {
    let user = vm.kernel.user_profile();
    let path_mappings = runtime_guest_path_mappings(vm);
    let read_paths = expand_host_access_paths(
        std::iter::once(vm.cwd.clone())
            .chain(
                path_mappings
                    .iter()
                    .map(|mapping| PathBuf::from(&mapping.host_path)),
            )
            .chain(std::iter::once(host_cwd.to_path_buf()))
            .collect::<Vec<_>>()
            .as_slice(),
    );
    let write_paths = dedupe_host_paths(
        std::iter::once(vm.cwd.clone())
            .chain(std::iter::once(host_cwd.to_path_buf()))
            .chain(runtime_guest_writable_host_paths(vm))
            .collect::<Vec<_>>()
            .as_slice(),
    );
    let allowed_node_builtins = configured_allowed_node_builtins(vm);
    let loopback_exempt_ports = configured_loopback_exempt_ports(vm);

    env.insert(
        String::from("AGENTOS_GUEST_PATH_MAPPINGS"),
        serde_json::to_string(&path_mappings).map_err(|error| {
            SidecarError::InvalidState(format!("failed to encode guest path mappings: {error}"))
        })?,
    );
    env.entry(String::from(EXECUTION_SANDBOX_ROOT_ENV))
        .or_insert_with(|| normalize_host_path(&vm.cwd).to_string_lossy().into_owned());
    env.insert(
        String::from("AGENTOS_EXTRA_FS_READ_PATHS"),
        serde_json::to_string(
            &read_paths
                .iter()
                .map(|path| path.to_string_lossy().into_owned())
                .collect::<Vec<_>>(),
        )
        .map_err(|error| {
            SidecarError::InvalidState(format!("failed to encode read paths: {error}"))
        })?,
    );
    env.insert(
        String::from("AGENTOS_EXTRA_FS_WRITE_PATHS"),
        serde_json::to_string(
            &write_paths
                .iter()
                .map(|path| path.to_string_lossy().into_owned())
                .collect::<Vec<_>>(),
        )
        .map_err(|error| {
            SidecarError::InvalidState(format!("failed to encode write paths: {error}"))
        })?,
    );
    env.insert(
        String::from("AGENTOS_ALLOWED_NODE_BUILTINS"),
        serde_json::to_string(&allowed_node_builtins).map_err(|error| {
            SidecarError::InvalidState(format!("failed to encode allowed builtins: {error}"))
        })?,
    );
    // The guest JS host platform drives subtractive global scrubbing in the
    // per-execution runtime shim (see prepend_v8_runtime_shim).
    env.insert(
        String::from("AGENTOS_JS_PLATFORM"),
        js_runtime_platform_env(vm).to_owned(),
    );
    // Module-resolution mode (omitted when full Node resolution / the default).
    if let Some(resolution) = js_runtime_module_resolution_env(vm) {
        env.insert(
            String::from("AGENTOS_JS_MODULE_RESOLUTION"),
            resolution.to_owned(),
        );
    }
    // Builtin allow-list gate for the live resolver. Present only when builtins
    // should be restricted (non-node platform => deny all; node + explicit
    // allow-list => exactly those). Absent => unrestricted (node default).
    if let Some(allowlist) = js_runtime_enforced_builtins(vm) {
        env.insert(
            String::from("AGENTOS_JS_BUILTIN_ALLOWLIST"),
            serde_json::to_string(&allowlist).map_err(|error| {
                SidecarError::InvalidState(format!(
                    "failed to encode jsRuntime builtin allow-list: {error}"
                ))
            })?,
        );
    }
    // Virtual OS identity (os.cpus/totalmem/freemem/homedir/userInfo/...) now
    // rides the typed `guest_runtime` (see `guest_runtime_identity`), exposed to
    // the guest as the `__agentOSVirtualOs` structured global by the runtime
    // shim — no longer the `AGENTOS_VIRTUAL_OS_*` env vars.
    // Virtual process uid/gid now ride the typed `guest_runtime` identity
    // (see `guest_runtime_identity`), not the `AGENTOS_VIRTUAL_PROCESS_*` env.
    env.entry(String::from("HOME"))
        .or_insert_with(|| user.homedir.clone());
    env.entry(String::from("USER"))
        .or_insert_with(|| user.username.clone());
    env.entry(String::from("LOGNAME"))
        .or_insert_with(|| user.username.clone());
    env.entry(String::from("SHELL"))
        .or_insert_with(|| user.shell.clone());
    env.entry(String::from("PATH")).or_insert_with(|| {
        vm.guest_env
            .get("PATH")
            .cloned()
            .unwrap_or_else(|| crate::vm::DEFAULT_GUEST_PATH_ENV.to_owned())
    });
    env.entry(String::from("TMPDIR"))
        .or_insert_with(|| String::from("/tmp"));
    env.insert(String::from("PWD"), guest_cwd.to_owned());
    if !loopback_exempt_ports.is_empty() {
        env.insert(
            String::from(LOOPBACK_EXEMPT_PORTS_ENV),
            serde_json::to_string(&loopback_exempt_ports).map_err(|error| {
                SidecarError::InvalidState(format!("failed to encode loopback exemptions: {error}"))
            })?,
        );
    }
    if let Some(guest_entrypoint) = guest_entrypoint {
        env.insert(String::from("AGENTOS_GUEST_ENTRYPOINT"), guest_entrypoint);
    }
    Ok(())
}

fn virtual_os_cpu_count(resource_limits: &ResourceLimits) -> usize {
    resource_limits.virtual_cpu_count.unwrap_or(1).max(1)
}

fn virtual_os_totalmem_bytes(resource_limits: &ResourceLimits) -> u64 {
    resource_limits
        .max_wasm_memory_bytes
        .unwrap_or(1024 * 1024 * 1024)
}

fn virtual_os_freemem_bytes(resource_limits: &ResourceLimits) -> u64 {
    resource_limits
        .max_wasm_memory_bytes
        .unwrap_or(512 * 1024 * 1024)
}

/// Build the typed per-execution JavaScript limits from the per-VM `VmLimits`
/// (sourced from `CreateVmConfig` on the BARE wire). These ride the execution
/// request, not `AGENTOS_*` env vars — see the env-vs-wire rule in
/// `crates/sidecar/CLAUDE.md`.
fn javascript_execution_limits(vm: &VmState) -> JavascriptExecutionLimits {
    JavascriptExecutionLimits {
        v8_heap_limit_mb: vm.limits.js_runtime.v8_heap_limit_mb,
        sync_rpc_wait_timeout_ms: vm.limits.js_runtime.sync_rpc_wait_timeout_ms,
    }
}

/// Build the typed per-execution guest-runtime identity (virtual `process.*`)
/// from kernel state. Replaces the `AGENTOS_VIRTUAL_PROCESS_{UID,GID,PID,PPID}`
/// env round-trip: the runtime shim reads these from `guest_runtime`, not env.
/// `uid`/`gid` come from the VM user profile (applied to every guest);
/// `pid`/`ppid` are per-process and only set for paths that assigned them.
fn guest_runtime_identity(
    vm: &VmState,
    virtual_pid: Option<u64>,
    virtual_ppid: Option<u64>,
) -> GuestRuntimeConfig {
    let user = vm.kernel.user_profile();
    let resource_limits = vm.kernel.resource_limits();
    GuestRuntimeConfig {
        virtual_uid: Some(u64::from(user.uid)),
        virtual_gid: Some(u64::from(user.gid)),
        virtual_pid,
        virtual_ppid,
        virtual_exec_path: None,
        os_cpu_count: Some(virtual_os_cpu_count(resource_limits) as u64),
        os_totalmem: Some(virtual_os_totalmem_bytes(resource_limits)),
        os_freemem: Some(virtual_os_freemem_bytes(resource_limits)),
        os_homedir: Some(user.homedir.clone()),
        os_hostname: None,
        os_shell: Some(user.shell.clone()),
        os_user: Some(user.username.clone()),
    }
}

/// The guest's virtual home directory, sourced from the VM user profile (the
/// same value carried to the guest as `os.homedir()` via `guest_runtime`). Used
/// by sidecar-internal `~`-path resolution; falls back to `/root` for a
/// non-absolute profile value.
fn guest_virtual_home(vm: &VmState) -> String {
    let homedir = vm.kernel.user_profile().homedir;
    if homedir.starts_with('/') {
        homedir
    } else {
        String::from("/root")
    }
}

/// Build the typed per-execution Python limits from the per-VM `VmLimits`.
fn python_execution_limits(vm: &VmState) -> PythonExecutionLimits {
    PythonExecutionLimits {
        output_buffer_max_bytes: Some(vm.limits.python.output_buffer_max_bytes),
        execution_timeout_ms: Some(vm.limits.python.execution_timeout_ms),
        max_old_space_mb: Some(vm.limits.python.max_old_space_mb),
        vfs_rpc_timeout_ms: Some(vm.limits.python.vfs_rpc_timeout_ms),
    }
}

/// Build the typed per-execution WebAssembly limits from the per-VM kernel
/// `ResourceLimits`. Replaces the old `apply_wasm_limit_env` env round-trip;
/// notably this is the path that finally enforces the stack cap that the
/// `AGENTOS_WASM_MAX_STACK_BYTES` env knob set but no reader consumed.
fn wasm_execution_limits(vm: &VmState) -> WasmExecutionLimits {
    let resource_limits = vm.kernel.resource_limits();
    WasmExecutionLimits {
        max_fuel: resource_limits.max_wasm_fuel,
        max_memory_bytes: resource_limits.max_wasm_memory_bytes,
        max_stack_bytes: resource_limits
            .max_wasm_stack_bytes
            .map(|value| value as u64),
    }
}

/// The guest JavaScript host platform configured for this VM, defaulting to
/// full Node.js emulation when no `jsRuntime` config was supplied at create.
fn js_runtime_platform(vm: &VmState) -> vm_config::JsRuntimePlatform {
    vm.configuration
        .js_runtime
        .as_ref()
        .map(|cfg| cfg.platform)
        .unwrap_or(vm_config::JsRuntimePlatform::Node)
}

/// Lowercase wire name for the configured platform, mirroring the serde
/// representation of `vm_config::JsRuntimePlatform`.
fn js_runtime_platform_env(vm: &VmState) -> &'static str {
    match js_runtime_platform(vm) {
        vm_config::JsRuntimePlatform::Node => "node",
        vm_config::JsRuntimePlatform::Browser => "browser",
        vm_config::JsRuntimePlatform::Neutral => "neutral",
        vm_config::JsRuntimePlatform::Bare => "bare",
    }
}

/// Wire name for the configured module-resolution mode, or `None` when it is the
/// full-Node default (which the live resolver also assumes when the env is unset).
fn js_runtime_module_resolution_env(vm: &VmState) -> Option<&'static str> {
    let resolution = vm
        .configuration
        .js_runtime
        .as_ref()
        .map(|cfg| cfg.module_resolution)
        .unwrap_or(vm_config::JsModuleResolution::Node);
    match resolution {
        vm_config::JsModuleResolution::Node => None,
        vm_config::JsModuleResolution::Relative => Some("relative"),
        vm_config::JsModuleResolution::None => Some("none"),
    }
}

/// The builtin allow-list the live resolver should enforce, or `None` to leave
/// builtins unrestricted (full Node default — preserving today's behavior).
/// Non-node platforms enforce an empty list (deny all builtins).
fn js_runtime_enforced_builtins(vm: &VmState) -> Option<Vec<String>> {
    if js_runtime_platform(vm) != vm_config::JsRuntimePlatform::Node {
        return Some(Vec::new());
    }
    vm.configuration
        .js_runtime
        .as_ref()
        .and_then(|cfg| cfg.allowed_builtins.clone())
}

fn configured_allowed_node_builtins(vm: &VmState) -> Vec<String> {
    // Non-node platforms expose no Node builtin modules at all.
    if js_runtime_platform(vm) != vm_config::JsRuntimePlatform::Node {
        return Vec::new();
    }
    // Under the node platform an explicit allow-list wins — including an explicit
    // empty list, which means deny all. Absence falls back to the engine default.
    let configured = match vm
        .configuration
        .js_runtime
        .as_ref()
        .and_then(|cfg| cfg.allowed_builtins.as_ref())
    {
        Some(list) => list.clone(),
        None => DEFAULT_ALLOWED_NODE_BUILTINS
            .iter()
            .map(|value| (*value).to_owned())
            .collect::<Vec<_>>(),
    };
    dedupe_strings(&configured)
}

fn configured_loopback_exempt_ports(vm: &VmState) -> Vec<String> {
    if !vm.configuration.loopback_exempt_ports.is_empty() {
        return vm
            .configuration
            .loopback_exempt_ports
            .iter()
            .map(ToString::to_string)
            .collect();
    }

    vm.create_loopback_exempt_ports
        .iter()
        .map(ToString::to_string)
        .collect()
}

/// Extract the `hostPath` string from a mount plugin's JSON-encoded config.
fn mount_config_host_path(config: &str) -> Option<String> {
    serde_json::from_str::<Value>(config)
        .ok()?
        .get("hostPath")
        .and_then(Value::as_str)
        .map(str::to_owned)
}

fn runtime_guest_writable_host_paths(vm: &VmState) -> Vec<PathBuf> {
    vm.configuration
        .mounts
        .iter()
        .filter(|mount| !mount.read_only)
        .filter_map(|mount| {
            ((mount.plugin.id == "host_dir") || (mount.plugin.id == "module_access"))
                .then(|| mount_config_host_path(&mount.plugin.config))
                .flatten()
                .map(PathBuf::from)
        })
        .collect()
}

fn runtime_guest_path_mappings(vm: &VmState) -> Vec<RuntimeGuestPathMapping> {
    let mut mappings = vm
        .configuration
        .mounts
        .iter()
        .filter_map(|mount| {
            ((mount.plugin.id == "host_dir") || (mount.plugin.id == "module_access"))
                .then(|| {
                    mount_config_host_path(&mount.plugin.config).map(|host_path| {
                        RuntimeGuestPathMapping {
                            guest_path: normalize_path(&mount.guest_path),
                            host_path,
                            read_only: mount.read_only,
                        }
                    })
                })
                .flatten()
        })
        .collect::<Vec<_>>();
    let mut command_root_mappings = vm
        .command_guest_paths
        .values()
        .filter_map(|guest_path| {
            Path::new(guest_path)
                .parent()
                .and_then(|parent| parent.to_str())
                .map(normalize_path)
        })
        .collect::<BTreeSet<_>>()
        .into_iter()
        .map(|guest_path| RuntimeGuestPathMapping {
            host_path: resolve_vm_guest_path_to_host(vm, &guest_path)
                .to_string_lossy()
                .into_owned(),
            guest_path,
            read_only: false,
        })
        .collect::<Vec<_>>();
    mappings.append(&mut command_root_mappings);
    let mut extra_node_modules_roots = mappings
        .iter()
        .filter(|mapping| mapping.guest_path.starts_with("/root/node_modules/"))
        .filter_map(|mapping| {
            host_node_modules_root(Path::new(&mapping.host_path)).map(|host_root| {
                RuntimeGuestPathMapping {
                    guest_path: String::from("/root/node_modules"),
                    host_path: host_root.to_string_lossy().into_owned(),
                    read_only: mapping.read_only,
                }
            })
        })
        .collect::<Vec<_>>();
    mappings.append(&mut extra_node_modules_roots);
    mappings.push(RuntimeGuestPathMapping {
        guest_path: String::from("/"),
        host_path: vm.cwd.to_string_lossy().into_owned(),
        read_only: false,
    });
    mappings.sort_by_key(|mapping| std::cmp::Reverse(mapping.guest_path.len()));
    mappings.dedup_by(|left, right| {
        left.guest_path == right.guest_path && left.host_path == right.host_path
    });
    mappings
}

/// Build a `Send`-able, read-only VFS module reader over the VM's read-only
/// `host_dir`/`module_access` mounts (and the derived `/root/node_modules` root
/// for nested mounts). When present, the V8 bridge thread resolves modules
/// inline against this reader — concurrently with the service loop — so a large
/// cold-start module graph never serializes behind / starves an in-flight ACP
/// `session/new` bootstrap on the single service-loop thread. The reader reads
/// the same mounted tree the guest sees (anchored `openat2`, escaping-symlink
/// refusal), never the host-direct path translator. Returns `None` when the VM
/// has no usable read-only mount, so resolution falls back to the service-loop
/// kernel reader.
fn build_module_reader(
    vm: &VmState,
    resolved: &ResolvedChildProcessExecution,
) -> Option<crate::plugins::host_dir::HostDirModuleReader> {
    let mut pairs: Vec<(String, PathBuf)> = vm
        .configuration
        .mounts
        .iter()
        .filter(|mount| mount.read_only)
        .filter(|mount| (mount.plugin.id == "host_dir") || (mount.plugin.id == "module_access"))
        .filter_map(|mount| {
            mount_config_host_path(&mount.plugin.config)
                .map(|host_path| (normalize_path(&mount.guest_path), PathBuf::from(host_path)))
        })
        .collect();

    let guest_entrypoint = resolved
        .env
        .get("AGENTOS_GUEST_ENTRYPOINT")
        .map(|path| normalize_path(path));
    if let Some(guest_entrypoint) = guest_entrypoint.as_deref() {
        let entrypoint_in_read_only_mount = pairs.iter().any(|(guest_path, _)| {
            guest_entrypoint == guest_path
                || guest_entrypoint.starts_with(&format!("{guest_path}/"))
        });
        if !entrypoint_in_read_only_mount {
            return None;
        }
    }

    // Mirror runtime_guest_path_mappings: a mount nested under
    // `/root/node_modules/<pkg>` implies a `/root/node_modules` root the resolver
    // walks, so expose that root too (e.g. software-package mounts).
    let extra_roots: Vec<(String, PathBuf)> = pairs
        .iter()
        .filter(|(guest_path, _)| guest_path.starts_with("/root/node_modules/"))
        .filter_map(|(_, host_path)| {
            host_node_modules_root(host_path).map(|root| (String::from("/root/node_modules"), root))
        })
        .collect();
    pairs.extend(extra_roots);

    crate::plugins::host_dir::HostDirModuleReader::from_mounts(pairs)
}

fn host_node_modules_root(path: &Path) -> Option<PathBuf> {
    if let Some(root) = path
        .ancestors()
        .filter(|candidate| {
            candidate.file_name().and_then(|name| name.to_str()) == Some("node_modules")
        })
        .last()
        .map(Path::to_path_buf)
    {
        return Some(root);
    }

    fs::canonicalize(path)
        .ok()?
        .ancestors()
        .filter(|candidate| {
            candidate.file_name().and_then(|name| name.to_str()) == Some("node_modules")
        })
        .last()
        .map(Path::to_path_buf)
}

#[cfg(test)]
mod runtime_guest_path_mapping_tests {
    use super::{host_node_modules_root, javascript_sync_rpc_option_bool};
    use serde_json::json;
    use std::fs;
    use std::time::{SystemTime, UNIX_EPOCH};

    #[test]
    fn host_node_modules_root_prefers_workspace_root_over_pnpm_package_node_modules() {
        let unique = SystemTime::now()
            .duration_since(UNIX_EPOCH)
            .expect("clock should be monotonic")
            .as_nanos();
        let temp = std::env::temp_dir().join(format!("secure-exec-sidecar-node-modules-{unique}"));
        let workspace_node_modules = temp.join("node_modules");
        let package_root = workspace_node_modules
            .join(".pnpm")
            .join("example@1.0.0")
            .join("node_modules")
            .join("@scope")
            .join("pkg");
        fs::create_dir_all(&package_root).expect("package root should be created");

        let resolved =
            host_node_modules_root(&package_root).expect("node_modules root should resolve");

        assert_eq!(resolved, workspace_node_modules);

        fs::remove_dir_all(&temp).expect("temp tree should be removed");
    }

    #[test]
    fn host_node_modules_root_preserves_symlinked_workspace_node_modules_path() {
        let unique = SystemTime::now()
            .duration_since(UNIX_EPOCH)
            .expect("clock should be monotonic")
            .as_nanos();
        let temp =
            std::env::temp_dir().join(format!("secure-exec-sidecar-node-modules-symlink-{unique}"));
        let workspace_node_modules = temp.join("node_modules");
        let package_link = workspace_node_modules.join("@scope").join("pkg");
        let real_package = temp.join("registry").join("agent").join("pkg");
        fs::create_dir_all(package_link.parent().expect("package parent should exist"))
            .expect("scoped parent should be created");
        fs::create_dir_all(&real_package).expect("real package root should be created");
        std::os::unix::fs::symlink(&real_package, &package_link)
            .expect("package symlink should be created");

        let resolved =
            host_node_modules_root(&package_link).expect("node_modules root should resolve");

        assert_eq!(resolved, workspace_node_modules);

        fs::remove_dir_all(&temp).expect("temp tree should be removed");
    }

    #[test]
    fn javascript_sync_rpc_option_bool_accepts_boolean_recursive_argument() {
        assert_eq!(
            javascript_sync_rpc_option_bool(&[json!("/workspace"), json!(true)], 1, "recursive"),
            Some(true)
        );
        assert_eq!(
            javascript_sync_rpc_option_bool(
                &[json!("/workspace"), json!({ "recursive": false })],
                1,
                "recursive"
            ),
            Some(false)
        );
    }
}

#[cfg(test)]
mod kernel_poll_sync_rpc_tests {
    use super::{
        service_javascript_kernel_poll_sync_rpc, ActiveExecution, ActiveProcess,
        JavascriptSyncRpcRequest, KernelPollFdResponse, SidecarKernel, ToolExecution,
        EXECUTION_DRIVER_NAME, JAVASCRIPT_COMMAND,
    };
    use secure_exec_kernel::command_registry::CommandDriver;
    use secure_exec_kernel::kernel::{KernelVmConfig, SpawnOptions};
    use secure_exec_kernel::mount_table::MountTable;
    use secure_exec_kernel::permissions::Permissions;
    use secure_exec_kernel::poll::{POLLHUP, POLLIN};
    use secure_exec_kernel::vfs::MemoryFileSystem;
    use serde_json::{json, Value};
    #[test]
    fn javascript_kernel_poll_sync_rpc_reports_multiple_kernel_fds() {
        let mut config = KernelVmConfig::new("vm-js-kernel-poll");
        config.permissions = Permissions::allow_all();
        let mut kernel = SidecarKernel::new(MountTable::new(MemoryFileSystem::new()), config);
        kernel
            .register_driver(CommandDriver::new(
                EXECUTION_DRIVER_NAME,
                [JAVASCRIPT_COMMAND],
            ))
            .expect("register execution driver");

        let kernel_handle = kernel
            .spawn_process(
                JAVASCRIPT_COMMAND,
                Vec::new(),
                SpawnOptions {
                    requester_driver: Some(String::from(EXECUTION_DRIVER_NAME)),
                    ..SpawnOptions::default()
                },
            )
            .expect("spawn javascript kernel process");
        let pid = kernel_handle.pid();

        let (stdin_read_fd, stdin_write_fd) = kernel
            .open_pipe(EXECUTION_DRIVER_NAME, pid)
            .expect("open kernel stdin pipe");
        kernel
            .fd_dup2(EXECUTION_DRIVER_NAME, pid, stdin_read_fd, 0)
            .expect("dup stdin pipe onto fd 0");
        kernel
            .fd_close(EXECUTION_DRIVER_NAME, pid, stdin_read_fd)
            .expect("close original stdin read fd");

        let process = ActiveProcess::new(
            pid,
            kernel_handle,
            super::GuestRuntimeKind::JavaScript,
            ActiveExecution::Tool(ToolExecution::default()),
        );

        kernel
            .fd_write(EXECUTION_DRIVER_NAME, pid, stdin_write_fd, b"poll-ready")
            .expect("write kernel stdin payload");
        kernel
            .fd_close(EXECUTION_DRIVER_NAME, pid, stdin_write_fd)
            .expect("close kernel stdin writer");

        let response = service_javascript_kernel_poll_sync_rpc(
            &mut kernel,
            &process,
            &JavascriptSyncRpcRequest {
                id: 1,
                method: String::from("__kernel_poll"),
                args: vec![
                    json!([
                        { "fd": 0, "events": POLLIN.bits() },
                        { "fd": 1, "events": POLLIN.bits() }
                    ]),
                    json!(250),
                ],
            },
        )
        .expect("poll kernel fds");

        assert_eq!(response["readyCount"], Value::from(1));
        let fds: Vec<KernelPollFdResponse> =
            serde_json::from_value(response["fds"].clone()).expect("kernel poll fd response");
        assert_eq!(
            fds,
            vec![
                KernelPollFdResponse {
                    fd: 0,
                    events: POLLIN.bits(),
                    revents: (POLLIN | POLLHUP).bits(),
                },
                KernelPollFdResponse {
                    fd: 1,
                    events: POLLIN.bits(),
                    revents: 0,
                },
            ]
        );

        process.kernel_handle.finish(0);
        kernel.waitpid(pid).expect("wait javascript kernel process");
    }
}

fn dedupe_strings(values: &[String]) -> Vec<String> {
    let mut seen = BTreeSet::new();
    let mut deduped = Vec::new();
    for value in values {
        if seen.insert(value.clone()) {
            deduped.push(value.clone());
        }
    }
    deduped
}

fn dedupe_host_paths(paths: &[PathBuf]) -> Vec<PathBuf> {
    let mut seen = BTreeSet::new();
    let mut deduped = Vec::new();
    for path in paths {
        let normalized = normalize_host_path(path);
        let key = normalized.to_string_lossy().into_owned();
        if seen.insert(key) {
            deduped.push(normalized);
        }
    }
    deduped
}

fn expand_host_access_paths(paths: &[PathBuf]) -> Vec<PathBuf> {
    let mut expanded = Vec::new();
    let mut seen = BTreeSet::new();

    let mut add_path = |candidate: PathBuf| {
        let normalized = normalize_host_path(&candidate);
        let key = normalized.to_string_lossy().into_owned();
        if seen.insert(key) {
            expanded.push(normalized);
        }
    };

    for host_path in paths {
        add_path(host_path.clone());
        if let Ok(realpath) = fs::canonicalize(host_path) {
            add_path(realpath);
        }

        if host_path.file_name().and_then(|name| name.to_str()) != Some("node_modules") {
            continue;
        }

        let mut current = host_path.parent();
        while let Some(parent) = current {
            let candidate = parent.join("node_modules");
            if candidate.exists() {
                add_path(candidate.clone());
                if let Ok(realpath) = fs::canonicalize(&candidate) {
                    add_path(realpath);
                }
            }
            current = parent.parent();
        }
    }

    expanded
}

fn prepare_javascript_shadow(
    vm: &mut VmState,
    resolved: &ResolvedChildProcessExecution,
) -> Result<(), SidecarError> {
    let guest_entrypoint = resolved
        .env
        .get("AGENTOS_GUEST_ENTRYPOINT")
        .cloned()
        // An absolute `entrypoint` may be a host path that lives inside the VM's
        // host cwd (callers can pass a fully-qualified host path). The guest sees
        // it at its translated guest path (host_cwd -> guest_cwd), so the shadow
        // must be keyed by that guest path rather than the raw host path. Falling
        // back to the host path here would materialize the file at the wrong guest
        // location and the runtime's `require()` would fail with "Cannot find
        // module".
        .or_else(|| {
            resolve_host_entrypoint_within_vm_host_cwd(vm, &resolved.entrypoint)
                .map(|(guest_entrypoint, _)| guest_entrypoint)
        })
        .or_else(|| {
            resolved
                .entrypoint
                .starts_with('/')
                .then(|| normalize_path(&resolved.entrypoint))
        });
    let Some(guest_entrypoint) = guest_entrypoint else {
        return Ok(());
    };
    if host_mount_path_for_guest_path(vm, &guest_entrypoint).is_some() {
        return Ok(());
    }
    if vm.kernel.lstat(&guest_entrypoint).is_err() {
        let host_entrypoint = {
            let candidate = Path::new(&resolved.entrypoint);
            if candidate.is_absolute() {
                candidate.to_path_buf()
            } else {
                resolved.host_cwd.join(candidate)
            }
        };
        if host_entrypoint.exists() {
            materialize_host_path_to_shadow(vm, &guest_entrypoint, &host_entrypoint)?;
            // The shadow write only stages the file on the host side; the runtime
            // resolves modules against the kernel VFS, so the staged entrypoint
            // must be synced into the kernel before execution starts (otherwise
            // `require()` reports "Cannot find module").
            return sync_shadow_entrypoint_into_kernel(vm, &guest_entrypoint);
        }
    }
    materialize_guest_path_to_shadow(vm, &guest_entrypoint)
}

/// Sync a freshly-staged shadow entrypoint into the kernel VFS so the runtime's
/// kernel-backed module resolver can read it. Mirrors the host->kernel file sync
/// used by the broader shadow reconciliation, but scoped to the single
/// entrypoint we just materialized.
fn sync_shadow_entrypoint_into_kernel(
    vm: &mut VmState,
    guest_entrypoint: &str,
) -> Result<(), SidecarError> {
    if vm.kernel.exists(guest_entrypoint).unwrap_or(false) {
        return Ok(());
    }
    let shadow_path = shadow_path_for_guest(vm, guest_entrypoint);
    let bytes = match fs::read(&shadow_path) {
        Ok(bytes) => bytes,
        Err(error) if error.kind() == std::io::ErrorKind::NotFound => return Ok(()),
        Err(error) => {
            return Err(SidecarError::Io(format!(
                "failed to read staged shadow entrypoint {}: {error}",
                shadow_path.display()
            )));
        }
    };
    if let Some(parent) = guest_parent_path(guest_entrypoint) {
        if !vm.kernel.exists(&parent).unwrap_or(false) {
            vm.kernel.mkdir(&parent, true).map_err(kernel_error)?;
        }
    }
    vm.kernel
        .write_file(guest_entrypoint, bytes)
        .map_err(kernel_error)?;
    Ok(())
}

fn guest_parent_path(guest_path: &str) -> Option<String> {
    let parent = Path::new(guest_path).parent()?;
    let parent = parent.to_string_lossy();
    if parent.is_empty() || parent == "/" {
        None
    } else {
        Some(parent.into_owned())
    }
}

fn materialize_host_path_to_shadow(
    vm: &VmState,
    guest_path: &str,
    host_path: &Path,
) -> Result<(), SidecarError> {
    let shadow_path = shadow_path_for_guest(vm, guest_path);
    let metadata = fs::symlink_metadata(host_path)
        .map_err(|error| SidecarError::Io(format!("failed to stat host entrypoint: {error}")))?;

    if metadata.file_type().is_symlink() {
        if let Some(parent) = shadow_path.parent() {
            fs::create_dir_all(parent).map_err(|error| {
                SidecarError::Io(format!("failed to create shadow symlink parent: {error}"))
            })?;
        }
        let _ = fs::remove_file(&shadow_path);
        let _ = fs::remove_dir_all(&shadow_path);
        let target = fs::read_link(host_path)
            .map_err(|error| SidecarError::Io(format!("failed to read host symlink: {error}")))?;
        std::os::unix::fs::symlink(&target, &shadow_path)
            .map_err(|error| SidecarError::Io(format!("failed to mirror host symlink: {error}")))?;
        return Ok(());
    }

    if metadata.is_dir() {
        fs::create_dir_all(&shadow_path).map_err(|error| {
            SidecarError::Io(format!("failed to create shadow directory: {error}"))
        })?;
        fs::set_permissions(
            &shadow_path,
            fs::Permissions::from_mode(metadata.permissions().mode() & 0o7777),
        )
        .map_err(|error| {
            SidecarError::Io(format!(
                "failed to set shadow directory mode on {}: {error}",
                shadow_path.display()
            ))
        })?;
        return Ok(());
    }

    if let Some(parent) = shadow_path.parent() {
        fs::create_dir_all(parent).map_err(|error| {
            SidecarError::Io(format!("failed to create shadow parent: {error}"))
        })?;
    }
    let bytes = fs::read(host_path)
        .map_err(|error| SidecarError::Io(format!("failed to read host entrypoint: {error}")))?;
    fs::write(&shadow_path, bytes).map_err(|error| {
        SidecarError::Io(format!(
            "failed to mirror host file into shadow root: {error}"
        ))
    })?;
    fs::set_permissions(
        &shadow_path,
        fs::Permissions::from_mode(metadata.permissions().mode() & 0o7777),
    )
    .map_err(|error| {
        SidecarError::Io(format!(
            "failed to set shadow file mode on {}: {error}",
            shadow_path.display()
        ))
    })?;
    Ok(())
}

fn materialize_guest_path_to_shadow(
    vm: &mut VmState,
    guest_path: &str,
) -> Result<(), SidecarError> {
    let stat = vm.kernel.lstat(guest_path).map_err(kernel_error)?;
    let shadow_path = shadow_path_for_guest(vm, guest_path);

    if stat.is_symbolic_link {
        if let Some(parent) = shadow_path.parent() {
            fs::create_dir_all(parent).map_err(|error| {
                SidecarError::Io(format!("failed to create shadow symlink parent: {error}"))
            })?;
        }
        let _ = fs::remove_file(&shadow_path);
        let _ = fs::remove_dir_all(&shadow_path);
        let target = vm.kernel.read_link(guest_path).map_err(kernel_error)?;
        std::os::unix::fs::symlink(&target, &shadow_path)
            .map_err(|error| SidecarError::Io(format!("failed to mirror symlink: {error}")))?;
        return Ok(());
    }

    if stat.is_directory {
        fs::create_dir_all(&shadow_path).map_err(|error| {
            SidecarError::Io(format!("failed to create shadow directory: {error}"))
        })?;
        fs::set_permissions(&shadow_path, fs::Permissions::from_mode(stat.mode & 0o7777)).map_err(
            |error| {
                SidecarError::Io(format!(
                    "failed to set shadow directory mode on {}: {error}",
                    shadow_path.display()
                ))
            },
        )?;
        return Ok(());
    }

    if let Some(parent) = shadow_path.parent() {
        fs::create_dir_all(parent).map_err(|error| {
            SidecarError::Io(format!("failed to create shadow parent: {error}"))
        })?;
    }
    let bytes = vm.kernel.read_file(guest_path).map_err(kernel_error)?;
    fs::write(&shadow_path, bytes).map_err(|error| {
        SidecarError::Io(format!(
            "failed to mirror guest file into shadow root: {error}"
        ))
    })?;
    fs::set_permissions(&shadow_path, fs::Permissions::from_mode(stat.mode & 0o7777)).map_err(
        |error| {
            SidecarError::Io(format!(
                "failed to set shadow file mode on {}: {error}",
                shadow_path.display()
            ))
        },
    )?;
    Ok(())
}

fn load_javascript_entrypoint_source(
    vm: &mut VmState,
    host_cwd: &Path,
    entrypoint: &str,
    env: &BTreeMap<String, String>,
) -> Option<String> {
    let mut read_guest_file = |path: &str| {
        vm.kernel
            .read_file(path)
            .ok()
            .and_then(|bytes| String::from_utf8(bytes).ok())
    };

    if let Some(source) = env
        .get("AGENTOS_GUEST_ENTRYPOINT")
        .filter(|path| path.starts_with('/'))
        .and_then(|path| read_guest_file(path))
    {
        return Some(source);
    }

    if entrypoint.starts_with('/') {
        if let Some(source) = read_guest_file(entrypoint) {
            return Some(source);
        }
    }

    let host_entrypoint = if Path::new(entrypoint).is_absolute() {
        PathBuf::from(entrypoint)
    } else {
        host_cwd.join(entrypoint)
    };
    let normalized_entrypoint = normalize_host_path(&host_entrypoint);
    let sandbox_root = normalize_host_path(&vm.cwd);
    let host_cwd = normalize_host_path(&vm.host_cwd);
    if !path_is_within_root(&normalized_entrypoint, &sandbox_root)
        && !path_is_within_root(&normalized_entrypoint, &host_cwd)
    {
        return None;
    }

    fs::read_to_string(&normalized_entrypoint).ok()
}

fn emit_dns_resolution_event<B>(
    bridge: &SharedBridge<B>,
    vm_id: &str,
    hostname: &str,
    source: KernelDnsResolutionSource,
    addresses: &[IpAddr],
    dns: &VmDnsConfig,
) where
    B: NativeSidecarBridge + Send + 'static,
    BridgeError<B>: fmt::Debug + Send + Sync + 'static,
{
    let _ = emit_structured_event(
        bridge,
        vm_id,
        "network.dns.resolved",
        audit_fields([
            ("hostname", hostname.to_owned()),
            ("source", source.as_str().to_owned()),
            (
                "addresses",
                addresses
                    .iter()
                    .map(ToString::to_string)
                    .collect::<Vec<_>>()
                    .join(","),
            ),
            ("address_count", addresses.len().to_string()),
            ("resolver_count", dns.name_servers.len().to_string()),
            (
                "resolvers",
                dns.name_servers
                    .iter()
                    .map(ToString::to_string)
                    .collect::<Vec<_>>()
                    .join(","),
            ),
        ]),
    );
}

fn emit_dns_record_resolution_event<B>(
    bridge: &SharedBridge<B>,
    vm_id: &str,
    hostname: &str,
    resolution: &DnsRecordResolution,
    dns: &VmDnsConfig,
) where
    B: NativeSidecarBridge + Send + 'static,
    BridgeError<B>: fmt::Debug + Send + Sync + 'static,
{
    if let Some(addresses) = dns_resolution_ip_addrs(resolution.records()) {
        emit_dns_resolution_event(
            bridge,
            vm_id,
            hostname,
            resolution.source(),
            &addresses,
            dns,
        );
        return;
    }

    let _ = emit_structured_event(
        bridge,
        vm_id,
        "network.dns.resolved",
        audit_fields([
            ("hostname", hostname.to_owned()),
            ("source", resolution.source().as_str().to_owned()),
            (
                "addresses",
                resolution
                    .records()
                    .iter()
                    .map(summarize_dns_record)
                    .collect::<Vec<_>>()
                    .join(","),
            ),
            ("address_count", resolution.records().len().to_string()),
            ("resolver_count", dns.name_servers.len().to_string()),
            (
                "resolvers",
                dns.name_servers
                    .iter()
                    .map(ToString::to_string)
                    .collect::<Vec<_>>()
                    .join(","),
            ),
        ]),
    );
}

fn emit_dns_resolution_failure_event<B>(
    bridge: &SharedBridge<B>,
    vm_id: &str,
    hostname: &str,
    dns: &VmDnsConfig,
    error: &SidecarError,
) where
    B: NativeSidecarBridge + Send + 'static,
    BridgeError<B>: fmt::Debug + Send + Sync + 'static,
{
    let _ = emit_structured_event(
        bridge,
        vm_id,
        "network.dns.resolve_failed",
        audit_fields([
            ("hostname", hostname.to_owned()),
            ("reason", error.to_string()),
            ("resolver_count", dns.name_servers.len().to_string()),
            (
                "resolvers",
                dns.name_servers
                    .iter()
                    .map(ToString::to_string)
                    .collect::<Vec<_>>()
                    .join(","),
            ),
        ]),
    );
}

fn parse_dns_record_type(rrtype: &str) -> Result<RecordType, SidecarError> {
    match rrtype {
        "A" => Ok(RecordType::A),
        "AAAA" => Ok(RecordType::AAAA),
        "MX" => Ok(RecordType::MX),
        "TXT" => Ok(RecordType::TXT),
        "SRV" => Ok(RecordType::SRV),
        "CNAME" => Ok(RecordType::CNAME),
        "PTR" => Ok(RecordType::PTR),
        "NS" => Ok(RecordType::NS),
        "SOA" => Ok(RecordType::SOA),
        "NAPTR" => Ok(RecordType::NAPTR),
        "CAA" => Ok(RecordType::CAA),
        "ANY" => Ok(RecordType::ANY),
        other => Err(SidecarError::Execution(format!(
            "ERR_NOT_IMPLEMENTED: dns rrtype {other} is not supported by the secure-exec dns bridge"
        ))),
    }
}

fn dns_resolution_to_node_value(
    resolution: &DnsRecordResolution,
    requested_type: &str,
) -> Result<Value, SidecarError> {
    let safe_ips = dns_resolution_safe_ip_set(resolution.records(), resolution.hostname())?;
    match requested_type {
        "A" | "AAAA" => Ok(Value::Array(
            resolution
                .records()
                .iter()
                .filter_map(|record| dns_record_ip_string(record, &safe_ips))
                .map(Value::String)
                .collect(),
        )),
        "MX" => Ok(Value::Array(
            resolution
                .records()
                .iter()
                .filter_map(|record| match record.data() {
                    RData::MX(mx) => Some(json!({
                        "priority": mx.preference,
                        "exchange": normalize_dns_name_for_node(&mx.exchange),
                        "type": "MX",
                    })),
                    _ => None,
                })
                .collect(),
        )),
        "TXT" => Ok(Value::Array(
            resolution
                .records()
                .iter()
                .filter_map(|record| match record.data() {
                    RData::TXT(txt) => Some(Value::Array(
                        txt.txt_data
                            .iter()
                            .map(|entry| Value::String(String::from_utf8_lossy(entry).into_owned()))
                            .collect(),
                    )),
                    _ => None,
                })
                .collect(),
        )),
        "SRV" => Ok(Value::Array(
            resolution
                .records()
                .iter()
                .filter_map(|record| match record.data() {
                    RData::SRV(srv) => Some(json!({
                        "priority": srv.priority,
                        "weight": srv.weight,
                        "port": srv.port,
                        "name": normalize_dns_name_for_node(&srv.target),
                        "type": "SRV",
                    })),
                    _ => None,
                })
                .collect(),
        )),
        "CNAME" => Ok(Value::Array(
            resolution
                .records()
                .iter()
                .filter_map(|record| match record.data() {
                    RData::CNAME(name) => Some(Value::String(normalize_dns_name_for_node(&name.0))),
                    _ => None,
                })
                .collect(),
        )),
        "PTR" => Ok(Value::Array(
            resolution
                .records()
                .iter()
                .filter_map(|record| match record.data() {
                    RData::PTR(name) => Some(Value::String(normalize_dns_name_for_node(&name.0))),
                    _ => None,
                })
                .collect(),
        )),
        "NS" => Ok(Value::Array(
            resolution
                .records()
                .iter()
                .filter_map(|record| match record.data() {
                    RData::NS(name) => Some(Value::String(normalize_dns_name_for_node(&name.0))),
                    _ => None,
                })
                .collect(),
        )),
        "SOA" => resolution
            .records()
            .iter()
            .find_map(|record| match record.data() {
                RData::SOA(soa) => Some(json!({
                    "nsname": normalize_dns_name_for_node(&soa.mname),
                    "hostmaster": normalize_dns_name_for_node(&soa.rname),
                    "serial": soa.serial,
                    "refresh": soa.refresh,
                    "retry": soa.retry,
                    "expire": soa.expire,
                    "minttl": soa.minimum,
                })),
                _ => None,
            })
            .ok_or_else(|| {
                SidecarError::Execution(String::from("failed to resolve DNS SOA record"))
            }),
        "NAPTR" => Ok(Value::Array(
            resolution
                .records()
                .iter()
                .filter_map(|record| match record.data() {
                    RData::NAPTR(naptr) => Some(json!({
                        "flags": String::from_utf8_lossy(&naptr.flags).into_owned(),
                        "service": String::from_utf8_lossy(&naptr.services).into_owned(),
                        "regexp": String::from_utf8_lossy(&naptr.regexp).into_owned(),
                        "replacement": normalize_dns_name_for_node(&naptr.replacement),
                        "order": naptr.order,
                        "preference": naptr.preference,
                    })),
                    _ => None,
                })
                .collect(),
        )),
        "CAA" => Ok(Value::Array(
            resolution
                .records()
                .iter()
                .filter_map(|record| match record.data() {
                    RData::CAA(caa) => {
                        let mut value = serde_json::Map::new();
                        value.insert(
                            "critical".to_owned(),
                            Value::from(u8::from(caa.issuer_critical)),
                        );
                        value.insert("type".to_owned(), Value::String(String::from("CAA")));
                        if caa.tag.eq_ignore_ascii_case("iodef") {
                            value.insert(
                                "iodef".to_owned(),
                                Value::String(
                                    caa.value_as_iodef()
                                        .map(|url| url.to_string())
                                        .unwrap_or_else(|_| {
                                            String::from_utf8_lossy(&caa.value).into_owned()
                                        }),
                                ),
                            );
                        } else if let Ok((issuer, _params)) = caa.value_as_issue() {
                            let field = if caa.tag.eq_ignore_ascii_case("issuewild") {
                                "issuewild"
                            } else {
                                "issue"
                            };
                            value.insert(
                                field.to_owned(),
                                Value::String(
                                    issuer.as_ref().map(ToString::to_string).unwrap_or_else(|| {
                                        String::from_utf8_lossy(&caa.value).into_owned()
                                    }),
                                ),
                            );
                        } else {
                            value.insert(
                                caa.tag.to_ascii_lowercase(),
                                Value::String(String::from_utf8_lossy(&caa.value).into_owned()),
                            );
                        }
                        Some(Value::Object(value))
                    }
                    _ => None,
                })
                .collect(),
        )),
        "ANY" => Ok(Value::Array(
            resolution
                .records()
                .iter()
                .filter_map(|record| dns_any_record_to_value(record, &safe_ips))
                .collect(),
        )),
        other => Err(SidecarError::Execution(format!(
            "ERR_NOT_IMPLEMENTED: dns rrtype {other} is not supported by the secure-exec dns bridge"
        ))),
    }
}

fn dns_resolution_safe_ip_set(
    records: &[Record],
    hostname: &str,
) -> Result<BTreeSet<IpAddr>, SidecarError> {
    let ips = records
        .iter()
        .filter_map(dns_record_ip_addr)
        .collect::<Vec<_>>();
    if ips.is_empty() {
        return Ok(BTreeSet::new());
    }
    Ok(filter_dns_safe_ip_addrs(ips, hostname)?
        .into_iter()
        .collect())
}

fn dns_resolution_ip_addrs(records: &[Record]) -> Option<Vec<IpAddr>> {
    let ips = records
        .iter()
        .filter_map(dns_record_ip_addr)
        .collect::<Vec<_>>();
    if ips.is_empty() {
        return None;
    }
    Some(ips)
}

fn dns_record_ip_addr(record: &Record) -> Option<IpAddr> {
    match record.data() {
        RData::A(address) => Some(IpAddr::V4(**address)),
        RData::AAAA(address) => Some(IpAddr::V6(**address)),
        _ => None,
    }
}

fn dns_record_ip_string(record: &Record, safe_ips: &BTreeSet<IpAddr>) -> Option<String> {
    let ip = dns_record_ip_addr(record)?;
    safe_ips.contains(&ip).then(|| ip.to_string())
}

fn dns_any_record_to_value(record: &Record, safe_ips: &BTreeSet<IpAddr>) -> Option<Value> {
    let value = match record.data() {
        RData::A(_) | RData::AAAA(_) => json!({
            "address": dns_record_ip_string(record, safe_ips)?,
            "ttl": record.ttl(),
            "type": record.record_type().to_string(),
        }),
        RData::MX(mx) => json!({
            "exchange": normalize_dns_name_for_node(&mx.exchange),
            "priority": mx.preference,
            "type": "MX",
        }),
        RData::TXT(txt) => json!({
            "entries": txt
                .txt_data
                .iter()
                .map(|entry| String::from_utf8_lossy(entry).into_owned())
                .collect::<Vec<_>>(),
            "type": "TXT",
        }),
        RData::SRV(srv) => json!({
            "name": normalize_dns_name_for_node(&srv.target),
            "port": srv.port,
            "priority": srv.priority,
            "weight": srv.weight,
            "type": "SRV",
        }),
        RData::CNAME(name) => json!({
            "value": normalize_dns_name_for_node(&name.0),
            "type": "CNAME",
        }),
        RData::PTR(name) => json!({
            "value": normalize_dns_name_for_node(&name.0),
            "type": "PTR",
        }),
        RData::NS(name) => json!({
            "value": normalize_dns_name_for_node(&name.0),
            "type": "NS",
        }),
        RData::SOA(soa) => json!({
            "nsname": normalize_dns_name_for_node(&soa.mname),
            "hostmaster": normalize_dns_name_for_node(&soa.rname),
            "serial": soa.serial,
            "refresh": soa.refresh,
            "retry": soa.retry,
            "expire": soa.expire,
            "minttl": soa.minimum,
            "type": "SOA",
        }),
        RData::NAPTR(naptr) => json!({
            "flags": String::from_utf8_lossy(&naptr.flags).into_owned(),
            "service": String::from_utf8_lossy(&naptr.services).into_owned(),
            "regexp": String::from_utf8_lossy(&naptr.regexp).into_owned(),
            "replacement": normalize_dns_name_for_node(&naptr.replacement),
            "order": naptr.order,
            "preference": naptr.preference,
            "type": "NAPTR",
        }),
        RData::CAA(caa) => {
            let mut value = serde_json::Map::new();
            value.insert(
                "critical".to_owned(),
                Value::from(u8::from(caa.issuer_critical)),
            );
            value.insert("type".to_owned(), Value::String(String::from("CAA")));
            if caa.tag.eq_ignore_ascii_case("iodef") {
                value.insert(
                    "iodef".to_owned(),
                    Value::String(
                        caa.value_as_iodef()
                            .map(|url| url.to_string())
                            .unwrap_or_else(|_| String::from_utf8_lossy(&caa.value).into_owned()),
                    ),
                );
            } else if let Ok((issuer, _params)) = caa.value_as_issue() {
                let field = if caa.tag.eq_ignore_ascii_case("issuewild") {
                    "issuewild"
                } else {
                    "issue"
                };
                value.insert(
                    field.to_owned(),
                    Value::String(
                        issuer
                            .as_ref()
                            .map(ToString::to_string)
                            .unwrap_or_else(|| String::from_utf8_lossy(&caa.value).into_owned()),
                    ),
                );
            }
            Value::Object(value)
        }
        _ => return None,
    };
    Some(value)
}

fn normalize_dns_name_for_node(name: &impl ToString) -> String {
    name.to_string().trim_end_matches('.').to_owned()
}

fn summarize_dns_record(record: &Record) -> String {
    match record.data() {
        RData::A(_) | RData::AAAA(_) => record.data().to_string(),
        _ => format!("{} {}", record.record_type(), record.data()),
    }
}

// build_root_filesystem, convert_root_lower_descriptor, convert_root_filesystem_entry,
// root_snapshot_entry moved to crate::bootstrap

// apply_root_filesystem_entry, ensure_parent_directories moved to crate::bootstrap

// ProcNetEntry moved to crate::state

fn find_socket_state_entry(
    vm: Option<&VmState>,
    kind: SocketQueryKind,
    request: &FindListenerRequest,
) -> Result<Option<SocketStateEntry>, SidecarError> {
    let vm = vm.ok_or_else(|| SidecarError::InvalidState(String::from("unknown sidecar VM")))?;

    for (process_id, process) in &vm.active_processes {
        if let Some(path) = request.path.as_deref() {
            if matches!(kind, SocketQueryKind::TcpListener) {
                for listener in process.unix_listeners.values() {
                    if listener.path() != path {
                        continue;
                    }
                    return Ok(Some(SocketStateEntry {
                        process_id: process_id.to_owned(),
                        host: None,
                        port: None,
                        path: Some(path.to_owned()),
                    }));
                }
            }
        }

        if request.path.is_none() {
            if let Some(entry) =
                find_kernel_socket_state_entry(&vm.kernel, process_id, process, kind, request)?
            {
                return Ok(Some(entry));
            }

            match kind {
                SocketQueryKind::TcpListener => {
                    for server in process.http_servers.values() {
                        let local_addr = server.guest_local_addr;
                        let local_host = local_addr.ip().to_string();
                        if !socket_host_matches(request.host.as_deref(), &local_host) {
                            continue;
                        }
                        if let Some(port) = request.port {
                            if local_addr.port() != port {
                                continue;
                            }
                        }
                        return Ok(Some(SocketStateEntry {
                            process_id: process_id.to_owned(),
                            host: Some(local_host),
                            port: Some(local_addr.port()),
                            path: None,
                        }));
                    }

                    for listener in process.tcp_listeners.values() {
                        if listener.kernel_socket_id.is_some() {
                            continue;
                        }
                        let local_addr = listener.guest_local_addr();
                        let local_host = local_addr.ip().to_string();
                        if !socket_host_matches(request.host.as_deref(), &local_host) {
                            continue;
                        }
                        if let Some(port) = request.port {
                            if local_addr.port() != port {
                                continue;
                            }
                        }
                        return Ok(Some(SocketStateEntry {
                            process_id: process_id.to_owned(),
                            host: Some(local_host),
                            port: Some(local_addr.port()),
                            path: None,
                        }));
                    }
                }
                SocketQueryKind::UdpBound => {
                    for socket in process.udp_sockets.values() {
                        if socket.kernel_socket_id.is_some() {
                            continue;
                        }
                        let Some(local_addr) = socket.local_addr() else {
                            continue;
                        };
                        let local_host = local_addr.ip().to_string();
                        if !socket_host_matches(request.host.as_deref(), &local_host) {
                            continue;
                        }
                        if let Some(port) = request.port {
                            if local_addr.port() != port {
                                continue;
                            }
                        }
                        return Ok(Some(SocketStateEntry {
                            process_id: process_id.to_owned(),
                            host: Some(local_host),
                            port: Some(local_addr.port()),
                            path: None,
                        }));
                    }
                }
            }
        }

        let child_pid = process.execution.child_pid();
        let inodes = socket_inodes_for_pid(child_pid)?;
        if inodes.is_empty() {
            continue;
        }

        if let Some(path) = request.path.as_deref() {
            if let Some(listener) = find_unix_socket_for_pid(child_pid, &inodes, path, process_id)?
            {
                return Ok(Some(listener));
            }
            continue;
        }

        let table_paths = match kind {
            SocketQueryKind::TcpListener => [
                format!("/proc/{child_pid}/net/tcp"),
                format!("/proc/{child_pid}/net/tcp6"),
            ],
            SocketQueryKind::UdpBound => [
                format!("/proc/{child_pid}/net/udp"),
                format!("/proc/{child_pid}/net/udp6"),
            ],
        };
        for table_path in table_paths {
            if let Some(entry) = find_inet_socket_for_pid(
                &table_path,
                &inodes,
                kind,
                request.host.as_deref(),
                request.port,
                process_id,
            )? {
                return Ok(Some(entry));
            }
        }
    }

    Ok(None)
}

fn require_vm_inspection_permission<B>(
    bridge: &SharedBridge<B>,
    vm_id: &str,
    capability: &str,
    domain: &str,
    resource: &str,
) -> Result<(), SidecarError>
where
    B: NativeSidecarBridge + Send + 'static,
    BridgeError<B>: fmt::Debug + Send + Sync + 'static,
{
    let decision = bridge.static_permission_decision(vm_id, capability, domain, Some(resource));
    if decision.as_ref().is_some_and(|decision| decision.allow) {
        return Ok(());
    }

    let reason = decision
        .and_then(|decision| decision.reason)
        .unwrap_or_else(|| format!("{capability} permission required"));
    Err(SidecarError::Execution(format!(
        "EACCES: permission denied, {resource}: {reason}"
    )))
}

fn socket_query_resource(kind: SocketQueryKind, request: &FindListenerRequest) -> String {
    if let Some(path) = request.path.as_deref() {
        return format!("unix://{path}");
    }

    let host = request.host.as_deref().unwrap_or("*");
    let port = request
        .port
        .map_or_else(|| String::from("*"), |port| port.to_string());
    match kind {
        SocketQueryKind::TcpListener => format!("tcp://{host}:{port}"),
        SocketQueryKind::UdpBound => format!("udp://{host}:{port}"),
    }
}

fn snapshot_vm_processes(vm: &VmState) -> Vec<ProcessSnapshotEntry> {
    let process_table = vm.kernel.list_processes();
    snapshot_vm_processes_inner(vm, &process_table)
}

fn snapshot_vm_processes_inner(
    vm: &VmState,
    process_table: &BTreeMap<u32, secure_exec_kernel::process_table::ProcessInfo>,
) -> Vec<ProcessSnapshotEntry> {
    let mut entries = Vec::new();

    for (process_id, process) in &vm.active_processes {
        collect_process_snapshot_entries(process_id, process, process_table, &mut entries);
    }

    for exited in &vm.exited_process_snapshots {
        entries.push(exited.process.clone());
    }

    entries
}

fn prune_exited_process_snapshots(vm: &mut VmState) {
    let cutoff = Instant::now() - EXITED_PROCESS_SNAPSHOT_RETENTION;
    while vm
        .exited_process_snapshots
        .front()
        .is_some_and(|snapshot| snapshot.captured_at < cutoff)
    {
        vm.exited_process_snapshots.pop_front();
    }
}

fn build_process_snapshot_entry(
    process_id: &str,
    process: &ActiveProcess,
    info: &secure_exec_kernel::process_table::ProcessInfo,
    exit_code: Option<i32>,
) -> ProcessSnapshotEntry {
    ProcessSnapshotEntry {
        process_id: process_id.to_owned(),
        pid: info.pid,
        ppid: info.ppid,
        pgid: info.pgid,
        sid: info.sid,
        driver: info.driver.clone(),
        command: info.command.clone(),
        args: Vec::new(),
        cwd: process.guest_cwd.clone(),
        status: if exit_code.is_some() {
            ProcessSnapshotStatus::Exited
        } else {
            match info.status {
                ProcessStatus::Running => ProcessSnapshotStatus::Running,
                ProcessStatus::Stopped => ProcessSnapshotStatus::Stopped,
                ProcessStatus::Exited => ProcessSnapshotStatus::Exited,
            }
        },
        exit_code: exit_code.or(info.exit_code),
    }
}

fn collect_process_snapshot_entries(
    process_id: &str,
    process: &ActiveProcess,
    process_table: &BTreeMap<u32, secure_exec_kernel::process_table::ProcessInfo>,
    entries: &mut Vec<ProcessSnapshotEntry>,
) {
    if let Some(info) = process_table.get(&process.kernel_pid) {
        entries.push(build_process_snapshot_entry(
            process_id, process, info, None,
        ));
    }

    for (child_id, child) in &process.child_processes {
        let child_process_id = format!("{process_id}/{child_id}");
        collect_process_snapshot_entries(&child_process_id, child, process_table, entries);
    }
}

fn find_kernel_socket_state_entry(
    kernel: &SidecarKernel,
    process_id: &str,
    process: &ActiveProcess,
    kind: SocketQueryKind,
    request: &FindListenerRequest,
) -> Result<Option<SocketStateEntry>, SidecarError> {
    let entry = match kind {
        SocketQueryKind::TcpListener => process
            .tcp_listeners
            .values()
            .filter_map(|listener| listener.kernel_socket_id)
            .find_map(|socket_id| {
                kernel_socket_state_entry(kernel, process_id, socket_id, kind, request)
            }),
        SocketQueryKind::UdpBound => process
            .udp_sockets
            .values()
            .filter_map(|socket| socket.kernel_socket_id)
            .find_map(|socket_id| {
                kernel_socket_state_entry(kernel, process_id, socket_id, kind, request)
            }),
    };

    if entry.is_some() {
        return Ok(entry);
    }

    for child in process.child_processes.values() {
        if let Some(entry) =
            find_kernel_socket_state_entry(kernel, process_id, child, kind, request)?
        {
            return Ok(Some(entry));
        }
    }

    Ok(None)
}

fn kernel_socket_state_entry(
    kernel: &SidecarKernel,
    process_id: &str,
    socket_id: SocketId,
    kind: SocketQueryKind,
    request: &FindListenerRequest,
) -> Option<SocketStateEntry> {
    let record = kernel.socket_get(socket_id)?;
    let local_address = record.local_address()?;
    match kind {
        SocketQueryKind::TcpListener if record.state() == SocketState::Listening => {}
        SocketQueryKind::TcpListener => return None,
        SocketQueryKind::UdpBound => {}
    }

    if !socket_host_matches(request.host.as_deref(), local_address.host()) {
        return None;
    }
    if request
        .port
        .is_some_and(|port| local_address.port() != port)
    {
        return None;
    }

    Some(SocketStateEntry {
        process_id: process_id.to_owned(),
        host: Some(local_address.host().to_owned()),
        port: Some(local_address.port()),
        path: None,
    })
}

fn socket_inodes_for_pid(pid: u32) -> Result<BTreeSet<u64>, SidecarError> {
    let fd_dir = PathBuf::from(format!("/proc/{pid}/fd"));
    let entries = match fs::read_dir(&fd_dir) {
        Ok(entries) => entries,
        Err(error) if error.kind() == std::io::ErrorKind::NotFound => return Ok(BTreeSet::new()),
        Err(error) => {
            return Err(SidecarError::Io(format!(
                "failed to read socket descriptors for process {pid}: {error}"
            )));
        }
    };

    let mut inodes = BTreeSet::new();
    for entry in entries {
        let entry = entry.map_err(|error| {
            SidecarError::Io(format!(
                "failed to inspect fd entry for process {pid}: {error}"
            ))
        })?;
        let target = match fs::read_link(entry.path()) {
            Ok(target) => target,
            Err(_) => continue,
        };
        if let Some(inode) = parse_socket_inode(&target) {
            inodes.insert(inode);
        }
    }

    Ok(inodes)
}

fn parse_socket_inode(target: &Path) -> Option<u64> {
    let value = target.to_string_lossy();
    let trimmed = value.strip_prefix("socket:[")?.strip_suffix(']')?;
    trimmed.parse().ok()
}

fn unix_socket_path(addr: &UnixSocketAddr) -> Option<String> {
    addr.as_pathname()
        .map(|path| path.to_string_lossy().into_owned())
}

fn find_unix_socket_for_pid(
    pid: u32,
    inodes: &BTreeSet<u64>,
    path: &str,
    process_id: &str,
) -> Result<Option<SocketStateEntry>, SidecarError> {
    let table_path = format!("/proc/{pid}/net/unix");
    let contents = match fs::read_to_string(&table_path) {
        Ok(contents) => contents,
        Err(error) if error.kind() == std::io::ErrorKind::NotFound => return Ok(None),
        Err(error) => {
            return Err(SidecarError::Io(format!(
                "failed to inspect unix sockets for process {pid}: {error}"
            )));
        }
    };

    for line in contents.lines().skip(1) {
        let columns = line.split_whitespace().collect::<Vec<_>>();
        if columns.len() < 8 {
            continue;
        }
        let Ok(inode) = columns[6].parse::<u64>() else {
            continue;
        };
        if !inodes.contains(&inode) || columns[7] != path {
            continue;
        }
        return Ok(Some(SocketStateEntry {
            process_id: process_id.to_owned(),
            host: None,
            port: None,
            path: Some(path.to_owned()),
        }));
    }

    Ok(None)
}

fn find_inet_socket_for_pid(
    table_path: &str,
    inodes: &BTreeSet<u64>,
    kind: SocketQueryKind,
    requested_host: Option<&str>,
    requested_port: Option<u16>,
    process_id: &str,
) -> Result<Option<SocketStateEntry>, SidecarError> {
    for entry in parse_proc_net_entries(table_path)? {
        if !inodes.contains(&entry.inode) {
            continue;
        }
        if matches!(kind, SocketQueryKind::TcpListener) && entry.state != "0A" {
            continue;
        }
        if !socket_host_matches(requested_host, &entry.local_host) {
            continue;
        }
        if let Some(port) = requested_port {
            if entry.local_port != port {
                continue;
            }
        }
        return Ok(Some(SocketStateEntry {
            process_id: process_id.to_owned(),
            host: Some(entry.local_host),
            port: Some(entry.local_port),
            path: None,
        }));
    }

    Ok(None)
}

fn is_unspecified_socket_host(host: &str) -> bool {
    host == "0.0.0.0" || host == "::"
}

fn is_loopback_socket_host(host: &str) -> bool {
    host == "127.0.0.1" || host == "::1" || host.eq_ignore_ascii_case("localhost")
}

pub(crate) fn vm_network_resource_counts(vm: &VmState) -> NetworkResourceCounts {
    let snapshot = vm.kernel.resource_snapshot();
    let mut counts = NetworkResourceCounts {
        sockets: snapshot.sockets,
        connections: snapshot.socket_connections,
    };
    for process in vm.active_processes.values() {
        let process_counts = process.sidecar_only_network_resource_counts();
        counts.sockets += process_counts.sockets;
        counts.connections += process_counts.connections;
    }
    counts
}

fn collect_javascript_socket_port_state(
    kernel: &SidecarKernel,
    process_id: &str,
    process: &ActiveProcess,
    tcp_guest_to_host: &mut BTreeMap<(JavascriptSocketFamily, u16), u16>,
    http_loopback_targets: &mut BTreeMap<
        (JavascriptSocketFamily, u16),
        JavascriptHttpLoopbackTarget,
    >,
    udp_guest_to_host: &mut BTreeMap<(JavascriptSocketFamily, u16), u16>,
    udp_host_to_guest: &mut BTreeMap<(JavascriptSocketFamily, u16), u16>,
    used_tcp_ports: &mut BTreeMap<JavascriptSocketFamily, BTreeSet<u16>>,
    used_udp_ports: &mut BTreeMap<JavascriptSocketFamily, BTreeSet<u16>>,
) {
    for (family, port) in process.tcp_port_reservations.values() {
        used_tcp_ports.entry(*family).or_default().insert(*port);
    }

    let mut record_tcp_listener = |guest_addr: SocketAddr, host_port: u16| {
        let family = JavascriptSocketFamily::from_ip(guest_addr.ip());
        used_tcp_ports
            .entry(family)
            .or_default()
            .insert(guest_addr.port());
        // VM-local loopback connects should also resolve listeners bound to
        // unspecified guest addresses like 0.0.0.0/::.
        tcp_guest_to_host.insert((family, guest_addr.port()), host_port);
    };

    for listener in process.tcp_listeners.values() {
        let local_addr = listener
            .kernel_socket_id
            .and_then(|socket_id| kernel.socket_get(socket_id))
            .and_then(|record| record.local_address().cloned())
            .and_then(|address| resolve_tcp_bind_addr(address.host(), address.port()).ok())
            .unwrap_or_else(|| listener.guest_local_addr());
        record_tcp_listener(local_addr, local_addr.port());
    }

    for (server_id, server) in &process.http_servers {
        let host_port = match server.listener.local_addr() {
            Ok(addr) => addr.port(),
            Err(_) => continue,
        };
        record_tcp_listener(server.guest_local_addr, host_port);
        let family = JavascriptSocketFamily::from_ip(server.guest_local_addr.ip());
        http_loopback_targets.insert(
            (family, server.guest_local_addr.port()),
            JavascriptHttpLoopbackTarget {
                process_id: process_id.to_owned(),
                server_id: *server_id,
            },
        );
    }

    if let Ok(http2) = process.http2.shared.lock() {
        for server in http2.servers.values() {
            record_tcp_listener(server.guest_local_addr, server.actual_local_addr.port());
        }
    }

    for socket in process.tcp_sockets.values() {
        let guest_addr = socket
            .kernel_socket_id
            .and_then(|socket_id| kernel.socket_get(socket_id))
            .and_then(|record| record.local_address().cloned())
            .and_then(|address| resolve_tcp_bind_addr(address.host(), address.port()).ok())
            .unwrap_or(socket.guest_local_addr);
        let family = JavascriptSocketFamily::from_ip(guest_addr.ip());
        used_tcp_ports
            .entry(family)
            .or_default()
            .insert(guest_addr.port());
    }

    for socket in process.udp_sockets.values() {
        let guest_addr = socket
            .kernel_socket_id
            .and_then(|socket_id| kernel.socket_get(socket_id))
            .and_then(|record| record.local_address().cloned())
            .and_then(|address| {
                resolve_udp_bind_addr(address.host(), address.port(), socket.family).ok()
            })
            .or_else(|| socket.local_addr());
        let Some(guest_addr) = guest_addr else {
            continue;
        };
        let family = JavascriptSocketFamily::from_ip(guest_addr.ip());
        used_udp_ports
            .entry(family)
            .or_default()
            .insert(guest_addr.port());
        if let Some(host_addr) = socket
            .socket
            .as_ref()
            .and_then(|socket| socket.local_addr().ok())
        {
            if is_loopback_ip(guest_addr.ip()) {
                udp_guest_to_host.insert((family, guest_addr.port()), host_addr.port());
                udp_host_to_guest.insert((family, host_addr.port()), guest_addr.port());
            }
        } else if socket.kernel_socket_id.is_some() && is_loopback_ip(guest_addr.ip()) {
            udp_guest_to_host.insert((family, guest_addr.port()), guest_addr.port());
            udp_host_to_guest.insert((family, guest_addr.port()), guest_addr.port());
        }
    }

    for (child_process_id, child) in &process.child_processes {
        let child_id = format!("{process_id}/{child_process_id}");
        collect_javascript_socket_port_state(
            kernel,
            &child_id,
            child,
            tcp_guest_to_host,
            http_loopback_targets,
            udp_guest_to_host,
            udp_host_to_guest,
            used_tcp_ports,
            used_udp_ports,
        );
    }
}

pub(crate) fn build_javascript_socket_path_context(
    vm: &VmState,
) -> Result<JavascriptSocketPathContext, SidecarError> {
    let mut loopback_exempt_ports = vm.create_loopback_exempt_ports.clone();
    loopback_exempt_ports.extend(vm.configuration.loopback_exempt_ports.iter().copied());
    let mut tcp_loopback_guest_to_host_ports = BTreeMap::new();
    let mut http_loopback_targets = BTreeMap::new();
    let mut udp_loopback_guest_to_host_ports = BTreeMap::new();
    let mut udp_loopback_host_to_guest_ports = BTreeMap::new();
    let mut used_tcp_guest_ports = BTreeMap::new();
    let mut used_udp_guest_ports = BTreeMap::new();
    for (process_id, process) in &vm.active_processes {
        collect_javascript_socket_port_state(
            &vm.kernel,
            process_id,
            process,
            &mut tcp_loopback_guest_to_host_ports,
            &mut http_loopback_targets,
            &mut udp_loopback_guest_to_host_ports,
            &mut udp_loopback_host_to_guest_ports,
            &mut used_tcp_guest_ports,
            &mut used_udp_guest_ports,
        );
    }
    Ok(JavascriptSocketPathContext {
        sandbox_root: vm.cwd.clone(),
        mounts: vm.configuration.mounts.clone(),
        listen_policy: vm.listen_policy,
        loopback_exempt_ports,
        tcp_loopback_guest_to_host_ports,
        http_loopback_targets,
        udp_loopback_guest_to_host_ports,
        udp_loopback_host_to_guest_ports,
        used_tcp_guest_ports,
        used_udp_guest_ports,
    })
}

fn check_network_resource_limit(
    limit: Option<usize>,
    current: usize,
    additional: usize,
    label: &str,
) -> Result<(), SidecarError> {
    if let Some(limit) = limit {
        if current.saturating_add(additional) > limit {
            return Err(SidecarError::Execution(format!(
                "EAGAIN: maximum {label} count reached"
            )));
        }
    }
    Ok(())
}

fn normalize_tcp_listen_host(
    host: Option<&str>,
) -> Result<(JavascriptSocketFamily, &'static str, &'static str), SidecarError> {
    match host.unwrap_or("127.0.0.1") {
        "127.0.0.1" | "localhost" => Ok((JavascriptSocketFamily::Ipv4, "127.0.0.1", "127.0.0.1")),
        "::1" => Ok((JavascriptSocketFamily::Ipv6, "::1", "::1")),
        "0.0.0.0" => Ok((JavascriptSocketFamily::Ipv4, "127.0.0.1", "0.0.0.0")),
        "::" => Ok((JavascriptSocketFamily::Ipv6, "::1", "::")),
        other => Err(SidecarError::Execution(format!(
            "EACCES: TCP listeners must bind to loopback or unspecified addresses, got {other}"
        ))),
    }
}

fn normalize_udp_bind_host(
    host: Option<&str>,
    family: JavascriptUdpFamily,
) -> Result<(&'static str, &'static str, JavascriptSocketFamily), SidecarError> {
    match (family, host) {
        (JavascriptUdpFamily::Ipv4, None) | (JavascriptUdpFamily::Ipv4, Some("0.0.0.0")) => {
            Ok(("127.0.0.1", "0.0.0.0", JavascriptSocketFamily::Ipv4))
        }
        (JavascriptUdpFamily::Ipv4, Some("127.0.0.1"))
        | (JavascriptUdpFamily::Ipv4, Some("localhost")) => {
            Ok(("127.0.0.1", "127.0.0.1", JavascriptSocketFamily::Ipv4))
        }
        (JavascriptUdpFamily::Ipv6, None) | (JavascriptUdpFamily::Ipv6, Some("::")) => {
            Ok(("::1", "::", JavascriptSocketFamily::Ipv6))
        }
        (JavascriptUdpFamily::Ipv6, Some("::1"))
        | (JavascriptUdpFamily::Ipv6, Some("localhost")) => {
            Ok(("::1", "::1", JavascriptSocketFamily::Ipv6))
        }
        (JavascriptUdpFamily::Ipv4, Some(other)) => Err(SidecarError::Execution(format!(
            "EACCES: udp4 sockets must bind to 127.0.0.1 or 0.0.0.0, got {other}"
        ))),
        (JavascriptUdpFamily::Ipv6, Some(other)) => Err(SidecarError::Execution(format!(
            "EACCES: udp6 sockets must bind to ::1 or ::, got {other}"
        ))),
    }
}

fn allocate_guest_listen_port(
    requested_port: u16,
    family: JavascriptSocketFamily,
    used_ports: &BTreeMap<JavascriptSocketFamily, BTreeSet<u16>>,
    policy: VmListenPolicy,
) -> Result<u16, SidecarError> {
    let is_allowed = |port: u16| {
        port >= policy.port_min
            && port <= policy.port_max
            && (policy.allow_privileged || port >= 1024)
    };
    let used = used_ports.get(&family);

    if requested_port != 0 {
        if !is_allowed(requested_port) {
            let reason = if requested_port < 1024 && !policy.allow_privileged {
                format!(
                    "EACCES: privileged listen port {requested_port} requires {}=true",
                    VM_LISTEN_ALLOW_PRIVILEGED_METADATA_KEY
                )
            } else {
                format!(
                    "EACCES: listen port {requested_port} is outside the allowed range {}-{}",
                    policy.port_min, policy.port_max
                )
            };
            return Err(SidecarError::Execution(reason));
        }
        if used.is_some_and(|ports| ports.contains(&requested_port)) {
            return Err(sidecar_net_error(std::io::Error::from_raw_os_error(
                libc::EADDRINUSE,
            )));
        }
        return Ok(requested_port);
    }

    let allocation_start = policy
        .port_min
        .max(if policy.allow_privileged { 1 } else { 1024 });
    for candidate in allocation_start..=policy.port_max {
        if used.is_some_and(|ports| ports.contains(&candidate)) {
            continue;
        }
        return Ok(candidate);
    }

    Err(sidecar_net_error(std::io::Error::from_raw_os_error(
        libc::EADDRINUSE,
    )))
}

fn socket_host_matches(requested: Option<&str>, actual: &str) -> bool {
    match requested {
        None => true,
        Some(requested) if requested == actual => true,
        Some(requested)
            if is_unspecified_socket_host(requested) && is_unspecified_socket_host(actual) =>
        {
            true
        }
        Some(requested) if is_unspecified_socket_host(requested) => is_loopback_socket_host(actual),
        Some(requested) if requested.eq_ignore_ascii_case("localhost") => {
            is_loopback_socket_host(actual)
        }
        _ => false,
    }
}

fn parse_proc_net_entries(table_path: &str) -> Result<Vec<ProcNetEntry>, SidecarError> {
    let contents = match fs::read_to_string(table_path) {
        Ok(contents) => contents,
        Err(error) if error.kind() == std::io::ErrorKind::NotFound => return Ok(Vec::new()),
        Err(error) => {
            return Err(SidecarError::Io(format!(
                "failed to inspect socket table {table_path}: {error}"
            )));
        }
    };

    let mut entries = Vec::new();
    for line in contents.lines().skip(1) {
        let columns = line.split_whitespace().collect::<Vec<_>>();
        if columns.len() < 10 {
            continue;
        }
        let Some((host, port)) = parse_proc_ip_port(columns[1]) else {
            continue;
        };
        let Ok(inode) = columns[9].parse::<u64>() else {
            continue;
        };
        entries.push(ProcNetEntry {
            local_host: host,
            local_port: port,
            state: columns[3].to_owned(),
            inode,
        });
    }

    Ok(entries)
}

fn parse_proc_ip_port(value: &str) -> Option<(String, u16)> {
    let (raw_ip, raw_port) = value.split_once(':')?;
    let port = u16::from_str_radix(raw_port, 16).ok()?;
    let host = match raw_ip.len() {
        8 => {
            let raw = u32::from_str_radix(raw_ip, 16).ok()?;
            Ipv4Addr::from(raw.to_le_bytes()).to_string()
        }
        32 => {
            let mut bytes = [0_u8; 16];
            for (index, chunk) in raw_ip.as_bytes().chunks(8).enumerate() {
                let word = u32::from_str_radix(std::str::from_utf8(chunk).ok()?, 16).ok()?;
                bytes[index * 4..(index + 1) * 4].copy_from_slice(&word.to_le_bytes());
            }
            Ipv6Addr::from(bytes).to_string()
        }
        _ => return None,
    };
    Some((host, port))
}

fn python_file_entrypoint(entrypoint: &str) -> Option<PathBuf> {
    let path = Path::new(entrypoint);
    (path.extension().and_then(|extension| extension.to_str()) == Some("py"))
        .then(|| path.to_path_buf())
}

fn add_runtime_guest_path_mapping(
    env: &mut BTreeMap<String, String>,
    guest_path: &str,
    host_path: &Path,
) {
    let mut mappings = env
        .get("AGENTOS_GUEST_PATH_MAPPINGS")
        .and_then(|value| serde_json::from_str::<Vec<Value>>(value).ok())
        .unwrap_or_default();
    mappings.retain(|mapping| {
        mapping
            .get("guestPath")
            .and_then(Value::as_str)
            .map(|existing| normalize_path(existing) != normalize_path(guest_path))
            .unwrap_or(true)
    });
    mappings.push(json!({
        "guestPath": normalize_path(guest_path),
        "hostPath": host_path.display().to_string(),
    }));
    if let Ok(serialized) = serde_json::to_string(&mappings) {
        env.insert(String::from("AGENTOS_GUEST_PATH_MAPPINGS"), serialized);
    }
}

fn add_runtime_host_access_path(
    env: &mut BTreeMap<String, String>,
    key: &str,
    host_path: &Path,
    expand: bool,
) {
    let existing = env
        .get(key)
        .and_then(|value| serde_json::from_str::<Vec<String>>(value).ok())
        .unwrap_or_default()
        .into_iter()
        .map(PathBuf::from)
        .collect::<Vec<_>>();
    let mut paths = existing;
    paths.push(host_path.to_path_buf());
    let normalized = if expand {
        expand_host_access_paths(&paths)
    } else {
        dedupe_host_paths(&paths)
    };
    let serialized = normalized
        .iter()
        .map(|path| path.to_string_lossy().into_owned())
        .collect::<Vec<_>>();
    if let Ok(serialized) = serde_json::to_string(&serialized) {
        env.insert(key.to_owned(), serialized);
    }
}

// discover_command_guest_paths moved to crate::bootstrap

fn is_path_like_specifier(specifier: &str) -> bool {
    specifier.starts_with('/')
        || specifier.starts_with("./")
        || specifier.starts_with("../")
        || specifier.starts_with("file:")
}

fn execution_wasm_permission_tier(tier: WasmPermissionTier) -> ExecutionWasmPermissionTier {
    match tier {
        WasmPermissionTier::Full => ExecutionWasmPermissionTier::Full,
        WasmPermissionTier::ReadWrite => ExecutionWasmPermissionTier::ReadWrite,
        WasmPermissionTier::ReadOnly => ExecutionWasmPermissionTier::ReadOnly,
        WasmPermissionTier::Isolated => ExecutionWasmPermissionTier::Isolated,
    }
}

fn resolve_wasm_permission_tier(
    vm: &VmState,
    command_name: Option<&str>,
    explicit_tier: Option<WasmPermissionTier>,
    entrypoint: &str,
) -> WasmPermissionTier {
    explicit_tier
        .or_else(|| command_name.and_then(|command| vm.command_permissions.get(command).copied()))
        .or_else(|| {
            Path::new(entrypoint)
                .file_name()
                .and_then(|name| name.to_str())
                .and_then(|command| vm.command_permissions.get(command).copied())
        })
        .unwrap_or(WasmPermissionTier::Full)
}

fn tokenize_shell_free_command(command: &str) -> Vec<String> {
    command
        .split_whitespace()
        .filter(|segment| !segment.is_empty())
        .map(str::to_owned)
        .collect()
}

fn is_posix_shell_builtin(command: &str) -> bool {
    matches!(
        command,
        "." | ":"
            | "break"
            | "cd"
            | "continue"
            | "eval"
            | "exec"
            | "exit"
            | "export"
            | "readonly"
            | "return"
            | "set"
            | "shift"
            | "times"
            | "trap"
            | "umask"
            | "unset"
    )
}

/// Single-token checks for shell-mode commands whose first word forces a real
/// shell even when the command string has no shell metacharacters. This is not
/// a parser: env-assignment prefixes (`FOO=bar cmd`) and shell reserved words
/// have no meaning outside `sh`, so whitespace-tokenizing them would silently
/// run the wrong program.
fn shell_first_token_requires_shell(token: &str) -> bool {
    token.contains('=') || is_shell_reserved_word(token)
}

fn is_shell_reserved_word(token: &str) -> bool {
    matches!(
        token,
        "if" | "then"
            | "elif"
            | "else"
            | "fi"
            | "for"
            | "in"
            | "do"
            | "done"
            | "while"
            | "until"
            | "case"
            | "esac"
            | "{"
            | "}"
            | "!"
    )
}

fn command_requires_shell(command: &str) -> bool {
    command.chars().any(|ch| {
        matches!(
            ch,
            '|' | '&'
                | ';'
                | '<'
                | '>'
                | '('
                | ')'
                | '$'
                | '`'
                | '*'
                | '?'
                | '['
                | ']'
                | '{'
                | '}'
                | '~'
                | '\''
                | '"'
                | '\\'
                | '\n'
        )
    })
}

fn host_mount_path_for_guest_path(vm: &VmState, guest_path: &str) -> Option<PathBuf> {
    let normalized = normalize_path(guest_path);

    let mut mounts = vm
        .configuration
        .mounts
        .iter()
        .filter_map(|mount| {
            ((mount.plugin.id == "host_dir") || (mount.plugin.id == "module_access"))
                .then(|| {
                    mount_config_host_path(&mount.plugin.config)
                        .map(|host_path| (mount.guest_path.as_str(), host_path))
                })
                .flatten()
        })
        .collect::<Vec<_>>();
    mounts.sort_by_key(|mount| std::cmp::Reverse(mount.0.len()));

    for (guest_root, host_root) in mounts {
        if normalized != guest_root && !normalized.starts_with(&format!("{guest_root}/")) {
            continue;
        }

        let suffix = normalized
            .strip_prefix(guest_root)
            .unwrap_or_default()
            .trim_start_matches('/');
        let mut path = PathBuf::from(host_root);
        if !suffix.is_empty() {
            path.push(suffix);
        }
        return Some(path);
    }

    None
}

fn host_runtime_path_for_guest_path_with_env(
    vm: &VmState,
    runtime_env: &BTreeMap<String, String>,
    guest_path: &str,
    default_host_cwd: &Path,
) -> Option<PathBuf> {
    if let Some(path) = host_mount_path_for_guest_path(vm, guest_path) {
        return Some(path);
    }
    if let Some(path) = host_path_from_runtime_guest_mappings(runtime_env, guest_path) {
        return Some(path);
    }

    let normalized = normalize_path(guest_path);
    let virtual_home = guest_virtual_home(vm);

    if normalized == virtual_home || normalized.starts_with(&format!("{virtual_home}/")) {
        let suffix = normalized
            .strip_prefix(&virtual_home)
            .unwrap_or_default()
            .trim_start_matches('/');
        let mut host_path = default_host_cwd.to_path_buf();
        if !suffix.is_empty() {
            host_path.push(suffix);
        }
        return Some(host_path);
    }

    None
}

#[derive(Deserialize, Serialize)]
struct RuntimeGuestPathMapping {
    #[serde(rename = "guestPath")]
    guest_path: String,
    #[serde(rename = "hostPath")]
    host_path: String,
    #[serde(rename = "readOnly", default)]
    read_only: bool,
}

pub(crate) fn host_path_from_runtime_guest_mappings(
    runtime_env: &BTreeMap<String, String>,
    guest_path: &str,
) -> Option<PathBuf> {
    let mappings = runtime_env
        .get("AGENTOS_GUEST_PATH_MAPPINGS")
        .and_then(|value| serde_json::from_str::<Vec<RuntimeGuestPathMapping>>(value).ok())?;
    let normalized = normalize_path(guest_path);

    let mut sorted_mappings = mappings
        .into_iter()
        .filter_map(|mapping| {
            (!mapping.guest_path.is_empty() && !mapping.host_path.is_empty()).then_some((
                normalize_path(&mapping.guest_path),
                PathBuf::from(mapping.host_path),
            ))
        })
        .collect::<Vec<_>>();
    sorted_mappings.sort_by_key(|mapping| std::cmp::Reverse(mapping.0.len()));

    for (guest_root, mut host_root) in sorted_mappings {
        if guest_root != "/"
            && normalized != guest_root
            && !normalized.starts_with(&format!("{guest_root}/"))
        {
            continue;
        }
        if guest_root == "/" && !normalized.starts_with('/') {
            continue;
        }

        if host_root.is_relative() {
            host_root = std::env::current_dir().ok()?.join(host_root);
        }

        let suffix = if guest_root == "/" {
            normalized.trim_start_matches('/')
        } else {
            normalized
                .strip_prefix(&guest_root)
                .unwrap_or_default()
                .trim_start_matches('/')
        };
        if !suffix.is_empty() {
            host_root.push(suffix);
        }
        return Some(host_root);
    }

    None
}

fn guest_runtime_path_for_host_path(
    runtime_env: &BTreeMap<String, String>,
    virtual_home: &str,
    cwd: &Path,
    host_path: &str,
) -> Option<String> {
    let resolved = if host_path.starts_with("file://") {
        PathBuf::from(host_path.trim_start_matches("file://"))
    } else if host_path.starts_with("file:") {
        PathBuf::from(host_path.trim_start_matches("file:"))
    } else {
        let candidate = PathBuf::from(host_path);
        if candidate.is_absolute() {
            candidate
        } else if host_path.starts_with("./") || host_path.starts_with("../") {
            cwd.join(candidate)
        } else {
            return None;
        }
    };
    let normalized = normalize_host_path(&resolved);

    if let Some(path) = guest_path_from_runtime_host_mappings(runtime_env, &normalized) {
        return Some(path);
    }

    let normalized_cwd = normalize_host_path(cwd);
    if !path_is_within_root(&normalized, &normalized_cwd) {
        return None;
    }

    let virtual_home = if virtual_home.starts_with('/') {
        virtual_home.to_string()
    } else {
        String::from("/root")
    };
    let suffix = normalized
        .strip_prefix(&normalized_cwd)
        .ok()?
        .to_string_lossy()
        .replace('\\', "/")
        .trim_start_matches('/')
        .to_owned();

    Some(if suffix.is_empty() {
        virtual_home
    } else {
        normalize_path(&format!("{virtual_home}/{suffix}"))
    })
}

fn guest_path_from_runtime_host_mappings(
    runtime_env: &BTreeMap<String, String>,
    host_path: &Path,
) -> Option<String> {
    let mappings = runtime_env
        .get("AGENTOS_GUEST_PATH_MAPPINGS")
        .and_then(|value| serde_json::from_str::<Vec<RuntimeGuestPathMapping>>(value).ok())?;
    let normalized = normalize_host_path(host_path);

    let mut sorted_mappings = mappings
        .into_iter()
        .filter_map(|mapping| {
            (!mapping.guest_path.is_empty() && !mapping.host_path.is_empty()).then_some((
                normalize_path(&mapping.guest_path),
                normalize_host_path(Path::new(&mapping.host_path)),
            ))
        })
        .collect::<Vec<_>>();
    sorted_mappings.sort_by_key(|mapping| std::cmp::Reverse(mapping.1.as_os_str().len()));

    for (guest_root, host_root) in sorted_mappings {
        if !path_is_within_root(&normalized, &host_root) {
            continue;
        }
        let suffix = normalized
            .strip_prefix(&host_root)
            .ok()?
            .to_string_lossy()
            .replace('\\', "/")
            .trim_start_matches('/')
            .to_owned();

        return Some(if suffix.is_empty() {
            guest_root
        } else if guest_root == "/" {
            normalize_path(&format!("/{suffix}"))
        } else {
            normalize_path(&format!("{guest_root}/{suffix}"))
        });
    }

    None
}

fn host_mount_path_for_guest_path_from_mounts(
    mounts: &[crate::protocol::MountDescriptor],
    guest_path: &str,
) -> Option<PathBuf> {
    let normalized = normalize_path(guest_path);

    let mut host_mounts = mounts
        .iter()
        .filter_map(|mount| {
            ((mount.plugin.id == "host_dir") || (mount.plugin.id == "module_access"))
                .then(|| {
                    mount_config_host_path(&mount.plugin.config)
                        .map(|host_path| (mount.guest_path.as_str(), host_path))
                })
                .flatten()
        })
        .collect::<Vec<_>>();
    host_mounts.sort_by_key(|mount| std::cmp::Reverse(mount.0.len()));

    for (guest_root, host_root) in host_mounts {
        if normalized != guest_root && !normalized.starts_with(&format!("{guest_root}/")) {
            continue;
        }

        let suffix = normalized
            .strip_prefix(guest_root)
            .unwrap_or_default()
            .trim_start_matches('/');
        let mut path = PathBuf::from(host_root);
        if !suffix.is_empty() {
            path.push(suffix);
        }
        return Some(path);
    }

    None
}

#[cfg(test)]
mod host_mount_path_for_guest_path_from_mounts_tests {
    use super::host_mount_path_for_guest_path_from_mounts;
    use crate::protocol::{MountDescriptor, MountPluginDescriptor};
    use serde_json::json;
    use std::path::PathBuf;

    #[test]
    fn resolves_module_access_mount_paths() {
        let mounts = vec![MountDescriptor {
            guest_path: String::from("/root/node_modules"),
            read_only: true,
            plugin: MountPluginDescriptor {
                id: String::from("module_access"),
                config: json!({
                    "hostPath": "/tmp/workspace/node_modules",
                })
                .to_string(),
            },
        }];

        let resolved =
            host_mount_path_for_guest_path_from_mounts(&mounts, "/root/node_modules/pkg/index.js")
                .expect("module_access mount should resolve");

        assert_eq!(
            resolved,
            PathBuf::from("/tmp/workspace/node_modules/pkg/index.js")
        );
    }
}

fn resolve_guest_socket_host_path(
    context: &JavascriptSocketPathContext,
    guest_path: &str,
) -> PathBuf {
    if let Some(path) = host_mount_path_for_guest_path_from_mounts(&context.mounts, guest_path) {
        return path;
    }

    let normalized = normalize_path(guest_path);
    let mut host_path = context.sandbox_root.clone();
    let suffix = normalized.trim_start_matches('/');
    if !suffix.is_empty() {
        host_path.push(suffix);
    }
    host_path
}

fn ensure_kernel_parent_directories(
    kernel: &mut SidecarKernel,
    path: &str,
) -> Result<(), SidecarError> {
    let parent = dirname(path);
    if parent != "/" && !kernel.exists(&parent).map_err(kernel_error)? {
        kernel.mkdir(&parent, true).map_err(kernel_error)?;
    }
    Ok(())
}

// JavascriptChildProcessSpawnOptions, JavascriptChildProcessSpawnRequest moved to crate::protocol
// ResolvedChildProcessExecution moved to crate::state

pub(crate) fn sanitize_javascript_child_process_internal_bootstrap_env(
    env: &BTreeMap<String, String>,
) -> BTreeMap<String, String> {
    const ALLOWED_KEYS: &[&str] = &[
        "AGENTOS_ALLOWED_NODE_BUILTINS",
        "AGENTOS_GUEST_PATH_MAPPINGS",
        "AGENTOS_LOOPBACK_EXEMPT_PORTS",
        "AGENTOS_VIRTUAL_PROCESS_EXEC_PATH",
        "AGENTOS_VIRTUAL_PROCESS_UID",
        "AGENTOS_VIRTUAL_PROCESS_GID",
        "AGENTOS_VIRTUAL_PROCESS_VERSION",
    ];

    env.iter()
        .filter(|(key, _)| {
            ALLOWED_KEYS.contains(&key.as_str()) || key.starts_with("AGENTOS_VIRTUAL_OS_")
        })
        .map(|(key, value)| (key.clone(), value.clone()))
        .collect()
}

// Network request types moved to crate::protocol

// VmDnsConfig, DnsResolutionSource moved to crate::state

fn resolve_tcp_bind_addr(host: &str, port: u16) -> Result<SocketAddr, SidecarError> {
    (host, port)
        .to_socket_addrs()
        .map_err(sidecar_net_error)?
        .next()
        .ok_or_else(|| {
            SidecarError::Execution(format!("failed to resolve TCP bind address {host}:{port}"))
        })
}

pub(crate) fn format_dns_resource(hostname: &str) -> String {
    format!("dns://{hostname}")
}

pub(crate) fn format_tcp_resource(host: &str, port: u16) -> String {
    format!("tcp://{host}:{port}")
}

fn is_loopback_ip(ip: IpAddr) -> bool {
    match ip {
        IpAddr::V4(ip) => ip.is_loopback(),
        IpAddr::V6(ip) => {
            ip.is_loopback()
                || ip
                    .to_ipv4_mapped()
                    .is_some_and(|mapped| mapped.is_loopback())
        }
    }
}

fn loopback_cidr(ip: IpAddr) -> &'static str {
    match ip {
        IpAddr::V4(ip) if ip.is_loopback() => "127.0.0.0/8",
        IpAddr::V6(ip)
            if ip
                .to_ipv4_mapped()
                .is_some_and(|mapped| mapped.is_loopback()) =>
        {
            "127.0.0.0/8"
        }
        IpAddr::V6(_) => "::1/128",
        IpAddr::V4(_) => "127.0.0.0/8",
    }
}

/// Returns the embedded IPv4 address of an IPv4-compatible IPv6 address
/// (`::a.b.c.d`): the first six 16-bit segments are zero and the final 32 bits
/// hold the IPv4 address. The all-zero (`::`) and loopback (`::1`) addresses are
/// deliberately excluded so they are handled by the unspecified/loopback paths
/// rather than treated as IPv4-compatible.
fn ipv4_compatible_embedded(ip: Ipv6Addr) -> Option<Ipv4Addr> {
    let segments = ip.segments();
    if segments[0..6].iter().any(|&s| s != 0) {
        return None;
    }
    let embedded = (u32::from(segments[6]) << 16) | u32::from(segments[7]);
    // Skip :: (0.0.0.0) and ::1 (0.0.0.1) — these are the IPv6 unspecified /
    // loopback addresses, not IPv4-compatible representations of an IPv4 host.
    if embedded == 0 || embedded == 1 {
        return None;
    }
    Some(Ipv4Addr::from(embedded))
}

fn restricted_non_loopback_ip_range(ip: IpAddr) -> Option<(&'static str, &'static str)> {
    match ip {
        IpAddr::V4(ip) => {
            if ip.is_unspecified() {
                // 0.0.0.0 is unspecified; the host stack routes a connect() to
                // it back to 127.0.0.1, so it must not bypass the loopback gate.
                return Some(("0.0.0.0/32", "unspecified"));
            }
            let [first, second, ..] = ip.octets();
            match (first, second) {
                (10, _) => Some(("10.0.0.0/8", "private")),
                (100, 64..=127) => Some(("100.64.0.0/10", "carrier-grade-nat")),
                (172, 16..=31) => Some(("172.16.0.0/12", "private")),
                (192, 168) => Some(("192.168.0.0/16", "private")),
                (169, 254) => Some(("169.254.0.0/16", "link-local")),
                // 224.0.0.0/4 is the IPv4 multicast range and 240.0.0.0/4 is
                // reserved/future-use (255.255.255.255 broadcast falls in it).
                // Neither is a legitimate unicast egress target, so a guest
                // connect to them must be denied rather than attempted.
                (224..=239, _) => Some(("224.0.0.0/4", "multicast")),
                (240..=255, _) => Some(("240.0.0.0/4", "reserved")),
                _ => None,
            }
        }
        IpAddr::V6(ip) => {
            if let Some(mapped) = ip.to_ipv4_mapped() {
                return restricted_non_loopback_ip_range(IpAddr::V4(mapped));
            }
            // IPv4-compatible IPv6 (::a.b.c.d): the first six segments are zero
            // and the last two carry an embedded IPv4 address. `to_ipv4_mapped`
            // returns None for this form, so without canonicalizing it here a
            // guest could spell a restricted IPv4 target (e.g. cloud-metadata
            // ::169.254.169.254) and bypass the IPv4 classifier. `::`/`::1` are
            // excluded so they fall through to the unspecified/loopback paths.
            if let Some(compat) = ipv4_compatible_embedded(ip) {
                return restricted_non_loopback_ip_range(IpAddr::V4(compat));
            }

            if ip.is_unspecified() {
                // :: is the IPv6 unspecified address; same routing hazard as
                // 0.0.0.0, so deny it rather than letting it reach the host.
                return Some(("::/128", "unspecified"));
            }

            let segments = ip.segments();
            if (segments[0] & 0xfe00) == 0xfc00 {
                return Some(("fc00::/7", "unique-local"));
            }
            if (segments[0] & 0xffc0) == 0xfe80 {
                return Some(("fe80::/10", "link-local"));
            }
            None
        }
    }
}

fn blocked_dns_resolution_error(
    resource: &str,
    ip: IpAddr,
    cidr: &str,
    label: &str,
) -> SidecarError {
    SidecarError::Execution(format!(
        "EACCES: blocked outbound network access to {resource}: {ip} is within restricted {label} range {cidr}"
    ))
}

fn blocked_loopback_connect_error(resource: &str, ip: IpAddr, port: u16) -> SidecarError {
    SidecarError::Execution(format!(
        "EACCES: blocked outbound network access to {resource}: {ip} is loopback ({}) and port {port} is not owned by this VM and is not listed in {LOOPBACK_EXEMPT_PORTS_ENV}",
        loopback_cidr(ip)
    ))
}

fn filter_dns_safe_ip_addrs(
    addresses: Vec<IpAddr>,
    hostname: &str,
) -> Result<Vec<IpAddr>, SidecarError> {
    let resource = format_dns_resource(hostname);
    let mut allowed = Vec::new();
    let mut blocked = None;

    for ip in addresses {
        if let Some((cidr, label)) = restricted_non_loopback_ip_range(ip) {
            blocked.get_or_insert((ip, cidr, label));
            continue;
        }
        allowed.push(ip);
    }

    if allowed.is_empty() {
        let (ip, cidr, label) = blocked.expect("blocked DNS results should capture a reason");
        return Err(blocked_dns_resolution_error(&resource, ip, cidr, label));
    }

    Ok(allowed)
}

fn loopback_connect_allowed(context: &JavascriptSocketPathContext, port: u16) -> bool {
    context.loopback_port_allowed(port)
}

fn filter_tcp_connect_ip_addrs(
    addresses: Vec<IpAddr>,
    host: &str,
    port: u16,
    context: &JavascriptSocketPathContext,
) -> Result<Vec<IpAddr>, SidecarError> {
    let resource = format_tcp_resource(host, port);
    let mut allowed = Vec::new();
    let mut blocked = None;

    for ip in addresses {
        if let Some((cidr, label)) = restricted_non_loopback_ip_range(ip) {
            blocked.get_or_insert_with(|| blocked_dns_resolution_error(&resource, ip, cidr, label));
            continue;
        }
        if is_loopback_ip(ip) && !loopback_connect_allowed(context, port) {
            blocked.get_or_insert_with(|| blocked_loopback_connect_error(&resource, ip, port));
            continue;
        }
        allowed.push(ip);
    }

    if allowed.is_empty() {
        return Err(blocked.expect("blocked TCP connect results should capture a reason"));
    }

    Ok(allowed)
}

fn resolve_tcp_connect_addr<B>(
    bridge: &SharedBridge<B>,
    kernel: &SidecarKernel,
    vm_id: &str,
    dns: &VmDnsConfig,
    host: &str,
    port: u16,
    context: &JavascriptSocketPathContext,
) -> Result<ResolvedTcpConnectAddr, SidecarError>
where
    B: NativeSidecarBridge + Send + 'static,
    BridgeError<B>: fmt::Debug + Send + Sync + 'static,
{
    let allowed = filter_tcp_connect_ip_addrs(
        resolve_dns_ip_addrs(
            bridge,
            kernel,
            vm_id,
            dns,
            host,
            DnsLookupPolicy::SkipPermissions,
        )?,
        host,
        port,
        context,
    )?;
    let ip = allowed
        .iter()
        .copied()
        .find(|candidate| {
            let family = JavascriptSocketFamily::from_ip(*candidate);
            context.translate_tcp_loopback_port(family, port).is_some()
        })
        // We do not implement Happy Eyeballs yet, so prefer IPv4 over a
        // verbatim IPv6-first DNS answer for general outbound TCP connects.
        .or_else(|| allowed.iter().copied().find(IpAddr::is_ipv4))
        .or_else(|| allowed.first().copied())
        .ok_or_else(|| {
            SidecarError::Execution(format!("failed to resolve TCP address {host}:{port}"))
        })?;
    let family = JavascriptSocketFamily::from_ip(ip);
    let translated_loopback_port = context.translate_tcp_loopback_port(family, port);
    let use_kernel_loopback = is_loopback_ip(ip) && translated_loopback_port == Some(port);
    let actual_port = if is_loopback_ip(ip) {
        translated_loopback_port.unwrap_or(port)
    } else {
        port
    };
    Ok(ResolvedTcpConnectAddr {
        actual_addr: SocketAddr::new(ip, actual_port),
        guest_remote_addr: SocketAddr::new(ip, port),
        use_kernel_loopback,
    })
}

fn resolve_dns_ip_addrs<B>(
    bridge: &SharedBridge<B>,
    kernel: &SidecarKernel,
    vm_id: &str,
    dns: &VmDnsConfig,
    hostname: &str,
    policy: DnsLookupPolicy,
) -> Result<Vec<IpAddr>, SidecarError>
where
    B: NativeSidecarBridge + Send + 'static,
    BridgeError<B>: fmt::Debug + Send + Sync + 'static,
{
    let resolution = match kernel.resolve_dns(hostname, policy) {
        Ok(resolution) => resolution,
        Err(error) => {
            let sidecar_error = kernel_error(error.clone());
            if error.code() != "EACCES" {
                emit_dns_resolution_failure_event(bridge, vm_id, hostname, dns, &sidecar_error);
            }
            return Err(sidecar_error);
        }
    };
    emit_dns_resolution_event(
        bridge,
        vm_id,
        hostname,
        resolution.source(),
        resolution.addresses(),
        dns,
    );
    Ok(resolution.addresses().to_vec())
}

fn resolve_dns_records<B>(
    bridge: &SharedBridge<B>,
    kernel: &SidecarKernel,
    vm_id: &str,
    dns: &VmDnsConfig,
    hostname: &str,
    record_type: RecordType,
    policy: DnsLookupPolicy,
) -> Result<DnsRecordResolution, SidecarError>
where
    B: NativeSidecarBridge + Send + 'static,
    BridgeError<B>: fmt::Debug + Send + Sync + 'static,
{
    let resolution = match kernel.resolve_dns_records(hostname, record_type, policy) {
        Ok(resolution) => resolution,
        Err(error) => {
            let sidecar_error = kernel_error(error.clone());
            if error.code() != "EACCES" {
                emit_dns_resolution_failure_event(bridge, vm_id, hostname, dns, &sidecar_error);
            }
            return Err(sidecar_error);
        }
    };
    emit_dns_record_resolution_event(bridge, vm_id, hostname, &resolution, dns);
    Ok(resolution)
}

fn filter_dns_ip_addrs(
    addresses: Vec<IpAddr>,
    family: Option<u8>,
) -> Result<Vec<IpAddr>, SidecarError> {
    let filtered: Vec<_> = match family.unwrap_or(0) {
        0 => addresses,
        4 => addresses
            .into_iter()
            .filter(|ip| matches!(ip, IpAddr::V4(_)))
            .collect(),
        6 => addresses
            .into_iter()
            .filter(|ip| matches!(ip, IpAddr::V6(_)))
            .collect(),
        other => {
            return Err(SidecarError::InvalidState(format!(
                "unsupported dns family {other}"
            )));
        }
    };

    if filtered.is_empty() {
        return Err(SidecarError::Execution(String::from(
            "failed to resolve DNS address for requested family",
        )));
    }

    Ok(filtered)
}

fn resolve_udp_bind_addr(
    host: &str,
    port: u16,
    family: JavascriptUdpFamily,
) -> Result<SocketAddr, SidecarError> {
    (host, port)
        .to_socket_addrs()
        .map_err(sidecar_net_error)?
        .find(|addr| family.matches_addr(addr))
        .ok_or_else(|| {
            SidecarError::Execution(format!(
                "failed to resolve {} UDP bind address {host}:{port}",
                family.socket_type()
            ))
        })
}

fn resolve_udp_addr<B>(request: UdpRemoteAddrRequest<'_, B>) -> Result<SocketAddr, SidecarError>
where
    B: NativeSidecarBridge + Send + 'static,
    BridgeError<B>: fmt::Debug + Send + Sync + 'static,
{
    let UdpRemoteAddrRequest {
        bridge,
        kernel,
        vm_id,
        dns,
        host,
        port,
        family,
        context,
    } = request;
    resolve_dns_ip_addrs(
        bridge,
        kernel,
        vm_id,
        dns,
        host,
        DnsLookupPolicy::SkipPermissions,
    )?
    .into_iter()
    .map(|ip| {
        let family_key = JavascriptSocketFamily::from_ip(ip);
        let actual_port = if is_loopback_ip(ip) {
            context
                .translate_udp_loopback_port(family_key, port)
                .unwrap_or(port)
        } else {
            port
        };
        SocketAddr::new(ip, actual_port)
    })
    .find(|addr| family.matches_addr(addr))
    .ok_or_else(|| {
        SidecarError::Execution(format!(
            "failed to resolve {} UDP address {host}:{port}",
            family.socket_type()
        ))
    })
}

fn socket_addr_family(addr: &SocketAddr) -> &'static str {
    match addr {
        SocketAddr::V4(_) => "IPv4",
        SocketAddr::V6(_) => "IPv6",
    }
}

fn javascript_net_timeout_value() -> Value {
    Value::String(String::from(JAVASCRIPT_NET_TIMEOUT_SENTINEL))
}

fn javascript_net_json_string(value: Value, label: &str) -> Result<Value, SidecarError> {
    serde_json::to_string(&value)
        .map(Value::String)
        .map_err(|error| {
            SidecarError::InvalidState(format!("failed to serialize {label} payload: {error}"))
        })
}

fn javascript_net_read_value(
    event: Option<JavascriptTcpSocketEvent>,
) -> Result<Value, SidecarError> {
    match event {
        Some(JavascriptTcpSocketEvent::Data(chunk)) => Ok(Value::String(
            base64::engine::general_purpose::STANDARD.encode(chunk),
        )),
        Some(JavascriptTcpSocketEvent::End | JavascriptTcpSocketEvent::Close { .. }) => {
            Ok(Value::Null)
        }
        Some(JavascriptTcpSocketEvent::Error { code, message }) => {
            let detail = code.unwrap_or_else(|| String::from("socket read"));
            Err(SidecarError::Execution(format!("{detail}: {message}")))
        }
        None => Ok(javascript_net_timeout_value()),
    }
}

fn io_error_code(error: &std::io::Error) -> Option<String> {
    match error.raw_os_error() {
        Some(libc::EADDRINUSE) => Some(String::from("EADDRINUSE")),
        Some(libc::EADDRNOTAVAIL) => Some(String::from("EADDRNOTAVAIL")),
        Some(libc::ECONNREFUSED) => Some(String::from("ECONNREFUSED")),
        Some(libc::ECONNRESET) => Some(String::from("ECONNRESET")),
        Some(libc::EINVAL) => Some(String::from("EINVAL")),
        Some(libc::EPIPE) => Some(String::from("EPIPE")),
        Some(libc::ETIMEDOUT) => Some(String::from("ETIMEDOUT")),
        Some(libc::EHOSTUNREACH) => Some(String::from("EHOSTUNREACH")),
        Some(libc::ENETUNREACH) => Some(String::from("ENETUNREACH")),
        _ => None,
    }
}

fn sidecar_net_error(error: std::io::Error) -> SidecarError {
    let message = match io_error_code(&error) {
        Some(code) => format!("{code}: {error}"),
        None => error.to_string(),
    };
    SidecarError::Execution(message)
}

fn tls_provider() -> Arc<rustls::crypto::CryptoProvider> {
    Arc::new(aws_lc_rs::default_provider())
}

fn tls_local_certificates(
    options: &JavascriptTlsBridgeOptions,
) -> Result<Vec<Vec<u8>>, SidecarError> {
    let Some(certificates) = options.cert.as_ref() else {
        return Ok(Vec::new());
    };
    tls_material_entries(certificates)
}

fn tls_material_entries(material: &JavascriptTlsMaterial) -> Result<Vec<Vec<u8>>, SidecarError> {
    match material {
        JavascriptTlsMaterial::Single(entry) => tls_data_value(entry).map(|value| vec![value]),
        JavascriptTlsMaterial::Many(entries) => entries.iter().map(tls_data_value).collect(),
    }
}

fn tls_data_value(value: &JavascriptTlsDataValue) -> Result<Vec<u8>, SidecarError> {
    match value {
        JavascriptTlsDataValue::Buffer { data } => base64::engine::general_purpose::STANDARD
            .decode(data)
            .map_err(|error| {
                SidecarError::InvalidState(format!("TLS material contains invalid base64: {error}"))
            }),
        JavascriptTlsDataValue::String { data } => Ok(data.as_bytes().to_vec()),
    }
}

fn tls_certificates_from_material(
    material: &JavascriptTlsMaterial,
) -> Result<Vec<CertificateDer<'static>>, SidecarError> {
    let mut certificates = Vec::new();
    for entry in tls_material_entries(material)? {
        let mut reader = std::io::BufReader::new(Cursor::new(entry.clone()));
        let parsed = rustls_pemfile::certs(&mut reader)
            .collect::<Result<Vec<_>, _>>()
            .map_err(sidecar_net_error)?;
        if parsed.is_empty() {
            certificates.push(CertificateDer::from(entry));
        } else {
            certificates.extend(parsed);
        }
    }
    if certificates.is_empty() {
        return Err(SidecarError::InvalidState(String::from(
            "TLS certificate material did not contain any certificates",
        )));
    }
    Ok(certificates)
}

fn tls_private_key_from_material(
    material: &JavascriptTlsMaterial,
) -> Result<PrivateKeyDer<'static>, SidecarError> {
    for entry in tls_material_entries(material)? {
        let mut reader = std::io::BufReader::new(Cursor::new(entry));
        if let Some(key) = rustls_pemfile::private_key(&mut reader).map_err(sidecar_net_error)? {
            return Ok(key);
        }
    }
    Err(SidecarError::InvalidState(String::from(
        "TLS private key material did not contain a supported key",
    )))
}

fn tls_root_store(options: &JavascriptTlsBridgeOptions) -> Result<RootCertStore, SidecarError> {
    let mut roots = RootCertStore::empty();
    if let Some(ca) = options.ca.as_ref() {
        for certificate in tls_certificates_from_material(ca)? {
            roots.add(certificate).map_err(|error| {
                SidecarError::InvalidState(format!("failed to add TLS CA certificate: {error}"))
            })?;
        }
        return Ok(roots);
    }

    for certificate in rustls_native_certs::load_native_certs().certs {
        roots.add(certificate).map_err(|error| {
            SidecarError::InvalidState(format!(
                "failed to add native TLS certificate to root store: {error}"
            ))
        })?;
    }
    Ok(roots)
}

fn build_client_tls_stream(
    stream: TcpStream,
    options: &JavascriptTlsBridgeOptions,
) -> Result<rustls::StreamOwned<ClientConnection, TcpStream>, SidecarError> {
    let config = build_client_tls_config(options)?;
    let server_name = options
        .servername
        .clone()
        .unwrap_or_else(|| String::from("localhost"));
    let server_name = ServerName::try_from(server_name)
        .map_err(|_| SidecarError::InvalidState(String::from("invalid TLS servername")))?;
    stream
        .set_read_timeout(Some(TLS_HANDSHAKE_TIMEOUT))
        .map_err(sidecar_net_error)?;
    stream
        .set_write_timeout(Some(TLS_HANDSHAKE_TIMEOUT))
        .map_err(sidecar_net_error)?;
    let mut tls_stream = rustls::StreamOwned::new(
        ClientConnection::new(Arc::new(config), server_name).map_err(|error| {
            SidecarError::Execution(format!("failed to start TLS client: {error}"))
        })?,
        stream,
    );
    while tls_stream.conn.is_handshaking() {
        tls_stream
            .conn
            .complete_io(&mut tls_stream.sock)
            .map_err(sidecar_net_error)?;
    }
    tls_stream
        .sock
        .set_read_timeout(Some(TCP_SOCKET_POLL_TIMEOUT))
        .map_err(sidecar_net_error)?;
    tls_stream
        .sock
        .set_write_timeout(None)
        .map_err(sidecar_net_error)?;
    Ok(tls_stream)
}

fn build_client_loopback_tls_stream(
    transport: crate::state::LoopbackTlsEndpoint,
    options: &JavascriptTlsBridgeOptions,
) -> Result<rustls::StreamOwned<ClientConnection, crate::state::LoopbackTlsEndpoint>, SidecarError>
{
    let config = build_client_tls_config(options)?;
    let server_name = options
        .servername
        .clone()
        .unwrap_or_else(|| String::from("localhost"));
    let server_name = ServerName::try_from(server_name)
        .map_err(|_| SidecarError::InvalidState(String::from("invalid TLS servername")))?;
    let mut tls_stream = rustls::StreamOwned::new(
        ClientConnection::new(Arc::new(config), server_name).map_err(|error| {
            SidecarError::Execution(format!("failed to start TLS client: {error}"))
        })?,
        transport,
    );
    match tls_stream.conn.complete_io(&mut tls_stream.sock) {
        Ok(_) => {}
        Err(error)
            if matches!(
                error.kind(),
                std::io::ErrorKind::WouldBlock | std::io::ErrorKind::TimedOut
            ) => {}
        Err(error) => return Err(sidecar_net_error(error)),
    }
    Ok(tls_stream)
}

fn build_client_tls_config(
    options: &JavascriptTlsBridgeOptions,
) -> Result<ClientConfig, SidecarError> {
    let provider = tls_provider();
    let builder = ClientConfig::builder_with_provider(provider.clone())
        .with_safe_default_protocol_versions()
        .map_err(|error| {
            SidecarError::InvalidState(format!("invalid TLS protocol config: {error}"))
        })?;

    let mut config = if options.reject_unauthorized == Some(false) {
        let verifier = Arc::new(InsecureTlsVerifier {
            supported_schemes: provider
                .signature_verification_algorithms
                .supported_schemes(),
        });
        builder
            .dangerous()
            .with_custom_certificate_verifier(verifier)
            .with_no_client_auth()
    } else {
        builder
            .with_root_certificates(tls_root_store(options)?)
            .with_no_client_auth()
    };

    if let Some(protocols) = options.alpn_protocols.as_ref() {
        config.alpn_protocols = protocols
            .iter()
            .map(|protocol| protocol.as_bytes().to_vec())
            .collect();
    }
    Ok(config)
}

fn build_server_tls_stream(
    stream: TcpStream,
    options: &JavascriptTlsBridgeOptions,
) -> Result<rustls::StreamOwned<ServerConnection, TcpStream>, SidecarError> {
    let config = build_server_tls_config(options)?;
    stream
        .set_read_timeout(Some(TLS_HANDSHAKE_TIMEOUT))
        .map_err(sidecar_net_error)?;
    stream
        .set_write_timeout(Some(TLS_HANDSHAKE_TIMEOUT))
        .map_err(sidecar_net_error)?;
    let mut tls_stream = rustls::StreamOwned::new(
        ServerConnection::new(Arc::new(config)).map_err(|error| {
            SidecarError::Execution(format!("failed to start TLS server: {error}"))
        })?,
        stream,
    );
    while tls_stream.conn.is_handshaking() {
        tls_stream
            .conn
            .complete_io(&mut tls_stream.sock)
            .map_err(sidecar_net_error)?;
    }
    tls_stream
        .sock
        .set_read_timeout(Some(TCP_SOCKET_POLL_TIMEOUT))
        .map_err(sidecar_net_error)?;
    tls_stream
        .sock
        .set_write_timeout(None)
        .map_err(sidecar_net_error)?;
    Ok(tls_stream)
}

fn build_server_loopback_tls_stream(
    transport: crate::state::LoopbackTlsEndpoint,
    options: &JavascriptTlsBridgeOptions,
) -> Result<rustls::StreamOwned<ServerConnection, crate::state::LoopbackTlsEndpoint>, SidecarError>
{
    let config = build_server_tls_config(options)?;
    Ok(rustls::StreamOwned::new(
        ServerConnection::new(Arc::new(config)).map_err(|error| {
            SidecarError::Execution(format!("failed to start TLS server: {error}"))
        })?,
        transport,
    ))
}

fn build_server_tls_config(
    options: &JavascriptTlsBridgeOptions,
) -> Result<ServerConfig, SidecarError> {
    let certificates = tls_certificates_from_material(options.cert.as_ref().ok_or_else(|| {
        SidecarError::InvalidState(String::from("TLS server upgrade requires a certificate"))
    })?)?;
    let key = tls_private_key_from_material(options.key.as_ref().ok_or_else(|| {
        SidecarError::InvalidState(String::from("TLS server upgrade requires a private key"))
    })?)?;

    let mut config = ServerConfig::builder_with_provider(tls_provider())
        .with_safe_default_protocol_versions()
        .map_err(|error| {
            SidecarError::InvalidState(format!("invalid TLS protocol config: {error}"))
        })?
        .with_no_client_auth()
        .with_single_cert(certificates, key)
        .map_err(|error| {
            SidecarError::InvalidState(format!("invalid TLS server config: {error}"))
        })?;

    if let Some(protocols) = options.alpn_protocols.as_ref() {
        config.alpn_protocols = protocols
            .iter()
            .map(|protocol| protocol.as_bytes().to_vec())
            .collect();
    }
    Ok(config)
}

fn tls_protocol_name(version: rustls::ProtocolVersion) -> String {
    match version {
        rustls::ProtocolVersion::TLSv1_2 => String::from("TLSv1.2"),
        rustls::ProtocolVersion::TLSv1_3 => String::from("TLSv1.3"),
        other => other
            .as_str()
            .map(str::to_owned)
            .unwrap_or_else(|| format!("{other:?}")),
    }
}

fn tls_cipher_bridge_value(suite: rustls::SupportedCipherSuite) -> Value {
    tls_bridge_object(vec![
        (
            "name",
            suite
                .suite()
                .as_str()
                .map(|value| Value::String(value.to_owned()))
                .unwrap_or(Value::Null),
        ),
        (
            "standardName",
            suite
                .suite()
                .as_str()
                .map(|value| Value::String(value.to_owned()))
                .unwrap_or(Value::Null),
        ),
        (
            "version",
            Value::String(if suite.tls13().is_some() {
                String::from("TLSv1.3")
            } else {
                String::from("TLSv1.2")
            }),
        ),
    ])
}

fn tls_certificate_bridge_value(certificate: &[u8], detailed: bool) -> Value {
    let mut fields = vec![("raw", tls_bridge_buffer_value(certificate))];
    if detailed {
        fields.push(("issuerCertificate", tls_bridge_undefined_value()));
    }
    tls_bridge_object(fields)
}

fn tls_bridge_buffer_value(bytes: &[u8]) -> Value {
    json!({
        "type": "buffer",
        "data": base64::engine::general_purpose::STANDARD.encode(bytes),
    })
}

fn tls_bridge_object(entries: Vec<(&str, Value)>) -> Value {
    let value = entries
        .into_iter()
        .map(|(key, value)| (key.to_owned(), value))
        .collect::<serde_json::Map<String, Value>>();
    json!({
        "type": "object",
        "id": 1,
        "value": value,
    })
}

fn tls_bridge_undefined_value() -> Value {
    json!({
        "type": "undefined",
    })
}

fn spawn_tcp_socket_reader(
    stream: TcpStream,
    sender: Sender<JavascriptTcpSocketEvent>,
    tls_mode: Arc<AtomicBool>,
    saw_local_shutdown: Arc<AtomicBool>,
    saw_remote_end: Arc<AtomicBool>,
    close_notified: Arc<AtomicBool>,
) {
    thread::spawn(move || {
        let mut stream = stream;
        let mut buffer = vec![0_u8; 64 * 1024];
        loop {
            if tls_mode.load(Ordering::SeqCst) {
                break;
            }
            match stream.read(&mut buffer) {
                Ok(0) => {
                    saw_remote_end.store(true, Ordering::SeqCst);
                    let _ = sender.send(JavascriptTcpSocketEvent::End);
                    if saw_local_shutdown.load(Ordering::SeqCst)
                        && !close_notified.swap(true, Ordering::SeqCst)
                    {
                        let _ = sender.send(JavascriptTcpSocketEvent::Close { had_error: false });
                    }
                    break;
                }
                Ok(bytes_read) => {
                    if sender
                        .send(JavascriptTcpSocketEvent::Data(
                            buffer[..bytes_read].to_vec(),
                        ))
                        .is_err()
                    {
                        break;
                    }
                }
                Err(error)
                    if matches!(
                        error.kind(),
                        std::io::ErrorKind::WouldBlock | std::io::ErrorKind::TimedOut
                    ) =>
                {
                    continue;
                }
                Err(error) => {
                    let code = io_error_code(&error);
                    let _ = sender.send(JavascriptTcpSocketEvent::Error {
                        code,
                        message: error.to_string(),
                    });
                    if !close_notified.swap(true, Ordering::SeqCst) {
                        let _ = sender.send(JavascriptTcpSocketEvent::Close { had_error: true });
                    }
                    break;
                }
            }
        }
    });
}

fn spawn_tls_socket_reader(
    tls_stream: Arc<Mutex<Option<ActiveTlsStream>>>,
    sender: Sender<JavascriptTcpSocketEvent>,
    saw_local_shutdown: Arc<AtomicBool>,
    saw_remote_end: Arc<AtomicBool>,
    close_notified: Arc<AtomicBool>,
) {
    thread::spawn(move || {
        let mut buffer = vec![0_u8; 64 * 1024];
        loop {
            let read_result = {
                let mut guard = match tls_stream.lock() {
                    Ok(guard) => guard,
                    Err(_) => return,
                };
                let Some(stream) = guard.as_mut() else {
                    return;
                };
                stream.read(&mut buffer)
            };

            match read_result {
                Ok(0) => {
                    saw_remote_end.store(true, Ordering::SeqCst);
                    let _ = sender.send(JavascriptTcpSocketEvent::End);
                    if saw_local_shutdown.load(Ordering::SeqCst)
                        && !close_notified.swap(true, Ordering::SeqCst)
                    {
                        let _ = sender.send(JavascriptTcpSocketEvent::Close { had_error: false });
                    }
                    break;
                }
                Ok(bytes_read) => {
                    if sender
                        .send(JavascriptTcpSocketEvent::Data(
                            buffer[..bytes_read].to_vec(),
                        ))
                        .is_err()
                    {
                        break;
                    }
                }
                Err(error)
                    if matches!(
                        error.kind(),
                        std::io::ErrorKind::WouldBlock | std::io::ErrorKind::TimedOut
                    ) =>
                {
                    // The TLS reader and writer share one rustls stream mutex. Yield after
                    // timed-out reads so request writes can acquire the lock promptly.
                    std::thread::sleep(Duration::from_millis(1));
                    continue;
                }
                Err(error) if error.kind() == std::io::ErrorKind::UnexpectedEof => {
                    saw_remote_end.store(true, Ordering::SeqCst);
                    let _ = sender.send(JavascriptTcpSocketEvent::End);
                    if saw_local_shutdown.load(Ordering::SeqCst)
                        && !close_notified.swap(true, Ordering::SeqCst)
                    {
                        let _ = sender.send(JavascriptTcpSocketEvent::Close { had_error: false });
                    }
                    break;
                }
                Err(error) => {
                    let code = io_error_code(&error);
                    let _ = sender.send(JavascriptTcpSocketEvent::Error {
                        code,
                        message: error.to_string(),
                    });
                    if !close_notified.swap(true, Ordering::SeqCst) {
                        let _ = sender.send(JavascriptTcpSocketEvent::Close { had_error: true });
                    }
                    break;
                }
            }
        }
    });
}

fn spawn_unix_socket_reader(
    stream: UnixStream,
    sender: Sender<JavascriptTcpSocketEvent>,
    saw_local_shutdown: Arc<AtomicBool>,
    saw_remote_end: Arc<AtomicBool>,
    close_notified: Arc<AtomicBool>,
) {
    thread::spawn(move || {
        let mut stream = stream;
        let mut buffer = vec![0_u8; 64 * 1024];
        loop {
            match stream.read(&mut buffer) {
                Ok(0) => {
                    saw_remote_end.store(true, Ordering::SeqCst);
                    let _ = sender.send(JavascriptTcpSocketEvent::End);
                    if saw_local_shutdown.load(Ordering::SeqCst)
                        && !close_notified.swap(true, Ordering::SeqCst)
                    {
                        let _ = sender.send(JavascriptTcpSocketEvent::Close { had_error: false });
                    }
                    break;
                }
                Ok(bytes_read) => {
                    if sender
                        .send(JavascriptTcpSocketEvent::Data(
                            buffer[..bytes_read].to_vec(),
                        ))
                        .is_err()
                    {
                        break;
                    }
                }
                Err(error) => {
                    let code = io_error_code(&error);
                    let _ = sender.send(JavascriptTcpSocketEvent::Error {
                        code,
                        message: error.to_string(),
                    });
                    if !close_notified.swap(true, Ordering::SeqCst) {
                        let _ = sender.send(JavascriptTcpSocketEvent::Close { had_error: true });
                    }
                    break;
                }
            }
        }
    });
}

fn terminate_child_process_tree(kernel: &mut SidecarKernel, process: &mut ActiveProcess) {
    let sqlite_database_ids = process.sqlite_databases.keys().copied().collect::<Vec<_>>();
    for database_id in sqlite_database_ids {
        let _ = close_sqlite_database(kernel, process, database_id);
    }
    process.sqlite_statements.clear();
    process.http_servers.clear();
    process.pending_http_requests.clear();
    if let Ok(mut http2) = process.http2.shared.lock() {
        let sessions = http2.sessions.values().cloned().collect::<Vec<_>>();
        http2.server_events.clear();
        http2.session_events.clear();
        http2.streams.clear();
        http2.servers.clear();
        http2.sessions.clear();
        drop(http2);
        for session in sessions {
            let (respond_to, _rx) = mpsc::channel();
            let _ = session.command_tx.send(Http2SessionCommand::Close {
                abrupt: true,
                respond_to,
            });
        }
    }

    let listener_ids = process.tcp_listeners.keys().cloned().collect::<Vec<_>>();
    for listener_id in listener_ids {
        if let Some(listener) = process.tcp_listeners.remove(&listener_id) {
            let _ = listener.close(kernel, process.kernel_pid);
        }
    }

    let sockets = process.tcp_sockets.keys().cloned().collect::<Vec<_>>();
    for socket_id in sockets {
        if let Some(socket) = process.tcp_sockets.remove(&socket_id) {
            let _ = socket.close(kernel, process.kernel_pid);
        }
    }

    let unix_listener_ids = process.unix_listeners.keys().cloned().collect::<Vec<_>>();
    for listener_id in unix_listener_ids {
        if let Some(listener) = process.unix_listeners.remove(&listener_id) {
            let _ = listener.close();
        }
    }

    let unix_sockets = process.unix_sockets.keys().cloned().collect::<Vec<_>>();
    for socket_id in unix_sockets {
        if let Some(socket) = process.unix_sockets.remove(&socket_id) {
            let _ = socket.close();
        }
    }

    let udp_socket_ids = process.udp_sockets.keys().cloned().collect::<Vec<_>>();
    for socket_id in udp_socket_ids {
        if let Some(mut socket) = process.udp_sockets.remove(&socket_id) {
            socket.close(kernel, process.kernel_pid);
        }
    }

    let child_ids = process.child_processes.keys().cloned().collect::<Vec<_>>();
    for child_id in child_ids {
        let Some(mut child) = process.child_processes.remove(&child_id) else {
            continue;
        };
        terminate_child_process_tree(kernel, &mut child);
        let _ = kernel.kill_process(EXECUTION_DRIVER_NAME, child.kernel_pid, SIGTERM);
        let _ = signal_runtime_process(child.execution.child_pid(), SIGTERM);
        child.kernel_handle.finish(0);
        let _ = kernel.wait_and_reap(child.kernel_pid);
    }
}

fn service_javascript_sqlite_sync_rpc(
    kernel: &mut SidecarKernel,
    process: &mut ActiveProcess,
    request: &JavascriptSyncRpcRequest,
) -> Result<Value, SidecarError> {
    match request.method.as_str() {
        "sqlite.constants" => Ok(json!({})),
        "sqlite.open" => sqlite_open_database(kernel, process, request),
        "sqlite.close" => {
            let database_id =
                javascript_sync_rpc_arg_u64(&request.args, 0, "sqlite.close database id")?;
            close_sqlite_database(kernel, process, database_id)?;
            Ok(Value::Null)
        }
        "sqlite.exec" => sqlite_exec_database(kernel, process, request),
        "sqlite.query" => sqlite_query_database(process, request),
        "sqlite.prepare" => sqlite_prepare_statement(process, request),
        "sqlite.location" => {
            let database_id =
                javascript_sync_rpc_arg_u64(&request.args, 0, "sqlite.location database id")?;
            let database = sqlite_database(process, database_id)?;
            Ok(database
                .vm_path
                .as_ref()
                .map(|path| Value::String(path.clone()))
                .unwrap_or(Value::Null))
        }
        "sqlite.checkpoint" => {
            let database_id =
                javascript_sync_rpc_arg_u64(&request.args, 0, "sqlite.checkpoint database id")?;
            let kernel_pid = process.kernel_pid;
            let database = sqlite_database_mut(process, database_id)?;
            sqlite_sync_database(kernel, kernel_pid, database)?;
            Ok(Value::Null)
        }
        "sqlite.statement.run" => sqlite_run_statement(kernel, process, request),
        "sqlite.statement.get" => sqlite_get_statement(process, request),
        "sqlite.statement.all" | "sqlite.statement.iterate" => {
            sqlite_all_statement(process, request)
        }
        "sqlite.statement.columns" => sqlite_statement_columns(process, request),
        "sqlite.statement.setReturnArrays" => {
            let statement_id = javascript_sync_rpc_arg_u64(
                &request.args,
                0,
                "sqlite.statement.setReturnArrays statement id",
            )?;
            let enabled = javascript_sync_rpc_arg_bool(
                &request.args,
                1,
                "sqlite.statement.setReturnArrays enabled",
            )?;
            sqlite_statement_mut(process, statement_id)?.return_arrays = enabled;
            Ok(Value::Null)
        }
        "sqlite.statement.setReadBigInts" => {
            let statement_id = javascript_sync_rpc_arg_u64(
                &request.args,
                0,
                "sqlite.statement.setReadBigInts statement id",
            )?;
            let enabled = javascript_sync_rpc_arg_bool(
                &request.args,
                1,
                "sqlite.statement.setReadBigInts enabled",
            )?;
            sqlite_statement_mut(process, statement_id)?.read_bigints = enabled;
            Ok(Value::Null)
        }
        "sqlite.statement.setAllowBareNamedParameters" => {
            let statement_id = javascript_sync_rpc_arg_u64(
                &request.args,
                0,
                "sqlite.statement.setAllowBareNamedParameters statement id",
            )?;
            let enabled = javascript_sync_rpc_arg_bool(
                &request.args,
                1,
                "sqlite.statement.setAllowBareNamedParameters enabled",
            )?;
            sqlite_statement_mut(process, statement_id)?.allow_bare_named_parameters = enabled;
            Ok(Value::Null)
        }
        "sqlite.statement.setAllowUnknownNamedParameters" => {
            let statement_id = javascript_sync_rpc_arg_u64(
                &request.args,
                0,
                "sqlite.statement.setAllowUnknownNamedParameters statement id",
            )?;
            let enabled = javascript_sync_rpc_arg_bool(
                &request.args,
                1,
                "sqlite.statement.setAllowUnknownNamedParameters enabled",
            )?;
            sqlite_statement_mut(process, statement_id)?.allow_unknown_named_parameters = enabled;
            Ok(Value::Null)
        }
        "sqlite.statement.finalize" => {
            let statement_id = javascript_sync_rpc_arg_u64(
                &request.args,
                0,
                "sqlite.statement.finalize statement id",
            )?;
            process
                .sqlite_statements
                .remove(&statement_id)
                .ok_or_else(|| {
                    SidecarError::InvalidState(format!(
                        "sqlite statement handle not found: {statement_id}"
                    ))
                })?;
            Ok(Value::Null)
        }
        other => Err(SidecarError::InvalidState(format!(
            "unsupported JavaScript sqlite sync RPC method {other}"
        ))),
    }
}

fn sqlite_open_database(
    kernel: &mut SidecarKernel,
    process: &mut ActiveProcess,
    request: &JavascriptSyncRpcRequest,
) -> Result<Value, SidecarError> {
    ensure_per_process_state_handle_capacity(process.sqlite_databases.len(), "sqlite database")?;
    let path = request.args.first().and_then(Value::as_str);
    let vm_path = path.filter(|value| !value.is_empty() && *value != ":memory:");
    let options = request.args.get(1);
    let read_only = sqlite_option_bool(options, "readOnly").unwrap_or(false);
    let create = sqlite_option_bool(options, "create").unwrap_or(!read_only);
    let timeout_ms = sqlite_option_u64(options, "timeout");

    process.next_sqlite_database_id += 1;
    let database_id = process.next_sqlite_database_id;

    let host_path = if vm_path.is_some() {
        Some(
            std::env::temp_dir()
                .join(format!(
                    "secure-exec-sidecar-sqlite-{}-{database_id}",
                    process.kernel_pid
                ))
                .join("database.sqlite"),
        )
    } else {
        None
    };

    if let Some(host_path) = host_path.as_ref() {
        if let Some(parent) = host_path.parent() {
            fs::create_dir_all(parent).map_err(|error| {
                SidecarError::Io(format!(
                    "failed to prepare sqlite temp directory {}: {error}",
                    parent.display()
                ))
            })?;
        }
    }

    if let (Some(vm_path), Some(host_path)) = (vm_path, host_path.as_ref()) {
        if kernel
            .exists_for_process(EXECUTION_DRIVER_NAME, process.kernel_pid, vm_path)
            .map_err(kernel_error)?
        {
            let contents = kernel
                .read_file_for_process(EXECUTION_DRIVER_NAME, process.kernel_pid, vm_path)
                .map_err(kernel_error)?;
            fs::write(host_path, contents).map_err(|error| {
                SidecarError::Io(format!(
                    "failed to materialize sqlite database {}: {error}",
                    host_path.display()
                ))
            })?;
        } else if read_only && !create {
            return Err(SidecarError::InvalidState(format!(
                "sqlite database does not exist: {vm_path}"
            )));
        }
    }

    let target = host_path
        .as_ref()
        .map(|path| path.to_string_lossy().into_owned())
        .unwrap_or_else(|| String::from(":memory:"));
    let mut flags = if read_only {
        SqliteOpenFlags::SQLITE_OPEN_READ_ONLY
    } else {
        SqliteOpenFlags::SQLITE_OPEN_READ_WRITE
    };
    if create && !read_only {
        flags |= SqliteOpenFlags::SQLITE_OPEN_CREATE;
    }

    let connection = SqliteConnection::open_with_flags(&target, flags).map_err(|error| {
        SidecarError::InvalidState(format!(
            "sqlite database open failed for {}: {error}",
            vm_path.unwrap_or(":memory:")
        ))
    })?;
    if let Some(timeout_ms) = timeout_ms {
        connection
            .busy_timeout(Duration::from_millis(timeout_ms))
            .map_err(sqlite_error)?;
    }
    if host_path.is_some() && !read_only {
        let _ = connection.pragma_update(None, "journal_mode", "WAL");
    }

    process.sqlite_databases.insert(
        database_id,
        ActiveSqliteDatabase {
            connection,
            host_path,
            vm_path: vm_path.map(String::from),
            dirty: false,
            transaction_depth: 0,
            read_only,
        },
    );

    Ok(json!(database_id))
}

fn sqlite_exec_database(
    kernel: &mut SidecarKernel,
    process: &mut ActiveProcess,
    request: &JavascriptSyncRpcRequest,
) -> Result<Value, SidecarError> {
    let database_id = javascript_sync_rpc_arg_u64(&request.args, 0, "sqlite.exec database id")?;
    let sql = javascript_sync_rpc_arg_str(&request.args, 1, "sqlite.exec sql")?;
    let kernel_pid = process.kernel_pid;
    let database = sqlite_database_mut(process, database_id)?;
    let before = database.connection.total_changes();
    database
        .connection
        .execute_batch(sql)
        .map_err(sqlite_error)?;
    mark_sqlite_mutation(database, sql);
    sqlite_sync_database(kernel, kernel_pid, database)?;
    Ok(json!(database
        .connection
        .total_changes()
        .saturating_sub(before)))
}

fn sqlite_query_database(
    process: &mut ActiveProcess,
    request: &JavascriptSyncRpcRequest,
) -> Result<Value, SidecarError> {
    let database_id = javascript_sync_rpc_arg_u64(&request.args, 0, "sqlite.query database id")?;
    let sql = javascript_sync_rpc_arg_str(&request.args, 1, "sqlite.query sql")?;
    let params = request.args.get(2);
    let options = request.args.get(3);
    let return_arrays = sqlite_option_bool(options, "returnArrays").unwrap_or(false);
    let read_bigints = sqlite_option_bool(options, "readBigInts").unwrap_or(false);
    let database = sqlite_database_mut(process, database_id)?;
    sqlite_query_rows(
        &mut database.connection,
        sql,
        params,
        return_arrays,
        read_bigints,
        true,
        false,
    )
}

fn sqlite_prepare_statement(
    process: &mut ActiveProcess,
    request: &JavascriptSyncRpcRequest,
) -> Result<Value, SidecarError> {
    ensure_per_process_state_handle_capacity(process.sqlite_statements.len(), "sqlite statement")?;
    let database_id = javascript_sync_rpc_arg_u64(&request.args, 0, "sqlite.prepare database id")?;
    let sql = javascript_sync_rpc_arg_str(&request.args, 1, "sqlite.prepare sql")?;
    let _ = sqlite_database(process, database_id)?;
    process.next_sqlite_statement_id += 1;
    let statement_id = process.next_sqlite_statement_id;
    process.sqlite_statements.insert(
        statement_id,
        ActiveSqliteStatement {
            database_id,
            sql: sql.to_owned(),
            return_arrays: false,
            read_bigints: false,
            allow_bare_named_parameters: false,
            allow_unknown_named_parameters: false,
        },
    );
    Ok(json!(statement_id))
}

fn sqlite_run_statement(
    kernel: &mut SidecarKernel,
    process: &mut ActiveProcess,
    request: &JavascriptSyncRpcRequest,
) -> Result<Value, SidecarError> {
    let statement_id =
        javascript_sync_rpc_arg_u64(&request.args, 0, "sqlite.statement.run statement id")?;
    let params = request.args.get(1);
    let statement_state = sqlite_statement(process, statement_id)?.clone();
    let kernel_pid = process.kernel_pid;
    let database = sqlite_database_mut(process, statement_state.database_id)?;
    let before = database.connection.total_changes();
    {
        let mut statement = database
            .connection
            .prepare(&statement_state.sql)
            .map_err(sqlite_error)?;
        bind_sqlite_parameters(
            &mut statement,
            params,
            statement_state.allow_bare_named_parameters,
            statement_state.allow_unknown_named_parameters,
        )?;
        statement.raw_execute().map_err(sqlite_error)?;
    }
    let changes = database.connection.total_changes().saturating_sub(before);
    let last_insert_rowid = database.connection.last_insert_rowid();
    mark_sqlite_mutation(database, &statement_state.sql);
    sqlite_sync_database(kernel, kernel_pid, database)?;
    let result = json!({
        "changes": changes,
        "lastInsertRowid": encode_sqlite_integer(last_insert_rowid, true),
    });
    Ok(result)
}

fn sqlite_get_statement(
    process: &mut ActiveProcess,
    request: &JavascriptSyncRpcRequest,
) -> Result<Value, SidecarError> {
    let statement_id =
        javascript_sync_rpc_arg_u64(&request.args, 0, "sqlite.statement.get statement id")?;
    let params = request.args.get(1);
    let statement_state = sqlite_statement(process, statement_id)?.clone();
    let database = sqlite_database_mut(process, statement_state.database_id)?;
    let rows = sqlite_query_rows(
        &mut database.connection,
        &statement_state.sql,
        params,
        statement_state.return_arrays,
        statement_state.read_bigints,
        statement_state.allow_bare_named_parameters,
        statement_state.allow_unknown_named_parameters,
    )?;
    Ok(rows
        .as_array()
        .and_then(|rows| rows.first().cloned())
        .unwrap_or(Value::Null))
}

fn sqlite_all_statement(
    process: &mut ActiveProcess,
    request: &JavascriptSyncRpcRequest,
) -> Result<Value, SidecarError> {
    let statement_id =
        javascript_sync_rpc_arg_u64(&request.args, 0, "sqlite.statement.all statement id")?;
    let params = request.args.get(1);
    let statement_state = sqlite_statement(process, statement_id)?.clone();
    let database = sqlite_database_mut(process, statement_state.database_id)?;
    sqlite_query_rows(
        &mut database.connection,
        &statement_state.sql,
        params,
        statement_state.return_arrays,
        statement_state.read_bigints,
        statement_state.allow_bare_named_parameters,
        statement_state.allow_unknown_named_parameters,
    )
}

fn sqlite_statement_columns(
    process: &mut ActiveProcess,
    request: &JavascriptSyncRpcRequest,
) -> Result<Value, SidecarError> {
    let statement_id =
        javascript_sync_rpc_arg_u64(&request.args, 0, "sqlite.statement.columns statement id")?;
    let statement_state = sqlite_statement(process, statement_id)?.clone();
    let database = sqlite_database_mut(process, statement_state.database_id)?;
    let statement = database
        .connection
        .prepare(&statement_state.sql)
        .map_err(sqlite_error)?;
    Ok(Value::Array(
        statement
            .column_names()
            .iter()
            .map(|name| json!({ "name": name }))
            .collect(),
    ))
}

fn sqlite_query_rows(
    connection: &mut SqliteConnection,
    sql: &str,
    params: Option<&Value>,
    return_arrays: bool,
    read_bigints: bool,
    allow_bare_named_parameters: bool,
    allow_unknown_named_parameters: bool,
) -> Result<Value, SidecarError> {
    let mut statement = connection.prepare(sql).map_err(sqlite_error)?;
    let column_names = statement
        .column_names()
        .iter()
        .map(|name| (*name).to_owned())
        .collect::<Vec<_>>();
    let column_count = statement.column_count();
    bind_sqlite_parameters(
        &mut statement,
        params,
        allow_bare_named_parameters,
        allow_unknown_named_parameters,
    )?;
    let mut rows = statement.raw_query();
    let mut encoded_rows = Vec::new();
    while let Some(row) = rows.next().map_err(sqlite_error)? {
        encoded_rows.push(encode_sqlite_row(
            row,
            &column_names,
            column_count,
            return_arrays,
            read_bigints,
        )?);
    }
    Ok(Value::Array(encoded_rows))
}

fn encode_sqlite_row(
    row: &rusqlite::Row<'_>,
    column_names: &[String],
    column_count: usize,
    return_arrays: bool,
    read_bigints: bool,
) -> Result<Value, SidecarError> {
    if return_arrays {
        let mut values = Vec::with_capacity(column_count);
        for index in 0..column_count {
            values.push(encode_sqlite_value_ref(
                row.get_ref(index).map_err(sqlite_error)?,
                read_bigints,
            )?);
        }
        return Ok(Value::Array(values));
    }

    let mut object = Map::with_capacity(column_count);
    for (index, name) in column_names.iter().enumerate() {
        object.insert(
            name.clone(),
            encode_sqlite_value_ref(row.get_ref(index).map_err(sqlite_error)?, read_bigints)?,
        );
    }
    Ok(Value::Object(object))
}

fn encode_sqlite_value_ref(
    value: SqliteValueRef<'_>,
    read_bigints: bool,
) -> Result<Value, SidecarError> {
    Ok(match value {
        SqliteValueRef::Null => Value::Null,
        SqliteValueRef::Integer(number) => encode_sqlite_integer(number, read_bigints),
        SqliteValueRef::Real(number) => json!(number),
        SqliteValueRef::Text(text) => Value::String(String::from_utf8_lossy(text).into_owned()),
        SqliteValueRef::Blob(bytes) => json!({
            "__agentosSqliteType": "uint8array",
            "value": base64::engine::general_purpose::STANDARD.encode(bytes),
        }),
    })
}

fn encode_sqlite_integer(number: i64, read_bigints: bool) -> Value {
    if read_bigints || number.abs() > SQLITE_JS_SAFE_INTEGER_MAX {
        json!({
            "__agentosSqliteType": "bigint",
            "value": number.to_string(),
        })
    } else {
        json!(number)
    }
}

fn bind_sqlite_parameters(
    statement: &mut SqliteStatement<'_>,
    params: Option<&Value>,
    allow_bare_named_parameters: bool,
    allow_unknown_named_parameters: bool,
) -> Result<(), SidecarError> {
    let Some(params) = params else {
        return Ok(());
    };
    match params {
        Value::Null => Ok(()),
        Value::Array(values) => {
            for (index, value) in values.iter().enumerate() {
                statement
                    .raw_bind_parameter(index + 1, decode_sqlite_parameter(value)?)
                    .map_err(sqlite_error)?;
            }
            Ok(())
        }
        Value::Object(map)
            if map
                .get("__agentosSqliteType")
                .and_then(Value::as_str)
                .is_none() =>
        {
            for (key, value) in map {
                let index =
                    resolve_sqlite_parameter_index(statement, key, allow_bare_named_parameters)?;
                let Some(index) = index else {
                    if allow_unknown_named_parameters {
                        continue;
                    }
                    return Err(SidecarError::InvalidState(format!(
                        "sqlite named parameter not found: {key}"
                    )));
                };
                statement
                    .raw_bind_parameter(index, decode_sqlite_parameter(value)?)
                    .map_err(sqlite_error)?;
            }
            Ok(())
        }
        other => statement
            .raw_bind_parameter(1, decode_sqlite_parameter(other)?)
            .map_err(sqlite_error),
    }
}

fn resolve_sqlite_parameter_index(
    statement: &mut SqliteStatement<'_>,
    key: &str,
    allow_bare_named_parameters: bool,
) -> Result<Option<usize>, SidecarError> {
    let mut candidates = vec![key.to_owned()];
    if allow_bare_named_parameters
        && !key.starts_with(':')
        && !key.starts_with('@')
        && !key.starts_with('$')
    {
        candidates.push(format!(":{key}"));
        candidates.push(format!("@{key}"));
        candidates.push(format!("${key}"));
    }
    for candidate in candidates {
        if let Some(index) = statement
            .parameter_index(&candidate)
            .map_err(sqlite_error)?
        {
            return Ok(Some(index));
        }
    }
    Ok(None)
}

fn decode_sqlite_parameter(value: &Value) -> Result<rusqlite::types::Value, SidecarError> {
    Ok(match value {
        Value::Null => rusqlite::types::Value::Null,
        Value::Bool(value) => rusqlite::types::Value::Integer(i64::from(*value)),
        Value::Number(value) => match (value.as_i64(), value.as_f64()) {
            (Some(integer), _) => rusqlite::types::Value::Integer(integer),
            (_, Some(real)) => rusqlite::types::Value::Real(real),
            _ => {
                return Err(SidecarError::InvalidState(String::from(
                    "sqlite parameter number is not representable",
                )));
            }
        },
        Value::String(value) => rusqlite::types::Value::Text(value.clone()),
        Value::Array(_) => {
            return Err(SidecarError::InvalidState(String::from(
                "sqlite parameters do not support nested arrays",
            )));
        }
        Value::Object(map) => match map.get("__agentosSqliteType").and_then(Value::as_str) {
            Some("bigint") => rusqlite::types::Value::Integer(
                map.get("value")
                    .and_then(Value::as_str)
                    .ok_or_else(|| {
                        SidecarError::InvalidState(String::from(
                            "sqlite bigint parameter missing string value",
                        ))
                    })?
                    .parse::<i64>()
                    .map_err(|error| {
                        SidecarError::InvalidState(format!(
                            "sqlite bigint parameter is not a signed 64-bit integer: {error}"
                        ))
                    })?,
            ),
            Some("uint8array") => rusqlite::types::Value::Blob(
                base64::engine::general_purpose::STANDARD
                    .decode(map.get("value").and_then(Value::as_str).ok_or_else(|| {
                        SidecarError::InvalidState(String::from(
                            "sqlite blob parameter missing base64 value",
                        ))
                    })?)
                    .map_err(|error| {
                        SidecarError::InvalidState(format!(
                            "sqlite blob parameter contains invalid base64: {error}"
                        ))
                    })?,
            ),
            Some(other) => {
                return Err(SidecarError::InvalidState(format!(
                    "unsupported sqlite tagged parameter type {other}"
                )));
            }
            None => {
                return Err(SidecarError::InvalidState(String::from(
                    "sqlite named parameter objects must be passed as the top-level params object",
                )));
            }
        },
    })
}

fn close_sqlite_database(
    kernel: &mut SidecarKernel,
    process: &mut ActiveProcess,
    database_id: u64,
) -> Result<(), SidecarError> {
    let mut database = process
        .sqlite_databases
        .remove(&database_id)
        .ok_or_else(|| {
            SidecarError::InvalidState(format!("sqlite database handle not found: {database_id}"))
        })?;
    process
        .sqlite_statements
        .retain(|_, statement| statement.database_id != database_id);
    sqlite_sync_database(kernel, process.kernel_pid, &mut database)?;
    let host_path = database.host_path.clone();
    drop(database);
    cleanup_sqlite_host_artifacts(host_path.as_deref())?;
    Ok(())
}

fn ensure_per_process_state_handle_capacity(len: usize, label: &str) -> Result<(), SidecarError> {
    if len >= MAX_PER_PROCESS_STATE_HANDLES {
        return Err(SidecarError::InvalidState(format!(
            "{label} handle limit exceeded: limit is {MAX_PER_PROCESS_STATE_HANDLES}"
        )));
    }
    Ok(())
}

fn sqlite_sync_database(
    kernel: &mut SidecarKernel,
    kernel_pid: u32,
    database: &mut ActiveSqliteDatabase,
) -> Result<(), SidecarError> {
    if !database.dirty
        || database.transaction_depth > 0
        || database.read_only
        || database.host_path.is_none()
        || database.vm_path.is_none()
    {
        return Ok(());
    }

    let _ = database
        .connection
        .execute_batch("PRAGMA wal_checkpoint(TRUNCATE)");
    let host_path = database.host_path.as_ref().expect("sqlite host path");
    if !host_path.exists() {
        return Ok(());
    }
    ensure_vm_parent_dir(
        kernel,
        kernel_pid,
        database.vm_path.as_deref().expect("sqlite vm path"),
    )?;
    let contents = fs::read(host_path).map_err(|error| {
        SidecarError::Io(format!(
            "failed to read sqlite temp database {}: {error}",
            host_path.display()
        ))
    })?;
    kernel
        .write_file_for_process(
            EXECUTION_DRIVER_NAME,
            kernel_pid,
            database.vm_path.as_deref().expect("sqlite vm path"),
            contents,
            None,
        )
        .map_err(kernel_error)?;
    database.dirty = false;
    Ok(())
}

fn cleanup_sqlite_host_artifacts(host_path: Option<&Path>) -> Result<(), SidecarError> {
    let Some(host_path) = host_path else {
        return Ok(());
    };
    let parent = host_path.parent().map(PathBuf::from);
    for suffix in ["", "-wal", "-shm"] {
        let path = PathBuf::from(format!("{}{}", host_path.display(), suffix));
        if path.exists() {
            fs::remove_file(&path).map_err(|error| {
                SidecarError::Io(format!(
                    "failed to remove sqlite temp artifact {}: {error}",
                    path.display()
                ))
            })?;
        }
    }
    if let Some(parent) = parent {
        let _ = fs::remove_dir_all(parent);
    }
    Ok(())
}

fn ensure_vm_parent_dir(
    kernel: &mut SidecarKernel,
    kernel_pid: u32,
    path: &str,
) -> Result<(), SidecarError> {
    let parent = dirname(path);
    if parent == "/" || parent == "." {
        return Ok(());
    }
    let mut current = String::new();
    for segment in parent.split('/').filter(|segment| !segment.is_empty()) {
        current.push('/');
        current.push_str(segment);
        if !kernel
            .exists_for_process(EXECUTION_DRIVER_NAME, kernel_pid, &current)
            .map_err(kernel_error)?
        {
            kernel
                .mkdir_for_process(EXECUTION_DRIVER_NAME, kernel_pid, &current, false, None)
                .map_err(kernel_error)?;
        }
    }
    Ok(())
}

fn sqlite_database(
    process: &ActiveProcess,
    database_id: u64,
) -> Result<&ActiveSqliteDatabase, SidecarError> {
    process.sqlite_databases.get(&database_id).ok_or_else(|| {
        SidecarError::InvalidState(format!("sqlite database handle not found: {database_id}"))
    })
}

fn sqlite_database_mut(
    process: &mut ActiveProcess,
    database_id: u64,
) -> Result<&mut ActiveSqliteDatabase, SidecarError> {
    process
        .sqlite_databases
        .get_mut(&database_id)
        .ok_or_else(|| {
            SidecarError::InvalidState(format!("sqlite database handle not found: {database_id}"))
        })
}

fn sqlite_statement(
    process: &ActiveProcess,
    statement_id: u64,
) -> Result<&ActiveSqliteStatement, SidecarError> {
    process.sqlite_statements.get(&statement_id).ok_or_else(|| {
        SidecarError::InvalidState(format!("sqlite statement handle not found: {statement_id}"))
    })
}

fn sqlite_statement_mut(
    process: &mut ActiveProcess,
    statement_id: u64,
) -> Result<&mut ActiveSqliteStatement, SidecarError> {
    process
        .sqlite_statements
        .get_mut(&statement_id)
        .ok_or_else(|| {
            SidecarError::InvalidState(format!("sqlite statement handle not found: {statement_id}"))
        })
}

fn mark_sqlite_mutation(database: &mut ActiveSqliteDatabase, sql: &str) {
    let normalized = sql.trim_start().to_ascii_lowercase();
    if normalized.starts_with("begin") || normalized.starts_with("savepoint") {
        database.dirty = true;
        database.transaction_depth += 1;
        return;
    }
    if normalized.starts_with("commit") || normalized.starts_with("release savepoint") {
        database.dirty = true;
        database.transaction_depth = database.transaction_depth.saturating_sub(1);
        return;
    }
    if normalized.starts_with("rollback") && !normalized.starts_with("rollback to") {
        database.dirty = true;
        database.transaction_depth = database.transaction_depth.saturating_sub(1);
        return;
    }
    if normalized.starts_with("insert")
        || normalized.starts_with("update")
        || normalized.starts_with("delete")
        || normalized.starts_with("replace")
        || normalized.starts_with("create")
        || normalized.starts_with("alter")
        || normalized.starts_with("drop")
        || normalized.starts_with("vacuum")
        || normalized.starts_with("reindex")
        || normalized.starts_with("analyze")
        || normalized.starts_with("attach")
        || normalized.starts_with("detach")
        || normalized.starts_with("pragma")
    {
        database.dirty = true;
    }
}

fn sqlite_option_bool(options: Option<&Value>, key: &str) -> Option<bool> {
    options
        .and_then(|value| value.get(key))
        .and_then(Value::as_bool)
}

fn sqlite_option_u64(options: Option<&Value>, key: &str) -> Option<u64> {
    options
        .and_then(|value| value.get(key))
        .and_then(Value::as_u64)
}

fn sqlite_error(error: rusqlite::Error) -> SidecarError {
    SidecarError::InvalidState(format!("sqlite error: {error}"))
}

pub(crate) fn javascript_sync_rpc_arg_str<'a>(
    args: &'a [Value],
    index: usize,
    label: &str,
) -> Result<&'a str, SidecarError> {
    args.get(index)
        .and_then(Value::as_str)
        .ok_or_else(|| SidecarError::InvalidState(format!("{label} must be a string argument")))
}

pub(crate) fn javascript_sync_rpc_arg_bool(
    args: &[Value],
    index: usize,
    label: &str,
) -> Result<bool, SidecarError> {
    args.get(index)
        .and_then(Value::as_bool)
        .ok_or_else(|| SidecarError::InvalidState(format!("{label} must be a boolean argument")))
}

pub(crate) fn javascript_sync_rpc_encoding(args: &[Value]) -> Option<String> {
    args.get(1).and_then(|value| {
        value.as_str().map(str::to_owned).or_else(|| {
            value
                .get("encoding")
                .and_then(Value::as_str)
                .map(str::to_owned)
        })
    })
}

pub(crate) fn javascript_sync_rpc_option_bool(
    args: &[Value],
    index: usize,
    key: &str,
) -> Option<bool> {
    let value = args.get(index)?;
    if key == "recursive" {
        if let Some(boolean) = value.as_bool() {
            return Some(boolean);
        }
    }
    value.get(key).and_then(Value::as_bool)
}

pub(crate) fn javascript_sync_rpc_option_u32(
    args: &[Value],
    index: usize,
    key: &str,
) -> Result<Option<u32>, SidecarError> {
    let Some(value) = args.get(index).and_then(|value| {
        if value.is_object() {
            value.get(key)
        } else if key == "mode" && value.is_number() {
            Some(value)
        } else {
            None
        }
    }) else {
        return Ok(None);
    };
    if value.is_null() {
        return Ok(None);
    }

    let numeric = value
        .as_u64()
        .or_else(|| {
            value
                .as_f64()
                .filter(|number| number.is_finite() && *number >= 0.0)
                .map(|number| number as u64)
        })
        .ok_or_else(|| SidecarError::InvalidState(format!("{key} must be numeric")))?;

    u32::try_from(numeric)
        .map(Some)
        .map_err(|_| SidecarError::InvalidState(format!("{key} must fit within u32")))
}

pub(crate) fn javascript_sync_rpc_arg_u32(
    args: &[Value],
    index: usize,
    label: &str,
) -> Result<u32, SidecarError> {
    let value = javascript_sync_rpc_arg_u64(args, index, label)?;
    u32::try_from(value)
        .map_err(|_| SidecarError::InvalidState(format!("{label} must fit within u32")))
}

pub(crate) fn javascript_sync_rpc_arg_i32(
    args: &[Value],
    index: usize,
    label: &str,
) -> Result<i32, SidecarError> {
    let Some(value) = args.get(index) else {
        return Err(SidecarError::InvalidState(format!("{label} is required")));
    };

    let numeric = value
        .as_i64()
        .or_else(|| {
            value
                .as_f64()
                .filter(|number| number.is_finite())
                .map(|number| number as i64)
        })
        .ok_or_else(|| SidecarError::InvalidState(format!("{label} must be a numeric argument")))?;

    i32::try_from(numeric)
        .map_err(|_| SidecarError::InvalidState(format!("{label} must fit within i32")))
}

pub(crate) fn javascript_sync_rpc_arg_u32_optional(
    args: &[Value],
    index: usize,
    label: &str,
) -> Result<Option<u32>, SidecarError> {
    javascript_sync_rpc_arg_u64_optional(args, index, label)?
        .map(|value| {
            u32::try_from(value)
                .map_err(|_| SidecarError::InvalidState(format!("{label} must fit within u32")))
        })
        .transpose()
}

pub(crate) fn javascript_sync_rpc_arg_u64(
    args: &[Value],
    index: usize,
    label: &str,
) -> Result<u64, SidecarError> {
    let Some(value) = args.get(index) else {
        return Err(SidecarError::InvalidState(format!("{label} is required")));
    };

    value
        .as_u64()
        .or_else(|| {
            value
                .as_f64()
                .filter(|number| number.is_finite() && *number >= 0.0)
                .map(|number| number as u64)
        })
        .ok_or_else(|| SidecarError::InvalidState(format!("{label} must be a numeric argument")))
}

pub(crate) fn javascript_sync_rpc_arg_u64_optional(
    args: &[Value],
    index: usize,
    label: &str,
) -> Result<Option<u64>, SidecarError> {
    let Some(value) = args.get(index) else {
        return Ok(None);
    };
    if value.is_null() {
        return Ok(None);
    }
    javascript_sync_rpc_arg_u64(args, index, label).map(Some)
}

pub(crate) fn javascript_sync_rpc_bytes_arg(
    args: &[Value],
    index: usize,
    label: &str,
) -> Result<Vec<u8>, SidecarError> {
    let Some(value) = args.get(index) else {
        return Err(SidecarError::InvalidState(format!("{label} is required")));
    };

    if let Some(text) = value.as_str() {
        return Ok(text.as_bytes().to_vec());
    }

    let Some(base64_value) = value
        .get("__agentOSType")
        .and_then(Value::as_str)
        .filter(|kind| *kind == "bytes")
        .and_then(|_| value.get("base64"))
        .and_then(Value::as_str)
    else {
        return Err(SidecarError::InvalidState(format!(
            "{label} must be a string or encoded bytes payload"
        )));
    };

    base64::engine::general_purpose::STANDARD
        .decode(base64_value)
        .map_err(|error| {
            SidecarError::InvalidState(format!("{label} contains invalid base64: {error}"))
        })
}

pub(crate) fn javascript_sync_rpc_bytes_value(bytes: &[u8]) -> Value {
    json!({
        "__agentOSType": "bytes",
        "base64": base64::engine::general_purpose::STANDARD.encode(bytes),
    })
}

#[derive(Debug, Deserialize)]
struct KernelPollFdRequest {
    fd: u32,
    events: u16,
}

#[derive(Debug, Deserialize, Serialize, PartialEq, Eq)]
struct KernelPollFdResponse {
    fd: u32,
    events: u16,
    revents: u16,
}

fn javascript_sync_rpc_base64_arg(
    args: &[Value],
    index: usize,
    label: &str,
) -> Result<Vec<u8>, SidecarError> {
    let value = javascript_sync_rpc_arg_str(args, index, label)?;
    base64::engine::general_purpose::STANDARD
        .decode(value)
        .map_err(|error| {
            SidecarError::InvalidState(format!("{label} contains invalid base64: {error}"))
        })
}

pub(crate) fn service_javascript_sync_rpc<B>(
    request: JavascriptSyncRpcServiceRequest<'_, B>,
) -> Result<Value, SidecarError>
where
    B: NativeSidecarBridge + Send + 'static,
    BridgeError<B>: fmt::Debug + Send + Sync + 'static,
{
    let JavascriptSyncRpcServiceRequest {
        bridge,
        vm_id,
        dns,
        socket_paths,
        kernel,
        process,
        sync_request: request,
        resource_limits,
        network_counts,
    } = request;
    match request.method.as_str() {
        // Module resolution / loading / format detection read the kernel VFS so
        // the resolver sees exactly what the guest and `kernel.readFile()` see.
        "_resolveModule"
        | "_resolveModuleSync"
        | "__resolve_module"
        | "_batchResolveModules"
        | "__batch_resolve_modules"
        | "_loadFile"
        | "_loadFileSync"
        | "__load_file"
        | "_moduleFormat"
        | "__module_format" => service_javascript_module_sync_rpc(kernel, process, request),
        // Polyfills are static guest expressions, not VFS reads.
        "_loadPolyfill" | "__load_polyfill" => {
            service_javascript_internal_bridge_sync_rpc(process, request)
        }
        "__kernel_stdin_read" => match &process.execution {
            ActiveExecution::Javascript(execution) => execution
                .read_kernel_stdin_sync_rpc(request)
                .map_err(|error| SidecarError::Execution(error.to_string())),
            ActiveExecution::Python(_) | ActiveExecution::Wasm(_) | ActiveExecution::Tool(_) => {
                service_javascript_kernel_stdin_sync_rpc(kernel, process, request)
            }
        },
        "__kernel_stdio_write" => {
            service_javascript_kernel_stdio_write_sync_rpc(kernel, process, request)
        }
        "__kernel_poll" => service_javascript_kernel_poll_sync_rpc(kernel, process, request),
        "__pty_set_raw_mode" => {
            service_javascript_pty_set_raw_mode_sync_rpc(kernel, process, request)
        }
        "crypto.hashDigest"
        | "crypto.hmacDigest"
        | "crypto.pbkdf2"
        | "crypto.scrypt"
        | "crypto.cipheriv"
        | "crypto.decipheriv"
        | "crypto.cipherivCreate"
        | "crypto.cipherivUpdate"
        | "crypto.cipherivFinal"
        | "crypto.sign"
        | "crypto.verify"
        | "crypto.asymmetricOp"
        | "crypto.createKeyObject"
        | "crypto.generateKeyPairSync"
        | "crypto.generateKeySync"
        | "crypto.generatePrimeSync"
        | "crypto.diffieHellman"
        | "crypto.diffieHellmanGroup"
        | "crypto.diffieHellmanSessionCreate"
        | "crypto.diffieHellmanSessionCall"
        | "crypto.diffieHellmanSessionDestroy"
        | "crypto.subtle" => service_javascript_crypto_sync_rpc(process, request),
        "dns.lookup" | "dns.resolve" | "dns.resolve4" | "dns.resolve6" => {
            service_javascript_dns_sync_rpc(bridge, kernel, vm_id, dns, request)
        }
        "net.http_listen" | "net.http_close" | "net.http_wait" | "net.http_respond" => {
            service_javascript_net_sync_rpc(JavascriptNetSyncRpcServiceRequest {
                bridge,
                vm_id,
                dns,
                socket_paths,
                kernel,
                process,
                sync_request: request,
                resource_limits,
                network_counts,
            })
        }
        "net.http2_server_listen"
        | "net.http2_server_poll"
        | "net.http2_server_close"
        | "net.http2_server_respond"
        | "net.http2_server_wait"
        | "net.http2_session_connect"
        | "net.http2_session_request"
        | "net.http2_session_settings"
        | "net.http2_session_set_local_window_size"
        | "net.http2_session_goaway"
        | "net.http2_session_close"
        | "net.http2_session_destroy"
        | "net.http2_session_poll"
        | "net.http2_session_wait"
        | "net.http2_stream_respond"
        | "net.http2_stream_push_stream"
        | "net.http2_stream_write"
        | "net.http2_stream_end"
        | "net.http2_stream_close"
        | "net.http2_stream_pause"
        | "net.http2_stream_resume"
        | "net.http2_stream_respond_with_file" => {
            service_javascript_http2_sync_rpc(JavascriptHttp2SyncRpcServiceRequest {
                bridge,
                kernel,
                vm_id,
                dns,
                socket_paths,
                process,
                sync_request: request,
                resource_limits,
                network_counts,
            })
        }
        "net.connect"
        | "net.reserve_tcp_port"
        | "net.release_tcp_port"
        | "net.listen"
        | "net.poll"
        | "net.socket_wait_connect"
        | "net.socket_read"
        | "net.socket_set_no_delay"
        | "net.socket_set_keep_alive"
        | "net.socket_upgrade_tls"
        | "net.socket_get_tls_client_hello"
        | "net.socket_tls_query"
        | "net.server_poll"
        | "net.server_accept"
        | "net.server_connections"
        | "net.upgrade_socket_write"
        | "net.upgrade_socket_end"
        | "net.upgrade_socket_destroy"
        | "net.write"
        | "net.shutdown"
        | "net.destroy"
        | "net.server_close"
        | "tls.get_ciphers" => {
            service_javascript_net_sync_rpc(JavascriptNetSyncRpcServiceRequest {
                bridge,
                vm_id,
                dns,
                socket_paths,
                kernel,
                process,
                sync_request: request,
                resource_limits,
                network_counts,
            })
        }
        "dgram.createSocket"
        | "dgram.bind"
        | "dgram.send"
        | "dgram.poll"
        | "dgram.close"
        | "dgram.address"
        | "dgram.setBufferSize"
        | "dgram.getBufferSize" => {
            service_javascript_dgram_sync_rpc(JavascriptDgramSyncRpcServiceRequest {
                bridge,
                kernel,
                vm_id,
                dns,
                socket_paths,
                process,
                sync_request: request,
                resource_limits,
                network_counts,
            })
        }
        "sqlite.constants"
        | "sqlite.open"
        | "sqlite.close"
        | "sqlite.exec"
        | "sqlite.query"
        | "sqlite.prepare"
        | "sqlite.location"
        | "sqlite.checkpoint"
        | "sqlite.statement.run"
        | "sqlite.statement.get"
        | "sqlite.statement.all"
        | "sqlite.statement.iterate"
        | "sqlite.statement.columns"
        | "sqlite.statement.setReturnArrays"
        | "sqlite.statement.setReadBigInts"
        | "sqlite.statement.setAllowBareNamedParameters"
        | "sqlite.statement.setAllowUnknownNamedParameters"
        | "sqlite.statement.finalize" => {
            service_javascript_sqlite_sync_rpc(kernel, process, request)
        }
        "process.kill" => {
            let target_pid =
                javascript_sync_rpc_arg_i32(&request.args, 0, "process.kill target pid")?;
            let signal = javascript_sync_rpc_arg_str(&request.args, 1, "process.kill signal")?;
            let parsed_signal = parse_signal(signal)?;
            if parsed_signal == 0 {
                kernel
                    .signal_process(EXECUTION_DRIVER_NAME, target_pid, parsed_signal)
                    .map_err(kernel_error)?;
                return Ok(Value::Null);
            }
            let process_pid = i32::try_from(process.kernel_pid)
                .map_err(|_| SidecarError::InvalidState("process pid exceeds i32".into()))?;
            if target_pid != process_pid {
                return Err(SidecarError::InvalidState(format!(
                    "unknown process pid {target_pid}"
                )));
            }
            process.pending_self_signal_exit = None;
            if parsed_signal != 0
                && !matches!(
                    canonical_signal_name(parsed_signal),
                    Some("SIGWINCH" | "SIGCHLD" | "SIGCONT" | "SIGURG")
                )
            {
                process.pending_self_signal_exit = Some(parsed_signal);
            }
            Ok(json!({
                "self": true,
                "action": "default",
            }))
        }
        "process.umask" => {
            let new_mask = javascript_sync_rpc_arg_u32_optional(&request.args, 0, "process umask")?;
            kernel
                .umask(EXECUTION_DRIVER_NAME, process.kernel_pid, new_mask)
                .map(|mask| json!(mask))
                .map_err(kernel_error)
        }
        "fs.chmodSync" | "fs.promises.chmod" => {
            let response =
                service_javascript_fs_sync_rpc(kernel, process, process.kernel_pid, request)?;
            mirror_process_chmod_to_host(process, request)?;
            Ok(response)
        }
        _ => service_javascript_fs_sync_rpc(kernel, process, process.kernel_pid, request),
    }
}

fn service_javascript_internal_bridge_sync_rpc(
    process: &ActiveProcess,
    request: &JavascriptSyncRpcRequest,
) -> Result<Value, SidecarError> {
    // Module resolution / loading / format now reads the kernel VFS via
    // `service_javascript_module_sync_rpc`. This host-context path only handles
    // polyfills, which are static guest expressions independent of the FS.
    let method = match request.method.as_str() {
        "_loadPolyfill" | "__load_polyfill" => "_loadPolyfill",
        other => {
            return Err(SidecarError::InvalidState(format!(
                "unsupported JavaScript internal bridge method {other}"
            )));
        }
    };

    handle_internal_bridge_call_from_host_context(
        &process.host_cwd,
        &process.guest_cwd,
        &process.env,
        method,
        &request.args,
    )
    .ok_or_else(|| {
        SidecarError::InvalidState(format!(
            "JavaScript internal bridge method {method} returned no value"
        ))
    })
}

fn mirror_process_chmod_to_host(
    process: &ActiveProcess,
    request: &JavascriptSyncRpcRequest,
) -> Result<(), SidecarError> {
    let guest_path = javascript_sync_rpc_arg_str(&request.args, 0, "filesystem chmod path")?;
    let mode = javascript_sync_rpc_arg_u32(&request.args, 1, "filesystem chmod mode")? & 0o7777;
    let Some(host_path) = resolve_process_guest_path_to_host(process, guest_path) else {
        return Ok(());
    };
    if !host_path.exists() {
        return Ok(());
    }
    fs::set_permissions(&host_path, fs::Permissions::from_mode(mode)).map_err(|error| {
        SidecarError::Io(format!(
            "failed to mirror chmod to host path {}: {error}",
            host_path.display()
        ))
    })
}

fn resolve_process_guest_path_to_host(
    process: &ActiveProcess,
    guest_path: &str,
) -> Option<PathBuf> {
    let normalized_guest_path = if guest_path.starts_with('/') {
        normalize_path(guest_path)
    } else {
        normalize_path(&format!(
            "{}/{}",
            process.guest_cwd.trim_end_matches('/'),
            guest_path
        ))
    };
    if let Some(host_path) =
        host_path_from_runtime_guest_mappings(&process.env, &normalized_guest_path)
    {
        return Some(host_path);
    }
    let normalized_guest_cwd = normalize_path(&process.guest_cwd);
    let mut host_root = normalize_host_path(&process.host_cwd);
    for _ in normalized_guest_cwd
        .trim_start_matches('/')
        .split('/')
        .filter(|segment| !segment.is_empty())
    {
        host_root = host_root.parent()?.to_path_buf();
    }
    if normalized_guest_path == "/" {
        Some(host_root)
    } else {
        Some(host_root.join(normalized_guest_path.trim_start_matches('/')))
    }
}

pub(crate) fn service_javascript_crypto_sync_rpc(
    process: &mut ActiveProcess,
    request: &JavascriptSyncRpcRequest,
) -> Result<Value, SidecarError> {
    match request.method.as_str() {
        "crypto.hashDigest" => {
            let algorithm = javascript_crypto_digest_algorithm(
                &request.args,
                0,
                "crypto.hashDigest algorithm",
            )?;
            let data = javascript_sync_rpc_base64_arg(&request.args, 1, "crypto.hashDigest data")?;
            Ok(Value::String(
                base64::engine::general_purpose::STANDARD.encode(algorithm.digest(&data)),
            ))
        }
        "crypto.hmacDigest" => {
            let algorithm = javascript_crypto_digest_algorithm(
                &request.args,
                0,
                "crypto.hmacDigest algorithm",
            )?;
            let key = javascript_sync_rpc_base64_arg(&request.args, 1, "crypto.hmacDigest key")?;
            let data = javascript_sync_rpc_base64_arg(&request.args, 2, "crypto.hmacDigest data")?;
            Ok(Value::String(
                base64::engine::general_purpose::STANDARD.encode(algorithm.hmac(&key, &data)?),
            ))
        }
        "crypto.pbkdf2" => {
            let password =
                javascript_sync_rpc_base64_arg(&request.args, 0, "crypto.pbkdf2 password")?;
            let salt = javascript_sync_rpc_base64_arg(&request.args, 1, "crypto.pbkdf2 salt")?;
            let iterations =
                javascript_sync_rpc_arg_u32(&request.args, 2, "crypto.pbkdf2 iterations")?;
            if iterations == 0 {
                return Err(SidecarError::InvalidState(String::from(
                    "crypto.pbkdf2 iterations must be greater than zero",
                )));
            }
            let key_len = usize::try_from(javascript_sync_rpc_arg_u64(
                &request.args,
                3,
                "crypto.pbkdf2 key length",
            )?)
            .map_err(|_| {
                SidecarError::InvalidState(String::from(
                    "crypto.pbkdf2 key length must fit within usize",
                ))
            })?;
            let algorithm =
                javascript_crypto_digest_algorithm(&request.args, 4, "crypto.pbkdf2 digest")?;
            let mut output = vec![0u8; key_len];
            algorithm.pbkdf2(&password, &salt, iterations, &mut output);
            Ok(Value::String(
                base64::engine::general_purpose::STANDARD.encode(output),
            ))
        }
        "crypto.scrypt" => {
            let password =
                javascript_sync_rpc_base64_arg(&request.args, 0, "crypto.scrypt password")?;
            let salt = javascript_sync_rpc_base64_arg(&request.args, 1, "crypto.scrypt salt")?;
            let key_len = usize::try_from(javascript_sync_rpc_arg_u64(
                &request.args,
                2,
                "crypto.scrypt key length",
            )?)
            .map_err(|_| {
                SidecarError::InvalidState(String::from(
                    "crypto.scrypt key length must fit within usize",
                ))
            })?;
            let options_json =
                javascript_sync_rpc_arg_str(&request.args, 3, "crypto.scrypt options")?;
            let options: JavascriptScryptOptions =
                serde_json::from_str(options_json).map_err(|error| {
                    SidecarError::InvalidState(format!(
                        "crypto.scrypt options must be valid JSON: {error}"
                    ))
                })?;
            let cost = options.cost.unwrap_or(DEFAULT_SCRYPT_COST);
            if cost == 0 || !cost.is_power_of_two() {
                return Err(SidecarError::InvalidState(String::from(
                    "crypto.scrypt cost must be a positive power of two",
                )));
            }
            let log_n = u8::try_from(cost.ilog2()).map_err(|_| {
                SidecarError::InvalidState(String::from(
                    "crypto.scrypt cost exceeds supported parameter range",
                ))
            })?;
            let params = ScryptParams::new(
                log_n,
                options.block_size.unwrap_or(DEFAULT_SCRYPT_BLOCK_SIZE),
                options
                    .parallelization
                    .unwrap_or(DEFAULT_SCRYPT_PARALLELIZATION),
                key_len,
            )
            .map_err(|error| {
                SidecarError::InvalidState(format!("crypto.scrypt options are invalid: {error}"))
            })?;
            let mut output = vec![0u8; key_len];
            scrypt(&password, &salt, &params, &mut output).map_err(|error| {
                SidecarError::Execution(format!("crypto.scrypt failed: {error}"))
            })?;
            Ok(Value::String(
                base64::engine::general_purpose::STANDARD.encode(output),
            ))
        }
        "crypto.cipheriv" => service_javascript_crypto_cipheriv_sync_rpc(request),
        "crypto.decipheriv" => service_javascript_crypto_decipheriv_sync_rpc(request),
        "crypto.cipherivCreate" => {
            service_javascript_crypto_cipheriv_create_sync_rpc(process, request)
        }
        "crypto.cipherivUpdate" => {
            service_javascript_crypto_cipheriv_update_sync_rpc(process, request)
        }
        "crypto.cipherivFinal" => {
            service_javascript_crypto_cipheriv_final_sync_rpc(process, request)
        }
        "crypto.sign" => service_javascript_crypto_sign_sync_rpc(request),
        "crypto.verify" => service_javascript_crypto_verify_sync_rpc(request),
        "crypto.asymmetricOp" => service_javascript_crypto_asymmetric_op_sync_rpc(request),
        "crypto.createKeyObject" => service_javascript_crypto_create_key_object_sync_rpc(request),
        "crypto.generateKeyPairSync" => {
            service_javascript_crypto_generate_key_pair_sync_rpc(request)
        }
        "crypto.generateKeySync" => service_javascript_crypto_generate_key_sync_rpc(request),
        "crypto.generatePrimeSync" => service_javascript_crypto_generate_prime_sync_rpc(request),
        "crypto.diffieHellman" => service_javascript_crypto_diffie_hellman_sync_rpc(request),
        "crypto.diffieHellmanGroup" => {
            service_javascript_crypto_diffie_hellman_group_sync_rpc(request)
        }
        "crypto.diffieHellmanSessionCreate" => {
            service_javascript_crypto_diffie_hellman_session_create_sync_rpc(process, request)
        }
        "crypto.diffieHellmanSessionCall" => {
            service_javascript_crypto_diffie_hellman_session_call_sync_rpc(process, request)
        }
        "crypto.diffieHellmanSessionDestroy" => {
            service_javascript_crypto_diffie_hellman_session_destroy_sync_rpc(process, request)
        }
        "crypto.subtle" => service_javascript_crypto_subtle_sync_rpc(request),
        _ => Err(SidecarError::InvalidState(format!(
            "unsupported JavaScript crypto sync RPC method {}",
            request.method
        ))),
    }
}

fn javascript_crypto_digest_algorithm(
    args: &[Value],
    index: usize,
    label: &str,
) -> Result<JavascriptCryptoDigestAlgorithm, SidecarError> {
    JavascriptCryptoDigestAlgorithm::parse(javascript_sync_rpc_arg_str(args, index, label)?)
}

impl JavascriptCryptoDigestAlgorithm {
    fn parse(value: &str) -> Result<Self, SidecarError> {
        match value.trim().to_ascii_lowercase().replace('-', "").as_str() {
            "md5" => Ok(Self::Md5),
            "sha1" => Ok(Self::Sha1),
            "sha256" => Ok(Self::Sha256),
            "sha512" => Ok(Self::Sha512),
            _ => Err(SidecarError::InvalidState(format!(
                "unsupported crypto digest algorithm {value}"
            ))),
        }
    }

    fn digest(self, data: &[u8]) -> Vec<u8> {
        match self {
            Self::Md5 => Md5::digest(data).to_vec(),
            Self::Sha1 => Sha1::digest(data).to_vec(),
            Self::Sha256 => Sha256::digest(data).to_vec(),
            Self::Sha512 => Sha512::digest(data).to_vec(),
        }
    }

    fn hmac(self, key: &[u8], data: &[u8]) -> Result<Vec<u8>, SidecarError> {
        match self {
            Self::Md5 => {
                let mut mac = Hmac::<Md5>::new_from_slice(key).map_err(|error| {
                    SidecarError::InvalidState(format!("invalid HMAC key: {error}"))
                })?;
                mac.update(data);
                Ok(mac.finalize().into_bytes().to_vec())
            }
            Self::Sha1 => {
                let mut mac = Hmac::<Sha1>::new_from_slice(key).map_err(|error| {
                    SidecarError::InvalidState(format!("invalid HMAC key: {error}"))
                })?;
                mac.update(data);
                Ok(mac.finalize().into_bytes().to_vec())
            }
            Self::Sha256 => {
                let mut mac = Hmac::<Sha256>::new_from_slice(key).map_err(|error| {
                    SidecarError::InvalidState(format!("invalid HMAC key: {error}"))
                })?;
                mac.update(data);
                Ok(mac.finalize().into_bytes().to_vec())
            }
            Self::Sha512 => {
                let mut mac = Hmac::<Sha512>::new_from_slice(key).map_err(|error| {
                    SidecarError::InvalidState(format!("invalid HMAC key: {error}"))
                })?;
                mac.update(data);
                Ok(mac.finalize().into_bytes().to_vec())
            }
        }
    }

    fn pbkdf2(self, password: &[u8], salt: &[u8], iterations: u32, output: &mut [u8]) {
        match self {
            Self::Md5 => pbkdf2_hmac::<Md5>(password, salt, iterations, output),
            Self::Sha1 => pbkdf2_hmac::<Sha1>(password, salt, iterations, output),
            Self::Sha256 => pbkdf2_hmac::<Sha256>(password, salt, iterations, output),
            Self::Sha512 => pbkdf2_hmac::<Sha512>(password, salt, iterations, output),
        }
    }
}

#[derive(Debug, Clone)]
enum JavascriptCryptoKeyMaterial {
    Private(PKey<Private>),
    Public(PKey<Public>),
    Secret(Vec<u8>),
}

#[derive(Debug, Clone, Deserialize, Serialize)]
struct JavascriptSerializedSandboxKeyObject {
    #[serde(rename = "type")]
    kind: String,
    #[serde(skip_serializing_if = "Option::is_none")]
    pem: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    raw: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none", rename = "asymmetricKeyType")]
    asymmetric_key_type: Option<String>,
    #[serde(
        skip_serializing_if = "Option::is_none",
        rename = "asymmetricKeyDetails"
    )]
    asymmetric_key_details: Option<Map<String, Value>>,
    #[serde(skip_serializing_if = "Option::is_none")]
    jwk: Option<Value>,
}

#[derive(Debug, Clone)]
struct JavascriptDirectKeyInput {
    key: JavascriptCryptoKeyMaterial,
    padding: Option<Padding>,
}

fn service_javascript_crypto_cipheriv_sync_rpc(
    request: &JavascriptSyncRpcRequest,
) -> Result<Value, SidecarError> {
    service_javascript_crypto_cipheriv_inner(request, false)
}

fn service_javascript_crypto_decipheriv_sync_rpc(
    request: &JavascriptSyncRpcRequest,
) -> Result<Value, SidecarError> {
    service_javascript_crypto_cipheriv_inner(request, true)
}

fn service_javascript_crypto_cipheriv_create_sync_rpc(
    process: &mut ActiveProcess,
    request: &JavascriptSyncRpcRequest,
) -> Result<Value, SidecarError> {
    ensure_per_process_state_handle_capacity(process.cipher_sessions.len(), "cipher session")?;
    let mode = javascript_sync_rpc_arg_str(&request.args, 0, "crypto.cipherivCreate mode")?;
    let decrypt = mode == "decipher";
    let algorithm =
        javascript_sync_rpc_arg_str(&request.args, 1, "crypto.cipherivCreate algorithm")?;
    let key = javascript_sync_rpc_base64_arg(&request.args, 2, "crypto.cipherivCreate key")?;
    let iv = javascript_sync_rpc_base64_arg_optional(&request.args, 3, "crypto.cipherivCreate iv")?;
    let options =
        javascript_sync_rpc_json_arg_optional(&request.args, 4, "crypto.cipherivCreate options")?;
    let auth_tag_len = javascript_crypto_requested_aead_tag_len(algorithm, options.as_ref())?;
    let context = javascript_crypto_build_cipher_context(
        algorithm,
        &key,
        iv.as_deref(),
        decrypt,
        options.as_ref(),
    )?;
    process.next_cipher_session_id += 1;
    let session_id = process.next_cipher_session_id;
    process.cipher_sessions.insert(
        session_id,
        ActiveCipherSession {
            algorithm: algorithm.to_string(),
            auth_tag_len,
            context,
        },
    );
    Ok(json!(session_id))
}

fn service_javascript_crypto_cipheriv_update_sync_rpc(
    process: &mut ActiveProcess,
    request: &JavascriptSyncRpcRequest,
) -> Result<Value, SidecarError> {
    let session_id =
        javascript_sync_rpc_arg_u64(&request.args, 0, "crypto.cipherivUpdate session id")?;
    let data = javascript_sync_rpc_base64_arg(&request.args, 1, "crypto.cipherivUpdate data")?;
    let session = process
        .cipher_sessions
        .get_mut(&session_id)
        .ok_or_else(|| {
            SidecarError::InvalidState(format!("Cipher session {session_id} not found"))
        })?;
    let result = javascript_crypto_cipher_update(&mut session.context, &data)?;
    Ok(Value::String(
        base64::engine::general_purpose::STANDARD.encode(result),
    ))
}

fn service_javascript_crypto_cipheriv_final_sync_rpc(
    process: &mut ActiveProcess,
    request: &JavascriptSyncRpcRequest,
) -> Result<Value, SidecarError> {
    let session_id =
        javascript_sync_rpc_arg_u64(&request.args, 0, "crypto.cipherivFinal session id")?;
    let mut session = process.cipher_sessions.remove(&session_id).ok_or_else(|| {
        SidecarError::InvalidState(format!("Cipher session {session_id} not found"))
    })?;
    let data = javascript_crypto_cipher_finalize(&mut session.context)?;
    let mut response = Map::new();
    response.insert(
        String::from("data"),
        Value::String(base64::engine::general_purpose::STANDARD.encode(data)),
    );
    if javascript_crypto_is_aead(&session.algorithm) {
        let mut auth_tag = vec![0_u8; session.auth_tag_len];
        session
            .context
            .get_tag(&mut auth_tag)
            .map_err(javascript_crypto_openssl_error)?;
        response.insert(
            String::from("authTag"),
            Value::String(base64::engine::general_purpose::STANDARD.encode(auth_tag)),
        );
    }
    Ok(Value::String(serde_json::to_string(&response).map_err(
        |error| SidecarError::InvalidState(format!("serialize cipher final response: {error}")),
    )?))
}

fn service_javascript_crypto_sign_sync_rpc(
    request: &JavascriptSyncRpcRequest,
) -> Result<Value, SidecarError> {
    let algorithm = request.args.first().and_then(Value::as_str);
    let data = javascript_sync_rpc_base64_arg(&request.args, 1, "crypto.sign data")?;
    let key_json = javascript_sync_rpc_arg_str(&request.args, 2, "crypto.sign key")?;
    let key_input =
        javascript_crypto_parse_direct_key_input(key_json, Some("private"), "crypto.sign key")?;
    let private_key = javascript_crypto_expect_private_key(key_input.key, "crypto.sign key")?;
    let mut signer = javascript_crypto_new_signer(algorithm, &private_key)?;
    if let Some(padding) = key_input.padding {
        signer
            .set_rsa_padding(padding)
            .map_err(javascript_crypto_openssl_error)?;
    }
    signer
        .update(&data)
        .map_err(javascript_crypto_openssl_error)?;
    Ok(Value::String(
        base64::engine::general_purpose::STANDARD.encode(
            signer
                .sign_to_vec()
                .map_err(javascript_crypto_openssl_error)?,
        ),
    ))
}

fn service_javascript_crypto_verify_sync_rpc(
    request: &JavascriptSyncRpcRequest,
) -> Result<Value, SidecarError> {
    let algorithm = request.args.first().and_then(Value::as_str);
    let data = javascript_sync_rpc_base64_arg(&request.args, 1, "crypto.verify data")?;
    let key_json = javascript_sync_rpc_arg_str(&request.args, 2, "crypto.verify key")?;
    let signature = javascript_sync_rpc_base64_arg(&request.args, 3, "crypto.verify signature")?;
    let key_input =
        javascript_crypto_parse_direct_key_input(key_json, Some("public"), "crypto.verify key")?;
    let public_key = javascript_crypto_expect_public_key(key_input.key, "crypto.verify key")?;
    let mut verifier = javascript_crypto_new_verifier(algorithm, &public_key)?;
    if let Some(padding) = key_input.padding {
        verifier
            .set_rsa_padding(padding)
            .map_err(javascript_crypto_openssl_error)?;
    }
    verifier
        .update(&data)
        .map_err(javascript_crypto_openssl_error)?;
    Ok(json!(verifier
        .verify(&signature)
        .map_err(javascript_crypto_openssl_error)?))
}

fn service_javascript_crypto_asymmetric_op_sync_rpc(
    request: &JavascriptSyncRpcRequest,
) -> Result<Value, SidecarError> {
    let operation = javascript_sync_rpc_arg_str(&request.args, 0, "crypto.asymmetricOp operation")?;
    let key_json = javascript_sync_rpc_arg_str(&request.args, 1, "crypto.asymmetricOp key")?;
    let data = javascript_sync_rpc_base64_arg(&request.args, 2, "crypto.asymmetricOp data")?;
    let expect_kind = match operation {
        "publicEncrypt" | "publicDecrypt" => Some("public"),
        "privateEncrypt" | "privateDecrypt" => Some("private"),
        other => {
            return Err(SidecarError::InvalidState(format!(
                "Unsupported asymmetric crypto operation: {other}"
            )));
        }
    };
    let key_input =
        javascript_crypto_parse_direct_key_input(key_json, expect_kind, "crypto.asymmetricOp key")?;
    let padding = key_input.padding.unwrap_or(Padding::PKCS1);
    let mut output = vec![0_u8; javascript_crypto_rsa_output_size(&key_input.key)?];
    let written = match (operation, key_input.key) {
        ("publicEncrypt", JavascriptCryptoKeyMaterial::Public(key))
        | ("publicDecrypt", JavascriptCryptoKeyMaterial::Public(key)) => {
            let rsa = key.rsa().map_err(javascript_crypto_openssl_error)?;
            if operation == "publicEncrypt" {
                rsa.public_encrypt(&data, &mut output, padding)
                    .map_err(javascript_crypto_openssl_error)?
            } else {
                rsa.public_decrypt(&data, &mut output, padding)
                    .map_err(javascript_crypto_openssl_error)?
            }
        }
        ("privateEncrypt", JavascriptCryptoKeyMaterial::Private(key))
        | ("privateDecrypt", JavascriptCryptoKeyMaterial::Private(key)) => {
            let rsa = key.rsa().map_err(javascript_crypto_openssl_error)?;
            if operation == "privateEncrypt" {
                rsa.private_encrypt(&data, &mut output, padding)
                    .map_err(javascript_crypto_openssl_error)?
            } else {
                rsa.private_decrypt(&data, &mut output, padding)
                    .map_err(javascript_crypto_openssl_error)?
            }
        }
        _ => {
            return Err(SidecarError::InvalidState(format!(
                "{operation} requires an RSA {} key",
                expect_kind.unwrap_or("asymmetric")
            )));
        }
    };
    output.truncate(written);
    Ok(Value::String(
        base64::engine::general_purpose::STANDARD.encode(output),
    ))
}

fn service_javascript_crypto_create_key_object_sync_rpc(
    request: &JavascriptSyncRpcRequest,
) -> Result<Value, SidecarError> {
    let operation =
        javascript_sync_rpc_arg_str(&request.args, 0, "crypto.createKeyObject operation")?;
    let key_json = javascript_sync_rpc_arg_str(&request.args, 1, "crypto.createKeyObject key")?;
    let expected = match operation {
        "createPrivateKey" => Some("private"),
        "createPublicKey" => Some("public"),
        other => {
            return Err(SidecarError::InvalidState(format!(
                "Unsupported key creation operation: {other}"
            )));
        }
    };
    let key_input =
        javascript_crypto_parse_direct_key_input(key_json, expected, "crypto.createKeyObject key")?;
    Ok(Value::String(
        serde_json::to_string(&javascript_crypto_serialize_sandbox_key_object(
            &key_input.key,
        )?)
        .map_err(|error| {
            SidecarError::InvalidState(format!("serialize crypto key object: {error}"))
        })?,
    ))
}

fn service_javascript_crypto_generate_key_pair_sync_rpc(
    request: &JavascriptSyncRpcRequest,
) -> Result<Value, SidecarError> {
    let key_type =
        javascript_sync_rpc_arg_str(&request.args, 0, "crypto.generateKeyPairSync type")?;
    let options = javascript_crypto_parse_serialized_options_arg(
        &request.args,
        1,
        "crypto.generateKeyPairSync options",
    )?
    .unwrap_or(Value::Object(Map::new()));
    let public_encoding = options.get("publicKeyEncoding").cloned();
    let private_encoding = options.get("privateKeyEncoding").cloned();

    let private_key = match key_type {
        "rsa" => {
            let bits = options
                .get("modulusLength")
                .and_then(Value::as_u64)
                .unwrap_or(2048) as u32;
            let exponent = options
                .get("publicExponent")
                .map(|value| javascript_crypto_u32_from_bridge_value(value, "rsa publicExponent"))
                .transpose()?
                .unwrap_or(65_537);
            let exponent = BigNum::from_u32(exponent).map_err(javascript_crypto_openssl_error)?;
            let rsa =
                Rsa::generate_with_e(bits, &exponent).map_err(javascript_crypto_openssl_error)?;
            PKey::from_rsa(rsa).map_err(javascript_crypto_openssl_error)?
        }
        "ec" => {
            let named_curve = options
                .get("namedCurve")
                .and_then(Value::as_str)
                .ok_or_else(|| {
                    SidecarError::InvalidState(String::from(
                        "crypto.generateKeyPairSync ec requires namedCurve",
                    ))
                })?;
            let group = EcGroup::from_curve_name(javascript_crypto_curve_nid(named_curve)?)
                .map_err(javascript_crypto_openssl_error)?;
            let key = EcKey::generate(&group).map_err(javascript_crypto_openssl_error)?;
            PKey::from_ec_key(key).map_err(javascript_crypto_openssl_error)?
        }
        "ed25519" => PKey::generate_ed25519().map_err(javascript_crypto_openssl_error)?,
        "x25519" => PKey::generate_x25519().map_err(javascript_crypto_openssl_error)?,
        other => {
            return Err(SidecarError::InvalidState(format!(
                "unsupported crypto key pair type {other}"
            )));
        }
    };
    let public_key = PKey::public_key_from_pem(
        &private_key
            .public_key_to_pem()
            .map_err(javascript_crypto_openssl_error)?,
    )
    .map_err(javascript_crypto_openssl_error)?;
    let response = if public_encoding.is_some() || private_encoding.is_some() {
        json!({
            "publicKey": javascript_crypto_serialize_encoded_key_value_public(&public_key, public_encoding.as_ref())?,
            "privateKey": javascript_crypto_serialize_encoded_key_value_private(&private_key, private_encoding.as_ref())?,
        })
    } else {
        json!({
            "publicKey": javascript_crypto_serialize_sandbox_key_object(&JavascriptCryptoKeyMaterial::Public(public_key))?,
            "privateKey": javascript_crypto_serialize_sandbox_key_object(&JavascriptCryptoKeyMaterial::Private(private_key))?,
        })
    };
    Ok(Value::String(serde_json::to_string(&response).map_err(
        |error| SidecarError::InvalidState(format!("serialize generated key pair: {error}")),
    )?))
}

fn service_javascript_crypto_generate_key_sync_rpc(
    request: &JavascriptSyncRpcRequest,
) -> Result<Value, SidecarError> {
    let key_type = javascript_sync_rpc_arg_str(&request.args, 0, "crypto.generateKeySync type")?;
    let options = javascript_crypto_parse_serialized_options_arg(
        &request.args,
        1,
        "crypto.generateKeySync options",
    )?
    .unwrap_or(Value::Object(Map::new()));
    let bit_length = options
        .get("length")
        .and_then(Value::as_u64)
        .ok_or_else(|| {
            SidecarError::InvalidState(String::from(
                "crypto.generateKeySync options.length is required",
            ))
        })? as usize;
    let mut raw = vec![0_u8; bit_length.div_ceil(8)];
    rand_bytes(&mut raw).map_err(javascript_crypto_openssl_error)?;
    let serialized = match key_type {
        "hmac" => javascript_crypto_serialize_sandbox_key_object(
            &JavascriptCryptoKeyMaterial::Secret(raw),
        )?,
        "aes" => javascript_crypto_serialize_sandbox_key_object(
            &JavascriptCryptoKeyMaterial::Secret(raw),
        )?,
        other => {
            return Err(SidecarError::InvalidState(format!(
                "unsupported crypto.generateKeySync type {other}"
            )));
        }
    };
    Ok(Value::String(serde_json::to_string(&serialized).map_err(
        |error| SidecarError::InvalidState(format!("serialize generated key: {error}")),
    )?))
}

fn service_javascript_crypto_generate_prime_sync_rpc(
    request: &JavascriptSyncRpcRequest,
) -> Result<Value, SidecarError> {
    let bits =
        javascript_sync_rpc_arg_u64(&request.args, 0, "crypto.generatePrimeSync size")? as i32;
    let options = javascript_crypto_parse_serialized_options_arg(
        &request.args,
        1,
        "crypto.generatePrimeSync options",
    )?
    .unwrap_or(Value::Object(Map::new()));
    let safe = options
        .get("safe")
        .and_then(Value::as_bool)
        .unwrap_or(false);
    let add = options
        .get("add")
        .map(|value| javascript_crypto_bignum_from_bridge_value(value, "prime add"))
        .transpose()?;
    let rem = options
        .get("rem")
        .map(|value| javascript_crypto_bignum_from_bridge_value(value, "prime rem"))
        .transpose()?;
    let mut prime = BigNum::new().map_err(javascript_crypto_openssl_error)?;
    prime
        .generate_prime(bits, safe, add.as_deref(), rem.as_deref())
        .map_err(javascript_crypto_openssl_error)?;
    let payload = if options
        .get("bigint")
        .and_then(Value::as_bool)
        .unwrap_or(false)
    {
        json!({
            "__type": "bigint",
            "value": prime.to_dec_str().map_err(javascript_crypto_openssl_error)?.to_string(),
        })
    } else {
        json!({
            "__type": "buffer",
            "value": base64::engine::general_purpose::STANDARD.encode(prime.to_vec()),
        })
    };
    Ok(Value::String(serde_json::to_string(&payload).map_err(
        |error| SidecarError::InvalidState(format!("serialize generated prime: {error}")),
    )?))
}

fn service_javascript_crypto_diffie_hellman_sync_rpc(
    request: &JavascriptSyncRpcRequest,
) -> Result<Value, SidecarError> {
    let options = javascript_sync_rpc_arg_str(&request.args, 0, "crypto.diffieHellman options")?;
    let parsed: Value = serde_json::from_str(options).map_err(|error| {
        SidecarError::InvalidState(format!(
            "crypto.diffieHellman options must be valid JSON: {error}"
        ))
    })?;
    let private_key = javascript_crypto_parse_key_material_value(
        parsed.get("privateKey").ok_or_else(|| {
            SidecarError::InvalidState(String::from("crypto.diffieHellman missing privateKey"))
        })?,
        Some("private"),
        "crypto.diffieHellman privateKey",
    )?;
    let public_key = javascript_crypto_parse_key_material_value(
        parsed.get("publicKey").ok_or_else(|| {
            SidecarError::InvalidState(String::from("crypto.diffieHellman missing publicKey"))
        })?,
        Some("public"),
        "crypto.diffieHellman publicKey",
    )?;
    let private_key =
        javascript_crypto_expect_private_key(private_key, "crypto.diffieHellman privateKey")?;
    let public_key =
        javascript_crypto_expect_public_key(public_key, "crypto.diffieHellman publicKey")?;
    let mut deriver = Deriver::new(&private_key).map_err(javascript_crypto_openssl_error)?;
    deriver
        .set_peer(&public_key)
        .map_err(javascript_crypto_openssl_error)?;
    let secret = deriver
        .derive_to_vec()
        .map_err(javascript_crypto_openssl_error)?;
    Ok(Value::String(
        serde_json::to_string(&json!({
            "__type": "buffer",
            "value": base64::engine::general_purpose::STANDARD.encode(secret),
        }))
        .map_err(|error| {
            SidecarError::InvalidState(format!("serialize derived secret: {error}"))
        })?,
    ))
}

fn service_javascript_crypto_diffie_hellman_group_sync_rpc(
    request: &JavascriptSyncRpcRequest,
) -> Result<Value, SidecarError> {
    let name = javascript_sync_rpc_arg_str(&request.args, 0, "crypto.diffieHellmanGroup name")?;
    let params = javascript_crypto_named_dh_group(name)?;
    let response = json!({
        "prime": {
            "__type": "buffer",
            "value": base64::engine::general_purpose::STANDARD.encode(params.prime_p().to_vec()),
        },
        "generator": {
            "__type": "buffer",
            "value": base64::engine::general_purpose::STANDARD.encode(params.generator().to_vec()),
        },
    });
    Ok(Value::String(serde_json::to_string(&response).map_err(
        |error| {
            SidecarError::InvalidState(format!("serialize diffieHellmanGroup response: {error}"))
        },
    )?))
}

fn service_javascript_crypto_diffie_hellman_session_create_sync_rpc(
    process: &mut ActiveProcess,
    request: &JavascriptSyncRpcRequest,
) -> Result<Value, SidecarError> {
    ensure_per_process_state_handle_capacity(
        process.diffie_hellman_sessions.len(),
        "diffie-hellman session",
    )?;
    let raw = javascript_sync_rpc_arg_str(
        &request.args,
        0,
        "crypto.diffieHellmanSessionCreate request",
    )?;
    let parsed: Value = serde_json::from_str(raw).map_err(|error| {
        SidecarError::InvalidState(format!(
            "crypto.diffieHellmanSessionCreate request must be valid JSON: {error}"
        ))
    })?;
    let session = match parsed.get("type").and_then(Value::as_str) {
        Some("group") => {
            let name = parsed.get("name").and_then(Value::as_str).ok_or_else(|| {
                SidecarError::InvalidState(String::from(
                    "crypto.diffieHellmanSessionCreate group requires name",
                ))
            })?;
            ActiveDiffieHellmanSession::Dh(ActiveDhSession {
                params: javascript_crypto_named_dh_group(name)?,
                key_pair: None,
            })
        }
        Some("dh") => {
            let args = parsed
                .get("args")
                .and_then(Value::as_array)
                .ok_or_else(|| {
                    SidecarError::InvalidState(String::from(
                        "crypto.diffieHellmanSessionCreate dh requires args",
                    ))
                })?;
            let params = javascript_crypto_build_dh_params(args)?;
            ActiveDiffieHellmanSession::Dh(ActiveDhSession {
                params,
                key_pair: None,
            })
        }
        Some("ecdh") => {
            let curve = parsed.get("name").and_then(Value::as_str).ok_or_else(|| {
                SidecarError::InvalidState(String::from(
                    "crypto.diffieHellmanSessionCreate ecdh requires name",
                ))
            })?;
            ActiveDiffieHellmanSession::Ecdh(ActiveEcdhSession {
                curve: curve.to_string(),
                key_pair: None,
            })
        }
        other => {
            return Err(SidecarError::InvalidState(format!(
                "Unsupported Diffie-Hellman session type: {}",
                other.unwrap_or("<missing>")
            )));
        }
    };
    process.next_diffie_hellman_session_id += 1;
    let session_id = process.next_diffie_hellman_session_id;
    process.diffie_hellman_sessions.insert(session_id, session);
    Ok(json!(session_id))
}

fn service_javascript_crypto_diffie_hellman_session_call_sync_rpc(
    process: &mut ActiveProcess,
    request: &JavascriptSyncRpcRequest,
) -> Result<Value, SidecarError> {
    let session_id = javascript_sync_rpc_arg_u64(
        &request.args,
        0,
        "crypto.diffieHellmanSessionCall session id",
    )?;
    let raw =
        javascript_sync_rpc_arg_str(&request.args, 1, "crypto.diffieHellmanSessionCall request")?;
    let parsed: Value = serde_json::from_str(raw).map_err(|error| {
        SidecarError::InvalidState(format!(
            "crypto.diffieHellmanSessionCall request must be valid JSON: {error}"
        ))
    })?;
    let method = parsed
        .get("method")
        .and_then(Value::as_str)
        .ok_or_else(|| {
            SidecarError::InvalidState(String::from(
                "crypto.diffieHellmanSessionCall request missing method",
            ))
        })?;
    let args = parsed
        .get("args")
        .and_then(Value::as_array)
        .cloned()
        .unwrap_or_default();
    let session = process
        .diffie_hellman_sessions
        .get_mut(&session_id)
        .ok_or_else(|| {
            SidecarError::InvalidState(format!("Diffie-Hellman session {session_id} not found"))
        })?;
    let (result, has_result) = match session {
        ActiveDiffieHellmanSession::Dh(session) => {
            javascript_crypto_call_dh_session(session, method, &args)?
        }
        ActiveDiffieHellmanSession::Ecdh(session) => {
            javascript_crypto_call_ecdh_session(session, method, &args)?
        }
    };
    Ok(Value::String(
        serde_json::to_string(&json!({
            "result": result,
            "hasResult": has_result,
        }))
        .map_err(|error| {
            SidecarError::InvalidState(format!("serialize diffie session result: {error}"))
        })?,
    ))
}

fn service_javascript_crypto_diffie_hellman_session_destroy_sync_rpc(
    process: &mut ActiveProcess,
    request: &JavascriptSyncRpcRequest,
) -> Result<Value, SidecarError> {
    let session_id = javascript_sync_rpc_arg_u64(
        &request.args,
        0,
        "crypto.diffieHellmanSessionDestroy session id",
    )?;
    process
        .diffie_hellman_sessions
        .remove(&session_id)
        .ok_or_else(|| {
            SidecarError::InvalidState(format!("Diffie-Hellman session {session_id} not found"))
        })?;
    Ok(Value::Null)
}

fn service_javascript_crypto_subtle_sync_rpc(
    request: &JavascriptSyncRpcRequest,
) -> Result<Value, SidecarError> {
    let raw = javascript_sync_rpc_arg_str(&request.args, 0, "crypto.subtle request")?;
    let parsed: Value = serde_json::from_str(raw).map_err(|error| {
        SidecarError::InvalidState(format!("crypto.subtle request must be valid JSON: {error}"))
    })?;
    let op = parsed.get("op").and_then(Value::as_str).ok_or_else(|| {
        SidecarError::InvalidState(String::from("crypto.subtle request missing op"))
    })?;
    match op {
        "digest" => {
            let algorithm = parsed
                .get("algorithm")
                .and_then(Value::as_str)
                .ok_or_else(|| {
                    SidecarError::InvalidState(String::from(
                        "crypto.subtle.digest missing algorithm",
                    ))
                })?;
            let data = parsed.get("data").and_then(Value::as_str).ok_or_else(|| {
                SidecarError::InvalidState(String::from("crypto.subtle.digest missing data"))
            })?;
            let bytes = base64::engine::general_purpose::STANDARD
                .decode(data)
                .map_err(|error| {
                    SidecarError::InvalidState(format!("crypto.subtle.digest data base64: {error}"))
                })?;
            let digest = JavascriptCryptoDigestAlgorithm::parse(algorithm)?.digest(&bytes);
            Ok(Value::String(
                serde_json::to_string(&json!({
                    "data": base64::engine::general_purpose::STANDARD.encode(digest),
                }))
                .map_err(|error| {
                    SidecarError::InvalidState(format!("serialize crypto.subtle digest: {error}"))
                })?,
            ))
        }
        "generateKey" => {
            let algorithm = parsed.get("algorithm").ok_or_else(|| {
                SidecarError::InvalidState(String::from(
                    "crypto.subtle.generateKey missing algorithm",
                ))
            })?;
            let name =
                javascript_crypto_subtle_algorithm_name(algorithm, "crypto.subtle.generateKey")?;
            if !matches!(name, "AES-GCM" | "AES-CBC" | "AES-CTR" | "AES-KW") {
                return Err(SidecarError::InvalidState(format!(
                    "Unsupported key algorithm: {name}"
                )));
            }
            let length_bits = algorithm
                .get("length")
                .and_then(Value::as_u64)
                .ok_or_else(|| {
                    SidecarError::InvalidState(String::from(
                        "crypto.subtle.generateKey AES algorithm requires length",
                    ))
                })?;
            if length_bits % 8 != 0 {
                return Err(SidecarError::InvalidState(String::from(
                    "crypto.subtle.generateKey length must be byte-aligned",
                )));
            }
            let length_bytes = usize::try_from(length_bits / 8).map_err(|_| {
                SidecarError::InvalidState(String::from(
                    "crypto.subtle.generateKey length is too large",
                ))
            })?;
            let mut raw = vec![0_u8; length_bytes];
            rand_bytes(&mut raw).map_err(javascript_crypto_openssl_error)?;
            let key = javascript_crypto_serialize_subtle_secret_key(
                &raw,
                javascript_crypto_normalize_subtle_secret_algorithm(algorithm.clone(), &raw)?,
                parsed
                    .get("extractable")
                    .and_then(Value::as_bool)
                    .unwrap_or(false),
                parsed.get("usages").cloned().unwrap_or_else(|| json!([])),
            )?;
            Ok(Value::String(
                serde_json::to_string(&json!({ "key": key })).map_err(|error| {
                    SidecarError::InvalidState(format!(
                        "serialize crypto.subtle generated key: {error}"
                    ))
                })?,
            ))
        }
        "importKey" => {
            let format = parsed
                .get("format")
                .and_then(Value::as_str)
                .ok_or_else(|| {
                    SidecarError::InvalidState(String::from(
                        "crypto.subtle.importKey missing format",
                    ))
                })?;
            if format != "raw" {
                return Err(SidecarError::InvalidState(format!(
                    "Unsupported import format: {format}"
                )));
            }
            let key_data = parsed
                .get("keyData")
                .and_then(Value::as_str)
                .ok_or_else(|| {
                    SidecarError::InvalidState(String::from(
                        "crypto.subtle.importKey missing keyData",
                    ))
                })?;
            let raw = base64::engine::general_purpose::STANDARD
                .decode(key_data)
                .map_err(|error| {
                    SidecarError::InvalidState(format!(
                        "crypto.subtle.importKey keyData base64: {error}"
                    ))
                })?;
            let algorithm = parsed.get("algorithm").ok_or_else(|| {
                SidecarError::InvalidState(String::from(
                    "crypto.subtle.importKey missing algorithm",
                ))
            })?;
            let key = javascript_crypto_serialize_subtle_secret_key(
                &raw,
                javascript_crypto_normalize_subtle_secret_algorithm(algorithm.clone(), &raw)?,
                parsed
                    .get("extractable")
                    .and_then(Value::as_bool)
                    .unwrap_or(false),
                parsed.get("usages").cloned().unwrap_or_else(|| json!([])),
            )?;
            Ok(Value::String(
                serde_json::to_string(&json!({ "key": key })).map_err(|error| {
                    SidecarError::InvalidState(format!(
                        "serialize crypto.subtle imported key: {error}"
                    ))
                })?,
            ))
        }
        "exportKey" => {
            let format = parsed
                .get("format")
                .and_then(Value::as_str)
                .ok_or_else(|| {
                    SidecarError::InvalidState(String::from(
                        "crypto.subtle.exportKey missing format",
                    ))
                })?;
            if format != "raw" {
                return Err(SidecarError::InvalidState(format!(
                    "Unsupported export format: {format}"
                )));
            }
            let raw = javascript_crypto_subtle_key_raw(
                parsed.get("key").ok_or_else(|| {
                    SidecarError::InvalidState(String::from("crypto.subtle.exportKey missing key"))
                })?,
                "crypto.subtle.exportKey key",
            )?;
            Ok(Value::String(
                serde_json::to_string(&json!({
                    "data": base64::engine::general_purpose::STANDARD.encode(raw),
                }))
                .map_err(|error| {
                    SidecarError::InvalidState(format!("serialize crypto.subtle export: {error}"))
                })?,
            ))
        }
        "encrypt" | "decrypt" => service_javascript_crypto_subtle_aes_crypt_sync_rpc(op, &parsed),
        _ => Err(SidecarError::InvalidState(format!(
            "Unsupported subtle operation: {op}"
        ))),
    }
}

fn javascript_crypto_subtle_algorithm_name<'a>(
    algorithm: &'a Value,
    label: &str,
) -> Result<&'a str, SidecarError> {
    if let Some(name) = algorithm.as_str() {
        return Ok(name);
    }
    algorithm
        .get("name")
        .and_then(Value::as_str)
        .ok_or_else(|| SidecarError::InvalidState(format!("{label} algorithm missing name")))
}

fn javascript_crypto_normalize_subtle_secret_algorithm(
    algorithm: Value,
    raw: &[u8],
) -> Result<Value, SidecarError> {
    let mut object = match algorithm {
        Value::String(name) => {
            let mut object = Map::new();
            object.insert(String::from("name"), Value::String(name));
            object
        }
        Value::Object(object) => object,
        _ => {
            return Err(SidecarError::InvalidState(String::from(
                "crypto.subtle secret algorithm must be a string or object",
            )));
        }
    };
    let name = object
        .get("name")
        .and_then(Value::as_str)
        .ok_or_else(|| {
            SidecarError::InvalidState(String::from("crypto.subtle secret algorithm missing name"))
        })?
        .to_string();
    if matches!(name.as_str(), "AES-GCM" | "AES-CBC" | "AES-CTR" | "AES-KW")
        && !object.contains_key("length")
    {
        object.insert(String::from("length"), json!(raw.len() * 8));
    }
    Ok(Value::Object(object))
}

fn javascript_crypto_serialize_subtle_secret_key(
    raw: &[u8],
    algorithm: Value,
    extractable: bool,
    usages: Value,
) -> Result<Value, SidecarError> {
    let raw_base64 = base64::engine::general_purpose::STANDARD.encode(raw);
    let source_key_object_data = javascript_crypto_serialize_sandbox_key_object(
        &JavascriptCryptoKeyMaterial::Secret(raw.to_vec()),
    )?;
    Ok(json!({
        "type": "secret",
        "algorithm": algorithm,
        "extractable": extractable,
        "usages": usages,
        "_raw": raw_base64,
        "_sourceKeyObjectData": source_key_object_data,
    }))
}

fn javascript_crypto_subtle_key_raw(key: &Value, label: &str) -> Result<Vec<u8>, SidecarError> {
    let raw = key.get("_raw").and_then(Value::as_str).ok_or_else(|| {
        SidecarError::InvalidState(format!("{label} must be a raw secret CryptoKey"))
    })?;
    base64::engine::general_purpose::STANDARD
        .decode(raw)
        .map_err(|error| SidecarError::InvalidState(format!("{label} raw base64: {error}")))
}

fn service_javascript_crypto_subtle_aes_crypt_sync_rpc(
    op: &str,
    parsed: &Value,
) -> Result<Value, SidecarError> {
    let algorithm = parsed.get("algorithm").ok_or_else(|| {
        SidecarError::InvalidState(format!("crypto.subtle.{op} missing algorithm"))
    })?;
    let name = javascript_crypto_subtle_algorithm_name(algorithm, &format!("crypto.subtle.{op}"))?;
    if name != "AES-GCM" {
        return Err(SidecarError::InvalidState(format!(
            "Unsupported subtle AES operation algorithm: {name}"
        )));
    }
    let key = javascript_crypto_subtle_key_raw(
        parsed
            .get("key")
            .ok_or_else(|| SidecarError::InvalidState(format!("crypto.subtle.{op} missing key")))?,
        &format!("crypto.subtle.{op} key"),
    )?;
    let iv = algorithm.get("iv").and_then(Value::as_str).ok_or_else(|| {
        SidecarError::InvalidState(format!("crypto.subtle.{op} AES-GCM missing iv"))
    })?;
    let iv = base64::engine::general_purpose::STANDARD
        .decode(iv)
        .map_err(|error| {
            SidecarError::InvalidState(format!("crypto.subtle.{op} iv base64: {error}"))
        })?;
    let data = parsed
        .get("data")
        .and_then(Value::as_str)
        .ok_or_else(|| SidecarError::InvalidState(format!("crypto.subtle.{op} missing data")))?;
    let mut data = base64::engine::general_purpose::STANDARD
        .decode(data)
        .map_err(|error| {
            SidecarError::InvalidState(format!("crypto.subtle.{op} data base64: {error}"))
        })?;
    let tag_len = javascript_crypto_subtle_aes_gcm_tag_len(algorithm)?;
    let mut options = Map::new();
    options.insert(String::from("authTagLength"), json!(tag_len));
    if let Some(additional_data) = algorithm.get("additionalData").and_then(Value::as_str) {
        options.insert(
            String::from("aad"),
            Value::String(additional_data.to_string()),
        );
    }
    let decrypt = op == "decrypt";
    if decrypt {
        if data.len() < tag_len {
            return Err(SidecarError::InvalidState(String::from(
                "crypto.subtle.decrypt AES-GCM data shorter than auth tag",
            )));
        }
        let auth_tag = data.split_off(data.len() - tag_len);
        options.insert(
            String::from("authTag"),
            Value::String(base64::engine::general_purpose::STANDARD.encode(auth_tag)),
        );
    }
    let cipher_name = format!("aes-{}-gcm", key.len() * 8);
    let mut context = javascript_crypto_build_cipher_context(
        &cipher_name,
        &key,
        Some(&iv),
        decrypt,
        Some(&Value::Object(options)),
    )?;
    let mut output = javascript_crypto_cipher_update(&mut context, &data)?;
    output.extend(javascript_crypto_cipher_finalize(&mut context)?);
    if !decrypt {
        let mut auth_tag = vec![0_u8; tag_len];
        context
            .get_tag(&mut auth_tag)
            .map_err(javascript_crypto_openssl_error)?;
        output.extend(auth_tag);
    }
    Ok(Value::String(
        serde_json::to_string(&json!({
            "data": base64::engine::general_purpose::STANDARD.encode(output),
        }))
        .map_err(|error| {
            SidecarError::InvalidState(format!("serialize crypto.subtle {op}: {error}"))
        })?,
    ))
}

fn javascript_crypto_subtle_aes_gcm_tag_len(algorithm: &Value) -> Result<usize, SidecarError> {
    let tag_bits = algorithm
        .get("tagLength")
        .and_then(Value::as_u64)
        .unwrap_or(128);
    if !tag_bits.is_multiple_of(8) {
        return Err(SidecarError::InvalidState(String::from(
            "crypto.subtle AES-GCM tagLength must be byte-aligned",
        )));
    }
    usize::try_from(tag_bits / 8).map_err(|_| {
        SidecarError::InvalidState(String::from("crypto.subtle AES-GCM tagLength too large"))
    })
}

fn service_javascript_crypto_cipheriv_inner(
    request: &JavascriptSyncRpcRequest,
    decrypt: bool,
) -> Result<Value, SidecarError> {
    let label = if decrypt {
        "crypto.decipheriv"
    } else {
        "crypto.cipheriv"
    };
    let algorithm = javascript_sync_rpc_arg_str(&request.args, 0, &format!("{label} algorithm"))?;
    let key = javascript_sync_rpc_base64_arg(&request.args, 1, &format!("{label} key"))?;
    let iv = javascript_sync_rpc_base64_arg_optional(&request.args, 2, &format!("{label} iv"))?;
    let data = javascript_sync_rpc_base64_arg(&request.args, 3, &format!("{label} data"))?;
    let options =
        javascript_sync_rpc_json_arg_optional(&request.args, 4, &format!("{label} options"))?;
    let auth_tag_len = javascript_crypto_requested_aead_tag_len(algorithm, options.as_ref())?;
    let mut context = javascript_crypto_build_cipher_context(
        algorithm,
        &key,
        iv.as_deref(),
        decrypt,
        options.as_ref(),
    )?;
    let payload = javascript_crypto_cipher_update(&mut context, &data)?;
    let final_bytes = javascript_crypto_cipher_finalize(&mut context)?;
    if decrypt {
        let mut output = payload;
        output.extend(final_bytes);
        return Ok(Value::String(
            base64::engine::general_purpose::STANDARD.encode(output),
        ));
    }

    let mut response = Map::new();
    let mut encrypted = payload;
    encrypted.extend(final_bytes);
    response.insert(
        String::from("data"),
        Value::String(base64::engine::general_purpose::STANDARD.encode(encrypted)),
    );
    if javascript_crypto_is_aead(algorithm) {
        let mut auth_tag = vec![0_u8; auth_tag_len];
        context
            .get_tag(&mut auth_tag)
            .map_err(javascript_crypto_openssl_error)?;
        response.insert(
            String::from("authTag"),
            Value::String(base64::engine::general_purpose::STANDARD.encode(auth_tag)),
        );
    }
    Ok(Value::String(serde_json::to_string(&response).map_err(
        |error| SidecarError::InvalidState(format!("serialize {label} response: {error}")),
    )?))
}

fn javascript_sync_rpc_base64_arg_optional(
    args: &[Value],
    index: usize,
    label: &str,
) -> Result<Option<Vec<u8>>, SidecarError> {
    if args.get(index).is_none() || args[index].is_null() {
        return Ok(None);
    }
    javascript_sync_rpc_base64_arg(args, index, label).map(Some)
}

fn javascript_sync_rpc_json_arg_optional(
    args: &[Value],
    index: usize,
    label: &str,
) -> Result<Option<Value>, SidecarError> {
    if args.get(index).is_none() || args[index].is_null() {
        return Ok(None);
    }
    let raw = javascript_sync_rpc_arg_str(args, index, label)?;
    serde_json::from_str(raw)
        .map(Some)
        .map_err(|error| SidecarError::InvalidState(format!("{label} must be valid JSON: {error}")))
}

fn javascript_crypto_parse_direct_key_input(
    raw: &str,
    expected: Option<&str>,
    label: &str,
) -> Result<JavascriptDirectKeyInput, SidecarError> {
    let parsed: Value = serde_json::from_str(raw).map_err(|error| {
        SidecarError::InvalidState(format!("{label} must be valid JSON: {error}"))
    })?;
    let padding = match parsed.as_object().and_then(|value| value.get("padding")) {
        Some(value) => javascript_crypto_padding_from_value(value)?,
        None => None,
    };
    Ok(JavascriptDirectKeyInput {
        key: javascript_crypto_parse_key_material_value(&parsed, expected, label)?,
        padding,
    })
}

fn javascript_crypto_parse_key_material_value(
    value: &Value,
    expected: Option<&str>,
    label: &str,
) -> Result<JavascriptCryptoKeyMaterial, SidecarError> {
    if let Some(object) = value.as_object() {
        if object.get("__type").and_then(Value::as_str) == Some("keyObject") {
            let serialized = object.get("value").ok_or_else(|| {
                SidecarError::InvalidState(format!("{label} keyObject is missing a value"))
            })?;
            return javascript_crypto_parse_serialized_key_object(serialized, expected, label);
        }
        if object.contains_key("type") && (object.contains_key("pem") || object.contains_key("raw"))
        {
            return javascript_crypto_parse_serialized_key_object(value, expected, label);
        }
        if let Some(source) = object.get("key") {
            return javascript_crypto_parse_key_source(
                source,
                object.get("format").and_then(Value::as_str),
                object.get("type").and_then(Value::as_str),
                expected,
                label,
            );
        }
    }
    javascript_crypto_parse_key_source(value, None, None, expected, label)
}

fn javascript_crypto_parse_key_source(
    source: &Value,
    format: Option<&str>,
    kind: Option<&str>,
    expected: Option<&str>,
    label: &str,
) -> Result<JavascriptCryptoKeyMaterial, SidecarError> {
    match source {
        Value::String(pem) => javascript_crypto_parse_key_from_pem(pem.as_bytes(), expected, label),
        Value::Object(object) if object.get("__type").and_then(Value::as_str) == Some("buffer") => {
            let data = javascript_crypto_decode_bridge_buffer(source, label)?;
            javascript_crypto_parse_key_from_bytes(&data, format, kind, expected, label)
        }
        Value::Object(_) => {
            if format == Some("jwk") {
                return Err(SidecarError::InvalidState(format!(
                    "{label} jwk inputs are not supported yet"
                )));
            }
            Err(SidecarError::InvalidState(format!(
                "{label} has an unsupported key shape"
            )))
        }
        _ => Err(SidecarError::InvalidState(format!(
            "{label} has an unsupported key value"
        ))),
    }
}

fn javascript_crypto_parse_key_from_pem(
    pem: &[u8],
    expected: Option<&str>,
    label: &str,
) -> Result<JavascriptCryptoKeyMaterial, SidecarError> {
    match expected {
        Some("private") => PKey::private_key_from_pem(pem)
            .map(JavascriptCryptoKeyMaterial::Private)
            .map_err(|error| {
                SidecarError::InvalidState(format!("{label} private key is invalid: {error}"))
            }),
        Some("public") => PKey::public_key_from_pem(pem)
            .map(JavascriptCryptoKeyMaterial::Public)
            .map_err(|error| {
                SidecarError::InvalidState(format!("{label} public key is invalid: {error}"))
            }),
        _ => PKey::private_key_from_pem(pem)
            .map(JavascriptCryptoKeyMaterial::Private)
            .or_else(|_| PKey::public_key_from_pem(pem).map(JavascriptCryptoKeyMaterial::Public))
            .map_err(|error| {
                SidecarError::InvalidState(format!("{label} PEM key is invalid: {error}"))
            }),
    }
}

fn javascript_crypto_parse_key_from_bytes(
    der: &[u8],
    format: Option<&str>,
    kind: Option<&str>,
    expected: Option<&str>,
    label: &str,
) -> Result<JavascriptCryptoKeyMaterial, SidecarError> {
    match (format.unwrap_or("der"), kind.or(expected)) {
        ("der", Some("pkcs8")) | ("der", Some("private")) => PKey::private_key_from_der(der)
            .map(JavascriptCryptoKeyMaterial::Private)
            .map_err(|error| {
                SidecarError::InvalidState(format!("{label} private key DER is invalid: {error}"))
            }),
        ("der", Some("spki")) | ("der", Some("public")) => PKey::public_key_from_der(der)
            .map(JavascriptCryptoKeyMaterial::Public)
            .map_err(|error| {
                SidecarError::InvalidState(format!("{label} public key DER is invalid: {error}"))
            }),
        _ => Err(SidecarError::InvalidState(format!(
            "{label} unsupported key bytes format"
        ))),
    }
}

fn javascript_crypto_parse_serialized_key_object(
    value: &Value,
    expected: Option<&str>,
    label: &str,
) -> Result<JavascriptCryptoKeyMaterial, SidecarError> {
    let serialized: JavascriptSerializedSandboxKeyObject = serde_json::from_value(value.clone())
        .map_err(|error| {
            SidecarError::InvalidState(format!("{label} keyObject is invalid: {error}"))
        })?;
    match serialized.kind.as_str() {
        "secret" => {
            if expected == Some("public") || expected == Some("private") {
                return Err(SidecarError::InvalidState(format!(
                    "{label} expected an asymmetric key"
                )));
            }
            Ok(JavascriptCryptoKeyMaterial::Secret(
                base64::engine::general_purpose::STANDARD
                    .decode(serialized.raw.unwrap_or_default())
                    .map_err(|error| {
                        SidecarError::InvalidState(format!(
                            "{label} secret key contains invalid base64: {error}"
                        ))
                    })?,
            ))
        }
        "private" => {
            let pem = serialized.pem.ok_or_else(|| {
                SidecarError::InvalidState(format!("{label} private keyObject is missing pem"))
            })?;
            javascript_crypto_parse_key_from_pem(pem.as_bytes(), Some("private"), label)
        }
        "public" => {
            let pem = serialized.pem.ok_or_else(|| {
                SidecarError::InvalidState(format!("{label} public keyObject is missing pem"))
            })?;
            javascript_crypto_parse_key_from_pem(pem.as_bytes(), Some("public"), label)
        }
        other => Err(SidecarError::InvalidState(format!(
            "{label} has unsupported keyObject type {other}"
        ))),
    }
}

fn javascript_crypto_expect_private_key(
    key: JavascriptCryptoKeyMaterial,
    label: &str,
) -> Result<PKey<Private>, SidecarError> {
    match key {
        JavascriptCryptoKeyMaterial::Private(key) => Ok(key),
        _ => Err(SidecarError::InvalidState(format!(
            "{label} requires a private key"
        ))),
    }
}

fn javascript_crypto_expect_public_key(
    key: JavascriptCryptoKeyMaterial,
    label: &str,
) -> Result<PKey<Public>, SidecarError> {
    match key {
        JavascriptCryptoKeyMaterial::Public(key) => Ok(key),
        JavascriptCryptoKeyMaterial::Private(key) => {
            let pem = key
                .public_key_to_pem()
                .map_err(javascript_crypto_openssl_error)?;
            PKey::public_key_from_pem(&pem).map_err(javascript_crypto_openssl_error)
        }
        _ => Err(SidecarError::InvalidState(format!(
            "{label} requires a public key"
        ))),
    }
}

fn javascript_crypto_new_signer<'a>(
    algorithm: Option<&'a str>,
    key: &'a PKey<Private>,
) -> Result<Signer<'a>, SidecarError> {
    if matches!(key.id(), PKeyId::ED25519 | PKeyId::ED448) || algorithm.is_none() {
        return Signer::new_without_digest(key).map_err(javascript_crypto_openssl_error);
    }
    Signer::new(
        javascript_crypto_message_digest_from_name(algorithm.ok_or_else(|| {
            SidecarError::InvalidState(String::from("crypto.sign requires a digest algorithm"))
        })?)?,
        key,
    )
    .map_err(javascript_crypto_openssl_error)
}

fn javascript_crypto_new_verifier<'a>(
    algorithm: Option<&'a str>,
    key: &'a PKey<Public>,
) -> Result<Verifier<'a>, SidecarError> {
    if matches!(key.id(), PKeyId::ED25519 | PKeyId::ED448) || algorithm.is_none() {
        return Verifier::new_without_digest(key).map_err(javascript_crypto_openssl_error);
    }
    Verifier::new(
        javascript_crypto_message_digest_from_name(algorithm.ok_or_else(|| {
            SidecarError::InvalidState(String::from("crypto.verify requires a digest algorithm"))
        })?)?,
        key,
    )
    .map_err(javascript_crypto_openssl_error)
}

fn javascript_crypto_message_digest_from_name(name: &str) -> Result<MessageDigest, SidecarError> {
    match name.trim().to_ascii_lowercase().replace('-', "").as_str() {
        "md5" => Ok(MessageDigest::md5()),
        "sha1" => Ok(MessageDigest::sha1()),
        "sha256" => Ok(MessageDigest::sha256()),
        "sha384" => Ok(MessageDigest::sha384()),
        "sha512" => Ok(MessageDigest::sha512()),
        other => Err(SidecarError::InvalidState(format!(
            "unsupported crypto digest algorithm {other}"
        ))),
    }
}

fn javascript_crypto_padding_from_value(value: &Value) -> Result<Option<Padding>, SidecarError> {
    let Some(number) = value.as_i64() else {
        return Ok(None);
    };
    let padding = match number {
        1 => Padding::PKCS1,
        3 => Padding::NONE,
        4 => Padding::PKCS1_OAEP,
        6 => Padding::PKCS1_PSS,
        other => {
            return Err(SidecarError::InvalidState(format!(
                "unsupported RSA padding constant {other}"
            )));
        }
    };
    Ok(Some(padding))
}

fn javascript_crypto_decode_bridge_buffer(
    value: &Value,
    label: &str,
) -> Result<Vec<u8>, SidecarError> {
    let base64_value = value
        .as_object()
        .filter(|object| object.get("__type").and_then(Value::as_str) == Some("buffer"))
        .and_then(|object| object.get("value"))
        .and_then(Value::as_str)
        .ok_or_else(|| {
            SidecarError::InvalidState(format!("{label} must be a serialized bridge buffer"))
        })?;
    base64::engine::general_purpose::STANDARD
        .decode(base64_value)
        .map_err(|error| {
            SidecarError::InvalidState(format!("{label} contains invalid base64: {error}"))
        })
}

fn javascript_crypto_serialize_sandbox_key_object(
    key: &JavascriptCryptoKeyMaterial,
) -> Result<Value, SidecarError> {
    let serialized = match key {
        JavascriptCryptoKeyMaterial::Private(key) => JavascriptSerializedSandboxKeyObject {
            kind: String::from("private"),
            pem: Some(
                String::from_utf8(
                    key.private_key_to_pem_pkcs8()
                        .map_err(javascript_crypto_openssl_error)?,
                )
                .map_err(|error| {
                    SidecarError::InvalidState(format!("private key PEM is not utf8: {error}"))
                })?,
            ),
            raw: None,
            asymmetric_key_type: javascript_crypto_pkey_type_name(key.id()),
            asymmetric_key_details: None,
            jwk: None,
        },
        JavascriptCryptoKeyMaterial::Public(key) => JavascriptSerializedSandboxKeyObject {
            kind: String::from("public"),
            pem: Some(
                String::from_utf8(
                    key.public_key_to_pem()
                        .map_err(javascript_crypto_openssl_error)?,
                )
                .map_err(|error| {
                    SidecarError::InvalidState(format!("public key PEM is not utf8: {error}"))
                })?,
            ),
            raw: None,
            asymmetric_key_type: javascript_crypto_pkey_type_name(key.id()),
            asymmetric_key_details: None,
            jwk: None,
        },
        JavascriptCryptoKeyMaterial::Secret(raw) => JavascriptSerializedSandboxKeyObject {
            kind: String::from("secret"),
            pem: None,
            raw: Some(base64::engine::general_purpose::STANDARD.encode(raw)),
            asymmetric_key_type: None,
            asymmetric_key_details: None,
            jwk: None,
        },
    };
    serde_json::to_value(serialized)
        .map_err(|error| SidecarError::InvalidState(format!("serialize key object: {error}")))
}

fn javascript_crypto_pkey_type_name(id: PKeyId) -> Option<String> {
    match id {
        PKeyId::RSA => Some(String::from("rsa")),
        PKeyId::EC => Some(String::from("ec")),
        PKeyId::ED25519 => Some(String::from("ed25519")),
        PKeyId::ED448 => Some(String::from("ed448")),
        PKeyId::X25519 => Some(String::from("x25519")),
        PKeyId::X448 => Some(String::from("x448")),
        PKeyId::DH => Some(String::from("dh")),
        _ => None,
    }
}

fn javascript_crypto_rsa_output_size(
    key: &JavascriptCryptoKeyMaterial,
) -> Result<usize, SidecarError> {
    match key {
        JavascriptCryptoKeyMaterial::Private(key) => key
            .rsa()
            .map(|rsa| rsa.size() as usize)
            .map_err(javascript_crypto_openssl_error),
        JavascriptCryptoKeyMaterial::Public(key) => key
            .rsa()
            .map(|rsa| rsa.size() as usize)
            .map_err(javascript_crypto_openssl_error),
        JavascriptCryptoKeyMaterial::Secret(_) => Err(SidecarError::InvalidState(String::from(
            "RSA operations require an asymmetric key",
        ))),
    }
}

fn javascript_crypto_parse_serialized_options_arg(
    args: &[Value],
    index: usize,
    label: &str,
) -> Result<Option<Value>, SidecarError> {
    let Some(raw) = args.get(index).and_then(Value::as_str) else {
        return Ok(None);
    };
    let parsed: Value = serde_json::from_str(raw).map_err(|error| {
        SidecarError::InvalidState(format!("{label} must be valid JSON: {error}"))
    })?;
    if parsed.get("hasOptions").and_then(Value::as_bool) == Some(true) {
        Ok(parsed.get("options").cloned())
    } else {
        Ok(None)
    }
}

fn javascript_crypto_u32_from_bridge_value(
    value: &Value,
    label: &str,
) -> Result<u32, SidecarError> {
    if let Some(number) = value.as_u64() {
        return u32::try_from(number)
            .map_err(|_| SidecarError::InvalidState(format!("{label} must fit within u32")));
    }
    let bytes = javascript_crypto_decode_bridge_buffer(value, label)?;
    if bytes.len() > 4 {
        return Err(SidecarError::InvalidState(format!(
            "{label} buffer is too large for u32"
        )));
    }
    Ok(bytes
        .into_iter()
        .fold(0_u32, |acc, byte| (acc << 8) | u32::from(byte)))
}

fn javascript_crypto_bignum_from_bridge_value(
    value: &Value,
    label: &str,
) -> Result<BigNum, SidecarError> {
    if let Some(object) = value.as_object() {
        if object.get("__type").and_then(Value::as_str) == Some("bigint") {
            let decimal = object.get("value").and_then(Value::as_str).ok_or_else(|| {
                SidecarError::InvalidState(format!("{label} bigint is missing a value"))
            })?;
            return BigNum::from_dec_str(decimal).map_err(javascript_crypto_openssl_error);
        }
    }
    let bytes = javascript_crypto_decode_bridge_buffer(value, label)?;
    BigNum::from_slice(&bytes).map_err(javascript_crypto_openssl_error)
}

fn javascript_crypto_curve_nid(name: &str) -> Result<Nid, SidecarError> {
    match name {
        "prime256v1" | "P-256" => Ok(Nid::X9_62_PRIME256V1),
        "secp384r1" | "P-384" => Ok(Nid::SECP384R1),
        "secp521r1" | "P-521" => Ok(Nid::SECP521R1),
        "secp256k1" => Ok(Nid::SECP256K1),
        other => Err(SidecarError::InvalidState(format!(
            "unsupported EC curve {other}"
        ))),
    }
}

fn javascript_crypto_named_dh_group(name: &str) -> Result<Dh<Params>, SidecarError> {
    match name {
        "modp2" => Dh::get_1024_160().map_err(javascript_crypto_openssl_error),
        "modp14" | "modp15" | "modp16" | "modp17" | "modp18" => {
            Dh::get_2048_256().map_err(javascript_crypto_openssl_error)
        }
        other => Err(SidecarError::InvalidState(format!(
            "unsupported Diffie-Hellman group {other}"
        ))),
    }
}

fn javascript_crypto_clone_dh_params(params: &Dh<Params>) -> Result<Dh<Params>, SidecarError> {
    Dh::from_pqg(
        params
            .prime_p()
            .to_owned()
            .map_err(javascript_crypto_openssl_error)?,
        params
            .prime_q()
            .map(|value| value.to_owned().map_err(javascript_crypto_openssl_error))
            .transpose()?,
        params
            .generator()
            .to_owned()
            .map_err(javascript_crypto_openssl_error)?,
    )
    .map_err(javascript_crypto_openssl_error)
}

fn javascript_crypto_build_dh_params(args: &[Value]) -> Result<Dh<Params>, SidecarError> {
    let Some(first) = args.first() else {
        return Err(SidecarError::InvalidState(String::from(
            "Diffie-Hellman session args are required",
        )));
    };
    if let Some(bits) = first.as_u64() {
        let generator = args
            .get(1)
            .map(|value| javascript_crypto_u32_from_bridge_value(value, "Diffie-Hellman generator"))
            .transpose()?
            .unwrap_or(2);
        return Dh::generate_params(bits as u32, generator)
            .map_err(javascript_crypto_openssl_error);
    }
    let prime = javascript_crypto_bignum_from_bridge_value(first, "Diffie-Hellman prime")?;
    let generator = args
        .get(1)
        .map(|value| javascript_crypto_bignum_from_bridge_value(value, "Diffie-Hellman generator"))
        .transpose()?
        .unwrap_or(BigNum::from_u32(2).map_err(javascript_crypto_openssl_error)?);
    Dh::from_pqg(prime, None, generator).map_err(javascript_crypto_openssl_error)
}

fn javascript_crypto_call_dh_session(
    session: &mut ActiveDhSession,
    method: &str,
    args: &[Value],
) -> Result<(Value, bool), SidecarError> {
    match method {
        "verifyError" => Ok((Value::Null, false)),
        "generateKeys" => {
            if session.key_pair.is_none() {
                session.key_pair = Some(
                    javascript_crypto_clone_dh_params(&session.params)?
                        .generate_key()
                        .map_err(javascript_crypto_openssl_error)?,
                );
            }
            let public = session
                .key_pair
                .as_ref()
                .expect("dh key pair")
                .public_key()
                .to_vec();
            Ok((javascript_crypto_bridge_buffer_value(&public), true))
        }
        "computeSecret" => {
            if session.key_pair.is_none() {
                session.key_pair = Some(
                    javascript_crypto_clone_dh_params(&session.params)?
                        .generate_key()
                        .map_err(javascript_crypto_openssl_error)?,
                );
            }
            let peer = javascript_crypto_bignum_from_bridge_value(
                args.first().ok_or_else(|| {
                    SidecarError::InvalidState(String::from(
                        "computeSecret requires peer public key",
                    ))
                })?,
                "Diffie-Hellman peer public key",
            )?;
            let secret = session
                .key_pair
                .as_ref()
                .expect("dh key pair")
                .compute_key(&peer)
                .map_err(javascript_crypto_openssl_error)?;
            Ok((javascript_crypto_bridge_buffer_value(&secret), true))
        }
        "getPrime" => Ok((
            javascript_crypto_bridge_buffer_value(&session.params.prime_p().to_vec()),
            true,
        )),
        "getGenerator" => Ok((
            javascript_crypto_bridge_buffer_value(&session.params.generator().to_vec()),
            true,
        )),
        "getPublicKey" => {
            if session.key_pair.is_none() {
                session.key_pair = Some(
                    javascript_crypto_clone_dh_params(&session.params)?
                        .generate_key()
                        .map_err(javascript_crypto_openssl_error)?,
                );
            }
            Ok((
                javascript_crypto_bridge_buffer_value(
                    &session
                        .key_pair
                        .as_ref()
                        .expect("dh key pair")
                        .public_key()
                        .to_vec(),
                ),
                true,
            ))
        }
        "getPrivateKey" => {
            if session.key_pair.is_none() {
                session.key_pair = Some(
                    javascript_crypto_clone_dh_params(&session.params)?
                        .generate_key()
                        .map_err(javascript_crypto_openssl_error)?,
                );
            }
            Ok((
                javascript_crypto_bridge_buffer_value(
                    &session
                        .key_pair
                        .as_ref()
                        .expect("dh key pair")
                        .private_key()
                        .to_vec(),
                ),
                true,
            ))
        }
        other => Err(SidecarError::InvalidState(format!(
            "Unsupported Diffie-Hellman method: {other}"
        ))),
    }
}

fn javascript_crypto_call_ecdh_session(
    session: &mut ActiveEcdhSession,
    method: &str,
    args: &[Value],
) -> Result<(Value, bool), SidecarError> {
    let nid = javascript_crypto_curve_nid(&session.curve)?;
    let group = EcGroup::from_curve_name(nid).map_err(javascript_crypto_openssl_error)?;
    match method {
        "verifyError" => Ok((Value::Null, false)),
        "generateKeys" => {
            if session.key_pair.is_none() {
                session.key_pair =
                    Some(EcKey::generate(&group).map_err(javascript_crypto_openssl_error)?);
            }
            let mut ctx = BigNumContext::new().map_err(javascript_crypto_openssl_error)?;
            let bytes = session
                .key_pair
                .as_ref()
                .expect("ecdh key pair")
                .public_key()
                .to_bytes(&group, PointConversionForm::UNCOMPRESSED, &mut ctx)
                .map_err(javascript_crypto_openssl_error)?;
            Ok((javascript_crypto_bridge_buffer_value(&bytes), true))
        }
        "computeSecret" => {
            if session.key_pair.is_none() {
                session.key_pair =
                    Some(EcKey::generate(&group).map_err(javascript_crypto_openssl_error)?);
            }
            let peer_bytes = javascript_crypto_decode_bridge_buffer(
                args.first().ok_or_else(|| {
                    SidecarError::InvalidState(String::from(
                        "computeSecret requires peer public key",
                    ))
                })?,
                "ECDH peer public key",
            )?;
            let mut ctx = BigNumContext::new().map_err(javascript_crypto_openssl_error)?;
            let peer_point = EcPoint::from_bytes(&group, &peer_bytes, &mut ctx)
                .map_err(javascript_crypto_openssl_error)?;
            let peer_key = EcKey::from_public_key(&group, &peer_point)
                .map_err(javascript_crypto_openssl_error)?;
            let private =
                PKey::from_ec_key(session.key_pair.as_ref().expect("ecdh key pair").to_owned())
                    .map_err(javascript_crypto_openssl_error)?;
            let peer = PKey::from_ec_key(peer_key).map_err(javascript_crypto_openssl_error)?;
            let mut deriver = Deriver::new(&private).map_err(javascript_crypto_openssl_error)?;
            deriver
                .set_peer(&peer)
                .map_err(javascript_crypto_openssl_error)?;
            let secret = deriver
                .derive_to_vec()
                .map_err(javascript_crypto_openssl_error)?;
            Ok((javascript_crypto_bridge_buffer_value(&secret), true))
        }
        "getPublicKey" => {
            if session.key_pair.is_none() {
                session.key_pair =
                    Some(EcKey::generate(&group).map_err(javascript_crypto_openssl_error)?);
            }
            let mut ctx = BigNumContext::new().map_err(javascript_crypto_openssl_error)?;
            let bytes = session
                .key_pair
                .as_ref()
                .expect("ecdh key pair")
                .public_key()
                .to_bytes(&group, PointConversionForm::UNCOMPRESSED, &mut ctx)
                .map_err(javascript_crypto_openssl_error)?;
            Ok((javascript_crypto_bridge_buffer_value(&bytes), true))
        }
        "getPrivateKey" => {
            if session.key_pair.is_none() {
                session.key_pair =
                    Some(EcKey::generate(&group).map_err(javascript_crypto_openssl_error)?);
            }
            Ok((
                javascript_crypto_bridge_buffer_value(
                    &session
                        .key_pair
                        .as_ref()
                        .expect("ecdh key pair")
                        .private_key()
                        .to_vec(),
                ),
                true,
            ))
        }
        other => Err(SidecarError::InvalidState(format!(
            "Unsupported Diffie-Hellman method: {other}"
        ))),
    }
}

fn javascript_crypto_serialize_encoded_key_value_public(
    key: &PKey<Public>,
    encoding: Option<&Value>,
) -> Result<Value, SidecarError> {
    if let Some(encoding) = encoding {
        let format = encoding
            .get("format")
            .and_then(Value::as_str)
            .unwrap_or("pem");
        return Ok(match format {
            "der" => json!({
                "kind": "buffer",
                "value": base64::engine::general_purpose::STANDARD
                    .encode(key.public_key_to_der().map_err(javascript_crypto_openssl_error)?),
            }),
            _ => json!({
                "kind": "string",
                "value": String::from_utf8(
                    key.public_key_to_pem().map_err(javascript_crypto_openssl_error)?,
                )
                .map_err(|error| SidecarError::InvalidState(format!("public key PEM utf8: {error}")))?,
            }),
        });
    }
    javascript_crypto_serialize_sandbox_key_object(&JavascriptCryptoKeyMaterial::Public(
        key.to_owned(),
    ))
}

fn javascript_crypto_serialize_encoded_key_value_private(
    key: &PKey<Private>,
    encoding: Option<&Value>,
) -> Result<Value, SidecarError> {
    if let Some(encoding) = encoding {
        let format = encoding
            .get("format")
            .and_then(Value::as_str)
            .unwrap_or("pem");
        return Ok(match format {
            "der" => json!({
                "kind": "buffer",
                "value": base64::engine::general_purpose::STANDARD
                    .encode(key.private_key_to_der().map_err(javascript_crypto_openssl_error)?),
            }),
            _ => json!({
                "kind": "string",
                "value": String::from_utf8(
                    key.private_key_to_pem_pkcs8().map_err(javascript_crypto_openssl_error)?,
                )
                .map_err(|error| SidecarError::InvalidState(format!("private key PEM utf8: {error}")))?,
            }),
        });
    }
    javascript_crypto_serialize_sandbox_key_object(&JavascriptCryptoKeyMaterial::Private(
        key.to_owned(),
    ))
}

fn javascript_crypto_bridge_buffer_value(bytes: &[u8]) -> Value {
    json!({
        "__type": "buffer",
        "value": base64::engine::general_purpose::STANDARD.encode(bytes),
    })
}

fn javascript_crypto_build_cipher_context(
    algorithm: &str,
    key: &[u8],
    iv: Option<&[u8]>,
    decrypt: bool,
    options: Option<&Value>,
) -> Result<Crypter, SidecarError> {
    let cipher = javascript_crypto_cipher_from_name(algorithm)?;
    let mode = if decrypt {
        Mode::Decrypt
    } else {
        Mode::Encrypt
    };
    let mut context =
        Crypter::new(cipher, mode, key, iv).map_err(javascript_crypto_openssl_error)?;
    if let Some(auto_padding) = options
        .and_then(|value| value.get("autoPadding"))
        .and_then(Value::as_bool)
    {
        context.pad(auto_padding);
    }
    if javascript_crypto_is_aead(algorithm) {
        if let Some(aad) = options
            .and_then(|value| value.get("aad"))
            .and_then(Value::as_str)
        {
            context
                .aad_update(
                    &base64::engine::general_purpose::STANDARD
                        .decode(aad)
                        .map_err(|error| {
                            SidecarError::InvalidState(format!(
                                "cipher aad contains invalid base64: {error}"
                            ))
                        })?,
                )
                .map_err(javascript_crypto_openssl_error)?;
        }
        if decrypt {
            if let Some(auth_tag) = options
                .and_then(|value| value.get("authTag"))
                .and_then(Value::as_str)
            {
                let decoded = base64::engine::general_purpose::STANDARD
                    .decode(auth_tag)
                    .map_err(|error| {
                        SidecarError::InvalidState(format!(
                            "cipher authTag contains invalid base64: {error}"
                        ))
                    })?;
                context
                    .set_tag(&decoded)
                    .map_err(javascript_crypto_openssl_error)?;
            }
        }
    }
    Ok(context)
}

fn javascript_crypto_requested_aead_tag_len(
    algorithm: &str,
    options: Option<&Value>,
) -> Result<usize, SidecarError> {
    if !javascript_crypto_is_aead(algorithm) {
        return Ok(0);
    }
    let requested = options
        .and_then(|value| value.get("authTagLength"))
        .and_then(Value::as_u64)
        .unwrap_or(javascript_crypto_aead_tag_len(algorithm) as u64);
    usize::try_from(requested).map_err(|_| {
        SidecarError::InvalidState(String::from("cipher authTagLength must fit within usize"))
    })
}

fn javascript_crypto_cipher_update(
    context: &mut Crypter,
    data: &[u8],
) -> Result<Vec<u8>, SidecarError> {
    let mut output = vec![0_u8; data.len() + 32];
    let written = context
        .update(data, &mut output)
        .map_err(javascript_crypto_openssl_error)?;
    output.truncate(written);
    Ok(output)
}

fn javascript_crypto_cipher_finalize(context: &mut Crypter) -> Result<Vec<u8>, SidecarError> {
    let mut output = vec![0_u8; 32];
    let written = context
        .finalize(&mut output)
        .map_err(javascript_crypto_openssl_error)?;
    output.truncate(written);
    Ok(output)
}

fn javascript_crypto_cipher_from_name(name: &str) -> Result<Cipher, SidecarError> {
    match name.to_ascii_lowercase().as_str() {
        "aes-128-cbc" => Ok(Cipher::aes_128_cbc()),
        "aes-192-cbc" => Ok(Cipher::aes_192_cbc()),
        "aes-256-cbc" => Ok(Cipher::aes_256_cbc()),
        "aes-128-ctr" => Ok(Cipher::aes_128_ctr()),
        "aes-192-ctr" => Ok(Cipher::aes_192_ctr()),
        "aes-256-ctr" => Ok(Cipher::aes_256_ctr()),
        "aes-128-gcm" => Ok(Cipher::aes_128_gcm()),
        "aes-192-gcm" => Ok(Cipher::aes_192_gcm()),
        "aes-256-gcm" => Ok(Cipher::aes_256_gcm()),
        other => Err(SidecarError::InvalidState(format!(
            "unsupported crypto cipher algorithm {other}"
        ))),
    }
}

fn javascript_crypto_is_aead(algorithm: &str) -> bool {
    algorithm.to_ascii_lowercase().ends_with("-gcm")
}

fn javascript_crypto_aead_tag_len(_algorithm: &str) -> usize {
    16
}

fn javascript_crypto_openssl_error(error: openssl::error::ErrorStack) -> SidecarError {
    SidecarError::Execution(format!("crypto operation failed: {error}"))
}

fn service_javascript_kernel_stdin_sync_rpc(
    kernel: &mut SidecarKernel,
    process: &mut ActiveProcess,
    request: &JavascriptSyncRpcRequest,
) -> Result<Value, SidecarError> {
    let max_bytes =
        javascript_sync_rpc_arg_u64_optional(&request.args, 0, "__kernel_stdin_read max bytes")?
            .map(|value| value.clamp(1, DEFAULT_KERNEL_STDIN_READ_MAX_BYTES as u64) as usize)
            .unwrap_or(DEFAULT_KERNEL_STDIN_READ_MAX_BYTES);
    let timeout_ms =
        javascript_sync_rpc_arg_u64_optional(&request.args, 1, "__kernel_stdin_read timeout ms")?
            .unwrap_or(DEFAULT_KERNEL_STDIN_READ_TIMEOUT_MS);

    match kernel
        .fd_read_with_timeout_result(
            EXECUTION_DRIVER_NAME,
            process.kernel_pid,
            0,
            max_bytes,
            Some(Duration::from_millis(timeout_ms)),
        )
        .map_err(kernel_error)
    {
        Ok(Some(chunk)) if !chunk.is_empty() => Ok(json!({
            "dataBase64": base64::engine::general_purpose::STANDARD.encode(chunk),
        })),
        Ok(Some(_)) => Ok(Value::Null),
        Ok(None) => Ok(json!({
            "done": true,
        })),
        Err(SidecarError::Kernel(error)) if error.starts_with("EAGAIN:") => Ok(Value::Null),
        Err(error) => Err(error),
    }
}

fn service_javascript_pty_set_raw_mode_sync_rpc(
    kernel: &mut SidecarKernel,
    process: &mut ActiveProcess,
    request: &JavascriptSyncRpcRequest,
) -> Result<Value, SidecarError> {
    let enabled = javascript_sync_rpc_arg_bool(&request.args, 0, "__pty_set_raw_mode enabled")?;
    kernel
        .pty_set_discipline(
            EXECUTION_DRIVER_NAME,
            process.kernel_pid,
            0,
            LineDisciplineConfig {
                canonical: Some(!enabled),
                echo: Some(!enabled),
                isig: Some(!enabled),
            },
        )
        .map_err(kernel_error)?;
    Ok(Value::Null)
}

fn service_javascript_kernel_stdio_write_sync_rpc(
    kernel: &mut SidecarKernel,
    process: &mut ActiveProcess,
    request: &JavascriptSyncRpcRequest,
) -> Result<Value, SidecarError> {
    let fd = javascript_sync_rpc_arg_u32(&request.args, 0, "__kernel_stdio_write fd")?;
    let chunk = javascript_sync_rpc_bytes_arg(&request.args, 1, "__kernel_stdio_write chunk")?;

    let written = match fd {
        1 => kernel
            .write_process_stdout(EXECUTION_DRIVER_NAME, process.kernel_pid, &chunk)
            .map_err(kernel_error)?,
        2 => kernel
            .write_process_stderr(EXECUTION_DRIVER_NAME, process.kernel_pid, &chunk)
            .map_err(kernel_error)?,
        other => {
            return Err(SidecarError::InvalidState(format!(
                "__kernel_stdio_write only supports fd 1/2, got {other}"
            )));
        }
    };

    let event = if fd == 1 {
        ActiveExecutionEvent::Stdout(chunk)
    } else {
        ActiveExecutionEvent::Stderr(chunk)
    };
    process.queue_pending_execution_event(event)?;

    Ok(json!(written))
}

fn service_javascript_kernel_poll_sync_rpc(
    kernel: &mut SidecarKernel,
    process: &ActiveProcess,
    request: &JavascriptSyncRpcRequest,
) -> Result<Value, SidecarError> {
    let fd_requests: Vec<KernelPollFdRequest> = serde_json::from_value(
        request
            .args
            .first()
            .cloned()
            .unwrap_or_else(|| Value::Array(Vec::new())),
    )
    .map_err(|error| {
        SidecarError::InvalidState(format!(
            "__kernel_poll fd list must be a JSON array of {{ fd, events }} objects: {error}"
        ))
    })?;
    let timeout_ms =
        javascript_sync_rpc_arg_u64_optional(&request.args, 1, "__kernel_poll timeout ms")?
            .unwrap_or_default();
    let timeout_ms = i32::try_from(timeout_ms).map_err(|_| {
        SidecarError::InvalidState(String::from("__kernel_poll timeout ms must fit within i32"))
    })?;

    let poll_fds = fd_requests
        .iter()
        .map(|entry| PollFd {
            fd: entry.fd,
            events: PollEvents::from_bits(entry.events),
            revents: PollEvents::empty(),
        })
        .collect::<Vec<_>>();
    let result = kernel
        .poll_fds(
            EXECUTION_DRIVER_NAME,
            process.kernel_pid,
            poll_fds,
            timeout_ms,
        )
        .map_err(kernel_error)?;

    Ok(json!({
        "readyCount": result.ready_count,
        "fds": result
            .fds
            .into_iter()
            .map(|entry| KernelPollFdResponse {
                fd: entry.fd,
                events: entry.events.bits(),
                revents: entry.revents.bits(),
            })
            .collect::<Vec<_>>(),
    }))
}

fn install_kernel_stdin_pipe(kernel: &mut SidecarKernel, pid: u32) -> Result<u32, SidecarError> {
    let (read_fd, write_fd) = kernel
        .open_pipe(EXECUTION_DRIVER_NAME, pid)
        .map_err(kernel_error)?;
    kernel
        .fd_dup2(EXECUTION_DRIVER_NAME, pid, read_fd, 0)
        .map_err(kernel_error)?;
    kernel
        .fd_close(EXECUTION_DRIVER_NAME, pid, read_fd)
        .map_err(kernel_error)?;
    Ok(write_fd)
}

fn javascript_child_process_stdin_mode(request: &JavascriptChildProcessSpawnRequest) -> &str {
    request
        .options
        .stdio
        .first()
        .map(String::as_str)
        .unwrap_or("pipe")
}

pub(crate) fn write_kernel_process_stdin(
    kernel: &mut SidecarKernel,
    process: &mut ActiveProcess,
    chunk: &[u8],
) -> Result<(), SidecarError> {
    if process.runtime == GuestRuntimeKind::JavaScript {
        return Ok(());
    }
    let Some(writer_fd) = process.kernel_stdin_writer_fd else {
        return Ok(());
    };
    kernel
        .fd_write(EXECUTION_DRIVER_NAME, process.kernel_pid, writer_fd, chunk)
        .map(|_| ())
        .map_err(kernel_error)
}

pub(crate) fn close_kernel_process_stdin(
    kernel: &mut SidecarKernel,
    process: &mut ActiveProcess,
) -> Result<(), SidecarError> {
    let Some(writer_fd) = process.kernel_stdin_writer_fd.take() else {
        return Ok(());
    };
    kernel
        .fd_close(EXECUTION_DRIVER_NAME, process.kernel_pid, writer_fd)
        .map_err(kernel_error)
}

fn parse_http_header_collection(
    headers: &BTreeMap<String, Value>,
    label: &str,
) -> Result<HttpHeaderCollection, SidecarError> {
    let mut normalized = BTreeMap::<String, Vec<String>>::new();
    let mut raw_pairs = Vec::new();

    for (raw_name, value) in headers {
        let normalized_name = raw_name.to_ascii_lowercase();
        let values = match value {
            Value::String(text) => vec![text.clone()],
            Value::Array(values) => values
                .iter()
                .map(|entry| {
                    entry.as_str().map(str::to_owned).ok_or_else(|| {
                        SidecarError::InvalidState(format!(
                            "{label} header {raw_name} must contain only strings"
                        ))
                    })
                })
                .collect::<Result<Vec<_>, _>>()?,
            other => {
                return Err(SidecarError::InvalidState(format!(
                    "{label} header {raw_name} must be a string or string array, received {other}"
                )));
            }
        };
        raw_pairs.extend(
            values
                .iter()
                .cloned()
                .map(|entry| (raw_name.clone(), entry)),
        );
        normalized
            .entry(normalized_name)
            .or_default()
            .extend(values);
    }

    Ok(HttpHeaderCollection {
        normalized,
        raw_pairs,
    })
}

fn http_headers_json(headers: &HttpHeaderCollection) -> Value {
    let map = headers
        .normalized
        .iter()
        .map(|(name, values)| {
            let value = if values.len() == 1 {
                Value::String(values[0].clone())
            } else {
                Value::Array(values.iter().cloned().map(Value::String).collect())
            };
            (name.clone(), value)
        })
        .collect::<Map<String, Value>>();
    Value::Object(map)
}

fn http_raw_headers_json(headers: &HttpHeaderCollection) -> Value {
    Value::Array(
        headers
            .raw_pairs
            .iter()
            .flat_map(|(name, value)| [Value::String(name.clone()), Value::String(value.clone())])
            .collect(),
    )
}

fn is_loopback_request_host(host: &str) -> bool {
    let bare = host
        .strip_prefix('[')
        .and_then(|value| value.strip_suffix(']'))
        .unwrap_or(host);
    matches!(bare, "localhost" | "127.0.0.1" | "::1")
}

fn serialize_http_loopback_request(
    url: &Url,
    options: &JavascriptHttpRequestOptions,
    headers: &HttpHeaderCollection,
) -> Result<String, SidecarError> {
    let body_base64 = options
        .body
        .as_ref()
        .map(|body| base64::engine::general_purpose::STANDARD.encode(body.as_bytes()));
    serde_json::to_string(&json!({
        "method": options.method.clone().unwrap_or_else(|| String::from("GET")),
        "url": http_request_target(url),
        "headers": http_headers_json(headers),
        "rawHeaders": http_raw_headers_json(headers),
        "bodyBase64": body_base64,
    }))
    .map_err(|error| SidecarError::Execution(format!("ERR_AGENTOS_NODE_SYNC_RPC: {error}")))
}

fn http_request_target(url: &Url) -> String {
    let path = if url.path().is_empty() {
        "/"
    } else {
        url.path()
    };
    format!(
        "{path}{}",
        url.query()
            .map(|query| format!("?{query}"))
            .unwrap_or_default()
    )
}

fn find_kernel_http_listener_process(vm: &VmState, port: u16) -> Option<String> {
    vm.active_processes
        .iter()
        .find_map(|(process_id, process)| {
            process.tcp_listeners.values().find_map(|listener| {
                let socket_id = listener.kernel_socket_id?;
                let record = vm.kernel.socket_get(socket_id)?;
                let local_addr = record
                    .local_address()
                    .and_then(|address| resolve_tcp_bind_addr(address.host(), address.port()).ok())
                    .unwrap_or_else(|| listener.guest_local_addr());
                if local_addr.port() == port && is_vm_local_http_listener_addr(local_addr.ip()) {
                    Some(process_id.to_owned())
                } else {
                    None
                }
            })
        })
}

fn is_vm_local_http_listener_addr(ip: IpAddr) -> bool {
    ip.is_loopback() || ip.is_unspecified()
}

fn serialize_kernel_http_fetch_request(
    port: u16,
    path: &str,
    options: &JavascriptHttpRequestOptions,
    headers: &HttpHeaderCollection,
) -> Vec<u8> {
    let method = options.method.as_deref().unwrap_or("GET");
    let mut lines = vec![format!("{method} {path} HTTP/1.1")];
    let mut has_host = false;
    let mut has_connection = false;
    let mut has_content_length = false;
    for (name, values) in &headers.normalized {
        match name.as_str() {
            "host" => has_host = true,
            "connection" => has_connection = true,
            "content-length" => has_content_length = true,
            _ => {}
        }
        lines.push(format!("{name}: {}", values.join(", ")));
    }
    if !has_host {
        lines.push(format!("Host: 127.0.0.1:{port}"));
    }
    if !has_connection {
        lines.push(String::from("Connection: close"));
    }
    let body = options.body.as_deref().unwrap_or("").as_bytes();
    if !has_content_length && !body.is_empty() {
        lines.push(format!("Content-Length: {}", body.len()));
    }
    lines.push(String::new());
    lines.push(String::new());

    let mut request = lines.join("\r\n").into_bytes();
    request.extend_from_slice(body);
    request
}

fn parse_kernel_http_fetch_response(
    buffer: &[u8],
    peer_closed: bool,
    url: &str,
) -> Result<Option<String>, SidecarError> {
    let Some(header_end) = find_http_header_end(buffer) else {
        return Ok(None);
    };
    let header_bytes = &buffer[..header_end];
    let head = String::from_utf8_lossy(header_bytes);
    let mut lines = head.split("\r\n");
    let status_line = lines.next().unwrap_or_default();
    let mut status_parts = status_line.splitn(3, ' ');
    let version = status_parts.next().unwrap_or_default();
    if !version.starts_with("HTTP/") {
        return Err(SidecarError::Execution(format!(
            "invalid vm.fetch HTTP response status line: {status_line}"
        )));
    }
    let status = status_parts
        .next()
        .ok_or_else(|| {
            SidecarError::Execution(format!(
                "invalid vm.fetch HTTP response status line: {status_line}"
            ))
        })?
        .parse::<u16>()
        .map_err(|error| {
            SidecarError::Execution(format!(
                "invalid vm.fetch HTTP response status code in {status_line:?}: {error}"
            ))
        })?;
    let status_text = status_parts.next().unwrap_or_default();
    let mut headers = Vec::new();
    let mut raw_headers = Vec::new();
    let mut content_length = None;
    let mut transfer_encoding_values = Vec::new();
    for line in lines {
        if line.is_empty() {
            continue;
        }
        let Some((name, value)) = line.split_once(':') else {
            return Err(SidecarError::Execution(format!(
                "invalid vm.fetch HTTP response header line: {line}"
            )));
        };
        let value = value.trim().to_owned();
        let normalized = name.to_ascii_lowercase();
        if normalized == "content-length" {
            content_length = Some(value.parse::<usize>().map_err(|error| {
                SidecarError::Execution(format!(
                    "invalid vm.fetch Content-Length header {value:?}: {error}"
                ))
            })?);
        } else if normalized == "transfer-encoding" {
            transfer_encoding_values.push(value.clone());
        }
        headers.push(json!([normalized, value.clone()]));
        raw_headers.push(Value::String(name.to_owned()));
        raw_headers.push(Value::String(value));
    }

    let body_start = header_end + 4;
    let transfer_encoding = transfer_encoding_tokens(&transfer_encoding_values);
    let is_chunked = transfer_encoding.iter().any(|token| token == "chunked");
    let body = if is_chunked {
        if content_length.is_some() {
            return Err(SidecarError::Execution(String::from(
                "vm.fetch HTTP response cannot include both Transfer-Encoding: chunked and Content-Length",
            )));
        }
        if transfer_encoding.len() != 1 {
            return Err(SidecarError::Execution(format!(
                "unsupported vm.fetch Transfer-Encoding: {}",
                transfer_encoding.join(", ")
            )));
        }
        let Some(decoded) = decode_kernel_http_chunked_body(&buffer[body_start..])? else {
            return Ok(None);
        };
        decoded
    } else if !transfer_encoding.is_empty() {
        return Err(SidecarError::Execution(format!(
            "unsupported vm.fetch Transfer-Encoding: {}",
            transfer_encoding.join(", ")
        )));
    } else if let Some(content_length) = content_length {
        let body_end = body_start.saturating_add(content_length);
        if buffer.len() < body_end {
            return Ok(None);
        }
        buffer[body_start..body_end].to_vec()
    } else if peer_closed {
        buffer[body_start..].to_vec()
    } else {
        return Ok(None);
    };

    serde_json::to_string(&json!({
        "status": status,
        "statusText": status_text,
        "headers": headers,
        "rawHeaders": raw_headers,
        "body": base64::engine::general_purpose::STANDARD.encode(&body),
        "bodyEncoding": "base64",
        "url": url,
    }))
    .map(Some)
    .map_err(|error| SidecarError::Execution(format!("ERR_AGENTOS_NODE_SYNC_RPC: {error}")))
}

fn find_http_header_end(buffer: &[u8]) -> Option<usize> {
    buffer.windows(4).position(|window| window == b"\r\n\r\n")
}

fn find_crlf(buffer: &[u8], start: usize) -> Option<usize> {
    buffer
        .get(start..)?
        .windows(2)
        .position(|window| window == b"\r\n")
        .map(|offset| start + offset)
}

fn transfer_encoding_tokens(values: &[String]) -> Vec<String> {
    values
        .iter()
        .flat_map(|value| value.split(','))
        .map(|token| token.trim().to_ascii_lowercase())
        .filter(|token| !token.is_empty())
        .collect()
}

fn decode_kernel_http_chunked_body(buffer: &[u8]) -> Result<Option<Vec<u8>>, SidecarError> {
    let mut offset = 0;
    let mut body = Vec::new();
    loop {
        let Some(line_end) = find_crlf(buffer, offset) else {
            return Ok(None);
        };
        let size_line = std::str::from_utf8(&buffer[offset..line_end]).map_err(|error| {
            SidecarError::Execution(format!(
                "invalid vm.fetch chunk size line encoding: {error}"
            ))
        })?;
        let size_part = size_line.split(';').next().unwrap_or_default();
        if size_part.is_empty() || !size_part.bytes().all(|byte| byte.is_ascii_hexdigit()) {
            return Err(SidecarError::Execution(format!(
                "invalid vm.fetch chunk size line: {size_line:?}"
            )));
        }
        let chunk_size = usize::from_str_radix(size_part, 16).map_err(|error| {
            SidecarError::Execution(format!(
                "invalid vm.fetch chunk size {size_part:?}: {error}"
            ))
        })?;
        let chunk_start = line_end + 2;
        let chunk_end = chunk_start
            .checked_add(chunk_size)
            .ok_or_else(|| SidecarError::Execution(String::from("vm.fetch chunk size overflow")))?;
        if chunk_size > 0 {
            let chunk_terminator_end = chunk_end.checked_add(2).ok_or_else(|| {
                SidecarError::Execution(String::from("vm.fetch chunk terminator overflow"))
            })?;
            if chunk_terminator_end > buffer.len() {
                return Ok(None);
            }
            if buffer.get(chunk_end..chunk_terminator_end) != Some(b"\r\n") {
                return Err(SidecarError::Execution(String::from(
                    "invalid vm.fetch chunk terminator",
                )));
            }
            body.extend_from_slice(&buffer[chunk_start..chunk_end]);
            offset = chunk_terminator_end;
            continue;
        }

        if buffer.get(chunk_start..chunk_start + 2) == Some(b"\r\n") {
            return Ok(Some(body));
        }
        let Some(trailer_end) = find_http_header_end(&buffer[chunk_start..]) else {
            return Ok(None);
        };
        let trailer_bytes = &buffer[chunk_start..chunk_start + trailer_end];
        let trailers = String::from_utf8_lossy(trailer_bytes);
        for line in trailers.split("\r\n") {
            if line.is_empty() {
                continue;
            }
            if line.starts_with(' ') || line.starts_with('\t') || !line.contains(':') {
                return Err(SidecarError::Execution(format!(
                    "invalid vm.fetch chunk trailer line: {line}"
                )));
            }
        }
        return Ok(Some(body));
    }
}

fn kernel_http_fetch_target_exit_code(error: &SidecarError) -> Option<i32> {
    let SidecarError::Execution(message) = error else {
        return None;
    };
    message
        .strip_prefix("vm.fetch target exited before responding (exit code ")?
        .strip_suffix(')')?
        .parse()
        .ok()
}

fn service_host_fetch_target_event<B>(
    bridge: &SharedBridge<B>,
    vm_id: &str,
    dns: &VmDnsConfig,
    socket_paths: &JavascriptSocketPathContext,
    kernel: &mut SidecarKernel,
    process: &mut ActiveProcess,
    resource_limits: &ResourceLimits,
    wait: Duration,
) -> Result<bool, SidecarError>
where
    B: NativeSidecarBridge + Send + 'static,
    BridgeError<B>: fmt::Debug + Send + Sync + 'static,
{
    let Some(event) = process
        .execution
        .poll_event_blocking(wait)
        .map_err(|error| SidecarError::Execution(error.to_string()))?
    else {
        return Ok(false);
    };

    match event {
        ActiveExecutionEvent::JavascriptSyncRpcRequest(request) => {
            let network_counts = process.network_resource_counts();
            let response = service_javascript_sync_rpc(JavascriptSyncRpcServiceRequest {
                bridge,
                vm_id,
                dns,
                socket_paths,
                kernel,
                process,
                sync_request: &request,
                resource_limits,
                network_counts,
            });
            match response {
                Ok(result) => process
                    .execution
                    .respond_javascript_sync_rpc_success(request.id, result)
                    .or_else(ignore_stale_javascript_sync_rpc_response)?,
                Err(error) => process
                    .execution
                    .respond_javascript_sync_rpc_error(
                        request.id,
                        javascript_sync_rpc_error_code(&error),
                        error.to_string(),
                    )
                    .or_else(ignore_stale_javascript_sync_rpc_response)?,
            }
        }
        ActiveExecutionEvent::Exited(code) => {
            return Err(SidecarError::Execution(format!(
                "vm.fetch target exited before responding (exit code {code})"
            )));
        }
        other => {
            process.queue_pending_execution_event(other)?;
        }
    }
    Ok(true)
}

fn drain_host_fetch_target_events<B>(
    bridge: &SharedBridge<B>,
    vm_id: &str,
    vm: &mut VmState,
    target_process_id: &str,
    socket_paths: &JavascriptSocketPathContext,
    resource_limits: &ResourceLimits,
) -> Result<(), SidecarError>
where
    B: NativeSidecarBridge + Send + 'static,
    BridgeError<B>: fmt::Debug + Send + Sync + 'static,
{
    for _ in 0..32 {
        let dns = vm.dns.clone();
        let Some(process) = vm.active_processes.get_mut(target_process_id) else {
            break;
        };
        let serviced = service_host_fetch_target_event(
            bridge,
            vm_id,
            &dns,
            socket_paths,
            &mut vm.kernel,
            process,
            resource_limits,
            Duration::from_millis(1),
        )?;
        if !serviced {
            break;
        }
    }
    Ok(())
}

fn dispatch_kernel_http_fetch<B>(
    bridge: &SharedBridge<B>,
    vm_id: &str,
    vm: &mut VmState,
    target_process_id: &str,
    port: u16,
    path: &str,
    options: &JavascriptHttpRequestOptions,
    headers: &HttpHeaderCollection,
    max_fetch_response_bytes: usize,
) -> Result<String, SidecarError>
where
    B: NativeSidecarBridge + Send + 'static,
    BridgeError<B>: fmt::Debug + Send + Sync + 'static,
{
    let socket_paths = build_javascript_socket_path_context(vm)?;
    let family = JavascriptSocketFamily::Ipv4;
    let local_port = allocate_guest_listen_port(
        0,
        family,
        &socket_paths.used_tcp_guest_ports,
        socket_paths.listen_policy,
    )?;
    let resource_limits = vm.kernel.resource_limits().clone();
    let network_counts = vm_network_resource_counts(vm);
    check_network_resource_limit(
        resource_limits.max_sockets,
        network_counts.sockets,
        2,
        "socket",
    )?;
    check_network_resource_limit(
        resource_limits.max_connections,
        network_counts.connections,
        2,
        "connection",
    )?;

    let kernel_pid = vm
        .active_processes
        .get(target_process_id)
        .ok_or_else(|| {
            SidecarError::InvalidState(format!(
                "vm.fetch target process disappeared: {target_process_id}"
            ))
        })?
        .kernel_pid;
    let socket_id = vm
        .kernel
        .socket_create(EXECUTION_DRIVER_NAME, kernel_pid, SocketSpec::tcp())
        .map_err(kernel_error)?;

    let result = dispatch_kernel_http_fetch_with_socket(
        bridge,
        vm_id,
        vm,
        target_process_id,
        kernel_pid,
        socket_id,
        local_port,
        port,
        path,
        options,
        headers,
        &socket_paths,
        &resource_limits,
        max_fetch_response_bytes,
    );
    let close_result = vm
        .kernel
        .socket_close(EXECUTION_DRIVER_NAME, kernel_pid, socket_id)
        .map_err(kernel_error);
    let cleanup_result = if result.is_err() {
        drain_host_fetch_target_events(
            bridge,
            vm_id,
            vm,
            target_process_id,
            &socket_paths,
            &resource_limits,
        )
    } else {
        Ok(())
    };
    match (result, close_result) {
        (Ok(response), Ok(())) => cleanup_result.map(|()| response),
        (Err(error), _) => Err(error),
        (Ok(_), Err(error)) => Err(error),
    }
}

#[allow(clippy::too_many_arguments)]
fn dispatch_kernel_http_fetch_with_socket<B>(
    bridge: &SharedBridge<B>,
    vm_id: &str,
    vm: &mut VmState,
    target_process_id: &str,
    kernel_pid: u32,
    socket_id: SocketId,
    local_port: u16,
    port: u16,
    path: &str,
    options: &JavascriptHttpRequestOptions,
    headers: &HttpHeaderCollection,
    socket_paths: &JavascriptSocketPathContext,
    resource_limits: &ResourceLimits,
    max_fetch_response_bytes: usize,
) -> Result<String, SidecarError>
where
    B: NativeSidecarBridge + Send + 'static,
    BridgeError<B>: fmt::Debug + Send + Sync + 'static,
{
    vm.kernel
        .socket_bind_inet(
            EXECUTION_DRIVER_NAME,
            kernel_pid,
            socket_id,
            InetSocketAddress::new("127.0.0.1", local_port),
        )
        .map_err(kernel_error)?;
    vm.kernel
        .socket_connect_inet_loopback(
            EXECUTION_DRIVER_NAME,
            kernel_pid,
            socket_id,
            InetSocketAddress::new("127.0.0.1", port),
        )
        .map_err(kernel_error)?;

    let request_bytes = serialize_kernel_http_fetch_request(port, path, options, headers);
    vm.kernel
        .socket_write(EXECUTION_DRIVER_NAME, kernel_pid, socket_id, &request_bytes)
        .map_err(kernel_error)?;

    let mut response_buffer = Vec::new();
    let mut peer_closed = false;
    let url = format!("http://127.0.0.1:{port}{path}");
    let deadline = Instant::now() + http_loopback_request_timeout();
    loop {
        if let Some(response) =
            parse_kernel_http_fetch_response(&response_buffer, peer_closed, &url)?
        {
            ensure_vm_fetch_response_within_limit(&response, "vm.fetch", max_fetch_response_bytes)?;
            return Ok(response);
        }
        if Instant::now() >= deadline {
            let preview = String::from_utf8_lossy(&response_buffer);
            return Err(SidecarError::Execution(format!(
                "vm.fetch timed out waiting for kernel TCP HTTP response ({} buffered bytes: {:?})",
                response_buffer.len(),
                preview.chars().take(200).collect::<String>()
            )));
        }

        {
            let dns = vm.dns.clone();
            let process = vm
                .active_processes
                .get_mut(target_process_id)
                .ok_or_else(|| {
                    SidecarError::InvalidState(format!(
                        "vm.fetch target process disappeared: {target_process_id}"
                    ))
                })?;
            service_host_fetch_target_event(
                bridge,
                vm_id,
                &dns,
                socket_paths,
                &mut vm.kernel,
                process,
                resource_limits,
                Duration::from_millis(5),
            )?;
        }

        let poll = vm
            .kernel
            .poll_targets(
                EXECUTION_DRIVER_NAME,
                kernel_pid,
                vec![PollTargetEntry::socket(
                    socket_id,
                    POLLIN | POLLHUP | POLLERR,
                )],
                5,
            )
            .map_err(kernel_error)?;
        let revents = poll
            .targets
            .first()
            .map(|entry| entry.revents)
            .unwrap_or_else(PollEvents::empty);
        if revents.intersects(POLLERR) {
            return Err(SidecarError::Execution(String::from(
                "vm.fetch kernel TCP socket reported POLLERR",
            )));
        }
        if revents.intersects(POLLIN) {
            match vm
                .kernel
                .socket_read(EXECUTION_DRIVER_NAME, kernel_pid, socket_id, 64 * 1024)
            {
                Ok(Some(bytes)) if !bytes.is_empty() => {
                    response_buffer.extend(bytes);
                    ensure_vm_fetch_raw_response_buffer_within_limit(
                        response_buffer.len(),
                        "vm.fetch",
                    )?;
                }
                Ok(Some(_)) => {}
                Ok(None) => peer_closed = true,
                Err(error) if error.code() == "EAGAIN" => {}
                Err(error) => return Err(kernel_error(error)),
            }
        }
        if revents.intersects(POLLHUP) {
            peer_closed = true;
        }
    }
}

fn outbound_http_response_json(url: &Url, response: ureq::Response) -> Result<Value, SidecarError> {
    let status = response.status();
    let status_text = response.status_text().to_owned();
    let mut header_pairs = Vec::new();
    let mut raw_headers = Vec::new();
    for raw_name in response.headers_names() {
        for value in response.all(&raw_name) {
            header_pairs.push(json!([raw_name.to_ascii_lowercase(), value]));
            raw_headers.push(Value::String(raw_name.clone()));
            raw_headers.push(Value::String(value.to_owned()));
        }
    }
    let mut reader = response.into_reader();
    let mut body = Vec::new();
    reader.read_to_end(&mut body).map_err(|error| {
        SidecarError::Execution(format!("failed to read HTTP response: {error}"))
    })?;
    serde_json::to_string(&json!({
        "status": status,
        "statusText": status_text,
        "headers": header_pairs,
        "rawHeaders": raw_headers,
        "body": base64::engine::general_purpose::STANDARD.encode(body),
        "bodyEncoding": "base64",
        "url": url.as_str(),
    }))
    .map(Value::String)
    .map_err(|error| SidecarError::Execution(format!("ERR_AGENTOS_NODE_SYNC_RPC: {error}")))
}

/// Split a ureq resolver `netloc` (`host:port`, with optional `[..]` IPv6
/// brackets) into its host and port components. Returns `None` if the port is
/// missing or unparseable.
fn split_netloc(netloc: &str) -> Option<(&str, u16)> {
    let (host, port) = netloc.rsplit_once(':')?;
    let port: u16 = port.parse().ok()?;
    let host = host
        .strip_prefix('[')
        .and_then(|rest| rest.strip_suffix(']'))
        .unwrap_or(host);
    Some((host, port))
}

fn issue_outbound_http_request(
    url: &Url,
    options: &JavascriptHttpRequestOptions,
    headers: &HttpHeaderCollection,
    pinned_addresses: &[IpAddr],
) -> Result<Value, SidecarError> {
    let method = options.method.as_deref().unwrap_or("GET");
    // Pin the underlying resolver to the egress-vetted addresses. ureq performs
    // its own DNS resolution for the TCP/TLS connect; without this override an
    // https:// request would re-resolve the hostname through the host resolver
    // (a rebinding DNS server could then return a private/metadata IP that the
    // earlier range check would have rejected). The pinned resolver returns only
    // the vetted addresses and refuses any host it was not vetted for, while the
    // request URL keeps the original hostname so TLS SNI and the Host header stay
    // correct.
    let pinned_host = url.host_str().map(str::to_owned);
    let pinned: Vec<IpAddr> = pinned_addresses.to_vec();
    let resolver = move |netloc: &str| -> std::io::Result<Vec<SocketAddr>> {
        let (host, port) = split_netloc(netloc).ok_or_else(|| {
            std::io::Error::new(
                std::io::ErrorKind::InvalidInput,
                format!("invalid network location: {netloc}"),
            )
        })?;
        let expected_host = pinned_host.as_deref();
        if expected_host != Some(host) {
            return Err(std::io::Error::new(
                std::io::ErrorKind::PermissionDenied,
                format!(
                    "EACCES: outbound HTTP resolver pinned to {expected_host:?}, refusing {host}"
                ),
            ));
        }
        if pinned.is_empty() {
            return Err(std::io::Error::new(
                std::io::ErrorKind::PermissionDenied,
                "EACCES: no egress-vetted address available for outbound HTTP request",
            ));
        }
        Ok(pinned.iter().map(|ip| SocketAddr::new(*ip, port)).collect())
    };
    let mut agent_builder = ureq::AgentBuilder::new()
        .resolver(resolver)
        .timeout_connect(Duration::from_secs(5))
        .timeout_read(Duration::from_secs(15))
        .timeout_write(Duration::from_secs(15));
    if url.scheme() == "https" {
        let tls_options = JavascriptTlsBridgeOptions {
            is_server: false,
            servername: url.host_str().map(str::to_owned),
            alpn_protocols: Some(vec![String::from("http/1.1")]),
            reject_unauthorized: options.reject_unauthorized,
            ..JavascriptTlsBridgeOptions::default()
        };
        agent_builder = agent_builder.tls_config(Arc::new(build_client_tls_config(&tls_options)?));
    }
    let agent = agent_builder.build();
    let mut request = agent.request_url(method, url);
    for (name, values) in &headers.normalized {
        if name == "host" {
            continue;
        }
        let header_value = values.join(", ");
        request = request.set(name, &header_value);
    }
    let response = match options.body.as_deref() {
        Some(body) => request.send_string(body),
        None => request.call(),
    };

    match response {
        Ok(response) => outbound_http_response_json(url, response),
        Err(ureq::Error::Status(_, response)) => outbound_http_response_json(url, response),
        Err(ureq::Error::Transport(error)) => Err(SidecarError::Execution(format!(
            "ERR_HTTP_REQUEST_FAILED: {error}"
        ))),
    }
}

fn wait_for_loopback_http_response<B>(
    request: LoopbackHttpResponseWaitRequest<'_, B>,
) -> Result<String, SidecarError>
where
    B: NativeSidecarBridge + Send + 'static,
    BridgeError<B>: fmt::Debug + Send + Sync + 'static,
{
    let LoopbackHttpResponseWaitRequest {
        bridge,
        vm_id,
        dns,
        socket_paths,
        kernel,
        process,
        resource_limits,
        request_key,
    } = request;
    let deadline = Instant::now() + http_loopback_request_timeout();
    loop {
        if let Some(response) = process
            .pending_http_requests
            .get(&request_key)
            .and_then(|response| response.clone())
        {
            process.pending_http_requests.remove(&request_key);
            return Ok(response);
        }

        if Instant::now() >= deadline {
            process.pending_http_requests.remove(&request_key);
            return Err(SidecarError::Execution(String::from(
                "HTTP loopback request timed out waiting for net.http_respond",
            )));
        }

        let Some(event) = process
            .execution
            .poll_event_blocking(Duration::from_millis(10))
            .map_err(|error| SidecarError::Execution(error.to_string()))?
        else {
            continue;
        };

        match event {
            ActiveExecutionEvent::JavascriptSyncRpcRequest(request) => {
                let network_counts = process.network_resource_counts();
                let response = service_javascript_sync_rpc(JavascriptSyncRpcServiceRequest {
                    bridge,
                    vm_id,
                    dns,
                    socket_paths,
                    kernel,
                    process,
                    sync_request: &request,
                    resource_limits,
                    network_counts,
                });
                match response {
                    Ok(result) => process
                        .execution
                        .respond_javascript_sync_rpc_success(request.id, result)
                        .or_else(ignore_stale_javascript_sync_rpc_response)?,
                    Err(error) => process
                        .execution
                        .respond_javascript_sync_rpc_error(
                            request.id,
                            javascript_sync_rpc_error_code(&error),
                            error.to_string(),
                        )
                        .or_else(ignore_stale_javascript_sync_rpc_response)?,
                }
            }
            ActiveExecutionEvent::Exited(code) => {
                process.pending_http_requests.remove(&request_key);
                return Err(SidecarError::Execution(format!(
                    "HTTP loopback server exited before responding (exit code {code})"
                )));
            }
            ActiveExecutionEvent::Stdout(_)
            | ActiveExecutionEvent::Stderr(_)
            | ActiveExecutionEvent::PythonVfsRpcRequest(_)
            | ActiveExecutionEvent::SignalState { .. } => {}
        }
    }
}

pub(crate) fn dispatch_loopback_http_request<B>(
    request: LoopbackHttpDispatchRequest<'_, B>,
) -> Result<String, SidecarError>
where
    B: NativeSidecarBridge + Send + 'static,
    BridgeError<B>: fmt::Debug + Send + Sync + 'static,
{
    let LoopbackHttpDispatchRequest {
        bridge,
        vm_id,
        dns,
        socket_paths,
        kernel,
        process,
        resource_limits,
        server_id,
        request_json,
    } = request;
    let request_id = {
        let server = process.http_servers.get_mut(&server_id).ok_or_else(|| {
            SidecarError::InvalidState(format!("HTTP target server disappeared: {server_id}"))
        })?;
        server.next_request_id += 1;
        server.next_request_id
    };
    process
        .pending_http_requests
        .insert((server_id, request_id), None);
    process.execution.send_javascript_stream_event(
        "http_request",
        json!({
            "serverId": server_id,
            "requestId": request_id,
            "request": request_json,
        }),
    )?;
    wait_for_loopback_http_response(LoopbackHttpResponseWaitRequest {
        bridge,
        vm_id,
        dns,
        socket_paths,
        kernel,
        process,
        resource_limits,
        request_key: (server_id, request_id),
    })
}

fn ensure_vm_fetch_response_within_limit(
    response_json: &str,
    operation: &str,
    limit: usize,
) -> Result<(), SidecarError> {
    let size = response_json.len();
    if size > limit {
        return Err(SidecarError::Execution(format!(
            "{operation} payload is {size} bytes, limit is {limit}"
        )));
    }
    Ok(())
}

fn ensure_vm_fetch_raw_response_buffer_within_limit(
    size: usize,
    operation: &str,
) -> Result<(), SidecarError> {
    if size > VM_FETCH_BUFFER_LIMIT_BYTES {
        return Err(SidecarError::Execution(format!(
            "{operation} raw response buffer is {size} bytes, limit is {VM_FETCH_BUFFER_LIMIT_BYTES}"
        )));
    }
    Ok(())
}

pub(crate) fn ensure_vm_fetch_response_frame_within_limit(
    response: &ResponseFrame,
    max_frame_bytes: usize,
) -> Result<(), SidecarError> {
    let max_frame_bytes = max_frame_bytes.min(VM_FETCH_BUFFER_LIMIT_BYTES);
    let frame = crate::protocol::to_generated_protocol_frame(
        &crate::protocol::ProtocolFrame::Response(response.clone()),
    )
    .map_err(|error| SidecarError::FrameTooLarge(error.to_string()))?;
    let WireProtocolFrame::ResponseFrame(_) = &frame else {
        return Err(SidecarError::FrameTooLarge(String::from(
            "vm fetch response converted to non-response wire frame",
        )));
    };
    WireFrameCodec::new(max_frame_bytes)
        .encode(&frame)
        .map(|_| ())
        .map_err(|error| SidecarError::FrameTooLarge(error.to_string()))
}

fn service_javascript_dns_sync_rpc<B>(
    bridge: &SharedBridge<B>,
    kernel: &SidecarKernel,
    vm_id: &str,
    dns: &VmDnsConfig,
    request: &JavascriptSyncRpcRequest,
) -> Result<Value, SidecarError>
where
    B: NativeSidecarBridge + Send + 'static,
    BridgeError<B>: fmt::Debug + Send + Sync + 'static,
{
    match request.method.as_str() {
        "dns.lookup" => {
            let payload = request
                .args
                .first()
                .cloned()
                .ok_or_else(|| {
                    SidecarError::InvalidState(String::from(
                        "dns.lookup requires a request payload",
                    ))
                })
                .and_then(|value| {
                    serde_json::from_value::<JavascriptDnsLookupRequest>(value).map_err(|error| {
                        SidecarError::InvalidState(format!("invalid dns.lookup payload: {error}"))
                    })
                })?;
            let addresses = filter_dns_ip_addrs(
                resolve_dns_ip_addrs(
                    bridge,
                    kernel,
                    vm_id,
                    dns,
                    &payload.hostname,
                    DnsLookupPolicy::CheckPermissions,
                )?,
                payload.family,
            )?;
            let addresses = filter_dns_safe_ip_addrs(addresses, &payload.hostname)?;
            Ok(Value::Array(
                addresses
                    .into_iter()
                    .map(|ip| {
                        json!({
                            "address": ip.to_string(),
                            "family": if ip.is_ipv6() { 6 } else { 4 },
                        })
                    })
                    .collect(),
            ))
        }
        "dns.resolve" | "dns.resolve4" | "dns.resolve6" => {
            let payload = request
                .args
                .first()
                .cloned()
                .ok_or_else(|| {
                    SidecarError::InvalidState(String::from(
                        "dns.resolve requires a request payload",
                    ))
                })
                .and_then(|value| {
                    serde_json::from_value::<JavascriptDnsResolveRequest>(value).map_err(|error| {
                        SidecarError::InvalidState(format!("invalid dns.resolve payload: {error}"))
                    })
                })?;
            let requested_type = match request.method.as_str() {
                "dns.resolve4" => String::from("A"),
                "dns.resolve6" => String::from("AAAA"),
                _ => payload
                    .rrtype
                    .as_deref()
                    .unwrap_or("A")
                    .to_ascii_uppercase(),
            };
            let record_type = parse_dns_record_type(&requested_type)?;
            let resolution = resolve_dns_records(
                bridge,
                kernel,
                vm_id,
                dns,
                &payload.hostname,
                record_type,
                DnsLookupPolicy::CheckPermissions,
            )?;
            dns_resolution_to_node_value(&resolution, &requested_type)
        }
        other => Err(SidecarError::InvalidState(format!(
            "unsupported JavaScript dns sync RPC method {other}"
        ))),
    }
}

fn service_javascript_dgram_sync_rpc<B>(
    request: JavascriptDgramSyncRpcServiceRequest<'_, B>,
) -> Result<Value, SidecarError>
where
    B: NativeSidecarBridge + Send + 'static,
    BridgeError<B>: fmt::Debug + Send + Sync + 'static,
{
    let JavascriptDgramSyncRpcServiceRequest {
        bridge,
        kernel,
        vm_id,
        dns,
        socket_paths,
        process,
        sync_request: request,
        resource_limits,
        network_counts,
    } = request;
    match request.method.as_str() {
        "dgram.createSocket" => {
            check_network_resource_limit(
                resource_limits.max_sockets,
                network_counts.sockets,
                1,
                "socket",
            )?;
            let payload = request
                .args
                .first()
                .cloned()
                .ok_or_else(|| {
                    SidecarError::InvalidState(String::from(
                        "dgram.createSocket requires a request payload",
                    ))
                })
                .and_then(|value| {
                    serde_json::from_value::<JavascriptDgramCreateSocketRequest>(value).map_err(
                        |error| {
                            SidecarError::InvalidState(format!(
                                "invalid dgram.createSocket payload: {error}"
                            ))
                        },
                    )
                })?;
            let family = JavascriptUdpFamily::from_socket_type(&payload.socket_type)?;
            let socket_id = process.allocate_udp_socket_id();
            process.udp_sockets.insert(
                socket_id.clone(),
                ActiveUdpSocket::new(kernel, process.kernel_pid, family)?,
            );
            Ok(json!({
                "socketId": socket_id,
                "type": family.socket_type(),
            }))
        }
        "dgram.bind" => {
            let socket_id = javascript_sync_rpc_arg_str(&request.args, 0, "dgram.bind socket id")?;
            let payload = request
                .args
                .get(1)
                .cloned()
                .ok_or_else(|| {
                    SidecarError::InvalidState(String::from(
                        "dgram.bind requires a request payload",
                    ))
                })
                .and_then(|value| {
                    serde_json::from_value::<JavascriptDgramBindRequest>(value).map_err(|error| {
                        SidecarError::InvalidState(format!("invalid dgram.bind payload: {error}"))
                    })
                })?;
            let socket = process.udp_sockets.get_mut(socket_id).ok_or_else(|| {
                SidecarError::InvalidState(format!("unknown UDP socket {socket_id}"))
            })?;
            let local_addr = socket.bind(
                kernel,
                process.kernel_pid,
                payload.address.as_deref(),
                payload.port,
                socket_paths,
            )?;
            Ok(json!({
                "localAddress": local_addr.ip().to_string(),
                "localPort": local_addr.port(),
                "family": socket_addr_family(&local_addr),
            }))
        }
        "dgram.send" => {
            let socket_id = javascript_sync_rpc_arg_str(&request.args, 0, "dgram.send socket id")?;
            let chunk = javascript_sync_rpc_bytes_arg(&request.args, 1, "dgram.send payload")?;
            let payload = request
                .args
                .get(2)
                .cloned()
                .ok_or_else(|| {
                    SidecarError::InvalidState(String::from(
                        "dgram.send requires a request payload",
                    ))
                })
                .and_then(|value| {
                    serde_json::from_value::<JavascriptDgramSendRequest>(value).map_err(|error| {
                        SidecarError::InvalidState(format!("invalid dgram.send payload: {error}"))
                    })
                })?;
            let socket = process.udp_sockets.get_mut(socket_id).ok_or_else(|| {
                SidecarError::InvalidState(format!("unknown UDP socket {socket_id}"))
            })?;
            let (written, local_addr) = socket.send_to(ActiveUdpSendToRequest {
                bridge,
                kernel,
                kernel_pid: process.kernel_pid,
                vm_id,
                dns,
                host: payload.address.as_deref().unwrap_or("localhost"),
                port: payload.port,
                context: socket_paths,
                contents: &chunk,
            })?;
            Ok(json!({
                "bytes": written,
                "localAddress": local_addr.ip().to_string(),
                "localPort": local_addr.port(),
                "family": socket_addr_family(&local_addr),
            }))
        }
        "dgram.poll" => {
            let socket_id = javascript_sync_rpc_arg_str(&request.args, 0, "dgram.poll socket id")?;
            let wait_ms =
                javascript_sync_rpc_arg_u64_optional(&request.args, 1, "dgram.poll wait ms")?
                    .unwrap_or_default();
            let event = {
                let socket = process.udp_sockets.get(socket_id).ok_or_else(|| {
                    SidecarError::InvalidState(format!("unknown UDP socket {socket_id}"))
                })?;
                socket.poll(kernel, process.kernel_pid, Duration::from_millis(wait_ms))?
            };

            match event {
                Some(JavascriptUdpSocketEvent::Message { data, remote_addr }) => {
                    let family = JavascriptSocketFamily::from_ip(remote_addr.ip());
                    let guest_remote_port = if is_loopback_ip(remote_addr.ip()) {
                        socket_paths
                            .guest_udp_port_for_host_port(family, remote_addr.port())
                            .unwrap_or(remote_addr.port())
                    } else {
                        remote_addr.port()
                    };
                    Ok(json!({
                    "type": "message",
                    "data": javascript_sync_rpc_bytes_value(&data),
                    "remoteAddress": remote_addr.ip().to_string(),
                    "remotePort": guest_remote_port,
                    "remoteFamily": socket_addr_family(&remote_addr),
                    }))
                }
                Some(JavascriptUdpSocketEvent::Error { code, message }) => Ok(json!({
                    "type": "error",
                    "code": code,
                    "message": message,
                })),
                None => Ok(Value::Null),
            }
        }
        "dgram.close" => {
            let socket_id = javascript_sync_rpc_arg_str(&request.args, 0, "dgram.close socket id")?;
            let mut socket = process.udp_sockets.remove(socket_id).ok_or_else(|| {
                SidecarError::InvalidState(format!("unknown UDP socket {socket_id}"))
            })?;
            socket.close(kernel, process.kernel_pid);
            Ok(Value::Null)
        }
        "dgram.address" => {
            let socket_id =
                javascript_sync_rpc_arg_str(&request.args, 0, "dgram.address socket id")?;
            let socket = process.udp_sockets.get(socket_id).ok_or_else(|| {
                SidecarError::InvalidState(format!("unknown UDP socket {socket_id}"))
            })?;
            let local_addr = socket.local_addr().ok_or_else(|| {
                SidecarError::Execution(String::from("EBADF: bad file descriptor"))
            })?;
            javascript_net_json_string(
                json!({
                    "address": local_addr.ip().to_string(),
                    "port": local_addr.port(),
                    "family": socket_addr_family(&local_addr),
                }),
                "dgram.address",
            )
        }
        "dgram.setBufferSize" => {
            let socket_id =
                javascript_sync_rpc_arg_str(&request.args, 0, "dgram.setBufferSize socket id")?;
            let which =
                javascript_sync_rpc_arg_str(&request.args, 1, "dgram.setBufferSize buffer kind")?;
            let size = javascript_sync_rpc_arg_u64(&request.args, 2, "dgram.setBufferSize size")?;
            let size = usize::try_from(size).map_err(|_| {
                SidecarError::InvalidState(String::from(
                    "dgram.setBufferSize size must fit within usize",
                ))
            })?;
            let socket = process.udp_sockets.get_mut(socket_id).ok_or_else(|| {
                SidecarError::InvalidState(format!("unknown UDP socket {socket_id}"))
            })?;
            socket.set_buffer_size(which, size)?;
            Ok(Value::Null)
        }
        "dgram.getBufferSize" => {
            let socket_id =
                javascript_sync_rpc_arg_str(&request.args, 0, "dgram.getBufferSize socket id")?;
            let which =
                javascript_sync_rpc_arg_str(&request.args, 1, "dgram.getBufferSize buffer kind")?;
            let socket = process.udp_sockets.get(socket_id).ok_or_else(|| {
                SidecarError::InvalidState(format!("unknown UDP socket {socket_id}"))
            })?;
            let size = socket.get_buffer_size(which)?;
            Ok(json!(size))
        }
        other => Err(SidecarError::InvalidState(format!(
            "unsupported JavaScript dgram sync RPC method {other}"
        ))),
    }
}

#[derive(Debug)]
struct ClientHttp2StreamState {
    send_stream: Option<h2::SendStream<Bytes>>,
}

#[derive(Debug)]
struct ServerHttp2StreamState {
    send_response: Option<ServerHttp2Responder>,
    send_stream: Option<h2::SendStream<Bytes>>,
}

#[derive(Debug)]
enum ServerHttp2Responder {
    Regular(server::SendResponse<Bytes>),
    Pushed(server::SendPushedResponse<Bytes>),
}

const HTTP2_DEFAULT_WINDOW_SIZE: u32 = 65_535;
const HTTP2_POLL_DELAY: Duration = Duration::from_millis(10);

fn http2_runtime_snapshot() -> Http2RuntimeSnapshot {
    Http2RuntimeSnapshot {
        effective_local_window_size: HTTP2_DEFAULT_WINDOW_SIZE,
        local_window_size: HTTP2_DEFAULT_WINDOW_SIZE,
        remote_window_size: HTTP2_DEFAULT_WINDOW_SIZE,
        next_stream_id: 1,
        outbound_queue_size: 1,
        deflate_dynamic_table_size: 0,
        inflate_dynamic_table_size: 0,
    }
}

fn http2_snapshot_json(snapshot: &Http2SessionSnapshot) -> Result<String, SidecarError> {
    serde_json::to_string(snapshot)
        .map_err(|error| SidecarError::Execution(format!("ERR_AGENTOS_NODE_SYNC_RPC: {error}")))
}

fn http2_event_value(event: &Http2BridgeEvent) -> Result<Value, SidecarError> {
    serde_json::to_string(event)
        .map(Value::String)
        .map_err(|error| SidecarError::Execution(format!("ERR_AGENTOS_NODE_SYNC_RPC: {error}")))
}

fn push_http2_server_event(
    shared: &Arc<Mutex<crate::state::Http2SharedState>>,
    server_id: u64,
    event: Http2BridgeEvent,
) {
    if let Ok(mut state) = shared.lock() {
        state
            .server_events
            .entry(server_id)
            .or_default()
            .push_back(event);
    }
}

fn push_http2_session_event(
    shared: &Arc<Mutex<crate::state::Http2SharedState>>,
    session_id: u64,
    event: Http2BridgeEvent,
) {
    if let Ok(mut state) = shared.lock() {
        state
            .session_events
            .entry(session_id)
            .or_default()
            .push_back(event);
    }
}

fn pop_http2_event(
    queue: &mut BTreeMap<u64, VecDeque<Http2BridgeEvent>>,
    id: u64,
) -> Option<Http2BridgeEvent> {
    queue.get_mut(&id).and_then(VecDeque::pop_front)
}

fn wait_for_http2_event(
    shared: &Arc<Mutex<crate::state::Http2SharedState>>,
    id: u64,
    is_server: bool,
    wait_ms: u64,
) -> Option<Http2BridgeEvent> {
    let deadline = Instant::now() + Duration::from_millis(wait_ms);
    loop {
        if let Ok(mut state) = shared.lock() {
            let queue = if is_server {
                &mut state.server_events
            } else {
                &mut state.session_events
            };
            if let Some(event) = pop_http2_event(queue, id) {
                return Some(event);
            }
        }
        if wait_ms == 0 || Instant::now() >= deadline {
            return None;
        }
        thread::sleep(HTTP2_POLL_DELAY);
    }
}

fn next_http2_session_id(shared: &mut crate::state::Http2SharedState) -> u64 {
    shared.next_session_id += 1;
    shared.next_session_id
}

fn next_http2_stream_id(shared: &mut crate::state::Http2SharedState) -> u64 {
    shared.next_stream_id += 1;
    shared.next_stream_id
}

fn http2_reason(code: Option<u32>) -> Reason {
    code.unwrap_or(Reason::NO_ERROR.into()).into()
}

fn http2_error_payload(message: impl Into<String>) -> String {
    serde_json::to_string(&json!({
        "name": "Error",
        "code": "ERR_HTTP2_ERROR",
        "message": message.into(),
    }))
    .unwrap_or_else(|_| {
        String::from(
            "{\"name\":\"Error\",\"code\":\"ERR_HTTP2_ERROR\",\"message\":\"HTTP/2 bridge error\"}",
        )
    })
}

fn http2_socket_snapshot(local_addr: SocketAddr, remote_addr: SocketAddr) -> Http2SocketSnapshot {
    Http2SocketSnapshot {
        encrypted: false,
        allow_half_open: false,
        local_address: Some(local_addr.ip().to_string()),
        local_port: Some(local_addr.port()),
        local_family: Some(socket_addr_family(&local_addr).to_string()),
        remote_address: Some(remote_addr.ip().to_string()),
        remote_port: Some(remote_addr.port()),
        remote_family: Some(socket_addr_family(&remote_addr).to_string()),
        servername: None,
        alpn_protocol: Some(String::from("h2c")),
    }
}

fn http2_wait_result(kind: &str, id: u64) -> Value {
    json!({
        "kind": kind,
        "id": id,
    })
}

fn is_http2_terminal_event(event: &Http2BridgeEvent, is_server: bool, id: u64) -> bool {
    if is_server {
        event.kind == "serverClose" && event.id == id
    } else {
        event.kind == "sessionClose" && event.id == id
    }
}

fn dispatch_http2_wait_loop(
    process: &ActiveProcess,
    id: u64,
    is_server: bool,
) -> Result<Value, SidecarError> {
    loop {
        if let Some(event) = wait_for_http2_event(&process.http2.shared, id, is_server, 50) {
            let payload = serde_json::to_value(&event).map_err(|error| {
                SidecarError::Execution(format!("ERR_AGENTOS_NODE_SYNC_RPC: {error}"))
            })?;
            process
                .execution
                .send_javascript_stream_event("http2", payload.clone())?;
            if is_http2_terminal_event(&event, is_server, id) {
                return Ok(payload);
            }
            continue;
        }

        let exists = process
            .http2
            .shared
            .lock()
            .map(|state| {
                if is_server {
                    state.servers.contains_key(&id)
                } else {
                    state.sessions.contains_key(&id)
                }
            })
            .unwrap_or(false);
        if !exists {
            return Ok(if is_server {
                http2_wait_result("serverClose", id)
            } else {
                http2_wait_result("sessionClose", id)
            });
        }
    }
}

fn dispatch_http_wait_loop(process: &ActiveProcess, server_id: u64) -> Result<Value, SidecarError> {
    loop {
        if !process.http_servers.contains_key(&server_id) {
            return Ok(json!({
                "kind": "serverClose",
                "id": server_id,
            }));
        }
        thread::sleep(Duration::from_millis(25));
    }
}

fn http2_settings_from_value(settings: &BTreeMap<String, Value>) -> BTreeMap<String, Value> {
    settings.clone()
}

fn parse_http2_headers_json(
    headers_json: &str,
    label: &str,
) -> Result<BTreeMap<String, Value>, SidecarError> {
    serde_json::from_str::<BTreeMap<String, Value>>(headers_json)
        .map_err(|error| SidecarError::InvalidState(format!("{label} must be valid JSON: {error}")))
}

fn apply_http2_header_values(
    header_map: &mut HeaderMap,
    name: &str,
    value: &Value,
) -> Result<(), SidecarError> {
    let header_name = HeaderName::from_bytes(name.as_bytes()).map_err(|error| {
        SidecarError::InvalidState(format!("invalid HTTP/2 header name {name:?}: {error}"))
    })?;
    match value {
        Value::Array(values) => {
            for value in values {
                apply_http2_header_values(header_map, name, value)?;
            }
        }
        Value::String(text) => {
            let value = HeaderValue::from_str(text).map_err(|error| {
                SidecarError::InvalidState(format!(
                    "invalid HTTP/2 header value for {name}: {error}"
                ))
            })?;
            header_map.append(header_name.clone(), value);
        }
        Value::Number(number) => {
            let value = HeaderValue::from_str(&number.to_string()).map_err(|error| {
                SidecarError::InvalidState(format!(
                    "invalid HTTP/2 numeric header value for {name}: {error}"
                ))
            })?;
            header_map.append(header_name.clone(), value);
        }
        Value::Bool(boolean) => {
            let value = HeaderValue::from_str(if *boolean { "true" } else { "false" }).map_err(
                |error| {
                    SidecarError::InvalidState(format!(
                        "invalid HTTP/2 boolean header value for {name}: {error}"
                    ))
                },
            )?;
            header_map.append(header_name.clone(), value);
        }
        Value::Null => {}
        Value::Object(_) => {
            return Err(SidecarError::InvalidState(format!(
                "unsupported HTTP/2 header object value for {name}"
            )));
        }
    }
    Ok(())
}

fn build_http2_request(headers_json: &str) -> Result<Request<()>, SidecarError> {
    let headers = parse_http2_headers_json(headers_json, "HTTP/2 request headers")?;
    let method = headers
        .get(":method")
        .and_then(Value::as_str)
        .unwrap_or("GET");
    let path = headers.get(":path").and_then(Value::as_str).unwrap_or("/");
    let mut builder = Request::builder()
        .method(Method::from_bytes(method.as_bytes()).map_err(|error| {
            SidecarError::InvalidState(format!("invalid HTTP/2 method {method:?}: {error}"))
        })?)
        .uri(path.parse::<Uri>().map_err(|error| {
            SidecarError::InvalidState(format!("invalid HTTP/2 path {path:?}: {error}"))
        })?);
    {
        let header_map = builder.headers_mut().expect("request header map");
        for (name, value) in &headers {
            if name.starts_with(':') {
                continue;
            }
            apply_http2_header_values(header_map, name, value)?;
        }
    }
    builder
        .body(())
        .map_err(|error| SidecarError::InvalidState(format!("invalid HTTP/2 request: {error}")))
}

fn build_http2_response(headers_json: &str) -> Result<Response<()>, SidecarError> {
    let headers = parse_http2_headers_json(headers_json, "HTTP/2 response headers")?;
    let status = headers
        .get(":status")
        .and_then(Value::as_u64)
        .or_else(|| {
            headers
                .get(":status")
                .and_then(Value::as_str)
                .and_then(|value| value.parse::<u16>().ok().map(u64::from))
        })
        .unwrap_or(200);
    let mut builder = Response::builder().status(status as u16);
    {
        let header_map = builder.headers_mut().expect("response header map");
        for (name, value) in &headers {
            if name.starts_with(':') {
                continue;
            }
            apply_http2_header_values(header_map, name, value)?;
        }
    }
    builder.body(()).map_err(|error| {
        SidecarError::InvalidState(format!("invalid HTTP/2 response headers: {error}"))
    })
}

fn serialize_http2_headers_map(
    pseudo: BTreeMap<String, Value>,
    headers: &HeaderMap,
) -> Result<String, SidecarError> {
    let mut serialized = pseudo;
    for (name, value) in headers {
        let name = name.as_str().to_string();
        let value = Value::String(
            value
                .to_str()
                .map_err(|error| {
                    SidecarError::Execution(format!("invalid HTTP/2 header value: {error}"))
                })?
                .to_owned(),
        );
        match serialized.get_mut(&name) {
            Some(Value::Array(values)) => values.push(value),
            Some(existing) => {
                let first = existing.clone();
                *existing = Value::Array(vec![first, value]);
            }
            None => {
                serialized.insert(name, value);
            }
        }
    }
    serde_json::to_string(&serialized)
        .map_err(|error| SidecarError::Execution(format!("ERR_AGENTOS_NODE_SYNC_RPC: {error}")))
}

fn serialize_http2_request_headers(
    request: &Request<h2::RecvStream>,
) -> Result<String, SidecarError> {
    let mut pseudo = BTreeMap::new();
    pseudo.insert(
        String::from(":method"),
        Value::String(request.method().as_str().to_string()),
    );
    pseudo.insert(
        String::from(":path"),
        Value::String(
            request
                .uri()
                .path_and_query()
                .map(|value| value.as_str().to_string())
                .unwrap_or_else(|| String::from("/")),
        ),
    );
    serialize_http2_headers_map(pseudo, request.headers())
}

fn serialize_http2_response_headers(
    response: &Response<h2::RecvStream>,
) -> Result<String, SidecarError> {
    let mut pseudo = BTreeMap::new();
    pseudo.insert(
        String::from(":status"),
        Value::Number(serde_json::Number::from(response.status().as_u16())),
    );
    serialize_http2_headers_map(pseudo, response.headers())
}

fn remove_http2_session_resources(
    shared: &Arc<Mutex<crate::state::Http2SharedState>>,
    session_id: u64,
) {
    if let Ok(mut state) = shared.lock() {
        state.sessions.remove(&session_id);
        state.session_events.remove(&session_id);
        let stream_ids = state
            .streams
            .iter()
            .filter_map(|(stream_id, stream)| {
                (stream.session_id == session_id).then_some(*stream_id)
            })
            .collect::<Vec<_>>();
        for stream_id in stream_ids {
            state.streams.remove(&stream_id);
        }
    }
}

fn spawn_http2_client_session(
    shared: Arc<Mutex<crate::state::Http2SharedState>>,
    session_id: u64,
    remote_addr: SocketAddr,
    tls: Option<JavascriptTlsBridgeOptions>,
    snapshot: Arc<Mutex<Http2SessionSnapshot>>,
    mut command_rx: UnboundedReceiver<Http2SessionCommand>,
) {
    thread::spawn(move || {
        let runtime = match TokioRuntimeBuilder::new_current_thread()
            .enable_all()
            .build()
        {
            Ok(runtime) => runtime,
            Err(error) => {
                push_http2_session_event(
                    &shared,
                    session_id,
                    Http2BridgeEvent {
                        kind: String::from("sessionError"),
                        id: session_id,
                        data: Some(http2_error_payload(error.to_string())),
                        ..Http2BridgeEvent::default()
                    },
                );
                remove_http2_session_resources(&shared, session_id);
                return;
            }
        };

        runtime.block_on(async move {
            let stream = match tokio::net::TcpStream::connect(remote_addr).await {
                Ok(stream) => stream,
                Err(error) => {
                    push_http2_session_event(
                        &shared,
                        session_id,
                        Http2BridgeEvent {
                            kind: String::from("sessionError"),
                            id: session_id,
                            data: Some(http2_error_payload(error.to_string())),
                            ..Http2BridgeEvent::default()
                        },
                    );
                    remove_http2_session_resources(&shared, session_id);
                    return;
                }
            };

            let local_addr = match stream.local_addr() {
                Ok(addr) => addr,
                Err(error) => {
                    push_http2_session_event(
                        &shared,
                        session_id,
                        Http2BridgeEvent {
                            kind: String::from("sessionError"),
                            id: session_id,
                            data: Some(http2_error_payload(error.to_string())),
                            ..Http2BridgeEvent::default()
                        },
                    );
                    remove_http2_session_resources(&shared, session_id);
                    return;
                }
            };

            {
                let mut snapshot_guard = snapshot.lock().expect("http2 snapshot lock");
                snapshot_guard.socket = http2_socket_snapshot(local_addr, remote_addr);
                if let Some(options) = tls.as_ref() {
                    snapshot_guard.encrypted = true;
                    snapshot_guard.alpn_protocol = Some(String::from("h2"));
                    snapshot_guard.socket.encrypted = true;
                    snapshot_guard.socket.servername = options.servername.clone();
                    snapshot_guard.socket.alpn_protocol = Some(String::from("h2"));
                }
                snapshot_guard.state = http2_runtime_snapshot();
            }
            if let Ok(snapshot_json) =
                http2_snapshot_json(&snapshot.lock().expect("http2 snapshot lock").clone())
            {
                push_http2_session_event(
                    &shared,
                    session_id,
                    Http2BridgeEvent {
                        kind: String::from("sessionConnect"),
                        id: session_id,
                        data: Some(snapshot_json),
                        ..Http2BridgeEvent::default()
                    },
                );
            }

            let io: Pin<Box<dyn Http2AsyncIo>> = if let Some(options) = tls.as_ref() {
                let server_name = match ServerName::try_from(
                    options
                        .servername
                        .clone()
                        .unwrap_or_else(|| String::from("localhost")),
                ) {
                    Ok(server_name) => server_name,
                    Err(_) => {
                        push_http2_session_event(
                            &shared,
                            session_id,
                            Http2BridgeEvent {
                                kind: String::from("sessionError"),
                                id: session_id,
                                data: Some(http2_error_payload("invalid TLS servername")),
                                ..Http2BridgeEvent::default()
                            },
                        );
                        remove_http2_session_resources(&shared, session_id);
                        return;
                    }
                };
                let connector = match build_client_tls_config(options) {
                    Ok(config) => TlsConnector::from(Arc::new(config)),
                    Err(error) => {
                        push_http2_session_event(
                            &shared,
                            session_id,
                            Http2BridgeEvent {
                                kind: String::from("sessionError"),
                                id: session_id,
                                data: Some(http2_error_payload(error.to_string())),
                                ..Http2BridgeEvent::default()
                            },
                        );
                        remove_http2_session_resources(&shared, session_id);
                        return;
                    }
                };
                match connector.connect(server_name, stream).await {
                    Ok(tls_stream) => Box::pin(tls_stream),
                    Err(error) => {
                        push_http2_session_event(
                            &shared,
                            session_id,
                            Http2BridgeEvent {
                                kind: String::from("sessionError"),
                                id: session_id,
                                data: Some(http2_error_payload(error.to_string())),
                                ..Http2BridgeEvent::default()
                            },
                        );
                        remove_http2_session_resources(&shared, session_id);
                        return;
                    }
                }
            } else {
                Box::pin(stream)
            };

            let (mut sender, connection) = match client::handshake(io).await {
                Ok(parts) => parts,
                Err(error) => {
                    push_http2_session_event(
                        &shared,
                        session_id,
                        Http2BridgeEvent {
                            kind: String::from("sessionError"),
                            id: session_id,
                            data: Some(http2_error_payload(error.to_string())),
                            ..Http2BridgeEvent::default()
                        },
                    );
                    remove_http2_session_resources(&shared, session_id);
                    return;
                }
            };

            let (status_tx, mut status_rx) = unbounded_channel::<Result<(), String>>();
            tokio::spawn(async move {
                let _ = status_tx.send(connection.await.map_err(|error| error.to_string()));
            });

            let streams: Arc<Mutex<BTreeMap<u64, ClientHttp2StreamState>>> =
                Arc::new(Mutex::new(BTreeMap::new()));

            loop {
                tokio::select! {
                    Some(result) = status_rx.recv() => {
                        if let Err(message) = result {
                            push_http2_session_event(
                                &shared,
                                session_id,
                                Http2BridgeEvent {
                                    kind: String::from("sessionError"),
                                    id: session_id,
                                    data: Some(http2_error_payload(message)),
                                    ..Http2BridgeEvent::default()
                                },
                            );
                        }
                        push_http2_session_event(
                            &shared,
                            session_id,
                            Http2BridgeEvent {
                                kind: String::from("sessionClose"),
                                id: session_id,
                                ..Http2BridgeEvent::default()
                            },
                        );
                        remove_http2_session_resources(&shared, session_id);
                        break;
                    }
                    Some(command) = command_rx.recv() => {
                        match command {
                            Http2SessionCommand::Request { headers_json, options_json, respond_to } => {
                                let request = match build_http2_request(&headers_json) {
                                    Ok(request) => request,
                                    Err(error) => {
                                        let _ = respond_to.send(Err(error.to_string()));
                                        continue;
                                    }
                                };
                                let options: JavascriptHttp2RequestOptions =
                                    serde_json::from_str(&options_json).unwrap_or_default();
                                let stream_id = {
                                    let mut state = shared.lock().expect("http2 shared state");
                                    let stream_id = next_http2_stream_id(&mut state);
                                    state.streams.insert(
                                        stream_id,
                                        ActiveHttp2Stream {
                                            session_id,
                                            paused: Arc::new(AtomicBool::new(false)),
                                        },
                                    );
                                    stream_id
                                };
                                match sender.send_request(request, options.end_stream) {
                                    Ok((response_future, send_stream)) => {
                                        if !options.end_stream {
                                            streams
                                                .lock()
                                                .expect("http2 client streams")
                                                .insert(stream_id, ClientHttp2StreamState { send_stream: Some(send_stream) });
                                        }
                                        let shared_clone = Arc::clone(&shared);
                                        let snapshot_clone = Arc::clone(&snapshot);
                                        tokio::spawn(async move {
                                            match response_future.await {
                                                Ok(response) => {
                                                    if let Ok(headers_json) = serialize_http2_response_headers(&response) {
                                                        push_http2_session_event(
                                                            &shared_clone,
                                                            session_id,
                                                            Http2BridgeEvent {
                                                                kind: String::from("clientResponseHeaders"),
                                                                id: stream_id,
                                                                data: Some(headers_json),
                                                                ..Http2BridgeEvent::default()
                                                            },
                                                        );
                                                    }
                                                    let mut body = response.into_body();
                                                    while let Some(chunk) = body.data().await {
                                                        match chunk {
                                                            Ok(bytes) => {
                                                                let paused = {
                                                                    let state = shared_clone.lock().expect("http2 shared state");
                                                                    state.streams.get(&stream_id).map(|stream| Arc::clone(&stream.paused))
                                                                };
                                                                if let Some(paused) = paused {
                                                                    while paused.load(Ordering::SeqCst) {
                                                                        tokio::time::sleep(HTTP2_POLL_DELAY).await;
                                                                    }
                                                                }
                                                                let _ = body.flow_control().release_capacity(bytes.len());
                                                                push_http2_session_event(
                                                                    &shared_clone,
                                                                    session_id,
                                                                    Http2BridgeEvent {
                                                                        kind: String::from("clientData"),
                                                                        id: stream_id,
                                                                        data: Some(base64::engine::general_purpose::STANDARD.encode(bytes)),
                                                                        ..Http2BridgeEvent::default()
                                                                    },
                                                                );
                                                            }
                                                            Err(error) => {
                                                                push_http2_session_event(
                                                                    &shared_clone,
                                                                    session_id,
                                                                    Http2BridgeEvent {
                                                                        kind: String::from("clientError"),
                                                                        id: stream_id,
                                                                        data: Some(http2_error_payload(error.to_string())),
                                                                        ..Http2BridgeEvent::default()
                                                                    },
                                                                );
                                                                break;
                                                            }
                                                        }
                                                    }
                                                    {
                                                        let mut snapshot = snapshot_clone.lock().expect("http2 snapshot lock");
                                                        snapshot.state.next_stream_id =
                                                            snapshot.state.next_stream_id.saturating_add(2);
                                                    }
                                                    push_http2_session_event(
                                                        &shared_clone,
                                                        session_id,
                                                        Http2BridgeEvent {
                                                            kind: String::from("clientEnd"),
                                                            id: stream_id,
                                                            ..Http2BridgeEvent::default()
                                                        },
                                                    );
                                                    push_http2_session_event(
                                                        &shared_clone,
                                                        session_id,
                                                        Http2BridgeEvent {
                                                            kind: String::from("clientClose"),
                                                            id: stream_id,
                                                            extra_number: Some(0),
                                                            ..Http2BridgeEvent::default()
                                                        },
                                                    );
                                                    if let Ok(mut state) = shared_clone.lock() {
                                                        state.streams.remove(&stream_id);
                                                    }
                                                }
                                                Err(error) => {
                                                    push_http2_session_event(
                                                        &shared_clone,
                                                        session_id,
                                                        Http2BridgeEvent {
                                                            kind: String::from("clientError"),
                                                            id: stream_id,
                                                            data: Some(http2_error_payload(error.to_string())),
                                                            ..Http2BridgeEvent::default()
                                                        },
                                                    );
                                                    push_http2_session_event(
                                                        &shared_clone,
                                                        session_id,
                                                        Http2BridgeEvent {
                                                            kind: String::from("clientClose"),
                                                            id: stream_id,
                                                            extra_number: Some(u32::from(Reason::INTERNAL_ERROR) as u64),
                                                            ..Http2BridgeEvent::default()
                                                        },
                                                    );
                                                    if let Ok(mut state) = shared_clone.lock() {
                                                        state.streams.remove(&stream_id);
                                                    }
                                                }
                                            }
                                        });
                                        let _ = respond_to.send(Ok(json!(stream_id)));
                                    }
                                    Err(error) => {
                                        if let Ok(mut state) = shared.lock() {
                                            state.streams.remove(&stream_id);
                                        }
                                        let _ = respond_to.send(Err(error.to_string()));
                                    }
                                }
                            }
                            Http2SessionCommand::Settings { settings_json, respond_to } => {
                                let settings = serde_json::from_str::<BTreeMap<String, Value>>(&settings_json)
                                    .unwrap_or_default();
                                {
                                    let mut snapshot = snapshot.lock().expect("http2 snapshot lock");
                                    snapshot.local_settings = http2_settings_from_value(&settings);
                                }
                                if let Ok(headers_json) = serde_json::to_string(&settings) {
                                    push_http2_session_event(
                                        &shared,
                                        session_id,
                                        Http2BridgeEvent {
                                            kind: String::from("sessionLocalSettings"),
                                            id: session_id,
                                            data: Some(headers_json.clone()),
                                            ..Http2BridgeEvent::default()
                                        },
                                    );
                                    push_http2_session_event(
                                        &shared,
                                        session_id,
                                        Http2BridgeEvent {
                                            kind: String::from("sessionSettingsAck"),
                                            id: session_id,
                                            ..Http2BridgeEvent::default()
                                        },
                                    );
                                }
                                let _ = respond_to.send(Ok(Value::Null));
                            }
                            Http2SessionCommand::SetLocalWindowSize { size, respond_to } => {
                                {
                                    let mut snapshot = snapshot.lock().expect("http2 snapshot lock");
                                    snapshot.state.local_window_size = size;
                                    snapshot.state.effective_local_window_size = size;
                                }
                                let value = snapshot
                                    .lock()
                                    .ok()
                                    .and_then(|snapshot| http2_snapshot_json(&snapshot.clone()).ok())
                                    .map(Value::String)
                                    .unwrap_or(Value::Null);
                                let _ = respond_to.send(Ok(value));
                            }
                            Http2SessionCommand::Goaway { error_code, last_stream_id, opaque_data, respond_to } => {
                                push_http2_session_event(
                                    &shared,
                                    session_id,
                                    Http2BridgeEvent {
                                        kind: String::from("sessionGoaway"),
                                        id: session_id,
                                        data: opaque_data.map(|value| {
                                            base64::engine::general_purpose::STANDARD.encode(value)
                                        }),
                                        extra_number: Some(error_code as u64),
                                        flags: Some(last_stream_id as u64),
                                        ..Http2BridgeEvent::default()
                                    },
                                );
                                let _ = respond_to.send(Ok(Value::Null));
                            }
                            Http2SessionCommand::Close { respond_to, .. } => {
                                let _ = respond_to.send(Ok(Value::Null));
                                push_http2_session_event(
                                    &shared,
                                    session_id,
                                    Http2BridgeEvent {
                                        kind: String::from("sessionClose"),
                                        id: session_id,
                                        ..Http2BridgeEvent::default()
                                    },
                                );
                                remove_http2_session_resources(&shared, session_id);
                                break;
                            }
                            Http2SessionCommand::StreamWrite { stream_id, chunk, end_stream, respond_to } => {
                                let result = streams
                                    .lock()
                                    .expect("http2 client streams")
                                    .get_mut(&stream_id)
                                    .and_then(|stream| stream.send_stream.as_mut())
                                    .ok_or_else(|| SidecarError::InvalidState(format!("unknown HTTP/2 client stream {stream_id}")))
                                    .and_then(|stream| stream.send_data(Bytes::from(chunk), end_stream).map_err(|error| SidecarError::Execution(error.to_string())));
                                match result {
                                    Ok(()) => {
                                        if end_stream {
                                            streams.lock().expect("http2 client streams").remove(&stream_id);
                                        }
                                        let _ = respond_to.send(Ok(Value::Bool(true)));
                                    }
                                    Err(error) => {
                                        let _ = respond_to.send(Err(error.to_string()));
                                    }
                                }
                            }
                            Http2SessionCommand::StreamClose { stream_id, error_code, respond_to } => {
                                let mut streams = streams.lock().expect("http2 client streams");
                                let Some(mut state) = streams.remove(&stream_id) else {
                                    let _ = respond_to.send(Err(format!("unknown HTTP/2 client stream {stream_id}")));
                                    continue;
                                };
                                if let Some(stream) = state.send_stream.as_mut() {
                                    stream.send_reset(http2_reason(error_code));
                                }
                                if let Ok(mut state) = shared.lock() {
                                    state.streams.remove(&stream_id);
                                }
                                push_http2_session_event(
                                    &shared,
                                    session_id,
                                    Http2BridgeEvent {
                                        kind: String::from("clientClose"),
                                        id: stream_id,
                                        extra_number: Some(u32::from(http2_reason(error_code)) as u64),
                                        ..Http2BridgeEvent::default()
                                    },
                                );
                                let _ = respond_to.send(Ok(Value::Null));
                            }
                            Http2SessionCommand::StreamRespond { respond_to, .. }
                            | Http2SessionCommand::StreamPush { respond_to, .. }
                            | Http2SessionCommand::StreamRespondWithFile { respond_to, .. } => {
                                let _ = respond_to.send(Err(String::from("HTTP/2 client streams cannot send server responses")));
                            }
                        }
                    }
                    else => break,
                }
            }
        });
    });
}

fn spawn_http2_server_session(
    shared: Arc<Mutex<crate::state::Http2SharedState>>,
    server_id: u64,
    session_id: u64,
    stream: TcpStream,
    tls: Option<JavascriptTlsBridgeOptions>,
    snapshot: Arc<Mutex<Http2SessionSnapshot>>,
    mut command_rx: UnboundedReceiver<Http2SessionCommand>,
) {
    thread::spawn(move || {
        let runtime = match TokioRuntimeBuilder::new_current_thread()
            .enable_all()
            .build()
        {
            Ok(runtime) => runtime,
            Err(error) => {
                push_http2_server_event(
                    &shared,
                    server_id,
                    Http2BridgeEvent {
                        kind: String::from("serverStreamError"),
                        id: session_id,
                        data: Some(http2_error_payload(error.to_string())),
                        ..Http2BridgeEvent::default()
                    },
                );
                remove_http2_session_resources(&shared, session_id);
                return;
            }
        };

        runtime.block_on(async move {
            if let Err(error) = stream.set_nonblocking(true) {
                push_http2_server_event(
                    &shared,
                    server_id,
                    Http2BridgeEvent {
                        kind: String::from("serverStreamError"),
                        id: session_id,
                        data: Some(http2_error_payload(error.to_string())),
                        ..Http2BridgeEvent::default()
                    },
                );
                remove_http2_session_resources(&shared, session_id);
                return;
            }
            let stream = match tokio::net::TcpStream::from_std(stream) {
                Ok(stream) => stream,
                Err(error) => {
                    push_http2_server_event(
                        &shared,
                        server_id,
                        Http2BridgeEvent {
                            kind: String::from("serverStreamError"),
                            id: session_id,
                            data: Some(http2_error_payload(error.to_string())),
                            ..Http2BridgeEvent::default()
                        },
                    );
                    remove_http2_session_resources(&shared, session_id);
                    return;
                }
            };
            let local_addr = match stream.local_addr() {
                Ok(addr) => addr,
                Err(error) => {
                    push_http2_server_event(
                        &shared,
                        server_id,
                        Http2BridgeEvent {
                            kind: String::from("serverStreamError"),
                            id: session_id,
                            data: Some(http2_error_payload(error.to_string())),
                            ..Http2BridgeEvent::default()
                        },
                    );
                    remove_http2_session_resources(&shared, session_id);
                    return;
                }
            };
            let remote_addr = match stream.peer_addr() {
                Ok(addr) => addr,
                Err(error) => {
                    push_http2_server_event(
                        &shared,
                        server_id,
                        Http2BridgeEvent {
                            kind: String::from("serverStreamError"),
                            id: session_id,
                            data: Some(http2_error_payload(error.to_string())),
                            ..Http2BridgeEvent::default()
                        },
                    );
                    remove_http2_session_resources(&shared, session_id);
                    return;
                }
            };
            {
                let mut snapshot_guard = snapshot.lock().expect("http2 snapshot lock");
                snapshot_guard.socket = http2_socket_snapshot(local_addr, remote_addr);
                if tls.is_some() {
                    snapshot_guard.encrypted = true;
                    snapshot_guard.alpn_protocol = Some(String::from("h2"));
                    snapshot_guard.socket.encrypted = true;
                    snapshot_guard.socket.alpn_protocol = Some(String::from("h2"));
                }
                snapshot_guard.state = http2_runtime_snapshot();
            }
            if let Ok(snapshot_json) =
                http2_snapshot_json(&snapshot.lock().expect("http2 snapshot lock").clone())
            {
                push_http2_server_event(
                    &shared,
                    server_id,
                    Http2BridgeEvent {
                        kind: String::from(if tls.is_some() {
                            "serverSecureConnection"
                        } else {
                            "serverConnection"
                        }),
                        id: server_id,
                        data: Some(serde_json::to_string(&http2_socket_snapshot(local_addr, remote_addr)).unwrap_or_default()),
                        ..Http2BridgeEvent::default()
                    },
                );
                push_http2_server_event(
                    &shared,
                    server_id,
                    Http2BridgeEvent {
                        kind: String::from("serverSession"),
                        id: server_id,
                        data: Some(snapshot_json),
                        extra_number: Some(session_id),
                        ..Http2BridgeEvent::default()
                    },
                );
            }

            let io: Pin<Box<dyn Http2AsyncIo>> = if let Some(options) = tls.as_ref() {
                let acceptor = match build_server_tls_config(options) {
                    Ok(config) => TlsAcceptor::from(Arc::new(config)),
                    Err(error) => {
                        push_http2_server_event(
                            &shared,
                            server_id,
                            Http2BridgeEvent {
                                kind: String::from("serverStreamError"),
                                id: session_id,
                                data: Some(http2_error_payload(error.to_string())),
                                ..Http2BridgeEvent::default()
                            },
                        );
                        remove_http2_session_resources(&shared, session_id);
                        return;
                    }
                };
                match acceptor.accept(stream).await {
                    Ok(tls_stream) => Box::pin(tls_stream),
                    Err(error) => {
                        push_http2_server_event(
                            &shared,
                            server_id,
                            Http2BridgeEvent {
                                kind: String::from("serverStreamError"),
                                id: session_id,
                                data: Some(http2_error_payload(error.to_string())),
                                ..Http2BridgeEvent::default()
                            },
                        );
                        remove_http2_session_resources(&shared, session_id);
                        return;
                    }
                }
            } else {
                Box::pin(stream)
            };

            let mut connection = match server::handshake(io).await {
                Ok(connection) => connection,
                Err(error) => {
                    push_http2_server_event(
                        &shared,
                        server_id,
                        Http2BridgeEvent {
                            kind: String::from("serverStreamError"),
                            id: session_id,
                            data: Some(http2_error_payload(error.to_string())),
                            ..Http2BridgeEvent::default()
                        },
                    );
                    remove_http2_session_resources(&shared, session_id);
                    return;
                }
            };

            let streams: Arc<Mutex<BTreeMap<u64, ServerHttp2StreamState>>> =
                Arc::new(Mutex::new(BTreeMap::new()));

            loop {
                tokio::select! {
                    incoming = connection.accept() => {
                        match incoming {
                            Some(Ok((request, respond))) => {
                                let headers_json = match serialize_http2_request_headers(&request) {
                                    Ok(headers) => headers,
                                    Err(error) => {
                                        push_http2_server_event(
                                            &shared,
                                            server_id,
                                            Http2BridgeEvent {
                                                kind: String::from("serverStreamError"),
                                                id: server_id,
                                                data: Some(http2_error_payload(error.to_string())),
                                                ..Http2BridgeEvent::default()
                                            },
                                        );
                                        continue;
                                    }
                                };
                                let stream_id = {
                                    let mut state = shared.lock().expect("http2 shared state");
                                    let stream_id = next_http2_stream_id(&mut state);
                                    state.streams.insert(
                                        stream_id,
                                        ActiveHttp2Stream {
                                            session_id,
                                            paused: Arc::new(AtomicBool::new(false)),
                                        },
                                    );
                                    stream_id
                                };
                                streams.lock().expect("http2 server streams").insert(
                                    stream_id,
                                    ServerHttp2StreamState {
                                        send_response: Some(ServerHttp2Responder::Regular(respond)),
                                        send_stream: None,
                                    },
                                );
                                let snapshot_json = snapshot
                                    .lock()
                                    .ok()
                                    .and_then(|snapshot| http2_snapshot_json(&snapshot.clone()).ok());
                                push_http2_server_event(
                                    &shared,
                                    server_id,
                                    Http2BridgeEvent {
                                        kind: String::from("serverStream"),
                                        id: server_id,
                                        data: Some(stream_id.to_string()),
                                        extra: snapshot_json,
                                        extra_number: Some(session_id),
                                        extra_headers: Some(headers_json),
                                        flags: Some(0),
                                    },
                                );
                                let shared_clone = Arc::clone(&shared);
                                tokio::spawn(async move {
                                    let mut body = request.into_body();
                                    while let Some(chunk) = body.data().await {
                                        match chunk {
                                            Ok(bytes) => {
                                                let paused = {
                                                    let state = shared_clone.lock().expect("http2 shared state");
                                                    state.streams.get(&stream_id).map(|stream| Arc::clone(&stream.paused))
                                                };
                                                if let Some(paused) = paused {
                                                    while paused.load(Ordering::SeqCst) {
                                                        tokio::time::sleep(HTTP2_POLL_DELAY).await;
                                                    }
                                                }
                                                let _ = body.flow_control().release_capacity(bytes.len());
                                                push_http2_server_event(
                                                    &shared_clone,
                                                    server_id,
                                                    Http2BridgeEvent {
                                                        kind: String::from("serverStreamData"),
                                                        id: stream_id,
                                                        data: Some(base64::engine::general_purpose::STANDARD.encode(bytes)),
                                                        ..Http2BridgeEvent::default()
                                                    },
                                                );
                                            }
                                            Err(error) => {
                                                push_http2_server_event(
                                                    &shared_clone,
                                                    server_id,
                                                    Http2BridgeEvent {
                                                        kind: String::from("serverStreamError"),
                                                        id: stream_id,
                                                        data: Some(http2_error_payload(error.to_string())),
                                                        ..Http2BridgeEvent::default()
                                                    },
                                                );
                                                break;
                                            }
                                        }
                                    }
                                    push_http2_server_event(
                                        &shared_clone,
                                        server_id,
                                        Http2BridgeEvent {
                                            kind: String::from("serverStreamEnd"),
                                            id: stream_id,
                                            ..Http2BridgeEvent::default()
                                        },
                                    );
                                });
                            }
                            Some(Err(error)) => {
                                push_http2_server_event(
                                    &shared,
                                    server_id,
                                    Http2BridgeEvent {
                                        kind: String::from("serverStreamError"),
                                        id: server_id,
                                        data: Some(http2_error_payload(error.to_string())),
                                        ..Http2BridgeEvent::default()
                                    },
                                );
                                break;
                            }
                            None => {
                                push_http2_server_event(
                                    &shared,
                                    server_id,
                                    Http2BridgeEvent {
                                        kind: String::from("sessionClose"),
                                        id: session_id,
                                        ..Http2BridgeEvent::default()
                                    },
                                );
                                remove_http2_session_resources(&shared, session_id);
                                break;
                            }
                        }
                    }
                    Some(command) = command_rx.recv() => {
                        match command {
                            Http2SessionCommand::Settings { settings_json, respond_to } => {
                                let settings = serde_json::from_str::<BTreeMap<String, Value>>(&settings_json)
                                    .unwrap_or_default();
                                if let Some(initial_window_size) = settings
                                    .get("initialWindowSize")
                                    .and_then(Value::as_u64)
                                {
                                    let _ = connection.set_initial_window_size(initial_window_size as u32);
                                }
                                {
                                    let mut snapshot = snapshot.lock().expect("http2 snapshot lock");
                                    snapshot.local_settings = http2_settings_from_value(&settings);
                                }
                                if let Ok(headers_json) = serde_json::to_string(&settings) {
                                    push_http2_session_event(
                                        &shared,
                                        session_id,
                                        Http2BridgeEvent {
                                            kind: String::from("sessionLocalSettings"),
                                            id: session_id,
                                            data: Some(headers_json),
                                            ..Http2BridgeEvent::default()
                                        },
                                    );
                                }
                                let _ = respond_to.send(Ok(Value::Null));
                            }
                            Http2SessionCommand::SetLocalWindowSize { size, respond_to } => {
                                connection.set_target_window_size(size);
                                {
                                    let mut snapshot = snapshot.lock().expect("http2 snapshot lock");
                                    snapshot.state.local_window_size = size;
                                    snapshot.state.effective_local_window_size = size;
                                }
                                let value = snapshot
                                    .lock()
                                    .ok()
                                    .and_then(|snapshot| http2_snapshot_json(&snapshot.clone()).ok())
                                    .map(Value::String)
                                    .unwrap_or(Value::Null);
                                let _ = respond_to.send(Ok(value));
                            }
                            Http2SessionCommand::Goaway { error_code, last_stream_id, opaque_data, respond_to } => {
                                connection.abrupt_shutdown(http2_reason(Some(error_code)));
                                push_http2_session_event(
                                    &shared,
                                    session_id,
                                    Http2BridgeEvent {
                                        kind: String::from("sessionGoaway"),
                                        id: session_id,
                                        data: opaque_data.map(|value| {
                                            base64::engine::general_purpose::STANDARD.encode(value)
                                        }),
                                        extra_number: Some(error_code as u64),
                                        flags: Some(last_stream_id as u64),
                                        ..Http2BridgeEvent::default()
                                    },
                                );
                                let _ = respond_to.send(Ok(Value::Null));
                            }
                            Http2SessionCommand::Close { abrupt, respond_to } => {
                                if abrupt {
                                    connection.abrupt_shutdown(Reason::NO_ERROR);
                                } else {
                                    connection.graceful_shutdown();
                                }
                                let _ = respond_to.send(Ok(Value::Null));
                                push_http2_session_event(
                                    &shared,
                                    session_id,
                                    Http2BridgeEvent {
                                        kind: String::from("sessionClose"),
                                        id: session_id,
                                        ..Http2BridgeEvent::default()
                                    },
                                );
                                remove_http2_session_resources(&shared, session_id);
                                break;
                            }
                            Http2SessionCommand::StreamRespond { stream_id, headers_json, respond_to } => {
                                let response = match build_http2_response(&headers_json) {
                                    Ok(response) => response,
                                    Err(error) => {
                                        let _ = respond_to.send(Err(error.to_string()));
                                        continue;
                                    }
                                };
                                let mut streams = streams.lock().expect("http2 server streams");
                                let Some(state) = streams.get_mut(&stream_id) else {
                                    let _ = respond_to.send(Err(format!("unknown HTTP/2 server stream {stream_id}")));
                                    continue;
                                };
                                let Some(send_response) = state.send_response.as_mut() else {
                                    let _ = respond_to.send(Err(format!("HTTP/2 server stream {stream_id} already responded")));
                                    continue;
                                };
                                match match send_response {
                                    ServerHttp2Responder::Regular(send_response) => {
                                        send_response.send_response(response, false)
                                    }
                                    ServerHttp2Responder::Pushed(send_response) => {
                                        send_response.send_response(response, false)
                                    }
                                } {
                                    Ok(send_stream) => {
                                        state.send_stream = Some(send_stream);
                                        state.send_response = None;
                                        let _ = respond_to.send(Ok(Value::Null));
                                    }
                                    Err(error) => {
                                        let _ = respond_to.send(Err(error.to_string()));
                                    }
                                }
                            }
                            Http2SessionCommand::StreamPush { stream_id, headers_json, respond_to } => {
                                let request = match build_http2_request(&headers_json) {
                                    Ok(request) => request,
                                    Err(error) => {
                                        let _ = respond_to.send(Err(error.to_string()));
                                        continue;
                                    }
                                };
                                let mut streams_guard = streams.lock().expect("http2 server streams");
                                let Some(state) = streams_guard.get_mut(&stream_id) else {
                                    let _ = respond_to.send(Err(format!("unknown HTTP/2 server stream {stream_id}")));
                                    continue;
                                };
                                let Some(send_response) = state.send_response.as_mut() else {
                                    let _ = respond_to.send(Err(format!("HTTP/2 server stream {stream_id} cannot push after responding")));
                                    continue;
                                };
                                let ServerHttp2Responder::Regular(send_response) = send_response else {
                                    let _ = respond_to.send(Err(format!("HTTP/2 pushed stream {stream_id} cannot create nested push promises")));
                                    continue;
                                };
                                match send_response.push_request(request) {
                                    Ok(pushed) => {
                                        let pushed_stream_id = {
                                            let mut state = shared.lock().expect("http2 shared state");
                                            let pushed_stream_id = next_http2_stream_id(&mut state);
                                            state.streams.insert(
                                                pushed_stream_id,
                                                ActiveHttp2Stream {
                                                    session_id,
                                                    paused: Arc::new(AtomicBool::new(false)),
                                                },
                                            );
                                            pushed_stream_id
                                        };
                                        streams_guard.insert(
                                            pushed_stream_id,
                                            ServerHttp2StreamState {
                                                send_response: Some(ServerHttp2Responder::Pushed(pushed)),
                                                send_stream: None,
                                            },
                                        );
                                        let _ = respond_to.send(Ok(json!({
                                            "streamId": pushed_stream_id,
                                            "headers": headers_json,
                                        }).to_string().into()));
                                    }
                                    Err(error) => {
                                        let _ = respond_to.send(Err(error.to_string()));
                                    }
                                }
                            }
                            Http2SessionCommand::StreamWrite { stream_id, chunk, end_stream, respond_to } => {
                                let mut streams = streams.lock().expect("http2 server streams");
                                let Some(state) = streams.get_mut(&stream_id) else {
                                    let _ = respond_to.send(Err(format!("unknown HTTP/2 server stream {stream_id}")));
                                    continue;
                                };
                                let Some(send_stream) = state.send_stream.as_mut() else {
                                    let _ = respond_to.send(Err(format!("HTTP/2 server stream {stream_id} has not sent response headers")));
                                    continue;
                                };
                                match send_stream.send_data(Bytes::from(chunk), end_stream) {
                                    Ok(()) => {
                                        if end_stream {
                                            streams.remove(&stream_id);
                                            if let Ok(mut state) = shared.lock() {
                                                state.streams.remove(&stream_id);
                                            }
                                            push_http2_server_event(
                                                &shared,
                                                server_id,
                                                Http2BridgeEvent {
                                                    kind: String::from("serverStreamClose"),
                                                    id: stream_id,
                                                    extra_number: Some(0),
                                                    ..Http2BridgeEvent::default()
                                                },
                                            );
                                        }
                                        let _ = respond_to.send(Ok(Value::Bool(true)));
                                    }
                                    Err(error) => {
                                        let _ = respond_to.send(Err(error.to_string()));
                                    }
                                }
                            }
                            Http2SessionCommand::StreamClose { stream_id, error_code, respond_to } => {
                                let mut streams_guard = streams.lock().expect("http2 server streams");
                                let Some(mut state) = streams_guard.remove(&stream_id) else {
                                    let _ = respond_to.send(Err(format!("unknown HTTP/2 server stream {stream_id}")));
                                    continue;
                                };
                                let reason = http2_reason(error_code);
                                if let Some(send_stream) = state.send_stream.as_mut() {
                                    send_stream.send_reset(reason);
                                }
                                if let Some(send_response) = state.send_response.as_mut() {
                                    match send_response {
                                        ServerHttp2Responder::Regular(send_response) => {
                                            send_response.send_reset(reason)
                                        }
                                        ServerHttp2Responder::Pushed(send_response) => {
                                            send_response.send_reset(reason)
                                        }
                                    }
                                }
                                if let Ok(mut shared_guard) = shared.lock() {
                                    shared_guard.streams.remove(&stream_id);
                                }
                                push_http2_server_event(
                                    &shared,
                                    server_id,
                                    Http2BridgeEvent {
                                        kind: String::from("serverStreamClose"),
                                        id: stream_id,
                                        extra_number: Some(u32::from(reason) as u64),
                                        ..Http2BridgeEvent::default()
                                    },
                                );
                                let _ = respond_to.send(Ok(Value::Null));
                            }
                            Http2SessionCommand::StreamRespondWithFile { stream_id, body, headers_json, options_json, respond_to } => {
                                let options: JavascriptHttp2FileResponseOptions =
                                    serde_json::from_str(&options_json).unwrap_or_default();
                                let response = match build_http2_response(&headers_json) {
                                    Ok(response) => response,
                                    Err(error) => {
                                        let _ = respond_to.send(Err(error.to_string()));
                                        continue;
                                    }
                                };
                                let offset = usize::try_from(options.offset.unwrap_or_default()).unwrap_or(0);
                                let body = if offset >= body.len() {
                                    Vec::new()
                                } else {
                                    let body = &body[offset..];
                                    match options.length {
                                        Some(length) if length >= 0 => {
                                            body[..body.len().min(length as usize)].to_vec()
                                        }
                                        _ => body.to_vec(),
                                    }
                                };
                                let mut streams_guard = streams.lock().expect("http2 server streams");
                                let Some(state) = streams_guard.get_mut(&stream_id) else {
                                    let _ = respond_to.send(Err(format!("unknown HTTP/2 server stream {stream_id}")));
                                    continue;
                                };
                                let Some(send_response) = state.send_response.as_mut() else {
                                    let _ = respond_to.send(Err(format!("HTTP/2 server stream {stream_id} already responded")));
                                    continue;
                                };
                                match match send_response {
                                    ServerHttp2Responder::Regular(send_response) => {
                                        send_response.send_response(response, body.is_empty())
                                    }
                                    ServerHttp2Responder::Pushed(send_response) => {
                                        send_response.send_response(response, body.is_empty())
                                    }
                                } {
                                    Ok(mut send_stream) => {
                                        state.send_response = None;
                                        if body.is_empty() {
                                            streams_guard.remove(&stream_id);
                                            if let Ok(mut shared_guard) = shared.lock() {
                                                shared_guard.streams.remove(&stream_id);
                                            }
                                        } else {
                                            if let Err(error) = send_stream.send_data(Bytes::from(body), true) {
                                                let _ = respond_to.send(Err(error.to_string()));
                                                continue;
                                            }
                                            streams_guard.remove(&stream_id);
                                            if let Ok(mut shared_guard) = shared.lock() {
                                                shared_guard.streams.remove(&stream_id);
                                            }
                                        }
                                        push_http2_server_event(
                                            &shared,
                                            server_id,
                                            Http2BridgeEvent {
                                                kind: String::from("serverStreamClose"),
                                                id: stream_id,
                                                extra_number: Some(0),
                                                ..Http2BridgeEvent::default()
                                            },
                                        );
                                        let _ = respond_to.send(Ok(Value::Null));
                                    }
                                    Err(error) => {
                                        let _ = respond_to.send(Err(error.to_string()));
                                    }
                                }
                            }
                            Http2SessionCommand::Request { respond_to, .. } => {
                                let _ = respond_to.send(Err(String::from("HTTP/2 server sessions cannot initiate client requests")));
                            }
                        }
                    }
                    else => break,
                }
            }
        });
    });
}

fn spawn_http2_server_accept_loop(
    shared: Arc<Mutex<crate::state::Http2SharedState>>,
    server_id: u64,
    listener: TcpListener,
) {
    thread::spawn(move || {
        let listener = listener;
        loop {
            let closed = shared
                .lock()
                .ok()
                .and_then(|state| {
                    state
                        .servers
                        .get(&server_id)
                        .map(|server| server.closed.load(Ordering::SeqCst))
                })
                .unwrap_or(true);
            if closed {
                break;
            }
            match listener.accept() {
                Ok((stream, _)) => {
                    let (command_tx, command_rx) = unbounded_channel();
                    let (guest_local_addr, secure, tls) = {
                        let state = shared.lock().expect("http2 shared state");
                        let server = state.servers.get(&server_id).expect("http2 server state");
                        (server.guest_local_addr, server.secure, server.tls.clone())
                    };
                    let (local_addr, remote_addr) = match (stream.local_addr(), stream.peer_addr())
                    {
                        (Ok(local_addr), Ok(remote_addr)) => (local_addr, remote_addr),
                        _ => continue,
                    };
                    let session_snapshot = Arc::new(Mutex::new(Http2SessionSnapshot {
                        encrypted: secure,
                        alpn_protocol: Some(if secure {
                            String::from("h2")
                        } else {
                            String::from("h2c")
                        }),
                        local_settings: BTreeMap::new(),
                        remote_settings: BTreeMap::new(),
                        state: http2_runtime_snapshot(),
                        socket: Http2SocketSnapshot {
                            local_address: Some(guest_local_addr.ip().to_string()),
                            local_port: Some(guest_local_addr.port()),
                            local_family: Some(socket_addr_family(&guest_local_addr).to_string()),
                            remote_address: Some(remote_addr.ip().to_string()),
                            remote_port: Some(remote_addr.port()),
                            remote_family: Some(socket_addr_family(&remote_addr).to_string()),
                            ..http2_socket_snapshot(local_addr, remote_addr)
                        },
                        ..Http2SessionSnapshot::default()
                    }));
                    let session_id = {
                        let mut state = shared.lock().expect("http2 shared state");
                        let session_id = next_http2_session_id(&mut state);
                        state
                            .sessions
                            .insert(session_id, ActiveHttp2Session { command_tx });
                        session_id
                    };
                    spawn_http2_server_session(
                        Arc::clone(&shared),
                        server_id,
                        session_id,
                        stream,
                        tls,
                        session_snapshot,
                        command_rx,
                    );
                }
                Err(error) if error.kind() == std::io::ErrorKind::WouldBlock => {
                    thread::sleep(HTTP2_POLL_DELAY);
                }
                Err(error) => {
                    push_http2_server_event(
                        &shared,
                        server_id,
                        Http2BridgeEvent {
                            kind: String::from("serverStreamError"),
                            id: server_id,
                            data: Some(http2_error_payload(error.to_string())),
                            ..Http2BridgeEvent::default()
                        },
                    );
                    thread::sleep(HTTP2_POLL_DELAY);
                }
            }
        }
    });
}

fn send_http2_command(
    session: &ActiveHttp2Session,
    command: impl FnOnce(Sender<Result<Value, String>>) -> Http2SessionCommand,
) -> Result<Value, SidecarError> {
    let (respond_to, response_rx) = mpsc::channel();
    session.command_tx.send(command(respond_to)).map_err(|_| {
        SidecarError::InvalidState(String::from("HTTP/2 session command channel closed"))
    })?;
    response_rx
        .recv_timeout(Duration::from_secs(30))
        .map_err(|_| {
            SidecarError::Execution(String::from("timed out waiting for HTTP/2 session command"))
        })?
        .map_err(SidecarError::Execution)
}

fn parse_http2_server_listen_payload(
    request: &JavascriptSyncRpcRequest,
) -> Result<JavascriptHttp2ServerListenRequest, SidecarError> {
    let payload_json =
        javascript_sync_rpc_arg_str(&request.args, 0, "net.http2_server_listen payload")?;
    serde_json::from_str(payload_json).map_err(|error| {
        SidecarError::InvalidState(format!(
            "net.http2_server_listen payload must be valid JSON: {error}"
        ))
    })
}

fn parse_http2_connect_payload(
    request: &JavascriptSyncRpcRequest,
) -> Result<JavascriptHttp2SessionConnectRequest, SidecarError> {
    let payload_json =
        javascript_sync_rpc_arg_str(&request.args, 0, "net.http2_session_connect payload")?;
    serde_json::from_str(payload_json).map_err(|error| {
        SidecarError::InvalidState(format!(
            "net.http2_session_connect payload must be valid JSON: {error}"
        ))
    })
}

fn http2_session_for_id(
    process: &ActiveProcess,
    session_id: u64,
) -> Result<ActiveHttp2Session, SidecarError> {
    let shared = process
        .http2
        .shared
        .lock()
        .map_err(|_| SidecarError::InvalidState(String::from("HTTP/2 state lock poisoned")))?;
    shared
        .sessions
        .get(&session_id)
        .cloned()
        .ok_or_else(|| SidecarError::InvalidState(format!("unknown HTTP/2 session {session_id}")))
}

fn http2_stream_for_id(
    process: &ActiveProcess,
    stream_id: u64,
) -> Result<ActiveHttp2Stream, SidecarError> {
    let shared = process
        .http2
        .shared
        .lock()
        .map_err(|_| SidecarError::InvalidState(String::from("HTTP/2 state lock poisoned")))?;
    shared
        .streams
        .get(&stream_id)
        .cloned()
        .ok_or_else(|| SidecarError::InvalidState(format!("unknown HTTP/2 stream {stream_id}")))
}

fn service_javascript_http2_sync_rpc<B>(
    request: JavascriptHttp2SyncRpcServiceRequest<'_, B>,
) -> Result<Value, SidecarError>
where
    B: NativeSidecarBridge + Send + 'static,
    BridgeError<B>: fmt::Debug + Send + Sync + 'static,
{
    let JavascriptHttp2SyncRpcServiceRequest {
        bridge,
        kernel,
        vm_id,
        dns,
        socket_paths,
        process,
        sync_request: request,
        resource_limits,
        network_counts,
    } = request;
    match request.method.as_str() {
        "net.http2_server_listen" => {
            check_network_resource_limit(
                resource_limits.max_sockets,
                network_counts.sockets,
                1,
                "socket",
            )?;
            let payload = parse_http2_server_listen_payload(request)?;
            let (family, bind_host, guest_host) =
                normalize_tcp_listen_host(payload.host.as_deref())?;
            let requested_port = payload.port.unwrap_or(0);
            bridge.require_network_access(
                vm_id,
                NetworkOperation::Listen,
                format_tcp_resource(bind_host, requested_port),
            )?;
            let port = allocate_guest_listen_port(
                requested_port,
                family,
                &socket_paths.used_tcp_guest_ports,
                socket_paths.listen_policy,
            )?;
            let mut listener =
                ActiveTcpListener::bind(bind_host, guest_host, port, payload.backlog)?;
            let guest_local_addr = listener.guest_local_addr();
            let closed = Arc::new(AtomicBool::new(false));
            {
                let mut state = process.http2.shared.lock().map_err(|_| {
                    SidecarError::InvalidState(String::from("HTTP/2 state lock poisoned"))
                })?;
                state.servers.insert(
                    payload.server_id,
                    ActiveHttp2Server {
                        actual_local_addr: listener.local_addr(),
                        guest_local_addr,
                        secure: payload.secure,
                        tls: payload.tls.clone().map(|mut tls| {
                            tls.is_server = payload.secure;
                            if payload.secure && tls.alpn_protocols.is_none() {
                                tls.alpn_protocols = Some(vec![String::from("h2")]);
                            }
                            tls
                        }),
                        closed: Arc::clone(&closed),
                    },
                );
                state.server_events.entry(payload.server_id).or_default();
            }
            spawn_http2_server_accept_loop(
                Arc::clone(&process.http2.shared),
                payload.server_id,
                listener.listener.take().ok_or_else(|| {
                    SidecarError::InvalidState(String::from(
                        "HTTP/2 listener missing host TCP socket",
                    ))
                })?,
            );
            javascript_net_json_string(
                json!({
                    "address": {
                        "address": guest_local_addr.ip().to_string(),
                        "family": socket_addr_family(&guest_local_addr),
                        "port": guest_local_addr.port(),
                    }
                }),
                "net.http2_server_listen",
            )
        }
        "net.http2_server_poll" => {
            let server_id =
                javascript_sync_rpc_arg_u64(&request.args, 0, "net.http2_server_poll server id")?;
            let wait_ms = javascript_sync_rpc_arg_u64_optional(
                &request.args,
                1,
                "net.http2_server_poll wait ms",
            )?
            .unwrap_or_default();
            match wait_for_http2_event(&process.http2.shared, server_id, true, wait_ms) {
                Some(event) => http2_event_value(&event),
                None => Ok(Value::Null),
            }
        }
        "net.http2_server_wait" => {
            let server_id =
                javascript_sync_rpc_arg_u64(&request.args, 0, "net.http2_server_wait server id")?;
            dispatch_http2_wait_loop(process, server_id, true)
        }
        "net.http2_server_close" => {
            let server_id =
                javascript_sync_rpc_arg_u64(&request.args, 0, "net.http2_server_close server id")?;
            let server = {
                let mut state = process.http2.shared.lock().map_err(|_| {
                    SidecarError::InvalidState(String::from("HTTP/2 state lock poisoned"))
                })?;
                state.servers.remove(&server_id)
            }
            .ok_or_else(|| {
                SidecarError::InvalidState(format!("unknown HTTP/2 server {server_id}"))
            })?;
            server.closed.store(true, Ordering::SeqCst);
            push_http2_server_event(
                &process.http2.shared,
                server_id,
                Http2BridgeEvent {
                    kind: String::from("serverClose"),
                    id: server_id,
                    ..Http2BridgeEvent::default()
                },
            );
            Ok(Value::Null)
        }
        "net.http2_server_respond" => {
            let server_id = javascript_sync_rpc_arg_u64(
                &request.args,
                0,
                "net.http2_server_respond server id",
            )?;
            let request_id = javascript_sync_rpc_arg_u64(
                &request.args,
                1,
                "net.http2_server_respond request id",
            )?;
            let response_json =
                javascript_sync_rpc_arg_str(&request.args, 2, "net.http2_server_respond payload")?;
            ensure_vm_fetch_response_within_limit(
                response_json,
                "net.http2_server_respond",
                VM_FETCH_BUFFER_LIMIT_BYTES,
            )?;
            serde_json::from_str::<Value>(response_json).map_err(|error| {
                SidecarError::Execution(format!(
                    "net.http2_server_respond payload must be valid JSON: {error}"
                ))
            })?;
            let Some(pending) = process
                .pending_http_requests
                .get_mut(&(server_id, request_id))
            else {
                return Err(SidecarError::InvalidState(format!(
                    "unknown pending HTTP/2 request {request_id} for server {server_id}"
                )));
            };
            *pending = Some(response_json.to_owned());
            Ok(Value::Bool(true))
        }
        "net.http2_session_connect" => {
            check_network_resource_limit(
                resource_limits.max_sockets,
                network_counts.sockets,
                1,
                "socket",
            )?;
            check_network_resource_limit(
                resource_limits.max_connections,
                network_counts.connections,
                1,
                "connection",
            )?;
            let payload = parse_http2_connect_payload(request)?;
            let authority = payload.authority.clone().unwrap_or_else(|| {
                format!(
                    "{}://{}:{}",
                    payload.protocol.as_deref().unwrap_or("http"),
                    payload.host.as_deref().unwrap_or("localhost"),
                    payload.port.unwrap_or(80)
                )
            });
            let url = Url::parse(&authority).map_err(|error| {
                SidecarError::InvalidState(format!(
                    "invalid HTTP/2 authority {authority:?}: {error}"
                ))
            })?;
            let secure = url.scheme() == "https" || payload.protocol.as_deref() == Some("https:");
            let host = payload
                .host
                .as_deref()
                .or_else(|| url.host_str())
                .unwrap_or("localhost");
            let port = payload.port.or_else(|| url.port()).unwrap_or(80);
            bridge.require_network_access(
                vm_id,
                NetworkOperation::Http,
                format_tcp_resource(host, port),
            )?;
            let resolved = {
                let shared = process.http2.shared.lock().map_err(|_| {
                    SidecarError::InvalidState(String::from("HTTP/2 state lock poisoned"))
                })?;
                shared
                    .servers
                    .values()
                    .find(|server| {
                        is_loopback_request_host(host) && server.guest_local_addr.port() == port
                    })
                    .map(|server| ResolvedTcpConnectAddr {
                        actual_addr: server.actual_local_addr,
                        guest_remote_addr: server.guest_local_addr,
                        use_kernel_loopback: false,
                    })
            };
            let resolved = match resolved {
                Some(resolved) => resolved,
                None => {
                    resolve_tcp_connect_addr(bridge, kernel, vm_id, dns, host, port, socket_paths)?
                }
            };
            let (command_tx, command_rx) = unbounded_channel();
            let snapshot = Arc::new(Mutex::new(Http2SessionSnapshot {
                encrypted: secure,
                alpn_protocol: Some(String::from(if secure { "h2" } else { "h2c" })),
                local_settings: http2_settings_from_value(&payload.settings),
                remote_settings: BTreeMap::new(),
                state: http2_runtime_snapshot(),
                socket: Http2SocketSnapshot {
                    encrypted: secure,
                    remote_address: Some(resolved.guest_remote_addr.ip().to_string()),
                    remote_port: Some(resolved.guest_remote_addr.port()),
                    remote_family: Some(
                        socket_addr_family(&resolved.guest_remote_addr).to_string(),
                    ),
                    servername: if secure {
                        payload
                            .tls
                            .as_ref()
                            .and_then(|tls| tls.servername.clone())
                            .or_else(|| Some(host.to_string()))
                    } else {
                        None
                    },
                    alpn_protocol: Some(String::from(if secure { "h2" } else { "h2c" })),
                    ..Http2SocketSnapshot::default()
                },
                ..Http2SessionSnapshot::default()
            }));
            let session_id = {
                let mut state = process.http2.shared.lock().map_err(|_| {
                    SidecarError::InvalidState(String::from("HTTP/2 state lock poisoned"))
                })?;
                let session_id = next_http2_session_id(&mut state);
                state
                    .sessions
                    .insert(session_id, ActiveHttp2Session { command_tx });
                state.session_events.entry(session_id).or_default();
                session_id
            };
            spawn_http2_client_session(
                Arc::clone(&process.http2.shared),
                session_id,
                resolved.actual_addr,
                if secure {
                    Some(payload.tls.unwrap_or(JavascriptTlsBridgeOptions {
                        is_server: false,
                        servername: Some(host.to_string()),
                        alpn_protocols: Some(vec![String::from("h2")]),
                        ..JavascriptTlsBridgeOptions::default()
                    }))
                } else {
                    None
                },
                Arc::clone(&snapshot),
                command_rx,
            );
            let snapshot_json =
                http2_snapshot_json(&snapshot.lock().expect("http2 snapshot lock").clone())?;
            javascript_net_json_string(
                json!({
                    "sessionId": session_id,
                    "state": snapshot_json,
                }),
                "net.http2_session_connect",
            )
        }
        "net.http2_session_request" => {
            let session_id = javascript_sync_rpc_arg_u64(
                &request.args,
                0,
                "net.http2_session_request session id",
            )?;
            let headers_json =
                javascript_sync_rpc_arg_str(&request.args, 1, "net.http2_session_request headers")?;
            let options_json =
                javascript_sync_rpc_arg_str(&request.args, 2, "net.http2_session_request options")?;
            let session = http2_session_for_id(process, session_id)?;
            send_http2_command(&session, |respond_to| Http2SessionCommand::Request {
                headers_json: headers_json.to_owned(),
                options_json: options_json.to_owned(),
                respond_to,
            })
        }
        "net.http2_session_settings" => {
            let session_id = javascript_sync_rpc_arg_u64(
                &request.args,
                0,
                "net.http2_session_settings session id",
            )?;
            let settings_json = javascript_sync_rpc_arg_str(
                &request.args,
                1,
                "net.http2_session_settings settings",
            )?;
            let session = http2_session_for_id(process, session_id)?;
            send_http2_command(&session, |respond_to| Http2SessionCommand::Settings {
                settings_json: settings_json.to_owned(),
                respond_to,
            })
        }
        "net.http2_session_set_local_window_size" => {
            let session_id = javascript_sync_rpc_arg_u64(
                &request.args,
                0,
                "net.http2_session_set_local_window_size session id",
            )?;
            let window_size = javascript_sync_rpc_arg_u64(
                &request.args,
                1,
                "net.http2_session_set_local_window_size window size",
            )?;
            let session = http2_session_for_id(process, session_id)?;
            send_http2_command(&session, |respond_to| {
                Http2SessionCommand::SetLocalWindowSize {
                    size: window_size as u32,
                    respond_to,
                }
            })
        }
        "net.http2_session_goaway" => {
            let session_id = javascript_sync_rpc_arg_u64(
                &request.args,
                0,
                "net.http2_session_goaway session id",
            )?;
            let error_code = javascript_sync_rpc_arg_u64(
                &request.args,
                1,
                "net.http2_session_goaway error code",
            )?;
            let last_stream_id = javascript_sync_rpc_arg_u64(
                &request.args,
                2,
                "net.http2_session_goaway last stream id",
            )?;
            let opaque_data = request
                .args
                .get(3)
                .and_then(Value::as_str)
                .map(|value| {
                    base64::engine::general_purpose::STANDARD
                        .decode(value)
                        .map_err(|error| {
                            SidecarError::InvalidState(format!("invalid GOAWAY payload: {error}"))
                        })
                })
                .transpose()?;
            let session = http2_session_for_id(process, session_id)?;
            send_http2_command(&session, |respond_to| Http2SessionCommand::Goaway {
                error_code: error_code as u32,
                last_stream_id: last_stream_id as u32,
                opaque_data,
                respond_to,
            })
        }
        "net.http2_session_close" | "net.http2_session_destroy" => {
            let session_id = javascript_sync_rpc_arg_u64(
                &request.args,
                0,
                "net.http2_session_close session id",
            )?;
            let session = http2_session_for_id(process, session_id)?;
            send_http2_command(&session, |respond_to| Http2SessionCommand::Close {
                abrupt: request.method == "net.http2_session_destroy",
                respond_to,
            })
        }
        "net.http2_session_poll" => {
            let session_id =
                javascript_sync_rpc_arg_u64(&request.args, 0, "net.http2_session_poll session id")?;
            let wait_ms = javascript_sync_rpc_arg_u64_optional(
                &request.args,
                1,
                "net.http2_session_poll wait ms",
            )?
            .unwrap_or_default();
            match wait_for_http2_event(&process.http2.shared, session_id, false, wait_ms) {
                Some(event) => http2_event_value(&event),
                None => Ok(Value::Null),
            }
        }
        "net.http2_session_wait" => {
            let session_id =
                javascript_sync_rpc_arg_u64(&request.args, 0, "net.http2_session_wait session id")?;
            dispatch_http2_wait_loop(process, session_id, false)
        }
        "net.http2_stream_respond" => {
            let stream_id = javascript_sync_rpc_arg_u64(
                &request.args,
                0,
                "net.http2_stream_respond stream id",
            )?;
            let headers_json =
                javascript_sync_rpc_arg_str(&request.args, 1, "net.http2_stream_respond headers")?;
            let stream = http2_stream_for_id(process, stream_id)?;
            let session = http2_session_for_id(process, stream.session_id)?;
            send_http2_command(&session, |respond_to| Http2SessionCommand::StreamRespond {
                stream_id,
                headers_json: headers_json.to_owned(),
                respond_to,
            })
        }
        "net.http2_stream_push_stream" => {
            let stream_id = javascript_sync_rpc_arg_u64(
                &request.args,
                0,
                "net.http2_stream_push_stream stream id",
            )?;
            let headers_json = javascript_sync_rpc_arg_str(
                &request.args,
                1,
                "net.http2_stream_push_stream headers",
            )?;
            let _options_json = javascript_sync_rpc_arg_str(
                &request.args,
                2,
                "net.http2_stream_push_stream options",
            )?;
            let stream = http2_stream_for_id(process, stream_id)?;
            let session = http2_session_for_id(process, stream.session_id)?;
            send_http2_command(&session, |respond_to| Http2SessionCommand::StreamPush {
                stream_id,
                headers_json: headers_json.to_owned(),
                respond_to,
            })
        }
        "net.http2_stream_write" => {
            let stream_id =
                javascript_sync_rpc_arg_u64(&request.args, 0, "net.http2_stream_write stream id")?;
            let chunk =
                javascript_sync_rpc_base64_arg(&request.args, 1, "net.http2_stream_write data")?;
            let stream = http2_stream_for_id(process, stream_id)?;
            let session = http2_session_for_id(process, stream.session_id)?;
            send_http2_command(&session, |respond_to| Http2SessionCommand::StreamWrite {
                stream_id,
                chunk,
                end_stream: false,
                respond_to,
            })
        }
        "net.http2_stream_end" => {
            let stream_id =
                javascript_sync_rpc_arg_u64(&request.args, 0, "net.http2_stream_end stream id")?;
            let chunk = request
                .args
                .get(1)
                .and_then(Value::as_str)
                .map(|value| {
                    base64::engine::general_purpose::STANDARD
                        .decode(value)
                        .map_err(|error| {
                            SidecarError::InvalidState(format!(
                                "invalid HTTP/2 stream payload: {error}"
                            ))
                        })
                })
                .transpose()?
                .unwrap_or_default();
            let stream = http2_stream_for_id(process, stream_id)?;
            let session = http2_session_for_id(process, stream.session_id)?;
            send_http2_command(&session, |respond_to| Http2SessionCommand::StreamWrite {
                stream_id,
                chunk,
                end_stream: true,
                respond_to,
            })
        }
        "net.http2_stream_close" => {
            let stream_id =
                javascript_sync_rpc_arg_u64(&request.args, 0, "net.http2_stream_close stream id")?;
            let code = javascript_sync_rpc_arg_u64_optional(
                &request.args,
                1,
                "net.http2_stream_close error code",
            )?
            .map(|value| value as u32);
            let stream = http2_stream_for_id(process, stream_id)?;
            let session = http2_session_for_id(process, stream.session_id)?;
            send_http2_command(&session, |respond_to| Http2SessionCommand::StreamClose {
                stream_id,
                error_code: code,
                respond_to,
            })
        }
        "net.http2_stream_pause" => {
            let stream_id =
                javascript_sync_rpc_arg_u64(&request.args, 0, "net.http2_stream_pause stream id")?;
            let stream = http2_stream_for_id(process, stream_id)?;
            stream.paused.store(true, Ordering::SeqCst);
            Ok(Value::Null)
        }
        "net.http2_stream_resume" => {
            let stream_id =
                javascript_sync_rpc_arg_u64(&request.args, 0, "net.http2_stream_resume stream id")?;
            let stream = http2_stream_for_id(process, stream_id)?;
            stream.paused.store(false, Ordering::SeqCst);
            Ok(Value::Null)
        }
        "net.http2_stream_respond_with_file" => {
            let stream_id = javascript_sync_rpc_arg_u64(
                &request.args,
                0,
                "net.http2_stream_respond_with_file stream id",
            )?;
            let path = javascript_sync_rpc_arg_str(
                &request.args,
                1,
                "net.http2_stream_respond_with_file path",
            )?;
            let headers_json = javascript_sync_rpc_arg_str(
                &request.args,
                2,
                "net.http2_stream_respond_with_file headers",
            )?;
            let options_json = javascript_sync_rpc_arg_str(
                &request.args,
                3,
                "net.http2_stream_respond_with_file options",
            )?;
            let stream = http2_stream_for_id(process, stream_id)?;
            let session = http2_session_for_id(process, stream.session_id)?;
            let guest_path = resolve_http2_file_response_guest_path(process, path);
            let body = kernel.read_file(&guest_path).map_err(kernel_error)?;
            send_http2_command(&session, |respond_to| {
                Http2SessionCommand::StreamRespondWithFile {
                    stream_id,
                    body,
                    headers_json: headers_json.to_owned(),
                    options_json: options_json.to_owned(),
                    respond_to,
                }
            })
        }
        other => Err(SidecarError::InvalidState(format!(
            "unsupported JavaScript HTTP/2 sync RPC method {other}"
        ))),
    }
}

const JAVASCRIPT_NET_POLL_MAX_WAIT: Duration = Duration::from_millis(50);
const EXITED_PROCESS_SNAPSHOT_RETENTION: Duration = Duration::from_secs(2);

fn resolve_http2_file_response_guest_path(process: &ActiveProcess, path: &str) -> String {
    if Path::new(path).is_absolute() {
        normalize_path(path)
    } else {
        normalize_path(&format!("{}/{}", process.guest_cwd, path))
    }
}

pub(crate) fn clamp_javascript_net_poll_wait(wait_ms: u64) -> Duration {
    // WASM net.poll runs on the sidecar's sync-RPC main thread. Guest-controlled waits
    // must stay bounded so one VM cannot stall dispose/shutdown or unrelated VM work.
    if wait_ms == 0 {
        Duration::ZERO
    } else {
        Duration::from_millis(wait_ms).min(JAVASCRIPT_NET_POLL_MAX_WAIT)
    }
}

pub(crate) fn service_javascript_net_sync_rpc<B>(
    request: JavascriptNetSyncRpcServiceRequest<'_, B>,
) -> Result<Value, SidecarError>
where
    B: NativeSidecarBridge + Send + 'static,
    BridgeError<B>: fmt::Debug + Send + Sync + 'static,
{
    let JavascriptNetSyncRpcServiceRequest {
        bridge,
        vm_id,
        dns,
        socket_paths,
        kernel,
        process,
        sync_request: request,
        resource_limits,
        network_counts,
    } = request;
    match request.method.as_str() {
        "net.http_listen" => {
            check_network_resource_limit(
                resource_limits.max_sockets,
                network_counts.sockets,
                1,
                "socket",
            )?;
            let payload_json =
                javascript_sync_rpc_arg_str(&request.args, 0, "net.http_listen payload")?;
            let payload: JavascriptHttpListenRequest =
                serde_json::from_str(payload_json).map_err(|error| {
                    SidecarError::InvalidState(format!(
                        "net.http_listen payload must be valid JSON: {error}"
                    ))
                })?;
            let (family, bind_host, guest_host) =
                normalize_tcp_listen_host(payload.hostname.as_deref())?;
            let requested_port = payload.port.unwrap_or(0);
            bridge.require_network_access(
                vm_id,
                NetworkOperation::Listen,
                format_tcp_resource(bind_host, requested_port),
            )?;
            let port = allocate_guest_listen_port(
                requested_port,
                family,
                &socket_paths.used_tcp_guest_ports,
                socket_paths.listen_policy,
            )?;
            let mut listener = ActiveTcpListener::bind(
                bind_host,
                guest_host,
                port,
                Some(DEFAULT_JAVASCRIPT_NET_BACKLOG),
            )?;
            let guest_local_addr = listener.guest_local_addr();
            process.http_servers.insert(
                payload.server_id,
                ActiveHttpServer {
                    listener: listener.listener.take().ok_or_else(|| {
                        SidecarError::InvalidState(String::from(
                            "HTTP listener missing host TCP socket",
                        ))
                    })?,
                    guest_local_addr,
                    next_request_id: 0,
                },
            );
            serde_json::to_string(&json!({
                "address": {
                    "address": guest_local_addr.ip().to_string(),
                    "family": socket_addr_family(&guest_local_addr),
                    "port": guest_local_addr.port(),
                }
            }))
            .map(Value::String)
            .map_err(|error| {
                SidecarError::Execution(format!("ERR_AGENTOS_NODE_SYNC_RPC: {error}"))
            })
        }
        "net.http_close" => {
            let server_id =
                javascript_sync_rpc_arg_u64(&request.args, 0, "net.http_close server id")?;
            let server = process.http_servers.remove(&server_id).ok_or_else(|| {
                SidecarError::InvalidState(format!("unknown HTTP server {server_id}"))
            })?;
            drop(server.listener);
            process
                .pending_http_requests
                .retain(|(pending_server_id, _), _| *pending_server_id != server_id);
            Ok(Value::Null)
        }
        "net.http_wait" => {
            let server_id =
                javascript_sync_rpc_arg_u64(&request.args, 0, "net.http_wait server id")?;
            dispatch_http_wait_loop(process, server_id)
        }
        "net.http_respond" => {
            let server_id =
                javascript_sync_rpc_arg_u64(&request.args, 0, "net.http_respond server id")?;
            let request_id =
                javascript_sync_rpc_arg_u64(&request.args, 1, "net.http_respond request id")?;
            let response_json =
                javascript_sync_rpc_arg_str(&request.args, 2, "net.http_respond payload")?;
            ensure_vm_fetch_response_within_limit(
                response_json,
                "net.http_respond",
                VM_FETCH_BUFFER_LIMIT_BYTES,
            )?;
            serde_json::from_str::<Value>(response_json).map_err(|error| {
                SidecarError::Execution(format!(
                    "net.http_respond payload must be valid JSON: {error}"
                ))
            })?;
            let Some(pending) = process
                .pending_http_requests
                .get_mut(&(server_id, request_id))
            else {
                return Err(SidecarError::InvalidState(format!(
                    "unknown pending HTTP request {request_id} for server {server_id}"
                )));
            };
            *pending = Some(response_json.to_owned());
            Ok(Value::Null)
        }
        "net.reserve_tcp_port" => {
            let payload = request
                .args
                .first()
                .cloned()
                .ok_or_else(|| {
                    SidecarError::InvalidState(String::from(
                        "net.reserve_tcp_port requires a request payload",
                    ))
                })
                .and_then(|value| {
                    serde_json::from_value::<JavascriptNetReserveTcpPortRequest>(value).map_err(
                        |error| {
                            SidecarError::InvalidState(format!(
                                "invalid net.reserve_tcp_port payload: {error}"
                            ))
                        },
                    )
                })?;
            let (family, _bind_host, guest_host) =
                normalize_tcp_listen_host(payload.host.as_deref())?;
            let requested_port = payload.port.unwrap_or(0);
            let port = allocate_guest_listen_port(
                requested_port,
                family,
                &socket_paths.used_tcp_guest_ports,
                socket_paths.listen_policy,
            )?;
            let reservation_id = process.allocate_tcp_port_reservation_id();
            process
                .tcp_port_reservations
                .insert(reservation_id.clone(), (family, port));
            Ok(json!({
                "reservationId": reservation_id,
                "localAddress": guest_host,
                "localPort": port,
                "family": match family {
                    JavascriptSocketFamily::Ipv4 => "IPv4",
                    JavascriptSocketFamily::Ipv6 => "IPv6",
                },
            }))
        }
        "net.release_tcp_port" => {
            let reservation_id =
                javascript_sync_rpc_arg_str(&request.args, 0, "net.release_tcp_port reservation")?;
            process.tcp_port_reservations.remove(reservation_id);
            Ok(Value::Null)
        }
        "net.connect" => {
            check_network_resource_limit(
                resource_limits.max_sockets,
                network_counts.sockets,
                1,
                "socket",
            )?;
            check_network_resource_limit(
                resource_limits.max_connections,
                network_counts.connections,
                1,
                "connection",
            )?;
            let payload = request
                .args
                .first()
                .cloned()
                .ok_or_else(|| {
                    SidecarError::InvalidState(String::from(
                        "net.connect requires a request payload",
                    ))
                })
                .and_then(|value| {
                    serde_json::from_value::<JavascriptNetConnectRequest>(value).map_err(|error| {
                        SidecarError::InvalidState(format!("invalid net.connect payload: {error}"))
                    })
                })?;
            if let Some(path) = payload.path.as_deref() {
                let guest_path = normalize_path(path);
                let host_path = resolve_guest_socket_host_path(socket_paths, &guest_path);
                let socket = ActiveUnixSocket::connect(&host_path, &guest_path)?;
                let socket_id = process.allocate_unix_socket_id();
                process.unix_sockets.insert(socket_id.clone(), socket);
                Ok(json!({
                    "socketId": socket_id,
                    "remotePath": guest_path,
                }))
            } else {
                let port = payload.port.ok_or_else(|| {
                    SidecarError::InvalidState(String::from(
                        "net.connect requires either a path or port",
                    ))
                })?;
                let host = payload.host.as_deref().unwrap_or("localhost");
                let local_reservation = payload.local_reservation.as_deref().and_then(|id| {
                    process
                        .tcp_port_reservations
                        .remove(id)
                        .map(|reservation| (id.to_owned(), reservation))
                });
                bridge.require_network_access(
                    vm_id,
                    NetworkOperation::Http,
                    format_tcp_resource(host, port),
                )?;
                if is_loopback_socket_host(host) {
                    let families = [JavascriptSocketFamily::Ipv4, JavascriptSocketFamily::Ipv6];
                    if let Some((family, target)) = families.iter().find_map(|family| {
                        socket_paths
                            .http_loopback_target(*family, port)
                            .map(|target| (*family, target))
                    }) {
                        if let Some((reservation_id, reservation)) = local_reservation {
                            process
                                .tcp_port_reservations
                                .insert(reservation_id, reservation);
                        }
                        let remote_address = match family {
                            JavascriptSocketFamily::Ipv4 => "127.0.0.1",
                            JavascriptSocketFamily::Ipv6 => "::1",
                        };
                        return Ok(json!({
                            "loopbackHttpTarget": {
                                "processId": target.process_id.clone(),
                                "serverId": target.server_id,
                                "host": remote_address,
                                "port": port,
                            },
                            "localAddress": match family {
                                JavascriptSocketFamily::Ipv4 => "127.0.0.1",
                                JavascriptSocketFamily::Ipv6 => "::1",
                            },
                            "localPort": payload.local_port.unwrap_or(0),
                            "remoteAddress": remote_address,
                            "remotePort": port,
                            "remoteFamily": match family {
                                JavascriptSocketFamily::Ipv4 => "IPv4",
                                JavascriptSocketFamily::Ipv6 => "IPv6",
                            },
                        }));
                    }
                }
                let connect_result = ActiveTcpSocket::connect(ActiveTcpConnectRequest {
                    bridge,
                    kernel,
                    kernel_pid: process.kernel_pid,
                    vm_id,
                    dns,
                    host,
                    port,
                    local_address: payload.local_address.as_deref(),
                    local_port: payload.local_port,
                    local_reservation: local_reservation
                        .as_ref()
                        .map(|(_, reservation)| *reservation),
                    context: socket_paths,
                });
                if let Err(error) = connect_result {
                    if let Some((reservation_id, reservation)) = local_reservation {
                        process
                            .tcp_port_reservations
                            .insert(reservation_id, reservation);
                    }
                    return Err(error);
                }
                let socket = connect_result?;
                let socket_id = process.allocate_tcp_socket_id();
                let local_addr = socket.guest_local_addr;
                let remote_addr = socket.guest_remote_addr;
                process.tcp_sockets.insert(socket_id.clone(), socket);
                Ok(json!({
                    "socketId": socket_id,
                    "localAddress": local_addr.ip().to_string(),
                    "localPort": local_addr.port(),
                    "remoteAddress": remote_addr.ip().to_string(),
                    "remotePort": remote_addr.port(),
                    "remoteFamily": socket_addr_family(&remote_addr),
                }))
            }
        }
        "net.listen" => {
            check_network_resource_limit(
                resource_limits.max_sockets,
                network_counts.sockets,
                1,
                "socket",
            )?;
            let payload = request
                .args
                .first()
                .cloned()
                .ok_or_else(|| {
                    SidecarError::InvalidState(String::from(
                        "net.listen requires a request payload",
                    ))
                })
                .and_then(|value| match value {
                    Value::String(json) => {
                        serde_json::from_str::<JavascriptNetListenRequest>(&json).map_err(|error| {
                            SidecarError::InvalidState(format!(
                                "invalid net.listen payload: {error}"
                            ))
                        })
                    }
                    other => serde_json::from_value::<JavascriptNetListenRequest>(other).map_err(
                        |error| {
                            SidecarError::InvalidState(format!(
                                "invalid net.listen payload: {error}"
                            ))
                        },
                    ),
                })?;
            if let Some(path) = payload.path.as_deref() {
                let guest_path = normalize_path(path);
                if kernel.exists(&guest_path).map_err(kernel_error)? {
                    return Err(sidecar_net_error(std::io::Error::from_raw_os_error(
                        libc::EADDRINUSE,
                    )));
                }

                let host_path = resolve_guest_socket_host_path(socket_paths, &guest_path);
                let on_host_mount =
                    host_mount_path_for_guest_path_from_mounts(&socket_paths.mounts, &guest_path)
                        .is_some();
                let listener = ActiveUnixListener::bind(&host_path, &guest_path, payload.backlog)?;
                if !on_host_mount {
                    ensure_kernel_parent_directories(kernel, &guest_path)?;
                    kernel
                        .write_file(&guest_path, Vec::new())
                        .map_err(kernel_error)?;
                }
                let listener_id = process.allocate_unix_listener_id();
                process.unix_listeners.insert(listener_id.clone(), listener);
                Ok(json!({
                    "serverId": listener_id,
                    "path": guest_path,
                }))
            } else {
                let (family, bind_host, guest_host) =
                    normalize_tcp_listen_host(payload.host.as_deref())?;
                let requested_port = payload.port.unwrap_or(0);
                bridge.require_network_access(
                    vm_id,
                    NetworkOperation::Listen,
                    format_tcp_resource(bind_host, requested_port),
                )?;
                let local_reservation = payload.local_reservation.as_deref().and_then(|id| {
                    process
                        .tcp_port_reservations
                        .remove(id)
                        .map(|reservation| (id.to_owned(), reservation))
                });
                let port = if requested_port != 0
                    && local_reservation
                        .as_ref()
                        .map(|(_, reservation)| *reservation)
                        == Some((family, requested_port))
                {
                    requested_port
                } else {
                    allocate_guest_listen_port(
                        requested_port,
                        family,
                        &socket_paths.used_tcp_guest_ports,
                        socket_paths.listen_policy,
                    )?
                };
                let listener_result = ActiveTcpListener::bind_kernel(
                    kernel,
                    process.kernel_pid,
                    guest_host,
                    port,
                    payload.backlog,
                );
                if let Err(error) = listener_result {
                    if let Some((reservation_id, reservation)) = local_reservation {
                        process
                            .tcp_port_reservations
                            .insert(reservation_id, reservation);
                    }
                    return Err(error);
                }
                let listener = listener_result?;
                let listener_id = process.allocate_tcp_listener_id();
                let local_addr = listener.guest_local_addr();
                process.tcp_listeners.insert(listener_id.clone(), listener);
                Ok(json!({
                    "serverId": listener_id,
                    "localAddress": local_addr.ip().to_string(),
                    "localPort": local_addr.port(),
                    "family": socket_addr_family(&local_addr),
                }))
            }
        }
        "net.poll" => {
            let socket_id = javascript_sync_rpc_arg_str(&request.args, 0, "net.poll socket id")?;
            let wait_ms =
                javascript_sync_rpc_arg_u64_optional(&request.args, 1, "net.poll wait ms")?
                    .unwrap_or_default();
            let wait = clamp_javascript_net_poll_wait(wait_ms);
            let event = if let Some(socket) = process.tcp_sockets.get_mut(socket_id) {
                socket.poll(kernel, process.kernel_pid, wait)?
            } else if let Some(socket) = process.unix_sockets.get_mut(socket_id) {
                socket.poll(wait)?
            } else {
                return Err(SidecarError::InvalidState(format!(
                    "unknown net socket {socket_id}"
                )));
            };

            match event {
                Some(JavascriptTcpSocketEvent::Data(chunk)) => Ok(json!({
                    "type": "data",
                    "data": javascript_sync_rpc_bytes_value(&chunk),
                })),
                Some(JavascriptTcpSocketEvent::End) => Ok(json!({
                    "type": "end",
                })),
                Some(JavascriptTcpSocketEvent::Error { code, message }) => Ok(json!({
                    "type": "error",
                    "code": code,
                    "message": message,
                })),
                Some(JavascriptTcpSocketEvent::Close { had_error }) => {
                    if let Some(socket) = process.tcp_sockets.remove(socket_id) {
                        if let Some(listener_id) = socket.listener_id.as_deref() {
                            if let Some(listener) = process.tcp_listeners.get_mut(listener_id) {
                                listener.release_connection(socket_id);
                            }
                        }
                    } else if let Some(socket) = process.unix_sockets.remove(socket_id) {
                        if let Some(listener_id) = socket.listener_id.as_deref() {
                            if let Some(listener) = process.unix_listeners.get_mut(listener_id) {
                                listener.release_connection(socket_id);
                            }
                        }
                    }
                    Ok(json!({
                        "type": "close",
                        "hadError": had_error,
                    }))
                }
                None => Ok(Value::Null),
            }
        }
        "net.socket_wait_connect" => {
            let socket_id =
                javascript_sync_rpc_arg_str(&request.args, 0, "net.socket_wait_connect socket id")?;
            if let Some(socket) = process.tcp_sockets.get(socket_id) {
                javascript_net_json_string(socket.socket_info(), "net.socket_wait_connect")
            } else {
                let socket = process.unix_sockets.get(socket_id).ok_or_else(|| {
                    SidecarError::InvalidState(format!("unknown net socket {socket_id}"))
                })?;
                javascript_net_json_string(socket.socket_info(), "net.socket_wait_connect")
            }
        }
        "net.socket_read" => {
            let socket_id =
                javascript_sync_rpc_arg_str(&request.args, 0, "net.socket_read socket id")?;
            if let Some(socket) = process.tcp_sockets.get_mut(socket_id) {
                javascript_net_read_value(socket.poll(
                    kernel,
                    process.kernel_pid,
                    Duration::ZERO,
                )?)
            } else {
                let socket = process.unix_sockets.get_mut(socket_id).ok_or_else(|| {
                    SidecarError::InvalidState(format!("unknown net socket {socket_id}"))
                })?;
                javascript_net_read_value(socket.poll(Duration::ZERO)?)
            }
        }
        "net.socket_set_no_delay" => {
            let socket_id =
                javascript_sync_rpc_arg_str(&request.args, 0, "net.socket_set_no_delay socket id")?;
            let enable =
                javascript_sync_rpc_arg_bool(&request.args, 1, "net.socket_set_no_delay enabled")?;
            if let Some(socket) = process.tcp_sockets.get_mut(socket_id) {
                socket.set_no_delay(enable)?;
            } else if !process.unix_sockets.contains_key(socket_id) {
                return Err(SidecarError::InvalidState(format!(
                    "unknown net socket {socket_id}"
                )));
            }
            Ok(Value::Null)
        }
        "net.socket_set_keep_alive" => {
            let socket_id = javascript_sync_rpc_arg_str(
                &request.args,
                0,
                "net.socket_set_keep_alive socket id",
            )?;
            let enable = javascript_sync_rpc_arg_bool(
                &request.args,
                1,
                "net.socket_set_keep_alive enabled",
            )?;
            let initial_delay_secs = javascript_sync_rpc_arg_u64_optional(
                &request.args,
                2,
                "net.socket_set_keep_alive initial delay seconds",
            )?;
            if let Some(socket) = process.tcp_sockets.get_mut(socket_id) {
                socket.set_keep_alive(enable, initial_delay_secs)?;
            } else if !process.unix_sockets.contains_key(socket_id) {
                return Err(SidecarError::InvalidState(format!(
                    "unknown net socket {socket_id}"
                )));
            }
            Ok(Value::Null)
        }
        "net.socket_upgrade_tls" => {
            let socket_id =
                javascript_sync_rpc_arg_str(&request.args, 0, "net.socket_upgrade_tls socket id")?;
            let options_json =
                javascript_sync_rpc_arg_str(&request.args, 1, "net.socket_upgrade_tls options")?;
            let options: JavascriptTlsBridgeOptions =
                serde_json::from_str(options_json).map_err(|error| {
                    SidecarError::InvalidState(format!(
                        "net.socket_upgrade_tls options must be valid JSON: {error}"
                    ))
                })?;
            let socket = process.tcp_sockets.get(socket_id).ok_or_else(|| {
                SidecarError::InvalidState(format!(
                    "unknown TCP socket {socket_id} for TLS upgrade"
                ))
            })?;
            socket.upgrade_tls(vm_id, kernel, options)?;
            Ok(Value::Null)
        }
        "net.socket_get_tls_client_hello" => {
            let socket_id = javascript_sync_rpc_arg_str(
                &request.args,
                0,
                "net.socket_get_tls_client_hello socket id",
            )?;
            let socket = process.tcp_sockets.get(socket_id).ok_or_else(|| {
                SidecarError::InvalidState(format!(
                    "unknown TCP socket {socket_id} for TLS client hello query"
                ))
            })?;
            socket.tls_client_hello_json(vm_id, kernel)
        }
        "net.socket_tls_query" => {
            let socket_id =
                javascript_sync_rpc_arg_str(&request.args, 0, "net.socket_tls_query socket id")?;
            let query =
                javascript_sync_rpc_arg_str(&request.args, 1, "net.socket_tls_query query")?;
            let detailed = request
                .args
                .get(2)
                .and_then(Value::as_bool)
                .unwrap_or(false);
            let socket = process.tcp_sockets.get(socket_id).ok_or_else(|| {
                SidecarError::InvalidState(format!("unknown TCP socket {socket_id} for TLS query"))
            })?;
            socket.tls_query(query, detailed)
        }
        "net.server_poll" => {
            let listener_id =
                javascript_sync_rpc_arg_str(&request.args, 0, "net.server_poll listener id")?;
            let wait_ms =
                javascript_sync_rpc_arg_u64_optional(&request.args, 1, "net.server_poll wait ms")?
                    .unwrap_or_default();
            let tcp_event = if let Some(listener) = process.tcp_listeners.get_mut(listener_id) {
                Some(listener.poll(kernel, process.kernel_pid, Duration::from_millis(wait_ms))?)
            } else {
                None
            };

            if let Some(event) = tcp_event {
                return match event {
                    Some(JavascriptTcpListenerEvent::Connection(pending)) => {
                        let PendingTcpSocket {
                            stream,
                            kernel_socket_id,
                            preallocated,
                            guest_local_addr,
                            guest_remote_addr,
                        } = pending;
                        if !preallocated {
                            if let Err(error) = check_network_resource_limit(
                                resource_limits.max_sockets,
                                network_counts.sockets,
                                1,
                                "socket",
                            )
                            .and_then(|()| {
                                check_network_resource_limit(
                                    resource_limits.max_connections,
                                    network_counts.connections,
                                    1,
                                    "connection",
                                )
                            }) {
                                if let Some(stream) = stream {
                                    let _ = stream.shutdown(Shutdown::Both);
                                }
                                return Ok(json!({
                                    "type": "error",
                                    "code": "EAGAIN",
                                    "message": error.to_string(),
                                }));
                            }
                        }
                        let socket = if let Some(stream) = stream {
                            ActiveTcpSocket::from_stream(
                                stream,
                                Some(listener_id.to_string()),
                                guest_local_addr,
                                guest_remote_addr,
                            )?
                        } else {
                            ActiveTcpSocket::from_kernel(
                                kernel_socket_id.ok_or_else(|| {
                                    SidecarError::InvalidState(String::from(
                                        "kernel TCP accept missing socket id",
                                    ))
                                })?,
                                Some(listener_id.to_string()),
                                guest_local_addr,
                                guest_remote_addr,
                            )
                        };
                        let socket_id = process.allocate_tcp_socket_id();
                        if let Some(listener) = process.tcp_listeners.get_mut(listener_id) {
                            listener.register_connection(&socket_id);
                        }
                        process.tcp_sockets.insert(socket_id.clone(), socket);
                        Ok(json!({
                            "type": "connection",
                            "socketId": socket_id,
                            "localAddress": guest_local_addr.ip().to_string(),
                            "localPort": guest_local_addr.port(),
                            "remoteAddress": guest_remote_addr.ip().to_string(),
                            "remotePort": guest_remote_addr.port(),
                            "remoteFamily": socket_addr_family(&guest_remote_addr),
                        }))
                    }
                    Some(JavascriptTcpListenerEvent::Error { code, message }) => Ok(json!({
                        "type": "error",
                        "code": code,
                        "message": message,
                    })),
                    None => Ok(Value::Null),
                };
            }

            let event = {
                let listener = process.unix_listeners.get_mut(listener_id).ok_or_else(|| {
                    SidecarError::InvalidState(format!("unknown net listener {listener_id}"))
                })?;
                listener.poll(Duration::from_millis(wait_ms))?
            };

            match event {
                Some(JavascriptUnixListenerEvent::Connection(pending)) => {
                    if let Err(error) = check_network_resource_limit(
                        resource_limits.max_sockets,
                        network_counts.sockets,
                        1,
                        "socket",
                    )
                    .and_then(|()| {
                        check_network_resource_limit(
                            resource_limits.max_connections,
                            network_counts.connections,
                            1,
                            "connection",
                        )
                    }) {
                        let _ = pending.stream.shutdown(Shutdown::Both);
                        return Ok(json!({
                            "type": "error",
                            "code": "EAGAIN",
                            "message": error.to_string(),
                        }));
                    }
                    let socket = ActiveUnixSocket::from_stream(
                        pending.stream,
                        Some(listener_id.to_string()),
                        pending.local_path.clone(),
                        pending.remote_path.clone(),
                    )?;
                    let socket_id = process.allocate_unix_socket_id();
                    if let Some(listener) = process.unix_listeners.get_mut(listener_id) {
                        listener.register_connection(&socket_id);
                    }
                    process.unix_sockets.insert(socket_id.clone(), socket);
                    Ok(json!({
                        "type": "connection",
                        "socketId": socket_id,
                        "localPath": pending.local_path,
                        "remotePath": pending.remote_path,
                    }))
                }
                Some(JavascriptUnixListenerEvent::Error { code, message }) => Ok(json!({
                    "type": "error",
                    "code": code,
                    "message": message,
                })),
                None => Ok(Value::Null),
            }
        }
        "net.server_accept" => {
            let listener_id =
                javascript_sync_rpc_arg_str(&request.args, 0, "net.server_accept listener id")?;
            if let Some(listener) = process.tcp_listeners.get_mut(listener_id) {
                return match listener.poll(kernel, process.kernel_pid, Duration::ZERO)? {
                    Some(JavascriptTcpListenerEvent::Connection(pending)) => {
                        let PendingTcpSocket {
                            stream,
                            kernel_socket_id,
                            preallocated,
                            guest_local_addr,
                            guest_remote_addr,
                        } = pending;
                        if !preallocated {
                            check_network_resource_limit(
                                resource_limits.max_sockets,
                                network_counts.sockets,
                                1,
                                "socket",
                            )?;
                            check_network_resource_limit(
                                resource_limits.max_connections,
                                network_counts.connections,
                                1,
                                "connection",
                            )?;
                        }
                        let info = json!({
                            "localAddress": guest_local_addr.ip().to_string(),
                            "localPort": guest_local_addr.port(),
                            "localFamily": socket_addr_family(&guest_local_addr),
                            "remoteAddress": guest_remote_addr.ip().to_string(),
                            "remotePort": guest_remote_addr.port(),
                            "remoteFamily": socket_addr_family(&guest_remote_addr),
                        });
                        let socket = if let Some(stream) = stream {
                            ActiveTcpSocket::from_stream(
                                stream,
                                Some(listener_id.to_string()),
                                guest_local_addr,
                                guest_remote_addr,
                            )?
                        } else {
                            ActiveTcpSocket::from_kernel(
                                kernel_socket_id.ok_or_else(|| {
                                    SidecarError::InvalidState(String::from(
                                        "kernel TCP accept missing socket id",
                                    ))
                                })?,
                                Some(listener_id.to_string()),
                                guest_local_addr,
                                guest_remote_addr,
                            )
                        };
                        let socket_id = process.allocate_tcp_socket_id();
                        if let Some(listener) = process.tcp_listeners.get_mut(listener_id) {
                            listener.register_connection(&socket_id);
                        }
                        process.tcp_sockets.insert(socket_id.clone(), socket);
                        javascript_net_json_string(
                            json!({
                                "socketId": socket_id,
                                "info": info,
                            }),
                            "net.server_accept",
                        )
                    }
                    Some(JavascriptTcpListenerEvent::Error { code, message }) => {
                        let detail = code.unwrap_or_else(|| String::from("server accept"));
                        Err(SidecarError::Execution(format!("{detail}: {message}")))
                    }
                    None => Ok(javascript_net_timeout_value()),
                };
            }

            let listener = process.unix_listeners.get_mut(listener_id).ok_or_else(|| {
                SidecarError::InvalidState(format!("unknown net listener {listener_id}"))
            })?;
            match listener.poll(Duration::ZERO)? {
                Some(JavascriptUnixListenerEvent::Connection(pending)) => {
                    check_network_resource_limit(
                        resource_limits.max_sockets,
                        network_counts.sockets,
                        1,
                        "socket",
                    )?;
                    check_network_resource_limit(
                        resource_limits.max_connections,
                        network_counts.connections,
                        1,
                        "connection",
                    )?;
                    let info = json!({
                        "localPath": pending.local_path.clone(),
                        "remotePath": pending.remote_path.clone(),
                    });
                    let socket = ActiveUnixSocket::from_stream(
                        pending.stream,
                        Some(listener_id.to_string()),
                        pending.local_path,
                        pending.remote_path,
                    )?;
                    let socket_id = process.allocate_unix_socket_id();
                    if let Some(listener) = process.unix_listeners.get_mut(listener_id) {
                        listener.register_connection(&socket_id);
                    }
                    process.unix_sockets.insert(socket_id.clone(), socket);
                    javascript_net_json_string(
                        json!({
                            "socketId": socket_id,
                            "info": info,
                        }),
                        "net.server_accept",
                    )
                }
                Some(JavascriptUnixListenerEvent::Error { code, message }) => {
                    let detail = code.unwrap_or_else(|| String::from("server accept"));
                    Err(SidecarError::Execution(format!("{detail}: {message}")))
                }
                None => Ok(javascript_net_timeout_value()),
            }
        }
        "net.server_connections" => {
            let listener_id = javascript_sync_rpc_arg_str(
                &request.args,
                0,
                "net.server_connections listener id",
            )?;
            if let Some(listener) = process.tcp_listeners.get(listener_id) {
                Ok(json!(listener.active_connection_count()))
            } else {
                let listener = process.unix_listeners.get(listener_id).ok_or_else(|| {
                    SidecarError::InvalidState(format!("unknown net listener {listener_id}"))
                })?;
                Ok(json!(listener.active_connection_count()))
            }
        }
        "net.upgrade_socket_write" => {
            let socket_id = javascript_sync_rpc_arg_str(
                &request.args,
                0,
                "net.upgrade_socket_write socket id",
            )?;
            let chunk =
                javascript_sync_rpc_base64_arg(&request.args, 1, "net.upgrade_socket_write chunk")?;
            let socket = process.tcp_sockets.get(socket_id).ok_or_else(|| {
                SidecarError::InvalidState(format!("unknown TCP socket {socket_id}"))
            })?;
            socket
                .write_all(kernel, process.kernel_pid, &chunk)
                .map(|written| json!(written))
        }
        "net.upgrade_socket_end" => {
            let socket_id =
                javascript_sync_rpc_arg_str(&request.args, 0, "net.upgrade_socket_end socket id")?;
            let socket = process.tcp_sockets.get(socket_id).ok_or_else(|| {
                SidecarError::InvalidState(format!("unknown TCP socket {socket_id}"))
            })?;
            socket.shutdown_write(kernel, process.kernel_pid)?;
            Ok(Value::Null)
        }
        "net.upgrade_socket_destroy" => {
            let socket_id = javascript_sync_rpc_arg_str(
                &request.args,
                0,
                "net.upgrade_socket_destroy socket id",
            )?;
            let socket = process.tcp_sockets.remove(socket_id).ok_or_else(|| {
                SidecarError::InvalidState(format!("unknown TCP socket {socket_id}"))
            })?;
            if let Some(listener_id) = socket.listener_id.as_deref() {
                if let Some(listener) = process.tcp_listeners.get_mut(listener_id) {
                    listener.release_connection(socket_id);
                }
            }
            let _ = socket.close(kernel, process.kernel_pid);
            Ok(Value::Null)
        }
        "net.write" => {
            let socket_id = javascript_sync_rpc_arg_str(&request.args, 0, "net.write socket id")?;
            let chunk = javascript_sync_rpc_bytes_arg(&request.args, 1, "net.write chunk")?;
            if let Some(socket) = process.tcp_sockets.get(socket_id) {
                socket
                    .write_all(kernel, process.kernel_pid, &chunk)
                    .map(|written| json!(written))
            } else {
                let socket = process.unix_sockets.get(socket_id).ok_or_else(|| {
                    SidecarError::InvalidState(format!("unknown net socket {socket_id}"))
                })?;
                socket.write_all(&chunk).map(|written| json!(written))
            }
        }
        "net.shutdown" => {
            let socket_id =
                javascript_sync_rpc_arg_str(&request.args, 0, "net.shutdown socket id")?;
            if let Some(socket) = process.tcp_sockets.get(socket_id) {
                socket.shutdown_write(kernel, process.kernel_pid)?;
            } else {
                let socket = process.unix_sockets.get(socket_id).ok_or_else(|| {
                    SidecarError::InvalidState(format!("unknown net socket {socket_id}"))
                })?;
                socket.shutdown_write()?;
            }
            Ok(Value::Null)
        }
        "net.destroy" => {
            let socket_id = javascript_sync_rpc_arg_str(&request.args, 0, "net.destroy socket id")?;
            if let Some(socket) = process.tcp_sockets.remove(socket_id) {
                if let Some(listener_id) = socket.listener_id.as_deref() {
                    if let Some(listener) = process.tcp_listeners.get_mut(listener_id) {
                        listener.release_connection(socket_id);
                    }
                }
                let _ = socket.close(kernel, process.kernel_pid);
                Ok(Value::Null)
            } else {
                let socket = process.unix_sockets.remove(socket_id).ok_or_else(|| {
                    SidecarError::InvalidState(format!("unknown net socket {socket_id}"))
                })?;
                if let Some(listener_id) = socket.listener_id.as_deref() {
                    if let Some(listener) = process.unix_listeners.get_mut(listener_id) {
                        listener.release_connection(socket_id);
                    }
                }
                let _ = socket.close();
                Ok(Value::Null)
            }
        }
        "net.server_close" => {
            let listener_id =
                javascript_sync_rpc_arg_str(&request.args, 0, "net.server_close listener id")?;
            if let Some(listener) = process.tcp_listeners.remove(listener_id) {
                listener.close(kernel, process.kernel_pid)?;
                Ok(Value::Null)
            } else {
                let listener = process.unix_listeners.remove(listener_id).ok_or_else(|| {
                    SidecarError::InvalidState(format!("unknown net listener {listener_id}"))
                })?;
                listener.close()?;
                Ok(Value::Null)
            }
        }
        "tls.get_ciphers" => javascript_net_json_string(
            Value::Array(
                tls_provider()
                    .cipher_suites
                    .iter()
                    .filter_map(|suite| {
                        suite
                            .suite()
                            .as_str()
                            .map(|value| Value::String(value.to_owned()))
                    })
                    .collect(),
            ),
            "tls.get_ciphers",
        ),
        _ => Err(SidecarError::InvalidState(format!(
            "unsupported JavaScript net sync RPC method {}",
            request.method
        ))),
    }
}

fn signal_name_for_stream_event(signal: i32) -> Option<&'static str> {
    match signal {
        libc::SIGHUP => Some("SIGHUP"),
        libc::SIGINT => Some("SIGINT"),
        libc::SIGUSR1 => Some("SIGUSR1"),
        libc::SIGALRM => Some("SIGALRM"),
        libc::SIGCONT => Some("SIGCONT"),
        libc::SIGTERM => Some("SIGTERM"),
        libc::SIGCHLD => Some("SIGCHLD"),
        libc::SIGWINCH => Some("SIGWINCH"),
        _ => None,
    }
}

pub(crate) fn canonical_signal_name(signal: i32) -> Option<&'static str> {
    match signal {
        1 => Some("SIGHUP"),
        2 => Some("SIGINT"),
        3 => Some("SIGQUIT"),
        4 => Some("SIGILL"),
        5 => Some("SIGTRAP"),
        6 => Some("SIGABRT"),
        7 => Some("SIGBUS"),
        8 => Some("SIGFPE"),
        9 => Some("SIGKILL"),
        10 => Some("SIGUSR1"),
        11 => Some("SIGSEGV"),
        12 => Some("SIGUSR2"),
        13 => Some("SIGPIPE"),
        14 => Some("SIGALRM"),
        15 => Some("SIGTERM"),
        17 => Some("SIGCHLD"),
        18 => Some("SIGCONT"),
        19 => Some("SIGSTOP"),
        20 => Some("SIGTSTP"),
        21 => Some("SIGTTIN"),
        22 => Some("SIGTTOU"),
        23 => Some("SIGURG"),
        24 => Some("SIGXCPU"),
        25 => Some("SIGXFSZ"),
        26 => Some("SIGVTALRM"),
        27 => Some("SIGPROF"),
        28 => Some("SIGWINCH"),
        29 => Some("SIGIO"),
        30 => Some("SIGPWR"),
        31 => Some("SIGSYS"),
        _ => None,
    }
}

fn dispatch_v8_process_signal(process: &ActiveProcess, signal: i32) -> Result<bool, SidecarError> {
    let Some(signal_name) = signal_name_for_stream_event(signal) else {
        return Ok(false);
    };
    process.execution.send_javascript_stream_event(
        "signal",
        json!({
            "signal": signal_name,
            "number": signal,
            "action": "default",
        }),
    )?;
    Ok(true)
}

fn dispatch_v8_session_signal_async(session: V8SessionHandle, signal: i32) {
    let Some(signal_name) = signal_name_for_stream_event(signal).map(str::to_owned) else {
        return;
    };
    thread::spawn(move || {
        thread::sleep(Duration::from_millis(1));
        let payload = v8_runtime::json_to_cbor_payload(&json!({
            "signal": signal_name,
            "number": signal,
            "action": "default",
        }))
        .unwrap_or_default();
        let _ = session.send_stream_event("signal", payload);
    });
}

pub(crate) fn parse_signal(signal: &str) -> Result<i32, SidecarError> {
    let trimmed = signal.trim();
    if trimmed.is_empty() {
        return Err(SidecarError::InvalidState(String::from(
            "kill_process requires a non-empty signal",
        )));
    }

    if let Ok(value) = trimmed.parse::<i32>() {
        return match value {
            0..=31 => Ok(value),
            _ => Err(SidecarError::InvalidState(format!(
                "unsupported kill_process signal {signal}"
            ))),
        };
    }

    let upper = trimmed.to_ascii_uppercase();
    let normalized = upper.strip_prefix("SIG").unwrap_or(&upper);

    signal_number_from_name(normalized).ok_or_else(|| {
        SidecarError::InvalidState(format!("unsupported kill_process signal {signal}"))
    })
}

fn signal_number_from_name(signal: &str) -> Option<i32> {
    match signal {
        "0" => Some(0),
        "HUP" => Some(1),
        "INT" => Some(2),
        "QUIT" => Some(3),
        "ILL" => Some(4),
        "TRAP" => Some(5),
        "ABRT" | "IOT" => Some(6),
        "BUS" => Some(7),
        "FPE" => Some(8),
        "KILL" => Some(9),
        "USR1" => Some(10),
        "SEGV" => Some(11),
        "USR2" => Some(12),
        "PIPE" => Some(13),
        "ALRM" => Some(14),
        "TERM" => Some(15),
        "STKFLT" => Some(16),
        "CHLD" => Some(17),
        "CONT" => Some(18),
        "STOP" => Some(19),
        "TSTP" => Some(20),
        "TTIN" => Some(21),
        "TTOU" => Some(22),
        "URG" => Some(23),
        "XCPU" => Some(24),
        "XFSZ" => Some(25),
        "VTALRM" => Some(26),
        "PROF" => Some(27),
        "WINCH" => Some(28),
        "IO" | "POLL" => Some(29),
        "PWR" => Some(30),
        "SYS" => Some(31),
        _ => None,
    }
}

pub(crate) fn runtime_child_is_alive(child_pid: u32) -> Result<bool, SidecarError> {
    Ok(runtime_child_exit_status(child_pid)?.is_none())
}

#[cfg(not(target_os = "macos"))]
fn runtime_child_exit_status(child_pid: u32) -> Result<Option<i32>, SidecarError> {
    if child_pid == 0 {
        return Ok(Some(0));
    }

    let wait_flags = WaitPidFlag::WNOHANG
        | WaitPidFlag::WNOWAIT
        | WaitPidFlag::WEXITED
        | WaitPidFlag::WUNTRACED
        | WaitPidFlag::WCONTINUED;
    match wait_on_child(WaitId::Pid(Pid::from_raw(child_pid as i32)), wait_flags) {
        Ok(WaitStatus::StillAlive)
        | Ok(WaitStatus::Stopped(_, _))
        | Ok(WaitStatus::Continued(_)) => Ok(None),
        Ok(WaitStatus::Exited(_, status)) => Ok(Some(status)),
        Ok(WaitStatus::Signaled(_, signal, _)) => Ok(Some(128 + signal as i32)),
        #[cfg(any(target_os = "linux", target_os = "android"))]
        Ok(WaitStatus::PtraceEvent(_, _, _) | WaitStatus::PtraceSyscall(_)) => Ok(None),
        Err(nix::errno::Errno::ECHILD) => Ok(Some(0)),
        Err(error) => Err(SidecarError::Execution(format!(
            "failed to inspect guest runtime process {child_pid}: {error}"
        ))),
    }
}

// macOS nix exposes no `waitid`/`WNOWAIT`, so we poll with `waitpid(WNOHANG)`.
// NOTE: unlike Linux's `waitid(WNOWAIT)`, `waitpid` REAPS an exited child rather
// than leaving it waitable. That is correct for this poll (the sidecar is the
// reaping parent), but a second status query after exit returns ECHILD → treated
// as "exited(0)" below.
#[cfg(target_os = "macos")]
fn runtime_child_exit_status(child_pid: u32) -> Result<Option<i32>, SidecarError> {
    if child_pid == 0 {
        return Ok(Some(0));
    }

    match waitpid(Pid::from_raw(child_pid as i32), Some(WaitPidFlag::WNOHANG)) {
        Ok(WaitStatus::StillAlive)
        | Ok(WaitStatus::Stopped(_, _))
        | Ok(WaitStatus::Continued(_)) => Ok(None),
        Ok(WaitStatus::Exited(_, status)) => Ok(Some(status)),
        Ok(WaitStatus::Signaled(_, signal, _)) => Ok(Some(128 + signal as i32)),
        Err(nix::errno::Errno::ECHILD) => Ok(Some(0)),
        Err(error) => Err(SidecarError::Execution(format!(
            "failed to inspect guest runtime process {child_pid}: {error}"
        ))),
    }
}

pub(crate) fn signal_runtime_process(child_pid: u32, signal: i32) -> Result<(), SidecarError> {
    if child_pid == 0 {
        return Ok(());
    }

    if !runtime_child_is_alive(child_pid)? {
        return Ok(());
    }

    if signal == 0 {
        return Ok(());
    }

    let parsed = Signal::try_from(signal).map_err(|_| {
        SidecarError::InvalidState(format!("unsupported kill_process signal {signal}"))
    })?;
    let result = send_signal(Pid::from_raw(child_pid as i32), Some(parsed));

    match result {
        Ok(()) => Ok(()),
        Err(nix::errno::Errno::ESRCH) => Ok(()),
        Err(error) => Err(SidecarError::Execution(format!(
            "failed to signal guest runtime process {child_pid}: {error}"
        ))),
    }
}

pub(crate) fn error_code(error: &SidecarError) -> &'static str {
    match error {
        SidecarError::InvalidState(_) => "invalid_state",
        SidecarError::ProtocolVersionMismatch(_) => "protocol_version_mismatch",
        SidecarError::BridgeVersionMismatch(_) => "bridge_version_mismatch",
        SidecarError::Conflict(_) => "conflict",
        SidecarError::Unauthorized(_) => "unauthorized",
        SidecarError::Unsupported(_) => "unsupported",
        SidecarError::FrameTooLarge(_) => "frame_too_large",
        SidecarError::Kernel(_) => "kernel_error",
        SidecarError::Plugin(_) => "plugin_error",
        SidecarError::Execution(_) => "execution_error",
        SidecarError::Bridge(_) => "bridge_error",
        SidecarError::Io(_) => "io_error",
    }
}

fn guest_errno_code(message: &str) -> Option<&str> {
    const TRUSTED_PREFIXES: &[&str] = &[
        "ERR_AGENTOS_NODE_SYNC_RPC",
        "ERR_AGENTOS_PYTHON_VFS_RPC",
        "ERR_AGENTOS_BRIDGE",
    ];

    let mut segments = message.split(':').map(str::trim);
    let first = segments.next()?;
    if is_guest_errno_segment(first) {
        return Some(first);
    }

    if TRUSTED_PREFIXES.contains(&first) {
        let second = segments.next()?;
        if is_guest_errno_segment(second) {
            return Some(second);
        }
    }

    None
}

fn is_guest_errno_segment(segment: &str) -> bool {
    segment.len() >= 2
        && segment.starts_with('E')
        && !segment.starts_with("ERR_")
        && segment[1..]
            .bytes()
            .all(|byte| byte.is_ascii_uppercase() || byte.is_ascii_digit() || byte == b'_')
}

pub(crate) fn javascript_sync_rpc_error_code(error: &SidecarError) -> String {
    let message = error.to_string();
    if let Some(code) = guest_errno_code(&message) {
        return code.to_owned();
    }
    if message.starts_with("ERR_NATIVE_BINARY_NOT_SUPPORTED:") {
        return String::from("ERR_NATIVE_BINARY_NOT_SUPPORTED");
    }

    let lower = message.to_ascii_lowercase();
    if lower.contains("no such file or directory")
        || lower.contains("entry not found")
        || lower.contains("not found")
    {
        return String::from("ENOENT");
    }
    if lower.contains("permission denied") {
        return String::from("EACCES");
    }
    if lower.contains("already exists")
        || lower.contains("already registered")
        || lower.contains("file exists")
    {
        return String::from("EEXIST");
    }
    if lower.contains("invalid argument") {
        return String::from("EINVAL");
    }

    String::from("ERR_AGENTOS_NODE_SYNC_RPC")
}

pub(crate) fn ignore_stale_javascript_sync_rpc_response(
    error: SidecarError,
) -> Result<(), SidecarError> {
    match error {
        SidecarError::Execution(message)
            if message.ends_with("is no longer pending")
                && message.starts_with("sync RPC request ") =>
        {
            Ok(())
        }
        SidecarError::Execution(message) => {
            let lower = message.to_ascii_lowercase();
            if lower.contains("sync rpc response")
                && (lower.contains("broken pipe") || lower.contains("channel closed unexpectedly"))
            {
                Ok(())
            } else {
                Err(SidecarError::Execution(message))
            }
        }
        other => Err(other),
    }
}

#[cfg(test)]
mod error_code_tests {
    use super::{guest_errno_code, javascript_sync_rpc_error_code, SidecarError};

    #[test]
    fn guest_errno_code_rejects_guest_controlled_errno_segments() {
        assert_eq!(guest_errno_code("user said 'EACCES: denied'"), None);
        assert_eq!(
            guest_errno_code("prefix: user said 'EPERM': more text"),
            None
        );
        assert_eq!(guest_errno_code("ERR_AGENTOS_FAKE: EACCES: denied"), None);
    }

    #[test]
    fn guest_errno_code_accepts_trusted_secure_exec_prefixes() {
        assert_eq!(
            guest_errno_code("ERR_AGENTOS_NODE_SYNC_RPC: EACCES: permission denied on /foo"),
            Some("EACCES")
        );
        assert_eq!(
            guest_errno_code("ERR_AGENTOS_PYTHON_VFS_RPC: ENOENT: missing file"),
            Some("ENOENT")
        );
        assert_eq!(guest_errno_code("EEXIST: already exists"), Some("EEXIST"));
    }

    #[test]
    fn javascript_sync_rpc_error_code_ignores_spoofed_errnos() {
        let error = SidecarError::Execution(String::from("user said 'EACCES: denied'"));
        assert_eq!(
            javascript_sync_rpc_error_code(&error),
            "ERR_AGENTOS_NODE_SYNC_RPC"
        );
    }

    #[test]
    fn javascript_sync_rpc_error_code_preserves_real_sidecar_errnos() {
        let error = SidecarError::Execution(String::from(
            "ERR_AGENTOS_NODE_SYNC_RPC: EACCES: permission denied on /foo",
        ));
        assert_eq!(javascript_sync_rpc_error_code(&error), "EACCES");
    }

    #[test]
    fn javascript_sync_rpc_error_code_maps_file_exists_messages() {
        let error = SidecarError::Io(String::from(
            "failed to create mapped guest directory /.next/server: File exists (os error 17)",
        ));
        assert_eq!(javascript_sync_rpc_error_code(&error), "EEXIST");
    }

    #[test]
    fn javascript_sync_rpc_error_code_preserves_native_binary_rejections() {
        let error = SidecarError::Execution(String::from(
            "ERR_NATIVE_BINARY_NOT_SUPPORTED: refused to execute native ELF guest binary at /tmp/fake-rg inside the VM",
        ));
        assert_eq!(
            javascript_sync_rpc_error_code(&error),
            "ERR_NATIVE_BINARY_NOT_SUPPORTED"
        );
    }
}
#[cfg(test)]
mod ssrf_egress_classifier_tests {
    // F-005/006/007 (sec-sidecar T1/T7/T11): the egress classifier must treat the
    // unspecified address (0.0.0.0 / ::), CGNAT (100.64.0.0/10), IPv6 spellings of
    // restricted IPv4 targets (::a.b.c.d), and reserved/multicast (240/4, 224/4) as
    // restricted. 0.0.0.0 routes to 127.0.0.1 on connect(), so leaving it
    // unclassified let a guest bypass the loopback port-ownership gate.
    //
    // These are bounded SAFEGUARD tests: they exercise the classifier and the DNS
    // egress filter directly (no network I/O, no Node), so they run fast and
    // deterministically. See FAILURES.md#F-005, #F-006, #F-007.
    use super::{
        filter_dns_safe_ip_addrs, is_loopback_ip, restricted_non_loopback_ip_range, SidecarError,
    };
    use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};

    fn assert_restricted(ip: IpAddr, expected_label: &str) {
        let classification = restricted_non_loopback_ip_range(ip);
        assert!(
            classification.is_some(),
            "{ip} must be classified as a restricted egress target"
        );
        let (_cidr, label) = classification.unwrap();
        assert_eq!(
            label, expected_label,
            "{ip} should be labelled {expected_label}, got {label}"
        );
    }

    fn assert_dns_denied(ip: IpAddr, label: &str) {
        match filter_dns_safe_ip_addrs(vec![ip], "attacker.example") {
            Err(SidecarError::Execution(message)) => assert!(
                message.starts_with("EACCES:"),
                "{label}: egress filter must deny with EACCES, got: {message}"
            ),
            other => panic!("{label}: expected EACCES denial, got {other:?}"),
        }
    }

    // F-005 (sec-sidecar T1).
    #[test]
    fn classifier_denies_unspecified_and_cgnat_targets() {
        // 0.0.0.0 (IPv4 unspecified) -> would route to host loopback.
        assert_restricted(IpAddr::V4(Ipv4Addr::UNSPECIFIED), "unspecified");
        // :: (IPv6 unspecified).
        assert_restricted(IpAddr::V6(Ipv6Addr::UNSPECIFIED), "unspecified");

        // CGNAT 100.64.0.0/10 spans 100.64.x.x .. 100.127.x.x.
        assert_restricted(
            IpAddr::V4(Ipv4Addr::new(100, 64, 0, 1)),
            "carrier-grade-nat",
        );
        assert_restricted(
            IpAddr::V4(Ipv4Addr::new(100, 127, 255, 254)),
            "carrier-grade-nat",
        );

        // Guard against over-blocking: addresses just outside 100.64/10 stay allowed.
        assert!(
            restricted_non_loopback_ip_range(IpAddr::V4(Ipv4Addr::new(100, 63, 255, 255)))
                .is_none(),
            "100.63.255.255 is outside CGNAT and must remain allowed"
        );
        assert!(
            restricted_non_loopback_ip_range(IpAddr::V4(Ipv4Addr::new(100, 128, 0, 0))).is_none(),
            "100.128.0.0 is outside CGNAT and must remain allowed"
        );

        // The DNS egress filter must also deny these via EACCES.
        assert_dns_denied(IpAddr::V4(Ipv4Addr::UNSPECIFIED), "0.0.0.0 (unspecified)");
        assert_dns_denied(IpAddr::V6(Ipv6Addr::UNSPECIFIED), ":: (unspecified)");
        assert_dns_denied(
            IpAddr::V4(Ipv4Addr::new(100, 64, 0, 1)),
            "100.64.0.1 (CGNAT)",
        );
    }

    // F-006 (sec-sidecar T7).
    #[test]
    fn classifier_denies_ipv6_spelled_metadata_addresses() {
        // The IPv4-mapped form (::ffff:169.254.169.254) was already handled; the
        // IPv4-compatible form (::169.254.169.254) is the gap this fixes.
        let mapped = "::ffff:169.254.169.254".parse::<Ipv6Addr>().unwrap();
        assert_restricted(IpAddr::V6(mapped), "link-local");

        let compat = "::169.254.169.254".parse::<Ipv6Addr>().unwrap();
        assert_restricted(IpAddr::V6(compat), "link-local");

        // Other IPv4-compatible private/CGNAT spellings must also be canonicalized.
        assert_restricted(
            IpAddr::V6("::10.0.0.1".parse::<Ipv6Addr>().unwrap()),
            "private",
        );
        assert_restricted(
            IpAddr::V6("::100.64.0.1".parse::<Ipv6Addr>().unwrap()),
            "carrier-grade-nat",
        );

        // Guard against over-blocking: the IPv6 unspecified/loopback addresses
        // are not IPv4-compatible host targets, and a public IPv4-compatible
        // address must remain allowed.
        assert_eq!(
            restricted_non_loopback_ip_range(IpAddr::V6(Ipv6Addr::UNSPECIFIED)),
            Some(("::/128", "unspecified")),
            ":: must classify as unspecified, not via the IPv4-compat path"
        );
        assert!(
            restricted_non_loopback_ip_range(IpAddr::V6(Ipv6Addr::LOCALHOST)).is_none()
                || is_loopback_ip(IpAddr::V6(Ipv6Addr::LOCALHOST)),
            "::1 must not be classified as a restricted IPv4-compatible target"
        );
        assert!(
            restricted_non_loopback_ip_range(IpAddr::V6("::8.8.8.8".parse::<Ipv6Addr>().unwrap()))
                .is_none(),
            "::8.8.8.8 (public IPv4-compatible) must remain allowed"
        );

        // The DNS egress filter must deny the IPv4-compat metadata spelling.
        assert_dns_denied(
            IpAddr::V6("::169.254.169.254".parse::<Ipv6Addr>().unwrap()),
            "::169.254.169.254 (IPv4-compat metadata)",
        );
    }

    // F-007 (sec-sidecar T11).
    #[test]
    fn classifier_denies_reserved_and_multicast_targets() {
        // 224.0.0.0/4 (multicast) and 240.0.0.0/4 (reserved / future use) are not
        // legitimate unicast egress targets; a guest connect to them must be
        // classified as restricted and denied.
        assert_restricted(IpAddr::V4(Ipv4Addr::new(224, 0, 0, 1)), "multicast");
        assert_restricted(IpAddr::V4(Ipv4Addr::new(239, 255, 255, 255)), "multicast");
        assert_restricted(IpAddr::V4(Ipv4Addr::new(240, 0, 0, 1)), "reserved");
        // 255.255.255.255 (limited broadcast) falls in 240.0.0.0/4.
        assert_restricted(IpAddr::V4(Ipv4Addr::BROADCAST), "reserved");

        // IPv4-compatible IPv6 spellings must canonicalize and be denied too.
        assert_restricted(
            IpAddr::V6("::224.0.0.1".parse::<Ipv6Addr>().unwrap()),
            "multicast",
        );
        assert_restricted(
            IpAddr::V6("::240.0.0.1".parse::<Ipv6Addr>().unwrap()),
            "reserved",
        );

        // Guard against over-blocking: addresses just outside 224/4 stay allowed.
        assert!(
            restricted_non_loopback_ip_range(IpAddr::V4(Ipv4Addr::new(223, 255, 255, 255)))
                .is_none(),
            "223.255.255.255 is outside 224/4 and must remain allowed"
        );

        // The DNS egress filter must also deny these via EACCES.
        assert_dns_denied(
            IpAddr::V4(Ipv4Addr::new(240, 0, 0, 1)),
            "240.0.0.1 (reserved)",
        );
        assert_dns_denied(
            IpAddr::V4(Ipv4Addr::new(224, 0, 0, 1)),
            "224.0.0.1 (multicast)",
        );
    }
}

/// Adversarial coverage for the DNS-rebinding gap (VECTORS.md D.3) on the
/// Python/Pyodide `httpRequestSync` outbound HTTP path. The egress range guard
/// (`filter_dns_safe_ip_addrs`) runs at resolution time, but `ureq` performs its
/// own DNS resolution for the TCP/TLS connect, so a rebinding DNS server could
/// previously make the second lookup land on a private/link-local/metadata IP
/// the first check rejected. The fix pins `ureq`'s resolver to the vetted
/// address set; these tests prove the connect is pinned and refuses any other
/// host or an empty (fully-rejected) address set.
#[cfg(test)]
mod dns_rebinding_pin_tests {
    use super::{issue_outbound_http_request, split_netloc, JavascriptHttpRequestOptions};
    use std::collections::BTreeMap;
    use std::io::{Read, Write};
    use std::net::{IpAddr, Ipv4Addr, TcpListener};
    use std::thread;
    use url::Url;

    fn empty_headers() -> super::HttpHeaderCollection {
        super::parse_http_header_collection(&BTreeMap::new(), "test headers")
            .expect("empty header collection")
    }

    fn options() -> JavascriptHttpRequestOptions {
        JavascriptHttpRequestOptions {
            method: Some(String::from("GET")),
            headers: BTreeMap::new(),
            body: None,
            reject_unauthorized: None,
        }
    }

    #[test]
    fn split_netloc_handles_hostnames_and_bracketed_ipv6() {
        assert_eq!(
            split_netloc("attacker.example:80"),
            Some(("attacker.example", 80))
        );
        assert_eq!(split_netloc("[::1]:443"), Some(("::1", 443)));
        assert_eq!(split_netloc("10.0.0.1:8080"), Some(("10.0.0.1", 8080)));
        assert_eq!(split_netloc("no-port"), None);
        assert_eq!(split_netloc("host:notaport"), None);
    }

    /// A loopback HTTP server stands in for the egress-vetted target. The
    /// request URL uses a *different* hostname (`attacker.example`) whose real
    /// DNS would resolve elsewhere; pinning forces the connect onto the vetted
    /// IP only. If the resolver were unpinned, the request would fail to reach
    /// this server (and on a real host could land on a private/metadata IP).
    #[test]
    fn outbound_http_connect_is_pinned_to_vetted_ip() {
        let listener = TcpListener::bind((Ipv4Addr::LOCALHOST, 0)).expect("bind loopback server");
        let port = listener.local_addr().expect("local addr").port();
        let server = thread::spawn(move || {
            let (mut stream, _) = listener.accept().expect("accept");
            let mut buf = [0u8; 1024];
            let _ = stream.read(&mut buf);
            stream
                .write_all(b"HTTP/1.1 200 OK\r\nContent-Length: 2\r\n\r\nhi")
                .expect("write response");
            let _ = stream.flush();
        });

        let url = Url::parse(&format!("http://attacker.example:{port}/")).expect("url");
        let pinned = vec![IpAddr::V4(Ipv4Addr::LOCALHOST)];
        let result = issue_outbound_http_request(&url, &options(), &empty_headers(), &pinned)
            .expect("pinned request should reach the vetted loopback target");
        let payload = result.as_str().expect("string payload");
        assert!(
            payload.contains("\"status\":200"),
            "expected 200 from pinned target, got: {payload}"
        );
        server.join().expect("server thread");
    }

    /// With no vetted address (every resolved IP was rejected by the range
    /// guard, or the literal IP was a blocked range), the pinned resolver must
    /// refuse rather than fall back to the host resolver.
    #[test]
    fn outbound_http_refuses_when_no_vetted_address() {
        let url = Url::parse("https://attacker.example/").expect("url");
        let error = issue_outbound_http_request(&url, &options(), &empty_headers(), &[])
            .expect_err("empty pinned set must be refused");
        let message = error.to_string();
        assert!(
            message.contains("EACCES") || message.contains("ERR_HTTP_REQUEST_FAILED"),
            "expected an egress refusal, got: {message}"
        );
    }
}
