Tools are the only way agents can interact with the world. All tools:
- Declare their capabilities
- Declare their side effects
- Declare their determinism
- Have resource bounds
- Are logged
pub trait Tool: Send + Sync {
fn id(&self) -> &ToolId;
fn required_capabilities(&self) -> &[Capability];
fn side_effects(&self) -> SideEffect;
fn determinism(&self) -> Determinism;
fn resource_bounds(&self) -> &ResourceBounds;
fn execute(&self, input: &[u8], context: &ExecutionContext) -> ToolResult<Vec<u8>>;
fn input_schema(&self) -> &str;
fn output_schema(&self) -> &str;
}| Type | Description | Example |
|---|---|---|
Pure |
No side effects | Hash computation |
Impure |
Has side effects | File write |
| Type | Description | Handling |
|---|---|---|
Deterministic |
Same input = same output | No special handling |
BoundedNonDeterminism |
Uses injected time/rand | Use seeded context |
NonDeterministic |
External factors | Logged, not replayed |
pub struct ResourceBounds {
pub timeout_ms: u64, // Maximum execution time
pub max_memory_bytes: Option<u64>, // Memory limit
pub max_fuel: Option<u64>, // WASM fuel limit
}1. Capability Check -> Denied? -> Log and Stop
2. Resource Check -> Exceeded? -> Log and Stop
3. Execute Tool
4. Normalize Response
5. Log Event (with hashes)
6. Return Response
Deterministic, no side effects:
impl Tool for EchoTool {
fn id(&self) -> &ToolId {
&ToolId::new("echo", "1.0.0")
}
fn required_capabilities(&self) -> &[Capability] {
&[]
}
fn side_effects(&self) -> SideEffect {
SideEffect::Pure
}
fn determinism(&self) -> Determinism {
Determinism::Deterministic
}
fn execute(&self, input: &[u8], _context: &ExecutionContext) -> ToolResult<Vec<u8>> {
Ok(input.to_vec())
}
}Deterministic BLAKE3 hashing:
impl Tool for HashTool {
fn execute(&self, input: &[u8], _context: &ExecutionContext) -> ToolResult<Vec<u8>> {
let hash = blake3::hash(input);
Ok(hash.to_hex().into_bytes())
}
}All tool responses are normalized:
pub struct ToolResponse<T> {
pub data: T,
pub response_hash: Hash, // Of normalized data
pub metadata: ToolResponseMetadata,
}Normalization ensures:
- Stable ordering for collections
- Canonical JSON representation
- Reproducible hashing
Tool errors are logged:
Event {
kind: EventKind::ToolResponse,
payload: ToolResponsePayload {
success: false,
error: Some("Timeout".to_string()),
// ...
},
}- Declare determinism honestly - Affects replay
- Use bounded resources - Prevent runaway execution
- Return structured errors - Enables better debugging
- Normalize outputs - Ensures reproducibility
- Document capabilities - Clear security requirements