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.
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.