Harness adapters
An adapter maps a harness tree into the files expected by a third-party coding-agent CLI and binds that harness to the served model. The harness and model together form the running agent. The tree never names a file path; the adapter does. Reef bundles four, one per third-party coding-agent CLI.
|
Adapter |
Config targets |
Install pin |
|---|---|---|
|
pi |
primary → pi-agent/settings.json, models → pi-agent/models.json |
npm @earendil-works/pi-coding-agent 0.84.2 |
|
opencode |
primary → opencode/opencode.json |
npm opencode-ai 1.18.18 |
|
claude |
primary → claude/settings.json |
npm @anthropic-ai/claude-code 2.1.257 |
|
dsh |
primary → dsh/profiles/headless/cordis.patch.yml, env → dsh/.env |
npm @deepseek-ai/dsh 0.1.2-alpha.5 |
The dsh adapter runs DeepSeek Harness headless (dsh --profile headless "<task>") with its whole home relocated by DSH_HOME. dsh composes its plugin tree from bundle layers plus one user patch layer, a YAML list of entries addressed by plugin id, so its primary config target is an object keyed by plugin id ({"agent-loop": {"config": {...}}}, or {"disabled": true}) that the adapter's quirks emit as that list. A string starting with !!js `` becomes a js expression, the form dsh's own bundles use. The adapter's defaults keep the session log uncompressed and the telemetry and the LLM title call disabled, and a composition that flips any of them is refused at render. Rules render to dsh's user global ``AGENTS.md; skills to skills/<name>/SKILL.md (dsh needs YAML frontmatter with name and description, synthesized when the node text has none); an agent_command renders as a user invocable skill (disable-model-invocation: true, run as /name) under the second skill root DSH_AGENTS_HOME, the only command surface dsh has; a code_extension renders as a plugin module the patch layer inserts by relative path. The model binding declares an llm-pi-ai route whose key is named by apiKeyEnv and supplied through the env target, dsh's .env launch environment layer.
The descriptor #
One descriptor.yaml declares how a tree configures and starts a running agent.
namethe adapter's idbinarythe executable an episode runsargvthe argument list for one headless prompt; {prompt} is substitutedfileswhere each node kind renders, like skills/{name}/SKILL.mdtrajectorythe format and path of the session log Reef reads backenvvariables pointing the agent's state under the episode root; {root} is substitutedinstallthe one-command install pin: kind (npm only), package, version, and binary_path under the install prefixmodel_bindingper API dialect (openai, anthropic), the config nodes Reef appends at evaluation time; {base_url}, {api_key}, and {model} substitute into string valuescleanup_whitelistfiles the agent itself writes at boot or during the run, tolerated instead of read as driftquirksan optional module for adapter-specific render checks and boot mutationsConnect a new agent #
To connect an agent that has no adapter yet:
-
The file it reads configuration from becomes a files.config target.
-
The command line that runs one prompt headless becomes binary and argv.
-
The path and format of its session log become trajectory. A new format subclasses TrajectoryReader (reef/harness/trajectory.py) and registers with @register_trajectory_reader.
-
The files its first boot creates go in cleanup_whitelist, so a fresh episode root is treated as clean. dir/** tolerates a whole subtree (session storage, node_modules); any other entry is a glob against the root-relative path, so anchor a single file with its full path, like pi-agent/auth.json. A bare directory name matches nothing under it.
reef/harness/descriptor.py validates every descriptor at load, and the bundled adapters under reef/harness/adapters/ are complete references. A third-party adapter registers on the reef.harness_adapters entry-point group. evolution.version_check: true in the recipe config writes an update prompt into the tree and ships for pi only. The prompt offers to run the update or skip in interactive mode and prints the instructions in headless mode. An opencode recipe that sets it refuses to boot. An evolved tree is adapter-specific: config node contents follow each adapter's schema.