Built-in Tools
14 tools are available by default when running without a config file (env-based mode). With a config file, built-in tools are added alongside any MCP tools defined in mcp_servers.
Tool Reference
Section titled “Tool Reference”| Tool | Description |
|---|---|
bash | Execute bash commands. Working directory persists between calls. Default timeout: 120s, max: 600s. |
read | Read a file with line numbers. Detects binary files. Max size: 256 KB. |
write | Write content to a file. Creates parent directories. Read-before-write guard. |
edit | Replace an exact string in a file (must appear exactly once). Read-before-write guard. |
patch | Apply unified diff patches to one or more files. Single-pass hunk application. |
glob | Find files matching a glob pattern. Skips hidden files. |
grep | Search file contents with regex. Uses rg when available, falls back to built-in. |
list | List directory contents as an indented tree. Skips common build artifacts. |
webfetch | Fetch content from a URL via HTTP GET. Supports text, markdown, HTML. Max: 5 MB. |
websearch | Search the web via Exa AI. Requires EXA_API_KEY. |
todowrite | Write/replace the full todo list. Only 1 item in progress at a time. |
todoread | Read the current todo list. |
skill | Load skill definitions from SKILL.md files. |
question | Ask the user structured questions (only available when on_question callback is set). |
Cross-Agent Coordination
Section titled “Cross-Agent Coordination”Blackboard
Section titled “Blackboard”The Blackboard is a shared Key -> Value store for squad agents (those dispatched via form_squad). Sub-agents receive three additional tools:
| Tool | Description |
|---|---|
blackboard_read | Read a value by key from the shared blackboard |
blackboard_write | Write a key-value pair to the shared blackboard |
blackboard_list | List all keys currently in the blackboard |
After each sub-agent completes, its result is automatically written to the blackboard under the key "agent:{name}".
Delegation Tools
Section titled “Delegation Tools”The orchestrator agent has two tools for dispatching work to sub-agents:
| Tool | Description |
|---|---|
delegate_task | Dispatch independent parallel subtasks to sub-agents |
form_squad | Dispatch collaborative subtasks that share a Blackboard |
Structured Output
Section titled “Structured Output”Set response_schema in config (or .structured_schema() on the builder) to constrain an agent’s output format. When configured:
- A synthetic
__respond__tool is injected into the agent’s tool set - The agent calls
__respond__to produce structured JSON - The result is available in
AgentOutput::structured
This works in both standalone and Restate execution paths.
Human-in-the-Loop (HITL)
Section titled “Human-in-the-Loop (HITL)”The --approve flag enables interactive approval before each tool execution round:
- The CLI prompts the user before tool calls are executed
- Denied tools receive error results so the LLM can adjust and retry
- In the Restate path, approval uses per-turn promise keys for durable waiting
Streaming
Section titled “Streaming”The on_text callback receives text deltas as they arrive from the LLM. Both Anthropic and OpenRouter providers implement SSE streaming. Sub-agents do not stream — only the orchestrator or top-level agent streams text.
Agent Events
Section titled “Agent Events”Structured AgentEvent variants are emitted via the OnEvent callback:
| Event | Description |
|---|---|
RunStarted | Agent run has begun |
TurnStarted | A new reasoning turn started |
LlmResponse | LLM returned a response |
ToolCallStarted | A tool call is about to execute |
ToolCallCompleted | A tool call finished |
ApprovalRequested | Waiting for HITL approval |
ApprovalDecision | HITL approval/denial received |
SubAgentsDispatched | Orchestrator dispatched sub-agents |
SubAgentCompleted | A sub-agent finished its task |
ContextSummarized | Context was compacted via summarization |
RunCompleted | Agent run completed successfully |
GuardrailDenied | A guardrail blocked the action |
GuardrailWarned | A guardrail issued a warning |
RunFailed | Agent run failed with an error |
RetryAttempt | Provider retrying after transient error |
DoomLoopDetected | Identical tool calls detected across turns |
SessionPruned | Old tool results were pruned |
AutoCompactionTriggered | Context overflow triggered auto-compaction |
ModelEscalated | Cascade provider escalated to a higher tier |
BudgetExceeded | Token budget exhausted |
Use --verbose / -v to emit events as JSON to stderr.
Cost Tracking
Section titled “Cost Tracking”estimate_cost(model, usage) -> Option<f64> returns estimated USD cost for known models (Claude 4, 3.5, and 3 generations, including OpenRouter aliases). The estimate accounts for cache read/write token rates. Cost is displayed in CLI output after each run.
Custom Tools
Section titled “Custom Tools”Implement the Tool trait and register with an agent:
use heartbit::{Tool, ToolDefinition, ToolOutput, Error};use serde_json::Value;use std::pin::Pin;use std::future::Future;
pub struct MyTool;
impl Tool for MyTool { fn definition(&self) -> ToolDefinition { ToolDefinition { name: "my_tool".into(), description: "Does something useful".into(), input_schema: serde_json::json!({ "type": "object", "properties": { "input": { "type": "string", "description": "The input value" } }, "required": ["input"] }), } }
fn execute( &self, input: Value, ) -> Pin<Box<dyn Future<Output = Result<ToolOutput, Error>> + Send + '_>> { Box::pin(async move { let input_str = input["input"].as_str().unwrap_or_default(); Ok(ToolOutput::success(format!("Processed: {input_str}"))) }) }}
// Register with an agent:// AgentRunner::builder(provider).tools(vec![Arc::new(MyTool)]).build()