Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion crates/tinytools/src/classification/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

mod types;

pub use types::{ToolCategory, ToolScope};
pub use types::{ToolCategory, ToolExposure, ToolScope};

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

priority medium security confident

Document the new public type ToolExposure

ToolExposure is now publicly exported from the module but does not yet appear to have a rustdoc comment on its definition (the diff only changes the re-export line). The repository rules require every public item to get a rustdoc comment with missing_docs treated as an error. Add /// documentation explaining what ToolExposure represents, how it is used (including its relation to ToolCategory and ToolScope), and any wire-format or default behaviour.

[RULE] missing-docs ·


#[cfg(test)]
mod test;
56 changes: 56 additions & 0 deletions crates/tinytools/src/classification/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,62 @@ pub enum ToolScope {
CliRpcOnly,
}

/// Where a tool is exposed to the model.
///
/// Every tool a host registers is dispatchable. This says which of them the
/// model is *told about* up front, and it is a property of the tool rather than
/// of a config posture, because the answer rarely varies by deployment: a tool
/// the model needs on most turns is direct, and one it needs on a handful of
/// turns a week is not, whoever is running the host.
///
/// The distinction exists because tool schemas are a fixed per-turn cost paid
/// on every request, and on a large tool surface they dominate it — measured on
/// OpenHuman's orchestrator, 45 KB of schema against 34 KB of system prompt.
/// A schema the model reads on one turn in five hundred is not worth its place
/// on the other four hundred and ninety-nine.
///
/// Modelled on Codex's `ToolExposure` (`codex-rs/tools/src/tool_executor.rs`),
/// which pairs `Deferred` with a BM25-indexed `tool_search`. This enum is
/// deliberately the smaller half of that design: Codex additionally
/// distinguishes its Code Mode surface, which has no equivalent here yet.
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum ToolExposure {
Comment on lines +41 to +43

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Add regression tests for ToolExposure

No test references ToolExposure, leaving its Direct default, predicates, and the new "direct"/"deferred"/"hidden" wire values unprotected against accidental changes. In particular, the repository requires every serializable payload type to pin its serde representation because mismatches otherwise surface as runtime decode failures.

AGENTS.md reference: AGENTS.md:L170-L172

Useful? React with 👍 / 👎.

/// Advertise the tool's schema on every request.
///
/// The default, and deliberately so: a tool that has thought about its own
/// exposure will say so, and one that has not should keep behaving exactly
/// as it did before this existed.
#[default]
Direct,
/// Register the tool and keep its schema off the wire, reachable through
/// the host's tool-search facility.
///
/// A host that offers no such facility must treat this as [`Self::Direct`]
/// rather than hiding the tool — a capability the model cannot see *and*
/// cannot look up is simply gone, which is a bigger regression than the
/// tokens it saves.
Deferred,
Comment on lines +51 to +58

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Specify the exposure contract before shipping it

This introduces host-facing behavior and an important constraint that hosts without search must reinterpret Deferred as Direct, but no document under docs/specs/ defines that accepted contract and no linked implementation plan exists. Add the specification and plan so downstream host implementations have a stable behavioral source rather than relying only on enum rustdoc.

AGENTS.md reference: AGENTS.md:L206-L208

Useful? React with 👍 / 👎.

/// Keep the tool dispatchable but never show it to the model.
///
/// For tools a host calls on the model's behalf, or that exist only to be
/// invoked by another tool.
Hidden,
}

impl ToolExposure {
/// Whether this tool's schema belongs in the initial tool list.
pub fn is_direct(self) -> bool {
matches!(self, Self::Direct)
}

/// Whether tool search may surface this tool.
pub fn is_searchable(self) -> bool {
matches!(self, Self::Deferred)
}
}


/// Category of a tool — used to scope which tools a given sub-agent may see.
///
/// The distinction is about *where the work happens*: a [`Self::System`] tool
Expand Down
4 changes: 2 additions & 2 deletions crates/tinytools/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
//! a tool hands back.
//! - [`spec`] — [`ToolSpec`], the declaration a model is shown.
//! - [`permission`] — [`PermissionLevel`], the privilege ladder.
//! - [`classification`] — [`ToolScope`] and [`ToolCategory`].
//! - [`classification`] — [`ToolScope`], [`ToolCategory`] and [`ToolExposure`].
//! - [`call`] — [`ToolCallOptions`] and [`ToolTimeout`], the per-invocation
//! inputs that are not arguments.
//! - [`context`] — [`ToolRunContext`], the narrow seam onto a live run.
Expand Down Expand Up @@ -107,7 +107,7 @@ pub mod tool;
pub mod workspace;

pub use call::{ToolCallOptions, ToolTimeout};
pub use classification::{ToolCategory, ToolScope};
pub use classification::{ToolCategory, ToolExposure, ToolScope};

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Update the package README for the new public type

This makes ToolExposure part of the crate's public surface, but the package README's “What is here” table still describes classification as containing only ToolScope and ToolCategory. Users reading the published crate README therefore receive an incomplete account of the API; update it in this commit as required for behavior and documentation changes.

AGENTS.md reference: AGENTS.md:L204-L205

Useful? React with 👍 / 👎.

pub use context::ToolRunContext;
pub use naming::{
ContextDetailOptions, context_detail_from_args, context_detail_from_args_with,
Expand Down
13 changes: 12 additions & 1 deletion crates/tinytools/src/tool/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use async_trait::async_trait;
use serde_json::Value;

use crate::call::{ToolCallOptions, ToolTimeout};
use crate::classification::{ToolCategory, ToolScope};
use crate::classification::{ToolCategory, ToolExposure, ToolScope};
use crate::context::ToolRunContext;
use crate::naming::{context_detail_from_args, humanize_tool_name};
use crate::permission::PermissionLevel;
Expand Down Expand Up @@ -123,6 +123,17 @@ pub trait Tool: Send + Sync {
ToolCategory::System
}

/// Where this tool is exposed to the model.
///
/// Defaults to [`ToolExposure::Direct`] so a tool that has not considered
/// the question behaves exactly as it did before this method existed.
/// Override it on a tool whose schema is large relative to how often the
/// model reaches for it — that is the trade this is here to make, and the
/// host's own budget report is the place to find the candidates.
fn exposure(&self) -> ToolExposure {
ToolExposure::Direct
}

/// Whether two concurrent invocations are safe to run in parallel within a
/// single model turn.
///
Expand Down
Loading