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)
| Tool | Description | Details |
|---|---|---|
memory_read | Read a memory file or list all files | Memory Tools |
memory_write | Write content to a memory file | Memory Tools |
schedule | Create, delete, or list cron jobs | Schedule Tool |
datetime | Get the current date and time in any timezone | Always available |
web_search | Search the web via Tavily, Exa, or SearXNG | Web Search Tool |
http | Make HTTP requests with optional auth presets | HTTP Tool |
email | Read, search, and send emails via IMAP/SMTP | Email Tool |
github | Interact with GitHub issues, PRs, repos, releases, and search | GitHub Tool |
browser | Automate web browsers with natural language (Stagehand) | Browser Tool |
sandbox_exec | Execute a command in a Docker sandbox container | Requires sandbox config. High-risk — requires approval |
opencode | Run a coding agent (claude-code, opencode) in a container | Requires 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.
| Tool | Description |
|---|---|
create_skill | Generate a new skill from a natural language description |
list_skills | List all skills with status and trigger info |
run_skill | Manually trigger a skill execution by ID |
manage_skill | Pause, resume, or delete a skill. High-risk — requires approval |
install_skill | Install 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:
- The agent assembles context including tool schemas in the system prompt
- The LLM returns a response that may include tool calls
- The agent executes each tool call via the registry
- Tool results are appended to conversation history as
toolrole messages - The agent loops back to the LLM with the updated history
- 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++;
}