agent-harness 0.4.0-alpha.3

Drive LLM coding agents — Claude Code, OpenAI Codex, and local Ollama / OpenAI-compatible models — from Rust behind one trait, with a normalized streaming event vocabulary. Bring your own agent too.
Documentation
agent-harness-0.4.0-alpha.3 has been yanked.

agent-harness

Drive LLM coding agents — Claude Code, OpenAI Codex, bob, and any ACP agent — from Rust behind one trait, with a single normalized streaming event vocabulary. Any OpenAI-compatible model — local Ollama or a hosted endpoint — runs on the built-in openai-compatible runtime (OpenHarness), which owns the agent loop in Rust. Bring your own agent too.

Imported as harness. One Harness trait, one RunEvent stream: your app learns the event shape once and never grows a per-agent parser — adding an agent is "write a parser into RunEvent," not "teach the UI another format."

# Pre-release: pin the exact version — cargo won't auto-select a pre-release.
agent-harness = "0.4.0-alpha.1"   # default: the Claude Code / Codex / bob adapters

# Add the opt-in backends — ACP agents (opencode / Gemini) + the local /
# OpenAI-compatible runtime:
agent-harness = { version = "0.4.0-alpha.1", features = ["acp", "openai-compatible"] }

# Or slim it to one adapter — no keychain SDK, no HTTP client:
agent-harness = { version = "0.4.0-alpha.1", default-features = false, features = ["claude"] }

Quickstart

Construct an agent, then run_channel streams normalized RunEvents. Swap the agent, keep the loop — that's the whole point.

Claude Code

use harness::{Claude, Harness, RunEvent, RunMode, RunRequest, RunTuning};

let (_handle, events) = Claude::new().run_channel(RunRequest {
    run_id: "1".into(),
    prompt: "Summarize the README in one sentence.".into(),
    cwd: None,
    mode: RunMode::Ask,
    tuning: RunTuning::default(),
    resume: None,
    attachments: Vec::new(),
})?;

for event in events {
    if let RunEvent::Text { delta, .. } = event {
        print!("{delta}");
    }
}

OpenAI Codex

Identical loop — construct Codex instead:

let (_handle, events) = harness::Codex::new().run_channel(request)?;

Open models — the openai-compatible feature

The claude/codex/acp adapters wrap an external agent. An OpenAI-compatible model has none to wrap, so the openai-compatible feature ships a runtime that is the agent — it speaks the OpenAI chat API and owns the tool loop (read/write/edit/bash/glob/grep + webfetch/websearch/todos/skills/subagents/MCP) in Rust, behind this same Harness trait. Think the Claude Code / Codex SDK, but for any open-source or OpenAI-compatible model (Ollama, OpenRouter, vLLM, …):

agent-harness = { version = "0.4", features = ["openai-compatible"] }
let ollama = harness::OpenHarness::ollama();   // or ::custom(OpenHarnessConfig { id, display_name, base_url, .. })
let (_handle, events) = ollama.run_channel(request)?;

Off by default (it pulls a blocking HTTP client + search libs), so the core crate stays lean — same RunEvent loop, no CLI required.

OpenCode, Gemini, Goose, and other ACP agents work via the acp feature — AcpHarness::opencode() spawns opencode acp and drives it over the Agent Client Protocol.

Bring your own agent

Harness only emits RunEvents, so an implementor can spawn a CLI or call an HTTP API — both fit. Register it alongside the built-ins; nothing to fork:

let registry = harness::Registry::new()
    .register(harness::Claude::new())
    .register(MyAgent::new());            // your own `impl Harness`

RunEvent derives Serialize with stable camelCase field names, so any transport (HTTP/SSE, an IPC channel) emits identical JSON.

What you get

  • Harness traitinfo / readiness / install / run / credential / login / list_models. Object-safe (Box<dyn Harness>).
  • RunEvent — text, thinking, tool start/end, plan, usage (with cache tokens), suggested edits, questions, lifecycle. Aligned with the Agent Client Protocol vocabulary.
  • Open Registry — compose built-ins and your own providers.

The subprocess engine is the standalone cli-stream crate.

Examples

Runnable demos (cargo run --example <name>):

  • run_prompt — Claude Code (default features).
  • run_acp — an ACP agent (opencode); add --features acp.
  • run_openai — an OpenAI-compatible / local model; add --features openai-compatible.
  • playground — a browser UI that streams a live run over SSE for any backend; add --features "openai-compatible acp".

License

MIT OR Apache-2.0.