Augureaugure
Tools

Tool System

How Augure's native tools work -- registration, execution, and function calling

Augure uses native tools that run in-process for speed. The LLM calls tools via function calling -- it receives tool schemas, returns structured tool calls, and the agent executes them.

NativeTool Interface

Every tool implements the NativeTool interface:

export interface NativeTool {
  name: string;
  description: string;
  parameters: Record<string, unknown>;  // JSON Schema
  execute: (params: unknown, ctx: ToolContext) => Promise<ToolResult>;
  configCheck?: (ctx: ToolContext) => string | null;  // null = configured, string = warning
  riskLevel?: "high";  // requires user approval before execution
}

export interface ToolResult {
  success: boolean;
  output: string;
  artifacts?: Artifact[];
}

export interface Artifact {
  type: "file" | "image" | "json";
  name: string;
  content: string;
}

The parameters field is a JSON Schema object that describes the tool's input. The LLM uses this to generate valid arguments.

ToolContext

Every tool receives a ToolContext giving it access to the agent's subsystems:

export interface ToolContext {
  config: AppConfig;
  memory: MemoryStore;
  scheduler: Scheduler;
  pool?: ContainerPool;
}

This allows tools to read/write memory, manage scheduled jobs, access Docker containers, and read configuration -- all without needing their own initialization.

ToolRegistry

The ToolRegistry manages tool registration and dispatch:

export class ToolRegistry {
  register(tool: NativeTool): void;      // Add a tool
  get(name: string): NativeTool;          // Look up by name
  list(): NativeTool[];                    // All registered tools
  toFunctionSchemas(): FunctionSchema[];   // Schemas for the LLM
  execute(name: string, params: unknown): Promise<ToolResult>;  // Dispatch
  setContext(ctx: ToolContext): void;       // Inject shared context
}

Function Schema Format

The toFunctionSchemas() method converts registered tools into the OpenAI-compatible function calling format:

export interface FunctionSchema {
  type: "function";
  function: {
    name: string;
    description: string;
    parameters: Record<string, unknown>;
  };
}

These schemas are injected into the system prompt so the LLM knows what tools are available and how to call them.

Available Tools

Augure provides 16 native tools across several packages:

Core Tools (@augure/tools)

ToolDescriptionDetails
memory_readRead a memory file or list all filesMemory Tools
memory_writeWrite content to a memory fileMemory Tools
scheduleCreate, delete, or list cron jobsSchedule Tool
datetimeGet the current date and time in any timezoneAlways available
web_searchSearch the web via Tavily, Exa, or SearXNGWeb Search Tool
httpMake HTTP requests with optional auth presetsHTTP Tool
emailRead, search, and send emails via IMAP/SMTPEmail Tool
githubInteract with GitHub issues, PRs, repos, releases, and searchGitHub Tool
browserAutomate web browsers with natural language (Stagehand)Browser Tool
sandbox_execExecute a command in a Docker sandbox containerRequires sandbox config. High-risk — requires approval
opencodeRun a coding agent (claude-code, opencode) in a containerRequires sandbox.codeAgent config. High-risk — requires approval

Configuration Awareness

Tools that require configuration (like web_search, email, opencode) implement a configCheck function. When these tools are not configured, the LLM sees a [NOT CONFIGURED] warning appended to their description with a link to the relevant documentation page. This way the agent can inform the user about what needs to be set up instead of failing silently.

Tiered Approval

Tools with riskLevel: "high" require explicit user approval before execution. When the approval gate is enabled, the agent sends an approval request to the user via the active channel (e.g. Telegram inline buttons with Approve/Reject) and waits for a response.

  • Approved — the tool executes normally
  • Rejected — the tool call returns "Tool call rejected by user" to the LLM
  • Timeout — auto-rejected after approval.timeoutMs (default: 2 minutes)

Three tools are currently high-risk: sandbox_exec, opencode, and manage_skill. All other tools execute immediately without approval.

Skill Tools (@augure/skills)

Registered when skills is configured. See Skills System for details.

ToolDescription
create_skillGenerate a new skill from a natural language description
list_skillsList all skills with status and trigger info
run_skillManually trigger a skill execution by ID
manage_skillPause, resume, or delete a skill. High-risk — requires approval
install_skillInstall a curated skill from the GitHub hub

Code Mode

When Code Mode is enabled, the tool system changes: instead of exposing all tools individually via function calling, the agent exposes a single execute_code tool. The LLM writes TypeScript that calls typed APIs (api.memory_read(...), api.http(...), etc.) in a sandbox. This reduces round-trips for multi-step tasks. All registered tools remain accessible inside the code via the auto-generated api.* proxy.

How Tool Calling Works

Without Code Mode, the standard flow is:

  1. The agent assembles context including tool schemas in the system prompt
  2. The LLM returns a response that may include tool calls
  3. The agent executes each tool call via the registry
  4. Tool results are appended to conversation history as tool role messages
  5. The agent loops back to the LLM with the updated history
  6. The loop continues until the LLM responds with plain text (no tool calls) or the max loop count is reached
while (loopCount < maxLoops) {
  const messages = assembleContext({ ... });
  const response = await llm.chat(messages);

  if (response.toolCalls.length === 0) {
    // Done -- return the text response
    return response.content;
  }

  // Execute each tool call
  for (const toolCall of response.toolCalls) {
    // High-risk tools require user approval first
    const tool = tools.get(toolCall.name);
    if (tool?.riskLevel === "high" && approvalGate) {
      const approved = await approvalGate.request(userId, toolCall.name, toolCall.arguments);
      if (!approved) {
        conversationHistory.push({ role: "tool", content: "Tool call rejected by user.", toolCallId: toolCall.id });
        continue;
      }
    }

    const result = await tools.execute(toolCall.name, toolCall.arguments);
    conversationHistory.push({
      role: "tool",
      content: result.output,
      toolCallId: toolCall.id,
    });
  }

  loopCount++;
}

On this page