Skip to content
Reference

Managed harness runtimes ​

Managed mode selects a native harness executable, records its resolved identity, and keeps reusable binaries in an external cache. It does not provide provider credentials or create an operating-system sandbox.

Runtime and version selection ​

Native agent selections accept runtime="system" or runtime="managed". System is the default and uses the executable discovered by that adapter. A system installation does not guarantee an exact version. Managed accepts an explicit semantic version or latest:

SDK selectionCLI selectionMeaning
{"harness": "codex", "models": [model]}--harness codex=MODELSystem executable.
{"harness": "codex", "models": [model], "runtime": "managed", "version": "0.155.1"}--runtime managed --harness [email protected]=MODELResolve or reuse that pin.
{"harness": "codex", "models": [model], "runtime": "managed", "version": "latest"}--runtime managed --harness codex=MODELResolve the current release for this invocation.

An SDK version requires runtime="managed"; AgentSpec validation raises ValueError("harness version requires managed runtime") otherwise. A CLI KIND@VERSION=MODEL likewise requires --runtime managed; the CLI rejects that combination during option validation. With managed mode and no explicit version, the SDK and CLI resolve latest. Do not supply latest where the test asserts an immutable pin. The accepted explicit version is MAJOR.MINOR.PATCH with an optional lowercase prerelease suffix. Build metadata and a leading v are not accepted by the selector parser.

Supported native adapter names are codex, pi, claude_code, and opencode. Selector aliases accepted by runtime acquisition include claude_code for Claude Code and open-code for OpenCode. ACP is not a managed native runtime; CLI validation rejects --runtime managed with an ACP selection. A custom ACP agent owns its launch executable and cache.

latest uses release metadata. The CLI creates an invocation-scoped pin shared by workers in that m3 test invocation, including xdist workers. A later CLI invocation resolves it again. A concrete version can use a valid matching cache entry without a metadata request. Cache hits are keyed by harness, resolved version, target, and archive digest.

Release metadata and target rules ​

The built-in recipes use these upstream release sources and tag conventions. The selected release still has to publish one asset matching the computed target.

HarnessMetadata sourceVersioned GitHub tag
Codexapi.github.com/repos/openai/codex/releases/{latest or tag}rust-v<VERSION>
Piapi.github.com/repos/earendil-works/pi/releases/{latest or tag}v<VERSION>
OpenCodeapi.github.com/repos/anomalyco/opencode/releases/{latest or tag}v<VERSION>
Claude Codedownloads.claude.ai/claude-code-releases/latest or /{VERSION}/manifest.jsonVendor manifest version; not a Git tag lookup

Target strings are computed by detect_target(kind, env=...). amd64 and x86_64 normalize to x64; aarch64 normalizes to arm64. The resolver reads target overrides from the selector mapping's env field, not from a new CLI option:

OverrideEffect
M3_RUNTIME_ARCHReplaces platform.machine() before architecture normalization.
M3_ROSETTA=1On arm64 macOS, labels the target architecture x64-rosetta.
M3_RUNTIME_LIBCFor OpenCode on Linux, selects the libc label; otherwise libc is detected and falls back to glibc.
M3_RUNTIME_AVX2=1For OpenCode on Linux, selects the avx2 variant instead of baseline.

Examples of target labels include darwin-arm64-64, linux-x64-64, windows-arm64-64, linux-x64-glibc-baseline, and linux-x64-musl-avx2. These labels describe M3's lookup input; they do not promise that a vendor publishes an asset for it.

Acquisition and recorded identity ​

The resolver reads the vendor release manifest, selects one target asset, and requires an independent SHA-256 value from GitHub release metadata. Claude Code uses the checksum in its platform manifest. A caller-supplied runtime selector must provide its own sha256; a hash computed only after downloading is not independent verification. M3 stages the archive, compares its SHA-256, rejects unsafe archive paths and entry types, extracts within size and member limits, checks the expected executable with --version, writes a receipt, and then creates a session lease. It does not infer a provider identity from an executable download.

The stable agent identity separates the requested selector from resolved metadata. snapshot.agent.harness records kind, runtime, and requested_selector; after resolution it can also record resolved_version, target, digest, verification_method, and immutable_release. Model identity separately records the requested model ID, observed provider, and observed model when the adapter reports them. An unresolved startup retains the requested selector without fabricating resolved fields.

Platform support ​

The resolver computes OS and CPU targets, then applies vendor filename or manifest-key matching rules. The rules below do not claim that matching assets exist in any particular vendor release:

HarnessTarget details
Codexcodex-{x86_64|aarch64}-apple-darwin on macOS, -unknown-linux-musl preferred then -unknown-linux-gnu on Linux, and -pc-windows-msvc.exe.zip on Windows.
Pipi-{darwin|linux|windows}-{x64|arm64} with .tar.gz, .tgz, or .zip suffix.
Claude CodeLooks up a platform key derived from the target in the vendor manifest and uses that entry's checksum. M3 does not hard-code a fixed platform allowlist.
OpenCodemacOS/Windows patterns include OS and x64 or arm64. Linux patterns additionally encode libc and baseline/AVX2; arm64 has a separate musl suffix.

M3 fails if the target's manifest key or matching asset is missing or ambiguous. Cache roots inside the project or PATH are rejected; so are blocked system-directory roots (including Windows system directories).

Cache location and precedence ​

Managed sessions download missing runtimes and reuse valid cached assets automatically. The default cache persists between runs; per-execution state is created separately.

