How to Use the OpenHuman Harness API to Embed the Agent in a Rust Binary

The OpenHuman Harness API exposes a builder-pattern façade that creates a fully-configured agent runtime, allowing you to embed the OpenHuman agent into any Rust binary with async methods for single-turn prompts and multi-turn conversations.

The tinyhumansai/openhuman repository distributes its core agent logic as a library crate named openhuman. The Harness type—re-exported at the crate root—serves as the primary entry point for programmatic use. Located in src/openhuman/agent/harness/mod.rs, this type provides a minimal, async API that mirrors the runtime used by the official desktop UI.

Understanding the Harness Architecture

The Harness type implements a builder-pattern lifecycle. Calling Harness::builder() returns a HarnessBuilder (defined in src/openhuman/agent/harness/session/builder/mod.rs) that captures configuration for the LLM provider, workspace persistence, and security policy. Once configured, invoking .build().await yields an initialized Harness instance ready to process prompts.

The builder accepts four critical configuration domains:

Configuring the Provider and Workspace

Selecting an LLM Provider

The Provider enum abstracts multiple LLM backends. Use Provider::openai_compatible() to target OpenAI or any OpenAI-compatible endpoint:

Provider::openai_compatible("https://api.openai.com/v1", "sk-…")
    .model("gpt-4o")

This logic lives in src/openhuman/agent/harness/provider.rs. The provider configuration supports custom endpoints, enabling use with local models or alternative cloud providers that adhere to the OpenAI API specification.

Choosing Workspace Persistence

The Workspace enum in src/openhuman/agent/harness/workspace.rs offers three variants:

  • Workspace::Ephemeral – Creates a temporary directory deleted on process exit, ideal for stateless automation
  • Workspace::Dir(path) – Pins a persistent location on disk for the action directory and tool outputs
  • Workspace::Inherit – Uses an existing workspace context

The workspace location determines where sandboxed tools write files and where the agent stores its artifact cache.

Setting Access Tiers

Security policies are enforced via the Access type in src/openhuman/agent/harness/access.rs:

  • Access::full() – Grants unrestricted tool usage and file system mutation
  • Access::supervised() – Requires confirmation for destructive actions
  • Access::readonly() – Blocks write operations entirely

The selected tier is encoded into the core's SecurityPolicy during build().

Running Single-Turn and Multi-Turn Conversations

Single-Turn Execution

For one-shot prompts, use harness.run(prompt).await, defined in src/openhuman/agent/harness/session/turn/mod.rs. This method initializes a session, sends the prompt, and returns the complete reply.

use openhuman::{Harness, Provider, Workspace, Access};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let harness = Harness::builder()
        .provider(
            Provider::openai_compatible(
                "https://api.openai.com/v1",
                std::env::var("OPENAI_API_KEY")?,
            )
            .model("gpt-4o-mini"),
        )
        .workspace(Workspace::Ephemeral)
        .access(Access::full())
        .backend_url("https://api.tinyhumans.ai")
        .build()
        .await?;

    let reply = harness.run("Summarize the OpenHuman architecture.").await?;
    println!("Agent reply:\n{}", reply.message);
    Ok(())
}

Multi-Turn Conversation Handling

For stateful interactions, harness.turn(prompt).await returns a TurnHandle that persists session context. The handle exposes methods to continue the conversation, inspect the session ID, or retrieve the full transcript.

use openhuman::{Harness, Provider, Workspace, Access, TurnHandle};

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    let harness = Harness::builder()
        .provider(
            Provider::openai_compatible(
                "https://api.openai.com/v1",
                std::env::var("OPENAI_API_KEY")?,
            )
            .model("gpt-4o"),
        )
        .workspace(Workspace::Dir("./my_workspace".into()))
        .access(Access::full())
        .build()
        .await?;

    let mut turn: TurnHandle = harness.run("You are a helpful assistant.").await?.into();

    for user_msg in [
        "What is the purpose of the `Harness` builder?",
        "Give me a concise example of tool usage.",
    ] {
        turn = turn.turn(user_msg).await?;
        println!("Assistant: {}", turn.reply().message);
    }

    let transcript = turn.transcript().await?;
    println!("Full transcript ({} turns):", transcript.len());
    for entry in transcript {
        println!("> {}", entry.message);
    }

    Ok(())
}

The transcript retrieval logic resides in src/openhuman/agent/harness/session/transcript.rs, while turn state management is handled in src/openhuman/agent/harness/session/turn/mod.rs.

Enabling Sandboxed Tool Execution

To execute sandboxed tools—such as the python_exec tool—set the OPENHUMAN_SANDBOX_MODE environment variable before building the harness. The sandbox context is managed in src/openhuman/agent/harness/sandbox_context.rs.

use openhuman::{Harness, Provider, Workspace, Access};

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    std::env::set_var("OPENHUMAN_SANDBOX_MODE", "sandboxed");

    let harness = Harness::builder()
        .provider(
            Provider::openai_compatible(
                "https://api.openai.com/v1",
                std::env::var("OPENAI_API_KEY")?,
            )
        )
        .workspace(Workspace::Ephemeral)
        .access(Access::full())
        .build()
        .await?;

    let reply = harness.run(
        "Run this Python code and return its output:\n```python\nprint(2 * 7)\n```"
    ).await?;

    println!("Tool output: {}", reply.message);
    Ok(())
}

Tool registration occurs in src/openhuman/tools/registry.rs, while result artifacts are processed through src/openhuman/agent/harness/tool_result_artifacts/mod.rs.

Summary

Embedding the OpenHuman agent requires minimal boilerplate:

  • Import openhuman::Harness from the crate root
  • Chain configuration methods on Harness::builder() to select providers, workspace type, and access tier
  • Await build() to instantiate the runtime
  • Use run() for single-turn requests or turn() for multi-turn sessions with transcript persistence
  • Enable OPENHUMAN_SANDBOX_MODE environment variable before instantiation to allow tool execution

Frequently Asked Questions

What is the difference between run() and turn() methods in the Harness API?

run() executes a single prompt within a new session and returns the final message, making it ideal for one-shot tasks. turn() initiates or continues a multi-turn conversation, returning a TurnHandle that preserves session state across multiple async calls and exposes the transcript history.

How do I persist agent state between process restarts?

Pass Workspace::Dir(path) to the builder instead of Workspace::Ephemeral. According to the implementation in src/openhuman/agent/harness/workspace.rs, this pins the action directory and artifact storage to a specific filesystem location, allowing the agent to resume from existing state on subsequent process launches.

Can I use the OpenHuman Harness API with local LLMs or only cloud providers?

You can use any OpenAI-compatible endpoint. The Provider::openai_compatible() method in src/openhuman/agent/harness/provider.rs accepts a custom base URL, enabling integration with local inference servers (such as Ollama or vLLM) that expose the OpenAI API schema, in addition to commercial cloud providers.

How do I restrict the agent's ability to modify files?

Set the access tier to Access::readonly() or Access::supervised() via the builder method defined in src/openhuman/agent/harness/access.rs. The readonly tier blocks all destructive tool invocations, while supervised requires explicit confirmation before executing write operations, encoding these constraints into the core SecurityPolicy.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →