# How to Use the OpenHuman Harness API for Agent Turns as Library Calls

> Learn to use the OpenHuman Harness API to integrate agent turns as library calls directly into your application. Streamline your agent development with this powerful Rust builder API.

- Repository: [Tiny Humans/openhuman](https://github.com/tinyhumansai/openhuman)
- Tags: how-to-guide
- Published: 2026-08-24

---

**The OpenHuman `Harness` type provides a Rust builder API that lets you embed the agent turn engine directly into your application without the desktop UI, using `run()` for first turns and `turn().session().send()` for follow-ups.**

The `tinyhumansai/openhuman` repository ships a Rust core library (`openhuman-core`) that houses the agent turn engine, persistence layer, and business logic. By leveraging the `Harness` API, you can configure LLM providers, autonomy tiers, and workspace persistence entirely through code, making it possible to integrate OpenHuman into servers, automation scripts, or other Rust applications without launching the Tauri desktop shell.

## Initializing the Harness with the Builder Pattern

The public entry point for embedding the core is the **`Harness`** type, located in [`src/embed/harness/mod.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/embed/harness/mod.rs). You construct a `Harness` once using **`Harness::builder()`**, which exposes a fluent API for configuring every aspect of the agent environment:

- **`provider`** – Configures the LLM endpoint (OpenAI, Claude, etc.) and authentication. Typical value: `Provider::openai_compatible(base_url, api_key).model("gpt-4o")`.
- **`workspace`** – Defines file system persistence. Use `Workspace::Ephemeral` for throw-away runs or `Workspace::Dir(PathBuf::from("./my_ws"))` for persistent storage.
- **`access`** – Sets the autonomy tier (read-only, supervised, or full). Use `Access::full()` to allow tool calls, file writes, and network requests.
- **`session`** – Provides a credential isolation identifier. Example: `Session::local("demo-session")`.
- **`backend_url`** – Base URL for TinyHumans cloud services (accounts, billing). Optional for offline usage.
- **`mcp`** – Optional MCP (Message-Consumer-Process) server configuration for external tool hosting.
- **`skills_dir`** – Path to directory containing SKILL.md bundles the agent can invoke.

The builder implementation resides in [`src/embed/harness/builder.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/embed/harness/builder.rs). When you invoke `.build().await?`, the harness initializes a Tokio runtime, in-process RPC server, and all domain services (memory, tools, security) pre-wired and ready to accept turns.

## Running Agent Turns

Once constructed, the `Harness` exposes two primary methods for driving conversation turns, as implemented in [`src/embed/harness/mod.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/embed/harness/mod.rs):

**`run(prompt)`** – Creates a new session, sends the initial prompt to the agent, and returns a `TurnResult`. This method handles session initialization automatically and is ideal for one-off interactions or when you want to start a fresh conversation context.

**`turn(prompt).session(&session_id).send().await?`** – Continues an existing session, preserving tool state, memory, and conversation history. You must pass the **`session_id`** returned by the previous turn's `TurnResult` to maintain continuity.

The `TurnResult` struct contains the assistant's reply text, any tool call results, and the session ID required for subsequent calls.

## Practical Implementation Examples

### Minimal One-Off Prompt

For ephemeral, single-turn usage without persistence, configure an ephemeral workspace and run a prompt immediately:

```rust
use openhuman_core::embed::harness::{Harness, Provider, Workspace, Access, Session};

#[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-mini"))
        .workspace(Workspace::Ephemeral)
        .access(Access::full())
        .session(Session::local("demo-session"))
        .build()
        .await?;

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

```

*Relevant source*: `Harness` definition – [`src/embed/harness/mod.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/embed/harness/mod.rs); Builder API – [`src/embed/harness/builder.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/embed/harness/builder.rs).

### Persistent Workspace with Multi-Turn Sessions

To maintain state across multiple turns and allow file system operations, use a persistent workspace and explicitly chain turns using the session ID:

```rust
use openhuman_core::embed::harness::{
    Harness, Provider, Workspace, Access, Session,
};

#[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())
        .session(Session::local("my-session"))
        .build()
        .await?;

    let turn1 = harness
        .run("Create a file called `hello.txt` with the text “Hello, OpenHuman!”.")
        .await?;
    println!("Turn 1 reply: {}", turn1.output);

    let turn2 = harness
        .turn("Read the contents of `hello.txt`.")
        .session(&turn1.session_id)
        .send()
        .await?;
    println!("Turn 2 reply: {}", turn2.output);
    Ok(())
}

```

*Key files*: Workspace handling – [`src/openhuman/config/schema/types.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/config/schema/types.rs); Access tier definition – [`src/openhuman/security/policy.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/security/policy.rs); Session persistence – [`src/openhuman/agent/session_db/mod.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/agent/session_db/mod.rs).

### Concurrent Server Usage Pattern

For embedding the harness in a web server or any concurrent context, wrap the instance in `Arc<Mutex<>>` to safely share across multiple request handlers:

```rust
use std::sync::Arc;
use tokio::sync::Mutex;
use openhuman_core::embed::harness::{
    Harness, Provider, Workspace, Access, Session,
};

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

    async fn handle_prompt(
        harness: Arc<Mutex<Harness>>, 
        prompt: String, 
        session_opt: Option<String>
    ) -> String {
        let mut h = harness.lock().await;
        let result = if let Some(sess) = session_opt {
            h.turn(&prompt).session(&sess).send().await
        } else {
            h.run(&prompt).await
        }
        .expect("turn failed");
        
        format!("{}\n---session-id---\n{}", result.output, result.session_id)
    }

    Ok(())
}

```

*Important files*: Core entry point – [`src/lib.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/lib.rs); Session handling – [`src/openhuman/agent/session_db/mod.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/agent/session_db/mod.rs).

## Summary

- Use **`Harness::builder()`** (defined in [`src/embed/harness/builder.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/embed/harness/builder.rs)) to configure the LLM provider, workspace persistence, autonomy tier, and session isolation before initialization.
- Invoke **`run(prompt)`** to execute first-turn operations or start new sessions automatically.
- Continue existing conversations with **`turn(prompt).session(&id).send()`**, passing the `session_id` from the previous `TurnResult` to preserve context.
- Configure **`Workspace::Ephemeral`** for stateless execution or **`Workspace::Dir(path)`** for persistent file system access and tool state.
- Wrap the `Harness` in **`Arc<Mutex<>>`** when integrating into concurrent servers or multi-threaded applications.

## Frequently Asked Questions

### How do I configure the LLM provider when using the Harness API?

Pass **`Provider::openai_compatible(base_url, api_key)`** to the builder's `provider()` method, then chain `.model("gpt-4o")` or your desired model identifier. This configuration is processed in [`src/embed/harness/builder.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/embed/harness/builder.rs) and supports OpenAI, Claude, or any OpenAI-compatible endpoint by adjusting the base URL and authentication headers.

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

**`run()`** initializes a new session automatically and is designed for first-turn execution or stateless interactions. **`turn()`** requires you to specify an existing session ID via the `.session()` builder method and is used for follow-up messages that must retain conversation history and tool state. Both methods are implemented in [`src/embed/harness/mod.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/embed/harness/mod.rs) and return a `TurnResult` containing the assistant's output and the active session ID.

### Can I use the Harness API without writing files to disk?

Yes. Configure the builder with **`Workspace::Ephemeral`** instead of `Workspace::Dir`. This setting creates a temporary in-memory workspace that is discarded when the `Harness` is dropped, making it ideal for serverless functions or CI/CD pipelines where persistence is undesirable. The workspace configuration is defined in [`src/openhuman/config/schema/types.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/config/schema/types.rs).

### Is the Harness API thread-safe for high-concurrency web servers?

The `Harness` type itself is not `Clone` or inherently thread-safe, but you can safely share it across async tasks by wrapping it in **`Arc<Mutex<Harness>>`**. Lock the mutex at the request entry point to serialize access to the agent turn engine, then release it after the `TurnResult` is obtained. This pattern allows multiple HTTP handler threads to utilize a single initialized harness without race conditions.