Tools
Tool Behaviour
Section titled “Tool Behaviour”Every tool implements Annihilation.Agent.Tool:
@callback name() :: String.t()@callback description() :: String.t()@callback parameters_schema() :: map() # JSON Schema@callback execute(args :: map(), context :: map()) :: {:ok, String.t() | map()} | {:error, String.t()}The context map passed to execute/2 contains:
:agent_id— the psychonaut’s unique identifier:bead_id— current bead identifier:burst_id— current burst identifier:project_root— absolute path to the project root:tool_call_id— ID for this tool invocation (for streaming updates)
Tool Registry
Section titled “Tool Registry”Agent.Tool.Registry is an ETS-based GenServer that maps tool names to modules. Built-in tools are auto-registered on startup. The registry supports:
# Look up a tool by name from allowed tools listToolRegistry.lookup("shell", agent_tools)
# Get tool definitions for provider APIToolRegistry.definitions(agent_tools)# -> [%{name: "shell", description: "...", parameters: %{...}}, ...]Validation-as-Feedback
Section titled “Validation-as-Feedback”Tool errors are never fatal. They become %ToolResult{is_error: true} and are sent back to the LLM, which self-corrects:
- Parameter validation failure -> error result with schema info
- Tool crash -> rescued -> error result with exception message
- Tool not found -> error result listing available tools
- Invalid JSON arguments -> error result with raw arguments string
Built-in Tools
Section titled “Built-in Tools”File Operations
Section titled “File Operations”| Tool | Risk | Description |
|---|---|---|
file_read | safe | Read file contents. Returns the full file content as a string. |
file_write | moderate | Write/create files. Integrates with LeaseManager for cooperative locking. Acquires exclusive lease before writing, releases after. |
peek_file | safe | Quick file preview. Returns first/last N lines without reading the entire file. |
peek_dir | safe | Directory listing with file sizes and modification times. |
Shell Execution
Section titled “Shell Execution”| Tool | Risk | Description |
|---|---|---|
shell | dangerous | Execute shell commands. Three-tier security via CommandGuard + TraumaGuard. |
Shell command classification:
CommandGuard.classify("ls -la") # -> :safeCommandGuard.classify("git reset --hard") # -> :cautionCommandGuard.classify("rm -rf /") # -> :danger
# Trauma escalation (experience-based)TraumaGuard.classify_with_trauma("npm install", trauma_records)# -> {:danger, "Matches past incident: npm install corrupted node_modules"}Danger patterns (blocked until Tether approval): rm -rf /, DROP DATABASE, git push --force, mkfs., dd if=, chmod -R 777, curl|bash
Caution patterns (logged, Tether notified): git reset --hard, rm -r, DROP, DELETE FROM, sudo, npm publish
Tether Interaction
Section titled “Tether Interaction”| Tool | Risk | Description |
|---|---|---|
ask_user | safe | Reach for the Tether with a question. Blocks until answered or timed out. |
Recursion
Section titled “Recursion”| Tool | Risk | Description |
|---|---|---|
recurse | moderate | Spawn a single recursive sub-agent. Parent blocks until child completes. |
recurse_fan_out | moderate | Concurrent map-style decomposition. Optional reduce step for synthesis. |
# Single recursive call%{"query" => "Analyze the authentication module", "context" => "..."}
# Fan-out with reduce%{ "tasks" => [ %{"task" => "Analyze module A", "context" => "..."}, %{"task" => "Analyze module B", "context" => "..."} ], "reduce_prompt" => "Synthesize findings from all analyses"}Guardrails: depth limit (default 5), global semaphore (16 slots), per-child timeout (5 min), recursive tools removed at max depth.
Search & Memory
Section titled “Search & Memory”| Tool | Risk | Description |
|---|---|---|
search_sessions | safe | FTS5 search over past session transcripts. Supports boolean operators, phrase matching, prefix matching. |
search_skills | safe | Find relevant skills from the catalog, ranked by Thompson sampling. |
create_skill | safe | Add a reusable skill template to the catalog. |
read_diary | safe | Read diary entries from past bursts. |
propose_playbook_delta | safe | Propose changes (add/update/deprecate) to playbook rules. |
Agent Messaging
Section titled “Agent Messaging”| Tool | Risk | Description |
|---|---|---|
check_messages | safe | Read unread messages from the inter-agent mailbox. |
list_psychonauts | safe | List all active psychonaut agents with their status. |
whois | safe | Look up agent details by ID. |
Pipeline Mutations
Section titled “Pipeline Mutations”| Tool | Risk | Description |
|---|---|---|
set_pipeline | moderate | Override the pipeline for the current bead. Goes through grounding queue. |
create_pipeline | moderate | Create a new pipeline definition. Goes through grounding queue. |
Utility
Section titled “Utility”| Tool | Risk | Description |
|---|---|---|
echo | safe | Return input as output. Useful for testing and debugging. |
Security
Section titled “Security”CommandGuard
Section titled “CommandGuard”Stateless pure functions for three-tier command classification:
CommandGuard.classify(command)# -> :safe | :caution | :dangerDanger takes priority over caution when a command matches both tiers.
TraumaGuard
Section titled “TraumaGuard”Experience-based escalation. Matches commands against a list of TraumaRecord structs:
%Annihilation.Security.TraumaRecord{ pattern: "npm install", # Plain string or ~r/ regex context: "npm install corrupted node_modules on 2026-02-15", severity: :danger}Even :safe commands are escalated to :danger if they match a trauma record. Pattern types:
- Plain strings: case-insensitive substring match
- Regex (
~r/prefix): compiled and matched at runtime
File Lease Manager
Section titled “File Lease Manager”Cooperative file locking via File.LeaseManager:
# Acquire exclusive lease before writingLeaseManager.acquire(path, agent_id, :exclusive)# -> :ok | {:conflict, %{agent_id: "other", lease_type: :exclusive, expires_at: ...}}
# Release after writingLeaseManager.release(path, agent_id)Lease types:
:exclusive— one writer, no other agent can write:shared_read— multiple readers allowed
Features:
- TTL: 5 minutes default, renewable
- Automatic cleanup: 30-second sweep for expired leases
- Crash recovery: process monitoring releases leases on agent crash
- Burst cleanup:
clear_all/0called on burst drain
Recursive children inherit parent leases as :read_only — they cannot acquire exclusive leases.
Creating Custom Tools
Section titled “Creating Custom Tools”Elixir Plugin Tools
Section titled “Elixir Plugin Tools”defmodule MyTool do @behaviour Annihilation.Agent.Tool
def name, do: "my_tool" def description, do: "Does something useful"
def parameters_schema do %{ "type" => "object", "properties" => %{ "input" => %{"type" => "string", "description" => "The input"} }, "required" => ["input"] } end
def execute(%{"input" => input}, _context) do {:ok, "Processed: #{input}"} endendPlace in .annihilation/tools/my_tool.ex for auto-discovery.