Python API reference
Auto-generated reference for the bashkit PyPI package, covering the public classes and functions exported from bashkit. Reflects the latest published release.
Install with
pip install bashkit. See the Embedding guide and LLM tools guide for task-oriented walkthroughs.
Bash
Core bash interpreter with virtual filesystem.
State persists between calls — files created in one execute() are available in subsequent calls.
Example (basic):
>>> bash = Bash()
>>> result = await bash.execute("echo 'Hello!'")
>>> print(result.stdout)
Hello!
Example (Python execution with external function handler):
>>> async def handler(fn_name: str, args: list, kwargs: dict) -> Any:
... return await tool_executor.call(fn_name, kwargs)
>>> bash = Bash(
... python=True,
... external_functions=["api_request"],
... external_handler=handler,
... )
>>> result = await bash.execute("python3 -c 'print(api_request())'")
Constructor
Bash(username: str | None = None, hostname: str | None = None, cwd: str | None = None, env: Mapping[str, str] | None = None, max_commands: int | None = None, max_loop_iterations: int | None = None, max_memory: int | None = None, timeout_seconds: float | None = None, python: bool = False, sqlite: bool = False, external_functions: list[str] | None = None, external_handler: ExternalHandler | None = None, files: dict[str, str | Callable[[], str]] | None = None, mounts: list[dict[str, Any]] | None = None, allowed_mount_paths: list[str] | None = None, readonly_filesystem: bool = False, custom_builtins: Mapping[str, BuiltinCallback] | None = None, network: NetworkConfig | None = None) -> None
Create a new Bash interpreter.
Parameters:
username— Custom username (default"user").hostname— Custom hostname (default"bashkit").cwd— Initial working directory for the shell. Sets the starting directory directly instead of running a leadingcd.env— Initial environment variables applied before execution, so scripts see them without anexportprelude.max_commands— Limit total commands executed.max_loop_iterations— Limit iterations per loop.max_memory— Memory limit in bytes for the VFS.timeout_seconds— Abort execution after this duration.python— Enable embedded Python (python3builtin).sqlite— Enable embedded SQLite (sqlite/sqlite3builtin). Defaults toFalse. WhenTrue, the Turso-backed engine is registered andBASHKIT_ALLOW_INPROCESS_SQLITE=1is injected automatically. DefaultSqliteLimitsapply: 4 MiB script cap, 256 MiB DB cap, 30 s wall-clock budget, resource-affecting PRAGMAs (cache_size,mmap_size, …) rejected,ATTACH/DETACHrejected.external_functions— Function names callable from Python code.external_handler— Async callback for external function calls. The callback must not call back into the sameBashinstance via live methods likeread_file(),fs(), orexecute(); those re-entrant calls are rejected.files— Dict mapping VFS paths to file contents or lazy callables.mounts— List of real host directory mount configs.allowed_mount_paths— Host path prefixes allowed for real filesystem mounts. Required when mounting sensitive host locations such as paths under a user home directory.readonly_filesystem— Deny all filesystem mutations after configured files and mounts are applied.custom_builtins— Constructor-time Python callbacks exposed as bash builtins. Each callback receives aBuiltinContextwith rawargvtokens, optional pipelinestdin, and a livefshandle to the virtual filesystem, and must return a stdout string, aBuiltinResult, or await either. Async callbacks run on the caller’s active asyncio loop forawait execute()and on a private loop forexecute_sync().network— Optional outbound HTTP / network configuration. Pass{"allow": [...]}for an explicit allowlist or{"allow_all": True}to allow every URL (mirrorsNetworkAllowlist::allow_all()in the Rust core). Set"block_private_ips": Falseto relax the SSRF guard. Add"credentials": [...]to inject headers transparently for matching URLs and"credential_placeholders": [...]to expose opaque placeholder env vars that are replaced with the real secret on the wire. When omitted, network access is disabled (current default). Preserved acrossreset()andfrom_snapshot()— placeholder env vars are regenerated on each rebuild.
Example:
>>> bash = Bash(
... timeout_seconds=30,
... files={"/input.txt": "some data"},
... custom_builtins={"ping": lambda ctx: "pong\n"},
... network={
... "allow": ["https://api.github.com"],
... "credentials": [
... {
... "pattern": "https://api.github.com",
... "kind": "bearer",
... "token": "ghp_xxx",
... }
... ],
... },
... )
execute
Bash.execute(commands: str, on_output: OutputHandler | None = None) -> ExecResult
Execute bash commands asynchronously.
Parameters:
commands— Bash script to run (likebash -c "commands").on_output— Optional callback receiving chunked(stdout, stderr)pairs during execution. Must be synchronous.
Async custom_builtins callbacks run on the caller’s active asyncio
loop.
Returns: ExecResult with stdout, stderr, exit_code.
Example:
>>> bash = Bash()
>>> result = await bash.execute("echo hello && echo world")
>>> print(result.stdout)
hello
world
execute_sync
Bash.execute_sync(commands: str, on_output: OutputHandler | None = None) -> ExecResult
Execute bash commands synchronously (blocking).
Not supported when external_handler is configured — use
execute() (async) instead. on_output must be synchronous.
Async custom_builtins callbacks run on a private loop here.
When called from inside a running event loop (e.g. Jupyter / IPython),
callbacks are dispatched to a background thread with their own loop so
that asyncio’s “cannot run while another loop is running” restriction
is not triggered.
Example:
>>> bash = Bash()
>>> result = bash.execute_sync("date +%Y")
>>> print(result.exit_code)
0
execute_or_throw
Bash.execute_or_throw(commands: str, on_output: OutputHandler | None = None) -> ExecResult
Execute commands asynchronously; raise BashError on non-zero exit.
on_output must be synchronous.
Example:
>>> bash = Bash()
>>> result = await bash.execute_or_throw("echo ok")
>>> # Raises BashError if the command fails:
>>> await bash.execute_or_throw("false") # doctest: +SKIP
Traceback (most recent call last):
...
BashError: ...
execute_sync_or_throw
Bash.execute_sync_or_throw(commands: str, on_output: OutputHandler | None = None) -> ExecResult
Execute commands synchronously; raise BashError on non-zero exit.
on_output must be synchronous.
Example:
>>> bash = Bash()
>>> result = bash.execute_sync_or_throw("echo ok")
>>> print(result.stdout.strip())
ok
cancel
Bash.cancel() -> None
Cancel the currently running execution.
Safe to call from any thread. Execution aborts at the next command boundary.
Example:
>>> import threading
>>> bash = Bash()
>>> threading.Timer(1.0, bash.cancel).start()
>>> # Long-running command will be cancelled after 1 second
clear_cancel
Bash.clear_cancel() -> None
Clear the cancellation flag so subsequent executions proceed normally.
Call this after a cancel() once the in-flight execution has
finished and you want to reuse the same Bash instance
(preserving VFS state). Without this, every future execute()
will immediately fail with "execution cancelled".
Note: Calling this while an execution is still in-flight may
allow that execution to continue past the cancellation point.
Wait for the cancelled execution to finish before clearing
(await the async call or let execute_sync return).
Example:
>>> bash = Bash()
>>> bash.cancel()
>>> bash.clear_cancel()
>>> result = bash.execute_sync("echo ok")
>>> result.exit_code
0
reset
Bash.reset() -> None
Reset interpreter to initial state.
Clears all VFS contents, environment variables, and shell state.
Re-applies the original files, mounts, and
custom_builtins configuration.
Example:
>>> bash = Bash()
>>> bash.execute_sync("echo hi > /tmp/file.txt")
>>> bash.reset()
>>> result = bash.execute_sync("cat /tmp/file.txt")
>>> result.exit_code # file is gone after reset
1
snapshot
Bash.snapshot(exclude_filesystem: bool = False, exclude_functions: bool = False) -> bytes
Serialize interpreter state to bytes.
snapshot_keyed
Bash.snapshot_keyed(key: bytes, exclude_filesystem: bool = False, exclude_functions: bool = False) -> bytes
Serialize interpreter state to HMAC-protected bytes.
shell_state
Bash.shell_state() -> ShellState
Capture a read-only shell-state snapshot.
restore_snapshot
Bash.restore_snapshot(data: bytes) -> None
Restore interpreter state from bytes produced by snapshot().
restore_snapshot_keyed
Bash.restore_snapshot_keyed(data: bytes, key: bytes) -> None
Restore interpreter state from bytes produced by snapshot_keyed().
from_snapshot
Bash.from_snapshot(data: bytes, username: str | None = None, hostname: str | None = None, cwd: str | None = None, env: Mapping[str, str] | None = None, max_commands: int | None = None, max_loop_iterations: int | None = None, max_memory: int | None = None, timeout_seconds: float | None = None, python: bool = False, sqlite: bool = False, external_functions: list[str] | None = None, external_handler: ExternalHandler | None = None, files: dict[str, str] | None = None, mounts: list[dict[str, Any]] | None = None, allowed_mount_paths: list[str] | None = None, readonly_filesystem: bool = False, custom_builtins: Mapping[str, BuiltinCallback] | None = None, network: NetworkConfig | None = None) -> Bash
Create a new Bash from snapshot bytes and optional constructor kwargs.
from_snapshot_keyed
Bash.from_snapshot_keyed(data: bytes, key: bytes, username: str | None = None, hostname: str | None = None, cwd: str | None = None, env: Mapping[str, str] | None = None, max_commands: int | None = None, max_loop_iterations: int | None = None, max_memory: int | None = None, timeout_seconds: float | None = None, python: bool = False, sqlite: bool = False, external_functions: list[str] | None = None, external_handler: ExternalHandler | None = None, files: dict[str, str] | None = None, mounts: list[dict[str, Any]] | None = None, allowed_mount_paths: list[str] | None = None, readonly_filesystem: bool = False, custom_builtins: Mapping[str, BuiltinCallback] | None = None, network: NetworkConfig | None = None) -> Bash
Create a new Bash from HMAC-protected snapshot bytes.
read_file
Bash.read_file(path: str) -> str
Read a VFS file as UTF-8 text.
write_file
Bash.write_file(path: str, content: str) -> None
Write UTF-8 text into the VFS.
append_file
Bash.append_file(path: str, content: str) -> None
Append UTF-8 text to a VFS file.
mkdir
Bash.mkdir(path: str, recursive: bool = False) -> None
Create a directory in the VFS.
exists
Bash.exists(path: str) -> bool
Return whether a VFS path exists.
remove
Bash.remove(path: str, recursive: bool = False) -> None
Remove a VFS file or directory.
stat
Bash.stat(path: str) -> dict[str, Any]
Return metadata for a VFS path.
chmod
Bash.chmod(path: str, mode: int) -> None
Change VFS permissions for a path.
symlink
Bash.symlink(target: str, link: str) -> None
Create a symlink in the VFS.
read_link
Bash.read_link(path: str) -> str
Return the symlink target for a VFS path.
read_dir
Bash.read_dir(path: str) -> list[dict[str, Any]]
Return directory entries with metadata.
ls
Bash.ls(path: str = '.') -> list[str]
Return entry names for a directory, or an empty list if it is missing.
glob
Bash.glob(pattern: str) -> list[str]
Return file paths matching a safe glob pattern.
fs
Bash.fs() -> FileSystem
Return a live filesystem handle.
Each operation acquires the interpreter lock, so the handle always
reflects the latest state (including after reset()).
Example:
>>> bash = Bash()
>>> bash.execute_sync("echo hello > /greeting.txt")
>>> fs = bash.fs()
>>> fs.read_file("/greeting.txt")
b'hello\n'
mount
Bash.mount(vfs_path: str, fs: FileSystem) -> None
Mount an external filesystem at the given VFS path.
Parameters:
vfs_path— Mount point inside the VFS.fs— FileSystem instance to mount.
Example:
>>> bash = Bash()
>>> overlay = FileSystem()
>>> overlay.write_file("/data.csv", b"a,b,c")
>>> bash.mount("/mnt/data", overlay)
>>> result = bash.execute_sync("cat /mnt/data/data.csv")
>>> print(result.stdout)
a,b,c
unmount
Bash.unmount(vfs_path: str) -> None
Unmount a previously mounted filesystem.
Example:
>>> bash = Bash()
>>> overlay = FileSystem()
>>> bash.mount("/mnt/ext", overlay)
>>> bash.unmount("/mnt/ext")
BashTool
Sandboxed bash interpreter for AI agents.
BashTool provides a safe execution environment for running bash commands with a virtual filesystem. All file operations are contained within the sandbox - no access to the real filesystem.
Adds LLM-facing contract metadata (description, system_prompt,
input_schema, output_schema) on top of the core interpreter.
Fields
name—strshort_description—strversion—str
Constructor
BashTool(username: str | None = None, hostname: str | None = None, cwd: str | None = None, env: Mapping[str, str] | None = None, max_commands: int | None = None, max_loop_iterations: int | None = None, max_memory: int | None = None, timeout_seconds: float | None = None, files: dict[str, str | Callable[[], str]] | None = None, mounts: list[dict[str, Any]] | None = None, allowed_mount_paths: list[str] | None = None, readonly_filesystem: bool = False, custom_builtins: Mapping[str, BuiltinCallback] | None = None, network: NetworkConfig | None = None) -> None
Create a new BashTool.
Parameters:
username— Custom username (default"user").hostname— Custom hostname (default"bashkit").cwd— Initial working directory for the shell. Sets the starting directory directly instead of running a leadingcd.env— Initial environment variables applied before execution, so scripts see them without anexportprelude.max_commands— Limit total commands executed.max_loop_iterations— Limit iterations per loop.max_memory— Memory limit in bytes for the VFS.timeout_seconds— Abort execution after this duration.files— Dict mapping VFS paths to file contents or lazy callables.mounts— List of real host directory mount configs.allowed_mount_paths— Host path prefixes allowed for real filesystem mounts. Required when mounting sensitive host locations such as paths under a user home directory.readonly_filesystem— Deny all filesystem mutations after configured files and mounts are applied.custom_builtins— Constructor-time Python callbacks exposed as bash builtins. Each callback receives aBuiltinContext(including a livefshandle to the virtual filesystem) and must return a stdout string, aBuiltinResult, or await either. Async callbacks run on the caller’s active asyncio loop forawait execute()and on a private loop forexecute_sync().network— Optional outbound HTTP / network configuration. SeeBash.__init__for accepted keys. Preserved acrossreset()andfrom_snapshot().
Example:
>>> tool = BashTool(
... timeout_seconds=30,
... custom_builtins={"ping": lambda ctx: "pong\n"},
... network={"allow_all": True},
... )
>>> print(tool.name)
bash
execute
BashTool.execute(commands: str, on_output: OutputHandler | None = None) -> ExecResult
Execute bash commands asynchronously.
Async custom_builtins callbacks run on the caller’s active asyncio
loop.
on_output must be synchronous.
Example:
>>> tool = BashTool()
>>> result = await tool.execute("ls /")
>>> result.success
True
execute_sync
BashTool.execute_sync(commands: str, on_output: OutputHandler | None = None) -> ExecResult
Execute bash commands synchronously (blocking).
Async custom_builtins callbacks run on a private loop here.
When called from inside a running event loop (e.g. Jupyter / IPython),
callbacks are dispatched to a background thread with their own loop so
that asyncio’s “cannot run while another loop is running” restriction
is not triggered.
on_output must be synchronous.
Example:
>>> tool = BashTool()
>>> result = tool.execute_sync("echo 42")
>>> result.stdout.strip()
'42'
execute_or_throw
BashTool.execute_or_throw(commands: str, on_output: OutputHandler | None = None) -> ExecResult
Execute commands asynchronously; raise BashError on non-zero exit.
on_output must be synchronous.
Example:
>>> tool = BashTool()
>>> result = await tool.execute_or_throw("echo ok")
>>> result.success
True
execute_sync_or_throw
BashTool.execute_sync_or_throw(commands: str, on_output: OutputHandler | None = None) -> ExecResult
Execute commands synchronously; raise BashError on non-zero exit.
on_output must be synchronous.
Example:
>>> tool = BashTool()
>>> result = tool.execute_sync_or_throw("echo ok")
>>> result.stdout.strip()
'ok'
cancel
BashTool.cancel() -> None
Cancel the currently running execution.
Safe to call from any thread.
Example:
>>> tool = BashTool()
>>> tool.cancel() # no-op if nothing is running
clear_cancel
BashTool.clear_cancel() -> None
Clear the cancellation flag so subsequent executions proceed normally.
Call this after a cancel() once the in-flight execution has
finished and you want to reuse the same BashTool instance
(preserving VFS state). Without this, every future execute()
will immediately fail with "execution cancelled".
Note: Calling this while an execution is still in-flight may
allow that execution to continue past the cancellation point.
Wait for the cancelled execution to finish before clearing
(await the async call or let execute_sync return).
Example:
>>> tool = BashTool()
>>> tool.cancel()
>>> tool.clear_cancel()
>>> result = tool.execute_sync("echo ok")
>>> result.exit_code
0
description
BashTool.description() -> str
Return the tool description for LLM consumption.
Example:
>>> tool = BashTool()
>>> desc = tool.description()
>>> "bash" in desc.lower()
True
help
BashTool.help() -> str
Return extended help text.
Example:
>>> tool = BashTool()
>>> help_text = tool.help()
>>> len(help_text) > 0
True
system_prompt
BashTool.system_prompt() -> str
Return the system prompt for LLM agents.
Includes tool description, usage guidelines, and capabilities.
Example:
>>> tool = BashTool()
>>> prompt = tool.system_prompt()
>>> "sandbox" in prompt.lower() or "bash" in prompt.lower()
True
input_schema
BashTool.input_schema() -> str
Return the JSON Schema for tool input.
Example:
>>> import json
>>> tool = BashTool()
>>> schema = json.loads(tool.input_schema())
>>> "commands" in str(schema)
True
output_schema
BashTool.output_schema() -> str
Return the JSON Schema for tool output.
Example:
>>> import json
>>> tool = BashTool()
>>> schema = json.loads(tool.output_schema())
>>> isinstance(schema, dict)
True
reset
BashTool.reset() -> None
Reset the tool to initial state.
Clears VFS, environment, and shell state while re-applying
constructor-time custom_builtins.
Example:
>>> tool = BashTool()
>>> tool.execute_sync("touch /tmp/file")
>>> tool.reset()
>>> result = tool.execute_sync("test -f /tmp/file")
>>> result.exit_code # file is gone
1
snapshot
BashTool.snapshot(exclude_filesystem: bool = False, exclude_functions: bool = False) -> bytes
Serialize interpreter state to bytes.
snapshot_keyed
BashTool.snapshot_keyed(key: bytes, exclude_filesystem: bool = False, exclude_functions: bool = False) -> bytes
Serialize interpreter state to HMAC-protected bytes.
shell_state
BashTool.shell_state() -> ShellState
Capture a read-only shell-state snapshot.
restore_snapshot
BashTool.restore_snapshot(data: bytes) -> None
Restore interpreter state from bytes produced by snapshot().
restore_snapshot_keyed
BashTool.restore_snapshot_keyed(data: bytes, key: bytes) -> None
Restore interpreter state from bytes produced by snapshot_keyed().
from_snapshot
BashTool.from_snapshot(data: bytes, username: str | None = None, hostname: str | None = None, cwd: str | None = None, env: Mapping[str, str] | None = None, max_commands: int | None = None, max_loop_iterations: int | None = None, max_memory: int | None = None, timeout_seconds: float | None = None, files: dict[str, str] | None = None, mounts: list[dict[str, Any]] | None = None, allowed_mount_paths: list[str] | None = None, readonly_filesystem: bool = False, custom_builtins: Mapping[str, BuiltinCallback] | None = None, network: NetworkConfig | None = None) -> BashTool
Create a new BashTool from snapshot bytes and optional constructor kwargs.
from_snapshot_keyed
BashTool.from_snapshot_keyed(data: bytes, key: bytes, username: str | None = None, hostname: str | None = None, cwd: str | None = None, env: Mapping[str, str] | None = None, max_commands: int | None = None, max_loop_iterations: int | None = None, max_memory: int | None = None, timeout_seconds: float | None = None, files: dict[str, str] | None = None, mounts: list[dict[str, Any]] | None = None, allowed_mount_paths: list[str] | None = None, readonly_filesystem: bool = False, custom_builtins: Mapping[str, BuiltinCallback] | None = None, network: NetworkConfig | None = None) -> BashTool
Create a new BashTool from HMAC-protected snapshot bytes.
read_file
BashTool.read_file(path: str) -> str
Read a VFS file as UTF-8 text.
write_file
BashTool.write_file(path: str, content: str) -> None
Write UTF-8 text into the VFS.
append_file
BashTool.append_file(path: str, content: str) -> None
Append UTF-8 text to a VFS file.
mkdir
BashTool.mkdir(path: str, recursive: bool = False) -> None
Create a directory in the VFS.
exists
BashTool.exists(path: str) -> bool
Return whether a VFS path exists.
remove
BashTool.remove(path: str, recursive: bool = False) -> None
Remove a VFS file or directory.
stat
BashTool.stat(path: str) -> dict[str, Any]
Return metadata for a VFS path.
chmod
BashTool.chmod(path: str, mode: int) -> None
Change VFS permissions for a path.
symlink
BashTool.symlink(target: str, link: str) -> None
Create a symlink in the VFS.
read_link
BashTool.read_link(path: str) -> str
Return the symlink target for a VFS path.
read_dir
BashTool.read_dir(path: str) -> list[dict[str, Any]]
Return directory entries with metadata.
ls
BashTool.ls(path: str = '.') -> list[str]
Return entry names for a directory, or an empty list if it is missing.
glob
BashTool.glob(pattern: str) -> list[str]
Return file paths matching a safe glob pattern.
fs
BashTool.fs() -> FileSystem
Return a live filesystem handle.
Each operation acquires the interpreter lock, so the handle always
reflects the latest state (including after reset()).
Example:
>>> tool = BashTool()
>>> tool.execute_sync("echo data > /out.txt")
>>> fs = tool.fs()
>>> fs.read_file("/out.txt")
b'data\n'
mount
BashTool.mount(vfs_path: str, fs: FileSystem) -> None
Mount an external filesystem at the given VFS path.
Example:
>>> tool = BashTool()
>>> ext = FileSystem()
>>> ext.write_file("/info.txt", b"external")
>>> tool.mount("/mnt/ext", ext)
>>> result = tool.execute_sync("cat /mnt/ext/info.txt")
>>> result.stdout.strip()
'external'
unmount
BashTool.unmount(vfs_path: str) -> None
Unmount a previously mounted filesystem.
Example:
>>> tool = BashTool()
>>> ext = FileSystem()
>>> tool.mount("/mnt/ext", ext)
>>> tool.unmount("/mnt/ext")
ScriptedTool
Compose Python callbacks as bash builtins for multi-tool orchestration.
Each registered tool becomes a bash builtin command. An LLM (or user) writes a single bash script that pipes, loops, and branches across tools.
Fields
name—strshort_description—strversion—str
Constructor
ScriptedTool(name: str, short_description: str | None = None, max_commands: int | None = None, max_loop_iterations: int | None = None) -> None
Create a new ScriptedTool.
Parameters:
name— Tool name (used as the LLM tool identifier).short_description— One-line description of the tool.max_commands— Limit total commands per execution.max_loop_iterations— Limit iterations per loop.
Example:
>>> tool = ScriptedTool("data_pipeline", short_description="ETL tools")
>>> print(tool.name)
data_pipeline
add_tool
ScriptedTool.add_tool(name: str, description: str, callback: Callable[[dict[str, Any], str | None], str], schema: dict[str, Any] | None = None) -> None
Register a Python callback as a bash builtin command.
Parameters:
name— Command name (becomes a bash builtin).description— Human-readable description of the sub-tool.callback—(params_dict, stdin_or_none) -> output_stringor an async callback that resolves to one. Async callbacks run on the caller’s active asyncio loop forawait execute()and on a private loop forexecute_sync().schema— Optional JSON Schema for the tool’s parameters.
Example:
>>> tool = ScriptedTool("math")
>>> tool.add_tool(
... "add", "Add two numbers",
... callback=lambda p, s=None: str(int(p["a"]) + int(p["b"])) + "\n",
... schema={
... "type": "object",
... "properties": {"a": {"type": "integer"}, "b": {"type": "integer"}},
... },
... )
>>> result = tool.execute_sync("add --a 2 --b 3")
>>> result.stdout.strip()
'5'
env
ScriptedTool.env(key: str, value: str) -> None
Set an environment variable for subsequent executions.
Example:
>>> tool = ScriptedTool("demo")
>>> tool.env("API_KEY", "secret-123")
>>> result = tool.execute_sync("echo $API_KEY")
>>> result.stdout.strip()
'secret-123'
execute
ScriptedTool.execute(commands: str) -> ExecResult
Execute commands asynchronously.
Async callbacks run on the caller’s active asyncio loop.
Example:
>>> tool = ScriptedTool("demo")
>>> tool.add_tool("hi", "Say hi", callback=lambda p, s=None: "hi\n")
>>> result = await tool.execute("hi")
>>> result.stdout.strip()
'hi'
execute_sync
ScriptedTool.execute_sync(commands: str) -> ExecResult
Execute commands synchronously (blocking).
Async callbacks run on a private loop here.
Example:
>>> tool = ScriptedTool("demo")
>>> tool.add_tool("ping", "Ping", callback=lambda p, s=None: "pong\n")
>>> result = tool.execute_sync("ping")
>>> result.stdout.strip()
'pong'
tool_count
ScriptedTool.tool_count() -> int
Return the number of registered sub-tools.
Example:
>>> tool = ScriptedTool("demo")
>>> tool.tool_count()
0
>>> tool.add_tool("a", "A", callback=lambda p, s=None: "")
>>> tool.tool_count()
1
description
ScriptedTool.description() -> str
Return the tool description for LLM consumption.
Example:
>>> tool = ScriptedTool("api", short_description="API tools")
>>> desc = tool.description()
>>> len(desc) > 0
True
help
ScriptedTool.help() -> str
Return extended help text listing all registered sub-tools.
Example:
>>> tool = ScriptedTool("api")
>>> tool.add_tool("fetch", "Fetch URL", callback=lambda p, s=None: "")
>>> "fetch" in tool.help()
True
system_prompt
ScriptedTool.system_prompt() -> str
Return the system prompt for LLM agents.
Includes descriptions of all registered sub-tools and usage examples.
Example:
>>> tool = ScriptedTool("api")
>>> tool.add_tool("fetch", "Fetch URL", callback=lambda p, s=None: "")
>>> prompt = tool.system_prompt()
>>> "fetch" in prompt.lower()
True
input_schema
ScriptedTool.input_schema() -> str
Return the JSON Schema for tool input.
Example:
>>> import json
>>> tool = ScriptedTool("api")
>>> schema = json.loads(tool.input_schema())
>>> "commands" in str(schema)
True
output_schema
ScriptedTool.output_schema() -> str
Return the JSON Schema for tool output.
Example:
>>> import json
>>> tool = ScriptedTool("api")
>>> schema = json.loads(tool.output_schema())
>>> isinstance(schema, dict)
True
FileSystem
Direct access to Bashkit’s virtual filesystem or a standalone mountable FS.
Two ways to create:
- In-memory (default) — starts empty:
>>> fs = FileSystem()
>>> fs.write_file("/hello.txt", b"hi")
>>> fs.read_file("/hello.txt")
b'hi'
- Backed by a real host directory:
>>> fs = FileSystem.real("/tmp/data", writable=False)
>>> fs.exists("/some-host-file.txt")
True
Constructor
FileSystem() -> None
Create a new empty in-memory filesystem.
Example:
>>> fs = FileSystem()
>>> fs.exists("/anything")
False
real
FileSystem.real(host_path: str, writable: bool = False) -> FileSystem
Create a filesystem backed by a real host directory.
Parameters:
host_path— Absolute path on the host to expose.writable— Allow write operations (default read-only).
Example:
>>> fs = FileSystem.real("/tmp/project", writable=True)
>>> fs.write_file("/tmp/project/out.txt", b"data")
from_capsule
FileSystem.from_capsule(capsule: Any) -> FileSystem
Create a filesystem from a PyCapsule exported by a native extension.
The capsule must wrap a bashkit.FileSystem.v1 stable ABI handle.
to_capsule
FileSystem.to_capsule() -> Any
Export this filesystem as a stable-ABI PyCapsule for native extension interop.
read_file
FileSystem.read_file(path: str) -> bytes
Read the entire contents of a file.
Parameters:
path— Absolute path in the filesystem.
Returns: File contents as bytes.
Example:
>>> fs = FileSystem()
>>> fs.write_file("/demo.txt", b"hello")
>>> fs.read_file("/demo.txt")
b'hello'
write_file
FileSystem.write_file(path: str, content: bytes) -> None
Write content to a file, creating or overwriting it.
Parameters:
path— Absolute path in the filesystem.content— Data to write.
Example:
>>> fs = FileSystem()
>>> fs.write_file("/output.txt", b"result data")
append_file
FileSystem.append_file(path: str, content: bytes) -> None
Append content to an existing file.
Parameters:
path— Absolute path in the filesystem.content— Data to append.
Example:
>>> fs = FileSystem()
>>> fs.write_file("/log.txt", b"line1\n")
>>> fs.append_file("/log.txt", b"line2\n")
>>> fs.read_file("/log.txt")
b'line1\nline2\n'
mkdir
FileSystem.mkdir(path: str, recursive: bool = False) -> None
Create a directory.
Parameters:
path— Absolute path for the new directory.recursive— Create parent directories as needed.
Example:
>>> fs = FileSystem()
>>> fs.mkdir("/a/b/c", recursive=True)
>>> fs.exists("/a/b/c")
True
remove
FileSystem.remove(path: str, recursive: bool = False) -> None
Remove a file or directory.
Parameters:
path— Absolute path to remove.recursive— Remove directory contents recursively.
Example:
>>> fs = FileSystem()
>>> fs.write_file("/tmp.txt", b"x")
>>> fs.remove("/tmp.txt")
>>> fs.exists("/tmp.txt")
False
stat
FileSystem.stat(path: str) -> dict[str, Any]
Get file metadata.
Returns: Dict with file_type, size, mode, modified, created.
Example:
>>> fs = FileSystem()
>>> fs.write_file("/f.txt", b"data")
>>> info = fs.stat("/f.txt")
>>> info["file_type"]
'file'
>>> info["size"]
4
read_dir
FileSystem.read_dir(path: str) -> list[dict[str, Any]]
List directory entries.
Returns: List of dicts, each with name and metadata keys.
Example:
>>> fs = FileSystem()
>>> fs.write_file("/dir/a.txt", b"a")
>>> entries = fs.read_dir("/dir")
>>> entries[0]["name"]
'a.txt'
exists
FileSystem.exists(path: str) -> bool
Check whether a path exists.
Example:
>>> fs = FileSystem()
>>> fs.exists("/nope")
False
>>> fs.write_file("/yes.txt", b"")
>>> fs.exists("/yes.txt")
True
rename
FileSystem.rename(from_path: str, to_path: str) -> None
Rename (move) a file or directory.
Example:
>>> fs = FileSystem()
>>> fs.write_file("/old.txt", b"data")
>>> fs.rename("/old.txt", "/new.txt")
>>> fs.exists("/new.txt")
True
copy
FileSystem.copy(from_path: str, to_path: str) -> None
Copy a file.
Example:
>>> fs = FileSystem()
>>> fs.write_file("/src.txt", b"data")
>>> fs.copy("/src.txt", "/dst.txt")
>>> fs.read_file("/dst.txt")
b'data'
symlink
FileSystem.symlink(target: str, link: str) -> None
Create a symbolic link.
Parameters:
target— Path the symlink points to.link— Path of the symlink itself.
Example:
>>> fs = FileSystem()
>>> fs.write_file("/real.txt", b"data")
>>> fs.symlink("/real.txt", "/link.txt")
>>> fs.read_file("/link.txt")
b'data'
chmod
FileSystem.chmod(path: str, mode: int) -> None
Change file permissions.
Parameters:
path— Absolute path.mode— Octal permission bits (e.g.0o755).
Example:
>>> fs = FileSystem()
>>> fs.write_file("/script.sh", b"#!/bin/bash")
>>> fs.chmod("/script.sh", 0o755)
read_link
FileSystem.read_link(path: str) -> str
Read the target of a symbolic link.
Example:
>>> fs = FileSystem()
>>> fs.write_file("/target.txt", b"data")
>>> fs.symlink("/target.txt", "/link.txt")
>>> fs.read_link("/link.txt")
'/target.txt'
ExecResult
Result from executing bash commands.
Example:
>>> bash = Bash()
>>> result = bash.execute_sync("echo hello")
>>> result.success
True
>>> result.stdout
'hello\n'
>>> result.exit_code
0
Fields
stdout—strstderr—strexit_code—interror—str | Nonesuccess—bool
to_dict
ExecResult.to_dict() -> dict[str, Any]
Convert result to a plain dictionary.
Returns: Dict with stdout, stderr, exit_code, error, stdout_truncated, stderr_truncated, final_env.
Example:
>>> bash = Bash()
>>> result = bash.execute_sync("echo hi")
>>> d = result.to_dict()
>>> d["stdout"]
'hi\n'
>>> d["exit_code"]
0
ShellState
Read-only snapshot of shell state.
Returned by Bash.shell_state() and BashTool.shell_state() for
prompt rendering and state inspection. This is a Python-friendly
inspection view, not a full-fidelity Rust ShellState mirror.
Mapping fields are immutable views. Use
snapshot(exclude_filesystem=True) when you need shell-only restore
bytes. Transient fields like last_exit_code and traps reflect the
captured snapshot, but the next top-level execute() / execute_sync()
clears them before running a new command.
Fields
env—Mapping[str, str]variables—Mapping[str, str]arrays—Mapping[str, Mapping[int, str]]assoc_arrays—Mapping[str, Mapping[str, str]]cwd—strlast_exit_code—intaliases—Mapping[str, str]traps—Mapping[str, str]
BuiltinContext
Invocation context for a custom builtin callback.
Fields
name—strargv—list[str]stdin—str | Noneenv—dict[str, str]cwd—strfs—FileSystem
BuiltinResult
Shell-facing result for a custom builtin callback.
Fields
stdout—strstderr—strexit_code—int
Constructor
BuiltinResult(stdout: str = '', stderr: str = '', exit_code: int = 0) -> None
BashError
Exception raised when a bash command exits with non-zero status.
Example:
>>> bash = Bash()
>>> try:
... bash.execute_sync_or_throw("exit 42")
... except BashError as e:
... print(e.exit_code)
42
Fields
exit_code—intstderr—strstdout—str
create_langchain_tool_spec()
create_langchain_tool_spec() -> dict[str, Any]
Create a LangChain-compatible tool specification.
Returns: Dict with name, description, and args_schema.
Example:
>>> spec = create_langchain_tool_spec()
>>> spec["name"]
'bash'
get_version()
get_version() -> str
Get the bashkit version string.
Example:
>>> version = get_version()
>>> isinstance(version, str)
True
Framework integrations
bashkit.langchain
LangChain integration for Bashkit.
Field
bashkit.langchain.Field(*args, **kwargs)
PrivateAttr
bashkit.langchain.PrivateAttr(*args, **kwargs)
create_bash_tool
bashkit.langchain.create_bash_tool(username: str | None = None, hostname: str | None = None, max_commands: int | None = None, max_loop_iterations: int | None = None, timeout_seconds: float | None = None, files: dict[str, str | Callable[[], str]] | None = None, mounts: list[dict[str, Any]] | None = None, allowed_mount_paths: list[str] | None = None, readonly_filesystem: bool = False, max_output_length: int = 100000) -> BashkitTool
Create a LangChain-compatible Bashkit tool.
Parameters:
username— Custom username for sandboxhostname— Custom hostname for sandboxmax_commands— Max commands to executemax_loop_iterations— Max loop iterationstimeout_seconds— Execution timeout in seconds. When set, commands that exceed this duration are aborted with exit code 124.files— Static VFS file mounts keyed by sandbox path.mounts— Real host directory mounts exposed inside the sandbox. Mounts are read-only by default; pass{"writable": True}on a mount config to allow writes.allowed_mount_paths— Host path prefixes allowed for real filesystem mounts. Required when mounting sensitive host locations such as paths under a user home directory.readonly_filesystem— Deny all filesystem mutations after configured files and mounts are applied.max_output_length— Maximum number of characters returned to the LangChain agent from one bash tool call before truncation.
Returns: BashkitTool instance for use with LangChain agents
Raises:
ImportError— If langchain-core is not installed
create_scripted_tool
bashkit.langchain.create_scripted_tool(scripted_tool: NativeScriptedTool) -> ScriptedToolLangChain
Create a LangChain-compatible tool from a configured ScriptedTool.
Parameters:
scripted_tool— A ScriptedTool with registered tool callbacks
Returns: ScriptedToolLangChain instance for use with LangChain agents
Raises:
ImportError— If langchain-core is not installed
bashkit.pydantic_ai
PydanticAI integration for Bashkit.
create_bash_tool
bashkit.pydantic_ai.create_bash_tool(username: str | None = None, hostname: str | None = None, max_commands: int | None = None, max_loop_iterations: int | None = None, timeout_seconds: float | None = None, max_output_length: int = 100000) -> Tool
Create a PydanticAI Tool wrapping Bashkit.
Parameters:
username— Custom username for sandboxhostname— Custom hostname for sandboxmax_commands— Max commands to executemax_loop_iterations— Max loop iterationstimeout_seconds— Execution timeout in seconds. When set, commands that exceed this duration are aborted with exit code 124.
Returns: Tool for use with Agent(tools=[...])
Raises:
ImportError— If pydantic-ai is not installed
bashkit.deepagents
Deep Agents integration for Bashkit.
create_bash_middleware
bashkit.deepagents.create_bash_middleware(**kwargs) -> BashkitMiddleware
Create BashkitMiddleware for Deep Agents.
create_bashkit_backend
bashkit.deepagents.create_bashkit_backend(**kwargs) -> BashkitBackend
Create BashkitBackend for Deep Agents.