Skip to content

Harness

harness

Harness abstraction for AI agent CLI tools.

A harness encapsulates everything specific to a particular agent CLI (Claude Code, OpenCode, etc.): how to build the command, what env vars it needs, where credentials are mounted, and how to parse its output.

Harness

Bases: ABC

Base class for agent CLI harnesses.

auth_mode property

Return 'api-key' if ANTHROPIC_API_KEY is set, else 'vertex'.

name abstractmethod property

Human-readable name for log messages.

supports_otel property

Whether the agent CLI supports OTEL telemetry export.

autoupdater_env_var property

Env var name to disable auto-updates.

build_args(prompt, model, extra_args=None) abstractmethod

Build the CLI argument list to run inside the container.

Source code in src/agentic_ci/harness.py
@abstractmethod
def build_args(self, prompt: str, model: str, extra_args: list[str] | None = None) -> list[str]:
    """Build the CLI argument list to run inside the container."""

build_env_args() abstractmethod

Return ['--env', 'K=V', ...] pairs for podman run (PodmanBackend only).

Container-image ENV vars (config dirs, AGENT_TOOL) are already set in the Containerfile, so this method should not override them.

Source code in src/agentic_ci/harness.py
@abstractmethod
def build_env_args(self) -> list[str]:
    """Return ['--env', 'K=V', ...] pairs for ``podman run`` (PodmanBackend only).

    Container-image ENV vars (config dirs, AGENT_TOOL) are already
    set in the Containerfile, so this method should not override them.
    """

build_env_script_lines(otel_port=None, otel_rate_file=None) abstractmethod

Return export K=V lines for the env script (OpenShellBackend only).

OpenShell extracts the container filesystem but drops OCI ENV metadata, so every required env var must be re-injected here. Config dirs use /sandbox/... paths per OpenShell convention.

Source code in src/agentic_ci/harness.py
@abstractmethod
def build_env_script_lines(
    self,
    otel_port: int | None = None,
    otel_rate_file: str | None = None,
) -> list[str]:
    """Return ``export K=V`` lines for the env script (OpenShellBackend only).

    OpenShell extracts the container filesystem but drops OCI ENV
    metadata, so every required env var must be re-injected here.
    Config dirs use ``/sandbox/...`` paths per OpenShell convention.
    """

build_otel_exec_env(otel_port=None) abstractmethod

Return ['--env', 'K=V', ...] pairs for podman exec when OTEL is enabled.

Source code in src/agentic_ci/harness.py
@abstractmethod
def build_otel_exec_env(self, otel_port: int | None = None) -> list[str]:
    """Return ['--env', 'K=V', ...] pairs for podman exec when OTEL is enabled."""

credential_mount_target() abstractmethod

Container-side home directory for credential mounts.

Source code in src/agentic_ci/harness.py
@abstractmethod
def credential_mount_target(self) -> str:
    """Container-side home directory for credential mounts."""

create_stream_processor(pid=0) abstractmethod

Return a stream processor for this harness's output format.

Source code in src/agentic_ci/harness.py
@abstractmethod
def create_stream_processor(self, pid: int = 0) -> Any:
    """Return a stream processor for this harness's output format."""

image_env_var() abstractmethod

Env var name for the fallback container image.

Source code in src/agentic_ci/harness.py
@abstractmethod
def image_env_var(self) -> str:
    """Env var name for the fallback container image."""

model_env_var() abstractmethod

Env var name for the model override.

Source code in src/agentic_ci/harness.py
@abstractmethod
def model_env_var(self) -> str:
    """Env var name for the model override."""

default_model() abstractmethod

Default model when no --model flag or env var is set.

Source code in src/agentic_ci/harness.py
@abstractmethod
def default_model(self) -> str:
    """Default model when no --model flag or env var is set."""

build_local_env(otel_port=None, otel_rate_file=None) abstractmethod

Return env vars as a plain dict for direct (local) execution.

Unlike build_env_args (podman --env format) or build_env_script_lines (OpenShell export format), this returns a dict suitable for merging into os.environ and passing to subprocess.Popen(env=...).

Source code in src/agentic_ci/harness.py
@abstractmethod
def build_local_env(
    self,
    otel_port: int | None = None,
    otel_rate_file: str | None = None,
) -> dict[str, str]:
    """Return env vars as a plain dict for direct (local) execution.

    Unlike build_env_args (podman --env format) or build_env_script_lines
    (OpenShell export format), this returns a dict suitable for merging
    into os.environ and passing to subprocess.Popen(env=...).
    """

ClaudeCodeHarness

Bases: Harness

Claude Code CLI harness.

OpenCodeHarness

Bases: Harness

OpenCode CLI harness.

create_harness(name)

Create a harness instance by name.

Source code in src/agentic_ci/harness.py
def create_harness(name: str) -> Harness:
    """Create a harness instance by name."""
    if name == "claude-code":
        return ClaudeCodeHarness()
    elif name == "opencode":
        return OpenCodeHarness()
    else:
        raise ValueError(f"Unknown harness: {name!r}. Choose 'claude-code' or 'opencode'.")