mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-05-05 02:31:47 +00:00
feat(tools): add microsandbox terminal backend
Adds microsandbox (https://microsandbox.dev) as a terminal backend alongside docker/singularity/modal/daytona/ssh. Commands run inside a libkrun microVM with its own kernel — meaningfully stronger isolation than the shared-kernel container backends, without a cloud dependency or a daemon. Backend shape is a thin wrapper around the msb CLI: one long-lived sandbox per environment (msb create), command execution via msb exec, teardown via msb stop + msb remove. Env-var filtering mirrors the Docker backend — explicit docker-style microsandbox_forward_env / microsandbox_env lists, skill passthroughs still filtered through _HERMES_PROVIDER_ENV_BLOCKLIST. Files: - tools/environments/microsandbox.py — new MicrosandboxEnvironment backend - tools/terminal_tool.py — dispatch, container_config keys, image resolution - hermes_cli/config.py — default microsandbox_* entries + env var sync - cli-config.yaml.example — 'Option 7' documented config block - tests/integration/test_microsandbox_terminal.py — skip-if-no-KVM integration tests for basic exec, filesystem, isolation, and the secret-leak regression Host requirements: Linux with /dev/kvm readable (or macOS on Apple Silicon) and msb on PATH or at MSB_PATH. Install: curl -fsSL https://install.microsandbox.dev | sh Follow-up PR will wire this into the hermes_cli/setup.py wizard.
This commit is contained in:
parent
5dda4cab41
commit
1f2303d3e2
5 changed files with 531 additions and 4 deletions
|
|
@ -873,6 +873,7 @@ def _get_env_config() -> Dict[str, Any]:
|
|||
"singularity_image": os.getenv("TERMINAL_SINGULARITY_IMAGE", f"docker://{default_image}"),
|
||||
"modal_image": os.getenv("TERMINAL_MODAL_IMAGE", default_image),
|
||||
"daytona_image": os.getenv("TERMINAL_DAYTONA_IMAGE", default_image),
|
||||
"microsandbox_image": os.getenv("TERMINAL_MICROSANDBOX_IMAGE", default_image),
|
||||
"cwd": cwd,
|
||||
"host_cwd": host_cwd,
|
||||
"docker_mount_cwd_to_workspace": mount_docker_cwd,
|
||||
|
|
@ -918,8 +919,8 @@ def _create_environment(env_type: str, image: str, cwd: str, timeout: int,
|
|||
Create an execution environment for sandboxed command execution.
|
||||
|
||||
Args:
|
||||
env_type: One of "local", "docker", "singularity", "modal", "daytona", "ssh"
|
||||
image: Docker/Singularity/Modal image name (ignored for local/ssh)
|
||||
env_type: One of "local", "docker", "singularity", "modal", "daytona", "microsandbox", "ssh"
|
||||
image: Docker/OCI/Singularity/Modal image name (ignored for local/ssh)
|
||||
cwd: Working directory
|
||||
timeout: Default command timeout
|
||||
ssh_config: SSH connection config (for env_type="ssh")
|
||||
|
|
@ -938,10 +939,13 @@ def _create_environment(env_type: str, image: str, cwd: str, timeout: int,
|
|||
volumes = cc.get("docker_volumes", [])
|
||||
docker_forward_env = cc.get("docker_forward_env", [])
|
||||
docker_env = cc.get("docker_env", {})
|
||||
microsandbox_volumes = cc.get("microsandbox_volumes", [])
|
||||
microsandbox_forward_env = cc.get("microsandbox_forward_env", [])
|
||||
microsandbox_env = cc.get("microsandbox_env", {})
|
||||
|
||||
if env_type == "local":
|
||||
return _LocalEnvironment(cwd=cwd, timeout=timeout)
|
||||
|
||||
|
||||
elif env_type == "docker":
|
||||
return _DockerEnvironment(
|
||||
image=image, cwd=cwd, timeout=timeout,
|
||||
|
|
@ -1022,6 +1026,18 @@ def _create_environment(env_type: str, image: str, cwd: str, timeout: int,
|
|||
persistent_filesystem=persistent, task_id=task_id,
|
||||
)
|
||||
|
||||
elif env_type == "microsandbox":
|
||||
# Lazy import so msb is only required when backend is selected.
|
||||
from tools.environments.microsandbox import MicrosandboxEnvironment as _MicrosandboxEnvironment
|
||||
return _MicrosandboxEnvironment(
|
||||
image=image, cwd=cwd, timeout=timeout,
|
||||
cpu=cpu, memory=memory, disk=disk,
|
||||
persistent_filesystem=persistent, task_id=task_id,
|
||||
volumes=microsandbox_volumes,
|
||||
forward_env=microsandbox_forward_env,
|
||||
env=microsandbox_env,
|
||||
)
|
||||
|
||||
elif env_type == "ssh":
|
||||
if not ssh_config or not ssh_config.get("host") or not ssh_config.get("user"):
|
||||
raise ValueError("SSH environment requires ssh_host and ssh_user to be configured")
|
||||
|
|
@ -1462,6 +1478,8 @@ def terminal_tool(
|
|||
image = overrides.get("modal_image") or config["modal_image"]
|
||||
elif env_type == "daytona":
|
||||
image = overrides.get("daytona_image") or config["daytona_image"]
|
||||
elif env_type == "microsandbox":
|
||||
image = overrides.get("microsandbox_image") or config["microsandbox_image"]
|
||||
else:
|
||||
image = ""
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue