# OpenHuman Embed

> Embed OpenHuman runtimes and independently configured agents in Rust applications.

- [Embedding OpenHuman](docs/gitbooks/en/developing/embed/README.md): `openhuman-embed` runs the OpenHuman core inside your Rust program. Your host owns the user interface, server transport and authentication flow; the core owns agent turns, memory, tools, approvals and execution policy. Calling its typed facades does not require a JSON-RPC server or the desktop shell.
- [Embedding API reference](docs/gitbooks/en/developing/embed/api-reference.md): Generate usable local Rust API documentation with `cargo doc -p openhuman-embed --no-deps --open`. Include `--features mcp,skills,channels,storage-sqlite` for the facade gates used by the offline examples, or select the feature set your host actually builds. For all exported optional surfaces use `--all-features`.
- [Embedding architecture](docs/gitbooks/en/developing/embed/architecture.md): Embed is the library facade above `openhuman-core`. The TinyHumans layer installs the backend SDK transport and owns product login/session behavior; the RPC layer adds JSON-RPC, server listeners and shipped host boot. A Rust embedder can stay at the facade when it owns those concerns itself.
- [Concepts](docs/gitbooks/en/developing/embed/concepts/README.md): Start with Runtime and agents to understand which settings are shared. Then follow the lifecycle of a request through provider selection, tools, approvals and its durable session. The scope tables distinguish runtime composition from the state a host must isolate per agent or profile.
- [Access and approvals](docs/gitbooks/en/developing/embed/concepts/access-approvals.md): `Access` groups permission level, origin, standing grants and trusted roots. Choose an origin and tier together: a trusted local host request and an external channel message are different security contexts. `Access::full()` configures both access fields. A sandbox is an additional execution boundary, configured through `AgentDefinitionSpec`.
- [Agents](docs/gitbooks/en/developing/embed/concepts/agents.md): Register an `AgentSpec` with `Runtime::agent`. Its ID selects the agent's home, durable session identity and addressed cron/channel work. An `Agent` clone refers to the same instance; it does not copy state or start another core. Runtime-wide credentials remain shared, so two agents are not two authenticated customers.
- [Channels](docs/gitbooks/en/developing/embed/concepts/channels.md): `Runtime::channels` creates listeners addressed to a live agent ID. A listener owns its transport task; dropping or stopping it ends that listener. The agent supplies the prompt, provider and tools used for every incoming message instead of handing messages to an operator agent.
- [Completer](docs/gitbooks/en/developing/embed/concepts/completer.md): Use `Completer` for one-shot text or structured generation when you do not need an agent, its tool loop or persistent transcript. It takes an explicit route and a `CompletionRequest`; the caller supplies the model and messages for every request.
- [Cron](docs/gitbooks/en/developing/embed/concepts/cron.md): The typed `Cron` facade creates named jobs with `JobSpec` and `JobSchedule`. `upsert` preserves a job's identity when the same name is configured again; `runs` exposes execution history. An agent-targeted job resolves the configured agent ID and executes with that agent's provider, prompt, tools and derived context.
- [Errors](docs/gitbooks/en/developing/embed/concepts/errors.md): Match the error type at its boundary: `RuntimeError` describes boot/configuration, `AgentError` describes registration/layout, and `CoreError` describes typed facade calls and turns. A missing compiled or selected domain is `CoreError::Unavailable`; it is a capability decision the host can reflect in its UI.
- [Hooks and seams](docs/gitbooks/en/developing/embed/concepts/hooks-seams.md): Runtime builders accept host-owned ports for storage, sessions, memory, tools and controller extensions. These are composition seams: the host supplies an implementation of the owning library's contract, while OpenHuman retains access policy, approvals and lifecycle.
- [MCP](docs/gitbooks/en/developing/embed/concepts/mcp.md): MCP connects a native agent turn to tools served over an MCP protocol transport. Configure a server on `AgentSpec::mcp`, with its transport, authentication and allowed tool names. Another agent on the same runtime does not inherit that server configuration.
- [Memory](docs/gitbooks/en/developing/embed/concepts/memory.md): `Runtime::memory(namespace)` returns a facade bound to that namespace. The caller establishes scope before sending record data; namespace identity is not taken from model arguments or record metadata. Learning and deletion use the selected memory engine contract.
- [Observability](docs/gitbooks/en/developing/embed/concepts/observability.md): Subscribe with `Runtime::events` before starting work you want to observe. Each event has a sequence number and runtime identity, with an agent ID and per-call turn ID where applicable. Repeated calls on the same durable session receive different turn IDs; the session ID is not the observation ID.
- [Profiles and SaaS](docs/gitbooks/en/developing/embed/concepts/profiles-saas.md): Ordinary runtime agents share an operator's configuration path and credentials. Use `ProfileRuntime` when one server serves different authenticated users who must not share those resources. Provision a profile, install its credential, and retain a `ProfileHandle` while serving that user's requests.
- [Providers](docs/gitbooks/en/developing/embed/concepts/providers.md): A runtime provider is the default inference choice for its agents. An agent can override it with an OpenAI-compatible route or a native `ChatModel<()>` through `Provider::custom`. The `providers` facade exposes the request/response contracts so custom adapters do not need to implement an HTTP server.
- [Runtime defaults](docs/gitbooks/en/developing/embed/concepts/runtime-defaults.md): The runtime's default provider, access policy and `ModelDefaults` reduce repeated setup. `AgentSpec` can override those defaults for an analyst, a writing agent or another workload without changing its siblings. Temperature and token limits are forwarded to the selected native model request, rather than being merely descriptive builder values.
- [Runtime](docs/gitbooks/en/developing/embed/concepts/runtime.md): A `Runtime` boots the in-process core once. It owns the selected domains, background services, module host and runtime defaults. Its agents share that core while using separately derived contexts. Build one runtime and pass an `Arc<Runtime>` to the parts of your application that register agents.
- [Skills](docs/gitbooks/en/developing/embed/concepts/skills.md): A skill bundle contains `SKILL.md` plus any referenced resources. `AgentSpec::skills_dir` copies bundles into the agent's `agents/<id>/skills/` tree. Copying is intentional: discovery rejects symlinked bundles, so linking a shared directory does not install it.
- [Testing](docs/gitbooks/en/developing/embed/concepts/testing.md): The runnable examples default to loopback fixtures. They assert request bodies, tool results, file effects, transcript scope and event ordering; a successful Rust compilation alone does not prove those behaviors. Live example execution is explicitly opt-in through the documented environment variables.
- [Tools](docs/gitbooks/en/developing/embed/concepts/tools.md): An agent's `ToolScopeSpec` chooses built-ins, named tools or `HostOnly`. Host-only mode starts with the host's advertised tools, requires an explicit bare prompt, and does not inherit an orchestrator tool catalog. This is useful when your application already owns read-only data access or tightly scoped actions.
- [Turns and sessions](docs/gitbooks/en/developing/embed/concepts/turns-sessions.md): `Agent::run` collects a turn. `Agent::turn` lets you set an explicit session, per-turn options, progress sink and cancellation before sending. Reuse the returned session identity for follow-up messages that should see the same transcript. Two calls on the same durable session remain distinct turn executions and receive distinct observation IDs.
- [Embedding FAQ](docs/gitbooks/en/developing/embed/faq.md): An ordinary embed process supports one `Runtime`; a second build returns `AlreadyRunning`. Add agents to the existing runtime. A multi-user host uses `ProfileRuntime` instead and needs a process dedicated to that mode. See runtime and profiles.
- [Embedding guides](docs/gitbooks/en/developing/embed/guides/README.md): Choose the guide that matches your host:
- [CLI chatbot](docs/gitbooks/en/developing/embed/guides/cli-chatbot.md): Keep one `Runtime` alive for the process and create an `Agent` for the conversation. The executable example starts with a single message, checks the reply, and prints it. A CLI input loop can send subsequent messages through the same agent; choose a stable thread identity when your host exposes separate conversations.
- [Deploy a server](docs/gitbooks/en/developing/embed/guides/deploy-server.md): Your HTTP host can keep a runtime alive and invoke its agents directly. The executable example binds a loopback listener, handles one request, and sends back the agent's reply. It exercises the request path without requiring an RPC server dependency in `openhuman-embed`.
- [Lean and headless hosts](docs/gitbooks/en/developing/embed/guides/lean-headless.md): `RuntimeBuilder::lean()` selects the agent, memory, and inference families without background services. Runtime module selection controls what is active; Cargo features control what is compiled. A lean preset alone does not shed compiled dependencies.
- [Test with a mock backend](docs/gitbooks/en/developing/embed/guides/mock-backend-testing.md): Run `node scripts/run-embed-examples.mjs` from the repository root. The runner builds and executes every example with the union of required features, clears inherited live-example credentials and operator workspace settings, and requires each program's `EXAMPLE_OK` marker after its assertions.
- [Multiple agents](docs/gitbooks/en/developing/embed/guides/multi-agent.md): A runtime owns shared services and credentials. Each `AgentSpec` selects its prompt, model route, access policy, and `action_dir`. Internal agent state belongs to its agent home; the action directory is where acting tools operate.
- [SaaS with multiple tenants](docs/gitbooks/en/developing/embed/guides/saas-multi-tenant.md): Use `ProfileRuntime` for a process serving multiple users. Provision each profile, install that profile's credential, and open a handle to its workspace. Reusing a thread id in two profiles does not share their conversation history.
- [Installation](docs/gitbooks/en/developing/embed/installation.md): `openhuman-embed` is currently an unpublished workspace crate. Use a local path dependency when developing in this repository, or a Git dependency on `https://github.com/tinyhumansai/openhuman` with `package = "openhuman-embed"`. Pin a reviewed Git revision for a deployed application; the nested vendored libraries are part of that revision's build inputs.
- [Embedding integrations](docs/gitbooks/en/developing/embed/integrations/README.md): An embedded runtime composes the capabilities its host supplies. Start with the adapter your application needs:
- [Channels](docs/gitbooks/en/developing/embed/integrations/channels.md): Enable the `channels` Cargo feature to use channel adapters. Your host configures the channel credential and binds incoming messages to an agent. In the Telegram example, the Bot API endpoint is a loopback stub, so updates and replies exercise the adapter without contacting Telegram.
- [Composio](docs/gitbooks/en/developing/embed/integrations/composio.md): Composio calls use the host backend transport. Install that transport before booting a managed runtime; see TinyHumans managed inference. An explicit inference provider route supplies model inference, not the connector transport.
- [Local and custom models](docs/gitbooks/en/developing/embed/integrations/local-models.md): For a local server that implements chat completions, use `Provider::openai_compatible` with its API root and model id. The constructor requires a bearer string even when a particular local server ignores it. Set the endpoint explicitly rather than relying on another user's installed configuration. The OpenAI-compatible example controls can point a live example at that local server.
- [MCP servers](docs/gitbooks/en/developing/embed/integrations/mcp.md): Enable the `mcp` Cargo feature and declare a server with `AgentSpec::mcp`. `McpServer::http` takes a registered name and endpoint; `allow_tools` narrows the remote tool list. Server configuration and its connections belong to the agent that declared them.
- [OpenAI-compatible providers and BYOK](docs/gitbooks/en/developing/embed/integrations/openai-compatible.md): Construct a route with `Provider::openai_compatible(base_url, api_key).model(model_id)`, then set it on the runtime or an individual agent. Pass the API root, such as a provider's `/v1` URL; the adapter appends `/chat/completions`. Supply an explicit model id so the route can register that model for its workload roles.
- [Storage drivers](docs/gitbooks/en/developing/embed/integrations/storage-drivers.md): Enable `storage-sqlite` for SQLite or `storage-mongodb` for MongoDB on your Embed dependency. These Cargo gates make the driver available; choosing a URL selects the runtime backend. Set `RuntimeConfig.storage.url`, pass a URL through `RuntimeBuilder::storage`, or set the `url` field in the `[storage]` section of the host's configuration. `OPENHUMAN_STORAGE_URL` is the environment override used by host configuration resolution.
- [TinyHumans managed inference](docs/gitbooks/en/developing/embed/integrations/tinyhumans-managed.md): Embed wraps the core; neither contains a TinyHumans backend client. A managed host boots through the builder in `openhuman-tinyhumans` (`openhuman_tinyhumans::RuntimeBuilder`), or calls `openhuman_tinyhumans::install`. That layer owns the transport, the authenticated client, the hosted routes and the hosted providers, and it plugs them into the core through the core's generic registries (chat models, voice, tool sources, the channel callback and memory engines). A host can register its own provider in the same registries. The connected host layer also owns session login. `RuntimeBuilder::backend_url` selects a URL; it does not connect anything.
- [Web search](docs/gitbooks/en/developing/embed/integrations/web-search.md): An agent's web-search tools (`web_search_tool`, `web_answer_tool`, `web_contents_tool`) run through the process-wide TinySearch module. Several agents in one runtime can each search with their own provider key: pin it on that agent's config, and it is never written to `config.toml`, the environment, or a log.
- [Quickstart](docs/gitbooks/en/developing/embed/quickstart.md): Start from a checkout of the repository and run `cargo run -p openhuman-embed --example run_turn`. The example boots an ephemeral runtime, creates its own local provider/backend fixtures and prints `hello from the stub`. It needs no inference account and does not contact a real backend.
- [Embedding troubleshooting](docs/gitbooks/en/developing/embed/troubleshooting.md): Use the typed error and the effective capability report together. A method that was compiled out is a different problem from a configured method with a provider failure. Keep credentials, user prompts and raw provider payloads out of routine diagnostics.
- [OpenHuman Embed](crates/openhuman-embed/README.md): A typed Rust facade for running the OpenHuman core inside your application: one Runtime per process, with independently configured Agents.
- [Embedding OpenHuman](docs/gitbooks/en/developing/embedding.md): The embedding documentation now lives in the Embedding section.
- [Cookbook](docs/gitbooks/en/developing/embed/cookbook.md): Executable recipes are indexed from their source headers. Each entry records the declared run mode and any named Embed feature required by the example. Full source includes the surrounding setup and assertions.
- [Compiled feature matrix](docs/gitbooks/en/developing/embed/capability-matrix.md): This matrix comes from the default-feature compiled capability-report example. Runtime presets can narrow enabled domains and services; core Cargo features determine which implementations are available. Named Embed features also gate facade exports: enable `mcp` or `skills` explicitly for their public setters even when core defaults compile those implementations. Embed defaults enable the named `channels` feature. See the installation guide for these separate layers.
- [RuntimeBuilder setters](docs/gitbooks/en/developing/embed/builder-setters.md): Public consuming methods that return `Self`, extracted from the RuntimeBuilder implementations. Static host and weight presets, inspection methods and build/run methods are excluded.
- [API index](docs/gitbooks/en/developing/embed/api-index.md): Generate the complete local Rust API reference with `cargo doc -p openhuman-embed --all-features --no-deps --open`. This crate is currently unpublished; docs.rs is a future publication target. Names below are source-listed exports, including conditional Cargo-gated items; local rustdoc with your selected features describes availability. Canonical facade source.
- [Embed examples](crates/openhuman-embed/examples/README.md): Cookbook

<!-- Source: docs/gitbooks/en/developing/embed/README.md -->
---
description: "Run the OpenHuman core inside a Rust application with one Runtime and independently configured Agents."
---

# Embedding OpenHuman

`openhuman-embed` runs the OpenHuman core inside your Rust program. Your host owns the user interface, server transport and authentication flow; the core owns agent turns, memory, tools, approvals and execution policy. Calling its typed facades does not require a JSON-RPC server or the desktop shell.

Build one `Runtime` per process and register the agents your product needs. Each agent can choose its own provider, prompt, working folder, tool scope, skills and MCP servers. Agents share runtime credentials; use `ProfileRuntime` when separate customers need separate credentials and workspaces.

## Start here

- [Installation](installation.md): dependency, Cargo gates, runtime setup and configuration.
- [Quickstart](quickstart.md): run a verified offline turn, then choose a live provider explicitly.
- [Architecture](architecture.md): ownership, lifecycle and the library chain.
- [Concepts](concepts/README.md): the 18 public API concepts and their runtime/agent scope.
- [Guides](guides/README.md): chatbots, multiple agents, SaaS, server deployment and tests.
- [Integrations](integrations/README.md): inference, MCP, connectors, storage and channels.
- [Cookbook](cookbook.md): generated inventory of executable examples.
- [API reference](api-reference.md): local rustdoc, source links and generated builder knobs.

## Choose the right surface

| Requirement | Start with |
| --- | --- |
| One agent with a small host | `Harness` |
| Several independently configured agents | `Runtime` and `AgentSpec` |
| Stateless model generation | `Completer` |
| Authenticated users with isolated credentials and state | `ProfileRuntime` |
| Public JSON-RPC server or shipped desktop host | The `openhuman-rpc` host layer |

The snippets in this section are injected from compiled, offline-tested Rust examples. Their full source files include the surrounding setup. `pnpm docs:check` checks source drift, local links, the generated API inventory and the published English mirror.

See [FAQ](faq.md) for design choices and [troubleshooting](troubleshooting.md) for setup failures.

<!-- Source: docs/gitbooks/en/developing/embed/api-reference.md -->
---
description: "Use local rustdoc and source-derived indexes for the complete Embed public surface."
---

# Embedding API reference

Generate usable local Rust API documentation with `cargo doc -p openhuman-embed --no-deps --open`. Include `--features mcp,skills,channels,storage-sqlite` for the facade gates used by the offline examples, or select the feature set your host actually builds. For all exported optional surfaces use `--all-features`.

The crate is currently `publish = false`; docs.rs links are a publication metadata target, not a claim that a live package reference exists. Local rustdoc and [canonical facade source](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs) are the current complete reference.

## Generated inventories

- [API index](api-index.md): names exported by the public facade and a versioned runtime report.
- [Machine-readable API index](api-index.json): export names, builder signatures, capabilities and page paths.
- [RuntimeBuilder setters](builder-setters.md): current public consuming setters with source locations.
- [Compiled feature matrix](capability-matrix.md): the actual core gates from the default-feature capability-report executable.
- [Cookbook](cookbook.md): example titles, run modes and required named facade features from source headers.

The generated setter list is the authoritative knob inventory. It excludes static presets and borrowed inspection/build/run methods; those belong in local rustdoc. Core compiled gates, runtime module/domain selection and named facade exports are separate availability layers.

## Public surface by purpose

| Purpose | API starting point |
| --- | --- |
| Boot and inspect a host | `RuntimeBuilder`, `Runtime`, `RuntimeInfo` |
| Register and remove scoped agents | `AgentSpec`, `Agent`, `AgentDefinitionSpec`, `RemoveAgent` |
| Configure execution and decisions | `Access`, `SandboxModeSpec`, `Approvals`, `ApprovalHandler` |
| Execute or stream native turns | `Turn`, `TurnOutcome`, `TurnStream`, `StreamEvent` |
| Supply custom model adapters | `Provider` and `providers::ChatModel` |
| Stateless generation | `Completer`, `CompletionRequest`, structured response helpers |
| Typed non-turn domains | Memory and cron facades, config/auth/artifacts/chat surface |
| Replace infrastructure ports | `seams`, `session_store`, memory engine and provider contracts |
| Serve isolated customer profiles | `ProfileRuntime`, `ProfileHandle`, `SaasConfig` |

## Versioned capability data

<!-- BEGIN EMBED: crates/openhuman-embed/examples/capability_report.rs#capability_report -->

```rust
    let info = runtime.capabilities();
    assert_eq!(info.schema_version, 1);
    assert!(info.defaults.routed_provider);
    let serialized = serde_json::to_string(&info)?;
    assert!(!serialized.contains("sk-test"));
    assert!(!serialized.contains(&provider.uri()));
    assert!(!serialized.contains(&runtime.workspace_dir().display().to_string()));
    println!("{}", serde_json::to_string_pretty(&info)?);
```

<!-- END EMBED -->

[Complete runnable example](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/examples/capability_report.rs), including runtime construction, imports and fixture setup.

The report's schema version is explicit. Read its fields rather than inferring capabilities from preset names, and handle future schema changes deliberately. It omits endpoint, credential, prompt and workspace values so hosts can expose build metadata without exposing private configuration.

<!-- Source: docs/gitbooks/en/developing/embed/architecture.md -->
---
description: "The host composes infrastructure around one core; agents derive scoped contexts over its runtime."
---

# Embedding architecture

Embed is the library facade above `openhuman-core`. The TinyHumans layer installs the backend SDK transport and owns product login/session behavior; the RPC layer adds JSON-RPC, server listeners and shipped host boot. A Rust embedder can stay at the facade when it owns those concerns itself.

## Ownership boundaries

| Component | Owns |
| --- | --- |
| Your application | UI/HTTP transport, user authentication, supplied credentials, application tools and resource lifetimes |
| OpenHuman core and Embed facade | Runtime composition, per-agent context, policy, approvals, sandbox selection and typed domain calls |
| TinyAgents | Model/tool loop, parsing, dialects, durable sessions and transcript replay |
| TinyInference | Native model/provider contracts and generic ordered fallback |
| TinyTools | Shared tool traits and contract types |
| Storage and module libraries | Their backend/module implementations; OpenHuman adapts and orchestrates them |

The library chain is core → Embed → TinyHumans → RPC → app/CLI/TUI. Shipped hosts depend on the layer immediately below them and use its curated facades. Embed users should not build new integrations on doc-hidden `__host` internals.

## Runtime composition versus agent scope

Cargo features decide which implementations exist in an artifact. Runtime domains, services, tool groups and module selection narrow those compiled choices. Agent specs narrow the runtime further and add provider, access, prompt, skills, MCP and action-directory choices.

A turn dispatches through the agent's native context, not through JSON-RPC or an operator fallback. Recursive child contexts inherit the parent host overrides, including custom provider and hook composition, while independent sibling agents remain separately scoped.

<!-- BEGIN EMBED: crates/openhuman-embed/examples/two_agents.rs#two_agents -->

```rust
    let analyst_dir = tempfile::tempdir()?;
    let writer_dir = tempfile::tempdir()?;
    let analyst = runtime.agent(
        AgentSpec::new("analyst")
            .system_prompt("ANALYST_PROMPT: summarize documents")
            .action_dir(analyst_dir.path())
            .access(openhuman_embed::Access::readonly()),
    )?;
    let writer = runtime.agent(
        AgentSpec::new("writer")
            .system_prompt("WRITER_PROMPT: compose explanations")
            .action_dir(writer_dir.path())
            .access(openhuman_embed::Access::full()),
    )?;
    assert_ne!(analyst.action_dir(), writer.action_dir());
    assert_ne!(analyst.home_dir(), writer.home_dir());
    assert_ne!(analyst.workspace_dir(), analyst.action_dir());
    assert!(!analyst.run("Analyze").await?.reply.is_empty());
    assert!(!writer.run("Explain").await?.reply.is_empty());
    if support::offline() {
        let requests = support::chat_requests(&provider).await;
        assert_eq!(requests.len(), 2);
        assert!(String::from_utf8_lossy(&requests[0].body).contains("ANALYST_PROMPT"));
        assert!(!String::from_utf8_lossy(&requests[0].body).contains("WRITER_PROMPT"));
        assert!(String::from_utf8_lossy(&requests[1].body).contains("WRITER_PROMPT"));
    }
    println!("two distinct prompts and action workspaces verified");
```

<!-- END EMBED -->

[Complete runnable example](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/examples/two_agents.rs), including runtime construction, imports and fixture setup.

## Lifetime and isolation

An agent handle keeps the core alive after the runtime handle is dropped. Explicit removal marks that instance removed, cancels turns and callbacks, denies parked approvals and clears its state/MCP registration. The ID remains reserved through cleanup, including natural last-handle destruction and cancellation of a started removal. Pointer-checked release prevents an old retained instance from removing a newer registry entry.

Agent separation is not credential separation: all ordinary runtime agents share the operator's config path and credential root. Use [profiles](concepts/profiles-saas.md) for independently authenticated users. See [storage architecture](../architecture/storage.md) for scope/driver ownership and [agent harness architecture](../architecture/agent-harness.md) for session execution.

<!-- Source: docs/gitbooks/en/developing/embed/concepts/README.md -->
---
description: "The ownership and lifecycle rules behind the Embed public API."
---

# Concepts

Start with Runtime and agents to understand which settings are shared. Then follow the lifecycle of a request through provider selection, tools, approvals and its durable session. The scope tables distinguish runtime composition from the state a host must isolate per agent or profile.

- [Runtime](runtime.md): A Runtime owns one initialized core and the process-wide resources that its agents share.
- [Runtime defaults](runtime-defaults.md): Runtime defaults form the starting point for newly registered agents; explicit agent settings override them.
- [Agents](agents.md): An Agent combines one identity, derived context and independently configured tool/model behavior on a shared Runtime.
- [Access and approvals](access-approvals.md): Access configures turn identity and permission policy; the host chooses how parked requests receive decisions.
- [Providers](providers.md): A provider supplies a route or native ChatModel implementation, with agent overrides and optional ordered fallback.
- [Turns and sessions](turns-sessions.md): A turn is one native model/tool loop; a session is the durable identity that makes subsequent turns a conversation.
- [Tools](tools.md): Tool catalogs expose only the allowed execution surface; host tools use the same native Tool contract as built-in tools.
- [MCP](mcp.md): MCP servers add remote tool discovery and execution to one agent, with explicit server configuration and tool allowlists.
- [Skills](skills.md): Skills are copied bundles of instructions and resources discovered inside an agent’s own home.
- [Memory](memory.md): The typed memory facade binds a namespace before learning, retrieving or forgetting records.
- [Cron](cron.md): Cron provides durable named jobs, explicit run-now execution and job history addressed to registered agents.
- [Channels](channels.md): A channel listener binds external messages to one registered agent and keeps transport lifecycle under host control.
- [Profiles and SaaS](profiles-saas.md): ProfileRuntime isolates authenticated users by workspace, credentials, conversations and event streams.
- [Completer](completer.md): Completer sends stateless model requests without booting a Runtime or entering the agent tool loop.
- [Hooks and seams](hooks-seams.md): Host seams replace infrastructure ports; hooks observe or control selected turn and tool boundaries.
- [Observability](observability.md): Runtime metadata events track lifecycle and approvals; turn streams carry per-turn progress and the final result.
- [Testing](testing.md): Deterministic local providers and explicit observations prove behavior without contacting real inference or backend services.
- [Errors](errors.md): Typed errors distinguish build availability, expected domain states, host setup failures and removed agent instances.

<!-- Source: docs/gitbooks/en/developing/embed/concepts/access-approvals.md -->
---
description: "Access configures turn identity and permission policy; the host chooses how parked requests receive decisions."
---

# Access and approvals

`Access` groups permission level, origin, standing grants and trusted roots. Choose an origin and tier together: a trusted local host request and an external channel message are different security contexts. `Access::full()` configures both access fields. A sandbox is an additional execution boundary, configured through `AgentDefinitionSpec`.

The core autonomy policy is disabled by default. Set `config.autonomy.enabled` when you require its command classification, approval gate, allowlist and budget enforcement. Platform isolation and the credential/system-root safety floor have separate responsibilities; a tool group filter is not an operating-system sandbox.

## Runtime and agent scope

| Runtime responsibility | Agent or call responsibility |
| --- | --- |
| Autonomy policy defaults and installed approval gate | Origin, access tier, grants, trusted roots and scoped decisions |

## Verified example

<!-- BEGIN EMBED: crates/openhuman-embed/examples/approvals.rs#approvals -->

```rust
    let scratch = tempfile::tempdir()?;
    let marker = scratch.path().join("approved-marker");
    let write_provider = support::scripted_provider(
        vec![support::tool_call_completion(
            "shell",
            &serde_json::json!({"command": format!("touch {}", marker.display())}).to_string(),
        )],
        "approved",
    )
    .await;
    let agent = runtime.agent(
        AgentSpec::new("supervised")
            .provider(support::route(&write_provider, "fixture"))
            .action_dir(scratch.path())
            .access(
                openhuman_embed::Access::supervised()
                    .origin(openhuman_embed::AgentTurnOrigin::WebChat {
                        thread_id: "demo".into(),
                        client_id: "host".into(),
                        request_id: None,
                    })
                    .auto_approve(Vec::<String>::new())
                    .auto_approve_all(false)
                    .trust(
                        scratch.path().display().to_string(),
                        openhuman_embed::TrustedAccess::ReadWrite,
                    ),
            )
            .definition(
                AgentDefinitionSpec::new().tools(ToolScopeSpec::Named(vec!["shell".into()])),
            ),
    )?;
    let pending_agent = agent.clone();
    let turn = tokio::spawn(async move { pending_agent.run("Create a marker").await });
    let pending = support::eventually("approval", || {
        agent.approvals().pending().ok()?.into_iter().next()
    })
    .await;
    assert!(!marker.exists(), "execution waits for the host decision");
    assert_eq!(pending.agent_id.as_deref(), Some("supervised"));
    agent.approvals().decide(
        &pending.request_id,
        openhuman_embed::ApprovalDecision::ApproveOnce,
    )?;
    assert_eq!(turn.await??.reply, "approved");
    assert!(marker.exists());
    assert!(agent.approvals().pending()?.is_empty());
    println!("parked write executed only after approval");
```

<!-- END EMBED -->

[Complete runnable example](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/examples/approvals.rs). The full file includes imports, runtime setup and local fixtures used by this excerpt.

## Behavior to account for

The verified example enables autonomy before building the runtime. It starts a real shell write, observes that no marker exists while the call is parked, and creates it only after `ApproveOnce`. `agent.approvals()` lists and decides only that instance's requests. A removed instance returns an empty pending list and rejects decisions even if the same public ID has been reused.

For asynchronous integration use `AgentSpec::approval_handler` or retain the handle returned by `Agent::handle_approvals`. Dropping a subscription cancels its pending decision future; it does not silently approve the request. Agent removal cancels both builder and live callbacks. Continue with the [approval callback example](../cookbook.md#polling-and-callback-approvals-with-cancellation) and [sandbox guide](../guides/multi-agent.md).

<!-- Source: docs/gitbooks/en/developing/embed/concepts/agents.md -->
---
description: "An Agent combines one identity, derived context and independently configured tool/model behavior on a shared Runtime."
---

# Agents

Register an `AgentSpec` with `Runtime::agent`. Its ID selects the agent's home, durable session identity and addressed cron/channel work. An `Agent` clone refers to the same instance; it does not copy state or start another core. Runtime-wide credentials remain shared, so two agents are not two authenticated customers.

Keep `action_dir` separate from the internal workspace. The action directory is where tools act; the agent home stores internal transcripts, skills and other state. The example gives an analyst and a writer distinct folders and prompts while sharing one provider connection setup.

## Runtime and agent scope

| Runtime responsibility | Agent or call responsibility |
| --- | --- |
| Core, shared configuration path and registry capacity | ID, context, prompt, action directory, provider and per-agent stores |

## Verified example

<!-- BEGIN EMBED: crates/openhuman-embed/examples/two_agents.rs#two_agents -->

```rust
    let analyst_dir = tempfile::tempdir()?;
    let writer_dir = tempfile::tempdir()?;
    let analyst = runtime.agent(
        AgentSpec::new("analyst")
            .system_prompt("ANALYST_PROMPT: summarize documents")
            .action_dir(analyst_dir.path())
            .access(openhuman_embed::Access::readonly()),
    )?;
    let writer = runtime.agent(
        AgentSpec::new("writer")
            .system_prompt("WRITER_PROMPT: compose explanations")
            .action_dir(writer_dir.path())
            .access(openhuman_embed::Access::full()),
    )?;
    assert_ne!(analyst.action_dir(), writer.action_dir());
    assert_ne!(analyst.home_dir(), writer.home_dir());
    assert_ne!(analyst.workspace_dir(), analyst.action_dir());
    assert!(!analyst.run("Analyze").await?.reply.is_empty());
    assert!(!writer.run("Explain").await?.reply.is_empty());
    if support::offline() {
        let requests = support::chat_requests(&provider).await;
        assert_eq!(requests.len(), 2);
        assert!(String::from_utf8_lossy(&requests[0].body).contains("ANALYST_PROMPT"));
        assert!(!String::from_utf8_lossy(&requests[0].body).contains("WRITER_PROMPT"));
        assert!(String::from_utf8_lossy(&requests[1].body).contains("WRITER_PROMPT"));
    }
    println!("two distinct prompts and action workspaces verified");
```

<!-- END EMBED -->

[Complete runnable example](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/examples/two_agents.rs). The full file includes imports, runtime setup and local fixtures used by this excerpt.

## Behavior to account for

IDs stay reserved until teardown completes, including while the last handle's destructor is clearing state. `remove_agent(id).await` marks retained handles removed, cancels active turns, denies parked approvals and releases the ID after cleanup. Add `.purge()` only when the agent's persisted home should also be deleted. Cancelling a started removal still performs teardown and a requested purge.

An old polling handle or approval callback never follows a reused ID to its replacement. Active host tools cancel cooperatively and may finish their external work after removal; the host still owns those external lifetimes. See [turns and sessions](turns-sessions.md) and [access and approvals](access-approvals.md).

<!-- Source: docs/gitbooks/en/developing/embed/concepts/channels.md -->
---
description: "A channel listener binds external messages to one registered agent and keeps transport lifecycle under host control."
---

# Channels

`Runtime::channels` creates listeners addressed to a live agent ID. A listener owns its transport task; dropping or stopping it ends that listener. The agent supplies the prompt, provider and tools used for every incoming message instead of handing messages to an operator agent.

The named Embed `channels` feature is enabled by default. The runtime must also allow the channel family. Create listeners inside the Tokio runtime after their agents have been registered.

## Runtime and agent scope

| Runtime responsibility | Agent or call responsibility |
| --- | --- |
| Channel implementation, listener registry and runtime domain ceiling | Bound agent, conversation identity and external-message access context |

## Verified example

<!-- BEGIN EMBED: crates/openhuman-embed/examples/telegram.rs#telegram -->

```rust
    let bot = wiremock::MockServer::start().await;
    let token = "123456:EXAMPLE";
    let updates = format!("/bot{token}/getUpdates");
    wiremock::Mock::given(wiremock::matchers::path(updates.clone())).respond_with(
        wiremock::ResponseTemplate::new(200).set_body_json(serde_json::json!({"ok":true,"result":[{
            "update_id":1,"message":{"message_id":1,"date":0,"text":"hello channel",
            "from":{"id":99,"is_bot":false,"username":"alice"},"chat":{"id":4242,"type":"private"}}
        }]}))).up_to_n_times(1).with_priority(1).mount(&bot).await;
    wiremock::Mock::given(wiremock::matchers::path(updates))
        .respond_with(
            wiremock::ResponseTemplate::new(200)
                .set_body_json(serde_json::json!({"ok":true,"result":[]}))
                .set_delay(std::time::Duration::from_millis(100)),
        )
        .with_priority(2)
        .mount(&bot)
        .await;
    wiremock::Mock::given(wiremock::matchers::any())
        .respond_with(
            wiremock::ResponseTemplate::new(200)
                .set_body_json(serde_json::json!({"ok":true,"result":{
            "message_id":2,"id":1,"is_bot":true,"username":"example_bot"}})),
        )
        .with_priority(10)
        .mount(&bot)
        .await;
    std::env::set_var("OPENHUMAN_TELEGRAM_BOT_API_BASE", bot.uri());
    let agent = runtime.agent(
        AgentSpec::new("channel-agent").definition(
            AgentDefinitionSpec::new()
                .bare_prompt("Reply briefly.")
                .tools(ToolScopeSpec::HostOnly),
        ),
    )?;
    let listener = runtime.channels().telegram(
        openhuman_embed::TelegramChannelSpec::new(token, agent.id()).allowed_users(["alice"]),
    )?;
    let mut sent = false;
    for _ in 0..200 {
        let requests = bot.received_requests().await.unwrap_or_default();
        sent = requests.iter().any(|request| {
            request.url.path().ends_with("/sendMessage")
                && serde_json::from_slice::<serde_json::Value>(&request.body).is_ok_and(|body| {
                    body["chat_id"].to_string().contains("4242")
                        && body["text"]
                            .as_str()
                            .is_some_and(|text| text.contains("hello from the stub"))
                })
        });
        if sent {
            break;
        }
        tokio::time::sleep(std::time::Duration::from_millis(50)).await;
    }
    listener.stop();
    assert!(
        sent,
        "the bound agent must send a reply to the stub Bot API"
    );
    std::env::remove_var("OPENHUMAN_TELEGRAM_BOT_API_BASE");
    println!("Telegram update answered in the correct chat");
```

<!-- END EMBED -->

[Complete runnable example](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/examples/telegram.rs). The full file includes imports, runtime setup and local fixtures used by this excerpt.

## Behavior to account for

The verified Telegram example uses a local Bot API stub, so the channel test does not contact Telegram. Its live mode requires explicit example credentials and remains opt-in.

External messages use `ExternalChannel` origin and a read-only cap. Model-requested writes and other external effects are not parked for approval from the sender. Keep public-chat tools read-only and perform authorized side effects in host code or a separately configured job. When the bound agent is removed, channel work fails as unavailable instead of falling back to the operator. See [channel integration](../integrations/channels.md).

<!-- Source: docs/gitbooks/en/developing/embed/concepts/completer.md -->
---
description: "Completer sends stateless model requests without booting a Runtime or entering the agent tool loop."
---

# Completer

Use `Completer` for one-shot text or structured generation when you do not need an agent, its tool loop or persistent transcript. It takes an explicit route and a `CompletionRequest`; the caller supplies the model and messages for every request.

A standalone completer does not claim the process runtime slot. An agent-backed completion uses the agent's effective configuration but still has no implicit durable session. Keep conversation state in your application if you need several completion calls to form a conversation.

## Runtime and agent scope

| Runtime responsibility | Agent or call responsibility |
| --- | --- |
| No runtime required for a standalone routed Completer | Optional effective agent configuration; no implicit tools or transcript |

## Verified example

<!-- BEGIN EMBED: crates/openhuman-embed/examples/completer.rs#completer -->

```rust
    let route = support::example_provider(&provider)?;
    let completer = openhuman_embed::Completer::new(route.route().expect("explicit route").clone());
    let response = completer
        .complete(openhuman_embed::CompletionRequest::new(
            route.model_id().expect("explicit model"),
            vec![openhuman_embed::ChatMessage::user("Say hello")],
        ))
        .await?;
    assert!(!response.text.is_empty());
    if support::offline() {
        assert_eq!(response.text, "completion reply");
        assert_eq!(support::chat_requests(&provider).await.len(), 1);
    }
    println!("{}", response.text);
```

<!-- END EMBED -->

[Complete runnable example](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/examples/completer.rs). The full file includes imports, runtime setup and local fixtures used by this excerpt.

## Behavior to account for

The example checks that exactly one inference request was made and verifies the returned text. It uses a local provider fixture by default; the source contains the explicit opt-in live route path.

Choose `Agent` when a model should call tools, resume a durable thread or execute with an agent's access context. Choose the structured completion helpers when the host only needs a validated data result. For routes and transport failures see [providers](providers.md) and [errors](errors.md).

## Timeouts and fallback

Set `CompletionRequest::timeout_ms(120_000)` to bound each physical provider HTTP request to 120 seconds. The optional field defaults to unset and is omitted from serialized requests when absent, retaining the provider's transport defaults. Each structured repair attempt receives its own physical timeout; this is not a total completion or ladder deadline.

A physical timeout is an RPC transport failure, so an explicit `CompletionLadder` can try its next route. By contrast, `Completer::timeout(duration)` bounds the entire logical completion, including repair attempts, and returns terminal `DeadlineExceeded`. Cancellation also stops the ladder without fallback. You can use both bounds when the host needs separate physical and logical limits.

A timed-out physical attempt has unknown reported cost. Ladder aggregate cost stays unknown, and a budgeted call keeps its conservative reservation if no authoritative usage arrived. The remaining budget may therefore refuse a fallback before it sends another request. See the [routing guide](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/ROUTING.md) for the explicit ladder API.

## Provider dispatch observation

Attach `Completer::physical_observer` to receive one content-free start and terminal outcome for each provider invocation. The observer runs inside budget admission, so a request refused by the budget produces no dispatch event. A dropped in-flight request reports cancellation; terminal outcomes carry duration and provider-reported usage when available. The existing `CompletionObserver` remains a logical completion hook and still fires once for the whole call, including structured repair attempts.

<!-- Source: docs/gitbooks/en/developing/embed/concepts/cron.md -->
---
description: "Cron provides durable named jobs, explicit run-now execution and job history addressed to registered agents."
---

# Cron

The typed `Cron` facade creates named jobs with `JobSpec` and `JobSchedule`. `upsert` preserves a job's identity when the same name is configured again; `runs` exposes execution history. An agent-targeted job resolves the configured agent ID and executes with that agent's provider, prompt, tools and derived context.

Background scheduling is a runtime service choice. Explicit `run_now` is useful for tests and host-triggered execution without relying on wall-clock scheduling. Keep the agent registered while scheduled work should be available.

## Runtime and agent scope

| Runtime responsibility | Agent or call responsibility |
| --- | --- |
| Cron storage, scheduler service and system-job handlers | Addressed job target and the agent configuration used for its turn |

## Verified example

<!-- BEGIN EMBED: crates/openhuman-embed/examples/cron.rs#cron -->

```rust
    let agent = runtime.agent(AgentSpec::new("scheduled"))?;
    let cron = runtime.cron();
    let spec = openhuman_embed::JobSpec::agent(
        "daily",
        agent.id(),
        "Say hello",
        openhuman_embed::JobSchedule::Cron {
            expr: "0 0 * * *".into(),
            tz: None,
        },
    );
    let first = cron.upsert(spec.clone())?;
    assert_eq!(cron.upsert(spec)?.id, first.id);
    let run = cron.run_now("daily").await?;
    assert!(run.success, "{}", run.output);
    if support::offline() {
        assert!(run.output.contains("hello from the stub"));
    }
    assert_eq!(cron.runs("daily", 1)?[0].status, "ok");
    assert!(cron.remove("daily")?);
    assert!(cron.list()?.is_empty());
    println!("cron execution and history verified");
```

<!-- END EMBED -->

[Complete runnable example](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/examples/cron.rs). The full file includes imports, runtime setup and local fixtures used by this excerpt.

## Behavior to account for

The example checks stable upsert identity, executes a job immediately, inspects a successful run record, and removes the job. No test waits for a daily schedule to arrive.

Removing or dropping an agent deregisters its context, leaving addressed cron jobs dormant rather than silently running them as the operator. Hosts can register runtime system-job handlers for work they own outside a model turn. Review [runtime configuration](runtime-defaults.md) and [channels](channels.md) when mixing scheduled work with public chat input.

<!-- Source: docs/gitbooks/en/developing/embed/concepts/errors.md -->
---
description: "Typed errors distinguish build availability, expected domain states, host setup failures and removed agent instances."
---

# Errors

Match the error type at its boundary: `RuntimeError` describes boot/configuration, `AgentError` describes registration/layout, and `CoreError` describes typed facade calls and turns. A missing compiled or selected domain is `CoreError::Unavailable`; it is a capability decision the host can reflect in its UI.

A structured `CoreError::Domain` carries a stable kind and `expected_user_state`. Expected stale-session or missing-resource states should be shown as notices rather than treated as internal faults. `Decode` signals a facade/domain shape mismatch; retrying unchanged input does not repair it.

## Runtime and agent scope

| Runtime responsibility | Agent or call responsibility |
| --- | --- |
| Boot errors, unavailable domains and shared transport setup | Registration errors, scoped domain failures and AgentRemoved on retained handles |

## Verified example

<!-- BEGIN EMBED: crates/openhuman-embed/examples/sandbox_access.rs#sandbox_access -->

```rust
    let scratch = tempfile::tempdir()?;
    let marker = scratch.path().join("attempted-write");
    let attempted_write = support::tool_call_completion(
        "write_file",
        &serde_json::json!({
            "path": marker.display().to_string(), "content": "example"
        })
        .to_string(),
    );
    let read_provider = support::scripted_provider(vec![attempted_write], "write refused").await;
    let read = runtime.agent(
        AgentSpec::new("read")
            .provider(support::route(&read_provider, "fixture"))
            .action_dir(scratch.path())
            .access(openhuman_embed::Access::readonly())
            .definition(
                AgentDefinitionSpec::new().sandbox(openhuman_embed::SandboxModeSpec::ReadOnly),
            ),
    )?;
    let full = runtime.agent(AgentSpec::new("full").access(openhuman_embed::Access::full()))?;
    assert_ne!(read.config().autonomy.level, full.config().autonomy.level);
    assert!(!read.run("Write a marker").await?.reply.is_empty());
    assert!(
        !marker.exists(),
        "readonly sandbox must refuse the attempted write"
    );
    let requests = support::chat_requests(&read_provider).await;
    assert!(!support::tool_names(&requests[0]).contains(&"write_file".to_string()));
    println!("readonly sandbox refused a model-requested write");
```

<!-- END EMBED -->

[Complete runnable example](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/examples/sandbox_access.rs). The full file includes imports, runtime setup and local fixtures used by this excerpt.

## Behavior to account for

The example's refused write is handled as a tool result while the turn can still finish with a reply. Do not assume every rejected tool action is a failed turn. Conversely, a finished stream can contain an error; always inspect `StreamEvent::Finished`.

Bearer routes refuse non-TLS remote endpoints before sending credentials. `BACKEND_UNAVAILABLE` means the embed-only host has no backend transport installed; check the host layer before rotating keys. Agent removal makes later turns on retained handles return `AgentRemoved`, even when its ID now belongs to another instance. See [troubleshooting](../troubleshooting.md) and [provider integration](../integrations/openai-compatible.md).

<!-- Source: docs/gitbooks/en/developing/embed/concepts/hooks-seams.md -->
---
description: "Host seams replace infrastructure ports; hooks observe or control selected turn and tool boundaries."
---

# Hooks and seams

Runtime builders accept host-owned ports for storage, sessions, memory, tools and controller extensions. These are composition seams: the host supplies an implementation of the owning library's contract, while OpenHuman retains access policy, approvals and lifecycle.

Learning sinks and tool hooks can be registered runtime-wide or on one agent. Agent hooks compose with parent runtime hooks and do not leak into sibling contexts. Use the live API's registration key to replace or remove runtime registrations; changes apply to subsequent turns. Learning sinks receive the upstream `TurnSummary`, while tool hooks receive the turn's `OpenHumanRunContext` and tool event.

## Runtime and agent scope

| Runtime responsibility | Agent or call responsibility |
| --- | --- |
| Shared port implementations and keyed global hook registrations | Scoped hooks and host overrides inherited by recursive child contexts |

## Verified example

<!-- BEGIN EMBED: crates/openhuman-embed/examples/hooks.rs#hooks -->

```rust
    let count = std::sync::Arc::new(std::sync::atomic::AtomicUsize::new(0));
    let agent = runtime.agent(
        AgentSpec::new("hooked").post_turn_hook(std::sync::Arc::new(Counter(count.clone()))),
    )?;
    let sibling = runtime.agent(AgentSpec::new("sibling"))?;
    assert!(!agent.run("Hello hook").await?.reply.is_empty());
    support::eventually("post-turn hook", || {
        (count.load(std::sync::atomic::Ordering::SeqCst) == 1).then_some(())
    })
    .await;
    assert!(!sibling.run("Hello sibling").await?.reply.is_empty());
    assert_eq!(count.load(std::sync::atomic::Ordering::SeqCst), 1);
    println!("post-turn hook isolated to its agent");
```

<!-- END EMBED -->

[Complete runnable example](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/examples/hooks.rs). The full file includes imports, runtime setup and local fixtures used by this excerpt.

## Behavior to account for

The example verifies that an agent-local post-turn counter observes the configured agent and not its sibling. Runtime live add/remove behavior is demonstrated in the [runtime events example](../cookbook.md#runtime-lifecycle-events-and-live-hook-registration).

Removing or replacing a builder-installed named hook through the runtime API affects that registration too; the runtime tracks and restores prior process slots when its seam ownership ends. Keep hooks fast, cancellation-aware and free of sensitive logging. Use the generated [setter list](../builder-setters.md) and [API reference](../api-reference.md) for the actual port signatures.

<!-- Source: docs/gitbooks/en/developing/embed/concepts/mcp.md -->
---
description: "MCP servers add remote tool discovery and execution to one agent, with explicit server configuration and tool allowlists."
---

# MCP

MCP connects a native agent turn to tools served over an MCP protocol transport. Configure a server on `AgentSpec::mcp`, with its transport, authentication and allowed tool names. Another agent on the same runtime does not inherit that server configuration.

Enable the named Embed `mcp` Cargo feature for `McpServer` and the agent setter. The default core may already contain MCP code, but a core capability flag does not activate a separately gated Embed API. Runtime domain selection must also allow the MCP family.

## Runtime and agent scope

| Runtime responsibility | Agent or call responsibility |
| --- | --- |
| Compiled MCP implementation and runtime domain ceiling | Server configuration, auth, discovery and allowed remote tools |

## Verified example

<!-- BEGIN EMBED: crates/openhuman-embed/examples/mcp.rs#mcp -->

```rust
    let mcp = wiremock::MockServer::start().await;
    wiremock::Mock::given(wiremock::matchers::method("POST"))
        .respond_with(McpStub)
        .mount(&mcp)
        .await;
    let discovery = wiremock::MockServer::start().await;
    wiremock::Mock::given(wiremock::matchers::path("/v1/chat/completions"))
        .respond_with(ForecastModel(std::sync::atomic::AtomicUsize::new(0)))
        .mount(&discovery)
        .await;
    let agent = runtime.agent(
        AgentSpec::new("mcp-client")
            .provider(support::route(&discovery, "fixture"))
            .mcp(
                openhuman_embed::McpServer::http("forecast", format!("{}/mcp", mcp.uri()))
                    .allow_tools(["forecast"]),
            )
            .access(openhuman_embed::Access::full()),
    )?;
    assert_eq!(
        agent
            .run("List the forecast server tools, then call its forecast tool")
            .await?
            .reply,
        "sunny verified"
    );
    let requests = mcp.received_requests().await.expect("MCP requests");
    let methods: Vec<String> = requests
        .iter()
        .filter_map(|r| {
            serde_json::from_slice::<serde_json::Value>(&r.body).ok()?["method"]
                .as_str()
                .map(str::to_string)
        })
        .collect();
    assert!(methods.iter().any(|m| m == "initialize"), "{methods:?}");
    assert!(methods.iter().any(|m| m == "tools/list"), "{methods:?}");
    assert!(methods.iter().any(|m| m == "tools/call"), "{methods:?}");
    let requests = support::chat_requests(&discovery).await;
    assert!(support::tool_results(&requests[1]).contains("forecast"));
    assert!(support::tool_results(&requests[2]).contains("sunny"));
    println!("MCP handshake, discovery and remote execution verified");
```

<!-- END EMBED -->

[Complete runnable example](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/examples/mcp.rs). The full file includes imports, runtime setup and local fixtures used by this excerpt.

## Behavior to account for

The example uses a real loopback protocol stub: it verifies initialization, `tools/list`, and `tools/call`, then checks that the model saw the remote result. It does not call an external MCP service or require credentials.

Tool allowlists reduce the remote catalog before model disclosure. Server authentication remains host-owned input, and remote tool side effects still pass through the relevant execution/access policy. Removing the agent evicts its MCP host after old-instance teardown, before the ID can be reused. See the [MCP integration](../integrations/mcp.md) for deployment setup.

<!-- Source: docs/gitbooks/en/developing/embed/concepts/memory.md -->
---
description: "The typed memory facade binds a namespace before learning, retrieving or forgetting records."
---

# Memory

`Runtime::memory(namespace)` returns a facade bound to that namespace. The caller establishes scope before sending record data; namespace identity is not taken from model arguments or record metadata. Learning and deletion use the selected memory engine contract.

A custom `memory_engine` can replace the default engine for a host or test. An agent's `MemoryBinding` configures its own memory context, while the runtime owns engine availability. Scope namespaces separately from provider/model choices: changing inference does not move a record to another tenant.

## Runtime and agent scope

| Runtime responsibility | Agent or call responsibility |
| --- | --- |
| Memory engine seam and available memory domain | MemoryBinding, acting context and bound namespace |

## Verified example

<!-- BEGIN EMBED: crates/openhuman-embed/examples/memory.rs#memory -->

```rust
    let acme = runtime.memory("team:acme")?;
    let other = runtime.memory("team:other")?;
    let params = serde_json::from_value(serde_json::json!({"text":"Acme ships on Fridays"}))?;
    let learned = acme.learn(params).await?;
    let hits = acme.get(vec![learned.id.clone()]).await?;
    assert_eq!(hits.len(), 1);
    assert_eq!(hits[0].meta.namespace.to_string(), "team:acme");
    assert!(other.get(vec![learned.id.clone()]).await?.is_empty());
    assert_eq!(other.forget(vec![learned.id.clone()]).await?.forgotten, 0);
    assert_eq!(acme.forget(vec![learned.id]).await?.forgotten, 1);
    println!("memory learning, tenant isolation and deletion verified");
```

<!-- END EMBED -->

[Complete runnable example](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/examples/memory.rs). The full file includes imports, runtime setup and local fixtures used by this excerpt.

## Behavior to account for

The example uses the reference in-memory engine and proves that another namespace cannot retrieve or forget the first namespace's record. Forgetting succeeds only through the owning namespace. This is a scope test, not a persistence claim; choose a durable engine/backend for application state that must survive restarts.

Background conversation ingestion can run independently of a completed turn. Tests should disable unrelated learning/ingest activity when asserting ephemeral-workspace cleanup. See [runtime defaults](runtime-defaults.md), [storage drivers](../integrations/storage-drivers.md), and the [memory architecture](../../architecture/memory.md).

<!-- Source: docs/gitbooks/en/developing/embed/concepts/observability.md -->
---
description: "Runtime metadata events track lifecycle and approvals; turn streams carry per-turn progress and the final result."
---

# Observability

Subscribe with `Runtime::events` before starting work you want to observe. Each event has a sequence number and runtime identity, with an agent ID and per-call turn ID where applicable. Repeated calls on the same durable session receive different turn IDs; the session ID is not the observation ID.

Runtime events contain lifecycle metadata, not user prompts or model output. Detailed text/tool progress belongs to the explicitly requested turn stream. A bounded event receiver reports lag explicitly rather than silently claiming that it delivered every event.

## Runtime and agent scope

| Runtime responsibility | Agent or call responsibility |
| --- | --- |
| Runtime sequence, content-safe lifecycle feed and registered-agent filter | Per-call turn correlation and opt-in text/tool progress stream |

## Verified example

<!-- BEGIN EMBED: crates/openhuman-embed/examples/runtime_events.rs#runtime_events -->

```rust
    let mut events = runtime.events();
    let count = Arc::new(AtomicUsize::new(0));
    runtime.post_turn_hook("live-counter", Some(Arc::new(Counter(count.clone()))));
    let agent = runtime.agent(AgentSpec::new("observed"))?;
    assert_eq!(agent.run("private payload one").await?.reply, "event reply");
    support::eventually("runtime hook", || {
        (count.load(Ordering::SeqCst) == 1).then_some(())
    })
    .await;
    runtime.post_turn_hook("live-counter", None);
    assert_eq!(agent.run("private payload two").await?.reply, "event reply");
    assert_eq!(
        count.load(Ordering::SeqCst),
        1,
        "removed hook stops observing turns"
    );
    runtime.remove_agent(agent.id()).await?;
    let mut previous = 0;
    let (mut added, mut started, mut ended) = (0, 0, 0);
    loop {
        let event =
            tokio::time::timeout(std::time::Duration::from_secs(5), events.recv()).await??;
        assert!(event.sequence > previous);
        previous = event.sequence;
        assert_eq!(event.agent_id.as_deref(), Some("observed"));
        assert!(!serde_json::to_string(&event)?.contains("private payload"));
        match event.kind {
            RuntimeEventKind::AgentAdded => added += 1,
            RuntimeEventKind::TurnStarted => {
                assert!(event.turn_id.is_some());
                started += 1;
            }
            RuntimeEventKind::TurnEnded { success } => {
                assert!(success);
                ended += 1;
            }
            RuntimeEventKind::AgentRemoved => break,
            _ => {}
        }
    }
    assert_eq!((added, started, ended), (1, 2, 2));
```

<!-- END EMBED -->

[Complete runnable example](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/examples/runtime_events.rs). The full file includes imports, runtime setup and local fixtures used by this excerpt.

## Behavior to account for

The example checks ordered add/start/end/remove events and confirms that private prompt text is absent from serialized metadata. Approval events are filtered to agents registered on this runtime. Correlation follows the effective origin thread; a request's later decision keeps its original turn correlation after the turn has ended.

When simultaneous turns share an origin thread, approval correlation can be ambiguous and its turn ID is absent rather than guessed. Handle receiver lag by refreshing state, and do not send text from `AgentProgress` to content-free analytics. See [turns and sessions](turns-sessions.md), [approvals](access-approvals.md), and [testing](testing.md).

<!-- Source: docs/gitbooks/en/developing/embed/concepts/profiles-saas.md -->
---
description: "ProfileRuntime isolates authenticated users by workspace, credentials, conversations and event streams."
---

# Profiles and SaaS

Ordinary runtime agents share an operator's configuration path and credentials. Use `ProfileRuntime` when one server serves different authenticated users who must not share those resources. Provision a profile, install its credential, and retain a `ProfileHandle` while serving that user's requests.

Profile mode claims the process separately from ordinary `Runtime` and `Harness`. Its root must satisfy the boot guard: absolute, existing, outside the operator's workspace and not world-writable. Remove single-user workspace/token environment variables before booting a profile server.

## Runtime and agent scope

| Runtime responsibility | Agent or call responsibility |
| --- | --- |
| One profile host, shared compiled core and managed backend transport | Per-profile credential, workspace, threads, memory and event access |

## Verified example

<!-- BEGIN EMBED: crates/openhuman-embed/examples/profiles.rs#profiles -->

```rust
    let profiles = openhuman_embed::ProfileRuntime::build(config).await?;
    let mut handles = Vec::new();
    for (user, text) in [("alice", "Alice private note"), ("bob", "Bob private note")] {
        let profile = profiles.provision(user).await?;
        profiles
            .set_credential(
                &profile.profile_id,
                openhuman_embed::profiles::ProfileCredentialKind::ApiKey,
                &format!("{user}-fixture"),
            )
            .await?;
        let handle = profiles.open(user).await?;
        let reply = handle.chat("shared-thread", text).await?;
        assert!(reply.text.contains(text), "{}", reply.text);
        handles.push(handle);
    }
    assert_ne!(handles[0].workspace_dir(), handles[1].workspace_dir());
    for (index, own, other) in [
        (0, "Alice private note", "Bob private note"),
        (1, "Bob private note", "Alice private note"),
    ] {
        let messages = handles[index].messages("shared-thread").await?;
        let text = serde_json::to_string(&messages)?;
        assert!(text.contains(own));
        assert!(!text.contains(other));
    }
    drop(handles);
    profiles.shutdown().await;
    println!("two profiles keep same-thread conversations separate");
```

<!-- END EMBED -->

[Complete runnable example](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/examples/profiles.rs). The full file includes imports, runtime setup and local fixtures used by this excerpt.

## Behavior to account for

The example creates Alice and Bob with the same thread name and verifies that each transcript contains only its own note. Handles keep their profiles open; release/idle eviction can close a profile only after those handles are dropped.

Production managed calls need the TinyHumans transport installed by the host layer. `ProfileHandle::call` exposes the reviewed user surface rather than arbitrary operator controllers. Profiles do not make a shared file-system action directory safe automatically; provision external application resources per user too. See [SaaS deployment guide](../guides/saas-multi-tenant.md) and [SaaS architecture](../../saas-profiles.md).

<!-- Source: docs/gitbooks/en/developing/embed/concepts/providers.md -->
---
description: "A provider supplies a route or native ChatModel implementation, with agent overrides and optional ordered fallback."
---

# Providers

A runtime provider is the default inference choice for its agents. An agent can override it with an OpenAI-compatible route or a native `ChatModel<()>` through `Provider::custom`. The `providers` facade exposes the request/response contracts so custom adapters do not need to implement an HTTP server.

Named workload roles bind an adapter to selections such as `hint:coding`. The example proves that the role-pinned model bypasses the primary fallback chain. These pins are explicit host configuration; they do not ask the model to decide its own provider.

## Runtime and agent scope

| Runtime responsibility | Agent or call responsibility |
| --- | --- |
| Default route/model and native adapter chain | Provider override, model selection and workload hint |

## Verified example

<!-- BEGIN EMBED: crates/openhuman-embed/examples/custom_provider.rs#custom_provider -->

```rust
    let provider = Provider::custom_with_fallback(
        Arc::new(LocalModel {
            reply: None,
            calls: failed.clone(),
        }),
        vec![Arc::new(LocalModel {
            reply: Some("fallback answer"),
            calls: fallback.clone(),
        })],
    )
    .model("local-custom")
    .role(
        "coding",
        Arc::new(LocalModel {
            reply: Some("coding answer"),
            calls: pinned.clone(),
        }),
    );
    let runtime = Runtime::builder()
        .config(support::offline_config())
        .backend_url(backend.uri())
        .provider(provider)
        .build()
        .await?;
    let definition = || {
        AgentDefinitionSpec::new()
            .bare_prompt("Reply briefly")
            .tools(ToolScopeSpec::HostOnly)
    };
    let regular = runtime.agent(AgentSpec::new("fallback-agent").definition(definition()))?;
    assert_eq!(regular.run("Hello").await?.reply, "fallback answer");
    assert_eq!(failed.load(Ordering::SeqCst), 1);
    assert_eq!(fallback.load(Ordering::SeqCst), 1);
    let coding = runtime.agent(
        AgentSpec::new("coding-agent")
            .model("hint:coding")
            .definition(definition()),
    )?;
    assert_eq!(
        coding.run("Explain a function").await?.reply,
        "coding answer"
    );
    assert_eq!(pinned.load(Ordering::SeqCst), 1);
    assert_eq!(
        failed.load(Ordering::SeqCst),
        1,
        "coding bypasses the primary chain"
    );
```

<!-- END EMBED -->

[Complete runnable example](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/examples/custom_provider.rs). The full file includes imports, runtime setup and local fixtures used by this excerpt.

## Behavior to account for

Ordered fallback advances only on eligible provider failures before visible output. Validation or unsupported-input failures stop the chain. Once text, reasoning or tool-call content becomes visible, the chosen stream keeps its identity and a later failure is reported without switching providers. Requests, options, correlation metadata and cancellation remain attached to the selected adapter.

Use HTTPS when sending bearer credentials; loopback HTTP is supported for local test/model servers. Managed inference needs the TinyHumans host transport and a runtime credential. See [OpenAI-compatible integration](../integrations/openai-compatible.md), [managed integration](../integrations/tinyhumans-managed.md), and [local models](../integrations/local-models.md).

<!-- Source: docs/gitbooks/en/developing/embed/concepts/runtime-defaults.md -->
---
description: "Runtime defaults form the starting point for newly registered agents; explicit agent settings override them."
---

# Runtime defaults

The runtime's default provider, access policy and `ModelDefaults` reduce repeated setup. `AgentSpec` can override those defaults for an analyst, a writing agent or another workload without changing its siblings. Temperature and token limits are forwarded to the selected native model request, rather than being merely descriptive builder values.

`Runtime::defaults()` returns a snapshot. Runtime default setters affect agents created afterward; existing agents retain the configuration they were built with. Configure an explicit agent override when its behavior must remain independent of future runtime defaults.

## Runtime and agent scope

| Runtime responsibility | Agent or call responsibility |
| --- | --- |
| Default provider, access and generation settings | Explicit override captured when AgentSpec is registered |

## Verified example

<!-- BEGIN EMBED: crates/openhuman-embed/examples/defaults_overrides.rs#defaults_overrides -->

```rust
    let inherited = runtime.agent(AgentSpec::new("inherited"))?;
    let override_provider = support::provider("override reply").await;
    let overridden = runtime.agent(
        AgentSpec::new("overridden")
            .provider(support::route(&override_provider, "override-model"))
            .access(openhuman_embed::Access::readonly())
            .model_defaults(openhuman_embed::ModelDefaults {
                temperature: Some(0.8),
                max_tokens: Some(128),
                ..Default::default()
            }),
    )?;
    let inherited_reply = inherited.run("Hello default").await?;
    let overridden_reply = overridden.run("Hello override").await?;
    if support::offline() {
        assert_eq!(inherited_reply.reply, "hello from the stub");
        let requests = support::chat_requests(&provider).await;
        let body: serde_json::Value = serde_json::from_slice(&requests[0].body)?;
        assert_eq!(body["model"], "fixture");
        assert_eq!(body["temperature"], 0.4);
        assert_eq!(body["max_tokens"], 256);
    }
    assert_eq!(overridden_reply.reply, "override reply");
    let requests = support::chat_requests(&override_provider).await;
    let body: serde_json::Value = serde_json::from_slice(&requests[0].body)?;
    assert_eq!(body["model"], "override-model");
    assert_eq!(body["temperature"], 0.8);
    assert_eq!(body["max_tokens"], 128);
    assert_eq!(runtime.defaults().model.temperature, Some(0.4));
    println!("default and override routes answered independently");
```

<!-- END EMBED -->

[Complete runnable example](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/examples/defaults_overrides.rs). The full file includes imports, runtime setup and local fixtures used by this excerpt.

## Behavior to account for

The example checks the request bodies actually received by both providers, including model, temperature and maximum tokens. This distinction matters when testing a configuration change: a correct defaults snapshot alone does not prove that inference received those values.

Use the generated [builder setters](../builder-setters.md) for the current knob list. [Configuration and installation](../installation.md) explain runtime configuration discovery; [providers](providers.md) cover route selection and native models.

<!-- Source: docs/gitbooks/en/developing/embed/concepts/runtime.md -->
---
description: "A Runtime owns one initialized core and the process-wide resources that its agents share."
---

# Runtime

A `Runtime` boots the in-process core once. It owns the selected domains, background services, module host and runtime defaults. Its agents share that core while using separately derived contexts. Build one runtime and pass an `Arc<Runtime>` to the parts of your application that register agents.

The process guard rejects a second runtime with `RuntimeError::AlreadyRunning`. Independent customer credentials and workspaces require [profiles](profiles-saas.md), rather than several ordinary runtimes. `Harness` is the one-agent shorthand and exposes its underlying runtime and agent if you later need more agents.

## Runtime and agent scope

| Runtime responsibility | Agent or call responsibility |
| --- | --- |
| Cargo gates and selected domains/services | Provider, prompt, working folder and tool scope |

## Verified example

<!-- BEGIN EMBED: crates/openhuman-embed/examples/capability_report.rs#capability_report -->

```rust
    let info = runtime.capabilities();
    assert_eq!(info.schema_version, 1);
    assert!(info.defaults.routed_provider);
    let serialized = serde_json::to_string(&info)?;
    assert!(!serialized.contains("sk-test"));
    assert!(!serialized.contains(&provider.uri()));
    assert!(!serialized.contains(&runtime.workspace_dir().display().to_string()));
    println!("{}", serde_json::to_string_pretty(&info)?);
```

<!-- END EMBED -->

[Complete runnable example](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/examples/capability_report.rs). The full file includes imports, runtime setup and local fixtures used by this excerpt.

## Behavior to account for

`RuntimeBuilder::describe()` reports a plan before boot; `Runtime::capabilities()` reports resolved configuration afterward. The versioned metadata excludes credentials, endpoints, prompts and workspace paths. Use its `configuration_resolved` flag when deciding whether to treat an inspection result as effective configuration.

Dropping the runtime handle does not tear down the core while an agent still holds it. Drop listeners, agents and the runtime before restarting the host in the same process. See [architecture](../architecture.md), [defaults](runtime-defaults.md), and the generated [builder setters](../builder-setters.md).

<!-- Source: docs/gitbooks/en/developing/embed/concepts/skills.md -->
---
description: "Skills are copied bundles of instructions and resources discovered inside an agent\u2019s own home."
---

# Skills

A skill bundle contains `SKILL.md` plus any referenced resources. `AgentSpec::skills_dir` copies bundles into the agent's `agents/<id>/skills/` tree. Copying is intentional: discovery rejects symlinked bundles, so linking a shared directory does not install it.

Enable the named Embed `skills` feature for skill setters and registry access. Library agents hide the operator's user skill directory unless `include_user_skills(true)` is explicitly selected. Treat skill installation as part of the agent's configuration, rather than reading arbitrary operator files into every tenant.

## Runtime and agent scope

| Runtime responsibility | Agent or call responsibility |
| --- | --- |
| Skill registry seam and default user-skill policy | Copied bundles, discovery context and explicit user-skill opt-in |

## Verified example

<!-- BEGIN EMBED: crates/openhuman-embed/examples/skills.rs#skills -->

```rust
    let skills = tempfile::tempdir()?;
    std::fs::create_dir(skills.path().join("summarize"))?;
    std::fs::write(skills.path().join("summarize/SKILL.md"),
        "---\nname: summarize\ndescription: Summarize documents clearly.\n---\nInclude the main points.\n")?;
    let agent = runtime.agent(AgentSpec::new("skilled").skills_dir(skills.path()))?;
    let copied = agent
        .workspace_dir()
        .join("agents/skilled/skills/summarize/SKILL.md");
    assert_eq!(
        std::fs::read_to_string(&copied)?,
        std::fs::read_to_string(skills.path().join("summarize/SKILL.md"))?
    );
    assert!(!std::fs::symlink_metadata(copied)?.file_type().is_symlink());
    assert!(!agent.run("Hello").await?.reply.is_empty());
    println!("skill bundle copied without symlinks");
```

<!-- END EMBED -->

[Complete runnable example](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/examples/skills.rs). The full file includes imports, runtime setup and local fixtures used by this excerpt.

## Behavior to account for

The example writes a bundle, registers an agent, checks the copied contents and verifies that the destination is not a symlink. Use the same directory layout for production bundles; do not depend on the fixture's temporary directory lifetime.

A resumed session's prompt is frozen, so adding or editing a bundle does not rewrite an existing conversation's recorded prefix. Start a new session when you want updated instructions to become visible, or build the agent with `AgentDefinitionSpec::refresh_prompt_on_resume(true)` so a resumed session is sent its current prompt. See [turns and sessions](turns-sessions.md) and the generated [builder setters](../builder-setters.md) for runtime policy knobs.

<!-- Source: docs/gitbooks/en/developing/embed/concepts/testing.md -->
---
description: "Deterministic local providers and explicit observations prove behavior without contacting real inference or backend services."
---

# Testing

The runnable examples default to loopback fixtures. They assert request bodies, tool results, file effects, transcript scope and event ordering; a successful Rust compilation alone does not prove those behaviors. Live example execution is explicitly opt-in through the documented environment variables.

A native `ChatModel<()>` stub is useful for provider selection and cancellation tests without HTTP. Protocol-level stubs remain useful for OpenAI-compatible routing, MCP or messaging transport behavior. Pick the boundary whose behavior your host needs to verify.

## Runtime and agent scope

| Runtime responsibility | Agent or call responsibility |
| --- | --- |
| Offline configuration, mock backend and injected model/engine ports | Isolated temp action directories, deterministic IDs and scoped assertions |

## Verified example

<!-- BEGIN EMBED: crates/openhuman-embed/examples/custom_provider.rs#custom_provider -->

```rust
    let provider = Provider::custom_with_fallback(
        Arc::new(LocalModel {
            reply: None,
            calls: failed.clone(),
        }),
        vec![Arc::new(LocalModel {
            reply: Some("fallback answer"),
            calls: fallback.clone(),
        })],
    )
    .model("local-custom")
    .role(
        "coding",
        Arc::new(LocalModel {
            reply: Some("coding answer"),
            calls: pinned.clone(),
        }),
    );
    let runtime = Runtime::builder()
        .config(support::offline_config())
        .backend_url(backend.uri())
        .provider(provider)
        .build()
        .await?;
    let definition = || {
        AgentDefinitionSpec::new()
            .bare_prompt("Reply briefly")
            .tools(ToolScopeSpec::HostOnly)
    };
    let regular = runtime.agent(AgentSpec::new("fallback-agent").definition(definition()))?;
    assert_eq!(regular.run("Hello").await?.reply, "fallback answer");
    assert_eq!(failed.load(Ordering::SeqCst), 1);
    assert_eq!(fallback.load(Ordering::SeqCst), 1);
    let coding = runtime.agent(
        AgentSpec::new("coding-agent")
            .model("hint:coding")
            .definition(definition()),
    )?;
    assert_eq!(
        coding.run("Explain a function").await?.reply,
        "coding answer"
    );
    assert_eq!(pinned.load(Ordering::SeqCst), 1);
    assert_eq!(
        failed.load(Ordering::SeqCst),
        1,
        "coding bypasses the primary chain"
    );
```

<!-- END EMBED -->

[Complete runnable example](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/examples/custom_provider.rs). The full file includes imports, runtime setup and local fixtures used by this excerpt.

## Behavior to account for

Run the offline example suite with `node scripts/run-embed-examples.mjs`; it clears inherited credentials/workspaces and uses one union feature graph for all examples. Run embed unit/integration tests with `cargo test -p openhuman-embed --features mcp,skills,channels,storage-sqlite --lib --tests -- --test-threads=1`.

Build your Tokio workers with the documented large stack constants for nested agent turns. Use channels, cancellation guards and bounded waits to prove lifecycle transitions instead of sleeps. The [mock-backend guide](../guides/mock-backend-testing.md) covers host fixtures, and [troubleshooting](../troubleshooting.md) distinguishes compile gates from runtime availability.

<!-- Source: docs/gitbooks/en/developing/embed/concepts/tools.md -->
---
description: "Tool catalogs expose only the allowed execution surface; host tools use the same native Tool contract as built-in tools."
---

# Tools

An agent's `ToolScopeSpec` chooses built-ins, named tools or `HostOnly`. Host-only mode starts with the host's advertised tools, requires an explicit bare prompt, and does not inherit an orchestrator tool catalog. This is useful when your application already owns read-only data access or tightly scoped actions.

`AgentSpec::tools` is a per-turn factory returning `HostTurnTools`. Factories can attach the turn's context to their executors without sharing mutable tool state across unrelated sessions. Import `Tool`, `ToolResult` and `ToolPolicy` from `openhuman_embed` so their types match the vendored implementation used by the core.

Declare a host executor's requirements with `Tool::policy`, for example `ToolPolicy::read_only()`. The host implements application-specific tools and owns their authorization, data boundaries and output handling; Embed supplies the generic execution contract.

## Runtime and agent scope

| Runtime responsibility | Agent or call responsibility |
| --- | --- |
| Domain/tool-group ceiling and shared execution policy | ToolScopeSpec, advertised host belt, attachments and prompt |

## Verified example

<!-- BEGIN EMBED: crates/openhuman-embed/examples/host_tools.rs#host_tools -->

```rust
    let calls = std::sync::Arc::new(std::sync::atomic::AtomicUsize::new(0));
    let recorded_calls = calls.clone();
    let tool_provider = support::scripted_provider(
        vec![support::tool_call_completion("host_ping", "{}")],
        "pong complete",
    )
    .await;
    let agent = runtime.agent(
        AgentSpec::new("host-tools")
            .provider(support::route(&tool_provider, "fixture"))
            .definition(
                AgentDefinitionSpec::new()
                    .bare_prompt("Use host_ping")
                    .tools(ToolScopeSpec::HostOnly),
            )
            .tools(move |_| {
                openhuman_embed::HostTurnTools::advertised(vec![Box::new(Ping(
                    recorded_calls.clone(),
                ))])
            }),
    )?;
    assert_eq!(agent.run("Ping").await?.reply, "pong complete");
    assert_eq!(calls.load(std::sync::atomic::Ordering::SeqCst), 1);
    let requests = support::chat_requests(&tool_provider).await;
    assert_eq!(support::tool_names(&requests[0]), vec!["host_ping"]);
    assert!(support::tool_results(&requests[1]).contains("pong"));
    println!("host_ping executed once; only host tools advertised");
```

<!-- END EMBED -->

[Complete runnable example](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/examples/host_tools.rs). The full file includes imports, runtime setup and local fixtures used by this excerpt.

## Behavior to account for

The example drives an actual host tool call and inspects the provider's advertised catalog: it contains only `host_ping`, which executes exactly once. A host executor remains responsible for authorizing its own application operations. Catalog visibility narrows what the model sees; it is not a substitute for permission checks or sandboxing.

Runtime tool hooks apply across agents; local hooks observe only their configured agent. Tool attachments can be added and removed for later turns, while existing durable sessions retain recorded declarations. See [hooks](hooks-seams.md), [MCP](mcp.md), and [access and approvals](access-approvals.md).

<!-- Source: docs/gitbooks/en/developing/embed/concepts/turns-sessions.md -->
---
description: "A turn is one native model/tool loop; a session is the durable identity that makes subsequent turns a conversation."
---

# Turns and sessions

`Agent::run` collects a turn. `Agent::turn` lets you set an explicit session, per-turn options, progress sink and cancellation before sending. Reuse the returned session identity for follow-up messages that should see the same transcript. Two calls on the same durable session remain distinct turn executions and receive distinct observation IDs.

`Agent::stream` returns a bounded `TurnStream`. Drain it while the turn is running: progress includes visible deltas and tool lifecycle notifications, followed by one `StreamEvent::Finished` containing the typed final result. Dropping the stream cancels its turn and recursive child work cooperatively.

## Runtime and agent scope

| Runtime responsibility | Agent or call responsibility |
| --- | --- |
| Session store provider and shared progress/event infrastructure | Durable agent/thread identity, transcript and per-call cancellation |

## Verified example

<!-- BEGIN EMBED: crates/openhuman-embed/examples/streaming.rs#streaming -->

```rust
    let agent = runtime.agent(AgentSpec::new("streamer"))?;
    let mut stream = agent.stream("Hello streaming");
    let mut count = 0;
    let mut deltas = String::new();
    let mut finished = None;
    while let Some(event) = stream.recv().await {
        match event {
            openhuman_embed::StreamEvent::Progress(progress) => {
                println!("{progress:?}");
                if let openhuman_embed::AgentProgress::TextDelta { delta, .. } = progress {
                    assert!(finished.is_none(), "text deltas precede Finished");
                    deltas.push_str(&delta);
                }
                count += 1;
            }
            openhuman_embed::StreamEvent::Finished(result) => finished = Some(result?),
        }
    }
    let outcome = finished.expect("stream ends with an outcome");
    assert!(!outcome.reply.is_empty());
    assert!(
        !deltas.is_empty(),
        "stream must publish visible text before Finished"
    );
    assert_eq!(deltas, outcome.reply);
    if support::offline() {
        let regular = agent.run("Hello streaming").await?;
        assert_eq!(
            outcome.reply, regular.reply,
            "streaming and collected turns agree"
        );
    }
    println!("progress events: {count}");
```

<!-- END EMBED -->

[Complete runnable example](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/examples/streaming.rs). The full file includes imports, runtime setup and local fixtures used by this excerpt.

## Attaching files

A host that already holds a file's bytes (an inbound photo from a channel webhook, an upload) attaches it with `Turn::attach(HostAttachment::new(bytes, mime).named(name))`, or several at once with `Turn::attachments`. Each file is staged into the agent's acting workspace and reaches the model ahead of the message: `image/*` as an image block, anything else as a document block. The shared media request layer handles bounded text and archive fallbacks; OpenHuman supplies image readouts and TinyDocs extraction for supported documents when the selected model cannot accept them natively. Unsupported binary formats remain available to the model as bounded metadata. The `[multimodal]` and `[multimodal_files]` count and size limits apply, and a turn over them fails before any model call. Blank or `application/octet-stream` types are sniffed from the name and bytes.

No local path is read, so attachments are accepted whatever the turn's origin, including external-channel turns whose `[IMAGE:…]`/`[FILE:…]` text markers are refused. Only a runtime-owned `Agent` carries them: a turn on a caller-built runtime's orchestrator (`Core::agent`) is refused with `attachments_unsupported` rather than run without its files, because `inference.agent_chat` carries only a string.

## Behavior to account for

The stream example checks that deltas precede the final result and that collected and streaming replies agree. A cancellation token passed to a turn is preserved by streaming; cancellation unblocks forwarding even when the consumer has stopped draining a full buffer.

The harness derives transcript paths from the thread and agent IDs without a timestamp. Resumed sessions reuse their recorded system prompt and tool declarations; edits to prompts, skills or integrations apply to new sessions. An agent built with `AgentDefinitionSpec::refresh_prompt_on_resume(true)` sends a resumed thread its current prompt instead: when the prompt changed, the thread's transcript generation is sealed and its conversation carries into the next, at the cost of one uncached prompt per change, and that turn's message opens with a one-time note that the instructions changed, so the model does not hold to what it said under the old ones. Compaction seals a generation and opens a child instead of erasing history.

A host can also compact a thread while its user is idle, so the next turn resends a summary instead of the whole history: `agent.compact_thread(&thread_id, CompactOptions { min_tokens, keep_recent_tokens }).await` returns `CompactOutcome::Compacted { tokens_before, tokens_after }`, `Skipped { tokens }` (below `min_tokens`, or nothing to fold; nothing written) or `Empty`. It runs the agent's own compaction (`[context] compaction_strategy` and the `summarizer_*` settings), places the summary as a user message after the system prompt, keeps the system prompt and tool declarations byte-identical (a memory block is rendered afresh for the new generation), and holds the session's turn lock, so a turn sent on the same thread meanwhile waits for it. See [observability](observability.md) for session IDs versus per-call turn IDs, and [API reference](../api-reference.md) for the session-store port.

<!-- Source: docs/gitbooks/en/developing/embed/faq.md -->
---
description: "Answers to common host, lifecycle, feature and provider choices."
---

# Embedding FAQ

## Can I create more than one runtime?

An ordinary embed process supports one `Runtime`; a second build returns `AlreadyRunning`. Add agents to the existing runtime. A multi-user host uses `ProfileRuntime` instead and needs a process dedicated to that mode. See [runtime](concepts/runtime.md) and [profiles](concepts/profiles-saas.md).

## Is an agent clone an independent agent?

No. It is another handle to the same agent instance, context and state. Register a different `AgentSpec` ID for a separate agent. Runtime credentials are still shared, so agent IDs alone do not isolate authenticated customers.

## Does lean mean fewer dependencies?

`RuntimeBuilder::lean()` selects a smaller runtime surface. Cargo still compiles enabled gates; use `--no-default-features` and explicit named gates for graph reduction. The [compiled matrix](capability-matrix.md) reports one fixed default build, and the [lean guide](guides/lean-headless.md) separates those controls.

## Why do MCP or skills methods disappear while the report says they are compiled?

The report describes core Cargo gates. Named Embed gates control facade exports too. Enable Embed's `mcp` or `skills` feature for their setters/re-exports, even when core defaults already compile the implementation. `channels` is named in Embed defaults.

## Can I supply an in-process model without HTTP?

Yes: `Provider::custom` takes the native `ChatModel<()>` contract. A provider can add an ordered fallback chain and explicit workload role pins. [Providers](concepts/providers.md) describes the rule that fallback stops after visible output.

## Do I need the backend for every agent turn?

An explicit BYOK/local/native provider can run independently of hosted inference. Hosted integrations, voice, billing or other backend surfaces need an installed backend transport and a credential. Use the [TinyHumans layer](integrations/tinyhumans-managed.md) for managed calls; Embed alone reports backend unavailable.

## Can an old handle control a replacement with the same ID?

No. Removal binds cancellation, callbacks and polling handles to the original lifecycle. It keeps the ID reserved until teardown/purge completes; later old handles cannot decide replacement approvals or send replacement turns. Dropping a handle does not purge its persisted home.

## Why did a prompt or tool change not affect a resumed thread?

A resumed session reuses recorded system messages and tool declarations. Start a new session for the changed prompt/tool catalog. Compaction preserves prior generations; it does not rewrite the old conversation. See [turns and sessions](concepts/turns-sessions.md).

## Where is the complete builder knob list?

[RuntimeBuilder setters](builder-setters.md) is generated from actual consuming public setters, with source signatures. Host/weight presets and non-setter methods are intentionally documented through [API reference](api-reference.md).

<!-- Source: docs/gitbooks/en/developing/embed/guides/README.md -->
---
description: "Build a chatbot, host multiple agents, serve profiles, or test an embedded runtime."
icon: code
---

# Embedding guides

Choose the guide that matches your host:

- [CLI chatbot](cli-chatbot.md): create an agent and collect or stream its reply.
- [Multiple agents](multi-agent.md): give agents independent prompts, access, and working folders.
- [SaaS with multiple tenants](saas-multi-tenant.md): isolate user state through profiles.
- [Deploy a server](deploy-server.md): call an embedded agent from your HTTP host.
- [Lean and headless hosts](lean-headless.md): select runtime services and Cargo features separately.
- [Test with a mock backend](mock-backend-testing.md): run the entire example suite offline.

Start with [Embedding](../README.md) for the library boundary and quickstart. See [Integrations](../integrations/README.md) for providers, storage, MCP, and channels.

<!-- Source: docs/gitbooks/en/developing/embed/guides/cli-chatbot.md -->
---
description: "Collect a reply or consume progress events in a Rust command-line host."
icon: code
---

# CLI chatbot

Keep one `Runtime` alive for the process and create an `Agent` for the conversation. The executable example starts with a single message, checks the reply, and prints it. A CLI input loop can send subsequent messages through the same agent; choose a stable thread identity when your host exposes separate conversations.

<!-- BEGIN EMBED: crates/openhuman-embed/examples/run_turn.rs#run_turn -->

```rust
    let agent = runtime.agent(
        AgentSpec::new("hello").definition(
            AgentDefinitionSpec::new()
                .bare_prompt("Reply briefly.")
                .tools(ToolScopeSpec::HostOnly),
        ),
    )?;
    let outcome = agent.run("Say hello").await?;
    assert!(!outcome.reply.is_empty());
    if support::offline() {
        assert_eq!(outcome.reply, "hello from the stub");
    }
    println!("{}", outcome.reply);
```

<!-- END EMBED -->

Run `cargo run -p openhuman-embed --example run_turn` from the repository root. It uses a loopback provider and temporary state. The [complete run_turn example](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/examples/run_turn.rs) includes imports, runtime construction, and offline setup.

For incremental output, consume `agent.stream(...)` until `StreamEvent::Finished`. Progress includes text deltas; the final event carries the same outcome shape as `run`. Dropping the stream cancels the underlying turn.

<!-- BEGIN EMBED: crates/openhuman-embed/examples/streaming.rs#streaming -->

```rust
    let agent = runtime.agent(AgentSpec::new("streamer"))?;
    let mut stream = agent.stream("Hello streaming");
    let mut count = 0;
    let mut deltas = String::new();
    let mut finished = None;
    while let Some(event) = stream.recv().await {
        match event {
            openhuman_embed::StreamEvent::Progress(progress) => {
                println!("{progress:?}");
                if let openhuman_embed::AgentProgress::TextDelta { delta, .. } = progress {
                    assert!(finished.is_none(), "text deltas precede Finished");
                    deltas.push_str(&delta);
                }
                count += 1;
            }
            openhuman_embed::StreamEvent::Finished(result) => finished = Some(result?),
        }
    }
    let outcome = finished.expect("stream ends with an outcome");
    assert!(!outcome.reply.is_empty());
    assert!(
        !deltas.is_empty(),
        "stream must publish visible text before Finished"
    );
    assert_eq!(deltas, outcome.reply);
    if support::offline() {
        let regular = agent.run("Hello streaming").await?;
        assert_eq!(
            outcome.reply, regular.reply,
            "streaming and collected turns agree"
        );
    }
    println!("progress events: {count}");
```

<!-- END EMBED -->

The [complete streaming example](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/examples/streaming.rs) checks that text arrives before completion and that the offline streamed reply equals a collected reply. [Provider configuration](../integrations/openai-compatible.md) covers explicit live inference.

<!-- Source: docs/gitbooks/en/developing/embed/guides/deploy-server.md -->
---
description: "Invoke agents from a host-owned HTTP service without adding RPC to Embed."
icon: code
---

# Deploy a server

Your HTTP host can keep a runtime alive and invoke its agents directly. The executable example binds a loopback listener, handles one request, and sends back the agent's reply. It exercises the request path without requiring an RPC server dependency in `openhuman-embed`.

<!-- BEGIN EMBED: crates/openhuman-embed/examples/server.rs#server -->

```rust
    use tokio::io::{AsyncReadExt, AsyncWriteExt};
    let agent = runtime.agent(
        AgentSpec::new("http-agent").definition(
            AgentDefinitionSpec::new()
                .bare_prompt("Reply briefly.")
                .tools(ToolScopeSpec::HostOnly),
        ),
    )?;
    let listener = tokio::net::TcpListener::bind("127.0.0.1:0").await?;
    let address = listener.local_addr()?;
    // A real host should use its HTTP framework and authenticate requests.
    // This single-request fixture keeps the library dependency direction intact.
    let server = tokio::spawn(async move {
        let (mut socket, _) = listener.accept().await?;
        let mut request = vec![0; 4096];
        let length = socket.read(&mut request).await?;
        assert!(String::from_utf8_lossy(&request[..length]).starts_with("GET /hello "));
        let reply = agent.run("Say hello").await?.reply;
        let response = format!(
            "HTTP/1.1 200 OK\r\nContent-Length: {}\r\nConnection: close\r\n\r\n{}",
            reply.len(),
            reply
        );
        socket.write_all(response.as_bytes()).await?;
        Ok::<_, anyhow::Error>(())
    });
    let reply = reqwest::get(format!("http://{address}/hello"))
        .await?
        .error_for_status()?
        .text()
        .await?;
    assert!(!reply.is_empty());
    if support::offline() {
        assert_eq!(reply, "hello from the stub");
    }
    server.await??;
    println!("host HTTP request reached the in-process agent");
```

<!-- END EMBED -->

Run `cargo run -p openhuman-embed --example server`. The [complete server example](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/examples/server.rs) checks the HTTP response against the deterministic provider reply.

Use your HTTP framework for a deployed service. The example's one-request parser is deliberately small; a deployed host must handle authentication, request limits, cancellation, and shutdown. Select agents from authenticated host state. For multiple users, use [SaaS profiles](saas-multi-tenant.md).

If your host needs OpenHuman's existing JSON-RPC server, depend on `openhuman-rpc`, the layer above Embed. Adding that dependency to Embed would reverse the crate chain. [Channels](../integrations/channels.md) provide another way to deliver messages to bound agents.

<!-- Source: docs/gitbooks/en/developing/embed/guides/lean-headless.md -->
---
description: "Control runtime activity separately from compile-time Cargo features."
icon: code
---

# Lean and headless hosts

`RuntimeBuilder::lean()` selects the agent, memory, and inference families without background services. Runtime module selection controls what is active; Cargo features control what is compiled. A lean preset alone does not shed compiled dependencies.

<!-- BEGIN EMBED: crates/openhuman-embed/examples/lean_headless.rs#lean_headless -->

```rust
    let info = runtime.capabilities();
    assert_eq!(info.weight, openhuman_embed::WeightClass::Lean);
    assert!(info.services.is_empty());
    let agent = runtime.agent(
        AgentSpec::new("lean").definition(
            AgentDefinitionSpec::new()
                .bare_prompt("Reply briefly.")
                .tools(ToolScopeSpec::HostOnly),
        ),
    )?;
    assert!(!agent.run("Hello lean runtime").await?.reply.is_empty());
    println!("lean runtime runs a turn without background services");
```

<!-- END EMBED -->

Run `cargo run -p openhuman-embed --example lean_headless`. The [complete lean_headless example](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/examples/lean_headless.rs) uses `ServiceSet::none()`, verifies the lean capability report, and completes an actual turn. For a host crate that needs fewer compiled features, disable default features on its Embed dependency and enable only the gates it needs. Verify enabled and disabled builds for that selection.

A headless host also owns process setup: initialize its encryption key before storing credentials, choose its state directory, and keep the runtime alive until shutdown. The example support code uses a random fixture key and temporary credential directory; deployed hosts supply a durable key and durable workspace.

[Storage drivers](../integrations/storage-drivers.md) covers persistent backends. The [complete capability_report example](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/examples/capability_report.rs) reports compiled features and active services without exposing provider keys or URLs.

<!-- Source: docs/gitbooks/en/developing/embed/guides/mock-backend-testing.md -->
---
description: "Run the embedding examples offline and verify behavior, not just compilation."
icon: code
---

# Test with a mock backend

Run `node scripts/run-embed-examples.mjs` from the repository root. The runner builds and executes every example with the union of required features, clears inherited live-example credentials and operator workspace settings, and requires each program's `EXAMPLE_OK` marker after its assertions.

The shared support module uses loopback Wiremock servers for backend and inference requests. It disables unrelated online services and supplies isolated keyring state. Examples assert replies, transmitted model settings, tool results, and state isolation. MCP and Telegram use their real protocol shapes against local stubs; SQLite performs real local writes.

<!-- BEGIN EMBED: crates/openhuman-embed/examples/host_tools.rs#host_tools -->

```rust
    let calls = std::sync::Arc::new(std::sync::atomic::AtomicUsize::new(0));
    let recorded_calls = calls.clone();
    let tool_provider = support::scripted_provider(
        vec![support::tool_call_completion("host_ping", "{}")],
        "pong complete",
    )
    .await;
    let agent = runtime.agent(
        AgentSpec::new("host-tools")
            .provider(support::route(&tool_provider, "fixture"))
            .definition(
                AgentDefinitionSpec::new()
                    .bare_prompt("Use host_ping")
                    .tools(ToolScopeSpec::HostOnly),
            )
            .tools(move |_| {
                openhuman_embed::HostTurnTools::advertised(vec![Box::new(Ping(
                    recorded_calls.clone(),
                ))])
            }),
    )?;
    assert_eq!(agent.run("Ping").await?.reply, "pong complete");
    assert_eq!(calls.load(std::sync::atomic::Ordering::SeqCst), 1);
    let requests = support::chat_requests(&tool_provider).await;
    assert_eq!(support::tool_names(&requests[0]), vec!["host_ping"]);
    assert!(support::tool_results(&requests[1]).contains("pong"));
    println!("host_ping executed once; only host tools advertised");
```

<!-- END EMBED -->

The [complete host_tools example](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/examples/host_tools.rs) checks the advertised catalog, executes a host tool exactly once, and verifies that its result reaches the next model request. This catches a host adapter that advertises a tool but never runs it.

Run a single gated example with `cargo run -p openhuman-embed --example mcp --features mcp`, or use the full runner to select all gates automatically. The individual examples default to offline mode. Only the [explicit live provider options](../integrations/openai-compatible.md) and [MongoDB live option](../integrations/storage-drivers.md) contact configured services.

<!-- Source: docs/gitbooks/en/developing/embed/guides/multi-agent.md -->
---
description: "Share one runtime while configuring independent agent prompts and working folders."
icon: code
---

# Multiple agents

A runtime owns shared services and credentials. Each `AgentSpec` selects its prompt, model route, access policy, and `action_dir`. Internal agent state belongs to its agent home; the action directory is where acting tools operate.

<!-- BEGIN EMBED: crates/openhuman-embed/examples/two_agents.rs#two_agents -->

```rust
    let analyst_dir = tempfile::tempdir()?;
    let writer_dir = tempfile::tempdir()?;
    let analyst = runtime.agent(
        AgentSpec::new("analyst")
            .system_prompt("ANALYST_PROMPT: summarize documents")
            .action_dir(analyst_dir.path())
            .access(openhuman_embed::Access::readonly()),
    )?;
    let writer = runtime.agent(
        AgentSpec::new("writer")
            .system_prompt("WRITER_PROMPT: compose explanations")
            .action_dir(writer_dir.path())
            .access(openhuman_embed::Access::full()),
    )?;
    assert_ne!(analyst.action_dir(), writer.action_dir());
    assert_ne!(analyst.home_dir(), writer.home_dir());
    assert_ne!(analyst.workspace_dir(), analyst.action_dir());
    assert!(!analyst.run("Analyze").await?.reply.is_empty());
    assert!(!writer.run("Explain").await?.reply.is_empty());
    if support::offline() {
        let requests = support::chat_requests(&provider).await;
        assert_eq!(requests.len(), 2);
        assert!(String::from_utf8_lossy(&requests[0].body).contains("ANALYST_PROMPT"));
        assert!(!String::from_utf8_lossy(&requests[0].body).contains("WRITER_PROMPT"));
        assert!(String::from_utf8_lossy(&requests[1].body).contains("WRITER_PROMPT"));
    }
    println!("two distinct prompts and action workspaces verified");
```

<!-- END EMBED -->

The [complete two_agents example](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/examples/two_agents.rs) sends real turns and checks the provider requests: the analyst prompt does not contain the writer prompt. It also checks different action directories and agent homes. Run `cargo run -p openhuman-embed --example two_agents`.

Agents on one runtime share the runtime configuration and credential root. Separate action directories are useful for cooperating agents; use [SaaS profiles](saas-multi-tenant.md) for user isolation.

Runtime defaults can supply a provider and model settings; individual agents override selected fields. The [complete defaults_overrides example](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/examples/defaults_overrides.rs) verifies both routes and their transmitted temperature and token limits.

<!-- BEGIN EMBED: crates/openhuman-embed/examples/defaults_overrides.rs#defaults_overrides -->

```rust
    let inherited = runtime.agent(AgentSpec::new("inherited"))?;
    let override_provider = support::provider("override reply").await;
    let overridden = runtime.agent(
        AgentSpec::new("overridden")
            .provider(support::route(&override_provider, "override-model"))
            .access(openhuman_embed::Access::readonly())
            .model_defaults(openhuman_embed::ModelDefaults {
                temperature: Some(0.8),
                max_tokens: Some(128),
                ..Default::default()
            }),
    )?;
    let inherited_reply = inherited.run("Hello default").await?;
    let overridden_reply = overridden.run("Hello override").await?;
    if support::offline() {
        assert_eq!(inherited_reply.reply, "hello from the stub");
        let requests = support::chat_requests(&provider).await;
        let body: serde_json::Value = serde_json::from_slice(&requests[0].body)?;
        assert_eq!(body["model"], "fixture");
        assert_eq!(body["temperature"], 0.4);
        assert_eq!(body["max_tokens"], 256);
    }
    assert_eq!(overridden_reply.reply, "override reply");
    let requests = support::chat_requests(&override_provider).await;
    let body: serde_json::Value = serde_json::from_slice(&requests[0].body)?;
    assert_eq!(body["model"], "override-model");
    assert_eq!(body["temperature"], 0.8);
    assert_eq!(body["max_tokens"], 128);
    assert_eq!(runtime.defaults().model.temperature, Some(0.4));
    println!("default and override routes answered independently");
```

<!-- END EMBED -->

<!-- Source: docs/gitbooks/en/developing/embed/guides/saas-multi-tenant.md -->
---
description: "Isolate user credentials, workspaces, and conversation history with ProfileRuntime."
icon: code
---

# SaaS with multiple tenants

Use `ProfileRuntime` for a process serving multiple users. Provision each profile, install that profile's credential, and open a handle to its workspace. Reusing a thread id in two profiles does not share their conversation history.

The complete example prepares a temporary SaaS root and initializes the keyring at `SaasConfig::operator_config().workspace_dir` before boot. SaaS validates the operator root, service token, permitted domains, and environment. A process-wide `OPENHUMAN_WORKSPACE` override is rejected. A SaaS core must be the only core in its process.

<!-- BEGIN EMBED: crates/openhuman-embed/examples/profiles.rs#profiles -->

```rust
    let profiles = openhuman_embed::ProfileRuntime::build(config).await?;
    let mut handles = Vec::new();
    for (user, text) in [("alice", "Alice private note"), ("bob", "Bob private note")] {
        let profile = profiles.provision(user).await?;
        profiles
            .set_credential(
                &profile.profile_id,
                openhuman_embed::profiles::ProfileCredentialKind::ApiKey,
                &format!("{user}-fixture"),
            )
            .await?;
        let handle = profiles.open(user).await?;
        let reply = handle.chat("shared-thread", text).await?;
        assert!(reply.text.contains(text), "{}", reply.text);
        handles.push(handle);
    }
    assert_ne!(handles[0].workspace_dir(), handles[1].workspace_dir());
    for (index, own, other) in [
        (0, "Alice private note", "Bob private note"),
        (1, "Bob private note", "Alice private note"),
    ] {
        let messages = handles[index].messages("shared-thread").await?;
        let text = serde_json::to_string(&messages)?;
        assert!(text.contains(own));
        assert!(!text.contains(other));
    }
    drop(handles);
    profiles.shutdown().await;
    println!("two profiles keep same-thread conversations separate");
```

<!-- END EMBED -->

Run `cargo run -p openhuman-embed --example profiles`. The [complete profiles example](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/examples/profiles.rs) installs a loopback backend transport, chats as Alice and Bob, then checks distinct workspaces and same-thread history isolation. No live account or backend is contacted.

Authenticate users in your host before selecting a profile. Keep the operator's configuration and gateway credential separate from user credentials. [Managed inference](../integrations/tinyhumans-managed.md) explains who provides the backend transport.

<!-- Source: docs/gitbooks/en/developing/embed/installation.md -->
---
description: "Add the Embed dependency, choose compile-time gates, and configure the host\u2019s runtime and state deliberately."
---

# Installation

`openhuman-embed` is currently an unpublished workspace crate. Use a local path dependency when developing in this repository, or a Git dependency on `https://github.com/tinyhumansai/openhuman` with `package = "openhuman-embed"`. Pin a reviewed Git revision for a deployed application; the nested vendored libraries are part of that revision's build inputs.

Run repository commands from its root. Build the facade with `cargo check -p openhuman-embed`; validate a narrow graph with `cargo check -p openhuman-embed --no-default-features`. Runtime weight presets and Cargo features control different things.

## Compile-time gates versus runtime selection

| Choice | What it controls |
| --- | --- |
| Cargo default features | Code and dependencies compiled into the artifact |
| Named Embed feature such as `mcp` or `skills` | Public facade items and the forwarded core gate |
| `RuntimeBuilder::modules`, domains, services and tool groups | Which compiled families a runtime exposes or starts |
| Agent tool scope and access | Which subset one agent can use |

Embed defaults forward the core defaults and enable the named Embed `channels` feature. Core defaults can include MCP and skills without enabling Embed's separately gated `McpServer`/agent MCP setter or skill setters. Add `--features mcp,skills` when using those public APIs. A runtime selection cannot enable a Cargo gate that was compiled out; inspect the [generated compiled matrix](capability-matrix.md) and the [source manifest](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/Cargo.toml).

`RuntimeBuilder::lean()` narrows runtime activity; it does not reduce the dependency graph by itself. Pair it with `--no-default-features` for a narrow build and explicitly add the gates you need. Use `scripts/assert-shed.sh` or the repository dependency simulator before claiming a dependency reduction. See [lean/headless guide](guides/lean-headless.md).

## Host runtime and credentials

Build a Tokio multi-thread runtime with `enable_all`, a large worker stack and a bounded blocking pool. The verified examples use the [core runtime constants](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-core/src/core/runtime/mod.rs) through their repository support module: currently a 20 MiB worker stack and 64 blocking workers. Tokio's default 2 MiB worker stack is insufficient for nested agent turns. Follow the full example's host setup rather than adding an unconfigured `#[tokio::main]` to a large agent workload.

Initialize the credential master key through `openhuman_embed::process::init_master_key` before persisting a runtime API key. Headless hosts can provide a protected 64-hex-character `OPENHUMAN_KEYRING_MASTER_KEY` or configured key file; keep this credential out of source and logs. The offline examples create disposable fixture keys, not production credentials.

## State, configuration and inspection

`Workspace::Ephemeral` is the library preset's default. `Workspace::Dir` moves both internal state and the credential root together; `workspace_dir` overrides internal state only, leaving the credential root at the resolved config path. Keep the action directory outside the internal workspace. Operator host presets use discovered configuration, while a supplied `Config` is resolved host configuration.

[Runtime defaults](concepts/runtime-defaults.md) explains inheritance. The generated [builder setters](builder-setters.md) is the source-derived list of configuration knobs, and `RuntimeBuilder::describe` reports the pre-build plan without starting services.

<!-- BEGIN EMBED: crates/openhuman-embed/examples/capability_report.rs#capability_report -->

```rust
    let info = runtime.capabilities();
    assert_eq!(info.schema_version, 1);
    assert!(info.defaults.routed_provider);
    let serialized = serde_json::to_string(&info)?;
    assert!(!serialized.contains("sk-test"));
    assert!(!serialized.contains(&provider.uri()));
    assert!(!serialized.contains(&runtime.workspace_dir().display().to_string()));
    println!("{}", serde_json::to_string_pretty(&info)?);
```

<!-- END EMBED -->

[Complete runnable example](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/examples/capability_report.rs), including runtime construction, imports and fixture setup.

The example checks the effective report after boot and proves that credentials, provider URL and workspace path are absent. The generated matrix uses the fixed default-feature report command; it describes that build, not every feature combination a host might compile.

<!-- Source: docs/gitbooks/en/developing/embed/integrations/README.md -->
---
description: "Choose provider, tool, storage, and channel adapters for your host."
icon: code
---

# Embedding integrations

An embedded runtime composes the capabilities its host supplies. Start with the adapter your application needs:

- [OpenAI-compatible providers and BYOK](openai-compatible.md)
- [TinyHumans managed inference](tinyhumans-managed.md)
- [Local models](local-models.md)
- [MCP servers](mcp.md)
- [Composio](composio.md)
- [Web search](web-search.md)
- [Storage drivers](storage-drivers.md)
- [Channels](channels.md)

[Guides](../guides/README.md) show how these pieces fit into a CLI, an HTTP server, or a SaaS host. Every linked executable example defaults to offline mode; a live endpoint requires an explicit choice.

<!-- Source: docs/gitbooks/en/developing/embed/integrations/channels.md -->
---
description: "Bind an incoming channel to an agent and verify the delivered reply."
icon: code
---

# Channels

Enable the `channels` Cargo feature to use channel adapters. Your host configures the channel credential and binds incoming messages to an agent. In the Telegram example, the Bot API endpoint is a loopback stub, so updates and replies exercise the adapter without contacting Telegram.

<!-- BEGIN EMBED: crates/openhuman-embed/examples/telegram.rs#telegram -->

```rust
    let bot = wiremock::MockServer::start().await;
    let token = "123456:EXAMPLE";
    let updates = format!("/bot{token}/getUpdates");
    wiremock::Mock::given(wiremock::matchers::path(updates.clone())).respond_with(
        wiremock::ResponseTemplate::new(200).set_body_json(serde_json::json!({"ok":true,"result":[{
            "update_id":1,"message":{"message_id":1,"date":0,"text":"hello channel",
            "from":{"id":99,"is_bot":false,"username":"alice"},"chat":{"id":4242,"type":"private"}}
        }]}))).up_to_n_times(1).with_priority(1).mount(&bot).await;
    wiremock::Mock::given(wiremock::matchers::path(updates))
        .respond_with(
            wiremock::ResponseTemplate::new(200)
                .set_body_json(serde_json::json!({"ok":true,"result":[]}))
                .set_delay(std::time::Duration::from_millis(100)),
        )
        .with_priority(2)
        .mount(&bot)
        .await;
    wiremock::Mock::given(wiremock::matchers::any())
        .respond_with(
            wiremock::ResponseTemplate::new(200)
                .set_body_json(serde_json::json!({"ok":true,"result":{
            "message_id":2,"id":1,"is_bot":true,"username":"example_bot"}})),
        )
        .with_priority(10)
        .mount(&bot)
        .await;
    std::env::set_var("OPENHUMAN_TELEGRAM_BOT_API_BASE", bot.uri());
    let agent = runtime.agent(
        AgentSpec::new("channel-agent").definition(
            AgentDefinitionSpec::new()
                .bare_prompt("Reply briefly.")
                .tools(ToolScopeSpec::HostOnly),
        ),
    )?;
    let listener = runtime.channels().telegram(
        openhuman_embed::TelegramChannelSpec::new(token, agent.id()).allowed_users(["alice"]),
    )?;
    let mut sent = false;
    for _ in 0..200 {
        let requests = bot.received_requests().await.unwrap_or_default();
        sent = requests.iter().any(|request| {
            request.url.path().ends_with("/sendMessage")
                && serde_json::from_slice::<serde_json::Value>(&request.body).is_ok_and(|body| {
                    body["chat_id"].to_string().contains("4242")
                        && body["text"]
                            .as_str()
                            .is_some_and(|text| text.contains("hello from the stub"))
                })
        });
        if sent {
            break;
        }
        tokio::time::sleep(std::time::Duration::from_millis(50)).await;
    }
    listener.stop();
    assert!(
        sent,
        "the bound agent must send a reply to the stub Bot API"
    );
    std::env::remove_var("OPENHUMAN_TELEGRAM_BOT_API_BASE");
    println!("Telegram update answered in the correct chat");
```

<!-- END EMBED -->

Run `cargo run -p openhuman-embed --example telegram --features channels`. The [complete telegram example](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/examples/telegram.rs) receives a stub update, runs the bound agent, and checks that the resulting reply was posted to the expected chat id.

Keep channel credentials in host configuration and map messages to the intended identity and conversation. A provider route controls inference; it does not configure a channel or provide a hosted relay. [Managed backend setup](tinyhumans-managed.md) explains that separate transport responsibility.

For a live Telegram host, supply the real Bot API configuration through your application's channel setup. This offline example deliberately has no live channel environment switch. [Deploy a server](../guides/deploy-server.md) covers a host-owned HTTP alternative.

<!-- Source: docs/gitbooks/en/developing/embed/integrations/composio.md -->
---
description: "Pin connector credentials per agent while the host supplies backend transport."
icon: code
---

# Composio

Composio calls use the host backend transport. Install that transport before booting a managed runtime; see [TinyHumans managed inference](tinyhumans-managed.md). An explicit inference provider route supplies model inference, not the connector transport.

Use `AgentSpec::composio(ComposioHostCredential)` to pin a connector credential to an agent. This keeps the credential selection on the host side instead of asking the model to choose another user's credential. Configure the agent's tool scope and access separately. The connector tools still pass through the host's execution policy.

The repository's [Composio agent integration test](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/tests/composio_agents.rs) verifies two agents using their own pinned credentials against a loopback backend. It checks the outgoing authentication and action results rather than contacting Composio. Run the Embed integration suite through the repository's cancellation-aware runner when changing this adapter.

Account login, linking, and credential acquisition belong to your connected host and connector layer. Keep secrets out of prompts and capability reports. For multiple users, use [SaaS profiles](../guides/saas-multi-tenant.md); independent action directories on a shared library runtime do not establish tenant isolation.

<!-- Source: docs/gitbooks/en/developing/embed/integrations/local-models.md -->
---
description: "Use a local OpenAI-compatible server or inject a native ChatModel implementation."
icon: code
---

# Local and custom models

For a local server that implements chat completions, use `Provider::openai_compatible` with its API root and model id. The constructor requires a bearer string even when a particular local server ignores it. Set the endpoint explicitly rather than relying on another user's installed configuration. The [OpenAI-compatible example controls](openai-compatible.md) can point a live example at that local server.

A native provider implements `openhuman_embed::providers::ChatModel<()>`. `Provider::custom_with_fallback` tries fallback models when the primary fails before output; validation failures and failures after streamed content do not switch providers. A named role pin can select another native model for a workload.

<!-- BEGIN EMBED: crates/openhuman-embed/examples/custom_provider.rs#custom_provider -->

```rust
    let provider = Provider::custom_with_fallback(
        Arc::new(LocalModel {
            reply: None,
            calls: failed.clone(),
        }),
        vec![Arc::new(LocalModel {
            reply: Some("fallback answer"),
            calls: fallback.clone(),
        })],
    )
    .model("local-custom")
    .role(
        "coding",
        Arc::new(LocalModel {
            reply: Some("coding answer"),
            calls: pinned.clone(),
        }),
    );
    let runtime = Runtime::builder()
        .config(support::offline_config())
        .backend_url(backend.uri())
        .provider(provider)
        .build()
        .await?;
    let definition = || {
        AgentDefinitionSpec::new()
            .bare_prompt("Reply briefly")
            .tools(ToolScopeSpec::HostOnly)
    };
    let regular = runtime.agent(AgentSpec::new("fallback-agent").definition(definition()))?;
    assert_eq!(regular.run("Hello").await?.reply, "fallback answer");
    assert_eq!(failed.load(Ordering::SeqCst), 1);
    assert_eq!(fallback.load(Ordering::SeqCst), 1);
    let coding = runtime.agent(
        AgentSpec::new("coding-agent")
            .model("hint:coding")
            .definition(definition()),
    )?;
    assert_eq!(
        coding.run("Explain a function").await?.reply,
        "coding answer"
    );
    assert_eq!(pinned.load(Ordering::SeqCst), 1);
    assert_eq!(
        failed.load(Ordering::SeqCst),
        1,
        "coding bypasses the primary chain"
    );
```

<!-- END EMBED -->

Run `cargo run -p openhuman-embed --example custom_provider`. The [complete custom_provider example](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/examples/custom_provider.rs) includes the concrete model implementation. It checks one primary failure, one fallback invocation, and a coding role that bypasses the primary chain. These native models are deterministic and make no inference network requests.

For live local inference, configure model availability and authentication in your server. For runtime selection and service controls, see [Lean and headless hosts](../guides/lean-headless.md).

<!-- Source: docs/gitbooks/en/developing/embed/integrations/mcp.md -->
---
description: "Connect a declared MCP server and verify remote discovery and execution."
icon: code
---

# MCP servers

Enable the `mcp` Cargo feature and declare a server with `AgentSpec::mcp`. `McpServer::http` takes a registered name and endpoint; `allow_tools` narrows the remote tool list. Server configuration and its connections belong to the agent that declared them.

<!-- BEGIN EMBED: crates/openhuman-embed/examples/mcp.rs#mcp -->

```rust
    let mcp = wiremock::MockServer::start().await;
    wiremock::Mock::given(wiremock::matchers::method("POST"))
        .respond_with(McpStub)
        .mount(&mcp)
        .await;
    let discovery = wiremock::MockServer::start().await;
    wiremock::Mock::given(wiremock::matchers::path("/v1/chat/completions"))
        .respond_with(ForecastModel(std::sync::atomic::AtomicUsize::new(0)))
        .mount(&discovery)
        .await;
    let agent = runtime.agent(
        AgentSpec::new("mcp-client")
            .provider(support::route(&discovery, "fixture"))
            .mcp(
                openhuman_embed::McpServer::http("forecast", format!("{}/mcp", mcp.uri()))
                    .allow_tools(["forecast"]),
            )
            .access(openhuman_embed::Access::full()),
    )?;
    assert_eq!(
        agent
            .run("List the forecast server tools, then call its forecast tool")
            .await?
            .reply,
        "sunny verified"
    );
    let requests = mcp.received_requests().await.expect("MCP requests");
    let methods: Vec<String> = requests
        .iter()
        .filter_map(|r| {
            serde_json::from_slice::<serde_json::Value>(&r.body).ok()?["method"]
                .as_str()
                .map(str::to_string)
        })
        .collect();
    assert!(methods.iter().any(|m| m == "initialize"), "{methods:?}");
    assert!(methods.iter().any(|m| m == "tools/list"), "{methods:?}");
    assert!(methods.iter().any(|m| m == "tools/call"), "{methods:?}");
    let requests = support::chat_requests(&discovery).await;
    assert!(support::tool_results(&requests[1]).contains("forecast"));
    assert!(support::tool_results(&requests[2]).contains("sunny"));
    println!("MCP handshake, discovery and remote execution verified");
```

<!-- END EMBED -->

Run `cargo run -p openhuman-embed --example mcp --features mcp`. The [complete mcp example](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/examples/mcp.rs) performs an actual MCP initialize handshake, lists remote tools, calls the forecast tool, and verifies the returned `sunny` result in the next model request.

The immediately available `mcp_list_tools` and `mcp_call_tool` bridge tools work with a cold configured server. Individual per-server tools use a cached catalog and are deferred by default. On a cold first turn, that catalog may still be warming in the background; do not assume `tool_search` already contains the per-server tool name. Use the bridge to discover and call it, and copy returned tool names and arguments.

MCP transport does not bypass the host's execution policy. Configure agent access and approvals for actions exposed by the server. [Mock testing](../guides/mock-backend-testing.md) keeps this protocol path local during development.

<!-- Source: docs/gitbooks/en/developing/embed/integrations/openai-compatible.md -->
---
description: "Route inference to an explicit endpoint and bearer without changing shared host configuration."
icon: code
---

# OpenAI-compatible providers and BYOK

Construct a route with `Provider::openai_compatible(base_url, api_key).model(model_id)`, then set it on the runtime or an individual agent. Pass the API root, such as a provider's `/v1` URL; the adapter appends `/chat/completions`. Supply an explicit model id so the route can register that model for its workload roles.

The runtime and agent routes in this example answer independently. Assertions inspect the actual request bodies, including model, temperature, and token limits.

<!-- BEGIN EMBED: crates/openhuman-embed/examples/defaults_overrides.rs#defaults_overrides -->

```rust
    let inherited = runtime.agent(AgentSpec::new("inherited"))?;
    let override_provider = support::provider("override reply").await;
    let overridden = runtime.agent(
        AgentSpec::new("overridden")
            .provider(support::route(&override_provider, "override-model"))
            .access(openhuman_embed::Access::readonly())
            .model_defaults(openhuman_embed::ModelDefaults {
                temperature: Some(0.8),
                max_tokens: Some(128),
                ..Default::default()
            }),
    )?;
    let inherited_reply = inherited.run("Hello default").await?;
    let overridden_reply = overridden.run("Hello override").await?;
    if support::offline() {
        assert_eq!(inherited_reply.reply, "hello from the stub");
        let requests = support::chat_requests(&provider).await;
        let body: serde_json::Value = serde_json::from_slice(&requests[0].body)?;
        assert_eq!(body["model"], "fixture");
        assert_eq!(body["temperature"], 0.4);
        assert_eq!(body["max_tokens"], 256);
    }
    assert_eq!(overridden_reply.reply, "override reply");
    let requests = support::chat_requests(&override_provider).await;
    let body: serde_json::Value = serde_json::from_slice(&requests[0].body)?;
    assert_eq!(body["model"], "override-model");
    assert_eq!(body["temperature"], 0.8);
    assert_eq!(body["max_tokens"], 128);
    assert_eq!(runtime.defaults().model.temperature, Some(0.4));
    println!("default and override routes answered independently");
```

<!-- END EMBED -->

The [complete defaults_overrides example](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/examples/defaults_overrides.rs) includes the loopback endpoint setup. Run `cargo run -p openhuman-embed --example defaults_overrides` for its offline assertions.

Examples using `support::example_provider` switch to live inference only with `OPENHUMAN_EXAMPLE_LIVE=1` and all three of `OPENHUMAN_EXAMPLE_BASE_URL`, `OPENHUMAN_EXAMPLE_API_KEY`, and `OPENHUMAN_EXAMPLE_MODEL`. These settings are example controls, not replacements for your application's credential configuration. Keep provider keys in your host's secret configuration.

For a non-HTTP implementation, [local and custom models](local-models.md) covers `Provider::custom`, native fallback, and role pins. [Managed inference](tinyhumans-managed.md) uses a different backend transport boundary.

<!-- Source: docs/gitbooks/en/developing/embed/integrations/storage-drivers.md -->
---
description: "Configure SQLite or MongoDB through Embed and inspect the selected backend."
icon: code
---

# Storage drivers

Enable `storage-sqlite` for SQLite or `storage-mongodb` for MongoDB on your Embed dependency. These Cargo gates make the driver available; choosing a URL selects the runtime backend. Set `RuntimeConfig.storage.url`, pass a URL through `RuntimeBuilder::storage`, or set the `url` field in the `[storage]` section of the host's configuration. `OPENHUMAN_STORAGE_URL` is the environment override used by host configuration resolution.

The SQLite example sets `config.storage.url` to a `sqlite:` URL under a temporary directory, then obtains `Runtime::storage`. It creates a scoped collection, writes a document, reads it back, and deletes it. A configured backend also supplies the default driver-backed session stores; an explicit host session-store adapter takes precedence.

<!-- BEGIN EMBED: crates/openhuman-embed/examples/storage.rs#storage_sqlite -->

```rust
    let mut config = support::offline_config();
    config.storage.url = Some(format!(
        "sqlite:{}",
        directory.path().join("storage").display()
    ));
    let runtime = Runtime::builder()
        .workspace(Workspace::Ephemeral)
        .config(config)
        .backend_url(backend.uri())
        .build()
        .await?;
    let storage = runtime.storage().expect("configured SQLite backend");
    let scoped = storage.for_scope(&Scope::local())?;
    let documents = scoped.documents();
    documents
        .ensure_collection(&CollectionSpec::new("settings"))
        .await?;
    let value = serde_json::json!({"theme":"dark"});
    documents
        .put("settings", "demo", value.clone(), Precondition::Absent)
        .await?;
    assert_eq!(
        documents
            .get("settings", "demo")
            .await?
            .expect("stored value")
            .doc,
        value
    );
    assert!(
        documents
            .delete("settings", "demo", Precondition::None)
            .await?
    );
    assert!(documents.get("settings", "demo").await?.is_none());
    assert_eq!(
        runtime.capabilities().storage.driver.as_deref(),
        Some("sqlite")
    );
```

<!-- END EMBED -->

Run `cargo run -p openhuman-embed --example storage --features storage-sqlite`. This performs real local SQLite writes. The [complete storage example](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/examples/storage.rs) also describes a MongoDB URL without connecting.

<!-- BEGIN EMBED: crates/openhuman-embed/examples/storage.rs#storage_mongodb -->

```rust
    // Enable `storage-mongodb` for a deployment that builds this config.
    // Describing it does not contact MongoDB and never exposes credentials.
    let mut mongo = support::offline_config();
    mongo.storage.url = Some("mongodb://app:example-password@127.0.0.1:27017/openhuman".into());
    let builder = RuntimeBuilder::library().config(mongo);
    let description = builder.describe();
    assert_eq!(description.storage.driver.as_deref(), Some("mongodb"));
    assert!(description.storage.configured);
    assert!(!serde_json::to_string(&description)?.contains("example-password"));
    // An explicit environment variable is the only path to a live database.
    if let Ok(url) = std::env::var("OPENHUMAN_EXAMPLE_MONGODB_URL") {
        let live = RuntimeBuilder::library().storage(url).build().await?;
        assert!(live.storage().is_some(), "MongoDB backend");
        assert_eq!(
            live.capabilities().storage.driver.as_deref(),
            Some("mongodb")
        );
    }
```

<!-- END EMBED -->

`RuntimeBuilder::describe()` reports the driver and whether storage is configured, omitting URL credentials. Describing a MongoDB URL does not prove the server is reachable or that the MongoDB driver was compiled. Building a runtime with that URL requires `storage-mongodb` and a reachable server.

The only live database path in this example is `OPENHUMAN_EXAMPLE_MONGODB_URL`. To exercise it deliberately, enable both `storage-sqlite` and `storage-mongodb` and supply your own MongoDB URL. The offline CI runner clears this setting. A deployed host supplies its persistent URL and credentials through its own configuration.

<!-- Source: docs/gitbooks/en/developing/embed/integrations/tinyhumans-managed.md -->
---
description: "Connect the TinyHumans layer before using managed inference and hosted services."
icon: code
---

# TinyHumans managed inference

Embed wraps the core; neither contains a TinyHumans backend client. A managed host boots through the builder in `openhuman-tinyhumans` (`openhuman_tinyhumans::RuntimeBuilder`), or calls `openhuman_tinyhumans::install`. That layer owns the transport, the authenticated client, the hosted routes and the hosted providers, and it plugs them into the core through the core's generic registries (chat models, voice, tool sources, the channel callback and memory engines). A host can register its own provider in the same registries. The connected host layer also owns session login. `RuntimeBuilder::backend_url` selects a URL; it does not connect anything.

Supply a library backend key with `RuntimeBuilder::api_key` and initialize the process master key before credential storage. Library mode does not perform user login. Managed inference uses the key as a bearer; backend REST calls resolve it through the backend credential seam. Without the TinyHumans layer connected, hosted backend calls return a typed backend-unavailable error.

The SaaS example registers a deterministic managed chat-model provider in the core's chat-model registry and installs separate profile credentials. It verifies managed turns without a live service.

<!-- BEGIN EMBED: crates/openhuman-embed/examples/profiles.rs#profiles -->

```rust
    let profiles = openhuman_embed::ProfileRuntime::build(config).await?;
    let mut handles = Vec::new();
    for (user, text) in [("alice", "Alice private note"), ("bob", "Bob private note")] {
        let profile = profiles.provision(user).await?;
        profiles
            .set_credential(
                &profile.profile_id,
                openhuman_embed::profiles::ProfileCredentialKind::ApiKey,
                &format!("{user}-fixture"),
            )
            .await?;
        let handle = profiles.open(user).await?;
        let reply = handle.chat("shared-thread", text).await?;
        assert!(reply.text.contains(text), "{}", reply.text);
        handles.push(handle);
    }
    assert_ne!(handles[0].workspace_dir(), handles[1].workspace_dir());
    for (index, own, other) in [
        (0, "Alice private note", "Bob private note"),
        (1, "Bob private note", "Alice private note"),
    ] {
        let messages = handles[index].messages("shared-thread").await?;
        let text = serde_json::to_string(&messages)?;
        assert!(text.contains(own));
        assert!(!text.contains(other));
    }
    drop(handles);
    profiles.shutdown().await;
    println!("two profiles keep same-thread conversations separate");
```

<!-- END EMBED -->

See the [complete profiles example](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/examples/profiles.rs) and its shared support transport for the full offline setup. For a deployed SDK-backed host, follow the connected layer's setup instead of copying the loopback transport.

Use [OpenAI-compatible routing](openai-compatible.md) when your host wants to send inference directly to a BYOK endpoint. That explicit provider route does not supply hosted backend services.

<!-- Source: docs/gitbooks/en/developing/embed/integrations/web-search.md -->
---
description: "Pin a web-search provider key per agent, held only in memory."
icon: code
---

# Web search

An agent's web-search tools (`web_search_tool`, `web_answer_tool`, `web_contents_tool`) run through the process-wide TinySearch module. Several agents in one runtime can each search with their own provider key: pin it on that agent's config, and it is never written to `config.toml`, the environment, or a log.

In the agent's `AgentSpec::config` closure, call `config.search.pin_host_credential("brave", SearchHostCredential::new(key))`. `SearchHostCredential` is exported from `openhuman_embed`.

`pin_host_credential` accepts `brave`, `exa`, `gemini`, `parallel`, `querit`, `tavily` and `tinyfish`, and refuses any other provider with `UnknownKeyedSearchProvider`. It turns search on and puts that provider on its direct route, the route a key authenticates; a pinned `gemini` key also serves deep research. `SearchHostCredential::with_base_url` sends the provider's calls to another endpoint, such as a proxy or a local stand-in under test.

While an agent's config carries a pinned key, its search calls run under that config instead of a reload of the runtime's `config.toml`. The module is reconfigured whenever the next call needs a different configuration, and calls take turns, so one agent's call never runs with another agent's key. Agents without a pinned key keep the runtime's settings, environment and default managed routing.

Provider order per role still comes from `search.roles` and TinySearch's defaults. A runtime signed in to the managed backend serves the managed providers first; set `search.roles` on the agent to put the pinned provider first.

The repository's [pinned-key integration test](https://github.com/tinyhumansai/openhuman/blob/main/tests/search_pinned_keys_e2e.rs) runs two agents with different keys against a loopback provider. It checks each call's outgoing key, and that no key reaches the environment, a file or a log line.

<!-- Source: docs/gitbooks/en/developing/embed/quickstart.md -->
---
description: "Run one agent against a local fixture, then choose a provider and working directory for your host."
---

# Quickstart

Start from a checkout of the repository and run `cargo run -p openhuman-embed --example run_turn`. The example boots an ephemeral runtime, creates its own local provider/backend fixtures and prints `hello from the stub`. It needs no inference account and does not contact a real backend.

## Create an agent and send a turn

The full example constructs the runtime before this excerpt. `AgentSpec` names the agent and gives it a bare prompt with `HostOnly` tools. With no host tools attached, this is a text-only agent: it cannot inherit the operator's built-in tool catalog.

<!-- BEGIN EMBED: crates/openhuman-embed/examples/run_turn.rs#run_turn -->

```rust
    let agent = runtime.agent(
        AgentSpec::new("hello").definition(
            AgentDefinitionSpec::new()
                .bare_prompt("Reply briefly.")
                .tools(ToolScopeSpec::HostOnly),
        ),
    )?;
    let outcome = agent.run("Say hello").await?;
    assert!(!outcome.reply.is_empty());
    if support::offline() {
        assert_eq!(outcome.reply, "hello from the stub");
    }
    println!("{}", outcome.reply);
```

<!-- END EMBED -->

[Complete runnable example](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/examples/run_turn.rs), including runtime construction, imports and fixture setup.

`Agent::run` returns a `TurnOutcome` containing the reply and session identity. Use `Agent::turn(...).session(...)` for subsequent messages in the same conversation, or [stream progress](concepts/turns-sessions.md) when your interface should display deltas as they arrive.

## Connect your own host

Replace the fixture provider with an explicit OpenAI-compatible `Provider` or a native custom model. Give tools an explicit action directory, decide whether autonomy enforcement should be enabled, and keep runtime state separate from acting-tool folders. Configure the Tokio worker stack and credential master key as described in [installation](installation.md).

The example's live path requires `OPENHUMAN_EXAMPLE_LIVE=1` plus `OPENHUMAN_EXAMPLE_BASE_URL`, `OPENHUMAN_EXAMPLE_API_KEY` and `OPENHUMAN_EXAMPLE_MODEL`. Choosing a live path is an explicit decision; setting credentials alone does not turn the default example into a live call.

For managed TinyHumans inference, use the [TinyHumans host integration](integrations/tinyhumans-managed.md), which installs the backend transport. Embed by itself does not install one.

## Extend the example

- Give an analyst and a writer separate tools and folders: [multiple agents](guides/multi-agent.md).
- Serve a request through your own HTTP transport: [deploy a server](guides/deploy-server.md).
- Attach application functions: [tools](concepts/tools.md).
- Isolate authenticated customers: [SaaS guide](guides/saas-multi-tenant.md).
- Keep the setup offline and deterministic: [mock-backend testing](guides/mock-backend-testing.md).

Drop listeners and agent handles before shutting down the runtime. For early removal, await `remove_agent` so retained handles stop admitting turns and the old ID is released only after cleanup.

<!-- Source: docs/gitbooks/en/developing/embed/troubleshooting.md -->
---
description: "Diagnose missing features, blocked turns, credentials, state paths and lifecycle failures."
---

# Embedding troubleshooting

Use the typed error and the effective capability report together. A method that was compiled out is a different problem from a configured method with a provider failure. Keep credentials, user prompts and raw provider payloads out of routine diagnostics.

| Symptom | Check and next step |
| --- | --- |
| `RuntimeError::AlreadyRunning` | Reuse the process runtime; drop old listeners, agents and runtime before restarting. Profiles require their own process mode. |
| Method or type not found at compile time | Check named Embed Cargo gates, especially `mcp`/`skills`. Core compiled flags alone do not activate those facade exports. |
| `CoreError::Unavailable` | Inspect effective domains/modules as well as compiled features. Runtime selections can narrow a compiled family. |
| `BACKEND_UNAVAILABLE` | Install the host backend transport, normally through `openhuman-tinyhumans`, before backend-touching calls. |
| Credential master-key failure | Initialize process master-key state before persisting a runtime API key; check headless key/file setup without printing its value. |
| Invalid or insecure provider route | Supply a nonblank endpoint/key and use HTTPS for remote bearer routes. Loopback fixtures are separate from remote endpoints. |
| Stack overflow on a nested turn | Use the documented Tokio worker stack; the default 2 MiB stack is insufficient. Follow the runnable example support setup. |
| A write is waiting indefinitely | Poll the same agent's approvals or retain an active callback subscription. Check origin, enabled autonomy and trusted action roots. |
| Streaming appears stalled | Drain the bounded progress stream while work runs; inspect the final result. Dropping it cancels forwarding and the turn cooperatively. |
| A skill is absent | Copy a real bundle into the agent's skill directory; symlinked bundles are rejected. New instructions need a new session. |
| Agent registration returns `DuplicateId` | Another instance or teardown still reserves the ID. Wait for removal/last-handle cleanup to finish. |
| Retained handle returns `AgentRemoved` | That instance was removed; obtain a newly registered handle rather than reusing the old one. |
| `Decode` error on a typed facade | Treat it as an API/result-shape mismatch; preserve safe metadata and report a regression. |

## Inspect without exposing credentials

<!-- BEGIN EMBED: crates/openhuman-embed/examples/capability_report.rs#capability_report -->

```rust
    let info = runtime.capabilities();
    assert_eq!(info.schema_version, 1);
    assert!(info.defaults.routed_provider);
    let serialized = serde_json::to_string(&info)?;
    assert!(!serialized.contains("sk-test"));
    assert!(!serialized.contains(&provider.uri()));
    assert!(!serialized.contains(&runtime.workspace_dir().display().to_string()));
    println!("{}", serde_json::to_string_pretty(&info)?);
```

<!-- END EMBED -->

[Complete runnable example](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/examples/capability_report.rs), including runtime construction, imports and fixture setup.

Before boot, `RuntimeBuilder::describe` can report a provisional plan; check `configuration_resolved`. After boot, `Runtime::capabilities` describes resolved defaults, storage source/driver and selection without its URL/key. Runtime events provide content-free lifecycle metadata, while explicit turn progress can contain text and should be handled accordingly.

Reproduce behavior with [offline testing](guides/mock-backend-testing.md) before using a live provider. The [error concept](concepts/errors.md) explains structured kinds and expected-user-state errors.

<!-- Source: crates/openhuman-embed/README.md -->
# OpenHuman Embed

A typed Rust facade for running the OpenHuman core inside your application: one Runtime per process, with independently configured Agents.

Read the [embedding documentation](https://github.com/tinyhumansai/openhuman/blob/main/docs/gitbooks/en/developing/embed/README.md), [installation](https://github.com/tinyhumansai/openhuman/blob/main/docs/gitbooks/en/developing/embed/installation.md), and [quickstart](https://github.com/tinyhumansai/openhuman/blob/main/docs/gitbooks/en/developing/embed/quickstart.md).

The [cookbook](https://github.com/tinyhumansai/openhuman/blob/main/docs/gitbooks/en/developing/embed/cookbook.md) is generated from executable examples. Generate complete local API docs with `cargo doc -p openhuman-embed --all-features --no-deps --open`; this workspace crate is currently unpublished.

Hosts supply transport, credentials and application resources. Runtime settings establish shared defaults; agents narrow provider, access, prompt and tool behavior. Use ProfileRuntime when users require separate credentials and workspaces.

## Cancelling one turn

Acquire `Turn::cancellation_handle()` before sending a turn. The handle is
cloneable and `cancel().await` waits for the turn to stop and for tracked
commands to be reaped. The agent remains available for later turns:



Cancellation is scoped to this turn, including while waiting for inference.
Turns with a cancellation handle use owned interpreter subprocesses rather
than the Node/Python pool, which has no acknowledged per-job abort API. This
trades warm-worker reuse for awaited cleanup; ordinary turns retain pooling.
The `meter` callback fires once on cancellation or a dropped send future,
with `None` when dispatch has not supplied usage yet.

Before send, cancellation prevents dispatch; after completion it is a no-op.
On Unix, the built-in shell, Node, Python and npm commands kill their process
group, including descendants. Other platforms stop the direct command. Host
tools that spawn independent tasks or processes must provide their own cleanup;
MCP server lifecycles remain owned by the agent. Keep polling `send()` while
awaiting cancellation, for example in a spawned task.

## Observing provider dispatches

Attach `Completer::physical_observer` to receive one content-free terminal event
for each admitted provider `ChatModel` invocation. Structured JSON repairs
produce separate events, while request and response content remains private.
Transport-level compatibility retries performed inside one provider invocation
remain within that event, and its latency includes those retries. With a model
budget, the explicit and turn-scoped observers run inside admission and after
gateway charge normalization, so they see the same authoritative amount the
ledger settles. A budget refusal before provider invocation creates no event.
The existing `CompletionObserver` remains a once-per-logical-call callback.

## Steering one turn

Acquire `Turn::steering_handle()` before sending a turn to add messages to it
while it runs. The handle is cloneable and `Send + Sync`; `steer(text)` queues
a user message that the agent loop applies at its next safe checkpoint:
before the first model call, after a tool batch has settled, or when the model
has just written its final answer. In that last case the draft stays on the
transcript, the steer follows it, and the model is called once more, so a
message that arrives during a plain chat turn's only model call still shapes
that turn's reply. Nothing interrupts a model call or a tool already running.
The model sees each steer as `[User steering message]: <text>`.

Nothing is dropped silently. Steers the turn ended without applying come back
on `TurnOutcome::unapplied_steers`, or from `TurnSteering::take_unapplied()`
when the turn failed or was cancelled. A steer sent after the turn ended is
refused with `SteerRefused`, which carries its text (`into_text()`). The host
decides whether to run what came back as the next turn.

## Scoped worker hooks

Hooks can be supplied at three levels: `RuntimeBuilder::tool_hook` /
`post_turn_hook` for all agents, `AgentSpec::tool_hook` / `post_turn_hook`
for one agent, and `Turn::tool_hook` / `post_turn_hook` for one dispatch.
Tool callbacks run in that order. Named agent updates replace only that agent’s callback; per-turn callbacks are additional.
`ToolHookContext` carries the agent/session identity when known, and `cwd`
follows the execution workspace descriptor (including `Turn::cwd`), falling
back to the embedding context's configured action root.
The agent and turn hooks are never installed in the global registry, so
concurrent workers and later turns do not pick up one another's callbacks.
Post-turn callbacks run asynchronously with an owned session snapshot.
Independently spawned tasks that build sessions must explicitly inherit
`tinyagents_hooks::scope::HookScope` to carry scoped hooks.

Gateway attribution headers can be attached to `Route::header(name, value)`
and used with `Turn::route`, or with `Provider::routed(route)` on an agent.
They follow only that route's endpoint and are never saved to configuration,
sent to background providers, or included as values in `Route`'s `Debug`.

### Inline permission and usage policy

`AgentSpec::can_use_tool` and `Turn::can_use_tool` await a host callback before
executing each tool. The callback can wait for an approval UI and return
`ToolHookDecision::Proceed`, `Deny`, or `ProceedWith`. It owns that wait;
returning `Ask` denies execution. These callbacks add to existing tool policies,
and a turn callback cannot override an agent denial.

`AgentSpec::stop_hook` and `Turn::stop_hook` receive cumulative usage after each
completed model call. Return `StopDecision::Continue` to observe usage, or
`Stop` to prevent subsequent calls. Completed tool rounds may still execute;
this is an after-call budget boundary, so hosts must refuse an already exhausted
budget before sending a turn. Provider-reported charges remain authoritative,
including known zero; missing charges remain unknown unless pricing is known.
A policy stop uses a deterministic partial summary rather than spending on
final-answer repair calls.

### Per-turn tools and subprocess environment

`Turn::tools` replaces this turn's host tool belt, including attached sources.
An empty belt revokes host tools; the next turn returns to the agent's belt.
Builtin tools still follow the agent definition. This supplies dynamic tools for
in-process hosts; statically declared MCP servers retain their creation-time
configuration.

`Turn::tool_env` supplies the base environment of owned builtin subprocesses.
Variables absent from it are not inherited from the daemon. The builtin command
builders retain their own security/runtime additions, including Git restrictions,
managed interpreter paths and scratch directories. Scoped turns bypass Node and
Python pools, which cannot acknowledge per-job cancellation or swap a job's
process environment. A host tool that spawns a separate Tokio task must explicitly
carry the command environment and cleanup scopes into that task.

Standalone exact source pins and generated Cargo patches: [consumer setup](CONSUMERS.md).
Ordered fallbacks and required exploration: [routing](ROUTING.md).
Host telemetry and the existing exporter: [observers](OBSERVERS.md).

<!-- Source: docs/gitbooks/en/developing/embedding.md -->
---
description: >-
  How to run the OpenHuman core inside your own Rust program: one Runtime,
  with any number of separately configured Agents on it.
icon: code
---
# Embedding OpenHuman

The embedding documentation now lives in the [Embedding section](embed/README.md).

Start with [installation](embed/installation.md) and the [quickstart](embed/quickstart.md), then use the [concepts](embed/concepts/README.md), [guides](embed/guides/README.md), [integrations](embed/integrations/README.md) and generated [cookbook](embed/cookbook.md).

The public library uses one Runtime per process with independently configured Agents. For isolated authenticated users, read [profiles and SaaS](embed/concepts/profiles-saas.md).

<!-- Source: docs/gitbooks/en/developing/embed/cookbook.md -->
# Cookbook

<!-- Generated by scripts/generate-embed-docs.mjs; do not edit. -->

Executable recipes are indexed from their source headers. Each entry records the declared run mode and any named Embed feature required by the example. Full source includes the surrounding setup and assertions.

## Polling and callback approvals with cancellation

Allow and deny real parked writes, then cancel an in-flight callback without losing its pending request.

[Source](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/examples/approvals.rs) · offline with loopback stubs; no live path.

Run: `cargo run -p openhuman-embed --example approvals`

## Stable capability metadata before and after boot

Stable capability metadata before and after boot.

[Source](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/examples/capability_report.rs) · offline with loopback stubs; no live path.

Run: `cargo run -p openhuman-embed --example capability_report`

## Stateless completion without booting an agent runtime

Stateless completion without booting an agent runtime.

[Source](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/examples/completer.rs) · offline; optional live via OPENHUMAN_EXAMPLE_LIVE=1 and BASE_URL/API_KEY/MODEL.

Run: `cargo run -p openhuman-embed --example completer`

## Run and inspect an agent cron job

Run and inspect an agent cron job.

[Source](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/examples/cron.rs) · offline; optional live via OPENHUMAN_EXAMPLE_LIVE=1 and BASE_URL/API_KEY/MODEL.

Run: `cargo run -p openhuman-embed --example cron`

## Native custom providers, fallback and workload role pins

Inject deterministic native models and prove fallback and role selection without inference HTTP.

[Source](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/examples/custom_provider.rs) · offline; no live inference path.

Run: `cargo run -p openhuman-embed --example custom_provider`

## Runtime defaults and per-agent overrides

Runtime defaults and per-agent overrides.

[Source](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/examples/defaults_overrides.rs) · offline; optional live via OPENHUMAN_EXAMPLE_LIVE=1 and BASE_URL/API_KEY/MODEL.

Run: `cargo run -p openhuman-embed --example defaults_overrides`

## A per-agent post-turn hook observes completed turns

A per-agent post-turn hook observes completed turns.

[Source](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/examples/hooks.rs) · offline; optional live via OPENHUMAN_EXAMPLE_LIVE=1 and BASE_URL/API_KEY/MODEL.

Run: `cargo run -p openhuman-embed --example hooks`

## Host tools and HostOnly keep the advertised tool catalog exact

Host tools and HostOnly keep the advertised tool catalog exact.

[Source](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/examples/host_tools.rs) · offline with loopback stubs; no live path.

Run: `cargo run -p openhuman-embed --example host_tools`

## Lean runtime without background services

Lean runtime without background services.

[Source](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/examples/lean_headless.rs) · offline with loopback stubs; optional live via OPENHUMAN_EXAMPLE_LIVE.

Run: `cargo run -p openhuman-embed --example lean_headless`

## Linux agent fleet memory and latency

Measure retained runtime-owned agents using loopback inference and two worker threads.

[Source](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/examples/linux_fleet.rs) · offline on Linux; use a fresh constrained cgroup for release measurements. · feature: mcp,storage-sqlite

Run: `cargo run -p openhuman-embed --release --example linux_fleet --no-default-features --features mcp,storage-sqlite`

## Connect an actual MCP protocol stub over loopback

Connect an actual MCP protocol stub over loopback.

[Source](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/examples/mcp.rs) · offline with loopback stubs; no live path. · feature: mcp,storage-sqlite

Run: `cargo run -p openhuman-embed --example mcp --features mcp,storage-sqlite`

## Tenant scoped memory facade with an in-memory engine

Tenant scoped memory facade with an in-memory engine.

[Source](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/examples/memory.rs) · offline with loopback stubs; no live path.

Run: `cargo run -p openhuman-embed --example memory`

## SaaS profiles isolate conversation history

SaaS profiles isolate conversation history.

[Source](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/examples/profiles.rs) · offline with loopback stubs; no live path. · feature: channels,storage-sqlite

Run: `cargo run -p openhuman-embed --example profiles --features channels,storage-sqlite`

## Hello agent: a prompt in and a reply out

Hello agent: a prompt in and a reply out.

[Source](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/examples/run_turn.rs) · offline; optional live via OPENHUMAN_EXAMPLE_LIVE=1 and BASE_URL/API_KEY/MODEL.

Run: `cargo run -p openhuman-embed --example run_turn`

## Runtime lifecycle events and live hook registration

Observe ordered metadata and add/remove a runtime-wide hook while agents keep running.

[Source](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/examples/runtime_events.rs) · offline with a loopback provider; no live path.

Run: `cargo run -p openhuman-embed --example runtime_events`

## Access tiers and explicit sandbox choices

Access tiers and explicit sandbox choices.

[Source](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/examples/sandbox_access.rs) · offline with loopback stubs; no live path.

Run: `cargo run -p openhuman-embed --example sandbox_access`

## Embed an agent behind a host-owned HTTP server

Embed an agent behind a host-owned HTTP server.

[Source](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/examples/server.rs) · offline; optional live via OPENHUMAN_EXAMPLE_LIVE=1 and BASE_URL/API_KEY/MODEL.

Run: `cargo run -p openhuman-embed --example server`

## Copy a skill bundle into its agent workspace

Copy a skill bundle into its agent workspace.

[Source](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/examples/skills.rs) · offline; optional live via OPENHUMAN_EXAMPLE_LIVE=1 and BASE_URL/API_KEY/MODEL. · feature: skills

Run: `cargo run -p openhuman-embed --example skills --features skills`

## SQLite writes and MongoDB configuration through Embed

Open a real local SQLite backend and describe a MongoDB deployment without connecting.

[Source](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/examples/storage.rs) · offline; optional live MongoDB with OPENHUMAN_EXAMPLE_MONGODB_URL and storage-mongodb. · feature: storage-sqlite

Run: `cargo run -p openhuman-embed --example storage --features storage-sqlite`

## Streaming progress with a draining receiver

Streaming progress with a draining receiver.

[Source](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/examples/streaming.rs) · offline; optional live via OPENHUMAN_EXAMPLE_LIVE=1 and BASE_URL/API_KEY/MODEL.

Run: `cargo run -p openhuman-embed --example streaming`

## Structured output parsed and checked against the request

Structured output parsed and checked against the request.

[Source](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/examples/structured_output.rs) · offline with loopback stubs; no live path.

Run: `cargo run -p openhuman-embed --example structured_output`

## Telegram channel answered by its bound agent via a local Bot API

Telegram channel answered by its bound agent via a local Bot API.

[Source](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/examples/telegram.rs) · offline with loopback stubs; no live path. · feature: channels

Run: `cargo run -p openhuman-embed --example telegram --features channels`

## Two agents with independent prompts and working folders

Two agents with independent prompts and working folders.

[Source](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/examples/two_agents.rs) · offline; optional live via OPENHUMAN_EXAMPLE_LIVE=1 and BASE_URL/API_KEY/MODEL.

Run: `cargo run -p openhuman-embed --example two_agents`

<!-- Source: docs/gitbooks/en/developing/embed/capability-matrix.md -->
# Compiled feature matrix

<!-- Generated by scripts/generate-embed-docs.mjs; do not edit. -->

This matrix comes from the default-feature compiled capability-report example. Runtime presets can narrow enabled domains and services; core Cargo features determine which implementations are available. Named Embed features also gate facade exports: enable `mcp` or `skills` explicitly for their public setters even when core defaults compile those implementations. Embed defaults enable the named `channels` feature. See the installation guide for these separate layers.

| Cargo feature | Compiled in this build |
| --- | --- |
| `channels` | Yes |
| `crash-reporting` | No |
| `default` | Yes |
| `documents` | No |
| `e2e-test-support` | No |
| `file-logging` | Yes |
| `flows` | Yes |
| `hosting` | No |
| `http-server` | Yes |
| `inbound-queues` | Yes |
| `inference` | No |
| `mcp` | Yes |
| `media` | Yes |
| `modules` | Yes |
| `rss-bench` | No |
| `scheduler-gate` | Yes |
| `skills` | Yes |
| `storage-file` | No |
| `storage-mongodb` | No |
| `storage-s3` | No |
| `storage-sqlite` | No |
| `voice` | No |
| `web3` | No |

<!-- Source: docs/gitbooks/en/developing/embed/builder-setters.md -->
# RuntimeBuilder setters

<!-- Generated by scripts/generate-embed-docs.mjs; do not edit. -->

Public consuming methods that return `Self`, extracted from the RuntimeBuilder implementations. Static host and weight presets, inspection methods and build/run methods are excluded.

- [`access`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/runtime/builder.rs#L385): `pub fn access(mut self, access: Access) -> Self`
- [`action_dir`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/runtime/builder.rs#L330): `pub fn action_dir(mut self, dir: impl Into<PathBuf>) -> Self`
- [`agent_defaults`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/runtime/builder.rs#L159): `pub fn agent_defaults(mut self, defaults: super::AgentDefaults) -> Self`
- [`api_key`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/runtime/builder.rs#L362): `pub fn api_key(mut self, key: impl Into<ApiKey>) -> Self`
- [`autonomy`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/runtime/builder.rs#L194): `pub fn autonomy(mut self, value: openhuman_core::config::schema::AutonomyConfig) -> Self`
- [`backend_url`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/runtime/builder.rs#L372): `pub fn backend_url(mut self, url: impl Into<String>) -> Self`
- [`boot_mode`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/runtime/builder.rs#L350): `pub fn boot_mode(mut self, mode: CoreBootMode) -> Self`
- [`config`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/runtime/builder.rs#L502): `pub fn config(mut self, config: Config) -> Self`
- [`config_source`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/runtime/builder.rs#L338): `pub fn config_source(mut self, source: ConfigSource) -> Self`
- [`configure_core_extensions`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/runtime/seams.rs#L435): `pub fn configure_core_extensions(self, configure: impl FnOnce(&Extensions)) -> Self`
- [`controller_extension`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/runtime/seams.rs#L447): `pub fn controller_extension(mut self, extension: ControllerExtension) -> Self`
- [`cron`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/runtime/builder.rs#L219): `pub fn cron(mut self, value: openhuman_core::config::schema::CronConfig) -> Self`
- [`cron_delivery`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/runtime/builder.rs#L264): `pub fn cron_delivery( mut self, delivery: Arc<dyn openhuman_core::cron::host_delivery::CronDelivery>, ) -> Self`
- [`cron_job_handler`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/runtime/builder.rs#L278): `pub fn cron_job_handler( mut self, kind: crate::cron::JobKind, handler: Arc<dyn crate::cron::JobKindHandler>, ) -> Self`
- [`defer_services`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/runtime/builder.rs#L420): `pub fn defer_services(mut self) -> Self`
- [`definition_base`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/runtime/builder.rs#L178): `pub fn definition_base(mut self, definition: crate::AgentDefinitionSpec) -> Self`
- [`domains`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/runtime/builder.rs#L432): `pub fn domains(mut self, domains: DomainSet) -> Self`
- [`host_kind`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/runtime/builder.rs#L449): `pub fn host_kind(mut self, host_kind: HostKind) -> Self`
- [`learning`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/runtime/builder.rs#L214): `pub fn learning(mut self, value: super::LearningSettings) -> Self`
- [`listen`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/runtime/builder.rs#L471): `pub fn listen(self, host: impl Into<String>, port: u16) -> Self`
- [`listen_host`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/runtime/builder.rs#L476): `pub fn listen_host(mut self, host: impl Into<String>) -> Self`
- [`listen_port`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/runtime/builder.rs#L482): `pub fn listen_port(mut self, port: u16) -> Self`
- [`live_policy`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/runtime/seams.rs#L496): `pub fn live_policy(mut self, policy: Arc<SecurityPolicy>) -> Self`
- [`max_agents`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/runtime/builder.rs#L293): `pub fn max_agents(mut self, limit: usize) -> Self`
- [`mcp_baseline`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/runtime/builder.rs#L189): `pub fn mcp_baseline(mut self, servers: impl IntoIterator<Item = crate::McpServer>) -> Self`
- [`memory_engine`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/runtime/builder.rs#L233): `pub fn memory_engine(mut self, engine: Arc<dyn tinymemory_api::MemoryEngine>) -> Self`
- [`model_defaults`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/runtime/builder.rs#L168): `pub fn model_defaults(mut self, model: super::ModelDefaults) -> Self`
- [`modules`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/runtime/selection.rs#L155): `pub fn modules(mut self, modules: impl IntoIterator<Item = RuntimeModule>) -> Self`
- [`post_turn_hook`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/runtime/seams.rs#L462): `pub fn post_turn_hook(mut self, hook: Arc<dyn LearningSink>) -> Self`
- [`privacy`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/runtime/builder.rs#L204): `pub fn privacy(mut self, value: openhuman_core::config::schema::PrivacyConfig) -> Self`
- [`provider`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/runtime/builder.rs#L378): `pub fn provider(mut self, provider: Provider) -> Self`
- [`sandbox`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/runtime/builder.rs#L173): `pub fn sandbox(mut self, mode: crate::SandboxModeSpec) -> Self`
- [`secrets`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/runtime/builder.rs#L209): `pub fn secrets(mut self, value: openhuman_core::config::schema::SecretsConfig) -> Self`
- [`server_launcher`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/runtime/seams.rs#L476): `pub fn server_launcher(mut self, launcher: ServerLauncher) -> Self`
- [`services`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/runtime/builder.rs#L404): `pub fn services(mut self, services: ServiceSet) -> Self`
- [`session`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/runtime/builder.rs#L492): `pub fn session(mut self, session: Session) -> Self`
- [`session_store`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/runtime/builder.rs#L248): `pub fn session_store(mut self, provider: Arc<dyn SessionStoreProvider>) -> Self`
- [`skills`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/runtime/builder.rs#L183): `pub fn skills(mut self, policy: super::SkillsPolicy) -> Self`
- [`storage`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/runtime/seams.rs#L489): `pub fn storage(mut self, source: impl Into<StorageSource>) -> Self`
- [`token`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/runtime/builder.rs#L459): `pub fn token(mut self, token: TokenSource) -> Self`
- [`tool_groups`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/runtime/builder.rs#L441): `pub fn tool_groups(mut self, tool_groups: ToolGroups) -> Self`
- [`tool_hook`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/runtime/seams.rs#L469): `pub fn tool_hook(mut self, hook: Arc<dyn ToolHook<OpenHumanRunContext>>) -> Self`
- [`tool_ranker`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/runtime/seams.rs#L454): `pub fn tool_ranker(mut self, ranker: Arc<dyn ToolRanker>) -> Self`
- [`tool_rules`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/runtime/builder.rs#L199): `pub fn tool_rules(mut self, value: tinytools::ToolRules) -> Self`
- [`workspace`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/runtime/builder.rs#L303): `pub fn workspace(mut self, workspace: Workspace) -> Self`
- [`workspace_dir`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/runtime/builder.rs#L317): `pub fn workspace_dir(mut self, dir: impl Into<PathBuf>) -> Self`

<!-- Source: docs/gitbooks/en/developing/embed/api-index.md -->
# API index

<!-- Generated by scripts/generate-embed-docs.mjs; do not edit. -->

Generate the complete local Rust API reference with `cargo doc -p openhuman-embed --all-features --no-deps --open`. This crate is currently unpublished; docs.rs is a future publication target. Names below are source-listed exports, including conditional Cargo-gated items; local rustdoc with your selected features describes availability. [Canonical facade source](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs).

- [`Access`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`Agent`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`AgentDefaults`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`AgentDefinition`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`AgentDefinitionSpec`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`AgentError`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`AgentLayout`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`AgentProgress`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`AgentSpec`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`AgentTurnOrigin`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`ApiKey`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`ApprovalDecision`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`ApprovalHandler`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`ApprovalSubscription`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`Approvals`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`ApprovalsError`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`Auth`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`AuthState`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`CancellationToken`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`ChannelError`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`ChannelListener`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`Channels`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`ChatMessage`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`ChatReply`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`CompactOptions`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`CompactOutcome`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`Completer`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`CompletionObserver`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`CompletionRequest`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`CompletionResponse`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`CompletionTrace`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`CompletionUsage`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`ComposioHostCredential`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`Config`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`ConfigSource`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`ConfigurationInfo`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`ControllerSchema`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`CoreAgent`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`CoreBootMode`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`CoreBuilder`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`CoreError`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`CoreRuntime`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`Cron`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`CronDelivery`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`CronError`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`CronReply`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`CronSweepReport`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`DEFAULT_MAX_AGENTS`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`DaemonConfig`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`DefaultsInfo`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`DefinitionBase`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`DirectProfileProvider`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`DomainSet`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`EventStreamError`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`GroupMode`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`HTTP_SERVER_COMPILED_IN`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`Harness`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`HarnessBuilder`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`HarnessCore`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`HarnessError`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`HostAttachment`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`HostKind`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`HostTools`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`HostTurnTools`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`HttpHeader`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`InMemorySessionStores`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`JobKind`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`JobKindHandler`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`JobOrigin`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`JobOutcome`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`JobRun`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`JobRunContext`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`JobRunRecord`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`JobSchedule`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`JobSpec`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`JobTarget`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`LastTurnUsage`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`LearningSettings`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`McpAuthConfig`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`McpServer`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`MemoryBinding`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`MemoryBlockSpec`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`ModelDefaults`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`NoTenant`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`OpenError`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`PendingApproval`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`PermissionFuture`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`PermissionLevel`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`PhysicalDispatchMetadata`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`PhysicalDispatchObserver`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`PhysicalDispatchOutcome`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`PhysicalDispatchStatus`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`PhysicalDispatchTerminal`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`PhysicalDispatchUsage`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`PickListenPortError`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`ProfileError`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`ProfileEvents`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`ProfileHandle`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`ProfileId`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`ProfileIdMode`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`ProfileRuntime`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`ProfileRuntimeBuilder`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`ProfileSummary`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`Provider`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`Provisioned`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`ReasoningEffort`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`RelayAccepted`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`RelayMessage`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`RemoveAgent`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`Route`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`Runtime`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`RuntimeBuilder`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`RuntimeConfig`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`RuntimeDefaults`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`RuntimeError`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`RuntimeEvent`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`RuntimeEventKind`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`RuntimeEvents`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`RuntimeFlags`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`RuntimeInfo`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`RuntimeModule`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`SaasConfig`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`SandboxModeSpec`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`ScheduledJob`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`SearchHostCredential`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`ServiceSet`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`Session`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`SessionStoreProvider`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`SkillsPolicy`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`SteerRefused`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`StorageInfo`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`StreamEvent`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`StreamMode`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`SystemJobContext`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`TelegramChannelSpec`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`Tenant`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`TokenSource`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`Tool`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`ToolAttachmentError`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`ToolExposure`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`ToolGroups`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`ToolPolicy`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`ToolResult`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`ToolScopeSpec`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`TrustedAccess`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`TrustedAutomationSource`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`Turn`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`TurnCancellation`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`TurnContext`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`TurnOutcome`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`TurnRequest`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`TurnSteering`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`TurnStream`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`UnknownKeyedSearchProvider`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`VOICE_COMPILED_IN`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`WeightClass`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`Workspace`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`absolute`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`agent_progress`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`artifacts`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`budget`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`cancellation`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`channels`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`chat_surface`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`cli`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`complete`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`config`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`cron`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`current_tenant`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`embeddings`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`events`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`fanout`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`identity`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`inbound`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`memory`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`modules`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`observe`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`process`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`profiles`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`providers`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`routing`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`run_from_args`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`schema_for_rpc_method`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`seams`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`secret_storage`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`session_store`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`skill_registry`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`stream`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)
- [`structured`](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/src/lib.rs)

The compiled capability report describes this build:

```json
{
  "schema_version": 1,
  "configuration_resolved": true,
  "compiled_features": {
    "channels": true,
    "crash-reporting": false,
    "default": true,
    "documents": false,
    "e2e-test-support": false,
    "file-logging": true,
    "flows": true,
    "hosting": false,
    "http-server": true,
    "inbound-queues": true,
    "inference": false,
    "mcp": true,
    "media": true,
    "modules": true,
    "rss-bench": false,
    "scheduler-gate": true,
    "skills": true,
    "storage-file": false,
    "storage-mongodb": false,
    "storage-s3": false,
    "storage-sqlite": false,
    "voice": false,
    "web3": false
  },
  "domains": [
    "agent",
    "memory",
    "threads",
    "config",
    "security",
    "flows",
    "skills",
    "channels",
    "inference",
    "integrations",
    "automation",
    "runtimes",
    "platform"
  ],
  "services": [],
  "tool_groups": {
    "coding": "withheld",
    "composio": "withheld",
    "docs": "withheld",
    "documents": "withheld",
    "goals": "withheld",
    "mcp": "withheld",
    "media": "withheld",
    "scheduling": "withheld",
    "skills": "withheld",
    "storage": "withheld",
    "system": "withheld",
    "web3": "withheld",
    "workflows": "withheld"
  },
  "host_kind": "Library",
  "max_agents": 1024,
  "defaults": {
    "model": {
      "temperature": 0.7,
      "max_tokens": null,
      "top_p": null,
      "max_iterations": 200
    },
    "provider_model": null,
    "routed_provider": false,
    "sandbox": "None",
    "approval_gate": true,
    "include_user_skills": false,
    "has_skills_root": false,
    "mcp_servers": 0,
    "templates": []
  },
  "configuration": {
    "autonomy_enabled": true,
    "privacy": "Standard",
    "secrets_encrypted": true,
    "cron_enabled": true,
    "learning": {
      "build_beliefs_every": 10,
      "learnings_limit": 8
    },
    "has_tool_rules": false
  },
  "storage": {
    "source": "workspace",
    "driver": null,
    "configured": false
  },
  "modules": [],
  "weight": "custom"
}
```

<!-- Source: crates/openhuman-embed/examples/README.md -->
# Embed examples

<!-- Generated by scripts/generate-embed-docs.mjs; do not edit. -->

[Cookbook](../../../docs/gitbooks/en/developing/embed/cookbook.md)

- [Polling and callback approvals with cancellation](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/examples/approvals.rs): Allow and deny real parked writes, then cancel an in-flight callback without losing its pending request. (offline with loopback stubs; no live path.)
- [Stable capability metadata before and after boot](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/examples/capability_report.rs): Stable capability metadata before and after boot. (offline with loopback stubs; no live path.)
- [Stateless completion without booting an agent runtime](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/examples/completer.rs): Stateless completion without booting an agent runtime. (offline; optional live via OPENHUMAN_EXAMPLE_LIVE=1 and BASE_URL/API_KEY/MODEL.)
- [Run and inspect an agent cron job](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/examples/cron.rs): Run and inspect an agent cron job. (offline; optional live via OPENHUMAN_EXAMPLE_LIVE=1 and BASE_URL/API_KEY/MODEL.)
- [Native custom providers, fallback and workload role pins](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/examples/custom_provider.rs): Inject deterministic native models and prove fallback and role selection without inference HTTP. (offline; no live inference path.)
- [Runtime defaults and per-agent overrides](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/examples/defaults_overrides.rs): Runtime defaults and per-agent overrides. (offline; optional live via OPENHUMAN_EXAMPLE_LIVE=1 and BASE_URL/API_KEY/MODEL.)
- [A per-agent post-turn hook observes completed turns](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/examples/hooks.rs): A per-agent post-turn hook observes completed turns. (offline; optional live via OPENHUMAN_EXAMPLE_LIVE=1 and BASE_URL/API_KEY/MODEL.)
- [Host tools and HostOnly keep the advertised tool catalog exact](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/examples/host_tools.rs): Host tools and HostOnly keep the advertised tool catalog exact. (offline with loopback stubs; no live path.)
- [Lean runtime without background services](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/examples/lean_headless.rs): Lean runtime without background services. (offline with loopback stubs; optional live via OPENHUMAN_EXAMPLE_LIVE.)
- [Linux agent fleet memory and latency](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/examples/linux_fleet.rs): Measure retained runtime-owned agents using loopback inference and two worker threads. (offline on Linux; use a fresh constrained cgroup for release measurements.; feature: mcp,storage-sqlite)
- [Connect an actual MCP protocol stub over loopback](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/examples/mcp.rs): Connect an actual MCP protocol stub over loopback. (offline with loopback stubs; no live path.; feature: mcp,storage-sqlite)
- [Tenant scoped memory facade with an in-memory engine](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/examples/memory.rs): Tenant scoped memory facade with an in-memory engine. (offline with loopback stubs; no live path.)
- [SaaS profiles isolate conversation history](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/examples/profiles.rs): SaaS profiles isolate conversation history. (offline with loopback stubs; no live path.; feature: channels,storage-sqlite)
- [Hello agent: a prompt in and a reply out](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/examples/run_turn.rs): Hello agent: a prompt in and a reply out. (offline; optional live via OPENHUMAN_EXAMPLE_LIVE=1 and BASE_URL/API_KEY/MODEL.)
- [Runtime lifecycle events and live hook registration](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/examples/runtime_events.rs): Observe ordered metadata and add/remove a runtime-wide hook while agents keep running. (offline with a loopback provider; no live path.)
- [Access tiers and explicit sandbox choices](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/examples/sandbox_access.rs): Access tiers and explicit sandbox choices. (offline with loopback stubs; no live path.)
- [Embed an agent behind a host-owned HTTP server](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/examples/server.rs): Embed an agent behind a host-owned HTTP server. (offline; optional live via OPENHUMAN_EXAMPLE_LIVE=1 and BASE_URL/API_KEY/MODEL.)
- [Copy a skill bundle into its agent workspace](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/examples/skills.rs): Copy a skill bundle into its agent workspace. (offline; optional live via OPENHUMAN_EXAMPLE_LIVE=1 and BASE_URL/API_KEY/MODEL.; feature: skills)
- [SQLite writes and MongoDB configuration through Embed](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/examples/storage.rs): Open a real local SQLite backend and describe a MongoDB deployment without connecting. (offline; optional live MongoDB with OPENHUMAN_EXAMPLE_MONGODB_URL and storage-mongodb.; feature: storage-sqlite)
- [Streaming progress with a draining receiver](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/examples/streaming.rs): Streaming progress with a draining receiver. (offline; optional live via OPENHUMAN_EXAMPLE_LIVE=1 and BASE_URL/API_KEY/MODEL.)
- [Structured output parsed and checked against the request](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/examples/structured_output.rs): Structured output parsed and checked against the request. (offline with loopback stubs; no live path.)
- [Telegram channel answered by its bound agent via a local Bot API](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/examples/telegram.rs): Telegram channel answered by its bound agent via a local Bot API. (offline with loopback stubs; no live path.; feature: channels)
- [Two agents with independent prompts and working folders](https://github.com/tinyhumansai/openhuman/blob/main/crates/openhuman-embed/examples/two_agents.rs): Two agents with independent prompts and working folders. (offline; optional live via OPENHUMAN_EXAMPLE_LIVE=1 and BASE_URL/API_KEY/MODEL.)
