Skip to content

Factory & Backends

Backend Factory

backends

Backend registry and factory.

create_backend(name, *, harness, **kwargs)

Create a backend instance by name.

Parameters:

Name Type Description Default
name str

Backend name ("podman" or "openshell").

required
harness Harness

Agent harness instance.

required
**kwargs Any

Backend-specific arguments (workdir, image, policy, timeout, etc.). sandbox_profile (a :class:~agentic_ci.sandbox_profile.SandboxProfile) is passed only to the OpenShell backend; other backends log a warning and ignore it.

{}

Returns:

Type Description
Backend

A Backend instance.

Source code in src/agentic_ci/backends/__init__.py
def create_backend(name: str, *, harness: Harness, **kwargs: Any) -> Backend:
    """Create a backend instance by name.

    Args:
        name: Backend name ("podman" or "openshell").
        harness: Agent harness instance.
        **kwargs: Backend-specific arguments (workdir, image, policy, timeout, etc.).
            ``sandbox_profile`` (a :class:`~agentic_ci.sandbox_profile.SandboxProfile`)
            is passed only to the OpenShell backend; other backends log a
            warning and ignore it.

    Returns:
        A Backend instance.
    """
    sandbox_profile = kwargs.get("sandbox_profile")
    if sandbox_profile is not None and name in ("local", "podman"):
        log.info(
            f"WARNING: sandbox profiles only apply to the OpenShell backend; "
            f"ignoring the profile for the {name} backend"
        )
    if name == "local":
        return LocalBackend(
            workdir=kwargs.get("workdir", "."),
            extra_env=kwargs.get("extra_env"),
            harness=harness,
        )
    elif name == "podman":
        return PodmanBackend(
            workdir=kwargs.get("workdir", "."),
            image=kwargs.get("image"),
            timeout=kwargs.get("timeout", 1200),
            extra_env=kwargs.get("extra_env"),
            harness=harness,
        )
    elif name == "openshell":
        # The profile is passed only when set, so a run without one constructs
        # the backend exactly as before.
        profile_kwargs = {} if sandbox_profile is None else {"sandbox_profile": sandbox_profile}
        return OpenShellBackend(
            workdir=kwargs.get("workdir", "."),
            image=kwargs.get("image"),
            policy=kwargs.get("policy"),
            extra_env=kwargs.get("extra_env"),
            approval_mode=kwargs.get("approval_mode"),
            memory=kwargs.get("memory"),
            cpu=kwargs.get("cpu"),
            gpu=kwargs.get("gpu"),
            harness=harness,
            **profile_kwargs,
        )
    else:
        raise ValueError(f"Unknown backend: {name!r}. Choose 'local', 'podman', or 'openshell'.")

Local Backend

local

Local (direct execution) backend for agentic-ci.

LocalBackend(workdir='.', extra_env=None, *, harness)

Bases: Backend

Runs an AI agent directly in the local environment.

No container or sandbox — the agent binary must already be installed and accessible on PATH. Useful when agentic-ci is running inside an existing CI container (e.g. Prow) where an extra isolation layer is unnecessary.

The agent runs as the host user with the host environment, so there is no sandbox boundary to protect and the workdir's .git is not restored after the run, unlike the Podman and OpenShell backends.

Source code in src/agentic_ci/backends/local.py
def __init__(self, workdir=".", extra_env=None, *, harness: Harness):
    super().__init__(workdir=workdir, image=None, harness=harness)
    self._extra_env = extra_env or {}

Podman Backend

podman

Podman container backend for agentic-ci.

PodmanBackend(workdir='.', image=None, timeout=1200, extra_env=None, *, harness)

Bases: Backend

Runs an AI agent inside a persistent Podman container.

setup() creates a long-running detached container. run() execs the agent inside it. stop() tears it down. The work directory is mounted into the container and gcloud credentials are mounted read-only. The host's git control files (.git/config, hooks, info/) are recorded each time the container starts. After each run the container is stopped, which kills every process the agent left behind, and the host copy is restored and released; the next run records the host copy again (keeping host edits made between runs) and starts the container again.

Source code in src/agentic_ci/backends/podman.py
def __init__(
    self,
    workdir=".",
    image=None,
    timeout=1200,
    extra_env=None,
    *,
    harness: Harness,
):
    super().__init__(workdir=workdir, image=image, harness=harness)
    self.timeout = timeout
    self._container_name = f"agentic-ci-{uuid4().hex}"
    self._config_dir = None
    self._extra_env = extra_env or {}
    # True while the container is stopped between runs; see _park().
    self._parked = False