# Connect your agents

**A coding agent — a *harness* — is the program that actually edits your code inside a task.** hlix ships its own, **Hlix**, and runs it when a task names nothing else. It also drives the coders you already use — Claude Code, Codex, Cursor Agent — through the same pipeline, with the same isolation, context, and QA around them.

That choice is per task, and it is genuinely yours: bring the coder you want, and the orchestration around it does not change. Swapping the harness changes who writes the code; it changes nothing else.

## Prerequisites

- A project in your workspace ([import one](/cli/import/), or create one in the dashboard)
- A provider API key for the harness you intend to use, stored as a [workspace provider config](#your-keys-your-models)
- Agency `owner` or `admin` to change the workspace-wide harness; `manage` access on a project to change that project's

## The harness matrix

Six harnesses are defined. **Availability is a property of this deployment**, not a plan tier: an unavailable harness has no adapter here, and hlix refuses to substitute a different one for it.

| Harness | Driver | Credentials | Model choice | Available |
| --- | --- | --- | --- | --- |
| `hlix` | native, backend-driven | none of its own | from the orchestration catalog | **Yes** — the default |
| `claude-code` | ACP driver, in-sandbox | `ANTHROPIC_API_KEY` | per run | **Yes** |
| `codex` | ACP driver, in-sandbox | `OPENAI_API_KEY` | per run | **Yes** |
| `cursor-agent` | ACP driver, in-sandbox | `CURSOR_API_KEY` | fixed by the CLI | **Yes** |
| `openai-agents` | ACP driver, in-sandbox | any one of OpenAI, Anthropic, Google, OpenRouter | per run | No adapter yet |
| `aider` | ACP driver, in-sandbox | any one of OpenAI, Anthropic, Google, OpenRouter | per run | No adapter yet |

`claude-code`, `codex`, and `cursor-agent` report structured results *and* usage, though `cursor-agent` takes no model selection. `aider` reports a structured result without usage. `hlix` reports a structured result and takes its model from the orchestration catalog's `worker` role rather than from a per-run coder selection.
**Unavailable is refused, never substituted:** A harness with no adapter fails the dispatch **before** the workspace is acquired, with an error naming the setting that chose it:

```text
Cannot run this task: this project's harness preference names the harness
"aider", which has no adapter in this deployment. Available harnesses:
claude-code, codex, cursor-agent, hlix. Change or clear that setting
and run the task again.
```

Falling back to a working harness would run your work through a coder you did not pick, and nothing in the output would say so.

## Two driver kinds

The `Driver` column above is the one distinction that changes how a task executes.

**Hlix (native).** hlix runs its own agent from the backend against the Coding Workspace's command and filesystem tools. The backend provisions the worktree first, wraps the turn so it survives a backend restart, and owns the git work itself — the agent only edits files; the runner stages, commits, and pushes. Because hlix drives the turn directly, this is the harness whose reasoning you can watch live and whose conversation is recoverable afterwards. It takes no coder credential of its own; its model is the `worker` role from the orchestration catalog.

**ACP driver (external coder).** hlix launches an in-sandbox driver process, which dispatches the real coder CLI — `claude`, `codex`, `cursor-agent` — inside the project's [Coding Workspace](/concepts/coding-workspace/). The driver creates the task's git worktree in-pod, runs the coder there, commits, and for a repository-backed project force-pushes the task branch. The coder's exit code is the completion signal; there is no polling. Per-task isolation is real: each task gets its own configuration directory, and automatic memory carry-over between tasks is disabled.

Both paths land in the same place: a task branch inside one persistent per-project sandbox, one git worktree per task, merged and reviewed by the same pipeline.

## How a harness is chosen

One cascade decides, in one pure function, so the answer cannot differ between the settings screen and the dispatcher. The first rung that names a harness wins.

| # | Rung | Set by | `source` |
| --- | --- | --- | --- |
| 1 | Per-dispatch override | the deployment's `SANDBOX_AGENT` environment variable | `task-override` |
| 2 | The task's assigned agent | `hlix agent import --runtime …`, or an agent record's runtime | `agent` |
| 3 | Project preference | `PUT /v1/api/model-config/harness/:projectId` | `project-preference` |
| 4 | Workspace preference | `PUT /v1/api/model-config/harness` | `organization-preference` |
| 5 | Inferred from the planner model | a non-Anthropic `planner` model preference | `planner-model` |
| 6 | System default | nothing above matched | `system-default` → `hlix` |

Availability is **not** consulted while resolving. Resolution answers "what did the configuration ask for"; the availability check answers "can this deployment run it". Folding them together would let an unavailable preference fall silently through to the next rung.

### Pin a harness

Workspace-wide (`owner` or `admin` only — it names the coder for every project):

```bash
curl -sS -X PUT "$HLIX_BASE_URL/v1/api/model-config/harness" \
  -H "x-api-key: $HLIX_API_KEY" \
  -H "X-Organization-Id: $HLIX_WORKSPACE_ID" \
  -H 'content-type: application/json' \
  -d '{"harness":"codex"}'
```

For one project (requires `manage` access; client collaborators are refused):

```bash
curl -sS -X PUT "$HLIX_BASE_URL/v1/api/model-config/harness/$PROJECT_ID" \
  -H "x-api-key: $HLIX_API_KEY" \
  -H "X-Organization-Id: $HLIX_WORKSPACE_ID" \
  -H 'content-type: application/json' \
  -d '{"harness":"claude-code"}'
```

`DELETE` on either path clears that rung, so the project inherits the workspace's setting and the workspace falls back to the default. Both writes are recorded in the [audit log](/reference/cli/) as `harness_preference.changed`.

Only an **available** harness can be stored. A preference naming `aider` or `openai-agents` is rejected at the API with a validation error rather than saved as a setting whose only effect would be to fail every future dispatch.

### Ask what would actually run

```bash
curl -sS "$HLIX_BASE_URL/v1/api/model-config/harness/$PROJECT_ID/resolved" \
  -H "x-api-key: $HLIX_API_KEY" \
  -H "X-Organization-Id: $HLIX_WORKSPACE_ID"
```

Expected result:

```json
{
  "harness": "codex",
  "source": "project-preference",
  "scopeId": "6f1c2a9e-8f0b-4a7d-9d33-2f0b1c7e5a41",
  "scopeFallback": true
}
```

`source` is the point — a bare `"codex"` would send you hunting through four settings. `scopeFallback: true` is honest bookkeeping: this endpoint answers for the two rungs a settings screen can change, plus the default beneath them. It knows nothing about a dispatch override or a task's assigned agent, because neither exists until a task is dispatched.

## Your keys, your models

hlix calls models with **your** provider keys. Store one per provider:

```bash
curl -sS -X POST "$HLIX_BASE_URL/v1/api/providers" \
  -H "x-api-key: $HLIX_API_KEY" \
  -H "X-Organization-Id: $HLIX_WORKSPACE_ID" \
  -H 'content-type: application/json' \
  -d '{"provider":"anthropic","apiKey":"sk-ant-…","enabled":true}'
```

Accepted providers are `anthropic`, `openai`, `openrouter`, `google_genai`, and `local`. The key is verified against that provider's model-list endpoint before it is trusted, stored encrypted, and never returned by any read endpoint. `PATCH /v1/api/providers/:provider` rotates it. `local` additionally requires `baseUrl`, so a self-hosted gateway can be pointed at.

Separately, `/v1/api/model-config/:role` sets which model each orchestration role uses — `classifier`, `interviewer`, `planner`, `reviewer`, `chat`, `orchestrator`, `worker`, `triage` — with optional `temperature` and `maxTokens`. The dashboard exposes the same list under **Settings → AI models**, one row per role. `triage` is the model that judges whether an incoming request becomes a cycle and which risk lane it enters; see [Triage](/concepts/triage/).

**How a harness gets its credentials** depends on its driver:

- `claude-code` receives the workspace's **Anthropic** configuration, including a custom base URL when one is set.
- `codex`, `cursor-agent`, and the other external coders receive credentials normalised to the OpenAI-compatible protocol, resolved from the provider named by your **`planner` model preference**. Pointing `planner` at an OpenRouter identifier is therefore what routes a non-Claude coder through OpenRouter.
- `hlix` uses no coder credential of its own; its model comes from the orchestration catalog for the `worker` role.
**The planner model has a second job:** Rung 5 of the cascade infers a harness from the planner model's provider, because `claude-code` cannot drive a non-Anthropic model. That inference resolves to `openai-agents`, which has **no adapter yet**.

Rung 5 sits *above* the system default, so a workspace with a non-Anthropic planner and no explicit harness preference reaches that unavailable inference rather than falling through to `hlix`, and the dispatch fails with the availability error above. Set an explicit workspace or project harness — including `hlix` — and the inference never runs.

## Let your local agent operate hlix

The [official hlix MCP server](/mcp/) points the other way: it connects Cursor, Claude Code, Codex, or Windsurf on your machine to the same workspace-scoped projects, tasks, cycles, comments, and review evidence the CLI exposes. It is read-only unless you explicitly enable its bounded additive tools.

That is the outbound direction. Importing a third-party MCP server *into* a cloud project is a separate, inbound flow — see [Import skills, agents & MCP](/cli/resources/).

## Common questions

**Can I run a different harness per project?** Yes — that is rung 3 of the cascade. `PUT /v1/api/model-config/harness/:projectId` sets it for one project; the workspace-wide setting is the fallback beneath it.

**Why did hlix refuse instead of falling back to a harness that works?** Because substituting a coder you did not choose is a correctness failure you cannot see in the output. The refusal names the setting that selected the unavailable harness, so there is one field to change.

**Does hlix see my provider keys?** It stores them encrypted, uses them to call models on your behalf, and never returns them from any read endpoint. Verification happens against the provider's own model-list endpoint before a key is trusted.

**Which key does a non-Claude coder use?** The one belonging to the provider named by your `planner` model preference — those coders receive credentials normalised to the OpenAI-compatible protocol. `claude-code` is the exception: it always receives the workspace's Anthropic configuration.

**What runs if I never choose a harness?** `hlix`. A task that names no agent runs on our own harness, which needs no coder credential of its own.

**Why would I pick an external coder over `hlix`?** Because you already rely on one. If your team's standards, prompts, or muscle memory are built around Claude Code or Codex, pin it and keep them — the isolation, review evidence, and QA around the task are identical either way. That is the point of the choice being per task.

**What does `hlix` do that an external CLI cannot?** hlix drives its turn directly rather than shelling out to a coder process, so its reasoning streams live into the worker card and its prompts and answers are readable afterwards in a cycle's Transcript. It is also the only harness where hlix owns the git operations rather than the in-sandbox driver.

## If a task will not start

- ``Cannot run this task: … names the harness "aider", which has no adapter in this deployment.`` — clear or change the setting the message names. Do not expect a fallback.
- The dispatch fails with a provider `401` — the workspace has no key for the provider that harness needs. `claude-code` needs Anthropic; the other external coders follow the `planner` preference's provider.
- `403` — ``Changing the workspace harness is restricted to owners and admins.`` The workspace-wide setting names the coder for every project, so it is agency administration.
- `403` on a project harness write — a client collaborator cannot change which coder runs a project, even with steer access.
- A validation error storing a preference — the harness is one of the two without an adapter. The API refuses to store a setting that could only fail later.
- `404` — ``Project not found.`` The project ID belongs to another workspace, or does not exist. Check `X-Organization-Id`.

## Next steps

[The Coding Workspace](/concepts/coding-workspace/)
  [Agents & orchestration](/concepts/agents/)
  [Official MCP server](/mcp/)
  [Import skills, agents & MCP](/cli/resources/)