Skip to content

Tools System

Eshan Roy edited this page Jun 21, 2026 · 4 revisions

Tools System

M31 Autonomous (M31A) provides a curated set of tools that the LLM can invoke to interact with the filesystem, run commands, search code, and access the web. Tools are registered on a Dispatcher that handles permissions, rate limiting, and execution.

Architecture

flowchart TD
    LLM[LLM Response<br/>tool_call chunks] --> Engine[workflow/engine.go<br/>consumeStreamWithTools]
    Engine --> Dispatcher[tools/dispatcher.go<br/>Execute ToolCall]
    Dispatcher --> Rate[Rate Limiter<br/>token bucket]
    Dispatcher --> Perm[Permission Check<br/>rules + risk level]
    Dispatcher --> Lookup[Tool Lookup<br/>by name]
    Lookup --> Exec[Tool.Execute<br/>ctx, input]
    Exec --> Result[ToolResult]
    Result --> LLM

    style LLM fill:#e1f5fe
    style Dispatcher fill:#fff3e0
    style Result fill:#e8f5e9
Loading

Built-in Tools

Bash

Source: internal/tools/bash.go

Property Value
Risk Level dangerous
Timeout 30 minutes (configurable per call, max 1800s)
Output Limit 50,000 characters
Schema {command: string, timeout?: integer}

Features:

  • Process group management (SIGINT then SIGKILL after grace period)
  • Non-interactive environment variables (CI=true, DEBIAN_FRONTEND=noninteractive)
  • Stdin closed immediately to prevent hangs
  • Binary output detection (null byte check)
  • Output truncation with [... output truncated] marker
  • Concurrent stdout/stderr reading with per-stream buffers

FileRead

Source: internal/tools/fileread.go

Property Value
Risk Level safe
Max File Size 5 MB

Features:

  • Line offset and limit for partial reads
  • Line number prefix (cat -n format)
  • Long line truncation (>2000 chars)
  • Support for images and PDFs (rendered as attachments)

FileWrite

Source: internal/tools/filewrite.go

Property Value
Risk Level medium

Features:

  • Atomic writes (temp file + rename)
  • Auto-backup before overwrite (configurable, max 10 backups per file)
  • Directory auto-creation for new file paths

Edit

Source: internal/tools/edit.go

Property Value
Risk Level dangerous

Features:

  • Exact string replacement (old_string → new_string)
  • Line-range replacement (start_line + end_line + new_string)
  • Cascading replace: 7 strategies (exact, line-trimmed, whitespace-normalized, indent-normalized, line-skip, fuzzy-anchor)
  • Levenshtein similarity for fuzzy matching
  • replace_all flag for batch replacements
  • LCS-based diff summary generation
  • Same atomic write and backup as FileWrite

Glob

Source: internal/tools/glob.go

Property Value
Risk Level safe
Max Results 1,000 (configurable)

Features:

  • Standard glob patterns (**/*.go, src/**/*.ts)
  • Fallback retry with *.<ext> pattern when no matches
  • Sorted results by modification time
  • Skip directories: node_modules, vendor, .next, dist, build, target, .venv, __pycache__

Grep

Source: internal/tools/grep.go

Property Value
Risk Level safe
Max Results 100 (configurable)
Pattern Length Capped to prevent ReDoS

Features:

  • Ripgrep backend when available, pure-Go fallback otherwise
  • Full regex support with fixed_string mode for literal searches
  • File type filtering (include glob parameter)
  • Context lines (before/after match)
  • Ring buffer streaming mode: O(contextLines) memory when context > 0, O(1) when context = 0
  • ReDoS protection: rejects nested/adjacent quantifier patterns
  • Gitignore-aware file filtering with mtime-based cache
  • Result truncation with count reporting

FileDelete

Source: internal/tools/filedelete.go

Property Value
Risk Level destructive

Features:

  • Auto-backup before deletion
  • Path validation (prevents traversal attacks)

FileMove

Source: internal/tools/filemove.go

Property Value
Risk Level medium

FileList

Source: internal/tools/filelist.go

Property Value
Risk Level safe

CodeMap

Source: internal/tools/codemap.go

Property Value
Risk Level safe