Set an external path with M3_HARNESS_CACHE_DIR, MCPTestKit(harness_cache_dir=...), AgentSession(..., harness_cache_dir=...), or the CLI test option --harness-cache-dir. Precedence is AgentSession argument, kit argument, CLI-provided environment, M3_HARNESS_CACHE_DIR, then the OS default. The standalone cache commands accept --cache-dir (alias --harness-cache-dir) directly, which takes precedence over the environment:

OSDefault cache root
Windows%LOCALAPPDATA%/m3/harnesses (or the standard Local AppData path)
macOS~/Library/Caches/m3/harnesses
Linux and other supported Unix targets$XDG_CACHE_HOME/m3/harnesses, falling back to ~/.cache/m3/harnesses

m3 runtime cache list and prune also accept --project-root. Without an explicit path or M3_HARNESS_CACHE_DIR, M3 uses the same OS default. Cache roots must be outside the project and PATH, and outside blocked system directories. Symlinked cache paths are rejected. In CI, retain the selected cache directory between jobs to avoid downloading the same pinned releases again.

The receipt's provenance object stores kind, version, target, query-stripped url, sha256, source, executable, asset_name, verification_method, and immutable_release. m3 runtime cache list --cache-dir PATH reports ready entries by kind, version, target, digest, and status. A malformed receipt or changed executable is reported as corrupt and is not accepted as a cache hit. Progress events report resolution, download, verification, extraction, readiness, and cache hits; download URLs omit query values. The cache stores immutable executable assets and receipts. Adapter configuration, credentials, MCP server state, and harness home/config files are per execution and are not shared as cache assets.

Runtime acquisitions and sessions share cache entries. M3 serializes entry installation and pruning with a lock. An active session holds a lease, shown as in_use by CLI listing. Sessions release their leases when closed.

Inspect and prune cached runtimes ​

To inspect downloaded versions or troubleshoot a cache entry, run:

sh
m3 runtime cache list

The JSON output includes each entry's harness, version, target, digest, and status. To inspect a non-default cache, pass --cache-dir PATH or set M3_HARNESS_CACHE_DIR.

Cleanup is optional. To reclaim disk space after trying several versions, close sessions and kits, then run:

sh
m3 runtime cache prune

Pruning removes entries without active leases, including corrupt entries. Later tests reacquire any runtimes they need. Keep the cache between comparison runs to retain the download savings. Use the same --cache-dir PATH override if your tests use a custom cache.

Pruning preserves leased entries. The CLI waits briefly for leases to clear, then exits with an operational error if entries remain in use. It does not stop running sessions.

Authentication and failure behavior ​

Downloading a binary and authenticating it to a model provider are separate operations. A warm runtime cache can avoid a vendor download while the test still needs provider network access. A provider login does not avoid an uncached runtime download.

AdapterAuth source used at launchRuntime isolation detail
CodexWhen credential_references is empty, M3 copies CODEX_HOME/auth.json (default ~/.codex/auth.json) to its private child home if present. Explicit references suppress that copy and map child variables from the process environment.Uses a per-execution CODEX_HOME; the copied auth file is mode 0600, is not followed if it is a symlink, and is removed with the execution workspace. A managed executable does not supply the login.
PiPi resolves its own provider credential from child environment/provider configuration. M3 can map credential_references from named environment variables.Launches with a temporary isolated home; host Pi settings are not copied.
Claude CodeClaude resolves auth from supported environment or its configured login mode. M3 can map credential_references; host auth files are not copied into the isolated child home.Receives a per-execution home, MCP config, and selected child environment.
OpenCodeOpenCode uses its configured provider environment. M3 can map credential_references into named child variables; host OpenCode settings are not copied.Starts an isolated server process and home/config tree for the execution.

See the configuration reference for supported mappings, provider variable examples, and login prerequisites. Credential specifics do not alter runtime download verification.

RuntimeManager.acquire() raises the public RuntimeValidationError (a RuntimeError and ValueError) for selector/acquisition failures. Common message strings identify these conditions:

ConditionRuntime validation message
Unsupported kindunsupported runtime kind
Bad semantic versioninvalid runtime version
Missing URL and metadata URLselector must provide a download URL or manifest_url
Missing independent checksumruntime metadata lacks an independent sha256
Digest mismatchruntime sha256 mismatch
No/ambiguous GitHub asset for targetno ... release asset matches target ... / ambiguous ... release asset matches target ...
Cache symlink or tampered receipt/treeruntime cache path contains a symlink / runtime cache entry failed verification
Expected executable absentruntime archive contains no expected executable
Version smoke test failureruntime executable failed --version smoke check / runtime executable reported an unexpected version
Entry lock timeoutruntime cache entry is busy
Latest manifest lock timeoutruntime manifest resolution lock timed out

In AgentSession, managed runtime resolution is the first startup action, before server startup, probes, or provider launch. Acquisition exceptions are recorded as a failed startup and surfaced to the caller as TransportError("agent session startup failed"); the detailed vendor error is not propagated as a stable session API. A missing native executable or provider credential is a harness startup/readiness failure, not a download failure. Invalid CLI selection and an exhausted cache-prune wait use operational exit code 2.

Managed binaries are reusable. Per-execution home/configuration and progress files are temporary; the active lease is released when the agent session closes. Cancellation during acquisition leaves the worker responsible for staging cleanup; if acquisition completes after its caller is cancelled, M3 releases the resulting lease. Pruning skips an entry with an active lease. A runtime cache controls where M3 stores vendor executables. It does not restrict the harness process to a filesystem, network, or CPU sandbox.