pub trait ToolGate: Plugin {
// Required method
fn next_turn_tool_allowlist<'life0, 'life1, 'async_trait>(
&'life0 self,
ctx: ToolGateContext<'life1>,
) -> Pin<Box<dyn Future<Output = Option<HashSet<String>>> + Send + 'async_trait>>
where Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait;
// Provided methods
fn denial_reason<'life0, 'life1, 'life2, 'async_trait>(
&'life0 self,
_tool_name: &'life1 str,
_ctx: ToolGateContext<'life2>,
) -> Pin<Box<dyn Future<Output = Option<String>> + Send + 'async_trait>>
where Self: Sync + 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait { ... }
fn conflict_priority(&self) -> i32 { ... }
fn tool_gate_class(&self) -> ToolGateClass { ... }
fn suppresses_advisory_gates(&self, _ctx: ToolGateContext<'_>) -> bool { ... }
}Expand description
Per-turn allowlist of tool names the model may invoke.
Returning Some(set) means: for the very next LLM call, narrow
the advertised tools to those whose names appear in set. Every
other tool the agent has access to is omitted from that one
request. None means no narrowing — the loop sends all tools.
Composition across multiple gates: the loop intersects every
Some allowlist; absent (None) gates do not constrain. If multiple
non-empty gate allowlists conflict to the empty set, the loop repairs
the composition by choosing the highest-priority gate and emits a
typed conflict event. Gates that own urgent recovery states should
override ToolGate::conflict_priority.
Single-shot semantics emerge from the trigger condition, not from
internal mutability: a gate that fires only on iteration == 0
is naturally single-shot per run. Conversation-scoped gates should
keep their cross-run state in an external store, not in the plugin
instance.
Required Methods§
fn next_turn_tool_allowlist<'life0, 'life1, 'async_trait>(
&'life0 self,
ctx: ToolGateContext<'life1>,
) -> Pin<Box<dyn Future<Output = Option<HashSet<String>>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
Provided Methods§
Sourcefn denial_reason<'life0, 'life1, 'life2, 'async_trait>(
&'life0 self,
_tool_name: &'life1 str,
_ctx: ToolGateContext<'life2>,
) -> Pin<Box<dyn Future<Output = Option<String>> + Send + 'async_trait>>where
Self: Sync + 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
fn denial_reason<'life0, 'life1, 'life2, 'async_trait>(
&'life0 self,
_tool_name: &'life1 str,
_ctx: ToolGateContext<'life2>,
) -> Pin<Box<dyn Future<Output = Option<String>> + Send + 'async_trait>>where
Self: Sync + 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
This gate’s specific reason for denying tool_name in the given
context. The runtime queries every gate after a hidden-tool call
so the error message names the actual narrower instead of guessing
from the intersected allowlist’s shape — that guess sent the model
to repair the wrong gate (e.g. a delivery_repair_gate strip read
as a capability_gate phase mismatch and triggered futile
plan-updates until wall-clock timeout).
Default: None — the runtime falls back to its shape-based
heuristic. Return Some(reason) only when this gate is actively
narrowing in a way that excludes tool_name in this context.
fn conflict_priority(&self) -> i32
fn tool_gate_class(&self) -> ToolGateClass
fn suppresses_advisory_gates(&self, _ctx: ToolGateContext<'_>) -> bool
Dyn Compatibility§
This trait is dyn compatible.
In older versions of Rust, dyn compatibility was called "object safety".