# How OpenHuman Handles Tool Disclosure Using Pack-Based Methods

> Discover how OpenHuman's pack-based methods manage tool disclosure with Advertised, Withheld, and Off modes. Learn to control LLM prompt visibility while retaining runtime access via PackRegistryHandle.

- Repository: [Tiny Humans/openhuman](https://github.com/tinyhumansai/openhuman)
- Tags: deep-dive
- Published: 2026-08-29

---

**OpenHuman implements a three-stage, pack-centric disclosure system that uses the `GroupMode` enum—`Advertised`, `Withheld`, and `Off`—to control which tools appear in LLM prompts while maintaining full runtime access through the `PackRegistryHandle`.**

The `tinyhumansai/openhuman` repository provides a robust framework for AI agent tool management through pack-based disclosure methods. This architecture allows developers to selectively control tool visibility in language model prompts without sacrificing the ability to invoke any registered tool at runtime.

## Core Components of Pack-Based Disclosure

The tool disclosure system relies on several key structures defined across the OpenHuman codebase. These components work together to separate tool registration from tool visibility.

### PackRegistryHandle

The **`PackRegistryHandle`** serves as the central registry holding all tools bound to the core during a turn. Defined in [`src/openhuman/tools/traits.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/tools/traits.rs) at lines 235-242, this handle persists the complete tool set throughout the agent's lifecycle, ensuring withheld tools remain callable even when excluded from prompts.

### GroupMode and ToolGroups

Tool visibility is controlled through the **`GroupMode`** enum and **`ToolGroups`** configuration structure located in [`src/openhuman/tools/toolpacks/groups.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/tools/toolpacks/groups.rs). The `GroupMode` enum defines three disclosure states:

- **`Advertised`** — Tools appear in the LLM prompt's tool catalog
- **`Withheld`** — Tools remain callable but are hidden from the prompt  
- **`Off`** — Tools are completely disabled and unregistered

The `ToolGroups` builder allows hosts to specify disclosure policies for entire tool packs, enabling fine-grained control over which functional domains the model can access.

### Filtering and Binding Functions

The system implements specific functions to manage the transition from configuration to execution:

- **`strip_packed_from_visible`** in [`src/openhuman/agent/harness/session/turn/tools.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/agent/harness/session/turn/tools.rs) at line 612 filters the tool list before sending to the LLM, removing any tools whose `GroupMode` is not `Advertised`

- **`bind_pack_registry`** in [`src/openhuman/agent/harness/session/builder/setters.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/agent/harness/session/builder/setters.rs) at lines 590-595 stores the complete tool set in the `PackRegistryHandle` for later dispatch

- **`append_pack_tools`** in [`src/openhuman/tools/ops.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/tools/ops.rs) at lines 1186-1203 injects pack-based tools into the final tool vector after domain-specific tools are collected

## The Five-Stage Disclosure Pipeline

OpenHuman processes tool disclosure through a sequential pipeline that separates configuration from runtime execution.

### 1. Builder Configuration

When constructing a `Harness` or `CoreBuilder`, developers supply a `ToolGroups` instance to define default disclosure policies. The core builder API in [`src/core/runtime/builder.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/core/runtime/builder.rs) at lines 449-510 exposes the `tool_groups` method for this configuration. Desktop builds typically use `ToolGroups::advertised()` while lightweight embedders may use `ToolGroups::none()`.

### 2. Tool Collection

The system gathers tools from all active domain-specific agents, then appends pack-based tools via `append_pack_tools`. This occurs in [`src/openhuman/tools/ops.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/tools/ops.rs), ensuring the full tool set is assembled before visibility filtering.

### 3. Registry Binding

The `bind_pack_registry` function stores the assembled tool list in a `PackRegistryHandle`. This binding, performed during session building, makes the complete registry available to the dispatcher for resolving tool calls that reference withheld tools.

### 4. Visibility Filtering

Just before transmitting the turn to the LLM, `strip_packed_from_visible` traverses the tool list and removes any tools where `GroupMode` is `Withheld` or `Off`. Only `Advertised` tools render into the prompt's tool catalog, reducing token consumption and preventing exposure of sensitive functionality.

### 5. Runtime Dispatch

If the model references a withheld tool by name—either from prior context or system messages—the dispatcher consults the `PackRegistryHandle` to locate and execute the tool. Because the registry contains all bound tools regardless of disclosure state, withheld tools remain fully functional.

## Configuring Tool Groups in Practice

The framework provides flexible APIs for configuring tool disclosure at build time.

### Advertising All Default Tools

To expose all pack-based tools to the model, use `ToolGroups::advertised()`:

```rust
use openhuman_core::openhuman::tools::toolpacks::ToolGroups;
use openhuman_core::embed::harness::Builder as HarnessBuilder;

let harness = HarnessBuilder::new()
    .tool_groups(ToolGroups::advertised())
    .build()
    .await?;

```

### Disabling All Pack-Based Tools

For lightweight deployments where pack tools should not be available, use `ToolGroups::none()`:

```rust
use openhuman_core::openhuman::tools::toolpacks::ToolGroups;

let harness = HarnessBuilder::new()
    .tool_groups(ToolGroups::none())
    .build()
    .await?;

```

### Withholding Specific Packs

To hide specific tool packs from the prompt while preserving runtime access:

```rust
use openhuman_core::openhuman::tools::toolpacks::{GroupMode, ToolGroups};

let mut groups = ToolGroups::advertised();
groups.set_mode("documents", GroupMode::Withheld);
let harness = HarnessBuilder::new()
    .tool_groups(groups)
    .build()
    .await?;

```

### Runtime Dispatch of Withheld Tools

The dispatcher can execute tools even when they were not advertised:

```rust
let registry = ctx.pack_registry_handle();
if let Some(tool) = registry.get("generate_document") {
    tool.run(args).await?;
}

```

## Why Pack-Based Disclosure Matters

The pack-centric approach provides several architectural advantages for production AI systems.

**Security and Cost Control.** Large tool packs for document generation, media processing, or web3 operations can be expensive or present security risks. The disclosure system ensures these tools are only advertised when hosts explicitly opt-in, preventing accidental invocation.

**Prompt Size Management.** LLM prompts have strict token limits. Hiding rarely-used tools through `GroupMode::Withheld` reduces prompt length and associated API costs without removing functionality.

**Feature Flag Integration.** Each tool pack maps to a Cargo feature flag. Setting `GroupMode::Off` guarantees that disabled features do not leak tool schemas into prompts, maintaining clean separation between compiled and exposed functionality.

## Summary

- OpenHuman uses a **three-state disclosure system** (`Advertised`, `Withheld`, `Off`) controlled by the `GroupMode` enum to manage tool visibility
- The **`PackRegistryHandle`** maintains a complete registry of all bound tools, ensuring withheld tools remain callable at runtime
- **`strip_packed_from_visible`** filters non-advertised tools from prompts before LLM transmission
- **`bind_pack_registry`** and **`append_pack_tools`** manage the transition from build-time configuration to runtime execution
- This architecture enables **token-efficient prompts** while preserving access to the full tool registry for advanced use cases

## Frequently Asked Questions

### What is the difference between Withheld and Off tools in OpenHuman?

`Withheld` tools are removed from the LLM prompt but remain registered in the `PackRegistryHandle`, allowing the model to call them if referenced by name from prior context. `Off` tools are completely disabled—they are neither advertised nor callable, effectively removing them from the runtime registry.

### How does OpenHuman prevent withheld tools from appearing in prompts?

The framework invokes `strip_packed_from_visible` in [`src/openhuman/agent/harness/session/turn/tools.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/agent/harness/session/turn/tools.rs) immediately before sending the turn to the language model. This function iterates the tool list and excludes any tools where `GroupMode` is not `Advertised`, ensuring only explicitly disclosed tools render into the prompt's tool catalog.

### Can a language model call a tool that wasn't advertised in the prompt?

Yes. If the model references a withheld tool by name—either learned from system messages or previous turns—the dispatcher consults the `PackRegistryHandle` established during `bind_pack_registry`. Since this registry contains all tools regardless of disclosure state, the system can execute withheld tools on demand.

### Where is the tool pack configuration defined in the OpenHuman codebase?

The primary configuration structures reside in [`src/openhuman/tools/toolpacks/groups.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/tools/toolpacks/groups.rs), which defines `ToolGroups` and the `GroupMode` enum. The core builder API exposing these controls is implemented in [`src/core/runtime/builder.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/core/runtime/builder.rs) at lines 449-510, while the runtime binding logic exists in [`src/openhuman/tools/toolpacks/ops.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/tools/toolpacks/ops.rs).