Features:

  • Project structure mapping
  • Symbol extraction
  • Dependency graph generation

WebFetch

Source: internal/tools/webfetch.go

Property Value
Risk Level medium
Max Redirects 5 (configurable)

Features:

  • HTTP to HTTPS auto-upgrade
  • HTML to markdown conversion
  • SSRF protection (blocks private, loopback, link-local IPs)
  • Custom User-Agent header
  • Content type detection

WebSearch

Source: internal/tools/websearch.go

Property Value
Risk Level safe

Features:

  • SearXNG meta-search engine backend
  • No API key required
  • Configurable instance URL
  • Structured results (title, URL, snippet)

TodoWrite

Source: internal/tools/todo.go

Property Value
Risk Level safe

Features:

  • Task tracking within the session
  • Stored in session directory

AskUserQuestion

Source: internal/tools/question.go

Property Value
Risk Level safe

Features:

  • Structured questions with multiple-choice options
  • Per-request response routing (prevents cross-caller mix-ups)
  • Timeout support

Agent

Source: internal/tools/agent.go

Property Value
Risk Level medium

Features:

  • Spawns parallel subagents with isolated worktrees
  • Background execution support
  • See Subagents for details

CodeComplexity

Source: internal/tools/codecomplexity.go

Property Value
Risk Level safe
Schema {skip_dirs?: string[]}

Features:

  • Scans Go source files in the project directory
  • Counts files and lines per package and per file
  • Ranks top packages and files by size
  • Produces a complexity score (low, medium, high)
  • Respects skip directories (configurable, merges with defaults)

See CodeComplexity Tool for detailed usage.

Default Registration

All tools are registered in internal/tools/defaults.go:

func DefaultDispatcher(workDir, backupDir, sessionsDir string, cfg *PermissionsConfig) (*Dispatcher, error) {
    d := NewDispatcher(cfg)
    d.Register(NewBash(workDir))
    d.Register(NewFileRead(workDir))
    d.Register(NewFileWrite(workDir, backupDir))
    d.Register(NewEdit(workDir, backupDir))
    d.Register(NewTodoWrite(sessionsDir, ""))
    d.Register(NewWebFetch(sessionsDir, false))
    d.Register(NewWebSearch(""))
    d.Register(NewAskUserQuestion(...))
    d.Register(NewGlob(workDir))
    d.Register(NewGrep(workDir))
    d.Register(NewFileList(workDir))
    d.Register(NewFileDelete(workDir, backupDir))
    d.Register(NewFileMove(workDir))
    d.Register(NewCodeMap(workDir))
    // Agent tool registered separately on parent dispatcher
    return d, nil
}

Dispatcher

Source: internal/tools/dispatcher.go

The Dispatcher is the central hub for tool execution. It provides:

Rate Limiting

Token bucket algorithm prevents resource exhaustion from LLMs generating excessive tool calls:

  • Burst capacity: ToolRateLimitBurst
  • Refill rate: ToolRateLimitPerSec
  • Background goroutine refills tokens; stopped via Stop() with sync.Once safety

Permission System

Every tool execution passes through ensurePermission():

  1. Rule matching -- Checks tool name and arguments against configured rules
  2. Risk assessment -- Classifies as safe, medium, dangerous, or destructive
  3. Agent defaults -- Per-agent permission profiles
  4. Interactive prompt -- Modal in the TUI (allow / allow always / deny / exit)
  5. Non-interactive fallback -- Blocks dangerous tools in shell mode

Input Normalization

The dispatcher handles two input formats from LLMs:

  • Nested: {"name":"Bash", "params":{"command":"ls"}}
  • Direct: {"name":"Bash", "command":"ls"}

Both are normalized to the same internal ToolInput struct.

Parameter Limit

Input is capped at 1000 parameters per tool call to prevent abuse.

Permission Request/Response Protocol

Communication between the dispatcher and TUI uses channels:

Dispatcher ──(PermissionRequest)──→ TUI modal
Dispatcher ←──(PermissionResponse)── TUI user action

Each request has a monotonically increasing ID (atomic counter) for routing responses to the correct waiter. Per-request channels prevent cross-caller response mix-ups in concurrent scenarios.

Clone this wiki locally