Skip to main content

AgentHooks

Trait AgentHooks 

Source
pub trait AgentHooks: Send + Sync {
    // Provided methods
    fn pre_tool_use<'life0, 'life1, 'async_trait>(
        &'life0 self,
        invocation: &'life1 ToolInvocation,
    ) -> Pin<Box<dyn Future<Output = ToolDecision> + Send + 'async_trait>>
       where 'life0: 'async_trait,
             'life1: 'async_trait,
             Self: 'async_trait { ... }
    fn post_tool_use<'life0, 'life1, 'life2, 'async_trait>(
        &'life0 self,
        _tool_name: &'life1 str,
        _result: &'life2 ToolResult,
    ) -> Pin<Box<dyn Future<Output = ()> + Send + 'async_trait>>
       where 'life0: 'async_trait,
             'life1: 'async_trait,
             'life2: 'async_trait,
             Self: 'async_trait { ... }
    fn on_event<'life0, 'life1, 'async_trait>(
        &'life0 self,
        _event: &'life1 AgentEvent,
    ) -> Pin<Box<dyn Future<Output = ()> + Send + 'async_trait>>
       where 'life0: 'async_trait,
             'life1: 'async_trait,
             Self: 'async_trait { ... }
    fn on_error<'life0, 'life1, 'async_trait>(
        &'life0 self,
        _error: &'life1 Error,
    ) -> Pin<Box<dyn Future<Output = bool> + Send + 'async_trait>>
       where 'life0: 'async_trait,
             'life1: 'async_trait,
             Self: 'async_trait { ... }
    fn on_context_compact<'life0, 'life1, 'async_trait>(
        &'life0 self,
        _messages: &'life1 [Message],
    ) -> Pin<Box<dyn Future<Output = Option<String>> + Send + 'async_trait>>
       where 'life0: 'async_trait,
             'life1: 'async_trait,
             Self: 'async_trait { ... }
    fn pre_llm_request<'life0, 'life1, 'async_trait>(
        &'life0 self,
        _request: &'life1 ChatRequest,
    ) -> Pin<Box<dyn Future<Output = RequestDecision> + Send + 'async_trait>>
       where 'life0: 'async_trait,
             'life1: 'async_trait,
             Self: 'async_trait { ... }
    fn on_llm_response<'life0, 'life1, 'async_trait>(
        &'life0 self,
        _response: &'life1 ChatResponse,
    ) -> Pin<Box<dyn Future<Output = ResponseDecision> + Send + 'async_trait>>
       where 'life0: 'async_trait,
             'life1: 'async_trait,
             Self: 'async_trait { ... }
}
Expand description

Lifecycle hooks for the agent loop. Implement this trait to customize agent behavior.

Provided Methods§

Source

fn pre_tool_use<'life0, 'life1, 'async_trait>( &'life0 self, invocation: &'life1 ToolInvocation, ) -> Pin<Box<dyn Future<Output = ToolDecision> + Send + 'async_trait>>
where 'life0: 'async_trait, 'life1: 'async_trait, Self: 'async_trait,

Called before a tool is executed.

Receives a structured ToolInvocation that bundles tool identity, tier, requested input, effective input, and listen-context — everything a server-side policy engine needs for an allow / block / confirm decision.

Return ToolDecision::Allow to proceed, ToolDecision::Block to reject, or ToolDecision::RequiresConfirmation to yield for user approval.

Source

fn post_tool_use<'life0, 'life1, 'life2, 'async_trait>( &'life0 self, _tool_name: &'life1 str, _result: &'life2 ToolResult, ) -> Pin<Box<dyn Future<Output = ()> + Send + 'async_trait>>
where 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait, Self: 'async_trait,

Called after a tool completes execution.

Source

fn on_event<'life0, 'life1, 'async_trait>( &'life0 self, _event: &'life1 AgentEvent, ) -> Pin<Box<dyn Future<Output = ()> + Send + 'async_trait>>
where 'life0: 'async_trait, 'life1: 'async_trait, Self: 'async_trait,

Called when the agent emits an event. Can be used for logging, metrics, or custom handling.

Source

fn on_error<'life0, 'life1, 'async_trait>( &'life0 self, _error: &'life1 Error, ) -> Pin<Box<dyn Future<Output = bool> + Send + 'async_trait>>
where 'life0: 'async_trait, 'life1: 'async_trait, Self: 'async_trait,

Called when an error occurs. Return true to attempt recovery, false to abort.

Source

fn on_context_compact<'life0, 'life1, 'async_trait>( &'life0 self, _messages: &'life1 [Message], ) -> Pin<Box<dyn Future<Output = Option<String>> + Send + 'async_trait>>
where 'life0: 'async_trait, 'life1: 'async_trait, Self: 'async_trait,

Called when context is about to be compacted due to length. Return a summary to use, or None to use default summarization.

Source

fn pre_llm_request<'life0, 'life1, 'async_trait>( &'life0 self, _request: &'life1 ChatRequest, ) -> Pin<Box<dyn Future<Output = RequestDecision> + Send + 'async_trait>>
where 'life0: 'async_trait, 'life1: 'async_trait, Self: 'async_trait,

Input guardrail: called with the outbound llm::ChatRequest before it is sent to the provider.

Return RequestDecision::Proceed to send it unchanged, RequestDecision::Modify to substitute a sanitized request, or RequestDecision::Block to refuse the call (e.g. prompt-injection or PII policy). The default proceeds unchanged.

Fresh-input timing: for a run’s first call on fresh Text / Message input the hook runs at ingestion time, against the request built from existing history plus the candidate message — BEFORE the message is durably appended, so a Block leaves nothing in history. The request actually sent may then differ only by SDK-added content: context compaction (a summary of the exact content the hook already saw — Block decisions err conservative) or a turn-budget reminder. A Modify on that first call is sent as-is (bypassing compaction once; an oversized replacement degrades to the normal overflow recovery). The hook fires exactly once per LLM call in every mode.

Source

fn on_llm_response<'life0, 'life1, 'async_trait>( &'life0 self, _response: &'life1 ChatResponse, ) -> Pin<Box<dyn Future<Output = ResponseDecision> + Send + 'async_trait>>
where 'life0: 'async_trait, 'life1: 'async_trait, Self: 'async_trait,

Output guardrail: called with the provider’s llm::ChatResponse before it is persisted or surfaced.

Return ResponseDecision::Accept to keep it, ResponseDecision::Block to reject it, or ResponseDecision::RetryWithFeedback to reject it and steer a retry (e.g. output moderation or secret-leakage detection). The default accepts.

Retry cap: every RetryWithFeedback pays for another LLM round-trip, so the loop bounds consecutive rejections at 8. When the cap is reached the run terminates with an error naming this hook instead of retrying (and billing) indefinitely. The rejection streak is persisted in the thread’s AgentState, so the cap binds both in-process looping runs and host-driven single-turn orchestration (repeated run_turn calls rehydrate the streak from the state store). The counter resets whenever a response is accepted.

Streaming caveat: the hook runs on the complete response, after the model has finished. In streaming mode the text deltas have already been emitted (and recorded) as stream events by then, so a Block / RetryWithFeedback decision keeps the response out of the thread’s message history and out of the model’s context, but a live consumer may have rendered the deltas. Disable streaming when blocked content must never be surfaced.

Dyn Compatibility§

This trait is dyn compatible.

In older versions of Rust, dyn compatibility was called "object safety".

Implementors§