> ## Documentation Index
> Fetch the complete documentation index at: https://codexceed.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# avatar.harness

Harness — the importable facade over the agent loop (§20, Phase 2.6).

`Harness` performs the default collaborator wiring the CLI used to hardcode in
`run_agent`: the tool registry, model client, context builder, verifier, and
permission policy. Every seam is overridable via a constructor kwarg, so a
downstream user can swap a provider, a tool set, the verification contract, or
the permission gate without touching the core. `from_env` builds a
`HarnessConfig` from the environment (the same `AVATAR_*` settings the CLI uses)
and applies the same defaults.

The facade only *assembles* the run; the `AgentRunner` still owns all state
mutation (invariant #2). `run` returns the terminal `TaskState`, identical to
what `cli.run_agent` returns, so the CLI can delegate straight through.

## Classes

### `Harness`

The importable facade: default wiring with every collaborator overridable.

Each collaborator defaults to the standard MVP wiring (the same the CLI used to
construct inline) and is replaceable via its kwarg — the Principle-A seam. The
facade constructs nothing on the run's behalf that the runner should own; it only
assembles the collaborators.

Args:
config: Harness config (budgets, workspace root, commands, denylist).
model: Model client; a default `OpenAIModelClient(config)` if omitted.
tools: The active `ToolRegistry`; `default_registry()` if omitted.
verifier: The completion verifier; `Verifier(config)` if omitted.
policy: The before-tool-call permission gate; the standard tier policy
(threaded with `config.sensitive_path_globs`) if omitted.
context\_builder: The per-turn context assembler; if omitted, a `ContextBuilder`
budgeted from the config's `AVATAR_CONTEXT_*` compaction fields.
emitter: The observation-only event emitter; a fresh `Emitter()` if omitted.

```python theme={null}
Harness(*, config: HarnessConfig, model: ModelClient | None = None, tools: ToolRegistry | None = None, verifier: Verifier | None = None, policy: PermissionPolicy | None = None, context_builder: ContextBuilder | None = None, emitter: Emitter | None = None) -> None
```

#### `Harness.arun(self, task: str, *, task_kind: Literal['edit', 'investigate', 'test_only'] = 'investigate', allow_dirty: bool = False) -> TaskState`

Async twin of `run` — the bare loop, for callers already on an event loop.

Use `session` instead when you also want the typed event stream + approval/cancel
controls; `arun` returns only the terminal state.

Args:
task: The natural-language task to run.
task\_kind: The verification contract to apply (`investigate` / `edit` / `test_only`).
allow\_dirty: When `True`, open the workspace despite uncommitted tracked changes (§15).

Returns:
The terminal `TaskState` after the loop settles.

#### `Harness.run(self, task: str, *, task_kind: Literal['edit', 'investigate', 'test_only'] = 'investigate', allow_dirty: bool = False) -> TaskState`

Run the agent loop over `task` synchronously and return the terminal `TaskState`.

The batch/library entry point; for an interactive UI use `session` (observation +
control) or `arun` (the bare async loop).

Args:
task: The natural-language task to run.
task\_kind: The verification contract to apply (`investigate` / `edit` / `test_only`).
allow\_dirty: When `True`, open the workspace despite uncommitted tracked changes (§15).

Returns:
The terminal `TaskState` after the loop settles.

#### `Harness.session(self, task: str, *, task_kind: Literal['edit', 'investigate', 'test_only'] = 'investigate', allow_dirty: bool = False, conversational: bool = False, advisory: bool = False, journal: JsonlEventJournal | None = None, unattended: bool = False) -> Session`

Open an interactive `Session` over `task` — the two-plane SDK surface (§13, §23).

Returns a not-yet-started session: drive it with `await session.run()` while
consuming `session.events()` (observation) and calling `session.resolve_approval()`
/ `session.cancel()` (control). This is the shape a TUI or autonomous wrapper binds to.

Args:
task: The natural-language task to run.
task\_kind: The verification contract to apply (`investigate` / `edit` / `test_only`).
allow\_dirty: When `True`, open the workspace despite uncommitted tracked changes (§15).
conversational: Terminal-boundary authority (§23.5, ADR-0046). The verifier steers in
either mode; `False` (default) pronounces repair exhaustion `failed`, `True` (the
interactive `ReplSession` default) defers to the human (`blocked`) at exhaustion.
A failed verdict is never laundered to `success`.
advisory: External-grading authority (ADR-0040 option A). `True` runs the verifier as
advisory — it reports but does not steer or gate, and a failed verdict is delivered
as `success` for a held-out grader (the eval success-probe) to judge. For callers
that grade externally (the eval harness); never the REPL. Precedes `conversational`.
journal: The write-ahead `JsonlEventJournal` for the run's typed events; `None`
(default) keeps the stream in memory only.
unattended: When `True`, the session auto-denies any tier-3/denylist `ask` instead
of awaiting a human — the right disposition for a batch/eval/autonomous run, where
blocking on a `resolve_approval` that never comes would deadlock the loop. `False`
(default) keeps the interactive path for a TUI/human at a REPL.

Returns:
A `Session` wrapping the run — observation out, control in.
