PythonSandboxClient
Configure the Python subprocess worker for local development, packaged applications, and container sidecars.
PythonSandboxClient
PythonSandboxClient implements the Sandbox interface. It spawns the Python worker, frames requests with a length prefix, and handles injected-callable RPC when Python code calls back into TypeScript.
Local repository
When tests or examples run from the monorepo, the client derives the repository root automatically, prefers .venv/bin/python when present, and prepends the repository root to PYTHONPATH:
import { PythonSandboxClient } from "@maniac-ai/agents/runtime";
const sandbox = new PythonSandboxClient();
await sandbox.run("x = 1 + 1\nprint(x)");Use this mode when Python package sources live beside the TypeScript package.
LocalREPL
LocalREPL is the default execution environment for agent REPL loops:
import { LocalREPL } from "@maniac-ai/agents/runtime";
const repl = new LocalREPL(); // wraps PythonSandboxClient()
const cell = await repl.execute("import json\nprint(json.dumps({'ok': True}))");
console.log(cell.stdout, repl.state.variables);Pass a custom sandbox to new LocalREPL(customSandbox) for tests or alternate worker paths.
LocalREPL also exposes getVar, setVar, inject, and aclose — mirroring the worker's namespace and injection surface.
Packaged applications
When the worker ships outside the repository layout, pass both the interpreter and the import path that contains runtime.adapters._subprocess_worker:
import { PythonSandboxClient } from "@maniac-ai/agents/runtime";
const sandbox = new PythonSandboxClient({
pythonExecutable: "/opt/maniac-worker/.venv/bin/python",
pythonPath: ["/opt/maniac-worker"]
});If installed under node_modules, auto-detection fails with an explicit error — always pass pythonExecutable and pythonPath in production bundles.
Sidecar or process image
For containers, deploy the Python worker as a sidecar or shared process image:
const sandbox = new PythonSandboxClient({
pythonExecutable: "/usr/local/bin/python",
pythonPath: ["/app/python"],
cwd: "/app/typescript/packages/agents",
env: { PYTHONUNBUFFERED: "1" }
});cwd is only used to derive the default repository root when pythonPath is not enough. Prefer explicit pythonExecutable and pythonPath in production.
Client options
| Option | Purpose |
|---|---|
pythonExecutable | Path to the Python interpreter |
pythonPath | Directories prepended to PYTHONPATH (joined with path.delimiter) |
cwd | Working directory for the spawned process |
env | Extra environment variables merged into the child process |
onSessionError | Callback when framing desync or unrecoverable reader errors terminate the session |
Injected callables
injectCallable(name, fn) registers a TypeScript function the worker can invoke from Python via await name(...). The runner uses this to expose tool handlers and the memory RPC to REPL cells.
Callable arguments and return values must be JSON-serializable. The worker rejects non-serializable values at the boundary.
Agent integration
Set repl on your agent spec:
const result = await runAgent(
{
id: "analyst",
instructions: "Use python_exec for data work.",
model,
repl: { namespace_extras: { app_name: "analyst" } }
},
"Plot the trend."
);runAgent constructs LocalREPL(spec.repl?.sandbox ?? undefined) unless you pass RunOptions.repl explicitly.