Harness¶
harness
¶
Harness abstraction for AI agent CLI tools.
A harness encapsulates everything specific to a particular agent CLI (Claude Code, OpenCode, Codex, etc.): how to build the command, what env vars it needs, where credentials are mounted, and how to parse its output.
EFFORT_NONE = 'none'
module-attribute
¶
Effort override value meaning "pass no effort flag".
AGENT_EFFORT_ENV_VAR = 'AGENT_REASONING_EFFORT'
module-attribute
¶
Env var carrying the effective reasoning effort into the agent environment.
Backends export it next to AGENT_MODEL so skills can read the effort in
use without knowing the harness. Like AGENT_MODEL it is output only:
:meth:Harness.resolve_efforts never reads it, so an agentic-ci run started
inside an agent does not inherit the outer run's effort.
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.
registry_key = ''
class-attribute
instance-attribute
¶
Key of this harness in agentic_ci.models.MODEL_REGISTRY.
models
property
¶
Model ids and effort levels for this harness, from the registry.
supports_otel
property
¶
Whether the agent CLI supports OTEL telemetry export.
autoupdater_env_var
property
¶
Env var name to disable auto-updates.
auth_mode_for_env(env=None)
¶
Return the authentication mode selected by env.
Source code in src/agentic_ci/harness.py
validate_credentials(env=None, *, allow_auth_file=False)
¶
Fail early when harness-specific credentials are unavailable.
Harnesses without additional validation requirements use this no-op implementation.
Source code in src/agentic_ci/harness.py
build_args(prompt, model, extra_args=None, otel_endpoint=None, externally_sandboxed=False)
abstractmethod
¶
Build the CLI argument list to run inside the container.
externally_sandboxed is True when the backend already isolates the
agent (OpenShell), so a harness may drop its own approval prompts and
inner sandbox. Harnesses that always bypass permissions ignore it.
Source code in src/agentic_ci/harness.py
build_env_args(env=None)
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
build_env_script_lines(otel_port=None, otel_rate_file=None, traceparent=None, env=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
build_otel_exec_env(otel_port=None, traceparent=None)
abstractmethod
¶
Return ['--env', 'K=V', ...] pairs for podman exec when OTEL is enabled.
credential_mount_target()
abstractmethod
¶
create_stream_processor(pid=0)
abstractmethod
¶
image_env_var()
abstractmethod
¶
model_env_var()
abstractmethod
¶
default_model()
¶
default_model_tiers()
¶
Registry low/medium/high routing tiers for this harness.
The high tier matches :meth:default_model so routed runs never
regress hard tasks. SkillConfig.model_tiers overrides entries.
Source code in src/agentic_ci/harness.py
effort_env_var()
abstractmethod
¶
subagent_effort_env_var()
¶
resolve_efforts(effort=None, env=None)
¶
Return the effective (effort, subagent_effort) for a run.
Precedence for the main effort: explicit effort argument, then the
:meth:effort_env_var value, then the registry default_effort.
The sub-agent effort is the :meth:subagent_effort_env_var value,
then the registry subagent_effort, then the main effort; it is
None for harnesses without a separate sub-agent knob. The literal
value none selects "no effort flag". Values are validated by
:meth:build_effort_args.
Source code in src/agentic_ci/harness.py
build_effort_args(effort, subagent_effort=None)
¶
Return CLI args that set reasoning effort for this harness.
Returns [] when both values are None. Raises ValueError
for a value outside the registry's efforts set, so an invalid
override fails before the agent starts. The result is passed to
build_args() through extra_args.
Source code in src/agentic_ci/harness.py
effort_args(effort, subagent_effort)
abstractmethod
¶
CLI fragment for already-validated efforts.
Harnesses without a sub-agent knob ignore subagent_effort.
classifier_effort()
¶
Effort for the classifier run, from the registry.
None means normal resolution (effort env var, else default_effort).
build_classifier_args(max_turns)
¶
Extra CLI args that bound the classifier run.
Default is no bound; harnesses whose CLI has a turn cap override this.
build_local_env(otel_port=None, otel_rate_file=None, traceparent=None, env=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
write_sandbox_config(config_dir, otel_enabled=False)
¶
Write agent-specific config files to the sandbox config dir.
Called by backends before container start. Default is a no-op.
sandbox_config_mounts(config_dir)
¶
Return list of (host_path, container_path) for config file mounts.
Called by backends to mount config files written by write_sandbox_config(). Default returns empty list.
Source code in src/agentic_ci/harness.py
ClaudeCodeHarness
¶
Bases: Harness
Claude Code CLI harness.
auth_mode_for_env(env=None)
¶
Return 'oauth' when CLAUDE_CODE_OAUTH_TOKEN is set and ANTHROPIC_API_KEY is not.
ANTHROPIC_API_KEY still wins, matching Claude Code's own authentication precedence. Only this harness selects 'oauth': a subscription token is a Claude Code credential, and OpenCode and Codex have no path to use it.
Source code in src/agentic_ci/harness.py
OpenCodeHarness
¶
Bases: Harness
OpenCode CLI harness.
build_otel_exec_env(otel_port=None, traceparent=None)
¶
Return OTel env vars for OpenCode.
See docs/otel-configuration.md for why these differ from Claude Code.
Source code in src/agentic_ci/harness.py
CodexHarness
¶
Bases: Harness
OpenAI Codex CLI harness.
auth_mode
property
¶
Codex uses OpenAI credentials, not Anthropic or Vertex.
auth_mode_for_env(env=None)
¶
run_settings_toml(model, extra_args=None, otel_endpoint=None)
¶
Return the config.toml lines that mirror a run's command-line settings.
Covers the model, the update check, the reasoning efforts that
:meth:effort_args put in extra_args, and the OTLP exporters, so a
codex exec the agent starts without those flags (such as the
implement and review agents a skill dispatches) uses the same model,
effort and telemetry. Every line is a top-level key or dotted key. No
credential is ever included.
Source code in src/agentic_ci/harness.py
create_harness(name)
¶
Create a harness instance by name.