jeevesagent.security.sandbox.subprocess_
========================================

.. py:module:: jeevesagent.security.sandbox.subprocess_

.. autoapi-nested-parse::

   Subprocess sandbox: runs each tool call in a child Python process.

   What you get:

   * **Process isolation** — a tool that crashes (segfault, OOM, etc.)
     takes down its own subprocess, not the agent.
   * **Hard timeout** — the parent process kills the child if it
     exceeds ``timeout_seconds``; the call returns
     ``ToolResult.error_(...)`` with a clear timeout message.
   * **Memory boundary** — the child's heap is independent; large
     intermediate values get GC'd by process exit even if the tool
     leaks them.

   What you don't get (yet):

   * Filesystem isolation, network restrictions, or syscall sandboxing.
     For real OS-level isolation, layer this with a Bubblewrap /
     Seatbelt / Docker / gVisor wrapper as Phase 6 follow-up.

   Constraints:

   * The wrapped tool host must be an :class:`InProcessToolHost` because
     we need access to the registered ``Tool.fn`` callable to ship it
     to the child process. MCP / external hosts can't be sandboxed
     this way (they're already a process boundary themselves —
     re-process-isolating them adds nothing).
   * The tool function and its arguments must be **picklable**. That
     means module-level functions (top-level ``def`` in a module);
     closures and locally-defined functions can't cross the process
     boundary. The ``@tool``-decorated functions in your application
     modules are usually fine.

   Cost:

   * Spawning a Python subprocess takes ~100-300ms on most platforms
     (macOS uses ``spawn`` start method which is slower than fork).
     Don't use this for fast tools — the spawn dwarfs the work. It
     pays off for tools that take seconds, can crash, or use a lot of
     memory.



Classes
-------

.. autoapisummary::

   jeevesagent.security.sandbox.subprocess_.SubprocessSandbox


Module Contents
---------------

.. py:class:: SubprocessSandbox(inner: jeevesagent.core.protocols.ToolHost, *, timeout_seconds: float = 30.0)

   Run each tool call in a fresh child Python process.


   .. py:method:: call(tool: str, args: collections.abc.Mapping[str, Any], *, call_id: str = '') -> jeevesagent.core.types.ToolResult
      :async:



   .. py:method:: list_tools(*, query: str | None = None) -> list[jeevesagent.core.types.ToolDef]
      :async:



   .. py:method:: watch() -> collections.abc.AsyncIterator[jeevesagent.core.types.ToolEvent]
      :async:



   .. py:property:: inner
      :type: jeevesagent.core.protocols.ToolHost



   .. py:property:: timeout_seconds
      :type: float



