Skip to main content

ToolExecutor

Trait ToolExecutor 

Source
pub trait ToolExecutor: Send + Sync {
    // Required method
    fn execute(
        &self,
        response: &str,
    ) -> impl Future<Output = Result<Option<ToolOutput>, ToolError>> + Send;

    // Provided methods
    fn execute_confirmed(
        &self,
        response: &str,
    ) -> impl Future<Output = Result<Option<ToolOutput>, ToolError>> + Send { ... }
    fn tool_definitions(&self) -> Vec<ToolDef> { ... }
    fn execute_tool_call(
        &self,
        _call: &ToolCall,
    ) -> impl Future<Output = Result<Option<ToolOutput>, ToolError>> + Send { ... }
    fn execute_tool_call_confirmed(
        &self,
        call: &ToolCall,
    ) -> impl Future<Output = Result<Option<ToolOutput>, ToolError>> + Send { ... }
    fn set_skill_env(&self, _env: Option<HashMap<String, String>>) { ... }
    fn set_effective_trust(&self, _level: SkillTrustLevel) { ... }
    fn is_tool_retryable(&self, _tool_id: &str) -> bool { ... }
}
Expand description

Async trait for tool execution backends.

Implementations include ShellExecutor, WebScrapeExecutor, CompositeExecutor, and FileExecutor.

§Contract

  • execute and execute_tool_call return Ok(None) when the executor does not handle the given input — callers must not treat None as an error.
  • All methods must be Send + Sync and free of blocking I/O.
  • Implementations must enforce their own security controls (blocklists, sandboxes, SSRF protection) before executing any side-effectful operation.
  • execute_confirmed and execute_tool_call_confirmed bypass confirmation gates only — all other security controls remain active.

§Two Invocation Paths

Legacy fenced blocks: The agent loop passes the raw LLM response string to execute. The executor parses ```bash or ```scrape blocks and executes each one.

Structured tool calls: The agent loop constructs a ToolCall from the LLM’s JSON tool-use response and dispatches it via execute_tool_call. This is the preferred path for new code.

§Example

use zeph_tools::{ToolExecutor, ToolCall, ToolOutput, ToolError, executor::ClaimSource};

#[derive(Debug)]
struct EchoExecutor;

impl ToolExecutor for EchoExecutor {
    async fn execute(&self, _response: &str) -> Result<Option<ToolOutput>, ToolError> {
        Ok(None) // not a fenced-block executor
    }

    async fn execute_tool_call(&self, call: &ToolCall) -> Result<Option<ToolOutput>, ToolError> {
        if call.tool_id != "echo" {
            return Ok(None);
        }
        let text = call.params.get("text")
            .and_then(|v| v.as_str())
            .unwrap_or("")
            .to_owned();
        Ok(Some(ToolOutput {
            tool_name: "echo".into(),
            summary: text,
            blocks_executed: 1,
            filter_stats: None,
            diff: None,
            streamed: false,
            terminal_id: None,
            locations: None,
            raw_response: None,
            claim_source: None,
        }))
    }
}

Required Methods§

Source

fn execute( &self, response: &str, ) -> impl Future<Output = Result<Option<ToolOutput>, ToolError>> + Send

Parse response for fenced tool blocks and execute them.

Returns Ok(None) when no tool blocks are found in response.

§Errors

Returns ToolError when a block is found but execution fails (blocked command, sandbox violation, network error, timeout, etc.).

Provided Methods§

Source

fn execute_confirmed( &self, response: &str, ) -> impl Future<Output = Result<Option<ToolOutput>, ToolError>> + Send

Execute bypassing confirmation checks (called after user approves).

Security controls other than the confirmation gate remain active. Default implementation delegates to execute.

§Errors

Returns ToolError on execution failure.

Source

fn tool_definitions(&self) -> Vec<ToolDef>

Return the tool definitions this executor can handle.

Used to populate the LLM’s tool schema at context-assembly time. Returns an empty Vec by default (for executors that only handle fenced blocks).

Source

fn execute_tool_call( &self, _call: &ToolCall, ) -> impl Future<Output = Result<Option<ToolOutput>, ToolError>> + Send

Execute a structured tool call. Returns Ok(None) if call.tool_id is not handled.

§Errors

Returns ToolError when the tool ID is handled but execution fails.

Source

fn execute_tool_call_confirmed( &self, call: &ToolCall, ) -> impl Future<Output = Result<Option<ToolOutput>, ToolError>> + Send

Execute a structured tool call bypassing confirmation checks.

Called after the user has explicitly approved the tool invocation. Default implementation delegates to execute_tool_call.

§Errors

Returns ToolError on execution failure.

Source

fn set_skill_env(&self, _env: Option<HashMap<String, String>>)

Inject environment variables for the currently active skill. No-op by default.

Called by the agent loop before each turn when the active skill specifies env vars. Implementations that ignore this (e.g. WebScrapeExecutor) may leave the default.

Source

fn set_effective_trust(&self, _level: SkillTrustLevel)

Set the effective trust level for the currently active skill. No-op by default.

Trust level affects which operations are permitted (e.g. network access, file writes).

Source

fn is_tool_retryable(&self, _tool_id: &str) -> bool

Whether the executor can safely retry this tool call on a transient error.

Only idempotent operations (e.g. read-only HTTP GET) should return true. Shell commands and other non-idempotent operations must keep the default false to prevent double-execution of side-effectful commands.

Dyn Compatibility§

This trait is not dyn compatible.

In older versions of Rust, dyn compatibility was called "object safety", so this trait is not object safe.

Implementors§