How Agents Are Managed and Executed Within the OpenHuman Rust Core

The OpenHuman Rust core implements agents as data-driven sub-processes defined by TOML specifications, indexed in a global singleton registry, and executed through the spawn_subagent meta-tool which creates isolated turn loops that collapse their interaction history into singular tool results.

In the OpenHuman framework, agents are not monolithic external services but composable, sandboxed sub-processes governed by declarative definitions. Understanding how agents are managed and executed within the OpenHuman Rust core reveals a lightweight architecture that enables dynamic tool filtering, prompt customization, and secure resource inheritance without spawning separate OS processes. The system leverages the AgentDefinitionRegistry and a dedicated sub-agent runner to orchestrate these workflows using the same Tokio-based turn machinery that powers parent agents.

Agent Definitions and the Global Registry

At the heart of the system lies the AgentDefinition struct located in src/openhuman/agent/harness/definition.rs. This structure encapsulates everything required to configure a sub-agent: its identifier, system prompt, target model, tool whitelist, runtime limits, and flags that control which parts of the parent prompt are omitted.

Built-in agent definitions are stored as TOML files within src/openhuman/agent/registry/agents/…/agent.toml and loaded at startup by the definition_loader in src/openhuman/agent/registry/agents/loader.rs. Users can extend the system by placing custom definitions under $OPENHUMAN_WORKSPACE/agents/*.toml, which the loader discovers and ingests during initialization.

All definitions are indexed in the AgentDefinitionRegistry, a singleton accessible via AgentDefinitionRegistry::global(). This registry populates at core startup through AgentDefinitionRegistry::init() and enables runtime validation of agent_id requests across the entire system.

// Load a custom agent definition at runtime
let registry = AgentDefinitionRegistry::global().unwrap();
let my_def = registry
    .list()
    .iter()
    .find(|d| d.id == "researcher")
    .expect("definition missing");

The spawn_subagent Meta-Tool

The harness constructs the parent agent’s tool list from the core tool catalog in src/openhuman/tools/mod.rs. Among these tools, the meta-tool SpawnSubagentTool (defined in src/openhuman/agent/orchestration/tools/spawn_subagent.rs) serves as the entry point for agent delegation.

When a parent turn invokes this tool, the implementation performs several critical operations:

  1. Registry Lookup: Validates the requested agent_id against the global registry.
  2. Tool Filtering: Intersects the parent’s available tools with the sub-agent’s tools whitelist, excluding any listed in disallowed_tools.
  3. Prompt Construction: Generates a narrow system prompt for the child based on the AgentDefinition configuration.
// Invoke a sub-agent from a parent turn
let tool = SpawnSubagentTool::new();
let args = json!({
    "agent_id": "researcher",
    "prompt": "Summarise the latest research papers on Rust async patterns.",
    "blocking": true
});
let result = tool.execute(args).await?;
println!("Sub-agent answer: {}", result.output());

Sub-Agent Execution Flow

Execution is handled by the sub-agent runner located in src/openhuman/agent/harness/subagent_runner/. The entry point run_subagent receives the definition, prompt, optional context, and the parent’s provider, then determines the execution mode.

Inline Execution: Runs on the same Tokio task, suitable for simple, blocking operations.

Async Worker Execution: Creates a reusable worker (default) that persists beyond the initial call, enabling stateful interactions addressed via a task_key.

The dispatch flow follows this explicit path through the codebase:

The sub-agent’s entire interaction loop—model calls, tool invocations, and policy checks—runs exactly like a normal turn, but its history collapses into a single result object.

// Create a reusable async worker addressable by task_key
let args = json!({
    "agent_id": "code_executor",
    "prompt": "Write a function that computes Fibonacci numbers.",
    "task_key": "fib_generator",
    "fresh": false   // reuse existing worker if present
});
let _ = tool.execute(args).await?;

Security and Resource Boundaries

Sub-agents inherit the parent’s security gate (src/openhuman/agent/tinyagents/host/security_gate.rs) and budget gate, ensuring that sandboxing policies propagate downstream. However, sub-agents also respect their own specific limits defined in AgentDefinition:

  • max_iterations: Caps the number of reasoning steps
  • timeout_secs: Hard limit on execution duration
  • max_result_chars: Restricts output payload size

The iteration_policy field, including iteration_policy::Extended, allows longer workflows when the default constraints prove too restrictive for complex tasks.

Lifecycle and Persistence Management

The registry populates during core initialization and remains immutable during runtime. Sub-agent lifecycle management depends on the invocation mode:

  • Blocking calls: State exists only for the duration of the turn; in-memory history is discarded immediately after result delivery.
  • Reusable workers: When invoked with a task_key and fresh: false, the worker persists in memory and can be addressed later via src/openhuman/agent/orchestration/tools/spawn_worker_thread.rs.

This architecture ensures that agents remain pure data objects—composable, observable, and subject to the same logging and policy layers that protect the parent turn.

Summary

  • AgentDefinition structs in src/openhuman/agent/harness/definition.rs store complete sub-agent specifications as TOML-driven data objects.
  • The AgentDefinitionRegistry singleton provides global access to agent definitions at runtime via AgentDefinitionRegistry::global().
  • SpawnSubagentTool in src/openhuman/agent/orchestration/tools/spawn_subagent.rs filters parent tools and initiates sub-agent execution.
  • The sub-agent runner reuses the core turn machinery in src/openhuman/agent/harness/subagent_runner/ while isolating execution contexts.
  • Sub-agents inherit parent security gates but respect their own max_iterations, timeout_secs, and max_result_chars limits.
  • Reusable async workers persist via task_key references, while blocking executions clean up immediately after result return.

Frequently Asked Questions

How do I register a custom agent definition in OpenHuman?

Place a TOML file following the AgentDefinition schema in $OPENHUMAN_WORKSPACE/agents/*.toml. The definition_loader in src/openhuman/agent/registry/agents/loader.rs automatically discovers and registers these files during core startup, making them available through AgentDefinitionRegistry::global().

What is the difference between blocking and reusable sub-agent execution?

Blocking execution runs the sub-agent inline on the same Tokio task and discards its state immediately after returning the result. Reusable execution creates an async worker that persists in memory, allowing subsequent calls to reference the same state via a task_key, as implemented in src/openhuman/agent/orchestration/tools/spawn_worker_thread.rs.

How does the parent agent's tool set get filtered for sub-agents?

The SpawnSubagentTool::execute method intersects the parent’s available tools from src/openhuman/tools/mod.rs with the sub-agent’s tools whitelist, then removes any tools specified in disallowed_tools. This ensures sub-agents operate with the minimal necessary capability surface.

Where are sub-agent execution limits enforced?

Limits defined in AgentDefinition—including max_iterations, timeout_secs, and max_result_chars—are enforced by the sub-agent runner in src/openhuman/agent/harness/subagent_runner/ops/runner.rs. Additionally, the parent’s security and budget gates from src/openhuman/agent/tinyagents/host/security_gate.rs wrap the sub-agent execution context.

Have a question about this repo?

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

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

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

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