axe

repository·master·Indexed 21 days ago

https://github.com/jrswab/axe

A CLI tool for managing and running LLM-powered agents designed with a Unix philosophy. Axe treats agents as small, focused, and composable programs that can be piped together and triggered via standard automation tools. It uses TOML configuration files to define system prompts, model selection, skills, and memory settings, supporting integration with MCP servers and Docker for isolated execution.

Tokens
129.8K
Snippets
345
Records
621
Agent score
70%

What's inside axe

  1. Core features of Axe

    master

    Axe provides several capabilities for managing and executing LLM agents:

    • Multi-provider support: Anthropic, OpenAI, Ollama (local), OpenCode, and AWS Bedrock.
    • Agent Configuration: Declarative, version-controllable definitions using TOML.
    • Sub-agent delegation: Agents can call other agents via LLM tool use (supports depth limiting and parallel execution).
    • Persistent memory: Uses timestamped markdown logs to carry context across runs, with LLM-assisted garbage collection.
    • Skill system: Reusable instruction sets shared across agents.
    • Unix-style integration: Supports stdin piping (e.g., git diff | axe run reviewer) and provides structured JSON output for scripting.
    • Tooling: Built-in sandboxed file operations, shell execution, URL fetching, and web search (with SSRF protection via output allowlists).
    • MCP support: Connects to external Model Context Protocol (MCP) servers via SSE or streamable-HTTP.
    • Execution controls: Token budgets via [budget] config or --max-tokens flag, and configurable retry logic for transient provider errors.
  2. What is Axe?

    master
    Axe is an orchestrator for LLM-powered agents defined via TOML configuration files. Unlike chatbot-centric tools, Axe treats agents like Unix programs: small, focused, and composable. Each agent is defined with a specific system prompt, model, skill files, context files, and a working directory. Axe is designed to be executed from the command line and composed with standard Unix tools like pipes, cron, and git hooks.
  3. Understand budget enforcement and exit codes

    master

    When an agent run exceeds the configured max_tokens budget, the following behavior occurs:

    1. Enforcement Timing: The budget is checked before each LLM call. If a call pushes the total usage over the limit, that call is allowed to complete, its tokens are recorded, and then the execution stops. Consequently, actual usage may slightly exceed the limit by the amount of the final response.
    2. Partial Results: The agent will return the last successful LLM response generated before the limit was hit, preserving any work completed so far.
    3. Warnings: A budget-exceeded warning is printed to stderr.
    4. Exit Code: The process exits with exit code 4, which specifically indicates that the budget was exhausted. This is distinct from other error codes:
      • 0: Success
      • 1: Runtime error
      • 2: Config error
      • 3: Transient provider error
      • 4: Budget exceeded
  4. Understand sub-agent execution and parallelization

    master

    Axe supports both sequential and parallel execution of sub-agents via the parallel configuration flag.

    Parallel Execution (parallel = true)

    • Single tool call: Runs in the main goroutine without extra overhead.
    • Multiple tool calls: All calls run concurrently. Results are collected and returned to the parent LLM in the original order.
    • Error handling: If one sub-agent fails while others succeed, the failed one returns an error result and the successful ones return normal results. All results are sent back to the parent LLM to decide the next step.
    • Cancellation: If the parent's context is cancelled, all running sub-agent goroutines are cancelled immediately.

    Sequential Execution (parallel = false)

    • Tool calls are executed one after another in the order they were requested.
  5. Restrict Agent Network Access with allowed_hosts

    master

    Agents using url_fetch or web_search can be restricted to specific hostnames using the allowed_hosts field in the agent TOML.

    • Empty or absent: All public hostnames are allowed.
    • Non-empty list: Only exact hostname matches are permitted (case-insensitive; no wildcard subdomains).
    • Private IPs: Always blocked (loopback, link-local, RFC 1918, CGNAT, IPv6 private).
    • Redirects: Each redirect destination is re-validated against the allowlist and private IP checks.
    • Sub-agents: Inherit the parent's allowed_hosts unless the sub-agent explicitly sets its own.
    allowed_hosts = ["api.example.com", "docs.example.com"]
  6. Understand refusal detection in JSON output

    master
    The refused boolean field in the JSON envelope is set to true if axe detects that the LLM declined to complete the task. This detection is heuristic and scans the response for phrases such as "I cannot", "I'm unable to", or "I must decline".
  7. Define an Agent Configuration (TOML)

    master

    Agents in Axe are configured using TOML files located in the <config_dir>/agents/ directory. Each file must be named <name>.toml and corresponds to an agent identifier.

    Required Fields

    • name: The agent identifier (must match the filename).
    • model: A provider/model string following the models.dev format (e.g., anthropic/claude-sonnet-4-20250514).

    Optional Fields

    • description: A human-readable description.
    • system_prompt: The agent's persona or instructions.
    • skill: A relative path to a SKILL.md file.
    • files: A list of glob patterns for context files.
    • workdir: The working directory used for glob resolution.
    • sub_agents: A list of names of other agents this agent can invoke.
    • [memory]: Sub-configuration for persistent memory:
      • enabled: Boolean to enable memory.
      • path: Custom directory for memory storage.
    • [params]: Model parameter overrides:
      • temperature: Float (0 means provider default).
      • max_tokens: Integer (0 means provider default).
    name = "my-agent"
    description = "A helpful assistant"
    model = "anthropic/claude-sonnet-4-20250514"
    system_prompt = "You are a helpful assistant."
    
    # Context files - glob patterns resolved from workdir or cwd
    files = ["src/**/*.go", "README.md"]
    
    [memory]
    enabled = true
    path = "~/.axe/memory/my-agent"
    
    [params]
    temperature = 0.3
    max_tokens = 4096
  8. Use sub-agents with tools

    master

    An agent can be configured to use both tools and sub-agents simultaneously by defining tools and sub_agents in its configuration.

    • Sequential Dispatch: The agent uses tools and sub-agents in alternating turns.
    • Parallel Dispatch: If parallel execution is enabled (default), the agent can dispatch multiple tool calls (e.g., a read_file call and a call_agent call) in a single turn.

    To control this behavior, you can set parallel = false in the sub_agents configuration to force sequential execution.

    tools = ["read_file"]
    sub_agents = ["helper"]
  9. Map OpenAI SSE events to StreamEvents

    master

    The OpenAI provider maps Server-Sent Events (SSE) from the /chat/completions endpoint into a sequence of StreamEvent types. The mapping logic handles text deltas, tool calls, and usage data.

    SSE Data ContentResulting StreamEvent
    [DONE] sentinelReturns io.EOF
    choices[0].delta.content (non-empty)StreamEventText with the content value
    choices[0].delta.tool_calls[N] (with id)StreamEventToolStart with ToolCallID and ToolName
    choices[0].delta.tool_calls[N] (no id, has arguments)StreamEventToolDelta with ToolCallID (via index) and ToolInput
    choices[0].finish_reason == "tool_calls"StreamEventToolEnd for all accumulated tool calls
    choices[0].finish_reason == "stop"Continues to next event (stores reason for the final event)
    usage present (empty choices)StreamEventDone with InputTokens, OutputTokens, and StopReason
    JSON parse failureProviderError with ErrCategoryServer
  10. Understand sub-agent `allowed_hosts` inheritance

    master

    When using sub-agents, the allowed_hosts configuration follows an inheritance with explicit override model. This allows you to control the security boundaries of your agent hierarchy recursively.

    Inheritance Rules:

    1. Explicit Override: If a sub-agent's TOML defines its own allowed_hosts list, that list is used exclusively for that sub-agent. It does not merge with the parent's list.
    2. Inheritance: If a sub-agent's TOML does not define allowed_hosts (or it is empty), it inherits the effective allowlist from its parent.
    3. Recursive Propagation: This rule applies at every level of the tree. A sub-agent's effective list (whether inherited or explicit) becomes the fallback for any sub-sub-agents it invokes.

    Security Implications:

    • A sub-agent can be more restrictive than its parent by defining a subset of hosts.
    • A sub-agent can be less restrictive than its parent by defining a different or wider set of hosts (this is treated as an explicit operator decision).
  11. Configure API keys and Base URLs

    master

    Axe resolves API keys and Base URLs using a specific precedence order. This allows you to manage secrets via environment variables or a global configuration file.

    Precedence Order:

    1. Environment Variables: These take highest precedence. If an environment variable is set (even to an empty string, it is treated as 'not set' and falls through), Axe will use it.
    2. Global Configuration File (config.toml): If the environment variable is unset, Axe looks for the key in the global config.toml.

    Supported Providers:

    • Anthropic: Requires an API key.
    • OpenAI: Requires an API key. Can be pointed to other services via base_url.
    • Ollama: No API key required.
  12. Debug context with `--dry-run`

    master

    The --dry-run flag is used to inspect exactly what data will be sent to the LLM without actually making an API call. This is useful for debugging context, estimating token costs, and verifying glob resolution.

    When using --dry-run, Axe displays:

    • The resolved system prompt
    • Skill contents
    • The resolved file list and their contents
    • Stdin input (if context was piped)
    • The selected model and parameters
    • Available sub-agents and injected tools
    axe run <agent> --dry-run