Why tinyagents Vendors tinytools: Type Safety and Cross-Crate Compatibility in OpenHuman

The tinyagents crate vendors a local copy of tinytools to guarantee that both the core OpenHuman library and the agent harness share identical type definitions, preventing Rust compiler errors caused by trait implementation mismatches across crate boundaries.

The tinyhumansai/openhuman repository employs a specific dependency strategy where the tinyagents module contains a vendored checkout of the tinytools crate rather than declaring it as a conventional crates.io dependency. This architectural decision ensures that critical tool-related types—specifically the Tool trait, ToolResult, ToolContent, and ToolRunContext—maintain strict type identity across the entire codebase.

The Core Problem: Type Identity Across Crate Boundaries

In Rust, the orphan rule prevents implementing a foreign trait for a foreign type. When openhuman (the core library) and tinyagents (the agent harness) both need to work with tool definitions, they must reference the exact same type instances. If each crate pulled tinytools independently from crates.io, Cargo could resolve them to different versions or distinct compiled instances.

This creates a type mismatch where tinytools::Tool from one crate is treated as a completely different type from tinytools::Tool in the other. Consequently, trait implementations made by tinyagents would not satisfy the interface expectations of openhuman, causing compilation failures when attempting to register tools or execute them through the harness.

Why Vendoring Solves the Diamond Dependency Problem

Vendoring tinytools inside tinyagents eliminates the risk of version skew and duplicate type definitions. The repository places the vendored copy at vendor/tinyagents/vendor/tinytools/, ensuring both crates reference the same source tree.

Preventing Duplicate Crate Instances

If tinyagents declared a regular dependency on tinytools while openhuman also depended on it, Cargo's resolution algorithm might select different versions for each consumer. Because Rust treats different crate instances as distinct types—even when they originate from the same source—a tool implementing tinytools::Tool from one instance would not satisfy the trait bound expecting tinytools::Tool from the other. Vendoring forces both crates to use the single compiled instance located in the vendor/ directory.

Guaranteed Version Alignment

The vendored checkout pins tinytools to a specific commit that is reviewed and synchronized with the core library's expectations. This guarantees that every build uses the same source tree for tool definitions. When openhuman updates its tool interfaces, the vendored copy in vendor/tinyagents/vendor/tinytools/ is updated simultaneously, eliminating subtle runtime mismatches caused by stray version bumps.

Simplified Auditing and CI

The repository maintains a dedicated CI script at scripts/ci/check-vendored.mjs that verifies the vendored copy matches the upstream source. This enforcement ensures that any changes to tinytools must be deliberately reviewed and synchronized across both crates. The explicit path dependency in vendor/tinyagents/Cargo.toml makes the relationship transparent to auditors and prevents accidental drift.

Build Isolation Without External Network Access

By including the source code directly in the repository, the build process does not require network access to crates.io to fetch tinytools. This aligns with the project's security posture where all third-party code is audited and locked in the repository, enabling reproducible builds in air-gapped environments.

Implementation Details and Source Evidence

The architectural decision is documented directly in the source code. In src/openhuman/tools/traits.rs, the module comments explicitly state that definitions live in tinytools and that the re-export is required so that dyn tinytools::Tool represents the same type across the entire codebase.

The Cargo.toml manifest at vendor/tinyagents/Cargo.toml declares the dependency using a path reference rather than a version constraint:

[dependencies]
tinytools = { path = "vendor/tinytools" }

This configuration instructs Cargo to compile the vendored source as part of the tinyagents build graph, ensuring that when openhuman imports tool types through src/openhuman/tools/traits.rs, it references the identical types used by the agent harness.

Practical Code Examples

The following examples demonstrate how vendoring enables seamless interoperability between the core library and the agent harness.

Using Tools Across Crate Boundaries

Because both crates import from the same vendored source, trait objects remain compatible:

use openhuman::tools::Tool;           // Re-export from vendored tinytools
use tinyagents::tools::ToolRunContext;

fn execute_tool(context: &dyn ToolRunContext) -> tinytools::ToolResult {
    // ToolResult from tinyagents matches the core's expectations
    let tool = MyCustomTool::new();
    tool.run(context)
}

Implementing a Tool for Both Systems

When implementing the Tool trait, the single type definition satisfies both openhuman and tinyagents:

use tinytools::{Tool, ToolRunContext, ToolResult};

struct DataProcessor;

impl Tool for DataProcessor {
    fn name(&self) -> &'static str { "data_processor" }
    
    fn run(&self, ctx: &dyn ToolRunContext) -> ToolResult {
        // Implementation uses the unified ToolRunContext type
        ToolResult::success(ctx.input())
    }
}

// Registration works because openhuman::tools::Tool is the same type
openhuman::tools::registry::register(Box::new(DataProcessor));

The key file src/openhuman/agent/tinyagents/tools.rs contains the actual integration code that relies on these unified type definitions to bridge the core runtime with the agent harness.

Summary

  • Vendoring prevents type mismatches by ensuring tinytools::Tool and related types have a single definition shared between openhuman and tinyagents.
  • Path dependencies in vendor/tinyagents/Cargo.toml force compilation from the vendored source rather than fetching potentially divergent versions from crates.io.
  • The orphan rule in Rust makes this necessary—without identical types, tinyagents could not implement traits that openhuman expects to use.
  • CI enforcement via scripts/ci/check-vendored.mjs ensures the vendored copy stays synchronized with upstream changes.
  • Build reproducibility improves because the repository contains all necessary source code, eliminating network dependencies during compilation.

Frequently Asked Questions

Why can't tinyagents just use a normal Cargo dependency on tinytools?

If tinyagents used a normal crates.io dependency while openhuman also depended on tinytools, Cargo could resolve the two usages to different versions or different compiled instances. Because Rust treats these as distinct types, tinyagents would be unable to implement the Tool trait that openhuman expects, violating Rust's orphan rules and causing compilation errors.

Where is the vendored tinytools code located in the repository?

The vendored checkout resides at vendor/tinyagents/vendor/tinytools/. The vendor/tinyagents/Cargo.toml file contains a path dependency pointing to this internal directory, ensuring both the core library and the agent harness reference identical source files.

How does the project ensure the vendored copy stays up to date?

The repository includes a CI script at scripts/ci/check-vendored.mjs that verifies the vendored copy matches the upstream tinytools source. This ensures any updates to the tool interfaces are deliberately synchronized across both crates, preventing accidental version drift.

Does vendoring affect how I implement custom tools for the OpenHuman runtime?

No, vendoring simplifies implementation. You import tool types from tinytools (or through openhuman::tools re-exports) knowing that the types match exactly what the tinyagents harness expects. This guarantees that when you register a tool with the core registry, the agent runner can invoke it without type compatibility issues.

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 →