jeevesagent.security.sandbox.subprocess¶
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 returnsToolResult.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
InProcessToolHostbecause we need access to the registeredTool.fncallable 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
defin 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
spawnstart 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¶
Run each tool call in a fresh child Python process. |
Module Contents¶
- class jeevesagent.security.sandbox.subprocess_.SubprocessSandbox(inner: jeevesagent.core.protocols.ToolHost, *, timeout_seconds: float = 30.0)[source]¶
Run each tool call in a fresh child Python process.
- async call(tool: str, args: collections.abc.Mapping[str, Any], *, call_id: str = '') jeevesagent.core.types.ToolResult[source]¶
- property inner: jeevesagent.core.protocols.ToolHost¶