A harness is a tiny program Archal spawns as a child process. Its job: read the task text out of one environment variable, invoke your real agent runtime, and print the final answer to stdout. That’s it. Put it atDocumentation Index
Fetch the complete documentation index at: https://docs.archal.ai/llms.txt
Use this file to discover all available pages before exploring further.
./.archal/harness.ts for new integrations. npx archal init
generates one for you; this page is the reference for writing or extending one
by hand.
Template
./.archal/harness.ts
Stdout contract
Archal extracts the final answer from stdout with this precedence (cli/src/runner/execution/response-extraction.ts:200):
- Whole stdout parses as a single JSON object. Archal reads the
payloads[]ortextfield. Recommended shape for structured output: - JSON object embedded in mixed stdout. Archal scans for any outermost
{...}block containingpayloadsortext. Works when your agent prints logs and then a final JSON payload, but is fragile — prefer option 1. - NDJSON (one JSON object per line). Archal parses each line and picks
the object with a usable
payloads/textfield. Useful when your harness streams intermediate events. - Plain text fallback. If nothing above matches, Archal joins trimmed non-empty stdout lines outside JSON blocks. Any log lines you wrote to stdout end up in the “answer”. Keep logs on stderr if you rely on this path.
console.log(JSON.stringify({ text: result }))
is the right default.
Environment variables Archal injects
Archal sets these on the harness process before it spawns:| Variable | Description |
|---|---|
ARCHAL_ENGINE_TASK | The full task text (scenario prompt or --task "..."). |
ARCHAL_PREFLIGHT | Set to 1 during the boot preflight. Short-circuit and exit 0. |
ARCHAL_<TWIN>_URL | Per-twin MCP endpoint (e.g. ARCHAL_GITHUB_URL). |
ARCHAL_<TWIN>_BASE_URL | Per-twin REST base URL (e.g. ARCHAL_GITHUB_BASE_URL). |
ARCHAL_TWIN_NAMES | Comma-separated list of twins in this run. |
ARCHAL_MCP_CONFIG | Path to an MCP server config JSON, if your runtime wants to mount MCP servers directly. |
ARCHAL_TOKEN | Bearer token, if you need to call hosted twin APIs directly. |
HTTPS_PROXY | Present when the TLS proxy is on. |
NODE_EXTRA_CA_CERTS | Path to the proxy’s short-lived CA cert (present when the proxy is on). |
OPENAI_API_KEY, ANTHROPIC_API_KEY, etc.) are not
injected by Archal. Put them in .env or your CI secret store and read them
in the harness.
Exit-code contract
| Code | Meaning |
|---|---|
0 | Run succeeded, final answer printed to stdout. Evaluator scores the trace. |
| non-zero | Runtime error. The run is marked failed; [P] criteria are not evaluated. |
[D] criteria about
twin state are still checked either way as long as the run produced a trace.
How to route traffic to the twins
Two options, both supported side-by-side:- Direct HTTP to the injected endpoints. Read
ARCHAL_<TWIN>_BASE_URL/ARCHAL_<TWIN>_URLand pass them to your SDK (new Octokit({ baseUrl: process.env.ARCHAL_GITHUB_BASE_URL })). This is the preferred path — explicit, no TLS hijacking. - TLS proxy (route mode). When your runtime has hardcoded service
domains you can’t override (for example SDKs calling
oauth2.googleapis.comdirectly), Archal starts a TLS proxy and setsHTTPS_PROXY/NODE_EXTRA_CA_CERTSon the harness process. Calls to known twin domains get intercepted transparently. The proxy is on by default for local harness runs — pass--no-proxyto disable it. See Route-mode trust and safety.
Sanity-check the harness in isolation
Reproduce the preflight without running a scenario:OK and exit 0. If it hangs or crashes, archal run
will too — fix the harness first.
