refactor: remove mini-swe-agent dependency — inline Docker/Modal backends (#2804)

Drop the mini-swe-agent git submodule. All terminal backends now use
hermes-agent's own environment implementations directly.

Docker backend:
- Inline the `docker run -d` container startup (was 15 lines in
  minisweagent's DockerEnvironment). Our wrapper already handled
  execute(), cleanup(), security hardening, volumes, and resource limits.

Modal backend:
- Import swe-rex's ModalDeployment directly instead of going through
  minisweagent's 90-line passthrough wrapper.
- Bake the _AsyncWorker pattern (from environments/patches.py) directly
  into ModalEnvironment for Atropos compatibility without monkey-patching.

Cleanup:
- Remove minisweagent_path.py (submodule path resolution helper)
- Remove submodule init/install from install.sh and setup-hermes.sh
- Remove mini-swe-agent from .gitmodules
- environments/patches.py is now a no-op (kept for backward compat)
- terminal_tool.py no longer does sys.path hacking for minisweagent
- mini_swe_runner.py guards imports (optional, for RL training only)
- Update all affected tests to mock the new direct subprocess calls
- Update README.md, CONTRIBUTING.md

No functionality change — all Docker, Modal, local, SSH, Singularity,
and Daytona backends behave identically. 6093 tests pass.
This commit is contained in:
Teknium 2026-03-24 07:30:25 -07:00 committed by GitHub
parent 2233f764af
commit 02b38b93cb
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
22 changed files with 283 additions and 591 deletions

View file

@ -51,13 +51,6 @@ logger = logging.getLogger(__name__)
from tools.interrupt import is_interrupted, _interrupt_event
# Add mini-swe-agent to path if not installed. In git worktrees the populated
# submodule may live in the main checkout rather than the worktree itself.
from minisweagent_path import ensure_minisweagent_on_path
ensure_minisweagent_on_path(Path(__file__).resolve().parent.parent)
# =============================================================================
# Custom Singularity Environment with more space
# =============================================================================
@ -1188,27 +1181,15 @@ def terminal_tool(
def check_terminal_requirements() -> bool:
"""Check if all requirements for the terminal tool are met.
Important: local and singularity backends now use Hermes' own environment
wrappers directly and do not require the ``minisweagent`` Python package to
be installed. Docker and Modal still rely on mini-swe-agent internals.
"""
"""Check if all requirements for the terminal tool are met."""
config = _get_env_config()
env_type = config["env_type"]
try:
if env_type == "local":
# Local execution uses Hermes' own LocalEnvironment wrapper and does
# not depend on minisweagent being importable.
return True
elif env_type == "docker":
ensure_minisweagent_on_path(Path(__file__).resolve().parent.parent)
if importlib.util.find_spec("minisweagent") is None:
logger.error("mini-swe-agent is required for docker terminal backend but is not importable")
return False
# Check if docker is available (use find_docker for macOS PATH issues)
from tools.environments.docker import find_docker
docker = find_docker()
if not docker:
@ -1225,7 +1206,6 @@ def check_terminal_requirements() -> bool:
return False
elif env_type == "ssh":
# Check that host and user are configured
if not config.get("ssh_host") or not config.get("ssh_user"):
logger.error(
"SSH backend selected but TERMINAL_SSH_HOST and TERMINAL_SSH_USER "
@ -1235,11 +1215,9 @@ def check_terminal_requirements() -> bool:
return True
elif env_type == "modal":
ensure_minisweagent_on_path(Path(__file__).resolve().parent.parent)
if importlib.util.find_spec("minisweagent") is None:
logger.error("mini-swe-agent is required for modal terminal backend but is not importable")
if importlib.util.find_spec("swerex") is None:
logger.error("swe-rex is required for modal terminal backend: pip install 'swe-rex[modal]'")
return False
# Check for modal token
has_token = os.getenv("MODAL_TOKEN_ID") is not None
has_config = Path.home().joinpath(".modal.toml").exists()
if not (has_token or has_config):
@ -1269,7 +1247,7 @@ def check_terminal_requirements() -> bool:
if __name__ == "__main__":
# Simple test when run directly
print("Terminal Tool Module (mini-swe-agent backend)")
print("Terminal Tool Module")
print("=" * 50)
config = _get_env_config()