Skip to main content
A harness is the small process Archal starts for a run. It reads the task, calls your agent, uses normal SDKs for service work, and prints the final answer.

Quick start

For scored clone runs, run the harness in Docker or the sandbox:
archal run scenario.md --harness . --docker
# or
archal run scenario.md --sandbox
Use the rest of this page when you need prompt files, model defaults, or the exact environment contract.

Saving a harness to the workspace

Saved harnesses and scenarios are workspace-scoped artifacts you reuse across projects. They are not auto-saved on archal run - the run uses whichever local file you pass via --harness or .archal.json. To make a local harness reusable across projects, create it explicitly:
archal harness create "My agent" --harness ./.archal/harness.ts
Push a scenario to the active workspace the same way:
archal scenario push ./scenarios/close-stale-issues.md
List what is saved with archal harness list and archal scenario list --source workspace. See archal scenario for the full subcommand reference.

Custom harness directory

Point --harness at any directory containing your agent code:
archal run scenario.md \
  --harness ./my-agent \
  --docker \
  -n 3
You do not need a manifest when the entrypoint is obvious. Add harness.json when you want command defaults, prompt files, or a default model.

Manifest (harness.json)

Drop harness.json in the harness directory:
{
  "version": 1,
  "defaultModel": "gpt-4.1",
  "promptFiles": ["system-prompt.md", "safety-guidelines.md"],
  "local": {
    "command": "node",
    "args": ["agent.mjs"],
    "env": {
      "MY_CUSTOM_VAR": "value"
    }
  }
}
FieldTypeRequiredDescription
version1YesSchema version. Must be 1.
defaultModelstringNoFallback model ID when --agent-model is not provided.
promptFilesstring[]NoMarkdown files loaded in order and prepended to the scenario task. Paths are relative to the harness directory.
local.commandstringNoCommand to spawn (e.g., node, python, npx).
local.argsstring[]NoArguments passed to the command.
local.envobjectNoExtra environment variables injected into the harness process.

Add prompt files

List markdown files in promptFiles when you want shared instructions prepended to every scenario task:
{
  "version": 1,
  "promptFiles": ["system-prompt.md", "safety-guidelines.md"],
  "local": { "command": "node", "args": ["agent.mjs"] }
}
Your agent receives the combined content in AGENT_TASK:
[Contents of system-prompt.md]

[Contents of safety-guidelines.md]

---

[Scenario prompt]
Good prompt files are usually role instructions, safety rules, or domain context.

Environment variables

Archal injects these into every harness process.

Task and model

VariableDescription
AGENT_TASKThe full task text (prompt files + scenario prompt). ## Expected Behavior is never included - it’s the evaluator holdout.
AGENT_MODELModel identifier from --agent-model or manifest defaultModel.

Service connectivity

VariableDescription
NODE_EXTRA_CA_CERTS / SSL_CERT_FILE / REQUESTS_CA_BUNDLEShort-lived CA paths for clients that need to trust the controlled TLS intercept.
Harnesses should call normal service domains such as https://api.github.com, https://slack.com/api, or https://api.stripe.com. Clone URLs, MCP configs, and run credentials are managed by Archal and are not part of the harness contract. When using archal clone start for manual integration instead of archal run, the per-clone URLs are exported as ARCHAL_<CLONE>_REST_URL and ARCHAL_<CLONE>_MCP_URL. See Clone sessions for the full env-var naming convention.

API keys

Passed through from your environment or config:
VariableProvider
OPENAI_API_KEYOpenAI
ANTHROPIC_API_KEYAnthropic
GEMINI_API_KEYGoogle

Tuning overrides

VariableDescriptionDefault
ARCHAL_MAX_TOKENSMax completion tokens per LLM callModel-specific
ARCHAL_TEMPERATURESampling temperature0.2
ARCHAL_REASONING_EFFORTOpenAI reasoning models: low, medium, highmedium
ARCHAL_THINKING_BUDGETExtended thinking: adaptive, off, or token countadaptive
ARCHAL_LLM_TIMEOUTPer-LLM-call timeout in seconds120
ARCHAL_LOG_LEVELHarness log verbosityinfo

Base URL overrides

For Azure OpenAI, API proxies, or self-hosted endpoints:
VariableDefault
ARCHAL_OPENAI_BASE_URLhttps://api.openai.com/v1
ARCHAL_ANTHROPIC_BASE_URLhttps://api.anthropic.com
ARCHAL_GEMINI_BASE_URLhttps://generativelanguage.googleapis.com/v1beta

Metrics and trace output

Archal sets these paths. Your harness may write JSON to them for richer reports:
VariableDescription
AGENT_METRICS_FILEPath to write metrics JSON (token counts, timing, exit reason)
AGENT_TRACE_FILEPath to write agent trace JSON (thinking, text, tool calls per step)

Service transport

Use the same client code you would use outside Archal. For GitHub, that can be gh, Octokit, or fetch("https://api.github.com/..."). The controlled runtime routes supported service traffic to the scenario clones and applies run credentials server-side.

Example: minimal custom harness

A minimal harness reads the task and invokes your real agent runtime:
// agent.mjs
const TASK = process.env.AGENT_TASK;
const MODEL = process.env.AGENT_MODEL || 'gpt-4.1';
const API_KEY = process.env.OPENAI_API_KEY;

const result = await runMyAgent({ task: TASK, model: MODEL, apiKey: API_KEY });
console.log(JSON.stringify({ text: result }));
With a manifest:
{
  "version": 1,
  "defaultModel": "gpt-4.1",
  "local": { "command": "node", "args": ["agent.mjs"] }
}
Run it:
archal run scenario.md --harness ./my-agent --docker