Skip to main content
Implement a Harness as the owner of one agent loop: validate compatibility, start its runtime, execute one PreparedTask, normalize a public RunResult, and release everything it created. The tutorial adapter below returns a configured answer instead of calling a model. That makes registry and lifecycle smoke tests deterministic. Replace only its execution body when integrating a real SDK or CLI; keep the same public contracts.

Record the Upstream Contract

Record the official framework or CLI version, supported model protocols, configuration format, prompt flow, tool and workspace behavior, installation method, timeouts, termination rules, trajectory format, and credential handling. Pin the version when it affects commands, prompts, parsing, or reproducibility, and prefer the public SDK or CLI over private functions.

Create the Minimal File

The smallest complete integration needs one implementation file and one package export:
Create src/agentcompass/harnesses/example_answer.py:
This covers the complete BaseHarness abstract surface: supports(), start_session(), and run_task(). It also shows close_session() explicitly even though the base class provides a no-op. BaseHarness.build_plan() copies matching config fields into plan_class, including the inherited inject_network_restriction_notice field.

Export and Inspect the Registration

Add the import to src/agentcompass/harnesses/__init__.py:
Then inspect discovery and the generated config schema:
The first command should contain example_answer and its description. The second should show answer with default Paris and the inherited network-notice field. An absent ID indicates an import or registration failure; a present ID does not prove that a real upstream runtime can install or launch.

Run One Task

Use example_exact_match from the Harness-driven Benchmark tutorial:
The command needs no endpoint because this tutorial Harness does not call req.model. The terminal result should complete with one selected task and report paths.run_info; its parent directory is the run directory. That directory should contain run_info.json, params.json, progress.json, progress.jsonl, logs/*.log, one details/*.json, and summary.md. The detail attempt should contain status: "completed", final_answer: "Paris", and correct: true after Benchmark evaluation. For a real Harness, repeat the smoke with one bounded upstream task and its actual credentials. A registry check alone never exercises installation, launch, parsing, cleanup, or the model endpoint.

Replace the Tutorial Execution Body

Keep public parameters in RuntimeHarnessConfig so the CLI, Python SDK, config files, and generated documentation resolve the same fields. Put normalized runtime choices such as version, launch mode, install strategy, step limit, command timeout, and cost behavior in a typed HarnessPlan. Do not mutate RunRequest, inspect private Benchmark fields, or persist secrets in a plan. Implement supports(environment, model) around capabilities rather than Benchmark IDs. Validate model protocol, shell and filesystem needs, endpoint forwarding, browser or GUI needs, workspace assumptions, credential location, and whether installation can work in the selected Environment. Reject unsupported combinations before Environment startup and never silently switch protocols, providers, install modes, or models. The runtime calls the lifecycle in this order:
Use start_session() for trusted installation, config generation, uploads, clients, or background services. run_task() executes exactly one PreparedTask and consumes only public fields such as prepared.input.prompt, messages, files, media, tools, and workspace. close_session() releases Harness-owned clients, processes, servers, temporary config, and background tasks after success, timeout, cancellation, or error; the runtime, not the Harness, closes the Environment.

Normalize Results Without Scoring

A Harness reports execution, not Benchmark correctness. Return the best available final_answer, requested files, ordered trajectory, token usage, timing, artifacts, and an accurate TaskStatus. Preserve timeout, refusal, invalid-output, termination, installation, launch, parsing, and model API errors. Do not set Benchmark correct or score in the Harness, and do not turn evaluator failure into Harness failure. A process exit code of zero is not automatically a correct result. Ensure the public answer is assigned to RunResult.final_answer; a Benchmark must not need to recover it from a Harness-private artifact. Support only reproducible installation strategies: a pinned preinstalled image, controlled installation before restricted execution, or an isolated driver-side optional extra. Do not assume every image has a package manager or compiler, and do not broaden the run network policy to make installation convenient. Inject model, judge, search, and provider credentials through supported Environment or configuration mechanisms. Recursively redact them from commands, files, logs, trajectories, URLs, exceptions, dataclass representations, and persisted metadata. Give each limit one owner: Harness command timeout limits the agent process, step limits bound the agent loop, model-client retries handle request transport, and runtime retries repeat a failed task attempt. Unknown model pricing must follow the Harness cost contract and should not terminate a run when the user explicitly selected an ignore-errors or disabled-cost mode.

Diagnose Failures by Stage

For a compact real lifecycle and result adapter, read qwen3vl_gui.py. For an Environment-executed agent that installs, launches, parses a public final answer, and converts a trajectory, read naive_search_agent/harness.py.